From 7c23ca94e2b7289c5564f53d834432b3e7db7c9c Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Sun, 15 May 2016 09:10:20 +0100 Subject: [PATCH 0001/4563] Multi-line string literal proposals. --- proposals/XXXX-multi-line-string-literals.md | 135 +++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 proposals/XXXX-multi-line-string-literals.md diff --git a/proposals/XXXX-multi-line-string-literals.md b/proposals/XXXX-multi-line-string-literals.md new file mode 100644 index 0000000000..79e871fea7 --- /dev/null +++ b/proposals/XXXX-multi-line-string-literals.md @@ -0,0 +1,135 @@ +# Feature name + +* Proposal: [SE-XXXX](https://github.com/apple/swift-evolution/blob/master/proposals/XXXX-name.md) +* Author(s): [John Holdsworth](https://github.com/johnno1962), [Brent Royal-Gordon](https://github.com/brentdax), [Tyler Cloutier](https://github.com/TheArtOfEngineering) +* Status: **[Awaiting review](#rationale)** +* Review manager: TBD + +## Introduction + +This proposal introduces multi-line string literals to Swift source code. +It proposes a number of different syntaxes that could achieve this goal +each of which has their own use case and constituency for discussion. + +[Swift-evolution thread](http://thread.gmane.org/gmane.comp.lang.swift.evolution/904/focus=15133) + +## Motivation + +Multi-line String literals are a common programming-language feature that is, to date, still missing in Swift. +Whether generating XML/JSON messages or building usage messages in Swift scripts, providing string literals that +extend over multiple lines offers a simple way to represent text without having to manually break lines using +string concatenation. Concatenation is ungainly and may result in slow compilation. + +## Proposed solutions + +This proposal puts forward three alternate syntaxes for discussion: `"continuation quotes"`, `"""long strings"""` +and `<<"heredoc"`. It has been shown all three can co-exist in the same parser and it is proposed all +three should be made available to the Swift developer to chose according to their preference though +this is intended to be determined by the review process. + +### Continuation quotes + +The basic idea of continuation quotes is straightforward. If a quoted string literal is not closed by " +before the end of the line and the first non-whitespace character on the next line is " it is taken to +be a continuation of the previous literal. + + let xml = " + " + " + " \(author) + " XML Developer's Guide + " Computer + " 44.95 + " 2000-10-01 + " An in-depth look at creating applications with XML. + " + " + "" + +The advantage of this format is it gives precise control of exactly what is included in the literal. It also +allows code to be formatted in an aesthetically pleasing manner. Its main disadvantage is that some external +editors will not be familiar with the format and will be unable to correctly syntax highlight literals. + +### Long strings + +Long strings are strings delimited by `"""triple quotes"""` that can contain newlines and individual `"` +characters without the need to escape them. + + assert( xml == """ + + + + \(author) + XML Developer's Guide + Computer + 44.95 + 2000-10-01 + An in-depth look at creating applications with XML. + + + """ ) + +To allow free formatting of the literal an indentation stripping operation is applied whereby +any whitespace characters in front of the closing delimiter are removed from each of the lines +in the literal. As part of this process any initial linefeed is also removed. This allows the +developer to paste literal content directly into the string without modification. Some concern +has been expressed about could introduce confusion if the prefixing indentation of each line does +not contain the same whitespace characters, though this can be checked for by a compiler warning. + +### Heredoc + +Taking a precedent from other languages, a syntax such as the following could be used to introduce +literals into a codebase. + + assert( xml == <<"XML" ) + + + + \(author) + XML Developer's Guide + Computer + 44.95 + 2000-10-01 + An in-depth look at creating applications with XML. + + + XML + +The same indentation stripping rules would be applied as for long strings. This syntax has the +advantage of being able to paste content in directly and the additional advantage that the +literal is separated from the line of code using it, increasing clarity. Its main disadvantage +is a more practical one: it is a more major departure for the compiler in that tokens +in the AST are no longer in source file order. Testing has, however, shown the toolchain +to be surprisingly robust in dealing with this change once a few assertions were removed. + +## Detailed design + +These changes are envisaged to be mostly confined to the Swift tokeniser: lib/Parse/Lexer.cpp. +The new string literals would be presented to the grammar as simply being string literals. +This has been explored in a PR for a [prototype toolchain](https://github.com/apple/swift/pull/2275) +and seems to be a robust approach. Other details are explored in the prototype such as +escaping the newline in literals resulting in it not being included in the final literal. + +## Impact on existing code + +As none of these syntaxes are currently legal Swift syntax they do not affect existing code as +they can not be present. None of these syntaxes preclude future directions such as introducing +string modifiers for example, `e"\w\d+"` to support non-standard and non-escaped literals. + +## Alternatives considered + +This proposal is inclusive in that it contains the multiple syntaxes discussed rather +than remove alternatives too early on in the process. In the swift-evolution thread +it became apparent that each syntax had its own, at times, non-overlapping constituency +of supporters and hopefully all three will be able to make it into the language. + +Other alternatives discussed revolved around the precise delimiter for `"""long strings"""` +with mention of `_"long strings"_` or `@"long strings"@`. + +------------------------------------------------------------------------------- + +# Rationale + +On [Date], the core team decided to **(TBD)** this proposal. +When the core team makes a decision regarding this proposal, +their rationale for the decision will be written here. From 47169faeba311f6869c688ebc3f77bb53c6c945e Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Mon, 16 May 2016 17:06:32 +0100 Subject: [PATCH 0002/4563] Spelling error --- proposals/XXXX-multi-line-string-literals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/XXXX-multi-line-string-literals.md b/proposals/XXXX-multi-line-string-literals.md index 79e871fea7..0aa91e78ca 100644 --- a/proposals/XXXX-multi-line-string-literals.md +++ b/proposals/XXXX-multi-line-string-literals.md @@ -24,7 +24,7 @@ string concatenation. Concatenation is ungainly and may result in slow compilati This proposal puts forward three alternate syntaxes for discussion: `"continuation quotes"`, `"""long strings"""` and `<<"heredoc"`. It has been shown all three can co-exist in the same parser and it is proposed all -three should be made available to the Swift developer to chose according to their preference though +three should be made available to the Swift developer to choose according to their preference though this is intended to be determined by the review process. ### Continuation quotes From aefbffae2efe08a6a05a87da367f7774474b144e Mon Sep 17 00:00:00 2001 From: Erica Sadun Date: Thu, 15 Sep 2016 10:34:12 -0600 Subject: [PATCH 0003/4563] Gardening batch one. Duplicating 521 pull request which cannot be automatically merged. Thank you @adius of Feram --- proposals/0107-unsaferawpointer.md | 2 +- proposals/0115-literal-syntax-protocols.md | 2 +- proposals/0117-non-public-subclassable-by-default.md | 2 +- proposals/0119-extensions-access-modifiers.md | 2 +- proposals/0122-use-colons-for-subscript-type-declarations.md | 2 +- proposals/0125-remove-nonobjectivecbase.md | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/0107-unsaferawpointer.md b/proposals/0107-unsaferawpointer.md index 0f44e6bebd..eb1cae670d 100644 --- a/proposals/0107-unsaferawpointer.md +++ b/proposals/0107-unsaferawpointer.md @@ -248,7 +248,7 @@ UnsafeMutableRawPointer { } ``` -The `load` and `storeBytes` operations are assymetric. `load` reads raw +The `load` and `storeBytes` operations are asymetric. `load` reads raw bytes but properly constructs a new value of type `T` with its own lifetime. Any copied references will be retained. In contrast, `storeBytes` only operates on a value's raw bytes, writing them into diff --git a/proposals/0115-literal-syntax-protocols.md b/proposals/0115-literal-syntax-protocols.md index c222bb8132..b3c0dc4717 100644 --- a/proposals/0115-literal-syntax-protocols.md +++ b/proposals/0115-literal-syntax-protocols.md @@ -29,7 +29,7 @@ Further, the standard library team has observed: (e.g., Int or String), and as far as the user at the call site is concerned, there is no visible conversion (even if one is happening behind the scenes). -[An earlier proposal](0041-conversion-protocol-conventions.md) was intended to address the first problem by introducing strong naming conventions for three kinds of conversion protocols (*from*, *to*, and *bidirectional*). The review highlighted the difficulity in establishing conventions that everyone is happy with. This proposal takes a different approach to solving the problem that originally inspired that proposal while also solving the awkwardness of the current names described by the standard library team. +[An earlier proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0041-conversion-protocol-conventions.md) was intended to address the first problem by introducing strong naming conventions for three kinds of conversion protocols (*from*, *to*, and *bidirectional*). The review highlighted the difficulty in establishing conventions that everyone is happy with. This proposal takes a different approach to solving the problem that originally inspired that proposal while also solving the awkwardness of the current names described by the standard library team. ## Proposed solution diff --git a/proposals/0117-non-public-subclassable-by-default.md b/proposals/0117-non-public-subclassable-by-default.md index 97be8b0ec1..d4eb7a4d5c 100644 --- a/proposals/0117-non-public-subclassable-by-default.md +++ b/proposals/0117-non-public-subclassable-by-default.md @@ -172,7 +172,7 @@ current module and that superclass's access level is not `open`. An `open` class may not also be declared `final`. The superclass of an `open` class must be `open`. This is consistent -with the existing access rule for superclasses. It may be desireable +with the existing access rule for superclasses. It may be desirable to lift this restriction in a future proposal. ### `open` class members diff --git a/proposals/0119-extensions-access-modifiers.md b/proposals/0119-extensions-access-modifiers.md index 40a8e311c0..75f1a4f3b1 100644 --- a/proposals/0119-extensions-access-modifiers.md +++ b/proposals/0119-extensions-access-modifiers.md @@ -102,7 +102,7 @@ public protocol SomeProtocol {} public extension A : SomeProtocol {} ``` -*Extensions* are also used for *protocol default implementations* in respect to the mentioned rules. That means that if someone would want to provide a public default implementation for a specific protocol there are three different ways to achive this goal: +*Extensions* are also used for *protocol default implementations* in respect to the mentioned rules. That means that if someone would want to provide a public default implementation for a specific protocol there are three different ways to achieve this goal: ```swift public protocol G { diff --git a/proposals/0122-use-colons-for-subscript-type-declarations.md b/proposals/0122-use-colons-for-subscript-type-declarations.md index 378c8ce7ea..321d8be2db 100644 --- a/proposals/0122-use-colons-for-subscript-type-declarations.md +++ b/proposals/0122-use-colons-for-subscript-type-declarations.md @@ -4,7 +4,7 @@ * Author: [James Froggatt](https://github.com/MutatingFunk) * Review Manager: [Chris Lattner](http://github.com/lattner) * Status: **Rejected** -* Decison Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-July/000258.html) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-July/000258.html) ## Introduction diff --git a/proposals/0125-remove-nonobjectivecbase.md b/proposals/0125-remove-nonobjectivecbase.md index 2a58572709..57e14871fa 100644 --- a/proposals/0125-remove-nonobjectivecbase.md +++ b/proposals/0125-remove-nonobjectivecbase.md @@ -91,7 +91,7 @@ Rename `ManagedBufferPointer.holdsUniqueReference` to `ManagedBufferPointer.isUniqueReference` to avoid confusion. Remove `ManagedBufferPointer.holdsUniqueOrPinnedReference` because there is no -public pinning API so having this public API is not neccessary. +public pinning API so having this public API is not necessary. ## Detailed design From eae7cf2b08cdc45331f9c356f6cfa2d256e6fbcb Mon Sep 17 00:00:00 2001 From: Erica Sadun Date: Thu, 15 Sep 2016 10:37:23 -0600 Subject: [PATCH 0004/4563] Gardening batch two. Duplicating 521 pull request which cannot be automatically merged. Thank you @adius of Feram --- proposals/0084-trailing-commas.md | 2 +- proposals/0085-package-manager-command-name.md | 2 +- proposals/0092-typealiases-in-protocols.md | 2 +- proposals/0096-dynamictype.md | 2 +- proposals/0102-noreturn-bottom-type.md | 2 +- proposals/0106-rename-osx-to-macos.md | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/0084-trailing-commas.md b/proposals/0084-trailing-commas.md index 3cae236cd2..91a4aa42b3 100644 --- a/proposals/0084-trailing-commas.md +++ b/proposals/0084-trailing-commas.md @@ -83,7 +83,7 @@ Allowing cut and paste or commenting of entire parameter lines means simple chan > "We should be consistent in either accepting or rejecting trailing commas everywhere we have comma-delimited syntax. I'm in favor of accepting it, since it's popular in languages where it's supported to enable a minimal-diff style, so that changes to code don't impact neighboring lines for purely syntactic reasons. > -> If you add an argument to a function, without trailing comma support, a comma has to be added to dirty the previous line In response to observations that tuples and function arguments are somehow different from collection literals because they generally have fixed arity, I'll note that we have an very prominent variadic function in the standard library, "print", and that adding or removing values to a "print" is a very common and natural thing to do +> If you add an argument to a function, without trailing comma support, a comma has to be added to dirty the previous line In response to observations that tuples and function arguments are somehow different from collection literals because they generally have fixed arity, I'll note that we have a very prominent variadic function in the standard library, "print", and that adding or removing values to a "print" is a very common and natural thing to do > > We've generally shied away from legislating style; see our rationale behind not requiring `self.` ([example](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005478.html)) In languages where trailing commas are pervasively allowed, such as Perl, Python, Ruby, and modern Javascript, I haven't seen any indication that this is a major problem. Less blood has definitely been shed over it than over bracing style and other style wars." - Joe Groff diff --git a/proposals/0085-package-manager-command-name.md b/proposals/0085-package-manager-command-name.md index 50f564df41..15deb18dd8 100644 --- a/proposals/0085-package-manager-command-name.md +++ b/proposals/0085-package-manager-command-name.md @@ -85,7 +85,7 @@ the new `swift package` subcommands are added, as aliases to those subcommands, for compatibility. They will be removed before Swift 3 is released. We acknowledge the possible need for a shorter version of the `swift package` -command, and believe we can revisit this to add an shorter alias for this in the +command, and believe we can revisit this to add a shorter alias for this in the future if necessary. See the alternatives section below. ## Impact on existing packages diff --git a/proposals/0092-typealiases-in-protocols.md b/proposals/0092-typealiases-in-protocols.md index b54fcb1eac..57922a5314 100644 --- a/proposals/0092-typealiases-in-protocols.md +++ b/proposals/0092-typealiases-in-protocols.md @@ -4,7 +4,7 @@ * Authors: [David Hart](https://github.com/hartbit), [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Chris Lattner](http://github.com/lattner) * Status: **Implemented (Swift 3)** -* Decison Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160516/017742.html) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160516/017742.html) * Bug: [SR-1539](https://bugs.swift.org/browse/SR-1539) ## Introduction diff --git a/proposals/0096-dynamictype.md b/proposals/0096-dynamictype.md index 4cfa5647da..9e279fa9f6 100644 --- a/proposals/0096-dynamictype.md +++ b/proposals/0096-dynamictype.md @@ -38,7 +38,7 @@ At this time, this operation cannot be written as a stdlib feature and it will b ## Impact on Existing Code -Adopting this proposal will break code and require migration support. The postfix property syntax must change to a operator call. +Adopting this proposal will break code and require migration support. The postfix property syntax must change to an operator call. ## Alternatives Considered diff --git a/proposals/0102-noreturn-bottom-type.md b/proposals/0102-noreturn-bottom-type.md index 42c1b3ff51..d07fca044b 100644 --- a/proposals/0102-noreturn-bottom-type.md +++ b/proposals/0102-noreturn-bottom-type.md @@ -66,7 +66,7 @@ The `@noreturn` attribute is removed from the language. Where `@noreturn` is currently used to exempt nonterminating code paths from control flow requirements such as exiting a `guard...else` clause or `return`-ing from a non-`Void` function, that exemption is -transfered to expressions of *uninhabited type*. +transferred to expressions of *uninhabited type*. ## Detailed design diff --git a/proposals/0106-rename-osx-to-macos.md b/proposals/0106-rename-osx-to-macos.md index 38f4231117..815e456fc6 100644 --- a/proposals/0106-rename-osx-to-macos.md +++ b/proposals/0106-rename-osx-to-macos.md @@ -4,7 +4,7 @@ * Author: [Erica Sadun](http://github.com/erica) * Review Manager: [Chris Lattner](http://github.com/lattner) * Status: **Implemented (Swift 3)** -* Decison Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-June/000193.html) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-June/000193.html) * Bugs: [SR-1823](https://bugs.swift.org/browse/SR-1823), [SR-1887](https://bugs.swift.org/browse/SR-1887) @@ -73,7 +73,7 @@ This proposal is purely additive. It will not affect existing code other than ad Instead of retaining and aliasing `os(OSX)`, it can be fully replaced by `os(macOS)`. This mirrors the situation with the phoneOS to iOS rename and would require a migration assistant to fixit old-style use. -Charlie Monroe points out: "Since Swift 3.0 is a code-breaking change my guess is that there is no burden if the Xcode migration assistent automatically changes all `#if os(OSX)` to `#if os(macOS)`, thus deprecating the term OSX, not burdening the developer at all. If iOS was renamed to phoneOS and kept versioning, you'd still expect `#if os(iOS)` to be matched when targetting phoneOS and vice-versa." +Charlie Monroe points out: "Since Swift 3.0 is a code-breaking change my guess is that there is no burden if the Xcode migration assistent automatically changes all `#if os(OSX)` to `#if os(macOS)`, thus deprecating the term OSX, not burdening the developer at all. If iOS was renamed to phoneOS and kept versioning, you'd still expect `#if os(iOS)` to be matched when targeting phoneOS and vice-versa." ## Unaddressed Issues From be2362f1dccd698b89e826343a0069cf322f402f Mon Sep 17 00:00:00 2001 From: Erica Sadun Date: Thu, 15 Sep 2016 10:41:09 -0600 Subject: [PATCH 0005/4563] Gardening batch three. Duplicating 521 pull request which cannot be automatically merged. Thank you @adius of Feram --- proposals/0048-generic-typealias.md | 2 +- proposals/0058-objectivecbridgeable.md | 4 ++-- proposals/0063-swiftpm-system-module-search-paths.md | 2 +- proposals/0067-floating-point-protocols.md | 6 +++--- proposals/0074-binary-search.md | 2 +- proposals/0077-operator-precedence.md | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/proposals/0048-generic-typealias.md b/proposals/0048-generic-typealias.md index 774dc58c22..2a55e51610 100644 --- a/proposals/0048-generic-typealias.md +++ b/proposals/0048-generic-typealias.md @@ -51,7 +51,7 @@ typealias DictionaryOfStrings = Dictionary // error: type 'T' does not conform to protocol 'Hashable' ``` -However, because this proposal is targetted at supporting aliases, it does not +However, because this proposal is targeted at supporting aliases, it does not allow *additional* constraints to be added to type parameters. For example, you can't write: diff --git a/proposals/0058-objectivecbridgeable.md b/proposals/0058-objectivecbridgeable.md index 92307fe350..1cf9196f2b 100644 --- a/proposals/0058-objectivecbridgeable.md +++ b/proposals/0058-objectivecbridgeable.md @@ -17,7 +17,7 @@ Swift-evolution thread: [\[Idea\] ObjectiveCBridgeable](https://lists.swift.org/ There is currently no good way to define a Swift-y API that makes use of generics, enums with associated values, structs, protocols with associated types, and other Swift features while still exposing that API to Objective-C. -This is especially prevelant in a mixed codebase. Often an API must be dumbed-down or Swift features eschewed because rewriting the entire codebase is impractical and Objective-C code must be able to call the new Swift code. This results in a situation where new code or refactored code adopts an Objective-C compatible API which is compromised, less type safe, and isn't as nice to work with as a truly native Swift API. +This is especially prevelent in a mixed codebase. Often an API must be dumbed-down or Swift features eschewed because rewriting the entire codebase is impractical and Objective-C code must be able to call the new Swift code. This results in a situation where new code or refactored code adopts an Objective-C compatible API which is compromised, less type safe, and isn't as nice to work with as a truly native Swift API. The cascading effect is even worse because when the last vestiges of Objective-C have been swept away, you're left with a mountain of Swift code that essentially looks like a direct port of Objective-C code and doesn't take advantage of any of Swift's modern features. @@ -230,7 +230,7 @@ However there doesn't appear to be a convincing case to support such complex beh ### BuiltInBridgeable -On the mailing list the idea of a protocol to supercede `ObjectiveCBridgeable` for built-in types like `Int` was brought up but not considered essential for this proposal. (The protocol would be decorative only, not having any functions or properties). +On the mailing list the idea of a protocol to supersede `ObjectiveCBridgeable` for built-in types like `Int` was brought up but not considered essential for this proposal. (The protocol would be decorative only, not having any functions or properties). These types are special because they bridge differently inside collections vs outside. The compiler already has *magic* knowledge of these types and I don't anticipate the list of types will ever get any longer. The only benefit of a `BuiltInBridgeable` protocol would be to explicitly declare which types have this "magic". diff --git a/proposals/0063-swiftpm-system-module-search-paths.md b/proposals/0063-swiftpm-system-module-search-paths.md index 7fe3a6f670..d0af1f70b2 100644 --- a/proposals/0063-swiftpm-system-module-search-paths.md +++ b/proposals/0063-swiftpm-system-module-search-paths.md @@ -136,7 +136,7 @@ parameters, they can be added on a per enum basis. `apt` is used across multiple distirbutions and the install-names for tools vary. Even for the same distribution install-names may vary across releases (eg. from Ubuntu 15.04 to Ubuntu 15.10) or even on -ocassion at finer granularity. +occasion at finer granularity. We will not add explicit handling for this, but one can imagine the enums for different system packagers could be supplemented in a backwards diff --git a/proposals/0067-floating-point-protocols.md b/proposals/0067-floating-point-protocols.md index 70bbc952d4..bfe6479351 100644 --- a/proposals/0067-floating-point-protocols.md +++ b/proposals/0067-floating-point-protocols.md @@ -188,7 +188,7 @@ public protocol FloatingPoint: Comparable, IntegerLiteralConvertible, SignedNumb /// This quantity, or a related quantity is sometimes called "epsilon" or /// "machine epsilon". We avoid that name because it has different meanings /// in different languages, which can lead to confusion, and because it - /// suggests that it is an good tolerance to use for comparisons, + /// suggests that it is a good tolerance to use for comparisons, /// which is almost never is. /// /// (See https://en.wikipedia.org/wiki/Machine_epsilon for more detail) @@ -449,7 +449,7 @@ public protocol FloatingPoint: Comparable, IntegerLiteralConvertible, SignedNumb @warn_unused_result func isLessThanOrEqualTo(_ other: Self) -> Bool - /// True if and only if `self` preceeds `other` in the IEEE 754 total order + /// True if and only if `self` precedes `other` in the IEEE 754 total order /// relation. /// /// This relation is a refinement of `<=` that provides a total order on all @@ -762,7 +762,7 @@ and `isNaN`. ## Changes from the draft proposal 1. Removed the `Arithmetic` protocol; it may be circulated again in the future -as an independent proposal, or as part of an new model for integers. +as an independent proposal, or as part of a new model for integers. 2. Removed the `add[ing]`, `subtract[ing]`, etc methods, which were hooks for `Arithmetic`. This proposal now includes only the existing operator forms. diff --git a/proposals/0074-binary-search.md b/proposals/0074-binary-search.md index da14970b48..1a9edbe547 100644 --- a/proposals/0074-binary-search.md +++ b/proposals/0074-binary-search.md @@ -193,7 +193,7 @@ extension Collection { ## Example usage -As an example of how the `partitionedIndex(of:)` method enables heterogenous binary search, this `SortedDictionary` type uses an array of `(Word, Definition)` tuples as its storage, sorted by `Word`. +As an example of how the `partitionedIndex(of:)` method enables heterogeneous binary search, this `SortedDictionary` type uses an array of `(Word, Definition)` tuples as its storage, sorted by `Word`. Better explained examples can be found in the Swift playground available [here to download](https://github.com/lorenzoracca/Swift-binary-search/blob/binarysearch/Binary%20Search%20Proposal.playground.zip). diff --git a/proposals/0077-operator-precedence.md b/proposals/0077-operator-precedence.md index 3ffdc4f420..65fd6e1ebb 100644 --- a/proposals/0077-operator-precedence.md +++ b/proposals/0077-operator-precedence.md @@ -421,7 +421,7 @@ precedence Multiplicative > Additive precedence Exponentiative > Multiplicative ``` -Appearence of precedence group name in any of these "declarations" would mean declaration of the precedence group. +Appearance of precedence group name in any of these "declarations" would mean declaration of the precedence group. Precedence relationship declaration would only allow `>` sign for consistency. Limitations on connecting unrelated imported groups could still hold. @@ -432,7 +432,7 @@ It would make each operator define precedence relationships. The graph of relationships would be considerably larger and less understandable in this case. -Precedence groups concept would still be present, but it would make one operator in each group "priveleged": +Precedence groups concept would still be present, but it would make one operator in each group "privileged": ```swift precedence - = + From 9721fdc37cbd7208f8544a1da10fc03746a32ef6 Mon Sep 17 00:00:00 2001 From: Erica Sadun Date: Thu, 15 Sep 2016 10:46:34 -0600 Subject: [PATCH 0006/4563] Gardening batch four. Duplicating 521 pull request which cannot be automatically merged. Thank you @adius of Feram --- proposals/0018-flexible-memberwise-initialization.md | 10 +++++----- proposals/0019-package-manager-testing.md | 2 +- proposals/0035-limit-inout-capture.md | 2 +- proposals/0038-swiftpm-c-language-targets.md | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/proposals/0018-flexible-memberwise-initialization.md b/proposals/0018-flexible-memberwise-initialization.md index 032f70a9ff..ef2a7c0624 100644 --- a/proposals/0018-flexible-memberwise-initialization.md +++ b/proposals/0018-flexible-memberwise-initialization.md @@ -37,13 +37,13 @@ Add to the list “all or nothing”. The compiler generates the entire initial It is common to have a type with a number of public members that are intended to be configured by clients, but also with some private state comprising implementation details of the type. This is especially prevalent in UI code which may expose many properties for configuring visual appearance, etc. Flexibile memberwise initialization can provide great benefit in these use cases, but it immediately becomes useless if it is "all or nothing". -We need a flexible solution that can synthesize memberwise initialization for some members while allowing the type auther full control over initialization of implementation details. +We need a flexible solution that can synthesize memberwise initialization for some members while allowing the type author full control over initialization of implementation details. ## Proposed solution I propose adding a `memberwise` declaration modifier for initializers which allows them to *opt-in* to synthesis of memberwise initialization. -This proposal adopts a model for property eligibility where stored properties automatically recieve memberwise initialization parameters unless they are deemed ineligible for one of several reasons. An *opt-in* model using a `memberwise` declaration modifier allowing properties to *opt-in* to memberwise initialization synthesis is also possible. +This proposal adopts a model for property eligibility where stored properties automatically receive memberwise initialization parameters unless they are deemed ineligible for one of several reasons. An *opt-in* model using a `memberwise` declaration modifier allowing properties to *opt-in* to memberwise initialization synthesis is also possible. The two approaches are not mutually exclusive: it is possible to use the *automatic* model when no properties have the `memberwise` declaration modifier and the *opt-in* model when one or more properties do have the `memberwise` declaration modifier. A future enhancement to this proposal may introduce the *opt-in* model, allowing programmers to choose which model is preferred for a specific type they are authoring. @@ -295,7 +295,7 @@ The rules of the current proposal are designed to synthesize memberwise paramete Introducing a `memberwise` declaration modifier for properties would allow programmers to specify exactly which properties should participate in memberwise initialization synthesis. It allows full control and has the clarity afforded by being explicit. -Specifc use cases this feature would support include allowing `private` properties to receive synthesized memberwise parameters in a `public` initializer, or allow `public` properties to be ommitted from parameter synthesis. +Specifc use cases this feature would support include allowing `private` properties to receive synthesized memberwise parameters in a `public` initializer, or allow `public` properties to be omitted from parameter synthesis. An example of this @@ -436,7 +436,7 @@ Obviously supporting memberwise initialization with Cocoa classes would require This is a reasonable option and and I expect a healthy debate about which default is better. The decision to adopt the *automatic* model by default was made for several reasons: -1. The memberwise initializer for structs does not currently require an annotation for properties to opt-in. Requiring an annotation for a mechanism designed to supercede that mechanism may be viewed as boilerplate. +1. The memberwise initializer for structs does not currently require an annotation for properties to opt-in. Requiring an annotation for a mechanism designed to supersede that mechanism may be viewed as boilerplate. 2. Stored properties with public visibility are often intialized directly with a value provided by the caller. 3. Stored properties with **less visibility** than a memberwise initializer are not eligible for memberwise initialization. No annotation is required to indicate that and it is usually not desired. 4. The *automatic* model cannot exist unless it is the default. The *opt-in* model can exist alongside the *automatic* model and itself be opted-into simply by specifying the `memberwise` declaration modifier on one or more properties. @@ -462,7 +462,7 @@ Reasons to limit memberwise parameter synthesis to members which are *at least* 3. It is likely the more common desire of the author of an initializer. If the caller can’t see a member it probably doesn’t make sense to allow them to initialize it. 4. If we expose more private-members by default then memberwise initialization is useless under the current proposal in many cases. There would be no way to prevent synthesis of parameters for more-private members. We have to choose between allowing callers to initialize our internal state or forgoing the benefit of memberwise initialization. 5. If a proposal for `@nomemberwise` is put forward and adopted that would allow us to prevent synthesis of parameters for members as desired. Unfortunately `@nomemberwise` would need to be used much more heavily than it otherwise would (i.e. to prevent synthesis of memberwise parameters for more-private members). It would be better if `@nomemberwise` was not necessary most of the time. -6. If callers must be able to provide memberwise arguments for more-private members directly it is still possible to allow that while taking advantage of memberwise initialization for same-or-less-private members. You just need to declare a `memberwise init` with explicitly declared parameters for the more-private members and initialize them manually in the body. If the "Access control for init" enhancement is accepted another option would be upgrading the visibility of `init` for the more-private member while retaining its access level for the getter and setter. Requiring the programmer to explicitly expose a more-private member either via `init` access control or by writing code that it directly is a arguably a very good thing. +6. If callers must be able to provide memberwise arguments for more-private members directly it is still possible to allow that while taking advantage of memberwise initialization for same-or-less-private members. You just need to declare a `memberwise init` with explicitly declared parameters for the more-private members and initialize them manually in the body. If the "Access control for init" enhancement is accepted another option would be upgrading the visibility of `init` for the more-private member while retaining its access level for the getter and setter. Requiring the programmer to explicitly expose a more-private member either via `init` access control or by writing code that it directly is arguably a very good thing. Reasons we might want to allow memberwise parameter synthesis for members with lower visiblity than the initializer: diff --git a/proposals/0019-package-manager-testing.md b/proposals/0019-package-manager-testing.md index 98fd318b53..51c3583590 100644 --- a/proposals/0019-package-manager-testing.md +++ b/proposals/0019-package-manager-testing.md @@ -18,7 +18,7 @@ will help ensure a stable and reliable packaging ecosystem. ## Proposed Solution We propose to extend our conventional package directory layout -to accomodate test modules. +to accommodate test modules. Any subdirectory of the package root directory named "Tests" or any subdirectory of an existing module directory named "Tests" will comprise a test module. diff --git a/proposals/0035-limit-inout-capture.md b/proposals/0035-limit-inout-capture.md index fb0c91a950..5ea29ee066 100644 --- a/proposals/0035-limit-inout-capture.md +++ b/proposals/0035-limit-inout-capture.md @@ -63,7 +63,7 @@ one in a long line of complaints on the topic. ## Proposed solution -I propose we make it so that implicitly capturing an `inout` parameter into a escapable +I propose we make it so that implicitly capturing an `inout` parameter into an escapable closure is an error. We added the explicit `@noescape` annotation in Swift 1.2, and have since adopted it throughout the standard library where appropriate, so the compromise has outlived its usefulness and become a source of confusion. diff --git a/proposals/0038-swiftpm-c-language-targets.md b/proposals/0038-swiftpm-c-language-targets.md index 53d377148a..70fb6fbcbc 100644 --- a/proposals/0038-swiftpm-c-language-targets.md +++ b/proposals/0038-swiftpm-c-language-targets.md @@ -111,7 +111,7 @@ addressed with this proposal: 2. No provision is made in this proposal for controlling compiler arguments. We will support the existing debug and release configurations using a fixed set - of compiler flags. We expect future proposals to accomodate the need to + of compiler flags. We expect future proposals to accommodate the need to modify those flags. 3. We intend for the feature to be built in such a way as to support any From ab38d8cfa4d4b6bb582610a48463225e09fe064e Mon Sep 17 00:00:00 2001 From: Junwei Zhuge Date: Fri, 16 Sep 2016 12:36:04 -0400 Subject: [PATCH 0007/4563] proposal 0022 typo (#529) --- proposals/0022-objc-selectors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0022-objc-selectors.md b/proposals/0022-objc-selectors.md index e0569414ff..6daa7dfa79 100644 --- a/proposals/0022-objc-selectors.md +++ b/proposals/0022-objc-selectors.md @@ -53,7 +53,7 @@ having to do the naming translation manually and get static checking that the method exists and is exposed to Objective-C. This proposal composes with the [Naming Functions with Argument Labels -proposal](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006262.html), which lets us name methods along with their argument labels, e.g.: +proposal](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006262.html), which let us name methods along with their argument labels, e.g.: let sel = #selector(UIView.insertSubview(_:atIndex:)) // produces the Selector "insertSubview:atIndex:" From 3ae702d5a6a24d76b3ff2d29f88db8e60a781c9f Mon Sep 17 00:00:00 2001 From: Jacob Bandes-Storch Date: Fri, 16 Sep 2016 09:37:42 -0700 Subject: [PATCH 0008/4563] Revert mistaken typo correction --- proposals/0022-objc-selectors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0022-objc-selectors.md b/proposals/0022-objc-selectors.md index 6daa7dfa79..e0569414ff 100644 --- a/proposals/0022-objc-selectors.md +++ b/proposals/0022-objc-selectors.md @@ -53,7 +53,7 @@ having to do the naming translation manually and get static checking that the method exists and is exposed to Objective-C. This proposal composes with the [Naming Functions with Argument Labels -proposal](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006262.html), which let us name methods along with their argument labels, e.g.: +proposal](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006262.html), which lets us name methods along with their argument labels, e.g.: let sel = #selector(UIView.insertSubview(_:atIndex:)) // produces the Selector "insertSubview:atIndex:" From 9cf2685293108ea3efcbebb7ee6a8618b83d4a90 Mon Sep 17 00:00:00 2001 From: Jacob Bandes-Storch Date: Sat, 17 Sep 2016 10:13:02 -0700 Subject: [PATCH 0009/4563] =?UTF-8?q?[gardening]=20asymetric=20=E2=86=92?= =?UTF-8?q?=20asymmetric?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proposals/0107-unsaferawpointer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0107-unsaferawpointer.md b/proposals/0107-unsaferawpointer.md index eb1cae670d..a54561be4f 100644 --- a/proposals/0107-unsaferawpointer.md +++ b/proposals/0107-unsaferawpointer.md @@ -248,7 +248,7 @@ UnsafeMutableRawPointer { } ``` -The `load` and `storeBytes` operations are asymetric. `load` reads raw +The `load` and `storeBytes` operations are asymmetric. `load` reads raw bytes but properly constructs a new value of type `T` with its own lifetime. Any copied references will be retained. In contrast, `storeBytes` only operates on a value's raw bytes, writing them into From 56ce75a6e2182c94f73bd54341af1aa81713be08 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Wed, 21 Sep 2016 09:42:29 -0700 Subject: [PATCH 0010/4563] Update status of SE-0138. --- proposals/0138-unsaferawbufferpointer.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0138-unsaferawbufferpointer.md b/proposals/0138-unsaferawbufferpointer.md index 4abcc1a4b1..994a407556 100644 --- a/proposals/0138-unsaferawbufferpointer.md +++ b/proposals/0138-unsaferawbufferpointer.md @@ -2,8 +2,9 @@ * Proposal: [SE-0138](0138-unsaferawbufferpointer.md) * Author: [Andrew Trick](https://github.com/atrick) -* Status: **Active Review (August 31...September 14)** * Review manager: [Dave Abrahams](https://github.com/dabrahams) +* Status: **Accepted with revisions** +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160919/027167.html) Contents: - [Introduction](#introduction) From f53e1341c1b44445bcd2fb543c9d573f194030c7 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Wed, 21 Sep 2016 09:50:06 -0700 Subject: [PATCH 0011/4563] Update status of SE-0138. Fix-up for commit 56ce75a6e2. --- index.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.xml b/index.xml index f87caf7330..7a26beabe7 100644 --- a/index.xml +++ b/index.xml @@ -138,7 +138,7 @@ - + From 1202af531896d3e1708708ff09e4f6dd91d43f47 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Fri, 23 Sep 2016 14:50:51 -0700 Subject: [PATCH 0012/4563] SE-0140 has been implemented. --- proposals/0140-bridge-optional-to-nsnull.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0140-bridge-optional-to-nsnull.md b/proposals/0140-bridge-optional-to-nsnull.md index 7fa37ec43a..201af6ede0 100644 --- a/proposals/0140-bridge-optional-to-nsnull.md +++ b/proposals/0140-bridge-optional-to-nsnull.md @@ -3,7 +3,7 @@ * Proposal: [SE-0140](0140-bridge-optional-to-nsnull.md) * Author: [Joe Groff](https://github.com/jckarter) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 3.0.1)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160912/027062.html) ## Introduction From 81a025d8a272cb1d35d697b531105bc196d11c72 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Fri, 23 Sep 2016 15:09:01 -0700 Subject: [PATCH 0013/4563] Add proposal for @available(swift, ...) --- proposals/NNNN-available-by-swift-version.md | 109 +++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 proposals/NNNN-available-by-swift-version.md diff --git a/proposals/NNNN-available-by-swift-version.md b/proposals/NNNN-available-by-swift-version.md new file mode 100644 index 0000000000..5e09cb6f60 --- /dev/null +++ b/proposals/NNNN-available-by-swift-version.md @@ -0,0 +1,109 @@ +# Availability by Swift version + +* Proposal: [SE-NNNN](NNNN-available-by-swift-version.md) +* Authors: [Graydon Hoare](https://github.com/graydon) +* Review Manager: TBD +* Status: **Awaiting review** + +## Introduction + +Swift's existing `@available(...)` attribute indicates the lifecycle of a +given declaration, either unconditionally or relative to a particular +platform or OS version range. + +It does not currently support indicating declaration lifecycle relative to +Swift language versions. This proposal seeks to extend it to do so. + +## Motivation + +As the Swift language progresses from one version to the next, some +declarations will be added, renamed, deprecated or removed from the +standard library. Existing code written for earlier versions of Swift will +be supported through a `-swift-version N` command-line flag, that runs the +compiler in a backward-compatibility mode for the specified "effective" +language version. + +When running in a backward-compatibility mode, the set of available +standard library declarations should change to match expectations of older +code. Currently the only mechanism for testing a language version is the +compiler-control statement `#if swift(>= N)` which is a static construct: +it can be used to compile-out a declaration from the standard library, but +evolving the standard library through this mechanism would necessitate +compiling the standard library once for each supported older language +version. + +It would be preferable to compile the standard library _once_ for all +supported language versions, but make declarations _conditionally +available_ depending on the effective language version of a _user_ of the +library. The existing `@available(...)` attribute is similar to this +use-case, and this proposal seeks to extend the attribute to support it. + +## Proposed solution + +The `@available(...)` attribute will be extended to support specifying +`swift` version numbers, in addition to its existing platform versions. + +As an example, an API that is removed in Swift 3.1 will be written +as: + +~~~~ +@available(swift, obsoleted: 3.1) +class Foo { + //... +} +~~~~ + +When compiling _user code_ in `-swift-version 3.0` mode, this declaration +would be available, but not when compiling in subsequent versions. + +## Detailed design + +The token `swift` will be added to the set of valid initial arguments +to the `@available(...)` attribute. It will be treated similarly, +but slightly differently, than the existing platform arguments. In +particular: + + - As with platform-based availability judgments, a declaration's + `swift` version availability will default to available-everywhere + if unspecified. + + - A declaration's `swift` version availability will be considered + in logical conjunction with its platform-based availability. + That is, a given declaration will be available if and only + if it is _both_ available to the current effective `swift` version + _and_ available to the current deployment-target platform. + + - Similar to the abbreviated form of platform availability, an + abbreviated form `@available(swift N)` will be permitted as a synonym + for `@available(swift, introduced: N)`. However, adding `swift` to + a platform availability abbreviation list will not be allowed. That is, + writing the following examples is not permitted: + + - `@available(swift 3, *)` + - `@available(swift 3, iOS 10, *)` + + This restriction is due to the fact that platform-availability lists + are interpreted disjunctively (as a logical-_OR_ of their arguments), + and adding a conjunct (logical-_AND_) to such a list would make + the abbreviation potentially ambiguous to readers. + +## Impact on existing code + +Existing code does not use this form of attribute, so will not be +affected at declaration-site. + +As declarations are annotated as unavailable or obsoleted via +this attribute, some user code may stop working, but the same risk exists +(with a worse user experience) in today's language any time declarations +are removed or conditionally-compiled out. The purpose of this proposal +is to provide a better user experience around such changes, and facilitate +backward-compatibility modes. + +## Alternatives considered + +The main alternative is compiling libraries separately for each language +version and using `#if swift(>=N)` to conditionally include varying APIs. +For a library used locally within a single project, recompiling for a +specific language version may be appropriate, but for shipping the standard +library it is more economical to compile once with all declarations, and +select a subset based on language version. From 2f6853bda13eabcea7329a90b4a7d3539a28d6ee Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Fri, 23 Sep 2016 15:23:04 -0700 Subject: [PATCH 0014/4563] SE-0110 has been implemented. --- proposals/0110-distingish-single-tuple-arg.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0110-distingish-single-tuple-arg.md b/proposals/0110-distingish-single-tuple-arg.md index ee0388889e..fb3c16b486 100644 --- a/proposals/0110-distingish-single-tuple-arg.md +++ b/proposals/0110-distingish-single-tuple-arg.md @@ -3,7 +3,7 @@ * Proposal: [SE-0110](0110-distingish-single-tuple-arg.md) * Authors: Vladimir S., Austin Zheng * Review Manager: [Chris Lattner](http://github.com/lattner) -* Status: **Accepted** +* Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-July/000215.html) * Bug: [SR-2008](https://bugs.swift.org/browse/SR-2008) From ad4297443b3faae5db9bcb7a57c5f2970a14a172 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 23 Sep 2016 16:27:31 -0700 Subject: [PATCH 0015/4563] Initiate review of SE-0141: Availability by Swift version --- index.xml | 1 + ...-swift-version.md => 0141-available-by-swift-version.md} | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) rename proposals/{NNNN-available-by-swift-version.md => 0141-available-by-swift-version.md} (96%) diff --git a/index.xml b/index.xml index 7a26beabe7..a861250164 100644 --- a/index.xml +++ b/index.xml @@ -141,6 +141,7 @@ + diff --git a/index.xslt b/index.xslt index 65ad322c55..ab55bcad38 100644 --- a/index.xslt +++ b/index.xslt @@ -38,6 +38,11 @@ + + Implementation in progress + + + Implemented (Swift 4) @@ -246,6 +251,10 @@ a.number.status-implemented { background-color: #319021; } + a.number.status-implementing { + background-color: #5abc4e; + background: repeating-linear-gradient(135deg, #5abc4e, #5abc4e 14.29%, #319021 14.29%, #319021 28.57%); + } a.number.status-accepted { background-color: #5abc4e; } From 04ecd9bafa6d92156e6d8292bc29c27f86de0b13 Mon Sep 17 00:00:00 2001 From: Anders Bertelrud Date: Wed, 26 Oct 2016 15:29:32 -0700 Subject: [PATCH 0064/4563] Second version --- ...NNN-package-manager-product-definitions.md | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 proposals/NNNN-package-manager-product-definitions.md diff --git a/proposals/NNNN-package-manager-product-definitions.md b/proposals/NNNN-package-manager-product-definitions.md new file mode 100644 index 0000000000..d2749728ba --- /dev/null +++ b/proposals/NNNN-package-manager-product-definitions.md @@ -0,0 +1,211 @@ +# Package Manager Product Definitions + +* Proposal: [SE-NNNN](NNNN-package-manager-product-definitions.md) +* Author: [Anders Bertelrud](https://github.com/abertelrud) +* Status: **Awaiting review** +* Review manager: TBD + +## Introduction + +This proposal introduces the concept of *products* for Swift Package Manager packages, and proposes enhancements to the package manifest format to let packages define products that can be referenced by other packages. + +Swift-evolution thread: [Discussion thread topic for that proposal](http://news.gmane.org/gmane.comp.lang.swift.evolution) + +## Motivation + +Currently, the Swift Package Manager has only a limited notion of the products that are built for a package. It has a set of rules by which it infers implicit products based on the contents of various targets, and it also has a small amount of undocumented, unsupported package manifest syntax for explicitly declaring products, but it provides no supported way for package authors to declare the products of their packages. + +Also, while the Swift Package Manager currently supports dependencies between packages, it has no support for declaring a dependency on anything more fine-grained than a package. + +Such fine-grained dependencies are often desired, and this desire has in the past been expressed as a request for the ability to define a dependency on any arbitrary target of a package. That, in turn, leads to requests to provide access control for targets, since a package author may want control over which targets can be independently accessed from outside the package. + +Even if visibility control for targets were to be provided (indeed, an early draft of the package manifest had the notion of "publishing" a target to external clients), there would still be no way for the package author to declare anything about the kind of product that should be built (such as the kind of library that should be produced). + +One consequence of the lack of ability to define dependencies at sub-package granularity is that package authors have to break up packages into several smaller packages in order to achieve the layering they want. This may be appropriate in some cases, but should be done based on what makes sense from a conceptual standpoint and not because of limitations of expressibility in the Swift Package Manager. + +For example, consider the package for a component that has both a library API and command line tools. Such a package is also likely to be layered into a set of core libraries that are dependencies of both the public library and the command line tools, but that should remain a private implementation detail as far as clients are concerned. + +Such a package would need to be split up into three separate packages in order to provide the appropriate dependency granularity: one for the public library, another for the command line tools, and a third, private package that provides the shared implementation for the other two packages. In the case of a single conceptual component that should have a single version number, this fracturing into multiple packages is directly opposed to the developer's preferred manner of packaging. + +What is needed is a way to allow package authors to declare conceptually distinct products of a package, and to allow client packages to define dependencies on individual products in a package. + +Furthermore, explicit product definitions allow the package author to control the types of artifacts produced from the targets in the package. This can include such things as whether a library is built as a static or a dynamic library, and we expect that additional product types will be added over time to let package authors build more kinds of artifacts. + +## Proposed solution + +We will introduce a documented and supported concept of a package product, along with package manifest improvements to let package authors define products and to let package clients define dependencies on products. In defining a product, a package author will be able to specify the type of product and a set of characteristics appropriate for the type. + +### Product definitions + +A package will be able to define an arbitrary number of products that are visible to all clients of the package. A product definition includes the type of product, the name (which must be unique among the products in the package), and the root set of targets that comprise the implementation of the product. + +Any target may be included in multiple products, though not all kinds of target apply to all products; for example, a test target is not able to be included in a library product. + +The products represent the publicly vended package outputs on which any client package can depend, and also define the characteristics of the produced artifacts. + +As mentioned earlier, Swift Package Manager already infers a set of implicit products based on the set of targets in the package. The inference rules will be supported and documented as part of this feature; this will allow all existing packages to continue to work unmodified, and will handle simple packages without a lot of extra work on the part of the author. Packages will only need to provide explicit product definitions when the intended products differ from what the Swift Package Manager would infer. + +The inference rules are detailed in the next section of this document. Products will only be inferred if there are no explicit product definitions in the package; in considering the various ways in which a mixture of implicit and explicit products could interact, this all-or-nothing condition has turned out to be the only policy that is simple and understandable enough to be workable. + +An example of a package containing explicit product definitions: + +``` +let package = Package( + name: "Hello", + targets: [ + Target(name: "Foo"), + Target(name: "Bar"), + Target(name: "Baz", dependencies: ["Foo", "Bar"]), + Target(name: "Exe", dependencies: ["Foo"]) + ], + products: [ + LibraryProduct(name: "Lib1", type: .static, targets: ["Bar"]), + LibraryProduct(name: "Lib2", type: .dynamic, targets: ["Baz"]), + ExecutableProduct(name: "Exe", targets: ["Exe"]), + TestProduct(name: "HelloTests", targets: ["ExeTests", "FooTests", "BarTests", "BazTests"]) + ] +) +``` + +Note that the test product is explicitly defined, since the presence of explicit product definitions prevents the inference of implicit products. + +*[NOTE: I expect that we will get a lot of pushback on this; it's particularly tedious to have to list all the test targets. Is this really what we want to go with? Test products, in particular, feel "different" somehow.]* + +The initial types of products that can be defined are executables, libraries, and tests. Libraries can be declared as static or dynamic, but when possible, the specific type of library should be omitted -- in this case, the build system will choose the most appropriate type to build based on the context in which the product will be used (depedning on the type of client, the platform, etc). + +A product definition lists the root targets to include in the product; the interfaces of those targets will be available to any clients (for product types where that makes sense). Any dependencies of those targets will also be included in the product, but won't be made visible to clients. The Swift compiler does not currently provide this granularity of visibility control, but it constitutes a declaration of intent that can be used by IDEs and other tools. We also hope that the compiler will one day support this level of visibility control. + +Any other targets on which the root targets depend will also be included in the product, but their interfaces will not be made available to clients. + +### Product dependencies + +A target will be able to declare its use of products defined by any of the package dependencies. This is in addition to the existing ability to depend on other targets in the same package. + +This is done through a new optional array parameter when instantiating a target: + +``` +Target(name: "Foo", dependencies: ["Bar", "Baz"], productUses: ["SomeProduct"]) +``` + +*[NOTE: We still need to make a final decision about this: one array or two? If two, then what names? If one array, then how do we distinguish products and targets?]* + +## Detailed design + +### Product definitions + +We will add a `products` parameter to the `Package()` initializer: + +``` +Package( + name: String, + pkgConfig: String? = nil, + providers: [SystemPackageProvider]? = nil, + targets: [Target] = [], + dependencies: [Package.Dependency] = [], + products: [Product]? = nil, + exclude: [String] = [] +) +``` + +The definition of the `Product` type will be: + +``` +public class Product { + public let name: String + public let targets: [String] + + public init(name: String, targets: [String]) { + self.name = name + self.targets = targets + } +} + +public class ExecutableProduct : Product { + // nothing else at this time +} + +public class LibraryProduct : Product { + public enum LibraryType { + case .static + case .dynamic + } + public let type: LibraryType? + + public init(name: String, type: LibraryType? = nil, targets: [String]) { + super.init(name: name, targets: targets) + self.type = type + } +} + +public class TestProduct : Product { + // nothing else at this time +} +``` + +*[QUESTION: Should `Product` be inside an `enum` for namespacing purposes?]* + +*[NOTE: I'm not particularly happy with the empty definitions of some of the product classes. Still, I do think that there is a conceptual hierarchy of product types, not just an enum. This will make more sense a the set of possible types of products grow, but is a bit weird right now.]* + +As with targets, there is no semantic significance to the order of the products in the array. + +### Implicit products + +If the `products` array is omitted, the Swift Package Manager will infer a set of implicit products based on the targets in the package. + +The rules for implicit products are: + +1. An executable product is implicitly defined for any target that produces an executable module (as defined here: https://github.com/apple/swift-package-manager/blob/master/Documentation/Reference.md). + +2. A library product is implicitly defined for any target that produces a library module. + +3. A test product is implicitly defined for any test target. + +As mentioned earlier, the implicit products are only inferred if the package contains no explicit product definitions. This is true even for test products. + +### Product dependencies + +We will add a `usedProducts` parameter to the `Target` initializer. This will allow any target to depend on products specified in other packages. + +``` +public final class Target { + /// A dependency on an individual target. + public enum TargetDependency { + /// A dependency on a target in the same project. + case Target(name: String) + } + + /// A use of a particular product. + public enum ProductUse { + /// A use of a product. If a package name is provided, it must match the name of one of the packages named in a `.Package()` directive. + case Product(name: String, package: String?) + } + + /// The name of the target. + public let name: String + + /// Dependencies on other targets in the package. + public var dependencies: [TargetDependency] + + /// Uses of products from other packages. + public var productUses: [ProductUse] + + /// Construct a target. + public init(name: String, dependencies: [TargetDependency] = [], productUses: [ProductUse] = []) { + self.name = name + self.dependencies = dependencies + self.productUses = productUses + } +} +``` + +*[ISSUE: I'm not particularly happy with where we ended up here. The terms "dependency" and "use" are not at all obvious, and apart from the short-name form, it isn't at all obvious that these dependencies are conceptually different. In this case, I think the original API had it right in preparing for being able to have different kinds of dependencies. I'm starting to go back toward thinking that we should model it correctly and then find some notation for the string short-form.]* + +In the absence of any product dependencies, the entire package will be considered to be dependent on all of the packages on which it depends. If the package contains at least one other product dependency, then all targets that depend on products from other packages will need to have product dependencies specified. + +*[ISSUE: This seems like another fairly unfortunate drop-off-a-cliff semantic, but short of adding a boolean to each `.Package()` declaration to say whether to depend on everything in that package, I don't know of a better solution. I'd like to discuss this a little bit more before broadcasting this proposal.]* + +## Impact on existing code + +There will be no impact on existing packages that follow the documented format of the package manifest. The Swift Package Manager will continue to infer products based the existing. + +We could also support packages that use the current undocumented product support by continuing to support the current `Product` types as a façade on the new API. From a0040c602057e7e7f8f2525f5af40a0b1de24246 Mon Sep 17 00:00:00 2001 From: Anders Bertelrud Date: Sun, 30 Oct 2016 22:32:55 -0700 Subject: [PATCH 0065/4563] Updated based on feedback. Added alternatives for cases in which we haven't made a decision, and added an Open Questions section as well as Alternatives Considered. --- ...NNN-package-manager-product-definitions.md | 240 ++++++++++++------ 1 file changed, 161 insertions(+), 79 deletions(-) diff --git a/proposals/NNNN-package-manager-product-definitions.md b/proposals/NNNN-package-manager-product-definitions.md index d2749728ba..563a1d7937 100644 --- a/proposals/NNNN-package-manager-product-definitions.md +++ b/proposals/NNNN-package-manager-product-definitions.md @@ -13,7 +13,7 @@ Swift-evolution thread: [Discussion thread topic for that proposal](http://news. ## Motivation -Currently, the Swift Package Manager has only a limited notion of the products that are built for a package. It has a set of rules by which it infers implicit products based on the contents of various targets, and it also has a small amount of undocumented, unsupported package manifest syntax for explicitly declaring products, but it provides no supported way for package authors to declare the products of their packages. +Currently, the Swift Package Manager has only a limited notion of the products that are built for a package. It has a set of rules by which it infers implicit products based on the contents of various targets, and it also has a small amount of undocumented, unsupported package manifest syntax for explicitly declaring products, but it provides no supported way for package authors to declare what their packages produce. Also, while the Swift Package Manager currently supports dependencies between packages, it has no support for declaring a dependency on anything more fine-grained than a package. @@ -39,13 +39,15 @@ We will introduce a documented and supported concept of a package product, along A package will be able to define an arbitrary number of products that are visible to all clients of the package. A product definition includes the type of product, the name (which must be unique among the products in the package), and the root set of targets that comprise the implementation of the product. -Any target may be included in multiple products, though not all kinds of target apply to all products; for example, a test target is not able to be included in a library product. +Any target may be included in multiple products, though not all kinds of targets apply to all products; for example, a test target is not able to be included in a library product. The products represent the publicly vended package outputs on which any client package can depend, and also define the characteristics of the produced artifacts. -As mentioned earlier, Swift Package Manager already infers a set of implicit products based on the set of targets in the package. The inference rules will be supported and documented as part of this feature; this will allow all existing packages to continue to work unmodified, and will handle simple packages without a lot of extra work on the part of the author. Packages will only need to provide explicit product definitions when the intended products differ from what the Swift Package Manager would infer. +As mentioned earlier, Swift Package Manager already infers a set of implicit products based on the set of targets in the package. One open question is whether inference rules will be supported and documented as part of this feature, in order to allow existing packages to continue to work unmodified and to avoid the need for explicit product definitions for simple packages. While the inference rules will need to be supported for existing packages, it is still an open question whether product will be inferred for packages using the Swift Pacakge Manager 4.0 version of the API. -The inference rules are detailed in the next section of this document. Products will only be inferred if there are no explicit product definitions in the package; in considering the various ways in which a mixture of implicit and explicit products could interact, this all-or-nothing condition has turned out to be the only policy that is simple and understandable enough to be workable. +The inference rules that apply to legacy packages are detailed in the next section of this document. + +Even if Swift Pacakge Manager 4.0 does infer products for non-legacy packages, products will only be inferred if there are no explicit product definitions in the package. The reason is that the interaction of a mixture of implicit and explicit products is likely to lead to significant complexity and to be a source of both confusion and bugs. An example of a package containing explicit product definitions: @@ -59,35 +61,81 @@ let package = Package( Target(name: "Exe", dependencies: ["Foo"]) ], products: [ - LibraryProduct(name: "Lib1", type: .static, targets: ["Bar"]), - LibraryProduct(name: "Lib2", type: .dynamic, targets: ["Baz"]), - ExecutableProduct(name: "Exe", targets: ["Exe"]), - TestProduct(name: "HelloTests", targets: ["ExeTests", "FooTests", "BarTests", "BazTests"]) + Library(name: "Lib1", type: .static, targets: ["Bar"]), + Library(name: "Lib2", type: .dynamic, targets: ["Baz"]), + Executable(name: "Exe", targets: ["Exe"]), ] ) ``` -Note that the test product is explicitly defined, since the presence of explicit product definitions prevents the inference of implicit products. - -*[NOTE: I expect that we will get a lot of pushback on this; it's particularly tedious to have to list all the test targets. Is this really what we want to go with? Test products, in particular, feel "different" somehow.]* +The initial types of products that can be defined are executables and libraries. Libraries can be declared as static or dynamic, but when possible, the specific type of library should be omitted -- in this case, the build system will choose the most appropriate type to build based on the context in which the product will be used (depedning on the type of client, the platform, etc). -The initial types of products that can be defined are executables, libraries, and tests. Libraries can be declared as static or dynamic, but when possible, the specific type of library should be omitted -- in this case, the build system will choose the most appropriate type to build based on the context in which the product will be used (depedning on the type of client, the platform, etc). +Note that tests are not considered to be products, and do not need to be explicitly defined. A product definition lists the root targets to include in the product; the interfaces of those targets will be available to any clients (for product types where that makes sense). Any dependencies of those targets will also be included in the product, but won't be made visible to clients. The Swift compiler does not currently provide this granularity of visibility control, but it constitutes a declaration of intent that can be used by IDEs and other tools. We also hope that the compiler will one day support this level of visibility control. Any other targets on which the root targets depend will also be included in the product, but their interfaces will not be made available to clients. +For example, in the package definition shown above, the library product `Lib2` would only vend the interface of `Baz` to clients. Since `Baz` depends on `Foo` and `Bar`, those two targets would also be compiled and linked into `Lib2`, but their interfaces should not be visible to clients of `Lib2`. + ### Product dependencies -A target will be able to declare its use of products defined by any of the package dependencies. This is in addition to the existing ability to depend on other targets in the same package. +A target will be able to declare its use of the products that are defined in any of the external package dependencies. This is in addition to the existing ability to depend on other targets in the same package. -This is done through a new optional array parameter when instantiating a target: +There are two basic approaches for how to represent this. As noted in the "Open questions" section below, more discussion is needed before we can choose which of these approaches to recommend: -``` -Target(name: "Foo", dependencies: ["Bar", "Baz"], productUses: ["SomeProduct"]) -``` +1. Extending the notion of the types of dependencies that can be listed in a target definition's `dependencies` parameter. + + To see how this works, remember that each string listed for the `dependencies` parameter of a target is just shorthand for a `.target(name: "...")` parameter, i.e. a dependency on a target within the same package. + + For example, the target definition: + + ``` + Target(name: "Foo", dependencies: ["Bar", "Baz"]) + ``` + + is shorthand for: + + ``` + Target(name: "Foo", dependencies: [.target(name: "Bar"), .target(name: "Baz")]) + ``` + + This could be extended to support product dependencies in addition to target dependencies. + + ``` + let package = Package( + name: "Hello", + dependencies: [ + .Package(url: "https://github.com/ExamplePackage", majorVersion: 1), + ], + targets: [ + Target(name: "Foo"), + Target(name: "Bar", dependencies: [ + .target(name: "Foo"), + .product(name: "Baz", package: "ExamplePackage") + ]) + ], + ) + ``` + + In this example, the package name is the short name of the package as defined by the package itself. A possibility would be to allow the package parameter to be optional when it is unambiguous. + + A significant drawback of this approach is the loss of an ability to use strings as unambiguous shorthand for `.target(name: "...")` values. To remedy this, strings could still be allowed, as long as there was a clear way to disambiguate the dependency in cases in which two or more entities have the same name. + + If we look at practical use cases, it seems rare that two completely unrelated entities (e.g. a target and a product) would have the same name. For example, if a target depends on `libAlamofire`, it doesn't actually matter whether it's the target or the product that is the dependency. So in most cases, a string would unambiguously capture the intent of the package author. If the shorthand form followed a search order, such as: + + - first look for a target with the specified name in the same package + - second, look for a product with the specified name in any package dependency -*[NOTE: We still need to make a final decision about this: one array or two? If two, then what names? If one array, then how do we distinguish products and targets?]* + then it should be possible for the shorthand notation to be sufficient in the vast majority of cases, with the possibility of using the more verbose form in the cases in which the shorthand form really is ambiguous. + +2. Alternatively, a new optional array parameter could be added to the instantiation of a target: + + ``` + Target(name: "Foo", dependencies: ["Bar", "Baz"], externalDependencies: ["SomeProduct"]) + ``` + + If we go this route, one of the difficulties is in choosing good names from the arrays. Also, we would still need to determine whether it would be sufficient to list strings in the `externalDependencies` parameter, or whether `(, )` tuples would be needed. ## Detailed design @@ -110,41 +158,32 @@ Package( The definition of the `Product` type will be: ``` -public class Product { - public let name: String - public let targets: [String] +public enum Product { + public class AbstractProduct { + public let name: String + public let targets: [String] + + public init(name: String, targets: [String]) + } - public init(name: String, targets: [String]) { - self.name = name - self.targets = targets + public class Executable : AbstractProduct { + public init(name: String, targets: [String]) + } + + public class Library : AbstractProduct { + public let type: LibraryType? + + public init(name: String, type: LibraryType? = nil, targets: [String]) } -} - -public class ExecutableProduct : Product { - // nothing else at this time -} -public class LibraryProduct : Product { public enum LibraryType { case .static case .dynamic } - public let type: LibraryType? - - public init(name: String, type: LibraryType? = nil, targets: [String]) { - super.init(name: name, targets: targets) - self.type = type - } -} - -public class TestProduct : Product { - // nothing else at this time } ``` -*[QUESTION: Should `Product` be inside an `enum` for namespacing purposes?]* - -*[NOTE: I'm not particularly happy with the empty definitions of some of the product classes. Still, I do think that there is a conceptual hierarchy of product types, not just an enum. This will make more sense a the set of possible types of products grow, but is a bit weird right now.]* +The namespacing allows the names of the product types to be kept short. As with targets, there is no semantic significance to the order of the products in the array. @@ -156,56 +195,99 @@ The rules for implicit products are: 1. An executable product is implicitly defined for any target that produces an executable module (as defined here: https://github.com/apple/swift-package-manager/blob/master/Documentation/Reference.md). -2. A library product is implicitly defined for any target that produces a library module. +2. If there are any library targets, a single library product is implicitly defined for all the library targets. The name of this product is based on the name of the package. -3. A test product is implicitly defined for any test target. +### Product dependencies -As mentioned earlier, the implicit products are only inferred if the package contains no explicit product definitions. This is true even for test products. +Depending on the decision about whether to extend the meaning of the `dependencies` parameter to target initialization or to add a new `externalDependencies` parameter, we will take one of these two approaches: -### Product dependencies +1. For the extended `dependencies` parameter approach: Add a new enum case to the `TargetDependency` enum to handle the case of dependencies on products, and another enum case to represent an unbound by-name dependency. Modify the string-literal conversion to create a by-name dependency instead of a target dependency, and add logic to bind the by-name dependencies to either target or product dependencies once the package graph has been resolved. -We will add a `usedProducts` parameter to the `Target` initializer. This will allow any target to depend on products specified in other packages. + Alternatively, we could handle the case of unbound by-name dependencies by initially recording them as dependencies on targets (as today), and by then converting them to dependencies on products if appropriate after the package has been loaded: -``` -public final class Target { - /// A dependency on an individual target. - public enum TargetDependency { - /// A dependency on a target in the same project. - case Target(name: String) - } + ``` + public final class Target { + /// Represents a dependency on another entity. + public enum TargetDependency { + /// A dependency on a target in the same project. + case Target(name: String) + /// A dependency on a product from a package dependency. If a package name is provided, it must match the name of one of the packages named in a `.Package()` directive. + case Product(name: String, package: String) + } + + /// The name of the target. + public let name: String - /// A use of a particular product. - public enum ProductUse { - /// A use of a product. If a package name is provided, it must match the name of one of the packages named in a `.Package()` directive. - case Product(name: String, package: String?) + /// Dependencies on other entities inside or outside the package. + public var dependencies: [TargetDependency] + + /// Construct a target. + public init(name: String, dependencies: [TargetDependency] = []) } + ``` + +2. For the separate `externalDependencies` parameter approach: We will add an `externalDependencies ` parameter to the `Target` initializer, along with additional type definitions to support it: + + ``` + public final class Target { + /// Represents a dependency on another target. + public enum TargetDependency { + /// A dependency on a target in the same project. + case Target(name: String) + } + + /// Represents a dependency on a product in an external package. + public enum ExternalTargetDependency { + /// A dependency on a product from a package dependency. If a package name is provided, it must match the name of one of the packages named in a `.Package()` directive. + case Product(name: String, package: String?) + } + + /// The name of the target. + public let name: String + + /// Dependencies on other targets in the package. + public var dependencies: [TargetDependency] - /// The name of the target. - public let name: String + /// Dependencies on products in external packages. + public var externalDependencies: [ExternalTargetDependency] + + /// Construct a target. + public init(name: String, dependencies: [TargetDependency] = [], externalDependencies: [ExternalTargetDependency] = []) + } + ``` - /// Dependencies on other targets in the package. - public var dependencies: [TargetDependency] +In the absence of any product dependencies, the entire package will be considered to be dependent on all of the packages on which it depends. If the package contains at least one other product dependency, then all targets that depend on products from other packages will need to have product dependencies specified. - /// Uses of products from other packages. - public var productUses: [ProductUse] +*[ISSUE: This seems like a fairly unfortunate drop-off-a-cliff semantic, but short of adding a boolean to each `.Package()` declaration to say whether to depend on everything in that package, I don't know of a better solution. I'd like to discuss this a little bit more before broadcasting this proposal.]* - /// Construct a target. - public init(name: String, dependencies: [TargetDependency] = [], productUses: [ProductUse] = []) { - self.name = name - self.dependencies = dependencies - self.productUses = productUses - } -} -``` +## Impact on existing code -*[ISSUE: I'm not particularly happy with where we ended up here. The terms "dependency" and "use" are not at all obvious, and apart from the short-name form, it isn't at all obvious that these dependencies are conceptually different. In this case, I think the original API had it right in preparing for being able to have different kinds of dependencies. I'm starting to go back toward thinking that we should model it correctly and then find some notation for the string short-form.]* +There will be no impact on existing packages that follow the documented Swift Package Manager 3.0 format of the package manifest. The Swift Package Manager will continue to infer products based the existing rules. We could also support packages that use the current undocumented product support by continuing to support the current `Product` types as a façade on the new API, but this does not seem worth the effort. -In the absence of any product dependencies, the entire package will be considered to be dependent on all of the packages on which it depends. If the package contains at least one other product dependency, then all targets that depend on products from other packages will need to have product dependencies specified. +If we decide to completely deprecate the implied products in the Swift Package Manager 4.0 API, then that will only affect packages once they upgrade their manifests to the SwiftPM 4.0 API. -*[ISSUE: This seems like another fairly unfortunate drop-off-a-cliff semantic, but short of adding a boolean to each `.Package()` declaration to say whether to depend on everything in that package, I don't know of a better solution. I'd like to discuss this a little bit more before broadcasting this proposal.]* +## Alternatives considered -## Impact on existing code +Instead of product definitions, fine-grained dependencies could be introduced by allowing targets to be marked as public or private to the package. This would not provide any way for a package author to control the type and characteristics of the various artifacts, however. Relying on the implied products that result from the inference rules is not likely to be scalable in the long term as we introduce new kinds of product types. -There will be no impact on existing packages that follow the documented format of the package manifest. The Swift Package Manager will continue to infer products based the existing. +*[As we address the open questions, the approaches not chosen will be described here]* -We could also support packages that use the current undocumented product support by continuing to support the current `Product` types as a façade on the new API. +## Open questions + +1. Should tests be considered to be products? + + Pro: Since tests are artifacts that can be produced during a build, just like products are, it may make sense for users to be able to express opinions about them in the same manner as for products. + + Con: Tests are conceptually different from products in their intended use, and it is much less likely that users will have specific opinions about the details of how tests are built. It seems conceptually cleaner to treat tests as a different kind of artifact than products. + +2. Should there be any inference of products at all? + + Pro: It can be much more convenient to not have to declare products, and to be able to add new products just by adding or renaming files and directories. + + Con: The very fact that it is so easy to change the set of products without modifying the package manifest can lead to unexpected behavior due to seemingly unrelated changes (e.g. creating a file called "main.swift" in a module changes that module from a library to an executable). + +3. Should target dependencies on products be separate from dependencies on other targets? + + Pro: Targets and products are different concepts, and it is possible for a target and a product to have the same name. This can lead to ambiguity. + + Con: Conceptually the *dependency* itself is the same concept, even if type of entity being depended on is technically different. In both cases (target and product), it is up to the build system to determine the exact type of artifacts that should be produced. In most cases, there is no actual semantic ambiguity, since the name of a target, product, and package are often the same uniquely identifiable "brand" name of the component. From 967275c266cf0007d044d957ace82e9eb12570da Mon Sep 17 00:00:00 2001 From: Anders Bertelrud Date: Fri, 4 Nov 2016 00:03:20 -0700 Subject: [PATCH 0066/4563] Significant update; instead of presenting all the choices inline, this proposal now takes a stance on the direction to go. The approaches not followed are listed in a separate section. --- ...NNN-package-manager-product-definitions.md | 291 ++++++++---------- 1 file changed, 128 insertions(+), 163 deletions(-) diff --git a/proposals/NNNN-package-manager-product-definitions.md b/proposals/NNNN-package-manager-product-definitions.md index 563a1d7937..d2ac016642 100644 --- a/proposals/NNNN-package-manager-product-definitions.md +++ b/proposals/NNNN-package-manager-product-definitions.md @@ -7,25 +7,25 @@ ## Introduction -This proposal introduces the concept of *products* for Swift Package Manager packages, and proposes enhancements to the package manifest format to let packages define products that can be referenced by other packages. +This proposal introduces the concept of *products* to the Swift Package Manager, and proposes enhancements to the `Package.swift` syntax to let packages define products that can be referenced by other packages. Swift-evolution thread: [Discussion thread topic for that proposal](http://news.gmane.org/gmane.comp.lang.swift.evolution) ## Motivation -Currently, the Swift Package Manager has only a limited notion of the products that are built for a package. It has a set of rules by which it infers implicit products based on the contents of various targets, and it also has a small amount of undocumented, unsupported package manifest syntax for explicitly declaring products, but it provides no supported way for package authors to declare what their packages produce. +Currently, the Swift Package Manager has only a limited notion of the products that are built for a package. It has a set of rules by which it infers implicit products based on the contents of the targets in the package, and it also has a small amount of undocumented, unsupported package manifest syntax for explicitly declaring products, but it provides no supported way for package authors to declare what their packages produce. Also, while the Swift Package Manager currently supports dependencies between packages, it has no support for declaring a dependency on anything more fine-grained than a package. -Such fine-grained dependencies are often desired, and this desire has in the past been expressed as a request for the ability to define a dependency on any arbitrary target of a package. That, in turn, leads to requests to provide access control for targets, since a package author may want control over which targets can be independently accessed from outside the package. +Such fine-grained dependencies are often desired, and this desire has in the past been expressed as a request for the ability to define a dependency on an arbitrary target in a package. That, in turn, leads to requests to provide access control for targets, since a package author may want control over which targets can be independently accessed from outside the package. -Even if visibility control for targets were to be provided (indeed, an early draft of the package manifest had the notion of "publishing" a target to external clients), there would still be no way for the package author to declare anything about the kind of product that should be built (such as the kind of library that should be produced). +Even if visibility control for targets were to be provided (indeed, an early draft of the package manifest syntax had the notion of "publishing" a target to external clients), there would still be no way for the package author to declare anything about the kind of product that should be built. -One consequence of the lack of ability to define dependencies at sub-package granularity is that package authors have to break up packages into several smaller packages in order to achieve the layering they want. This may be appropriate in some cases, but should be done based on what makes sense from a conceptual standpoint and not because of limitations of expressibility in the Swift Package Manager. +One consequence of the lack of ability to define dependencies at subpackage granularity is that package authors have to break up packages into several smaller packages in order to achieve the layering they want. Such package decomposition may be appropriate in some cases, but should be done based on what makes sense from a conceptual standpoint and not because the Swift Package Manager doesn't allow the author to express their intent. -For example, consider the package for a component that has both a library API and command line tools. Such a package is also likely to be layered into a set of core libraries that are dependencies of both the public library and the command line tools, but that should remain a private implementation detail as far as clients are concerned. +For example, consider the package for a component that has both a library API and command line tools. Such a package is also likely to be partitioned into a set of core libraries on which both the public library and the command line tools depend, but which should remain a private implementation detail as far as clients are concerned. -Such a package would need to be split up into three separate packages in order to provide the appropriate dependency granularity: one for the public library, another for the command line tools, and a third, private package that provides the shared implementation for the other two packages. In the case of a single conceptual component that should have a single version number, this fracturing into multiple packages is directly opposed to the developer's preferred manner of packaging. +Such a package would currently need to be split up into three separate packages in order to provide the appropriate dependency granularity: one for the public library, another for the command line tools, and a third, private package to provide the shared implementation used by the other two packages. In the case of a single conceptual component that should have a single version number, this fracturing into multiple packages is directly contrary to the developer's preferred manner of packaging. What is needed is a way to allow package authors to declare conceptually distinct products of a package, and to allow client packages to define dependencies on individual products in a package. @@ -33,109 +33,97 @@ Furthermore, explicit product definitions allow the package author to control th ## Proposed solution -We will introduce a documented and supported concept of a package product, along with package manifest improvements to let package authors define products and to let package clients define dependencies on products. In defining a product, a package author will be able to specify the type of product and a set of characteristics appropriate for the type. +We will introduce a documented and supported concept of a package product, along with package manifest improvements to let package authors define products and to let package clients define dependencies on such products. In defining a product, a package author will be able specify the type of product as well as its characteristics. ### Product definitions -A package will be able to define an arbitrary number of products that are visible to all clients of the package. A product definition includes the type of product, the name (which must be unique among the products in the package), and the root set of targets that comprise the implementation of the product. +A package will be able to define an arbitrary number of products that are visible to all direct clients of the package. A product definition consists of a product type, a name (which must be unique among the products in the package), and the root targets that comprise the implementation of the product. There may also be additional properties depending on the type of product (for example, a library may be static or dynamic). -Any target may be included in multiple products, though not all kinds of targets apply to all products; for example, a test target is not able to be included in a library product. +Any target may be included in multiple products, though not all kinds of targets are usable in any kind of product; for example, a test target is not able to be included in a library product. -The products represent the publicly vended package outputs on which any client package can depend, and also define the characteristics of the produced artifacts. +The products represent the publicly vended package outputs on which any client package can depend. Other artifacts might also be created as part of building the package, but what the products specifically define are the conceptual "outputs" of the package, i.e. those that make sense to think of as something a client package can depend on and use. Examples of artifacts that are not necessarily products include built unit tests and helper tools that are used only during the build of the package. -As mentioned earlier, Swift Package Manager already infers a set of implicit products based on the set of targets in the package. One open question is whether inference rules will be supported and documented as part of this feature, in order to allow existing packages to continue to work unmodified and to avoid the need for explicit product definitions for simple packages. While the inference rules will need to be supported for existing packages, it is still an open question whether product will be inferred for packages using the Swift Pacakge Manager 4.0 version of the API. - -The inference rules that apply to legacy packages are detailed in the next section of this document. - -Even if Swift Pacakge Manager 4.0 does infer products for non-legacy packages, products will only be inferred if there are no explicit product definitions in the package. The reason is that the interaction of a mixture of implicit and explicit products is likely to lead to significant complexity and to be a source of both confusion and bugs. - -An example of a package containing explicit product definitions: +An example of a package that defines two library products and one executable product: ``` let package = Package( - name: "Hello", + name: "MyServer", targets: [ - Target(name: "Foo"), - Target(name: "Bar"), - Target(name: "Baz", dependencies: ["Foo", "Bar"]), - Target(name: "Exe", dependencies: ["Foo"]) + Target(name: "Utils"), + Target(name: "HTTP", dependencies: ["Utils"]), + Target(name: "ClientAPI", dependencies: ["HTTP", "Utils"]), + Target(name: "ServerAPI", dependencies: ["HTTP"]), + Target(name: "ServerDaemon", dependencies: ["ServerAPI"]) ], products: [ - Library(name: "Lib1", type: .static, targets: ["Bar"]), - Library(name: "Lib2", type: .dynamic, targets: ["Baz"]), - Executable(name: "Exe", targets: ["Exe"]), + Library(name: "ClientLib", type: .static, targets: ["ClientAPI"]), + Library(name: "ServerLib", type: .dynamic, targets: ["ServerAPI"]), + Executable(name: "myserver", targets: ["ServerDaemon"]), ] ) ``` -The initial types of products that can be defined are executables and libraries. Libraries can be declared as static or dynamic, but when possible, the specific type of library should be omitted -- in this case, the build system will choose the most appropriate type to build based on the context in which the product will be used (depedning on the type of client, the platform, etc). +The initial types of products that can be defined are executables and libraries. Libraries can be declared as either static or dynamic, but when possible, the specific type of library should be left unspecified, letting the build system chooses a suitable default for the platform. Note that tests are not considered to be products, and do not need to be explicitly defined. -A product definition lists the root targets to include in the product; the interfaces of those targets will be available to any clients (for product types where that makes sense). Any dependencies of those targets will also be included in the product, but won't be made visible to clients. The Swift compiler does not currently provide this granularity of visibility control, but it constitutes a declaration of intent that can be used by IDEs and other tools. We also hope that the compiler will one day support this level of visibility control. +A product definition lists the root targets to include in the product; for product types that vend interfaces (e.g. libraries), the root targets are those whose modules will be available to clients. Any dependencies of those targets will also be included in the product, but won't be made visible to clients. The Swift compiler does not currently provide this granularity of module visibility control, but the set of root targets still constitutes a declaration of intent that can be used by IDEs and other tools. We also hope that the compiler will one day support this level of visibility control. -Any other targets on which the root targets depend will also be included in the product, but their interfaces will not be made available to clients. +For example, in the package definition shown above, the library product `ClientLib` would only vend the interface of the `ClientAPI` module to clients. Since `ClientAPI` depends on `HTTP` and `Utilities`, those two targets would also be compiled and linked into `ClientLib`, but their interfaces should not be visible to clients of `ClientLib`. -For example, in the package definition shown above, the library product `Lib2` would only vend the interface of `Baz` to clients. Since `Baz` depends on `Foo` and `Bar`, those two targets would also be compiled and linked into `Lib2`, but their interfaces should not be visible to clients of `Lib2`. +### Implicit products -### Product dependencies +SwiftPM 3 applies a set of rules to infer products based on the targets in the package. For backward compatibility, SwiftPM 4 will apply the same rules to packages that use the SwiftPM 3 PackageDescription API. The `package describe` command will show implied product definitions. -A target will be able to declare its use of the products that are defined in any of the external package dependencies. This is in addition to the existing ability to depend on other targets in the same package. +When switching to the SwiftPM 4 PackageDescription API, the package author takes over responsibility for defining the products. There will be tool support (probably in the form of a fix-it on a "package defines no products" warning) to make it easy for the author to add such definitions. Also, the `package init` command will be extended to automatically add the appropriate product definitions to the manifest when it creates the package. -There are two basic approaches for how to represent this. As noted in the "Open questions" section below, more discussion is needed before we can choose which of these approaches to recommend: +There was significant discussion about whether the implicit product rules should continue to be supported alongside the explicit product declarations. The tradeoffs are described in the "Alternatives considered" section. -1. Extending the notion of the types of dependencies that can be listed in a target definition's `dependencies` parameter. +### Product dependencies - To see how this works, remember that each string listed for the `dependencies` parameter of a target is just shorthand for a `.target(name: "...")` parameter, i.e. a dependency on a target within the same package. - - For example, the target definition: - - ``` - Target(name: "Foo", dependencies: ["Bar", "Baz"]) - ``` - - is shorthand for: - - ``` - Target(name: "Foo", dependencies: [.target(name: "Bar"), .target(name: "Baz")]) - ``` +A target will be able to declare its use of the products that are defined in any of the external package dependencies. This is in addition to the existing ability to declare dependencies on other targets in the same package. + +To support this, the `dependencies` parameter of the `Target()` initializer will be extended to also allow product references. + +To see how this works, remember that each string listed for the `dependencies` parameter is just shorthand for `.target(name: "...")`, i.e. a dependency on a target within the same package. - This could be extended to support product dependencies in addition to target dependencies. +For example, the target definition: - ``` - let package = Package( - name: "Hello", - dependencies: [ - .Package(url: "https://github.com/ExamplePackage", majorVersion: 1), - ], - targets: [ - Target(name: "Foo"), - Target(name: "Bar", dependencies: [ - .target(name: "Foo"), - .product(name: "Baz", package: "ExamplePackage") - ]) - ], - ) - ``` +``` +Target(name: "Foo", dependencies: ["Bar", "Baz"]) +``` - In this example, the package name is the short name of the package as defined by the package itself. A possibility would be to allow the package parameter to be optional when it is unambiguous. +is shorthand for: - A significant drawback of this approach is the loss of an ability to use strings as unambiguous shorthand for `.target(name: "...")` values. To remedy this, strings could still be allowed, as long as there was a clear way to disambiguate the dependency in cases in which two or more entities have the same name. +``` +Target(name: "Foo", dependencies: [.target(name: "Bar"), .target(name: "Baz")]) +``` + +This will be extended to support product dependencies in addition to target dependencies: - If we look at practical use cases, it seems rare that two completely unrelated entities (e.g. a target and a product) would have the same name. For example, if a target depends on `libAlamofire`, it doesn't actually matter whether it's the target or the product that is the dependency. So in most cases, a string would unambiguously capture the intent of the package author. If the shorthand form followed a search order, such as: +``` +let package = Package( + name: "MyClientLib", + dependencies: [ + .package(url: "https://github.com/Alamofire/Alamofire", majorVersion: 3), + ], + targets: [ + Target(name: "MyUtils"), + Target(name: "MyClientLib", dependencies: [ + .target(name: "MyUtils"), + .product(name: "Alamofire", package: "Alamofire") + ]) + ] +) +``` - - first look for a target with the specified name in the same package - - second, look for a product with the specified name in any package dependency +The package name is the canonical name of the package, as defined in the manifest of the package that defines the product. The product name is the name specified in the product definition of that same manifest. - then it should be possible for the shorthand notation to be sufficient in the vast majority of cases, with the possibility of using the more verbose form in the cases in which the shorthand form really is ambiguous. +In order to continue supporting the convenience of being able to use plain strings as shorthand, and in light of the fact that most of the time the names of packages and products are unique enough to avoid confusion, we will extend the short-hand notation so that a string can refer to either a target or a product. -2. Alternatively, a new optional array parameter could be added to the instantiation of a target: +The Package Manager will first try to resolve the name to a target in the same package; if there isn't one, it will instead to resolve it to a product in one of the packages specified in the `dependencies` parameter of the `Package()` initializer. - ``` - Target(name: "Foo", dependencies: ["Bar", "Baz"], externalDependencies: ["SomeProduct"]) - ``` - - If we go this route, one of the difficulties is in choosing good names from the arrays. Also, we would still need to determine whether it would be sufficient to list strings in the `externalDependencies` parameter, or whether `(, )` tuples would be needed. +For both the shorthand form and the complete form of product references, the only products that will be visible to the package are those in packages that are declared as direct dependencies -- the products of indirect dependencies are not visible to the package. ## Detailed design @@ -159,26 +147,16 @@ The definition of the `Product` type will be: ``` public enum Product { - public class AbstractProduct { - public let name: String - public let targets: [String] - - public init(name: String, targets: [String]) - } - - public class Executable : AbstractProduct { + public class Executable { public init(name: String, targets: [String]) } - public class Library : AbstractProduct { - public let type: LibraryType? - - public init(name: String, type: LibraryType? = nil, targets: [String]) - } - - public enum LibraryType { - case .static - case .dynamic + public class Library { + public enum LibType { + case .static + case .dynamic + } + public init(name: String, type: LibType? = nil, targets: [String]) } } ``` @@ -189,9 +167,9 @@ As with targets, there is no semantic significance to the order of the products ### Implicit products -If the `products` array is omitted, the Swift Package Manager will infer a set of implicit products based on the targets in the package. +If the `products` array is omitted, and if the package uses version 3 of the PackageDescription API, the Swift Package Manager will infer a set of implicit products based on the targets in the package. -The rules for implicit products are: +The rules for implicit products are the same as in SwiftPM 3: 1. An executable product is implicitly defined for any target that produces an executable module (as defined here: https://github.com/apple/swift-package-manager/blob/master/Documentation/Reference.md). @@ -199,95 +177,82 @@ The rules for implicit products are: ### Product dependencies -Depending on the decision about whether to extend the meaning of the `dependencies` parameter to target initialization or to add a new `externalDependencies` parameter, we will take one of these two approaches: - -1. For the extended `dependencies` parameter approach: Add a new enum case to the `TargetDependency` enum to handle the case of dependencies on products, and another enum case to represent an unbound by-name dependency. Modify the string-literal conversion to create a by-name dependency instead of a target dependency, and add logic to bind the by-name dependencies to either target or product dependencies once the package graph has been resolved. - - Alternatively, we could handle the case of unbound by-name dependencies by initially recording them as dependencies on targets (as today), and by then converting them to dependencies on products if appropriate after the package has been loaded: - - ``` - public final class Target { - /// Represents a dependency on another entity. - public enum TargetDependency { - /// A dependency on a target in the same project. - case Target(name: String) - /// A dependency on a product from a package dependency. If a package name is provided, it must match the name of one of the packages named in a `.Package()` directive. - case Product(name: String, package: String) - } - - /// The name of the target. - public let name: String - - /// Dependencies on other entities inside or outside the package. - public var dependencies: [TargetDependency] - - /// Construct a target. - public init(name: String, dependencies: [TargetDependency] = []) - } - ``` +We will add a new enum case to the `TargetDependency` enum to represent dependencies on products, and another enum case to represent an unbound by-name dependency. The string-literal conversion will create a by-name dependency instead of a target dependency, and there will be logic to bind the by-name dependencies to either target or product dependencies once the package graph has been resolved: -2. For the separate `externalDependencies` parameter approach: We will add an `externalDependencies ` parameter to the `Target` initializer, along with additional type definitions to support it: +``` +public final class Target { - ``` - public final class Target { - /// Represents a dependency on another target. - public enum TargetDependency { - /// A dependency on a target in the same project. - case Target(name: String) - } + /// Represents a target's dependency on another entity. + public enum TargetDependency { + /// A dependency on a target in the same project. + case Target(name: String) - /// Represents a dependency on a product in an external package. - public enum ExternalTargetDependency { - /// A dependency on a product from a package dependency. If a package name is provided, it must match the name of one of the packages named in a `.Package()` directive. - case Product(name: String, package: String?) - } + /// A dependency on a product from a package dependency. The package name match the name of one of the packages named in a `.package()` directive. + case Product(name: String, package: String) - /// The name of the target. - public let name: String + /// A by-name dependency that resolves to either a target or a product, as above, after the package graph has been loaded. + case ByName(name: String) + } - /// Dependencies on other targets in the package. - public var dependencies: [TargetDependency] + /// The name of the target. + public let name: String - /// Dependencies on products in external packages. - public var externalDependencies: [ExternalTargetDependency] + /// Dependencies on other entities inside or outside the package. + public var dependencies: [TargetDependency] - /// Construct a target. - public init(name: String, dependencies: [TargetDependency] = [], externalDependencies: [ExternalTargetDependency] = []) - } - ``` - -In the absence of any product dependencies, the entire package will be considered to be dependent on all of the packages on which it depends. If the package contains at least one other product dependency, then all targets that depend on products from other packages will need to have product dependencies specified. + /// Construct a target. + public init(name: String, dependencies: [TargetDependency] = []) +} +``` -*[ISSUE: This seems like a fairly unfortunate drop-off-a-cliff semantic, but short of adding a boolean to each `.Package()` declaration to say whether to depend on everything in that package, I don't know of a better solution. I'd like to discuss this a little bit more before broadcasting this proposal.]* +For compatibility reasons, packages using the Swift 3 version of the PackageDescription API will have implicit dependencies on the directly specified packages. Packages that have adopted the Swift 4 version need to declare their product dependencies explicitly. ## Impact on existing code -There will be no impact on existing packages that follow the documented Swift Package Manager 3.0 format of the package manifest. The Swift Package Manager will continue to infer products based the existing rules. We could also support packages that use the current undocumented product support by continuing to support the current `Product` types as a façade on the new API, but this does not seem worth the effort. - -If we decide to completely deprecate the implied products in the Swift Package Manager 4.0 API, then that will only affect packages once they upgrade their manifests to the SwiftPM 4.0 API. +There will be no impact on existing packages that follow the documented Swift Package Manager 3.0 format of the package manifest. Until the package is upgraded to the 4.0 format, the Swift Package Manager will continue to infer products based the existing rules. ## Alternatives considered -Instead of product definitions, fine-grained dependencies could be introduced by allowing targets to be marked as public or private to the package. This would not provide any way for a package author to control the type and characteristics of the various artifacts, however. Relying on the implied products that result from the inference rules is not likely to be scalable in the long term as we introduce new kinds of product types. +Many alternatives were considered during the development of this proposal, and in many cases the choice was between two distinct approaches, each with clear advantages and disadvantages. This section summarizes the major alternate approaches. -*[As we address the open questions, the approaches not chosen will be described here]* +### Not adding product definitions -## Open questions +Instead of product definitions, fine-grained dependencies could be introduced by allowing targets to be marked as public or private to the package. -1. Should tests be considered to be products? +_Advantage_ - Pro: Since tests are artifacts that can be produced during a build, just like products are, it may make sense for users to be able to express opinions about them in the same manner as for products. - - Con: Tests are conceptually different from products in their intended use, and it is much less likely that users will have specific opinions about the details of how tests are built. It seems conceptually cleaner to treat tests as a different kind of artifact than products. - -2. Should there be any inference of products at all? +It would avoid the need to introduce new concepts. + +_Disadvantage_ + +It would not provide any way for a package author to control the type and characteristics of the various artifacts. Relying on the implied products that result from the inference rules is not likely to be scalable in the long term as we introduce new kinds of product types. + +### Inferring implicit products + +An obvious alternative to the proposal would be to keep the inference rules even in v4 of the PackageDescription API. - Pro: It can be much more convenient to not have to declare products, and to be able to add new products just by adding or renaming files and directories. +_Advantage_ - Con: The very fact that it is so easy to change the set of products without modifying the package manifest can lead to unexpected behavior due to seemingly unrelated changes (e.g. creating a file called "main.swift" in a module changes that module from a library to an executable). +It can be a lot more convenient to not have to declare products, and to be able to add new products just by adding or renaming files and directories. This is particularly true for small, simple packages. -3. Should target dependencies on products be separate from dependencies on other targets? +_Disadvantages_ - Pro: Targets and products are different concepts, and it is possible for a target and a product to have the same name. This can lead to ambiguity. +The very fact that it is so easy to change the set of products without modifying the package manifest can lead to unexpected behavior due to seemingly unrelated changes (e.g. creating a file called `main.swift` in a module changes that module from a library to an executable). + +Also, as packages become more complex and new conceptual parts on which clients can depend are introduced, the interaction of implicit rules with the explicit product definitions can become very complicated. + +We plan to provide some of the convenience through tooling. For example, an IDE (or `swift package` itself on the command line) can offer to add product definitions when it notices certain types of changes to the structure of the package. + +### Distinguishing between target dependencies and product dependencies + +The proposal extends the `Target()` initializer's `dependencies` parameter to allow products as well as targets; another approach would have been to add a new parameter, e.g. `externalDependencies` or `productDependencies`. + +_Advantage_ + +Targets and products are different types of entities, and it is possible for a target and a product to have the same name. Having the `dependencies` parameter as a heterogeneous list can lead to ambiguity. - Con: Conceptually the *dependency* itself is the same concept, even if type of entity being depended on is technically different. In both cases (target and product), it is up to the build system to determine the exact type of artifacts that should be produced. In most cases, there is no actual semantic ambiguity, since the name of a target, product, and package are often the same uniquely identifiable "brand" name of the component. +_Disadvantages_ + +Conceptually the *dependency* itself is the same concept, even if type of entity being depended on is technically different. In both cases (target and product), it is up to the build system to determine the exact type of artifacts that should be produced. In most cases, there is no actual semantic ambiguity, since the name of a target, product, and package are often the same uniquely identifiable "brand" name of the component. + +Also, separating out each type of dependency into individual homogeneous lists doesn't scale. If a third type of dependency needs to be introduced, a third parameter would also need to be introduced. Keeping the list heterogeneous avoids this. From 497403891532940b384c6071d6c6ad1fbf1c53e2 Mon Sep 17 00:00:00 2001 From: Anders Bertelrud Date: Mon, 14 Nov 2016 13:55:01 -0800 Subject: [PATCH 0067/4563] Updated based on feedback --- ...NNN-package-manager-product-definitions.md | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/proposals/NNNN-package-manager-product-definitions.md b/proposals/NNNN-package-manager-product-definitions.md index d2ac016642..dc49034c01 100644 --- a/proposals/NNNN-package-manager-product-definitions.md +++ b/proposals/NNNN-package-manager-product-definitions.md @@ -2,7 +2,7 @@ * Proposal: [SE-NNNN](NNNN-package-manager-product-definitions.md) * Author: [Anders Bertelrud](https://github.com/abertelrud) -* Status: **Awaiting review** +* Status: **Awaiting Review** * Review manager: TBD ## Introduction @@ -13,11 +13,11 @@ Swift-evolution thread: [Discussion thread topic for that proposal](http://news. ## Motivation -Currently, the Swift Package Manager has only a limited notion of the products that are built for a package. It has a set of rules by which it infers implicit products based on the contents of the targets in the package, and it also has a small amount of undocumented, unsupported package manifest syntax for explicitly declaring products, but it provides no supported way for package authors to declare what their packages produce. +Currently, the Swift Package Manager has only a limited notion of the products that are built for a package. It does have a set of rules by which it infers implicit products based on the contents of the targets in the package, and it also has a small amount of undocumented, unsupported package manifest syntax for explicitly declaring products, but it provides no supported way for package authors to declare what their packages produce. Also, while the Swift Package Manager currently supports dependencies between packages, it has no support for declaring a dependency on anything more fine-grained than a package. -Such fine-grained dependencies are often desired, and this desire has in the past been expressed as a request for the ability to define a dependency on an arbitrary target in a package. That, in turn, leads to requests to provide access control for targets, since a package author may want control over which targets can be independently accessed from outside the package. +Such fine-grained dependencies are often desired, and this desire has in the past been expressed as a request for the ability to declare a dependency on an arbitrary target in a package. That, in turn, leads to requests to provide access control for targets, since a package author may want control over which targets can be independently accessed from outside the package. Even if visibility control for targets were to be provided (indeed, an early draft of the package manifest syntax had the notion of "publishing" a target to external clients), there would still be no way for the package author to declare anything about the kind of product that should be built. @@ -27,9 +27,9 @@ For example, consider the package for a component that has both a library API an Such a package would currently need to be split up into three separate packages in order to provide the appropriate dependency granularity: one for the public library, another for the command line tools, and a third, private package to provide the shared implementation used by the other two packages. In the case of a single conceptual component that should have a single version number, this fracturing into multiple packages is directly contrary to the developer's preferred manner of packaging. -What is needed is a way to allow package authors to declare conceptually distinct products of a package, and to allow client packages to define dependencies on individual products in a package. +What is needed is a way to allow package authors to define conceptually distinct products of a package, and to allow client packages to declare dependencies on individual products in a package. -Furthermore, explicit product definitions allow the package author to control the types of artifacts produced from the targets in the package. This can include such things as whether a library is built as a static or a dynamic library, and we expect that additional product types will be added over time to let package authors build more kinds of artifacts. +Furthermore, explicit product definitions would allow the package author to control the types of artifacts produced from the targets in the package. This would include such things as whether a library is built as a static archive or a dynamic library. We expect that additional product types will be added over time to let package authors build more kinds of artifacts. ## Proposed solution @@ -39,9 +39,9 @@ We will introduce a documented and supported concept of a package product, along A package will be able to define an arbitrary number of products that are visible to all direct clients of the package. A product definition consists of a product type, a name (which must be unique among the products in the package), and the root targets that comprise the implementation of the product. There may also be additional properties depending on the type of product (for example, a library may be static or dynamic). -Any target may be included in multiple products, though not all kinds of targets are usable in any kind of product; for example, a test target is not able to be included in a library product. +Any target may be included in multiple products, though not all kinds of targets are usable in every kind of product; for example, a test target is not able to be included in a library product. -The products represent the publicly vended package outputs on which any client package can depend. Other artifacts might also be created as part of building the package, but what the products specifically define are the conceptual "outputs" of the package, i.e. those that make sense to think of as something a client package can depend on and use. Examples of artifacts that are not necessarily products include built unit tests and helper tools that are used only during the build of the package. +The products represent the publicly vended package outputs on which client packages can depend. Other artifacts might also be created as part of building the package, but what the products specifically define are the conceptual "outputs" of the package, i.e. those that make sense to think of as something a client package can depend on and use. Examples of artifacts that are not necessarily products include built unit tests and helper tools that are used only during the build of the package. An example of a package that defines two library products and one executable product: @@ -53,12 +53,12 @@ let package = Package( Target(name: "HTTP", dependencies: ["Utils"]), Target(name: "ClientAPI", dependencies: ["HTTP", "Utils"]), Target(name: "ServerAPI", dependencies: ["HTTP"]), - Target(name: "ServerDaemon", dependencies: ["ServerAPI"]) + Target(name: "ServerDaemon", dependencies: ["ServerAPI"]), ], products: [ - Library(name: "ClientLib", type: .static, targets: ["ClientAPI"]), - Library(name: "ServerLib", type: .dynamic, targets: ["ServerAPI"]), - Executable(name: "myserver", targets: ["ServerDaemon"]), + .Library(name: "ClientLib", type: .static, targets: ["ClientAPI"]), + .Library(name: "ServerLib", type: .dynamic, targets: ["ServerAPI"]), + .Executable(name: "myserver", targets: ["ServerDaemon"]), ] ) ``` @@ -67,7 +67,7 @@ The initial types of products that can be defined are executables and libraries. Note that tests are not considered to be products, and do not need to be explicitly defined. -A product definition lists the root targets to include in the product; for product types that vend interfaces (e.g. libraries), the root targets are those whose modules will be available to clients. Any dependencies of those targets will also be included in the product, but won't be made visible to clients. The Swift compiler does not currently provide this granularity of module visibility control, but the set of root targets still constitutes a declaration of intent that can be used by IDEs and other tools. We also hope that the compiler will one day support this level of visibility control. +A product definition lists the root targets to include in the product; for product types that vend interfaces (e.g. libraries), the root targets are those whose modules will be available to clients. Any dependencies of those targets will also be included in the product, but won't be made visible to clients. The Swift compiler does not currently provide this granularity of module visibility control, but the set of root targets still constitutes a declaration of intent that can be used by IDEs and other tools. We also hope that the compiler will one day support this level of visibility control. See [SR-3205](https://bugs.swift.org/browse/SR-3205) for more details. For example, in the package definition shown above, the library product `ClientLib` would only vend the interface of the `ClientAPI` module to clients. Since `ClientAPI` depends on `HTTP` and `Utilities`, those two targets would also be compiled and linked into `ClientLib`, but their interfaces should not be visible to clients of `ClientLib`. @@ -119,6 +119,8 @@ let package = Package( The package name is the canonical name of the package, as defined in the manifest of the package that defines the product. The product name is the name specified in the product definition of that same manifest. +The package name is optional, since the product name is almost always unambiguous (and is frequently the same as the package name). The package name must be specified if there is more than one product with the same name in the package graph (this does not currently work from a technical perspective, since Swift module names must currently be unique within the package graph). + In order to continue supporting the convenience of being able to use plain strings as shorthand, and in light of the fact that most of the time the names of packages and products are unique enough to avoid confusion, we will extend the short-hand notation so that a string can refer to either a target or a product. The Package Manager will first try to resolve the name to a target in the same package; if there isn't one, it will instead to resolve it to a product in one of the packages specified in the `dependencies` parameter of the `Package()` initializer. @@ -138,7 +140,7 @@ Package( providers: [SystemPackageProvider]? = nil, targets: [Target] = [], dependencies: [Package.Dependency] = [], - products: [Product]? = nil, + products: [Product] = [], exclude: [String] = [] ) ``` @@ -188,7 +190,7 @@ public final class Target { case Target(name: String) /// A dependency on a product from a package dependency. The package name match the name of one of the packages named in a `.package()` directive. - case Product(name: String, package: String) + case Product(name: String, package: String?) /// A by-name dependency that resolves to either a target or a product, as above, after the package graph has been loaded. case ByName(name: String) @@ -241,7 +243,7 @@ The very fact that it is so easy to change the set of products without modifying Also, as packages become more complex and new conceptual parts on which clients can depend are introduced, the interaction of implicit rules with the explicit product definitions can become very complicated. -We plan to provide some of the convenience through tooling. For example, an IDE (or `swift package` itself on the command line) can offer to add product definitions when it notices certain types of changes to the structure of the package. +We plan to provide some of the convenience through tooling. For example, an IDE (or `swift package` itself on the command line) can offer to add product definitions when it notices certain types of changes to the structure of the package. We believe that having defined product types lets the tools present a lot better diagnostics and other forms of help to users, since it provides a clear statement of intent on the part of the package author. ### Distinguishing between target dependencies and product dependencies From 85188c115234c08886120873912356ebaae4e8c0 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 17 Nov 2016 09:22:30 -0800 Subject: [PATCH 0068/4563] SE-0143 is accepted. --- index.xml | 2 +- proposals/0143-conditional-conformances.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.xml b/index.xml index 4c906952dd..8cbcaeb5f1 100644 --- a/index.xml +++ b/index.xml @@ -143,7 +143,7 @@ - + diff --git a/proposals/0143-conditional-conformances.md b/proposals/0143-conditional-conformances.md index 62235d05dd..e68abf3e92 100644 --- a/proposals/0143-conditional-conformances.md +++ b/proposals/0143-conditional-conformances.md @@ -3,7 +3,7 @@ * Proposal: [SE-0143](0143-conditional-conformances.md) * Author: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Awaiting review** +* Status: **Accepted** * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/91725ee83fa34c81942a634dcdfa9d2441fbd853/proposals/0143-conditional-conformances.md) ## Introduction From ecc8d00a9e932c9f31c96c730828b1c9f38c4cfd Mon Sep 17 00:00:00 2001 From: Daniel Dunbar Date: Thu, 17 Nov 2016 09:27:34 -0800 Subject: [PATCH 0069/4563] [SE-0145] Update proposal with "automatic pinning", on by default. --- .../0145-package-manager-version-pinning.md | 91 +++++++++++++------ 1 file changed, 63 insertions(+), 28 deletions(-) diff --git a/proposals/0145-package-manager-version-pinning.md b/proposals/0145-package-manager-version-pinning.md index 5e20c5a504..796944d7ac 100644 --- a/proposals/0145-package-manager-version-pinning.md +++ b/proposals/0145-package-manager-version-pinning.md @@ -55,23 +55,6 @@ Our proposal is designed to satisfy several different use cases for such a behav dependencies in use, so that the resulting product can be exactly recreated later if necessary. -### Mechanism and policy - -This proposal primarily addresses the _mechanism_ used to record -and manage version-pinning information, in support of _specific -workflows_ with elevated demands for reproducible builds. - -In addition to this, certain _policy_ choices around default -behavior are included; these are set initially to different -defaults than in many package managers. Specifically the default -behavior is to _not_ generate pinning information unless -requested, for reasons outlined in the alternatives discussion. - -If the policy choice turns out to be wrong, the default can be -changed without difficulty. We actively expect to revisit this policy -choice once we have had experience with the mechanism, and can -observe how it is being used. - ### Current Behavior The package manager *NEVER* updates a locally cloned package from its current @@ -90,10 +73,6 @@ clones were "pinned", however, there has heretofore been no way to share that pinning information with other team members. This proposal is aimed at addressing that. -Thus, although our policy choice here is to not _generate_ pinning information -by default in a location which is likely to be checked in, our local behavior on -any individual developers desktop is always as if there is some pinning. - ## Proposed solution We will introduce support for an **optional** new file `Package.pins` adjacent @@ -111,15 +90,29 @@ practice it will be a JSON data file. This file *may* be checked into SCM by the user, so that its effects apply to all users of the package. However, it may also be maintained only locally (e.g., placed in the `.gitignore` file). We intend to leave it to package authors to -decide which use case is best for their project. +decide which use case is best for their project. We will **recommend** that it +not be checked in by library authors, at least for released versions, since pins +are not inherited and thus this information may be confusing. We may codify this +recommendation into a warning in a future package manager workflow which +provided assistance in publishing package versions. In the presence of a top-level `Package.pins` file, the package manager will respect the pinned dependencies recorded in the file whenever it needs to do dependency resolution (e.g., on the initial checkout or when updating). -The pins file will not override Manifest specified version requirements and it -will be an error (with proper diagnostics) if there is a conflict between the pins -and the manifest specification. +In the absence of a top-level `Package.pins` file, the package manager will +operate based purely on the requirements specified in the package manifest, but +will then automatically record the choices it makes into a `Package.pins` file +as part of the "auto-pinning" feature. The goal of this behavior is to encourage +reproducible behavior among package authors who share the pin file (typically by +checking it in). + +We will also provide an explicit mechanism by which package authors can opt out +of the "auto-pinning" default for their package. + +The pins file will *not* override manifest specified version requirements and it +will be an error (with proper diagnostics) if there is a conflict between the +pins and the manifest specification. The pins file will also not influence dependency resolution for dependent packages; for example if application A depends on library B which in turn depends on library C, @@ -134,7 +127,7 @@ library B when deciding which version of library C to use. ``` $ swift package pin ( [--all] | [] [] ) [--message ] ``` - + The `package-name` refers to the name of the package as specified in its manifest. This command pins one or all dependencies. The command which pins a single version can optionally take a specific version to pin to, if unspecified (or with `--all`) the behavior is to pin to the current package version in use. Examples: @@ -153,9 +146,15 @@ library B when deciding which version of library C to use. behavior for the closure of the dependencies outside of them being named in the manifest. +2. We will add two additional commands to `pin` as part of the "auto-pinning" workflow (see below): + + ``` + $ swift package pin ( [--enable-autopin] | [--disable-autopin] ) + ``` + + These will enable or disable auto-pinning for the package (this state is + recorded in the `Package.pins` file). -2. Dependencies are never automatically pinned, pinning is only ever taken as a result of an explicit user action. - 3. We will add a new command `unpin`: ``` @@ -177,10 +176,46 @@ library B when deciding which version of library C to use. * The `[--repin]` argument can be used to lift the version pinning restrictions. In this case, the behavior is that all packages are updated, and packages which were previously pinned are then repinned to the latest resolved versions. + When automatic pinning is enabled, `package update` would be default have absolutely no effect without `--repin`. Thus, we will make `package update` act as if `--repin` was specified whenever automatic pinning is enabled. This is a special case, but we believe it is most likely to match what the user expects, and avoids have a command syntax which has no useful behavior in the automatic pinning mode. + 6. The update and checkout will both emit logs, notifying the user that pinning is in effect. 7. The `swift package show-dependencies` subcommand will be updated to indicate if a dependency is pinned. + +### Automatic Pinning + +The package manager will have automatic pinning enabled by default (this is +equivalent to `swift package pin --enable-autopin`), although package project +owners can choose to disable this if they wish to have more fine grained control +over their pinning behavior. + +When automatic pinning is enabled, the package manager will automatic record all +package dependencies in the `Package.pins` file. If package authors do not check +this file into their source control, the behavior will typically be no different +than the existing package manager behavior (one exception is the `package +update` behavior described above). + +If a package author does check the file into source control, the effect will be +that anyone developing directly on this package will end up sharing the same +dependency versions (and modifications will be commited as part of the SCM +history). + +The automatic pinning behavior is an extension of the behaviors above, and works +as follows: + + * When enabled, the package manager will write all dependency versions into the + pin file after any operation which changes the set of active working + dependencies (for example, if a new dependency is added). + + * A package author can still change the individual pinned versions using the + `package pin` commands, these will simply update the pinned state. + + * Some commands do not make sense when automatic pinning is enabled; for + example, it is not possible to `unpin` and attempts to do so will produce an + error. + + ## Future Directions We have intentionally kept the pin file format an implementation detail in order From ed0d77f9a3b1207045d43ee203da7b6285d96b50 Mon Sep 17 00:00:00 2001 From: Daniel Dunbar Date: Thu, 17 Nov 2016 09:44:22 -0800 Subject: [PATCH 0070/4563] [SE-145] A couple more notes on automatic pinning. --- .../0145-package-manager-version-pinning.md | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/proposals/0145-package-manager-version-pinning.md b/proposals/0145-package-manager-version-pinning.md index 796944d7ac..55aa0449f6 100644 --- a/proposals/0145-package-manager-version-pinning.md +++ b/proposals/0145-package-manager-version-pinning.md @@ -103,12 +103,12 @@ dependency resolution (e.g., on the initial checkout or when updating). In the absence of a top-level `Package.pins` file, the package manager will operate based purely on the requirements specified in the package manifest, but will then automatically record the choices it makes into a `Package.pins` file -as part of the "auto-pinning" feature. The goal of this behavior is to encourage -reproducible behavior among package authors who share the pin file (typically by -checking it in). +as part of the "automatic pinning" feature. The goal of this behavior is to +encourage reproducible behavior among package authors who share the pin file +(typically by checking it in). We will also provide an explicit mechanism by which package authors can opt out -of the "auto-pinning" default for their package. +of the automatic pinning default for their package. The pins file will *not* override manifest specified version requirements and it will be an error (with proper diagnostics) if there is a conflict between the @@ -146,15 +146,21 @@ library B when deciding which version of library C to use. behavior for the closure of the dependencies outside of them being named in the manifest. -2. We will add two additional commands to `pin` as part of the "auto-pinning" workflow (see below): +2. We will add two additional commands to `pin` as part of the automatic pinning + workflow (see below): ``` $ swift package pin ( [--enable-autopin] | [--disable-autopin] ) ``` - These will enable or disable auto-pinning for the package (this state is + These will enable or disable automatic pinning for the package (this state is recorded in the `Package.pins` file). + These commands are verbose, but the expectation is that they are very + infrequently run, just to establish the desired behavior for a particular + project, and then the pin file (containing this state) is checked in to + source control. + 3. We will add a new command `unpin`: ``` @@ -162,6 +168,8 @@ library B when deciding which version of library C to use. ``` This is the counterpart to the pin command, and unpins one or all packages. + It is an error to attempt to `unpin` when automatic pinning is enabled. + 4. We will fetch and resolve the dependencies when running the pin commands, in case we don't have the complete dependency graph yet. 5. We will extend the workflow for update to honor version pinning, that is, it will only update packages which are unpinned, and it will only update to versions which can satisfy the existing pins. The update command will, however, also take an optional argument `--repin`: @@ -215,6 +223,13 @@ as follows: example, it is not possible to `unpin` and attempts to do so will produce an error. +Since package pin information is **not** inherited across dependencies, our +recommendation is that packages which are primarily intended to be consumed by +other developers either *disable* automatic pinning or put the `Package.pins` +file into `.gitignore`, so that users are not confused why they get different +versions of dependencies that are those being used by the library authors while +they develop. + ## Future Directions @@ -230,8 +245,25 @@ There will be change in the behaviors of `swift build` and `swift package update ## Alternative considered +### Minimal pin feature set + +A prior version of this proposal did not pin by default. Since this proposal +includes this behavior, we could in theory eliminate the fine grained pinning +feature set we expose, like `package pin ` and `package unpin`. + +However, we believe it is important for package authors to retain a large amount +of control over how their package is developed, and we wish the community to +aspire to following semantic versioning strictly. For that reason, we wanted to +support mechanisms so that package authors wishing to follow this model could +still pin individual dependencies. + + ### Pin by default +_This discussion is historical, from a prior version of a proposal which did not +include the automatic pinning behavior; which we altered the proposal for. We +have left it in the proposal for historical context._ + Much discussion has revolved around a single policy-default question: whether SwiftPM should generate a pins file as a matter of course any time it builds. This is how some other package managers work, and it is viewed as a From 5fa2aa449ba5c1748ed0a58f237245b6c35b6aec Mon Sep 17 00:00:00 2001 From: Daniel Dunbar Date: Thu, 17 Nov 2016 11:51:22 -0800 Subject: [PATCH 0071/4563] [SE-145] Minor updates to diagnostics and option name. --- proposals/0145-package-manager-version-pinning.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0145-package-manager-version-pinning.md b/proposals/0145-package-manager-version-pinning.md index 55aa0449f6..beaf3fcf5f 100644 --- a/proposals/0145-package-manager-version-pinning.md +++ b/proposals/0145-package-manager-version-pinning.md @@ -111,8 +111,8 @@ We will also provide an explicit mechanism by which package authors can opt out of the automatic pinning default for their package. The pins file will *not* override manifest specified version requirements and it -will be an error (with proper diagnostics) if there is a conflict between the -pins and the manifest specification. +will be a warning if there is a conflict between the pins and the manifest +specification. The pins file will also not influence dependency resolution for dependent packages; for example if application A depends on library B which in turn depends on library C, @@ -135,9 +135,9 @@ library B when deciding which version of library C to use. * `$ swift package pin Foo` - pins `Foo` at current resolved version. * `$ swift package pin Foo 1.2.3` - pins `Foo` at 1.2.3. The specified version should be valid and resolvable. - The `--reason` option is an optional argument to document the reason for pinning a dependency. This could be helpful for user to later remember why a dependency was pinned. Example: + The `--message` option is an optional argument to document the reason for pinning a dependency. This could be helpful for user to later remember why a dependency was pinned. Example: - `$ swift package pin Foo --reason "The patch updates for Foo are really unstable and need screening."` + `$ swift package pin Foo --message "The patch updates for Foo are really unstable and need screening."` NOTE: When we refer to dependencies in the context of pinning, we are referring to *all* dependencies of a package, i.e. the transitive closure of @@ -178,7 +178,7 @@ library B when deciding which version of library C to use. $ swift package update [--repin] ``` - * Update command errors if there are no unpinned packages which can be updated. + * The update command will warn if there are no unpinned packages which can be updated. * Otherwise, the behavior is to update all unpinned packages to the latest possible versions which can be resolved while respecting the existing pins. From 8b9f805ed6c539832f50b7f033610129c29a9d3b Mon Sep 17 00:00:00 2001 From: Daniel Dunbar Date: Thu, 17 Nov 2016 16:41:10 -0800 Subject: [PATCH 0072/4563] [SE-0146] Package Manager Product Definitions - Move to active review. --- index.xml | 1 + ...ons.md => 0146-package-manager-product-definitions.md} | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) rename proposals/{NNNN-package-manager-product-definitions.md => 0146-package-manager-product-definitions.md} (98%) diff --git a/index.xml b/index.xml index 4c906952dd..372d249d50 100644 --- a/index.xml +++ b/index.xml @@ -146,6 +146,7 @@ + + + + + + + + + + + + + + + + + + + Swift Evolution + + +
+
+ + +

Swift Evolution

+
+
+ +
+
+
+
    + +
+
+
+
+ +
+
+ + + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000000..87e52fe3a8 --- /dev/null +++ b/index.js @@ -0,0 +1,747 @@ +// ===--- index.js - Swift Evolution --------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +// ===---------------------------------------------------------------------===// + +/** Holds the primary data used on this page: metadata about Swift Evolution proposals. */ +var proposals + +/** + * To be updated when proposals are confirmed to have been implemented + * in a new language version. + */ +var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4'] + +/** Storage for the user's current selection of filters when filtering is toggled off. */ +var filterSelection = [] + +var REPO_PROPOSALS_BASE_URL = 'https://github.com/apple/swift-evolution/blob/master/proposals' + +/** + * `name`: Mapping of the states in the proposals JSON to human-readable names. + * + * `shortName`: Mapping of the states in the proposals JSON to short human-readable names. + * Used for the left-hand column of proposal statuses. + * + * `className`: Mapping of states in the proposals JSON to the CSS class names used + * to manipulate and display proposals based on their status. + */ +var states = { + '.awaitingReview': { + name: 'Awaiting Review', + shortName: 'Awaiting Review', + className: 'awaiting-review' + }, + '.scheduledForReview': { + name: 'Scheduled for Review', + shortName: 'Scheduled', + className: 'scheduled-for-review' + }, + '.activeReview': { + name: 'Active Review', + shortName: 'Active Review', + className: 'active-review' + }, + '.returnedForRevision': { + name: 'Returned for Revision', + shortName: 'Returned', + className: 'returned-for-revision' + }, + '.withdrawn': { + name: 'Withdrawn', + shortName: 'Withdrawn', + className: 'withdrawn' + }, + '.deferred': { + name: 'Deferred', + shortName: 'Deferred', + className: 'deferred' + }, + '.accepted': { + name: 'Accepted', + shortName: 'Accepted', + className: 'accepted' + }, + '.acceptedWithRevisions': { + name: 'Accepted with revisions', + shortName: 'Accepted', + className: 'accepted-with-revisions' + }, + '.rejected': { + name: 'Rejected', + shortName: 'Rejected', + className: 'rejected' + }, + '.implemented': { + name: 'Implemented', + shortName: 'Implemented', + className: 'implemented' + }, + '.error': { + name: 'Error', + shortName: 'Error', + className: 'error' + } +} + +init() + +/** Primary entry point. */ +function init () { + var req = new window.XMLHttpRequest() + + req.addEventListener('load', function (e) { + proposals = JSON.parse(req.responseText) + + // don't display malformed proposals + proposals = proposals.filter(function (proposal) { + return !proposal.errors + }) + + // descending numeric sort based the numeric nnnn in a proposal ID's SE-nnnn + proposals.sort(function compareProposalIDs (p1, p2) { + return parseInt(p1.id.match(/\d\d\d\d/)[0]) - parseInt(p2.id.match(/\d\d\d\d/)[0]) + }) + proposals = proposals.reverse() + + render() + addEventListeners() + + // apply filters when the page loads with a search already filled out. + // typically this happens after navigating backwards in a tab's history. + if (document.querySelector('#search-filter').value.trim()) { + filterProposals() + } + }) + + req.addEventListener('error', function (e) { + document.querySelector('#proposals-count').innerText = 'Proposal data failed to load.' + }) + + req.open('get', 'https://data.swift.org/swift-evolution/proposals') + req.send() +} + +/** + * Creates an Element. Convenience wrapper for `document.createElement`. + * + * @param {string} elementType - The tag name. 'div', 'span', etc. + * @param {string[]} attributes - A list of attributes. Use `className` for `class`. + * @param {(string | Element)[]} children - A list of either text or other Elements to be nested under this Element. + * @returns {Element} The new node. + */ +function html (elementType, attributes, children) { + var element = document.createElement(elementType) + + if (attributes) { + Object.keys(attributes).forEach(function (attributeName) { + var value = attributes[attributeName] + if (attributeName === 'className') attributeName = 'class' + element.setAttribute(attributeName, value) + }) + } + + if (!children) return element + if (!Array.isArray(children)) children = [children] + + children.forEach(function (child) { + if (!child) { + console.warn('Null child ignored during creation of ' + elementType) + return + } + if (Object.getPrototypeOf(child) === String.prototype) { + child = document.createTextNode(child) + } + + element.appendChild(child) + }) + + return element +} + +/** + * Adds the dynamic portions of the page to the DOM, primarily the list + * of proposals and list of statuses used for filtering. + * + * These `render` functions are only called once when the page loads, + * the rest of the interactivity is based on toggling `display: none`. + */ +function render () { + renderNav() + renderBody() +} + +/** Renders the top navigation bar. */ +function renderNav () { + var nav = document.querySelector('nav') + + // This list intentionally omits .acceptedWithRevisions and .error; + // .acceptedWithRevisions proposals are combined in the filtering UI + // with .accepted proposals. + var checkboxes = [ + '.awaitingReview', '.scheduledForReview', '.activeReview', '.returnedForRevision', '.accepted', + '.implemented', '.deferred', '.rejected', '.withdrawn' + ].map(function (state) { + var className = states[state].className + + return html('li', null, [ + html('input', { type: 'checkbox', id: 'filter-by-' + className, value: className }), + html('label', { className: className, tabindex: '0', role: 'button', 'for': 'filter-by-' + className }, [ + states[state].name + ]) + ]) + }) + + var expandableArea = html('div', { className: 'filter-options expandable' }, [ + html('h5', { id: 'filter-options-label'}, 'Status'), + html('ul', { className: 'filter-by-status'}) + ]) + + nav.querySelector('.nav-contents').appendChild(expandableArea) + + checkboxes.forEach(function (box) { + nav.querySelector('.filter-by-status').appendChild(box) + }) + + // The 'Implemented' filter selection gets an extra row of options if selected. + var implementedCheckboxIfPresent = checkboxes.filter(function (cb) { + return cb.querySelector(`#filter-by-${states['.implemented'].className}`) + })[0] + + if (implementedCheckboxIfPresent) { + // add an extra row of options to filter by language version + var versionRowHeader = html('h5', { id: 'version-options-label', className: 'hidden' }, 'Language Version') + var versionRow = html('ul', { id: 'version-options', className: 'filter-by-status hidden'}) + + /** Helper to give versions like 3.0.1 an okay ID to use in a DOM element. (swift-3-0-1) */ + function idSafeName (name) { + return 'swift-' + name.replace(/\./g, '-') + } + + var versionOptions = languageVersions.map(function (version) { + return html('li', null, [ + html('input', { + type: 'checkbox', + id: 'filter-by-swift-' + idSafeName(version), + className: 'filter-by-swift-version', + value: 'swift-' + idSafeName(version) + }), + html('label', { + tabindex: '0', + role: 'button', + 'for': 'filter-by-swift-' + idSafeName(version) + }, 'Swift ' + version) + ]) + }) + + versionOptions.forEach(function (version) { + versionRow.appendChild(version) + }) + + expandableArea.appendChild(versionRowHeader) + expandableArea.appendChild(versionRow) + } + + return nav +} + +/** Displays the main list of proposals that takes up the majority of the page. */ +function renderBody () { + var article = document.querySelector('article') + + var proposalAttachPoint = article.querySelector('.proposals-list') + + var proposalPresentationOrder = [ + '.awaitingReview', '.scheduledForReview', '.activeReview', '.returnedForRevision', '.accepted', + '.acceptedWithRevisions', '.implemented', '.deferred', '.rejected', '.withdrawn' + ] + + proposalPresentationOrder.map(function (state) { + var matchingProposals = proposals.filter(function (p) { return p.status && p.status.state === state }) + matchingProposals.map(function (proposal) { + var proposalBody = html('section', { id: proposal.id, className: 'proposal ' + proposal.id }, [ + html('div', { className: 'status-pill-container' }, [ + html('span', { className: 'status-pill color-' + states[state].className }, [ + states[proposal.status.state].shortName + ]) + ]), + html('div', { className: 'proposal-content' }, [ + html('div', { className: 'proposal-header' }, [ + html('span', { className: 'proposal-id'}, [ + proposal.id + ]), + html('h4', { className: 'proposal-title' }, [ + html('a', { + href: REPO_PROPOSALS_BASE_URL + '/' + proposal.link, + target: '_blank' + }, [ + proposal.title + ]) + ]) + ]) + ]) + ]) + + var detailNodes = [] + detailNodes.push(renderAuthors(proposal.authors)) + + if (proposal.reviewManager.name) detailNodes.push(renderReviewManager(proposal.reviewManager)) + if (proposal.trackingBugs) detailNodes.push(renderTrackingBugs(proposal.trackingBugs)) + if (state === '.implemented') detailNodes.push(renderVersion(proposal.status.version)) + + if (state === '.acceptedWithRevisions') detailNodes.push(renderStatus(proposal.status)) + + if (state === '.activeReview' || state === '.scheduledForReview') { + detailNodes.push(renderStatus(proposal.status)) + detailNodes.push(renderReviewPeriod(proposal.status)) + } + + var details = html('div', { className: 'proposal-details' }, detailNodes) + + proposalBody.querySelector('.proposal-content').appendChild(details) + proposalAttachPoint.appendChild(proposalBody) + }) + }) + + // Update the "(n) proposals" text + updateProposalsCount(article.querySelectorAll('.proposal').length) + + return article +} + +/** Authors have a `name` and optional `link`. */ +function renderAuthors (authors) { + var authorNodes = authors.map(function (author) { + if (author.link.length > 0) { + return html('a', { href: author.link, target: '_blank' }, author.name) + } else { + return document.createTextNode(author.name) + } + }) + + authorNodes = _joinNodes(authorNodes, ', ') + + return html('div', { className: 'authors proposal-detail' }, [ + html('div', { className: 'proposal-detail-label' }, + authors.length > 1 ? 'Authors: ' : 'Author: ' + ), + html('div', { className: 'proposal-detail-value' }, authorNodes) + ]) +} + +/** Review managers have a `name` and optional `link`. */ +function renderReviewManager (reviewManager) { + return html('div', { className: 'review-manager proposal-detail' }, [ + html('div', { className: 'proposal-detail-label' }, 'Review Manager: '), + html('div', { className: 'proposal-detail-value'}, [ + reviewManager.link + ? html('a', { href: reviewManager.link, target: '_blank' }, reviewManager.name) + : reviewManager.name + ]) + ]) +} + +/** Tracking bugs linked in a proposal are updated via bugs.swift.org. */ +function renderTrackingBugs (bugs) { + var bugNodes = bugs.map(function (bug) { + return html('a', { href: bug.link, target: '_blank' }, [ + bug.id, + ' (', + bug.assignee || 'Unassigned', + ', ', + bug.status, + ')' + ]) + }) + + bugNodes = _joinNodes(bugNodes, ', ') + + return html('div', { className: 'proposal-detail' }, [ + html('div', { className: 'proposal-detail-label' }, [ + bugs.length > 1 ? 'Bugs: ' : 'Bug: ' + ]), + html('div', { className: 'bug-list proposal-detail-value' }, + bugNodes + ) + ]) +} + +/** For `.implemented` proposals, display the version of Swift in which they first appeared. */ +function renderVersion (version) { + return html('div', { className: 'proposal-detail' }, [ + html('div', { className: 'proposal-detail-label' }, [ + 'Implemented In: ' + ]), + html('div', { className: 'proposal-detail-value' }, [ + 'Swift ' + version + ]) + ]) +} + +/** For some proposal states like `.activeReview`, it helps to see the status in the same details list. */ +function renderStatus (status) { + return html('div', { className: 'proposal-detail' }, [ + html('div', { className: 'proposal-detail-label' }, [ + 'Status: ' + ]), + html('div', { className: 'proposal-detail-value' }, [ + states[status.state].name + ]) + ]) +} + +/** + * Review periods are ISO-8601-style 'YYYY-MM-DD' dates. + */ +function renderReviewPeriod (status) { + var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', + 'August', 'September', 'October', 'November', 'December' + ] + + var start = new Date(status.start) + var end = new Date(status.end) + + return html('div', { className: 'proposal-detail' }, [ + html('div', { className: 'proposal-detail-label' }, [ + 'Scheduled: ' + ]), + html('div', { className: 'proposal-detail-value' }, [ + months[start.getUTCMonth()], + ' ', + start.getUTCDate().toString(), + ' — ', + months[end.getUTCMonth()], + ' ', + end.getUTCDate().toString() + ]) + ]) +} + +/** Utility used by some of the `render*` functions to add comma text nodes between DOM nodes. */ +function _joinNodes (nodeList, text) { + return nodeList.map(function (node) { + return [node, text] + }).reduce(function (result, pair, index, pairs) { + if (index === pairs.length - 1) pair.pop() + return result.concat(pair) + }, []) +} + +/** Adds UI interactivity to the page. Primarily activates the filtering controls. */ +function addEventListeners () { + var nav = document.querySelector('nav') + + // typing in the search field causes the filter to be reapplied. + nav.addEventListener('keyup', filterProposals) + nav.addEventListener('change', filterProposals) + + // clearing the search field also hides the X symbol + nav.querySelector('#clear-button').addEventListener('click', function () { + nav.querySelector('#search-filter').value = '' + nav.querySelector('#clear-button').classList.toggle('hidden') + filterProposals() + }) + + // each of the individual statuses needs to trigger filtering as well + ;[].forEach.call(nav.querySelectorAll('.filter-by-status input'), function (element) { + element.addEventListener('change', filterProposals) + }) + + var expandableArea = document.querySelector('.filter-options') + var implementedToggle = document.querySelector('#filter-by-implemented') + implementedToggle.addEventListener('change', function () { + // hide or show the row of version options depending on the status of the 'Implemented' option + ;['#version-options', '#version-options-label'].forEach(function (selector) { + expandableArea.querySelector(selector).classList.toggle('hidden') + }) + + // don't persist any version selections when the row is hidden + ;[].concat.apply([], expandableArea.querySelectorAll('.filter-by-swift-version')).forEach(function (versionCheckbox) { + versionCheckbox.checked = false + }) + }) + + document.querySelector('.filter-button').addEventListener('click', toggleFiltering) + + var filterToggle = document.querySelector('.filter-toggle') + filterToggle.querySelector('.toggle-filter-panel').addEventListener('click', toggleFilterPanel) + + // Behavior conditional on certain browser features + var CSS = window.CSS + if (CSS) { + // emulate position: sticky when it isn't available. + if (!(CSS.supports('position', 'sticky') || CSS.supports('position', '-webkit-sticky'))) { + window.addEventListener('scroll', function () { + var breakpoint = document.querySelector('header').getBoundingClientRect().bottom + var nav = document.querySelector('nav') + var position = window.getComputedStyle(nav).position + + // this is measuring whether or not the header has scrolled offscreen + if (breakpoint <= 0) { + if (position !== 'fixed') { + nav.style.position = 'fixed' + } + } else if (position === 'fixed') { + nav.style.position = 'static' + } + }) + } + } + + // on smaller screens, hide the filter panel when scrolling + if (window.matchMedia('(max-width: 414px)').matches) { + window.addEventListener('scroll', function () { + var breakpoint = document.querySelector('header').getBoundingClientRect().bottom + if (breakpoint <= 0 && document.querySelector('.expandable').classList.contains('expanded')) { + toggleFilterPanel() + } + }) + } +} + +/** + * Toggles whether filters are active. Rather than being cleared, they are saved to be restored later. + * Additionally, toggles the presence of the "Filtered by:" status indicator. + */ +function toggleFiltering () { + var filterDescription = document.querySelector('.filter-toggle') + var shouldPreserveSelection = !filterDescription.classList.contains('hidden') + + filterDescription.classList.toggle('hidden') + var selected = document.querySelectorAll('.filter-by-status input[type=checkbox]:checked') + var filterButton = document.querySelector('.filter-button') + + if (shouldPreserveSelection) { + filterSelection = [].map.call(selected, function (checkbox) { return checkbox.id }) + ;[].forEach.call(selected, function (checkbox) { checkbox.checked = false }) + + filterButton.setAttribute('aria-pressed', 'false') + } else { // restore it + filterSelection.forEach(function (id) { + var checkbox = document.getElementById(id) + checkbox.checked = true + }) + + filterButton.setAttribute('aria-pressed', 'true') + } + + document.querySelector('.expandable').classList.remove('expanded') + filterButton.classList.toggle('active') + + filterProposals() +} + +/** + * Expands or constracts the filter panel, which contains buttons that + * let users filter proposals based on their current stage in the + * Swift Evolution process. + */ +function toggleFilterPanel () { + var panel = document.querySelector('.expandable') + var button = document.querySelector('.toggle-filter-panel') + + panel.classList.toggle('expanded') + + if (panel.classList.contains('expanded')) { + button.setAttribute('aria-pressed', 'true') + } else { + button.setAttribute('aria-pressed', 'false') + } +} + +/** + * Applies both the status-based and text-input based filters to the proposals list. + */ +function filterProposals () { + var filterElement = document.querySelector('#search-filter') + var filter = filterElement.value + + var clearButton = document.querySelector('#clear-button') + if (filter.length === 0) { + clearButton.classList.add('hidden') + } else { + clearButton.classList.remove('hidden') + } + + // The search input treats words as order-independent. + var matchingSets = filter.split(/\s/) + .filter(function (s) { return s.length > 0 }) + .map(function (part) { return _searchProposals(part) }) + + if (filter.trim().length === 0) { + matchingSets = [proposals.concat()] + } + + var intersection = matchingSets.reduce(function (intersection, candidates) { + return intersection.filter(function (alreadyIncluded) { return candidates.indexOf(alreadyIncluded) !== -1 }) + }, matchingSets[0] || []) + + _applyFilter(intersection) +} + +/** + * Utility used by `filterProposals`. + * + * Picks out various fields in a proposal which users may want to key + * off of in their text-based filtering. + * + * @param {string} filterText - A raw word of text as entered by the user. + * @returns {Proposal[]} The proposals that match the entered text, taken from the global list. + */ +function _searchProposals (filterText) { + var filterExpression = filterText.toLowerCase() + + var searchableProperties = [ + ['id'], + ['title'], + ['reviewManager', 'name'], + ['status', 'state'], + ['status', 'version'], + ['authors', 'name'], + ['authors', 'link'], + ['trackingBugs', 'link'], + ['trackingBugs', 'status'], + ['trackingBugs', 'id'], + ['trackingBugs', 'assignee'] + ] + + // reflect over the proposals and find ones with matching properties + var matchingProposals = proposals.filter(function (proposal) { + var match = false + searchableProperties.forEach(function (propertyList) { + var value = proposal + + propertyList.forEach(function (propertyName, index) { + if (!value) return + value = value[propertyName] + if (index < propertyList.length - 1) { + // For arrays, apply the property check to each child element. + // Note that this only looks to a depth of one property. + if (Array.isArray(value)) { + var matchCondition = value.some(function (element) { + return element[propertyList[index + 1]] && element[propertyList[index + 1]].toString().toLowerCase().indexOf(filterExpression) >= 0 + }) + + if (matchCondition) { + match = true + } + } else { + return + } + } else if (value && value.toString().toLowerCase().indexOf(filterExpression) >= 0) { + match = true + } + }) + }) + + return match + }) + + return matchingProposals +} + +/** + * Helper for `filterProposals` that actually makes the filter take effect. + * + * @param {Proposal[]} matchingProposals - The proposals that have passed the text filtering phase. + * @returns {Void} Toggles `display: hidden` to apply the filter. + */ +function _applyFilter (matchingProposals) { + // filter out proposals based on the grouping checkboxes + var allStateCheckboxes = document.querySelector('nav').querySelectorAll('.filter-by-status input:checked') + var selectedStates = [].map.call(allStateCheckboxes, function (checkbox) { return checkbox.value }) + + var selectedStateNames = [].map.call(allStateCheckboxes, function (checkbox) { return checkbox.nextElementSibling.innerText.trim() }) + updateFilterDescription(selectedStateNames) + + if (selectedStates.length) { + matchingProposals = matchingProposals + .filter(function (proposal) { + return selectedStates.some(function (state) { + return proposal.status.state.toLowerCase().indexOf(state.split('-')[0]) >= 0 + }) + }) + + // handle version-specific filtering options + if (selectedStates.some(function (state) { return state.match(/swift/i) })) { + matchingProposals = matchingProposals + .filter(function (proposal) { + return selectedStates.some(function (state) { + if (!(proposal.status.state === '.implemented')) return true // only filter among Implemented (N.N.N) + + var version = state.split(/\D+/).filter(function (s) { return s.length }).join('.') + + if (!version.length) return false // it's not a state that represents a version number + if (proposal.status.version === version) return true + return false + }) + }) + } + } + + var filteredProposals = proposals.filter(function (proposal) { + return matchingProposals.indexOf(proposal) === -1 + }) + + matchingProposals.forEach(function (proposal) { + var matchingElements = [].concat.apply([], document.querySelectorAll('.' + proposal.id)) + matchingElements.forEach(function (element) { element.classList.remove('hidden') }) + }) + + filteredProposals.forEach(function (proposal) { + var filteredElements = [].concat.apply([], document.querySelectorAll('.' + proposal.id)) + filteredElements.forEach(function (element) { element.classList.add('hidden') }) + }) + + updateProposalsCount(matchingProposals.length) +} + +/** + * Changes the text after 'Filtered by: ' to reflect the current status filters. + * + * After FILTER_DESCRIPTION_LIMIT filters are explicitly named, start combining the descriptive text + * to just state the number of status filters taking effect, not what they are. + * + * @param {string[]} selectedStateNames - CSS class names corresponding to which statuses were selected. + * Populated from the global `stateNames` array. + */ +function updateFilterDescription (selectedStateNames) { + var FILTER_DESCRIPTION_LIMIT = 2 + var stateCount = selectedStateNames.length + + // Limit the length of filter text on small screens. + if (window.matchMedia('(max-width: 414px)').matches) { + FILTER_DESCRIPTION_LIMIT = 1 + } + + var container = document.querySelector('.toggle-filter-panel') + + // modify the state names to clump together Implemented with version names + var swiftVersionStates = selectedStateNames.filter(function (state) { return state.match(/swift/i) }) + + if (swiftVersionStates.length > 0 && swiftVersionStates.length <= FILTER_DESCRIPTION_LIMIT) { + selectedStateNames = selectedStateNames.filter(function (state) { return !state.match(/swift|implemented/i) }) + .concat(`Implemented (${swiftVersionStates.join(', ')})`) + } + + if (selectedStateNames.length > FILTER_DESCRIPTION_LIMIT) { + container.innerText = `${stateCount} Filters` + } else if (selectedStateNames.length === 0) { + container.innerText = 'All Statuses' + } else { + container.innerText = selectedStateNames.join(' or ') + } +} + +/** Updates the `${n} Proposals` display just above the proposals list. */ +function updateProposalsCount (count) { + var numberField = document.querySelector('#proposals-count-number') + numberField.innerText = (count.toString() + ' proposal' + (count !== 1 ? 's' : '')) +} diff --git a/index.xml b/index.xml deleted file mode 100644 index e9b2d59737..0000000000 --- a/index.xml +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/index.xslt b/index.xslt deleted file mode 100644 index 4988dc05f7..0000000000 --- a/index.xslt +++ /dev/null @@ -1,282 +0,0 @@ - - - - - - - - - - - - Swift-Evolution Proposal Status - - - -

Swift Programming Language Evolution: Proposal Status

- -

The Swift evolution process describes the process by which Swift evolves. This page tracks the currently active proposals in that process.

- - - Active reviews - - - - - Upcoming reviews - - - - - Proposals awaiting scheduling - - - - - Accepted (awaiting implementation) - This is the list of proposals which have been accepted for inclusion into Swift, but they are not implemented yet, and may not have anyone signed up to implement them. If they are not implemented in time for Swift 3, they will roll into a subsequent release. - - - - - Implementation in progress - - - - - Implemented (Swift 4) - - - - - Implemented (Swift 3.1) - - - - - Implemented (Swift 3.0.1) - - - - - Implemented (Swift 3) - - - - - Implemented (Swift 2.2) - - - - - Deferred for future discussion - - - - - Returned for revision - - - - - Rejected - - - - - Withdrawn - - - - -
- - - - - - -
-

-

- - -

(none)

-
- - - - - -
-
-
-
-
- - - - - SE- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
From ec0924f84ab642d15965b482bff877e9c57390db Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Wed, 18 Jan 2017 16:47:48 -0800 Subject: [PATCH 0103/4563] [Status] Fix scrolling content height in browsers that lack `sticky`. --- index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/index.js b/index.js index 87e52fe3a8..b92cc86ab5 100644 --- a/index.js +++ b/index.js @@ -482,14 +482,21 @@ function addEventListeners () { var breakpoint = document.querySelector('header').getBoundingClientRect().bottom var nav = document.querySelector('nav') var position = window.getComputedStyle(nav).position + var shadowNav // maintain the main content height when the main 'nav' is removed from the flow // this is measuring whether or not the header has scrolled offscreen if (breakpoint <= 0) { if (position !== 'fixed') { + shadowNav = nav.cloneNode(true) + shadowNav.classList.add('clone') + shadowNav.style.visibility = 'hidden' + nav.parentNode.insertBefore(shadowNav, document.querySelector('main')) nav.style.position = 'fixed' } } else if (position === 'fixed') { nav.style.position = 'static' + shadowNav = document.querySelector('nav.clone') + if (shadowNav) shadowNav.parentNode.removeChild(shadowNav) } }) } From 24aafe9aa0cbcec757ab495c32e8718d14d5f809 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Wed, 18 Jan 2017 20:24:15 -0800 Subject: [PATCH 0104/4563] Mark SE-0080 as implemented in Swift 3.1. --- proposals/0080-failable-numeric-initializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0080-failable-numeric-initializers.md b/proposals/0080-failable-numeric-initializers.md index 880c1676bd..98eb594918 100644 --- a/proposals/0080-failable-numeric-initializers.md +++ b/proposals/0080-failable-numeric-initializers.md @@ -3,7 +3,7 @@ * Proposal: [SE-0080](0080-failable-numeric-initializers.md) * Author: [Matthew Johnson](https://github.com/anandabits) * Review Manager: [Chris Lattner](http://github.com/lattner) -* Status: **Accepted with revisions** +* Status: **Implemented (Swift 3.1)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000150.html) * Bug: [SR-1491](https://bugs.swift.org/browse/SR-1491) From 2ac5407215c97d9b6d14b22d44a55d1b9ed46691 Mon Sep 17 00:00:00 2001 From: Sergey Dunets Date: Thu, 19 Jan 2017 09:55:40 +0200 Subject: [PATCH 0105/4563] Fix broken link in SE-0069 (#588) --- proposals/0069-swift-mutability-for-foundation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0069-swift-mutability-for-foundation.md b/proposals/0069-swift-mutability-for-foundation.md index c5f0b80c35..e2da0fcb07 100644 --- a/proposals/0069-swift-mutability-for-foundation.md +++ b/proposals/0069-swift-mutability-for-foundation.md @@ -22,7 +22,7 @@ This concept is so important that it is literally the second thing taught in _Th > > Use `let` to make a constant and `var` to make a variable. The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once. > -> __Excerpt From: Apple Inc. “[The Swift Programming Language (Swift 2.1).](https://itun.es/us/jEUH0.l__)__” +> __Excerpt From: Apple Inc. “[The Swift Programming Language (Swift 3.0.1).](https://itun.es/us/jEUH0.l)__” When certain Foundation types are imported into Swift, they do not fully take advantage of the features that Swift has to offer developers for controlling mutability of their objects. From 825ec83f0ea93b28493a01ca3740042d1298863b Mon Sep 17 00:00:00 2001 From: Chris Eidhof Date: Thu, 19 Jan 2017 20:26:04 +0100 Subject: [PATCH 0106/4563] Proposal for generic subscripts (#584) --- proposals/XXXX-generic-subscripts.md | 69 ++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 proposals/XXXX-generic-subscripts.md diff --git a/proposals/XXXX-generic-subscripts.md b/proposals/XXXX-generic-subscripts.md new file mode 100644 index 0000000000..504c6fa368 --- /dev/null +++ b/proposals/XXXX-generic-subscripts.md @@ -0,0 +1,69 @@ +# Generic Subscripts + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Chris Eidhof](http://github.com/chriseidhof/) +* Review Manager: TBD +* Status: **Awaiting review** + +* Bugs: [SR-115](https://bugs.swift.org/browse/SR-115?jql=text%20~%20%22Generic%20subscript%22) + +## Introduction + +Make it possible to have generic subscripts. Example: + +```swift +extension Collection { + subscript(indices: Indices) -> [Iterator.Element] where Indices.Iterator.Element == Index { + // ... + } +} +``` + +Or a generic return type: + +```swift +extension JSON { + subscript(key: String) -> T? { + // ... + } +} +``` + +Swift-evolution thread: [Generic Subscripts](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170109/030064.html). + +## Motivation + +Currently, subscripts can't be generic. This is limiting in a number of ways: + +- Some subscripts are very specific and could be made more generic. +- Some generic methods would feel more natural as a subscript, but currently can't be. This also makes it impossible to use them as lvalues. + +This feature is also mentioned in the generics manifesto under [generic subscripts](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-subscripts). + +## Proposed solution + +Add generics to subscripts. There are two pieces to this: where to add the generic parameter list, and where to add the `where`-clause. The most straightforward way would be to use the same syntax as methods: + +```swift +extension Dictionary { + subscript(indices: Indices) -> [Iterator.Element] where Indices.Iterator.Element == Index { + // ... + } +} +``` + +## Source compatibility + +This is a purely additive change. We don't propose changing the Standard Library to use this new feature, that should be part of a separate proposal. (Likewise, we should consider making subscripts `throws` in a [separate proposal](https://github.com/brentdax/swift-evolution/blob/throwing-properties/proposals/0000-throwing-properties.md)). + +## Effect on ABI stability + +It won’t change the ABI of existing subscript calls. + +## Effect on API resilience + +It won’t change the ABI of existing subscript calls, but if the standard library introduces new generic subscripts that replace older non-generic subscripts, it will impact ABI. + +## Alternatives considered + +None. From b5788812dc7dc6c4f94f8a3ab1bade2f4ab07d0a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 19 Jan 2017 11:35:04 -0800 Subject: [PATCH 0107/4563] SE-0148 Generic Subscripts --- ...{XXXX-generic-subscripts.md => 0148-generic-subscripts.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{XXXX-generic-subscripts.md => 0148-generic-subscripts.md} (95%) diff --git a/proposals/XXXX-generic-subscripts.md b/proposals/0148-generic-subscripts.md similarity index 95% rename from proposals/XXXX-generic-subscripts.md rename to proposals/0148-generic-subscripts.md index 504c6fa368..be9e3287fe 100644 --- a/proposals/XXXX-generic-subscripts.md +++ b/proposals/0148-generic-subscripts.md @@ -1,8 +1,8 @@ # Generic Subscripts -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0148](0148-generic-subscripts.md) * Authors: [Chris Eidhof](http://github.com/chriseidhof/) -* Review Manager: TBD +* Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Awaiting review** * Bugs: [SR-115](https://bugs.swift.org/browse/SR-115?jql=text%20~%20%22Generic%20subscript%22) From 027a0663ad4cb196e9adc28013fd34a088413037 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 19 Jan 2017 13:38:34 -0800 Subject: [PATCH 0108/4563] Initiate review of SE-0148: Generic Subscripts --- proposals/0148-generic-subscripts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0148-generic-subscripts.md b/proposals/0148-generic-subscripts.md index be9e3287fe..dd65a05965 100644 --- a/proposals/0148-generic-subscripts.md +++ b/proposals/0148-generic-subscripts.md @@ -3,7 +3,7 @@ * Proposal: [SE-0148](0148-generic-subscripts.md) * Authors: [Chris Eidhof](http://github.com/chriseidhof/) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Awaiting review** +* Status: **Active review (January 19...January 24, 2017)** * Bugs: [SR-115](https://bugs.swift.org/browse/SR-115?jql=text%20~%20%22Generic%20subscript%22) From 747eb63c52de14a7b841bb2dc9737a05f7f2c11c Mon Sep 17 00:00:00 2001 From: Chris Eidhof Date: Mon, 23 Jan 2017 18:54:38 +0100 Subject: [PATCH 0109/4563] Generic subscripts fixes (#590) * More motivation * Default arguments --- proposals/0148-generic-subscripts.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/proposals/0148-generic-subscripts.md b/proposals/0148-generic-subscripts.md index dd65a05965..68ed47597d 100644 --- a/proposals/0148-generic-subscripts.md +++ b/proposals/0148-generic-subscripts.md @@ -38,7 +38,7 @@ Currently, subscripts can't be generic. This is limiting in a number of ways: - Some subscripts are very specific and could be made more generic. - Some generic methods would feel more natural as a subscript, but currently can't be. This also makes it impossible to use them as lvalues. -This feature is also mentioned in the generics manifesto under [generic subscripts](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-subscripts). +This feature is also mentioned in the generics manifesto under [generic subscripts](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generic-subscripts). The [Rationalizing Sequence end-operation names](https://github.com/apple/swift-evolution/blob/master/proposals/0132-sequence-end-ops.md) proposal could greatly benefit from this, as well as the ideas in the [String Manifesto](https://github.com/apple/swift/blob/master/docs/StringManifesto.md). ## Proposed solution @@ -52,6 +52,16 @@ extension Dictionary { } ``` +*Update Jan 20*: during the review it came up that while we're at it, we should add default arguments to subscripts. For example, the following (contrived) example: + +```swift +subscript(index: A? = nil) -> Element { + // ... +} +``` + +Adding default arguments would unify the compiler's handling of subscripts and functions. + ## Source compatibility This is a purely additive change. We don't propose changing the Standard Library to use this new feature, that should be part of a separate proposal. (Likewise, we should consider making subscripts `throws` in a [separate proposal](https://github.com/brentdax/swift-evolution/blob/throwing-properties/proposals/0000-throwing-properties.md)). From 19764b6e7466910b0a7c5058148401877888e6dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Mon, 23 Jan 2017 22:03:58 +0100 Subject: [PATCH 0110/4563] Proposal: Package Manager Support for ToT development --- proposals/xxxx-package-manager-top-of-tree.md | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 proposals/xxxx-package-manager-top-of-tree.md diff --git a/proposals/xxxx-package-manager-top-of-tree.md b/proposals/xxxx-package-manager-top-of-tree.md new file mode 100644 index 0000000000..4578ec67cf --- /dev/null +++ b/proposals/xxxx-package-manager-top-of-tree.md @@ -0,0 +1,40 @@ +# Package Manager Support for Top of Tree development + +* Proposal: [SE-XXXX](xxxx-package-manager-top-of-tree.md) +* Author: [Boris Bügling](https://github.com/neonichu) +* Review Manager: [Daniel Dunbar](https://github.com/ddunbar) +* Status: Discussion + +## Introduction + +This proposal adds enhancements to `swift package edit` to support development of packages without strict versioning ("top of tree" development). + +## Motivation + +The package manager currently supports package dependencies which are strictly versioned according to semantic versioning. This works well for users of packages and it is possible to edit a package in place using `swift package edit` already, but we want to allow developers to manually check out repositories on their machines as overrides. This is useful when developing multiple packages in tandem or when working on packages alongside an application. + +When a developer owns multiple packages that depend on each other, it can be necessary to work on a feature across more than one of them at the same time, without having to tag versions in between. The repositories for each package would usually already be checked out and managed manually by the developer, allowing them to switch branches or perform other SCM operations at will. + +A similar situation will arise when working on a feature that requires code changes to both an application and a dependent package. Developers want to iterate on the package by directly in the context of the application without having to release spurious versions of the package. Allowing developers to provide their own checkouts to the package manager as overrides will make this workflow much easier than it currently is. + +## Proposed solution + +As a solution to this problem, we propose to extend `swift package edit` to take an optional path argument to an existing checkout so that users can manually manage source control operations. + +## Detailed Design + +We will extend the `edit` subcommand with a new optional argument `--path`: + +```bash +$ swift package edit bar --path ../bar +``` + +This allows users to manage their own checkout of the `bar` repository and will make the package manager use that instead of checking out a tagged version as it normally would. Concretely, this will make `./Packages/bar` a symbolic link to the given path in the local filesystem, store this mapping inside the workspace and will ensure that the package manager will no longer be responsible for managing checkouts for the `bar` package, instead the user is responsible for managing the source control operations on their own. This is consistent with the current behavior of `swift edit`. Using `swift package unedit` will also work unchanged, but the checkout itself will not be deleted, only the symlink. If there is no existing checkout at the given filesystem location, the package manager will do an initial clone on the user's behalf. + +## Impact on existing code + +There will no impact on existing code. + +## Alternative considered + +We could have used the symlink in the `Packages` directory as primary data, but decided against it in order to be able to provide better diagnostics and distinguish use of `swift package edit` from the user manually creating symbolic links. This makes the symlink optional, but we decided to still create it in order to keep the structure of the `Packages` directory consistent independently of the use of this feature. From a4cbf95b9a143da91cdb999dae93323cc37e4c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Tue, 24 Jan 2017 00:30:10 +0100 Subject: [PATCH 0111/4563] Proposal: Package Manager Support for branches --- .../xxxx-package-manager-branch-support.md | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 proposals/xxxx-package-manager-branch-support.md diff --git a/proposals/xxxx-package-manager-branch-support.md b/proposals/xxxx-package-manager-branch-support.md new file mode 100644 index 0000000000..2078769865 --- /dev/null +++ b/proposals/xxxx-package-manager-branch-support.md @@ -0,0 +1,86 @@ +# Package Manager Support for branches + +* Proposal: [SE-XXXX](xxxx-package-manager-branch-support.md) +* Author: [Boris Bügling](https://github.com/neonichu) +* Review Manager: [Daniel Dunbar](https://github.com/ddunbar) +* Status: Discussion + +## Introduction + +This proposal adds enhancements to the package manifest to support development of packages without strict versioning. This is one of two features, along with "Package Manager Support for Top of Tree development", being proposed to enable use of SwiftPM to develop on "top of tree" of related packages. + +## Motivation + +The package manager currently supports packages dependencies which are strictly versioned according to semantic versioning. This is how a package's dependencies should be specified when that package is released, but this requirement hinders some development workflows: + +- bootstrapping a new package which does not yet have a version at all +- developing related packages in tandem in between releases, when one package may depend on the latest revision of another, which has not yet been tagged for release + +## Proposed solution + +As a solution to this problem, we propose to extend the package manifest to allow specifying a branch or revision instead of a version to support revlocked packages and initial bootstrapping. In addition, we will also allow specifying a branch or revision as an option to the `pin` subcommand. + +## Detailed Design + +### Specifying branches or revisions in the manifest + +We will introduce a second initializer for `.Package` which takes a branch instead of a version range: + +```swift +import PackageDescription + +let package = Package( + name: "foo", + dependencies: [ + .Package(url: "http://url/to/bar", branch: "development"), + ] +) +``` + +In addition, there is also the option to use a concrete revision instead: + +```swift +import PackageDescription + +let package = Package( + name: "foo", + dependencies: [ + .Package(url: "http://url/to/bar", revision: "0123456789012345678901234567890123456789"), + ] +) +``` + +Note that the revision parameter is a string, but it will still be sanity checked by the package manager. It will only accept the full 40 character commit hash here for Git and not a commit-ish or tree-ish. + +Whenever dependencies are checked out or updated, if a dependency on a package specifies a branch instead of a version, the latest commit on that branch will be checked out for that package. If a dependency on a package specifies a branch instead of a version range, it will override any versioned dependencies present in the current package graph that other packages might specify. + +For example, consider this graph with the packages A, B, C and D: + +A -> (B:master, C:master) + B -> D:branch1 + C -> D:branch2 + +The package manager will emit an error in this case, because there are dependencies on package D for both `branch1` and `branch2`. + +While this feature is useful during development, a package's dependencies should be updated to point at versions instead of branches before that package is tagged for release. This is because a released package should provide a stable specification of its dependencies, and not break when a branch changes over time. To enforce this, it is an error if a package referenced by a version-based dependency specifies a branch in any of its dependencies. + +Running `swift package update` will update packages referencing branches to their latest remote state. Running `swift package pin` will store the commit hash for the currently checked out revision in the pins file, as well as the branch name, so that other users of the package will receive the exact same revision if pinning is enabled. If a revision was specified, users will always receive that specific revision and `swift package update` becomes a no op. + +### Pinning to a branch or revision + +In addition to specifying a branch or revision in the manifest, we will also allow specifying it when pinning: + +```bash +$ swift pin --branch +$ swift pin --revision +``` + +This is meant to be used for situations where users want to temporarily change the source of a package, but it is just an alternative way to get the same semantics and error handling described in the previous section. + +## Impact on existing code + +There will no impact on existing code. + +## Alternative considered + +We decided to make using a version-based package dependency with unversioned dependencies an error, because a released package should provide a stable specification of its dependencies. A dependency on a branch could break at any time when the branch is being changed over time. From a99a69dfc4c7c09150af30a11ddf2cbdd00d2aa9 Mon Sep 17 00:00:00 2001 From: Daniel Dunbar Date: Tue, 24 Jan 2017 08:29:02 -0800 Subject: [PATCH 0112/4563] Assign proposal #s and dates for PR#594. --- ...r-top-of-tree.md => 0149-package-manager-top-of-tree.md} | 6 ++++-- ...ch-support.md => 0150-package-manager-branch-support.md} | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) rename proposals/{xxxx-package-manager-top-of-tree.md => 0149-package-manager-top-of-tree.md} (94%) rename proposals/{xxxx-package-manager-branch-support.md => 0150-package-manager-branch-support.md} (96%) diff --git a/proposals/xxxx-package-manager-top-of-tree.md b/proposals/0149-package-manager-top-of-tree.md similarity index 94% rename from proposals/xxxx-package-manager-top-of-tree.md rename to proposals/0149-package-manager-top-of-tree.md index 4578ec67cf..2f0d98911e 100644 --- a/proposals/xxxx-package-manager-top-of-tree.md +++ b/proposals/0149-package-manager-top-of-tree.md @@ -1,9 +1,11 @@ # Package Manager Support for Top of Tree development -* Proposal: [SE-XXXX](xxxx-package-manager-top-of-tree.md) +* Proposal: [SE-0149](0149-package-manager-top-of-tree.md) * Author: [Boris Bügling](https://github.com/neonichu) * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) -* Status: Discussion +* Status: **Active review (January 24...January 31, 2017)** + +* Bugs: [SR-3709](https://bugs.swift.org/browse/SR-3709) ## Introduction diff --git a/proposals/xxxx-package-manager-branch-support.md b/proposals/0150-package-manager-branch-support.md similarity index 96% rename from proposals/xxxx-package-manager-branch-support.md rename to proposals/0150-package-manager-branch-support.md index 2078769865..e1da1b2d58 100644 --- a/proposals/xxxx-package-manager-branch-support.md +++ b/proposals/0150-package-manager-branch-support.md @@ -1,9 +1,11 @@ # Package Manager Support for branches -* Proposal: [SE-XXXX](xxxx-package-manager-branch-support.md) +* Proposal: [SE-0150](0150-package-manager-branch-support.md) * Author: [Boris Bügling](https://github.com/neonichu) * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) -* Status: Discussion +* Status: **Active review (January 24...January 31, 2017)** + +* Bugs: [SR-666](https://bugs.swift.org/browse/SR-666) ## Introduction From 5b7d7232c9a072df455a43ceae8944409bb2ec24 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 24 Jan 2017 09:54:46 -0800 Subject: [PATCH 0113/4563] Process: improve directions for the "start of review" email. And drop the link in there that always trips us up. --- process.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/process.md b/process.md index b47c078eb7..7de1cc692e 100644 --- a/process.md +++ b/process.md @@ -122,7 +122,7 @@ A given proposal can be in one of several states: ## Review announcement When a proposal enters review, an email using the following template will be -sent to the swift-evolution-announce and swift-evolution mailing lists: +sent to the swift-evolution mailing list and BCC'd to the swift-evolution-announce mailing list: --- @@ -131,7 +131,7 @@ Hello Swift community, The review of "\<\>" begins now and runs through \<\>. The proposal is available here: -> +> http://linkToProposal Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at From 56e1c8ed24289f2e78efcb992cd48a6bbb77b2c2 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 27 Jan 2017 20:32:49 -0800 Subject: [PATCH 0114/4563] Accept SE-0148 "Generic Subscripts" --- proposals/0148-generic-subscripts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0148-generic-subscripts.md b/proposals/0148-generic-subscripts.md index 68ed47597d..be676b009e 100644 --- a/proposals/0148-generic-subscripts.md +++ b/proposals/0148-generic-subscripts.md @@ -3,7 +3,7 @@ * Proposal: [SE-0148](0148-generic-subscripts.md) * Authors: [Chris Eidhof](http://github.com/chriseidhof/) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (January 19...January 24, 2017)** +* Status: **Accepted** * Bugs: [SR-115](https://bugs.swift.org/browse/SR-115?jql=text%20~%20%22Generic%20subscript%22) From 86d1b3605f8be52b21da2a3afc0a00678581c63d Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Fri, 27 Jan 2017 20:50:20 -0800 Subject: [PATCH 0115/4563] Initial WIP draft of the tools version proposal. --- .../NNNN-package-manager-tools-version.md | 221 ++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 proposals/NNNN-package-manager-tools-version.md diff --git a/proposals/NNNN-package-manager-tools-version.md b/proposals/NNNN-package-manager-tools-version.md new file mode 100644 index 0000000000..ffb52d644d --- /dev/null +++ b/proposals/NNNN-package-manager-tools-version.md @@ -0,0 +1,221 @@ +# Package Manager Tools Version +* Proposal: [SE-NNNN](NNNN-package-manager-tools-version.md) +* Author: [Rick Ballard](https://github.com/rballard) +* Review manager: TBD +* Status: **WIP** + +## Introduction + +This proposal introduces a "Swift tools version" which is declared for each Swift package. +The tools version declares the minimum version of the Swift tools required to +use the package, determines what version of the PackageDescription API should +be used in the Package.swift manifest, and determines which Swift language +compatibility version should be used to parse the Package.swift manifest. + +This feature shall be added to Swift 3.1, to allow packages to manage the transition +from Swift 3 to Swift 4 compatibility. + +## Motivation + +This proposal addresses three problems with one mechanism. + +First, when a package adopts new features of Swift or the Swift Package Manager, +it may no longer be able to be compiled by an older version of the Swift tools, +and older tools may not even be able to interpret its Package.swift manifest. +Without a mechanism to handle this, a package author who tags a version +of their package which uses new features may break the builds of all +clients of that package unless those clients immediately adopt +the newest tools. This is especially problematic during development of +a new version of Swift, when new features have been added which are not yet +suppoted by the current release of Swift. + +Second, we may wish to evolve the Package.swift PackageDescription API to conform +to newer Swift conventions or to correct historical mistakes. We need some mechanism +to allow us to revise API without requiring packages to adopt +the changes immediately, and to allow packages to be compatable with both older and +newer Swift tools when desired. + +Finally, as the Package.swift manifest is itself written in Swift, some mechanism +is needed to control which Swift language compatibility version should be used +when interpreting the manifest. This cannot be determined by a compatibility +version property in the manifest itself, as we must know what compatibility version +to interpret the manifest with before we have access to the data in the manifest. + +## Proposed solution + +Each package will specify a Swift version (the "Swift tools version") which is the minimum version +of Swift currently needed to build that package. This minimum version +will be specified in a file in the package, so it is managed in source +control just like any other package data and may differ for different +tagged versions of the package. + +When adopting new or revised PackageDescription API, or when making changes to a Package's source +code which require a new version of Swift, users will be expected to update +the package's Swift tools version to specify that it requires that version +of Swift. + +When an incompatible revision is made to the PackageDescription API, the Swift Package Manager +will continue to include the older version of the PackageDescription module for backwards +compatibility. The Swift tools version shall determine which version of the PackageDescription +module will be used when interpreting the Package.swift manifest. + +The Swift Tools Version will also determine which Swift language compatibility +version should be used when interpreting the Package.swift manifest. +And it will determine the default Swift language compatibility version used to compile +the package's sources if not otherwise specified. + +## Detailed design + +When resolving package dependencies, if the version of a dependency that would normally +be chosen specifies a Swift tools version which is greater than the version in use, that +version of the dependency will be considered ineligable and dependency +resolution will continue with evaluating the next-best version. If +no version of a dependency (which otherwise meets the version requirements +from the package dependency graph) supports the version of Swift in use, a +dependency resolution error will result. + +When new PackageDescription API is added which would not be understood by a +prior version of the Swift Package Manager, it will be added to the current version +of the PackageDescription module and will not require the package manager to +include a version of the module without that API. However, if that new API is used in the +Package.swift manifest, it will cause the package manager to validate that the +Swift tools version of that package specifies a version of the tools which understands that API, +or to emit an error with instructions to update the Swift tools version if not. +Note that if a [version-specific manifest](https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#version-specific-manifest-selection) +is present for the older tools version, that tools version will validate as +allowable even when newer features are adopted in the main Package.swift manifest. + +The Swift Package Manager may consider the Swift tools version of a package +for other compatibility-related purposes as well. For example, if a +bugfix in a new version of the Swift Package Manager might break older +packages, the fixed behavior might only be applied to packages which have +adopted the newer Swift tools version. + +The Swift tools version will be specified in a file named ".swift-version", +at the root level of the package. This file will follow the conventions +already estabished by the [SwiftEnv](https://github.com/kylef/swiftenv/) +project. The file will contain either a Swift marketing version number +or the name of a snapshot which has been published on Swift.org. + +The Swift Package Manager will have a database of known snapshot names which have +been published on Swift.org, as well as which Swift tools version each snapshot +corresponds to. A snapshot shall correspond to the first release which included +or will include that snapshot's content, and all snapshots +of a release in development shall be considered equivalent for the purposes +of the Swift tools version. This feature does not attempt to specify +compatibility between different snapshots during the development of a release, and +only allows snapshot names to be specified to preserve compatibility with SwiftEnv. + +The Swift tools version, as reported by package manager commands, will always take +the form of a Swift marketing version (with +one, two, or three version components), the string "future", or the string "trunk". +"future" indicates that the .swift-version file specifies a snapshot name which is unknown and +thus belongs to a future version of Swift. "trunk" indicates that the .swift-version file specifies +a snapshot name which is known to this version of Swift, but which has not yet +been given a designated marketing version. "trunk" shall always be considered +compatible with the current version of package manager, and "future" shall +always be considered incompatible. + +A new `swift package toolsversion` command will be added to manage the +Swift tools version. This command will behave as follows: + +* `swift package toolsversion` will report the Swift tools version of the package. + +* `swift package toolsversion [value]` will set the Swift tools version to `value`. +It will also print an informational message advising the user of any changes that this +tools version change will necessitate, depending on what the prior and new tools versions +were. For example, changing the tools version might require converting the Package.swift manifest to +a different Swift language version, and to a different version of the PackageDescription API. + +* `swift package toolsversion --update` will set the Swift tools version to the version +of the tools currently in use. + +If no `.swift-version` file is present, the Swift tools version is "3.0". +It is expected that in the future all Swift packages will include a +`.swift-version` file. `swift package init` will create this file and set +it to the marketing version of the tools in use. + +The Swift tools version will determine the default Swift language compatibility version +used to compile the package's Swift sources if unspecified, but the Swift language +compatibility version for the package's sources is otherwise decoupled from the +Swift tools version. A separate Swift evolution proposal will describe how +to specify a Swift language compatibility version for package sources. + +## Examples + +* The author of a package created with Swift 3 wishes to adopt the new Swift 4 +product definitions API in their manifest. Using a Swift 4 toolchain, the author first runs +`swift package toolsversion --update` to make their package require Swift 4.0. They then make +any changes to their Package.swift manifest needed to make it compatible with the Swift 4 +language version and the revised Swift 4 Package Manager PackageDescription API. +The author is now free to adopt new PackageDescription API in their Package.swift manifest. +They are not required to update the language version of their package sources at the same time. + +* A package author wishes to support both the Swift 3 and Swift 4 tools, while +conditionally adopting Swift 4 language features. The author specifies both +Swift language compatibility versions for their package sources (using a +mechanism discussed in a seperate evolution proposal). Because their package +needs to support Swift 3 tools, the package's Swift tools version must be set +to `3.1`. Their Package.swift manifest must continue to be compatible with the +Swift 3 language, and must continue to use the Swift 3.1 version of the +PackageDescription API. + +* The author of a package created with Swift 3 wishes to convert the package's sources to the Swift 4 language +version. They specify Swift 4 as their package's language compatibility version (using a mechanism +discussed in a seperate evolution proposal). When they try to build their package, the package manager +emits an error informing them that they must update their Swift tools version to 4.0 or later, because +the Swift 4 tools are required to build a package when it no longer supports the Swift 3 language version. +The author runs `swift package toolsversion --update` to make their package require Swift 4.0. They then make +any changes to their Package.swift manifest required to make it compatible with the Swift 4 +language version and the revised Swift 4 Package Manager PackageDescription API. + +## Impact on existing code + +There is no impact on existing packages. Since existing packages either have no +.swift-version file, or have a .swift-version which specifies a Swift 3 +toolchain or version, those packages will default to building their sources +with the Swift 3 language compatibility mode, and will be able to be +built by both Swift 3 and Swift 4. + +Use of the package manager's [version-specific tag selection](https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#version-specific-tag-selection) +mechanism will no longer be necessary in many situations. Previously, authors needed to employ +that mechanism in order to tag the last version of a package compatible with an old +version of Swift before adopting new Swift features, to avoid break clients +of the package still using the old version of Swift. Now, when adopting new Swift features, a +package author merely needs to set their Swift tools version to that new version, and +dependency resolution from an older Swift version (in Swift 3.1 or later) will consider +those new package versions ineligable. + +The existing version-specific tag selection mechanism may still be useful for +authors who wish to publish new parallel versions of their package for multiple +versions of Swift. + +Use of the package manager's [version-specific manifest selection](https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#version-specific-manifest-selection) +mechanism may still be useful for authors who wish to conditionally adopt new Swift features in +their Package.swift manifest without needing to update their Swift tools version to +exclude older versions of Swift. + +Packages which have used conditional compilation blocks in the Package.swift +manifest to adopt new PackageDescription features while remaining compatible +with older versions of Swift will no longer be able to do so for future versions +of Swift, and must instead use version-specific manifest selection. This is +because when the newer tools interpret the Package.swift manifest, those tools will see +that new PackageDescription APIs are in use, will not detect the alternate code behind +the conditional compilation blocks, and will thus emit an error requiring the +user to update the Swift tools version to a version which supports those new +APIs. + +## Alternatives considered + +WIP. + +* Why not just try reparsing manifest in different versions until we find one that parses successfully? + +* Why not just rely on conditional compilation, and/or the old version mechanisms? + +* Why tie these three needs to one version? + +* Why not use semver for this? + +* Note that without this, packages either would fail to build with swift 4 without a change to explicitly specify 3, +or would forever default to swift 3 unless specified otherwise From 970711e6a1dfe196141d46e03c3eef1b07139e07 Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Tue, 31 Jan 2017 09:28:35 -0800 Subject: [PATCH 0116/4563] Update proposal based on PR feedback from the past few days. --- .../NNNN-package-manager-tools-version.md | 87 ++++++++++++------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/proposals/NNNN-package-manager-tools-version.md b/proposals/NNNN-package-manager-tools-version.md index ffb52d644d..1bc3d8c7c0 100644 --- a/proposals/NNNN-package-manager-tools-version.md +++ b/proposals/NNNN-package-manager-tools-version.md @@ -24,16 +24,18 @@ it may no longer be able to be compiled by an older version of the Swift tools, and older tools may not even be able to interpret its Package.swift manifest. Without a mechanism to handle this, a package author who tags a version of their package which uses new features may break the builds of all -clients of that package unless those clients immediately adopt -the newest tools. This is especially problematic during development of -a new version of Swift, when new features have been added which are not yet -suppoted by the current release of Swift. - -Second, we may wish to evolve the Package.swift PackageDescription API to conform -to newer Swift conventions or to correct historical mistakes. We need some mechanism -to allow us to revise API without requiring packages to adopt -the changes immediately, and to allow packages to be compatable with both older and -newer Swift tools when desired. +clients of that package who are using older tools unless that pacakage adjusts its +major semantic version, which would unnecessarily stop clients who are using the +latest tools from getting that version. This is especially problematic during development of +a new version of the Swift tools, when new features have been added which are not yet +suppoted by the current release of the Swift tools. + +Second, one specific planned change for the Swift Package Manager is a revision +of the Package.swift PackageDesciption API, to make it conform to +newer Swift conventions and to correct historical mistakes. In order to support backwards +compatibility, the old version of the PackageDescription API must remain available. +We need some way to determine which version of the PackageDescription API a package +wishes to use. Finally, as the Package.swift manifest is itself written in Swift, some mechanism is needed to control which Swift language compatibility version should be used @@ -44,7 +46,7 @@ to interpret the manifest with before we have access to the data in the manifest ## Proposed solution Each package will specify a Swift version (the "Swift tools version") which is the minimum version -of Swift currently needed to build that package. This minimum version +of the Swift tools currently needed to build that package. This minimum version will be specified in a file in the package, so it is managed in source control just like any other package data and may differ for different tagged versions of the package. @@ -52,7 +54,7 @@ tagged versions of the package. When adopting new or revised PackageDescription API, or when making changes to a Package's source code which require a new version of Swift, users will be expected to update the package's Swift tools version to specify that it requires that version -of Swift. +of the Swift tools. When an incompatible revision is made to the PackageDescription API, the Swift Package Manager will continue to include the older version of the PackageDescription module for backwards @@ -71,7 +73,7 @@ be chosen specifies a Swift tools version which is greater than the version in u version of the dependency will be considered ineligable and dependency resolution will continue with evaluating the next-best version. If no version of a dependency (which otherwise meets the version requirements -from the package dependency graph) supports the version of Swift in use, a +from the package dependency graph) supports the version of the Swift tools in use, a dependency resolution error will result. When new PackageDescription API is added which would not be understood by a @@ -93,7 +95,7 @@ adopted the newer Swift tools version. The Swift tools version will be specified in a file named ".swift-version", at the root level of the package. This file will follow the conventions -already estabished by the [SwiftEnv](https://github.com/kylef/swiftenv/) +already estabished by the [swiftenv](https://github.com/kylef/swiftenv/) project. The file will contain either a Swift marketing version number or the name of a snapshot which has been published on Swift.org. @@ -104,9 +106,9 @@ or will include that snapshot's content, and all snapshots of a release in development shall be considered equivalent for the purposes of the Swift tools version. This feature does not attempt to specify compatibility between different snapshots during the development of a release, and -only allows snapshot names to be specified to preserve compatibility with SwiftEnv. +only allows snapshot names to be specified to preserve compatibility with swiftenv. -The Swift tools version, as reported by package manager commands, will always take +The Swift tools version, as output by package manager commands, will always take the form of a Swift marketing version (with one, two, or three version components), the string "future", or the string "trunk". "future" indicates that the .swift-version file specifies a snapshot name which is unknown and @@ -114,20 +116,22 @@ thus belongs to a future version of Swift. "trunk" indicates that the .swift-ver a snapshot name which is known to this version of Swift, but which has not yet been given a designated marketing version. "trunk" shall always be considered compatible with the current version of package manager, and "future" shall -always be considered incompatible. +always be considered incompatible. It is an error to specify "future" or "trunk" +directly in the `.swift-version` file; these specifiers are determined +from the package manager's interpretation of the `.swift-version` file. -A new `swift package toolsversion` command will be added to manage the +A new `swift package tools-version` command will be added to manage the Swift tools version. This command will behave as follows: -* `swift package toolsversion` will report the Swift tools version of the package. +* `swift package tools-version` will report the Swift tools version of the package. -* `swift package toolsversion [value]` will set the Swift tools version to `value`. +* `swift package tools-version --set ` will set the Swift tools version to `value`. It will also print an informational message advising the user of any changes that this tools version change will necessitate, depending on what the prior and new tools versions were. For example, changing the tools version might require converting the Package.swift manifest to a different Swift language version, and to a different version of the PackageDescription API. -* `swift package toolsversion --update` will set the Swift tools version to the version +* `swift package tools-version --set-current` will set the Swift tools version to the version of the tools currently in use. If no `.swift-version` file is present, the Swift tools version is "3.0". @@ -145,7 +149,7 @@ to specify a Swift language compatibility version for package sources. * The author of a package created with Swift 3 wishes to adopt the new Swift 4 product definitions API in their manifest. Using a Swift 4 toolchain, the author first runs -`swift package toolsversion --update` to make their package require Swift 4.0. They then make +`swift package tools-version --update` to make their package require the Swift 4.0 tools. They then make any changes to their Package.swift manifest needed to make it compatible with the Swift 4 language version and the revised Swift 4 Package Manager PackageDescription API. The author is now free to adopt new PackageDescription API in their Package.swift manifest. @@ -160,12 +164,12 @@ to `3.1`. Their Package.swift manifest must continue to be compatible with the Swift 3 language, and must continue to use the Swift 3.1 version of the PackageDescription API. -* The author of a package created with Swift 3 wishes to convert the package's sources to the Swift 4 language +* The author of a package created with the Swift 3 tools wishes to convert the package's sources to the Swift 4 language version. They specify Swift 4 as their package's language compatibility version (using a mechanism discussed in a seperate evolution proposal). When they try to build their package, the package manager emits an error informing them that they must update their Swift tools version to 4.0 or later, because the Swift 4 tools are required to build a package when it no longer supports the Swift 3 language version. -The author runs `swift package toolsversion --update` to make their package require Swift 4.0. They then make +The author runs `swift package tools-version --update` to make their package require the Swift 4.0 tools. They then make any changes to their Package.swift manifest required to make it compatible with the Swift 4 language version and the revised Swift 4 Package Manager PackageDescription API. @@ -175,29 +179,29 @@ There is no impact on existing packages. Since existing packages either have no .swift-version file, or have a .swift-version which specifies a Swift 3 toolchain or version, those packages will default to building their sources with the Swift 3 language compatibility mode, and will be able to be -built by both Swift 3 and Swift 4. +built by both Swift 3 and Swift 4 tools. Use of the package manager's [version-specific tag selection](https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#version-specific-tag-selection) mechanism will no longer be necessary in many situations. Previously, authors needed to employ that mechanism in order to tag the last version of a package compatible with an old -version of Swift before adopting new Swift features, to avoid break clients -of the package still using the old version of Swift. Now, when adopting new Swift features, a +version of the Swift tools before adopting new Swift features, to avoid break clients +of the package still using the old version of the Swift tools. Now, when adopting new Swift features, a package author merely needs to set their Swift tools version to that new version, and -dependency resolution from an older Swift version (in Swift 3.1 or later) will consider +dependency resolution performed by an older version of the Swift tools (starting with Swift 3.1) will consider those new package versions ineligable. The existing version-specific tag selection mechanism may still be useful for authors who wish to publish new parallel versions of their package for multiple -versions of Swift. +versions of the Swift tools. Use of the package manager's [version-specific manifest selection](https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#version-specific-manifest-selection) -mechanism may still be useful for authors who wish to conditionally adopt new Swift features in +mechanism may still be useful for authors who wish to conditionally adopt new features of the Swift tool in their Package.swift manifest without needing to update their Swift tools version to exclude older versions of Swift. Packages which have used conditional compilation blocks in the Package.swift manifest to adopt new PackageDescription features while remaining compatible -with older versions of Swift will no longer be able to do so for future versions +with older versions of the Swift tools will no longer be able to do so for future versions of Swift, and must instead use version-specific manifest selection. This is because when the newer tools interpret the Package.swift manifest, those tools will see that new PackageDescription APIs are in use, will not detect the alternate code behind @@ -205,6 +209,25 @@ the conditional compilation blocks, and will thus emit an error requiring the user to update the Swift tools version to a version which supports those new APIs. +The following table shows an example of which Swift language version will be used to interpret the Package.swift manifest, and to interpret the package's sources, based on the Swift tools in use and the parameters specified by the package. + +| Swift Tools | .swift-version | [Swift Language Compatibility Version](http://link/to/proposal) | Language Version Used | +|:---:|:---:|:---:| --- | +| 3.1 | Not Present | Not Present | Manifest: 3 Sources: 3 | +| 3.1 | 3.1 | Not Present | Manifest: 3 Sources: 3 | +| 3.1 | 3.1 | 3 | Manifest: 3 Sources: 3 | +| 3.1 | 3.1 | 3, 4 | Manifest: 3 Sources: 3 | +| Any | 3.1 | 4 | Error | +| 3.1 | 4.0 | Any | Error | +| 4.0 | Not Present | Not Present | Manifest: 3 Sources: 3 | +| 4.0 | 3.1 | Not Present | Manifest: 3 Sources: 3 | +| 4.0 | 3.1 | 3 | Manifest: 3 Sources: 3 | +| 4.0 | 3.1 | 3, 4 | Manifest: 3 Sources: 4 | +| 4.0 | 4.0 | Not Present | Manifest: 4 Sources: 4 | +| 4.0 | 4.0 | 3 | Manifest: 4 Sources: 3 | +| 4.0 | 4.0 | 3, 4 | Manifest: 4 Sources: 4 | +| 4.0 | 4.0 | 4 | Manifest: 4 Sources: 4 | + ## Alternatives considered WIP. @@ -219,3 +242,5 @@ WIP. * Note that without this, packages either would fail to build with swift 4 without a change to explicitly specify 3, or would forever default to swift 3 unless specified otherwise + +* Explain why extra file isn't a burden (mostly maintain automatically) From e692c2ef7f0cc88c302af384a411745099a4267c Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Wed, 1 Feb 2017 13:19:38 -0800 Subject: [PATCH 0117/4563] Revisions based on Ankit's 2017/2/1 feedback; also typo fixes. --- proposals/NNNN-package-manager-tools-version.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-package-manager-tools-version.md b/proposals/NNNN-package-manager-tools-version.md index 1bc3d8c7c0..544111498e 100644 --- a/proposals/NNNN-package-manager-tools-version.md +++ b/proposals/NNNN-package-manager-tools-version.md @@ -152,13 +152,16 @@ product definitions API in their manifest. Using a Swift 4 toolchain, the author `swift package tools-version --update` to make their package require the Swift 4.0 tools. They then make any changes to their Package.swift manifest needed to make it compatible with the Swift 4 language version and the revised Swift 4 Package Manager PackageDescription API. +Since their package sources are still written with Swift 3, they should specify +the Swift 3 language compatibility version in their manifest, if they didn't already, +so that it doesn't start defaulting to building the sources as Swift 4 code. The author is now free to adopt new PackageDescription API in their Package.swift manifest. They are not required to update the language version of their package sources at the same time. * A package author wishes to support both the Swift 3 and Swift 4 tools, while conditionally adopting Swift 4 language features. The author specifies both Swift language compatibility versions for their package sources (using a -mechanism discussed in a seperate evolution proposal). Because their package +mechanism discussed in a separate evolution proposal). Because their package needs to support Swift 3 tools, the package's Swift tools version must be set to `3.1`. Their Package.swift manifest must continue to be compatible with the Swift 3 language, and must continue to use the Swift 3.1 version of the @@ -166,7 +169,7 @@ PackageDescription API. * The author of a package created with the Swift 3 tools wishes to convert the package's sources to the Swift 4 language version. They specify Swift 4 as their package's language compatibility version (using a mechanism -discussed in a seperate evolution proposal). When they try to build their package, the package manager +discussed in a separate evolution proposal). When they try to build their package, the package manager emits an error informing them that they must update their Swift tools version to 4.0 or later, because the Swift 4 tools are required to build a package when it no longer supports the Swift 3 language version. The author runs `swift package tools-version --update` to make their package require the Swift 4.0 tools. They then make From a5dac9aeb0f4f71d7ddbf49c73850163a250fa76 Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Wed, 1 Feb 2017 16:00:53 -0800 Subject: [PATCH 0118/4563] Wrote first draft of Alternatives Considered --- .../NNNN-package-manager-tools-version.md | 162 ++++++++++++++++-- 1 file changed, 147 insertions(+), 15 deletions(-) diff --git a/proposals/NNNN-package-manager-tools-version.md b/proposals/NNNN-package-manager-tools-version.md index 544111498e..11242edad5 100644 --- a/proposals/NNNN-package-manager-tools-version.md +++ b/proposals/NNNN-package-manager-tools-version.md @@ -154,7 +154,7 @@ any changes to their Package.swift manifest needed to make it compatible with th language version and the revised Swift 4 Package Manager PackageDescription API. Since their package sources are still written with Swift 3, they should specify the Swift 3 language compatibility version in their manifest, if they didn't already, -so that it doesn't start defaulting to building the sources as Swift 4 code. +so that it doesn't start defaulting to building their sources as Swift 4 code. The author is now free to adopt new PackageDescription API in their Package.swift manifest. They are not required to update the language version of their package sources at the same time. @@ -233,17 +233,149 @@ The following table shows an example of which Swift language version will be use ## Alternatives considered -WIP. - -* Why not just try reparsing manifest in different versions until we find one that parses successfully? - -* Why not just rely on conditional compilation, and/or the old version mechanisms? - -* Why tie these three needs to one version? - -* Why not use semver for this? - -* Note that without this, packages either would fail to build with swift 4 without a change to explicitly specify 3, -or would forever default to swift 3 unless specified otherwise - -* Explain why extra file isn't a burden (mostly maintain automatically) +We considered a number of alternative approaches that might avoid the need for +adding this new Swift tools version; however, we think that this proposal +is compelling compared to the alternatives considered. + +### Don't change the PackageDescription manifest API + +If we chose not to change the PackageDescription API, we would not need a way +to determine which version of the module to use when interpreting a manifest. +However, we think that it is important for this API to be made compliant with +the Swift language conventions, and to review the API with our community. +It would be best to do this now, while the Swift package ecosystem is relatively +young; in the future, when the ecosystem is more mature, it will be more +painful to make significant changes to this API. + +Not changing this API would still leave the problem of figuring out which +Swift language compatibility version to interpret the manifest in. It's possible +that Package.swift manifests won't be significantly affected by Swift +language changes in Swift 4, and could mostly work in either language compatibility +mode without changes. However, we don't know whether that will be the case, +and it would be a significant risk to assume that it will be. + +Finally, we will need to add new API to the PackageDescription module to +support new features, and without a Swift tools version, adoption of new features +would break existing clients of a package that aren't using the latest tools. + +### Rely on conditional compilation blocks + +We could choose to ask package authors to use Swift conditional compilation +blocks to make their manifests compatible with both Swift 3 and Swift 4. +Unfortunately, this might be a lot of work for package authors, and result in a +hard-to-read manifests, if the PackageDescription API changes or Swift 4 +language changes are significant. + +Another major downside of this approach is that until package authors do the +work of adding conditional compilation blocks, their packages would fail to +build with the Swift 4 tools. In order to build with the Swift 4 tools, you'd +both need to update your own packages with conditional compilation, and you'd +need to wait for any packages you depend upon to do the same. This could be a +major obstacle to adopting the Swift 4 tools. + +Finally, we are not convinced that all authors would bother to add conditional +compilation blocks to preserve Swift 3 compatibility when they update their +packages for the Swift 4 tools. Any packages which were updated but not given +conditional compilation blocks would now break the builds of any clients still +using the Swift 3 tools. + +### Rely on semantic versioning + +We could expect that package authors bump their packages' major semantic version +when updating those packages for the Swift 4 tools, thereby preventing clients +who were still using the Swift 3 tools from automatically getting the updated +version of their dependency and failing to build. There are several problems with +this approach. + +First, this does nothing to allow packages to be used with new Swift tools without +needing to be updated for those tools. We don't want package authors to need +to immediately adopt the Swift 4 language compatibility version and PackageDescription +API before they can build their package with the new tools. + +Second, this forces clients of a package to explicitly opt-in to updated +versions of their dependencies, even if there was otherwise no API change. +The Swift tools version mechanism that we have proposed allows packages +to automatically get updated versions of their dependencies when using Swift +tools that are new enough to be able to build them, which is preferable. + +Finally, we are not confident that all package authors would reliably update +their semantic version when updating their package for newer tools. If they failed +to do so, clients still using the older Swift tools would fail to build. + +### Relying on the package manager's existing versioning mechanisms + +In Swift 3, the package manager introduced two versioning mechanisms: +[version-specific tag selection](https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#version-specific-tag-selection), +and [version-specific manifest selection](https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#version-specific-manifest-selection). +These mechanisms can be used to publish updated versions of a package without +breaking the builds of clients who are still using older Swift tools. We think +that these mechanisms are still useful and can be used in concert with the +Swift tools version, as described in the "Impact on existing code" section. +However, they are insufficient to completely solve the versioning problem. + +These mechanisms allow a package to be updated for new Swift tools without +breaking clients who are still using older Swift tools, but they do not allow +a package that has not been updated to be built with new Swift tools. Again, +we don't want package authors to need to immediately adopt the Swift 4 language +compatibility version and PackageDescription API before they can build their +package with the new tools. + +These mechanisms are also opt-in and may not be known to all package authors. +If a package author fails to explicitly adopt these mechanisms when updating +a package, they will break the builds of clients that are still using older +Swift tools. In contrast, the Swift tools version mechanism that we have proposed works +by default without requiring package authors to know about extra opt-in +mechanisms. + +### Automatically re-interpret Package.swift manifest in different modes + +We considered having the package manager automatically try to reinterpret +a Package.swift manifest in different modes until it finds a mode that +can successfully interpret it, so that we wouldn't need an explicit specifier +of which Swift language compatibility version or PackageDescription module version +the Package.swift manifest is using. + +We saw three major problems with this. First, this would make it very difficult +to provide high quality diagnostics when a manifest has an error or warning. +If the manifest cannot be interpreted cleanly in any of the supported modes, +we'd have no way to know which mode it should have been interpreted in -- +or whether the required mode is even known to the version of the Swift +tools in use. That means that the errors we provide might be incorrect +with respect to the actual version of the Swift language or the PackageDescription +module that the manifest targets. + +Second, any subtle incompatibilities introduced by a difference in Swift language +compatibility versions could cause the manifest to interpret without errors +in the wrong mode, but result in unexpected behavior. + +Finally, this could cause performance problems for the package manager. Because +the package manager needs to interpret Package.swift manifests from potentially +many packages during dependency resolution, forcing it to interpret +each manifest multiple times could add an undesirable delay to the +`swift build` or `swift package update` commands. + +### Provide finer-grained versioning controls + +In this proposal we've provided a single versioning mechanism which controls multiple +things: the Swift language compatibility version used to parse the manifest, +the version of the PackageDescription module to use, and the minimum version +of the tools which will consider a package version eligible during dependency +resolution. We could instead provide separate controls for each of these +things. However, we feel that doing so would add unnecessary complexity to +the Swift package manager. We do not see a compelling use-case for needing +to control these different features independently, so we are consolidating +them into one version mechanism as a valuable simplification. + +### Specify the Swift tools version without needing a new file for it + +This proposal does add a new file (`.swift-version`) that every package is +expected to provide. We could instead store the Swift tools version somewhere +else. The most likely candidate for that would be the Package.swift manifest +itself. The immediate problem with doing so is that we need to know how to +interpret the Swift manifest in order to get data out of it, and the Swift +tools version provides information needed to know how to interpret the manifest. +We could consider putting the version at the very top of the file, perhaps +in a specially-formatted Swift code comment, and parsing the top of the file +to extract that version before running it through the Swift interpreter. + +(More discussion of this alternative to come) From 8a091dd3d9d779235e6f6a27c927868062d7bb80 Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Wed, 1 Feb 2017 20:25:05 -0800 Subject: [PATCH 0119/4563] Update proposal to spell out three possibilities for how to specify the Swift Tools Version --- .../NNNN-package-manager-tools-version.md | 242 +++++++++++++----- 1 file changed, 179 insertions(+), 63 deletions(-) diff --git a/proposals/NNNN-package-manager-tools-version.md b/proposals/NNNN-package-manager-tools-version.md index 11242edad5..0e6b164cfc 100644 --- a/proposals/NNNN-package-manager-tools-version.md +++ b/proposals/NNNN-package-manager-tools-version.md @@ -24,24 +24,25 @@ it may no longer be able to be compiled by an older version of the Swift tools, and older tools may not even be able to interpret its Package.swift manifest. Without a mechanism to handle this, a package author who tags a version of their package which uses new features may break the builds of all -clients of that package who are using older tools unless that pacakage adjusts its +clients of that package who are using older tools unless that package adjusts its major semantic version, which would unnecessarily stop clients who are using the latest tools from getting that version. This is especially problematic during development of a new version of the Swift tools, when new features have been added which are not yet -suppoted by the current release of the Swift tools. +supported by the current release of the Swift tools. Second, one specific planned change for the Swift Package Manager is a revision -of the Package.swift PackageDesciption API, to make it conform to +of the Package.swift PackageDescription API, to make it conform to newer Swift conventions and to correct historical mistakes. In order to support backwards compatibility, the old version of the PackageDescription API must remain available. We need some way to determine which version of the PackageDescription API a package wishes to use. -Finally, as the Package.swift manifest is itself written in Swift, some mechanism -is needed to control which Swift language compatibility version should be used -when interpreting the manifest. This cannot be determined by a compatibility -version property in the manifest itself, as we must know what compatibility version -to interpret the manifest with before we have access to the data in the manifest. +Finally, as the Package.swift manifest is itself written in Swift, some +mechanism is needed to control which Swift language compatibility version should +be used when interpreting the manifest. This cannot be determined by a property +on the Package object in the manifest itself, as we must know what compatibility +version to interpret the manifest with before we have access to data specified +by Swift code in the manifest. ## Proposed solution @@ -70,7 +71,7 @@ the package's sources if not otherwise specified. When resolving package dependencies, if the version of a dependency that would normally be chosen specifies a Swift tools version which is greater than the version in use, that -version of the dependency will be considered ineligable and dependency +version of the dependency will be considered ineligible and dependency resolution will continue with evaluating the next-best version. If no version of a dependency (which otherwise meets the version requirements from the package dependency graph) supports the version of the Swift tools in use, a @@ -79,7 +80,7 @@ dependency resolution error will result. When new PackageDescription API is added which would not be understood by a prior version of the Swift Package Manager, it will be added to the current version of the PackageDescription module and will not require the package manager to -include a version of the module without that API. However, if that new API is used in the +include a version of the module without that API. However, if that new API is used in a Package.swift manifest, it will cause the package manager to validate that the Swift tools version of that package specifies a version of the tools which understands that API, or to emit an error with instructions to update the Swift tools version if not. @@ -87,15 +88,108 @@ Note that if a [version-specific manifest](https://github.com/apple/swift-packag is present for the older tools version, that tools version will validate as allowable even when newer features are adopted in the main Package.swift manifest. +The Swift tools version will determine the default Swift language compatibility version +used to compile the package's Swift sources if unspecified, but the Swift language +compatibility version for the package's sources is otherwise decoupled from the +Swift tools version. A separate Swift evolution proposal will describe how +to specify a Swift language compatibility version for package sources. + The Swift Package Manager may consider the Swift tools version of a package for other compatibility-related purposes as well. For example, if a bugfix in a new version of the Swift Package Manager might break older packages, the fixed behavior might only be applied to packages which have adopted the newer Swift tools version. +A new `swift package tools-version` command will be added to manage the +Swift tools version. This command will behave as follows: + +* `swift package tools-version` will report the Swift tools version of the package. + +* `swift package tools-version --set ` will set the Swift tools version to `value`. +It will also print an informational message advising the user of any changes that this +tools version change will necessitate, depending on what the prior and new tools versions +were. For example, changing the tools version might require converting the Package.swift manifest to +a different Swift language version, and to a different version of the PackageDescription API. + +* `swift package tools-version --set-current` will set the Swift tools version to the version +of the tools currently in use. + +If a package does not specify a Swift tools version, the Swift tools version is +"3.0.0". It is expected that in the future all Swift packages will specify a +Swift tools version. `swift package init` will set the Swift tools version of a +package it creates to the version of the tools in use. + +## How the Swift tools version is specified + +We have three options in mind for specifying the Swift tools version, and have +not yet chosen which to use. We'd appreciate input before this proposal is +submitted for review. The three options we considered are: + +### Swift tools version in the Package.swift manifest + +#### Approach + +The Swift tools version will be specified by a special comment in the first line +of the Package.swift manifest. This is similar to how a DTD is defined for XML +documents. To specify a tools version, a Package.swift file must begin with the +string `// swift-tools-version:`, followed by a version number specifier. + +Though the Swift tools version refers to a Swift marketing version number and is +not a proper semantic version, the version number specifier shall follow the +syntax defined by [semantic versioning 2.0.0](http://semver.org/spec/v2.0.0.html). +This syntax allows for an optional pre-release version component or build +version component; those components will not be used by the package manager currently, but +may be used in a future release to provide finer-grained compatibility controls during the +development of a new version of Swift. + +After the version number specifier, an optional `;` character may be present; +it, and anything else after it until the end of the first line, will be ignored by +this version of the package manager, but is reserved for the use of future +versions of the package manager. + +The package manager will attempt to detect approximate misspellings of the Swift +tools version comment. As such, it is an error if the first line of the file +begins with `//`, then zero or more than one whitespace characters, and then the +string `swift-tools-version`, or if any part of `swift-tools-version` is +capitalized. Any other first line of the file will not be considered to be a +Swift tools version comment, in which case the Swift tools version will be considered to +be `3.0.0`. + +#### Advantages + +* Users may like being able to see the Swift tools version when reading a +Package.swift manifest, without needing to go look at a separate file or run +`swift package tools-version`. + +* Keeping the Swift tools version in the same file as the rest of the manifest +data eliminates the possibility of forgetting to include both the Package.swift +manifest and a file specifying the Swift tools version in every commit which +should affect both. Users may also find it more convenient to only need to +commit a single file when making manifest changes. + +* Tools which wish to identify a Package.swift manifest (as distinct from +a Swift file which happens to be named "Package") may use the presence +of the version comment as an indicator of the file type. However, as long as +Swift 3.0 packages must be recognized, we cannot count on all Package.swift files +to provide this comment. + +#### Disadvantages + +* This essentially puts a second, tiny, domain-specific language into the +top of a Swift file, and requires the first line of the Package.swift manifest to +be parsed for this before it is run through the Swift interpreter. This will not +cause any concrete problems that we know of, but may be considered distasteful. + +* Novice users may be confused by seeing the Swift tools version at the top +of their Package.swift manifest file. + +### Swift tools version in a .swift-version file + +#### Approach + The Swift tools version will be specified in a file named ".swift-version", at the root level of the package. This file will follow the conventions -already estabished by the [swiftenv](https://github.com/kylef/swiftenv/) +already established by the [swiftenv](https://github.com/kylef/swiftenv/) project. The file will contain either a Swift marketing version number or the name of a snapshot which has been published on Swift.org. @@ -107,43 +201,80 @@ of a release in development shall be considered equivalent for the purposes of the Swift tools version. This feature does not attempt to specify compatibility between different snapshots during the development of a release, and only allows snapshot names to be specified to preserve compatibility with swiftenv. +Versioning comparison between snapshots within a release may be added in a +future version of the package manager. -The Swift tools version, as output by package manager commands, will always take -the form of a Swift marketing version (with -one, two, or three version components), the string "future", or the string "trunk". -"future" indicates that the .swift-version file specifies a snapshot name which is unknown and -thus belongs to a future version of Swift. "trunk" indicates that the .swift-version file specifies -a snapshot name which is known to this version of Swift, but which has not yet -been given a designated marketing version. "trunk" shall always be considered -compatible with the current version of package manager, and "future" shall -always be considered incompatible. It is an error to specify "future" or "trunk" -directly in the `.swift-version` file; these specifiers are determined -from the package manager's interpretation of the `.swift-version` file. +The Swift tools version, as output by `swift package tools-version`, will always +take the form of a Swift version number, the string "future", or the string +"trunk". "future" indicates that the .swift-version file specifies a snapshot +name which is unknown and thus belongs to a future version of Swift. "trunk" +indicates that the .swift-version file specifies a snapshot name which is known +to this version of Swift, but which has not yet been given a designated +marketing version. "trunk" shall always be considered compatible with the +current version of package manager, and "future" shall always be considered +incompatible. It is an error to specify "future" or "trunk" directly in the +`.swift-version` file; these specifiers are determined from the package +manager's interpretation of the `.swift-version` file. -A new `swift package tools-version` command will be added to manage the -Swift tools version. This command will behave as follows: +#### Advantages -* `swift package tools-version` will report the Swift tools version of the package. +* If we wish to store the Swift tools version outside of the Package.swift +manifest, this gives us a way to do so by reusing a file that many packages +already have, instead of adding yet another file. -* `swift package tools-version --set ` will set the Swift tools version to `value`. -It will also print an informational message advising the user of any changes that this -tools version change will necessitate, depending on what the prior and new tools versions -were. For example, changing the tools version might require converting the Package.swift manifest to -a different Swift language version, and to a different version of the PackageDescription API. +* This has some synergy with the behavior of swiftenv. When the package manager +updates the Swift tools version to something newer, swiftenv will know to +fetch and use the toolchain for that version before trying to build that package +(for the top-level package only). -* `swift package tools-version --set-current` will set the Swift tools version to the version -of the tools currently in use. +#### Disadvantages -If no `.swift-version` file is present, the Swift tools version is "3.0". -It is expected that in the future all Swift packages will include a -`.swift-version` file. `swift package init` will create this file and set -it to the marketing version of the tools in use. +* Because swiftenv allows toolchain names in this file, the package manager +must as well. Allowing toolchain names significantly increases the complexity +of this feature, as it means that we need to maintain a database mapping toolchain +names to Swift versions. There may be process complexities in making sure that +as we produce new toolchains, the database in the toolchain knows about the name +of that toolchain, as well as all other toolchains that have been created. -The Swift tools version will determine the default Swift language compatibility version -used to compile the package's Swift sources if unspecified, but the Swift language -compatibility version for the package's sources is otherwise decoupled from the -Swift tools version. A separate Swift evolution proposal will describe how -to specify a Swift language compatibility version for package sources. +* The necessity of handling "future" and "trunk" versions, as output by +`swift package tools-version`, increases the complexity of interacting +with this feature for other tools that know about the package manager. + +* It is not clear if the synergy with swiftenv is perfect, or if there are +situations where users would prefer to specify a different toolchain for +swiftenv than they need their Swift tools version set to. + +### Swift tools version in a .swift-tools-version file + +#### Approach + +The Swift tools version will be specified in a file named ".swift-tools-version", +at the root level of the package. This will be a JSON file with one key, +`swift-tools-version`. (Any other keys will be ignored by this version of the +package manager). The value for this key will be a version number specifier. + +Though the Swift tools version refers to a Swift marketing version number and is +not a proper semantic version, the version number specifier shall follow the +syntax defined by [semantic versioning 2.0.0](http://semver.org/spec/v2.0.0.html). +This syntax allows for an optional pre-release version component or build +version component; this will not be used by the package manager currently, but +may be in a future release to provide stricter compatibility controls during the +development of a new version of Swift. + +#### Advantages + +* As this is a JSON file, it has a clear and familiar syntax and is easily +extensible for future use. + +* Because the Swift tools version is not specified in the Package.swift file, the +user is discouraged from trying to hand-edit this version and will usually leave +it up to the tools to manage, through the `swift package tools-version` command. +That will decrease the likelihood of mistakes. + +#### Disadvantages + +* This adds yet another file that every Swift package will need to have and +manage in source control. ## Examples @@ -178,11 +309,10 @@ language version and the revised Swift 4 Package Manager PackageDescription API. ## Impact on existing code -There is no impact on existing packages. Since existing packages either have no -.swift-version file, or have a .swift-version which specifies a Swift 3 -toolchain or version, those packages will default to building their sources -with the Swift 3 language compatibility mode, and will be able to be -built by both Swift 3 and Swift 4 tools. +There is no impact on existing packages. Since existing packages do not specify +a Swift tools version, they will default to building their sources +with the Swift 3 language compatibility mode, and will be able to be built +by both Swift 3 and Swift 4 tools. Use of the package manager's [version-specific tag selection](https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#version-specific-tag-selection) mechanism will no longer be necessary in many situations. Previously, authors needed to employ @@ -191,14 +321,14 @@ version of the Swift tools before adopting new Swift features, to avoid break cl of the package still using the old version of the Swift tools. Now, when adopting new Swift features, a package author merely needs to set their Swift tools version to that new version, and dependency resolution performed by an older version of the Swift tools (starting with Swift 3.1) will consider -those new package versions ineligable. +those new package versions ineligible. The existing version-specific tag selection mechanism may still be useful for authors who wish to publish new parallel versions of their package for multiple versions of the Swift tools. Use of the package manager's [version-specific manifest selection](https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#version-specific-manifest-selection) -mechanism may still be useful for authors who wish to conditionally adopt new features of the Swift tool in +mechanism may still be useful for authors who wish to conditionally adopt new features of the Swift tools in their Package.swift manifest without needing to update their Swift tools version to exclude older versions of Swift. @@ -214,7 +344,7 @@ APIs. The following table shows an example of which Swift language version will be used to interpret the Package.swift manifest, and to interpret the package's sources, based on the Swift tools in use and the parameters specified by the package. -| Swift Tools | .swift-version | [Swift Language Compatibility Version](http://link/to/proposal) | Language Version Used | +| Swift Tools | Swift Tools Version | [Swift Language Compatibility Version](http://link/to/proposal) | Language Version Used | |:---:|:---:|:---:| --- | | 3.1 | Not Present | Not Present | Manifest: 3 Sources: 3 | | 3.1 | 3.1 | Not Present | Manifest: 3 Sources: 3 | @@ -365,17 +495,3 @@ things. However, we feel that doing so would add unnecessary complexity to the Swift package manager. We do not see a compelling use-case for needing to control these different features independently, so we are consolidating them into one version mechanism as a valuable simplification. - -### Specify the Swift tools version without needing a new file for it - -This proposal does add a new file (`.swift-version`) that every package is -expected to provide. We could instead store the Swift tools version somewhere -else. The most likely candidate for that would be the Package.swift manifest -itself. The immediate problem with doing so is that we need to know how to -interpret the Swift manifest in order to get data out of it, and the Swift -tools version provides information needed to know how to interpret the manifest. -We could consider putting the version at the very top of the file, perhaps -in a specially-formatted Swift code comment, and parsing the top of the file -to extract that version before running it through the Swift interpreter. - -(More discussion of this alternative to come) From ae2fbb6f49a0bcfff4b9724120b74235b92f4155 Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Wed, 1 Feb 2017 20:32:27 -0800 Subject: [PATCH 0120/4563] Tweak to rule for detecting misspellings of the swift tools version comment --- proposals/NNNN-package-manager-tools-version.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/proposals/NNNN-package-manager-tools-version.md b/proposals/NNNN-package-manager-tools-version.md index 0e6b164cfc..e3f3f3711c 100644 --- a/proposals/NNNN-package-manager-tools-version.md +++ b/proposals/NNNN-package-manager-tools-version.md @@ -149,11 +149,10 @@ versions of the package manager. The package manager will attempt to detect approximate misspellings of the Swift tools version comment. As such, it is an error if the first line of the file -begins with `//`, then zero or more than one whitespace characters, and then the -string `swift-tools-version`, or if any part of `swift-tools-version` is -capitalized. Any other first line of the file will not be considered to be a -Swift tools version comment, in which case the Swift tools version will be considered to -be `3.0.0`. +begins with `//`, contains the string `swift-tools-version` (with any +capitalization), but is not otherwise a valid tools version comment. Any other first +line of the file will not be considered to be a Swift tools version comment, in +which case the Swift tools version will be considered to be `3.0.0`. #### Advantages From 76630cbaa5c4b48483743cf9507b79ab2ee543be Mon Sep 17 00:00:00 2001 From: Daniel Dunbar Date: Fri, 3 Feb 2017 09:59:42 -0800 Subject: [PATCH 0121/4563] [SE-0149] Proposal is accepted. --- proposals/0149-package-manager-top-of-tree.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0149-package-manager-top-of-tree.md b/proposals/0149-package-manager-top-of-tree.md index 2f0d98911e..9e4bb46e5a 100644 --- a/proposals/0149-package-manager-top-of-tree.md +++ b/proposals/0149-package-manager-top-of-tree.md @@ -3,7 +3,7 @@ * Proposal: [SE-0149](0149-package-manager-top-of-tree.md) * Author: [Boris Bügling](https://github.com/neonichu) * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) -* Status: **Active review (January 24...January 31, 2017)** +* Status: **Accepted** * Bugs: [SR-3709](https://bugs.swift.org/browse/SR-3709) From b37c5bfc90abbf610f1695e1fa809ae0f9f92f87 Mon Sep 17 00:00:00 2001 From: Daniel Dunbar Date: Fri, 3 Feb 2017 09:59:49 -0800 Subject: [PATCH 0122/4563] [SE-0150] Proposal is accepted. --- proposals/0150-package-manager-branch-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0150-package-manager-branch-support.md b/proposals/0150-package-manager-branch-support.md index e1da1b2d58..1d42193ba2 100644 --- a/proposals/0150-package-manager-branch-support.md +++ b/proposals/0150-package-manager-branch-support.md @@ -3,7 +3,7 @@ * Proposal: [SE-0150](0150-package-manager-branch-support.md) * Author: [Boris Bügling](https://github.com/neonichu) * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) -* Status: **Active review (January 24...January 31, 2017)** +* Status: **Accepted** * Bugs: [SR-666](https://bugs.swift.org/browse/SR-666) From 185b12711bfe1e3b7982d255b538f8dad9ae242c Mon Sep 17 00:00:00 2001 From: Daniel Duan Date: Wed, 18 Jan 2017 21:45:34 -0800 Subject: [PATCH 0123/4563] Normalize Enum Case Representation --- ...NNNN-Normalize-Enum-Case-Representation.md | 245 ++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 proposals/NNNN-Normalize-Enum-Case-Representation.md diff --git a/proposals/NNNN-Normalize-Enum-Case-Representation.md b/proposals/NNNN-Normalize-Enum-Case-Representation.md new file mode 100644 index 0000000000..42e3ad1dc1 --- /dev/null +++ b/proposals/NNNN-Normalize-Enum-Case-Representation.md @@ -0,0 +1,245 @@ +# Normalize Enum Case Representation + +* Proposal: [SE-NNNN][] +* Authors: [Daniel Duan][], [Joe Groff][] +* Review Manager: TBD +* Status: **Awaiting review** + +## Introduction + +In Swift 3, associated values for an enum case are represented by +a labeled-tuple. This has several undesired effects: inconsistency in enum value +construction syntax, many forms of pattern matching, missing features such as +specifying default value and missed opportunity for layout improvements. + +This proposal aims to make enums more "regular" by replacing tuple as the +representation of associated values, making declaration and construction of enum +cases more function-like. + +Swift-evolution thread: [Compound Names For Enum Cases][SE Thread] + +## Motivation + +**Each enum case declares a function that can be used to create a corresponding +value. To users who expect these functions to behave "normally", surprises +await.** + +1. Associated value labels aren't part of the function name. + + After [SE-0111][] Swift function's fully qualified name consists of its + base-name and all argument labels. As an illustration, one can invoke + a function with its full name: + + ```swift + func f(x: Int, y: Int) {} + f(x: y:)(0, 0) // Okay, this is equivalent to f(x: 0, y: 0) + ``` + + This, however, cannot be done when enum cases with associated value were + constructed: + + ```swift + enum Foo { + case bar(x: Int, y: Int) + } + Foo.bar(x: y:)(0, 0) // Does not compile as of Swift 3 + ``` + + Here, `x` and `y` are labels of bar's payload (a tuple), as opposed to being + part of the case's formal name. This is inconsistent with rest of the + language. + +2. Default value for parameters isn't available in case declarations. + + ```swift + enum Animation { + case fadeIn(duration: TimeInterval = 0.3) // Nope! + } + let anim = Animation.fadeIn() // Would be nice, too bad! + ``` + +**Associated values being a tuple complicates pattern matching.** + +The least unexpected pattern to match a `bar` value is the following: + +```swift +if case let .bar(x: p, y: q) = Foo.bar(x: 0, y: 1) { + print(p, q) // 0 1 +} +``` + +In Swift 3, there are a few alternatives that may not be obvious to new users. + +1. A pattern with a single value would match and result in a tuple: + + ```swift + if case let .bar(wat) = Foo.bar(x: 0, y: 1) { + print(wat.y) // 1 + } + ``` + +2. Labels in patterns are not enforced: + + ```swift + // note: there's no label in the following pattern + if case let .bar(p, q) = Foo.bar(x: 0, y: 1) { + print(p, q) // 0 1 + } + ``` + +These complex rules makes pattern matching difficult to teach and to expand to +other types. + +**Moving away from tuple-as-associated-value also give us opportunity to improve +enum's memory layout** since each associated value would no longer play double +duty as part of the tuple's memory layout. + +## Proposed Solution + +When a enum case has associated values, they will no longer form a tuple. Their +labels will become part of the case's declared name. Patterns matching such +values must include labels. + +This proposal also introduce the ability to include a default value for each +associated value in the declaration. + +## Detailed Design + +### Make associated value labels part of case's name +When labels are present in enum case's payload, they will become part of case's +declared name instead of being labels for fields in a tuple. In details, when +constructing an enum value with the case name, label names must either be +supplied in the argument list it self, or as part of the full name. + +```swift +Foo.bar(x: 0, y: 0) // Okay, the Swift 3 way. +Foo.bar(x: y:)(0, 0) // Equivalent to the previous line. +Foo.bar(x: y:)(x: 0, y: 0) // This would be an error, however. +``` + +Note that since the labels aren't part of a tuple, they no longer participate in +type checking, similar to functions: + +```swift +let f = Foo.bar // f has type (Int, Int) -> Foo +f(0, 0) // Okay! +f(x: 0, y: 0) // Won't compile. +``` + +Enum cases should have distinct *full* names. Therefore, shared base name will be allowed: + +```swift +enum Expr { + case literal(bool: Bool) + case literal(int: Int) +} +``` + +### Add default value in enum case declarations +From a user's point view, declaring an enum case should remain the same as Swift +3 except now it's possible to add `= expression` after the type of an +associated value to convey a default value for that field. Updated syntax: + +```ebnf +union-style-enum-case = enum-case-name [enum-case-associated-value-clause]; +enum-case-associated-value-clause = "(" ")" + | "(" enum-case-associated-value-list ")"; +enum-case-associated-value-list = enum-associated-value-element + | enum-associated-value-element "," + enum-case-associated-value-list; +enum-case-associated-value-element = element-name type-annotation + [enum-case-element-default-value-clause] + | type [enum-case-element-default-value-clause]; +element-name = identifier; +enum-case-element-default-value-clause = "=" expression; +``` + +### Simplify pattern matching rules on enums +Syntax for enum case patterns will be the following: + +```ebnf +enum-case-pattern = [type-identifier] "." enum-case-name [enum-case-associated-value-pattern]; +enum-case-associated-value-pattern = "(" [enum-case-associated-value-list-pattern] ")"; +enum-case-associated-value-list-pattern = enum-case-associated-value-list-pattern-element + | enum-case-associated-value-list-pattern-element "," + enum-case-associated-value-list-pattern; +enum-case-associated-value-list-element = pattern | identifier ":" pattern; +``` + +… and `case-associated-value-pattern` will be added to the list of various +`pattern`s. + +Note that `enum-case-associated-value-pattern` is identical to `tuple-pattern` +except in names. It is introduced here to denote semantic difference between the +two. Whereas the syntax in Swift 3 allows a single `tuple-pattern-element` to +match the entire case payload, the number of +`enum-case-associated-value-list-pattern-element`s must be equal to that of +associated value of the case in order to be a match. This means code in the next +example will be deprecated under this proposal: + +```swift +if case let .bar(wat) = Foo.bar(x: 0, y: 1) { // syntax error + // … +} +``` + +Further, `identifier` in `enum-case-associated-value-list-pattern-element` must +be the same as the label of corresponding associated value intended for the +match. So this will be deprecated as well: + +```swift +if case let .bar(p, q) = Foo.bar(x: 0, y: 1) { // missing `x:` and `y:` + // … +} +``` + +## Source compatibility + +As detailed in the previous section, this proposal deprecates certain pattern +matching syntax. + +Other changes to the syntax are additive and source-compatible with Swift 3. For +example, matching a case with associated value solely by its name should still +work: + +```swift +switch Foo.bar(x: 0, y: 1) { +case .bar: // matches. + print("bar!") +} +``` + +## Effect on ABI stability and resilience + +After this proposal, enum cases may have compound names, which would be mangled +differently than Swift 3. + +The compiler may also layout enums differently now that payloads are not +constrained by having to be part of a tuple. + +## Alternative Considered + +To maintain maximum source compatibility, we could introduce a rule that matches +all associated values to a labeled tuple. As T.J. Usiyan +[pointed out][TJs comment], implementation of the equality protocal would be +simplified due to tuple's conformance to `Equatable`. This feature may still be +introduced with alternative syntax (perhaps related to splats) later without +source-breakage. And the need to implement `Equatable` may also disappear with +auto-devriving for `Equitable` conformance. + +A syntax that did stay for source compatibility is allowing `()` in patterns +that match enum cases without associated values: + +```swift +if let case .x() = Foo.baz { // … } +``` + +We could remove this syntax as it would make the pattern look more consistent to +the case's declaration. + +[SE-0111]: https://github.com/apple/swift-evolution/blob/master/proposals/0111-remove-arg-label-type-significance.md +[Daniel Duan]: https://github.com/dduan +[Joe Groff]: https://github.com/jckarter +[SE-NNNN]: NNNN-Normalize-Enum-Case-Representation.md +[TJs comment]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030614.html +[SE Thread]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030477.html From b0e8d543577b98462d507bab3131f02c673877f6 Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Fri, 3 Feb 2017 11:00:59 -0800 Subject: [PATCH 0124/4563] Clarify that we only expect to make a new PackageDescription module once; add another rejected alternative. --- .../NNNN-package-manager-tools-version.md | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/proposals/NNNN-package-manager-tools-version.md b/proposals/NNNN-package-manager-tools-version.md index e3f3f3711c..5ae298b634 100644 --- a/proposals/NNNN-package-manager-tools-version.md +++ b/proposals/NNNN-package-manager-tools-version.md @@ -31,8 +31,8 @@ a new version of the Swift tools, when new features have been added which are no supported by the current release of the Swift tools. Second, one specific planned change for the Swift Package Manager is a revision -of the Package.swift PackageDescription API, to make it conform to -newer Swift conventions and to correct historical mistakes. In order to support backwards +of the Package.swift PackageDescription API for Swift 4, to make it conform to +Swift conventions and to correct historical mistakes. In order to support backwards compatibility, the old version of the PackageDescription API must remain available. We need some way to determine which version of the PackageDescription API a package wishes to use. @@ -57,10 +57,12 @@ code which require a new version of Swift, users will be expected to update the package's Swift tools version to specify that it requires that version of the Swift tools. -When an incompatible revision is made to the PackageDescription API, the Swift Package Manager -will continue to include the older version of the PackageDescription module for backwards -compatibility. The Swift tools version shall determine which version of the PackageDescription -module will be used when interpreting the Package.swift manifest. +The Swift Package Manager will continue to include the Swift 3 version of the +PackageDescription module for backwards compatibility, in addition to including +the new version of the PackageDescription module, as designed in a forthcoming +evolution proposal. The Swift tools version shall determine which version of the +PackageDescription module will be used when interpreting the Package.swift +manifest. The Swift Tools Version will also determine which Swift language compatibility version should be used when interpreting the Package.swift manifest. @@ -77,16 +79,14 @@ no version of a dependency (which otherwise meets the version requirements from the package dependency graph) supports the version of the Swift tools in use, a dependency resolution error will result. -When new PackageDescription API is added which would not be understood by a -prior version of the Swift Package Manager, it will be added to the current version -of the PackageDescription module and will not require the package manager to -include a version of the module without that API. However, if that new API is used in a -Package.swift manifest, it will cause the package manager to validate that the -Swift tools version of that package specifies a version of the tools which understands that API, -or to emit an error with instructions to update the Swift tools version if not. -Note that if a [version-specific manifest](https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#version-specific-manifest-selection) +New PackageDescription API will be added as needed for upcoming features. When +new API is used in a Package.swift manifest, it will cause the package manager +to validate that the Swift tools version of that package specifies a version of +the tools which understands that API, or to emit an error with instructions to +update the Swift tools version if not. Note that if a [version-specific manifest](https://github.com/apple/swift-package-manager/blob/master/Documentation/Usage.md#version-specific-manifest-selection) is present for the older tools version, that tools version will validate as -allowable even when newer features are adopted in the main Package.swift manifest. +allowable even when newer features are adopted in the main Package.swift +manifest. The Swift tools version will determine the default Swift language compatibility version used to compile the package's Swift sources if unspecified, but the Swift language @@ -494,3 +494,16 @@ things. However, we feel that doing so would add unnecessary complexity to the Swift package manager. We do not see a compelling use-case for needing to control these different features independently, so we are consolidating them into one version mechanism as a valuable simplification. + +### Rename the PackageDescription module + +We considered giving the new version of the PackageDescription module +a different name, and having users change the import statement in their +Package.swift when they want to adopt the revised PackageDescription API. +This would mean that the package manager would not automatically switch +which version of the PackageDescription module to use based on the Swift +tools version. However, this did not seem like a better experience for our +users. It would also allow users to import both modules, which we do not +want to support. And it would allow users to continue using the old +PackageDescription API in a manifest that is otherwise updated for +Swift 4, which we also do not want to support. From fd28010aaef077b9783d738eacd912915b55c67e Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Fri, 3 Feb 2017 11:06:05 -0800 Subject: [PATCH 0125/4563] Clarify reasoning for not relying on semantic versioning. --- proposals/NNNN-package-manager-tools-version.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-package-manager-tools-version.md b/proposals/NNNN-package-manager-tools-version.md index 5ae298b634..3172da3f0b 100644 --- a/proposals/NNNN-package-manager-tools-version.md +++ b/proposals/NNNN-package-manager-tools-version.md @@ -419,7 +419,10 @@ this approach. First, this does nothing to allow packages to be used with new Swift tools without needing to be updated for those tools. We don't want package authors to need to immediately adopt the Swift 4 language compatibility version and PackageDescription -API before they can build their package with the new tools. +API before they can build their package with the new tools. Using a Swift +tools version allows us to support multiple versions of the PackageDescription +API and the Swift language, so existing packages will continue to work +with newer tools automatically. Second, this forces clients of a package to explicitly opt-in to updated versions of their dependencies, even if there was otherwise no API change. From 6753456a1adf85048615c63a442dfb22efdb34a1 Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Fri, 3 Feb 2017 12:18:55 -0800 Subject: [PATCH 0126/4563] Update to tie default behavior to the Swift tools version --- ...age-manager-swift-compatibility-version.md | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/proposals/NNNN-package-manager-swift-compatibility-version.md b/proposals/NNNN-package-manager-swift-compatibility-version.md index a6ae6492c9..08a10ed83c 100644 --- a/proposals/NNNN-package-manager-swift-compatibility-version.md +++ b/proposals/NNNN-package-manager-swift-compatibility-version.md @@ -1,7 +1,7 @@ # Package Manager Swift Compatibility Version * Proposal: [SE-NNNN](NNNN-package-manager-swift-compatibility-version.md) -* Author: [Daniel Dunbar](https://github.com/ddunbar) +* Author: [Daniel Dunbar](https://github.com/ddunbar), [Rick Ballard](http://github.com/rballard) * Review manager: N/A * Status: **WIP** @@ -15,12 +15,12 @@ feature to the package manager. The Swift compiler now supports a "compatibility version" flag which specifies the Swift major language version that the compiler should try to accept. We need support for an additional package manager manifest feature in order for this -feature to be used effectively by Swift packages. +feature to be used by Swift packages. ## Proposed solution We will add support to the package manifest declaration to specify a set of -supported versions: +supported Swift language versions: ```swift let package = Package( @@ -36,9 +36,12 @@ use. If a package does not support any version compatible with the current compiler, we will report an error. -If a package does not specify any compatible Swift versions, we will assume that -it is compatible with all Swift versions and behave accordingly (i.e., build -using the major version of the Swift compiler in use). +If a package does not specify any Swift compatibility versions, the +compatibility version to be used will match the major version of the the +package's Swift tools version (as discussed in a separate evolution proposal). A +Swift tools version with a major version of '3' will imply a default Swift +compatibility version of '3', and a Swift tools version with a major version +of '4' will imply a default Swift compatibility version of '4'. ## Detailed design @@ -67,29 +70,29 @@ behavior should be used for this package. Here are concrete examples of how the package manager will compile code, depending on its compatibility declarations: -* Version 3 Packager Manager & Swift 3 (only) Compatibile Package +* Version 3 Packager Manager & Swift 3 (only) Compatible Package The package manager will compile the code with `-swift-version 3`. -* Version 3 Packager Manager & Swift 4 (only) Compatibile Package +* Version 3 Packager Manager & Swift 4 (only) Compatible Package The package manager will report an error, since the package supports no version compatible with the tools. -* Version 3 Packager Manager & Swift [3, 4] Compatibile Package +* Version 3 Packager Manager & Swift [3, 4] Compatible Package The package manager will compile the code with `-swift-version 3`, matching the major version of the tools in use. -* Version 4 Packager Manager & Swift 3 (only) Compatibile Package +* Version 4 Packager Manager & Swift 3 (only) Compatible Package The package manager will compile the code with `-swift-version 3`. -* Version 4 Packager Manager & Swift 4 (only) Compatibile Package +* Version 4 Packager Manager & Swift 4 (only) Compatible Package The package manager will compile the code with `-swift-version 4`. -* Version 4 Packager Manager & Swift [3, 4] Compatibile Package +* Version 4 Packager Manager & Swift [3, 4] Compatible Package The package manager will compile the code with `-swift-version 4`. @@ -103,23 +106,28 @@ depending on its compatibility declarations: ## Impact on existing code Since this is a new API, all packages will use the default behavior once -implemented. This means that the Swift 4 package manager will immediately begin -compiling packages in Swift 4 mode. This is as intended, although it may expose -Swift 4 source incompatibilities in packages and require them to adopt an -explicit declaration for Swift 3 compatibility. +implemented. Because the default Swift tools version of existing packages +is "3.0.0" (pending approval of the Swift tools version proposal), the Swift +4 package manager will build such packages in Swift 3 mode. When packages +wish to migrate to the Swift 4 language, they can either update their +Swift tools version or specify Swift 4 as compatible Swift version. + +New packages created with `swift package init` by the Swift 4 tools will +build with the Swift 4 language by default, due to the Swift tools version +that `swift package init` chooses. This is a new manifest API, so packages which adopt this API will no longer be buildable with package manager versions which do not recognize that -API. Packages which require support for that can use the features added in -[SE-0135](https://github.com/apple/swift-evolution/blob/master/proposals/0135-package-manager-support-for-differentiating-packages-by-swift-version.md). +API. We expect to add support for this API to Swift 3.1, so it will be possible +to create packages which support the Swift 4 and 3 languages and the Swift +4 and 3.1 tools. ## Alternatives considered -We could have made several different choices for the behavior when a package -does not declare compatible versions, for example we could conservatively assume -it requires Swift 3. However, we expect that there will be minimal source -breaking changes for the forseeable future of Swift development, and would -prefer to optimize for most packages being built in the latest Swift mode. +We could have made the Swift compatibility version default to the version of the +Swift tools in use if not specified. However, tying this to the Swift tools +version allows existing Swift 3 language packages to build with the Swift 4 +tools without needing to explicitly specify a Swift compatibility version. We chose not to support any command line features to modify the selected version (e.g., to force a Swift 4 compiler to use Swift 3 mode where acceptable) in From d3bb189b4b0556778a645db7cd9515953e0e5a47 Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Fri, 3 Feb 2017 12:26:11 -0800 Subject: [PATCH 0127/4563] Add alternative mention build settings. Other clarification. --- ...NNN-package-manager-swift-compatibility-version.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-package-manager-swift-compatibility-version.md b/proposals/NNNN-package-manager-swift-compatibility-version.md index 08a10ed83c..32e8659f26 100644 --- a/proposals/NNNN-package-manager-swift-compatibility-version.md +++ b/proposals/NNNN-package-manager-swift-compatibility-version.md @@ -126,10 +126,17 @@ to create packages which support the Swift 4 and 3 languages and the Swift We could have made the Swift compatibility version default to the version of the Swift tools in use if not specified. However, tying this to the Swift tools -version allows existing Swift 3 language packages to build with the Swift 4 -tools without needing to explicitly specify a Swift compatibility version. +version instead allows existing Swift 3 language packages to build with the +Swift 4 tools without changes, as they won't need to explicitly specify a Swift +compatibility version in order to continue to build with the Swift 3 language. We chose not to support any command line features to modify the selected version (e.g., to force a Swift 4 compiler to use Swift 3 mode where acceptable) in order to keep this proposal simple. We will consider these in the future if they prove necessary. + +We considered supporting a way to set the Swift compatibility version on +a per-module basis, instead of needing to set it for the entire package. +We think this would be best done using build settings, which the package +manager does not yet support. We are pending per-module support for this +feature until build settings are supported. From aa570274cb148409a0f18fc6014fac727075bd4c Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Fri, 3 Feb 2017 12:32:12 -0800 Subject: [PATCH 0128/4563] Change the name of the property to 'swiftLanguageVersions' --- ...age-manager-swift-compatibility-version.md | 57 ++++++++++--------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/proposals/NNNN-package-manager-swift-compatibility-version.md b/proposals/NNNN-package-manager-swift-compatibility-version.md index 32e8659f26..611f7613c5 100644 --- a/proposals/NNNN-package-manager-swift-compatibility-version.md +++ b/proposals/NNNN-package-manager-swift-compatibility-version.md @@ -7,15 +7,15 @@ ## Introduction -This proposal adds support for the Swift compiler's new "compatibility version" -feature to the package manager. +This proposal adds support for the Swift compiler's new "language compatibility +version" feature to the package manager. ## Motivation -The Swift compiler now supports a "compatibility version" flag which specifies -the Swift major language version that the compiler should try to accept. We need -support for an additional package manager manifest feature in order for this -feature to be used by Swift packages. +The Swift compiler now supports a "language compatibility version" flag which +specifies the Swift major language version that the compiler should try to +accept. We need support for an additional package manager manifest feature in +order for this feature to be used by Swift packages. ## Proposed solution @@ -26,27 +26,28 @@ supported Swift language versions: let package = Package( name: "HTTP", ... - compatibleSwiftVersions: [3, 4]) + swiftLanguageVersions: [3, 4]) ``` -When building a package, we will always select the compatible Swift version that +When building a package, we will always select the Swift language version that is most close to (but not exceeding) the major version of the Swift compiler in use. If a package does not support any version compatible with the current compiler, we will report an error. -If a package does not specify any Swift compatibility versions, the -compatibility version to be used will match the major version of the the +If a package does not specify any Swift language versions, the +language version to be used will match the major version of the the package's Swift tools version (as discussed in a separate evolution proposal). A Swift tools version with a major version of '3' will imply a default Swift -compatibility version of '3', and a Swift tools version with a major version -of '4' will imply a default Swift compatibility version of '4'. +language version of '3', and a Swift tools version with a major version +of '4' will imply a default Swift language version of '4'. ## Detailed design We are operating under the assumption that for the immediate future, the Swift -version accepted by the compiler will remain an integer major version. +language compatibility version accepted by the compiler will remain an integer +major version. With this change, the complete package initializer will be: @@ -58,41 +59,41 @@ With this change, the complete package initializer will be: targets: [Target] = [], products: [Product] = [], dependencies: [Dependency] = [], - compatibleSwiftVersions: [Int]? = nil, + swiftLanguageVersions: [Int]? = nil, exclude: [String] = [] ``` -where absence of the optional compatible version list indicates the default +where absence of the optional language version list indicates the default behavior should be used for this package. ### Example Behaviors Here are concrete examples of how the package manager will compile code, -depending on its compatibility declarations: +depending on its language version declarations: -* Version 3 Packager Manager & Swift 3 (only) Compatible Package +* Version 3 Packager Manager & Swift 3 (only) Language Package The package manager will compile the code with `-swift-version 3`. -* Version 3 Packager Manager & Swift 4 (only) Compatible Package +* Version 3 Packager Manager & Swift 4 (only) Language Package - The package manager will report an error, since the package supports no version - compatible with the tools. + The package manager will report an error, since the package supports no language + version compatible with the tools. -* Version 3 Packager Manager & Swift [3, 4] Compatible Package +* Version 3 Packager Manager & Swift [3, 4] Language Package The package manager will compile the code with `-swift-version 3`, matching the major version of the tools in use. -* Version 4 Packager Manager & Swift 3 (only) Compatible Package +* Version 4 Packager Manager & Swift 3 (only) Language Package The package manager will compile the code with `-swift-version 3`. -* Version 4 Packager Manager & Swift 4 (only) Compatible Package +* Version 4 Packager Manager & Swift 4 (only) Language Package The package manager will compile the code with `-swift-version 4`. -* Version 4 Packager Manager & Swift [3, 4] Compatible Package +* Version 4 Packager Manager & Swift [3, 4] Language Package The package manager will compile the code with `-swift-version 4`. @@ -110,7 +111,7 @@ implemented. Because the default Swift tools version of existing packages is "3.0.0" (pending approval of the Swift tools version proposal), the Swift 4 package manager will build such packages in Swift 3 mode. When packages wish to migrate to the Swift 4 language, they can either update their -Swift tools version or specify Swift 4 as compatible Swift version. +Swift tools version or specify Swift 4 as their language version. New packages created with `swift package init` by the Swift 4 tools will build with the Swift 4 language by default, due to the Swift tools version @@ -124,18 +125,18 @@ to create packages which support the Swift 4 and 3 languages and the Swift ## Alternatives considered -We could have made the Swift compatibility version default to the version of the +We could have made the Swift language version default to the version of the Swift tools in use if not specified. However, tying this to the Swift tools version instead allows existing Swift 3 language packages to build with the Swift 4 tools without changes, as they won't need to explicitly specify a Swift -compatibility version in order to continue to build with the Swift 3 language. +language version in order to continue to build with the Swift 3 language. We chose not to support any command line features to modify the selected version (e.g., to force a Swift 4 compiler to use Swift 3 mode where acceptable) in order to keep this proposal simple. We will consider these in the future if they prove necessary. -We considered supporting a way to set the Swift compatibility version on +We considered supporting a way to set the Swift language version on a per-module basis, instead of needing to set it for the entire package. We think this would be best done using build settings, which the package manager does not yet support. We are pending per-module support for this From d93ca94e0a4d9e4d0c0c1e23ff4035bf0008a60e Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Fri, 3 Feb 2017 12:34:41 -0800 Subject: [PATCH 0129/4563] Add the word 'language' to the proposal name, for clarity --- ...N-package-manager-swift-language-compatibility-version.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{NNNN-package-manager-swift-compatibility-version.md => NNNN-package-manager-swift-language-compatibility-version.md} (97%) diff --git a/proposals/NNNN-package-manager-swift-compatibility-version.md b/proposals/NNNN-package-manager-swift-language-compatibility-version.md similarity index 97% rename from proposals/NNNN-package-manager-swift-compatibility-version.md rename to proposals/NNNN-package-manager-swift-language-compatibility-version.md index 611f7613c5..c5c093581c 100644 --- a/proposals/NNNN-package-manager-swift-compatibility-version.md +++ b/proposals/NNNN-package-manager-swift-language-compatibility-version.md @@ -1,6 +1,6 @@ -# Package Manager Swift Compatibility Version +# Package Manager Swift Language Compatibility Version -* Proposal: [SE-NNNN](NNNN-package-manager-swift-compatibility-version.md) +* Proposal: [SE-NNNN](NNNN-package-manager-swift-language-compatibility-version.md) * Author: [Daniel Dunbar](https://github.com/ddunbar), [Rick Ballard](http://github.com/rballard) * Review manager: N/A * Status: **WIP** From da885458eaa69e4c4247de6b9176821498fc87f1 Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Tue, 7 Feb 2017 09:37:03 -0800 Subject: [PATCH 0130/4563] Prepare for submitting for review --- ...NN-package-manager-swift-language-compatibility-version.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-package-manager-swift-language-compatibility-version.md b/proposals/NNNN-package-manager-swift-language-compatibility-version.md index c5c093581c..2bd602035c 100644 --- a/proposals/NNNN-package-manager-swift-language-compatibility-version.md +++ b/proposals/NNNN-package-manager-swift-language-compatibility-version.md @@ -2,8 +2,8 @@ * Proposal: [SE-NNNN](NNNN-package-manager-swift-language-compatibility-version.md) * Author: [Daniel Dunbar](https://github.com/ddunbar), [Rick Ballard](http://github.com/rballard) -* Review manager: N/A -* Status: **WIP** +* Review manager: Anders Bertelrud +* Status: Scheduled for review (February 7...February 13, 2017) ## Introduction From f2d41830ebcf56823517ec18bff5e84dc74b39ef Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Tue, 7 Feb 2017 11:44:06 -0800 Subject: [PATCH 0131/4563] We chose to store the Swift tools version in the Package.swift manifest. --- .../NNNN-package-manager-tools-version.md | 213 ++++++------------ 1 file changed, 65 insertions(+), 148 deletions(-) diff --git a/proposals/NNNN-package-manager-tools-version.md b/proposals/NNNN-package-manager-tools-version.md index 3172da3f0b..e72db49146 100644 --- a/proposals/NNNN-package-manager-tools-version.md +++ b/proposals/NNNN-package-manager-tools-version.md @@ -119,15 +119,7 @@ If a package does not specify a Swift tools version, the Swift tools version is Swift tools version. `swift package init` will set the Swift tools version of a package it creates to the version of the tools in use. -## How the Swift tools version is specified - -We have three options in mind for specifying the Swift tools version, and have -not yet chosen which to use. We'd appreciate input before this proposal is -submitted for review. The three options we considered are: - -### Swift tools version in the Package.swift manifest - -#### Approach +### How the Swift tools version is specified The Swift tools version will be specified by a special comment in the first line of the Package.swift manifest. This is similar to how a DTD is defined for XML @@ -154,139 +146,20 @@ capitalization), but is not otherwise a valid tools version comment. Any other f line of the file will not be considered to be a Swift tools version comment, in which case the Swift tools version will be considered to be `3.0.0`. -#### Advantages - -* Users may like being able to see the Swift tools version when reading a -Package.swift manifest, without needing to go look at a separate file or run -`swift package tools-version`. - -* Keeping the Swift tools version in the same file as the rest of the manifest -data eliminates the possibility of forgetting to include both the Package.swift -manifest and a file specifying the Swift tools version in every commit which -should affect both. Users may also find it more convenient to only need to -commit a single file when making manifest changes. - -* Tools which wish to identify a Package.swift manifest (as distinct from -a Swift file which happens to be named "Package") may use the presence -of the version comment as an indicator of the file type. However, as long as -Swift 3.0 packages must be recognized, we cannot count on all Package.swift files -to provide this comment. - -#### Disadvantages - -* This essentially puts a second, tiny, domain-specific language into the -top of a Swift file, and requires the first line of the Package.swift manifest to -be parsed for this before it is run through the Swift interpreter. This will not -cause any concrete problems that we know of, but may be considered distasteful. - -* Novice users may be confused by seeing the Swift tools version at the top -of their Package.swift manifest file. - -### Swift tools version in a .swift-version file - -#### Approach - -The Swift tools version will be specified in a file named ".swift-version", -at the root level of the package. This file will follow the conventions -already established by the [swiftenv](https://github.com/kylef/swiftenv/) -project. The file will contain either a Swift marketing version number -or the name of a snapshot which has been published on Swift.org. - -The Swift Package Manager will have a database of known snapshot names which have -been published on Swift.org, as well as which Swift tools version each snapshot -corresponds to. A snapshot shall correspond to the first release which included -or will include that snapshot's content, and all snapshots -of a release in development shall be considered equivalent for the purposes -of the Swift tools version. This feature does not attempt to specify -compatibility between different snapshots during the development of a release, and -only allows snapshot names to be specified to preserve compatibility with swiftenv. -Versioning comparison between snapshots within a release may be added in a -future version of the package manager. - -The Swift tools version, as output by `swift package tools-version`, will always -take the form of a Swift version number, the string "future", or the string -"trunk". "future" indicates that the .swift-version file specifies a snapshot -name which is unknown and thus belongs to a future version of Swift. "trunk" -indicates that the .swift-version file specifies a snapshot name which is known -to this version of Swift, but which has not yet been given a designated -marketing version. "trunk" shall always be considered compatible with the -current version of package manager, and "future" shall always be considered -incompatible. It is an error to specify "future" or "trunk" directly in the -`.swift-version` file; these specifiers are determined from the package -manager's interpretation of the `.swift-version` file. - -#### Advantages - -* If we wish to store the Swift tools version outside of the Package.swift -manifest, this gives us a way to do so by reusing a file that many packages -already have, instead of adding yet another file. - -* This has some synergy with the behavior of swiftenv. When the package manager -updates the Swift tools version to something newer, swiftenv will know to -fetch and use the toolchain for that version before trying to build that package -(for the top-level package only). - -#### Disadvantages - -* Because swiftenv allows toolchain names in this file, the package manager -must as well. Allowing toolchain names significantly increases the complexity -of this feature, as it means that we need to maintain a database mapping toolchain -names to Swift versions. There may be process complexities in making sure that -as we produce new toolchains, the database in the toolchain knows about the name -of that toolchain, as well as all other toolchains that have been created. - -* The necessity of handling "future" and "trunk" versions, as output by -`swift package tools-version`, increases the complexity of interacting -with this feature for other tools that know about the package manager. - -* It is not clear if the synergy with swiftenv is perfect, or if there are -situations where users would prefer to specify a different toolchain for -swiftenv than they need their Swift tools version set to. - -### Swift tools version in a .swift-tools-version file - -#### Approach - -The Swift tools version will be specified in a file named ".swift-tools-version", -at the root level of the package. This will be a JSON file with one key, -`swift-tools-version`. (Any other keys will be ignored by this version of the -package manager). The value for this key will be a version number specifier. - -Though the Swift tools version refers to a Swift marketing version number and is -not a proper semantic version, the version number specifier shall follow the -syntax defined by [semantic versioning 2.0.0](http://semver.org/spec/v2.0.0.html). -This syntax allows for an optional pre-release version component or build -version component; this will not be used by the package manager currently, but -may be in a future release to provide stricter compatibility controls during the -development of a new version of Swift. - -#### Advantages - -* As this is a JSON file, it has a clear and familiar syntax and is easily -extensible for future use. - -* Because the Swift tools version is not specified in the Package.swift file, the -user is discouraged from trying to hand-edit this version and will usually leave -it up to the tools to manage, through the `swift package tools-version` command. -That will decrease the likelihood of mistakes. - -#### Disadvantages - -* This adds yet another file that every Swift package will need to have and -manage in source control. - ## Examples * The author of a package created with Swift 3 wishes to adopt the new Swift 4 -product definitions API in their manifest. Using a Swift 4 toolchain, the author first runs -`swift package tools-version --update` to make their package require the Swift 4.0 tools. They then make -any changes to their Package.swift manifest needed to make it compatible with the Swift 4 -language version and the revised Swift 4 Package Manager PackageDescription API. -Since their package sources are still written with Swift 3, they should specify -the Swift 3 language compatibility version in their manifest, if they didn't already, -so that it doesn't start defaulting to building their sources as Swift 4 code. -The author is now free to adopt new PackageDescription API in their Package.swift manifest. -They are not required to update the language version of their package sources at the same time. +product definitions API in their manifest. Using a Swift 4 toolchain, the author +first runs `swift package tools-version --update` to make their package require +the Swift 4.0 tools. They then make any changes to their Package.swift manifest +needed to make it compatible with the Swift 4 language version and the revised +Swift 4 Package Manager PackageDescription API. Since their package sources are +still written with Swift 3, they should specify the Swift 3 language +compatibility version in their manifest, if they didn't already, so that it +doesn't start defaulting to building their sources as Swift 4 code. The author +is now free to adopt new PackageDescription API in their Package.swift manifest. +They are not required to update the language version of their package sources at +the same time. * A package author wishes to support both the Swift 3 and Swift 4 tools, while conditionally adopting Swift 4 language features. The author specifies both @@ -297,14 +170,17 @@ to `3.1`. Their Package.swift manifest must continue to be compatible with the Swift 3 language, and must continue to use the Swift 3.1 version of the PackageDescription API. -* The author of a package created with the Swift 3 tools wishes to convert the package's sources to the Swift 4 language -version. They specify Swift 4 as their package's language compatibility version (using a mechanism -discussed in a separate evolution proposal). When they try to build their package, the package manager -emits an error informing them that they must update their Swift tools version to 4.0 or later, because -the Swift 4 tools are required to build a package when it no longer supports the Swift 3 language version. -The author runs `swift package tools-version --update` to make their package require the Swift 4.0 tools. They then make -any changes to their Package.swift manifest required to make it compatible with the Swift 4 -language version and the revised Swift 4 Package Manager PackageDescription API. +* The author of a package created with the Swift 3 tools wishes to convert the +package's sources to the Swift 4 language version. They specify Swift 4 as their +package's language compatibility version (using a mechanism discussed in a +separate evolution proposal). When they try to build their package, the package +manager emits an error informing them that they must update their Swift tools +version to 4.0 or later, because the Swift 4 tools are required to build a +package when it no longer supports the Swift 3 language version. The author runs +`swift package tools-version --update` to make their package require the Swift +4.0 tools. They then make any changes to their Package.swift manifest required +to make it compatible with the Swift 4 language version and the revised Swift 4 +Package Manager PackageDescription API. ## Impact on existing code @@ -341,7 +217,10 @@ the conditional compilation blocks, and will thus emit an error requiring the user to update the Swift tools version to a version which supports those new APIs. -The following table shows an example of which Swift language version will be used to interpret the Package.swift manifest, and to interpret the package's sources, based on the Swift tools in use and the parameters specified by the package. +The following table shows an example of which Swift language version will be +used to interpret the Package.swift manifest, and to interpret the package's +sources, based on the Swift tools in use and the parameters specified by the +package. | Swift Tools | Swift Tools Version | [Swift Language Compatibility Version](http://link/to/proposal) | Language Version Used | |:---:|:---:|:---:| --- | @@ -510,3 +389,41 @@ users. It would also allow users to import both modules, which we do not want to support. And it would allow users to continue using the old PackageDescription API in a manifest that is otherwise updated for Swift 4, which we also do not want to support. + +### Store the Swift tools version in a separate file + +Instead of storing the Swift tools version in a comment at the top of the +Package.swift manifest, we considered storing it in a separate file. +Possibilities considered were to either store it as JSON in a +`.swift-tools-version` file, or to store it in a `.swift-version` file in the +format already established by the [swiftenv](https://github.com/kylef/swiftenv/) +tool. + +Reasons we prefer to store this in the Package.swift manifest include: + +* Keeping the Swift tools version in the same file as the rest of the manifest +data eliminates the possibility of forgetting to include both the Package.swift +manifest and a file specifying the Swift tools version in every commit which +should affect both. Users may also find it more convenient to only need to +commit a single file when making manifest changes. + +* Users may like being able to see the Swift tools version when reading a +Package.swift manifest, instead of needing to look in a separate file. + +* Supporting the `.swift-version` standard would require supporting toolchain +names as a standing for a Swift tools version, which complicates this mechanism +significantly. + +### Specify the Swift tools version with a more "Swifty" syntax + +Several alternative suggestions were given for how to spell the comment that +specifies the Swift tools version. We could make it look like a line of Swift +code (`let toolsVersion = ToolsVersion._3_1`), a compiler directive +(`#swift-pm(tools-version: 3.1)`), or use camel case in the comment (`// +swiftToolsVersion: 3.1`). We rejected the former two suggestions because this +version is not actually going to be interpreted by the Swift compiler, so it's +misleading to make it appears as if it is a valid line of Swift code. Embedding +metadata in a leading comment is a strategy with a clear precedent in XML & +SGML. For the latter suggestion, we are preferring kebab-case +(`swift-tools-version`) because it is distinct from normal Swift naming, and +more clearly stands out as its own special (tiny) language, which it is. From 8bd2b8cc9a9e9226af84a099a171668415cc9c0d Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Tue, 7 Feb 2017 11:49:19 -0800 Subject: [PATCH 0132/4563] Amend to allow eliding the patch version component --- proposals/NNNN-package-manager-tools-version.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/proposals/NNNN-package-manager-tools-version.md b/proposals/NNNN-package-manager-tools-version.md index e72db49146..aa53c14466 100644 --- a/proposals/NNNN-package-manager-tools-version.md +++ b/proposals/NNNN-package-manager-tools-version.md @@ -128,11 +128,17 @@ string `// swift-tools-version:`, followed by a version number specifier. Though the Swift tools version refers to a Swift marketing version number and is not a proper semantic version, the version number specifier shall follow the -syntax defined by [semantic versioning 2.0.0](http://semver.org/spec/v2.0.0.html). -This syntax allows for an optional pre-release version component or build -version component; those components will not be used by the package manager currently, but -may be used in a future release to provide finer-grained compatibility controls during the -development of a new version of Swift. +syntax defined by [semantic versioning 2.0.0](http://semver.org/spec/v2.0.0.html), +with an amendment that the patch version component shall be optional and shall +be considered to be `0` if not specified. As we expect that patch versions will +not affect tools compatibility, the package manager will automatically elide the +patch version component when appropriate, including when setting a version using +the `swift package tools-version --set-current` command, to avoid unnecessarily +restricting package compatibility to specific patch versions. The semver syntax +allows for an optional pre-release version component or build version component; +those components will not be used by the package manager currently, but may be +used in a future release to provide finer-grained compatibility controls during +the development of a new version of Swift. After the version number specifier, an optional `;` character may be present; it, and anything else after it until the end of the first line, will be ignored by From 4eca7991a60186ba140b454476b274f9acc38599 Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Tue, 7 Feb 2017 11:50:31 -0800 Subject: [PATCH 0133/4563] Prepare for submitting proposal for review --- proposals/NNNN-package-manager-tools-version.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-package-manager-tools-version.md b/proposals/NNNN-package-manager-tools-version.md index aa53c14466..132567d9d3 100644 --- a/proposals/NNNN-package-manager-tools-version.md +++ b/proposals/NNNN-package-manager-tools-version.md @@ -1,8 +1,8 @@ # Package Manager Tools Version * Proposal: [SE-NNNN](NNNN-package-manager-tools-version.md) * Author: [Rick Ballard](https://github.com/rballard) -* Review manager: TBD -* Status: **WIP** +* Review manager: Anders Bertelrud +* Status: Scheduled for review (February 7...February 13, 2017) ## Introduction From acb325c6843cc74126a777c1ba856c728105d00b Mon Sep 17 00:00:00 2001 From: Anders Bertelrud Date: Tue, 7 Feb 2017 13:08:18 -0800 Subject: [PATCH 0134/4563] Add proposal number and dates for SE-0151, and make adjustments to make the header match the template. --- ...package-manager-swift-language-compatibility-version.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-package-manager-swift-language-compatibility-version.md => 0151-package-manager-swift-language-compatibility-version.md} (97%) diff --git a/proposals/NNNN-package-manager-swift-language-compatibility-version.md b/proposals/0151-package-manager-swift-language-compatibility-version.md similarity index 97% rename from proposals/NNNN-package-manager-swift-language-compatibility-version.md rename to proposals/0151-package-manager-swift-language-compatibility-version.md index 2bd602035c..8b5c8169e5 100644 --- a/proposals/NNNN-package-manager-swift-language-compatibility-version.md +++ b/proposals/0151-package-manager-swift-language-compatibility-version.md @@ -1,9 +1,9 @@ # Package Manager Swift Language Compatibility Version -* Proposal: [SE-NNNN](NNNN-package-manager-swift-language-compatibility-version.md) +* Proposal: [SE-0151](0151-package-manager-swift-language-compatibility-version.md) * Author: [Daniel Dunbar](https://github.com/ddunbar), [Rick Ballard](http://github.com/rballard) -* Review manager: Anders Bertelrud -* Status: Scheduled for review (February 7...February 13, 2017) +* Review Manager: Anders Bertelrud +* Status: **Active review (February 7...February 13, 2017)** ## Introduction From 2c59efdd0f6c4836e6411e56d43f11d5b21f9482 Mon Sep 17 00:00:00 2001 From: Anders Bertelrud Date: Tue, 7 Feb 2017 13:32:25 -0800 Subject: [PATCH 0135/4563] Add proposal number and dates for SE-0152, and make adjustments to make the header match the template. --- ...ols-version.md => 0152-package-manager-tools-version.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-package-manager-tools-version.md => 0152-package-manager-tools-version.md} (99%) diff --git a/proposals/NNNN-package-manager-tools-version.md b/proposals/0152-package-manager-tools-version.md similarity index 99% rename from proposals/NNNN-package-manager-tools-version.md rename to proposals/0152-package-manager-tools-version.md index 132567d9d3..b0063fc485 100644 --- a/proposals/NNNN-package-manager-tools-version.md +++ b/proposals/0152-package-manager-tools-version.md @@ -1,8 +1,8 @@ # Package Manager Tools Version -* Proposal: [SE-NNNN](NNNN-package-manager-tools-version.md) +* Proposal: [SE-0152](0152-package-manager-tools-version.md) * Author: [Rick Ballard](https://github.com/rballard) -* Review manager: Anders Bertelrud -* Status: Scheduled for review (February 7...February 13, 2017) +* Review Manager: Anders Bertelrud +* Status: **Active review (February 7...February 13, 2017)** ## Introduction From 8a347bc78517e1a47cff401ca6eae59518f7cbe2 Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Mon, 19 Dec 2016 16:12:30 -0800 Subject: [PATCH 0136/4563] [WIP] new new integers proposal --- proposals/0000-even-more-improved-integers.md | 1133 +++++++++++++++++ 1 file changed, 1133 insertions(+) create mode 100644 proposals/0000-even-more-improved-integers.md diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md new file mode 100644 index 0000000000..46bc3bcd1a --- /dev/null +++ b/proposals/0000-even-more-improved-integers.md @@ -0,0 +1,1133 @@ +# Protocol-oriented integers (take 2) + +* Proposal: [SE-NNNN](0000-even-more-improved-integers.md) +* Authors: [Dave Abrahams](https://github.com/dabrahams), [Maxim Moiseev](https://github.com/moiseev) +* Review Manager: TBD +* Status: **Awaiting review** +* Bug: [SR-3196](https://bugs.swift.org/browse/SR-3196) +* Previous Proposal: [SE-0104][se104] + +## Introduction + +This proposal is an evolution of the [SE-0104][se104]. The goal is still to +clean up Swifts integer APIs and make them more useful for generic programming. + +The language has evolved in ways that affect integers APIs, since the time +original proposal was approved for Swift 3. Besides, we attempted to implement +the proposed model in The Standard Library and found that some essential APIs +were missing, whereas others could be safely removed. + +Major changes to the APIs introduced by this proposal (as compared to +[SE-0104][se104]) will be listed in a [dedicated section](#major-differences-introduced-by-this-proposal). + +## Motivation + +Swift's integer protocols don't currently provide a suitable basis for generic +programming. See [this blog post](http://blog.krzyzanowskim.com/2015/03/01/swift_madness_of_generic_integer/) +for an example of an attempt to implement a generic algorithm over integers. + +The way the `Arithmetic` protocol is defined, it does not generalize to floating +point numbers and also slows down compilation by requiring every concrete type +to provide an implementation of arithmetic operators, thus polluting the +overload set. + +Converting from one integer type to another is performed using the concept of +the 'maximum width integer' (see `MaxInt`), which is an artificial limitation. +The very existence of `MaxInt` makes it unclear what to do, should someone +implement `Int256`, for example. + +Another annoying problem is the inability to use integers of different types in +comparison and bit-shift operations. For example, the following snippets won't +compile: + +```Swift +var x: Int8 = 42 +let y = 1 +let z = 0 + +x <<= y // error: binary operator '<<=' cannot be applied to operands of type 'Int8' and 'Int' +if x > z { ... } // error: binary operator '>' cannot be applied to operands of type 'Int8' and 'Int' +``` + +Currently, bit-shifting a negative number of (or too many) bits causes a trap +on some platforms, which makes low-level bit manipulations needlessly dangerous +and unpredictable. + +Finally, the current design predates many of the improvements that came in +Swift 2, and hasn't been revised since then. + +## Proposed solution + +We propose a new model that does not have above mentioned problems and is +more easily extensible. + +~~~~ + +--------------+ +-------------+ + +------>+ Arithmetic | | Comparable | + | | (+,-,*,/) | | (==,<,>,...)| + | +-------------++ +---+---------+ + | ^ ^ ++-------+------------+ | | +| SignedArithmetic | +-+-------+------+ +| (unary -) | | BinaryInteger | ++------+-------------+ | (words,%,...) | + ^ ++---+-----+-----+ + | +-----------^ ^ ^-----------+ + | | | | ++------+---------++ +---------+-------------+ ++------------------+ +| SignedInteger | | FixedWidthInteger | | UnsignedInteger | +| | | (bitwise,overflow,...)| | | ++---------------+-+ +-+-----------------+---+ ++------------------+ + ^ ^ ^ ^ + | | | | + | | | | + ++--------+-+ +-+-------+-+ + |Int family |-+ |UInt family|-+ + +-----------+ | +-----------+ | + +-----------+ +-----------+ +~~~~ + + +There are several benefits provided by this model over the old one: + +- It allows mixing integer types in generic functions. + + The possibility to initialize instances of any concrete integer type with + values of any other concrete integer type enables writing functions that + operate on more than one type conforming to `BinaryInteger`, such as + heterogeneous comparisons or bit shifts, described later. + +- It removes the overload resolution overhead. + + Arithmetic and bitwise operations can now be defined as free functions + delegating work to concrete types. This approach significantly reduces the + number of overloads for those operations, which used to be defined for every + single concrete integer type. + +- It enables protocol sharing between integer and floating point types. + + Note the exclusion of the `%` operation from `Arithmetic`. Its behavior for + floating point numbers is sufficiently different from the one for integers + that using it in generic context would lead to confusion. The `FloatingPoint` + protocol introduced by + [SE-0067](0067-floating-point-protocols.md) + should now refine `SignedArithmetic`. + +- It makes future extensions possible. + + The proposed model eliminates the 'largest integer type' concept previously + used to interoperate between integer types (see `toIntMax` in the current + model) and instead provides access to machine words. It also introduces the + `doubleWidthMultiply`, `doubleWidthDivide`, and `quotientAndRemainder` + methods. Together these changes can be used to provide an efficient + implementation of bignums that would be hard to achieve otherwise. + +The attempted implementation in The Standard Library is available +[in the new-integer-protocols branch][impl]. + +### A note on bit shifts + +This proposal introduces the concepts of *smart shifts* and *masking shifts*. + +The semantics of shift operations are +[often undefined](http://llvm.org/docs/LangRef.html#bitwise-binary-operations) +in under- or over-shift cases. *Smart shifts*, implemented by `>>` and `<<`, +are designed to address this problem and always behave in a well defined way, +as shown in the examples below: + +- `x << -2` is equivalent to `x >> 2` + +- `(1 as UInt8) >> 42)` will evaluate to `0` + +- `(-128 as Int8) >> 42)` will evaluate to `0xff` or `-1` + +In most scenarios, the right hand operand is a literal constant, and branches +for handling under- and over-shift cases can be optimized away. For other +cases, this proposal provides *masking shifts*, implemented by `&>>` and `&<<`. +A masking shift logically preprocesses the right hand operand by masking its +bits to produce a value in the range `0...(x-1)` where `x` is the number of +bits in the left hand operand. On most architectures this masking is already +performed by the CPU's shift instructions and has no cost. Both kinds of shift +avoid undefined behavior and produce uniform semantics across architectures. + + +## Detailed design + +### Major differences introduced by this proposal + +* [SE-0091][se91] removed the necessity to dispatch generic operators through special methods. + +* Standard Library no longer provides `+` and `-` operators for `Strideable` types. + + This has proven to be a problematic, as one could have written mixed-type code + like `let x: Int64 = 42; x += (1 as Int)`, which should not be supported. + Besides, since the `Stride` of an unsigned type is signed, Standard Library + had to implement a hack to make code like `let x: UInt = 42; x += (1 as Int)` + ambiguous. These operators were only necessary because they made advancing + collection indices convenient, which is no longer the case as of Swift 3 and + introduction of the [new indexing model](0065-collections-move-indices.md). + +* Shifts and other bitwise operations were moved from `FixedWidthInteger` to `BinaryInteger`. + +* `BitwiseOperations` protocol was deprecated. + +* `minimumSignedRepresentationBitWidth` property was removed. + +* `trailingZeros` property was added to the `BinaryInteger` protocol. + +* Endian-converting initializers and properties were added to the `FixedWidthInteger` protocol. + +### Protocols + +#### `Arithmetic` + +The `Arithmetic` protocol declares binary arithmetic +operators – such as `+`, `-` and `*` — and their mutating counterparts. + +It provides a suitable basis for arithmetic on scalars such as integers and +floating point numbers. + +Both mutating and non-mutating operations are declared in the protocol, however +only the mutating ones are required, as default implementations of the +non-mutating ones are provided by a protocol extension. + +The `Magnitude` associated type is able to hold the absolute value of any +possible value of `Self`. Concrete types do not have to provide a type alias for +it, as it can be inferred from the `magnitude` property. This property can +be useful in operations that are simpler to implement in terms of unsigned +values, for example, printing a value of an integer, which is just printing a +'-' character in front of an absolute value. + +Please note that for ordinary work, the `magnitude` property **should not** +be preferred to the `abs(_)` function, whose return value is of the same type +as its argument. + + +```Swift +public protocol Arithmetic : Equatable, ExpressibleByIntegerLiteral { + /// Creates a new instance from the given integer, if it can be represented + /// exactly. + /// + /// If the value passed as `source` is not representable exactly, the result + /// is `nil`. In the following example, the constant `x` is successfully + /// created from a value of `100`, while the attempt to initialize the + /// constant `y` from `1_000` fails because the `Int8` type can represent + /// `127` at maximum: + /// + /// let x = Int8(exactly: 100) + /// // x == Optional(100) + /// let y = Int8(exactly: 1_000) + /// // y == nil + /// + /// - Parameter source: A floating-point value to convert to an integer. + init?(exactly source: T) + + /// A type that can represent the absolute value of any possible value of the + /// conforming type. + associatedtype Magnitude : Equatable, ExpressibleByIntegerLiteral + + /// The magnitude of this value. + /// + /// For any numeric value `x`, `x.magnitude` is the absolute value of `x`. + /// You can use the `magnitude` property in operations that are simpler to + /// implement in terms of unsigned values, such as printing the value of an + /// integer, which is just printing a '-' character in front of an absolute + /// value. + /// + /// let x = -200 + /// // x.magnitude == 200 + /// + /// The global `abs(_:)` function provides more familiar syntax when you need + /// to find an absolute value. In addition, because `abs(_:)` always returns + /// a value of the same type, even in a generic context, using the function + /// instead of the `magnitude` property is encouraged. + /// + /// - SeeAlso: `abs(_:)` + var magnitude: Magnitude { get } + + /// Returns the sum of the two given values. + /// + /// The sum of `lhs` and `rhs` must be representable in the same type. In the + /// following example, the result of `100 + 200` is greater than the maximum + /// representable `Int8` value: + /// + /// let x: Int8 = 10 + 21 + /// // x == 31 + /// let y: Int8 = 100 + 121 + /// // Overflow error + /// + /// - Parameters: + /// - lhs: The first value to add. + /// - rhs: The second value to add. + static func +(_ lhs: Self, _ rhs: Self) -> Self + + /// Adds the given value to this value in place. + /// + /// This method serves as the basis for the in-place addition operator + /// (`+=`). For example: + /// + /// var (x, y) = (15, 15) + /// x.add(7) + /// // x == 22 + /// y += 7 + /// // y == 22 + /// + /// - Parameter rhs: The value to add to this value. + /// + /// - SeeAlso: `adding(_:)` + static func +=(_ lhs: inout Self, rhs: Self) + + /// Returns the difference of the two given values. + /// + /// The difference of `lhs` and `rhs` must be representable in the same type. + /// In the following example, the result of `10 - 21` is less than zero, the + /// minimum representable `UInt` value: + /// + /// let x: UInt = 21 - 10 + /// // x == 11 + /// let y: UInt = 10 - 21 + /// // Overflow error + /// + /// - Parameters: + /// - lhs: A numeric value. + /// - rhs: The value to subtract from `lhs`. + static func -(_ lhs: Self, _ rhs: Self) -> Self + + /// Subtracts the given value from this value in place. + /// + /// This method serves as the basis for the in-place subtraction operator + /// (`-=`). For example: + /// + /// var (x, y) = (15, 15) + /// x.subtract(7) + /// // x == 8 + /// y -= 7 + /// // y == 8 + /// + /// - Parameter rhs: The value to subtract from this value. + /// + /// - SeeAlso: `subtracting(_:)` + static func -=(_ lhs: inout Self, rhs: Self) + + /// Returns the product of the two given values. + /// + /// The product of `lhs` and `rhs` must be representable in the same type. In + /// the following example, the result of `10 * 50` is greater than the + /// maximum representable `Int8` value. + /// + /// let x: Int8 = 10 * 5 + /// // x == 50 + /// let y: Int8 = 10 * 50 + /// // Overflow error + /// + /// - Parameters: + /// - lhs: The first value to multiply. + /// - rhs: The second value to multiply. + static func *(_ lhs: Self, _ rhs: Self) -> Self + + /// Multiples this value by the given value in place. + /// + /// This method serves as the basis for the in-place multiplication operator + /// (`*=`). For example: + /// + /// var (x, y) = (15, 15) + /// x.multiply(by: 7) + /// // x == 105 + /// y *= 7 + /// // y == 105 + /// + /// - Parameter rhs: The value to multiply by this value. + /// + /// - SeeAlso: `multiplying(by:)` + static func *=(_ lhs: inout Self, rhs: Self) + + /// Returns the quotient of dividing the first value by the second. + /// + /// For integer types, any remainder of the division is discarded. + /// + /// let x = 21 / 5 + /// // x == 4 + /// + /// - Parameters: + /// - lhs: The value to divide. + /// - rhs: The value to divide `lhs` by. `rhs` must not be zero. + static func /(_ lhs: Self, _ rhs: Self) -> Self + + /// Divides this value by the given value in place. + /// + /// This method serves as the basis for the in-place division operator + /// (`/=`). For example: + /// + /// var (x, y) = (15, 15) + /// x.divide(by: 7) + /// // x == 2 + /// y /= 7 + /// // y == 2 + /// + /// - Parameter rhs: The value to divide this value by. `rhs` must not be + /// zero. + /// + /// - SeeAlso: `dividing(by:)` + static func /=(_ lhs: inout Self, rhs: Self) +} + +extension Arithmetic { + public init() { self = 0 } +} +``` + +#### `SignedArithmetic` + +The `SignedArithmetic` protocol is for numbers that can be negated. + +```Swift +public protocol SignedArithmetic : Arithmetic { + /// Returns the additive inverse of this value. + /// + /// let x = 21 + /// let y = -x + /// // y == -21 + /// + /// - Returns: The additive inverse of this value. + /// + /// - SeeAlso: `negate()` + static prefix func - (_ operand: Self) -> Self + + /// Replaces this value with its additive inverse. + /// + /// The following example uses the `negate()` method to negate the value of + /// an integer `x`: + /// + /// var x = 21 + /// x.negate() + /// // x == -21 + /// + /// - SeeAlso: The unary minus operator (`-`). + mutating func negate() +} + +extension SignedArithmetic { + public static prefix func - (_ operand: Self) -> Self { + var result = operand + result.negate() + return result + } + + public mutating func negate() { + self = Self() - self + } +} +``` + +#### `BinaryInteger` + +The `BinaryInteger` protocol is the basis for all the integer types provided by +the standard library. + +The `isEqual(to:)` and `isLess(than:)` methods provide implementations for +`Equatable` and `Comparable` protocol conformances. Similar to how arithmetic +operations are dispatched in `Arithmetic`, `==` and `<` operators for +homogeneous comparisons are implemented as generic free functions invoking the +`isEqual(to:)` and `isLess(than:)` protocol methods respectively. + +This protocol adds 4 new initializers. One of them allows to create integers +from floating point numbers, if the value is representable exactly, others +support construction from instances of any type conforming to `BinaryInteger`, +using different strategies: + + - Initialize `Self` with the value, provided that the value is representable. + The precondition should be satisfied by the caller. + + - Extend or truncate the value to fit into `Self` + + - Clamp the value to the representable range of `Self` + +```Swift +public protocol BinaryInteger : + Comparable, Hashable, Arithmetic, CustomStringConvertible, Strideable { + + /// A Boolean value indicating whether this type is a signed integer type. + /// + /// *Signed* integer types can represent both positive and negative values. + /// *Unsigned* integer types can represent only nonnegative values. + static var isSigned: Bool { get } + + /// Creates an integer from the given floating-point value, if it can be + /// represented exactly. + /// + /// If the value passed as `source` is not representable exactly, the result + /// is `nil`. In the following example, the constant `x` is successfully + /// created from a value of `21.0`, while the attempt to initialize the + /// constant `y` from `21.5` fails: + /// + /// let x = Int(exactly: 21.0) + /// // x == Optional(21) + /// let y = Int(exactly: 21.5) + /// // y == nil + /// + /// - Parameter source: A floating-point value to convert to an integer. + init?(exactly source: T) + + /// Creates an integer from the given floating-point value, truncating any + /// fractional part. + /// + /// Truncating the fractional part of `source` is equivalent to rounding + /// toward zero. + /// + /// let x = Int(21.5) + /// // x == 21 + /// let y = Int(-21.5) + /// // y == -21 + /// + /// If `source` is outside the bounds of this type after truncation, a + /// runtime error may occur. + /// + /// let z = UInt(-21.5) + /// // Error: ...the result would be less than UInt.min + /// + /// - Parameter source: A floating-point value to convert to an integer. + /// `source` must be representable in this type after truncation. + init(_ source: T) + + /// Creates an new instance from the given integer. + /// + /// If the value passed as `source` is not representable in this type, a + /// runtime error may occur. + /// + /// let x = -500 as Int + /// let y = Int32(x) + /// // y == -500 + /// + /// // -500 is not representable as a 'UInt32' instance + /// let z = UInt32(x) + /// // Error + /// + /// - Parameter source: An integer to convert. `source` must be representable + /// in this type. + init(_ source: T) + + /// Creates a new instance from the bit pattern of the given instance by + /// sign-extending or truncating to fit this type. + /// + /// When the bit width of `T` (the type of `source`) is equal to or greater + /// than this type's bit width, the result is the truncated + /// least-significant bits of `source`. For example, when converting a + /// 16-bit value to an 8-bit type, only the lower 8 bits of `source` are + /// used. + /// + /// let p: Int16 = -500 + /// // 'p' has a binary representation of 11111110_00001100 + /// let q = Int8(extendingOrTruncating: p) + /// // q == 12 + /// // 'q' has a binary representation of 00001100 + /// + /// When the bit width of `T` is less than this type's bit width, the result + /// is *sign-extended* to fill the remaining bits. That is, if `source` is + /// negative, the result is padded with ones; otherwise, the result is + /// padded with zeros. + /// + /// let u: Int8 = 21 + /// // 'u' has a binary representation of 00010101 + /// let v = Int16(extendingOrTruncating: u) + /// // v == 21 + /// // 'v' has a binary representation of 00000000_00010101 + /// + /// let w: Int8 = -21 + /// // 'w' has a binary representation of 11101011 + /// let x = Int16(extendingOrTruncating: w) + /// // x == -21 + /// // 'x' has a binary representation of 11111111_11101011 + /// let y = UInt16(extendingOrTruncating: w) + /// // y == 65515 + /// // 'y' has a binary representation of 11111111_11101011 + /// + /// - Parameter source: An integer to convert to this type. + init(extendingOrTruncating source: T) + + /// Creates a new instance with the representable value that's closest to the + /// given integer. + /// + /// If the value passed as `source` is greater than the maximum representable + /// value in this type, the result is the type's `max` value. If `source` is + /// less than the smallest representable value in this type, the result is + /// the type's `min` value. + /// + /// In this example, `x` is initialized as an `Int8` instance by clamping + /// `500` to the range `-128...127`, and `y` is initialized as a `UInt` + /// instance by clamping `-500` to the range `0...UInt.max`. + /// + /// let x = Int8(clamping: 500) + /// // x == 127 + /// // x == Int8.max + /// + /// let y = UInt(clamping: -500) + /// // y == 0 + /// + /// - Parameter source: An integer to convert to this type. + init(clamping source: T) + + /// Returns the n-th word, counting from the least significant to most + /// significant, of this value's binary representation. + /// + /// The `word(at:)` method returns negative values in two's complement + /// representation, regardless of a type's underlying implementation. If `n` + /// is greater than the number of words in this value's current + /// representation, the result is `0` for positive numbers and `~0` for + /// negative numbers. + /// + /// - Parameter n: The word to return, counting from the least significant to + /// most significant. `n` must be greater than or equal to zero. + /// - Returns: An word-sized, unsigned integer with the bit pattern of the + /// n-th word of this value. + func word(at n: Int) -> UInt + + /// The number of bits in the current binary representation of this value. + /// + /// This property is a constant for instances of fixed-width integer + /// types. + var bitWidth : Int { get } + + /// The number of trailing zeros in this value's binary representation. + /// + /// For example, in a fixed-width integer type with a `bitWidth` value of 8, + /// the number -8 has three trailing zeros. + /// + /// let x = Int8(bitPattern: 0b1111_1000) + /// // x == -8 + /// // x.trailingZeros == 3 + var trailingZeros: Int { get } + + /// Returns the remainder of dividing the first value by the second. + /// + /// The result has the same sign as `lhs` and is less than `rhs.magnitude`. + /// + /// let x = 22 % 5 + /// // x == 2 + /// let y = 22 % -5 + /// // y == 2 + /// let z = -22 % -5 + /// // z == -2 + /// + /// - Parameters: + /// - lhs: The value to divide. + /// - rhs: The value to divide `lhs` by. `rhs` must not be zero. + static func %(_ lhs: Self, _ rhs: Self) -> Self + + /// Replaces this value with the remainder of itself divided by the given + /// value. For example: + /// + /// var x = 15 + /// x %= 7 + /// // x == 1 + /// + /// - Parameter rhs: The value to divide this value by. `rhs` must not be + /// zero. + /// + /// - SeeAlso: `remainder(dividingBy:)` + static func %=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the inverse of the bits set in the argument. + /// + /// The bitwise NOT operator (`~`) is a prefix operator that returns a value + /// in which all the bits of its argument are flipped: Bits that are `1` in + /// the argument are `0` in the result, and bits that are `0` in the argument + /// are `1` in the result. This is equivalent to the inverse of a set. For + /// example: + /// + /// let x: UInt8 = 5 // 0b00000101 + /// let notX = ~x // 0b11111010 + /// + /// Performing a bitwise NOT operation on 0 returns a value with every bit + /// set to `1`. + /// + /// let allOnes = ~UInt8.min // 0b11111111 + /// + /// - Complexity: O(1). + static prefix func ~ (_ x: Self) -> Self + + /// Returns the result of performing a bitwise AND operation on this value + /// and the given value. + /// + /// A bitwise AND operation results in a value that has each bit set to `1` + /// where *both* of its arguments have that bit set to `1`. For example: + /// + /// let x: UInt8 = 5 // 0b00000101 + /// let y: UInt8 = 14 // 0b00001110 + /// let z = x.bitwiseAnd(y) // 0b00000100 + /// + /// This method serves as the basis for the bitwise AND operator (`&`). + /// + /// - Parameter rhs: The value to perform the bitwise AND with this value. + /// - Returns: A new value with each bit set to `1` where this value and + /// `rhs` both have bits set to `1`. + static func &(_ lhs: Self, _ rhs: Self) -> Self + static func &=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the result of performing a bitwise OR operation on this value and + /// the given value. + /// + /// A bitwise OR operation results in a value that has each bit set to `1` + /// where *one or both* of its arguments have that bit set to `1`. For + /// example: + /// + /// let x: UInt8 = 5 // 0b00000101 + /// let y: UInt8 = 14 // 0b00001110 + /// let z = x.bitwiseOr(y) // 0b00001111 + /// + /// This method serves as the basis for the bitwise OR operator (`|`). + /// + /// - Parameter rhs: The value to perform the bitwise OR with this value. + /// - Returns: A new value with each bit set to `1` where this value, `rhs`, + /// or both have bits set to `1`. + static func |(_ lhs: Self, _ rhs: Self) -> Self + static func |=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the result of performing a bitwise XOR operation on this value + /// and the given value. + /// + /// A bitwise XOR operation, also known as an exclusive OR operation, results + /// in a value that has each bit set to `1` where *one or the other but not + /// both* of its arguments had that bit set to `1`. For example: + /// + /// let x: UInt8 = 5 // 0b00000101 + /// let y: UInt8 = 14 // 0b00001110 + /// let z = x.bitwiseXor(y) // 0b00001011 + /// + /// This method serves as the basis for the bitwise XOR operator (`^`). + /// + /// - Parameter rhs: The value to perform the bitwise XOR with this value. + /// - Returns: A new value with each bit set to `1` where either this value + /// or `rhs`, but not both, have bits set to `1`. + static func ^(_ lhs: Self, _ rhs: Self) -> Self + static func ^=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the result of shifting this value's binary representation the + /// specified number of digits to the right. + /// + /// In a *masking shift*, the bit pattern of the value passed as `rhs` is + /// masked to produce a value between zero and the bit width of `lhs`. The + /// shift is performed using this masked value. Masking shifts require more + /// care to use correctly than a traditional bit shift, but are likely to be + /// more efficient when used with shift amounts that are not compile-time + /// constants. On most architectures, a masking shift compiles down to a + /// single instruction. + /// + /// For example, if you shift an 8-bit, unsigned integer by 2, the shift + /// amount requires no masking. + /// + /// let x: UInt8 = 30 // 0b00011110 + /// let y = x &>> 2 + /// // y == 7 // 0b00000111 + /// + /// However, if you shift it by 11, it first bitmasks `rhs` to `3`, and then + /// uses that masked value as the number of bits to shift `x`. + /// + /// let z = x &>> 11 + /// // z == 3 // 0b00000011 + /// + /// Relationship to the Right Shift Operator + /// ---------------------------------------- + /// + /// The masking right shift operator handles attempted overshifts and + /// undershifts differently from the right shift operator (`>>`). When the + /// value passed as `rhs` in a masking shift is within the range + /// `0...> 2 + /// // y1 == 7 // 0b00000111 + /// let y2 = x >> 2 + /// // y2 == 7 // 0b00000111 + /// + /// The right shift operator does not mask its right-hand-side argument, so + /// passing `11` as `rhs` shifts all the bits of `x` to zero. + /// + /// let z1 = x &>> 11 + /// // z1 == 240 // 0b00000011 + /// let z2 = x >> 11 + /// // z2 == 0 // 0b00000000 + /// + /// - Parameter rhs: The number of bits to shift this value to the right. If + /// `rhs` is outside the range `0..>` + static func &>>(_ lhs: Self, _ rhs: Self) -> Self + static func &>>=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the result of shifting this value's binary representation the + /// specified number of digits to the left. + /// + /// In a *masking shift*, the bit pattern of the value passed as `rhs` is + /// masked to produce a value between zero and the bit width of `lhs`. The + /// shift is performed using this masked value. Masking shifts require more + /// care to use correctly than a traditional bit shift, but are likely to be + /// more efficient when used with shift amounts that are not compile-time + /// constants. On most architectures, a masking shift compiles down to a + /// single instruction. + /// + /// For example, if you shift an 8-bit, unsigned integer by 2, the shift + /// amount requires no masking. + /// + /// let x: UInt8 = 30 // 0b00011110 + /// let y = x &>> 2 + /// // y == 120 // 0b01111000 + /// + /// However, if you shift it by 11, it first bitmasks `rhs` to `3`, and then + /// uses that masked value as the number of bits to shift `x`. + /// + /// let z = x &<< 11 + /// // z == 240 // 0b11110000 + /// + /// Relationship to the Left Shift Operator + /// --------------------------------------- + /// + /// The masking left shift operator handles attempted overshifts and + /// undershifts differently from the left shift operator (`<<`). When the + /// value passed as `rhs` in a masking shift is within the range + /// `0...>`, `<<` + static func &<<(_ lhs: Self, _ rhs: Self) -> Self + static func &<<=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the quotient and remainder of this value divided by the given + /// value. + /// + /// Use this method to calculate the quotient and remainder of a division at + /// the same time. + /// + /// let x = 1_000_000 + /// let (q, r) = x.quotientAndRemainder(dividingBy: 933) + /// // q == 1071 + /// // r == 757 + /// + /// - Parameter rhs: The value to divide this value by. + /// - Returns: A tuple containing the quotient and remainder of this value + /// divided by `rhs`. + func quotientAndRemainder(dividingBy rhs: Self) + -> (quotient: Self, remainder: Self) + + /// Returns `-1` if this value is negative and `1` if it's positive; + /// otherwise, `0`. + /// + /// - Returns: The sign of this number, expressed as an integer of the same + /// type. + func signum() -> Self +} +``` + +#### `FixedWidthInteger` + +The `FixedWidthInteger` protocol adds the notion of endianness, and static +properties for type bounds and size. + +The `WithOverflow` family of methods is used in default implementations of +mutating arithmetic methods (see the `Arithmetic` protocol). Having these +methods allows the library to provide both bounds-checked and masking +implementations of arithmetic operations, without duplicating code. + +The `doubleWidthMultiply` and `doubleWidthDivide` methods are necessary building +blocks to implement support for integer types of a greater width such as +arbitrary-precision integers. + +```Swift +public protocol FixedWidthInteger : BinaryInteger { + /// The number of bits used for the underlying binary representation of + /// values of this type. + /// + /// An unsigned, fixed-width integer type can represent values from 0 through + /// `(2 ** bitWidth) - 1`, where `**` is exponentiation. A signed, + /// fixed-width integer type can represent values from + /// `-(2 ** bitWidth - 1)` through `(2 ** bitWidth - 1) - 1`. For example, + /// the `Int8` type has a `bitWidth` value of 8 and can store any integer in + /// the range `-128...127`. + static var bitWidth : Int { get } + + /// The maximum representable integer in this type. + /// + /// For unsigned integer types, this value is `(2 ** bitWidth) - 1`, where + /// `**` is exponentiation. For signed integer types, this value is + /// `(2 ** bitWidth - 1) - 1`. + static var max: Self { get } + + /// The minimum representable value. + /// + /// For unsigned integer types, this value is always `0`. For signed integer + /// types, this value is `-(2 ** bitWidth - 1)`, where `**` is + /// exponentiation. + static var min: Self { get } + + /// Returns the sum of this value and the given value along with a flag + /// indicating whether overflow occurred in the operation. + /// + /// - Parameter other: The value to add to this value. + /// - Returns: A tuple containing the result of the addition along with a + /// flag indicating whether overflow occurred. If the `overflow` component + /// is `.none`, the `partialValue` component contains the entire sum. If + /// the `overflow` component is `.overflow`, an overflow occurred and the + /// `partialValue` component contains the truncated sum of this value and + /// `other`. + /// + /// - SeeAlso: `+` + func addingWithOverflow(_ other: Self) + -> (partialValue: Self, overflow: ArithmeticOverflow) + + /// Returns the difference of this value and the given value along with a + /// flag indicating whether overflow occurred in the operation. + /// + /// - Parameter other: The value to subtract from this value. + /// - Returns: A tuple containing the result of the subtraction along with a + /// flag indicating whether overflow occurred. If the `overflow` component + /// is `.none`, the `partialValue` component contains the entire + /// difference. If the `overflow` component is `.overflow`, an overflow + /// occurred and the `partialValue` component contains the truncated + /// result of `other` subtracted from this value. + /// + /// - SeeAlso: `-` + func subtractingWithOverflow(_ other: Self) + -> (partialValue: Self, overflow: ArithmeticOverflow) + + /// Returns the product of this value and the given value along with a flag + /// indicating whether overflow occurred in the operation. + /// + /// - Parameter other: The value to multiply by this value. + /// - Returns: A tuple containing the result of the multiplication along with + /// a flag indicating whether overflow occurred. If the `overflow` + /// component is `.none`, the `partialValue` component contains the entire + /// product. If the `overflow` component is `.overflow`, an overflow + /// occurred and the `partialValue` component contains the truncated + /// product of this value and `other`. + /// + /// - SeeAlso: `*`, `doubleWidthMultiply(_:_:)` + func multipliedWithOverflow(by other: Self) + -> (partialValue: Self, overflow: ArithmeticOverflow) + + /// Returns the quotient of dividing this value by the given value along with + /// a flag indicating whether overflow occurred in the operation. + /// + /// For a value `x`, if zero is passed as `other`, the result is + /// `(x, .overflow)`. + /// + /// - Parameter other: The value to divide this value by. + /// - Returns: A tuple containing the result of the division along with a + /// flag indicating whether overflow occurred. If the `overflow` component + /// is `.none`, the `partialValue` component contains the entire quotient. + /// If the `overflow` component is `.overflow`, an overflow occurred and + /// the `partialValue` component contains the truncated quotient. + /// + /// - SeeAlso: `/`, `doubleWidthDivide(_:_:)` + func dividedWithOverflow(by other: Self) + -> (partialValue: Self, overflow: ArithmeticOverflow) + + /// Returns a tuple containing the high and low parts of the result of + /// multiplying its arguments. + /// + /// Use this method to calculate the full result of a product that would + /// otherwise overflow. Unlike traditional truncating multiplication, the + /// `doubleWidthMultiply(_:_:)` method returns both the `high` and `low` + /// parts of the product of `lhs` and `rhs`. The following example uses this + /// method to multiply two `UInt8` values that normally overflow when + /// multiplied: + /// + /// let x: UInt8 = 100 + /// let y: UInt8 = 20 + /// let result = UInt8.doubleWidthMultiply(100, 20) + /// // result.high == 0b00000111 + /// // result.low == 0b11010000 + /// + /// The product of `x` and `y` is 2000, which is too large to represent in a + /// `UInt8` instance. The `high` and `low` components of the `result` tuple + /// represent 2000 when concatenated to form a double-width integer; that + /// is, using `result.high` as the high byte and `result.low` as the low byte + /// of a `UInt16` instance. + /// + /// let z = UInt16(result.high) << 8 | UInt16(result.low) + /// // z == 2000 + /// + /// - Parameters: + /// - lhs: A value to multiply. + /// - rhs: Another value to multiply. + /// - Returns: A tuple containing the high and low parts of the result of + /// multiplying `lhs` and `rhs`. + /// + /// - SeeAlso: `multipliedWithOverflow(by:)` + static func doubleWidthMultiply(_ lhs: Self, _ rhs: Self) + -> (high: Self, low: Magnitude) + + /// Returns a tuple containing the quotient and remainder of dividing the + /// first argument by the second. + /// + /// The resulting quotient must be representable within the bounds of the + /// type. If the quotient of dividing `lhs` by `rhs` is too large to + /// represent in the type, a runtime error may occur. + /// + /// - Parameters: + /// - lhs: A tuple containing the high and low parts of a double-width + /// integer. The `high` component of the tuple carries the sign, if the + /// type is signed. + /// - rhs: The integer to divide into `lhs`. + /// - Returns: A tuple containing the quotient and remainder of `lhs` divided + /// by `rhs`. + static func doubleWidthDivide( + _ lhs: (high: Self, low: Magnitude), _ rhs: Self) + -> (quotient: Self, remainder: Self) + + /// The number of bits equal to 1 in this value's binary representation. + /// + /// For example, in a fixed-width integer type with a `bitWidth` value of 8, + /// the number 31 has five bits equal to 1. + /// + /// let x: Int8 = 0b0001_1111 + /// // x == 31 + /// // x.popcount == 5 + var popcount: Int { get } + + /// The number of leading zeros in this value's binary representation. + /// + /// For example, in a fixed-width integer type with a `bitWidth` value of 8, + /// the number 31 has three leading zeros. + /// + /// let x: Int8 = 0b0001_1111 + /// // x == 31 + /// // x.leadingZeros == 3 + /// - SeeAlso: `BinaryInteger.trailingZeros` + var leadingZeros: Int { get } + + /// Creates an integer from its big-endian representation, changing the + /// byte order if necessary. + init(bigEndian value: Self) + + /// Creates an integer from its little-endian representation, changing the + /// byte order if necessary. + init(littleEndian value: Self) + + /// The big-endian representation of this integer. + /// + /// If necessary, the byte order of this value is reversed from the typical + /// byte order of this integer type. On a big-endian platform, for any + /// integer `x`, `x == x.bigEndian`. + /// + /// - SeeAlso: `littleEndian` + var bigEndian: Self { get } + + /// The little-endian representation of this integer. + /// + /// If necessary, the byte order of this value is reversed from the typical + /// byte order of this integer type. On a little-endian platform, for any + /// integer `x`, `x == x.littleEndian`. + /// + /// - SeeAlso: `bigEndian` + var littleEndian: Self { get } + + /// A representation of this integer with the byte order swapped. + var byteSwapped: Self { get } +} +``` + +#### Auxiliary protocols + +```Swift +public protocol UnsignedInteger : BinaryInteger { + associatedtype Magnitude : BinaryInteger +} +public protocol SignedInteger : BinaryInteger, SignedArithmetic { + associatedtype Magnitude : BinaryInteger +} +``` + +### Extra operators + +In addition to the operators described in the [protocols section](#protocols), we also provide convenient heterogeneous versions of the following operators: + +#### Heterogeneous shifts, equality, and comparison + +```Swift +extension BinaryInteger { + static func &>> (lhs: Self, rhs: Other) -> Self + static func &>>= (lhs: inout Self, rhs: Other) + static func &<< (lhs: Self, rhs: Other) -> Self + static func &<<= (lhs: inout Self, rhs: Other) + + static func >> (lhs: Self, rhs: Other) -> Self + static func >>= (lhs: inout Self, rhs: Other) + static func << (lhs: Self, rhs: Other) -> Self + static func <<= (lhs: inout Self, rhs: Other) + + static func == (lhs: Self, rhs: Other) -> Bool + static func != (lhs: Self, rhs: Other) -> Bool + + static func < (lhs: Self, rhs: Other) -> Bool + static func <= (lhs: Self, rhs: Other) -> Bool + static func > (lhs: Self, rhs: Other) -> Bool + static func >= (lhs: Self, rhs: Other) -> Bool +} +``` + +#### Masking arithmetic + +```Swift +public func &* (lhs: T, rhs: T) -> T +public func &- (lhs: T, rhs: T) -> T +public func &+ (lhs: T, rhs: T) -> T +``` + + +## Non-goals + +This proposal: + +- *DOES NOT* solve the integer promotion problem, which would allow mixed-type + arithmetic. However, we believe that it is an important step in the right + direction. + +- *DOES NOT* include the implementation of a `BigInt` type, but allows it + to be implemented in the future. + +- *DOES NOT* propose including a `DoubleWidth` integer type in the standard + library, but provides a proof-of-concept implementation. + + +## Source compatibility + +The proposed change is designed to be as non-breaking as possible, and it has been proven that it does not break code on concrete integer types. However, there are still a few API breaking changes in the realm of generic code: + +* Integer protocols in Swift up to and including version 3 were not particularly +useful for generic programming, but were rather a means of sharing +implementation between conforming types. Therefore we believe the amount of code +that relied on these protocols is relatively small. The breakage can be further +reduced by introducing proper aliases for the removed protocols with deprecation +warnings. + +* Deprecation of the `BitwiseOperations` protocol. We find it hard to imagine a type that conforms to this protocol, but is *not* a binary integer type. + +* Addition of 'smart' shifts will change the behavior of existing code. It will still compile, but will be potentially less performant due to extra logic involved. In a case, where this becomes a problem, newly introduced masking shift operators can be used instead. Unfortunately, performance characteristics of the code cannot be statically checked, and thus migration cannot be provided. + + +[se104]: 0104-improved-integers.md +[se91]: 0091-improving-operators-in-protocols.md +[impl]: https://github.com/apple/swift/tree/new-integer-protocols From 8188ff0747e25d43afbc7593688a055a6021eecf Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Wed, 21 Dec 2016 15:35:06 -0800 Subject: [PATCH 0137/4563] Editorial pass --- proposals/0000-even-more-improved-integers.md | 219 +++++++----------- 1 file changed, 81 insertions(+), 138 deletions(-) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index 46bc3bcd1a..9770ad970e 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -9,16 +9,16 @@ ## Introduction -This proposal is an evolution of the [SE-0104][se104]. The goal is still to -clean up Swifts integer APIs and make them more useful for generic programming. +This proposal is an evolution of [SE-0104][se104]. The goal is still to clean up +Swifts integer APIs and make them more useful for generic programming. The language has evolved in ways that affect integers APIs, since the time -original proposal was approved for Swift 3. Besides, we attempted to implement -the proposed model in The Standard Library and found that some essential APIs +original proposal was approved for Swift 3. We also attempted to implement +the proposed model in the standard library and found that some essential APIs were missing, whereas others could be safely removed. Major changes to the APIs introduced by this proposal (as compared to -[SE-0104][se104]) will be listed in a [dedicated section](#major-differences-introduced-by-this-proposal). +[SE-0104][se104]) are listed in a [dedicated section](#major-differences-introduced-by-this-proposal). ## Motivation @@ -53,8 +53,8 @@ Currently, bit-shifting a negative number of (or too many) bits causes a trap on some platforms, which makes low-level bit manipulations needlessly dangerous and unpredictable. -Finally, the current design predates many of the improvements that came in -Swift 2, and hasn't been revised since then. +Finally, the current design predates many of the improvements that came since +Swift 1, and hasn't been revised since then. ## Proposed solution @@ -68,23 +68,23 @@ more easily extensible. | +-------------++ +---+---------+ | ^ ^ +-------+------------+ | | -| SignedArithmetic | +-+-------+------+ -| (unary -) | | BinaryInteger | -+------+-------------+ | (words,%,...) | - ^ ++---+-----+-----+ - | +-----------^ ^ ^-----------+ - | | | | -+------+---------++ +---------+-------------+ ++------------------+ -| SignedInteger | | FixedWidthInteger | | UnsignedInteger | -| | | (bitwise,overflow,...)| | | -+---------------+-+ +-+-----------------+---+ ++------------------+ - ^ ^ ^ ^ - | | | | - | | | | - ++--------+-+ +-+-------+-+ - |Int family |-+ |UInt family|-+ - +-----------+ | +-----------+ | - +-----------+ +-----------+ +| SignedArithmetic | +-+-------+-----------+ +| (unary -) | | BinaryInteger | ++------+-------------+ |(words,%,bitwise,...)| + ^ ++---+-----+----------+ + | +-----------^ ^ ^---------------+ + | | | | ++------+---------++ +---------+---------------+ +--+----------------+ +| SignedInteger | | FixedWidthInteger | | UnsignedInteger | +| | |(endianness,overflow,...)| | | ++---------------+-+ +-+--------------------+--+ +-+-----------------+ + ^ ^ ^ ^ + | | | | + | | | | + ++--------+-+ +-+-------+-+ + |Int family |-+ |UInt family|-+ + +-----------+ | +-----------+ | + +-----------+ +-----------+ ~~~~ @@ -99,19 +99,18 @@ There are several benefits provided by this model over the old one: - It removes the overload resolution overhead. - Arithmetic and bitwise operations can now be defined as free functions - delegating work to concrete types. This approach significantly reduces the - number of overloads for those operations, which used to be defined for every - single concrete integer type. + Arithmetic and bitwise operations can now be defined as generic operators on + protocols. This approach significantly reduces the number of overloads for + those operations, which used to be defined for every single concrete integer + type. - It enables protocol sharing between integer and floating point types. Note the exclusion of the `%` operation from `Arithmetic`. Its behavior for floating point numbers is sufficiently different from the one for integers that using it in generic context would lead to confusion. The `FloatingPoint` - protocol introduced by - [SE-0067](0067-floating-point-protocols.md) - should now refine `SignedArithmetic`. + protocol introduced by [SE-0067](0067-floating-point-protocols.md) should now + refine `SignedArithmetic`. - It makes future extensions possible. @@ -122,7 +121,7 @@ There are several benefits provided by this model over the old one: methods. Together these changes can be used to provide an efficient implementation of bignums that would be hard to achieve otherwise. -The attempted implementation in The Standard Library is available +The implementation of proposed model in the standard library is available [in the new-integer-protocols branch][impl]. ### A note on bit shifts @@ -145,9 +144,9 @@ In most scenarios, the right hand operand is a literal constant, and branches for handling under- and over-shift cases can be optimized away. For other cases, this proposal provides *masking shifts*, implemented by `&>>` and `&<<`. A masking shift logically preprocesses the right hand operand by masking its -bits to produce a value in the range `0...(x-1)` where `x` is the number of -bits in the left hand operand. On most architectures this masking is already -performed by the CPU's shift instructions and has no cost. Both kinds of shift +bits to produce a value in the range `0...(x-1)` where `x` is the number of bits +in the left hand operand. On most architectures this masking is already +performed by the CPU's shift instructions and has no cost. Both kinds of shift avoid undefined behavior and produce uniform semantics across architectures. @@ -159,13 +158,14 @@ avoid undefined behavior and produce uniform semantics across architectures. * Standard Library no longer provides `+` and `-` operators for `Strideable` types. - This has proven to be a problematic, as one could have written mixed-type code - like `let x: Int64 = 42; x += (1 as Int)`, which should not be supported. - Besides, since the `Stride` of an unsigned type is signed, Standard Library - had to implement a hack to make code like `let x: UInt = 42; x += (1 as Int)` + They were problematic, as one could have written mixed-type code like `let x: + Int64 = 42; x += (1 as Int)`, which would compile, but shouldn't. Besides, + since the `Stride` of an unsigned type is signed, Standard Library had to + implement a hack to make code like `let x: UInt = 42; x += (1 as Int)` ambiguous. These operators were only necessary because they made advancing - collection indices convenient, which is no longer the case as of Swift 3 and - introduction of the [new indexing model](0065-collections-move-indices.md). + collection indices convenient, which is no longer the case since the + introduction of the [new indexing model](0065-collections-move-indices.md) in + Swift 3. * Shifts and other bitwise operations were moved from `FixedWidthInteger` to `BinaryInteger`. @@ -175,14 +175,15 @@ avoid undefined behavior and produce uniform semantics across architectures. * `trailingZeros` property was added to the `BinaryInteger` protocol. -* Endian-converting initializers and properties were added to the `FixedWidthInteger` protocol. +* Endian-converting initializers and properties were added to the +`FixedWidthInteger` protocol. ### Protocols #### `Arithmetic` -The `Arithmetic` protocol declares binary arithmetic -operators – such as `+`, `-` and `*` — and their mutating counterparts. +The `Arithmetic` protocol declares binary arithmetic operators – such as `+`, +`-`, and `*` — and their mutating counterparts. It provides a suitable basis for arithmetic on scalars such as integers and floating point numbers. @@ -255,26 +256,15 @@ public protocol Arithmetic : Equatable, ExpressibleByIntegerLiteral { /// // x == 31 /// let y: Int8 = 100 + 121 /// // Overflow error - /// - /// - Parameters: - /// - lhs: The first value to add. - /// - rhs: The second value to add. static func +(_ lhs: Self, _ rhs: Self) -> Self /// Adds the given value to this value in place. /// - /// This method serves as the basis for the in-place addition operator - /// (`+=`). For example: + /// For example: /// - /// var (x, y) = (15, 15) - /// x.add(7) - /// // x == 22 + /// var x = 15 /// y += 7 /// // y == 22 - /// - /// - Parameter rhs: The value to add to this value. - /// - /// - SeeAlso: `adding(_:)` static func +=(_ lhs: inout Self, rhs: Self) /// Returns the difference of the two given values. @@ -287,26 +277,15 @@ public protocol Arithmetic : Equatable, ExpressibleByIntegerLiteral { /// // x == 11 /// let y: UInt = 10 - 21 /// // Overflow error - /// - /// - Parameters: - /// - lhs: A numeric value. - /// - rhs: The value to subtract from `lhs`. static func -(_ lhs: Self, _ rhs: Self) -> Self /// Subtracts the given value from this value in place. /// - /// This method serves as the basis for the in-place subtraction operator - /// (`-=`). For example: + /// For example: /// - /// var (x, y) = (15, 15) - /// x.subtract(7) - /// // x == 8 + /// var x = 15 /// y -= 7 /// // y == 8 - /// - /// - Parameter rhs: The value to subtract from this value. - /// - /// - SeeAlso: `subtracting(_:)` static func -=(_ lhs: inout Self, rhs: Self) /// Returns the product of the two given values. @@ -319,26 +298,15 @@ public protocol Arithmetic : Equatable, ExpressibleByIntegerLiteral { /// // x == 50 /// let y: Int8 = 10 * 50 /// // Overflow error - /// - /// - Parameters: - /// - lhs: The first value to multiply. - /// - rhs: The second value to multiply. static func *(_ lhs: Self, _ rhs: Self) -> Self /// Multiples this value by the given value in place. /// - /// This method serves as the basis for the in-place multiplication operator - /// (`*=`). For example: + /// For example: /// - /// var (x, y) = (15, 15) - /// x.multiply(by: 7) - /// // x == 105 + /// var x = 15 /// y *= 7 /// // y == 105 - /// - /// - Parameter rhs: The value to multiply by this value. - /// - /// - SeeAlso: `multiplying(by:)` static func *=(_ lhs: inout Self, rhs: Self) /// Returns the quotient of dividing the first value by the second. @@ -347,27 +315,15 @@ public protocol Arithmetic : Equatable, ExpressibleByIntegerLiteral { /// /// let x = 21 / 5 /// // x == 4 - /// - /// - Parameters: - /// - lhs: The value to divide. - /// - rhs: The value to divide `lhs` by. `rhs` must not be zero. static func /(_ lhs: Self, _ rhs: Self) -> Self /// Divides this value by the given value in place. /// - /// This method serves as the basis for the in-place division operator - /// (`/=`). For example: + /// For example: /// - /// var (x, y) = (15, 15) - /// x.divide(by: 7) - /// // x == 2 + /// var x = 15 /// y /= 7 /// // y == 2 - /// - /// - Parameter rhs: The value to divide this value by. `rhs` must not be - /// zero. - /// - /// - SeeAlso: `dividing(by:)` static func /=(_ lhs: inout Self, rhs: Self) } @@ -424,16 +380,9 @@ extension SignedArithmetic { The `BinaryInteger` protocol is the basis for all the integer types provided by the standard library. -The `isEqual(to:)` and `isLess(than:)` methods provide implementations for -`Equatable` and `Comparable` protocol conformances. Similar to how arithmetic -operations are dispatched in `Arithmetic`, `==` and `<` operators for -homogeneous comparisons are implemented as generic free functions invoking the -`isEqual(to:)` and `isLess(than:)` protocol methods respectively. - -This protocol adds 4 new initializers. One of them allows to create integers -from floating point numbers, if the value is representable exactly, others -support construction from instances of any type conforming to `BinaryInteger`, -using different strategies: +This protocol adds a few new initializers. Two of them allow to create integers +from floating point numbers, others support construction from instances of any +type conforming to `BinaryInteger`, using different strategies: - Initialize `Self` with the value, provided that the value is representable. The precondition should be satisfied by the caller. @@ -442,6 +391,8 @@ using different strategies: - Clamp the value to the representable range of `Self` +`BinaryInteger` also declares bitwise and shift operators. + ```Swift public protocol BinaryInteger : Comparable, Hashable, Arithmetic, CustomStringConvertible, Strideable { @@ -653,13 +604,7 @@ public protocol BinaryInteger : /// /// let x: UInt8 = 5 // 0b00000101 /// let y: UInt8 = 14 // 0b00001110 - /// let z = x.bitwiseAnd(y) // 0b00000100 - /// - /// This method serves as the basis for the bitwise AND operator (`&`). - /// - /// - Parameter rhs: The value to perform the bitwise AND with this value. - /// - Returns: A new value with each bit set to `1` where this value and - /// `rhs` both have bits set to `1`. + /// let z = x & y // 0b00000100 static func &(_ lhs: Self, _ rhs: Self) -> Self static func &=(_ lhs: inout Self, _ rhs: Self) @@ -672,13 +617,7 @@ public protocol BinaryInteger : /// /// let x: UInt8 = 5 // 0b00000101 /// let y: UInt8 = 14 // 0b00001110 - /// let z = x.bitwiseOr(y) // 0b00001111 - /// - /// This method serves as the basis for the bitwise OR operator (`|`). - /// - /// - Parameter rhs: The value to perform the bitwise OR with this value. - /// - Returns: A new value with each bit set to `1` where this value, `rhs`, - /// or both have bits set to `1`. + /// let z = x | y // 0b00001111 static func |(_ lhs: Self, _ rhs: Self) -> Self static func |=(_ lhs: inout Self, _ rhs: Self) @@ -691,13 +630,7 @@ public protocol BinaryInteger : /// /// let x: UInt8 = 5 // 0b00000101 /// let y: UInt8 = 14 // 0b00001110 - /// let z = x.bitwiseXor(y) // 0b00001011 - /// - /// This method serves as the basis for the bitwise XOR operator (`^`). - /// - /// - Parameter rhs: The value to perform the bitwise XOR with this value. - /// - Returns: A new value with each bit set to `1` where either this value - /// or `rhs`, but not both, have bits set to `1`. + /// let z = x ^ y // 0b00001011 static func ^(_ lhs: Self, _ rhs: Self) -> Self static func ^=(_ lhs: inout Self, _ rhs: Self) @@ -843,8 +776,8 @@ public protocol BinaryInteger : #### `FixedWidthInteger` -The `FixedWidthInteger` protocol adds the notion of endianness, and static -properties for type bounds and size. +The `FixedWidthInteger` protocol adds the notion of endianness as well as static +properties for type bounds and bit width. The `WithOverflow` family of methods is used in default implementations of mutating arithmetic methods (see the `Arithmetic` protocol). Having these @@ -1062,28 +995,38 @@ public protocol SignedInteger : BinaryInteger, SignedArithmetic { ### Extra operators -In addition to the operators described in the [protocols section](#protocols), we also provide convenient heterogeneous versions of the following operators: +In addition to the operators described in the [protocols section](#protocols), we also provide a few extensions that are not protocol requirements: -#### Heterogeneous shifts, equality, and comparison +#### Heterogeneous shifts ```Swift extension BinaryInteger { - static func &>> (lhs: Self, rhs: Other) -> Self + // Masking shifts + static func &>> (lhs: Self, rhs: Other) -> Self static func &>>= (lhs: inout Self, rhs: Other) - static func &<< (lhs: Self, rhs: Other) -> Self + static func &<< (lhs: Self, rhs: Other) -> Self static func &<<= (lhs: inout Self, rhs: Other) - static func >> (lhs: Self, rhs: Other) -> Self + // 'Smart' shifts + static func >> (lhs: Self, rhs: Other) -> Self static func >>= (lhs: inout Self, rhs: Other) - static func << (lhs: Self, rhs: Other) -> Self + static func << (lhs: Self, rhs: Other) -> Self static func <<= (lhs: inout Self, rhs: Other) +} +``` + +#### Heterogeneous equality and comparison +```Swift +extension BinaryInteger { + // Equality static func == (lhs: Self, rhs: Other) -> Bool static func != (lhs: Self, rhs: Other) -> Bool - static func < (lhs: Self, rhs: Other) -> Bool + // Comparison + static func < (lhs: Self, rhs: Other) -> Bool static func <= (lhs: Self, rhs: Other) -> Bool - static func > (lhs: Self, rhs: Other) -> Bool + static func > (lhs: Self, rhs: Other) -> Bool static func >= (lhs: Self, rhs: Other) -> Bool } ``` From 5eb57c6eb151dc0775d204b44ec5f1a39f54eee1 Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Thu, 5 Jan 2017 15:45:08 -0800 Subject: [PATCH 0138/4563] Renaming the what's new section --- proposals/0000-even-more-improved-integers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index 9770ad970e..c3688cdcb7 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -18,7 +18,7 @@ the proposed model in the standard library and found that some essential APIs were missing, whereas others could be safely removed. Major changes to the APIs introduced by this proposal (as compared to -[SE-0104][se104]) are listed in a [dedicated section](#major-differences-introduced-by-this-proposal). +[SE-0104][se104]) are listed in a [dedicated section](#whats-new-since-se-0104). ## Motivation @@ -152,7 +152,7 @@ avoid undefined behavior and produce uniform semantics across architectures. ## Detailed design -### Major differences introduced by this proposal +### What's new since SE-0104 * [SE-0091][se91] removed the necessity to dispatch generic operators through special methods. From a1d23c60ac7a98f2e824b901d66c783a3df4c6b5 Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Thu, 5 Jan 2017 15:45:23 -0800 Subject: [PATCH 0139/4563] Mentioning DoubleWidth in what's new section --- proposals/0000-even-more-improved-integers.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index c3688cdcb7..d39bc20d52 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -178,6 +178,8 @@ avoid undefined behavior and produce uniform semantics across architectures. * Endian-converting initializers and properties were added to the `FixedWidthInteger` protocol. +* Standard library introduces the new type `DoubleWidth`. + ### Protocols #### `Arithmetic` From 6fbb9171a4c7a40b707ae88f8e8ea353a4de9237 Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Thu, 5 Jan 2017 15:45:33 -0800 Subject: [PATCH 0140/4563] Adding mention of prefix + --- proposals/0000-even-more-improved-integers.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index d39bc20d52..e0ffd6c065 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -331,6 +331,10 @@ public protocol Arithmetic : Equatable, ExpressibleByIntegerLiteral { extension Arithmetic { public init() { self = 0 } + + public static prefix func + (x: Self) -> Self { + return x + } } ``` From 94ca9345b843de32872bb7e4aa07aa8d72047c7b Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Fri, 6 Jan 2017 15:33:58 -0800 Subject: [PATCH 0141/4563] More details in what's new section --- proposals/0000-even-more-improved-integers.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index e0ffd6c065..0a57728432 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -156,6 +156,8 @@ avoid undefined behavior and produce uniform semantics across architectures. * [SE-0091][se91] removed the necessity to dispatch generic operators through special methods. + All operators are now declared by protocols as `static func`s. + * Standard Library no longer provides `+` and `-` operators for `Strideable` types. They were problematic, as one could have written mixed-type code like `let x: @@ -169,12 +171,22 @@ avoid undefined behavior and produce uniform semantics across architectures. * Shifts and other bitwise operations were moved from `FixedWidthInteger` to `BinaryInteger`. + Left shift operation on an unbounded integer should infinitely extend the + number, and never drop set bits when they reach the leftmost position in the + underlying representation. + * `BitwiseOperations` protocol was deprecated. + We believe there are no useful entities that support bitwise operations, but at + the same time are not binary integers. + * `minimumSignedRepresentationBitWidth` property was removed. * `trailingZeros` property was added to the `BinaryInteger` protocol. + `leadingZeros` and `popcount` properties are still defined by the + `FixedWidthInteger` protocol. + * Endian-converting initializers and properties were added to the `FixedWidthInteger` protocol. From 13a8d053995f7bd931fceb8c4b1aea25136f7212 Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Fri, 13 Jan 2017 12:21:12 -0800 Subject: [PATCH 0142/4563] Minor corrections --- proposals/0000-even-more-improved-integers.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index 0a57728432..ad1bed91ef 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -12,7 +12,7 @@ This proposal is an evolution of [SE-0104][se104]. The goal is still to clean up Swifts integer APIs and make them more useful for generic programming. -The language has evolved in ways that affect integers APIs, since the time +The language has evolved in ways that affect integers APIs since the time the original proposal was approved for Swift 3. We also attempted to implement the proposed model in the standard library and found that some essential APIs were missing, whereas others could be safely removed. @@ -33,7 +33,7 @@ overload set. Converting from one integer type to another is performed using the concept of the 'maximum width integer' (see `MaxInt`), which is an artificial limitation. -The very existence of `MaxInt` makes it unclear what to do, should someone +The very existence of `MaxInt` makes it unclear what to do should someone implement `Int256`, for example. Another annoying problem is the inability to use integers of different types in @@ -172,8 +172,8 @@ avoid undefined behavior and produce uniform semantics across architectures. * Shifts and other bitwise operations were moved from `FixedWidthInteger` to `BinaryInteger`. Left shift operation on an unbounded integer should infinitely extend the - number, and never drop set bits when they reach the leftmost position in the - underlying representation. + number, and never drop set bits when they reach the most significant position + in the underlying representation. * `BitwiseOperations` protocol was deprecated. From 51ef15453b0b9eea29603127734ae46138bc1109 Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Fri, 13 Jan 2017 12:24:02 -0800 Subject: [PATCH 0143/4563] Section on DoubleWidth --- proposals/0000-even-more-improved-integers.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index ad1bed91ef..8406ec51aa 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -192,6 +192,8 @@ avoid undefined behavior and produce uniform semantics across architectures. * Standard library introduces the new type `DoubleWidth`. + See [this section](#doublewidth) for more details. + ### Protocols #### `Arithmetic` @@ -1011,6 +1013,18 @@ public protocol SignedInteger : BinaryInteger, SignedArithmetic { } ``` +### DoubleWidth + +The `DoubleWidth` type allows to create wider fixed-width integer types from +the ones available in the standard library. + +Standard library currently provides fixed-width integer types of up to 64 bits. +A value of `DoubleWidth` will double the range of the underlying type and +implement all the `FixedWidthInteger` requirements. _Please note_ though that +the implementation will not necessarily be the most efficient one, so it would +not be a good idea to use `DoubleWidth` instead of a built-in `Int64`. + + ### Extra operators In addition to the operators described in the [protocols section](#protocols), we also provide a few extensions that are not protocol requirements: @@ -1069,9 +1083,6 @@ This proposal: - *DOES NOT* include the implementation of a `BigInt` type, but allows it to be implemented in the future. -- *DOES NOT* propose including a `DoubleWidth` integer type in the standard - library, but provides a proof-of-concept implementation. - ## Source compatibility From 3ef234e38dde51588fcb839529edcee4fe47b80f Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Mon, 30 Jan 2017 15:20:08 -0800 Subject: [PATCH 0144/4563] No init() on Arithmetic --- proposals/0000-even-more-improved-integers.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index 8406ec51aa..a73c7cf00f 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -344,8 +344,6 @@ public protocol Arithmetic : Equatable, ExpressibleByIntegerLiteral { } extension Arithmetic { - public init() { self = 0 } - public static prefix func + (x: Self) -> Self { return x } @@ -390,7 +388,7 @@ extension SignedArithmetic { } public mutating func negate() { - self = Self() - self + self = 0 - self } } ``` @@ -792,6 +790,10 @@ public protocol BinaryInteger : /// type. func signum() -> Self } + +extension BinaryInteger { + init() { self = 0 } +} ``` #### `FixedWidthInteger` From 0b5c511257aa01fee28ab46c6a8abc1bb7b39d5b Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Wed, 8 Feb 2017 15:06:29 -0800 Subject: [PATCH 0145/4563] var words and moving / to BinaryInteger --- proposals/0000-even-more-improved-integers.md | 63 +++++++++---------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index a73c7cf00f..77efa8183b 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -177,11 +177,16 @@ avoid undefined behavior and produce uniform semantics across architectures. * `BitwiseOperations` protocol was deprecated. - We believe there are no useful entities that support bitwise operations, but at - the same time are not binary integers. + We believe there are no useful entities that support bitwise operations, but + at the same time are not binary integers. * `minimumSignedRepresentationBitWidth` property was removed. +* `word(at:)` methods was replaced by `var words: Words` where `Words` is a new + associated type. + + This will help avoid quadratic complexity in algorithms iterating over all words, and also allow standard library to provide a default conformance to `Hashable`. + * `trailingZeros` property was added to the `BinaryInteger` protocol. `leadingZeros` and `popcount` properties are still defined by the @@ -324,23 +329,6 @@ public protocol Arithmetic : Equatable, ExpressibleByIntegerLiteral { /// y *= 7 /// // y == 105 static func *=(_ lhs: inout Self, rhs: Self) - - /// Returns the quotient of dividing the first value by the second. - /// - /// For integer types, any remainder of the division is discarded. - /// - /// let x = 21 / 5 - /// // x == 4 - static func /(_ lhs: Self, _ rhs: Self) -> Self - - /// Divides this value by the given value in place. - /// - /// For example: - /// - /// var x = 15 - /// y /= 7 - /// // y == 2 - static func /=(_ lhs: inout Self, rhs: Self) } extension Arithmetic { @@ -415,6 +403,8 @@ type conforming to `BinaryInteger`, using different strategies: public protocol BinaryInteger : Comparable, Hashable, Arithmetic, CustomStringConvertible, Strideable { + associatedtype Words : Collection // where Iterator.Element == UInt + /// A Boolean value indicating whether this type is a signed integer type. /// /// *Signed* integer types can represent both positive and negative values. @@ -535,20 +525,9 @@ public protocol BinaryInteger : /// - Parameter source: An integer to convert to this type. init(clamping source: T) - /// Returns the n-th word, counting from the least significant to most - /// significant, of this value's binary representation. - /// - /// The `word(at:)` method returns negative values in two's complement - /// representation, regardless of a type's underlying implementation. If `n` - /// is greater than the number of words in this value's current - /// representation, the result is `0` for positive numbers and `~0` for - /// negative numbers. - /// - /// - Parameter n: The word to return, counting from the least significant to - /// most significant. `n` must be greater than or equal to zero. - /// - Returns: An word-sized, unsigned integer with the bit pattern of the - /// n-th word of this value. - func word(at n: Int) -> UInt + /// The collection of words in two's complement representation of the value, + /// from the least significant to most. + var words: Words { get } /// The number of bits in the current binary representation of this value. /// @@ -566,6 +545,24 @@ public protocol BinaryInteger : /// // x.trailingZeros == 3 var trailingZeros: Int { get } + + /// Returns the quotient of dividing the first value by the second. + /// + /// For integer types, any remainder of the division is discarded. + /// + /// let x = 21 / 5 + /// // x == 4 + static func /(_ lhs: Self, _ rhs: Self) -> Self + + /// Divides this value by the given value in place. + /// + /// For example: + /// + /// var x = 15 + /// y /= 7 + /// // y == 2 + static func /=(_ lhs: inout Self, rhs: Self) + /// Returns the remainder of dividing the first value by the second. /// /// The result has the same sign as `lhs` and is less than `rhs.magnitude`. From 952422ed466695bc5c574fad84e661e660eb61be Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Wed, 8 Feb 2017 15:16:41 -0800 Subject: [PATCH 0146/4563] s/Arithmetic/Number --- proposals/0000-even-more-improved-integers.md | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index 77efa8183b..cd7aa5842f 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -26,7 +26,7 @@ Swift's integer protocols don't currently provide a suitable basis for generic programming. See [this blog post](http://blog.krzyzanowskim.com/2015/03/01/swift_madness_of_generic_integer/) for an example of an attempt to implement a generic algorithm over integers. -The way the `Arithmetic` protocol is defined, it does not generalize to floating +The way the `Number` protocol is defined, it does not generalize to floating point numbers and also slows down compilation by requiring every concrete type to provide an implementation of arithmetic operators, thus polluting the overload set. @@ -63,12 +63,12 @@ more easily extensible. ~~~~ +--------------+ +-------------+ - +------>+ Arithmetic | | Comparable | + +------>+ Number | | Comparable | | | (+,-,*,/) | | (==,<,>,...)| | +-------------++ +---+---------+ | ^ ^ +-------+------------+ | | -| SignedArithmetic | +-+-------+-----------+ +| SignedNumber | +-+-------+-----------+ | (unary -) | | BinaryInteger | +------+-------------+ |(words,%,bitwise,...)| ^ ++---+-----+----------+ @@ -106,11 +106,11 @@ There are several benefits provided by this model over the old one: - It enables protocol sharing between integer and floating point types. - Note the exclusion of the `%` operation from `Arithmetic`. Its behavior for + Note the exclusion of the `%` operation from `Number`. Its behavior for floating point numbers is sufficiently different from the one for integers that using it in generic context would lead to confusion. The `FloatingPoint` protocol introduced by [SE-0067](0067-floating-point-protocols.md) should now - refine `SignedArithmetic`. + refine `SignedNumber`. - It makes future extensions possible. @@ -180,6 +180,9 @@ avoid undefined behavior and produce uniform semantics across architectures. We believe there are no useful entities that support bitwise operations, but at the same time are not binary integers. +* `Arithmetic` and `SignedArithmetic` protocols have been renamed to `Number` + and `SignedNumber`. + * `minimumSignedRepresentationBitWidth` property was removed. * `word(at:)` methods was replaced by `var words: Words` where `Words` is a new @@ -201,9 +204,9 @@ avoid undefined behavior and produce uniform semantics across architectures. ### Protocols -#### `Arithmetic` +#### `Number` -The `Arithmetic` protocol declares binary arithmetic operators – such as `+`, +The `Number` protocol declares binary arithmetic operators – such as `+`, `-`, and `*` — and their mutating counterparts. It provides a suitable basis for arithmetic on scalars such as integers and @@ -226,7 +229,7 @@ as its argument. ```Swift -public protocol Arithmetic : Equatable, ExpressibleByIntegerLiteral { +public protocol Number : Equatable, ExpressibleByIntegerLiteral { /// Creates a new instance from the given integer, if it can be represented /// exactly. /// @@ -331,19 +334,19 @@ public protocol Arithmetic : Equatable, ExpressibleByIntegerLiteral { static func *=(_ lhs: inout Self, rhs: Self) } -extension Arithmetic { +extension Number { public static prefix func + (x: Self) -> Self { return x } } ``` -#### `SignedArithmetic` +#### `SignedNumber` -The `SignedArithmetic` protocol is for numbers that can be negated. +The `SignedNumber` protocol is for numbers that can be negated. ```Swift -public protocol SignedArithmetic : Arithmetic { +public protocol SignedNumber : Number { /// Returns the additive inverse of this value. /// /// let x = 21 @@ -368,7 +371,7 @@ public protocol SignedArithmetic : Arithmetic { mutating func negate() } -extension SignedArithmetic { +extension SignedNumber { public static prefix func - (_ operand: Self) -> Self { var result = operand result.negate() @@ -401,7 +404,7 @@ type conforming to `BinaryInteger`, using different strategies: ```Swift public protocol BinaryInteger : - Comparable, Hashable, Arithmetic, CustomStringConvertible, Strideable { + Comparable, Hashable, Number, CustomStringConvertible, Strideable { associatedtype Words : Collection // where Iterator.Element == UInt @@ -799,7 +802,7 @@ The `FixedWidthInteger` protocol adds the notion of endianness as well as static properties for type bounds and bit width. The `WithOverflow` family of methods is used in default implementations of -mutating arithmetic methods (see the `Arithmetic` protocol). Having these +mutating arithmetic methods (see the `Number` protocol). Having these methods allows the library to provide both bounds-checked and masking implementations of arithmetic operations, without duplicating code. @@ -1007,7 +1010,7 @@ public protocol FixedWidthInteger : BinaryInteger { public protocol UnsignedInteger : BinaryInteger { associatedtype Magnitude : BinaryInteger } -public protocol SignedInteger : BinaryInteger, SignedArithmetic { +public protocol SignedInteger : BinaryInteger, SignedNumber { associatedtype Magnitude : BinaryInteger } ``` From 8733dd72d08abf2b2f8c3b44ff72551631048cf6 Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Thu, 9 Feb 2017 10:15:07 -0800 Subject: [PATCH 0147/4563] Adding link to discussion and correcting protocol name. --- proposals/0000-even-more-improved-integers.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index cd7aa5842f..4aaae501f9 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -6,6 +6,7 @@ * Status: **Awaiting review** * Bug: [SR-3196](https://bugs.swift.org/browse/SR-3196) * Previous Proposal: [SE-0104][se104] +* Discussion on swift-evolution: [here](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170109/030191.html). ## Introduction @@ -26,10 +27,10 @@ Swift's integer protocols don't currently provide a suitable basis for generic programming. See [this blog post](http://blog.krzyzanowskim.com/2015/03/01/swift_madness_of_generic_integer/) for an example of an attempt to implement a generic algorithm over integers. -The way the `Number` protocol is defined, it does not generalize to floating -point numbers and also slows down compilation by requiring every concrete type -to provide an implementation of arithmetic operators, thus polluting the -overload set. +The way the `IntegerArithmetic` protocol is defined, it does not generalize to +floating point numbers and also slows down compilation by requiring every +concrete type to provide an implementation of arithmetic operators, thus +polluting the overload set. Converting from one integer type to another is performed using the concept of the 'maximum width integer' (see `MaxInt`), which is an artificial limitation. From 1367caf1aacbd5fddfe3b27fd4b95873b27dbbcd Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Fri, 10 Feb 2017 12:43:33 -0800 Subject: [PATCH 0148/4563] Renaming doubleWidthMultiply and doubleWidthDivide --- proposals/0000-even-more-improved-integers.md | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index 4aaae501f9..c2b4dff204 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -118,9 +118,10 @@ There are several benefits provided by this model over the old one: The proposed model eliminates the 'largest integer type' concept previously used to interoperate between integer types (see `toIntMax` in the current model) and instead provides access to machine words. It also introduces the - `doubleWidthMultiply`, `doubleWidthDivide`, and `quotientAndRemainder` - methods. Together these changes can be used to provide an efficient - implementation of bignums that would be hard to achieve otherwise. + `multiplied(by:_:FullWidth)`, `dividing(_:, _:FullWidth)`, and + `quotientAndRemainder` methods. Together these changes can be used to provide + an efficient implementation of bignums that would be hard to achieve + otherwise. The implementation of proposed model in the standard library is available [in the new-integer-protocols branch][impl]. @@ -807,11 +808,15 @@ mutating arithmetic methods (see the `Number` protocol). Having these methods allows the library to provide both bounds-checked and masking implementations of arithmetic operations, without duplicating code. -The `doubleWidthMultiply` and `doubleWidthDivide` methods are necessary building +The `multiplied(by:, _: FullWidth)` and `dividing(_:, _: FullWidth)` methods are necessary building blocks to implement support for integer types of a greater width such as arbitrary-precision integers. ```Swift + +public enum FullWidth { case fullWidth } +public enum ReportingOverflow { case reportingOverflow } + public protocol FixedWidthInteger : BinaryInteger { /// The number of bits used for the underlying binary representation of /// values of this type. @@ -850,7 +855,7 @@ public protocol FixedWidthInteger : BinaryInteger { /// `other`. /// /// - SeeAlso: `+` - func addingWithOverflow(_ other: Self) + func adding(_ other: Self, _: ReportingOverflow) -> (partialValue: Self, overflow: ArithmeticOverflow) /// Returns the difference of this value and the given value along with a @@ -865,7 +870,7 @@ public protocol FixedWidthInteger : BinaryInteger { /// result of `other` subtracted from this value. /// /// - SeeAlso: `-` - func subtractingWithOverflow(_ other: Self) + func subtracting(_ other: Self, _: ReportingOverflow) -> (partialValue: Self, overflow: ArithmeticOverflow) /// Returns the product of this value and the given value along with a flag @@ -879,8 +884,8 @@ public protocol FixedWidthInteger : BinaryInteger { /// occurred and the `partialValue` component contains the truncated /// product of this value and `other`. /// - /// - SeeAlso: `*`, `doubleWidthMultiply(_:_:)` - func multipliedWithOverflow(by other: Self) + /// - SeeAlso: `*`, `multiplied(by:_:FullWidth)` + func multiplied(by other: Self, _: ReportingOverflow) -> (partialValue: Self, overflow: ArithmeticOverflow) /// Returns the quotient of dividing this value by the given value along with @@ -896,23 +901,23 @@ public protocol FixedWidthInteger : BinaryInteger { /// If the `overflow` component is `.overflow`, an overflow occurred and /// the `partialValue` component contains the truncated quotient. /// - /// - SeeAlso: `/`, `doubleWidthDivide(_:_:)` - func dividedWithOverflow(by other: Self) + /// - SeeAlso: `/`, `dividing(_:_:FullWidth)` + func divided(by other: Self, _: ReportingOverflow) -> (partialValue: Self, overflow: ArithmeticOverflow) /// Returns a tuple containing the high and low parts of the result of - /// multiplying its arguments. + /// multiplying this value by an argument. /// /// Use this method to calculate the full result of a product that would /// otherwise overflow. Unlike traditional truncating multiplication, the - /// `doubleWidthMultiply(_:_:)` method returns both the `high` and `low` - /// parts of the product of `lhs` and `rhs`. The following example uses this - /// method to multiply two `UInt8` values that normally overflow when + /// `multiplied(by:_:FullWidth)` method returns both the `high` and `low` + /// parts of the product of `self` and `other`. The following example uses + /// this method to multiply two `UInt8` values that normally overflow when /// multiplied: /// /// let x: UInt8 = 100 /// let y: UInt8 = 20 - /// let result = UInt8.doubleWidthMultiply(100, 20) + /// let result = x.multiplied(by: y, .fullWidth) /// // result.high == 0b00000111 /// // result.low == 0b11010000 /// @@ -927,30 +932,27 @@ public protocol FixedWidthInteger : BinaryInteger { /// /// - Parameters: /// - lhs: A value to multiply. - /// - rhs: Another value to multiply. + /// - other: A value to multiplied `self` by. /// - Returns: A tuple containing the high and low parts of the result of - /// multiplying `lhs` and `rhs`. + /// multiplying `self` and `other`. /// - /// - SeeAlso: `multipliedWithOverflow(by:)` - static func doubleWidthMultiply(_ lhs: Self, _ rhs: Self) - -> (high: Self, low: Magnitude) + /// - SeeAlso: `multiplied(by:,_:ReportingOverflow)` + func multiplied(by other: Self, _: FullWidth) -> (high: Self, low: Magnitude) /// Returns a tuple containing the quotient and remainder of dividing the - /// first argument by the second. + /// first argument by this value. /// /// The resulting quotient must be representable within the bounds of the - /// type. If the quotient of dividing `lhs` by `rhs` is too large to + /// type. If the quotient of dividing `lhs` by `self` is too large to /// represent in the type, a runtime error may occur. /// /// - Parameters: /// - lhs: A tuple containing the high and low parts of a double-width /// integer. The `high` component of the tuple carries the sign, if the /// type is signed. - /// - rhs: The integer to divide into `lhs`. /// - Returns: A tuple containing the quotient and remainder of `lhs` divided - /// by `rhs`. - static func doubleWidthDivide( - _ lhs: (high: Self, low: Magnitude), _ rhs: Self) + /// by `self`. + func dividing(_ lhs: (high: Self, low: Magnitude), _: FullWidth) -> (quotient: Self, remainder: Self) /// The number of bits equal to 1 in this value's binary representation. From 13b78fde958f72a75217690b478215e91bbc5769 Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Fri, 10 Feb 2017 12:54:18 -0800 Subject: [PATCH 0149/4563] DoubleWidth as an enum --- proposals/0000-even-more-improved-integers.md | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index c2b4dff204..b9e04e7c9b 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -202,7 +202,7 @@ avoid undefined behavior and produce uniform semantics across architectures. * Standard library introduces the new type `DoubleWidth`. - See [this section](#doublewidth) for more details. + See [this section](#doublewidth-t) for more details. ### Protocols @@ -905,12 +905,12 @@ public protocol FixedWidthInteger : BinaryInteger { func divided(by other: Self, _: ReportingOverflow) -> (partialValue: Self, overflow: ArithmeticOverflow) - /// Returns a tuple containing the high and low parts of the result of - /// multiplying this value by an argument. + /// Returns a double-width value containing the high and low parts of the + /// result of multiplying this value by an argument. /// /// Use this method to calculate the full result of a product that would /// otherwise overflow. Unlike traditional truncating multiplication, the - /// `multiplied(by:_:FullWidth)` method returns both the `high` and `low` + /// `multiplied(by:_:FullWidth)` method returns both the high and low /// parts of the product of `self` and `other`. The following example uses /// this method to multiply two `UInt8` values that normally overflow when /// multiplied: @@ -922,7 +922,7 @@ public protocol FixedWidthInteger : BinaryInteger { /// // result.low == 0b11010000 /// /// The product of `x` and `y` is 2000, which is too large to represent in a - /// `UInt8` instance. The `high` and `low` components of the `result` tuple + /// `UInt8` instance. The `high` and `low` components of the `result` /// represent 2000 when concatenated to form a double-width integer; that /// is, using `result.high` as the high byte and `result.low` as the low byte /// of a `UInt16` instance. @@ -931,13 +931,12 @@ public protocol FixedWidthInteger : BinaryInteger { /// // z == 2000 /// /// - Parameters: - /// - lhs: A value to multiply. /// - other: A value to multiplied `self` by. /// - Returns: A tuple containing the high and low parts of the result of /// multiplying `self` and `other`. /// /// - SeeAlso: `multiplied(by:,_:ReportingOverflow)` - func multiplied(by other: Self, _: FullWidth) -> (high: Self, low: Magnitude) + func multiplied(by other: Self, _: FullWidth) -> DoubleWidth /// Returns a tuple containing the quotient and remainder of dividing the /// first argument by this value. @@ -947,12 +946,12 @@ public protocol FixedWidthInteger : BinaryInteger { /// represent in the type, a runtime error may occur. /// /// - Parameters: - /// - lhs: A tuple containing the high and low parts of a double-width + /// - lhs: A value containing the high and low parts of a double-width /// integer. The `high` component of the tuple carries the sign, if the /// type is signed. /// - Returns: A tuple containing the quotient and remainder of `lhs` divided /// by `self`. - func dividing(_ lhs: (high: Self, low: Magnitude), _: FullWidth) + func dividing(_ lhs: DoubleWidth, _: FullWidth) -> (quotient: Self, remainder: Self) /// The number of bits equal to 1 in this value's binary representation. @@ -1018,7 +1017,7 @@ public protocol SignedInteger : BinaryInteger, SignedNumber { } ``` -### DoubleWidth +### DoubleWidth\ The `DoubleWidth` type allows to create wider fixed-width integer types from the ones available in the standard library. @@ -1029,6 +1028,27 @@ implement all the `FixedWidthInteger` requirements. _Please note_ though that the implementation will not necessarily be the most efficient one, so it would not be a good idea to use `DoubleWidth` instead of a built-in `Int64`. +```swift +public enum DoubleWidth { + case .parts(high: T, low: T.Magnitude) + + public var high: T { get } + public var low: T.Magnitude { get } +} +``` + +Representing it as an `enum` instead of a simple struct allows to use it both +as a single value, as well as in destructuring matches. + +```swift +let high = doubleWidthValue.high +let low = doubleWidthValue.low + +// or + +case let (high, low) = doubleWidthValue +``` + ### Extra operators From 3bb0414efdcaa4994801bb9d0d9ff15ccd768083 Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Fri, 10 Feb 2017 12:56:41 -0800 Subject: [PATCH 0150/4563] Correct link to the DoubleWidth secion --- proposals/0000-even-more-improved-integers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index b9e04e7c9b..49248284e4 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -202,7 +202,7 @@ avoid undefined behavior and produce uniform semantics across architectures. * Standard library introduces the new type `DoubleWidth`. - See [this section](#doublewidth-t) for more details. + See [this section](#doublewidtht) for more details. ### Protocols From efce367ac481981f4849b79098049d8b32041b3f Mon Sep 17 00:00:00 2001 From: Jacob Bandes-Storch Date: Sat, 21 Jan 2017 01:36:25 -0800 Subject: [PATCH 0151/4563] [Status] Style updates for increased clarity --- index.css | 64 ++++++++++++++++++++++--------------------------------- index.js | 41 +++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/index.css b/index.css index ca99b2630c..cc996b889b 100644 --- a/index.css +++ b/index.css @@ -18,24 +18,21 @@ body { font-family: "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; font-size: 18px; + -webkit-text-size-adjust: none; line-height: 1.5; color: #333; - font-weight: 300; margin: 0; } h1 { margin-left: 10px; font-size: 54px; - font-weight: 200; - line-height: 1 } h4 { margin-bottom: 0em; font-size: 20px; - font-weight: 300; - line-height: 33px; + line-height: 1.2em; } a:link { @@ -101,6 +98,7 @@ header { #title { display: inline-block; line-height: 48px; + font-weight: 200; } #title a:link { color: #333; } @@ -194,7 +192,6 @@ section ul, section li { #proposals-count { line-height: 40px; font-size: 16px; - font-weight: 300; color: #333; padding: 4px 0px; } @@ -207,32 +204,25 @@ section ul, section li { color: #888; font-weight: 300; font-size: 20px; - width: 105px; - min-width: 105px; + margin-right: 0.7em; vertical-align: top; /* align with the proposal title */ - line-height: 33px; + line-height: 1.2em; display: inline-block; } .proposal { - color: #888; - font-size: 16px; - font-weight: 300; + font-size: 14px; display: flex; flex-direction: row; margin-bottom: 40px; } -.proposal a { - font-weight: 300; -} - .proposal-list, .proposal { max-width: none; } .proposal-header { - margin-bottom: 10px; + margin-bottom: 6px; } .proposal-details { @@ -247,6 +237,8 @@ section ul, section li { } .proposal-detail-label { + color: #888; + font-weight: 200; display: inline-block; vertical-align: top; padding-right: 6px; @@ -254,8 +246,16 @@ section ul, section li { .proposal-detail-value { display: inline-block; - font-weight: 300; - color: #666; + color: #333; +} + +.proposal-title a:link, +.proposal-title a:visited, +.proposal-title a:active, +.proposal-detail-value a:link, +.proposal-detail-value a:visited, +.proposal-detail-value a:active { + color: #333; } .authors .proposal-detail-value { @@ -279,13 +279,14 @@ section ul, section li { } .proposal-title { + font-weight: 400; margin-top: 0; display: inline-block; max-width: 650px; } .status-pill-container { - margin-top: 3px; + margin-top: -2px; } .status-pill { @@ -294,7 +295,7 @@ section ul, section li { border-radius: 4px; padding: 0px; white-space: nowrap; - font-size: 17px; + font-size: 16px; font-weight: 200; line-height: 26px; text-align: center; @@ -433,7 +434,6 @@ section ul, section li { padding: 0px 25px; position: relative; left: -20px; - font-family: "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; } ::-webkit-search-cancel-button { @@ -465,7 +465,7 @@ section ul, section li { } .header-contents { - flex-basis: 310px; + flex-basis: auto; } .nav-contents { @@ -521,21 +521,15 @@ section ul, section li { } h4 a { - font-size: 18px; - word-wrap: break-all; white-space: normal; display: inline-block; line-height: 24px; - } - - .proposal-id { - font-size: 20px; - display:inline-block; + -webkit-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; } .status-pill { - font-size: 14px; - font-weight: 300; width: inherit; max-width: inherit; min-width: inherit; @@ -549,7 +543,6 @@ section ul, section li { .proposal { flex-direction: column; - font-size: 16px; max-width: 100%; padding: 0px 10px; } @@ -560,7 +553,6 @@ section ul, section li { .proposal-detail-label, .proposal-detail-value { display: inline; - padding-right: 0px; } } @@ -568,10 +560,6 @@ section ul, section li { h1 { font-size: 54px } - - h4 { - font-size: 22px; - } } @media (max-width: 414px){ diff --git a/index.js b/index.js index b92cc86ab5..84a78b67db 100644 --- a/index.js +++ b/index.js @@ -200,8 +200,8 @@ function renderNav () { }) var expandableArea = html('div', { className: 'filter-options expandable' }, [ - html('h5', { id: 'filter-options-label'}, 'Status'), - html('ul', { className: 'filter-by-status'}) + html('h5', { id: 'filter-options-label' }, 'Status'), + html('ul', { className: 'filter-by-status' }) ]) nav.querySelector('.nav-contents').appendChild(expandableArea) @@ -218,7 +218,7 @@ function renderNav () { if (implementedCheckboxIfPresent) { // add an extra row of options to filter by language version var versionRowHeader = html('h5', { id: 'version-options-label', className: 'hidden' }, 'Language Version') - var versionRow = html('ul', { id: 'version-options', className: 'filter-by-status hidden'}) + var versionRow = html('ul', { id: 'version-options', className: 'filter-by-status hidden' }) /** Helper to give versions like 3.0.1 an okay ID to use in a DOM element. (swift-3-0-1) */ function idSafeName (name) { @@ -274,7 +274,7 @@ function renderBody () { ]), html('div', { className: 'proposal-content' }, [ html('div', { className: 'proposal-header' }, [ - html('span', { className: 'proposal-id'}, [ + html('span', { className: 'proposal-id' }, [ proposal.id ]), html('h4', { className: 'proposal-title' }, [ @@ -340,7 +340,7 @@ function renderAuthors (authors) { function renderReviewManager (reviewManager) { return html('div', { className: 'review-manager proposal-detail' }, [ html('div', { className: 'proposal-detail-label' }, 'Review Manager: '), - html('div', { className: 'proposal-detail-value'}, [ + html('div', { className: 'proposal-detail-value' }, [ reviewManager.link ? html('a', { href: reviewManager.link, target: '_blank' }, reviewManager.name) : reviewManager.name @@ -408,19 +408,32 @@ function renderReviewPeriod (status) { var start = new Date(status.start) var end = new Date(status.end) + var startMonth = start.getUTCMonth() + var endMonth = end.getUTCMonth() + + var detailNodes = [months[startMonth], ' '] + + if (startMonth === endMonth) { + detailNodes.push( + start.getUTCDate().toString(), + '–', + end.getUTCDate().toString() + ) + } else { + detailNodes.push( + start.getUTCDate().toString(), + ' – ', + months[endMonth], + ' ', + end.getUTCDate().toString() + ) + } + return html('div', { className: 'proposal-detail' }, [ html('div', { className: 'proposal-detail-label' }, [ 'Scheduled: ' ]), - html('div', { className: 'proposal-detail-value' }, [ - months[start.getUTCMonth()], - ' ', - start.getUTCDate().toString(), - ' — ', - months[end.getUTCMonth()], - ' ', - end.getUTCDate().toString() - ]) + html('div', { className: 'proposal-detail-value' }, detailNodes) ]) } From 2a13bf7c991ed9900c96d27bcf1722d6ad89e8e3 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Sun, 12 Feb 2017 18:24:45 -0800 Subject: [PATCH 0152/4563] [Status] Remove spurious template strings and use regular ones. --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 84a78b67db..82456df097 100644 --- a/index.js +++ b/index.js @@ -748,11 +748,11 @@ function updateFilterDescription (selectedStateNames) { if (swiftVersionStates.length > 0 && swiftVersionStates.length <= FILTER_DESCRIPTION_LIMIT) { selectedStateNames = selectedStateNames.filter(function (state) { return !state.match(/swift|implemented/i) }) - .concat(`Implemented (${swiftVersionStates.join(', ')})`) + .concat('Implemented (' + swiftVersionStates.join(', ') + ')') } if (selectedStateNames.length > FILTER_DESCRIPTION_LIMIT) { - container.innerText = `${stateCount} Filters` + container.innerText = stateCount + ' Filters' } else if (selectedStateNames.length === 0) { container.innerText = 'All Statuses' } else { From 1944237b16256db9281d64b0e8f7b08e599fad87 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Sun, 12 Feb 2017 18:25:25 -0800 Subject: [PATCH 0153/4563] [Status] Remove old TODO. --- index.css | 1 - 1 file changed, 1 deletion(-) diff --git a/index.css b/index.css index cc996b889b..66a4aebd19 100644 --- a/index.css +++ b/index.css @@ -448,7 +448,6 @@ section ul, section li { cursor: pointer; } -/* TODO: was: 414 */ @media (max-width: 1000px) { body { font-size: 16px; From 6cbec852d2f385a770fb160e0dcbdc6690045119 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Sun, 12 Feb 2017 18:26:27 -0800 Subject: [PATCH 0154/4563] [Status] Fix baseline of detail text labels. --- index.css | 1 - 1 file changed, 1 deletion(-) diff --git a/index.css b/index.css index 66a4aebd19..f08f684239 100644 --- a/index.css +++ b/index.css @@ -240,7 +240,6 @@ section ul, section li { color: #888; font-weight: 200; display: inline-block; - vertical-align: top; padding-right: 6px; } From 9d7e6868a404106269a08af3bd11efa521ff522c Mon Sep 17 00:00:00 2001 From: Jacob Bandes-Storch Date: Sun, 12 Feb 2017 18:34:23 -0800 Subject: [PATCH 0155/4563] [Status] Use Apple system font when available --- index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.css b/index.css index f08f684239..7c7a6d9e95 100644 --- a/index.css +++ b/index.css @@ -16,7 +16,7 @@ } body { - font-family: "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; font-size: 18px; -webkit-text-size-adjust: none; line-height: 1.5; From 7bd1213bead73db7566ae596a1eed63b9fdb6f72 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Mon, 13 Feb 2017 10:57:22 -0800 Subject: [PATCH 0156/4563] [Status] Fix baseline of detail text labels. --- index.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.css b/index.css index f08f684239..227939e339 100644 --- a/index.css +++ b/index.css @@ -241,11 +241,13 @@ section ul, section li { font-weight: 200; display: inline-block; padding-right: 6px; + vertical-align: top; } .proposal-detail-value { display: inline-block; color: #333; + padding-top: 1.4px; } .proposal-title a:link, From bb239dc3731fa130469978b72b30b784a46fadc4 Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Mon, 13 Feb 2017 11:55:11 -0800 Subject: [PATCH 0157/4563] trailingZeros -> trailingZeroBits --- proposals/0000-even-more-improved-integers.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/0000-even-more-improved-integers.md b/proposals/0000-even-more-improved-integers.md index 49248284e4..91f7802c4d 100644 --- a/proposals/0000-even-more-improved-integers.md +++ b/proposals/0000-even-more-improved-integers.md @@ -192,9 +192,9 @@ avoid undefined behavior and produce uniform semantics across architectures. This will help avoid quadratic complexity in algorithms iterating over all words, and also allow standard library to provide a default conformance to `Hashable`. -* `trailingZeros` property was added to the `BinaryInteger` protocol. +* `trailingZeroBits` property was added to the `BinaryInteger` protocol. - `leadingZeros` and `popcount` properties are still defined by the + `leadingZeroBits` and `popcount` properties are still defined by the `FixedWidthInteger` protocol. * Endian-converting initializers and properties were added to the @@ -547,8 +547,8 @@ public protocol BinaryInteger : /// /// let x = Int8(bitPattern: 0b1111_1000) /// // x == -8 - /// // x.trailingZeros == 3 - var trailingZeros: Int { get } + /// // x.trailingZeroBits == 3 + var trailingZeroBits: Int { get } /// Returns the quotient of dividing the first value by the second. @@ -971,9 +971,9 @@ public protocol FixedWidthInteger : BinaryInteger { /// /// let x: Int8 = 0b0001_1111 /// // x == 31 - /// // x.leadingZeros == 3 - /// - SeeAlso: `BinaryInteger.trailingZeros` - var leadingZeros: Int { get } + /// // x.leadingZeroBits == 3 + /// - SeeAlso: `BinaryInteger.trailingZeroBits` + var leadingZeroBits: Int { get } /// Creates an integer from its big-endian representation, changing the /// byte order if necessary. From 1323494e10e861e08ce6ae958b8796b8b3ecc27e Mon Sep 17 00:00:00 2001 From: Jacob Bandes-Storch Date: Tue, 14 Feb 2017 21:48:54 -0800 Subject: [PATCH 0158/4563] [Status] Fix search field font on iOS (#601) --- index.css | 1 + 1 file changed, 1 insertion(+) diff --git a/index.css b/index.css index 3bc83d6d9b..9fa86128b5 100644 --- a/index.css +++ b/index.css @@ -435,6 +435,7 @@ section ul, section li { padding: 0px 25px; position: relative; left: -20px; + font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; } ::-webkit-search-cancel-button { From e7ce86a96ac76b47cdfcf089e240b35e911e4d58 Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Wed, 15 Feb 2017 20:26:30 +0530 Subject: [PATCH 0159/4563] Mark SE-0146 as implemented --- proposals/0146-package-manager-product-definitions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0146-package-manager-product-definitions.md b/proposals/0146-package-manager-product-definitions.md index 3ebb797ebb..5cd0a42429 100644 --- a/proposals/0146-package-manager-product-definitions.md +++ b/proposals/0146-package-manager-product-definitions.md @@ -3,7 +3,7 @@ * Proposal: [SE-0146](0146-package-manager-product-definitions.md) * Author: [Anders Bertelrud](https://github.com/abertelrud) * Review manager: Daniel Dunbar -* Status: **Accepted** +* Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-November/000298.html) * Bug: [SR-3606](https://bugs.swift.org/browse/SR-3606) From a29cf55da6c695cc01937cb8beda62dff9844b77 Mon Sep 17 00:00:00 2001 From: Bas Broek Date: Wed, 15 Feb 2017 18:32:28 +0100 Subject: [PATCH 0160/4563] Fix SE-0033's rationale (#603) --- proposals/0033-import-objc-constants.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0033-import-objc-constants.md b/proposals/0033-import-objc-constants.md index 71e4bf0ce4..0544620da2 100644 --- a/proposals/0033-import-objc-constants.md +++ b/proposals/0033-import-objc-constants.md @@ -4,7 +4,7 @@ * Author: [Jeff Kelley](https://github.com/SlaunchaMan) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Implemented (Swift 3)** -* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-April/000097.html) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160307/011996.html) ## Introduction From 7bf62896fbb7475083902278a0883c8eeec3cfb7 Mon Sep 17 00:00:00 2001 From: wh1pch81n Date: Wed, 15 Feb 2017 12:34:38 -0500 Subject: [PATCH 0161/4563] adding shield images (#579) --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7fd7ba9bb5..9088d44534 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # Swift Programming Language Evolution +[![Swift](https://img.shields.io/badge/Swift%204%20stage%201-Open%20to%20requests-brightgreen.svg)](#swift_stage) + **Before you initiate a pull request**, please read the process document. Ideas should be thoroughly discussed on the [swift-evolution mailing list](https://swift.org/community/#swift-evolution) first. @@ -19,7 +21,7 @@ Goals for past versions are included at the bottom of the document for historical purposes, but are not necessarily indicative of the features shipped. The release notes for each shipped version are the definitive list of notable changes in each release. - + ## Development major version: Swift 4.0 Expected release date: Late 2017 From 78f3a6070a3fbfeccc83ac934dd8b1666b127fc0 Mon Sep 17 00:00:00 2001 From: Anders Bertelrud Date: Wed, 15 Feb 2017 09:38:07 -0800 Subject: [PATCH 0162/4563] Mark SE-0151 and SE-0152 as accepted. --- ...151-package-manager-swift-language-compatibility-version.md | 2 +- proposals/0152-package-manager-tools-version.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0151-package-manager-swift-language-compatibility-version.md b/proposals/0151-package-manager-swift-language-compatibility-version.md index 8b5c8169e5..de28611162 100644 --- a/proposals/0151-package-manager-swift-language-compatibility-version.md +++ b/proposals/0151-package-manager-swift-language-compatibility-version.md @@ -3,7 +3,7 @@ * Proposal: [SE-0151](0151-package-manager-swift-language-compatibility-version.md) * Author: [Daniel Dunbar](https://github.com/ddunbar), [Rick Ballard](http://github.com/rballard) * Review Manager: Anders Bertelrud -* Status: **Active review (February 7...February 13, 2017)** +* Status: **Accepted** ## Introduction diff --git a/proposals/0152-package-manager-tools-version.md b/proposals/0152-package-manager-tools-version.md index b0063fc485..31b1b18d8e 100644 --- a/proposals/0152-package-manager-tools-version.md +++ b/proposals/0152-package-manager-tools-version.md @@ -1,8 +1,9 @@ # Package Manager Tools Version + * Proposal: [SE-0152](0152-package-manager-tools-version.md) * Author: [Rick Ballard](https://github.com/rballard) * Review Manager: Anders Bertelrud -* Status: **Active review (February 7...February 13, 2017)** +* Status: **Accepted** ## Introduction From 29343fb6aa66889d0964f470ccfab0e14f448eca Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Wed, 15 Feb 2017 23:19:35 +0530 Subject: [PATCH 0163/4563] Mark 151, 152 as implemented (#605) --- ...0151-package-manager-swift-language-compatibility-version.md | 2 +- proposals/0152-package-manager-tools-version.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0151-package-manager-swift-language-compatibility-version.md b/proposals/0151-package-manager-swift-language-compatibility-version.md index de28611162..6738bb51bd 100644 --- a/proposals/0151-package-manager-swift-language-compatibility-version.md +++ b/proposals/0151-package-manager-swift-language-compatibility-version.md @@ -3,7 +3,7 @@ * Proposal: [SE-0151](0151-package-manager-swift-language-compatibility-version.md) * Author: [Daniel Dunbar](https://github.com/ddunbar), [Rick Ballard](http://github.com/rballard) * Review Manager: Anders Bertelrud -* Status: **Accepted** +* Status: **Implemented (Swift 3.1)** ## Introduction diff --git a/proposals/0152-package-manager-tools-version.md b/proposals/0152-package-manager-tools-version.md index 31b1b18d8e..c1e77480c8 100644 --- a/proposals/0152-package-manager-tools-version.md +++ b/proposals/0152-package-manager-tools-version.md @@ -3,7 +3,7 @@ * Proposal: [SE-0152](0152-package-manager-tools-version.md) * Author: [Rick Ballard](https://github.com/rballard) * Review Manager: Anders Bertelrud -* Status: **Accepted** +* Status: **Implemented (Swift 3.1)** ## Introduction From 76171a826f3b71aea8b08981587f6380eb6a2a8c Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Thu, 16 Feb 2017 01:57:51 +0530 Subject: [PATCH 0164/4563] Add JIRAs for 151, 152 --- ...0151-package-manager-swift-language-compatibility-version.md | 2 ++ proposals/0152-package-manager-tools-version.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/proposals/0151-package-manager-swift-language-compatibility-version.md b/proposals/0151-package-manager-swift-language-compatibility-version.md index 6738bb51bd..9da02d0739 100644 --- a/proposals/0151-package-manager-swift-language-compatibility-version.md +++ b/proposals/0151-package-manager-swift-language-compatibility-version.md @@ -5,6 +5,8 @@ * Review Manager: Anders Bertelrud * Status: **Implemented (Swift 3.1)** +* Bugs: [SR-3964](https://bugs.swift.org/browse/SR-3964) + ## Introduction This proposal adds support for the Swift compiler's new "language compatibility diff --git a/proposals/0152-package-manager-tools-version.md b/proposals/0152-package-manager-tools-version.md index c1e77480c8..62eb4a6fd5 100644 --- a/proposals/0152-package-manager-tools-version.md +++ b/proposals/0152-package-manager-tools-version.md @@ -5,6 +5,8 @@ * Review Manager: Anders Bertelrud * Status: **Implemented (Swift 3.1)** +* Bugs: [SR-3965](https://bugs.swift.org/browse/SR-3965) + ## Introduction This proposal introduces a "Swift tools version" which is declared for each Swift package. From 710ef65b9fcbc40c29d8e595a46c977be5c5616a Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Thu, 16 Feb 2017 16:30:15 -0800 Subject: [PATCH 0165/4563] Update shield image to Swift 4, stage 2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9088d44534..8c6a8ab8da 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Swift Programming Language Evolution -[![Swift](https://img.shields.io/badge/Swift%204%20stage%201-Open%20to%20requests-brightgreen.svg)](#swift_stage) +[![Swift](https://img.shields.io/badge/Swift%204%20stage%202-Open%20to%20requests-brightgreen.svg)](#swift_stage) **Before you initiate a pull request**, please read the process document. Ideas should be thoroughly discussed on the [swift-evolution mailing list](https://swift.org/community/#swift-evolution) first. From ed6695b25f29502ddff0227f6101645f26b0d8ca Mon Sep 17 00:00:00 2001 From: Torin Kwok Date: Sat, 18 Feb 2017 06:44:13 +0800 Subject: [PATCH 0166/4563] [Proposal] Compensate for the inconsistency of @NSCopying's behaviour (#595) * Create NNNN-compensate-for-the-inconsistency-of-nsopyings-behaviour.md * Update NNNN-compensate-for-the-inconsistency-of-nsopyings-behaviour.md * Appending "Alternatives Considered" section * Update NNNN-compensate-for-the-inconsistency-of-nsopyings-behaviour.md * Update NNNN-compensate-for-the-inconsistency-of-nsopyings-behaviour.md * Update * Fixed with a more suitable verb *arise* -> *emit* --- ...he-inconsistency-of-nsopyings-behaviour.md | 227 ++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 proposals/NNNN-compensate-for-the-inconsistency-of-nsopyings-behaviour.md diff --git a/proposals/NNNN-compensate-for-the-inconsistency-of-nsopyings-behaviour.md b/proposals/NNNN-compensate-for-the-inconsistency-of-nsopyings-behaviour.md new file mode 100644 index 0000000000..a96d06436b --- /dev/null +++ b/proposals/NNNN-compensate-for-the-inconsistency-of-nsopyings-behaviour.md @@ -0,0 +1,227 @@ +# Compensate for the inconsistency of `@NSCopying`'s behaviour + +* Proposal: SE-NNNN +* Authors: [Torin Kwok](https://github.com/TorinKwok) +* Review Manager: TBD +* Status: **Awaiting review** + + + +## Introduction + +First of all, in Swift, the Objective-C `copy` property attribute translates to `@NSCopying`. + +Like Objective-C, in Swift, avoiding accessing ivar via setter methods in initializer is considered as the best pratice. Unlike Objective-C, which gives developers the freedom to decide on whether assign a value to a property by invoking setter or by accessing ivar directly, accessing a property in Swift from within an initializer always does direct access to the storage rather than going through the setter, even if using `dot` syntax. + +However, as a side-effect, `@NSCopying` attribute does not work as consistently as we usually expected in Swift initializers after developers declared a property as `@NSCopying`. + +This proposal is intent on proposing several solutions to this inconsistency. + +## Swift-evolution thread + +- [@NSCopying currently does not affect initializers (from *The Week Of Monday 23 January 2017 Archive*)](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170123/031049.html) +- [@NSCopying currently does not affect initializers (from *The Week Of Monday 30 January 2017 Archive*)](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170130/031162.html) + +## Motivation + +Here's an example of the inconsistency mentioned above: + +```swift +class Person: NSObject, NSCopying { + + var firstName: String + var lastName: String + var job: String? + + init( firstName: String, lastName: String, job: String? = nil ) { + self.firstName = firstName + self.lastName = lastName + self.job = job + + super.init() + } + + /// Conformance to protocol + func copy( with zone: NSZone? = nil ) -> Any { + let theCopy = Person.init( firstName: firstName, lastName: lastName ) + theCopy.job = job + + return theCopy + } + + /// For convenience of debugging + override var description: String { + return "\(firstName) \(lastName)" + ( job != nil ? ", \(job!)" : "" ) + } + + } +``` + +`Person` class has promised that it conforms to `` protocol. + +``` swift +let johnAppleseed = Person( firstName: "John", lastName: "Appleseed", job: "CEO" ) +var refJohnAppleseed = johnAppleseed // assigning wihtout copying semantic + +refJohnAppleseed.job = "Engineer" + +// `cloneJohnAppleseed` and `johnAppleseed` have the identical `job` ... + +print( refJohnAppleseed ) // Prints "John Appleseed, Engineer" +print( johnAppleseed ) // Prints "John Appleseed, Engineer" too + +// ... and the assertion **would not** fail: +assert( refJohnAppleseed === johnAppleseed ) + +// Assigning a copy of johnAppleseed to clonedJohnAppleseed, +// which was returned by `copy( zone: ) -> Any` +var clonedJohnAppleseed = johnAppleseed/* refJohnAppleseed is also okay */.copy() as! Person + +clonedJohnAppleseed.job = "Designer" +print( clonedJohnAppleseed ) // Prints "John Appleseed, Designer" +print( johnAppleseed ) // Prints "John Appleseed, Engineer" +// Alright as what you see, setting the job of `clonedJohnAppleseed` doesn't affect the +// job stored in `johnAppleseed`. +``` + +Up to now, everything seems to run right. However, problem will soon emerge once we begin introducing a new class consuming instances of `Person` class: + +``` swift +class Department: NSObject { + + // Here, we're expecting that `self.employee` would automatically + // store the deeply-copied instance of `Person` class + @NSCopying var employee: Person + + init( employee candidate: Person ) { + + // CAUTION! That's the key point: + // `self.employee` has been marked with `@NSCopying` attribute + // but what would take place here is only the shallow-copying. + // + // In the other words, `self.employee` will share identical underlying + // object with `candidate`. + self.employee = candidate + super.init() + + // Assertion will definitely fail since Swift do not actually + // copy the value assigned to this property even though + // `self.employee` has been marked as `@NSCoyping`: + + /* assert( self.employee !== employee ) */ + } + + override var description: String { + return "A Department: [ ( \(employee) ) ]" + } + + } +``` + +`Department`'s disignated initializer receives an external instance of `Person` and expects to assign its deeply-copied value to `self.employee` property. + +``` swift +let isaacNewton = Person( firstName: "Isaac", lastName: "Newton", job: "Mathematician" ) +let lab = Department.init( employee: isaacNewton ) + +isaacNewton.job = "Astronomer" + +print( isaacNewton ) // Prints "Isaac Newton, Astronomer" + +print( lab.employee ) // Prints "Isaac Newton, Astronomer" +// Expected output printed here is "Isaac Newton, Mathematician" instead +``` + +Setting the job of `isaacNewton` affects the job stored in `lab.employee`. That's an unexpected behavior as we have declared `employee` property as `@NSCopying`. Obviously, `@NSCopying` semantic became effectless implicitly in the initializer of `Department` class. + +For the moment, if we indeed require copy we have to invoke `copy()` method explictly on instances that wanna be copied to make sure that classes' properties are able to store deeply-copied results during the initialization: + +``` swift +init( employee candidate: Person ) { + // ... + self.employee = candidate.copy() as! Person + // ... + } +``` + +The reason why it is considered *inconsistency* is that `@NSCopying` contract will be well respected within the rest of class definition: + +``` swift +lab.employee = isaacNewton +isaacNewton.job = "Physicist" + +print( isaacNewton ) // Prints "Isaac Newton, Physicist" +print( lab.employee ) // Prints "Isaac Newton, Astronomer" +``` + +It is undeniably reasonable to enfoce programmers to access instance variables directly from initializer methods because of the potential troubles made by setter methods' additional side-effects when the initialization is not complete yet. However, I believe we at least should be warned by Swift compiler when we assigned an instance of `NSCopying` conforming class to a class's property declared as `@NSCopying` during the initialization. + +In Objective-C, developers can make decision on this process explicitly by writting done either: + +```objc +- ( instancetype )initWithName: ( NSString* )name { + // ... + self->_name = [ name copy ]; + // ... + } +``` + +or: + +```objc +- ( instancetype )initWithName: ( NSString* )name { + // ... + self.name = name; /* self.name has been qualified with @property ( copy ) */ + // ... + } +``` + +Speaking of Swift, however, there is no stuff like `->` operator to access ivar directly. As a result, with property marked with `@NSCopying` attribute, developers who are new to this language, expecially those who have had experience of writting Objective-C, are likely to automatically suppose it acts normally when they're writing down code like `self.employee = candidate` in initializer. That's bug-prone. + +## Proposed solution + +### Compiler magic + +Do the compiler magic to call `copy( with: )` in the initializer so that `@NSCopying` attribute no longer subjects to the fact that setter methods would not be invoked in initializers. **Copying should always take place after a property has been declared as `@NSCopying`**. It seems like the most direct way to maintain the `@NSCopying` contract without changing the underlying direct-storage model. + +### Compile-time checking + +Have compiler emit a **compile-time error or warning** if developers are performing an assignment operation from within an initializer between a property declared as `@NSCopying` and an instance of a `` protocol conforming class. Also, speaking of GUI integrated development environments such as Xcode, leaving this kind of error or warning **FIXABLE** would be needed in order to make them can be quickly fixed by both IDEs and migrator tools through simply appending `.copy() as! AutoInferredClassType`. + +With the adjustment mentioned above, following code fragment, for instance, will no longer be successfully compiled: + +``` swift +... +class Person: NSObject, NSCopying { /* ... */ } +... +@NSCopying var employee: Person +... +init( employee candidate: Person ) { + // ... + self.employee = candidate + // ... + } +``` + +GUI IDE will be expected to leave developers a fixable error or warning, and thus if we hit the either red or yelloe point in Xcode, or something similar to those in other IDEs, they will automatically append the lacked statement: + +> self.employee = candidate***.copy() as! Person*** + +Inferring `AutoInferredClassType` from context should be the responsibility of compiler. + +## Source compatibility + +Projects written with prior versions of Swift that have not yet adopted this proposal may fail to be built due to the compile-time error. But overall, it will be easy to be resolved. IDEs' Fix-it and auto migrator tools will deal with all works painlessly. + +## Effect on ABI stability + +The proposal doesn't change the ABI of existing language features. + +## Alternatives Considered + +There is no alternatives considered at this time. From 2f27ccb8520a12aede2e3861e7a8fdf9ffd4800e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 17 Feb 2017 15:17:12 -0800 Subject: [PATCH 0167/4563] Initiate review of SE-0153 "Compensate for the inconsistency of 's behaviour" --- ...ensate-for-the-inconsistency-of-nscopyings-behaviour.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-compensate-for-the-inconsistency-of-nsopyings-behaviour.md => 0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md} (98%) diff --git a/proposals/NNNN-compensate-for-the-inconsistency-of-nsopyings-behaviour.md b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md similarity index 98% rename from proposals/NNNN-compensate-for-the-inconsistency-of-nsopyings-behaviour.md rename to proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md index a96d06436b..1577360d62 100644 --- a/proposals/NNNN-compensate-for-the-inconsistency-of-nsopyings-behaviour.md +++ b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md @@ -1,9 +1,9 @@ # Compensate for the inconsistency of `@NSCopying`'s behaviour -* Proposal: SE-NNNN +* Proposal: SE-0153 * Authors: [Torin Kwok](https://github.com/TorinKwok) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (February 17...22, 2017)** + Number | | Comparable | - | | (+,-,*,/) | | (==,<,>,...)| - | +-------------++ +---+---------+ - | ^ ^ -+-------+------------+ | | -| SignedNumber | +-+-------+-----------+ -| (unary -) | | BinaryInteger | -+------+-------------+ |(words,%,bitwise,...)| - ^ ++---+-----+----------+ - | +-----------^ ^ ^---------------+ - | | | | -+------+---------++ +---------+---------------+ +--+----------------+ -| SignedInteger | | FixedWidthInteger | | UnsignedInteger | -| | |(endianness,overflow,...)| | | -+---------------+-+ +-+--------------------+--+ +-+-----------------+ - ^ ^ ^ ^ - | | | | - | | | | - ++--------+-+ +-+-------+-+ - |Int family |-+ |UInt family|-+ - +-----------+ | +-----------+ | - +-----------+ +-----------+ -~~~~ - - -There are several benefits provided by this model over the old one: - -- It allows mixing integer types in generic functions. - - The possibility to initialize instances of any concrete integer type with - values of any other concrete integer type enables writing functions that - operate on more than one type conforming to `BinaryInteger`, such as - heterogeneous comparisons or bit shifts, described later. - -- It removes the overload resolution overhead. - - Arithmetic and bitwise operations can now be defined as generic operators on - protocols. This approach significantly reduces the number of overloads for - those operations, which used to be defined for every single concrete integer - type. - -- It enables protocol sharing between integer and floating point types. - - Note the exclusion of the `%` operation from `Number`. Its behavior for - floating point numbers is sufficiently different from the one for integers - that using it in generic context would lead to confusion. The `FloatingPoint` - protocol introduced by [SE-0067](0067-floating-point-protocols.md) should now - refine `SignedNumber`. - -- It makes future extensions possible. - - The proposed model eliminates the 'largest integer type' concept previously - used to interoperate between integer types (see `toIntMax` in the current - model) and instead provides access to machine words. It also introduces the - `multiplied(by:_:FullWidth)`, `dividing(_:, _:FullWidth)`, and - `quotientAndRemainder` methods. Together these changes can be used to provide - an efficient implementation of bignums that would be hard to achieve - otherwise. - -The implementation of proposed model in the standard library is available -[in the new-integer-protocols branch][impl]. - -### A note on bit shifts - -This proposal introduces the concepts of *smart shifts* and *masking shifts*. - -The semantics of shift operations are -[often undefined](http://llvm.org/docs/LangRef.html#bitwise-binary-operations) -in under- or over-shift cases. *Smart shifts*, implemented by `>>` and `<<`, -are designed to address this problem and always behave in a well defined way, -as shown in the examples below: - -- `x << -2` is equivalent to `x >> 2` - -- `(1 as UInt8) >> 42)` will evaluate to `0` - -- `(-128 as Int8) >> 42)` will evaluate to `0xff` or `-1` - -In most scenarios, the right hand operand is a literal constant, and branches -for handling under- and over-shift cases can be optimized away. For other -cases, this proposal provides *masking shifts*, implemented by `&>>` and `&<<`. -A masking shift logically preprocesses the right hand operand by masking its -bits to produce a value in the range `0...(x-1)` where `x` is the number of bits -in the left hand operand. On most architectures this masking is already -performed by the CPU's shift instructions and has no cost. Both kinds of shift -avoid undefined behavior and produce uniform semantics across architectures. - - -## Detailed design - -### What's new since SE-0104 - -* [SE-0091][se91] removed the necessity to dispatch generic operators through special methods. - - All operators are now declared by protocols as `static func`s. - -* Standard Library no longer provides `+` and `-` operators for `Strideable` types. - - They were problematic, as one could have written mixed-type code like `let x: - Int64 = 42; x += (1 as Int)`, which would compile, but shouldn't. Besides, - since the `Stride` of an unsigned type is signed, Standard Library had to - implement a hack to make code like `let x: UInt = 42; x += (1 as Int)` - ambiguous. These operators were only necessary because they made advancing - collection indices convenient, which is no longer the case since the - introduction of the [new indexing model](0065-collections-move-indices.md) in - Swift 3. - -* Shifts and other bitwise operations were moved from `FixedWidthInteger` to `BinaryInteger`. - - Left shift operation on an unbounded integer should infinitely extend the - number, and never drop set bits when they reach the most significant position - in the underlying representation. - -* `BitwiseOperations` protocol was deprecated. - - We believe there are no useful entities that support bitwise operations, but - at the same time are not binary integers. - -* `Arithmetic` and `SignedArithmetic` protocols have been renamed to `Number` - and `SignedNumber`. - -* `minimumSignedRepresentationBitWidth` property was removed. - -* `word(at:)` methods was replaced by `var words: Words` where `Words` is a new - associated type. - - This will help avoid quadratic complexity in algorithms iterating over all words, and also allow standard library to provide a default conformance to `Hashable`. - -* `trailingZeroBits` property was added to the `BinaryInteger` protocol. - - `leadingZeroBits` and `popcount` properties are still defined by the - `FixedWidthInteger` protocol. - -* Endian-converting initializers and properties were added to the -`FixedWidthInteger` protocol. - -* Standard library introduces the new type `DoubleWidth`. - - See [this section](#doublewidtht) for more details. - -### Protocols - -#### `Number` - -The `Number` protocol declares binary arithmetic operators – such as `+`, -`-`, and `*` — and their mutating counterparts. - -It provides a suitable basis for arithmetic on scalars such as integers and -floating point numbers. - -Both mutating and non-mutating operations are declared in the protocol, however -only the mutating ones are required, as default implementations of the -non-mutating ones are provided by a protocol extension. - -The `Magnitude` associated type is able to hold the absolute value of any -possible value of `Self`. Concrete types do not have to provide a type alias for -it, as it can be inferred from the `magnitude` property. This property can -be useful in operations that are simpler to implement in terms of unsigned -values, for example, printing a value of an integer, which is just printing a -'-' character in front of an absolute value. - -Please note that for ordinary work, the `magnitude` property **should not** -be preferred to the `abs(_)` function, whose return value is of the same type -as its argument. - - -```Swift -public protocol Number : Equatable, ExpressibleByIntegerLiteral { - /// Creates a new instance from the given integer, if it can be represented - /// exactly. - /// - /// If the value passed as `source` is not representable exactly, the result - /// is `nil`. In the following example, the constant `x` is successfully - /// created from a value of `100`, while the attempt to initialize the - /// constant `y` from `1_000` fails because the `Int8` type can represent - /// `127` at maximum: - /// - /// let x = Int8(exactly: 100) - /// // x == Optional(100) - /// let y = Int8(exactly: 1_000) - /// // y == nil - /// - /// - Parameter source: A floating-point value to convert to an integer. - init?(exactly source: T) - - /// A type that can represent the absolute value of any possible value of the - /// conforming type. - associatedtype Magnitude : Equatable, ExpressibleByIntegerLiteral - - /// The magnitude of this value. - /// - /// For any numeric value `x`, `x.magnitude` is the absolute value of `x`. - /// You can use the `magnitude` property in operations that are simpler to - /// implement in terms of unsigned values, such as printing the value of an - /// integer, which is just printing a '-' character in front of an absolute - /// value. - /// - /// let x = -200 - /// // x.magnitude == 200 - /// - /// The global `abs(_:)` function provides more familiar syntax when you need - /// to find an absolute value. In addition, because `abs(_:)` always returns - /// a value of the same type, even in a generic context, using the function - /// instead of the `magnitude` property is encouraged. - /// - /// - SeeAlso: `abs(_:)` - var magnitude: Magnitude { get } - - /// Returns the sum of the two given values. - /// - /// The sum of `lhs` and `rhs` must be representable in the same type. In the - /// following example, the result of `100 + 200` is greater than the maximum - /// representable `Int8` value: - /// - /// let x: Int8 = 10 + 21 - /// // x == 31 - /// let y: Int8 = 100 + 121 - /// // Overflow error - static func +(_ lhs: Self, _ rhs: Self) -> Self - - /// Adds the given value to this value in place. - /// - /// For example: - /// - /// var x = 15 - /// y += 7 - /// // y == 22 - static func +=(_ lhs: inout Self, rhs: Self) - - /// Returns the difference of the two given values. - /// - /// The difference of `lhs` and `rhs` must be representable in the same type. - /// In the following example, the result of `10 - 21` is less than zero, the - /// minimum representable `UInt` value: - /// - /// let x: UInt = 21 - 10 - /// // x == 11 - /// let y: UInt = 10 - 21 - /// // Overflow error - static func -(_ lhs: Self, _ rhs: Self) -> Self - - /// Subtracts the given value from this value in place. - /// - /// For example: - /// - /// var x = 15 - /// y -= 7 - /// // y == 8 - static func -=(_ lhs: inout Self, rhs: Self) - - /// Returns the product of the two given values. - /// - /// The product of `lhs` and `rhs` must be representable in the same type. In - /// the following example, the result of `10 * 50` is greater than the - /// maximum representable `Int8` value. - /// - /// let x: Int8 = 10 * 5 - /// // x == 50 - /// let y: Int8 = 10 * 50 - /// // Overflow error - static func *(_ lhs: Self, _ rhs: Self) -> Self - - /// Multiples this value by the given value in place. - /// - /// For example: - /// - /// var x = 15 - /// y *= 7 - /// // y == 105 - static func *=(_ lhs: inout Self, rhs: Self) -} - -extension Number { - public static prefix func + (x: Self) -> Self { - return x - } -} -``` - -#### `SignedNumber` - -The `SignedNumber` protocol is for numbers that can be negated. - -```Swift -public protocol SignedNumber : Number { - /// Returns the additive inverse of this value. - /// - /// let x = 21 - /// let y = -x - /// // y == -21 - /// - /// - Returns: The additive inverse of this value. - /// - /// - SeeAlso: `negate()` - static prefix func - (_ operand: Self) -> Self - - /// Replaces this value with its additive inverse. - /// - /// The following example uses the `negate()` method to negate the value of - /// an integer `x`: - /// - /// var x = 21 - /// x.negate() - /// // x == -21 - /// - /// - SeeAlso: The unary minus operator (`-`). - mutating func negate() -} - -extension SignedNumber { - public static prefix func - (_ operand: Self) -> Self { - var result = operand - result.negate() - return result - } - - public mutating func negate() { - self = 0 - self - } -} -``` - -#### `BinaryInteger` - -The `BinaryInteger` protocol is the basis for all the integer types provided by -the standard library. - -This protocol adds a few new initializers. Two of them allow to create integers -from floating point numbers, others support construction from instances of any -type conforming to `BinaryInteger`, using different strategies: - - - Initialize `Self` with the value, provided that the value is representable. - The precondition should be satisfied by the caller. - - - Extend or truncate the value to fit into `Self` - - - Clamp the value to the representable range of `Self` - -`BinaryInteger` also declares bitwise and shift operators. - -```Swift -public protocol BinaryInteger : - Comparable, Hashable, Number, CustomStringConvertible, Strideable { - - associatedtype Words : Collection // where Iterator.Element == UInt - - /// A Boolean value indicating whether this type is a signed integer type. - /// - /// *Signed* integer types can represent both positive and negative values. - /// *Unsigned* integer types can represent only nonnegative values. - static var isSigned: Bool { get } - - /// Creates an integer from the given floating-point value, if it can be - /// represented exactly. - /// - /// If the value passed as `source` is not representable exactly, the result - /// is `nil`. In the following example, the constant `x` is successfully - /// created from a value of `21.0`, while the attempt to initialize the - /// constant `y` from `21.5` fails: - /// - /// let x = Int(exactly: 21.0) - /// // x == Optional(21) - /// let y = Int(exactly: 21.5) - /// // y == nil - /// - /// - Parameter source: A floating-point value to convert to an integer. - init?(exactly source: T) - - /// Creates an integer from the given floating-point value, truncating any - /// fractional part. - /// - /// Truncating the fractional part of `source` is equivalent to rounding - /// toward zero. - /// - /// let x = Int(21.5) - /// // x == 21 - /// let y = Int(-21.5) - /// // y == -21 - /// - /// If `source` is outside the bounds of this type after truncation, a - /// runtime error may occur. - /// - /// let z = UInt(-21.5) - /// // Error: ...the result would be less than UInt.min - /// - /// - Parameter source: A floating-point value to convert to an integer. - /// `source` must be representable in this type after truncation. - init(_ source: T) - - /// Creates an new instance from the given integer. - /// - /// If the value passed as `source` is not representable in this type, a - /// runtime error may occur. - /// - /// let x = -500 as Int - /// let y = Int32(x) - /// // y == -500 - /// - /// // -500 is not representable as a 'UInt32' instance - /// let z = UInt32(x) - /// // Error - /// - /// - Parameter source: An integer to convert. `source` must be representable - /// in this type. - init(_ source: T) - - /// Creates a new instance from the bit pattern of the given instance by - /// sign-extending or truncating to fit this type. - /// - /// When the bit width of `T` (the type of `source`) is equal to or greater - /// than this type's bit width, the result is the truncated - /// least-significant bits of `source`. For example, when converting a - /// 16-bit value to an 8-bit type, only the lower 8 bits of `source` are - /// used. - /// - /// let p: Int16 = -500 - /// // 'p' has a binary representation of 11111110_00001100 - /// let q = Int8(extendingOrTruncating: p) - /// // q == 12 - /// // 'q' has a binary representation of 00001100 - /// - /// When the bit width of `T` is less than this type's bit width, the result - /// is *sign-extended* to fill the remaining bits. That is, if `source` is - /// negative, the result is padded with ones; otherwise, the result is - /// padded with zeros. - /// - /// let u: Int8 = 21 - /// // 'u' has a binary representation of 00010101 - /// let v = Int16(extendingOrTruncating: u) - /// // v == 21 - /// // 'v' has a binary representation of 00000000_00010101 - /// - /// let w: Int8 = -21 - /// // 'w' has a binary representation of 11101011 - /// let x = Int16(extendingOrTruncating: w) - /// // x == -21 - /// // 'x' has a binary representation of 11111111_11101011 - /// let y = UInt16(extendingOrTruncating: w) - /// // y == 65515 - /// // 'y' has a binary representation of 11111111_11101011 - /// - /// - Parameter source: An integer to convert to this type. - init(extendingOrTruncating source: T) - - /// Creates a new instance with the representable value that's closest to the - /// given integer. - /// - /// If the value passed as `source` is greater than the maximum representable - /// value in this type, the result is the type's `max` value. If `source` is - /// less than the smallest representable value in this type, the result is - /// the type's `min` value. - /// - /// In this example, `x` is initialized as an `Int8` instance by clamping - /// `500` to the range `-128...127`, and `y` is initialized as a `UInt` - /// instance by clamping `-500` to the range `0...UInt.max`. - /// - /// let x = Int8(clamping: 500) - /// // x == 127 - /// // x == Int8.max - /// - /// let y = UInt(clamping: -500) - /// // y == 0 - /// - /// - Parameter source: An integer to convert to this type. - init(clamping source: T) - - /// The collection of words in two's complement representation of the value, - /// from the least significant to most. - var words: Words { get } - - /// The number of bits in the current binary representation of this value. - /// - /// This property is a constant for instances of fixed-width integer - /// types. - var bitWidth : Int { get } - - /// The number of trailing zeros in this value's binary representation. - /// - /// For example, in a fixed-width integer type with a `bitWidth` value of 8, - /// the number -8 has three trailing zeros. - /// - /// let x = Int8(bitPattern: 0b1111_1000) - /// // x == -8 - /// // x.trailingZeroBits == 3 - var trailingZeroBits: Int { get } - - - /// Returns the quotient of dividing the first value by the second. - /// - /// For integer types, any remainder of the division is discarded. - /// - /// let x = 21 / 5 - /// // x == 4 - static func /(_ lhs: Self, _ rhs: Self) -> Self - - /// Divides this value by the given value in place. - /// - /// For example: - /// - /// var x = 15 - /// y /= 7 - /// // y == 2 - static func /=(_ lhs: inout Self, rhs: Self) - - /// Returns the remainder of dividing the first value by the second. - /// - /// The result has the same sign as `lhs` and is less than `rhs.magnitude`. - /// - /// let x = 22 % 5 - /// // x == 2 - /// let y = 22 % -5 - /// // y == 2 - /// let z = -22 % -5 - /// // z == -2 - /// - /// - Parameters: - /// - lhs: The value to divide. - /// - rhs: The value to divide `lhs` by. `rhs` must not be zero. - static func %(_ lhs: Self, _ rhs: Self) -> Self - - /// Replaces this value with the remainder of itself divided by the given - /// value. For example: - /// - /// var x = 15 - /// x %= 7 - /// // x == 1 - /// - /// - Parameter rhs: The value to divide this value by. `rhs` must not be - /// zero. - /// - /// - SeeAlso: `remainder(dividingBy:)` - static func %=(_ lhs: inout Self, _ rhs: Self) - - /// Returns the inverse of the bits set in the argument. - /// - /// The bitwise NOT operator (`~`) is a prefix operator that returns a value - /// in which all the bits of its argument are flipped: Bits that are `1` in - /// the argument are `0` in the result, and bits that are `0` in the argument - /// are `1` in the result. This is equivalent to the inverse of a set. For - /// example: - /// - /// let x: UInt8 = 5 // 0b00000101 - /// let notX = ~x // 0b11111010 - /// - /// Performing a bitwise NOT operation on 0 returns a value with every bit - /// set to `1`. - /// - /// let allOnes = ~UInt8.min // 0b11111111 - /// - /// - Complexity: O(1). - static prefix func ~ (_ x: Self) -> Self - - /// Returns the result of performing a bitwise AND operation on this value - /// and the given value. - /// - /// A bitwise AND operation results in a value that has each bit set to `1` - /// where *both* of its arguments have that bit set to `1`. For example: - /// - /// let x: UInt8 = 5 // 0b00000101 - /// let y: UInt8 = 14 // 0b00001110 - /// let z = x & y // 0b00000100 - static func &(_ lhs: Self, _ rhs: Self) -> Self - static func &=(_ lhs: inout Self, _ rhs: Self) - - /// Returns the result of performing a bitwise OR operation on this value and - /// the given value. - /// - /// A bitwise OR operation results in a value that has each bit set to `1` - /// where *one or both* of its arguments have that bit set to `1`. For - /// example: - /// - /// let x: UInt8 = 5 // 0b00000101 - /// let y: UInt8 = 14 // 0b00001110 - /// let z = x | y // 0b00001111 - static func |(_ lhs: Self, _ rhs: Self) -> Self - static func |=(_ lhs: inout Self, _ rhs: Self) - - /// Returns the result of performing a bitwise XOR operation on this value - /// and the given value. - /// - /// A bitwise XOR operation, also known as an exclusive OR operation, results - /// in a value that has each bit set to `1` where *one or the other but not - /// both* of its arguments had that bit set to `1`. For example: - /// - /// let x: UInt8 = 5 // 0b00000101 - /// let y: UInt8 = 14 // 0b00001110 - /// let z = x ^ y // 0b00001011 - static func ^(_ lhs: Self, _ rhs: Self) -> Self - static func ^=(_ lhs: inout Self, _ rhs: Self) - - /// Returns the result of shifting this value's binary representation the - /// specified number of digits to the right. - /// - /// In a *masking shift*, the bit pattern of the value passed as `rhs` is - /// masked to produce a value between zero and the bit width of `lhs`. The - /// shift is performed using this masked value. Masking shifts require more - /// care to use correctly than a traditional bit shift, but are likely to be - /// more efficient when used with shift amounts that are not compile-time - /// constants. On most architectures, a masking shift compiles down to a - /// single instruction. - /// - /// For example, if you shift an 8-bit, unsigned integer by 2, the shift - /// amount requires no masking. - /// - /// let x: UInt8 = 30 // 0b00011110 - /// let y = x &>> 2 - /// // y == 7 // 0b00000111 - /// - /// However, if you shift it by 11, it first bitmasks `rhs` to `3`, and then - /// uses that masked value as the number of bits to shift `x`. - /// - /// let z = x &>> 11 - /// // z == 3 // 0b00000011 - /// - /// Relationship to the Right Shift Operator - /// ---------------------------------------- - /// - /// The masking right shift operator handles attempted overshifts and - /// undershifts differently from the right shift operator (`>>`). When the - /// value passed as `rhs` in a masking shift is within the range - /// `0...> 2 - /// // y1 == 7 // 0b00000111 - /// let y2 = x >> 2 - /// // y2 == 7 // 0b00000111 - /// - /// The right shift operator does not mask its right-hand-side argument, so - /// passing `11` as `rhs` shifts all the bits of `x` to zero. - /// - /// let z1 = x &>> 11 - /// // z1 == 240 // 0b00000011 - /// let z2 = x >> 11 - /// // z2 == 0 // 0b00000000 - /// - /// - Parameter rhs: The number of bits to shift this value to the right. If - /// `rhs` is outside the range `0..>` - static func &>>(_ lhs: Self, _ rhs: Self) -> Self - static func &>>=(_ lhs: inout Self, _ rhs: Self) - - /// Returns the result of shifting this value's binary representation the - /// specified number of digits to the left. - /// - /// In a *masking shift*, the bit pattern of the value passed as `rhs` is - /// masked to produce a value between zero and the bit width of `lhs`. The - /// shift is performed using this masked value. Masking shifts require more - /// care to use correctly than a traditional bit shift, but are likely to be - /// more efficient when used with shift amounts that are not compile-time - /// constants. On most architectures, a masking shift compiles down to a - /// single instruction. - /// - /// For example, if you shift an 8-bit, unsigned integer by 2, the shift - /// amount requires no masking. - /// - /// let x: UInt8 = 30 // 0b00011110 - /// let y = x &>> 2 - /// // y == 120 // 0b01111000 - /// - /// However, if you shift it by 11, it first bitmasks `rhs` to `3`, and then - /// uses that masked value as the number of bits to shift `x`. - /// - /// let z = x &<< 11 - /// // z == 240 // 0b11110000 - /// - /// Relationship to the Left Shift Operator - /// --------------------------------------- - /// - /// The masking left shift operator handles attempted overshifts and - /// undershifts differently from the left shift operator (`<<`). When the - /// value passed as `rhs` in a masking shift is within the range - /// `0...>`, `<<` - static func &<<(_ lhs: Self, _ rhs: Self) -> Self - static func &<<=(_ lhs: inout Self, _ rhs: Self) - - /// Returns the quotient and remainder of this value divided by the given - /// value. - /// - /// Use this method to calculate the quotient and remainder of a division at - /// the same time. - /// - /// let x = 1_000_000 - /// let (q, r) = x.quotientAndRemainder(dividingBy: 933) - /// // q == 1071 - /// // r == 757 - /// - /// - Parameter rhs: The value to divide this value by. - /// - Returns: A tuple containing the quotient and remainder of this value - /// divided by `rhs`. - func quotientAndRemainder(dividingBy rhs: Self) - -> (quotient: Self, remainder: Self) - - /// Returns `-1` if this value is negative and `1` if it's positive; - /// otherwise, `0`. - /// - /// - Returns: The sign of this number, expressed as an integer of the same - /// type. - func signum() -> Self -} - -extension BinaryInteger { - init() { self = 0 } -} -``` - -#### `FixedWidthInteger` - -The `FixedWidthInteger` protocol adds the notion of endianness as well as static -properties for type bounds and bit width. - -The `WithOverflow` family of methods is used in default implementations of -mutating arithmetic methods (see the `Number` protocol). Having these -methods allows the library to provide both bounds-checked and masking -implementations of arithmetic operations, without duplicating code. - -The `multiplied(by:, _: FullWidth)` and `dividing(_:, _: FullWidth)` methods are necessary building -blocks to implement support for integer types of a greater width such as -arbitrary-precision integers. - -```Swift - -public enum FullWidth { case fullWidth } -public enum ReportingOverflow { case reportingOverflow } - -public protocol FixedWidthInteger : BinaryInteger { - /// The number of bits used for the underlying binary representation of - /// values of this type. - /// - /// An unsigned, fixed-width integer type can represent values from 0 through - /// `(2 ** bitWidth) - 1`, where `**` is exponentiation. A signed, - /// fixed-width integer type can represent values from - /// `-(2 ** bitWidth - 1)` through `(2 ** bitWidth - 1) - 1`. For example, - /// the `Int8` type has a `bitWidth` value of 8 and can store any integer in - /// the range `-128...127`. - static var bitWidth : Int { get } - - /// The maximum representable integer in this type. - /// - /// For unsigned integer types, this value is `(2 ** bitWidth) - 1`, where - /// `**` is exponentiation. For signed integer types, this value is - /// `(2 ** bitWidth - 1) - 1`. - static var max: Self { get } - - /// The minimum representable value. - /// - /// For unsigned integer types, this value is always `0`. For signed integer - /// types, this value is `-(2 ** bitWidth - 1)`, where `**` is - /// exponentiation. - static var min: Self { get } - - /// Returns the sum of this value and the given value along with a flag - /// indicating whether overflow occurred in the operation. - /// - /// - Parameter other: The value to add to this value. - /// - Returns: A tuple containing the result of the addition along with a - /// flag indicating whether overflow occurred. If the `overflow` component - /// is `.none`, the `partialValue` component contains the entire sum. If - /// the `overflow` component is `.overflow`, an overflow occurred and the - /// `partialValue` component contains the truncated sum of this value and - /// `other`. - /// - /// - SeeAlso: `+` - func adding(_ other: Self, _: ReportingOverflow) - -> (partialValue: Self, overflow: ArithmeticOverflow) - - /// Returns the difference of this value and the given value along with a - /// flag indicating whether overflow occurred in the operation. - /// - /// - Parameter other: The value to subtract from this value. - /// - Returns: A tuple containing the result of the subtraction along with a - /// flag indicating whether overflow occurred. If the `overflow` component - /// is `.none`, the `partialValue` component contains the entire - /// difference. If the `overflow` component is `.overflow`, an overflow - /// occurred and the `partialValue` component contains the truncated - /// result of `other` subtracted from this value. - /// - /// - SeeAlso: `-` - func subtracting(_ other: Self, _: ReportingOverflow) - -> (partialValue: Self, overflow: ArithmeticOverflow) - - /// Returns the product of this value and the given value along with a flag - /// indicating whether overflow occurred in the operation. - /// - /// - Parameter other: The value to multiply by this value. - /// - Returns: A tuple containing the result of the multiplication along with - /// a flag indicating whether overflow occurred. If the `overflow` - /// component is `.none`, the `partialValue` component contains the entire - /// product. If the `overflow` component is `.overflow`, an overflow - /// occurred and the `partialValue` component contains the truncated - /// product of this value and `other`. - /// - /// - SeeAlso: `*`, `multiplied(by:_:FullWidth)` - func multiplied(by other: Self, _: ReportingOverflow) - -> (partialValue: Self, overflow: ArithmeticOverflow) - - /// Returns the quotient of dividing this value by the given value along with - /// a flag indicating whether overflow occurred in the operation. - /// - /// For a value `x`, if zero is passed as `other`, the result is - /// `(x, .overflow)`. - /// - /// - Parameter other: The value to divide this value by. - /// - Returns: A tuple containing the result of the division along with a - /// flag indicating whether overflow occurred. If the `overflow` component - /// is `.none`, the `partialValue` component contains the entire quotient. - /// If the `overflow` component is `.overflow`, an overflow occurred and - /// the `partialValue` component contains the truncated quotient. - /// - /// - SeeAlso: `/`, `dividing(_:_:FullWidth)` - func divided(by other: Self, _: ReportingOverflow) - -> (partialValue: Self, overflow: ArithmeticOverflow) - - /// Returns a double-width value containing the high and low parts of the - /// result of multiplying this value by an argument. - /// - /// Use this method to calculate the full result of a product that would - /// otherwise overflow. Unlike traditional truncating multiplication, the - /// `multiplied(by:_:FullWidth)` method returns both the high and low - /// parts of the product of `self` and `other`. The following example uses - /// this method to multiply two `UInt8` values that normally overflow when - /// multiplied: - /// - /// let x: UInt8 = 100 - /// let y: UInt8 = 20 - /// let result = x.multiplied(by: y, .fullWidth) - /// // result.high == 0b00000111 - /// // result.low == 0b11010000 - /// - /// The product of `x` and `y` is 2000, which is too large to represent in a - /// `UInt8` instance. The `high` and `low` components of the `result` - /// represent 2000 when concatenated to form a double-width integer; that - /// is, using `result.high` as the high byte and `result.low` as the low byte - /// of a `UInt16` instance. - /// - /// let z = UInt16(result.high) << 8 | UInt16(result.low) - /// // z == 2000 - /// - /// - Parameters: - /// - other: A value to multiplied `self` by. - /// - Returns: A tuple containing the high and low parts of the result of - /// multiplying `self` and `other`. - /// - /// - SeeAlso: `multiplied(by:,_:ReportingOverflow)` - func multiplied(by other: Self, _: FullWidth) -> DoubleWidth - - /// Returns a tuple containing the quotient and remainder of dividing the - /// first argument by this value. - /// - /// The resulting quotient must be representable within the bounds of the - /// type. If the quotient of dividing `lhs` by `self` is too large to - /// represent in the type, a runtime error may occur. - /// - /// - Parameters: - /// - lhs: A value containing the high and low parts of a double-width - /// integer. The `high` component of the tuple carries the sign, if the - /// type is signed. - /// - Returns: A tuple containing the quotient and remainder of `lhs` divided - /// by `self`. - func dividing(_ lhs: DoubleWidth, _: FullWidth) - -> (quotient: Self, remainder: Self) - - /// The number of bits equal to 1 in this value's binary representation. - /// - /// For example, in a fixed-width integer type with a `bitWidth` value of 8, - /// the number 31 has five bits equal to 1. - /// - /// let x: Int8 = 0b0001_1111 - /// // x == 31 - /// // x.popcount == 5 - var popcount: Int { get } - - /// The number of leading zeros in this value's binary representation. - /// - /// For example, in a fixed-width integer type with a `bitWidth` value of 8, - /// the number 31 has three leading zeros. - /// - /// let x: Int8 = 0b0001_1111 - /// // x == 31 - /// // x.leadingZeroBits == 3 - /// - SeeAlso: `BinaryInteger.trailingZeroBits` - var leadingZeroBits: Int { get } - - /// Creates an integer from its big-endian representation, changing the - /// byte order if necessary. - init(bigEndian value: Self) - - /// Creates an integer from its little-endian representation, changing the - /// byte order if necessary. - init(littleEndian value: Self) - - /// The big-endian representation of this integer. - /// - /// If necessary, the byte order of this value is reversed from the typical - /// byte order of this integer type. On a big-endian platform, for any - /// integer `x`, `x == x.bigEndian`. - /// - /// - SeeAlso: `littleEndian` - var bigEndian: Self { get } - - /// The little-endian representation of this integer. - /// - /// If necessary, the byte order of this value is reversed from the typical - /// byte order of this integer type. On a little-endian platform, for any - /// integer `x`, `x == x.littleEndian`. - /// - /// - SeeAlso: `bigEndian` - var littleEndian: Self { get } - - /// A representation of this integer with the byte order swapped. - var byteSwapped: Self { get } -} -``` - -#### Auxiliary protocols - -```Swift -public protocol UnsignedInteger : BinaryInteger { - associatedtype Magnitude : BinaryInteger -} -public protocol SignedInteger : BinaryInteger, SignedNumber { - associatedtype Magnitude : BinaryInteger -} -``` - -### DoubleWidth\ - -The `DoubleWidth` type allows to create wider fixed-width integer types from -the ones available in the standard library. - -Standard library currently provides fixed-width integer types of up to 64 bits. -A value of `DoubleWidth` will double the range of the underlying type and -implement all the `FixedWidthInteger` requirements. _Please note_ though that -the implementation will not necessarily be the most efficient one, so it would -not be a good idea to use `DoubleWidth` instead of a built-in `Int64`. - -```swift -public enum DoubleWidth { - case .parts(high: T, low: T.Magnitude) - - public var high: T { get } - public var low: T.Magnitude { get } -} -``` - -Representing it as an `enum` instead of a simple struct allows to use it both -as a single value, as well as in destructuring matches. - -```swift -let high = doubleWidthValue.high -let low = doubleWidthValue.low - -// or - -case let (high, low) = doubleWidthValue -``` - - -### Extra operators - -In addition to the operators described in the [protocols section](#protocols), we also provide a few extensions that are not protocol requirements: - -#### Heterogeneous shifts - -```Swift -extension BinaryInteger { - // Masking shifts - static func &>> (lhs: Self, rhs: Other) -> Self - static func &>>= (lhs: inout Self, rhs: Other) - static func &<< (lhs: Self, rhs: Other) -> Self - static func &<<= (lhs: inout Self, rhs: Other) - - // 'Smart' shifts - static func >> (lhs: Self, rhs: Other) -> Self - static func >>= (lhs: inout Self, rhs: Other) - static func << (lhs: Self, rhs: Other) -> Self - static func <<= (lhs: inout Self, rhs: Other) -} -``` - -#### Heterogeneous equality and comparison - -```Swift -extension BinaryInteger { - // Equality - static func == (lhs: Self, rhs: Other) -> Bool - static func != (lhs: Self, rhs: Other) -> Bool - - // Comparison - static func < (lhs: Self, rhs: Other) -> Bool - static func <= (lhs: Self, rhs: Other) -> Bool - static func > (lhs: Self, rhs: Other) -> Bool - static func >= (lhs: Self, rhs: Other) -> Bool -} -``` - -#### Masking arithmetic - -```Swift -public func &* (lhs: T, rhs: T) -> T -public func &- (lhs: T, rhs: T) -> T -public func &+ (lhs: T, rhs: T) -> T -``` - - -## Non-goals - -This proposal: - -- *DOES NOT* solve the integer promotion problem, which would allow mixed-type - arithmetic. However, we believe that it is an important step in the right - direction. - -- *DOES NOT* include the implementation of a `BigInt` type, but allows it - to be implemented in the future. - - -## Source compatibility - -The proposed change is designed to be as non-breaking as possible, and it has been proven that it does not break code on concrete integer types. However, there are still a few API breaking changes in the realm of generic code: - -* Integer protocols in Swift up to and including version 3 were not particularly -useful for generic programming, but were rather a means of sharing -implementation between conforming types. Therefore we believe the amount of code -that relied on these protocols is relatively small. The breakage can be further -reduced by introducing proper aliases for the removed protocols with deprecation -warnings. - -* Deprecation of the `BitwiseOperations` protocol. We find it hard to imagine a type that conforms to this protocol, but is *not* a binary integer type. - -* Addition of 'smart' shifts will change the behavior of existing code. It will still compile, but will be potentially less performant due to extra logic involved. In a case, where this becomes a problem, newly introduced masking shift operators can be used instead. Unfortunately, performance characteristics of the code cannot be statically checked, and thus migration cannot be provided. - - -[se104]: 0104-improved-integers.md -[se91]: 0091-improving-operators-in-protocols.md -[impl]: https://github.com/apple/swift/tree/new-integer-protocols diff --git a/proposals/0104-improved-integers.md b/proposals/0104-improved-integers.md index 48bf0bf799..4e510a3344 100644 --- a/proposals/0104-improved-integers.md +++ b/proposals/0104-improved-integers.md @@ -1,32 +1,40 @@ # Protocol-oriented integers * Proposal: [SE-0104](0104-improved-integers.md) -* Authors: [Dave Abrahams](https://github.com/dabrahams), [Dmitri Gribenko](https://github.com/gribozavr), [Maxim Moiseev](https://github.com/moiseev) -* Review Manager: [Chris Lattner](http://github.com/lattner) -* Status: **Accepted** -* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-June/000206.html) -* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/0440700fc555a6c72abb4af807c8b79fb1bec592/proposals/0104-improved-integers.md) +* Authors: [Dave Abrahams](https://github.com/dabrahams), [Maxim Moiseev](https://github.com/moiseev) +* Review Manager: TBD +* Status: **Awaiting review** * Bug: [SR-3196](https://bugs.swift.org/browse/SR-3196) +* Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/0440700fc555a6c72abb4af807c8b79fb1bec592/proposals/0104-improved-integers.md), [2](https://github.com/apple/swift-evolution/blob/957ab545e05adb94507792e7871b38e34b56a0a5/proposals/0104-improved-integers.md). +* Discussion on swift-evolution: [here](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170109/030191.html). ## Introduction This proposal cleans up Swifts integer APIs and makes them more useful for generic programming. +The language has evolved in ways that affect integers APIs since the time the +original proposal was approved for Swift 3. We also attempted to implement +the proposed model in the standard library and found that some essential APIs +were missing, whereas others could be safely removed. + +Major changes to the APIs introduced by this revision are listed in a +[dedicated section](#whats-new-in-this-revision). + ## Motivation Swift's integer protocols don't currently provide a suitable basis for generic programming. See [this blog post](http://blog.krzyzanowskim.com/2015/03/01/swift_madness_of_generic_integer/) for an example of an attempt to implement a generic algorithm over integers. -The way the `Arithmetic` protocol is defined, it does not generalize to floating -point numbers and also slows down compilation by requiring every concrete -type to provide an implementation of arithmetic operators as free functions, -thus polluting the overload set. +The way the `IntegerArithmetic` protocol is defined, it does not generalize to +floating point numbers and also slows down compilation by requiring every +concrete type to provide an implementation of arithmetic operators, thus +polluting the overload set. Converting from one integer type to another is performed using the concept of the 'maximum width integer' (see `MaxInt`), which is an artificial limitation. -The very existence of `MaxInt` makes it unclear what to do, should someone +The very existence of `MaxInt` makes it unclear what to do should someone implement `Int256`, for example. Another annoying problem is the inability to use integers of different types in @@ -46,9 +54,8 @@ Currently, bit-shifting a negative number of (or too many) bits causes a trap on some platforms, which makes low-level bit manipulations needlessly dangerous and unpredictable. -Finally, the current design predates many of the improvements that came in -Swift 2, and hasn't been revised since then. - +Finally, the current design predates many of the improvements that came since +Swift 1, and hasn't been revised since then. ## Proposed solution @@ -57,28 +64,28 @@ more easily extensible. ~~~~ +--------------+ +-------------+ - +------>+ Arithmetic | | Comparable | + +------>+ Number | | Comparable | | | (+,-,*,/) | | (==,<,>,...)| | +-------------++ +---+---------+ | ^ ^ +-------+------------+ | | -| SignedArithmetic | +-+-------+------+ -| (unary -) | | BinaryInteger | -+------+-------------+ | (words,%,...) | - ^ ++---+-----+-----+ - | +-----------^ ^ ^-----------+ - | | | | -+------+---------++ +---------+-------------+ ++------------------+ -| SignedInteger | | FixedWidthInteger | | UnsignedInteger | -| | | (bitwise,overflow,...)| | | -+---------------+-+ +-+-----------------+---+ ++------------------+ - ^ ^ ^ ^ - | | | | - | | | | - ++--------+-+ +-+-------+-+ - |Int family |-+ |UInt family|-+ - +-----------+ | +-----------+ | - +-----------+ +-----------+ +| SignedNumber | +-+-------+-----------+ +| (unary -) | | BinaryInteger | ++------+-------------+ |(words,%,bitwise,...)| + ^ ++---+-----+----------+ + | +-----------^ ^ ^---------------+ + | | | | ++------+---------++ +---------+---------------+ +--+----------------+ +| SignedInteger | | FixedWidthInteger | | UnsignedInteger | +| | |(endianness,overflow,...)| | | ++---------------+-+ +-+--------------------+--+ +-+-----------------+ + ^ ^ ^ ^ + | | | | + | | | | + ++--------+-+ +-+-------+-+ + |Int family |-+ |UInt family|-+ + +-----------+ | +-----------+ | + +-----------+ +-----------+ ~~~~ @@ -87,41 +94,37 @@ There are several benefits provided by this model over the old one: - It allows mixing integer types in generic functions. The possibility to initialize instances of any concrete integer type with -values of any other concrete integer type enables writing functions that -operate on more than one type conforming to `BinaryInteger`, such as heterogeneous -comparisons or bit shifts, described later. + values of any other concrete integer type enables writing functions that + operate on more than one type conforming to `BinaryInteger`, such as + heterogeneous comparisons or bit shifts, described later. - It removes the overload resolution overhead. - Arithmetic and bitwise operations can now be defined as free functions -delegating work to concrete types. This approach significantly reduces the -number of overloads for those operations, which used to be defined for every -single concrete integer type. + Arithmetic and bitwise operations can now be defined as generic operators on + protocols. This approach significantly reduces the number of overloads for + those operations, which used to be defined for every single concrete integer + type. - It enables protocol sharing between integer and floating point types. - Note the exclusion of the `%` operation from `Arithmetic`. Its behavior for -floating point numbers is sufficiently different from the one for integers that -using it in generic context would lead to confusion. The `FloatingPoint` protocol -introduced by -[SE-0067](https://github.com/apple/swift-evolution/blob/0440700fc555a6c72abb4af807c8b79fb1bec592/proposals/0104-improved-integers.md) -should now refine `SignedArithmetic`. + Note the exclusion of the `%` operation from `Number`. Its behavior for + floating point numbers is sufficiently different from the one for integers + that using it in generic context would lead to confusion. The `FloatingPoint` + protocol introduced by [SE-0067](0067-floating-point-protocols.md) should now + refine `SignedNumber`. - It makes future extensions possible. - The proposed model eliminates the 'largest integer type' concept previously used -to interoperate between integer types (see `toIntMax` in the current model) and -instead provides access to machine words. It also introduces the -`doubleWidthMultiply`, `doubleWidthDivide`, and `quotientAndRemainder` methods. -Together these changes can be used to provide an efficient implementation of -bignums that would be hard to achieve otherwise. - -The prototype implementation, available -[here](https://github.com/apple/swift/blob/master/test/Prototypes/Integers.swift.gyb) -contains a `DoubleWidth` generic type that uses two values of any -`FixedInteger` type to represent a value of twice the width, demonstrating the -suitability of the proposed model for generic programming. + The proposed model eliminates the 'largest integer type' concept previously + used to interoperate between integer types (see `toIntMax` in the current + model) and instead provides access to machine words. It also introduces the + `multiplied(by:_:FullWidth)`, `dividing(_:, _:FullWidth)`, and + `quotientAndRemainder` methods. Together these changes can be used to provide + an efficient implementation of bignums that would be hard to achieve + otherwise. +The implementation of proposed model in the standard library is available +[in the new-integer-protocols branch][impl]. ### A note on bit shifts @@ -143,20 +146,75 @@ In most scenarios, the right hand operand is a literal constant, and branches for handling under- and over-shift cases can be optimized away. For other cases, this proposal provides *masking shifts*, implemented by `&>>` and `&<<`. A masking shift logically preprocesses the right hand operand by masking its -bits to produce a value in the range `0...(x-1)` where `x` is the number of -bits in the left hand operand. On most architectures this masking is already -performed by the CPU's shift instructions and has no cost. Both kinds of shift +bits to produce a value in the range `0...(x-1)` where `x` is the number of bits +in the left hand operand. On most architectures this masking is already +performed by the CPU's shift instructions and has no cost. Both kinds of shift avoid undefined behavior and produce uniform semantics across architectures. ## Detailed design +### What's new in this revision + +* [SE-0091][se91] removed the necessity to dispatch generic operators through +special methods. + + All operators are now declared by protocols as `static func`s. + +* Standard Library no longer provides `+` and `-` operators for `Strideable` +types. + + They were problematic, as one could have written mixed-type code like `let x: + Int64 = 42; x += (1 as Int)`, which would compile, but shouldn't. Besides, + since the `Stride` of an unsigned type is signed, Standard Library had to + implement a hack to make code like `let x: UInt = 42; x += (1 as Int)` + ambiguous. These operators were only necessary because they made advancing + collection indices convenient, which is no longer the case since the + introduction of the [new indexing model](0065-collections-move-indices.md) in + Swift 3. + +* Shifts and other bitwise operations were moved from `FixedWidthInteger` to +`BinaryInteger`. + + Left shift operation on an unbounded integer should infinitely extend the + number, and never drop set bits when they reach the most significant position + in the underlying representation. + +* `BitwiseOperations` protocol was deprecated. + + We believe there are no useful entities that support bitwise operations, but + at the same time are not binary integers. + +* `Arithmetic` and `SignedArithmetic` protocols have been renamed to `Number` + and `SignedNumber`. + +* `minimumSignedRepresentationBitWidth` property was removed. + +* `word(at:)` methods was replaced by `var words: Words` where `Words` is a new + associated type. + + This will help avoid quadratic complexity in algorithms iterating over all + words, and also allow standard library to provide a default conformance to + `Hashable`. + +* `trailingZeroBits` property was added to the `BinaryInteger` protocol. + + `leadingZeroBits` and `popcount` properties are still defined by the + `FixedWidthInteger` protocol. + +* Endian-converting initializers and properties were added to the +`FixedWidthInteger` protocol. + +* Standard library introduces the new type `DoubleWidth`. + + See [this section](#doublewidtht) for more details. + ### Protocols -#### `Arithmetic` +#### `Number` -The `Arithmetic` protocol declares methods backing binary arithmetic -operators—such as `+`, `-` and `*`—and their mutating counterparts. +The `Number` protocol declares binary arithmetic operators – such as `+`, +`-`, and `*` — and their mutating counterparts. It provides a suitable basis for arithmetic on scalars such as integers and floating point numbers. @@ -166,7 +224,7 @@ only the mutating ones are required, as default implementations of the non-mutating ones are provided by a protocol extension. The `Magnitude` associated type is able to hold the absolute value of any -possible value of `Self`. Concrete types do not have to provide a typealias for +possible value of `Self`. Concrete types do not have to provide a type alias for it, as it can be inferred from the `magnitude` property. This property can be useful in operations that are simpler to implement in terms of unsigned values, for example, printing a value of an integer, which is just printing a @@ -178,426 +236,873 @@ as its argument. ```Swift -public protocol Arithmetic : Equatable, IntegerLiteralConvertible { - /// Initializes to the value of `source` if it is representable exactly, - /// returns `nil` otherwise. +public protocol Number : Equatable, ExpressibleByIntegerLiteral { + /// Creates a new instance from the given integer, if it can be represented + /// exactly. + /// + /// If the value passed as `source` is not representable exactly, the result + /// is `nil`. In the following example, the constant `x` is successfully + /// created from a value of `100`, while the attempt to initialize the + /// constant `y` from `1_000` fails because the `Int8` type can represent + /// `127` at maximum: + /// + /// let x = Int8(exactly: 100) + /// // x == Optional(100) + /// let y = Int8(exactly: 1_000) + /// // y == nil + /// + /// - Parameter source: A floating-point value to convert to an integer. init?(exactly source: T) - associatedtype Magnitude : Arithmetic + /// A type that can represent the absolute value of any possible value of the + /// conforming type. + associatedtype Magnitude : Equatable, ExpressibleByIntegerLiteral + + /// The magnitude of this value. + /// + /// For any numeric value `x`, `x.magnitude` is the absolute value of `x`. + /// You can use the `magnitude` property in operations that are simpler to + /// implement in terms of unsigned values, such as printing the value of an + /// integer, which is just printing a '-' character in front of an absolute + /// value. + /// + /// let x = -200 + /// // x.magnitude == 200 + /// + /// The global `abs(_:)` function provides more familiar syntax when you need + /// to find an absolute value. In addition, because `abs(_:)` always returns + /// a value of the same type, even in a generic context, using the function + /// instead of the `magnitude` property is encouraged. + /// + /// - SeeAlso: `abs(_:)` var magnitude: Magnitude { get } - func adding(_ other: Self) -> Self - func subtracting(_ other: Self) -> Self - func multiplied(by other: Self) -> Self - func divided(by other: Self) -> Self + /// Returns the sum of the two given values. + /// + /// The sum of `lhs` and `rhs` must be representable in the same type. In the + /// following example, the result of `100 + 200` is greater than the maximum + /// representable `Int8` value: + /// + /// let x: Int8 = 10 + 21 + /// // x == 31 + /// let y: Int8 = 100 + 121 + /// // Overflow error + static func +(_ lhs: Self, _ rhs: Self) -> Self + + /// Adds the given value to this value in place. + /// + /// For example: + /// + /// var x = 15 + /// y += 7 + /// // y == 22 + static func +=(_ lhs: inout Self, rhs: Self) + + /// Returns the difference of the two given values. + /// + /// The difference of `lhs` and `rhs` must be representable in the same type. + /// In the following example, the result of `10 - 21` is less than zero, the + /// minimum representable `UInt` value: + /// + /// let x: UInt = 21 - 10 + /// // x == 11 + /// let y: UInt = 10 - 21 + /// // Overflow error + static func -(_ lhs: Self, _ rhs: Self) -> Self - mutating func add(_ other: Self) - mutating func subtract(_ other: Self) - mutating func multiply(by other: Self) - mutating func divide(by other: Self) + /// Subtracts the given value from this value in place. + /// + /// For example: + /// + /// var x = 15 + /// y -= 7 + /// // y == 8 + static func -=(_ lhs: inout Self, rhs: Self) + + /// Returns the product of the two given values. + /// + /// The product of `lhs` and `rhs` must be representable in the same type. In + /// the following example, the result of `10 * 50` is greater than the + /// maximum representable `Int8` value. + /// + /// let x: Int8 = 10 * 5 + /// // x == 50 + /// let y: Int8 = 10 * 50 + /// // Overflow error + static func *(_ lhs: Self, _ rhs: Self) -> Self + + /// Multiples this value by the given value in place. + /// + /// For example: + /// + /// var x = 15 + /// y *= 7 + /// // y == 105 + static func *=(_ lhs: inout Self, rhs: Self) } -extension Arithmetic { - public init() { self = 0 } +extension Number { + public static prefix func + (x: Self) -> Self { + return x + } } ``` -#### `SignedArithmetic` +#### `SignedNumber` -The `SignedArithmetic` protocol is for numbers that can be negated. +The `SignedNumber` protocol is for numbers that can be negated. ```Swift -public protocol SignedArithmetic : Arithmetic { - func negated() -> Self +public protocol SignedNumber : Number { + /// Returns the additive inverse of this value. + /// + /// let x = 21 + /// let y = -x + /// // y == -21 + /// + /// - Returns: The additive inverse of this value. + /// + /// - SeeAlso: `negate()` + static prefix func - (_ operand: Self) -> Self + + /// Replaces this value with its additive inverse. + /// + /// The following example uses the `negate()` method to negate the value of + /// an integer `x`: + /// + /// var x = 21 + /// x.negate() + /// // x == -21 + /// + /// - SeeAlso: The unary minus operator (`-`). mutating func negate() } -extension SignedArithmetic { - public func negated() -> Self { - return Self() - self +extension SignedNumber { + public static prefix func - (_ operand: Self) -> Self { + var result = operand + result.negate() + return result } + public mutating func negate() { - self = negated() + self = 0 - self } } ``` #### `BinaryInteger` -The `BinaryInteger` protocol is the basis for all the integer types provided by the -standard library. +The `BinaryInteger` protocol is the basis for all the integer types provided by +the standard library. -The `isEqual(to:)` and `isLess(than:)` methods provide implementations for -`Equatable` and `Comparable` protocol conformances. Similar to how arithmetic -operations are dispatched in `Arithmetic`, `==` and `<` operators for -homogeneous comparisons are implemented as generic free functions invoking the -`isEqual(to:)` and `isLess(than:)` protocol methods respectively. +This protocol adds a few new initializers. Two of them allow to create integers +from floating point numbers, others support construction from instances of any +type conforming to `BinaryInteger`, using different strategies: -This protocol adds 4 new initializers. One of them allows to create integers -from floating point numbers, if the value is representable exactly, others -support construction from instances of any type conforming to `BinaryInteger`, using -different strategies: - - - Initialze `Self` with the value, provided that the value is representable. + - Initialize `Self` with the value, provided that the value is representable. The precondition should be satisfied by the caller. - Extend or truncate the value to fit into `Self` - Clamp the value to the representable range of `Self` +`BinaryInteger` also declares bitwise and shift operators. + ```Swift -public protocol BinaryInteger: - Comparable, Arithmetic, - IntegerLiteralConvertible, CustomStringConvertible { +public protocol BinaryInteger : + Comparable, Hashable, Number, CustomStringConvertible, Strideable { - static var isSigned: Bool { get } + associatedtype Words : Collection // where Iterator.Element == UInt - func isEqual(to other: Self) -> Bool - func isLess(than other: Self) -> Bool + /// A Boolean value indicating whether this type is a signed integer type. + /// + /// *Signed* integer types can represent both positive and negative values. + /// *Unsigned* integer types can represent only nonnegative values. + static var isSigned: Bool { get } - /// Creates an instance of `Self` that has the exact value of `source`, - /// returns `nil` otherwise. + /// Creates an integer from the given floating-point value, if it can be + /// represented exactly. + /// + /// If the value passed as `source` is not representable exactly, the result + /// is `nil`. In the following example, the constant `x` is successfully + /// created from a value of `21.0`, while the attempt to initialize the + /// constant `y` from `21.5` fails: + /// + /// let x = Int(exactly: 21.0) + /// // x == Optional(21) + /// let y = Int(exactly: 21.5) + /// // y == nil + /// + /// - Parameter source: A floating-point value to convert to an integer. init?(exactly source: T) - /// Truncates the `source` to the closest representable value of `Self`. + /// Creates an integer from the given floating-point value, truncating any + /// fractional part. + /// + /// Truncating the fractional part of `source` is equivalent to rounding + /// toward zero. + /// + /// let x = Int(21.5) + /// // x == 21 + /// let y = Int(-21.5) + /// // y == -21 + /// + /// If `source` is outside the bounds of this type after truncation, a + /// runtime error may occur. + /// + /// let z = UInt(-21.5) + /// // Error: ...the result would be less than UInt.min + /// + /// - Parameter source: A floating-point value to convert to an integer. + /// `source` must be representable in this type after truncation. init(_ source: T) - /// Creates an instance of `Self` from `source` if it is representable. + /// Creates an new instance from the given integer. /// - /// - Precondition: the value of `source` is representable in `Self`. + /// If the value passed as `source` is not representable in this type, a + /// runtime error may occur. + /// + /// let x = -500 as Int + /// let y = Int32(x) + /// // y == -500 + /// + /// // -500 is not representable as a 'UInt32' instance + /// let z = UInt32(x) + /// // Error + /// + /// - Parameter source: An integer to convert. `source` must be representable + /// in this type. init(_ source: T) - /// Creates in instance of `Self` from `source` by sign-extending it - /// indefinitely and then truncating to fit `Self`. + /// Creates a new instance from the bit pattern of the given instance by + /// sign-extending or truncating to fit this type. + /// + /// When the bit width of `T` (the type of `source`) is equal to or greater + /// than this type's bit width, the result is the truncated + /// least-significant bits of `source`. For example, when converting a + /// 16-bit value to an 8-bit type, only the lower 8 bits of `source` are + /// used. + /// + /// let p: Int16 = -500 + /// // 'p' has a binary representation of 11111110_00001100 + /// let q = Int8(extendingOrTruncating: p) + /// // q == 12 + /// // 'q' has a binary representation of 00001100 + /// + /// When the bit width of `T` is less than this type's bit width, the result + /// is *sign-extended* to fill the remaining bits. That is, if `source` is + /// negative, the result is padded with ones; otherwise, the result is + /// padded with zeros. + /// + /// let u: Int8 = 21 + /// // 'u' has a binary representation of 00010101 + /// let v = Int16(extendingOrTruncating: u) + /// // v == 21 + /// // 'v' has a binary representation of 00000000_00010101 + /// + /// let w: Int8 = -21 + /// // 'w' has a binary representation of 11101011 + /// let x = Int16(extendingOrTruncating: w) + /// // x == -21 + /// // 'x' has a binary representation of 11111111_11101011 + /// let y = UInt16(extendingOrTruncating: w) + /// // y == 65515 + /// // 'y' has a binary representation of 11111111_11101011 + /// + /// - Parameter source: An integer to convert to this type. init(extendingOrTruncating source: T) - /// Creates in instance of `Self` containing the closest representable - /// value of `source`. + /// Creates a new instance with the representable value that's closest to the + /// given integer. + /// + /// If the value passed as `source` is greater than the maximum representable + /// value in this type, the result is the type's `max` value. If `source` is + /// less than the smallest representable value in this type, the result is + /// the type's `min` value. + /// + /// In this example, `x` is initialized as an `Int8` instance by clamping + /// `500` to the range `-128...127`, and `y` is initialized as a `UInt` + /// instance by clamping `-500` to the range `0...UInt.max`. + /// + /// let x = Int8(clamping: 500) + /// // x == 127 + /// // x == Int8.max + /// + /// let y = UInt(clamping: -500) + /// // y == 0 + /// + /// - Parameter source: An integer to convert to this type. init(clamping source: T) - /// Returns the n-th word, counting from the least significant to most - /// significant, of the underlying representation of `self`. - /// Should return `0` for positive numbers and `~0` for negative ones if `n` - /// is greater than the number of words in current representation of `self`. - func word(at n: Int) -> UInt + /// The collection of words in two's complement representation of the value, + /// from the least significant to most. + var words: Words { get } - /// The number of bits in current representation of `self` - /// Will be constant for fixed-width integer types. + /// The number of bits in the current binary representation of this value. + /// + /// This property is a constant for instances of fixed-width integer + /// types. var bitWidth : Int { get } - /// The number of bits required to represent the value of `self` in a signed - /// type using two's complement representation. The minimum value for this - /// property should naturally be 1. - var minimumSignedRepresentationBitWidth: Int { get } + /// The number of trailing zeros in this value's binary representation. + /// + /// For example, in a fixed-width integer type with a `bitWidth` value of 8, + /// the number -8 has three trailing zeros. + /// + /// let x = Int8(bitPattern: 0b1111_1000) + /// // x == -8 + /// // x.trailingZeroBits == 3 + var trailingZeroBits: Int { get } - /// Returns the remainder of division of `self` by `other`. - func remainder(dividingBy other: Self) -> Self - /// Replaces `self` with the remainder of division of `self` by `other`. - mutating func formRemainder(dividingBy other: Self) + /// Returns the quotient of dividing the first value by the second. + /// + /// For integer types, any remainder of the division is discarded. + /// + /// let x = 21 / 5 + /// // x == 4 + static func /(_ lhs: Self, _ rhs: Self) -> Self + + /// Divides this value by the given value in place. + /// + /// For example: + /// + /// var x = 15 + /// y /= 7 + /// // y == 2 + static func /=(_ lhs: inout Self, rhs: Self) - /// Returns a pair of values, containing the quotient and the remainder of - /// division of `self` by `other`. + /// Returns the remainder of dividing the first value by the second. + /// + /// The result has the same sign as `lhs` and is less than `rhs.magnitude`. + /// + /// let x = 22 % 5 + /// // x == 2 + /// let y = 22 % -5 + /// // y == 2 + /// let z = -22 % -5 + /// // z == -2 + /// + /// - Parameters: + /// - lhs: The value to divide. + /// - rhs: The value to divide `lhs` by. `rhs` must not be zero. + static func %(_ lhs: Self, _ rhs: Self) -> Self + + /// Replaces this value with the remainder of itself divided by the given + /// value. For example: + /// + /// var x = 15 + /// x %= 7 + /// // x == 1 + /// + /// - Parameter rhs: The value to divide this value by. `rhs` must not be + /// zero. + /// + /// - SeeAlso: `remainder(dividingBy:)` + static func %=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the inverse of the bits set in the argument. + /// + /// The bitwise NOT operator (`~`) is a prefix operator that returns a value + /// in which all the bits of its argument are flipped: Bits that are `1` in + /// the argument are `0` in the result, and bits that are `0` in the argument + /// are `1` in the result. This is equivalent to the inverse of a set. For + /// example: + /// + /// let x: UInt8 = 5 // 0b00000101 + /// let notX = ~x // 0b11111010 + /// + /// Performing a bitwise NOT operation on 0 returns a value with every bit + /// set to `1`. + /// + /// let allOnes = ~UInt8.min // 0b11111111 + /// + /// - Complexity: O(1). + static prefix func ~ (_ x: Self) -> Self + + /// Returns the result of performing a bitwise AND operation on this value + /// and the given value. + /// + /// A bitwise AND operation results in a value that has each bit set to `1` + /// where *both* of its arguments have that bit set to `1`. For example: + /// + /// let x: UInt8 = 5 // 0b00000101 + /// let y: UInt8 = 14 // 0b00001110 + /// let z = x & y // 0b00000100 + static func &(_ lhs: Self, _ rhs: Self) -> Self + static func &=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the result of performing a bitwise OR operation on this value and + /// the given value. + /// + /// A bitwise OR operation results in a value that has each bit set to `1` + /// where *one or both* of its arguments have that bit set to `1`. For + /// example: + /// + /// let x: UInt8 = 5 // 0b00000101 + /// let y: UInt8 = 14 // 0b00001110 + /// let z = x | y // 0b00001111 + static func |(_ lhs: Self, _ rhs: Self) -> Self + static func |=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the result of performing a bitwise XOR operation on this value + /// and the given value. + /// + /// A bitwise XOR operation, also known as an exclusive OR operation, results + /// in a value that has each bit set to `1` where *one or the other but not + /// both* of its arguments had that bit set to `1`. For example: + /// + /// let x: UInt8 = 5 // 0b00000101 + /// let y: UInt8 = 14 // 0b00001110 + /// let z = x ^ y // 0b00001011 + static func ^(_ lhs: Self, _ rhs: Self) -> Self + static func ^=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the result of shifting this value's binary representation the + /// specified number of digits to the right. + /// + /// In a *masking shift*, the bit pattern of the value passed as `rhs` is + /// masked to produce a value between zero and the bit width of `lhs`. The + /// shift is performed using this masked value. Masking shifts require more + /// care to use correctly than a traditional bit shift, but are likely to be + /// more efficient when used with shift amounts that are not compile-time + /// constants. On most architectures, a masking shift compiles down to a + /// single instruction. + /// + /// For example, if you shift an 8-bit, unsigned integer by 2, the shift + /// amount requires no masking. + /// + /// let x: UInt8 = 30 // 0b00011110 + /// let y = x &>> 2 + /// // y == 7 // 0b00000111 + /// + /// However, if you shift it by 11, it first bitmasks `rhs` to `3`, and then + /// uses that masked value as the number of bits to shift `x`. + /// + /// let z = x &>> 11 + /// // z == 3 // 0b00000011 + /// + /// Relationship to the Right Shift Operator + /// ---------------------------------------- + /// + /// The masking right shift operator handles attempted overshifts and + /// undershifts differently from the right shift operator (`>>`). When the + /// value passed as `rhs` in a masking shift is within the range + /// `0...> 2 + /// // y1 == 7 // 0b00000111 + /// let y2 = x >> 2 + /// // y2 == 7 // 0b00000111 + /// + /// The right shift operator does not mask its right-hand-side argument, so + /// passing `11` as `rhs` shifts all the bits of `x` to zero. /// - /// The default implementation simply invokes `divided(by:)` and - /// `remainder(dividingBy:)`, which in case of built-in types will be fused - /// into a single instruction by the compiler. + /// let z1 = x &>> 11 + /// // z1 == 240 // 0b00000011 + /// let z2 = x >> 11 + /// // z2 == 0 // 0b00000000 /// - /// Conforming types can override the default behavior in order to - /// provide a more efficient implementation. - func quotientAndRemainder(dividingBy other: Self) -> (Self, Self) + /// - Parameter rhs: The number of bits to shift this value to the right. If + /// `rhs` is outside the range `0..>` + static func &>>(_ lhs: Self, _ rhs: Self) -> Self + static func &>>=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the result of shifting this value's binary representation the + /// specified number of digits to the left. + /// + /// In a *masking shift*, the bit pattern of the value passed as `rhs` is + /// masked to produce a value between zero and the bit width of `lhs`. The + /// shift is performed using this masked value. Masking shifts require more + /// care to use correctly than a traditional bit shift, but are likely to be + /// more efficient when used with shift amounts that are not compile-time + /// constants. On most architectures, a masking shift compiles down to a + /// single instruction. + /// + /// For example, if you shift an 8-bit, unsigned integer by 2, the shift + /// amount requires no masking. + /// + /// let x: UInt8 = 30 // 0b00011110 + /// let y = x &>> 2 + /// // y == 120 // 0b01111000 + /// + /// However, if you shift it by 11, it first bitmasks `rhs` to `3`, and then + /// uses that masked value as the number of bits to shift `x`. + /// + /// let z = x &<< 11 + /// // z == 240 // 0b11110000 + /// + /// Relationship to the Left Shift Operator + /// --------------------------------------- + /// + /// The masking left shift operator handles attempted overshifts and + /// undershifts differently from the left shift operator (`<<`). When the + /// value passed as `rhs` in a masking shift is within the range + /// `0...>`, `<<` + static func &<<(_ lhs: Self, _ rhs: Self) -> Self + static func &<<=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the quotient and remainder of this value divided by the given + /// value. + /// + /// Use this method to calculate the quotient and remainder of a division at + /// the same time. + /// + /// let x = 1_000_000 + /// let (q, r) = x.quotientAndRemainder(dividingBy: 933) + /// // q == 1071 + /// // r == 757 + /// + /// - Parameter rhs: The value to divide this value by. + /// - Returns: A tuple containing the quotient and remainder of this value + /// divided by `rhs`. + func quotientAndRemainder(dividingBy rhs: Self) + -> (quotient: Self, remainder: Self) + + /// Returns `-1` if this value is negative and `1` if it's positive; + /// otherwise, `0`. + /// + /// - Returns: The sign of this number, expressed as an integer of the same + /// type. + func signum() -> Self +} + +extension BinaryInteger { + init() { self = 0 } } ``` #### `FixedWidthInteger` -The `FixedWidthInteger` protocol adds binary bitwise operations and bit shifts -to the `BinaryInteger` protocol. +The `FixedWidthInteger` protocol adds the notion of endianness as well as static +properties for type bounds and bit width. The `WithOverflow` family of methods is used in default implementations of -mutating arithmetic methods (see the `Arithmetic` protocol). Having these +mutating arithmetic methods (see the `Number` protocol). Having these methods allows the library to provide both bounds-checked and masking implementations of arithmetic operations, without duplicating code. -Bitwise binary and shift operators are implemented the same way as arithmetic -operations: a free function dispatches a call to a corresponding protocol -method. - -The `doubleWidthMultiply` and `doubleWidthDivide` methods are necessary building -blocks to implement support for integer types of a greater width such as -arbitrary-precision integers. +The `multiplied(by:, _: FullWidth)` and `dividing(_:, _: FullWidth)` methods +are necessary building blocks to implement support for integer types of a +greater width such as arbitrary-precision integers. ```Swift + +public enum FullWidth { case fullWidth } +public enum ReportingOverflow { case reportingOverflow } + public protocol FixedWidthInteger : BinaryInteger { - /// Returns the bit width of the underlying binary - /// representation of values of `self`. + /// The number of bits used for the underlying binary representation of + /// values of this type. + /// + /// An unsigned, fixed-width integer type can represent values from 0 through + /// `(2 ** bitWidth) - 1`, where `**` is exponentiation. A signed, + /// fixed-width integer type can represent values from + /// `-(2 ** bitWidth - 1)` through `(2 ** bitWidth - 1) - 1`. For example, + /// the `Int8` type has a `bitWidth` value of 8 and can store any integer in + /// the range `-128...127`. static var bitWidth : Int { get } - /// Returns the maximum value representable by `Self`. + /// The maximum representable integer in this type. + /// + /// For unsigned integer types, this value is `(2 ** bitWidth) - 1`, where + /// `**` is exponentiation. For signed integer types, this value is + /// `(2 ** bitWidth - 1) - 1`. static var max: Self { get } - /// Returns the minimum value representable by 'Self'. - static var min: Self { get } - /// Adds `other` to `self` returning a pair containing the partial result - /// of addition and an overflow flag. - func addingWithOverflow( - other: Self - ) -> (partialValue: Self, overflow: ArithmeticOverflow) - - /// Subtracts `other` from `self` returning a pair containing the partial - /// result of subtraction and an overflow flag. - func subtractingWithOverflow( - other: Self - ) -> (partialValue: Self, overflow: ArithmeticOverflow) - - /// Multiplies `self` by `other` returning a pair containing the partial - /// result of multiplication and an overflow flag. - func multipliedWithOverflow( - by other: Self - ) -> (partialValue: Self, overflow: ArithmeticOverflow) - - /// Divides `self` by `other` returning a pair containing the partial - /// result of division and an overflow flag. - func dividedWithOverflow( - by other: Self - ) -> (partialValue: Self, overflow: ArithmeticOverflow) - - /// Returns the partial result of getting a remainder of division of `self` - /// by `other`, and an overflow flag. - func remainderWithOverflow( - dividingBy other: Self - ) -> (partialValue: Self, overflow: ArithmeticOverflow) - - /// Returns the result of the 'bitwise and' operation, applied - /// to `self` and `other`. - func bitwiseAnd(other: Self) -> Self - - /// Returns the result of the 'bitwise or' operation, applied - /// to `self` and `other`. - func bitwiseOr(other: Self) -> Self - - /// Returns the result of the 'bitwise exclusive or' operation, applied - /// to `self` and `other`. - func bitwiseXor(other: Self) -> Self - - /// Returns the result of shifting the binary representation - /// of `self` by `other` binary digits to the right. - func maskingShiftRight(other: Self) -> Self - - /// Returns the result of shifting the binary representation - /// of `self` by `other` binary digits to the left. - func maskingShiftLeft(other: Self) -> Self - - /// Returns a pair containing the `high` and `low` parts of the result - /// of `lhs` multiplied by `rhs`. - static func doubleWidthMultiply(_ lhs: Self, _ rhs: Self) - -> (high: Self, low: Magnitude) - - /// Returns a pair containing a quotient and a remainder of `lhs` divided by - /// `rhs`, where `lhs` is itself a pair of `high` and `low` words of a double - /// width number. - static func doubleWidthDivide( - _ lhs: (high: Self, low: Magnitude), _ rhs: Self - ) -> (quotient: Self, remainder: Self) - - - /// Returns a number of set (i.e. equal to 1) bits in the representation of - /// `self`. - var popcount: Int { get } - - /// Returns the number of leading zeros in the representation of `self`. - var leadingZeros: Int { get } -} -``` + /// The minimum representable value. + /// + /// For unsigned integer types, this value is always `0`. For signed integer + /// types, this value is `-(2 ** bitWidth - 1)`, where `**` is + /// exponentiation. + static var min: Self { get } -#### Auxiliary protocols + /// Returns the sum of this value and the given value along with a flag + /// indicating whether overflow occurred in the operation. + /// + /// - Parameter other: The value to add to this value. + /// - Returns: A tuple containing the result of the addition along with a + /// flag indicating whether overflow occurred. If the `overflow` component + /// is `.none`, the `partialValue` component contains the entire sum. If + /// the `overflow` component is `.overflow`, an overflow occurred and the + /// `partialValue` component contains the truncated sum of this value and + /// `other`. + /// + /// - SeeAlso: `+` + func adding(_ other: Self, _: ReportingOverflow) + -> (partialValue: Self, overflow: ArithmeticOverflow) -```Swift -public protocol UnsignedInteger : BinaryInteger { - associatedtype Magnitude : BinaryInteger -} -public protocol SignedInteger : BinaryInteger, SignedArithmetic { - associatedtype Magnitude : BinaryInteger -} -``` + /// Returns the difference of this value and the given value along with a + /// flag indicating whether overflow occurred in the operation. + /// + /// - Parameter other: The value to subtract from this value. + /// - Returns: A tuple containing the result of the subtraction along with a + /// flag indicating whether overflow occurred. If the `overflow` component + /// is `.none`, the `partialValue` component contains the entire + /// difference. If the `overflow` component is `.overflow`, an overflow + /// occurred and the `partialValue` component contains the truncated + /// result of `other` subtracted from this value. + /// + /// - SeeAlso: `-` + func subtracting(_ other: Self, _: ReportingOverflow) + -> (partialValue: Self, overflow: ArithmeticOverflow) + /// Returns the product of this value and the given value along with a flag + /// indicating whether overflow occurred in the operation. + /// + /// - Parameter other: The value to multiply by this value. + /// - Returns: A tuple containing the result of the multiplication along with + /// a flag indicating whether overflow occurred. If the `overflow` + /// component is `.none`, the `partialValue` component contains the entire + /// product. If the `overflow` component is `.overflow`, an overflow + /// occurred and the `partialValue` component contains the truncated + /// product of this value and `other`. + /// + /// - SeeAlso: `*`, `multiplied(by:_:FullWidth)` + func multiplied(by other: Self, _: ReportingOverflow) + -> (partialValue: Self, overflow: ArithmeticOverflow) -### Operators + /// Returns the quotient of dividing this value by the given value along with + /// a flag indicating whether overflow occurred in the operation. + /// + /// For a value `x`, if zero is passed as `other`, the result is + /// `(x, .overflow)`. + /// + /// - Parameter other: The value to divide this value by. + /// - Returns: A tuple containing the result of the division along with a + /// flag indicating whether overflow occurred. If the `overflow` component + /// is `.none`, the `partialValue` component contains the entire quotient. + /// If the `overflow` component is `.overflow`, an overflow occurred and + /// the `partialValue` component contains the truncated quotient. + /// + /// - SeeAlso: `/`, `dividing(_:_:FullWidth)` + func divided(by other: Self, _: ReportingOverflow) + -> (partialValue: Self, overflow: ArithmeticOverflow) -#### Arithmetic + /// Returns a double-width value containing the high and low parts of the + /// result of multiplying this value by an argument. + /// + /// Use this method to calculate the full result of a product that would + /// otherwise overflow. Unlike traditional truncating multiplication, the + /// `multiplied(by:_:FullWidth)` method returns both the high and low + /// parts of the product of `self` and `other`. The following example uses + /// this method to multiply two `UInt8` values that normally overflow when + /// multiplied: + /// + /// let x: UInt8 = 100 + /// let y: UInt8 = 20 + /// let result = x.multiplied(by: y, .fullWidth) + /// // result.high == 0b00000111 + /// // result.low == 0b11010000 + /// + /// The product of `x` and `y` is 2000, which is too large to represent in a + /// `UInt8` instance. The `high` and `low` components of the `result` + /// represent 2000 when concatenated to form a double-width integer; that + /// is, using `result.high` as the high byte and `result.low` as the low byte + /// of a `UInt16` instance. + /// + /// let z = UInt16(result.high) << 8 | UInt16(result.low) + /// // z == 2000 + /// + /// - Parameters: + /// - other: A value to multiplied `self` by. + /// - Returns: A tuple containing the high and low parts of the result of + /// multiplying `self` and `other`. + /// + /// - SeeAlso: `multiplied(by:,_:ReportingOverflow)` + func multiplied(by other: Self, _: FullWidth) -> DoubleWidth -```Swift -public func + (lhs: T, rhs: T) -> T -public func += (lhs: inout T, rhs: T) -public func - (lhs: T, rhs: T) -> T -public func -= (lhs: inout T, rhs: T) -public func * (lhs: T, rhs: T) -> T -public func *= (lhs: inout T, rhs: T) -public func / (lhs: T, rhs: T) -> T -public func /= (lhs: inout T, rhs: T) -public func % (lhs: T, rhs: T) -> T -public func %= (lhs: inout T, rhs: T) -``` + /// Returns a tuple containing the quotient and remainder of dividing the + /// first argument by this value. + /// + /// The resulting quotient must be representable within the bounds of the + /// type. If the quotient of dividing `lhs` by `self` is too large to + /// represent in the type, a runtime error may occur. + /// + /// - Parameters: + /// - lhs: A value containing the high and low parts of a double-width + /// integer. The `high` component of the tuple carries the sign, if the + /// type is signed. + /// - Returns: A tuple containing the quotient and remainder of `lhs` divided + /// by `self`. + func dividing(_ lhs: DoubleWidth, _: FullWidth) + -> (quotient: Self, remainder: Self) + + /// The number of bits equal to 1 in this value's binary representation. + /// + /// For example, in a fixed-width integer type with a `bitWidth` value of 8, + /// the number 31 has five bits equal to 1. + /// + /// let x: Int8 = 0b0001_1111 + /// // x == 31 + /// // x.popcount == 5 + var popcount: Int { get } -##### Implementation example + /// The number of leading zeros in this value's binary representation. + /// + /// For example, in a fixed-width integer type with a `bitWidth` value of 8, + /// the number 31 has three leading zeros. + /// + /// let x: Int8 = 0b0001_1111 + /// // x == 31 + /// // x.leadingZeroBits == 3 + /// - SeeAlso: `BinaryInteger.trailingZeroBits` + var leadingZeroBits: Int { get } -_Only homogeneous arithmetic operations are supported._ + /// Creates an integer from its big-endian representation, changing the + /// byte order if necessary. + init(bigEndian value: Self) -```Swift -public func + (lhs: T, rhs: T) -> T { - return lhs.adding(rhs) -} + /// Creates an integer from its little-endian representation, changing the + /// byte order if necessary. + init(littleEndian value: Self) -extension Arithmetic { - public func adding(_ rhs: Self) -> Self { - var lhs = self - lhs.add(rhs) - return lhs - } -} + /// The big-endian representation of this integer. + /// + /// If necessary, the byte order of this value is reversed from the typical + /// byte order of this integer type. On a big-endian platform, for any + /// integer `x`, `x == x.bigEndian`. + /// + /// - SeeAlso: `littleEndian` + var bigEndian: Self { get } -extension FixedWidthInteger { - public mutating func add(_ rhs: Self) { - let (result, overflow) = self.addingWithOverflow(rhs) - self = result - } -} + /// The little-endian representation of this integer. + /// + /// If necessary, the byte order of this value is reversed from the typical + /// byte order of this integer type. On a little-endian platform, for any + /// integer `x`, `x == x.littleEndian`. + /// + /// - SeeAlso: `bigEndian` + var littleEndian: Self { get } -public struct Int8 { - public func addingWithOverflow(_ rhs: DoubleWidth) - -> (partialValue: DoubleWidth, overflow: ArithmeticOverflow) { - // efficient implementation - } + /// A representation of this integer with the byte order swapped. + var byteSwapped: Self { get } } ``` - -#### Masking arithmetic +#### Auxiliary protocols ```Swift -public func &* (lhs: T, rhs: T) -> T -public func &- (lhs: T, rhs: T) -> T -public func &+ (lhs: T, rhs: T) -> T +public protocol UnsignedInteger : BinaryInteger { + associatedtype Magnitude : BinaryInteger +} +public protocol SignedInteger : BinaryInteger, SignedNumber { + associatedtype Magnitude : BinaryInteger +} ``` -##### Implementation +### DoubleWidth\ -These operators call `WithOverflow` family of methods from `FixedWidthInteger` -and simply return the `partialValue` part, ignoring the possible overflow. +The `DoubleWidth` type allows to create wider fixed-width integer types from +the ones available in the standard library. -```Swift -public func &+ (lhs: T, rhs: T) -> T { - return lhs.addingWithOverflow(rhs).partialValue -} +Standard library currently provides fixed-width integer types of up to 64 bits. +A value of `DoubleWidth` will double the range of the underlying type and +implement all the `FixedWidthInteger` requirements. _Please note_ though that +the implementation will not necessarily be the most efficient one, so it would +not be a good idea to use `DoubleWidth` instead of a built-in `Int64`. -public struct Int8 { - public func addingWithOverflow(_ other: DoubleWidth) - -> (partialValue: DoubleWidth, overflow: ArithmeticOverflow) { - // efficient implementation - } +```swift +public enum DoubleWidth { + case .parts(high: T, low: T.Magnitude) + + public var high: T { get } + public var low: T.Magnitude { get } } ``` +Representing it as an `enum` instead of a simple struct allows to use it both +as a single value, as well as in destructuring matches. -#### Homogeneous comparison +```swift +let high = doubleWidthValue.high +let low = doubleWidthValue.low -```Swift -public func == (lhs:T, rhs: T) -> Bool -public func != (lhs:T, rhs: T) -> Bool -public func < (lhs: T, rhs: T) -> Bool -public func > (lhs: T, rhs: T) -> Bool -public func >= (lhs: T, rhs: T) -> Bool -public func <= (lhs: T, rhs: T) -> Bool -``` +// or -The implementation is similar to the homogeneous arithmetic operators above. +case let (high, low) = doubleWidthValue +``` -#### Heterogeneous comparison +### Extra operators -```Swift -public func == (lhs:T, rhs: U) -> Bool -public func != (lhs:T, rhs: U) -> Bool -public func < (lhs: T, rhs: U) -> Bool -public func > (lhs: T, rhs: U) -> Bool -public func >= (lhs: T, rhs: U) -> Bool -public func <= (lhs: T, rhs: U) -> Bool -``` +In addition to the operators described in the [protocols section](#protocols), +we also provide a few extensions that are not protocol requirements: -##### Implementation example +#### Heterogeneous shifts ```Swift -public func == (lhs:T, rhs: U) -> Bool { - return (lhs > 0) == (rhs > 0) - && T(extendingOrTruncating: rhs) == lhs - && U(extendingOrTruncating: lhs) == rhs -} - -extension FixedWidthInteger { - public init(extendingOrTruncating source: T) { - // converting `source` into the value of `Self` - } +extension BinaryInteger { + // Masking shifts + static func &>> (lhs: Self, rhs: Other) -> Self + static func &>>= (lhs: inout Self, rhs: Other) + static func &<< (lhs: Self, rhs: Other) -> Self + static func &<<= (lhs: inout Self, rhs: Other) + + // 'Smart' shifts + static func >> (lhs: Self, rhs: Other) -> Self + static func >>= (lhs: inout Self, rhs: Other) + static func << (lhs: Self, rhs: Other) -> Self + static func <<= (lhs: inout Self, rhs: Other) } ``` - -#### Shifts +#### Heterogeneous equality and comparison ```Swift -public func << (lhs: T, rhs: U) -> T -public func << (lhs: T, rhs: Word) -> T -public func <<= (lhs: inout T, rhs: U) -public func <<= (lhs: inout T, rhs: T) - -public func >> (lhs: T, rhs: U) -> T -public func >> (lhs: T, rhs: Word) -> T -public func >>= (lhs: inout T, rhs: T) -public func >>= (lhs: inout T, rhs: U) - -public func &<< (lhs: T, rhs: U) -> T -public func &<< (lhs: T, rhs: T) -> T -public func &<<= (lhs: inout T, rhs: U) -public func &<<= (lhs: inout T, rhs: T) - -public func &>> (lhs: T, rhs: U) -> T -public func &>> (lhs: T, rhs: T) -> T -public func &>>= (lhs: inout T, rhs: U) -public func &>>= (lhs: inout T, rhs: T) +extension BinaryInteger { + // Equality + static func == (lhs: Self, rhs: Other) -> Bool + static func != (lhs: Self, rhs: Other) -> Bool + + // Comparison + static func < (lhs: Self, rhs: Other) -> Bool + static func <= (lhs: Self, rhs: Other) -> Bool + static func > (lhs: Self, rhs: Other) -> Bool + static func >= (lhs: Self, rhs: Other) -> Bool +} ``` -##### Notes on the implementation of mixed-type shifts - -The implementation is similar to the heterogeneous comparison. The only -difference is that because shifting left truncates the high bits of fixed-width -integers, it is hard to define what a left shift would mean to an -arbitrary-precision integer. Therefore we only allow shifts where the left -operand conforms to the `FixedWidthInteger` protocol. The right operand, -however, can be an arbitrary `BinaryInteger`. - -#### Bitwise operations +#### Masking arithmetic ```Swift -public func | (lhs: T, rhs: T) -> T -public func |= (lhs: inout T, rhs: T) -public func & (lhs: T, rhs: T) -> T -public func &= (lhs: inout T, rhs: T) -public func ^ (lhs: T, rhs: T) -> T -public func ^= (lhs: inout T, rhs: T) +public func &* (lhs: T, rhs: T) -> T +public func &- (lhs: T, rhs: T) -> T +public func &+ (lhs: T, rhs: T) -> T ``` -## Impact on existing code - -The new model is designed to be a drop-in replacement for the current one. One -feature that has been deliberately removed is the concept of `the widest -integer type`, which will require a straightforward code migration. - -Existing code that does not implement its own integer types (or rely on the -existing protocol hierarchy in any other way) should not be affected. It may -be slightly wordier than necessary due to all the type conversions that are -no longer required, but will continue to work. - - ## Non-goals This proposal: @@ -609,5 +1114,29 @@ This proposal: - *DOES NOT* include the implementation of a `BigInt` type, but allows it to be implemented in the future. -- *DOES NOT* propose including a `DoubleWidth` integer type in the standard - library, but provides a proof-of-concept implementation. + +## Source compatibility + +The proposed change is designed to be as non-breaking as possible, and it has +been proven that it does not break code on concrete integer types. However, +there are still a few API breaking changes in the realm of generic code: + +* Integer protocols in Swift up to and including version 3 were not particularly +useful for generic programming, but were rather a means of sharing +implementation between conforming types. Therefore we believe the amount of code +that relied on these protocols is relatively small. The breakage can be further +reduced by introducing proper aliases for the removed protocols with deprecation +warnings. + +* Deprecation of the `BitwiseOperations` protocol. We find it hard to imagine a +type that conforms to this protocol, but is *not* a binary integer type. + +* Addition of 'smart' shifts will change the behavior of existing code. It will +still compile, but will be potentially less performant due to extra logic +involved. In a case, where this becomes a problem, newly introduced masking +shift operators can be used instead. Unfortunately, performance characteristics +of the code cannot be statically checked, and thus migration cannot be provided. + + +[se91]: 0091-improving-operators-in-protocols.md +[impl]: https://github.com/apple/swift/tree/new-integer-protocols \ No newline at end of file From 35942a3aa703692c120315ead13a7b730b8f86bf Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 17 Feb 2017 18:08:57 -0600 Subject: [PATCH 0170/4563] Proposal for adding custom dictionary .keys and .values collections (#555) * Add proposal for custom dictionary collections * Add missing syntax highlighting * Add note about ABI impacts * Revise proposal to use nested types Generic types can now have nested types inside them, so the `Keys` and `Values` collections can be properly nested inside `Dictionary`. --- ...00-dictionary-key-and-value-collections.md | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 proposals/0000-dictionary-key-and-value-collections.md diff --git a/proposals/0000-dictionary-key-and-value-collections.md b/proposals/0000-dictionary-key-and-value-collections.md new file mode 100644 index 0000000000..b54135a88f --- /dev/null +++ b/proposals/0000-dictionary-key-and-value-collections.md @@ -0,0 +1,176 @@ +# Provide Custom Collections for Dictionary Keys and Values + +- Proposal: [SE-0000](0000-dictionary-keys-and-values.md) +- Author: [Nate Cook](https://github.com/natecook1000) +- Review Manager: TBD +- Status: **Awaiting review** + +## Introduction + +This proposal addresses significant unexpected performance gaps when using dictionaries. It introduces type-specific collections for a `Dictionary` instance's `keys` and `values` properties. + +New collection types provide efficient key lookup and mutable access to dictionary values, allowing in-place updates and copy-on-write optimization of stored values. The addition of these new types impacts the standard library ABI, since we won't be able to use types aliases from the existing types for `keys` and `values`. + +Swift-evolution thread: [[Proposal Draft] Provide Custom Collections for Dictionary Keys and Values](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161010/027815.html) + + +## Motivation + +This proposal address two problems: + +* While a dictionary's `keys` collection is fine for iteration, its implementation is inefficient when looking up a specific key, because `LazyMapCollection` doesn't know how to forward lookups to the underlying dictionary storage. +* Dictionaries do not offer value-mutating APIs. The mutating key-based subscript wraps values in an `Optional`. This prevents types with copy-on-write optimizations from recognizing they are singly referenced. + +This proposal uses the following `[String: [Int]]` dictionary to demonstrate these problems: + +```swift +var dict = ["one": [1], "two": [2, 2], "three": [3, 3, 3]] +``` + +### Inefficient `dict.keys` Search + +Swift coders normally test key membership using `nil` checks or underscored optional bindings: + +```swift +if dict["one"] != nil { + // ... +} +if let _ = dict["one"] { + // ... +} +``` + +These approaches provide the expected performance of a dictionary lookup but they read neither well nor "Swifty". Checking the `keys` view reads much better but introduces a serious performance penalty: this approach requires a linear search through a dictionary's keys to find a match. + +```swift +if dict.keys.contains("one") { + // ... +} +``` + +A similar dynamic plays out when comparing `dict.index(forKey:)` and `dict.keys.index(of:)`. + +### Inefficient Value Mutation + +Dictionary values can be modified through the keyed subscript by direct reassignment or by using optional chaining. Both of these statements append `1` to the array stored by the key `"one"`: + +```swift +// Direct re-assignment +dict["one"] = (dict["one"] ?? []) + [1] + +// Optional chaining +dict["one"]?.append(1) +``` + +Both approaches present problems. The first is complex and hard to read. The second ignores the case where `"one"` is not a key in the dictionary, and is therefore less useful even if more streamlined. Furthermore, neither approach allows the array to grow in place—they introduce an unnecessary copy of the array's contents even though `dict` is the sole holder of its storage. + +Adding mutation to a dictionary's index-based subscripting isn't possible. Changing a key stored at a particular index would almost certainly modify its hash value, rendering the index incorrect. This violates the requirements of the `MutableCollection` protocol. + +## Proposed Solution + +This proposal adds custom collections for the `keys` and `values` dictionary properties. This follows the example set by `String`, which presents multiple views of its contents. A new `Keys` collection introduces efficient key lookup, while a new `Values` collection provides a mutable collection interface to dictionary values. Both new collections are nested in the `Dictionary` type. + +These changes make the simple approach for testing whether a dictionary contains a key an efficient one: + +```swift +// Fast, not slow +if dict.keys.contains("one") { + // ... +} +``` + +As a mutable collection, `values` enables modification without copies or clumsy code: + +```swift +if let i = dict.index(forKey: "one") { + dict.values[i].append(1) // no copy here +} else { + dict["one"] = [1] +} +``` + +Both the `keys` and `values` collections share the same index type as `Dictionary`. This allows the above sample to be rewritten as: + +```swift +// Using `dict.keys.index(of:)` +if let i = dict.keys.index(of: "one") { + dict.values[i].append(1) +} else { + dict["one"] = [1] +} +``` + +## Detailed design + +* The standard library introduces two new collection types: `Dictionary.Keys` and `Dictionary.Values`. +* A `Dictionary`'s `keys` and `values` properties change from `LazyMapCollection` to these new types. +* The new collection types are not directly constructable. They are presented only as views into a dictionary. + +```swift +struct Dictionary: ... { + /// A collection view of a dictionary's keys. + struct Keys: Collection { + subscript(i: Index) -> Key { get } + // Other `Collection` requirements + } + + /// A mutable collection view of a dictionary's values. + struct Values: MutableCollection { + subscript(i: Index) -> Value { get set } + // Other `Collection` requirements + } + + var keys: Keys { get } + var values: Values { get set } + // Remaining Dictionary declarations +} +``` + +## Impact on existing code + +The performance improvements of using the new `Dictionary.Keys` type and the mutability of the `Dictionary.Values` collection are both additive in nature. + +Most uses of these properties are transitory in nature. Adopting this proposal should not produce a major impact on existing code. The only impact on existing code exists where a program explicitly specifies the type of a dictionary's `keys` or `values` property. In those cases, the fix would be to change the specified type. + + +## Alternatives considered + +1. Add additional compiler features that manage mutation through existing key-based subscripting without the copy-on-write problems of the current implementation. This could potentially be handled by upcoming changes to copy-on-write semantics and/or inout access. + +2. Provide new APIs for updating dictionary values with a default value, eliminating the double-lookup for a missing key. The approach outlined in this proposal provides a way to remove one kind of double-lookup (mutating a value that exists) but doesn't eliminate all of them (in particular, checking for the existence of a key before adding). + + These could be written in a variety of ways: + + ```swift + // Using a 'SearchState' type to remember key position + dict.entries["one"] + .withDefault([]) + .append(1) + + // Using a two-argument subscript + dict["one", withDefault: []].append(1) + + // Using a closure with an inout argument + dict.withValue(forKey: "one") { (v: inout Value?) in + if v != nil { + v!.append(1) + } else { + v = [1] + } + } + ``` + +3. Restructure `Dictionary`'s collection interface such that the `Element` type of a dictionary is its `Value` type instead of a `(Key, Value)` tuple. That would allow the `Dictionary` type itself to be a mutable collection with an `entries` or `keysAndValues` view similar to the current collection interface. This interface might look a bit like this: + + ```swift + let valuesOnly = Array(dict) + // [[2, 2], [1], [3, 3, 3]] + let keysAndValues = Array(dict.entries) + // [("two", [2, 2]), ("one", [1]), ("three", [3, 3, 3])] + + let foo = dict["one"] + // Optional([1]) + + let i = dict.keys.index(of: "one")! + dict[i].append(1) + ``` From 7e5290be87de2b410a2c91227a3dce5b6a81e1cd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 17 Feb 2017 16:10:33 -0800 Subject: [PATCH 0171/4563] Initiate review of SE-0154 "Provide Custom Collections for Dictionary Keys and Values" --- ...ions.md => 0154-dictionary-key-and-value-collections.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{0000-dictionary-key-and-value-collections.md => 0154-dictionary-key-and-value-collections.md} (97%) diff --git a/proposals/0000-dictionary-key-and-value-collections.md b/proposals/0154-dictionary-key-and-value-collections.md similarity index 97% rename from proposals/0000-dictionary-key-and-value-collections.md rename to proposals/0154-dictionary-key-and-value-collections.md index b54135a88f..fc77fadbe6 100644 --- a/proposals/0000-dictionary-key-and-value-collections.md +++ b/proposals/0154-dictionary-key-and-value-collections.md @@ -1,9 +1,9 @@ # Provide Custom Collections for Dictionary Keys and Values -- Proposal: [SE-0000](0000-dictionary-keys-and-values.md) +- Proposal: [SE-0154](0154-dictionary-keys-and-values.md) - Author: [Nate Cook](https://github.com/natecook1000) -- Review Manager: TBD -- Status: **Awaiting review** +- Review Manager: [Doug Gregor](https://github.com/DougGregor) +- Status: **Active review (February 17...22, 2017)** ## Introduction From b87c106c1da285f49a2c3fd7e2de83d4d9263efb Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 17 Feb 2017 16:28:51 -0800 Subject: [PATCH 0172/4563] Fix link --- proposals/0154-dictionary-key-and-value-collections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0154-dictionary-key-and-value-collections.md b/proposals/0154-dictionary-key-and-value-collections.md index fc77fadbe6..4419fdee58 100644 --- a/proposals/0154-dictionary-key-and-value-collections.md +++ b/proposals/0154-dictionary-key-and-value-collections.md @@ -1,6 +1,6 @@ # Provide Custom Collections for Dictionary Keys and Values -- Proposal: [SE-0154](0154-dictionary-keys-and-values.md) +- Proposal: [SE-0154](0154-dictionary-key-and-value-collections.md) - Author: [Nate Cook](https://github.com/natecook1000) - Review Manager: [Doug Gregor](https://github.com/DougGregor) - Status: **Active review (February 17...22, 2017)** From aff6e5aaabc3eb1ab661af78eaf2e338dc6e3aa6 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 17 Feb 2017 22:20:23 -0500 Subject: [PATCH 0173/4563] Initiate review of SE-0155: Normalize Enum Case Representation. --- ...n.md => 0155-normalize-enum-case-representation.md} | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) rename proposals/{NNNN-Normalize-Enum-Case-Representation.md => 0155-normalize-enum-case-representation.md} (97%) diff --git a/proposals/NNNN-Normalize-Enum-Case-Representation.md b/proposals/0155-normalize-enum-case-representation.md similarity index 97% rename from proposals/NNNN-Normalize-Enum-Case-Representation.md rename to proposals/0155-normalize-enum-case-representation.md index 42e3ad1dc1..ae872fe8a7 100644 --- a/proposals/NNNN-Normalize-Enum-Case-Representation.md +++ b/proposals/0155-normalize-enum-case-representation.md @@ -1,9 +1,9 @@ # Normalize Enum Case Representation -* Proposal: [SE-NNNN][] +* Proposal: [SE-0155][] * Authors: [Daniel Duan][], [Joe Groff][] -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [John McCall][] +* Status: **Active review (February 17...26, 2017)** ## Introduction @@ -30,6 +30,7 @@ await.** base-name and all argument labels. As an illustration, one can invoke a function with its full name: + ```swift func f(x: Int, y: Int) {} f(x: y:)(0, 0) // Okay, this is equivalent to f(x: 0, y: 0) @@ -240,6 +241,7 @@ the case's declaration. [SE-0111]: https://github.com/apple/swift-evolution/blob/master/proposals/0111-remove-arg-label-type-significance.md [Daniel Duan]: https://github.com/dduan [Joe Groff]: https://github.com/jckarter -[SE-NNNN]: NNNN-Normalize-Enum-Case-Representation.md +[SE-0155]: 0155-normalize-enum-case-representation.md [TJs comment]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030614.html [SE Thread]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030477.html +[John McCall]: https://github.com/rjmccall From 22ad2faa4a64e063fd5ddd20dd49c5c92a3d85c2 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 18 Feb 2017 10:54:32 +0000 Subject: [PATCH 0174/4563] [SE-0147 ... SE-0152] Add the Rationale links (#612) --- proposals/0147-move-unsafe-initialize-from.md | 3 ++- proposals/0148-generic-subscripts.md | 6 +++--- proposals/0149-package-manager-top-of-tree.md | 4 ++-- proposals/0150-package-manager-branch-support.md | 4 ++-- ...ackage-manager-swift-language-compatibility-version.md | 8 ++++---- proposals/0152-package-manager-tools-version.md | 6 +++--- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/proposals/0147-move-unsafe-initialize-from.md b/proposals/0147-move-unsafe-initialize-from.md index b04a9ac5ab..5596fdb4b1 100644 --- a/proposals/0147-move-unsafe-initialize-from.md +++ b/proposals/0147-move-unsafe-initialize-from.md @@ -1,9 +1,10 @@ # Move UnsafeMutablePointer.initialize(from:) to UnsafeMutableBufferPointer * Proposal: [SE-0147](0147-move-unsafe-initialize-from.md) -* Authors: [Ben Cohen](https://github.com/airspeedswift) +* Author: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Accepted** +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170102/029945.html) ## Introduction diff --git a/proposals/0148-generic-subscripts.md b/proposals/0148-generic-subscripts.md index be676b009e..c346dfb578 100644 --- a/proposals/0148-generic-subscripts.md +++ b/proposals/0148-generic-subscripts.md @@ -1,11 +1,11 @@ # Generic Subscripts * Proposal: [SE-0148](0148-generic-subscripts.md) -* Authors: [Chris Eidhof](http://github.com/chriseidhof/) +* Author: [Chris Eidhof](http://github.com/chriseidhof/) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Accepted** - -* Bugs: [SR-115](https://bugs.swift.org/browse/SR-115?jql=text%20~%20%22Generic%20subscript%22) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170123/031048.html) +* Bug: [SR-115](https://bugs.swift.org/browse/SR-115) ## Introduction diff --git a/proposals/0149-package-manager-top-of-tree.md b/proposals/0149-package-manager-top-of-tree.md index 9e4bb46e5a..e26a1dbd35 100644 --- a/proposals/0149-package-manager-top-of-tree.md +++ b/proposals/0149-package-manager-top-of-tree.md @@ -4,8 +4,8 @@ * Author: [Boris Bügling](https://github.com/neonichu) * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) * Status: **Accepted** - -* Bugs: [SR-3709](https://bugs.swift.org/browse/SR-3709) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170130/031427.html) +* Bug: [SR-3709](https://bugs.swift.org/browse/SR-3709) ## Introduction diff --git a/proposals/0150-package-manager-branch-support.md b/proposals/0150-package-manager-branch-support.md index 1d42193ba2..d11a1c178d 100644 --- a/proposals/0150-package-manager-branch-support.md +++ b/proposals/0150-package-manager-branch-support.md @@ -4,8 +4,8 @@ * Author: [Boris Bügling](https://github.com/neonichu) * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) * Status: **Accepted** - -* Bugs: [SR-666](https://bugs.swift.org/browse/SR-666) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170130/031428.html) +* Bug: [SR-666](https://bugs.swift.org/browse/SR-666) ## Introduction diff --git a/proposals/0151-package-manager-swift-language-compatibility-version.md b/proposals/0151-package-manager-swift-language-compatibility-version.md index 9da02d0739..8bb92994e0 100644 --- a/proposals/0151-package-manager-swift-language-compatibility-version.md +++ b/proposals/0151-package-manager-swift-language-compatibility-version.md @@ -1,11 +1,11 @@ # Package Manager Swift Language Compatibility Version * Proposal: [SE-0151](0151-package-manager-swift-language-compatibility-version.md) -* Author: [Daniel Dunbar](https://github.com/ddunbar), [Rick Ballard](http://github.com/rballard) -* Review Manager: Anders Bertelrud +* Authors: [Daniel Dunbar](https://github.com/ddunbar), [Rick Ballard](http://github.com/rballard) +* Review Manager: [Anders Bertelrud](https://github.com/abertelrud) * Status: **Implemented (Swift 3.1)** - -* Bugs: [SR-3964](https://bugs.swift.org/browse/SR-3964) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170213/032021.html) +* Bug: [SR-3964](https://bugs.swift.org/browse/SR-3964) ## Introduction diff --git a/proposals/0152-package-manager-tools-version.md b/proposals/0152-package-manager-tools-version.md index 62eb4a6fd5..6da53ccdc7 100644 --- a/proposals/0152-package-manager-tools-version.md +++ b/proposals/0152-package-manager-tools-version.md @@ -2,10 +2,10 @@ * Proposal: [SE-0152](0152-package-manager-tools-version.md) * Author: [Rick Ballard](https://github.com/rballard) -* Review Manager: Anders Bertelrud +* Review Manager: [Anders Bertelrud](https://github.com/abertelrud) * Status: **Implemented (Swift 3.1)** - -* Bugs: [SR-3965](https://bugs.swift.org/browse/SR-3965) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170213/032023.html) +* Bug: [SR-3965](https://bugs.swift.org/browse/SR-3965) ## Introduction From a680476e3ad4cbebb0312201ea5bc4e54496dd43 Mon Sep 17 00:00:00 2001 From: Patrick Balestra Date: Sun, 19 Feb 2017 05:50:41 +0000 Subject: [PATCH 0175/4563] Update 0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md (#611) --- ...ompensate-for-the-inconsistency-of-nscopyings-behaviour.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md index f1b6fafdd4..c135e88d2b 100644 --- a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md +++ b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md @@ -66,7 +66,7 @@ class Person: NSObject, NSCopying { ``` swift let johnAppleseed = Person( firstName: "John", lastName: "Appleseed", job: "CEO" ) -var refJohnAppleseed = johnAppleseed // assigning wihtout copying semantic +var refJohnAppleseed = johnAppleseed // assigning without copying semantic refJohnAppleseed.job = "Engineer" @@ -123,7 +123,7 @@ class Department: NSObject { } ``` -`Department`'s disignated initializer receives an external instance of `Person` and expects to assign its deeply-copied value to `self.employee` property. +`Department`'s designated initializer receives an external instance of `Person` and expects to assign its deeply-copied value to `self.employee` property. ``` swift let isaacNewton = Person( firstName: "Isaac", lastName: "Newton", job: "Mathematician" ) From 620d573b0c81517128355affa18718b088d41eb1 Mon Sep 17 00:00:00 2001 From: Torin Kwok Date: Sun, 19 Feb 2017 14:16:49 +0800 Subject: [PATCH 0176/4563] Update 0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md (#610) Priority of "Solution: Compiler magic" has been lowered to `Alternatives Considered` because the potential misleading mentioned by reviewers. --- ...te-for-the-inconsistency-of-nscopyings-behaviour.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md index c135e88d2b..e8e1ce0977 100644 --- a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md +++ b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md @@ -185,10 +185,6 @@ Speaking of Swift, however, there is no stuff like `->` operator to access ivar ## Proposed solution -### Compiler magic - -Do the compiler magic to call `copy( with: )` in the initializer so that `@NSCopying` attribute no longer subjects to the fact that setter methods would not be invoked in initializers. **Copying should always take place after a property has been declared as `@NSCopying`**. It seems like the most direct way to maintain the `@NSCopying` contract without changing the underlying direct-storage model. - ### Compile-time checking Have compiler emit a **compile-time error or warning** if developers are performing an assignment operation from within an initializer between a property declared as `@NSCopying` and an instance of a `` protocol conforming class. Also, speaking of GUI integrated development environments such as Xcode, leaving this kind of error or warning **FIXABLE** would be needed in order to make them can be quickly fixed by both IDEs and migrator tools through simply appending `.copy() as! AutoInferredClassType`. @@ -224,4 +220,8 @@ The proposal doesn't change the ABI of existing language features. ## Alternatives Considered -There is no alternatives considered at this time. +### Compiler magic + +Do the compiler magic to call `copy( with: )` in the initializer so that `@NSCopying` attribute no longer subjects to the fact that setter methods would not be invoked in initializers. **Copying should always take place after a property has been declared as `@NSCopying`**. It seems like the most direct way to maintain the `@NSCopying` contract without changing the underlying direct-storage model. + +The compiler magic is able to fix the problem but a developer who is new to Swift would continue to be confused if `@NSCopying` had magic but `didSet` and other behaviors did not. From 43dae663737b92526bf79b5c9c778851994638c3 Mon Sep 17 00:00:00 2001 From: Andy Bargh Date: Mon, 20 Feb 2017 19:19:32 +0000 Subject: [PATCH 0177/4563] Fix typo (#614) --- proposals/0155-normalize-enum-case-representation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0155-normalize-enum-case-representation.md b/proposals/0155-normalize-enum-case-representation.md index ae872fe8a7..8865cbddbf 100644 --- a/proposals/0155-normalize-enum-case-representation.md +++ b/proposals/0155-normalize-enum-case-representation.md @@ -222,7 +222,7 @@ constrained by having to be part of a tuple. To maintain maximum source compatibility, we could introduce a rule that matches all associated values to a labeled tuple. As T.J. Usiyan -[pointed out][TJs comment], implementation of the equality protocal would be +[pointed out][TJs comment], implementation of the equality protocol would be simplified due to tuple's conformance to `Equatable`. This feature may still be introduced with alternative syntax (perhaps related to splats) later without source-breakage. And the need to implement `Equatable` may also disappear with From f4eb9605e8953c05c3d5d3df17ff2275d8c43ccc Mon Sep 17 00:00:00 2001 From: Andy Bargh Date: Mon, 20 Feb 2017 19:33:37 +0000 Subject: [PATCH 0178/4563] Fix typos (#615) --- proposals/0155-normalize-enum-case-representation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0155-normalize-enum-case-representation.md b/proposals/0155-normalize-enum-case-representation.md index 8865cbddbf..a3b7923bcb 100644 --- a/proposals/0155-normalize-enum-case-representation.md +++ b/proposals/0155-normalize-enum-case-representation.md @@ -226,7 +226,7 @@ all associated values to a labeled tuple. As T.J. Usiyan simplified due to tuple's conformance to `Equatable`. This feature may still be introduced with alternative syntax (perhaps related to splats) later without source-breakage. And the need to implement `Equatable` may also disappear with -auto-devriving for `Equitable` conformance. +auto-deriving for `Equatable` conformance. A syntax that did stay for source compatibility is allowing `()` in patterns that match enum cases without associated values: From cd7b716fcff0063bda55577de4bae5d4f81f6325 Mon Sep 17 00:00:00 2001 From: Austin Zheng Date: Mon, 20 Feb 2017 12:59:18 -0800 Subject: [PATCH 0179/4563] Make initial feedback changes --- .../XXXX-recursive-protocol-constraints.md | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/proposals/XXXX-recursive-protocol-constraints.md b/proposals/XXXX-recursive-protocol-constraints.md index 0fd7426ced..fce6c861e7 100644 --- a/proposals/XXXX-recursive-protocol-constraints.md +++ b/proposals/XXXX-recursive-protocol-constraints.md @@ -102,16 +102,7 @@ protocols in an incorrect manner, might cease to be accepted by the compiler aft ## Detailed design -The following standard library types and protocols will be removed: - -* `_BidirectionalIndexable` -* `_Indexable` -* `_IndexableBase` -* `_MutableIndexable` -* `_RandomAccessIndexable` -* `_RangeReplaceableIndexable` - -The following standard library protocols and types will change. +The following standard library protocols and types will change in order to support recursive protocol constraints. Note that since the specific collection types conform to `Collection`, and `Collection` refines `Sequence`, not all the constraints need to be defined on every collection-related associated type. @@ -121,10 +112,6 @@ Default values of all changed associated types remain the same, unless explicitl All "Change associated type" entries reflect the complete, final state of the associated type definition, including removal of underscored protocols and addition of any new constraints. -### `Any*Collection` (all variants) - -* Remove all `C.Subsequence` and `C.Indices` constraints from public `init` - ### `Arithmetic` * Change associated type: `associatedtype Magnitude : Arithmetic` @@ -138,8 +125,8 @@ removal of underscored protocols and addition of any new constraints. ### `Collection` * Remove conformance to `_Indexable` -* Change associated type: `associatedtype SubSequence : Collection where SubSequence.Index == Index, SubSequence.Indices == Indices` -* Change associated type: `associatedtype Indices : Collection where Indices.Iterator.Element == Index, Indices.Index == Index, Indices.SubSequence == Indices` +* Change associated type: `associatedtype SubSequence : Collection where SubSequence.Index == Index` +* Change associated type: `associatedtype Indices : Collection where Indices.Iterator.Element == Index, Indices.Index == Index` ### `Default*Indices` (all variants) @@ -157,11 +144,6 @@ removal of underscored protocols and addition of any new constraints. * Add default associated type conformance: `typealias SubSequence = ${Self}` -### `Mirror` - -* Remove all `C.Subsequence` and `C.Indices` constraints from `init(_ subject: Subject, children: C, displayStyle: DisplayStyle?, ancestorRepresentation: AncestorRepresentation)` -* Remove all `C.Subsequence` and `C.Indices` constraints from `init(_ subject: Subject, unlabeledChildren: C, displayStyle: DisplayStyle?, ancestorRepresentation: AncestorRepresentation)` - ### `MutableCollection` * Change associated type: `associatedtype SubSequence : MutableCollection` From 5a72aef98cfc4ea9972bc97409f3291fc2234fa3 Mon Sep 17 00:00:00 2001 From: Austin Zheng Date: Mon, 20 Feb 2017 13:06:58 -0800 Subject: [PATCH 0180/4563] Replace example with discussion about Foundation.Data --- .../XXXX-recursive-protocol-constraints.md | 64 ++----------------- 1 file changed, 5 insertions(+), 59 deletions(-) diff --git a/proposals/XXXX-recursive-protocol-constraints.md b/proposals/XXXX-recursive-protocol-constraints.md index fce6c861e7..c06a355d29 100644 --- a/proposals/XXXX-recursive-protocol-constraints.md +++ b/proposals/XXXX-recursive-protocol-constraints.md @@ -171,65 +171,11 @@ From a source compatibility perspective, this is a purely additive change if the is possible that users may have written code which defines semantically incorrect associated types, which the compiler now rejects because of the additional constraints. We do not consider this scenario "source-breaking". -The following source listing is an example of code accepted by the Swift 3.0.1 compiler which is nevertheless -semantically incorrect, and would be rejected by a Swift compiler implementing this change. - -```swift -struct BadSequence : Sequence, IteratorProtocol { - typealias Element = Int - // BadSubSequence.Element isn't equal to BadSequence.Element; this is incorrect. - typealias SubSequence = BadSubSequence - - var idx = 0 - - // Incorrect implementations of protocol requirements. - public func prefix(_ maxLength: Int) -> BadSubSequence { return BadSubSequence() } - public func suffix(_ maxLength: Int) -> BadSubSequence { return BadSubSequence() } - public func dropFirst(_ n: Int) -> BadSubSequence { return BadSubSequence() } - public func dropLast(_ n: Int) -> BadSubSequence { return BadSubSequence() } - public func split(maxSplits: Int, - omittingEmptySubsequences: Bool, - whereSeparator isSeparator: (Int) throws -> Bool) rethrows -> [BadSubSequence] { - return [BadSubSequence()] - } - - mutating func next() -> Int? { - if idx == 10 { - return nil - } - let value = idx * 10 - idx = idx + 1 - return value - } - - func makeIterator() -> BadSequence { - var aCopy = self - aCopy.idx = 0 - return aCopy - } -} - -struct BadSubSequence : Sequence, IteratorProtocol { - typealias Element = String - - var idx = 0 - - mutating func next() -> String? { - if idx == 10 { - return nil - } - let value = "\(idx)" - idx = idx + 1 - return value - } - - func makeIterator() -> BadSubSequence { - var aCopy = self - aCopy.idx = 0 - return aCopy - } -} -``` +An example of code that currently compiles but is semantically invalid is an implementation of a range-replacable +collection's subsequence that isn't itself range-replaceable. This is a constraint that cannot be enforced by the compiler +without this change. For some time, the `Data` type in Foundation violated this constraint; user-written code that is +similarly problematic will cease to compile using a Swift toolchain that includes these standard library and compiler +changes. ## Impact on ABI stability From 3bd39113e128361d70be7f718e5428a3db3f2c86 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Mon, 20 Feb 2017 13:08:53 -0800 Subject: [PATCH 0181/4563] [Status] Fix baseline of detail text labels. Thanks @jtbandes! --- index.css | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/index.css b/index.css index 9fa86128b5..6c00d78c32 100644 --- a/index.css +++ b/index.css @@ -234,6 +234,7 @@ section ul, section li { .proposal-detail { break-inside: avoid; + display: flex; } .proposal-detail-label { @@ -241,13 +242,11 @@ section ul, section li { font-weight: 200; display: inline-block; padding-right: 6px; - vertical-align: top; } .proposal-detail-value { display: inline-block; color: #333; - padding-top: 1.4px; } .proposal-title a:link, @@ -552,6 +551,10 @@ section ul, section li { flex-direction: column; } + .proposal-detail { + display: block; + } + .proposal-detail-label, .proposal-detail-value { display: inline; } From a8483427d561c103894c17cfb8fc579498fcd76d Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Mon, 20 Feb 2017 19:48:42 -0800 Subject: [PATCH 0182/4563] Fix link target typo in SE-0066. --- proposals/0066-standardize-function-type-syntax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0066-standardize-function-type-syntax.md b/proposals/0066-standardize-function-type-syntax.md index 950867a313..58f092627e 100644 --- a/proposals/0066-standardize-function-type-syntax.md +++ b/proposals/0066-standardize-function-type-syntax.md @@ -2,7 +2,7 @@ * Proposal: [SE-0066](0066-standardize-function-type-syntax.md) * Author: [Chris Lattner](https://github.com/lattner) -* Review Manager: [Doug Gregor](https://github/com/DougGregor) +* Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000138.html) * Commit: [apple/swift@3d2b5bc](https://github.com/apple/swift/commit/3d2b5bcc5350e1dea2ed8a0a95cd12ff5c760f24) From 891d5d4c54e7eab5271418b2b6c720402a56ebce Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 20 Feb 2017 20:48:28 -0800 Subject: [PATCH 0183/4563] Add stagae 2 goals/priorities. --- README.md | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8c6a8ab8da..63c09b0f8e 100644 --- a/README.md +++ b/README.md @@ -31,16 +31,13 @@ source stability for Swift 3 code and to provide ABI stability for the Swift standard library. To that end, the Swift 4 release will be divided into two stages. -Stage 1 focuses on the essentials required for source and ABI +Stage 1 focused on the essentials required for source and ABI stability. Features that don't fundamentally change the ABI of existing language features or imply an ABI-breaking change to the -standard library will not be considered in this stage. **Swift 4 is -currently only considering proposals that fit in Stage 1**. +standard library will not be considered in this stage. -Stage 2 will commence once the implementation work on the Stage 1 -features is cresting, and can contain a few other large and small -features. We expect that stage 2 will commence some time in Spring -2017. +Stage 2 opened in mid-February and extends until April 1, 2017, after +which proposals will be held for a later version of Swift. The high-priority features supporting Stage 1's source and ABI stability goals are: @@ -94,6 +91,40 @@ stability goals are: need a comprehensive design to understand how it will change the ABI. +Swift 4 stage 2 builds on the goals of stage 1. It differs in that +stage 2 proposals may include some additive changes and changes to +existing features that don't affect the ABI. There are a few focus +areas for Swift 4 stage 2: + +* Stage 1 proposals: Any proposal that would have been eligible for + stage 1 is a priority for stage 2. + +* Source-breaking changes: The Swift 4 compiler will provide a + source-compatibility mode to allow existing Swift 3 sources to + compile, but source-breaking changes can manifest in "Swift 4" + mode. That said, changes to fundamental parts of Swift's syntax or + standard library APIs that breaks source code are better + front-loaded into Swift 4 than delayed until later + releases. Relative to Swift 3, the bar for such changes is + significantly higher: + + * The existing syntax/API being changed must be actively harmful. + * The new syntax/API must clearly be better and not conflict with existing Swift syntax. + * There must be a reasonably automatable migration path for existing code. + +* Improvements to existing Standard Library facilities: Additive + changes that improve existing standard library facilities can be + considered. With standard library additions in particular, proposals + that provide corresponding implementations are preferred. Potential + focus areas for improvement include collections (e.g., new + collection algorithms) and improvements to the ergonomics of + `Dictionary`. + +* Foundation improvements: We anticipate proposing some targeted + improvements to Foundation API to continue the goal of making the + Cocoa SDK work seamlessly in Swift. Details on the specific goals + will be provided as we get started on Swift 4 stage 2. + ## Previous releases * [Swift 3.0](releases/swift-3_0.md) - Released on September 13, 2016 From dff1ab57643e75f0a0bdb533cd9ef42a2d3c29d0 Mon Sep 17 00:00:00 2001 From: Maxim Moiseev Date: Tue, 21 Feb 2017 13:30:59 -0800 Subject: [PATCH 0184/4563] [SE-0104] Removing / from Number (#618) --- proposals/0104-improved-integers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0104-improved-integers.md b/proposals/0104-improved-integers.md index 4e510a3344..52519265a3 100644 --- a/proposals/0104-improved-integers.md +++ b/proposals/0104-improved-integers.md @@ -65,7 +65,7 @@ more easily extensible. ~~~~ +--------------+ +-------------+ +------>+ Number | | Comparable | - | | (+,-,*,/) | | (==,<,>,...)| + | | (+,-,*) | | (==,<,>,...)| | +-------------++ +---+---------+ | ^ ^ +-------+------------+ | | From 4cda6176876e4630629b7afdd586832f4320eb35 Mon Sep 17 00:00:00 2001 From: Maxim Moiseev Date: Tue, 21 Feb 2017 15:33:35 -0800 Subject: [PATCH 0185/4563] [SE-0104] Fixing a doc comment (#619) --- proposals/0104-improved-integers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0104-improved-integers.md b/proposals/0104-improved-integers.md index 52519265a3..ac4b2c62c6 100644 --- a/proposals/0104-improved-integers.md +++ b/proposals/0104-improved-integers.md @@ -251,7 +251,7 @@ public protocol Number : Equatable, ExpressibleByIntegerLiteral { /// let y = Int8(exactly: 1_000) /// // y == nil /// - /// - Parameter source: A floating-point value to convert to an integer. + /// - Parameter source: A BinaryInteger value to convert to an integer. init?(exactly source: T) /// A type that can represent the absolute value of any possible value of the From 2420a2dfa69569c18fdfea8c613e6c8789d4e3a2 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 22 Feb 2017 09:43:33 -0800 Subject: [PATCH 0186/4563] SE-0104 is under review again. --- proposals/0104-improved-integers.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0104-improved-integers.md b/proposals/0104-improved-integers.md index ac4b2c62c6..15abea3efd 100644 --- a/proposals/0104-improved-integers.md +++ b/proposals/0104-improved-integers.md @@ -2,8 +2,8 @@ * Proposal: [SE-0104](0104-improved-integers.md) * Authors: [Dave Abrahams](https://github.com/dabrahams), [Maxim Moiseev](https://github.com/moiseev) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Joe Groff](https://github.com/jckarter) +* Status: **Active review (February 17...25, 2017)** * Bug: [SR-3196](https://bugs.swift.org/browse/SR-3196) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/0440700fc555a6c72abb4af807c8b79fb1bec592/proposals/0104-improved-integers.md), [2](https://github.com/apple/swift-evolution/blob/957ab545e05adb94507792e7871b38e34b56a0a5/proposals/0104-improved-integers.md). * Discussion on swift-evolution: [here](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170109/030191.html). @@ -1139,4 +1139,4 @@ of the code cannot be statically checked, and thus migration cannot be provided. [se91]: 0091-improving-operators-in-protocols.md -[impl]: https://github.com/apple/swift/tree/new-integer-protocols \ No newline at end of file +[impl]: https://github.com/apple/swift/tree/new-integer-protocols From 599016b05859156c74bbd8172d8b5df7f4bf3e0e Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Wed, 22 Feb 2017 23:43:06 +0530 Subject: [PATCH 0187/4563] Update status of SE-0149 --- proposals/0149-package-manager-top-of-tree.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0149-package-manager-top-of-tree.md b/proposals/0149-package-manager-top-of-tree.md index e26a1dbd35..cef6819403 100644 --- a/proposals/0149-package-manager-top-of-tree.md +++ b/proposals/0149-package-manager-top-of-tree.md @@ -3,7 +3,7 @@ * Proposal: [SE-0149](0149-package-manager-top-of-tree.md) * Author: [Boris Bügling](https://github.com/neonichu) * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) -* Status: **Accepted** +* Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170130/031427.html) * Bug: [SR-3709](https://bugs.swift.org/browse/SR-3709) From 43ca098355762014f53e1b54e02d2f6a01253385 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Feb 2017 14:29:11 -0500 Subject: [PATCH 0188/4563] Return SE-0155 for revision. --- proposals/0155-normalize-enum-case-representation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0155-normalize-enum-case-representation.md b/proposals/0155-normalize-enum-case-representation.md index a3b7923bcb..100bef31be 100644 --- a/proposals/0155-normalize-enum-case-representation.md +++ b/proposals/0155-normalize-enum-case-representation.md @@ -3,7 +3,7 @@ * Proposal: [SE-0155][] * Authors: [Daniel Duan][], [Joe Groff][] * Review Manager: [John McCall][] -* Status: **Active review (February 17...26, 2017)** +* Status: **Returned for revision** ## Introduction From 5511db7c6cca8a7fc6123f2f7bcd0360f084b960 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 24 Feb 2017 22:16:09 -0800 Subject: [PATCH 0189/4563] Accept SE-0154: Provide Custom Collections for Dictionary Keys and Values --- proposals/0154-dictionary-key-and-value-collections.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0154-dictionary-key-and-value-collections.md b/proposals/0154-dictionary-key-and-value-collections.md index 4419fdee58..bdac78c1d5 100644 --- a/proposals/0154-dictionary-key-and-value-collections.md +++ b/proposals/0154-dictionary-key-and-value-collections.md @@ -3,7 +3,8 @@ - Proposal: [SE-0154](0154-dictionary-key-and-value-collections.md) - Author: [Nate Cook](https://github.com/natecook1000) - Review Manager: [Doug Gregor](https://github.com/DougGregor) -- Status: **Active review (February 17...22, 2017)** +- Status: **Accepted** +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170220/033159.html) ## Introduction From 53c1b3bc60152390452962060d106a924566e838 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Sat, 25 Feb 2017 19:16:48 -0800 Subject: [PATCH 0190/4563] [Status] Note proposals' full statuses when returned for revision. --- index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/index.js b/index.js index 82456df097..0b0be54f69 100644 --- a/index.js +++ b/index.js @@ -303,6 +303,10 @@ function renderBody () { detailNodes.push(renderReviewPeriod(proposal.status)) } + if (state === '.returnedForRevision') { + detailNodes.push(renderStatus(proposal.status)) + } + var details = html('div', { className: 'proposal-details' }, detailNodes) proposalBody.querySelector('.proposal-content').appendChild(details) From 1cfc5f7d1510bb3089efa9c50f1546a4f10c36a3 Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Tue, 28 Feb 2017 22:28:06 +0530 Subject: [PATCH 0191/4563] Update status for SE-0150 --- proposals/0150-package-manager-branch-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0150-package-manager-branch-support.md b/proposals/0150-package-manager-branch-support.md index d11a1c178d..4756651ffa 100644 --- a/proposals/0150-package-manager-branch-support.md +++ b/proposals/0150-package-manager-branch-support.md @@ -3,7 +3,7 @@ * Proposal: [SE-0150](0150-package-manager-branch-support.md) * Author: [Boris Bügling](https://github.com/neonichu) * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) -* Status: **Accepted** +* Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170130/031428.html) * Bug: [SR-666](https://bugs.swift.org/browse/SR-666) From 5569ea2c633963920a893554bce64a57dd61b9dc Mon Sep 17 00:00:00 2001 From: David Hart Date: Tue, 28 Feb 2017 22:06:16 +0100 Subject: [PATCH 0192/4563] Subclass existentials (#607) * first draft * Severe rewrite of the proposal to take feedback into account * fixed typo and formating * new attempt to fix formatting * Applied corrections and chose one solution for class/AnyObject * added a section on inheritance clause * aded mailing list disucssion link --- proposals/XXXX-subclass-existentials.md | 185 ++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 proposals/XXXX-subclass-existentials.md diff --git a/proposals/XXXX-subclass-existentials.md b/proposals/XXXX-subclass-existentials.md new file mode 100644 index 0000000000..855f7703dc --- /dev/null +++ b/proposals/XXXX-subclass-existentials.md @@ -0,0 +1,185 @@ +# Class and Subtype existentials + +* Proposal: [SE-XXXX](XXXX-subclass-existentials.md) +* Authors: [David Hart](http://github.com/hartbit/), [Austin Zheng](http://github.com/austinzheng) +* Review Manager: TBD +* Status: TBD + +## Introduction + +This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subtypes which conform to protocols. + +[Mailing list discussion](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170123/031066.html) + +## Motivation + +Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the `&` protocol composition syntax: + +```swift +Protocol1 & Protocol2 +``` + +On the other hand, Objective-C is capable of expressing existentials of classes and subclasses conforming to protocols with the following syntax: + +```objc +id +Base* +``` + +We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C. + +## Proposed solution + +The proposal keeps the existing `&` syntax but allows the first element, and only the first, to be either the `AnyObject` keyword or of class type. The equivalent to the above Objective-C types would look like this: + +```swift +AnyObject & Protocol1 & Protocol2 +Base & Protocol +``` + +As in Objective-C, the first line is an existential of classes which conform to `Protocol1` and `Protocol2`, and the second line is an existential of subtypes of `Base` which conform to `Protocol`. + +Here are the new proposed rules for what is valid in a existential conjunction syntax: + +### 1. The first element in the protocol composition syntax can be the `AnyObject` keyword to enforce a class constraint: + +```swift +protocol P {} +struct S : P {} +class C : P {} +let t: P & AnyObject // Compiler error: AnyObject requirement must be in first position +let u: AnyObject & P = S() // Compiler error: S is not of class type +let v: AnyObject & P = C() // Compiles successfully +``` + +### 2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class: + +```swift +protocol P {} +struct S {} +class C {} +class D : P {} +class E : C, P {} +let t: P & C // Compiler error: subclass constraint must be in first position +let u: S & P // Compiler error: S is not of class type +let v: C & P = D() // Compiler error: D is not a subtype of C +let w: C & P = E() // Compiles successfully +``` + +### 3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps: + +* Expand the typealias +* Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a `class` constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class). +* Check that the type does not contain two class-type constraints + +```swift +class C {} +class D : C {} +class E {} +protocol P1 {} +protocol P2 {} +typealias TA1 = AnyObject & P1 +typealias TA2 = AnyObject & P2 +typealias TA3 = C & P2 +typealias TA4 = D & P2 +typealias TA5 = E & P2 + +typealias TA5 = TA1 & TA2 +// Expansion: typealias TA5 = AnyObject & P1 & AnyObject & P2 +// Normalization: typealias TA5 = AnyObject & P1 & P2 +// TA5 is valid + +typealias TA6 = TA1 & TA3 +// Expansion: typealias TA6 = AnyObject & P1 & C & P2 +// Normalization (AnyObject < C): typealias TA6 = C & P1 & P2 +// TA6 is valid + +typealias TA7 = TA3 & TA4 +// Expansion: typealias TA7 = C & P2 & D & P2 +// Normalization (C < D): typealias TA7 = D & P2 +// TA7 is valid + +typealias TA8 = TA4 & TA5 +// Expansion: typealias TA8 = D & P2 & E & P2 +// Normalization: typealias TA8 = D & E & P2 +// TA8 is invalid because the D and E constraints are incompatible +``` + +## `class` and `AnyObject` + +This proposal merges the concepts of `class` and `AnyObject`, which now have the same meaning: they represent an existential for classes. To get rid of the duplication, we suggest only keeping `AnyObject` around. To reduce source-breakage to a minimum, `class` could be redefined as `typealias class = AnyObject` and give a deprecation warning on `class` for the first version of Swift this proposal is implemented in. Later, `class` could be removed in a subsequent version of Swift. + +## Inheritance clauses and `typealias` + +To improve readability and reduce confusion, a class conforming to a typealias which contains a class type constraint does not implicitly inherit the class type: inheritance should stay explicit. Here are a few examples to remind what the current rules are and to make the previous sentence clearer: + +The proposal does not change the rule which forbids using the protocol composition syntax in the inheritance clause: + +```swift +protocol P1 {} +protocol P2 {} +class C {} + +class D : P1 & P2 {} // Compiler error +class E : C & P1 {} // Compiler error +``` + +Class `D` in the previous example does not inherit a base class so it can be expressed using the inheritance/conformance syntax or through a typealias: + +```swift +class D : P1, P2 {} // Valid +typealias P12 = P1 & P2 +class D : P12 {} // Valid +``` + +Class `E` above inherits a base class. The inheritance must be explicitly declared in the inheritance clause and can't be implicitly derived from a typealias: + +``` +class E : C, P1 {} // Valid +typealias CP1 = C & P1 +class E : CP1 {} // Compiler error: class 'E' does not inherit from class 'C' +class E : C, CP1 {} // Valid: the inheritance is explicitly declared +``` + +## Source compatibility + +This change will not break Swift 3 compability mode because Objective-C types will continue to be imported as before. But in Swift 4 mode, all types bridged from Objective-C which use the equivalent Objective-C existential syntax could break code which does not meet the new protocol requirements. For example, the following Objective-C code: + +```objc +@interface MyViewController +- (void)setup:(nonnull UIViewController*)tableViewController; +@end +``` + +is imported into Swift-3 mode as: + +```swift +class MyViewController { + func setup(tableViewController: UIViewController) {} +} +``` + +which allows calling the function with an invalid parameter: + +```swift +let myViewController: MyViewController() +myViewController.setup(UIViewController()) +``` + +The previous code continues to compile but still crashs if the Objective-C code calls a method of `UITableViewDataSource` or `UITableViewDelegate`. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as: + +```swift +class MyViewController { + func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {} +} +``` + +That would then cause the Swift code run in version 4 mode to fail to compile with an error which states that `UIViewController` does not conform to the `UITableViewDataSource` and `UITableViewDelegate` protocols. + +## Alternatives considered + +An alternative solution to the `class`/`AnyObject` duplication was to keep both, redefine `AnyObject` as `typealias AnyObject = class` and favor the latter when used as a type name. + +## Acknowledgements + +Thanks to [Austin Zheng](http://github.com/austinzheng) and [Matthew Johnson](https://github.com/anandabits) who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from. \ No newline at end of file From 42c320fe3a3d637bbeeaba8c53a492ef480536b2 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 Feb 2017 13:09:00 -0800 Subject: [PATCH 0193/4563] Initiate review of SE-0156: Class and Subtype existentials --- ...lass-existentials.md => 0156-subclass-existentials.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{XXXX-subclass-existentials.md => 0156-subclass-existentials.md} (96%) diff --git a/proposals/XXXX-subclass-existentials.md b/proposals/0156-subclass-existentials.md similarity index 96% rename from proposals/XXXX-subclass-existentials.md rename to proposals/0156-subclass-existentials.md index 855f7703dc..54d812a29f 100644 --- a/proposals/XXXX-subclass-existentials.md +++ b/proposals/0156-subclass-existentials.md @@ -1,9 +1,9 @@ # Class and Subtype existentials -* Proposal: [SE-XXXX](XXXX-subclass-existentials.md) -* Authors: [David Hart](http://github.com/hartbit/), [Austin Zheng](http://github.com/austinzheng) -* Review Manager: TBD -* Status: TBD +* Proposal: [SE-0156](0156-subclass-existentials.md) +* Authors: [David Hart](http://github.com/hartbit), [Austin Zheng](http://github.com/austinzheng) +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (February 28...March 7, 2017)** ## Introduction From 9b28219683f89deee76078fc442d23f92d517cf9 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 28 Feb 2017 16:31:07 -0500 Subject: [PATCH 0194/4563] Officially number SE-0157 and put it in active review. --- ...onstraints.md => 0157-recursive-protocol-constraints.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{XXXX-recursive-protocol-constraints.md => 0157-recursive-protocol-constraints.md} (98%) diff --git a/proposals/XXXX-recursive-protocol-constraints.md b/proposals/0157-recursive-protocol-constraints.md similarity index 98% rename from proposals/XXXX-recursive-protocol-constraints.md rename to proposals/0157-recursive-protocol-constraints.md index c06a355d29..045b3b3fa1 100644 --- a/proposals/XXXX-recursive-protocol-constraints.md +++ b/proposals/0157-recursive-protocol-constraints.md @@ -1,9 +1,9 @@ # Support recursive constraints on associated types -* Proposal: [SE-NNNN](XXXX-recursive-protocol-constraints.md) +* Proposal: [SE-0157](0157-recursive-protocol-constraints.md) * Authors: [Douglas Gregor](https://github.com/DougGregor), [Erica Sadun](https://github.com/erica), [Austin Zheng](https://github.com/austinzheng) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: John McCall +* Status: **Active review (February 28...March 8, 2017)** ## Introduction From 4060cdcea934eee3144ed8099067546be514a89f Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 28 Feb 2017 16:35:41 -0500 Subject: [PATCH 0195/4563] Minor: linkify my name in SE-0157. --- proposals/0157-recursive-protocol-constraints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0157-recursive-protocol-constraints.md b/proposals/0157-recursive-protocol-constraints.md index 045b3b3fa1..872dd3ea08 100644 --- a/proposals/0157-recursive-protocol-constraints.md +++ b/proposals/0157-recursive-protocol-constraints.md @@ -2,7 +2,7 @@ * Proposal: [SE-0157](0157-recursive-protocol-constraints.md) * Authors: [Douglas Gregor](https://github.com/DougGregor), [Erica Sadun](https://github.com/erica), [Austin Zheng](https://github.com/austinzheng) -* Review Manager: John McCall +* Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (February 28...March 8, 2017)** ## Introduction From fa9a58702fb9f813c2d0240648486e68a78b7235 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 1 Mar 2017 15:32:27 +0000 Subject: [PATCH 0196/4563] [SE-0147] Implemented (Swift 3.1) --- proposals/0147-move-unsafe-initialize-from.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0147-move-unsafe-initialize-from.md b/proposals/0147-move-unsafe-initialize-from.md index 5596fdb4b1..e00c87539c 100644 --- a/proposals/0147-move-unsafe-initialize-from.md +++ b/proposals/0147-move-unsafe-initialize-from.md @@ -3,8 +3,9 @@ * Proposal: [SE-0147](0147-move-unsafe-initialize-from.md) * Author: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 3.1)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170102/029945.html) +* Pull Request: [apple/swift#6601](https://github.com/apple/swift/pull/6601) ## Introduction From b10d480580619c8e000912a29bd639b78d6b401b Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 1 Mar 2017 15:38:00 +0000 Subject: [PATCH 0197/4563] [SE-0148] Implemented (Swift 4) --- proposals/0148-generic-subscripts.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0148-generic-subscripts.md b/proposals/0148-generic-subscripts.md index c346dfb578..dfab2598ef 100644 --- a/proposals/0148-generic-subscripts.md +++ b/proposals/0148-generic-subscripts.md @@ -1,9 +1,9 @@ # Generic Subscripts * Proposal: [SE-0148](0148-generic-subscripts.md) -* Author: [Chris Eidhof](http://github.com/chriseidhof/) +* Author: [Chris Eidhof](https://github.com/chriseidhof) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170123/031048.html) * Bug: [SR-115](https://bugs.swift.org/browse/SR-115) From 31c637122b23edcc31741af1a3b356fbaf0b2894 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 1 Mar 2017 10:00:26 -0800 Subject: [PATCH 0198/4563] Revert "Update 0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md" (#625) --- ...te-for-the-inconsistency-of-nscopyings-behaviour.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md index e8e1ce0977..c135e88d2b 100644 --- a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md +++ b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md @@ -185,6 +185,10 @@ Speaking of Swift, however, there is no stuff like `->` operator to access ivar ## Proposed solution +### Compiler magic + +Do the compiler magic to call `copy( with: )` in the initializer so that `@NSCopying` attribute no longer subjects to the fact that setter methods would not be invoked in initializers. **Copying should always take place after a property has been declared as `@NSCopying`**. It seems like the most direct way to maintain the `@NSCopying` contract without changing the underlying direct-storage model. + ### Compile-time checking Have compiler emit a **compile-time error or warning** if developers are performing an assignment operation from within an initializer between a property declared as `@NSCopying` and an instance of a `` protocol conforming class. Also, speaking of GUI integrated development environments such as Xcode, leaving this kind of error or warning **FIXABLE** would be needed in order to make them can be quickly fixed by both IDEs and migrator tools through simply appending `.copy() as! AutoInferredClassType`. @@ -220,8 +224,4 @@ The proposal doesn't change the ABI of existing language features. ## Alternatives Considered -### Compiler magic - -Do the compiler magic to call `copy( with: )` in the initializer so that `@NSCopying` attribute no longer subjects to the fact that setter methods would not be invoked in initializers. **Copying should always take place after a property has been declared as `@NSCopying`**. It seems like the most direct way to maintain the `@NSCopying` contract without changing the underlying direct-storage model. - -The compiler magic is able to fix the problem but a developer who is new to Swift would continue to be confused if `@NSCopying` had magic but `didSet` and other behaviors did not. +There is no alternatives considered at this time. From e6e5a8b42a3c355f6885997026d414f5b950abfb Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 1 Mar 2017 10:44:18 -0800 Subject: [PATCH 0199/4563] Accept SE-0153: Compensate for the inconsistency of `@NSCopying`'s behaviour --- ...e-inconsistency-of-nscopyings-behaviour.md | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md index c135e88d2b..45cb583882 100644 --- a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md +++ b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md @@ -3,7 +3,8 @@ * Proposal: [SE-0153](0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md) * Authors: [Torin Kwok](https://github.com/TorinKwok) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (February 17...22, 2017)** +* Status: **Accepted** +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170227/033357.html) + Number | | Comparable | - | | (+,-,*) | | (==,<,>,...)| - | +-------------++ +---+---------+ + +-------------+ +-------------+ + +------>+ Numeric | | Comparable | + | | (+,-,*) | | (==,<,>,...)| + | +------------++ +---+---------+ | ^ ^ +-------+------------+ | | -| SignedNumber | +-+-------+-----------+ +| SignedNumeric | +-+-------+-----------+ | (unary -) | | BinaryInteger | +------+-------------+ |(words,%,bitwise,...)| ^ ++---+-----+----------+ @@ -109,11 +109,11 @@ There are several benefits provided by this model over the old one: - It enables protocol sharing between integer and floating point types. - Note the exclusion of the `%` operation from `Number`. Its behavior for + Note the exclusion of the `%` operation from `Numeric`. Its behavior for floating point numbers is sufficiently different from the one for integers that using it in generic context would lead to confusion. The `FloatingPoint` protocol introduced by [SE-0067](0067-floating-point-protocols.md) should now - refine `SignedNumber`. + refine `SignedNumeric`. - It makes future extensions possible. @@ -187,8 +187,8 @@ types. We believe there are no useful entities that support bitwise operations, but at the same time are not binary integers. -* `Arithmetic` and `SignedArithmetic` protocols have been renamed to `Number` - and `SignedNumber`. +* `Arithmetic` and `SignedArithmetic` protocols have been renamed to `Numeric` + and `SignedNumeric`. * `minimumSignedRepresentationBitWidth` property was removed. @@ -213,9 +213,9 @@ types. ### Protocols -#### `Number` +#### `Numeric` -The `Number` protocol declares binary arithmetic operators – such as `+`, +The `Numeric` protocol declares binary arithmetic operators – such as `+`, `-`, and `*` — and their mutating counterparts. It provides a suitable basis for arithmetic on scalars such as integers and @@ -238,7 +238,7 @@ as its argument. ```Swift -public protocol Number : Equatable, ExpressibleByIntegerLiteral { +public protocol Numeric : Equatable, ExpressibleByIntegerLiteral { /// Creates a new instance from the given integer, if it can be represented /// exactly. /// @@ -343,19 +343,19 @@ public protocol Number : Equatable, ExpressibleByIntegerLiteral { static func *=(_ lhs: inout Self, rhs: Self) } -extension Number { +extension Numeric { public static prefix func + (x: Self) -> Self { return x } } ``` -#### `SignedNumber` +#### `SignedNumeric` -The `SignedNumber` protocol is for numbers that can be negated. +The `SignedNumeric` protocol is for numbers that can be negated. ```Swift -public protocol SignedNumber : Number { +public protocol SignedNumeric : Numeric { /// Returns the additive inverse of this value. /// /// let x = 21 @@ -380,7 +380,7 @@ public protocol SignedNumber : Number { mutating func negate() } -extension SignedNumber { +extension SignedNumeric { public static prefix func - (_ operand: Self) -> Self { var result = operand result.negate() @@ -413,7 +413,7 @@ type conforming to `BinaryInteger`, using different strategies: ```Swift public protocol BinaryInteger : - Comparable, Hashable, Number, CustomStringConvertible, Strideable { + Comparable, Hashable, Numeric, CustomStringConvertible, Strideable { associatedtype Words : Collection // where Iterator.Element == UInt @@ -811,7 +811,7 @@ The `FixedWidthInteger` protocol adds the notion of endianness as well as static properties for type bounds and bit width. The `WithOverflow` family of methods is used in default implementations of -mutating arithmetic methods (see the `Number` protocol). Having these +mutating arithmetic methods (see the `Numeric` protocol). Having these methods allows the library to provide both bounds-checked and masking implementations of arithmetic operations, without duplicating code. @@ -1019,7 +1019,7 @@ public protocol FixedWidthInteger : BinaryInteger { public protocol UnsignedInteger : BinaryInteger { associatedtype Magnitude : BinaryInteger } -public protocol SignedInteger : BinaryInteger, SignedNumber { +public protocol SignedInteger : BinaryInteger, SignedNumeric { associatedtype Magnitude : BinaryInteger } ``` From 36df69422276d8904dfebb76a35c67f66c99e34e Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Fri, 3 Mar 2017 14:47:35 -0800 Subject: [PATCH 0204/4563] popcount -> populationCount --- proposals/0104-improved-integers.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/proposals/0104-improved-integers.md b/proposals/0104-improved-integers.md index b33f93f6c7..729c6c3734 100644 --- a/proposals/0104-improved-integers.md +++ b/proposals/0104-improved-integers.md @@ -199,9 +199,11 @@ types. words, and also allow standard library to provide a default conformance to `Hashable`. +* `popcount` property was renamed to `populationCount`. + * `trailingZeroBits` property was added to the `BinaryInteger` protocol. - `leadingZeroBits` and `popcount` properties are still defined by the + `leadingZeroBits` and `populationCount` properties are still defined by the `FixedWidthInteger` protocol. * Endian-converting initializers and properties were added to the @@ -968,8 +970,8 @@ public protocol FixedWidthInteger : BinaryInteger { /// /// let x: Int8 = 0b0001_1111 /// // x == 31 - /// // x.popcount == 5 - var popcount: Int { get } + /// // x.populationCount == 5 + var populationCount: Int { get } /// The number of leading zeros in this value's binary representation. /// From 2b5e3b3e11e458c5fdac705583a81476c40ec50c Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Fri, 3 Mar 2017 14:54:02 -0800 Subject: [PATCH 0205/4563] FullWidth and ReportingOverflow operations --- proposals/0104-improved-integers.md | 35 +++++++++++++---------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/proposals/0104-improved-integers.md b/proposals/0104-improved-integers.md index 729c6c3734..7c345b0a02 100644 --- a/proposals/0104-improved-integers.md +++ b/proposals/0104-improved-integers.md @@ -120,7 +120,7 @@ There are several benefits provided by this model over the old one: The proposed model eliminates the 'largest integer type' concept previously used to interoperate between integer types (see `toIntMax` in the current model) and instead provides access to machine words. It also introduces the - `multiplied(by:_:FullWidth)`, `dividing(_:, _:FullWidth)`, and + `multipliedFullWidth(by:)`, `dividingFullWidth(_:)`, and `quotientAndRemainder` methods. Together these changes can be used to provide an efficient implementation of bignums that would be hard to achieve otherwise. @@ -812,20 +812,17 @@ extension BinaryInteger { The `FixedWidthInteger` protocol adds the notion of endianness as well as static properties for type bounds and bit width. -The `WithOverflow` family of methods is used in default implementations of +The `ReportingOverflow` family of methods is used in default implementations of mutating arithmetic methods (see the `Numeric` protocol). Having these methods allows the library to provide both bounds-checked and masking implementations of arithmetic operations, without duplicating code. -The `multiplied(by:, _: FullWidth)` and `dividing(_:, _: FullWidth)` methods -are necessary building blocks to implement support for integer types of a -greater width such as arbitrary-precision integers. +The `multipliedFullWidth(by:)` and `dividingFullWidth(_:)` methods are +necessary building blocks to implement support for integer types of a greater +width such as arbitrary-precision integers. ```Swift -public enum FullWidth { case fullWidth } -public enum ReportingOverflow { case reportingOverflow } - public protocol FixedWidthInteger : BinaryInteger { /// The number of bits used for the underlying binary representation of /// values of this type. @@ -864,7 +861,7 @@ public protocol FixedWidthInteger : BinaryInteger { /// `other`. /// /// - SeeAlso: `+` - func adding(_ other: Self, _: ReportingOverflow) + func addingReportingOverflow(_ other: Self) -> (partialValue: Self, overflow: ArithmeticOverflow) /// Returns the difference of this value and the given value along with a @@ -879,7 +876,7 @@ public protocol FixedWidthInteger : BinaryInteger { /// result of `other` subtracted from this value. /// /// - SeeAlso: `-` - func subtracting(_ other: Self, _: ReportingOverflow) + func subtractingReportingOverflow(_ other: Self) -> (partialValue: Self, overflow: ArithmeticOverflow) /// Returns the product of this value and the given value along with a flag @@ -893,8 +890,8 @@ public protocol FixedWidthInteger : BinaryInteger { /// occurred and the `partialValue` component contains the truncated /// product of this value and `other`. /// - /// - SeeAlso: `*`, `multiplied(by:_:FullWidth)` - func multiplied(by other: Self, _: ReportingOverflow) + /// - SeeAlso: `*`, `multipliedFullWidth(by:)` + func multipliedReportingOverflow(by other: Self) -> (partialValue: Self, overflow: ArithmeticOverflow) /// Returns the quotient of dividing this value by the given value along with @@ -910,8 +907,8 @@ public protocol FixedWidthInteger : BinaryInteger { /// If the `overflow` component is `.overflow`, an overflow occurred and /// the `partialValue` component contains the truncated quotient. /// - /// - SeeAlso: `/`, `dividing(_:_:FullWidth)` - func divided(by other: Self, _: ReportingOverflow) + /// - SeeAlso: `/`, `dividingFullWidth(_:)` + func dividedReportingOverflow(by other: Self) -> (partialValue: Self, overflow: ArithmeticOverflow) /// Returns a double-width value containing the high and low parts of the @@ -919,14 +916,14 @@ public protocol FixedWidthInteger : BinaryInteger { /// /// Use this method to calculate the full result of a product that would /// otherwise overflow. Unlike traditional truncating multiplication, the - /// `multiplied(by:_:FullWidth)` method returns both the high and low + /// `multipliedFullWidth(by:)` method returns both the high and low /// parts of the product of `self` and `other`. The following example uses /// this method to multiply two `UInt8` values that normally overflow when /// multiplied: /// /// let x: UInt8 = 100 /// let y: UInt8 = 20 - /// let result = x.multiplied(by: y, .fullWidth) + /// let result = x.multipliedFullWidth(by: y) /// // result.high == 0b00000111 /// // result.low == 0b11010000 /// @@ -944,8 +941,8 @@ public protocol FixedWidthInteger : BinaryInteger { /// - Returns: A tuple containing the high and low parts of the result of /// multiplying `self` and `other`. /// - /// - SeeAlso: `multiplied(by:,_:ReportingOverflow)` - func multiplied(by other: Self, _: FullWidth) -> DoubleWidth + /// - SeeAlso: `multipliedReportingOverflow(by:)` + func multipliedFullWidth(by other: Self) -> DoubleWidth /// Returns a tuple containing the quotient and remainder of dividing the /// first argument by this value. @@ -960,7 +957,7 @@ public protocol FixedWidthInteger : BinaryInteger { /// type is signed. /// - Returns: A tuple containing the quotient and remainder of `lhs` divided /// by `self`. - func dividing(_ lhs: DoubleWidth, _: FullWidth) + func dividingFullWidth(_ lhs: DoubleWidth) -> (quotient: Self, remainder: Self) /// The number of bits equal to 1 in this value's binary representation. From 85ed93258952066af733364ea5b842f721fea93e Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Fri, 3 Mar 2017 14:54:32 -0800 Subject: [PATCH 0206/4563] Magnitude requirement --- proposals/0104-improved-integers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0104-improved-integers.md b/proposals/0104-improved-integers.md index 7c345b0a02..5b5123190a 100644 --- a/proposals/0104-improved-integers.md +++ b/proposals/0104-improved-integers.md @@ -260,7 +260,7 @@ public protocol Numeric : Equatable, ExpressibleByIntegerLiteral { /// A type that can represent the absolute value of any possible value of the /// conforming type. - associatedtype Magnitude : Equatable, ExpressibleByIntegerLiteral + associatedtype Magnitude : Numeric, Comparable /// The magnitude of this value. /// From 73e844eb7b17494c745dbc1918c5562bf919f32f Mon Sep 17 00:00:00 2001 From: Constantino Tsarouhas Date: Mon, 6 Mar 2017 18:15:36 +0100 Subject: [PATCH 0207/4563] Reformatted quotes in 0115 (#630) --- proposals/0115-literal-syntax-protocols.md | 112 ++++++++++----------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/proposals/0115-literal-syntax-protocols.md b/proposals/0115-literal-syntax-protocols.md index b3c0dc4717..c57e446d49 100644 --- a/proposals/0115-literal-syntax-protocols.md +++ b/proposals/0115-literal-syntax-protocols.md @@ -21,13 +21,13 @@ The standard library currently has protocols that use the term `Convertible` in Further, the standard library team has observed: - The "literal" protocols are not about conversion, they are about adopting - a certain syntax provided by the language. "Convertible" in the name is - a red herring: a type can't be convertible from an integer literal because - there is no "IntegerLiteral" entity in the type system. - The literal *becomes* typed as the corresponding literal type - (e.g., Int or String), and as far as the user at the call site is concerned, - there is no visible conversion (even if one is happening behind the scenes). +> The "literal" protocols are not about conversion, they are about adopting +> a certain syntax provided by the language. "Convertible" in the name is +> a red herring: a type can't be convertible from an integer literal because +> there is no "IntegerLiteral" entity in the type system. +> The literal *becomes* typed as the corresponding literal type +> (e.g., Int or String), and as far as the user at the call site is concerned, +> there is no visible conversion (even if one is happening behind the scenes). [An earlier proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0041-conversion-protocol-conventions.md) was intended to address the first problem by introducing strong naming conventions for three kinds of conversion protocols (*from*, *to*, and *bidirectional*). The review highlighted the difficulty in establishing conventions that everyone is happy with. This proposal takes a different approach to solving the problem that originally inspired that proposal while also solving the awkwardness of the current names described by the standard library team. @@ -83,26 +83,26 @@ The discussion thread for this proposal includes abundant bike shedding on the n Some of the names that have been suggested have been inaccurate due to a misunderstanding of what the protocols do. Dave Abrahams explained during the discussion: - No, it's exactly the opposite, as I keep saying. Conformance to this - protocol does *not* mean you can initialize the type with a literal. - Proof: - -```swift - func f() -> T { - return T(integerLiteral: 43) // Error - return T(43) // Also an Error - } - -// It means an instance of the type can be *written* as a literal: - - func f() -> T { - return 43 // OK - } -``` - - Everybody's confused about the meaning of the protocol, and doesn't like - the proposed names because they imply exactly the actual meaning of the - protocol, which they misunderstand. +> No, it's exactly the opposite, as I keep saying. Conformance to this +> protocol does *not* mean you can initialize the type with a literal. +> Proof: +> +> ```swift +> func f() -> T { +> return T(integerLiteral: 43) // Error +> return T(43) // Also an Error +> } +> +> // It means an instance of the type can be *written* as a literal: +> +> func f() -> T { +> return 43 // OK +> } +>``` +> +> Everybody's confused about the meaning of the protocol, and doesn't like +> the proposed names because they imply exactly the actual meaning of the +> protocol, which they misunderstand. ### Previous Version @@ -121,35 +121,35 @@ Several commenters suggested that this naming scheme is confusing at the site of Nate Cook provided the best explanation of the potential confusion: - Primarily, the new names read like we're saying that a conforming type is a - literal, compounding a common existing confusion between literals and types - that can be initialized with a literal. Swift's type inference can sometimes - make it seem like dark magic is afoot with literal conversions—for example, - you need to understand an awful lot about the standard library to figure out - why line 1 works here but not line 2: - -Note: The comment above is still valid if it is corrected to say "types that can have instances *written as* a literal" rather than "types that can be *initialized with* a literal". - -```swift -var x = [1, 2, 3, 4, 5] -let y = [10, 20] - -x[1..<2] = [10, 20] // 1 -x[1..<2] = y // 2 -``` - - These new names are a (small) step in the wrong direction. While it's true - that the type system doesn't have an IntegerLiteral type, the language does - have integer literals. If someone reads: - -```swift -extension MyInt : Syntax.IntegerLiteral { ... } -``` - - the implication is that MyInt is an integer literal, and therefore instances - of MyInt should be usable wherever an integer literal is usable. - The existing "Convertible" wording may be a red herring, but it at least - suggests that there's a difference between a literal and a concrete type. +> Primarily, the new names read like we're saying that a conforming type is a +> literal, compounding a common existing confusion between literals and types +> that can be initialized with a literal. Swift's type inference can sometimes +> make it seem like dark magic is afoot with literal conversions—for example, +> you need to understand an awful lot about the standard library to figure out +> why line 1 works here but not line 2: + +>```swift +>var x = [1, 2, 3, 4, 5] +>let y = [10, 20] +> +>x[1..<2] = [10, 20] // 1 +>x[1..<2] = y // 2 +>``` + +(Note: The comment above is still valid if it is corrected to say "types that can have instances *written as* a literal" rather than "types that can be *initialized with* a literal".) + +> These new names are a (small) step in the wrong direction. While it's true +> that the type system doesn't have an IntegerLiteral type, the language does +> have integer literals. If someone reads: +> +>```swift +>extension MyInt : Syntax.IntegerLiteral { ... } +>``` +> +> the implication is that MyInt is an integer literal, and therefore instances +> of MyInt should be usable wherever an integer literal is usable. +> The existing "Convertible" wording may be a red herring, but it at least +> suggests that there's a difference between a literal and a concrete type. ### Namespace names From a28667967c609c829bcdcd770c09ffc4b308944f Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Tue, 7 Mar 2017 13:13:25 +0000 Subject: [PATCH 0208/4563] Put forward single solution --- proposals/XXXX-multi-line-string-literals.md | 108 +++++++++---------- 1 file changed, 51 insertions(+), 57 deletions(-) diff --git a/proposals/XXXX-multi-line-string-literals.md b/proposals/XXXX-multi-line-string-literals.md index 0aa91e78ca..901342f292 100644 --- a/proposals/XXXX-multi-line-string-literals.md +++ b/proposals/XXXX-multi-line-string-literals.md @@ -8,47 +8,24 @@ ## Introduction This proposal introduces multi-line string literals to Swift source code. -It proposes a number of different syntaxes that could achieve this goal +This has been discussed a few times on swift-evolution most recently +putting forward a number of different syntaxes that could achieve this goal each of which has their own use case and constituency for discussion. [Swift-evolution thread](http://thread.gmane.org/gmane.comp.lang.swift.evolution/904/focus=15133) ## Motivation -Multi-line String literals are a common programming-language feature that is, to date, still missing in Swift. -Whether generating XML/JSON messages or building usage messages in Swift scripts, providing string literals that -extend over multiple lines offers a simple way to represent text without having to manually break lines using -string concatenation. Concatenation is ungainly and may result in slow compilation. +Multi-line String literals are a common programming-language feature that is, to date, still missing in +Swift. Whether generating XML/JSON messages or building usage messages in Swift scripts, providing string +literals that extend over multiple lines offers a simple way to represent text without having to manually +break lines using string concatenation. Concatenation is ungainly and may result in slow compilation. -## Proposed solutions +## Proposed solution -This proposal puts forward three alternate syntaxes for discussion: `"continuation quotes"`, `"""long strings"""` -and `<<"heredoc"`. It has been shown all three can co-exist in the same parser and it is proposed all -three should be made available to the Swift developer to choose according to their preference though -this is intended to be determined by the review process. - -### Continuation quotes - -The basic idea of continuation quotes is straightforward. If a quoted string literal is not closed by " -before the end of the line and the first non-whitespace character on the next line is " it is taken to -be a continuation of the previous literal. - - let xml = " - " - " - " \(author) - " XML Developer's Guide - " Computer - " 44.95 - " 2000-10-01 - " An in-depth look at creating applications with XML. - " - " - "" - -The advantage of this format is it gives precise control of exactly what is included in the literal. It also -allows code to be formatted in an aesthetically pleasing manner. Its main disadvantage is that some external -editors will not be familiar with the format and will be unable to correctly syntax highlight literals. +After consideration this proposal puts forward a single simple syntax for inclusion: `"""long strings"""`. +This has the advantage that it is well supported by syntax the highlighters on github and existing editors +and is a relatively minor change to the Swift Lexer. Interpolation would work as before. ### Long strings @@ -76,6 +53,47 @@ developer to paste literal content directly into the string without modification has been expressed about could introduce confusion if the prefixing indentation of each line does not contain the same whitespace characters, though this can be checked for by a compiler warning. +## Detailed design + +These changes are envisaged to be mostly confined to the Swift tokeniser: lib/Parse/Lexer.cpp. +The new string literals would be presented to the grammar as simply being string literals. +This has been explored in a PR for a [prototype toolchain](https://github.com/apple/swift/pull/2275) +and seems to be a robust approach. Other details are explored in the prototype such as +escaping the newline in literals resulting in it not being included in the final literal. + +## Impact on existing code + +As this proposal is additive it does not affect existing code. + +## Alternatives considered + +Two other alternative syntaxes were discussed in the swift-evolution thread. +It became apparent that each syntax had its own, at times, non-overlapping +constituency of supporters. + +### Continuation quotes + +The basic idea of continuation quotes is straightforward. If a quoted string literal is not closed by " +before the end of the line and the first non-whitespace character on the next line is " it is taken to +be a continuation of the previous literal. + + let xml = " + " + " + " \(author) + " XML Developer's Guide + " Computer + " 44.95 + " 2000-10-01 + " An in-depth look at creating applications with XML. + " + " + "" + +The advantage of this format is it gives precise control of exactly what is included in the literal. It also +allows code to be formatted in an aesthetically pleasing manner. Its main disadvantage is that some external +editors will not be familiar with the format and will be unable to correctly syntax highlight literals. + ### Heredoc Taking a precedent from other languages, a syntax such as the following could be used to introduce @@ -102,30 +120,6 @@ is a more practical one: it is a more major departure for the compiler in that t in the AST are no longer in source file order. Testing has, however, shown the toolchain to be surprisingly robust in dealing with this change once a few assertions were removed. -## Detailed design - -These changes are envisaged to be mostly confined to the Swift tokeniser: lib/Parse/Lexer.cpp. -The new string literals would be presented to the grammar as simply being string literals. -This has been explored in a PR for a [prototype toolchain](https://github.com/apple/swift/pull/2275) -and seems to be a robust approach. Other details are explored in the prototype such as -escaping the newline in literals resulting in it not being included in the final literal. - -## Impact on existing code - -As none of these syntaxes are currently legal Swift syntax they do not affect existing code as -they can not be present. None of these syntaxes preclude future directions such as introducing -string modifiers for example, `e"\w\d+"` to support non-standard and non-escaped literals. - -## Alternatives considered - -This proposal is inclusive in that it contains the multiple syntaxes discussed rather -than remove alternatives too early on in the process. In the swift-evolution thread -it became apparent that each syntax had its own, at times, non-overlapping constituency -of supporters and hopefully all three will be able to make it into the language. - -Other alternatives discussed revolved around the precise delimiter for `"""long strings"""` -with mention of `_"long strings"_` or `@"long strings"@`. - ------------------------------------------------------------------------------- # Rationale From bcaa0318e6cd5875b4aed080eb012c456f995056 Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Thu, 16 Feb 2017 14:05:50 +0530 Subject: [PATCH 0209/4563] Add package manager manifest api redesign proposal --- ...x-package-manager-manifest-api-redesign.md | 695 ++++++++++++++++++ 1 file changed, 695 insertions(+) create mode 100644 proposals/xxxx-package-manager-manifest-api-redesign.md diff --git a/proposals/xxxx-package-manager-manifest-api-redesign.md b/proposals/xxxx-package-manager-manifest-api-redesign.md new file mode 100644 index 0000000000..96443add82 --- /dev/null +++ b/proposals/xxxx-package-manager-manifest-api-redesign.md @@ -0,0 +1,695 @@ +# Package Manager Manifest API Redesign + +* Proposal: [SE-XXXX](xxxx-package-manager-manifest-api-redesign.md) +* Author: [Ankit Aggarwal](https://github.com/aciidb0mb3r) +* Review Manager: [Rick Ballard](https://github.com/rballard) +* Status: **Discussion** + +## Introduction + +This is a proposal for redesigning the `Package.swift` manifest APIs provided +by Swift Package Manager. +This proposal only redesigns the existing public APIs and does not add any +new functionality; any API to be added for new functionality will happen in +separate proposals. + +## Motivation + +The `Package.swift` manifest APIs were designed prior to the [API Design +Guidelines] (https://swift.org/documentation/api-design-guidelines/), and their +design was not reviewed by the evolution process. Additionally, there are +several small areas which can be cleaned up to make the overall API more +"Swifty". + +We would like to redesign these APIs as necessary to provide clean, +conventions-compliant APIs that we can rely on in the future. Because we +anticipate that the user community for the Swift Package Manager will grow +considerably in Swift 4, we would like to make these changes now, before +more packages are created using the old API. + +## Proposed solution + +Note: Access modifier is omitted from the diffs and examples for brevity. The +access modifier is `public` for all APIs unless specified. + +* Remove `successor()` and `predecessor()` from `Version`. + + These methods neither have well defined semantics nor are used a lot + (internally or publicly). For e.g., the current implementation of + `successor()` always just increases the patch version. + + +
+ View diff +

+ ```diff + struct Version { + - func successor() -> Version + + - func predecessor() -> Version + } + ``` +

+ +* Convert `Version`'s `buildMetadataIdentifier` property to an array. + + According to SemVer 2.0, build metadata is a series of dot separated + identifiers. Currently this is represented as an optional string property + in the `Version` struct. We propose to change this property to an array + (similar to `prereleaseIdentifiers` property). To maintain backwards + compatiblility in PackageDescription 3 API, we will keep the optional + string as a computed property based on the new array property. We will also + keep the version initializer that takes the `buildMetadataIdentifier` string. + +* Make all properties of `Package` and `Target` mutable. + + Currently, `Package` has three immutable and four mutable properties, and + `Target` has one immutable and one mutable property. We propose to make all + properties mutable to allow complex customization on the package object + after initial declaration. + +
+ View diff and example +

+ + Diff: + ```diff + final class Target { + - let name: String + + var name: String + } + + final class Package { + - let name: String + + var name: String + + - let pkgConfig: String? + + var pkgConfig: String? + + - let providers: [SystemPackageProvider]? + + var providers: [SystemPackageProvider]? + } + ``` + + Example: + ```swift + let package = Package( + name: "FooPackage", + targets: [ + Target(name: "Foo", dependencies: ["Bar"]), + ] + ) + + #if os(Linux) + package.targets[0].dependencies = ["BarLinux"] + #endif + ``` +

+ +* Change `Target.Dependency` enum cases to lowerCamelCase. + + According to API design guidelines, everything other than types should be in lowerCamelCase. + +
+ View diff and example +

+ + Diff: + ```diff + enum Dependency { + - case Target(name: String) + + case target(name: String) + + - case Product(name: String, package: String?) + + case product(name: String, package: String?) + + - case ByName(name: String) + + case byName(name: String) + } + ``` + + Example: + ```diff + let package = Package( + name: "FooPackage", + targets: [ + Target( + name: "Foo", + dependencies: [ + - .Target(name: "Bar"), + + .target(name: "Bar"), + + - .Product(name: "SwiftyJSON", package: "SwiftyJSON"), + + .product(name: "SwiftyJSON", package: "SwiftyJSON"), + ] + ), + ] + ) + ``` +

+ +* Add default parameter to the enum case `Target.Dependency.product`. + + The associated value `package` in the (enum) case `product`, is an optional + `String`. It should have the default value `nil` so clients don't need to + write it if they prefer using explicit enum cases but don't want to specify + the package name i.e. it should be possible to write `.product(name: + "Foo")` instead of `.product(name: "Foo", package: nil)`. + + If + [SE-0155](https://github.com/apple/swift-evolution/blob/master/proposals/0155-normalize-enum-case-representation.md) + is accepted, we can directly add a default value. Otherwise, we will use a + static factory method to provide default value for `package`. + +* Rename all enums cases to have a suffix `Item` and favor static methods. + + Since static methods are more extensible than enum cases right now, we + should discourage use of direct use of enum initializers and provide a + static method for each case. It is not possible to have overloads on enum + cases, so as a convention we propose to rename all enum cases to have a + suffix "Item" for future extensibility. + +* Change `SystemPackageProvider` enum cases to lowerCamelCase and their payloads to array. + + According to API design guidelines, everything other than types should be + in lowerCamelCase. + + This enum allows SwiftPM System Packages to emit hints in case of build + failures due to absence of a system package. Currently, only one system + package per system packager can be specified. We propose to allow + specifying multiple system packages by changing the payload to be an array. + +
+ View diff and example +

+ + Diff: + ```diff + enum SystemPackageProvider { + - case Brew(String) + + case brew([String]) + + - case Apt(String) + + case apt([String]) + } + ``` + + Example: + + ```diff + let package = Package( + name: "Copenssl", + pkgConfig: "openssl", + providers: [ + - .Brew("openssl"), + + .brew(["openssl"]), + + - .Apt("openssl-dev"), + + .apt(["openssl", "libssl-dev"]), + ] + ) + ``` +

+ + +* Remove implicit target dependency rule for test targets. + + There is an implicit test target dependency rule: a test target "FooTests" + implicity depends on a target "Foo", if "Foo" exists and "FooTests" doesn't + explicitly declare any dependency. We propose to remove this rule because: + + 1. It is a non obvious "magic" rule that has to be learned. + 2. It is not possible for "FooTests" to remove dependency on "Foo" while + having no other (target) dependency. + 3. It makes real dependencies less discoverable. + 4. It may cause issues when we get support for mechanically editing target + dependencies. + +* Use factory methods for creating objects. + + We propose to always use factory methods to create objects except for the + main `Package` object. This gives a lot of flexibility and extensibility to + the APIs because Swift's type system can infer the top level type in a + context and allow using the shorthand dot syntax. + + Concretely, we will make these changes: + + * Add a factory method `target` to `Target` class and change the current + initializer to private. + +
+ View example and diff +

+ + Example: + + ```diff + let package = Package( + name: "Foo", + target: [ + - Target(name: "Foo", dependencies: ["Utility"]), + + .target(name: "Foo", dependencies: ["Utility"]), + ] + ) + ``` +

+ + * Introduce a `Product` class with two subclasses: `Executable` and + `Library`. These subclasses will be nested inside `Product` class + instead of being a top level declaration in the module. Nesting will give + us a namespace for products and it is easy to find all the supported + products when the product types grows to a large number. We will add two + factory methods to `Product` class: `library` and `executable` to create + respective products. + + ```swift + /// Represents a product. + class Product { + + /// The name of the product. + let name: String + + private init(name: String) { + self.name = name + } + + /// Represents an executable product. + final class Executable: Product { + + /// The names of the targets in this product. + let targets: [String] + + private init(name: String, targets: [String]) + } + + /// Represents a library product. + final class Library: Product { + /// The type of library product. + enum LibraryType: String { + case `static` + case `dynamic` + } + + /// The names of the targets in this product. + let targets: [String] + + /// The type of the library. + /// + /// If the type is unspecified, package manager will automatically choose a type. + let type: LibraryType? + + private init(name: String, type: LibraryType? = nil, targets: [String]) + } + + /// Create a libary product. + static func library(name: String, type: LibraryType? = nil, targets: [String]) -> Library + + /// Create an executable product. + static func executable(name: String, targets: [String]) -> Library + } + ``` + +
+ View example +

+ + Example: + + ```swift + let package = Package( + name: "Foo", + target: [ + .target(name: "Foo", dependencies: ["Utility"]), + .target(name: "tool", dependencies: ["Foo"]), + ], + products: [ + .executable(name: "tool", targets: ["tool"]), + .library(name: "Foo", targets: ["Foo"]), + .library(name: "FooDy", type: .dynamic, targets: ["Foo"]), + ] + ) + ``` +

+ + +* Special syntax for version initializers. + + A simplified summary of what is commonly supported in other package managers: + + | Package Manager | x-ranges | tilde (`~` or `~>`) | caret (`^`) | + |-----------------|---------------|-------------------------|---------------| + | npm | Supported | Allows patch-level changes if a minor version is specified on the comparator. Allows minor-level changes if not. | patch and minor updates | + | Cargo | Supported | Same as above | Same as above | + | CocoaPods | Not supported | Same as above | Not supported | + | Carthage | Not supported | patch and minor updates | Not supported | + + Some general notes: + + Every package manager we looked at supports the tilde `~` operator in some form, and it's generally + recommended as "the right thing", because package maintainers often fail to increment their major package + version when they should, incrementing their minor version instead. See e.g. how Google created a + [6-minute instructional video](https://www.youtube.com/watch?v=x4ARXyovvPc) about this operator for CocoaPods. + This is a form of version overconstraint; your package should be compatible with everything with the same + major version, but people don't trust that enough to rely on it. But version overconstraint is harmful, + because it leads to "dependency hell" (unresolvable dependencies due to conflicting requirements for a package + in the dependency graph). + + We'd like to encourage a better standard of behavior in the Swift Package Manager. In the future, we'd like + to add tooling to let the package manager automatically help you use Semantic Versioning correctly, + so that your clients can trust your major version. If we can get package maintainers to use SemVer correctly, + through automatic enforcement in the future or community norms for now, then caret `^` becomes + the best operator to use most of the time. That is, you should be able to specify a minimum version, + and you should be willing to let your package use anything after that up to the next major version. This + means you'll get safe updates automatically, and you'll avoid overconstraining and introducing dependency hell. + + Caret `^` and tilde `~` syntax is somewhat standard, but is syntactically non-obvious; we'd prefer a syntax + that doesn't require reading a manual for novices to understand, even if that means we break with the + syntactic convention established by the other package managers which support caret `^` and tilde `~`. + We'd like to make it possible to follow the tilde `~` use case (with different syntax), but caret `^` + should be the most convenient, to encourage its use. + + What we propose: + + * We will introduce a factory method which takes a lower bound version and + forms a range that goes upto the next major version (i.e. caret). + + ```swift + // 1.0.0 ..< 2.0.0 + .package(url: "/SwiftyJSON", from: "1.0.0"), + + // 1.2.0 ..< 2.0.0 + .package(url: "/SwiftyJSON", from: "1.2.0"), + + // 1.5.8 ..< 2.0.0 + .package(url: "/SwiftyJSON", from: "1.5.8"), + ``` + + * We will introduce a factory method which takes `Requirement`, to + conveniently specify common ranges. + + `Requirement` is an enum defined as follows: + + ```swift + enum Requirement { + /// The requirement is specified by an exact version. + case exact(Version) + + /// The requirement is specified by a version range. + case range(Range) + + /// The requirement is specified by a source control revision. + case revision(String) + + /// The requirement is specified by a source control branch. + case branch(String) + + /// Creates a specifier for an exact version. + static func only(_ version: Version) -> Requirement + + /// Creates a specified for a range starting at the given lower bound + /// and going upto next major version. + static func uptoNextMajor(_ version: Version) -> Requirement + + /// Creates a specified for a range starting at the given lower bound + /// and going upto next minor version. + static func uptoNextMinor(_ version: Version) -> Requirement + } + ``` + + Examples: + + ```swift + // 1.5.8 ..< 2.0.0 + .package(url: "/SwiftyJSON", .uptoNextMajor("1.5.8")), + + // 1.5.8 ..< 1.6.0 + .package(url: "/SwiftyJSON", .uptoNextMinor("1.5.8")), + + // 1.5.8 + .package(url: "/SwiftyJSON", .only("1.5.8")), + ``` + + * This will also give us ability to add more complex features in future: + + Examples: + > Note that we're not actually proposing these as part of this proposal. + + ```swift + .package(url: "/SwiftyJSON", .uptoNextMajor("1.5.8").excluding("1.6.4")), + + .package(url: "/SwiftyJSON", .only("1.5.8", "1.6.3")), + ``` + + * We will introduce a factory method which takes `Range`, to specify + arbitrary open range. + + ```swift + // Constraint to an arbitrary open range. + .package(url: "/SwiftyJSON", "1.2.3"..<"1.2.6"), + ``` + + * We will introduce a factory method which takes `ClosedRange`, to specify + arbitrary closed range. + + ```swift + // Constraint to an arbitrary closed range. + .package(url: "/SwiftyJSON", "1.2.3"..."1.2.8"), + ``` + * As specified by branch + [proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0150-package-manager-branch-support.md), + we will add these factory methods: + + ```swift + .package(url: "/SwiftyJSON", branch: "develp"), + .package(url: "/SwiftyJSON", revision: "e74b07278b926c9ec6f9643455ea00d1ce04a021") + ``` + + * We will remove all of the current factory methods: + + ```swift + // Constraint to a major version. + .Package(url: "/SwiftyJSON", majorVersion: 1), + + // Constraint to a major and minor version. + .Package(url: "/SwiftyJSON", majorVersion: 1, minor: 2), + + // Constraint to an exact version. + .Package(url: "/SwiftyJSON", "1.2.3"), + + // Constraint to an arbitrary range. + .Package(url: "/SwiftyJSON", versions: "1.2.3"..<"1.2.6"), + + // Constraint to an arbitrary closed range. + .Package(url: "/SwiftyJSON", versions: "1.2.3"..."1.2.8"), + ``` + +* Adjust order of parameters on `Package` class: + + We propose to reorder the parameters of `Package` class to: `name`, + `pkgConfig`, `products`, `dependencies`, `targets`, `swiftLanguageVersions`. + + The rationale behind this reorder is that the most interesting parts of a + package are its product and dependencies, so they should be at the top. + Targets are usually important during development of the package. Placing + them at the end keeps it easier for the developer to jump to end of the + file to access them. Note that the `swiftLanguageVersions` property will likely + be removed once we support Build Settings, but that will be discussed in a separate proposal. + + +
+ View example +

+ + Example: + + ```swift + let package = Package( + name: "Paper", + products: [ + .executable(name: "tool", targets: ["tool"]), + .libary(name: "Paper", type: .static, targets: ["Paper"]), + .libary(name: "PaperDy", type: .dynamic, targets: ["Paper"]), + ], + dependencies: [ + .package(url: "http://github.com/SwiftyJSON", from: "1.2.3"), + .package(url: "../CHTTPParser", .uptoNextMinor("2.2.0")), + .package(url: "http://some/other/lib", .only("1.2.3")), + ] + targets: [ + .target( + name: "tool", + dependencies: [ + "Paper", + "SwiftyJSON" + ]), + .target( + name: "Paper", + dependencies: [ + "Basic", + .target(name: "Utility"), + .product(name: "CHTTPParser"), + ]) + ] + ) + ``` +

+ +* Eliminate exclude in future (via custom layouts feature). + + We expect to remove the `exclude` property after we get support for custom + layouts. The exact details will be in the proposal of that feature. + +## Example manifests + +* A regular manifest. + +```swift +let package = Package( + name: "Paper", + products: [ + .executable(name: "tool", targets: ["tool"]), + .libary(name: "Paper", targets: ["Paper"]), + .libary(name: "PaperStatic", type: .static, targets: ["Paper"]), + .libary(name: "PaperDynamic", type: .dynamic, targets: ["Paper"]), + ], + dependencies: [ + .package(url: "http://github.com/SwiftyJSON", from: "1.2.3"), + .package(url: "../CHTTPParser", .uptoNextMinor("2.2.0")), + .package(url: "http://some/other/lib", .only("1.2.3")), + ] + targets: [ + .target( + name: "tool", + dependencies: [ + "Paper", + "SwiftyJSON" + ]), + .target( + name: "Paper", + dependencies: [ + "Basic", + .target(name: "Utility"), + .product(name: "CHTTPParser"), + ]) + ] +) +``` + +* A system package manifest. + +```swift +let package = Package( + name: "Copenssl", + pkgConfig: "openssl", + providers: [ + .brew(["openssl"]), + .apt(["openssl", "libssl-dev"]), + ] +) +``` + +## Impact on existing code + +The above changes will be implemented only in the new Package Description v4 +library. The v4 runtime library will release with Swift 4 and packages will be +able to opt-in into it as described by +[SE-0152](https://github.com/apple/swift-evolution/blob/master/proposals/0152-package-manager-tools-version.md). + +There will be no automatic migration feature for updating the manifests from v3 +to v4. To indicate the replacements of old APIs, we will annotate them using +the `@unavailable` attribute where possible. Unfortunately, this will not cover +all the changes for e.g. rename of the target dependency enum cases. + +All new packages created with `swift package init` command in Swift 4 tools +will by default to use the v4 manifest. It will be possible to switch to v3 +manifest version by changing the tools version using `swift package +tools-version --set 3.1`. However, the manifest will needed to be adjusted to +use the older APIs manually. + +Unless declared in the manifest, existing packages automatically default +to the Swift 3 minimum tools version; since the Swift 4 tools will also include +the v3 manifest API, they will build as expected. + +A package which needs to support both Swift 3 and Swift 4 tools will need to +stay on the v3 manifest API and support the Swift 3 language version for its +sources, using the API described in the proposal +[SE-0151](https://github.com/apple/swift-evolution/blob/master/proposals/0151-package-manager-swift-language-compatibility-version.md). + +An existing package which wants to use the new v4 manifest APIs will need to bump its +minimum tools version to 4.0 or later using the command `$ swift package tools-version +--set-current`, and then modify the manifest file with the changes described in +this proposal. + +## Alternatives considered + +* Add variadic overloads. + + Adding variadic overload allows omitting parenthesis which leads to less + cognitive load on eyes, especially when there is only one value which needs + to be specified. For e.g.: + + Target(name: "Foo", dependencies: "Bar") + + might looked better than: + + Target(name: "Foo", dependencies: ["Bar"]) + + However, plurals words like `dependencies` and `targets` imply a collection + which implies brackets. It also makes the grammar wrong. Therefore, we + reject this option. + +* Version exclusion. + + It is not uncommon to have a specific package version break something, and + it is undesirable to "fix" this by adjusting the range to exclude it + because this overly constrains the graph and can prevent picking up the + version with the fix. + + This is desirable but it should be proposed separately. + +* Inline package declaration. + + We should probably support declaring a package dependency anywhere we + support spelling a package name. It is very common to only have one target + require a dependency, and annoying to have to specify the name twice. + + This is desirable but it should be proposed separately. + +* Introduce an "identity rule" to determine if an API should use an initializer + or a factory method: + + Under this rule, an entity having an identity, will use a type initializer + and everything else will use factory methods. `Package`, `Target` and + `Product` are identities. However, a product referenced in a target + dependency is not an identity. + + We rejected this because it may become a source of confusion for users. + Another downside is that the product initializers will have to used with + the dot notation (e.g.: `.Executable(name: "tool", targets: ["tool"])`) + which is a little awkward because we expect factory methods and enum cases + to use the dot syntax. This can be solved by moving these products outside + of `Product` class but we think having a namespace for product provides a + lot of value. + +* Upgrade `SystemPackageProvider` enum to a struct. + + We thought about upgrading `SystemPackageProvider` to a struct when we had + the "identity" rule but since we're dropping that, there is no need for + this change. + + ```swift + public struct SystemPackageProvider { + enum PackageManager { + case apt + case brew + } + + /// The system package manager. + let packageManager: PackageManager + + /// The array of system packages. + let packages: [String] + + init(_ packageManager: PackageManager, packages: [String]) + } + ``` From 5cefa94389f674cc60a9d8c6a4c3a605f2bace22 Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Tue, 7 Mar 2017 12:41:16 -0800 Subject: [PATCH 0210/4563] Assign proposal number and review dates for SE-0158 Package Manager Manifest API Redesign --- ...sign.md => 0158-package-manager-manifest-api-redesign.md} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename proposals/{xxxx-package-manager-manifest-api-redesign.md => 0158-package-manager-manifest-api-redesign.md} (99%) diff --git a/proposals/xxxx-package-manager-manifest-api-redesign.md b/proposals/0158-package-manager-manifest-api-redesign.md similarity index 99% rename from proposals/xxxx-package-manager-manifest-api-redesign.md rename to proposals/0158-package-manager-manifest-api-redesign.md index 96443add82..d424bc80c4 100644 --- a/proposals/xxxx-package-manager-manifest-api-redesign.md +++ b/proposals/0158-package-manager-manifest-api-redesign.md @@ -1,9 +1,10 @@ # Package Manager Manifest API Redesign -* Proposal: [SE-XXXX](xxxx-package-manager-manifest-api-redesign.md) +* Proposal: [SE-0158](0158-package-manager-manifest-api-redesign.md) * Author: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Rick Ballard](https://github.com/rballard) -* Status: **Discussion** +* Status: **Active review (March 7...March 13, 2017)** +* Bug: [SR-3949](https://bugs.swift.org/browse/SR-3949) ## Introduction From 61369efdcd797b99cd084c93cf0c3462bb64cf69 Mon Sep 17 00:00:00 2001 From: Daniel Duan Date: Wed, 8 Mar 2017 11:48:55 -0800 Subject: [PATCH 0211/4563] Revise SE-0155 --- ...0155-normalize-enum-case-representation.md | 368 +++++++++++------- 1 file changed, 226 insertions(+), 142 deletions(-) diff --git a/proposals/0155-normalize-enum-case-representation.md b/proposals/0155-normalize-enum-case-representation.md index 100bef31be..767286ea98 100644 --- a/proposals/0155-normalize-enum-case-representation.md +++ b/proposals/0155-normalize-enum-case-representation.md @@ -3,143 +3,146 @@ * Proposal: [SE-0155][] * Authors: [Daniel Duan][], [Joe Groff][] * Review Manager: [John McCall][] -* Status: **Returned for revision** ## Introduction -In Swift 3, associated values for an enum case are represented by -a labeled-tuple. This has several undesired effects: inconsistency in enum value -construction syntax, many forms of pattern matching, missing features such as -specifying default value and missed opportunity for layout improvements. +In Swift 3, associated values of an enum case are represented by a tuple. This +implementation causes inconsistencies in case declaration, construction and +pattern matching in several places. -This proposal aims to make enums more "regular" by replacing tuple as the -representation of associated values, making declaration and construction of enum -cases more function-like. - -Swift-evolution thread: [Compound Names For Enum Cases][SE Thread] +Enums, therefore, can be made more "regular" when we replace tuple as the +representation of associated case values. This proposal aims to define the +effect of doings so on various parts of the language. ## Motivation -**Each enum case declares a function that can be used to create a corresponding -value. To users who expect these functions to behave "normally", surprises -await.** - -1. Associated value labels aren't part of the function name. - - After [SE-0111][] Swift function's fully qualified name consists of its - base-name and all argument labels. As an illustration, one can invoke - a function with its full name: - - - ```swift - func f(x: Int, y: Int) {} - f(x: y:)(0, 0) // Okay, this is equivalent to f(x: 0, y: 0) - ``` - - This, however, cannot be done when enum cases with associated value were - constructed: +When user declares a case for an enum, a function which constructs the +corresponding case value is declared. We'll refer to such functions as _case +constructors_ in this proposal. - ```swift - enum Foo { - case bar(x: Int, y: Int) - } - Foo.bar(x: y:)(0, 0) // Does not compile as of Swift 3 - ``` +```swift +enum Expr { + // this case declares the case constructor `Expr.elet(_:_:)` + indirect case elet(locals: [(String, Expr)], body: Expr) +} - Here, `x` and `y` are labels of bar's payload (a tuple), as opposed to being - part of the case's formal name. This is inconsistent with rest of the - language. +// f's signature is f(_: _), type is ([(String, Expr)], Expr) -> Expr +let f = Expr.elet -2. Default value for parameters isn't available in case declarations. +// `f` is just a function +f([], someExpr) // construct a `Expr.elet` +``` - ```swift - enum Animation { - case fadeIn(duration: TimeInterval = 0.3) // Nope! - } - let anim = Animation.fadeIn() // Would be nice, too bad! - ``` +There are many surprising aspects of enum constructors, however: -**Associated values being a tuple complicates pattern matching.** +1. After [SE-0111][], Swift function's fully qualified name consists of its base + name and all of its argument labels. User can use the full name of the + function at use site. In the example above, `locals` and `body` are currently + not part of the case constructors name, therefore the expected syntax in + invalid. -The least unexpected pattern to match a `bar` value is the following: + ```swift + func f(x: Int, y: Int) {} + f(x: y:)(0, 0) // Okay, this is equivalent to f(x: 0, y: 0) + Expr.elet(locals: body:)([], someExpr) // this doesn't work in Swift 3 + ``` +2. Case constructors cannot include a default value for each parameter. This + is yet another feature available to functions. -```swift -if case let .bar(x: p, y: q) = Foo.bar(x: 0, y: 1) { - print(p, q) // 0 1 -} -``` +3. Some user may expect, similar to function, that case constructors can be + overloaded. After all, two enum cases with the same full name but different + types are clearly distinct to both the compiler and the user. This is + unavailable to Swift 3 users. -In Swift 3, there are a few alternatives that may not be obvious to new users. +As previous mentioned, these are symptoms of associated values being a tuple +instead of having its own distinct semantics. This problem manifests more in +Swift 3's pattern matching: 1. A pattern with a single value would match and result in a tuple: ```swift - if case let .bar(wat) = Foo.bar(x: 0, y: 1) { - print(wat.y) // 1 + // this works for reasons most user probably don't expect! + if case .elet(let wat) = anExpr { + eval(wat.body) } ``` 2. Labels in patterns are not enforced: ```swift - // note: there's no label in the following pattern - if case let .bar(p, q) = Foo.bar(x: 0, y: 1) { - print(p, q) // 0 1 + // note: there's no label in the first sub-pattern + if case .elet(let p, let body: q) = anExpr { + // code } ``` -These complex rules makes pattern matching difficult to teach and to expand to +These extra rules makes pattern matching difficult to teach and to expand to other types. -**Moving away from tuple-as-associated-value also give us opportunity to improve -enum's memory layout** since each associated value would no longer play double -duty as part of the tuple's memory layout. - ## Proposed Solution -When a enum case has associated values, they will no longer form a tuple. Their -labels will become part of the case's declared name. Patterns matching such -values must include labels. - -This proposal also introduce the ability to include a default value for each -associated value in the declaration. +We'll add first class syntax (which largely resemble the syntax in Swift 3) for +declaring associated values with labels. Tuple will no longer be used to +represent the aggregate of associated values for an enum case. This means +pattern matching for enum cases needs its own syntax as well (as opposed to +piggybacking on tuple patterns, which remains in the language for tuples.). ## Detailed Design -### Make associated value labels part of case's name -When labels are present in enum case's payload, they will become part of case's -declared name instead of being labels for fields in a tuple. In details, when -constructing an enum value with the case name, label names must either be +### Compound Names For Enum Constructors + +Associated values' labels should be part of the enum case's constructor name. +When constructing an enum value with the case name, label names must either be supplied in the argument list it self, or as part of the full name. ```swift -Foo.bar(x: 0, y: 0) // Okay, the Swift 3 way. -Foo.bar(x: y:)(0, 0) // Equivalent to the previous line. -Foo.bar(x: y:)(x: 0, y: 0) // This would be an error, however. +Expr.elet(locals: [], body: anExpr) // Okay, the Swift 3 way. +Expr.elet(locals: body:)([], anExpr) // Okay, equivalent to the previous line. +Expr.elet(locals: body:)(locals: 0, body: 0) // This would be an error, however. ``` Note that since the labels aren't part of a tuple, they no longer participate in -type checking, similar to functions: +type checking, behaving consistently with functions. ```swift -let f = Foo.bar // f has type (Int, Int) -> Foo -f(0, 0) // Okay! -f(x: 0, y: 0) // Won't compile. +let f = Expr.elet // f has type ([(String, Expr)], Expr) -> Expr +f([], anExpr) // Okay! +f(locals: [], body: anExpr) // Won't compile. ``` -Enum cases should have distinct *full* names. Therefore, shared base name will be allowed: +Enum cases should have distinct *full* names. Therefore, shared base name will +be allowed: ```swift -enum Expr { - case literal(bool: Bool) - case literal(int: Int) +enum SyntaxTree { + case type(variables: [TypeVariable]) + case type(instantiated: [Type]) } ``` -### Add default value in enum case declarations +Using only the base name in pattern matching for the previous example would be +ambiguous and result in an compile error. In this case, the full name must be +supplied to disambiguate. + +```swift +case .type // error: ambiguous +case .type(variables: let variables) // Okay +``` + +### Default Parameter Values For Enum Constructors + From a user's point view, declaring an enum case should remain the same as Swift 3 except now it's possible to add `= expression` after the type of an -associated value to convey a default value for that field. Updated syntax: +associated value to convey a default value for that field. + +```swift +enum Animation { + case fadeIn(duration: TimeInterval = 0.3) // Okay! +} +let anim = Animation.fadeIn() // Great! +``` + +Updated syntax: ```ebnf union-style-enum-case = enum-case-name [enum-case-associated-value-clause]; @@ -150,98 +153,179 @@ enum-case-associated-value-list = enum-associated-value-element enum-case-associated-value-list; enum-case-associated-value-element = element-name type-annotation [enum-case-element-default-value-clause] - | type [enum-case-element-default-value-clause]; + | type + [enum-case-element-default-value-clause]; element-name = identifier; enum-case-element-default-value-clause = "=" expression; ``` -### Simplify pattern matching rules on enums -Syntax for enum case patterns will be the following: +### Enum Case "Overloading" -```ebnf -enum-case-pattern = [type-identifier] "." enum-case-name [enum-case-associated-value-pattern]; -enum-case-associated-value-pattern = "(" [enum-case-associated-value-list-pattern] ")"; -enum-case-associated-value-list-pattern = enum-case-associated-value-list-pattern-element - | enum-case-associated-value-list-pattern-element "," - enum-case-associated-value-list-pattern; -enum-case-associated-value-list-element = pattern | identifier ":" pattern; +An enum may contain cases with the same full name but with associated values of +different types. For example: + +```swift +enum Expr { + case literal(Bool) + case literal(Int) +} ``` -… and `case-associated-value-pattern` will be added to the list of various -`pattern`s. +The above cases have overloaded constructors, which follow the same rules as +functions at call site for disambiguation: -Note that `enum-case-associated-value-pattern` is identical to `tuple-pattern` -except in names. It is introduced here to denote semantic difference between the -two. Whereas the syntax in Swift 3 allows a single `tuple-pattern-element` to -match the entire case payload, the number of -`enum-case-associated-value-list-pattern-element`s must be equal to that of -associated value of the case in order to be a match. This means code in the next -example will be deprecated under this proposal: +```swift +// It's clear which case is being constructed in the following. +let aBool: Expr = .literal(false) +let anInt: Expr = .literal(42) +``` + +User must specify an `as` expression in sub-patterns in pattern matching, in +order to match with such cases: + +```swift +case .literal(let value) // this is ambiguous +case .literal(let value as Bool) // matches `case literal(Bool)` +``` + +### Alternative Payload-less Case Declaration + +In Swift 3, the following syntax is valid: ```swift -if case let .bar(wat) = Foo.bar(x: 0, y: 1) { // syntax error - // … +enum Tree { + case leaf() // the type of this constructor is confusing! } ``` -Further, `identifier` in `enum-case-associated-value-list-pattern-element` must -be the same as the label of corresponding associated value intended for the -match. So this will be deprecated as well: +`Tree.leaf` has a very unexpected type to most Swift users: `(()) -> Tree` + +We propose this syntax declare the "bare" case instead. So it's going to be +the equivalent of ```swift -if case let .bar(p, q) = Foo.bar(x: 0, y: 1) { // missing `x:` and `y:` - // … +enum Tree { + case leaf // `()` is optional and does the same thing. } ``` -## Source compatibility +### Pattern Consistency + +*(The following enum will be used throughout code snippets in this section).* + +```swift +indirect enum Expr { + case variable(name: String) + case lambda(parameters: [String], body: Expr) +} +``` + +Compared to patterns in Swift 3, matching against enum cases will follow +stricter rules. This is a consequence of no longer relying on tuple patterns. + +When an associated value has a label, the sub-pattern must include the label +exactly as declared. There are two variants that should look familiar to Swift +3 users. Variant 1 allows user to bind the associated value to arbitrary name in +the pattern by requiring the label: + +```swift +case .variable(name: let x) // okay +case .variable(x: let x) // compile error; there's no label `x` +case .lambda(parameters: let params, body: let body) // Okay +case .lambda(params: let params, body: let body) // error: 1st label mismatches +``` -As detailed in the previous section, this proposal deprecates certain pattern -matching syntax. +User may choose not to use binding names that differ from labels. In this +variant, the corresponding value will bind to the label, resulting in this +shorter form: -Other changes to the syntax are additive and source-compatible with Swift 3. For -example, matching a case with associated value solely by its name should still -work: +```swift +case .variable(let name) // okay, because the name is the same as the label +case .lambda(let parameters, let body) // this is okay too, same reason. +case .variable(let x) // compiler error. label must appear one way or another. +case .lambda(let params, let body) // compiler error, same reason as above. +``` + +Only one of these variants may appear in a single pattern. Swift compiler will +raise a compile error for mixed usage. + +```swift +case .lambda(parameters: let params, let body) // error, can not mix the two. +``` + +Some patterns will no longer match enum cases. For example, all associated +values can bind as a tuple in Swift 3, this will no longer work after this +proposal: ```swift -switch Foo.bar(x: 0, y: 1) { -case .bar: // matches. - print("bar!") +// deprecated: matching all associated values as a tuple +if case let .lambda(f) = anLambdaExpr { + evaluateLambda(parameters: f.parameters, body: f.body) } ``` -## Effect on ABI stability and resilience +## Source compatibility + +Despite a few additions, case declaration remain mostly source-compatible with +Swift 3, with the exception of the change detailed in "Alternative Payload-less +Case Declaration". -After this proposal, enum cases may have compound names, which would be mangled -differently than Swift 3. +Syntax for case constructor at use site remain source-compatible. -The compiler may also layout enums differently now that payloads are not -constrained by having to be part of a tuple. +A large portion of pattern matching syntax for enum cases with associated values +remain unchanged. But patterns for matching all values as a tuple, patterns that +elide the label and binds to names that differ from the labels, patterns that +include labels for some sub-patterns but the rest of them are deprecated by this +proposal. Therefore this is a source breaking change. + +## Effect on ABI stability and resilience + +After this proposal, enum cases may have compound names. This means the standard +library will expose different symbols for enum constructors. The name mangling +rules should also change accordingly. ## Alternative Considered -To maintain maximum source compatibility, we could introduce a rule that matches -all associated values to a labeled tuple. As T.J. Usiyan -[pointed out][TJs comment], implementation of the equality protocol would be -simplified due to tuple's conformance to `Equatable`. This feature may still be -introduced with alternative syntax (perhaps related to splats) later without +Between case declaration and pattern matching, there exist many reasonable +combinations of improvement. On one hand, we can optimize for consistency, +simplicity and teachability by bringing in as much similarity between enum and +other part of the language as possible. Many decisions in the first revision +were made in favor if doing so. Through the feedbacks from swift-evolution, we +found that some of the changes impedes the ergonomics of these features too much +. In this section, we describe some of the alternatives that were raised and +rejected in hope to strike a balance between the two end of the goals. + +We discussed allowing user to declare a *parameter name* ("internal names") +for each associated value. Such names may be used in various rules in pattern +matching. Some feedback suggested they maybe used as property names when we +make enum case subtypes of the enum and resembles a struct. This feature is not +included in this proposal because parameter names are not very useful *today*. +Using them in patterns actually don't improve consistency as users don't use +them outside normal function definitions at all. If enum case gains a function +body in a future proposal, it'd be better to define the semantics of parameter +names then, as opposed to locking it down now. + +To maintain ergonomics/source compatibility, we could allow user to choose +arbitrary bindings for each associated value. The problem is it makes the +pattern deviate a lot from declaration and makes it hard for beginners to +understand. This also decrease readability for seasoned users. + +Along the same line, a pattern that gets dropped is binding all associated +values as a labeled tuple, which tuple pattern allowed in Swift 3. As T.J. +Usiyan [pointed out][TJs comment], implementation of the equality protocol would +be simplified due to tuple's conformance to `Equatable`. This feature may still +be introduced with alternative syntax (perhaps related to splats) later without source-breakage. And the need to implement `Equatable` may also disappear with auto-deriving for `Equatable` conformance. -A syntax that did stay for source compatibility is allowing `()` in patterns -that match enum cases without associated values: - -```swift -if let case .x() = Foo.baz { // … } -``` +The previous revision of this proposal mandated that the labeled form of +sub-pattern (`case .elet(locals: let x, body: let y)`) be the only acceptable +pattern. Turns out the community considers this to be too verbose in some cases. -We could remove this syntax as it would make the pattern look more consistent to -the case's declaration. -[SE-0111]: https://github.com/apple/swift-evolution/blob/master/proposals/0111-remove-arg-label-type-significance.md +[SE-0155]: 0155-normalize-enum-case-representation.md +[SE-0111]: 0111-remove-arg-label-type-significance.md [Daniel Duan]: https://github.com/dduan [Joe Groff]: https://github.com/jckarter -[SE-0155]: 0155-normalize-enum-case-representation.md -[TJs comment]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030614.html -[SE Thread]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030477.html [John McCall]: https://github.com/rjmccall +[TJs comment]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030614.html From a0e647f0117b5dc248f9d3f0e730ff76e3ef9be1 Mon Sep 17 00:00:00 2001 From: Daniel Duan Date: Wed, 8 Mar 2017 19:15:43 -0800 Subject: [PATCH 0212/4563] add metadata for SE-0155 revision 2 --- proposals/0155-normalize-enum-case-representation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/proposals/0155-normalize-enum-case-representation.md b/proposals/0155-normalize-enum-case-representation.md index 767286ea98..2afab7d50f 100644 --- a/proposals/0155-normalize-enum-case-representation.md +++ b/proposals/0155-normalize-enum-case-representation.md @@ -3,6 +3,8 @@ * Proposal: [SE-0155][] * Authors: [Daniel Duan][], [Joe Groff][] * Review Manager: [John McCall][] +* Status: **Awaiting review** +* Previous Revision: [1][Revision 1] ## Introduction @@ -14,6 +16,8 @@ Enums, therefore, can be made more "regular" when we replace tuple as the representation of associated case values. This proposal aims to define the effect of doings so on various parts of the language. +Swift-evolution thread: [Normalize Enum Case Representation (rev. 2)][] + ## Motivation When user declares a case for an enum, a function which constructs the @@ -329,3 +333,5 @@ pattern. Turns out the community considers this to be too verbose in some cases. [Joe Groff]: https://github.com/jckarter [John McCall]: https://github.com/rjmccall [TJs comment]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030614.html +[Revision 1]: https://github.com/apple/swift-evolution/blob/43ca098355762014f53e1b54e02d2f6a01253385/proposals/0155-normalize-enum-case-representation.md +[Normalize Enum Case Representation (rev. 2)]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170306/033626.html From 3c1438de9c0b4fd3fcb443e4eac099546ac27b15 Mon Sep 17 00:00:00 2001 From: Daniel Duan Date: Thu, 9 Mar 2017 14:23:05 -0800 Subject: [PATCH 0213/4563] removed overloading --- ...0155-normalize-enum-case-representation.md | 45 +++---------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/proposals/0155-normalize-enum-case-representation.md b/proposals/0155-normalize-enum-case-representation.md index 2afab7d50f..4d547ce435 100644 --- a/proposals/0155-normalize-enum-case-representation.md +++ b/proposals/0155-normalize-enum-case-representation.md @@ -42,7 +42,7 @@ There are many surprising aspects of enum constructors, however: 1. After [SE-0111][], Swift function's fully qualified name consists of its base name and all of its argument labels. User can use the full name of the function at use site. In the example above, `locals` and `body` are currently - not part of the case constructors name, therefore the expected syntax in + not part of the case constructors name, therefore the expected syntax is invalid. ```swift @@ -53,11 +53,6 @@ There are many surprising aspects of enum constructors, however: 2. Case constructors cannot include a default value for each parameter. This is yet another feature available to functions. -3. Some user may expect, similar to function, that case constructors can be - overloaded. After all, two enum cases with the same full name but different - types are clearly distinct to both the compiler and the user. This is - unavailable to Swift 3 users. - As previous mentioned, these are symptoms of associated values being a tuple instead of having its own distinct semantics. This problem manifests more in Swift 3's pattern matching: @@ -163,35 +158,6 @@ element-name = identifier; enum-case-element-default-value-clause = "=" expression; ``` -### Enum Case "Overloading" - -An enum may contain cases with the same full name but with associated values of -different types. For example: - -```swift -enum Expr { - case literal(Bool) - case literal(Int) -} -``` - -The above cases have overloaded constructors, which follow the same rules as -functions at call site for disambiguation: - -```swift -// It's clear which case is being constructed in the following. -let aBool: Expr = .literal(false) -let anInt: Expr = .literal(42) -``` - -User must specify an `as` expression in sub-patterns in pattern matching, in -order to match with such cases: - -```swift -case .literal(let value) // this is ambiguous -case .literal(let value as Bool) // matches `case literal(Bool)` -``` - ### Alternative Payload-less Case Declaration In Swift 3, the following syntax is valid: @@ -204,12 +170,12 @@ enum Tree { `Tree.leaf` has a very unexpected type to most Swift users: `(()) -> Tree` -We propose this syntax declare the "bare" case instead. So it's going to be -the equivalent of +We propose this syntax become illegal. User must explicitly declare +associated value of type `Void` if needed: ```swift enum Tree { - case leaf // `()` is optional and does the same thing. + case leaf(Void) } ``` @@ -326,6 +292,9 @@ The previous revision of this proposal mandated that the labeled form of sub-pattern (`case .elet(locals: let x, body: let y)`) be the only acceptable pattern. Turns out the community considers this to be too verbose in some cases. +A drafted version of this proposal considered allowing "overloaded" declaration +of enum cases (same full-name, but with associated values with different types). +We ultimately decided that this feature is out of the scope of this proposal. [SE-0155]: 0155-normalize-enum-case-representation.md [SE-0111]: 0111-remove-arg-label-type-significance.md From 34688629e5fa8ebd5740175e470865eee844e0d5 Mon Sep 17 00:00:00 2001 From: Bret Dahlgren Date: Sun, 12 Mar 2017 21:41:37 -0400 Subject: [PATCH 0214/4563] Update 0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md (#638) Fixing small typos. --- ...-for-the-inconsistency-of-nscopyings-behaviour.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md index 45cb583882..09a0616a6a 100644 --- a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md +++ b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md @@ -17,7 +17,7 @@ First of all, in Swift, the Objective-C `copy` property attribute translates to `@NSCopying`. -Like Objective-C, in Swift, avoiding accessing ivar via setter methods in initializer is considered as the best pratice. Unlike Objective-C, which gives developers the freedom to decide on whether assign a value to a property by invoking setter or by accessing ivar directly, accessing a property in Swift from within an initializer always does direct access to the storage rather than going through the setter, even if using `dot` syntax. +Like Objective-C, in Swift, avoiding accessing ivar via setter methods in initializer is considered as the best practice. Unlike Objective-C, which gives developers the freedom to decide on whether assign a value to a property by invoking setter or by accessing ivar directly, accessing a property in Swift from within an initializer always does direct access to the storage rather than going through the setter, even if using `dot` syntax. However, as a side-effect, `@NSCopying` attribute does not work as consistently as we usually expected in Swift initializers after developers declared a property as `@NSCopying`. @@ -90,7 +90,7 @@ print( johnAppleseed ) // Prints "John Appleseed, Engineer" // job stored in `johnAppleseed`. ``` -Up to now, everything seems to run right. However, problem will soon emerge once we begin introducing a new class consuming instances of `Person` class: +Up to now, everything seems to run right. However, problems will soon emerge once we begin introducing a new class consuming instances of `Person` class: ``` swift class Department: NSObject { @@ -140,7 +140,7 @@ print( lab.employee ) // Prints "Isaac Newton, Astronomer" Setting the job of `isaacNewton` affects the job stored in `lab.employee`. That's an unexpected behavior as we have declared `employee` property as `@NSCopying`. Obviously, `@NSCopying` semantic became effectless implicitly in the initializer of `Department` class. -For the moment, if we indeed require copy we have to invoke `copy()` method explictly on instances that wanna be copied to make sure that classes' properties are able to store deeply-copied results during the initialization: +For the moment, if we indeed require copy we have to invoke `copy()` method explictly on instances that want to be copied to make sure that classes' properties are able to store deeply-copied results during the initialization: ``` swift init( employee candidate: Person ) { @@ -160,9 +160,9 @@ print( isaacNewton ) // Prints "Isaac Newton, Physicist" print( lab.employee ) // Prints "Isaac Newton, Astronomer" ``` -It is undeniably reasonable to enfoce programmers to access instance variables directly from initializer methods because of the potential troubles made by setter methods' additional side-effects when the initialization is not complete yet. However, I believe we at least should be warned by Swift compiler when we assigned an instance of `NSCopying` conforming class to a class's property declared as `@NSCopying` during the initialization. +It is undeniably reasonable to enforce programmers to access instance variables directly from initializer methods because of the potential troubles made by setter methods' additional side-effects when the initialization is not complete yet. However, I believe we at least should be warned by the Swift compiler when we assigned an instance of `NSCopying` conforming class to a class's property declared as `@NSCopying` during the initialization. -In Objective-C, developers can make decision on this process explicitly by writting done either: +In Objective-C, developers can make a decision on this process explicitly by writing done either: ```objc - ( instancetype )initWithName: ( NSString* )name { @@ -182,7 +182,7 @@ or: } ``` -Speaking of Swift, however, there is no stuff like `->` operator to access ivar directly. As a result, with property marked with `@NSCopying` attribute, developers who are new to this language, expecially those who have had experience of writting Objective-C, are likely to automatically suppose it acts normally when they're writing down code like `self.employee = candidate` in initializer. That's bug-prone. +Speaking of Swift, however, there is no stuff like `->` operator to access ivar directly. As a result, with property marked with `@NSCopying` attribute, developers who are new to this language, expecially those who have had experience of writing Objective-C, are likely to automatically suppose it acts normally when they're writing down code like `self.employee = candidate` in initializer. That's bug-prone. ## Proposed solution From 65da95ca437a0aaae7d39712635b0048e921e7c8 Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Thu, 16 Mar 2017 22:37:03 -0700 Subject: [PATCH 0215/4563] Revise proposal based on feedback during evolution review --- ...8-package-manager-manifest-api-redesign.md | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/proposals/0158-package-manager-manifest-api-redesign.md b/proposals/0158-package-manager-manifest-api-redesign.md index d424bc80c4..07d63c0596 100644 --- a/proposals/0158-package-manager-manifest-api-redesign.md +++ b/proposals/0158-package-manager-manifest-api-redesign.md @@ -3,7 +3,7 @@ * Proposal: [SE-0158](0158-package-manager-manifest-api-redesign.md) * Author: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Rick Ballard](https://github.com/rballard) -* Status: **Active review (March 7...March 13, 2017)** +* Status: **Active review (March 7...March 15, 2017)** * Bug: [SR-3949](https://bugs.swift.org/browse/SR-3949) ## Introduction @@ -404,16 +404,13 @@ access modifier is `public` for all APIs unless specified. /// The requirement is specified by a source control branch. case branch(String) - /// Creates a specifier for an exact version. - static func only(_ version: Version) -> Requirement - /// Creates a specified for a range starting at the given lower bound /// and going upto next major version. - static func uptoNextMajor(_ version: Version) -> Requirement + static func upToNextMajor(from version: Version) -> Requirement /// Creates a specified for a range starting at the given lower bound /// and going upto next minor version. - static func uptoNextMinor(_ version: Version) -> Requirement + static func upToNextMinor(from version: Version) -> Requirement } ``` @@ -421,13 +418,13 @@ access modifier is `public` for all APIs unless specified. ```swift // 1.5.8 ..< 2.0.0 - .package(url: "/SwiftyJSON", .uptoNextMajor("1.5.8")), + .package(url: "/SwiftyJSON", .upToNextMajor(from: "1.5.8")), // 1.5.8 ..< 1.6.0 - .package(url: "/SwiftyJSON", .uptoNextMinor("1.5.8")), + .package(url: "/SwiftyJSON", .upToNextMinor(from: "1.5.8")), // 1.5.8 - .package(url: "/SwiftyJSON", .only("1.5.8")), + .package(url: "/SwiftyJSON", .exact("1.5.8")), ``` * This will also give us ability to add more complex features in future: @@ -436,9 +433,9 @@ access modifier is `public` for all APIs unless specified. > Note that we're not actually proposing these as part of this proposal. ```swift - .package(url: "/SwiftyJSON", .uptoNextMajor("1.5.8").excluding("1.6.4")), + .package(url: "/SwiftyJSON", .upToNextMajor(from: "1.5.8").excluding("1.6.4")), - .package(url: "/SwiftyJSON", .only("1.5.8", "1.6.3")), + .package(url: "/SwiftyJSON", .exact("1.5.8", "1.6.3")), ``` * We will introduce a factory method which takes `Range`, to specify @@ -456,13 +453,14 @@ access modifier is `public` for all APIs unless specified. // Constraint to an arbitrary closed range. .package(url: "/SwiftyJSON", "1.2.3"..."1.2.8"), ``` - * As specified by branch - [proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0150-package-manager-branch-support.md), - we will add these factory methods: + * As a slight modification to the + [branch proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0150-package-manager-branch-support.md), + we will add cases for specifying a branch or revision, rather than + adding factory methods for them: ```swift - .package(url: "/SwiftyJSON", branch: "develp"), - .package(url: "/SwiftyJSON", revision: "e74b07278b926c9ec6f9643455ea00d1ce04a021") + .package(url: "/SwiftyJSON", .branch("develop")), + .package(url: "/SwiftyJSON", .revision("e74b07278b926c9ec6f9643455ea00d1ce04a021")) ``` * We will remove all of the current factory methods: @@ -512,9 +510,9 @@ access modifier is `public` for all APIs unless specified. .libary(name: "PaperDy", type: .dynamic, targets: ["Paper"]), ], dependencies: [ - .package(url: "http://github.com/SwiftyJSON", from: "1.2.3"), - .package(url: "../CHTTPParser", .uptoNextMinor("2.2.0")), - .package(url: "http://some/other/lib", .only("1.2.3")), + .package(url: "http://github.com/SwiftyJSON/SwiftyJSON"", from: "1.2.3"), + .package(url: "../CHTTPParser", .upToNextMinor(from: "2.2.0")), + .package(url: "http://some/other/lib", .exact("1.2.3")), ] targets: [ .target( @@ -554,9 +552,9 @@ let package = Package( .libary(name: "PaperDynamic", type: .dynamic, targets: ["Paper"]), ], dependencies: [ - .package(url: "http://github.com/SwiftyJSON", from: "1.2.3"), - .package(url: "../CHTTPParser", .uptoNextMinor("2.2.0")), - .package(url: "http://some/other/lib", .only("1.2.3")), + .package(url: "http://github.com/SwiftyJSON/SwiftyJSON"", from: "1.2.3"), + .package(url: "../CHTTPParser", .upToNextMinor(from: "2.2.0")), + .package(url: "http://some/other/lib", .exact("1.2.3")), ] targets: [ .target( From 4ba5780cc25e687ab5964e22e2236a9023c1a2ce Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Thu, 16 Mar 2017 23:02:20 -0700 Subject: [PATCH 0216/4563] Accept SE-0158 Package Manager Manifest API Redesign --- proposals/0158-package-manager-manifest-api-redesign.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0158-package-manager-manifest-api-redesign.md b/proposals/0158-package-manager-manifest-api-redesign.md index 07d63c0596..83db94a03e 100644 --- a/proposals/0158-package-manager-manifest-api-redesign.md +++ b/proposals/0158-package-manager-manifest-api-redesign.md @@ -3,8 +3,9 @@ * Proposal: [SE-0158](0158-package-manager-manifest-api-redesign.md) * Author: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Rick Ballard](https://github.com/rballard) -* Status: **Active review (March 7...March 15, 2017)** +* Status: **Accepted** * Bug: [SR-3949](https://bugs.swift.org/browse/SR-3949) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170313/033989.html) ## Introduction From 33fd9cfae88eb8d18cf61f4af9abfb33636a0e84 Mon Sep 17 00:00:00 2001 From: Tanner Date: Sat, 18 Mar 2017 18:19:26 +0100 Subject: [PATCH 0217/4563] remove extra `"` --- proposals/0158-package-manager-manifest-api-redesign.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0158-package-manager-manifest-api-redesign.md b/proposals/0158-package-manager-manifest-api-redesign.md index 83db94a03e..c56e9e998f 100644 --- a/proposals/0158-package-manager-manifest-api-redesign.md +++ b/proposals/0158-package-manager-manifest-api-redesign.md @@ -553,7 +553,7 @@ let package = Package( .libary(name: "PaperDynamic", type: .dynamic, targets: ["Paper"]), ], dependencies: [ - .package(url: "http://github.com/SwiftyJSON/SwiftyJSON"", from: "1.2.3"), + .package(url: "http://github.com/SwiftyJSON/SwiftyJSON", from: "1.2.3"), .package(url: "../CHTTPParser", .upToNextMinor(from: "2.2.0")), .package(url: "http://some/other/lib", .exact("1.2.3")), ] From 2585d641ef0d075b98b11eaee57394f5d0e3eef5 Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Mon, 20 Mar 2017 14:00:10 +0530 Subject: [PATCH 0218/4563] [SE-0158] Fix an expandable section --- proposals/0158-package-manager-manifest-api-redesign.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0158-package-manager-manifest-api-redesign.md b/proposals/0158-package-manager-manifest-api-redesign.md index c56e9e998f..c0fae27276 100644 --- a/proposals/0158-package-manager-manifest-api-redesign.md +++ b/proposals/0158-package-manager-manifest-api-redesign.md @@ -249,8 +249,8 @@ access modifier is `public` for all APIs unless specified. let package = Package( name: "Foo", target: [ - - Target(name: "Foo", dependencies: ["Utility"]), - + .target(name: "Foo", dependencies: ["Utility"]), + - Target(name: "Foo", dependencies: ["Utility"]), + + .target(name: "Foo", dependencies: ["Utility"]), ] ) ``` From 2a424c36f5c1e39ca384001211a07d423b9d98b2 Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Mon, 20 Mar 2017 14:04:59 +0530 Subject: [PATCH 0219/4563] [SE-0158] Minor markdown formatting fixes --- proposals/0158-package-manager-manifest-api-redesign.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0158-package-manager-manifest-api-redesign.md b/proposals/0158-package-manager-manifest-api-redesign.md index c0fae27276..ded1e1f0ab 100644 --- a/proposals/0158-package-manager-manifest-api-redesign.md +++ b/proposals/0158-package-manager-manifest-api-redesign.md @@ -44,6 +44,7 @@ access modifier is `public` for all APIs unless specified.
View diff

+ ```diff struct Version { - func successor() -> Version @@ -74,7 +75,7 @@ access modifier is `public` for all APIs unless specified.

View diff and example

- Diff: + Diff: ```diff final class Target { - let name: String @@ -511,7 +512,7 @@ access modifier is `public` for all APIs unless specified. .libary(name: "PaperDy", type: .dynamic, targets: ["Paper"]), ], dependencies: [ - .package(url: "http://github.com/SwiftyJSON/SwiftyJSON"", from: "1.2.3"), + .package(url: "http://github.com/SwiftyJSON/SwiftyJSON", from: "1.2.3"), .package(url: "../CHTTPParser", .upToNextMinor(from: "2.2.0")), .package(url: "http://some/other/lib", .exact("1.2.3")), ] From 9705c7288a07bf92a860a8b129135c81fa154edf Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Mon, 20 Mar 2017 19:35:38 +0530 Subject: [PATCH 0220/4563] Update SE-0158 status --- proposals/0158-package-manager-manifest-api-redesign.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0158-package-manager-manifest-api-redesign.md b/proposals/0158-package-manager-manifest-api-redesign.md index ded1e1f0ab..d6fd6b8c07 100644 --- a/proposals/0158-package-manager-manifest-api-redesign.md +++ b/proposals/0158-package-manager-manifest-api-redesign.md @@ -3,7 +3,7 @@ * Proposal: [SE-0158](0158-package-manager-manifest-api-redesign.md) * Author: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Rick Ballard](https://github.com/rballard) -* Status: **Accepted** +* Status: **Implemented (Swift 4)** * Bug: [SR-3949](https://bugs.swift.org/browse/SR-3949) * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170313/033989.html) From 15fdaeca540dfa4ca1b9550c213fbe035009dc51 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 20 Mar 2017 10:32:24 -0700 Subject: [PATCH 0221/4563] Accept SE-0156: Class and Subtype existentials --- proposals/0156-subclass-existentials.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0156-subclass-existentials.md b/proposals/0156-subclass-existentials.md index c4d797a2c1..882dac94f8 100644 --- a/proposals/0156-subclass-existentials.md +++ b/proposals/0156-subclass-existentials.md @@ -3,7 +3,8 @@ * Proposal: [SE-0156](0156-subclass-existentials.md) * Authors: [David Hart](http://github.com/hartbit), [Austin Zheng](http://github.com/austinzheng) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (February 28...March 7, 2017)** +* Status: **Accepted** +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170320/034123.html) ## Introduction From 7c4ace04d03bac097d3afc4e60dffc2fddb2edaa Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 20 Mar 2017 11:00:05 -0700 Subject: [PATCH 0222/4563] SE-0156: Amend proposal by removing the ordering rules, per core team decision --- proposals/0156-subclass-existentials.md | 54 +++++++++++++++++++------ 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/proposals/0156-subclass-existentials.md b/proposals/0156-subclass-existentials.md index 882dac94f8..d951096f0f 100644 --- a/proposals/0156-subclass-existentials.md +++ b/proposals/0156-subclass-existentials.md @@ -31,7 +31,7 @@ We propose to provide similar expressive power to Swift, which will also improve ## Proposed solution -The proposal keeps the existing `&` syntax but allows the first element, and only the first, to be either the `AnyObject` keyword or of class type. The equivalent to the above Objective-C types would look like this: +The proposal keeps the existing `&` syntax but allows one of the elements to be either `AnyObject` or of class type. The equivalent to the above Objective-C types would look like this: ```swift AnyObject & Protocol1 & Protocol2 @@ -42,18 +42,20 @@ As in Objective-C, the first line is an existential of classes which conform to Here are the new proposed rules for what is valid in a existential conjunction syntax: -### 1. The first element in the protocol composition syntax can be the `AnyObject` keyword to enforce a class constraint: +### 1. An element in the protocol composition syntax can be the `AnyObject` keyword to enforce a class constraint: ```swift protocol P {} struct S : P {} class C : P {} -let t: P & AnyObject // Compiler error: AnyObject requirement must be in first position -let u: AnyObject & P = S() // Compiler error: S is not of class type -let v: AnyObject & P = C() // Compiles successfully +class D { } +let t: AnyObject & P = S() // Compiler error: S is not of class type +let u: AnyObject & P = C() // Compiles successfully +let v: P & AnyObject = C() // Compiles successfully +let w: P & AnyObject = D() // Compiler error: class D does not conform to protocol P ``` -### 2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class: +### 2. An element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class: ```swift protocol P {} @@ -61,17 +63,37 @@ struct S {} class C {} class D : P {} class E : C, P {} -let t: P & C // Compiler error: subclass constraint must be in first position let u: S & P // Compiler error: S is not of class type let v: C & P = D() // Compiler error: D is not a subtype of C let w: C & P = E() // Compiles successfully ``` -### 3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps: - -* Expand the typealias -* Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a `class` constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class). -* Check that the type does not contain two class-type constraints +### 3. If a protocol composition contains both a class type and `AnyObject`, the class type supersedes the `AnyObject` constraint: + +```swift +protocol P {} +class C {} +class D : C, P { } +let u: AnyObject & C & P = D() // Okay: D is a subclass of C and conforms to P +let v: C & P = u // Okay: C & P is equivalent to AnyObject & C & P +let w: AnyObject & C & P = v // Okay: AnyObject & C & P is equivalent to C & P +``` + +### 4. If a protocol composition contains two class types, either the class types must be the same or one must be a subclass of the other. In the latter case, the subclass type supersedes the superclass type: + +```swift +protocol P {} +class C {} +class D : C { } +class E : C { } +class F : D, P { } +let t: C & D & P = F() // Okay: F is a subclass of D and conforms to P +let u: D & P = t // Okay: D & P is equivalent to C & D & P +let v: C & D & P = u // Okay: C & D & P is equivalent to D & P +let w: D & E & P // Compiler error: D is not a subclass of E or vice-versa +``` + +### 5. When a protocol composition type contains one or more typealiases, the validity of the type is determined by expanding the typealiases into their component protocols, class types, and `AnyObject` constraints, then following the rules described above: ```swift class C {} @@ -181,6 +203,14 @@ That would then cause the Swift code run in version 4 mode to fail to compile wi An alternative solution to the `class`/`AnyObject` duplication was to keep both, redefine `AnyObject` as `typealias AnyObject = class` and favor the latter when used as a type name. +The [reviewed version of the +proposal](https://github.com/apple/swift-evolution/blob/78da25ec4acdc49ad9b68fb58300e49c33bc6355/proposals/0156-subclass-existentials.md) +included rules that required the class type (or `AnyObject`) to be +first within the protocol composition, e.g., `AnyObject & Protocol1` +was well-formed but `Protocol1 & AnyObject` would produce a compiler +error. When accepting this proposal, the core team removed these +rules; see the decision notes at the top for more information. + ## Acknowledgements Thanks to [Austin Zheng](http://github.com/austinzheng) and [Matthew Johnson](https://github.com/anandabits) who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from. From f97e35283a1ef5992715812687725948e86a9889 Mon Sep 17 00:00:00 2001 From: Torin Kwok Date: Tue, 21 Mar 2017 02:03:36 +0800 Subject: [PATCH 0223/4563] Fixed a typo in SE-0153 (#642) `introduce` => `introducing` --- ...-compensate-for-the-inconsistency-of-nscopyings-behaviour.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md index 09a0616a6a..3023583f92 100644 --- a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md +++ b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md @@ -200,7 +200,7 @@ The proposal doesn't change the ABI of existing language features. ### Compile-time checking -Instead of introduce the copy within the initializer, have the compiler emit a **compile-time error or warning** if developers are performing an assignment operation from within an initializer between a property declared as `@NSCopying` and an instance of a `` protocol conforming class. Also, speaking of GUI integrated development environments such as Xcode, leaving this kind of error or warning **FIXABLE** would be needed in order to make them can be quickly fixed by both IDEs and migrator tools through simply appending `.copy() as! AutoInferredClassType`. +Instead of introducing the copy within the initializer, have the compiler emit a **compile-time error or warning** if developers are performing an assignment operation from within an initializer between a property declared as `@NSCopying` and an instance of a `` protocol conforming class. Also, speaking of GUI integrated development environments such as Xcode, leaving this kind of error or warning **FIXABLE** would be needed in order to make them can be quickly fixed by both IDEs and migrator tools through simply appending `.copy() as! AutoInferredClassType`. With the adjustment mentioned above, following code fragment, for instance, will no longer be successfully compiled: From 6f370ac576a0f60aab1c068790e0a73ecb773872 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 20 Mar 2017 13:20:42 -0700 Subject: [PATCH 0224/4563] Record SR for SE-0156 --- proposals/0156-subclass-existentials.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0156-subclass-existentials.md b/proposals/0156-subclass-existentials.md index d951096f0f..6d027453c7 100644 --- a/proposals/0156-subclass-existentials.md +++ b/proposals/0156-subclass-existentials.md @@ -5,6 +5,7 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Accepted** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170320/034123.html) +* Bug: [SR-4296](https://bugs.swift.org/browse/SR-4296) ## Introduction From 46dee6c68d515b76bbe38dbafead3b69fefa2cec Mon Sep 17 00:00:00 2001 From: David Hart Date: Tue, 21 Mar 2017 00:46:53 +0100 Subject: [PATCH 0225/4563] Fix private access levels proposal (#627) * Fix private access levels proposal * minor fixes --- proposals/XXXX-fix-private-access-levels.md | 50 +++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 proposals/XXXX-fix-private-access-levels.md diff --git a/proposals/XXXX-fix-private-access-levels.md b/proposals/XXXX-fix-private-access-levels.md new file mode 100644 index 0000000000..fb0bd6f86f --- /dev/null +++ b/proposals/XXXX-fix-private-access-levels.md @@ -0,0 +1,50 @@ +# Fix Private Access Levels + +* Proposal: [SE-XXXX]() +* Authors: [David Hart](http://github.com/hartbit) +* Review Manager: TBD +* Status: TBD + +## Introduction + +This proposal presents the problems that came with the the access level modifications in [SE-0025](https://github.com/apple/swift-evolution/blob/master/proposals/0025-scoped-access-level.md) and proposes reverting to Swift 2 behaviour. + +## Motivation + +Since the release of Swift 3, the access level change of SE-0025 was met with dissatisfaction by a substantial proportion of the general Swift community. Those changes can be viewed as *actively harmful*, the new requirement for syntax/API changes. + +The `private` keyword is a "soft default" access modifier for restricting access within a file. Scoped access is not a good behavior for a "soft default" because it is extremely common to use several extensions within a file. A "soft default" (and therefore `private`) should work well with this idiom. It is fair to say that changing the behavior of `private` such that it does not work well with extensions meets the criteria of actively harmful in the sense that it subtly encourages overuse of scoped access control and discourages the more reasonable default by giving it the awkward name `fileprivate`. + +Compared to a file-based access level, the scoped-based access level adds meaningful information by hiding implementation details which do not concern other types or extensions in the same file. But is that distinction between `private` and `fileprivate` actively used by the larger community of Swift developers? And if it were used pervasively, would it be worth the cognitive load and complexity of keeping two very similar access levels in the language? This proposal argues that answer to both questions is no and therefore wish to simplify Swift's access control story by removing scoped access and leaving more design breathing space for future discussions around submodules. + +## Detailed design + +The `private` keyword should be reverted back to its Swift 2 file-based meaning and the `fileprivate` keyword should be deprecated. + +## Source compatibility + +In Swift 3 compatibility mode, the compiler will continue to treat `private` and `fileprivate` as was previously the case. + +In Swift 4 mode, the compiler will deprecate the `fileprivate` keyword and revert the semantics of the `private` access level to be file based. The migrator will rename all uses of `fileprivate` to `private`. + +Cases where a type had `private` declarations with the same signature in different scopes will produce a compiler error. For example, the following piece of code compiles in Swift 3 compatibilty mode but generates a `Invalid redeclaration of 'foo()'` error in Swift 4 mode. + +```swift +struct Foo { + private func bar() {} +} + +extension Foo { + private func bar() {} +} +``` + +## Alternatives Considered + +1. Deprecate `fileprivate` and modify the semantics of `private` to include same-type extension scopes in the same file. +2. Deprecate `fileprivate` and modify the semantics of `private` to include same-type extension scopes in the same module. +3. Revert `private` to be file-based and introduce the scope-based access level under a new name. + +## Thanks + +I'd like to extend my thanks to Xiaodi Wu and Matthew Johnson for their respective contributions. \ No newline at end of file From 3ce8d07fc0bd6511b92da136093f6147d67edfb1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 20 Mar 2017 16:52:00 -0700 Subject: [PATCH 0226/4563] Initiate review of SE-0159: Fix Private Access Levels --- ...e-access-levels.md => 0159-fix-private-access-levels.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{XXXX-fix-private-access-levels.md => 0159-fix-private-access-levels.md} (95%) diff --git a/proposals/XXXX-fix-private-access-levels.md b/proposals/0159-fix-private-access-levels.md similarity index 95% rename from proposals/XXXX-fix-private-access-levels.md rename to proposals/0159-fix-private-access-levels.md index fb0bd6f86f..de1c863f05 100644 --- a/proposals/XXXX-fix-private-access-levels.md +++ b/proposals/0159-fix-private-access-levels.md @@ -1,9 +1,9 @@ # Fix Private Access Levels -* Proposal: [SE-XXXX]() +* Proposal: [SE-0159]() * Authors: [David Hart](http://github.com/hartbit) -* Review Manager: TBD -* Status: TBD +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (March 20...27, 2017)** ## Introduction From 18f817f22d0f60bd914712eb25d6102d24b3582e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 20 Mar 2017 16:53:08 -0700 Subject: [PATCH 0227/4563] Fix missing link --- proposals/0159-fix-private-access-levels.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0159-fix-private-access-levels.md b/proposals/0159-fix-private-access-levels.md index de1c863f05..58b76a5e2b 100644 --- a/proposals/0159-fix-private-access-levels.md +++ b/proposals/0159-fix-private-access-levels.md @@ -1,6 +1,6 @@ # Fix Private Access Levels -* Proposal: [SE-0159]() +* Proposal: [SE-0159](0159-fix-private-access-levels.md) * Authors: [David Hart](http://github.com/hartbit) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active review (March 20...27, 2017)** From ca8d7629b9958112c39a011055003f3758f04811 Mon Sep 17 00:00:00 2001 From: Avery Magnotti Date: Mon, 20 Mar 2017 19:57:28 -0400 Subject: [PATCH 0228/4563] fix broken links (#637) --- releases/swift-3_0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releases/swift-3_0.md b/releases/swift-3_0.md index 482e340585..6f80064510 100644 --- a/releases/swift-3_0.md +++ b/releases/swift-3_0.md @@ -43,8 +43,8 @@ development experience. It focused on several areas: Swift 3 is the first release to enable broad scale adoption across multiple platforms, including significant -functionality in the [Swift core libraries](../https://swift.org/core-libraries/) -(Foundation, libdispatch, XCTest, etc), portability to a number of platforms including Linux/x86, Raspberry Pi, and Android, and the [Swift package manager](../https://swift.org/package-manager/) to easily manage the distribution of Swift soure code. +functionality in the [Swift core libraries](https://swift.org/core-libraries/) +(Foundation, libdispatch, XCTest, etc), portability to a number of platforms including Linux/x86, Raspberry Pi, and Android, and the [Swift package manager](https://swift.org/package-manager/) to easily manage the distribution of Swift soure code. Finally, Swift 3 also includes a mix of relatively small but important additions to the language and standard library that make solving common problems easier and From d82633592073db84a43c222364a7f07d7810a472 Mon Sep 17 00:00:00 2001 From: Alex Curran Date: Tue, 21 Mar 2017 13:52:12 +0100 Subject: [PATCH 0229/4563] [SE-0159] Change incorrectly specified method name (#653) --- proposals/0159-fix-private-access-levels.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0159-fix-private-access-levels.md b/proposals/0159-fix-private-access-levels.md index 58b76a5e2b..1760215d46 100644 --- a/proposals/0159-fix-private-access-levels.md +++ b/proposals/0159-fix-private-access-levels.md @@ -27,7 +27,7 @@ In Swift 3 compatibility mode, the compiler will continue to treat `private` and In Swift 4 mode, the compiler will deprecate the `fileprivate` keyword and revert the semantics of the `private` access level to be file based. The migrator will rename all uses of `fileprivate` to `private`. -Cases where a type had `private` declarations with the same signature in different scopes will produce a compiler error. For example, the following piece of code compiles in Swift 3 compatibilty mode but generates a `Invalid redeclaration of 'foo()'` error in Swift 4 mode. +Cases where a type had `private` declarations with the same signature in different scopes will produce a compiler error. For example, the following piece of code compiles in Swift 3 compatibilty mode but generates a `Invalid redeclaration of 'bar()'` error in Swift 4 mode. ```swift struct Foo { @@ -47,4 +47,4 @@ extension Foo { ## Thanks -I'd like to extend my thanks to Xiaodi Wu and Matthew Johnson for their respective contributions. \ No newline at end of file +I'd like to extend my thanks to Xiaodi Wu and Matthew Johnson for their respective contributions. From 323f539cc88048835ccb6961a41c56894b31cd54 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 22 Mar 2017 00:28:15 -0400 Subject: [PATCH 0230/4563] Accept SE-0157. --- proposals/0157-recursive-protocol-constraints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0157-recursive-protocol-constraints.md b/proposals/0157-recursive-protocol-constraints.md index 06a551631b..a4b516a3d3 100644 --- a/proposals/0157-recursive-protocol-constraints.md +++ b/proposals/0157-recursive-protocol-constraints.md @@ -3,7 +3,7 @@ * Proposal: [SE-0157](0157-recursive-protocol-constraints.md) * Authors: [Douglas Gregor](https://github.com/DougGregor), [Erica Sadun](https://github.com/erica), [Austin Zheng](https://github.com/austinzheng) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (February 28...March 8, 2017)** +* Status: **Accepted** ## Introduction From 9da450f7474544f8cb335fe0d0f2164a1a0f67a8 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 22 Mar 2017 05:52:12 +0000 Subject: [PATCH 0231/4563] [SE-0157] Add the Rationale and SR-1445 links --- proposals/0157-recursive-protocol-constraints.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/0157-recursive-protocol-constraints.md b/proposals/0157-recursive-protocol-constraints.md index a4b516a3d3..0426b6a571 100644 --- a/proposals/0157-recursive-protocol-constraints.md +++ b/proposals/0157-recursive-protocol-constraints.md @@ -4,6 +4,8 @@ * Authors: [Douglas Gregor](https://github.com/DougGregor), [Erica Sadun](https://github.com/erica), [Austin Zheng](https://github.com/austinzheng) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Accepted** +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170320/034266.html) +* Bug: [SR-1445](https://bugs.swift.org/browse/SR-1445) ## Introduction From ce2824c1dc738c990a2faee3d6e4a615380c62a2 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 21 Mar 2017 22:54:27 -0700 Subject: [PATCH 0232/4563] Proposal to limit `@objc` inference. (#624) --- proposals/NNNN-objc-inference.md | 472 +++++++++++++++++++++++++++++++ 1 file changed, 472 insertions(+) create mode 100644 proposals/NNNN-objc-inference.md diff --git a/proposals/NNNN-objc-inference.md b/proposals/NNNN-objc-inference.md new file mode 100644 index 0000000000..2bb7bcb1ae --- /dev/null +++ b/proposals/NNNN-objc-inference.md @@ -0,0 +1,472 @@ +# Limiting `@objc` inference + +* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-objc-inference.md) +* Author(s): [Doug Gregor](https://github.com/DougGregor) +* Status: **Awaiting review** +* Review manager: TBD + +## Introduction + +One can explicitly write `@objc` on any Swift declaration that can be +expressed in Objective-C. As a convenience, Swift also *infers* +`@objc` in a number of places to improve interoperability with +Objective-C and eliminate boilerplate. This proposal scales back the +inference of `@objc` to only those cases where the declaration *must* +be available to Objective-C to maintain semantic coherence of the model, +e.g., when overriding an `@objc` method or implementing a requirement +of an `@objc` protocol. Other cases currently supported (e.g., a +method declared in a subclass of `NSObject`) would no longer infer +`@objc`, but one could continue to write it explicitly to produce +Objective-C entry points. + +Swift-evolution thread: [here](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160509/017308.html) and [here](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170102/029909.html) + +## Motivation + +There are several observations motivating this proposal. The first is +that Swift's rules for inference of `@objc` are fairly baroque, and it +is often unclear to users when `@objc` will be inferred. This proposal +seeks to make the inference rules more straightforward. The second +observation is that it is fairly easy to write Swift classes that +inadvertently cause Objective-C selector collisions due to +overloading, e.g., + +```swift +class MyNumber : NSObject { + init(_ int: Int) { } + init(_ double: Double) { } // error: initializer 'init' with Objective-C selector 'init:' + // conflicts with previous declaration with the same Objective-C selector +} +``` + +The example above also illustrates the third observation, which is +that code following the [Swift API Design +Guidelines](https://swift.org/documentation/api-design-guidelines/) +will use Swift names that often translate into very poor Objective-C +names that violate the [Objective-C Coding Guidelines for +Cocoa](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html). Specifically, +the Objective-C selectors for the initializers above should include a noun +describing the first argument, e.g., `initWithInteger:` and +`initWithDouble:`, which requires explicit `@objc` annotations anyway: + +```swift +class MyNumber : NSObject { + @objc(initWithInteger:) init(_ int: Int) { } + @objc(initWithDouble:) init(_ double: Double) { } +} +``` + +The final observation is that there is a cost for each Objective-C +entry point, because the Swift compiler must create a "thunk" method +that maps from the Objective-C calling convention to the Swift calling +convention and is recorded within Objective-C metadata. This increases +the size of the binary (preliminary tests on some Cocoa[Touch] apps +found that 6-8% of binary size was in these thunks alone, some of +which are undoubtedly unused), and can have some impact on load time +(the dynamic linker has to sort through the Objective-C metadata for +these thunks). + +## Proposed solution + +The proposed solution is to limit the inference of `@objc` to only +those places where it is required for semantic consistency of the +programming model. + +### Constructs that (still) infer `@objc` + +Specifically, `@objc` will continue to be inferred +for a declaration when: + +* The declaration is an override of an `@objc` declaration, e.g., + + ```swift + class Super { + @objc func foo() { } + } + + class Sub : Super { + /* inferred @objc */ + override func foo() { } + } + ``` + + This inference is required so that Objective-C callers to the method + `Super.foo()` will appropriately invoke the overriding method + `Sub.foo()`. + +* The declaration satisfies a requirement of an `@objc` protocol, + e.g., + + ```swift + @objc protocol MyDelegate { + func bar() + } + + class MyClass : MyDelegate { + /* inferred @objc */ + func bar() { } + } + ``` + + This inference is required because anyone calling + `MyDelegate.bar()`, whether from Objective-C or Swift, will do so + via an Objective-C message send, so conforming to the protocol + requires an Objective-C entry point. + +* The declaration has the `@IBAction` or `@IBOutlet` attribute. This + inference is required because the interaction with Interface Builder + occurs entirely through the Objective-C runtime, and therefore + depends on the existence of an Objective-C entrypoint. + +* The declaration has the `@NSManaged` attribute. This inference is + required because the interaction with CoreData occurs entirely + through the Objective-C runtime, and therefore depends on the + existence of an Objective-C entrypoint. + +The list above describes cases where Swift 3 already performs +inference of `@objc` and will continue to do so if this proposal is +accepted. + +### `dynamic` no longer infers `@objc` + +A declaration that is `dynamic` will no longer infer `@objc`. For example: + +```swift +class MyClass { + dynamic func foo() { } // error: 'dynamic' method must be '@objc' + @objc dynamic func bar() { } // okay +} +``` + +This change is intended to separate current implementation +limitations from future language evolution: the current +implementation supports `dynamic` by always using the Objective-C +message send mechanism, allowing replacement of `dynamic` +implementations via the Objective-C runtime (e.g., `class_addMethod` +and `class_replaceMethod`). In the future, it is plausible that the +Swift language and runtime will evolve to support `dynamic` without +relying on the Objective-C runtime, and it's important that we leave +the door open for that language evolution. + +This change therefore does two things. First, it makes it clear that +the `dynamic` behavior is tied to the Objective-C runtime. Second, +it means that well-formed Swift 4 code will continue to work in the +same way should Swift gain the ability to provide `dynamic` without +relying on Objective-C: at that point, the method `foo()` above will +become well-formed, and the method `bar()` will continue to work as +it does today through the Objective-C runtime. Indeed, this change +is the right way forward even if Swift never supports `dynamic` in +its own runtime, following the precedent of +[SE-0070](https://github.com/apple/swift-evolution/blob/master/proposals/0070-optional-requirements.md), +which required the Objective-C-only protocol feature "optional +requirements" to be explicitly marked with `@objc`. + +### `NSObject`-derived classes no longer infer `@objc` + +A declaration within an `NSObject`-derived class will no longer infer +`@objc`. For example: + +```swift +class MyClass : NSObject { + func foo() { } // not exposed to Objective-C in Swift 4 +} +``` + +This is the only major change of this proposal, because it means +that a large number of methods that Swift 3 would have exposed to +Objective-C (and would, therefore, be callable from Objective-C code +in a mixed project) will no longer be exposed. On the other hand, +this is the most unpredictable part of the Swift 3 model, because +such methods infer `@objc` only when the method can be expressed in +Objective-C. For example: + +```swift +extension MyClass { + func bar(param: ObjCClass) { } // exposed to Objective-C in Swift 3; not exposed by this proposal + func baz(param: SwiftStruct) { } // not exposed to Objective-C +} +``` + +With this proposal, neither method specifies `@objc` nor is either +required by the semantic model to expose an Objective-C entrypoint, +so they don't infer `@objc`: there is no need to reason about the +type of the parameter's suitability in Objective-C. + +## Side benefit: more reasonable expectations for `@objc` protocol extensions + +Users are often surprised to realize that extensions of `@objc` +protocols do not, in fact, produce Objective-C entrypoints: + +```swift +@objc protocol P { } + +extension P { + func bar() { } +} + +class C : NSObject { } + +let c = C() +print(c.respondsToSelector("bar")) // prints "false" +``` + +The expectation that `P.bar()` has an Objective-C entry point is set +by the fact that `NSObject`-derived Swift classes do implicitly create +Objective-C entry points for declarations within class extensions when +possible, but Swift does not (and, practically speaking, cannot) do +the same for protocol extensions. + +A previous mini-proposal [discussed +here](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005312.html) +suggested requiring `@nonobjc` for members of `@objc` protocol +extensions. However, limiting inference of `@objc` eliminates the +expectation itself, addressing the problem from a different angle. + +## Source compatibility + +The two changes that remove inference of `@objc` are both +source-breaking in different ways. The `dynamic` change mostly +straightforward: + +* In Swift 4 mode, introduce an error that when a `dynamic` + declaration does not explicitly state `@objc`, with a Fix-It to add + the `@objc`. + +* In Swift 3 compatibility mode, continue to infer `@objc` for + `dynamic` methods. However, introduce a warning that such code will + be ill-formed in Swift 4, along with a Fix-It to add the + `@objc`. + +* A Swift 3-to-4 migrator could employ the same logic as Swift 3 + compatibility mode to update `dynamic` declarations appropriately. + + + +The elimination of inference of `@objc` for declarations in `NSObject` +subclasses is more complicated. Considering again the three cases: + +* In Swift 4 mode, do not infer `@objc` for such + declarations. Source-breaking changes that will be introduced include: + + * If `#selector` or `#keyPath` refers to one such declaration, an + error will be produced on previously-valid code that the + declaration is not `@objc`. In most cases, a Fix-It will suggest + the addition of `@objc`. + + * The lack of `@objc` means that Objective-C code in mixed-source + projects won't be able to call these declarations. Most problems + caused by this will result in warnings or errors from the + Objective-C compiler (due to unrecognized selectors), but some + might only be detected at runtime. These latter cases will be + hard-to-detect. + + * Other tools and frameworks that rely on the presence of + Objective-C entrypoints but do not make use of Swift's + facilities for referring to them will fail. This case is + particularly hard to diagnose well, and failures of this sort + are likely to cause runtime failures that only the developer can + diagnose and correct. + +* In Swift 3 compatibility mode, continue to infer `@objc` for these + declarations. When `@objc` is inferred based on this rule, modify + the generated header (i.e., the header used by Objective-C code to + call into Swift code) so that the declaration contains a + "deprecated" attribute indicating that the Swift declaration should + be explicitly marked with `@objc`. For example: + + ```swift + class MyClass : NSObject { + func foo() { } + } + ``` + + will produce a generated header that includes: + + ```objc + @interface MyClass : NSObject + -(void)foo NS_DEPRECATED("MyClass.foo() requires an explicit `@objc` in Swift 4"); + @end + ``` + + This way, any reference to that declaration from Objective-C code + will produce a warning about the deprecation. Users can silence the + warning by adding an explicit `@objc`. + +* A Swift 3-to-4 migrator is the hardest part of the story. Ideally, + the migrator to only add `@objc` in places where it is needed, so + that we see some of the expected benefits of code-size + reduction. However, there are two problems with doing so: + + 1. Some of the uses that imply the need to add `@objc` come from + Objective-C code, so a Swift 3-to-4 migrator would also need to + compile the Objective-C code (possibly with a modified version of + the Objective-C compiler) and match up the "deprecated" warnings + mentioned in the Swift 3 compatibility mode bullet with Swift + declarations. + + 2. The migrator can't reason about dynamically-constructed selectors + or the behavior of other tools that might directly use the + Objective-C runtime, so failing to add a `@objc` will lead to + migrated programs that compile but fail to execute correctly. + +### Overriding of declarations introduced in class extensions + +Swift's class model doesn't support overriding of declarations +introduced in class extensions. For example, the following code +produces an amusing error message on the override: + +```swift +class MySuperclass { } + +extension MySuperclass { + func extMethod() { } +} + +class MySubclass : MySuperclass { } + +extension MySubclass { + override func extMethod() { } // error: declarations in extensions cannot override yet +} +``` + +However, this *does* work in Swift 3 when the method is `@objc`, e.g., + +```swift +class MySuperclass { } + +extension MySuperclass { + @objc func extMethod() { } +} + +class MySubclass : MySuperclass { } + +extension MySubclass { + override func extMethod() { } // okay! Objective-C message dispatch allows this +} +``` + +Removing `@objc` inference for `NSObject` subclasses will therefore +break this correct Swift 3 code: + +```swift +class MySuperclass { } + +extension MySuperclass : NSObject { + func extMethod() { } // implicitly @objc in Swift 3, not @objc in Swift 4 +} + +class MySubclass : MySuperclass { } + +extension MySubclass { + override func extMethod() { } // okay in Swift 3, error in Swift 4: declarations in extensions cannot override yet +} +``` + +There are several potential solutions to this problem, but both are +out-of-scope for this particular proposal: + +1. Require that a non-`@objc` declaration in a class extension by +explicitly declared `final` so that it is clear from the source that +this declaration cannot be overridden. + +2. Extend Swift's class model to permit overriding of declarations +introduced in extensions. + + +Additionally, a non-`final` `@objc` declaration in a class extension +is implicitly `dynamic`. This is a second-order effect of Swift's +class model not allowing declarations introduced in class extensions +to be overridable: such declarations must always be `@objc` and (more +importantly) be called via the Objective-C runtime (`objc_msgSend`), +so they are `dynamic` *in practice*. Moreover, as an implementation +convenience in the Swift compiler, Swift infers `dynamic`. If in fact +Swift gains the ability to override declarations introduced in a class +extension (without `@objc`), the inference of `dynamic` would no +longer be necessary. Removing that inference in such a future version +of Swift could break existing applications that swizzle those methods +via the Objective-C runtime. There are a few options here: + +1. Require that `@objc` declarations in a class extension be explicitly + stated to be either `final` or `dynamic`. Note that there is no + enforcement that these methods are not swizzled via the Objective-C + runtime, so this is merely a case of forcing the user to be explicit + about whether this method is overridable and to maintain the current + behavior if Swift gets the ability to add overridable declarations + in class extensions. + +2. Consider the inference of 'dynamic' to be an implementation detail, + not a semantic contract. Specifically, it means that we reserve the + right to break Swift applications that swizzle declarations not + explicitly marked `explicit` in some future version of Swift where + one can introduce non-`@objc` overridable declarations in class + extensions. It is plausible that the Objective-C runtime could be + extended to realize when it is being asked to swizzle an Objective-C + entry point for a non-`dynamic` Swift declaration, which would + provide a more graceful failure mode. + + +## Effect on ABI stability + +This proposal has no effect on the Swift ABI, because it only concerns the Objective-C entry points for Swift entities, which have always been governed by the already-set-in-stone Objective-C ABI. Whether a particular Swift entity is `@objc` or not does not affect its Swift ABI. + +## Effect on API resilience + +The [library evolution document](https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst) notes that adding or removing `@objc` is not a resilient API change. Therefore, changing the inference behavior of `@objc` doesn't really have an impact on API resilience beyond the normal concerns about errors of omission: prior to this proposal, forgetting to add `@nonobjc` meant that an API might be stuck vending an Objective-C entry point it didn't want to expose; with this proposal, forgetting to add `@objc` means that an API might fail to be usable from Objective-C. The latter problem, at least, can be addressed by exposing an additional entrypoint. Moreover, adding an Objective-C entrypoint is "less" ABI-breaking that removing an Objective-C entrypoint, because the former is only breaking for `open` or `dynamic` members. + +## Alternatives considered + +Aside from the obvious alternative of "do nothing", there are ways to +address some of the problems called out in the +[Motivation](#motivation) section without eliminating inference in the +cases we're talking about, or to soften the requirements on some +constructs. + +### Mangling Objective-C selectors +Some of the problems with Objective-C selector collisions could be +addressed by using "mangled" selector names for Swift-defined +declarations. For example, given: + +```swift +class MyClass : NSObject { + func print(_ value: Int) { } +} +``` + +Instead of choosing the Objective-C selector "print:" by default, +which is likely to conflict, we could use a mangled selector name like +`__MyModule__MyClass__print__Int:` that is unlikely to conflict with +anything else in the program. However, this change would also be +source-breaking for the same reasons that restricting `@objc` +inference is: dynamic behavior that constructs Objective-C selectors +or tools outside of Swift that expect certain selectors will break at +run-time. + +### Completely eliminating `@objc` inference + +Another alternative to this proposal is to go further and completely +eliminate `@objc` inference. This would simplify the programming model +further---it's exposed to Objective-C only if it's marked +`@objc`---but at the cost of significantly more boilerplate for +applications that use Objective-C frameworks. For example: + +```swift +class Sub : Super { + @objc override func foo() { } // @objc is now required +} + +class MyClass : MyDelegate { + @objc func bar() { } // @objc is now required +} +``` + +I believe that this proposal strikes the right balance already, where +`@objc` is inferred when it's needed to maintain the semantic model, +and can be explicitly added to document those places where the user is +intentionally exposing an Objective-C entrypoint for some +reason. Thus, explicitly writing `@objc` indicates intent without +creating boilerplate. + +# Acknowledgments + +Thanks to Brian King for noting the inference of `dynamic` and its +relationship to this proposal. From dd26e3924671088c61b498a3cd92205db5ac1c1c Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 21 Mar 2017 22:57:04 -0700 Subject: [PATCH 0233/4563] kick this off --- proposals/{NNNN-objc-inference.md => 0160-objc-inference.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{NNNN-objc-inference.md => 0160-objc-inference.md} (100%) diff --git a/proposals/NNNN-objc-inference.md b/proposals/0160-objc-inference.md similarity index 100% rename from proposals/NNNN-objc-inference.md rename to proposals/0160-objc-inference.md From c57be1e6d7284165ff0ea47908a9713f2e0171c5 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 21 Mar 2017 23:00:55 -0700 Subject: [PATCH 0234/4563] Fix a link --- proposals/0160-objc-inference.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0160-objc-inference.md b/proposals/0160-objc-inference.md index 2bb7bcb1ae..7f07ce4e13 100644 --- a/proposals/0160-objc-inference.md +++ b/proposals/0160-objc-inference.md @@ -1,9 +1,9 @@ # Limiting `@objc` inference -* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-objc-inference.md) -* Author(s): [Doug Gregor](https://github.com/DougGregor) -* Status: **Awaiting review** -* Review manager: TBD +* Proposal: [SE-0160](https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md) +* Author: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (March 21...28, 2017)** +* Review manager: [Chris Lattner](http://github.com/lattner) ## Introduction From a5190cc5bd868af86bff1f700b053dd989824b80 Mon Sep 17 00:00:00 2001 From: Alan Date: Wed, 22 Mar 2017 22:24:35 -0700 Subject: [PATCH 0235/4563] Example typo fix I am fairly certain the example meant to adhere C to the P protocol as well as inheriting from NSObject. --- proposals/0160-objc-inference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0160-objc-inference.md b/proposals/0160-objc-inference.md index 7f07ce4e13..72344dfdbd 100644 --- a/proposals/0160-objc-inference.md +++ b/proposals/0160-objc-inference.md @@ -204,7 +204,7 @@ extension P { func bar() { } } -class C : NSObject { } +class C : NSObject, P { } let c = C() print(c.respondsToSelector("bar")) // prints "false" From ecbc0ba378b9406fc576603532778b0a66c45a14 Mon Sep 17 00:00:00 2001 From: Alan Date: Wed, 22 Mar 2017 22:35:54 -0700 Subject: [PATCH 0236/4563] Fix for link typo Remove the space between `[API Design Guidelines]` and it's `(...)` link to fix markdown parsing --- proposals/0158-package-manager-manifest-api-redesign.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0158-package-manager-manifest-api-redesign.md b/proposals/0158-package-manager-manifest-api-redesign.md index d6fd6b8c07..d2103ef4d9 100644 --- a/proposals/0158-package-manager-manifest-api-redesign.md +++ b/proposals/0158-package-manager-manifest-api-redesign.md @@ -18,7 +18,7 @@ separate proposals. ## Motivation The `Package.swift` manifest APIs were designed prior to the [API Design -Guidelines] (https://swift.org/documentation/api-design-guidelines/), and their +Guidelines](https://swift.org/documentation/api-design-guidelines/), and their design was not reviewed by the evolution process. Additionally, there are several small areas which can be cleaned up to make the overall API more "Swifty". From 0389b1f49fc55b1a898701c549ce89738307b9fc Mon Sep 17 00:00:00 2001 From: Alan Date: Wed, 22 Mar 2017 22:50:30 -0700 Subject: [PATCH 0237/4563] Requested syntax update for respondsToSelector Use the current Swift syntax for `responds(to selector: Selector)` --- proposals/0160-objc-inference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0160-objc-inference.md b/proposals/0160-objc-inference.md index 72344dfdbd..cfa92dbbf2 100644 --- a/proposals/0160-objc-inference.md +++ b/proposals/0160-objc-inference.md @@ -207,7 +207,7 @@ extension P { class C : NSObject, P { } let c = C() -print(c.respondsToSelector("bar")) // prints "false" +print(c.responds(to: Selector("bar"))) // prints "false" ``` The expectation that `P.bar()` has an Objective-C entry point is set From 670b0d5d87b33b27e8b53647d2b371810132f65c Mon Sep 17 00:00:00 2001 From: Andrew Mackarous Date: Fri, 24 Mar 2017 15:00:59 -0400 Subject: [PATCH 0238/4563] Update 0068-universal-self.md Add markdown language syntax highlighting --- proposals/0068-universal-self.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0068-universal-self.md b/proposals/0068-universal-self.md index ca35591a25..b2ea6de7bc 100644 --- a/proposals/0068-universal-self.md +++ b/proposals/0068-universal-self.md @@ -24,7 +24,7 @@ a static member or passing types for unsafe bitcasts, among other uses. You can either specify a type by its full name or use `self.dynamicType` to access an instance's dynamic runtime type as a value. -``` +```swift struct MyStruct { static func staticMethod() { ... } func instanceMethod() { From 4f0b57e669372aa9c704cd70ade6fada8438d302 Mon Sep 17 00:00:00 2001 From: Kelly Thomas Kline Date: Sat, 25 Mar 2017 14:19:35 -0700 Subject: [PATCH 0239/4563] Fix grammar --- proposals/XXXX-multi-line-string-literals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/XXXX-multi-line-string-literals.md b/proposals/XXXX-multi-line-string-literals.md index 901342f292..172b235423 100644 --- a/proposals/XXXX-multi-line-string-literals.md +++ b/proposals/XXXX-multi-line-string-literals.md @@ -24,7 +24,7 @@ break lines using string concatenation. Concatenation is ungainly and may result ## Proposed solution After consideration this proposal puts forward a single simple syntax for inclusion: `"""long strings"""`. -This has the advantage that it is well supported by syntax the highlighters on github and existing editors +This has the advantage that it is well supported by the syntax highlighters on github and existing editors and is a relatively minor change to the Swift Lexer. Interpolation would work as before. ### Long strings From c8165b41b188c3d095425a0b4636fcf299ee9036 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Tue, 28 Mar 2017 16:18:08 -0700 Subject: [PATCH 0240/4563] Amend SE-0138: UnsafeRawBufferPointer - Normalize slice type. (#651) * Amend SE-0138: UnsafeRawBufferPointer - Normalize slice type. * Add UnsafeBufferPointer.init(rebasing:) for consistentcy with UnsafeRawBufferPointer. --- proposals/0138-unsaferawbufferpointer.md | 142 ++++++++++++++++++++++- 1 file changed, 137 insertions(+), 5 deletions(-) diff --git a/proposals/0138-unsaferawbufferpointer.md b/proposals/0138-unsaferawbufferpointer.md index 5a43758a1d..61b3219650 100644 --- a/proposals/0138-unsaferawbufferpointer.md +++ b/proposals/0138-unsaferawbufferpointer.md @@ -154,6 +154,57 @@ Add an `Array.withUnsafe[Mutable]Bytes(_)` method that passes an Add a `withUnsafeMutableBytes(of:_)` function that passes an `UnsafeRawBufferPointer` view of a value of type `T` to the closure body. +## Amendment to normalize the slice type + +The original version of this proposal defined +`Unsafe[Mutable]BufferPointer` to be its own `SubSequence` type. This +is the `Collection`'s slice type returned by a range subscript +getter. Using a single type for buffers and buffer slices is very +convenient for working with flat memory regions, but it is +inconsistent with Swift's `Collection` semantics. The problem is that +it transparently rebases the slice's `Int`-type indices to zero. This +has the potential to break generic algorithms, which expect to use the +same indices across both the original `Collection` and its slices. + +The amended version of this proposal changes `SubSequence` to `[Mutable]RandomAccessSlice` + +`rebasing` initializers have been added to allow explicit conversion +from a slice to a zero-based `Unsafe[Mutable]RawBufferPointer`. + +This results in the following behavioral changes: + +Passing a region within buffer to another function that takes a buffer +can no longer be done via subscript: + +Incorrect: `takesRawBuffer(buffer[i.. /// An iterator for the bytes referenced by `${Self}`. public struct Iterator : IteratorProtocol, Sequence { @@ -866,6 +918,44 @@ public struct Unsafe${Mutable}RawBufferPointer } % end # !mutable +% if not mutable: + /// Creates a raw buffer over the same memory as the given raw buffer slice. + /// + /// The new raw buffer will represent the same region of memory as the slice, + /// but it's indices will be rebased to zero. Given: + /// + /// let slice = buffer[n..) { + self.init(start: slice.base.baseAddress! + slice.startIndex, + count: slice.count) + } +% end # !mutable + + /// Creates a raw buffer over the same memory as the given raw buffer slice. + /// + /// The new raw buffer will represent the same region of memory as the slice, + /// but it's indices will be rebased to zero. Given: + /// + /// let slice = buffer[n.. + ) { + self.init(start: slice.base.baseAddress! + slice.startIndex, + count: slice.count) + } + /// Always zero, which is the index of the first byte in a /// non-empty buffer. public var startIndex: Int { @@ -905,13 +995,11 @@ public struct Unsafe${Mutable}RawBufferPointer /// Accesses the bytes in the memory region within `bounds` as a `UInt8` /// values. - public subscript(bounds: Range) -> Unsafe${Mutable}RawBufferPointer { + public subscript(bounds: Range) -> ${Mutable}RandomAccessSlice { get { _debugPrecondition(bounds.lowerBound >= startIndex) _debugPrecondition(bounds.upperBound <= endIndex) - return Unsafe${Mutable}RawBufferPointer( - start: baseAddress.map { $0 + bounds.lowerBound }, - count: bounds.count) + return ${Mutable}RandomAccessSlice(base: self, bounds: bounds) } % if mutable: nonmutating set { @@ -1051,6 +1139,50 @@ extension ${Self} { } } %end + +% for mutable in (True, False): +% Mutable = 'Mutable' if mutable else '' + +extension Unsafe${Mutable}BufferPointer { + +% if not Mutable: + /// Creates a buffer over the same memory as the given buffer slice. + /// + /// The new buffer will represent the same region of memory as the slice, + /// but it's indices will be rebased to zero. Given: + /// + /// let slice = buffer[n..>) { + self.init(start: slice.base.baseAddress! + slice.startIndex, + count: slice.count) + } +% end # !mutable + + /// Creates a buffer over the same memory as the given buffer slice. + /// + /// The new buffer will represent the same region of memory as the slice, + /// but it's indices will be rebased to zero. Given: + /// + /// let slice = buffer[n..> + ) { + self.init(start: slice.base.baseAddress! + slice.startIndex, + count: slice.count) + } +} +% end ``` ## Implementation status From 0c13e78fb1b431aacb8f6d07b7c810a1f1a40897 Mon Sep 17 00:00:00 2001 From: Aaron Brager Date: Tue, 28 Mar 2017 22:19:30 -0700 Subject: [PATCH 0241/4563] Fix code typo in Subclass Existentials proposal (#657) --- proposals/0156-subclass-existentials.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0156-subclass-existentials.md b/proposals/0156-subclass-existentials.md index 6d027453c7..899ba4cd87 100644 --- a/proposals/0156-subclass-existentials.md +++ b/proposals/0156-subclass-existentials.md @@ -186,7 +186,7 @@ class MyViewController { which allows calling the function with an invalid parameter: ```swift -let myViewController: MyViewController() +let myViewController = MyViewController() myViewController.setup(UIViewController()) ``` From 9fea71d5fea54ec8cea27c71bb9e3c872615e1b0 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Wed, 29 Mar 2017 08:59:57 -0700 Subject: [PATCH 0242/4563] fix typo in SE-0082 (#661) --- proposals/0082-swiftpm-package-edit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0082-swiftpm-package-edit.md b/proposals/0082-swiftpm-package-edit.md index 3d00972f7c..6cc40edb80 100644 --- a/proposals/0082-swiftpm-package-edit.md +++ b/proposals/0082-swiftpm-package-edit.md @@ -81,7 +81,7 @@ Our proposed solution is as follows: If a such an editable package is present in `Packages`, then `swift build` will always use the exact sources in this directory to build, regardless of - it's state, git repository status, tags, or the tag desired by dependency + its state, git repository status, tags, or the tag desired by dependency resolution. In other words, this will "just build" against the sources that are present. From f013c9738d5cb0f5dd7898902757dbee7fc755ec Mon Sep 17 00:00:00 2001 From: JP Simard Date: Wed, 29 Mar 2017 09:00:12 -0700 Subject: [PATCH 0243/4563] fix typo in SE-0152 (#660) --- proposals/0152-package-manager-tools-version.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0152-package-manager-tools-version.md b/proposals/0152-package-manager-tools-version.md index 6da53ccdc7..80e5d27a7e 100644 --- a/proposals/0152-package-manager-tools-version.md +++ b/proposals/0152-package-manager-tools-version.md @@ -420,7 +420,7 @@ commit a single file when making manifest changes. Package.swift manifest, instead of needing to look in a separate file. * Supporting the `.swift-version` standard would require supporting toolchain -names as a standing for a Swift tools version, which complicates this mechanism +names as a stand-in for a Swift tools version, which complicates this mechanism significantly. ### Specify the Swift tools version with a more "Swifty" syntax From c57441c7e88c1a39fa054e9297905bc273c8c6f3 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 29 Mar 2017 16:34:28 -0700 Subject: [PATCH 0244/4563] First draft of String proposal 1 --- proposals/0161-StringRevision1.md | 342 ++++++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 proposals/0161-StringRevision1.md diff --git a/proposals/0161-StringRevision1.md b/proposals/0161-StringRevision1.md new file mode 100644 index 0000000000..dcc72124e2 --- /dev/null +++ b/proposals/0161-StringRevision1.md @@ -0,0 +1,342 @@ +# String Revision: Collection Conformance, C Interop, Transcoding + +* Proposal: [SE-NNNN](NNNN-StringCollection.md) +* Authors: [Ben Cohen](https://github.com/airspeedswift), [Dave Abrahams](http://github.com/dabrahams/) +* Review Manager: TBD +* Status: **Awaiting review** + +## Introduction + +This proposal is to implement a subset of the changes from the [Swift 4 +String +Manifesto](https://github.com/apple/swift/blob/master/docs/StringManifesto.md). + +Specifically: + + * Make `String` conform to `BidirectionalCollection` + * Make `String` conform to `RangeReplaceableCollection` + * Create a `Substring` type for `String.SubSequence` + * Create a `Unicode` protocol to allow for generic operations over both types. + * Consolidate on a concise set of C interop methods. + * Revise the transoding infrastructure. + +Other existing aspects of `String` remain unchanged for the purposes of this +proposal. + +## Motivation + +This proposal follows up on a number of recommendations found in the manifesto: + +`Collection` conformance was dropped from `String` in Swift 2. After +reevaluation, the feeling is that the minor semantic discrepancies (mainly with +`RangeReplaceableCollection`) are outweighed by the significant benefits of +restoring these conformances. For more detail on the reasoning, see +[here](https://github.com/apple/swift/blob/master/docs/StringManifesto.md#string-should-be-a-collection-of-characters-again) + +While it is not a collection, the Swift 3 string does have slicing operations. +`String` is currently serving as its own subsequence, allowing substrings +to share storage with their "owner". This can lead to memory leaks when small substrings of larger +strings are stored long-term (see [here](https://github.com/apple/swift/blob/master/docs/StringManifesto.md#substrings) +for more detail on this problem). Introducing a separate type of `Substring` to +serve as `String.Subsequence` is recommended to resolve this issue, in a similar +fashion to `ArraySlice`. + +As noted in the manifesto, support for interoperation with nul-terminated C +strings in Swift 3 is scattered and incoherent, with 6 ways to transform a C +string into a `String` and four ways to do the inverse. These APIs should be +replaced with a simpler set of methods on `String`. + +## Proposed solution + +A new type, `Substring`, will be introduced. Similar to `ArraySlice` it will +be documented as only for short- to medium-term storage: + +> **Important** +> +> Long-term storage of `Substring` instances is discouraged. A substring holds a +> reference to the entire storage of a larger string, not just to the portion it +> presents, even after the original string’s lifetime ends. Long-term storage of +> a substring may therefore prolong the lifetime of elements that are no longer +> otherwise accessible, which can appear to be memory leakage. + +Aside from minor differences, such as having a `SubSequence` of `Self` and a +larger size to describe the range of the subsequence, `Substring` +will be near-identical from a user perspective. + +In order to be able to write extensions accross both `String` and `Substring`, +a new `Unicode` protocol to which the two types will conform will be +introduced. For the purposes of this proposal, `Unicode` will be defined as a +protocol to be used whenver you would previously extend `String`. It should be +possible to substitute `extension Unicode { ... }` in Swift 4 wherever +`extension String { ... }` was written in Swift 3, with one exception: any +passing of `self` into an API that takes a concrete `String` will need to be +rewritten as `String(self)`. If `Self` is a `String` then this should +effectively optimize to a no-op, whereas if `Self` is a `Substring` then this +will force a copy, helping to avoid the "memory leak" problems described above. + +The exact nature of the protocol – such as which methods should be protocol +requirements vs which can be implemented as protocol extensions, are considered +implementation details and so not covered in this proposal. + +`Unicode` will conform to `BidirectionalCollection`. +`RangeReplaceableCollection` conformance will be added directly onto the +`String` and `Substring` types, as it is possible future `Unicode`-conforming +types might not be range-replaceable (e.g. an immutable type that wraps +a `const char *`). + +The C string interop methods will be updated to those described +[here](https://github.com/apple/swift/blob/master/docs/StringManifesto.md#c-string-interop): +a single `withCString` operation and two `init(cString:)` constructors, one for +UTF8 and one for arbitrary encodings. The primary change is to remove +"non-repairing" variants of construction from nul-terminated C strings. In both +of the construction APIs, any invalid encoding sequence detected will have its +longest valid prefix replaced by `U+FFFD`, the Unicode replacement character, +per the Unicode specification. This covers the common case. The replacement is +done physically in the underlying storage and the validity of the result is +recorded in the String's encoding such that future accesses need not be slowed +down by possible error repair separately. Construction that is aborted when +encoding errors are detected can be accomplished using APIs on the encoding. + +The current transcoding support will be updated to improve usability and +performance. The primary changes will be: + + - to allow transcoding directly from one encoding to another without having + to triangulate through an intermediate scalar value + - to add the ability to transcode an input collection in reverse, allowing the + different views on `String` to be made bi-directional + - to have decoding take a collection rather than an iterator, and return an + index of its progress into the source, allowing that method to be static + +The standard library currently lacks a `Latin1` codec, so a +`enum Latin1: UnicodeEncoding` type will be added. + +## Detailed design + +The following additions will be made to the standard library: + +```swift +protocol Unicode: BidirectionalCollection { + // Implementation detail as described above +} + +extension String: Unicode, RangeReplaceableCollection { + typealias SubSequence = Substring +} + +struct Substring: Unicode, RangeReplaceableCollection { + typealias SubSequence = Substring + // near-identical API surface area to String +} +``` + +The subscript operations on `String` will be amended to return `Substring`: + +```swift +struct String { + subscript(bounds: Range) -> Substring { get } + subscript(bounds: ClosedRange) -> Substring { get } +} +``` + +Note that properties or methods that due to their nature create new `String` +storage (such as `lowercased()`) will _not_ change. + +C string interop will be consolidated on the following methods: + +```swift +extension String { + /// Constructs a `String` having the same contents as `nulTerminatedUTF8`. + /// + /// - Parameter nulTerminatedUTF8: a sequence of contiguous UTF-8 encoded + /// bytes ending just before the first zero byte (NUL character). + init(cString nulTerminatedUTF8: UnsafePointer) + + /// Constructs a `String` having the same contents as `nulTerminatedCodeUnits`. + /// + /// - Parameter nulTerminatedCodeUnits: a sequence of contiguous code units in + /// the given `encoding`, ending just before the first zero code unit. + /// - Parameter encoding: describes the encoding in which the code units + /// should be interpreted. + init( + cString nulTerminatedCodeUnits: UnsafePointer, + encoding: Encoding) + + /// Invokes the given closure on the contents of the string, represented as a + /// pointer to a null-terminated sequence of UTF-8 code units. + func withCString( + _ body: (UnsafePointer) throws -> Result) rethrows -> Result +} +``` + +Additionally, the current ability to pass a Swift `String` into C methods that +take a C string will remain as-is. + +A new protocol, `UnicodeEncoding`, will be added to replace the current +`UnicodeCodec` protocol: + +```swift +public enum UnicodeParseResult { +/// Indicates valid input was recognized. +/// +/// `resumptionPoint` is the end of the parsed region +case valid(T, resumptionPoint: Index) // FIXME: should these be reordered? +/// Indicates invalid input was recognized. +/// +/// `resumptionPoint` is the next position at which to continue parsing after +/// the invalid input is repaired. +case error(resumptionPoint: Index) + +/// Indicates that there was no more input to consume. +case emptyInput + + /// If any input was consumed, the point from which to continue parsing. + var resumptionPoint: Index? { + switch self { + case .valid(_,let r): return r + case .error(let r): return r + case .emptyInput: return nil + } + } +} + +/// An encoding for text with UnicodeScalar as a common currency type +public protocol UnicodeEncoding { + /// The maximum number of code units in an encoded unicode scalar value + static var maxLengthOfEncodedScalar: Int { get } + + /// A type that can represent a single UnicodeScalar as it is encoded in this + /// encoding. + associatedtype EncodedScalar : EncodedScalarProtocol + + /// Produces a scalar of this encoding if possible; returns `nil` otherwise. + static func encode( + _:Scalar) -> Self.EncodedScalar? + + /// Parse a single unicode scalar forward from `input`. + /// + /// - Parameter knownCount: a number of code units known to exist in `input`. + /// **Note:** passing a known compile-time constant is strongly advised, + /// even if it's zero. + static func parseScalarForward( + _ input: C, knownCount: Int /* = 0, via extension */ + ) -> ParseResult + where C.Iterator.Element == EncodedScalar.Iterator.Element + + /// Parse a single unicode scalar in reverse from `input`. + /// + /// - Parameter knownCount: a number of code units known to exist in `input`. + /// **Note:** passing a known compile-time constant is strongly advised, + /// even if it's zero. + static func parseScalarReverse( + _ input: C, knownCount: Int /* = 0 , via extension */ + ) -> ParseResult + where C.Iterator.Element == EncodedScalar.Iterator.Element +} + +/// Parsing multiple unicode scalar values +extension UnicodeEncoding { + @discardableResult + public static func parseForward( + _ input: C, + repairingIllFormedSequences makeRepairs: Bool = true, + into output: (EncodedScalar) throws->Void + ) rethrows -> (remainder: C.SubSequence, errorCount: Int) + + @discardableResult + public static func parseReverse( + _ input: C, + repairingIllFormedSequences makeRepairs: Bool = true, + into output: (EncodedScalar) throws->Void + ) rethrows -> (remainder: C.SubSequence, errorCount: Int) + where C.SubSequence : BidirectionalCollection, + C.SubSequence.SubSequence == C.SubSequence, + C.SubSequence.Iterator.Element == EncodedScalar.Iterator.Element +} +``` + + +`UnicodeCodec` will be updated to refine `UnicodeEncoding`, and all +existing codecs will conform to it. + +Note, depending on whether this change lands before or after some of the +generics features, generic `where` clauses may need to be added temporarily. + +## Source compatibility + +Adding collection conformance to `String` should not materially impact source +stability as it is purely additive: Swift 3's `String` interface currently +fulfills all of the requirements for a bidirectional range replaceable +collection. + +Altering `String`'s slicing operations to return a different type is source +breaking. The following mitigating steps are proposed: + + - Add a deprecated subscript operator that will run in Swift 3 compatibility + mode and which will return a `String` not a `Substring`. + + - Add deprecated versions of all current slicing methods to similarly return a + `String`. + +i.e.: + +```swift +extension String { + @available(swift, obsoleted: 4) + subscript(bounds: Range) -> String { + return String(characters[bounds]) + } + + @available(swift, obsoleted: 4) + subscript(bounds: ClosedRange) -> String { + return String(characters[bounds]) + } +} +``` + +In a review of 77 popular Swift projects found on GitHub, these changes +resolved any build issues in the 12 projects that assumed an explicit `String` +type returned from slicing operations. + +Due to the change in internal implementation, this means that these operations +will be _O(n)_ rather than _O(1)_. This is not expected to be a major concern, +based on experiences from a similar change made to Java, but projects will be +able to work around performance issues without upgrading to Swift 4 by +explicitly typing slices as `Substring`, which will call the Swift 4 variant, +and which will be available but not invoked by default in Swift 3 mode. + +The C string interoperability methods outside the ones described in the +detailed design will remain in Swift 3 mode, be deprecated in Swift 4 mode, and +be removed in a subsequent release. `UnicodeCodec` will be similarly deprecated. + +## Effect on ABI stability + +As a fundamental currency type for Swift, it is essential that the `String` +type (and its associated subsequence) is in a good long-term state before being +locked down when Swift declares ABI stability. Shrinking the size of `String` +to be 64 bits is an important part of this. + +## Effect on API resilience + +Decisions about the API resilience of the `String` type are still to be +determined, but are not adversely affected by this proposal. + +## Alternatives considered + +For a more in-depth discussion of some of the trade-offs in string design, see +the manifesto and associated [evolution thread](). + +This proposal does not yet introduce an implicit conversion from `Substring` to +`String`. The decision on whether to add this will be deferred pending feedback +on the initial implementation. The intention is to make a preview toolchain +available for feedback, including on whether this implicit conversion is +necessary, prior to the release of Swift 4. + +Several of the types related to `String`, such as the encodings, would ideally +reside inside a namespace rather than live at the top level of the standard +library. The best namespace for this is probably `Unicode`, but this is also +the name of the protocol. At some point if we gain the ability to nest enums +and types inside protocols, they should be moved there. Putting them inside +`String` or some other enum namespace is probably not worthwhile in the +mean-time. + + From 7cc9a7dc82b59e005ec49fe046b3ff72219476e0 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 29 Mar 2017 16:39:14 -0700 Subject: [PATCH 0245/4563] Update with evolution thread --- proposals/0161-StringRevision1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0161-StringRevision1.md b/proposals/0161-StringRevision1.md index dcc72124e2..1ac95dcd68 100644 --- a/proposals/0161-StringRevision1.md +++ b/proposals/0161-StringRevision1.md @@ -323,7 +323,7 @@ determined, but are not adversely affected by this proposal. ## Alternatives considered For a more in-depth discussion of some of the trade-offs in string design, see -the manifesto and associated [evolution thread](). +the manifesto and associated [evolution thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/thread.html#30497). This proposal does not yet introduce an implicit conversion from `Substring` to `String`. The decision on whether to add this will be deferred pending feedback From 3a822c799011ace682712532cfabfe32e9203fbb Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 29 Mar 2017 17:23:28 -0700 Subject: [PATCH 0246/4563] fix typo --- proposals/0161-StringRevision1.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0161-StringRevision1.md b/proposals/0161-StringRevision1.md index 1ac95dcd68..c514e316e1 100644 --- a/proposals/0161-StringRevision1.md +++ b/proposals/0161-StringRevision1.md @@ -1,6 +1,6 @@ # String Revision: Collection Conformance, C Interop, Transcoding -* Proposal: [SE-NNNN](NNNN-StringCollection.md) +* Proposal: [SE-0161](0161-StringRevision1.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Dave Abrahams](http://github.com/dabrahams/) * Review Manager: TBD * Status: **Awaiting review** @@ -18,7 +18,7 @@ Specifically: * Create a `Substring` type for `String.SubSequence` * Create a `Unicode` protocol to allow for generic operations over both types. * Consolidate on a concise set of C interop methods. - * Revise the transoding infrastructure. + * Revise the transcoding infrastructure. Other existing aspects of `String` remain unchanged for the purposes of this proposal. From 1e70602684b0eeae1bdbb781fa753f847b7c0b2b Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Thu, 30 Mar 2017 09:18:51 -0700 Subject: [PATCH 0247/4563] Proposal for Keypath (#644) * Proposal for Keypath * Update with pull request now that I have one * Fix path code example to reference firstFriendsNameKeyPath * Update based on swift-evolution feedback: - fix example so that it compiles - move to #keyPath syntax - add a parameter label for the subscripts - various prose tweaks to account for the above --- proposals/XXXX-key-paths.md | 202 ++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 proposals/XXXX-key-paths.md diff --git a/proposals/XXXX-key-paths.md b/proposals/XXXX-key-paths.md new file mode 100644 index 0000000000..85c0193be3 --- /dev/null +++ b/proposals/XXXX-key-paths.md @@ -0,0 +1,202 @@ +# Smart KeyPaths: Better Key-Value Coding for Swift + +* Proposal: SE-NNNN +* Authors: [David Smith](https://github.com/Catfish-Man), [Michael LeHew](https://github.com/mlehew), [Joe Groff](https://github.com/jckarter) +* Review Manager: TBD +* Status: **Awaiting Review** +* Associated PRs: + * [#644](https://github.com/apple/swift-evolution/pull/644) + +## Introduction +We propose a family of concrete _Key Path_ types that represent uninvoked references to properties that can be composed to form paths through many values and directly get/set their underlying values. + +## Motivation +#### We Can Do Better than String +On Darwin platforms Swift's existing `#keyPath()` syntax provides a convenient way to safely *refer* to properties. Unfortunately, once validated, the expression becomes a `String` which has a number of important limitations: + +* Loss of type information (requiring awkward `Any` APIs) +* Unnecessarily slow to parse +* Only applicable to `NSObjects` +* Limited to Darwin platforms + +#### Use/Mention Distinctions +While methods can be referred to without invoking them (`let x = foo.bar` instead of `let x = foo.bar()`), this is not currently possible for properties and subscripts. + +Making indirect references to a properties' concrete types also lets us expose metadata about the property, and in the future additional behaviors. + +#### More Expressive KeyPaths +We would also like to support being able to use _Key Paths_ to access into collections, which is not currently possible. + +## Proposed solution +We propose expanding the capabilities of the `#keyPath` directive to produce `KeyPath` objects instead of `Strings`. `KeyPaths` are a family of generic classes _(structs and protocols here would be ideal, but requires generalized existentials)_ which encapsulate a property reference or chain of property references, including the type, mutability, property name(s), and ability to set/get values. + +Here's a sample of it in use: + +```swift +class Person { + var name: String + var friends: [Person] = [] + var bestFriend: Person? = nil + init(name: String) { + self.name = name + } +} + +var han = Person(name: "Han Solo") +var luke = Person(name: "Luke Skywalker") +luke.friends.append(han) + +// create a key path and use it +let firstFriendsNameKeyPath = #keyPath(Person, .friends[0].name) +let firstFriend = luke[keyPath: firstFriendsNameKeyPath] // "Han Solo" + +// or equivalently, with type inferred from context +luke[keyPath: #keyPath(.friends[0].name)] // "Han Solo" + +// rename Luke's first friend +luke[keyPath: firstFriendsNameKeyPath] = "A Disreputable Smuggler" + +// optional properties work too +let bestFriendsNameKeyPath = #keyPath(Person, .bestFriend?.name) +let bestFriendsName = luke[keyPath: bestFriendsNameKeyPath] // nil, if he is the last Jedi +``` + +## Detailed design +### Core KeyPath Types +`KeyPaths` are a hierarchy of progressively more specific classes, based on whether we have prior knowledge of the path through the object graph we wish to traverse. + +##### Unknown Path / Unknown Root Type +`AnyKeyPath` is fully type-erased, referring to 'any route' through an object/value graph for 'any root'. Because of type-erasure many operations can fail at runtime and are thus optional. + +```swift +class AnyKeyPath: CustomDebugStringConvertible, Hashable { + // MARK - Composition + // Returns nil if path.rootType != self.valueType + func appending(path: AnyKeyPath) -> AnyKeyPath? + + // MARK - Runtime Information + class var rootType: Any.Type + class var valueType: Any.Type + + static func == (lhs: AnyKeyPath, rhs: AnyKeyPath) -> Bool + var hashValue: Int +} +``` +##### Unknown Path / Known Root Type +If we know a little more type information (what kind of thing the key path is relative to), then we can use `PartialKeyPath`, which refers to an 'any route' from a known root: + +```swift +class PartialKeyPath: AnyKeyPath { + // MARK - Composition + // Returns nil if Value != self.valueType + func appending(path: AnyKeyPath) -> PartialKeyPath? + func appending(path: KeyPath) -> KeyPath? + func appending(path: ReferenceKeyPath) -> ReferenceKeyPath? +} +``` + +##### Known Path / Known Root Type +When we know both what the path is relative to and what it refers to, we can use `KeyPath`. Thanks to the knowledge of the `Root` and `Value` types, all of the failable operations lose their `Optional`. + +```swift +public class KeyPath: PartialKeyPath { + // MARK - Composition + func appending(path: KeyPath) -> KeyPath + func appending(path: WritableKeyPath) -> Self + func appending(path: ReferenceWritableKeyPath) -> ReferenceWritableKeyPath +} +``` + +##### Value/Reference Mutation Semantics Mutation +Finally, we have a pair of subclasses encapsulating value/reference mutation semantics. These have to be distinct because mutating a copy of a value is not very useful, so we need to mutate an inout value. + +```swift +class WritableKeyPath: KeyPath { + // MARK - Composition + func appending(path: WritableKeyPath) -> WritableKeyPath +} + +class ReferenceWritableKeyPath: WritableKeyPath { + override func appending(path: WritableKeyPath) -> ReferenceWritableKeyPath +} +``` + +### Access and Mutation Through KeyPaths +To get or set values for a given root and key path we effectively add the following subscripts to all Swift types. + +```swift +extension Any { + subscript(keyPath path: AnyKeyPath) -> Any? { get } + subscript(keyPath path: PartialKeyPath) -> Any { get } + subscript(keyPath path: KeyPath) -> Value { get } + subscript(keyPath path: WritableKeyPath) -> Value { set, get } +} +``` + +This allows for code like + +```swift +let someKeyPath = ... +person[keyPath: someKeyPath] +``` + +which is both appealingly readable, and doesn't require read-modify-write copies (subscripts access `self` inout). Conflicts with existing subscripts are avoided by using a named parameter and generics to only accept key paths with a `Root` of the type in question. + +### Referencing Key Paths +Forming a `KeyPath` borrows from the same syntax added in Swift 3 to confirm the existence of a given key path, only now producing concrete values instead of Strings. Optionals are handled via optional-chaining. Multiply dotted expressions are allowed as well, and work just as if they were composed via the `appending` methods on `KeyPath`. + + +There is no change or interaction with the #keyPath() syntax introduced in Swift 3. `#keyPath(Person.bestFriend.name)` will still produce a String, whereas `#keyPath(Person, .bestFriend.name)` will produce a `KeyPath`. + +### Performance +The performance of interacting with a property via `KeyPaths` should be close to the cost of calling the property directly. + +## Source compatibility +This change is additive and there should no affect on existing source. + +## Effect on ABI stability +This feature adds the following requirements to ABI stability: + +- mechanism to access key paths of public properties + +We think a protocol-based design would be preferable once the language has sufficient support for generalized existentials to make that ergonomic. By keeping the class hierarchy closed and the concrete implementations private to the implementation it should be tractable to provide compatibility with an open protocol-based design in the future. + +## Effect on API resilience +This should not significantly impact API resilience, as it merely provides a new mechanism for operating on existing APIs. + +## Alternatives considered + +#### More Features +Various drafts of this proposal have included additional features (decomposable key paths, prefix comparisons, support for custom `KeyPath` subclasses, creating a `KeyPath` from a `String` at runtime, `KeyPaths` conforming to `Codable`, bound key paths as a concrete type, etc.). We anticipate approaching these enhancements additively once the core `KeyPath` functionality is in place. + +#### Spelling +We also explored many different spellings, each with different strengths. We have chosen the current syntax for the clarity and discoverability it provides in practice. + +| `#keyPath` | Function Type Reference | Lisp-style | +| --- | --- | --- | +| `#keyPath(Person, .friends[0].name)` | `Person.friends[0].name` | \``Person.friend.name` | +| `#keyPath(luke, .friends[0].name)` |`luke[.friends[0].name]` | `luke`\``.friends[0].name` | +| `#keyPath(luke.friends[0], .name)`| `luke.friends[0][.name]` | `luke.friends[0]`\``.name` | + +While the crispness of the function-type-reference is appealing, it becomes ambigious when working with type properties. The spelling of the escape-sigil of the Lisp-style remains a barrier to adoption, but could be considered in the future should `#keyPath` prove a burden. + +We think most of the situations where `#keyPath` could be overly taxing likely wont show up outside of demonstrative examples: + +```swift +// you would likely never type this: +#keyPath(Person, .friends).appending(#keyPath(Array, [0])) + +// since you can just type this: +#keyPath(Person, .friends[0]) + +// .appending is more likely used in situations like this: +let somePath : PartialKeyPath<[Person]> = ... +#keyPath(Person, .friends).appending(somePath) + +// similarly, you'd never type this: +person[keyPath: #keyPath(Person, .friends[0])) + +// since that is just a roundabout way of saying: +person.friends[0] +``` + From 89a7e0b648fedc29cf61e7ad063ffef275fdf702 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 30 Mar 2017 09:21:19 -0700 Subject: [PATCH 0248/4563] Initiate review of SE-0161: Smart KeyPaths: Better Key-Value Coding for Swift --- proposals/{XXXX-key-paths.md => 0161-key-paths.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{XXXX-key-paths.md => 0161-key-paths.md} (98%) diff --git a/proposals/XXXX-key-paths.md b/proposals/0161-key-paths.md similarity index 98% rename from proposals/XXXX-key-paths.md rename to proposals/0161-key-paths.md index 85c0193be3..aa136ac0aa 100644 --- a/proposals/XXXX-key-paths.md +++ b/proposals/0161-key-paths.md @@ -1,9 +1,9 @@ # Smart KeyPaths: Better Key-Value Coding for Swift -* Proposal: SE-NNNN +* Proposal: [SE-0161](0161-key-paths.md) * Authors: [David Smith](https://github.com/Catfish-Man), [Michael LeHew](https://github.com/mlehew), [Joe Groff](https://github.com/jckarter) -* Review Manager: TBD -* Status: **Awaiting Review** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (March 30...April 5, 2017)** * Associated PRs: * [#644](https://github.com/apple/swift-evolution/pull/644) From fd1f2ddea7c2c5e66a995911179ce924f2916b2d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 30 Mar 2017 16:50:44 -0700 Subject: [PATCH 0249/4563] [SE-0160] Clarify the migration story (#663) --- proposals/0160-objc-inference.md | 284 +++++++++++++++++-------------- 1 file changed, 158 insertions(+), 126 deletions(-) diff --git a/proposals/0160-objc-inference.md b/proposals/0160-objc-inference.md index cfa92dbbf2..8018bc698c 100644 --- a/proposals/0160-objc-inference.md +++ b/proposals/0160-objc-inference.md @@ -4,6 +4,7 @@ * Author: [Doug Gregor](https://github.com/DougGregor) * Status: **Active review (March 21...28, 2017)** * Review manager: [Chris Lattner](http://github.com/lattner) +* Implementation: [Pull request](https://github.com/apple/swift/pull/8379) ## Introduction @@ -228,9 +229,10 @@ The two changes that remove inference of `@objc` are both source-breaking in different ways. The `dynamic` change mostly straightforward: -* In Swift 4 mode, introduce an error that when a `dynamic` - declaration does not explicitly state `@objc`, with a Fix-It to add - the `@objc`. +* In Swift 4 mode, introduce an error when a `dynamic` declaration + does not explicitly state `@objc` (or infer it based on one of the + `@objc` inference rules that still applies in Swift 4), with a + Fix-It to add the `@objc`. * In Swift 3 compatibility mode, continue to infer `@objc` for `dynamic` methods. However, introduce a warning that such code will @@ -240,170 +242,200 @@ straightforward: * A Swift 3-to-4 migrator could employ the same logic as Swift 3 compatibility mode to update `dynamic` declarations appropriately. - - The elimination of inference of `@objc` for declarations in `NSObject` subclasses is more complicated. Considering again the three cases: -* In Swift 4 mode, do not infer `@objc` for such - declarations. Source-breaking changes that will be introduced include: +* In Swift 4 mode, do not infer `@objc` for such declarations. + Source-breaking changes that will be introduced include: * If `#selector` or `#keyPath` refers to one such declaration, an error will be produced on previously-valid code that the declaration is not `@objc`. In most cases, a Fix-It will suggest the addition of `@objc`. - * The lack of `@objc` means that Objective-C code in mixed-source - projects won't be able to call these declarations. Most problems - caused by this will result in warnings or errors from the - Objective-C compiler (due to unrecognized selectors), but some - might only be detected at runtime. These latter cases will be - hard-to-detect. + * If a message is sent to one of these declarations via + `AnyObject`, the compiler may produce an error (if no `@objc` + entity by that name exists anywhere) or a failure might occur at + runtime (if another, unrelated `@objc` entity exists with that + same name). For example: + + ```swift + class MyClass : NSObject { + func foo() { } + func bar() { } + } + + class UnrelatedClass : NSObject { + @objc func bar() { } + } + + func test(object: AnyObject) { + object.foo?() // Swift 3: can call method MyClass.foo() + // Swift 4: compiler error, no @objc method "foo()" + object.bar?() // Swift 3: can call MyClass.bar() or UnrelatedClass.bar() + // Swift 4: can only call UnrelatedClass.bar() + } + ``` + + * If one of these declarations is written in a class extension and + is overridden, the override will produce an error in Swift 4 + because Swift's class model does not support overriding + declarations introduced in class extensions. For example: + + ```swift + class MySuperclass : NSObject { } + + extension MySuperclass { + func extMethod() { } // implicitly @objc in Swift 3, not in Swift 4 + } + + class MySubclass : MySuperclass { + override func extMethod() { } // Swift 3: okay + // Swift 4: error "declarations in extensions cannot override yet" + } + ``` + + * Objective-C code in mixed-source projects won't be able to call + these declarations. Most problems caused by this will result in + warnings or errors from the Objective-C compiler (due to + unrecognized selectors); some may only be detected at runtime, + similarly to the `AnyObject` case described above. * Other tools and frameworks that rely on the presence of - Objective-C entrypoints but do not make use of Swift's - facilities for referring to them will fail. This case is - particularly hard to diagnose well, and failures of this sort - are likely to cause runtime failures that only the developer can - diagnose and correct. + Objective-C entrypoints (e.g., via strings) but do not make use + of Swift's facilities for referring to them will fail. This case + is particularly hard to diagnose well, and failures of this sort + are likely to cause runtime failures (e.g., unrecoignized + selectors) that only the developer can diagnose and correct. * In Swift 3 compatibility mode, continue to infer `@objc` for these - declarations. When `@objc` is inferred based on this rule, modify - the generated header (i.e., the header used by Objective-C code to - call into Swift code) so that the declaration contains a - "deprecated" attribute indicating that the Swift declaration should - be explicitly marked with `@objc`. For example: + declarations. We can warn about uses of the `@objc` entrypoints in + cases where the `@objc` is inferred in Swift 3 but will not be in + Swift 4. - ```swift - class MyClass : NSObject { - func foo() { } - } - ``` +* A Swift 3-to-4 migrator is the hardest part of the story. The + migrator should have a switch: a "conservative" option and a + "minimal" option. - will produce a generated header that includes: + * The "conservative" option (which is the best default) simply adds + explicit `@objc` annotations to every entity that was implicitly + `@objc` in Swift 3 but would not implicitly be `@objc` in Swift + 4. Migrated projects won't get the benefits of the more-limited + `@objc` inference, but they will work out-of-the-box. - ```objc - @interface MyClass : NSObject - -(void)foo NS_DEPRECATED("MyClass.foo() requires an explicit `@objc` in Swift 4"); - @end - ``` + * The "minimal" option attempts to only add `@objc` in places where + it is needed to maintain the semantics of the program. It would be + driven by the diagnostics mentioned above (for `#selector`, + `#keyPath`, `AnyObject` messaging, and overrides), but some manual + intervention will be involved to catch the runtime cases. More + discussion of the migration workflow follows. - This way, any reference to that declaration from Objective-C code - will produce a warning about the deprecation. Users can silence the - warning by adding an explicit `@objc`. -* A Swift 3-to-4 migrator is the hardest part of the story. Ideally, - the migrator to only add `@objc` in places where it is needed, so - that we see some of the expected benefits of code-size - reduction. However, there are two problems with doing so: +## "Minimal" migration workflow - 1. Some of the uses that imply the need to add `@objc` come from - Objective-C code, so a Swift 3-to-4 migrator would also need to - compile the Objective-C code (possibly with a modified version of - the Objective-C compiler) and match up the "deprecated" warnings - mentioned in the Swift 3 compatibility mode bullet with Swift - declarations. +To migrate a Swift 3 project to Swift 4 without introducing spurious +Objective-C entry points, we can apply the following workflow: - 2. The migrator can't reason about dynamically-constructed selectors - or the behavior of other tools that might directly use the - Objective-C runtime, so failing to add a `@objc` will lead to - migrated programs that compile but fail to execute correctly. +1. In Swift 4 mode, address all of the warnings about uses of +declarations for which `@objc` was inferred based on the deprecated +rule. +2. Set the environment variable `SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT` +to a value between 1 and 3 (see below) and test the application. Clean +up any "deprecated `@objc` entrypoint` warnings. +3. Migrate to Swift 4 with "minimal" migration, which at this point +will only add `@objc` to explicitly `dynamic` declarations. -### Overriding of declarations introduced in class extensions +The following subsections describe this migration in more detail. -Swift's class model doesn't support overriding of declarations -introduced in class extensions. For example, the following code -produces an amusing error message on the override: +### Step 1: Address compiler warnings -```swift -class MySuperclass { } +The compiler can warn about most instances of the source-breaking +changes outlined above. Here is an example that demonstrates the +warnings in Swift code, all of which are generated by the Swift +compiler: -extension MySuperclass { - func extMethod() { } -} +```swift +class MyClass : NSObject { + func foo() { } -class MySubclass : MySuperclass { } + var property: NSObject? = nil -extension MySubclass { - override func extMethod() { } // error: declarations in extensions cannot override yet + func baz() { } } -``` -However, this *does* work in Swift 3 when the method is `@objc`, e.g., - -```swift -class MySuperclass { } - -extension MySuperclass { - @objc func extMethod() { } +extension MyClass { + func bar() { } } -class MySubclass : MySuperclass { } +class MySubClass : MyClass { + override func foo() { } // okay -extension MySubclass { - override func extMethod() { } // okay! Objective-C message dispatch allows this + override func bar() { } // warning: override of instance method + // 'bar()' from extension of 'MyClass' depends on deprecated inference + // of '@objc' } -``` -Removing `@objc` inference for `NSObject` subclasses will therefore -break this correct Swift 3 code: +func test(object: AnyObject, mine: MyClass) { + _ = #selector(MyClass.foo) // warning: argument of `#selector` + // refers to instance method `foo()` in `MyClass` that uses deprecated + // `@objc` inference -```swift -class MySuperclass { } + _ = #keyPath(MyClass.property) // warning: argument of '#keyPath' + // refers to property 'property' in 'MyClass' that uses deprecated + // `@objc` inference -extension MySuperclass : NSObject { - func extMethod() { } // implicitly @objc in Swift 3, not @objc in Swift 4 + _ = object.baz?() // warning: reference to instance + // method 'baz()' of 'MyClass' that uses deprecated `@objc` inference } +``` -class MySubclass : MySuperclass { } +For mixed-source projects, the Swift compiler will annotate the +generated Swift header with "deprecation" attributes, so that any +references to those declarations *from Objective-C code* will also +produce warnings. For example: -extension MySubclass { - override func extMethod() { } // okay in Swift 3, error in Swift 4: declarations in extensions cannot override yet +```objective-c +#import "MyApp-Swift.h" + +void test(MyClass *mine) { + [mine foo]; // warning: -[MyApp.MyClass foo] uses deprecated + // '@objc' inference; add '@objc' to provide an Objective-C entrypoint } ``` -There are several potential solutions to this problem, but both are -out-of-scope for this particular proposal: - -1. Require that a non-`@objc` declaration in a class extension by -explicitly declared `final` so that it is clear from the source that -this declaration cannot be overridden. - -2. Extend Swift's class model to permit overriding of declarations -introduced in extensions. - - -Additionally, a non-`final` `@objc` declaration in a class extension -is implicitly `dynamic`. This is a second-order effect of Swift's -class model not allowing declarations introduced in class extensions -to be overridable: such declarations must always be `@objc` and (more -importantly) be called via the Objective-C runtime (`objc_msgSend`), -so they are `dynamic` *in practice*. Moreover, as an implementation -convenience in the Swift compiler, Swift infers `dynamic`. If in fact -Swift gains the ability to override declarations introduced in a class -extension (without `@objc`), the inference of `dynamic` would no -longer be necessary. Removing that inference in such a future version -of Swift could break existing applications that swizzle those methods -via the Objective-C runtime. There are a few options here: - -1. Require that `@objc` declarations in a class extension be explicitly - stated to be either `final` or `dynamic`. Note that there is no - enforcement that these methods are not swizzled via the Objective-C - runtime, so this is merely a case of forcing the user to be explicit - about whether this method is overridable and to maintain the current - behavior if Swift gets the ability to add overridable declarations - in class extensions. - -2. Consider the inference of 'dynamic' to be an implementation detail, - not a semantic contract. Specifically, it means that we reserve the - right to break Swift applications that swizzle declarations not - explicitly marked `explicit` in some future version of Swift where - one can introduce non-`@objc` overridable declarations in class - extensions. It is plausible that the Objective-C runtime could be - extended to realize when it is being asked to swizzle an Objective-C - entry point for a non-`dynamic` Swift declaration, which would - provide a more graceful failure mode. - +### Step 2: Address (opt-in) runtime warnings + +Swift 3 compatibility mode augments each of the Objective-C +entrypoints introduced based on the deprecated `@objc` inference rules +with a call to a new runtime function +`swift_objc_swift3ImplicitObjCEntrypoint`. This entry point can be +used in two ways to find cases where an Objective-C entry point that +will be eliminated by the migration to Swift 4: + +* In a debugger, one can set a breakpoint on +`swift_objc_swift3ImplicitObjCEntrypoint` to catch specific cases +where the Objective-C entry point is getting called. + +* One can set the environment variable +`SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT` to one of three different +values to cause the Swift runtime to log uses of these Objective-C +entry points: + 1. Log calls to these entry points with a message such as: + ```***Swift runtime: entrypoint -[MyApp.MyClass foo] generated by implicit @objc inference is deprecated and will be removed in Swift 4``` + 2. Log (as in #1) and emit a backtrace showing how that Objective-C + entry point was invoked. + 3. Log with a backtrace (as in #2), then crash. This last stage is + useful for automated testing leading up to the migration to Swift 4. + +Testing with logging enabled should uncover uses of the Objective-C +entry points that use the deprecated rules. As explicit `@objc` is +added to each case, the runtime warnings will go away. + +### Step 3: Migrate to Swift 4 + +At this point, one can migrate to Swift 4. Building in Swift 4 will +remove the Objective-C entry points for any remaining case where +`@objc` was inferred based on the deprecated rules. ## Effect on ABI stability From fc14e365e91b4bdaa253f85cc4a32deb1c0093a8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 Mar 2017 08:25:17 -0700 Subject: [PATCH 0250/4563] Revise SE-0160 per core team feedback (#665) --- proposals/0160-objc-inference.md | 110 ++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/proposals/0160-objc-inference.md b/proposals/0160-objc-inference.md index 8018bc698c..ff624075d5 100644 --- a/proposals/0160-objc-inference.md +++ b/proposals/0160-objc-inference.md @@ -2,7 +2,9 @@ * Proposal: [SE-0160](https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md) * Author: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (March 21...28, 2017)** +* Status: **Returned for Revision** ([Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170327/034730.html)) +* Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/0389b1f49fc55b1a898701c549ce89738307b9fc/proposals/0160-objc-inference.md) + * Review manager: [Chris Lattner](http://github.com/lattner) * Implementation: [Pull request](https://github.com/apple/swift/pull/8379) @@ -71,7 +73,9 @@ these thunks). The proposed solution is to limit the inference of `@objc` to only those places where it is required for semantic consistency of the -programming model. +programming model. Then, add some class-level and extension-level +annotations to reduce boilerplate for cases where one wants to +enable/disable `@objc` inference more widely. ### Constructs that (still) infer `@objc` @@ -128,6 +132,19 @@ The list above describes cases where Swift 3 already performs inference of `@objc` and will continue to do so if this proposal is accepted. +### Additional constructs that will infer `@objc` + +These are new cases that *should* infer `@objc`, but currently don't +in Swift. `@objc` should be inferred when: + +* The declaration has the `@GKInspectable` attribute. This inference + is required because the interaction with GameplayKit occurs entirely + through the Objective-C runtime. + +* The declaration has the `@IBInspectable` attribute. This inference + is required because the interaction with Interface Builder occurs entirely + through the Objective-C runtime. + ### `dynamic` no longer infers `@objc` A declaration that is `dynamic` will no longer infer `@objc`. For example: @@ -193,6 +210,89 @@ required by the semantic model to expose an Objective-C entrypoint, so they don't infer `@objc`: there is no need to reason about the type of the parameter's suitability in Objective-C. +### Re-enabling `@objc` inference within a class hierarchy + +Some libraries and systems still depend greatly on the Objective-C +runtime's introspection facilities. For example, XCTest uses +Objective-C runtime metadata to find the test cases in `XCTestCase` +subclasses. To support such systems, introduce a new attribute for +classes in Swift, spelled `@objcMembers`, that re-enables `@objc` +inference for the class, its extensions, its subclasses, and (by +extension) all of their extensions. For example: + +```swift +@objcMembers +class MyClass : NSObject { + func foo() { } // implicitly @objc + + func bar() -> (Int, Int) // not @objc, because tuple returns + // aren't representable in Objective-C +} + +extension MyClass { + func baz() { } // implicitly @objc +} + +class MySubClass : MyClass { + func wibble() { } // implicitly @objc +} + +extension MySubClass { + func wobble() { } // implicitly @objc +} +``` + +This will be paired with an Objective-C attribute, spelled +`swift_objc_members`, that allows imported Objective-C classes to be +imported as `@objcMembers`: + +```objective-c +__attribute__((swift_objc_members)) +@interface XCTestCase : XCTest +/* ... */ +@end +``` + +will be imported into Swift as: + +```swift +@objcMembers +class XCTestCase : XCTest { /* ... */ } +``` + +### Enabling/disabling `@objc` inference within an extension + +There might be certain regions of code for which all of (or none of) +the entry points should be exposed to Objective-C. Allow either +`@objc` or `@nonobjc` to be specified on an `extension`. The `@objc` +or `@nonobjc` will apply to any member of that extension that does not +have its own `@objc` or `@nonobjc` annotation. For example: + +```swift +class SwiftClass { } + +@objc extension SwiftClass { + func foo() { } // implicitly @objc + func bar() -> (Int, Int) // error: tuple type (Int, Int) not + // expressible in @objc. add @nonobjc or move this method to fix the issue +} + +@objcMembers +class MyClass : NSObject { + func wibble() { } // implicitly @objc +} + +@nonobjc extension MyClass { + func wobble() { } // not @objc, despite @objcMembers +} +``` + +Note that `@objc` on an extension provides less-surprising behavior +than the implicit `@objc` inference of Swift 3, because it indicates +the intent to expose *everything* in that extension to Objective-C. If +some member within that extension cannot be exposed to Objective-C, +such as `SwiftClass.bar()`, the compiler will produce an error. + ## Side benefit: more reasonable expectations for `@objc` protocol extensions Users are often surprised to realize that extensions of `@objc` @@ -502,3 +602,9 @@ creating boilerplate. Thanks to Brian King for noting the inference of `dynamic` and its relationship to this proposal. + +# Revision history + +[Version 1](https://github.com/apple/swift-evolution/blob/0389b1f49fc55b1a898701c549ce89738307b9fc/proposals/0160-objc-inference.md) +of this proposal did not include the use of `@objcMembers` on classes +or the use of `@objc`/`@nonobjc` on extensions to mass-annotate. \ No newline at end of file From 428d6868d77dd4643a7464a02e3c41a049fa157a Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 31 Mar 2017 08:29:09 -0700 Subject: [PATCH 0251/4563] run 0160 --- proposals/0160-objc-inference.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0160-objc-inference.md b/proposals/0160-objc-inference.md index ff624075d5..b80ad96e30 100644 --- a/proposals/0160-objc-inference.md +++ b/proposals/0160-objc-inference.md @@ -2,7 +2,7 @@ * Proposal: [SE-0160](https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md) * Author: [Doug Gregor](https://github.com/DougGregor) -* Status: **Returned for Revision** ([Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170327/034730.html)) +* Status: **Active Review March 31...April 2** * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/0389b1f49fc55b1a898701c549ce89738307b9fc/proposals/0160-objc-inference.md) * Review manager: [Chris Lattner](http://github.com/lattner) @@ -607,4 +607,4 @@ relationship to this proposal. [Version 1](https://github.com/apple/swift-evolution/blob/0389b1f49fc55b1a898701c549ce89738307b9fc/proposals/0160-objc-inference.md) of this proposal did not include the use of `@objcMembers` on classes -or the use of `@objc`/`@nonobjc` on extensions to mass-annotate. \ No newline at end of file +or the use of `@objc`/`@nonobjc` on extensions to mass-annotate. From 09308d4fbfd2e389525186897d08ead900695712 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 31 Mar 2017 13:24:39 -0400 Subject: [PATCH 0252/4563] Put SE-0155 back under active review. --- proposals/0155-normalize-enum-case-representation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0155-normalize-enum-case-representation.md b/proposals/0155-normalize-enum-case-representation.md index 4d547ce435..f544390d5d 100644 --- a/proposals/0155-normalize-enum-case-representation.md +++ b/proposals/0155-normalize-enum-case-representation.md @@ -3,7 +3,7 @@ * Proposal: [SE-0155][] * Authors: [Daniel Duan][], [Joe Groff][] * Review Manager: [John McCall][] -* Status: **Awaiting review** +* Status: **Active review (March 31...April 10, 2017)** * Previous Revision: [1][Revision 1] ## Introduction From 94fd6819c5bb20ccc3a6180f93e56062ee9d6d83 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 31 Mar 2017 22:52:05 +0100 Subject: [PATCH 0253/4563] [SE-0148] Revert to Accepted Pending implementation of default arguments. --- proposals/0148-generic-subscripts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0148-generic-subscripts.md b/proposals/0148-generic-subscripts.md index dfab2598ef..bf19069c8a 100644 --- a/proposals/0148-generic-subscripts.md +++ b/proposals/0148-generic-subscripts.md @@ -3,7 +3,7 @@ * Proposal: [SE-0148](0148-generic-subscripts.md) * Author: [Chris Eidhof](https://github.com/chriseidhof) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Implemented (Swift 4)** +* Status: **Accepted** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170123/031048.html) * Bug: [SR-115](https://bugs.swift.org/browse/SR-115) From 290dd894abcbb9d60ad3e662805a144de0c84164 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 3 Apr 2017 11:35:40 -0700 Subject: [PATCH 0254/4563] Reject SE-0159: Fix Private Access Levels --- proposals/0159-fix-private-access-levels.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0159-fix-private-access-levels.md b/proposals/0159-fix-private-access-levels.md index 1760215d46..1b439e9641 100644 --- a/proposals/0159-fix-private-access-levels.md +++ b/proposals/0159-fix-private-access-levels.md @@ -3,7 +3,7 @@ * Proposal: [SE-0159](0159-fix-private-access-levels.md) * Authors: [David Hart](http://github.com/hartbit) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (March 20...27, 2017)** +* Status: **Rejected** ([Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170403/034902.html)) ## Introduction From fac0db0ccf1b51c06b7adc5082336ca6f318f61d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 3 Apr 2017 11:47:29 -0700 Subject: [PATCH 0255/4563] Add a bug tracking SE-0160. --- proposals/0160-objc-inference.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0160-objc-inference.md b/proposals/0160-objc-inference.md index b80ad96e30..e4b31ebf74 100644 --- a/proposals/0160-objc-inference.md +++ b/proposals/0160-objc-inference.md @@ -7,6 +7,7 @@ * Review manager: [Chris Lattner](http://github.com/lattner) * Implementation: [Pull request](https://github.com/apple/swift/pull/8379) +* Bug: [SR-4481](https://bugs.swift.org/browse/SR-4481) ## Introduction From 99315a5ac70d69d2bed6ecd503bc5f1d11ac293e Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Sat, 18 Mar 2017 01:23:14 +0530 Subject: [PATCH 0256/4563] Add Package Manager Custom Targets Layout proposal --- ...N-package-manager-custom-targets-layout.md | 261 ++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 proposals/NNNN-package-manager-custom-targets-layout.md diff --git a/proposals/NNNN-package-manager-custom-targets-layout.md b/proposals/NNNN-package-manager-custom-targets-layout.md new file mode 100644 index 0000000000..5e22f0e7d7 --- /dev/null +++ b/proposals/NNNN-package-manager-custom-targets-layout.md @@ -0,0 +1,261 @@ +# Package Manager Custom Targets Layout + +* Proposal: [SE-NNNN](NNNN-package-manager-custom-targets-layout.md) +* Author: [Ankit Aggarwal](https://github.com/aciidb0mb3r) +* Review Manager: [Rick Ballard](https://github.com/rballard) +* Status: **Discussion** +* Bug: [SR-29](https://bugs.swift.org/browse/SR-29) + +## Introduction + +This proposal enhances the `Package.swift` manifest APIs to support custom +target layouts, and removes a convention which allowed omission of targets from +the manifest. + +## Motivation + +The Package Manager uses a convention system to infer targets structure from +disk layout. This works well for most packages, which can easily adopt the +conventions, and frees users from needing to update their `Package.swift` file +every time they add or remove sources. Adopting the conventions is more +difficult for some packages, however – especially existing C libraries or large +projects, which would be difficult to reorganize. We intend to give users a way +to make such projects into packages without needing to conform to our +conventions. + +The current convention rules make it very convenient to add new targets and +source files by inferring them automatically from disk, but they also can be +confusing, overly-implicit, and difficult to debug; for example, if the user +does not follow the conventions correctly which determine their targets, they +may wind up with targets they don't expect, or not having targets they did +expect, and either way their clients can't easily see which targets are available +by looking at the `Package.swift` manifest. We want to retain convenience where it +really matters, such as easy addition of new source files, but require explicit +declarations where being explicit adds significant value. We also want to make +sure that the implicit conventions we keep are straightforward and easy to +remember. + +## Proposed solution + +* We propose to stop inferring targets from disk. They must be explicitly declared + in the manifest file. The inference was not very useful, as targets eventually + need to be declared in order to use common features such as product and target + dependencies, or build settings (which are planned for Swift 4). Explicit + target declarations make a package easier to understand by clients, and allow us + to provide good diagnostics when the layout on disk does not match the + declarations. + +* We propose to remove the requirement that name of a test target must have + suffix "Tests". Instead, test targets will be explicitly declared as such + in the manifest file. + +* We propose a list of pre-defined search paths for declared targets. + + When a target does not declare an explicit path, these directories will be used + to search for the target. The name of the directory must match the name of + the target. The search will be done in order and will be case-sensitive. + + Regular targets: package root, Sources, Source, src, srcs. + Test targets: Tests, package root, Sources, Source, src, srcs. + + It is an error if a target is found in more than one of these paths. In + such cases, the path should be explicitly declared using the path property + proposed below. + +* We propose to add a factory method `testTarget` to the `Target` class, to define + test targets. + + ```swift + .testTarget(name: "FooTests", dependencies: ["Foo"]) + ``` + +* We propose to add three properties to the `Target` class: `path`, `sources` and + `exclude`. + + * `path`: This property defines the path to the top-level directory containing the + target's sources, relative to the package root. It is not legal for this path + to escape the package root, i.e., values like "../Foo", "/Foo" are invalid. The + default value of this property will be `nil`, which means the target will be + searched for in the pre-defined paths. The empty string ("") or dot (".") implies + that the target's sources are directly inside the package root. + + * `sources`: This property defines the source files to be included in the + target. The default value of this property will be nil, which means all + valid source files found in the target's path will be included. This can + contain directories and individual source files. Directories will be + searched recursively for valid source files. Paths specified are relative + to the target path. + + Each source file will be represented by String type. In future, we will + consider upgrading this to its own type to allow per-file build settings. + The new type would conform to `CustomStringConvertible`, so existing + declarations would continue to work (except where the strings were + constructed programatically). + + * `exclude`: This property can be used to exclude certain files and + directories from being picked up as sources. Exclude paths are relative + to the target path. This property has more precedence than `sources` + property. + + _Note: We plan to support globbing in future, but to keep this proposal short + we are not proposing it right now._ + +* It is an error if the paths of two targets overlap (unless resolved with `exclude`). + + ```swift + // This is an error: + .target(name: "Bar", path: "Sources/Bar"), + .testTarget(name: "BarTests", dependencies: ["Bar"], path: "Sources/Bar/Tests"), + + // This works: + .target(name: "Bar", path: "Sources/Bar", exclude: ["Tests"]), + .testTarget(name: "BarTests", dependencies: ["Bar"], path: "Sources/Bar/Tests"), + ``` + +* For C family library targets, we propose to add a `publicHeadersPath` + property. + + This property defines the path to the directory containing public headers of + a C target. This path is relative to the target path and default value of + this property is `include`. This mechanism should be further improved + in the future, but there are several behaviors, such as modulemap generation, + which currently depend of having only one public headers directory. We will address + those issues separately in a future proposal. + + _All existing rules related to custom and automatic modulemap remain intact._ + +* Remove exclude from `Package` class. + + This property is no longer required because of the above proposed + per-target exclude property. + +* The templates provided by the `swift package init` subcommand will be updated + according to the above rules, so that users do not need to manually + add their first target to the manifest. + +## Examples: + +* Dummy manifest containing all Swift code. + +```swift +let package = Package( + name: "SwiftyJSON", + targets: [ + .target( + name: "Utility", + path: "Sources/BasicCode" + ), + + .target( + name: "SwiftyJSON", + dependencies: ["Utility"], + path: "SJ", + sources: ["SwiftyJSON.swift"] + ), + + .testTarget( + name: "AllTests", + dependencies: ["Utility", "SwiftyJSON"], + path: "Tests", + exclude: ["Fixtures"] + ), + ] +) +``` + +* LibYAML + +```swift +let packages = Package( + name: "LibYAML", + targets: [ + .target( + name: "libyaml", + sources: ["src"] + ) + ] +) +``` + +* Node.js http-parser + +```swift +let packages = Package( + name: "http-parser", + targets: [ + .target( + name: "http-parser", + publicHeaders: ".", + sources: ["http_parser.c"] + ) + ] +) +``` + +* swift-build-tool + +```swift +let packages = Package( + name: "llbuild", + targets: [ + .target( + name: "swift-build-tool", + path: ".", + sources: [ + "lib/Basic", + "lib/llvm/Support", + "lib/Core", + "lib/BuildSystem", + "products/swift-build-tool/swift-build-tool.cpp", + ] + ) + ] +) +``` + +## Impact on existing code + +These enhancements will be added to the version 4 manifest API, which will +release with Swift 4. There will be no impact on packages using the version 3 +manifest API. When packages update their minimum tools version to 4.0, they +will need to update the manifest according to the changes in this proposal. + +There are two flat layouts supported in Swift 3: + +1. Source files directly in the package root. +2. Source files directly inside a `Sources/` directory. + +If packages want to continue using either of these flat layouts, they will need +to explicitly set a target path to the flat directory; otherwise, a directory +named after the target is expected. For example, if a package `Foo` has +following layout: + +``` +Package.swift +Sources/main.swift +Sources/foo.swift +``` + +The updated manifest will look like this: + +```swift +// swift-tools-version:4.0 +import PackageDescription + +let package = Package( + name: "Foo", + targets: [ + .target(name: "Foo", path: "Sources"), + ] +) +``` + +## Alternatives considered + +We considered making a more minimal change which disabled the flat layouts +by default, and provided a top-level property to allow opting back in to them. +This would allow us to discourage these layouts – which we would like +to do before the package ecosystem grows – without needing to add a fully +customizable API. However, we think the fuller API we've proposed here is fairly +straightforward and provides the ability to make a number of existing projects +into packages, so we think this is worth doing at this time. From 5b873bec31e4a6dadf039ad7526974901e253ffe Mon Sep 17 00:00:00 2001 From: Rick Ballard Date: Tue, 4 Apr 2017 10:02:12 -0700 Subject: [PATCH 0257/4563] Assign number and review dates for SE-0162 "Package Manager Custom Target Layouts" --- ...out.md => 0162-package-manager-custom-target-layouts.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-package-manager-custom-targets-layout.md => 0162-package-manager-custom-target-layouts.md} (98%) diff --git a/proposals/NNNN-package-manager-custom-targets-layout.md b/proposals/0162-package-manager-custom-target-layouts.md similarity index 98% rename from proposals/NNNN-package-manager-custom-targets-layout.md rename to proposals/0162-package-manager-custom-target-layouts.md index 5e22f0e7d7..dfe579537c 100644 --- a/proposals/NNNN-package-manager-custom-targets-layout.md +++ b/proposals/0162-package-manager-custom-target-layouts.md @@ -1,9 +1,9 @@ -# Package Manager Custom Targets Layout +# Package Manager Custom Target Layouts -* Proposal: [SE-NNNN](NNNN-package-manager-custom-targets-layout.md) +* Proposal: [SE-0162](0162-package-manager-custom-target-layouts.md) * Author: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Rick Ballard](https://github.com/rballard) -* Status: **Discussion** +* Status: **Active review (April 4...April 10, 2017)** * Bug: [SR-29](https://bugs.swift.org/browse/SR-29) ## Introduction From 61be061eeaa7996548555b9ef1df6488df04d826 Mon Sep 17 00:00:00 2001 From: Bas Broek Date: Wed, 5 Apr 2017 06:35:22 +0200 Subject: [PATCH 0258/4563] Fix typo in SE-0155 (#670) --- proposals/0155-normalize-enum-case-representation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0155-normalize-enum-case-representation.md b/proposals/0155-normalize-enum-case-representation.md index f544390d5d..ea15d56080 100644 --- a/proposals/0155-normalize-enum-case-representation.md +++ b/proposals/0155-normalize-enum-case-representation.md @@ -14,7 +14,7 @@ pattern matching in several places. Enums, therefore, can be made more "regular" when we replace tuple as the representation of associated case values. This proposal aims to define the -effect of doings so on various parts of the language. +effect of doing so on various parts of the language. Swift-evolution thread: [Normalize Enum Case Representation (rev. 2)][] From 329c165c2b7907e8e92d8338907548f7e5e400de Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 5 Apr 2017 14:35:18 -0400 Subject: [PATCH 0259/4563] Make "String Revision" SE-163 and put it under active review --- .../{0161-StringRevision1.md => 0163-string-revision-1.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{0161-StringRevision1.md => 0163-string-revision-1.md} (98%) diff --git a/proposals/0161-StringRevision1.md b/proposals/0163-string-revision-1.md similarity index 98% rename from proposals/0161-StringRevision1.md rename to proposals/0163-string-revision-1.md index c514e316e1..81ceb8d793 100644 --- a/proposals/0161-StringRevision1.md +++ b/proposals/0163-string-revision-1.md @@ -1,9 +1,9 @@ # String Revision: Collection Conformance, C Interop, Transcoding -* Proposal: [SE-0161](0161-StringRevision1.md) +* Proposal: [SE-0163](0161-string-reivision-1.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Dave Abrahams](http://github.com/dabrahams/) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (April 5...11, 2017)** ## Introduction From 6850bc39dd2560d786343327dd8cf1dd4b73b9c5 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 5 Apr 2017 14:40:14 -0400 Subject: [PATCH 0260/4563] Typo in self URL --- proposals/0163-string-revision-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0163-string-revision-1.md b/proposals/0163-string-revision-1.md index 81ceb8d793..9293ef533f 100644 --- a/proposals/0163-string-revision-1.md +++ b/proposals/0163-string-revision-1.md @@ -1,6 +1,6 @@ # String Revision: Collection Conformance, C Interop, Transcoding -* Proposal: [SE-0163](0161-string-reivision-1.md) +* Proposal: [SE-0163](0161-string-revision-1.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Dave Abrahams](http://github.com/dabrahams/) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (April 5...11, 2017)** From 40d055f3f27b33191281ce15743841bbbb1d4639 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 5 Apr 2017 14:41:49 -0400 Subject: [PATCH 0261/4563] Somehow I snuck another typo into this twenty-five character URL --- proposals/0163-string-revision-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0163-string-revision-1.md b/proposals/0163-string-revision-1.md index 9293ef533f..c7e2cc9845 100644 --- a/proposals/0163-string-revision-1.md +++ b/proposals/0163-string-revision-1.md @@ -1,6 +1,6 @@ # String Revision: Collection Conformance, C Interop, Transcoding -* Proposal: [SE-0163](0161-string-revision-1.md) +* Proposal: [SE-0163](0163-string-revision-1.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Dave Abrahams](http://github.com/dabrahams/) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (April 5...11, 2017)** From 4122875a9f6a261b6f872837a9c6a6ef286a57ac Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 5 Apr 2017 13:53:09 -0700 Subject: [PATCH 0262/4563] Add bug number for SE-0142. --- proposals/0142-associated-types-constraints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0142-associated-types-constraints.md b/proposals/0142-associated-types-constraints.md index 428385ff57..04a9d810da 100644 --- a/proposals/0142-associated-types-constraints.md +++ b/proposals/0142-associated-types-constraints.md @@ -5,7 +5,7 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Accepted** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161003/027606.html) - +* Bugs: [SR-4506](https://bugs.swift.org/browse/SR-4506) ## Introduction From 0607d208ce2b81fceb146722b46e2ac18eee78d4 Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 5 Apr 2017 17:56:35 -0400 Subject: [PATCH 0263/4563] Proposal to remove support for final in protocol extensions (#636) * Add proposal to remove support for final in protocol extensions * Add a link to the mailing list discussion --- ...ve-final-support-in-protocol-extensions.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 proposals/XXXX-remove-final-support-in-protocol-extensions.md diff --git a/proposals/XXXX-remove-final-support-in-protocol-extensions.md b/proposals/XXXX-remove-final-support-in-protocol-extensions.md new file mode 100644 index 0000000000..d652978724 --- /dev/null +++ b/proposals/XXXX-remove-final-support-in-protocol-extensions.md @@ -0,0 +1,52 @@ +# Remove final support in protocol extensions + +* Proposal: [SE-XXXX](XXXX-remove-final-support-in-protocol-extensions.md) +* Authors: [Brian King](https://github.com/KingOfBrian) +* Review Manager: TBD +* Status: **Awaiting review** +* Bug: [SR-1762](https://bugs.swift.org/browse/SR-1762) + +## Introduction +This proposal disallows the `final` keyword when declaring functions in protocol +extensions. + +*Discussion took place on the Swift Evolution mailing list in the [Remove support for final in protocol extensions](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170306/033604.html) thread.* + +## Motivation + +In the current version of Swift, the `final` keyword does not modify +dispatch behavior in any way, and it does not generate an error message. +This keyword has no use in Swift's current protocol model. Functions in +protocol extensions cannot be overridden and will always use direct dispatch. + +Jordan Rose described the history behind this behavior: +``` +We originally required `final` to signify that there was no +dynamic dispatch going on. Once we started allowing protocol extension +methods to fulfill requirements, it became more confusing than useful. +``` + +## Detailed design + +If adopted, the compiler will flag the use of the `final` keyword on functions +declared within a protocol extension, and emit an error or warning. This +behavior is consistent with `final` use in structures and enumerations. + +## Source compatibility + +This change will impact source compatibility. Existing use of `final` in +protocol extensions will raise a compilation error. The compiler will address +this by source migration and fixits. When running in Swift 3 mode, a warning +will be generated instead of an error. + +## Effect on ABI stability + +This proposal does not affect ABI stability. + +## Effect on API resilience + +This proposal does not affect API resilience. + +## Alternatives considered + +There are no alternatives considered at this time. From b75bdcab3657767b69ba57b43e038eb625757a97 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 5 Apr 2017 15:01:21 -0700 Subject: [PATCH 0264/4563] Initiate review for SE-0164: Remove final support in protocol extensions --- ... => 0164-remove-final-support-in-protocol-extensions.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{XXXX-remove-final-support-in-protocol-extensions.md => 0164-remove-final-support-in-protocol-extensions.md} (91%) diff --git a/proposals/XXXX-remove-final-support-in-protocol-extensions.md b/proposals/0164-remove-final-support-in-protocol-extensions.md similarity index 91% rename from proposals/XXXX-remove-final-support-in-protocol-extensions.md rename to proposals/0164-remove-final-support-in-protocol-extensions.md index d652978724..bec19dc9e5 100644 --- a/proposals/XXXX-remove-final-support-in-protocol-extensions.md +++ b/proposals/0164-remove-final-support-in-protocol-extensions.md @@ -1,9 +1,9 @@ # Remove final support in protocol extensions -* Proposal: [SE-XXXX](XXXX-remove-final-support-in-protocol-extensions.md) +* Proposal: [SE-0164](0164-remove-final-support-in-protocol-extensions.md) * Authors: [Brian King](https://github.com/KingOfBrian) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (April 5...8, 2017)** * Bug: [SR-1762](https://bugs.swift.org/browse/SR-1762) ## Introduction From 55e61f459632eca2face40e571a517919f846cfb Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 5 Apr 2017 16:02:04 -0700 Subject: [PATCH 0265/4563] Return SE-0161 for revision --- proposals/0161-key-paths.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0161-key-paths.md b/proposals/0161-key-paths.md index aa136ac0aa..3d2969e13a 100644 --- a/proposals/0161-key-paths.md +++ b/proposals/0161-key-paths.md @@ -3,7 +3,7 @@ * Proposal: [SE-0161](0161-key-paths.md) * Authors: [David Smith](https://github.com/Catfish-Man), [Michael LeHew](https://github.com/mlehew), [Joe Groff](https://github.com/jckarter) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (March 30...April 5, 2017)** +* Status: **Returned for revision** ([Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170403/035061.html)) * Associated PRs: * [#644](https://github.com/apple/swift-evolution/pull/644) From 4c9ee9a872dbaa21a357637e15dd74babf9c0262 Mon Sep 17 00:00:00 2001 From: Michael LeHew Date: Wed, 5 Apr 2017 16:47:27 -0700 Subject: [PATCH 0266/4563] Key path literal syntax (#671) * Adopt new escape-literal sigil * Fix a few words * Add a Why \? section in alternatives: * Describe restriciton on subscript parameters --- proposals/0161-key-paths.md | 52 +++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/proposals/0161-key-paths.md b/proposals/0161-key-paths.md index 3d2969e13a..dd0e771141 100644 --- a/proposals/0161-key-paths.md +++ b/proposals/0161-key-paths.md @@ -25,10 +25,12 @@ While methods can be referred to without invoking them (`let x = foo.bar` instea Making indirect references to a properties' concrete types also lets us expose metadata about the property, and in the future additional behaviors. #### More Expressive KeyPaths -We would also like to support being able to use _Key Paths_ to access into collections, which is not currently possible. +We would also like to support being able to use _Key Paths_ to access into collections and other subscriptable types, which is not currently possible. ## Proposed solution -We propose expanding the capabilities of the `#keyPath` directive to produce `KeyPath` objects instead of `Strings`. `KeyPaths` are a family of generic classes _(structs and protocols here would be ideal, but requires generalized existentials)_ which encapsulate a property reference or chain of property references, including the type, mutability, property name(s), and ability to set/get values. +We propose introducing a new expression similar to function type references (e.g. `Type.method`), but for properties and subscripts. To avoid ambiguities with type properties, we propose we escape such expressions using `\` to indicate that you are talking about the property, not its invocation. + +These property reference expressions produce `KeyPath` objects, rather than `Strings`. `KeyPaths` are a family of generic classes _(structs and protocols here would be ideal, but requires generalized existentials)_ which encapsulate a property reference or chain of property references, including the type, mutability, property name(s), and ability to set/get values. Here's a sample of it in use: @@ -47,17 +49,17 @@ var luke = Person(name: "Luke Skywalker") luke.friends.append(han) // create a key path and use it -let firstFriendsNameKeyPath = #keyPath(Person, .friends[0].name) +let firstFriendsNameKeyPath = \Person.friends[0].name) let firstFriend = luke[keyPath: firstFriendsNameKeyPath] // "Han Solo" // or equivalently, with type inferred from context -luke[keyPath: #keyPath(.friends[0].name)] // "Han Solo" +luke[keyPath: \.friends[0].name] // "Han Solo" // rename Luke's first friend luke[keyPath: firstFriendsNameKeyPath] = "A Disreputable Smuggler" // optional properties work too -let bestFriendsNameKeyPath = #keyPath(Person, .bestFriend?.name) +let bestFriendsNameKeyPath = \Person.bestFriend?.name) let bestFriendsName = luke[keyPath: bestFriendsNameKeyPath] // nil, if he is the last Jedi ``` @@ -143,13 +145,14 @@ person[keyPath: someKeyPath] which is both appealingly readable, and doesn't require read-modify-write copies (subscripts access `self` inout). Conflicts with existing subscripts are avoided by using a named parameter and generics to only accept key paths with a `Root` of the type in question. ### Referencing Key Paths -Forming a `KeyPath` borrows from the same syntax added in Swift 3 to confirm the existence of a given key path, only now producing concrete values instead of Strings. Optionals are handled via optional-chaining. Multiply dotted expressions are allowed as well, and work just as if they were composed via the `appending` methods on `KeyPath`. +Forming a `KeyPath` utilizes a new escape sigil `\`. We feel this best serves our needs of disambiguating from existing `#keyPath` expressions (which will continue to produce `Strings`) and existing type properties. +Optionals are handled via optional-chaining. Multiply dotted expressions are allowed as well, and work just as if they were composed via the `appending` methods on `KeyPath -There is no change or interaction with the #keyPath() syntax introduced in Swift 3. `#keyPath(Person.bestFriend.name)` will still produce a String, whereas `#keyPath(Person, .bestFriend.name)` will produce a `KeyPath`. +Forming a key path through subscripts (e.g. Array / Dictionary) will have the limitation that the parameter's type(s) must be `Hashable`. Should the archival and serialization proposal be accepted, we would also like to include `Codable` with an eye towards being able to make key paths `Codable` themselves in the future. ### Performance -The performance of interacting with a property via `KeyPaths` should be close to the cost of calling the property directly. +The performance of interacting with a property/subscript via `KeyPaths` should be close to the cost of calling the property directly. ## Source compatibility This change is additive and there should no affect on existing source. @@ -172,31 +175,24 @@ Various drafts of this proposal have included additional features (decomposable #### Spelling We also explored many different spellings, each with different strengths. We have chosen the current syntax for the clarity and discoverability it provides in practice. -| `#keyPath` | Function Type Reference | Lisp-style | -| --- | --- | --- | -| `#keyPath(Person, .friends[0].name)` | `Person.friends[0].name` | \``Person.friend.name` | -| `#keyPath(luke, .friends[0].name)` |`luke[.friends[0].name]` | `luke`\``.friends[0].name` | -| `#keyPath(luke.friends[0], .name)`| `luke.friends[0][.name]` | `luke.friends[0]`\``.name` | +| Case | `#keyPath` | Function Type Reference | Escape | +| --- | --- | --- | --- | +| Fully qualified | `#keyPath(Person, .friends[0].name)` | `Person.friends[0].name` | `\Person.friends[0].name` | +| Type Inferred| `#keyPath(.friends[0].name)` |`Person.friends[0].name]` | `\.friends[0].name` | -While the crispness of the function-type-reference is appealing, it becomes ambigious when working with type properties. The spelling of the escape-sigil of the Lisp-style remains a barrier to adoption, but could be considered in the future should `#keyPath` prove a burden. +While the crispness of the function-type-reference is appealing, it becomes ambigious when working with type properties. The escape-sigil variant avoids this, and remains quite readable. -We think most of the situations where `#keyPath` could be overly taxing likely wont show up outside of demonstrative examples: +#### Why `\`? +During review many different sigils were considered: -```swift -// you would likely never type this: -#keyPath(Person, .friends).appending(#keyPath(Array, [0])) +**No Sigil**: This matches function type references, but suffers from ambigiuty with wanting to actually call a type property. Having to type `let foo: KeyPath` while consistent with function type references, really is not that great (even for function type references). -// since you can just type this: -#keyPath(Person, .friends[0]) +**Back Tick**: Borrowing from lisp, back-tick was what we used in initial discussions of this proposal (it was easy to write on a white-board), but it was not chosen because it is hard to type in markdown, and comes dangerously close to conflicting with other parser intrinsics. -// .appending is more likely used in situations like this: -let somePath : PartialKeyPath<[Person]> = ... -#keyPath(Person, .friends).appending(somePath) +**Pound**: We considered `#` as well, and while it is appealing, we'd like to save it for the future. `#` also has a slightly more computational connotation in Swift so far. For instance, `#keyPath` 'identifies if its valid and returns a String', `#available` does the neccesary computation to verify availability and yields a boolean. -// similarly, you'd never type this: -person[keyPath: #keyPath(Person, .friends[0])) +**Back Slash**: Where `#` is computational, `\` in Swift has more of a 'behave differently for a moment' connotation, and that seems to fit exactly what we want when forming a key path. -// since that is just a roundabout way of saying: -person.friends[0] -``` +#### Function Type References +We think the disambiguating benefits of the escape-sigil would greatly benefit function type references, but such considerations are outside the scope of this proposal. From d927238126bb7a2ac5992792de1145e30f32f1d9 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 5 Apr 2017 16:50:06 -0700 Subject: [PATCH 0267/4563] Initiate second review of SE-0161: Smart KeyPaths: Better Key-Value Coding for Swift --- proposals/0161-key-paths.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0161-key-paths.md b/proposals/0161-key-paths.md index dd0e771141..eaa5415d66 100644 --- a/proposals/0161-key-paths.md +++ b/proposals/0161-key-paths.md @@ -3,9 +3,10 @@ * Proposal: [SE-0161](0161-key-paths.md) * Authors: [David Smith](https://github.com/Catfish-Man), [Michael LeHew](https://github.com/mlehew), [Joe Groff](https://github.com/jckarter) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Returned for revision** ([Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170403/035061.html)) +* Status: **Active review (April 5...9, 2017)** * Associated PRs: * [#644](https://github.com/apple/swift-evolution/pull/644) +* Previous Revision: [1][https://github.com/apple/swift-evolution/blob/55e61f459632eca2face40e571a517919f846cfb/proposals/0161-key-paths.md] ## Introduction We propose a family of concrete _Key Path_ types that represent uninvoked references to properties that can be composed to form paths through many values and directly get/set their underlying values. From 991ea2166bc5a08e5c55352fa5528ffd97468776 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 5 Apr 2017 16:57:04 -0700 Subject: [PATCH 0268/4563] Fix link in SE-0161 --- proposals/0161-key-paths.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0161-key-paths.md b/proposals/0161-key-paths.md index eaa5415d66..f52264506f 100644 --- a/proposals/0161-key-paths.md +++ b/proposals/0161-key-paths.md @@ -6,7 +6,7 @@ * Status: **Active review (April 5...9, 2017)** * Associated PRs: * [#644](https://github.com/apple/swift-evolution/pull/644) -* Previous Revision: [1][https://github.com/apple/swift-evolution/blob/55e61f459632eca2face40e571a517919f846cfb/proposals/0161-key-paths.md] +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/55e61f459632eca2face40e571a517919f846cfb/proposals/0161-key-paths.md) ## Introduction We propose a family of concrete _Key Path_ types that represent uninvoked references to properties that can be composed to form paths through many values and directly get/set their underlying values. From 148a713659a81f1d6beb153fe7450fc94ccae021 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Wed, 5 Apr 2017 19:36:20 -0500 Subject: [PATCH 0269/4563] Add new dictionary & set proposal (#669) Kick off review --- ...ence-based-init-and-merge-to-dictionary.md | 2 +- proposals/0165-dict.md | 492 ++++++++++++++++++ 2 files changed, 493 insertions(+), 1 deletion(-) create mode 100644 proposals/0165-dict.md diff --git a/proposals/0100-add-sequence-based-init-and-merge-to-dictionary.md b/proposals/0100-add-sequence-based-init-and-merge-to-dictionary.md index c75df238b8..b4d2d3792a 100644 --- a/proposals/0100-add-sequence-based-init-and-merge-to-dictionary.md +++ b/proposals/0100-add-sequence-based-init-and-merge-to-dictionary.md @@ -3,7 +3,7 @@ * Proposal: [SE-0100](0100-add-sequence-based-init-and-merge-to-dictionary.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: TBD -* Status: **Deferred** +* Status: **Withdrawn** ## Introduction diff --git a/proposals/0165-dict.md b/proposals/0165-dict.md new file mode 100644 index 0000000000..961c499821 --- /dev/null +++ b/proposals/0165-dict.md @@ -0,0 +1,492 @@ +# Dictionary & Set Enhancements + +- Proposal: [SE-0165](0165-dict.md) +- Author: [Nate Cook](https://github.com/natecook1000) +- Review Manager: [Ben Cohen](https://github.com/airspeedswift) +- Status: **Active review (April 5...11, 2017)** + +## Introduction + +This proposal comprises a variety of commonly (and less commonly) suggested improvements to the standard library's `Dictionary` type, from merging initializers to dictionary-specific `filter` and `mapValues` methods. The proposed additions to `Dictionary`, and the corresponding changes to `Set`, are detailed in the sections below. + +- Suggested Improvements: + - [Merging initializers and methods](#1-merging-initializers-and-methods) + - [Key-based subscript with default value](#2-key-based-subscript-with-default-value) + - [Dictionary-specific map and filter](#3-dictionary-specific-map-and-filter) + - [Visible `Dictionary` capacity](#4-visible-dictionary-capacity) + - [A `grouped(by:)` method for sequences](#5-a-groupedby-method-for-sequences) + - [Relevant changes to `Set`](#6-apply-relevant-changes-to-set) +- [Detailed Design](#detailed-design) + - [Sandbox with Prototype][sandbox] +- [Source Compatibility](#source-compatibility) + +--- + +## 1. Merging initializers and methods + +The `Dictionary` type should allow initialization from a sequence of `(Key, Value)` tuples and offer methods that merge a sequence of `(Key, Value)` tuples into a new or existing dictionary, using a closure to combine values for duplicate keys. + +- [First message of discussion thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/006124.html) +- [Initial proposal draft](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006665.html) +- [Prior standalone proposal (SE-100)](https://github.com/apple/swift-evolution/blob/master/proposals/0100-add-sequence-based-init-and-merge-to-dictionary.md) + +`Array` and `Set` both have initializers that create a new instance from a sequence of elements. The `Array` initializer is useful for converting other sequences and collections to the "standard" collection type, while the `Set` initializer is essential for recovering set operations after performing any functional operations on a set. For example, filtering a set produces a collection without any set operations available. + +```swift +let numberSet = Set(1 ... 100) +let fivesOnly = numberSet.lazy.filter { $0 % 5 == 0 } +``` + +`fivesOnly` is a `LazyFilterCollection>` instead of a `Set`—sending that back through the `Set` sequence initializer restores the expected methods. + +```swift +let fivesOnlySet = Set(numberSet.lazy.filter { $0 % 5 == 0 }) +fivesOnlySet.isSubsetOf(numberSet) // true +``` + +`Dictionary`, on the other hand, has no such initializer, so a similar operation leaves no room to recover dictionary functionality without building a mutable `Dictionary` via iteration or functional methods. These techniques also don't support type inference from the source sequence, increasing verbosity. + +```swift +let numberDictionary = ["one": 1, "two": 2, "three": 3, "four": 4] +let evenOnly = numberDictionary.lazy.filter { (_, value) in + value % 2 == 0 +} + +var viaIteration: [String: Int] = [:] +for (key, value) in evenOnly { + viaIteration[key] = value +} + +let viaReduce: [String: Int] = evenOnly.reduce([:]) { (cumulative, kv) in + var dict = cumulative + dict[kv.key] = kv.value + return dict +} +``` + +Beyond initialization, `Array` and `Set` both also provide a method to add a new block of elements to an existing collection. `Array` provides this via `append(contentsOf:)` for the common appending case or `replaceSubrange(_:with:)` for general inserting or replacing, while the unordered `Set` type lets you pass any sequence to `unionInPlace(_:)` to add elements to an existing set. + +Once again, `Dictionary` has no corresponding API -- looping and adding elements one at a time as shown above is the only way to merge new elements into an existing dictionary. + + +### Proposed solution + +This proposal puts forward two new ways to convert `(Key, Value)` sequences to dictionary form: a full-width initializer and a set of merging APIs that handle input data with duplicate keys. + +#### Sequence-based initializer + +The proposed solution would add a new initializer to `Dictionary` that accepts any sequence of `(Key, Value)` tuple pairs. + +```swift +init( + _ keysAndValues: S) +``` + +With the proposed initializer, creating a `Dictionary` instance from a sequence of key/value pairs is as easy as creating an `Array` or `Set`: + +```swift +let viaProposed = Dictionary(evenOnly) +``` + +Like `Array.init(_:)` and `Set.init(_:)`, this is a full-width initializer. To ensure this, the initializer has a precondition that each key in the supplied sequence is unique, and traps whenever that condition isn't met. When duplicate keys are possible in the input, the merging initializer described in the next section must be used instead. + +The new initializer allows for some convenient uses that aren't currently possible. + +- Initializing from a `DictionaryLiteral` (the type, not an actual literal) + + ```swift + let literal: DictionaryLiteral = ["a": 1, "b": 2, "c": 3, "d": 4] + let dictFromDL = Dictionary(literal) + ``` + +- Converting an array to an indexed dictionary (popular on the thread) + + ```swift + let names = ["Cagney", "Lacey", "Bensen"] + let dict = Dictionary(names.enumerated().map { (i, val) in (i + 1, val) }) + // [2: "Lacey", 3: "Bensen", 1: "Cagney"] + ``` + +- Initializing from a pair of zipped sequences (examples abound) + + ```swift + let letters = "abcdef".characters.lazy.map(String.init) + let dictFromZip = Dictionary(zip(letters, 1...10)) + // ["b": 2, "e": 5, "a": 1, "f": 6, "d": 4, "c": 3] + ``` + + > This particular use is currently blocked by [SR-922](https://bugs.swift.org/browse/SR-922). As a workaround, add `.map {(key: $0, value: $1)}`. + +#### Merging initializer and methods + +Creating a `Dictionary` from a dictional literal checks the keys for uniqueness, trapping on a duplicate. The sequence-based initializer shown above has the same requirement. + +```swift +let duplicates: DictionaryLiteral = ["a": 1, "b": 2, "a": 3, "b": 4] +let letterDict = Dictionary(duplicates) +// error: Duplicate key found: "a" +``` + +Because some use cases can be forgiving of duplicate keys, this proposal includes a second new initializer. This initializer allows the caller to supply, along with the sequence, a combining closure that's called with the old and new values for any duplicate keys. + +```swift +init( + merging keysAndValues: S, + mergingValues combine: (Value, Value) throws -> Value +) rethrows where S.Iterator.Element == (key: Key, value: Value) +``` + +This example shows how one could keep the first value of all those supplied for a duplicate key. + +```swift +let letterDict2 = Dictionary(merging: duplicates, mergingValues: { (first, _) in first }) +// ["b": 2, "a": 1] +``` + +Or the largest value for any duplicate keys. + +```swift +let letterDict3 = Dictionary(merging: duplicates, mergingValues: max) +// ["b": 4, "a": 3] +``` + +At other times the merging initializer could be used to combine values for duplicate keys. Donnacha Oisín Kidney wrote a neat `frequencies()` method for sequences as an example of such a use in the thread. + +```swift +extension Sequence where Iterator.Element: Hashable { + func frequencies() -> [Iterator.Element: Int] { + return Dictionary(merging: self.lazy.map { v in (v, 1) }, mergingValues: +) + } +} +[1, 2, 2, 3, 1, 2, 4, 5, 3, 2, 3, 1].frequencies() +// [2: 4, 4: 1, 5: 1, 3: 3, 1: 3] +``` + +This proposal also includes new mutating and non-mutating methods for `Dictionary` that merge the contents of a sequence of `(Key, Value)` tuples into an existing dictionary, `merge(_:mergingValues:)` and `merged(with:mergingValues:)`. + +```swift +mutating func merge( + _ other: S, + mergingValues combine: (Value, Value) throws -> Value +) rethrows where S.Iterator.Element == (key: Key, value: Value) + +func merged( + with other: S, + mergingValues combine: (Value, Value) throws -> Value +) rethrows -> [Key: Value] where S.Iterator.Element == (key: Key, value: Value) +``` + +As above, there are a wide variety of uses for the merge. + +```swift +// Adding default values +let defaults: [String: Bool] = ["foo": false, "bar": false, "baz": false] +var options: [String: Bool] = ["foo": true, "bar": false] +options.merge(defaults) { (old, _) in old } +// options is now ["foo": true, "bar": false, "baz": false] + +// Summing counts repeatedly +var bugCounts: [String: Int] = ["bees": 9, "ants": 112, ...] +while bugCountingSource.hasMoreData() { + bugCounts.merge(bugCountingSource.countMoreBugs(), mergingValues: +) +} +``` + +--- + +## 2. Key-based subscript with default value + +Another common challenge with dictionaries is iteratively making changes to key/value pairs that may or may not already be present. For example, to iteratively add count the frequencies of letters in a string, one might write something like the following: + +```swift +let source = "how now brown cow" +var frequencies: [Character: Int] = [:] +for c in source.characters { + if frequencies[c] == nil { + frequencies[c] = 1 + } else { + frequencies[c]! += 1 + } +} +``` + +Testing for `nil` and assigning through the force unwrapping operator are awkward at best for such a common operation. Furthermore, the `Optional` return type of the current keyed subscript complicates efficiencies that could be gained for this type of `modify` action under a future ownership model. + +### Proposed solution + +A keyed subscript with a default value neatly simplifies this usage. Instead of checking for `nil`, one can pass the default value along with the key as a `default` subscript parameter. + +```swift +let source = "how now brown cow" +var frequencies: [Character: Int] = [:] +for c in source.characters { + frequencies[c, default: 0] += 1 +} +``` + +The return type of this subscript is a non-optional `Value`. Note that accessing the subscript as a getter does not store the default value in the dictionary—the following two lines are equivalent: + +```swift +let x = frequencies["a", default: 0] +let y = frequencies["a"] ?? 0 +``` + +--- + +## 3. Dictionary-specific map and filter + +The standard `map` and `filter` methods, while always useful and beloved, aren't ideal when applied to dictionaries. In both cases, the desired end-result is frequently another dictionary instead of an array of key-value pairs—even with the sequence-based initializer proposed above this is an inefficient way of doing things. + +Additionally, the standard `map` method doesn't gracefully support passing a function when transforming only the *values* of a dictionary. The transform function must accept and return key/value pairs, necessitating a custom closure in nearly every case. + +Assuming the addition of a sequence-based initializer, the current `filter` and `map` look like the following: + +```swift +let numbers = ["one": 1, "two": 2, "three": 3, "four": 4] +let evens = Dictionary(numbers.lazy.filter { $0.value % 2 == 0 })! +// ["four": 4, "two": 2] +let strings = Dictionary(numbers.lazy.map { (k, v) in (k, String(v)) })! +// ["three": "3", "four": "4", "one": "1", "two": "2"] +``` + +### Proposed solution + +This proposal adds two new methods for `Dictionary`: + +1. A `mapValues` method that keeps a dictionary's keys intact while transforming the values. Mapping a dictionary's key/value pairs can't always produce a new dictionary, due to the possibility of key collisions, but mapping only the values can produce a new dictionary with the same underlying layout as the original. + + ```swift + let strings = numbers.mapValues(String.init) + // ["three": "3", "four": "4", "one": "1", "two": "2"] + ``` + +2. A `Dictionary`-returning `filter` method. While transforming the keys and values of a dictionary can result in key collisions, filtering the elements of a dictionary can at worst replicate the entire dictionary. + + ```swift + let evens = numbers.filter { $0.value % 2 == 0 } + // ["four": 4, "two": 2] + ``` + +Both of these can be made significantly more efficient than their `Sequence`-sourced counterparts. For example, the `mapValues` method can simply copy the portion of the storage that holds the keys to the new dictionary before transforming the values. + +--- + +## 4. Visible dictionary capacity + +As you add elements to a dictionary, it automatically grows its backing storage as necessary. This reallocation is a significant operation—unlike arrays, where the existing elements can be copied to a new block of storage en masse, every key/value pair must be moved over individually, recalculating the hash value for the key to find its position in the larger backing buffer. + +While dictionaries uses an exponential growth strategy to make this as efficient as possible, beyond the `init(minimumCapacity:)` initializer they do not expose a way to reserve a specific capacity. In addition, adding a key/value pair to a dictionary is guaranteed not to invalidate existing indices as long as the capacity doesn't change, yet we don't provide any way of seeing a dictionary's current or post-addition capacity. + +### Proposed solution + +`Dictionary` should add a `capacity` property and a `reserveCapacity(_:)` method, like those used in range-replaceable collections. The `capacity` of a dictionary is the number of elements it can hold without reallocating a larger backing storage, while calling `reserveCapacity(_:)` allocates a large enough buffer to hold the requested number of elements without reallocating. + +```swift +var numbers = ["one": 1, "two": 2, "three": 3, "four": 4] +numbers.capacity // 6 +numbers.reserveCapacity(20) +numbers.capacity // 24 +``` + +Because hashed collections use extra storage capacity to reduce the likelihood and cost of collisions, the value of the `capacity` property won't be equal to the actual size of the backing storage. Likewise, the capacity after calling `reserveCapacity(_:)` will be at least as large as the argument, but usually larger. (In its current implementation, `Dictionary` always has a power of 2-sized backing storage.) + +--- + +## 5. A `grouped(by:)` method for sequences + +As a final `Dictionary`-related issue, grouping elements in a sequence by some computed key is a commonly requested addition that we can add as part of this omnibus proposal. Pass a closure that converts each value in a sequence to a hashable type `T`; the result of the method is a dictionary with keys of type `T` and values of type `[Iterator.Element]`. + +```swift +let names = ["Patti", "Aretha", "Anita", "Gladys"] + +// By first letter +names.grouped(by: { $0.characters.first! }) +// ["P": ["Patti"], "G": ["Gladys"], "A": ["Aretha", "Anita"]] + +// By name length +names.grouped { $0.utf16.count } +// [5: ["Patti", "Anita"], 6: ["Aretha", "Gladys"]] +``` + +--- + +## 6. Apply relevant changes to `Set` + +As the `Set` and `Dictionary` types are similar enough to share large chunks of their implementations, it makes sense to look at which of these `Dictionary` enhancements can also be applied to `Set`. Of the symbols proposed above, the following additions would also be useful and appropriate for the `Set` type: + +- Add a `Set`-specific `filter` method that returns a new set—this would function essentially as a predicate-based `intersection` method. +- Add a `capacity` property and `reserveCapacity()` method, for the reasons listed above. + + + +## Detailed design + +With the exception of the proposed capacity property and method, the proposed additions to `Dictionary`, `Set`, and `Sequence` are available in [this Swift Sandbox][sandbox]. Note that this prototype is not a proposed implementation; rather a way to try out the behavior of the proposed changes. + +Collected in one place, these are the new APIs for `Dictionary`, `Set`, and `Sequence`: + +```swift +struct Dictionary { + typealias Element = (key: Key, value: Value) + + // existing declarations + + /// Creates a new dictionary using the key/value pairs in the given sequence. + /// If the given sequence has any duplicate keys, the result is `nil`. + init(_ keysAndValues: S) where S.Iterator.Element == Element + + /// Creates a new dictionary using the key/value pairs in the given sequence, + /// using a combining closure to determine the value for any duplicate keys. + init( + merging keysAndValues: S, + mergingValues combine: (Value, Value) throws -> Value + ) rethrows where S.Iterator.Element == Element + + /// Merges the key/value pairs in the given sequence into the dictionary, + /// using a combining closure to determine the value for any duplicate keys. + mutating func merge( + _ other: S, + mergingValues combine: (Value, Value) throws -> Value + ) rethrows where S.Iterator.Element == Element + + /// Returns a new dictionary created by merging the key/value pairs in the + /// given sequence into the dictionary, using a combining closure to determine + /// the value for any duplicate keys. + func merged( + with other: S, + mergingValues combine: (Value, Value) throws -> Value + ) rethrows -> [Key: Value] where S.Iterator.Element == Element + + /// Accesses the element with the given key, or the specified default value, + /// if the dictionary doesn't contain the given key. + subscript(key: Key, default defaultValue: Value) -> Value { get set } + + /// Returns a new dictionary containing the key/value pairs that satisfy + /// the given predicate. + func filter(_ isIncluded: (Key, Value) throws -> Bool) rethrows -> [Key: Value] + + /// Returns a new dictionary containing the existing keys and the results of + /// mapping the given closure over the dictionary's values. + func mapValues(_ transform: (Value) throws -> T) rethrows -> [Key: T] + + /// The number of key/value pairs that can be stored by the dictionary without + /// reallocating storage. + var capacity: Int { get } + + /// Ensures that the dictionary has enough storage for `capacity` key/value + /// pairs. + var reserveCapacity(_ capacity: Int) +} + +struct Set { + // existing declarations + + /// Returns a new set containing the elements that satisfy the given predicate. + func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> Set + + /// The number of elements that can be stored by the set without + /// reallocating storage. + var capacity: Int { get } + + /// Ensures that the set has enough storage for `capacity` elements. + var reserveCapacity(_ capacity: Int) +} + +extension Sequence { + /// Returns a dictionary where the keys are the groupings returned by + /// the given closure and the values are arrays of the elements that + /// returned each specific key. + func grouped( + by grouping: (Iterator.Element) throws -> Key + ) rethrows -> [Key: [Iterator.Element]] +} +``` + +## Alternatives Considered + +- An earlier version of this proposal declared the first merging initializer as failable, returning `nil` when a sequence with duplicate keys was passed. That initializer is really only appropriate when working with known unique keys, however, as there isn't a feasible recovery path when there can be duplicate keys. That leaves two possibilities: + 1. The source input can *never* have duplicate keys, and the programmer has to write `!` or unwrap in some other way, or + 2. The source input *can* have duplicate keys, in which case the programmer should be using `init(merging:mergingValues:)` instead. + +- An earlier version of this proposal included the addition of two new top-level functions to the standard library: `first(_:_:)` and `last(_:_:)`, which return their first and last arguments, respectively. These new functions would be passed instead of a custom closure to a merging method or initializer: + + ```swift + let firstWins = Dictionary(merging: duplicates, mergingValues: first) + // ["b": 2, "a": 1] + + let lastWins = Dictionary(merging: duplicates, mergingValues: last) + // ["b": 4, "a": 3] + ``` + + As an alternative to the `first(_:_:)` and `last(_:_:)` functions, at the cost of three additional overloads for the merging initializer and methods, we could allow users to specify the `useFirst` and `useLast` cases of a `MergeCollisionStrategy` enumeration. + + ```swift + extension Dictionary { + /// The strategy to use when merging a sequence of key-value pairs into a dictionary. + enum MergeCollisionStrategy { + /// If there is more than one instance of a key in the sequence to merge, use + /// only the first value for the dictionary. + case useFirst + /// If there is more than one instance of a key in the sequence to merge, use + /// only the last value for the dictionary. + case useLast + } + + init( + merging keysAndValues: S, + mergingValues strategy: MergeCollisionStrategy + ) + + // other merging overloads + } + ``` + + In use, this overload would look similar to the functional version, but may aid in discoverability: + + ```swift + let firstWins = Dictionary(merging: duplicates, mergingValues: .useFirst) + // ["b": 2, "a": 1] + + let lastWins = Dictionary(merging: duplicates, mergingValues: .useLast) + // ["b": 4, "a": 3] + ``` + +- An earlier version of this proposal included a change to both collections' `remove(at:)` method, such that it would return both the removed element and the next valid index in the iteration sequence. This change lets a programmer write code that would remove elements of a dictionary or a set in place, which can't currently be done with as much efficiency due to index invalidation: + + ```swift + var i = dict.startIndex + while i != dict.endIndex { + if shouldRemove(dict[i]) { + (_, i) = dict.remove(at: i) + } else { + dict.formIndex(after: &i) + } + } + ``` + + This change to `remove(at:)` has been deferred to a later proposal that can better deal with the impact on existing code. + + +## Source compatibility + +All the proposed additions are purely additive and should impose only a minimal source compatibility burden. The addition of same-type returning `filter(_:)` methods to `Dictionary` and `Set` could cause problems if code is relying on the previous behavior of returning an array. For example, the following code would break after this change: + +```swift +let digits = Set(0..<10) +let evens = digits.filter { $0 % 2 == 0 } +functionThatTakesAnArray(evens) +``` + +To mitigate the impact of this on code compiled in Swift 3 mode, `Dictionary` and `Set` will have an additional overload of the `Array`-returning `filter(_:)` that is marked as obsoleted in Swift 4. This will allow Swift 3 code to continue to compile and work as expected: + +```swift +extension Set { + @available(swift, obsoleted: 4) + func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> [Element] + + @available(swift, introduced: 4) + func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> Set +} +``` + +[sandbox]: http://swift.sandbox.bluemix.net/#/repl/58e57b5bbd5eea20992d59e7 From 26c6a340b2ceeedf474759b8ef0cea5b7cad5f68 Mon Sep 17 00:00:00 2001 From: Ian Partridge Date: Thu, 6 Apr 2017 16:03:18 +0100 Subject: [PATCH 0270/4563] Update 0161-key-paths.md --- proposals/0161-key-paths.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0161-key-paths.md b/proposals/0161-key-paths.md index f52264506f..f918c84349 100644 --- a/proposals/0161-key-paths.md +++ b/proposals/0161-key-paths.md @@ -50,7 +50,7 @@ var luke = Person(name: "Luke Skywalker") luke.friends.append(han) // create a key path and use it -let firstFriendsNameKeyPath = \Person.friends[0].name) +let firstFriendsNameKeyPath = \Person.friends[0].name let firstFriend = luke[keyPath: firstFriendsNameKeyPath] // "Han Solo" // or equivalently, with type inferred from context @@ -60,7 +60,7 @@ luke[keyPath: \.friends[0].name] // "Han Solo" luke[keyPath: firstFriendsNameKeyPath] = "A Disreputable Smuggler" // optional properties work too -let bestFriendsNameKeyPath = \Person.bestFriend?.name) +let bestFriendsNameKeyPath = \Person.bestFriend?.name let bestFriendsName = luke[keyPath: bestFriendsNameKeyPath] // nil, if he is the last Jedi ``` From 336e8ba674841e47c431bbfa09bcee217345bee7 Mon Sep 17 00:00:00 2001 From: Itai Ferber Date: Thu, 6 Apr 2017 10:48:54 -0700 Subject: [PATCH 0271/4563] Proposal for Foundation Swift Archival & Serialization API (#639) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Proposal for Swift Archival & Serialization API * Typo fix * Update proposal with swift-evolution feedback * Split Codable into Encodable and Decodable * CodingKey stringValue no longer optional * Add unkeyed container and remove array responsibility from keyed containers * codingKeyContext → codingPath * Fix CodingUserInfoKey example * Typo fix --- .../XXXX-swift-archival-serialization.md | 2227 +++++++++++++++++ 1 file changed, 2227 insertions(+) create mode 100644 proposals/XXXX-swift-archival-serialization.md diff --git a/proposals/XXXX-swift-archival-serialization.md b/proposals/XXXX-swift-archival-serialization.md new file mode 100644 index 0000000000..35c0ca343d --- /dev/null +++ b/proposals/XXXX-swift-archival-serialization.md @@ -0,0 +1,2227 @@ +# Swift Archival & Serialization + +* Proposal: SE-NNNN +* Author(s): [Itai Ferber](https://github.com/itaiferber), [Michael LeHew](https://github.com/mlehew), [Tony Parker](https://github.com/parkera) +* Review Manager: TBD +* Status: **Awaiting review** +* Associated PRs: + * [#8124](https://github.com/apple/swift/pull/8124) + * [#8125](https://github.com/apple/swift/pull/8125) + +## Introduction + +Foundation's current archival and serialization APIs (`NSCoding`, `NSJSONSerialization`, `NSPropertyListSerialization`, etc.), while fitting for the dynamism of Objective-C, do not always map optimally into Swift. This document lays out the design of an updated API that improves the developer experience of performing archival and serialization in Swift. + +Specifically: + +* It aims to provide a solution for the archival of Swift `struct` and `enum` types +* It aims to provide a more type-safe solution for serializing to external formats, such as JSON and plist + +## Motivation + +The primary motivation for this proposal is the inclusion of native Swift `enum` and `struct` types in archival and serialization. Currently, developers targeting Swift cannot participate in `NSCoding` without being willing to abandon `enum` and `struct` types — `NSCoding` is an `@objc` protocol, conformance to which excludes non-`class` types. This is can be limiting in Swift because small `enums` and `structs` can be an idiomatic approach to model representation; developers who wish to perform archival have to either forgo the Swift niceties that constructs like `enums` provide, or provide an additional compatibility layer between their "real" types and their archivable types. + +Secondarily, we would like to refine Foundation's existing serialization APIs (`NSJSONSerialization` and `NSPropertyListSerialization`) to better match Swift's strong type safety. From experience, we find that the conversion from the unstructured, untyped data of these formats into strongly-typed data structures is a good fit for archival mechanisms, rather than taking the less safe approach that 3rd-party JSON conversion approaches have taken (described further in an appendix below). + +We would like to offer a solution to these problems without sacrificing ease of use or type safety. + +## Agenda + +This proposal is the first stage of three that introduce different facets of a whole Swift archival and serialization API: + +1. This proposal describes the basis for this API, focusing on the protocols that users adopt and interface with +2. The next stage will propose specific API for new encoders +3. The final stage will discuss how this new API will interop with `NSCoding` as it is today + +[SE-NNNN](https://github.com/itaiferber/swift-evolution/blob/swift-encoders/proposals/XXXX-swift-encoders.md) provides stages 2 and 3. + +## Proposed solution + +We will be introducing the `Encodable` and `Decodable` protocols, adoption of which will allow end user types to participate in encoding and decoding: + +```swift +// Codable implies Encodable and Decodable +// If all properties are Codable, protocol implementation is automatically generated by the compiler: +public struct Location : Codable { + public let latitude: Double + public let longitude: Double +} + +public enum Animal : Int, Codable { + case chicken = 1 + case dog + case turkey + case cow +} + +public struct Farm : Codable { + public let name: String + public let location: Location + public let animals: [Animal] +} +``` + +With developer participation, we will offer encoders and decoders (described in [SE-NNNN](https://github.com/itaiferber/swift-evolution/blob/swift-encoders/proposals/XXXX-swift-encoders.md), not here) that take advantage of this conformance to offer type-safe serialization of user models: + +```swift +let farm = Farm(name: "Old MacDonald's Farm", + location: Location(latitude: 51.621648, longitude: 0.269273), + animals: [.chicken, .dog, .cow, .turkey, .dog, .chicken, .cow, .turkey, .dog]) +let payload: Data = try JSONEncoder().encode(farm) + +do { + let farm = try JSONDecoder().decode(Farm.self, from: payload) + + // Extracted as user types: + let coordinates = "\(farm.location.latitude, farm.location.longitude)" +} catch { + // Encountered error during deserialization +} +``` + +This gives developers access to their data in a type-safe manner and a recognizable interface. + +## Detailed design + +We will be introducing the following new types: + + * `protocol Encodable` & `protocol Decodable`: Adopted by types to opt into archival. Implementation can be synthesized by the compiler in cases where all properties are also `Encodable` or `Decodable` + * `protocol CodingKey`: Adopted by types used as keys for keyed containers, replacing `String` keys with semantic types. Implementation can be synthesized by the compiler in most cases + * `protocol Encoder`: Adopted by types which can take `Encodable` values and encode them into a native format + * `protocol KeyedEncodingContainerProtocol`: Adopted by types which provide a concrete way to store encoded values by `CodingKey`. Types adopting `Encoder` should provide types conforming to `KeyedEncodingContainerProtocol` to vend + * `struct KeyedEncodingContainer`: A concrete type-erased box for exposing `KeyedEncodingContainerProtocol` types; this is a type consumers of the API interact with directly + * `protocol UnkeyedEncodingContainer`: Adopted by types which provide a concrete way to stored encoded values with no keys. Types adopting `Encoder` should provide types conforming to `UnkeyedEncodingContainer` to vend + * `protocol SingleValueEncodingContainer`: Adopted by types which provide a concrete way to store a single encoded value. Types adopting `Encoder` should provide types conforming to `SingleValueEncodingContainer` to vend + * `protocol Decoder`: Adopted by types which can take payloads in a native format and decode `Decodable` values out of them + * `protocol KeyedDecodingContainerProtocol`: Adopted by types which provide a concrete way to retrieve encoded values from storage by `CodingKey`. Types adopting `Decoder` should provide types conforming to `KeyedDecodingContainerProtocol` to vend + * `struct KeyedDecodingContainer`: A concrete type-erased box for exposing `KeyedDecodingContainerProtocol` types; this is a type consumers of the API interact with directly + * `protocol UnkeyedDecodingContainer`: Adopted by types which provide a concrete way to retrieve encoded values from storage with no keys. Types adopting `Decoder` should provide types conforming to `UnkeyedDecodingContainer` to vend + * `protocol SingleValueDecodingContainer`: Adopted by types which provide a concrete way to retrieve a single encoded value from storage. Types adopting `Decoder` should provide types conforming to `SingleValueDecodingContainer` to vend + * `struct CodingUserInfoKey`: A `String RawRepresentable struct` for representing keys to use in `Encoders`' and `Decoders`' `userInfo` dictionaries + +To support user types, we expose the `Encodable` and `Decodable` protocols: + +```swift +/// Conformance to `Encodable` indicates that a type can encode itself to an external representation. +public protocol Encodable { + /// Encodes `self` into the given encoder. + /// + /// If `self` fails to encode anything, `encoder` will encode an empty keyed container in its place. + /// + /// - parameter encoder: The encoder to write data to. + /// - throws: An error if any values are invalid for `encoder`'s format. + func encode(to encoder: Encoder) throws +} + +/// Conformance to `Decodable` indicates that a type can decode itself from an external representation. +public protocol Decodable { + /// Initializes `self` by decoding from `decoder`. + /// + /// - parameter decoder: The decoder to read data from. + /// - throws: An error if reading from the decoder fails, or if read data is corrupted or otherwise invalid. + init(from decoder: Decoder) throws +} + +/// Conformance to `Codable` indicates that a type can convert itself into and out of an external representation. +public typealias Codable = Encodable & Decodable +``` + +By adopting these protocols, user types opt in to this system. + +Structured types (i.e. types which encode as a collection of properties) encode and decode their properties in a keyed manner. Keys are semantic `String`-convertible `enums` which map properties to encoded names. Keys must conform to the `CodingKey` protocol: + +```swift +/// Conformance to `CodingKey` indicates that a type can be used as a key for encoding and decoding. +public protocol CodingKey { + /// The string to use in a named collection (e.g. a string-keyed dictionary). + var stringValue: String { get } + + /// Initializes `self` from a string. + /// + /// - parameter stringValue: The string value of the desired key. + /// - returns: An instance of `Self` from the given string, or `nil` if the given string does not correspond to any instance of `Self`. + init?(stringValue: String) + + /// The int to use in an indexed collection (e.g. an int-keyed dictionary). + var intValue: Int? { get } + + /// Initializes `self` from an integer. + /// + /// - parameter intValue: The integer value of the desired key. + /// - returns: An instance of `Self` from the given integer, or `nil` if the given integer does not correspond to any instance of `Self`. + init?(intValue: Int) +} +``` + +For performance, where relevant, keys may be `Int`-convertible, and `Encoders` may choose to make use of `Ints` over `Strings` as appropriate. Framework types should provide keys which have both for flexibility and performance across different types of `Encoders`. + +By default, `CodingKey` conformance can be derived for `enums` which have no raw type and no associated values, or `String` or `Int` backing: + +```swift +enum Keys1 : CodingKey { + case a // (stringValue: "a", intValue: nil) + case b // (stringValue: "b", intValue: nil) + + // The compiler automatically generates the following: + var stringValue: String { + switch self { + case .a: return "a" + case .b: return "b" + } + } + + init?(stringValue: String) { + switch stringValue { + case "a": self = .a + case "b": self = .b + default: return nil + } + } + + var intValue: Int? { + return nil + } + + init?(intValue: Int) { + return nil + } +} + +enum Keys2 : String, CodingKey { + case c = "foo" // (stringValue: "foo", intValue: nil) + case d // (stringValue: "d", intValue: nil) + + // stringValue, init?(stringValue:), intValue, and init?(intValue:) are generated by the compiler as well +} + +enum Keys3 : Int, CodingKey { + case e = 4 // (stringValue: "e", intValue: 4) + case f // (stringValue: "f", intValue: 5) + case g = 9 // (stringValue: "g", intValue: 9) + + // stringValue, init?(stringValue:), intValue, and init?(intValue:) are generated by the compiler as well +} +``` + +Coding keys which are not `enum`s, have associated values, or have other raw representations must implement these methods manually. + +In addition to automatic `CodingKey` requirement synthesis for `enums`, `Encodable` & `Decodable` requirements can be automatically synthesized for certain types as well: + +1. Types conforming to `Encodable` whose properties are all `Encodable` get an automatically generated `String`-backed `CodingKey` `enum` mapping properties to case names. Similarly for `Decodable` types whose properties are all `Decodable` +2. Types falling into (1) — and types which manually provide a `CodingKey` `enum` (named `CodingKeys`, directly, or via a `typealias`) whose cases map 1-to-1 to `Encodable`/`Decodable` properties by name — get automatic synthesis of `init(from:)` and `encode(to:)` as appropriate, using those properties and keys +3. Types which fall into neither (1) nor (2) will have to provide a custom key type if needed and provide their own `init(from:)` and `encode(to:)`, as appropriate + +This synthesis can always be overridden by a manual implementation of any protocol requirements. Many types will either allow for automatic synthesis of all of codability (1), or provide a custom key subset and take advantage of automatic method synthesis (2). + +### Encoding and Decoding + +Types which are `Encodable` encode their data into a container provided by their `Encoder`: + +```swift +/// An `Encoder` is a type which can encode values into a native format for external representation. +public protocol Encoder { + /// Returns an encoding container appropriate for holding multiple values keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - returns: A new keyed encoding container. + /// - precondition: May not be called after a prior `self.unkeyedContainer()` call. + /// - precondition: May not be called after a value has been encoded through a previous `self.singleValueContainer()` call. + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer + + /// Returns an encoding container appropriate for holding multiple unkeyed values. + /// + /// - returns: A new empty unkeyed container. + /// - precondition: May not be called after a prior `self.container(keyedBy:)` call. + /// - precondition: May not be called after a value has been encoded through a previous `self.singleValueContainer()` call. + func unkeyedContainer() -> UnkeyedEncodingContainer + + /// Returns an encoding container appropriate for holding a single primitive value. + /// + /// - returns: A new empty single value container. + /// - precondition: May not be called after a prior `self.container(keyedBy:)` call. + /// - precondition: May not be called after a prior `self.unkeyedContainer()` call. + /// - precondition: May not be called after a value has been encoded through a previous `self.singleValueContainer()` call. + func singleValueContainer() -> SingleValueEncodingContainer + + /// The path of coding keys taken to get to this point in encoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } +} + +// Continuing examples from before; below is automatically generated by the compiler if no customization is needed. +public struct Location : Codable { + private enum CodingKeys : CodingKey { + case latitude + case longitude + } + + public func encode(to encoder: Encoder) throws { + // Generic keyed encoder gives type-safe key access: cannot encode with keys of the wrong type. + let container = encoder.container(keyedBy: CodingKeys.self) + + // The encoder is generic on the key -- free key autocompletion here. + try container.encode(latitude, forKey: .latitude) + try container.encode(longitude, forKey: .longitude) + } +} + +public struct Farm : Codable { + private enum CodingKeys : CodingKey { + case name + case location + case animals + } + + public func encode(to encoder: Encoder) throws { + let container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(name, forKey: .name) + try container.encode(location, forKey: .location) + try container.encode(animals, forKey: .animals) + } +} +``` + +Similarly, `Decodable` types initialize from data read from their `Decoder`'s container: + +```swift +/// A `Decoder` is a type which can decode values from a native format into in-memory representations. +public protocol Decoder { + /// Returns the data stored in `self` as represented in a container keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not a keyed container. + func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer + + /// Returns the data stored in `self` as represented in a container appropriate for holding values with no keys. + /// + /// - returns: An unkeyed container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not an unkeyed container. + func unkeyedContainer() throws -> UnkeyedDecodingContainer + + /// Returns the data stored in `self` as represented in a container appropriate for holding a single primitive value. + /// + /// - returns: A single value container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not a single value container. + func singleValueContainer() throws -> SingleValueDecodingContainer + + /// The path of coding keys taken to get to this point in decoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } +} + +// Continuing examples from before; below is automatically generated by the compiler if no customization is needed. +public struct Location : Codable { + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + latitude = try container.decode(Double.self, forKey: .latitude) + longitude = try container.decode(Double.self, forKey: .longitude) + } +} + +public struct Farm : Codable { + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + name = try container.decode(String.self, forKey: .name) + location = try container.decode(Location.self, forKey: .location) + animals = try container.decode([Animal].self, forKey: .animals) + } +} +``` + +### Keyed Containers + +Keyed containers are the primary interface that most `Codable` types interact with for encoding and decoding. Through these, `Codable` types have strongly-keyed access to encoded data by using keys that are semantically correct for the operations they want to express. + +Since semantically incompatible keys will rarely (if ever) share the same key type, it is impossible to mix up key types within the same container (as is possible with `String` keys), and since the type is known statically, keys get autocompletion by the compiler. + +```swift +/// Conformance to `KeyedEncodingContainerProtocol` indicates that a type provides a view into an `Encoder`'s storage and is used to hold the encoded properties of an `Encodable` type in a keyed manner. +/// +/// Encoders should provide types conforming to `KeyedEncodingContainerProtocol` for their format. +public protocol KeyedEncodingContainerProtocol { + associatedtype Key : CodingKey + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: T?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Bool?, forKey key: Key) throws + mutating func encode(_ value: Int?, forKey key: Key) throws + mutating func encode(_ value: Int8?, forKey key: Key) throws + mutating func encode(_ value: Int16?, forKey key: Key) throws + mutating func encode(_ value: Int32?, forKey key: Key) throws + mutating func encode(_ value: Int64?, forKey key: Key) throws + mutating func encode(_ value: UInt?, forKey key: Key) throws + mutating func encode(_ value: UInt8?, forKey key: Key) throws + mutating func encode(_ value: UInt16?, forKey key: Key) throws + mutating func encode(_ value: UInt32?, forKey key: Key) throws + mutating func encode(_ value: UInt64?, forKey key: Key) throws + mutating func encode(_ value: Float?, forKey key: Key) throws + mutating func encode(_ value: Double?, forKey key: Key) throws + mutating func encode(_ value: String?, forKey key: Key) throws + mutating func encode(_ value: Data?, forKey key: Key) throws + + /// Encodes the given object weakly for the given key. + /// + /// For `Encoder`s that implement this functionality, this will only encode the given object and associate it with the given key if it is encoded unconditionally elsewhere in the payload (either previously or in the future). + /// + /// For formats which don't support this feature, the default implementation encodes the given object unconditionally. + /// + /// - parameter object: The object to encode. + /// - parameter key: The key to associate the object with. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encodeWeak(_ object: T?, forKey key: Key) throws + + /// The path of coding keys taken to get to this point in encoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } +} + +/// `KeyedEncodingContainer` is a type-erased box for `KeyedEncodingContainerProtocol` types, similar to `AnyCollection` and `AnyHashable`. This is the type which consumers of the API interact with directly. +public struct KeyedEncodingContainer : KeyedEncodingContainerProtocol { + associatedtype Key = K + + /// Initializes `self` with the given container. + /// + /// - parameter container: The container to hold. + init(_ container: Container) where Container.Key == Key + + // + methods from KeyedEncodingContainerProtocol +} + +/// Conformance to `KeyedDecodingContainerProtocol` indicates that a type provides a view into a `Decoder`'s storage and is used to hold the encoded properties of a `Decodable` type in a keyed manner. +/// +/// Decoders should provide types conforming to `KeyedDecodingContainerProtocol` for their format. +public protocol KeyedDecodingContainerProtocol { + associatedtype Key : CodingKey + + /// All the keys the `Decoder` has for this container. + /// + /// Different keyed containers from the same `Decoder` may return different keys here; it is possible to encode with multiple key types which are not convertible to one another. This should report all keys present which are convertible to the requested type. + var allKeys: [Key] { get } + + /// Returns whether the `Decoder` contains a value associated with the given key. + /// + /// The value associated with the given key may be a null value as appropriate for the data format. + /// + /// - parameter key: The key to search for. + /// - returns: Whether the `Decoder` has an entry for the given key. + func contains(_ key: Key) -> Bool + + /// Decodes a value of the given type for the given key. + /// + /// A default implementation is given for these types which calls into the `decodeIfPresent` implementations below. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `CocoaError.coderValueNotFound` if `self` does not have an entry for the given key or if the value is null. + func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool + func decode(_ type: Int.Type, forKey key: Key) throws -> Int + func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 + func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 + func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 + func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 + func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt + func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 + func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 + func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 + func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 + func decode(_ type: Float.Type, forKey key: Key) throws -> Float + func decode(_ type: Double.Type, forKey key: Key) throws -> Double + func decode(_ type: String.Type, forKey key: Key) throws -> String + func decode(_ type: Data.Type, forKey key: Key) throws -> Data + func decode(_ type: T.Type, forKey key: Key) throws -> T + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool? + func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int? + func decodeIfPresent(_ type: Int8.Type, forKey key: Key) throws -> Int8? + func decodeIfPresent(_ type: Int16.Type, forKey key: Key) throws -> Int16? + func decodeIfPresent(_ type: Int32.Type, forKey key: Key) throws -> Int32? + func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64? + func decodeIfPresent(_ type: UInt.Type, forKey key: Key) throws -> UInt? + func decodeIfPresent(_ type: UInt8.Type, forKey key: Key) throws -> UInt8? + func decodeIfPresent(_ type: UInt16.Type, forKey key: Key) throws -> UInt16? + func decodeIfPresent(_ type: UInt32.Type, forKey key: Key) throws -> UInt32? + func decodeIfPresent(_ type: UInt64.Type, forKey key: Key) throws -> UInt64? + func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float? + func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double? + func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String? + func decodeIfPresent(_ type: Data.Type, forKey key: Key) throws -> Data? + func decodeIfPresent(_ type: T.Type, forKey key: Key) throws -> T? + + /// The path of coding keys taken to get to this point in decoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } +} + +/// `KeyedDecodingContainer` is a type-erased box for `KeyedDecodingContainerProtocol` types, similar to `AnyCollection` and `AnyHashable`. This is the type which consumers of the API interact with directly. +public struct KeyedDecodingContainer : KeyedDecodingContainerProtocol { + associatedtype Key = K + + /// Initializes `self` with the given container. + /// + /// - parameter container: The container to hold. + init(_ container: Container) where Container.Key == Key + + // + methods from KeyedDecodingContainerProtocol +} +``` + +These `encode(_:forKey:)` and `decode(_:forKey:)` overloads give strong, static type guarantees about what is encodable (preventing accidental attempts to encode an invalid type), and provide a list of primitive types which are common to all encoders and decoders that users can rely on. + +When the conditional conformance feature lands in Swift, the ability to express that "a collection of things which are `Codable` is `Codable`" will allow collections (`Array`, `Dictionary`, etc.) to be extended and fall into these overloads as well. + +### Unkeyed Containers + +For some types, when the source and destination of a payload can be guaranteed to agree on the payload layout and format (e.g. in cross-process communication, where both sides agree on the payload format), it may be appropriate to eschew the encoding of keys and encode sequentially, without keys. In this case, a type may choose to make use of an unkeyed container for its properties: + +```swift +/// Conformance to `UnkeyedEncodingContainer` indicates that a type provides a view into an `Encoder`'s storage and is used to hold the encoded properties of an `Encodable` type sequentially, without keys. +/// +/// Encoders should provide types conforming to `UnkeyedEncodingContainer` for their format. +public protocol UnkeyedEncodingContainer { + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: T?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Bool?) throws + mutating func encode(_ value: Int?) throws + mutating func encode(_ value: Int8?) throws + mutating func encode(_ value: Int16?) throws + mutating func encode(_ value: Int32?) throws + mutating func encode(_ value: Int64?) throws + mutating func encode(_ value: UInt?) throws + mutating func encode(_ value: UInt8?) throws + mutating func encode(_ value: UInt16?) throws + mutating func encode(_ value: UInt32?) throws + mutating func encode(_ value: UInt64?) throws + mutating func encode(_ value: Float?) throws + mutating func encode(_ value: Double?) throws + mutating func encode(_ value: String?) throws + mutating func encode(_ value: Data?) throws + + /// Encodes the given object weakly. + /// + /// For `Encoder`s that implement this functionality, this will only encode the given object if it is encoded unconditionally elsewhere in the payload (either previously or in the future). + /// + /// For formats which don't support this feature, the default implementation encodes the given object unconditionally. + /// + /// - parameter object: The object to encode. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encodeWeak(_ object: T?) throws + + /// Encodes the elements of the given sequence. + /// + /// A default implementation of these is given in an extension. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Bool + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Int + // ... + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Data + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element : Encodable + + /// The path of coding keys taken to get to this point in encoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } +} + +/// Conformance to `UnkeyedDecodingContainer` indicates that a type provides a view into a `Decoder`'s storage and is used to hold the encoded properties of a `Decodable` type sequentially, without keys. +/// +/// Decoders should provide types conforming to `UnkeyedDecodingContainer` for their format. +public protocol UnkeyedDecodingContainer { + /// Returns the number of elements (if known) contained within this container. + var count: Int? { get } + + /// Returns whether there are no more elements left to be decoded in the container. + var isAtEnd: Bool { get } + + /// Decodes a value of the given type. + /// + /// A default implementation is given for these types which calls into the `decodeIfPresent` implementations below. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `CocoaError.coderValueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: Bool.Type) throws -> Bool + mutating func decode(_ type: Int.Type) throws -> Int + mutating func decode(_ type: Int8.Type) throws -> Int8 + mutating func decode(_ type: Int16.Type) throws -> Int16 + mutating func decode(_ type: Int32.Type) throws -> Int32 + mutating func decode(_ type: Int64.Type) throws -> Int64 + mutating func decode(_ type: UInt.Type) throws -> UInt + mutating func decode(_ type: UInt8.Type) throws -> UInt8 + mutating func decode(_ type: UInt16.Type) throws -> UInt16 + mutating func decode(_ type: UInt32.Type) throws -> UInt32 + mutating func decode(_ type: UInt64.Type) throws -> UInt64 + mutating func decode(_ type: Float.Type) throws -> Float + mutating func decode(_ type: Double.Type) throws -> Double + mutating func decode(_ type: String.Type) throws -> String + mutating func decode(_ type: Data.Type) throws -> Data + mutating func decode(_ type: T.Type) throws -> T + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Bool.Type) throws -> Bool? + mutating func decodeIfPresent(_ type: Int.Type) throws -> Int? + mutating func decodeIfPresent(_ type: Int8.Type) throws -> Int8? + mutating func decodeIfPresent(_ type: Int16.Type) throws -> Int16? + mutating func decodeIfPresent(_ type: Int32.Type) throws -> Int32? + mutating func decodeIfPresent(_ type: Int64.Type) throws -> Int64? + mutating func decodeIfPresent(_ type: UInt.Type) throws -> UInt? + mutating func decodeIfPresent(_ type: UInt8.Type) throws -> UInt8? + mutating func decodeIfPresent(_ type: UInt16.Type) throws -> UInt16? + mutating func decodeIfPresent(_ type: UInt32.Type) throws -> UInt32? + mutating func decodeIfPresent(_ type: UInt64.Type) throws -> UInt64? + mutating func decodeIfPresent(_ type: Float.Type) throws -> Float? + mutating func decodeIfPresent(_ type: Double.Type) throws -> Double? + mutating func decodeIfPresent(_ type: String.Type) throws -> String? + mutating func decodeIfPresent(_ type: Data.Type) throws -> Data? + mutating func decodeIfPresent(_ type: T.Type) throws -> T? + + /// The path of coding keys taken to get to this point in decoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } +} +``` + +Unkeyed encoding is fragile and generally not appropriate for archival without specific format guarantees, so keyed encoding remains the recommended approach (and is why `CodingKey` `enums` are synthesized by default unless otherwise declined). + +### Single Value Containers + +For other types, an array or dictionary container may not even make sense (e.g. values which are `RawRepresentable` as a single primitive value). Those types may encode and decode directly as a single value, instead of requesting an outer container: + +```swift +/// A `SingleValueEncodingContainer` is a container which can support the storage and direct encoding of a single non-keyed value. +public protocol SingleValueEncodingContainer { + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: Bool) throws + mutating func encode(_ value: Int) throws + mutating func encode(_ value: Int8) throws + mutating func encode(_ value: Int16) throws + mutating func encode(_ value: Int32) throws + mutating func encode(_ value: Int64) throws + mutating func encode(_ value: UInt) throws + mutating func encode(_ value: UInt8) throws + mutating func encode(_ value: UInt16) throws + mutating func encode(_ value: UInt32) throws + mutating func encode(_ value: UInt64) throws + mutating func encode(_ value: Float) throws + mutating func encode(_ value: Double) throws + mutating func encode(_ value: String) throws + mutating func encode(_ value: Data) throws +} + +/// A `SingleValueDecodingContainer` is a container which can support the storage and direct decoding of a single non-keyed value. +public protocol SingleValueDecodingContainer { + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: Bool.Type) throws -> Bool + func decode(_ type: Int.Type) throws -> Int + func decode(_ type: Int8.Type) throws -> Int8 + func decode(_ type: Int16.Type) throws -> Int16 + func decode(_ type: Int32.Type) throws -> Int32 + func decode(_ type: Int64.Type) throws -> Int64 + func decode(_ type: UInt.Type) throws -> UInt + func decode(_ type: UInt8.Type) throws -> UInt8 + func decode(_ type: UInt16.Type) throws -> UInt16 + func decode(_ type: UInt32.Type) throws -> UInt32 + func decode(_ type: UInt64.Type) throws -> UInt64 + func decode(_ type: Float.Type) throws -> Float + func decode(_ type: Double.Type) throws -> Double + func decode(_ type: String.Type) throws -> String + func decode(_ type: Data.Type) throws -> Data +} + +// Continuing example from before; below is automatically generated by the compiler if no customization is needed. +public enum Animal : Int, Codable { + public func encode(to encoder: Encoder) throws { + // Encode as a single value; no keys. + try encoder.singleValueContainer().encode(self.rawValue) + } + + public init(from decoder: Decoder) throws { + // Decodes as a single value; no keys. + let intValue = try decoder.singleValueContainer().decode(Int.self) + if let value = Self(rawValue: intValue) { + self = value + } else { + throw CocoaError.error(.coderReadCorrupt) + } + } +} +``` + +In the example given above, since `Animal` uses a single value container, `[.chicken, .dog, .cow, .turkey, .dog, .chicken, .cow, .turkey, .dog]` would encode directly as `[1, 2, 4, 3, 2, 1, 4, 3, 2]`. + +### Nesting + +In practice, some types may also need to control how data is nested within their container, or potentially nest other containers within their container. Keyed containers allow this by returning nested containers of differing types: + +```swift +// Continuing from before +public protocol KeyedEncodingContainerProtocol { + /// Stores a keyed encoding container for the given key and returns it. + /// + /// - parameter keyType: The key type to use for the container. + /// - parameter key: The key to encode the container for. + /// - returns: A new keyed encoding container. + mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer + + /// Stores an unkeyed encoding container for the given key and returns it. + /// + /// - parameter key: The key to encode the container for. + /// - returns: A new unkeyed encoding container. + mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer +} + +public protocol KeyedDecodingContainerProtocol { + /// Returns the data stored for the given key as represented in a container keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - parameter key: The key that the nested container is associated with. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not a keyed container. + func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer + + /// Returns the data stored for the given key as represented in an unkeyed container. + /// + /// - parameter key: The key that the nested container is associated with. + /// - returns: An unkeyed decoding container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not an unkeyed container. + func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer +} +``` + +This can be common when coding against specific external data representations: + +```swift +// User type for interfacing with a specific JSON API. JSON API expects encoding as {"id": ..., "properties": {"name": ..., "timestamp": ...}}. Swift type differs from encoded type, and encoding needs to match a spec: +struct Record : Codable { + // We care only about these values from the JSON payload + let id: Int + let name: String + let timestamp: Double + + // ... + + private enum Keys : CodingKey { + case id + case properties + } + + private enum PropertiesKeys : CodingKey { + case name + case timestamp + } + + public func encode(to encoder: Encoder) throws { + let container = encoder.container(keyedBy: Keys.self, type: .dictionary) + try container.encode(id, forKey: .id) + + // Set a dictionary for the "properties" key + let nested = container.nestedContainer(keyedBy: PropertiesKeys.self, forKey: .properties) + try nested.encode(name, forKey: .name) + try nested.encode(timestamp, forKey: .timestamp) + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: Keys.self) + id = try container.decode(Int.self, forKey: .id) + + let nested = try container.nestedContainer(keyedBy: PropertiesKeys.self, forKey: .properties) + name = try nested.decode(String.self, forKey: .name) + timestamp = try nested.decode(Double.self, forKey: .timestamp) + } +} +``` + +Unkeyed containers allow for the same types of nesting: + +```swift +// Continuing from before +public protocol UnkeyedEncodingContainer { + /// Encodes a nested container keyed by the given type and returns it. + /// + /// - parameter keyType: The key type to use for the container. + /// - returns: A new keyed encoding container. + mutating func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer + + /// Encodes an unkeyed encoding container and returns it. + /// + /// - returns: A new unkeyed encoding container. + mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer +} + +public protocol UnkeyedDecodingContainer { + /// Decodes a nested container keyed by the given type. + /// + /// - parameter type: The key type to use for the container. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not a keyed container. + mutating func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer + + /// Decodes an unkeyed nested container. + /// + /// - returns: An unkeyed decoding container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not an unkeyed container. + mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer +} +``` + +### Dynamic Context-Based Behavior + +In some cases, types may need context in order to decide on their external representation. Some types may choose a different representation based on the encoding format that they are being read from or written to, and others based on other runtime contextual information. To facilitate this, `Encoders` and `Decoders` expose user-supplied context for consumption: + +```swift +/// Represents a user-defined key for providing context for encoding and decoding. +public struct CodingUserInfoKey : RawRepresentable, Hashable { + typealias RawValue = String + let rawValue: String + init?(rawValue: String) +} + +// Continuing from before: +public protocol Encoder { + /// Any contextual information set by the user for encoding. + var userInfo: [CodingUserInfoKey : Any] { get } +} + +public protocol Decoder { + /// Any contextual information set by the user for decoding. + var userInfo: [CodingUserInfoKey : Any] { get } +} +``` + +Consuming types may then support setting contextual information to inform their encoding and decoding: + +```swift +public struct Person : Encodable { + public static let codingUserInfoKey = CodingUserInfoKey("com.foocorp.person.codingUserInfoKey") + + public struct UserInfo { + let shouldEncodePrivateFields: Bool + // ... + } + + func encode(to encoder: Encoder) throws { + if let context = encoder.userInfo[Person.codingUserInfoKey] as? Person.UserInfo { + if context.shouldEncodePrivateFields { + // Do something special. + } + } + + // Fall back to default. + } +} + +let encoder = ... +encoder.userInfo[Person.codingUserInfoKey] = Person.UserInfo(...) +let data = try encoder.encode(person) +``` + +`Encoders` and `Decoders` may choose to expose contextual information about their configuration as part of the context as well if necessary. + +### Inheritance + +Inheritance in this system is supported much like it is with `NSCoding` — on encoding, objects which inherit from a type that is `Encodable` encode `super` using their encoder, and pass a decoder to `super.init(from:)` on decode if they inherit from a type that is `Decodable`. With the existing `NSCoding` API, this is most often done like so, by convention: + +```objc +- (void)encodeWithCoder:(NSCoder *)encoder { + [super encodeWithCoder:encoder]; + // ... encode properties +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + if ((self = [super initWithCoder:decoder])) { + // ... decode properties + } + + return self; +} +``` + +In practice, this approach means that the properties of `self` and the properties of `super` get encoded into the same container: if `self` encodes values for keys `"a"`, `"b"`, and `"c"`, and `super` encodes `"d"`, `"e"`, and `"f"`, the resulting object is encoded as `{"a": ..., "b": ..., "c": ..., "d": ..., "e": ..., "f": ...}`. This approach has two drawbacks: + +1. Things which `self` encodes may overwrite `super`'s (or vice versa, depending on when `-[super encodeWithCoder:]` is called +2. `self` and `super` may not encode into different container types (e.g. `self` in a sequential fashion, and `super` in a keyed fashion) + +The second point is not an issue for `NSKeyedArchiver`, since all values encode with keys (sequentially coded elements get autogenerated keys). This proposed API, however, allows for `self` and `super` to explicitly request conflicting containers (`.array` and `.dictionary`, which may not be mixed, depending on the data format). + +To remedy both of these points, we adopt a new convention for inheritance-based coding — encoding `super` as a sub-object of `self`: + +```swift +public class MyCodable : SomethingCodable { + public func encode(to encoder: Encoder) throws { + let container = encoder.container(keyedBy: CodingKeys.self) + // ... encode some properties + + // superEncoder() gives `super` a nested container to encode into (for + // a predefined key). + try super.encode(to: container.superEncoder()) + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + // ... decode some properties + + // Allow `super` to decode from the nested container. + try super.init(from: container.superDecoder()) + } +} +``` + +If a shared container is desired, it is still possible to call `super.encode(to: encoder)` and `super.init(from: decoder)`, but we recommend the safer containerized option. + +`superEncoder()` and `superDecoder()` are provided on containers to provide handles to nested containers for `super` to use. + +```swift +// Continuing from before +public protocol KeyedEncodingContainerProtocol { + /// Stores a new nested container for the default `super` key and returns a new `Encoder` instance for encoding `super` into that container. + /// + /// Equivalent to calling `superEncoder(forKey:)` with `Key(stringValue: "super", intValue: 0)`. + /// + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + mutating func superEncoder() -> Encoder + + /// Stores a new nested container for the given key and returns a new `Encoder` instance for encoding `super` into that container. + /// + /// - parameter key: The key to encode `super` for. + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + mutating func superEncoder(forKey key: Key) -> Encoder +} + +public protocol KeyedDecodingContainerProtocol { + /// Returns a `Decoder` instance for decoding `super` from the container associated with the default `super` key. + /// + /// Equivalent to calling `superDecoder(forKey:)` with `Key(stringValue: "super", intValue: 0)`. + /// + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `CocoaError.coderValueNotFound` if `self` does not have an entry for the default `super` key, or if the stored value is null. + func superDecoder() throws -> Decoder + + /// Returns a `Decoder` instance for decoding `super` from the container associated with the given key. + /// + /// - parameter key: The key to decode `super` for. + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `CocoaError.coderValueNotFound` if `self` does not have an entry for the given key, or if the stored value is null. + func superDecoder(forKey key: Key) throws -> Decoder +} + +public protocol UnkeyedEncodingContainer { + /// Encodes a nested container and returns an `Encoder` instance for encoding `super` into that container. + /// + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + mutating func superEncoder() -> Encoder +} + +public protocol UnkeyedDecodingContainer { + /// Decodes a nested container and returns a `Decoder` instance for decoding `super` from that container. + /// + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `CocoaError.coderValueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func superDecoder() throws -> Decoder +} +``` + +### Primitive `Codable` Conformance + +The encoding container types offer overloads for working with and processing the API's primitive types (`String`, `Int`, `Double`, etc.). However, for ease of implementation (both in this API and others), it can be helpful for these types to conform to `Codable` themselves. Thus, along with these overloads, we will offer `Codable` conformance on these types: + +```swift +extension Bool : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Bool.self) + } + + public func encode(to encoder: Encoder) throws { + try encoder.singleValueContainer().encode( self) + } +} + +// Repeat for others... +``` + +This conformance allows one to write functions which accept `Codable` types without needing specific overloads for the fifteen primitive types as well. + +Since Swift's function overload rules prefer more specific functions over generic functions, the specific overloads are chosen where possible (e.g. `encode("Hello, world!", forKey: .greeting)` will choose `encode(_: String, forKey: Key)` over `encode(_: T, forKey: Key)`). + +#### Additional Extensions + +Along with the primitive `Codable` conformance above, extensions on `Codable` `RawRepresentable` types whose `RawValue` is a primitive types will provide default implementations for encoding and decoding: + +```swift +public extension RawRepresentable where RawValue == Bool, Self : Codable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw CocoaError.error(.coderReadCorrupt) + } + + self = value + } + + public func encode(to encoder: Encoder) throws { + try encoder.singleValueContainer().encode(self.rawValue) + } +} + +// Repeat for others... +``` + +This allows for trivial `Codable` conformance of `enum` types (and manual `RawRepresentable` implementations) with primitive backing. + +## Source compatibility + +This proposal is additive — existing code will not have to change due to this API addition. This implementation can be made available in both Swift 4 and the Swift 3 compatibility mode. + +## Effect on ABI stability + +The addition of this API will not be an ABI-breaking change. However, this will add limitations for changes in future versions of Swift, as parts of the API will have to remain unchanged between versions of Swift (barring some additions, discussed below). + +## Effect on API resilience + +Much like new API added to the standard library, once added, many changes to this API will be ABI- and source-breaking changes. In particular, changes which change the types or names of methods or arguments, add required methods on protocols or classes, or remove supplied default implementations will break client behavior. + +The following types may not have methods added to them without providing default implementations: + +* `Encodable` +* `Decodable` +* `CodingKey` +* `Encoder` +* `KeyedEncodingContainerProtocol` + * `KeyedEncodingContainer` +* `UnkeyedEncodingContainer` +* `SingleValueEncodingContainer` +* `Decoder` +* `KeyedDecodingContainerProtocol` + * `KeyedDecodingContainer` +* `UnkeyedEncodingContainer` +* `SingleValueDecodingContainer` + +Various extensions to Swift primitive types (`Bool`, `Int`, `Double`, etc.) and to `RawRepresentable` types (`where RawValue == Bool`, `== Int`, `== Double`, etc.) may also not be removed. + +In general, changes to the proposed types will be restricted as described in the [library evolution document](https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst) in the Swift repository. + +## Alternatives considered + +The following are a few of the more notable approaches considered for the problem: + +1. Leverage the existing `NSCoding` implementation by adding support for `struct` and `enum` types, either through `NSCoding` itself, or through a similar protocol. + + * Although technically feasible, this can feel like a "missed opportunity" for offering something better tuned for Swift. This approach would also not offer any additional integration with `JSONSerialization` and `PropertyListSerialization`, unless JSON and plist archivers were added to offer support. + +2. The following type-erased, declarative approach: + + ```swift + // Similar hack to AnyHashable; these wrap values which have not yet been + // encoded, or not yet decoded. + struct AnyEncodable { ... } + struct AnyDecodable { ... } + + protocol CodingPrimitive {} + protocol PrimitiveCodable { /* same as above */ } + + protocol OrderedCodable { + init(from: [AnyDecodable]) throws + var encoded: [AnyEncodable] { get } + } + + protocol KeyedCodable { + init(from: [String: AnyDecodable]) throws + var encoded: [String: AnyEncodable] { get } + } + + // Same as above + protocol OrderedEncoder { ... } + protocol OrderedDecoder { ... } + protocol KeyedEncoder { ... } + protocol KeyedDecoder { ... } + + // Sample: + struct Location: OrderedCodable { + let latitude: Double + let longitude: Double + + init(from array: [AnyDecodable]) throws { + guard array.count == 2 else { /* throw */ } + + // These `.as()` calls perform the actual decoding, and fail by + // throwing an error. + let latitude = try array[0].as(Double.self) + let longitude = try array[1].as(Double.self) + try self.init(latitutde: latitude, longitude: longitude) + } + + var encoded: [AnyEncodable] { + // With compiler support, AnyEncodable() can be automatic. + return [AnyEncodable(latitude), AnyEncodable(longitude)] + } + } + + struct Farm: KeyedCodable { + let name: String + let location: Location + let animals: [Animal] + + init(from dictionary: [String: AnyDecodable]) throws { + guard let name = dictionary["name"], + let location = dictionary["location"], + let animals = dictionary["animals"] else { /* throw */ } + + self.name = try name.as(String.self) + self.location = try location.as(Location.self) + self.animals = try animals.asArrayOf(Animal.self) + } + + var encoded: [String: AnyEncodable] { + // Similarly, AnyEncodable() should go away. + return ["name": AnyEncodable(name), + "location": AnyEncodable(location), + "animals": AnyEncodable(animals)] + } + } + ``` + + Although the more declarative nature of this approach can be appealing, this suffers from the same problem that `JSONSerialization` currently does: as-casting. Getting an intermediate type-erased value requires casting to get a "real" value out. Doing this with an `as?`-cast requires compiler support to interject code to decode values of a given type out of their type-erased containers (similar to what happens today with `AnyHashable`). If the user requests a value of a different type than what is stored, however, the `as?`-cast will fail by returning `nil` — there is no meaningful way to report the failure. Getting the code to `throw` in cases like this requires methods on `AnyDecodable` (as shown above), but these can be confusing (when should you use `.as()` and when should you use `as?`?). + + Modifications can be made to improve this: + + ```swift + protocol OrderedCodable { + // AnyDecodable can wrap anything, including [AnyDecodable]; unwrapping + // these can be tedious, so we want to give default implementations + // that do this. + // Default implementations for these are given in terms of the + // initializer below. + init?(from: AnyDecodable?) throws + init(from: AnyDecodable) throws + + init(from: [AnyDecodable]) throws + var encoded: [AnyEncodable] { get } + } + + protocol KeyedCodable { + // AnyDecodable can wrap anything, including [String: AnyDecodable]; + // unwrapping these can be tedious, so we want to give default + // implementations that do this. + // Default implementations for these are given in terms of the + // initializer below. + init?(from: AnyDecodable?) throws + init(from: AnyDecodable) throws + + init(from: [String: AnyDecodable]) throws + var encoded: [String: AnyEncodable] { get } + } + + // Sample: + struct Location: OrderedCodable { + // ... + init(from array: [AnyDecodable]) throws { + guard array.count == 2 else { /* throw */ } + let latitude = try Double(from: array[0]) + let longitude = try Double(from: array[1]) + try self.init(latitude: latitude, longitude: longitude) + } + // ... + } + + struct Farm: KeyedCodable { + // ... + init(from dictionary: [String: AnyDecodable]) throws { + guard let name = try String(from: dictionary["name"]), + let Location = try Location(from: dictionary["location"]) + let animals = try [Animal](from: dictionary["animals"]) else { + /* throw */ + } + + self.name = name + self.location = location + self.animals = animals + } + // ... + } + ``` + + By providing the new initializer methods, we can perform type casting via initialization, rather than by explicit casts. This pushes the `.as()` calls into the Swift primitives (`CodingPrimitive`s, `Array`, `Dictionary`), hiding them from end users. However, this has a different problem, namely that by offering the same type-erased initializers, `OrderedCodable` and `KeyedCodable` now conflict, and it is impossible to conform to both. + + The declarative benefits here are not enough to outweigh the fact that this does not effectively remove the need to `as?`-cast. + +3. The following approach, which relies on compiler code generation: + + ```swift + protocol Codable { + /// `EncodedType` is an intermediate representation of `Self` -- it has + /// the properties from `Self` that need to be archived and unarchived + /// (and performs that archival work), but represents at type that is + /// not yet domain-validated like `self` is. + associatedtype EncodedType: CodingRepresentation + init(from encoded: EncodedType) + var encoded: EncodedType { get } + } + + protocol CodingPrimitive {} + protocol CodingRepresentation {} + protocol PrimitiveCodingRepresentation: CodingRepresentation { + /* Similar to PrimitiveCodable above */ + } + + protocol OrderedCodingRepresentation: CodingRepresentation { + /* Similar to OrderedCodable above */ + } + + protocol KeyedCodingRepresentation: CodingRepresentation { + /* Similar to KeyedCodable above */ + } + + // Sample: + struct Location : Codable { + let latitude: Double + let longitude: Double + + // --------------------------------------------------------------------- + // Ideally, the following could be generated by the compiler (in simple + // cases; developers can choose to implement subsets of the following + // code based on where they might need to perform customizations. + init(from encoded: Encoded) throws { + latitude = encoded.latitude + longitude = encoded.longitude + } + + var encoded: Encoded { + return Encoded(self) + } + + // Keyed coding is the default generated by the compiler; consumers who + // want OrderedCodingRepresentation need to provide their own encoded + // type. + struct Encoded: OrderedCodingRepresentation { + let latitude: String + let longitude: String + + init(_ location: Location) { + latitude = location.latitude + longitude = location.longitude + } + + init(from: KeyedDecoder) throws { ... } + func encode(to: KeyedEncoder) { ... } + } + // --------------------------------------------------------------------- + } + ``` + + This approach separates encoding and decoding into constituent steps: + + 1. Converting `self` into a representation fit for encoding (`EncodedType`, particularly if `EncodedType` has different properties from `Self`) + 2. Converting that representation into data (`encode(into:)`) + 3. Converting arbitrary bytes into validated types (`EncodedType.init(from:)`) + 4. Converting validated data and types into a domain-validated value (`Self.init(from:)`). + + These steps can be generated by the compiler in simple cases, with gradations up to the developer providing implementations for all of these. With this approach, it would be possible to: + + 1. Have a type where all code generation is left to the compiler + 2. Have a type where `EncodedType` is autogenerated, but the user implements `init(from:)` (allowing for custom domain validation on decode) or `var encoded`, or both + 3. Have a type where the user supplies `EncodedType`, `Self.init(from:)`, and `var encoded`, but the compiler generates `EncodedType.init(from:)` and `EncodedType.encode(into:)`. This allows the user to control what properties `EncodedType` has (or control its conformance to one of the `CodingRepresentation` types) without having to perform the actual `encode` and `decode` calls + 4. Have a type where the user supplies everything, giving them full control of encoding and decoding (for implementing archive versioning and other needs) + + While cases 1 and 2 save on boilerplate, types which need to be customized have significantly more boilerplate to write by hand. + +4. The following approach, which delineates between keyed encoding (with `String` keys) and ordered encoding (this is the approach proposed in v1 and v2 of this proposal): + + ```swift + protocol PrimitiveCodable { + associatedtype Atom: CodingAtom + var atomValue: Atom { get } + init(atomValue value: Atom) + } + + protocol OrderedCodable { + init(from decoder: OrderedDecoder) throws + func encode(into encoder: OrderedEncoder) + } + + protocol KeyedCodable { + init(from decoder: KeyedDecoder) throws + func encode(into encoder: KeyedEncoder) + } + + protocol OrderedEncoder { + func encode(_ value: Value?) where Value: CodingAtom + func encode(_ value: Value?) where Value: PrimitiveCodable + func encode(_ value: Value?) where Value: OrderedCodable + func encode(_ value: Value?) where Value: KeyedCodable + func encode(_ value: Value?) where Value: OrderedCodable & KeyedCodable + } + + protocol OrderedDecoder { + var count: Int { get } + func decode(_ type: Value.Type) throws -> Value? where Value: CodingAtom + func decode(_ type: Value.Type) throws -> Value? where Value: PrimitiveCodable + func decode(_ type: Value.Type) throws -> Value? where Value: OrderedCodable + func decode(_ type: Value.Type) throws -> Value? where Value: KeyedCodable + func decode(_ type: Value.Type) throws -> Value? where Value: OrderedCodable & KeyedCodable + } + + protocol KeyedEncoder { + func encode(_ value: Value?, forKey key: String) where Value: CodingPrimitive + func encode(_ value: Value?, forKey key: String) where Value: PrimitiveCodable + func encode(_ value: Value?, forKey key: String) where Value: OrderedCodable + func encode(_ value: Value?, forKey key: String) where Value: KeyedCodable + func encode(_ value: Value?, forKey key: String) where Value: OrderedCodable & KeyedCodable + } + + protocol KeyedDecoder { + var allKeys: [String] { get } + func hasValue(forKey key: String) -> Bool + func decode(_ type: Value.Type, forKey key: String) throws -> Value? where Value: CodingPrimitive + func decode(_ type: Value.Type, forKey key: String) throws -> Value? where Value: PrimitiveCodable + func decode(_ type: Value.Type, forKey key: String) throws -> Value? where Value: OrderedCodable + func decode(_ type: Value.Type, forKey key: String) throws -> Value? where Value: KeyedCodable + func decode(_ type: Value.Type, forKey key: String) throws -> Value? where Value: OrderedCodable & KeyedCodable + } + ``` + + Although this semantically separates between different types of encoding, the multiple protocols can be confusing, and it is not immediately apparent which to adopt and use. This also specifically calls out a difference between string-keyed and non-keyed coding, which is unnecessary. + +5. A closure-based version of the current approach which scopes keyed encoders/decoders to call sites via closures: + + ```swift + protocol Encoder { + func encode(as value: Bool) throws + // ... + + func with(keys type: Key.Type, _ block: (KeyedEncoder) throws -> Void) rethrows + // ... + } + + internal struct Record : Codable { + let id: Int + let name: String + let timestamp: Double + + // ... + + public func encode(into encoder: Encoder) throws { + try encoder.with(keys: Keys.self) { keyedEncode in + try keyedEncode.encode(id, forKey: .id) + + try keyedEncode.encode(.dictionary, forKey: .properties, keys: PropertiesKeys.self) { properties in + try properties.encode(name, forKey: .name) + try properties.encode(timestamp, forKey: .timestamp) + } + } + } + } + ``` + + However, this cannot currently be applied to decoding: + + ```swift + public init(from decoder: Decoder) throws { + // This closure implicitly references self. Since Swift has no + // guarantees that this closure will get called exactly once, self must + // be fully initialized before this call. + // + // This would require all instance variables to be vars with default + // values. + try decoder.with(keys: Keys.self) { keyedDecoder in + id = try keyedDecoder.decode(Int.self, forKey: .id) + // ... + } + } + ``` + + Although it is not currently possible to initialize `self` within a closure in Swift, this may be added in the future as annotations make these guarantees possible. + +6. A previous approach similar to the current approach with single value encode calls available directly on `Encoder`, and a `KeyedEncoder` type instead of `KeyedEncodingContainer`: + + ```swift + public protocol Encoder { + func keyed(by: Key.Type) throws -> KeyedEncoder + + func encode(as: Bool) throws + func encode(as: Int) throws + func encode(as: Int8) throws + func encode(as: Int16) throws + func encode(as: Int32) throws + // ... + } + + public class KeyedEncoder { + // Identical to KeyedEncodingContainer + } + ``` + +# Appendix + +## `JSONSerialization` Friction and Third-Party Solutions (Motivation) + +The following example usage of `JSONSerialization` is taken from the README of [SwiftyJSON](https://github.com/swiftyjson/swiftyjson), a third-party library that many developers use to interface with JSON models: + +```swift +if let statusesArray = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]], + let user = statusesArray[0]["user"] as? [String: Any], + let username = user["name"] as? String { + // Finally we got the username +} +``` + +SwiftyJSON attempts to elide the verbosity of casting by offering the following solution instead: + +```swift +let json = JSON(data: dataFromNetworking) +if let userName = json[0]["user"]["name"].string { + // Now you got your value +} +``` + +This friction is not necessarily a design flaw in the API, simply a truth of interfacing between JavaScript and JSON's generally untyped, unstructured contents, and Swift's strict typing. Some libraries, like SwiftyJSON, do this at the cost of type safety; others, like ObjectMapper and Argo below, maintain type safety by offering archival functionality for JSON types: + +```swift +// Taken from https://github.com/Hearst-DD/ObjectMapper +class User: Mappable { + var username: String? + var age: Int? + var weight: Double! + var array: [AnyObject]? + var dictionary: [String : AnyObject] = [:] + var bestFriend: User? // Nested User object + var friends: [User]? // Array of Users + var birthday: NSDate? + + required init?(map: Map) { + + } + + // Mappable + func mapping(map: Map) { + username <- map["username"] + age <- map["age"] + weight <- map["weight"] + array <- map["arr"] + dictionary <- map["dict"] + bestFriend <- map["best_friend"] + friends <- map["friends"] + birthday <- (map["birthday"], DateTransform()) + } +} + +struct Temperature: Mappable { + var celsius: Double? + var fahrenheit: Double? + + init?(map: Map) { + + } + + mutating func mapping(map: Map) { + celsius <- map["celsius"] + fahrenheit <- map["fahrenheit"] + } +} +``` + +or the more functional + +```swift +// Taken from https://github.com/thoughtbot/Argo +struct User { + let id: Int + let name: String + let email: String? + let role: Role + let companyName: String + let friends: [User] +} + +extension User: Decodable { + static func decode(j: JSON) -> Decoded { + return curry(User.init) + <^> j <| "id" + <*> j <| "name" + <*> j <|? "email" // Use ? for parsing optional values + <*> j <| "role" // Custom types that also conform to Decodable just work + <*> j <| ["company", "name"] // Parse nested objects + <*> j <|| "friends" // parse arrays of objects + } +} + +// Wherever you receive JSON data: +let json: Any? = try? NSJSONSerialization.JSONObjectWithData(data, options: []) +if let j: Any = json { + let user: User? = decode(j) +} +``` + +There are tradeoffs made here as well. ObjectMapper requires that all of your properties be optional, while Argo relies on a vast collection of custom operators and custom curried initializer functions to do its work. (While not shown in the snippet above, `User.init` code in reality is effectively implemented as `User.init(id)(name)(email)(role)(companyName)(friends)`.) + +We would like to provide a solution that skirts neither type safety, nor ease-of-use and -implementation. + +## Unabridged API + +```swift +/// Conformance to `Encodable` indicates that a type can encode itself to an external representation. +public protocol Encodable { + /// Encodes `self` into the given encoder. + /// + /// If `self` fails to encode anything, `encoder` will encode an empty keyed container in its place. + /// + /// - parameter encoder: The encoder to write data to. + /// - throws: An error if any values are invalid for `encoder`'s format. + func encode(to encoder: Encoder) throws +} + +/// Conformance to `Decodable` indicates that a type can decode itself from an external representation. +public protocol Decodable { + /// Initializes `self` by decoding from `decoder`. + /// + /// - parameter decoder: The decoder to read data from. + /// - throws: An error if reading from the decoder fails, or if read data is corrupted or otherwise invalid. + init(from decoder: Decoder) throws +} + +/// Conformance to `Codable` indicates that a type can convert itself into and out of an external representation. +public typealias Codable = Encodable & Decodable + +/// Conformance to `CodingKey` indicates that a type can be used as a key for encoding and decoding. +public protocol CodingKey { + /// The string to use in a named collection (e.g. a string-keyed dictionary). + var stringValue: String { get } + + /// Initializes `self` from a string. + /// + /// - parameter stringValue: The string value of the desired key. + /// - returns: An instance of `Self` from the given string, or `nil` if the given string does not correspond to any instance of `Self`. + init?(stringValue: String) + + /// The int to use in an indexed collection (e.g. an int-keyed dictionary). + var intValue: Int? { get } + + /// Initializes `self` from an integer. + /// + /// - parameter intValue: The integer value of the desired key. + /// - returns: An instance of `Self` from the given integer, or `nil` if the given integer does not correspond to any instance of `Self`. + init?(intValue: Int) +} + +/// An `Encoder` is a type which can encode values into a native format for external representation. +public protocol Encoder { + /// The path of coding keys taken to get to this point in encoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// Any contextual information set by the user for encoding. + var userInfo: [CodingUserInfoKey : Any] { get } + + /// Returns an encoding container appropriate for holding multiple values keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - returns: A new keyed encoding container. + /// - precondition: May not be called after a prior `self.unkeyedContainer()` call. + /// - precondition: May not be called after a value has been encoded through a previous `self.singleValueContainer()` call. + func container(keyedBy type: Key.Type) -> KeyedEncodingContainer + + /// Returns an encoding container appropriate for holding multiple unkeyed values. + /// + /// - returns: A new empty unkeyed container. + /// - precondition: May not be called after a prior `self.container(keyedBy:)` call. + /// - precondition: May not be called after a value has been encoded through a previous `self.singleValueContainer()` call. + func unkeyedContainer() -> UnkeyedEncodingContainer + + /// Returns an encoding container appropriate for holding a single primitive value. + /// + /// - returns: A new empty single value container. + /// - precondition: May not be called after a prior `self.container(keyedBy:)` call. + /// - precondition: May not be called after a prior `self.unkeyedContainer()` call. + /// - precondition: May not be called after a value has been encoded through a previous `self.singleValueContainer()` call. + func singleValueContainer() -> SingleValueEncodingContainer +} + +/// A `Decoder` is a type which can decode values from a native format into in-memory representations. +public protocol Decoder { + /// The path of coding keys taken to get to this point in decoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// Any contextual information set by the user for decoding. + var userInfo: [CodingUserInfoKey : Any] { get } + + /// Returns the data stored in `self` as represented in a container keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not a keyed container. + func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer + + /// Returns the data stored in `self` as represented in a container appropriate for holding values with no keys. + /// + /// - returns: An unkeyed container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not an unkeyed container. + func unkeyedContainer() throws -> UnkeyedDecodingContainer + + /// Returns the data stored in `self` as represented in a container appropriate for holding a single primitive value. + /// + /// - returns: A single value container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not a single value container. + func singleValueContainer() throws -> SingleValueDecodingContainer +} + +/// Conformance to `KeyedEncodingContainerProtocol` indicates that a type provides a view into an `Encoder`'s storage and is used to hold the encoded properties of an `Encodable` type in a keyed manner. +/// +/// Encoders should provide types conforming to `KeyedEncodingContainerProtocol` for their format. +public protocol KeyedEncodingContainerProtocol { + associatedtype Key : CodingKey + + /// The path of coding keys taken to get to this point in encoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: T?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Bool?, forKey key: Key) throws + mutating func encode(_ value: Int?, forKey key: Key) throws + mutating func encode(_ value: Int8?, forKey key: Key) throws + mutating func encode(_ value: Int16?, forKey key: Key) throws + mutating func encode(_ value: Int32?, forKey key: Key) throws + mutating func encode(_ value: Int64?, forKey key: Key) throws + mutating func encode(_ value: UInt?, forKey key: Key) throws + mutating func encode(_ value: UInt8?, forKey key: Key) throws + mutating func encode(_ value: UInt16?, forKey key: Key) throws + mutating func encode(_ value: UInt32?, forKey key: Key) throws + mutating func encode(_ value: UInt64?, forKey key: Key) throws + mutating func encode(_ value: Float?, forKey key: Key) throws + mutating func encode(_ value: Double?, forKey key: Key) throws + mutating func encode(_ value: String?, forKey key: Key) throws + mutating func encode(_ value: Data?, forKey key: Key) throws + + /// Encodes the given object weakly for the given key. + /// + /// For `Encoder`s that implement this functionality, this will only encode the given object and associate it with the given key if it is encoded unconditionally elsewhere in the payload (either previously or in the future). + /// + /// For formats which don't support this feature, the default implementation encodes the given object unconditionally. + /// + /// - parameter object: The object to encode. + /// - parameter key: The key to associate the object with. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encodeWeak(_ object: T?, forKey key: Key) throws + + /// Stores a keyed encoding container for the given key and returns it. + /// + /// - parameter keyType: The key type to use for the container. + /// - parameter key: The key to encode the container for. + /// - returns: A new keyed encoding container. + mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer + + /// Stores an unkeyed encoding container for the given key and returns it. + /// + /// - parameter key: The key to encode the container for. + /// - returns: A new unkeyed encoding container. + mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer + + /// Stores a new nested container for the default `super` key and returns a new `Encoder` instance for encoding `super` into that container. + /// + /// Equivalent to calling `superEncoder(forKey:)` with `Key(stringValue: "super", intValue: 0)`. + /// + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + mutating func superEncoder() -> Encoder + + /// Stores a new nested container for the given key and returns a new `Encoder` instance for encoding `super` into that container. + /// + /// - parameter key: The key to encode `super` for. + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + mutating func superEncoder(forKey key: Key) -> Encoder +} + +/// `KeyedEncodingContainer` is a type-erased box for `KeyedEncodingContainerProtocol` types, similar to `AnyCollection` and `AnyHashable`. This is the type which consumers of the API interact with directly. +public struct KeyedEncodingContainer : KeyedEncodingContainerProtocol { + associatedtype Key = K + + /// Initializes `self` with the given container. + /// + /// - parameter container: The container to hold. + init(_ container: Container) where Container.Key == Key + + /// The path of coding keys taken to get to this point in encoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: T?, forKey key: Key) throws + + /// Encodes the given value for the given key. + /// + /// - parameter value: The value to encode. + /// - parameter key: The key to associate the value with. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Bool?, forKey key: Key) throws + mutating func encode(_ value: Int?, forKey key: Key) throws + mutating func encode(_ value: Int8?, forKey key: Key) throws + mutating func encode(_ value: Int16?, forKey key: Key) throws + mutating func encode(_ value: Int32?, forKey key: Key) throws + mutating func encode(_ value: Int64?, forKey key: Key) throws + mutating func encode(_ value: UInt?, forKey key: Key) throws + mutating func encode(_ value: UInt8?, forKey key: Key) throws + mutating func encode(_ value: UInt16?, forKey key: Key) throws + mutating func encode(_ value: UInt32?, forKey key: Key) throws + mutating func encode(_ value: UInt64?, forKey key: Key) throws + mutating func encode(_ value: Float?, forKey key: Key) throws + mutating func encode(_ value: Double?, forKey key: Key) throws + mutating func encode(_ value: String?, forKey key: Key) throws + mutating func encode(_ value: Data?, forKey key: Key) throws + + /// Encodes the given object weakly for the given key. + /// + /// For `Encoder`s that implement this functionality, this will only encode the given object and associate it with the given key if it is encoded unconditionally elsewhere in the payload (either previously or in the future). + /// + /// For formats which don't support this feature, the default implementation encodes the given object unconditionally. + /// + /// - parameter object: The object to encode. + /// - parameter key: The key to associate the object with. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encodeWeak(_ object: T?, forKey key: Key) throws + + /// Stores a keyed encoding container for the given key and returns it. + /// + /// - parameter keyType: The key type to use for the container. + /// - parameter key: The key to encode the container for. + /// - returns: A new keyed encoding container. + mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer + + /// Stores an unkeyed encoding container for the given key and returns it. + /// + /// - parameter key: The key to encode the container for. + /// - returns: A new unkeyed encoding container. + mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer + + /// Stores a new nested container for the default `super` key and returns a new `Encoder` instance for encoding `super` into that container. + /// + /// Equivalent to calling `superEncoder(forKey:)` with `Key(stringValue: "super", intValue: 0)`. + /// + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + mutating func superEncoder() -> Encoder + + /// Stores a new nested container for the given key and returns a new `Encoder` instance for encoding `super` into that container. + /// + /// - parameter key: The key to encode `super` for. + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + mutating func superEncoder(forKey key: Key) -> Encoder +} + +/// Conformance to `KeyedDecodingContainerProtocol` indicates that a type provides a view into a `Decoder`'s storage and is used to hold the encoded properties of a `Decodable` type in a keyed manner. +/// +/// Decoders should provide types conforming to `KeyedDecodingContainerProtocol` for their format. +public protocol KeyedDecodingContainerProtocol { + associatedtype Key : CodingKey + + /// The path of coding keys taken to get to this point in decoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// All the keys the `Decoder` has for this container. + /// + /// Different keyed containers from the same `Decoder` may return different keys here; it is possible to encode with multiple key types which are not convertible to one another. This should report all keys present which are convertible to the requested type. + var allKeys: [Key] { get } + + /// Returns whether the `Decoder` contains a value associated with the given key. + /// + /// The value associated with the given key may be a null value as appropriate for the data format. + /// + /// - parameter key: The key to search for. + /// - returns: Whether the `Decoder` has an entry for the given key. + func contains(_ key: Key) -> Bool + + /// Decodes a value of the given type for the given key. + /// + /// A default implementation is given for these types which calls into the `decodeIfPresent` implementations below. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `CocoaError.coderValueNotFound` if `self` does not have an entry for the given key or if the value is null. + func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool + func decode(_ type: Int.Type, forKey key: Key) throws -> Int + func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 + func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 + func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 + func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 + func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt + func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 + func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 + func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 + func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 + func decode(_ type: Float.Type, forKey key: Key) throws -> Float + func decode(_ type: Double.Type, forKey key: Key) throws -> Double + func decode(_ type: String.Type, forKey key: Key) throws -> String + func decode(_ type: Data.Type, forKey key: Key) throws -> Data + func decode(_ type: T.Type, forKey key: Key) throws -> T + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool? + func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int? + func decodeIfPresent(_ type: Int8.Type, forKey key: Key) throws -> Int8? + func decodeIfPresent(_ type: Int16.Type, forKey key: Key) throws -> Int16? + func decodeIfPresent(_ type: Int32.Type, forKey key: Key) throws -> Int32? + func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64? + func decodeIfPresent(_ type: UInt.Type, forKey key: Key) throws -> UInt? + func decodeIfPresent(_ type: UInt8.Type, forKey key: Key) throws -> UInt8? + func decodeIfPresent(_ type: UInt16.Type, forKey key: Key) throws -> UInt16? + func decodeIfPresent(_ type: UInt32.Type, forKey key: Key) throws -> UInt32? + func decodeIfPresent(_ type: UInt64.Type, forKey key: Key) throws -> UInt64? + func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float? + func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double? + func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String? + func decodeIfPresent(_ type: Data.Type, forKey key: Key) throws -> Data? + func decodeIfPresent(_ type: T.Type, forKey key: Key) throws -> T? + + /// Returns the data stored for the given key as represented in a container keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - parameter key: The key that the nested container is associated with. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not a keyed container. + func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer + + /// Returns the data stored for the given key as represented in an unkeyed container. + /// + /// - parameter key: The key that the nested container is associated with. + /// - returns: An unkeyed decoding container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not an unkeyed container. + func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer + + /// Returns a `Decoder` instance for decoding `super` from the container associated with the default `super` key. + /// + /// Equivalent to calling `superDecoder(forKey:)` with `Key(stringValue: "super", intValue: 0)`. + /// + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `CocoaError.coderValueNotFound` if `self` does not have an entry for the default `super` key, or if the stored value is null. + func superDecoder() throws -> Decoder + + /// Returns a `Decoder` instance for decoding `super` from the container associated with the given key. + /// + /// - parameter key: The key to decode `super` for. + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `CocoaError.coderValueNotFound` if `self` does not have an entry for the given key, or if the stored value is null. + func superDecoder(forKey key: Key) throws -> Decoder +} + +/// `KeyedDecodingContainer` is a type-erased box for `KeyedDecodingContainerProtocol` types, similar to `AnyCollection` and `AnyHashable`. This is the type which consumers of the API interact with directly. +public struct KeyedDecodingContainer : KeyedDecodingContainerProtocol { + associatedtype Key = K + + /// Initializes `self` with the given container. + /// + /// - parameter container: The container to hold. + init(_ container: Container) where Container.Key == Key + + /// The path of coding keys taken to get to this point in decoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// All the keys the `Decoder` has for this container. + /// + /// Different keyed containers from the same `Decoder` may return different keys here; it is possible to encode with multiple key types which are not convertible to one another. This should report all keys present which are convertible to the requested type. + var allKeys: [Key] { get } + + /// Returns whether the `Decoder` contains a value associated with the given key. + /// + /// The value associated with the given key may be a null value as appropriate for the data format. + /// + /// - parameter key: The key to search for. + /// - returns: Whether the `Decoder` has an entry for the given key. + func contains(_ key: Key) -> Bool + + /// Decodes a value of the given type for the given key. + /// + /// A default implementation is given for these types which calls into the `decodeIfPresent` implementations below. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `CocoaError.coderValueNotFound` if `self` does not have an entry for the given key or if the value is null. + func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool + func decode(_ type: Int.Type, forKey key: Key) throws -> Int + func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 + func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 + func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 + func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 + func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt + func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 + func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 + func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 + func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 + func decode(_ type: Float.Type, forKey key: Key) throws -> Float + func decode(_ type: Double.Type, forKey key: Key) throws -> Double + func decode(_ type: String.Type, forKey key: Key) throws -> String + func decode(_ type: Data.Type, forKey key: Key) throws -> Data + func decode(_ type: T.Type, forKey key: Key) throws -> T + + /// Decodes a value of the given type for the given key, if present. + /// + /// This method returns `nil` if the container does not have a value associated with `key`, or if the value is null. The difference between these states can be distinguished with a `contains(_:)` call. + /// + /// - parameter type: The type of value to decode. + /// - parameter key: The key that the decoded value is associated with. + /// - returns: A decoded value of the requested type, or `nil` if the `Decoder` does not have an entry associated with the given key, or if the value is a null value. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value is not convertible to the requested type. + func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool? + func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int? + func decodeIfPresent(_ type: Int8.Type, forKey key: Key) throws -> Int8? + func decodeIfPresent(_ type: Int16.Type, forKey key: Key) throws -> Int16? + func decodeIfPresent(_ type: Int32.Type, forKey key: Key) throws -> Int32? + func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64? + func decodeIfPresent(_ type: UInt.Type, forKey key: Key) throws -> UInt? + func decodeIfPresent(_ type: UInt8.Type, forKey key: Key) throws -> UInt8? + func decodeIfPresent(_ type: UInt16.Type, forKey key: Key) throws -> UInt16? + func decodeIfPresent(_ type: UInt32.Type, forKey key: Key) throws -> UInt32? + func decodeIfPresent(_ type: UInt64.Type, forKey key: Key) throws -> UInt64? + func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float? + func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double? + func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String? + func decodeIfPresent(_ type: Data.Type, forKey key: Key) throws -> Data? + func decodeIfPresent(_ type: T.Type, forKey key: Key) throws -> T? + + /// Returns the data stored for the given key as represented in a container keyed by the given key type. + /// + /// - parameter type: The key type to use for the container. + /// - parameter key: The key that the nested container is associated with. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not a keyed container. + func nestedContainer(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer + + /// Returns the data stored for the given key as represented in an unkeyed container. + /// + /// - parameter key: The key that the nested container is associated with. + /// - returns: An unkeyed decoding container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not an unkeyed container. + func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer + + /// Returns a `Decoder` instance for decoding `super` from the container associated with the default `super` key. + /// + /// Equivalent to calling `superDecoder(forKey:)` with `Key(stringValue: "super", intValue: 0)`. + /// + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `CocoaError.coderValueNotFound` if `self` does not have an entry for the default `super` key, or if the stored value is null. + func superDecoder() throws -> Decoder + + /// Returns a `Decoder` instance for decoding `super` from the container associated with the given key. + /// + /// - parameter key: The key to decode `super` for. + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `CocoaError.coderValueNotFound` if `self` does not have an entry for the given key, or if the stored value is null. + func superDecoder(forKey key: Key) throws -> Decoder +} + +/// Conformance to `UnkeyedEncodingContainer` indicates that a type provides a view into an `Encoder`'s storage and is used to hold the encoded properties of an `Encodable` type sequentially, without keys. +/// +/// Encoders should provide types conforming to `UnkeyedEncodingContainer` for their format. +public protocol UnkeyedEncodingContainer { + /// The path of coding keys taken to get to this point in encoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: T?) throws + + /// Encodes the given value. + /// + /// - parameter value: The value to encode. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encode(_ value: Bool?) throws + mutating func encode(_ value: Int?) throws + mutating func encode(_ value: Int8?) throws + mutating func encode(_ value: Int16?) throws + mutating func encode(_ value: Int32?) throws + mutating func encode(_ value: Int64?) throws + mutating func encode(_ value: UInt?) throws + mutating func encode(_ value: UInt8?) throws + mutating func encode(_ value: UInt16?) throws + mutating func encode(_ value: UInt32?) throws + mutating func encode(_ value: UInt64?) throws + mutating func encode(_ value: Float?) throws + mutating func encode(_ value: Double?) throws + mutating func encode(_ value: String?) throws + mutating func encode(_ value: Data?) throws + + /// Encodes the given object weakly. + /// + /// For `Encoder`s that implement this functionality, this will only encode the given object if it is encoded unconditionally elsewhere in the payload (either previously or in the future). + /// + /// For formats which don't support this feature, the default implementation encodes the given object unconditionally. + /// + /// - parameter object: The object to encode. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + mutating func encodeWeak(_ object: T?) throws + + /// Encodes the elements of the given sequence. + /// + /// A default implementation of these is given in an extension. + /// + /// - parameter sequence: The sequences whose contents to encode. + /// - throws: An error if any of the contained values throws an error. + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Bool + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Int + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Int8 + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Int16 + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Int32 + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Int64 + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == UInt + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == UInt8 + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == UInt16 + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == UInt32 + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == UInt64 + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Float + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Double + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == String + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element == Data + mutating func encode(contentsOf sequence: Sequence) throws where Sequence.Iterator.Element : Encodable + + /// Encodes a nested container keyed by the given type and returns it. + /// + /// - parameter keyType: The key type to use for the container. + /// - returns: A new keyed encoding container. + mutating func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer + + /// Encodes an unkeyed encoding container and returns it. + /// + /// - returns: A new unkeyed encoding container. + mutating func nestedUnkeyedContainer() -> UnkeyedEncodingContainer + + /// Encodes a nested container and returns an `Encoder` instance for encoding `super` into that container. + /// + /// - returns: A new `Encoder` to pass to `super.encode(to:)`. + mutating func superEncoder() -> Encoder +} + +/// Conformance to `UnkeyedDecodingContainer` indicates that a type provides a view into a `Decoder`'s storage and is used to hold the encoded properties of a `Decodable` type sequentially, without keys. +/// +/// Decoders should provide types conforming to `UnkeyedDecodingContainer` for their format. +public protocol UnkeyedDecodingContainer { + /// The path of coding keys taken to get to this point in decoding. + /// A `nil` value indicates an unkeyed container. + var codingPath: [CodingKey?] { get } + + /// Returns the number of elements (if known) contained within this container. + var count: Int? { get } + + /// Returns whether there are no more elements left to be decoded in the container. + var isAtEnd: Bool { get } + + /// Decodes a value of the given type. + /// + /// A default implementation is given for these types which calls into the `decodeIfPresent` implementations below. + /// + /// - parameter type: The type of value to decode. + /// - returns: A value of the requested type, if present for the given key and convertible to the requested type. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value is not convertible to the requested type. + /// - throws: `CocoaError.coderValueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func decode(_ type: Bool.Type) throws -> Bool + mutating func decode(_ type: Int.Type) throws -> Int + mutating func decode(_ type: Int8.Type) throws -> Int8 + mutating func decode(_ type: Int16.Type) throws -> Int16 + mutating func decode(_ type: Int32.Type) throws -> Int32 + mutating func decode(_ type: Int64.Type) throws -> Int64 + mutating func decode(_ type: UInt.Type) throws -> UInt + mutating func decode(_ type: UInt8.Type) throws -> UInt8 + mutating func decode(_ type: UInt16.Type) throws -> UInt16 + mutating func decode(_ type: UInt32.Type) throws -> UInt32 + mutating func decode(_ type: UInt64.Type) throws -> UInt64 + mutating func decode(_ type: Float.Type) throws -> Float + mutating func decode(_ type: Double.Type) throws -> Double + mutating func decode(_ type: String.Type) throws -> String + mutating func decode(_ type: Data.Type) throws -> Data + mutating func decode(_ type: T.Type) throws -> T + + /// Decodes a value of the given type, if present. + /// + /// This method returns `nil` if the container has no elements left to decode, or if the value is null. The difference between these states can be distinguished by checking `isAtEnd`. + /// + /// - parameter type: The type of value to decode. + /// - returns: A decoded value of the requested type, or `nil` if the value is a null value, or if there are no more elements to decode. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value is not convertible to the requested type. + mutating func decodeIfPresent(_ type: Bool.Type) throws -> Bool? + mutating func decodeIfPresent(_ type: Int.Type) throws -> Int? + mutating func decodeIfPresent(_ type: Int8.Type) throws -> Int8? + mutating func decodeIfPresent(_ type: Int16.Type) throws -> Int16? + mutating func decodeIfPresent(_ type: Int32.Type) throws -> Int32? + mutating func decodeIfPresent(_ type: Int64.Type) throws -> Int64? + mutating func decodeIfPresent(_ type: UInt.Type) throws -> UInt? + mutating func decodeIfPresent(_ type: UInt8.Type) throws -> UInt8? + mutating func decodeIfPresent(_ type: UInt16.Type) throws -> UInt16? + mutating func decodeIfPresent(_ type: UInt32.Type) throws -> UInt32? + mutating func decodeIfPresent(_ type: UInt64.Type) throws -> UInt64? + mutating func decodeIfPresent(_ type: Float.Type) throws -> Float? + mutating func decodeIfPresent(_ type: Double.Type) throws -> Double? + mutating func decodeIfPresent(_ type: String.Type) throws -> String? + mutating func decodeIfPresent(_ type: Data.Type) throws -> Data? + mutating func decodeIfPresent(_ type: T.Type) throws -> T? + + /// Decodes a nested container keyed by the given type. + /// + /// - parameter type: The key type to use for the container. + /// - returns: A keyed decoding container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not a keyed container. + mutating func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer + + /// Decodes an unkeyed nested container. + /// + /// - returns: An unkeyed decoding container view into `self`. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered stored value is not an unkeyed container. + mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer + + /// Decodes a nested container and returns a `Decoder` instance for decoding `super` from that container. + /// + /// - returns: A new `Decoder` to pass to `super.init(from:)`. + /// - throws: `CocoaError.coderValueNotFound` if the encountered encoded value is null, or of there are no more values to decode. + mutating func superDecoder() throws -> Decoder +} + +/// A `SingleValueEncodingContainer` is a container which can support the storage and direct encoding of a single non-keyed value. +public protocol SingleValueEncodingContainer { + /// Encodes a single value of the given type. + /// + /// - parameter value: The value to encode. + /// - throws: `CocoaError.coderInvalidValue` if the given value is invalid in the current context for this format. + /// - precondition: May not be called after a previous `self.encode(_:)` call. + mutating func encode(_ value: Bool) throws + mutating func encode(_ value: Int) throws + mutating func encode(_ value: Int8) throws + mutating func encode(_ value: Int16) throws + mutating func encode(_ value: Int32) throws + mutating func encode(_ value: Int64) throws + mutating func encode(_ value: UInt) throws + mutating func encode(_ value: UInt8) throws + mutating func encode(_ value: UInt16) throws + mutating func encode(_ value: UInt32) throws + mutating func encode(_ value: UInt64) throws + mutating func encode(_ value: Float) throws + mutating func encode(_ value: Double) throws + mutating func encode(_ value: String) throws + mutating func encode(_ value: Data) throws +} + +/// A `SingleValueDecodingContainer` is a container which can support the storage and direct decoding of a single non-keyed value. +public protocol SingleValueDecodingContainer { + /// Decodes a single value of the given type. + /// + /// - parameter type: The type to decode as. + /// - returns: A value of the requested type. + /// - throws: `CocoaError.coderTypeMismatch` if the encountered encoded value cannot be converted to the requested type. + func decode(_ type: Bool.Type) throws -> Bool + func decode(_ type: Int.Type) throws -> Int + func decode(_ type: Int8.Type) throws -> Int8 + func decode(_ type: Int16.Type) throws -> Int16 + func decode(_ type: Int32.Type) throws -> Int32 + func decode(_ type: Int64.Type) throws -> Int64 + func decode(_ type: UInt.Type) throws -> UInt + func decode(_ type: UInt8.Type) throws -> UInt8 + func decode(_ type: UInt16.Type) throws -> UInt16 + func decode(_ type: UInt32.Type) throws -> UInt32 + func decode(_ type: UInt64.Type) throws -> UInt64 + func decode(_ type: Float.Type) throws -> Float + func decode(_ type: Double.Type) throws -> Double + func decode(_ type: String.Type) throws -> String + func decode(_ type: Data.Type) throws -> Data +} + +/// Represents a user-defined key for providing context for encoding and decoding. +public struct CodingUserInfoKey : RawRepresentable, Hashable { + typealias RawValue = String + let rawValue: String + init?(rawValue: String) + init(_ value: String) +} + +// Repeat for all primitive types... +extension Bool : Codable { + public init(from decoder: Decoder) throws { + self = try decoder.singleValueContainer().decode(Bool.self) + } + + public func encode(to encoder: Encoder) throws { + try encoder.singleValueContainer().encode( self) + } +} + +// Repeat for all primitive types... +public extension RawRepresentable where RawValue == Bool, Self : Codable { + public init(from decoder: Decoder) throws { + let decoded = try decoder.singleValueContainer().decode(RawValue.self) + guard let value = Self(rawValue: decoded) else { + throw CocoaError.error(.coderReadCorrupt) + } + + self = value + } + + public func encode(to encoder: Encoder) throws { + try encoder.singleValueContainer().encode(self.rawValue) + } +} +``` From 0c356f787dbbb42c079b389f69c278f956ba8c81 Mon Sep 17 00:00:00 2001 From: Itai Ferber Date: Thu, 6 Apr 2017 11:01:32 -0700 Subject: [PATCH 0272/4563] Proposal for Foundation Swift Encoders (#640) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Proposal for Swift Encoders * Add missing Alternatives Considered section * Update with feedback from swift-evolution * Incorporate the Codable → {En,De}codable split * Add userInfo dictionaries --- proposals/XXXX-swift-encoders.md | 408 +++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 proposals/XXXX-swift-encoders.md diff --git a/proposals/XXXX-swift-encoders.md b/proposals/XXXX-swift-encoders.md new file mode 100644 index 0000000000..910f33f5de --- /dev/null +++ b/proposals/XXXX-swift-encoders.md @@ -0,0 +1,408 @@ +# Swift Encoders + +* Proposal: SE-NNNN +* Author(s): [Itai Ferber](https://github.com/itaiferber), [Michael LeHew](https://github.com/mlehew), [Tony Parker](https://github.com/parkera) +* Review Manager: TBD +* Status: **Awaiting review** +* Associated PRs: + * [#8124](https://github.com/apple/swift/pull/8124) + +## Introduction + +As part of the proposal for a Swift archival and serialization API ([SE-NNNN](https://github.com/itaiferber/swift-evolution/blob/swift-archival-serialization/proposals/XXXX-swift-archival-serialization.md)), we are also proposing new API for specific new encoders and decoders, as well as introducing support for new `Codable` types in `NSKeyedArchiver` and `NSKeyedUnarchiver`. + +This proposal composes the latter two stages laid out in [SE-NNNN](https://github.com/itaiferber/swift-evolution/blob/swift-archival-serialization/proposals/XXXX-swift-archival-serialization.md). + +## Motivation + +With the base API discussed in [SE-NNNN](https://github.com/itaiferber/swift-evolution/blob/swift-archival-serialization/proposals/XXXX-swift-archival-serialization.md), we want to provide new encoders for consumers of this API, as well as provide a consistent story for bridging this new API with our existing `NSCoding` implementations. We would like to offer a base level of support that users can depend on, and set a pattern that third parties can follow in implementing and extending their own encoders. + +## Proposed solution + +We will: + +1. Add two new encoders and decoders to support encoding Swift value trees in JSON and property list formats +2. Add support for passing `Codable` Swift values to `NSKeyedArchiver` and `NSKeyedUnarchiver`, and add `Codable` conformance to our Swift value types + +## Detailed design + +### New Encoders and Decoders + +#### JSON + +One of the key motivations for the introduction of this API was to allow safer interaction between Swift values and their JSON representations. For values which are `Codable`, users can encode to and decode from JSON with `JSONEncoder` and `JSONDecoder`: + +```swift +open class JSONEncoder { + // MARK: Top-Level Encoding + + /// Encodes the given top-level value and returns its JSON representation. + /// + /// - parameter value: The value to encode. + /// - returns: A new `Data` value containing the encoded JSON data. + /// - throws: `CocoaError.coderInvalidValue` if a non-comforming floating-point value is encountered during archiving, and the encoding strategy is `.throw`. + /// - throws: An error if any value throws an error during encoding. + open func encode(_ value: T) throws -> Data + + // MARK: Customization + + /// The formatting of the output JSON data. + public enum OutputFormatting { + /// Produce JSON compacted by removing whitespace. This is the default formatting. + case compact + + /// Produce human-readable JSON with indented output. + case prettyPrinted + } + + /// The strategy to use for encoding `Date` values. + public enum DateEncodingStrategy { + /// Defer to `Date` for choosing an encoding. This is the default strategy. + case deferredToDate + + /// Encode the `Date` as a UNIX timestamp (as a JSON number). + case secondsSince1970 + + /// Encode the `Date` as UNIX millisecond timestamp (as a JSON number). + case millisecondsSince1970 + + /// Encode the `Date` as an ISO-8601-formatted string (in RFC 3339 format). + @available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) + case iso8601 + + /// Encode the `Date` as a string formatted by the given formatter. + case formatted(DateFormatter) + + /// Encode the `Date` as a custom value encoded by the given closure. + /// + /// If the closure fails to encode a value into the given encoder, the encoder will encode an empty `.default` container in its place. + case custom((_ value: Date, _ encoder: Encoder) throws -> Void) + } + + /// The strategy to use for encoding `Data` values. + public enum DataEncodingStrategy { + /// Encoded the `Data` as a Base64-encoded string. This is the default strategy. + case base64 + + /// Encode the `Data` as a custom value encoded by the given closure. + /// + /// If the closure fails to encode a value into the given encoder, the encoder will encode an empty `.default` container in its place. + case custom((_ value: Data, _ encoder: Encoder) throws -> Void) + } + + /// The strategy to use for non-JSON-conforming floating-point values (IEEE 754 infinity and NaN). + public enum NonConformingFloatEncodingStrategy { + /// Throw upon encountering non-conforming values. This is the default strategy. + case `throw` + + /// Encode the values using the given representation strings. + case convertToString(positiveInfinity: String, negativeInfinity: String, nan: String) + } + + /// The output format to produce. Defaults to `.compact`. + open var outputFormatting: OutputFormatting + + /// The strategy to use in encoding dates. Defaults to `.deferredToDate`. + open var dateEncodingStrategy: DateEncodingStrategy + + /// The strategy to use in encoding binary data. Defaults to `.base64`. + open var dataEncodingStrategy: DataEncodingStrategy + + /// The strategy to use in encoding non-conforming numbers. Defaults to `.throw`. + open var nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy + + /// Contextual information to expose during encoding. + open var userInfo: [CodingUserInfoKey : Any] +} + +open class JSONDecoder { + // MARK: Top-Level Decoding + + /// Decodes a top-level value of the given type from the given JSON representation. + /// + /// - parameter type: The type of the value to decode. + /// - parameter data: The data to decode from. + /// - returns: A value of the requested type. + /// - throws: `CocoaError.coderReadCorrupt` if values requested from the payload are corrupted, or if the given data is not valid JSON. + /// - throws: An error if any value throws an error during decoding. + open func decode(_ type: T.Type, from data: Data) throws -> Value + + // MARK: Customization + + /// The strategy to use for decoding `Date` values. + public enum DateDecodingStrategy { + /// Defer to `Date` for decoding. This is the default strategy. + case deferredToDate + + /// Decode the `Date` as a UNIX timestamp from a JSON number. + case secondsSince1970 + + /// Decode the `Date` as UNIX millisecond timestamp from a JSON number. + case millisecondsSince1970 + + /// Decode the `Date` as an ISO-8601-formatted string (in RFC 3339 format). + @available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) + case iso8601 + + /// Decode the `Date` as a string parsed by the given formatter. + case formatted(DateFormatter) + + /// Decode the `Date` as a custom value decoded by the given closure. + case custom((_ decoder: Decoder) throws -> Date) + } + + /// The strategy to use for decoding `Data` values. + public enum DataDecodingStrategy { + /// Decode the `Data` from a Base64-encoded string. This is the default strategy. + case base64 + + /// Decode the `Data` as a custom value decoded by the given closure. + case custom((_ decoder: Decoder) throws -> Data) + } + + /// The strategy to use for non-JSON-conforming floating-point values (IEEE 754 infinity and NaN). + public enum NonConformingFloatDecodingStrategy { + /// Throw upon encountering non-conforming values. This is the default strategy. + case `throw` + + /// Decode the values from the given representation strings. + case convertFromString(positiveInfinity: String, negativeInfinity: String, nan: String) + } + + /// The strategy to use in decoding dates. Defaults to `.deferredToDate`. + open var dateDecodingStrategy: DateDecodingStrategy + + /// The strategy to use in decoding binary data. Defaults to `.base64`. + open var dataDecodingStrategy: DataDecodingStrategy + + /// The strategy to use in decoding non-conforming numbers. Defaults to `.throw`. + open var nonConformingFloatDecodingStrategy: NonConformingFloatDecodingStrategy + + /// Contextual information to expose during decoding. + open var userInfo: [CodingUserInfoKey : Any] +} +``` + +Usage: + +```swift +var encoder = JSONEncoder() +encoder.dateEncodingStrategy = .iso8601 +encoder.dataEncodingStrategy = .custom(myBase85Encoder) + +// Since JSON does not natively allow for infinite or NaN values, we can customize strategies for encoding these non-conforming values. +encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: "INF", negativeInfinity: "-INF", nan: "NaN") + +// MyValue conforms to Codable +let topLevel = MyValue(...) + +let payload: Data +do { + payload = try encoder.encode(topLevel) +} catch { + // Some value threw while encoding. +} + +// ... + +var decoder = JSONDecoder() +decoder.dateDecodingStrategy = .iso8601 +decoder.dataDecodingStrategy = .custom(myBase85Decoder) + +// Look for and match these values when decoding `Double`s or `Float`s. +decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: "INF", negativeInfinity: "-INF", nan: "NaN") + +let topLevel: MyValue +do { + topLevel = try decoder.decode(MyValue.self, from: payload) +} catch { + // Data was corrupted, or some value threw while decoding. +} +``` + +It should be noted here that `JSONEncoder` and `JSONDecoder` do not themselves conform to `Encoder` and `Decoder`; instead, they contain private nested types which do conform to `Encoder` and `Decoder`, which are passed to values' `encode(to:)` and `init(from:)`. This is because `JSONEncoder` and `JSONDecoder` must present a different top-level API than they would at intermediate levels. + +#### Property List + +We also intend to support the property list format, with `PropertyListEncoder` and `PropertyListDecoder`: + +```swift +open class PropertyListEncoder { + // MARK: Top-Level Encoding + + /// Encodes the given top-level value and returns its property list representation. + /// + /// - parameter value: The value to encode. + /// - returns: A new `Data` value containing the encoded property list data. + /// - throws: An error if any value throws an error during encoding. + open func encode(_ value: T) throws -> Data + + // MARK: Customization + + /// The output format to write the property list data in. Defaults to `.binary`. + open var outputFormat: PropertyListSerialization.PropertyListFormat + + /// Contextual information to expose during encoding. + open var userInfo: [CodingUserInfoKey : Any] +} + +open class PropertyListDecoder { + // MARK: Top-Level Decoding + + /// Decodes a top-level value of the given type from the given property list representation. + /// + /// - parameter type: The type of the value to decode. + /// - parameter data: The data to decode from. + /// - returns: A value of the requested type. + /// - throws: `CocoaError.coderReadCorrupt` if values requested from the payload are corrupted, or if the given data is not a valid property list. + /// - throws: An error if any value throws an error during decoding. + open func decode(_ type: T.Type, from data: Data) throws -> Value + + /// Decodes a top-level value of the given type from the given property list representation. + /// + /// - parameter type: The type of the value to decode. + /// - parameter data: The data to decode from. + /// - parameter format: The parsed property list format. + /// - returns: A value of the requested type along with the detected format of the property list. + /// - throws: `CocoaError.coderReadCorrupt` if values requested from the payload are corrupted, or if the given data is not a valid property list. + /// - throws: An error if any value throws an error during decoding. + open func decode(_ type: Value.Type, from data: Data, format: inout PropertyListSerialization.PropertyListFormat) throws -> Value + + // MARK: Customization + + /// Contextual information to expose during decoding. + open var userInfo: [CodingUserInfoKey : Any] +} +``` + +Usage: + +```swift +let encoder = PropertyListEncoder() +let topLevel = MyValue(...) +let payload: Data +do { + payload = try encoder.encode(topLevel) +} catch { + // Some value threw while encoding. +} + +// ... + +let decoder = PropertyListDecoder() +let topLevel: MyValue +do { + topLevel = try decoder.decode(MyValue.self, from: payload) +} catch { + // Data was corrupted, or some value threw while decoding. +} +``` + +Like with JSON, `PropertyListEncoder` and `PropertyListDecoder` also provide private nested types which conform to `Encoder` and `Decoder` for performing the archival. + +### Foundation-Provided Errors + +Along with providing the above encoders and decoders, we would like to promote the use of a common set of error codes and messages across all new encoders and decoders. A common vocabulary of expected errors allows end-users to write code agnostic about the specific encoder/decoder implementation they are working with, whether first-party or third-party: + +```swift +extension CocoaError.Code { + /// Thrown when a value incompatible with the output format is encoded. + public static var coderInvalidValue: CocoaError.Code + + /// Thrown when a value of a given type is requested but the encountered value is of an incompatible type. + public static var coderTypeMismatch: CocoaError.Code + + /// Thrown when read data is corrupted or otherwise invalid for the format. This value already exists today. + public static var coderReadCorrupt: CocoaError.Code + + /// Thrown when a requested key or value is unexpectedly null or missing. This value already exists today. + public static var coderValueNotFound: CocoaError.Code +} + +// These reexpose the values above. +extension CocoaError { + public static var coderInvalidValue: CocoaError.Code + + public static var coderTypeMismatch: CocoaError.Code +} +``` + +The localized description strings associated with the two new error codes are: + +* `.coderInvalidValue`: "The data is not valid for encoding in this format." +* `.coderTypeMismatch`: "The data couldn't be read because it isn't in the correct format." (Precedent from `NSCoderReadCorruptError`.) + +All of these errors will include the coding key path that led to the failure in the error's `userInfo` dictionary under `NSCodingPathErrorKey`, along with a non-localized, developer-facing failure reason under `NSDebugDescriptionErrorKey`. + +### `NSKeyedArchiver` & `NSKeyedUnarchiver` Changes + +Although our primary objectives for this new API revolve around Swift, we would like to make it easy for current consumers to make the transition to `Codable` where appropriate. As part of this, we would like to bridge compatibility between new `Codable` types (or newly-`Codable`-adopting types) and existing `NSCoding` types. + +To do this, we want to introduce changes to `NSKeyedArchiver` and `NSKeyedUnarchiver` in Swift that allow archival of `Codable` types intermixed with `NSCoding` types: + +```swift +// These are provided in the Swift overlay, and included in swift-corelibs-foundation. +extension NSKeyedArchiver { + public func encodeCodable(_ codable: Encodable?, forKey key: String) { ... } +} + +extension NSKeyedUnarchiver { + public func decodeCodable(_ type: T.Type, forKey key: String) -> T? { ... } +} +``` + +> NOTE: Since these changes are being made in extensions in the Swift overlay, it is not yet possible for these methods to be overridden. These can therefore not be added to `NSCoder`, since `NSKeyedArchiver` and `NSKeyedUnarchiver` would not be able to provide concrete implementations. In order to call these methods, it is necessary to downcast from an `NSCoder` to `NSKeyedArchiver`/`NSKeyedUnarchiver` directly. Since subclasses of `NSKeyedArchiver` and `NSKeyedUnarchiver` in Swift will inherit these implementations without being able to override them (which is wrong), we will `NSRequiresConcreteImplementation()` dynamically in subclasses. + +The addition of these methods allows the introduction of `Codable` types into existing `NSCoding` structures, allowing for a transition to `Codable` types where appropriate. + +#### Refining `encode(_:forKey:)` + +Along with these extensions, we would like to refine the import of `-[NSCoder encodeObject:forKey:]`, which is currently imported into Swift as `encode(_: Any?, forKey: String)`. This method currently accepts Objective-C and Swift objects conforming to `NSCoding` (non-conforming objects produce a runtime error), as well as bridgeable Swift types (`Data`, `String`, `Array`, etc.); we would like to extend it to support new Swift `Codable` types, which would otherwise produce a runtime error upon call. + +`-[NSCoder encodeObject:forKey:]` will be given a new Swift name of `encodeObject(_:forKey:)`, and we will provide a replacement `encode(_: Any?, forKey: String)` in the overlay which will funnel out to either `encodeCodable(_:forKey:)` or `encodeObject(_:forKey:)` as appropriate. This should maintain source compatibility for end users already calling `encode(_:forKey:)`, as well as behavior compatibility for subclassers of `NSCoder` and `NSKeyedArchiver` who may be providing their own `encode(_:forKey:)`. + +#### Semantics of `Codable` Types in Archives + +There are a few things to note about including `Codable` values in `NSKeyedArchiver` archives: + +* Bridgeable Foundation types will always bridge before encoding. This is to facilitate writing Foundation types in a compatible format from both Objective-C and Swift + * On decode, these types will decode either as their Objective-C or Swift version, depending on user need (`decodeObject(forKey:)` will decode as an Objective-C object; `decodeCodable(_:forKey:)` as a Swift value) +* User types, which are not bridgeable, do not write out a `$class` and can only be decoded in Swift. In the future, we may add API to allow Swift types to provide an Objective-C class to decode as, effectively allowing for user bridging across archival + +##### Foundation Types Adopting `Codable` + +The following Foundation Swift types will be adopting `Codable`, and will encode as their bridged types when encoded through `NSKeyedArchiver`, as mentioned above: + +* `AffineTransform` +* `Calendar` +* `CharacterSet` +* `Date` +* `DateComponents` +* `DateInterval` +* `Decimal` +* `IndexPath` +* `IndexSet` +* `Locale` +* `Measurement` +* `Notification` +* `PersonNameComponents` +* `TimeZone` +* `URL` +* `URLComponents` +* `URLRequest` +* `UUID` + +Along with these, the `Array`, `Dictionary`, and `Set` types will gain `Codable` conformance (as part of the Conditional Conformance feature), and encode through `NSKeyedArchiver` as `NSArray`, `NSDictionary`, and `NSSet` respectively. + +## Source compatibility + +The majority of this proposal is additive. The changes to `NSKeyedArchiver` are intended to be non-source-breaking changes, and non-behavior-breaking changes for subclasses in Objective-C and Swift. + +## Effect on ABI stability + +The addition of this API will not be an ABI-breaking change. However, this will add limitations for changes in future versions of Swift, as parts of the API will have to remain unchanged between versions of Swift (barring some additions, discussed below). + +## Effect on API resilience + +Much like new API added to the standard library, once added, some changes to this API will be ABI- and source-breaking changes. Changes to the new encoder and decoder classes provided above will be restricted as described in the [library evolution document](https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst) in the Swift repository; in particular, the removal of methods or nested types or changes to argument types will break client behavior. Additionally, additions to provided options `enum`s will be a source-breaking change for users performing an exhaustive switch over their cases; removal of cases will be ABI-breaking. + From f2b61adbb87ac4add05193140542bad91fa38441 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 6 Apr 2017 11:00:02 -0700 Subject: [PATCH 0273/4563] Initiate review for SE-0166: Swift Archival & Serialization --- ...erialization.md => 0166-swift-archival-serialization.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{XXXX-swift-archival-serialization.md => 0166-swift-archival-serialization.md} (99%) diff --git a/proposals/XXXX-swift-archival-serialization.md b/proposals/0166-swift-archival-serialization.md similarity index 99% rename from proposals/XXXX-swift-archival-serialization.md rename to proposals/0166-swift-archival-serialization.md index 35c0ca343d..3bd9e6ffa1 100644 --- a/proposals/XXXX-swift-archival-serialization.md +++ b/proposals/0166-swift-archival-serialization.md @@ -1,9 +1,9 @@ # Swift Archival & Serialization -* Proposal: SE-NNNN +* Proposal: [SE-0166](0166-swift-archival-serialization.md) * Author(s): [Itai Ferber](https://github.com/itaiferber), [Michael LeHew](https://github.com/mlehew), [Tony Parker](https://github.com/parkera) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (April 6...12, 2017)** * Associated PRs: * [#8124](https://github.com/apple/swift/pull/8124) * [#8125](https://github.com/apple/swift/pull/8125) From 355233bd1a5c657b65a4ac05835c83c36da4952c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 6 Apr 2017 11:03:45 -0700 Subject: [PATCH 0274/4563] Initiate review for SE-0167: Swift Encoders --- proposals/0166-swift-archival-serialization.md | 4 ++-- ...XXXX-swift-encoders.md => 0167-swift-encoders.md} | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) rename proposals/{XXXX-swift-encoders.md => 0167-swift-encoders.md} (94%) diff --git a/proposals/0166-swift-archival-serialization.md b/proposals/0166-swift-archival-serialization.md index 3bd9e6ffa1..8298fb1e02 100644 --- a/proposals/0166-swift-archival-serialization.md +++ b/proposals/0166-swift-archival-serialization.md @@ -33,7 +33,7 @@ This proposal is the first stage of three that introduce different facets of a w 2. The next stage will propose specific API for new encoders 3. The final stage will discuss how this new API will interop with `NSCoding` as it is today -[SE-NNNN](https://github.com/itaiferber/swift-evolution/blob/swift-encoders/proposals/XXXX-swift-encoders.md) provides stages 2 and 3. +[SE-0167](https://github.com/itaiferber/swift-evolution/blob/swift-encoders/proposals/0167-swift-encoders.md) provides stages 2 and 3. ## Proposed solution @@ -61,7 +61,7 @@ public struct Farm : Codable { } ``` -With developer participation, we will offer encoders and decoders (described in [SE-NNNN](https://github.com/itaiferber/swift-evolution/blob/swift-encoders/proposals/XXXX-swift-encoders.md), not here) that take advantage of this conformance to offer type-safe serialization of user models: +With developer participation, we will offer encoders and decoders (described in [SE-0167](0167-swift-encoders.md), not here) that take advantage of this conformance to offer type-safe serialization of user models: ```swift let farm = Farm(name: "Old MacDonald's Farm", diff --git a/proposals/XXXX-swift-encoders.md b/proposals/0167-swift-encoders.md similarity index 94% rename from proposals/XXXX-swift-encoders.md rename to proposals/0167-swift-encoders.md index 910f33f5de..47458097f7 100644 --- a/proposals/XXXX-swift-encoders.md +++ b/proposals/0167-swift-encoders.md @@ -1,21 +1,21 @@ # Swift Encoders -* Proposal: SE-NNNN +* Proposal: [SE-0166](0167-swift-encoders.md) * Author(s): [Itai Ferber](https://github.com/itaiferber), [Michael LeHew](https://github.com/mlehew), [Tony Parker](https://github.com/parkera) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (April 6...12, 2017)** * Associated PRs: * [#8124](https://github.com/apple/swift/pull/8124) ## Introduction -As part of the proposal for a Swift archival and serialization API ([SE-NNNN](https://github.com/itaiferber/swift-evolution/blob/swift-archival-serialization/proposals/XXXX-swift-archival-serialization.md)), we are also proposing new API for specific new encoders and decoders, as well as introducing support for new `Codable` types in `NSKeyedArchiver` and `NSKeyedUnarchiver`. +As part of the proposal for a Swift archival and serialization API ([SE-0166](0166-swift-archival-serialization.md)), we are also proposing new API for specific new encoders and decoders, as well as introducing support for new `Codable` types in `NSKeyedArchiver` and `NSKeyedUnarchiver`. -This proposal composes the latter two stages laid out in [SE-NNNN](https://github.com/itaiferber/swift-evolution/blob/swift-archival-serialization/proposals/XXXX-swift-archival-serialization.md). +This proposal composes the latter two stages laid out in [SE-0166](0166-swift-archival-serialization.md). ## Motivation -With the base API discussed in [SE-NNNN](https://github.com/itaiferber/swift-evolution/blob/swift-archival-serialization/proposals/XXXX-swift-archival-serialization.md), we want to provide new encoders for consumers of this API, as well as provide a consistent story for bridging this new API with our existing `NSCoding` implementations. We would like to offer a base level of support that users can depend on, and set a pattern that third parties can follow in implementing and extending their own encoders. +With the base API discussed in [SE-0166](0166-swift-archival-serialization.md), we want to provide new encoders for consumers of this API, as well as provide a consistent story for bridging this new API with our existing `NSCoding` implementations. We would like to offer a base level of support that users can depend on, and set a pattern that third parties can follow in implementing and extending their own encoders. ## Proposed solution From 456ea8d36c1be0f2c6d69bd4c98bd23a3c2f6b62 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 6 Apr 2017 11:08:15 -0700 Subject: [PATCH 0275/4563] Fix link --- proposals/0166-swift-archival-serialization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0166-swift-archival-serialization.md b/proposals/0166-swift-archival-serialization.md index 8298fb1e02..8cb0aa70a0 100644 --- a/proposals/0166-swift-archival-serialization.md +++ b/proposals/0166-swift-archival-serialization.md @@ -33,7 +33,7 @@ This proposal is the first stage of three that introduce different facets of a w 2. The next stage will propose specific API for new encoders 3. The final stage will discuss how this new API will interop with `NSCoding` as it is today -[SE-0167](https://github.com/itaiferber/swift-evolution/blob/swift-encoders/proposals/0167-swift-encoders.md) provides stages 2 and 3. +[SE-0167](0167-swift-encoders.md) provides stages 2 and 3. ## Proposed solution From ba8746f54bb15abc3e6d175889d9700bea91bb19 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Thu, 6 Apr 2017 12:01:20 -0700 Subject: [PATCH 0276/4563] Fix link typo. --- proposals/0167-swift-encoders.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0167-swift-encoders.md b/proposals/0167-swift-encoders.md index 47458097f7..1b6842bd7a 100644 --- a/proposals/0167-swift-encoders.md +++ b/proposals/0167-swift-encoders.md @@ -1,6 +1,6 @@ # Swift Encoders -* Proposal: [SE-0166](0167-swift-encoders.md) +* Proposal: [SE-0167](0167-swift-encoders.md) * Author(s): [Itai Ferber](https://github.com/itaiferber), [Michael LeHew](https://github.com/mlehew), [Tony Parker](https://github.com/parkera) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active review (April 6...12, 2017)** From 0cc9a60fe367b70d69154c13c9378af2b56d3102 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 6 Apr 2017 12:31:46 -0700 Subject: [PATCH 0277/4563] Bless SE-0168 Multi-Line String Literals and begin review. --- ...terals.md => 0168-multi-line-string-literals.md} | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) rename proposals/{XXXX-multi-line-string-literals.md => 0168-multi-line-string-literals.md} (92%) diff --git a/proposals/XXXX-multi-line-string-literals.md b/proposals/0168-multi-line-string-literals.md similarity index 92% rename from proposals/XXXX-multi-line-string-literals.md rename to proposals/0168-multi-line-string-literals.md index 172b235423..2a6b37f383 100644 --- a/proposals/XXXX-multi-line-string-literals.md +++ b/proposals/0168-multi-line-string-literals.md @@ -1,9 +1,9 @@ # Feature name -* Proposal: [SE-XXXX](https://github.com/apple/swift-evolution/blob/master/proposals/XXXX-name.md) +* Proposal: [SE-0168](https://github.com/apple/swift-evolution/blob/master/proposals/0168-multi-line-string-literals.md) * Author(s): [John Holdsworth](https://github.com/johnno1962), [Brent Royal-Gordon](https://github.com/brentdax), [Tyler Cloutier](https://github.com/TheArtOfEngineering) -* Status: **[Awaiting review](#rationale)** -* Review manager: TBD +* Status: **Active review (April 6...12, 2017)** +* Review manager: [Joe Groff](https://github.com/jckarter) ## Introduction @@ -120,10 +120,3 @@ is a more practical one: it is a more major departure for the compiler in that t in the AST are no longer in source file order. Testing has, however, shown the toolchain to be surprisingly robust in dealing with this change once a few assertions were removed. -------------------------------------------------------------------------------- - -# Rationale - -On [Date], the core team decided to **(TBD)** this proposal. -When the core team makes a decision regarding this proposal, -their rationale for the decision will be written here. From b4ac07ea6d67939e9277182d97e4076738795539 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 6 Apr 2017 12:34:50 -0700 Subject: [PATCH 0278/4563] Fill in placeholder title for SE-0168. --- proposals/0168-multi-line-string-literals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0168-multi-line-string-literals.md b/proposals/0168-multi-line-string-literals.md index 2a6b37f383..fb2ffd09d7 100644 --- a/proposals/0168-multi-line-string-literals.md +++ b/proposals/0168-multi-line-string-literals.md @@ -1,4 +1,4 @@ -# Feature name +# Multi-Line String Literals * Proposal: [SE-0168](https://github.com/apple/swift-evolution/blob/master/proposals/0168-multi-line-string-literals.md) * Author(s): [John Holdsworth](https://github.com/johnno1962), [Brent Royal-Gordon](https://github.com/brentdax), [Tyler Cloutier](https://github.com/TheArtOfEngineering) From 23a76b91c072334199e7f34b474d380bd22c09df Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Thu, 6 Apr 2017 13:29:32 -0700 Subject: [PATCH 0279/4563] Clean up some small variations from the template. "Author(s)" to "Authors" or "Author", as appropriate. A proposal's self-link should be relative to the proposals folder. --- proposals/0160-objc-inference.md | 2 +- proposals/0166-swift-archival-serialization.md | 2 +- proposals/0167-swift-encoders.md | 2 +- proposals/0168-multi-line-string-literals.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0160-objc-inference.md b/proposals/0160-objc-inference.md index e4b31ebf74..572e9f185b 100644 --- a/proposals/0160-objc-inference.md +++ b/proposals/0160-objc-inference.md @@ -1,6 +1,6 @@ # Limiting `@objc` inference -* Proposal: [SE-0160](https://github.com/apple/swift-evolution/blob/master/proposals/0160-objc-inference.md) +* Proposal: [SE-0160](0160-objc-inference.md) * Author: [Doug Gregor](https://github.com/DougGregor) * Status: **Active Review March 31...April 2** * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/0389b1f49fc55b1a898701c549ce89738307b9fc/proposals/0160-objc-inference.md) diff --git a/proposals/0166-swift-archival-serialization.md b/proposals/0166-swift-archival-serialization.md index 8cb0aa70a0..efb7d4dc6a 100644 --- a/proposals/0166-swift-archival-serialization.md +++ b/proposals/0166-swift-archival-serialization.md @@ -1,7 +1,7 @@ # Swift Archival & Serialization * Proposal: [SE-0166](0166-swift-archival-serialization.md) -* Author(s): [Itai Ferber](https://github.com/itaiferber), [Michael LeHew](https://github.com/mlehew), [Tony Parker](https://github.com/parkera) +* Authors: [Itai Ferber](https://github.com/itaiferber), [Michael LeHew](https://github.com/mlehew), [Tony Parker](https://github.com/parkera) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active review (April 6...12, 2017)** * Associated PRs: diff --git a/proposals/0167-swift-encoders.md b/proposals/0167-swift-encoders.md index 1b6842bd7a..b7bc9cf16e 100644 --- a/proposals/0167-swift-encoders.md +++ b/proposals/0167-swift-encoders.md @@ -1,7 +1,7 @@ # Swift Encoders * Proposal: [SE-0167](0167-swift-encoders.md) -* Author(s): [Itai Ferber](https://github.com/itaiferber), [Michael LeHew](https://github.com/mlehew), [Tony Parker](https://github.com/parkera) +* Authors: [Itai Ferber](https://github.com/itaiferber), [Michael LeHew](https://github.com/mlehew), [Tony Parker](https://github.com/parkera) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active review (April 6...12, 2017)** * Associated PRs: diff --git a/proposals/0168-multi-line-string-literals.md b/proposals/0168-multi-line-string-literals.md index fb2ffd09d7..b20c00ec28 100644 --- a/proposals/0168-multi-line-string-literals.md +++ b/proposals/0168-multi-line-string-literals.md @@ -1,7 +1,7 @@ # Multi-Line String Literals -* Proposal: [SE-0168](https://github.com/apple/swift-evolution/blob/master/proposals/0168-multi-line-string-literals.md) -* Author(s): [John Holdsworth](https://github.com/johnno1962), [Brent Royal-Gordon](https://github.com/brentdax), [Tyler Cloutier](https://github.com/TheArtOfEngineering) +* Proposal: [SE-0168](0168-multi-line-string-literals.md) +* Authors: [John Holdsworth](https://github.com/johnno1962), [Brent Royal-Gordon](https://github.com/brentdax), [Tyler Cloutier](https://github.com/TheArtOfEngineering) * Status: **Active review (April 6...12, 2017)** * Review manager: [Joe Groff](https://github.com/jckarter) From da970dda2c1927f9e899e171b9619d6ff719ec77 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 6 Apr 2017 22:19:54 +0100 Subject: [PATCH 0280/4563] [SE-0168] Gmane => Pipermail (fixes broken link) One of the author links is still broken. --- proposals/0168-multi-line-string-literals.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0168-multi-line-string-literals.md b/proposals/0168-multi-line-string-literals.md index b20c00ec28..8a9ff2b716 100644 --- a/proposals/0168-multi-line-string-literals.md +++ b/proposals/0168-multi-line-string-literals.md @@ -2,8 +2,9 @@ * Proposal: [SE-0168](0168-multi-line-string-literals.md) * Authors: [John Holdsworth](https://github.com/johnno1962), [Brent Royal-Gordon](https://github.com/brentdax), [Tyler Cloutier](https://github.com/TheArtOfEngineering) +* Review Manager: [Joe Groff](https://github.com/jckarter) * Status: **Active review (April 6...12, 2017)** -* Review manager: [Joe Groff](https://github.com/jckarter) +* Bug: [SR-170](https://bugs.swift.org/browse/SR-170) ## Introduction @@ -12,7 +13,7 @@ This has been discussed a few times on swift-evolution most recently putting forward a number of different syntaxes that could achieve this goal each of which has their own use case and constituency for discussion. -[Swift-evolution thread](http://thread.gmane.org/gmane.comp.lang.swift.evolution/904/focus=15133) +[Swift-evolution thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001565.html) ## Motivation From 234207fd4729b1a687af526738aaba18edee0ac8 Mon Sep 17 00:00:00 2001 From: David Hart Date: Fri, 7 Apr 2017 01:07:19 +0200 Subject: [PATCH 0281/4563] Improve Interaction Between `private` Declarations and Extensions (#668) * initial version * Add swift syntax highlighting * Remove bullet point indentation * Remove mention of subclasses until disucssed * Fixed typos and wrapping * Add motivation and write compatibility section * Add clarification about subclasses * Fix typos & add precisions and formatting * Integrate Chris' participation as co-author * Rename to follow new title * Fix link in header * Add examples for extensions in another file --- ...een-private-declarations-and-extensions.md | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 proposals/XXXX-improve-interaction-between-private-declarations-and-extensions.md diff --git a/proposals/XXXX-improve-interaction-between-private-declarations-and-extensions.md b/proposals/XXXX-improve-interaction-between-private-declarations-and-extensions.md new file mode 100644 index 0000000000..972a992bc2 --- /dev/null +++ b/proposals/XXXX-improve-interaction-between-private-declarations-and-extensions.md @@ -0,0 +1,140 @@ +# Improve Interaction Between `private` Declarations and Extensions + +* Proposal: [SE-XXXX](XXXX-improve-interaction-between-private-declarations-and-extensions.md) +* Authors: [David Hart](http://github.com/hartbit), [Chris Lattner](https://github.com/lattner) +* Review Manager: TBD +* Status: TBD + +## Introduction + +In Swift 3, a declaration marked `private` may be accessed by anything nested in the scope of the private declaration, for example a private property or method defined on a struct may be accessed by methods defined within that struct. + +This model was introduced by [SE-0025](0025-scoped-access-level.md) and with nearly a year of experience using this model, it has worked well in almost all cases. The primary case it falls down is when the implementation of a type is split into a base definition and a set of extensions. Because of the SE-0025 model, extensions to the type are not allowed to access private members defined on that type. + +This proposal recommends extending `private` access control so that members defined in an extension of a type have the same access as members defined on the type itself, so long as the type and extension are in the same source file. We expect this to dramatically reduce the number of uses of `fileprivate` in practice. + +## Motivation + +[SE-0025](0025-scoped-access-level.md) defined the `private` access control level to be used for scoped access, and introduced `fileprivate` for the case when a declaration needs to be visible across declarations, but not only within a file. The goal of the proposal was for `fileprivate` to be used rarely. However, that goal of the proposal has not been realized: Swift's reliance on extensions as a grouping and conformance mechanism has made `fileprivate` more necessary than expected. + +The prevalence of `fileprivate` in practice has caused mixed reactions from Swift developers, culminating in proposal [SE-0159](0159-fix-private-access-levels.md) which suggested reverting the access control model to Swift 2’s design. That proposal was rejected for two reasons: scoped access is something that many developers use and value, and because it was seen as too large of a change given Swift 4’s source compatibility goals. + +In contrast to SE-0159, this proposal is an extremely narrow change (which is almost completely additive) to the SE-0025 model, which embraces the extension oriented design of Swift. The authors believe that this change will not preclude introduction of submodules in the future. + + +## Detailed Design + +The only change this proposal introduces is for extensions of a type defined in the same source file as the type itself. Simply, the access control scope of the extension is considered to be the access control scope of the extended type. + +This has two ramifications: + +* Declarations in one of these extensions get access to the `private` members of the type. + +* If the declaration in the extension itself is defined as `private`, then they are accessible to declarations in the type, and other extensions of that type (in the same file). + +Here is a simple code example that demonstrates this: + +```swift +struct S { + private var p: Int + + func f() { + use(g()) // ok, g() is accessible within S + } +} + +extension S { + private func g() { + use(p) // ok, g() has access to p, since it is in an extension on S. + } +} + +extension S { + func h() { + use(g()) // Ok, h() has access to g() since it defined in the access control scope for S. + } +} +``` + +Please note: + +* This visibility does **not** extend to subclasses of a class in the same file, it only affects extensions of the type itself. + +* Constrained extensions are extensions, so this visibility **does** extend to them as well. For example, the body of `extension Optional where Wrapped == String { }` would have access to `private` members of Optional, assuming the extension is defined in the same file as `Optional`. + +* This proposal does not change the behavior of extensions that are not in the same file as the type - i.e., extensions in a seperate file to the type do not share access between themselves: + +```swift +// FileA.swift +struct A { +} + +// FileB.swift +extension A { + private func foo() { + bar() // not ok, foo() does not have access to bar() + } +} + +extension A { + private func bar() { + } +} +``` + +* This proposal does not change access control behavior for types nested within each other. As in Swift 3, inner types have access to the private members of outer types, but outer types cannot refer to private members of inner types. For example: + +```swift +struct Outer { + private var outerValue = 42 + + struct Inner { + private var innerValue = 57 + + func innerTest() { + print(outerValue) // still ok. + } + } + + func test(_ i: Inner) { + print(i.innerValue) // still an error + } +} +``` + +## Source Compatibility + +In Swift 3 compatibility mode, the compiler will continue to treat `private` as before. In Swift 4 mode, the compiler will modify the semantics of `private` to follow the rules of this proposal. No migration will be necessary as this proposal merely broadens the visibility of `private`. + +Cases where a type had `private` declarations with the same signature in the same type/extension but in different scopes will produce a compiler error in Swift 4. For example, the following piece of code compiles in Swift 3 compatibilty mode but generates a `Invalid redeclaration of 'bar()'` error in Swift 4 mode: + +```swift +struct Foo { + private func bar() {} +} + +extension Foo { + private func bar() {} +} +``` + +## Alternatives Considered + +Access control has been hotly debated on the swift-evolution mailing list, both in the Swift 3 cycle (leading to [SE-0025](0025-scoped-access-level.md) and most recently in Swift 4 which led to [SE-0159](0159-fix-private-access-levels.md). There have been too many proposals to summarize here, including the introduction of a `scoped` keyword. + +The core team has made it clear that most of those proposals are not in scope for discussion in Swift 4 (or any later release), given the significant impact on source compatibility. This is the primary reason for this narrow scope proposal. + +A future direction that may be interesting to consider and debate (as a separate proposal, potentially after Swift 4) is whether extensions within the same file as a type should be treated as parts of the extended type **in general**. This would allow idioms like this, for example: + + +```swift +struct Foo { + var x: Int +} +// ... +extension Foo { + var y: Int +} +``` + +However, this is specifically **not** part of this proposal at this time. It is merely a possible avenue to consider in the future. \ No newline at end of file From e0e04f785dbf5bff138b75e9c47bf94e7db28447 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 6 Apr 2017 16:08:37 -0700 Subject: [PATCH 0282/4563] Initiate review of SE-0169: Improve Interaction Between `private` Declarations and Extensions --- ...eraction-between-private-declarations-and-extensions.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{XXXX-improve-interaction-between-private-declarations-and-extensions.md => 0169-improve-interaction-between-private-declarations-and-extensions.md} (97%) diff --git a/proposals/XXXX-improve-interaction-between-private-declarations-and-extensions.md b/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md similarity index 97% rename from proposals/XXXX-improve-interaction-between-private-declarations-and-extensions.md rename to proposals/0169-improve-interaction-between-private-declarations-and-extensions.md index 972a992bc2..1a724b223f 100644 --- a/proposals/XXXX-improve-interaction-between-private-declarations-and-extensions.md +++ b/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md @@ -1,9 +1,9 @@ # Improve Interaction Between `private` Declarations and Extensions -* Proposal: [SE-XXXX](XXXX-improve-interaction-between-private-declarations-and-extensions.md) +* Proposal: [SE-0169](0169-improve-interaction-between-private-declarations-and-extensions.md) * Authors: [David Hart](http://github.com/hartbit), [Chris Lattner](https://github.com/lattner) -* Review Manager: TBD -* Status: TBD +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (April 6...11, 2017)** ## Introduction From 2380c072ffe1766a856b6350b211061150d784da Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 6 Apr 2017 20:02:07 -0700 Subject: [PATCH 0283/4563] 0160 is accepted. --- proposals/0160-objc-inference.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0160-objc-inference.md b/proposals/0160-objc-inference.md index 572e9f185b..1f4f686968 100644 --- a/proposals/0160-objc-inference.md +++ b/proposals/0160-objc-inference.md @@ -2,7 +2,8 @@ * Proposal: [SE-0160](0160-objc-inference.md) * Author: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review March 31...April 2** +* Status: **Accepted** +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-April/000349.html) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/0389b1f49fc55b1a898701c549ce89738307b9fc/proposals/0160-objc-inference.md) * Review manager: [Chris Lattner](http://github.com/lattner) From 204d4d75942bbb2e51cc93802ffb663d36ad1140 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 7 Apr 2017 11:52:55 +0100 Subject: [PATCH 0284/4563] [SE-0160] Prevent

tags in HTML list items --- proposals/0160-objc-inference.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0160-objc-inference.md b/proposals/0160-objc-inference.md index 1f4f686968..644e3ac919 100644 --- a/proposals/0160-objc-inference.md +++ b/proposals/0160-objc-inference.md @@ -2,11 +2,10 @@ * Proposal: [SE-0160](0160-objc-inference.md) * Author: [Doug Gregor](https://github.com/DougGregor) +* Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Accepted** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-April/000349.html) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/0389b1f49fc55b1a898701c549ce89738307b9fc/proposals/0160-objc-inference.md) - -* Review manager: [Chris Lattner](http://github.com/lattner) * Implementation: [Pull request](https://github.com/apple/swift/pull/8379) * Bug: [SR-4481](https://bugs.swift.org/browse/SR-4481) From a7a6460e2e4ed2177bc6f3bdd76b4a765096deea Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Fri, 7 Apr 2017 15:38:42 -0700 Subject: [PATCH 0285/4563] Add SR for SE-153 --- ...3-compensate-for-the-inconsistency-of-nscopyings-behaviour.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md index 3023583f92..62cf0a3450 100644 --- a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md +++ b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md @@ -5,6 +5,7 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Accepted** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170227/033357.html) +* Bug: [SR-4538](https://bugs.swift.org/browse/SR-4538) `#` parameter-value-list + * parameter-value-list --> parameter-value | parameter-value-pair `::` parameter-value-list + * parameter-value-pair --> parameter `:` value + * parameter --> `proposal` | `filter` | `version` | `search` + * value --> ** Any URL-encoded text. ** + * + * For example: + * /#proposal:SE-0180,SE-0123 + * /#filter:.rejected::version:3::search:access + * + * Four types of parameters are supported: + * - proposal: A comma-separated list of proposal IDs. Treated as an 'or' search. + * - filter: A comma-separated list of proposal statuses to apply as a filter. + * - version: A comma-separated list of Swift version numbers to apply as a filter. + * - search: Raw, URL-encoded text used to filter by individual term. + * + * @param {string} fragment - A URI fragment to use as the basis for a search. + */ +function _applyFragment (fragment) { + if (!fragment) return + fragment = fragment.substring(1) // remove the # + + var actions = { proposal: [], search: null, filter: [], version: [] } + + Object.keys(actions).forEach(function (action) { + var pattern = new RegExp(action + ':([^:]+)(::|$)') + var values = fragment.match(pattern) + + if (values) { + var value = values[1] // 1st capture group from the RegExp + if (action === 'search') { + value = decodeURIComponent(value) + } else { + value = value.split(',') + } + + actions[action] = value + } + }) + + if (actions.proposal || actions.search) { + document.querySelector('#search-filter').value = actions.proposal || actions.search + } + + if (actions.version.length) { + var versionSelections = actions.version.map(function (version) { + return document.querySelector('#filter-by-swift-' + _idSafeName(version)) + }).filter(function (version) { + return !!version + }) + + versionSelections.forEach(function (versionSelection) { + versionSelection.checked = true + }) + + if (versionSelections.length) { + document.querySelector( + '#filter-by-' + states['.implemented'].className + ).checked = true + } + } + + if (actions.filter.length) { + var statusSelections = actions.filter.map(function (status) { + var state = states[status] + if (!state) return // fragment contains a nonexistent state + + return document.querySelector('#filter-by-' + state.className) + }).filter(function (status) { + return !!status + }) + + statusSelections.forEach(function (statusSelection) { + statusSelection.checked = true + }) + } + + // the version panel needs to be activated if any are specified + if (actions.version.length) { + ;['#version-options', '#version-options-label'].forEach(function (selector) { + document.querySelector('.filter-options') + .querySelector(selector).classList + .toggle('hidden') + }) + } + + // specifying any filter in the fragment should activate the filters in the UI + if (actions.version.length || actions.filter.length) { + toggleFilterPanel() + toggleFiltering() + } + + filterProposals() +} + +/** + * Writes out the current search and filter settings to document.location + * via window.replaceState. + */ +function _updateURIFragment () { + var actions = { proposal: [], search: null, filter: [], version: [] } + + var search = document.querySelector('#search-filter') + + if (search.value && search.value.match(/(SE-\d\d\d\d)($|((,SE-\d\d\d\d)+))/i)) { + actions.proposal = search.value.toUpperCase().split(',') + } else { + actions.search = search.value + } + + var selectedVersions = document.querySelectorAll('.filter-by-swift-version:checked') + var versions = [].map.call(selectedVersions, function (checkbox) { + return checkbox.value.split('swift-swift-')[1].split('-').join('.') + }) + + actions.version = versions + + var selectedStatuses = document.querySelectorAll('.filtered-by-status:checked') + var filters = [].map.call(selectedStatuses, function (checkbox) { + var className = checkbox.value + + var correspondingStatus = Object.keys(states).filter(function (status) { + if (states[status].className === className) return true + return false + })[0] + + return correspondingStatus + }) + + // .implemented is redundant if any specific implementation versions are selected. + if (actions.version.length) { + filters = filters.filter(function (status) { + return status !== '.implemented' + }) + } + + actions.filter = filters + + // build the actual fragment string. + var fragments = [] + if (actions.proposal.length) fragments.push('proposal:' + actions.proposal.join(',')) + if (actions.filter.length) fragments.push('filter:' + actions.filter.join(',')) + if (actions.version.length) fragments.push('version:' + actions.version.join(',')) + + // encoding the search lets you search for `::` and other edge cases. + if (actions.search) fragments.push('search:' + encodeURIComponent(actions.search)) + + if (!fragments.length) { + window.history.replaceState(null, null, '/') + return + } + + var fragment = '#' + fragments.join('::') + + window.history.replaceState(null, null, fragment) +} + +/** Helper to give versions like 3.0.1 an okay ID to use in a DOM element. (swift-3-0-1) */ +function _idSafeName (name) { + return 'swift-' + name.replace(/\./g, '-') +} + /** * Changes the text after 'Filtered by: ' to reflect the current status filters. * From 10df192003ac871bc3535afd264ff1b5b96bc681 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Sat, 8 Jul 2017 19:37:00 -0700 Subject: [PATCH 0400/4563] [Status] Properly populate search field from URI fragment. --- index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 2e6d8bf69e..d5be7759bc 100644 --- a/index.js +++ b/index.js @@ -779,8 +779,10 @@ function _applyFragment (fragment) { } }) - if (actions.proposal || actions.search) { - document.querySelector('#search-filter').value = actions.proposal || actions.search + if (actions.proposal.length) { + document.querySelector('#search-filter').value = actions.proposal.join(',') + } else if(actions.search) { + document.querySelector('#search-filter').value = actions.search } if (actions.version.length) { From fdbf17316b441deaf6c9754c4650e59d080e6add Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Sat, 8 Jul 2017 19:41:25 -0700 Subject: [PATCH 0401/4563] [Status] Use relative path for clearing fragments. --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index d5be7759bc..4a6e4026f7 100644 --- a/index.js +++ b/index.js @@ -889,7 +889,7 @@ function _updateURIFragment () { if (actions.search) fragments.push('search:' + encodeURIComponent(actions.search)) if (!fragments.length) { - window.history.replaceState(null, null, '/') + window.history.replaceState(null, null, './') return } From 537e908f80ebe8788513eca9be2349f33ecfb773 Mon Sep 17 00:00:00 2001 From: John McCall Date: Sun, 9 Jul 2017 02:22:24 -0400 Subject: [PATCH 0402/4563] Accept SE-0180. --- proposals/0180-string-index-overhaul.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0180-string-index-overhaul.md b/proposals/0180-string-index-overhaul.md index e18531d026..f32dd62a8e 100644 --- a/proposals/0180-string-index-overhaul.md +++ b/proposals/0180-string-index-overhaul.md @@ -3,7 +3,7 @@ * Proposal: [SE-0180](0180-string-index-overhaul.md) * Authors: [Dave Abrahams](https://github.com/dabrahams) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active review (June 22...28)** +* Status: **Accepted** * Pull Request Implementing This Proposal: https://github.com/apple/swift/pull/9806 * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/72b8d90becd60b7cc7695607ae908ef251f1e966/proposals/0180-string-index-overhaul.md) From 6a680df20b62b696f094a4347bdd43a5130f3c81 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 9 Jul 2017 16:51:45 +0100 Subject: [PATCH 0403/4563] [SE-0180] Add the Rationale link --- proposals/0180-string-index-overhaul.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/proposals/0180-string-index-overhaul.md b/proposals/0180-string-index-overhaul.md index f32dd62a8e..05ff5f0b73 100644 --- a/proposals/0180-string-index-overhaul.md +++ b/proposals/0180-string-index-overhaul.md @@ -1,15 +1,13 @@ # String Index Overhaul * Proposal: [SE-0180](0180-string-index-overhaul.md) -* Authors: [Dave Abrahams](https://github.com/dabrahams) +* Author: [Dave Abrahams](https://github.com/dabrahams) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Accepted** -* Pull Request Implementing This Proposal: https://github.com/apple/swift/pull/9806 +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170703/037942.html) +* Pull Request: [apple/swift#9806](https://github.com/apple/swift/pull/9806) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/72b8d90becd60b7cc7695607ae908ef251f1e966/proposals/0180-string-index-overhaul.md) - -*During the review process, add the following fields as needed:* - ## Introduction Today `String` shares an `Index` type with its `CharacterView` but not From 61ce6861b3931ac9301f59cf23264b067182785c Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Mon, 19 Jun 2017 18:15:30 +0530 Subject: [PATCH 0404/4563] Add Package Manager C/C++ Language Standard Support proposal --- ...NN-package-manager-cpp-language-version.md | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 proposals/NNNN-package-manager-cpp-language-version.md diff --git a/proposals/NNNN-package-manager-cpp-language-version.md b/proposals/NNNN-package-manager-cpp-language-version.md new file mode 100644 index 0000000000..7723fbca68 --- /dev/null +++ b/proposals/NNNN-package-manager-cpp-language-version.md @@ -0,0 +1,82 @@ +# Package Manager C/C++ Language Standard Support + +* Proposal: SE-NNNN +* Authors: [Ankit Aggarwal](https://github.com/aciidb0mb3r) +* Review Manager: [Daniel Dunbar](https://github.com/ddunbar) +* Status: **Discussion** + +## Introduction + +This proposal adds support for declaring the language standard for C and C++ +targets in a SwiftPM package. + +## Motivation + +The C++ language standard is one of the most important build setting needed to +compile C++ targets. We want to add some mechanism to declare it until we get +the complete build settings feature, which is deferred from the Swift 4 release. + +## Proposed solution + +We will add support to the package manifest declaration to specify the C and C++ +language standards: + +```swift +let package = Package( + name: "CHTTP", + ... + cStandard: .c89, + cxxStandard: .cxx11 +) +``` + +These settings will apply to all the C and C++ targets in the package. The +default value of these properties will be `nil`, i.e. language standard flag +will not be passed when invoking the C/C++ compiler. + +_Once we get the build settings feature, we will deprecate these properties._ + +## Detailed design + +The C/C++ language standard will be defined by the enums below and +updated as per the Clang compiler [repository](https://github.com/llvm-mirror/clang/blob/master/include/clang/Frontend/LangStandards.def). + +```swift +public enum CLanguageStandard { + case c89 + case c90 + case iso9899_1990 + case iso9899_199409 + case gnu89 + case gnu90 + case c99 + case iso9899_1999 + case gnu99 + case c11 + case iso9899_2011 + case gnu11 +} + +public enum CXXLanguageStandard { + case cxx98 + case cxx03 + case gnucxx98 + case gnucxx03 + case cxx11 + case gnucxx11 + case cxx14 + case gnucxx14 + case cxx1z + case gnucxx1z +} +``` +## Impact on existing code + +There will be no impact on existing packages because this is a new API and the +default behaviour remains unchanged. + +## Alternatives considered + +We considered adding this property at target level but we think that will +pollute the target namespace. Moreover, this is a temporary measure until we get +the build settings feature. From d89787e040731bf76e8f7378e22b4f47a2029a05 Mon Sep 17 00:00:00 2001 From: Daniel Dunbar Date: Tue, 11 Jul 2017 10:17:49 -0700 Subject: [PATCH 0405/4563] [0181] Assign proposal # and review period. --- ...ersion.md => 0181-package-manager-cpp-language-version.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{NNNN-package-manager-cpp-language-version.md => 0181-package-manager-cpp-language-version.md} (94%) diff --git a/proposals/NNNN-package-manager-cpp-language-version.md b/proposals/0181-package-manager-cpp-language-version.md similarity index 94% rename from proposals/NNNN-package-manager-cpp-language-version.md rename to proposals/0181-package-manager-cpp-language-version.md index 7723fbca68..7c47e8abcf 100644 --- a/proposals/NNNN-package-manager-cpp-language-version.md +++ b/proposals/0181-package-manager-cpp-language-version.md @@ -1,9 +1,9 @@ # Package Manager C/C++ Language Standard Support -* Proposal: SE-NNNN +* Proposal: [SE-0181](0181-package-manager-cpp-language-version.md) * Authors: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) -* Status: **Discussion** +* Status: **Active review (July 11...14)** ## Introduction From b7641b0ed38bd1f8801902d545d2cbf2994629a5 Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Wed, 12 Jul 2017 23:36:40 +0100 Subject: [PATCH 0406/4563] Newline escapes in Swift Strings (#695) * newline continuation * newline continuation * newline continuation * Trailing whitespace * Final newline * Tuning/Rewrite * Tuning/Rewrite --- proposals/XXXX-newline-escape-in-strings.md | 136 ++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 proposals/XXXX-newline-escape-in-strings.md diff --git a/proposals/XXXX-newline-escape-in-strings.md b/proposals/XXXX-newline-escape-in-strings.md new file mode 100644 index 0000000000..3cb1abb273 --- /dev/null +++ b/proposals/XXXX-newline-escape-in-strings.md @@ -0,0 +1,136 @@ +# String Newline Escaping + +* Proposal: [SE-XXXX](XXXX-newline-escape-in-strings.md) +* Authors: [John Holdsworth](https://github.com/johnno1962), [David Hart](https://github.com/hartbit), [Adrian Zubarev](https://github.com/DevAndArtist) +* Review Manager: TBD +* Status: **Awaiting review** + +* Previous Proposal: [SE-0168](0168-multi-line-string-literals.md) + +## Introduction + +This proposal introduces the ability to escape newlines in single and multi-line strings to improve readability and maintenance of source material containing excessively long lines. + +This proposal adds onto [SE-0168](0168-multi-line-string-literals.md). + +Swift-evolution thread: [Discussion thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170417/035923.html) + +## Motivation + +Escaping newlines in multi-line strings was removed from the [SE-0168](0168-multi-line-string-literals.md) proposal by the Core Team with the following rational: + +> Discussion on the list raised the idea of allowing a line to end with \ to "escape" the newline and elide it from the value of the literal; the core team had concerns about only allowing that inside multi-line literals and felt that that could also be considered later as an additive feature. + +Adding them to multi-line strings would have introduced an inconsistency with respect to conventional string literals. This proposal conforms both multi-line and conventional string construction to allow newline escaping, enabling developers to split text over multiple source lines without introducing new line breaks. This approach enhances source legibility. For example: + +```swift +// Excessively long line that requires scrolling to read +let text = """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + """ + +// Shorter lines that are easier to read, but represent the same long line +let text = """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod \ + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, \ + quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + """ +``` + +Incorporating a string continuation character is well founded, used in other development languages, and carries little risk of confusing naive users. + +## Detailed design + +This proposal introduces `\` as a line continuation character which escapes newlines matching the following regular-expression: `/\\[ \t]*\n/`. In other terms, line continuation requires a `\` character, followed by zero or more whitespace characters, followed by a newline character. All those characters are omitted from the resulting string. + +As a consequence, these rules follow: + +* All whitespace characters between `\` and the newline are disregarded. +* All whitespace characters before `\` are included in the string as is. +* An escape character at the end of the last line of a literal is an error, as no newlines follow. + +For example: + +```swift +let str1 = """↵ + line one \↵ + line two \ ↵ + line three↵ + """↵ + +let str2 = "line one \↵ +line two \ ↵ +line three"↵ + +assert(str1 == "line one line two line three") +assert(str2 == "line one line two line three") +assert(str1 == str2) +``` + +This does not affect the indentation removal feature of multiline strings and does not suggest that indentation removal should be added to conventional strings but it does give them consistent treatment. + +## Further discussions + +The following topics are related to escaping newlines. If newline-escaping where accepted it may be appropriate to revisit these decisions when considering this proposal though that shouldn't be the focus of the discussion. + +### Reconsidering stripping the last newline in multi-line strings + +When SE-0168 was reviewed, it was decided to strip the last newline in multi-line strings. Doing the opposite would have been ill-advised without a line continuation character to escape it when necessary. If this proposal is accepted, it might be worth reconsidering this decision and include the final newline in the literal. For example, it would allow easier concatenation of multi-line strings: + +```swift +var xml = """ + + + """ + +for (id, author, title, genre, price) in bookTuples { + xml += """ + + \(author) + \(title) + \(genre) + \(price) + + """ +} + +xml += """ + + """ +``` + +### Warning about trailing whitespace in multi-line strings + +During the implementation of SE-0168, it was decided not to warn about trailing whitespace in multi-line strings. One of the reasons brought up was that the only way to silence the warning was with a no-op character sequence at the end of the line; the only option back then was `\("")`, which is less than ideal. With this proposal, a slightly more elegant solution is now available: `\n\`. + +## Source compatibility + +As this proposal is additive proposing a syntax that is not currently allowed in Swift this does not affect existing source. + +## Effect on ABI stability + +This proposal does not affect ABI stability. + +## Effect on API resilience + +This proposal does not affect ABI resilience. + +## Alternatives considered + +It has been heavily debated between the authors of the proposals wether newline escaping should be supported in single-line strings. One argument against it is that the lack of indentation stripping in single-line strings forces strings to include no indentation, hindering the readability of code by visually breaking scopes when returning the column 1: + +```swift +class Messager { + let defaultMessage = "This is a long string that will wrap over multiple \ +lines. Because we don't strip indentation like with multi-line strings, the \ +author has no choice but to remove all indentation." + + func send(message: String?) { + precondition(message == nil || !message!.isEmpty, "You can't send an \ +empty message, it has no meaning.") + print(message ?? defaultMessage) + } +} +``` + +Another counter-argument is that further proposals might come up to fix this problem by introducing indentation stripping to single-line strings, muddying the distinction between single and multi-line strings. From d414e84f056350f254d11107d92db0d0346e0d1a Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 12 Jul 2017 15:46:23 -0700 Subject: [PATCH 0407/4563] Remove the "Further discussions" section because this needs to be a small scope review and those suggestions changes aren't possible to change at this point. Include a few editorial changes and kick off review. --- ...s.md => 0182-newline-escape-in-strings.md} | 57 +++++-------------- 1 file changed, 13 insertions(+), 44 deletions(-) rename proposals/{XXXX-newline-escape-in-strings.md => 0182-newline-escape-in-strings.md} (58%) diff --git a/proposals/XXXX-newline-escape-in-strings.md b/proposals/0182-newline-escape-in-strings.md similarity index 58% rename from proposals/XXXX-newline-escape-in-strings.md rename to proposals/0182-newline-escape-in-strings.md index 3cb1abb273..9bd311d86e 100644 --- a/proposals/XXXX-newline-escape-in-strings.md +++ b/proposals/0182-newline-escape-in-strings.md @@ -1,17 +1,15 @@ # String Newline Escaping -* Proposal: [SE-XXXX](XXXX-newline-escape-in-strings.md) +* Proposal: [SE-0182](0182-newline-escape-in-strings.md) * Authors: [John Holdsworth](https://github.com/johnno1962), [David Hart](https://github.com/hartbit), [Adrian Zubarev](https://github.com/DevAndArtist) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Chris Lattner](https://github.com/lattner) +* Status: **Active review (July 12...17)** * Previous Proposal: [SE-0168](0168-multi-line-string-literals.md) ## Introduction -This proposal introduces the ability to escape newlines in single and multi-line strings to improve readability and maintenance of source material containing excessively long lines. - -This proposal adds onto [SE-0168](0168-multi-line-string-literals.md). +This proposal is a refinement of [SE-0168](0168-multi-line-string-literals.md) which introduces the ability to escape newlines in single and multi-line strings to improve readability and maintenance of source material containing excessively long lines. Swift-evolution thread: [Discussion thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170417/035923.html) @@ -41,7 +39,7 @@ Incorporating a string continuation character is well founded, used in other dev ## Detailed design -This proposal introduces `\` as a line continuation character which escapes newlines matching the following regular-expression: `/\\[ \t]*\n/`. In other terms, line continuation requires a `\` character, followed by zero or more whitespace characters, followed by a newline character. All those characters are omitted from the resulting string. +This proposal introduces `\` as a line continuation character which escapes newlines matching the following regular-expression: `/\\[ \t]*\n/`. In other terms, line continuation requires a `\` character, followed by zero or more horizontal whitespace characters, followed by a newline character. All those characters are omitted from the resulting string. As a consequence, these rules follow: @@ -69,43 +67,9 @@ assert(str1 == str2) This does not affect the indentation removal feature of multiline strings and does not suggest that indentation removal should be added to conventional strings but it does give them consistent treatment. -## Further discussions - -The following topics are related to escaping newlines. If newline-escaping where accepted it may be appropriate to revisit these decisions when considering this proposal though that shouldn't be the focus of the discussion. - -### Reconsidering stripping the last newline in multi-line strings - -When SE-0168 was reviewed, it was decided to strip the last newline in multi-line strings. Doing the opposite would have been ill-advised without a line continuation character to escape it when necessary. If this proposal is accepted, it might be worth reconsidering this decision and include the final newline in the literal. For example, it would allow easier concatenation of multi-line strings: - -```swift -var xml = """ - - - """ - -for (id, author, title, genre, price) in bookTuples { - xml += """ - - \(author) - \(title) - \(genre) - \(price) - - """ -} - -xml += """ - - """ -``` - -### Warning about trailing whitespace in multi-line strings - -During the implementation of SE-0168, it was decided not to warn about trailing whitespace in multi-line strings. One of the reasons brought up was that the only way to silence the warning was with a no-op character sequence at the end of the line; the only option back then was `\("")`, which is less than ideal. With this proposal, a slightly more elegant solution is now available: `\n\`. - ## Source compatibility -As this proposal is additive proposing a syntax that is not currently allowed in Swift this does not affect existing source. +This proposal does not affect existing source, because it is purely additive - enabling syntax that is not currently allowed in Swift. ## Effect on ABI stability @@ -117,7 +81,7 @@ This proposal does not affect ABI resilience. ## Alternatives considered -It has been heavily debated between the authors of the proposals wether newline escaping should be supported in single-line strings. One argument against it is that the lack of indentation stripping in single-line strings forces strings to include no indentation, hindering the readability of code by visually breaking scopes when returning the column 1: +It has been heavily debated between the authors of the proposals whether newline escaping should be supported in single-line strings. One argument against it is that the lack of indentation stripping in single-line strings forces strings to include no indentation, hindering the readability of code by visually breaking scopes when returning the column 1: ```swift class Messager { @@ -133,4 +97,9 @@ empty message, it has no meaning.") } ``` -Another counter-argument is that further proposals might come up to fix this problem by introducing indentation stripping to single-line strings, muddying the distinction between single and multi-line strings. +Another argument against it is that lines that are sufficiently long and/or complex could use +multi-line string literals with newline escaping, so there is no need to include them in single +quoted strings. + +A counter-argument is that it is important to keep single and triple quoted strings consistent +with each other. From 2e046e3fc88dbbc7ccaa3c80a5741534b2e9ca78 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Sun, 16 Jul 2017 15:30:48 -0700 Subject: [PATCH 0408/4563] [Status] Use the normal query string format for fragment parameters. --- index.js | 77 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/index.js b/index.js index 4a6e4026f7..af1cf11c63 100644 --- a/index.js +++ b/index.js @@ -736,18 +736,18 @@ function _applyFilter (matchingProposals) { } /** - * Parses a URI fragment and applies either a search or a filter to the page. + * Parses a URI fragment and applies a search and filters to the page. * - * Syntax: - * fragment --> `#` parameter-value-list - * parameter-value-list --> parameter-value | parameter-value-pair `::` parameter-value-list - * parameter-value-pair --> parameter `:` value - * parameter --> `proposal` | `filter` | `version` | `search` + * Syntax (a query string within a fragment): + * fragment --> `#?` parameter-value-list + * parameter-value-list --> parameter-value | parameter-value-pair `&` parameter-value-list + * parameter-value-pair --> parameter `=` value + * parameter --> `proposal` | `status` | `version` | `search` * value --> ** Any URL-encoded text. ** * * For example: - * /#proposal:SE-0180,SE-0123 - * /#filter:.rejected::version:3::search:access + * /#?proposal:SE-0180,SE-0123 + * /#?status=rejected&version=3&search=access * * Four types of parameters are supported: * - proposal: A comma-separated list of proposal IDs. Treated as an 'or' search. @@ -758,13 +758,15 @@ function _applyFilter (matchingProposals) { * @param {string} fragment - A URI fragment to use as the basis for a search. */ function _applyFragment (fragment) { - if (!fragment) return - fragment = fragment.substring(1) // remove the # + if (!fragment || fragment.substr(0, 2) !== '#?') return + fragment = fragment.substring(2) // remove the #? - var actions = { proposal: [], search: null, filter: [], version: [] } + // use this literal's keys as the source of truth for key-value pairs in the fragment + var actions = { proposal: [], search: null, status: [], version: [] } + // parse the fragment as a query string Object.keys(actions).forEach(function (action) { - var pattern = new RegExp(action + ':([^:]+)(::|$)') + var pattern = new RegExp(action + '=([^=]+)(&|$)') var values = fragment.match(pattern) if (values) { @@ -779,9 +781,11 @@ function _applyFragment (fragment) { } }) + // perform key-specific parsing and checks + if (actions.proposal.length) { document.querySelector('#search-filter').value = actions.proposal.join(',') - } else if(actions.search) { + } else if (actions.search) { document.querySelector('#search-filter').value = actions.search } @@ -803,10 +807,20 @@ function _applyFragment (fragment) { } } - if (actions.filter.length) { - var statusSelections = actions.filter.map(function (status) { - var state = states[status] - if (!state) return // fragment contains a nonexistent state + // track this state specifically for toggling the version panel + var implementedSelected = false + + // update the filter selections in the nav + if (actions.status.length) { + var statusSelections = actions.status.map(function (status) { + var stateName = Object.keys(states).filter(function (state) { + return states[state].className === status + })[0] + + if (!stateName) return // fragment contains a nonexistent state + state = states[stateName] + + if (stateName === '.implemented') implementedSelected = true return document.querySelector('#filter-by-' + state.className) }).filter(function (status) { @@ -819,7 +833,7 @@ function _applyFragment (fragment) { } // the version panel needs to be activated if any are specified - if (actions.version.length) { + if (actions.version.length || implementedSelected) { ;['#version-options', '#version-options-label'].forEach(function (selector) { document.querySelector('.filter-options') .querySelector(selector).classList @@ -828,7 +842,7 @@ function _applyFragment (fragment) { } // specifying any filter in the fragment should activate the filters in the UI - if (actions.version.length || actions.filter.length) { + if (actions.version.length || actions.status.length) { toggleFilterPanel() toggleFiltering() } @@ -841,7 +855,7 @@ function _applyFragment (fragment) { * via window.replaceState. */ function _updateURIFragment () { - var actions = { proposal: [], search: null, filter: [], version: [] } + var actions = { proposal: [], search: null, status: [], version: [] } var search = document.querySelector('#search-filter') @@ -859,7 +873,7 @@ function _updateURIFragment () { actions.version = versions var selectedStatuses = document.querySelectorAll('.filtered-by-status:checked') - var filters = [].map.call(selectedStatuses, function (checkbox) { + var statuses = [].map.call(selectedStatuses, function (checkbox) { var className = checkbox.value var correspondingStatus = Object.keys(states).filter(function (status) { @@ -867,34 +881,35 @@ function _updateURIFragment () { return false })[0] - return correspondingStatus + return states[correspondingStatus].className }) // .implemented is redundant if any specific implementation versions are selected. if (actions.version.length) { - filters = filters.filter(function (status) { - return status !== '.implemented' + statuses = statuses.filter(function (status) { + return status !== states['.implemented'].className }) } - actions.filter = filters + actions.status = statuses // build the actual fragment string. var fragments = [] - if (actions.proposal.length) fragments.push('proposal:' + actions.proposal.join(',')) - if (actions.filter.length) fragments.push('filter:' + actions.filter.join(',')) - if (actions.version.length) fragments.push('version:' + actions.version.join(',')) + if (actions.proposal.length) fragments.push('proposal=' + actions.proposal.join(',')) + if (actions.status.length) fragments.push('status=' + actions.status.join(',')) + if (actions.version.length) fragments.push('version=' + actions.version.join(',')) - // encoding the search lets you search for `::` and other edge cases. - if (actions.search) fragments.push('search:' + encodeURIComponent(actions.search)) + // encoding the search lets you search for `??` and other edge cases. + if (actions.search) fragments.push('search=' + encodeURIComponent(actions.search)) if (!fragments.length) { window.history.replaceState(null, null, './') return } - var fragment = '#' + fragments.join('::') + var fragment = '#?' + fragments.join('&') + // avoid creating new history entries each time a search or filter updates window.history.replaceState(null, null, fragment) } From 46e656164c9deb0c044b7158baba3f7d0d8388b8 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 18 Jul 2017 16:32:43 -0700 Subject: [PATCH 0409/4563] Modify `String` operations to work on `Substring` (#728) * Initial draft * Update NNNN-substring-affordances.md * typo * Remove hashed containers, pending shared representation discussion --- proposals/NNNN-substring-affordances.md | 82 +++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 proposals/NNNN-substring-affordances.md diff --git a/proposals/NNNN-substring-affordances.md b/proposals/NNNN-substring-affordances.md new file mode 100644 index 0000000000..811590602b --- /dev/null +++ b/proposals/NNNN-substring-affordances.md @@ -0,0 +1,82 @@ +# Substring performance affordances + +* Proposal: [SE-NNNN](NNNN-substring-affordances.md) +* Authors: [Ben Cohen](https://github.com/airspeedswift) +* Review Manager: TBD +* Status: **Awaiting review** + +## Introduction + +This proposal modifies a small number of methods in the standard library that +are commonly used with the `Substring` type: + + - Modify the `init` on floating point and integer types, to construct them + from `StringProtocol` rather than `String`. +- Change `join` to be an extension `where Element: StringProtocol` +- Have `Substring.filter` to return a `String` + +## Motivation + +Swift 4 introduced `Substring` as the slice type for `String`. Previously, +`String` had been its own slice type, but this leads to issues where string +buffers can be unexpectedly retained. This approach was adopted instead of the +alternative of having the slicing operation make a copy. A copying slicing +operation would have negative performance consequences, and would also conflict +with the requirement that `Collection` be sliceable in constant time. In cases +where an API requires a `String`, the user must construct a new `String` from a +`Substring`. This can be thought of as a "deferral" of the copy that was +avoided at the time of the slice. + +There are a few places in the standard library where it is notably inefficient +to force a copy of a substring in order to use it with a string: joining +substrings, and converting substrings to integers. In particular, these +operations are likely to be used inside a loop over a number of substrings +extracted from a string – for example, splitting a string into substrings, +then rejoining them. + +Additionally, per SE-163, operations on `Substring` that produce a fresh string +(such as `.uppercase`) should return a `String`. This changes +`Substring.filter` to do so. + +## Proposed solution + +Add the following to the standard library: + +```swift +extension FixedWidthInteger { + public init?(_ text: S, radix: Int = 10) +} + +extension Float/Double/Float80 { + public init?(_ text: S, radix: Int = 10) +} + +extension Sequence where Element: StringProtocol { + public func joined(separator: String = "") -> String +} + +extension Substring { + public func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> String +} +``` + +These additions are deliberately narrow in scope. They are _not_ intended to +solve a general problem of being able to interchange substrings for strings (or +more generally slices for collections) generically in different APIs. See the +alternatives considered section for more on this. + +## Source compatibility + +No impact, these are generalizing an existing API to a protocol (in case of numeric conversion/joining) or modify a type newly introduced in Swift 4 (in +case of `filter`). + +## Effect on ABI stability + +The switch from conrete to generic types needs to be made before ABI stability. + +## Alternatives considered + +The goal of this proposal is to generalize existing methods that are specific +to string processing. Further affordances, such as implicit or explicit +conversions of `String` to `Substring`, might solve this problem more generally +but are considered out of scope for this proposal. From a3bfc2b6b727d8b04ce720d49f380cce65eb69bb Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 18 Jul 2017 16:34:13 -0700 Subject: [PATCH 0410/4563] kick off 0831 --- ...bstring-affordances.md => 0831-substring-affordances.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-substring-affordances.md => 0831-substring-affordances.md} (95%) diff --git a/proposals/NNNN-substring-affordances.md b/proposals/0831-substring-affordances.md similarity index 95% rename from proposals/NNNN-substring-affordances.md rename to proposals/0831-substring-affordances.md index 811590602b..bd57542bb6 100644 --- a/proposals/NNNN-substring-affordances.md +++ b/proposals/0831-substring-affordances.md @@ -1,9 +1,9 @@ # Substring performance affordances -* Proposal: [SE-NNNN](NNNN-substring-affordances.md) +* Proposal: [SE-0831](0831-substring-affordances.md) * Authors: [Ben Cohen](https://github.com/airspeedswift) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Chris Lattner](https://github.com/lattner) +* Status: **Active review (July 18...21)** ## Introduction From ef3177627e00d0d996457365a86ee1017d2fd335 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 18 Jul 2017 16:35:43 -0700 Subject: [PATCH 0411/4563] Fix proposal number. --- ...1-substring-affordances.md => 0183-substring-affordances.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{0831-substring-affordances.md => 0183-substring-affordances.md} (98%) diff --git a/proposals/0831-substring-affordances.md b/proposals/0183-substring-affordances.md similarity index 98% rename from proposals/0831-substring-affordances.md rename to proposals/0183-substring-affordances.md index bd57542bb6..eade550cea 100644 --- a/proposals/0831-substring-affordances.md +++ b/proposals/0183-substring-affordances.md @@ -1,6 +1,6 @@ # Substring performance affordances -* Proposal: [SE-0831](0831-substring-affordances.md) +* Proposal: [SE-0183](0183-substring-affordances.md) * Authors: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Active review (July 18...21)** From 2becff8efde3829194a5e0a6b15f7ac58462b23c Mon Sep 17 00:00:00 2001 From: Ling Wang Date: Tue, 18 Jul 2017 20:47:59 -0500 Subject: [PATCH 0412/4563] Fix a typo (#731) --- proposals/0180-string-index-overhaul.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0180-string-index-overhaul.md b/proposals/0180-string-index-overhaul.md index 05ff5f0b73..7af7e88fa2 100644 --- a/proposals/0180-string-index-overhaul.md +++ b/proposals/0180-string-index-overhaul.md @@ -173,7 +173,7 @@ resulting sequence of code units. let s = "e\u{301}galite\u{301}" // "égalité" let i = Array(s.unicodeScalars.indices) print(s[i[1]...]) // "◌́galité" -print(s[.. Date: Wed, 19 Jul 2017 11:27:34 -0700 Subject: [PATCH 0413/4563] [0181] Update status to accepted. - Also, apply the trivial revision we accepted. --- proposals/0181-package-manager-cpp-language-version.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/proposals/0181-package-manager-cpp-language-version.md b/proposals/0181-package-manager-cpp-language-version.md index 7c47e8abcf..6ff99ac1da 100644 --- a/proposals/0181-package-manager-cpp-language-version.md +++ b/proposals/0181-package-manager-cpp-language-version.md @@ -3,7 +3,9 @@ * Proposal: [SE-0181](0181-package-manager-cpp-language-version.md) * Authors: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) -* Status: **Active review (July 11...14)** +* Status: **Implemented (Swift 4)** +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170717/038142.html) +* Pull Request: [apple/swift-package-manager#1264](https://github.com/apple/swift-package-manager/pull/1264) ## Introduction @@ -25,8 +27,8 @@ language standards: let package = Package( name: "CHTTP", ... - cStandard: .c89, - cxxStandard: .cxx11 + cLanguageStandard: .c89, + cxxLanguageStandard: .cxx11 ) ``` From 97ddfc9f7175026ac70d5ccf2907f4939467f0a6 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 19 Jul 2017 16:38:48 -0700 Subject: [PATCH 0414/4563] 0182 is accepted with revisions. --- proposals/0182-newline-escape-in-strings.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0182-newline-escape-in-strings.md b/proposals/0182-newline-escape-in-strings.md index 9bd311d86e..0df32de6d5 100644 --- a/proposals/0182-newline-escape-in-strings.md +++ b/proposals/0182-newline-escape-in-strings.md @@ -3,7 +3,8 @@ * Proposal: [SE-0182](0182-newline-escape-in-strings.md) * Authors: [John Holdsworth](https://github.com/johnno1962), [David Hart](https://github.com/hartbit), [Adrian Zubarev](https://github.com/DevAndArtist) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Active review (July 12...17)** +* Status: **Accepted with Revision** +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-July/000393.html) * Previous Proposal: [SE-0168](0168-multi-line-string-literals.md) From f87bcb9b0f51dec13f6afc1c2f68e3a965edc4fe Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Thu, 20 Jul 2017 14:15:18 -0700 Subject: [PATCH 0415/4563] SE-0104 Revision 4 (#733) * SE-0104 Revision 4 * Update for revision review --- proposals/0104-improved-integers.md | 313 +++++++++++----------------- 1 file changed, 126 insertions(+), 187 deletions(-) diff --git a/proposals/0104-improved-integers.md b/proposals/0104-improved-integers.md index 4005db4411..9db939ea76 100644 --- a/proposals/0104-improved-integers.md +++ b/proposals/0104-improved-integers.md @@ -2,10 +2,13 @@ * Proposal: [SE-0104](0104-improved-integers.md) * Authors: [Dave Abrahams](https://github.com/dabrahams), [Maxim Moiseev](https://github.com/moiseev) -* Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Implemented (Swift 4)** +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Implemented (Swift 4). In revision review ** July 20-24 2017 * Bug: [SR-3196](https://bugs.swift.org/browse/SR-3196) -* Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/0440700fc555a6c72abb4af807c8b79fb1bec592/proposals/0104-improved-integers.md), [2](https://github.com/apple/swift-evolution/blob/957ab545e05adb94507792e7871b38e34b56a0a5/proposals/0104-improved-integers.md). +* Previous Revisions: + [1](https://github.com/apple/swift-evolution/blob/0440700fc555a6c72abb4af807c8b79fb1bec592/proposals/0104-improved-integers.md), + [2](https://github.com/apple/swift-evolution/blob/957ab545e05adb94507792e7871b38e34b56a0a5/proposals/0104-improved-integers.md), + [3](https://github.com/apple/swift-evolution/blob/80f57a6b7645126fe0220dcb91c19565e447d5d8/proposals/0104-improved-integers.md) * Discussion on swift-evolution: [here](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170109/030191.html). * Decision notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170227/033372.html) @@ -140,9 +143,9 @@ as shown in the examples below: - `x << -2` is equivalent to `x >> 2` -- `(1 as UInt8) >> 42)` will evaluate to `0` +- `(1 as UInt8) >> 42` will evaluate to `0` -- `(-128 as Int8) >> 42)` will evaluate to `0xff` or `-1` +- `(-128 as Int8) >> 42` will evaluate to `0xff` or `-1` In most scenarios, the right hand operand is a literal constant, and branches for handling under- and over-shift cases can be optimized away. For other @@ -158,61 +161,66 @@ avoid undefined behavior and produce uniform semantics across architectures. ### What's new in this revision -* [SE-0091][se91] removed the necessity to dispatch generic operators through -special methods. - - All operators are now declared by protocols as `static func`s. - -* Standard Library no longer provides `+` and `-` operators for `Strideable` -types. - - They were problematic, as one could have written mixed-type code like `let x: - Int64 = 42; x += (1 as Int)`, which would compile, but shouldn't. Besides, - since the `Stride` of an unsigned type is signed, Standard Library had to - implement a hack to make code like `let x: UInt = 42; x += (1 as Int)` - ambiguous. These operators were only necessary because they made advancing - collection indices convenient, which is no longer the case since the - introduction of the [new indexing model](0065-collections-move-indices.md) in - Swift 3. - -* Shifts and other bitwise operations were moved from `FixedWidthInteger` to -`BinaryInteger`. - - Left shift operation on an unbounded integer should infinitely extend the - number, and never drop set bits when they reach the most significant position - in the underlying representation. - -* `BitwiseOperations` protocol was deprecated. - - We believe there are no useful entities that support bitwise operations, but - at the same time are not binary integers. - -* `Arithmetic` and `SignedArithmetic` protocols have been renamed to `Numeric` - and `SignedNumeric`. - -* `minimumSignedRepresentationBitWidth` property was removed. - -* `word(at:)` methods was replaced by `var words: Words` where `Words` is a new - associated type. - - This will help avoid quadratic complexity in algorithms iterating over all - words, and also allow standard library to provide a default conformance to - `Hashable`. - -* `popcount` property was renamed to `populationCount`. - -* `trailingZeroBits` property was added to the `BinaryInteger` protocol. - - `leadingZeroBits` and `populationCount` properties are still defined by the - `FixedWidthInteger` protocol. - -* Endian-converting initializers and properties were added to the -`FixedWidthInteger` protocol. - -* Standard library introduces the new type `DoubleWidth`. - - See [this section](#doublewidtht) for more details. - +* Shift operators were reorganized + ([pull request](https://github.com/apple/swift/pull/11044)) + + - Masking shift operators were moved from `BinaryInteger` to + `FixedWidthInteger`. + - Non-masking shift operators were moved from `FixedWidthInteger` to + `BinaryInteger`. + + **Rationale:** attempts to implement masking shifts for + arbitrary-precision integers have failed because + the + [semantics aren't clear](https://gist.github.com/xwu/d68baefaae9e9291d2e65bd12ad51be2#semantics-of-masking-shifts-for-arbitrary-precision-integers). + Attempts to clarify the definition of the semantics of masking shift + for `BinaryInteger` have failed, indicating that the operation + doesn't actually make sense outside of `FixedWidthInteger`. + +* `ArithmeticOverflow` was removed in favor of using a simple `Bool`. + + **Rationale:** the enum + [proves to have poor ergonomics](https://gist.github.com/xwu/d68baefaae9e9291d2e65bd12ad51be2#arithmeticoverflow). + It only appears as part of a result tuple, where a label already + helps counteract the readability deficit usually caused by + returning an un-labeled `Bool`. + +* `BinaryInteger`'s initializers from floating point values were + changed from: + + ```swift + init?(exactly source: T) + init(_ source: T) + ``` + + to: + + ```swift + init?(exactly source: T) + init(_ source: T) + ``` + + **Rationale:** Attempts to implement these initializers for + arbitrary models of `FloatingPoint` have failed + (see + [here](https://github.com/apple/swift/blob/826f8daf4a25657965f65cbb7343e751c76fe2e1/stdlib/public/core/DoubleWidth.swift.gyb#L100-L106) and + [here](https://github.com/apple/swift/blob/826f8daf4a25657965f65cbb7343e751c76fe2e1/test/Prototypes/BigInt.swift#L145-L151)) + whereas they are + clearly + [implementable](https://github.com/apple/swift/blob/826f8daf4a25657965f65cbb7343e751c76fe2e1/stdlib/public/core/DoubleWidth.swift.gyb#L108-L159) for + models of `BinaryFloatingPoint`, suggesting that the operation + doesn't actually make sense outside of `BinaryFloatingPoint`. + +* `BinaryInteger`'s `init(extendingOrTruncating:)` was renamed to + `init(truncatingIfNeeded:)` + + **Rationale:** `extendingOrTruncating` emphasizes a part of the + semantics (“extending”) that is lossless and thus doesn't warrant + the implied warning of an argument label. The two use cases for + this initializer are intentional truncation and optimizing out range + checks that are known by the programmer to be un-needed. Both these + cases are better served by `truncatingIfNeeded`. + ### Protocols #### `Numeric` @@ -439,7 +447,7 @@ public protocol BinaryInteger : /// // y == nil /// /// - Parameter source: A floating-point value to convert to an integer. - init?(exactly source: T) + init?(exactly source: T) /// Creates an integer from the given floating-point value, truncating any /// fractional part. @@ -460,7 +468,7 @@ public protocol BinaryInteger : /// /// - Parameter source: A floating-point value to convert to an integer. /// `source` must be representable in this type after truncation. - init(_ source: T) + init(_ source: T) /// Creates an new instance from the given integer. /// @@ -490,7 +498,7 @@ public protocol BinaryInteger : /// /// let p: Int16 = -500 /// // 'p' has a binary representation of 11111110_00001100 - /// let q = Int8(extendingOrTruncating: p) + /// let q = Int8(truncatingIfNeeded: p) /// // q == 12 /// // 'q' has a binary representation of 00001100 /// @@ -501,21 +509,21 @@ public protocol BinaryInteger : /// /// let u: Int8 = 21 /// // 'u' has a binary representation of 00010101 - /// let v = Int16(extendingOrTruncating: u) + /// let v = Int16(truncatingIfNeeded: u) /// // v == 21 /// // 'v' has a binary representation of 00000000_00010101 /// /// let w: Int8 = -21 /// // 'w' has a binary representation of 11101011 - /// let x = Int16(extendingOrTruncating: w) + /// let x = Int16(truncatingIfNeeded: w) /// // x == -21 /// // 'x' has a binary representation of 11111111_11101011 - /// let y = UInt16(extendingOrTruncating: w) + /// let y = UInt16(truncatingIfNeeded: w) /// // y == 65515 /// // 'y' has a binary representation of 11111111_11101011 /// /// - Parameter source: An integer to convert to this type. - init(extendingOrTruncating source: T) + init(truncatingIfNeeded source: T) /// Creates a new instance with the representable value that's closest to the /// given integer. @@ -665,117 +673,19 @@ public protocol BinaryInteger : /// Returns the result of shifting this value's binary representation the /// specified number of digits to the right. - /// - /// In a *masking shift*, the bit pattern of the value passed as `rhs` is - /// masked to produce a value between zero and the bit width of `lhs`. The - /// shift is performed using this masked value. Masking shifts require more - /// care to use correctly than a traditional bit shift, but are likely to be - /// more efficient when used with shift amounts that are not compile-time - /// constants. On most architectures, a masking shift compiles down to a - /// single instruction. - /// - /// For example, if you shift an 8-bit, unsigned integer by 2, the shift - /// amount requires no masking. - /// - /// let x: UInt8 = 30 // 0b00011110 - /// let y = x &>> 2 - /// // y == 7 // 0b00000111 - /// - /// However, if you shift it by 11, it first bitmasks `rhs` to `3`, and then - /// uses that masked value as the number of bits to shift `x`. - /// - /// let z = x &>> 11 - /// // z == 3 // 0b00000011 - /// - /// Relationship to the Right Shift Operator - /// ---------------------------------------- - /// - /// The masking right shift operator handles attempted overshifts and - /// undershifts differently from the right shift operator (`>>`). When the - /// value passed as `rhs` in a masking shift is within the range - /// `0...> 2 - /// // y1 == 7 // 0b00000111 - /// let y2 = x >> 2 - /// // y2 == 7 // 0b00000111 - /// - /// The right shift operator does not mask its right-hand-side argument, so - /// passing `11` as `rhs` shifts all the bits of `x` to zero. - /// - /// let z1 = x &>> 11 - /// // z1 == 240 // 0b00000011 - /// let z2 = x >> 11 - /// // z2 == 0 // 0b00000000 - /// - /// - Parameter rhs: The number of bits to shift this value to the right. If - /// `rhs` is outside the range `0..>` - static func &>>(_ lhs: Self, _ rhs: Self) -> Self - static func &>>=(_ lhs: inout Self, _ rhs: Self) + static func >>(_ lhs: Self, _ rhs: RHS) -> Self - /// Returns the result of shifting this value's binary representation the + /// Stores the result of shifting a value's binary representation the + /// specified number of digits to the right in the left-hand-side variable. + static func >>=(_ lhs: inout Self, _ rhs: RHS) + + /// Returns the result of shifting a value's binary representation the /// specified number of digits to the left. - /// - /// In a *masking shift*, the bit pattern of the value passed as `rhs` is - /// masked to produce a value between zero and the bit width of `lhs`. The - /// shift is performed using this masked value. Masking shifts require more - /// care to use correctly than a traditional bit shift, but are likely to be - /// more efficient when used with shift amounts that are not compile-time - /// constants. On most architectures, a masking shift compiles down to a - /// single instruction. - /// - /// For example, if you shift an 8-bit, unsigned integer by 2, the shift - /// amount requires no masking. - /// - /// let x: UInt8 = 30 // 0b00011110 - /// let y = x &>> 2 - /// // y == 120 // 0b01111000 - /// - /// However, if you shift it by 11, it first bitmasks `rhs` to `3`, and then - /// uses that masked value as the number of bits to shift `x`. - /// - /// let z = x &<< 11 - /// // z == 240 // 0b11110000 - /// - /// Relationship to the Left Shift Operator - /// --------------------------------------- - /// - /// The masking left shift operator handles attempted overshifts and - /// undershifts differently from the left shift operator (`<<`). When the - /// value passed as `rhs` in a masking shift is within the range - /// `0...>`, `<<` - static func &<<(_ lhs: Self, _ rhs: Self) -> Self - static func &<<=(_ lhs: inout Self, _ rhs: Self) + static func << (_ lhs: Self, _ rhs: RHS) -> Self + + /// Stores the result of shifting a value's binary representation the + /// specified number of digits to the left in the left-hand-side variable + static func <<= (_ lhs: inout Self, _ rhs: RHS) /// Returns the quotient and remainder of this value divided by the given /// value. @@ -862,7 +772,7 @@ public protocol FixedWidthInteger : BinaryInteger { /// /// - SeeAlso: `+` func addingReportingOverflow(_ other: Self) - -> (partialValue: Self, overflow: ArithmeticOverflow) + -> (partialValue: Self, overflow: Bool) /// Returns the difference of this value and the given value along with a /// flag indicating whether overflow occurred in the operation. @@ -877,7 +787,7 @@ public protocol FixedWidthInteger : BinaryInteger { /// /// - SeeAlso: `-` func subtractingReportingOverflow(_ other: Self) - -> (partialValue: Self, overflow: ArithmeticOverflow) + -> (partialValue: Self, overflow: Bool) /// Returns the product of this value and the given value along with a flag /// indicating whether overflow occurred in the operation. @@ -892,7 +802,7 @@ public protocol FixedWidthInteger : BinaryInteger { /// /// - SeeAlso: `*`, `multipliedFullWidth(by:)` func multipliedReportingOverflow(by other: Self) - -> (partialValue: Self, overflow: ArithmeticOverflow) + -> (partialValue: Self, overflow: Bool) /// Returns the quotient of dividing this value by the given value along with /// a flag indicating whether overflow occurred in the operation. @@ -909,7 +819,7 @@ public protocol FixedWidthInteger : BinaryInteger { /// /// - SeeAlso: `/`, `dividingFullWidth(_:)` func dividedReportingOverflow(by other: Self) - -> (partialValue: Self, overflow: ArithmeticOverflow) + -> (partialValue: Self, overflow: Bool) /// Returns a double-width value containing the high and low parts of the /// result of multiplying this value by an argument. @@ -1009,6 +919,27 @@ public protocol FixedWidthInteger : BinaryInteger { /// A representation of this integer with the byte order swapped. var byteSwapped: Self { get } + + + /// Returns the result of shifting a value's binary representation the + /// specified number of digits to the right, masking the shift amount to the + /// type's bit width. + static func &>>(_ lhs: Self, _ rhs: Self) -> Self + + /// Calculates the result of shifting a value's binary representation the + /// specified number of digits to the right, masking the shift amount to the + /// type's bit width, and stores the result in the left-hand-side variable. + static func &>>=(_ lhs: inout Self, _ rhs: Self) + + /// Returns the result of shifting a value's binary representation the + /// specified number of digits to the left, masking the shift amount to the + /// type's bit width. + static func &<<(_ lhs: Self, _ rhs: Self) -> Self + + /// Returns the result of shifting a value's binary representation the + /// specified number of digits to the left, masking the shift amount to the + /// type's bit width, and stores the result in the left-hand-side variable. + static func &<<=(_ lhs: inout Self, _ rhs: Self) } ``` @@ -1059,24 +990,32 @@ case let (high, low) = doubleWidthValue ### Extra operators In addition to the operators described in the [protocols section](#protocols), -we also provide a few extensions that are not protocol requirements: +we also provide a few extensions: + +#### Non-mutating homogeneous shifts + +```Swift +extension FixedWidthInteger { + public static func &>> (lhs: Self, rhs: Self) -> Self + public static func &<< (lhs: Self, rhs: Self) -> Self +``` #### Heterogeneous shifts ```Swift extension BinaryInteger { - // Masking shifts - static func &>> (lhs: Self, rhs: Other) -> Self - static func &>>= (lhs: inout Self, rhs: Other) - static func &<< (lhs: Self, rhs: Other) -> Self - static func &<<= (lhs: inout Self, rhs: Other) - // 'Smart' shifts static func >> (lhs: Self, rhs: Other) -> Self static func >>= (lhs: inout Self, rhs: Other) static func << (lhs: Self, rhs: Other) -> Self static func <<= (lhs: inout Self, rhs: Other) } +extension FixedWidthInteger { + public static func &>> (lhs: Self, rhs: Other) -> Self + public static func &>>= (lhs: inout Self, rhs: Other) + public static func &<< (lhs: Self, rhs: Other) -> Self + public static func &<<= (lhs: inout Self, rhs: Other) +} ``` #### Heterogeneous equality and comparison From 91f8ed0620e438712e5d72b20a65a68d6e0a8a1d Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Thu, 20 Jul 2017 15:32:57 -0700 Subject: [PATCH 0416/4563] Update status of SE-0104 to follow the template in process.md --- proposals/0104-improved-integers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0104-improved-integers.md b/proposals/0104-improved-integers.md index 9db939ea76..d543792518 100644 --- a/proposals/0104-improved-integers.md +++ b/proposals/0104-improved-integers.md @@ -3,7 +3,7 @@ * Proposal: [SE-0104](0104-improved-integers.md) * Authors: [Dave Abrahams](https://github.com/dabrahams), [Maxim Moiseev](https://github.com/moiseev) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Implemented (Swift 4). In revision review ** July 20-24 2017 +* Status: **Active review (July 20...24)** * Bug: [SR-3196](https://bugs.swift.org/browse/SR-3196) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/0440700fc555a6c72abb4af807c8b79fb1bec592/proposals/0104-improved-integers.md), From f578b72f886e8bcdb90c57c7fa4a2a920e8f35c3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 26 Jul 2017 13:21:47 -0700 Subject: [PATCH 0417/4563] 0183 is accepted. --- proposals/0183-substring-affordances.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0183-substring-affordances.md b/proposals/0183-substring-affordances.md index eade550cea..32866b5725 100644 --- a/proposals/0183-substring-affordances.md +++ b/proposals/0183-substring-affordances.md @@ -1,9 +1,10 @@ # Substring performance affordances * Proposal: [SE-0183](0183-substring-affordances.md) -* Authors: [Ben Cohen](https://github.com/airspeedswift) +* Author: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Active review (July 18...21)** +* Status: **Accepted** +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-July/000395.html) ## Introduction From a04f4c2004c53fbb2dc276672ea403f5d28a361c Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Wed, 26 Jul 2017 14:45:19 -0700 Subject: [PATCH 0418/4563] [Status] Display returned-for-revision proposals after implemented ones. Proposals that are returned for revision aren't necessarily being actively discussed, unlike the other three orange/active states. --- index.css | 4 ++-- index.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/index.css b/index.css index 6c00d78c32..844f4f048b 100644 --- a/index.css +++ b/index.css @@ -589,8 +589,8 @@ section ul, section li { border-color: rgb(255, 149, 0); } .color-returned-for-revision { - color: rgb(255, 149, 0); - border-color: rgb(255, 149, 0); + color: rgb(88, 86, 214); + border-color: rgb(88, 86, 214); } .color-deferred { diff --git a/index.js b/index.js index af1cf11c63..8445ed0146 100644 --- a/index.js +++ b/index.js @@ -189,8 +189,8 @@ function renderNav () { // .acceptedWithRevisions proposals are combined in the filtering UI // with .accepted proposals. var checkboxes = [ - '.awaitingReview', '.scheduledForReview', '.activeReview', '.returnedForRevision', '.accepted', - '.implemented', '.deferred', '.rejected', '.withdrawn' + '.awaitingReview', '.scheduledForReview', '.activeReview', '.accepted', + '.implemented', '.returnedForRevision', '.deferred', '.rejected', '.withdrawn' ].map(function (state) { var className = states[state].className @@ -257,8 +257,8 @@ function renderBody () { var proposalAttachPoint = article.querySelector('.proposals-list') var proposalPresentationOrder = [ - '.awaitingReview', '.scheduledForReview', '.activeReview', '.returnedForRevision', '.accepted', - '.acceptedWithRevisions', '.implemented', '.deferred', '.rejected', '.withdrawn' + '.awaitingReview', '.scheduledForReview', '.activeReview', '.accepted', + '.acceptedWithRevisions', '.implemented', '.returnedForRevision', '.deferred', '.rejected', '.withdrawn' ] proposalPresentationOrder.map(function (state) { From e77bbeb040217081a0b20ce48688668448dd2bf7 Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Sun, 30 Jul 2017 04:24:25 +0800 Subject: [PATCH 0419/4563] Fix typo (#735) --- .../0158-package-manager-manifest-api-redesign.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/0158-package-manager-manifest-api-redesign.md b/proposals/0158-package-manager-manifest-api-redesign.md index d2103ef4d9..07e274837f 100644 --- a/proposals/0158-package-manager-manifest-api-redesign.md +++ b/proposals/0158-package-manager-manifest-api-redesign.md @@ -304,7 +304,7 @@ access modifier is `public` for all APIs unless specified. private init(name: String, type: LibraryType? = nil, targets: [String]) } - /// Create a libary product. + /// Create a library product. static func library(name: String, type: LibraryType? = nil, targets: [String]) -> Library /// Create an executable product. @@ -508,8 +508,8 @@ access modifier is `public` for all APIs unless specified. name: "Paper", products: [ .executable(name: "tool", targets: ["tool"]), - .libary(name: "Paper", type: .static, targets: ["Paper"]), - .libary(name: "PaperDy", type: .dynamic, targets: ["Paper"]), + .library(name: "Paper", type: .static, targets: ["Paper"]), + .library(name: "PaperDy", type: .dynamic, targets: ["Paper"]), ], dependencies: [ .package(url: "http://github.com/SwiftyJSON/SwiftyJSON", from: "1.2.3"), @@ -549,9 +549,9 @@ let package = Package( name: "Paper", products: [ .executable(name: "tool", targets: ["tool"]), - .libary(name: "Paper", targets: ["Paper"]), - .libary(name: "PaperStatic", type: .static, targets: ["Paper"]), - .libary(name: "PaperDynamic", type: .dynamic, targets: ["Paper"]), + .library(name: "Paper", targets: ["Paper"]), + .library(name: "PaperStatic", type: .static, targets: ["Paper"]), + .library(name: "PaperDynamic", type: .dynamic, targets: ["Paper"]), ], dependencies: [ .package(url: "http://github.com/SwiftyJSON/SwiftyJSON", from: "1.2.3"), From d84e158bde38944f48ed7dd7df0c032653489ac2 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Mon, 31 Jul 2017 15:31:40 -0400 Subject: [PATCH 0420/4563] Create 0184-improved-pointers.md (#736) --- proposals/0184-improved-pointers.md | 382 ++++++++++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 proposals/0184-improved-pointers.md diff --git a/proposals/0184-improved-pointers.md b/proposals/0184-improved-pointers.md new file mode 100644 index 0000000000..83dfbcddff --- /dev/null +++ b/proposals/0184-improved-pointers.md @@ -0,0 +1,382 @@ +# Improved pointers + +* Proposal: [SE-0184](0184-improved-pointers.md) +* Authors: [Kelvin Ma](https://github.com/kelvin13) (“*Taylor Swift*”) +* Review Manager: TBD +* Status: **Awaiting review** + +## Introduction + +Swift’s pointer types are an important interface for low-level memory manipulation, but the current API design is not very consistent, complete, or convenient. Many memory methods demand a `capacity:` or `count:` argument, forcing the user to manually track the size of the memory block, even though most of the time this is either unnecessary, or redundant as buffer pointers track this information natively. In some places, poor naming choices and overengineered function signatures compromise memory safety by leading users to believe that they have allocated or freed memory when in fact, they have not. + +This proposal seeks to improve the Swift pointer API by ironing out naming inconsistencies, adding sensible default argument values, adding missing methods, and reducing excessive verbosity, offering a more convenient, more sensible, and less bug-prone API. + +The [previous draft](https://gist.github.com/kelvin13/a9c033193a28b1d4960a89b25fbffb06) of this proposal was relatively source-breaking, calling for a separation of functionality between singular pointer types and vector (buffer) pointer types. This proposal instead separates functionality between internally-tracked length pointer types and externally-tracked length pointer types. This results in an equally elegant API with about one-third less surface area. + +Swift-evolution thread: [Pitch: Improved Swift pointers](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170710/038013.html), [Pitch: More Improved Swift pointers](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170717/038121.html) + +## Motivation + +Right now, `UnsafeMutableBufferPointer` is kind of a black box when it comes to producing and modifying instances of it. To create, bind, allocate, initialize, deinitialize, and deallocate them, you have to extract `baseAddress`es and `count`s. This is unfortunate because `UnsafeMutableBufferPointer` provides a handy container for tracking the size of a memory buffer, but to actually make use of this information, the buffer pointer must be disassembled. In practice, this means the use of memory buffers requires frequent (and annoying) conversion back and forth between buffer pointers and base address–count pairs. For example, to move-initialize memory between two buffer pointers, you have to write this: + +```swift +buffer1.baseAddress?.moveInitialize(from: buffer2.baseAddress!, count: buffer1.count) +``` + +The `?` is sometimes exchanged with an `!` depending on the personality of the author, as normally, neither operator is meaningful here — the `baseAddress` is never `nil` if the buffer pointer was created around an instance of `UnsafeMutablePointer`. + +Memory buffer allocation is especially painful, since it requires the creation of a temporary `UnsafeMutablePointer` instance. This means that the following “idiom” is very common in Swift code: + +```swift +let buffer = UnsafeMutableBufferPointer(start: UnsafeMutablePointer.allocate(capacity: byteCount), count: byteCount) +``` + +Aside from being extremely long and unwieldy, and requiring the creation of a temporary, `byteCount` must appear twice. + +You can’t even cast buffer pointer types to their mutable or immutable forms without creating a temporary. + +```swift + +var mutableBuffer = UnsafeMutableBufferPointer(start: UnsafeMutablePointer(mutating: immutableBuffer.baseAddress!), count: immutableBuffer.count) +``` + +Currently, memory is deallocated by an instance method on `UnsafeMutablePointer`, `deallocate(count:)`. Like much of the Swift pointer API, performing this operation on a buffer pointer requires extracting `baseAddress!` and `count`. It is very common for the allocation code above to be immediately followed by: + +```swift +defer +{ + buffer.baseAddress?.deallocate(capacity: buffer.count) +} +``` + +This method is extremely problematic because nearly all users, on first seeing the signature of `deallocate(capacity:)`, will naturally conclude from the `capacity` label that `deallocate(capacity:)` is equivalent to some kind of `realloc()` that can only shrink the buffer. However this is not the actual behavior — `deallocate(capacity:)` actually *ignores* the `capacity` argument and just calls `free()` on `self`. The current API is not only awkward and suboptimal, it is *misleading*. You can write perfectly legal Swift code that shouldn’t segfault, but still can, for example + +```swift +var ptr = UnsafeMutablePointer.allocate(capacity: 1000000) +ptr.initialize(to: 13, count: 1000000) +ptr.deallocate(capacity: 500000) // deallocate the second half of the memory block +ptr[0] // segmentation fault +``` + +where the first 500000 addresses should still be valid if the [documentation](https://developer.apple.com/documentation/swift/unsafemutablepointer/2295090-deallocate) is to be read literally. + +Users who are *aware* of this behavior may also choose to disregard the `capacity` argument and write things like this: + +```swift +defer +{ + buffer.baseAddress?.deallocate(capacity: 42) +} +``` + +which is functionally equivalent. However this will lead to disastrous source breakage if the implementation of `deallocate(capacity:)` is ever “corrected”. Since the API would not change, such code would still compile, but suddenly start failing at runtime. Thus, the current API, combined with incorrect documentation, is serving as a vector for introducing memory bugs into Swift code. + +Inconsistencies exist in the memorystate functions. The `initialize(from:count:)` method on `UnsafeMutablePointer` has a repeating variant, `initialize(to:count:)`, but `assign(from:count:)` has no such variant, even though it would make just as much sense for it to have one. + +While most of the memorystate functions are absent from `UnsafeMutableBufferPointer`, there is *one* strange exception — `UnsafeMutableBufferPointer` features an `initialize(from:)` method which takes a `Sequence`. This method was originally an inhabitant of `UnsafeMutablePointer` before it was [supposedly](https://github.com/apple/swift-evolution/blob/master/proposals/0147-move-unsafe-initialize-from.md#detailed-design) moved to `UnsafeMutableBufferPointer` by [SE-147](https://github.com/apple/swift-evolution/blob/master/proposals/0147-move-unsafe-initialize-from.md). This decision appears to have never been carried out, as the type still features an active `initialize(from:)` method which takes a `Collection`. (The original SE-147 method took a `Collection`.) + +Finally, the naming of some `UnsafeMutableRawPointer` members deserves a second look. While the original API intended to introduce a naming convention where `bytes` refers to uninitialized memory, `capacity` to uninitialized elements, and `count` to initialized elements, the actual usage of the three words does not always agree. In `copyBytes(from:count:)`, `count` refers to the number of *bytes*, which may or may not be initialized. Similarly, the `UnsafeMutableRawBufferPointer` `allocate(count:)` type method includes a `count` argument which actually refers to uninitialized bytes. + +## Proposed solution + +The presence of an associated `count` variable in `UnsafeMutableBufferPointer`, and the absence of it in `UnsafeMutablePointer` leads to a very natural and elegant set of memory APIs. Because buffer length is tracked externally when using `UnsafeMutablePointer`, memory methods on it should explicitly ask for the sizing parameter. Conversely, because buffer length is tracked internally by `UnsafeMutableBufferPointer`, memory methods on it should supply the buffer’s own `count` property for the operation’s sizing parameter. This means you would call + +```swift +ptr1.initialize(from: ptr2, count: count) +``` + +on an `UnsafeMutablePointer`, but + +```swift +buffer1.initialize(from: buffer2) +``` + +on an `UnsafeMutableBufferPointer`. This differs from the [previous draft](https://gist.github.com/kelvin13/a9c033193a28b1d4960a89b25fbffb06) of this proposal, in that this expansion is more additive and less source-breaking, preserving the much of the sized API present on `UnsafeMutablePointer`. + +In detail, the following changes should be made: + +- **remove the `capacity` parameter from `deallocate(capacity:)` and `deallocate(bytes:alignedTo:)`** + +Removing `capacity` from `deallocate(capacity:)` will end the confusion over what `deallocate()` does, making it obvious that `deallocate()` will free the *entire* memory block at `self`, just as if `free()` were called on it. + +The old `deallocate(capacity:)` method should be marked as `unavailable` since it currently encourages dangerously incorrect code. This avoids misleading future users, forces current users to address this potentially catastrophic memory bug, and leaves the possibility open for us to add a `deallocate(count:)` method in the future, or perhaps even a `reallocate(toCount:)` method. + +Along similar lines, the `bytes` and `alignedTo` parameters should be removed from the `deallocate(bytes:alignedTo:)` method on `UnsafeMutableRawPointer`. + +- **add unsized memory methods to `UnsafeMutableBufferPointer`** + +The following methods will be added to `UnsafeMutableBufferPointer`, giving it parity with `UnsafeMutablePointer`. + +```swift +static func allocate(capacity:Int) -> UnsafeMutableBufferPointer +func deallocate() + +func assign(from:UnsafeBufferPointer) +func moveAssign(from:UnsafeMutableBufferPointer) +func moveInitialize(from:UnsafeMutableBufferPointer) +func initialize(from:UnsafeBufferPointer) +func initialize(to:Element) +func deinitialize() +``` + +Where there would have been a `capacity:` or `count:` argument on these methods, this value will be filled by the buffer pointer’s own `count` property. For the binary operations `assign(from:)`, `moveAssign(from:)`, `moveInitialize(from:)`, and `initialize(from:)`, it is assumed that the other buffer pointer contains *at least* as many elements as `self` does. + +- **add an `assign(to:count:)` method to `UnsafeMutablePointer` and an `assign(to:)` method to `UnsafeMutableBufferPointer`** + +This addresses the missing assignment analogues to the `initialize(to:count:)` and `initialize(to:)` methods. + +- **add a default value of `1` to all size parameters on `UnsafeMutablePointer` and applicable size parameters on `UnsafeMutableRawPointer`** + +Since the most common use case for plain pointers is to manage one single instance of a type, the size parameters on `UnsafeMutablePointer`’s memory methods are good candidates for a default value of `1`. Any size parameter on `UnsafeMutableRawPointer`’s memory methods which take a stride quantity should also receive a default value of `1`. The size parameters in `UnsafeMutableRawPointer`’s other methods should not receive a default value as they refer to byte quantities. + +- **rename `copyBytes(from:count:)` to `copy(from:bytes:)`** + +To reduce the inconsistency in our use of the words `bytes`, `count`, and `capacity`, we will enforce the convention that: + +* `bytes` refers to, well, a byte quantity that is *not assumed* to be initialized. +* `capacity` refers to a strided quantity that is *not assumed* to be initialized. +* `count` refers to a strided quantity that is *assumed* to be initialized. + +Since this makes the word “bytes” occur twice in `copyBytes(from:bytes:)`, we should drop the “Bytes” suffix and further rename the method to `copy(from:bytes:)`. Since `UnsafeMutableRawPointer` is inherently untyped, it is obvious that any memory transfer operation on it is a bytewise operation so the “Bytes” suffix adds only verbosity and no clarity. An unsized version of this method will also be added to `UnsafeMutableRawBufferPointer`. + +We do not rename the `count` property on `UnsafeMutableRawBufferPointer` to `bytes` since this could be confused with the actual buffer data. + +- **rename `count` in `UnsafeMutableRawBufferPointer.allocate(count:)` to `bytes` and add an `alignedTo` parameter to make it `UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)`** + +This brings it in line with the `UnsafeMutableRawPointer` allocator, and avoids the contradictory and inconsistent use of `count` to represent a byte quantity. Currently `UnsafeMutableRawBufferPointer.allocate(count:)` aligns to the size of `Int`, an assumption not shared by its plain variant. + +- **fix the ordering of the arguments in `initializeMemory(as:at:count:to:)` and rename the argument `to:` to `repeating:` in all repeating memorystate functions** + +The ordering of the `to:` and `count:` argument labels in the `initializeMemory(as:at:count:to:)` method on `UnsafeMutableRawPointer` contradicts the rest of the Swift pointer API, where `to:` precedes `count:`. + +Because the ordering `initializeMemory(as:at:to:count:)` conflicts with the use of `to:` as the argument label for a target type, this argument should be renamed to `repeating:`. The word `repeating:` is much more clear in terms of describing the methods’ behavior, and is consistent with the use of the word in the `Array` API. + +- **add the sized memorystate functions `withMemoryRebound(to:count:_:)` to `UnsafeMutableBufferPointer`, and `initializeMemory(as:at:repeating:count:)`, `initializeMemory(as:from:count:)` `moveInitializeMemory(as:from:count:)`, and `bindMemory(to:count:)` to `UnsafeMutableRawBufferPointer`** + +Since buffer pointers track their own `count`, this value is a natural fit for the `count:` argument in most of the memorystate functions. However, since raw buffer pointers don’t inherently “know” how many instances of an arbitrary type fit in themselves, `initializeMemory(as:at:repeating:count:)` and `bindMemory(to:capacity:)` should retain their size parameters. + +For similar reasons, `UnsafeMutableBufferPointer.withMemoryRebound(to:capacity:_:)` keeps its size parameter because `capacity` may be different from `count` with a differently laid out type. + +- **add a `init(mutating:)` initializer to `UnsafeMutableBufferPointer`** + +This makes it much easier to make a mutable copy of an immutable buffer pointer. Such an initializer already exists on `UnsafeMutableRawBufferPointer`, so adding one to `UnsafeMutableBufferPointer` is also necessary for consistency. The reverse initializer, from `UnsafeMutableBufferPointer` to `UnsafeBufferPointer` should also be added for completeness. + +- **add mutable overloads to non-vacating memorystate method arguments on `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer`** + +Non vacating memorystate operations such as `assign(from:)` and `initialize(from:)` on `UnsafeMutableBufferPointer`, and `copy(from:)` and `initializeMemory(at:from:count:)` on `UnsafeMutableRawBufferPointer` currently take immutable source arguments. They should be overloaded to accept mutable source arguments to reduce the need for pointer immutability casts. Currently, for plain pointers, this is covered by a compiler subtyping relationship between `UnsafePointer` and `UnsafeMutablePointer`. No such relationship exists between `UnsafeBufferPointer` and `UnsafeMutableBufferPointer` or their raw counterparts. + +- **finally deprecate `initialize(from:)` from `UnsafeMutablePointer`** + +This method was supposed to be deprecated per [SE-147](https://github.com/apple/swift-evolution/blob/master/proposals/0147-move-unsafe-initialize-from.md). Better late than never. + + +## What this proposal does not do + +- **add missing memorystate functions that take `Collection`s and `Sequence`s** + +This proposal also does not address the missing `Collection`/`Sequence` memorystate functions, although it does call for (finally) removing `UnsafeMutablePointer.initialize(from:)`. This is out of scope for this proposal, and the missing functions can always be filled in at a later date, as part of a purely additive proposal. + +- **remove subscripts from `UnsafePointer` and `UnsafeMutablePointer`** + +There are strong arguments for removing subscript capability from plain pointer types. + +> Subscripts on `UnsafePointer` and `UnsafeMutablePointer` are inconsistent with their intended purpose. For example, `ptr[0]` and `ptr.pointee` both do the same thing. Furthermore, it is not immediately obvious from the grammar that the subscript parameter is an *offset*. New users may conclude that subscripting a pointer at `[89]` dereferences the memory at *address* `0x0000000000000059`! It would make more sense to use pointer arithmetic and the `pointee` property to access memory at an offset from `self` than to allow subscripting. + +> C programmers who defend this kind of syntax are reminded that many things obvious to C programmers, are obvious to *only* C programmers. The trend in Swift’s design is to separate C’s array–pointer duality; pointer subscripts run counter to that goal. + +> There is an argument that singular pointer subscripts are useful when singular pointers of unknown length are returned by C APIs. The counter-argument is that you almost never need to do random access into a vector of unknown length, rather you would iterate one element at a time until you reach the end. This lends itself well to pointer incrementation and the `pointee` property rather than subscripting. + +```swift +while ptr.pointee != sentinel +{ + ... + + ptr += 1 +} +``` + +However, this is a source breaking change, and there are (rare) cases where this syntax is helpful, for example, when dealing with multi-element strides. + +```swift +var pixel:UnsafeMutablePointer = base +while pixel < base + size +{ + // assign the RGBA color #ff1096ff + pixel[0] = 0xff + pixel[1] = 0x10 + pixel[2] = 0x96 + pixel[3] = 0xff + pixel += 4 +} +``` + +## Detailed design + +```diff +struct UnsafeMutablePointer +{ +--- static func allocate(capacity:Int) -> UnsafeMutablePointer ++++ static func allocate(capacity:Int = 1) -> UnsafeMutablePointer +--- func deallocate(capacity _:Int) ++++ func deallocate() + +--- func initialize(from:C) + ++++ func assign(repeating:Pointee, count:Int = 1) + +--- func assign(from:UnsafePointer, count:Int) +--- func moveAssign(from:UnsafeMutablePointer, count:Int) +--- func moveInitialize(from:UnsafeMutablePointer, count:Int) +--- func initialize(from:UnsafePointer, count:Int) +--- func initialize(repeating:Element, count:Int) +--- func deinitialize(count:Int) +--- func withMemoryRebound(to:T.Type, count:Int, _ body:(UnsafeMutablePointer) -> Result) + ++++ func assign(from:UnsafePointer, count:Int = 1) ++++ func moveAssign(from:UnsafeMutablePointer, count:Int = 1) ++++ func moveInitialize(from:UnsafeMutablePointer, count:Int = 1) ++++ func initialize(from:UnsafePointer, count:Int = 1) ++++ func initialize(repeating:Element, count:Int = 1) ++++ func deinitialize(count:Int = 1) ++++ func withMemoryRebound(to:T.Type, count:Int = 1, _ body:(UnsafeMutablePointer) -> Result) +} + +struct UnsafeMutableRawPointer +{ +--- func deallocate(bytes _:Int, alignedTo _:Int) ++++ func deallocate() + +--- func copyBytes(from:UnsafeRawPointer, count:Int) ++++ func copy(from:UnsafeRawPointer, bytes:Int) +--- func initializeMemory(as:T.Type, at:Int, count:Int, to:T) ++++ func initializeMemory(as:T.Type, at:Int, repeating:T, count:Int = 1) + +--- func initializeMemory(as:C.Element.Type, from:C) + +--- func bindMemory(to:T.Type, count:Int) +--- func initializeMemory(as:T.Type, from:UnsafePointer, count:Int) +--- func moveInitializeMemory(as:T.Type, from:UnsafeMutablePointer, count:Int) + ++++ func bindMemory(to:T.Type, count:Int = 1) ++++ func initializeMemory(as:T.Type, from:UnsafePointer, count:Int = 1) ++++ func moveInitializeMemory(as:T.Type, from:UnsafeMutablePointer, count:Int = 1) +} + +struct UnsafeBufferPointer +{ ++++ init(_:UnsafeMutableBufferPointer) +} + +struct UnsafeMutableBufferPointer +{ ++++ init(mutating:UnsafeBufferPointer) + ++++ static func allocate(capacity:Int) -> UnsafeMutableBufferPointer ++++ func deallocate() + ++++ func assign(from:UnsafeBufferPointer) ++++ func assign(from:UnsafeMutableBufferPointer) ++++ func assign(repeating:Pointee) ++++ func moveAssign(from:UnsafeMutableBufferPointer) ++++ func moveInitialize(from:UnsafeMutableBufferPointer) ++++ func initialize(from:UnsafeBufferPointer) ++++ func initialize(from:UnsafeMutableBufferPointer) ++++ func initialize(repeating:Element) ++++ func deinitialize() ++++ func withMemoryRebound ++++ (to:T.Type, count:Int, _ body:(UnsafeMutableBufferPointer) -> Result) +} + +struct UnsafeMutableRawBufferPointer +{ +--- static func allocate(count:Int) -> UnsafeMutableRawBufferPointer ++++ static func allocate(bytes:Int, alignedTo:Int) -> UnsafeMutableRawBufferPointer + func deallocate() ++++ func bindMemory(to:Element.Type, count:Int) +--- func copyBytes(from: UnsafeRawBufferPointer) ++++ func copy(from:UnsafeRawBufferPointer) ++++ func copy(from:UnsafeMutableRawBufferPointer) ++++ func initializeMemory(as:Element.Type, at:Int, repeating:Element, count:Int) ++++ func initializeMemory(as:Element.Type, from:UnsafeBufferPointer, count:Int) ++++ func initializeMemory(as:Element.Type, from:UnsafeMutableBufferPointer, count:Int) ++++ func moveInitializeMemory(as:Element.Type, from:UnsafeMutableBufferPointer, count:Int) +} +``` + +## Source compatibility + +Some parts of this proposal are source breaking. This proposal is significantly less source breaking than its [previous iteration](https://gist.github.com/kelvin13/a9c033193a28b1d4960a89b25fbffb06). + +- **remove the `capacity` parameter from `deallocate(capacity:)` and `deallocate(bytes:alignedTo:)`** + +This change is source-breaking, but this is a Good Thing™. The current API encourages incorrect code to be written, and sets us up for potentially catastrophic source breakage down the road should the implementations of `deallocate(capacity:)` and `deallocate(bytes:alignedTo:)` ever be “fixed”, so users should be forced to stop using them as soon as possible. + +- **add unsized memory methods to `UnsafeMutableBufferPointer`** + +This change is purely additive. + +- **add an `assign(to:count:)` method to `UnsafeMutablePointer` and an `assign(to:)` method to `UnsafeMutableBufferPointer`** + +This change is purely additive. + +- **add a default value of `1` to all size parameters on `UnsafeMutablePointer` and applicable size parameters on `UnsafeMutableRawPointer`** + +This change is purely additive. + +- **rename `copyBytes(from:count:)` to `copy(from:bytes:)`** + +This change is source breaking but can be trivially automigrated. + +- **rename `count` in `UnsafeMutableRawBufferPointer.allocate(count:)` to `bytes` and add an `alignedTo` parameter to make it `UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)`** + +This change is source breaking but can be trivially automigrated. The `alignedTo:` parameter can be filled in with `MemoryLayout.stride`. + +- **fix the ordering of the arguments in `initializeMemory(as:at:count:to:)` and rename the argument `to:` to `repeating:` in all repeating memorystate functions** + +This change is source breaking but can be trivially automigrated. + +- **add the sized memorystate functions `withMemoryRebound(to:count:_:)` to `UnsafeMutableBufferPointer`, and `initializeMemory(as:at:repeating:count:)`, `initializeMemory(as:from:count:)` `moveInitializeMemory(as:from:count:)`, and `bindMemory(to:count:)` to `UnsafeMutableRawBufferPointer`** + +This change is purely additive. + +- **add a `init(mutating:)` initializer to `UnsafeMutableBufferPointer`** + +This change is purely additive. + +- **add mutable overloads to non-vacating memorystate method arguments on `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer`** + +This change is purely additive. + +- **finally deprecate `initialize(from:)` from `UnsafeMutablePointer`** + +This change is source-breaking only for the reason that such code should never have compiled in the first place. + +## Effect on ABI stability + +Removing sized deallocators changes the existing ABI, as will renaming some of the methods and their argument labels. + +## Effect on API resilience + +Some proposed changes in this proposal change the public API. + +Removing sized deallocators right now will break ABI, but offers increased ABI and API stability in the future as reallocator methods can be added in the future without having to rename `deallocate(capacity:)` which currently occupies a “reallocator” name, but has “`free()`” behavior. + +This proposal seeks to tackle all the breaking changes required for such an overhaul of Swift pointers, and leaves unanswered only additive changes that still need to be made in the future, reducing the need for future breakage. + +## Alternatives considered + +- **keeping sized deallocators and fixing the stdlib implementation instead** + +Instead of dropping the `capacity` parameter from `deallocate(capacity:)`, we could fix the underlying implementation so that the function actually deallocates `capacity`’s worth of memory. However this would be catastrophically, and silently, source-breaking as existing code would continue compiling, but suddenly start leaking or segfaulting at runtime. `deallocate(capacity:)` can always be added back at a later date without breaking ABI or API, once users have been forced to address this potential bug. + +- **adding an initializer `UnsafeMutableBufferPointer.init(allocatingCount:)` instead of a type method to `UnsafeMutableBufferPointer`** + +The allocator could be expressed as an initializer instead of a type method. However since allocation involves acquisition of an external resource, perhaps it is better to keep with the existing convention that allocation is performed differently than regular buffer pointer construction. + +- **using the argument label `value:` instead of `repeating:` in methods such as `initialize(repeating:count:)` (originally `initialize(to:count:)`)** + +The label `value:` or `toValue:` doesn’t fully capture the repeating nature of the argument, and is inconsistent with `Array.init(repeating:count:)`. While `value:` sounds less strange when `count == 1`, on consistency and technical correctness, `repeating:` is the better term. Furthermore, `value` is a common variable name, meaning that function calls with `value:` as the label would be prone to looking like this: + +```swift +ptr.initialize(value: value) +``` From 17ace1debae24c1744a81b4c71401f90b982bb4a Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Mon, 31 Jul 2017 14:21:03 -0700 Subject: [PATCH 0421/4563] Update SE-0184 to fit the proposal template. --- proposals/0184-improved-pointers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0184-improved-pointers.md b/proposals/0184-improved-pointers.md index 83dfbcddff..b5b62a8836 100644 --- a/proposals/0184-improved-pointers.md +++ b/proposals/0184-improved-pointers.md @@ -1,7 +1,7 @@ # Improved pointers * Proposal: [SE-0184](0184-improved-pointers.md) -* Authors: [Kelvin Ma](https://github.com/kelvin13) (“*Taylor Swift*”) +* Author: [Kelvin Ma (“Taylor Swift”)](https://github.com/kelvin13) * Review Manager: TBD * Status: **Awaiting review** From 7ce871dde8d9204db7cf83661f5887eac05987ad Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Tue, 1 Aug 2017 16:08:27 -0500 Subject: [PATCH 0422/4563] Mark some proposals as implemented (#737) --- proposals/0154-dictionary-key-and-value-collections.md | 2 +- proposals/0163-string-revision-1.md | 2 +- proposals/0171-reduce-with-inout.md | 2 +- proposals/0174-filter-range-replaceable.md | 1 + proposals/0180-string-index-overhaul.md | 2 +- proposals/0182-newline-escape-in-strings.md | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/proposals/0154-dictionary-key-and-value-collections.md b/proposals/0154-dictionary-key-and-value-collections.md index 540fb77a78..0258383838 100644 --- a/proposals/0154-dictionary-key-and-value-collections.md +++ b/proposals/0154-dictionary-key-and-value-collections.md @@ -3,7 +3,7 @@ * Proposal: [SE-0154](0154-dictionary-key-and-value-collections.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170220/033159.html) ## Introduction diff --git a/proposals/0163-string-revision-1.md b/proposals/0163-string-revision-1.md index 70a8fd54ca..638c884859 100644 --- a/proposals/0163-string-revision-1.md +++ b/proposals/0163-string-revision-1.md @@ -3,7 +3,7 @@ * Proposal: [SE-0163](0163-string-revision-1.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Dave Abrahams](http://github.com/dabrahams/) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 4)** * Revision: 2 * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/7513547ddac66b06770a1fd620aad915d75987ff/proposals/0163-string-revision-1.md) * Decision Notes: [Rationale #1](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170417/035919.html), [Rationale #2](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170515/036728.html) diff --git a/proposals/0171-reduce-with-inout.md b/proposals/0171-reduce-with-inout.md index c323796926..5a7b48652e 100644 --- a/proposals/0171-reduce-with-inout.md +++ b/proposals/0171-reduce-with-inout.md @@ -3,7 +3,7 @@ * Proposal: [SE-0171](0171-reduce-with-inout.md) * Author: [Chris Eidhof](https://github.com/chriseidhof) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** +* Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170424/036126.html) ## Introduction diff --git a/proposals/0174-filter-range-replaceable.md b/proposals/0174-filter-range-replaceable.md index 5dc513d1f4..8b89b7db29 100644 --- a/proposals/0174-filter-range-replaceable.md +++ b/proposals/0174-filter-range-replaceable.md @@ -5,6 +5,7 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Accepted** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-May/000374.html) +* Bug: [SR-3444](https://bugs.swift.org/browse/SR-3444) ## Introduction diff --git a/proposals/0180-string-index-overhaul.md b/proposals/0180-string-index-overhaul.md index 7af7e88fa2..af5a2a54d9 100644 --- a/proposals/0180-string-index-overhaul.md +++ b/proposals/0180-string-index-overhaul.md @@ -3,7 +3,7 @@ * Proposal: [SE-0180](0180-string-index-overhaul.md) * Author: [Dave Abrahams](https://github.com/dabrahams) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Accepted** +* Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170703/037942.html) * Pull Request: [apple/swift#9806](https://github.com/apple/swift/pull/9806) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/72b8d90becd60b7cc7695607ae908ef251f1e966/proposals/0180-string-index-overhaul.md) diff --git a/proposals/0182-newline-escape-in-strings.md b/proposals/0182-newline-escape-in-strings.md index 0df32de6d5..6fbeb03649 100644 --- a/proposals/0182-newline-escape-in-strings.md +++ b/proposals/0182-newline-escape-in-strings.md @@ -3,7 +3,7 @@ * Proposal: [SE-0182](0182-newline-escape-in-strings.md) * Authors: [John Holdsworth](https://github.com/johnno1962), [David Hart](https://github.com/hartbit), [Adrian Zubarev](https://github.com/DevAndArtist) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Accepted with Revision** +* Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-July/000393.html) * Previous Proposal: [SE-0168](0168-multi-line-string-literals.md) From f6e2d263b6627032ddb38d9ecd9d8e5573fae832 Mon Sep 17 00:00:00 2001 From: Peng Wang Date: Thu, 3 Aug 2017 14:04:53 -0700 Subject: [PATCH 0423/4563] Update 0064-property-selectors.md (#721) Fixes minor typos. --- proposals/0064-property-selectors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0064-property-selectors.md b/proposals/0064-property-selectors.md index cc606ce558..07c8fbf558 100644 --- a/proposals/0064-property-selectors.md +++ b/proposals/0064-property-selectors.md @@ -53,5 +53,5 @@ The introduction of the new `#selector` overrides has no impact on existing code ## Alternatives considered -A long term alternive could arrise from the design of lenses in Swift. But as this is purely hypothetical and out of scope for Swift 3, this proposal fixes the need for referencing property selectors in a type-safe way straight-away. +A long term alternative could arise from the design of lenses in Swift. But as this is purely hypothetical and out of scope for Swift 3, this proposal fixes the need for referencing property selectors in a type-safe way straight-away. From 57306daec0c0af223ed2bb8e87575b50c45dbbb8 Mon Sep 17 00:00:00 2001 From: Alexander von Below Date: Fri, 4 Aug 2017 09:13:59 +0200 Subject: [PATCH 0424/4563] Update 0107-unsaferawpointer.md Fixed the link to the migration guide --- proposals/0107-unsaferawpointer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0107-unsaferawpointer.md b/proposals/0107-unsaferawpointer.md index 442dfe25e3..0414d5a687 100644 --- a/proposals/0107-unsaferawpointer.md +++ b/proposals/0107-unsaferawpointer.md @@ -8,7 +8,7 @@ For detailed instructions on how to migrate your code to this new Swift 3 API refer to the -[UnsafeRawPointer Migration Guide](https://swift.org/migration-guide/se-0107-migrate.html). See +[UnsafeRawPointer Migration Guide](https://swift.org/migration-guide-swift3/se-0107-migrate.html). See also: See `bindMemory(to:capacity:)`, `assumingMemoryBound(to:)`, and `withMemoryRebound(to:capacity:)`. From 72544ac7bd2cd01abcbc64d2656eaf08f0674892 Mon Sep 17 00:00:00 2001 From: Tristan Shi Date: Tue, 8 Aug 2017 01:45:33 -0700 Subject: [PATCH 0425/4563] Fix unnecessary horizontal scrollbar --- index.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.css b/index.css index 844f4f048b..f10ae2ea6d 100644 --- a/index.css +++ b/index.css @@ -70,7 +70,7 @@ header { display: flex; flex-wrap: wrap; padding-top: 20px; - min-width: 100vw; + min-width: 100%; height: 140px; flex-direction: row; text-align: left; @@ -118,7 +118,7 @@ nav { background: rgb(248, 248, 248); border-bottom: 1px solid rgb(230, 230, 230); - min-width: 100vw; + min-width: 100%; } @supports ((position: sticky) or (position: -webkit-sticky)) { @@ -150,7 +150,7 @@ footer { background: #333; color: #ccc; font-size: 12px; - min-width: 100vw; + min-width: 100%; justify-content: center; display: flex; flex-direction: row; From 9cc90f33b6659adeaf92355c359e34e6fed73254 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 8 Aug 2017 09:14:49 -0700 Subject: [PATCH 0426/4563] Swift 5: start your engines. --- README.md | 179 ++++++++++++++++-------------------------- releases/swift-4_0.md | 102 ++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 111 deletions(-) create mode 100644 releases/swift-4_0.md diff --git a/README.md b/README.md index 63c09b0f8e..24272b2c94 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Swift Programming Language Evolution -[![Swift](https://img.shields.io/badge/Swift%204%20stage%202-Open%20to%20requests-brightgreen.svg)](#swift_stage) +[![Swift](https://img.shields.io/badge/Swift%205-Open%20to%20requests-brightgreen.svg)](#swift_stage) **Before you initiate a pull request**, please read the process document. Ideas should be thoroughly discussed on the [swift-evolution mailing list](https://swift.org/community/#swift-evolution) first. @@ -9,124 +9,81 @@ This repository tracks the ongoing evolution of Swift. It contains: * Goals for upcoming Swift releases (this document). * The [Swift evolution review status][proposal-status] tracking proposals to change Swift. * The [Swift evolution process](process.md) that governs the evolution of Swift. -* [Commonly Rejected Changes](commonly_proposed.md), proposals which have been denied in the past. +* [Commonly Rejected Changes](commonly_proposed.md), proposals that have been denied in the past. This document describes goals for the Swift language on a per-release -basis, usually listing minor releases adding to the currently shipping -version and one major release out. Each release will have many +basis. These releases include minor releases that add to the currently shipping +version plus one major release out. Each release will have many smaller features or changes independent of these larger goals, and not -all goals are reached for each release. +all goals will be reached for each release. + +For historical purposes, the bottom of the document includes goals for past versions. These goals do not necessarily indicate which +features actually shipped for a given version. Those are documented in each version’s release notes. -Goals for past versions are included at the bottom of the document for -historical purposes, but are not necessarily indicative of the -features shipped. The release notes for each shipped version are the -definitive list of notable changes in each release. -## Development major version: Swift 4.0 - -Expected release date: Late 2017 - -The Swift 4 release is designed around two primary goals: to provide -source stability for Swift 3 code and to provide ABI stability for the -Swift standard library. To that end, the Swift 4 release will be -divided into two stages. - -Stage 1 focused on the essentials required for source and ABI -stability. Features that don't fundamentally change the ABI of -existing language features or imply an ABI-breaking change to the -standard library will not be considered in this stage. - -Stage 2 opened in mid-February and extends until April 1, 2017, after -which proposals will be held for a later version of Swift. - -The high-priority features supporting Stage 1's source and ABI -stability goals are: - -* Source stability features: the Swift language will need [some - accommodations](https://github.com/apple/swift-evolution/blob/master/proposals/0141-available-by-swift-version.md) - to support code bases that target different language versions, to - help Swift deliver on its source-compatibility goals while still - enabling rapid progress. - -* Resilience: resilience provides a way for public APIs to evolve over - time, while maintaining a stable ABI. For example, resilience - eliminates the [fragile base class - problem](https://en.wikipedia.org/wiki/Fragile_base_class) that - occurs in some object-oriented languages (e.g., C++) by describing - the types of API changes that can be made without breaking ABI - (e.g., "a new stored property or method can be added to a class"). - -* Stabilizing the ABI: There are a ton of small details that need to - be audited and improved in the code generation model, including - interaction with the Swift runtime. While not specifically - user-facing, the decisions here affect performance and (in some rare - cases) the future evolution of Swift. - -* Generics improvements needed by the standard library: the standard - library has a number of workarounds for language deficiencies, many - of which manifest as extraneous underscored protocols and - workarounds. If the underlying language deficiencies remain, they - become a permanent part of the stable ABI. [Conditional - conformances](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md), - [recursive protocol - requirements](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#recursive-protocol-constraints-), - and [where clauses for associated - types](https://github.com/apple/swift-evolution/blob/master/proposals/0142-associated-types-constraints.md) - are known to be in this category, but it's plausible that other - features will be in scope if they would be used in the standard - library. - -* String re-evaluation: String is one of the most important - fundamental types in the language. Swift 4 seeks to make strings more - powerful and easier-to-use, while retaining Unicode correctness by - default. - -* 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. - -Swift 4 stage 2 builds on the goals of stage 1. It differs in that -stage 2 proposals may include some additive changes and changes to -existing features that don't affect the ABI. There are a few focus -areas for Swift 4 stage 2: - -* Stage 1 proposals: Any proposal that would have been eligible for - stage 1 is a priority for stage 2. - -* Source-breaking changes: The Swift 4 compiler will provide a - source-compatibility mode to allow existing Swift 3 sources to - compile, but source-breaking changes can manifest in "Swift 4" - mode. That said, changes to fundamental parts of Swift's syntax or - standard library APIs that breaks source code are better - front-loaded into Swift 4 than delayed until later - releases. Relative to Swift 3, the bar for such changes is - significantly higher: - - * The existing syntax/API being changed must be actively harmful. - * The new syntax/API must clearly be better and not conflict with existing Swift syntax. - * There must be a reasonably automatable migration path for existing code. - -* Improvements to existing Standard Library facilities: Additive - changes that improve existing standard library facilities can be - considered. With standard library additions in particular, proposals - that provide corresponding implementations are preferred. Potential - focus areas for improvement include collections (e.g., new - collection algorithms) and improvements to the ergonomics of - `Dictionary`. - -* Foundation improvements: We anticipate proposing some targeted - improvements to Foundation API to continue the goal of making the - Cocoa SDK work seamlessly in Swift. Details on the specific goals - will be provided as we get started on Swift 4 stage 2. + +## Development major version: Swift 5.0 + +Expected release date: Late 2018 + +### Primary Focus: ABI Stability + +The Swift 5 release **will** provide [ABI stability](https://github.com/apple/swift/blob/master/docs/ABIStabilityManifesto.md#what-is-abi-stability) for the Swift Standard Library. ABI stability enables OS vendors to embed a Swift Standard Library and runtime in the OS that is compatible with applications built with Swift 5 or later. Progress towards achieving ABI stability will be tracked at a high level on the [ABI Dashboard](https://swift.org/abi-stability/). + +ABI stability is only one of two pieces needed to support binary frameworks. The second half is *module stability* (see "[The Big Picture](https://github.com/apple/swift/blob/master/docs/ABIStabilityManifesto.md#the-big-picture)" of the [ABI Stability Manifesto](https://github.com/apple/swift/blob/master/docs/ABIStabilityManifesto.md) for more information). While we’d like to support this for Swift 5, it will be a stretch goal, and may not make it in time. + +The need to achieve ABI stability in Swift 5 will guide most of the priorities for the release. In addition, there are important goals to complete that carry over from Swift 4 that are prerequisites to locking down the ABI of the standard library: + +- **Generics features needed for standard library**. We will finish implementing [conditional conformances](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md) and [recursive protocol requirements](https://github.com/apple/swift-evolution/blob/master/proposals/0157-recursive-protocol-constraints.md), which are needed for the standard library to achieve ABI stability. Both of these have gone through the evolution proposal process and there are no known other generics enhancements needed for ABI stability. + +- **API resilience**. We will implement the essential pieces need to support API resilience, in order to allow public APIs for a library to evolve over time while maintaining a stable ABI. + +- **Memory ownership model**. An (opt-in) Cyclone/Rust-inspired memory [ownership model](https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md) is strongly desirable for systems programming and for other high-performance applications that require predictable and deterministic performance. Part of this model was introduced in Swift 4 when we began to [ enforce exclusive access to memory](https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md). In Swift 5 our goal is to tackle the [pieces of the ownership model that are key to ABI stability](https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md#priorities-for-abi-stability). + +### Other Improvements + +Beyond ABI stability (which focuses mostly on getting a bunch of low-level implementation details of the language finalized), in Swift 5 the evolution process welcomes additions that improve the overall usability of the language and standard library, including but not restricted to: + +- **String ergonomics**. We will complete more of the work outlined in the [String Manifesto](https://github.com/apple/swift/blob/master/docs/StringManifesto.md) to make `String` easier to use and more performant. This work may include the addition of new text processing affordances to the language and standard library, and language-level support for regular expressions. In addition to ergonomic changes, the internal implementation of `String` offers many opportunities for enhancing performance which we would like to exploit. + +- **Improvements to existing standard library facilities**. We will consider other minor additions to existing library features, but are not open for significant new facilities outside of supporting the primary focuses of this release. + +- **Foundation improvements**. We anticipate proposing some targeted improvements to Foundation API to further the goal of making the Cocoa SDK work seamlessly in Swift. + +- **Syntactic additions**. Syntactic changes do not increase the expressive power of the language but do increase its complexity. Consequently, such changes must be extremely well-motivated and will be subject to additional scrutiny. We will expect proposals to include concrete data about how wide spread the positive impact will be. + +- **Laying groundwork for a new concurrency model**. We will lay groundwork for a new concurrency model, especially as needed for ABI stability. Finalizing such a model, however, is a *non-goal* for Swift 5. A key focus area will be on designing language affordances for creating and using asynchronous APIs and dealing with the problems created by callback-heavy code. + +### Source Stability + +Similar to [Swift 4](releases/swift-4_0.md) , the Swift 5 compiler will provide a source compatibility mode to allow source code written using some previous versions of Swift to compile with the Swift 5 compiler. The Swift 5 compiler will at least support code written in Swift 4, but may also extend back to supporting code written in Swift 3. The final decision on the latter will be made in early 2018. + +Source-breaking changes in Swift 5 will have an even higher bar than in Swift 4, following these guidelines: + +* The current syntax/API must be shown to actively cause problems for users. +* The new syntax/API must be clearly better and must not conflict with existing Swift syntax. +* There must be a reasonably automated migration path for existing code. + +### Evolution Process for Swift 5 + +Unlike [Swift 4](releases/swift-4_0.md), there will be no "stage 1" and "stage 2" for the evolution process. Proposals that fit within the general focus of the release are welcome until **March 1, 2018**. Proposals will still be considered after that, but the bar will be increasingly high to accept changes for Swift 5. + +The broader range of proposals for Swift 5 compared to Swift 4 incurs the risk of diluting the focus on ABI stability. +To mitigate that risk, **every evolution proposal will need a working implementation, with test cases, in order to be considered for review**. An idea can be pitched and a proposal written prior to providing an implementation, but a pull request for a proposal will not be accepted for review until an implementation is available. + +More precisely: + +1. Once a proposal is written, the authors submit the proposal via a pull request to the `swift-evolution` repository. + +2. The Core Team will regularly review `swift-evolution` pull requests, and provide feedback to the authors in the pull request on whether or not the proposal looks within reason of something that might be accepted. + +3. If a proposal gets a positive indicator from the Core Team for later review, the authors must provide an implementation prior to the proposal being formally reviewed. An implementation should be provided in the form of a pull request against the impacted repositories (e.g., `swift`, `swift-package-manager`), and the proposal should be updated with a link to that pull request. The existence of an implementation does not guarantee that the proposal will be accepted, but it is instrumental in evaluating the quality and impact of the proposal. + +We want to strike a balance between encouraging open discussion of potential changes to the language and standard library while also providing more focus when changes are actually reviewed. Further, having implementations on hand allow the changes to be more easily tried out before they are officially accepted as part of the language. In particular, development of the initial pull request for a proposal remains a very open review process that everyone in the community can contribute a lot to. Similarly, members of the community can help craft implementations for a proposal even if they aren't the authors of the proposal. ## Previous releases +* [Swift 4.0](releases/swift-4_0.md) - Endgame * [Swift 3.0](releases/swift-3_0.md) - Released on September 13, 2016 * [Swift 2.2](releases/swift-2_2.md) - Released on March 21, 2016 diff --git a/releases/swift-4_0.md b/releases/swift-4_0.md new file mode 100644 index 0000000000..6ec8251c2e --- /dev/null +++ b/releases/swift-4_0.md @@ -0,0 +1,102 @@ +# Swift 4.0 - Expected to be released in Late 2017 + +**Swift 4 is currently in engineering convergence, and no more evolution proposals are being accepted for this release.** + +The Swift 4 release is designed around two primary goals: to provide +source stability for Swift 3 code and to provide ABI stability for the +Swift standard library. To that end, the Swift 4 release will be +divided into two stages. + +Stage 1 focused on the essentials required for source and ABI +stability. Features that don't fundamentally change the ABI of +existing language features or imply an ABI-breaking change to the +standard library will not be considered in this stage. + +Stage 2 opened in mid-February and extends until April 1, 2017, after +which proposals will be held for a later version of Swift. + +The high-priority features supporting Stage 1's source and ABI +stability goals are: + +* Source stability features: the Swift language will need [some + accommodations](https://github.com/apple/swift-evolution/blob/master/proposals/0141-available-by-swift-version.md) + to support code bases that target different language versions, to + help Swift deliver on its source-compatibility goals while still + enabling rapid progress. + +* Resilience: resilience provides a way for public APIs to evolve over + time, while maintaining a stable ABI. For example, resilience + eliminates the [fragile base class + problem](https://en.wikipedia.org/wiki/Fragile_base_class) that + occurs in some object-oriented languages (e.g., C++) by describing + the types of API changes that can be made without breaking ABI + (e.g., "a new stored property or method can be added to a class"). + +* Stabilizing the ABI: There are a ton of small details that need to + be audited and improved in the code generation model, including + interaction with the Swift runtime. While not specifically + user-facing, the decisions here affect performance and (in some rare + cases) the future evolution of Swift. + +* Generics improvements needed by the standard library: the standard + library has a number of workarounds for language deficiencies, many + of which manifest as extraneous underscored protocols and + workarounds. If the underlying language deficiencies remain, they + become a permanent part of the stable ABI. [Conditional + conformances](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md), + [recursive protocol + requirements](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#recursive-protocol-constraints-), + and [where clauses for associated + types](https://github.com/apple/swift-evolution/blob/master/proposals/0142-associated-types-constraints.md) + are known to be in this category, but it's plausible that other + features will be in scope if they would be used in the standard + library. + +* String re-evaluation: String is one of the most important + fundamental types in the language. Swift 4 seeks to make strings more + powerful and easier-to-use, while retaining Unicode correctness by + default. + +* 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. + +Swift 4 stage 2 builds on the goals of stage 1. It differs in that +stage 2 proposals may include some additive changes and changes to +existing features that don't affect the ABI. There are a few focus +areas for Swift 4 stage 2: + +* Stage 1 proposals: Any proposal that would have been eligible for + stage 1 is a priority for stage 2. + +* Source-breaking changes: The Swift 4 compiler will provide a + source-compatibility mode to allow existing Swift 3 sources to + compile, but source-breaking changes can manifest in "Swift 4" + mode. That said, changes to fundamental parts of Swift's syntax or + standard library APIs that breaks source code are better + front-loaded into Swift 4 than delayed until later + releases. Relative to Swift 3, the bar for such changes is + significantly higher: + + * The existing syntax/API being changed must be actively harmful. + * The new syntax/API must clearly be better and not conflict with existing Swift syntax. + * There must be a reasonably automatable migration path for existing code. + +* Improvements to existing Standard Library facilities: Additive + changes that improve existing standard library facilities can be + considered. With standard library additions in particular, proposals + that provide corresponding implementations are preferred. Potential + focus areas for improvement include collections (e.g., new + collection algorithms) and improvements to the ergonomics of + `Dictionary`. + +* Foundation improvements: We anticipate proposing some targeted + improvements to Foundation API to continue the goal of making the + Cocoa SDK work seamlessly in Swift. Details on the specific goals + will be provided as we get started on Swift 4 stage 2. From 24b0150bbf40b30e0cc62c2df0c19f47ef6c4ba5 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Tue, 8 Aug 2017 13:10:00 -0400 Subject: [PATCH 0427/4563] fix typos (#740) --- proposals/0184-improved-pointers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0184-improved-pointers.md b/proposals/0184-improved-pointers.md index b5b62a8836..4416e72118 100644 --- a/proposals/0184-improved-pointers.md +++ b/proposals/0184-improved-pointers.md @@ -99,7 +99,7 @@ In detail, the following changes should be made: Removing `capacity` from `deallocate(capacity:)` will end the confusion over what `deallocate()` does, making it obvious that `deallocate()` will free the *entire* memory block at `self`, just as if `free()` were called on it. -The old `deallocate(capacity:)` method should be marked as `unavailable` since it currently encourages dangerously incorrect code. This avoids misleading future users, forces current users to address this potentially catastrophic memory bug, and leaves the possibility open for us to add a `deallocate(count:)` method in the future, or perhaps even a `reallocate(toCount:)` method. +The old `deallocate(capacity:)` method should be marked as `unavailable` since it currently encourages dangerously incorrect code. This avoids misleading future users, forces current users to address this potentially catastrophic memory bug, and leaves the possibility open for us to add a `deallocate(capacity:)` method in the future, or perhaps even a `reallocate(toCapacity:)` method. Along similar lines, the `bytes` and `alignedTo` parameters should be removed from the `deallocate(bytes:alignedTo:)` method on `UnsafeMutableRawPointer`. @@ -228,7 +228,7 @@ struct UnsafeMutablePointer --- func moveAssign(from:UnsafeMutablePointer, count:Int) --- func moveInitialize(from:UnsafeMutablePointer, count:Int) --- func initialize(from:UnsafePointer, count:Int) ---- func initialize(repeating:Element, count:Int) +--- func initialize(to:Pointee, count:Int) --- func deinitialize(count:Int) --- func withMemoryRebound(to:T.Type, count:Int, _ body:(UnsafeMutablePointer) -> Result) @@ -236,7 +236,7 @@ struct UnsafeMutablePointer +++ func moveAssign(from:UnsafeMutablePointer, count:Int = 1) +++ func moveInitialize(from:UnsafeMutablePointer, count:Int = 1) +++ func initialize(from:UnsafePointer, count:Int = 1) -+++ func initialize(repeating:Element, count:Int = 1) ++++ func initialize(repeating:Pointee, count:Int = 1) +++ func deinitialize(count:Int = 1) +++ func withMemoryRebound(to:T.Type, count:Int = 1, _ body:(UnsafeMutablePointer) -> Result) } @@ -276,7 +276,7 @@ struct UnsafeMutableBufferPointer +++ func assign(from:UnsafeBufferPointer) +++ func assign(from:UnsafeMutableBufferPointer) -+++ func assign(repeating:Pointee) ++++ func assign(repeating:Element) +++ func moveAssign(from:UnsafeMutableBufferPointer) +++ func moveInitialize(from:UnsafeMutableBufferPointer) +++ func initialize(from:UnsafeBufferPointer) @@ -293,7 +293,7 @@ struct UnsafeMutableRawBufferPointer +++ static func allocate(bytes:Int, alignedTo:Int) -> UnsafeMutableRawBufferPointer func deallocate() +++ func bindMemory(to:Element.Type, count:Int) ---- func copyBytes(from: UnsafeRawBufferPointer) +--- func copyBytes(from:UnsafeRawBufferPointer) +++ func copy(from:UnsafeRawBufferPointer) +++ func copy(from:UnsafeMutableRawBufferPointer) +++ func initializeMemory(as:Element.Type, at:Int, repeating:Element, count:Int) From 802c90ddb234b41473f846e67f0c09497b05171b Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 9 Aug 2017 16:05:28 -0700 Subject: [PATCH 0428/4563] Proposal to synthesize Equatable/Hashable for value types. (#706) * Proposal to synthesize Equatable/Hashable for value types. * Updates based on the WIP implementation * Update to match the proposal template * Remove the _original type declaration_ requirement * Fix typos * Forbid synthesis in extensions (see SR-4920). --- .../NNNN-synthesize-equatable-hashable.md | 345 ++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 proposals/NNNN-synthesize-equatable-hashable.md diff --git a/proposals/NNNN-synthesize-equatable-hashable.md b/proposals/NNNN-synthesize-equatable-hashable.md new file mode 100644 index 0000000000..f6fe239acc --- /dev/null +++ b/proposals/NNNN-synthesize-equatable-hashable.md @@ -0,0 +1,345 @@ +# Synthesizing `Equatable` and `Hashable` conformance + +* Proposal: [SE-NNNN](NNNN-synthesize-equatable-hashable.md) +* Author: [Tony Allevato](https://github.com/allevato) +* Review Manager: TBD +* Status: **Awaiting review** + +## Introduction + +Developers have to write large amounts of boilerplate code to support +equatability and hashability of complex types. This proposal offers a way for +the compiler to automatically synthesize conformance to `Equatable` and +`Hashable` to reduce this boilerplate, in a subset of scenarios where generating +the correct implementation is known to be possible. + +Swift-evolution thread: [Universal Equatability, Hashability, and Comparability +](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160307/012099.html) + +## Motivation + +Building robust types in Swift can involve writing significant boilerplate +code to support hashability and equatability. By eliminating the complexity for +the users, we make `Equatable`/`Hashable` types much more appealing to users and +allow them to use their own types in contexts that require equatability and +hashability with no added effort on their part (beyond declaring the +conformance). + +Equality is pervasive across many types, and for each one users must implement +the `==` operator such that it performs a fairly rote memberwise equality test. +As an example, an equality test for a basic `struct` is fairly uninteresting: + +```swift +struct Person: Equatable { + static func == (lhs: Person, rhs: Person) -> Bool { + return lhs.firstName == rhs.firstName && + lhs.lastName == rhs.lastName && + lhs.birthDate == rhs.birthDate && + ... + } +} +``` + +What's worse is that this operator must be updated if any properties are added, +removed, or changed, and since it must be manually written, it's possible to get +it wrong, either by omission or typographical error. + +Likewise, hashability is necessary when one wishes to store a type in a +`Set` or use one as a multi-valued `Dictionary` key. Writing high-quality, +well-distributed hash functions is not trivial so developers may not put a great +deal of thought into them—especially as the number of properties +increases—not realizing that their performance could potentially suffer +as a result. And as with equality, writing it manually means there is the +potential for it to not only be inefficient, but incorrect as well. + +In particular, the code that must be written to implement equality for +`enum`s is quite verbose: + +```swift +enum Token: Equatable { + case string(String) + case number(Int) + case lparen + case rparen + + static func == (lhs: Token, rhs: Token) -> Bool { + switch (lhs, rhs) { + case (.string(let lhsString), .string(let rhsString)): + return lhsString == rhsString + case (.number(let lhsNumber), .number(let lhsNumber)): + return lhsNumber == rhsNumber + case (.lparen, .lparen), (.rparen, .rparen): + return true + default: + return false + } + } +} +``` + +Crafting a high-quality hash function for this `enum` would be similarly +inconvenient to write. + +Swift already derives `Equatable` and `Hashable` conformance for a small subset +of `enum`s: those for which the cases have no associated values (which includes +enums with raw types). Two instances of such an `enum` are equal if they are the +same case, and an instance's hash value is its ordinal: + +```swift +enum Foo { + case zero, one, two +} + +let x = (Foo.one == Foo.two) // evaluates to false +let y = Foo.one.hashValue // evaluates to 1 +``` + +Likewise, conformance to `RawRepresentable` is automatically derived for `enum`s +with a raw type, and the recently approved `Encodable`/`Decodable` protocols +also support synthesis of their operations when possible. Since there is +precedent for synthesized conformances in Swift, we propose extending it to +these fundamental protocols. + +## Proposed solution + +In general, we propose that a type synthesize conformance to +`Equatable`/`Hashable` if all of its members are `Equatable`/`Hashable`. We +describe the specific conditions under which these conformances are synthesized +below, followed by the details of how the conformance requirements are +implemented. + +### Requesting synthesis is opt-in + +Users must _opt-in_ to automatic synthesis by declaring their type as +`Equatable` or `Hashable` without implementing any of their requirements. This +conformance must be part of the _original type declaration_ and not on an +extension (see [Synthesis in extensions](#synthesis-in-extensions) below for +more on this). + +Any type that declares such conformance and satisfies the conditions below +will cause the compiler to synthesize an implementation of `==`/`hashValue` +for that type. + +Making the synthesis opt-in—as opposed to automatic derivation without +an explicit declaration—provides a number of benefits: + +* The syntax for opting in is natural; there is no clear analogue in Swift + today for having a type opt out of a feature. + +* It requires users to make a conscious decision about the public API surfaced + by their types. Types cannot accidentally "fall into" conformances that the + user does not wish them to; a type that does not initially support `Equatable` + can be made to at a later date, but the reverse is a breaking change. + +* The conformances supported by a type can be clearly seen by examining + its source code; nothing is hidden from the user. + +* We reduce the work done by the compiler and the amount of code generated + by not synthesizing conformances that are not desired and not used. + +* As will be discussed later, explicit conformance significantly simplifies + the implementation for recursive types. + +There is one exception to this rule: the current behavior will be preserved that +`enum` types with cases that have no associated values (including those with raw +values) conform to `Equatable`/`Hashable` _without_ the user explicitly +declaring those conformances. While this does add some inconsistency to `enum`s +under this proposal, changing this existing behavior would be source-breaking. +The question of whether such `enum`s should be required to opt-in as well can +be revisited at a later date if so desired. + +### Overriding synthesized conformances + +Any user-provided implementations of `==` or `hashValue` will override the +default implementations that would be provided by the compiler. + +### Conditions where synthesis is allowed + +For brevity, let `P` represent either the protocol `Equatable` or `Hashable` in +the descriptions below. + +#### Synthesized requirements for `enum`s + +For an `enum`, synthesis of `P`'s requirements is based on the conformances of +its cases' associated values. Computed properties are not considered. + +The following rules determine whether `P`'s requirements can be synthesized for +an `enum`: + +* The compiler does **not** synthesize `P`'s requirements for an `enum` with no + cases because it is not possible to create instances of such types. + +* The compiler synthesizes `P`'s requirements for an `enum` with one or more + cases if and only if all of the associated values of all of its cases conform + to `P`. + +#### Synthesized requirements for `struct`s + +For a `struct`, synthesis of `P`'s requirements is based on the conformances of +**only** its stored instance properties. Neither static properties nor computed +instance properties (those with custom getters) are considered. + +The following rules determine whether `P`'s requirements can be synthesized for +a `struct`: + +* The compiler trivially synthesizes `P`'s requirements for a `struct` with *no* + stored properties. (All instances of a `struct` with no stored properties can + be considered equal and hash to the same value if the user opts in to this.) + +* The compiler synthesizes `P`'s requirements for a `struct` with one or more + stored properties if and only if all of the types of all of its stored + properties conform to `P`. + +### Considerations for recursive types + +By making the synthesized conformances opt-in, recursive types have their +requirements fall into place with no extra effort. In any cycle belonging to a +recursive type, every type in that cycle must declare its conformance +explicitly. If a type does so but cannot have its conformance synthesized +because it does not satisfy the conditions above, then it is simply an error for +_that_ type and not something that must be detected earlier by the compiler in +order to reason about _all_ the other types involved in the cycle. (On the other +hand, if conformance were implicit, the compiler would have to fully traverse +the entire cycle to determine eligibility, which would make implementation much +more complex). + +### Implementation details + +An `enum T: Equatable` that satisfies the conditions above will receive a +synthesized implementation of `static func == (lhs: T, rhs: T) -> Bool` that +returns `true` if and only if `lhs` and `rhs` are the same case and have +payloads that are memberwise-equal. + +An `enum T: Hashable` that satisfies the conditions above will receive a +synthesized implementation of `var hashValue: Int { get }` that uses an +unspecified hash function to compute the hash value by incorporating +the case's ordinal (i.e., definition order) followed by the hash values of its +associated values as its terms, also in definition order. + +A `struct T: Equatable` that satisfies the conditions above will receive a +synthesized implementation of `static func == (lhs: T, rhs: T) -> Bool` that +returns `true` if and only if `lhs.x == rhs.x` for all stored properties `x` in +`T`. If the `struct` has no stored properties, this operator simply returns +`true`. + +A `struct T: Hashable` that satisfies the conditions above will receive a +synthesized implementation of `var hashValue: Int { get }` that uses an +unspecified hash function to compute the hash value by incorporating +the hash values of the fields as its terms, in definition order. If the `struct` +has no stored properties, this property evaluates to a fixed value not specified +here. + + The choice of hash function is left as an implementation detail, +not a fixed part of the design; as such, users should not depend on specific +characteristics of its behavior. The most likely implementation would call the +standard library's `_mixInt` function on each member's hash value and then +combine them with exclusive-or (`^`), which mirrors the way `Collection` types +are hashed today. + +## Source compatibility + +By making the conformance opt-in, this is a purely additive change that does +not affect existing code. We also avoid source-breaking changes by not changing +the behavior for `enum`s with no associated values, which will continue to +implicitly conform to `Equatable` and `Hashable` even without explicitly +declaring the conformance. + +## Effect on ABI stability + +This feature is purely additive and does not change ABI. + +## Effect on API resilience + +N/A. + +## Alternatives considered + +In order to realistically scope this proposal, we considered but ultimately +deferred the following items, some of which could be proposed additively in the +future. + +### Synthesis in extensions + +Requirements will be synthesized only for protocol conformances that are +_part of the type declaration itself;_ conformances added in extensions will +not be synthesized. + +For `struct`s, synthesizing a requirement would not be safe in an extension +in a different module or in a different file in the same module because any +`private` or `fileprivate` members of the `struct` would not be accessible +there. Extensions within the same file would be safe now that `private` members +are also accessible from extensions of the containing type in the same file. + +However, to align with `Codable` in the context of +[SR-4920](https://bugs.swift.org/browse/SR-4920), we will also currently +forbid synthesized requirements in extensions in the same file; this specific +case can be revisited later for all derived conformances. + +We note that conformances to `enum` types would be safe to synthesize anywhere +because the cases and their associated values are always as accessible as the +`enum` type itself, but we apply the same rule above for consistency; users do +not have to memorize an intricate table of what is derivable and where. + +### Synthesis for `class` types and tuples + +We do not synthesize conformances for `class` types. The conditions above become +more complicated in inheritance hierarchies, and equality requires that +`static func ==` be implemented in terms of an overridable instance method for +it to be dispatched dynamically. Even for `final` classes, the conditions are +not as clear-cut as they are for value types because we have to take superclass +behavior into consideration. Finally, since objects have reference identity, +memberwise equality may not necessarily imply that two instances are equal. + +We do not synthesize conformances for tuples at this time. While this would +nicely round out the capabilities of value types, allow the standard library to +remove the hand-crafted implementations of `==` for up-to-arity-6 tuples, and +allow those types to be used in generic contexts where `Equatable` conformance +is required, adding conformances to non-nominal types would require additional +work. + +### Omitting fields from synthesized conformances + +Some commenters have expressed a desire to tag certain properties of a `struct` +from being included in automatically generated equality tests or hash value +computations. This could be valuable, for example, if a property is merely used +as an internal cache and does not actually contribute to the "value" of the +instance. Under the rules above, if this cached value was equatable, a user +would have to override `==` and `hashValue` and provide their own +implementations to ignore it. + +Such a feature, which could be implemented with an attribute such as +`@transient`, would likely also play a role in other protocols like +`Encodable`/`Decodable`. This could be done as a purely additive change on top +of this proposal, so we propose not doing this at this time. + +### Implicit derivation + +An earlier draft of this proposal made derived conformances implicit (without +declaring `Equatable`/`Hashable` explicitly). This has been changed +because—in addition to the reasons mentioned earlier in the +proposal—`Encodable`/`Decodable` provide a precedent for having the +conformance be explicit. More importantly, however, determining derivability for +recursive types is _significantly more difficult_ if conformance is implicit, +because it requires examining the entire dependency graph for a particular type +and to properly handle cycles in order to decide if the conditions are +satisfied. + +### Support for `Comparable` + +The original discussion thread also included `Comparable` as a candidate for +automatic generation. Unlike equatability and hashability, however, +comparability requires an ordering among the members being compared. +Automatically using the definition order here might be too surprising for users, +but worse, it also means that reordering properties in the source code changes +the code's behavior at runtime. (This is true for hashability as well if a +multiplicative hash function is used, but hash values are not intended to be +persistent and reordering the terms does not produce a significant _behavioral_ +change.) + +## Acknowledgments + +Thanks to Joe Groff for spinning off the original discussion thread, Jose Cheyo +Jimenez for providing great real-world examples of boilerplate needed to support +equatability for some value types, Mark Sands for necromancing the +swift-evolution thread that convinced me to write this up, and everyone on +swift-evolution since then for giving me feedback on earlier drafts. From 310f9d68b4ecb6b019abc2ac644a538b5b627dd1 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 9 Aug 2017 16:06:38 -0700 Subject: [PATCH 0429/4563] kick off 0185 --- ...le-hashable.md => 0185-synthesize-equatable-hashable.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-synthesize-equatable-hashable.md => 0185-synthesize-equatable-hashable.md} (98%) diff --git a/proposals/NNNN-synthesize-equatable-hashable.md b/proposals/0185-synthesize-equatable-hashable.md similarity index 98% rename from proposals/NNNN-synthesize-equatable-hashable.md rename to proposals/0185-synthesize-equatable-hashable.md index f6fe239acc..f905c010f2 100644 --- a/proposals/NNNN-synthesize-equatable-hashable.md +++ b/proposals/0185-synthesize-equatable-hashable.md @@ -1,9 +1,9 @@ # Synthesizing `Equatable` and `Hashable` conformance -* Proposal: [SE-NNNN](NNNN-synthesize-equatable-hashable.md) +* Proposal: [SE-0185](0185-synthesize-equatable-hashable.md) * Author: [Tony Allevato](https://github.com/allevato) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Chris Lattner](https://github.com/lattner) +* Status: **Active review (Aug 9...15)** ## Introduction From a5f862259294a5a6d41c5199f69d755211f064d0 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Wed, 9 Aug 2017 17:24:54 -0700 Subject: [PATCH 0430/4563] Spell out month name. --- proposals/0185-synthesize-equatable-hashable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0185-synthesize-equatable-hashable.md b/proposals/0185-synthesize-equatable-hashable.md index f905c010f2..f327b832e3 100644 --- a/proposals/0185-synthesize-equatable-hashable.md +++ b/proposals/0185-synthesize-equatable-hashable.md @@ -3,7 +3,7 @@ * Proposal: [SE-0185](0185-synthesize-equatable-hashable.md) * Author: [Tony Allevato](https://github.com/allevato) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Active review (Aug 9...15)** +* Status: **Active review (August 9...15)** ## Introduction From d122a5d47c0c892651eab03ae8e975dd3c5ff846 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 16 Aug 2017 15:33:30 -0700 Subject: [PATCH 0431/4563] 0185 is accepted! --- proposals/0185-synthesize-equatable-hashable.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0185-synthesize-equatable-hashable.md b/proposals/0185-synthesize-equatable-hashable.md index f327b832e3..b9646803ff 100644 --- a/proposals/0185-synthesize-equatable-hashable.md +++ b/proposals/0185-synthesize-equatable-hashable.md @@ -3,7 +3,8 @@ * Proposal: [SE-0185](0185-synthesize-equatable-hashable.md) * Author: [Tony Allevato](https://github.com/allevato) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Active review (August 9...15)** +* Status: **Accepted** +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-August/000400.html) ## Introduction From 38da1b35abaa5ba28b23ef7c0779cc3546a035f3 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Sun, 20 Aug 2017 14:28:20 -0700 Subject: [PATCH 0432/4563] Add a link to the implementation PR for SE-0185 --- proposals/0185-synthesize-equatable-hashable.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0185-synthesize-equatable-hashable.md b/proposals/0185-synthesize-equatable-hashable.md index b9646803ff..c3202fc3a0 100644 --- a/proposals/0185-synthesize-equatable-hashable.md +++ b/proposals/0185-synthesize-equatable-hashable.md @@ -4,6 +4,7 @@ * Author: [Tony Allevato](https://github.com/allevato) * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Accepted** +* Pull Request: [apple/swift#9619](https://github.com/apple/swift/pull/9619) * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-August/000400.html) ## Introduction From 9927023e3a4ce9e053fae5607bba462194829368 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Sun, 20 Aug 2017 15:14:05 -0700 Subject: [PATCH 0433/4563] Add pull request link to proposal template now that one is required Thanks @benrimmington for noticing and proposing this change; I couldn't resurrect your PR since the fork is gone! From the readme: > The broader range of proposals for Swift 5 compared to Swift 4 incurs the risk of diluting the focus on ABI stability. To mitigate that risk, every evolution proposal will need a working implementation, with test cases, in order to be considered for review. --- 0000-template.md | 1 + 1 file changed, 1 insertion(+) diff --git a/0000-template.md b/0000-template.md index 62fdd5e53a..656a87ed73 100644 --- a/0000-template.md +++ b/0000-template.md @@ -4,6 +4,7 @@ * Authors: [Author 1](https://github.com/swiftdev), [Author 2](https://github.com/swiftdev) * Review Manager: TBD * Status: **Awaiting review** +* Pull Request: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) *During the review process, add the following fields as needed:* From 4cc0c1c8699c9c76dacdcd372d7baa1c7d97d962 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 23 Aug 2017 10:08:43 -0700 Subject: [PATCH 0434/4563] Revert "fix typos (#740)" This reverts commit 24b0150bbf40b30e0cc62c2df0c19f47ef6c4ba5. --- proposals/0184-improved-pointers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0184-improved-pointers.md b/proposals/0184-improved-pointers.md index 4416e72118..b5b62a8836 100644 --- a/proposals/0184-improved-pointers.md +++ b/proposals/0184-improved-pointers.md @@ -99,7 +99,7 @@ In detail, the following changes should be made: Removing `capacity` from `deallocate(capacity:)` will end the confusion over what `deallocate()` does, making it obvious that `deallocate()` will free the *entire* memory block at `self`, just as if `free()` were called on it. -The old `deallocate(capacity:)` method should be marked as `unavailable` since it currently encourages dangerously incorrect code. This avoids misleading future users, forces current users to address this potentially catastrophic memory bug, and leaves the possibility open for us to add a `deallocate(capacity:)` method in the future, or perhaps even a `reallocate(toCapacity:)` method. +The old `deallocate(capacity:)` method should be marked as `unavailable` since it currently encourages dangerously incorrect code. This avoids misleading future users, forces current users to address this potentially catastrophic memory bug, and leaves the possibility open for us to add a `deallocate(count:)` method in the future, or perhaps even a `reallocate(toCount:)` method. Along similar lines, the `bytes` and `alignedTo` parameters should be removed from the `deallocate(bytes:alignedTo:)` method on `UnsafeMutableRawPointer`. @@ -228,7 +228,7 @@ struct UnsafeMutablePointer --- func moveAssign(from:UnsafeMutablePointer, count:Int) --- func moveInitialize(from:UnsafeMutablePointer, count:Int) --- func initialize(from:UnsafePointer, count:Int) ---- func initialize(to:Pointee, count:Int) +--- func initialize(repeating:Element, count:Int) --- func deinitialize(count:Int) --- func withMemoryRebound(to:T.Type, count:Int, _ body:(UnsafeMutablePointer) -> Result) @@ -236,7 +236,7 @@ struct UnsafeMutablePointer +++ func moveAssign(from:UnsafeMutablePointer, count:Int = 1) +++ func moveInitialize(from:UnsafeMutablePointer, count:Int = 1) +++ func initialize(from:UnsafePointer, count:Int = 1) -+++ func initialize(repeating:Pointee, count:Int = 1) ++++ func initialize(repeating:Element, count:Int = 1) +++ func deinitialize(count:Int = 1) +++ func withMemoryRebound(to:T.Type, count:Int = 1, _ body:(UnsafeMutablePointer) -> Result) } @@ -276,7 +276,7 @@ struct UnsafeMutableBufferPointer +++ func assign(from:UnsafeBufferPointer) +++ func assign(from:UnsafeMutableBufferPointer) -+++ func assign(repeating:Element) ++++ func assign(repeating:Pointee) +++ func moveAssign(from:UnsafeMutableBufferPointer) +++ func moveInitialize(from:UnsafeMutableBufferPointer) +++ func initialize(from:UnsafeBufferPointer) @@ -293,7 +293,7 @@ struct UnsafeMutableRawBufferPointer +++ static func allocate(bytes:Int, alignedTo:Int) -> UnsafeMutableRawBufferPointer func deallocate() +++ func bindMemory(to:Element.Type, count:Int) ---- func copyBytes(from:UnsafeRawBufferPointer) +--- func copyBytes(from: UnsafeRawBufferPointer) +++ func copy(from:UnsafeRawBufferPointer) +++ func copy(from:UnsafeMutableRawBufferPointer) +++ func initializeMemory(as:Element.Type, at:Int, repeating:Element, count:Int) From b159d48bf5a05e5dcfa83e2e7dbb2f64c15ada27 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 23 Aug 2017 10:08:46 -0700 Subject: [PATCH 0435/4563] Revert "Update SE-0184 to fit the proposal template." This reverts commit 17ace1debae24c1744a81b4c71401f90b982bb4a. --- proposals/0184-improved-pointers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0184-improved-pointers.md b/proposals/0184-improved-pointers.md index b5b62a8836..83dfbcddff 100644 --- a/proposals/0184-improved-pointers.md +++ b/proposals/0184-improved-pointers.md @@ -1,7 +1,7 @@ # Improved pointers * Proposal: [SE-0184](0184-improved-pointers.md) -* Author: [Kelvin Ma (“Taylor Swift”)](https://github.com/kelvin13) +* Authors: [Kelvin Ma](https://github.com/kelvin13) (“*Taylor Swift*”) * Review Manager: TBD * Status: **Awaiting review** From 29aea8ca010a4459f7782d6e66e192762b135059 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 23 Aug 2017 10:08:47 -0700 Subject: [PATCH 0436/4563] Revert "Create 0184-improved-pointers.md (#736)" This reverts commit d84e158bde38944f48ed7dd7df0c032653489ac2. --- proposals/0184-improved-pointers.md | 382 ---------------------------- 1 file changed, 382 deletions(-) delete mode 100644 proposals/0184-improved-pointers.md diff --git a/proposals/0184-improved-pointers.md b/proposals/0184-improved-pointers.md deleted file mode 100644 index 83dfbcddff..0000000000 --- a/proposals/0184-improved-pointers.md +++ /dev/null @@ -1,382 +0,0 @@ -# Improved pointers - -* Proposal: [SE-0184](0184-improved-pointers.md) -* Authors: [Kelvin Ma](https://github.com/kelvin13) (“*Taylor Swift*”) -* Review Manager: TBD -* Status: **Awaiting review** - -## Introduction - -Swift’s pointer types are an important interface for low-level memory manipulation, but the current API design is not very consistent, complete, or convenient. Many memory methods demand a `capacity:` or `count:` argument, forcing the user to manually track the size of the memory block, even though most of the time this is either unnecessary, or redundant as buffer pointers track this information natively. In some places, poor naming choices and overengineered function signatures compromise memory safety by leading users to believe that they have allocated or freed memory when in fact, they have not. - -This proposal seeks to improve the Swift pointer API by ironing out naming inconsistencies, adding sensible default argument values, adding missing methods, and reducing excessive verbosity, offering a more convenient, more sensible, and less bug-prone API. - -The [previous draft](https://gist.github.com/kelvin13/a9c033193a28b1d4960a89b25fbffb06) of this proposal was relatively source-breaking, calling for a separation of functionality between singular pointer types and vector (buffer) pointer types. This proposal instead separates functionality between internally-tracked length pointer types and externally-tracked length pointer types. This results in an equally elegant API with about one-third less surface area. - -Swift-evolution thread: [Pitch: Improved Swift pointers](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170710/038013.html), [Pitch: More Improved Swift pointers](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170717/038121.html) - -## Motivation - -Right now, `UnsafeMutableBufferPointer` is kind of a black box when it comes to producing and modifying instances of it. To create, bind, allocate, initialize, deinitialize, and deallocate them, you have to extract `baseAddress`es and `count`s. This is unfortunate because `UnsafeMutableBufferPointer` provides a handy container for tracking the size of a memory buffer, but to actually make use of this information, the buffer pointer must be disassembled. In practice, this means the use of memory buffers requires frequent (and annoying) conversion back and forth between buffer pointers and base address–count pairs. For example, to move-initialize memory between two buffer pointers, you have to write this: - -```swift -buffer1.baseAddress?.moveInitialize(from: buffer2.baseAddress!, count: buffer1.count) -``` - -The `?` is sometimes exchanged with an `!` depending on the personality of the author, as normally, neither operator is meaningful here — the `baseAddress` is never `nil` if the buffer pointer was created around an instance of `UnsafeMutablePointer`. - -Memory buffer allocation is especially painful, since it requires the creation of a temporary `UnsafeMutablePointer` instance. This means that the following “idiom” is very common in Swift code: - -```swift -let buffer = UnsafeMutableBufferPointer(start: UnsafeMutablePointer.allocate(capacity: byteCount), count: byteCount) -``` - -Aside from being extremely long and unwieldy, and requiring the creation of a temporary, `byteCount` must appear twice. - -You can’t even cast buffer pointer types to their mutable or immutable forms without creating a temporary. - -```swift - -var mutableBuffer = UnsafeMutableBufferPointer(start: UnsafeMutablePointer(mutating: immutableBuffer.baseAddress!), count: immutableBuffer.count) -``` - -Currently, memory is deallocated by an instance method on `UnsafeMutablePointer`, `deallocate(count:)`. Like much of the Swift pointer API, performing this operation on a buffer pointer requires extracting `baseAddress!` and `count`. It is very common for the allocation code above to be immediately followed by: - -```swift -defer -{ - buffer.baseAddress?.deallocate(capacity: buffer.count) -} -``` - -This method is extremely problematic because nearly all users, on first seeing the signature of `deallocate(capacity:)`, will naturally conclude from the `capacity` label that `deallocate(capacity:)` is equivalent to some kind of `realloc()` that can only shrink the buffer. However this is not the actual behavior — `deallocate(capacity:)` actually *ignores* the `capacity` argument and just calls `free()` on `self`. The current API is not only awkward and suboptimal, it is *misleading*. You can write perfectly legal Swift code that shouldn’t segfault, but still can, for example - -```swift -var ptr = UnsafeMutablePointer.allocate(capacity: 1000000) -ptr.initialize(to: 13, count: 1000000) -ptr.deallocate(capacity: 500000) // deallocate the second half of the memory block -ptr[0] // segmentation fault -``` - -where the first 500000 addresses should still be valid if the [documentation](https://developer.apple.com/documentation/swift/unsafemutablepointer/2295090-deallocate) is to be read literally. - -Users who are *aware* of this behavior may also choose to disregard the `capacity` argument and write things like this: - -```swift -defer -{ - buffer.baseAddress?.deallocate(capacity: 42) -} -``` - -which is functionally equivalent. However this will lead to disastrous source breakage if the implementation of `deallocate(capacity:)` is ever “corrected”. Since the API would not change, such code would still compile, but suddenly start failing at runtime. Thus, the current API, combined with incorrect documentation, is serving as a vector for introducing memory bugs into Swift code. - -Inconsistencies exist in the memorystate functions. The `initialize(from:count:)` method on `UnsafeMutablePointer` has a repeating variant, `initialize(to:count:)`, but `assign(from:count:)` has no such variant, even though it would make just as much sense for it to have one. - -While most of the memorystate functions are absent from `UnsafeMutableBufferPointer`, there is *one* strange exception — `UnsafeMutableBufferPointer` features an `initialize(from:)` method which takes a `Sequence`. This method was originally an inhabitant of `UnsafeMutablePointer` before it was [supposedly](https://github.com/apple/swift-evolution/blob/master/proposals/0147-move-unsafe-initialize-from.md#detailed-design) moved to `UnsafeMutableBufferPointer` by [SE-147](https://github.com/apple/swift-evolution/blob/master/proposals/0147-move-unsafe-initialize-from.md). This decision appears to have never been carried out, as the type still features an active `initialize(from:)` method which takes a `Collection`. (The original SE-147 method took a `Collection`.) - -Finally, the naming of some `UnsafeMutableRawPointer` members deserves a second look. While the original API intended to introduce a naming convention where `bytes` refers to uninitialized memory, `capacity` to uninitialized elements, and `count` to initialized elements, the actual usage of the three words does not always agree. In `copyBytes(from:count:)`, `count` refers to the number of *bytes*, which may or may not be initialized. Similarly, the `UnsafeMutableRawBufferPointer` `allocate(count:)` type method includes a `count` argument which actually refers to uninitialized bytes. - -## Proposed solution - -The presence of an associated `count` variable in `UnsafeMutableBufferPointer`, and the absence of it in `UnsafeMutablePointer` leads to a very natural and elegant set of memory APIs. Because buffer length is tracked externally when using `UnsafeMutablePointer`, memory methods on it should explicitly ask for the sizing parameter. Conversely, because buffer length is tracked internally by `UnsafeMutableBufferPointer`, memory methods on it should supply the buffer’s own `count` property for the operation’s sizing parameter. This means you would call - -```swift -ptr1.initialize(from: ptr2, count: count) -``` - -on an `UnsafeMutablePointer`, but - -```swift -buffer1.initialize(from: buffer2) -``` - -on an `UnsafeMutableBufferPointer`. This differs from the [previous draft](https://gist.github.com/kelvin13/a9c033193a28b1d4960a89b25fbffb06) of this proposal, in that this expansion is more additive and less source-breaking, preserving the much of the sized API present on `UnsafeMutablePointer`. - -In detail, the following changes should be made: - -- **remove the `capacity` parameter from `deallocate(capacity:)` and `deallocate(bytes:alignedTo:)`** - -Removing `capacity` from `deallocate(capacity:)` will end the confusion over what `deallocate()` does, making it obvious that `deallocate()` will free the *entire* memory block at `self`, just as if `free()` were called on it. - -The old `deallocate(capacity:)` method should be marked as `unavailable` since it currently encourages dangerously incorrect code. This avoids misleading future users, forces current users to address this potentially catastrophic memory bug, and leaves the possibility open for us to add a `deallocate(count:)` method in the future, or perhaps even a `reallocate(toCount:)` method. - -Along similar lines, the `bytes` and `alignedTo` parameters should be removed from the `deallocate(bytes:alignedTo:)` method on `UnsafeMutableRawPointer`. - -- **add unsized memory methods to `UnsafeMutableBufferPointer`** - -The following methods will be added to `UnsafeMutableBufferPointer`, giving it parity with `UnsafeMutablePointer`. - -```swift -static func allocate(capacity:Int) -> UnsafeMutableBufferPointer -func deallocate() - -func assign(from:UnsafeBufferPointer) -func moveAssign(from:UnsafeMutableBufferPointer) -func moveInitialize(from:UnsafeMutableBufferPointer) -func initialize(from:UnsafeBufferPointer) -func initialize(to:Element) -func deinitialize() -``` - -Where there would have been a `capacity:` or `count:` argument on these methods, this value will be filled by the buffer pointer’s own `count` property. For the binary operations `assign(from:)`, `moveAssign(from:)`, `moveInitialize(from:)`, and `initialize(from:)`, it is assumed that the other buffer pointer contains *at least* as many elements as `self` does. - -- **add an `assign(to:count:)` method to `UnsafeMutablePointer` and an `assign(to:)` method to `UnsafeMutableBufferPointer`** - -This addresses the missing assignment analogues to the `initialize(to:count:)` and `initialize(to:)` methods. - -- **add a default value of `1` to all size parameters on `UnsafeMutablePointer` and applicable size parameters on `UnsafeMutableRawPointer`** - -Since the most common use case for plain pointers is to manage one single instance of a type, the size parameters on `UnsafeMutablePointer`’s memory methods are good candidates for a default value of `1`. Any size parameter on `UnsafeMutableRawPointer`’s memory methods which take a stride quantity should also receive a default value of `1`. The size parameters in `UnsafeMutableRawPointer`’s other methods should not receive a default value as they refer to byte quantities. - -- **rename `copyBytes(from:count:)` to `copy(from:bytes:)`** - -To reduce the inconsistency in our use of the words `bytes`, `count`, and `capacity`, we will enforce the convention that: - -* `bytes` refers to, well, a byte quantity that is *not assumed* to be initialized. -* `capacity` refers to a strided quantity that is *not assumed* to be initialized. -* `count` refers to a strided quantity that is *assumed* to be initialized. - -Since this makes the word “bytes” occur twice in `copyBytes(from:bytes:)`, we should drop the “Bytes” suffix and further rename the method to `copy(from:bytes:)`. Since `UnsafeMutableRawPointer` is inherently untyped, it is obvious that any memory transfer operation on it is a bytewise operation so the “Bytes” suffix adds only verbosity and no clarity. An unsized version of this method will also be added to `UnsafeMutableRawBufferPointer`. - -We do not rename the `count` property on `UnsafeMutableRawBufferPointer` to `bytes` since this could be confused with the actual buffer data. - -- **rename `count` in `UnsafeMutableRawBufferPointer.allocate(count:)` to `bytes` and add an `alignedTo` parameter to make it `UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)`** - -This brings it in line with the `UnsafeMutableRawPointer` allocator, and avoids the contradictory and inconsistent use of `count` to represent a byte quantity. Currently `UnsafeMutableRawBufferPointer.allocate(count:)` aligns to the size of `Int`, an assumption not shared by its plain variant. - -- **fix the ordering of the arguments in `initializeMemory(as:at:count:to:)` and rename the argument `to:` to `repeating:` in all repeating memorystate functions** - -The ordering of the `to:` and `count:` argument labels in the `initializeMemory(as:at:count:to:)` method on `UnsafeMutableRawPointer` contradicts the rest of the Swift pointer API, where `to:` precedes `count:`. - -Because the ordering `initializeMemory(as:at:to:count:)` conflicts with the use of `to:` as the argument label for a target type, this argument should be renamed to `repeating:`. The word `repeating:` is much more clear in terms of describing the methods’ behavior, and is consistent with the use of the word in the `Array` API. - -- **add the sized memorystate functions `withMemoryRebound(to:count:_:)` to `UnsafeMutableBufferPointer`, and `initializeMemory(as:at:repeating:count:)`, `initializeMemory(as:from:count:)` `moveInitializeMemory(as:from:count:)`, and `bindMemory(to:count:)` to `UnsafeMutableRawBufferPointer`** - -Since buffer pointers track their own `count`, this value is a natural fit for the `count:` argument in most of the memorystate functions. However, since raw buffer pointers don’t inherently “know” how many instances of an arbitrary type fit in themselves, `initializeMemory(as:at:repeating:count:)` and `bindMemory(to:capacity:)` should retain their size parameters. - -For similar reasons, `UnsafeMutableBufferPointer.withMemoryRebound(to:capacity:_:)` keeps its size parameter because `capacity` may be different from `count` with a differently laid out type. - -- **add a `init(mutating:)` initializer to `UnsafeMutableBufferPointer`** - -This makes it much easier to make a mutable copy of an immutable buffer pointer. Such an initializer already exists on `UnsafeMutableRawBufferPointer`, so adding one to `UnsafeMutableBufferPointer` is also necessary for consistency. The reverse initializer, from `UnsafeMutableBufferPointer` to `UnsafeBufferPointer` should also be added for completeness. - -- **add mutable overloads to non-vacating memorystate method arguments on `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer`** - -Non vacating memorystate operations such as `assign(from:)` and `initialize(from:)` on `UnsafeMutableBufferPointer`, and `copy(from:)` and `initializeMemory(at:from:count:)` on `UnsafeMutableRawBufferPointer` currently take immutable source arguments. They should be overloaded to accept mutable source arguments to reduce the need for pointer immutability casts. Currently, for plain pointers, this is covered by a compiler subtyping relationship between `UnsafePointer` and `UnsafeMutablePointer`. No such relationship exists between `UnsafeBufferPointer` and `UnsafeMutableBufferPointer` or their raw counterparts. - -- **finally deprecate `initialize(from:)` from `UnsafeMutablePointer`** - -This method was supposed to be deprecated per [SE-147](https://github.com/apple/swift-evolution/blob/master/proposals/0147-move-unsafe-initialize-from.md). Better late than never. - - -## What this proposal does not do - -- **add missing memorystate functions that take `Collection`s and `Sequence`s** - -This proposal also does not address the missing `Collection`/`Sequence` memorystate functions, although it does call for (finally) removing `UnsafeMutablePointer.initialize(from:)`. This is out of scope for this proposal, and the missing functions can always be filled in at a later date, as part of a purely additive proposal. - -- **remove subscripts from `UnsafePointer` and `UnsafeMutablePointer`** - -There are strong arguments for removing subscript capability from plain pointer types. - -> Subscripts on `UnsafePointer` and `UnsafeMutablePointer` are inconsistent with their intended purpose. For example, `ptr[0]` and `ptr.pointee` both do the same thing. Furthermore, it is not immediately obvious from the grammar that the subscript parameter is an *offset*. New users may conclude that subscripting a pointer at `[89]` dereferences the memory at *address* `0x0000000000000059`! It would make more sense to use pointer arithmetic and the `pointee` property to access memory at an offset from `self` than to allow subscripting. - -> C programmers who defend this kind of syntax are reminded that many things obvious to C programmers, are obvious to *only* C programmers. The trend in Swift’s design is to separate C’s array–pointer duality; pointer subscripts run counter to that goal. - -> There is an argument that singular pointer subscripts are useful when singular pointers of unknown length are returned by C APIs. The counter-argument is that you almost never need to do random access into a vector of unknown length, rather you would iterate one element at a time until you reach the end. This lends itself well to pointer incrementation and the `pointee` property rather than subscripting. - -```swift -while ptr.pointee != sentinel -{ - ... - - ptr += 1 -} -``` - -However, this is a source breaking change, and there are (rare) cases where this syntax is helpful, for example, when dealing with multi-element strides. - -```swift -var pixel:UnsafeMutablePointer = base -while pixel < base + size -{ - // assign the RGBA color #ff1096ff - pixel[0] = 0xff - pixel[1] = 0x10 - pixel[2] = 0x96 - pixel[3] = 0xff - pixel += 4 -} -``` - -## Detailed design - -```diff -struct UnsafeMutablePointer -{ ---- static func allocate(capacity:Int) -> UnsafeMutablePointer -+++ static func allocate(capacity:Int = 1) -> UnsafeMutablePointer ---- func deallocate(capacity _:Int) -+++ func deallocate() - ---- func initialize(from:C) - -+++ func assign(repeating:Pointee, count:Int = 1) - ---- func assign(from:UnsafePointer, count:Int) ---- func moveAssign(from:UnsafeMutablePointer, count:Int) ---- func moveInitialize(from:UnsafeMutablePointer, count:Int) ---- func initialize(from:UnsafePointer, count:Int) ---- func initialize(repeating:Element, count:Int) ---- func deinitialize(count:Int) ---- func withMemoryRebound(to:T.Type, count:Int, _ body:(UnsafeMutablePointer) -> Result) - -+++ func assign(from:UnsafePointer, count:Int = 1) -+++ func moveAssign(from:UnsafeMutablePointer, count:Int = 1) -+++ func moveInitialize(from:UnsafeMutablePointer, count:Int = 1) -+++ func initialize(from:UnsafePointer, count:Int = 1) -+++ func initialize(repeating:Element, count:Int = 1) -+++ func deinitialize(count:Int = 1) -+++ func withMemoryRebound(to:T.Type, count:Int = 1, _ body:(UnsafeMutablePointer) -> Result) -} - -struct UnsafeMutableRawPointer -{ ---- func deallocate(bytes _:Int, alignedTo _:Int) -+++ func deallocate() - ---- func copyBytes(from:UnsafeRawPointer, count:Int) -+++ func copy(from:UnsafeRawPointer, bytes:Int) ---- func initializeMemory(as:T.Type, at:Int, count:Int, to:T) -+++ func initializeMemory(as:T.Type, at:Int, repeating:T, count:Int = 1) - ---- func initializeMemory(as:C.Element.Type, from:C) - ---- func bindMemory(to:T.Type, count:Int) ---- func initializeMemory(as:T.Type, from:UnsafePointer, count:Int) ---- func moveInitializeMemory(as:T.Type, from:UnsafeMutablePointer, count:Int) - -+++ func bindMemory(to:T.Type, count:Int = 1) -+++ func initializeMemory(as:T.Type, from:UnsafePointer, count:Int = 1) -+++ func moveInitializeMemory(as:T.Type, from:UnsafeMutablePointer, count:Int = 1) -} - -struct UnsafeBufferPointer -{ -+++ init(_:UnsafeMutableBufferPointer) -} - -struct UnsafeMutableBufferPointer -{ -+++ init(mutating:UnsafeBufferPointer) - -+++ static func allocate(capacity:Int) -> UnsafeMutableBufferPointer -+++ func deallocate() - -+++ func assign(from:UnsafeBufferPointer) -+++ func assign(from:UnsafeMutableBufferPointer) -+++ func assign(repeating:Pointee) -+++ func moveAssign(from:UnsafeMutableBufferPointer) -+++ func moveInitialize(from:UnsafeMutableBufferPointer) -+++ func initialize(from:UnsafeBufferPointer) -+++ func initialize(from:UnsafeMutableBufferPointer) -+++ func initialize(repeating:Element) -+++ func deinitialize() -+++ func withMemoryRebound -+++ (to:T.Type, count:Int, _ body:(UnsafeMutableBufferPointer) -> Result) -} - -struct UnsafeMutableRawBufferPointer -{ ---- static func allocate(count:Int) -> UnsafeMutableRawBufferPointer -+++ static func allocate(bytes:Int, alignedTo:Int) -> UnsafeMutableRawBufferPointer - func deallocate() -+++ func bindMemory(to:Element.Type, count:Int) ---- func copyBytes(from: UnsafeRawBufferPointer) -+++ func copy(from:UnsafeRawBufferPointer) -+++ func copy(from:UnsafeMutableRawBufferPointer) -+++ func initializeMemory(as:Element.Type, at:Int, repeating:Element, count:Int) -+++ func initializeMemory(as:Element.Type, from:UnsafeBufferPointer, count:Int) -+++ func initializeMemory(as:Element.Type, from:UnsafeMutableBufferPointer, count:Int) -+++ func moveInitializeMemory(as:Element.Type, from:UnsafeMutableBufferPointer, count:Int) -} -``` - -## Source compatibility - -Some parts of this proposal are source breaking. This proposal is significantly less source breaking than its [previous iteration](https://gist.github.com/kelvin13/a9c033193a28b1d4960a89b25fbffb06). - -- **remove the `capacity` parameter from `deallocate(capacity:)` and `deallocate(bytes:alignedTo:)`** - -This change is source-breaking, but this is a Good Thing™. The current API encourages incorrect code to be written, and sets us up for potentially catastrophic source breakage down the road should the implementations of `deallocate(capacity:)` and `deallocate(bytes:alignedTo:)` ever be “fixed”, so users should be forced to stop using them as soon as possible. - -- **add unsized memory methods to `UnsafeMutableBufferPointer`** - -This change is purely additive. - -- **add an `assign(to:count:)` method to `UnsafeMutablePointer` and an `assign(to:)` method to `UnsafeMutableBufferPointer`** - -This change is purely additive. - -- **add a default value of `1` to all size parameters on `UnsafeMutablePointer` and applicable size parameters on `UnsafeMutableRawPointer`** - -This change is purely additive. - -- **rename `copyBytes(from:count:)` to `copy(from:bytes:)`** - -This change is source breaking but can be trivially automigrated. - -- **rename `count` in `UnsafeMutableRawBufferPointer.allocate(count:)` to `bytes` and add an `alignedTo` parameter to make it `UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)`** - -This change is source breaking but can be trivially automigrated. The `alignedTo:` parameter can be filled in with `MemoryLayout.stride`. - -- **fix the ordering of the arguments in `initializeMemory(as:at:count:to:)` and rename the argument `to:` to `repeating:` in all repeating memorystate functions** - -This change is source breaking but can be trivially automigrated. - -- **add the sized memorystate functions `withMemoryRebound(to:count:_:)` to `UnsafeMutableBufferPointer`, and `initializeMemory(as:at:repeating:count:)`, `initializeMemory(as:from:count:)` `moveInitializeMemory(as:from:count:)`, and `bindMemory(to:count:)` to `UnsafeMutableRawBufferPointer`** - -This change is purely additive. - -- **add a `init(mutating:)` initializer to `UnsafeMutableBufferPointer`** - -This change is purely additive. - -- **add mutable overloads to non-vacating memorystate method arguments on `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer`** - -This change is purely additive. - -- **finally deprecate `initialize(from:)` from `UnsafeMutablePointer`** - -This change is source-breaking only for the reason that such code should never have compiled in the first place. - -## Effect on ABI stability - -Removing sized deallocators changes the existing ABI, as will renaming some of the methods and their argument labels. - -## Effect on API resilience - -Some proposed changes in this proposal change the public API. - -Removing sized deallocators right now will break ABI, but offers increased ABI and API stability in the future as reallocator methods can be added in the future without having to rename `deallocate(capacity:)` which currently occupies a “reallocator” name, but has “`free()`” behavior. - -This proposal seeks to tackle all the breaking changes required for such an overhaul of Swift pointers, and leaves unanswered only additive changes that still need to be made in the future, reducing the need for future breakage. - -## Alternatives considered - -- **keeping sized deallocators and fixing the stdlib implementation instead** - -Instead of dropping the `capacity` parameter from `deallocate(capacity:)`, we could fix the underlying implementation so that the function actually deallocates `capacity`’s worth of memory. However this would be catastrophically, and silently, source-breaking as existing code would continue compiling, but suddenly start leaking or segfaulting at runtime. `deallocate(capacity:)` can always be added back at a later date without breaking ABI or API, once users have been forced to address this potential bug. - -- **adding an initializer `UnsafeMutableBufferPointer.init(allocatingCount:)` instead of a type method to `UnsafeMutableBufferPointer`** - -The allocator could be expressed as an initializer instead of a type method. However since allocation involves acquisition of an external resource, perhaps it is better to keep with the existing convention that allocation is performed differently than regular buffer pointer construction. - -- **using the argument label `value:` instead of `repeating:` in methods such as `initialize(repeating:count:)` (originally `initialize(to:count:)`)** - -The label `value:` or `toValue:` doesn’t fully capture the repeating nature of the argument, and is inconsistent with `Array.init(repeating:count:)`. While `value:` sounds less strange when `count == 1`, on consistency and technical correctness, `repeating:` is the better term. Furthermore, `value` is a common variable name, meaning that function calls with `value:` as the label would be prone to looking like this: - -```swift -ptr.initialize(value: value) -``` From 55791d37d89be9492edcf780a7f5302cceab6a00 Mon Sep 17 00:00:00 2001 From: salmanjamil Date: Mon, 28 Aug 2017 00:29:14 +0500 Subject: [PATCH 0437/4563] Fixed Typos (#745) * Fixed Typos fixed a few typos. Changed `Wrapper` to `Wrapped` * Update 0143-conditional-conformances.md --- proposals/0143-conditional-conformances.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/0143-conditional-conformances.md b/proposals/0143-conditional-conformances.md index a6527dff2d..e6d7f5162f 100644 --- a/proposals/0143-conditional-conformances.md +++ b/proposals/0143-conditional-conformances.md @@ -178,14 +178,14 @@ protocol HasIdentity { } extension SomeWrapper: Equatable where Wrapped: Equatable { - static func ==(lhs: SomeWrapper, rhs: SomeWrapper) -> Bool { + static func ==(lhs: SomeWrapper, rhs: SomeWrapper) -> Bool { return lhs.wrapped == rhs.wrapped } } // error: SomeWrapper already stated conformance to Equatable extension SomeWrapper: Equatable where Wrapped: HasIdentity { - static func ==(lhs: SomeWrapper, rhs: SomeWrapper) -> Bool { + static func ==(lhs: SomeWrapper, rhs: SomeWrapper) -> Bool { return lhs.wrapped === rhs.wrapped } } @@ -222,7 +222,7 @@ that are orthogonal to conditional conformances. ### Implied conditional conformances Stating conformance to a protocol implicitly states conformances to -any of the protocols that it the protocol inherits. This is already +any of the protocols that the protocol inherits. This is already the case in Swift today: one can declare conformance to the `Collection` protocol, and it implies conformance to `Sequence` as well. @@ -344,13 +344,13 @@ protocol HasIdentity { } extension SomeWrapper: Equatable where Wrapped: Equatable { - static func ==(lhs: SomeWrapper, rhs: SomeWrapper) -> Bool { + static func ==(lhs: SomeWrapper, rhs: SomeWrapper) -> Bool { return lhs.wrapped == rhs.wrapped } } extension SomeWrapper: Equatable where Wrapped: HasIdentity { - static func ==(lhs: SomeWrapper, rhs: SomeWrapper) -> Bool { + static func ==(lhs: SomeWrapper, rhs: SomeWrapper) -> Bool { return lhs.wrapped === rhs.wrapped } } @@ -373,7 +373,7 @@ It is due to the possibility of #4 occurring that we refer to the two conditiona ```swift // Possible tie-breaker conformance extension SomeWrapper: Equatable where Wrapped: Equatable & HasIdentity, { - static func ==(lhs: SomeWrapper, rhs: SomeWrapper) -> Bool { + static func ==(lhs: SomeWrapper, rhs: SomeWrapper) -> Bool { return lhs.wrapped == rhs.wrapped } } From d05c3cb1939d4cf91b69a70de2e4e8166275e1fc Mon Sep 17 00:00:00 2001 From: Kelvin Date: Sat, 2 Sep 2017 00:00:07 -0500 Subject: [PATCH 0438/4563] Unsafe[Mutable][Raw][Buffer]Pointer: add missing methods, adjust existing labels for clarity, and remove deallocation size. (#744) * 0184 * implementation is no longer a prototype * Update and rename 0184-improved-pointers.md to 0184-unsafe-pointers-add-missing.md --- 0184-unsafe-pointers-add-missing.md | 781 ++++++++++++++++++++++++++++ 1 file changed, 781 insertions(+) create mode 100644 0184-unsafe-pointers-add-missing.md diff --git a/0184-unsafe-pointers-add-missing.md b/0184-unsafe-pointers-add-missing.md new file mode 100644 index 0000000000..23a21a10c3 --- /dev/null +++ b/0184-unsafe-pointers-add-missing.md @@ -0,0 +1,781 @@ +# Unsafe[Mutable][Raw][Buffer]Pointer: add missing methods, adjust existing labels for clarity, and remove deallocation size + +* Proposal: [SE-0184](0184-improved-pointers.md) +* Author: [Kelvin Ma (“Taylor Swift”)](https://github.com/kelvin13) +* Review Manager: TBD +* Status: **Awaiting review** + +## Introduction + +Swift’s pointer types are an important interface for low-level memory manipulation, but the current API design is not very consistent, complete, or convenient. Many memory methods demand a `capacity:` or `count:` argument, forcing the user to manually track the size of the memory block, even though most of the time this is either unnecessary, or redundant as buffer pointers track this information natively. In some places, poor naming choices and overengineered function signatures compromise memory safety by leading users to believe that they have allocated or freed memory when in fact, they have not. + +This proposal seeks to improve the Swift pointer API by ironing out naming inconsistencies, adding sensible default argument values, adding missing methods, and reducing excessive verbosity, offering a more convenient, more sensible, and less bug-prone API. We also attempt to introduce a buffer pointer API that supports partial initialization without excessively compromising memory state safety. + +Swift-evolution thread: [Pitch: Improved Swift pointers](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170710/038013.html), [Pitch: More Improved Swift pointers](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170717/038121.html) + +Implementation branch: [**PR 11464**](https://github.com/apple/swift/pull/11464) + +## Background + +There are four binary memorystate operations: *initialization*, *move-initialization*, *assignment*, and *move-assignment*. They can be grouped according to how they affect the source buffer and the destination buffer. **Copy** operations only read from the source buffer, leaving it unchanged. **Move** operations deinitialize the source memory, decrementing the reference count by 1 if the memory type is not a trivial type. **Retaining** operations initialize the destination memory, incrementing the reference count by 1 if applicable. **Releasing** operations deinitialize the destination memory before reinitializing it with the new values, resulting in a net change in the reference count of 0, if applicable. + +| | Copy (+0) | Move (−1) | +| -------------: |----------: | ---------: | +| **Retaining (+1)** | initialize | move-initialize | +| **Releasing (+0)** | assign | move-assign | + +Note: deinitialization by itself is a unary operation; it decrements the reference count of the buffer by 1. + +The four main types of Swift pointers we have currently support different subsets of this toolbox. + +### UnsafeMutablePointer + +| | Copy | Move | +| ------------- |----------: | ---------: | +| **Retaining** | `initialize(to:count:)`, `initialize(from:count:)` | `moveInitialize(from:count:)` | +| **Releasing** | `assign(from:count:)` | `moveAssign(from:count:)` | + +### UnsafeMutableRawPointer + +| | Copy | Move | +| ------------- |----------: | ---------: | +| **Retaining** | `initializeMemory(as:at:count:to:)`, `initializeMemory(as:from:count:)` | `moveInitializeMemory(as:from:count:)` | +| **Releasing** | | | + +### UnsafeMutableBufferPointer + +| | Copy | Move | +| ------------- |----------: | ---------: | +| **Retaining** | `initialize(from:)` | | +| **Releasing** | | | + + +### UnsafeMutableRawBufferPointer + +| | Copy | Move | +| ------------- |----------: | ---------: | +| **Retaining** | `initializeMemory(as:from:)` | | +| **Releasing** | | | + +There are unary memorystate operations such as *deinitialization* and *type rebinding*, which are not listed in the tables, but are still covered by this proposal. Raw pointers also have a unique operation, *bytewise-copying*, which we will lump together with the memorystate functions, but does not actually change a pointer’s memory state. + +## Motivation + +Right now, `UnsafeMutableBufferPointer` is kind of a black box when it comes to producing and modifying instances of it. Much of the API present on `UnsafeMutablePointer` is absent on its buffer variant. To create, bind, allocate, initialize, deinitialize, and deallocate them, you have to extract `baseAddress`es and `count`s. This is unfortunate because `UnsafeMutableBufferPointer` provides a handy container for tracking the size of a memory buffer, but to actually make use of this information, the buffer pointer must be disassembled. In practice, this means the use of memory buffers requires frequent (and annoying) conversion back and forth between buffer pointers and base address–count pairs. For example, to move-initialize memory between two buffer pointers, you have to write this: + +```swift +buffer1.baseAddress?.moveInitialize(from: buffer2.baseAddress!, count: buffer1.count) +``` + +The `?` is sometimes exchanged with an `!` depending on the personality of the author, as normally, neither operator is meaningful here — the `baseAddress` is never `nil` if the buffer pointer was created around an instance of `UnsafeMutablePointer`. + +Memory buffer allocation is especially painful, since it requires the creation of a temporary `UnsafeMutablePointer` instance. This means that the following “idiom” is very common in Swift code: + +```swift +let buffer = UnsafeMutableBufferPointer(start: UnsafeMutablePointer.allocate(capacity: byteCount), count: byteCount) +``` + +Aside from being extremely long and unwieldy, and requiring the creation of a temporary, `byteCount` must appear twice. + +You can’t even cast buffer pointer types to their mutable or immutable forms without creating a temporary. + +```swift + +var mutableBuffer = UnsafeMutableBufferPointer(start: UnsafeMutablePointer(mutating: immutableBuffer.baseAddress!), count: immutableBuffer.count) +``` + +Currently, memory is deallocated by an instance method on `UnsafeMutablePointer`, `deallocate(count:)`. Like much of the Swift pointer API, performing this operation on a buffer pointer requires extracting `baseAddress!` and `count`. It is very common for the allocation code above to be immediately followed by: + +```swift +defer +{ + buffer.baseAddress?.deallocate(capacity: buffer.count) +} +``` + +This method is extremely problematic because nearly all users, on first seeing the signature of `deallocate(capacity:)`, will naturally conclude from the `capacity` label that `deallocate(capacity:)` is equivalent to some kind of `realloc()` that can only shrink the buffer. However this is not the actual behavior — `deallocate(capacity:)` actually *ignores* the `capacity` argument and just calls `free()` on `self`. The current API is not only awkward and suboptimal, it is *misleading*. You can write perfectly legal Swift code that shouldn’t segfault, but still can, for example + +```swift +var ptr = UnsafeMutablePointer.allocate(capacity: 1000000) +ptr.initialize(to: 13, count: 1000000) +ptr.deallocate(capacity: 500000) // deallocate the second half of the memory block +ptr[0] // segmentation fault +``` + +where the first 500000 addresses should still be valid if the [documentation](https://developer.apple.com/documentation/swift/unsafemutablepointer/2295090-deallocate) is to be read literally. + +Users who are *aware* of this behavior may also choose to disregard the `capacity` argument and write things like this: + +```swift +defer +{ + buffer.baseAddress?.deallocate(capacity: 42) +} +``` + +which is functionally equivalent. However this will lead to disastrous source breakage if the implementation of `deallocate(capacity:)` is ever “corrected”. Since the API would not change, such code would still compile, but suddenly start failing at runtime. Thus, the current API, combined with incorrect documentation, is serving as a vector for introducing memory bugs into Swift code. + +The Swift pointer API is incomplete in other ways too. For example, the `initialize(from:count:)` method on `UnsafeMutablePointer` has a repeated-value copy variant, `initialize(to:count:)`, but `assign(from:count:)` has no such variant, even though it would make just as much sense for it to have one. + +Finally, some of the naming choices in the current API deserve a second look. While the original API intended to introduce a naming convention where `bytes` refers to uninitialized memory, `capacity` to uninitialized elements, and `count` to initialized elements, the actual usage of the three words does not always agree. In `copyBytes(from:count:)`, `count` refers to the number of *bytes*, which may or may not be initialized. Similarly, the `UnsafeMutableRawBufferPointer` `allocate(count:)` type method includes a `count` argument which actually refers to uninitialized bytes. The argument label `to:` is also excessively overloaded; sometimes it refers to a type `T.Type`, and sometimes it refers to a repeated value parameter. This becomes problematic when both parameters appear in the same method, as in `initializeMemory(as:at:count:to)`. + +## Proposed solution + +The goal of the API redesign is to bring all of the functionality in `UnsafeMutablePointer` and `UnsafeMutableRawPointer` to their buffer types, `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer`. `UnsafeMutableRawBufferPointer` already contains some of this functionality, providing a useful blueprint for the proposed `UnsafeMutableBufferPointer` API. + +The full toolbox of methods that we could possibly support includes: + + - allocation + - deallocation + + - initialization + - move-initialization + + - assignment + - move-assignment + + - deinitialization + - type rebinding + + - bytewise copying + +Because copy operations (initialization and assignment) don’t mutate the source argument, they can also come in a form which takes a repeated-value source instead of a buffer source. + + - initialization (repeated-value) + - assignment (repeated-value) + +`UnsafeMutablePointer` and `UnsafeMutableRawPointer` already contain repeated-value methods for initialization in the form of `initialize(to:count:)` and `initializeMemory(as:at:count:to:)`. This proposal will add the assignment analogues. For reasons explained later, the argument label for the repeated-value parameter will be referred to as `repeating:`, not `to:`. + +In their most general form, these functions are written like this: + +``` +static +func allocate(as:count:) -> PointerType +func deallocate() + +func initialize(as:at:repeating:count:) +func initialize(as:at:from:count:) +func moveInitialize(as:at:from:count:) + +func assign(as:at:repeating:count:) +func assign(as:at:from:count:) +func moveAssign(as:at:from:count:) + +func deinitialize(as:at:count:) +func rebindMemory(as:count:) + +func copyBytes(at:from:count:) +``` + +where + + - **`as:`** refers to the element type + - **`at:`** refers to an offset from `self`, in strides of the element type + - **`repeating:`** refers to a repeating value + - **`from:`** refers to a second pointer which serves as the **source** + - **`count:`** refers the number of elements the operation operates on + +On actual pointer types, most of these parameters are unnecessary, and some of the methods themselves either don’t make sense to support, or are not practically useful. + + - it only makes sense for immutable pointer types to support deallocation and type rebinding. Note that Swift’s memory model does not require memory to be mutable for deallocation. + + - raw (untyped) pointers should not support any operations which involve deinitialization on `self`. This rules out deinitialization itself, as well as any releasing operations (assignment, move-assignment). + + - typed pointers don’t need an `as:` parameter (except for type rebinding) — they already have a type. It also doesn’t make sense for them to support byte-wise copying. + + - pointers for which it is syntactically easy to offset in strides (for example, by pointer arithmetic with `+`), don’t need to take an `at:` argument. + +> note: some of these conceptual argument labels have different names in the real API. `as:` is written as `to:` in the type-rebinding methods because it sounds better. `count:` is sometimes written as `capacity:` or `bytes:` to express the assumptions about the stride and initialization state of the memory in question. + +> * `bytes` refers to, well, a byte quantity that is *not assumed* to be initialized. +> * `capacity` refers to a strided quantity that is *not assumed* to be initialized. +> * `count` refers to a strided quantity that is *assumed* to be initialized. + +> note: we don’t bother supporting an `at:` offset in type rebinding operations since we don’t anticipate much use for such a feature. + +### `UnsafePointer` + +``` +func deallocate() +func withMemoryRebound(to:capacity:_:) -> Result +``` + +`UnsafePointer` does not get an allocator static method, since you almost always want a mutable pointer to newly allocated memory. Its type rebinding method is also written as a decorator, taking a trailing closure, for memory safety. `UnsafePointer` does not take `at:` arguments since `+` provides pointer arithmetic for it. + +### `UnsafeMutablePointer` + +``` +static +func allocate(capacity:) -> UnsafeMutablePointer +func deallocate() + +func initialize(repeating:count:) +func initialize(from:count:) +func moveInitialize(from:count:) + +func assign(repeating:count:) +func assign(from:count:) +func moveAssign(from:count:) + +func deinitialize(count:) +func withMemoryRebound(to:capacity:_:) -> Result +``` + +Like `UnsafePointer`, `UnsafeMutablePointer`’s type rebinding method is written as a decorator, and its methods do not need `at:` arguments. + +> note: the `count:` argument in `initialize(repeating:count:)`, `assign(repeating:count:)`, and `deinitialize(count:)` and the `capacity:` argument in `allocate(capacity:)` should receive a default value of `1`, since those methods are commonly used with a count of `1`. + +> `withMemoryRebound(to:capacity:_:)` should *not* receive a default `count:` value, to avoid misleading users about type binding in Swift’s memory model. (The entire memory block must be rebound, not just the first element under the pointer.) + +### `UnsafeRawPointer` + +``` +func deallocate() + +func bindMemory(to:capacity:) -> UnsafePointer +``` + +### `UnsafeMutableRawPointer` + +``` +static +func allocate(bytes:alignedTo:) -> UnsafeMutableRawPointer +func deallocate() + +func initializeMemory(as:at:repeating:count:) -> UnsafeMutablePointer +func initializeMemory(as:at:from:count:) -> UnsafeMutablePointer +func moveInitializeMemory(as:at:from:count:) -> UnsafeMutablePointer + +func bindMemory(to:capacity:) -> UnsafeMutablePointer + +func copy(from:bytes:) +``` + +The `as:` argument in `allocate(bytes:alignedTo:)` is represented by an alignment parameter which takes an integer. This is more useful since we often need a computed alignment (like when aligning a structure) instead of a preset type alignment. + +> note: although raw pointers support pointer arithmetic, it is not done in strides, and `ptr + offset * MemoryLayout.stride` is painfully verbose, so these methods take an `at:` offset. + +> note: the `at:` arguments in `UnsafeMutableRawPointer` should all have a default value of `0`; i.e., no offset. + +------------- + +Buffer pointers are conceptually similar, except the `count:` argument is often unnecessary since they track their own length internally. This means you would call + +```swift +ptr1.initialize(repeating: value, count: count) +``` + +on an `UnsafeMutablePointer`, but + +```swift +buffer1.initialize(repeating: value) +``` + +on an `UnsafeMutableBufferPointer`. + +Implementing unary operations like repeated-value initialization, repeated-value assignment, and type rebinding is straightforward. However, with binary operations like move-initialization, which involves both a source buffer and a destination buffer, the question of whose `count` to use becomes important. + +One option is to use the destination’s `count`, and set the precondition that source`.count` `>=` destination`.count`. The benefit to this is that the destination is always guaranteed to be fully initialized, so that a sizeless `deinitialize()` can be safely called on it. However, in the case of move-initialization and move-assignment, it can leave the source buffer partially *deinitialized* which is just as big a problem. It is also not very useful in practice, since real collections tend to grow monotonically, periodically moving their contents into larger and larger buffers. + +A better option is to use the source’s `count`, combined with an `at:` offset, and set the precondition that `offset` `+` source`.count` `<=` destination`.count`. This *`at:from:`* system inspired by existing `UnsafeMutableRawPointer` APIs is an extremely useful system for supporting partially initialized buffer pointers by allowing us to initialize, assign, and move buffers in segments. For example, it would now be easy to concatenate multiple buffers into one. + +```swift +let pixels:Int = scanlines.map{ $0.count }.reduce(0, +) +var image = UnsafeMutableBufferPointer.allocate(capacity: pixels) + +var filled:Int = 0 +for scanline:UnsafeMutableBufferPointer in scanlines +{ + image.moveInitialize(at: filled, from: scanline) + filled += scanline.count +} + +image.deinitialize(at: 0, count: filled) +image.deallocate() +``` + +Under this system, it will be impossible to leave part of a source buffer deinitialized, and every segment of a destination buffer will be accessible (instead of only segments starting at index `0`.) + +> note: we use `at:` instead of `+` because pointer arithmetic does not play well with the nillable buffer pointer `baseAddress`. + +> note: while deinitialization can be performed on a buffer pointer using its own `count` property, we have decided it’s better to explicitly ask for the `at:count:` pair to be consistent with real use patterns and the rest of the proposed API which operates on segments of `self`. + +### `UnsafeBufferPointer` + +``` +func deallocate() +func withMemoryRebound(to:_:) -> Result +``` + +The buffer type rebind method dynamically computes the new count by performing multiplication and integer division, since the target type may have a different stride than the original type. This is in line with existing precedent in the generic buffer method `initializeMemory(as:from:)` on `UnsafeMutableRawBufferPointer`. + +### `UnsafeMutableBufferPointer` + +``` +static +func allocate(capacity:) -> UnsafeMutableBufferPointer +func deallocate() + +func initialize(repeating:) +func initialize(at:from:) +func moveInitialize(at:from:) + +func assign(repeating:) +func assign(at:from:) +func moveAssign(at:from:) + +func deinitialize(at:count) +func withMemoryRebound(to:_:) -> Result +``` + +The buffer type rebind method works the same way as in `UnsafeBufferPointer`. (Type rebinding never cares about mutability.) + +> note: the `at:` arguments in `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer` should *not* receive default values, as they are an integral part of the buffer pointer memory state safety system, and so it is important they appear at the call site. + +### `UnsafeRawBufferPointer` + +``` +func deallocate() + +func bindMemory(to:) -> UnsafeBufferPointer +``` + +### `UnsafeMutableRawBufferPointer` + +``` +static +func allocate(bytes:alignedTo:) -> UnsafeMutableRawBufferPointer +func deallocate() + +func initializeMemory(as:repeating:) -> UnsafeMutableBufferPointer +func initializeMemory(as:at:from:) -> UnsafeMutableBufferPointer +func moveInitializeMemory(as:at:from:) -> UnsafeMutableBufferPointer + +func bindMemory(to:) -> UnsafeMutableBufferPointer + +func copyBytes(at:from:) +``` + +> note: `initializeMemory(as:repeating:)` performs integer division on `self.count` (just like `bindMemory(to:)`) + +> note: the return values of `initializeMemory(as:repeating:)`, `initializeMemory(as:at:from:)`, and `moveInitializeMemory(as:at:from:)` should all be marked as `@discardableResult`. + +> note: `copyBytes(from:)` takes an `at:` argument because just like `UnsafeBufferPointer`, offsetting its `baseAddress` is not syntactically easy. The function name also has “`Bytes`” in it since it’s missing the `bytes:` argument label, and we need to emphasize that it only performs a bytewise copy. + +## Detailed changes + +The proposed new API attempts to build on the existing API wherever possible. With the exception of `deallocate()` (which has good justification to replace `deallocate(capacity:)` and `deallocate(bytes:alignedTo:)`), all changes are either pure additive changes, or renames which are trivial to automigrate. This reduces the amount of source breakage. + +- **fix the ordering of the arguments in `initializeMemory(as:at:count:to:)` and rename the argument `to:` to `repeating:` in all repeated-value copy functions** + +The ordering of the `to:` and `count:` argument labels in the `initializeMemory(as:at:count:to:)` method on `UnsafeMutableRawPointer` contradicts the rest of the Swift pointer API, where `to:` precedes `count:`. + +Because the ordering `initializeMemory(as:at:to:count:)` conflicts with the use of `to:` as the argument label for a target type, this argument should be renamed to `repeating:`. The word `repeating:` is much more clear in terms of describing the methods’ behavior, and is consistent with the use of the word in the `Array` API. + +- **add the repeated-value copy assignment method `assign(repeating:count:)`** + +This addresses the missing assignment analogue to the `initialize(to:count:)` method. + +- **rename `copyBytes(from:count:)` to `copy(from:bytes:)` on `UnsafeMutableRawPointer`** + +We will enforce the convention of the use of the words `bytes`, `count`, and `capacity`. + +Since this makes the word “bytes” occur twice in `copyBytes(from:bytes:)`, we should drop the “Bytes” suffix and further rename the method to `copy(from:bytes:)`. Since `UnsafeMutableRawPointer` is inherently untyped, it is obvious that any memory transfer operation on it is a bytewise operation so the “Bytes” suffix adds only verbosity and no clarity. An unsized version of this method will also be added to `UnsafeMutableRawBufferPointer`, but keeping the “`Bytes`” suffix. + +We do not rename the `count` property on `UnsafeMutableRawBufferPointer` to `bytes` since this could be confused with the actual buffer data. + +- **rename `count` in `UnsafeMutableRawBufferPointer.allocate(count:)` to `bytes` and add an `alignedTo` parameter to make it `UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)`** + +This brings it in line with the `UnsafeMutableRawPointer` allocator, and avoids the contradictory and inconsistent use of `count` to represent a byte quantity. Currently `UnsafeMutableRawBufferPointer.allocate(count:)` aligns to the size of `UInt`, an assumption not shared by its plain variant. + +- **add an `init(mutating:)` initializer to `UnsafeMutableBufferPointer`** + +This makes it much easier to make a mutable copy of an immutable buffer pointer. Such an initializer already exists on `UnsafeMutableRawBufferPointer`, so adding one to `UnsafeMutableBufferPointer` is also necessary for consistency. The reverse initializer, from `UnsafeMutableBufferPointer` to `UnsafeBufferPointer` should also be added for completeness. + +- **add a mutable overload to the `copy(from:)` method on `UnsafeMutableRawBufferPointer`, the `initialize(at:from:)` and `assign(at:from:)` methods on `UnsafeMutableBufferPointer`, and the `initializeMemory(as:at:from:)` method on `UnsafeMutableRawBufferPointer`** + +Currently, for plain pointers, there is a compiler subtyping relationship between `UnsafePointer` and `UnsafeMutablePointer`. No such relationship exists between `UnsafeBufferPointer` and `UnsafeMutableBufferPointer` or their raw counterparts, so it is necessary to provide mutable overloads for these functions. + +- **add `deallocate()` to all pointer types, replacing any existing deallocation methods** + +Removing `capacity` from `deallocate(capacity:)` will end the confusion over what `deallocate()` does, making it obvious that `deallocate()` will free the *entire* memory block at `self`, just as if `free()` were called on it. + +The old `deallocate(capacity:)` method should be marked as `deprecated` and eventually removed since it currently encourages dangerously incorrect code. This avoids misleading future users, encourages current users to address this potentially catastrophic memory bug, and leaves the possibility open for us to add a `deallocate(capacity:)` method in the future, or perhaps even a `reallocate(toCapacity:)` method. + +Along similar lines, the `bytes` and `alignedTo` parameters should be removed from the `deallocate(bytes:alignedTo:)` method on `UnsafeMutableRawPointer` and `UnsafeRawPointer`. + +An unsized `deallocate()` method should be added to all pointer types, even immutable ones, as Swift’s memory model does not require memory to be mutable for deallocation. This fixes [SR-3309](https://bugs.swift.org/browse/SR-3309). Note, immutable raw buffer pointers already support this API. + +> note: changes to deallocation methods are not listed in the type-by-type overview below. All items in the following list are either non-source breaking, or trivially automigratable. + +### `UnsafePointer` + +#### Existing methods + +``` +func withMemoryRebound(to:capacity:_:) -> Result +``` + +### `UnsafeMutablePointer` + +#### Existing methods + +``` +static +func allocate(capacity:) -> UnsafeMutablePointer + +func initialize(from:count:) +func moveInitialize(from:count:) + +func assign(from:count:) +func moveAssign(from:count:) + +func deinitialize(count:) +func withMemoryRebound(to:capacity:_:) -> Result +``` + +#### Renamed methods + +```diff +--- func initialize(to:count:) ++++ func initialize(repeating:count:) +``` + +#### New methods + +```diff ++++ func assign(repeating:count:) +``` + +### `UnsafeRawPointer` + +#### Existing methods + +``` +func bindMemory(to:capacity:) -> UnsafePointer +``` + +### `UnsafeMutableRawPointer` + +#### Existing methods + +``` +static +func allocate(bytes:alignedTo:) -> UnsafeMutableRawPointer + +func bindMemory(to:capacity:) -> UnsafeMutablePointer +``` + +#### Renamed methods + +```diff +--- func initializeMemory(as:at:count:to:) -> UnsafeMutablePointer ++++ func initializeMemory(as:at:repeating:count:) -> UnsafeMutablePointer + +--- func copyBytes(from:count:) ++++ func copy(from:bytes:) +``` + +#### New arguments + +```diff +--- func initializeMemory(as:from:count:) -> UnsafeMutablePointer +--- func moveInitializeMemory(as:from:count:) -> UnsafeMutablePointer ++++ func initializeMemory(as:at:from:count:) -> UnsafeMutablePointer ++++ func moveInitializeMemory(as:at:from:count:) -> UnsafeMutablePointer +``` + +> note: The new `at:` argument has a backwards-compatible default argument of `0`. This not only makes sense, and is consistent with the existing default value of `at:` in `initializeMemory(as:at:count:to:)`, it also prevents source breakage. + +> note: We are adding a *new* default argument of `MemoryLayout.alignment` for the `alignment` parameter in `allocate(bytes:alignedTo:)`. The rationale is that Swift is introducing a language-level default guarantee of word-aligned storage, so the default argument reflects Swift’s memory model. Higher alignments (such as 16-byte alignment) should be specified explicitly by the user. + +### `UnsafeBufferPointer` + +#### New methods + +```diff ++++ func deallocate() + ++++ withMemoryRebound(to:_:) -> Result +``` + +### `UnsafeMutableBufferPointer` + +#### New methods + +```diff ++++ static ++++ func allocate(capacity:) -> UnsafeMutableBufferPointer ++++ func deallocate() + ++++ func initialize(repeating:) ++++ func initialize(at:from:) ++++ func moveInitialize(at:from:) + ++++ func assign(repeating:) ++++ func assign(at:from:) ++++ func moveAssign(at:from:) + ++++ func deinitialize(at:count:) ++++ func withMemoryRebound(to:_:) -> Result +``` + +### `UnsafeRawBufferPointer` + +#### Existing methods + +``` +deallocate() +``` + +#### New methods + +```diff ++++ func bindMemory(to:) -> UnsafeBufferPointer +``` + +### `UnsafeMutableRawBufferPointer` + +#### Existing methods + +``` +deallocate() +``` + +#### New/renamed arguments + +```diff +--- static +--- func allocate(count:) -> UnsafeMutableRawBufferPointer ++++ static ++++ func allocate(bytes:alignedTo:) -> UnsafeMutableRawBufferPointer + +--- func copyBytes(from:) ++++ func copyBytes(at:from:) +``` + +#### New methods + +```diff ++++ func initializeMemory(as:repeating:) -> UnsafeMutableBufferPointer ++++ func initializeMemory(as:at:from:) -> UnsafeMutableBufferPointer ++++ func moveInitializeMemory(as:at:from:) -> UnsafeMutableBufferPointer + ++++ func bindMemory(to:) -> UnsafeMutableBufferPointer +``` + +> note: for backwards compatibility, the `alignedTo:` argument in `allocate(bytes:alignedTo:)` should take a default value of `MemoryLayout.alignment`. This requires [SR-5664](https://bugs.swift.org/browse/SR-5664) to be fixed before it will work properly. + +> note: The new `at:` argument in `copyBytes(at:from:)` has a backwards-compatible default argument of `0`. This poses no risk to memory state safety, since this method can only perform a bytewise copy anyways. + +## What this proposal does not do + +- **attempt to fully support partial initialization** + +This proposal attempts to design a buffer interface that provides some semblance of memory state safety. However, it does not fully address issues relating to ergonomics such as + + - overloading `+` for buffer pointers, allowing “pointer arithmetic” to be performed on buffer pointers + - easier buffer pointer slicing, which does not produce wasteful `MutableRandomAccessSlice>` structures + +nor does it attempt to design a higher level buffer type which would be able to provide stronger memory state guarantees. + +We expect possible solutions to these problems to purely additive, and would not require modifying the methods this proposal will introduce. + +- **address problems relating to the generic `Sequence` buffer API** + +Buffer pointers are currently missing generic `assign(from:S)` and `initializeMemory(as:S.Element.Type, from:S)` methods. The existing protocol oriented API also lacks polish and is inconvenient to use. (For example, it returns tuples.) This is an issue that can be tackled separately from the lower-level buffer-pointer-to-buffer-pointer API. + +## Detailed design + +```diff +struct UnsafePointer +{ ++++ func deallocate() + + func withMemoryRebound(to:T.Type, capacity:Int, _ body:(UnsafePointer) -> Result) + -> Result +} + +struct UnsafeMutablePointer +{ +--- static func allocate(capacity:Int) -> UnsafeMutablePointer ++++ static func allocate(capacity:Int = 1) -> UnsafeMutablePointer + +--- func deallocate(capacity:Int) ++++ func deallocate() + +--- func initialize(to:Pointee, count:Int = 1) ++++ func initialize(repeating:Pointee, count:Int = 1) + func initialize(from:UnsafePointer, count:Int) + moveInitialize(from:UnsafeMutablePointer, count:Int) + ++++ func assign(repeating:Pointee, count:Int = 1) + func assign(from:UnsafePointer, count:Int) + func moveAssign(from:UnsafeMutablePointer, count:Int) + +--- func deinitialize(count:Int) ++++ func deinitialize(count:Int = 1) + + func withMemoryRebound(to:T.Type, capacity:Int, _ body:(UnsafeMutablePointer) -> Result) + -> Result +} + +struct UnsafeRawPointer +{ +--- func deallocate(bytes:Int, alignedTo:Int) ++++ func deallocate() + + func bindMemory(to:T.Type, count:Int) -> UnsafeMutablePointer +} + +struct UnsafeMutableRawPointer +{ +--- static +--- func allocate(bytes:Int, alignedTo:Int) -> UnsafeMutableRawPointer ++++ static ++++ func allocate(bytes:Int, alignedTo:Int = MemoryLayout.alignment) ++++ -> UnsafeMutableRawPointer +--- func deallocate(bytes:Int, alignedTo:Int) ++++ func deallocate() + +--- func initializeMemory(as:T.Type, at:Int = 0, count:Int = 1, to:T) -> UnsafeMutablePointer ++++ func initializeMemory(as:T.Type, at:Int = 0, repeating:T, count:Int = 1) -> UnsafeMutablePointer + +--- func initializeMemory(as:T.Type, from:UnsafePointer, count:Int) -> UnsafeMutablePointer +--- func moveInitializeMemory(as:T.Type, from:UnsafeMutablePointer, count:Int) +--- -> UnsafeMutablePointer ++++ func initializeMemory(as:T.Type, at:Int = 0, from:UnsafePointer, count:Int) ++++ -> UnsafeMutablePointer ++++ func moveInitializeMemory(as:T.Type, at:Int = 0, from:UnsafeMutablePointer, count:Int) ++++ -> UnsafeMutablePointer + + func bindMemory(to:T.Type, count:Int) -> UnsafeMutablePointer + +--- func copyBytes(from:UnsafeRawPointer, count:Int) ++++ func copy(from:UnsafeRawPointer, bytes:Int) +} + +struct UnsafeBufferPointer +{ ++++ init(_:UnsafeMutableBufferPointer) + ++++ func deallocate() + ++++ func withMemoryRebound ++++ (to:T.Type, _ body:(UnsafeBufferPointer) -> Result) +} + +struct UnsafeMutableBufferPointer +{ ++++ init(mutating:UnsafeBufferPointer) + ++++ static ++++ func allocate(capacity:Int) -> UnsafeMutableBufferPointer + ++++ func initialize(repeating:Element) ++++ func initialize(at:Int, from:UnsafeBufferPointer) ++++ func initialize(at:Int, from:UnsafeMutableBufferPointer) ++++ func moveInitialize(at:Int, from:UnsafeMutableBufferPointer) + ++++ func assign(repeating:Element) ++++ func assign(at:Int, from:UnsafeBufferPointer) ++++ func assign(at:Int, from:UnsafeMutableBufferPointer) ++++ func moveAssign(at:Int, from:UnsafeMutableBufferPointer) + ++++ func deinitialize(at:Int, count:Int) + ++++ func withMemoryRebound ++++ (to:T.Type, _ body:(UnsafeMutableBufferPointer) -> Result) +} + +struct UnsafeRawBufferPointer +{ + func deallocate() + ++++ func bindMemory(to:T.Type) -> UnsafeBufferPointer +} + +struct UnsafeMutableRawBufferPointer +{ +--- static func allocate(count:Int) -> UnsafeMutableRawBufferPointer ++++ static func allocate(bytes:Int, alignedTo:Int = MemoryLayout.alignment) ++++ -> UnsafeMutableRawBufferPointer + func deallocate() + ++++ func initializeMemory(as:T.Type, repeating:T) -> UnsafeMutableBufferPointer ++++ func initializeMemory(as:T.Type, at:Int, from:UnsafeBufferPointer) ++++ -> UnsafeMutableBufferPointer ++++ func initializeMemory(as:T.Type, at:Int, from:UnsafeMutableBufferPointer) ++++ -> UnsafeMutableBufferPointer ++++ func moveInitializeMemory(as:T.Type, at:Int, from:UnsafeMutableBufferPointer) ++++ -> UnsafeMutableBufferPointer + ++++ func bindMemory(to:T.Type) -> UnsafeMutableBufferPointer + +--- func copyBytes(from:UnsafeRawBufferPointer) ++++ func copyBytes(at:Int = 0, from:UnsafeRawBufferPointer) ++++ func copyBytes(at:Int = 0, from:UnsafeMutableRawBufferPointer) +} +``` + +## Source compatibility + +- **fix the ordering of the arguments in `initializeMemory(as:at:count:to:)` and rename the argument `to:` to `repeating:` in all repeated-value copy functions** + +This change is source breaking but can be trivially automigrated. + +- **add the repeated-value copy assignment method `assign(repeating:count:)`** + +This change is purely additive. + +- **rename `copyBytes(from:count:)` to `copy(from:bytes:)` on `UnsafeMutableRawPointer`** + +This change is source breaking but can be trivially automigrated. + +- **rename `count` in `UnsafeMutableRawBufferPointer.allocate(count:)` to `bytes` and add an `alignedTo` parameter to make it `UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)`** + +This change is source breaking but can be trivially automigrated. The `alignedTo:` parameter can be filled in with `MemoryLayout.stride`. If [SR-5664](https://bugs.swift.org/browse/SR-5664) is fixed, `MemoryLayout.stride` can even be provided as a default argument. + +- **add an `init(mutating:)` initializer to `UnsafeMutableBufferPointer`** + +This change is purely additive. + +- **add a mutable overload to the `copy(from:)` method on `UnsafeMutableRawBufferPointer`, the `initialize(at:from:)` and `assign(at:from:)` methods on `UnsafeMutableBufferPointer`, and the `initializeMemory(as:at:from:)` method on `UnsafeMutableRawBufferPointer`** + +This change is purely additive. + +- **add `deallocate()` to all pointer types, replacing any existing deallocation methods** + +This change is source-breaking, but this is a Good Thing™. The current API encourages incorrect code to be written, and sets us up for potentially catastrophic source breakage down the road should the implementations of `deallocate(capacity:)` and `deallocate(bytes:alignedTo:)` ever be “fixed”, so users should be forced to stop using them as soon as possible. + + +## Effect on ABI stability + +Removing sized deallocators changes the existing ABI, as will renaming some of the methods and their argument labels. + +## Effect on API resilience + +Some proposed changes in this proposal change the public API. + +Removing sized deallocators right now will break ABI, but offers increased ABI and API stability in the future as reallocator methods can be added in the future without having to rename `deallocate(capacity:)` which currently occupies a “reallocator” name, but has “`free()`” behavior. + +This proposal seeks to tackle all the breaking changes required for such an overhaul of Swift pointers, and leaves unanswered only additive changes that still need to be made in the future, reducing the need for future breakage. + +## Alternatives considered + +- **keeping sized deallocators and fixing the stdlib implementation instead** + +Instead of dropping the `capacity` parameter from `deallocate(capacity:)`, we could fix the underlying implementation so that the function actually deallocates `capacity`’s worth of memory. However this would be catastrophically, and silently, source-breaking as existing code would continue compiling, but suddenly start leaking or segfaulting at runtime. `deallocate(capacity:)` can always be added back at a later date without breaking ABI or API, once users have been forced to address this potential bug. + +- **adding an initializer `UnsafeMutableBufferPointer.init(allocatingCount:)` instead of a type method to `UnsafeMutableBufferPointer`** + +The allocator could be expressed as an initializer instead of a type method. However since allocation involves acquisition of an external resource, perhaps it is better to keep with the existing convention that allocation is performed differently than regular buffer pointer construction. + +- **using the argument label `value:` instead of `repeating:` in methods such as `initialize(repeating:count:)` (originally `initialize(to:count:)`)** + +The label `value:` or `toValue:` doesn’t fully capture the repeating nature of the argument, and is inconsistent with `Array.init(repeating:count:)`. While `value:` sounds less strange when `count == 1`, on consistency and technical correctness, `repeating:` is the better term. Furthermore, `value` is a common variable name, meaning that function calls with `value:` as the label would be prone to looking like this: + +```swift +ptr.initialize(value: value) +``` + From 3c08560e7f654c8f9357dac9fa05a4893a7faa11 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 2 Sep 2017 06:14:02 +0100 Subject: [PATCH 0439/4563] [SE-0184] Move to `proposals` folder --- .../0184-unsafe-pointers-add-missing.md | 1 + 1 file changed, 1 insertion(+) rename 0184-unsafe-pointers-add-missing.md => proposals/0184-unsafe-pointers-add-missing.md (99%) diff --git a/0184-unsafe-pointers-add-missing.md b/proposals/0184-unsafe-pointers-add-missing.md similarity index 99% rename from 0184-unsafe-pointers-add-missing.md rename to proposals/0184-unsafe-pointers-add-missing.md index 23a21a10c3..4b42e7a1e3 100644 --- a/0184-unsafe-pointers-add-missing.md +++ b/proposals/0184-unsafe-pointers-add-missing.md @@ -4,6 +4,7 @@ * Author: [Kelvin Ma (“Taylor Swift”)](https://github.com/kelvin13) * Review Manager: TBD * Status: **Awaiting review** +* Pull Request: [apple/swift#11464](https://github.com/apple/swift/pull/11464) ## Introduction From e805cef89cd7fa19a0fed54e32ff63d1dc24007f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 1 Sep 2017 22:24:44 -0700 Subject: [PATCH 0440/4563] Initiate review of SE-0184: Unsafe[Mutable][Raw][Buffer]Pointer: add missing methods, adjust existing labels for clarity, and remove deallocation size --- proposals/0184-unsafe-pointers-add-missing.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0184-unsafe-pointers-add-missing.md b/proposals/0184-unsafe-pointers-add-missing.md index 4b42e7a1e3..b77dd1fb16 100644 --- a/proposals/0184-unsafe-pointers-add-missing.md +++ b/proposals/0184-unsafe-pointers-add-missing.md @@ -1,9 +1,9 @@ # Unsafe[Mutable][Raw][Buffer]Pointer: add missing methods, adjust existing labels for clarity, and remove deallocation size -* Proposal: [SE-0184](0184-improved-pointers.md) +* Proposal: [SE-0184](0184-unsafe-pointers-add-missing.md) * Author: [Kelvin Ma (“Taylor Swift”)](https://github.com/kelvin13) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (September 1...7, 2017)** * Pull Request: [apple/swift#11464](https://github.com/apple/swift/pull/11464) ## Introduction From 2e0a04d5fdc05de30bcd7882595591f595db6fdc Mon Sep 17 00:00:00 2001 From: Greg Spiers Date: Tue, 9 May 2017 09:24:06 +0100 Subject: [PATCH 0441/4563] Add draft proposal for ownership keyword removal in protocols. --- ...-ownership-keyword-support-in-protocols.md | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 proposals/NNNN-remove-ownership-keyword-support-in-protocols.md diff --git a/proposals/NNNN-remove-ownership-keyword-support-in-protocols.md b/proposals/NNNN-remove-ownership-keyword-support-in-protocols.md new file mode 100644 index 0000000000..8fb8de0bd7 --- /dev/null +++ b/proposals/NNNN-remove-ownership-keyword-support-in-protocols.md @@ -0,0 +1,62 @@ +# Remove ownership keyword support in protocols + +* Proposal: [SE-NNNN](NNNN-remove-ownership-keyword-support-in-protocols.md) +* Authors: [Greg Spiers](https://github.com/gspiers) +* Review Manager: TBD +* Status: +* Pull Request: [apple/swift#11744](https://github.com/apple/swift/pull/11744) +* Bug: [SR-479](https://bugs.swift.org/browse/SR-479) + +## Introduction + +This proposal removes support for the keywords `weak` and `unowned` for property declarations in a protocol. + +Swift-evolution thread: [Ownership on protocol property requirements](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170501/036495.html) thread. + +## Motivation + +Currently it's possible to use the weak/unowned keywords for a property requirement in a protocol. This can lead to confusion as specifying one of these keywords does not enforce or raise any warnings in the adopting type of that protocol: + +```swift + +class A {} + +protocol P { + weak var weakVar: A? { get set } +} + +class B: P { + var weakVar: A? // Not declared weak, no compiler warning/error +} + +``` + +This can lead to unexpected and surprising behaviour from the point of view of users. The keywords themselves are currently meaningless inside of a protocol but look like they would have an effect when the protocol is adopted. + +This change is consistent with removing keywords that do not have any meaning like `final` in protocol extensions: [SE-0164](0164-remove-final-support-in-protocol-extensions.md). + +## Proposed solution + +Although the case could be made that the keywords should have meaning in a protocol, as they are currently implemented today they don't have an effect. This proposal aims to cleanup the misleading syntax and isn't meant to remove functionality only correct to existing behaviour. + +This proposal suggests removing support for `weak` and `unowned` in a protocol. + +## Detailed design + +In existing Swift modes, 3 and 4, the compiler will warn about the use of `weak` and `unowned` in a protocol and suggest a fix to remove the keywords. In Swift 5 mode the compiler will error and offer a fixit to remove the keywords. + +## Source compatibility + +This is a source breaking change but one that would only correct code that already has broken assumptions. For existing Swift modes, 3 and 4, the compiler will raise a compilation warning instead of an error. + +## Effect on ABI stability + +This proposal does not affect ABI stability. + +## Effect on API resilience + +This proposal does not affect API resilience. + +## Alternatives considered + +There is an argument in making `weak` and `unowned` have meaning in a protocol but this does open up other questions and is probably better as a topic of a separate discussion/proposal. As this would be additive it can be addressed at a later point when we have a clearer understanding. From b9d815099453912069998b579948a3319246042b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A1n=20Canzonetta?= Date: Sat, 2 Sep 2017 14:36:33 -0300 Subject: [PATCH 0442/4563] Update stale status (#746) --- proposals/0176-enforce-exclusive-access-to-memory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0176-enforce-exclusive-access-to-memory.md b/proposals/0176-enforce-exclusive-access-to-memory.md index 4c6ddbbabe..76ee6602d6 100644 --- a/proposals/0176-enforce-exclusive-access-to-memory.md +++ b/proposals/0176-enforce-exclusive-access-to-memory.md @@ -3,7 +3,7 @@ * Proposal: [SE-0176](0176-enforce-exclusive-access-to-memory.md) * Authors: [John McCall](https://github.com/rjmccall) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted pending revision review (May 12...May 17)** +* Status: **Implemented (Swift 4)** * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/7e6816c22a29b0ba9bdf63ff92b380f9e963860a/proposals/0176-enforce-exclusive-access-to-memory.md) * Previous Discussion: [Email Thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170501/036308.html) From abe20baea443b7aa7ffe22a4d1202df442d9a434 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 3 Sep 2017 07:35:38 +0100 Subject: [PATCH 0443/4563] [SE-0104 Revision 4] Implemented (Swift 4) * * --- proposals/0104-improved-integers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0104-improved-integers.md b/proposals/0104-improved-integers.md index d543792518..8dd8177690 100644 --- a/proposals/0104-improved-integers.md +++ b/proposals/0104-improved-integers.md @@ -3,7 +3,7 @@ * Proposal: [SE-0104](0104-improved-integers.md) * Authors: [Dave Abrahams](https://github.com/dabrahams), [Maxim Moiseev](https://github.com/moiseev) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active review (July 20...24)** +* Status: **Implemented (Swift 4)** * Bug: [SR-3196](https://bugs.swift.org/browse/SR-3196) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/0440700fc555a6c72abb4af807c8b79fb1bec592/proposals/0104-improved-integers.md), From f19236c66a4e1a3d1c6914e6f21c871b40d3329e Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 3 Sep 2017 08:32:24 +0100 Subject: [PATCH 0444/4563] [SE-0183] Implemented (Swift 4) * * * --- proposals/0183-substring-affordances.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0183-substring-affordances.md b/proposals/0183-substring-affordances.md index 32866b5725..7e0ddae817 100644 --- a/proposals/0183-substring-affordances.md +++ b/proposals/0183-substring-affordances.md @@ -3,8 +3,9 @@ * Proposal: [SE-0183](0183-substring-affordances.md) * Author: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Accepted** +* Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-July/000395.html) +* Bug: [SR-4933](https://bugs.swift.org/browse/SR-4933) ## Introduction From 087d3fa5034719e3c3e5076bcec9af80af04c319 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 3 Sep 2017 09:14:12 +0100 Subject: [PATCH 0445/4563] [SE-0075] Implemented (Swift 4.1) * * * * --- proposals/0075-import-test.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0075-import-test.md b/proposals/0075-import-test.md index b61015c984..579cd5a6ef 100644 --- a/proposals/0075-import-test.md +++ b/proposals/0075-import-test.md @@ -3,7 +3,7 @@ * Proposal: [SE-0075](0075-import-test.md) * Author: [Erica Sadun](http://github.com/erica) * Review Manager: [Chris Lattner](http://github.com/lattner) -* Status: **Accepted** +* Status: **Implemented (Swift 4.1)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000159.html) * Bug: [SR-1560](https://bugs.swift.org/browse/SR-1560) From b58fc6de03fee7cf4847d79d659a1735448918b7 Mon Sep 17 00:00:00 2001 From: Matt Eaton Date: Mon, 4 Sep 2017 12:43:32 -0500 Subject: [PATCH 0446/4563] Update 0181-package-manager-cpp-language-version.md (#748) Minor grammar fix to reduce any confusion on the intention of the sentence. --- proposals/0181-package-manager-cpp-language-version.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0181-package-manager-cpp-language-version.md b/proposals/0181-package-manager-cpp-language-version.md index 6ff99ac1da..40b7c3ed32 100644 --- a/proposals/0181-package-manager-cpp-language-version.md +++ b/proposals/0181-package-manager-cpp-language-version.md @@ -33,7 +33,7 @@ let package = Package( ``` These settings will apply to all the C and C++ targets in the package. The -default value of these properties will be `nil`, i.e. language standard flag +default value of these properties will be `nil`, i.e., a language standard flag will not be passed when invoking the C/C++ compiler. _Once we get the build settings feature, we will deprecate these properties._ From 08ce36990bb94ee142004be497ca1ea7e828579b Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 5 Sep 2017 03:47:07 +0100 Subject: [PATCH 0447/4563] Update commit/PR links in template and proposals --- 0000-template.md | 2 +- proposals/0002-remove-currying.md | 2 +- proposals/0003-remove-var-parameters.md | 2 +- proposals/0004-remove-pre-post-inc-decrement.md | 2 +- proposals/0015-tuple-comparison-operators.md | 2 +- proposals/0020-if-swift-version.md | 2 +- proposals/0021-generalized-naming.md | 2 +- proposals/0022-objc-selectors.md | 2 +- proposals/0026-abstract-classes-and-methods.md | 0 proposals/0029-remove-implicit-tuple-splat.md | 2 +- proposals/0031-adjusting-inout-declarations.md | 2 +- proposals/0040-attributecolons.md | 2 +- ...clare-variables-in-case-labels-with-multiple-patterns.md | 2 +- proposals/0052-iterator-post-nil-guarantee.md | 2 +- proposals/0053-remove-let-from-function-parameters.md | 2 +- proposals/0054-abolish-iuo.md | 2 +- proposals/0055-optional-unsafe-pointers.md | 2 +- proposals/0063-swiftpm-system-module-search-paths.md | 2 +- proposals/0065-collections-move-indices.md | 2 +- proposals/0066-standardize-function-type-syntax.md | 2 +- proposals/0067-floating-point-protocols.md | 2 +- proposals/0072-eliminate-implicit-bridging-conversions.md | 4 +--- proposals/0085-package-manager-command-name.md | 2 +- proposals/0093-slice-base.md | 2 +- proposals/0109-remove-boolean.md | 0 proposals/0114-buffer-naming.md | 2 +- proposals/0117-non-public-subclassable-by-default.md | 2 +- proposals/0121-remove-optional-comparison-operators.md | 2 +- proposals/0128-unicodescalar-failable-initializer.md | 2 +- proposals/0133-rename-flatten-to-joined.md | 6 +++--- proposals/0134-rename-string-properties.md | 2 +- proposals/0136-memory-layout-of-values.md | 2 +- proposals/0147-move-unsafe-initialize-from.md | 2 +- proposals/0160-objc-inference.md | 2 +- proposals/0161-key-paths.md | 1 - proposals/0166-swift-archival-serialization.md | 3 +-- proposals/0167-swift-encoders.md | 3 +-- proposals/0173-swap-indices.md | 2 +- proposals/0178-character-unicode-view.md | 2 +- proposals/0179-swift-run-command.md | 2 +- proposals/0180-string-index-overhaul.md | 2 +- proposals/0181-package-manager-cpp-language-version.md | 2 +- proposals/0184-unsafe-pointers-add-missing.md | 2 +- proposals/0185-synthesize-equatable-hashable.md | 2 +- 44 files changed, 43 insertions(+), 48 deletions(-) mode change 100755 => 100644 proposals/0026-abstract-classes-and-methods.md mode change 100755 => 100644 proposals/0109-remove-boolean.md diff --git a/0000-template.md b/0000-template.md index 656a87ed73..442e549084 100644 --- a/0000-template.md +++ b/0000-template.md @@ -4,7 +4,7 @@ * Authors: [Author 1](https://github.com/swiftdev), [Author 2](https://github.com/swiftdev) * Review Manager: TBD * Status: **Awaiting review** -* Pull Request: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) +* Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) *During the review process, add the following fields as needed:* diff --git a/proposals/0002-remove-currying.md b/proposals/0002-remove-currying.md index cb7fc5411d..490a90e235 100644 --- a/proposals/0002-remove-currying.md +++ b/proposals/0002-remove-currying.md @@ -3,7 +3,7 @@ * Proposal: [SE-0002](0002-remove-currying.md) * Author: [Joe Groff](https://github.com/jckarter) * Status: **Implemented (Swift 3)** -* Commit: [apple/swift@983a674](https://github.com/apple/swift/commit/983a674e0ca35a85532d70a3eb61e71a6d024108) +* Implementation: [apple/swift@983a674](https://github.com/apple/swift/commit/983a674e0ca35a85532d70a3eb61e71a6d024108) ## Introduction diff --git a/proposals/0003-remove-var-parameters.md b/proposals/0003-remove-var-parameters.md index 42bd4d44cc..c889905cb6 100644 --- a/proposals/0003-remove-var-parameters.md +++ b/proposals/0003-remove-var-parameters.md @@ -5,7 +5,7 @@ * Review Manager: [Joe Pamer](https://github.com/jopamer) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/008145.html) -* Commit: [apple/swift@8a5ed40](https://github.com/apple/swift/commit/8a5ed405bf1f92ec3fc97fa46e52528d2e8d67d9) +* Implementation: [apple/swift@8a5ed40](https://github.com/apple/swift/commit/8a5ed405bf1f92ec3fc97fa46e52528d2e8d67d9) ## Note diff --git a/proposals/0004-remove-pre-post-inc-decrement.md b/proposals/0004-remove-pre-post-inc-decrement.md index 5e7d57640b..56386e0d10 100644 --- a/proposals/0004-remove-pre-post-inc-decrement.md +++ b/proposals/0004-remove-pre-post-inc-decrement.md @@ -3,7 +3,7 @@ * Proposal: [SE-0004](0004-remove-pre-post-inc-decrement.md) * Author: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** -* Commit: [apple/swift@8e12008](https://github.com/apple/swift/commit/8e12008d2b34a605f8766310f53d5668f3d50955) +* Implementation: [apple/swift@8e12008](https://github.com/apple/swift/commit/8e12008d2b34a605f8766310f53d5668f3d50955) ## Introduction diff --git a/proposals/0015-tuple-comparison-operators.md b/proposals/0015-tuple-comparison-operators.md index 61a7363469..d0335ddda8 100644 --- a/proposals/0015-tuple-comparison-operators.md +++ b/proposals/0015-tuple-comparison-operators.md @@ -5,7 +5,7 @@ * Review Manager: [Dave Abrahams](https://github.com/dabrahams) * Status: **Implemented (Swift 2.2)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/004423.html) -* Pull Request: [apple/swift#408](https://github.com/apple/swift/pull/408) +* Implementation: [apple/swift#408](https://github.com/apple/swift/pull/408) ## Introduction diff --git a/proposals/0020-if-swift-version.md b/proposals/0020-if-swift-version.md index 2ad7621e01..771a7eeb7e 100644 --- a/proposals/0020-if-swift-version.md +++ b/proposals/0020-if-swift-version.md @@ -4,7 +4,7 @@ * Author: [David Farler](https://github.com/bitjammer) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 2.2)** -* Commit: [apple/swift@c32fb8e](https://github.com/apple/swift/commit/c32fb8e7b9a67907e8b6580a46717c6a345ec7c6) +* Implementation: [apple/swift@c32fb8e](https://github.com/apple/swift/commit/c32fb8e7b9a67907e8b6580a46717c6a345ec7c6) ## Introduction diff --git a/proposals/0021-generalized-naming.md b/proposals/0021-generalized-naming.md index fd42e004f4..f35bdb8de8 100644 --- a/proposals/0021-generalized-naming.md +++ b/proposals/0021-generalized-naming.md @@ -5,7 +5,7 @@ * Review Manager: [Joe Groff](https://github.com/jckarter) * Status: **Implemented (Swift 2.2)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-January/000021.html) -* Commit: [apple/swift@ecfde0e](https://github.com/apple/swift/commit/ecfde0e71c61184989fde0f93f8d6b7f5375b99a) +* Implementation: [apple/swift@ecfde0e](https://github.com/apple/swift/commit/ecfde0e71c61184989fde0f93f8d6b7f5375b99a) ## Introduction diff --git a/proposals/0022-objc-selectors.md b/proposals/0022-objc-selectors.md index e0569414ff..119092ff5e 100644 --- a/proposals/0022-objc-selectors.md +++ b/proposals/0022-objc-selectors.md @@ -5,7 +5,7 @@ * Review Manager: [Joe Groff](https://github.com/jckarter) * Status: **Implemented (Swift 2.2)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160125/007797.html) -* Pull Request: [apple/swift#1170](https://github.com/apple/swift/pull/1170) +* Implementation: [apple/swift#1170](https://github.com/apple/swift/pull/1170) ## Introduction diff --git a/proposals/0026-abstract-classes-and-methods.md b/proposals/0026-abstract-classes-and-methods.md old mode 100755 new mode 100644 diff --git a/proposals/0029-remove-implicit-tuple-splat.md b/proposals/0029-remove-implicit-tuple-splat.md index bd0c5fcc0f..7b8f4976cd 100644 --- a/proposals/0029-remove-implicit-tuple-splat.md +++ b/proposals/0029-remove-implicit-tuple-splat.md @@ -5,7 +5,7 @@ * Review Manager: [Joe Groff](http://github.com/jckarter) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-February/000033.html) -* Commit: [apple/swift@8e12008](https://github.com/apple/swift/commit/8e12008d2b34a605f8766310f53d5668f3d50955) +* Implementation: [apple/swift@8e12008](https://github.com/apple/swift/commit/8e12008d2b34a605f8766310f53d5668f3d50955) ## Introduction diff --git a/proposals/0031-adjusting-inout-declarations.md b/proposals/0031-adjusting-inout-declarations.md index 8aa74dbda5..cf21931ecb 100644 --- a/proposals/0031-adjusting-inout-declarations.md +++ b/proposals/0031-adjusting-inout-declarations.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010571.html) -* Pull Request: [apple/swift#1333](https://github.com/apple/swift/pull/1333) +* Implementation: [apple/swift#1333](https://github.com/apple/swift/pull/1333) ## Introduction diff --git a/proposals/0040-attributecolons.md b/proposals/0040-attributecolons.md index 010637bf06..560737f027 100644 --- a/proposals/0040-attributecolons.md +++ b/proposals/0040-attributecolons.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160307/012100.html) -* Pull Request: [apple/swift#1537](https://github.com/apple/swift/pull/1537) +* Implementation: [apple/swift#1537](https://github.com/apple/swift/pull/1537) ## Introduction diff --git a/proposals/0043-declare-variables-in-case-labels-with-multiple-patterns.md b/proposals/0043-declare-variables-in-case-labels-with-multiple-patterns.md index e829ceb59b..69848ab3c3 100644 --- a/proposals/0043-declare-variables-in-case-labels-with-multiple-patterns.md +++ b/proposals/0043-declare-variables-in-case-labels-with-multiple-patterns.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160321/013250.html) -* Pull Request: [apple/swift#1383](https://github.com/apple/swift/pull/1383) +* Implementation: [apple/swift#1383](https://github.com/apple/swift/pull/1383) ## Introduction diff --git a/proposals/0052-iterator-post-nil-guarantee.md b/proposals/0052-iterator-post-nil-guarantee.md index 1be29dce17..30b7c2c8f4 100644 --- a/proposals/0052-iterator-post-nil-guarantee.md +++ b/proposals/0052-iterator-post-nil-guarantee.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000135.html) -* Pull Request: [apple/swift#1702](https://github.com/apple/swift/pull/1702) +* Implementation: [apple/swift#1702](https://github.com/apple/swift/pull/1702) ## Introduction diff --git a/proposals/0053-remove-let-from-function-parameters.md b/proposals/0053-remove-let-from-function-parameters.md index 6f7820c6c7..88d14a9f40 100644 --- a/proposals/0053-remove-let-from-function-parameters.md +++ b/proposals/0053-remove-let-from-function-parameters.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-March/000082.html) -* Pull Request: [apple/swift#1812](https://github.com/apple/swift/pull/1812) +* Implementation: [apple/swift#1812](https://github.com/apple/swift/pull/1812) ## Introduction diff --git a/proposals/0054-abolish-iuo.md b/proposals/0054-abolish-iuo.md index 05d87f0173..98093158d2 100644 --- a/proposals/0054-abolish-iuo.md +++ b/proposals/0054-abolish-iuo.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-March/000084.html) -* Pull Request: [apple/swift#2322](https://github.com/apple/swift/pull/2322) +* Implementation: [apple/swift#2322](https://github.com/apple/swift/pull/2322) ## Introduction diff --git a/proposals/0055-optional-unsafe-pointers.md b/proposals/0055-optional-unsafe-pointers.md index 2d823d5c2d..7e25998e0e 100644 --- a/proposals/0055-optional-unsafe-pointers.md +++ b/proposals/0055-optional-unsafe-pointers.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-March/000086.html) -* Pull Request: [apple/swift#1878](https://github.com/apple/swift/pull/1878) +* Implementation: [apple/swift#1878](https://github.com/apple/swift/pull/1878) ## Introduction diff --git a/proposals/0063-swiftpm-system-module-search-paths.md b/proposals/0063-swiftpm-system-module-search-paths.md index d0af1f70b2..9c7e91a353 100644 --- a/proposals/0063-swiftpm-system-module-search-paths.md +++ b/proposals/0063-swiftpm-system-module-search-paths.md @@ -5,7 +5,7 @@ * Review Manager: [Anders Bertelrud](https://github.com/abertelrud) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-April/000103.html) -* Pull Request: [apple/swift-package-manager#257](https://github.com/apple/swift-package-manager/pull/257) +* Implementation: [apple/swift-package-manager#257](https://github.com/apple/swift-package-manager/pull/257) ## Introduction diff --git a/proposals/0065-collections-move-indices.md b/proposals/0065-collections-move-indices.md index 3ba442df0f..7d1393f0d7 100644 --- a/proposals/0065-collections-move-indices.md +++ b/proposals/0065-collections-move-indices.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-April/000115.html), [Swift-evolution thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160229/011552.html) -* Pull Request: [apple/swift#2108](https://github.com/apple/swift/pull/2108) +* Implementation: [apple/swift#2108](https://github.com/apple/swift/pull/2108) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/21fac2e8034e79e4f44c1c8799808fc8cba83395/proposals/0065-collections-move-indices.md), [2](https://github.com/apple/swift-evolution/blob/1a821cf7ccbdf1d7566e9ce2e991bdd835ba3b7d/proposals/0065-collections-move-indices.md), [3](https://github.com/apple/swift-evolution/blob/d44c3e7c189ba39ddf8a914ae8b78b71f88fdcdf/proposals/0065-collections-move-indices.md), [4](https://github.com/apple/swift-evolution/blob/57639040dc08d2f0b16d9bda527db069589b58d1/proposals/0065-collections-move-indices.md) ## Summary diff --git a/proposals/0066-standardize-function-type-syntax.md b/proposals/0066-standardize-function-type-syntax.md index 58f092627e..0cf669f685 100644 --- a/proposals/0066-standardize-function-type-syntax.md +++ b/proposals/0066-standardize-function-type-syntax.md @@ -5,7 +5,7 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000138.html) -* Commit: [apple/swift@3d2b5bc](https://github.com/apple/swift/commit/3d2b5bcc5350e1dea2ed8a0a95cd12ff5c760f24) +* Implementation: [apple/swift@3d2b5bc](https://github.com/apple/swift/commit/3d2b5bcc5350e1dea2ed8a0a95cd12ff5c760f24) ## Introduction diff --git a/proposals/0067-floating-point-protocols.md b/proposals/0067-floating-point-protocols.md index bfe6479351..9cdba205ec 100644 --- a/proposals/0067-floating-point-protocols.md +++ b/proposals/0067-floating-point-protocols.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000121.html) -* Pull Request: [apple/swift#2453](https://github.com/apple/swift/pull/2453) +* Implementation: [apple/swift#2453](https://github.com/apple/swift/pull/2453) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/fb1368a6a5474f57aa8f1846b5355d18753098f3/proposals/0067-floating-point-protocols.md) ## Introduction diff --git a/proposals/0072-eliminate-implicit-bridging-conversions.md b/proposals/0072-eliminate-implicit-bridging-conversions.md index b1d9006042..4db795e54e 100644 --- a/proposals/0072-eliminate-implicit-bridging-conversions.md +++ b/proposals/0072-eliminate-implicit-bridging-conversions.md @@ -5,9 +5,7 @@ * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000137.html) -* Pull Requests: [apple/swift#2419](https://github.com/apple/swift/pull/2419), - [apple/swift#2440](https://github.com/apple/swift/pull/2440), - [apple/swift#2441](https://github.com/apple/swift/pull/2441) +* Implementation: [apple/swift#2419](https://github.com/apple/swift/pull/2419) ## Introduction In Swift 1.2, we attempted to remove all implicit bridging conversions from the language. Unfortunately, problems with how the v1.2 compiler imported various un-annotated Objective-C APIs caused us to scale back on our ambitions. diff --git a/proposals/0085-package-manager-command-name.md b/proposals/0085-package-manager-command-name.md index 15deb18dd8..b956d0ead5 100644 --- a/proposals/0085-package-manager-command-name.md +++ b/proposals/0085-package-manager-command-name.md @@ -5,7 +5,7 @@ * Review Manager: [Daniel Dunbar](http://github.com/ddunbar) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160516/017728.html) -* Pull Request: [apple/swift-package-manager#364](https://github.com/apple/swift-package-manager/pull/364) +* Implementation: [apple/swift-package-manager#364](https://github.com/apple/swift-package-manager/pull/364) ## Note diff --git a/proposals/0093-slice-base.md b/proposals/0093-slice-base.md index 8dd553ffbd..672757767b 100644 --- a/proposals/0093-slice-base.md +++ b/proposals/0093-slice-base.md @@ -5,7 +5,7 @@ * Review Manager: [Dave Abrahams](https://github.com/dabrahams) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160523/019109.html) -* Pull Request: [apple/swift#2929](https://github.com/apple/swift/pull/2929) +* Implementation: [apple/swift#2929](https://github.com/apple/swift/pull/2929) ## Introduction diff --git a/proposals/0109-remove-boolean.md b/proposals/0109-remove-boolean.md old mode 100755 new mode 100644 diff --git a/proposals/0114-buffer-naming.md b/proposals/0114-buffer-naming.md index 9bc4db0560..f4668c5472 100644 --- a/proposals/0114-buffer-naming.md +++ b/proposals/0114-buffer-naming.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](http://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-July/000221.html) -* Pull Request: [apple/swift#3374](https://github.com/apple/swift/pull/3374) +* Implementation: [apple/swift#3374](https://github.com/apple/swift/pull/3374) ## Introduction diff --git a/proposals/0117-non-public-subclassable-by-default.md b/proposals/0117-non-public-subclassable-by-default.md index d4eb7a4d5c..8039e328a9 100644 --- a/proposals/0117-non-public-subclassable-by-default.md +++ b/proposals/0117-non-public-subclassable-by-default.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](http://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-July/000268.html) -* Pull Request: [apple/swift#3882](https://github.com/apple/swift/pull/3882) +* Implementation: [apple/swift#3882](https://github.com/apple/swift/pull/3882) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/367086f18a5deaf8f9dfbe3f5a4846ef19addf38/proposals/0117-non-public-subclassable-by-default.md), [2](https://github.com/apple/swift-evolution/blob/2989538daa1640cfa6a56f80b5c7599967af0905/proposals/0117-non-public-subclassable-by-default.md), [3](https://github.com/apple/swift-evolution/blob/15c18d24adb7e701ae831b643e0803f1b6e601d9/proposals/0117-non-public-subclassable-by-default.md) ## Introduction diff --git a/proposals/0121-remove-optional-comparison-operators.md b/proposals/0121-remove-optional-comparison-operators.md index 137ac717e1..5a8b90fee4 100644 --- a/proposals/0121-remove-optional-comparison-operators.md +++ b/proposals/0121-remove-optional-comparison-operators.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](http://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-July/000245.html) -* Pull Request: [apple/swift#3637](https://github.com/apple/swift/pull/3637) +* Implementation: [apple/swift#3637](https://github.com/apple/swift/pull/3637) ## Introduction diff --git a/proposals/0128-unicodescalar-failable-initializer.md b/proposals/0128-unicodescalar-failable-initializer.md index 63e83f92c2..dfac20a297 100644 --- a/proposals/0128-unicodescalar-failable-initializer.md +++ b/proposals/0128-unicodescalar-failable-initializer.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](http://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-July/000259.html) -* Pull Request: [apple/swift#3662](https://github.com/apple/swift/pull/3662) +* Implementation: [apple/swift#3662](https://github.com/apple/swift/pull/3662) ## Introduction diff --git a/proposals/0133-rename-flatten-to-joined.md b/proposals/0133-rename-flatten-to-joined.md index 583ef23d94..397fe41cc8 100644 --- a/proposals/0133-rename-flatten-to-joined.md +++ b/proposals/0133-rename-flatten-to-joined.md @@ -5,9 +5,9 @@ * Review Manager: [Chris Lattner](http://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-July/000265.html) -* Pull Requests: [apple/swift#3809](https://github.com/apple/swift/pull/3809), - [apple/swift#3838](https://github.com/apple/swift/pull/3838), - [apple/swift#3839](https://github.com/apple/swift/pull/3839) +* Implementation: [apple/swift#3809](https://github.com/apple/swift/pull/3809), + [apple/swift#3838](https://github.com/apple/swift/pull/3838), + [apple/swift#3839](https://github.com/apple/swift/pull/3839) ## Introduction diff --git a/proposals/0134-rename-string-properties.md b/proposals/0134-rename-string-properties.md index 2e12c1545a..6e5398ed48 100644 --- a/proposals/0134-rename-string-properties.md +++ b/proposals/0134-rename-string-properties.md @@ -5,7 +5,7 @@ * Review Manager: [Chris Lattner](http://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-July/000266.html) -* Pull Request: [apple/swift#3816](https://github.com/apple/swift/pull/3816) +* Implementation: [apple/swift#3816](https://github.com/apple/swift/pull/3816) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/aea8b836d21051076663c5692ec1d09bb3222527/proposals/0134-rename-string-properties.md) ## Introduction diff --git a/proposals/0136-memory-layout-of-values.md b/proposals/0136-memory-layout-of-values.md index 864fd48dc8..bc9b8463a8 100644 --- a/proposals/0136-memory-layout-of-values.md +++ b/proposals/0136-memory-layout-of-values.md @@ -5,7 +5,7 @@ * Review Manager: [Dave Abrahams](https://github.com/dabrahams) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160808/026164.html) -* Pull Request: [apple/swift#4041](https://github.com/apple/swift/pull/4041) +* Implementation: [apple/swift#4041](https://github.com/apple/swift/pull/4041) ## Introduction diff --git a/proposals/0147-move-unsafe-initialize-from.md b/proposals/0147-move-unsafe-initialize-from.md index e00c87539c..4dfd492ba7 100644 --- a/proposals/0147-move-unsafe-initialize-from.md +++ b/proposals/0147-move-unsafe-initialize-from.md @@ -5,7 +5,7 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 3.1)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170102/029945.html) -* Pull Request: [apple/swift#6601](https://github.com/apple/swift/pull/6601) +* Implementation: [apple/swift#6601](https://github.com/apple/swift/pull/6601) ## Introduction diff --git a/proposals/0160-objc-inference.md b/proposals/0160-objc-inference.md index f544bc03ad..26b0890bb9 100644 --- a/proposals/0160-objc-inference.md +++ b/proposals/0160-objc-inference.md @@ -6,7 +6,7 @@ * Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-April/000349.html) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/0389b1f49fc55b1a898701c549ce89738307b9fc/proposals/0160-objc-inference.md) -* Implementation: [Pull request](https://github.com/apple/swift/pull/8379) +* Implementation: [apple/swift#8379](https://github.com/apple/swift/pull/8379) * Bug: [SR-4481](https://bugs.swift.org/browse/SR-4481) ## Introduction diff --git a/proposals/0161-key-paths.md b/proposals/0161-key-paths.md index 7dd51fad3f..56a835fea7 100644 --- a/proposals/0161-key-paths.md +++ b/proposals/0161-key-paths.md @@ -5,7 +5,6 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Accepted** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-April/000356.html) -* Associated PR: [#644](https://github.com/apple/swift-evolution/pull/644) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/55e61f459632eca2face40e571a517919f846cfb/proposals/0161-key-paths.md) ## Introduction diff --git a/proposals/0166-swift-archival-serialization.md b/proposals/0166-swift-archival-serialization.md index 8f902096f1..80310a2964 100644 --- a/proposals/0166-swift-archival-serialization.md +++ b/proposals/0166-swift-archival-serialization.md @@ -5,8 +5,7 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-April/000367.html) -* Associated PRs: - * [#9004](https://github.com/apple/swift/pull/9004) +* Implementation: [apple/swift#9004](https://github.com/apple/swift/pull/9004) ## Introduction diff --git a/proposals/0167-swift-encoders.md b/proposals/0167-swift-encoders.md index 55ccb7c259..05110f1a4d 100644 --- a/proposals/0167-swift-encoders.md +++ b/proposals/0167-swift-encoders.md @@ -5,8 +5,7 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Accepted** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-April/000368.html) -* Associated PRs: - * [#9005](https://github.com/apple/swift/pull/9005) +* Implementation: [apple/swift#9005](https://github.com/apple/swift/pull/9005) ## Introduction diff --git a/proposals/0173-swap-indices.md b/proposals/0173-swap-indices.md index fdb4d1f522..7d454bb5b5 100644 --- a/proposals/0173-swap-indices.md +++ b/proposals/0173-swap-indices.md @@ -5,7 +5,7 @@ * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170424/036229.html) -* Associated PR: [#9119](https://github.com/apple/swift/pull/9119) +* Implementation: [apple/swift#9119](https://github.com/apple/swift/pull/9119) ## Introduction diff --git a/proposals/0178-character-unicode-view.md b/proposals/0178-character-unicode-view.md index 0d7d513e1a..2dc8a59361 100644 --- a/proposals/0178-character-unicode-view.md +++ b/proposals/0178-character-unicode-view.md @@ -5,7 +5,7 @@ * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170515/036714.html) -* Pull Request: [apple/swift#9675](https://github.com/apple/swift/pull/9675) +* Implementation: [apple/swift#9675](https://github.com/apple/swift/pull/9675) ## Introduction diff --git a/proposals/0179-swift-run-command.md b/proposals/0179-swift-run-command.md index eb844159e0..1f7e897108 100644 --- a/proposals/0179-swift-run-command.md +++ b/proposals/0179-swift-run-command.md @@ -5,7 +5,7 @@ * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) * Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170529/036909.html) -* Pull Request: [apple/swift-package-manager#1187](https://github.com/apple/swift-package-manager/pull/1187) +* Implementation: [apple/swift-package-manager#1187](https://github.com/apple/swift-package-manager/pull/1187) ## Introduction diff --git a/proposals/0180-string-index-overhaul.md b/proposals/0180-string-index-overhaul.md index af5a2a54d9..db80a39fbe 100644 --- a/proposals/0180-string-index-overhaul.md +++ b/proposals/0180-string-index-overhaul.md @@ -5,7 +5,7 @@ * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170703/037942.html) -* Pull Request: [apple/swift#9806](https://github.com/apple/swift/pull/9806) +* Implementation: [apple/swift#9806](https://github.com/apple/swift/pull/9806) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/72b8d90becd60b7cc7695607ae908ef251f1e966/proposals/0180-string-index-overhaul.md) ## Introduction diff --git a/proposals/0181-package-manager-cpp-language-version.md b/proposals/0181-package-manager-cpp-language-version.md index 40b7c3ed32..8f53eba3cd 100644 --- a/proposals/0181-package-manager-cpp-language-version.md +++ b/proposals/0181-package-manager-cpp-language-version.md @@ -5,7 +5,7 @@ * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) * Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170717/038142.html) -* Pull Request: [apple/swift-package-manager#1264](https://github.com/apple/swift-package-manager/pull/1264) +* Implementation: [apple/swift-package-manager#1264](https://github.com/apple/swift-package-manager/pull/1264) ## Introduction diff --git a/proposals/0184-unsafe-pointers-add-missing.md b/proposals/0184-unsafe-pointers-add-missing.md index b77dd1fb16..60443cfe79 100644 --- a/proposals/0184-unsafe-pointers-add-missing.md +++ b/proposals/0184-unsafe-pointers-add-missing.md @@ -4,7 +4,7 @@ * Author: [Kelvin Ma (“Taylor Swift”)](https://github.com/kelvin13) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active review (September 1...7, 2017)** -* Pull Request: [apple/swift#11464](https://github.com/apple/swift/pull/11464) +* Implementation: [apple/swift#11464](https://github.com/apple/swift/pull/11464) ## Introduction diff --git a/proposals/0185-synthesize-equatable-hashable.md b/proposals/0185-synthesize-equatable-hashable.md index c3202fc3a0..8acf69da18 100644 --- a/proposals/0185-synthesize-equatable-hashable.md +++ b/proposals/0185-synthesize-equatable-hashable.md @@ -4,7 +4,7 @@ * Author: [Tony Allevato](https://github.com/allevato) * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Accepted** -* Pull Request: [apple/swift#9619](https://github.com/apple/swift/pull/9619) +* Implementation: [apple/swift#9619](https://github.com/apple/swift/pull/9619) * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-August/000400.html) ## Introduction From d40df27745a95eda9b8b5e56179bf362320ac8a9 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 6 Sep 2017 21:53:31 +0100 Subject: [PATCH 0448/4563] [SE-0109] Update header, keeping CRLF line endings --- proposals/0109-remove-boolean.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0109-remove-boolean.md b/proposals/0109-remove-boolean.md index 88d2cd8ad8..a5112a62af 100644 --- a/proposals/0109-remove-boolean.md +++ b/proposals/0109-remove-boolean.md @@ -5,8 +5,8 @@ * Review Manager: [Doug Gregor](http://github.com/DougGregor) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160711/024270.html) -* Commits: [apple/swift@76cf339](https://github.com/apple/swift/commit/76cf339694a41293dbbec9672b6df87a864087f2), - [apple/swift@af30ae3](https://github.com/apple/swift/commit/af30ae32226813ec14c2bef80cb090d3e6c586fb) +* Implementation: [apple/swift@76cf339](https://github.com/apple/swift/commit/76cf339694a41293dbbec9672b6df87a864087f2), + [apple/swift@af30ae3](https://github.com/apple/swift/commit/af30ae32226813ec14c2bef80cb090d3e6c586fb) ## Introduction From fe8ec5395dcc84997e07bb9fb1b2ebc172e6dede Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 6 Sep 2017 22:02:00 +0100 Subject: [PATCH 0449/4563] Use **Awaiting implementation** in template status --- 0000-template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/0000-template.md b/0000-template.md index 442e549084..fd3e6feea1 100644 --- a/0000-template.md +++ b/0000-template.md @@ -3,11 +3,11 @@ * Proposal: [SE-NNNN](NNNN-filename.md) * Authors: [Author 1](https://github.com/swiftdev), [Author 2](https://github.com/swiftdev) * Review Manager: TBD -* Status: **Awaiting review** -* Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) +* Status: **Awaiting implementation** *During the review process, add the following fields as needed:* +* Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/), [Additional Commentary](https://lists.swift.org/pipermail/swift-evolution/) * Bugs: [SR-NNNN](https://bugs.swift.org/browse/SR-NNNN), [SR-MMMM](https://bugs.swift.org/browse/SR-MMMM) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md) From f4d73b1bb15431e752215b2fac5880a31576917c Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Wed, 6 Sep 2017 15:29:07 -0700 Subject: [PATCH 0450/4563] Note that implementations are now required for proposals. --- process.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process.md b/process.md index 6b9c426154..89a162e9aa 100644 --- a/process.md +++ b/process.md @@ -52,7 +52,7 @@ Swift, please consider how your proposal fits in with the larger goals of the upcoming Swift release. Proposals that are clearly out of scope for the upcoming Swift release will not be brought up for review. If you can't resist discussing a proposal that you know is out of scope, please include the tag `[Out of scope]` in the subject. * **Socialize the idea**: propose a rough sketch of the idea on the [swift-evolution mailing list][swift-evolution-mailing-list], the problems it solves, what the solution looks like, etc., to gauge interest from the community. -* **Develop the proposal**: expand the rough sketch into a complete proposal, using the [proposal template](0000-template.md), and continue to refine the proposal on the evolution mailing list. Prototyping an implementation and its uses along with the proposal is encouraged, because it helps ensure both technical feasibility of the proposal as well as validating that the proposal solves the problems it is meant to solve. +* **Develop the proposal**: expand the rough sketch into a complete proposal, using the [proposal template](0000-template.md), and continue to refine the proposal on the evolution mailing list. Prototyping an implementation and its uses along with the proposal is *required* because it helps ensure both technical feasibility of the proposal as well as validating that the proposal solves the problems it is meant to solve. * **Request a review**: initiate a pull request to the [swift-evolution repository][swift-evolution-repo] to indicate to the core team that you would like the proposal to be reviewed. When the proposal is sufficiently detailed and clear, and addresses feedback from earlier discussions of the idea, the pull request will be accepted. The proposal will be assigned a proposal number as well as a core team member to manage the review. * **Address feedback**: in general, and especially [during the review period][proposal-status], be responsive to questions and feedback about the proposal. From 1dc60860b79b0fa3ee87453035054b939429bc4d Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Wed, 20 Sep 2017 13:02:50 -0700 Subject: [PATCH 0451/4563] Schedule review for SE-0186. --- ...> 0186-remove-ownership-keyword-support-in-protocols.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-remove-ownership-keyword-support-in-protocols.md => 0186-remove-ownership-keyword-support-in-protocols.md} (93%) diff --git a/proposals/NNNN-remove-ownership-keyword-support-in-protocols.md b/proposals/0186-remove-ownership-keyword-support-in-protocols.md similarity index 93% rename from proposals/NNNN-remove-ownership-keyword-support-in-protocols.md rename to proposals/0186-remove-ownership-keyword-support-in-protocols.md index 8fb8de0bd7..cb6dff54ce 100644 --- a/proposals/NNNN-remove-ownership-keyword-support-in-protocols.md +++ b/proposals/0186-remove-ownership-keyword-support-in-protocols.md @@ -1,9 +1,9 @@ # Remove ownership keyword support in protocols -* Proposal: [SE-NNNN](NNNN-remove-ownership-keyword-support-in-protocols.md) +* Proposal: [SE-0186](0186-remove-ownership-keyword-support-in-protocols.md) * Authors: [Greg Spiers](https://github.com/gspiers) -* Review Manager: TBD -* Status: +* Review Manager: Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Status: **Active review (September 20...27 2017)** * Pull Request: [apple/swift#11744](https://github.com/apple/swift/pull/11744) * Bug: [SR-479](https://bugs.swift.org/browse/SR-479) From 4104ae04fc95fa932ec3e04d0d9bf4fe3973540e Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Wed, 20 Sep 2017 13:24:31 -0700 Subject: [PATCH 0452/4563] Fix typo. "Review Manager: " was repeated twice. --- proposals/0186-remove-ownership-keyword-support-in-protocols.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0186-remove-ownership-keyword-support-in-protocols.md b/proposals/0186-remove-ownership-keyword-support-in-protocols.md index cb6dff54ce..f2c995cc48 100644 --- a/proposals/0186-remove-ownership-keyword-support-in-protocols.md +++ b/proposals/0186-remove-ownership-keyword-support-in-protocols.md @@ -2,7 +2,7 @@ * Proposal: [SE-0186](0186-remove-ownership-keyword-support-in-protocols.md) * Authors: [Greg Spiers](https://github.com/gspiers) -* Review Manager: Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Active review (September 20...27 2017)** * Pull Request: [apple/swift#11744](https://github.com/apple/swift/pull/11744) * Bug: [SR-479](https://bugs.swift.org/browse/SR-479) From c0f9aa3d97699530c7a6a7c111317c454badda78 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 24 Sep 2017 18:11:24 +0100 Subject: [PATCH 0453/4563] [SE-0186] `Pull Request` => `Implementation` --- .../0186-remove-ownership-keyword-support-in-protocols.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0186-remove-ownership-keyword-support-in-protocols.md b/proposals/0186-remove-ownership-keyword-support-in-protocols.md index f2c995cc48..599800d720 100644 --- a/proposals/0186-remove-ownership-keyword-support-in-protocols.md +++ b/proposals/0186-remove-ownership-keyword-support-in-protocols.md @@ -1,10 +1,10 @@ # Remove ownership keyword support in protocols * Proposal: [SE-0186](0186-remove-ownership-keyword-support-in-protocols.md) -* Authors: [Greg Spiers](https://github.com/gspiers) +* Author: [Greg Spiers](https://github.com/gspiers) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Active review (September 20...27 2017)** -* Pull Request: [apple/swift#11744](https://github.com/apple/swift/pull/11744) +* Implementation: [apple/swift#11744](https://github.com/apple/swift/pull/11744) * Bug: [SR-479](https://bugs.swift.org/browse/SR-479) ## Introduction From f4eefac8e227d2f1d88e6134c215e61ee6d87981 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 26 Sep 2017 12:41:03 -0700 Subject: [PATCH 0454/4563] Remove Equatable version, switch to remove(where:) --- proposals/NNNN-RemoveWhere.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/proposals/NNNN-RemoveWhere.md b/proposals/NNNN-RemoveWhere.md index 872e84d2b4..c46113624b 100644 --- a/proposals/NNNN-RemoveWhere.md +++ b/proposals/NNNN-RemoveWhere.md @@ -90,12 +90,19 @@ Add the following to `RangeReplaceableCollection`: ```swift protocol RangeReplaceableCollection { /// Removes every element satisfying the given predicate from the collection. - mutating func removeAll(where: (Iterator.Element) throws -> Bool) rethrows + mutating func remove(where: (Iterator.Element) throws -> Bool) rethrows } -extension RangeReplaceableCollection where Iterator.Element: Equatable { - /// Removes every element equal to the given element from the collection. - mutating func removeAll(equalTo element: Iterator.Element) +extension RangeReplaceableCollection { + mutating func remove(where: (Iterator.Element) throws -> Bool) rethrows { + // default implementation in terms of self = self.filter + } +} + +extension RangeReplaceableCollection where Self: MutableCollection { + mutating func remove(where: (Iterator.Element) throws -> Bool) rethrows { + // more efficient implementation + } } ``` @@ -113,12 +120,7 @@ This change is purely additive so has no API resilience consequences. ## Alternatives considered -Regarding the name: `remove` instead of `removeAll` was considered. -`removeAll(equalTo: 5)` seems clearer when seen alongside similar methods -`removeFirst(5)` and `remove(at: 5)`. In the case of `remove(where:)`, the -`All` in the basename is preserved for trailing closures. - -`removeAll(where:)` takes a closure with `true` for elements to remove. +`remove(where:)` takes a closure with `true` for elements to remove. `filter` takes a closure with elements to keep. In both cases, `true` is the "active" case, so likely to be what the user wants without having to apply a negation. The naming of `filter` is unfortunately ambiguous as to whether it's From 0f4a24d6ded2ab7cb39c1a68e0f92705a7615d73 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 26 Sep 2017 12:45:05 -0700 Subject: [PATCH 0455/4563] eliminate a couple of missed All's --- proposals/NNNN-RemoveWhere.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proposals/NNNN-RemoveWhere.md b/proposals/NNNN-RemoveWhere.md index c46113624b..fbe42f47bc 100644 --- a/proposals/NNNN-RemoveWhere.md +++ b/proposals/NNNN-RemoveWhere.md @@ -1,4 +1,4 @@ -# Adding in-place `removeAll` to the Standard Library +# Adding in-place `remove(where:)` to the Standard Library * Proposal: [SE-NNNN](NNNN-filename.md) * Authors: [Ben Cohen](https://github.com/airspeedswift) @@ -61,11 +61,10 @@ important example of this, because its elements (graphemes) are variable width. ## Proposed solution -Add the following methods to `RangeReplaceableCollection`: +Add the following method to `RangeReplaceableCollection`: ```swift -nums.removeAll(equalTo: 9) -nums.removeAll(where: isOdd) +nums.remove(where: isOdd) ``` The default implementation will use the protocol's `init()` and `append(_:)` From 2573ec2e522f63834b50aa015fc13a9b64ea8998 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Tue, 26 Sep 2017 13:57:18 -0700 Subject: [PATCH 0456/4563] [Status] Add implementation links. (#747) Add implementation (pull request or commit) links to proposals' detail lists. --- index.css | 5 +++-- index.js | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/index.css b/index.css index f10ae2ea6d..59fb400ab7 100644 --- a/index.css +++ b/index.css @@ -242,6 +242,7 @@ section ul, section li { font-weight: 200; display: inline-block; padding-right: 6px; + white-space: nowrap; } .proposal-detail-value { @@ -270,11 +271,11 @@ section ul, section li { width: 223px; } -.bug-list { +.bug-list, .implementation-list { width: 310px; } -.bug-list a { +.bug-list a, .implementation-list a { word-wrap: break-word; } diff --git a/index.js b/index.js index 8445ed0146..cc4a4f5900 100644 --- a/index.js +++ b/index.js @@ -10,6 +10,8 @@ // // ===---------------------------------------------------------------------===// +'use strict' + /** Holds the primary data used on this page: metadata about Swift Evolution proposals. */ var proposals @@ -22,7 +24,8 @@ var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4'] /** Storage for the user's current selection of filters when filtering is toggled off. */ var filterSelection = [] -var REPO_PROPOSALS_BASE_URL = 'https://github.com/apple/swift-evolution/blob/master/proposals' +var GITHUB_BASE_URL = 'https://github.com/' +var REPO_PROPOSALS_BASE_URL = GITHUB_BASE_URL + 'apple/swift-evolution/blob/master/proposals' /** * `name`: Mapping of the states in the proposals JSON to human-readable names. @@ -293,7 +296,7 @@ function renderBody () { if (proposal.reviewManager.name) detailNodes.push(renderReviewManager(proposal.reviewManager)) if (proposal.trackingBugs) detailNodes.push(renderTrackingBugs(proposal.trackingBugs)) if (state === '.implemented') detailNodes.push(renderVersion(proposal.status.version)) - + if (proposal.implementation) detailNodes.push(renderImplementation(proposal.implementation)) if (state === '.acceptedWithRevisions') detailNodes.push(renderStatus(proposal.status)) if (state === '.activeReview' || state === '.scheduledForReview') { @@ -375,6 +378,30 @@ function renderTrackingBugs (bugs) { ]) } +/** Implementations are required alongside proposals (after Swift 4.0). */ +function renderImplementation (implementations) { + var implNodes = implementations.map(function (impl) { + return html('a', { + href: GITHUB_BASE_URL + impl.account + '/' + impl.repository + '/' + impl.type + '/' + impl.id + }, [ + impl.repository, + impl.type === 'pull' ? '#' : '@', + impl.id.substr(0, 7) + ]) + }) + + implNodes = _joinNodes(implNodes, ', ') + + var label = 'Implementation: ' + + return html('div', { className: 'proposal-detail' }, [ + html('div', { className: 'proposal-detail-label' }, [label]), + html('div', { className: 'implementation-list proposal-detail-value' }, + implNodes + ) + ]) +} + /** For `.implemented` proposals, display the version of Swift in which they first appeared. */ function renderVersion (version) { return html('div', { className: 'proposal-detail' }, [ @@ -638,6 +665,9 @@ function _searchProposals (filterText) { ['status', 'version'], ['authors', 'name'], ['authors', 'link'], + ['implementation', 'account'], + ['implementation', 'repository'], + ['implementation', 'id'], ['trackingBugs', 'link'], ['trackingBugs', 'status'], ['trackingBugs', 'id'], @@ -818,7 +848,7 @@ function _applyFragment (fragment) { })[0] if (!stateName) return // fragment contains a nonexistent state - state = states[stateName] + var state = states[stateName] if (stateName === '.implemented') implementedSelected = true From f1d36a9618b2df1487290b71d5f92144f28fb5db Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 26 Sep 2017 16:02:35 -0700 Subject: [PATCH 0457/4563] Swift 4-ify --- proposals/NNNN-RemoveWhere.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/proposals/NNNN-RemoveWhere.md b/proposals/NNNN-RemoveWhere.md index fbe42f47bc..59307f7675 100644 --- a/proposals/NNNN-RemoveWhere.md +++ b/proposals/NNNN-RemoveWhere.md @@ -8,9 +8,9 @@ ## Introduction It is common to want to remove all occurrences of a certain element from a -collection. This proposal is to add two in-place `remove` algorithms to the +collection. This proposal is to add a `remove` algorithm to the standard library, which will remove all entries in a collection in-place -matching either an `Equatable` value, or match that a certain criteria. +matching a given predicate. ## Motivation @@ -47,7 +47,7 @@ if var i = nums.index(where: isOdd) { } j += 1 } - nums.removeSubrange(i.. Date: Sat, 30 Sep 2017 16:16:37 -0700 Subject: [PATCH 0458/4563] Accept SE-0186. --- .../0186-remove-ownership-keyword-support-in-protocols.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/0186-remove-ownership-keyword-support-in-protocols.md b/proposals/0186-remove-ownership-keyword-support-in-protocols.md index 599800d720..f52189b3e2 100644 --- a/proposals/0186-remove-ownership-keyword-support-in-protocols.md +++ b/proposals/0186-remove-ownership-keyword-support-in-protocols.md @@ -3,9 +3,11 @@ * Proposal: [SE-0186](0186-remove-ownership-keyword-support-in-protocols.md) * Author: [Greg Spiers](https://github.com/gspiers) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active review (September 20...27 2017)** -* Implementation: [apple/swift#11744](https://github.com/apple/swift/pull/11744) +* Status: **Accepted** +* Implementation: [apple/swift#11744](https://github.com/apple/swift/pull/11744 +* [Review thread on swift-evolution](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170918/039863.html) * Bug: [SR-479](https://bugs.swift.org/browse/SR-479) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170925/040012.html) ## Introduction From a445bc30821189e0b2dda5c12ffeb02dc8869bae Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Sat, 30 Sep 2017 16:17:30 -0700 Subject: [PATCH 0459/4563] Fix broken markdown link --- proposals/0186-remove-ownership-keyword-support-in-protocols.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0186-remove-ownership-keyword-support-in-protocols.md b/proposals/0186-remove-ownership-keyword-support-in-protocols.md index f52189b3e2..ff0ba493eb 100644 --- a/proposals/0186-remove-ownership-keyword-support-in-protocols.md +++ b/proposals/0186-remove-ownership-keyword-support-in-protocols.md @@ -4,7 +4,7 @@ * Author: [Greg Spiers](https://github.com/gspiers) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Accepted** -* Implementation: [apple/swift#11744](https://github.com/apple/swift/pull/11744 +* Implementation: [apple/swift#11744](https://github.com/apple/swift/pull/11744) * [Review thread on swift-evolution](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170918/039863.html) * Bug: [SR-479](https://bugs.swift.org/browse/SR-479) * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170925/040012.html) From 93f74e7e92ba64303de56d6fe9178598d97b8bd8 Mon Sep 17 00:00:00 2001 From: Akshay Shrimali Date: Sun, 1 Oct 2017 23:37:21 +0530 Subject: [PATCH 0460/4563] Update index.css --- index.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.css b/index.css index 59fb400ab7..1a1b1a1998 100644 --- a/index.css +++ b/index.css @@ -563,7 +563,7 @@ section ul, section li { @media (max-width: 768px) { h1 { - font-size: 54px + font-size: 54px; } } @@ -613,4 +613,4 @@ section ul, section li { .color-withdrawn { color: rgb(255, 59, 48); border-color: rgb(255, 59, 48); -} \ No newline at end of file +} From 997cce8b455057d2120bae1bb9ba9e1fc5305fc7 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Thu, 5 Oct 2017 15:50:35 -0500 Subject: [PATCH 0461/4563] SE-184a (Unsafe[Mutable][Raw][Buffer]Pointer overhaul, part 1) (#750) * amended se-184 * unsafe pointers part 1 * rename to 0184a scheme * add deallocate() to list * implementation branch * merge se 0184a into 0184 * merged into se 1084 primary document --- proposals/0184-unsafe-pointers-add-missing.md | 433 +++++------------- 1 file changed, 119 insertions(+), 314 deletions(-) diff --git a/proposals/0184-unsafe-pointers-add-missing.md b/proposals/0184-unsafe-pointers-add-missing.md index 60443cfe79..9b4e9fb0c7 100644 --- a/proposals/0184-unsafe-pointers-add-missing.md +++ b/proposals/0184-unsafe-pointers-add-missing.md @@ -1,76 +1,37 @@ # Unsafe[Mutable][Raw][Buffer]Pointer: add missing methods, adjust existing labels for clarity, and remove deallocation size -* Proposal: [SE-0184](0184-unsafe-pointers-add-missing.md) +* Proposal: [SE-0184](0184a-unsafe-pointers-part-1.md) * Author: [Kelvin Ma (“Taylor Swift”)](https://github.com/kelvin13) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (September 1...7, 2017)** -* Implementation: [apple/swift#11464](https://github.com/apple/swift/pull/11464) +* Status: +* Implementation: [PR 12200](https://github.com/apple/swift/pull/12200) ## Introduction -Swift’s pointer types are an important interface for low-level memory manipulation, but the current API design is not very consistent, complete, or convenient. Many memory methods demand a `capacity:` or `count:` argument, forcing the user to manually track the size of the memory block, even though most of the time this is either unnecessary, or redundant as buffer pointers track this information natively. In some places, poor naming choices and overengineered function signatures compromise memory safety by leading users to believe that they have allocated or freed memory when in fact, they have not. +*This document is a spin-off from a much larger [original proposal](https://github.com/kelvin13/swift-evolution/blob/e888af466c9993de977f6999a131eadd33291b06/proposals/0184-unsafe-pointers-add-missing.md), which covers only those aspects of SE-1084 which do not deal with partial buffer memory state. Designing the partial buffer memory state API clearly requires more work, and has been left out of the scope of this document.* -This proposal seeks to improve the Swift pointer API by ironing out naming inconsistencies, adding sensible default argument values, adding missing methods, and reducing excessive verbosity, offering a more convenient, more sensible, and less bug-prone API. We also attempt to introduce a buffer pointer API that supports partial initialization without excessively compromising memory state safety. +Swift’s pointer types are an important interface for low-level memory manipulation, but the current API design is not very consistent, complete, or convenient. In some places, poor naming choices and overengineered function signatures compromise memory safety by leading users to believe that they have allocated or freed memory when in fact, they have not. This proposal seeks to improve the Swift pointer API by ironing out naming inconsistencies, adding missing methods, and reducing excessive verbosity, offering a more convenient, more sensible, and less bug-prone API. -Swift-evolution thread: [Pitch: Improved Swift pointers](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170710/038013.html), [Pitch: More Improved Swift pointers](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170717/038121.html) +Swift-evolution threads: [Pitch: Improved Swift pointers](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170710/038013.html), [Pitch: More Improved Swift pointers](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170717/038121.html) -Implementation branch: [**PR 11464**](https://github.com/apple/swift/pull/11464) +Implementation branch: [`kelvin13:se-0184a`](https://github.com/kelvin13/swift/tree/se-0184a) ## Background -There are four binary memorystate operations: *initialization*, *move-initialization*, *assignment*, and *move-assignment*. They can be grouped according to how they affect the source buffer and the destination buffer. **Copy** operations only read from the source buffer, leaving it unchanged. **Move** operations deinitialize the source memory, decrementing the reference count by 1 if the memory type is not a trivial type. **Retaining** operations initialize the destination memory, incrementing the reference count by 1 if applicable. **Releasing** operations deinitialize the destination memory before reinitializing it with the new values, resulting in a net change in the reference count of 0, if applicable. +There are four binary memorystate operations: *initialization*, *move-initialization*, *assignment*, and *move-assignment*, and two unary memorystate operations: *deinitialization* and *type rebinding*. The binary operations can be grouped according to how they affect the source buffer and the destination buffer. **Copy** operations only read from the source buffer, leaving it unchanged. **Move** operations deinitialize the source memory, decrementing the reference count by 1 if the memory type is not a trivial type. **Retaining** operations initialize the destination memory, incrementing the reference count by 1 if applicable. **Releasing** operations deinitialize the destination memory before reinitializing it with the new values, resulting in a net change in the reference count of 0, if applicable. | | Copy (+0) | Move (−1) | | -------------: |----------: | ---------: | | **Retaining (+1)** | initialize | move-initialize | | **Releasing (+0)** | assign | move-assign | -Note: deinitialization by itself is a unary operation; it decrements the reference count of the buffer by 1. +Raw pointers also have a unique operation, *bytewise-copying*, which we will lump together with the memorystate functions, but does not actually change a pointer’s memory state. -The four main types of Swift pointers we have currently support different subsets of this toolbox. - -### UnsafeMutablePointer - -| | Copy | Move | -| ------------- |----------: | ---------: | -| **Retaining** | `initialize(to:count:)`, `initialize(from:count:)` | `moveInitialize(from:count:)` | -| **Releasing** | `assign(from:count:)` | `moveAssign(from:count:)` | - -### UnsafeMutableRawPointer - -| | Copy | Move | -| ------------- |----------: | ---------: | -| **Retaining** | `initializeMemory(as:at:count:to:)`, `initializeMemory(as:from:count:)` | `moveInitializeMemory(as:from:count:)` | -| **Releasing** | | | - -### UnsafeMutableBufferPointer - -| | Copy | Move | -| ------------- |----------: | ---------: | -| **Retaining** | `initialize(from:)` | | -| **Releasing** | | | - - -### UnsafeMutableRawBufferPointer - -| | Copy | Move | -| ------------- |----------: | ---------: | -| **Retaining** | `initializeMemory(as:from:)` | | -| **Releasing** | | | - -There are unary memorystate operations such as *deinitialization* and *type rebinding*, which are not listed in the tables, but are still covered by this proposal. Raw pointers also have a unique operation, *bytewise-copying*, which we will lump together with the memorystate functions, but does not actually change a pointer’s memory state. +Most of these operations become more relevant in the discussion of partial buffer memory state, which is not in the scope of this document. This document only proposes changes related to memory allocation, type-rebinding, and two special *unary* forms of initialization and assignment which initialize memory to a fixed, repeating value. ## Motivation -Right now, `UnsafeMutableBufferPointer` is kind of a black box when it comes to producing and modifying instances of it. Much of the API present on `UnsafeMutablePointer` is absent on its buffer variant. To create, bind, allocate, initialize, deinitialize, and deallocate them, you have to extract `baseAddress`es and `count`s. This is unfortunate because `UnsafeMutableBufferPointer` provides a handy container for tracking the size of a memory buffer, but to actually make use of this information, the buffer pointer must be disassembled. In practice, this means the use of memory buffers requires frequent (and annoying) conversion back and forth between buffer pointers and base address–count pairs. For example, to move-initialize memory between two buffer pointers, you have to write this: - -```swift -buffer1.baseAddress?.moveInitialize(from: buffer2.baseAddress!, count: buffer1.count) -``` - -The `?` is sometimes exchanged with an `!` depending on the personality of the author, as normally, neither operator is meaningful here — the `baseAddress` is never `nil` if the buffer pointer was created around an instance of `UnsafeMutablePointer`. - -Memory buffer allocation is especially painful, since it requires the creation of a temporary `UnsafeMutablePointer` instance. This means that the following “idiom” is very common in Swift code: +Right now, `UnsafeMutableBufferPointer` is kind of a black box when it comes to producing and modifying instances of it. Much of the API present on `UnsafeMutablePointer` is absent on its buffer variant. To create, bind, allocate, initialize, and deallocate them, you have to extract `baseAddress`es and `count`s. This is unfortunate because `UnsafeMutableBufferPointer` provides a handy container for tracking the size of a memory buffer, but to actually make use of this information, the buffer pointer must be disassembled. In practice, this means the use of memory buffers requires frequent (and annoying) conversion back and forth between buffer pointers and base address–count pairs. For example, buffer allocation requires the creation of a temporary `UnsafeMutablePointer` instance. This means that the following “idiom” is very common in Swift code: ```swift let buffer = UnsafeMutableBufferPointer(start: UnsafeMutablePointer.allocate(capacity: byteCount), count: byteCount) @@ -94,6 +55,8 @@ defer } ``` +The `?` is sometimes exchanged with an `!` depending on the personality of the author, as normally, neither operator is meaningful here — the `baseAddress` is never `nil` if the buffer pointer was created around an instance of `UnsafeMutablePointer`. + This method is extremely problematic because nearly all users, on first seeing the signature of `deallocate(capacity:)`, will naturally conclude from the `capacity` label that `deallocate(capacity:)` is equivalent to some kind of `realloc()` that can only shrink the buffer. However this is not the actual behavior — `deallocate(capacity:)` actually *ignores* the `capacity` argument and just calls `free()` on `self`. The current API is not only awkward and suboptimal, it is *misleading*. You can write perfectly legal Swift code that shouldn’t segfault, but still can, for example ```swift @@ -116,18 +79,16 @@ defer which is functionally equivalent. However this will lead to disastrous source breakage if the implementation of `deallocate(capacity:)` is ever “corrected”. Since the API would not change, such code would still compile, but suddenly start failing at runtime. Thus, the current API, combined with incorrect documentation, is serving as a vector for introducing memory bugs into Swift code. -The Swift pointer API is incomplete in other ways too. For example, the `initialize(from:count:)` method on `UnsafeMutablePointer` has a repeated-value copy variant, `initialize(to:count:)`, but `assign(from:count:)` has no such variant, even though it would make just as much sense for it to have one. - Finally, some of the naming choices in the current API deserve a second look. While the original API intended to introduce a naming convention where `bytes` refers to uninitialized memory, `capacity` to uninitialized elements, and `count` to initialized elements, the actual usage of the three words does not always agree. In `copyBytes(from:count:)`, `count` refers to the number of *bytes*, which may or may not be initialized. Similarly, the `UnsafeMutableRawBufferPointer` `allocate(count:)` type method includes a `count` argument which actually refers to uninitialized bytes. The argument label `to:` is also excessively overloaded; sometimes it refers to a type `T.Type`, and sometimes it refers to a repeated value parameter. This becomes problematic when both parameters appear in the same method, as in `initializeMemory(as:at:count:to)`. ## Proposed solution -The goal of the API redesign is to bring all of the functionality in `UnsafeMutablePointer` and `UnsafeMutableRawPointer` to their buffer types, `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer`. `UnsafeMutableRawBufferPointer` already contains some of this functionality, providing a useful blueprint for the proposed `UnsafeMutableBufferPointer` API. +The ultimate goal of the API redesign is to bring all of the functionality in `UnsafeMutablePointer` and `UnsafeMutableRawPointer` to their buffer types, `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer`. Operations which are covered by this proposal are in **bold**. The full toolbox of methods that we could possibly support includes: - - allocation - - deallocation + - **allocation** + - **deallocation** - initialization - move-initialization @@ -136,64 +97,17 @@ The full toolbox of methods that we could possibly support includes: - move-assignment - deinitialization - - type rebinding + - **type rebinding** - bytewise copying Because copy operations (initialization and assignment) don’t mutate the source argument, they can also come in a form which takes a repeated-value source instead of a buffer source. - - initialization (repeated-value) - - assignment (repeated-value) + - **initialization (repeated-value)** + - **assignment (repeated-value)** `UnsafeMutablePointer` and `UnsafeMutableRawPointer` already contain repeated-value methods for initialization in the form of `initialize(to:count:)` and `initializeMemory(as:at:count:to:)`. This proposal will add the assignment analogues. For reasons explained later, the argument label for the repeated-value parameter will be referred to as `repeating:`, not `to:`. -In their most general form, these functions are written like this: - -``` -static -func allocate(as:count:) -> PointerType -func deallocate() - -func initialize(as:at:repeating:count:) -func initialize(as:at:from:count:) -func moveInitialize(as:at:from:count:) - -func assign(as:at:repeating:count:) -func assign(as:at:from:count:) -func moveAssign(as:at:from:count:) - -func deinitialize(as:at:count:) -func rebindMemory(as:count:) - -func copyBytes(at:from:count:) -``` - -where - - - **`as:`** refers to the element type - - **`at:`** refers to an offset from `self`, in strides of the element type - - **`repeating:`** refers to a repeating value - - **`from:`** refers to a second pointer which serves as the **source** - - **`count:`** refers the number of elements the operation operates on - -On actual pointer types, most of these parameters are unnecessary, and some of the methods themselves either don’t make sense to support, or are not practically useful. - - - it only makes sense for immutable pointer types to support deallocation and type rebinding. Note that Swift’s memory model does not require memory to be mutable for deallocation. - - - raw (untyped) pointers should not support any operations which involve deinitialization on `self`. This rules out deinitialization itself, as well as any releasing operations (assignment, move-assignment). - - - typed pointers don’t need an `as:` parameter (except for type rebinding) — they already have a type. It also doesn’t make sense for them to support byte-wise copying. - - - pointers for which it is syntactically easy to offset in strides (for example, by pointer arithmetic with `+`), don’t need to take an `at:` argument. - -> note: some of these conceptual argument labels have different names in the real API. `as:` is written as `to:` in the type-rebinding methods because it sounds better. `count:` is sometimes written as `capacity:` or `bytes:` to express the assumptions about the stride and initialization state of the memory in question. - -> * `bytes` refers to, well, a byte quantity that is *not assumed* to be initialized. -> * `capacity` refers to a strided quantity that is *not assumed* to be initialized. -> * `count` refers to a strided quantity that is *assumed* to be initialized. - -> note: we don’t bother supporting an `at:` offset in type rebinding operations since we don’t anticipate much use for such a feature. - ### `UnsafePointer` ``` @@ -201,7 +115,9 @@ func deallocate() func withMemoryRebound(to:capacity:_:) -> Result ``` -`UnsafePointer` does not get an allocator static method, since you almost always want a mutable pointer to newly allocated memory. Its type rebinding method is also written as a decorator, taking a trailing closure, for memory safety. `UnsafePointer` does not take `at:` arguments since `+` provides pointer arithmetic for it. +`UnsafePointer` does not get an allocator static method, since you almost always want a mutable pointer to newly allocated memory. Its type rebinding method is also written as a decorator, taking a trailing closure, for memory safety. + +Most immutable pointer types currently do not have a deallocation method. This proposal adds them, fixing [SR-3309](https://bugs.swift.org/browse/SR-3309). Note, immutable raw buffer pointers already support this API. ### `UnsafeMutablePointer` @@ -211,22 +127,27 @@ func allocate(capacity:) -> UnsafeMutablePointer func deallocate() func initialize(repeating:count:) -func initialize(from:count:) -func moveInitialize(from:count:) +func initialize(to:) func assign(repeating:count:) -func assign(from:count:) -func moveAssign(from:count:) -func deinitialize(count:) func withMemoryRebound(to:capacity:_:) -> Result ``` -Like `UnsafePointer`, `UnsafeMutablePointer`’s type rebinding method is written as a decorator, and its methods do not need `at:` arguments. +Like `UnsafePointer`, `UnsafeMutablePointer`’s type rebinding method is written as a decorator. -> note: the `count:` argument in `initialize(repeating:count:)`, `assign(repeating:count:)`, and `deinitialize(count:)` and the `capacity:` argument in `allocate(capacity:)` should receive a default value of `1`, since those methods are commonly used with a count of `1`. +Previously, the single-element repeated-initialization case was supported by a default argument of `1` on `initialize(repeating:count:)`’s `count:` parameter, but it was decided this was too confusing in terms of API readability. For example, calls to `initialize(repeating:count:)` and its corresponding method on `UnsafeMutableBufferPointer` were prone to look the same. -> `withMemoryRebound(to:capacity:_:)` should *not* receive a default `count:` value, to avoid misleading users about type binding in Swift’s memory model. (The entire memory block must be rebound, not just the first element under the pointer.) +```swift +plainPointer.initialize(repeating: pointee) +bufferPointer.initialize(repeating: repeatedValue) +``` + +Increasing API surface by adding this method is justified by the large number of calls to `initialize(to:count:)` in the standard library (and likely other code) which rely on the default argument of `1`. We do *not* need to add a corresponding `assignPointee(to:)` method since this can be done with the assignment operator. + +```swift +ptr.pointee = newValue +``` ### `UnsafeRawPointer` @@ -240,66 +161,17 @@ func bindMemory(to:capacity:) -> UnsafePointer ``` static -func allocate(bytes:alignedTo:) -> UnsafeMutableRawPointer +func allocate(byteCount:alignment:) -> UnsafeMutableRawPointer func deallocate() -func initializeMemory(as:at:repeating:count:) -> UnsafeMutablePointer -func initializeMemory(as:at:from:count:) -> UnsafeMutablePointer -func moveInitializeMemory(as:at:from:count:) -> UnsafeMutablePointer +func initializeMemory(as:repeating:count:) -> UnsafeMutablePointer func bindMemory(to:capacity:) -> UnsafeMutablePointer - -func copy(from:bytes:) -``` - -The `as:` argument in `allocate(bytes:alignedTo:)` is represented by an alignment parameter which takes an integer. This is more useful since we often need a computed alignment (like when aligning a structure) instead of a preset type alignment. - -> note: although raw pointers support pointer arithmetic, it is not done in strides, and `ptr + offset * MemoryLayout.stride` is painfully verbose, so these methods take an `at:` offset. - -> note: the `at:` arguments in `UnsafeMutableRawPointer` should all have a default value of `0`; i.e., no offset. - -------------- - -Buffer pointers are conceptually similar, except the `count:` argument is often unnecessary since they track their own length internally. This means you would call - -```swift -ptr1.initialize(repeating: value, count: count) ``` -on an `UnsafeMutablePointer`, but - -```swift -buffer1.initialize(repeating: value) -``` - -on an `UnsafeMutableBufferPointer`. - -Implementing unary operations like repeated-value initialization, repeated-value assignment, and type rebinding is straightforward. However, with binary operations like move-initialization, which involves both a source buffer and a destination buffer, the question of whose `count` to use becomes important. - -One option is to use the destination’s `count`, and set the precondition that source`.count` `>=` destination`.count`. The benefit to this is that the destination is always guaranteed to be fully initialized, so that a sizeless `deinitialize()` can be safely called on it. However, in the case of move-initialization and move-assignment, it can leave the source buffer partially *deinitialized* which is just as big a problem. It is also not very useful in practice, since real collections tend to grow monotonically, periodically moving their contents into larger and larger buffers. - -A better option is to use the source’s `count`, combined with an `at:` offset, and set the precondition that `offset` `+` source`.count` `<=` destination`.count`. This *`at:from:`* system inspired by existing `UnsafeMutableRawPointer` APIs is an extremely useful system for supporting partially initialized buffer pointers by allowing us to initialize, assign, and move buffers in segments. For example, it would now be easy to concatenate multiple buffers into one. - -```swift -let pixels:Int = scanlines.map{ $0.count }.reduce(0, +) -var image = UnsafeMutableBufferPointer.allocate(capacity: pixels) - -var filled:Int = 0 -for scanline:UnsafeMutableBufferPointer in scanlines -{ - image.moveInitialize(at: filled, from: scanline) - filled += scanline.count -} - -image.deinitialize(at: 0, count: filled) -image.deallocate() -``` +Currently, `UnsafeMutableRawPointer`’s methods take an `at:` offset argument that is interpreted in strides. This argument is not currently in use in the entire Swift standard library, and we believe that it is not useful in practice. -Under this system, it will be impossible to leave part of a source buffer deinitialized, and every segment of a destination buffer will be accessible (instead of only segments starting at index `0`.) - -> note: we use `at:` instead of `+` because pointer arithmetic does not play well with the nillable buffer pointer `baseAddress`. - -> note: while deinitialization can be performed on a buffer pointer using its own `count` property, we have decided it’s better to explicitly ask for the `at:count:` pair to be consistent with real use patterns and the rest of the proposed API which operates on segments of `self`. +Unlike `UnsafeMutablePointer`, we do not add a single-instance initialize method to `UnsafeMutableRawPointer`, as such a method would probably not be useful. However, we still remove the default argument of `1` from the `count:` argument in `initializeMemory(as:repeating:count:)` to prevent confusion with calls to its buffer variant. ### `UnsafeBufferPointer` @@ -310,6 +182,8 @@ func withMemoryRebound(to:_:) -> Result The buffer type rebind method dynamically computes the new count by performing multiplication and integer division, since the target type may have a different stride than the original type. This is in line with existing precedent in the generic buffer method `initializeMemory(as:from:)` on `UnsafeMutableRawBufferPointer`. +> Note: **calling `deallocate()` on a buffer pointer is only defined behavior if the buffer pointer references a complete heap memory block**. This operation may become supported in a wider variety of cases in the future if Swift gets a more sophisticated heap allocation backend. + ### `UnsafeMutableBufferPointer` ``` @@ -318,21 +192,14 @@ func allocate(capacity:) -> UnsafeMutableBufferPointer func deallocate() func initialize(repeating:) -func initialize(at:from:) -func moveInitialize(at:from:) func assign(repeating:) -func assign(at:from:) -func moveAssign(at:from:) -func deinitialize(at:count) func withMemoryRebound(to:_:) -> Result ``` The buffer type rebind method works the same way as in `UnsafeBufferPointer`. (Type rebinding never cares about mutability.) -> note: the `at:` arguments in `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer` should *not* receive default values, as they are an integral part of the buffer pointer memory state safety system, and so it is important they appear at the call site. - ### `UnsafeRawBufferPointer` ``` @@ -345,59 +212,29 @@ func bindMemory(to:) -> UnsafeBufferPointer ``` static -func allocate(bytes:alignedTo:) -> UnsafeMutableRawBufferPointer +func allocate(byteCount:alignment:) -> UnsafeMutableRawBufferPointer func deallocate() func initializeMemory(as:repeating:) -> UnsafeMutableBufferPointer -func initializeMemory(as:at:from:) -> UnsafeMutableBufferPointer -func moveInitializeMemory(as:at:from:) -> UnsafeMutableBufferPointer func bindMemory(to:) -> UnsafeMutableBufferPointer - -func copyBytes(at:from:) ``` > note: `initializeMemory(as:repeating:)` performs integer division on `self.count` (just like `bindMemory(to:)`) -> note: the return values of `initializeMemory(as:repeating:)`, `initializeMemory(as:at:from:)`, and `moveInitializeMemory(as:at:from:)` should all be marked as `@discardableResult`. - -> note: `copyBytes(from:)` takes an `at:` argument because just like `UnsafeBufferPointer`, offsetting its `baseAddress` is not syntactically easy. The function name also has “`Bytes`” in it since it’s missing the `bytes:` argument label, and we need to emphasize that it only performs a bytewise copy. - -## Detailed changes - -The proposed new API attempts to build on the existing API wherever possible. With the exception of `deallocate()` (which has good justification to replace `deallocate(capacity:)` and `deallocate(bytes:alignedTo:)`), all changes are either pure additive changes, or renames which are trivial to automigrate. This reduces the amount of source breakage. - -- **fix the ordering of the arguments in `initializeMemory(as:at:count:to:)` and rename the argument `to:` to `repeating:` in all repeated-value copy functions** +> note: the return value of `initializeMemory(as:repeating:)` should be marked as `@discardableResult`. -The ordering of the `to:` and `count:` argument labels in the `initializeMemory(as:at:count:to:)` method on `UnsafeMutableRawPointer` contradicts the rest of the Swift pointer API, where `to:` precedes `count:`. +We also make several miscellaneous changes to the API in order to tidy things up. -Because the ordering `initializeMemory(as:at:to:count:)` conflicts with the use of `to:` as the argument label for a target type, this argument should be renamed to `repeating:`. The word `repeating:` is much more clear in terms of describing the methods’ behavior, and is consistent with the use of the word in the `Array` API. +- **rename `copyBytes(from:count:)` and `copyBytes(from:)` to `copyMemory(from:byteCount:)` and `copyMemory(from:)`** -- **add the repeated-value copy assignment method `assign(repeating:count:)`** - -This addresses the missing assignment analogue to the `initialize(to:count:)` method. - -- **rename `copyBytes(from:count:)` to `copy(from:bytes:)` on `UnsafeMutableRawPointer`** - -We will enforce the convention of the use of the words `bytes`, `count`, and `capacity`. - -Since this makes the word “bytes” occur twice in `copyBytes(from:bytes:)`, we should drop the “Bytes” suffix and further rename the method to `copy(from:bytes:)`. Since `UnsafeMutableRawPointer` is inherently untyped, it is obvious that any memory transfer operation on it is a bytewise operation so the “Bytes” suffix adds only verbosity and no clarity. An unsized version of this method will also be added to `UnsafeMutableRawBufferPointer`, but keeping the “`Bytes`” suffix. - -We do not rename the `count` property on `UnsafeMutableRawBufferPointer` to `bytes` since this could be confused with the actual buffer data. - -- **rename `count` in `UnsafeMutableRawBufferPointer.allocate(count:)` to `bytes` and add an `alignedTo` parameter to make it `UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)`** - -This brings it in line with the `UnsafeMutableRawPointer` allocator, and avoids the contradictory and inconsistent use of `count` to represent a byte quantity. Currently `UnsafeMutableRawBufferPointer.allocate(count:)` aligns to the size of `UInt`, an assumption not shared by its plain variant. +This brings the method names in line with the rest of the raw pointer API. - **add an `init(mutating:)` initializer to `UnsafeMutableBufferPointer`** This makes it much easier to make a mutable copy of an immutable buffer pointer. Such an initializer already exists on `UnsafeMutableRawBufferPointer`, so adding one to `UnsafeMutableBufferPointer` is also necessary for consistency. The reverse initializer, from `UnsafeMutableBufferPointer` to `UnsafeBufferPointer` should also be added for completeness. -- **add a mutable overload to the `copy(from:)` method on `UnsafeMutableRawBufferPointer`, the `initialize(at:from:)` and `assign(at:from:)` methods on `UnsafeMutableBufferPointer`, and the `initializeMemory(as:at:from:)` method on `UnsafeMutableRawBufferPointer`** - -Currently, for plain pointers, there is a compiler subtyping relationship between `UnsafePointer` and `UnsafeMutablePointer`. No such relationship exists between `UnsafeBufferPointer` and `UnsafeMutableBufferPointer` or their raw counterparts, so it is necessary to provide mutable overloads for these functions. - -- **add `deallocate()` to all pointer types, replacing any existing deallocation methods** +- **deprecate the sized deallocation API** Removing `capacity` from `deallocate(capacity:)` will end the confusion over what `deallocate()` does, making it obvious that `deallocate()` will free the *entire* memory block at `self`, just as if `free()` were called on it. @@ -405,7 +242,9 @@ The old `deallocate(capacity:)` method should be marked as `deprecated` and even Along similar lines, the `bytes` and `alignedTo` parameters should be removed from the `deallocate(bytes:alignedTo:)` method on `UnsafeMutableRawPointer` and `UnsafeRawPointer`. -An unsized `deallocate()` method should be added to all pointer types, even immutable ones, as Swift’s memory model does not require memory to be mutable for deallocation. This fixes [SR-3309](https://bugs.swift.org/browse/SR-3309). Note, immutable raw buffer pointers already support this API. +As discussed earlier, an unsized `deallocate()` method should be added to all pointer types, even immutable ones, as Swift’s memory model does not require memory to be mutable for deallocation. + +> note: the deallocation size parameters were originally included in early versions of Swift in order to support a more sophisticated hypothetical heap allocator backend that we wanted to have in the future. (Swift currently calls `malloc(_:)` and `free()`.) While such a backend would theoretically run more efficiently than the C backend, overengineering Swift to support it in the future has proven to be a detriment to users right now. By removing the size parameters now, we make it easier and safer to reintroduce such an API in the future without inadvertently causing silent source breakage. > note: changes to deallocation methods are not listed in the type-by-type overview below. All items in the following list are either non-source breaking, or trivially automigratable. @@ -417,6 +256,12 @@ An unsized `deallocate()` method should be added to all pointer types, even immu func withMemoryRebound(to:capacity:_:) -> Result ``` +#### New methods + +```diff ++++ func deallocate() +``` + ### `UnsafeMutablePointer` #### Existing methods @@ -425,13 +270,6 @@ func withMemoryRebound(to:capacity:_:) -> Result static func allocate(capacity:) -> UnsafeMutablePointer -func initialize(from:count:) -func moveInitialize(from:count:) - -func assign(from:count:) -func moveAssign(from:count:) - -func deinitialize(count:) func withMemoryRebound(to:capacity:_:) -> Result ``` @@ -445,6 +283,9 @@ func withMemoryRebound(to:capacity:_:) -> Result #### New methods ```diff ++++ func deallocate() + ++++ func initialize(to:) +++ func assign(repeating:count:) ``` @@ -456,39 +297,41 @@ func withMemoryRebound(to:capacity:_:) -> Result func bindMemory(to:capacity:) -> UnsafePointer ``` +#### New methods + +```diff ++++ func deallocate() +``` + ### `UnsafeMutableRawPointer` #### Existing methods ``` -static -func allocate(bytes:alignedTo:) -> UnsafeMutableRawPointer - func bindMemory(to:capacity:) -> UnsafeMutablePointer ``` -#### Renamed methods +#### New methods ```diff ---- func initializeMemory(as:at:count:to:) -> UnsafeMutablePointer -+++ func initializeMemory(as:at:repeating:count:) -> UnsafeMutablePointer - ---- func copyBytes(from:count:) -+++ func copy(from:bytes:) ++++ func deallocate() ``` -#### New arguments +#### Renamed methods and dropped arguments ```diff ---- func initializeMemory(as:from:count:) -> UnsafeMutablePointer ---- func moveInitializeMemory(as:from:count:) -> UnsafeMutablePointer -+++ func initializeMemory(as:at:from:count:) -> UnsafeMutablePointer -+++ func moveInitializeMemory(as:at:from:count:) -> UnsafeMutablePointer -``` +--- static +--- func allocate(bytes:alignedTo:) -> UnsafeMutableRawPointer + ++++ static ++++ func allocate(byteCount:alignment:) -> UnsafeMutableRawPointer -> note: The new `at:` argument has a backwards-compatible default argument of `0`. This not only makes sense, and is consistent with the existing default value of `at:` in `initializeMemory(as:at:count:to:)`, it also prevents source breakage. +--- func initializeMemory(as:at:count:to:) -> UnsafeMutablePointer ++++ func initializeMemory(as:repeating:count:) -> UnsafeMutablePointer -> note: We are adding a *new* default argument of `MemoryLayout.alignment` for the `alignment` parameter in `allocate(bytes:alignedTo:)`. The rationale is that Swift is introducing a language-level default guarantee of word-aligned storage, so the default argument reflects Swift’s memory model. Higher alignments (such as 16-byte alignment) should be specified explicitly by the user. +--- func copyBytes(from:count:) ++++ func copyMemory(from:byteCount:) +``` ### `UnsafeBufferPointer` @@ -510,14 +353,8 @@ func bindMemory(to:capacity:) -> UnsafeMutablePointer +++ func deallocate() +++ func initialize(repeating:) -+++ func initialize(at:from:) -+++ func moveInitialize(at:from:) - +++ func assign(repeating:) -+++ func assign(at:from:) -+++ func moveAssign(at:from:) -+++ func deinitialize(at:count:) +++ func withMemoryRebound(to:_:) -> Result ``` @@ -543,44 +380,31 @@ deallocate() deallocate() ``` -#### New/renamed arguments +#### Renamed methods and new/renamed arguments ```diff --- static --- func allocate(count:) -> UnsafeMutableRawBufferPointer +++ static -+++ func allocate(bytes:alignedTo:) -> UnsafeMutableRawBufferPointer ++++ func allocate(byteCount:alignment:) -> UnsafeMutableRawBufferPointer --- func copyBytes(from:) -+++ func copyBytes(at:from:) ++++ func copyMemory(from:) ``` #### New methods ```diff +++ func initializeMemory(as:repeating:) -> UnsafeMutableBufferPointer -+++ func initializeMemory(as:at:from:) -> UnsafeMutableBufferPointer -+++ func moveInitializeMemory(as:at:from:) -> UnsafeMutableBufferPointer +++ func bindMemory(to:) -> UnsafeMutableBufferPointer ``` -> note: for backwards compatibility, the `alignedTo:` argument in `allocate(bytes:alignedTo:)` should take a default value of `MemoryLayout.alignment`. This requires [SR-5664](https://bugs.swift.org/browse/SR-5664) to be fixed before it will work properly. - -> note: The new `at:` argument in `copyBytes(at:from:)` has a backwards-compatible default argument of `0`. This poses no risk to memory state safety, since this method can only perform a bytewise copy anyways. - ## What this proposal does not do -- **attempt to fully support partial initialization** +- **attempt to fully partial initialization** -This proposal attempts to design a buffer interface that provides some semblance of memory state safety. However, it does not fully address issues relating to ergonomics such as - - - overloading `+` for buffer pointers, allowing “pointer arithmetic” to be performed on buffer pointers - - easier buffer pointer slicing, which does not produce wasteful `MutableRandomAccessSlice>` structures - -nor does it attempt to design a higher level buffer type which would be able to provide stronger memory state guarantees. - -We expect possible solutions to these problems to purely additive, and would not require modifying the methods this proposal will introduce. +This proposal does not attempt to fill in most of the memory state APIs for buffer pointers, as doing so necessitates designing a partial initialization system, as well as a possible buffer slice rework. - **address problems relating to the generic `Sequence` buffer API** @@ -599,23 +423,22 @@ struct UnsafePointer struct UnsafeMutablePointer { ---- static func allocate(capacity:Int) -> UnsafeMutablePointer -+++ static func allocate(capacity:Int = 1) -> UnsafeMutablePointer + static func allocate(capacity:Int) -> UnsafeMutablePointer --- func deallocate(capacity:Int) +++ func deallocate() --- func initialize(to:Pointee, count:Int = 1) -+++ func initialize(repeating:Pointee, count:Int = 1) ++++ func initialize(repeating:Pointee, count:Int) ++++ func initialize(to:Pointee) func initialize(from:UnsafePointer, count:Int) - moveInitialize(from:UnsafeMutablePointer, count:Int) + func moveInitialize(from:UnsafeMutablePointer, count:Int) -+++ func assign(repeating:Pointee, count:Int = 1) ++++ func assign(repeating:Pointee, count:Int) func assign(from:UnsafePointer, count:Int) func moveAssign(from:UnsafeMutablePointer, count:Int) ---- func deinitialize(count:Int) -+++ func deinitialize(count:Int = 1) + func deinitialize(count:Int) func withMemoryRebound(to:T.Type, capacity:Int, _ body:(UnsafeMutablePointer) -> Result) -> Result @@ -634,26 +457,21 @@ struct UnsafeMutableRawPointer --- static --- func allocate(bytes:Int, alignedTo:Int) -> UnsafeMutableRawPointer +++ static -+++ func allocate(bytes:Int, alignedTo:Int = MemoryLayout.alignment) -+++ -> UnsafeMutableRawPointer ++++ func allocate(byteCount:Int, alignment:Int) -> UnsafeMutableRawPointer --- func deallocate(bytes:Int, alignedTo:Int) +++ func deallocate() --- func initializeMemory(as:T.Type, at:Int = 0, count:Int = 1, to:T) -> UnsafeMutablePointer -+++ func initializeMemory(as:T.Type, at:Int = 0, repeating:T, count:Int = 1) -> UnsafeMutablePointer ++++ func initializeMemory(as:T.Type, repeating:T, count:Int) -> UnsafeMutablePointer ---- func initializeMemory(as:T.Type, from:UnsafePointer, count:Int) -> UnsafeMutablePointer ---- func moveInitializeMemory(as:T.Type, from:UnsafeMutablePointer, count:Int) ---- -> UnsafeMutablePointer -+++ func initializeMemory(as:T.Type, at:Int = 0, from:UnsafePointer, count:Int) -+++ -> UnsafeMutablePointer -+++ func moveInitializeMemory(as:T.Type, at:Int = 0, from:UnsafeMutablePointer, count:Int) -+++ -> UnsafeMutablePointer + func initializeMemory(as:T.Type, from:UnsafePointer, count:Int) -> UnsafeMutablePointer + func moveInitializeMemory(as:T.Type, from:UnsafeMutablePointer, count:Int) + -> UnsafeMutablePointer func bindMemory(to:T.Type, count:Int) -> UnsafeMutablePointer --- func copyBytes(from:UnsafeRawPointer, count:Int) -+++ func copy(from:UnsafeRawPointer, bytes:Int) ++++ func copyMemory(from:UnsafeRawPointer, byteCount:Int) } struct UnsafeBufferPointer @@ -674,16 +492,7 @@ struct UnsafeMutableBufferPointer +++ func allocate(capacity:Int) -> UnsafeMutableBufferPointer +++ func initialize(repeating:Element) -+++ func initialize(at:Int, from:UnsafeBufferPointer) -+++ func initialize(at:Int, from:UnsafeMutableBufferPointer) -+++ func moveInitialize(at:Int, from:UnsafeMutableBufferPointer) - +++ func assign(repeating:Element) -+++ func assign(at:Int, from:UnsafeBufferPointer) -+++ func assign(at:Int, from:UnsafeMutableBufferPointer) -+++ func moveAssign(at:Int, from:UnsafeMutableBufferPointer) - -+++ func deinitialize(at:Int, count:Int) +++ func withMemoryRebound +++ (to:T.Type, _ body:(UnsafeMutableBufferPointer) -> Result) @@ -699,56 +508,55 @@ struct UnsafeRawBufferPointer struct UnsafeMutableRawBufferPointer { --- static func allocate(count:Int) -> UnsafeMutableRawBufferPointer -+++ static func allocate(bytes:Int, alignedTo:Int = MemoryLayout.alignment) -+++ -> UnsafeMutableRawBufferPointer ++++ static func allocate(byteCount:Int, alignment:Int) -> UnsafeMutableRawBufferPointer func deallocate() +++ func initializeMemory(as:T.Type, repeating:T) -> UnsafeMutableBufferPointer -+++ func initializeMemory(as:T.Type, at:Int, from:UnsafeBufferPointer) -+++ -> UnsafeMutableBufferPointer -+++ func initializeMemory(as:T.Type, at:Int, from:UnsafeMutableBufferPointer) -+++ -> UnsafeMutableBufferPointer -+++ func moveInitializeMemory(as:T.Type, at:Int, from:UnsafeMutableBufferPointer) -+++ -> UnsafeMutableBufferPointer +++ func bindMemory(to:T.Type) -> UnsafeMutableBufferPointer --- func copyBytes(from:UnsafeRawBufferPointer) -+++ func copyBytes(at:Int = 0, from:UnsafeRawBufferPointer) -+++ func copyBytes(at:Int = 0, from:UnsafeMutableRawBufferPointer) ++++ func copyMemory(from:UnsafeRawBufferPointer) } ``` ## Source compatibility -- **fix the ordering of the arguments in `initializeMemory(as:at:count:to:)` and rename the argument `to:` to `repeating:` in all repeated-value copy functions** +Everything is additive except the following. Can we deprecate all of +the original functions in Swift 5, then drop those from the binary +later in Swift 6? -This change is source breaking but can be trivially automigrated. - -- **add the repeated-value copy assignment method `assign(repeating:count:)`** +- **add `deallocate()` to all pointer types, replacing any existing deallocation methods** -This change is purely additive. +The migrator needs to drop the existing `capacity` and `alignedTo` arguments. -- **rename `copyBytes(from:count:)` to `copy(from:bytes:)` on `UnsafeMutableRawPointer`** +- **in `UnsafeMutableRawPointer.allocate(count:alignedTo:)` rename `count` to `byteCount` and `alignedTo` to `alignment`** -This change is source breaking but can be trivially automigrated. +- **in `UnsafeMutableRawBufferPointer.allocate(count:)` rename `count` to `byteCount` and add an `alignment` parameter** -- **rename `count` in `UnsafeMutableRawBufferPointer.allocate(count:)` to `bytes` and add an `alignedTo` parameter to make it `UnsafeMutableRawBufferPointer.allocate(bytes:alignedTo:)`** +This change is source breaking but can be trivially automigrated. The +`alignment:` parameter can be filled in with `MemoryLayout.stride`. -This change is source breaking but can be trivially automigrated. The `alignedTo:` parameter can be filled in with `MemoryLayout.stride`. If [SR-5664](https://bugs.swift.org/browse/SR-5664) is fixed, `MemoryLayout.stride` can even be provided as a default argument. +- **fix the arguments to `initialize(repeating:Pointee, count:Int)`** -- **add an `init(mutating:)` initializer to `UnsafeMutableBufferPointer`** +Note: initialize(to:Pointee) is backward compatible whenever the +caller relied on a default `count = 1`. -This change is purely additive. +An annotation could otherwise rename `to` to `repeating`, but we don't +want that to interfere with the default count case, so this might need to be a migrator rule. -- **add a mutable overload to the `copy(from:)` method on `UnsafeMutableRawBufferPointer`, the `initialize(at:from:)` and `assign(at:from:)` methods on `UnsafeMutableBufferPointer`, and the `initializeMemory(as:at:from:)` method on `UnsafeMutableRawBufferPointer`** +- **fix the ordering of the arguments in `initializeMemory(as:at:count:to:)`, rename `to:` to `repeating:`, and remove the `at:` argument** -This change is purely additive. +This change is source breaking but can be trivially automigrated. The +`to` argument changes position and is relabeled as `repeating`. -- **add `deallocate()` to all pointer types, replacing any existing deallocation methods** +The migrator could be taught to convert the `at:` argument into +pointer arithmetic on `self`. However, we found no code on Github that +uses the `at:` argument, so it is low priority. -This change is source-breaking, but this is a Good Thing™. The current API encourages incorrect code to be written, and sets us up for potentially catastrophic source breakage down the road should the implementations of `deallocate(capacity:)` and `deallocate(bytes:alignedTo:)` ever be “fixed”, so users should be forced to stop using them as soon as possible. +- **rename `copyBytes(from:count:)` to `copyMemory(from:byteCount:)`** +This change is source breaking but can be trivially automigrated. ## Effect on ABI stability @@ -760,8 +568,6 @@ Some proposed changes in this proposal change the public API. Removing sized deallocators right now will break ABI, but offers increased ABI and API stability in the future as reallocator methods can be added in the future without having to rename `deallocate(capacity:)` which currently occupies a “reallocator” name, but has “`free()`” behavior. -This proposal seeks to tackle all the breaking changes required for such an overhaul of Swift pointers, and leaves unanswered only additive changes that still need to be made in the future, reducing the need for future breakage. - ## Alternatives considered - **keeping sized deallocators and fixing the stdlib implementation instead** @@ -779,4 +585,3 @@ The label `value:` or `toValue:` doesn’t fully capture the repeating nature of ```swift ptr.initialize(value: value) ``` - From 4954943a87179138ae5a0f61eb929fc2b5bcdd84 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 5 Oct 2017 13:51:57 -0700 Subject: [PATCH 0462/4563] Accept SE-0184: Unsafe[Mutable][Raw][Buffer]Pointer: add missing methods, adjust existing labels for clarity, and remove deallocation size --- proposals/0184-unsafe-pointers-add-missing.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0184-unsafe-pointers-add-missing.md b/proposals/0184-unsafe-pointers-add-missing.md index 9b4e9fb0c7..24ca806096 100644 --- a/proposals/0184-unsafe-pointers-add-missing.md +++ b/proposals/0184-unsafe-pointers-add-missing.md @@ -1,11 +1,12 @@ # Unsafe[Mutable][Raw][Buffer]Pointer: add missing methods, adjust existing labels for clarity, and remove deallocation size -* Proposal: [SE-0184](0184a-unsafe-pointers-part-1.md) +* Proposal: [SE-0184](0184-unsafe-pointers-add-missing.md) * Author: [Kelvin Ma (“Taylor Swift”)](https://github.com/kelvin13) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: +* Status: **Accepted** * Implementation: [PR 12200](https://github.com/apple/swift/pull/12200) + ## Introduction *This document is a spin-off from a much larger [original proposal](https://github.com/kelvin13/swift-evolution/blob/e888af466c9993de977f6999a131eadd33291b06/proposals/0184-unsafe-pointers-add-missing.md), which covers only those aspects of SE-1084 which do not deal with partial buffer memory state. Designing the partial buffer memory state API clearly requires more work, and has been left out of the scope of this document.* From 8190a72a6373f1e76515fbe577c833e130c46380 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 5 Oct 2017 13:53:31 -0700 Subject: [PATCH 0463/4563] Add decision notes --- proposals/0184-unsafe-pointers-add-missing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0184-unsafe-pointers-add-missing.md b/proposals/0184-unsafe-pointers-add-missing.md index 24ca806096..f811b5b7a7 100644 --- a/proposals/0184-unsafe-pointers-add-missing.md +++ b/proposals/0184-unsafe-pointers-add-missing.md @@ -5,6 +5,7 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Accepted** * Implementation: [PR 12200](https://github.com/apple/swift/pull/12200) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171002/040248.html) ## Introduction From de695c075c17c024441c22f2ccc700243961b796 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Thu, 5 Oct 2017 14:16:25 -0700 Subject: [PATCH 0464/4563] Update implementation link formatting. --- proposals/0184-unsafe-pointers-add-missing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0184-unsafe-pointers-add-missing.md b/proposals/0184-unsafe-pointers-add-missing.md index f811b5b7a7..3eb3d13325 100644 --- a/proposals/0184-unsafe-pointers-add-missing.md +++ b/proposals/0184-unsafe-pointers-add-missing.md @@ -4,7 +4,7 @@ * Author: [Kelvin Ma (“Taylor Swift”)](https://github.com/kelvin13) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Accepted** -* Implementation: [PR 12200](https://github.com/apple/swift/pull/12200) +* Implementation: [apple/swift#12200](https://github.com/apple/swift/pull/12200) * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171002/040248.html) From 121a41f101a1d2ba60cddf90567f68608a4957a8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 10 Oct 2017 10:55:13 -0700 Subject: [PATCH 0465/4563] SE-0167 was implemented in Swift 4 --- proposals/0167-swift-encoders.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0167-swift-encoders.md b/proposals/0167-swift-encoders.md index 05110f1a4d..12d34f4a7d 100644 --- a/proposals/0167-swift-encoders.md +++ b/proposals/0167-swift-encoders.md @@ -3,7 +3,7 @@ * Proposal: [SE-0167](0167-swift-encoders.md) * Authors: [Itai Ferber](https://github.com/itaiferber), [Michael LeHew](https://github.com/mlehew), [Tony Parker](https://github.com/parkera) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-April/000368.html) * Implementation: [apple/swift#9005](https://github.com/apple/swift/pull/9005) From 9b4023aa6fab708be2688f8f8f4877ee29414be9 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 10 Oct 2017 10:55:26 -0700 Subject: [PATCH 0466/4563] SE-0186 is implemented in Swift master --- .../0186-remove-ownership-keyword-support-in-protocols.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0186-remove-ownership-keyword-support-in-protocols.md b/proposals/0186-remove-ownership-keyword-support-in-protocols.md index ff0ba493eb..1c18994eaf 100644 --- a/proposals/0186-remove-ownership-keyword-support-in-protocols.md +++ b/proposals/0186-remove-ownership-keyword-support-in-protocols.md @@ -3,8 +3,7 @@ * Proposal: [SE-0186](0186-remove-ownership-keyword-support-in-protocols.md) * Author: [Greg Spiers](https://github.com/gspiers) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Accepted** -* Implementation: [apple/swift#11744](https://github.com/apple/swift/pull/11744) +* Status: **Implemented (Swift 4.1)** * [Review thread on swift-evolution](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170918/039863.html) * Bug: [SR-479](https://bugs.swift.org/browse/SR-479) * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170925/040012.html) From 3538e87d1765bcbd28335337cbb5fa9f9165a582 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Fri, 6 Oct 2017 14:27:22 -0700 Subject: [PATCH 0467/4563] Add proposal "Restrict Cross-module Struct Initializers" --- ...strict-cross-module-struct-initializers.md | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 proposals/nnnn-restrict-cross-module-struct-initializers.md diff --git a/proposals/nnnn-restrict-cross-module-struct-initializers.md b/proposals/nnnn-restrict-cross-module-struct-initializers.md new file mode 100644 index 0000000000..282b4a22a4 --- /dev/null +++ b/proposals/nnnn-restrict-cross-module-struct-initializers.md @@ -0,0 +1,102 @@ +# Restrict Cross-module Struct Initializers + +* Proposal: [SE-NNNN](NNNN-non-exhaustive-enums.md) +* Authors: [Jordan Rose](https://github.com/jrose-apple) +* Review Manager: TBD +* Status: **Awaiting review** + + + +## Introduction + +Adding a property to a public struct in Swift ought to not be a source-breaking change. However, a client in another target can currently extend a struct with a new initializer that directly initializes the struct's fields. This proposal forbids that, requiring any cross-target initializers to use `self.init(…)` or assign to `self` instead. This matches an existing restriction for classes, where cross-module initializers must be convenience initializers. + + +## Motivation + +Swift structs are designed to be flexible, allowing library authors to change their implementation between releases. This goes all the way to changing the set of stored properties that make up the struct. Since initializers have to initialize every stored property, they have two options: + +- Assign each property before returning or using `self`. +- Assign all properties at once by using `self.init(…)` or `self = …`. + +The former requires knowing every stored property in the struct. If all of those properties happen to be public, however, a client in another target can implement their own initializer, and suddenly adding a new stored property (public or not) becomes a source-breaking change. + +Additionally, initializers are often used with `let` properties to enforce a struct's invariants. Consider this (contrived) example: + +```swift +public struct BalancedPair { + public let positive: Int + public let negative: Int + public init(absoluteValue: Int) { + assert(absoluteValue >= 0) + self.positive = absoluteValue + self.negative = -absoluteValue + } +} +``` + +At this point a user of BalancedPair ought to be able to assume that `positive` and `negative` always hold opposite values. However, an unsuspecting (or malicious) client could add their own initializer that breaks this invariant: + +```swift +import ContrivedExampleKit +extension BalancedPair { + init(positiveOnly value: Int) { + self.positive = value + self.negative = 0 + } +} +``` + +Anything that prevents the library author from enforcing the invariants of their type is a danger and contrary to the spirit of Swift. + + +## Proposed solution + +If an initializer is declared in a different module from a struct, it must use `self.init(…)` or `self = …` before returning or accessing `self`. Failure to do so will produce a warning in Swift 4 and an error in Swift 5. + +The recommendation for library authors who wish to continue allowing this is to explicitly declare a public memberwise initializer for clients in other modules to use. + + +### C structs + +C structs are not exempt from this rule, but all C structs are imported with a memberwise initializer anyway. This *still* does not guarantee source compatibility because C code owners occasionally decide to split up or rename members of existing structs, but this proposal does not make that situation worse. Most C structs also have a no-argument initializer that fills the struct with zeros unless one of the members is marked `_Nonnull`. + + +## Source compatibility + +This makes existing code invalid in Swift 5, which is a source compatibility break. + +This makes adding a stored property to a struct a source-compatible change (except for Swift 4 clients that choose to ignore the warning). + + +## Effect on ABI stability + +This is required for structs to avoid exposing the layout of their properties in a library's binary interface. + + +## Effect on Library Evolution + +It is now a binary-compatible change to add a public or non-public stored property to a struct. + +It is still not a binary-compatible change to remove a public stored property from a struct. + + +## Alternatives considered + +### Do nothing + +We've survived so far, so we can live without this for libraries that don't have binary compatibility concerns, but not being able to enforce invariants is still a motivating reason to do this proposal. + + +### Distinguish between "structs with a fixed set of stored properties" and "structs that may get new stored properties later" + +This actually *is* a distinction we want to make for code in frameworks with binary compatibility constraints, where the ability to add new members to a struct forces client code to use extra indirection. However, this should be an advanced feature in the language, and limiting its use to binary frameworks is a good way to keep it out of the way for most developers. A library author can get nearly the same effect simply by defining a public memberwise initializer, something that's common to do anyway. \ No newline at end of file From 9a35e25693fb5422ac0dcef8cea76165d7aa0431 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Mon, 9 Oct 2017 15:43:14 -0700 Subject: [PATCH 0468/4563] Cross-module struct initializers: update for feedback - Add an exception for structs from testably-imported modules. - Explicitly mention '@_fixed_layout' in "Alternatives considered". - Add a section in "Alternatives considered" specifically about C structs. --- ...strict-cross-module-struct-initializers.md | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/proposals/nnnn-restrict-cross-module-struct-initializers.md b/proposals/nnnn-restrict-cross-module-struct-initializers.md index 282b4a22a4..3b6a977e3c 100644 --- a/proposals/nnnn-restrict-cross-module-struct-initializers.md +++ b/proposals/nnnn-restrict-cross-module-struct-initializers.md @@ -4,12 +4,12 @@ * Authors: [Jordan Rose](https://github.com/jrose-apple) * Review Manager: TBD * Status: **Awaiting review** +* Pull Request: [apple/swift#12352](https://github.com/apple/swift/pull/12352) +* Pre-review discussion: [Restrict Cross-module Struct Initializers](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171002/040261.html) + + +## Introduction + +We propose to deprecate the controversial version of a `Sequence.flatMap` method +and provide the same functionality under a different, and potentially more +descriptive, name. + + + +## Motivation + +The Swift standard library currently defines 3 distinct overloads for `flatMap`: + +~~~swift +Sequence.flatMap(_: (Element) -> S) -> [S.Element] + where S : Sequence +Optional.flatMap(_: (Wrapped) -> U?) -> U? +Sequence.flatMap(_: (Element) -> U?) -> [U] +~~~ + +The last one, despite being useful in certain situations, can be (and often is) +misused. Consider the following snippet: + +~~~swift +struct Person { + var age: Int + var name: String +} + +func getAges(people: [Person]) -> [Int] { + return people.flatMap { $0.age } +} +~~~ + +What happens inside `getAges` is: thanks to the implicit promotion to +`Optional`, the result of the closure gets wrapped into a `.some`, then +immediately unwrapped by the implementation of `flatMap`, and appended to the +result array. All this unnecessary wrapping and unwrapping can be easily avoided +by just using `map` instead. + +~~~swift +func getAges(people: [Person]) -> [Int] { + return people.map { $0.age } +} +~~~ + +It gets even worse when we consider future code modifications, like the one +where Swift 4 introduced a `String` conformance to the `Collection` protocol. +The following code used to compile (due to the `flatMap` overload in question). + +~~~swift +func getNames(people: [Person]) -> [String] { + return people.flatMap { $0.name } +} +~~~ + +But it no longer does, because now there is a better overload that does not +involve implicit promotion. In this particular case, the compiler error would be +obvious, as it would point at the same line where `flatMap` is used. Imagine +however if it was just a `let names = people.flatMap { $0.name }` statement, and +the `names` variable were used elsewhere. The compiler error would be +misleading. + +## Proposed solution + +We propose to deprecate the controversial overload of `flatMap` and re-introduce +the same functionality under a new name. The name being `filteredMap(_:)` as we +believe it best describes the intent of this function. + +For reference, here are the alternative names from other languages: +- Haskell, Idris + `
mapMaybe :: (a -> Maybe b) -> [a] -> [b]` +- Ocaml (Core and Batteries)
 + `filter_map : 'a t -> f:('a -> 'b option) -> 'b t` +- F#
 + `List.choose : ('T -> 'U option) -> 'T list -> 'U list` +- Rust
 + `fn filter_map(self, f: F) -> FilterMap
 where F: FnMut(Self::Item) -> Option` +- Scala + `
def collect[B](pf: PartialFunction[A, B]): List[B]` + + +## Source compatibility + +Since the old function will still be available (although deprecated) all +the existing code will compile, producing a deprecation warning and a fix-it. + +## Effect on ABI stability + +This is an additive API change, and does not affect ABI stability. + +## Effect on API resilience + +Ideally, the deprecated `flatMap` overload would not exist at the time when ABI +stability is declared, but in the worst case, it will be available in a +deprecated form from a library post-ABI stability. + +## Alternatives considered + +It was attempted in the past to warn about this kind of misuse and do the right +thing instead by means of a deprecated overload with a non-optional-returning +closure. The attempt failed due to another implicit promotion (this time to +`Any`). + +The following alternative names for this function were considered: +- `mapNonNil(_:)
` + Does not communicate what happens to nil’s +- `mapSome(_:)
` + Reads more like «map some elements of the sequence, but not the others» + rather than «process only the ones that produce an Optional.some» +- `filterMap(_:)` + 
Does not really follow the naming guidelines and doesn’t seem to be common + enough to be considered a term of art. From d187094d9258c78729a97a8081206b9bb45ac317 Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Sun, 29 Oct 2017 00:08:53 +0900 Subject: [PATCH 0471/4563] [status page] Add 4.1 to the language versions SE-0075 and SE-0186 are already implemented for Swift 4.1. --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index cc4a4f5900..d3cd0529c5 100644 --- a/index.js +++ b/index.js @@ -19,7 +19,7 @@ var proposals * To be updated when proposals are confirmed to have been implemented * in a new language version. */ -var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4'] +var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4', '4.1'] /** Storage for the user's current selection of filters when filtering is toggled off. */ var filterSelection = [] From 4199bb64ac61b10e0da05a4d71a7014151413a4b Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Wed, 1 Nov 2017 02:04:30 +0900 Subject: [PATCH 0472/4563] SE-0185 is implemented in Swift master (#756) --- proposals/0185-synthesize-equatable-hashable.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0185-synthesize-equatable-hashable.md b/proposals/0185-synthesize-equatable-hashable.md index 8acf69da18..0efe39436b 100644 --- a/proposals/0185-synthesize-equatable-hashable.md +++ b/proposals/0185-synthesize-equatable-hashable.md @@ -3,8 +3,7 @@ * Proposal: [SE-0185](0185-synthesize-equatable-hashable.md) * Author: [Tony Allevato](https://github.com/allevato) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Accepted** -* Implementation: [apple/swift#9619](https://github.com/apple/swift/pull/9619) +* Status: **Implemented (Swift 4.1)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-August/000400.html) ## Introduction From 4b0dc96e10bc0ba43e759ac076fbe96f7ff8713d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 31 Oct 2017 10:16:59 -0700 Subject: [PATCH 0473/4563] SE-0161 Smart KeyPaths were implemented in 4.0.x --- proposals/0161-key-paths.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0161-key-paths.md b/proposals/0161-key-paths.md index 56a835fea7..771c307f18 100644 --- a/proposals/0161-key-paths.md +++ b/proposals/0161-key-paths.md @@ -3,7 +3,7 @@ * Proposal: [SE-0161](0161-key-paths.md) * Authors: [David Smith](https://github.com/Catfish-Man), [Michael LeHew](https://github.com/mlehew), [Joe Groff](https://github.com/jckarter) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 4.0)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-April/000356.html) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/55e61f459632eca2face40e571a517919f846cfb/proposals/0161-key-paths.md) From 6b4e28d6bc1b26dece3dd92da4a3e8381a0ae2fc Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 2 Nov 2017 13:12:35 -0700 Subject: [PATCH 0474/4563] SE-0157 "Recursive constraints" is implemented in Swift 4.1 --- proposals/0157-recursive-protocol-constraints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0157-recursive-protocol-constraints.md b/proposals/0157-recursive-protocol-constraints.md index 0426b6a571..c0fea174ae 100644 --- a/proposals/0157-recursive-protocol-constraints.md +++ b/proposals/0157-recursive-protocol-constraints.md @@ -3,7 +3,7 @@ * Proposal: [SE-0157](0157-recursive-protocol-constraints.md) * Authors: [Douglas Gregor](https://github.com/DougGregor), [Erica Sadun](https://github.com/erica), [Austin Zheng](https://github.com/austinzheng) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 4.1)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170320/034266.html) * Bug: [SR-1445](https://bugs.swift.org/browse/SR-1445) From adb9fb8c7c87d36d67c44e6963749891b3319bb2 Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Fri, 3 Nov 2017 09:42:34 -0700 Subject: [PATCH 0475/4563] Prepare for review --- ...filteredmap.md => 0187-introduce-filtermap.md} | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) rename proposals/{0000-introduce-filteredmap.md => 0187-introduce-filtermap.md} (90%) diff --git a/proposals/0000-introduce-filteredmap.md b/proposals/0187-introduce-filtermap.md similarity index 90% rename from proposals/0000-introduce-filteredmap.md rename to proposals/0187-introduce-filtermap.md index 92ae96f8cd..90dcfab2c0 100644 --- a/proposals/0000-introduce-filteredmap.md +++ b/proposals/0187-introduce-filtermap.md @@ -1,12 +1,12 @@ -# Introduce Sequence.filteredMap(_:) +# Introduce Sequence.filterMap(_:) -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0187](0187-introduce-filtermap.md) * Authors: [Max Moiseev](https://github.com/moiseev) * Review Manager: TBD * Status: **Awaiting implementation** +* Swift-evolution discussion: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171023/040609.html - ## Motivation The Swift standard library currently defines 3 distinct overloads for `flatMap`: @@ -85,7 +81,7 @@ misleading. ## Proposed solution We propose to deprecate the controversial overload of `flatMap` and re-introduce -the same functionality under a new name. The name being `filteredMap(_:)` as we +the same functionality under a new name. The name being `filterMap(_:)` as we believe it best describes the intent of this function. For reference, here are the alternative names from other languages: @@ -129,6 +125,3 @@ The following alternative names for this function were considered: - `mapSome(_:)
` Reads more like «map some elements of the sequence, but not the others» rather than «process only the ones that produce an Optional.some» -- `filterMap(_:)` - 
Does not really follow the naming guidelines and doesn’t seem to be common - enough to be considered a term of art. From ebf32099159300162a142621f990cc5b6bd673c0 Mon Sep 17 00:00:00 2001 From: Yuka Ezura Date: Sun, 5 Nov 2017 13:13:22 +0900 Subject: [PATCH 0476/4563] fix sample code (#759) modified the variable name of sample code. --- proposals/0185-synthesize-equatable-hashable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0185-synthesize-equatable-hashable.md b/proposals/0185-synthesize-equatable-hashable.md index 0efe39436b..4a13dffbf5 100644 --- a/proposals/0185-synthesize-equatable-hashable.md +++ b/proposals/0185-synthesize-equatable-hashable.md @@ -67,7 +67,7 @@ enum Token: Equatable { switch (lhs, rhs) { case (.string(let lhsString), .string(let rhsString)): return lhsString == rhsString - case (.number(let lhsNumber), .number(let lhsNumber)): + case (.number(let lhsNumber), .number(let rhsNumber)): return lhsNumber == rhsNumber case (.lparen, .lparen), (.rparen, .rparen): return true From 9e70265d3b114914187aac393a96ea4b718cfa3b Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 7 Nov 2017 18:08:31 -0500 Subject: [PATCH 0477/4563] Put "SE-0186: Introduce Sequence.filterMap(_:)" under active review. --- ...87-introduce-filtermap.md => 0186-introduce-filtermap.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/0187-introduce-filtermap.md => 0186-introduce-filtermap.md (96%) diff --git a/proposals/0187-introduce-filtermap.md b/0186-introduce-filtermap.md similarity index 96% rename from proposals/0187-introduce-filtermap.md rename to 0186-introduce-filtermap.md index 90dcfab2c0..6b1840a84f 100644 --- a/proposals/0187-introduce-filtermap.md +++ b/0186-introduce-filtermap.md @@ -1,9 +1,9 @@ # Introduce Sequence.filterMap(_:) -* Proposal: [SE-0187](0187-introduce-filtermap.md) +* Proposal: [SE-0186](0186-introduce-filtermap.md) * Authors: [Max Moiseev](https://github.com/moiseev) -* Review Manager: TBD -* Status: **Awaiting implementation** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (November 7...14, 2017)** * Swift-evolution discussion: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171023/040609.html + +## Introduction + +Currently, adding a new case to an enum is a source-breaking change, which is very inconvenient for library authors. This proposal aims to distinguish between enums that are _exhaustive_ (meaning they will never get any new cases) and those that are _non-exhaustive,_ and to ensure that clients handle any future cases when dealing with the latter. This declaration only affects clients from outside the original module. + +swift-evolution thread: [Enums and Source Compatibility](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170807/038663.html) + + +## Motivation + +It's well-established that many enums need to grow new cases in new versions of a library. For example, in last year's release of iOS 10, Foundation's [DateComponentsFormatter.UnitsStyle][] gained a `brief` case and UIKit's [UIKeyboardType][] gained an `asciiCapableNumberPad` case. Large error enums also often grow new cases to go with new operations supported by the library. This all implies that library authors *must* have a way to add new cases to enums. + +At the same time, we really like that you can exhaustively switch over enums. This feature helps prevent bugs and makes it possible to enforce [definitive initialization][DI] without having `default` cases in every `switch`. So we don't want to get rid of enums where every case is known, either. This calls for a new annotation that can distinguish between exhaustive and non-exhaustive enums. + +To see how this distinction will play out in practice, I investigated the public headers of Foundation in the macOS SDK. Out of all 60 or so `NS_ENUM`s in Foundation, only 6 of them are clearly exhaustive: + +- [ComparisonResult](https://developer.apple.com/documentation/foundation/comparisonresult) +- [NSKeyValueChange](https://developer.apple.com/documentation/foundation/nskeyvaluechange) / [NSKeyValueSetMutationKind](https://developer.apple.com/documentation/foundation/nskeyvaluesetmutationkind) +- [NSRectEdge](https://developer.apple.com/documentation/foundation/nsrectedge) +- [FileManager.URLRelationship](https://developer.apple.com/documentation/foundation/filemanager.urlrelationship) +- *maybe* [Decimal.CalculationError](https://developer.apple.com/documentation/foundation/nsdecimalnumber.calculationerror) + +...with a handful more that could go either way, such as [Stream.Status](https://developer.apple.com/documentation/foundation/stream.status). This demonstrates that there is a clear default for public enums, at least in Objective-C. + + [DateComponentsFormatter.UnitsStyle]: https://developer.apple.com/documentation/foundation/datecomponentsformatter.unitsstyle + [UIKeyboardType]: https://developer.apple.com/documentation/uikit/uikeyboardtype + [DI]: https://developer.apple.com/swift/blog/?id=28 + + +## Proposed solution + +Public enums can be declared as `exhaustive` or as `nonexhaustive`, defaulting to `exhaustive` for consistency with Swift 4.0 and earlier. + +When a client tries to switch over a `nonexhaustive` enum, they must include a `default` case unless the enum is declared in the same module as the switch. In Swift 4 mode, omitting this case will result in a warning; in Swift 5, it will be an error. + +Enums imported from C will be `nonexhaustive` by default, with a new C-side annotation to make them `exhaustive`. These enums conservatively always have the "cross-module" behavior. + + +## Detailed design + +### Definition-side + +```swift +public nonexhaustive enum HomeworkExcuse { + case eatenByPet + case thoughtItWasDueNextWeek +} + +public exhaustive enum GregorianWeekday { + case monday // ISO 8601 says weeks start on Monday + case tuesday + case wednesday + case thursday + case friday + case saturday + case sunday +} +``` + +A public enum can now be declared `nonexhaustive` or `exhaustive`, with `exhaustive` as the default. (This is a context-sensitive keyword, like `final`.) In Swift 5 the compiler will warn if a public enum does not have either modifier; since it affects how the enum is used outside the library, it should be a conscious decision. + +A warning is emitted when using either keyword on a non-public enum, since they have no effect within a module. + +The naming and spelling of these annotations is discussed in the "Alternatives Considered" section at the end of this proposal. + + +### Use-side + +When a `nonexhaustive` enum defined in module A is used **from another module**, any switch statement that matches against it must include a catch-all case (either `default` or an "ignore" `_` pattern). + +```swift +switch excuse { +case eatenByPet: + // … +case thoughtItWasDueNextWeek: + // … +} +``` + +In Swift 5, this would be an error. To maintain source compatibility, this would only produce a warning in Swift 4 mode. The Swift 4 program will trap at run time if an unknown enum case is actually encountered. + +All other uses of enums (`if case`, creation, accessing members, etc) do not change. Only the exhaustiveness checking of switches is affected by `exhaustive` and `nonexhaustive`, and then only across module boundaries. Non-exhaustive switches over `exhaustive` enums (and boolean values) will continue to be invalid in all language modes. + +> Note: Once Swift supports cross-module inlinable functions, switch statements in such functions will also need to provide a catch-all case, even for non-exhaustive enums declared in the same module. + +Here's a more complicated example: + +```swift +switch (excuse, notifiedTeacherBeforeDeadline) { +case (.eatenByPet, true): + // … +case (.thoughtItWasDueNextWeek, true): + // … +case (_, false): + // … +} +``` + +This switch handles all *known* patterns, but still doesn't account for the possibility of a new enum case when the second tuple element is `true`. This should be an error in Swift 5 and a warning in Swift 4, like the first example. + +The consequences of losing exhaustiveness checking for `nonexhaustive` enums are discussed in the "Alternatives Considered" section at the end of this proposal. + + +### C Enums + +Enums imported from C are a bit trickier, because it's difficult to tell whether they're part of the current project or not. An `NS_ENUM` in Apple's SDK should probably be treated as non-exhaustive, but one in your own framework might be exhaustive. Even there, though, it's possible that there's a "private case" defined in a .m file: + +```objc +// MyAppPaperSupport.h +typedef NS_ENUM(NSInteger, PaperSize) { + PaperSizeUSLetter = 0, + PaperSizeA4 = 1, + PaperSizePhoto4x6 = 2 +}; +``` +```objc +// MyAppPaperSupport.m +static const PaperSize PaperSizeStickyNote = 255; +``` + +(While this pattern may be unfamiliar, it is used in Apple's SDKs, though not often.) + +Therefore, enums imported from C will be treated conservatively: an otherwise-unannotated `NS_ENUM` will be imported as `nonexhaustive` and treated as such in all contexts. The newly-added C attribute `enum_extensibility` can be used to override this behavior: + +```objc +typedef NS_ENUM(NSInteger, GregorianMonth) { + GregorianMonthJanuary = 1, + GregorianMonthFebruary, + GregorianMonthMarch, + GregorianMonthApril, + GregorianMonthMay, + GregorianMonthJune, + GregorianMonthJuly, + GregorianMonthAugust, + GregorianMonthSeptember, + GregorianMonthOctober, + GregorianMonthNovember, + GregorianMonthDecember, +} __attribute__((enum_extensibility(closed))); +``` + +Apple doesn't speak about future plans for its SDKs, so having an alternate form of `NS_ENUM` that includes this attribute is out of scope for this proposal. + +Apart from the effect on switches, an imported `exhaustive` enum's `init(rawValue:)` will also enforce that the case is one of those known at compile time. Imported `nonexhaustive` enums will continue to perform no checking on the raw value. + +> This section only applies to enums that Swift considers "true enums", rather than option sets or funny integer values. In the past, the only way to get this behavior was to use the `NS_ENUM` or `CF_ENUM` macros, but the presence of `enum_extensibility(closed)` *or* `enum_extensibility(open)` will instruct Swift to treat the enum as a "true enum". Similarly, the newly-added `flag_enum` C attribute can be used to signify an option set like `NS_OPTIONS`. + + + +## Source compatibility + +It is now a source-compatible change to add a case to a non-exhaustive enum. + +It is still not a source-compatible change to remove a case from an enum (exhaustive or non-exhaustive). + +It is a source-compatible change to change a non-exhaustive enum into an exhaustive enum, but not vice versa. + + +## Effect on ABI stability + +Currently, the layout of a public enum is known at compile time in both the defining library and in its clients. For a library concerned about binary compatibility, the layout of a non-exhaustive enum must not be exposed to clients, since the library may choose to add a new case that does not fit in that layout in its next release. + +This change does not affect the layout of `@objc` enums, which always have the same representation as a similarly-defined C enum. For enums with raw types, a 32-bit integer can be used as the representation rather than a fully opaque value, on the grounds that 4 billion is a reasonable upper limit for the number of distinct cases in an enum without payloads. (Note that the representation of a non-`@objc` enum's case may differ from its raw value; this improves the efficiency of `switch` statements when all cases are known at compile time.) + +These considerations should not affect libraries shipped with their clients, including SwiftPM packages. In these cases, the compiler is always free to optimize based on the layout of an enum because the library won't change. + + +## Effect on Library Evolution + +It is now a binary-compatible change to add a case to a non-exhaustive enum. + +It is still not a binary-compatible change to remove a case from an enum (exhaustive or non-exhaustive). + +The proposed specialized representation for enums with raw types means that adding or removing a raw type is not a binary-compatible change. + +It is not a binary-compatible change to add `@objc` to an enum, nor to remove it. + +Taking an existing non-exhaustive enum and making it exhaustive is something we'd like to support without breaking binary compatibility, but there is no design for that yet. The reverse will not be allowed. + + +## Future Direction: Non-Public Cases + +The work required for non-exhaustive enums also allows for the existence of non-public cases in a public enum. This already shows up in practice in Apple's SDKs, as described briefly in the section on "C Enums" above. Like "enum inheritance", this kind of behavior can mostly be emulated by using a second enum inside the library, but that's not sufficient if the non-public values need to be vended opaquely to clients. + + +## Alternatives considered + +### Syntax + +#### Naming: "closed" and "open" + +The original description of the problem used "closed" and "open" to describe exhaustive and non-exhaustive enums, respectively. However, this conflicts with the use of `open` in classes and their members. In this usage, `open` is clearly a greater level of access than `public`, in that clients of an `open` class can do everything they can with a `public` class and more; it is source-compatible to turn a `public` class into an `open` one. For enums, however, it is exhaustive enums that are "greater": you can do everything you can with a non-exhaustive enum and more, and it is source-compatible to turn a non-exhaustive enum into an exhaustive one (at the cost of a warning). + + +#### Naming: Other options + +Several more options were suggested during initial discussions: + +- `complete` / `incomplete` +- `covered` / ? +- **`exhaustive` / `nonexhaustive`** +- ? / `extensible` +- `final` / `nonfinal` +- `finite` / `nonfinite` (note: not "infinite") +- `fixed` / ? +- `locked` / ? +- `total` / `partial` + +I don't have a strong preference for any particular choice as long as it *isn't* "closed" / "open", for the reasons described above. I picked `exhaustive` and `nonexhaustive` because they match the names proposed [in Rust][] and [in C++][], but they are a little long. (Unfortunately, Clang's `enum_extensibility` attribute, recently added by us at Apple, uses `open` and `closed`.) + +Note that "extensible" does have one problem: Apple already uses [`NS_TYPED_EXTENSIBLE_ENUM `][NS_TYPED_EXTENSIBLE_ENUM] to refer to enum-like sets of constants (usually strings) that *clients* can add "cases" to. That's not the same meaning as the exhaustiveness discussed in this proposal. + + [in Rust]: https://github.com/rust-lang/rust/issues/44109 + [in C++]: http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0375r0.html + [NS_TYPED_EXTENSIBLE_ENUM]: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/doc/uid/TP40014216-CH8-ID206 + + +#### Modifier or attribute? + +This proposal suggests new *modifiers* for enums, `nonexhaustive` and `exhaustive`; they could also be attributes `@nonexhaustive` and `@exhaustive`. I went with a modifier because most attributes only affect the *definition* of an API, not its use. This isn't a hard rule, but it's a good place to start. + + +#### Modifier or member? + +In addition to the modifier approach detailed in this proposal, discussion on swift-evolution also suggested mirroring the form of a `switch` statement by using an additional kind of declaration inside an enum: + +```swift +public enum HomeworkExcuse { + case eatenByPet + case thoughtItWasDueNextWeek + default // NEW +} +``` + +`continue` and `final` were also suggested for this additional declaration. I'm not inherently against this approach, but it does seem a little harder to spot when looking at the generated interface for a library. Unless it receives significant acclaim over the modifier approach, I'm inclined to stick with the simpler thing. + + +### Default to non-exhaustive + +My initial approach was to make `public` enums non-exhaustive by default, and require a library author to opt in to being exhaustive. However, this would be a source-breaking change, and therefore a source of significant confusion in updating from Swift 4 to Swift 5. + + +### Preserve exhaustiveness diagnostics for non-exhaustive enums + +In the initial discussion, multiple people were unhappy with the loss of compiler warnings for switches over non-exhaustive enums that comes with using `default`—they wanted to be able to handle all cases that exist today, and have the compiler tell them when new ones were added. Ultimately I decided not to include this in the proposal with the expectation is that switches over non-exhaustive enums should be uncommon. + +There were two suggestions for this, described below. Both are additive features that could be added to the language later even if we decide to leave them out now. + +#### `future` cases + +```swift +switch excuse { +case .eatenByPet: + // … +case .thoughtItWasDueNextWeek: + // … +future: + // … +} +``` + +Like `default`, the `future` case would be executed if none of the other cases match; unlike `default`, the compiler would still warn you if you failed to account for all existing cases. However, this results in some of your code being *impossible to test,* since you can't write a test that passes an unknown value to this switch. This may be true in practice with a `default` case, but it's not expected to be the common case for non-exhaustive enums. The expectation is that switches over non-exhaustive enums are uncommon. + +(It's also unclear how this would work with switches over more complicated patterns, although it seems reasonable to limit it to matching a single enum value.) + + +#### `switch!` + +```swift +switch! excuse { +case .eatenByPet: + // … +case .thoughtItWasDueNextWeek: + // … +} +``` + +`switch!` is a more limited form than `future`, which does not support any action other than trapping when the enum is not one of the known cases. This avoids some of the problems with `future` (such as making it much less important to test), but isn't exactly in the spirit of non-exhaustive enums, where you *know* there will be more cases in the future. It's also still added complexity for the language. + + + +### "Can there be a kind of open enum where you can add new cases in extensions?" + +There is no push to allow adding new cases to an enum from *outside* a library. This use case (no pun intended) is more appropriate for a RawRepresentable struct, where the library defines some initial values as static properties. (You can already switch over struct values in Swift as long as they are Equatable.) From 82dd07a9a03f0493878ce7b0899d70607ec30d34 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Wed, 13 Sep 2017 12:07:32 -0700 Subject: [PATCH 0510/4563] Non-exhaustive enums: update for feedback - Default to 'nonexhaustive' in Swift 5 mode, instead of having no default. - Mention Brent Royal-Gordon's '#invalid' idea. - Clean up typography. --- proposals/nnnn-non-exhaustive-enums.md | 47 +++++++++++++++++++------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/proposals/nnnn-non-exhaustive-enums.md b/proposals/nnnn-non-exhaustive-enums.md index 15792d7f0b..5b33a6e372 100644 --- a/proposals/nnnn-non-exhaustive-enums.md +++ b/proposals/nnnn-non-exhaustive-enums.md @@ -17,7 +17,7 @@ ## Introduction -Currently, adding a new case to an enum is a source-breaking change, which is very inconvenient for library authors. This proposal aims to distinguish between enums that are _exhaustive_ (meaning they will never get any new cases) and those that are _non-exhaustive,_ and to ensure that clients handle any future cases when dealing with the latter. This declaration only affects clients from outside the original module. +Currently, adding a new case to an enum is a source-breaking change, which is very inconvenient for library authors. This proposal aims to distinguish between enums that are _exhaustive_ (meaning they will never get any new cases) and those that are _non-exhaustive,_ and to ensure that clients handle any future cases when dealing with the latter. This change only affects clients from outside the original module. swift-evolution thread: [Enums and Source Compatibility](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170807/038663.html) @@ -45,7 +45,7 @@ To see how this distinction will play out in practice, I investigated the public ## Proposed solution -Public enums can be declared as `exhaustive` or as `nonexhaustive`, defaulting to `exhaustive` for consistency with Swift 4.0 and earlier. +Public enums can be declared as `exhaustive` or as `nonexhaustive`. In Swift 4 mode, the default behavior will be `exhaustive` for source compatibility; in Swift 5 it will be `nonexhaustive`. When a client tries to switch over a `nonexhaustive` enum, they must include a `default` case unless the enum is declared in the same module as the switch. In Swift 4 mode, omitting this case will result in a warning; in Swift 5, it will be an error. @@ -73,11 +73,11 @@ public exhaustive enum GregorianWeekday { } ``` -A public enum can now be declared `nonexhaustive` or `exhaustive`, with `exhaustive` as the default. (This is a context-sensitive keyword, like `final`.) In Swift 5 the compiler will warn if a public enum does not have either modifier; since it affects how the enum is used outside the library, it should be a conscious decision. +A public enum can now be declared `nonexhaustive` or `exhaustive`. (This is a context-sensitive keyword, like `final`.) The default behavior is `exhaustive` in Swift 4 mode and `nonexhaustive` in Swift 5; there is further discussion of these defaults in the "Default behavior" section below. A warning is emitted when using either keyword on a non-public enum, since they have no effect within a module. -The naming and spelling of these annotations is discussed in the "Alternatives Considered" section at the end of this proposal. +The naming and spelling of these annotations is discussed in the "Alternatives considered" section at the end of this proposal. ### Use-side @@ -114,10 +114,27 @@ case (_, false): This switch handles all *known* patterns, but still doesn't account for the possibility of a new enum case when the second tuple element is `true`. This should be an error in Swift 5 and a warning in Swift 4, like the first example. -The consequences of losing exhaustiveness checking for `nonexhaustive` enums are discussed in the "Alternatives Considered" section at the end of this proposal. +The consequences of losing exhaustiveness checking for `nonexhaustive` enums are discussed in the "Alternatives considered" section at the end of this proposal. -### C Enums +### Default behavior + +Making `nonexhaustive` the default behavior was not a lightly-made decision. There are two obvious alternatives here: leave `exhaustive` as the default, and have *no* default, at least in Swift 5 mode. An earlier version of this proposal went with the latter, but got significant pushback for making public enums more complicated than just adding `public`. This argues for having *some* default. + +The use cases for public enums fall into three main categories: + +| Use Case | Exhaustive | Non-exhaustive | +|--------------------------------|--------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| +| Multi-module app | The desired behavior. Compiler can find all clients if the enum becomes non-exhaustive. | Compiler can find all clients if the enum becomes exhaustive. | +| Open-source library (SwiftPM) | Changing to non-exhaustive is a source-breaking change; it produces errors in any clients. | Changing to exhaustive produces warnings in any clients. | +| ABI-stable library (Apple OSs) | **Cannot** change to non-exhaustive; it would break binary compatibility. | Changing to exhaustive produces warnings in clients (probably dependent on deployment target). | + +Although multi-module apps are likely responsible for most uses of `public`, they also provide the environment in which it is easiest to make changes, since both the "library" and the "client" are part of the same project. For actual libraries, `nonexhaustive` is a much better place to start; if it is a mistake, a minor release of the library can fix the issue without requiring immediate source changes in clients. + +Defaulting to `nonexhaustive` in Swift 5 is effectively a language change from Swift 4, where all enums were treated as exhaustive. This does require care when manually migrating code from Swift 4 to Swift 5, or when copying existing example code from online into a Swift 5 module. However, this still only affects situations where an enum is (1) public and (2) switched over (3) from another module, and even when this *does* occur it is still reasonable to fix. + + +### C enums Enums imported from C are a bit trickier, because it's difficult to tell whether they're part of the current project or not. An `NS_ENUM` in Apple's SDK should probably be treated as non-exhaustive, but one in your own framework might be exhaustive. Even there, though, it's possible that there's a "private case" defined in a .m file: @@ -194,9 +211,10 @@ It is not a binary-compatible change to add `@objc` to an enum, nor to remove it Taking an existing non-exhaustive enum and making it exhaustive is something we'd like to support without breaking binary compatibility, but there is no design for that yet. The reverse will not be allowed. -## Future Direction: Non-Public Cases +## Future direction: non-public cases + +The work required for non-exhaustive enums also allows for the existence of non-public cases in a public enum. This already shows up in practice in Apple's SDKs, as described briefly in the section on "C enums" above. Like "enum inheritance", this kind of behavior can mostly be emulated by using a second enum inside the library, but that's not sufficient if the non-public values need to be vended opaquely to clients. -The work required for non-exhaustive enums also allows for the existence of non-public cases in a public enum. This already shows up in practice in Apple's SDKs, as described briefly in the section on "C Enums" above. Like "enum inheritance", this kind of behavior can mostly be emulated by using a second enum inside the library, but that's not sufficient if the non-public values need to be vended opaquely to clients. ## Alternatives considered @@ -251,11 +269,6 @@ public enum HomeworkExcuse { `continue` and `final` were also suggested for this additional declaration. I'm not inherently against this approach, but it does seem a little harder to spot when looking at the generated interface for a library. Unless it receives significant acclaim over the modifier approach, I'm inclined to stick with the simpler thing. -### Default to non-exhaustive - -My initial approach was to make `public` enums non-exhaustive by default, and require a library author to opt in to being exhaustive. However, this would be a source-breaking change, and therefore a source of significant confusion in updating from Swift 4 to Swift 5. - - ### Preserve exhaustiveness diagnostics for non-exhaustive enums In the initial discussion, multiple people were unhappy with the loss of compiler warnings for switches over non-exhaustive enums that comes with using `default`—they wanted to be able to handle all cases that exist today, and have the compiler tell them when new ones were added. Ultimately I decided not to include this in the proposal with the expectation is that switches over non-exhaustive enums should be uncommon. @@ -294,6 +307,14 @@ case .thoughtItWasDueNextWeek: `switch!` is a more limited form than `future`, which does not support any action other than trapping when the enum is not one of the known cases. This avoids some of the problems with `future` (such as making it much less important to test), but isn't exactly in the spirit of non-exhaustive enums, where you *know* there will be more cases in the future. It's also still added complexity for the language. +### Testing invalid cases + +Another issue with non-exhaustive enums is that clients cannot properly test what happens when a new case is introduced, almost by definition. Brent Royal-Gordon came with the idea to have a new type annotation that would allow the creation of an invalid enum value. Since this is only something to use for testing, the initial version of the idea used `@testable` as the spelling for the annotation. The tests could then use a special expression, `#invalid`, to pass this invalid value to a function with a `@testable` enum parameter. + +However, this would only work in cases where the action to be taken does not actually depend on the enum value. If it needs to be passed to the original library that owns the enum, or used with an API that is not does not have this annotation, the code still cannot be tested properly. + +This is an additive feature, so we can come back and consider it in more detail even if we leave it out of the language for now. Meanwhile, the effect can be imitated using an Optional or ImplicitlyUnwrappedOptional parameter. + ### "Can there be a kind of open enum where you can add new cases in extensions?" From c3c2ff8ecf34be0ee2e12990690dd315a9abfff8 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Fri, 15 Sep 2017 10:49:44 -0700 Subject: [PATCH 0511/4563] Further feedback for "Non-exhaustive enums" - Add a source example of an unannotated public enum, to make it clear that this is legal. - Treat enums as exhaustive when they come from testably-imported modules. - Don't start warning on missing default cases for C enums in Swift 4 mode until Swift 5 is actually released. We shouldn't even introduce warnings for this in a minor version release unless someone specifically asks for the enum to be non-exhaustive. --- proposals/nnnn-non-exhaustive-enums.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/proposals/nnnn-non-exhaustive-enums.md b/proposals/nnnn-non-exhaustive-enums.md index 5b33a6e372..0f7e05d5e2 100644 --- a/proposals/nnnn-non-exhaustive-enums.md +++ b/proposals/nnnn-non-exhaustive-enums.md @@ -71,6 +71,12 @@ public exhaustive enum GregorianWeekday { case saturday case sunday } + +// Defaults to 'nonexhaustive' in Swift 5. +public enum PrinterKind { + case inkjet + case laser +} ``` A public enum can now be declared `nonexhaustive` or `exhaustive`. (This is a context-sensitive keyword, like `final`.) The default behavior is `exhaustive` in Swift 4 mode and `nonexhaustive` in Swift 5; there is further discussion of these defaults in the "Default behavior" section below. @@ -95,6 +101,8 @@ case thoughtItWasDueNextWeek: In Swift 5, this would be an error. To maintain source compatibility, this would only produce a warning in Swift 4 mode. The Swift 4 program will trap at run time if an unknown enum case is actually encountered. +To simplify a common use case, enums from modules imported as `@testable` will always be treated as exhaustive as well. + All other uses of enums (`if case`, creation, accessing members, etc) do not change. Only the exhaustiveness checking of switches is affected by `exhaustive` and `nonexhaustive`, and then only across module boundaries. Non-exhaustive switches over `exhaustive` enums (and boolean values) will continue to be invalid in all language modes. > Note: Once Swift supports cross-module inlinable functions, switch statements in such functions will also need to provide a catch-all case, even for non-exhaustive enums declared in the same module. @@ -174,6 +182,8 @@ typedef NS_ENUM(NSInteger, GregorianMonth) { Apple doesn't speak about future plans for its SDKs, so having an alternate form of `NS_ENUM` that includes this attribute is out of scope for this proposal. +This change will affect code *even in Swift 4 mode* (although it will only produce warnings there), so to ease the transition otherwise-unannotated C enums will continue to be `exhaustive` until Swift 5 is released. That is, all Swift 4.x compilers will treat unannotated `NS_ENUM` declarations as `exhaustive`; a Swift 5 compiler with a Swift 4 mode will treat them as `nonexhaustive`. + Apart from the effect on switches, an imported `exhaustive` enum's `init(rawValue:)` will also enforce that the case is one of those known at compile time. Imported `nonexhaustive` enums will continue to perform no checking on the raw value. > This section only applies to enums that Swift considers "true enums", rather than option sets or funny integer values. In the past, the only way to get this behavior was to use the `NS_ENUM` or `CF_ENUM` macros, but the presence of `enum_extensibility(closed)` *or* `enum_extensibility(open)` will instruct Swift to treat the enum as a "true enum". Similarly, the newly-added `flag_enum` C attribute can be used to signify an option set like `NS_OPTIONS`. From 88e1ab16b1f9ddedc79436536d22f7c8f856f0f8 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Fri, 29 Sep 2017 14:49:03 -0700 Subject: [PATCH 0512/4563] Yet further feedback for "Non-exhaustive enums" - Add a "Comparison with other languages" section. - Clarify up front that this only affects cross-module public enums. - Mention the possibility of a compatibility checker. - Link to Rex Fenley's counter-argument in favor of exhaustive-by-default. - Include a code example for why `#invalid` isn't sufficient for testing default cases. - Include "sealed"/"nonsealed" as a possible spelling (cf. Scala). - Acknowledge "Drop `nonexhaustive`" as a considered alternative. - Remove references to a "C++ proposal" for a "non-exhaustive" annotation. This one paper from last year hasn't gone anywhere yet. - Link to the implementation PR. --- proposals/nnnn-non-exhaustive-enums.md | 76 ++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/proposals/nnnn-non-exhaustive-enums.md b/proposals/nnnn-non-exhaustive-enums.md index 0f7e05d5e2..08b8818b6f 100644 --- a/proposals/nnnn-non-exhaustive-enums.md +++ b/proposals/nnnn-non-exhaustive-enums.md @@ -4,7 +4,8 @@ * Authors: [Jordan Rose](https://github.com/jrose-apple) * Review Manager: TBD * Status: **Awaiting review** -* Pull Request: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) +* Pull Request: [apple/swift#11961](https://github.com/apple/swift/pull/11961) +* Pre-review discussion: [Enums and Source Compatibility](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170807/038663.html), with additional [orphaned thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170911/039787.html) - - ## Introduction We propose to deprecate the controversial version of a `Sequence.flatMap` method From 591ddf4fba1cce3d13bac49b6b0c024ab349128d Mon Sep 17 00:00:00 2001 From: Jacob Bandes-Storch Date: Sat, 6 Jan 2018 22:37:27 -0800 Subject: [PATCH 0533/4563] Deriving collections of enum cases (#114) * Proposal for deriving collections of enum cases * Make the derived implementation opt-in --- .../0000-derived-collection-of-enum-cases.md | 296 ++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 proposals/0000-derived-collection-of-enum-cases.md diff --git a/proposals/0000-derived-collection-of-enum-cases.md b/proposals/0000-derived-collection-of-enum-cases.md new file mode 100644 index 0000000000..ceac51994d --- /dev/null +++ b/proposals/0000-derived-collection-of-enum-cases.md @@ -0,0 +1,296 @@ +# Derived Collection of Enum Cases + +* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/0000-derived-collection-of-enum-cases.md) +* Author(s): [Jacob Bandes-Storch](https://github.com/jtbandes), [Brent Royal-Gordon](https://github.com/brentdax), [Robert Widmann](https://github.com/CodaFi) +* Status: **Awaiting implementation** +* Review manager: TBD + +## Introduction + +> *It is a truth universally acknowledged, that a programmer in possession of an `enum` with many cases, must eventually be in want of dynamic enumeration over them.* + +[Enumeration types](enums) without associated values (henceforth referred to as "*simple enums*") have a finite, fixed number of cases, yet working with them programmatically is challenging. It would be natural to enumerate all the cases, count them, determine the highest `rawValue`, or produce a Collection of them. However, despite the fact that both the Swift compiler and the Swift runtime are aware of this information, there is no safe and sanctioned way for users to retrieve it. Users must resort to various [workarounds](#workarounds) in order to iterate over all cases of a simple enum. + +This topic was brought up [three][se1] [different][se2] [times][se3] in just the first two months of swift-evolution's existence. It was the [very first language feature request][SR-30] on the Swift bug tracker. It's a [frequent][so1] [question][so2] on Stack Overflow (between them, these two questions have over 400 upvotes and 60 answers). It's a [popular][nateblog] [topic][ericablog] on blogs. It is one of just eight [examples][sourcery] shipped with Sourcery. + +We propose the introduction of a protocol, `ValueEnumerable`, to indicate that a type has a finite, enumerable set of values. Moreover, we propose an opt-in derived implementation of `ValueEnumerable` for the common case of a simple enum. + +### Prior discussion on Swift-Evolution +- [List of all Enum values (for simple enums)][se1] (December 8, 2015) +- [Proposal: Enum 'count' functionality][se2] (December 21, 2015) +- [Draft Proposal: count property for enum types][se3] (January 17, 2016) +- † [Pre-proposal: CaseEnumerable protocol (derived collection of enum cases)][se4] (January 17, 2016) +- † [ValueEnumerable protocol with derived implementation for enums][se5] (April 15, 2016) + +…more than a year passes… + +- [[Pitch] Synthesized static enum property to iterate over cases][se6] (September 8, 2017) +- † [Re-pitch: Deriving collections of enum cases][se7] (November 6, 2017) + +_**†** = a precursor to this proposal_ + +[se1]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html +[se2]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html +[se3]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html +[se4]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006876.html +[se5]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160411/015098.html +[se6]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170904/039598.html +[se7]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171106/040950.html + +[so1]: http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type +[so2]: http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum + +[enums]: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html +[SR-30]: https://bugs.swift.org/browse/SR-30 +[nateblog]: http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ +[ericablog]: http://ericasadun.com/2015/07/12/swift-enumerations-or-how-to-annoy-tom/ +[sourcery]: https://cdn.rawgit.com/krzysztofzablocki/Sourcery/master/docs/enum-cases.html + + +## Motivation + +### Use cases + +Examples online typically pose the question "How do I get all the enum cases?", or even "How do I get the count of enum cases?", without fully explaining what the code will do with that information. To guide our design, we focus on two categories of use cases: + +1. The code must greedily iterate over all possible cases, carrying out some action for each case. For example, imagine enumerating all combinations of suit and rank to build a deck of playing cards: + + ```swift + let deck = Suit.magicListOfAllCases.flatMap { suit in + (1...13).map { rank in + PlayingCard(suit: suit, rank: rank) + } + } + ``` + +2. The code must access information about all possible cases on demand. For example, imagine displaying all the cases through a lazy rendering mechanism like `UITableViewDataSource`: + + ```swift + class SuitTableViewDataSource: NSObject, UITableViewDataSource { + func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int { + return Suit.magicListOfAllCases.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) + let suit = Suit.magicListOfAllCases[indexPath.row] + cell.titleView!.text = suit.localizedName + return cell + } + } + ``` + +To limit our scope, we are primarily interested in *simple* enums—those without any associated values—although we would also like to allow more complicated enums and structs to manually participate in this mechanism. + +The second use case suggests that access by contiguous, zero-based integer index is important for at least some uses. At minimum, it should be easy to construct an `Array` from the list of cases, but ideally the list could be used like an array directly, at least for simple enums. + + +### Workarounds + +The most basic approach to producing a collection of all cases is by manual construction: + +```swift +enum Attribute { + case date, name, author +} +protocol Entity { + func value(for attribute: Attribute) -> Value +} +// Cases must be listed explicitly: +[Attribute.date, .name, .author].map{ entity.value(for: $0) }.joined(separator: "\n") +``` + +For `RawRepresentable` enums, users have often relied on iterating over the known (or assumed) allowable raw values: + +*Excerpt from Nate Cook's post, [Loopy, Random Ideas for Extending "enum"][nateblog] (October 2014):* + +```swift +enum Reindeer: Int { + case Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donner, Blitzen, Rudolph +} +extension Reindeer { + static var allCases: [Reindeer] { + var cur = 0 + return Array( + GeneratorOf { + return Reindeer(rawValue: cur++) + } + ) + } + static var caseCount: Int { + var max: Int = 0 + while let _ = self(rawValue: ++max) {} + return max + } + static func randomCase() -> Reindeer { + // everybody do the Int/UInt32 shuffle! + let randomValue = Int(arc4random_uniform(UInt32(caseCount))) + return self(rawValue: randomValue)! + } +} +``` + +Or creating the enums by `unsafeBitCast` from their hashValue, which is assumed to expose their memory representation: + +*Excerpt from Erica Sadun's post, [Swift: Enumerations or how to annoy Tom][ericablog], with full implementation in [this gist](https://gist.github.com/erica/dd1f6616b4124c588cf7) (July 12, 2015):* + +```swift +static func fromHash(hashValue index: Int) -> Self { + let member = unsafeBitCast(UInt8(index), Self.self) + return member +} + +public init?(hashValue hash: Int) { + if hash >= Self.countMembers() {return nil} + self = Self.fromHash(hashValue: hash) +} +``` + +Or using a `switch` statement, making it a compilation error to forget to add a case, as with Dave Sweeris's [Enum Enhancer](https://github.com/TheOtherDave/EnumEnhancer) (which includes some extra functionality to avoid the boilerplate required for `.cases` and `.labels`): + +```swift +enum RawValueEnum : Int, EnumeratesCasesAndLabels { + static let enhancer:EnumEnhancer = EnhancedGenerator { + // `$0` is a RawValueEnum? + switch $0 { + case .none: $0 = .zero + case .some(let theCase): + switch theCase { + case .zero: $0 = .one + case .one: $0 = nil + } + } + } + case zero = 0 + case one = 1 +} +``` + +There are many problems with these existing techniques: + +- They are ad-hoc and can't benefit every enum type without duplicated code. +- They are not standardized across codebases, nor provided automatically by libraries such as Foundation and {App,UI}Kit. +- They are dangerous at worst, bug-prone in most cases (such as when enum cases are added, but the user forgets to update a hard-coded static collection), and awkward at best. + +### Resilience implications + +This last point is especially important as we begin to concern ourselves with library resilience. Future versions of Swift should allow library authors to add and deprecate public enum cases without breaking binary compatibility. But if clients are manually constructing arrays of "all cases", those arrays will not correspond to the version of the library they are running against. + +At the same time, the principle that libraries ought to control the promises they make should apply to any case-listing feature. Participation in a "list all cases" mechanism should be optional and opt-in. + +### Precedent in other languages + +- Rust does not seem to have a solution for this problem. + +- C#'s Enum has several [methods](https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx) available for reflection, including `GetValues()` and `GetNames()`. + +- Java [implicitly declares](http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3) a static `values()` function, returning an array of enum values, and `valueOf(String name)` which takes a String and returns the enum value with the corresponding name (or throws an exception). More examples [here](http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3). + +- The Template Haskell extension to Haskell provides a function `reify` which extracts [info about types](http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info), including their constructors. + +## Proposed solution + +We propose introducing a `ValueEnumerable` protocol to the Swift Standard Library. The compiler will derive an implementation automatically for simple enums when the conformance is specified. + +```swift +enum Ma: ValueEnumerable { case 马, 吗, 妈, 码, 骂, 麻, 🐎, 🐴 } + +Ma.allValues // returns some Collection whose Iterator.Element is Ma +Ma.allValues.count // returns 8 +Array(Ma.allValues) // returns [Ma.马, .吗, .妈, .码, .骂, .麻, .🐎, .🐴] +``` + +## Detailed design + +- The `ValueEnumerable` protocol will have the following declaration: + + ```swift + public protocol ValueEnumerable { + associatedtype ValueCollection: Collection where ValueCollection.Element == Self + static var allValues: ValueCollection { get } + } + ``` + +- The compiler will synthesize an implementation of ValueEnumerable for an enum type if and only if: + + - the enum contains only cases without associated values; + - the enum is not `@objc`; + - the enum has an explicit `ValueEnumerable` conformance (and does not fulfil the protocol's requirements). + +- Enums **imported from C/Obj-C headers** have no runtime metadata, and thus will not participate in the derived ValueEnumerable conformance, given the proposed implementation. + +- Cases marked `unavailable` or `deprecated` **will be** included in `allValues`, because they are present in the metadata. (Note that `switch` statements are already required to handle these cases.) + + +## Naming + +**tl;dr: Core team, please bikeshed!** + +The names `ValueEnumerable` / `T.allValues` were chosen for the following reasons: + +- the `-able` suffix indicates the [**capability**](https://swift.org/documentation/api-design-guidelines.html) to enumerate the type's values. +- the `all-` prefix avoids confusion with other meanings of the word "values" (plural noun; transitive verb in simple present tense). +- avoids the word "case", which might imply that the feature is `enum`-specific. +- avoids the word "finite", which is slightly obscure — and finiteness is not a necessary condition for iterating over values. + +However, this proposal isn't all-or-nothing with regards to names; final naming should of course be at the Swift team's discretion. Other alternatives considered include: + +- `T.values` +- `CaseEnumerable` / `T.cases` / `T.allCases` +- `FiniteType` +- `FiniteValueType` + +## Source compatibility + +This proposal only adds functionality, so existing code will not be affected. (The identifier `ValueEnumerable` doesn't make very many appearances in Google and GitHub searches.) + + +## Effect on ABI stability + +The proposed implementation **adds** a runtime function `swift_EnumEnumerateCaseValues` returning the number of cases in a simple enum given its metadata. Changes to user code (adding or removing a case of a simple enum) do not affect ABI. + +## Effect on API resilience + +User programs will come to rely on the `ValueEnumerable` protocol and its `allValues` and `ValueCollection` requirements. Due to the use of an associated type for the property's type, the derived implementation is free to change the concrete Collection it returns without breaking the API. + + +## Implementation + +In 2016, Robert Widmann put together a [sample implementation](https://github.com/apple/swift/commit/b8e6e9ff2e59c4834933dcd0fa7dd537fb25caaa) of CaseEnumerable. It provides a runtime function to read the number of cases from the enum type's metadata, and uses `Builtin.reinterpretCast` to convert each value to the enum type. + + +## Alternatives considered + +The community has not raised any solutions whose APIs differ significantly from this proposal, except for solutions which provide strictly **more** functionality. + +The functionality could also be provided entirely through the `Mirror`/reflection APIs. This would likely result in much more obscure and confusing usage patterns. + +Unavailable and deprecated cases could be excluded from `allValues`. This would require modifications to metadata or an implementation that does not rely on metadata. + +#### Provide a default collection + +Declaring this in the Standard Library reduces the amount of compiler magic required +to implement the protocol. However, it also exposes a public unsafe entrypoint to the +reflection API that we consider unacceptable. + +```swift +extension ValueEnumerable where ValueCollection == DefaultCaseCollection { + public static var allValues: DefaultCaseCollection { + return DefaultCaseCollection(unsafeForEnum: Self.self) + } +} + +public struct DefaultCaseCollection: RandomAccessCollection { + public var startIndex: Int { return 0 } + public let endIndex: Int + + public init(unsafeForEnum _: Enum.Type) { + endIndex = _countCaseValues(Enum.self) + } + + public subscript(i: Int) -> Enum { + precondition(indices.contains(i), "Case index out of range") + return Builtin.reinterpretCast(i) as Enum + } +} +``` From 8e0bf62c04e4d7de3bc04e492011620cec2be2c7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 6 Jan 2018 22:42:28 -0800 Subject: [PATCH 0534/4563] Initiate review of SE-0194: Derived Collection of Enum Cases --- ...um-cases.md => 0194-derived-collection-of-enum-cases.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{0000-derived-collection-of-enum-cases.md => 0194-derived-collection-of-enum-cases.md} (98%) diff --git a/proposals/0000-derived-collection-of-enum-cases.md b/proposals/0194-derived-collection-of-enum-cases.md similarity index 98% rename from proposals/0000-derived-collection-of-enum-cases.md rename to proposals/0194-derived-collection-of-enum-cases.md index ceac51994d..1182a3cd4b 100644 --- a/proposals/0000-derived-collection-of-enum-cases.md +++ b/proposals/0194-derived-collection-of-enum-cases.md @@ -1,9 +1,9 @@ # Derived Collection of Enum Cases -* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/master/proposals/0000-derived-collection-of-enum-cases.md) +* Proposal: [SE-0194](https://github.com/apple/swift-evolution/blob/master/proposals/0194-derived-collection-of-enum-cases.md) * Author(s): [Jacob Bandes-Storch](https://github.com/jtbandes), [Brent Royal-Gordon](https://github.com/brentdax), [Robert Widmann](https://github.com/CodaFi) -* Status: **Awaiting implementation** -* Review manager: TBD +* Status: **Active Review (January 6, 2017...January 11, 2018)** +* Review manager: [Doug Gregor](https://github.com/DougGregor) ## Introduction From def13f172e28d0966bb73b86613c236e9545292f Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 7 Jan 2018 13:26:51 +0000 Subject: [PATCH 0535/4563] [SE-0194] Fix date range, add implementation link --- proposals/0194-derived-collection-of-enum-cases.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/proposals/0194-derived-collection-of-enum-cases.md b/proposals/0194-derived-collection-of-enum-cases.md index 1182a3cd4b..9146e47b47 100644 --- a/proposals/0194-derived-collection-of-enum-cases.md +++ b/proposals/0194-derived-collection-of-enum-cases.md @@ -1,15 +1,16 @@ # Derived Collection of Enum Cases -* Proposal: [SE-0194](https://github.com/apple/swift-evolution/blob/master/proposals/0194-derived-collection-of-enum-cases.md) -* Author(s): [Jacob Bandes-Storch](https://github.com/jtbandes), [Brent Royal-Gordon](https://github.com/brentdax), [Robert Widmann](https://github.com/CodaFi) -* Status: **Active Review (January 6, 2017...January 11, 2018)** -* Review manager: [Doug Gregor](https://github.com/DougGregor) +* Proposal: [SE-0194](0194-derived-collection-of-enum-cases.md) +* Authors: [Jacob Bandes-Storch](https://github.com/jtbandes), [Brent Royal-Gordon](https://github.com/brentdax), [Robert Widmann](https://github.com/CodaFi) +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (January 6...11)** +* Implementation: [apple/swift#13655](https://github.com/apple/swift/pull/13655) ## Introduction > *It is a truth universally acknowledged, that a programmer in possession of an `enum` with many cases, must eventually be in want of dynamic enumeration over them.* -[Enumeration types](enums) without associated values (henceforth referred to as "*simple enums*") have a finite, fixed number of cases, yet working with them programmatically is challenging. It would be natural to enumerate all the cases, count them, determine the highest `rawValue`, or produce a Collection of them. However, despite the fact that both the Swift compiler and the Swift runtime are aware of this information, there is no safe and sanctioned way for users to retrieve it. Users must resort to various [workarounds](#workarounds) in order to iterate over all cases of a simple enum. +[Enumeration types][enums] without associated values (henceforth referred to as "*simple enums*") have a finite, fixed number of cases, yet working with them programmatically is challenging. It would be natural to enumerate all the cases, count them, determine the highest `rawValue`, or produce a Collection of them. However, despite the fact that both the Swift compiler and the Swift runtime are aware of this information, there is no safe and sanctioned way for users to retrieve it. Users must resort to various [workarounds](#workarounds) in order to iterate over all cases of a simple enum. This topic was brought up [three][se1] [different][se2] [times][se3] in just the first two months of swift-evolution's existence. It was the [very first language feature request][SR-30] on the Swift bug tracker. It's a [frequent][so1] [question][so2] on Stack Overflow (between them, these two questions have over 400 upvotes and 60 answers). It's a [popular][nateblog] [topic][ericablog] on blogs. It is one of just eight [examples][sourcery] shipped with Sourcery. From 814cdd15420d7f0c0a04f2c8fa182c6dfb36a693 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Sun, 7 Jan 2018 21:24:19 -0800 Subject: [PATCH 0536/4563] [Status] Fix alignment of details for proposals with implementations. --- index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.css b/index.css index 1a1b1a1998..c62e024cc4 100644 --- a/index.css +++ b/index.css @@ -271,7 +271,7 @@ section ul, section li { width: 223px; } -.bug-list, .implementation-list { +.bug-list { width: 310px; } From 3531c2d788bcf24cd367e0a9840796e224b7305c Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 9 Jan 2018 01:49:45 +0000 Subject: [PATCH 0537/4563] [SE-0187] Change to "Implemented (Swift 4.1)" --- proposals/0187-introduce-filtermap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0187-introduce-filtermap.md b/proposals/0187-introduce-filtermap.md index 944d9ee1e9..73774d68f2 100644 --- a/proposals/0187-introduce-filtermap.md +++ b/proposals/0187-introduce-filtermap.md @@ -3,7 +3,7 @@ * Proposal: [SE-0187](0187-introduce-filtermap.md) * Authors: [Max Moiseev](https://github.com/moiseev) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Implemented (Swift 5)** +* Status: **Implemented (Swift 4.1)** * Implementation: [apple/swift#12819](https://github.com/apple/swift/pull/12819) * Decision Notes: [Review #0](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171023/040609.html), From d7dbf97b0ca0b70f9e27fbb02118754d4524d9c0 Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Wed, 10 Jan 2018 01:38:40 +0900 Subject: [PATCH 0538/4563] [SE-0075] Improve indentation --- proposals/0075-import-test.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/0075-import-test.md b/proposals/0075-import-test.md index 579cd5a6ef..d25dfe0041 100644 --- a/proposals/0075-import-test.md +++ b/proposals/0075-import-test.md @@ -25,11 +25,11 @@ Swift's existing set of build configurations specify platform differences, not m ```swift #if canImport(UIKit) - // UIKit-based code - #elseif canImport(Cocoa) - // OSX code - #elseif - // Workaround/text, whatever + // UIKit-based code +#elseif canImport(Cocoa) + // OSX code +#elseif + // Workaround/text, whatever #endif ``` @@ -38,7 +38,7 @@ Guarding code with operating system tests can be less future-proofed than testin ```swift // Exclusive os tests are brittle #if !os(Linux) - // Matches OSX, iOS, watchOS, tvOS, Windows, FreeBSD + // Matches OSX, iOS, watchOS, tvOS, Windows, FreeBSD #endif ``` @@ -73,7 +73,7 @@ frameworks at runtime is to do it via Obj-C. Some sort of check like the ones yo #if canImport(module) // provide solution with module APIs - #else +#else // provide alternative solution that does not depend on that module #endif ``` From 19cff04ceca13587d7819abf07f26c2106ce994b Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Wed, 10 Jan 2018 04:42:24 +0900 Subject: [PATCH 0539/4563] [SE-0190] Add decision notes (#780) --- proposals/0190-target-environment-platform-condition.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0190-target-environment-platform-condition.md b/proposals/0190-target-environment-platform-condition.md index c7431f0224..757c06c034 100644 --- a/proposals/0190-target-environment-platform-condition.md +++ b/proposals/0190-target-environment-platform-condition.md @@ -6,6 +6,7 @@ * Status: **Implemented (Swift 4.1)** * [Swift Evolution Review Thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171127/041695.html) * Implementation: [apple/swift#12964](https://github.com/apple/swift/pull/12964) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171127/041741.html) ## Introduction From 89b86dae47e1ef549a78e66c1874caf3ad36efc6 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 9 Jan 2018 21:35:43 -0800 Subject: [PATCH 0540/4563] Dynamic Member Lookup Proposal This proposes introducing a DynamicMemberLookupProtocol protocol, as a cornerstone for improving interoperability with dynamic languages. --- proposals/0195-dynamic-member-lookup.md | 790 ++++++++++++++++++++++++ 1 file changed, 790 insertions(+) create mode 100644 proposals/0195-dynamic-member-lookup.md diff --git a/proposals/0195-dynamic-member-lookup.md b/proposals/0195-dynamic-member-lookup.md new file mode 100644 index 0000000000..fbd876627b --- /dev/null +++ b/proposals/0195-dynamic-member-lookup.md @@ -0,0 +1,790 @@ +# Introduce User-defined "Dynamic Member Lookup" Types + +* Proposal: [SE-0195](0195-dynamic-member-lookup.md) +* Author: [Chris Lattner](https://github.com/lattner) +* Review Manager: TBD +* Implementation: [PR13361](https://github.com/apple/swift/pull/13361) +* Status: **Pending Review** + +## Introduction + +This proposal introduces a new `DynamicMemberLookupProtocol` type to the standard +library. Types that conform to it provide "dot" syntax for arbitrary names which are resolved +at runtime - in a **completely type safe** way. It is simple syntactic sugar which has a +non-invasive implementation in the compiler. It allows the user to write: + +```swift + a = someValue.someMember + someValue.someMember = a + mutateParameter(&someValue.someMember) +```` + +and have it be interpreted by the compiler as: + +```swift + a = someValue[dynamicMember: "someMember"] + someValue[dynamicMember: "someMember"] = a + mutateParameter(&someValue[dynamicMember: "someMember"]) +``` + +This allows the static type of `someValue` to decide how to implement these dynamic member +references. + +Many other languages have analogous features e.g., the [dynamic](https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/dynamic-language-runtime-overview) feature in C#, the [Dynamic trait in Scala](https://blog.scalac.io/2015/05/21/dynamic-member-lookup-in-scala.html), the composition of Objective-C's +[explicit properties](https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/DeclaredProperty.html) and underlying [messaging infrastructure](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtHowMessagingWorks.html)). This sort +of functionality is great for implementing dynamic language interoperability, dynamic +[proxy APIs](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html), and other APIs (e.g. for JSON processing). + +The driving motivation for this feature is to improve interoperability with inherently dynamic +languages like Python, Javascript, Ruby and others. That said, this feature is designed such +that it can be applied to other inherently dynamic domains in a modular way. **NOTE**: if +you are generally supportive of interoperability with dynamic languages but are +concerned about the potential for abuse of this feature, please see the [Reducing Potential +Abuse](#reducing-potential-abuse) section in the Alternatives Considered section and voice +your support for one of those directions that limit the feature. + +## Motivation and Context + +Swift is well known for being exceptional at interworking with existing C and Objective-C +APIs, but its support for calling APIs written in scripting languages like Python, Perl, and Ruby +is quite lacking. + +C and Objective-C are integrated into Swift by expending a heroic amount of effort into +integrating Clang ASTs, remapping existing APIs in an attempt to feel "Swifty", and by +providing a large number of attributes and customization points for changing the behavior +of this integration when writing an Objective-C header. The end result of this massive +investment of effort is that Swift tries to provide an (arguably) *better* experience when +programming against these legacy APIs than Objective-C itself does. + +When considering the space of dynamic languages, four things are clear: 1) there are several +different languages of interest, and they each have significant communities in different areas: +for example, Python is big in data science and machine learning, Ruby is popular for building +server side apps, a few people apparently use Javascript, and even Perl is still widely used. +2) These languages have decades of library building behind them, sometimes with [significant +communities](https://pandas.pydata.org) and 3) there are one or two orders of magnitude +more users of these libraries than there are people currently using Swift. 4) APIs written in +these languages will never feel "Swifty", both because of serious differences between the +type systems of Swift and these languages, and because of runtime issues like the Python +GIL. + +In the extensive discussion and development of this feature (including hundreds of emails to +swift-evolution) we considered many different implementation approaches. These include +things like Objective-C style bridging support, F# type providers, generated wrappers, foreign +class support, and others described in the [Alternative Python Interoperability +Approaches](#alternative-python-interoperability-approaches) section. + +The conclusion of these many discussions was that it is better to embrace the fact +that these languages are inherently dynamic and meet them where they are: F# type providers +and generated wrappers require this proposal to work in the first place (because, for example, +Javascript doesn't have classes and Python doesn't have stored property declarations) and +providing a good code completion experience for dynamic languages requires incorporation +of flow-sensitive analysis into SourceKit (something that is [fully compatible](#future-directions-python-code-completion) +with this proposal). + +Given that Swift already has an intentionally incredibly syntax-extensible design, we only need +two minor enhancements to the language to support these dynamic languages in an +ergonomic way: this proposal (which introduces `DynamicMemberLookupProtocol`) and a +related +[`DynamicCallable`](https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d) +proposal. + +To show the impact of these proposals, consider this Python code: + +```Python +class Dog: + def __init__(self, name): + self.name = name + self.tricks = [] # creates a new empty list for each dog + + def add_trick(self, trick): + self.tricks.append(trick) +``` + +we would like to be able to use this from Swift like this (the comments show the +corresponding syntax you would use in Python): + +```swift + // import DogModule + // import DogModule.Dog as Dog // an alternate + let Dog = Python.import("DogModule.Dog") + + // dog = Dog("Brianna") + let dog = Dog("Brianna") + + // dog.add_trick("Roll over") + dog.add_trick("Roll over") + + // cuteDog = Dog("Kaylee").add_trick("snore") + let cuteDog = Dog("Kaylee").add_trick("snore") +``` + +Of course, this would also apply to standard Python APIs as well. Here is an example +working with the Python `pickle` API and the builtin Python function `open`: + +```swift + // import pickle + let pickle = Python.import("pickle") + + // file = open(filename) + let file = Python.open(filename) + + // blob = file.read() + let blob = file.read() + + // result = pickle.loads(blob) + let result = pickle.loads(blob) +``` + +This can all be expressed today as library functionality written in Swift, but without this +proposal, the code required is unnecessarily verbose and gross. Without it (but *with* the +related [`DynamicCallable` proposal](https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d) +the code would have explicit member lookups all over the place: + +```swift + // import pickle + let pickle = Python.get(member: "import")("pickle") + + // file = open(filename) + let file = Python.get(member: "open")(filename) + + // blob = file.read() + let blob = file.get(member: "read")() + + // result = pickle.loads(blob) + let result = pickle.get(member: "loads")(blob) + + // dog2 = Dog("Kaylee").add_trick("snore") + let dog2 = Dog("Kaylee").get(member: "add_trick")("snore") +``` + +If you'd like to explore what Python interoperability looks like with plain Swift 4 (i.e. without +either of these proposals) then check out the [Xcode 9 playground demonstrating Python +interoperability](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171204/042029.html) +that has been periodically posted to swift-evolution over the last few months. + +While this is a syntactic sugar proposal, we believe that this expands Swift to be usable in +important new domains. In addition to dynamic language interoperability, this sort of +functionality is useful for other APIs, e.g. when working with dynamically typed unstructured +data like JSON, which could provide an API like `jsonValue?.jsonField1?.jsonField2` +where each field is dynamically looked up. An example of this is shown below in the +"Example Usage" section. + +## Proposed solution + +We propose introducing a new protocol to the standard library: + +```swift +/// Types type conform to this protocol have the behavior that member lookup - +/// accessing `someval.member` will always succeed. Failures to find normally +/// declared members of `member` will be turned into subscript references using +/// the `someval[dynamicMember: member]` member. +/// +public protocol DynamicMemberLookupProtocol { + // Implementations of this protocol must have a subscript(dynamicMember:) + // implementation where the keyword type is some type that is + // ExpressibleByStringLiteral. It can be get-only or get/set which defines + // the mutability of the resultant dynamic properties. + + // subscript + // (dynamicMember name: KeywordType) -> LookupValue { get } +} +``` + +It also extends the language such that member lookup syntax (`x.y`) - when it otherwise fails +(because there is no member `y` defined on the type of `x`) and when applied to a value which +conforms to `DynamicMemberLookupProtocol` - is accepted and +transformed into a call to the subscript in the protocol. The produced value is a mutable +L-value if the type conforming to `DynamicMemberLookupProtocol` implements a mutable +subscript, or immutable otherwise. This allows the type to perform arbitrary runtime +processing to calculate the value to return. The dynamically computed property can be used +the same way as an explicitly declared computed property, including being passed `inout` if +mutable. + +The protocol is intentionally designed to be flexible: the implementation can take the member +name through any `ExpressibleByStringLiteral` type, including `StaticString` and of +course `String`. The result type may also be any type the implementation desires, including +an `Optional`, `ImplicitlyUnwrappedOptional` or some other type, which allows the +implementation to reflect dynamic failures in a way the user can be expected to process (e.g., +see the JSON example below). + +This protocol is implemented as a "marker protocol" which enables the magic name lookup +behavior, but does not have any explicitly declared requirements within its body. This is +because Swift's type system doesn't have the ability to directly express the requirements we +have: consider that subscripts can have mutating getters and nonmutating setters. These +are important to support, because it affects when and if values may be get and set through +a potentially immutable base type. Alternative implementation approaches were explored, +and are discussed in the "[Alternatives +Considered](#declare-an-explicit-subscript-requirement)" section below. It is important to +note that despite the inability for the Swift langauge to check the requirements of the protocol +itself, the implementation of this does so directly, so this is just an implementation concern. + +In the discussion cycle, there was significant concern about abuse of this feature, particularly +if someone retroactively conforms a type to `DynamicMemberLookupProtocol`. Further, +it is easy to argue that dynamic behavior is a core part of the contract of a type's behavior, +not something that should be changable retroactively. For +this reason, the compiler only permits conformance of this protocol on the original type +definition, not extensions. If for some reason there becomes a reason to relax this requirement, +we can evaluate that as a future swift-evolution proposal based on its own merits. See the +"[Alternatives Considered](#reducing-potential-abuse)" section below for further ways to +reduce potential for abuse. + + +## Example Usage + +While there are many potential uses of this sort of API one motivating example comes from a +[prototype Python interoperability layer](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171204/042029.html). +There are many ways to implement this, and the details are not +particularly important, but it is perhaps useful to know that this is directly useful to address +the motivation section described above. Given a currency type of `PyVal`, an implementation +may look like: + +```swift +struct PyVal : DynamicMemberLookupProtocol { + ... + subscript(dynamicMember member: String) -> PyVal { + get { + let result = PyObject_GetAttrString(borrowedPyObject, member)! + return PyVal(owned: result) + } + set { + PyObject_SetAttrString(borrowedPyObject, member, + newValue.borrowedPyObject) + } + } +} +``` + +Another example use are JSON libraries which represent JSON blobs as a Swift enum, e.g.: + +```swift +enum JSON { + case IntValue(Int) + case StringValue(String) + case ArrayValue(Array) + case DictionaryValue(Dictionary) +} +``` + +Today, it is not unusual for them to implement members like this to allow drilling down into +the JSON value: + +```swift +extension JSON { + var stringValue : String? { + if case .StringValue(let str) = self { + return str + } + return nil + } + subscript(index: Int) -> JSON? { + if case .ArrayValue(let arr) = self { + return index < arr.count ? arr[index] : nil + } + return nil + } + subscript(key: String) -> JSON? { + if case .DictionaryValue(let dict) = self { + return dict[key] + } + return nil + } +} +``` + +This allows someone to drill into a JSON value with code like: +`json[0]?["name"]?["first"]?.stringValue`. On the other hand, if we add a simple +conformance to `DynamicMemberLookupProtocol` like this: + +```swift +enum JSON : DynamicMemberLookupProtocol { + ... + subscript(dynamicMember member: String) -> JSON? { + if case .DictionaryValue(let dict) = self { + return dict[member] + } + return nil + } +} +``` + +Now clients are able to write more natural code like: +`json[0]?.name?.first?.stringValue` which is close to the expressivity of Javascript... +while being fully type safe! + +It is important to note that this proposal does *not* include introducing or changing an existing +JSON type to use this mechanic, we only point out that this proposal allows subsequent work +to turn this on if sagacious API authors approve of it. + + +### Future Directions: Python Code Completion + +In the extensive discussion of alternative implementation approaches, one concern raised +was whether or not we could ever get a good code completion experience with this design. +It would be nice to get at least something along the lines of what Swift provides for +`AnyObject` lookup, where you can get a "big list" and filter down quickly as you type. + +After extensive discussion at the Core Team, we concluded that the *best* way to get a good +code completion for Python APIs in Swift (if and when that becomes a priority) is to build such +functionality into SourceKit, and model it directly after the way that existing Python IDEs +provide their code completion. + +The observation is that a state of the art Python code completion experience requires +incorporating simple control flow analysis and unsound heuristics into the the model in order +to take local hints into account, pre-filtering the lists. These heuristics would be inappropriate +to include in the static type system of the Swift language (in any form), and thus we believe it +is better to build this as special support in SourceKit (e.g. special casing code completion on +`Python.PyVal` (or whatever the currency type ends up being called) to incorporate these +techniques. + +In any case, while we would like to see such future developments, but they are beyond the +scope of this proposal, and are a tooling discussion for "swift-dev", not a language evolution +discussion. + + +## Source compatibility + +This is a strictly additive proposal with no source breaking changes. + +## Effect on ABI stability + +This is a strictly additive proposal with no ABI breaking changes. + +## Effect on API resilience + +Types that conform to this protocol will always succeed at member lookup (`x.foo` will +always be accepted by the compiler): members that are explictly declared in the type or in +a visible extension will be found and referenced, and anything else will be handled by the +dynamic lookup feature. + +That behavior could lead to a surprising behavior change if the API evolves over time: adding +a new staticly declared member to the type or an extension will cause clients to resolve that +name to the static declaration instead of being dynamically dispatched. This is inherent to +this sort of feature, and means it should not be used on types that have a large amount of +API, API that is likely to change over time, or API with names that are likely to conflict. + +## Alternatives considered + +A few alternatives were considered: + +### Naming + +Suggestions for a better name for the protocol and the subscript (along with rationale to +support them) are more than welcome. + +On naming of `subscript(dynamicMember:)`, we intentionally gave it a long and verbose +names so they stay out of the way of user code completion. The members of this protocol +are really just compiler interoperability glue. If there was a Swift attribute to disable the +subscript from showing up in code completion, we would use it (such an attribute would +also be useful for the `LiteralConvertible` and other compiler magic protocols). + +### Declare an explicit subscript requirement + +We considered (and tried hard) to declare an explicit `subscript` requirement inside the +protocol, but ran into several problems: + +First, we seek to support both get-only and get/set dynamic +properties. If we tried to reflect these capabilities into the type system, we'd end up +with two protocols: `DynamicMemberLookupProtocol` and +`MutableDynamicMemberLookupProtocol`. This expands the surface area of the proposal, +and would make the implementation more complicated. + +Second, recall that getters and setters can be both `mutating` and `nonmutating`. We definitely +need the ability to represent that, but could choose to either reflect that in the requirement +signature (dramatically expanding the number of protocols) or not (make the requirement be +`mutating` for both, but allow an implementation to have a stricter implementation). Both +options could work, but neither is great. + +Third, the natural way to express the subscript requirement is with associated types, perhaps +something like this (using the simplest get-only case to illustrate the point): + +```swift +protocol DynamicMemberLookupProtocol { + associatedtype DynamicMemberLookupKeyword : ExpressibleByStringLiteral + associatedtype DynamicMemberLookupValue + subscript(dynamicMember name: DynamicMemberLookupKeyword) + -> DynamicMemberLookupValue { mutating get } +} +``` + +However, if we go this approach, then this marker protocol is now a "Protocol with +Associated Type" (PAT) which (among other things) prevents protocols that further refine the +protocol from being usable as existentials - Swift does not yet support [Generalized +Existentials](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials), +and probably will not until Swift 6 at the earliest. This also pollutes the type with the two +associated type names. + +The other attempted way to implement this was with a generic subscript, like this: + +```swift +protocol DynamicMemberLookupProtocol { + subscript + (dynamicMember name: DynamicMemberLookupKeywordType) + -> DynamicMemberLookupValue { mutating get } +} +``` + +This fixes the problem with PATs, but has the distinct disadvantage that it is impossible to +fulfill the subscript requirement with a concrete (non-generic) subscript implementation. + +### Model this with methods, instead of a labeled subscript + +It may be surprising to some that this functionality is modeled as a subscript instead of a +get/set method pair. This is intentional though: subscripts are the way that Swift supports +parameterized l-values like we're are trying to expose here. Exposing this as two methods +doesn't fit into the language as cleanly, and would make the compiler implementation more +invasive. It is better to use the existing model for l-values directly. + +### Make this be a attribute on a type, instead of a protocol conformance + +One option that came up in discussion was to model this as an attribute instead of a protocol +conformance. Instead of: + +```swift +struct PyVal : DynamicMemberLookupProtocol { ... } +``` + +we could spell this as something like: + +```swift +@dynamicMemberLookup +struct PyVal { ... } +``` + +Both of these approaches can work. The primary arguments in favor of the attribute is that +the `DynamicMemberLookupProtocol` is an unusual one in some ways: + +1) It has no formal members. +2) The requirements are enforced by the compiler, not by the type system. +3) The requirements permit and use arbitrary overloads. +4) The protocol cannot (usefully) be used in a generic context or as a type constraint. +5) This protocol can only be conformed to in the main declaration, not in an extension. + +All of those points are true, but the author of this proposal still thinks that this is better +modeled as a protocol, here are some reasons: + +1) Protocols describe *semantics* of a conforming type, and this proposal provides key + behavior to the type that conforms to it. +2) When a type uses this proposal, it provides a fundamental change to the type's behavior. + While it isn't perfectly followed, attributes generally do not have this sort of effect on a type. +3) This proposal allows a type to hook into primitive language syntax. All of the ways to do + this today are spelled with protocols (e.g. the `ExpressibleBy...` protocols. +4) Attributes are syntactically very light-weight, which makes this easier to overlook - given + the sigificant effect on a type, we prefer it to be more visible. +5) The oddities observed above may be eliminated over time if there is a reason to: for + example, there is no technical reasons that types cannot retroactively conform. It is + theoretically possible that the Swift generics system could be extended to support these + requirements, etc. +6) As Xiaodi Wu suggests, you could imagine this feature as one where conformance to the + protocol gives a default implementation of an infinite number of methods. + + + +### Reducing Potential Abuse + +In the discussion cycle, there was significant concern about abuse of this feature, particularly +if someone retroactively conforms a type to `DynamicMemberLookupProtocol`. For +this reason, the compiler only permits conformance of this protocol on the original type +definition, not extensions. + +On the other hand, the potential for abuse has never been a strong guiding principle for Swift +features (and many existing features could theoretically be abused (including operator +overloading, emoji identifiers, +`AnyObject` lookup, `ImplicitlyUnwrappedOptional`, and many more). If we look to +other language communities, we find that Objective-C has many inherently dynamic features. +Furthermore, directly +analogous "dynamic" features were [added to C#](https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/dynamic-language-runtime-overview) and Scala late in +their evolution cycle, and no one has produced evidence that they led to abuse. + +Finally, despite **extensive discussion** on the mailing list and lots of concern +about how this feature could be abused, no one has been able to produce (even one) +non-malicious example where someone would adopt this protocol inappropriately and lead to +harm for users (and of course, if you're consuming an API produced by a malicious entity, you +are already doomed. `:-)`). + +Fortunately (if and only if a compelling example of harm were demonstrated) there are many +different ways to assuage concerns of "abuse" of this feature, e.g.: + +1) We could prevent this from being a part of some other protocol definition, because any + types that conform to that protocol will transitively get the conformance. The cost of this is + that it prevents use of dynamic member lookup with existentials (which are one of the core + dynamic features of Swift), but we could relax that requirement in the future if there was a + compelling use-case for doing so. + +2) Have the compiler specifically bless individual well-known types, e.g. `Python.PyVal` (or + whatever it is eventually named) by baking in awareness of these types into the compiler. + Such an approach would require a Swift evolution proposal to add a new type that + conforms to this. + +3) We could add a redundant attribute or use some other way to make conformance to + `DynamicMemberLookupProtocol` more visible, e.g.: + +```swift +@dynamic +struct PyVal : DynamicMemberLookupProtocol {...} +``` + +If you are concerned about abuse, feel free to "+1" one of these alternatives in the review +cycle. Ideas and other suggestions for how to reduce possibility of misuse are welcome as +well. + + +### Increasing Visibility of Dynamic Member Lookups + +People have suggested that we add some explicit syntax to accesses to make the dynamic +lookup visible, e.g.: `foo.^bar` or `foo->bar` or some other punctuation character we +haven't already used. In my opinion, this is the wrong thing to do for several reasons: + +1) Swift's type system already includes features (optionals, IUOs, runtime failure) for handling + failability. Keeping that orthogonal to this proposal is good because that allows API + authors to make the right decision for expressing the needs of their use-case. +2) Swift already has a dynamic member lookup feature, "`AnyObject` dispatch" which does + not use additional punctuation, so this would break precedent. The syntax and behavior + of AnyObject dispatch was carefully considered in a situation that was directly analogous + to this - Swift 1 days - where nullability audited headers were rare. +3) Adding punctuation to the lookup itself reduces the likelihood that an API author would + make the lookups return strong optionals, because of increased syntactic noise. +4) The point of this proposal is to make use of dynamic language APIs more elegant than + what is already possible: making use of them ugly (this punctuation character would be + pervasive through use of the APIs and just add visual noise, not clarity) undermines the + entire purpose of this proposal. +5) There are already other features (including operator overloading, subscripts, forthcoming + `DynamicCallable`, etc) that are just as dynamic as property lookup when implemented + on a type like `PyVal`. Requiring additional syntax for "`a.b`" but not "`a + b`" (which can + be just as dynamic) would be inconsistent. +6) Language syntax is not the only way to handle this. IDEs like Xcode could color code + dynamic member lookups differently, making their semantics visible without adversely + affecting the code that is written. + +It probably helps to consider an example. Assume we used the `^` sigil to represent a dynamic +operation (member lookup, call, dynamic operator, etc). This would give us syntax like +`foo.^bar` for member lookup, `baz^(42)` for calls. The API author isn't forced to pick an +operator that follows this scheme, but has the option to do so. Such a design would +change reasonable code like this: + +```swift + let np = Python.import("numpy") + let x = np.array([6, 7, 8]) + let y = np.arange(24).reshape(2, 3, 4) + + let a = np.ones(3, dtype: np.int32) + let b = np.linspace(0, pi, 3) + let c = a+b + let d = np.exp(c) + print(d) +``` + +into: + +```swift + let np = Python.import("numpy") + let b = np^.array^([6, 7, 8]) + let y = np^.arange^(24)^.reshape^(2, 3, 4) + + let a = np^.ones^(3, dtype: np^.int32) + let b = np^.linspace^(0, pi, 3) + let c = a+^b + let d = np^.exp^(c) +``` + +This does not improve clarity of code, it merely serves to obfuscate logic. It is immediately +apparent from the APIs being used, the API style, and the static types (in Xcode or through +static declarations) that this is all Python stuff. When you start mixing in use of native Swift +types like dictionaries (something we want to encourage because they are typed!) you end up +with an inconsistent mismash where people would just try adding syntax or applying fixits +continuously until the code builds. + +The example gets ever worse if the implementation chooses to return a strong optional value, +because you'd end up with something like this (just showing a couple lines): + +```swift + let y = np^.arange?^(24)?^.reshape?^(2, 3, 4)! + let a = np^.ones?^(3, dtype: np^.int32!)! +``` + +This is so bad that no one would actually do this. Making the Python operators return +optionals would be even worse, since binary operators break optional chaining. + +## Alternative Python Interoperability Approaches + +In addition to the alternatives above (which provide different approaches to refine a +proposal along the lines of this one), there have also been extensive discussion of different +approaches to the problem of dynamic language interoperability on the whole. Before +we explore those alternatives, we need to understand the common sources of concern. + +The biggest objection to this proposal is that it is "completely dynamic", and some +people have claimed that Swift intentionally pushes people towards static types. Others +claim that dynamic features like this are unsafe (what if a member doesn't exist?), and claim +that Swift does not provide unsafe features. + +While the aims are largely correct for pure Swift code, the only examples of language +interoperability we have so far is with C and Objective-C, and Swift's interop with those is +indeed already memory unsafe, unsound, and far more invasive than what we propose. +Observe: + +1) Swift does have completely dynamic features, even ones that can be used unsafely or + unwisely. There was a recent talk at Swift Summit 2017 that explored some of these. +2) Bridging to dynamic languages inherently requires "fully dynamic" facilities, because the + imported language has fully dynamic capabilities, and programmers use them. This is + pervasive in Python code, and is also reasonable common in Objective-C "the `id` type". +3) The Objective-C interoperability approach to handling the "inherently dynamic" parts of + Objective-C is a feature called "AnyObject dispatch". If you aren't failiar, it returns + members lookup as `ImplicitlyUnwrappedOptional` types (aka, `T!` types), which are + extremely dangerous to work with - though not "unsafe" in the Swift sense. +4) Beyond the problems with IUOs, Anyobject lookup is *also* completely non-type safe when + the lookup is successful: it is entirely possible to access a property or method as "String" + even if it were declared as an integer in Objective-C: the bridging logic doesn't even return + such a lookup as `nil` in general. +5) The implementation of "AnyObject lookup" is incredibly invasive across the compiler and + has been the source of a large number of bugs. +6) Beyond AnyObject lookup, the Clang importer itself is ~25 thousand lines of code, and + while it is the source of great power, it is also a continuous source of bugs and problems. + Beyond the code in the Clang importer library itself, it also has tendrils in almost + every part of the compiler and runtime. + +in contrast, the combination of the `DynamicMemberLookup` and `DynamicCallable` +proposals are both minimally invasive in the compiler, and fully type safe. Implementations of +these proposals may choose to provide one of three styles of fully type safe implementation: + +1) Return optional types, forcing clients to deal with dynamic failures. We show an example + of this in the JSON example above. +2) Return IUO types, providing the ability for clients to deal with dynamic failures, but allow + them to ignore them otherwise. This approach is similar to AnyObject dispatch, but is + more type safe. +3) Return non-optional types, and enforce type safety at runtime with traps. This is the + approach followed in the Python interop example above, but that bridge is under + development and may switch to another approach if it provides a better experience. The + actual prototype already has a more holistic approach for handling failability that isn't + describe here. + +The style of design depends on the exact bridging problem being solved, e.g. the customs of +the language being interoperated with. Also, yes, it is possible to use these APIs to provide +an unsafe API, but that is true of literally every feature in Swift - Swift provides support +for unsafe features as part of its goals to be pragmatic. + +With this as background, let's explore the proposed alternatives approaches to dynamic +language interoperability: + +### Direct language support for Python (and all the other languages) + +We considered implementing something analogous to the Clang importer for Python, which +would add a first class Python specific type(s) to Swift language and/or standard library. We +rejected this option because: + +1) Such integration would **require that we do something like this proposal anyway**, + because Python (like Objective-C) is a fundamentally dynamic language, and we need a + way to express that fundamental dynamism: either this proposal or something like + "`AnyObject` for Python". +2) Python is actually far less "inherently typed" than Objective-C is, because everything + is typed with the equivalent of `id`, whereas the Objective-C community has used concrete + types for many things (and with the introduction of Objective-C Generics and other + features, this has become even more common). +3) it would be *significantly* more invasive in the compiler than this proposal. While it is + unlikely to be as large a scope of impact as the Clang importer in terms of bulk of code, it + would require just as many tendrils spread throughout the compiler. +4) Taking this approach would set the precedent for all other dynamic languages to get first + class language support baked into the Swift compiler, leading to an even greater + complexity spiral down the road. +5) The proposed "first class support" doesn't substantially improve the experience of working + with Python over this proposal, so it is all pain and no gain. + +Several people have suggested that a "Clang-importer" style Python interoperability approach +could use the "Type Hints" introduced in +[PEP 484](https://www.python.org/dev/peps/pep-0484/), which would invalidate that last +point above. This approach to progressive typing for Python was prototyped in +[mypy](http://mypy-lang.org/) and first [shipped in Python +3.5](https://docs.python.org/3/library/typing.html) in September 2015. + +Unfortunately, it isn't reasonable to expect Type Hints to significantly improve the experience +working with Python in Swift for several reasons, including: + +1) PEP 484 states: "It should also be emphasized that **Python will remain a dynamically + typed language, and the authors have no desire to ever make type hints mandatory, + even by convention.**" (the emphasis is by the authors of PEP 484). This means we + need this proposal or something like AnyObject lookup ... forever. +2) These annotations are only currently supported on a [provisional + basis](https://docs.python.org/3/glossary.html#term-provisional-api), which means that + they are "deliberately excluded from .. backwards compatibility guarantees... up to and + including removal of the interface". Because they are subject to change and potentially + removal, they haven't gotten wide adoption. +3) Unlike Objective-C type annotations, these type hints are inherently unsound by design. + Further, Python APIs often creep to accepting many more types than a Swift programmer + would expect, which means that a type annotation is often "so broad as to be unhelpful" or + "too narrow to be correct". +4) These annotations are not dynamically enforced, and they are specifically designed to + "influence" static analysis tools, which means that they are also frequently wrong. In the + context of a narrowly used static analysis tool, a wrong type annotation is merely annoying. + In the context of bridging into Swift, being incorrect is a real problem. +5) The fact that they have only been available in Python **3** (which has a smaller community + than Python 2) and have only been shipping for 2 years, means that they haven't been used + by many people, and which contributes to their low adoption. +6) It is a goal to work with other dynamic language communities beyond Python, and not + all have progressive typing facilities like this one. +7) Type annotations help the most in situations when you are "mixing and matching" Swift + code with an existing body of some other code that you are able to modify. While it is + possible that some people will want to mix and match Swift and Python, by far the most + common reason for wanting to interoperate with a dynamic langauge is to leverage the + existing APIs that the community provides in a black box manner. Being black box means + that you want to reuse the code, but you don't want to touch and own it yourself. +8) Finally, the idea of Swift providing a "better Python than Python itself does" + under-appreciates the effort that the Python community has spent trying to achieve the + same goals. Its community includes a lot of people in it that understand the + benefits of static and progressive typing (e.g. the [mypy](http://mypy-lang.org/) community) + and they have spent a lot of time on this problem. Despite their efforts, the ideas have low + adoption: Swift bridging doesn't change the fundamental reasons for this. + +Finally, it is important to realize that Swift and Clang have a +special relationship because they were designed by many of the same people - so their +implementations are similar in many respects. Further, the design of both Clang and the +Objective-C language shifted in major ways to support interoperability with Swift - including +the introduction of ARC, Modules, Objective-C generics, pervasive lazy decl resolution, and +many more minor features). + +It is extremely unlikely that another established language/compiler community would accept +the scope of changes necessary to provide great importer support for them, and it is also +extremely unlikely that one would just magically work out of the box for what we need it to +do. That said, our goals aren't to provide a better Python than Python, only to embrace +Python (and other dynamic languages) for what they are, without polluting the Swift compiler +and runtime with a ton of complexity that we'll have to carry around forever. + + +### Introduce F# style "Type Providers" into Swift + +[Type providers](https://docs.microsoft.com/en-us/dotnet/fsharp/tutorials/type-providers/) are +a cool feature of the F# language. They are an expansive (and quite +complex) metaprogramming system which permits a "metaprogrammed library" to synthesize +types and other language features into the program based on statically knowable type +databases. This leads to significantly improved type safety in the case where schemas for +dynamic APIs are available (e.g. a JSON schema) but which are not apparent in the source +code. + +While type providers are extremely interesting and might be considered for inclusion into a +future macro system in Swift, it is important to understand that they just provide a way for +library developers to extend the compiler along the lines of the Clang importer in Swift. As +such, they aren't actually helpful for this proposal for all of the reasons described +in the section above, but the most important point is that: + +Type providers can only "provide" a type that is expressible in the language, and dynamic + languages do have completely dynamic features, so **something that provides the +semantics of `DynamicMemberLookup` will be required** with that approach anyway. We'd +have to take this proposal (or something substantially similar) before type providers would be +useful for Python in the first place. + +Because of this, we believe that type providers and other optional typing facilities are +a potentially interesting follow-on to this proposal which should be measured according to +their own merits. The author of this proposal is also personally skeptical that type providers +could ever be done in a way that is acceptable within the goals of Swift (e.g. to +someday have fast compile times). + +### Introduce a language independent "foreign class" feature to Swift + +One suggestion was to introduce a [general "foreign class" feature to Swift](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171218/042227.html). The core team met to discuss this and concluded that it was the wrong direction to go. Among opinions held by core team members, several believed it forcing other languages models into the Swift model would violate their fundamental principles (e.g. Go and Javascript doesn't *have* classes), some felt it would be too invasive into the compiler, and others believed that such an approach ends up requiring a `DynamicMemberLookup` related feature anyway - because +e.g. Python doesn't require property declarations. + +### Use "automatically generated wrappers" to interface with Python + +This approach has [numerous problems](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20180101/042522.html). Beyond that, wrappers also fundamentally require that we take (something like) this proposal +to work in the first place. The primary issue is that Python (and other dynamic languages) +require no property declarations. If you have no property declaration, there is nothing for the +wrapper generator to [w]rap or generate. + From 25ec3030b7b656e5d7df7c150ab6f53723cdc0a2 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Fri, 12 Jan 2018 17:05:45 -0800 Subject: [PATCH 0541/4563] Return SE-0192 for revision. --- proposals/0192-non-exhaustive-enums.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0192-non-exhaustive-enums.md b/proposals/0192-non-exhaustive-enums.md index eb42b42538..338dc24103 100644 --- a/proposals/0192-non-exhaustive-enums.md +++ b/proposals/0192-non-exhaustive-enums.md @@ -3,9 +3,10 @@ * Proposal: [SE-0192](0192-non-exhaustive-enums.md) * Authors: [Jordan Rose](https://github.com/jrose-apple) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active Review (December 19, 2017...January 3, 2018)** +* Status: **Returned for revision** * Implementation: [apple/swift#11961](https://github.com/apple/swift/pull/11961) * Pre-review discussion: [Enums and Source Compatibility](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170807/038663.html), with additional [orphaned thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170911/039787.html) +* Review discussion: [Review author summarizes some feedback from review discussion and proposes alternatives](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20180101/042480.html) + +## Introduction ## + +The standard library currently includes API which allows a type to customize its +description in Xcode playgrounds and Swift Playgrounds. This API takes the +form of the `PlaygroundQuickLook` enum which enumerates types which are +supported for quick looks, and the `CustomPlaygroundQuickLookable` protocol +which allows a type to return a custom `PlaygroundQuickLook` value for an +instance. + +This is brittle, and to avoid dependency inversions, many of the cases are typed +as taking `Any` instead of a more appropriate type. This proposal suggests that +we deprecate `PlaygroundQuickLook` and `CustomPlaygroundQuickLookable` in Swift +4.1 so they can be removed entirely in Swift 5, preventing them from being +included in the standard library's stable ABI. To maintain compatibility with +older playgrounds, the deprecated symbols will be present in a temporary +compatibility shim library which will be automatically imported in playground +contexts. (This will represent an intentional source break for projects, +packages, and other non-playground Swift code which use `PlaygroundQuickLook` or +`CustomPlaygroundQuickLookable` when they switch to the Swift 5.0 compiler, even +in the compatibility modes.) + +Since it is still useful to allow types to provide alternate descriptions for +playgrounds, we propose to add a new protocol to the PlaygroundSupport framework +which allows types to do just that. (PlaygroundSupport is a framework delivered +by the [swift-xcode-playground-support project](https://github.com/apple/swift-xcode-playground-support) +which provides API specific to working in the playgrounds environment). The new +`CustomPlaygroundDisplayConvertible` protocol would allow instances to return an +alternate object or value (as an `Any`) which would serve as their +description. The PlaygroundLogger framework, also part of +swift-xcode-playground-support, will be updated to understand this protocol. + +Swift-evolution thread: [Discussion thread topic for that proposal](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20180108/042639.html) + +## Motivation ## + +The `PlaygroundQuickLook` enum which currently exists in the standard library is +substandard: + +```swift +public enum PlaygroundQuickLook { + case text(String) + case int(Int64) + case uInt(UInt64) + case float(Float32) + case double(Float64) + case image(Any) + case sound(Any) + case color(Any) + case bezierPath(Any) + case attributedString(Any) + case rectangle(Float64, Float64, Float64, Float64) + case point(Float64, Float64) + case size(Float64, Float64) + case bool(Bool) + case range(Int64, Int64) + case view(Any) + case sprite(Any) + case url(String) + case _raw([UInt8], String) +} +``` + +The names of these enum cases do not necessarily match current Swift naming +conventions (e.g. `uInt`), and many cases are typed as `Any` to avoid dependency +inversions between the standard library and higher-level frameworks like +Foundation and AppKit or UIKit. It also contains cases which the +PlaygroundLogger framework does not understand (e.g. `sound`), and this listing +of cases introduces revlock between PlaygroundLogger and the standard library +that makes it challenging to introduce support for new types of quick looks. + +Values of this enum are provided to the PlaygroundLogger framework by types via +conformances to the `CustomPlaygroundQuickLookable` protocol: + +```swift +public protocol CustomPlaygroundQuickLookable { + var customPlaygroundQuickLook: PlaygroundQuickLook { get } +} +``` + +This protocol itself is not problematic, but if `PlaygroundQuickLook` is being +removed, then it needs to be removed as well. Additionally, there is a companion +underscored protocol which should be removed as well: + +```swift +public protocol _DefaultCustomPlaygroundQuickLookable { + var _defaultCustomPlaygroundQuickLook: PlaygroundQuickLook { get } +} +``` + +## Proposed solution ## + +To solve this issue, we propose the following changes: + + - Introduce a new `CustomPlaygroundDisplayConvertible` protocol in + PlaygroundSupport in Swift 4.1 to allow types to provide an alternate + description for playground logging + - Deprecate `PlaygroundQuickLook` and `CustomPlaygroundQuickLookable` in Swift + 4.1, suggesting users use `CustomPlaygroundDisplayConvertible` instead + - Remove `PlaygroundQuickLook` and `CustomPlaygroundQuickLookable` from the + standard library in Swift 5.0 + - Provide an automatically-imported shim library for the playgrounds context + to provide the deprecated instances of `PlaygroundQuickLook` and + `CustomPlaygroundQuickLookable` for pre-Swift 5 playgrounds + +## Detailed design ## + +To provide a more flexible API, we propose deprecating and ultimately removing +the `PlaygroundQuickLook` enum and `CustomPlaygroundQuickLookable` protocol in +favor of a simpler design. Instead, we propose introducing a protocol which just +provides the ability to return an `Any` that serves as a stand-in for the +instance being logged: + +```swift +/// A type that supplies a custom description for playground logging. +/// +/// All types have a default description for playgrounds. This protocol +/// allows types to provide custom descriptions which are then logged in +/// place of the original instance. +/// +/// Playground logging can generate, at a minimum, a structured description +/// of any type. Playground logging is also capable of generating a richer, +/// more specialized description of core types -- for instance, the contents +/// of a `String` are logged, as are the components of an `NSColor` or +/// `UIColor`. +/// +/// The current playground logging implementation logs specialized +/// descriptions of at least the following types: +/// +/// - `String` and `NSString` +/// - `Int` and `UInt` (including the sized variants) +/// - `Float` and `Double` +/// - `Bool` +/// - `Date` and `NSDate` +/// - `NSAttributedString` +/// - `NSNumber` +/// - `NSRange` +/// - `URL` and `NSURL` +/// - `CGPoint`, `CGSize`, and `CGRect` +/// - `NSColor`, `UIColor`, `CGColor`, and `CIColor` +/// - `NSImage`, `UIImage`, `CGImage`, and `CIImage` +/// - `NSBezierPath` and `UIBezierPath` +/// - `NSView` and `UIView` +/// +/// Playground logging may also be able to support specialized descriptions +/// of other types. +/// +/// Implementors of `CustomPlaygroundDisplayConvertible` may return a value of +/// one of the above types to also receive a specialized log description. +/// Implementors may also return any other type, and playground logging will +/// generated structured logging for the returned value. +public protocol CustomPlaygroundDisplayConvertible { + /// Returns the custom playground description for this instance. + /// + /// If this type has value semantics, the instance returned should be + /// unaffected by subsequent mutations if possible. + var playgroundDescription: Any { get } +} +``` + +Additionally, instead of placing this protocol in the standard library, we +propose placing this protocol in the PlaygroundSupport framework, as it is only +of interest in the playgrounds environment. Should demand warrant it, a future +proposal could suggest lowering this protocol into the standard library. + +If this proposal is accepted, then code like the following: + +```swift +extension MyStruct: CustomPlaygroundQuickLookable { + var customPlaygroundQuickLook: PlaygroundQuickLook { + return .text("A description of this MyStruct instance") + } +} +``` + +would be replaced with something like the following: + +```swift +extension MyStruct: CustomPlaygroundDisplayConvertible { + var playgroundDescription: Any { + return "A description of this MyStruct instance" + } +} +``` + +This proposal also allows types which wish to be represented structurally +(like an array or dictionary) to return a type which is logged structurally +instead of requiring an implementation of the `CustomReflectable` protocol: + +```swift +extension MyStruct: CustomPlaygroundDisplayConvertible { + var playgroundDescription: Any { + return [1, 2, 3] + } +} +``` + +This is an enhancement over the existing `CustomPlaygroundQuickLookable` +protocol, which only supported returning opaque, quick lookable values for +playground logging. + +Implementations of `CustomPlaygroundDisplayConvertible` may potentially chain +from one to another. For instance, with: + +```swift +extension MyStruct: CustomPlaygroundDisplayConvertible { + var playgroundDescription: Any { + return "MyStruct description for playgrounds" + } +} + +extension MyOtherStruct: CustomPlaygroundDisplayConvertible { + var playgroundDescription: Any { + return MyStruct() + } +} +``` + +Playground logging for `MyOtherStruct` would generate the string "MyStruct +description for playgrounds" rather than the structural view of `MyStruct`. It +is legal, however, for playground logging implementations to cap chaining to a +reasonable limit to guard against infinite recursion. + +## Source compatibility ## + +This proposal is explicitly suggesting that we make a source-breaking change in +Swift 5 to remove `PlaygroundQuickLook`, `CustomPlaygroundQuickLookable`, and +`_DefaultCustomPlaygroundQuickLookable`. Looking at a GitHub search, there are +fewer than 900 references to `CustomPlaygroundQuickLookable` in Swift source +code; from a cursory glance, many of these are duplicates, from forks of the +Swift repo itself (i.e. the definition of `CustomPlaygroundQuickLookable` in +the standard library), or are clearly implemented using pre-Swift 3 names of the +enum cases in `PlaygroundQuickLook`. (As a point of comparison, there are over +185,000 references to `CustomStringConvertible` in Swift code on GitHub, and +over 145,000 references to `CustomDebugStringConvertible`, so +`CustomPlaygroundQuickLookable` is clearly used many orders of magnitude less +than those protocols.) Furthermore, it does not appear that any projects +currently in the source compatibility suite use these types. + +However, to mitigate the impact of this change, we propose to provide a limited +source compatibility shim for the playgrounds context. This will be delivered as +part of the swift-xcode-playground-support project as a library containing the +deprecated `PlaygroundQuickLook` and `CustomPlaygroundQuickLookable` protocols. +This library would be imported automatically in playgrounds. This source +compatibility shim would not be available outside of playgrounds, so any +projects, packages, or other Swift code would be intentionally broken by this +change when upgrading to the Swift 5.0 compiler, even when compiling in a +compatibility mode. + +Due to the limited usage of these protocols, and the potential challenge in +migration, this proposal does not include any proposed migrator changes to +support the replacement of `CustomPlaygroundQuickLookable` with +`CustomPlaygroundDisplayConvertible`. Instead, we intend for Swift 4.1 to be a +deprecation period for these APIs, allowing any code bases which implement +`CustomPlaygroundQuickLookable` to manually switch to the new protocol. While +this migration may not be trivial programatically, it should -- in most cases -- +be fairly trivial for someone to hand-migrate to +`CustomPlaygroundDisplayConvertible`. During the deprecation period, the +PlaygroundLogger framework will continue to honor implementations of +`CustomPlaygroundQuickLookable`, though it will prefer implementations of +`CustomPlaygroundDisplayConvertible` if both are present on a given type. + +## Effect on ABI stability ## + +This proposal affects ABI stability as it removes an enum and a pair of +protocols from the standard library. Since this proposal proposes adding +`CustomPlaygroundDisplayConvertible` to PlaygroundSupport instead of the +standard library, there is no impact of ABI stability from the new protocol, as +PlaygroundSupport does not need to maintain a stable ABI, as its clients -- +playgrounds -- are always recompiled from source. + +Since playgrounds are always compiled from source, the temporary shim library +does not represent a new ABI guarantee, and it may be removed if the compiler +drops support for the Swift 3 and 4 compatibility modes in a future Swift +release. + +Removing `PlaygroundQuickLook` from the standard library also potentially allows +us to remove a handful of runtime entry points which were included to support +the `PlaygroundQuickLook(reflecting:)` API. + +## Effect on API resilience ## + +This proposal does not impact API resilience. + +## Alternatives considered ## + +### Do nothing ### + +One valid alternative to this proposal is to do nothing: we could continue to +live with the existing enum and protocol. As noted above, these are fairly poor, +and do not serve the needs of playgrounds particularly well. Since this is our +last chance to remove them prior to ABI stability, we believe that doing nothing +is not an acceptable alternative. + +### Provide type-specific protocols ### + +Another alternative we considered was to provide type-specific protocols for +providing playground descriptions. We would introduce new protocols like +`CustomNSColorConvertible`, `CustomNSAttributedStringConvertible`, etc. which +would allow types to provide descriptions as each of the opaquely-loggable +types supported by PlaygroundLogger. + +This alternative was rejected as it would balloon the API surface for +playgrounds, and it also would not provide a good way to select a preferred +description. (That is, what would PlaygroundLogger select as the +description of an instance if it implemented both `CustomNSColorConvertible` +*and* `CustomNSAttributedStringConvertible`?) + +### Implement `CustomPlaygroundDisplayConvertible` in the standard library ### + +As an alternative to implementing `CustomPlaygroundDisplayConvertible` in +PlaygroundSupport, we could implement it in the standard library. This would +make it available in all contexts (i.e. in projects and packages, not just in +playgrounds), but this protocol is not particularly useful outside of the +playground context, so this proposal elects not to place +`CustomPlaygroundDisplayConvertible` in the standard library. + +Additionally, it should be a source-compatible change to move this protocol to +the standard library in a future Swift version should that be desirable. Since +playgrounds are always compiled from source, the fact that this would be an ABI +change for PlaygroundSupport does not matter, and a compatibility typealias +could be provided in PlaygroundSupport to maintain compatibility with code which +explicitly qualified the name of the `CustomPlaygroundDisplayConvertible` +protocol. + +### Have `CustomPlaygroundDisplayConvertible` return something other than `Any` ### + +One minor alternative considered was to have +`CustomPlaygroundDisplayConvertible` return a value with a more specific type +than `Any`. For example: + +```swift +protocol CustomPlaygroundDisplayConvertible { + var playgroundDescription: CustomPlaygroundDisplayConvertible { get } +} +``` + +or: + +```swift +protocol PlaygroundDescription {} + +protocol CustomPlaygroundDisplayConvertible { + var playgroundDescription: PlaygroundDescription { get } +} +``` + +In both cases, core types which the playground logger supports would conform to +the appropriate protocol such that they could be returned from implementations +of `playgroundDescription`. + +The benefit to this approach is that it is more self-documenting than the +approach proposed in this document, as a user can look up all of the types which +conform to a particular protocol to know what the playground logger understands. +However, this approach has a number of pitfalls, largely because it's +intentional that the proposal uses `Any` instead of a more-constrained protocol. +It should be possible to return anything as the stand-in for an instance, +including values without opaque playground quick look views, so that it's easier +to construct an alternate structured view of a type (without having to override +the more complex `CustomReflectable` protocol). Furthermore, by making the API +in the library use a general type like `Any`, this proposal prevents revlock +from occurring between IDEs and the libraries, as the IDE's playground logger +can implement support for opaque logging of new types without requiring library +changes. (And IDEs can opt to support a subset of types if they prefer, whereas +if the libraries promised support an IDE would effectively be compelled to +provide it.) + +### Have `CustomPlaygroundDisplayConvertible` return an `Any?` instead of an `Any` ### + +One alternative considered was to have `CustomPlaygroundDisplayConvertible` +return an `Any?` instead of an `Any`. This would permit individual instances to +opt-out of a custom playground description by returning nil instead of a +concrete value or object. + +Although that capability is no longer present, in most cases implementors of +`CustomPlaygroundDisplayConvertible` may return a custom description which +closely mirrors their default description. One big exception to this are classes +which are considered core types, such as `NSView` and `UIView`, as one level of +subclass may wish to customize its description while deeper level may wish to +use the default description (which is currently a rendered image of the view). +This proposal does not permit that; the second-level subclass must return a +custom description one way or another, and due to the chaining nature of +`CustomPlaygroundDisplayConvertible` implementations, it cannot return `self` +and have that reliably indicate to the playground logger implementation that +that means "don't use a custom description". + +This issue seems to be limited enough that it should not tarnish the API design +as a whole. Returning `Any` and not `Any?` is easier to understand, so this +proposal opts to do that. Should this be a larger issue than anticipated, a +future proposal could introduce a struct like `DefaultPlaygroundDescription` +which the playground logger would understand to mean "don't check for a +`CustomPlaygroundDisplayConvertible` conformance on the wrapped value". + +### Alternate Names for `CustomPlaygroundDisplayConvertible` ### + +Finally, as this introduces a new protocol, there are other possible names: + +- `CustomPlaygroundRepresentable` +- `CustomPlaygroundConvertible` +- `CustomPlaygroundPreviewConvertible` +- `CustomPlaygroundQuickLookConvertible` +- `CustomPlaygroundValuePresentationConvertible` +- `CustomPlaygroundPresentationConvertible` + +`CustomPlaygroundRepresentable` was rejected as it does not match the naming +convention established by +`CustomStringConvertible`/`CustomDebugStringConvertible`. +`CustomPlaygroundConvertible` was rejected as not being specific enough -- types +conforming to this protocol are not themselves convertible to playgrounds, but +are instead custom convertible for playground display. +`CustomPlaygroundPreviewConvertible` is very similar to +`CustomPlaygroundDisplayConvertible`, but implies more about the presentation +than is appropriate as a playground environment is free to display it any way it +wants, not just as a "preview". `CustomPlaygroundQuickLookConvertible` was +rejected as it potentially invokes the to-be-removed `PlaygroundQuickLook` enum. +`CustomPlaygroundValuePresentationConvertible` and +`CustomPlaygroundPresentationConvertible` were rejected as too long of names for +the protocol. From 15539d5d8789c3dd4899b45046e79ffa5ebcb4cd Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Thu, 1 Feb 2018 16:19:07 -0800 Subject: [PATCH 0561/4563] Accept 196, and provide rationale. --- proposals/0196-diagnostic-directives.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/proposals/0196-diagnostic-directives.md b/proposals/0196-diagnostic-directives.md index 4a6164da3c..e4ee3452e0 100644 --- a/proposals/0196-diagnostic-directives.md +++ b/proposals/0196-diagnostic-directives.md @@ -5,7 +5,7 @@ * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Implementation: [apple/swift#14048](https://github.com/apple/swift/pull/14048) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/ab0c22a2340be9bfcb82e6f237752b4d959a93b7/proposals/0196-diagnostic-directives.md) -* Status: **Active review (January 24...30)** +* Status: **Accepted** ## Introduction @@ -151,6 +151,20 @@ to this proposal, and both could be addressed in future proposals. # Rationale -On [Date], the core team decided to **(TBD)** this proposal. -When the core team makes a decision regarding this proposal, -their rationale for the decision will be written here. +On February 1, 2018 the Core Team decided to **accept** this proposal with +slight revision over the [original proposal](https://github.com/apple/swift-evolution/blob/ab0c22a2340be9bfcb82e6f237752b4d959a93b7/proposals/0196-diagnostic-directives.md). + +The only revision over the original proposal is to change the syntax to use +`#warning()` instead of `#warning `. This fits well with +most of Swift's existing compiler directives, and was strongly supported in +the [review discussion](https://forums.swift.org/t/se-0196-compiler-diagnostic-directives/8734). + +The review discussion also covered a variety of possible extensions or +variants to this proposal, including support for using `#warning` [as an +expression](https://forums.swift.org/t/se-0196-compiler-diagnostic-directives/8734/21) +instead of a line directive and [support for runtime issues](https://forums.swift.org/t/se-0196-compiler-diagnostic-directives/8734/6). +The Core Team decided that while these directions are interesting and worth +exploring, they are complementary to the core functionality serviced by this +proposal. Further, keeping `#warning` as a line directive allows it to be +used in a wide variety of contexts, and serves a different need than using it +as a placeholder expression. From b0df1ff6956c9801ce056f0f8dd7d44c0679632d Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 1 Feb 2018 22:30:52 -0500 Subject: [PATCH 0562/4563] Accept SE-0197 with revisions. --- proposals/0197-remove-where.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/proposals/0197-remove-where.md b/proposals/0197-remove-where.md index 9d7f56c9d4..a76ba27f6e 100644 --- a/proposals/0197-remove-where.md +++ b/proposals/0197-remove-where.md @@ -1,15 +1,17 @@ -# Adding in-place `remove(where:)` to the Standard Library +# Adding in-place `removeAll(where:)` to the Standard Library * Proposal: [SE-0197](0197-remove-where.md) * Author: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (January 25...30)** +* Status: **Accepted with Revisions** * Implementation: [apple/swift#11576](https://github.com/apple/swift/pull/11576) +* Review: [Thread](https://forums.swift.org/t/se-0197-add-in-place-remove-where/8872) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/feec7890d6c193e9260ac9905456f25ef5656acd/proposals/0197-remove-where.md) ## Introduction It is common to want to remove all occurrences of a certain element from a -collection. This proposal is to add a `remove` algorithm to the +collection. This proposal is to add a `removeAll` algorithm to the standard library, which will remove all entries in a collection in-place matching a given predicate. @@ -65,7 +67,7 @@ important example of this, because its elements (graphemes) are variable width. Add the following method to `RangeReplaceableCollection`: ```swift -nums.remove(where: isOdd) +nums.removeAll(where: isOdd) ``` The default implementation will use the protocol's `init()` and `append(_:)` @@ -90,11 +92,11 @@ Add the following to `RangeReplaceableCollection`: ```swift protocol RangeReplaceableCollection { /// Removes every element satisfying the given predicate from the collection. - mutating func remove(where: (Iterator.Element) throws -> Bool) rethrows + mutating func removeAll(where: (Iterator.Element) throws -> Bool) rethrows } extension RangeReplaceableCollection { - mutating func remove(where: (Iterator.Element) throws -> Bool) rethrows { + mutating func removeAll(where: (Iterator.Element) throws -> Bool) rethrows { // default implementation similar to self = self.filter } } @@ -119,7 +121,7 @@ This change is purely additive so has no API resilience consequences. ## Alternatives considered -`remove(where:)` takes a closure with `true` for elements to remove. +`removeAll(where:)` takes a closure with `true` for elements to remove. `filter` takes a closure with elements to keep. In both cases, `true` is the "active" case, so likely to be what the user wants without having to apply a negation. The naming of `filter` is unfortunately ambiguous as to whether it's @@ -131,3 +133,8 @@ have an equivalent for collections of `Equatable` elements. A similar addition could be made that removes every element equal to a given value. This could easily be done as a further additive proposal later. +The initial proposal of this feature named it `remove(where:)`. During review, +it was agreed that this was unnecessarily ambiguous about whether all the +matching elements should be removed or just the first, and so the method was +renamed to `removeAll(where:)`. + From 3bccceedbe9085c5e1a7e79d8a6cf2bfaa06d476 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 2 Feb 2018 21:07:22 -0800 Subject: [PATCH 0563/4563] SE-0143 "Conditional conformances" is implemented in Swift 5. --- proposals/0143-conditional-conformances.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0143-conditional-conformances.md b/proposals/0143-conditional-conformances.md index 031ac60109..63c32314e1 100644 --- a/proposals/0143-conditional-conformances.md +++ b/proposals/0143-conditional-conformances.md @@ -3,7 +3,7 @@ * Proposal: [SE-0143](0143-conditional-conformances.md) * Author: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Accepted** +* Status: **Implemented (Swift 5)** * Decision Notes: [Review extended](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161107/028745.html), [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028888.html) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/91725ee83fa34c81942a634dcdfa9d2441fbd853/proposals/0143-conditional-conformances.md) From 92834e49489aa4a098597e3221c020b8404e81da Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Thu, 8 Feb 2018 23:21:01 -0800 Subject: [PATCH 0564/4563] Accept SE-0195 --- proposals/0195-dynamic-member-lookup.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/0195-dynamic-member-lookup.md b/proposals/0195-dynamic-member-lookup.md index 4862c4a8e5..1d82468347 100644 --- a/proposals/0195-dynamic-member-lookup.md +++ b/proposals/0195-dynamic-member-lookup.md @@ -5,7 +5,9 @@ * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Implementation: [apple/swift#13361](https://github.com/apple/swift/pull/13361) * [Previous Revision #1](https://github.com/apple/swift-evolution/commit/59c7455170c231f3df9ab0ba923262e126afaa06#diff-b3460d13f154c3d6b1d8396e4159a1d2) -* Status: **Active review (January 23...February 8)** +* Status: **Accepted** +* Decision Notes: [Review extended](https://forums.swift.org/t/se-0195-introduce-user-defined-dynamic-member-lookup-types/8658/126), [Rationale](https://forums.swift.org/t/se-0195-introduce-user-defined-dynamic-member-lookup-types/8658/160) + ## Introduction From 2dd7b996770dc02825e7888bc2efa5457113a9e7 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 12 Feb 2018 12:00:36 -0800 Subject: [PATCH 0565/4563] [common rejections] Add ! to the list of logic operators, clarify C heritage (#788) --- commonly_proposed.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commonly_proposed.md b/commonly_proposed.md index 5f92c308fc..82509fd043 100644 --- a/commonly_proposed.md +++ b/commonly_proposed.md @@ -4,7 +4,7 @@ This is a list of changes to the Swift language that are frequently proposed but Additionally, proposals for out-of-scope changes will not be scheduled for review. The [readme file](README.md) identifies a list of priorities for the next major release of Swift, and the [proposal review status page](https://apple.github.io/swift-evolution/) includes a list of changes that have been deferred for future discussion because they were deemed to be out of scope at the time of review (in addition to a list of changes proposed and rejected after a formal review). -Several of the discussions below refer to "C family" languages. This is intended to mean the extended family of languages that resemble C at a syntactic level. This includes languages like C++, C#, Objective-C, Java, and Javascript. +Several of the discussions below refer to "C family" languages. This is intended to mean the extended family of languages that resemble C at a syntactic level, such as C++, C#, Objective-C, Java, and Javascript. Swift embraces its C heritage. Where it deviates from other languages in the family, it does so because the feature was thought actively harmful (such as the pre/post-increment `++`) or to reduce needless clutter (such as `;` or parentheses in `if` statements). ## Basic Syntax and Operators @@ -12,7 +12,7 @@ Several of the discussions below refer to "C family" languages. This is intende * [Remove `;` semicolons](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/002421.html): Semicolons within a line are an intentional expressivity feature. Semicolons at the end of the line should be handled by a linter, not by the compiler. - * [Replace logical operators (`&&`, `||`, etc.) with words like "and" and "or"](https://lists.swift.org/pipermail/swift-evolution/2015-December/000032.html), [allowing non-punctuation as operators](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005669.html) and infix functions: The operator and identifier grammars are intentionally partitioned in Swift, which is a key part of how user-defined overloaded operators are supported. Requiring the compiler to see the "operator" declaration to know how to parse a file would break the ability to be able to parse a Swift file without parsing all of its imports. This has a major negative effect on tooling support. + * [Replace logical operators (`&&`, `||`, `!`, etc.) with words like "and", "or", "not"](https://lists.swift.org/pipermail/swift-evolution/2015-December/000032.html), and [allow non-punctuation operators](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005669.html) and infix functions: The operator and identifier grammars are intentionally partitioned in Swift, which is a key part of how user-defined overloaded operators are supported. Requiring the compiler to see the "operator" declaration to know how to parse a file would break the ability to be able to parse a Swift file without parsing all of its imports. This has a major negative effect on tooling support. While not needing infix support, `not` would need operator or keyword status to omit the parentheses as `!` can, and `not somePredicate()` visually binds too loosely compared to `!somePredicate()`. * [Replace `?:` ternary operator](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/002609.html): Definitely magical, but it serves a very important use-case for terse selection of different values. Proposals for alternatives have been intensely discussed, but none have been "better enough" for it to make sense to diverge from the precedent established by the C family of languages. From 2688607e6fb8619376eef9f56b5cc870a524681c Mon Sep 17 00:00:00 2001 From: Chris Eidhof Date: Mon, 12 Feb 2018 22:51:58 +0100 Subject: [PATCH 0566/4563] Bool toggle proposal (#782) * Bool toggle proposal * Suggestions by @xwu * Feature name * Update and rename nnnn-bool-toggle.md to 0199-bool-toggle.md --- proposals/0199-bool-toggle.md | 77 +++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 proposals/0199-bool-toggle.md diff --git a/proposals/0199-bool-toggle.md b/proposals/0199-bool-toggle.md new file mode 100644 index 0000000000..1542af6c1c --- /dev/null +++ b/proposals/0199-bool-toggle.md @@ -0,0 +1,77 @@ +# Adding `toggle` to `Bool` + +* Proposal: [SE-0199](0199-bool-toggle.md) +* Authors: [Chris Eidhof](http://chris.eidhof.nl) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift/) +* Status: **Active review (February 12-19)** + + +## Introduction + +I propose adding a `mutating func toggle` to `Bool`. It toggles the `Bool`. + +- Swift-evolution thread: [Discussion thread topic for that proposal](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20180108/042767.html) +- Swift forums thread: [pitch: adding toggle to Bool](https://forums.swift.org/t/pitch-adding-toggle-to-bool/7414) + +## Motivation + +For `Bool` variables, it is common to want to toggle the state of the variable. In larger (nested) structs, the duplication involved can become especially annoying: + +```swift +myVar.prop1.prop2.enabled = !myVar.prop1.prop2.enabled +``` + +It's also easy to make a mistake in the code above if there are multiple `Bool` vars. + +## Proposed solution + +Add a method `toggle` on `Bool`: + +```swift +extension Bool { + /// Equivalent to `someBool = !someBool` + /// + /// Useful when operating on long chains: + /// +  /// myVar.prop1.prop2.enabled.toggle() +  mutating func toggle() { + self = !self + } +} +``` + +This allows us to write the example above without duplication: + +```swift +myVar.prop1.prop2.enabled.toggle() +``` + +`!` and `toggle()` mirror the API design for `-` and `negate()`. (Thanks to Xiaodi Wu for pointing this out). + +## Detailed design + +N/A + +## Source compatibility + +This is strictly additive. + +## Effect on ABI stability + +N/A + +## Effect on API resilience + +N/A + +## Alternatives considered + +Other names could be: + +- `invert` +- `negate` +- `flip` + +From the brief discussion on SE, it seems like `toggle` is the clear winner. + +Some people also suggested adding a non-mutating variant (in other words, a method with the same semantics as the prefix `!` operator), but that's out of scope for this proposal, and in line with commonly rejected proposals. From 0bbeb5dd25a2b723e13e0b7ae60e3b39ef6cc32a Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Tue, 13 Feb 2018 16:46:00 -0800 Subject: [PATCH 0567/4563] Add implementation PR to SE-0199. --- proposals/0199-bool-toggle.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0199-bool-toggle.md b/proposals/0199-bool-toggle.md index 1542af6c1c..70332eaa60 100644 --- a/proposals/0199-bool-toggle.md +++ b/proposals/0199-bool-toggle.md @@ -4,6 +4,7 @@ * Authors: [Chris Eidhof](http://chris.eidhof.nl) * Review Manager: [Ben Cohen](https://github.com/airspeedswift/) * Status: **Active review (February 12-19)** +* Implementation: [apple/swift#14586](https://github.com/apple/swift/pull/14586) ## Introduction From fcdd6e9db86f1e2dd48ad8b5a04e0e386549ffce Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Thu, 15 Feb 2018 14:49:10 -0800 Subject: [PATCH 0568/4563] [Status] Constrain detail items to keep columns aligned --- index.css | 1 + 1 file changed, 1 insertion(+) diff --git a/index.css b/index.css index c62e024cc4..8a983698f5 100644 --- a/index.css +++ b/index.css @@ -235,6 +235,7 @@ section ul, section li { .proposal-detail { break-inside: avoid; display: flex; + max-width: 373px; } .proposal-detail-label { From 5e17a4854f5b7013088dfa19fcb8275ab3230a48 Mon Sep 17 00:00:00 2001 From: Evan Tang Date: Wed, 21 Feb 2018 16:05:10 -0600 Subject: [PATCH 0569/4563] Initial Commit --- proposals/NNNN-lazy-compactmap-sequence.md | 184 +++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 proposals/NNNN-lazy-compactmap-sequence.md diff --git a/proposals/NNNN-lazy-compactmap-sequence.md b/proposals/NNNN-lazy-compactmap-sequence.md new file mode 100644 index 0000000000..48475e2fb2 --- /dev/null +++ b/proposals/NNNN-lazy-compactmap-sequence.md @@ -0,0 +1,184 @@ +# Feature name + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [TellowKrinkle](https://github.com/TellowKrinkle), [Johannes Weiß](https://github.com/weissi) +* Review Manager: TBD +* Status: **Awaiting implementation** +* Implementation: Coming Soon + +## Introduction + +Chaining multiple `.map()`s and `.filter()`s on a lazy collection leads +to suboptimal codegen, as well as large, painful type names. +To improve this, we propose adding a LazyCompactMap{Sequence, Collection} +type along with some overloads on the other lazy collection types' `.map(_:)` +and `.filter(_:)` functions which return this type to get better codegen +and shorter type names. + +Swift-evolution thread: [Discussion thread topic for the proposal](https://forums.swift.org/t/introduce-lazy-version-of-compactmap/9835/1) + +## Motivation + +The current lazy system is very good for easily defining transforms on collections, but certain constructs can lead to less-than-optimal codegen. + +For example, the code collection.map(map1).filter(filter1).map(map2).filter(filter2) will lead to code like this in the formIndex(after:) method: + +```swift +do { + do { + collection.formIndex(after: &i) + } while !filter1(map1(collection[i])) +} while !filter2(map2(map1(collection[i]))) +``` +while it could be represented with this more efficient single loop: +```swift +do { + collection.formIndex(after: &i) +} while !filter1(map1(collection[i])) && !filter2(map2(map1(collection[i]))) +``` +Currently, you can get a single loop by instead using this compactMap: +```swift +collection.compactMap { + let a = map1($0) + guard filter1(a) else { return nil } + let b = map2(a) + guard filter2(b) else { return nil } + return b +} +``` +but this removes the nice composability of the chained map/filter combination. + +The standard library recently got an override on LazyMapCollection and LazyFilterCollection +which combines multiple filters and maps in a row, however it does not work with alternating maps and filters. + +## Proposed solution + +Define a `LazyCompactMapCollection` collection (and sequence) which represents a compactMap. +Then, add overrides on `LazyMapCollection.filter`, `LazyFilterCollection.map`, +`Lazy*Collection.compactMap`, and `LazyCompactMapCollection.{filter, map}` +to return a `LazyCompactMapCollection` that combines all the maps and filters. + +As an added bonus, you’ll never see a giant chain of +`LazyMapCollection, ...>` again + +## Detailed design + +A new `LazyCompactMapCollection` and equivalent Sequence should be defined like so: +```swift +public struct LazyCompactMapCollection { + internal var _base: Base + internal let _transform: (Base.Element) -> Element? + + internal init(_base: Base, transform: @escaping (Base.Element) -> Element?) { + self._base = _base + self._transform = transform + } +} +``` +with a very similar set of overrides to the current `LazyFilterCollection` + +Then, the following extensions should be added (with equivalent ones for Lazy Sequences): +```swift +extension LazyMapCollection { + public func compactMap(_ transform: @escaping (Element) -> U?) -> LazyCompactMapCollection { + let mytransform = self._transform + return LazyCompactMapCollection( + _base: self._base, + transform: { transform(mytransform($0)) } + ) + } + + public func filter(_ isIncluded: @escaping (Element) -> Bool) -> LazyCompactMapCollection { + let mytransform = self._transform + return LazyCompactMapCollection( + _base: self._base, + transform: { + let transformed = mytransform($0) + return isIncluded(transformed) ? transformed : nil + } + ) + } +} + +extension LazyFilterCollection { + public func compactMap(_ transform: @escaping (Base.Element) -> U?) -> LazyCompactMapCollection { + let mypredicate = self._predicate + return LazyCompactMapCollection( + _base: self._base, + transform: { mypredicate($0) ? transform($0) : nil } + ) + } + + public func map(_ transform: @escaping (Base.Element) -> U) -> LazyCompactMapCollection { + let mypredicate = self._predicate + return LazyCompactMapCollection( + _base: self._base, + transform: { mypredicate($0) ? transform($0) : nil } + ) + } +} + +extension LazyCompactMapCollection { + public func compactMap(_ transform: @escaping (Element) -> U?) -> LazyCompactMapCollection { + let mytransform = self._transform + return LazyCompactMapCollection( + _base: self._base, + transform: { + guard let halfTransformed = mytransform($0) else { return nil } + return transform(halfTransformed) + } + ) + } + + public func map(_ transform: @escaping (Element) -> U) -> LazyCompactMapCollection { + let mytransform = self._transform + return LazyCompactMapCollection( + _base: self._base, + transform: { + guard let halfTransformed = mytransform($0) else { return nil } + return transform(halfTransformed) + } + ) + } + + public func filter(_ isIncluded: @escaping (Element) -> Bool) -> LazyCompactMapCollection { + let mytransform = self._transform + return LazyCompactMapCollection( + _base: self._base, + transform: { + guard let halfTransformed = mytransform($0), isIncluded(halfTransformed) else { return nil } + return halfTransformed + } + ) + } +} +``` + +## Source compatibility + +While most code will just work with the new extensions, code that does the following: +```swift +let array = [0, 1, 22] +let tmp = array.lazy.map(String.init).filter { $0.count == 1 } +let filtered: LazyFilterCollection> = tmp +``` +will break. + +However, this type of code is probably rare and similar code will already +be broken by the previously mentioned change that coalesces +`.filter(_:).filter(_:)` and `.map(_:).map(_:)` + +## Effect on ABI stability + +N/A + +## Effect on API resilience + +N/A + +## Alternatives considered + +The main alternative would be to not do this at all. This alternative +isn't great, as it can be many times slower when the map/filter functions +do little work, as shown by [this test](https://gist.github.com/tellowkrinkle/818c8d9ce467f272c889bdd503784d63) + From 774847eae4125beb1505c0af2353cd7a7ef0cbb2 Mon Sep 17 00:00:00 2001 From: tellowkrinkle Date: Wed, 21 Feb 2018 16:07:33 -0600 Subject: [PATCH 0570/4563] Formatting fix --- proposals/NNNN-lazy-compactmap-sequence.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-lazy-compactmap-sequence.md b/proposals/NNNN-lazy-compactmap-sequence.md index 48475e2fb2..ba29d37491 100644 --- a/proposals/NNNN-lazy-compactmap-sequence.md +++ b/proposals/NNNN-lazy-compactmap-sequence.md @@ -10,7 +10,7 @@ Chaining multiple `.map()`s and `.filter()`s on a lazy collection leads to suboptimal codegen, as well as large, painful type names. -To improve this, we propose adding a LazyCompactMap{Sequence, Collection} +To improve this, we propose adding a `LazyCompactMap{Sequence, Collection}` type along with some overloads on the other lazy collection types' `.map(_:)` and `.filter(_:)` functions which return this type to get better codegen and shorter type names. From b4f1b34a61d7b7f6af4ed9966254ebf6541e0322 Mon Sep 17 00:00:00 2001 From: John Eismeier Date: Fri, 23 Feb 2018 16:27:17 -0500 Subject: [PATCH 0571/4563] Prpose fix a typo --- proposals/0082-swiftpm-package-edit.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0082-swiftpm-package-edit.md b/proposals/0082-swiftpm-package-edit.md index 6cc40edb80..624a34e7cf 100644 --- a/proposals/0082-swiftpm-package-edit.md +++ b/proposals/0082-swiftpm-package-edit.md @@ -70,7 +70,7 @@ intended user action with the current contents of the tree. Our proposed solution is as follows: -1. Move the default location for checked depencency sources to be "hidden" (an +1. Move the default location for checked dependency sources to be "hidden" (an implementation detail). The package manager build system will by default try to ensure that any normal build always runs against the exact sources specified by the tag which was selected by dependency resolution. @@ -86,7 +86,7 @@ Our proposed solution is as follows: are present. When an editable package is present, it will be used to satisfy all instances - of that Package in the depencency graph. It should be possible to edit all, + of that Package in the dependency graph. It should be possible to edit all, some, or none of the packages in a dependency graph, without restriction. This solution is intended to directly address the desired behaviors of the From a6bee7c74a7b6c0dc1f71004d09f0329cae631f0 Mon Sep 17 00:00:00 2001 From: Evan Tang Date: Mon, 26 Feb 2018 17:16:55 -0600 Subject: [PATCH 0572/4563] Updated Source Compatibility section to properly reflect how things will break --- proposals/NNNN-lazy-compactmap-sequence.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-lazy-compactmap-sequence.md b/proposals/NNNN-lazy-compactmap-sequence.md index ba29d37491..f60534713a 100644 --- a/proposals/NNNN-lazy-compactmap-sequence.md +++ b/proposals/NNNN-lazy-compactmap-sequence.md @@ -156,13 +156,15 @@ extension LazyCompactMapCollection { ## Source compatibility -While most code will just work with the new extensions, code that does the following: +While most code will just work with the new extensions, code that relies on +the return type of `LazyCollection.compactMap(_:)` will break, however, the return type of the deprecated `LazyCollection.flatMap(_:)` has been left as is to reduce code breakage. + +In addition, code like following code will break: ```swift let array = [0, 1, 22] let tmp = array.lazy.map(String.init).filter { $0.count == 1 } let filtered: LazyFilterCollection> = tmp ``` -will break. However, this type of code is probably rare and similar code will already be broken by the previously mentioned change that coalesces From 1dd7fec036a5d55a81a2e1fbcb213a3daca13d7f Mon Sep 17 00:00:00 2001 From: Evan Tang Date: Mon, 26 Feb 2018 17:18:30 -0600 Subject: [PATCH 0573/4563] Added PR link for implementation --- proposals/NNNN-lazy-compactmap-sequence.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/NNNN-lazy-compactmap-sequence.md b/proposals/NNNN-lazy-compactmap-sequence.md index f60534713a..2874ab5d4c 100644 --- a/proposals/NNNN-lazy-compactmap-sequence.md +++ b/proposals/NNNN-lazy-compactmap-sequence.md @@ -1,10 +1,10 @@ -# Feature name +# Lazy CompactMap Sequence * Proposal: [SE-NNNN](NNNN-filename.md) * Authors: [TellowKrinkle](https://github.com/TellowKrinkle), [Johannes Weiß](https://github.com/weissi) * Review Manager: TBD -* Status: **Awaiting implementation** -* Implementation: Coming Soon +* Status: **Awaiting review** +* Implementation: [apple/swift#14841](https://github.com/apple/swift/pull/14841) ## Introduction From f0fb6000663eccd3e3a166ae455dd9bdb9b2c9dc Mon Sep 17 00:00:00 2001 From: Evan Tang Date: Mon, 26 Feb 2018 17:19:12 -0600 Subject: [PATCH 0574/4563] Fixed proposal name in document --- proposals/NNNN-lazy-compactmap-sequence.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-lazy-compactmap-sequence.md b/proposals/NNNN-lazy-compactmap-sequence.md index 2874ab5d4c..1df78e59a9 100644 --- a/proposals/NNNN-lazy-compactmap-sequence.md +++ b/proposals/NNNN-lazy-compactmap-sequence.md @@ -1,6 +1,6 @@ # Lazy CompactMap Sequence -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-NNNN](NNNN-lazy-compactmap-sequence.md) * Authors: [TellowKrinkle](https://github.com/TellowKrinkle), [Johannes Weiß](https://github.com/weissi) * Review Manager: TBD * Status: **Awaiting review** From 6591496881319b749d4503a748317aedcd5be46a Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 27 Feb 2018 19:12:07 -0800 Subject: [PATCH 0575/4563] Update 0198-playground-quicklook-api-revamp.md --- proposals/0198-playground-quicklook-api-revamp.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0198-playground-quicklook-api-revamp.md b/proposals/0198-playground-quicklook-api-revamp.md index e7f8ed0214..4932184d0c 100644 --- a/proposals/0198-playground-quicklook-api-revamp.md +++ b/proposals/0198-playground-quicklook-api-revamp.md @@ -4,7 +4,8 @@ * Authors: [Connor Wakamo](https://github.com/cwakamo) * Implementation: Swift 4.1 deprecation ([apple/swift#13911](https://github.com/apple/swift/pull/13911)], introduction of new protocol ([apple/swift-xcode-playground-support#21](https://github.com/apple/swift-xcode-playground-support/pull/21)), Swift 5 removal + shim library ([apple/swift#14252](https://github.com/apple/swift/pull/14252), [apple/swift-corelibs-foundation#1415](https://github.com/apple/swift-corelibs-foundation/pull/1415), [apple/swift-xcode-playground-support#20](https://github.com/apple/swift-xcode-playground-support/pull/20)) * Review Manager: [Ben Cohen](https://github.com/airspeedswift/) -* Status: **Active review (February 1...8)** +* Review thread: [SE-0198 review](https://forums.swift.org/t/se-0198-playground-quicklook-api-revamp/9448/16) +* Status: **Accepted** ## Introduction -Currently, adding a new case to an enum is a source-breaking change, which is very inconvenient for library authors. This proposal aims to distinguish between enums that are _frozen_ (meaning they will never get any new cases) and those that are _non-frozen,_ and to ensure that clients handle any future cases when dealing with the latter. Some key notes: +Currently, adding a new case to an enum is a source-breaking change, something that's at odds with Apple's established process for evolving APIs. This proposal aims to distinguish between enums that are _frozen_ (meaning they will never get any new cases) and those that are _non-frozen,_ and to ensure that clients handle any future cases when dealing with the latter. -- This only affects `public` enums. -- With rare exceptions, this does not affect `switch` statements in the same target as the enum. +A key note: in this version of the proposal, *nothing changes for user-defined Swift enums.* This only affects C enums and enums in the standard library and overlays today. (This refers to libraries that Apple could hypothetically ship with its OSs, as it does with Foundation.framework and the Objective-C runtime.) The features described here may be used by third-party libraries in the future. ### Differences from the first revision -- `unknown case` has been added, to preserve exhaustivity checking -- The name of the attribute is now `@frozen` rather than `@exhaustive` +- [This now only affects C enums and enums defined in the standard library and overlays](https://forums.swift.org/t/se-0192-non-exhaustive-enums/7291/337) +- The `unknown` case has been added, to preserve exhaustivity checking +- The term used to describe enums that will not change is now "frozen" rather than "exhaustive" - The proposal now describes what will happen if you "break the contract" in a new library version - Much more discussion of future directions and alternatives considered @@ -38,9 +37,9 @@ Thanks to everyone who offered feedback! ## Motivation -It's well-established that many enums need to grow new cases in new versions of a library. For example, in last year's release of iOS 10, Foundation's [DateComponentsFormatter.UnitsStyle][] gained a `brief` case and UIKit's [UIKeyboardType][] gained an `asciiCapableNumberPad` case. Large error enums also often grow new cases to go with new operations supported by the library. This all implies that library authors *must* have a way to add new cases to enums. +It's well-established that many enums need to grow new cases in new versions of a library. For example, in last year's release of iOS 10, Foundation's [DateComponentsFormatter.UnitsStyle][] gained a `brief` case and UIKit's [UIKeyboardType][] gained an `asciiCapableNumberPad` case. Large error enums also often grow new cases to go with new operations supported by the library. This all implies that library authors *must* have a way to add new cases to enums without breaking binary compatibility. -At the same time, we really like that you can exhaustively switch over enums. This feature helps prevent bugs and makes it possible to enforce [definitive initialization][DI] without having `default` cases in every `switch`. So we don't want to get rid of enums where every case is known, either. This calls for a new annotation that can distinguish between enums where every case can be known statically and enums that might grow new cases in the future, which will be applied to enums defined in Swift as well as those imported from C and Objective-C. +At the same time, we really like that you can exhaustively switch over enums. This feature helps prevent bugs and makes it possible to enforce [definitive initialization][DI] without having `default` cases in every `switch`. So we don't want to get rid of enums where every case is known, either. This calls for a distinction between enums where every case can be known statically and enums that might grow new cases in the future. To see how this distinction will play out in practice, I investigated the public headers of Foundation in the macOS SDK. Out of all 60 or so `NS_ENUM`s in Foundation, only 6 of them are clearly intended to be switched exhaustively: @@ -59,47 +58,16 @@ To see how this distinction will play out in practice, I investigated the public ## Proposed solution -In Swift 5, public enums can be declared as `@frozen`; public enums without this attribute are *non-frozen.* (Grammatical note: they are not "unfrozen" because that implies that they were frozen at one point.) +In Swift 4.2, enums imported from C and enums defined in the standard library and overlays are either *frozen* or *non-frozen.* (Grammatical note: they are not "unfrozen" because that implies that they were frozen at one point.) -When a client tries to switch over a non-frozen enum, they must include either a `default` case or a new `unknown` case unless the enum is declared in the same module as the switch. (This is only relevant across module boundaries because otherwise the compiler knows that the developer is able to update all use sites.) In Swift 4 mode, omitting this case will result in a warning; in Swift 5, it will be an error. +When a client tries to switch over a non-frozen enum, they must include either a `default` case or a new `unknown` case. In Swift 4 mode, omitting this case will result in a warning; in Swift 5 (when there is a Swift 5), it will be an error. -In Swift 4 mode, all public enums will implicitly be `@frozen` for source compatibility. - -Enums imported from C will be non-frozen by default, with a new C-side annotation to make them `@frozen`. These enums conservatively always have the "cross-module" behavior. +All enums written in Swift outside of the standard library and overlays will implicitly be considered frozen in Swift 4.2. Enums imported from C will be non-frozen by default, with a new C-side annotation to treat them as frozen. ## Detailed design -### Definition-side - -```swift -@frozen public enum GregorianWeekday { - case monday // ISO 8601 says weeks start on Monday - case tuesday - case wednesday - case thursday - case friday - case saturday - case sunday -} - -// Defaults to "non-frozen" in Swift 5. -public enum HomeworkExcuse { - case eatenByPet - case thoughtItWasDueNextWeek -} -``` - -A public enum can now be declared `@frozen`. This attribute is implicitly added to public enums in Swift 4 mode; writing it explicitly is ignored. There is further discussion of these defaults in the "Default behavior" section below. - -A warning is emitted when using `@frozen` on a non-public enum, since it has no effect within a module. - -The naming and spelling of this annotation is discussed in the "Alternatives considered" section at the end of this proposal. - - -### Use-side - -When a non-frozen enum defined in module A is used **from another module**, any switch statement that matches against it must include a catch-all case (`default`, an "ignore" `_` pattern, or the new `unknown` case described below). +When switching over a non-frozen enum, the switch statement that matches against it must include a catch-all case (`default`, an "ignore" `_` pattern, or the new `unknown` case described below). ```swift switch excuse { @@ -112,11 +80,7 @@ case .thoughtItWasDueNextWeek: In Swift 5, this would be an error. To maintain source compatibility, this would only produce a warning in Swift 4 mode. The Swift 4 program will trap at run time if an unknown enum case is actually encountered. -To simplify a common use case, enums from modules imported as `@testable` will always be treated as frozen as well. - -All other uses of enums (`if case`, creation, accessing members, etc) do not change. Only the exhaustiveness checking of switches is affected by `@frozen`, and then only across module boundaries. Non-exhaustive switches over `@frozen` enums (and boolean values) will continue to be invalid in all language modes. - -> Note: Once Swift supports cross-module inlinable functions, switch statements in such functions will also need to provide a catch-all case, even for non-frozen enums declared in the same module. +All other uses of enums (`if case`, creation, accessing members, etc) do not change. Only the exhaustiveness checking of switches is affected by the frozen/non-frozen distinction. Non-exhaustive switches over frozen enums (and boolean values) will continue to be invalid in all language modes. Here's a more complicated example: @@ -134,9 +98,9 @@ case (_, false): This switch handles all *known* patterns, but still doesn't account for the possibility of a new enum case when the second tuple element is `true`. This should be an error in Swift 5 and a warning in Swift 4, like the first example. -### `unknown case` +### `unknown` cases -The downside of using a `default` case is that the compiler can no longer alert a developer that a particular enum has elements that aren't explicitly handled in the `switch`. To remedy this, `switch` will be augmented with a new kind of case, spelled `unknown case`. +The downside of using a `default` case is that the compiler can no longer alert a developer that a particular enum has elements that aren't explicitly handled in the `switch`. To remedy this, `switch` will be augmented with a new kind of case, spelled `unknown`. ```swift switch excuse { @@ -144,20 +108,22 @@ case .eatenByPet: // … case .thoughtItWasDueNextWeek: // … -unknown case: +unknown: // … } ``` -Like `default`, `unknown case` matches any value. However, unlike `default` (and the "ignore" pattern `_`), the compiler will produce a *warning* if all known elements of the enum have not already been matched. This is a warning rather than an error so that adding new elements to the enum remains a source-compatible change. (This is also why `unknown case` matches any value rather than just those not seen at compile-time.) +Like `default`, `unknown` matches any value; it is a "catch-all" case. However, unlike `default` (and the "ignore" pattern `_`), the compiler will produce a *warning* if all known elements of the enum have not already been matched. This is a warning rather than an error so that adding new elements to the enum remains a source-compatible change. (This is also why `unknown` matches any value rather than just those not seen at compile-time.) + +`unknown` must be the last case in a `switch`. This is not strictly necessary, but it is consistent with `default`. This restriction is discussed further in the "`unknown` patterns" section under "Future directions". -`unknown case` must be the last case in a `switch`. This is not strictly necessary, but it is consistent with `default`. This restriction is discussed further in the "`unknown case` patterns" section under "Future directions". +It is an error to use `unknown` with a pattern that does not contain any enums at all. -The compiler will warn if the enum being matched by `unknown case` is `@frozen`. This is a warning rather than an error so that adding `@frozen` to the enum remains a source-compatible change. It is an error to use `unknown case` with a non-enum-typed value. +The compiler will warn if all enums in the pattern being matched by `unknown` are explicitly annotated as frozen. This is a warning rather than an error so that annotating an enum as frozen remains a source-compatible change. If the pattern contains any enums that are implicitly frozen (i.e. because it is a user-defined Swift enum), `unknown` is permitted, in order to make it easier to adapt to newly-added cases. -A `switch` may not have both a `default` case and an `unknown case`. Since both patterns match any value, whichever pattern was written first would be chosen, making the other unreachable. This restriction is discussed further under "Alternatives considered". +A `switch` may not have both a `default` case and an `unknown` case. Since both patterns match any value, whichever pattern was written first would be chosen, making the other unreachable. This restriction is discussed further under "Alternatives considered". -`unknown case` has a downside that it is not testable, since there is no way to create an enum value that does not match any known cases, and there wouldn't be a safe way to use it if there was one. However, combining `unknown case` with other cases using `fallthrough` can get the effect of following another case's behavior while still getting compiler warnings for new cases. +`unknown` has a downside that it is not testable, since there is no way to create an enum value that does not match any known cases, and there wouldn't be a safe way to use it if there was one. However, combining `unknown` with other cases using `fallthrough` can get the effect of following another case's behavior while still getting compiler warnings for new cases. ```swift switch excuse { @@ -166,38 +132,35 @@ case .eatenByPet: case .thoughtItWasDueNextWeek: fallthrough -unknown case: +unknown: askForDueDateExtension() } ``` -The name `unknown case` was chosen not conflict with any existing valid Swift code. Discussion of the naming for this case is included at the end of the proposal under "Alternatives Considered". - - -### Default behavior - -Making "non-frozen" the default behavior was not a lightly-made decision. There are two obvious alternatives here: leave `@frozen` as the default, and have *no* default, at least in Swift 5 mode. An early version of this proposal went with the latter, but got significant pushback for making public enums more complicated than just adding `public`. This argues for having *some* default. - -The use cases for public enums fall into three main categories: - -| Use Case | Frozen | Non-frozen | -|--------------------------------|----------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------| -| Multi-module app | The desired behavior. Compiler can find all clients if the enum becomes non-frozen. | Compiler can find all clients if the enum becomes frozen. | -| Open-source library (SwiftPM) | Changing to non-frozen is a source-breaking change; it produces errors in any clients. | Changing to frozen produces warnings in any clients. | -| ABI-stable library (Apple OSs) | **Cannot** change to non-frozen; it would break binary compatibility. | Changing to frozen produces warnings in clients (probably dependent on deployment target). | - -Although multi-module apps are likely responsible for most uses of `public`, they also provide the environment in which it is easiest to make changes, since both the "library" and the "client" are part of the same project. For actual libraries, "non-frozen" is a much better place to start; if it is a mistake, a minor release of the library can fix the issue without requiring immediate source changes in clients. - -Defaulting to non-frozen in Swift 5 is effectively a language change from Swift 4, where all enums were treated as frozen. This does require care when manually migrating code from Swift 4 to Swift 5, or when copying existing example code from online into a Swift 5 module. However, this still only affects situations where an enum is (1) public and (2) switched over (3) from another module, and even when this *does* occur it is still reasonable to fix. - -> This was one of the most controversial parts of the proposal. In the original swift-evolution thread, Rex Fenley [summarized the downsides][downsides] pretty well. Rather than present a simplified view of the concerns, I suggest reading his email directly. - - [downsides]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170918/039867.html +> Sharp-eyed readers (and those who saw the intermediate version of this proposal) will note that this syntax conflicts with a possible use of labeled `break` or `continue`: +> +> ```swift +> switch excuse { +> case .eatenByPet: +> // … +> case .thoughtItWasDueNextWeek: +> complainLoudly() +> unknown: for next in contrivedExamples { +> for inner in moreContrivedExamples { +> if inner.use(next) { +> break unknown +> } +> } +> } +> } +> ``` +> +> The likelihood of this coming up in practice, however, is exceedingly small, and furthermore it's something we can diagnose pretty clearly as soon as the compiler sees the `break unknown`. Further discussion of the naming for this case is included at the end of the proposal under "Alternatives Considered". ### C enums -Enums imported from C are a bit trickier, because it's difficult to tell whether they're part of the current project or not. An `NS_ENUM` in Apple's SDK should probably be treated as non-frozen, but one in your own framework might be frozen. Even there, though, it's possible that there's a "private case" defined in a .m file: +Enums imported from C are tricky, because it's difficult to tell whether they're part of the current project or not. An `NS_ENUM` in Apple's SDK should probably be treated as non-frozen, but one in your own framework might be frozen. Even there, though, it's possible that there's a "private case" defined in a .m file: ```objc // MyAppPaperSupport.h @@ -235,13 +198,66 @@ typedef NS_ENUM(NSInteger, GregorianMonth) { Apple doesn't speak about future plans for its SDKs, so having an alternate form of `NS_ENUM` that includes this attribute is out of scope for this proposal. -This change will affect code *even in Swift 4 mode* (although it will only produce warnings there), so to ease the transition otherwise-unannotated C enums will continue to be `@frozen` until Swift 5 is released. That is, all Swift 4.x compilers will treat unannotated `NS_ENUM` declarations as frozen; a Swift 5 compiler with a Swift 4 mode will treat them as non-frozen. - -Apart from the effect on switches, an imported `@frozen` enum's `init(rawValue:)` will also enforce that the case is one of those known at compile time. Imported non-frozen enums will continue to perform no checking on the raw value. +Apart from the effect on switches, a frozen C enum's `init(rawValue:)` will also enforce that the case is one of those known at compile time. Imported non-frozen enums will continue to perform no checking on the raw value. > This section only applies to enums that Swift considers "true enums", rather than option sets or funny integer values. In the past, the only way to get this behavior was to use the `NS_ENUM` or `CF_ENUM` macros, but the presence of `enum_extensibility(closed)` *or* `enum_extensibility(open)` will instruct Swift to treat the enum as a "true enum". Similarly, the newly-added `flag_enum` C attribute can be used to signify an option set like `NS_OPTIONS`. +### Effect on the standard library and overlays + +The majority of enums defined in the standard library do not need the flexibility afforded by being non-frozen, and so will be marked as frozen. This includes the following enums: + +- ❄️ ClosedRange.Index +- ❄️ FloatingPointSign +- ❄️ FloatingPointClassification +- ❄️ Never +- ❄️ Optional +- ❄️ UnicodeDecodingResult +- ❄️ Unicode.ParseResult + +The following public enums in the standard library will *not* be marked as frozen: + +- DecodingError +- EncodingError +- FloatingPointRoundingRule +- Mirror.AncestorRepresentation +- Mirror.DisplayStyle +- PlaygroundQuickLook (deprecated anyway) + +And while the overlays are not strictly part of the Swift Open Source project (since they are owned by framework teams at Apple), the tentative plan would be to mark these two enums as frozen: + +- ❄️ ARCamera.TrackingState (a tri-state of "on", "off", and "limited(Reason)") +- ❄️ DispatchTimeoutResult ("success" and "timed out") + +And the other public enums in the overlays would be non-frozen: + +- ARCamera.TrackingState.Reason +- Calendar.Component +- Calendar.Identifier +- Calendar.MatchingPolicy +- Calendar.RepeatedTimePolicy +- Calendar.SearchDirection +- CGPathFillRule +- Data.Deallocator +- DispatchData.Deallocator +- DispatchIO.StreamType +- DispatchPredicate +- DispatchQoS.QoSClass +- DispatchQueue.AutoreleaseFrequency +- DispatchQueue.GlobalQueuePriority (deprecated anyway) +- DispatchTimeInterval +- JSONDecoder.DataDecodingStrategy +- JSONDecoder.DateDecodingStrategy +- JSONDecoder.KeyDecodingStrategy +- JSONDecoder.NonConformingFloatDecodingStrategy +- JSONEncoder.DataEncodingStrategy +- JSONEncoder.DateEncodingStrategy +- JSONEncoder.KeyEncodingStrategy +- JSONEncoder.NonConformingFloatEncodingStrategy +- MachErrorCode +- POSIXErrorCode + + ## Comparison with other languages "Enums", "unions", "variant types", "sum types", or "algebraic data types" are present in a number of other modern languages, most of which don't seem to treat this as an important problem. @@ -277,7 +293,9 @@ Enums in **D** are like enums in C, but D distinguishes `switch` from `final swi ## Source compatibility -It is now a source-compatible change to add a case to a public non-frozen enum. +It is now a source-compatible change to add a case to a non-frozen enum (whether imported from C or defined in the standard library). + +It is not a source-compatible change to add a case to a frozen enum. It is still not a source-compatible change to remove a case from a public enum (frozen or non-frozen). @@ -286,23 +304,21 @@ It is a source-compatible change to change a non-frozen enum into a frozen enum, ### Breaking the contract -If a library author adds a case to an enum marked `@frozen`, any existing switches will likely not handle this new case. The compiler will produce an error for any such switch (i.e. those without a `default` case or `_` pattern to match the enum value), noting that the case is unhandled; this is the same error that is produced for a non-exhaustive switch in Swift 4. +If a library author adds a case to a frozen enum, any existing switches will likely not handle this new case. The compiler will produce an error for any such switch (i.e. those without a `default` case or `_` pattern to match the enum value), noting that the case is unhandled; this is the same error that is produced for a non-exhaustive switch in Swift 4. -If a library author removes `@frozen` from an enum previously marked `@frozen`, the compiler will likewise produce an error for any switch that does not have a `default` case or `_` pattern, noting that it needs either a `default` case or `unknown case`. This is the same error that people will see when porting code from Swift 4 to Swift 5, so it needs to explain the issue clearly and offer a clear recommendation of what to do. +If a library author changes an enum previously marked frozen to make it non-frozen, the compiler will likewise produce an error for any switch that does not have a `default` case or `_` pattern, noting that it needs either a `default` or `unknown` case. ## Effect on ABI stability -Currently, the layout of a public enum is known at compile time in both the defining library and in its clients. For a library concerned about binary compatibility, the layout of a non-frozen enum must not be exposed to clients, since the library may choose to add a new case that does not fit in that layout in its next release. +The layout of a non-frozen Swift enum must not be exposed to clients, since the library may choose to add a new case that does not fit in that layout in its next release. This results in extra indirection when that enum appears in public API. The layout of a frozen enum will continue to be made available to clients for optimization purposes. -This change does not affect the layout of `@objc` enums, which always have the same representation as a similarly-defined C enum. (Note that the representation of a non-`@objc` enum's case may differ from its raw value; this improves the efficiency of `switch` statements when all cases are known at compile time.) - -These considerations should not affect libraries shipped with their clients, including SwiftPM packages. In these cases, the compiler is always free to optimize based on the layout of an enum because the library won't change. +This change does not affect the layout of `@objc` enums, whether imported from C or defined in Swift. (Note that the representation of a non-`@objc` enum's case may differ from its raw value; this improves the efficiency of `switch` statements when all cases are known at compile time.) ## Effect on Library Evolution -It is now a binary-compatible change to add a case to a public non-frozen enum. +It is now a binary-compatible change to add a case to a non-frozen enum. It is still not a binary-compatible change to remove a case from a public enum (frozen or non-frozen). @@ -313,16 +329,23 @@ Taking an existing non-frozen enum and making it frozen is something we'd like t ### Breaking the contract -Because the compiler uses the set of cases in a frozen enum to determine its in-memory representation and calling convention, adding a new case or removing `@frozen` from an enum in a library will result in "undefined behavior" from any client apps that have not been recompiled. This means a loss of memory-safety and type-safety on par with a misuse of "unsafe" types, which would most likely lead to crashes but could lead to code unexpectedly being executed or skipped. In short, things would be very bad. +Because the compiler uses the set of cases in a frozen enum to determine its in-memory representation and calling convention, adding a new case or marking such an enum as non-frozen will result in "undefined behavior" from any client apps that have not been recompiled. This means a loss of memory-safety and type-safety on par with a misuse of "unsafe" types, which would most likely lead to crashes but could lead to code unexpectedly being executed or skipped. In short, things would be very bad. Some ideas for how to prevent library authors from breaking the rules accidentally are discussed in "Compatibility checking" under "Future directions". +As a special case, switching over an unexpected value in an `@objc` enum (whether imported or defined in Swift) will always result in a trap rather than "undefined behavior", even if the enum is frozen. + ## Future directions -### `unknown case` patterns +### Non-frozen Swift enums outside the standard library + +Earlier versions of this proposal included syntax that allowed *all* public Swift enums to have a frozen/non-frozen distinction, rather than just those in the standard library and overlays. This is still something we want to support, but the core team has made it clear that such a distinction is only worth it for libraries that have binary compatibility concerns (such as those installed into a standard location and used by multiple clients), at least without a more developed notion of versioning and version-locking. Exactly what it means to be a "library with binary compatibility concerns" is a large topic that deserves its own proposal. + -As described, `unknown case` can only be used when switching over a single enum value; it does not work when trying to match a tuple element, or another enum's associated type. In theory, we could make a new *pattern* kind that allows matching unknown cases anywhere within a larger pattern: +### `unknown` patterns + +As described, `unknown` can only be used to match the entire switched value; it does not work when trying to match a tuple element, or another enum's associated type. In theory, we could make a new *pattern* kind that allows matching unknown cases anywhere within a larger pattern: ```swift switch (excuse, notifiedTeacherBeforeDeadline) { @@ -330,49 +353,49 @@ case (.eatenByPet, true): // … case (.thoughtItWasDueNextWeek, true): // … -case (unknown case, true): +case (#unknown, true): // … case (_, false): // … } ``` -However, I'm not quite sure how the exhaustivity checking falls out here. In the following code, which case is chosen for `(.thoughtItWasDueNextWeek, true)`? +(The `#unknown` spelling is chosen by analogy with `#selector` to not conflict with existing syntax; it is not intended to be a final proposal.) + +However, this produces potentially surprising results when followed by a case that could also match a particular input. Because `unknown` acts as a catch-all, the input `(.thoughtItWasDueNextWeek, true)` would result in case 2 being chosen rather than case 3. ```swift switch (excuse, notifiedTeacherBeforeDeadline) { case (.eatenByPet, true): // 1 // … -case (unknown case, true): // 2 +case (#unknown, true): // 2 // … -case (_, false): // 3 +case (.thoughtItWasDueNextWeek, _): // 3 // … -case (.thoughtItWasDueNextWeek, _): // 4 +case (_, false): // 4 // … } ``` -- Case 2 is plausible because `unknown case` will catch any cases that aren't accounted for in the switch (in order to preserve source stability), and `.thoughtItWasDueNextWeek` hasn't been accounted for *yet.* This makes case 4 unreachable, which had better show up in a compiler warning. - -- Case 4 is also plausible because `.thoughtItWasDueNextWeek` is clearly mentioned in the switch, and therefore it's not "unknown". This is probably what the user intended, but would be much more difficult to implement, and runs into the same trouble as mixing `default` and `unknown case` as described below under "Alternatives considered". +The compiler would warn about this, at least, since there is a known value that can reach the `unknown` pattern. -In the single value situation, `unknown case` must go last to avoid these issues. It's not possible to enforce the same thing for arbitrary patterns because there may be multiple enums in the pattern whose unknown cases need to be treated differently. This also means that it will be more difficult to suggest missing cases in compiler diagnostics, since the cases may be order-dependent. +As a top-level case, `unknown` must go last to avoid this issue. However, it's not possible to enforce the same thing for arbitrary patterns because there may be multiple enums in the pattern whose unknown cases need to be treated differently. -A key point of this discussion is that as proposed `unknown case` merely produces a *warning* when the compiler can see that some enum cases are unhandled, rather than an error. This is what makes it difficult to implement in non-top-level positions. If the compiler produced an error instead, it would make more sense to use a pattern-like syntax for `unknown case` now (see the naming discussions under "Alternatives considered"). However, if the compiler produced an error, then adding a new case would not be a source-compatible change. +A key point of this discussion is that as proposed `unknown` merely produces a *warning* when the compiler can see that some enum cases are unhandled, rather than an error. If the compiler produced an error instead, it would make more sense to use a pattern-like syntax for `unknown` (see the naming discussions under "Alternatives considered"). However, if the compiler produced an error, then adding a new case would not be a source-compatible change. -For all these reasons, generalized `unknown case` patterns are not being included in this proposal. +For these reasons, generalized `unknown` patterns are not being included in this proposal. ### Non-public cases The work required for non-frozen enums also allows for the existence of non-public cases in a public enum. This already shows up in practice in Apple's SDKs, as described briefly in the section on "C enums" above. Like "enum inheritance", this kind of behavior can mostly be emulated by using a second enum inside the library, but that's not sufficient if the non-public values need to be vended opaquely to clients. -Were such a proposal to be written, I advise that a frozen enum not be permitted to have non-public cases. +Were such a proposal to be written, I advise that a frozen enum not be permitted to have non-public cases. An enum in a user-defined library would then be implicitly considered frozen if and only if it had no non-public cases. ### Compatibility checking -Of course, the compiler can't stop a library author from adding a new case to a non-frozen enum, even though that will break source and binary compatibility. We already have two ideas on how we could catch mistakes of this nature: +Of course, the compiler can't stop a library author from adding a new case to a frozen enum, even though that will break source and binary compatibility. We already have two ideas on how we could catch mistakes of this nature: - A checker that can compare APIs across library versions, using swiftmodule files or similar. @@ -425,110 +448,78 @@ For enums with raw types, a 32-bit integer can be used as the representation rat As such, this representation change is out of scope for this proposal. -### Revision-locked imports - -When a client module decides to import a *specific* version of a library, there's no danger of a non-frozen enum changing out from under them. This isn't something that can be done with system libraries, but it could be useful both for libraries you build yourself and for dependencies you plan to ship with your application. A few people have proposed syntax to indicate this: - -```swift -static import ContrivedExampleKit -// or -import ContrivedExampleKit @ 1.20 // verifies the version number somehow -``` - -With this syntax, all public enums in the library will be treated as frozen. (It's questionable whether this also applies to libraries re-exported through the library named here as well.) This syntax would not, of course, be valid to use with libraries that are shipped as part of an OS; those libraries may be updated *without* recompiling the client, and so unknown cases must be handled. - -I'm leery of this being used for packages that depend on other packages, which would keep a client of both libraries from being able to update them each independently. That said, it's useful when you have a *multi-module* package, where the two targets in the package will clearly never get out of sync. (It's also mildly useful for multi-module *applications,* but there you can just mark every public enum as `@frozen` and get the same effect.) - -This is an additive feature that does not affect ABI, and as such could be added to the language in the future. - - ## Alternatives considered -### Syntax +### Terminology and syntax -#### Annotation naming: "closed" and "open" +#### Terminology: "closed" and "open" -The original description of the problem used "closed" and "open" to describe frozen and non-frozen enums, respectively. However, this conflicts with the use of `open` in classes and their members. In this usage, `open` is clearly a greater level of access than `public`, in that clients of an `open` class can do everything they can with a `public` class and more; it is source-compatible to turn a `public` class into an `open` one. For enums, however, it is frozen enums that are "greater": you can do everything you can with a non-frozen enum and more, and it is source-compatible to turn a non-frozen enum into a frozen one (at the cost of a warning). +The original description of the problem used "closed" and "open" to describe frozen and non-frozen enums, respectively. However, this conflicts with the use of `open` in classes and their members. In this usage, `open` is clearly a greater level of access than `public`, in that clients of an `open` class can do everything they can with a `public` class and more; it is source-compatible to turn a `public` class into an `open` one. For enums, however, it is frozen enums that are "greater": you can do everything you can with a non-frozen enum and more, and it would be source-compatible for a standard library contributor to turn a non-frozen enum into a frozen one (at the cost of a warning). -#### Annotation naming: other options +#### Terminology: other options Several more options were suggested during initial discussions: -- `complete` ("incomplete") -- `covered` (?) -- **`exhaustive`** (non-exhaustive) -- `nonextensible` (?) -- `final` (non-final) -- `finite` (non-finite, not "infinite") -- `fixed` (?) -- `locked` (?) -- `sealed` (non-sealed) -- `total` (partial) +- complete / incomplete +- covered +- exhaustive / non-exhaustive +- non-extensible +- final / non-final +- finite / non-finite (not "infinite") +- fixed +- locked +- sealed / non-sealed +- total / partial -I didn't have a strong preference for any particular choice as long as it *isn't* "closed" / "open", for the reasons described above. In the first revision of this proposal I picked `exhaustive` because it matches the name proposed [in Rust][rust]. (Unfortunately, Clang's `enum_extensibility` attribute, recently added by us at Apple, uses `open` and `closed`.) +I didn't have a strong preference for any particular choice as long as it *isn't* "closed" / "open", for the reasons described above. In the first revision of this proposal I picked "exhaustive" because it matches the name proposed [in Rust][rust]. (Unfortunately, Clang's `enum_extensibility` attribute, recently added by us at Apple, uses `open` and `closed`.) Note that "nonextensible" does have one problem: Apple already uses [`NS_TYPED_EXTENSIBLE_ENUM `][NS_TYPED_EXTENSIBLE_ENUM] to refer to enum-like sets of constants (usually strings) that *clients* can add "cases" to. That's not the same meaning as the exhaustiveness discussed in this proposal. -During the first review for this proposal, Brent Royal-Gordon suggested `@frozen`, which was met with general approval or at least no major objections. +During the first review for this proposal, Brent Royal-Gordon suggested "frozen", which was met with general approval or at least no major objections. [NS_TYPED_EXTENSIBLE_ENUM]: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/doc/uid/TP40014216-CH8-ID206 -#### Modifier or attribute? - -This proposal suggests a new *attribute* for enums, `@frozen`; it could also be a modifier `frozen`, implemented as a context-sensitive keyword. The original version of the proposal went with a modifier because most attributes only affect the *definition* of an API, not its use, but in preliminary discussions the core team felt that an attribute was a better fit. - +#### `unknown` naming -#### Annotation or member? - -In addition to the attribute approach detailed in this proposal, discussion on swift-evolution also suggested mirroring the form of a `switch` statement by using an additional kind of declaration inside an enum: - -```swift -public enum HomeworkExcuse { - case eatenByPet - case thoughtItWasDueNextWeek - default // NEW -} -``` +The first version of this proposal did not include `unknown`, but did discuss it as a "considered alternative" under the name `future`. Previous discussions have also used `unexpected` or `undeclared` to describe this feature as well. -`continue` and `final` were also suggested for this additional declaration. I'm not inherently against this approach, but it does seem a little harder to spot when looking at the generated interface for a library. In preliminary discussions, the core team was not particularly fond of this approach, however. +It was pointed out that neither `future` nor `unexpected` really described the feature being provided. `unknown` does not just handle cases added in the future; it also handles private cases and invalid values for C enums. Nor are such cases entirely unexpected, since the compiler is telling the developer to expect them. `undeclared` has fewer issues, but certainly private cases can be declared *somewhere;* the declarations just aren't visible. +The "intermediate" revision of this proposal where `unknown` was first added used the spelling `unknown case`, but restricted the new case to only match values that *were* enums rather than values *containing* enums. When that restriction was loosened, the reading of `unknown case` as "(enum) cases that I don't know about" no longer made as much sense. -#### `unknown case` naming +During discussion, the name `unknown default` (or `@unknown default`) was suggested as an alternative to `unknown case`, since the semantics behave very much like `default`. However, it isn't the "default" that's "unknown". Other proposed spellings included `default unknown` (a simple attempt to avoid reading "unknown" as an adjective modifying "default") and `default(unknown)` (by analogy with `private(set)`). -The first version of this proposal did not include `unknown case`, but did discuss it as a "considered alternative" under the name `future`. Previous discussions have also used `unexpected`, `undeclared`, or a plain `unknown` to describe this feature as well. +Moving away from "unknown", `@unused default` was also suggested, but the case is *not* unused. A more accurate `@runtimeReachableOnly` (or even `@runtimeOnly`) was proposed instead, but that's starting to get overly verbose for something that will appear reasonably often. -It was pointed out that neither `future` nor `unexpected` really described the feature being provided. `unknown case` does not just handle cases added in the future; it also handles private cases and invalid values for C enums. Nor are such cases entirely unexpected, since the compiler is telling the developer to expect them. `undeclared` has fewer issues, but certainly private cases are declared *somewhere;* the declarations just aren't visible. +For standalone names, `fallback` was also suggested, but semantically that seems a little too close to "default", and in terms of actual keywords it was pointed out that this was very similar to `fallthrough` despite having no relation. `invisible` was suggested as well (though in the context of patterns rather than cases), but that doesn't exactly apply to future cases. -It would be nice™ if we could just spell this new case `unknown`. However, that's ambiguous with the existing syntax for labeled control-flow statements: +To summarize, the following spellings were considered for `unknown`: -```swift -switch excuse { -case .eatenByPet: - // … -case .thoughtItWasDueNextWeek: - complainLoudly() - unknown: for next in contrivedExamples { - for inner in moreContrivedExamples { - if inner.use(next) { - break unknown - } - } - } -} -``` +- `future:` +- `unexpected:` +- `undeclared:` +- `unknown case:` +- `unknown default:` +- `@unknown default:` +- `@unused default:` +- `@runtimeReachableOnly default:` +- `default unknown:` +- `default(unknown):` +- `fallback:` +- `invisible:` -While it is *unlikely* that someone would use `unknown` for a control-flow label, it is possible. Making `unknown` a contextual keyword before `case` preserves this source compatibility. +and I picked `unknown:` as the best option, admittedly as much for *not* having *unwanted* connotations as for having *good* connotations. -During discussion, the name `unknown default` was suggested as an alternative to `unknown case`, since the semantics behave very much like `default`. I went with `unknown case` because it's a little shorter and a little more descriptive ("this is what to do with cases---as in enum cases---that I don't know about right now"), but I'm not too attached to it. +A bigger change would be to make a custom *pattern* instead of a custom *case,* even if it were subject to the same restrictions in implementation (see "`unknown` patterns" above). This usually meant using a symbol of some kind to distinguish the "unknown" from a normal label or pattern, leading to `case #unknown` or similar. This makes the new feature less special, since it's "just another pattern". However, it would be surprising to have such a pattern but keep the restrictions described in this proposal; thus, it would only make sense to do this if we were going to implement fully general pattern-matching for this feature. See "`unknown` patterns" above for more discussion. -A bigger change would be to make a custom *pattern* instead of a custom *case,* even if it were subject to the same restrictions in implementation (see "`unknown case` patterns" above). This usually meant using a symbol of some kind to distinguish the "unknown" from a normal label or pattern, leading to `case #unknown` or similar. This makes the new feature less special, since it's "just another pattern". However, it would be surprising to have such a pattern but keep the restrictions described in this proposal; thus, it would only make sense to do this if we were going to implement fully general pattern-matching for this feature. See "`unknown case` patterns" above for more discussion. +Finally, there was the option to put an annotation on a `switch` instead of customizing the catch-all case, e.g. `@warnUnknownCases switch x {`. This is implementable but feels easier for a developer to forget to write, and the compiler can only help if the developer actually *has* implemented all of the current cases alongside their `default` case. ### `switch!` -`switch!` was an alternative to `unknown case` that would not support any action other than trapping when the enum is not one of the known cases. This avoids some of the problems with `unknown case` (such as making it much less important to test), but isn't exactly in the spirit of non-frozen enums, where you *know* there will be more cases in the future. +`switch!` was an alternative to `unknown` that would not support any action other than trapping when the enum is not one of the known cases. This avoids some of the problems with `unknown` (such as making it much less important to test), but isn't exactly in the spirit of non-frozen enums, where you *know* there will be more cases in the future. The following two examples would be equivalent (except perhaps in the form of the diagnostic produced). @@ -547,7 +538,7 @@ case .eatenByPet: // … case .thoughtItWasDueNextWeek: // … -unknown case: +unknown: fatalError("unknown case in switch: \(excuse)") } ``` @@ -575,28 +566,23 @@ override func process(_ transaction: @testable Transaction) { This is an additive feature, so we can come back and consider it in more detail even if we leave it out of the language for now. Meanwhile, the effect can be imitated using an Optional or ImplicitlyUnwrappedOptional parameter. -### Implicitly treat enums without binary compatibility concerns as `@frozen` - -Several people questioned whether it was necessary to make this distinction for libraries without binary compatibility concerns, i.e. those that are shipped with their client apps. While there may be a need to do something to handle enums shipped with Apple's OS SDKs, it's arguable whether this is worth it for "source libraries", such as SwiftPM packages. +### Allow enums defined in source packages to be considered non-frozen -This question can be rephrased as "is adding a case to an enum a source-breaking change?" The distinction between frozen and non-frozen enums puts that choice in the hands of the library author, with the default answer—the one that does not require extra annotation—being "no, it is not". If adding a new enum case is not a source-breaking change, then it can be done in a minor version release of a library, one intended to be backwards-compatible. Like deprecations, this can produce new warnings, but not new errors, and it should not (if done carefully) break existing code. This isn't a critical feature for a language to have, but I would argue that it's a useful one for library developers. +The first version of this proposal applied the frozen/non-frozen distinction to all public enums, even those in user-defined libraries. The motivation for this was to allow package authors to add cases to their enums without it being a source-breaking change, meaning it can be done in a minor version release of a library (i.e. one intended to be backwards-compatible). Like deprecations, this can produce new warnings, but not new errors, and it should not (if done carefully) break existing code. -> I wrote a [longer response] to this idea to Dave DeLong on the swift-evolution list. Dave [wasn't entirely convinced][delong-response]. +The core team decided that this feature was not worth the disruption and long-term inconvenience it would cause for users who did not care about this capability. - [longer response]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20180101/042530.html - [delong-response]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20180101/042549.html +### Leave out `unknown` -### Leave out `unknown case` - -The [initial version][] of this proposal did not include `unknown case`, and required people to use `default` to handle cases added in the future instead. However, many people were unhappy with the loss of exhaustivity checking for `switch` statements, both for enums in libraries distributed as source and enums imported from Apple's SDKs. While this is an additive feature that does not affect ABI, it seems to be one that the community considers a necessary part of a language model that provides non-frozen enums. +The [initial version][] of this proposal did not include `unknown`, and required people to use `default` to handle cases added in the future instead. However, many people were unhappy with the loss of exhaustivity checking for `switch` statements, both for enums in libraries distributed as source and enums imported from Apple's SDKs. While this is an additive feature that does not affect ABI, it seems to be one that the community considers a necessary part of a language model that provides non-frozen enums. [initial version]: https://github.com/apple/swift-evolution/blob/a773d07ff4beab8b7855adf0ac56d1e13bb7b44c/proposals/0192-non-exhaustive-enums.md -### Mixing `unknown case` and `default` +### Mixing `unknown` and `default` -The proposal as written forbids including both `unknown case` and `default` in the same `switch`. It's certainly possible to define what it would mean: +The proposal as written forbids including both `unknown` and `default` in the same `switch`. Most people would expect this to have the following behavior: ```swift switch excuse { @@ -610,23 +596,18 @@ default: } ``` -However, I can't think of an actual use case for this; it's not clear what action one would take in the `unknown case` that they wouldn't take in the `default` case. Furthermore, this becomes a situation where the same code behaves differently before and after recompilation: +However, I can't think of an actual use case for this; it's not clear what action one would take in the `unknown` that they wouldn't take in the `default` case. Furthermore, this becomes a situation where the same code behaves differently before and after recompilation: 1. A new case is added to the HomeworkExcuse enum, say, `droppedInMud`. -2. When using the new version of the library with an existing built client app, the `droppedInMud` case will end up in the `unknown case` part of the `switch`. +2. When using the new version of the library with an existing built client app, the `droppedInMud` case will end up in the `unknown` part of the `switch`. 3. When the client app *is* recompiled, the `droppedInMud` case will end up in the `default` case. The compiler will not (and cannot) provide any indication that the behavior has changed. Without a resolution to these concerns, this feature does not seem worth including in the proposal. It's also additive and has no ABI impact, so if we do find use cases for it in the future we can always add it then. -### Non-frozen enums in Swift 4 mode - -This proposal provides no way to declare non-frozen enums in Swift 4 mode. We would need to introduce a new attribute (`@nonfrozen`) to allow that. Since we expect people to move projects to Swift 5 over time, however, this isn't a long-term concern. Not every new feature needs to be available in Swift 4 mode, and the proposal is simpler without a negative attribute. - - ### Introduce a new declaration kind instead -There have been a few suggestions to distinguish `enum` from some other kind of declaration that acts similarly but allows adding cases, which would avoid breaking compatibility with Swift 4: +There have been a few suggestions to distinguish `enum` from some other kind of declaration that acts similarly but allows adding cases: ```swift choices HomeworkExcuse { @@ -635,11 +616,11 @@ choices HomeworkExcuse { } ``` -My biggest concern with this is that it increases the possibility of a library author accidentally publishing a (frozen) `enum` when they meant to publish a (non-frozen) `choices`. As described above, the opposite mistake is one that can be corrected without breaking source compatibility, but this one cannot. +My biggest concern with this is that if we ever *do* expand this beyond the standard library and overlays, it increases the possibility of a library author accidentally publishing a (frozen) `enum` when they meant to publish a (non-frozen) `choices`. As described above, the opposite mistake is one that can be corrected without breaking source compatibility, but this one cannot. A smaller concern is that both `enum` and `choices` would behave the same when they *aren't* `public`. -Stepping back, increasing the surface area of the language in this way does not seem desirable. Exhaustivity has been a key part of how Swift enums work, but it is not their only feature. Given how people already struggle with the decision of "struct vs. class" when defining a new type, introducing another pair of "similar but different" declaration kinds would have to come with strong benefits. +Stepping back, increasing the surface area of the language in this way does not seem desirable. Exhaustive switching has been a key part of how Swift enums work, but it is not their only feature. Given how people already struggle with the decision of "struct vs. class" when defining a new type, introducing another pair of "similar but different" declaration kinds would have to come with strong benefits. My conclusion is that it is better to think of frozen and non-frozen enums as two variants of the same declaration kind, rather than as two different declaration kinds. @@ -648,7 +629,7 @@ My conclusion is that it is better to think of frozen and non-frozen enums as tw Everything you can do with non-frozen enums, you can do with protocols as well, except for: -- exhaustivity checking with `unknown case` +- exhaustivity checking with `unknown` - forbidding others from adding their own "cases" ```swift @@ -675,7 +656,7 @@ This is a valid model; it's close to what Scala does (as mentioned above), and i ### Import non-frozen C enums as RawRepresentable structs -The Swift compiler already makes a distinction between plain C enums, enums marked with the `flag_enum` Clang attribute (`NS_OPTIONS`), and enums marked with the `enum_extensibility` Clang attribute (`NS_ENUM`). The first two categories were deemed to not be sufficiently similar to Swift enums and are imported instead as structs. Given that we're most immediately concerned about *C* enums growing new cases (specifically, those in Apple's existing Objective-C SDKs), we could sidestep the problem by importing *all* C enums as structs except for those marked `enum_extensibility(closed)`. However, this doesn't solve the problem for future Swift libraries, while still requiring changes to existing `switch` statements across many many projects. Furthermore, it would probably be harder to implement high-quality migration support from Swift 4 to Swift 5, since the structs-formerly-enums will look like any other structs imported from C. +The Swift compiler already makes a distinction between plain C enums, enums marked with the `flag_enum` Clang attribute (`NS_OPTIONS`), and enums marked with the `enum_extensibility` Clang attribute (`NS_ENUM`). The first two categories were deemed to not be sufficiently similar to Swift enums and are imported instead as structs. Given that we're most immediately concerned about *C* enums growing new cases (specifically, those in Apple's existing Objective-C SDKs), we could sidestep the problem by importing *all* C enums as structs except for those marked `enum_extensibility(closed)`. However, this doesn't solve the problem for future Swift libraries, while still requiring changes to existing `switch` statements across many many projects, and it doesn't support the exhaustivity checking provided by `unknown`. Furthermore, it would probably be harder to implement high-quality migration support from Swift 4 to Swift 5, since the structs-formerly-enums will look like any other structs imported from C. ### Get Apple to stop adding new cases to C enums From bf0083256f5a87512cc0b27373886d2d21eb9b56 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Fri, 16 Mar 2018 07:22:51 -0700 Subject: [PATCH 0597/4563] Extend review of SE-0192 for a second phase. --- proposals/0192-non-exhaustive-enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0192-non-exhaustive-enums.md b/proposals/0192-non-exhaustive-enums.md index 1bd4242535..4ac99a1fb2 100644 --- a/proposals/0192-non-exhaustive-enums.md +++ b/proposals/0192-non-exhaustive-enums.md @@ -3,7 +3,7 @@ * Proposal: [SE-0192](0192-non-exhaustive-enums.md) * Authors: [Jordan Rose](https://github.com/jrose-apple) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Returned for revision** +* Status: **March 16 - 23, 2018** * Implementation: [apple/swift#14945](https://github.com/apple/swift/pull/14945) * Previous revision: [1](https://github.com/apple/swift-evolution/blob/a773d07ff4beab8b7855adf0ac56d1e13bb7b44c/proposals/0192-non-exhaustive-enums.md), [2 (informal)](https://github.com/jrose-apple/swift-evolution/blob/57dfa2408fe210ed1d5a1251f331045b988ee2f0/proposals/0192-non-exhaustive-enums.md) * Pre-review discussion: [Enums and Source Compatibility](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170807/038663.html), with additional [orphaned thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170911/039787.html) From f6f750b272ca1b9ee86ac8239e965029e63be1b0 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Fri, 16 Mar 2018 07:39:22 -0700 Subject: [PATCH 0598/4563] Fix review date format for 192 --- proposals/0192-non-exhaustive-enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0192-non-exhaustive-enums.md b/proposals/0192-non-exhaustive-enums.md index 4ac99a1fb2..8465f19a22 100644 --- a/proposals/0192-non-exhaustive-enums.md +++ b/proposals/0192-non-exhaustive-enums.md @@ -3,7 +3,7 @@ * Proposal: [SE-0192](0192-non-exhaustive-enums.md) * Authors: [Jordan Rose](https://github.com/jrose-apple) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **March 16 - 23, 2018** +* Status: **Active Review (March 16...23, 2018)** * Implementation: [apple/swift#14945](https://github.com/apple/swift/pull/14945) * Previous revision: [1](https://github.com/apple/swift-evolution/blob/a773d07ff4beab8b7855adf0ac56d1e13bb7b44c/proposals/0192-non-exhaustive-enums.md), [2 (informal)](https://github.com/jrose-apple/swift-evolution/blob/57dfa2408fe210ed1d5a1251f331045b988ee2f0/proposals/0192-non-exhaustive-enums.md) * Pre-review discussion: [Enums and Source Compatibility](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170807/038663.html), with additional [orphaned thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170911/039787.html) From cd64dbec4eecb02c5b2571a50fdd72249d61cc99 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 16 Mar 2018 09:43:57 -0700 Subject: [PATCH 0599/4563] SE-0194 "Derived Collection of Enum Cases" is implemented for Swift 4.2 --- proposals/0194-derived-collection-of-enum-cases.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0194-derived-collection-of-enum-cases.md b/proposals/0194-derived-collection-of-enum-cases.md index 110e80fff9..4ea2fd8540 100644 --- a/proposals/0194-derived-collection-of-enum-cases.md +++ b/proposals/0194-derived-collection-of-enum-cases.md @@ -4,8 +4,7 @@ * Authors: [Jacob Bandes-Storch](https://github.com/jtbandes), [Brent Royal-Gordon](https://github.com/brentdax), [Robert Widmann](https://github.com/CodaFi) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Review Thread: [SE-0194 review][se8] -* Status: **Accepted (with revisions)** -* Implementation: [apple/swift#13655](https://github.com/apple/swift/pull/13655) +* Status: **Implemented (Swift 4.2)** ## Introduction From 45d54c48fa699df0c73f9bdc2181beb7dde2ce6d Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Sat, 11 Nov 2017 04:18:40 +0000 Subject: [PATCH 0600/4563] Raw String escaping --- proposals/NNNN-raw-string-escaping.md | 60 +++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 proposals/NNNN-raw-string-escaping.md diff --git a/proposals/NNNN-raw-string-escaping.md b/proposals/NNNN-raw-string-escaping.md new file mode 100644 index 0000000000..a48d3e123f --- /dev/null +++ b/proposals/NNNN-raw-string-escaping.md @@ -0,0 +1,60 @@ +# "Raw" mode string escaping + +* Proposal: [SE-NNNN](NNNN-raw-string-escaping.md) +* Authors: [John Holdsworth](https://github.com/johnno1962) +* Review Manager: TBD +* Status: **Implementation ready** + +* Implementation: [apple/swift#NNNNN](https://github.com/johnno1962/swift/commits/master) +* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/), [Additional Commentary](https://lists.swift.org/pipermail/swift-evolution/) +* Bugs: [SR-6362](https://bugs.swift.org/browse/SR-6362) + +## Introduction + +During the discussion on multi-line spring literals a mode for raw-escaped strings was discussed but postponed for later consideration. This proposal picks up this idea and suggests the smallest of changes be made to the Swift lexer to allow the entry of all string literals that contain unknown backslash escapes by prefixing them with "r" adopting the precedent from the Python language. + +## Motivation + +One area where this form of quoting would be useful is entering regular expressions. As patterns can often contain elements such as \w or \S these do not translate well to the existing string literal syntax resulting in strings such as + + let sentence = "\\w+(\\s+\\w+)\\." + +This is sometimes referred to as the "picket fencing" problem. + +## Proposed solution + +The proposal suggests a new "raw" string literal syntax by prefixing any string with an "r" character which alters slightly the behaviour of the compiler in realising the literal. + + let sentence = r"\w+(\s+\w+)*\." + +In raw single line and multi-line literals, it is proposed existing escapes \\, \", \t, \r, \n, \u or \( are processed as before. You will always need to be able to escape " and if you do you'll need to have a way of escaping \ and \( is too useful to leave out so why not just say existing escapes are processed as is. Otherwise, if character following the \ is not one of those currently recognised it is not an escape and both characters are literally included in the string rather than give an error on compilation. This would seem to be the simplest rule. The one exception is \ which it could be argued should still report an error in non-triple quoted strings. + +Some examples of raw mode literals and their existing literal equivalents: + +r"\\\n\(var)\\n\"" == "\\\n\(var)\\n\"" + +r"\?\y\=" == "\\?\\y\\=" + +r"\ +" == compiler error? + + +## Detailed design + +The changes are confined to the file lib/Parse/Lexer.cpp and involves check for a flag whether the string was prefixed by r when processing unknown backslash escapes and is localised to the function Lexer::lexCharacter in the code. If the "r" introducer was present, both the backslash and the unknown character are passed into the literal in Lexer::getEncodedStringSegment otherwise the existing behaviour is retained of emitting an error. A further minor change is also required to the main switch statement in Lexer::lexImpl. + +## Source compatibility + +This is a purely additive change. The syntax proposed is not currently valid Swift. + +## Effect on ABI stability + +None. + +## Effect on API resilience + +None. + +## Alternatives considered + +Some might argue for a "pure" raw unescaping string where no escapes are recognised at all but would seem an extreme position that would be less useful in practice. How then would you include a newline or " in a string? It would also be more difficult to implement. \ No newline at end of file From 98cedd95d0302d967d8e0b2df8f7f1f7e69bb3ce Mon Sep 17 00:00:00 2001 From: John Holdsworth Date: Thu, 23 Nov 2017 10:34:46 +0000 Subject: [PATCH 0601/4563] Raw string literals --- proposals/NNNN-raw-string-escaping.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/proposals/NNNN-raw-string-escaping.md b/proposals/NNNN-raw-string-escaping.md index a48d3e123f..2f5f73b307 100644 --- a/proposals/NNNN-raw-string-escaping.md +++ b/proposals/NNNN-raw-string-escaping.md @@ -1,17 +1,17 @@ -# "Raw" mode string escaping +# "Raw" mode string literals * Proposal: [SE-NNNN](NNNN-raw-string-escaping.md) * Authors: [John Holdsworth](https://github.com/johnno1962) * Review Manager: TBD * Status: **Implementation ready** -* Implementation: [apple/swift#NNNNN](https://github.com/johnno1962/swift/commits/master) +* Implementation: [apple/swift#13055](https://github.com/apple/swift/pull/13055) * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/), [Additional Commentary](https://lists.swift.org/pipermail/swift-evolution/) * Bugs: [SR-6362](https://bugs.swift.org/browse/SR-6362) ## Introduction -During the discussion on multi-line spring literals a mode for raw-escaped strings was discussed but postponed for later consideration. This proposal picks up this idea and suggests the smallest of changes be made to the Swift lexer to allow the entry of all string literals that contain unknown backslash escapes by prefixing them with "r" adopting the precedent from the Python language. +During the discussion on multi-line spring literals a mode for "raw-mode" strings was discussed but postponed for later consideration. This proposal looks to move this idea forward and suggests the smallest of changes be made to the Swift lexer to allow the entry of single and multi-line "raw" string literals by prefixing them with "r". This adopts the precedent from the Python language. In raw literals, the \ character would have no special meaning. ## Motivation @@ -19,29 +19,30 @@ One area where this form of quoting would be useful is entering regular expressi let sentence = "\\w+(\\s+\\w+)\\." -This is sometimes referred to as the "picket fencing" problem. +This is sometimes referred to as the "picket fencing" problem. Another example is entering windows file paths. ## Proposed solution -The proposal suggests a new "raw" string literal syntax by prefixing any string with an "r" character which alters slightly the behaviour of the compiler in realising the literal. +The proposal suggests a new "raw" string literal syntax by prefixing any string with an "r" character which alters slightly the behaviour of the compiler in realising the literal. The \ character would loose it role as an escaping introducer altogether. let sentence = r"\w+(\s+\w+)*\." - -In raw single line and multi-line literals, it is proposed existing escapes \\, \", \t, \r, \n, \u or \( are processed as before. You will always need to be able to escape " and if you do you'll need to have a way of escaping \ and \( is too useful to leave out so why not just say existing escapes are processed as is. Otherwise, if character following the \ is not one of those currently recognised it is not an escape and both characters are literally included in the string rather than give an error on compilation. This would seem to be the simplest rule. The one exception is \ which it could be argued should still report an error in non-triple quoted strings. Some examples of raw mode literals and their existing literal equivalents: -r"\\\n\(var)\\n\"" == "\\\n\(var)\\n\"" + r"\n\(var)\n" == "\\n\\(var)\\n" -r"\?\y\=" == "\\?\\y\\=" + r"\?\y\=" == "\\?\\y\\=" -r"\ -" == compiler error? + r"c:\windows\system32" == "c:\\windows\\system32" + r""" + Line One\ + Line Two\ + """ == "Line One\\\nLineTwo\\" ## Detailed design -The changes are confined to the file lib/Parse/Lexer.cpp and involves check for a flag whether the string was prefixed by r when processing unknown backslash escapes and is localised to the function Lexer::lexCharacter in the code. If the "r" introducer was present, both the backslash and the unknown character are passed into the literal in Lexer::getEncodedStringSegment otherwise the existing behaviour is retained of emitting an error. A further minor change is also required to the main switch statement in Lexer::lexImpl. +The changes are confined to the file lib/Parse/Lexer.cpp and involves check for a flag whether the string was prefixed by r disabling processing processing of backslash escapes and is localised to the function Lexer::lexCharacter in the code. If the "r" introducer was present, both the backslash and the unknown character are passed into the literal in Lexer::getEncodedStringSegment. A further minor change is also required to the main switch statement in Lexer::lexImpl and Token.h to convey the flag from parsing to code generation phases. ## Source compatibility @@ -57,4 +58,4 @@ None. ## Alternatives considered -Some might argue for a "pure" raw unescaping string where no escapes are recognised at all but would seem an extreme position that would be less useful in practice. How then would you include a newline or " in a string? It would also be more difficult to implement. \ No newline at end of file +An alternative proposal where known escapes retained their functionality and anything else passed through was considered but found to be too difficult to reason about in practice. \ No newline at end of file From 1f1bc5e969ee7ca3ad222b4a0f2fa7dad81394e7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 16 Mar 2018 10:01:25 -0700 Subject: [PATCH 0602/4563] Initiate review of SE-0200: "Raw" mode string literals --- ...N-raw-string-escaping.md => 0200-raw-string-escaping.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-raw-string-escaping.md => 0200-raw-string-escaping.md} (94%) diff --git a/proposals/NNNN-raw-string-escaping.md b/proposals/0200-raw-string-escaping.md similarity index 94% rename from proposals/NNNN-raw-string-escaping.md rename to proposals/0200-raw-string-escaping.md index 2f5f73b307..b80fa2fe77 100644 --- a/proposals/NNNN-raw-string-escaping.md +++ b/proposals/0200-raw-string-escaping.md @@ -1,9 +1,9 @@ # "Raw" mode string literals -* Proposal: [SE-NNNN](NNNN-raw-string-escaping.md) +* Proposal: [SE-0200](0200-raw-string-escaping.md) * Authors: [John Holdsworth](https://github.com/johnno1962) -* Review Manager: TBD -* Status: **Implementation ready** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active Review (March 16...26, 2018)** * Implementation: [apple/swift#13055](https://github.com/apple/swift/pull/13055) * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/), [Additional Commentary](https://lists.swift.org/pipermail/swift-evolution/) From f81976951dcc42472642b033251884c92ffabcf3 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Fri, 16 Mar 2018 10:56:33 -0700 Subject: [PATCH 0603/4563] Update 0199-bool-toggle.md --- proposals/0199-bool-toggle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0199-bool-toggle.md b/proposals/0199-bool-toggle.md index 14d9e77ee9..71ad94751c 100644 --- a/proposals/0199-bool-toggle.md +++ b/proposals/0199-bool-toggle.md @@ -3,7 +3,7 @@ * Proposal: [SE-0199](0199-bool-toggle.md) * Authors: [Chris Eidhof](http://chris.eidhof.nl) * Review Manager: [Ben Cohen](https://github.com/airspeedswift/) -* Status: **Accepted** +* Status: **Implemented** for Swift 4.2 * Decision notes: [Rationale](https://forums.swift.org/t/accepted-se-199-add-toggle-to-bool/10681) * Implementation: [apple/swift#14586](https://github.com/apple/swift/pull/14586) * Review thread: [Swift evolution forum](https://forums.swift.org/t/se-0199-adding-toggle-method-to-bool/) From a35698c3b8ca3436df3a5240f72bb936eb25aa3e Mon Sep 17 00:00:00 2001 From: Norio Nomura Date: Sun, 18 Mar 2018 00:34:54 +0900 Subject: [PATCH 0604/4563] Fix typo (#805) --- proposals/0200-raw-string-escaping.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0200-raw-string-escaping.md b/proposals/0200-raw-string-escaping.md index b80fa2fe77..ef186bbd7d 100644 --- a/proposals/0200-raw-string-escaping.md +++ b/proposals/0200-raw-string-escaping.md @@ -11,7 +11,7 @@ ## Introduction -During the discussion on multi-line spring literals a mode for "raw-mode" strings was discussed but postponed for later consideration. This proposal looks to move this idea forward and suggests the smallest of changes be made to the Swift lexer to allow the entry of single and multi-line "raw" string literals by prefixing them with "r". This adopts the precedent from the Python language. In raw literals, the \ character would have no special meaning. +During the discussion on multi-line string literals a mode for "raw-mode" strings was discussed but postponed for later consideration. This proposal looks to move this idea forward and suggests the smallest of changes be made to the Swift lexer to allow the entry of single and multi-line "raw" string literals by prefixing them with "r". This adopts the precedent from the Python language. In raw literals, the \ character would have no special meaning. ## Motivation @@ -58,4 +58,4 @@ None. ## Alternatives considered -An alternative proposal where known escapes retained their functionality and anything else passed through was considered but found to be too difficult to reason about in practice. \ No newline at end of file +An alternative proposal where known escapes retained their functionality and anything else passed through was considered but found to be too difficult to reason about in practice. From 9d06ec91d93aa5469bd94e90d25c532c6b77360f Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 17 Mar 2018 15:41:33 +0000 Subject: [PATCH 0605/4563] [SE-0200] Remove pipermail links --- proposals/0200-raw-string-escaping.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/proposals/0200-raw-string-escaping.md b/proposals/0200-raw-string-escaping.md index ef186bbd7d..8cdbe10b1a 100644 --- a/proposals/0200-raw-string-escaping.md +++ b/proposals/0200-raw-string-escaping.md @@ -4,9 +4,7 @@ * Authors: [John Holdsworth](https://github.com/johnno1962) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active Review (March 16...26, 2018)** - * Implementation: [apple/swift#13055](https://github.com/apple/swift/pull/13055) -* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/), [Additional Commentary](https://lists.swift.org/pipermail/swift-evolution/) * Bugs: [SR-6362](https://bugs.swift.org/browse/SR-6362) ## Introduction From 1bc3f3da2323c88afa041a6e3fb219b238a872a1 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Sat, 17 Mar 2018 12:07:09 -0400 Subject: [PATCH 0606/4563] Discourse updates (#803) * Discourse updates * Update README.md --- README.md | 2 +- process.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0609bdeec0..3be205d4a7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Swift](https://img.shields.io/badge/Swift%205-Open%20to%20requests-brightgreen.svg)](#swift_stage) -**Before you initiate a pull request**, please read the process document. Ideas should be thoroughly discussed on the [swift-evolution mailing list](https://swift.org/community/#swift-evolution) first. +**Before you initiate a pull request**, please read the process document. Ideas should be thoroughly discussed on the [swift-evolution forums](https://swift.org/community/#swift-evolution) first. This repository tracks the ongoing evolution of Swift. It contains: diff --git a/process.md b/process.md index abfbccbad8..306008f267 100644 --- a/process.md +++ b/process.md @@ -44,7 +44,7 @@ Please state explicitly whether you believe that the proposal should be accepted ## How to propose a change -* **Check prior proposals**: many ideas come up frequently, and may either be in active discussion on the mailing list, or may have been discussed already and have joined the [Commonly Rejected Proposals](commonly_proposed.md) list. Please check the mailing list archives and this list for context before proposing something new. +* **Check prior proposals**: many ideas come up frequently, and may either be in active discussion on the forums, or may have been discussed already and have joined the [Commonly Rejected Proposals](commonly_proposed.md) list. Please [search the forums](https://forums.swift.org/search) for context before proposing something new. * **Consider the goals of the upcoming Swift release**: Each major Swift release is focused on a [specific set of goals](README.md) described early in the release cycle. When proposing a change to @@ -52,7 +52,7 @@ Swift, please consider how your proposal fits in with the larger goals of the upcoming Swift release. Proposals that are clearly out of scope for the upcoming Swift release will not be brought up for review. If you can't resist discussing a proposal that you know is out of scope, please include the tag `[Out of scope]` in the subject. * **Socialize the idea**: propose a rough sketch of the idea in the ["pitches" section of the Swift forums](https://forums.swift.org/c/evolution/pitches), the problems it solves, what the solution looks like, etc., to gauge interest from the community. -* **Develop the proposal**: expand the rough sketch into a complete proposal, using the [proposal template](0000-template.md), and continue to refine the proposal on the evolution mailing list. Prototyping an implementation and its uses along with the proposal is *required* because it helps ensure both technical feasibility of the proposal as well as validating that the proposal solves the problems it is meant to solve. +* **Develop the proposal**: expand the rough sketch into a complete proposal, using the [proposal template](0000-template.md), and continue to refine the proposal on the forums. Prototyping an implementation and its uses along with the proposal is *required* because it helps ensure both technical feasibility of the proposal as well as validating that the proposal solves the problems it is meant to solve. * **Request a review**: initiate a pull request to the [swift-evolution repository][swift-evolution-repo] to indicate to the core team that you would like the proposal to be reviewed. When the proposal is sufficiently detailed and clear, and addresses feedback from earlier discussions of the idea, the pull request will be accepted. The proposal will be assigned a proposal number as well as a core team member to manage the review. * **Address feedback**: in general, and especially [during the review period][proposal-status], be responsive to questions and feedback about the proposal. @@ -79,7 +79,7 @@ intent during the review period. After the review has completed, the core team will make a decision on the proposal. The review manager is responsible for determining consensus among the core team members, then reporting their decision -to the proposal authors and mailing list. The review manager will +to the proposal authors and forums. The review manager will update the proposal's state in the [swift-evolution repository][swift-evolution-repo] to reflect that decision. From 2f7c3f1878c70d9b52b1e027298744502f1fce42 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 17 Mar 2018 16:29:19 +0000 Subject: [PATCH 0607/4563] [0000-template.md] Use forums instead of pipermail --- 0000-template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/0000-template.md b/0000-template.md index fd3e6feea1..43190333b0 100644 --- a/0000-template.md +++ b/0000-template.md @@ -8,7 +8,7 @@ *During the review process, add the following fields as needed:* * Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) -* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/), [Additional Commentary](https://lists.swift.org/pipermail/swift-evolution/) +* Decision Notes: [Rationale](https://forums.swift.org/), [Additional Commentary](https://forums.swift.org/) * Bugs: [SR-NNNN](https://bugs.swift.org/browse/SR-NNNN), [SR-MMMM](https://bugs.swift.org/browse/SR-MMMM) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md) * Previous Proposal: [SE-XXXX](XXXX-filename.md) @@ -19,7 +19,7 @@ A short description of what the feature is. Try to keep it to a single-paragraph "elevator pitch" so the reader understands what problem this proposal is addressing. -Swift-evolution thread: [Discussion thread topic for that proposal](https://lists.swift.org/pipermail/swift-evolution/) +Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/) ## Motivation From 6cf97f1c9381a3d850365ea56045588119e367e7 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Sat, 17 Mar 2018 16:10:44 -0700 Subject: [PATCH 0608/4563] Fix the status format in SE-0199 to match the template. --- proposals/0199-bool-toggle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0199-bool-toggle.md b/proposals/0199-bool-toggle.md index 71ad94751c..ac2e1e13e7 100644 --- a/proposals/0199-bool-toggle.md +++ b/proposals/0199-bool-toggle.md @@ -3,7 +3,7 @@ * Proposal: [SE-0199](0199-bool-toggle.md) * Authors: [Chris Eidhof](http://chris.eidhof.nl) * Review Manager: [Ben Cohen](https://github.com/airspeedswift/) -* Status: **Implemented** for Swift 4.2 +* Status: **Implemented (Swift 4.2)** * Decision notes: [Rationale](https://forums.swift.org/t/accepted-se-199-add-toggle-to-bool/10681) * Implementation: [apple/swift#14586](https://github.com/apple/swift/pull/14586) * Review thread: [Swift evolution forum](https://forums.swift.org/t/se-0199-adding-toggle-method-to-bool/) From 6b32355c7240ec06bd629ebae796f9072c9875d4 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 21 Mar 2018 15:52:00 -0700 Subject: [PATCH 0609/4563] Update NNNN-containsOnly.md --- proposals/NNNN-containsOnly.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-containsOnly.md b/proposals/NNNN-containsOnly.md index 67ca2005ca..d643c7516f 100644 --- a/proposals/NNNN-containsOnly.md +++ b/proposals/NNNN-containsOnly.md @@ -45,6 +45,8 @@ nums.containsOnly(9) nums.containsOnly(where: isOdd) ``` +on the basis that it aids readability and avoids performance pitfalls from the composed alternatives. + ## Detailed design Add the following extensions to `Sequence`: @@ -77,4 +79,8 @@ This change is purely additive so has no API resilience consequences. ## Alternatives considered -Not adding it. +Not adding it, since it can be trivially (if confusingly) composed. + +Much name bikeshedding has ensued. The primary rival for `containsOnly` is `all`. `containsOnly` is preferred as it is more explicit, and echoes the existing `contains`. Naming it `all` suggests a renaming of `contains` to `any` would be appropriate – but this is already a fairly heavily-used term elsewhere in Swift, and is less explicit. + +`contains(only:)` is discounted due to trailing closures dropping the argument label, rendering it indistiguishable from `contains(where:)`. From 431897cdd44ef000b01890ec17fc49766829d87c Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Thu, 22 Mar 2018 10:53:19 -0700 Subject: [PATCH 0610/4563] Update 0198-playground-quicklook-api-revamp.md --- proposals/0198-playground-quicklook-api-revamp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0198-playground-quicklook-api-revamp.md b/proposals/0198-playground-quicklook-api-revamp.md index f7ca2280c9..1d41d3d315 100644 --- a/proposals/0198-playground-quicklook-api-revamp.md +++ b/proposals/0198-playground-quicklook-api-revamp.md @@ -5,7 +5,7 @@ * Implementation: Swift 4.1 deprecation ([apple/swift#13911](https://github.com/apple/swift/pull/13911)], introduction of new protocol ([apple/swift-xcode-playground-support#21](https://github.com/apple/swift-xcode-playground-support/pull/21)), Swift 5 removal + shim library ([apple/swift#14252](https://github.com/apple/swift/pull/14252), [apple/swift-corelibs-foundation#1415](https://github.com/apple/swift-corelibs-foundation/pull/1415), [apple/swift-xcode-playground-support#20](https://github.com/apple/swift-xcode-playground-support/pull/20)) * Review Manager: [Ben Cohen](https://github.com/airspeedswift/) * Review thread: [SE-0198 review](https://forums.swift.org/t/se-0198-playground-quicklook-api-revamp/9448/16) -* Status: **Accepted (with revisions)** +* Status: **Implemented in 4.1** + +## Table of contents + +* [Introduction](#intro) +* [Motivation](#why) + - [The Status Quo](#status-quo) + - [Universal hash functions](#universal-hashing) +* [Proposed solution](#proposed-solution) + - [The `Hasher` struct](#hasher) + - [The `hash(into:)` requirement](#hash-into) +* [Detailed design](#detailed-design) + - [`Hasher`](#hasher-details) + - [`Hashable`](#hashable-details) +* [Source compatibility](#source-compatibility) +* [Effect on ABI stability](#abi) +* [Effect on API resilience](#resilience) +* [Alternatives considered](#alternatives) + - [Leaving `Hashable` as is](#leave-hashable-alone) + - [Defining a protocol refinement](#protocol-refinement) + - [Making `hash(into:)` generic over a `Hasher` protocol](#generic-hasher) + - [Change `hash(into:)` to take a closure instead of a new type](#closure-hasher) + +## Introduction + +This proposal introduces a new `Hasher` type representing the standard +library's universal hash function, and it extends the `Hashable` +protocol with a new `hash(into:)` requirement that expresses hashing +in terms of `Hasher`. This new requirement is intended to replace the +old `hashValue` property, which is deprecated. + +Switching to `hash(into:)` moves the choice of a hash function out of +`Hashable` implementations, and into the standard library. This makes +it considerably easier to manually conform types to `Hashable` -- the +task of writing a custom implementation reduces to identifying the +parts of a type that should contribute to the hash value. + +Standardizing on a single, high-quality hash function greatly improves +the reliability of `Set` and `Dictionary`. The hash function can be +specially selected and tuned to work well with the hash tables used by +these collections, preventing hash collision patterns that would break +the expected performance of common operations. + +`Hasher` is a resilient struct, enabling future versions of the +standard library to improve the hash function without the need to +change (or even recompile) existing code that implements `Hashable`. +Not baking any particular hash function into `Hashable` types is +especially important in case a weakness is found in the current +algorithm. + +Swift-evolution thread: [Combining Hashes](https://forums.swift.org/t/combining-hashes/9082) + +[SE-0185]: 0185-synthesize-equatable-hashable.md +[SE-0143]: 0143-conditional-conformances.md + +## Motivation + +The Swift Standard Library includes two general-purpose hashing +collections, `Set` and `Dictionary`. These collections are built +around hash tables, which are probabilistic data structures; their +expected performance critically depends on the expected distribution +of the elements stored in them, along with the quality of the hash +function that is used to derive bucket indices for individual +elements. + +With a good hash function, simple lookups, insertions and removals +take constant time on average. However, when the hash function is not +well-suited to the data, expected performance can become proportional +to the number of elements stored in the table. If the table is large +enough, such a regression can easily lead to unacceptable performance. +For example, when they're overwhelmed with hash collisions, +applications and network services may stop processing new user events +or network requests for prolonged periods of time; easily enough to +crash the app or bring down the service. + +### The Status Quo + +Since Swift version 1.0, `Hashable` has had a single requirement on +top of `Equatable`: the `hashValue` property. `hashValue` looks +deceptively simple, but implementing it is unreasonably hard: Not only +do we need to decide which components of our type should be involved +in hashing, but we also have to come up with a way to somehow distill +these components down into a single integer value. The API is +essentially asking us to implement a new hash function, from scratch, +every single time we conform to `Hashable`. + +Given adequate documentation, it is reasonable to expect that an +experienced programmer implementing a custom type would be able to +identify what parts need to be hashed. On the other hand, implementing +a good hash function requires careful consideration and specialist +knowledge. It is unreasonable to expect Swift programmers to invest +time and effort to get this right for every `Hashable` type out +there. + +For example, consider the code below, extracted directly from the +documentation of `Hashable`. Is this a good implementation of +`hashValue`? + +```swift +struct GridPoint { + var x: Int + var y: Int +} + +extension GridPoint: Hashable { + var hashValue: Int { + return x.hashValue ^ y.hashValue &* 16777619 + } + + static func == (lhs: GridPoint, rhs: GridPoint) -> Bool { + return lhs.x == rhs.x && lhs.y == rhs.y + } +} +``` + +The answer is that it depends; while the hash values it produces are +perfectly fine if `x` and `y` are expected to be small integers coming +from trusted sources, this hash function does have some undesirable +properties: + +1. The clever bit manipulations make it hard to understand what the + code does, or how it works. For example, can you tell what makes + 16777619 a better choice for the multiplier than, say, 16777618? + What are the precedence rules for `^` and `&*` again? What's with + the ampersand, anyway? + + We just wanted to use `GridPoint` values as keys in a + `Dictionary`, but first, we need to spend a couple of hours + learning about bitwise operations, integer overflows and the + exciting properties of coprime numbers. + + (For what it's worth, the magic constant used in the example above + is the same as the one used for the 32-bit version of the [FNV-1a] + hashing algorithm, which uses a similar (if a little more + complicated) method to distill arbitrary byte sequences down into + a single integer.) + +2. It is trivially easy to construct an arbitrarily large set of + `GridPoint` values that aren't equal, but have the same hash + value. If the values come from an untrusted source, they may + sometimes be deliberately chosen to induce collisions. + +3. The hash function doesn't do a particularly great job at mixing up + the input data; the hash values it produces tend to form long + chains of sequential integer clusters. While these aren't as bad + as hash collisions, some hash table operations can slow down + drasticaly when such clusters are present. (In Swift 4.1, `Set` + and `Dictionary` use open addressing with linear probing, and they + have to do some clever postprocessing of hash values to get rid of + such patterns.) + +It seems desirable for the standard library to provide better guidance +for people implementing `hashValue`. + +### Universal hash functions + +With [SE-0185], Swift 4.1 introduced compiler support for automatic +synthesis of `Hashable` conformance for certain types. For example, +the `GridPoint` struct above can be made to conform to `Hashable` +without explicitly defining `hashValue` (or `==`): + +```swift +struct GridPoint: Hashable { + var x: Int + var y: Int + + // hashValue and == are automatically synthesized by the compiler +} +``` + +[SE-0185] did not specify a hash function to be used for such +conformances, leaving it as an implementation detail of the compiler +and the standard library. Doing this well requires the use of a hash +function that works equally well on any number of components, +regardless of their expected distributions. + +[SE-0185]: https://github.com/apple/swift-evolution/blob/master/proposals/0185-synthesize-equatable-hashable.md + +Luckily, this problem has occured in other contexts before, and there +is an extensive list of hash functions that have been designed for +exactly such cases: [Foller-Noll-Vo][FNV-1a], [MurmurHash], +[CityHash], [SipHash], and [HighwayHash] are just a small selection of +these. (The last two algorithms have some light cryptographic elements +so that they provide reasonable level of protection against hash +collision attacks. This makes them better-suited for use in +general-purpose hashed collections like `Set` and `Dictionary`.) + +[FNV-1a]: http://www.isthe.com/chongo/tech/comp/fnv/index.html +[MurmurHash]: https://github.com/aappleby/smhasher +[CityHash]: https://github.com/google/cityhash +[SipHash]: https://131002.net/siphash/ +[HighwayHash]: https://github.com/google/highwayhash + +Since [SE-0185] required the standard library to implement a +high-quality universal hash function, it seems like a good idea to +expose it as public API, so that manual `Hashable` implementations can +take advantage of it, too. + +Universal hash functions work by maintaining some internal state -- +this can be as simple as a single 32/64-bit integer value (for +e.g. [FNV-1a]), but it is usually much wider than that. For example, +[SipHash] maintains a state of 256 bits, while [HighwayHash] uses 1024 +bits. + +## Proposed solution + +We solve `Hashable`'s implementation problems in two parts. First, we +make the standard library's hash function public. Second, we replace +`hashValue` with a requirement that is designed specifically to +eliminate the guesswork from manual `Hashable` implementations. + +### The `Hasher` struct + +We propose to expose the standard library's standard hash function as +a new, public struct type, called `Hasher`. This new struct captures +the state of the hash function, and provides the following operations: + +1. An initializer to create an empty state. To make hash values less + predictable, the standard hash function uses an arbitrary 128-bit + seed value, specified as a tuple of two `UInt64` integers. + `Hasher` uses a per-execution random seed value by default; + however, we also allow programmers to specify their own seed + values, primarily to enable per-instance random seeding in their + custom hashing collections. + + ```swift + public struct Hasher { + public init() // Use the default per-execution random seed value + public init(seed: (UInt64, UInt64)) // Use the specified seed value + } + ``` + +2. A set of operations to mix in new bits into the state of the hash + function. For reasons of efficiency, these are built around + appending integer values to the hasher: + + ```swift + extension Hasher { + public mutating func append(bits: Int) + public mutating func append(bits: UInt) + public mutating func append(bits: Int64) + public mutating func append(bits: UInt64) + public mutating func append(bits: Int32) + public mutating func append(bits: UInt32) + // etc. + } + ``` + + (We expect most hashable types will have discrete components; the + bits to be hashed will rarely be available as a contiguous + byte sequence.) + +3. An operation to finalize the state, extracting the hash value from it. + + ```swift + extension Hasher { + public mutating func finalize() -> Int + } + ``` + + Finalizing the hasher state invalidates it; it is illegal to call + `append` or `finalized` on a finalized hasher. + +Here is how one may use `Hasher` as a standalone type: + +```swift +var hasher = Hasher() // Initialize state, usually by random seeding +hasher.append(bits: 23) // Mix in several integers' worth of bits +hasher.append(bits: 42) +let hash = hasher.finalize() // Finalize the state and return the hash +``` + +Within the same execution of a Swift program, `Hasher`s are guaranteed +to return the same hash value in `finalize()`, as long as they start +with the same seed, and they are fed the exact same sequence of bytes. + +However, `Hasher` may generate entirely different hash values in other +executions, *even if it is seeded with the same value*. This +randomization is a critical feature, as it makes it much harder for +potential attackers to predict hash values. `Hashable` has always been +documented to explicitly allow such nondeterminism: + +> - Important: Hash values are not guaranteed to be equal across +> different executions of your program. Do not save hash values to +> use in a future execution. +> +> -- `Hashable` documentation + +(Random seeding can be disabled by setting a special environment +variable; see [Effect on ABI stability](#abi) for details.) + +The choice of which hash function `Hasher` implements is an +implementation detail of the standard library, and may change in any +new release. This includes the size and internal layout of `Hasher` +itself. (The current implementation uses SipHash-1-3 with 320 bits of +state.) + +### The `hash(into:)` requirement + +Introducing `Hasher` is a big improvement, but it's only half of the +story: `Hashable` itself needs to be updated to make better use of it. + +We propose to change the `Hashable` protocol by adding a new +`hash(into:)` requirement, while, at the same time, deprecating +`hashValue`: + +```swift +public protocol Hashable { + @available(*, deprecated: 4.2) + var hashValue: Int { get } + + func hash(into hasher: inout Hasher) +} +``` + +(Please see the section on [Source +compatibility](#source-compatibility) on how we'll do this without +breaking code written for previous versions of Swift.) + +At first glance, it may not be obvious why we need to replace +`hashValue`. After all, `Hasher` can be used to take the guesswork out +of its implementation: + +```swift +extension GridPoint: Hashable { + var hashValue: Int { + var hasher = Hasher() + hasher.append(x) + hasher.append(y) + return hasher.finalize() + } +} +``` + +What's wrong with this? What makes `hash(into:)` so much better that's +worth the cost of a change to a basic protocol? + +* **Better Discoverability** -- With `hashValue`, you need to know + about `Hasher` to make use of it: the API does not direct you to do + the right thing. Worse, you have to do extra busywork by manually + initializing and finalizing a `Hasher` instance directly in your + `hashValue` implementation. Compare the code above to the + `hash(into:)` implementation below: + + ```swift + extension GridPoint: Hashable { + func hash(into hasher: inout Hasher) { + hasher.append(x) + hasher.append(y) + } + } + ``` + + This is nice and easy, with minimal boilerplate. `Hasher` is part of + the function signature; people who need to implement `hash(into:)` + are naturally guided to learn about it. + +* **Guaranteed Dispersion Quality** -- Keeping the existing + `hashValue` interface would mean that there was no way for `Set` and + `Dictionary` to guarantee that `Hashable` types produce hash values + with good enough dispersion. Therefore, these collections would need + to keep postprocessing hash values. We'd like to eliminate + postprocessing overhead for types that upgrade to `Hasher`. + +* **Hasher Customizability** -- `hash(into:)` moves the initialization + of `Hasher` out of `Hashable` types, and into hashing + collections. This allows the standard library to customize `Hasher` + to each collection instance. For example, the stdlib could start + using a different seed value for every new `Set` and `Dictionary` + instance; this somewhat improves reliability by making hash values + even less predictable, but (probably more importantly), it + drastically improves the performance of some relatively common + operations involving [copying data between `Set`/`Dictionary` + instances][quadratic-copy]. + +* **Better Performance** -- Similarly, `hash(into:)` moves the + finalization step out of `Hashable`. Finalization is a relatively + expensive operation; for example, in SipHash-1-3, it costs three + times as much as a single 64-bit `append`. Repeating it for every + single component of a composite type would make hashing unreasonably + slow. + + For example, consider the `GridRectangle` type below: + ```swift + struct GridRectangle { + let topLeft: GridPoint + let bottomRight: GridPoint + } + ``` + + With `hashValue`, its `Hashable` implementation would look like this: + ```swift + extension GridRectangle: Hashable { + var hashValue: Int { // SLOW, DO NOT USE + var hasher = Hasher() + hasher.append(bits: topLeft.hashValue) + hasher.append(bits: bottomRight.hashValue) + return hasher.finalize() + } + } + ``` + + Both of the `hashValue` invocations above create and finalize + separate hashers. Assuming finalization takes three times as much + time as a single append (and generously assuming that initialization + is free) this takes 15 appends' worth of time: + + ``` + 1 hasher.append(bits: topLeft.hashValue) + 1 hasher.append(bits: topLeft.x) (in topLeft.hashValue) + 1 hasher.append(bits: topLeft.y) + 3 hasher.finalize() + 1 hasher.append(bits: bottomRight.hashValue) + 1 hasher.append(bits: bottomRight.x) (in bottomRight.hashValue) + 1 hasher.append(bits: bottomRight.y) + 3 hasher.finalize() + 3 hasher.finalize() + --- + 15 + ``` + + Switching to `hash(into:)` gets us the following code: + + ```swift + extension GridRegion: Hashable { + func hash(into hasher: inout Hasher) { + hasher.append(topLeft) + hasher.append(bottomRight) + } + } + ``` + + This reduces the cost of hashing to just four appends and a single + finalization, which takes less than half the time of our original + approach: + + ``` + 1 hasher.append(bits: topLeft.x) (in topLeft.hashValue) + 1 hasher.append(bits: topLeft.y) + 1 hasher.append(bits: bottomRight.x) (in bottomRight.hashValue) + 1 hasher.append(bits: bottomRight.y) + 3 hasher.finalize() (outside of hash(into:)) + --- + 7 + ``` + +Switching to `hash(into:)` gets us more robust hash values faster, and +with cleaner, simpler code. + + +[quadratic-copy]: https://bugs.swift.org/browse/SR-3268 + + +## Detailed design + +### `Hasher` + +Add the following type to the standard library: + +```swift +/// Represents the universal hash function used by `Set` and `Dictionary`. +/// +/// The hash function is a mapping from a 128-bit seed value and an +/// arbitrary sequence of bytes to an integer hash value. The seed value +/// is specified during `Hasher` initialization, while the byte sequence +/// is fed to the hasher using a series of calls to mutating `append` +/// methods. When all bytes have been fed to the hasher, the hash value +/// can be retrieved by calling `finalize()`: +/// +/// ``` +/// var hasher = Hasher() // Use the default per-execution seed. +/// hasher.append(23) +/// hasher.append("Hello") // You can append bits from any Hashable value. +/// let hashValue = hasher.finalize() +/// ``` +/// +/// The underlying hash algorithm is designed to exhibit avalanche +/// effects: slight changes to the seed or the input byte sequence +/// will produce drastic changes in the generated hash value. +/// +/// - Note: The hash algorithm implemented by `Hasher` may change between +/// any two versions of the standard library. Do not save or otherwise +/// reuse hash values across executions of your program. +public struct Hasher { + /// Initialize a new hasher from the default seed value. + /// The default seed is set during process startup, usually from a + /// high-quality random source. + public init() + /// Initialize a new hasher from the specified seed value. + public init(seed: (UInt64, UInt64)) + + /// Append `value` to this hasher. + @inlinable + public mutating func append(_ value: H) { + value.hash(into: &self) + } + + /// Append the bit pattern `bits` to this hasher. + /// This adds exactly `Int.bitWidth` bits to the hasher state, + /// in native byte order. + public mutating func append(bits: Int) + + /// Append the bit pattern `bits` to this hasher. + /// This adds exactly `UInt.bitWidth` bits to the hasher state, + /// in native byte order. + public mutating func append(bits: UInt) + + /// Append the bit pattern `bits` to this hasher. + /// This adds exactly 8 bytes to the hasher state, in native byte order. + public mutating func append(bits: Int64) + + /// Append the bit pattern `bits` to this hasher. + /// This adds exactly 8 bytes to the hasher state, in native byte order. + public mutating func append(bits: UInt64) + + /// Append the bit pattern `bits` to this hasher. + /// This adds exactly 4 bytes to the hasher state, in native byte order. + public mutating func append(bits: Int32) + + /// Append the bit pattern `bits` to this hasher. + /// This adds exactly 4 bytes to the hasher state, in native byte order. + public mutating func append(bits: UInt32) + + /// Append the bit pattern `bits` to this hasher. + /// This adds exactly 2 bytes to the hasher state, in native byte order. + public mutating func append(bits: Int16) + + /// Append the bit pattern `bits` to this hasher. + /// This adds exactly 2 bytes to the hasher state, in native byte order. + public mutating func append(bits: UInt16) + + /// Append the single byte `bits` to this hasher. + public mutating func append(bits: Int8) + /// Append the single byte `bits` to this hasher. + public mutating func append(bits: UInt8) + /// Append the raw bytes in `buffer` to this hasher. + public mutating func append(bits buffer: UnsafeRawBufferPointer) + + /// Finalize the hasher state and return the hash value. + /// Finalizing invalidates the hasher; it cannot be appended to or + /// finalized again. + public mutating func finalize() -> Int +} +``` + +## `Hashable` + +Change the `Hashable` protocol as follows. + +```swift +/// A type that can be hashed into a `Hasher`. +/// +/// You can use any type that conforms to the `Hashable` protocol in a set or as +/// a dictionary key. Many types in the standard library conform to `Hashable`: +/// Strings, integers, floating-point and Boolean values, and even sets are +/// hashable by default. Some other types, such as optionals, arrays and ranges +/// automatically become hashable when their type arguments implement the same. +/// +/// Your own custom types can be hashable as well. When you define an +/// enumeration without associated values, it gains `Hashable` conformance +/// automatically, and you can add `Hashable` conformance to your other custom +/// types by implementing the `hash(into:)` function. For structs whose stored +/// properties are all `Hashable`, and for enum types that have all-`Hashable` +/// associated values, the compiler is able to provide an implementation of +/// `hash(into:)` automatically. +/// +/// Hashing a value means feeding its essential components into a hash function, +/// represented by the `Hasher` type. Essential components are those that +/// contribute to the type's implementation of `Equatable`. Two instances that +/// are equal must feed the same values to `Hasher` in `hash(into:)`, in the +/// same order. +/// +/// Conforming to the Hashable Protocol +/// =================================== +/// +/// To use your own custom type in a set or as the key type of a dictionary, +/// add `Hashable` conformance to your type. The `Hashable` protocol inherits +/// from the `Equatable` protocol, so you must also satisfy that protocol's +/// requirements. +/// +/// A custom type's `Hashable` and `Equatable` requirements are automatically +/// synthesized by the compiler when you declare `Hashable` conformance in the +/// type's original declaration and your type meets these criteria: +/// +/// - For a `struct`, all its stored properties must conform to `Hashable`. +/// - For an `enum`, all its associated values must conform to `Hashable`. (An +/// `enum` without associated values has `Hashable` conformance even without +/// the declaration.) +/// +/// To customize your type's `Hashable` conformance, to adopt `Hashable` in a +/// type that doesn't meet the criteria listed above, or to extend an existing +/// type to conform to `Hashable`, implement the `hash(into:)` function in your +/// custom type. To ensure that your type meets the semantic requirements of the +/// `Hashable` and `Equatable` protocols, it's a good idea to also customize +/// your type's `Equatable` conformance to match the `hash(into:)` definition. +/// +/// As an example, consider a `GridPoint` type that describes a location in a +/// grid of buttons. Here's the initial declaration of the `GridPoint` type: +/// +/// /// A point in an x-y coordinate system. +/// struct GridPoint { +/// var x: Int +/// var y: Int +/// } +/// +/// You'd like to create a set of the grid points where a user has already +/// tapped. Because the `GridPoint` type is not hashable yet, it can't be used +/// as the `Element` type for a set. To add `Hashable` conformance, provide an +/// `==` operator function and a `hash(into:)` function. +/// +/// extension GridPoint: Hashable { +/// func hash(into hasher: inout Hasher) { +/// hasher.append(x) +/// hasher.append(y) +/// } +/// +/// static func == (lhs: GridPoint, rhs: GridPoint) -> Bool { +/// return lhs.x == rhs.x && lhs.y == rhs.y +/// } +/// } +/// +/// The `hash(into:)` property in this example feeds the properties `x` and `y` +/// to the supplied hasher; these are the same properties compared by the +/// implementation of the `==` operator function. +/// +/// Now that `GridPoint` conforms to the `Hashable` protocol, you can create a +/// set of previously tapped grid points. +/// +/// var tappedPoints: Set = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y: 1)] +/// let nextTap = GridPoint(x: 0, y: 1) +/// if tappedPoints.contains(nextTap) { +/// print("Already tapped at (\(nextTap.x), \(nextTap.y)).") +/// } else { +/// tappedPoints.insert(nextTap) +/// print("New tap detected at (\(nextTap.x), \(nextTap.y)).") +/// } +/// // Prints "New tap detected at (0, 1).") +public protocol Hashable { + /// The hash value. + /// + /// Hash values are not guaranteed to be equal across different executions of + /// your program. Do not save hash values to use during a future execution. + @available(*, deprecated: 4.2) + var hashValue: Int { get } + + /// Hash the essential components of this value into the hash function + /// represented by `hasher`, by appending them to it. + /// + /// Essential components are precisely those that are compared in the type's + /// implementation of `Equatable`. + func hash(into hasher: inout Hasher) +} +``` + +## Source compatibility + +The introduction of the new `Hasher` type is a purely additive change. +However, adding the `hash(into:)` requirement and deprecating +`hashValue` are potentially source breaking changes. Deprecating a +requirement in a basic protocol like `Hashable` is an especially +drastic change; we must make every effort to make sure there is a +smooth transition path to Swift 4.2 for existing code implementing +`hashValue`. + +Usually, such changes can be implemented in the standard library by +adding appropriate default implementations gated on language +version. For example, this is how `hash(into:)` can be expressed in +terms of `hashValue`, and vice versa: + +```swift +extension Hashable { + @available(*, obsoleted: 4.2) + func hash(into hasher: inout Hasher) { + hasher.append(bits: self.hashValue) + } + + @available(*, introduced: 4.2, deprecated: 4.2) + var hashValue: Int { + var hasher = Hasher() + self.hash(into: &hasher) + return hasher.finalize() + } +} +``` + +However, in the case of `Hashable`, such default implementations would +interfere with [SE-0185]'s automatic conformance synthesis. To ensure +compatibility, we rather need to extend automatic synthesis to supply +definitions functionally equivalent to the ones above, except directly +within the compiler rather than in the standard library. + +Code written for Swift 4.1 or earlier will continue to compile (in the +corresponding language mode) after this proposal is implemented. The +compiler will synthesize the missing `hash(into:)` requirement +automatically. + +When upgrading to Swift 4.2, `Hashable` types written for earlier +versions of Swift will need to be migrated to implement `hash(into:)` +instead of `hashValue`. There are two options for doing this: + +1. For types that support [SE-0185]'s automatic `Hashable` synthesis, + upgrading to Swift 4.2 can be as simple as removing the explicit + `hashValue` implementation. Note that [SE-0143] added conditional + conformances to `Hashable` in the standard library, which makes + automatic synthesis available for many more types than before. +2. In cases where automatic synthesis is unavailable, or if it would + produce undesirable results, the `hashValue` implementation + need to be replaced by a corresponding implementation of + `hash(into:)`. + +The compiler should simplify the migration process by providing +fix-its for both options. A fix-it to remove `hashValue` should only +be provided if automatic synthesis is available. For the second +option, it would be nice to have the compiler suggest a full +implementation of `hash(into:)`, but a template would probably +suffice: + +``` +// Before: +var hashValue: Int { + return x.hashValue ^ y.hashValue &* 16777619 +} + +// After: +func hash(into hasher: inout Hasher) { + // Append all components that should be hashed. + // These should be the same components that you look at in your + // implementation of `==` for `Equatable`. + hasher.append(<# component1 #>) + hasher.append(<# component2 #>) + + // For reference, this type originally implemented `hashValue` + // as follows: + // ``` + // return x.hashValue ^ y.hashValue &* 16777619 + // ``` +} +``` + +Whatever the fix-it does, it must not leave the function body empty, +because it would compile and run without warning, but it would produce +terrible hash values. + +## Effect on ABI stability + +`Hasher` and `hash(into:)` are additive changes that extend the ABI of +the standard library. `Hasher` is a fully resilient struct, with +opaque size/layout and mostly opaque members. (The only exception is +the generic function `append(_:)`, which is provided as a syntactic +convenience.) + +While this proposal deprecates the `hashValue` requirement, it doesn't +remove it. Types implementing `Hashable` will continue to provide an +implementation for it, although the implementation may be provided +automatically by compiler synthesis. + +To implement nondeterminism, `Hasher` uses an internal seed value +initialized by the runtime during process startup. The value is +usually produced by a random number generator, but this may be +disabled by defining the `SWIFT_DETERMINISTIC_HASHING` environment +variable with a value of `1` prior to starting a Swift process. + +## Effect on API resilience + +Replacing `hashValue` with `hash(into:)` moves the responsibility of +choosing a suitable hash function out of `Hashable` implementations +and into the standard library, behind a resiliency boundary. + +`Hasher` is explicitly designed so that future versions of the +standard library will be able to replace the hash function. +`Hashable` implementations compiled for previous versions will +automatically pick up the improved algorithm when linked with the new +release. This includes changing the size or internal layout of the +`Hasher` state itself. + +(We foresee several reasons why we may want to replace the hash +function. For example, we may need to do so if a weakness is +discovered in the current function, to restore the reliability of +`Set` and `Dictionary`. We may also want to tweak the hash function to +adapt it to the special requirements of certain environments (such as +network services), or to generally improve hashing performance.) + +## Alternatives considered + +### Leaving `Hashable` as is + +One option that we considered is to expose `Hasher`, but to leave the +`Hashable` protocol as is. Individual `Hashable` types would be able +to choose whether or not to use it or to roll their own hash +functions. + +We felt this was an unsatisfying approach, and we gave an extensive +rationale for changing `Hashable` in the section on [The `hash(into:)` +requirement](#hash-into). + +### Defining a protocol refinement + +There have been several attempts ([1][h1], [2][h2], [3][h3]) to fix `Hashable` +by creating a new protocol that refines it: + +[h1]: https://github.com/apple/swift/blob/swift-4.1-branch/validation-test/stdlib/HashingPrototype.swift +[h2]: https://github.com/attaswift/SipHash +[h3]: https://gist.github.com/regexident/1b8e84974da2243e5199e760508d2d25 + +```swift +protocol Hashable { + var hashValue: Int { get } +} + +protocol Hashable2: Hashable { + func hash(into hasher: inout Hasher) +} + +extension Hashable2 { + var hashValue: Int { + var hasher = Hasher() + hash(into: &hasher) + return hasher.finalize() + } +} +``` + +While this is a useful approach for external hashing packages, we +believe it to be unsuitable for the standard library. Adding a new +protocol would add a significant new source of user confusion about +hashing, and it would needlessly expand the standard library's API +surface area. + +The new protocol would need to have a new name, but `Hashable` already +has the perfect name for a protocol representing hashable things -- so +we'd need to choose an imperfect name for the "better" protocol. + +While deprecating a protocol requirement is a significant change, we +believe it to be less harmful overall than leaving `Hashable` +unchanged, or trying to have two parallel protocols for the same thing. + +Additionally, adding a second protocol would lead to complications +with `Hashable` synthesis. It's also unclear if `Set` and `Dictionary` +would be able to consistently use `Hasher` for their primary hashing +API. (These problems are not unsolvable, but they may involve adding +special one-off compiler support for the new protocol. For example, we +may want to automatically derive `Hashable2` conformance for all types +that implement `Hashable`.) + +### Making `hash(into:)` generic over a `Hasher` protocol + +It would be nice to allow Swift programmers to define their own hash +functions, and to plug them into any `Hashable` type: + +```swift +protocol Hasher { + func append(bits: Int) + func append(bits: UInt) + /// etc. +} +protocol Hashable { + func hash(into hasher: inout H) +} +``` + +However, we believe this would add an unnnecessary degree of freedom. +In particular, we do not foresee a need for adding support for custom +hashers in `Set` and `Dictionary`. On the other hand, there are +distinct advantages to standardizing on a single, high-quality hash +function: + +* Adding a generic type parameter to `hash(into:)` would complicate + the `Hashable` API. +* By supporting only a single `Hasher`, we can concentrate our efforts + on making sure it runs fast. For example, we know that the + standard hasher's opaque mutating functions won't ever perform any + retain/release operations, or otherwise mutate any of the + reference types we may encounter during hashing; describing this + fact to the compiler may enable optimizations that would not + otherwise be possible. +* Generics aren't zero-cost abstractions in Swift. We may be tempted + to think that we could gain some performance by plugging in a less + sophisticated hash function. This is not necessarily the case -- + support for custom hashers comes with significant overhead that + can easily overshadow the (slight, if any) algorithmic + disadvantage of the standard `Hasher`. + +### Change `hash(into:)` to take a closure instead of a new type + +A variant of the previous idea is to represent the hasher by a simple +closure taking an integer argument: + +```swift +protocol Hashable { + func hash(into hasher: (Int) -> Void) +} + +extension GridPoint: Hashable { + func hash(into hasher: (Int) -> Void) { + hasher(x) + hasher(y) + } +} +``` + +While this is an attractively minimal API, it has problems with +granularity -- it doesn't allow adding anything less than an +`Int`'s worth of bits to the hash state. + +Additionally, like generics, the performance of such a closure-based +interface would compare unfavorably to `Hasher`, since the compiler +wouldn't be able to guarantee anything about the potential +side-effects of the closure. From 4e3d5d278270e78e7fa9112800bf02b7eb60b84d Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Fri, 30 Mar 2018 22:26:46 +0100 Subject: [PATCH 0621/4563] =?UTF-8?q?Fix=20issues=20revealed=20by=20@milse?= =?UTF-8?q?mann=E2=80=99s=20kind=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proposals/NNNN-hashable-enhancements.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/proposals/NNNN-hashable-enhancements.md b/proposals/NNNN-hashable-enhancements.md index 1d322574a5..d6031d998c 100644 --- a/proposals/NNNN-hashable-enhancements.md +++ b/proposals/NNNN-hashable-enhancements.md @@ -74,11 +74,10 @@ Swift-evolution thread: [Combining Hashes](https://forums.swift.org/t/combining- The Swift Standard Library includes two general-purpose hashing collections, `Set` and `Dictionary`. These collections are built -around hash tables, which are probabilistic data structures; their -expected performance critically depends on the expected distribution -of the elements stored in them, along with the quality of the hash -function that is used to derive bucket indices for individual -elements. +around hash tables, whose expected performance critically depends on +the expected distribution of the elements stored in them, along with +the quality of the hash function that is used to derive bucket indices +for individual elements. With a good hash function, simple lookups, insertions and removals take constant time on average. However, when the hash function is not @@ -875,8 +874,10 @@ protocol Hashable { } ``` -However, we believe this would add an unnnecessary degree of freedom. -In particular, we do not foresee a need for adding support for custom +However, we believe this would add a degree of generality whose costs +are unjustifiable relative to their potential gain. We expect the +ability to create custom hashers would rarely be exercised. For +example, we do not foresee a need for adding support for custom hashers in `Set` and `Dictionary`. On the other hand, there are distinct advantages to standardizing on a single, high-quality hash function: @@ -888,9 +889,9 @@ function: standard hasher's opaque mutating functions won't ever perform any retain/release operations, or otherwise mutate any of the reference types we may encounter during hashing; describing this - fact to the compiler may enable optimizations that would not + fact to the compiler enables optimizations that would not otherwise be possible. -* Generics aren't zero-cost abstractions in Swift. We may be tempted +* Generics in Swift aren't zero-cost abstractions. We may be tempted to think that we could gain some performance by plugging in a less sophisticated hash function. This is not necessarily the case -- support for custom hashers comes with significant overhead that From 1e99bfb87e184ec2e3265ced8bca8d9a347b1e2d Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Fri, 30 Mar 2018 23:07:20 +0100 Subject: [PATCH 0622/4563] Expand section on Hasher.append. --- proposals/NNNN-hashable-enhancements.md | 31 +++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/proposals/NNNN-hashable-enhancements.md b/proposals/NNNN-hashable-enhancements.md index d6031d998c..f4e86043c6 100644 --- a/proposals/NNNN-hashable-enhancements.md +++ b/proposals/NNNN-hashable-enhancements.md @@ -262,10 +262,33 @@ the state of the hash function, and provides the following operations: } ``` - (We expect most hashable types will have discrete components; the - bits to be hashed will rarely be available as a contiguous - byte sequence.) + We expect most hashable types will have discrete + components. However, we do provide an `append` overload that takes + bytes from an `UnsafeRawBufferPointer`, in cases where the bits to + be hashed are available as a single, contiguous byte sequence: + ```swift + extension Hasher { + public mutating func append(bits buffer: UnsafeRawBufferPointer) + } + ``` + + To make it easier to express `hash(into:)` in terms of `Hashable` + components, we provide a variant of `append` that simply calls + `hash(into:)` on the supplied value: + + ```swift + extension Hasher { + @inlinable + public mutating func append(_ value: H) { + value.hash(into: &self) + } + } + ``` + + This is purely for convenience; `hasher.append(foo)` is slightly + easier to type than `foo.hash(into: &hasher)`. + 3. An operation to finalize the state, extracting the hash value from it. ```swift @@ -275,7 +298,7 @@ the state of the hash function, and provides the following operations: ``` Finalizing the hasher state invalidates it; it is illegal to call - `append` or `finalized` on a finalized hasher. + `append` or `finalize` on a hasher that's already finalized. Here is how one may use `Hasher` as a standalone type: From a418764bb50fa4ad888ffcc7d9ba561aee6abb72 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Sat, 31 Mar 2018 01:19:23 +0100 Subject: [PATCH 0623/4563] Light copy editing. Add a link to an interesting forum pitch. --- proposals/NNNN-hashable-enhancements.md | 57 +++++++++++++------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/proposals/NNNN-hashable-enhancements.md b/proposals/NNNN-hashable-enhancements.md index f4e86043c6..2f4307559d 100644 --- a/proposals/NNNN-hashable-enhancements.md +++ b/proposals/NNNN-hashable-enhancements.md @@ -74,20 +74,20 @@ Swift-evolution thread: [Combining Hashes](https://forums.swift.org/t/combining- The Swift Standard Library includes two general-purpose hashing collections, `Set` and `Dictionary`. These collections are built -around hash tables, whose expected performance critically depends on -the expected distribution of the elements stored in them, along with -the quality of the hash function that is used to derive bucket indices -for individual elements. +around hash tables, whose performance is critically dependent on the +expected distribution of the elements stored in them, along with the +quality of the hash function that is used to derive bucket indices for +individual elements. With a good hash function, simple lookups, insertions and removals -take constant time on average. However, when the hash function is not -well-suited to the data, expected performance can become proportional -to the number of elements stored in the table. If the table is large -enough, such a regression can easily lead to unacceptable performance. -For example, when they're overwhelmed with hash collisions, -applications and network services may stop processing new user events -or network requests for prolonged periods of time; easily enough to -crash the app or bring down the service. +take constant time on average. However, when the hash function isn't +carefully chosen to suit the data, the expected time of such +operations can become proportional to the number of elements stored in +the table. If the table is large enough, such a regression can easily +lead to unacceptable performance. For example, when they're +overwhelmed with hash collisions, applications and network services +may stop processing new events for prolonged periods of time; this can +easily be enough to crash the app or to bring down the service. ### The Status Quo @@ -241,8 +241,8 @@ the state of the hash function, and provides the following operations: ```swift public struct Hasher { - public init() // Use the default per-execution random seed value - public init(seed: (UInt64, UInt64)) // Use the specified seed value + public init() // Use the default per-execution random seed value + public init(seed: (UInt64, UInt64)) // Combines `seed` with the default seed } ``` @@ -474,11 +474,11 @@ worth the cost of a change to a basic protocol? approach: ``` - 1 hasher.append(bits: topLeft.x) (in topLeft.hashValue) + 1 hasher.append(bits: topLeft.x) (in topLeft.hash(into:)) 1 hasher.append(bits: topLeft.y) - 1 hasher.append(bits: bottomRight.x) (in bottomRight.hashValue) + 1 hasher.append(bits: bottomRight.x) (in bottomRight.hash(into:)) 1 hasher.append(bits: bottomRight.y) - 3 hasher.finalize() (outside of hash(into:)) + 3 hasher.finalize() (outside of GridRectangle.hash(into:)) --- 7 ``` @@ -724,9 +724,11 @@ extension Hashable { However, in the case of `Hashable`, such default implementations would interfere with [SE-0185]'s automatic conformance synthesis. To ensure -compatibility, we rather need to extend automatic synthesis to supply -definitions functionally equivalent to the ones above, except directly -within the compiler rather than in the standard library. +compatibility, we [currently][either-or-requirements] need to move +these definitions to the compiler, by extending automatic synthesis to +supply definitions functionally equivalent to the ones above. + +[either-or-requirements]: https://forums.swift.org/t/mutually-exclusive-default-implementations/11044 Code written for Swift 4.1 or earlier will continue to compile (in the corresponding language mode) after this proposal is implemented. The @@ -744,7 +746,7 @@ instead of `hashValue`. There are two options for doing this: automatic synthesis available for many more types than before. 2. In cases where automatic synthesis is unavailable, or if it would produce undesirable results, the `hashValue` implementation - need to be replaced by a corresponding implementation of + needs to be replaced by a corresponding implementation of `hash(into:)`. The compiler should simplify the migration process by providing @@ -794,10 +796,10 @@ implementation for it, although the implementation may be provided automatically by compiler synthesis. To implement nondeterminism, `Hasher` uses an internal seed value -initialized by the runtime during process startup. The value is -usually produced by a random number generator, but this may be -disabled by defining the `SWIFT_DETERMINISTIC_HASHING` environment -variable with a value of `1` prior to starting a Swift process. +initialized by the runtime during process startup. The seed is usually +produced by a random number generator, but this may be disabled by +defining the `SWIFT_DETERMINISTIC_HASHING` environment variable with a +value of `1` prior to starting a Swift process. ## Effect on API resilience @@ -828,9 +830,8 @@ One option that we considered is to expose `Hasher`, but to leave the to choose whether or not to use it or to roll their own hash functions. -We felt this was an unsatisfying approach, and we gave an extensive -rationale for changing `Hashable` in the section on [The `hash(into:)` -requirement](#hash-into). +We felt this was an unsatisfying approach; the rationale behind this +is explained in the section on [The `hash(into:)` requirement](#hash-into). ### Defining a protocol refinement From 8d9f51f060b8c772f7adabee486112a97569d2b3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 1 Apr 2018 17:12:21 -0700 Subject: [PATCH 0624/4563] Depluralize single author authors lists, scratching a pedantic itch. --- ...-compensate-for-the-inconsistency-of-nscopyings-behaviour.md | 2 +- proposals/0164-remove-final-support-in-protocol-extensions.md | 2 +- proposals/0173-swap-indices.md | 2 +- proposals/0176-enforce-exclusive-access-to-memory.md | 2 +- proposals/0177-add-clamped-to-method.md | 2 +- proposals/0179-swift-run-command.md | 2 +- proposals/0181-package-manager-cpp-language-version.md | 2 +- proposals/0187-introduce-filtermap.md | 2 +- proposals/0189-restrict-cross-module-struct-initializers.md | 2 +- proposals/0191-eliminate-indexdistance.md | 2 +- proposals/0192-non-exhaustive-enums.md | 2 +- proposals/0193-cross-module-inlining-and-specialization.md | 2 +- proposals/0198-playground-quicklook-api-revamp.md | 2 +- proposals/0199-bool-toggle.md | 2 +- proposals/0200-raw-string-escaping.md | 2 +- proposals/0202-random-unification.md | 2 +- proposals/0203-rename-sequence-elements-equal.md | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md index a3c349ef29..33efbdcfcf 100644 --- a/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md +++ b/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md @@ -1,7 +1,7 @@ # Compensate for the inconsistency of `@NSCopying`'s behaviour * Proposal: [SE-0153](0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md) -* Authors: [Torin Kwok](https://github.com/TorinKwok) +* Author: [Torin Kwok](https://github.com/TorinKwok) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Accepted** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170227/033357.html) diff --git a/proposals/0164-remove-final-support-in-protocol-extensions.md b/proposals/0164-remove-final-support-in-protocol-extensions.md index 62f71f86a7..8bf12af92d 100644 --- a/proposals/0164-remove-final-support-in-protocol-extensions.md +++ b/proposals/0164-remove-final-support-in-protocol-extensions.md @@ -1,7 +1,7 @@ # Remove final support in protocol extensions * Proposal: [SE-0164](0164-remove-final-support-in-protocol-extensions.md) -* Authors: [Brian King](https://github.com/KingOfBrian) +* Author: [Brian King](https://github.com/KingOfBrian) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2017-April/000355.html) diff --git a/proposals/0173-swap-indices.md b/proposals/0173-swap-indices.md index 7d454bb5b5..f6deb4a050 100644 --- a/proposals/0173-swap-indices.md +++ b/proposals/0173-swap-indices.md @@ -1,7 +1,7 @@ # Add `MutableCollection.swapAt(_:_:)` * Proposal: [SE-0173](0173-swap-indices.md) -* Authors: [Ben Cohen](https://github.com/airspeedswift) +* Author: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170424/036229.html) diff --git a/proposals/0176-enforce-exclusive-access-to-memory.md b/proposals/0176-enforce-exclusive-access-to-memory.md index 76ee6602d6..0680ccb258 100644 --- a/proposals/0176-enforce-exclusive-access-to-memory.md +++ b/proposals/0176-enforce-exclusive-access-to-memory.md @@ -1,7 +1,7 @@ # Enforce Exclusive Access to Memory * Proposal: [SE-0176](0176-enforce-exclusive-access-to-memory.md) -* Authors: [John McCall](https://github.com/rjmccall) +* Author: [John McCall](https://github.com/rjmccall) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Implemented (Swift 4)** * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/7e6816c22a29b0ba9bdf63ff92b380f9e963860a/proposals/0176-enforce-exclusive-access-to-memory.md) diff --git a/proposals/0177-add-clamped-to-method.md b/proposals/0177-add-clamped-to-method.md index 5f4ab903c9..89639aa910 100644 --- a/proposals/0177-add-clamped-to-method.md +++ b/proposals/0177-add-clamped-to-method.md @@ -1,7 +1,7 @@ # Add clamp(to:) to the stdlib * Proposal: [SE-0177](0177-add-clamped-to-method.md) -* Authors: [Nicholas Maccharoli](https://github.com/Nirma) +* Author: [Nicholas Maccharoli](https://github.com/Nirma) * Review Manager: TBD * Status: **Returned for revision** diff --git a/proposals/0179-swift-run-command.md b/proposals/0179-swift-run-command.md index 45c9b93289..728efbe82b 100644 --- a/proposals/0179-swift-run-command.md +++ b/proposals/0179-swift-run-command.md @@ -1,7 +1,7 @@ # Swift `run` Command * Proposal: [SE-0179](0179-swift-run-command.md) -* Authors: [David Hart](http://github.com/hartbit/) +* Author: [David Hart](http://github.com/hartbit/) * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) * Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170529/036909.html) diff --git a/proposals/0181-package-manager-cpp-language-version.md b/proposals/0181-package-manager-cpp-language-version.md index 8f53eba3cd..7389edd4bb 100644 --- a/proposals/0181-package-manager-cpp-language-version.md +++ b/proposals/0181-package-manager-cpp-language-version.md @@ -1,7 +1,7 @@ # Package Manager C/C++ Language Standard Support * Proposal: [SE-0181](0181-package-manager-cpp-language-version.md) -* Authors: [Ankit Aggarwal](https://github.com/aciidb0mb3r) +* Author: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Daniel Dunbar](https://github.com/ddunbar) * Status: **Implemented (Swift 4)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170717/038142.html) diff --git a/proposals/0187-introduce-filtermap.md b/proposals/0187-introduce-filtermap.md index 73774d68f2..c7e03ba0cf 100644 --- a/proposals/0187-introduce-filtermap.md +++ b/proposals/0187-introduce-filtermap.md @@ -1,7 +1,7 @@ # Introduce Sequence.compactMap(_:) * Proposal: [SE-0187](0187-introduce-filtermap.md) -* Authors: [Max Moiseev](https://github.com/moiseev) +* Author: [Max Moiseev](https://github.com/moiseev) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Implemented (Swift 4.1)** * Implementation: [apple/swift#12819](https://github.com/apple/swift/pull/12819) diff --git a/proposals/0189-restrict-cross-module-struct-initializers.md b/proposals/0189-restrict-cross-module-struct-initializers.md index 5574c9697b..dcf935f60c 100644 --- a/proposals/0189-restrict-cross-module-struct-initializers.md +++ b/proposals/0189-restrict-cross-module-struct-initializers.md @@ -1,7 +1,7 @@ # Restrict Cross-module Struct Initializers * Proposal: [SE-0189](0189-restrict-cross-module-struct-initializers.md) -* Authors: [Jordan Rose](https://github.com/jrose-apple) +* Author: [Jordan Rose](https://github.com/jrose-apple) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Implemented (Swift 4.1)** * Implementation: [apple/swift#12834](https://github.com/apple/swift/pull/12834) diff --git a/proposals/0191-eliminate-indexdistance.md b/proposals/0191-eliminate-indexdistance.md index b4ade311f0..d03b54e62b 100644 --- a/proposals/0191-eliminate-indexdistance.md +++ b/proposals/0191-eliminate-indexdistance.md @@ -1,7 +1,7 @@ # Eliminate `IndexDistance` from `Collection` * Proposal: [SE-0191](0191-eliminate-indexdistance.md) -* Authors: [Ben Cohen](https://github.com/airspeedswift) +* Author: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 4.1)** * Implementation: [apple/swift#12641](https://github.com/apple/swift/pull/12641) diff --git a/proposals/0192-non-exhaustive-enums.md b/proposals/0192-non-exhaustive-enums.md index 8465f19a22..b52f850859 100644 --- a/proposals/0192-non-exhaustive-enums.md +++ b/proposals/0192-non-exhaustive-enums.md @@ -1,7 +1,7 @@ # Handling Future Enum Cases * Proposal: [SE-0192](0192-non-exhaustive-enums.md) -* Authors: [Jordan Rose](https://github.com/jrose-apple) +* Author: [Jordan Rose](https://github.com/jrose-apple) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Active Review (March 16...23, 2018)** * Implementation: [apple/swift#14945](https://github.com/apple/swift/pull/14945) diff --git a/proposals/0193-cross-module-inlining-and-specialization.md b/proposals/0193-cross-module-inlining-and-specialization.md index e41cc28b2b..9227726413 100644 --- a/proposals/0193-cross-module-inlining-and-specialization.md +++ b/proposals/0193-cross-module-inlining-and-specialization.md @@ -1,7 +1,7 @@ # Cross-module inlining and specialization * Proposal: [SE-0193](0193-cross-module-inlining-and-specialization.md) -* Authors: [Slava Pestov](https://github.com/slavapestov) +* Author: [Slava Pestov](https://github.com/slavapestov) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Accepted** * Evolution review thread: [https://forums.swift.org/t/se-0193-cross-module-inlining-and-specialization/7310](https://forums.swift.org/t/se-0193-cross-module-inlining-and-specialization/7310) diff --git a/proposals/0198-playground-quicklook-api-revamp.md b/proposals/0198-playground-quicklook-api-revamp.md index 1d41d3d315..66229956a3 100644 --- a/proposals/0198-playground-quicklook-api-revamp.md +++ b/proposals/0198-playground-quicklook-api-revamp.md @@ -1,7 +1,7 @@ # Playground QuickLook API Revamp # * Proposal: [SE-0198](0198-playground-quicklook-api-revamp.md) -* Authors: [Connor Wakamo](https://github.com/cwakamo) +* Author: [Connor Wakamo](https://github.com/cwakamo) * Implementation: Swift 4.1 deprecation ([apple/swift#13911](https://github.com/apple/swift/pull/13911)], introduction of new protocol ([apple/swift-xcode-playground-support#21](https://github.com/apple/swift-xcode-playground-support/pull/21)), Swift 5 removal + shim library ([apple/swift#14252](https://github.com/apple/swift/pull/14252), [apple/swift-corelibs-foundation#1415](https://github.com/apple/swift-corelibs-foundation/pull/1415), [apple/swift-xcode-playground-support#20](https://github.com/apple/swift-xcode-playground-support/pull/20)) * Review Manager: [Ben Cohen](https://github.com/airspeedswift/) * Review thread: [SE-0198 review](https://forums.swift.org/t/se-0198-playground-quicklook-api-revamp/9448/16) diff --git a/proposals/0199-bool-toggle.md b/proposals/0199-bool-toggle.md index ac2e1e13e7..d289733c68 100644 --- a/proposals/0199-bool-toggle.md +++ b/proposals/0199-bool-toggle.md @@ -1,7 +1,7 @@ # Adding `toggle` to `Bool` * Proposal: [SE-0199](0199-bool-toggle.md) -* Authors: [Chris Eidhof](http://chris.eidhof.nl) +* Author: [Chris Eidhof](http://chris.eidhof.nl) * Review Manager: [Ben Cohen](https://github.com/airspeedswift/) * Status: **Implemented (Swift 4.2)** * Decision notes: [Rationale](https://forums.swift.org/t/accepted-se-199-add-toggle-to-bool/10681) diff --git a/proposals/0200-raw-string-escaping.md b/proposals/0200-raw-string-escaping.md index 8cdbe10b1a..15253adf54 100644 --- a/proposals/0200-raw-string-escaping.md +++ b/proposals/0200-raw-string-escaping.md @@ -1,7 +1,7 @@ # "Raw" mode string literals * Proposal: [SE-0200](0200-raw-string-escaping.md) -* Authors: [John Holdsworth](https://github.com/johnno1962) +* Author: [John Holdsworth](https://github.com/johnno1962) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active Review (March 16...26, 2018)** * Implementation: [apple/swift#13055](https://github.com/apple/swift/pull/13055) diff --git a/proposals/0202-random-unification.md b/proposals/0202-random-unification.md index d6f4aec936..62409838cd 100644 --- a/proposals/0202-random-unification.md +++ b/proposals/0202-random-unification.md @@ -1,7 +1,7 @@ # Random Unification * Proposal: [SE-0202](0202-random-unification.md) -* Authors: [Alejandro Alonso](https://github.com/Azoy) +* Author: [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Ben Cohen](http://github.com/AirspeedSwift/) * Status: **Active Review (March 23...April 3, 2018)** * Implementation: [apple/swift#12772](https://github.com/apple/swift/pull/12772) diff --git a/proposals/0203-rename-sequence-elements-equal.md b/proposals/0203-rename-sequence-elements-equal.md index 39fcd138a2..0d2ce52610 100644 --- a/proposals/0203-rename-sequence-elements-equal.md +++ b/proposals/0203-rename-sequence-elements-equal.md @@ -1,7 +1,7 @@ # Rename Sequence.elementsEqual * Proposal: [SE-0203](0203-rename-sequence-elements-equal.md) -* Authors: [Xiaodi Wu](https://github.com/xwu) +* Author: [Xiaodi Wu](https://github.com/xwu) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Active Review (March 29...April 6, 2018)** * Implementation: [apple/swift#12884](https://github.com/apple/swift/pull/12884) From fce64e89f97f3683be4682d106ff79d927cde830 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 1 Apr 2018 17:44:11 -0700 Subject: [PATCH 0625/4563] more pedantic editing, I don't think anything here is controversial. --- proposals/0202-random-unification.md | 44 ++++++++++++++-------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/proposals/0202-random-unification.md b/proposals/0202-random-unification.md index 62409838cd..9e730a0fb9 100644 --- a/proposals/0202-random-unification.md +++ b/proposals/0202-random-unification.md @@ -83,7 +83,7 @@ Considering all of this, I believe it is very important that Swift provides deve ### Random Number Generator -To kick this off, we will be discussing the rngs that each operating system will utilize. We will make the default random generator cryptographically secure. This means on each operating system, the random api for Swift will produce a secure random number. In addition to being cryptographically secure, the default random generator will also be thread safe. It is also worth noting that if any of the following fail, particularly reading from /dev/urandom, then we produce a fatal error and abort the application. Reasons why I went with this approach in Alternatives Considered at the bottom of this proposal. +To kick this off, we will be discussing the RNGs that each operating system will utilize. We will make the default random generator cryptographically secure. This means on each operating system, the random API for Swift will produce a secure random number. In addition to being cryptographically secure, the default random generator will also be thread safe. It is also worth noting that if any of the following fail, particularly reading from /dev/urandom, then we produce a fatal error and abort the application. Reasons why I went with this approach in Alternatives Considered at the bottom of this proposal. #### Darwin Platform @@ -95,7 +95,7 @@ To kick this off, we will be discussing the rngs that each operating system will We require that the kernel version be >= 3.17 as this was the release that introduced the `getrandom(2)` system call. We also require that glibc be >= 2.25 because this release exposed the `` header. -| Kernel Version < 3.17 && Glibc Version < 2.25 | Kernel Version >= 3.17 && Glibc Version >= 2.25 | +| Kernel Version < 3.17 || Glibc Version < 2.25 | Kernel Version >= 3.17 && Glibc Version >= 2.25 | |:---------------------------------------------:|:-----------------------------------------------:| | Read from `/dev/urandom` | Use `getrandom(2)` | @@ -107,11 +107,11 @@ We require that the kernel version be >= 3.17 as this was the release that intro ### Random API -For the core API, introduce a new protocol named `RandomNumberGenerator`. This type is used to define rngs that can be used within the stdlib. Developers can conform to this type and use their own custom rng throughout their whole application. +For the core API, introduce a new protocol named `RandomNumberGenerator`. This type is used to define RNGs that can be used within the stdlib. Developers can conform to this type and use their own custom RNG throughout their whole application. -Then for the stdlib's default rng implementation, introduce a new struct named `Random`. This struct contains a singleton called `default` which provides access to the methods of `RandomNumberGenerator`. +Then for the stdlib's default RNG implementation, introduce a new struct named `Random`. This struct contains a singleton called `default` which provides access to the methods of `RandomNumberGenerator`. -Next, we will make extension methods for `FixedWidthInteger`, `BinaryFloatingPoint` and `Bool`. For numeric types, this allows developers to select a value within a range and swap out the rng used to select a value within the range. +Next, we will make extension methods for `FixedWidthInteger`, `BinaryFloatingPoint` and `Bool`. For numeric types, this allows developers to select a value within a range and swap out the RNG used to select a value within the range. `FixedWidthInteger` example: ```swift @@ -160,7 +160,7 @@ print(greetings.random(using: myCustomRandomNumberGenerator) as Any) // This ret #### Shuffle API -As a result of adding the random api, it only makes sense to utilize that power to fuel the shuffle methods. We extend `MutableCollection` to add a method to shuffle the collection itself, and extend `Sequence` to add a method to return a shuffled version of itself in a new array. Example: +As a result of adding the random API, it only makes sense to utilize that power to fuel the shuffle methods. We extend `MutableCollection` to add a method to shuffle the collection itself, and extend `Sequence` to add a method to return a shuffled version of itself in a new array. Example: ```swift var greetings = ["hey", "hi", "hello", "hola"] @@ -180,7 +180,7 @@ The actual implementation can be found here: [apple/swift#12772](https://github. ```swift public protocol RandomNumberGenerator { // This determines the functionality for producing a random number. - // Required to implement by all rngs. + // Required to implement by all RNGs. func next() -> UInt64 } @@ -194,9 +194,9 @@ extension RandomNumberGenerator { public func next(upperBound: T) -> T } -// The stdlib rng +// The stdlib RNG. public struct Random : RandomNumberGenerator { - // Public facing api + // Public facing API public static let `default` = Random() // Prevents initialization of this struct @@ -223,7 +223,7 @@ extension Collection { using generator: T ) -> Element? - /// Uses the standard library's default rng + /// Uses the standard library's default RNG public func random() -> Element? { return random(using: Random.default) } @@ -241,7 +241,7 @@ where Bound : FixedWidthInteger, using generator: T ) -> Element? - /// Uses the standard library's default rng + /// Uses the standard library's default RNG public func random() -> Element? { return random(using: Random.default) } @@ -258,7 +258,7 @@ where Bound : FixedWidthInteger, using generator: T ) -> Element? - /// Uses the standard library's default rng + /// Uses the standard library's default RNG public func random() -> Element? { return random(using: Random.default) } @@ -282,7 +282,7 @@ where Self.Stride : SignedInteger, using generator: T ) -> Self - /// Uses the standard library's default rng + /// Uses the standard library's default RNG public static func random(in range: Range) -> Self { return Self.random(in: range, using: Random.default) } @@ -292,7 +292,7 @@ where Self.Stride : SignedInteger, using generator: T ) -> Self - /// Uses the standard library's default rng + /// Uses the standard library's default RNG public static func random(in range: ClosedRange) -> Self { return Self.random(in: range, using: Random.default) } @@ -317,7 +317,7 @@ where Self.RawSignificand : FixedWidthInteger, using generator: T ) -> Self - /// Uses the standard library's default rng + /// Uses the standard library's default RNG public static func random(in range: Range) -> Self { return Self.random(in: range, using: Random.default) } @@ -327,7 +327,7 @@ where Self.RawSignificand : FixedWidthInteger, using generator: T ) -> Self - /// Uses the standard library's default rng + /// Uses the standard library's default RNG public static func random(in range: ClosedRange) -> Self { return Self.random(in: range, using: Random.default) } @@ -343,7 +343,7 @@ extension Bool { using generator: T ) -> Bool - /// Uses the standard library's default rng + /// Uses the standard library's default RNG public static func random() -> Bool { return Bool.random(using: Random.default) } @@ -358,7 +358,7 @@ extension Sequence { using generator: T ) -> [Element] - /// Uses the standard library's default rng + /// Uses the standard library's default RNG public func shuffled() -> [Element] { return shuffled(using: Random.default) } @@ -369,7 +369,7 @@ extension MutableCollection { using generator: T ) - /// Uses the standard library's default rng + /// Uses the standard library's default RNG public mutating func shuffle() { shuffle(using: Random.default) } @@ -406,17 +406,17 @@ let randomDice = Int.random(in: 1 ... 6)! "I just want a random dice roll, what is this ! the compiler is telling me to add?" -This syntax wouldn't make sense for a custom rng that deterministically generates numbers with no fail. This also goes against the "general use" design that the core team and much of the community expressed. +This syntax wouldn't make sense for a custom RNG that deterministically generates numbers with no fail. This also goes against the "general use" design that the core team and much of the community expressed. Looking at Rust, we can observe that they also abort when an unexpected error occurs with any of the forms of randomness. [source](https://doc.rust-lang.org/rand/src/rand/os.rs.html) -It would be silly to account for these edge cases that would only happen to those who need to update their os, optimize their file descriptors, or deleted their `/dev/urandom`. Accounting for these cases sacrifices the clean api for everyone else. +It would be silly to account for these edge cases that would only happen to those who need to update their os, optimize their file descriptors, or deleted their `/dev/urandom`. Accounting for these cases sacrifices the clean API for everyone else. ### Shouldn't this fallback on something more secure at times of low entropy? Thomas Hühn explains it very well [here](https://www.2uo.de/myths-about-urandom/). There is also a deeper discussion [here talking about python's implementation](https://www.python.org/dev/peps/pep-0524). Both articles discuss that even though /dev/urandom may not have enough entropy at a fresh install, "It doesn't matter. The underlying cryptographic building blocks are designed such that an attacker cannot predict the outcome." Using `getrandom(2)` on linux systems where the kernel version is >= 3.17, will block if it decides that the entropy pool is too small. In python's implementation, they fallback to reading `/dev/urandom` if `getrandom(2)` decides there is not enough entropy. -### Why not make the default rng non-secure? +### Why not make the default RNG non-secure? Swift is a safe language which means that it shouldn't be encouraging non-experienced developers to be pushing unsecure code. Making the default secure removes this issue and gives developers a feeling of comfort knowing their code is ready to go out of the box. From c0a0647019f5671d3a00a13513d45d1cb861050a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Tue, 3 Apr 2018 11:21:54 -0700 Subject: [PATCH 0626/4563] Update 0201-package-manager-local-dependencies.md --- proposals/0201-package-manager-local-dependencies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0201-package-manager-local-dependencies.md b/proposals/0201-package-manager-local-dependencies.md index 5d107d051d..550857e2b7 100644 --- a/proposals/0201-package-manager-local-dependencies.md +++ b/proposals/0201-package-manager-local-dependencies.md @@ -3,7 +3,7 @@ * Proposal: [SE-0201](0201-package-manager-local-dependencies.md) * Author: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **Active Review (March 22...29, 2018)** +* Status: **Accepted** ## Introduction From abf2eff95f01e8fbfe3c053b9471c103d5b19b6d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 3 Apr 2018 11:33:48 -0700 Subject: [PATCH 0627/4563] SE-0200 '"Raw" mode string literals' is returned for revision. --- proposals/0200-raw-string-escaping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0200-raw-string-escaping.md b/proposals/0200-raw-string-escaping.md index 15253adf54..2c9b4c5951 100644 --- a/proposals/0200-raw-string-escaping.md +++ b/proposals/0200-raw-string-escaping.md @@ -3,7 +3,7 @@ * Proposal: [SE-0200](0200-raw-string-escaping.md) * Author: [John Holdsworth](https://github.com/johnno1962) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review (March 16...26, 2018)** +* Status: ****Returned for revision** * Implementation: [apple/swift#13055](https://github.com/apple/swift/pull/13055) * Bugs: [SR-6362](https://bugs.swift.org/browse/SR-6362) From 102b2f2770f0dab29f254a254063847388647a4a Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Tue, 3 Apr 2018 12:58:26 -0700 Subject: [PATCH 0628/4563] Remove some extra asterisks. --- proposals/0200-raw-string-escaping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0200-raw-string-escaping.md b/proposals/0200-raw-string-escaping.md index 2c9b4c5951..aa150eb4bb 100644 --- a/proposals/0200-raw-string-escaping.md +++ b/proposals/0200-raw-string-escaping.md @@ -3,7 +3,7 @@ * Proposal: [SE-0200](0200-raw-string-escaping.md) * Author: [John Holdsworth](https://github.com/johnno1962) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: ****Returned for revision** +* Status: **Returned for revision** * Implementation: [apple/swift#13055](https://github.com/apple/swift/pull/13055) * Bugs: [SR-6362](https://bugs.swift.org/browse/SR-6362) From cfff096c10e67ae8fba2aa3977f7dc127c0f2ef5 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Tue, 3 Apr 2018 13:00:50 -0700 Subject: [PATCH 0629/4563] Conform SE-0198's implementation status to the template. --- proposals/0198-playground-quicklook-api-revamp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0198-playground-quicklook-api-revamp.md b/proposals/0198-playground-quicklook-api-revamp.md index 66229956a3..962e6aef4c 100644 --- a/proposals/0198-playground-quicklook-api-revamp.md +++ b/proposals/0198-playground-quicklook-api-revamp.md @@ -5,7 +5,7 @@ * Implementation: Swift 4.1 deprecation ([apple/swift#13911](https://github.com/apple/swift/pull/13911)], introduction of new protocol ([apple/swift-xcode-playground-support#21](https://github.com/apple/swift-xcode-playground-support/pull/21)), Swift 5 removal + shim library ([apple/swift#14252](https://github.com/apple/swift/pull/14252), [apple/swift-corelibs-foundation#1415](https://github.com/apple/swift-corelibs-foundation/pull/1415), [apple/swift-xcode-playground-support#20](https://github.com/apple/swift-xcode-playground-support/pull/20)) * Review Manager: [Ben Cohen](https://github.com/airspeedswift/) * Review thread: [SE-0198 review](https://forums.swift.org/t/se-0198-playground-quicklook-api-revamp/9448/16) -* Status: **Implemented in 4.1** +* Status: **Implemented (Swift 4.1)** + +## Introduction + +This proposal introduces an annotating forced-unwrapping operator to the Swift standard library. It augments the `?`, `??`, and `!` family, adding `!!`. This "unwrap or die" operator provides code-sourced rationales for failed unwraps, supporting self-documentation and safer development. The `!!` operator is commonly implemented in the wider Swift Community and should be considered for official adoption. + +The new operator benefits both experienced and new Swift users. It takes this form: + +```let value = wrappedValue !! <# "Explanation why lhs cannot be nil." #>``` + +It provides a sugared and succinct equivalent of the following `guard`, unwrapping its left hand value and branching to a fatal error on nil values: + +``` +guard let value = wrappedValue else { + fatalError(<# "Explanation why lhs cannot be nil." #>) +} +``` + +This approach replaces commented versions that do not emit explanations when an unwrap fails: + +``` +let value = wrappedValue! // Explanation why lhs cannot be nil +``` + +Adopting this operator: + +* Encourages a thoughtful approach to unwrapping, +* Promotes reasons for trapping during unwraps, which are visible at run time, and +* Provides a succinct and easily-taught form for new Swift learners. + + +*This proposal was first discussed on the Swift Evolution list in the +[\[Pitch\] Introducing the "Unwrap or Die" operator to the standard library](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170626/037730.html) thread. It has been further discussed in the Swift Forums on the [Resolved: Insert "!" is a bad fixit"](https://forums.swift.org/t/resolved-insert-is-a-bad-fixit/10764) thread.* + +## Motivation + +"Unwrap or Die" has been widely adopted in the Swift community. This approach provides a best practices approach that establishes informative run-time diagnostics with compiler-checked rationales (the rhs is mandatory). Relying solely on comments only conveys in the source code itself the reason why the developer thinks the value cannot be nil. Requiring a string on the rhs of `!!` provides useful information all the way from source to the console should the underlying guarantee fail at runtime. This produces "better" failures with explicit explanations. If you’re going to write a comment, why not make that comment useful for debugging at the same time? + +### The Naive User / Fixit Problem + +Swift's [`Insert "!"` fixit](https://i.imgur.com/I05TkbJ.jpg) is a dangerous enticement to new Swift language users. Force-unwrapping should be used sparingly and thoughtfully. Beginners have a strong tendency to throw code at the compiler until it runs. Consider the following line of code and its fixit. Mashing the "Fix" button inserts an unwrap after the `url` instance. + +![](https://i.imgur.com/I05TkbJ.jpg) + +```swift +let resourceData = try String(contentsOf: url, encoding: .utf8) + +// Error: Value of optional type 'URL?' not unwrapped; did you mean to use '!' or '?'? +// Fix: Insert '!' +``` + +Experienced developers easily distinguish whether an optional value reflects an overlooked error condition, in which case they rearchitect to introduce a better pattern, or if the value in question is guaranteed to never contain nil and can safely support an unwrap. + +Inexperienced developers, unless they’re moving from a language with similar constructs, usually will not make such distinctions. They’re focused on getting past a compilation barrier, without realizing the hazard of nil values at the point of use. + +Quincey Morris writes with respect to the `url` example: + +> The problem with the fixit (for inexperienced Swift programmers) is that this is almost certainly not the point at which things went wrong. Almost always (for the inexperienced) the problem is that url is optional by accident, and _the correct fix is to add a ! to the RHS of the assignment from which url’s type was inferred — or, at least, to handle the optional there)_. + +The "fixit" solution adds an unwrap at the point of use: + +```swift +let resourceData = try String(contentsOf: url!, encoding: .utf8) +``` + +A better solution tests and unwraps _at the point of declaration_ using `if-let` or `guard-let`: + +```swift +let destination = "http://swift.org" +guard let url = URL(string: destination) else { + fatalError("Invalid URL string: \(destination)") +} + +let destination = "☹️" +guard let url = URL(string: destination) else { + fatalError("Invalid URL string: \(destination)") +} +``` + +Swift's "Add !" is a syntactic fix, not a semantic one. Swift cannot holistically evaluate user code intent. It cannot recommend or fixit a `guard let` or `if let` alternative at a disjoint location that leads to the point of error. + +### Moral Hazards + +Fixits should not be used as bandaids to make things compile. Swift's "Add !" fixit is a modest courtesy for experienced users and a moral hazard for the inexperienced. Stack Overflow and the developer forums are littered with questions regarding ["unexpectedly found nil" errors at runtime](https://duckduckgo.com/?q=unexpectedly+found+nil&bext=msl&atb=v99-7_g&ia=qa): + +> If you go through the Swift tab on Stack Overflow and take a drink every time you see a blatantly inappropriate use of ! in a questioner’s code due to them just mashing the fix-it, you will soon be dead of alcohol poisoning. +> +> -- _Charles Srstka_ + +Introducing `!!` allows Swift to offer a better, more educational fixit that guides new users to better solutions. If the expression cannot guarantee that the lhs is non-nil, coders are better served using `if let`/`guard let` constructions. Replacing the `!` fixit with `!! <# "Explanation why the left hand value cannot be nil." #>` offers a user-supporting hint that counters "Value of optional type not unwrapped" confusion. + +### Runtime Diagnostics + +Forced unwraps are an essential part of the Swift programming language. In many cases, a `!` forced unwrap is the correct way to unwrap an optional value. This proposal does not challenge this. The new operator promotes safety, maintainability, and readability as an alternative, not evolutionary, use. `!!`'s documentary "guided landing" explains why unwrapping is guaranteed to be non-nil. + +It takes its cue from existing Swift constructs (like `precondition` and `assert`) that incorporate meaningful output strings when the app traps: + +```swift +assert(!array.isEmpty, "Array guaranteed to be non-empty because...") +let lastItem = array.last! +``` + +Guard statements, assertions, preconditions, and fatal errors allow developers to backtrack and correct assumptions that underlie their design. When an application traps from annotated assertions and preconditions, debug console output explains *why* straight away. You don't have to hunt down the code, read the line that failed, then establish the context around the line to understand the reason. Embedded rationales are even more important when you didn’t write this code yourself. + +Incorporating messages explains the use of forced unwraps in code. This provides better self documentation of facts that are known to be true. These messages support better code reading, maintenance, and future modifications. + +When an optional can a priori be *guaranteed to be non-nil*, using guards, assertions, preconditions, etc, are relatively heavy-handed approaches to document these established truths. When you already know that an array is not-empty, you should be able to specify this in a single line using a simple operator: + +```swift +// Existing force-unwrap +let lastItem = array.last! // Array guaranteed to be non-empty because... + +// Proposed unwrap operator with fallback explanation +let lastItem = array.last !! "Array guaranteed to be non-empty because..." +``` + +Consider the following scenario of a custom view controller subclass that only accepts children of a certain kind. It is known a priori that the cast will succeed: + +```swift +let existing = childViewControllers as? Array + !! "TableViewController must only have TableRowViewControllers as children" +``` + +This pattern extends to any type-erased or superclass where a cast will always be valid. + +## Examples of Real-World Use + +Here are a variety of examples that demonstrate the `!!` operator in real-world use: + +```swift +// In a right-click gesture recognizer action handler +let event = NSApp.currentEvent + !! "Trying to get current event for right click, but there's no event" + +// In a custom view controller subclass that only +// accepts children of a certain kind: +let existing = childViewControllers as? Array + !! "TableViewController must only have TableRowViewControllers as children" + +// Providing a value based on an initializer that returns an optional: +lazy var sectionURL: URL = { + return URL(string: "myapp://section/\(identifier)") + !! "can't create URL for section \(identifier)" +}() + +// Retrieving an image from an embedded framework: +private static let addImage: NSImage = { + let bundle = Bundle(for: FlagViewController.self) + let image = bundle.image(forResource: "add") !! "Missing 'add' image" + image.isTemplate = true + return image +}() + +// Asserting consistency of an internal model: +let flag = command.flag(with: flagID) !! "Unable to retrieve non-custom flag for id \(flagID.string)" + +// drawRect: +override draw(_ rect: CGRect) { + let context = UIGraphicsGetCurrentContext() !! "`drawRect` context guarantee was breeched" +} +``` + +The `!!` operator generally falls in two use scenarios: + +1. **Asserting System Framework Correctness**: The `NSApp.currentEvent` property returns an `Optional` as there’s not always a current event going on. It is always safe to assert an actual event in the action handler of a right-click gesture recognizer. If this ever fails, `!!` provides an immediately and clear description of where the system framework has not worked according to expectations. + +2. **Asserting Application Logic Correctness**: The `!!` operator ensures that outlets are properly hooked up and that the internal data model is in a consistent state. The related error messages explicitly mention specific outlet and data details. + +These areas identify when resources haven't been added to the right target, when a URL has been mis-entered, or when a model update has not propagated completely to its supporting use. Incorporating a diagnostic message, provides immediate feedback as to why the code is failing and where. + +### The Black Swan Deployment + +In one [real-world case](http://ericasadun.com/2017/01/23/safe-programming-optionals-and-hackintoshes/), a developer's deployed code crashed when querying Apple's [smart battery interface](https://developer.apple.com/library/content/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/PowerMgmt/PowerMgmt.html) on a Hackintosh. Since the laptop in question wasn’t an actual Apple platform, it used a simulated AppleSmartBatteryManager interface. In this case, the simulated manager didn’t publish the full suite of values normally guaranteed by the manager’s API. The developer’s API-driven contract assumptions meant that forced unwraps broke his app: + +> Since IOKit just gives you back dictionaries, a missing key, is well… not there, and nil. you know how well Swift likes nils… + +Applications normally can’t plan for, anticipate, or provide workarounds for code running on unofficial platforms. There are too many unforeseen factors that cannot be incorporated into realistic code that ships. Adopting a universal "unwrap or die" style with explanations enables you to "guide the landing" on these unforseen ["Black Swan"](https://en.wikipedia.org/wiki/Black_swan_theory) failures: + +``` +guard let value = dict[guaranteedKey] + else { + fatalError("Functionality compromised when unwrapping " + + "Apple Smart Battery Dictionary value.") + return +} + +// or more succinctly + +let value = dict[guaranteedKey] !! "Functionality compromised when unwrapping Apple Smart Battery Dictionary value." +``` + +The `!!` operator reduces the overhead involved in debugging unexpected Black Swan deployments. This practice adds robustness and assumes that in reality bad execution can happen for the oddest of reasons. Providing diagnostic information even when your assumptions are "guaranteed" to be correct is a always positive coding style. + +### Real World Deployment + +The `!!` operator is in reasonably wide use in the Swift community, including in the personal toolboxes of this proposal's authors. For example, Constantino Tsarouhas explains how `!!` has affected his development, enhancing code reading and introducing mindful unwrapping practices: + +> I’ve been happily using !! in my codebases for over a year. It significantly improved understanding what my code does, even after several months of not reading it. I almost never use ! now. The extra keystrokes also act as a deterrent to use this escape hatch too often. + +## On Forced Unwraps + +This proposal _does not_ eliminate or prejudge the `!`operator. Using `!!` should be a positive house standards choice, especially when the use of explanatory text becomes cumbersome. The following example using an unwrapping operator: + +```swift +// Constructed date values will always produce valid results +return Date.sharedCalendar.date(byAdding: rhs, to: lhs)! +``` + +is far simpler than an equivalent version using `guard`: + +```swift +guard let date = Date.sharedCalendar.date(byAdding: lhs, to: rhs) else { + // This should never happen + fatalError("Constructed date values will always produce valid results") +} +return date +``` + +and slightly more succinct than: + +```swift +return Date.sharedCalendar.date(byAdding: lhs, to: rhs) + !! "Constructed date values will always produce valid results" +``` + +The major difference is that the first example lacks the runtime diagnostic output of the latter. This last example accrues all the benefits of `!!`. Those benefits are ultimately the choice of the adopter. + +An often-touted misconception exists that force unwraps are, in and of themselves, *bad*: that they were only created to accommodate legacy apps, and that you should never use force-unwrapping in your code. This isn’t true. + +There are many good reasons to use force unwraps, though if you're often reaching for it, it's a bad sign. Force-unwrapping can be a better choice than throwing in meaningless default values with nil-coalescing or applying optional chaining when the presence of nil would indicate a serious failure. + +Introducing the `!!` operator endorses and encourages the use of "mindful force-unwrapping". It incorporates the reason *why* the forced unwrap should be safe (for example, *why* the array can’t be empty at this point, not just that it is unexpectedly empty). If you’re already going to write a comment, why not make that comment useful for debugging at the same time? + +Using `!!` provides syntactic sugar for the following common unwrap pattern: + +```swift +guard let y = x + else { fatalError("reason") } + +// becomes + +let y = x !! "reason" + +// and avoids + +let y = x! // reason +``` + +Although comments document in-code reasoning, these explanations are not emitted when the application traps on the forced unwrap: + +> As the screener of a non-zero number of radars resulting from unwrapped nils, I would certainly appreciate more use of `guard let x = x else { fatalError("explanation") }` and hope that `!!` would encourage it. +> -- Ben Cohen + +Sometimes it’s not necessary to explain your use of a forced unwrap. In those cases the normal `!` operator will remain, even after the introduction of `!!`. You can continue using `!`, as before, just as you can leave off the string from a precondition. + +Similarly, updating Swift's recommended fixit from `Insert !` to `Insert <# "Explanation why lhs cannot be nil." #>` does not prevent the use of `!` for the experienced user and requires no more keystrokes or actions on the part of the experienced user. + +## On Adding a New Operator to Swift + +Although burning a new operator is a serious choice, `!!` is a good candidate for adoption: + +* It matches and parallels the existing `??` operator. +* It fosters better understanding of optionals and the legitimate use of force-unwrapping in a way that encourages safe coding and good documentation, both in source and at run-time. +* `!!` sends the right semantic message. It communicates that "unwrap or die" is an unsafe operation and that failures should be both extraordinary and explained. + +The new operator is consciously based on `!`, the *unsafe* forced unwrap operator, and not on `??`, the *safe* fallback nil-coalescing operator. Its symbology therefore follows `!` and not `?`. + +## Detailed Design + +```swift +infix operator !!: NilCoalescingPrecedence + +extension Optional { + /// Performs an unsafe forced-unwrap operation, returning + /// the wrapped value of an `Optional` instance or + /// executing `fatalError` using the message on the rhs + /// of the operator. + /// + /// The result of a successful operation will be the same type + /// as the wrapped value of the left-hand side argument. + /// + /// The `optional` lhs is checked first, and a `fatalError` + /// is called only if the left hand side is nil. + /// + /// Use this operator when the lhs is guaranteed to be non-nil, + /// for example: + /// + /// ``` + /// // This URL is well formed + /// let normalURL = URL(string: "http://swift.org") + /// !! "URL is not well formed" + /// + /// // An emoji character cannot be used to construct a URL + /// let badURL = URL(string: "😱") + /// !! "URL is not well formed" + /// ``` + /// + /// - Parameters: + /// - optional: An optional value. + /// - message: A message to emit via `fatalError` upon + /// failing to unwrap the optional. + public static func !!( + optional: Optional, + errorMessage: @autoclosure () -> String + ) -> Wrapped { + guard let wrapped = optional else { fatalError(errorMessage()) } + return wrapped + } +} +``` + +### Updating Fixits + +A well-designed error message explains why an error occurred and offer user-supporting directions for remedying the situation. The current error/fixit works like this: + +```C++ +ERROR(missing_unwrap_optional,none, + "value of optional type %0 not unwrapped; did you mean to use '!' " + "or '?'?", + (Type)) + +diag.fixItInsertAfter(affected->getEndLoc(), "!"); +``` + +Adopting `!!` allows Swift to refresh the error message in question and provide multiple fixits: + +> Found unexpected value of optional type %0; did you mean to unwrap this +> optional or provide a default fallback value? Use `if-let` and +> `if-guard` statements to unwrap optionals before use in statements +> and expressions. +> +> * Insert '!!' to unwrap values that cannot be nil: `!! <# "statement why lhs cannot be nil." #>` +> * Insert '??' to provide a default fallback value: `?? <# default value #>` + +### Optimized Builds and Runtime Errors + +With one notable exception, the `!!` operator should follow the same semantics as `Optional.unsafelyUnwrapped`, which establishes a precedent for this approach: + +> "The unsafelyUnwrapped property provides the same value as the forced unwrap operator (postfix !). However, in optimized builds (-O), no check is performed to ensure that the current instance actually has a value. Accessing this property in the case of a nil value is a serious programming error and could lead to undefined behavior or a runtime error." + +By following `Optional.unsafelyUnwrapped`, this approach is consistent with Swift's [error handling system](https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#logic-failures): + +> "Logic failures are intended to be handled by fixing the code. It means checks of logic failures can be removed if the code is tested enough. Actually checks of logic failures for various operations, `!`, `array[i]`, `&+` and so on, are designed and implemented to be removed when we use `-Ounchecked`. It is useful for heavy computation like image processing and machine learning in which overhead of those checks is not permissible." + +Like `assert`, `unsafelyUnwrapped` does not perform a check in optimized builds. The forced unwrap `!` operator does as does `precondition`. The new "unwrap or die" operator, `!!`, should behave like `precondition` and not `assert` to preserve trapping information in optimized builds. + +Unfortunately, there is no direct way at this time to emit the `#file` name and `#line` number with the above code. We hope the dev team can somehow work around this limitation to produce that information at the `!!` site. The `!!`-alternative design that uses a `() -> Never` closure in the "Alternative Designs" section provides that information today. + +## Future Directions + +#### Calling Context + +Right now, `fatalError` reports the line and file of the `!!` operator implementation rather than the code where the operator is used. At some point Swift may allow operator implementations with more than two parameters. At such time, the `!!` operator should incorporate the source line and file of the forced unwrap call: + +```swift +public static func !!(optional: Optional, errorMessage: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line) -> Wrapped +``` + +This could be a minimal modification to lib/AST/Decl.cpp:4919, to the `FuncDecl::isBinaryOperator()` implementation, enabling `size() > 2` if `get(2+)->isDefaultArgument()`: + +```c++ + bool FuncDecl::isBinaryOperator() const { + if (!isOperator()) + return false; + + auto *params = getParameterList(getDeclContext()->isTypeContext()); + return params->size() == 2 && + !params->get(0)->isVariadic() && + !params->get(1)->isVariadic(); +} +``` + +Having a line and file reference for the associated failure point would be a major advantage in adopting this proposal, even if some under-the-covers "Swift Magic™" must be applied to the `!!` implementation. + +#### `Never` as a Bottom Type + +If `Never` ever becomes a true bottom type as in [SE-0102](https://github.com/apple/swift-evolution/blob/master/proposals/0102-noreturn-bottom-type.md), Swift will be able to use `fatalError()` on the right hand side of nil-coalescing. + +> If [`Never` as a bottom type] were supported by the compiler, it would enable some potentially useful things, for instance using a nonreturning function directly as a parameter to a higher-order function that expects a result...or allowing a subclass to override a method and covariantly return `Never`. +> +> -- [SE-0102](https://github.com/apple/swift-evolution/blob/master/proposals/0102-noreturn-bottom-type.md#never-as-a-universal-bottom-subtype) + +```swift +// Legal if a (desirable) `Never` bottom type is adopted +let x = y ?? fatalError("reason") +``` + +This proposal supports using a `String` (or more properly a string autoclosure) on the rhs of a `!!` operator in preference to a `Never` bottom type or a `() -> Never` closure with `??` for the reasons that are enumerated here: + +- A string provides the cleanest user experience, and allows the greatest degree of in-place self-documentation. + +- A string respects DRY, and avoids using *both* the operator and the call to `fatalError` or `preconditionFailure` to signal an unsafe condition: +`let last = array.last !! "Array guaranteed non-empty because..." // readable` +versus: +`let last = array.last !! fatalError("Array guaranteed non-empty because...") // redundant` + +- A string allows the operator *itself* to unsafely fail, just as the unary version of `!` does now. It does this with additional feedback to the developer during testing, code reading, and code maintenance. The string provides a self-auditing in-line annotation of the reason why the forced unwrap has been well considered, using a language construct to support this. + +- A string disallows a potentially unsafe `Never` call that does not reflect a serious programming error, for example: +`let last = array.last !! f() + // where func f() -> Never { while true {} }` + +- Using `foo ?? Never` requires a significant foundational understanding of the language, which includes a lot of heavy lifting to understand how and why it works. Using `!!` is a simpler approach that is more easily taught to new developers: "This variation of forced unwrap emits this message if the lhs is nil." Although a `Never` closure solution can be cobbled together in today's Swift, `!!` operator solution can be as well. Neither one requires a fundamental change to the language. + +Pushing forward on this proposal does not in any way reflect on adopting the still-desirable `Never` bottom type. + +## Alternatives Considered + +We present two alternative designs that cover a similar space and an extension of `!!` that adds throwing and `Never` closures. Error-throwing operaters was previously discussed in an early proposal by Pyry Jahkola and Erica Sadun: [Introducing an error-throwing nil-coalescing operator](https://gist.github.com/erica/5a26d523f3d6ffb74e34d179740596f7). + +Reasons why these alternatives may not be ideal are covered in the future directions section, such as why extending the `??` operator to fail or throw creates a foundational re-architecting of its semantics. + +### Introducing `fail(_:)` + +Fail allows you to supply a string or error on the right hand side of the coalescing operator. It infers the `Wrapped` type from context: + +``` +// This URL is well formed +let normalURL = URL(string: "http://swift.org") ?? fail("URL is not well formed") + +// This URL is not well formed and should raise a fatal error +let emojiURL = URL(string: "😱") ?? fail("URL is not well formed") + + // A custom error +let illformedURL = ErrorMessage("URL is not well formed") + +do { + This URL is fine + let normalURL = try URL(string: "http:swift.org") ?? fail(illformedURL) + + This URL is not well formed and will throw + let emojiURL = try URL(string: "😱") ?? fail(illformedURL) +} catch { + print(error) +} +``` + +Here is the basic implementation. It avoids using `Wrapped` as its generic variable as `fail` can be used to replace any contextual type. + +``` +// Inspired by Maz Jaleel, https://github.com/Mazyod + +/// Unconditionally prints a given message and stops execution, +/// allowing placement at any point where a contextual type +/// is required. +/// +/// - Parameters: +/// - message: The string to print. The default is an empty string. +/// - file: The file name to print with `message`. The default is the file +/// where `fail(_:file:line:)` is called. +/// - line: The line number to print along with `message`. The default is the +/// line number where `fail(_:file:line:)` is called. +public func fail( + _ message: String = "", + file: StaticString = #file, + line: UInt = #line +) -> T { + fatalError(message, file: file, line: line) +} + +/// Unconditionally throw an error, allowing placement at +/// any point where a contextual type is required. +/// +/// - Parameters: +/// - error: The error to throw. +public func fail(_ error: E) throws -> T { + throw error +} +``` + +### Extending `??` for throwing and `Never` + +A second alternate design overloads `??`, as was discussed several years ago in [this preliminary proposal](https://gist.github.com/erica/5a26d523f3d6ffb74e34d179740596f7), adopting Dave DeLong's suggestion of overriding `??`: + +``` +// where illFormedURL is an error +let normalURL = try URL(string: "http://swift.org") ?? illFormedURL +let badURL = try URL(string: "😱") ?? illFormedURL + +// Using a `Never` call +let normalURL = URL(string: "http://swift.org") ?? fatalError("URL is not well formed") +let badURL = URL(string: "😱") ?? fatalError("URL is not well formed") +``` + +Here is the design: + +``` +extension Optional { + /// Performs an unwrapping operation, returning the wrapped value of an + /// `Optional` instance or throwing an error corresponding to + /// the rhs of the operation. For example: + /// + /// ``` + /// let normalURL = try URL(string: "http://swift.org") ?? illFormedURL + /// let badURL = try URL(string: "😱") ?? illFormedURL + /// ``` + /// - Parameters: + /// - optionalValue: An optional value. + /// - error: an error to throw should the optional be nil. + public static func ?? ( + optionalValue: Wrapped?, + error: E + ) throws -> Wrapped { + guard let value = optionalValue else { throw error } + return value + } + + /// Performs an unwrapping operation, returning the wrapped value of an + /// `Optional` instance or executing a closure that is guaranteed + /// to never return, such as fatalError(). Use this operator + /// when the rhs should never execute, for example: + /// + /// ``` + /// let normalURL = URL(string: "http://swift.org") + /// ?? fatalError("URL is not well formed") + /// let badURL = URL(string: "😱") ?? fatalError("URL is not well formed") + /// ``` + /// + /// - Parameters: + /// - optionalValue: An optional value. + /// - never: a `Never` closure to execute should the optional be nil. + public static func ?? ( + optionalValue: Wrapped?, + never: @autoclosure () -> Never + ) -> Wrapped { + guard let value = optionalValue else { never() } + return value + } +} +``` + +## Extending `!!` beyond strings + +Both throwing and `Never` behaviors can be added to `!!`. These offer the same community sourced features used in the nil-coalescing design. The `Never` variation provides line and file information at the point of use. + +``` +extension Optional { + /// Performs an unsafe forced-unwrap operation, returning + /// the wrapped value of an `Optional` instance or + /// throwing an error corresponding to the rhs of the operator. + /// + /// The result of a successful operation will be the same type + /// as the wrapped value of the left-hand side argument. + /// + /// The `optional` lhs is checked first, and an error is thrown + /// only if the left hand side is nil. + /// + /// Use this operator when the lhs is guaranteed to be non-nil, + /// for example: + /// + /// ``` + /// // `illFormedURL` is an `Error`-conforming instance. + /// + /// // This URL is well formed + /// let normalURL = try URL(string: "http://swift.org") !! illFormedURL + /// + /// // An emoji character cannot be used to construct a URL + /// let badURL = try URL(string: "😱") !! illFormedURL + /// ``` + /// + /// - Parameters: + /// - optional: An optional value. + /// - error: an error to throw should the optional be nil. + public static func !!( + optional: Optional, + error: E + ) throws -> Wrapped { + guard let wrapped = optional else { throw error } + return value + } + + /// Performs an unsafe forced-unwrap operation, returning + /// the wrapped value of an `Optional` instance or + /// executing a closure that is guaranteed to never return, + /// such as fatalError(). + /// + /// The result of a successful operation will be the same type + /// as the wrapped value of the left-hand side argument. + /// + /// The `optional` lhs is checked first, and an error is thrown + /// only if the left hand side is nil. + /// + /// Use this operator when the rhs should never execute, + /// for example: + /// + /// ``` + /// // This URL is well formed + /// let normalURL = URL(string: "http://swift.org") + /// !! fatalError("URL is not well formed") + /// + /// // An emoji character cannot be used to construct a URL + /// let badURL = URL(string: "😱") + /// !! fatalError("URL is not well formed") + /// ``` + /// + /// - Parameters: + /// - optional: An optional value. + /// - error: an error to throw should the optional be nil. + public static func !!( + optional: Optional, + never: @autoclosure () -> Never + ) throws -> Wrapped { + guard let wrapped = optionalValue else { never() } + return wrapped +} +``` + +## Additional Operators Considered + +The throwing variation of `!!` could be named `?!`, taking an `Error` on the rhs. This extension introduces a non-fatal operator that creates variation of `try` specifically for optionals. + +Adding `?!` produces the following operator family: + +|----------|--------------------| +| Operator | Use | +|----------|--------------------| +| ! | Force unwrap | +| ? | Optional chaining | +| !! | Unwrap or die | +| ?? | Nil coalescing | +| ?! | Unwrap or throw | +| try | Throw on error | +| try! | Die on error | +| try? | Nil on error | +| as! | Die on failed cast | +| as? | Nil on failed cast | +|----------|--------------------| + +## Source compatibility + +This proposal is strictly additive. + +## Effect on ABI stability + +This proposal does not affect ABI stability. + +## Effect on API resilience + +This proposal does not affect ABI resilience. From 92faf86f43d3d61823b71ad663c205a30dba33b1 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Mon, 14 May 2018 09:34:20 -0700 Subject: [PATCH 0691/4563] SE-0212 has been accepted. --- proposals/0212-compiler-version-directive.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0212-compiler-version-directive.md b/proposals/0212-compiler-version-directive.md index 1305a02ceb..81a668d845 100644 --- a/proposals/0212-compiler-version-directive.md +++ b/proposals/0212-compiler-version-directive.md @@ -4,7 +4,7 @@ * Author: [David Hart](https://github.com/hartbit) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Implementation: [apple/swift#15977](https://github.com/apple/swift/pull/15977) -* Status: **In active review (April 30 – May 7)** +* Status: **Accepted** ## Introduction From 7d395fb8d2ebefe6f625dadc6dfb56f66a62a0ff Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 16 May 2018 09:37:50 -0700 Subject: [PATCH 0692/4563] SE-0210 is implemented in Swift 4.2 --- proposals/0210-key-path-offset.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0210-key-path-offset.md b/proposals/0210-key-path-offset.md index e6c6718ae9..28e72d5e13 100644 --- a/proposals/0210-key-path-offset.md +++ b/proposals/0210-key-path-offset.md @@ -3,7 +3,7 @@ * Proposal: [SE-0210](0210-key-path-offset.md) * Authors: [Joe Groff](https://github.com/jckarter) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 4.2)** * Implementation: [apple/swift#15519](https://github.com/apple/swift/pull/15519) ## Introduction From d3ceee204a0cea4fbb6e720137f039b784d92493 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 16 May 2018 09:55:51 -0700 Subject: [PATCH 0693/4563] Update to mark as implemented for 4.2 --- proposals/0207-containsOnly.md | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/proposals/0207-containsOnly.md b/proposals/0207-containsOnly.md index cff80f0ffd..8411c6bb0d 100644 --- a/proposals/0207-containsOnly.md +++ b/proposals/0207-containsOnly.md @@ -4,7 +4,7 @@ * Authors: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) * Implementation: [apple/swift#15120](https://github.com/apple/swift/pull/15120) -* Status: **Accepted With Revision** +* Status: **Implemented (Swift 4.2)** * Decision Notes: [Rationale](https://forums.swift.org/t/se-0207-add-a-containsonly-algorithm-to-sequence/11686/102) ## Introduction @@ -38,12 +38,11 @@ Set(nums).count == 1 && Set(nums).first == 9 ## Proposed solution -Introduce two algorithms on `Sequence` which test every element and return -`true` if they all match: +Introduce an algorithm on `Sequence` which test every element and return +`true` if they all match a given predicate: ```swift -nums.containsOnly(9) -nums.containsOnly(where: isOdd) +nums.allSatisfy(isOdd) ``` on the basis that it aids readability and avoids performance pitfalls from the composed alternatives. @@ -56,13 +55,7 @@ Add the following extensions to `Sequence`: extension Sequence { /// Returns a Boolean value indicating whether every element of the sequence /// satisfies the given predicate. - func containsOnly(where predicate: (Element) throws -> Bool) rethrows -> Bool -} - -extension Sequence where Element: Equatable { - /// Returns a Boolean value indicating whether every element of the sequence - /// equals the given element. - func containsOnly(_ element: Element) -> Bool + func allSatisfy(_ predicate: (Element) throws -> Bool) rethrows -> Bool } ``` @@ -82,6 +75,4 @@ This change is purely additive so has no API resilience consequences. Not adding it, since it can be trivially (if confusingly) composed. -Much name bikeshedding has ensued. The primary rival for `containsOnly` is `all`. `containsOnly` is preferred as it is more explicit, and echoes the existing `contains`. Naming it `all` suggests a renaming of `contains` to `any` would be appropriate – but this is already a fairly heavily-used term elsewhere in Swift, and is less explicit. - -`contains(only:)` is discounted due to trailing closures dropping the argument label, rendering it indistiguishable from `contains(where:)`. +Much name bikeshedding has ensued. Names considered included `containsOnly` and `all`. `all` has strong precedent in other languages, but was considered unclear at the call site (adding an argument label does not help here given trailing closures omit them). Naming it `all` suggests a renaming of `contains` to `any` would be appropriate – but this is already a fairly heavily-used term elsewhere in Swift, and is less explicit. `containsOnly` is is more explicit, and echoes the existing `contains`, but is too easily misread at the use-site as “contains one instance equal to,” especially when considering empty collections. `contains(only:)` was discounted due to trailing closures dropping the argument label, rendering it indistiguishable from `contains(where:)`. From cc8f7d77db4cb0e4171f7cfd2bc0e8e62b8769f4 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 16 May 2018 09:56:18 -0700 Subject: [PATCH 0694/4563] Update 0207-containsOnly.md --- proposals/0207-containsOnly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0207-containsOnly.md b/proposals/0207-containsOnly.md index 8411c6bb0d..97f41ae3fd 100644 --- a/proposals/0207-containsOnly.md +++ b/proposals/0207-containsOnly.md @@ -1,4 +1,4 @@ -# Add a `containsOnly` algorithm to `Sequence` +# Add a `allSatisfy` algorithm to `Sequence` * Proposal: [SE-0207](0207-containsOnly.md) * Authors: [Ben Cohen](https://github.com/airspeedswift) From 380e0b63db3c0d7eadbc3d2b48fd7094fd708089 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 16 May 2018 14:07:50 -0400 Subject: [PATCH 0695/4563] Accept SE-0213: Literal init via coercion --- proposals/0213-literal-init-via-coercion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0213-literal-init-via-coercion.md b/proposals/0213-literal-init-via-coercion.md index 8a85632fb7..83519a0a87 100644 --- a/proposals/0213-literal-init-via-coercion.md +++ b/proposals/0213-literal-init-via-coercion.md @@ -3,7 +3,7 @@ * Proposal: [SE-0213](0213-literal-init-via-coercion.md) * Authors: [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (May 10...15)** +* Status: **Accepted** * Implementation: [apple/swift#15311](https://github.com/apple/swift/pull/15311) ## Introduction From e25f0473ee96ee46bbcdc1dfbb675048d04f0dad Mon Sep 17 00:00:00 2001 From: Bas Broek Date: Thu, 17 May 2018 18:09:24 +0200 Subject: [PATCH 0696/4563] Fix typos in SE-0207 (#851) --- proposals/0207-containsOnly.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0207-containsOnly.md b/proposals/0207-containsOnly.md index 97f41ae3fd..df5aa065c7 100644 --- a/proposals/0207-containsOnly.md +++ b/proposals/0207-containsOnly.md @@ -1,4 +1,4 @@ -# Add a `allSatisfy` algorithm to `Sequence` +# Add an `allSatisfy` algorithm to `Sequence` * Proposal: [SE-0207](0207-containsOnly.md) * Authors: [Ben Cohen](https://github.com/airspeedswift) @@ -11,7 +11,7 @@ It is common to want to confirm that every element of a sequence equals a value, or matches certain criteria. Many implementations of this can be found -in use on github. This proposal adds such a method to `Sequence`. +in use on GitHub. This proposal adds such a method to `Sequence`. ## Motivation @@ -38,7 +38,7 @@ Set(nums).count == 1 && Set(nums).first == 9 ## Proposed solution -Introduce an algorithm on `Sequence` which test every element and return +Introduce an algorithm on `Sequence` which tests every element and returns `true` if they all match a given predicate: ```swift @@ -75,4 +75,4 @@ This change is purely additive so has no API resilience consequences. Not adding it, since it can be trivially (if confusingly) composed. -Much name bikeshedding has ensued. Names considered included `containsOnly` and `all`. `all` has strong precedent in other languages, but was considered unclear at the call site (adding an argument label does not help here given trailing closures omit them). Naming it `all` suggests a renaming of `contains` to `any` would be appropriate – but this is already a fairly heavily-used term elsewhere in Swift, and is less explicit. `containsOnly` is is more explicit, and echoes the existing `contains`, but is too easily misread at the use-site as “contains one instance equal to,” especially when considering empty collections. `contains(only:)` was discounted due to trailing closures dropping the argument label, rendering it indistiguishable from `contains(where:)`. +Much name bikeshedding has ensued. Names considered included `containsOnly` and `all`. `all` has strong precedent in other languages, but was considered unclear at the call site (adding an argument label does not help here given trailing closures omit them). Naming it `all` suggests a renaming of `contains` to `any` would be appropriate – but this is already a fairly heavily-used term elsewhere in Swift, and is less explicit. `containsOnly` is more explicit, and echoes the existing `contains`, but is too easily misread at the use-site as “contains one instance equal to,” especially when considering empty collections. `contains(only:)` was discounted due to trailing closures dropping the argument label, rendering it indistiguishable from `contains(where:)`. From b86b13dff6d825ca6501e6dcadf47bde8acdb489 Mon Sep 17 00:00:00 2001 From: Erica Sadun Date: Fri, 18 May 2018 11:40:18 -0600 Subject: [PATCH 0697/4563] [Proposal] Rename DictionaryLiteral (#850) --- proposals/XXXX-DictionaryLiterals.md | 99 ++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 proposals/XXXX-DictionaryLiterals.md diff --git a/proposals/XXXX-DictionaryLiterals.md b/proposals/XXXX-DictionaryLiterals.md new file mode 100644 index 0000000000..8fb5647fce --- /dev/null +++ b/proposals/XXXX-DictionaryLiterals.md @@ -0,0 +1,99 @@ +# Renaming the `DictionaryLiteral` type to `KeyValueList` + +* Proposal: SE-TBD +* Author: [Erica Sadun](https://github.com/erica), [Chéyo Jiménez](https://github.com/masters3d) +* Status: [Preliminary implementation](https://github.com/apple/swift/pull/16577) +* Review manager: tbd + +## Introduction + +This proposal renames the confusing and misnamed [`DictionaryLiteral`](https://github.com/apple/swift/blob/c25188bafd1c775d4ceecc4a795f614f00451bf9/stdlib/public/core/Mirror.swift#L646) type to `KeyValueList`. This type is neither a dictionary nor a literal. It is a list of key-value pairs. + +There is no strong motivation to deprecate. The type does not produce active harm. Instead, it adds measurable (if small) utility and will be part of the ABI. A sensible renaming mitigates the most problematic issue with the type. + +*This proposal was discussed in the Swift Forums on the [100% bikeshed topic: DictionaryLiteral](https://forums.swift.org/t/100-bikeshed-topic-dictionaryliteral/7385) thread.* + +## Motivation + +This proposal renames the standard library's `DictionaryLiteral` before the Swift Language declares ABI stability. The type is confusingly misnamed. A `DictionaryLiteral` is neither a dictionary nor a literal. + +* It offers no key-based value lookup. +* It does not represent a fixed value in source code. + +It seems reasonable to give the type to a better name that fits its role and purpose. + +#### Current Use: + +`DictionaryLiteral` establishes the `Mirror` API's children: + +``` +public init( + _ subject: Subject, + children: DictionaryLiteral, + displayStyle: Mirror.DisplayStyle? = default, + ancestorRepresentation: Mirror.AncestorRepresentation = default +) +``` + +* This implementation depends on `DictionaryLiteral`'s continued existence. +* The `@dynamiccallable` proposal will provide another use case for this type. + +Even when narrowly used, a type's reach is no longer a sufficient reason to deprecate it or remove it from the language. Absent *active harm*, source stability takes precedence. In this case, the `DictionaryLiteral` type causes no measurable harm beyond API sprawl and the issues with its name. The latter is easily fixed. + +#### Current Limits: + +The type's doc comments note an inefficient lookup implementation. This issue can be resolved in future Swift releases if needed: + +``` +/// Some operations that are efficient on a dictionary are slower when using +/// `DictionaryLiteral`. In particular, to find the value matching a key, you +/// must search through every element of the collection. The call to +/// `index(where:)` in the following example must traverse the whole +/// collection to find the element that matches the predicate +``` + +#### Utility: + +The type's support of duplicate keys could become be a feature for scanning key-value pairs: + +``` +/// You initialize a `DictionaryLiteral` instance using a Swift dictionary +/// literal. Besides maintaining the order of the original dictionary literal, +/// `DictionaryLiteral` also allows duplicates keys. +``` + +This key-value pair processing might support custom initializers. It allows duplicate keys and preserves declaration order, which are both reasonably useful features. + +## Detailed Design + +`DictionaryLiteral` is renamed to `KeyValueList`. A typealias preserves the old name for compatibility but can be deprecated as of Swift 5.0. + +This name was extensively bikeshedded on the [Swift Forum thread](https://forums.swift.org/t/100-bikeshed-topic-dictionaryliteral/7385) before proposal. The runner up name was `KeyValueArray`. + +## Source compatibility + +The old name can live on indefinitely via a typealias (which has no ABI consequences, so could be retired at a later date once everyone has had plenty of time to address the deprecation warnings). + +Removing it as not carrying its weight (and instead using `[(Key,Value)]`, which is basically what it’s a wrapper for) is probably off the table for reasons of source stability. + +## Effect on ABI stability + +Should be decided before ABI Stability is declared. + +## Effect on API resilience + +None. + +## Alternatives and Future Directions + +* This proposal does not change syntax. It processes an ordered immutable list of pairs declared using `[:]` syntax. This syntax offers better visual aesthetics than `[(,)]`. + +* Swift cannot yet replace `DictionaryLiteral` with conditional conformance using `Array: ExpressibleByDictionaryLiteral where Element = (Key,Value)` because [parameterized extensions](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#parameterized-extensions) are not yet available. Further, creating an array from a dictionary literal may be unnecessarily confusing. + +* This proposal does not deprecate and remove the type, as `Mirror` relies on its existence. This proposal does not recommend the removal of the `Mirror` type as third party custom debugging features rely on this feature. + +* A forum discussion considered a one-time split of the standard library, creating a "SwiftDeprecated" module that could eventually be phased out. That idea lies outside the scope of this proposal and involves a tradeoff between sunsetting APIs in the future for a slight reduction in size of today's standard library. Most applications will not use these APIs, whether such an approach is taken or not. + +## Acknowledgments + +Thanks, Ben Cohen, for pointing out this problem and starting the forum thread to arrive at a better name. Thanks Chris Lattner and Ted Kremenek for design direction. \ No newline at end of file From 3b72086b6a4ab35dcb63126ce311fad21b6d56e2 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 18 May 2018 10:44:17 -0700 Subject: [PATCH 0698/4563] Merge this proposal and kick it off. --- proposals/XXXX-DictionaryLiterals.md | 99 ---------------------------- 1 file changed, 99 deletions(-) delete mode 100644 proposals/XXXX-DictionaryLiterals.md diff --git a/proposals/XXXX-DictionaryLiterals.md b/proposals/XXXX-DictionaryLiterals.md deleted file mode 100644 index 8fb5647fce..0000000000 --- a/proposals/XXXX-DictionaryLiterals.md +++ /dev/null @@ -1,99 +0,0 @@ -# Renaming the `DictionaryLiteral` type to `KeyValueList` - -* Proposal: SE-TBD -* Author: [Erica Sadun](https://github.com/erica), [Chéyo Jiménez](https://github.com/masters3d) -* Status: [Preliminary implementation](https://github.com/apple/swift/pull/16577) -* Review manager: tbd - -## Introduction - -This proposal renames the confusing and misnamed [`DictionaryLiteral`](https://github.com/apple/swift/blob/c25188bafd1c775d4ceecc4a795f614f00451bf9/stdlib/public/core/Mirror.swift#L646) type to `KeyValueList`. This type is neither a dictionary nor a literal. It is a list of key-value pairs. - -There is no strong motivation to deprecate. The type does not produce active harm. Instead, it adds measurable (if small) utility and will be part of the ABI. A sensible renaming mitigates the most problematic issue with the type. - -*This proposal was discussed in the Swift Forums on the [100% bikeshed topic: DictionaryLiteral](https://forums.swift.org/t/100-bikeshed-topic-dictionaryliteral/7385) thread.* - -## Motivation - -This proposal renames the standard library's `DictionaryLiteral` before the Swift Language declares ABI stability. The type is confusingly misnamed. A `DictionaryLiteral` is neither a dictionary nor a literal. - -* It offers no key-based value lookup. -* It does not represent a fixed value in source code. - -It seems reasonable to give the type to a better name that fits its role and purpose. - -#### Current Use: - -`DictionaryLiteral` establishes the `Mirror` API's children: - -``` -public init( - _ subject: Subject, - children: DictionaryLiteral, - displayStyle: Mirror.DisplayStyle? = default, - ancestorRepresentation: Mirror.AncestorRepresentation = default -) -``` - -* This implementation depends on `DictionaryLiteral`'s continued existence. -* The `@dynamiccallable` proposal will provide another use case for this type. - -Even when narrowly used, a type's reach is no longer a sufficient reason to deprecate it or remove it from the language. Absent *active harm*, source stability takes precedence. In this case, the `DictionaryLiteral` type causes no measurable harm beyond API sprawl and the issues with its name. The latter is easily fixed. - -#### Current Limits: - -The type's doc comments note an inefficient lookup implementation. This issue can be resolved in future Swift releases if needed: - -``` -/// Some operations that are efficient on a dictionary are slower when using -/// `DictionaryLiteral`. In particular, to find the value matching a key, you -/// must search through every element of the collection. The call to -/// `index(where:)` in the following example must traverse the whole -/// collection to find the element that matches the predicate -``` - -#### Utility: - -The type's support of duplicate keys could become be a feature for scanning key-value pairs: - -``` -/// You initialize a `DictionaryLiteral` instance using a Swift dictionary -/// literal. Besides maintaining the order of the original dictionary literal, -/// `DictionaryLiteral` also allows duplicates keys. -``` - -This key-value pair processing might support custom initializers. It allows duplicate keys and preserves declaration order, which are both reasonably useful features. - -## Detailed Design - -`DictionaryLiteral` is renamed to `KeyValueList`. A typealias preserves the old name for compatibility but can be deprecated as of Swift 5.0. - -This name was extensively bikeshedded on the [Swift Forum thread](https://forums.swift.org/t/100-bikeshed-topic-dictionaryliteral/7385) before proposal. The runner up name was `KeyValueArray`. - -## Source compatibility - -The old name can live on indefinitely via a typealias (which has no ABI consequences, so could be retired at a later date once everyone has had plenty of time to address the deprecation warnings). - -Removing it as not carrying its weight (and instead using `[(Key,Value)]`, which is basically what it’s a wrapper for) is probably off the table for reasons of source stability. - -## Effect on ABI stability - -Should be decided before ABI Stability is declared. - -## Effect on API resilience - -None. - -## Alternatives and Future Directions - -* This proposal does not change syntax. It processes an ordered immutable list of pairs declared using `[:]` syntax. This syntax offers better visual aesthetics than `[(,)]`. - -* Swift cannot yet replace `DictionaryLiteral` with conditional conformance using `Array: ExpressibleByDictionaryLiteral where Element = (Key,Value)` because [parameterized extensions](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#parameterized-extensions) are not yet available. Further, creating an array from a dictionary literal may be unnecessarily confusing. - -* This proposal does not deprecate and remove the type, as `Mirror` relies on its existence. This proposal does not recommend the removal of the `Mirror` type as third party custom debugging features rely on this feature. - -* A forum discussion considered a one-time split of the standard library, creating a "SwiftDeprecated" module that could eventually be phased out. That idea lies outside the scope of this proposal and involves a tradeoff between sunsetting APIs in the future for a slight reduction in size of today's standard library. Most applications will not use these APIs, whether such an approach is taken or not. - -## Acknowledgments - -Thanks, Ben Cohen, for pointing out this problem and starting the forum thread to arrive at a better name. Thanks Chris Lattner and Ted Kremenek for design direction. \ No newline at end of file From 0917f82924d1ab008228b6400f685d7120319718 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 18 May 2018 10:45:37 -0700 Subject: [PATCH 0699/4563] Should have "git mv"'d instead of "mv"d the file, sigh. --- proposals/0214-DictionaryLiteral.md | 100 ++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 proposals/0214-DictionaryLiteral.md diff --git a/proposals/0214-DictionaryLiteral.md b/proposals/0214-DictionaryLiteral.md new file mode 100644 index 0000000000..e754f6f9f5 --- /dev/null +++ b/proposals/0214-DictionaryLiteral.md @@ -0,0 +1,100 @@ +# Renaming the `DictionaryLiteral` type to `KeyValueList` + +* Proposal: [SE-0214](0214-DictionaryLiteral.md) +* Authors: [Erica Sadun](https://github.com/erica), [Chéyo Jiménez](https://github.com/masters3d) +* Status: Active review (May 18...May 23, 2018) +* Review manager: [Chris Lattner](https://github.com/lattner) +* Implementation: [PR16577](https://github.com/apple/swift/pull/16577) + +## Introduction + +This proposal renames the confusing and misnamed [`DictionaryLiteral`](https://github.com/apple/swift/blob/c25188bafd1c775d4ceecc4a795f614f00451bf9/stdlib/public/core/Mirror.swift#L646) type to `KeyValueList`. This type is neither a dictionary nor a literal. It is a list of key-value pairs. + +There is no strong motivation to deprecate. The type does not produce active harm. Instead, it adds measurable (if small) utility and will be part of the ABI. A sensible renaming mitigates the most problematic issue with the type. + +*This proposal was discussed in the Swift Forums on the [100% bikeshed topic: DictionaryLiteral](https://forums.swift.org/t/100-bikeshed-topic-dictionaryliteral/7385) thread.* + +## Motivation + +This proposal renames the standard library's `DictionaryLiteral` before the Swift Language declares ABI stability. The type is confusingly misnamed. A `DictionaryLiteral` is neither a dictionary nor a literal. + +* It offers no key-based value lookup. +* It does not represent a fixed value in source code. + +It seems reasonable to give the type to a better name that fits its role and purpose. + +#### Current Use: + +`DictionaryLiteral` establishes the `Mirror` API's children: + +``` +public init( + _ subject: Subject, + children: DictionaryLiteral, + displayStyle: Mirror.DisplayStyle? = default, + ancestorRepresentation: Mirror.AncestorRepresentation = default +) +``` + +* This implementation depends on `DictionaryLiteral`'s continued existence. +* The `@dynamiccallable` proposal will provide another use case for this type. + +Even when narrowly used, a type's reach is no longer a sufficient reason to deprecate it or remove it from the language. Absent *active harm*, source stability takes precedence. In this case, the `DictionaryLiteral` type causes no measurable harm beyond API sprawl and the issues with its name. The latter is easily fixed. + +#### Current Limits: + +The type's doc comments note an inefficient lookup implementation. This issue can be resolved in future Swift releases if needed: + +``` +/// Some operations that are efficient on a dictionary are slower when using +/// `DictionaryLiteral`. In particular, to find the value matching a key, you +/// must search through every element of the collection. The call to +/// `index(where:)` in the following example must traverse the whole +/// collection to find the element that matches the predicate +``` + +#### Utility: + +The type's support of duplicate keys could become be a feature for scanning key-value pairs: + +``` +/// You initialize a `DictionaryLiteral` instance using a Swift dictionary +/// literal. Besides maintaining the order of the original dictionary literal, +/// `DictionaryLiteral` also allows duplicates keys. +``` + +This key-value pair processing might support custom initializers. It allows duplicate keys and preserves declaration order, which are both reasonably useful features. + +## Detailed Design + +`DictionaryLiteral` is renamed to `KeyValueList`. A typealias preserves the old name for compatibility but can be deprecated as of Swift 5.0. + +This name was extensively bikeshedded on the [Swift Forum thread](https://forums.swift.org/t/100-bikeshed-topic-dictionaryliteral/7385) before proposal. The runner up name was `KeyValueArray`. + +## Source compatibility + +The old name can live on indefinitely via a typealias (which has no ABI consequences, so could be retired at a later date once everyone has had plenty of time to address the deprecation warnings). + +Removing it as not carrying its weight (and instead using `[(Key,Value)]`, which is basically what it’s a wrapper for) is probably off the table for reasons of source stability. + +## Effect on ABI stability + +Should be decided before ABI Stability is declared. + +## Effect on API resilience + +None. + +## Alternatives and Future Directions + +* This proposal does not change syntax. It processes an ordered immutable list of pairs declared using `[:]` syntax. This syntax offers better visual aesthetics than `[(,)]`. + +* Swift cannot yet replace `DictionaryLiteral` with conditional conformance using `Array: ExpressibleByDictionaryLiteral where Element = (Key,Value)` because [parameterized extensions](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#parameterized-extensions) are not yet available. Further, creating an array from a dictionary literal may be unnecessarily confusing. + +* This proposal does not deprecate and remove the type, as `Mirror` relies on its existence. This proposal does not recommend the removal of the `Mirror` type as third party custom debugging features rely on this feature. + +* A forum discussion considered a one-time split of the standard library, creating a "SwiftDeprecated" module that could eventually be phased out. That idea lies outside the scope of this proposal and involves a tradeoff between sunsetting APIs in the future for a slight reduction in size of today's standard library. Most applications will not use these APIs, whether such an approach is taken or not. + +## Acknowledgments + +Thanks, Ben Cohen, for pointing out this problem and starting the forum thread to arrive at a better name. Thanks Chris Lattner and Ted Kremenek for design direction. From d04acc1f09472e8fa5d1ee30105c387bb78eda0c Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 18 May 2018 20:20:20 +0100 Subject: [PATCH 0700/4563] [SE-0214] Fix the "Status not found" parse error --- proposals/0214-DictionaryLiteral.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0214-DictionaryLiteral.md b/proposals/0214-DictionaryLiteral.md index e754f6f9f5..a20cf1ecea 100644 --- a/proposals/0214-DictionaryLiteral.md +++ b/proposals/0214-DictionaryLiteral.md @@ -2,9 +2,9 @@ * Proposal: [SE-0214](0214-DictionaryLiteral.md) * Authors: [Erica Sadun](https://github.com/erica), [Chéyo Jiménez](https://github.com/masters3d) -* Status: Active review (May 18...May 23, 2018) -* Review manager: [Chris Lattner](https://github.com/lattner) -* Implementation: [PR16577](https://github.com/apple/swift/pull/16577) +* Review Manager: [Chris Lattner](https://github.com/lattner) +* Status: **Active review (May 18...23)** +* Implementation: [apple/swift#16577](https://github.com/apple/swift/pull/16577) ## Introduction From 12315c44dd6b36fec924f4f6c30f48d8784ae4cc Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Fri, 18 May 2018 19:27:17 -0700 Subject: [PATCH 0701/4563] [SE-0214] Fix typo (#852) --- proposals/0214-DictionaryLiteral.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0214-DictionaryLiteral.md b/proposals/0214-DictionaryLiteral.md index a20cf1ecea..3ce42d2c4c 100644 --- a/proposals/0214-DictionaryLiteral.md +++ b/proposals/0214-DictionaryLiteral.md @@ -37,7 +37,7 @@ public init( ``` * This implementation depends on `DictionaryLiteral`'s continued existence. -* The `@dynamiccallable` proposal will provide another use case for this type. +* The `@dynamicCallable` proposal will provide another use case for this type. Even when narrowly used, a type's reach is no longer a sufficient reason to deprecate it or remove it from the language. Absent *active harm*, source stability takes precedence. In this case, the `DictionaryLiteral` type causes no measurable harm beyond API sprawl and the issues with its name. The latter is easily fixed. From 577b515e619aef98ae910031e5eab2f9f7ad64a9 Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Wed, 23 May 2018 19:43:52 -0700 Subject: [PATCH 0702/4563] Update 0201-package-manager-local-dependencies.md --- proposals/0201-package-manager-local-dependencies.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0201-package-manager-local-dependencies.md b/proposals/0201-package-manager-local-dependencies.md index 7470cbb6bd..de681db9e5 100644 --- a/proposals/0201-package-manager-local-dependencies.md +++ b/proposals/0201-package-manager-local-dependencies.md @@ -3,7 +3,8 @@ * Proposal: [SE-0201](0201-package-manager-local-dependencies.md) * Author: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **Accepted** +* Status: **Implemented (Swift 4.2)** +* Implementation: [apple/swift-package-manager#1583](https://github.com/apple/swift-package-manager/pull/1583) * Bug: [SR-7433](https://bugs.swift.org/browse/SR-7433) ## Introduction From 0d3718632214de7b36de38350fa5cc81f7cc962e Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Tue, 29 May 2018 17:03:17 -0700 Subject: [PATCH 0703/4563] Update 0208-package-manager-system-library-targets.md --- proposals/0208-package-manager-system-library-targets.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0208-package-manager-system-library-targets.md b/proposals/0208-package-manager-system-library-targets.md index 8ddd7b11b5..7c7d322625 100644 --- a/proposals/0208-package-manager-system-library-targets.md +++ b/proposals/0208-package-manager-system-library-targets.md @@ -3,7 +3,8 @@ * Proposal: [SE-0208](0208-package-manager-system-library-targets.md) * Author: [Ankit Aggarwal](https://github.com/aciidb0mb3r), [Daniel Dunbar](https://github.com/ddunbar) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **Accepted** +* Status: **Implemented (Swift 4.2)** +* Implementation: [apple/swift-package-manager#1586](https://github.com/apple/swift-package-manager/pull/1586) * Bug: [SR-7434](https://bugs.swift.org/browse/SR-7434) ## Introduction From 5579218301de1b883c0db19800b7f342a222f455 Mon Sep 17 00:00:00 2001 From: BJ Homer Date: Wed, 30 May 2018 23:40:08 -0600 Subject: [PATCH 0704/4563] Add Flatten Optional Try? proposal --- proposals/xxxx-flatten-optional-try.md | 249 +++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 proposals/xxxx-flatten-optional-try.md diff --git a/proposals/xxxx-flatten-optional-try.md b/proposals/xxxx-flatten-optional-try.md new file mode 100644 index 0000000000..75929214d6 --- /dev/null +++ b/proposals/xxxx-flatten-optional-try.md @@ -0,0 +1,249 @@ +# Flatten nested optionals resulting from 'try?' + +* Proposal: [SE-NNNN](NNNN-flatten-optional-try.md) +* Authors: [BJ Homer](https://github.com/bjhomer) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#16942](https://github.com/apple/swift/pull/16942) + +## Introduction + +Swift's `try?` statement currently makes it easy to introduce a +nested optional. Nested optionals are difficult for users to reason +about, and Swift tries to avoid producing them in other common cases. + +This document proposes giving `try?` the same optional-flattening +behavior found in other common Swift features, to avoid the common +occurrence of a nested optional. + +Swift-evolution thread: [Make try? + optional chain flattening work together](https://forums.swift.org/t/make-try-optional-chain-flattening-work-together/7415) + +## Motivation + +It's currently quite easy to end up with a nested `Optional` type when +using `try?`. Although it is valid to construct a nested optional, it +is usually not what the developer intended. + +Swift has various mechanisms to avoid accidentally creating nested optionals. For example: + +```swift +// Note how 'as?' produces the same type regardless of whether the value +// being cast is optional or not. +let x = nonOptionalValue() as? MyType // x is of type 'MyType?' +let y = optionalValue() as? MyType // y is of type 'MyType?' + +// Note how optional chaining produces the same type whether or not the +// call produces an optional value. +let a = optThing?.pizza() // a is of type 'Pizza?' +let b = optThing?.optionalPizza() // b is of type 'Pizza?' +``` + +However, `try?` behaves differently: + +```swift +let q = try? harbor.boat() // q is of type 'Boat?' +let r = try? harbor.optionalBoat() // r is of type 'Boat??' +``` + +The above examples are contrived, but it's actually quite common to end +up with a nested optional in production code. For example: + +```swift +// The result of 'foo.makeBar()' is 'Bar?' because of the optional +// chaining on 'foo'. The 'try?' adds an additional layer of +// optionality. So the type of 'x' is 'Bar??' +let x = try? foo?.makeBar() + +// JSONSerialization.jsonObject(with:) returns an 'Any'. We use 'as?' to +// verify that the result is of the expected type, but the result is that 'dict' +// is now of type '[String: Any]??' because 'try?' added an additional layer. +let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] +``` + +A survey of existing code suggests that it is common for users to work around +this behavior using one of the following patterns: + +```swift +// Pattern 1: Double if-let or guard-let +if let optionalX = try? self.optionalThing(), + let x = optionalX { + // Use 'x' here +} + +// Pattern 2: Introducing parentheses to let 'as?' flatten for us +if let x = (try? somethingAsAny()) as? JournalEntry { + // use 'x' here +} + +// Pattern 3: Pattern matching +if case let x?? = try? optionalThing() { + // use 'x' here +} +``` + +The need for these workarounds makes the language more difficult to +learn and use, and they don't really give us any benefit in return. + +Code using `try?` generally does not care to distinguish between the error case +and the nil-result case, which is why all these patterns focus on extracting the +value and ignore the error. If code does care to specifically detect errors, +it should probably be using `do/try/catch` instead. + +## Proposed solution + +In Swift 5, `try?` will mirror the behavior of `as?` and `foo?.bar`: it will +preserve the "optional-ness" of the sub-expression if possible. If the +sub-expression produces a non-optional value, the one level of Optional will +be added. + +This results in the following changes to the type of a `try?` expression: + +```swift +// Swift 4: 'String??' +// Swift 5: 'String?' +let myString = try? String(data: someData, encoding: .utf8) + +// Swift 4: '[String: Any]??' +// Swift 5: '[String: Any]?' +let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] +``` + +There are no changes to the type when the sub-expression produces a non-optional +type. + +```swift +// Swift 4: 'String?' +// Swift 5: 'String?' +let fileContents = try? String(contentsOf: someURL) +``` + +If the sub-expression produces a nested optional, the result is equally +nested: + +```swift +func doubleOptionalInt() throws -> Int?? { + return 3 +} + +// Swift 4: 'Int???' +// Swift 5: 'Int??' +let x = try? doubleOptionalInt() +``` + +## Detailed design + +The type of a `try?` expression in Swift 4 is defined as `Optional`, +where `T` is the type of the expression following the `try?` keyword. + +In Swift 5, the type of a `try?` expression will be some type `U`, where +`U` is an `Optional<_>` type and where the sub-expression type `T` is +coercible to `U`. The type constraint system automatically chooses the +smallest level of optional-nesting needed to satisfy this constraint, +which results in the behavior described in this proposal. + +#### Generics + +Some questions have been raised regarding the interoperability with +generic code, as in the following example: + +```swift +func test(fn: () throws -> T) -> T? { + + // Will this line change behavior if T is optional? + if let result = try? fn() { + print("We got a result!") + return result + } + else { + print("There was an error") + return nil + } +} + +// T is inferred as 'Int' here +let value = test({ return 15 }) + +// T is inferred as 'Int?' here +let value2 = test({ return 15 as Int? }) +``` + +The answer is that it does not matter if `T` is optional at runtime. +At compile time, `result` has a clearly-defined type: `T`. This is +true in both Swift 4 and Swift 5 modes; because `T` is not known to be +an `Optional` type, a single layer of `Optional` is added via the `try?` +expression and then unwrapped via `if let`. + +Generic code that uses `try?` can continue to use it as always without +concern for whether the generic type might be optional at runtime. + + +## Source compatibility + +This *is* a source-breaking change for most `try?` expressions that operate +on an `Optional` sub-expression. We can provide backward-compatible behavior +when the compiler is running in Swift 4 mode, and a migration should be +possible for most common cases. + +It appears that most uses of `try?` in the Swift Compatibility Library +are with non-optional expressions. The code most liikely to be impacted +is of the form `try? foo() as? Bar`, but even that is relatively rare, +and can be migrated. + +Source code that is intended for use with both Swift 4 and Swift 5 +compilers can write code as follows, which behaves identically in +both Swift 4 and Swift 5 under this proposal: + +``` +// Original Swift 4 code: +let x = try? produceAnOptional() + + +// If you only care about detecting whether a value was produced, +// you can use `Optional.flatMap` to flatten the optional. +let x = try? produceAnOptional().flatMap({ $0 }) + + +// If you want to detect the error case separately from the +// nil-value case, use do/try/catch: +do { + let x = try produceAnOptional() + // Handle the optional result +} +catch { + // Handle the error case +} +``` + +## Effect on ABI stability + +No impact on ABI. + +## Effect on API resilience + +A `try?` expression is never exposed at function boundaries, so API +resilience should be unaffected. + +## Alternatives considered + +### Alter the binding precedence of try? + +For expressions like `let x = try? getObject() as? Foo`, the nested optional +can be eliminated by parsing the expression as `(try? getObject) as? Foo`. +Adding explicit parentheses like this is already a common workaround for +the double-optional problem. + +However, this change would not resolve cases of `try?` with optional chaining +(e.g. `try? foo?.bar?.baz()`), nor cases where an optional result is returned +directly from a funtion (e.g. `try? cachedValue(for: key)`). + +Altering the binding precedence of `try?` would also be *far* more +source-breaking than this proposal. + +### Do nothing + +It is possible to write correct code under the current model. We are not +proposing to eliminate nested Optionals from the language entirely, so +we could just expect users to figure them out. + +This is a workable solution, but it is odd to have such a complex structure +produced as part of a syntactic sugar designed to simplify a common case. \ No newline at end of file From 78a3e6a066994b76d382f8ee83c15b96aa5fcdb7 Mon Sep 17 00:00:00 2001 From: BJ Homer Date: Fri, 1 Jun 2018 14:08:15 -0600 Subject: [PATCH 0705/4563] Add source compatibility suite analysis results. --- proposals/xxxx-flatten-optional-try.md | 49 ++++++++++++++++++++------ 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/proposals/xxxx-flatten-optional-try.md b/proposals/xxxx-flatten-optional-try.md index 75929214d6..c0246d9525 100644 --- a/proposals/xxxx-flatten-optional-try.md +++ b/proposals/xxxx-flatten-optional-try.md @@ -179,28 +179,25 @@ concern for whether the generic type might be optional at runtime. ## Source compatibility -This *is* a source-breaking change for most `try?` expressions that operate -on an `Optional` sub-expression. We can provide backward-compatible behavior -when the compiler is running in Swift 4 mode, and a migration should be -possible for most common cases. - -It appears that most uses of `try?` in the Swift Compatibility Library -are with non-optional expressions. The code most liikely to be impacted -is of the form `try? foo() as? Bar`, but even that is relatively rare, -and can be migrated. +This is a source-breaking change for `try?` expressions that operate on an +`Optional` sub-expression if they do not explicitly flatten the optional +themselves. It appears that those cases are rare, though; see the analysis +below for details. We can provide backward-compatible behavior when the compiler +is running in Swift 4 mode, and a migration should be possible for most common +cases. Source code that is intended for use with both Swift 4 and Swift 5 compilers can write code as follows, which behaves identically in both Swift 4 and Swift 5 under this proposal: -``` +```swift // Original Swift 4 code: let x = try? produceAnOptional() // If you only care about detecting whether a value was produced, // you can use `Optional.flatMap` to flatten the optional. -let x = try? produceAnOptional().flatMap({ $0 }) +let x = (try? produceAnOptional()).flatMap { $0 } // If you want to detect the error case separately from the @@ -214,6 +211,36 @@ catch { } ``` +#### Swift Source Compatibility Suite analysis + +The Swift Source Compatibility Suite suggests that this is unlikely to +be a breaking change for most users. I manually inspected the use +cases of `try?` in the compatibility suite. Here are the results: + +* There are **613** total instances of `try?` in the compatibility suite. The vast majority of those appear to use non-optional sub-expressions, and would be unaffected by this proposal. + +* There are **4** instances of `try? ... as?`. All four of them wrap the `try?` in parentheses to get the flattening behavior, and would be source-compatible either way. They all look something like this: + + ```swift + (try? JSONSerialization.jsonObject(with: $0)) as? NSDictionary + ``` + +* There are **12** cases of `try? foo?.bar()` across 3 projects. +**10** of those assign it to `_ = try? foo?.bar()` , so the resulting type does not matter. +**2** of those cases have a `Void` sub-expression type, and do not assign the result to any variable. + +* There are **6** instances of `try? somethingReturningOptional()` . They all flatten it manually using `flatMap { $0 }`, and are thus source-compatible with this change. + + ```swift + (try? get(key)).flatMap { $0 } + ``` + +* As far as I can tell, there are **zero** cases in the entire suite where a double-optional is actually used to distinguish between the error case and the nil-as-a-value case. + +* As far as I can tell, there are **zero** cases of source incompatibility found in the compatibility suite. + + + ## Effect on ABI stability No impact on ABI. From a4f9cadf39be46e55ca9d4826bedff6b2a3a814a Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Mon, 4 Jun 2018 00:01:30 -0700 Subject: [PATCH 0706/4563] [Proposal] Dynamic Callable (`@dynamicCallable` attribute) --- proposals/XXXX-dynamic-callable.md | 392 +++++++++++++++++++++++++++++ 1 file changed, 392 insertions(+) create mode 100644 proposals/XXXX-dynamic-callable.md diff --git a/proposals/XXXX-dynamic-callable.md b/proposals/XXXX-dynamic-callable.md new file mode 100644 index 0000000000..bff4a1766b --- /dev/null +++ b/proposals/XXXX-dynamic-callable.md @@ -0,0 +1,392 @@ +# Introduce user-defined dynamically "callable" types + +* Proposal: SE-TBD +* Authors: [Chris Lattner](https://github.com/lattner), [Dan Zheng](https://github.com/dan-zheng) +* Review Manager: TBD +* Status: **Awaiting implementation** + +Note: this proposal is largely adapted from Chris's original pitch [here](https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d). + +## Introduction + +This proposal is a follow-on to [SE-0195 - Introduce User-defined "Dynamic Member +Lookup" Types](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md) +which shipped in Swift 4.2. It introduces a new `@dynamicCallable` attribute, which marks +a type as being "callable" with normal syntax. It is simple syntactic sugar +which allows the user to write: + +```swift +a = someValue(keyword1: 42, "foo", keyword2: 19) +```` + +and have it be interpreted by the compiler as: + +```swift +a = someValue.dynamicallyCall(withKeywordArguments: [ + "keyword1": 42, "": "foo", "keyword2": 19 +]) +``` + +Many other languages have analogous features (e.g. Python "callables", C++ `operator()`, and +[functors in many other languages](https://en.wikipedia.org/wiki/Function_object)), but the +primary motivation of this proposal is to allow elegant and natural interoperation with +dynamic languages in Swift. + + Swift-evolution threads: + - [Pitch: Introduce user-defined dynamically "callable" + types](https://forums.swift.org/t/pitch-introduce-user-defined-dynamically-callable-types/7038). + - [Pitch #2: Introduce user-defined dynamically “callable” types](https://forums.swift.org/t/pitch-2-introduce-user-defined-dynamically-callable-types/7112). + - Current pitch thread: [Pitch #3: Introduce user-defined dynamically “callable” types](https://forums.swift.org/t/pitch-3-introduce-user-defined-dynamically-callable-types/12232) + +## Motivation and context + +Swift is exceptional at interworking with existing C and Objective-C +APIs and we would like to extend this interoperability to dynamic languages like +Python, JavaScript, Perl, and Ruby. We explored this overall goal in a long design process +wherein the Swift evolution community evaluated multiple different implementation +approaches. The conclusion was that the best approach was to put most of the complexity into +dynamic language specific bindings written as pure-Swift libraries, but add small hooks in +Swift to allow these bindings to provide a natural experience to their clients. +[SE-0195](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md) +was the first step in this process, which introduced a binding to naturally express member +lookup rules in dynamic languages. + +What does interoperability with Python mean? Let's explain this by looking at an example. +Here is some simple Python code: + +```Python +class Dog: + def __init__(self, name): + self.name = name + self.tricks = [] # creates a new empty list for each dog + + def add_trick(self, trick): + self.tricks.append(trick) +``` + +With the SE-0195 features introduced in Swift 4.2, it is possible to implement a [Python +interoperability layer](https://github.com/apple/swift/tree/tensorflow/stdlib/public/Python) +written in Swift. It interoperates with the Python runtime, and project all Python values into a +single `PythonObject` type. It allows us to call into the `Dog` class like this: + +```swift +// import DogModule.Dog as Dog +let Dog = Python.import.call(with: "DogModule.Dog") + +// dog = Dog("Brianna") +let dog = Dog.call(with: "Brianna") + +// dog.add_trick("Roll over") +dog.add_trick.call(with: "Roll over") + +// dog2 = Dog("Kaylee").add_trick("snore") +let dog2 = Dog.call(with: "Kaylee").add_trick.call(with: "snore") +``` + +This also works with arbitrary other APIs as well. Here is an example +working with the Python `pickle` API and the builtin Python function `open`. Note that we +choose to put builtin Python functions like `import` and `open` into a `Python` namespace +to avoid polluting the global namespace, but other designs are possible: + +```swift +// import pickle +let pickle = Python.import.call(with: "pickle") + +// file = open(filename) +let file = Python.open.call(with: filename) + +// blob = file.read() +let blob = file.read.call() + +// result = pickle.loads(blob) +let result = pickle.loads.call(with: blob) +``` + +This capability works well, but the syntactic burden of having to use +`foo.call(with: bar, baz)` instead of `foo(bar, baz)` is significant. +Beyond the syntactic weight, it directly harms code clarity by making code hard to read +and understand, cutting against a core value of Swift. + +The `@dynamicCallable` attribute in proposal directly solves this problem. +These examples become their natural and clear form, and effectively match the +original Python code in expressiveness: + +```swift +// import DogModule.Dog as Dog +let Dog = Python.import(“DogModule.Dog") + +// dog = Dog("Brianna") +let dog = Dog("Brianna") + +// dog.add_trick("Roll over") +dog.add_trick("Roll over") + +// dog2 = Dog("Kaylee").add_trick("snore") +let dog2 = Dog("Kaylee").add_trick("snore") +``` + +Python builtins: + +```swift +// import pickle +let pickle = Python.import("pickle") + +// file = open(filename) +let file = Python.open(filename) + +// blob = file.read() +let blob = file.read() + +// result = pickle.loads(blob) +let result = pickle.loads(blob) +``` + +This is a proposal is purely syntactic sugar - it introduces no new semantic model to Swift at +all. We believe that interoperability with scripting languages is an important and rising need in +the Swift community, particularly as Swift makes inroads into the server development and +machine learning communities. This sort of capability is also highly precedented in other +languages, and is a generally useful language feature that could be used for other purposes +as well (e.g. for implementing dynamic proxy objects). + +## Proposed solution + +We propose introducing a new `@dynamicCallable` attribute to the Swift language which may be +applied to structs, classes, enums, and protocols. This follows the precedent of +[SE-0195](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md). + +Before this proposal, values of these types are not valid in a function call +position: the only callable values that Swift has are those with a function type (functions, methods, +closures, etc) and metatypes (which are initializer expressions like `String(42)`). Thus, +it is always an error to "call" an instance of a nominal type (like a struct, for instance). + +With this proposal, types that adopt the new attribute on their primary type declaration +become "callable" and are required to implement one or more methods to implement the call +behavior. + +To support these cases, a type with this attribute is required to implement at least one of the +following two methods. In the examples below, `T*` are arbitrary types, `S*` +must conform to `ExpressibleByStringLiteral` (e.g. `String` and `StaticString`). + +```swift +func dynamicallyCall(withArguments: [T1]) -> T2 +func dynamicallyCall(withKeywordArguments: [S : T3]) -> T4 +``` + +We write `Arguments` as an array type and `KeywordArguments` as an dictionary type, but these +are allowed to be any type that conforms to the `ExpressibleByArrayLiteral` and +`ExpressibleByDictionaryLiteral` protocols, respectively. The later is inclusive of +[`DictionaryLiteral`](https://developer.apple.com/documentation/swift/dictionaryliteral) which +can represent multiple instances of a 'key' in the collection. This is important to support +duplicated and positional arguments (because positional arguments have the empty string `""` +as their key). + +If a type implements the `withKeywordArguments:` method, it may be dynamically called with both +positional and keyword arguments (positional arguments have the empty string `""` as their key). +If a type only implements the `withArguments:` method but is called with keyword arguments, +a compile time error is emitted. + +Because this is a syntactic sugar proposal, additional behavior of the implementation +methods is directly expressed: for example, if these types are defined to be `throws` or +`@discardableResult` then the corresponding sugared call is as well. + +### Ambiguity resolution: most specific match + +Since there are two `@dynamicCallable` methods, there may be multiple ways to handle some +dynamic calls. What happens if a type specifies both the `withKeywordArguments:` and +`withArguments:` methods? + +We propose that the type checker resolve this ambiguity towards the tightest match based +on syntactic form of the expression. If a type implements both the `withKeywordArguments:` and +`withArguments:` methods, the compiler will use the `withArguments:` method for call sites that +have no keyword arguments and the `withKeywordArguments:` method for call sites that have at least one +keyword argument. + +This ambiguity resolution rule works out very naturally given the behavior of the Swift type +checker, because it only resolves call expressions when the type of the base expression is +known. At that point, it knows the capabilities of the type (whether the base is a function +type, metatype, or a type where one of these two methods exist) and it knows the syntactic +form of the call. + +This proposal does not require massive or invasive changes to the constraint solver. Please look +at the implementation for more details. + +## Example usage + +Here, we sketch some example bindings to show how this could be used in practice. Note +that there are lots of design decisions that are orthogonal to +this proposal (e.g. how to handle exceptions) that we aren't going into here. This is just to +show how this feature provides an underlying facility that authors of language bindings can +use to achieve their desired result. These examples also show +`@dynamicMemberLookup` to illustrate how they work together, but elides all other the +implementation details. + +JavaScript supports callable objects but does not have keyword arguments. + +Here is a sample JavaScript binding: + +```swift +@dynamicCallable @dynamicMemberLookup +struct JSValue { + // JavaScript doesn't have keyword arguments. + @discardableResult + func dynamicallyCall(withArguments: [JSValue]) -> JSValue { ... } + + // This is a `@dynamicMemberLookup` requirement. + subscript(dynamicMember member: JSValue) -> JSValue {...} + + // ... other stuff ... +} +``` + +On the other hand, a common JavaScript pattern is to take a dictionary of values as a stand-in +for argument labels (called like `example({first: 1, second: 2, third: 3})` in +JavaScript). A JavaScript bridge in Swift could choose to implement keyword argument support +to allow this to be called as `example(first: 1, second: 2, third: 3)` from Swift +code (kudos to Ben Rimmington for this observation). + +Python does support keyword arguments. While a Python binding could implement just the +`withKeywordArguments:` method, it may be better to implement both the non-keyword and keyword forms +to make the non-keyword case slightly more efficient (avoid allocating temporary storage) and to make +direct calls with positional arguments nicer (`x.dynamicallyCall(withArguments: 1, 2)` +instead of `x.dynamicallyCall(withKeywordArguments: ["": 1, "": 2])`). + +Here is a sample Python binding: + +```swift +@dynamicCallable @dynamicMemberLookup +struct PythonObject { + // Python supports arbitrary mixes of keyword arguments and non-keyword + // arguments. + @discardableResult + func dynamicallyCall( + withKeywordArguments: DictionaryLiteral + ) -> PythonObject { ... } + + // An implementation of a Python binding could choose to implement this + // method as well, avoiding allocation of a temporary array. + @discardableResult + func dynamicallyCall(withArguments: [PythonObject]) -> PythonObject { ... } + + // This is a `@dynamicMemberLookup` requirement. + subscript(dynamicMember member: String) -> PythonObject {...} + + // ... other stuff ... +} +``` + +## Limitations + +Following the precedent of SE-0195, this attribute must be placed on the primary definition +of a type, not on an extension. + +This proposal does not introduce the ability to provide dynamically callable +`static`/`class` members. We don't believe this is important given the goal of +supporting dynamic languages like Python, but it could be explored if a use case is discovered +in the future. Such future work should keep in mind that call syntax on metatypes is already +meaningful, and that ambiguity would have to be resolved somehow (e.g. through the most specific rule). + +This proposal supports direct calls of values and methods, but subsets out support for +currying methods in Smalltalk family languages. This is just an +implementation limitation given the current state of currying in the Swift compiler. Support +can be added in the future if there is a specific need. + +## Source compatibility + +This is a strictly additive proposal with no source breaking changes. + +## Effect on ABI stability + +This is a strictly additive proposal with no ABI breaking changes. + +## Effect on API resilience + +This has no impact on API resilience which is not already captured by other language +features. + +## Future directions + +### Dynamic member calling (for Smalltalk family languages) + +In addition to supporting languages like Python and JavaScript, we would also like to grow to +support Smalltalk derived languages like Ruby and Squeak. These languages resolve +*method* calls using both the base name as well as the keyword arguments at the same time. +For example, consider this Ruby code: + +```Ruby +time = Time.zone.parse(user_time) +``` + +The `Time.zone` reference is a member lookup, but `zone.parse(user_time)` is a method +call, and needs to be handled differently than a lookup of `zone.parse` followed by a direct +function call. + +This can be handled by adding a new `@dynamicMemberCallable` attribute, which acts similarly to +`@dynamicCallable` but enables dynamic member calls (instead of dynamic calls of `self`). + +`@dynamicMemberCallable` would have the following requirements: + +```swift +func dynamicallyCallMethod(named: S1, withArguments: [T5]) -> T6 +func dynamicallyCallMethod(named: S2, withKeywordArguments: [S3 : T7]) -> T8 +``` + +Here is a sample Ruby binding: + +```swift +@dynamicMemberCallable @dynamicMemberLookup +struct RubyObject { + @discardableResult + func dynamicallyCallMethod( + named: String, withKeywordArguments: DictionaryLiteral + ) -> RubyObject { ... } + + // This is a `@dynamicMemberLookup` requirement. + subscript(dynamicMember member: String) -> RubyObject {...} + + // ... other stuff ... +} +``` + +### Variable-sized list of arguments + +This proposal is mainly directed at dynamic language interoperability. In this +use-case, it makes sense to take a variable sized list of arguments where each argument has +the same type. However, there are other use cases where it could make sense to support +static argument lists, akin to `operator()` in C++. For example, consider something like this: + +```swift +struct BinaryFunction { + func call(_ argument1: T1, _ argument1: T2) -> U { ... } +} +``` + +It is not unreasonable to look ahead to a day where sugaring such things is supported, +particularly when and if Swift gets [variadic generics](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#variadic-generics). +This could allow typesafe n-ary smart function pointer types. + +We feel that the approach outlined in this proposal supports this direction. When and if a +motivating use case for the above feature comes up, we can simply add a new form to +represent it, and enhance the type checker to prefer that according to the "most specific +match" rule. If this is a likely direction, then it might be better to name the attribute +`@callable` instead of `@dynamicCallable` in anticipation of that future growth. + +## Alternatives considered + +Many alternatives were considered and discussed. Most of them are captured in the +["Alternatives Considered" section of SE-0195](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md#alternatives-considered). + +Here are a few points raised in the discussion: + + - It was suggested that we use subscripts to represent the call implementations instead of a + function call, aligning with `@dynamicMemberLookup`. We think that functions are a better + fit here: the reason `@dynamicMemberLookup` uses subscripts is to allow the members to + be l-values, but call results are not l-values. + + - It was requested that we design and implement the 'static callable' version of this proposal + in conjunction with the dynamic version proposed here. In the author's opinion, it is + important to consider static callable support as a likely future direction to make sure that + the two features sit well next to each other and have a consistent design (something we + believe this proposal has done) but it doesn't make sense to join the two proposals. So + far, there have been no strong motivating use case presented for the static callable version, + and Swift lacks certain generics features (e.g. variadics) that would be necessary to make + static callables general. We feel that static callable should stand alone on its own merits. From dd03adbeedd6cd1bbbd45492fe7c57a4ddd71a71 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Mon, 4 Jun 2018 00:19:52 -0700 Subject: [PATCH 0707/4563] Update with link to implementation. --- proposals/XXXX-dynamic-callable.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/XXXX-dynamic-callable.md b/proposals/XXXX-dynamic-callable.md index bff4a1766b..ca035ddf62 100644 --- a/proposals/XXXX-dynamic-callable.md +++ b/proposals/XXXX-dynamic-callable.md @@ -3,7 +3,8 @@ * Proposal: SE-TBD * Authors: [Chris Lattner](https://github.com/lattner), [Dan Zheng](https://github.com/dan-zheng) * Review Manager: TBD -* Status: **Awaiting implementation** +* Implementation: https://github.com/apple/swift/pull/16980 +* Status: **Awaiting review** Note: this proposal is largely adapted from Chris's original pitch [here](https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d). From 9451d2bb7bafb59fad95187d6d0e581b26e0ebc0 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Mon, 4 Jun 2018 05:59:24 -0700 Subject: [PATCH 0708/4563] Wrap doc to 80 columns, minor edits. --- proposals/XXXX-dynamic-callable.md | 314 ++++++++++++++++------------- 1 file changed, 171 insertions(+), 143 deletions(-) diff --git a/proposals/XXXX-dynamic-callable.md b/proposals/XXXX-dynamic-callable.md index ca035ddf62..2b9f49d3d2 100644 --- a/proposals/XXXX-dynamic-callable.md +++ b/proposals/XXXX-dynamic-callable.md @@ -6,7 +6,8 @@ * Implementation: https://github.com/apple/swift/pull/16980 * Status: **Awaiting review** -Note: this proposal is largely adapted from Chris's original pitch [here](https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d). +Note: this proposal is largely adapted from Chris's original pitch +[here](https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d). ## Introduction @@ -33,42 +34,47 @@ Many other languages have analogous features (e.g. Python "callables", C++ `oper primary motivation of this proposal is to allow elegant and natural interoperation with dynamic languages in Swift. - Swift-evolution threads: - - [Pitch: Introduce user-defined dynamically "callable" - types](https://forums.swift.org/t/pitch-introduce-user-defined-dynamically-callable-types/7038). - - [Pitch #2: Introduce user-defined dynamically “callable” types](https://forums.swift.org/t/pitch-2-introduce-user-defined-dynamically-callable-types/7112). - - Current pitch thread: [Pitch #3: Introduce user-defined dynamically “callable” types](https://forums.swift.org/t/pitch-3-introduce-user-defined-dynamically-callable-types/12232) +Swift-evolution threads: + - [Pitch: Introduce user-defined dynamically "callable" + types](https://forums.swift.org/t/pitch-introduce-user-defined-dynamically-callable-types/7038). + - [Pitch #2: Introduce user-defined dynamically “callable” + types](https://forums.swift.org/t/pitch-2-introduce-user-defined-dynamically-callable-types/7112). + - Current pitch thread: [Pitch #3: Introduce user-defined dynamically “callable” + types](https://forums.swift.org/t/pitch-3-introduce-user-defined-dynamically-callable-types/12232) ## Motivation and context -Swift is exceptional at interworking with existing C and Objective-C -APIs and we would like to extend this interoperability to dynamic languages like -Python, JavaScript, Perl, and Ruby. We explored this overall goal in a long design process -wherein the Swift evolution community evaluated multiple different implementation -approaches. The conclusion was that the best approach was to put most of the complexity into -dynamic language specific bindings written as pure-Swift libraries, but add small hooks in -Swift to allow these bindings to provide a natural experience to their clients. +Swift is exceptional at interworking with existing C and Objective-C APIs and +we would like to extend this interoperability to dynamic languages like Python, +JavaScript, Perl, and Ruby. We explored this overall goal in a long design +process wherein the Swift evolution community evaluated multiple different +implementation approaches. The conclusion was that the best approach was to put +most of the complexity into dynamic language specific bindings written as +pure-Swift libraries, but add small hooks in Swift to allow these bindings to +provide a natural experience to their clients. [SE-0195](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md) -was the first step in this process, which introduced a binding to naturally express member -lookup rules in dynamic languages. +was the first step in this process, which introduced a binding to naturally +express member lookup rules in dynamic languages. -What does interoperability with Python mean? Let's explain this by looking at an example. -Here is some simple Python code: +What does interoperability with Python mean? Let's explain this by looking at +an example. Here's some simple Python code: ```Python class Dog: def __init__(self, name): self.name = name - self.tricks = [] # creates a new empty list for each dog + self.tricks = [] # creates a new empty list for each `Dog` def add_trick(self, trick): self.tricks.append(trick) ``` -With the SE-0195 features introduced in Swift 4.2, it is possible to implement a [Python -interoperability layer](https://github.com/apple/swift/tree/tensorflow/stdlib/public/Python) -written in Swift. It interoperates with the Python runtime, and project all Python values into a -single `PythonObject` type. It allows us to call into the `Dog` class like this: +With the [SE-0195 `@dynamicMemberLookup` feature](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md) +introduced in Swift 4.2, it is possible to implement a [Python interoperability +layer](https://github.com/apple/swift/tree/tensorflow/stdlib/public/Python) +written in Swift. It interoperates with the Python runtime, and project all +Python values into a single `PythonObject` type. It allows us to call into the +`Dog` class like this: ```swift // import DogModule.Dog as Dog @@ -84,10 +90,11 @@ dog.add_trick.call(with: "Roll over") let dog2 = Dog.call(with: "Kaylee").add_trick.call(with: "snore") ``` -This also works with arbitrary other APIs as well. Here is an example -working with the Python `pickle` API and the builtin Python function `open`. Note that we -choose to put builtin Python functions like `import` and `open` into a `Python` namespace -to avoid polluting the global namespace, but other designs are possible: +This also works with arbitrary other APIs as well. Here is an example working +with the Python `pickle` API and the builtin Python function `open`. Note that +we choose to put builtin Python functions like `import` and `open` into a +`Python` namespace to avoid polluting the global namespace, but other designs +are possible: ```swift // import pickle @@ -104,12 +111,12 @@ let result = pickle.loads.call(with: blob) ``` This capability works well, but the syntactic burden of having to use -`foo.call(with: bar, baz)` instead of `foo(bar, baz)` is significant. -Beyond the syntactic weight, it directly harms code clarity by making code hard to read -and understand, cutting against a core value of Swift. +`foo.call(with: bar, baz)` instead of `foo(bar, baz)` is significant. Beyond +the syntactic weight, it directly harms code clarity by making code hard to +read and understand, cutting against a core value of Swift. -The `@dynamicCallable` attribute in proposal directly solves this problem. -These examples become their natural and clear form, and effectively match the +The `@dynamicCallable` attribute in this proposal directly solves this problem. +With it, these examples become more natural and clear, effectively matching the original Python code in expressiveness: ```swift @@ -142,84 +149,93 @@ let blob = file.read() let result = pickle.loads(blob) ``` -This is a proposal is purely syntactic sugar - it introduces no new semantic model to Swift at -all. We believe that interoperability with scripting languages is an important and rising need in -the Swift community, particularly as Swift makes inroads into the server development and -machine learning communities. This sort of capability is also highly precedented in other -languages, and is a generally useful language feature that could be used for other purposes -as well (e.g. for implementing dynamic proxy objects). +This is a proposal is purely syntactic sugar - it introduces no new semantic +model to Swift at all. We believe that interoperability with scripting +languages is an important and rising need in the Swift community, particularly +as Swift makes inroads into the server development and machine learning +communities. This sort of capability is also highly precedented in other +languages, and is a generally useful language feature that could be used for +other purposes as well (e.g. for implementing dynamic proxy objects). ## Proposed solution -We propose introducing a new `@dynamicCallable` attribute to the Swift language which may be -applied to structs, classes, enums, and protocols. This follows the precedent of +We propose introducing a new `@dynamicCallable` attribute to the Swift language +which may be applied to structs, classes, enums, and protocols. This follows +the precedent of [SE-0195](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md). Before this proposal, values of these types are not valid in a function call -position: the only callable values that Swift has are those with a function type (functions, methods, -closures, etc) and metatypes (which are initializer expressions like `String(42)`). Thus, -it is always an error to "call" an instance of a nominal type (like a struct, for instance). +position: the only callable values that Swift has are those with a function +type (functions, methods, closures, etc) and metatypes (which are initializer +expressions like `String(42)`). Thus, it is always an error to "call" an +instance of a nominal type (like a struct, for instance). -With this proposal, types that adopt the new attribute on their primary type declaration -become "callable" and are required to implement one or more methods to implement the call -behavior. +With this proposal, types that adopt the new attribute on their primary type +declaration become "callable" and are required to implement one or more methods +for handling the call behavior. -To support these cases, a type with this attribute is required to implement at least one of the -following two methods. In the examples below, `T*` are arbitrary types, `S*` -must conform to `ExpressibleByStringLiteral` (e.g. `String` and `StaticString`). +To support these cases, a type with this attribute is required to implement at +least one of the following two methods. In the examples below, `T*` are +arbitrary types, `S*` must conform to `ExpressibleByStringLiteral` (e.g. +`String` and `StaticString`). ```swift func dynamicallyCall(withArguments: [T1]) -> T2 func dynamicallyCall(withKeywordArguments: [S : T3]) -> T4 ``` -We write `Arguments` as an array type and `KeywordArguments` as an dictionary type, but these -are allowed to be any type that conforms to the `ExpressibleByArrayLiteral` and -`ExpressibleByDictionaryLiteral` protocols, respectively. The later is inclusive of -[`DictionaryLiteral`](https://developer.apple.com/documentation/swift/dictionaryliteral) which -can represent multiple instances of a 'key' in the collection. This is important to support -duplicated and positional arguments (because positional arguments have the empty string `""` -as their key). - -If a type implements the `withKeywordArguments:` method, it may be dynamically called with both -positional and keyword arguments (positional arguments have the empty string `""` as their key). -If a type only implements the `withArguments:` method but is called with keyword arguments, -a compile time error is emitted. - -Because this is a syntactic sugar proposal, additional behavior of the implementation -methods is directly expressed: for example, if these types are defined to be `throws` or -`@discardableResult` then the corresponding sugared call is as well. +We write `Arguments` as an array type and `KeywordArguments` as a dictionary +type, but these can actually be any type that conforms to the +`ExpressibleByArrayLiteral` and `ExpressibleByDictionaryLiteral` protocols, +respectively. The later is inclusive of +[`DictionaryLiteral`](https://developer.apple.com/documentation/swift/dictionaryliteral) +which can represent multiple instances of a 'key' in the collection. This is +important to support duplicated and positional arguments (because positional +arguments have the empty string `""` as their key). + +If a type implements the `withKeywordArguments:` method, it may be dynamically +called with both positional and keyword arguments (positional arguments have +the empty string `""` as their key). If a type only implements the +`withArguments:` method but is called with keyword arguments, a compile-time +error is emitted. + +Because this is a syntactic sugar proposal, additional behavior of the +implementation methods is directly expressed: for example, if these types are +defined to be `throws` or `@discardableResult` then the corresponding sugared +call is as well. ### Ambiguity resolution: most specific match -Since there are two `@dynamicCallable` methods, there may be multiple ways to handle some -dynamic calls. What happens if a type specifies both the `withKeywordArguments:` and -`withArguments:` methods? +Since there are two `@dynamicCallable` methods, there may be multiple ways to +handle some dynamic calls. What happens if a type specifies both the +`withArguments:` and `withKeywordArguments:` methods? -We propose that the type checker resolve this ambiguity towards the tightest match based -on syntactic form of the expression. If a type implements both the `withKeywordArguments:` and -`withArguments:` methods, the compiler will use the `withArguments:` method for call sites that -have no keyword arguments and the `withKeywordArguments:` method for call sites that have at least one -keyword argument. +We propose that the type checker resolve this ambiguity towards the tightest +match based on syntactic form of the expression. If a type implements both the +`withArguments:` and `withKeywordArguments:` methods, the compiler will use the +`withArguments:` method for call sites that have no keyword arguments and the +`withKeywordArguments:` method for call sites that have at least one keyword +argument. -This ambiguity resolution rule works out very naturally given the behavior of the Swift type -checker, because it only resolves call expressions when the type of the base expression is -known. At that point, it knows the capabilities of the type (whether the base is a function -type, metatype, or a type where one of these two methods exist) and it knows the syntactic -form of the call. +This ambiguity resolution rule works out very naturally given the behavior of +the Swift type checker, because it only resolves call expressions when the type +of the base expression is known. At that point, it knows the capabilities of +the type (whether the base is a function type, metatype, or a valid +`@dynamicCallable` type where one of these two methods exist) and it knows the +syntactic form of the call. -This proposal does not require massive or invasive changes to the constraint solver. Please look -at the implementation for more details. +This proposal does not require massive or invasive changes to the constraint +solver. Please look at the implementation for more details. ## Example usage -Here, we sketch some example bindings to show how this could be used in practice. Note -that there are lots of design decisions that are orthogonal to -this proposal (e.g. how to handle exceptions) that we aren't going into here. This is just to -show how this feature provides an underlying facility that authors of language bindings can -use to achieve their desired result. These examples also show -`@dynamicMemberLookup` to illustrate how they work together, but elides all other the -implementation details. +Here, we sketch some example bindings to show how this could be used in +practice. Note that there are lots of design decisions that are orthogonal to +this proposal (e.g. how to handle exceptions) that we aren't going into here. +This is just to show how this feature provides an underlying facility that +language bindings authors can use to achieve their desired result. These +examples also show `@dynamicMemberLookup` to illustrate how they work together, +but elides other implementation details. JavaScript supports callable objects but does not have keyword arguments. @@ -239,17 +255,20 @@ struct JSValue { } ``` -On the other hand, a common JavaScript pattern is to take a dictionary of values as a stand-in -for argument labels (called like `example({first: 1, second: 2, third: 3})` in -JavaScript). A JavaScript bridge in Swift could choose to implement keyword argument support -to allow this to be called as `example(first: 1, second: 2, third: 3)` from Swift -code (kudos to Ben Rimmington for this observation). - -Python does support keyword arguments. While a Python binding could implement just the -`withKeywordArguments:` method, it may be better to implement both the non-keyword and keyword forms -to make the non-keyword case slightly more efficient (avoid allocating temporary storage) and to make -direct calls with positional arguments nicer (`x.dynamicallyCall(withArguments: 1, 2)` -instead of `x.dynamicallyCall(withKeywordArguments: ["": 1, "": 2])`). +On the other hand, a common JavaScript pattern is to take a dictionary of +values as a stand-in for argument labels (called like +`example({first: 1, second: 2, third: 3})` in JavaScript). A JavaScript bridge +in Swift could choose to implement keyword argument support to allow this to be +called as `example(first: 1, second: 2, third: 3)` from Swift code (kudos to +Ben Rimmington for [this +observation](https://forums.swift.org/t/pitch-3-introduce-user-defined-dynamically-callable-types/12232/45)). + +Python does support keyword arguments. While a Python binding could implement +only the `withKeywordArguments:` method, it is be better to implement both the +non-keyword and keyword forms to make the non-keyword case slightly more +efficient (avoid allocating temporary storage) and to make direct calls with +positional arguments nicer (`x.dynamicallyCall(withArguments: 1, 2)` instead of +`x.dynamicallyCall(withKeywordArguments: ["": 1, "": 2])`). Here is a sample Python binding: @@ -277,19 +296,20 @@ struct PythonObject { ## Limitations -Following the precedent of SE-0195, this attribute must be placed on the primary definition -of a type, not on an extension. +Following the precedent of SE-0195, this attribute must be placed on the +primary definition of a type, not on an extension. This proposal does not introduce the ability to provide dynamically callable -`static`/`class` members. We don't believe this is important given the goal of -supporting dynamic languages like Python, but it could be explored if a use case is discovered -in the future. Such future work should keep in mind that call syntax on metatypes is already -meaningful, and that ambiguity would have to be resolved somehow (e.g. through the most specific rule). +`static`/`class` members. We don't believe this is important given the goal of +supporting dynamic languages like Python, but it could be explored if a use +case is discovered in the future. Such future work should keep in mind that +call syntax on metatypes is already meaningful, and that ambiguity would have +to be resolved somehow (e.g. through the most specific rule). -This proposal supports direct calls of values and methods, but subsets out support for -currying methods in Smalltalk family languages. This is just an -implementation limitation given the current state of currying in the Swift compiler. Support -can be added in the future if there is a specific need. +This proposal supports direct calls of values and methods, but subsets out +support for currying methods in Smalltalk family languages. This is just an +implementation limitation given the current state of currying in the Swift +compiler. Support can be added in the future if there is a specific need. ## Source compatibility @@ -301,28 +321,29 @@ This is a strictly additive proposal with no ABI breaking changes. ## Effect on API resilience -This has no impact on API resilience which is not already captured by other language -features. +This has no impact on API resilience which is not already captured by other +language features. ## Future directions ### Dynamic member calling (for Smalltalk family languages) -In addition to supporting languages like Python and JavaScript, we would also like to grow to -support Smalltalk derived languages like Ruby and Squeak. These languages resolve -*method* calls using both the base name as well as the keyword arguments at the same time. -For example, consider this Ruby code: +In addition to supporting languages like Python and JavaScript, we would also +like to grow to support Smalltalk derived languages like Ruby and Squeak. These +languages resolve *method* calls using both the base name as well as the +keyword arguments at the same time. For example, consider this Ruby code: ```Ruby time = Time.zone.parse(user_time) ``` -The `Time.zone` reference is a member lookup, but `zone.parse(user_time)` is a method -call, and needs to be handled differently than a lookup of `zone.parse` followed by a direct -function call. +The `Time.zone` reference is a member lookup, but `zone.parse(user_time)` is a +method call, and needs to be handled differently than a lookup of `zone.parse` +followed by a direct function call. -This can be handled by adding a new `@dynamicMemberCallable` attribute, which acts similarly to -`@dynamicCallable` but enables dynamic member calls (instead of dynamic calls of `self`). +This can be handled by adding a new `@dynamicMemberCallable` attribute, which +acts similarly to `@dynamicCallable` but enables dynamic member calls (instead +of dynamic calls of `self`). `@dynamicMemberCallable` would have the following requirements: @@ -351,9 +372,10 @@ struct RubyObject { ### Variable-sized list of arguments This proposal is mainly directed at dynamic language interoperability. In this -use-case, it makes sense to take a variable sized list of arguments where each argument has -the same type. However, there are other use cases where it could make sense to support -static argument lists, akin to `operator()` in C++. For example, consider something like this: +use-case, it makes sense to take a variable sized list of arguments where each +argument has the same type. However, there are other use cases where it could +make sense to support static argument lists, akin to `operator()` in C++. +For example, consider something like this: ```swift struct BinaryFunction { @@ -361,33 +383,39 @@ struct BinaryFunction { } ``` -It is not unreasonable to look ahead to a day where sugaring such things is supported, -particularly when and if Swift gets [variadic generics](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#variadic-generics). +It is not unreasonable to look ahead to a day where sugaring such things is +supported, particularly when/if Swift gets [variadic +generics](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#variadic-generics). This could allow typesafe n-ary smart function pointer types. -We feel that the approach outlined in this proposal supports this direction. When and if a -motivating use case for the above feature comes up, we can simply add a new form to -represent it, and enhance the type checker to prefer that according to the "most specific -match" rule. If this is a likely direction, then it might be better to name the attribute -`@callable` instead of `@dynamicCallable` in anticipation of that future growth. +We feel that the approach outlined in this proposal supports this direction. +When and if a motivating use case for the above feature comes up, we can +simply add a new form to represent it, and enhance the type checker to prefer +that according to the "most specific match" rule. If this is a likely +direction, then it might be better to name the attribute `@callable` instead of +`@dynamicCallable` in anticipation of that future growth. ## Alternatives considered -Many alternatives were considered and discussed. Most of them are captured in the -["Alternatives Considered" section of SE-0195](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md#alternatives-considered). +Many alternatives were considered and discussed. Most of them are captured in +the ["Alternatives Considered" section of +SE-0195](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md#alternatives-considered). Here are a few points raised in the discussion: - - It was suggested that we use subscripts to represent the call implementations instead of a - function call, aligning with `@dynamicMemberLookup`. We think that functions are a better - fit here: the reason `@dynamicMemberLookup` uses subscripts is to allow the members to - be l-values, but call results are not l-values. - - - It was requested that we design and implement the 'static callable' version of this proposal - in conjunction with the dynamic version proposed here. In the author's opinion, it is - important to consider static callable support as a likely future direction to make sure that - the two features sit well next to each other and have a consistent design (something we - believe this proposal has done) but it doesn't make sense to join the two proposals. So - far, there have been no strong motivating use case presented for the static callable version, - and Swift lacks certain generics features (e.g. variadics) that would be necessary to make - static callables general. We feel that static callable should stand alone on its own merits. +- It was suggested that we use subscripts to represent the call + implementations instead of a function call, aligning with + `@dynamicMemberLookup`. We think that functions are a better fit here: the + reason `@dynamicMemberLookup` uses subscripts is to allow the members to be + l-values, but call results are not l-values. + +- It was requested that we design and implement the 'static callable' version + of this proposal in conjunction with the dynamic version proposed here. In + the author's opinion, it is important to consider static callable support as + a likely future direction to make sure that the two features sit well next + to each other and have a consistent design (something we believe this + proposal has done) but it doesn't make sense to join the two proposals. So + far, there have been no strong motivating use case presented for the static + callable version, and Swift lacks certain generics features (e.g. variadics) + that would be necessary to make static callables general. We feel that static + callable should stand alone on its own merits. From de7727f7dcf7bbfdea6763a87f4c8c534f27406e Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Mon, 4 Jun 2018 15:38:26 -0700 Subject: [PATCH 0709/4563] Update README.md Adjust expected release timeline for Swift 5. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3be205d4a7..a1d804878a 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ features actually shipped for a given version. Those are documented in each vers ## Development major version: Swift 5.0 -Expected release date: Late 2018 +Expected release date: Early 2019 ### Primary Focus: ABI Stability From 269bd92eb50412f7167ed9d15302489a053d6197 Mon Sep 17 00:00:00 2001 From: Paul Solt Date: Mon, 11 Jun 2018 14:39:09 -0400 Subject: [PATCH 0710/4563] Status Fix for Swift 4.2 Filtering Swift Evolution This proposal doesn't appear when you filter by Swift 4.2: https://apple.github.io/swift-evolution/#?version=4.2 I believe the status needs to change to "Implemented (Swift 4.2)" --- proposals/0197-remove-where.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0197-remove-where.md b/proposals/0197-remove-where.md index ac3eb9521d..e74c332e92 100644 --- a/proposals/0197-remove-where.md +++ b/proposals/0197-remove-where.md @@ -3,7 +3,7 @@ * Proposal: [SE-0197](0197-remove-where.md) * Author: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Implemented** for Swift 4.2 +* Status: **Implemented (Swift 4.2)** * Implementation: [apple/swift#11576](https://github.com/apple/swift/pull/11576) * Review: [Thread](https://forums.swift.org/t/se-0197-add-in-place-remove-where/8872) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/feec7890d6c193e9260ac9905456f25ef5656acd/proposals/0197-remove-where.md) From 3543a4042b4264ebac3cab041b93dc0625197813 Mon Sep 17 00:00:00 2001 From: Bas Broek Date: Mon, 11 Jun 2018 22:31:38 -0700 Subject: [PATCH 0711/4563] Fix syntax highlighting in SE-005 (#854) --- .../0005-objective-c-name-translation.md | 304 +++++++++--------- 1 file changed, 155 insertions(+), 149 deletions(-) diff --git a/proposals/0005-objective-c-name-translation.md b/proposals/0005-objective-c-name-translation.md index da47e03465..f25b75dc5a 100644 --- a/proposals/0005-objective-c-name-translation.md +++ b/proposals/0005-objective-c-name-translation.md @@ -48,15 +48,15 @@ APIs that feel right in Objective-C can feel wordy when used in Swift. For example: ```swift - let content = listItemView.text.stringByTrimmingCharactersInSet( - NSCharacterSet.whitespaceAndNewlineCharacterSet()) +let content = listItemView.text.stringByTrimmingCharactersInSet( + NSCharacterSet.whitespaceAndNewlineCharacterSet()) ``` The APIs used here follow the Objective-C guidelines. A more "Swifty" version of the same code might instead look like this: ```swift - let content = listItemView.text.trimming(.whitespaceAndNewlines) +let content = listItemView.text.trimming(.whitespaceAndNewlines) ``` The latter example more closely adheres to the [Swift API Design @@ -140,43 +140,43 @@ To get a sense of what these transformations do, consider a portion of the imported `UIBezierPath` API in Swift 2: ```swift - class UIBezierPath : NSObject, NSCopying, NSCoding { - convenience init(ovalInRect: CGRect) - func moveToPoint(_: CGPoint) - func addLineToPoint(_: CGPoint) - func addCurveToPoint(_: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint) - func addQuadCurveToPoint(_: CGPoint, controlPoint: CGPoint) - func appendPath(_: UIBezierPath) - func bezierPathByReversingPath() -> UIBezierPath - func applyTransform(_: CGAffineTransform) - var empty: Bool { get } - func containsPoint(_: CGPoint) -> Bool - func fillWithBlendMode(_: CGBlendMode, alpha: CGFloat) - func strokeWithBlendMode(_: CGBlendMode, alpha: CGFloat) - func copyWithZone(_: NSZone) -> AnyObject - func encodeWithCoder(_: NSCoder) - } +class UIBezierPath : NSObject, NSCopying, NSCoding { + convenience init(ovalInRect: CGRect) + func moveToPoint(_: CGPoint) + func addLineToPoint(_: CGPoint) + func addCurveToPoint(_: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint) + func addQuadCurveToPoint(_: CGPoint, controlPoint: CGPoint) + func appendPath(_: UIBezierPath) + func bezierPathByReversingPath() -> UIBezierPath + func applyTransform(_: CGAffineTransform) + var empty: Bool { get } + func containsPoint(_: CGPoint) -> Bool + func fillWithBlendMode(_: CGBlendMode, alpha: CGFloat) + func strokeWithBlendMode(_: CGBlendMode, alpha: CGFloat) + func copyWithZone(_: NSZone) -> AnyObject + func encodeWithCoder(_: NSCoder) +} ``` And the same API imported under our current, experimental implementation of this proposal: ```swift - class UIBezierPath : NSObject, NSCopying, NSCoding { - convenience init(ovalIn rect: CGRect) - func move(to point: CGPoint) - func addLine(to point: CGPoint) - func addCurve(to endPoint: CGPoint, controlPoint1 controlPoint1: CGPoint, controlPoint2 controlPoint2: CGPoint) - func addQuadCurve(to endPoint: CGPoint, controlPoint controlPoint: CGPoint) - func append(_ bezierPath: UIBezierPath) - func reversing() -> UIBezierPath - func apply(_ transform: CGAffineTransform) - var isEmpty: Bool { get } - func contains(_ point: CGPoint) -> Bool - func fill(_ blendMode: CGBlendMode, alpha alpha: CGFloat) - func stroke(_ blendMode: CGBlendMode, alpha alpha: CGFloat) - func copy(with zone: NSZone = nil) -> AnyObject - func encode(with aCoder: NSCoder) - } +class UIBezierPath : NSObject, NSCopying, NSCoding { + convenience init(ovalIn rect: CGRect) + func move(to point: CGPoint) + func addLine(to point: CGPoint) + func addCurve(to endPoint: CGPoint, controlPoint1 controlPoint1: CGPoint, controlPoint2 controlPoint2: CGPoint) + func addQuadCurve(to endPoint: CGPoint, controlPoint controlPoint: CGPoint) + func append(_ bezierPath: UIBezierPath) + func reversing() -> UIBezierPath + func apply(_ transform: CGAffineTransform) + var isEmpty: Bool { get } + func contains(_ point: CGPoint) -> Bool + func fill(_ blendMode: CGBlendMode, alpha alpha: CGFloat) + func stroke(_ blendMode: CGBlendMode, alpha alpha: CGFloat) + func copy(with zone: NSZone = nil) -> AnyObject + func encode(with aCoder: NSCoder) +} ``` In the latter case, a number of words that restated type information @@ -279,14 +279,14 @@ A couple of basic rules govern all matches: acronyms or prefixes:

-func documentForURL(_: NSURL) -> NSDocument?
-
+ func documentForURL(_: NSURL) -> NSDocument? + - while preventing partial-word mismatches: + while preventing partial-word mismatches: -
-   var thumbnailPreview : UIView  // not matched
-   
+
+  var thumbnailPreview : UIView  // not matched
+  
* **Matched text extends to the end of the type name**. Because we accept a match for *any suffix* of the type name, this code: @@ -313,14 +313,14 @@ Matches are a sequence of one or more of the following: matches `String` in `NSString`:
-func appendString(_: NSString)
-
+ func appendString(_: NSString) + * `Index` in the selector piece matches `Int` in the type name:
-func characterAtIndex(_: Int) -> unichar
-
+ func characterAtIndex(_: Int) -> unichar + * **Collection matches** @@ -328,33 +328,33 @@ func characterAtIndex(_: Int) -> unichar the type name:
-func removeObjectsAtIndexes(_: NSIndexSet)
-
+ func removeObjectsAtIndexes(_: NSIndexSet) + * A plural noun in the selector piece matches a collection type name if the noun's singular form matches the name of the collection's element type: -
-func arrangeObjects(_: [AnyObject]) -> [AnyObject]
-
+
+    func arrangeObjects(_: [AnyObject]) -> [AnyObject]
+    
* **Special suffix matches** * The empty string in the selector piece matches `Type` or `_t` in the type name:
-func writableTypesForSaveOperation(_: NSSaveOperationType) -> [String]
-func objectForKey(_: KeyType) -> AnyObject
-func startWithQueue(_: dispatch_queue_t, completionHandler: MKMapSnapshotCompletionhandler)
-
+ func writableTypesForSaveOperation(_: NSSaveOperationType) -> [String] + func objectForKey(_: KeyType) -> AnyObject + func startWithQueue(_: dispatch_queue_t, completionHandler: MKMapSnapshotCompletionhandler) + * The empty string in the selector piece matches *one or more digits followed by "D"* in the type name:
-func pointForCoordinate(_: CLLocationCoordinate2D) -> NSPoint
-
+ func pointForCoordinate(_: CLLocationCoordinate2D) -> NSPoint + In the examples above, the italic text is effectively skipped, so the bold part of the selector piece can be matched and pruned. @@ -378,27 +378,27 @@ skipped. user to write backticks. For example,
-extension NSParagraphStyle {
-  class func defaultParagraphStyle() -> NSParagraphStyle
-}
-let defaultStyle = NSParagraphStyle.defaultParagraphStyle()  // OK
-
+ extension NSParagraphStyle { +   class func defaultParagraphStyle() -> NSParagraphStyle + } + let defaultStyle = NSParagraphStyle.defaultParagraphStyle() // OK + would become:
-extension NSParagraphStyle {
-  class func `default`() -> NSParagraphStyle
-}
-let defaultStyle = NSParagraphStyle.`default`()              // Awkward
-
+ extension NSParagraphStyle { +   class func `default`() -> NSParagraphStyle + } + let defaultStyle = NSParagraphStyle.`default`() // Awkward + By contrast, later selector pieces become argument labels, which are allowed to match Swift keywords without requiring backticks:
-receiver.handle(someMessage, for: somebody)  // OK
-
+ receiver.handle(someMessage, for: somebody) // OK + * **Never transform a name into "get", "set", "with", "for", or "using"**, just to avoid creating absurdly vacuous names. @@ -413,19 +413,19 @@ receiver.handle(someMessage, for: somebody) // OK the parameter that follows. For example,
-func setTextColor(_: UIColor)
-...
-button.setTextColor(.red())  // clear
-
+ func setTextColor(_: UIColor) + ... + button.setTextColor(.red()) // clear + If we were to drop `Color`, leaving just `Text`, call sites would become confusing:
-func setText(_: UIColor)
-...
-button.setText(.red())      // appears to be setting the text!
-
+ func setText(_: UIColor) + ... + button.setText(.red()) // appears to be setting the text! + Note: We don't maintain a list of nouns, but if we did, this rule could be more simply phrased as "don't prune a suffix @@ -438,9 +438,9 @@ button.setText(.red()) // appears to be setting the text! of the class.
-var gestureRecognizers: [UIGestureRecognizer]
-func addGestureRecognizer(_: UIGestureRecognizer)
-
+ var gestureRecognizers: [UIGestureRecognizer] + func addGestureRecognizer(_: UIGestureRecognizer) + If we were to drop `GestureRecognizer`, leaving just `add`, we end up with a method that conceptually modifies the @@ -448,9 +448,9 @@ func addGestureRecognizer(_: UIGestureRecognizer) do so:
-var gestureRecognizers: [UIGestureRecognizer]
-func add(_: UIGestureRecognizer) // should indicate that we're adding to the property
-
+ var gestureRecognizers: [UIGestureRecognizer] + func add(_: UIGestureRecognizer) // should indicate that we're adding to the property + #### Pruning Steps @@ -471,20 +471,20 @@ shown: receiver. For example:
-extension NSColor {
-  func colorWithAlphaComponent(_: CGFloat) -> NSColor
-}
-let translucentForeground = foregroundColor.colorWithAlphaComponent(0.5)
-
+ extension NSColor { +   func colorWithAlphaComponent(_: CGFloat) -> NSColor + } + let translucentForeground = foregroundColor.colorWithAlphaComponent(0.5) + becomes:
-extension NSColor {
-  func withAlphaComponent(_: CGFloat) -> NSColor
-}
-let translucentForeground = foregroundColor.withAlphaComponent(0.5)
-
+ extension NSColor { +   func withAlphaComponent(_: CGFloat) -> NSColor + } + let translucentForeground = foregroundColor.withAlphaComponent(0.5) + 2. **Prune an additional hanging "By"**. Specifically, if @@ -497,20 +497,20 @@ let translucentForeground = foregroundColor.withAlphaComponent(0.5 b.frobnicating(c)`. For example:
-extension NSString {
-  func stringByApplyingTransform(_: NSString, reverse: Bool) -> NSString?
-}
-let sanitizedInput = rawInput.stringByApplyingTransform(NSStringTransformToXMLHex, reverse: false)
-
+ extension NSString { +   func stringByApplyingTransform(_: NSString, reverse: Bool) -> NSString? + } + let sanitizedInput = rawInput.stringByApplyingTransform(NSStringTransformToXMLHex, reverse: false) + becomes:
-extension NSString {
-  func applyingTransform(_: NSString, reverse: Bool) -> NString?
-}
-let sanitizedInput = rawInput.applyingTransform(NSStringTransformToXMLHex, reverse: false)
-
+ extension NSString { +   func applyingTransform(_: NSString, reverse: Bool) -> NString? + } + let sanitizedInput = rawInput.applyingTransform(NSStringTransformToXMLHex, reverse: false) + 3. **Prune a match for any type name in the signature from the tail of the preceding selector piece**. Specifically, @@ -524,54 +524,54 @@ let sanitizedInput = rawInput.applyingTransform(NSStringTransformToXMLHex For example,
-extension NSDocumentController {
-  func documentForURL(_ url: NSURL) -> NSDocument? // parameter introducer
-}
-extension NSManagedObjectContext {
-  var parentContext: NSManagedObjectContext?       // property
-}
-extension UIColor {
-  class func darkGrayColor() -> UIColor            // zero-argument method
-}
-...
-myDocument = self.documentForURL(locationOfFile)
-if self.managedObjectContext.parentContext != changedContext { return }
-foregroundColor = .darkGrayColor()
-
+ extension NSDocumentController { +   func documentForURL(_ url: NSURL) -> NSDocument? // parameter introducer + } + extension NSManagedObjectContext { +   var parentContext: NSManagedObjectContext? // property + } + extension UIColor { +   class func darkGrayColor() -> UIColor // zero-argument method + } + ... + myDocument = self.documentForURL(locationOfFile) + if self.managedObjectContext.parentContext != changedContext { return } + foregroundColor = .darkGrayColor() + becomes:
-extension NSDocumentController {
-  func documentFor(_ url: NSURL) -> NSDocument?
-}
-extension NSManagedObjectContext {
-  var parent : NSManagedObjectContext?
-}
-extension UIColor {
-  class func darkGray() -> UIColor
-}
-...
-myDocument = self.documentFor(locationOfFile)
-if self.managedObjectContext.parent != changedContext { return }
-foregroundColor = .darkGray()
-
+ extension NSDocumentController { +   func documentFor(_ url: NSURL) -> NSDocument? + } + extension NSManagedObjectContext { +   var parent : NSManagedObjectContext? + } + extension UIColor { +   class func darkGray() -> UIColor + } + ... + myDocument = self.documentFor(locationOfFile) + if self.managedObjectContext.parent != changedContext { return } + foregroundColor = .darkGray() + 3. **Prune a match for the enclosing type from the base name of a method so long as the match starts after a verb**. For example,
-extension UIViewController {
-  func dismissViewControllerAnimated(flag: Bool, completion: (() -> Void)? = nil)
-}
-
+ extension UIViewController { +   func dismissViewControllerAnimated(flag: Bool, completion: (() -> Void)? = nil) + } + becomes:
-extension UIViewController {
-  func dismissAnimated(flag: Bool, completion: (() -> Void)? = nil)
-}
-
+ extension UIViewController { +   func dismissAnimated(flag: Bool, completion: (() -> Void)? = nil) + } + ##### Why Does Order Matter? @@ -581,10 +581,12 @@ prevent both the head and tail from being pruned, prioritizing head-pruning steps can keep method families together. For example, in NSFontDescriptor: - func fontDescriptorWithSymbolicTraits(_: NSFontSymbolicTraits) -> NSFontDescriptor - func fontDescriptorWithSize(_: CGFloat) -> UIFontDescriptor - func fontDescriptorWithMatrix(_: CGAffineTransform) -> UIFontDescriptor - ... +```swift +func fontDescriptorWithSymbolicTraits(_: NSFontSymbolicTraits) -> NSFontDescriptor +func fontDescriptorWithSize(_: CGFloat) -> UIFontDescriptor +func fontDescriptorWithMatrix(_: CGAffineTransform) -> UIFontDescriptor +... +``` becomes: @@ -633,9 +635,11 @@ UIView.animateWithDuration( to become: - rootViewController.present(alert, animated: true) - UIView.animateWithDuration( - 0.2, delay: 0.0, animations: { self.logo.alpha = 0.0 }) { _ in self.logo.hidden = true } +```swift +rootViewController.present(alert, animated: true) +UIView.animateWithDuration( + 0.2, delay: 0.0, animations: { self.logo.alpha = 0.0 }) { _ in self.logo.hidden = true } +``` #### Add First Argument Labels @@ -684,8 +688,10 @@ array.enumerateObjects() { // OK **For Boolean properties, use the name of the getter as the property name in Swift*. For example: - @interface NSBezierPath : NSObject - @property (readonly,getter=isEmpty) BOOL empty; +```swift +@interface NSBezierPath : NSObject +@property (readonly,getter=isEmpty) BOOL empty; +``` will become @@ -712,13 +718,13 @@ as adopting `Comparable`. A survey of Foundation classes reveals not just NSDate but a few other classes that would be affected by this change. -
+```swift
 func compare(other: NSDate) -> NSComparisonResult
 func compare(decimalNumber: NSNumber) -> NSComparisonResult
 func compare(otherObject: NSIndexPath) -> NSComparisonResult
 func compare(string: String) -> NSComparisonResult
 func compare(otherNumber: NSNumber) -> NSComparisonResult
-
+``` ## Impact on existing code From 47d90df2aa2b64234d6c8795a3f538a24fe1e86b Mon Sep 17 00:00:00 2001 From: Matt Diephouse Date: Tue, 12 Jun 2018 00:11:48 -0700 Subject: [PATCH 0712/4563] Proposal to conform `Never` to `Equatable` and `Hashable` (#855) --- ...conform-never-to-hashable-and-equatable.md | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 proposals/XXXX-conform-never-to-hashable-and-equatable.md diff --git a/proposals/XXXX-conform-never-to-hashable-and-equatable.md b/proposals/XXXX-conform-never-to-hashable-and-equatable.md new file mode 100644 index 0000000000..0c5168d793 --- /dev/null +++ b/proposals/XXXX-conform-never-to-hashable-and-equatable.md @@ -0,0 +1,79 @@ +# Conform `Never` to `Equatable` and `Hashable` + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Author: [Matt Diephouse](https://github.com/mdiep) +* Review Manager: TBD +* Status: **Awaiting implementation** + +## Introduction +Extend `Never` so it conforms to `Equatable` and `Hashable`. + +Swift-evolution thread: [Conform Never to Equatable and Hashable](https://forums.swift.org/t/conform-never-to-equatable-and-hashable/12934) + +## Motivation +`Never` is very useful for representing impossible code. Most people are familiar with it as the return type of functions like `fatalError`, but `Never` is also useful when working with generic classes. + +For example, a `Result` type might use `Never` for its `Value` to represent something that _always_ errors or use `Never` for its `Error` to represent something that _never_ errors. + +Conditional conformances to `Equatable` and `Hashable` are also very useful when working with `enum`s so you can test easily or work with collections. + +But those don’t play well together. Without conformance to `Equatable` and `Hashable`, `Never` disqualifies your generic type from being `Equatable` and `Hashable`. + +## Proposed solution +The standard library should add `Equatable` and `Hashable` implementations for `Never`: + +```swift +extension Never: Equatable { + public static func == (lhs: Never, rhs: Never) -> Bool { + switch (lhs, rhs) { + } + } +} + +extension Never: Hashable { + public func hash(into hasher: inout Hasher) { + } +} +``` + +## Detailed design +The question that most often comes up is how `Never` should implement `Equatable`. How do you compare to `Never` values? + +But there are no `Never` values; it’s an uninhabitable type. Thankfully Swift makes this easy. By switching over the left- and right-hand sides, Swift correctly notices that there are no missing `case`s. Since there are no missing `case`s and every `case` returns a `Bool`, the function compiles. + +The new `Hashable` design makes its implementation even easier: the function does nothing. + +## Source compatibility +Existing applications may have their own versions of these conformances. In this case, Swift will give a redundant conformance error. + +## Effect on ABI stability +None. + +## Effect on API resilience +None. + +## Alternatives considered +### Make `Never` conform to _all_ protocols +As a bottom type, `Never` could conceivably conform to every protocol automatically. This would have some advantages and might be ideal, but would require a lot more work to determine the design and implement the behavior. + +### Don’t include this functionality in the standard library +This creates a significant headache—particularly for library authors. Since redundant conformance would be an error, the community would need to settle on a de facto library to add this conformance. + +### Require generic types to add conditional conformances with `Never` +An example `Result` type could manually add `Equatable` and `Hashable` implementations for `Never`s: + +```swift +extension Result: Equatable where Value == Never, Error: Equatable { + … +} + +extension Result: Equatable where Value: Hashable, Error == Never { + … +} + +extension Result: Equatable where Value == Never, Error == Never { + … +} +``` + +Adding so many extra conditional conformances is an unreasonable amount of work. \ No newline at end of file From fc675d002cb65910656ec940dfb44a734d3be3ac Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 12 Jun 2018 00:14:11 -0700 Subject: [PATCH 0713/4563] Initiate review for SE-0215 --- ...e.md => 0215-conform-never-to-hashable-and-equatable.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{XXXX-conform-never-to-hashable-and-equatable.md => 0215-conform-never-to-hashable-and-equatable.md} (94%) diff --git a/proposals/XXXX-conform-never-to-hashable-and-equatable.md b/proposals/0215-conform-never-to-hashable-and-equatable.md similarity index 94% rename from proposals/XXXX-conform-never-to-hashable-and-equatable.md rename to proposals/0215-conform-never-to-hashable-and-equatable.md index 0c5168d793..cf5236f78e 100644 --- a/proposals/XXXX-conform-never-to-hashable-and-equatable.md +++ b/proposals/0215-conform-never-to-hashable-and-equatable.md @@ -1,9 +1,9 @@ # Conform `Never` to `Equatable` and `Hashable` -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0215](0215-conform-never-to-hashable-and-equatable.md) * Author: [Matt Diephouse](https://github.com/mdiep) -* Review Manager: TBD -* Status: **Awaiting implementation** +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Status: **Active review (June 12...19)** ## Introduction Extend `Never` so it conforms to `Equatable` and `Hashable`. From d326aca111d2ce7bea233f93cfb3420f806a63b9 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 12 Jun 2018 17:47:35 +0100 Subject: [PATCH 0714/4563] [SE-0215] Add link to implementation pull request --- proposals/0215-conform-never-to-hashable-and-equatable.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0215-conform-never-to-hashable-and-equatable.md b/proposals/0215-conform-never-to-hashable-and-equatable.md index cf5236f78e..2fbb1706a1 100644 --- a/proposals/0215-conform-never-to-hashable-and-equatable.md +++ b/proposals/0215-conform-never-to-hashable-and-equatable.md @@ -4,6 +4,7 @@ * Author: [Matt Diephouse](https://github.com/mdiep) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Active review (June 12...19)** +* Implementation: [apple/swift#16857](https://github.com/apple/swift/pull/16857) ## Introduction Extend `Never` so it conforms to `Equatable` and `Hashable`. @@ -76,4 +77,4 @@ extension Result: Equatable where Value == Never, Error == Never { } ``` -Adding so many extra conditional conformances is an unreasonable amount of work. \ No newline at end of file +Adding so many extra conditional conformances is an unreasonable amount of work. From cf4a8f18269040312258c35cf41e794da09c565e Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 12 Jun 2018 16:44:44 -0700 Subject: [PATCH 0715/4563] SE-0212 is implemented in 4.2 --- proposals/0212-compiler-version-directive.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0212-compiler-version-directive.md b/proposals/0212-compiler-version-directive.md index 81a668d845..c5dd2776bd 100644 --- a/proposals/0212-compiler-version-directive.md +++ b/proposals/0212-compiler-version-directive.md @@ -4,7 +4,7 @@ * Author: [David Hart](https://github.com/hartbit) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Implementation: [apple/swift#15977](https://github.com/apple/swift/pull/15977) -* Status: **Accepted** +* Status: **Implemented (Swift 4.2)** ## Introduction From 9dfc5f37671af8c2a83afd5bab9caafefbb9a57f Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 12 Jun 2018 23:20:41 -0400 Subject: [PATCH 0716/4563] Assign the dynamic-callable proposal SE-0216 and put it in review --- .../{XXXX-dynamic-callable.md => 0216-dynamic-callable.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{XXXX-dynamic-callable.md => 0216-dynamic-callable.md} (99%) diff --git a/proposals/XXXX-dynamic-callable.md b/proposals/0216-dynamic-callable.md similarity index 99% rename from proposals/XXXX-dynamic-callable.md rename to proposals/0216-dynamic-callable.md index 2b9f49d3d2..21dd1bbb1e 100644 --- a/proposals/XXXX-dynamic-callable.md +++ b/proposals/0216-dynamic-callable.md @@ -1,10 +1,10 @@ # Introduce user-defined dynamically "callable" types -* Proposal: SE-TBD +* Proposal: SE-0216 * Authors: [Chris Lattner](https://github.com/lattner), [Dan Zheng](https://github.com/dan-zheng) -* Review Manager: TBD +* Review Manager: [John McCall](https://github.com/rjmccall) * Implementation: https://github.com/apple/swift/pull/16980 -* Status: **Awaiting review** +* Status: **Active Review (June 12...26, 2018)** Note: this proposal is largely adapted from Chris's original pitch [here](https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d). From 26015dbdf807aa9107125e3c872bc4aecc9b4984 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 13 Jun 2018 12:00:28 +0100 Subject: [PATCH 0717/4563] [SE-0216] Add missing proposal ID link --- proposals/0216-dynamic-callable.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0216-dynamic-callable.md b/proposals/0216-dynamic-callable.md index 21dd1bbb1e..ee7c91cbcf 100644 --- a/proposals/0216-dynamic-callable.md +++ b/proposals/0216-dynamic-callable.md @@ -1,9 +1,9 @@ # Introduce user-defined dynamically "callable" types -* Proposal: SE-0216 +* Proposal: [SE-0216](0216-dynamic-callable.md) * Authors: [Chris Lattner](https://github.com/lattner), [Dan Zheng](https://github.com/dan-zheng) * Review Manager: [John McCall](https://github.com/rjmccall) -* Implementation: https://github.com/apple/swift/pull/16980 +* Implementation: [apple/swift#16980](https://github.com/apple/swift/pull/16980) * Status: **Active Review (June 12...26, 2018)** Note: this proposal is largely adapted from Chris's original pitch From 02ebb072545c61d8f31f63f16e690020056b0964 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Wed, 13 Jun 2018 09:25:43 -0700 Subject: [PATCH 0718/4563] [SE-0216] Clarify "general callable behavior" in future directions section. (#860) --- proposals/0216-dynamic-callable.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/proposals/0216-dynamic-callable.md b/proposals/0216-dynamic-callable.md index ee7c91cbcf..8e68e5b494 100644 --- a/proposals/0216-dynamic-callable.md +++ b/proposals/0216-dynamic-callable.md @@ -369,13 +369,16 @@ struct RubyObject { } ``` -### Variable-sized list of arguments +### General callable behavior -This proposal is mainly directed at dynamic language interoperability. In this -use-case, it makes sense to take a variable sized list of arguments where each -argument has the same type. However, there are other use cases where it could -make sense to support static argument lists, akin to `operator()` in C++. -For example, consider something like this: +This proposal is mainly directed at dynamic language interoperability. For this +use case, it makes sense for the `dynamicallyCall` method to take a +variable-sized list of arguments where each argument has the same type. +However, it may be useful to support general callable behavior (akin to +`operator()` in C++) where the desugared "callable" method can have a fixed +number of arguments and arguments of different types. + +For example, consider something like: ```swift struct BinaryFunction { @@ -389,11 +392,14 @@ generics](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#v This could allow typesafe n-ary smart function pointer types. We feel that the approach outlined in this proposal supports this direction. -When and if a motivating use case for the above feature comes up, we can -simply add a new form to represent it, and enhance the type checker to prefer -that according to the "most specific match" rule. If this is a likely -direction, then it might be better to name the attribute `@callable` instead of -`@dynamicCallable` in anticipation of that future growth. +When/if a motivating use case for general callable behavior comes up, we can +simply add a new form to represent it and enhance the type checker to prefer +that during ambiguity resolution. If this is a likely direction, then it may be +better to name the attribute `@callable` instead of `@dynamicCallable` in +anticipation of that future growth. + +We believe that general callable behavior and `@dynamicCallable` are orthogonal +features and should be evaluated separately. ## Alternatives considered From 06a694eba3b5d83cb613227f34d645bc83b24d94 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 13 Jun 2018 09:45:25 -0700 Subject: [PATCH 0719/4563] Remove link to the old gist. This proposal is now canonical. --- proposals/0216-dynamic-callable.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/proposals/0216-dynamic-callable.md b/proposals/0216-dynamic-callable.md index 8e68e5b494..498f6ef67f 100644 --- a/proposals/0216-dynamic-callable.md +++ b/proposals/0216-dynamic-callable.md @@ -6,9 +6,6 @@ * Implementation: [apple/swift#16980](https://github.com/apple/swift/pull/16980) * Status: **Active Review (June 12...26, 2018)** -Note: this proposal is largely adapted from Chris's original pitch -[here](https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d). - ## Introduction This proposal is a follow-on to [SE-0195 - Introduce User-defined "Dynamic Member From f13edd7854831652bdfd0665e0ead8af6e8c5415 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 14 Jun 2018 21:19:53 +0100 Subject: [PATCH 0720/4563] [SE-0195] Remove links to the old gist --- proposals/0195-dynamic-member-lookup.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/proposals/0195-dynamic-member-lookup.md b/proposals/0195-dynamic-member-lookup.md index 513a698c13..3541b01560 100644 --- a/proposals/0195-dynamic-member-lookup.md +++ b/proposals/0195-dynamic-member-lookup.md @@ -87,9 +87,7 @@ with this proposal). Given that Swift already has an intentionally incredibly syntax-extensible design, we only need two minor enhancements to the language to support these dynamic languages in an ergonomic way: this proposal (which introduces the `@dynamicMemberLookup` attribute) and a -related -[`@dynamicCallable`](https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d) -proposal. +related [`@dynamicCallable`](0216-dynamic-callable.md) proposal. To show the impact of these proposals, consider this Python code: @@ -140,7 +138,7 @@ working with the Python `pickle` API and the builtin Python function `open`: This can all be expressed today as library functionality written in Swift, but without this proposal, the code required is unnecessarily verbose and gross. Without it (but *with* the -related [`@dynamicCallable` proposal](https://gist.github.com/lattner/a6257f425f55fe39fd6ac7a2354d693d) +related [`@dynamicCallable`](0216-dynamic-callable.md) proposal) the code would have explicit member lookups all over the place: ```swift From 3b040610805b1554087ed65245b4b40b2b42c379 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 18 Jun 2018 17:56:45 +0100 Subject: [PATCH 0721/4563] [SE-0214] Accepted with revisions (KeyValuePairs) (#862) --- proposals/0214-DictionaryLiteral.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/proposals/0214-DictionaryLiteral.md b/proposals/0214-DictionaryLiteral.md index 3ce42d2c4c..5de64336bb 100644 --- a/proposals/0214-DictionaryLiteral.md +++ b/proposals/0214-DictionaryLiteral.md @@ -1,14 +1,16 @@ -# Renaming the `DictionaryLiteral` type to `KeyValueList` +# Renaming the `DictionaryLiteral` type to `KeyValuePairs` * Proposal: [SE-0214](0214-DictionaryLiteral.md) * Authors: [Erica Sadun](https://github.com/erica), [Chéyo Jiménez](https://github.com/masters3d) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Active review (May 18...23)** +* Status: **Accepted with revisions** * Implementation: [apple/swift#16577](https://github.com/apple/swift/pull/16577) +* Decision Notes: [Rationale](https://forums.swift.org/t/accepted-with-revision-se-0214-renaming-the-dictionaryliteral-type-to-keyvaluepairs/13661) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/12315c44dd6b36fec924f4f6c30f48d8784ae4cc/proposals/0214-DictionaryLiteral.md) ## Introduction -This proposal renames the confusing and misnamed [`DictionaryLiteral`](https://github.com/apple/swift/blob/c25188bafd1c775d4ceecc4a795f614f00451bf9/stdlib/public/core/Mirror.swift#L646) type to `KeyValueList`. This type is neither a dictionary nor a literal. It is a list of key-value pairs. +This proposal renames the confusing and misnamed [`DictionaryLiteral`](https://github.com/apple/swift/blob/c25188bafd1c775d4ceecc4a795f614f00451bf9/stdlib/public/core/Mirror.swift#L646) type to `KeyValuePairs`. This type is neither a dictionary nor a literal. It is a list of key-value pairs. There is no strong motivation to deprecate. The type does not produce active harm. Instead, it adds measurable (if small) utility and will be part of the ABI. A sensible renaming mitigates the most problematic issue with the type. @@ -67,7 +69,7 @@ This key-value pair processing might support custom initializers. It allows dupl ## Detailed Design -`DictionaryLiteral` is renamed to `KeyValueList`. A typealias preserves the old name for compatibility but can be deprecated as of Swift 5.0. +`DictionaryLiteral` is renamed to `KeyValuePairs`. A typealias preserves the old name for compatibility but can be deprecated as of Swift 5.0. This name was extensively bikeshedded on the [Swift Forum thread](https://forums.swift.org/t/100-bikeshed-topic-dictionaryliteral/7385) before proposal. The runner up name was `KeyValueArray`. From 68257da6807227b5e6dbc87f0bfe71f54dd8c38d Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 19 Jun 2018 15:28:27 -0700 Subject: [PATCH 0722/4563] SE-0202 amendment: Remove `Collection.randomElement` requirement. As a protocol requirement, this poses a potential forward compatibility hazard with move-only collections, and it's far from the only collection operation that would trap on a huge range used as a collection. --- proposals/0202-random-unification.md | 66 +++++----------------------- 1 file changed, 10 insertions(+), 56 deletions(-) diff --git a/proposals/0202-random-unification.md b/proposals/0202-random-unification.md index 31c5defc48..b8667302d1 100644 --- a/proposals/0202-random-unification.md +++ b/proposals/0202-random-unification.md @@ -131,7 +131,7 @@ let randomBool2 = Bool.random(using: &myCustomRandomNumberGenerator) #### Random Element -For `Collection` we add a random method with default implementation for collections to get a random element. +For `Collection` we add an extension method for collections to get a random element. `Collection` example: ```swift @@ -143,6 +143,13 @@ print(greetings.randomElement()!) // This returns an Optional print(greetings.randomElement(using: &myCustomRandomNumberGenerator)!) // This returns an Optional ``` +Note that some types make it easy to form collections with more elements than +can be represented as an `Int`, such as the range `Int.min...Int.max`, and +`randomElement` will likely trap on such collections. However, such ranges +are likely to trap when used with almost any collection API, and the +`random(in:)` method on `FixedWidthInteger` can be used for this purpose +instead. + #### Shuffle API As a result of adding the random API, it only makes sense to utilize that power to fuel the shuffle methods. We extend `MutableCollection` to add a method to shuffle the collection itself, and extend `Sequence` to add a method to return a shuffled version of itself in a new array. Example: @@ -199,14 +206,6 @@ public struct Random : RandomNumberGenerator { public mutating func next() -> T } -public protocol Collection { - // Returns a random element from the collection - func randomElement( - using generator: inout T - ) -> Element? -} - -// Default implementation extension Collection { // Returns a random element from the collection // Can return nil if isEmpty is true @@ -220,52 +219,12 @@ extension Collection { } } -// We have to add this extension to support syntax like (Int.min ..< Int.max).random() -// That fails because Collection's implementation utilizes the count property and -// from Int.min to Int.max, an Int overflows with that big of a value. -extension Range -where Bound : FixedWidthInteger, - Bound.Stride : SignedInteger, - Bound.Magnitude : UnsignedInteger { - // Returns a random element within lowerBound and upperBound - // Can return nil if lowerBound == upperBound - public func randomElement( - using generator: inout T - ) -> Element? - - /// Uses the standard library's default RNG - public func randomElement() -> Element? { - return randomElement(using: &Random.default) - } -} - -// We have to add this extension to support syntax like (Int.min ... Int.max).random() -// That fails because Collection's implementation utilizes the count property and -// from Int.min to Int.max, an Int overflows with that big of a value. -extension ClosedRange -where Bound : FixedWidthInteger, - Bound.Stide : SignedInteger, - Bound.Magnitude : UnsignedInteger { - // Returns a random element within lowerBound and upperBound - public func randomElement( - using generator: inout T - ) -> Element? - - /// Uses the standard library's default RNG - public func randomElement() -> Element? { - return random(using: &Random.default) - } -} - // Enables developers to use things like Int.random(in: 5 ..< 12) which does not use modulo bias. -// This differs from things like (5 ..< 12).randomElement() because those ranges return an Optional, whereas -// this returns a non-optional. Ranges get the benefit of using random, but this is the preferred -// method as it provides a cleaner API to users and clearly expresses the operation. // It is worth noting that any empty range entered here will abort the program. // We do this to preserve a general use case design that the core team expressed. // For those that are that unsure whether or not their range is empty or not, -// they can simply call random from the range itself to produce an Optional, or if/guard check -// whether or not the range is empty beforehand, then use these functions. +// they can if/guard check whether or not the range is empty beforehand, then +// use these functions. extension FixedWidthInteger where Self.Stride : SignedInteger, Self.Magnitude : UnsignedInteger { @@ -292,9 +251,6 @@ where Self.Stride : SignedInteger, } // Enables developers to use things like Double.random(in: 5 ..< 12) which does not use modulo bias. -// This differs from things like (5.0 ..< 12.0).randomElement() because those ranges return an Optional, whereas -// this returns a non-optional. Ranges get the benefit of using random, but this is the preferred -// method as it provides a cleaner API to users and clearly expresses the operation. // It is worth noting that any empty range entered here will abort the program. // We do this to preserve a general use case design that the core team expressed. // For those that are that unsure whether or not their range is empty or not, @@ -393,8 +349,6 @@ In a world where this did return an error to Swift, it would require types like ```swift let randomDice = Int.random(in: 1 ... 6)! -// would be equivalent to -// let randomDice = (1 ... 6).randomElement()! ``` "I just want a random dice roll, what is this ! the compiler is telling me to add?" From 3be43e041eb66f6c62c12bd095ef177565256de1 Mon Sep 17 00:00:00 2001 From: Liron Yahdav Date: Thu, 21 Jun 2018 19:43:44 -0700 Subject: [PATCH 0723/4563] Update 0026-abstract-classes-and-methods.md (#865) typos --- proposals/0026-abstract-classes-and-methods.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0026-abstract-classes-and-methods.md b/proposals/0026-abstract-classes-and-methods.md index 5b663a6a74..c7cb8f8f28 100644 --- a/proposals/0026-abstract-classes-and-methods.md +++ b/proposals/0026-abstract-classes-and-methods.md @@ -133,8 +133,8 @@ stabilizing in Swift 3.0. ## Alternatives considered As first reading, it seems that protocols and protocol extensions might fit the need. It -actually does not because abstract classes can have attributs and properties that -protocols does not support. +actually does not because abstract classes can have attributes and properties that +protocols do not support. An alternative solution would be to add attributes to protocols and protocol extensions, but this might break compatibility with Objective-C runtime. From d06d9acc37682ceede64f61ba842ce28732da7bc Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 22 Jun 2018 10:53:51 +0100 Subject: [PATCH 0724/4563] [SE-0211] Accepted with revisions (numericValue) --- proposals/0211-unicode-scalar-properties.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/proposals/0211-unicode-scalar-properties.md b/proposals/0211-unicode-scalar-properties.md index d9950a74ae..4ab0dda3ec 100644 --- a/proposals/0211-unicode-scalar-properties.md +++ b/proposals/0211-unicode-scalar-properties.md @@ -1,10 +1,12 @@ # Add Unicode Properties to `Unicode.Scalar` * Proposal: [SE-0211](0211-unicode-scalar-properties.md) -* Authors: [Tony Allevato](https://github.com/allevato) +* Author: [Tony Allevato](https://github.com/allevato) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **In active review (April 24 – May 1)** +* Status: **Accepted with revisions** * Implementation: [apple/swift#15593](https://github.com/apple/swift/pull/15593) +* Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0211-add-unicode-properties-to-unicode-scalar/13857) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/9b1c670206052f5c94bcb20df1c30c27a06e9755/proposals/0211-unicode-scalar-properties.md) ## Introduction @@ -330,7 +332,7 @@ extension Unicode.Scalar.Properties { public var numericType: Unicode.NumericType? /// Corresponds to the `Numeric_Value` Unicode property. - public var numericValue: Double + public var numericValue: Double? } extension Unicode { From 74d62ec62b742848ee03005c478eccdc5f7d504d Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Thu, 21 Jun 2018 07:15:41 -0700 Subject: [PATCH 0725/4563] Add SwiftPM proposal template This adds a new proposal template for SwiftPM proposals. The existing Swift proposal template is renamed to 0000-swift-template.md and both templates are moved into a directory called `proposal-templates/`. --- process.md | 2 +- .../0000-swift-template.md | 0 proposal-templates/0000-swiftpm-template.md | 62 +++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) rename 0000-template.md => proposal-templates/0000-swift-template.md (100%) create mode 100644 proposal-templates/0000-swiftpm-template.md diff --git a/process.md b/process.md index 306008f267..3c3eccbdbb 100644 --- a/process.md +++ b/process.md @@ -52,7 +52,7 @@ Swift, please consider how your proposal fits in with the larger goals of the upcoming Swift release. Proposals that are clearly out of scope for the upcoming Swift release will not be brought up for review. If you can't resist discussing a proposal that you know is out of scope, please include the tag `[Out of scope]` in the subject. * **Socialize the idea**: propose a rough sketch of the idea in the ["pitches" section of the Swift forums](https://forums.swift.org/c/evolution/pitches), the problems it solves, what the solution looks like, etc., to gauge interest from the community. -* **Develop the proposal**: expand the rough sketch into a complete proposal, using the [proposal template](0000-template.md), and continue to refine the proposal on the forums. Prototyping an implementation and its uses along with the proposal is *required* because it helps ensure both technical feasibility of the proposal as well as validating that the proposal solves the problems it is meant to solve. +* **Develop the proposal**: expand the rough sketch into a complete proposal, using the [proposal template](proposal-templates/0000-swift-template.md), and continue to refine the proposal on the forums. Prototyping an implementation and its uses along with the proposal is *required* because it helps ensure both technical feasibility of the proposal as well as validating that the proposal solves the problems it is meant to solve. * **Request a review**: initiate a pull request to the [swift-evolution repository][swift-evolution-repo] to indicate to the core team that you would like the proposal to be reviewed. When the proposal is sufficiently detailed and clear, and addresses feedback from earlier discussions of the idea, the pull request will be accepted. The proposal will be assigned a proposal number as well as a core team member to manage the review. * **Address feedback**: in general, and especially [during the review period][proposal-status], be responsive to questions and feedback about the proposal. diff --git a/0000-template.md b/proposal-templates/0000-swift-template.md similarity index 100% rename from 0000-template.md rename to proposal-templates/0000-swift-template.md diff --git a/proposal-templates/0000-swiftpm-template.md b/proposal-templates/0000-swiftpm-template.md new file mode 100644 index 0000000000..b3593ea03e --- /dev/null +++ b/proposal-templates/0000-swiftpm-template.md @@ -0,0 +1,62 @@ +# Package Manager Feature name + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Author 1](https://github.com/swiftdev), [Author 2](https://github.com/swiftdev) +* Review Manager: TBD +* Status: **Awaiting implementation** + +*During the review process, add the following fields as needed:* + +* Implementation: [apple/swift-package-manager#NNNNN](https://github.com/apple/swift-package-manager/pull/NNNNN) +* Decision Notes: [Rationale](https://forums.swift.org/), [Additional Commentary](https://forums.swift.org/) +* Bugs: [SR-NNNN](https://bugs.swift.org/browse/SR-NNNN), [SR-MMMM](https://bugs.swift.org/browse/SR-MMMM) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md) +* Previous Proposal: [SE-XXXX](XXXX-filename.md) + +## Introduction + +A short description of what the feature is. Try to keep it to a single-paragraph +"elevator pitch" so the reader understands what problem this proposal is +addressing. + +Swift-evolution thread: [Discussion thread topic for that +proposal](https://forums.swift.org/) + +## Motivation + +Describe the problems that this proposal seeks to address. If the problem is +that some functionality is currently hard to use, show how it is currently used +and describe its drawbacks. If it's completely new functionality that cannot be +emulated, motivate why this new functionality would help Swift developers create +better Swift packages. + +## Proposed solution + +Describe your solution to the problem. Provide examples and describe how they +work. Show how your solution is better than current workarounds: is it cleaner, +easier, or more efficient? + +## Detailed design + +Describe the design of the solution in detail. If it involves adding or +modifying functionality in the package manager, explain how the package manager +behaves in different scenarios and with existing features. If it's a new API in +the `Package.swift` manifest, show the full API and its documentation comments +detailing what it does. The detail in this section should be sufficient for +someone who is *not* one of the author of the proposal to be able to reasonably +implement the feature. + +## Security + +Does this change have any impact on security, safety, or privacy? + +## Impact on exisiting packages + +Explain if and how this proposal will affect the behavior of existing packages. +If there will be impact, is it possible to gate the changes on the tools version +of the package manifest? + +## Alternatives considered + +Describe alternative approaches to addressing the same problem, and +why you chose this approach instead. From 65bf4a5b324adacbda4c4059bdbb9465aad873c0 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Fri, 29 Jun 2018 16:54:11 -0700 Subject: [PATCH 0726/4563] Inaugurate "unwrap or die" as SE-0217 --- proposals/{XXXX-bangbang.md => 0217-bangbang.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{XXXX-bangbang.md => 0217-bangbang.md} (99%) diff --git a/proposals/XXXX-bangbang.md b/proposals/0217-bangbang.md similarity index 99% rename from proposals/XXXX-bangbang.md rename to proposals/0217-bangbang.md index 288e8ccefc..0ef79bfad9 100644 --- a/proposals/XXXX-bangbang.md +++ b/proposals/0217-bangbang.md @@ -1,8 +1,8 @@ # Introducing the `!!` "Unwrap or Die" operator to the Swift Standard Library -* Proposal: SE-TBD +* Proposal: SE-0217 * Author(s): [Ben Cohen](https://github.com/airspeedswift), [Dave DeLong](https://github.com/davedelong), [Paul Cantrell](https://github.com/pcantrell), [Erica Sadun](http://github.com/erica), and several other folk -* Status: _Implementedish_ +* Status: **Active Review (June 30...July 7, 2018)** - ## Introduction This proposal introduces an annotating forced-unwrapping operator to the Swift standard library. It augments the `?`, `??`, and `!` family, adding `!!`. This "unwrap or die" operator provides code-sourced rationales for failed unwraps, supporting self-documentation and safer development. The `!!` operator is commonly implemented in the wider Swift Community and should be considered for official adoption. From 51b11dcfec6684b1ed0f4d76ebc7a8d256f06e1a Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 30 Jun 2018 21:23:42 +0100 Subject: [PATCH 0731/4563] [SE-0217] Fix the Additional Operators table --- proposals/0217-bangbang.md | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/proposals/0217-bangbang.md b/proposals/0217-bangbang.md index 7fc6fa908f..fe2b0139e7 100644 --- a/proposals/0217-bangbang.md +++ b/proposals/0217-bangbang.md @@ -616,20 +616,18 @@ The throwing variation of `!!` could be named `?!`, taking an `Error` on the rhs Adding `?!` produces the following operator family: -|----------|--------------------| | Operator | Use | -|----------|--------------------| -| ! | Force unwrap | -| ? | Optional chaining | -| !! | Unwrap or die | -| ?? | Nil coalescing | -| ?! | Unwrap or throw | -| try | Throw on error | -| try! | Die on error | -| try? | Nil on error | -| as! | Die on failed cast | -| as? | Nil on failed cast | -|----------|--------------------| +| :------- | :----------------- | +| `!` | Force unwrap | +| `?` | Optional chaining | +| `!!` | Unwrap or die | +| `??` | Nil coalescing | +| `?!` | Unwrap or throw | +| `try` | Throw on error | +| `try!` | Die on error | +| `try?` | Nil on error | +| `as!` | Die on failed cast | +| `as?` | Nil on failed cast | ## Source compatibility From 8a67e1288aa20b333dbeb49778f09f076e00be1f Mon Sep 17 00:00:00 2001 From: Daiki Matsudate Date: Tue, 3 Jul 2018 02:56:48 +0900 Subject: [PATCH 0732/4563] [Proposal] introduce Dictionary.compactMapValues(_:) (#787) * add 0000-introduce-compact-map-values * edits/rewrite to clean up language issues * fix typo * make status awating review * Number SE and schedule for review --- .../0218-introduce-compact-map-values.md | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 proposals/0218-introduce-compact-map-values.md diff --git a/proposals/0218-introduce-compact-map-values.md b/proposals/0218-introduce-compact-map-values.md new file mode 100644 index 0000000000..c716868932 --- /dev/null +++ b/proposals/0218-introduce-compact-map-values.md @@ -0,0 +1,103 @@ +# Introduce `compactMapValues` to Dictionary + +* Proposal: [SE-0218](0218-introduce-compact-map-values.md) +* Authors: [Daiki Matsudate](https://github.com/d-date) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Pending Review (July 3-9 2018)** + + + +* Implementation: [apple/swift#15017](https://github.com/apple/swift/pull/15017) +* Pitch: [Forum thread](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741) + + + + + +## Introduction + +This proposal adds a combined filter/map operation to `Dictionary`, as a companion to the `mapValues` and filter methods introduced by [SE-0165](https://github.com/apple/swift-evolution/blob/master/proposals/0165-dict.md). The new compactMapValues operation corresponds to compactMap on Sequence. + +- Swift forums pitch: [Add compactMapValues to Dictionary](https://forums.swift.org/t/pitch-add-compactmapvalues-to-dictionary/8741) + +## Motivation + +Swift 4 introduced two new `Dictionary` operations: the new method `mapValues` and a new version of `filter`. They correspond to the `Sequence` methods `map` and `filter`, respectively, but they operate on `Dictionary` values and return dictionaries rather than arrays. + +However, [SE-0165](https://github.com/apple/swift-evolution/blob/master/proposals/0165-dict.md) left a gap in the API: it did not introduce a `Dictionary`-specific version of `compactMap`. We sometimes need to transform and filter values of a `Dictionary` at the same time, and `Dictionary` does not currently provide an operation that directly supports this. + +For example, consider the task of filtering out `nil` values from a `Dictionary` of optionals: + +```swift +let d: [String: String?] = ["a": "1", "b": nil, "c": "3"] +let r1 = d.filter { $0.value != nil }.mapValues { $0! } +let r2 = d.reduce(into: [String: String]()) { (result, item) in result[item.key] = item.value } +// r1 == r2 == ["a": "1", "c": "3"] +``` + +Or try running a failable conversion on dictionary values: + +```swift +let d: [String: String] = ["a": "1", "b": "2", "c": "three"] +let r1 = d.mapValues(Int.init).filter { $0.value != nil }.mapValues { $0! } +let r2 = d.reduce(into: [String: Int]()) { (result, item) in result[item.key] = Int(item.value) } +// r == ["a": 1, "b": 2] +``` + +While `mapValues` and `filter` can be combined to solve this tasks, the solution needs multiple passes on the input dictionary, which is not particularly efficient. `reduce(into:)` provides a more efficient solution, but it is rather tricky to get right, and it obscures the intended meaning of the code with implementation details. + +It seems worth adding an extra extension method to `Dictionary` for this operation; its obvious name is `compactMapValues(_:)`, combining the precedents set by `compactMap` and `mapValues`. + +```swift +let r3 = d.compactMapValues(Int.init) +``` + +## Proposed solution + +Add the following to `Dictionary`: + +```swift +let d: [String: String?] = ["a": "1", "b": nil, "c": "3"] +let r4 = d.compactMapValues({$0}) +// r4 == ["a": "1", "c": "3"] +``` + +Or, + +```swift +let d: [String: String] = ["a": "1", "b": "2", "c": "three"] +let r5 = d.compactMapValues(Int.init) +// r5 == ["a": 1, "b": 2] +``` + +## Detailed design + +Add the following to `Dictionary`: + +```swift +extension Dictionary { + public func compactMapValues(_ transform: (Value) throws -> T?) rethrows -> [Key: T] { + return try self.reduce(into: [Key: T](), { (result, x) in + if let value = try transform(x.value) { + result[x.key] = value + } + }) + } +} +``` + +## Source compatibility + +This change is purely additive so has no source compatibility consequences. + +## Effect on ABI stability + +This change is purely additive so has no ABI stability consequences. + +## Effect on API resilience + +This change is purely additive so has no API resilience consequences. + +## Alternatives considered + +We can simply omit this method from the standard library -- however, we already have `mapValues` and `filter`, and it seems reasonable to fill the API hole left between them with a standard extension. From 26a1e350145e99b03b877808cdbb7424d03f5cd3 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 2 Jul 2018 11:10:44 -0700 Subject: [PATCH 0733/4563] Update 0218-introduce-compact-map-values.md --- proposals/0218-introduce-compact-map-values.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0218-introduce-compact-map-values.md b/proposals/0218-introduce-compact-map-values.md index c716868932..7b57526cbc 100644 --- a/proposals/0218-introduce-compact-map-values.md +++ b/proposals/0218-introduce-compact-map-values.md @@ -3,7 +3,7 @@ * Proposal: [SE-0218](0218-introduce-compact-map-values.md) * Authors: [Daiki Matsudate](https://github.com/d-date) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Pending Review (July 3-9 2018)** +* Status: **Scheduled for Review: July 3...9)** From cb7dba8fc80ec319eb204df1360dcb52c585def0 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 2 Jul 2018 11:11:41 -0700 Subject: [PATCH 0734/4563] Delete rogue paren --- proposals/0218-introduce-compact-map-values.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0218-introduce-compact-map-values.md b/proposals/0218-introduce-compact-map-values.md index 7b57526cbc..6e623a4a97 100644 --- a/proposals/0218-introduce-compact-map-values.md +++ b/proposals/0218-introduce-compact-map-values.md @@ -3,7 +3,7 @@ * Proposal: [SE-0218](0218-introduce-compact-map-values.md) * Authors: [Daiki Matsudate](https://github.com/d-date) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Scheduled for Review: July 3...9)** +* Status: **Scheduled for Review: July 3...9** From 749b1624790accf2997719d8ca32ce22069d5314 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Thu, 5 Jul 2018 10:23:35 -0700 Subject: [PATCH 0735/4563] Kick off review for 0218 --- proposals/0218-introduce-compact-map-values.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0218-introduce-compact-map-values.md b/proposals/0218-introduce-compact-map-values.md index 6e623a4a97..0d907baf88 100644 --- a/proposals/0218-introduce-compact-map-values.md +++ b/proposals/0218-introduce-compact-map-values.md @@ -3,7 +3,7 @@ * Proposal: [SE-0218](0218-introduce-compact-map-values.md) * Authors: [Daiki Matsudate](https://github.com/d-date) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Scheduled for Review: July 3...9** +* Status: **In review: July 5...11** From 357b9b9ef5e084dab9ca5a5214f814679fc47ea9 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 5 Jul 2018 19:12:15 +0100 Subject: [PATCH 0736/4563] [SE-0218] Fix "missing or invalid proposal status" --- proposals/0218-introduce-compact-map-values.md | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/proposals/0218-introduce-compact-map-values.md b/proposals/0218-introduce-compact-map-values.md index 0d907baf88..0d8bfccb3b 100644 --- a/proposals/0218-introduce-compact-map-values.md +++ b/proposals/0218-introduce-compact-map-values.md @@ -1,24 +1,16 @@ # Introduce `compactMapValues` to Dictionary * Proposal: [SE-0218](0218-introduce-compact-map-values.md) -* Authors: [Daiki Matsudate](https://github.com/d-date) +* Author: [Daiki Matsudate](https://github.com/d-date) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **In review: July 5...11** - - - +* Status: **Active review (July 5...11)** * Implementation: [apple/swift#15017](https://github.com/apple/swift/pull/15017) -* Pitch: [Forum thread](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741) - - - - ## Introduction This proposal adds a combined filter/map operation to `Dictionary`, as a companion to the `mapValues` and filter methods introduced by [SE-0165](https://github.com/apple/swift-evolution/blob/master/proposals/0165-dict.md). The new compactMapValues operation corresponds to compactMap on Sequence. -- Swift forums pitch: [Add compactMapValues to Dictionary](https://forums.swift.org/t/pitch-add-compactmapvalues-to-dictionary/8741) +- Swift forums pitch: [Add compactMapValues to Dictionary](https://forums.swift.org/t/add-compactmapvalues-to-dictionary/8741) ## Motivation From a6db6023f96bb3f38fc65171fcab6b5b722fe48c Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sat, 7 Jul 2018 11:23:17 -0400 Subject: [PATCH 0737/4563] Update commonly_proposed.md (inferred return for guard bodies) (#869) * commonly_proposed.md: As discussed on Swift Evolution, inferred `return` for `guard` bodies is a commonly proposed change. --- commonly_proposed.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/commonly_proposed.md b/commonly_proposed.md index 82509fd043..08ea2712e8 100644 --- a/commonly_proposed.md +++ b/commonly_proposed.md @@ -31,6 +31,8 @@ Several of the discussions below refer to "C family" languages. This is intended * [Remove support for `default:` in `switch` and just use `case _:`](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001422.html): `default` is widely used, `case _` is too magical, and `default` is widely precedented in many C family languages. * [Rename `guard` to `unless`](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005534.html): It is a common request that `guard` be renamed `unless`. People requesting this change argue that `guard` is simply a logically inverted `if` statement, and therefore `unless` is a more obvious keyword. However, such requests stem from a fundamental misunderstanding of the functionality provided by `guard`. Unlike `if`, `guard` *enforces* that the code within its curly braces provides an early exit from the codepath. In other words, a `guard` block **must** `return`, `throw`, `break`, `continue` or call a function that does not return, such as `fatalError()`. This differs from `if` quite significantly, and therefore the parallels assumed between `guard` and `if` are not valid. + + * [Infer `return` for omitted `guard` body](https://forums.swift.org/t/inferred-return-for-guard-statement/12099/11): It has been proposed many times to allow omission of the `guard` body for the sake of brevity. However, a core principle of Swift is to make control flow explicit and visible. For example, the `try` keyword exists solely to indicate to the human reader where thrown errors can happen. Implicit returns would violate this principle, favoring terseness over clarity in a way that isn't typical of Swift. Furthermore, there are many ways of exiting the scope other than `return` (loops may want `break` or `continue`), and not every function has an obvious default value to return. * [Change closure literal syntax](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/002583.html): Closure syntax in Swift has been carefully debated internally, and aspects of the design have strong motivations. It is unlikely that we'll find something better, and any proposals to change it should have a very detailed understanding of the Swift grammar. From 14ba31fa020376ece04c4af566034fa3803d8610 Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Tue, 10 Jul 2018 10:24:07 -0700 Subject: [PATCH 0738/4563] Add Package Manager Dependency Mirroring proposal (#873) --- ...NN-package-manager-dependency-mirroring.md | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 proposals/NNNN-package-manager-dependency-mirroring.md diff --git a/proposals/NNNN-package-manager-dependency-mirroring.md b/proposals/NNNN-package-manager-dependency-mirroring.md new file mode 100644 index 0000000000..3b216e29bf --- /dev/null +++ b/proposals/NNNN-package-manager-dependency-mirroring.md @@ -0,0 +1,85 @@ +# Package Manager Dependency Mirroring + +* Proposal: [SE-NNNN](NNNN-package-manager-dependency-mirroring.md) +* Authors: [Ankit Aggarwal](https://github.com/aciidb0mb3r) +* Review Manager: [Boris Bügling](https://github.com/neonichu) +* Status: **In Review** + +## Introduction + +A dependency mirror refers to an alternate source location which exactly replicates the contents of the original source. This is a proposal for adding support for dependency mirroring in SwiftPM. + +## Motivation + +Dependency mirroring is useful for several reasons: + +- **Availability**: Mirrors can ensure that a dependency can be always fetched, in case the original source is unavailable or even deleted. +- **Cache**: Access to the original source location could be slow or forbidden in the current environment. +- **Validation**: Mirrors can help with screening the upstream updates before making them available internally within a company. + +## Proposed solution + +We propose to introduce a "package configuration" file to store per-dependency mirroring information that SwiftPM can use as additional input. + +We propose to allow registering a mirror using the following command: + +```sh +$ swift package config set-mirror \ + --package-url \ + --mirror-url + +# Example: + +$ swift package config set-mirror \ + --package-url https://github.com/Core/libCore.git \ + --mirror-url https://mygithub.com/myOrg/libCore.git +``` + +A dependency's mirror URL will be used instead of its original URL to perform all relevant git operations, such as fetching and updating the dependency. It will be possible to mirror both direct and transitive dependencies of a package. + +## Detailed design + +### Package Configuration File + +The package configuration file will be expected at this location: + + /.swiftpm/config + +Similar to the `Package.resolved` file, the configuration file of a dependency will not affect a top-level package. + +This file will be managed through SwiftPM commands and users are not expected to edit it by hand. The format of this file is an implementation detail but it will be JSON in practice. + +The configuration file can be expanded to add other information if it makes sense to add it there. Other tools and IDEs written on top of SwiftPM, can also use the `.swiftpm` directory to store their auxiliary files. + +### Dependency Mirroring + +In addition to the `set-mirror` command described above, SwiftPM will provide a command to unset mirror URLs: + +```sh +$ swift package config unset-mirror \ + (--mirror-url | --package-url | --all) + +# Examples: + +$ swift package config unset-mirror --package-url https://github.com/Core/libCore.git +$ swift package config unset-mirror --mirror-url https://mygithub.com/myOrg/libCore.git +$ swift package config unset-mirror --all +``` + +A dependency can have only one mirror URL at a time; `set-mirror` command will replace any previous mirror URL for that dependency. + +SwiftPM will allow overriding the path of the configuration file using the environment variable `SWIFTPM_MIRROR_CONFIG`. This allows using mirrors on arbitrary packages that don't have a config file or require different configurations in different environments. Note that the file at this variable will override only the mirror configuration, if in future we have other configuration stored in the configuration file. + +The `Package.resolved` file will contain the mirror URLs that were used during dependency resolution. + +## Impact on existing packages + +This is an additive feature and doesn't impact existing packages. + +## Alternatives considered + +We considered using a dedicated file for storing mirror information. However, there is no good reason to have a new file specifically for mirrors. A generic file gives us flexibility if we discover the need to store more configuration. + +We considered adding a global configuration file for storing the mirror information. A global file could be convenient for some users, but it can also cause "gotcha" moments if the file is used when it shouldn't be used and vice versa. Users who understand this risk can export the `SWIFTPM_MIRROR_CONFIG` in their shell to achieve a similar effect. + +We considered storing the mirroring information in the `Package.swift` manifest file but that doesn't fit well with several of the use-cases of mirrors. The manifest file is also fundamentally different from the configuration file. The manifest file defines how a package is configured and built, whereas the mirror configuration provides overrides for fetching the package dependencies. For e.g., different users would want to use different mirror files depending on their environment, or a user may want to use a mirror on a package they don't have permission to edit. From e7a3caec95ff408bc8462786d7289ebe490b1288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Tue, 10 Jul 2018 19:25:36 +0200 Subject: [PATCH 0739/4563] Update and rename NNNN-package-manager-dependency-mirroring.md to 0219-package-manager-dependency-mirroring.md --- ...roring.md => 0219-package-manager-dependency-mirroring.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{NNNN-package-manager-dependency-mirroring.md => 0219-package-manager-dependency-mirroring.md} (97%) diff --git a/proposals/NNNN-package-manager-dependency-mirroring.md b/proposals/0219-package-manager-dependency-mirroring.md similarity index 97% rename from proposals/NNNN-package-manager-dependency-mirroring.md rename to proposals/0219-package-manager-dependency-mirroring.md index 3b216e29bf..e0c6f01002 100644 --- a/proposals/NNNN-package-manager-dependency-mirroring.md +++ b/proposals/0219-package-manager-dependency-mirroring.md @@ -1,9 +1,9 @@ # Package Manager Dependency Mirroring -* Proposal: [SE-NNNN](NNNN-package-manager-dependency-mirroring.md) +* Proposal: [SE-0219](0219-package-manager-dependency-mirroring.md) * Authors: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **In Review** +* Status: **Active review (07 10...07 17)** ## Introduction From 1f4fedd9db97cde42445456a50e3a3fc9a41f9ac Mon Sep 17 00:00:00 2001 From: Thiago Holanda Date: Tue, 10 Jul 2018 19:37:40 +0200 Subject: [PATCH 0740/4563] fix active review date format from proposal SE-0219 --- proposals/0219-package-manager-dependency-mirroring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0219-package-manager-dependency-mirroring.md b/proposals/0219-package-manager-dependency-mirroring.md index e0c6f01002..f34810b538 100644 --- a/proposals/0219-package-manager-dependency-mirroring.md +++ b/proposals/0219-package-manager-dependency-mirroring.md @@ -3,7 +3,7 @@ * Proposal: [SE-0219](0219-package-manager-dependency-mirroring.md) * Authors: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **Active review (07 10...07 17)** +* Status: **Active review (July 10...17)** ## Introduction From f3dad07643ee34880119961dddce0d2a2b197ff1 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 10 Jul 2018 13:40:01 -0700 Subject: [PATCH 0741/4563] [SE-0213] Update link to the implementation PR --- proposals/0213-literal-init-via-coercion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0213-literal-init-via-coercion.md b/proposals/0213-literal-init-via-coercion.md index 83519a0a87..a00570de05 100644 --- a/proposals/0213-literal-init-via-coercion.md +++ b/proposals/0213-literal-init-via-coercion.md @@ -4,7 +4,7 @@ * Authors: [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Accepted** -* Implementation: [apple/swift#15311](https://github.com/apple/swift/pull/15311) +* Implementation: [apple/swift#15311](https://github.com/apple/swift/pull/17860) ## Introduction From 65b86d4d642af2a9e81b802752bb3f7fcf026d19 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 10 Jul 2018 23:37:07 +0100 Subject: [PATCH 0742/4563] [SE-0213] Update link to the implementation PR --- proposals/0213-literal-init-via-coercion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0213-literal-init-via-coercion.md b/proposals/0213-literal-init-via-coercion.md index a00570de05..f5cdb0e0ba 100644 --- a/proposals/0213-literal-init-via-coercion.md +++ b/proposals/0213-literal-init-via-coercion.md @@ -4,7 +4,7 @@ * Authors: [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Accepted** -* Implementation: [apple/swift#15311](https://github.com/apple/swift/pull/17860) +* Implementation: [apple/swift#17860](https://github.com/apple/swift/pull/17860) ## Introduction From 8fce12ae15fbe8a3687238304b91959864da58fc Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Tue, 10 Jul 2018 16:28:49 -0700 Subject: [PATCH 0743/4563] [SE-0219] Add security section --- proposals/0219-package-manager-dependency-mirroring.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/0219-package-manager-dependency-mirroring.md b/proposals/0219-package-manager-dependency-mirroring.md index f34810b538..ed9cfd00a7 100644 --- a/proposals/0219-package-manager-dependency-mirroring.md +++ b/proposals/0219-package-manager-dependency-mirroring.md @@ -72,6 +72,10 @@ SwiftPM will allow overriding the path of the configuration file using the envir The `Package.resolved` file will contain the mirror URLs that were used during dependency resolution. +## Security + +There is no security impact since mirrors only work for the top-level package, and dependencies can't add mirrors on downstream packages. There is a potential privacy concern in case someone accidentally commits their private mirror configuration file in a public package. + ## Impact on existing packages This is an additive feature and doesn't impact existing packages. From 004bf065f4dbd4bbc9b1b8ff0ba15450906a4f36 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 14 Jul 2018 09:30:47 +0100 Subject: [PATCH 0744/4563] [SE-0218] Change to Accepted, add Decision Notes --- proposals/0218-introduce-compact-map-values.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0218-introduce-compact-map-values.md b/proposals/0218-introduce-compact-map-values.md index 0d8bfccb3b..d4c903069f 100644 --- a/proposals/0218-introduce-compact-map-values.md +++ b/proposals/0218-introduce-compact-map-values.md @@ -3,8 +3,9 @@ * Proposal: [SE-0218](0218-introduce-compact-map-values.md) * Author: [Daiki Matsudate](https://github.com/d-date) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active review (July 5...11)** +* Status: **Accepted** * Implementation: [apple/swift#15017](https://github.com/apple/swift/pull/15017) +* Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0218-introduce-compactmapvalues-to-dictionary/14448) ## Introduction From d69097a10e500d7356d01f72862072869ff56589 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Sat, 14 Jul 2018 06:29:32 -0700 Subject: [PATCH 0745/4563] Accept SE-0215 --- .../0215-conform-never-to-hashable-and-equatable.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/proposals/0215-conform-never-to-hashable-and-equatable.md b/proposals/0215-conform-never-to-hashable-and-equatable.md index 2fbb1706a1..4ee683ddf1 100644 --- a/proposals/0215-conform-never-to-hashable-and-equatable.md +++ b/proposals/0215-conform-never-to-hashable-and-equatable.md @@ -3,7 +3,8 @@ * Proposal: [SE-0215](0215-conform-never-to-hashable-and-equatable.md) * Author: [Matt Diephouse](https://github.com/mdiep) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active review (June 12...19)** +* Status: **Accepted** +* Decision Notes: [Rationale](https://forums.swift.org/t/se-0215-conform-never-to-equatable-and-hashable/13586/45) * Implementation: [apple/swift#16857](https://github.com/apple/swift/pull/16857) ## Introduction @@ -78,3 +79,9 @@ extension Result: Equatable where Value == Never, Error == Never { ``` Adding so many extra conditional conformances is an unreasonable amount of work. + +### Amendment from Core Team + +As part of the [review decision](https://forums.swift.org/t/se-0215-conform-never-to-equatable-and-hashable/13586/45) from the Core Team +when accepting this proposal, in addition to `Equatable` and `Hashable` conformances being added to `Never` this proposal +now also includes adding conformances to the `Comparable` and `Error` protocols as well. From d94781084c4a26d73d2051f4031adca765938d42 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Sat, 14 Jul 2018 07:04:09 -0700 Subject: [PATCH 0746/4563] SE-217 has been rejected. --- proposals/0217-bangbang.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0217-bangbang.md b/proposals/0217-bangbang.md index fe2b0139e7..dfa086b074 100644 --- a/proposals/0217-bangbang.md +++ b/proposals/0217-bangbang.md @@ -3,7 +3,8 @@ * Proposal: [SE-0217](0217-bangbang.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Dave DeLong](https://github.com/davedelong), [Paul Cantrell](https://github.com/pcantrell), [Erica Sadun](https://github.com/erica), and several other folk * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Active Review (June 30...July 7, 2018)** +* Status: **Rejected** +* Decision Notes: [Rationale](https://forums.swift.org/t/se-0217-the-unwrap-or-die-operator/14107/222) ## Introduction From dfeabac90660317cc639018f173522c6e866674f Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 16 Jul 2018 15:58:07 +0300 Subject: [PATCH 0747/4563] Create optional-iteration.md --- proposals/optional-iteration.md | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 proposals/optional-iteration.md diff --git a/proposals/optional-iteration.md b/proposals/optional-iteration.md new file mode 100644 index 0000000000..93f050a347 --- /dev/null +++ b/proposals/optional-iteration.md @@ -0,0 +1,71 @@ +# Optional Iteration + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Anthony Latsis](https://github.com/AnthonyLatsis) +* Review Manager: TBD +* Status: **Awaiting implementation** + +## Introduction + +Optionals are a key feature of Swift; they comprise a concise and elegant syntax that serves a great means of brevity +when it comes to expressing "do something if there's a value, skip otherwise". +Thereby, we have a powerful tool that seamlessly interacts with code. Some vivid examples are optional chaining, +optional invocation `foo?()` and even `if let`. This proposal considers further supporting this convenience in `for-in` loops. + +Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/another-try-at-allowing-optional-iteration/14376?u=anthonylatsis) + +## Motivation + +Loops are indeed a common statement. When working with optional sequences, a possibility to optionally iterate +(that is, iterate if there is a value, otherwise skip) is self-explanatory. However, Swift currently doesn't offer a way to express +this 'natively', in the language of optionals. Optional sequences are illegal as a `for-in` loop attribute. The most common and correct way of putting it, +especially when we need to handle the `nil` case (`else`) is + +```swift +if let sequence = optionalSequence { + for element in sequence { ... } +} // else { ... } +``` + +Alternative workarounds include `?? []` (for `Array`) and `sequence?.forEach`. + +The bottom line being, if we don't require `else`, why not say `for? element in optionalSequence { ... }` ? + +## Proposed solution + +Optional `for-in` loops and the possibility to use optional sequences therein. The `?` notation, however, will be a semantic +emphasys rather than a functional syntactic unit. There will be no `for!`. The latter is redundant, but this decision was primarily +made based on the potential confusion that an otherwise left without syntactic changes `for-in` loop could lead to confusion +("clarity over brevity"). The `?`, in fact, is not necessary: the sequence can be force-unwrapped if needed or left as-is +without requiring addition syntax. + +``` swift +var array: [Int]? = [1, 2, 3] + +for element in array { ... } // An optional loop with exactly the same syntax is considered a source of confusion + +for? element in array { ... } + +``` + +## Detailed design + +An optional `for-in` loop over a nil sequence does nothing. Otherwise, it iterates normally. The `?` notation in `for?` is +required if the passed sequence is optional. Roughly, one can imagine an optional `for-in` loop as `sequence?.forEach`. + +With a yet rather vague picture of the implementation, I assume one of the options is to enclose an optional loop in an `if-let` +statement during sil-gen. + +## Source compatibility + +This feature is purely additive and hence does not imply source-breaking changes. +Usage is context-sensitive and migration should be up to the user. + +## Effect on ABI stability + +This feature likely changes the code generation model. + +## Alternatives considered + +An similar approach was to leave out any syntactic changes. The downsides are briefly explained in the +(Proposed solution)[proposed-solution] section. From fddbe507fda9b96706e9bc82c10736169070f353 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 16 Jul 2018 16:33:43 +0300 Subject: [PATCH 0748/4563] Update optional-iteration.md --- proposals/optional-iteration.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/proposals/optional-iteration.md b/proposals/optional-iteration.md index 93f050a347..79694190a2 100644 --- a/proposals/optional-iteration.md +++ b/proposals/optional-iteration.md @@ -35,7 +35,7 @@ The bottom line being, if we don't require `else`, why not say `for? element in Optional `for-in` loops and the possibility to use optional sequences therein. The `?` notation, however, will be a semantic emphasys rather than a functional syntactic unit. There will be no `for!`. The latter is redundant, but this decision was primarily -made based on the potential confusion that an otherwise left without syntactic changes `for-in` loop could lead to confusion +made based on the potential confusion that an otherwise left without syntactic changes `for-in` loop could lead to ("clarity over brevity"). The `?`, in fact, is not necessary: the sequence can be force-unwrapped if needed or left as-is without requiring addition syntax. @@ -53,8 +53,7 @@ for? element in array { ... } An optional `for-in` loop over a nil sequence does nothing. Otherwise, it iterates normally. The `?` notation in `for?` is required if the passed sequence is optional. Roughly, one can imagine an optional `for-in` loop as `sequence?.forEach`. -With a yet rather vague picture of the implementation, I assume one of the options is to enclose an optional loop in an `if-let` -statement during sil-gen. +With a yet rather vague picture of the implementation, I assume one of the options is to enclose an optional loop in an `if-let` statement during sil-gen. ## Source compatibility @@ -67,5 +66,5 @@ This feature likely changes the code generation model. ## Alternatives considered -An similar approach was to leave out any syntactic changes. The downsides are briefly explained in the -(Proposed solution)[proposed-solution] section. +A similar approach was to leave out any syntactic changes. The downside is briefly explained in the +[Proposed solution](#proposed-solution) section. From 9ffc6fadad724efa7d321c6a73dcdad1ad7edd11 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 16 Jul 2018 23:31:03 +0300 Subject: [PATCH 0749/4563] Update optional-iteration.md --- proposals/optional-iteration.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/optional-iteration.md b/proposals/optional-iteration.md index 79694190a2..10f0807ef3 100644 --- a/proposals/optional-iteration.md +++ b/proposals/optional-iteration.md @@ -9,8 +9,7 @@ Optionals are a key feature of Swift; they comprise a concise and elegant syntax that serves a great means of brevity when it comes to expressing "do something if there's a value, skip otherwise". -Thereby, we have a powerful tool that seamlessly interacts with code. Some vivid examples are optional chaining, -optional invocation `foo?()` and even `if let`. This proposal considers further supporting this convenience in `for-in` loops. +Thereby, we have a powerful tool that seamlessly interacts with code. Some vivid examples are optional chaining with assignments, optional invocation `foo?()` and even `if let`. This proposal considers further supporting this convenience in `for-in` loops. Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/another-try-at-allowing-optional-iteration/14376?u=anthonylatsis) From 170b79fdc2dd8642f32415737804d64d49c2913c Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 16 Jul 2018 20:37:47 -0400 Subject: [PATCH 0750/4563] Amend 0202 (#879) --- proposals/0202-random-unification.md | 75 +++++++++++++++------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/proposals/0202-random-unification.md b/proposals/0202-random-unification.md index b8667302d1..81744c6ea4 100644 --- a/proposals/0202-random-unification.md +++ b/proposals/0202-random-unification.md @@ -90,31 +90,31 @@ To kick this off, the standard library will provide a default RNG. Each platform For the core API, introduce a new protocol named `RandomNumberGenerator`. This type is used to define RNGs that can be used within the stdlib. Developers can conform to this type and use their own custom RNG throughout their whole application. -Then for the stdlib's default RNG implementation, introduce a new struct named `Random`. This struct contains a singleton called `default` which provides access to the methods of `RandomNumberGenerator`. +Then for the stdlib's default RNG implementation, introduce a new struct named `SystemRandomNumberGenerator`. Next, we will make extension methods for `FixedWidthInteger`, `BinaryFloatingPoint` and `Bool`. For numeric types, this allows developers to select a value within a range and swap out the RNG used to select a value within the range. `FixedWidthInteger` example: ```swift // Utilizes the standard library's default random -/// (alias to Int.random(in: 0 ..< 10, using: &Random.default)) +// Alias to: +// var rng = SystemRandomNumberGenerator() +// Int.random(in: 0 ..< 10, using: &rng) let randomIntFrom0To10 = Int.random(in: 0 ..< 10) let randomUIntFrom10Through100 = UInt.random(in: 10 ... 100, using: &myCustomRandomNumberGenerator) // The following are examples on how to get full width integers let randomInt = Int.random(in: .min ... .max) - -// an alternative spelling could be: -// let randomUInt = myCustomRandomNumberGenerator.next() -// this takes advantage of the fact that generators can produce unsigned integers let randomUInt = UInt.random(in: .min ... .max, using: &myCustomRandomNumberGenerator) ``` `BinaryFloatingPoint` example: ```swift // Utilizes the standard library's default random -// (alias to Float.random(in: 0 ..< 1, using: &Random.default)) +// Alias to: +// var rng = SystemRandomNumberGenerator() +// Float.random(in: 0 ..< 1, using: &rng) let randomFloat = Float.random(in: 0 ..< 1) let randomDouble = Double.random(in: 0 ... .pi, using: &myCustomRandomNumberGenerator) ``` @@ -122,7 +122,9 @@ let randomDouble = Double.random(in: 0 ... .pi, using: &myCustomRandomNumberGene `Bool` example: ```swift // Utilizes the standard library's default random -// (alias to Bool.random(using: &Random.default)) +// Alias to: +// var rng = SystemRandomNumberGenerator() +// Bool.random(using: &rng) let randomBool1 = Bool.random() let randomBool2 = Bool.random(using: &myCustomRandomNumberGenerator) ``` @@ -138,7 +140,9 @@ For `Collection` we add an extension method for collections to get a random elem let greetings = ["hey", "hi", "hello", "hola"] // Utilizes the standard library's default random -// (alias to greetings.randomElement(using: &Random.default)) +// Alias to: +// var rng = SystemRandomNumberGenerator() +// greetings.randomElement(using: &rng)! print(greetings.randomElement()!) // This returns an Optional print(greetings.randomElement(using: &myCustomRandomNumberGenerator)!) // This returns an Optional ``` @@ -158,7 +162,9 @@ As a result of adding the random API, it only makes sense to utilize that power var greetings = ["hey", "hi", "hello", "hola"] // Utilizes the standard library's default random -// (alias to greetings.shuffle(using: &Random.default)) +// Alias to: +// var rng = SystemRandomNumberGenerator() +// greetings.shuffle(using: &rng) greetings.shuffle() print(greetings) // A possible output could be ["hola", "hello", "hey", "hi"] @@ -188,15 +194,9 @@ extension RandomNumberGenerator { } // The stdlib RNG. -public struct Random : RandomNumberGenerator { - // Public facing API - public static var `default`: Random { - get { return Random() } - set { /* Discard */ } - } - - // Prevents initialization of this struct - private init() {} +public struct SystemRandomNumberGenerator : RandomNumberGenerator { + + public init() {} // Conformance for `RandomNumberGenerator`, calls one of the crypto functions. public mutating func next() -> UInt64 @@ -215,7 +215,8 @@ extension Collection { /// Uses the standard library's default RNG public func randomElement() -> Element? { - return randomElement(using: &Random.default) + var g = SystemRandomNumberGenerator() + return randomElement(using: &g) } } @@ -225,9 +226,7 @@ extension Collection { // For those that are that unsure whether or not their range is empty or not, // they can if/guard check whether or not the range is empty beforehand, then // use these functions. -extension FixedWidthInteger -where Self.Stride : SignedInteger, - Self.Magnitude : UnsignedInteger { +extension FixedWidthInteger { public static func random( in range: Range, @@ -236,7 +235,8 @@ where Self.Stride : SignedInteger, /// Uses the standard library's default RNG public static func random(in range: Range) -> Self { - return Self.random(in: range, using: &Random.default) + var g = SystemRandomNumberGenerator() + return Self.random(in: range, using: &g) } public static func random( @@ -246,7 +246,8 @@ where Self.Stride : SignedInteger, /// Uses the standard library's default RNG public static func random(in range: ClosedRange) -> Self { - return Self.random(in: range, using: &Random.default) + var g = SystemRandomNumberGenerator() + return Self.random(in: range, using: &g) } } @@ -256,10 +257,7 @@ where Self.Stride : SignedInteger, // For those that are that unsure whether or not their range is empty or not, // they can simply if/guard check the bounds to make sure they can correctly form // ranges which a random number can be formed from. -extension BinaryFloatingPoint -where Self.RawSignificand : FixedWidthInteger, - Self.RawSignificand.Stride : SignedInteger & FixedWidthInteger, - Self.RawSignificand.Magnitude : UnsignedInteger { +extension BinaryFloatingPoint where Self.RawSignificand : FixedWidthInteger { public static func random( in range: Range, @@ -268,7 +266,8 @@ where Self.RawSignificand : FixedWidthInteger, /// Uses the standard library's default RNG public static func random(in range: Range) -> Self { - return Self.random(in: range, using: &Random.default) + var g = SystemRandomNumberGenerator() + return Self.random(in: range, using: &g) } public static func random( @@ -278,7 +277,8 @@ where Self.RawSignificand : FixedWidthInteger, /// Uses the standard library's default RNG public static func random(in range: ClosedRange) -> Self { - return Self.random(in: range, using: &Random.default) + var g = SystemRandomNumberGenerator() + return Self.random(in: range, using: &g) } } @@ -294,7 +294,8 @@ extension Bool { /// Uses the standard library's default RNG public static func random() -> Bool { - return Bool.random(using: &Random.default) + var g = SystemRandomNumberGenerator() + return Bool.random(using: &g) } } @@ -309,7 +310,8 @@ extension Sequence { /// Uses the standard library's default RNG public func shuffled() -> [Element] { - return shuffled(using: &Random.default) + var g = SystemRandomNumberGenerator() + return shuffled(using: &g) } } @@ -320,7 +322,8 @@ extension MutableCollection { /// Uses the standard library's default RNG public mutating func shuffle() { - shuffle(using: &Random.default) + var g = SystemRandomNumberGenerator() + shuffle(using: &g) } } ``` @@ -379,6 +382,6 @@ There were a bit of discussion for and against this. Initially I was on board wi This was a very heavily discussed topic that we can't skip over. -I think we came into agreement that `range.random()` should be possible, however the discussion was around whether or not this is the primary spelling for getting random numbers. Having a range as the primary spelling makes it fairly simple to get a random number from. Ranges are also very desirable because it doesn't encourage modulo bias. Also, since we decided pretty early on that we're going to trap if for whatever reason we can't get a random number, this gave `range.random()` the excuse to return a non optional. +I think we came into agreement that `range.randomElement()` should be possible, however the discussion was around whether or not this is the primary spelling for getting random numbers. Having a range as the primary spelling makes it fairly simple to get a random number from. Ranges are also very desirable because it doesn't encourage modulo bias. Also, since we decided pretty early on that we're going to trap if for whatever reason we can't get a random number, this gave `range.randomElement()` the excuse to return a non optional. -On the other end of the spectrum, we came into early agreement that `Collection.random()` needs to return an optional in the case of an empty collection. If ranges were the primary spelling, then we would need to create exceptions for them to return non optionals. This would satisfy the general use design, but as we agreed that `.random()` behaves more like `.first`, `.last`, `.min()`, and `.max()`. Because of this, `.random()` has to return an optional to keep the consistent semantics. This justifies the static functions on the numeric types as the primary spelling as they can be the ones to return non optionals. These static functions are also the spelling for how developers think about going about random numbers. "Ok, I need a random integer from x and y." This helps give these functions the upper hand in terms of discoverability. +On the other end of the spectrum, we came into early agreement that `Collection.randomElement()` needs to return an optional in the case of an empty collection. If ranges were the primary spelling, then we would need to create exceptions for them to return non optionals. This would satisfy the general use design, but as we agreed that `.randomElement()` behaves more like `.first`, `.last`, `.min()`, and `.max()`. Because of this, `.randomElement()` has to return an optional to keep the consistent semantics. This justifies the static functions on the numeric types as the primary spelling as they can be the ones to return non optionals. These static functions are also the spelling for how developers think about going about random numbers. "Ok, I need a random integer from x and y." This helps give these functions the upper hand in terms of discoverability. From 5e324eb4b29f6764880d7727c7754e1d5df19660 Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Mon, 16 Jul 2018 20:32:06 -0700 Subject: [PATCH 0751/4563] Converted pitch to proposal format --- proposals/nnnn-binaryinteger-iseven-isodd.md | 121 +++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 proposals/nnnn-binaryinteger-iseven-isodd.md diff --git a/proposals/nnnn-binaryinteger-iseven-isodd.md b/proposals/nnnn-binaryinteger-iseven-isodd.md new file mode 100644 index 0000000000..a53fd95731 --- /dev/null +++ b/proposals/nnnn-binaryinteger-iseven-isodd.md @@ -0,0 +1,121 @@ +# Adding `isEven` and `isOdd` properties to `BinaryInteger` + +* Proposal: [SE-NNNN](NNNN-binaryinteger-iseven-isodd.md) +* Authors: [Robert MacEachern](https://robmaceachern.com), [SiliconUnicorn](https://forums.swift.org/u/siliconunicorn/summary) +* Review Manager: TBD +* Status: **Awaiting implementation** + +## Introduction + +We propose adding `var isEven: Bool` and `var isOdd: Bool` to `BinaryInteger`. These are convenience properties for querying the [parity](https://en.wikipedia.org/wiki/Parity_(mathematics)) of the integer. + +Swift-evolution thread: [Even and Odd Integers](https://forums.swift.org/t/even-and-odd-integers/11774) + +## Motivation + +It is sometimes useful to know the evenness or oddness (parity) of an integer and switch the behaviour based on the result. The most typical way to do this is using `value % 2 == 0` to determine if `value` is even, or `value % 2 != 0` to determine if `value` is odd. + +```swift +// Gray background for even rows, white for odd. +view.backgroundColor = indexPath.row % 2 == 0 ? .gray : .white + +// Enable button if we have odd number of photos +buttonSave.isEnabled = photos.count % 2 != 0 +``` + +It is also possible to use the bitwise AND operator (`value & 1 == 0`) which will inevitably lead to discussions about which one is faster and attempts at profiling them, etc, etc. + +There are a few more specific motivations for this proposal: + +### Commonality + +The need to determine the parity of an integer isn’t restricted to a limited problem domain. + +### Readability + +This proposal significantly improves readability. There is no need to understand operator precedence rules (`%` has higher precedence than `==`) which are non-obvious. + +The properties are also fewer characters wide than the modulus approach (maximum 7 characters for `.isEven` vs 9 for ` % 2 == 0`) which saves horizontal space while being clearer in intent. +```swift +view.backgroundColor = indexPath.row % 2 == 0 ? .gray : .white +view.backgroundColor = indexPath.row.isEven ? .gray : .white + +buttonSave.isEnabled = photos.count % 2 != 0 +buttonSave.isEnabled = photos.count.isOdd +``` + +### Discoverability + +Determining whether a value is even or odd is a common question across programming languages, at least based on these Stack Overflow questions: +[c - How do I check if an integer is even or odd?](https://stackoverflow.com/questions/160930/how-do-i-check-if-an-integer-is-even-or-odd) 300,000+ views +[java - Check whether number is even or odd](https://stackoverflow.com/questions/7342237/check-whether-number-is-even-or-odd) 350,000+ views +[Check if a number is odd or even in python](https://stackoverflow.com/questions/21837208/check-if-a-number-is-odd-or-even-in-python) 140,000+ views + +IDEs will be able to suggest `.isEven` and `.isOdd` as part of autocomplete which will aid discoverability. + +### Consistency + +It would be relatively easy to reproduce the properties in user code but there would be benefits to having a standard implementation. It may not be obvious to some users exactly which protocol these properties belong on (`Int`?, `SignedInteger`?, `FixedWidthInteger`?, `BinaryInteger`?). This inconsistency can be seen in a [popular Swift utility library](https://github.com/SwifterSwift/SwifterSwift/blob/master/Sources/Extensions/SwiftStdlib/SignedIntegerExtensions.swift#L28) which defines these properties on `SignedInteger` which results in the properties being inaccessible for unsigned integers. + +These properties will also eliminate the need to use modulus 2 and bitwise AND 1 to determine parity. + +Adding `isEven` and `isOdd` is also consistent with the `.isEmpty` utility property, which is a convenience for `.count == 0`. +```swift +if array.count == 0 { ... } +if array.isEmpty { ... } + +if value % 2 == 0 { ... } +if value.isEven { ... } +``` + +### Correctness + +There is a minor correctness risk in misinterpreting something like `value % 2 == 0`, particularly when used in a more complex statement, when compared to `value.isEven`. + +### Performance + +This proposal likely won’t have a major positive impact on performance but it should not introduce any additional overhead thanks to `@inlineable`. + +## Proposed solution + +Add two computed properties, `isEven` and `isOdd`, to `BinaryInteger` + +```swift +extension BinaryInteger { + @inlinable + /// A Boolean value indicating whether this value is even. + /// + /// An integer is even if it is evenly divisible by two. + public var isEven: Bool { + return self % 2 == 0 + } + + @inlinable + /// A Boolean value indicating whether this value is odd. + /// + /// An integer is odd if it is not evenly divisible by two. + public var isOdd: Bool { + return self % 2 != 0 + } +} +``` + +## Detailed design + +N/A + +## Source compatibility + +This is strictly additive. + +## Effect on ABI stability + +N/A + +## Effect on API resilience + +N/A + +## Alternatives considered + +`divisible(by:)` From cbf36dd5a9ca96b9c917bc53af9fe24bf5c2b488 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 18 Jul 2018 01:12:20 +0300 Subject: [PATCH 0752/4563] Update optional-iteration.md --- proposals/optional-iteration.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/optional-iteration.md b/proposals/optional-iteration.md index 10f0807ef3..1ba916c414 100644 --- a/proposals/optional-iteration.md +++ b/proposals/optional-iteration.md @@ -7,9 +7,10 @@ ## Introduction -Optionals are a key feature of Swift; they comprise a concise and elegant syntax that serves a great means of brevity -when it comes to expressing "do something if there's a value, skip otherwise". -Thereby, we have a powerful tool that seamlessly interacts with code. Some vivid examples are optional chaining with assignments, optional invocation `foo?()` and even `if let`. This proposal considers further supporting this convenience in `for-in` loops. +Optionals are a key feature of Swift and a powerful tool that seamlessly interacts with code; +they comprise an elegant and concise syntax that serves a great means of brevity when it comes to expressing +"act accordingly if there's a value, skip otherwise". +Some vivid examples are optional chaining with assignments, optional invocation `foo?()` and even `if let`. This proposal considers further supporting this convenience in `for-in` loops. Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/another-try-at-allowing-optional-iteration/14376?u=anthonylatsis) From 6bd59576705a62fb2b71d89541db3f668014ad97 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 18 Jul 2018 01:18:09 +0300 Subject: [PATCH 0753/4563] Update optional-iteration.md --- proposals/optional-iteration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/optional-iteration.md b/proposals/optional-iteration.md index 1ba916c414..52564868af 100644 --- a/proposals/optional-iteration.md +++ b/proposals/optional-iteration.md @@ -27,7 +27,7 @@ if let sequence = optionalSequence { } // else { ... } ``` -Alternative workarounds include `?? []` (for `Array`) and `sequence?.forEach`. +Alternative workarounds include `?? []` for `Array` and `sequence?.forEach`, which excludes usage of control transfer statements. The bottom line being, if we don't require `else`, why not say `for? element in optionalSequence { ... }` ? From 59635bfc95c56cbaf6afe4e9f33205a64d505c3c Mon Sep 17 00:00:00 2001 From: Soroush Khanlou Date: Fri, 20 Jul 2018 04:42:48 +0000 Subject: [PATCH 0754/4563] add a proposal for count(where:) (#840) --- proposals/XXXX-count-where.md | 70 +++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 proposals/XXXX-count-where.md diff --git a/proposals/XXXX-count-where.md b/proposals/XXXX-count-where.md new file mode 100644 index 0000000000..231723da9d --- /dev/null +++ b/proposals/XXXX-count-where.md @@ -0,0 +1,70 @@ +# `count(where:)` + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Soroush Khanlou](https://github.com/khanlou) +* Review Manager: TBD +* Status: **Awaiting implementation** + +## Introduction + +While Swift's `Sequence` models brings a lot of niceties that we didn't have access to in Objective-C, like `map` and `filter`, there are other useful operations on sequences that the standard library doesn't support yet. One current missing operation is `count(where:)`, which counts the number of elements in a `Sequence` that pass some test. + +Swift-evolution thread: [`count(where:)` on Sequence](https://forums.swift.org/t/count-where-on-sequence/11186) + +## Motivation + +Counting the number of objects that pass a test has a wide range of uses in many domains. However, Swift currently doesn't give its users a simple way to perform this operation. While the behavior can currently be approximated with a `filter` and a `count`, this approach creates an intermediate array which it immediately discards. This is a bit wasteful. + + [1, 2, 3, -1, -2].filter({ $0 > 0 }).count // => 3 + +To correctly avoid a potentially expensive intermediate array, you can use the Swift's `lazy` subsystem: + + [1, 2, 3, -1, -2].lazy.filter({ $0 > 0 }).count // => 3 + +However, using `lazy` comes with the downside of being forced to use an `@escaping` block. Lastly, you could rely on an eminently unreadable `reduce`: + + [1, 2, 3, -1, -2].reduce(0) { $1 > 0 ? $0 + 1 : $0 } + +These three solutions lie on a spectrum between "easy to write, but include performance traps" to "performant, but require Swift arcana to write". + +## Proposed solution + +The proposed solution would avoid a performance trap and provide a simple interface for users to both read and write. Autocomplete should present it to them handily as well. + + [1, 2, 3, -1, -2].count(where: { $0 > 0 }) // => 3 + +I use it as an extension in my code regularly, and I think it'd make a nice addition to the standard library. + +## Detailed design + +A reference implementation for the function is included here: + + extension Sequence { + func count(where predicate: (Element) throws -> Bool) rethrows -> Int { + var count = 0 + for element in self { + if try predicate(element) { + count += 1 + } + } + return count + } + } + +The recommended implementation can be found [in a pull request to `apple/swift`](https://github.com/apple/swift/pull/16099). + +## Source compatibility + +This change is additive only. + +## Effect on ABI stability + +This change is additive only. + +## Effect on API resilience + +This change is additive only. + +## Alternatives considered + +One alternative worth discussing is the addition of `count(of:)`, which can be implemented on sequences where `Element: Equatable`. This function returns the count of all objects that are equal to the parameter. I'm open to amending this proposal to include this function, but in practice I've never used or needed this function, so I've omitted it here. From 9af4ab07d0655e6077ab194e091dcea1d790a9d3 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Thu, 19 Jul 2018 21:44:18 -0700 Subject: [PATCH 0755/4563] brand count-where as SE-0220 --- proposals/{XXXX-count-where.md => 0220-count-where.md} | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename proposals/{XXXX-count-where.md => 0220-count-where.md} (94%) diff --git a/proposals/XXXX-count-where.md b/proposals/0220-count-where.md similarity index 94% rename from proposals/XXXX-count-where.md rename to proposals/0220-count-where.md index 231723da9d..b67b33f97c 100644 --- a/proposals/XXXX-count-where.md +++ b/proposals/0220-count-where.md @@ -1,9 +1,9 @@ # `count(where:)` -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0220](0220-count-where.md) * Authors: [Soroush Khanlou](https://github.com/khanlou) -* Review Manager: TBD -* Status: **Awaiting implementation** +* Review Manager: [Chris Lattner](https://github.com/lattner) +* Status: **Scheduled for Review August 6 - 12, 2018 ** ## Introduction @@ -68,3 +68,4 @@ This change is additive only. ## Alternatives considered One alternative worth discussing is the addition of `count(of:)`, which can be implemented on sequences where `Element: Equatable`. This function returns the count of all objects that are equal to the parameter. I'm open to amending this proposal to include this function, but in practice I've never used or needed this function, so I've omitted it here. + From 916268db7ef15250b3d842d765ed238978b9ae04 Mon Sep 17 00:00:00 2001 From: Thiago Holanda Date: Fri, 20 Jul 2018 15:10:56 +0200 Subject: [PATCH 0756/4563] fix schedule for review date format from proposal SE-0220 (#880) --- proposals/0220-count-where.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0220-count-where.md b/proposals/0220-count-where.md index b67b33f97c..b885aea179 100644 --- a/proposals/0220-count-where.md +++ b/proposals/0220-count-where.md @@ -3,7 +3,7 @@ * Proposal: [SE-0220](0220-count-where.md) * Authors: [Soroush Khanlou](https://github.com/khanlou) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Scheduled for Review August 6 - 12, 2018 ** +* Status: **Scheduled for review (August 6...12)** ## Introduction From 7df28e671e1b4ed3721afeb4371297fa3234c297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Fri, 20 Jul 2018 16:08:46 +0200 Subject: [PATCH 0757/4563] Update 0219-package-manager-dependency-mirroring.md --- proposals/0219-package-manager-dependency-mirroring.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0219-package-manager-dependency-mirroring.md b/proposals/0219-package-manager-dependency-mirroring.md index ed9cfd00a7..4caf7c6d96 100644 --- a/proposals/0219-package-manager-dependency-mirroring.md +++ b/proposals/0219-package-manager-dependency-mirroring.md @@ -3,7 +3,8 @@ * Proposal: [SE-0219](0219-package-manager-dependency-mirroring.md) * Authors: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **Active review (July 10...17)** +* Status: **Accepted** +* Bug: [SR-8328](https://bugs.swift.org/browse/SR-8328) ## Introduction From d89c05bb401611c7c2437ca6dbcccaf3680aee52 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 20 Jul 2018 15:46:50 -0500 Subject: [PATCH 0758/4563] Proposal for Array.init(unsafeUninitializedCapacity:initializingWith:) --- .../0000-array-uninitialized-initializer.md | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 proposals/0000-array-uninitialized-initializer.md diff --git a/proposals/0000-array-uninitialized-initializer.md b/proposals/0000-array-uninitialized-initializer.md new file mode 100644 index 0000000000..55f29d514e --- /dev/null +++ b/proposals/0000-array-uninitialized-initializer.md @@ -0,0 +1,240 @@ +# Accessing an Array's Uninitialized Buffer + +* Proposal: [SE-NNNN](NNNN-array-uninitialized-buffer.md) +* Author: [Nate Cook](https://github.com/natecook1000) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: *Exists as an underscored API* +* Bug: [SR-3087](https://bugs.swift.org/browse/SR-3087) + +## Introduction + +This proposal suggests a new initializer for `Array` and `ContiguousArray` +that would provide access to a newly created array's uninitialized storage buffer. + +Swift-evolution thread: [https://forums.swift.org/t/array-initializer-with-access-to-uninitialized-buffer/13689](https://forums.swift.org/t/array-initializer-with-access-to-uninitialized-buffer/13689) + +## Motivation + +Some collection operations require working on a fixed-size buffer of uninitialized memory. +For example, one O(*n*) algorithm for performing a stable partition of an array is as follows: + +1. Create a new array the same size as the original array. +2. Iterate over the original array, + copying matching elements to the beginning of the new array + and non-matching elements to the end. +3. When finished iterating, reverse the slice of non-matching elements. + +Unfortunately, the standard library provides no way to create an array +of a particular size without allocating every element, +or to copy elements to the end of an array's buffer +without initializing every preceding element. +Even if we avoid initialization by manually allocating the memory using an `UnsafeMutableBufferPointer`, +there's no way to convert that buffer into an array without copying the contents. +There simply isn't a way to implement this particular algorithm with maximum efficiency in Swift. + +## Proposed solution + +Adding a new `Array` initializer +that lets a program work with the uninitialized buffer +would fill in this missing functionality. +This new initializer takes a closure that operates on an `UnsafeMutableBufferPointer` +and an `inout` count of initialized elements. +This closure has access to the uninitialized contents +of the newly created array's storage, +and must set the intialized count of the array before exiting. + +```swift +var myArray = Array(unsafeUninitializedCapacity: 10) { buffer, initializedCount in + for x in 1..<5 { + buffer[x] = x + } + buffer[0] = 10 + initializedCount = 5 +} +// myArray == [10, 1, 2, 3, 4] +``` + +With this new initializer, it's possible to implement the stable partition +as an extension to the `Collection` protocol, without any unnecessary copies: + +```swift +func stablyPartitioned(by belongsInFirstPartition: (Element) -> Bool) -> [Element] { + let result = Array(unsafeUninitializedCapacity: count) { buffer, initializedCount in + var low = buffer.baseAddress! + var high = low + buffer.count + for element in self { + if belongsInFirstPartition(element) { + low.initialize(to: element) + low += 1 + } else { + high -= 1 + high.initialize(to: element) + } + } + + let highIndex = high - buffer.baseAddress! + buffer[highIndex...].reverse() + initializedCount = buffer.count + } + return result +} +``` + +## Detailed design + +The new initializer is added to both `Array` and `ContiguousArray`. + +```swift +/// Creates an array with the specified capacity, then calls the given +/// closure with a buffer covering the array's uninitialized memory. +/// +/// Inside the closure, set the `initializedCount` parameter to the number of +/// elements that are initialized by the closure. The memory in the range +/// `buffer[0.., + _ initializedCount: inout Int + ) throws -> Void +) rethrows +``` + +In particular, +note that the buffer is guaranteed to address only the specified capacity, +though the final array may have a capacity greater than that as an optimization. + +## Source compatibility + +This is an additive change to the standard library, +so there is no effect on source compatibility. + +## Effect on ABI stability + +This addition has no effect on ABI stability. + +## Effect on API resilience + +The additional APIs will be a permanent part of the standard library, +and will need to remain public API. + +## Alternatives considered + +### Creating an array from a buffer + +An `Array` initializer that simply converts an `UnsafeMutableBufferPointer` +into an array's backing storage seems like it would be another solution. +However, an array's storage includes information +about the count and capacity at the beginning of its buffer, +so an `UnsafeMutableBufferPointer` created from scratch isn't usable. + +### Mutable version + +A draft version of this proposal centered around +a mutating `withFullCapacityUnsafeMutableBufferPointer` method +instead of the initializer. +However, that approach poses a problem: +the buffer passed to the closure might have an unpredictable size. + +```swift +let c = myArray.capacity +myArray.withFullCapacityUnsafeMutableBufferPointer { buffer, initializedCount in + // If `myArray` is shared, the storage gets cloned before here, so + // `buffer.count` may be greater than, less than, or equal to `c`. +} + +myArray.reserveCapacity(100) +myArray.withFullCapacityUnsafeMutableBufferPointer { buffer, initializedCount in + // `buffer.count` may be greater than 100 due to padding + // or when `myArray.count` is greater than 100. +} +``` + +Trying to solve this problem by providing +an explicit capacity as a parameter to the mutating method +leads to even more verbose and potentially confusing names, +without entirely removing the unpredictability. +An API with these characteristics seems quite difficult to understand and use properly. + +### Naming considerations + +There are two important details of this API that led to the proposed spelling. +First, the initializer is *unsafe*, +in that the user must be sure to properly manage the memory +addressed by the closure's buffer pointer parameter. +Second, the initializer provides access to the array's *uninitialized* storage, +unlike the other `Array.withUnsafe...` methods that already exist. +Because trailing closures are commonly used, +it's important to include those terms in the initial argument label, +such that they're always visible at the use site. + +This proposal leaves out wording that would reference two other relevant concepts: + +- *reserving capacity*: +Arrays currently have a `reserveCapacity(_:)` method, +which is somewhat akin to the first step of the initializer. +However, that method is used for the sake of optimizing performance when adding to an array, +rather than providing direct access to the array's capacity. +In fact, as part of the `RangeReplaceableCollection` protocol, +that method doesn't even require any action to be taken by the targeted type. +For those reasons, +the idea of "reserving" capacity doesn't seem as appropriate +as providing a specific capacity that will be used. + +- *unmanaged*: +The proposed initializer is unusual in that it converts +the lifetime management of manually initialized instances to be automatically managed, +as elements of an `Array` instance. +The only other type that performs this kind of conversion is `Unmanaged`, +which is primarily used at the border of Swift and C interoperability, +particularly with Core Foundation. +Additionally, `Unmanaged` can be used to maintain and manage the lifetime of an instance +over a long period of time, +while this initializer performs the conversion as soon as the closure executes. +As above, this term doesn't seem appropriate for use with this new API. + + +## Addendum + +You can Try This At Home™ with this extension, +which provides the semantics +(but not the performance benefits) +of the proposed initializer: + +```swift +extension Array { + public init( + unsafeUninitializedCapacity: Int, + initializingWith initializer: ( + _ buffer: inout UnsafeMutableBufferPointer, + _ initializedCount: inout Int + ) throws -> Void + ) rethrows { + var buffer = UnsafeMutableBufferPointer.allocate(capacity: unsafeUninitializedCapacity) + defer { buffer.deallocate() } + var initializedCount = 0 + try initializer(&buffer, &initializedCount) + self = Array(buffer[0.. Date: Fri, 20 Jul 2018 14:23:44 -0700 Subject: [PATCH 0759/4563] [SE-0213] Mark proposal as implemented --- proposals/0213-literal-init-via-coercion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0213-literal-init-via-coercion.md b/proposals/0213-literal-init-via-coercion.md index f5cdb0e0ba..d8d086dad6 100644 --- a/proposals/0213-literal-init-via-coercion.md +++ b/proposals/0213-literal-init-via-coercion.md @@ -3,7 +3,7 @@ * Proposal: [SE-0213](0213-literal-init-via-coercion.md) * Authors: [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5)** * Implementation: [apple/swift#17860](https://github.com/apple/swift/pull/17860) ## Introduction From 903e19fd9fc68c88ccd6d8b1ddca9f6665d8b17d Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Mon, 23 Jul 2018 10:16:53 -0700 Subject: [PATCH 0760/4563] Character Properties (#847) * Character properties proposal * Kick off review --- proposals/0221-character-properties.md | 342 +++++++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 proposals/0221-character-properties.md diff --git a/proposals/0221-character-properties.md b/proposals/0221-character-properties.md new file mode 100644 index 0000000000..8483684c6b --- /dev/null +++ b/proposals/0221-character-properties.md @@ -0,0 +1,342 @@ +# Character Properties +* Proposal: [SE-0221](0221-character-properties.md) +* Authors: [Michael Ilseman](https://github.com/milseman), [Tony Allevato](https://github.com/allevato) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **In review (July 23...29)** +* Implementation: https://github.com/apple/swift/pull/15880 + +## Introduction + +@allevato (a co-author here) proposed [Add Unicode Properties to Unicode.Scalar](https://github.com/apple/swift-evolution/blob/master/proposals/0211-unicode-scalar-properties.md), which exposes Unicode properties from the [Unicode Character Database](http://unicode.org/reports/tr44/). These are Unicode expert/enthusiast oriented properties that give a finer granularity of control and answer highly-technical and specific Unicody enquiries. + +However, they are not ergonomic and Swift makes no attempt to clarify their interpretation or usage: meaning and proper interpretation is directly tied to the Unicode Standard and the version of Unicode available at run time. There’s some low-hanging ergo-fruit ripe for picking by exposing properties directly on `Character`. + +Pitch thread: [Character and String properties](https://forums.swift.org/t/pitch-character-and-string-properties/11620) + +## Motivation + +`String` is a collection whose element is `Character`, which represents an [extended grapheme cluster](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (commonly just called “grapheme”). This makes `Character` one of the first types encountered both by newcomers to Swift as well as by experienced Swift developers playing around in new domains (e.g. scripting). Yet `Character` exposes little functionality other than the ability to order it with respect to other characters, and a way to access the raw [Unicode scalar values](https://unicode.org/glossary/#unicode_scalar_value) that comprise it. + +This proposal adds several queries to increase the usefulness of `Character` and approachability of programming in Swift. It tries to walk the fuzzy line between what Swift can give reasonably good answers to, and what would require the user to adopt more elaborate linguistic analysis frameworks or techniques. + +## Proposed Solution +(Note that Unicode does not define properties on graphemes in general. Swift is defining its own semantics in terms of Unicode semantics derived from scalar properties, semantics on strings, or both) + +### Character Properties + +```swift +extension Character { + /// Whether this Character is ASCII. + @inlinable + public var isASCII: Bool { ... } + + /// Returns the ASCII encoding value of this Character, if ASCII. + /// + /// Note: "\r\n" (CR-LF) is normalized to "\n" (LF), which will return 0x0A + @inlinable + public var asciiValue: UInt8? { ... } + + /// Whether this Character represents whitespace, including newlines. + /// + /// Examples: + /// * "\t" (U+0009 CHARACTER TABULATION) + /// * " " (U+0020 SPACE) + /// * U+2029 PARAGRAPH SEPARATOR + /// * U+3000 IDEOGRAPHIC SPACE + /// + public var isWhitespace: Bool { ... } + + /// Whether this Character represents a newline. + /// + /// Examples: + /// * "\n" (U+000A): LINE FEED (LF) + /// * "\r" (U+000D): CARRIAGE RETURN (CR) + /// * "\r\n" (U+000A U+000D): CR-LF + /// * U+0085: NEXT LINE (NEL) + /// * U+2028: LINE SEPARATOR + /// * U+2029: PARAGRAPH SEPARATOR + /// + public var isNewline: Bool { ... } + + /// Whether this Character represents a number. + /// + /// Examples: + /// * "7" (U+0037 DIGIT SEVEN) + /// * "⅚" (U+215A VULGAR FRACTION FIVE SIXTHS) + /// * "㊈" (U+3288 CIRCLED IDEOGRAPH NINE) + /// * "𝟠" (U+1D7E0 MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT) + /// * "๒" (U+0E52 THAI DIGIT TWO) + /// + public var isNumber: Bool { ... } + + /// Whether this Character represents a whole number. See + /// `Character.wholeNumberValue` + @inlinable + public var isWholeNumber: Bool { ... } + + /// If this Character is a whole number, return the value it represents, else + /// nil. + /// + /// Examples: + /// * "1" (U+0031 DIGIT ONE) => 1 + /// * "५" (U+096B DEVANAGARI DIGIT FIVE) => 5 + /// * "๙" (U+0E59 THAI DIGIT NINE) => 9 + /// * "万" (U+4E07 CJK UNIFIED IDEOGRAPH-4E07) => 10_000 + /// + public var wholeNumberValue: Int? { ... } + + /// Whether this Character represents a hexadecimal digit. + /// + /// Hexadecimal digits include 0-9, Latin letters a-f and A-F, and their + /// fullwidth compatibility forms. To get their value, see + /// `Character.hexadecimalDigitValue` + @inlinable + public var isHexadecimalDigit: Bool { ... } + + /// If this Character is a hexadecimal digit, returns the value it represents, + /// else nil. + /// + /// Hexadecimal digits include 0-9, Latin letters a-f and A-F, and their + /// fullwidth compatibility forms. + public var hexadecimalDigitValue: Int? { ... } + + /// Whether this Character is a letter. + /// + /// Examples: + /// * "A" (U+0041 LATIN CAPITAL LETTER A) + /// * "é" (U+0065 LATIN SMALL LETTER E, U+0301 COMBINING ACUTE ACCENT) + /// * "ϴ" (U+03F4 GREEK CAPITAL THETA SYMBOL) + /// * "ڈ" (U+0688 ARABIC LETTER DDAL) + /// * "日" (U+65E5 CJK UNIFIED IDEOGRAPH-65E5) + /// * "ᚨ" (U+16A8 RUNIC LETTER ANSUZ A) + /// + public var isLetter: Bool { ... } + + /// Perform case conversion to uppercase + /// + /// Examples: + /// * "é" (U+0065 LATIN SMALL LETTER E, U+0301 COMBINING ACUTE ACCENT) + /// => "É" (U+0045 LATIN CAPITAL LETTER E, U+0301 COMBINING ACUTE ACCENT) + /// * "и" (U+0438 CYRILLIC SMALL LETTER I) + /// => "И" (U+0418 CYRILLIC CAPITAL LETTER I) + /// * "π" (U+03C0 GREEK SMALL LETTER PI) + /// => "Π" (U+03A0 GREEK CAPITAL LETTER PI) + /// * "ß" (U+00DF LATIN SMALL LETTER SHARP S) + /// => "SS" (U+0053 LATIN CAPITAL LETTER S, U+0053 LATIN CAPITAL LETTER S) + /// + /// Note: Returns a String as case conversion can result in multiple + /// Characters. + public func uppercased() -> String { ... } + + /// Perform case conversion to lowercase + /// + /// Examples: + /// * "É" (U+0045 LATIN CAPITAL LETTER E, U+0301 COMBINING ACUTE ACCENT) + /// => "é" (U+0065 LATIN SMALL LETTER E, U+0301 COMBINING ACUTE ACCENT) + /// * "И" (U+0418 CYRILLIC CAPITAL LETTER I) + /// => "и" (U+0438 CYRILLIC SMALL LETTER I) + /// * "Π" (U+03A0 GREEK CAPITAL LETTER PI) + /// => "π" (U+03C0 GREEK SMALL LETTER PI) + /// + /// Note: Returns a String as case conversion can result in multiple + /// Characters. + public func lowercased() -> String { ... } + + /// Whether this Character is considered uppercase. + /// + /// Uppercase Characters vary under case-conversion to lowercase, but not when + /// converted to uppercase. + /// + /// Examples: + /// * "É" (U+0045 LATIN CAPITAL LETTER E, U+0301 COMBINING ACUTE ACCENT) + /// * "И" (U+0418 CYRILLIC CAPITAL LETTER I) + /// * "Π" (U+03A0 GREEK CAPITAL LETTER PI) + /// + @inlinable + public var isUppercase: Bool { ... } + + /// Whether this Character is considered lowercase. + /// + /// Lowercase Characters vary under case-conversion to lowercase, but not when + /// converted to uppercase. + /// + /// Examples: + /// * "é" (U+0065 LATIN SMALL LETTER E, U+0301 COMBINING ACUTE ACCENT) + /// * "и" (U+0438 CYRILLIC SMALL LETTER I) + /// * "π" (U+03C0 GREEK SMALL LETTER PI) + /// + @inlinable + public var isLowercase: Bool { ... } + + /// Whether this Character changes under any form of case conversion. + @inlinable + public var isCased: Bool { ... } + + /// Whether this Character represents a symbol + /// + /// Examples: + /// * "®" (U+00AE REGISTERED SIGN) + /// * "⌹" (U+2339 APL FUNCTIONAL SYMBOL QUAD DIVIDE) + /// * "⡆" (U+2846 BRAILLE PATTERN DOTS-237) + /// + public var isSymbol: Bool { ... } + + /// Whether this Character represents a symbol used in mathematical formulas + /// + /// Examples: + /// * "+" (U+002B PLUS SIGN) + /// * "∫" (U+222B INTEGRAL) + /// * "ϰ" (U+03F0 GREEK KAPPA SYMBOL) + /// + /// Note: This is not a strict subset of isSymbol. This includes characters + /// used both as letters and commonly in mathematical formulas. For example, + /// "ϰ" (U+03F0 GREEK KAPPA SYMBOL) is considered a both mathematical symbol + /// and a letter. + /// + public var isMathSymbol: Bool { ... } + + /// Whether this Character represents a currency symbol + /// + /// Examples: + /// * "$" (U+0024 DOLLAR SIGN) + /// * "¥" (U+00A5 YEN SIGN) + /// * "€" (U+20AC EURO SIGN) + public var isCurrencySymbol: Bool { ... } + + /// Whether this Character represents punctuation + /// + /// Examples: + /// * "!" (U+0021 EXCLAMATION MARK) + // * "؟" (U+061F ARABIC QUESTION MARK) + /// * "…" (U+2026 HORIZONTAL ELLIPSIS) + /// * "—" (U+2014 EM DASH) + /// * "“" (U+201C LEFT DOUBLE QUOTATION MARK) + /// + public var isPunctuation: Bool { ... } + + /// Whether this Character has an emoji presentation + /// + /// Examples: + /// + /// * "2\u{FE0F}\u{20E3}".isEmoji // True (2️⃣) + /// * "\u{00A9}\u{FE0F}".isEmoji // True (©️) + /// * "\u{2708}".isEmoji // False (U+2708 AIRPLANE) + /// * "\u{2708}\u{FE0F}.isEmoji" // True (U+2708 AIRPLANE, emoji_presentation_selector) + /// + /// Note: When a presentation selector is absent, this returns whether the + /// Character is presented as emoji *by default*. Whether an environment + /// chooses to actually render these as emoji or textually may be context or + /// platform dependent. + /// + public var isEmoji: Bool { ... } +} +``` + +Additionally, we propose an explicit `ascii:` label be added to `FixedWidthInteger`’s failable init from a `String`, and an additional one defined over `Character`. We argue the old name is harmful and an explicit label more closely adheres to the [API Design Guidelines](https://swift.org/documentation/api-design-guidelines/). See “Detailed Semantics and Rationale” below. + +```diff +- FixedWidthInteger.init?(_: S, radix: Int = 10) ++ FixedWidthInteger.init?(ascii: S, radix: Int = 10) ++ FixedWithInteger.init?(ascii: Character, radix: Int = 10) +``` + + +## Detailed Semantics and Rationale + +Some fuzziness is inherent in modeling human writing systems and the rules of grapheme breaking allow for semantically meaningless, yet technically valid, graphemes. In light of all this, we make a best effort and try to discover some principle to follow. Principles are useful for evaluating tradeoffs, but are not hard rules that always lead to a single clear answer. + +The closest applicable principle might be something similar to W3C’s [Principle of Tolerance](https://www.w3.org/DesignIssues/Principles.html), paraphrased as “Be liberal in what you accept, conservative in what you produce”. Character properties can be roughly grouped into those that “produce” specific values or behaviors, and those that “accept” graphemes under a fuzzy classification. + +### Restrictive Properties + +Properties that provide a clear interpretation or which the stdlib produces a specific value for should be restrictive. One example is `wholeNumberValue`. `wholeNumberValue` *produces* an `Int` from a `Character`, which means it needs to be *restrictive*, permitting only the graphemes with unambiguous whole number values. It only returns a value for single-scalar graphemes whose sole scalar has an integral numeric value. Thus, `wholeNumberValue` returns nil for “7̅” (7 followed by U+0305 COMBINING OVERLINE) as there is no clear interpretation of the value. Any attempt to produce a specific integer from “7̅” would be suspect. + +Restrictive properties typically accept/reject based on an analysis of the entire grapheme. + +* Values: `isASCII` / `asciiValue`, `isWholeNumber` / `wholeNumberValue`, `isHexDigit` / `hexDigitValue` +* Casing: `isUppercase` / `uppercased()`, `isLowercase` / `lowercased()`, `isCased` + +### Permissive Properties + +Where there is no clear interpretation or specific value to produce, we try to be as permissive as reasonable. For example, `isLetter` just queries the first scalar to see if it is “letter-like”, and thus handles unforeseeable combinations of a base letter-like scalar with subsequent combining, modifying, or extending scalars. `isLetter` merely answers a general (fuzzy) question, but doesn’t prescribe further interpretation. + +Permissive APIs should in general be non-inlinable and their documentation may be less precise regarding details and corner cases. This allows for a greater degree of library evolution. Permissive properties typically accept/reject based on an analysis of part of the grapheme. + +* Fuzzy queries: `isNumber`, `isLetter`, `isSymbol` / `isMathSymbol` / `isCurrencySymbol`, `isPunctuation`, `isEmoji` + +#### Newlines and Whitespace + +Newlines encompass more than hard line-breaks in traditional written language; they are common terminators for programmer strings. Whether a `Character` such as `"\n\u{301}"` (a newline with a combining accent over it) is a newline is debatable. Either interpretation can lead to inconsistencies. If true, then a program might skip the first scalar in a new entry (whatever such a combining scalar at the start could mean). If false, then a `String` with newline terminators inside of it would return false for `myStr.contains { $0.isNewline }`, which is counter-intuitive. The same is true of whitespace. + +We recommend that the precise semantics of `isWhitespace` and `isNewline` be unspecified regarding graphemes consisting of leading whitespace/newlines followed by combining scalars. + +### Adding `ascii:` Label to `FixedWidthInteger.init?(_: S, radix: Int = 10)` + +We argue that the `ascii:` label is required to clarify two primary ambiguities: the kinds of digits accepted and the encoding subset supported. + +The existence of support for radices up to 36 implies that the kinds of digits accepted be restricted to Latin numerals and consecutively-encoded Latin letters (i.e. hexadecimal digits along with the next 20 letters). This is quite a mental leap to make without an explicit label. Additionally, this initializer rejects fullwidth compatibility forms which are otherwise considered digits with radices (i.e. they are hexadecimal digits with similar subsequent 20 letters). + +We argue that the rules regarding omitting argument label for value-preserving type conversions do not apply as this initializer is not monomorphic due to casing. + +This label’s clarity is more apparent in the proposed `FixedWithInteger.init?(ascii: Character, radix: Int = 10)`, and would preserve clarity with more-permissive initializers in the future. + +## Source Compatibility +The properties on `Character` are strictly additive. The addition of the `ascii:` label to `FixedWithInteger.init(_:String,radix:Int)` will need to go through the normal unavailable/renamed deprecation process. + +## Effect on ABI Stability +These changes are ABI-additive: they introduce new ABI surface area to keep stable. + +The proposed solution includes recommended `@inlinable` annotations on properties which derive their value from other properties (thus benefitting from optimizations), or which are well-defined and stable under future Unicode versions (e.g. ASCII-related properties). + +## Additions and Alternatives Considered + +### Titlecase + +Titlecase can be useful for some legacy scalars (ligatures) as well as for Strings when combined with word-breaking logic. However, it seems pretty obscure to surface on Character directly. + +### String.Lines, String.Words + +These have been deferred from this pitch to keep focus and await a more generalized lazy split collection. + +### Rename Permissive `isFoo` to `hasFoo` + +This was mentioned above in discussion of `isNewline` semantics and could also apply to `isWhitespace`. However, it would be awkward for `isNumber` or `isLetter`. What the behavior should be for exotic whitespace and newlines is heavily debatable. We’re sticking to `isNewline/isWhitespace` for now, but are open to argument. + +### Design as `Character.has(OptionSet<…>, exclusively: …)` + +There could be something valuable to glean from this, but we reject this approach as somewhat un-Swifty with a poor discovery experience, especially for new or casual users. It does, however, make the semantic distinctions above very explicit at the call site. + +### Add Failable FixedWidthInteger/FloatingPoint Initializers Taking Character + +In addition to (or perhaps instead of) properties like `wholeNumberValue`, add `Character`-based `FixedWidthInteger.init?(_:Character)`. Similarly `FloatingPoint.init?(_:Character)` which includes vulgar fractions and the like (if single-scalar, perhaps). However, these do not have direct counterparts in this pitch as named, at least without an explicit argument label clarifying their semantics. + +We could consider adding something like `FixedWidthInteger.init?(hexDigit: Character)` and `FixedWithInteger.init?(wholeNumber: Character)` which correspond to `hexDigitValue` and `wholeNumberValue`, and similarly a counterpart for `String`. But, we don’t feel this carries its weight as surfaced directly at the top level of e.g. `Int`. We prefer to keep this avenue open for future directions involving more general number parsing and grapheme evaluation logic. + +### Keep `FixedWidthInteger.init(_:radix:)` around, or change `FixedWidthInteger.init(_:radix:)` to support full-width compatibility forms` + +Rather than rename with an `ascii:` label, keep the old name around to be built upon later with a general number parsing system. We argue that the radix argument makes such an API highly dubious if not constrained to ASCII and full-width compatibility forms (e.g. akin to proposed `Character.hexDigitValue`). + +Another alternative is to change the semantics to also accept full-width compatibility forms. Much of the argument for why the API should have an explicit label still apply, though the `radix` label does provide some prodding when provided. We’d prefer the explicit label if possible, but this could be a lessor of evils source-compatibility-preserving alternative. + +### Drop `isASCII/HexDigit/WholeNumber`: Check for `nil` Instead + +This alternative is to drop `isASCII`, `isHexDigit`, and `isWholeNumber` and instead use `if let` or compare explicitly to `nil`. + +We decided to provide these convenience properties both for discoverability as well as use in more complex expressions: `c.isHexDigit && c.isLetter`, `c.isASCII && c.isWhitespace`, etc. We don’t think they add significant weight or undue API surface area. + +### Add `numericValue: Double?` in addition to, or instead of, `wholeNumberValue: Int?`. Alternatively, add a `rationalValue: (numerator: Int, denominator: Int)?`. + +Unicode defines numeric values for whole numbers, hex digits, and rational numbers (vulgar fractions). As an implementation artifact (ICU only vends a double), Unicode.Scalar.Properties’s `numericValue` is a double rather than an enum of a rational or whole number. We could follow suit and add such a value to `Character`, restricted to single-scalar graphemes. We could also remove `wholeNumberValue`, letting users test if the double is integral. Alternatively or additionally, we could provide a `rationalValue` capable of handling non-whole-numbers. + +As far as adding a `rationalValue` is concerned, we do not feel that support for vulgar fractions and other obscure Unicode scalars (e.g. baseball score-keeping) warrants an addition to `Character` directly. `wholeNumberValue` producing an Int is a more fluid solution than a Double which happens to be integral, so we’re hesitant to replace `wholeNumberValue` entirely with a `numericValue`. Since `numericValue` would only add utility for these obscure characters, we’re not sure if it’s worth adding. + +Suggestions for alternative names for `wholeNumberValue`would be appreciated. + +### Replace `isEmoji` with 3 APIs, something something like `isEmojiPresentable`, `isEmojiWithDefaultTextPresentation`, and `isEmojiWithDefaultEmojiPresentation` + +`isEmoji` encompasses both Characters with a default emoji presentation, as well as Characters with a default textual presentation and an explicit `U+FE0F` (emoji presentation selector). It rejects default emoji presentation Characters with an explicit `U+FE0E` (text presentation selector), as well as default textual presentation Characters which do not have an emoji presentation selector. + +However, these only cover the default; whether something will actually be rendered as emoji depends on the render and environment. We’re open to further refining `isEmoji` with more API, but are unsure if they carry their weight. We currently feel this might be best left up to querying the rendering environment itself. We’re interested in thoughts and/or use cases. + +We are also considering relaxing `isEmoji` to return true for default-textual Characters without an explicit text presentation selector. For example, `U+2708` (AIRPLANE) by default is rendered as `✈` and not `✈️`. As proposed, `Character("\u{U+2708}").isEmoji` would return false, but we are considering having it return true because it *could* be rendered as emoji. + From f0b4e79c37f8adedfaadba7c0c259c78780f1782 Mon Sep 17 00:00:00 2001 From: Thiago Holanda Date: Mon, 23 Jul 2018 19:44:41 +0200 Subject: [PATCH 0761/4563] fix status of proposal SE-0221 --- proposals/0221-character-properties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0221-character-properties.md b/proposals/0221-character-properties.md index 8483684c6b..cfd30dfb68 100644 --- a/proposals/0221-character-properties.md +++ b/proposals/0221-character-properties.md @@ -2,7 +2,7 @@ * Proposal: [SE-0221](0221-character-properties.md) * Authors: [Michael Ilseman](https://github.com/milseman), [Tony Allevato](https://github.com/allevato) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **In review (July 23...29)** +* Status: **Active review (July 23...29)** * Implementation: https://github.com/apple/swift/pull/15880 ## Introduction From 30b74b52141a2a16d19f041acaa0a53a2ab2fede Mon Sep 17 00:00:00 2001 From: Omar Al-Ejel Date: Thu, 26 Jul 2018 10:48:24 -0700 Subject: [PATCH 0762/4563] Update 0022-objc-selectors.md typo --- proposals/0022-objc-selectors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0022-objc-selectors.md b/proposals/0022-objc-selectors.md index 119092ff5e..d280cc6bf9 100644 --- a/proposals/0022-objc-selectors.md +++ b/proposals/0022-objc-selectors.md @@ -112,7 +112,7 @@ from a string. The primary alternative is [type-safe selectors](https://lists.swift.org/pipermail/swift-evolution/2015-December/000233.html), -which would introduce a new "selector" calling convetion to capture +which would introduce a new "selector" calling convention to capture the type of an `@objc` method, including its selector. One major benefit of type-safe selectors is that they can carry type information, improving type safety. From that discussion, referencing From b1e279b60917f3d6b7e16a64284ea00400c0f0dc Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Thu, 26 Jul 2018 13:38:01 -0700 Subject: [PATCH 0763/4563] Fix typos in Character Properties --- proposals/0221-character-properties.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0221-character-properties.md b/proposals/0221-character-properties.md index cfd30dfb68..3d8094647d 100644 --- a/proposals/0221-character-properties.md +++ b/proposals/0221-character-properties.md @@ -157,8 +157,8 @@ extension Character { /// Whether this Character is considered lowercase. /// - /// Lowercase Characters vary under case-conversion to lowercase, but not when - /// converted to uppercase. + /// Lowercase Characters vary under case-conversion to uppercase, but not when + /// converted to lowercase. /// /// Examples: /// * "é" (U+0065 LATIN SMALL LETTER E, U+0301 COMBINING ACUTE ACCENT) @@ -316,7 +316,7 @@ We could consider adding something like `FixedWidthInteger.init?(hexDigit: Chara Rather than rename with an `ascii:` label, keep the old name around to be built upon later with a general number parsing system. We argue that the radix argument makes such an API highly dubious if not constrained to ASCII and full-width compatibility forms (e.g. akin to proposed `Character.hexDigitValue`). -Another alternative is to change the semantics to also accept full-width compatibility forms. Much of the argument for why the API should have an explicit label still apply, though the `radix` label does provide some prodding when provided. We’d prefer the explicit label if possible, but this could be a lessor of evils source-compatibility-preserving alternative. +Another alternative is to change the semantics to also accept full-width compatibility forms. Much of the argument for why the API should have an explicit label still apply, though the `radix` label does provide some prodding when provided. We’d prefer the explicit label if possible, but this could be a lesser of evils source-compatibility-preserving alternative. ### Drop `isASCII/HexDigit/WholeNumber`: Check for `nil` Instead From 9e44932452e1daead98f2bc2e58711eb489e9751 Mon Sep 17 00:00:00 2001 From: Bas Broek Date: Sun, 29 Jul 2018 06:09:09 +0200 Subject: [PATCH 0764/4563] Update SE-0110's status to accepted (#887) See https://github.com/apple/swift-evolution/pull/832#issuecomment-408623053 --- proposals/0110-distingish-single-tuple-arg.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0110-distingish-single-tuple-arg.md b/proposals/0110-distingish-single-tuple-arg.md index c0ae883a48..cd006d24ec 100644 --- a/proposals/0110-distingish-single-tuple-arg.md +++ b/proposals/0110-distingish-single-tuple-arg.md @@ -3,7 +3,7 @@ * Proposal: [SE-0110](0110-distingish-single-tuple-arg.md) * Authors: Vladimir S., [Austin Zheng](https://github.com/austinzheng) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Deferred** +* Status: **Accepted** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-July/000215.html), [Additional Commentary](https://lists.swift.org/pipermail/swift-evolution-announce/2017-June/000386.html) * Bug: [SR-2008](https://bugs.swift.org/browse/SR-2008) From 90fdad82d66cb8b7e8ec7ac4d6bb0e9c907032bc Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 29 Jul 2018 05:11:11 +0100 Subject: [PATCH 0765/4563] [SE-0220] Add the implementation link --- proposals/0220-count-where.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0220-count-where.md b/proposals/0220-count-where.md index b885aea179..471ca1cc7f 100644 --- a/proposals/0220-count-where.md +++ b/proposals/0220-count-where.md @@ -1,9 +1,10 @@ # `count(where:)` * Proposal: [SE-0220](0220-count-where.md) -* Authors: [Soroush Khanlou](https://github.com/khanlou) +* Author: [Soroush Khanlou](https://github.com/khanlou) * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Scheduled for review (August 6...12)** +* Implementation: [apple/swift#16099](https://github.com/apple/swift/pull/16099) ## Introduction From fdb725c240033c5273860b0a66d2189d62a97608 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 29 Jul 2018 05:13:13 +0100 Subject: [PATCH 0766/4563] [SE-0221] Update the implementation link --- proposals/0221-character-properties.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0221-character-properties.md b/proposals/0221-character-properties.md index 3d8094647d..388ad77823 100644 --- a/proposals/0221-character-properties.md +++ b/proposals/0221-character-properties.md @@ -1,9 +1,10 @@ # Character Properties + * Proposal: [SE-0221](0221-character-properties.md) * Authors: [Michael Ilseman](https://github.com/milseman), [Tony Allevato](https://github.com/allevato) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Active review (July 23...29)** -* Implementation: https://github.com/apple/swift/pull/15880 +* Implementation: [apple/swift#15880](https://github.com/apple/swift/pull/15880) ## Introduction From 2b2079edae4a5bfd9cf3594f8ea7475969ffba77 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 29 Jul 2018 06:30:42 +0100 Subject: [PATCH 0767/4563] [SE-0215] Update status to implemented (Swift 5) --- proposals/0215-conform-never-to-hashable-and-equatable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0215-conform-never-to-hashable-and-equatable.md b/proposals/0215-conform-never-to-hashable-and-equatable.md index 4ee683ddf1..089b7c63df 100644 --- a/proposals/0215-conform-never-to-hashable-and-equatable.md +++ b/proposals/0215-conform-never-to-hashable-and-equatable.md @@ -3,7 +3,7 @@ * Proposal: [SE-0215](0215-conform-never-to-hashable-and-equatable.md) * Author: [Matt Diephouse](https://github.com/mdiep) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Accepted** +* Status: **Implemented (Swift 5)** * Decision Notes: [Rationale](https://forums.swift.org/t/se-0215-conform-never-to-equatable-and-hashable/13586/45) * Implementation: [apple/swift#16857](https://github.com/apple/swift/pull/16857) From c328b0c74b77126819d74fab13122b6bbb6634f5 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 29 Jul 2018 06:30:54 +0100 Subject: [PATCH 0768/4563] [SE-0218] Update status to implemented (Swift 5) --- proposals/0218-introduce-compact-map-values.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0218-introduce-compact-map-values.md b/proposals/0218-introduce-compact-map-values.md index d4c903069f..b5b97fe62e 100644 --- a/proposals/0218-introduce-compact-map-values.md +++ b/proposals/0218-introduce-compact-map-values.md @@ -3,7 +3,7 @@ * Proposal: [SE-0218](0218-introduce-compact-map-values.md) * Author: [Daiki Matsudate](https://github.com/d-date) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** +* Status: **Implemented (Swift 5)** * Implementation: [apple/swift#15017](https://github.com/apple/swift/pull/15017) * Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0218-introduce-compactmapvalues-to-dictionary/14448) From 465ce964d627deffe9f28c6978b2986a593255f8 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 30 Jul 2018 15:06:55 -0400 Subject: [PATCH 0769/4563] Declare this as SE-0222 and put it in review --- ...pactmap-sequence.md => 0222-lazy-compactmap-sequence.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-lazy-compactmap-sequence.md => 0222-lazy-compactmap-sequence.md} (97%) diff --git a/proposals/NNNN-lazy-compactmap-sequence.md b/proposals/0222-lazy-compactmap-sequence.md similarity index 97% rename from proposals/NNNN-lazy-compactmap-sequence.md rename to proposals/0222-lazy-compactmap-sequence.md index 3e530f656b..7176d4b098 100644 --- a/proposals/NNNN-lazy-compactmap-sequence.md +++ b/proposals/0222-lazy-compactmap-sequence.md @@ -1,9 +1,9 @@ # Lazy CompactMap Sequence -* Proposal: [SE-NNNN](NNNN-lazy-compactmap-sequence.md) +* Proposal: [SE-0222](0222-lazy-compactmap-sequence.md) * Authors: [TellowKrinkle](https://github.com/TellowKrinkle), [Johannes Weiß](https://github.com/weissi) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (July 30th...August 5th, 2018)** * Implementation: [apple/swift#14841](https://github.com/apple/swift/pull/14841) ## Introduction From 3d871d9ea900c633f190a56276a0518a6648da9f Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Wed, 1 Aug 2018 21:35:05 -0700 Subject: [PATCH 0770/4563] Updates to proposal --- ...-binaryinteger-iseven-isodd-isdivisible.md | 133 ++++++++++++++++++ proposals/nnnn-binaryinteger-iseven-isodd.md | 121 ---------------- 2 files changed, 133 insertions(+), 121 deletions(-) create mode 100644 proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md delete mode 100644 proposals/nnnn-binaryinteger-iseven-isodd.md diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md b/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md new file mode 100644 index 0000000000..99238bced5 --- /dev/null +++ b/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md @@ -0,0 +1,133 @@ +# Adding `isEven`, `isOdd`, `isDivisible` to `BinaryInteger` + +* Proposal: [SE-NNNN](NNNN-binaryinteger-iseven-isodd.md) +* Authors: [Robert MacEachern](https://robmaceachern.com), [SiliconUnicorn](https://forums.swift.org/u/siliconunicorn/summary) +* Review Manager: TBD +* Status: **Awaiting implementation** + +## Introduction + +We propose adding `var isEven: Bool`, `var isOdd: Bool`, and `func isDivisible(by denominator: Self) -> Bool` to the `BinaryInteger` protocol. `isEven` and `isOdd` are convenience properties for querying the [parity](https://en.wikipedia.org/wiki/Parity_(mathematics)) of the integer and `isDivisible` is a more general function to determine the divisibility of an integer by an arbitrary denominator. + +Swift-evolution thread: [Even and Odd Integers](https://forums.swift.org/t/even-and-odd-integers/11774) + +## Motivation + +It is sometimes necessary to know whether or not an integer is divisible by a particular value. The most common case is testing for divisibility by 2, even and oddness. + +**Commonality:** Testing divisibility shows up in a surprising number of contexts including UI code, algorithm implementations (often in the form of assertions), tests, benchmarks, documentation and tutorial code. + +The most common way to test a value for divisibility is by using the remainder operator (`%`) checking for a remainder of zero: `12 % 2 == 0 // returns true. 12 is divisible by 2`. Similarly, testing for indivisibility is done by checking for a remainder other than zero: `13 % 2 != 0 // returns true. 13 is not divisible by 2`. + +Alternatively, it is also possible to use the bitwise AND operator (`&`) to check the even/oddness of a value: `12 & 1 == 0 // returns true`. + +```swift +// Gray background for even rows, white for odd. +view.backgroundColor = indexPath.row % 2 == 0 ? .gray : .white + +// Codable.swift.gyb in apple/swift +guard count % 2 == 0 else { throw DecodingError.dataCorrupted(...) } + +// Bool.swift in apple/swift +public static func random(using generator: inout T) -> Bool { + return (generator.next() >> 17) & 1 == 0 +} + +// KeyPath.swift in apple/swift +_sanityCheck(bytes > 0 && bytes % 4 == 0, "capacity must be multiple of 4 bytes") + +// ReversedCollection Index.base documentation https://developer.apple.com/documentation/swift/reversedcollection/index/2965437-base +guard let i = reversedNumbers.firstIndex(where: { $0 % 2 == 0 }) +``` + +**Readability:** This proposal significantly improves readability. There is no need to understand operator precedence rules (`%` has higher precedence than `==`) which are non-obvious. + +The properties are also fewer characters wide than the modulus approach (maximum 7 characters for `.isEven` vs 9 for ` % 2 == 0`) which saves horizontal space while being clearer in intent. + +```swift +// Gray background for even rows, white for odd. +view.backgroundColor = indexPath.row.isEven ? .gray : .white + +// Codable.swift.gyb in apple/swift +guard count.isEven else { throw DecodingError.dataCorrupted(...) } + +// Bool.swift in apple/swift +public static func random(using generator: inout T) -> Bool { + return (generator.next() >> 17).isEven +} + +// KeyPath.swift in apple/swift +_sanityCheck(bytes > 0 && bytes.isDivisible(by: 4), "capacity must be multiple of 4 bytes") +``` + +**Discoverability:** Determining whether a value is even or odd is a common question across programming languages, at least based on these Stack Overflow questions: +[c - How do I check if an integer is even or odd?](https://stackoverflow.com/questions/160930/how-do-i-check-if-an-integer-is-even-or-odd) 300,000+ views +[java - Check whether number is even or odd](https://stackoverflow.com/questions/7342237/check-whether-number-is-even-or-odd) 350,000+ views +[Check if a number is odd or even in python](https://stackoverflow.com/questions/21837208/check-if-a-number-is-odd-or-even-in-python) 140,000+ views + +IDEs will be able to suggest `.isEven` and `.isOdd` as part of autocomplete which will aid discoverability. + +**Consistency:** It would be relatively easy to reproduce the properties in user code but there would be benefits to having a standard implementation. It may not be obvious to some users exactly which protocol these properties belong on (`Int`?, `SignedInteger`?, `FixedWidthInteger`?, `BinaryInteger`?). This inconsistency can be seen in a [popular Swift utility library](https://github.com/SwifterSwift/SwifterSwift/blob/master/Sources/Extensions/SwiftStdlib/SignedIntegerExtensions.swift#L28) which defines `isEven` and `isOdd` on `SignedInteger` which results in the properties being inaccessible for unsigned integers. + +These properties will also eliminate the need to use remainder 2 and bitwise AND 1 to determine parity. + +Adding `isEven` and `isOdd` is also consistent with the `.isEmpty` utility property, which is a convenience for `.count == 0`. +```swift +if array.count == 0 { ... } +if array.isEmpty { ... } + +if value % 2 == 0 { ... } +if value.isEven { ... } +``` + +**Correctness:** There is a minor correctness risk in misinterpreting something like `value % 2 == 0`, particularly when used in a more complex statement, when compared to `value.isEven`. + +**Performance:** This proposal likely won’t have a major positive impact on performance but it should not introduce any additional overhead thanks to `@inlineable`. + +## Proposed solution + +Add two computed properties, `isEven` and `isOdd`, and a function `isDivisible` to the `BinaryInteger` protocol. + +```swift + @inlinable + /// A Boolean value indicating whether this value is even. + /// + /// An integer is even if it is divisible by two. + public var isEven: Bool { + return isDivisible(by: 2) + } + + @inlinable + /// A Boolean value indicating whether this value is odd. + /// + /// An integer is odd if it is not divisible by two. + public var isOdd: Bool { + return !isDivisible(by: 2) + } + + @inlinable + /// + func isDivisible(by denominator: Self) -> Bool { + return self % denominator == 0 + } +``` + +## Detailed design + +N/A + +## Source compatibility + +This is strictly additive. + +## Effect on ABI stability + +N/A + +## Effect on API resilience + +N/A + +## Alternatives considered + +N/A diff --git a/proposals/nnnn-binaryinteger-iseven-isodd.md b/proposals/nnnn-binaryinteger-iseven-isodd.md deleted file mode 100644 index a53fd95731..0000000000 --- a/proposals/nnnn-binaryinteger-iseven-isodd.md +++ /dev/null @@ -1,121 +0,0 @@ -# Adding `isEven` and `isOdd` properties to `BinaryInteger` - -* Proposal: [SE-NNNN](NNNN-binaryinteger-iseven-isodd.md) -* Authors: [Robert MacEachern](https://robmaceachern.com), [SiliconUnicorn](https://forums.swift.org/u/siliconunicorn/summary) -* Review Manager: TBD -* Status: **Awaiting implementation** - -## Introduction - -We propose adding `var isEven: Bool` and `var isOdd: Bool` to `BinaryInteger`. These are convenience properties for querying the [parity](https://en.wikipedia.org/wiki/Parity_(mathematics)) of the integer. - -Swift-evolution thread: [Even and Odd Integers](https://forums.swift.org/t/even-and-odd-integers/11774) - -## Motivation - -It is sometimes useful to know the evenness or oddness (parity) of an integer and switch the behaviour based on the result. The most typical way to do this is using `value % 2 == 0` to determine if `value` is even, or `value % 2 != 0` to determine if `value` is odd. - -```swift -// Gray background for even rows, white for odd. -view.backgroundColor = indexPath.row % 2 == 0 ? .gray : .white - -// Enable button if we have odd number of photos -buttonSave.isEnabled = photos.count % 2 != 0 -``` - -It is also possible to use the bitwise AND operator (`value & 1 == 0`) which will inevitably lead to discussions about which one is faster and attempts at profiling them, etc, etc. - -There are a few more specific motivations for this proposal: - -### Commonality - -The need to determine the parity of an integer isn’t restricted to a limited problem domain. - -### Readability - -This proposal significantly improves readability. There is no need to understand operator precedence rules (`%` has higher precedence than `==`) which are non-obvious. - -The properties are also fewer characters wide than the modulus approach (maximum 7 characters for `.isEven` vs 9 for ` % 2 == 0`) which saves horizontal space while being clearer in intent. -```swift -view.backgroundColor = indexPath.row % 2 == 0 ? .gray : .white -view.backgroundColor = indexPath.row.isEven ? .gray : .white - -buttonSave.isEnabled = photos.count % 2 != 0 -buttonSave.isEnabled = photos.count.isOdd -``` - -### Discoverability - -Determining whether a value is even or odd is a common question across programming languages, at least based on these Stack Overflow questions: -[c - How do I check if an integer is even or odd?](https://stackoverflow.com/questions/160930/how-do-i-check-if-an-integer-is-even-or-odd) 300,000+ views -[java - Check whether number is even or odd](https://stackoverflow.com/questions/7342237/check-whether-number-is-even-or-odd) 350,000+ views -[Check if a number is odd or even in python](https://stackoverflow.com/questions/21837208/check-if-a-number-is-odd-or-even-in-python) 140,000+ views - -IDEs will be able to suggest `.isEven` and `.isOdd` as part of autocomplete which will aid discoverability. - -### Consistency - -It would be relatively easy to reproduce the properties in user code but there would be benefits to having a standard implementation. It may not be obvious to some users exactly which protocol these properties belong on (`Int`?, `SignedInteger`?, `FixedWidthInteger`?, `BinaryInteger`?). This inconsistency can be seen in a [popular Swift utility library](https://github.com/SwifterSwift/SwifterSwift/blob/master/Sources/Extensions/SwiftStdlib/SignedIntegerExtensions.swift#L28) which defines these properties on `SignedInteger` which results in the properties being inaccessible for unsigned integers. - -These properties will also eliminate the need to use modulus 2 and bitwise AND 1 to determine parity. - -Adding `isEven` and `isOdd` is also consistent with the `.isEmpty` utility property, which is a convenience for `.count == 0`. -```swift -if array.count == 0 { ... } -if array.isEmpty { ... } - -if value % 2 == 0 { ... } -if value.isEven { ... } -``` - -### Correctness - -There is a minor correctness risk in misinterpreting something like `value % 2 == 0`, particularly when used in a more complex statement, when compared to `value.isEven`. - -### Performance - -This proposal likely won’t have a major positive impact on performance but it should not introduce any additional overhead thanks to `@inlineable`. - -## Proposed solution - -Add two computed properties, `isEven` and `isOdd`, to `BinaryInteger` - -```swift -extension BinaryInteger { - @inlinable - /// A Boolean value indicating whether this value is even. - /// - /// An integer is even if it is evenly divisible by two. - public var isEven: Bool { - return self % 2 == 0 - } - - @inlinable - /// A Boolean value indicating whether this value is odd. - /// - /// An integer is odd if it is not evenly divisible by two. - public var isOdd: Bool { - return self % 2 != 0 - } -} -``` - -## Detailed design - -N/A - -## Source compatibility - -This is strictly additive. - -## Effect on ABI stability - -N/A - -## Effect on API resilience - -N/A - -## Alternatives considered - -`divisible(by:)` From 3ab45ee079f4ca208a5cf9afe830aa41dfc76c1a Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Thu, 2 Aug 2018 22:12:38 -0700 Subject: [PATCH 0771/4563] Refinement of motivation sections --- ...-binaryinteger-iseven-isodd-isdivisible.md | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md b/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md index 99238bced5..cfe0b34b9e 100644 --- a/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md +++ b/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md @@ -13,11 +13,11 @@ Swift-evolution thread: [Even and Odd Integers](https://forums.swift.org/t/even- ## Motivation -It is sometimes necessary to know whether or not an integer is divisible by a particular value. The most common case is testing for divisibility by 2, even and oddness. +It is sometimes necessary to know whether or not an integer is divisible by a particular value. The most common case is testing for divisibility by 2 (even and oddness). -**Commonality:** Testing divisibility shows up in a surprising number of contexts including UI code, algorithm implementations (often in the form of assertions), tests, benchmarks, documentation and tutorial code. +**Commonality:** Testing divisibility shows up in a surprising number of contexts including UI code, algorithm implementations (often in the form of assertions), tests, benchmarks, documentation and tutorial/educational code. -The most common way to test a value for divisibility is by using the remainder operator (`%`) checking for a remainder of zero: `12 % 2 == 0 // returns true. 12 is divisible by 2`. Similarly, testing for indivisibility is done by checking for a remainder other than zero: `13 % 2 != 0 // returns true. 13 is not divisible by 2`. +Currently, the most common way to test a value for divisibility is by using the remainder operator (`%`) checking for a remainder of zero: `12 % 2 == 0 // returns true. 12 is divisible by 2`. Similarly, testing for indivisibility is done by checking for a remainder other than zero: `13 % 2 != 0 // returns true. 13 is not divisible by 2`. Alternatively, it is also possible to use the bitwise AND operator (`&`) to check the even/oddness of a value: `12 & 1 == 0 // returns true`. @@ -40,9 +40,16 @@ _sanityCheck(bytes > 0 && bytes % 4 == 0, "capacity must be multiple of 4 bytes" guard let i = reversedNumbers.firstIndex(where: { $0 % 2 == 0 }) ``` -**Readability:** This proposal significantly improves readability. There is no need to understand operator precedence rules (`%` has higher precedence than `==`) which are non-obvious. +Determining whether a value is even or odd is a common question across programming languages, at least based on these Stack Overflow questions: +[c - How do I check if an integer is even or odd?](https://stackoverflow.com/questions/160930/how-do-i-check-if-an-integer-is-even-or-odd) 300,000+ views +[java - Check whether number is even or odd](https://stackoverflow.com/questions/7342237/check-whether-number-is-even-or-odd) 350,000+ views +[Check if a number is odd or even in python](https://stackoverflow.com/questions/21837208/check-if-a-number-is-odd-or-even-in-python) 140,000+ views -The properties are also fewer characters wide than the modulus approach (maximum 7 characters for `.isEven` vs 9 for ` % 2 == 0`) which saves horizontal space while being clearer in intent. +Convenience properties or functions equivalent to `isEven` and `isOdd` are available in the standard libraries of many other programming languages, including: [Ruby](https://ruby-doc.org/core-2.2.2/Integer.html#method-i-odd-3F), [Haskell](http://hackage.haskell.org/package/base-4.11.1.0/docs/Prelude.html#v:even), [Clojure](https://clojuredocs.org/clojure.core/odd_q), and according to [RosettaCode](https://www.rosettacode.org/wiki/Even_or_odd): Julia, Racket, Scheme, Smalltalk, Common Lisp. + +**Readability:** This proposal significantly improves readability, as expressions read like straightforward English sentences. There is no need to mentally parse and understand non-obvious operator precedence rules (`%` has higher precedence than `==`). + +The `isEven` and `isOdd` properties are also fewer characters wide than the remainder approach (maximum 7 characters for `.isEven` vs 9 for ` % 2 == 0`) which saves horizontal space while being clearer in intent. ```swift // Gray background for even rows, white for odd. @@ -60,29 +67,30 @@ public static func random(using generator: inout T) -> _sanityCheck(bytes > 0 && bytes.isDivisible(by: 4), "capacity must be multiple of 4 bytes") ``` -**Discoverability:** Determining whether a value is even or odd is a common question across programming languages, at least based on these Stack Overflow questions: -[c - How do I check if an integer is even or odd?](https://stackoverflow.com/questions/160930/how-do-i-check-if-an-integer-is-even-or-odd) 300,000+ views -[java - Check whether number is even or odd](https://stackoverflow.com/questions/7342237/check-whether-number-is-even-or-odd) 350,000+ views -[Check if a number is odd or even in python](https://stackoverflow.com/questions/21837208/check-if-a-number-is-odd-or-even-in-python) 140,000+ views +**Discoverability:** IDEs will be able to suggest `isEven`, `isOdd`, and `isDivisible` as part of autocomplete which will aid discoverability. It will also be familiar to users coming from languages that also support functionality similar to `isEven` and `isOdd`. -IDEs will be able to suggest `.isEven` and `.isOdd` as part of autocomplete which will aid discoverability. +**Trivially composable:** It would be relatively easy to reproduce the proposed functionality in user code but there would be benefits to having a standard implementation. It may not be obvious to some users exactly which protocol these properties belong on (`Int`?, `SignedInteger`?, `FixedWidthInteger`?, `BinaryInteger`?). This inconsistency can be seen in a [popular Swift utility library](https://github.com/SwifterSwift/SwifterSwift/blob/master/Sources/Extensions/SwiftStdlib/SignedIntegerExtensions.swift#L28) which defines `isEven` and `isOdd` on `SignedInteger` which results in the properties being inaccessible for unsigned integers. -**Consistency:** It would be relatively easy to reproduce the properties in user code but there would be benefits to having a standard implementation. It may not be obvious to some users exactly which protocol these properties belong on (`Int`?, `SignedInteger`?, `FixedWidthInteger`?, `BinaryInteger`?). This inconsistency can be seen in a [popular Swift utility library](https://github.com/SwifterSwift/SwifterSwift/blob/master/Sources/Extensions/SwiftStdlib/SignedIntegerExtensions.swift#L28) which defines `isEven` and `isOdd` on `SignedInteger` which results in the properties being inaccessible for unsigned integers. +Testing the parity of integers is also relatively common in sample code and educational usage. In this context, it’s usually not appropriate for an author to introduce this functionality (unless they are teaching extensions!) in order to avoid distracting from the main task at hand (e.g. filter, map, etc). It may also be the same situation for authoring test code: it'd be used if it were there but it's not worth the overhead of defining it manually. -These properties will also eliminate the need to use remainder 2 and bitwise AND 1 to determine parity. +This functionality will also eliminate the need to use the remainder operator or bitwise AND 1 when querying the divisibility of an integer. -Adding `isEven` and `isOdd` is also consistent with the `.isEmpty` utility property, which is a convenience for `.count == 0`. -```swift -if array.count == 0 { ... } -if array.isEmpty { ... } +**Correctness:** It isn't [uncommon](https://github.com/apple/swift/blob/master/stdlib/public/core/RangeReplaceableCollection.swift#L1090) to see tests for oddness written as `value % 2 == 1` in Swift, but this is incorrect for negative odd values. The semantics of the `%` operator vary between programming languages, such as Ruby and Python, which can be surprising. +``` +// Swift: +7 % 2 == 1 // true +-7 % 2 == 1 // false. -7 % 2 evaluates to -1 -if value % 2 == 0 { ... } -if value.isEven { ... } +// Ruby and Python +7 % 2 == 1 // true +-7 % 2 == 1 // true ``` -**Correctness:** There is a minor correctness risk in misinterpreting something like `value % 2 == 0`, particularly when used in a more complex statement, when compared to `value.isEven`. +There is also a minor correctness risk in misinterpreting something like `value % 2 == 0`, particularly when used in a more complex statement, when compared to `value.isEven`. + +**Performance:** It's _possible_ that `isDivisible` could be implemented in a more performant way than `% denominator == 0` for more complex types, such as a BigInteger/BigNum type. -**Performance:** This proposal likely won’t have a major positive impact on performance but it should not introduce any additional overhead thanks to `@inlineable`. +The addition of `isEven` and `isOdd` likely won’t have a major positive impact on performance but it should not introduce any additional overhead thanks to `@inlineable`. ## Proposed solution From b3b448a9c97a9134109085c25b29b7ec5a5e2fee Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Fri, 3 Aug 2018 11:35:22 -0700 Subject: [PATCH 0772/4563] Expanded on alternatives and other editing --- ...-binaryinteger-iseven-isodd-isdivisible.md | 68 ++++++++++++++++--- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md b/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md index cfe0b34b9e..a5ef19a907 100644 --- a/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md +++ b/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md @@ -7,7 +7,7 @@ ## Introduction -We propose adding `var isEven: Bool`, `var isOdd: Bool`, and `func isDivisible(by denominator: Self) -> Bool` to the `BinaryInteger` protocol. `isEven` and `isOdd` are convenience properties for querying the [parity](https://en.wikipedia.org/wiki/Parity_(mathematics)) of the integer and `isDivisible` is a more general function to determine the divisibility of an integer by an arbitrary denominator. +This proposal adds `var isEven: Bool`, `var isOdd: Bool`, and `func isDivisible(by denominator: Self) -> Bool` to the `BinaryInteger` protocol. `isEven` and `isOdd` are convenience properties for querying the [parity](https://en.wikipedia.org/wiki/Parity_(mathematics)) of the integer and `isDivisible` is a more general function to determine the divisibility of an integer by an arbitrary denominator. Swift-evolution thread: [Even and Odd Integers](https://forums.swift.org/t/even-and-odd-integers/11774) @@ -21,9 +21,11 @@ Currently, the most common way to test a value for divisibility is by using the Alternatively, it is also possible to use the bitwise AND operator (`&`) to check the even/oddness of a value: `12 & 1 == 0 // returns true`. +Some examples of divisibility in code (see more in appendix): + ```swift -// Gray background for even rows, white for odd. -view.backgroundColor = indexPath.row % 2 == 0 ? .gray : .white +// UITableView alternating row colour +cell.contentView.backgroundColor = indexPath.row % 2 == 0 ? .gray : .white // Codable.swift.gyb in apple/swift guard count % 2 == 0 else { throw DecodingError.dataCorrupted(...) } @@ -52,8 +54,8 @@ Convenience properties or functions equivalent to `isEven` and `isOdd` are avail The `isEven` and `isOdd` properties are also fewer characters wide than the remainder approach (maximum 7 characters for `.isEven` vs 9 for ` % 2 == 0`) which saves horizontal space while being clearer in intent. ```swift -// Gray background for even rows, white for odd. -view.backgroundColor = indexPath.row.isEven ? .gray : .white +// UITableView alternating row colour +cell.contentView.backgroundColor = indexPath.row.isEven ? .gray : .white // Codable.swift.gyb in apple/swift guard count.isEven else { throw DecodingError.dataCorrupted(...) } @@ -67,13 +69,13 @@ public static func random(using generator: inout T) -> _sanityCheck(bytes > 0 && bytes.isDivisible(by: 4), "capacity must be multiple of 4 bytes") ``` -**Discoverability:** IDEs will be able to suggest `isEven`, `isOdd`, and `isDivisible` as part of autocomplete which will aid discoverability. It will also be familiar to users coming from languages that also support functionality similar to `isEven` and `isOdd`. +**Discoverability:** IDEs will be able to suggest `isEven`, `isOdd`, and `isDivisible` as part of autocomplete on integer types which will aid discoverability. It will also be familiar to users coming from languages that also support functionality similar to `isEven` and `isOdd`. **Trivially composable:** It would be relatively easy to reproduce the proposed functionality in user code but there would be benefits to having a standard implementation. It may not be obvious to some users exactly which protocol these properties belong on (`Int`?, `SignedInteger`?, `FixedWidthInteger`?, `BinaryInteger`?). This inconsistency can be seen in a [popular Swift utility library](https://github.com/SwifterSwift/SwifterSwift/blob/master/Sources/Extensions/SwiftStdlib/SignedIntegerExtensions.swift#L28) which defines `isEven` and `isOdd` on `SignedInteger` which results in the properties being inaccessible for unsigned integers. -Testing the parity of integers is also relatively common in sample code and educational usage. In this context, it’s usually not appropriate for an author to introduce this functionality (unless they are teaching extensions!) in order to avoid distracting from the main task at hand (e.g. filter, map, etc). It may also be the same situation for authoring test code: it'd be used if it were there but it's not worth the overhead of defining it manually. +Testing the parity of integers is also relatively common in sample code and educational usage. In this context, it’s usually not appropriate for an author to introduce this functionality (unless they are teaching extensions!) in order to avoid distracting from the main task at hand (e.g. filter, map, etc). It may also be the same situation for authoring test code: it'd be used if it existed but it's not worth the overhead of defining it manually. -This functionality will also eliminate the need to use the remainder operator or bitwise AND 1 when querying the divisibility of an integer. +This functionality will also eliminate the need to use the remainder operator or bitwise AND when querying the divisibility of an integer. **Correctness:** It isn't [uncommon](https://github.com/apple/swift/blob/master/stdlib/public/core/RangeReplaceableCollection.swift#L1090) to see tests for oddness written as `value % 2 == 1` in Swift, but this is incorrect for negative odd values. The semantics of the `%` operator vary between programming languages, such as Ruby and Python, which can be surprising. ``` @@ -86,7 +88,7 @@ This functionality will also eliminate the need to use the remainder operator or -7 % 2 == 1 // true ``` -There is also a minor correctness risk in misinterpreting something like `value % 2 == 0`, particularly when used in a more complex statement, when compared to `value.isEven`. +There is also a minor correctness risk in misinterpreting something like `value % 2 == 0`, particularly when used in a more complex statement, when compared to `value.isEven`, e.g. `bytes > 0 && bytes % 4 == 0`. **Performance:** It's _possible_ that `isDivisible` could be implemented in a more performant way than `% denominator == 0` for more complex types, such as a BigInteger/BigNum type. @@ -97,6 +99,9 @@ The addition of `isEven` and `isOdd` likely won’t have a major positive impact Add two computed properties, `isEven` and `isOdd`, and a function `isDivisible` to the `BinaryInteger` protocol. ```swift +// Integers.swift.gyb +// On protocol BinaryInteger + @inlinable /// A Boolean value indicating whether this value is even. /// @@ -114,7 +119,15 @@ Add two computed properties, `isEven` and `isOdd`, and a function `isDivisible` } @inlinable + /// Returns a Boolean value that indicates whether the integer is divisible + /// by another integer. /// + /// A number is divisible by another number if it is capable of being divided + /// by the other number without a remainder. + /// + /// - Parameter denominator: An integer to test divisibility by. + /// - Returns: `true` if the integer is divisible by `denominator`; + /// otherwise, `false`. func isDivisible(by denominator: Self) -> Bool { return self % denominator == 0 } @@ -138,4 +151,39 @@ N/A ## Alternatives considered -N/A +### `isEvenlyDivisible` instead of `isDivisible` + +There was some concern that `isDivisible` was not clearly named, but the majority of pitch respondents found `isDivisible` acceptable. + +### Only `isEven/isOdd` or only `isDivisible`. + +During the pitch phase there were discussions about including only one of `isEven/isOdd` or `isDivisible` in the proposal. + +On the one hand there were concerns that `isEven/isOdd` would not provide enough utility to justify inclusion into the standard library and that `isDivisible` was preferable as it was more general. `isEven/isOdd` are also trivial inverses of each other which Swift, as a rule, doesn't include in the standard library. + +On the other hand there was some unscientific analysis that indicated that even/oddness accounted for 60-80% of the operations in which the result of the remainder operator was compared against zero. This lent some support to including `isEven/isOdd` over `isDivisible`. There is also more precedence in other languages for including `isEven/isOdd` over `isDivisible`. + +The authors decided that both were worthy of including in the proposal. Odd and even numbers have had special [shorthand labels](http://mathforum.org/library/drmath/view/65413.html) for thousands of years and are used frequently enough to justify the small additional weight `isEven/isOdd` would add to the standard library. `isDivisible` will greatly improve clarity and readability for arbitrary divisibility checks and also avoid potentially surprising `%` operator semantics with negative values. + +## Appendix + +### Other example uses in code (and beyond) + +* `% 2 == 0` appears 63 times in the [Apple/Swift](https://github.com/apple/swift) repository. +* [Initializing a cryptographic cipher](https://github.com/krzyzanowskim/CryptoSwift/blob/31efdd85ceb4190ee358a0516c6e82d8fd7b9377/Sources/CryptoSwift/Rabbit.swift#L89) +* Colouring a checker/chess board and [determining piece colour](https://github.com/nvzqz/Sage/blob/dec5edd97ba45d46bf94bc2e264d3ce2be6404ad/Sources/Piece.swift#L276) +* Multiple occurrences in cpp Boost library. [Example: handleResizingVertex45](https://www.boost.org/doc/libs/1_62_0/boost/polygon/polygon_45_set_data.hpp) +* Alternating bar colours in a chart +* [VoronoiFilter implementation](https://github.com/BradLarson/GPUImage/blob/master/framework/Source/GPUImageJFAVoronoiFilter.m#L428) and [PoissonBlendFilter](https://github.com/BradLarson/GPUImage/blob/167b0389bc6e9dc4bb0121550f91d8d5d6412c53/framework/Source/GPUImagePoissonBlendFilter.m#L142) +* [Image blurring library](https://github.com/nicklockwood/FXBlurView/blob/master/FXBlurView/FXBlurView.m#L58) +* UPC check digit calculation [(spec)](http://www.gs1.org/how-calculate-check-digit-manually) (values in odd digit indices are multiplied by 3) +* [Precondition check in sqlite function](https://github.com/sqlcipher/sqlcipher/blob/c6f709fca81c910ba133aaf6330c28e01ccfe5f8/src/crypto_impl.c#L1296) +* [Alternating UITableView cell background style](https://github.com/alloy/HockeySDK-CocoaPods/blob/master/Pods/HockeySDK/Classes/BITFeedbackListViewController.m#L502). NSTableView [has built in support](https://developer.apple.com/documentation/appkit/nstableview/1533967-usesalternatingrowbackgroundcolo?language=objc) for this. +* [Barcode reading](https://github.com/TheLevelUp/ZXingObjC/blob/d952cc02beb948ab49832661528c5e3e4953885e/ZXingObjC/oned/rss/expanded/ZXRSSExpandedReader.m#L449) +* [CSS row and column styling](https://www.w3.org/Style/Examples/007/evenodd.en.html) with `nth-child(even)` and `nth-child(odd)` (h/t @brentdax) +* Various test code + +Some _really_ real-world examples: +* [Printing even or odd pages only](https://i.stack.imgur.com/TE4Xn.png) +* New [Hearthstone even/odd cards](https://blizzardwatch.com/2018/03/16/hearthstones-new-even-odd-cards-going-shake-meta/) +* A variety of [even-odd rationing](https://en.m.wikipedia.org/wiki/Odd–even_rationing) rules: [parking rules](https://www.icgov.org/city-government/departments-and-divisions/transportation-services/parking/oddeven-parking), [water usage restrictions](https://vancouver.ca/home-property-development/understanding-watering-restrictions.aspx), [driving restrictions](https://auto.ndtv.com/news/odd-even-in-delhi-5-things-you-need-to-know-1773720) From afcb9481eb328ac5d0a2f74e36643400bada7b46 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 6 Aug 2018 15:25:55 -0400 Subject: [PATCH 0773/4563] Extend review until Tuesday --- proposals/0222-lazy-compactmap-sequence.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0222-lazy-compactmap-sequence.md b/proposals/0222-lazy-compactmap-sequence.md index 7176d4b098..648713884d 100644 --- a/proposals/0222-lazy-compactmap-sequence.md +++ b/proposals/0222-lazy-compactmap-sequence.md @@ -3,7 +3,7 @@ * Proposal: [SE-0222](0222-lazy-compactmap-sequence.md) * Authors: [TellowKrinkle](https://github.com/TellowKrinkle), [Johannes Weiß](https://github.com/weissi) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (July 30th...August 5th, 2018)** +* Status: **Active review (July 30th...August 7th, 2018)** * Implementation: [apple/swift#14841](https://github.com/apple/swift/pull/14841) ## Introduction From f135b3ff3b06ad6b221b388d6a42e8338d2e71e1 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 6 Aug 2018 13:10:07 -0700 Subject: [PATCH 0774/4563] kick off 0220 --- proposals/0220-count-where.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0220-count-where.md b/proposals/0220-count-where.md index 471ca1cc7f..c178519a3d 100644 --- a/proposals/0220-count-where.md +++ b/proposals/0220-count-where.md @@ -3,7 +3,7 @@ * Proposal: [SE-0220](0220-count-where.md) * Author: [Soroush Khanlou](https://github.com/khanlou) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Scheduled for review (August 6...12)** +* Status: **Active review (August 6...12)** * Implementation: [apple/swift#16099](https://github.com/apple/swift/pull/16099) ## Introduction From f17ff60a227d8bcb04c61eaf791f493e07e96aef Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Mon, 6 Aug 2018 16:24:37 -0500 Subject: [PATCH 0775/4563] Add withUnsafeMutableBufferPointerToFullCapacity, and other changes --- .../0000-array-uninitialized-initializer.md | 244 ++++++++++++------ 1 file changed, 167 insertions(+), 77 deletions(-) diff --git a/proposals/0000-array-uninitialized-initializer.md b/proposals/0000-array-uninitialized-initializer.md index 55f29d514e..3ad607c1f2 100644 --- a/proposals/0000-array-uninitialized-initializer.md +++ b/proposals/0000-array-uninitialized-initializer.md @@ -4,13 +4,13 @@ * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: TBD * Status: **Awaiting review** -* Implementation: *Exists as an underscored API* +* Implementation: https://github.com/apple/swift/pull/17389 * Bug: [SR-3087](https://bugs.swift.org/browse/SR-3087) ## Introduction -This proposal suggests a new initializer for `Array` and `ContiguousArray` -that would provide access to a newly created array's uninitialized storage buffer. +This proposal suggests a new initializer and method for `Array` and `ContiguousArray` +that provide access to an array's uninitialized storage buffer. Swift-evolution thread: [https://forums.swift.org/t/array-initializer-with-access-to-uninitialized-buffer/13689](https://forums.swift.org/t/array-initializer-with-access-to-uninitialized-buffer/13689) @@ -33,24 +33,33 @@ Even if we avoid initialization by manually allocating the memory using an `Unsa there's no way to convert that buffer into an array without copying the contents. There simply isn't a way to implement this particular algorithm with maximum efficiency in Swift. +We also see this limitation when working with C APIs +that fill a buffer with an unknown number of elements and return the count. +The workarounds are the same as above: +either initialize an array before passing it +or copy the elements from an unsafe mutable buffer into an array after calling. + ## Proposed solution Adding a new `Array` initializer -that lets a program work with the uninitialized buffer +that lets a program work with an uninitialized buffer, +and a method for accessing an existing array's buffer +of both initialized and uninitialized memory, would fill in this missing functionality. -This new initializer takes a closure that operates on an `UnsafeMutableBufferPointer` + +The new initializer takes a closure that operates on an `UnsafeMutableBufferPointer` and an `inout` count of initialized elements. This closure has access to the uninitialized contents of the newly created array's storage, and must set the intialized count of the array before exiting. ```swift -var myArray = Array(unsafeUninitializedCapacity: 10) { buffer, initializedCount in +var myArray = Array(unsafeUninitializedCapacity: 10) { buffer in for x in 1..<5 { buffer[x] = x } buffer[0] = 10 - initializedCount = 5 + return 5 } // myArray == [10, 1, 2, 3, 4] ``` @@ -60,7 +69,8 @@ as an extension to the `Collection` protocol, without any unnecessary copies: ```swift func stablyPartitioned(by belongsInFirstPartition: (Element) -> Bool) -> [Element] { - let result = Array(unsafeUninitializedCapacity: count) { buffer, initializedCount in + let result = Array(unsafeUninitializedCapacity: count) { + buffer, initializedCount in var low = buffer.baseAddress! var high = low + buffer.count for element in self { @@ -81,36 +91,36 @@ func stablyPartitioned(by belongsInFirstPartition: (Element) -> Bool) -> [Elemen } ``` +The + ## Detailed design -The new initializer is added to both `Array` and `ContiguousArray`. +The new initializer and method are added to both `Array` and `ContiguousArray`. ```swift -/// Creates an array with the specified capacity, then calls the given -/// closure with a buffer covering the array's uninitialized memory. +/// Creates an array with the specified capacity, then calls the given closure +/// with a buffer covering the array's uninitialized memory. /// -/// Inside the closure, set the `initializedCount` parameter to the number of -/// elements that are initialized by the closure. The memory in the range -/// `buffer[0.. Void ) rethrows -``` - -In particular, -note that the buffer is guaranteed to address only the specified capacity, -though the final array may have a capacity greater than that as an optimization. - -## Source compatibility - -This is an additive change to the standard library, -so there is no effect on source compatibility. -## Effect on ABI stability - -This addition has no effect on ABI stability. +/// Calls the given closure with a pointer to the full capacity of the +/// array's mutable contiguous storage. +/// +/// - Parameters: +/// - capacity: The capacity to guarantee for the array. `capacity` must +/// be greater than or equal to the array's current `count`. +/// - body: A closure that can modify or deinitialize existing +/// elements or initialize new elements. +/// - Parameters: +/// - buffer: An unsafe mutable buffer of the array's full storage, +/// including any uninitialized capacity after the initialized +/// elements. Only the elements in `buffer[0..( + capacity: Int, + _ body: ( + _ buffer: inout UnsafeMutableBufferPointer, + _ initializedCount: inout Int + ) throws -> Result +) rethrows -> Result +``` -## Effect on API resilience +### Specifying a capacity -The additional APIs will be a permanent part of the standard library, -and will need to remain public API. +Both the initializer and the mutating method take +the specific capacity that a user wants to work with as a parameter. +In each case, the buffer passed to the closure has a count +that is exactly the same as the specified capacity, +even if the ultimate capacity of the new or existing array is larger. +This helps avoid bugs where a user assumes that the capacity they observe +before calling the mutating method would match the size of the buffer. -## Alternatives considered +The method requires that the capacity specified be at least the current `count` of the array +to prevent nonsensical operations, +like reducing the size of the array from the middle. +That is, this will result in a runtime error: -### Creating an array from a buffer +```swift +var a = Array(1...10) +a.withUnsafeMutableBufferPointerToFullCapacity(capacity: 5) { ... } +``` -An `Array` initializer that simply converts an `UnsafeMutableBufferPointer` -into an array's backing storage seems like it would be another solution. -However, an array's storage includes information -about the count and capacity at the beginning of its buffer, -so an `UnsafeMutableBufferPointer` created from scratch isn't usable. +### Guarantees after throwing -### Mutable version +If the closure parameter to either the initializer +or the mutating method throws, +the `initializedCount` value at the time an error is thrown is assumed to be correct. +This means that a user who needs to throw from inside the closure has one of two options. +Before throwing, they must: -A draft version of this proposal centered around -a mutating `withFullCapacityUnsafeMutableBufferPointer` method -instead of the initializer. -However, that approach poses a problem: -the buffer passed to the closure might have an unpredictable size. +1. deinitialize any newly initialized instances or re-initialize any deinitialized instances, or +2. update `initializedCount` to the new count. -```swift -let c = myArray.capacity -myArray.withFullCapacityUnsafeMutableBufferPointer { buffer, initializedCount in - // If `myArray` is shared, the storage gets cloned before here, so - // `buffer.count` may be greater than, less than, or equal to `c`. -} +In either case, +the postconditions that `buffer[0.. Void ) rethrows { var buffer = UnsafeMutableBufferPointer.allocate(capacity: unsafeUninitializedCapacity) - defer { buffer.deallocate() } var initializedCount = 0 + defer { + buffer.baseAddress?.deinitialize(count: initializedCount) + buffer.deallocate() + } + try initializer(&buffer, &initializedCount) - self = Array(buffer[0..( + capacity: Int, + _ body: ( + _ buffer: inout UnsafeMutableBufferPointer, + _ initializedCount: inout Int + ) throws -> Result + ) rethrows -> Result { + var buffer = UnsafeMutableBufferPointer.allocate(capacity: capacity) + buffer.initialize(from: self) + var initializedCount = self.count + defer { + buffer.baseAddress?.deinitialize(count: initializedCount) + buffer.deallocate() + } + + let result = try body(&buffer, &initializedCount) + self = Array(buffer[.. Date: Mon, 30 Jul 2018 18:28:20 -0700 Subject: [PATCH 0776/4563] SE-0192 was implemented in Swift 5 It's actually implemented in Swift 4.2 too, but we decided not to turn on the diagnostics there. I went with "Swift 5" to not confuse people. --- proposals/0192-non-exhaustive-enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0192-non-exhaustive-enums.md b/proposals/0192-non-exhaustive-enums.md index cef602e07b..f5627eafa2 100644 --- a/proposals/0192-non-exhaustive-enums.md +++ b/proposals/0192-non-exhaustive-enums.md @@ -3,7 +3,7 @@ * Proposal: [SE-0192](0192-non-exhaustive-enums.md) * Author: [Jordan Rose](https://github.com/jrose-apple) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Accepted with Revision** +* Status: **Implemented (Swift 5)** * Implementation: [apple/swift#14945](https://github.com/apple/swift/pull/14945) * Previous revision: [1](https://github.com/apple/swift-evolution/blob/a773d07ff4beab8b7855adf0ac56d1e13bb7b44c/proposals/0192-non-exhaustive-enums.md), [2 (informal)](https://github.com/jrose-apple/swift-evolution/blob/57dfa2408fe210ed1d5a1251f331045b988ee2f0/proposals/0192-non-exhaustive-enums.md), [3](https://github.com/apple/swift-evolution/blob/af284b519443d3d985f77cc366005ea908e2af59/proposals/0192-non-exhaustive-enums.md) * Pre-review discussion: [Enums and Source Compatibility](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170807/038663.html), with additional [orphaned thread](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170911/039787.html) From 7d5cf5a9c26e33f3a3f0ee39231cb11e9e809a6b Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 10 Aug 2018 11:30:56 -0500 Subject: [PATCH 0777/4563] Address feedback from the forum discussion Thanks to taylorswift, jawbroken, and Karl --- .../0000-array-uninitialized-initializer.md | 76 +++++++++++-------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/proposals/0000-array-uninitialized-initializer.md b/proposals/0000-array-uninitialized-initializer.md index 3ad607c1f2..f4fce47f93 100644 --- a/proposals/0000-array-uninitialized-initializer.md +++ b/proposals/0000-array-uninitialized-initializer.md @@ -54,12 +54,12 @@ of the newly created array's storage, and must set the intialized count of the array before exiting. ```swift -var myArray = Array(unsafeUninitializedCapacity: 10) { buffer in +var myArray = Array(unsafeUninitializedCapacity: 10) { buffer, initializedCount in for x in 1..<5 { buffer[x] = x } buffer[0] = 10 - return 5 + initializedCount = 5 } // myArray == [10, 1, 2, 3, 4] ``` @@ -68,13 +68,13 @@ With this new initializer, it's possible to implement the stable partition as an extension to the `Collection` protocol, without any unnecessary copies: ```swift -func stablyPartitioned(by belongsInFirstPartition: (Element) -> Bool) -> [Element] { - let result = Array(unsafeUninitializedCapacity: count) { +func stablyPartitioned(by belongsInFirstPartition: (Element) throws -> Bool) rethrows -> [Element] { + return try Array(unsafeUninitializedCapacity: count) { buffer, initializedCount in var low = buffer.baseAddress! var high = low + buffer.count for element in self { - if belongsInFirstPartition(element) { + if try belongsInFirstPartition(element) { low.initialize(to: element) low += 1 } else { @@ -87,12 +87,9 @@ func stablyPartitioned(by belongsInFirstPartition: (Element) -> Bool) -> [Elemen buffer[highIndex...].reverse() initializedCount = buffer.count } - return result } ``` -The - ## Detailed design The new initializer and method are added to both `Array` and `ContiguousArray`. @@ -101,7 +98,7 @@ The new initializer and method are added to both `Array` and `ContiguousArray`. /// Creates an array with the specified capacity, then calls the given closure /// with a buffer covering the array's uninitialized memory. /// -/// The closure must return set its second parameter to a number `c`, the number +/// The closure must set its second parameter to a number `c`, the number /// of elements that are initialized. The memory in the range `buffer[0.. Void ) rethrows -/// Calls the given closure with a pointer to the full capacity of the -/// array's mutable contiguous storage. +/// Calls the given closure with a buffer of the array's mutable contiguous +/// storage, reserving the specified capacity if necessary. +/// +/// The closure must set its second parameter to a number `c`, the number +/// of elements that are initialized. The memory in the range `buffer[0..( +public mutating func withUnsafeMutableBufferPointerToStorage( capacity: Int, _ body: ( _ buffer: inout UnsafeMutableBufferPointer, @@ -173,7 +175,7 @@ That is, this will result in a runtime error: ```swift var a = Array(1...10) -a.withUnsafeMutableBufferPointerToFullCapacity(capacity: 5) { ... } +a.withUnsafeMutableBufferPointerToStorage(capacity: 5) { ... } ``` ### Guarantees after throwing @@ -208,14 +210,22 @@ Because trailing closures are commonly used, it's important to include those terms in the initial argument label, such that they're always visible at the use site. -#### `withUnsafeMutableBufferPointerToFullCapacity(capacity:_:)` +#### `withUnsafeMutableBufferPointerToStorage(capacity:_:)` The mutating method is closely linked to the existing methods for accessing an array's storage via mutable buffer pointer, -but has the important distinction of including access to uninitialized elements. +but has the important distinction of including access +to not just the elements of the array, +but also the uninitialized portion of the array's storage. Extending the name of the closest existing method (`withUnsafeMutableBufferPointer`) to mark the distinction makes the relationship (hopefully) clear. +**Suggested alternatives:** + +- `withUnsafeMutableBufferPointerToReservedCapacity(_:_:)` +- `withUnsafeMutableBufferPointer(reservingCapacity:_:)` +- `withUnsafeMutableBufferPointerToFullCapacity(capacity:_:)` + #### Unused terminology This proposal leaves out wording that would reference two other relevant concepts: @@ -244,8 +254,6 @@ while this initializer performs the conversion as soon as the closure executes. As above, this term doesn't seem appropriate for use with this new API. - - ## Source compatibility This is an additive change to the standard library, @@ -269,6 +277,17 @@ instead of using an `inout` parameter. This proposal uses the parameter instead, so that the method and initializer use the same closure type. +In addition, the throwing behavior described above requires that +the initialized count be set as an `inout` parameter instead of as a return value. +Not every `Element` type can be trivially initialized, +so a user that deinitializes some elements and then needs to throw an error would be stuck. +(This is only an issue with the mutating method.) +Removing the `throws` capability from the closure +would solve this problem and simplify the new APIs' semantics, +but would be inconsistent with the other APIs in this space +and would make them unsuitable to use as building blocks +for higher-level operations like `stablyPartitioned(by:)`. + ### Creating an array from a buffer An `Array` initializer that simply converts an `UnsafeMutableBufferPointer` @@ -293,20 +312,11 @@ extension Array { _ initializedCount: inout Int ) throws -> Void ) rethrows { - var buffer = UnsafeMutableBufferPointer.allocate(capacity: unsafeUninitializedCapacity) - var initializedCount = 0 - defer { - buffer.baseAddress?.deinitialize(count: initializedCount) - buffer.deallocate() - } - - try initializer(&buffer, &initializedCount) self = [] - self.reserveCapacity(unsafeUninitializedCapacity) - self.append(contentsOf: buffer[..( + public mutating func withUnsafeMutableBufferPointerToStorage( capacity: Int, _ body: ( _ buffer: inout UnsafeMutableBufferPointer, From b7107f4776828fdf4baa0ed62201127d8fa763de Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Sat, 11 Aug 2018 10:40:21 -0500 Subject: [PATCH 0778/4563] Minor revision --- proposals/0000-array-uninitialized-initializer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-array-uninitialized-initializer.md b/proposals/0000-array-uninitialized-initializer.md index f4fce47f93..0f503c29ff 100644 --- a/proposals/0000-array-uninitialized-initializer.md +++ b/proposals/0000-array-uninitialized-initializer.md @@ -285,7 +285,7 @@ so a user that deinitializes some elements and then needs to throw an error woul Removing the `throws` capability from the closure would solve this problem and simplify the new APIs' semantics, but would be inconsistent with the other APIs in this space -and would make them unsuitable to use as building blocks +and would make them more difficult to use as building blocks for higher-level operations like `stablyPartitioned(by:)`. ### Creating an array from a buffer From aeb2fadfb6c6c3f70826dd44990bd3058649efe4 Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Sun, 12 Aug 2018 22:15:48 -0700 Subject: [PATCH 0779/4563] Changed `isDivisible` to `isMultiple` --- ...-binaryinteger-iseven-isodd-isdivisible.md | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md b/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md index a5ef19a907..65a0fe4023 100644 --- a/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md +++ b/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md @@ -1,4 +1,4 @@ -# Adding `isEven`, `isOdd`, `isDivisible` to `BinaryInteger` +# Adding `isEven`, `isOdd`, `isMultiple` to `BinaryInteger` * Proposal: [SE-NNNN](NNNN-binaryinteger-iseven-isodd.md) * Authors: [Robert MacEachern](https://robmaceachern.com), [SiliconUnicorn](https://forums.swift.org/u/siliconunicorn/summary) @@ -7,21 +7,21 @@ ## Introduction -This proposal adds `var isEven: Bool`, `var isOdd: Bool`, and `func isDivisible(by denominator: Self) -> Bool` to the `BinaryInteger` protocol. `isEven` and `isOdd` are convenience properties for querying the [parity](https://en.wikipedia.org/wiki/Parity_(mathematics)) of the integer and `isDivisible` is a more general function to determine the divisibility of an integer by an arbitrary denominator. +This proposal adds `var isEven: Bool`, `var isOdd: Bool`, and `func isMultiple(of divisor: Self) -> Bool` to the `BinaryInteger` protocol. `isEven` and `isOdd` are convenience properties for querying the [parity](https://en.wikipedia.org/wiki/Parity_(mathematics)) of the integer and `isMultiple` is a more general function to determine whether an integer is a multiple of another integer. Swift-evolution thread: [Even and Odd Integers](https://forums.swift.org/t/even-and-odd-integers/11774) ## Motivation -It is sometimes necessary to know whether or not an integer is divisible by a particular value. The most common case is testing for divisibility by 2 (even and oddness). +It is sometimes necessary to know whether or not an integer is a multiple of another. The most common case is testing if a value is a multiple of 2 (even and oddness). -**Commonality:** Testing divisibility shows up in a surprising number of contexts including UI code, algorithm implementations (often in the form of assertions), tests, benchmarks, documentation and tutorial/educational code. +**Commonality:** Testing if a value is a multiple of another shows up in a surprising number of contexts including UI code, algorithm implementations (often in the form of assertions), tests, benchmarks, documentation and tutorial/educational code. -Currently, the most common way to test a value for divisibility is by using the remainder operator (`%`) checking for a remainder of zero: `12 % 2 == 0 // returns true. 12 is divisible by 2`. Similarly, testing for indivisibility is done by checking for a remainder other than zero: `13 % 2 != 0 // returns true. 13 is not divisible by 2`. +Currently, the most common way to test if a value is a multiple is by using the remainder operator (`%`) checking for a remainder of zero: `12 % 2 == 0 // returns true. 12 is a multiple of 2`. Similarly, testing that a value is _not_ a multiple of another is done by checking for a remainder other than zero: `13 % 2 != 0 // returns true. 13 is not a multiple of 2`. Alternatively, it is also possible to use the bitwise AND operator (`&`) to check the even/oddness of a value: `12 & 1 == 0 // returns true`. -Some examples of divisibility in code (see more in appendix): +Some examples of testing multiples in code (see more in appendix): ```swift // UITableView alternating row colour @@ -66,10 +66,10 @@ public static func random(using generator: inout T) -> } // KeyPath.swift in apple/swift -_sanityCheck(bytes > 0 && bytes.isDivisible(by: 4), "capacity must be multiple of 4 bytes") +_sanityCheck(bytes > 0 && bytes.isMultiple(of: 4), "capacity must be multiple of 4 bytes") ``` -**Discoverability:** IDEs will be able to suggest `isEven`, `isOdd`, and `isDivisible` as part of autocomplete on integer types which will aid discoverability. It will also be familiar to users coming from languages that also support functionality similar to `isEven` and `isOdd`. +**Discoverability:** IDEs will be able to suggest `isEven`, `isOdd`, and `isMultiple` as part of autocomplete on integer types which will aid discoverability. It will also be familiar to users coming from languages that also support functionality similar to `isEven` and `isOdd`. **Trivially composable:** It would be relatively easy to reproduce the proposed functionality in user code but there would be benefits to having a standard implementation. It may not be obvious to some users exactly which protocol these properties belong on (`Int`?, `SignedInteger`?, `FixedWidthInteger`?, `BinaryInteger`?). This inconsistency can be seen in a [popular Swift utility library](https://github.com/SwifterSwift/SwifterSwift/blob/master/Sources/Extensions/SwiftStdlib/SignedIntegerExtensions.swift#L28) which defines `isEven` and `isOdd` on `SignedInteger` which results in the properties being inaccessible for unsigned integers. @@ -88,15 +88,17 @@ This functionality will also eliminate the need to use the remainder operator or -7 % 2 == 1 // true ``` +The `%` operator will also trap when the righthand side is zero. The proposed solution does not. + There is also a minor correctness risk in misinterpreting something like `value % 2 == 0`, particularly when used in a more complex statement, when compared to `value.isEven`, e.g. `bytes > 0 && bytes % 4 == 0`. -**Performance:** It's _possible_ that `isDivisible` could be implemented in a more performant way than `% denominator == 0` for more complex types, such as a BigInteger/BigNum type. +**Performance:** It's _possible_ that `isMultiple` could be implemented in a more performant way than `% divisor == 0` for more complex types, such as a BigInteger/BigNum type. The addition of `isEven` and `isOdd` likely won’t have a major positive impact on performance but it should not introduce any additional overhead thanks to `@inlineable`. ## Proposed solution -Add two computed properties, `isEven` and `isOdd`, and a function `isDivisible` to the `BinaryInteger` protocol. +Add two computed properties, `isEven` and `isOdd`, and a function `isMultiple` to the `BinaryInteger` protocol. ```swift // Integers.swift.gyb @@ -105,31 +107,34 @@ Add two computed properties, `isEven` and `isOdd`, and a function `isDivisible` @inlinable /// A Boolean value indicating whether this value is even. /// - /// An integer is even if it is divisible by two. + /// An integer is even if it is a multiple of two. public var isEven: Bool { - return isDivisible(by: 2) + return isMultiple(of: 2) } @inlinable /// A Boolean value indicating whether this value is odd. /// - /// An integer is odd if it is not divisible by two. + /// An integer is odd if it is not a multiple of two. public var isOdd: Bool { - return !isDivisible(by: 2) + return !isEven } @inlinable - /// Returns a Boolean value that indicates whether the integer is divisible - /// by another integer. + /// Returns a Boolean value that indicates whether the integer is a + /// multiple of another integer. + /// + /// For two integers a and b, b is a multiple of a if b = na for some integer n. /// - /// A number is divisible by another number if it is capable of being divided - /// by the other number without a remainder. + /// - Note: 0 is a multiple of every integer. /// - /// - Parameter denominator: An integer to test divisibility by. - /// - Returns: `true` if the integer is divisible by `denominator`; + /// - Parameter divisor: The integer to test. + /// - Returns: `true` if `self` is a multiple of `divisor`; /// otherwise, `false`. - func isDivisible(by denominator: Self) -> Bool { - return self % denominator == 0 + public func isMultiple(of divisor: Self) -> Bool { + guard self != 0 else { return true } + guard divisor != 0 else { return false } + return self % divisor == 0 } ``` @@ -151,19 +156,26 @@ N/A ## Alternatives considered -### `isEvenlyDivisible` instead of `isDivisible` +### `isDivisible` instead of `isMultiple` + +The original discussions during the pitch phase where related to an `isDivisible(by:)` alternative to `isMultiple`. [Issues](https://forums.swift.org/t/even-and-odd-integers/11774/83) related to divisibility and division by zero were discussed and `isMultiple` was proposed as a solution that 1) avoids trapping on zero, and 2) avoids confusion where a value that is _divisible_ by zero would not be _dividable_ in Swift. e.g. -There was some concern that `isDivisible` was not clearly named, but the majority of pitch respondents found `isDivisible` acceptable. +``` +let y = 0 +if 10.isDivisible(by: y) { + let val = 10 / y // traps +} +``` -### Only `isEven/isOdd` or only `isDivisible`. +### Only `isEven/isOdd` or only `isMultiple`. -During the pitch phase there were discussions about including only one of `isEven/isOdd` or `isDivisible` in the proposal. +During the pitch phase there were discussions about including only one of `isEven/isOdd` or `isMultiple` in the proposal. -On the one hand there were concerns that `isEven/isOdd` would not provide enough utility to justify inclusion into the standard library and that `isDivisible` was preferable as it was more general. `isEven/isOdd` are also trivial inverses of each other which Swift, as a rule, doesn't include in the standard library. +On the one hand there were concerns that `isEven/isOdd` would not provide enough utility to justify inclusion into the standard library and that `isMultiple` was preferable as it was more general. `isEven/isOdd` are also trivial inverses of each other which Swift, as a rule, doesn't include in the standard library. -On the other hand there was some unscientific analysis that indicated that even/oddness accounted for 60-80% of the operations in which the result of the remainder operator was compared against zero. This lent some support to including `isEven/isOdd` over `isDivisible`. There is also more precedence in other languages for including `isEven/isOdd` over `isDivisible`. +On the other hand there was some unscientific analysis that indicated that even/oddness accounted for 60-80% of the operations in which the result of the remainder operator was compared against zero. This lent some support to including `isEven/isOdd` over `isMultiple`. There is also more precedence in other languages for including `isEven/isOdd` over `isMultiple`. -The authors decided that both were worthy of including in the proposal. Odd and even numbers have had special [shorthand labels](http://mathforum.org/library/drmath/view/65413.html) for thousands of years and are used frequently enough to justify the small additional weight `isEven/isOdd` would add to the standard library. `isDivisible` will greatly improve clarity and readability for arbitrary divisibility checks and also avoid potentially surprising `%` operator semantics with negative values. +The authors decided that both were worthy of including in the proposal. Odd and even numbers have had special [shorthand labels](http://mathforum.org/library/drmath/view/65413.html) for thousands of years and are used frequently enough to justify the small additional weight `isEven/isOdd` would add to the standard library. `isMultiple` will greatly improve clarity and readability for arbitrary divisibility checks and also avoid potentially surprising `%` operator semantics with negative values. ## Appendix From c2134898392fe91399bd0e217c303cfc32f54de8 Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Sun, 12 Aug 2018 22:16:41 -0700 Subject: [PATCH 0780/4563] Renamed proposal file --- ...divisible.md => nnnn-binaryinteger-iseven-isodd-ismultiple.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{nnnn-binaryinteger-iseven-isodd-isdivisible.md => nnnn-binaryinteger-iseven-isodd-ismultiple.md} (100%) diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md similarity index 100% rename from proposals/nnnn-binaryinteger-iseven-isodd-isdivisible.md rename to proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md From 42ad2e8769af26e727481c0a9ad5189f0858377f Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 13 Aug 2018 09:38:23 -0700 Subject: [PATCH 0781/4563] Inaugurate SE-0223, array uninitialized buffer initializer --- ...itializer.md => 0223-array-uninitialized-initializer.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{0000-array-uninitialized-initializer.md => 0223-array-uninitialized-initializer.md} (98%) diff --git a/proposals/0000-array-uninitialized-initializer.md b/proposals/0223-array-uninitialized-initializer.md similarity index 98% rename from proposals/0000-array-uninitialized-initializer.md rename to proposals/0223-array-uninitialized-initializer.md index 0f503c29ff..292a3d261f 100644 --- a/proposals/0000-array-uninitialized-initializer.md +++ b/proposals/0223-array-uninitialized-initializer.md @@ -1,9 +1,9 @@ # Accessing an Array's Uninitialized Buffer -* Proposal: [SE-NNNN](NNNN-array-uninitialized-buffer.md) +* Proposal: [SE-0223](0223-array-uninitialized-buffer.md) * Author: [Nate Cook](https://github.com/natecook1000) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Joe Groff](https://github.com/jckarter) +* Status: **Active review (August 13...August 19, 2018)** * Implementation: https://github.com/apple/swift/pull/17389 * Bug: [SR-3087](https://bugs.swift.org/browse/SR-3087) From 3316332a3b03c7aa664fd340dac6b343ff4bce3f Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Mon, 13 Aug 2018 09:50:08 -0700 Subject: [PATCH 0782/4563] Replace `@inlinable` with `@_transparent` --- proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md index 65a0fe4023..b1563888d8 100644 --- a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md +++ b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md @@ -104,7 +104,7 @@ Add two computed properties, `isEven` and `isOdd`, and a function `isMultiple` t // Integers.swift.gyb // On protocol BinaryInteger - @inlinable + @_transparent /// A Boolean value indicating whether this value is even. /// /// An integer is even if it is a multiple of two. @@ -112,7 +112,7 @@ Add two computed properties, `isEven` and `isOdd`, and a function `isMultiple` t return isMultiple(of: 2) } - @inlinable + @_transparent /// A Boolean value indicating whether this value is odd. /// /// An integer is odd if it is not a multiple of two. From a9d9d311ee292af1962da2d19b9d3afe9cc956e3 Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Mon, 13 Aug 2018 09:51:01 -0700 Subject: [PATCH 0783/4563] Use `_lowWord` in implementation of `isEven` --- proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md index b1563888d8..ce3000b625 100644 --- a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md +++ b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md @@ -109,7 +109,7 @@ Add two computed properties, `isEven` and `isOdd`, and a function `isMultiple` t /// /// An integer is even if it is a multiple of two. public var isEven: Bool { - return isMultiple(of: 2) + return _lowWord % 2 == 0 } @_transparent From 50bef4a28e56c80d1d635aefda7d0c882b81eb2b Mon Sep 17 00:00:00 2001 From: Thiago Holanda Date: Mon, 13 Aug 2018 21:04:08 +0200 Subject: [PATCH 0784/4563] =?UTF-8?q?[SE-0223]=20Fix=20address=20from=20pr?= =?UTF-8?q?oposal=E2=80=99s=20markdown=20(#889)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proposals/0223-array-uninitialized-initializer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0223-array-uninitialized-initializer.md b/proposals/0223-array-uninitialized-initializer.md index 292a3d261f..da0d9c790f 100644 --- a/proposals/0223-array-uninitialized-initializer.md +++ b/proposals/0223-array-uninitialized-initializer.md @@ -1,6 +1,6 @@ # Accessing an Array's Uninitialized Buffer -* Proposal: [SE-0223](0223-array-uninitialized-buffer.md) +* Proposal: [SE-0223](0223-array-uninitialized-initializer.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: [Joe Groff](https://github.com/jckarter) * Status: **Active review (August 13...August 19, 2018)** From 3b5ddc6e6f905a434731196e94b86878e4c3bb3e Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 13 Aug 2018 20:15:52 +0100 Subject: [PATCH 0785/4563] [SE-0223] Fix implementation link for status page --- proposals/0223-array-uninitialized-initializer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0223-array-uninitialized-initializer.md b/proposals/0223-array-uninitialized-initializer.md index da0d9c790f..0ca72cfafd 100644 --- a/proposals/0223-array-uninitialized-initializer.md +++ b/proposals/0223-array-uninitialized-initializer.md @@ -4,7 +4,7 @@ * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: [Joe Groff](https://github.com/jckarter) * Status: **Active review (August 13...August 19, 2018)** -* Implementation: https://github.com/apple/swift/pull/17389 +* Implementation: [apple/swift#17389](https://github.com/apple/swift/pull/17389) * Bug: [SR-3087](https://bugs.swift.org/browse/SR-3087) ## Introduction From e6f9241653c059511d5e843d99b0b4931ab55730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=ADn?= Date: Tue, 14 Aug 2018 00:16:31 +0000 Subject: [PATCH 0786/4563] Support 'less than' operator in compilation conditions (#877) * Add "Support 'less than' operator in compilation conditions" proposal * Add implementations * Add explanation about why supporting "<=" and ">" is not desired --- proposals/nnnn-ifswift-lessthan-operator.md | 92 +++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 proposals/nnnn-ifswift-lessthan-operator.md diff --git a/proposals/nnnn-ifswift-lessthan-operator.md b/proposals/nnnn-ifswift-lessthan-operator.md new file mode 100644 index 0000000000..d6c9f90b04 --- /dev/null +++ b/proposals/nnnn-ifswift-lessthan-operator.md @@ -0,0 +1,92 @@ +# Support 'less than' operator in compilation conditions + +* Proposal: [SE-NNNN](NNNN-ifswift-lessthan-operator.md) +* Authors: [Daniel Martín](https://github.com/danielmartin) +* Review Manager: TBD +* Status: TBD +* Bugs: [SR-6852](https://bugs.swift.org/browse/SR-6852) +* Implementations: https://github.com/apple/swift/pull/14503 (Stale?) + https://github.com/apple/swift/pull/17960 + +## Introduction + +This proposal augments the functionality implemented for proposal +[SE-0020](https://github.com/apple/swift-evolution/blob/master/proposals/0020-if-swift-version.md) +with the introduction of a new valid operator in compilation +condition: "<". The aim is that the syntax `#if swift(<4.2)` is +supported by the language. + +Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/support-for-more-operators-in-if-swift-build-configuration-option/14343) + +## Motivation + +The main motivation for introducing a new "<" operator in compilation +conditions is to be able to write Swift code that is easier to read. + +For example, if we want to only compile some piece of code if the +Swift version is less than 4.2, right now we have to write the following +code: + +```swift +#if !swift(>=4.2) +// This will only be executed if the Swift version is less than 4.2. +#endif +``` + +With the introduction of support for the "<" unary operator, the +refactored code would be more clear and readable: + +```swift +#if swift(<4.2) +// This will only be executed if the Swift version is less than 4.2. +#endif +``` + +In the former snippet, the `!` can be easily missed in a code +review. The latter snippet reads more like plain English. + +Support for other operators like "<=" and ">" is not desired, as they +make a statement about future releases and they don't account for +patch releases. That means that client code will need to be updated if +a patch release didn't fix a particular issue with the compiler, for +example. + +## Proposed solution + +The solution is small change in the parser so that the operator "<" is +supported. Diagnostic messages about invalid unary operators must be +updated as well. + +## Detailed design + +The place in the parser where `#if swift(...)` is parsed is +`ParseIfConfig.cpp`. There are two classes that will require +modification: `ValidateIfConfigCondition`, to take into account the +"<" operator, and `EvaluateIfConfigCondition`, to actually evaluate +the new operator semantics. A new '<' operator for `Version` will also +need to be implemented. + +The diagnostic message when the operator is not valid also needs to +change. I propose changing it from + +``` +unexpected platform condition argument: expected a unary comparison, such as '>=2.2' +``` + +to + +``` +unexpected platform condition argument: expected a unary comparison '>=' or '<'; for example, '>=2.2' or '<2.2' +``` + +## Source compatibility + +This has no effect in source compatibility. + +## Effect on ABI stability + +This has no effect in ABI stability. + +## Effect on API resilience + +This has no effect in API resilience. From 5626442fc0d9d94b3662d4c27f72824eabb36bf3 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Mon, 13 Aug 2018 17:22:12 -0700 Subject: [PATCH 0787/4563] Kick off SE-0224. --- ...ssthan-operator.md => 0224-ifswift-lessthan-operator.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{nnnn-ifswift-lessthan-operator.md => 0224-ifswift-lessthan-operator.md} (94%) diff --git a/proposals/nnnn-ifswift-lessthan-operator.md b/proposals/0224-ifswift-lessthan-operator.md similarity index 94% rename from proposals/nnnn-ifswift-lessthan-operator.md rename to proposals/0224-ifswift-lessthan-operator.md index d6c9f90b04..137f766b62 100644 --- a/proposals/nnnn-ifswift-lessthan-operator.md +++ b/proposals/0224-ifswift-lessthan-operator.md @@ -1,9 +1,9 @@ # Support 'less than' operator in compilation conditions -* Proposal: [SE-NNNN](NNNN-ifswift-lessthan-operator.md) +* Proposal: [SE-0224](0224-ifswift-lessthan-operator.md) * Authors: [Daniel Martín](https://github.com/danielmartin) -* Review Manager: TBD -* Status: TBD +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Status: **Active review (August 13...August 19, 2018)** * Bugs: [SR-6852](https://bugs.swift.org/browse/SR-6852) * Implementations: https://github.com/apple/swift/pull/14503 (Stale?) https://github.com/apple/swift/pull/17960 From 66ae73492d2f7affe5b49fc72bac42015b8bf186 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Mon, 13 Aug 2018 17:42:44 -0700 Subject: [PATCH 0788/4563] [SE-0224] Fix formatting of implementation links. --- proposals/0224-ifswift-lessthan-operator.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0224-ifswift-lessthan-operator.md b/proposals/0224-ifswift-lessthan-operator.md index 137f766b62..b2676fd894 100644 --- a/proposals/0224-ifswift-lessthan-operator.md +++ b/proposals/0224-ifswift-lessthan-operator.md @@ -5,8 +5,7 @@ * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Active review (August 13...August 19, 2018)** * Bugs: [SR-6852](https://bugs.swift.org/browse/SR-6852) -* Implementations: https://github.com/apple/swift/pull/14503 (Stale?) - https://github.com/apple/swift/pull/17960 +* Implementations: [apple/swift#14503](https://github.com/apple/swift/pull/14503) (Stale?), [apple/swift#17690](https://github.com/apple/swift/pull/17960) ## Introduction From 457633a797c7ff7d8b0bcc0a9823833378c5fc7c Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Mon, 13 Aug 2018 19:51:01 -0700 Subject: [PATCH 0789/4563] Added link to @stephentyrone's implementation --- proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md index ce3000b625..c77a907fc6 100644 --- a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md +++ b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md @@ -3,7 +3,8 @@ * Proposal: [SE-NNNN](NNNN-binaryinteger-iseven-isodd.md) * Authors: [Robert MacEachern](https://robmaceachern.com), [SiliconUnicorn](https://forums.swift.org/u/siliconunicorn/summary) * Review Manager: TBD -* Status: **Awaiting implementation** +* Status: **Implemented** +* Implementation: [apple/swift#18689](https://github.com/apple/swift/pull/18689) ## Introduction From 141ed79dda11e438bb56181033518b9844d17fc3 Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Mon, 13 Aug 2018 20:17:47 -0700 Subject: [PATCH 0790/4563] Simplify proposed solution section now that implementation is available --- ...n-binaryinteger-iseven-isodd-ismultiple.md | 34 +++---------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md index c77a907fc6..04df97ff8d 100644 --- a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md +++ b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md @@ -79,6 +79,7 @@ Testing the parity of integers is also relatively common in sample code and educ This functionality will also eliminate the need to use the remainder operator or bitwise AND when querying the divisibility of an integer. **Correctness:** It isn't [uncommon](https://github.com/apple/swift/blob/master/stdlib/public/core/RangeReplaceableCollection.swift#L1090) to see tests for oddness written as `value % 2 == 1` in Swift, but this is incorrect for negative odd values. The semantics of the `%` operator vary between programming languages, such as Ruby and Python, which can be surprising. + ``` // Swift: 7 % 2 == 1 // true @@ -106,37 +107,12 @@ Add two computed properties, `isEven` and `isOdd`, and a function `isMultiple` t // On protocol BinaryInteger @_transparent - /// A Boolean value indicating whether this value is even. - /// - /// An integer is even if it is a multiple of two. - public var isEven: Bool { - return _lowWord % 2 == 0 - } + public var isEven: Bool { return _lowWord % 2 == 0 } @_transparent - /// A Boolean value indicating whether this value is odd. - /// - /// An integer is odd if it is not a multiple of two. - public var isOdd: Bool { - return !isEven - } - - @inlinable - /// Returns a Boolean value that indicates whether the integer is a - /// multiple of another integer. - /// - /// For two integers a and b, b is a multiple of a if b = na for some integer n. - /// - /// - Note: 0 is a multiple of every integer. - /// - /// - Parameter divisor: The integer to test. - /// - Returns: `true` if `self` is a multiple of `divisor`; - /// otherwise, `false`. - public func isMultiple(of divisor: Self) -> Bool { - guard self != 0 else { return true } - guard divisor != 0 else { return false } - return self % divisor == 0 - } + public var isOdd: Bool { return !isEven } + + func isMultiple(of other: Self) -> Bool ``` ## Detailed design From 2809705a28d408982ea7f1f81c02f7fffa19766a Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Mon, 13 Aug 2018 20:35:16 -0700 Subject: [PATCH 0791/4563] Fix argument label --- proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md index 04df97ff8d..280ec405bb 100644 --- a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md +++ b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md @@ -8,7 +8,7 @@ ## Introduction -This proposal adds `var isEven: Bool`, `var isOdd: Bool`, and `func isMultiple(of divisor: Self) -> Bool` to the `BinaryInteger` protocol. `isEven` and `isOdd` are convenience properties for querying the [parity](https://en.wikipedia.org/wiki/Parity_(mathematics)) of the integer and `isMultiple` is a more general function to determine whether an integer is a multiple of another integer. +This proposal adds `var isEven: Bool`, `var isOdd: Bool`, and `func isMultiple(of other: Self) -> Bool` to the `BinaryInteger` protocol. `isEven` and `isOdd` are convenience properties for querying the [parity](https://en.wikipedia.org/wiki/Parity_(mathematics)) of the integer and `isMultiple` is a more general function to determine whether an integer is a multiple of another integer. Swift-evolution thread: [Even and Odd Integers](https://forums.swift.org/t/even-and-odd-integers/11774) From 81987765af24c9e65472cf75f6ed7c852bdb5bfe Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Mon, 13 Aug 2018 20:44:02 -0700 Subject: [PATCH 0792/4563] Replace `@inlinable` reference with `@_transparent` --- proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md index 280ec405bb..ddb4b7ea70 100644 --- a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md +++ b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md @@ -96,7 +96,7 @@ There is also a minor correctness risk in misinterpreting something like `value **Performance:** It's _possible_ that `isMultiple` could be implemented in a more performant way than `% divisor == 0` for more complex types, such as a BigInteger/BigNum type. -The addition of `isEven` and `isOdd` likely won’t have a major positive impact on performance but it should not introduce any additional overhead thanks to `@inlineable`. +The addition of `isEven` and `isOdd` likely won’t have a major positive impact on performance but it should not introduce any additional overhead thanks to `@_transparent`. ## Proposed solution From cd8357336aa7c8c10e86a3ed01f2a16e45d1d6b4 Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Tue, 14 Aug 2018 08:56:48 -0700 Subject: [PATCH 0793/4563] Updated author link to Github address --- proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md index ddb4b7ea70..fde230bbb3 100644 --- a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md +++ b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md @@ -1,7 +1,7 @@ # Adding `isEven`, `isOdd`, `isMultiple` to `BinaryInteger` * Proposal: [SE-NNNN](NNNN-binaryinteger-iseven-isodd.md) -* Authors: [Robert MacEachern](https://robmaceachern.com), [SiliconUnicorn](https://forums.swift.org/u/siliconunicorn/summary) +* Authors: [Robert MacEachern](https://github.com/robmaceachern), [SiliconUnicorn](https://forums.swift.org/u/siliconunicorn/summary) * Review Manager: TBD * Status: **Implemented** * Implementation: [apple/swift#18689](https://github.com/apple/swift/pull/18689) From 805280170b0064970c7d50bccb5c4f1f428e02aa Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 15 Aug 2018 00:15:53 +0300 Subject: [PATCH 0794/4563] Update optional-iteration.md --- proposals/optional-iteration.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/optional-iteration.md b/proposals/optional-iteration.md index 52564868af..beb1bd2f89 100644 --- a/proposals/optional-iteration.md +++ b/proposals/optional-iteration.md @@ -8,7 +8,7 @@ ## Introduction Optionals are a key feature of Swift and a powerful tool that seamlessly interacts with code; -they comprise an elegant and concise syntax that serves a great means of brevity when it comes to expressing +they comprise an elegant and concise syntax that serves a great means in expressing "act accordingly if there's a value, skip otherwise". Some vivid examples are optional chaining with assignments, optional invocation `foo?()` and even `if let`. This proposal considers further supporting this convenience in `for-in` loops. @@ -18,8 +18,8 @@ Swift-evolution thread: [Discussion thread topic for that proposal](https://foru Loops are indeed a common statement. When working with optional sequences, a possibility to optionally iterate (that is, iterate if there is a value, otherwise skip) is self-explanatory. However, Swift currently doesn't offer a way to express -this 'natively', in the language of optionals. Optional sequences are illegal as a `for-in` loop attribute. The most common and correct way of putting it, -especially when we need to handle the `nil` case (`else`) is +this 'natively', in the language of optionals: optional sequences are illegal as a `for-in` loop attribute. The most common and way of putting it, +especially when handling the `nil` case (`else`) is required, would be ```swift if let sequence = optionalSequence { @@ -33,9 +33,9 @@ The bottom line being, if we don't require `else`, why not say `for? element in ## Proposed solution -Optional `for-in` loops and the possibility to use optional sequences therein. The `?` notation, however, will be a semantic +Optional `for-in` loops and hence the possibility to use optional sequences an the sequence attribute. The `?` notation, however, will be a semantic emphasys rather than a functional syntactic unit. There will be no `for!`. The latter is redundant, but this decision was primarily -made based on the potential confusion that an otherwise left without syntactic changes `for-in` loop could lead to +made based on the incosistency and potential confusion that an otherwise left without syntactic changes `for-in` loop could lead to ("clarity over brevity"). The `?`, in fact, is not necessary: the sequence can be force-unwrapped if needed or left as-is without requiring addition syntax. From 471da22f0d76de139424aabb2a79c4b27d14ea41 Mon Sep 17 00:00:00 2001 From: Rob MacEachern Date: Tue, 14 Aug 2018 15:38:37 -0700 Subject: [PATCH 0795/4563] Update Micah's contact info --- proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md index fde230bbb3..28c99115c2 100644 --- a/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md +++ b/proposals/nnnn-binaryinteger-iseven-isodd-ismultiple.md @@ -1,7 +1,7 @@ # Adding `isEven`, `isOdd`, `isMultiple` to `BinaryInteger` * Proposal: [SE-NNNN](NNNN-binaryinteger-iseven-isodd.md) -* Authors: [Robert MacEachern](https://github.com/robmaceachern), [SiliconUnicorn](https://forums.swift.org/u/siliconunicorn/summary) +* Authors: [Robert MacEachern](https://github.com/robmaceachern), [Micah Hansonbrook](https://github.com/SiliconUnicorn) * Review Manager: TBD * Status: **Implemented** * Implementation: [apple/swift#18689](https://github.com/apple/swift/pull/18689) From f42e948ed3b2ff1e322a8441a8d77d27fb942964 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Mon, 30 Apr 2018 19:25:02 +0100 Subject: [PATCH 0796/4563] [SE-0206] Incorporate acceptance decision notes to proposal text - Remove Hasher.init(seed:) - Remove most Hasher.combine overloads - Change Hasher.finalize to be __consuming - Clarify that Hasher is order-sensitive - Clarify hashValue's deprecated status (only custom implementations are deprecated) - Clarify that SE-0206 is *not* going to be source-breaking; hashValue impls will compile with a warning in 4.2 mode. --- proposals/0206-hashable-enhancements.md | 326 +++++++++--------------- 1 file changed, 126 insertions(+), 200 deletions(-) diff --git a/proposals/0206-hashable-enhancements.md b/proposals/0206-hashable-enhancements.md index bea01d40c9..2044fe3711 100644 --- a/proposals/0206-hashable-enhancements.md +++ b/proposals/0206-hashable-enhancements.md @@ -9,13 +9,13 @@ [apple/swift#14913](https://github.com/apple/swift/pull/14913) (standard library, underscored),
[apple/swift#15122](https://github.com/apple/swift/pull/15122) (automatic synthesis)
TBD (de-underscoring, full `Hasher` API) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/f5a020ec79cdb64fc8700af91b1a1ece2d2fb141/proposals/0206-hashable-enhancements.md) @@ -111,8 +111,8 @@ time and effort to get this right for every `Hashable` type out there. For example, consider the code below, extracted directly from the -documentation of `Hashable`. Is this a good implementation of -`hashValue`? +documentation of Swift 4.1's `Hashable`. Is this a good implementation +of `hashValue`? ```swift struct GridPoint { @@ -235,88 +235,62 @@ the state of the hash function, and provides the following operations: 1. An initializer to create an empty state. To make hash values less predictable, the standard hash function uses a per-execution random - seed by default. A separate initializer also allows programmers to - optionally supply an additional 128-bit seed value, specified as a - tuple of two `UInt64` values: + seed, so that generated hash values will be different in each + execution of a Swift program. ```swift public struct Hasher { - public init() // Use the default per-execution random seed value - public init(seed: (UInt64, UInt64)) // Combines `seed` with the default seed + public init() // Uses a per-execution random seed value } ``` - `Hasher` implements a separate hash function for each seed value, - uncorrelated with the others. This enables `Hasher` to be used in - data structures requiring more than one hash function, such as - [Bloom filters][bloom]. (Per-instance seeding is also useful to - [stabilize the performance][quadratic-copy] of "regular" hashing - collections like `Set` and `Dictionary`.) +2. Operations to feed new bytes to the hash function, mixing them + into its state: - [bloom]: https://en.wikipedia.org/wiki/Bloom_filter - - Note that the custom seed is mixed with the default seed, so - specifying one *doesn't* disable nondeterministic hashing. The - algorithm implemented by `Hasher` is not part of its API, and - enforcing randomization makes it harder for Swift programs to - accidentally rely on any specific algorithm. Any such dependency - would make it more difficult for the standard library to change - `Hasher` in future releases. - -2. A set of operations to mix in new bits into the state of the hash - function. For reasons of efficiency, these are built around - feeding integer values to the hasher: - - ```swift - extension Hasher { - public mutating func combine(bits: Int) - public mutating func combine(bits: UInt) - public mutating func combine(bits: Int64) - public mutating func combine(bits: UInt64) - public mutating func combine(bits: Int32) - public mutating func combine(bits: UInt32) - // etc. - } - ``` - - We expect most hashable types will consist of discrete components, - hashed sequentially, one by one. However, we provide a `combine` - overload that takes bytes from an `UnsafeRawBufferPointer`, for - use in cases where the bits to be hashed are available as a - single, contiguous byte sequence: - ```swift extension Hasher { - public mutating func combine(bits buffer: UnsafeRawBufferPointer) + public mutating func combine(bytes buffer: UnsafeRawBufferPointer) + public mutating func combine(_ value: H) } ``` + + `combine(bytes:)` is the most general operation, suitable for use + when the bytes to be hashed are available as a single contiguous + region in memory. + + `combine(_:)` is a convenience operation that can take any + `Hashable` value; we expect this will be more frequently + useful. (We'll see how this is implemented in the next section.) 3. An operation to finalize the state, extracting the hash value from it. ```swift extension Hasher { - public mutating func finalize() -> Int + public __consuming func finalize() -> Int } ``` - Finalizing the hasher state invalidates it; it is illegal to call - `combine` or `finalize` on a hasher that's already finalized. + Finalizing the hasher state consumes it; it is illegal to call + `combine` or `finalize` on a hasher you don't own, or on one that's + already been finalized. -Here is how one may use `Hasher` as a standalone type: +For example, here is how one may use `Hasher` as a standalone type: ```swift var hasher = Hasher() // Initialize state, usually by random seeding -hasher.combine(bits: 23) // Mix in several integers' worth of bits -hasher.combine(bits: 42) -let hash = hasher.finalize() // Finalize the state and return the hash +hasher.combine(23) // Mix in several integer's worth of bytes +hasher.combine(42) +print(hasher.finalize()) // Finalize the state and return the hash ``` Within the same execution of a Swift program, `Hasher`s are guaranteed -to return the same hash value in `finalize()`, as long as they start -with the same seed, and they are fed the exact same sequence of bytes. +to return the same hash value in `finalize()`, as long as they are fed +the exact same sequence of bytes. (Note that the order of `combine` +operations matters; swapping the two integers above will produce a +completely different hash.) However, `Hasher` may generate entirely different hash values in other -executions, *even if it is seeded with the same value*. This +executions, *even if it is fed the exact same byte sequence*. This randomization is a critical feature, as it makes it much harder for potential attackers to predict hash values. `Hashable` has always been documented to explicitly allow such nondeterminism: @@ -342,21 +316,21 @@ Introducing `Hasher` is a big improvement, but it's only half of the story: `Hashable` itself needs to be updated to make better use of it. We propose to change the `Hashable` protocol by adding a new -`hash(into:)` requirement, while, at the same time, deprecating -`hashValue`: +`hash(into:)` requirement: ```swift public protocol Hashable: Equatable { - @available(*, deprecated: 4.2) var hashValue: Int { get } - func hash(into hasher: inout Hasher) } ``` -(Please see the section on [Source +At the same time, we deprecate custom implementations of the +`hashValue` property. (Please see the section on [Source compatibility](#source-compatibility) on how we'll do this without -breaking code written for previous versions of Swift.) +breaking code written for previous versions of Swift.) In some future +language version, we intend to convert `hashValue` to an extension +method. To make it easier to express `hash(into:)` in terms of `Hashable` components, `Hasher` provides a variant of `combine` that simply calls @@ -464,13 +438,13 @@ worth the cost of a change to a basic protocol? is free) this takes 15 combines' worth of time: ``` - 1 hasher.combine(bits: topLeft.hashValue) - 1 hasher.combine(bits: topLeft.x) (in topLeft.hashValue) - 1 hasher.combine(bits: topLeft.y) + 1 hasher.combine(topLeft.hashValue) + 1 hasher.combine(topLeft.x) (in topLeft.hashValue) + 1 hasher.combine(topLeft.y) 3 hasher.finalize() - 1 hasher.combine(bits: bottomRight.hashValue) - 1 hasher.combine(bits: bottomRight.x) (in bottomRight.hashValue) - 1 hasher.combine(bits: bottomRight.y) + 1 hasher.combine(bottomRight.hashValue) + 1 hasher.combine(bottomRight.x) (in bottomRight.hashValue) + 1 hasher.combine(bottomRight.y) 3 hasher.finalize() 3 hasher.finalize() --- @@ -493,11 +467,11 @@ worth the cost of a change to a basic protocol? approach: ``` - 1 hasher.combine(bits: topLeft.x) (in topLeft.hash(into:)) - 1 hasher.combine(bits: topLeft.y) - 1 hasher.combine(bits: bottomRight.x) (in bottomRight.hash(into:)) - 1 hasher.combine(bits: bottomRight.y) - 3 hasher.finalize() (outside of GridRectangle.hash(into:)) + 1 hasher.combine(topLeft.x) (in topLeft.hash(into:)) + 1 hasher.combine(topLeft.y) + 1 hasher.combine(bottomRight.x) (in bottomRight.hash(into:)) + 1 hasher.combine(bottomRight.y) + 3 hasher.finalize() (outside of GridRectangle.hash(into:)) --- 7 ``` @@ -540,19 +514,10 @@ Add the following type to the standard library: /// any two versions of the standard library. Do not save or otherwise /// reuse hash values across executions of your program. public struct Hasher { - /// Initialize a new hasher using the default seed value. - /// The default seed is set during process startup, usually from a - /// high-quality random source. - /// - /// This is equivalent to calling `init(seed:)` with a value of `(0, 0)`. + /// Initialize a new hasher using a per-execution seed value. + /// The seed is set during process startup, usually from a high-quality + /// random source. public init() - - /// Initialize a new hasher using a seed value that is derived from a - /// combination of the default seed and the specified custom seed. - /// - /// The default seed is set during process startup, usually from a - /// high-quality random source. - public init(seed: (UInt64, UInt64)) /// Feed `value` to this hasher, mixing its essential parts into /// the hasher state. @@ -561,51 +526,16 @@ public struct Hasher { value.hash(into: &self) } - /// Mix the bit pattern `bits` into the state of this hasher. - /// This adds exactly `Int.bitWidth` bits to the hasher state, - /// in native byte order. - public mutating func combine(bits: Int) - - /// Mix the bit pattern `bits` into the state of this hasher. - /// This adds exactly `UInt.bitWidth` bits to the hasher state, - /// in native byte order. - public mutating func combine(bits: UInt) - - /// Mix the bit pattern `bits` into the state of this hasher. - /// This adds exactly 8 bytes to the hasher state, in native byte order. - public mutating func combine(bits: Int64) - - /// Mix the bit pattern `bits` into the state of this hasher. - /// This adds exactly 8 bytes to the hasher state, in native byte order. - public mutating func combine(bits: UInt64) - - /// Mix the bit pattern `bits` into the state of this hasher. - /// This adds exactly 4 bytes to the hasher state, in native byte order. - public mutating func combine(bits: Int32) - - /// Mix the bit pattern `bits` into the state of this hasher. - /// This adds exactly 4 bytes to the hasher state, in native byte order. - public mutating func combine(bits: UInt32) - - /// Mix the bit pattern `bits` into the state of this hasher. - /// This adds exactly 2 bytes to the hasher state, in native byte order. - public mutating func combine(bits: Int16) - - /// Mix the bit pattern `bits` into the state of this hasher. - /// This adds exactly 2 bytes to the hasher state, in native byte order. - public mutating func combine(bits: UInt16) - - /// Mix the single byte `bits` into the state of this hasher. - public mutating func combine(bits: Int8) - /// Mix the single byte `bits` into the state of this hasher. - public mutating func combine(bits: UInt8) - /// Mix the raw bytes in `buffer` into the state of this hasher. - public mutating func combine(bits buffer: UnsafeRawBufferPointer) + /// Feed the raw bytes in `buffer` into this hasher, mixing its bits into + /// the hasher state. + public mutating func combine(bytes buffer: UnsafeRawBufferPointer) /// Finalize the hasher state and return the hash value. - /// Finalizing invalidates the hasher; additional bits cannot be combined - /// into it, and it cannot be finalized again. - public mutating func finalize() -> Int + /// + /// Finalizing consumes the hasher: it is illegal to finalize a hasher you + /// don't own, or to perform operations on a finalized hasher. (These may + /// become compile-time errors in the future.) + public __consuming func finalize() -> Int } ``` @@ -706,7 +636,6 @@ public protocol Hashable: Equatable { /// /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. - @available(*, deprecated: 4.2) var hashValue: Int { get } /// Hash the essential components of this value into the hash function @@ -722,26 +651,53 @@ public protocol Hashable: Equatable { ## Source compatibility The introduction of the new `Hasher` type is a purely additive change. -However, adding the `hash(into:)` requirement and deprecating -`hashValue` are potentially source breaking changes. Deprecating a -requirement in a basic protocol like `Hashable` is an especially -drastic change; we must make every effort to make sure there is a -smooth transition path to Swift 4.2 for existing code implementing -`hashValue`. - -Usually, such changes can be implemented in the standard library by -adding appropriate default implementations gated on language -version. For example, this is how `hash(into:)` can be expressed in -terms of `hashValue`, and vice versa: +However, adding the `hash(into:)` requirement is potentially source +breaking. To ensure we keep compatibility with code written for +previous versions of Swift, while also allowing new code to only +implement `hash(into:)`, we extend [SE-0185]'s automatic `Hashable` +synthesis to automatically derive either of these requirements when +the other has been manually implemented. -```swift -extension Hashable { - @available(*, obsoleted: 4.2) +Code written for Swift 4.1 or earlier will continue to compile (in the +corresponding language mode) after this proposal is implemented. The +compiler will synthesize the missing `hash(into:)` requirement +automatically: + +``` +struct GridPoint41: Hashable { // Written for Swift 4.1 or below + let x: Int + let y: Int + + var hashValue: Int { + return x.hashValue ^ y.hashValue &* 16777619 + } + + // Supplied automatically by the compiler: func hash(into hasher: inout Hasher) { - hasher.combine(bits: self.hashValue) + hasher.combine(self.hashValue) } +} +``` + +The compiler will emit a deprecation warning when it needs to do this +in 4.2 mode. (However, note that code that merely uses `hashValue` +will continue to compile without warnings.) + +Code written for Swift 4.2 or later should conform to `Hashable` by +implementing `hash(into:)`. The compiler will then complete the +conformance with a suitable `hashValue` definition: - @available(*, introduced: 4.2, deprecated: 4.2) +``` +struct GridPoint42: Hashable { // Written for Swift 4.2 + let x: Int + let y: Int + + func hash(into hasher: inout Hasher) { + hasher.combine(x) + hasher.combine(y) + } + + // Supplied automatically by the compiler: var hashValue: Int { var hasher = Hasher() self.hash(into: &hasher) @@ -750,65 +706,37 @@ extension Hashable { } ``` -However, in the case of `Hashable`, such default implementations would -interfere with [SE-0185]'s automatic conformance synthesis. To ensure -compatibility, we [currently][either-or-requirements] need to move -these definitions to the compiler, by extending automatic synthesis to -supply definitions functionally equivalent to the ones above. - -[either-or-requirements]: https://forums.swift.org/t/mutually-exclusive-default-implementations/11044 +When upgrading to Swift 4.2, `Hashable` types written for earlier +versions of Swift should be migrated to implement `hash(into:)` +instead of `hashValue`. -Code written for Swift 4.1 or earlier will continue to compile (in the -corresponding language mode) after this proposal is implemented. The -compiler will synthesize the missing `hash(into:)` requirement -automatically. +For types that satisfy [SE-0185]'s requirements for `Hashable` +synthesis, this can be as easy as removing the explicit `hashValue` +implementation. Note that Swift 4.2 implements conditional `Hashable` +conformances for many standard library types that didn't have it +before; this enables automatic `Hashable` synthesis for types that use +these as components. -When upgrading to Swift 4.2, `Hashable` types written for earlier -versions of Swift will need to be migrated to implement `hash(into:)` -instead of `hashValue`. There are two options for doing this: - -1. For types that support [SE-0185]'s automatic `Hashable` synthesis, - upgrading to Swift 4.2 can be as simple as removing the explicit - `hashValue` implementation. Note that [SE-0143] added conditional - conformances to `Hashable` in the standard library, which makes - automatic synthesis available for many more types than before. -2. In cases where automatic synthesis is unavailable, or if it would - produce undesirable results, the `hashValue` implementation - needs to be replaced by a corresponding implementation of - `hash(into:)`. - -The compiler should simplify the migration process by providing -fix-its for both options. A fix-it to remove `hashValue` should only -be provided if automatic synthesis is available. For the second -option, it would be nice to have the compiler suggest a full -implementation of `hash(into:)`, but a template would probably -suffice: +For types that still need to manually implement `Hashable`, the +migrator can be updated to help with this process. For example, the +`GridPoint41.hashValue` implementation above can be mechanically +rewritten as follows: ``` -// Before: -var hashValue: Int { - return x.hashValue ^ y.hashValue &* 16777619 -} +struct GridPoint41: Hashable { // Migrated from Swift 4.1 + let x: Int + let y: Int -// After: -func hash(into hasher: inout Hasher) { - // Feed all components that should be hashed into the hasher. - // These should be the same components that you look at in your - // implementation of `==` for `Equatable`. - hasher.combine(<# component1 #>) - hasher.combine(<# component2 #>) - - // For reference, this type originally implemented `hashValue` - // as follows: - // ``` - // return x.hashValue ^ y.hashValue &* 16777619 - // ``` + // Migrated from hashValue: + func hash(into hasher: inout Hasher) { + hash.combine(x.hashValue ^ y.hashValue &* 16777619) + } } ``` -Whatever the fix-it does, it must not leave the function body empty, -because it would compile and run without warning, but it would produce -terrible hash values. +This will not provide the same hash quality as combining both members +one by one, but it may be useful as a starting point. + ## Effect on ABI stability @@ -929,9 +857,7 @@ functions, and to plug them into any `Hashable` type: ```swift protocol Hasher { - func combine(bits: Int) - func combine(bits: UInt) - /// etc. + func combine(bytes: UnsafeRawBufferPointer) } protocol Hashable { func hash(into hasher: inout H) From b6e46f9a9c76555fcffa9a11998cdc36f6546eb1 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Mon, 30 Apr 2018 19:27:35 +0100 Subject: [PATCH 0797/4563] [SE-0206] Update status to Implemented --- proposals/0206-hashable-enhancements.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0206-hashable-enhancements.md b/proposals/0206-hashable-enhancements.md index 2044fe3711..7e23b346b9 100644 --- a/proposals/0206-hashable-enhancements.md +++ b/proposals/0206-hashable-enhancements.md @@ -7,8 +7,8 @@ * Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/115) * Implementation:
[apple/swift#14913](https://github.com/apple/swift/pull/14913) (standard library, underscored),
- [apple/swift#15122](https://github.com/apple/swift/pull/15122) (automatic synthesis)
- TBD (de-underscoring, full `Hasher` API) + [apple/swift#16009](https://github.com/apple/swift/pull/16009) (`Hasher` interface),
+ [apple/swift#16073](https://github.com/apple/swift/pull/16073) (automatic synthesis, de-underscoring)
* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/f5a020ec79cdb64fc8700af91b1a1ece2d2fb141/proposals/0206-hashable-enhancements.md) +
  • Loading ...
  • diff --git a/index.js b/index.js index 19547d4c3f..12728aeb1d 100644 --- a/index.js +++ b/index.js @@ -263,7 +263,7 @@ function renderBody () { '.awaitingReview', '.scheduledForReview', '.activeReview', '.accepted', '.acceptedWithRevisions', '.implemented', '.returnedForRevision', '.deferred', '.rejected', '.withdrawn' ] - + proposalAttachPoint.innerHTML = ''; proposalPresentationOrder.map(function (state) { var matchingProposals = proposals.filter(function (p) { return p.status && p.status.state === state }) matchingProposals.map(function (proposal) { From 9cbbf1f7cf18149898edd6fc7ae48252d5d867f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Behnam=20Mohammadi=20=28=D8=A8=D9=87=D9=86=D8=A7=D9=85=20?= =?UTF-8?q?=D9=85=D8=AD=D9=85=D8=AF=DB=8C=29?= Date: Sun, 13 Jan 2019 16:05:41 +0330 Subject: [PATCH 0976/4563] added loading state in first page load --- index.html | 3 +-- index.js | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 1cfea6dccb..6edbf190f8 100644 --- a/index.html +++ b/index.html @@ -77,8 +77,7 @@

    Swift Evolution

      - -
    • Loading ...
    • +
    diff --git a/index.js b/index.js index 12728aeb1d..aadab11470 100644 --- a/index.js +++ b/index.js @@ -128,9 +128,10 @@ function init () { }) req.addEventListener('error', function (e) { - document.querySelector('#proposals-count').innerText = 'Proposal data failed to load.' + document.querySelector('#proposals-count-number').innerText = 'Proposal data failed to load.' }) + document.querySelector('#proposals-count-number').innerHTML = 'Loading ...'; req.open('get', 'https://data.swift.org/swift-evolution/proposals') req.send() } @@ -263,7 +264,7 @@ function renderBody () { '.awaitingReview', '.scheduledForReview', '.activeReview', '.accepted', '.acceptedWithRevisions', '.implemented', '.returnedForRevision', '.deferred', '.rejected', '.withdrawn' ] - proposalAttachPoint.innerHTML = ''; + proposalPresentationOrder.map(function (state) { var matchingProposals = proposals.filter(function (p) { return p.status && p.status.state === state }) matchingProposals.map(function (proposal) { From f145c057152b2733fa797f2517a694c4823ca9f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Behnam=20Mohammadi=20=28=D8=A8=D9=87=D9=86=D8=A7=D9=85=20?= =?UTF-8?q?=D9=85=D8=AD=D9=85=D8=AF=DB=8C=29?= Date: Sun, 13 Jan 2019 16:49:53 +0330 Subject: [PATCH 0977/4563] added debounce for filterProposals --- index.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index aadab11470..1cc846d563 100644 --- a/index.js +++ b/index.js @@ -482,8 +482,9 @@ function addEventListeners () { var nav = document.querySelector('nav') // typing in the search field causes the filter to be reapplied. - nav.addEventListener('keyup', filterProposals) - nav.addEventListener('change', filterProposals) + var filterProposalsDebounce = debounce(filterProposals, 250); + nav.addEventListener('keyup', filterProposalsDebounce) + nav.addEventListener('change', filterProposalsDebounce) // clearing the search field also hides the X symbol nav.querySelector('#clear-button').addEventListener('click', function () { @@ -991,3 +992,14 @@ function updateProposalsCount (count) { var numberField = document.querySelector('#proposals-count-number') numberField.innerText = (count.toString() + ' proposal' + (count !== 1 ? 's' : '')) } + +function debounce(func, delay) { + let timeoutId = null; + return function() { + clearTimeout(timeoutId); + var args = arguments; + timeoutId = setTimeout(function() { + func.apply(undefined, args); + }, delay); + }; +}; \ No newline at end of file From d22ffda0ca2521f6e316aaef29c3f2e3147b8da4 Mon Sep 17 00:00:00 2001 From: Scott Perry Date: Mon, 14 Jan 2019 22:06:33 -0800 Subject: [PATCH 0978/4563] [Proposal] Ordered collection diffing (#968) * Add collection diffing proposal for pitching to swift-evolution * BidirectionalCollection should inherit from OrderedCollection, as pointed out by @Michael_Ilseman * StringProtocol is also a bidirectional collection * Add Kyle to the authors list * Updates from the pitch and other feedback --- proposals/NNNN-ordered-collection-diffing.md | 339 +++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 proposals/NNNN-ordered-collection-diffing.md diff --git a/proposals/NNNN-ordered-collection-diffing.md b/proposals/NNNN-ordered-collection-diffing.md new file mode 100644 index 0000000000..40835d02aa --- /dev/null +++ b/proposals/NNNN-ordered-collection-diffing.md @@ -0,0 +1,339 @@ +# Ordered Collection Diffing + +* Proposal: SE-NNNN +* Authors: [Scott Perry](https://github.com/numist), [Kyle Macomber](https://github.com/kylemacomber) +* Review Manager: TBD +* Status: **Awaiting review** +* Prototype: [numist/Diffing](https://github.com/numist/Diffing) + +## Introduction + +This proposal describes additions to the standard library that provide an interchange format for diffs as well as diffing/patching functionality for ordered collection types. + +## Motivation + +Representing, manufacturing, and applying transactions between states today requires writing a lot of error-prone code. This proposal is inspired by the convenience of the `diffutils` suite when interacting with text files, and the reluctance to solve similar problems in code with `libgit2`. + +Many state management patterns would benefit from improvements in this area, including undo/redo stacks, generational stores, and syncing differential content to/from a service. + +## Proposed solution + +A new type representing the difference between ordered collections is introduced along with methods that support its production and application. + +Using this API, a line-by-line three-way merge can be performed in a few lines of code: + +``` swift +// Split the contents of the sources into lines +let baseLines = base.components(separatedBy: "\n") +let theirLines = theirs.components(separatedBy: "\n") +let myLines = mine.components(separatedBy: "\n") + +// Create a difference from base to theirs +let diff = theirLines.shortestEditScript(from:baseLines) + +// Apply it to mine, if possible +guard let patchedLines = myLines.applying(diff) else { + print("Merge conflict applying patch, manual merge required") + return +} + +// Reassemble the result +let patched = patchedLines.joined(separator: "\n") +print(patched) +``` + +## Detailed design + +### Producing diffs + +Collections can only be efficiently diffed when they have a strong sense of order, so difference production is added to `BidirectionalCollection`: + +``` swift +@available(swift, introduced: 5.1) +extension BidirectionalCollection { + /// Returns the difference needed to produce the receiver's state from the + /// parameter's state with the fewest possible changes, using the provided + /// closure to establish equivalence between elements. + /// + /// This function does not infer element moves, but they can be computed + /// using `OrderedCollectionDifference.inferringMoves()` if desired. + /// + /// Implementation is an optimized variation of the algorithm described by + /// E. Myers (1986). + /// + /// - Parameters: + /// - other: The base state. + /// - areEquivalent: A closure that returns whether the two + /// parameters are equivalent. + /// + /// - Returns: The difference needed to produce the reciever's state from + /// the parameter's state. + /// + /// - Complexity: O(*n* * *d*), where *n* is `other.count + self.count` and + /// *d* is the number of changes between the two ordered collections. + public func shortestEditScript( + from other: C, by areEquivalent: (Element, C.Element) -> Bool + ) -> OrderedCollectionDifference + where C : BidirectionalCollection, C.Element == Self.Element +} + +extension BidirectionalCollection where Element: Equatable { + /// Returns the difference needed to produce the receiver's state from the + /// parameter's state with the fewest possible changes, using equality to + /// establish equivalence between elements. + /// + /// This function does not infer element moves, but they can be computed + /// using `OrderedCollectionDifference.inferringMoves()` if desired. + /// + /// Implementation is an optimized variation of the algorithm described by + /// E. Myers (1986). + /// + /// - Parameters: + /// - other: The base state. + /// + /// - Returns: The difference needed to produce the reciever's state from + /// the parameter's state. + /// + /// - Complexity: O(*n* * *d*), where *n* is `other.count + self.count` and + /// *d* is the number of changes between the two ordered collections. + public func shortestEditScript(from other: C) -> OrderedCollectionDifference + where C: BidirectionalCollection, C.Element == Self.Element +``` + +The `shortestEditScript(from:)` method determines the fewest possible edits required to transition betewen the two states and stores them in a difference type, which is defined as: + +``` swift +/// A type that represents the difference between two ordered collection states. +@available(swift, introduced: 5.1) +public struct OrderedCollectionDifference { + /// A type that represents a single change to an ordered collection. + /// + /// The `offset` of each `insert` refers to the offset of its `element` in + /// the final state after the difference is fully applied. The `offset` of + /// each `remove` refers to the offset of its `element` in the original + /// state. Non-`nil` values of `associatedWith` refer to the offset of the + /// complementary change. + public enum Change { + case insert(offset: Int, element: ChangeElement, associatedWith: Int?) + case remove(offset: Int, element: ChangeElement, associatedWith: Int?) + } + + /// Creates an instance from a collection of changes. + /// + /// For clients interested in the difference between two ordered + /// collections, see `OrderedCollection.shortestEditScript(from:)`. + /// + /// To guarantee that instances are unambiguous and safe for compatible base + /// states, this initializer will fail unless its parameter meets to the + /// following requirements: + /// + /// 1) All insertion offsets are unique + /// 2) All removal offsets are unique + /// 3) All offset associations between insertions and removals are symmetric + /// + /// - Parameter changes: A collection of changes that represent a transition + /// between two states. + /// + /// - Complexity: O(*n* * log(*n*)), where *n* is the length of the + /// parameter. + public init?(_ c: C) where C.Element == Change + + /// The `.insert` changes contained by this difference, from lowest offset to highest + public var insertions: [Change] { get } + + /// The `.remove` changes contained by this difference, from lowest offset to highest + public var removals: [Change] { get } +} + +/// An OrderedCollectionDifference is itself a Collection. +/// +/// The enumeration order of `Change` elements is: +/// +/// 1. `.remove`s, from highest `offset` to lowest +/// 2. `.insert`s, from lowest `offset` to highest +/// +/// This guarantees that applicators on compatible base states are safe when +/// written in the form: +/// +/// ``` +/// for c in diff { +/// switch c { +/// case .remove(offset: let o, element: _, associatedWith: _): +/// arr.remove(at: o) +/// case .insert(offset: let o, element: let e, associatedWith: _): +/// arr.insert(e, at: o) +/// } +/// } +/// ``` +extension OrderedCollectionDifference : Collection { + public typealias Element = OrderedCollectionDifference.Change + public struct Index: Comparable, Hashable {} +} + +extension OrderedCollectionDifference.Change: Equatable where ChangeElement: Equatable {} +extension OrderedCollectionDifference: Equatable where ChangeElement: Equatable {} + +extension OrderedCollectionDifference.Change: Hashable where ChangeElement: Hashable {} +extension OrderedCollectionDifference: Hashable where ChangeElement: Hashable { + /// Infers which `ChangeElement`s have been both inserted and removed only + /// once and returns a new difference with those associations. + /// + /// - Returns: an instance with all possible moves inferred. + /// + /// - Complexity: O(*n*) where *n* is `self.count` + public func inferringMoves() -> OrderedCollectionDifference +} + +extension OrderedCollectionDifference: Codable where ChangeElement: Codable {} +``` + +A `Change` is a single mutating operation, an `OrderedCollectionDifference` is a plurality of such operations that represents a complete transition between two states. Given the interdependence of the changes, `OrderedCollectionDifference` has no mutating members, but it does allow index- and `Slice`-based access to its changes via `Collection` conformance as well as a validating initializer taking a `Collection`. + +Fundamentally, there are only two operations that mutate ordered collections, `insert(_:at:)` and `remove(at:)`, but there are benefits from being able to represent other operations such as moves and replacements, especially for UIs that may want to animate a move differently from an `insert`/`remove` pair. These operations are represented using `associatedWith:`. When non-`nil`, they refer to the offset of the counterpart as described in the headerdoc. + +In a similar way, the name `shortestEditScript(from:)` uses a term of art to admit the use of an algorithm that compromises between performance and a minimal output. It computes the [longest common subsequence](https://en.wikipedia.org/wiki/Longest_common_subsequence_problem) between the two collections, but not the [longest common substring](https://en.wikipedia.org/wiki/Longest_common_substring_problem) (which is a much slower operation). In the future other algorithms may be added as different methods to satisfy the need for different performance and output characteristics. + +### Application of instances of `OrderedCollectionDifference` + +``` swift +extension RangeReplaceableCollection { + /// Applies a difference to a collection. + /// + /// - Parameter difference: The difference to be applied. + /// + /// - Returns: An instance representing the state of the receiver with the + /// difference applied, or `nil` if the difference is incompatible with + /// the receiver's state. + /// + /// - Complexity: O(*n* + *c*), where *n* is `self.count` and *c* is the + /// number of changes contained by the parameter. + @available(swift, introduced: 5.1) + public func applying(_ difference: OrderedCollectionDifference) -> Self? +} +``` + +Applying a diff to an incompatible base state is the only way application can fail. `applying(_:)` expresses this by returning nil. + +## Source compatibility + +This proposal is additive and the names of the types it proposes are not likely to already be in wide use, so it does not represent a significant risk to source compatibility. + +## Effect on ABI stability + +This proposal does not affect ABI stability. + +## Effect on API resilience + +This feature is additive and symbols marked with `@available(swift, introduced: 5.1)` as appropriate. + +## Alternatives considered + +### `shortestEditScript(from:by:)` defined in protocol instead of extension + +Different algorithms with different premises and/or semantics are free to be defined using different function names. + +### Communicating changes via a series of callbacks + +Breaking up a transaction into a sequence of imperative events is not very Swifty, and the pattern has proven to be fertile ground for defects. + +### More cases in `OrderedCollectionDifference.Change` + +While other cases such as `.move` are tempting, the proliferation of code in switch statements is unwanted overhead for clients that don't care about the "how" of a state transition so much as the "what". + +The use of associated offsets allows for more information to be encoded into the diff without making it more difficult to use. You've already seen how associated offsets can be used to illustrate moves (as produced by `inferringMoves()`): + +``` swift +OrderedCollectionDifference([ + .remove(offset:0, element: "value", associatedWith: 4), + .insert(offset:4, element: "value", associatedWith: 0) +]) +``` + +But they can also be used to illustrate replacement when the offsets refer to the same position (and the element is different): + +``` swift +OrderedCollectionDifference([ + .remove(offset:0, element: "oldvalue", associatedWith: 0), + .insert(offset:0, element: "newvalue", associatedWith: 0) +]) +``` + +Differing offsets and elements can be combined when a value is both moved and replaced (or changed): + +``` swift +OrderedCollectionDifference([ + .remove(offset:4, element: "oldvalue", associatedWith: 0), + .insert(offset:0, element: "newvalue", associatedWith: 4) +]) +``` + +Neither of these two latter forms can be inferred from a diff by inferringMoves(), but they can be legally expressed by any API that vends a difference. + +### `applying(_:) throws -> Self` instead of `applying(_:) -> Self?` + +Applying a diff can only fail when the base state is incompatible. As such, the additional granularity provided by an error type does not add any value. + +### Use `Index` instead of offset in `Change` + +Because indexes cannot be navigated in the absence of the collection instance that generated them, a diff based on indexes instead of offsets would be much more limited in usefulness as a boundary type. If indexes are required, they can be rehydrated from the offsets in the presence of the collection(s) to which they belong. + +### `OrderedCollection` conformance for `OrderedCollectionDifference` + +Because the change offsets refer directly to the resting positions of elements in the base and modified states, the changes represent the same state transition regardless of their order. The purpose of ordering is to optimize for understanding, safety, and/or performance. In fact, this prototype already contains examples of two different equally valid sort orders: + +* The order provided by `for in` is optimized for safe diff application when modifying a compatible base state one element at a time. +* `applying(_:)` uses a different order where `insert` and `remove` instances are interleaved based on their adjusted offsets in the base state. + +Both sort orders are "correct" in representing the same state transition. + +### `Change` generic on both `BaseElement` and `OtherElement` instead of just `Element` + +Application of differences would only be possible when both `Element` types were equal, and there would be additional cognitive overhead with comparators with the type `(Element, Other.Element) -> Bool`. + +Since the comparator forces both types to be effectively isomorphic, a diff generic over only one type can satisfy the need by mapping one (or both) ordered collections to force their `Element` types to match. + +### `difference(from:using:)` with an enum parameter for choosing the diff algorithm instead of `shortestEditScript(from:)` + +This is an attractive API concept, but it can be very cumbersome to extend. This is especially the case for types like `OrderedSet` that—through member uniqueness and fast membership testing—have the capability to support very fast diff algorithms that aren't appropriate for other types. + +### `CollectionDifference` or just `Difference` instead of `OrderedCollectionDifference` + +The name `OrderedCollectionDifference` gives us the opportunity to build a family of related types in the future, as the difference type in this proposal is (intentionally) unsuitable for representing differences between keyed collections (which don't shift their elements' keys on insertion/removal) or structural differences between treelike collections (which are multidimensional). + +## Intentional omissions: + +### Further adoption + +This API allows for more interesting functionality that is not included in this proposal. + +For example, this propsal could have included a `reversed()` function on the difference type that would return a new difference that would undo the application of the original. + +The lack of additional conveniences and functionality is intentional; the goal of this proposal is to lay the groundwork that such extensions would be built upon. + +In the case of `reversed()`, clients of the API in this proposal can use `Collection.map()` to invert the case of each `Change` and feed the result into `OrderedCollectionDifference.init(_:)`: + +``` swift +let diff: OrderedCollectionDifference = /* ... */ +let reversed = OrderedCollectionDifference( + diff.map({(change) -> OrderedCollectionDifference.Change in + switch change { + case .insert(offset: let o, element: let e, associatedWith: let a): + return .remove(offset: o, element: e, associatedWith: a) + case .remove(offset: let o, element: let e, associatedWith: let a): + return .insert(offset: o, element: e, associatedWith: a) + } + }) +)! +``` + +### `mutating apply(_:)` + +There is no mutating applicator because there is no algorithmic advantage to in-place application. + +### `mutating inferringMoves()` + +While there may be savings to be had from in-place move inferencing; we're holding this function for a future proposal. + +### Formalizing the concept of an ordered collection + +This problem warrants a proposal of its own. \ No newline at end of file From a06c9af5573d42ed7829efec8ffd676ed9860ba9 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 14 Jan 2019 22:09:08 -0800 Subject: [PATCH 0979/4563] Initiate review of SE-0240: Ordered Collection Diffing --- ...ection-diffing.md => 0240-ordered-collection-diffing.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-ordered-collection-diffing.md => 0240-ordered-collection-diffing.md} (99%) diff --git a/proposals/NNNN-ordered-collection-diffing.md b/proposals/0240-ordered-collection-diffing.md similarity index 99% rename from proposals/NNNN-ordered-collection-diffing.md rename to proposals/0240-ordered-collection-diffing.md index 40835d02aa..1ab1e4d349 100644 --- a/proposals/NNNN-ordered-collection-diffing.md +++ b/proposals/0240-ordered-collection-diffing.md @@ -1,9 +1,9 @@ # Ordered Collection Diffing -* Proposal: SE-NNNN +* Proposal: SE-0240 * Authors: [Scott Perry](https://github.com/numist), [Kyle Macomber](https://github.com/kylemacomber) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (January 14...22, 2019)** * Prototype: [numist/Diffing](https://github.com/numist/Diffing) ## Introduction From 6065aef88c7b45c9a588cd481450c5dadb943117 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Tue, 15 Jan 2019 13:07:18 -0800 Subject: [PATCH 0980/4563] Add self-link to SE-0240. --- proposals/0240-ordered-collection-diffing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0240-ordered-collection-diffing.md b/proposals/0240-ordered-collection-diffing.md index 1ab1e4d349..f639e62f71 100644 --- a/proposals/0240-ordered-collection-diffing.md +++ b/proposals/0240-ordered-collection-diffing.md @@ -1,6 +1,6 @@ # Ordered Collection Diffing -* Proposal: SE-0240 +* Proposal: [SE-0240](0240-ordered-collection-diffing.md) * Authors: [Scott Perry](https://github.com/numist), [Kyle Macomber](https://github.com/kylemacomber) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active review (January 14...22, 2019)** @@ -336,4 +336,4 @@ While there may be savings to be had from in-place move inferencing; we're holdi ### Formalizing the concept of an ordered collection -This problem warrants a proposal of its own. \ No newline at end of file +This problem warrants a proposal of its own. From 7a0fbe0199eadd4487e1d743f015adf4678e8d53 Mon Sep 17 00:00:00 2001 From: Jon Shier Date: Thu, 24 Jan 2019 15:23:49 -0500 Subject: [PATCH 0981/4563] Update SE-0235 to match implemented API. --- proposals/0235-add-result.md | 201 ++++++++++++++++++++++------------- 1 file changed, 127 insertions(+), 74 deletions(-) diff --git a/proposals/0235-add-result.md b/proposals/0235-add-result.md index a7029c4136..be026f197b 100644 --- a/proposals/0235-add-result.md +++ b/proposals/0235-add-result.md @@ -20,14 +20,14 @@ Swift's [Error Handling Rationale and Proposal](https://github.com/apple/swift/b ## Proposed solution ```swift -public enum Result { - case value(Value), error(Error) +public enum Result { + case success(Success), failure(Failure) } ``` -`Result` is a pragmatic compromise between competing error handling concerns both present and future. +`Result` is a pragmatic compromise between competing error handling concerns both present and future. -The `Error` type captured in the `.error` case is constrained to `Swift.Error` to simplify and underline `Result`'s intended use for manually propagating the result of a failable computation. +The `Failure` type captured in the `.failure` case is constrained to `Error` to simplify and underline `Result`'s intended use for manually propagating the result of a failable computation. ### Usage @@ -54,11 +54,11 @@ URLSession.shared.dataTask(with: url) { (data, response, error) in While this code is only a few lines long, it exposes Swift's complete lack of automatic error handling for asynchronous APIs. Not only was the `error` forcibly unwrapped (or perhaps handled using a slightly less elegant `if` statement), but a possibly impossible scenario was created. What happens if `response` or `data` are `nil`? Is it even possible? It shouldn't be, but Swift currently lacks the ability to express this impossibility. Using `Result` for the same scenario allows for much more elegant code: ```swift -URLSession.shared.dataTask(with: url) { (result: Result<(URLResponse, Data), Error>) in // Type added for illustration purposes. +URLSession.shared.dataTask(with: url) { (result: Result<(response: URLResponse, data: Data?), Error>) in // Type added for illustration purposes. switch result { - case .value(let response): - handleResponse(response.0, data: response.1) - case .error(let error): + case let .success(success): + handleResponse(success) + case let .error(error): handleError(error) } } @@ -103,7 +103,9 @@ let configuration = Result { try String(contentsOfFile: configuration) } // Sometime later... func doSomethingWithConfiguration() { - ... + switch configuration { + ... + } } ``` @@ -149,90 +151,128 @@ Additional convenience API on `Result` could make many of these cases even more As implemented in the PR (annotations pending): ```swift -/// A value that represents either a success or failure, capturing associated -/// values in both cases. +/// A value that represents either a success or a failure, including an +/// associated value in each case. @_frozen -public enum Result { - /// A normal result, storing a `Value`. - case value(Value) +public enum Result { + /// A success, storing a `Success` value. + case success(Success) - /// An error result, storing an `Error`. - case error(Error) + /// A failure, storing a `Failure` value. + case failure(Failure) - /// Evaluates the given transform closure when this `Result` instance is - /// `.success`, passing the value as a parameter. + /// Returns a new result, mapping any success value using the given + /// transformation. + /// + /// Use this method when you need to transform the value of a `Result` + /// instance when it represents a success. The following example transforms + /// the integer success value of a result into a string: + /// + /// func getNextInteger() -> Result { /* ... */ } /// - /// Use the `map` method with a closure that returns a non-`Result` value. + /// let integerResult = getNextInteger() + /// // integerResult == .success(5) + /// let stringResult = integerResult.map({ String($0) }) + /// // stringResult == .success("5") /// - /// - Parameter transform: A closure that takes the successful value of the + /// - Parameter transform: A closure that takes the success value of this /// instance. - /// - Returns: A new `Result` instance with the result of the transform, if - /// it was applied. - public func map( - _ transform: (Value) -> NewValue - ) -> Result + /// - Returns: A `Result` instance with the result of evaluating `transform` + /// as the new success value if this instance represents a success. + public func map( + _ transform: (Success) -> NewSuccess + ) -> Result { } - /// Evaluates the given transform closure when this `Result` instance is - /// `.failure`, passing the error as a parameter. + /// Returns a new result, mapping any failure value using the given + /// transformation. + /// + /// Use this method when you need to transform the value of a `Result` + /// instance when it represents a failure. The following example transforms + /// the error value of a result by wrapping it in a custom `Error` type: + /// + /// struct DatedError: Error { + /// var error: Error + /// var date: Date /// - /// Use the `mapError` method with a closure that returns a non-`Result` - /// value. + /// init(_ error: Error) { + /// self.error = error + /// self.date = Date() + /// } + /// } + /// + /// let result: Result = // ... + /// // result == .failure() + /// let resultWithDatedError = result.mapError({ e in DatedError(e) }) + /// // result == .failure(DatedError(error: , date: )) /// /// - Parameter transform: A closure that takes the failure value of the /// instance. - /// - Returns: A new `Result` instance with the result of the transform, if - /// it was applied. - public func mapError( - _ transform: (Error) -> NewError - ) -> Result + /// - Returns: A `Result` instance with the result of evaluating `transform` + /// as the new failure value if this instance represents a failure. + public func mapError( + _ transform: (Failure) -> NewFailure + ) -> Result { } - /// Evaluates the given transform closure when this `Result` instance is - /// `.success`, passing the value as a parameter and flattening the result. + /// Returns a new result, mapping any success value using the given + /// transformation and unwrapping the produced result. /// - /// - Parameter transform: A closure that takes the successful value of the + /// - Parameter transform: A closure that takes the success value of the /// instance. - /// - Returns: A new `Result` instance, either from the transform or from - /// the previous error value. - public func flatMap( - _ transform: (Value) -> Result - ) -> Result + /// - Returns: A `Result` instance with the result of evaluating `transform` + /// as the new failure value if this instance represents a failure. + public func flatMap( + _ transform: (Success) -> Result + ) -> Result { } - /// Evaluates the given transform closure when this `Result` instance is - /// `.failure`, passing the error as a parameter and flattening the result. + /// Returns a new result, mapping any failure value using the given + /// transformation and unwrapping the produced result. /// - /// - Parameter transform: A closure that takes the error value of the + /// - Parameter transform: A closure that takes the failure value of the /// instance. - /// - Returns: A new `Result` instance, either from the transform or from - /// the previous success value. - public func flatMapError( - _ transform: (Error) -> Result - ) -> Result - - /// Unwraps the `Result` into a throwing expression. + /// - Returns: A `Result` instance, either from the closure or the previous + /// `.success`. + public func flatMapError( + _ transform: (Failure) -> Result + ) -> Result { } + + /// Returns the success value as a throwing expression. /// - /// - Returns: The success value, if the instance is a success. - /// - Throws: The error value, if the instance is a failure. - public func unwrapped() throws -> Value + /// Use this method to retrieve the value of this result if it represents a + /// success, or to catch the value if it represents a failure. + /// + /// let integerResult: Result = .success(5) + /// do { + /// let value = try integerResult.get() + /// print("The value is \(value).") + /// } catch error { + /// print("Error retrieving the value: \(error)") + /// } + /// // Prints "The value is 5." + /// + /// - Returns: The success value, if the instance represents a success. + /// - Throws: The failure value, if the instance represents a failure. + public func get() throws -> Success { } } -extension Result where Error == Swift.Error { - /// Create an instance by capturing the output of a throwing closure. +extension Result where Failure == Swift.Error { + /// Creates a new result by evaluating a throwing closure, capturing the + /// returned value as a success, or any thrown error as a failure. /// - /// - Parameter throwing: A throwing closure to evaluate. + /// - Parameter body: A throwing closure to evaluate. @_transparent - public init(catching body: () throws -> Value) + public init(catching body: () throws -> Success) { } } -extension Result : Equatable where Value : Equatable, Error : Equatable { } +extension Result: Equatable where Success: Equatable, Failure: Equatable { } -extension Result : Hashable where Value : Hashable, Error : Hashable { } +extension Result: Hashable where Success: Hashable, Failure: Hashable { } ``` ## Adding `Swift.Error` self-conformance -It's expected that most uses of `Result` will use `Swift.Error` as the `Error` type argument. Currently, that is impossible with the generic signature proposed here because the `Swift.Error` protocol type does not itself conform to the `Swift.Error` protocol. This is due to a restriction in the generic system which only allows certain `@objc` protocol types to conform to their protocol. Lifting this restriction in general is out of scope for Swift 5, but it *can* be lifted specifically for `Swift.Error`, making it conform to itself. Making that change is part of this proposal. This has the immediate benefit for this proposal of allowing `Swift.Error` to be used as the `Error` type argument; otherwise, the `Error` argument would have to be unconstrained. It is also generally useful for working with errors in generic code. +As part of the prepatory work for this proposal, self-conformance was added for `Error` (and only `Error`). This is also generally useful for working with errors in a generic context. -This self-conformance does not extend to protocol compositions including the `Swift.Error` protocol, only the exact type `Swift.Error`. It will be possible to add such compositions in the future, but that is out of scope for Swift 5. +This self-conformance does not extend to protocol compositions including the `Error` protocol, only the exact type `Error`. It will be possible to add such compositions in the future, but that is out of scope for Swift 5. ## Other Languages Many other languages have a `Result` type or equivalent: @@ -245,7 +285,7 @@ Many other languages have a `Result` type or equivalent: ## Source compatibility -This is an additive change, but could conflict with `Result` types already defined. +This is an additive change, but work done to improve type name shadowing has made it a non-issue. ## Effect on ABI stability @@ -253,13 +293,24 @@ This proposal adds a type to the standard library and so will affect the ABI onc ## Effect on API resilience -Addition of `Result` should be future proof against future needs surrounding error handling. +Addition of `Result` should be future proof against additional needs surrounding error handling. ## Alternatives considered -### Alternative Spellings of `Result` +### Alternative Spellings of `Result` A few alternate spellings were proposed: +A previously revised version of this proposal proposed: +```swift +enum Result { + case value(Value) + case error(Error) +} + +However, community opposition to this spelling resulted in the current, final spelling. + +Additionally, a hybrid spelling was proposed: + ```swift enum Result { case success(Value) @@ -267,7 +318,9 @@ enum Result { } ``` -This creates an unfortunate asymmetry between the names of the payload types and the names of the cases. This is just additional complexity that has to be remembered. It's true that these case names don't align with some of the most common community implementations of the `Result` type, meaning that adoption of `Result` will require additional source changes for clients of those projects. However, this cannot be an overriding concern. +This creates an unfortunate asymmetry between the names of the payload types and the names of the cases. This is additional complexity that has to be remembered. + +Finally, spellings using `Wrapped` as the success type name were proposed: ```swift enum Result { @@ -284,7 +337,7 @@ enum Result { The use of `Wrapped` in these spellings emphasizes more of a similarity to `Optional` than seems appropriate. -### Alternatives to `Result` +### Alternatives to `Result` - `Result`: A `Result` without a generic error type fits well with the current error design in Swift. However, it prevents the future addition of typed error handling (typed `throws`). @@ -292,7 +345,7 @@ The use of `Wrapped` in these spellings emphasizes more of a similarity to `Opti ### Constraint on the `Error` type -A previous version of this proposal did not constrain the `Error` type to conform to `Swift.Error`. This was largely because adding such a requirement would block `Swift.Error` from being used as the `Error` type, since `Swift.Error` does not conform to itself. Since we've now figured out how how to make that conformance work, this constraint is unblocked. +A previous version of this proposal did not constrain the `Failure` type to conform to `Error`. This was largely because adding such a requirement would block `Error` from being used as the `Error` type, since `Error` does not conform to itself. Since we've now figured out how how to make that conformance work, this constraint is unblocked. Constraining the error type to conform to `Error` is a very low burden, and it has several benefits: @@ -300,14 +353,14 @@ Constraining the error type to conform to `Error` is a very low burden, and it h - It encourages better practices for error values, such as using meaningful wrapper types for errors instead of raw `Int`s and `String`s. -- It immediately catches the simple transposition error of writing `Result`. Programmers coming from functional languages that use `Either` as a `Result` type are especially prone to this mistake: such languages often write the error type as the first argument due to the useful monadic properties of `Either E`. +- It immediately catches the simple transposition error of writing `Result`. Programmers coming from functional languages that use `Either` as a `Result` type are especially prone to this mistake: such languages often write the error type as the first argument due to the useful monadic properties of `Either E`. ### Operations -A previous verson of this proposal included operations for optionally projecting out the `value` and `error` cases. These operations are useful, but they should be added uniformly for all `enum`s, and `Result` should not commit to providing hard-coded versions that may interfere with future language evolution. In the meantime, it is easy for programmers to add these operations with extensions in their own code. +A previous verson of this proposal included operations for optionally projecting out the `value` and `error` cases. These operations are useful, but they should be added uniformly for all `enum`s, and `Result` should not commit to providing hard-coded versions that may interfere with future language evolution. In the meantime, it is easy for programmers to add these operations with extensions in their own code. -A previous version of this proposal included a `fold` operation. This operation is essentially an expression-based `switch`, and like the optional case projections, it would be better to provide a general language solution for it than to add a more limited design that covers only a single type. +A previous version of this proposal included a `fold` operation. This operation is essentially an expression-based `switch`, and like the optional case projections, it would be better to provide a general language solution for it than to add a more limited design that covers only a single type. -A previous version of this proposal did not label the closure parameter for the catching initializer. Single-argument unlabeled initializers are conventionally used for conversions, which this is not; usually the closure will be written explicitly, but in case it isn't, a parameter label is appropriate. +A previous version of this proposal did not label the closure parameter for the catching initializer. Single-argument unlabeled initializers are conventionally used for conversions, which this is not; usually the closure will be written explicitly, but in case it isn't, a parameter label is appropriate. -There are several different names that would be reasonable for the `unwrapped` operation, such as `get`. None of these names seem obviously better than `unwrapped`. +There are several different names that would be reasonable for the `unwrapped` operation, such as `get`. None of these names seem obviously better than `unwrapped`. From 132e7cdb095e822b5936b6c9e23cb8df3cc2c049 Mon Sep 17 00:00:00 2001 From: Jon Shier Date: Thu, 24 Jan 2019 15:28:34 -0500 Subject: [PATCH 0982/4563] Update sentence. --- proposals/0235-add-result.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0235-add-result.md b/proposals/0235-add-result.md index be026f197b..fdc4921871 100644 --- a/proposals/0235-add-result.md +++ b/proposals/0235-add-result.md @@ -285,7 +285,7 @@ Many other languages have a `Result` type or equivalent: ## Source compatibility -This is an additive change, but work done to improve type name shadowing has made it a non-issue. +This is an additive change which could conflict with existing `Result` types, but work done to improve type name shadowing has made it a non-issue. ## Effect on ABI stability From ae16a16c414968939f9533800ff268fb611740c7 Mon Sep 17 00:00:00 2001 From: Jon Shier Date: Thu, 24 Jan 2019 17:08:49 -0500 Subject: [PATCH 0983/4563] Fix Data type. --- proposals/0235-add-result.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0235-add-result.md b/proposals/0235-add-result.md index fdc4921871..a089171fc8 100644 --- a/proposals/0235-add-result.md +++ b/proposals/0235-add-result.md @@ -54,10 +54,10 @@ URLSession.shared.dataTask(with: url) { (data, response, error) in While this code is only a few lines long, it exposes Swift's complete lack of automatic error handling for asynchronous APIs. Not only was the `error` forcibly unwrapped (or perhaps handled using a slightly less elegant `if` statement), but a possibly impossible scenario was created. What happens if `response` or `data` are `nil`? Is it even possible? It shouldn't be, but Swift currently lacks the ability to express this impossibility. Using `Result` for the same scenario allows for much more elegant code: ```swift -URLSession.shared.dataTask(with: url) { (result: Result<(response: URLResponse, data: Data?), Error>) in // Type added for illustration purposes. +URLSession.shared.dataTask(with: url) { (result: Result<(response: URLResponse, data: Data), Error>) in // Type added for illustration purposes. switch result { case let .success(success): - handleResponse(success) + handleResponse(success.response, data: data) case let .error(error): handleError(error) } From 65605d52cb3927130f5e39bd6b54c6a3073cbc24 Mon Sep 17 00:00:00 2001 From: Jon Shier Date: Thu, 24 Jan 2019 17:28:15 -0500 Subject: [PATCH 0984/4563] success.data --- proposals/0235-add-result.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0235-add-result.md b/proposals/0235-add-result.md index a089171fc8..f2cdacf251 100644 --- a/proposals/0235-add-result.md +++ b/proposals/0235-add-result.md @@ -57,7 +57,7 @@ While this code is only a few lines long, it exposes Swift's complete lack of au URLSession.shared.dataTask(with: url) { (result: Result<(response: URLResponse, data: Data), Error>) in // Type added for illustration purposes. switch result { case let .success(success): - handleResponse(success.response, data: data) + handleResponse(success.response, data: success.data) case let .error(error): handleError(error) } From c35e2a7c1511b54668f4c2b9a7aa3753551fce14 Mon Sep 17 00:00:00 2001 From: Adib Faramarzi Date: Fri, 25 Jan 2019 11:05:37 +0330 Subject: [PATCH 0985/4563] Fix markdown block not being closed close the markdown for swift --- proposals/0235-add-result.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0235-add-result.md b/proposals/0235-add-result.md index f2cdacf251..d33c9eee3c 100644 --- a/proposals/0235-add-result.md +++ b/proposals/0235-add-result.md @@ -306,6 +306,7 @@ enum Result { case value(Value) case error(Error) } +``` However, community opposition to this spelling resulted in the current, final spelling. From ecac17062421b212b92fae0408c4c0f5147cb756 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Mon, 28 Jan 2019 16:36:12 -0800 Subject: [PATCH 0986/4563] Explicit Encoded Offsets for String Indices --- ...n-string-index-explicit-encoding-offset.md | 218 ++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 proposals/nnnn-string-index-explicit-encoding-offset.md diff --git a/proposals/nnnn-string-index-explicit-encoding-offset.md b/proposals/nnnn-string-index-explicit-encoding-offset.md new file mode 100644 index 0000000000..e3b65274b8 --- /dev/null +++ b/proposals/nnnn-string-index-explicit-encoding-offset.md @@ -0,0 +1,218 @@ +# Explicit Encoded Offsets for String Indices +* Proposal: SE-NNNN +* Authors: [Michael Ilseman](https://github.com/milseman) +* Review Manager: John McCall +* Status: **Awaiting review** +* Implementation: [apple/swift#22108](https://github.com/apple/swift/pull/22108) + +## Introduction + +[SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md) introduced a computed variable and initializer surrounding the concept of an `encodedOffset`. Unfortunately, that approach is flawed for its intended purpose, and is commonly misused in ways that Swift 5 is [more likely to expose](https://bugs.swift.org/browse/SR-9749). We propose a set of alternative APIs for each intended use and every existing misuse. + +Swift-evolution thread: TBD + +## Motivation + +String abstracts away details about the underlying encoding used in its storage. String.Index is opaque and represents a position within a String or Substring. This can make serializing a string alongside its indices difficult, and for that reason [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md) added a computed variable and initializer `encodedOffset` in Swift 4.0. + +String was always meant to be capable of handling multiple backing encodings for its contents, and this is realized in Swift 5. [String now uses UTF-8](https://forums.swift.org/t/string-s-abi-and-utf-8/17676) for its preferred “fast” native encoding, but has a resilient fallback for strings of different encodings. Currently, we only use this fall-back for lazily-bridged Cocoa strings, which are commonly encoded as UTF-16, though it can be extended in the future thanks to resilience. + +Unfortunately, [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s approach of a single notion of `encodedOffset` is flawed. A string can be serialized with a choice of encodings, and the offset is therefore encoding-dependent and requires access to the contents of the string to calculate. A comment in [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)'s example source mentioned that `encodedOffset` assumes UTF-16, which happened to be the only encoding used internally by String at the time (for offset purposes). + +Furthermore, the majority of uses of `encodedOffset` in the wild are not following [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s intended purpose and are sensitive to encoding changes. `encodedOffset` is frequently misused under the assumption that all Characters are comprised of a single code unit, which is error-prone and Swift 5 might surface the underlying bugs in more situations. It is also sometimes used for mapping Cocoa string indices, which happens to work in Swift 4 but might not in Swift 5, and Foundation already provides better alternatives. + +Because Swift 5 may introduce a semantic difference in behavior, it is important to rush this fix into the 5.0 release so that developers can preserve existing semantics or switch to correct semantics as needed. We hope the majority of existing uses will be replaced with something more ergonomic such as [offset-based subscripting](https://forums.swift.org/t/shorthand-for-offsetting-startindex-and-endindex/9397) in a future release, but we need some migration path for code now. + + +## Proposed solution + +We propose fixing all existing use and misuse scenarios by adding per-view offset initializers and methods to `String.Index`, in addition to one for the default view of Characters. We propose deprecating `encodedOffset` to direct existing code towards these replacements. + +
    Details: String’s views and encodings + +String has 3 views which correspond to the most popular Unicode encodings: UTF-8, UTF-16, and UTF-32 (via the Unicode scalar values). String’s default view is of Characters. + +```swift +let myString = "abc\r\nいろは" +Array(myString.utf8) // UTF-8 encoded +Array(myString.utf16) // UTF-16 encoded +Array(myString.unicodeScalars.lazy.map { $0.value }) // UTF-32 encoded +Array(myString); Array(myString.indices) // Not an encoding, but provides offset-based access to `Characters` +``` +
    + +### Uses in the Wild +
    + +GitHub code search yields [nearly 1500 uses](https://github.com/search?l=Swift&q=encodedOffset&type=Code) , and nearly-none of them are for [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s intended purpose. Below I present the 3 most common uses. + +```swift +// Common code for these examples +let myString: String = ... +let start: String.Index = ... +let end: String.Index = ... +let utf16OffsetRange: Range = ... +let nsRange: NSRange = ... +``` + + +#### Offset-based `Character` indexing + +The most common misuse of `encodedOffset` assumes that all Characters in a String are comprised of a single code unit. This is wrong and a source of surprising bugs, even for exclusively ASCII content: `"\r\n".count == 1`. + +```swift +let (i, j): (Int, Int) = ... // Something computed in terms of myString.count + +// Problematic code +myString[String.Index(encodedOffset: i]..` and `NSRange`. Foundation already provides convenient initializers for this purpose already, and using them is the preferred approach: + +```swift +// Problematic code +let myNSRange = NSRange(location: start.encodedOffset, length: end.encodedOffset - start.encodedOffset) +let myStrRange = String.Index(encodedOffset: nsRange.lowerBound).. + + +## Detailed design + +```swift +extension String.Index { + /// The UTF-16 code unit offset corresponding to this Index + public func offset(within utf16: String.UTF16View) -> Int { + return utf16.distance(from: utf16.startIndex, to: self) + } + /// The UTF-8 code unit offset corresponding to this Index + public func offset(within utf8: String.UTF8View) -> Int { + return utf8.distance(from: utf8.startIndex, to: self) + } + /// The Unicode scalar offset corresponding to this Index + public func offset(within scalars: String.UnicodeScalarView) -> Int { + return scalars.distance(from: scalars.startIndex, to: self) + } + /// The Character offset corresponding to this Index + public func offset(within str: String) -> Int { + return str.distance(from: str.startIndex, to: self) + } + + /// Creates a new index at the specified UTF-16 code unit offset + /// + /// - Parameter offset: An offset in UTF-16 code units. + public init(offset: Int, within utf16: String.UTF16View) { + let (start, end) = (utf16.startIndex, utf16.endIndex) + guard offset >= 0, + let idx = utf16.index(start, offsetBy: offset, limitedBy: end) + else { + self = end + return + } + self = idx + } + + /// Creates a new index at the specified UTF-8 code unit offset + /// + /// - Parameter offset: An offset in UTF-8 code units. + public init(offset: Int, within utf8: String.UTF8View) { + let (start, end) = (utf8.startIndex, utf8.endIndex) + guard offset >= 0, + let idx = utf8.index(start, offsetBy: offset, limitedBy: end) + else { + self = end + return + } + self = idx + } + + /// Creates a new index at the specified Unicode scalar offset + /// + /// - Parameter offset: An offset in terms of Unicode.Scalars + public init(offset: Int, within scalars: String.UnicodeScalarView) { + let (start, end) = (scalars.startIndex, scalars.endIndex) + guard offset >= 0, + let idx = scalars.index(start, offsetBy: offset, limitedBy: end) + else { + self = end + return + } + self = idx + } + + /// Creates a new index at the specified Character offset + /// + /// - Parameter offset: An offset in terms of Characters + public init(offset: Int, within str: String) { + let (start, end) = (str.startIndex, str.endIndex) + guard offset >= 0, + let idx = str.index(start, offsetBy: offset, limitedBy: end) + else { + self = end + return + } + self = idx + } +} + +``` + +This gives developers: + +1. The ability to choose a specific encoding for serialization, the original intended purpose. +2. The ability to fix any code that assumed fixed-encoding-width Characters by choosing the most-natural variant that just takes a String. +3. The ability to migrate their uses for Cocoa index mapping by choosing UTF-16. + + +## Source Compatibility + +This deprecates existing API and provides alternatives. Deprecation preserves source compatibility and strongly hints towards correct usage. But, other changes in Swift 5 introduce potential semantic drift. + +## Effect of ABI stability + +This change is necessary to realize our goal of opaque String indices and an encoding-abstracted String representation in time for ABI stability in Swift 5. + +## Effect on API resilience + +Added APIs are all resilient and can be replaced with more efficient implementations that preserve correctness as String evolves. + +## Alternatives Considered + +### Do Nothing + +If `encodedOffset` was only used for serialization, *and* such serialization/deserialization would record and preserve the original encoding, *and* we amend [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s comment to avoid nailing it down to any given encoding, no change would be necessary. Unfortunately, there is no way to query or preserve internal encoding and there is considerable use and misuse in the wild, as mentioned in the “Uses in the Wild” disclosure section. + From 666215845e7795c119ec7865075ea4096053df7a Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 28 Jan 2019 21:41:58 -0500 Subject: [PATCH 0987/4563] Put SE-0241 in active review --- ...set.md => 0241-string-index-explicit-encoding-offset.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{nnnn-string-index-explicit-encoding-offset.md => 0241-string-index-explicit-encoding-offset.md} (98%) diff --git a/proposals/nnnn-string-index-explicit-encoding-offset.md b/proposals/0241-string-index-explicit-encoding-offset.md similarity index 98% rename from proposals/nnnn-string-index-explicit-encoding-offset.md rename to proposals/0241-string-index-explicit-encoding-offset.md index e3b65274b8..d6f27b1536 100644 --- a/proposals/nnnn-string-index-explicit-encoding-offset.md +++ b/proposals/0241-string-index-explicit-encoding-offset.md @@ -1,8 +1,8 @@ # Explicit Encoded Offsets for String Indices -* Proposal: SE-NNNN +* Proposal: SE-0241 * Authors: [Michael Ilseman](https://github.com/milseman) -* Review Manager: John McCall -* Status: **Awaiting review** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (January 29th...February 4rd, 2019)** * Implementation: [apple/swift#22108](https://github.com/apple/swift/pull/22108) ## Introduction From 2726ed3ee0141f931e27d3529c3ee4c65379636d Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 28 Jan 2019 21:43:06 -0500 Subject: [PATCH 0988/4563] Linkify SE-0241's self-reference --- proposals/0241-string-index-explicit-encoding-offset.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0241-string-index-explicit-encoding-offset.md b/proposals/0241-string-index-explicit-encoding-offset.md index d6f27b1536..ea8fd8667b 100644 --- a/proposals/0241-string-index-explicit-encoding-offset.md +++ b/proposals/0241-string-index-explicit-encoding-offset.md @@ -1,5 +1,5 @@ # Explicit Encoded Offsets for String Indices -* Proposal: SE-0241 +* Proposal: [SE-0241](https://github.com/apple/swift-evolution/blob/master/proposals/0241-string-index-explicit-encoding-offset.md) * Authors: [Michael Ilseman](https://github.com/milseman) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (January 29th...February 4rd, 2019)** From 1a87ee0d06dd9ce0fc04e952836da31af1afa8c9 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 28 Jan 2019 21:46:55 -0500 Subject: [PATCH 0989/4563] Typo fix "4rd", really? --- proposals/0241-string-index-explicit-encoding-offset.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0241-string-index-explicit-encoding-offset.md b/proposals/0241-string-index-explicit-encoding-offset.md index ea8fd8667b..6185e5fa32 100644 --- a/proposals/0241-string-index-explicit-encoding-offset.md +++ b/proposals/0241-string-index-explicit-encoding-offset.md @@ -2,7 +2,7 @@ * Proposal: [SE-0241](https://github.com/apple/swift-evolution/blob/master/proposals/0241-string-index-explicit-encoding-offset.md) * Authors: [Michael Ilseman](https://github.com/milseman) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (January 29th...February 4rd, 2019)** +* Status: **Active review (January 29th...February 3rd, 2019)** * Implementation: [apple/swift#22108](https://github.com/apple/swift/pull/22108) ## Introduction From aa9b8a561fe920577eafe195c880d62b1ed36b48 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 28 Jan 2019 21:49:45 -0500 Subject: [PATCH 0990/4563] Add review link --- proposals/0241-string-index-explicit-encoding-offset.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0241-string-index-explicit-encoding-offset.md b/proposals/0241-string-index-explicit-encoding-offset.md index 6185e5fa32..4e3f0bca56 100644 --- a/proposals/0241-string-index-explicit-encoding-offset.md +++ b/proposals/0241-string-index-explicit-encoding-offset.md @@ -4,13 +4,12 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (January 29th...February 3rd, 2019)** * Implementation: [apple/swift#22108](https://github.com/apple/swift/pull/22108) +* Review: ([review](https://forums.swift.org/t/se-0241-explicit-encoded-offsets-for-string-indices/19929)) ## Introduction [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md) introduced a computed variable and initializer surrounding the concept of an `encodedOffset`. Unfortunately, that approach is flawed for its intended purpose, and is commonly misused in ways that Swift 5 is [more likely to expose](https://bugs.swift.org/browse/SR-9749). We propose a set of alternative APIs for each intended use and every existing misuse. -Swift-evolution thread: TBD - ## Motivation String abstracts away details about the underlying encoding used in its storage. String.Index is opaque and represents a position within a String or Substring. This can make serializing a string alongside its indices difficult, and for that reason [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md) added a computed variable and initializer `encodedOffset` in Swift 4.0. From 4fb1f1f2a168a3794507786e53bcfb4c1daf866f Mon Sep 17 00:00:00 2001 From: Scott Perry Date: Wed, 30 Jan 2019 20:03:59 -0800 Subject: [PATCH 0991/4563] Collection diffing proposal evolution (#978) * Rename shortestEditScript(from:) to difference(from:) based on proposal feedback * Incorporate proposal feedback from core team --- proposals/0240-ordered-collection-diffing.md | 127 ++++++++----------- 1 file changed, 55 insertions(+), 72 deletions(-) diff --git a/proposals/0240-ordered-collection-diffing.md b/proposals/0240-ordered-collection-diffing.md index f639e62f71..8d42c12855 100644 --- a/proposals/0240-ordered-collection-diffing.md +++ b/proposals/0240-ordered-collection-diffing.md @@ -4,21 +4,21 @@ * Authors: [Scott Perry](https://github.com/numist), [Kyle Macomber](https://github.com/kylemacomber) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active review (January 14...22, 2019)** -* Prototype: [numist/Diffing](https://github.com/numist/Diffing) +* Implementation: [apple/swift#21845](https://github.com/apple/swift/pull/21845) ## Introduction -This proposal describes additions to the standard library that provide an interchange format for diffs as well as diffing/patching functionality for ordered collection types. +This proposal describes additions to the standard library that provide an interchange format for diffs as well as diffing/patching functionality for appropriate collection types. ## Motivation -Representing, manufacturing, and applying transactions between states today requires writing a lot of error-prone code. This proposal is inspired by the convenience of the `diffutils` suite when interacting with text files, and the reluctance to solve similar problems in code with `libgit2`. +Representing, manufacturing, and applying transactions between states today requires writing a lot of error-prone code. This proposal is inspired by the convenience of the `diffutils` suite when interacting with text files, and the reluctance to solve similar problems in code by linking `libgit2`. Many state management patterns would benefit from improvements in this area, including undo/redo stacks, generational stores, and syncing differential content to/from a service. ## Proposed solution -A new type representing the difference between ordered collections is introduced along with methods that support its production and application. +A new type representing the difference between collections is introduced along with methods that support its production and application. Using this API, a line-by-line three-way merge can be performed in a few lines of code: @@ -29,7 +29,7 @@ let theirLines = theirs.components(separatedBy: "\n") let myLines = mine.components(separatedBy: "\n") // Create a difference from base to theirs -let diff = theirLines.shortestEditScript(from:baseLines) +let diff = theirLines.difference(from:baseLines) // Apply it to mine, if possible guard let patchedLines = myLines.applying(diff) else { @@ -46,20 +46,16 @@ print(patched) ### Producing diffs -Collections can only be efficiently diffed when they have a strong sense of order, so difference production is added to `BidirectionalCollection`: +Most diffing algorithms have collection access patterns that are not appropriate for basic collections, so difference production is dependant on conformance to `BidirectionalCollection`: ``` swift @available(swift, introduced: 5.1) extension BidirectionalCollection { /// Returns the difference needed to produce the receiver's state from the - /// parameter's state with the fewest possible changes, using the provided - /// closure to establish equivalence between elements. + /// parameter's state, using the provided closure to establish equivalence + /// between elements. /// - /// This function does not infer element moves, but they can be computed - /// using `OrderedCollectionDifference.inferringMoves()` if desired. - /// - /// Implementation is an optimized variation of the algorithm described by - /// E. Myers (1986). + /// This function does not infer moves. /// /// - Parameters: /// - other: The base state. @@ -69,24 +65,22 @@ extension BidirectionalCollection { /// - Returns: The difference needed to produce the reciever's state from /// the parameter's state. /// - /// - Complexity: O(*n* * *d*), where *n* is `other.count + self.count` and - /// *d* is the number of changes between the two ordered collections. - public func shortestEditScript( + /// - Complexity: For pathological inputs, worst case performance is + /// O(`self.count` * `other.count`). Faster execution can be expected + /// when the collections share many common elements. + public func difference( from other: C, by areEquivalent: (Element, C.Element) -> Bool - ) -> OrderedCollectionDifference + ) -> CollectionDifference where C : BidirectionalCollection, C.Element == Self.Element } extension BidirectionalCollection where Element: Equatable { /// Returns the difference needed to produce the receiver's state from the - /// parameter's state with the fewest possible changes, using equality to - /// establish equivalence between elements. + /// parameter's state, using equality to establish equivalence between + /// elements. /// /// This function does not infer element moves, but they can be computed - /// using `OrderedCollectionDifference.inferringMoves()` if desired. - /// - /// Implementation is an optimized variation of the algorithm described by - /// E. Myers (1986). + /// using `CollectionDifference.inferringMoves()` if desired. /// /// - Parameters: /// - other: The base state. @@ -94,19 +88,21 @@ extension BidirectionalCollection where Element: Equatable { /// - Returns: The difference needed to produce the reciever's state from /// the parameter's state. /// - /// - Complexity: O(*n* * *d*), where *n* is `other.count + self.count` and - /// *d* is the number of changes between the two ordered collections. - public func shortestEditScript(from other: C) -> OrderedCollectionDifference + /// - Complexity: For pathological inputs, worst case performance is + /// O(`self.count` * `other.count`). Faster execution can be expected + /// when the collections share many common elements, or if `Element` + /// also conforms to `Hashable`. + public func difference(from other: C) -> CollectionDifference where C: BidirectionalCollection, C.Element == Self.Element ``` -The `shortestEditScript(from:)` method determines the fewest possible edits required to transition betewen the two states and stores them in a difference type, which is defined as: +The `difference(from:)` method produces an instance of a difference type, defined as: ``` swift -/// A type that represents the difference between two ordered collection states. +/// A type that represents the difference between two collection states. @available(swift, introduced: 5.1) -public struct OrderedCollectionDifference { - /// A type that represents a single change to an ordered collection. +public struct CollectionDifference { + /// A type that represents a single change to a collection. /// /// The `offset` of each `insert` refers to the offset of its `element` in /// the final state after the difference is fully applied. The `offset` of @@ -120,8 +116,8 @@ public struct OrderedCollectionDifference { /// Creates an instance from a collection of changes. /// - /// For clients interested in the difference between two ordered - /// collections, see `OrderedCollection.shortestEditScript(from:)`. + /// For clients interested in the difference between two collections, see + /// `BidirectionalCollection.difference(from:)`. /// /// To guarantee that instances are unambiguous and safe for compatible base /// states, this initializer will fail unless its parameter meets to the @@ -145,7 +141,7 @@ public struct OrderedCollectionDifference { public var removals: [Change] { get } } -/// An OrderedCollectionDifference is itself a Collection. +/// A CollectionDifference is itself a Collection. /// /// The enumeration order of `Change` elements is: /// @@ -165,35 +161,33 @@ public struct OrderedCollectionDifference { /// } /// } /// ``` -extension OrderedCollectionDifference : Collection { - public typealias Element = OrderedCollectionDifference.Change +extension CollectionDifference : Collection { + public typealias Element = CollectionDifference.Change public struct Index: Comparable, Hashable {} } -extension OrderedCollectionDifference.Change: Equatable where ChangeElement: Equatable {} -extension OrderedCollectionDifference: Equatable where ChangeElement: Equatable {} +extension CollectionDifference.Change: Equatable where ChangeElement: Equatable {} +extension CollectionDifference: Equatable where ChangeElement: Equatable {} -extension OrderedCollectionDifference.Change: Hashable where ChangeElement: Hashable {} -extension OrderedCollectionDifference: Hashable where ChangeElement: Hashable { +extension CollectionDifference.Change: Hashable where ChangeElement: Hashable {} +extension CollectionDifference: Hashable where ChangeElement: Hashable { /// Infers which `ChangeElement`s have been both inserted and removed only /// once and returns a new difference with those associations. /// /// - Returns: an instance with all possible moves inferred. /// /// - Complexity: O(*n*) where *n* is `self.count` - public func inferringMoves() -> OrderedCollectionDifference + public func inferringMoves() -> CollectionDifference } -extension OrderedCollectionDifference: Codable where ChangeElement: Codable {} +extension CollectionDifference: Codable where ChangeElement: Codable {} ``` -A `Change` is a single mutating operation, an `OrderedCollectionDifference` is a plurality of such operations that represents a complete transition between two states. Given the interdependence of the changes, `OrderedCollectionDifference` has no mutating members, but it does allow index- and `Slice`-based access to its changes via `Collection` conformance as well as a validating initializer taking a `Collection`. +A `Change` is a single mutating operation, a `CollectionDifference` is a plurality of such operations that represents a complete transition between two states. Given the interdependence of the changes, `CollectionDifference` has no mutating members, but it does allow index- and `Slice`-based access to its changes via `Collection` conformance as well as a validating initializer taking a `Collection`. -Fundamentally, there are only two operations that mutate ordered collections, `insert(_:at:)` and `remove(at:)`, but there are benefits from being able to represent other operations such as moves and replacements, especially for UIs that may want to animate a move differently from an `insert`/`remove` pair. These operations are represented using `associatedWith:`. When non-`nil`, they refer to the offset of the counterpart as described in the headerdoc. +Fundamentally, there are only two operations that mutate collections, `insert(_:at:)` and `remove(_:at:)`, but there are benefits from being able to represent other operations such as moves and replacements, especially for UIs that may want to animate a move differently from an `insert`/`remove` pair. These operations are represented using `associatedWith:`. When non-`nil`, they refer to the offset of the counterpart as described in the headerdoc. -In a similar way, the name `shortestEditScript(from:)` uses a term of art to admit the use of an algorithm that compromises between performance and a minimal output. It computes the [longest common subsequence](https://en.wikipedia.org/wiki/Longest_common_subsequence_problem) between the two collections, but not the [longest common substring](https://en.wikipedia.org/wiki/Longest_common_substring_problem) (which is a much slower operation). In the future other algorithms may be added as different methods to satisfy the need for different performance and output characteristics. - -### Application of instances of `OrderedCollectionDifference` +### Application of instances of `CollectionDifference` ``` swift extension RangeReplaceableCollection { @@ -208,7 +202,7 @@ extension RangeReplaceableCollection { /// - Complexity: O(*n* + *c*), where *n* is `self.count` and *c* is the /// number of changes contained by the parameter. @available(swift, introduced: 5.1) - public func applying(_ difference: OrderedCollectionDifference) -> Self? + public func applying(_ difference: CollectionDifference) -> Self? } ``` @@ -228,22 +222,20 @@ This feature is additive and symbols marked with `@available(swift, introduced: ## Alternatives considered -### `shortestEditScript(from:by:)` defined in protocol instead of extension - -Different algorithms with different premises and/or semantics are free to be defined using different function names. +The following is an incomplete list based on common feedback received during the process of developing this API: ### Communicating changes via a series of callbacks Breaking up a transaction into a sequence of imperative events is not very Swifty, and the pattern has proven to be fertile ground for defects. -### More cases in `OrderedCollectionDifference.Change` +### More cases in `CollectionDifference.Change` While other cases such as `.move` are tempting, the proliferation of code in switch statements is unwanted overhead for clients that don't care about the "how" of a state transition so much as the "what". The use of associated offsets allows for more information to be encoded into the diff without making it more difficult to use. You've already seen how associated offsets can be used to illustrate moves (as produced by `inferringMoves()`): ``` swift -OrderedCollectionDifference([ +CollectionDifference([ .remove(offset:0, element: "value", associatedWith: 4), .insert(offset:4, element: "value", associatedWith: 0) ]) @@ -252,7 +244,7 @@ OrderedCollectionDifference([ But they can also be used to illustrate replacement when the offsets refer to the same position (and the element is different): ``` swift -OrderedCollectionDifference([ +CollectionDifference([ .remove(offset:0, element: "oldvalue", associatedWith: 0), .insert(offset:0, element: "newvalue", associatedWith: 0) ]) @@ -261,7 +253,7 @@ OrderedCollectionDifference([ Differing offsets and elements can be combined when a value is both moved and replaced (or changed): ``` swift -OrderedCollectionDifference([ +CollectionDifference([ .remove(offset:4, element: "oldvalue", associatedWith: 0), .insert(offset:0, element: "newvalue", associatedWith: 4) ]) @@ -277,28 +269,19 @@ Applying a diff can only fail when the base state is incompatible. As such, the Because indexes cannot be navigated in the absence of the collection instance that generated them, a diff based on indexes instead of offsets would be much more limited in usefulness as a boundary type. If indexes are required, they can be rehydrated from the offsets in the presence of the collection(s) to which they belong. -### `OrderedCollection` conformance for `OrderedCollectionDifference` - -Because the change offsets refer directly to the resting positions of elements in the base and modified states, the changes represent the same state transition regardless of their order. The purpose of ordering is to optimize for understanding, safety, and/or performance. In fact, this prototype already contains examples of two different equally valid sort orders: - -* The order provided by `for in` is optimized for safe diff application when modifying a compatible base state one element at a time. -* `applying(_:)` uses a different order where `insert` and `remove` instances are interleaved based on their adjusted offsets in the base state. - -Both sort orders are "correct" in representing the same state transition. - ### `Change` generic on both `BaseElement` and `OtherElement` instead of just `Element` Application of differences would only be possible when both `Element` types were equal, and there would be additional cognitive overhead with comparators with the type `(Element, Other.Element) -> Bool`. -Since the comparator forces both types to be effectively isomorphic, a diff generic over only one type can satisfy the need by mapping one (or both) ordered collections to force their `Element` types to match. +Since the comparator forces both types to be effectively isomorphic, a diff generic over only one type can satisfy the need by mapping one (or both) collections to force their `Element` types to match. -### `difference(from:using:)` with an enum parameter for choosing the diff algorithm instead of `shortestEditScript(from:)` +### `difference(from:using:)` with an enum parameter for choosing the diff algorithm instead of `difference(from:)` This is an attractive API concept, but it can be very cumbersome to extend. This is especially the case for types like `OrderedSet` that—through member uniqueness and fast membership testing—have the capability to support very fast diff algorithms that aren't appropriate for other types. -### `CollectionDifference` or just `Difference` instead of `OrderedCollectionDifference` +### `CollectionDifference` or just `Difference` instead of `CollectionDifference` -The name `OrderedCollectionDifference` gives us the opportunity to build a family of related types in the future, as the difference type in this proposal is (intentionally) unsuitable for representing differences between keyed collections (which don't shift their elements' keys on insertion/removal) or structural differences between treelike collections (which are multidimensional). +The name `CollectionDifference` gives us the opportunity to build a family of related types in the future, as the difference type in this proposal is (intentionally) unsuitable for representing differences between keyed collections (which don't shift their elements' keys on insertion/removal) or structural differences between treelike collections (which are multidimensional). ## Intentional omissions: @@ -306,16 +289,16 @@ The name `OrderedCollectionDifference` gives us the opportunity to build a famil This API allows for more interesting functionality that is not included in this proposal. -For example, this propsal could have included a `reversed()` function on the difference type that would return a new difference that would undo the application of the original. +For example, this propsal could have included a `inverted()` function on the difference type that would return a new difference that would undo the application of the original. The lack of additional conveniences and functionality is intentional; the goal of this proposal is to lay the groundwork that such extensions would be built upon. -In the case of `reversed()`, clients of the API in this proposal can use `Collection.map()` to invert the case of each `Change` and feed the result into `OrderedCollectionDifference.init(_:)`: +In the case of `inverted()`, clients of the API in this proposal can use `Collection.map()` to invert the case of each `Change` and feed the result into `CollectionDifference.init(_:)`: ``` swift -let diff: OrderedCollectionDifference = /* ... */ -let reversed = OrderedCollectionDifference( - diff.map({(change) -> OrderedCollectionDifference.Change in +let diff: CollectionDifference = /* ... */ +let inverted = CollectionDifference( + diff.map({(change) -> CollectionDifference.Change in switch change { case .insert(offset: let o, element: let e, associatedWith: let a): return .remove(offset: o, element: e, associatedWith: a) From a0c5548ce93bd4cfdd14b7db70b1eccf3234fc30 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 30 Jan 2019 20:23:59 -0800 Subject: [PATCH 0992/4563] Accept SE-0240: Ordered Collection Diffing --- proposals/0240-ordered-collection-diffing.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0240-ordered-collection-diffing.md b/proposals/0240-ordered-collection-diffing.md index 8d42c12855..a6ab59975c 100644 --- a/proposals/0240-ordered-collection-diffing.md +++ b/proposals/0240-ordered-collection-diffing.md @@ -3,8 +3,9 @@ * Proposal: [SE-0240](0240-ordered-collection-diffing.md) * Authors: [Scott Perry](https://github.com/numist), [Kyle Macomber](https://github.com/kylemacomber) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (January 14...22, 2019)** +* Status: **Accepted** * Implementation: [apple/swift#21845](https://github.com/apple/swift/pull/21845) +* Decision notes: [Rationale](https://forums.swift.org/t/accepted-with-modifications-se-0240-ordered-collection-diffing/20008) ## Introduction From 14a16438a2f18551aa040841dcf0b62b8044b06c Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Thu, 31 Jan 2019 11:08:26 -0800 Subject: [PATCH 0993/4563] Update SE-0241 to minimal semantics-preserving change. --- ...1-string-index-explicit-encoding-offset.md | 161 +++++++++--------- 1 file changed, 85 insertions(+), 76 deletions(-) diff --git a/proposals/0241-string-index-explicit-encoding-offset.md b/proposals/0241-string-index-explicit-encoding-offset.md index 4e3f0bca56..de20fa9068 100644 --- a/proposals/0241-string-index-explicit-encoding-offset.md +++ b/proposals/0241-string-index-explicit-encoding-offset.md @@ -1,14 +1,14 @@ -# Explicit Encoded Offsets for String Indices +# Deprecate String Index Encoded Offsets * Proposal: [SE-0241](https://github.com/apple/swift-evolution/blob/master/proposals/0241-string-index-explicit-encoding-offset.md) * Authors: [Michael Ilseman](https://github.com/milseman) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (January 29th...February 3rd, 2019)** +* Status: **Active review (January 29th…February 3rd, 2019)** * Implementation: [apple/swift#22108](https://github.com/apple/swift/pull/22108) * Review: ([review](https://forums.swift.org/t/se-0241-explicit-encoded-offsets-for-string-indices/19929)) ## Introduction -[SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md) introduced a computed variable and initializer surrounding the concept of an `encodedOffset`. Unfortunately, that approach is flawed for its intended purpose, and is commonly misused in ways that Swift 5 is [more likely to expose](https://bugs.swift.org/browse/SR-9749). We propose a set of alternative APIs for each intended use and every existing misuse. +[SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md) introduced a computed variable and initializer surrounding the concept of an `encodedOffset` for serialization purposes. Unfortunately, that approach is flawed for its intended purpose and is commonly misused in ways that Swift 5 is [more likely to expose](https://bugs.swift.org/browse/SR-9749). It is too late in the Swift 5.0 release to solve all existing problems, so we propose deprecating `encodedOffset` and introducing a targeted, semantics-preserving alternative. ## Motivation @@ -16,16 +16,71 @@ String abstracts away details about the underlying encoding used in its storage. String was always meant to be capable of handling multiple backing encodings for its contents, and this is realized in Swift 5. [String now uses UTF-8](https://forums.swift.org/t/string-s-abi-and-utf-8/17676) for its preferred “fast” native encoding, but has a resilient fallback for strings of different encodings. Currently, we only use this fall-back for lazily-bridged Cocoa strings, which are commonly encoded as UTF-16, though it can be extended in the future thanks to resilience. -Unfortunately, [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s approach of a single notion of `encodedOffset` is flawed. A string can be serialized with a choice of encodings, and the offset is therefore encoding-dependent and requires access to the contents of the string to calculate. A comment in [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)'s example source mentioned that `encodedOffset` assumes UTF-16, which happened to be the only encoding used internally by String at the time (for offset purposes). +Unfortunately, [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s approach of a single notion of `encodedOffset` is flawed. A string can be serialized with a choice of encodings, and the offset is therefore encoding-dependent and requires access to the contents of the string to calculate. A comment in [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s example source mentioned that `encodedOffset` assumes UTF-16, which happened to be the only encoding used internally by String at the time (for offset purposes). Furthermore, the majority of uses of `encodedOffset` in the wild are not following [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s intended purpose and are sensitive to encoding changes. `encodedOffset` is frequently misused under the assumption that all Characters are comprised of a single code unit, which is error-prone and Swift 5 might surface the underlying bugs in more situations. It is also sometimes used for mapping Cocoa string indices, which happens to work in Swift 4 but might not in Swift 5, and Foundation already provides better alternatives. -Because Swift 5 may introduce a semantic difference in behavior, it is important to rush this fix into the 5.0 release so that developers can preserve existing semantics or switch to correct semantics as needed. We hope the majority of existing uses will be replaced with something more ergonomic such as [offset-based subscripting](https://forums.swift.org/t/shorthand-for-offsetting-startindex-and-endindex/9397) in a future release, but we need some migration path for code now. ## Proposed solution -We propose fixing all existing use and misuse scenarios by adding per-view offset initializers and methods to `String.Index`, in addition to one for the default view of Characters. We propose deprecating `encodedOffset` to direct existing code towards these replacements. +We propose a targeted semantics-preserving off-ramp for uses of `encodedOffset`. Because Swift 5 may introduce a semantic difference in behavior, it is important to rush this fix into the 5.0 release so that developers can preserve existing semantics. Potential solutions to other problems, including the original intended purpose of `encodedOffset`, are highlighted in “Alternatives Considered”. + + +## Detailed design + +```swift +extension String.Index { + /// The UTF-16 code unit offset corresponding to this Index + public func utf16Offset(in s: S) -> Int { + return s.utf16.distance(from: s.utf16.startIndex, to: self) + } + /// Creates a new index at the specified UTF-16 code unit offset + /// + /// - Parameter offset: An offset in UTF-16 code units. + public init(utf16Offset offset: Int, in s: S) { + let (start, end) = (s.utf16.startIndex, s.utf16.endIndex) + guard offset >= 0, + let idx = s.utf16.index(start, offsetBy: offset, limitedBy: end) + else { + self = end.nextEncoded // internal method returning endIndex+1 + return + } + self = idx + } +} + +``` + +We try to match the original semantics as close as we reasonably can. If the user supplies an out-of-bounds offset to the initializer, we will form an invalid index. If the out-of-bounds offset is exactly equivalent to the count, the returned index will compare equal with `endIndex`, otherwise it will compare greater than `endIndex`. + + +## Source Compatibility + +This deprecates existing API and provides a semantics-preserving alternative. Deprecation preserves source compatibility and strongly hints towards correct usage. But, other changes in Swift 5 introduce potential semantic drift in old code. + +## Effect of ABI stability + +This change is ABI-additive, but necessary due to other ABI changes in Swift 5. + +## Effect on API resilience + +Added APIs are all resilient and can be replaced with more efficient implementations that preserve correctness as String evolves. + +## Alternatives Considered + +### Do Nothing + +If `encodedOffset` was only used for serialization, *and* such serialization/deserialization would record and preserve the original encoding, *and* we amend [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s comment to avoid nailing it down to any given encoding, no change would be necessary. Unfortunately, there is no way to query or preserve internal encoding and there is considerable use and misuse in the wild, as mentioned in the “Uses in the Wild” disclosure section. + +### Fix all the Bugs + +This proposal originally introduced a set of API attempting to solve 3 problems: + +1. SE-0180’s `encodedOffset`, meant for serialization purposes, needs to be parameterized over the encoding in which the string will be serialized in +2. Existing uses of `encodedOffset` need a semantics-preserving off-ramp for Swift 5, which is expressed in terms of UTF-16 offsets +3. Existing misuses of `encodedOffset`, which assume all characters are a single UTF-16 code unit, need a semantics-fixing alternative +
    Details: String’s views and encodings @@ -40,7 +95,7 @@ Array(myString); Array(myString.indices) // Not an encoding, but provides offset ```
    -### Uses in the Wild +#### Uses in the Wild
    GitHub code search yields [nearly 1500 uses](https://github.com/search?l=Swift&q=encodedOffset&type=Code) , and nearly-none of them are for [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s intended purpose. Below I present the 3 most common uses. @@ -109,109 +164,63 @@ let subStr = myString[strLower.. +#### Potential Solution -## Detailed design +
    Original Proposed Solution + +Here is a (slightly revised) version of the original proposal: ```swift -extension String.Index { /// The UTF-16 code unit offset corresponding to this Index - public func offset(within utf16: String.UTF16View) -> Int { - return utf16.distance(from: utf16.startIndex, to: self) - } + public func offset(in utf16: S.UTF16View) -> Int { ... } + /// The UTF-8 code unit offset corresponding to this Index - public func offset(within utf8: String.UTF8View) -> Int { - return utf8.distance(from: utf8.startIndex, to: self) - } + public func offset(in utf8: S.UTF8View) -> Int { ... } + /// The Unicode scalar offset corresponding to this Index - public func offset(within scalars: String.UnicodeScalarView) -> Int { - return scalars.distance(from: scalars.startIndex, to: self) - } + public func offset(in scalars: S.UnicodeScalarView) -> Int { ... } + /// The Character offset corresponding to this Index - public func offset(within str: String) -> Int { - return str.distance(from: str.startIndex, to: self) - } + public func offset(in str: S) -> Int { ... } /// Creates a new index at the specified UTF-16 code unit offset /// /// - Parameter offset: An offset in UTF-16 code units. - public init(offset: Int, within utf16: String.UTF16View) { - let (start, end) = (utf16.startIndex, utf16.endIndex) - guard offset >= 0, - let idx = utf16.index(start, offsetBy: offset, limitedBy: end) - else { - self = end - return - } - self = idx - } + public init(offset: Int, in utf16: S.UTF16View) { ... } /// Creates a new index at the specified UTF-8 code unit offset /// /// - Parameter offset: An offset in UTF-8 code units. - public init(offset: Int, within utf8: String.UTF8View) { - let (start, end) = (utf8.startIndex, utf8.endIndex) - guard offset >= 0, - let idx = utf8.index(start, offsetBy: offset, limitedBy: end) - else { - self = end - return - } - self = idx - } + public init(offset: Int, in utf8: S.UTF8View) { ... } /// Creates a new index at the specified Unicode scalar offset /// /// - Parameter offset: An offset in terms of Unicode.Scalars - public init(offset: Int, within scalars: String.UnicodeScalarView) { - let (start, end) = (scalars.startIndex, scalars.endIndex) - guard offset >= 0, - let idx = scalars.index(start, offsetBy: offset, limitedBy: end) - else { - self = end - return - } - self = idx - } + public init(offset: Int, in scalars: S.UnicodeScalarView) { ... } /// Creates a new index at the specified Character offset /// /// - Parameter offset: An offset in terms of Characters - public init(offset: Int, within str: String) { - let (start, end) = (str.startIndex, str.endIndex) - guard offset >= 0, - let idx = str.index(start, offsetBy: offset, limitedBy: end) - else { - self = end - return - } - self = idx - } + public init(offset: Int, in str: S) { ... } } - ``` + This gives developers: 1. The ability to choose a specific encoding for serialization, the original intended purpose. 2. The ability to fix any code that assumed fixed-encoding-width Characters by choosing the most-natural variant that just takes a String. 3. The ability to migrate their uses for Cocoa index mapping by choosing UTF-16. +However, it’s not clear this is the best approach for Swift and more design work is needed: -## Source Compatibility - -This deprecates existing API and provides alternatives. Deprecation preserves source compatibility and strongly hints towards correct usage. But, other changes in Swift 5 introduce potential semantic drift. - -## Effect of ABI stability - -This change is necessary to realize our goal of opaque String indices and an encoding-abstracted String representation in time for ABI stability in Swift 5. - -## Effect on API resilience +* Overloading only on view type makes it easy to accidentally omit a view and end up with character offsets. E.g. `String.Index(offset: myUTF16Offset, in: myUTF16String)` instead of `String.Index(offset: myUTF16Offset, in: myUTF16String.utf16)`. +* Producing new indices is usually done by the collection itself rather than parameterizing an index initializer. This should be handled with something more ergonomic such as offset-based indexing in a future release. +* In real code in the wild, almost all created indices are immediately used to subscript the string or one of its views. This should be handled with something more ergonomic such as [offset-based subscripting](https://forums.swift.org/t/shorthand-for-offsetting-startindex-and-endindex/9397) in a future release. -Added APIs are all resilient and can be replaced with more efficient implementations that preserve correctness as String evolves. +
    -## Alternatives Considered +#### Conclusion -### Do Nothing - -If `encodedOffset` was only used for serialization, *and* such serialization/deserialization would record and preserve the original encoding, *and* we amend [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s comment to avoid nailing it down to any given encoding, no change would be necessary. Unfortunately, there is no way to query or preserve internal encoding and there is considerable use and misuse in the wild, as mentioned in the “Uses in the Wild” disclosure section. +It is too late in the Swift 5.0 release to design and add all of these API. Instead, we’re proposing an urgent, targeted fix for the second problem. From f13b96987234b9abac0ce188212f12cce13e688b Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 31 Jan 2019 14:30:12 -0500 Subject: [PATCH 0994/4563] Extend the SE-0241 review by two days --- proposals/0241-string-index-explicit-encoding-offset.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0241-string-index-explicit-encoding-offset.md b/proposals/0241-string-index-explicit-encoding-offset.md index de20fa9068..1bdd8dbafe 100644 --- a/proposals/0241-string-index-explicit-encoding-offset.md +++ b/proposals/0241-string-index-explicit-encoding-offset.md @@ -2,7 +2,7 @@ * Proposal: [SE-0241](https://github.com/apple/swift-evolution/blob/master/proposals/0241-string-index-explicit-encoding-offset.md) * Authors: [Michael Ilseman](https://github.com/milseman) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (January 29th…February 3rd, 2019)** +* Status: **Active review (January 29th…February 5th, 2019)** * Implementation: [apple/swift#22108](https://github.com/apple/swift/pull/22108) * Review: ([review](https://forums.swift.org/t/se-0241-explicit-encoded-offsets-for-string-indices/19929)) From 985d06926306d1ab633161b547d30790a5b2a6d4 Mon Sep 17 00:00:00 2001 From: William Entriken Date: Wed, 6 Feb 2019 12:28:00 -0500 Subject: [PATCH 0995/4563] Typo (#985) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a1d804878a..2a40d722c3 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ The need to achieve ABI stability in Swift 5 will guide most of the priorities f - **Generics features needed for standard library**. We will finish implementing [conditional conformances](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md) and [recursive protocol requirements](https://github.com/apple/swift-evolution/blob/master/proposals/0157-recursive-protocol-constraints.md), which are needed for the standard library to achieve ABI stability. Both of these have gone through the evolution proposal process and there are no known other generics enhancements needed for ABI stability. -- **API resilience**. We will implement the essential pieces need to support API resilience, in order to allow public APIs for a library to evolve over time while maintaining a stable ABI. +- **API resilience**. We will implement the essential pieces needed to support API resilience, in order to allow public APIs for a library to evolve over time while maintaining a stable ABI. - **Memory ownership model**. An (opt-in) Cyclone/Rust-inspired memory [ownership model](https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md) is strongly desirable for systems programming and for other high-performance applications that require predictable and deterministic performance. Part of this model was introduced in Swift 4 when we began to [ enforce exclusive access to memory](https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md). In Swift 5 our goal is to tackle the [pieces of the ownership model that are key to ABI stability](https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md#priorities-for-abi-stability). From c1da486e4963538a49e3a6bb7dc11cf1e21d3172 Mon Sep 17 00:00:00 2001 From: Sho Ikeda Date: Tue, 12 Feb 2019 03:34:32 +0900 Subject: [PATCH 0996/4563] Update the statuses for SE-0236, SE-0238 and SE-0239 (#981) * [SE-0236] Update the status to Implemented (Swift 5) * [SE-0238] Update the status to Implemented (Swift 5) * [SE-0239] Update the status to Implemented (Swift 5) --- .../0236-package-manager-platform-deployment-settings.md | 2 +- proposals/0238-package-manager-build-settings.md | 2 +- proposals/0239-codable-range.md | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/proposals/0236-package-manager-platform-deployment-settings.md b/proposals/0236-package-manager-platform-deployment-settings.md index 8371e5d0e5..d960b744e8 100644 --- a/proposals/0236-package-manager-platform-deployment-settings.md +++ b/proposals/0236-package-manager-platform-deployment-settings.md @@ -3,7 +3,7 @@ * Proposal: [SE-0236](0236-package-manager-platform-deployment-settings.md) * Authors: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **Accepted with revisions** +* Status: **Implemented (Swift 5)** * Decision Notes: [Rationale](https://forums.swift.org/t/accepted-with-modifications-se-0236-package-manager-platform-deployment-settings/18420) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/aebb22c0f3e139fd921d14f79c3945af99d0342d/proposals/0236-package-manager-platform-deployment-settings.md) diff --git a/proposals/0238-package-manager-build-settings.md b/proposals/0238-package-manager-build-settings.md index e852abe1b8..a739eb65a8 100644 --- a/proposals/0238-package-manager-build-settings.md +++ b/proposals/0238-package-manager-build-settings.md @@ -4,7 +4,7 @@ * Decision Notes: [Draft Thread](https://forums.swift.org/t/draft-proposal-target-specific-build-settings/18031) * Authors: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **Accepted with revisions** +* Status: **Implemented (Swift 5)** * Decision Notes: [Rationale](https://forums.swift.org/t/accepted-with-modifications-se-0238-package-manager-target-specific-build-settings/18590) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/1a2801a3dc912b093f2cda13eafd54f0d98b3c8e/proposals/0238-package-manager-build-settings.md) diff --git a/proposals/0239-codable-range.md b/proposals/0239-codable-range.md index 4587759ec1..db707108b8 100644 --- a/proposals/0239-codable-range.md +++ b/proposals/0239-codable-range.md @@ -3,8 +3,9 @@ * Proposal: [SE-0239](0239-codable-range.md) * Authors: [Dale Buckley](https://github.com/dlbuckley), [Ben Cohen](https://github.com/airspeedswift), [Maxim Moiseev](https://github.com/moiseev) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Implementation: [apple/swift#19532](https://github.com/apple/swift/pull/19532) -* Status: **Accepted** +* Implementation: [apple/swift#19532](https://github.com/apple/swift/pull/19532), + [apple/swift#21857](https://github.com/apple/swift/pull/21857) +* Status: **Implemented (Swift 5)** * Review: [Discussion thread](https://forums.swift.org/t/se-0239-add-codable-conformance-to-range-types/18794/51) From 0a755f3fd4fd00f4f66811ee1e1cf97a85dd1188 Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Fri, 15 Feb 2019 11:25:09 +0330 Subject: [PATCH 0997/4563] fixed code style --- index.html | 2 +- index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 6edbf190f8..2472d081f7 100644 --- a/index.html +++ b/index.html @@ -77,7 +77,7 @@

    Swift Evolution

      - +
    diff --git a/index.js b/index.js index 1cc846d563..31b622051f 100644 --- a/index.js +++ b/index.js @@ -131,7 +131,7 @@ function init () { document.querySelector('#proposals-count-number').innerText = 'Proposal data failed to load.' }) - document.querySelector('#proposals-count-number').innerHTML = 'Loading ...'; + document.querySelector('#proposals-count-number').innerHTML = 'Loading ...' req.open('get', 'https://data.swift.org/swift-evolution/proposals') req.send() } From c8e3dc45b40feb4d28c806539827a2f531763579 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 15 Feb 2019 15:09:00 -0500 Subject: [PATCH 0998/4563] Accept SE-0241 --- proposals/0241-string-index-explicit-encoding-offset.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0241-string-index-explicit-encoding-offset.md b/proposals/0241-string-index-explicit-encoding-offset.md index 1bdd8dbafe..aa13d7d199 100644 --- a/proposals/0241-string-index-explicit-encoding-offset.md +++ b/proposals/0241-string-index-explicit-encoding-offset.md @@ -2,9 +2,9 @@ * Proposal: [SE-0241](https://github.com/apple/swift-evolution/blob/master/proposals/0241-string-index-explicit-encoding-offset.md) * Authors: [Michael Ilseman](https://github.com/milseman) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (January 29th…February 5th, 2019)** +* Status: **Accepted** * Implementation: [apple/swift#22108](https://github.com/apple/swift/pull/22108) -* Review: ([review](https://forums.swift.org/t/se-0241-explicit-encoded-offsets-for-string-indices/19929)) +* Review: ([review](https://forums.swift.org/t/se-0241-explicit-encoded-offsets-for-string-indices/19929)) ([acceptance](https://forums.swift.org/t/accepted-se-0241-explicit-encoded-offsets-for-string-indices/20540)) ## Introduction From faede89ab1828148d67ee62efbb8d40b96193c9a Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 18 Feb 2019 10:27:51 -0600 Subject: [PATCH 0999/4563] Synthesize default values for the memberwise initializer (#936) * Synthesize default values for memberwise initializer * in -> for --- proposals/nnnn-default-values-memberwise.md | 95 +++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 proposals/nnnn-default-values-memberwise.md diff --git a/proposals/nnnn-default-values-memberwise.md b/proposals/nnnn-default-values-memberwise.md new file mode 100644 index 0000000000..282c313d25 --- /dev/null +++ b/proposals/nnnn-default-values-memberwise.md @@ -0,0 +1,95 @@ +# Synthesize default values for the memberwise initializer + +* Proposal: [SE-NNNN](NNNN-default-values-memberwise.md) +* Author: [Alejandro Alonso](https://github.com/Azoy) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#19743](https://github.com/apple/swift/pull/19743) + +## Introduction + +This proposal aims to solve a simple outstanding problem with the way the Swift compiler currently synthesizes the memberwise initializer for structures by synthesizing default values for properties with default initializers. + +*This is mentioned in the "State of the Memberwise Initializer" forum post: [here](https://forums.swift.org/t/state-of-the-memberwise-initializer/17168)* + +## Motivation + +Currently the Swift compiler is able to synthesize a fairly basic memberwise initializer for structures. + +```swift +struct Dog { + var age: Int + var name: String +} +``` + +The compiler is able to synthesize a memberwise iniailizer for this structure which simply looks like this: + +```swift +init(age: Int, name: String) +``` + +But, lets say we want all dogs to have a default value of `0` for the age: + +```swift +struct Dog { + var age: Int = 0 + var name: String +} +``` + +A user might naively try using this default value when constructing their `Dog` instance: + +```swift +// I just want to set the name of Dog, sparky is a newborn +let sparky = Dog(name: "Sparky") +``` + +To their surprise, they can't. `missing argument for parameter 'age' in call`. Using the compiler synthesized memberwise initializer has turned to become a nuisance rather than a nice removal of boilerplate. In many cases the user may optionally just define their own initializer with a default value for the age parameter. + +```swift +struct Dog { + var age: Int = 0 + var name: String + + // This is defined because the Swift compiler can't generate default values for properties with an initial value + init(age: Int = 0, name: String) { + self.age = age + self.name = name + } +} +``` + +## Proposed solution + +I propose simply doing the obvious and synthesizing default values for properties with default initializers in the memberwise initializer. Simple code like the following will simply work: + +```swift +struct Dog { + var age: Int = 0 + var name: String +} + +// This now works +let sparky = Dog(name: "Sparky") // Dog(age: 0, name: "Sparky") +``` + +## Detailed design + +This change does not alter the requirements needed to synthesize the memberwise initializer, but rather if we can synthesize the memberwise initializer, also synthesize default values for properties with default initializers. Note that we can only synthesize values for *variables* that have declared default initializers and not *constants*. + +## Source compatibility + +This is a purely additive feature, thus source compatibility is not affected. + +## Effect on ABI stability + +This feature does not alter ABI, thus ABI stability is not affected. + +## Effect on API resilience + +As the memberwise initializer is only synthesized as an internal initializer, this feature does not affect API resilience. + +## Alternatives considered + +We could simply not do this and save this proposal for a solution much larger in regards to fixing more problems the memberwise initializer has. The downside is that we put off obvious changes like this for much longer because of wanting to solve a bigger problem. I agree we should solve the bigger problems, but by solving problems like this it aids in the solution of the larger problem. \ No newline at end of file From 1741d71d75cb5fff735883c03c9b0b8f1a513139 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Mon, 18 Feb 2019 08:32:21 -0800 Subject: [PATCH 1000/4563] Launch review for SE-0242. --- ...lues-memberwise.md => 0242-default-values-memberwise.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{nnnn-default-values-memberwise.md => 0242-default-values-memberwise.md} (94%) diff --git a/proposals/nnnn-default-values-memberwise.md b/proposals/0242-default-values-memberwise.md similarity index 94% rename from proposals/nnnn-default-values-memberwise.md rename to proposals/0242-default-values-memberwise.md index 282c313d25..b9e562e2c7 100644 --- a/proposals/nnnn-default-values-memberwise.md +++ b/proposals/0242-default-values-memberwise.md @@ -1,9 +1,9 @@ # Synthesize default values for the memberwise initializer -* Proposal: [SE-NNNN](NNNN-default-values-memberwise.md) +* Proposal: [SE-0242](0242-default-values-memberwise.md) * Author: [Alejandro Alonso](https://github.com/Azoy) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Status: **Active review (February 18 - February 26, 2019)** * Implementation: [apple/swift#19743](https://github.com/apple/swift/pull/19743) ## Introduction From f836d66a4d9207682a4400da21da8dc229af5626 Mon Sep 17 00:00:00 2001 From: Lily Ballard Date: Tue, 19 Feb 2019 21:03:02 -0800 Subject: [PATCH 1001/4563] Update my name in proposals (#986) --- proposals/0007-remove-c-style-for-loops.md | 2 +- proposals/0010-add-staticstring-unicodescalarview.md | 2 +- proposals/0015-tuple-comparison-operators.md | 2 +- proposals/0032-sequencetype-find.md | 2 +- proposals/0034-disambiguating-line.md | 2 +- proposals/0045-scan-takewhile-dropwhile.md | 2 +- proposals/0068-universal-self.md | 2 +- proposals/0075-import-test.md | 2 +- proposals/0094-sequence-function.md | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/proposals/0007-remove-c-style-for-loops.md b/proposals/0007-remove-c-style-for-loops.md index def47e117c..e6124e2a04 100644 --- a/proposals/0007-remove-c-style-for-loops.md +++ b/proposals/0007-remove-c-style-for-loops.md @@ -101,6 +101,6 @@ for(var i=0 ; i < array.count ;i++){ * "For what it's worth we don't have a single C style for loop in the Lyft codebase." -- Keith Smiley, keithbsmiley@gmail.com * "Just checked; ditto Khan Academy." -- Andy Matsuchak, andy@andymatuschak.org * "We’ve developed a number of Swift apps for various clients over the past year and have not needed C style for loops either." -- Eric Chamberlain, eric.chamberlain@arctouch.com -* "Every time I've tried to use a C-style for loop, I've ended up switching to a while loop because my iteration variable ended up having the wrong type (e.g. having an optional type when the value must be non-optional for the body to execute). The Postmates codebase contains no instances of C-style for loops in Swift." -- Kevin Ballard, kevin@sb.org +* "Every time I've tried to use a C-style for loop, I've ended up switching to a while loop because my iteration variable ended up having the wrong type (e.g. having an optional type when the value must be non-optional for the body to execute). The Postmates codebase contains no instances of C-style for loops in Swift." -- Lily Ballard, lily@sb.org * "I found a couple of cases of them in my codebase, but they were trivially transformed into “proper” Swift-style for loops that look better anyway. If it were a vote, I’d vote for eliminating C-style." -- Sean Heber, sean@fifthace.com diff --git a/proposals/0010-add-staticstring-unicodescalarview.md b/proposals/0010-add-staticstring-unicodescalarview.md index dc4d4c1a5e..58f9477d64 100644 --- a/proposals/0010-add-staticstring-unicodescalarview.md +++ b/proposals/0010-add-staticstring-unicodescalarview.md @@ -1,7 +1,7 @@ # Add StaticString.UnicodeScalarView * Proposal: [SE-0010](0010-add-staticstring-unicodescalarview.md) -* Author: [Kevin Ballard](https://github.com/kballard) +* Author: [Lily Ballard](https://github.com/lilyball) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Rejected** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-February/000045.html) diff --git a/proposals/0015-tuple-comparison-operators.md b/proposals/0015-tuple-comparison-operators.md index d0335ddda8..4c8836c11a 100644 --- a/proposals/0015-tuple-comparison-operators.md +++ b/proposals/0015-tuple-comparison-operators.md @@ -1,7 +1,7 @@ # Tuple comparison operators * Proposal: [SE-0015](0015-tuple-comparison-operators.md) -* Author: [Kevin Ballard](https://github.com/kballard) +* Author: [Lily Ballard](https://github.com/lilyball) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) * Status: **Implemented (Swift 2.2)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/004423.html) diff --git a/proposals/0032-sequencetype-find.md b/proposals/0032-sequencetype-find.md index eadeee7f55..e141c7ac5b 100644 --- a/proposals/0032-sequencetype-find.md +++ b/proposals/0032-sequencetype-find.md @@ -1,7 +1,7 @@ # Add `first(where:)` method to `Sequence` * Proposal: [SE-0032](0032-sequencetype-find.md) -* Author: [Kevin Ballard](https://github.com/kballard) +* Author: [Lily Ballard](https://github.com/lilyball) * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000134.html) diff --git a/proposals/0034-disambiguating-line.md b/proposals/0034-disambiguating-line.md index cab8a53630..f47d0a4984 100644 --- a/proposals/0034-disambiguating-line.md +++ b/proposals/0034-disambiguating-line.md @@ -45,7 +45,7 @@ file-name → static-string-literal­ ## Alternatives considered -A more flexible grammar was suggested, however, as Kevin Ballard pointed out: +A more flexible grammar was suggested, however, as Lily Ballard pointed out: > This feature isn't something end users are going to use. And it's not something that will ever reasonably apply to anything except `#file` and `#line`. This feature is only ever intended to be used by tools that auto-generate source files. The most important concerns here really should just be that whatever we use is trivial to generate correctly by even the simplest of tools and is readable. And since this won't ever apply to anything beyond `#file` and `#line`, there's no need to try to generalize this feature at all. diff --git a/proposals/0045-scan-takewhile-dropwhile.md b/proposals/0045-scan-takewhile-dropwhile.md index 77020a821a..b5bb721125 100644 --- a/proposals/0045-scan-takewhile-dropwhile.md +++ b/proposals/0045-scan-takewhile-dropwhile.md @@ -1,7 +1,7 @@ # Add prefix(while:) and drop(while:) to the stdlib * Proposal: [SE-0045](0045-scan-takewhile-dropwhile.md) -* Author: [Kevin Ballard](https://github.com/kballard) +* Author: [Lily Ballard](https://github.com/lilyball) * Review Manager: [Chris Lattner](http://github.com/lattner) * Status: **Implemented (Swift 3.1)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000136.html) diff --git a/proposals/0068-universal-self.md b/proposals/0068-universal-self.md index df51a09475..cda7483211 100644 --- a/proposals/0068-universal-self.md +++ b/proposals/0068-universal-self.md @@ -56,7 +56,7 @@ Not at this time ## Acknowledgements -Thanks Sean Heber, Kevin Ballard, Joe Groff, Timothy Wood, Brent Royal-Gordon, Andrey Tarantsov, Austin Zheng +Thanks Sean Heber, Lily Ballard, Joe Groff, Timothy Wood, Brent Royal-Gordon, Andrey Tarantsov, Austin Zheng ## Rationale diff --git a/proposals/0075-import-test.md b/proposals/0075-import-test.md index d25dfe0041..5683741c2c 100644 --- a/proposals/0075-import-test.md +++ b/proposals/0075-import-test.md @@ -11,7 +11,7 @@ Expanding the build configuration suite to test for the ability to import certain modules was [first introduced](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010693.html) -on the Swift-Evolution list by Kevin Ballard. Although his initial idea (checking for Darwin +on the Swift-Evolution list by Lily Ballard. Although her initial idea (checking for Darwin to differentiate Apple targets from non-Apple targets) proved problematic, developers warmly greeted the notion of an import-based configuration test. Dmitri Gribenko wrote, "There's a direction that we want to move to a unified name for the libc module for all platform, so 'can import Darwin' might not be a viable long-term strategy." diff --git a/proposals/0094-sequence-function.md b/proposals/0094-sequence-function.md index dd1d6a6a30..03fd09abd0 100644 --- a/proposals/0094-sequence-function.md +++ b/proposals/0094-sequence-function.md @@ -1,7 +1,7 @@ # Add sequence(first:next:) and sequence(state:next:) to the stdlib * Proposal: [SE-0094](0094-sequence-function.md) -* Authors: [Kevin Ballard](https://github.com/kballard), [Erica Sadun](http://github.com/erica) +* Authors: [Lily Ballard](https://github.com/lilyball), [Erica Sadun](http://github.com/erica) * Review Manager: [Chris Lattner](http://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000170.html) From c904d7890ee8aeaf4e96522e0559fd19bf3fa09b Mon Sep 17 00:00:00 2001 From: Bernhard Schmidt-Hackenberg Date: Wed, 20 Feb 2019 18:22:43 +0100 Subject: [PATCH 1002/4563] Update 0235-add-result.md (#987) fixed a spelling error on line 361: "previous verson" -> "version" --- proposals/0235-add-result.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0235-add-result.md b/proposals/0235-add-result.md index d33c9eee3c..bfb4547fed 100644 --- a/proposals/0235-add-result.md +++ b/proposals/0235-add-result.md @@ -358,7 +358,7 @@ Constraining the error type to conform to `Error` is a very low burden, and it h ### Operations -A previous verson of this proposal included operations for optionally projecting out the `value` and `error` cases. These operations are useful, but they should be added uniformly for all `enum`s, and `Result` should not commit to providing hard-coded versions that may interfere with future language evolution. In the meantime, it is easy for programmers to add these operations with extensions in their own code. +A previous version of this proposal included operations for optionally projecting out the `value` and `error` cases. These operations are useful, but they should be added uniformly for all `enum`s, and `Result` should not commit to providing hard-coded versions that may interfere with future language evolution. In the meantime, it is easy for programmers to add these operations with extensions in their own code. A previous version of this proposal included a `fold` operation. This operation is essentially an expression-based `switch`, and like the optional case projections, it would be better to provide a general language solution for it than to add a more limited design that covers only a single type. From cea936367d83f0e0387157ce015e9f051e75a0b9 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 21 Feb 2019 09:55:10 +0900 Subject: [PATCH 1003/4563] SE-0200 add scala syntax spec link for completeness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds link to Scala language syntax spec in prior art section – mostly just for completeness, since most other languages in the table contain such link. --- proposals/0200-raw-string-escaping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0200-raw-string-escaping.md b/proposals/0200-raw-string-escaping.md index 9420f5707c..422addb28f 100644 --- a/proposals/0200-raw-string-escaping.md +++ b/proposals/0200-raw-string-escaping.md @@ -175,7 +175,7 @@ The following links explore the existing art in other languages. We were inspire | `R"(Hello, world!)"` | [C++11](https://en.cppreference.com/w/cpp/language/string_literal) | Yes | No | | `r"Hello, world!"` | [D](https://tour.dlang.org/tour/en/basics/alias-strings), [Python](http://wiki.c2.com/?RawStrings) | Yes | No | | `r#"Hello, world!"#` | [Rust](https://doc.rust-lang.org/reference/tokens.html#raw-string-literals) | Yes | Would need to drop the opening `r` and maybe change the delimiter from `#`. | -| `"""hello \' world"""` and `raw"Hello, world!"` | Scala | No/Yes | No | +| `"""hello \' world"""` and `raw"Hello, world!"` | [Scala](https://www.scala-lang.org/files/archive/spec/2.13/13-syntax-summary.html) | No/Yes | No | | ``` `Hello, world!` ``` | [D](https://tour.dlang.org/tour/en/basics/alias-strings), [Go](https://golang.org/ref/spec), \`...\` | No (conflicts with escaped identifiers) | No, needs Rust multiplicity | | ``` ``...`` ``` | [Java](http://openjdk.java.net/jeps/326), any number of \` | No (conflicts with escaped identifiers) | Yes | From 616c613fc0770d5e18ad19fd77aeef57246ebf58 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 22 Feb 2019 23:06:47 +0000 Subject: [PATCH 1004/4563] [SE-0241] Update status to implemented (Swift 5) --- proposals/0241-string-index-explicit-encoding-offset.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/0241-string-index-explicit-encoding-offset.md b/proposals/0241-string-index-explicit-encoding-offset.md index aa13d7d199..33a349037e 100644 --- a/proposals/0241-string-index-explicit-encoding-offset.md +++ b/proposals/0241-string-index-explicit-encoding-offset.md @@ -1,8 +1,9 @@ # Deprecate String Index Encoded Offsets -* Proposal: [SE-0241](https://github.com/apple/swift-evolution/blob/master/proposals/0241-string-index-explicit-encoding-offset.md) -* Authors: [Michael Ilseman](https://github.com/milseman) + +* Proposal: [SE-0241](0241-string-index-explicit-encoding-offset.md) +* Author: [Michael Ilseman](https://github.com/milseman) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5)** * Implementation: [apple/swift#22108](https://github.com/apple/swift/pull/22108) * Review: ([review](https://forums.swift.org/t/se-0241-explicit-encoded-offsets-for-string-indices/19929)) ([acceptance](https://forums.swift.org/t/accepted-se-0241-explicit-encoded-offsets-for-string-indices/20540)) From 8c1462592939271449c841cbcd1511fbc002f09c Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 22 Feb 2019 23:37:39 +0000 Subject: [PATCH 1005/4563] [SE-0241] Replace inline links with references --- .../0241-string-index-explicit-encoding-offset.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/proposals/0241-string-index-explicit-encoding-offset.md b/proposals/0241-string-index-explicit-encoding-offset.md index 33a349037e..e1ceb3a54c 100644 --- a/proposals/0241-string-index-explicit-encoding-offset.md +++ b/proposals/0241-string-index-explicit-encoding-offset.md @@ -9,17 +9,17 @@ ## Introduction -[SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md) introduced a computed variable and initializer surrounding the concept of an `encodedOffset` for serialization purposes. Unfortunately, that approach is flawed for its intended purpose and is commonly misused in ways that Swift 5 is [more likely to expose](https://bugs.swift.org/browse/SR-9749). It is too late in the Swift 5.0 release to solve all existing problems, so we propose deprecating `encodedOffset` and introducing a targeted, semantics-preserving alternative. +[SE-0180][] introduced a computed variable and initializer surrounding the concept of an `encodedOffset` for serialization purposes. Unfortunately, that approach is flawed for its intended purpose and is commonly misused in ways that Swift 5 is [more likely to expose](https://bugs.swift.org/browse/SR-9749). It is too late in the Swift 5.0 release to solve all existing problems, so we propose deprecating `encodedOffset` and introducing a targeted, semantics-preserving alternative. ## Motivation -String abstracts away details about the underlying encoding used in its storage. String.Index is opaque and represents a position within a String or Substring. This can make serializing a string alongside its indices difficult, and for that reason [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md) added a computed variable and initializer `encodedOffset` in Swift 4.0. +String abstracts away details about the underlying encoding used in its storage. String.Index is opaque and represents a position within a String or Substring. This can make serializing a string alongside its indices difficult, and for that reason [SE-0180][] added a computed variable and initializer `encodedOffset` in Swift 4.0. String was always meant to be capable of handling multiple backing encodings for its contents, and this is realized in Swift 5. [String now uses UTF-8](https://forums.swift.org/t/string-s-abi-and-utf-8/17676) for its preferred “fast” native encoding, but has a resilient fallback for strings of different encodings. Currently, we only use this fall-back for lazily-bridged Cocoa strings, which are commonly encoded as UTF-16, though it can be extended in the future thanks to resilience. -Unfortunately, [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s approach of a single notion of `encodedOffset` is flawed. A string can be serialized with a choice of encodings, and the offset is therefore encoding-dependent and requires access to the contents of the string to calculate. A comment in [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s example source mentioned that `encodedOffset` assumes UTF-16, which happened to be the only encoding used internally by String at the time (for offset purposes). +Unfortunately, [SE-0180][]’s approach of a single notion of `encodedOffset` is flawed. A string can be serialized with a choice of encodings, and the offset is therefore encoding-dependent and requires access to the contents of the string to calculate. A comment in [SE-0180][]’s example source mentioned that `encodedOffset` assumes UTF-16, which happened to be the only encoding used internally by String at the time (for offset purposes). -Furthermore, the majority of uses of `encodedOffset` in the wild are not following [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s intended purpose and are sensitive to encoding changes. `encodedOffset` is frequently misused under the assumption that all Characters are comprised of a single code unit, which is error-prone and Swift 5 might surface the underlying bugs in more situations. It is also sometimes used for mapping Cocoa string indices, which happens to work in Swift 4 but might not in Swift 5, and Foundation already provides better alternatives. +Furthermore, the majority of uses of `encodedOffset` in the wild are not following [SE-0180][]’s intended purpose and are sensitive to encoding changes. `encodedOffset` is frequently misused under the assumption that all Characters are comprised of a single code unit, which is error-prone and Swift 5 might surface the underlying bugs in more situations. It is also sometimes used for mapping Cocoa string indices, which happens to work in Swift 4 but might not in Swift 5, and Foundation already provides better alternatives. @@ -72,13 +72,13 @@ Added APIs are all resilient and can be replaced with more efficient implementat ### Do Nothing -If `encodedOffset` was only used for serialization, *and* such serialization/deserialization would record and preserve the original encoding, *and* we amend [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s comment to avoid nailing it down to any given encoding, no change would be necessary. Unfortunately, there is no way to query or preserve internal encoding and there is considerable use and misuse in the wild, as mentioned in the “Uses in the Wild” disclosure section. +If `encodedOffset` was only used for serialization, *and* such serialization/deserialization would record and preserve the original encoding, *and* we amend [SE-0180][]’s comment to avoid nailing it down to any given encoding, no change would be necessary. Unfortunately, there is no way to query or preserve internal encoding and there is considerable use and misuse in the wild, as mentioned in the “Uses in the Wild” disclosure section. ### Fix all the Bugs This proposal originally introduced a set of API attempting to solve 3 problems: -1. SE-0180’s `encodedOffset`, meant for serialization purposes, needs to be parameterized over the encoding in which the string will be serialized in +1. [SE-0180][]’s `encodedOffset`, meant for serialization purposes, needs to be parameterized over the encoding in which the string will be serialized in 2. Existing uses of `encodedOffset` need a semantics-preserving off-ramp for Swift 5, which is expressed in terms of UTF-16 offsets 3. Existing misuses of `encodedOffset`, which assume all characters are a single UTF-16 code unit, need a semantics-fixing alternative @@ -99,7 +99,7 @@ Array(myString); Array(myString.indices) // Not an encoding, but provides offset #### Uses in the Wild
    -GitHub code search yields [nearly 1500 uses](https://github.com/search?l=Swift&q=encodedOffset&type=Code) , and nearly-none of them are for [SE-0180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md)’s intended purpose. Below I present the 3 most common uses. +GitHub code search yields [nearly 1500 uses](https://github.com/search?l=Swift&q=encodedOffset&type=Code) , and nearly-none of them are for [SE-0180][]’s intended purpose. Below I present the 3 most common uses. ```swift // Common code for these examples @@ -225,3 +225,4 @@ However, it’s not clear this is the best approach for Swift and more design wo It is too late in the Swift 5.0 release to design and add all of these API. Instead, we’re proposing an urgent, targeted fix for the second problem. +[SE-0180]: From e7b278f13f447f9d00f68ec0f00bc1a60d922c95 Mon Sep 17 00:00:00 2001 From: Pavol Vaskovic Date: Wed, 27 Feb 2019 01:50:30 +0100 Subject: [PATCH 1006/4563] Remove outdated discussion of get vs unwrapped (#989) The modifications made to the proposal upon acceptance by the Core Team make this paragraph confusing and out-of context. --- proposals/0235-add-result.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/proposals/0235-add-result.md b/proposals/0235-add-result.md index bfb4547fed..dbdee3a2b2 100644 --- a/proposals/0235-add-result.md +++ b/proposals/0235-add-result.md @@ -363,5 +363,3 @@ A previous version of this proposal included operations for optionally projectin A previous version of this proposal included a `fold` operation. This operation is essentially an expression-based `switch`, and like the optional case projections, it would be better to provide a general language solution for it than to add a more limited design that covers only a single type. A previous version of this proposal did not label the closure parameter for the catching initializer. Single-argument unlabeled initializers are conventionally used for conversions, which this is not; usually the closure will be written explicitly, but in case it isn't, a parameter label is appropriate. - -There are several different names that would be reasonable for the `unwrapped` operation, such as `get`. None of these names seem obviously better than `unwrapped`. From 30b0426d41dbaeab5bfa8b57c408c501c61d3fc8 Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Wed, 27 Feb 2019 21:06:43 +0330 Subject: [PATCH 1007/4563] removed debounce --- index.js | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/index.js b/index.js index 31b622051f..d995900c23 100644 --- a/index.js +++ b/index.js @@ -481,10 +481,9 @@ function _joinNodes (nodeList, text) { function addEventListeners () { var nav = document.querySelector('nav') - // typing in the search field causes the filter to be reapplied. - var filterProposalsDebounce = debounce(filterProposals, 250); - nav.addEventListener('keyup', filterProposalsDebounce) - nav.addEventListener('change', filterProposalsDebounce) + // typing in the search field causes the filter to be reapplied. + nav.addEventListener('keyup', filterProposals) + nav.addEventListener('change', filterProposals) // clearing the search field also hides the X symbol nav.querySelector('#clear-button').addEventListener('click', function () { @@ -991,15 +990,4 @@ function updateFilterDescription (selectedStateNames) { function updateProposalsCount (count) { var numberField = document.querySelector('#proposals-count-number') numberField.innerText = (count.toString() + ' proposal' + (count !== 1 ? 's' : '')) -} - -function debounce(func, delay) { - let timeoutId = null; - return function() { - clearTimeout(timeoutId); - var args = arguments; - timeoutId = setTimeout(function() { - func.apply(undefined, args); - }, delay); - }; -}; \ No newline at end of file +} \ No newline at end of file From 9a322a08f4da0cdf8a593f5644bd2cd4b6d0d119 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Sun, 3 Mar 2019 11:47:06 -0800 Subject: [PATCH 1008/4563] Update SE-0220 proposal status Due to type checker performance impact, this feature was reverted so resetting the status to Accepted --- proposals/0220-count-where.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0220-count-where.md b/proposals/0220-count-where.md index 20833cd058..7c5358603e 100644 --- a/proposals/0220-count-where.md +++ b/proposals/0220-count-where.md @@ -3,7 +3,7 @@ * Proposal: [SE-0220](0220-count-where.md) * Author: [Soroush Khanlou](https://github.com/khanlou) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Implemented (Swift 5)** +* Status: **Accepted** * Implementation: [apple/swift#16099](https://github.com/apple/swift/pull/16099) ## Introduction From 4f9d14c6d94348fae51d29969e715aed60cc7e61 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Mon, 4 Mar 2019 09:41:09 -0600 Subject: [PATCH 1009/4563] Create 0233-codepoint-and-character-literals.md (#939) * Create 0233-codepoint-and-character-literals.md * Kick off review --- .../0233-codepoint-and-character-literals.md | 226 ++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 proposals/0233-codepoint-and-character-literals.md diff --git a/proposals/0233-codepoint-and-character-literals.md b/proposals/0233-codepoint-and-character-literals.md new file mode 100644 index 0000000000..e2893aa6af --- /dev/null +++ b/proposals/0233-codepoint-and-character-literals.md @@ -0,0 +1,226 @@ +# Integer-convertible character literals + +* Proposal: SE-0240 +* Authors: [Kelvin Ma](https://github.com/kelvin13) ([@*taylorswift*](https://forums.swift.org/u/taylorswift/summary)), [Chris Lattner](https://github.com/lattner) ([@*Chris_Lattner3*](https://forums.swift.org/u/Chris_Lattner3/summary)), [John Holdsworth](https://github.com/johnno1962) ([@*johnno1962*](https://forums.swift.org/u/johnno1962/summary)) +* Review manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: *Active review (March 4 - March 12, 2019)* +* Implementation: [apple/swift#21873](https://github.com/apple/swift/pull/21873) +* Threads: [1](https://forums.swift.org/t/prepitch-character-integer-literals/10442) + +## Introduction + +Swift’s `String` type is designed for Unicode correctness and abstracts away the underlying binary representation of the string to model it as a `Collection` of grapheme clusters. This is an appropriate string model for human-readable text, as to a human reader, the atomic unit of a string is (usually) the extended grapheme cluster. When treated this way, many logical string operations “just work” the way users expect. + +However, it is also common in programming to need to express values which are intrinsically numeric, but have textual meaning, when taken as an ASCII value. We propose adding a new literal syntax takes single-quotes (`'`), and is transparently convertible to Swift’s integer types. This syntax, but not the behavior, will extend to all “single element” text literals, up to and including `Character`, and will become the preferred literal syntax these types. + +## Motivation + +A pain point of using characters in Swift is they lack a first-class literal syntax. Users have to manually coerce string literals to a `Character` or `Unicode.Scalar` type using `as Character` or `as Unicode.Scalar`, respectively. Having the collection share the same syntax as its element also harms code clarity and makes it difficult to tell if a double-quoted literal is being used as a string or character in some cases. + +Additional challenges arise when using ASCII scalars in Swift. Swift currently provides no static mechanism to assert that a unicode scalar literal is restricted to the ASCII range, and lacks a readable literal syntax for such values as well. In C, `'a'` is a `uint8_t` literal, equivalent to `97`. Swift has no such equivalent, requiring awkward spellings like `UInt8(ascii: "a")`, or spelling out the values in hex or decimal directly. This harms readability of code, and makes bytestring processing in Swift painful. + +```c +static char const hexcodes[16] = { + '0', '1', '2', '3', '4' ,'5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f' +}; +``` + +```swift +let hexcodes = [ + UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), + UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), + UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "a"), UInt8(ascii: "b"), + UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f") +] +``` + +Higher-level constructs can regain some readability, + +```swift +let hexcodes = [ + "0", "1", "2", "3", + "4", "5", "6", "7", + "8", "9", "a", "b", + "c", "d", "e", "f" +].map{ UInt8(ascii: $0) } +``` + +but may not be familiar to all users, and can come at a runtime cost. + +In addition, the `init(ascii:)` initializer only exists on `UInt8`. If you're working with other types like `Int8` (common when dealing with C APIs that take `char`), it is much more awkward. Consider scanning through a `char*` buffer as an `UnsafeBufferPointer`: + +```swift +for scalar in int8buffer { + switch scalar { + case Int8(UInt8(ascii: "a")) ... Int8(UInt8(ascii: "f")): + // lowercase hex letter + case Int8(UInt8(ascii: "A")) ... Int8(UInt8(ascii: "F")): + // uppercase hex letter + case Int8(UInt8(ascii: "0")) ... Int8(UInt8(ascii: "9")): + // hex digit + default: + // something else + } +} +``` + +Transforming `Unicode.Scalar` literals also sacrifices compile-time guarantees. The statement `let char: UInt8 = 1989` is a compile time error, whereas `let char: UInt8 = .init(ascii: "߅")` is a run time error. + +ASCII scalars are inherently textual, so it should be possible to express them with a textual literal directly. Just as applying the `String` APIs runs counter to Swift’s stated design goals of safety and efficiency, requiring users to express basic data values in such a verbose way runs counter to our design goal of [expressiveness](https://swift.org/about/#swiftorg-and-open-source). + +Integer character literals would provide benefits to `String` users. One of the [future directions](https://gist.github.com/milseman/bb39ef7f170641ae52c13600a512782f#unmanaged-strings) for `String` is to provide performance-sensitive or low-level users with direct access to code units. Having numeric character literals for use with this API is highly motivating. Furthermore, improving Swift’s bytestring ergonomics is an [important part](https://forums.swift.org/t/prepitch-character-integer-literals/10442/140?u=taylorswift) of our long term goal of expanding into embedded platforms. + +## Proposed solution + +The most straightforward solution is to conform Swift’s integer types to `ExpressibleByUnicodeScalarLiteral`. Due to ABI constraints, it is not currently possible to add this conformance, so we will add the conformance *implementations* to the standard library, and allow users to “enable” to the feature by declaring this conformance in user code, for example: + +```swift +extension Int8: ExpressibleByUnicodeScalarLiteral { } +``` +Once the Swift ABI supports retroactive conformances, this conformance can be declared in the standard library, making it available by default. + +These integer conversions will only be valid for the ASCII range `U+0 ..< U+128`; unicode scalar literals outside of that range will be invalid and will generate compile-time errors similar to the way we currently diagnose overflowing integer literals. This is a conservative approach, as allowing transparent unicode conversion to integer types carries encoding pitfalls users may not anticipate or easily understand. + +Because it is currently possible to call literal initializers at run-time, a run-time precondition failure will occur if a non-ASCII value is passed to the integer initializer. (We expect the compiler to elide this precondition check for “normal” invocations.) + +```swift +let u: Unicode.Scalar = '\u{FF}' +let i1: Int = '\u{FF}' // compile-time error +let i2: Int = .init(unicodeScalarLiteral: u) // run-time error +``` + +| `ExpressibleBy`… | `UnicodeScalarLiteral` | `ExtendedGraphemeClusterLiteral` | `StringLiteral` | +| --- | --- | --- | --- | +| `UInt8:`, … , `Int:` | yes* (initially opt-in) | no | no | +| `Unicode.Scalar:` | yes | no | no | +| `Character:` | yes (inherited) | yes | no | +| `String:` | yes | yes | yes | +| `StaticString:` | yes | yes | yes | + +> Cells marked with an asterisk `*` indicate behavior that is different from the current language behavior. + +The ASCII range restriction will only apply to single-quote literals coerced to a `Unicode.Scalar` and (either statically or dynamically) converted to an integer type. Any valid `Unicode.Scalar` can be written as a single-quoted unicode scalar literal, and any valid `Character` can be written as a single-quoted character literal. + +| | `'a'` | `'é'` | `'β'` | `'𓀎'` | `'👩‍✈️'` | `"ab"` | +| --- | --- | --- | --- | --- | --- | --- | +| `:String` | `"a"` | `"é"` | `"β"` | `"𓀎"` | `"👩‍✈️"` | "ab" +| `:Character` | `'a'` | `'é'` | `'β'` | `'𓀎'` | `'👩‍✈️'` +| `:Unicode.Scalar` | U+0061 | U+00E9 | U+03B2 | U+1300E +| `:UInt32` | 97 | +| `:UInt16` | 97 | +| `:UInt8` | 97 | +| `:Int8` | 97 | + +With these changes, the hex code example can be written much more naturally: + +```swift +let hexcodes: [UInt8] = [ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f' +] + +for scalar in int8buffer { + switch scalar { + case 'a' ... 'f': + // lowercase hex letter + case 'A' ... 'F': + // uppercase hex letter + case '0' ... '9': + // hex digit + default: + // something else + } +} +``` + +### Choice of single quotes + +We propose to adopt the `'x'` syntax for all textual literal types up to and including `ExtendedGraphemeClusterLiteral`, but not including `StringLiteral`. These literals will be used to express integer types, `Character`, `Unicode.Scalar`, and types like `UTF16.CodeUnit` in the standard library. + +The default inferred literal type for `let x = 'a'` will be `Character`, following the principle of least surprise. This also allows for a natural user-side syntax for differentiating methods overloaded on both `Character` and `String`. + +Use of single quotes for character/scalar literals is precedented in other languages, including C, Objective-C, C++, Java, and Rust, although different languages have slightly differing ideas about what a “character” is. We choose to use the single quote syntax specifically because it reinforces the notion that strings and character values are different: the former is a sequence, the later is an element. Character types also don't support string literal interpolation, which is another reason to move away from double quotes. + +### Single quotes in Swift, a historical perspective + +In Swift 1.0, single quotes were reserved for some yet-to-be determined syntactical purpose. Since then, pretty much all of the things that might have used single quotes have already found homes in other parts of the Swift syntactical space: + +- syntax for [multi-line string literals](https://github.com/apple/swift-evolution/blob/master/proposals/0168-multi-line-string-literals.md) uses triple quotes (`"""`) + +- string interpolation syntax uses standard double quote syntax. + +- raw-mode string literals settled into the `#""#` syntax. + +- In current discussions around [regex literals](https://forums.swift.org/t/string-update/7398/6), most people seem to prefer slashes (`/`). + +Given that, and the desire for lightweight syntax for single chararcter syntax, and the precedent in other languages for characters, it is natural to use single quotes for this purpose. + +### Existing double quote initializers for characters + +We propose deprecating the double quote literal form for `Character` and `Unicode.Scalar` types and slowly migrating them out of Swift. + +```swift +let c2 = 'f' // preferred +let c1: Character = "f" // deprecated +``` + +## Detailed Design + +The only standard library changes will be to extend `FixedWidthInteger` to have the `init(unicodeScalarLiteral:)` initializer required by `ExpressibleByUnicodeScalarLiteral`. Static ASCII range checking will be done in the type checker, dynamic ASCII range checking will be done by a runtime precondition. + +```swift +extension FixedWidthInteger { + init(unicodeScalarLiteral: Unicode.Scalar) +} +``` + +The default inferred type for all single-quoted literals will be `Character`. This addresses a language pain point where declaring a `Character` requires type context. + +## Source compatibility + +This proposal could be done in a way that is strictly additive, but we feel it is best to deprecate the existing double quote initializers for characters, and the `UInt8.init(ascii:)` initializer. + +Here is a specific sketch of a deprecation policy: + + * Continue accepting these in Swift 5 mode with no change. + + * Introduce the new syntax support into Swift 5.1. + + * Swift 5.1 mode would start producing deprecation warnings (with a fixit to change double quotes to single quotes.) + + * The Swift 5 to 5.1 migrator would change the syntax (by virtue of applying the deprecation fixits.) + + * Swift 6 would not accept the old syntax. + +During the transition period, `"a"` will remain a valid unicode scalar literal, but attempting to initialize integer types with double-quoted ASCII literals will produce an error. + +``` +let ascii:Int8 = "a" // error +``` + +However, as this will only be possible in new code, and will produce a deprecation warning from the outset, this should not be a problem. + +## Effect on ABI stability + +All changes except deprecating the `UInt8.init(ascii:)` initializer are either additive, or limited to the type checker, parser, or lexer. + +Removing `UInt8.init(ascii:)` would break ABI, but this is not necessary to implement the proposal, it’s merely housekeeping. + +## Effect on API resilience + +None. + +## Alternatives considered + +### Integer initializers + +Some have proposed extending the `UInt8(ascii:)` initializer to other integer types (`Int8`, `UInt16`, … , `Int`). However, this forgoes compile-time validity checking, and entails a substantial increase in API surface area for questionable gain. + +### Lifting the ASCII range restriction + +Some have proposed allowing any unicode scalar literal whose codepoint index does not overflow the target integer type to be convertible to that integer type. Consensus was that this is an easy source of unicode encoding bugs, and provides little utility to the user. If people change their minds in the future, this restriction can always be lifted in a source and ABI compatible way. + +### Single-quoted ASCII strings + +Some have proposed allowing integer *array* types to be expressible by *multi-character* ASCII strings such as `'abcd'`. We consider this to be out of scope of this proposal, as well as unsupported by precedent in C and related languages. From 129c2d7867b4ddf075f83d07fe3b5631d167c7b4 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 4 Mar 2019 07:43:16 -0800 Subject: [PATCH 1010/4563] Update and rename 0233-codepoint-and-character-literals.md to 0243-codepoint-and-character-literals.md --- ...ter-literals.md => 0243-codepoint-and-character-literals.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{0233-codepoint-and-character-literals.md => 0243-codepoint-and-character-literals.md} (99%) diff --git a/proposals/0233-codepoint-and-character-literals.md b/proposals/0243-codepoint-and-character-literals.md similarity index 99% rename from proposals/0233-codepoint-and-character-literals.md rename to proposals/0243-codepoint-and-character-literals.md index e2893aa6af..21be7dabc3 100644 --- a/proposals/0233-codepoint-and-character-literals.md +++ b/proposals/0243-codepoint-and-character-literals.md @@ -1,6 +1,6 @@ # Integer-convertible character literals -* Proposal: SE-0240 +* Proposal: [SE-0243]() * Authors: [Kelvin Ma](https://github.com/kelvin13) ([@*taylorswift*](https://forums.swift.org/u/taylorswift/summary)), [Chris Lattner](https://github.com/lattner) ([@*Chris_Lattner3*](https://forums.swift.org/u/Chris_Lattner3/summary)), [John Holdsworth](https://github.com/johnno1962) ([@*johnno1962*](https://forums.swift.org/u/johnno1962/summary)) * Review manager: [Ben Cohen](https://github.com/airspeedswift) * Status: *Active review (March 4 - March 12, 2019)* From 0a5dbf70cd26398674d4b5fb9dee6f941a47c4b3 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 4 Mar 2019 07:43:32 -0800 Subject: [PATCH 1011/4563] Update 0243-codepoint-and-character-literals.md --- proposals/0243-codepoint-and-character-literals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0243-codepoint-and-character-literals.md b/proposals/0243-codepoint-and-character-literals.md index 21be7dabc3..9c7fac8d49 100644 --- a/proposals/0243-codepoint-and-character-literals.md +++ b/proposals/0243-codepoint-and-character-literals.md @@ -1,6 +1,6 @@ # Integer-convertible character literals -* Proposal: [SE-0243]() +* Proposal: [SE-0243](https://github.com/apple/swift-evolution/blob/master/proposals/0243-codepoint-and-character-literals.md) * Authors: [Kelvin Ma](https://github.com/kelvin13) ([@*taylorswift*](https://forums.swift.org/u/taylorswift/summary)), [Chris Lattner](https://github.com/lattner) ([@*Chris_Lattner3*](https://forums.swift.org/u/Chris_Lattner3/summary)), [John Holdsworth](https://github.com/johnno1962) ([@*johnno1962*](https://forums.swift.org/u/johnno1962/summary)) * Review manager: [Ben Cohen](https://github.com/airspeedswift) * Status: *Active review (March 4 - March 12, 2019)* From 29d81dc00da6569f236d062828f4d760ac432e6e Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 4 Mar 2019 16:52:56 +0000 Subject: [PATCH 1012/4563] [SE-0243] Fix "status not found" warning --- proposals/0243-codepoint-and-character-literals.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0243-codepoint-and-character-literals.md b/proposals/0243-codepoint-and-character-literals.md index 9c7fac8d49..57dec5a6f6 100644 --- a/proposals/0243-codepoint-and-character-literals.md +++ b/proposals/0243-codepoint-and-character-literals.md @@ -1,9 +1,9 @@ # Integer-convertible character literals -* Proposal: [SE-0243](https://github.com/apple/swift-evolution/blob/master/proposals/0243-codepoint-and-character-literals.md) +* Proposal: [SE-0243](0243-codepoint-and-character-literals.md) * Authors: [Kelvin Ma](https://github.com/kelvin13) ([@*taylorswift*](https://forums.swift.org/u/taylorswift/summary)), [Chris Lattner](https://github.com/lattner) ([@*Chris_Lattner3*](https://forums.swift.org/u/Chris_Lattner3/summary)), [John Holdsworth](https://github.com/johnno1962) ([@*johnno1962*](https://forums.swift.org/u/johnno1962/summary)) * Review manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: *Active review (March 4 - March 12, 2019)* +* Status: **Active review (March 4 - March 12, 2019)** * Implementation: [apple/swift#21873](https://github.com/apple/swift/pull/21873) * Threads: [1](https://forums.swift.org/t/prepitch-character-integer-literals/10442) From 31a2e709abefa10cbf0ebb9294b51ef529742d2d Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 4 Mar 2019 17:15:42 +0000 Subject: [PATCH 1013/4563] [SE-0243] Fix parsing of authors list Using same format as SE-0184 --- proposals/0243-codepoint-and-character-literals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0243-codepoint-and-character-literals.md b/proposals/0243-codepoint-and-character-literals.md index 57dec5a6f6..f39f8a7e9c 100644 --- a/proposals/0243-codepoint-and-character-literals.md +++ b/proposals/0243-codepoint-and-character-literals.md @@ -1,7 +1,7 @@ # Integer-convertible character literals * Proposal: [SE-0243](0243-codepoint-and-character-literals.md) -* Authors: [Kelvin Ma](https://github.com/kelvin13) ([@*taylorswift*](https://forums.swift.org/u/taylorswift/summary)), [Chris Lattner](https://github.com/lattner) ([@*Chris_Lattner3*](https://forums.swift.org/u/Chris_Lattner3/summary)), [John Holdsworth](https://github.com/johnno1962) ([@*johnno1962*](https://forums.swift.org/u/johnno1962/summary)) +* Authors: [Kelvin Ma (“Taylor Swift”)](https://github.com/kelvin13), [Chris Lattner](https://github.com/lattner), [John Holdsworth](https://github.com/johnno1962) * Review manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Active review (March 4 - March 12, 2019)** * Implementation: [apple/swift#21873](https://github.com/apple/swift/pull/21873) From 25eb37facd5022caa4e2abd971cb9eb5424258d4 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 5 Mar 2019 13:36:16 -0800 Subject: [PATCH 1014/4563] Opaque result types (#995) * Opaque result types * Update proposals/NNNN-opaque-result-types.md Typo, thanks @benrimmington Co-Authored-By: jckarter * Update proposals/NNNN-opaque-result-types.md Typo, thanks @benrimmington Co-Authored-By: jckarter * More updates Thanks @marcusrossel * More compelling example without where clauses? * share blame * Start review --- proposals/0244-opaque-result-types.md | 848 ++++++++++++++++++++++++++ 1 file changed, 848 insertions(+) create mode 100644 proposals/0244-opaque-result-types.md diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md new file mode 100644 index 0000000000..b15f548e0a --- /dev/null +++ b/proposals/0244-opaque-result-types.md @@ -0,0 +1,848 @@ +# Opaque Result Types + +* Proposal: [SE-0244](0244-opaque-result-types.md) +* Author: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **In review (March 5 – March 15)** +* Pull Request: [apple/swift#21137](https://github.com/apple/swift/pull/21137) +* Toolchain: https://github.com/apple/swift/pull/21137#issuecomment-468118328 + +## Introduction + +This proposal introduces the ability to hide the specific result type of a function from its callers. The result type is described only by its capabilities, for example, to say that a function returns a `Collection` without specifying which specific collection. Clients can use the resulting values freely, but the underlying concrete type is known only to the implementation of the function and need not be spelled out explicitly. + +Consider an operation like the following: + +```swift +let resultValues = values.lazy.map(transform).filter { $0 != nil }.map { $0! } +``` + +If we want to encapsulate that code in another method, we're going to have to write out the type of that lazy-map-filter chain, which is rather ugly: + +```swift +extension LazyMapCollection { + public func compactMap( + _ transform: @escaping (Elements.Element) -> ElementOfResult? + ) + -> LazyMapSequence< + LazyFilterSequence< + LazyMapSequence + >, + ElementOfResult + > { + return self.map(transform).filter { $0 != nil }.map { $0! } + } +} +``` + +The author of `compactMap(_:)` doesn't want to have to figure out that type, +and the clients of `compactMap(_:)` don't want to have to reason about it. `compactMap(_:)` is the subject of [SE-0222](https://github.com/apple/swift-evolution/blob/master/proposals/0222-lazy-compactmap-sequence.md), which introduces a new standard library type `LazyCompactMapCollection` for the sole purpose of describing the result type of `compactMap(_:)`: + +```swift +extension LazyMapCollection { + public func compactMap( + _ transform: @escaping (Element) -> ElementOfResult? + ) + -> LazyCompactMapCollection { + return LazyCompactMapCollection(/*...*/) + } +} +``` + +This is less verbose, but requires the introduction of a new, public type solely to describe the result of this function, increasing the surface area of the standard library and requiring a nontrivial amount of implementation. + +Other libraries may want to provide libraries of composable generic components in a similar manner. +A graphics library could provide primitives for shapes: + +```swift +protocol Shape { + func draw(to: Surface) + + func collides(with: Other) -> Bool +} + +struct Rectangle: Shape { ... } +struct Circle: Shape { ... } +``` + +along with composable transformations to combine and modify primitive shapes: + +```swift +struct Union: Shape { + var a: A, b: B + ... +} + +struct Intersect: Shape { + var a: A, b: B + ... +} + +struct Transformed: Shape { + var shape: S + var transform: Matrix3x3 + ... +} +``` + +One could compose these transformations by using the existential type `Shape` instead of generic arguments, but doing so would imply more dynamism and runtime overhead than may be desired. By composing generic containers, generic specialization can optimize together the composed operations like it does for `lazy` collections. A game or graphics app may want to define objects in terms of their shapes: + +``` +protocol GameObject { + // The shape of the object + associatedtype Shape: Shapes.Shape + + var shape: Shape { get } +} +``` + +However, this means that implementers of the `GameObject` protocol would now be burdened with writing out long, explicit types for their shapes: + +``` +struct EightPointedStar: GameObject { + var shape: Union> { + return Union(Rectangle(), Transformed(Rectangle(), by: .fortyFiveDegrees) + } +} +``` + +Opaque result types allow the method to state the capabilities of its result type without tying it down to a concrete type. For example, the above could instead be written: + +```swift +struct EightPointedStar: GameObject { + var shape: some Shape { + return Union(Rectangle(), Transformed(Rectangle(), by: .fortyFiveDegrees) + } +} +``` + +to declare that an `EightPointedStar` has some `Shape` without having to specify exactly what shape thatis. The underlying concrete type is hidden, and can even change from one version of the library to the next without breaking those clients, because the actual type identity was never exposed. This allows the library to provide a potentially-more-efficient design without expanding the surface area of the library or burdening implementors of the library's protocols with impractical type compositions. + +Swift-evolution thread: [Opaque result types](https://forums.swift.org/t/opaque-result-types/15645) + +## Proposed solution + +This proposal introduces syntax that can be used to describe an opaque result type for a function. Instead of specifying a specific return type, a function can declare that it returns `some` type that satisfies a set of constraints, such as protocol requirements. This `some` specifier can only be used in the result type of a function, the type of a property, or the element type of a subscript declaration. The return type is backed by some specific concrete type, but that type is only known to the implementation of that function/property/subscript. Everywhere else, the type is opaque, and is described only by its characteristics and originating function/property/subscript. For example, a function can declare that it produces something that's a `MutableCollection` and `RangeReplaceableCollection`: + +```swift +func makeMeACollection(with element: T) + -> some MutableCollection & RangeReplaceableCollection +{ + return [element] // ok: an array of T satisfies all of the requirements +} +``` + +Following the `some` keyword is a class, protocol, `Any`, `AnyObject`, or composition thereof (joined with `&`). A caller to `makeMeACollection(_:)` can rely on the result type satisfying all of the requirements listed. For example: + +```swift +var c = makeMeACollection(with: 17) +c.append(c.first!) // ok: it's a RangeReplaceableCollection +c[c.startIndex] = c.first! // ok: it's a MutableCollection +print(c.reversed()) // ok: all Collection/Sequence operations are available + +func foo(_ : C) { } +foo(c) // ok: C inferred to opaque result type of makeMeACollection() +``` + +Moreover, opaque result types to be used freely with other generics, e.g., forming a collection of the results: + +```swift +var cc = [c] +cc.append(c) // ok: cc's Element == the result type of makeMeACollection +var c2 = makeMeACollection(with: 38) +cc.append(c2) // ok: Element == the result type of makeMeACollection +``` + +### Type identity +An opaque result type is not considered equivalent to its underlying type by the static type system: + +```swift +var intArray = [Int]() +cc.append(intArray) // error: [Int] is not known to equal the result type of makeMeACollection +``` + +However, as with generic type parameters, one can inspect an opaque type's underlying type at runtime. For example, a conditional cast could determine whether the result of `makeMeACollection` is of a particular type: + +```swift +if let arr = makeMeACollection(Int.self) as? [Int] { + print("It's an [Int], \(arr)\n") +} else { + print("Guessed wrong") +} +``` + +In other words, opaque result types are only opaque to the static type system. They don't exist at runtime. + +### Implementing a function returning an opaque type +The implementation of a function returning an opaque type must return a value of the same concrete type `T` from each `return` statement, and `T` must meet all of the constraints stated on the opaque type. For example: + +```swift +protocol P { } +extension Int : P { } +extension String : P { } + +func f1() -> some P { + return "opaque" +} + +func f2(i: Int) -> some P { // ok: both returns produce Int + if i > 10 { return i } + return 0 +} + +func f2(flip: Bool) -> some P { + if flip { return 17 } + return "a string" // error: different return types Int and String +} + +func f3() -> some P { + return 3.1419 // error: Double does not conform to P +} + +func f4() -> some P { + let p: P = "hello" + return p // error: protocol type P does not conform to P +} + +func f5() -> some P { + return f1() // ok: f1() returns an opaque type that conforms to P +} + +protocol Initializable { init() } + +func f6(_: T.Type) -> some P { + return T() // ok: T will always be a concrete type conforming to P +} +``` + +These rules guarantee that there is a single concrete type produced by any call to the function. The concrete type can depend on the generic type arguments (as in the `f6()` example), but must be consistent across all `return` statements. + +Note that recursive calls *are* allowed, and are known to produce a value of the same concrete type, but that the concrete type itself is not known: + +```swift +func f7(_ i: Int) -> some P { + if i == 0 { + return f7(1) // ok: returning our own opaque result type + } else if i < 0 { + let result: Int = f7(-i) // error: opaque result type of f7() is not convertible to Int + return result + } else { + return 0 // ok: grounds the recursion with a concrete type + } +} +``` + +Of course, there must be at least one `return` statement that provides a concrete type. + +Note that a function cannot call itself recursively in a way that forms a type parameterized on the function's own opaque result type, since this would mean that the opaque type is infinitely recursive: + +```swift +struct Wrapper: P { var value: T } + +func f8(_ i: Int) -> some P { + // invalid; this binds the opaque result type to Wrapper, + // which is Wrapper>, which is + // Wrapper>>... + return Wrapper(f8(i + 1)) +} +``` + +A function with an opaque result type is also required to have a `return` +statement even if it does not terminate: + +```swift +func f9() -> some P { + fatalError("not implemented") + + // error: no return statement to get opaque type +} +``` + +This requirement is necessary because, even though `f9`'s return value cannot be reached, the return type of `f9` can still be propagated by local type inference +or generic instantiation in ways that don't require evaluating `f9`, so a type +for `f9` must be available: + +```swift +let delayedF9 = { f9() } // closure has type () -> return type of f9 +``` + +We can't necessarily default the underlying type to `Never`, since `Never` may not +conform to the constraints of the opaque type. If `Never` does conform, and it +is desired as the underlying return type, that can be written explicitly as a +`return` statement: + +``` +extension Never: P {} +func f9b() -> some P { + return fatalError("not implemented") // OK, explicitly binds return type to Never +} +``` + +### Properties and subscripts + +Opaque result types can also be used with properties and subscripts: + +```swift +struct GameObject { + var shape: some Shape { ... } +} +``` + +For computed properties, the concrete type is determined by the `return` statements in the getter. Opaque result types can also be used in stored properties that have an initializer, in which case the concrete type is the type of the initializer: + +```swift +let strings: some Collection = ["hello", "world"] +``` + +Properties and subscripts of opaque result type can be mutable. For example: + +```swift +// Module A +public protocol P { + mutating func flip() +} + +private struct Witness: P { + mutating func flip() { ... } +} + +public var someP: some P = Witness() + +// Module B +import A +someP.flip() // ok: flip is a mutating function called on a variable +``` + +With a subscript or a computed property, the type of the value provided to the setter (e.g., `newValue`) is determined by the `return` statements in the getter, so the type is consistent and known only to the implementation of the property or subscript. For example: + +```swift +protocol P { } +private struct Impl: P { } + +public struct Vendor { + private var storage: [Impl] = [...] + + public var count: Int { + return storage.count + } + + public subscript(index: Int) -> some P { + get { + return storage[index] + } + set (newValue) { + storage[index] = newValue + } + } +} + +var vendor = Vendor() +vendor[0] = vendor[2] // ok: can move elements around +``` + +### Associated type inference +While one can use type inference to declare variables of the opaque result type of a function, there is no direct way to name the opaque result type: + +```swift +func f1() -> some P { /* ... */ } + +let vf1 = f1() // type of vf1 is the opaque result type of f1() +``` + +However, the type inference used to satisfy associated type requirements can deduce an opaque result type as the associated type of the protocol: + +```swift +protocol GameObject { + associatedtype ObjectShape: Shape + + var shape: ObjectShape +} + +struct Player: GameObject { + var shape: some Shape { ... } + + /* infers typealias Shape = opaque result type of Player.shape */ +} + +let sv: S.SomeType // ok: names the opaque result type of S.someValue() +sv = S().someValue() // ok: returns the same opaque result type +``` + +Note that having a name for the opaque result type still doesn't give information about the underlying concrete type. For example, the only way to create an instance of the type `S.SomeType` is by calling `S.someValue()`. + +### Opaque result types vs. existentials + +On the surface, opaque result types are quite similar to (generalized) existential types: in each case, the specific concrete type is unknown to the static type system, and can be manipulated only through the stated capabilities (e.g., protocol and superclass constraints). There are some similarities between the two features for code where the identity of the return type does not matter. For example: + +```swift +protocol P + func foo() +} + +func anyP() -> P { /* ... */ } +func someP() -> some P { /* ... */ } + +anyP().foo() // ok +someP().foo() // ok +``` + +However, the fundamental difference between opaque result types and existentials revolves around type identity. All instances of an opaque result type are guaranteed to have the same type at run time, whereas different instances of an existential type may have different types at run time. It is this aspect of existential types that makes their use so limited in Swift. For example, consider a function that takes two values of (existential) type `Equatable` and tries to compare them: + +```swift +protocol Equatable { + static func ==(lhs: Self, rhs: Self) -> Bool +} + +func isEqual(_ x: Equatable, y: Equatable) -> Bool { + return x == y +} +``` + +The `==` operator is meant to take two values of the same type and compare them. It's clear how that could work for a call like `isEqual(1, 2)`, because both `x` and `y` store values of type `Int`. + +But what about a call `isEqual(1, "one")`? Both `Int` and `String` are `Equatable`, so the call to `isEqual` should be well-formed. However, how would the evaluation of `==` work? There is no operator `==` that works with an `Int` on the left and a `String` on the right, so it would fail at run-time with a type mismatch. + +Swift rejects the example with the following diagnostic: + +``` +error: protocol 'Equatable' can only be used as a generic constraint because it has Self or associated type requirements +``` + +The generalized existentials proposal dedicates quite a bit of its design space to [ways to check whether two instances of existential type contain the same type at runtime](https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#real-types-to-existentials-associated-types) to cope with this aspect of existential types. Generalized existentials can make it *possible* to cope with values of `Equatable` type, but it can't make it easy. The following is a correct implementation of `isEqual` using generalized existentials: + +```swift +func isEqual(_ x: Equatable, y: Equatable) -> Bool { + if let yAsX = y as? x.Self { + return x == yAsX + } + + if let xAsY = x as? y.Self { + return xAsY == y + } + + return false +} +``` + +Note that the user must explicitly cope with the potential for run-time type mismatches, because the Swift language will not implicitly defer type checking to run time. + +Existentials also interact poorly with generics, because a value of existential type does not conform to its own protocol. For example: + +```swift +protocol P { } + +func acceptP(_: T) { } +func provideP(_ p: P) { + acceptP(p) // error: protocol type 'P' cannot conform to 'P' because only + // concrete types can conform to protocols +} +``` + +[Hamish](https://stackoverflow.com/users/2976878/hamish) provides a [complete explanation on StackOverflow](https://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself) as to why an existential of type `P` does not conform to the protocol `P`. The following example from that answer demonstrates the point with an initializer requirement: + +```swift +protocol P { + init() +} + +struct S: P {} +struct S1: P {} + +extension Array where Element: P { + mutating func appendNew() { + // If Element is P, we cannot possibly construct a new instance of it, as you cannot + // construct an instance of a protocol. + append(Element()) + } +} + +var arr: [P] = [S(), S1()] + +// error: Using 'P' as a concrete type conforming to protocol 'P' is not supported +arr.appendNew() +``` + +Hamish notes that: + +> We cannot possibly call `appendNew()` on a `[P]`, because `P` (the `Element`) is not a concrete type and therefore cannot be instantiated. It must be called on an array with concrete-typed elements, where that type conforms to `P`. + +The major limitations that Swift places on existentials are, fundamentally, because different instances of existential type may have different types at run time. Generalized existentials can lift some restrictions (e.g., they can allow values of type `Equatable` or `Collection` to exist), but they cannot make the potential for run-time type conflicts disappear without weakening the type-safety guarantees provided by the language (e.g., `x == y` for `Equatable` `x` and `y` will still be an error) nor make existentials as powerful as concrete types (existentials still won't conform to their own protocols). + +Opaque result types have none of these limitations, because an opaque result type is a name for some fixed-but-hidden concrete type. If a function returns `some Equatable` result type, one can compare the results of successive calls to the function with `==`: + +```swift +func getEquatable() -> some Equatable { + return Int.random(in: 0..<10) +} + +let x = getEquatable() +let y = getEquatable() +if x == y { // ok: calls to getEquatable() always return values of the same type + print("Bingo!") +} +``` + +Opaque result types *do* conform to the protocols they name, because opaque result types are another name for some concrete type that is guaranteed to conform to those protocols. For example: + +```swift +func isEqualGeneric(_ lhs: T, _ rhs: T) -> Bool { + return lhs == rhs +} + +let x = getEquatable() +let y = getEquatable() +if isEqual(x, y) { // ok: the opaque result of getEquatable() conforms to Equatable + print("Bingo!") +} +``` + +(Generalized) existentials are well-suited for use in heterogeneous collections, or other places where one expects the run-time types of values to vary and there is little need to compare two different values of existential type. However, they don't fit the use cases outlined for opaque result types, which require the types that result from calls to compose well with generics and provide the same capabilities as a concrete type. + +## Detailed design + +### Grammar of opaque result types + +The grammatical production for opaque result types is straightforward: + +``` +type ::= opaque-type + +opaque-type ::= 'some' type +``` + +The `type` following the `'some'` keyword is semantically restricted to be a class or existential type, meaning it must consist only of `Any`, `AnyObject`, protocols, or base classes, possibly composed using `&`. + +### Restrictions on opaque result types + +Opaque result types can only be used as the result type of a function, the type of a variable, or the result type of a subscript. The opaque type must be the entire return type of the function, For example, one cannot return an optional opaque result type: + +```swift +func f(flip: Bool) -> (some P)? { // error: `some P` is not the entire return type + ... +} +``` + +Opaque result types cannot be used in the requirements of a protocol: + +```swift +protocol Q { + func f() -> some P // error: cannot use opaque result type within a protocol +} +``` + +Associated types provide a better way to model the same problem, and the requirements can then be satisfied by a function that produces an opaque result type. + +Similarly to the restriction on protocols, opaque result types cannot be used for a non-`final` declaration within a class: + +```swift +class C { + func f() -> some P { ... } // error: cannot use opaque result type with a non-final method + final func g() -> some P { ... } // ok +} +``` + +### Uniqueness of opaque result types + +Opaque result types are uniqued based on the function/property/subscript and any generic type arguments. For example: + +```swift +func makeOpaque(_: T.Type) -> some Any { /* ... */ } +var x = makeOpaque(Int.self) +x = makeOpaque(Double.self) // error: "opaque" type from makeOpaque is distinct from makeOpaque +``` + +This includes any generic type arguments from outer contexts, e.g., + +```swift +extension Array where Element: Comparable { + func opaqueSorted() -> some Sequence { /* ... */ } +} + +var x = [1, 2, 3]. opaqueSorted() +x = ["a", "b", "c"].opaqueSorted() // error: opaque result types for [Int].opaqueSorted() and [String].opaqueSorted() differ +``` + +### Implementation strategy + +From an implementation standpoint, a client of a function with an opaque result type needs to treat values of that result type like any other resilient value type: its size, alignment, layout, and operations are unknown. + +However, when the body of the function is known to the client (e.g., due to inlining or because the client is in the same compilation unit as the function), the compiler's optimizer will have access to the specific concrete type, eliminating the indirection cost of the opaque result type. + +## Source compatibility + +Opaque result types are purely additive. They can be used as a tool to improve long-term source (and binary) stability, by not exposing the details of a result type to clients. + +If opaque result types are retroactively adopted in a library, it would initially break source compatibility (e.g., if types like `EnumeratedSequence`, `FlattenSequence`, and `JoinedSequence` were removed from the public API) but could provide longer-term benefits for both source and ABI stability because fewer details would be exposed to clients. There are some mitigations for source compatibility, e.g., a longer deprecation cycle for the types or overloading the old signature (that returns the named types) with the new signature (that returns an opaque result type). + +## Effect on ABI stability + +Opaque result types are an ABI-additive feature, so do not in and of themselves impact existing ABI. However, additional runtime support is however needed to support instantiating opaque result types across ABI boundaries, meaning that *a Swift 5.1 runtime will be required to deploy code that uses opaque types in public API*. Also, changing an existing API to make use of opaque result types instead of returning concrete types would be an ABI-breaking change, so one of the source compatibility mitigations mentioned above would also need to be deployed to maintain ABI compatibility with existing binary clients. + +## Effect on API resilience + +Opaque result types are part of the result type of a function/type of a variable/element type of a subscript. The requirements that describe the opaque result type cannot change without breaking the API/ABI. However, the underlying concrete type *can* change from one version to the next without breaking ABI, because that type is not known to clients of the API. + +One notable exception to the above rule is `@inlinable`: an `@inlinable` declaration with an opaque result type requires that the underlying concrete type be `public` or `@usableFromInline`. Moreover, the underlying concrete type *cannot be changed* without breaking backward compatibility, because it's identity has been exposed by inlining the body of the function. That makes opaque result types somewhat less compelling for the `compactMap` example presented in the introduction, because one cannot have `compactMap` be marked `@inlinable` with an opaque result type, and then later change the underlying concrete type to something more efficient. + +We could allow an API originally specified using an opaque result type to later evolve to specify the specific result type. The result type itself would have to become visible to clients, and this might affect source compatibility, but (mangled name aside) such a change would be resilient. + +## Rust's `impl Trait` + +The proposed Swift feature is largely based on Rust's `impl Trait` language feature, described by [Rust RFC 1522](https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md) and extended by [Rust RFC 1951](https://github.com/rust-lang/rfcs/blob/master/text/1951-expand-impl-trait.md). There are only a small number of differences between this feature as expressed in Swift vs. Rust's `impl Trait` as described in RFC 1522: + +* Swift's need for a stable ABI necessitates translation of opaque result types as resilient types, which is unnecessary in Rust's model, where the concrete type can always be used for code generation. +* Swift's opaque result types are fully opaque, because Swift doesn't have pass-through protocols like Rust's `Send` trait, which simplifies the type checking problem slightly. +* Due to associated type inference, Swift already has a way to "name" an opaque result type in some cases. + +## Alternatives considered + +The main design question here is the keyword to use to introduce an opaque +return type. This proposal suggests the word `some`, since it has the right connotation by analogy to `Any` which is used to describe dynamically type-erased containers (and has been proposed to be a general way of referring to existential types)--a function that has an opaque return type returns *some* specific type +that conforms to the given constraints, whereas an existential can contain *any* type dynamically at any point in time. There are nonetheless reasonable objections to this keyword: + +- `some` is already used in the language as one of the case names for `Optional`; it is rare to need to spell `Optional.some` or `.some` explicitly, but it could nonetheless introduce confusion. +- In spoken language, `some type` and `sum type` sound the same. +- `swift some` could be a difficult term to search for. (However, on Google it currently gives [reasonable results](https://www.google.com/search?client=safari&rls=en&q=swift+some&ie=UTF-8&oe=UTF-8) about Optional in Swift.) + +Another obvious candidate is `opaque`, following the title of this very proposal. The word "opaque" is itself an overloaded term with existing meaning in many domains, which is unfortunate: + +``` +protocol Shape {} + +func translucentRectangle() -> opaque Shape { ... } +``` + +The term "opaque" is also fairly heavily overloaded in the Swift implementation (albeit not so much the source-level programming model). It may be that there is a better term, such as "abstract return types", to refer to this feature in its entirety. + +## Future Directions + +### `where` constraints on associated types of opaque types + +This proposal does not yet provide a syntax for specifying constraints on +associated types implied by an opaque type's protocol constraints. This is important for the feature to be useful with many standard library protocols such as `Collection`, since nearly every use case where someone wants to return `some Collection` would also want to specify the type of the `Element`s in that collection. The design of this feature is itself a fairly involved implementation effort and language design discussion, so it makes sense to split it out as a separate proposal. Normally in +Swift these constraints are expressed in `where` clauses; however, this is +not directly possible for opaque types because there is no way to name the +underlying type. This is similar to the spelling problem for [generalized existentials](https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md), and we'd want to find a syntax solution that works well for both. Possibilities include: + +- Allowing a placeholder like `_` to refer to the unnamed opaque type in the where clause: + + ``` + func foo() -> some Collection where _.Element == Int { ... } + ``` + + Possible issues with this approach include: + + - The specification for the opaque type is no longer syntactically self-contained, being spread between the return type of the function and the `where` constraints, which is an implementation and readability challenge. + - This overloads the `_` token, something Swift has thus far managed to avoid for the most part (and a common complaint about Scala in particular). + - This only scales to one opaque/existential type. Future extensions of opaque result types could conceivably allow multiple opaque types to be in a declaration. + +- Adding a shorthand similar to Rust's `Trait` syntax to allow associated constraints to be spelled together with the core protocol type. With opaque types, this could look something like this: + + ``` + // same type constraint + func foo() -> some Collection<.Element == Int> { ... } + // associated type protocol constraint + func foo() -> some Collection<.Element: SignedInteger> { ... } + ``` + + This could be a generally useful shorthand syntax for declaring a protocol constraint with associated type constraints, usable in `where` clauses, `some` clauses, and generalized existentials, at the risk of complicating the grammar and introducing More Than One Way To Do It for writing generic constraints. + +### Conditional conformances + +When a generic function returns an adapter type, it's not uncommon for the adapter to use [conditional conformances](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md) to reflect the capabilities of its underlying type parameters. For example, consider the `reversed()` operation: + +```swift +extension BidirectionalCollection { + public func reversed() -> ReversedCollection { + return ReversedCollection(...) + } +} +``` + +`ReversedCollection` is always a `BidirectionalCollection`, with a conditional conformance to `RandomAccessCollection`: + +```swift +public struct ReversedCollection: BidirectionalCollection { + /* ... */ +} + +extension ReversedCollection: RandomAccessCollection + where C: RandomAccessCollection { + /* ... */ +} +``` + +What happens if we hid the `ReversedCollection` adapter type behind an opaque result type? + +```swift +extension BidirectionalCollection { + public func reversed() -> some BidirectionalCollection<.Element == Element> { + return ReversedCollection(...) + } +} +``` + +Now, clients that call `reversed()` on a `RandomAccessCollection` would get back something that is only known to be a `BidirectionalCollection`, and there would be no way to treat it as a `RandomAccessCollection`. The library could provide another overload of `reversed()`: + +```swift +extension RandomAccessCollection { + public func reversed() -> some RandomAccessCollection<.Element == Element> { + return ReversedCollection(...) + } +} +``` + +However, doing so is messy, and the client would have no way to know that the type returned by the two `reversed()` functions are, in fact, the same. To express the conditional conformance behavior, we could eventually extend the syntax of opaque result types to describe additional capabilities of the resulting type that depend on extended requirements. For example, we could state that the result of `reversed()` is *also* a `RandomAccessCollection` when `Self` is a `RandomAccessCollection`. One possible syntax: + +```swift +extension BidirectionalCollection { + public func reversed() + -> some BidirectionalCollection<.Element == Element> + -> some RandomAccessCollection where Self: RandomAccessCollection { + return ReversedCollection(...) + } +} +``` + +Here, we add a second return type and `where` clause that states additional information about the opaque result type (it is a `RandomAccessCollection`) as well as the requirements under which that capability is available (the `where Self: RandomAccessCollection`). One could have many conditional clauses, e.g., + +```swift +extension BidirectionalCollection { + public func reversed() + -> some BidirectionalCollection<.Element == Element> + -> some RandomAccessCollection where Self: RandomAccessCollection + -> some MutableCollection where Self: MutableCollection { + return ReversedCollection(...) + } +} +``` + +Here, the opaque result type conforms to `MutableCollection` when the `Self` type conforms to `MutableCollection`. This conditional result is independent of whether the opaque result type conforms to `RandomAccessCollection`. + +Note that Rust [didn't tackle the issue of conditional constraints](https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md#compatibility-with-conditional-trait-bounds) in their initial design of `impl Trait` types. + +### Opaque type aliases + +Opaque result types are tied to a specific declaration. They offer no way to state that two related APIs with opaque result types produce the *same* underlying concrete type. For example, the `LazyCompactMapCollection` type proposed in [SE-0222](https://github.com/apple/swift-evolution/blob/master/proposals/0222-lazy-compactmap-sequence.md) is used to describe four different-but-related APIs: lazy `compactMap`, `filter`, and `map` on various types. + +Opaque type aliases would allow us to provide a named type with stated capabilities, for which the underlying implementation type is still hidden from the API and ABI of clients like an opaque result type. For example: + +```swift +public typealias LazyCompactMapCollection + : some Collection<.Element == ElementOfResult> + = LazyMapSequence< + LazyFilterSequence< + LazyMapSequence + >, + ElementOfResult + > +``` + +In this strawman syntax, the opaque result type following the `:` is how clients see `LazyCompactMapCollection`. The underlying concrete type, spelled after the `=`, is visible only to the implementation (see below for more details). + +With this feature, multiple APIs could be described as returning a `LazyCompactMapCollection`: + +```swift +extension LazyMapCollection { + public func compactMap(_ transform: @escaping (Element) -> U?) -> LazyCompactMapCollection { + // ... + } + + public func filter(_ isIncluded: @escaping (Element) -> Bool) -> LazyCompactMapCollection { + // ... + } +} +``` + +From the client perspective, both APIs would return the same type, but the specific underlying type would not be known. + +```swift +var compactMapOp = values.lazy.map(f).compactMap(g) +if Bool.random() { + compactMapOp = values.lazy.map(f).filter(h) // ok: both APIs have the same type +} +``` + +The underlying concrete type of an opaque type alias would have restricted visibility, its access being the more restrictive of the access level below the type alias's access (e.g., `internal` for a `public` opaque type alias, `private` for an `internal` opaque type alias) and the access levels of any type mentioned in the underlying concrete type. For the opaque type alias `LazyCompactMapCollection` above, this would be the most restrictive of `internal` (one level below `public`) and the types involved in the underlying type (`LazyMapSequence`, `LazyFilterSequence`), all of which are public. Therefore, the access of the underlying concrete type is `internal`. + +If, instead, the concrete underlying type of `LazyCompactMapCollection` involved a private type, e.g., + +```swift +private struct LazyCompactMapCollectionImpl { + // ... +} + +public typealias LazyCompactMapCollection + : some Collection<.Element == ElementOfResult> + = LazyCompactMapCollectionImpl +``` + +then the access of the underlying concrete type would be `private`. + +The access of the underlying concrete type only affects the type checking of function bodies. If the function body has access to the underlying concrete type, then the opaque typealias and its underlying concrete type are considered to be equivalent. Extending the example above: + +```swift +extension LazyMapCollection { + public func compactMap(_ transform: @escaping (Element) -> U?) -> LazyCompactMapCollection { + // ok so long as we are in the same file as the opaque type alias LazyCompactMapCollection, + // because LazyCompactMapCollectionImpl and + // LazyCompactMapCollection are known to be identical + return LazyCompactMapCollectionImpl(elements, transform) + } +} +``` + +Opaque type aliases might also offer an alternative solution to the issue with conditional conformance. Instead of annotating the `opaque` result type with all of the possible conditional conformances, we can change `reversed()` to return an opaque type alias `Reversed`, giving us a name for the resulting type: + +```swift +public typealias Reversed + : some BidirectionalCollection<.Element == Base.Element> + = ReversedCollection +``` + +Then, we can describe conditional conformances on `Reversed`: + +```swift +extension Reversed: RandomAccessCollection where Element == Base.Element { } +``` + +The conditional conformance must be satisfied by the underlying concrete type (here, `ReversedCollection`), and the extension must be empty: `Reversed` would be the same as `ReversedCollection` at runtime, so one could not add any API to `Reversed` beyond what `ReversedCollection` supports. + +### Opaque argument types + +The idea of opaque result types could be analogized to argument types, as an alternative shorthand to writing simple generic functions without explicit type arguments. [Rust RFC 1951](https://github.com/rust-lang/rfcs/blob/master/text/1951-expand-impl-trait.md) makes the argument for extending `impl Trait` to arguments in Rust. In Swift, this would mean that: + +```swift +func foo(x: some P) { ... } +``` + +would be sugar for: + +```swift +func foo(x: T) { ... } +``` + +And a more involved example like this: + +```swift +func concatenate( + _ x: C, + _ y: D +) -> some Collection<.Element == C.Element> + where C.Element == D.Element +{ + return LazyConcat(x, y) +} +``` + +could be simplified to: + +```swift +func concatenate(_ x: some Collection<.Element == E>, + _ y: some Collection<.Element == E>) + -> some Collection<.Element == E> { + return LazyConcat(x, y) +} +``` + +Unlike opaque return types, this doesn't add any additional expressivity to the language, but it can make the declarations of many generic functions look simpler and more streamlined, reducing the "angle bracket blindness" caused by the syntactic overhead and indirection that traditional generic argument declaration syntax imposes. From 6304b62ac3083c315a20cebb33d7fc4cd3cf65a4 Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Tue, 5 Mar 2019 19:47:58 -0800 Subject: [PATCH 1015/4563] [SE-0244] Update status to match template. --- proposals/0244-opaque-result-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index b15f548e0a..20efdb5e9d 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -3,7 +3,7 @@ * Proposal: [SE-0244](0244-opaque-result-types.md) * Author: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **In review (March 5 – March 15)** +* Status: Status: **Active review (March 5...March 15, 2019)** * Pull Request: [apple/swift#21137](https://github.com/apple/swift/pull/21137) * Toolchain: https://github.com/apple/swift/pull/21137#issuecomment-468118328 From 18a854ef9cff557f7d1a73e474346cb8150cc77d Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Tue, 5 Mar 2019 19:48:37 -0800 Subject: [PATCH 1016/4563] Fix typo. --- proposals/0244-opaque-result-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index 20efdb5e9d..32e2ca16f5 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -3,7 +3,7 @@ * Proposal: [SE-0244](0244-opaque-result-types.md) * Author: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: Status: **Active review (March 5...March 15, 2019)** +* Status: **Active review (March 5...March 15, 2019)** * Pull Request: [apple/swift#21137](https://github.com/apple/swift/pull/21137) * Toolchain: https://github.com/apple/swift/pull/21137#issuecomment-468118328 From cddeb19786b5daf51e84a8cbdfd6234ecc3ce1a0 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Tue, 5 Mar 2019 20:44:49 -0800 Subject: [PATCH 1017/4563] Remove the "we won't do single quotes for character literals" point It turns out that, we are seriously considering it! --- commonly_proposed.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/commonly_proposed.md b/commonly_proposed.md index 08ea2712e8..857315fa33 100644 --- a/commonly_proposed.md +++ b/commonly_proposed.md @@ -18,8 +18,6 @@ Several of the discussions below refer to "C family" languages. This is intended ## Strings, Characters, and Collection Types - * [Single-quotes `''` for character literals](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003977.html): Swift takes the approach of highly valuing Unicode. However, there are multiple concepts of a character that could make sense in Unicode, and none is so much more commonly used than the others that it makes sense to privilege them. We'd rather save single quoted literals for a greater purpose (e.g. non-escaped string literals). - * Make `Array` subscript access return `T?` or `T!` instead of `T`: The current array behavior is [intentional](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/002446.html), as it accurately reflects the fact that out-of-bounds array access is a logic error. Changing the current behavior would slow `Array` accesses to an unacceptable degree. This topic has come up [multiple](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/002425.html) times before but is very unlikely to be accepted. ## Control Flow, Closures, Optional Binding, and Error Handling From 4256936fd1ddbabfa4ed4ab6f22b69922096e67c Mon Sep 17 00:00:00 2001 From: David Hart Date: Wed, 6 Mar 2019 13:25:47 +0100 Subject: [PATCH 1018/4563] Fix typos, code errors, code indentation and general readability --- proposals/0244-opaque-result-types.md | 198 ++++++++++++-------------- 1 file changed, 91 insertions(+), 107 deletions(-) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index 32e2ca16f5..89c986665e 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -22,29 +22,21 @@ If we want to encapsulate that code in another method, we're going to have to wr ```swift extension LazyMapCollection { public func compactMap( - _ transform: @escaping (Elements.Element) -> ElementOfResult? - ) - -> LazyMapSequence< - LazyFilterSequence< - LazyMapSequence - >, - ElementOfResult - > { + _ transform: @escaping (Elements.Element) -> ElementOfResult? + ) -> LazyMapSequence>, ElementOfResult> { return self.map(transform).filter { $0 != nil }.map { $0! } } } ``` -The author of `compactMap(_:)` doesn't want to have to figure out that type, -and the clients of `compactMap(_:)` don't want to have to reason about it. `compactMap(_:)` is the subject of [SE-0222](https://github.com/apple/swift-evolution/blob/master/proposals/0222-lazy-compactmap-sequence.md), which introduces a new standard library type `LazyCompactMapCollection` for the sole purpose of describing the result type of `compactMap(_:)`: +The author of `compactMap(_:)` doesn't want to have to figure out that type, and the clients of `compactMap(_:)` don't want to have to reason about it. `compactMap(_:)` is the subject of [SE-0222](https://github.com/apple/swift-evolution/blob/master/proposals/0222-lazy-compactmap-sequence.md), which introduces a new standard library type `LazyCompactMapCollection` for the sole purpose of describing the result type of `compactMap(_:)`: ```swift extension LazyMapCollection { public func compactMap( - _ transform: @escaping (Element) -> ElementOfResult? - ) - -> LazyCompactMapCollection { - return LazyCompactMapCollection(/*...*/) + _ transform: @escaping (Element) -> ElementOfResult? + ) -> LazyCompactMapCollection { + return LazyCompactMapCollection(/* ... */) } } ``` @@ -61,8 +53,8 @@ protocol Shape { func collides(with: Other) -> Bool } -struct Rectangle: Shape { ... } -struct Circle: Shape { ... } +struct Rectangle: Shape { /* ... */ } +struct Circle: Shape { /* ... */ } ``` along with composable transformations to combine and modify primitive shapes: @@ -70,24 +62,24 @@ along with composable transformations to combine and modify primitive shapes: ```swift struct Union: Shape { var a: A, b: B - ... + // ... } struct Intersect: Shape { var a: A, b: B - ... + // ... } struct Transformed: Shape { var shape: S var transform: Matrix3x3 - ... + // ... } ``` One could compose these transformations by using the existential type `Shape` instead of generic arguments, but doing so would imply more dynamism and runtime overhead than may be desired. By composing generic containers, generic specialization can optimize together the composed operations like it does for `lazy` collections. A game or graphics app may want to define objects in terms of their shapes: -``` +```swift protocol GameObject { // The shape of the object associatedtype Shape: Shapes.Shape @@ -98,7 +90,7 @@ protocol GameObject { However, this means that implementers of the `GameObject` protocol would now be burdened with writing out long, explicit types for their shapes: -``` +```swift struct EightPointedStar: GameObject { var shape: Union> { return Union(Rectangle(), Transformed(Rectangle(), by: .fortyFiveDegrees) @@ -116,7 +108,7 @@ struct EightPointedStar: GameObject { } ``` -to declare that an `EightPointedStar` has some `Shape` without having to specify exactly what shape thatis. The underlying concrete type is hidden, and can even change from one version of the library to the next without breaking those clients, because the actual type identity was never exposed. This allows the library to provide a potentially-more-efficient design without expanding the surface area of the library or burdening implementors of the library's protocols with impractical type compositions. +to declare that an `EightPointedStar` has some `Shape` without having to specify exactly what shape that is. The underlying concrete type is hidden, and can even change from one version of the library to the next without breaking those clients, because the actual type identity was never exposed. This allows the library to provide a potentially-more-efficient design without expanding the surface area of the library or burdening implementors of the library's protocols with impractical type compositions. Swift-evolution thread: [Opaque result types](https://forums.swift.org/t/opaque-result-types/15645) @@ -125,9 +117,7 @@ Swift-evolution thread: [Opaque result types](https://forums.swift.org/t/opaque- This proposal introduces syntax that can be used to describe an opaque result type for a function. Instead of specifying a specific return type, a function can declare that it returns `some` type that satisfies a set of constraints, such as protocol requirements. This `some` specifier can only be used in the result type of a function, the type of a property, or the element type of a subscript declaration. The return type is backed by some specific concrete type, but that type is only known to the implementation of that function/property/subscript. Everywhere else, the type is opaque, and is described only by its characteristics and originating function/property/subscript. For example, a function can declare that it produces something that's a `MutableCollection` and `RangeReplaceableCollection`: ```swift -func makeMeACollection(with element: T) - -> some MutableCollection & RangeReplaceableCollection -{ +func makeMeACollection(with element: T) -> some MutableCollection & RangeReplaceableCollection { return [element] // ok: an array of T satisfies all of the requirements } ``` @@ -136,12 +126,12 @@ Following the `some` keyword is a class, protocol, `Any`, `AnyObject`, or compos ```swift var c = makeMeACollection(with: 17) -c.append(c.first!) // ok: it's a RangeReplaceableCollection +c.append(c.first!) // ok: it's a RangeReplaceableCollection c[c.startIndex] = c.first! // ok: it's a MutableCollection -print(c.reversed()) // ok: all Collection/Sequence operations are available +print(c.reversed()) // ok: all Collection/Sequence operations are available func foo(_ : C) { } -foo(c) // ok: C inferred to opaque result type of makeMeACollection() +foo(c) // ok: C inferred to opaque result type of makeMeACollection() ``` Moreover, opaque result types to be used freely with other generics, e.g., forming a collection of the results: @@ -150,15 +140,16 @@ Moreover, opaque result types to be used freely with other generics, e.g., formi var cc = [c] cc.append(c) // ok: cc's Element == the result type of makeMeACollection var c2 = makeMeACollection(with: 38) -cc.append(c2) // ok: Element == the result type of makeMeACollection +cc.append(c2) // ok: Element == the result type of makeMeACollection ``` ### Type identity + An opaque result type is not considered equivalent to its underlying type by the static type system: ```swift var intArray = [Int]() -cc.append(intArray) // error: [Int] is not known to equal the result type of makeMeACollection +cc.append(intArray) // error: [Int] is not known to equal the result type of makeMeACollection ``` However, as with generic type parameters, one can inspect an opaque type's underlying type at runtime. For example, a conditional cast could determine whether the result of `makeMeACollection` is of a particular type: @@ -173,7 +164,8 @@ if let arr = makeMeACollection(Int.self) as? [Int] { In other words, opaque result types are only opaque to the static type system. They don't exist at runtime. -### Implementing a function returning an opaque type +### Implementing a function returning an opaque type + The implementation of a function returning an opaque type must return a value of the same concrete type `T` from each `return` statement, and `T` must meet all of the constraints stated on the opaque type. For example: ```swift @@ -185,33 +177,33 @@ func f1() -> some P { return "opaque" } -func f2(i: Int) -> some P { // ok: both returns produce Int +func f2(i: Int) -> some P { // ok: both returns produce Int if i > 10 { return i } return 0 } func f2(flip: Bool) -> some P { if flip { return 17 } - return "a string" // error: different return types Int and String + return "a string" // error: different return types Int and String } func f3() -> some P { - return 3.1419 // error: Double does not conform to P + return 3.1419 // error: Double does not conform to P } func f4() -> some P { let p: P = "hello" - return p // error: protocol type P does not conform to P + return p // error: protocol type P does not conform to P } func f5() -> some P { - return f1() // ok: f1() returns an opaque type that conforms to P + return f1() // ok: f1() returns an opaque type that conforms to P } protocol Initializable { init() } func f6(_: T.Type) -> some P { - return T() // ok: T will always be a concrete type conforming to P + return T() // ok: T will always be a concrete type conforming to P } ``` @@ -224,7 +216,7 @@ func f7(_ i: Int) -> some P { if i == 0 { return f7(1) // ok: returning our own opaque result type } else if i < 0 { - let result: Int = f7(-i) // error: opaque result type of f7() is not convertible to Int + let result: Int = f7(-i) // error: opaque result type of f7() is not convertible to Int return result } else { return 0 // ok: grounds the recursion with a concrete type @@ -247,8 +239,7 @@ func f8(_ i: Int) -> some P { } ``` -A function with an opaque result type is also required to have a `return` -statement even if it does not terminate: +A function with an opaque result type is also required to have a `return` statement even if it does not terminate: ```swift func f9() -> some P { @@ -258,9 +249,7 @@ func f9() -> some P { } ``` -This requirement is necessary because, even though `f9`'s return value cannot be reached, the return type of `f9` can still be propagated by local type inference -or generic instantiation in ways that don't require evaluating `f9`, so a type -for `f9` must be available: +This requirement is necessary because, even though `f9`'s return value cannot be reached, the return type of `f9` can still be propagated by local type inference or generic instantiation in ways that don't require evaluating `f9`, so a type for `f9` must be available: ```swift let delayedF9 = { f9() } // closure has type () -> return type of f9 @@ -271,7 +260,7 @@ conform to the constraints of the opaque type. If `Never` does conform, and it is desired as the underlying return type, that can be written explicitly as a `return` statement: -``` +```swift extension Never: P {} func f9b() -> some P { return fatalError("not implemented") // OK, explicitly binds return type to Never @@ -284,7 +273,7 @@ Opaque result types can also be used with properties and subscripts: ```swift struct GameObject { - var shape: some Shape { ... } + var shape: some Shape { /* ... */ } } ``` @@ -303,14 +292,14 @@ public protocol P { } private struct Witness: P { - mutating func flip() { ... } + mutating func flip() { /* ... */ } } public var someP: some P = Witness() // Module B import A -someP.flip() // ok: flip is a mutating function called on a variable +someP.flip() // ok: flip is a mutating function called on a variable ``` With a subscript or a computed property, the type of the value provided to the setter (e.g., `newValue`) is determined by the `return` statements in the getter, so the type is consistent and known only to the implementation of the property or subscript. For example: @@ -320,7 +309,7 @@ protocol P { } private struct Impl: P { } public struct Vendor { - private var storage: [Impl] = [...] + private var storage: [Impl] = [/* ... */] public var count: Int { return storage.count @@ -346,7 +335,7 @@ While one can use type inference to declare variables of the opaque result type ```swift func f1() -> some P { /* ... */ } -let vf1 = f1() // type of vf1 is the opaque result type of f1() +let vf1 = f1() // type of vf1 is the opaque result type of f1() ``` However, the type inference used to satisfy associated type requirements can deduce an opaque result type as the associated type of the protocol: @@ -359,13 +348,13 @@ protocol GameObject { } struct Player: GameObject { - var shape: some Shape { ... } - - /* infers typealias Shape = opaque result type of Player.shape */ + var shape: some Shape { /* ... */ } + + // infers typealias Shape = opaque result type of Player.shape } -let sv: S.SomeType // ok: names the opaque result type of S.someValue() -sv = S().someValue() // ok: returns the same opaque result type +let pos: Player.ObjectShape // ok: names the opaque result type of S.someValue() +pos = Player().shape // ok: returns the same opaque result type ``` Note that having a name for the opaque result type still doesn't give information about the underlying concrete type. For example, the only way to create an instance of the type `S.SomeType` is by calling `S.someValue()`. @@ -382,8 +371,8 @@ protocol P func anyP() -> P { /* ... */ } func someP() -> some P { /* ... */ } -anyP().foo() // ok -someP().foo() // ok +anyP().foo() // ok +someP().foo() // ok ``` However, the fundamental difference between opaque result types and existentials revolves around type identity. All instances of an opaque result type are guaranteed to have the same type at run time, whereas different instances of an existential type may have different types at run time. It is this aspect of existential types that makes their use so limited in Swift. For example, consider a function that takes two values of (existential) type `Equatable` and tries to compare them: @@ -415,11 +404,11 @@ func isEqual(_ x: Equatable, y: Equatable) -> Bool { if let yAsX = y as? x.Self { return x == yAsX } - + if let xAsY = x as? y.Self { return xAsY == y } - + return false } ``` @@ -433,7 +422,7 @@ protocol P { } func acceptP(_: T) { } func provideP(_ p: P) { - acceptP(p) // error: protocol type 'P' cannot conform to 'P' because only + acceptP(p) // error: protocol type 'P' cannot conform to 'P' because only // concrete types can conform to protocols } ``` @@ -477,7 +466,7 @@ func getEquatable() -> some Equatable { let x = getEquatable() let y = getEquatable() -if x == y { // ok: calls to getEquatable() always return values of the same type +if x == y { // ok: calls to getEquatable() always return values of the same type print("Bingo!") } ``` @@ -491,7 +480,7 @@ func isEqualGeneric(_ lhs: T, _ rhs: T) -> Bool { let x = getEquatable() let y = getEquatable() -if isEqual(x, y) { // ok: the opaque result of getEquatable() conforms to Equatable +if isEqual(x, y) { // ok: the opaque result of getEquatable() conforms to Equatable print("Bingo!") } ``` @@ -506,7 +495,7 @@ The grammatical production for opaque result types is straightforward: ``` type ::= opaque-type - + opaque-type ::= 'some' type ``` @@ -518,7 +507,7 @@ Opaque result types can only be used as the result type of a function, the type ```swift func f(flip: Bool) -> (some P)? { // error: `some P` is not the entire return type - ... + // ... } ``` @@ -526,7 +515,7 @@ Opaque result types cannot be used in the requirements of a protocol: ```swift protocol Q { - func f() -> some P // error: cannot use opaque result type within a protocol + func f() -> some P // error: cannot use opaque result type within a protocol } ``` @@ -536,8 +525,8 @@ Similarly to the restriction on protocols, opaque result types cannot be used fo ```swift class C { - func f() -> some P { ... } // error: cannot use opaque result type with a non-final method - final func g() -> some P { ... } // ok + func f() -> some P { /* ... */ } // error: cannot use opaque result type with a non-final method + final func g() -> some P { /* ... */ } // ok } ``` @@ -548,7 +537,7 @@ Opaque result types are uniqued based on the function/property/subscript and any ```swift func makeOpaque(_: T.Type) -> some Any { /* ... */ } var x = makeOpaque(Int.self) -x = makeOpaque(Double.self) // error: "opaque" type from makeOpaque is distinct from makeOpaque +x = makeOpaque(Double.self) // error: "opaque" type from makeOpaque is distinct from makeOpaque ``` This includes any generic type arguments from outer contexts, e.g., @@ -559,7 +548,7 @@ extension Array where Element: Comparable { } var x = [1, 2, 3]. opaqueSorted() -x = ["a", "b", "c"].opaqueSorted() // error: opaque result types for [Int].opaqueSorted() and [String].opaqueSorted() differ +x = ["a", "b", "c"].opaqueSorted() // error: opaque result types for [Int].opaqueSorted() and [String].opaqueSorted() differ ``` ### Implementation strategy @@ -606,10 +595,10 @@ that conforms to the given constraints, whereas an existential can contain *any* Another obvious candidate is `opaque`, following the title of this very proposal. The word "opaque" is itself an overloaded term with existing meaning in many domains, which is unfortunate: -``` +```swift protocol Shape {} -func translucentRectangle() -> opaque Shape { ... } +func translucentRectangle() -> opaque Shape { /* ... */ } ``` The term "opaque" is also fairly heavily overloaded in the Swift implementation (albeit not so much the source-level programming model). It may be that there is a better term, such as "abstract return types", to refer to this feature in its entirety. @@ -626,7 +615,7 @@ underlying type. This is similar to the spelling problem for [generalized existe - Allowing a placeholder like `_` to refer to the unnamed opaque type in the where clause: - ``` + ```swift func foo() -> some Collection where _.Element == Int { ... } ``` @@ -638,7 +627,7 @@ underlying type. This is similar to the spelling problem for [generalized existe - Adding a shorthand similar to Rust's `Trait` syntax to allow associated constraints to be spelled together with the core protocol type. With opaque types, this could look something like this: - ``` + ```swift // same type constraint func foo() -> some Collection<.Element == Int> { ... } // associated type protocol constraint @@ -663,12 +652,11 @@ extension BidirectionalCollection { ```swift public struct ReversedCollection: BidirectionalCollection { - /* ... */ + // ... } -extension ReversedCollection: RandomAccessCollection - where C: RandomAccessCollection { - /* ... */ +extension ReversedCollection: RandomAccessCollection where C: RandomAccessCollection { + // ... } ``` @@ -677,7 +665,7 @@ What happens if we hid the `ReversedCollection` adapter type behind an opaque re ```swift extension BidirectionalCollection { public func reversed() -> some BidirectionalCollection<.Element == Element> { - return ReversedCollection(...) + return ReversedCollection(/* ... */) } } ``` @@ -687,7 +675,7 @@ Now, clients that call `reversed()` on a `RandomAccessCollection` would get back ```swift extension RandomAccessCollection { public func reversed() -> some RandomAccessCollection<.Element == Element> { - return ReversedCollection(...) + return ReversedCollection(/* ... */) } } ``` @@ -696,10 +684,11 @@ However, doing so is messy, and the client would have no way to know that the ty ```swift extension BidirectionalCollection { - public func reversed() - -> some BidirectionalCollection<.Element == Element> - -> some RandomAccessCollection where Self: RandomAccessCollection { - return ReversedCollection(...) + public func reversed() + -> some BidirectionalCollection<.Element == Element> + -> some RandomAccessCollection where Self: RandomAccessCollection + { + return ReversedCollection(/* ... */) } } ``` @@ -708,11 +697,12 @@ Here, we add a second return type and `where` clause that states additional info ```swift extension BidirectionalCollection { - public func reversed() - -> some BidirectionalCollection<.Element == Element> - -> some RandomAccessCollection where Self: RandomAccessCollection - -> some MutableCollection where Self: MutableCollection { - return ReversedCollection(...) + public func reversed() + -> some BidirectionalCollection<.Element == Element> + -> some RandomAccessCollection where Self: RandomAccessCollection + -> some MutableCollection where Self: MutableCollection + { + return ReversedCollection(/* ... */) } } ``` @@ -730,12 +720,7 @@ Opaque type aliases would allow us to provide a named type with stated capabilit ```swift public typealias LazyCompactMapCollection : some Collection<.Element == ElementOfResult> - = LazyMapSequence< - LazyFilterSequence< - LazyMapSequence - >, - ElementOfResult - > + = LazyMapSequence>, ElementOfResult> ``` In this strawman syntax, the opaque result type following the `:` is how clients see `LazyCompactMapCollection`. The underlying concrete type, spelled after the `=`, is visible only to the implementation (see below for more details). @@ -744,13 +729,13 @@ With this feature, multiple APIs could be described as returning a `LazyCompactM ```swift extension LazyMapCollection { - public func compactMap(_ transform: @escaping (Element) -> U?) -> LazyCompactMapCollection { - // ... - } + public func compactMap(_ transform: @escaping (Element) -> U?) -> LazyCompactMapCollection { + // ... + } - public func filter(_ isIncluded: @escaping (Element) -> Bool) -> LazyCompactMapCollection { - // ... - } + public func filter(_ isIncluded: @escaping (Element) -> Bool) -> LazyCompactMapCollection { + // ... + } } ``` @@ -813,13 +798,13 @@ The conditional conformance must be satisfied by the underlying concrete type (h The idea of opaque result types could be analogized to argument types, as an alternative shorthand to writing simple generic functions without explicit type arguments. [Rust RFC 1951](https://github.com/rust-lang/rfcs/blob/master/text/1951-expand-impl-trait.md) makes the argument for extending `impl Trait` to arguments in Rust. In Swift, this would mean that: ```swift -func foo(x: some P) { ... } +func foo(x: some P) { /* ... */ } ``` would be sugar for: ```swift -func foo(x: T) { ... } +func foo(x: T) { /* ... */ } ``` And a more involved example like this: @@ -828,9 +813,7 @@ And a more involved example like this: func concatenate( _ x: C, _ y: D -) -> some Collection<.Element == C.Element> - where C.Element == D.Element -{ +) -> some Collection<.Element == C.Element> where C.Element == D.Element { return LazyConcat(x, y) } ``` @@ -838,9 +821,10 @@ func concatenate( could be simplified to: ```swift -func concatenate(_ x: some Collection<.Element == E>, - _ y: some Collection<.Element == E>) - -> some Collection<.Element == E> { +func concatenate( + _ x: some Collection<.Element == E>, + _ y: some Collection<.Element == E> +) -> some Collection<.Element == E> { return LazyConcat(x, y) } ``` From 1c72ecf0c6caed29400490e860627ca7d94afd70 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 6 Mar 2019 12:39:49 +0000 Subject: [PATCH 1019/4563] [Status] Add Swift 5.1 to Language Version filter --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index d995900c23..a2c75b4496 100644 --- a/index.js +++ b/index.js @@ -19,7 +19,7 @@ var proposals * To be updated when proposals are confirmed to have been implemented * in a new language version. */ -var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4', '4.1', '4.2', '5'] +var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4', '4.1', '4.2', '5', '5.1'] /** Storage for the user's current selection of filters when filtering is toggled off. */ var filterSelection = [] @@ -990,4 +990,4 @@ function updateFilterDescription (selectedStateNames) { function updateProposalsCount (count) { var numberField = document.querySelector('#proposals-count-number') numberField.innerText = (count.toString() + ' proposal' + (count !== 1 ? 's' : '')) -} \ No newline at end of file +} From 2b28cfbe1a65d808dd8855df6780efeb4a43d890 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 6 Mar 2019 12:43:23 +0000 Subject: [PATCH 1020/4563] [SE-0240] Update status to implemented (Swift 5.1) --- proposals/0240-ordered-collection-diffing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0240-ordered-collection-diffing.md b/proposals/0240-ordered-collection-diffing.md index a6ab59975c..f6f7f51f33 100644 --- a/proposals/0240-ordered-collection-diffing.md +++ b/proposals/0240-ordered-collection-diffing.md @@ -3,7 +3,7 @@ * Proposal: [SE-0240](0240-ordered-collection-diffing.md) * Authors: [Scott Perry](https://github.com/numist), [Kyle Macomber](https://github.com/kylemacomber) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 5.1)** * Implementation: [apple/swift#21845](https://github.com/apple/swift/pull/21845) * Decision notes: [Rationale](https://forums.swift.org/t/accepted-with-modifications-se-0240-ordered-collection-diffing/20008) From c97df337d3c05ffe7860848deac2e5a86f14cd2c Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 6 Mar 2019 08:51:16 -0800 Subject: [PATCH 1021/4563] SE-244: Include structural opaque types in future directions --- proposals/0244-opaque-result-types.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index 32e2ca16f5..21007650c8 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -616,6 +616,29 @@ The term "opaque" is also fairly heavily overloaded in the Swift implementation ## Future Directions +### Opaque types in structural position + +This proposal only allows for the entire return type of a declaration to be +made opaque. It would be reasonable to eventually generalize this to allow +for opaque types to appear structurally, as part of an optional, array, or +other generic type: + +``` +func collection(or not: Bool) -> (some Collection)? { + if not { return nil } + return [1, 2, 3] +} +``` + +Furthermore, there could conceivably be multiple opaque parts of a compound +return type: + +``` +func twoCollections() -> (some Collection, some Collection) { + return ([1, 2, 3], ["one": 1, "two": 2, "three": 3]) +} +``` + ### `where` constraints on associated types of opaque types This proposal does not yet provide a syntax for specifying constraints on From ddb5aad06c27fd4f1629505f74ba9f0a9550b8c7 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 7 Mar 2019 15:55:32 +0000 Subject: [PATCH 1022/4563] [SE-0244] Rename field to match proposal template --- proposals/0244-opaque-result-types.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index c3551571d9..c4840ef5f2 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -1,10 +1,10 @@ # Opaque Result Types * Proposal: [SE-0244](0244-opaque-result-types.md) -* Author: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) +* Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Active review (March 5...March 15, 2019)** -* Pull Request: [apple/swift#21137](https://github.com/apple/swift/pull/21137) +* Implementation: [apple/swift#21137](https://github.com/apple/swift/pull/21137) * Toolchain: https://github.com/apple/swift/pull/21137#issuecomment-468118328 ## Introduction From e0f124d42f72cd648f4a2cc327110d4b63000e0c Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 11 Mar 2019 09:34:38 -0700 Subject: [PATCH 1023/4563] Re-pitch of init(unsafeUninitializedCapacity:initializingWith:) (#993) * First cut of initializer-only * Update the try-at-home implementation * Add vDSP example * tweak example * Fix the try-this-at-home version again Don't leak memory and provide the correct throwing semantics. * Remove vestiges of the proposed method * Update link to implementation PR * Remove the (needs updating) bit * Update title --- .../NNNN-array-uninitialized-initializer.md | 291 ++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 proposals/NNNN-array-uninitialized-initializer.md diff --git a/proposals/NNNN-array-uninitialized-initializer.md b/proposals/NNNN-array-uninitialized-initializer.md new file mode 100644 index 0000000000..0d218f5440 --- /dev/null +++ b/proposals/NNNN-array-uninitialized-initializer.md @@ -0,0 +1,291 @@ +# Add an Array Initializer with Access to Uninitialized Storage + +* Proposal: [SE-NNNN](NNNN-array-uninitialized-initializer.md) +* Author: [Nate Cook](https://github.com/natecook1000) +* Review Manager: TBD +* Status: **Awaiting review** +* Previous Proposal: [SE-0223](https://github.com/apple/swift-evolution/blob/master/proposals/0223-array-uninitialized-initializer.md) +* Implementation: [apple/swift#23134](https://github.com/apple/swift/pull/23134) +* Bug: [SR-3087](https://bugs.swift.org/browse/SR-3087) + +## Introduction + +This proposal suggests a new initializer for `Array` and `ContiguousArray` +that provides access to an array's uninitialized storage buffer. + +Swift-evolution thread: [https://forums.swift.org/t/array-initializer-with-access-to-uninitialized-buffer/13689](https://forums.swift.org/t/array-initializer-with-access-to-uninitialized-buffer/13689) + +## Motivation + +Some collection operations require working on a fixed-size buffer of uninitialized memory. +For example, one O(*n*) algorithm for performing a stable partition of an array is as follows: + +1. Create a new array the same size as the original array. +2. Iterate over the original array, + copying matching elements to the beginning of the new array + and non-matching elements to the end. +3. When finished iterating, reverse the slice of non-matching elements. + +Unfortunately, the standard library provides no way to create an array of a +particular size without initializing every element. Even if we +avoid initialization by manually allocating the memory using an +`UnsafeMutableBufferPointer`, there's no way to convert that buffer into an +array without copying the contents. There simply isn't a way to implement this +particular algorithm with maximum efficiency in Swift. + +We also see this limitation when working with C APIs that fill a buffer with an +unknown number of elements and return the count. The workarounds are the same +as above: either initialize an array before passing it or copy the elements +from an unsafe mutable buffer into an array after calling. + +## Proposed solution + +Add a new `Array` initializer that lets a program work with an uninitialized +buffer. + +The new initializer takes a closure that operates on an +`UnsafeMutableBufferPointer` and an `inout` count of initialized elements. This +closure has access to the uninitialized contents of the newly created array's +storage, and must set the intialized count of the array before exiting. + +```swift +var myArray = Array(unsafeUninitializedCapacity: 10) { buffer, initializedCount in + for x in 1..<5 { + buffer[x] = x + } + buffer[0] = 10 + initializedCount = 5 +} +// myArray == [10, 1, 2, 3, 4] +``` + +With this new initializer, it's possible to implement the stable partition +as an extension to the `Collection` protocol, without any unnecessary copies: + +```swift +func stablyPartitioned(by belongsInFirstPartition: (Element) throws -> Bool) rethrows -> [Element] { + return try Array(unsafeUninitializedCapacity: count) { + buffer, initializedCount in + var low = buffer.baseAddress! + var high = low + buffer.count + do { + for element in self { + if try belongsInFirstPartition(element) { + low.initialize(to: element) + low += 1 + } else { + high -= 1 + high.initialize(to: element) + } + } + + let highIndex = high - buffer.baseAddress! + buffer[highIndex...].reverse() + initializedCount = buffer.count + } catch { + let lowCount = low - buffer.baseAddress! + let highCount = (buffer.baseAddress! + buffer.count) - high + buffer.baseAddress!.deinitialize(count: lowCount) + high.deinitialize(count: highCount) + throw error + } + } +} +``` + +This also facilitates efficient interfacing with C functions. For example, +suppose you wanted to wrap the function `vDSP_vsadd` in a Swift function that +returns the result as an array. This function requires you give it an unsafe +buffer into which it writes results. This is easy to do with an array, but you +would have to initialize the array with zeroes first. With a function like +`vDSP_vsadd`, this unnecessary zeroing out would eat into the slight speed edge +that the function gives you, defeating the point. This can be neatly solved +by using the proposed initializer: + +```swift +extension Array where Element == Float { + func dspAdd(scalar: Float) -> [Float] { + let n = self.count + return self.withUnsafeBufferPointer { buf in + var scalar = scalar + return Array(unsafeUninitializedCapacity: n) { rbuf, count in + vDSP_vsadd(buf.baseAddress!, 1, &scalar, rbuf.baseAddress!, 1, UInt(n)) + count = n + } + } + } +} +``` + +## Detailed design + +The new initializer is added to both `Array` and `ContiguousArray`. + +```swift +/// Creates an array with the specified capacity, then calls the given closure +/// with a buffer covering the array's uninitialized memory. +/// +/// The closure must set its second parameter to a number `c`, the number +/// of elements that are initialized. The memory in the range `buffer[0.., + _ initializedCount: inout Int + ) throws -> Void +) rethrows +``` + +### Specifying a capacity + +The initializer takes the specific capacity that a user wants to work with as a +parameter. The buffer passed to the closure has a count that is exactly the +same as the specified capacity, even if the ultimate capacity of the new array is larger. + +### Guarantees after throwing + +If the closure parameter to the initializer throws, the `initializedCount` +value at the time an error is thrown is assumed to be correct. This means that +a user who needs to throw from inside the closure has one of two options. +Before throwing, they must: + +1. deinitialize any newly initialized instances, or +2. update `initializedCount` to the correct count. + +In either case, the postconditions that `buffer[0.., + _ initializedCount: inout Int + ) throws -> Void + ) rethrows { + var buffer = UnsafeMutableBufferPointer + .allocate(capacity: unsafeUninitializedCapacity) + defer { buffer.deallocate() } + + var count = 0 + do { + try initializer(&buffer, &count) + } catch { + buffer.baseAddress!.deinitialize(count: count) + throw error + } + self = Array(buffer[0.. Date: Mon, 11 Mar 2019 09:37:16 -0700 Subject: [PATCH 1024/4563] Kick off review for SE-0245. --- ...itializer.md => 0245-array-uninitialized-initializer.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-array-uninitialized-initializer.md => 0245-array-uninitialized-initializer.md} (98%) diff --git a/proposals/NNNN-array-uninitialized-initializer.md b/proposals/0245-array-uninitialized-initializer.md similarity index 98% rename from proposals/NNNN-array-uninitialized-initializer.md rename to proposals/0245-array-uninitialized-initializer.md index 0d218f5440..afccc1eb0c 100644 --- a/proposals/NNNN-array-uninitialized-initializer.md +++ b/proposals/0245-array-uninitialized-initializer.md @@ -1,9 +1,9 @@ # Add an Array Initializer with Access to Uninitialized Storage -* Proposal: [SE-NNNN](NNNN-array-uninitialized-initializer.md) +* Proposal: [SE-0245](0245-array-uninitialized-initializer.md) * Author: [Nate Cook](https://github.com/natecook1000) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Status: **Active review (March 11 - 18, 2019)** * Previous Proposal: [SE-0223](https://github.com/apple/swift-evolution/blob/master/proposals/0223-array-uninitialized-initializer.md) * Implementation: [apple/swift#23134](https://github.com/apple/swift/pull/23134) * Bug: [SR-3087](https://bugs.swift.org/browse/SR-3087) From d08c5526ac7f162e6cd4576bcdd577d7a7e8b7f8 Mon Sep 17 00:00:00 2001 From: Alejandro Date: Mon, 11 Mar 2019 15:52:35 -0500 Subject: [PATCH 1025/4563] Illustrate the memberwise init & tuple restriction (#1000) --- proposals/0242-default-values-memberwise.md | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/proposals/0242-default-values-memberwise.md b/proposals/0242-default-values-memberwise.md index b9e562e2c7..26c4f90081 100644 --- a/proposals/0242-default-values-memberwise.md +++ b/proposals/0242-default-values-memberwise.md @@ -68,12 +68,48 @@ I propose simply doing the obvious and synthesizing default values for propertie struct Dog { var age: Int = 0 var name: String + + // The generated memberwise init: + init(age: Int = 0, name: String) } // This now works let sparky = Dog(name: "Sparky") // Dog(age: 0, name: "Sparky") ``` +The following example displays the memberwise initializer being produced by the compiler with a combination of variables with default values. + +```swift +struct Alphabet { + var a: Int = 97 + let b: String + var c: String = "c" + let d: Bool = true + var e: Double = Double.random(in: 0 ... .pi) + + // The generated memberwise init: + init( + a: Int = 97, + b: String, + c: String = "c", + e: Double = Double.random(in: 0 ... .pi) + ) +} +``` + +Notice the `d` variable does not get an entry in the memberwise initializer because it is a constant whose value is already assigned. This behavior already exists with the current initializer. + +In the case where multiple variables are being initialized together, we cannot generate a default value for them in the memberwise initializer. For example: + +```swift +struct Person { + var (firstName, lastName) = ("First", "Last") + + // The generated memberwise init: + init(firstName: String, lastName: String) +} +``` + ## Detailed design This change does not alter the requirements needed to synthesize the memberwise initializer, but rather if we can synthesize the memberwise initializer, also synthesize default values for properties with default initializers. Note that we can only synthesize values for *variables* that have declared default initializers and not *constants*. From de0a6b5c56a9290fe46f2fa471a80cb7a7da29bb Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Mon, 11 Mar 2019 13:59:26 -0700 Subject: [PATCH 1026/4563] Accept SE-0242 --- proposals/0242-default-values-memberwise.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0242-default-values-memberwise.md b/proposals/0242-default-values-memberwise.md index 26c4f90081..252b1877c2 100644 --- a/proposals/0242-default-values-memberwise.md +++ b/proposals/0242-default-values-memberwise.md @@ -3,7 +3,8 @@ * Proposal: [SE-0242](0242-default-values-memberwise.md) * Author: [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active review (February 18 - February 26, 2019)** +* Status: **Accepted** +* Decision Notes: [Rationale](https://forums.swift.org/t/se-0242-synthesize-default-values-for-the-memberwise-initializer/20618/98) * Implementation: [apple/swift#19743](https://github.com/apple/swift/pull/19743) ## Introduction From cb7e59177d55fe7383bc8af0fc8345e57225ebde Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Mon, 11 Mar 2019 17:20:27 -0400 Subject: [PATCH 1027/4563] "Mathable" (#992) * Draft mathable proposal. * Update proposals/0000-mathable.md Co-Authored-By: stephentyrone * Update proposals/0000-mathable.md Co-Authored-By: stephentyrone * More detail. * Update 0000-mathable.md Clean up formatting * Updated function list. * Update 0000-mathable.md Updated to match implementation. * Update 0000-mathable.md Typos. * Update 0000-mathable.md * Update 0000-mathable.md * Update 0000-mathable.md * Update 0000-mathable.md * Update 0000-mathable.md --- proposals/0000-mathable.md | 408 +++++++++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 proposals/0000-mathable.md diff --git a/proposals/0000-mathable.md b/proposals/0000-mathable.md new file mode 100644 index 0000000000..86cc42b6a2 --- /dev/null +++ b/proposals/0000-mathable.md @@ -0,0 +1,408 @@ +# Generic Math(s) Functions + +* Proposal: [SE-NNNN](NNNN-mathable.md) +* Author: [Stephen Canon](https://github.com/stephentyrone) +* Review Manager: TBD +* Status: **Awaiting Review** +* Implementation: [apple/swift#23140](https://github.com/apple/swift/pull/23140) + +## Introduction + +This proposal introduces two new protocols to the standard library: `ElementaryFunctions` +and `Real`. These protocols combine to provide "basic math functions" in generic contexts +for floating-point and SIMD types, and provide a path to extend that functionality to +planned complex types in the future. + +[Swift Evolution Pitch thread](https://forums.swift.org/t/generic-math-functions/21059) + +## Motivation + +`BinaryFloatingPoint` (and the protocols it refines) provides a powerful set of +abstractions for writing numerical code, but it does not include the transcendental +operations defined by the C math library, which are instead imported by the platform +overlay as a set of overloaded concrete free functions. + +There are two deficiencies with the current approach. First, *what* you need to import +to get this functions varies depending on platform, forcing the familiar but awkward +`#if` dance: +```swift +#if canImport(Darwin) +import Darwin +#elseif canImport(GlibC) +... +``` +This shouldn't be required for functionality that is intended to be available on all +platforms. + +Second, these bindings are overloaded for the various concrete `BinaryFloatingPoint` +types, but there's no way to use them generically. Suppose we want to implement the +"sigmoid" function generically: +```swift +func sigmoid(_ x: T) -> T where T: FloatingPoint { + return 1/(1 + exp(-x)) +} +``` +This doesn't work, because `exp` is not available on the `FloatingPoint` protocol. +Currently, you might work around this limitation by doing something like: +```swift +func sigmoid(_ x: T) -> T where T: FloatingPoint { + return 1/(1 + T(exp(-Double(x)))) +} +``` +but that's messy, inefficient if T is less precise than Double, and inaccurate if +T is more precise than Double. We can and should do better in Swift. + +With the changes in this proposal, the full implementation would become: +```swift +import Math + +func sigmoid(_ x: T) -> T where T: Real { + return 1/(1 + exp(-x)) +} +``` + +## Proposed solution + +There are four pieces of this proposal: first, we introduce the protocol +`ElementaryFunctions`: +```swift +public protocol ElementaryFunctions { + associatedtype Math: ElementaryFunctionHooks where Math.Value == Self +} +``` +The associatedtype provides implementation hooks for the elementary functions +so that they are available as "namespaced" static functions like: +```swift +(swift) Float.Math.exp(1) +// r0 : Float = 2.7182817 +``` +All of the standard library `FloatingPoint` types conform to +`ElementaryFunctions`; SIMD types conform conditionally if their `Scalar` +type conforms, and a planned `Complex` type would also conform. + +The second piece of the proposal is the protocol `Real`: +``` +public protocol Real: FloatingPoint, ElementaryFunctions { } +``` +This protocol does not add much API surface, but it is what most users will +write generic code against. The benefit of this protocol is that it allows +us to avoid multiple constraints for most simple generic functions, and to +adopt a clearer naming scheme for the protocol that most users see, while +also giving `ElementaryFunctions` a more precise name, suitable for +sophisticated uses. + +The third piece of the proposal is the `Math` module. This module sits next +to the standard library, and provides generic free function implementations +of math operations. Unlike the previous two additions, the availability of +these functions is gated on Swift 5.1. +```swift +(swift) import Math +(swift) exp(1.0) +// r0 : Float = 2.7182817 +``` + +Finally, we will update the platform imports to obsolete existing functions +covered by the new free functions in the `Math` module, and also remove the +imports of the suffixed functions (which were actually never +intended to be available in Swift). The Platform module will re-export the +Math module, which allows most source code to migrate without any changes +necessary. Updates will only be necessary with functions like `atan2(y: x:)` +where we are adding argument labels or `logGamma( )` where we have new +function names. In these cases we will deprecate the old functions instead +of obsoleting them to allow users time to migrate. + +## Detailed design + +The full API provided by `ElementaryFunctions` is as follows: +```swift +/// The square root of `x`. +/// +/// For real types, if `x` is negative the result is `.nan`. For complex +/// types there is a branch cut on the negative real axis. +static func sqrt(_ x: Value) -> Value + +/// The cosine of `x`, interpreted as an angle in degrees. +static func cos(_ x: Value) -> Value + +/// The sine of `x`, interpreted as an angle in degrees. +static func sin(_ x: Value) -> Value + +/// The tangent of `x`, interpreted as an angle in degrees. +static func tan(_ x: Value) -> Value + +/// The inverse cosine of `x` in degrees. +static func acos(_ x: Value) -> Value + +/// The inverse sine of `x` in degrees. +static func asin(_ x: Value) -> Value + +/// The inverse tangent of `x` in degrees. +/// +/// See also atan2(y:x:). +static func atan(_ x: Value) -> Value + +/// The hyperbolic cosine of `x`. +static func cosh(_ x: Value) -> Value + +/// The hyperbolic sine of `x`. +static func sinh(_ x: Value) -> Value + +/// The hyperbolic tangent of `x`. +static func tanh(_ x: Value) -> Value + +/// The inverse hyperbolic cosine of `x`. +static func acosh(_ x: Value) -> Value + +/// The inverse hyperbolic sine of `x`. +static func asinh(_ x: Value) -> Value + +/// The inverse hyperbolic tangent of `x`. +static func atanh(_ x: Value) -> Value + +/// The exponential function applied to `x`, or `e**x`. +static func exp(_ x: Value) -> Value + +/// Two raised to to power `x`. +static func exp2(_ x: Value) -> Value + +/// Ten raised to to power `x`. +static func exp10(_ x: Value) -> Value + +/// `exp(x) - 1` evaluated so as to preserve accuracy close to zero. +static func expm1(_ x: Value) -> Value + +/// The natural logarithm of `x`. +static func log(_ x: Value) -> Value + +/// The base-two logarithm of `x`. +static func log2(_ x: Value) -> Value + +/// The base-ten logarithm of `x`. +static func log10(_ x: Value) -> Value + +/// `log(1 + x)` evaluated so as to preserve accuracy close to zero. +static func log1p(_ x: Value) -> Value + +/// The error function evaluated at `x`. +static func erf(_ x: Value) -> Value + +/// The complimentary error function evaluated at `x`. +static func erfc(_ x: Value) -> Value + +/// The gamma function evaluated at `x`. +/// +/// For integral `x`, `gamma(x)` is `(x-1)` factorial. +static func gamma(_ x: Value) -> Value + +/// `x**y` interpreted as `exp(y * log(x))` +/// +/// For real types, if `x` is negative the result is NaN, even if `y` has +/// an integral value. For complex types, there is a branch cut on the +/// negative real axis. +static func pow(_ x: Value, _ y: Value) -> Value + +/// `x` raised to the `n`th power. +/// +/// The product of `n` copies of `x`. +static func pow(_ x: Value, _ n: Int) -> Value + +/// The `n`th root of `x`. +/// +/// For real types, if `x` is negative and `n` is even, the result is NaN. +/// For complex types, there is a branch cut along the negative real axis. +static func root(_ x: Value, _ n: Int) -> Value + +/// `atan(y/x)` with quadrant fixup. +/// +/// For real numbers `y` and `x`, there is an infinite family of angles +/// whose tangent is `y/x`. `atan2` selects the representative that is the +/// angle between the vector `(x, y)` and the real axis in the range [-π, π]. +/// +/// For complex numbers `y` and `x`, this function is simply defined by +/// `atan(y/x)`, with the branch cuts implied by that defintion. +static func atan2(y: Value, x: Value) -> Value + +/// `log(gamma(x))` computed without undue overflow. +/// +/// For real types, `log(abs(gamma(x)))` is returned, and the sign of +/// `gamma(x)` is available via `signGamma(x)`. +/// +/// For complex types, `log(gamma(x))` is returned, and the `signGamma` +/// function does not exist. +static func logGamma(_ x: Value) -> Value +``` +These functions directly follow the math library names used in most other +languages, as there is not a good reason to break with existing precedent. +The changes worth noting are as follows: +- `exp10` does not exist in most C math libraries. It is a generally useful +function, corresponding to `log10`. We'll fall back on implementing it as +`pow(10, x)` on platforms that don't have it in the system library. +- There are *two* functions named `pow` with different signatures. One +implements the IEEE 754 `powr` function (nan if `x` is negative), the other +restricts the exponent to have type `Int`, and implements the IEEE 754 `pown` +function. +- The function `root` does not exist in most math libraries; it computes the +nth root of x. For now this is implemented in terms of `pow`, but we may +adopt other implementations for better speed or accuracy in the future. +- Argument labels have been added to `atan2(y:x:)`. This is the only math.h +function whose argument order regularly causes bugs, so it would be good +to clarify here. +- `logGamma` is introduced instead of the existing `lgamma`, and returns a +single value instead of a tuple. The sign is still available for real types +via a new `signGamma` function, but requires a separate function call. The +motivation for this approach is two-fold: first, the more common use case is +to want only the first value, so returning a tuple creates noise: + + let (result, _) = lgamma(x) + + Second, there's an outstanding bug that results from the C interfaces being +re-exported in Swift where `lgamma` is ambiguous; it can be either the +platform shim returning `(T, Int)`, or the C library function returning +`Double`; we want to deprecate the first and make the second unavailable. +Simulataneously introducing yet another function with the same name would +create a bit of a mess. + +### Future expansion +The following functions recommended by IEEE 754 are not provided at this point +(because implementations are not widely available), but are planned for future expansion, +possibly with implementation directly in Swift: `cospi`, `sinpi`, `tanpi`, `acospi`, `asinpi, +atanpi`, `exp2m1`, `exp10m1`, `log2p1`, `log10p1`, `compound` (these are the names +used by IEEE 754; Swift can use different names if we like). + +### Functions not defined on Mathable +The following functions are exported by , but will not be defined on Mathable: +`frexp`, `ilogb`, `ldexp`, `logb`, `modf`, `scalbn`, `scalbln`, `fabs`, `hypot`, `ceil`, +`floor`, `nearbyint`, `rint`, `lrint`, `llrint`, `round`, `lround`, `llround`, `trunc`, `fmod`, +`remainder`, `remquo`, `copysign`, `nan`, `nextafter`, `nexttoward`, `fdim`, `fmin`, `fmax`, +`fma`. + +Most of these are not defined on Mathable because they are inherently bound to the +semantics of `FloatingPoint` or `BinaryFloatingPoint`, and so cannot be defined for +types such as Complex or Decimal. Equivalents to many of them are already defined on +`[Binary]FloatingPoint` anyway--in those cases free functions are defined by +the Math module, but will be generic over `FloatingPoint` or `BinaryFloatingPoint`: +```swift +@available(swift, introduced: 5.1) +@_alwaysEmitIntoClient +public func ceil(_ x: T) -> T where T: FloatingPoint { + return x.rounded(.up) +} + +@available(swift, introduced: 5.1) +@_alwaysEmitIntoClient +public func floor(_ x: T) -> T where T: FloatingPoint { + return x.rounded(.down) +} + +@available(swift, introduced: 5.1) +@_alwaysEmitIntoClient +public func round(_ x: T) -> T where T: FloatingPoint { + return x.rounded() +} + +@available(swift, introduced: 5.1) +@_alwaysEmitIntoClient +public func trunc(_ x: T) -> T where T: FloatingPoint { + return x.rounded(.towardZero) +} + +@available(swift, introduced: 5.1) +@_alwaysEmitIntoClient +public func fma(_ x: T, _ y: T, _ z: T) -> T where T: FloatingPoint { + return z.addingProduct(x, y) +} + +@available(swift, introduced: 5.1) +@_alwaysEmitIntoClient +public func remainder(_ x: T, _ y: T) -> T where T: FloatingPoint { + return x.remainder(dividingBy: y) +} + +@available(swift, introduced: 5.1) +@_alwaysEmitIntoClient +public func fmod(_ x: T, _ y: T) -> T where T: FloatingPoint { + return x.truncatingRemainder(dividingBy: y) +} +``` +These definitions replace the definitions existing in the platform module. + +A few of the other functions (`nearbyint`, `rint`) are fundamentally tied to the +C language notion of dynamic floating-point rounding-modes, which is not modeled +by Swift (and which we do not have plans to support--even if Swift adds rounding-mode +control, we should avoid the C fenv model). These are deprecated. + +The remainder will not be moved into the Math module at this point, as they can +be written more naturally in terms of the `FloatingPoint` API. We intend to +deprecate them. + +### Future expansion + +## Source compatibility +This is an additive change, but it entails some changes for platform modules; the existing +platform implementations provided by the Darwin or GLibc should be deprecated and +made to redirect people to the new operations. + +## Effect on ABI stability +For the standard library, this is an additive change. We'll need to continue to support the +old platform hooks to provide binary stability, but will mark them deprecated or obsoleted. + +## Effect on API resilience +This is an additive change. + +## Alternatives considered +1. The name `ElementaryFunctions` is a marked improvement on the earlier `Mathable`, but +is still imperfect. It's ever so slightly a lie (Gamma is not considered elementary), +but the other reasonable choice `Transcendental` would also be a lie (square root is not +transcendental). As discussed above, the introduction of the `Real` protocol mostly +renders this issue moot; most code will be constrained to that instead. + +2. The names of these functions are strongly conserved across languages, but they are +not universal; we *could* consider more verbose names inline with Swift usual patterns. +`sine`, `cosine`, `inverseTangent`, etc. This holds some appeal especially for the more +niche functions (like `expm1`), but the weight of common practice is pretty strong here; +almost all languages use essentially the same names for these operations. Another +alternative would be to break these up into `TrigonometricFunctions`, +`HyperbolicFunctions`, `ExponentialFunctions`, etc, but I don't think that actually +buys us very much. + +3. We may also want to add `log(_ base: T, _ x: T) -> T` at some future point as a +supplement to the existing `log`, `log2`, and `log10` functions. Python and Julia both +provide a similar interface. Doing this correctly requires a building block that the C math +library doesn't provide (an extra-precise `log` or `log2` that returns a head-tail +representation of the result); without this building block rounding errors creep in even for +exact cases: + + >>> from math import log + >>> log(3**20, 3) + 19.999999999999996 + + Julia includes a warning about this in their documentation that basically says "Use log2 +or log10 instead if base is 2 or 10". We could take that approach, but base 2 and 10 +cover 99% of uses, so I would rather wait to provide this function until we have time to +do it correctly. + +4. We could spell `log` (the natural logarithm) `ln`. This would resolve some ambiguity for +users with a background in certain fields, at the cost of diverging from the vast majority +of programming languages. Rust and Kotlin do spell it this way, so we wouldn't be completely +alone. It would also avoid using a function name that potentially conflicts (visually or +syntactically) with an obvious name for logging facilities. + +5. We could put the free functions into the standard library instead of a separate module. +Having them in a separate module helps avoid adding stuff to the global namespace +unless you're actually using it, which is generally nice, and the precedent from other +languages is pretty strong here: `#include `, `import math`, etc. Having the +implementation hooks defined in the standard library makes them available in modules that +only need them in a few places or want to use them in inlinable functions but don't want +to have them in the global namespace or re-export them. + +6. We could define an operator like `^` or `**` for one or both definitions of `pow`. I +have opted to keep new operators out of this proposal, in the interests of focusing on +the functions and their implementation hooks. I would consider such an operator to be an +additive change to be considered in a separate proposal. + +7. Add the constants `pi` and `e` to `T.Math`. There's a bit of a question about how to +handle these with hypothetical arbitrary-precision types, but that's not a great reason +to keep them out for the concrete types we already have. Plus we already have `pi` on +FloatingPoint, so someone implementing such a type already needs to make a decision about +how to handle it. There's a second question of how to handle these with `Complex` or +`SIMD` types; one solution would be to only define them for types conforming to `Real`. From e54cada0415f5ca5983d1639939626ce78d42f79 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Mon, 11 Mar 2019 14:22:12 -0700 Subject: [PATCH 1028/4563] Kick off SE-0246 --- proposals/{0000-mathable.md => 0246-mathable.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{0000-mathable.md => 0246-mathable.md} (99%) diff --git a/proposals/0000-mathable.md b/proposals/0246-mathable.md similarity index 99% rename from proposals/0000-mathable.md rename to proposals/0246-mathable.md index 86cc42b6a2..47fb7b425a 100644 --- a/proposals/0000-mathable.md +++ b/proposals/0246-mathable.md @@ -1,9 +1,9 @@ # Generic Math(s) Functions -* Proposal: [SE-NNNN](NNNN-mathable.md) +* Proposal: [SE-0246](0246-mathable.md) * Author: [Stephen Canon](https://github.com/stephentyrone) -* Review Manager: TBD -* Status: **Awaiting Review** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (March 11 - 20, 2019)** * Implementation: [apple/swift#23140](https://github.com/apple/swift/pull/23140) ## Introduction From f3de7690271b46e3e80920d444baf5eeb077c0c2 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Mon, 11 Mar 2019 17:56:55 -0400 Subject: [PATCH 1029/4563] Update 0246-mathable.md s/degrees/radians/ --- proposals/0246-mathable.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/0246-mathable.md b/proposals/0246-mathable.md index 47fb7b425a..735a9d73d5 100644 --- a/proposals/0246-mathable.md +++ b/proposals/0246-mathable.md @@ -121,22 +121,22 @@ The full API provided by `ElementaryFunctions` is as follows: /// types there is a branch cut on the negative real axis. static func sqrt(_ x: Value) -> Value -/// The cosine of `x`, interpreted as an angle in degrees. +/// The cosine of `x`, interpreted as an angle in radians. static func cos(_ x: Value) -> Value -/// The sine of `x`, interpreted as an angle in degrees. +/// The sine of `x`, interpreted as an angle in radians. static func sin(_ x: Value) -> Value -/// The tangent of `x`, interpreted as an angle in degrees. +/// The tangent of `x`, interpreted as an angle in radians. static func tan(_ x: Value) -> Value -/// The inverse cosine of `x` in degrees. +/// The inverse cosine of `x` in radians. static func acos(_ x: Value) -> Value -/// The inverse sine of `x` in degrees. +/// The inverse sine of `x` in radians. static func asin(_ x: Value) -> Value -/// The inverse tangent of `x` in degrees. +/// The inverse tangent of `x` in radians. /// /// See also atan2(y:x:). static func atan(_ x: Value) -> Value From d6ff95cba23f8cb61d7c3e97eb51e1f485502bbd Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Mon, 11 Mar 2019 16:42:22 -0700 Subject: [PATCH 1030/4563] Contiguous Strings (#996) * Add Contiguous Strings proposal * Remove attributes from description; add more alternatives considered * Add most-viable alternative --- proposals/nnnn-contiguous-strings.md | 241 +++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 proposals/nnnn-contiguous-strings.md diff --git a/proposals/nnnn-contiguous-strings.md b/proposals/nnnn-contiguous-strings.md new file mode 100644 index 0000000000..60536c803c --- /dev/null +++ b/proposals/nnnn-contiguous-strings.md @@ -0,0 +1,241 @@ +# Contiguous Strings + +* Proposal: SE-NNNN +* Authors: [Michael Ilseman](https://github.com/milseman) +* Review Manager: TBD +* Status: **Implementation In Progress** +* Implementation: [apple/swift#23051](https://github.com/apple/swift/pull/23051) +* Bugs: [SR-6475](https://bugs.swift.org/browse/SR-6475) + +## Introduction + +One of the most common API requests from performance-minded users of string is a way to get direct access to the raw underlying code units. Now that [Swift 5 uses UTF-8](https://forums.swift.org/t/string-s-abi-and-utf-8/17676) for its preferred encoding, we can provide this. + +“Contiguous strings” are strings that are capable of providing a pointer and length to [validly encoded](https://en.wikipedia.org/wiki/UTF-8#Invalid_byte_sequences) UTF-8 contents in constant time. Contiguous strings include: + +* All native Swift string forms +* Lazily bridged Cocoa strings that provide a pointer to contiguous ASCII +* Any “shared string” concept we may add in the future + +Noncontiguous strings include: + +* Lazily-bridged Cocoa strings that don’t provide a pointer to contiguous ASCII (even if they do have contiguous UTF-16 code units) +* Any “foreign string” concept we may add in the future + +Contiguous strings are heavily optimized and more efficient to process than noncontiguous strings. However, noncontiguous string contents don’t have to be copied when imported into Swift, which is profitable for strings which may never be read from, such as imported NSString dictionary keys. + +Swift-evolution thread: TBD + +## Motivation + +In Swift 5.0, `String.UTF8View` supports `withContiguousStorageIfAvailable()`, which succeeds on contiguous strings but returns `nil` on noncontiguous strings. While it’s nice to run a fast algorithm when you can, users are left in the dark if this doesn’t succeed. Even if it’s known to probably succeed, guarding against `nil` and having a valid fall-back path is not ergonomic. + +## Proposed solution + +We propose adding to String and Substring: + +* A way to query if a string is contiguous +* A way to force a string to be contiguous +* A way to run a closure over the raw UTF-8 contents of a string + +(For rationale on why StringProtocol was excluded, see “What about StringProtocol?” in Alternatives Considered) + +## Detailed design + +```swift +extension String { + /// Returns whether this string is capable of providing access to + /// validly-encoded UTF-8 contents in contiguous memory in O(1) time. + /// + /// Contiguous strings always operate in O(1) time for withUTF8 and always + /// give a result for String.UTF8View.withContiguousStorageIfAvailable. + /// Contiguous strings also benefit from fast-paths and better optimizations. + /// + public var isContiguous: Bool { get } + + /// If this string is not contiguous, make it so. If this mutates the string, + /// it will invalidate any pre-existing indices. + /// + /// Complexity: O(n) if non-contiguous, O(1) if already contiguous + /// + public mutating func makeContiguous() + + /// Runs `body` over the content of this string in contiguous memory. If this + /// string is not contiguous, this will first make it contiguous, which will + /// also speed up subsequent access. If this mutates the string, + /// it will invalidate any pre-existing indices. + /// + /// Note that it is unsafe to escape the pointer provided to `body`. For + /// example, strings of up to 15 UTF-8 code units in length may be represented + /// in a small-string representation, and thus will be spilled into + /// temporary stack space which is invalid after `withUTF8` finishes + /// execution. + /// + /// Complexity: O(n) if non-contiguous, O(1) if already contiguous + /// + public mutating func withUTF8( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R +} + +// Contiguous UTF-8 strings +extension Substring { + /// Returns whether this string is capable of providing access to + /// validly-encoded UTF-8 contents in contiguous memory in O(1) time. + /// + /// Contiguous strings always operate in O(1) time for withUTF8 and always + /// give a result for String.UTF8View.withContiguousStorageIfAvailable. + /// Contiguous strings also benefit from fast-paths and better optimizations. + /// + public var isContiguous: Bool { get } + + /// If this string is not contiguous, make it so. If this mutates the + /// substring, it will invalidate any pre-existing indices. + /// + /// Complexity: O(n) if non-contiguous, O(1) if already contiguous + /// + public mutating func makeContiguous() + + /// Runs `body` over the content of this substring in contiguous memory. If + /// this substring is not contiguous, this will first make it contiguous, + /// which will also speed up subsequent access. If this mutates the substring, + /// it will invalidate any pre-existing indices. + /// + /// Note that it is unsafe to escape the pointer provided to `body`. For + /// example, strings of up to 15 UTF-8 code units in length may be represented + /// in a small-string representation, and thus will be spilled into + /// temporary stack space which is invalid after `withUTF8` finishes + /// execution. + /// + /// Complexity: O(n) if non-contiguous, O(1) if already contiguous + /// + public mutating func withUTF8( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R +} +``` + +(For rationale as to why `withUTF8` is marked `mutating`, see “Wait, why is `withUTF8` marked as `mutating`?” in Alternatives Considered.) + +## Source compatibility + +All changes are additive. + + +## Effect on ABI stability + +All changes are additive. ABI-relevant attributes are provided in “Detailed design”. + + +## Effect on API resilience + +The APIs for String and Substring have their implementations exposed, as they are expressed in terms of assumptions and mechanisms already (non-resiliently) present in Swift 5.0. + + +## Alternatives considered + +### Wait, why is `withUTF8` marked as `mutating`? + +If the string is noncontiguous, something has to happen to get contiguous UTF-8 contents in memory. We have 3 basic options here: + +##### 1. Copy into a new heap allocation, run the closure, then throw it away + +This approach takes inspiration from Array’s `withUnsafeBufferPointer`, which runs directly on its contents if it can, otherwise it makes a temporary copy to run over. However, unlike Array, noncontiguous strings are much more common and there are no type-level guarantees of contiguity. For example, Array with a value-type `Element` is always contiguous, so the vast majority of performance sensitive operations over arrays (e.g. `Array`) can never trigger this allocation. + +Because String does not have Array’s guarantee, this approach would harm the ability to reason about the performance characteristics of code. This approach would also keep the string on the slow-path for access after the method is called. If the contents are worth reading, they’re worth ensuring contiguity. + +##### 2. Trap if noncontiguous + +This would be the ideal API for platforms without Cocoa interoperability (unless/until we introduce noncontiguous foreign strings, at least), as it will always succeed. However, this would be a terrible source of crashes in libraries and applications that forget to exhaustively test their code with noncontiguous strings. This would hit cross-platform packages especially hard. + +Even for libraries confined to Cocoa-interoperable platforms, whether an imported NSString is contiguous or not could change version-to-version of the OS, producing difficult to reason about bugs. + +##### 3. Fine, make it `mutating` + +The proposed compromise makes `withUTF8` mutating, forcing the contents to be bridged if noncontiguous. This has the downside of forcing such strings to be declared `var` rather than `let`, but mitigates the downsides of the other approaches. If the contents are worth reading, they’re worth ensuring contiguity. + +##### Or, introduce a separate type, `ContiguousUTF8String`, instead + +Introducing a new type would introduce an API schism and further complicate the +nature of StringProtocol. We don't consider the downsides to be worth the upside +of dropping `mutable`. + +### What about StringProtocol? + +Adding these methods to StringProtocol would allow code to be generic over String and Substring (and in the somewhat unlikely event we add new conformers, those as well). This can be done at any time, but there are some issues with doing this right now. + +When it comes to adding these methods on StringProtocol, we have two options: + +##### 1. Add it as a protocol extension + +This approach would add the functions in an extension, ideally resilient and versioned. Unfortunately, `makeContiguous()` wouldn’t be implementable, as we cannot reassign self to a concrete type, so we’d have to add some additional requirements to StringProtocol surrounding forming a new contiguous string of the same concrete type. + +##### 2. Add it as a requirement with a default implementation + +This approach would add it as a customization hook that’s dynamically dispatched to the concrete type’s real implementations. The default implementation would be resilient and versioned and trap if called; any new conformers to StringProtocol would need to be versioned and accommodated here. + +Adding new versioned-and-defaulted requirements to a protocol can be done at any point while preserving ABI stability. For now, we’re not sure it’s worth the extra witness table entries at this point. This also hits some of the pain points of option 1: any conformers to StringProtocol must satisfy `makeContiguous`’s post-condition of ensuring contiguity without changing concrete type. + +This can be added in the future. + +### Name it `isContiguousUTF8` and `makeContiguousUTF8()` + +This proposal is introducing the concept of "contiguous strings" which is +blessed as *the* fast-path in String's stable ABI for strings that can provide +UTF-8 contents in contiguous memory. If a string cannot provide UTF-8 content in +contiguous memory, it does receive these benefits, even if it happens to have +content in some other encoding in contiguous memory. + +We feel the concept of string contiguity in Swift is inherently tied to UTF-8, +and worth claiming the term "contiguous" unqualified in encoding. That being +said, this is a weakly held opinion and `isContiguousUTF8` is acceptable as +well. + +### Put this on the UTF8View + +Contiguous UTF-8 is baked into String's ABI as the fastest representation; this +is about directly exposing that fact to performance-minded users. Other views +will not have this, so we believe it makes sense to put directly on String. +UTF-8 is special for performance and contiguity concerns. + +### The Most Viable Alternative + +Here's an alternative formulation that uses an explicit "UTF8" in the name and +avoids `mutating` and index-invalidation (by using strategy \#1 above). + +```swift +extension [Sub]String { + /// + public var isContiguousUTF8: Bool { get } + + /// Produce a contiguous UTF-8 string with the same contents as `self`. + /// Returns `self` is already contiguous, otherwise returns a new copy. + /// + /// Complexity: O(1) if `self` is contiguous UTF-8, otherwise O(n) + /// + public var contiguousUTF8: [Sub]String { get } + + /// Runs `body` over the content of this string in contiguous memory. If this + /// string is not contiguous UTF-8, this will first make a contiguous copy and + /// run over that. + /// + /// Note that it is unsafe to escape the pointer provided to `body`, even if + /// `self` is contiguous UTF-8. For example, strings of up to 15 UTF-8 code + /// units in length may be represented in a small-string representation, and + /// thus will be spilled into temporary stack space which is invalid after + /// `withUTF8` returns. + /// + /// Complexity: O(1) if `self` is contiguous UTF-8, otherwise O(n) + /// + public func withUTF8( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R +} +``` + +This formulation would be preferable for situations where strings are nearly- +always contiguous UTF-8. Also, the computed property is less invasive than a +mutating method as it can be used on the right-hand-side of a let binding. + +Choosing between this and the version proposed depends on how users expect to +use it. We're eagerly awaiting feedback during the review. From 5fd2b8ebcbde16983e78f251b8a023066cff37fd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 11 Mar 2019 16:45:49 -0700 Subject: [PATCH 1031/4563] Initiate review of SE-0247: Contiguous Strings --- ...n-contiguous-strings.md => 0247-contiguous-strings.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{nnnn-contiguous-strings.md => 0247-contiguous-strings.md} (98%) diff --git a/proposals/nnnn-contiguous-strings.md b/proposals/0247-contiguous-strings.md similarity index 98% rename from proposals/nnnn-contiguous-strings.md rename to proposals/0247-contiguous-strings.md index 60536c803c..4887279cf6 100644 --- a/proposals/nnnn-contiguous-strings.md +++ b/proposals/0247-contiguous-strings.md @@ -1,9 +1,9 @@ # Contiguous Strings -* Proposal: SE-NNNN +* Proposal: SE-0247 * Authors: [Michael Ilseman](https://github.com/milseman) -* Review Manager: TBD -* Status: **Implementation In Progress** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (March 11 - 18, 2019)** * Implementation: [apple/swift#23051](https://github.com/apple/swift/pull/23051) * Bugs: [SR-6475](https://bugs.swift.org/browse/SR-6475) @@ -24,7 +24,7 @@ Noncontiguous strings include: Contiguous strings are heavily optimized and more efficient to process than noncontiguous strings. However, noncontiguous string contents don’t have to be copied when imported into Swift, which is profitable for strings which may never be read from, such as imported NSString dictionary keys. -Swift-evolution thread: TBD +Swift-evolution thread: [Pitch: Contiguous Strings](https://forums.swift.org/t/pitch-contiguous-strings/21206) ## Motivation From add1bb54abe4696bc0c3c9a67f35d3920a31eee8 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 12 Mar 2019 00:23:46 +0000 Subject: [PATCH 1032/4563] [SE-0247] Fix "missing proposal ID link" error --- proposals/0247-contiguous-strings.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0247-contiguous-strings.md b/proposals/0247-contiguous-strings.md index 4887279cf6..ca2d9fd825 100644 --- a/proposals/0247-contiguous-strings.md +++ b/proposals/0247-contiguous-strings.md @@ -1,7 +1,7 @@ # Contiguous Strings -* Proposal: SE-0247 -* Authors: [Michael Ilseman](https://github.com/milseman) +* Proposal: [SE-0247](0247-contiguous-strings.md) +* Author: [Michael Ilseman](https://github.com/milseman) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active review (March 11 - 18, 2019)** * Implementation: [apple/swift#23051](https://github.com/apple/swift/pull/23051) From 54d5e2f9be75e3e17ab9b2ef8d26a02d78a70de3 Mon Sep 17 00:00:00 2001 From: "Steve (Numerics) Canon" Date: Tue, 12 Mar 2019 01:40:09 -0400 Subject: [PATCH 1033/4563] First draft of simd additions pitch --- proposals/0000-simd-additions.md | 188 +++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 proposals/0000-simd-additions.md diff --git a/proposals/0000-simd-additions.md b/proposals/0000-simd-additions.md new file mode 100644 index 0000000000..e40822a6b5 --- /dev/null +++ b/proposals/0000-simd-additions.md @@ -0,0 +1,188 @@ +# SIMD additions + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Stephen Canon](https://github.com/stephentyrone) +* Review Manager: TBD +* Status: **Awaiting implementation** + +## Introduction + +A few teams within Apple have requested additions to the new SIMD types +and protocols to better support their use cases. In addition, there are some features +we punted out of the original review because we were up against a hard time +deadline to which we would like to give further consideration. + +## Motivation + +This is a bit of a grab bag of SIMD features, so it's hard to present a single coherent +motivation for their addition. Basically, they are features that people who are writing +a significant amount of code against the existing SIMD types have found they are +missing. I'll attempt to motivate each new API individually in the proposed solution +section below. + +## Proposed solution + +### Extending vectors +Add the following initializers: +```swift +extension SIMD3 { + /// The vector (xy.x, xy.y, z) + public init(_ xy: SIMD2, _ z: Scalar) +} + +extension SIMD4 { + /// The vector (xyz.x, xyz.y, xyz.z, w) + public init(_ xyz: SIMD3, _ w: Scalar) +} +``` +These are broadly useful in graphics contexts for working with homogeneous coordinates, +where the last component needs to be treated separately, and where you frequently want +to extend vectors by adding an additional component at API boundaries. These could +alternatively be spelled like `xy.appending(z)`; there are two reasons that I'm avoiding +that: +- I would expect `appending( )` to return the same type; here the result type is different. +- I would expect `appending( )` to be available on all SIMD types, but it breaks down +beyond `SIMD4`, because there is no `SIMD5` in the standard library. + +### Loading and storing from collections +Add the following: +```swift +extension SIMD { + /// Extracts a vector with consecutive elements taken from `collection` + /// beginning at the index `start`. + /// + /// - Precondition: `collection` contains enough elements to fill the + /// vector. + public init(_ collection: C, start: C.Index) + where C: Collection, C.Element == Scalar + + /// Replaces the elements of `collection` beginning at the index + /// `start` with the elements of this vector. + /// + /// - Precondition: `collection` has space for all elements of the + /// vector. + public func store(into collection: inout C, start: C.Index) + where C: MutableCollection, C.Element == Scalar +} +``` +These are primarily useful for working with arrays or UBP of vector data, such as are used +to marshal vertex or color data to and from the GPU. They are especially useful with +3-element vector types, where we may want to avoid the extra padding element that +`Array>` or `UnsafeBufferPointer>` would incur. + +The `init` is pretty clear; I am not really sold on the naming of `store` at this point, and +would love to hear other suggestions. If `SIMD` types conformed to `Sequence`, this would +nearly be `collection.replaceSubrange(start...start+v.count, with: v)` for +`RRC`, but we would like to enforce that this doesn't grow the collection; it should only +replace exactly `count` elements, so it's slightly different--the `RRC` interface would be +redundant. + +The intention is that these should codegen to vector loads when the elements are actually +contiguous in memory, but still work with collections that are not contiguous. Early +experimentation suggests that we get the desired codgen even with fully generic +implementations of these operations. + +### Horizontal operations +Generally in SIMD programming you try to avoid horizontal operations as much as +possible, but frequently you need to do a few of them at the end of a chain of +computations. The reductions that we most often need to perform are: +```swift +extension SIMD where Scalar: Comparable { + func min() -> Scalar + func max() -> Scalar +} + +extension SIMD where Scalar: BinaryFloatingPoint { + func sum() -> Scalar +} + +extension SIMD where Scalar: FixedWidthInteger { + func sum() -> Scalar +} +``` +One might reasonably ask why the last two are not collapsed onto `AdditiveArithmetic`. +The answer is that we want to use `&+` for reduction on integer vectors, which isn't defined +on `AdditiveArithmetic`. One might follow-up with "isn't that prone to overflow?" The +answer is yes, but it's the operation you generally want in a SIMD context; to get the +"safe" sum you widen to the double-width type, then sum that. We might reasonably use +a different name for this operation though, like `wrappingSum`, to be explicit. + +In addition, we would provide the following two reductions on Mask vectors: +```swift +/// True if any lane of the mask is true +func any(_ mask: SIMDMask) -> Bool + +/// True if every lane of the mask is true +func all(_ mask: SIMDMask) -> Bool +``` +These two are defined as free functions, because at use sites they read significantly more +clearly like `if any(x .< 0)` than, e.g. `if (x .< 0).any()`. We could consider using a +more verbose spelling like `if (x .< 0).anyIsTrue( )`, but these functions are used +quite heavily, and there's a strong preference from the teams we've been working with for +the concise free function spellings. + +We would also like to add: +```swift +extension SIMD where Scalar: Comparable { + func indexOfMinValue() -> Int + func indexOfMaxValue() -> Int +} +``` +The exact spelling of these is not super important to our would-be clients, and I'm not +really wedded to these names. I would love to get some suggestions for them. + +### Min, max, clamp +```swift +extension SIMD where Scalar: Comparable { + static func min(_ lhs: Self, _ rhs: Self) -> Self + static func max(_ lhs: Self, _ rhs: Self) -> Self + mutating func clamp(to range: ClosedRange) + func clamped(to range: ClosedRange) -> Self + mutating func clamp(lowerBound: Self, upperBound: Self) + func clamped(lowerBound: Self, upperBound: Self) -> Self +} +``` +These are all fairly simple to implement in terms of the existing +`replacing(with: where:)`, but for two factors: first, getting them right for floating-point +types is a little bit subtle, and second these are so heavily used that it makes sense to +have an actual API for them, rather than leaving each developer to implement them on their +own. + +### "Swizzles" aka "Permutes" +Early drafts of the previous proposal for SIMD had the following initializer: +```swift +init(gathering source: D, at index: I) +where D : SIMDVector, D.Element == Element, + I : SIMDIntegerVector & SIMDVectorN { + self.init() + for i in 0 ..< count { + if index[i] >= 0 && index[i] < source.count { + self[i] = source[Int(index[i])] + } + } +} +``` +it was removed from later drafts because the naming wasn't quite right, but it's also not +quite implementable with the "generic" SIMD structure that the community settled on. In +particular, we can't enforce the constraint that the index vector (`I`) has the same number +of elements as the vector type being initialized, because rather than having a +`SIMDVectorN` protocol conformance, we just have the `SIMDN` type. + +We can work around this by moving the init down to the types themselves, at the cost +of some code repetition. This is a critical operation for writing efficient SIMD code, +so we definitely want to provide it. In addition, we hope that it can eventually form the +backing implementation for arbitrary named compile-time swizzles like `v.xyxy`. + +I'm exploring a few ways to add this functionality now, but I'm interested in getting other +thoughts from the community. + +### "one" +One last requestion from internal developers is +```swift +extension SIMD where Scalar: ExpressibleByIntegerLiteral { + static var one: Self { return Self(repeating: 1) } +} +``` +This is a fairly niche feature, but gets used heavily enough that folks would really +appreciate having a short name for it. `.zero` already exists from `AdditiveArithmetic`, +which makes this seem somewhat reasonable to me. From b5bbc5ae1f53189641951acfd50870f5b886859e Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 12 Mar 2019 14:02:00 -0400 Subject: [PATCH 1034/4563] Update 0246-mathable.md s/Mathable/ElementaryFunctions/ cleanup, removed empty duplicate Future Expansion section. --- proposals/0246-mathable.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/proposals/0246-mathable.md b/proposals/0246-mathable.md index 735a9d73d5..14246c6002 100644 --- a/proposals/0246-mathable.md +++ b/proposals/0246-mathable.md @@ -269,14 +269,14 @@ possibly with implementation directly in Swift: `cospi`, `sinpi`, `tanpi`, `acos atanpi`, `exp2m1`, `exp10m1`, `log2p1`, `log10p1`, `compound` (these are the names used by IEEE 754; Swift can use different names if we like). -### Functions not defined on Mathable -The following functions are exported by , but will not be defined on Mathable: +### Functions not defined on ElementaryFunctions +The following functions are exported by , but will not be defined on ElementaryFunctions: `frexp`, `ilogb`, `ldexp`, `logb`, `modf`, `scalbn`, `scalbln`, `fabs`, `hypot`, `ceil`, `floor`, `nearbyint`, `rint`, `lrint`, `llrint`, `round`, `lround`, `llround`, `trunc`, `fmod`, `remainder`, `remquo`, `copysign`, `nan`, `nextafter`, `nexttoward`, `fdim`, `fmin`, `fmax`, `fma`. -Most of these are not defined on Mathable because they are inherently bound to the +Most of these are not defined on ElementaryFunctions because they are inherently bound to the semantics of `FloatingPoint` or `BinaryFloatingPoint`, and so cannot be defined for types such as Complex or Decimal. Equivalents to many of them are already defined on `[Binary]FloatingPoint` anyway--in those cases free functions are defined by @@ -335,8 +335,6 @@ The remainder will not be moved into the Math module at this point, as they can be written more naturally in terms of the `FloatingPoint` API. We intend to deprecate them. -### Future expansion - ## Source compatibility This is an additive change, but it entails some changes for platform modules; the existing platform implementations provided by the Darwin or GLibc should be deprecated and From 022263e1cde8b23f9ed2dbbe6a6a646e6646ad6c Mon Sep 17 00:00:00 2001 From: "Steve (Numerics) Canon" Date: Wed, 13 Mar 2019 19:30:29 -0400 Subject: [PATCH 1035/4563] Updates for the mathable proposal. --- proposals/0246-mathable.md | 93 ++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/proposals/0246-mathable.md b/proposals/0246-mathable.md index 14246c6002..a8edcaac99 100644 --- a/proposals/0246-mathable.md +++ b/proposals/0246-mathable.md @@ -5,6 +5,7 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (March 11 - 20, 2019)** * Implementation: [apple/swift#23140](https://github.com/apple/swift/pull/23140) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/b5bbc5ae1f53189641951acfd50870f5b886859e/proposals/0246-mathable.md) ## Introduction @@ -82,7 +83,9 @@ type conforms, and a planned `Complex` type would also conform. The second piece of the proposal is the protocol `Real`: ``` -public protocol Real: FloatingPoint, ElementaryFunctions { } +public protocol Real: FloatingPoint, ElementaryFunctions +where Math: RealFunctionHooks { +} ``` This protocol does not add much API surface, but it is what most users will write generic code against. The benefit of this protocol is that it allows @@ -137,8 +140,6 @@ static func acos(_ x: Value) -> Value static func asin(_ x: Value) -> Value /// The inverse tangent of `x` in radians. -/// -/// See also atan2(y:x:). static func atan(_ x: Value) -> Value /// The hyperbolic cosine of `x`. @@ -183,17 +184,6 @@ static func log10(_ x: Value) -> Value /// `log(1 + x)` evaluated so as to preserve accuracy close to zero. static func log1p(_ x: Value) -> Value -/// The error function evaluated at `x`. -static func erf(_ x: Value) -> Value - -/// The complimentary error function evaluated at `x`. -static func erfc(_ x: Value) -> Value - -/// The gamma function evaluated at `x`. -/// -/// For integral `x`, `gamma(x)` is `(x-1)` factorial. -static func gamma(_ x: Value) -> Value - /// `x**y` interpreted as `exp(y * log(x))` /// /// For real types, if `x` is negative the result is NaN, even if `y` has @@ -211,25 +201,52 @@ static func pow(_ x: Value, _ n: Int) -> Value /// For real types, if `x` is negative and `n` is even, the result is NaN. /// For complex types, there is a branch cut along the negative real axis. static func root(_ x: Value, _ n: Int) -> Value - +``` +`Real` builds on this set by adding the following additional operations on +`RealFunctionHooks` that are either difficult to implement for complex types or only make +sense for real types: +```swift /// `atan(y/x)` with quadrant fixup. /// -/// For real numbers `y` and `x`, there is an infinite family of angles -/// whose tangent is `y/x`. `atan2` selects the representative that is the -/// angle between the vector `(x, y)` and the real axis in the range [-π, π]. -/// -/// For complex numbers `y` and `x`, this function is simply defined by -/// `atan(y/x)`, with the branch cuts implied by that defintion. +/// There is an infinite family of angles whose tangent is `y/x`. `atan2` +/// selects the representative that is the angle between the vector `(x, y)` +/// and the real axis in the range [-π, π]. static func atan2(y: Value, x: Value) -> Value -/// `log(gamma(x))` computed without undue overflow. +/// The error function evaluated at `x`. +static func erf(_ x: Value) -> Value + +/// The complimentary error function evaluated at `x`. +static func erfc(_ x: Value) -> Value + +/// sqrt(x*x + y*y) computed without undue overflow or underflow. +/// +/// Returns a numerically precise result even if one or both of x*x or +/// y*y overflow or underflow. +static func hypot(_ x: Value, _ y: Value) -> Value + +/// The gamma function evaluated at `x`. /// -/// For real types, `log(abs(gamma(x)))` is returned, and the sign of -/// `gamma(x)` is available via `signGamma(x)`. +/// For integral `x`, `gamma(x)` is `(x-1)` factorial. +static func gamma(_ x: Value) -> Value + +/// `log(gamma(x))` computed without undue overflow. /// -/// For complex types, `log(gamma(x))` is returned, and the `signGamma` -/// function does not exist. +/// `log(abs(gamma(x)))` is returned. To recover the sign of `gamma(x)`, +/// use `signGamma(x)`. static func logGamma(_ x: Value) -> Value + +/// The sign of `gamma(x)`. +/// +/// This function is typically used in conjunction with `logGamma(x)`, which +/// computes `log(abs(gamma(x)))`, to recover the sign information that is +/// lost to the absolute value. +/// +/// `gamma(x)` has a simple pole at each non-positive integer and an +/// essential singularity at infinity; we arbitrarily choose to return +/// `.plus` for the sign in those cases. For all other values, `signGamma(x)` +/// is `.plus` if `x >= 0` or `trunc(x)` is odd, and `.minus` otherwise. +static func signGamma(_ x: Value) -> FloatingPointSign ``` These functions directly follow the math library names used in most other languages, as there is not a good reason to break with existing precedent. @@ -248,8 +265,8 @@ adopt other implementations for better speed or accuracy in the future. function whose argument order regularly causes bugs, so it would be good to clarify here. - `logGamma` is introduced instead of the existing `lgamma`, and returns a -single value instead of a tuple. The sign is still available for real types -via a new `signGamma` function, but requires a separate function call. The +single value instead of a tuple. The sign is still available via a new `signGamma` +function, but requires a separate function call. The motivation for this approach is two-fold: first, the more common use case is to want only the first value, so returning a tuple creates noise: @@ -271,7 +288,7 @@ used by IEEE 754; Swift can use different names if we like). ### Functions not defined on ElementaryFunctions The following functions are exported by , but will not be defined on ElementaryFunctions: -`frexp`, `ilogb`, `ldexp`, `logb`, `modf`, `scalbn`, `scalbln`, `fabs`, `hypot`, `ceil`, +`frexp`, `ilogb`, `ldexp`, `logb`, `modf`, `scalbn`, `scalbln`, `fabs`, `ceil`, `floor`, `nearbyint`, `rint`, `lrint`, `llrint`, `round`, `lround`, `llround`, `trunc`, `fmod`, `remainder`, `remquo`, `copysign`, `nan`, `nextafter`, `nexttoward`, `fdim`, `fmin`, `fmax`, `fma`. @@ -349,9 +366,7 @@ This is an additive change. ## Alternatives considered 1. The name `ElementaryFunctions` is a marked improvement on the earlier `Mathable`, but -is still imperfect. It's ever so slightly a lie (Gamma is not considered elementary), -but the other reasonable choice `Transcendental` would also be a lie (square root is not -transcendental). As discussed above, the introduction of the `Real` protocol mostly +is still imperfect. As discussed above, the introduction of the `Real` protocol mostly renders this issue moot; most code will be constrained to that instead. 2. The names of these functions are strongly conserved across languages, but they are @@ -383,7 +398,8 @@ do it correctly. users with a background in certain fields, at the cost of diverging from the vast majority of programming languages. Rust and Kotlin do spell it this way, so we wouldn't be completely alone. It would also avoid using a function name that potentially conflicts (visually or -syntactically) with an obvious name for logging facilities. +syntactically) with an obvious name for logging facilities. However, depending on font, +`ln` is easily confused with `in`, and it breaks the similarity with the other `log` functions. 5. We could put the free functions into the standard library instead of a separate module. Having them in a separate module helps avoid adding stuff to the global namespace @@ -404,3 +420,14 @@ to keep them out for the concrete types we already have. Plus we already have `p FloatingPoint, so someone implementing such a type already needs to make a decision about how to handle it. There's a second question of how to handle these with `Complex` or `SIMD` types; one solution would be to only define them for types conforming to `Real`. + +## Changes from previous revisions +1. A number of functions (`atan2`, `erf`, `erfc`, `gamma`, `logGamma`) have been moved +from `ElementaryFunctions` onto `Real`. The rationale for this is threefold: `atan2` never +made much sense for non-real arguments. Implementations of `erf`, `erfc`, `gamma` and +`logGamma` are not available on all platforms. Finally, those four functions are not actually +"elementary", so the naming is more accurate following this change. + +2. `hypot` has been added to `Real`. We would like to have a more general solution for +efficiently rescaling computations in the future, but `hypot` is a tool that people can use +today. From 54d85bb6c1e39c1a4cea169408327079bb37637d Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Wed, 13 Mar 2019 22:20:49 -0400 Subject: [PATCH 1036/4563] Key Path Expressions as Functions (#977) * Key Path Literal Function Expressions * Schedule review --- ...N-key-path-literal-function-expressions.md | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 proposals/NNNN-key-path-literal-function-expressions.md diff --git a/proposals/NNNN-key-path-literal-function-expressions.md b/proposals/NNNN-key-path-literal-function-expressions.md new file mode 100644 index 0000000000..c9f76dbbff --- /dev/null +++ b/proposals/NNNN-key-path-literal-function-expressions.md @@ -0,0 +1,150 @@ +# Key Path Expressions as Functions + +* Proposal: [SE-0247](0247-key-path-literal-function-expressions.md) +* Authors: [Stephen Celis](https://github.com/stephencelis), [Greg Titus](https://github.com/gregomni) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Scheduled (March 18 - 26, 2019)** +* Implementation: [apple/swift#19448](https://github.com/apple/swift/pull/19448) + + + +## Introduction + +This proposal introduces the ability to use the key path expression `\Root.value` wherever functions of `(Root) -> Value` are allowed. + +Swift-evolution thread: [Key Path Expressions as Functions](https://forums.swift.org/t/key-path-expressions-as-functions/19587) + +Previous discussions: + +- [Allow key path literal syntax in expressions expecting function type](https://forums.swift.org/t/allow-key-path-literal-syntax-in-expressions-expecting-function-type/16453) +- [Key path getter promotion](https://forums.swift.org/t/key-path-getter-promotion/11185) +- [[Pitch] KeyPath based map, flatMap, filter](https://forums.swift.org/t/pitch-keypath-based-map-flatmap-filter/6266) + +## Motivation + +One-off closures that traverse from a root type to a value are common in Swift. Consider the following `User` struct: + +```swift +struct User { + let email: String + let isAdmin: Bool +} +``` + +Applying `map` allows the following code to gather an array of emails from a source user array: + +```swift +users.map { $0.email } +``` + +Similarly, `filter` can collect an array of admins: + +```swift +users.filter { $0.isAdmin } +``` + +These ad hoc closures are short and sweet but Swift already has a shorter and sweeter syntax that can describe this: key paths. The Swift forum has [previously proposed](https://forums.swift.org/t/pitch-support-for-map-and-flatmap-with-smart-key-paths/6073) adding `map`, `flatMap`, and `compactMap` overloads that accept key paths as input. Popular libraries [define overloads](https://github.com/ReactiveCocoa/ReactiveSwift/search?utf8=✓&q=KeyPath&type=) of their own. Adding an overload per function, though, is a losing battle. + +## Proposed solution + +Swift should allow `\Root.value` key path expressions wherever it allows `(Root) -> Value` functions: + +```swift +users.map(\.email) + +users.filter(\.isAdmin) +``` + +## Detailed design + +As implemented in [apple/swift#19448](https://github.com/apple/swift/pull/19448), occurrences of `\Root.value` are implicitly converted to key path applications of `{ $0[keyPath: \Root.value] }` wherever `(Root) -> Value` functions are expected. For example: + +``` swift +users.map(\.email) +``` + +Is equivalent to: + +``` swift +users.map { $0[keyPath: \User.email] } +``` + +The implementation is limited to key path literal expressions (for now), which means the following is not allowed: + +``` swift +let kp = \User.email // KeyPath +users.map(kp) +``` + +> 🛑 Cannot convert value of type 'WritableKeyPath' to expected argument type '(Person) throws -> String' + +But the following is: + +``` swift +let f1: (User) -> String = \User.email +users.map(f1) + +let f2: (User) -> String = \.email +users.map(f2) + +let f3 = \User.email as (User) -> String +users.map(f3) + +let f4 = \.email as (User) -> String +users.map(f4) +``` + +Any key path expression can be used where a function of the same shape is expected. A few more examples include: + +``` swift +// Multi-segment key paths +users.map(\.email.count) + +// `self` key paths +[1, nil, 3, nil, 5].compactMap(\.self) +``` + +## Effect on source compatibility, ABI stability, and API resilience + +This is a purely additive change and has no impact. + +## Future direction + +### `@callable` + +It was suggested in [the proposal thread](https://forums.swift.org/t/key-path-expressions-as-functions/19587/4) that a future direction in Swift would be to introduce a `@callable` mechanism or `Callable` protocol as a static equivalent of `@dynamicCallable`. Functions could be treated as the existential of types that are `@callable`, and `KeyPath` could be `@callable` to adopt the same functionality as this proposal. Such a change would be backwards-compatible with this proposal and does not need to block its implementation. + +### `ExpressibleByKeyPathLiteral` protocol + +It was also suggested [in the implementation's discussion](https://github.com/apple/swift/pull/19448) that it might be appropriate to define an `ExpressibleByKeyPathLiteral` protocol, though discussion in [the proposal thread](https://forums.swift.org/t/key-path-expressions-as-functions/19587/14) questioned the limited utility of such a protocol. + +## Alternatives considered + +### `^` prefix operator + +The `^` prefix operator offers a common third party solution for many users: + +```Swift +prefix operator ^ + +prefix func ^ (keyPath: KeyPath) -> (Root) -> Value { + return { root in root[keyPath: keyPath] } +} + +users.map(^\.email) + +users.filter(^\.isAdmin) +``` + +Although handy, it is less readable and less convenient than using key path syntax alone. + +### Accept `KeyPath` instead of literal expressions + +There has been some concern expressed that accepting the literal syntax but not key paths may be confusing, though this behavior is in line with how other literals work, and the most general use case will be with literals, not key paths that are passed around. Accepting key paths directly would also be more limiting and prevent exploring the [future directions](#future-direction) of `Callable` or `ExpressibleByKeyPathLiteral` protocols. From 4cbb608fc751d113190c4c44847fe2eac71bc47a Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 13 Mar 2019 19:39:53 -0700 Subject: [PATCH 1037/4563] String Gaps and Missing APIs (#991) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * String Gaps and Missing APIs Adds proposal for addressing gaps and missing trivial String APIs. * 6 simple APIs on Unicode’s various encodings * 2 generic initializers for string indices and ranges of indices * `Substring.base`, equivalent to `Slice.base` * Make `Character.UTF8View` and `Character.UTF16View` public * Add `Unicode.Scalar.UTF8View` * Remove annotations from detailed description --- proposals/nnnn-string-gaps-missing-apis.md | 237 +++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 proposals/nnnn-string-gaps-missing-apis.md diff --git a/proposals/nnnn-string-gaps-missing-apis.md b/proposals/nnnn-string-gaps-missing-apis.md new file mode 100644 index 0000000000..0116983339 --- /dev/null +++ b/proposals/nnnn-string-gaps-missing-apis.md @@ -0,0 +1,237 @@ +# String Gaps and Missing APIs + +* Proposal: SE-NNNN +* Authors: [Michael Ilseman](https://github.com/milseman) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#22869](https://github.com/apple/swift/pull/22869/files) +* Bugs: [SR-9955](https://bugs.swift.org/browse/SR-9955) + +## Introduction + +String and related types are missing trivial and obvious functionality, much of which currently exists internally but has not been made API. We propose adding 9 new methods/properties and 3 new code unit views. + +Swift-evolution thread: TBD + +## Motivation + +These missing APIs address [commonly encountered](https://forums.swift.org/t/efficiently-retrieving-utf8-from-a-character-in-a-string/19916) gaps and [missing functionality](https://bugs.swift.org/browse/SR-9955) for users of String and its various types, often leading developers to [reinvent](https://github.com/apple/swift-nio-http2/blob/master/Sources/NIOHPACK/HPACKHeader.swift#L412) the same trivial definitions. + +## Proposed solution + +We propose: + +* 6 simple APIs on Unicode’s various encodings +* 2 generic initializers for string indices and ranges of indices +* `Substring.base`, equivalent to `Slice.base` +* Make `Character.UTF8View` and `Character.UTF16View` public +* Add `Unicode.Scalar.UTF8View` + +## Detailed design + +### 1. Unicode obvious/trivial additions + +This functionality existed internally as helpers and is generally useful (even if they’re simple) for anyone working with Unicode. + +```swift + +extension Unicode.ASCII { + /// Returns whether the given code unit represents an ASCII scalar + public static func isASCII(_ x: CodeUnit) -> Bool +} + +extension Unicode.UTF8 { + /// Returns the number of code units required to encode the given Unicode + /// scalar. + /// + /// Because a Unicode scalar value can require up to 21 bits to store its + /// value, some Unicode scalars are represented in UTF-8 by a sequence of up + /// to 4 code units. The first code unit is designated a *lead* byte and the + /// rest are *continuation* bytes. + /// + /// let anA: Unicode.Scalar = "A" + /// print(anA.value) + /// // Prints "65" + /// print(UTF8.width(anA)) + /// // Prints "1" + /// + /// let anApple: Unicode.Scalar = "🍎" + /// print(anApple.value) + /// // Prints "127822" + /// print(UTF16.width(anApple)) + /// // Prints "4" + /// + /// - Parameter x: A Unicode scalar value. + /// - Returns: The width of `x` when encoded in UTF-8, from `1` to `4`. + public static func width(_ x: Unicode.Scalar) -> Int + + /// Returns whether the given code unit represents an ASCII scalar + public static func isASCII(_ x: CodeUnit) -> Bool +} + +extension Unicode.UTF16 { + /// Returns a Boolean value indicating whether the specified code unit is a + /// high or low surrogate code unit. + public static func isSurrogate(_ x: CodeUnit) -> Bool + + /// Returns whether the given code unit represents an ASCII scalar + public static func isASCII(_ x: CodeUnit) -> Bool +} + +extension Unicode.UTF32 { + /// Returns whether the given code unit represents an ASCII scalar + public static func isASCII(_ x: CodeUnit) -> Bool +} + +``` + +### 2. Generic initializers for String.Index and Range + +Concrete versions of this exist parameterized over String, but versions generic over StringProtocol are missing. + +```swift +extension String.Index { + /// Creates an index in the given string that corresponds exactly to the + /// specified position. + /// + /// If the index passed as `sourcePosition` represents the start of an + /// extended grapheme cluster---the element type of a string---then the + /// initializer succeeds. + /// + /// The following example converts the position of the Unicode scalar `"e"` + /// into its corresponding position in the string. The character at that + /// position is the composed `"é"` character. + /// + /// let cafe = "Cafe\u{0301}" + /// print(cafe) + /// // Prints "Café" + /// + /// let scalarsIndex = cafe.unicodeScalars.firstIndex(of: "e")! + /// let stringIndex = String.Index(scalarsIndex, within: cafe)! + /// + /// print(cafe[...stringIndex]) + /// // Prints "Café" + /// + /// If the index passed as `sourcePosition` doesn't have an exact + /// corresponding position in `target`, the result of the initializer is + /// `nil`. For example, an attempt to convert the position of the combining + /// acute accent (`"\u{0301}"`) fails. Combining Unicode scalars do not have + /// their own position in a string. + /// + /// let nextScalarsIndex = cafe.unicodeScalars.index(after: scalarsIndex) + /// let nextStringIndex = String.Index(nextScalarsIndex, within: cafe) + /// + /// print(nextStringIndex) + /// // Prints "nil" + /// + /// - Parameters: + /// - sourcePosition: A position in a view of the `target` parameter. + /// `sourcePosition` must be a valid index of at least one of the views + /// of `target`. + /// - target: The string referenced by the resulting index. + public init?( + _ sourcePosition: String.Index, within target: S + ) +} + +extension Range where Bound == String.Index { + public init?(_ range: NSRange, in string: __shared S) +} +``` + +### 3. Substring provides access to its base + +Slice, the default SubSequence type, provides `base` for accessing the original Collection. Substring, String’s SubSequence, should as well. + +```swift +extension Substring { + /// Returns the underlying string from which this Substring was derived. + public var base: String { get } +} + +``` + +### 4. Add in missing views on Character + +Character’s UTF8View and UTF16View has existed internally, but we should make it public. + +```swift + +extension Character { + /// A view of a character's contents as a collection of UTF-8 code units. See + /// String.UTF8View for more information + public typealias UTF8View = String.UTF8View + + /// A UTF-8 encoding of `self`. + public var utf8: UTF8View { get } + + /// A view of a character's contents as a collection of UTF-16 code units. See + /// String.UTF16View for more information + public typealias UTF16View = String.UTF16View + + /// A UTF-16 encoding of `self`. + public var utf16: UTF16View { get } +} +``` + + +### 5. Add in a RandomAccessCollection UTF8View on Unicode.Scalar + +Unicode.Scalar has a UTF16View with is a RandomAccessCollection, but not a UTF8View. + +```swift +extension Unicode.Scalar { + public struct UTF8View { + internal init(value: Unicode.Scalar) + internal var value: Unicode.Scalar + } + + public var utf8: UTF8View { get } +} + +extension Unicode.Scalar.UTF8View : RandomAccessCollection { + public typealias Indices = Range + + /// The position of the first code unit. + public var startIndex: Int { get } + + /// The "past the end" position---that is, the position one + /// greater than the last valid subscript argument. + /// + /// If the collection is empty, `endIndex` is equal to `startIndex`. + public var endIndex: Int { get } + + /// Accesses the code unit at the specified position. + /// + /// - Parameter position: The position of the element to access. `position` + /// must be a valid index of the collection that is not equal to the + /// `endIndex` property. + public subscript(position: Int) -> UTF8.CodeUnit +} +``` + +## Source compatibility + +All changes are additive. + +## Effect on ABI stability + +All changes are additive. ABI-relevant attributes are provided in “Detailed design”. + +## Effect on API resilience + +* Unicode encoding additions and `Substring.base` are trivial and can never change in definition, so their implementations are exposed. +* `String.Index` initializers are resilient and versioned. +* Character’s views already exist as inlinable in 5.0, we just replace `internal` with `public` +* Unicode.Scalar.UTF8View's implementation is fully exposed (for performance), but is versioned + +## Alternatives considered + +### Do Nothing + +Various flavors of “do nothing” include stating a given API is not useful or waiting for a rethink of some core concept. Each of these API gaps frequently come up on the forums, bug reports, or seeing developer usage in the wild. Rethinks are unlikely to happen anytime soon. We believe these gaps should be closed immediately. + +### Do More + +This proposal is meant to round out holes and provide some simple additions, keeping the scope narrow for Swift 5.1. We could certainly do more in all of these areas, but that would require a more design iteration and could be dependent on other missing functionality. + From 4aa2290ededcf222c716139d71915e0dcfbeef25 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Wed, 13 Mar 2019 19:43:08 -0700 Subject: [PATCH 1038/4563] Kick off SE-0248 --- ...aps-missing-apis.md => 0248-string-gaps-missing-apis.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{nnnn-string-gaps-missing-apis.md => 0248-string-gaps-missing-apis.md} (98%) diff --git a/proposals/nnnn-string-gaps-missing-apis.md b/proposals/0248-string-gaps-missing-apis.md similarity index 98% rename from proposals/nnnn-string-gaps-missing-apis.md rename to proposals/0248-string-gaps-missing-apis.md index 0116983339..55c81e219e 100644 --- a/proposals/nnnn-string-gaps-missing-apis.md +++ b/proposals/0248-string-gaps-missing-apis.md @@ -1,9 +1,9 @@ # String Gaps and Missing APIs -* Proposal: SE-NNNN +* Proposal: [SE-0248](0248-string-gaps-missing-apis.md) * Authors: [Michael Ilseman](https://github.com/milseman) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Status: **Active review (March 13 - 22, 2019)** * Implementation: [apple/swift#22869](https://github.com/apple/swift/pull/22869/files) * Bugs: [SR-9955](https://bugs.swift.org/browse/SR-9955) From c870eaab1c131b716fd07f25e8901e787cbf586b Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Wed, 13 Mar 2019 19:58:27 -0700 Subject: [PATCH 1039/4563] Badge the key path proposal as SE-0249. Avoids a conflict with the other SE-0247. --- ...sions.md => 0249-key-path-literal-function-expressions.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{NNNN-key-path-literal-function-expressions.md => 0249-key-path-literal-function-expressions.md} (97%) diff --git a/proposals/NNNN-key-path-literal-function-expressions.md b/proposals/0249-key-path-literal-function-expressions.md similarity index 97% rename from proposals/NNNN-key-path-literal-function-expressions.md rename to proposals/0249-key-path-literal-function-expressions.md index c9f76dbbff..68b9b245b9 100644 --- a/proposals/NNNN-key-path-literal-function-expressions.md +++ b/proposals/0249-key-path-literal-function-expressions.md @@ -1,9 +1,9 @@ # Key Path Expressions as Functions -* Proposal: [SE-0247](0247-key-path-literal-function-expressions.md) +* Proposal: [SE-0249](0249-key-path-literal-function-expressions.md) * Authors: [Stephen Celis](https://github.com/stephencelis), [Greg Titus](https://github.com/gregomni) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Scheduled (March 18 - 26, 2019)** +* Status: **Scheduled for review (March 18...26, 2019)** * Implementation: [apple/swift#19448](https://github.com/apple/swift/pull/19448) - ## Introduction This proposal introduces the ability to use the key path expression `\Root.value` wherever functions of `(Root) -> Value` are allowed. From 712170f5ae8502b40f3ed16cfae52b6947f5262b Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 16 Mar 2019 03:42:51 +0000 Subject: [PATCH 1042/4563] [SE-0068] Update status to implemented (Swift 5.1) --- proposals/0068-universal-self.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/0068-universal-self.md b/proposals/0068-universal-self.md index cda7483211..4b703ba153 100644 --- a/proposals/0068-universal-self.md +++ b/proposals/0068-universal-self.md @@ -1,9 +1,10 @@ # Expanding Swift `Self` to class members and value types * Proposal: [SE-0068](0068-universal-self.md) -* Author: [Erica Sadun](http://github.com/erica) -* Review Manager: [Chris Lattner](http://github.com/lattner) -* Status: **Accepted with revisions** +* Author: [Erica Sadun](https://github.com/erica) +* Review Manager: [Chris Lattner](https://github.com/lattner) +* Status: **Implemented (Swift 5.1)** +* Implementation: [apple/swift#22863](https://github.com/apple/swift/pull/22863) * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160425/015977.html) * Bug: [SR-1340](https://bugs.swift.org/browse/SR-1340) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/bcd77b028cb2fc9f07472532b120e927c7e48b34/proposals/0068-universal-self.md), [2](https://github.com/apple/swift-evolution/blob/13d9771e86c5639b8320f05e5daa31a62bac0f07/proposals/0068-universal-self.md) From 9bc888fb1eb1dc351806755e962094c89d3e9eef Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 18 Mar 2019 21:28:03 +0000 Subject: [PATCH 1043/4563] [SE-0248] Update "Effect on ABI stability" section (#1003) --- proposals/0248-string-gaps-missing-apis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0248-string-gaps-missing-apis.md b/proposals/0248-string-gaps-missing-apis.md index 1272ac3fec..cfae8bd0bc 100644 --- a/proposals/0248-string-gaps-missing-apis.md +++ b/proposals/0248-string-gaps-missing-apis.md @@ -216,7 +216,7 @@ All changes are additive. ## Effect on ABI stability -All changes are additive. ABI-relevant attributes are provided in “Detailed design”. +All changes are additive. ## Effect on API resilience From 2ebb74d0d850b1049f2988cf0da757d718e6238e Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 19 Mar 2019 07:32:12 -0700 Subject: [PATCH 1044/4563] Kick off review --- proposals/0249-key-path-literal-function-expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0249-key-path-literal-function-expressions.md b/proposals/0249-key-path-literal-function-expressions.md index 69f48c7808..c5b9e39d1c 100644 --- a/proposals/0249-key-path-literal-function-expressions.md +++ b/proposals/0249-key-path-literal-function-expressions.md @@ -3,7 +3,7 @@ * Proposal: [SE-0249](0249-key-path-literal-function-expressions.md) * Authors: [Stephen Celis](https://github.com/stephencelis), [Greg Titus](https://github.com/gregomni) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Scheduled for review (March 18...26, 2019)** +* Status: **In review (March 19...27, 2019)** * Implementation: [apple/swift#19448](https://github.com/apple/swift/pull/19448) ## Introduction From 621a30928492f95e0c3e67fd1e4d110e8da0af10 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 19 Mar 2019 17:33:26 +0000 Subject: [PATCH 1045/4563] [SE-0249] Fix "missing or invalid proposal status" --- proposals/0249-key-path-literal-function-expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0249-key-path-literal-function-expressions.md b/proposals/0249-key-path-literal-function-expressions.md index c5b9e39d1c..ce651f7fdb 100644 --- a/proposals/0249-key-path-literal-function-expressions.md +++ b/proposals/0249-key-path-literal-function-expressions.md @@ -3,7 +3,7 @@ * Proposal: [SE-0249](0249-key-path-literal-function-expressions.md) * Authors: [Stephen Celis](https://github.com/stephencelis), [Greg Titus](https://github.com/gregomni) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **In review (March 19...27, 2019)** +* Status: **Active review (March 19...27, 2019)** * Implementation: [apple/swift#19448](https://github.com/apple/swift/pull/19448) ## Introduction From c942ca9d3aa7a3c139e3d9d7cbb29c0946ec76fc Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 19 Mar 2019 10:41:30 -0700 Subject: [PATCH 1046/4563] Property delegates proposal. --- proposals/NNNN-property-delegates.md | 639 +++++++++++++++++++++++++++ 1 file changed, 639 insertions(+) create mode 100644 proposals/NNNN-property-delegates.md diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md new file mode 100644 index 0000000000..60726d6b82 --- /dev/null +++ b/proposals/NNNN-property-delegates.md @@ -0,0 +1,639 @@ +# Property Delegates + +* Proposal: [SE-NNNN](NNNN-property-delegates.md) +* Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) +* Review Manager: **TBD** +* Status: **Awaiting review** + +## Introduction + +There are property implementation patterns that come up repeatedly. +Rather than hardcode a fixed set of patterns into the compiler, +we should provide a general "property delegate" mechanism to allow +these patterns to be defined as libraries. + +This is an alternative approach to some of the problems intended to be addressed by the [2015-6 property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md). Some of the examples are the same, but this proposal and design is meant to be freestanding. However, there is a section that discusses the substantive differences from that design at the end of this proposal. + +[Pitch](TODO)
    + +## Motivation + +We've tried to accommodate several important patterns for properties with +targeted language support, but this support has been narrow in scope and +utility. For instance, Swift provides `lazy` properties as a primitive +language feature, since lazy initialization is common and is often necessary to +avoid having properties be exposed as `Optional`. Without this language +support, it takes a lot of boilerplate to get the same effect: + +```swift +struct Foo { + // lazy var foo = 1738 + private var _foo: Int? + var foo: Int { + get { + if let value = _foo { return value } + let initialValue = 1738 + _foo = initialValue + return initialValue + } + set { + _foo = newValue + } + } +} +``` + +Building `lazy` into the language has several disadvantages. It makes the +language and compiler more complex and less orthogonal. It's also inflexible; +there are many variations on lazy initialization that make sense, but we +wouldn't want to hardcode language support for all of them. + +There are important property patterns outside of lazy initialization. It often +makes sense to have "delayed", once-assignable-then-immutable properties to +support multi-phase initialization: + +```swift +class Foo { + let immediatelyInitialized = "foo" + var _initializedLater: String? + + // We want initializedLater to present like a non-optional 'let' to user code; + // it can only be assigned once, and can't be accessed before being assigned. + var initializedLater: String { + get { return _initializedLater! } + set { + assert(_initializedLater == nil) + _initializedLater = newValue + } + } +} +``` + +Implicitly-unwrapped optionals allow this in a pinch, but give up a lot of +safety compared to a non-optional 'let'. Using IUO for multi-phase +initialization gives up both immutability and nil-safety. + +The attribute `@NSCopying` introduces a use of `NSCopying.copy()` to +create a copy on assignment. The implementation pattern may look familiar: + +```swift +class Foo { + // @NSCopying var text: NSAttributedString + var _text: NSAttributedString + var text: NSAttributedString { + get { return _text } + set { _text = newValue.copy() as! NSAttributedString } + } +} +``` + +## Proposed solution + +We propose the introduction of **property delegates**, which allow a +`var` declaration to state which **delegate** is used to implement +it. Borrowing from [Kotlin's delegated +properties](https://kotlinlang.org/docs/reference/delegated-properties.html), +we propose the `by` keyword to indicate the delegate: + +```swift +var foo by Lazy = 1738 +``` + +This implements the property `foo` in a way described by the *property delegate type* for `Lazy`: + +```swift +@propertyDelegate +enum Lazy { + case uninitialized(() -> Value) + case initialized(Value) + + init(initialValue: @autoclosure @escaping () -> Value) { + self = .uninitialized(initialValue) + } + + var value: Value { + mutating get { + switch self { + case .uninitialized(let initializer): + let value = initializer() + self = .initialized(value) + return value + case .initialized(let value): + return value + } + } + set { + self = .initialized(newValue) + } + } +} +``` + +A property delegate type provides the storage for a property that +names it after `by`. The `value` property provides the actual +implementation of the delegate, while the (optional) +`init(initialValue:)` enables initialization of the storage from a +value of the property's type. The property declaration + +```swift +var foo by Lazy = 1738 +``` + +translates to: + +```swift +private var $foo: Lazy = Lazy(initialValue: 1738) +var foo: Int { + get { return $foo.value } + set { $foo.value = newValue } +} +``` + +The use of the prefix `$` for the synthesized storage property name is +deliberate: it provides a predictable name for the backing storage, +so that delegate types can provide API. For example, we could provide +a `reset(_:)` operation on `Lazy` to set it back to a new value: + +```swift +extension Lazy { + /// Reset the state back to "uninitialized" with a new, + /// possibly-different initial value to be computed on the next access. + mutating func reset(_ newValue: @autoclosure @escaping () -> Value) { + self = .uninitialized(newValue) + } +} + +$foo.reset(42) +``` + +The synthesized storage property will be `private` by +default. However, it can be given more lenient access by putting the +access level after `by`, e.g., + +```swift +// both foo and $foo are publicly visible +public var foo: Int by public Lazy = 1738 +``` + +## Examples + +Before describing the detailed design, here are some more examples of +delegates. + +### Delayed Initialization + +A property delegate can model "delayed" initialization delegate, where +the definite initialization (DI) rules for properties are enforced +dynamically rather than at compile time. This can avoid the need for +implicitly-unwrapped optionals in multi-phase initialization. We can +implement both a mutable variant, which allows for reassignment like a +`var`: + +```swift +@propertyDelegate +struct DelayedMutable { + private var _value: Value? = nil + + var value: Value { + get { + guard let value = _value else { + fatalError("property accessed before being initialized") + } + return value + } + set { + _value = newValue + } + } + + /// "Reset" the delegate so it can be initialized again. + mutating func reset() { + _value = nil + } +} +``` + +and an immutable variant, which only allows a single initialization like +a `let`: + +```swift +@propertyDelegate +struct DelayedImmutable: Value { + private var _value: Value? = nil + + var value: Value { + get { + guard let value = _value else { + fatalError("property accessed before being initialized") + } + return value + } + + // Perform an initialization, trapping if the + // value is already initialized. + set { + if _value != nil { + fatalError("property initialized twice") + } + _value = initialValue + } + } +} +``` + +This enables multi-phase initialization, like this: + +```swift +class Foo { + var x: Int by DelayedImmutable + + init() { + // We don't know "x" yet, and we don't have to set it + } + + func initializeX(x: Int) { + self.x = x // Will crash if 'self.x' is already initialized + } + + func getX() -> Int { + return x // Will crash if 'self.x' wasn't initialized + } +} +``` + +### `NSCopying` + +Many Cocoa classes implement value-like objects that require explicit copying. +Swift currently provides an `@NSCopying` attribute for properties to give +them delegate like Objective-C's `@property(copy)`, invoking the `copy` method +on new objects when the property is set. We can turn this into a delegate: + +```swift +@propertyDelegate +stuct Copying { + private var _value: Value + + init(initialValue value: Value) { + // Copy the value on initialization. + self._value = value.copy() as! Value + } + + var value: Value { + get { return _value } + set { + // Copy the value on reassignment. + _value = newValue().copy() as! Value + } + } +} +``` + +This implementation would address the problem detailed in +[SE-0153](https://github.com/apple/swift-evolution/blob/master/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md). Leaving the `copy()` out of `init(initialValue:)` implements the pre-SE-0153 semantics. + +### `Unsafe(Mutable)Pointer` + +The `Unsafe(Mutable)Pointer` types could be augmented to be property delegate types, allowing one to access the referenced value directly. For example: + + +```swift +@propertyDelegate +struct UnsafeMutablePointer { + var pointee: Pointee { ... } + + var value: Pointee { + get { return pointee } + set { pointee = newValue + } +} + +var someInt: Int by UnsafeMutablePointer = ... +someInt = 17 // equivalent to someInt.value = 17 +``` + +## Detailed design + +### Property delegate types + +A *property delegate type* is a type that can be used as a property +delegate. There are three basic requirements for a property delegate +type: + +1. The property delegate type must be defined with the attribute +`@propertyDelegate`. The attribute indicates that the type is meant to +be used as a property delegate type, and provides a point at which the +compiler can verify the other consistency rules. +2. The property delegate type must be generic, with a single generic +type parameter. The type parameter will filled in with the type of the +variable that uses the property delegate type. +3. The property delegate type must have a property named `value` whose +type is that of the single generic type parameter. This is the +property used by the compiler to access the underlying value on the +delegate instance. + +### Initialization of synthesized storage properties + +Introducing a property delegate to a property makes that property +computed (with a getter/setter) and introduces a stored property whose +type uses the delegate type. That stored property can be initialized +in one of three ways: + +1. Via the property delegate type's zero-parameter `init()` +initializer, if the property is not initialized. This +allows a property delegate type to provide implicit +initialization. For example, the `DelayedMutable` property delegate +type depends on this delegate to allow one to avoid the normal +definition-initialization requirements and implement the +delayed-initialization pattern. + +2. Via a value of the original property's type (e.g., `Int` in `var +foo: Int by Lazy`, using the the property delegate type's +`init(initialValue:)` initializer. That initializer must have a single +parameter of the property delegate type's generic type parameter (or +be an `@autoclosure` thereof). When `init(initialValue:)` is present, +is is always used for the initial value provided on the property +declaration. For example: + +```swift +var foo by Lazy = 17 + +// ... implemented as +private var $foo: Lazy = .init(initialValue: 17) +var foo: Int { /* access via $foo.value as described above */ } +``` + +3. Via a value of the property delegate type, using any initializer of +the property delegate type. This applies for property delegate types +that do not have an `init(initialValue:)`, such as with the `UnsafeMutablePointer` example: + +```swift +var addressOfInt: UnsafePointer = ... +var someInt: Int by UnsafeMutablePointer = .init(mutating: addressOfInt) + +// ... implemented as +private var $someInt: UnsafeMutablePointer = .init(mutating: addressOfInt) +var someInt: Int { /* access via $someInt.value */ } +``` + +### Type inference with delegates + +Type inference for properties with delegates involves both the type +annotation of the original property (if present) and the delegate +type, using the initialization of the synthesized stored property. For +example: + +```swift +var foo by Lazy = 17 +// type inference as in... +private var $foo: Lazy = .init(initialValue: 17) +// infers the type of 'foo' to be 'Int' +``` + +The same applies for property delegate types that don't have an +`init(initialValue:)`, e.g., + +```swift +var someInt by UnsafeMutable = .init(pointer: addressOfInt) +// type inference as in... +private var $someInt: UnsafeMutable = .init(pointer: addressOfInt) +// infers the type of 'someInt' to be 'Int' +``` + +### Using delegates in property declarations + +A property declaration can specify its delegate following the `by` +keyword: + +```text +pattern-initializer ::= pattern property-delegate[opt] initializer[opt] + +property-delegate ::= 'by' access-level-modifier[opt] type +``` + +The *type* in a *property-delegate* must refer to a 'property delegate +type'_ without specifying a generic argument. The +*access-level-modifier* can be any of `private`, `fileprivate`, +`internal`, or `public`, but cannot be less restrictive than the +property declaration itself. + +Properties cannot be declared using delegates inside protocols. A +property with a delegate that is declared within a class must be +`final` and cannot override another property. A property with a +delegate may not declare any accessors. + +### Mutability of properties with delegates + +A property with a delegate can be specified with either `var` or +`let`. If it spelled with `let`, it cannot be mutated---even if the +specified property delegate type has a `nonmutating set` on its +`value` property. Similarly, a property spelled with `var` but whose +delegate type has a `value` that lacks a setter (or has an +inaccessible setter) cannot be mutated directly. However, the +synthesized storage property could be mutated. + +### Out-of-line initialization of properties with delegates + +A property that has a delegate can be initialized after it is defined, +either via the property itself (if the delegate type has an +`init(initialValue:)`) or via the synthesized storage property. For +example: + + +```swift +let x: Int by Lazy +// ... +x = 17 // okay, treated as $x = .init(initialValue: 17) +``` + +The synthesized storage property can also be initialized directly, +e.g., + +```swift +var y: Int by UnsafeMutable +// ... +$y = UnsafeMutable(pointer: addressOfInt) // okay +``` + +Note that the rules of [definite +initialization](https://developer.apple.com/swift/blog/?id=28) (DI) +apply to properties that have delegates. Let's expand the example of +`x` above to include a re-assignment and use `var`: + +var x2: Int by Lazy +// ... +x2 = 17 // okay, treated as $x2 = .init(initialValue: 17) +// ... +x2 = 42 // okay, treated as x2 = 42 (calls the Lazy.value setter) +``` + +A property with a delegate whose delegate type has a zero-parameter +initializer (`init()`) will be implicitly initialized via the +zero-parameter initializer. For example, + +```swift +var z: Int by DelayedMutable +/* implicitly initialized with.. */ +$z = DelayedMutable.init() +``` + +### Memberwise initializers + +Structs implicitly declare memberwise initializers based on the stored +properties of the struct. With a property that has a delegate, the +property is technically computed because it's the synthesized property +(of the delegate's type) that is stored. However, the delegate itself +might be an implementation detail that should not affect the form of +the memberwise initializer. + +The parameter type that is introduced into an implicit memberwise +initializer for a property with a delegate is determined as follows: + +* If the delegate type contains an `init(initialValue:)`, the + parameter type is the original type of the property. +* When the delegate type does not contain an `init(initialValue:)`, + the parameter type is the type of the synthesized storage property + (i.e., a specialization of the delegate type). In this case, the + access level of the implicit initializer may need to be adjusted to + account for a visibility of the delegate: if the delegate is private + (e.g., `var x: Int by UnsafeMutable`), then the implicit memberwise + initializer will be `private`. + +For example: + +```swift +struct Foo { + var x: Int by fileprivate UnsafeMutable + var y: Int by Lazy = 17 + + // implicit memberwise initializer: + fileprivate init(x: UnsafeMutable, y: Int = 17) { + self.$x = x + self.$y = .init(initialValue: y) + } +} +``` + +Synthesis for `Encodable`, `Decodable`, `Hashable`, and `Equatable` +follows the same rules, using the underlying `value` of the property +delegate type contains an `init(initialValue:)` and the synthesized +storage property's type otherwise. + +## Impact on existing code + +By itself, this is an additive feature that doesn't impact existing +code. However, with some of the property delegates suggested, it can +potentially obsolete existing, hardcoded language +features. `@NSCopying` could be completely replaced by a `Copying` +property delegate type introduced in the `ObjectiveC` module. `lazy` +cannot be completely replaced because it's initial value can refer to +the `self` of the enclosing type; see 'deferred evaluation of +initialization expressions_. However, it may still make sense to +introduce a `Lazy` property delegate type to cover many of the common +use cases, leaving the more-magical `lazy` as a backward-compatibility +feature. + +## Alternatives considered + +### Using a formal protocol instead of `@propertyDelegate` + +Instead of a new attribute, we could introduce a `Propertydelegate` +protocol to describe the semantic constraints on property delegate +types. It might look like this: + +```swift +protocol Propertydelegate { + associatedtype Value + var value: Value { get } +} +``` + +There are a few issues here. First, a single protocol +`Propertydelegate` cannot handle all of the variants of `value` that +are implied by the section 'Mutability of properties with delegates'_, +because we'd need to cope with `mutating get` as well as `set` and +`nonmutating set`. Moreover, protocols don't support optional +requirements, like `init(initialValue:)` (which also has two +forms: one accepting a `Value` and one accepting an `@autoclosure () +-> Value`) and `init()`. To cover all of these cases, we would need a +several related-but-subtly-different protocols. + +The second issue that, even if there were a single `Propertydelegate` +protocol, we don't know of any useful generic algorithms or data +structures that seem to be implemented in terms of only +`Propertydelegate`. + +## Future directions + +The functionality proposed here is quite broad, so to attempt to minimize +the review burden of the initial proposal, I've factored out several +aspects for separate consideration: + +### Composing delegates + +It is useful to be able to compose delegates, for instance, to have a +lazy property that's also synchronized. Linear composition could be +supported by allowing delegates to stack, each referring to the +underlying property beneath it by `value`. However, this form of +composition can be treacherous, since it allows for "incorrect" +compositions of delegates. One of `lazy • synchronized` or +`synchronized • lazy` is going to do the wrong thing. With the current +proposal, composed delegates must be explicitly implemented as a new +property delegate type. + +### Deferred evaluation of initialization expressions + +This proposal does not suggest changing the allowed operations inside +initialization expressions; in particular, an initialization of an +instance property may not refer to `self` or other instance properties or +methods, due to the potential for the expression to execute before the +value is fully initialized: + +```swift +struct Foo { + var a = 1 + var b = a // Not allowed + var c = foo() // Not allowed + + func foo() { } +} +``` + +This is inconvenient for delegates like `lazy` that only ever evaluate the +initial value expression after the true initialization phase has completed, +and where it's desirable to reference `self` to lazily initialize. +delegates could be extended to annotate the initializer as "deferred", +which would allow the initializer expression to refer to `self`, while +preventing the initializer expression from being evaluated at initialization +time. (If we consider delegates to be essentially always fragile, this could +be inferred from the delegate implementation.) + +## Changes from the 2015-6 property delegates design + +Property delegates were [proposed and +reviewed](https://github.com/apple/swift-evolution/blob/dc52ebb3ef85108eecf009248c130eaa0c43d6d4/proposals/0030-property-delegate-decls.md) +in late 2015/early 2016. The design did not converge, and the proposal +was deferred. This proposal picks up the thread, using much of the +same motivation and core design ideas, but attempting to simplify the +feature and narrow the feature set. Some substantive differences from +the prior proposal are: + +* delegates were introduced into a property with the `[delegate]` + syntax, rather than the `by delegate` syntax described here. See the + prior iteration of the proposal for other discussions of syntax. +* delegates are always expressed by a (generic) type. The prior design + iteration had a new kind of declaration (introduced by the + `delegate` keyword). Having a new kind of declaration allowed for + the introduction of specialized syntax, but it also greatly + increased the surface area (and implementation cost) of the + proposal. Using a generic type makes property delegates more of a + syntactic-sugar feature. +* delegates cannot declare new kinds of accessors (e.g., the + `didChange` example from the prior proposal). +* delegates used for properties declared within a type cannot refer to + the `self` of their enclosing type. This eliminates some use cases + (e.g., implementing a `Synchronized` property delegate type that + uses a lock defined on the enclosing type), but simplifies the + proposal. +* delegates in this proposal can be initialized out-of-line, and one + can use the `$`-prefixed name to refer to the delegate + instance. These were future directions in the prior proposal. From 6e0bdff5ab46ac510be2005ce68b7f986c7b2851 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 19 Mar 2019 13:33:44 -0700 Subject: [PATCH 1047/4563] Propose the existence of Swift code style guidelines and a formatter. (#994) * Propose the existence of an official Swift style guide and formatter. * Emphasize that style guidelines will not be forced upon users. The word "official" has been a point of contention for some in the pitch thread, and there has been some confusion about whether or not the style guidelines being discussed would be made made mandatory. The text has been updated throughout to clarify that they will not be mandatory. * Clarifications about swift-format and future style discussions. * Some additional clarifications. * Additional clarification and alternatives considered. * Add header to better call out tool configurability. --- .../NNNN-swift-style-guide-and-formatter.md | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 proposals/NNNN-swift-style-guide-and-formatter.md diff --git a/proposals/NNNN-swift-style-guide-and-formatter.md b/proposals/NNNN-swift-style-guide-and-formatter.md new file mode 100644 index 0000000000..0c47bcd0fd --- /dev/null +++ b/proposals/NNNN-swift-style-guide-and-formatter.md @@ -0,0 +1,210 @@ +# Swift Code Style Guidelines and Formatter + +* Proposal: [SE-NNNN](NNNN-swift-style-guide-and-formatter.md) +* Authors: [Tony Allevato](https://github.com/allevato), [Dave Abrahams](https://github.com/dabrahams) +* Review Manager: TBD +* Status: **Awaiting review** + +## Introduction + +We propose that the Swift project adopt a set of code style +guidelines and provide a formatting tool that lets users easily +diagnose and update their code according to those guidelines. +These guidelines would _not_ be mandatory for all projects, but +encouraged for Swift code to follow for general consistency. + +## Motivation + +At the time of this writing, there is no single style agreed on +by developers using Swift. Indeed, even Apple's own Swift +projects on GitHub—such as the standard library, Foundation, +Swift NIO, and so forth—have adopted their own varying styles. +Furthermore, in many cases the code in those projects—despite +the owners' best efforts—is not always completely consistent in +terms of style. + +In the absence of strict or recommended language-specific +guidelines, many organizations adopt company-wide or project-wide +style guides, which other developers may, and do, choose to +adopt. But creating code style guidelines that are maintained by +the language owners and community, along with tooling that allows +users to easily adopt those guidelines, provides a number of +additional benefits: + +1. The guidelines serve as a clear and easily referenceable + source of language best practices and patterns, rather than + developers trying to glean these by reading existing code. +1. Developers can move from one codebase to another without + incurring the mental load of learning and conforming to a + different style or being required to reconfigure their + development environment. +1. Developers spend less time worrying about how to format their + code and more on the program's logic. +1. Likewise, code reviewers spend less time commenting on code + formatting issues and more on its logic and design. + +The first two points in particular align well with the Swift +team's goal of making the language easy to learn. They also +remove learning barriers for developers who want to contribute +to a new open-source project, or to the language itself. + +## Proposed solution + +This proposal consists of two parts, discussed below: + +1. We propose that Swift adopt a set of code style guidelines for + the Swift language. +2. We propose formally adopting a formatting tool into the Swift + project that will allow users to update their code in + accordance with those guidelines. + +### Style Guide + +This meta-proposal does not attempt to define any specific style +guidelines. Its purpose is to answer the following existential +question: + +> Should the Swift language adopt a set of code style guidelines +> and a formatting tool? + +If the answer to this is "yes", then **subsequent proposals** +will be pitched to discuss and ratify those guidelines. In order +to keep those discussions scoped and focused, we plan to present +proposed guidelines to the community in multiple phases with each +centered around a particular theme, such as (but not necessarily +limited to) **typographical concerns,** **code consistency,** +and **best practices.** + +The proposal authors wish to emphasize that **we are not +proposing that users be required or forced** to use a particular +set of style conventions. The Swift compiler will **not** be +changed in any way that would prevent otherwise syntactically +valid code from compiling. Users who wish to reject the style +guidelines and adopt a different style for their own projects are +free to do so without the tooling pushing back on that decision. + +### Formatting Tool + +If the proposal is accepted, the Swift project will adopt an +official code formatting tool. The adoption of such a tool into +the Swift project will not preclude other similar tools being +written, but the expectation is that this tool will be officially +maintained as part of the Swift project and will (once the +details are decided) format users' code in accordance with the +accepted code style guidelings. + +The proposal authors ([among others](#acknowledgments)) have +collaborated on the `swift-format` tool currently hosted at +https://github.com/google/swift/tree/format and propose its +adoption into the Swift project. We propose this specific tool +because it satisfies all of the following goals: + +* It is syntax-oriented, which provides high reliability and + performance (especially once it adopts recently developed + in-process parsing APIs) as compared to SourceKit-based + solutions. +* It uses SwiftSyntax to process code—the Swift project's + preferred method of developing such tools—rather than a + distinct parsing implementation that must separately track + language evolution. +* It comes with a continuing support commitment from active + maintainers. + +The tool will be used as part of evaluating options for the +proposed code style guidelines, as part of a follow-up proposal on +the details of the guidelines themselves. + +### Configurability of Formatting + +`swift-format` will allow configuration of some practical +formatting decisions like indentation size, line length, and +respecting existing newlines. In mixed-language projects, some +tools in a developer's workflow may not easily support +configuring these on a per-language basis. + +We are also willing to consider additional degrees of +configurability. A tool that is not configurable only works for +users who are completely satisfied with the defaults. A tool that +is configurable is still usable by anyone who wants to leave it +configured to the default settings, but can also be tailored to +the unique needs of individual code bases. Even if style +guidelines ratified later encourage a particular default +configuration, users with different needs should still be able to +reap benefits from using the tool. + +As with the style guidelines above, the adopted formatting tool +will not be forced upon a developer's workflow by any part of the +Swift toolchain. Users who wish not to use it will have the +option to simply not run it on their code. + +## Alternatives considered + +We could not propose any particular style guidelines and leave it +to individual developers and teams to create their own (if they +so desired). That does not address the points listed in +[Motivation](#motivation) above. + +We could propose style guidelines but no official formatting +tool. However, we feel that a tool that works out-of-the-box +without any other installation requirements or mandatory +configuration is a major benefit to users. The existence of such +a tool does not diminish the value of other tools that aim to +enforce good coding patterns, be they the same, complementary, or +outright different patterns than those proposed in future Swift +coding style guidelines. + +We could make style guidelines mandatory, or at least enforced in +a very opinionated manner by the formatter (similar to Go). We +have chosen not to do so given that Swift is a well-established +language. Users who are happy with the default guidelines can +simply use them as-is, developers who have different preferences +are not unnecessarily constrained. + +Some Swift users have suggested that instead of proposing any +style guidelines, tooling should be able to transform code +to the developer's personal style upon checkout and then back to +some canonical style upon check-in, allowing individual +developers to code in whatever style they wished. While such +ideas are intriguing, we find them to be more of an academic +curiosity than a practical solution: + +* Varying personal styles would hinder team communication. Team + members should be able to discuss code on a whiteboard without + it looking foreign to other people in the room, and to make + API and language design decisions based on a clear idea of how + the code will look. +* This approach assumes that all tools on all platforms used in + the developer's workflow support this approach. The development + experience would suffer if the code does not use the same + format locally as it does on their code review system, if + remote builds reported errors at different line numbers because + they used a checked-in snapshot with a different style, or if + symbolicated crash logs contain line numbers that must be + matched to one specific "rendering" of the project's source + code long after the fact. +* If the source of truth of the source code is saved in some + canonical format and transformed when checked in/out, then + there must still be some decision about what that canonical + style _is._ + +Indeed, nothing in this proposal would _prevent_ a developer from +using a workflow like the one described above, if they wished to +implement it. + +## Acknowledgments + +We gratefully acknowledge the following contributors, without +whom this work would not have been possible: + +* the other contributors to `swift-format`: Austin Belknap + ([@dabelknap](https://github.com/dabelknap)), + Harlan Haskins ([@harlanhaskins](https://github.com/harlanhaskins)), + Alexander Lash ([@abl](https://github.com/abl)), + Lauren White ([@LaurenWhite](https://github.com/LaurenWhite)), + and Andrés Tamez Hernandez ([@atamez31](https://github.com/atamez31)), +* Argyrios Kyrtzidis ([@akyrtzi](https://github.com/akyrtzi)) for + his insight and help on using SwiftSyntax, +* and Kyle Macomber ([@kylemacomber](https://github.com/kylemacomber)), + who advocated for using results of existing research for + `swift-format`'s implementation and found the Oppen paper, + instead of inventing solutions from whole cloth. From 60266bb8d125dee56f2d270b3fd25aa9eeeb63c8 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 19 Mar 2019 13:41:09 -0700 Subject: [PATCH 1048/4563] Launch review for SE-0245. --- ...ormatter.md => 0250-swift-style-guide-and-formatter.md} | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename proposals/{NNNN-swift-style-guide-and-formatter.md => 0250-swift-style-guide-and-formatter.md} (96%) diff --git a/proposals/NNNN-swift-style-guide-and-formatter.md b/proposals/0250-swift-style-guide-and-formatter.md similarity index 96% rename from proposals/NNNN-swift-style-guide-and-formatter.md rename to proposals/0250-swift-style-guide-and-formatter.md index 0c47bcd0fd..4f026b102a 100644 --- a/proposals/NNNN-swift-style-guide-and-formatter.md +++ b/proposals/0250-swift-style-guide-and-formatter.md @@ -1,9 +1,10 @@ # Swift Code Style Guidelines and Formatter -* Proposal: [SE-NNNN](NNNN-swift-style-guide-and-formatter.md) +* Proposal: [SE-0250](0250-swift-style-guide-and-formatter.md) * Authors: [Tony Allevato](https://github.com/allevato), [Dave Abrahams](https://github.com/dabrahams) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Status: **Active review (March 19 - April 10, 2019)** +* Discussion thread: [An Official Style Guide and Formatter for Swift](https://forums.swift.org/t/pitch-an-official-style-guide-and-formatter-for-swift/21025) ## Introduction From 8bd26acba887d9735df43a40cb0406f5a54dfeb0 Mon Sep 17 00:00:00 2001 From: "Steve (Numerics) Canon" Date: Tue, 19 Mar 2019 16:56:09 -0400 Subject: [PATCH 1049/4563] Revisions to simd-additions based on pitch feedback, added reference to implementation. --- proposals/0000-simd-additions.md | 452 ++++++++++++++++++++++--------- 1 file changed, 330 insertions(+), 122 deletions(-) diff --git a/proposals/0000-simd-additions.md b/proposals/0000-simd-additions.md index e40822a6b5..e7a6a08c97 100644 --- a/proposals/0000-simd-additions.md +++ b/proposals/0000-simd-additions.md @@ -1,9 +1,9 @@ # SIMD additions -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-NNNN](NNNN-simd-additions.md) * Authors: [Stephen Canon](https://github.com/stephentyrone) * Review Manager: TBD -* Status: **Awaiting implementation** +* Status: [**Implemented**](https://github.com/apple/swift/pull/23421) ## Introduction @@ -12,177 +12,385 @@ and protocols to better support their use cases. In addition, there are some fea we punted out of the original review because we were up against a hard time deadline to which we would like to give further consideration. -## Motivation +This is a bit of a grab bag of SIMD features, so I'm deviating from the usual proposal +structure. Each new addition has its own motivation, proposed solution, and alternatives +considered section. -This is a bit of a grab bag of SIMD features, so it's hard to present a single coherent -motivation for their addition. Basically, they are features that people who are writing -a significant amount of code against the existing SIMD types have found they are -missing. I'll attempt to motivate each new API individually in the proposed solution -section below. +## Table of Contents +1. [Static `scalarCount`](#scalarCount) +2. [Extending Vectors](#extending) +3. [Swizzling](#swizzling) +4. [Reductions](#reduction) +5. [Lanewise `min`, `max`, and `clamp`](#minMaxClamp) +6. [`.one`](#one) +7. [`any` and `all`](#anyAndAll) +8. [`copysign`](#copysign) -## Proposed solution + -### Extending vectors -Add the following initializers: +## Static scalarCount +### Motivation +In functions that construct new SIMD vectors, especially initializers, one frequently wants +to perform some validation involving `scalarCount` *before* doing the work to create the +vector. Currently, `scalarCount` is defined as an instance property (following the pattern +of `count` on collection). + +However, all SIMD vectors of a given type have the same `scalarCount`, so semantically +it makes sense to have available as a static property as well. There's precedent for having +this duplication in the `.bitWidth` property on fixed-width integers. + +### Detailed Design +```swift +extension SIMDStorage { + /// The number of scalars, or elements, in a vector of this type. + public static var scalarCount: Int { + return Self().scalarCount + } +} +``` +The property is defined as an extension on SIMDStorage because it makes semantic +sense there (`SIMD` refines `SIMDStorage`). It is defined in terms of the existing member +property (instead of the apparently more-logical vise-versa) because that way it +automatically works for all existing SIMD types with no changes. In practice this +introduces no extra overhead at runtime. + +### Alternatives Considered +Not doing anything. Users can always fall back on the weird-but-effective +`Self().scalarCount`. + + + +## Extending vectors +### Motivation +When working with homogeneous coordinates in graphics, the last component frequently +needs to be treated separately--this means that you frequently want to extract the first +(n-1) components, do arithmetic on them and the final component separately, and then +re-assemble them. At API boundaries, you frequently take in (n-1) component vectors, +immediately extend them to perform math, and then return out only the first (n-1) +components. + +### Detailed design +In order to support extending vectors from (n-1) to n components, add the following two +intializers: ```swift extension SIMD3 { - /// The vector (xy.x, xy.y, z) - public init(_ xy: SIMD2, _ z: Scalar) + public init(_ xy: SIMD2, _ z: Scalar) { + self.init(xy.x, xy.y, z) + } } extension SIMD4 { - /// The vector (xyz.x, xyz.y, xyz.z, w) - public init(_ xyz: SIMD3, _ w: Scalar) + public init(_ xyz: SIMD3, _ w: Scalar) { + self.init(xyz.x, xyz.y, xyz.z, w) + } } ``` -These are broadly useful in graphics contexts for working with homogeneous coordinates, -where the last component needs to be treated separately, and where you frequently want -to extend vectors by adding an additional component at API boundaries. These could -alternatively be spelled like `xy.appending(z)`; there are two reasons that I'm avoiding -that: -- I would expect `appending( )` to return the same type; here the result type is different. + +### Alternatives Considered +These could alternatively be spelled like `xy.appending(z)`; there are two reasons that +I'm avoiding that: +- I would expect `appending( )` to return the same type; but the result type is different. - I would expect `appending( )` to be available on all SIMD types, but it breaks down beyond `SIMD4`, because there is no `SIMD5` in the standard library. -### Loading and storing from collections -Add the following: +We could have also used an explicit parameter label. +```swift +let v = SIMD3(...) +let x = SIMD4(v, 1) // proposed above +let y = SIMD4(v, appending: 1) // with parameter label +``` +My feeling is that the behavior is clear without the label, but it's very reasonable to argue +for an explicit label instead. + + + +## Swizzling +### Motivation +In C-family languages, clang defines "vector swizzles" (aka permutes, aka shuffles, ... ) +that let you select and re-arrange elements from a vector: +```c +#import +simd_float4 x = { 1, 2, 3, 4}; +x.zyx; // (simd_float3){3, 2, 1}; +``` +This comes from an identical feature in graphics shader-languages, where it is very +heavily used. + +### Detailed design +For Swift, we want to restrict the feature somewhat, but also make it more powerful. +In shader languages and clang extensions, you can even use swizzled vectors as *lvalues*, +so long as the same element does not appear twice. I proposed to define general +permutes[†] as get-only subscripts. By restricting them from appearing as setters, we +gain the flexibility to not require they be compile-time constants: ```swift extension SIMD { - /// Extracts a vector with consecutive elements taken from `collection` - /// beginning at the index `start`. + /// Extracts the scalars at specified indices to form a SIMD2. /// - /// - Precondition: `collection` contains enough elements to fill the - /// vector. - public init(_ collection: C, start: C.Index) - where C: Collection, C.Element == Scalar - - /// Replaces the elements of `collection` beginning at the index - /// `start` with the elements of this vector. + /// The low-order log2(scalarCount) bits of each element of the + /// `masking` index vector are used to index self. E.g.: /// - /// - Precondition: `collection` has space for all elements of the - /// vector. - public func store(into collection: inout C, start: C.Index) - where C: MutableCollection, C.Element == Scalar + /// let x = SIMD4(0, 1, 2, 3) + /// let y = x[SIMD2(5,2)] // (1, 2) + /// + /// Because of this, the index is always in-range and no trap + /// can occur. + public subscript(masking: SIMD2) -> SIMD2 + where Index: FixedWidthInteger { + var result = SIMD2() + var masked = masking & Index(scalarCount - 1) + for i in result.indices { + result[i] = self[Int(masked[i])] + } + return result + } } + +let v = SIMD4(1,2,3,4) +let xyz = SIMD3(2,1,0) +let w = v[xyz] // SIMD3(3,2,1) ``` -These are primarily useful for working with arrays or UBP of vector data, such as are used -to marshal vertex or color data to and from the GPU. They are especially useful with -3-element vector types, where we may want to avoid the extra padding element that -`Array>` or `UnsafeBufferPointer>` would incur. +There's one special case, which is when the vector being subscripted has three elements. +In this case, we can't just mask the index to log2(scalarCount) bits. Instead, we extend +the vector being subscripted to a four-element vector (x, y, z, z) and mask to two bits. +This is expected to be an exceptionally rare case--power-of-two dictionaries are the +norm when dynamic indices are used; the principle concern is merely that we define the +result to be *something understandable and easily computable* rather than leaving it +undefined. + +### Alternatives Considered +1. We might want an explicit label on this subscript, but as with the extending inits, I +believe that its use is generally clear enough in context. -The `init` is pretty clear; I am not really sold on the naming of `store` at this point, and -would love to hear other suggestions. If `SIMD` types conformed to `Sequence`, this would -nearly be `collection.replaceSubrange(start...start+v.count, with: v)` for -`RRC`, but we would like to enforce that this doesn't grow the collection; it should only -replace exactly `count` elements, so it's slightly different--the `RRC` interface would be -redundant. +2. The main question is "what should the behavior for out-of-range indices be?" The +definition I have chosen here is simple to explain and maps efficiently to the hardware, +but there are at least two other good options: it could be a precondition failure, or it +could fill the vector with zero in lanes that have out of range indices. The first option +(trapping) is undesirable because it's less efficient with dynamic indices. The second +would be slightly more efficient on some architectures, but is also significantly more +magic. I believe that the proposed alternative has the best balance of explainable +behavior and efficiency. -The intention is that these should codegen to vector loads when the elements are actually -contiguous in memory, but still work with collections that are not contiguous. Early -experimentation suggests that we get the desired codgen even with fully generic -implementations of these operations. + -### Horizontal operations +## Reductions (or "Horizontal Operations") +### Motivation Generally in SIMD programming you try to avoid horizontal operations as much as possible, but frequently you need to do a few of them at the end of a chain of -computations. The reductions that we most often need to perform are: +computations. For example, if you're summing an array, you would sum into a bank +of vector accumulators first, then sum those down to a single vector. Now you need +to get from that vector to a scalar by summing the elements. This is where reductions +enter. + +`sum` is also a basic building block for things like the dot product (and hence matrix +multiplication), so it's very valuable to have an efficient implementation provided by the +standard library. Similarly you want to have `min` and `max` to handle things like rescaling +for computational geometry. + +### Detailed design ```swift extension SIMD where Scalar: Comparable { - func min() -> Scalar - func max() -> Scalar -} + /// The least element in the vector. + public var min: Scalar -extension SIMD where Scalar: BinaryFloatingPoint { - func sum() -> Scalar + /// The greatest element in the vector. + public var max: Scalar +} + +extension SIMD where Scalar: FixedWidthInteger { + /// Returns the sum of the scalars in the vector, computed with + /// wrapping addition. + /// + /// Equivalent to indices.reduce(into: 0) { $0 &+= self[$1] }. + public var wrappedSum: Scalar } -extension SIMD where Scalar: FixedWidthInteger { - func sum() -> Scalar +extension SIMD where Scalar: FloatingPoint { + /// Returns the sum of the scalars in the vector. + public var sum: Scalar } ``` -One might reasonably ask why the last two are not collapsed onto `AdditiveArithmetic`. -The answer is that we want to use `&+` for reduction on integer vectors, which isn't defined -on `AdditiveArithmetic`. One might follow-up with "isn't that prone to overflow?" The -answer is yes, but it's the operation you generally want in a SIMD context; to get the -"safe" sum you widen to the double-width type, then sum that. We might reasonably use -a different name for this operation though, like `wrappingSum`, to be explicit. -In addition, we would provide the following two reductions on Mask vectors: -```swift -/// True if any lane of the mask is true -func any(_ mask: SIMDMask) -> Bool +### Alternatives Considered +We could call the integer operation `sum` as well, but it seems better to reserve that name +for the trapping operation in case we ever want to add it (just like we use `&+` for integer +addition on vectors, even though there is no `+`). We may want to define a floating-point +sum with relaxed semantics for accumulation ordering at some point in the future (I plan +to define `sum` as the binary tree sum here--that's the best tradeoff between reproducibility +and performance). + +I dropped `indexOfMinValue` and `indexOfMaxValue` from this proposal for two reasons: +- there's some disagreement about whether or not they're important enough to include +- it's not clear what we should name them; If they're sufficiently important, we probably +want to have them on Collection some day, too, so the bar for the naming pattern that we +establish is somewhat higher. + + -/// True if every lane of the mask is true -func all(_ mask: SIMDMask) -> Bool +## `any` and `all` +### Motivation +`any` and `all` are special reductions that operate on boolean vectors (`SIMDMask`). They +return `true` if and only if *any* (or *all*) lanes of the boolean vector are `true`. These are +used to do things like branch around edge-case fixup: +```swift +if any(x .< 0) { // handle negative x } ``` -These two are defined as free functions, because at use sites they read significantly more -clearly like `if any(x .< 0)` than, e.g. `if (x .< 0).any()`. We could consider using a -more verbose spelling like `if (x .< 0).anyIsTrue( )`, but these functions are used -quite heavily, and there's a strong preference from the teams we've been working with for -the concise free function spellings. -We would also like to add: +### Detailed design +`any` and `all` are free functions: ```swift -extension SIMD where Scalar: Comparable { - func indexOfMinValue() -> Int - func indexOfMaxValue() -> Int +public func any(_ mask: SIMDMask) -> Bool { + return mask._storage.min < 0 +} + +public func all(_ mask: SIMDMask) -> Bool { + return mask._storage.max < 0 } ``` -The exact spelling of these is not super important to our would-be clients, and I'm not -really wedded to these names. I would love to get some suggestions for them. -### Min, max, clamp +### Alternatives Considered +*Why* are `any` and `all` free functions while `max` and `min` and `sum` are member +properties? Because of readability in their typical use sites. `min`, `max`, and `sum` are +frequently applied to a named value: ```swift -extension SIMD where Scalar: Comparable { - static func min(_ lhs: Self, _ rhs: Self) -> Self - static func max(_ lhs: Self, _ rhs: Self) -> Self - mutating func clamp(to range: ClosedRange) - func clamped(to range: ClosedRange) -> Self - mutating func clamp(lowerBound: Self, upperBound: Self) - func clamped(lowerBound: Self, upperBound: Self) -> Self +let accumulator = /* do some work */ +return accumulator.sum +``` +`any` and `all` are most often used with nameless comparison results: +```swift +if any(x .< minValue .| x .> maxValue) { + // handle special cases } ``` -These are all fairly simple to implement in terms of the existing -`replacing(with: where:)`, but for two factors: first, getting them right for floating-point -types is a little bit subtle, and second these are so heavily used that it makes sense to -have an actual API for them, rather than leaving each developer to implement them on their -own. +To my mind, this would read significantly less clearly as +```swift +if (x .< minValue .| x .> maxValue).any` { +``` +or +```swift +if (x .< minValue .| x .> maxValue).anyIsTrue` { +``` +because there's no "noun" that the property applies to. There was a proposal in the fall +to make them static functions on `Bool` so that one could write +```swift +if .any(x .< minValue) { +} +``` +but I'm not convinced that's actually better than a free function. + + -### "Swizzles" aka "Permutes" -Early drafts of the previous proposal for SIMD had the following initializer: +## `min`, `max`, and `clamp` +### Motivation +We have lanewise arithmetic on SIMD types, but we don't have lanewise `min` and `max`. +We're also missing `clamp` to restrict values to a specified range. + +### Detailed design ```swift -init(gathering source: D, at index: I) -where D : SIMDVector, D.Element == Element, - I : SIMDIntegerVector & SIMDVectorN { - self.init() - for i in 0 ..< count { - if index[i] >= 0 && index[i] < source.count { - self[i] = source[Int(index[i])] - } +extension SIMD where Scalar: Comparable { + /// The lanewise minimum of two vectors. + /// + /// Each element of the result is the minimum of the corresponding + /// elements of the inputs. + public static func min(_ lhs: Self, _ rhs: Self) -> Self + + /// The lanewise maximum of two vectors. + /// + /// Each element of the result is the maximum of the corresponding + /// elements of the inputs. + public static func max(_ lhs: Self, _ rhs: Self) -> Self + + /// Replace any values less than lowerBound with lowerBound, and any + /// values greater than upperBound with upperBound. + /// + /// For floating-point vectors, `.nan` is replaced with `lowerBound`. + public mutating func clamp(lowerBound: Self, upperBound: Self) { + self = self.clamped(lowerBound: lowerBound, upperBound: upperBound) + } + + /// The vector formed by replacing any values less than lowerBound + /// with lowerBound, and any values greater than upperBound with + /// upperBound. + /// + /// For floating-point vectors, `.nan` is replaced with `lowerBound`. + public func clamped(lowerBound: Self, upperBound: Self) -> Self { + return Self.min(upperBound, Self.max(lowerBound, self)) } } ``` -it was removed from later drafts because the naming wasn't quite right, but it's also not -quite implementable with the "generic" SIMD structure that the community settled on. In -particular, we can't enforce the constraint that the index vector (`I`) has the same number -of elements as the vector type being initialized, because rather than having a -`SIMDVectorN` protocol conformance, we just have the `SIMDN` type. -We can work around this by moving the init down to the types themselves, at the cost -of some code repetition. This is a critical operation for writing efficient SIMD code, -so we definitely want to provide it. In addition, we hope that it can eventually form the -backing implementation for arbitrary named compile-time swizzles like `v.xyxy`. +### Alternatives Considered +These could be free functions, but that introduces an ambiguity if someone retroactively +conforms SIMD types to Comparable because they want lexicographic ordering. + +They could be spelled out `lanewiseMaximum` or similar, to clarify that they operate +lanewise (Chris suggested this in the pitch thread), but we don't spell out `+` as +"lanewise-plus", so it seems weird to do it here. The default assumption is that SIMD +operations are lanewise. -I'm exploring a few ways to add this functionality now, but I'm interested in getting other -thoughts from the community. + -### "one" -One last requestion from internal developers is +## `.one` +### Motivation +SIMD types cannot be `ExpressibleByIntegerLiteral` (it results in type system +ambiguity for common expressions). We already have `.zero`, so adding `.one` makes +sense as a convenience. + +### Detailed design ```swift extension SIMD where Scalar: ExpressibleByIntegerLiteral { - static var one: Self { return Self(repeating: 1) } + public static var one: Self { + return Self(repeating: 1) + } } ``` -This is a fairly niche feature, but gets used heavily enough that folks would really -appreciate having a short name for it. `.zero` already exists from `AdditiveArithmetic`, -which makes this seem somewhat reasonable to me. + +### Alternatives Considered +- Do nothing. We don't *need* this, but it has turned out to be a useful convenience. +- Why stop at `one`? Why not `two`? Because that way lies madness. + + + +## `copysign` +### Motivation +Copysign is equivalent to the `init(signOf: magnitudeOf:)` init on `FloatingPoint` +scalar types. It turns out to be a bit of a pain to implement, and folks would like to have it +on vectors. This is more of an addition to SE-0246, but that's already nearly finished, and +they are quite useful for working with vectors, so I'm tucking them in here. + +### Detailed design +```swift +// These two functions will actually go into the Math module, assuming +// that SE-0246 is approved. +public func copysign(_ magnitude: V, _ sign: V) -> V +where V: SIMD, V.Scalar: FloatingPoint + +public func copysign(_ magnitude: T, _ sign: T) -> T +where T: FloatingPoint + +``` + +### Alternatives Considered +Do nothing. Would make life a bit more painful, but we could always add them sometime +in the future. + +## Source compatibility + +These are all purely additiive changes with no effect on source stability. + +## Effect on ABI stability + +These are all purely additiive changes with no effect on source stability. + +## Effect on API resilience + +These are all purely additiive changes with no effect on source stability. + +## Alternatives Considered + +The pitch for this proposal included some operations for loading and storing from a +collection. As Jordan pointed out in the pitch thread, we already have an init from +Sequence, which together with slices makes the load mostly irrelevant. The store +operation did not have satisfactory naming, and I would like to come up with a better +pattern for these that handles iterating over a sequence of SIMD vectors loaded from +a collection of scalars and storing them out as a single pattern, rather than building +it up one piece at a time. From 3e3d1e5fce1c0a4f5975410c1e1f2c970ccc5288 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 20 Mar 2019 21:08:14 -0700 Subject: [PATCH 1050/4563] Address feedback from Brent, Joe, David, John, Jordan, Slava. --- proposals/NNNN-property-delegates.md | 240 ++++++++++++--------------- 1 file changed, 104 insertions(+), 136 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 60726d6b82..a9c1fc64ae 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -4,6 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: **TBD** * Status: **Awaiting review** +* Implementation: [PR #23440](https://github.com/apple/swift/pull/23440) ## Introduction @@ -12,7 +13,7 @@ Rather than hardcode a fixed set of patterns into the compiler, we should provide a general "property delegate" mechanism to allow these patterns to be defined as libraries. -This is an alternative approach to some of the problems intended to be addressed by the [2015-6 property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md). Some of the examples are the same, but this proposal and design is meant to be freestanding. However, there is a section that discusses the substantive differences from that design at the end of this proposal. +This is an alternative approach to some of the problems intended to be addressed by the [2015-2016 property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md). Some of the examples are the same, but this proposal and design is meant to be freestanding. However, there is a section that discusses the substantive differences from that design at the end of this proposal. [Pitch](TODO)
    @@ -142,7 +143,7 @@ var foo by Lazy = 1738 translates to: ```swift -private var $foo: Lazy = Lazy(initialValue: 1738) +var $foo: Lazy = Lazy(initialValue: 1738) var foo: Int { get { return $foo.value } set { $foo.value = newValue } @@ -166,8 +167,8 @@ extension Lazy { $foo.reset(42) ``` -The synthesized storage property will be `private` by -default. However, it can be given more lenient access by putting the +Like other declarations, the synthesized storage property will be +`internal` by default, or the access level of the original property if it is less than `internal`. However, it can be given more lenient access by putting the access level after `by`, e.g., ```swift @@ -175,6 +176,18 @@ access level after `by`, e.g., public var foo: Int by public Lazy = 1738 ``` +The property delegate instance can be initialized directly by providing the initializer arguments in parentheses after the name. For example, + +```swift +extension Lazy { + init(closure: @escaping () -> Value) { + self = .uninitialized(closure) + } +} + +public var foo: Int by Lazy(closure: { 42 }) +``` + ## Examples Before describing the detailed design, here are some more examples of @@ -307,7 +320,7 @@ struct UnsafeMutablePointer { } } -var someInt: Int by UnsafeMutablePointer = ... +var someInt: Int by UnsafeMutablePointer(mutating: addressOfAnInt) someInt = 17 // equivalent to someInt.value = 17 ``` @@ -336,44 +349,35 @@ delegate instance. Introducing a property delegate to a property makes that property computed (with a getter/setter) and introduces a stored property whose type uses the delegate type. That stored property can be initialized -in one of three ways: - -1. Via the property delegate type's zero-parameter `init()` -initializer, if the property is not initialized. This -allows a property delegate type to provide implicit -initialization. For example, the `DelayedMutable` property delegate -type depends on this delegate to allow one to avoid the normal -definition-initialization requirements and implement the -delayed-initialization pattern. - -2. Via a value of the original property's type (e.g., `Int` in `var -foo: Int by Lazy`, using the the property delegate type's -`init(initialValue:)` initializer. That initializer must have a single -parameter of the property delegate type's generic type parameter (or -be an `@autoclosure` thereof). When `init(initialValue:)` is present, -is is always used for the initial value provided on the property -declaration. For example: - -```swift -var foo by Lazy = 17 - -// ... implemented as -private var $foo: Lazy = .init(initialValue: 17) -var foo: Int { /* access via $foo.value as described above */ } -``` - -3. Via a value of the property delegate type, using any initializer of -the property delegate type. This applies for property delegate types -that do not have an `init(initialValue:)`, such as with the `UnsafeMutablePointer` example: - -```swift -var addressOfInt: UnsafePointer = ... -var someInt: Int by UnsafeMutablePointer = .init(mutating: addressOfInt) - -// ... implemented as -private var $someInt: UnsafeMutablePointer = .init(mutating: addressOfInt) -var someInt: Int { /* access via $someInt.value */ } -``` +in one of two ways: + +1. Via a value of the original property's type (e.g., `Int` in `var + foo: Int by Lazy`, using the the property delegate type's + `init(initialValue:)` initializer. That initializer must have a single + parameter of the property delegate type's generic type parameter (or + be an `@autoclosure` thereof). When `init(initialValue:)` is present, + is is always used for the initial value provided on the property + declaration. For example: + + ```swift + var foo by Lazy = 17 + + // ... implemented as + var $foo: Lazy = Lazy(initialValue: 17) + var foo: Int { /* access via $foo.value as described above */ } + ``` + + +2. Via a value of the property delegate type, by placing the call arguments after the property delegate type: + + ```swift + var addressOfInt: UnsafePointer = ... + var someInt: Int by UnsafeMutablePointer(mutating: addressOfInt) + + // ... implemented as + var $someInt: UnsafeMutablePointer = UnsafeMutablePointer(mutating: addressOfInt) + var someInt: Int { /* access via $someInt.value */ } + ``` ### Type inference with delegates @@ -385,17 +389,16 @@ example: ```swift var foo by Lazy = 17 // type inference as in... -private var $foo: Lazy = .init(initialValue: 17) +var $foo: Lazy = Lazy(initialValue: 17) // infers the type of 'foo' to be 'Int' ``` -The same applies for property delegate types that don't have an -`init(initialValue:)`, e.g., +The same applies when directly initialize the property delegate type, e.g., ```swift -var someInt by UnsafeMutable = .init(pointer: addressOfInt) +var someInt by UnsafeMutablePointer(mutating: addressOfInt) // type inference as in... -private var $someInt: UnsafeMutable = .init(pointer: addressOfInt) +var $someInt: UnsafeMutablePointer = UnsafeMutablePointer.init(mutating: addressOfInt) // infers the type of 'someInt' to be 'Int' ``` @@ -407,7 +410,10 @@ keyword: ```text pattern-initializer ::= pattern property-delegate[opt] initializer[opt] -property-delegate ::= 'by' access-level-modifier[opt] type +property-delegate ::= 'by' access-level-modifier[opt] type property-delegate-init[opt] + +property-delegate-init ::= parenthesized-expression + ::= tuple-expression ``` The *type* in a *property-delegate* must refer to a 'property delegate @@ -416,20 +422,11 @@ type'_ without specifying a generic argument. The `internal`, or `public`, but cannot be less restrictive than the property declaration itself. -Properties cannot be declared using delegates inside protocols. A -property with a delegate that is declared within a class must be -`final` and cannot override another property. A property with a -delegate may not declare any accessors. - ### Mutability of properties with delegates -A property with a delegate can be specified with either `var` or -`let`. If it spelled with `let`, it cannot be mutated---even if the -specified property delegate type has a `nonmutating set` on its -`value` property. Similarly, a property spelled with `var` but whose -delegate type has a `value` that lacks a setter (or has an -inaccessible setter) cannot be mutated directly. However, the -synthesized storage property could be mutated. +A property with a delegate must be introduced with the `var` keyword. +If the `value` property of the behavior type lacks a setter (or the setter is inaccessible), `value` will not have a setter. However, the +synthesized storage property could still be mutated. ### Out-of-line initialization of properties with delegates @@ -459,6 +456,7 @@ initialization](https://developer.apple.com/swift/blog/?id=28) (DI) apply to properties that have delegates. Let's expand the example of `x` above to include a re-assignment and use `var`: +```swift var x2: Int by Lazy // ... x2 = 17 // okay, treated as $x2 = .init(initialValue: 17) @@ -466,16 +464,6 @@ x2 = 17 // okay, treated as $x2 = .init(initialValue: 17) x2 = 42 // okay, treated as x2 = 42 (calls the Lazy.value setter) ``` -A property with a delegate whose delegate type has a zero-parameter -initializer (`init()`) will be implicitly initialized via the -zero-parameter initializer. For example, - -```swift -var z: Int by DelayedMutable -/* implicitly initialized with.. */ -$z = DelayedMutable.init() -``` - ### Memberwise initializers Structs implicitly declare memberwise initializers based on the stored @@ -495,7 +483,7 @@ initializer for a property with a delegate is determined as follows: (i.e., a specialization of the delegate type). In this case, the access level of the implicit initializer may need to be adjusted to account for a visibility of the delegate: if the delegate is private - (e.g., `var x: Int by UnsafeMutable`), then the implicit memberwise + (e.g., `var x: Int by private UnsafeMutablePointer`), then the implicit memberwise initializer will be `private`. For example: @@ -518,13 +506,38 @@ follows the same rules, using the underlying `value` of the property delegate type contains an `init(initialValue:)` and the synthesized storage property's type otherwise. +### $ identifiers + +Currently, identifiers starting with a `$` are not permitted in Swift programs. Today, such identifiers are only used in LLDB, where they can be used to name persistent values within a debugging session. + +This proposal loosens these rules slightly: the Swift compiler will introduce identifiers that start with `$` (for the synthesized storage property), and Swift code can reference those properties. However, Swift code cannot declare any new entities with an identifier that begins with `$`. For example: + +```swift +var x by Lazy = 17 +print($x) // okay to refer to compiler-defined $x +let $y = 17 // error: cannot declare entity with $-prefixed name '$y' +``` + +### Restrictions on the use of property delegates + +There are a number of restrictions on the use of property delegates when defining a property: + +* A property with a delegate may not declared inside a protocol. +* An instance property with a delegate may not declared inside an extension. +* An instance property may not be declared in an `enum`. +* A property with a delegate that is declared within a class must be +`final` and cannot override another property. +* A property with a delegate may not declare any accessors. +* A property with a delegate cannot be `lazy`, `@NSCopying`, or `@NSManaged`. +* A property with a delegate must be the only property declared within its enclosing declaration (e.g., `var (x, y) by Lazy = /* ... */` is ill-formed) + ## Impact on existing code By itself, this is an additive feature that doesn't impact existing code. However, with some of the property delegates suggested, it can potentially obsolete existing, hardcoded language features. `@NSCopying` could be completely replaced by a `Copying` -property delegate type introduced in the `ObjectiveC` module. `lazy` +property delegate type introduced in the `Foundation` module. `lazy` cannot be completely replaced because it's initial value can refer to the `self` of the enclosing type; see 'deferred evaluation of initialization expressions_. However, it may still make sense to @@ -562,78 +575,33 @@ protocol, we don't know of any useful generic algorithms or data structures that seem to be implemented in terms of only `Propertydelegate`. -## Future directions - -The functionality proposed here is quite broad, so to attempt to minimize -the review burden of the initial proposal, I've factored out several -aspects for separate consideration: - -### Composing delegates - -It is useful to be able to compose delegates, for instance, to have a -lazy property that's also synchronized. Linear composition could be -supported by allowing delegates to stack, each referring to the -underlying property beneath it by `value`. However, this form of -composition can be treacherous, since it allows for "incorrect" -compositions of delegates. One of `lazy • synchronized` or -`synchronized • lazy` is going to do the wrong thing. With the current -proposal, composed delegates must be explicitly implemented as a new -property delegate type. - -### Deferred evaluation of initialization expressions - -This proposal does not suggest changing the allowed operations inside -initialization expressions; in particular, an initialization of an -instance property may not refer to `self` or other instance properties or -methods, due to the potential for the expression to execute before the -value is fully initialized: - -```swift -struct Foo { - var a = 1 - var b = a // Not allowed - var c = foo() // Not allowed - - func foo() { } -} -``` - -This is inconvenient for delegates like `lazy` that only ever evaluate the -initial value expression after the true initialization phase has completed, -and where it's desirable to reference `self` to lazily initialize. -delegates could be extended to annotate the initializer as "deferred", -which would allow the initializer expression to refer to `self`, while -preventing the initializer expression from being evaluated at initialization -time. (If we consider delegates to be essentially always fragile, this could -be inferred from the delegate implementation.) - -## Changes from the 2015-6 property delegates design +## The 2015-2016 property behaviors design -Property delegates were [proposed and -reviewed](https://github.com/apple/swift-evolution/blob/dc52ebb3ef85108eecf009248c130eaa0c43d6d4/proposals/0030-property-delegate-decls.md) +Property delegates address a similar set of use cases to *property behaviors*, which were [proposed and +reviewed](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md) in late 2015/early 2016. The design did not converge, and the proposal was deferred. This proposal picks up the thread, using much of the -same motivation and core design ideas, but attempting to simplify the +same motivation and some design ideas, but attempting to simplify the feature and narrow the feature set. Some substantive differences from the prior proposal are: -* delegates were introduced into a property with the `[delegate]` +* Behaviors were introduced into a property with the `[behavior]` syntax, rather than the `by delegate` syntax described here. See the - prior iteration of the proposal for other discussions of syntax. -* delegates are always expressed by a (generic) type. The prior design - iteration had a new kind of declaration (introduced by the - `delegate` keyword). Having a new kind of declaration allowed for + property behaviors proposal for more information. +* Delegates are always expressed by a (generic) type. Property behaviors + had a new kind of declaration (introduced by the + `behavior` keyword). Having a new kind of declaration allowed for the introduction of specialized syntax, but it also greatly increased the surface area (and implementation cost) of the proposal. Using a generic type makes property delegates more of a - syntactic-sugar feature. -* delegates cannot declare new kinds of accessors (e.g., the - `didChange` example from the prior proposal). -* delegates used for properties declared within a type cannot refer to + syntactic-sugar feature that is easier to implement and explain. +* Delegates cannot declare new kinds of accessors (e.g., the + `didChange` example from the property behaviors proposal). +* Delegates used for properties declared within a type cannot refer to the `self` of their enclosing type. This eliminates some use cases (e.g., implementing a `Synchronized` property delegate type that - uses a lock defined on the enclosing type), but simplifies the - proposal. -* delegates in this proposal can be initialized out-of-line, and one - can use the `$`-prefixed name to refer to the delegate - instance. These were future directions in the prior proposal. + uses a lock defined on the enclosing type), but simplifies the + design. +* Delegates can be initialized out-of-line, and one + can use the `$`-prefixed name to refer to the storage property. + These were future directions in the property behaviors proposal. From 0d2231543cd0ea10b1154b475ed86bcd7fa98b50 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Wed, 20 Mar 2019 23:10:38 -0700 Subject: [PATCH 1051/4563] Mark SE-0245 as been Accepted --- proposals/0245-array-uninitialized-initializer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0245-array-uninitialized-initializer.md b/proposals/0245-array-uninitialized-initializer.md index afccc1eb0c..5f480da0c2 100644 --- a/proposals/0245-array-uninitialized-initializer.md +++ b/proposals/0245-array-uninitialized-initializer.md @@ -3,7 +3,7 @@ * Proposal: [SE-0245](0245-array-uninitialized-initializer.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active review (March 11 - 18, 2019)** +* Status: **Accepted** * Previous Proposal: [SE-0223](https://github.com/apple/swift-evolution/blob/master/proposals/0223-array-uninitialized-initializer.md) * Implementation: [apple/swift#23134](https://github.com/apple/swift/pull/23134) * Bug: [SR-3087](https://bugs.swift.org/browse/SR-3087) From 6b61568264355a55001e179440a39e98f0b6268f Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 21 Mar 2019 16:46:31 +0000 Subject: [PATCH 1052/4563] [SE-0244] Update the implementation link --- proposals/0244-opaque-result-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index c4840ef5f2..656ee0103b 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Active review (March 5...March 15, 2019)** -* Implementation: [apple/swift#21137](https://github.com/apple/swift/pull/21137) +* Implementation: [apple/swift#22072](https://github.com/apple/swift/pull/22072) * Toolchain: https://github.com/apple/swift/pull/21137#issuecomment-468118328 ## Introduction From 3afc4c68a4062ff045415f5eafb9d4956b30551b Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 21 Mar 2019 13:12:35 -0400 Subject: [PATCH 1053/4563] Extend review for SE-0246 --- proposals/0246-mathable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0246-mathable.md b/proposals/0246-mathable.md index a8edcaac99..28bb79dad3 100644 --- a/proposals/0246-mathable.md +++ b/proposals/0246-mathable.md @@ -3,7 +3,7 @@ * Proposal: [SE-0246](0246-mathable.md) * Author: [Stephen Canon](https://github.com/stephentyrone) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (March 11 - 20, 2019)** +* Status: **Active review (March 11 – 25th, 2019)** * Implementation: [apple/swift#23140](https://github.com/apple/swift/pull/23140) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/b5bbc5ae1f53189641951acfd50870f5b886859e/proposals/0246-mathable.md) From 8e605d17be02480ba289e571b45c2a596141f761 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Thu, 21 Mar 2019 12:42:31 -0700 Subject: [PATCH 1054/4563] Update 0000-simd-additions.md --- proposals/0000-simd-additions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0000-simd-additions.md b/proposals/0000-simd-additions.md index e7a6a08c97..c523aef852 100644 --- a/proposals/0000-simd-additions.md +++ b/proposals/0000-simd-additions.md @@ -3,7 +3,8 @@ * Proposal: [SE-NNNN](NNNN-simd-additions.md) * Authors: [Stephen Canon](https://github.com/stephentyrone) * Review Manager: TBD -* Status: [**Implemented**](https://github.com/apple/swift/pull/23421) +* Status: Awaiting Review +* Implementation: [swift/#23421](https://github.com/apple/swift/pull/23421) ## Introduction From d68342c0b3151fda5b6dc833944021196948f3ea Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Thu, 21 Mar 2019 12:42:57 -0700 Subject: [PATCH 1055/4563] Update 0000-simd-additions.md --- proposals/0000-simd-additions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-simd-additions.md b/proposals/0000-simd-additions.md index c523aef852..721c9aa539 100644 --- a/proposals/0000-simd-additions.md +++ b/proposals/0000-simd-additions.md @@ -4,7 +4,7 @@ * Authors: [Stephen Canon](https://github.com/stephentyrone) * Review Manager: TBD * Status: Awaiting Review -* Implementation: [swift/#23421](https://github.com/apple/swift/pull/23421) +* Implementation: [apple/swift#23421](https://github.com/apple/swift/pull/23421) ## Introduction From 3f382514647e16be8520af357f6f7798c9aa2268 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Thu, 21 Mar 2019 15:45:06 -0400 Subject: [PATCH 1056/4563] Update 0000-simd-additions.md --- proposals/0000-simd-additions.md | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/proposals/0000-simd-additions.md b/proposals/0000-simd-additions.md index 721c9aa539..eef95af6d4 100644 --- a/proposals/0000-simd-additions.md +++ b/proposals/0000-simd-additions.md @@ -25,7 +25,6 @@ considered section. 5. [Lanewise `min`, `max`, and `clamp`](#minMaxClamp) 6. [`.one`](#one) 7. [`any` and `all`](#anyAndAll) -8. [`copysign`](#copysign)
    @@ -349,34 +348,9 @@ extension SIMD where Scalar: ExpressibleByIntegerLiteral { - Do nothing. We don't *need* this, but it has turned out to be a useful convenience. - Why stop at `one`? Why not `two`? Because that way lies madness. - - -## `copysign` -### Motivation -Copysign is equivalent to the `init(signOf: magnitudeOf:)` init on `FloatingPoint` -scalar types. It turns out to be a bit of a pain to implement, and folks would like to have it -on vectors. This is more of an addition to SE-0246, but that's already nearly finished, and -they are quite useful for working with vectors, so I'm tucking them in here. - -### Detailed design -```swift -// These two functions will actually go into the Math module, assuming -// that SE-0246 is approved. -public func copysign(_ magnitude: V, _ sign: V) -> V -where V: SIMD, V.Scalar: FloatingPoint - -public func copysign(_ magnitude: T, _ sign: T) -> T -where T: FloatingPoint - -``` - -### Alternatives Considered -Do nothing. Would make life a bit more painful, but we could always add them sometime -in the future. - ## Source compatibility -These are all purely additiive changes with no effect on source stability. +These are all purely additive changes with no effect on source stability. ## Effect on ABI stability From ad711ddc9b0e56c0935ea34d75544aed793e6fea Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 21 Mar 2019 14:26:08 -0700 Subject: [PATCH 1057/4563] Minor cleanups based on feedback --- proposals/NNNN-property-delegates.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index a9c1fc64ae..310d5e7f11 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -13,7 +13,7 @@ Rather than hardcode a fixed set of patterns into the compiler, we should provide a general "property delegate" mechanism to allow these patterns to be defined as libraries. -This is an alternative approach to some of the problems intended to be addressed by the [2015-2016 property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md). Some of the examples are the same, but this proposal and design is meant to be freestanding. However, there is a section that discusses the substantive differences from that design at the end of this proposal. +This is an alternative approach to some of the problems intended to be addressed by the [2015-2016 property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md). Some of the examples are the same, but this proposal a completely different approach designed to be simpler, easier to understand for users, and less invasive in the compiler implementation. There is a section that discusses the substantive differences from that design at the end of this proposal. [Pitch](TODO)
    @@ -306,7 +306,7 @@ This implementation would address the problem detailed in ### `Unsafe(Mutable)Pointer` -The `Unsafe(Mutable)Pointer` types could be augmented to be property delegate types, allowing one to access the referenced value directly. For example: +The `Unsafe(Mutable)Pointer` types could be augmented to be property delegate types, allowing one to access the referenced value directly using the `by` syntax. For example: ```swift From 38ecd019705eea553813d6dd39c48b60f869af9a Mon Sep 17 00:00:00 2001 From: "Steve (Numerics) Canon" Date: Thu, 21 Mar 2019 20:40:52 -0400 Subject: [PATCH 1058/4563] Updates for math functions proposal. Moved static implementations out of the "Math" pseudo-namespace and back onto the types themselves. This lets us drop a layer of protocol indirection that would have been fixed in the ABI "forever". --- proposals/0246-mathable.md | 133 ++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 46 deletions(-) diff --git a/proposals/0246-mathable.md b/proposals/0246-mathable.md index 28bb79dad3..23b0afaa1b 100644 --- a/proposals/0246-mathable.md +++ b/proposals/0246-mathable.md @@ -5,7 +5,7 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (March 11 – 25th, 2019)** * Implementation: [apple/swift#23140](https://github.com/apple/swift/pull/23140) -* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/b5bbc5ae1f53189641951acfd50870f5b886859e/proposals/0246-mathable.md) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/b5bbc5ae1f53189641951acfd50870f5b886859e/proposals/0246-mathable.md) [2](https://github.com/apple/swift-evolution/blob/3afc4c68a4062ff045415f5eafb9d4956b30551b/proposals/0246-mathable.md) ## Introduction @@ -68,23 +68,54 @@ There are four pieces of this proposal: first, we introduce the protocol `ElementaryFunctions`: ```swift public protocol ElementaryFunctions { - associatedtype Math: ElementaryFunctionHooks where Math.Value == Self + + /// The cosine of `x`. + static func cos(_ x: Self) -> Self + + /// The sine of `x`. + static func sin(_ x: Self) -> Self + + /// The tangent of `x`. + static func tan(_ x: Self) -> Self + + ... } ``` -The associatedtype provides implementation hooks for the elementary functions -so that they are available as "namespaced" static functions like: +Conformance to this protocol means that the elementary functions are available as +static functions: ```swift -(swift) Float.Math.exp(1) +(swift) Float.exp(1) // r0 : Float = 2.7182817 ``` -All of the standard library `FloatingPoint` types conform to -`ElementaryFunctions`; SIMD types conform conditionally if their `Scalar` -type conforms, and a planned `Complex` type would also conform. +(For the full set of functions provided, see Detailed Design below). All of the standard +library `FloatingPoint` types conform to `ElementaryFunctions`; a future `Complex` +type would also conform. `SIMD` types do not conform themselves, but the operations +are defined on them when their scalar type conforms to the protocol. The second piece of the proposal is the protocol `Real`: ``` -public protocol Real: FloatingPoint, ElementaryFunctions -where Math: RealFunctionHooks { +public protocol Real: FloatingPoint, ElementaryFunctions { + + /// `atan(y/x)` with quadrant fixup. + /// + /// There is an infinite family of angles whose tangent is `y/x`. + /// `atan2` selects the representative that is the angle between + /// the vector `(x, y)` and the real axis in the range [-π, π]. + static func atan2(y: Self, x: Self) -> Self + + /// The error function evaluated at `x`. + static func erf(_ x: Self) -> Self + + /// The complimentary error function evaluated at `x`. + static func erfc(_ x: Self) -> Self + + /// sqrt(x*x + y*y) computed without undue overflow or underflow. + /// + /// Returns a numerically precise result even if one or both of x*x or + /// y*y overflow or underflow. + static func hypot(_ x: Self, _ y: Self) -> Self + + ... } ``` This protocol does not add much API surface, but it is what most users will @@ -122,119 +153,118 @@ The full API provided by `ElementaryFunctions` is as follows: /// /// For real types, if `x` is negative the result is `.nan`. For complex /// types there is a branch cut on the negative real axis. -static func sqrt(_ x: Value) -> Value +static func sqrt(_ x: Self) -> Self /// The cosine of `x`, interpreted as an angle in radians. -static func cos(_ x: Value) -> Value +static func cos(_ x: Self) -> Self /// The sine of `x`, interpreted as an angle in radians. -static func sin(_ x: Value) -> Value +static func sin(_ x: Self) -> Self /// The tangent of `x`, interpreted as an angle in radians. -static func tan(_ x: Value) -> Value +static func tan(_ x: Self) -> Self /// The inverse cosine of `x` in radians. -static func acos(_ x: Value) -> Value +static func acos(_ x: Self) -> Self /// The inverse sine of `x` in radians. -static func asin(_ x: Value) -> Value +static func asin(_ x: Self) -> Self /// The inverse tangent of `x` in radians. -static func atan(_ x: Value) -> Value +static func atan(_ x: Self) -> Self /// The hyperbolic cosine of `x`. -static func cosh(_ x: Value) -> Value +static func cosh(_ x: Self) -> Self /// The hyperbolic sine of `x`. -static func sinh(_ x: Value) -> Value +static func sinh(_ x: Self) -> Self /// The hyperbolic tangent of `x`. -static func tanh(_ x: Value) -> Value +static func tanh(_ x: Self) -> Self /// The inverse hyperbolic cosine of `x`. -static func acosh(_ x: Value) -> Value +static func acosh(_ x: Self) -> Self /// The inverse hyperbolic sine of `x`. -static func asinh(_ x: Value) -> Value +static func asinh(_ x: Self) -> Self /// The inverse hyperbolic tangent of `x`. -static func atanh(_ x: Value) -> Value +static func atanh(_ x: Self) -> Self /// The exponential function applied to `x`, or `e**x`. -static func exp(_ x: Value) -> Value +static func exp(_ x: Self) -> Self /// Two raised to to power `x`. -static func exp2(_ x: Value) -> Value +static func exp2(_ x: Self) -> Self /// Ten raised to to power `x`. -static func exp10(_ x: Value) -> Value +static func exp10(_ x: Self) -> Self /// `exp(x) - 1` evaluated so as to preserve accuracy close to zero. -static func expm1(_ x: Value) -> Value +static func expm1(_ x: Self) -> Self /// The natural logarithm of `x`. -static func log(_ x: Value) -> Value +static func log(_ x: Self) -> Self /// The base-two logarithm of `x`. -static func log2(_ x: Value) -> Value +static func log2(_ x: Self) -> Self /// The base-ten logarithm of `x`. -static func log10(_ x: Value) -> Value +static func log10(_ x: Self) -> Self /// `log(1 + x)` evaluated so as to preserve accuracy close to zero. -static func log1p(_ x: Value) -> Value +static func log1p(_ x: Self) -> Self /// `x**y` interpreted as `exp(y * log(x))` /// /// For real types, if `x` is negative the result is NaN, even if `y` has /// an integral value. For complex types, there is a branch cut on the /// negative real axis. -static func pow(_ x: Value, _ y: Value) -> Value +static func pow(_ x: Self, _ y: Self) -> Self /// `x` raised to the `n`th power. /// /// The product of `n` copies of `x`. -static func pow(_ x: Value, _ n: Int) -> Value +static func pow(_ x: Self, _ n: Int) -> Self /// The `n`th root of `x`. /// /// For real types, if `x` is negative and `n` is even, the result is NaN. /// For complex types, there is a branch cut along the negative real axis. -static func root(_ x: Value, _ n: Int) -> Value +static func root(_ x: Self, _ n: Int) -> Self ``` -`Real` builds on this set by adding the following additional operations on -`RealFunctionHooks` that are either difficult to implement for complex types or only make -sense for real types: +`Real` builds on this set by adding the following additional operations that are either +difficult to implement for complex types or only make sense for real types: ```swift /// `atan(y/x)` with quadrant fixup. /// /// There is an infinite family of angles whose tangent is `y/x`. `atan2` /// selects the representative that is the angle between the vector `(x, y)` /// and the real axis in the range [-π, π]. -static func atan2(y: Value, x: Value) -> Value +static func atan2(y: Self, x: Self) -> Self /// The error function evaluated at `x`. -static func erf(_ x: Value) -> Value +static func erf(_ x: Self) -> Self /// The complimentary error function evaluated at `x`. -static func erfc(_ x: Value) -> Value +static func erfc(_ x: Self) -> Self /// sqrt(x*x + y*y) computed without undue overflow or underflow. /// /// Returns a numerically precise result even if one or both of x*x or /// y*y overflow or underflow. -static func hypot(_ x: Value, _ y: Value) -> Value +static func hypot(_ x: Self, _ y: Self) -> Self /// The gamma function evaluated at `x`. /// /// For integral `x`, `gamma(x)` is `(x-1)` factorial. -static func gamma(_ x: Value) -> Value +static func gamma(_ x: Self) -> Self /// `log(gamma(x))` computed without undue overflow. /// /// `log(abs(gamma(x)))` is returned. To recover the sign of `gamma(x)`, /// use `signGamma(x)`. -static func logGamma(_ x: Value) -> Value +static func logGamma(_ x: Self) -> Self /// The sign of `gamma(x)`. /// @@ -246,7 +276,7 @@ static func logGamma(_ x: Value) -> Value /// essential singularity at infinity; we arbitrarily choose to return /// `.plus` for the sign in those cases. For all other values, `signGamma(x)` /// is `.plus` if `x >= 0` or `trunc(x)` is odd, and `.minus` otherwise. -static func signGamma(_ x: Value) -> FloatingPointSign +static func signGamma(_ x: Self) -> FloatingPointSign ``` These functions directly follow the math library names used in most other languages, as there is not a good reason to break with existing precedent. @@ -365,8 +395,8 @@ old platform hooks to provide binary stability, but will mark them deprecated or This is an additive change. ## Alternatives considered -1. The name `ElementaryFunctions` is a marked improvement on the earlier `Mathable`, but -is still imperfect. As discussed above, the introduction of the `Real` protocol mostly +1. The name `ElementaryFunctions` is a marked improvement on the earlier `Mathable`, +but is still imperfect. As discussed above, the introduction of the `Real` protocol mostly renders this issue moot; most code will be constrained to that instead. 2. The names of these functions are strongly conserved across languages, but they are @@ -400,6 +430,8 @@ of programming languages. Rust and Kotlin do spell it this way, so we wouldn't b alone. It would also avoid using a function name that potentially conflicts (visually or syntactically) with an obvious name for logging facilities. However, depending on font, `ln` is easily confused with `in`, and it breaks the similarity with the other `log` functions. +As an assistance, we will add `ln` in the `Math` module but mark it unavailable, referring +users to `log`. 5. We could put the free functions into the standard library instead of a separate module. Having them in a separate module helps avoid adding stuff to the global namespace @@ -431,3 +463,12 @@ made much sense for non-real arguments. Implementations of `erf`, `erfc`, `gamma 2. `hypot` has been added to `Real`. We would like to have a more general solution for efficiently rescaling computations in the future, but `hypot` is a tool that people can use today. + +3. I have dropped the `.Math` pseudo-namespace associatedtype from the protocols. +In earlier versions of the proposal, the static functions were spelled `Float.Math.sin(x)`. +This was motivated by a desire to avoid "magic" underscore-prefixed implementation +trampolines, while still grouping these functions together under a single `.Math` in +autocompletion lists. Whatever stylistic benefits this might have were judged to be not +worth the extra layer of machinery that would be fixed into the ABI even if we get a "real" +namespace mechanism at some future point. These functions now appear simply as +`Float.sin(x)`. From a68116b36baff9ec9dad34e3937a59d379049809 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Fri, 22 Mar 2019 07:34:12 -0700 Subject: [PATCH 1059/4563] Accept SE-0248. --- proposals/0248-string-gaps-missing-apis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0248-string-gaps-missing-apis.md b/proposals/0248-string-gaps-missing-apis.md index cfae8bd0bc..9e1426cd08 100644 --- a/proposals/0248-string-gaps-missing-apis.md +++ b/proposals/0248-string-gaps-missing-apis.md @@ -3,7 +3,7 @@ * Proposal: [SE-0248](0248-string-gaps-missing-apis.md) * Author: [Michael Ilseman](https://github.com/milseman) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active review (March 13 - 22, 2019)** +* Status: **Accepted** * Implementation: [apple/swift#22869](https://github.com/apple/swift/pull/22869) * Bugs: [SR-9955](https://bugs.swift.org/browse/SR-9955) From a3bbf9275327616e24614fc45baad0bf968155b7 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 22 Mar 2019 11:59:05 -0400 Subject: [PATCH 1060/4563] Mark as SE-0251, put in review --- .../{0000-simd-additions.md => 0251-simd-additions.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{0000-simd-additions.md => 0251-simd-additions.md} (98%) diff --git a/proposals/0000-simd-additions.md b/proposals/0251-simd-additions.md similarity index 98% rename from proposals/0000-simd-additions.md rename to proposals/0251-simd-additions.md index eef95af6d4..4361ca94d1 100644 --- a/proposals/0000-simd-additions.md +++ b/proposals/0251-simd-additions.md @@ -1,9 +1,9 @@ # SIMD additions -* Proposal: [SE-NNNN](NNNN-simd-additions.md) +* Proposal: [SE-0251](0251-simd-additions.md) * Authors: [Stephen Canon](https://github.com/stephentyrone) -* Review Manager: TBD -* Status: Awaiting Review +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: Active Review (March 22nd...April 1st, 2019) * Implementation: [apple/swift#23421](https://github.com/apple/swift/pull/23421) ## Introduction From f363c20b90e5c5cb39b8ccdfccfca88bfbf0a6d3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 22 Mar 2019 10:13:37 -0700 Subject: [PATCH 1061/4563] Add Harlan's user-defaults example for by "by(...)" initialization syntax. --- proposals/NNNN-property-delegates.md | 30 ++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 310d5e7f11..e961052b98 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -20,11 +20,7 @@ This is an alternative approach to some of the problems intended to be addressed ## Motivation We've tried to accommodate several important patterns for properties with -targeted language support, but this support has been narrow in scope and -utility. For instance, Swift provides `lazy` properties as a primitive -language feature, since lazy initialization is common and is often necessary to -avoid having properties be exposed as `Optional`. Without this language -support, it takes a lot of boilerplate to get the same effect: +targeted language support, like `lazy` and `@NSCopying`, but this support has been narrow in scope and utility. For instance, Swift provides `lazy` properties as a primitive language feature, since lazy initialization is common and is often necessary to avoid having properties be exposed as `Optional`. Without this language support, it takes a lot of boilerplate to get the same effect: ```swift struct Foo { @@ -176,18 +172,32 @@ access level after `by`, e.g., public var foo: Int by public Lazy = 1738 ``` -The property delegate instance can be initialized directly by providing the initializer arguments in parentheses after the name. For example, +The property delegate instance can be initialized directly by providing the initializer arguments in parentheses after the name. This could be used, for example, when a particular property delegate requires more setup to provide access to a value (example courtesy of Harlan Haskins): ```swift -extension Lazy { - init(closure: @escaping () -> Value) { - self = .uninitialized(closure) +@propertyDelegate +struct UserDefault { + let key: String + let defaultValue: T + + var value: T { + get { + return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue + } + set { + UserDefaults.standard.set(newValue, forKey: key) + } } } -public var foo: Int by Lazy(closure: { 42 }) +enum GlobalSettings { + static var isFooFeatureEnabled: Bool by UserDefault(key: "FOO_FEATURE_ENABLED", defaultValue: false) + static var isBarFeatureEnabled: Bool by UserDefault(key: "BAR_FEATURE_ENABLED", defaultValue: false) +} ``` +Property delegates can be applied to properties at global, local, or type scope. + ## Examples Before describing the detailed design, here are some more examples of From 910437abbfc75ee51781f74f187f80c53ec9e659 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 22 Mar 2019 10:17:48 -0700 Subject: [PATCH 1062/4563] Fix spelling of "PropertyDelegate" protocol. --- proposals/NNNN-property-delegates.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index e961052b98..67479531b0 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -559,19 +559,19 @@ feature. ### Using a formal protocol instead of `@propertyDelegate` -Instead of a new attribute, we could introduce a `Propertydelegate` +Instead of a new attribute, we could introduce a `PropertyDelegate` protocol to describe the semantic constraints on property delegate types. It might look like this: ```swift -protocol Propertydelegate { +protocol PropertyDelegate { associatedtype Value var value: Value { get } } ``` There are a few issues here. First, a single protocol -`Propertydelegate` cannot handle all of the variants of `value` that +`PropertyDelegate` cannot handle all of the variants of `value` that are implied by the section 'Mutability of properties with delegates'_, because we'd need to cope with `mutating get` as well as `set` and `nonmutating set`. Moreover, protocols don't support optional @@ -580,10 +580,10 @@ forms: one accepting a `Value` and one accepting an `@autoclosure () -> Value`) and `init()`. To cover all of these cases, we would need a several related-but-subtly-different protocols. -The second issue that, even if there were a single `Propertydelegate` +The second issue that, even if there were a single `PropertyDelegate` protocol, we don't know of any useful generic algorithms or data structures that seem to be implemented in terms of only -`Propertydelegate`. +`PropertyDelegate`. ## The 2015-2016 property behaviors design From e9e610a55896dc48c0a50bf4517963fd60835387 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 22 Mar 2019 10:24:53 -0700 Subject: [PATCH 1063/4563] Note that property delegates can be backward-deployed. --- proposals/NNNN-property-delegates.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 67479531b0..97265cea1f 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -555,6 +555,10 @@ introduce a `Lazy` property delegate type to cover many of the common use cases, leaving the more-magical `lazy` as a backward-compatibility feature. +## Backward compatibility + +The property delegates language feature as proposed has no impact on the ABI or runtime. Binaries that use property delegates can be backward-deployed to the Swift 5.0 runtime. + ## Alternatives considered ### Using a formal protocol instead of `@propertyDelegate` From d2ddc94f43e318637220558a2eeae84f715701ff Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 22 Mar 2019 10:38:40 -0700 Subject: [PATCH 1064/4563] Clarify the rules for specifying access of the synthesized storage property --- proposals/NNNN-property-delegates.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 97265cea1f..5ed0bdeba2 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -474,6 +474,20 @@ x2 = 17 // okay, treated as $x2 = .init(initialValue: 17) x2 = 42 // okay, treated as x2 = 42 (calls the Lazy.value setter) ``` +### Access to the storage property + +By default, the synthesized storage property will have `internal` access or the access of the original property, whichever is more restrictive. An alternative access can be provided for the synthesized storage property by specifying the access level after `by`, e.g., + +```swift +// both foo and $foo are publicly visible +public var foo: Int by public Lazy = 1738 + +// bar is publicly visible, $bar is privately visible +public var bar: Int by private Lazy = 1738 +``` + +The specified access level cannot be greater than `public` and cannot be greater than the original property. + ### Memberwise initializers Structs implicitly declare memberwise initializers based on the stored From 47726e77837e7938865c7d9f5e79860c3f79f32b Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 22 Mar 2019 17:43:26 +0000 Subject: [PATCH 1065/4563] [SE-0251] Fix "status not found" warning (+ typos) --- proposals/0251-simd-additions.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0251-simd-additions.md b/proposals/0251-simd-additions.md index 4361ca94d1..36efa6ab9f 100644 --- a/proposals/0251-simd-additions.md +++ b/proposals/0251-simd-additions.md @@ -1,9 +1,9 @@ # SIMD additions * Proposal: [SE-0251](0251-simd-additions.md) -* Authors: [Stephen Canon](https://github.com/stephentyrone) +* Author: [Stephen Canon](https://github.com/stephentyrone) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: Active Review (March 22nd...April 1st, 2019) +* Status: **Active Review (March 22nd...April 1st, 2019)** * Implementation: [apple/swift#23421](https://github.com/apple/swift/pull/23421) ## Introduction @@ -71,7 +71,7 @@ components. ### Detailed design In order to support extending vectors from (n-1) to n components, add the following two -intializers: +initializers: ```swift extension SIMD3 { public init(_ xy: SIMD2, _ z: Scalar) { @@ -354,11 +354,11 @@ These are all purely additive changes with no effect on source stability. ## Effect on ABI stability -These are all purely additiive changes with no effect on source stability. +These are all purely additive changes with no effect on source stability. ## Effect on API resilience -These are all purely additiive changes with no effect on source stability. +These are all purely additive changes with no effect on source stability. ## Alternatives Considered From 21933c054a2e053d8e47d0f2d3b374e811c6442c Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Sat, 23 Mar 2019 23:49:20 -0500 Subject: [PATCH 1066/4563] Marke SE-0245 as implemented. --- proposals/0245-array-uninitialized-initializer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0245-array-uninitialized-initializer.md b/proposals/0245-array-uninitialized-initializer.md index 5f480da0c2..2e19b15560 100644 --- a/proposals/0245-array-uninitialized-initializer.md +++ b/proposals/0245-array-uninitialized-initializer.md @@ -3,7 +3,7 @@ * Proposal: [SE-0245](0245-array-uninitialized-initializer.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Accepted** +* Status: **Implemented (Swift 5.1)** * Previous Proposal: [SE-0223](https://github.com/apple/swift-evolution/blob/master/proposals/0223-array-uninitialized-initializer.md) * Implementation: [apple/swift#23134](https://github.com/apple/swift/pull/23134) * Bug: [SR-3087](https://bugs.swift.org/browse/SR-3087) From b51f03883d014f89f69117cfc5b1ade241654b2e Mon Sep 17 00:00:00 2001 From: Daniel Duan Date: Sun, 24 Mar 2019 15:53:02 -0700 Subject: [PATCH 1067/4563] Fix typo in SE184 Pretty sure this will be discovered after SE-1084 comes around but doesn't hurt to fix it earlier than that. --- proposals/0184-unsafe-pointers-add-missing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0184-unsafe-pointers-add-missing.md b/proposals/0184-unsafe-pointers-add-missing.md index 1d073fe211..38a2e9e4f3 100644 --- a/proposals/0184-unsafe-pointers-add-missing.md +++ b/proposals/0184-unsafe-pointers-add-missing.md @@ -10,7 +10,7 @@ ## Introduction -*This document is a spin-off from a much larger [original proposal](https://github.com/kelvin13/swift-evolution/blob/e888af466c9993de977f6999a131eadd33291b06/proposals/0184-unsafe-pointers-add-missing.md), which covers only those aspects of SE-1084 which do not deal with partial buffer memory state. Designing the partial buffer memory state API clearly requires more work, and has been left out of the scope of this document.* +*This document is a spin-off from a much larger [original proposal](https://github.com/kelvin13/swift-evolution/blob/e888af466c9993de977f6999a131eadd33291b06/proposals/0184-unsafe-pointers-add-missing.md), which covers only those aspects of SE-0184 which do not deal with partial buffer memory state. Designing the partial buffer memory state API clearly requires more work, and has been left out of the scope of this document.* Swift’s pointer types are an important interface for low-level memory manipulation, but the current API design is not very consistent, complete, or convenient. In some places, poor naming choices and overengineered function signatures compromise memory safety by leading users to believe that they have allocated or freed memory when in fact, they have not. This proposal seeks to improve the Swift pointer API by ironing out naming inconsistencies, adding missing methods, and reducing excessive verbosity, offering a more convenient, more sensible, and less bug-prone API. From a3bdcd52824c0d714d66ccfeab2de31896008ed8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 25 Mar 2019 12:54:46 -0700 Subject: [PATCH 1068/4563] Clarify restrictions a bit --- proposals/NNNN-property-delegates.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 5ed0bdeba2..93b31932dc 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -552,8 +552,9 @@ There are a number of restrictions on the use of property delegates when definin * A property with a delegate that is declared within a class must be `final` and cannot override another property. * A property with a delegate may not declare any accessors. -* A property with a delegate cannot be `lazy`, `@NSCopying`, or `@NSManaged`. -* A property with a delegate must be the only property declared within its enclosing declaration (e.g., `var (x, y) by Lazy = /* ... */` is ill-formed) +* A property with a delegate cannot be `lazy`, `@NSCopying`, `@NSManaged`, `weak`, or `unowned`. +* A property with a delegate must be the only property declared within its enclosing declaration (e.g., `var (x, y) by Lazy = /* ... */` is ill-formed). +* The `value` property and (if present) `init(initialValue:)` of a property delegate type shall have the same access as the property delegate type. ## Impact on existing code From bd74611c79019dc6bbbf445941c554243677ef6b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 25 Mar 2019 14:25:28 -0700 Subject: [PATCH 1069/4563] Describe how we could reference the enclosing 'self' in a property delegate type --- proposals/NNNN-property-delegates.md | 137 ++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 93b31932dc..5b9a1b7005 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -13,7 +13,7 @@ Rather than hardcode a fixed set of patterns into the compiler, we should provide a general "property delegate" mechanism to allow these patterns to be defined as libraries. -This is an alternative approach to some of the problems intended to be addressed by the [2015-2016 property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md). Some of the examples are the same, but this proposal a completely different approach designed to be simpler, easier to understand for users, and less invasive in the compiler implementation. There is a section that discusses the substantive differences from that design at the end of this proposal. +This is an alternative approach to some of the problems intended to be addressed by the [2015-2016 property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md). Some of the examples are the same, but this proposal takes a completely different approach designed to be simpler, easier to understand for users, and less invasive in the compiler implementation. There is a section that discusses the substantive differences from that design at the end of this proposal. [Pitch](TODO)
    @@ -604,7 +604,7 @@ protocol, we don't know of any useful generic algorithms or data structures that seem to be implemented in terms of only `PropertyDelegate`. -## The 2015-2016 property behaviors design +### The 2015-2016 property behaviors design Property delegates address a similar set of use cases to *property behaviors*, which were [proposed and reviewed](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md) @@ -634,3 +634,136 @@ the prior proposal are: * Delegates can be initialized out-of-line, and one can use the `$`-prefixed name to refer to the storage property. These were future directions in the property behaviors proposal. + +## Future Directions + +### Referencing the enclosing 'self' in a delegate type + +Manually-written getters and setters for properties declared in a type often refer to the `self` of their enclosing type. For example, this can be used to notify clients of a change to a property's value: + +```swift +public class MyClass: Superclass { + private var backingMyVar: Int + public var myVar: Int { + get { return backingMyVar } + set { + if newValue != backingMyVar { + self.broadcastValueChanged(oldValue: backingMyVar, newValue: newValue) + } + backingMyVar = newValue + } + } +} +``` + +This "broadcast a notification that the value has changed" implementation cannot be cleanly factored into a property behavior type, because it needs access to both the underlying storage value (here, `backingMyVar`) and the `self` of the enclosing type. We could require a separate call to register the `self` instance with the delegate type, e.g., + +```swift +protocol Observed { + func broadcastValueChanged(oldValue: T, newValue: T) +} + +@propertyDelegate +public struct Observable { + public var stored: Value + var observed: Observed? + + public init(initialValue: Value) { + self.stored = initialValue + } + + public func register(_ observed: Observable) { + self.observed = observed + } + + public var value: Value { + get { return stored } + set { + if newValue != stored { + observed?.broadcastValueChanged(oldValue: stored, newValue: newValue) + } + stored = newValue + } + } +} +``` + +However, this means that one would have to manually call `register(_:)` in the initializer for `MyClass`: + +```swift +public class MyClass: Superclass { + public var myVar: Int by Observable = 17 + + init() { + // self.$myVar gets initialized with Observable(initialValue: 17) here + super.init() + self.$myVar.register(self) // register as an Observable + } +} +``` + +This isn't as automatic as we would like, and it requires us to have a separate reference to the `self` that is stored within `Observable`. + +Instead, we could extend the ad hoc protocol used to access the storage property of a `@propertyDelegate` type a bit further. Instead of (or in addition to) a `value` property, a property delegate type could provide a `subscript(instanceSelf:)` and/or `subscript(typeSelf:)` that receive `self` as a parameter. For example: + + +```swift +@propertyDelegate +public struct Observable { + public var stored: Value + + public init(initialValue: Value) { + self.stored = initialValue + } + + public subscript(instanceSelf observed: OuterSelf) -> Value { + get { return stored } + set { + if newValue != stored { + observed.broadcastValueChanged(oldValue: stored, newValue: newValue) + } + stored = newValue + } + } +} +``` + +The (generic) subscript gets access to the enclosing `self` type via its subscript parameter, eliminating the need for the separate `register(_:)` step and the (type-erased) storage of the outer `self`. The desugaring within `MyClass` would be as follows: + +```swift +public class MyClass: Superclass { + public var myVar: Int by Observable = 17 + + // desugars to... + internal var $myVar: Observable = Observable(initialValue: 17) + public var myVar: Int { + get { return $myVar[instanceSelf: self] } + set { $myVar[instanceSelf: self] = newValue } + } +} +``` + +This change is backward-compatible with the rest of the proposal. Property delegate types could provide any (non-empty) subset of the three ways to access the underlying value: + +* For instance properties, `subscript(instanceSelf:)` as shown above. +* For static or class properties, `subscript(typeSelf:)`, similar to the above but accepting a metatype parameter. +* For global/local properties, or when the appropriate `subscript` mentioned above isn't provided by the delegate type, the `value` property would be used. + +The main challenge with this design is that it doesn't directly work when the enclosing type is a value type and the property is settable. In such cases, one would expect `self` to be passed `inout`, e.g., + +```swift +public struct MyStruct { + public var myVar: Int by Observable = 17 + + // desugars to... + internal var $myVar: Observable = Observable(initialValue: 17) + public var myVar: Int { + get { return $myVar[instanceSelf: self] } + set { $myVar[instanceSelf: &self] = newValue } + } +} +``` + +There are a few issues here: first, subscripts don't allow `inout` parameters in the first place, so we would have to figure out how to implement support for such a feature. Second, passing `self` as `inout` while performing access to the property `self.myVar` violates Swift's exclusivity rules ([generalized accessors](https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md#generalized-accessors) might help address this). Third, property delegate types that want to support `subscript(instanceSelf:)` for both value and reference types would have to overload on `inout` or would have to have a different subscript name (e.g., `subscript(mutatingInstanceSelf:)`). + +So, while we feel that this is a promising future direction that fits in well with the proposal as-is, the open design questions are significant enough that we do not want to tackle them all in a single proposal. From a3c765195ac01d0e01a7376adf2914b1ac992d65 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 25 Mar 2019 14:47:09 -0700 Subject: [PATCH 1070/4563] Clarify that "inout self" isn't the only possible future direction --- proposals/NNNN-property-delegates.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 5b9a1b7005..66c53dc411 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -749,7 +749,7 @@ This change is backward-compatible with the rest of the proposal. Property deleg * For static or class properties, `subscript(typeSelf:)`, similar to the above but accepting a metatype parameter. * For global/local properties, or when the appropriate `subscript` mentioned above isn't provided by the delegate type, the `value` property would be used. -The main challenge with this design is that it doesn't directly work when the enclosing type is a value type and the property is settable. In such cases, one would expect `self` to be passed `inout`, e.g., +The main challenge with this design is that it doesn't directly work when the enclosing type is a value type and the property is settable. In such cases, the parameter to the subscript would get a copy of the entire enclosing value, which would not allow mutation, On the other hand, one could try to pass `self` as `inout`, e.g., ```swift public struct MyStruct { @@ -766,4 +766,4 @@ public struct MyStruct { There are a few issues here: first, subscripts don't allow `inout` parameters in the first place, so we would have to figure out how to implement support for such a feature. Second, passing `self` as `inout` while performing access to the property `self.myVar` violates Swift's exclusivity rules ([generalized accessors](https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md#generalized-accessors) might help address this). Third, property delegate types that want to support `subscript(instanceSelf:)` for both value and reference types would have to overload on `inout` or would have to have a different subscript name (e.g., `subscript(mutatingInstanceSelf:)`). -So, while we feel that this is a promising future direction that fits in well with the proposal as-is, the open design questions are significant enough that we do not want to tackle them all in a single proposal. +So, while we feel that support for accessing the enclosing type's `self` is useful and as future direction, and this proposal could be extended to accommodate it, the open design questions are significant enough that we do not want to tackle them all in a single proposal. From 43ee4086b7cb183929abdf5ab7bd788a38b8fbae Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 25 Mar 2019 16:43:54 -0700 Subject: [PATCH 1071/4563] Accept SE-0247 "Contiguous Strings" with "UTF8" suffix. Accept SE-0247 "Contiguous Strings" with a small amendment to rename `isContiguous` to `isContiguousUTF8` and `makeContiguous()` to `makeContiguousUTF8()`. --- proposals/0247-contiguous-strings.md | 31 ++++++++-------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/proposals/0247-contiguous-strings.md b/proposals/0247-contiguous-strings.md index ca2d9fd825..b7aa80c3f8 100644 --- a/proposals/0247-contiguous-strings.md +++ b/proposals/0247-contiguous-strings.md @@ -3,7 +3,7 @@ * Proposal: [SE-0247](0247-contiguous-strings.md) * Author: [Michael Ilseman](https://github.com/milseman) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (March 11 - 18, 2019)** +* Status: **Accepted** * Implementation: [apple/swift#23051](https://github.com/apple/swift/pull/23051) * Bugs: [SR-6475](https://bugs.swift.org/browse/SR-6475) @@ -51,14 +51,14 @@ extension String { /// give a result for String.UTF8View.withContiguousStorageIfAvailable. /// Contiguous strings also benefit from fast-paths and better optimizations. /// - public var isContiguous: Bool { get } + public var isContiguousUTF8: Bool { get } /// If this string is not contiguous, make it so. If this mutates the string, /// it will invalidate any pre-existing indices. /// /// Complexity: O(n) if non-contiguous, O(1) if already contiguous /// - public mutating func makeContiguous() + public mutating func makeContiguousUTF8() /// Runs `body` over the content of this string in contiguous memory. If this /// string is not contiguous, this will first make it contiguous, which will @@ -87,14 +87,14 @@ extension Substring { /// give a result for String.UTF8View.withContiguousStorageIfAvailable. /// Contiguous strings also benefit from fast-paths and better optimizations. /// - public var isContiguous: Bool { get } + public var isContiguousUTF8: Bool { get } /// If this string is not contiguous, make it so. If this mutates the /// substring, it will invalidate any pre-existing indices. /// /// Complexity: O(n) if non-contiguous, O(1) if already contiguous /// - public mutating func makeContiguous() + public mutating func makeContiguousUTF8() /// Runs `body` over the content of this substring in contiguous memory. If /// this substring is not contiguous, this will first make it contiguous, @@ -168,29 +168,16 @@ When it comes to adding these methods on StringProtocol, we have two options: ##### 1. Add it as a protocol extension -This approach would add the functions in an extension, ideally resilient and versioned. Unfortunately, `makeContiguous()` wouldn’t be implementable, as we cannot reassign self to a concrete type, so we’d have to add some additional requirements to StringProtocol surrounding forming a new contiguous string of the same concrete type. +This approach would add the functions in an extension, ideally resilient and versioned. Unfortunately, `makeContiguousUTF8()` wouldn’t be implementable, as we cannot reassign self to a concrete type, so we’d have to add some additional requirements to StringProtocol surrounding forming a new contiguous string of the same concrete type. ##### 2. Add it as a requirement with a default implementation This approach would add it as a customization hook that’s dynamically dispatched to the concrete type’s real implementations. The default implementation would be resilient and versioned and trap if called; any new conformers to StringProtocol would need to be versioned and accommodated here. -Adding new versioned-and-defaulted requirements to a protocol can be done at any point while preserving ABI stability. For now, we’re not sure it’s worth the extra witness table entries at this point. This also hits some of the pain points of option 1: any conformers to StringProtocol must satisfy `makeContiguous`’s post-condition of ensuring contiguity without changing concrete type. +Adding new versioned-and-defaulted requirements to a protocol can be done at any point while preserving ABI stability. For now, we’re not sure it’s worth the extra witness table entries at this point. This also hits some of the pain points of option 1: any conformers to StringProtocol must satisfy `makeContiguousUTF8`’s post-condition of ensuring contiguity without changing concrete type. This can be added in the future. -### Name it `isContiguousUTF8` and `makeContiguousUTF8()` - -This proposal is introducing the concept of "contiguous strings" which is -blessed as *the* fast-path in String's stable ABI for strings that can provide -UTF-8 contents in contiguous memory. If a string cannot provide UTF-8 content in -contiguous memory, it does receive these benefits, even if it happens to have -content in some other encoding in contiguous memory. - -We feel the concept of string contiguity in Swift is inherently tied to UTF-8, -and worth claiming the term "contiguous" unqualified in encoding. That being -said, this is a weakly held opinion and `isContiguousUTF8` is acceptable as -well. - ### Put this on the UTF8View Contiguous UTF-8 is baked into String's ABI as the fastest representation; this @@ -200,8 +187,8 @@ UTF-8 is special for performance and contiguity concerns. ### The Most Viable Alternative -Here's an alternative formulation that uses an explicit "UTF8" in the name and -avoids `mutating` and index-invalidation (by using strategy \#1 above). +Here's an alternative formulation that avoids `mutating` and +index-invalidation (by using strategy \#1 above). ```swift extension [Sub]String { From e60bac23bf0d6f345ddb48fbf64ea8324fce79a9 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 25 Mar 2019 18:29:45 -0700 Subject: [PATCH 1072/4563] Update 0244-opaque-result-types.md --- proposals/0244-opaque-result-types.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index 656ee0103b..b992b64163 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -3,9 +3,10 @@ * Proposal: [SE-0244](0244-opaque-result-types.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active review (March 5...March 15, 2019)** +* Status: **Returned for revision** * Implementation: [apple/swift#22072](https://github.com/apple/swift/pull/22072) * Toolchain: https://github.com/apple/swift/pull/21137#issuecomment-468118328 +* Review thread: https://forums.swift.org/t/se-0244-opaque-result-types/21252 ## Introduction From a4e5369634b454eb9e7256bb031eadde00b412fe Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 26 Mar 2019 13:49:57 -0700 Subject: [PATCH 1073/4563] [Proposal] KeyPath Dynamic Member Lookup (#1008) * [Proposal] KeyPath Dynamic Member Lookup This proposal attempts to enable stronger-typed version of the dynamic member lookup by extending functionality of an existing `@dynamicMemberLookup` attribute with key path based variants. * Initiate review for SE-0252. --- .../0252-keypath-dynamic-member-lookup.md | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 proposals/0252-keypath-dynamic-member-lookup.md diff --git a/proposals/0252-keypath-dynamic-member-lookup.md b/proposals/0252-keypath-dynamic-member-lookup.md new file mode 100644 index 0000000000..2623fe5f14 --- /dev/null +++ b/proposals/0252-keypath-dynamic-member-lookup.md @@ -0,0 +1,125 @@ +# Key Path Member Lookup + +* Proposal: [SE-0252](0252-keypath-dynamic-member-lookup.md) +* Authors: [Doug Gregor](https://github.com/DougGregor), [Pavel Yaskevich](https://github.com/xedin) +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Status: **Active review (March 26 - April 5, 2019)** +* Implementation: [PR #23436](https://github.com/apple/swift/pull/23436) + +## Introduction + +This proposal attempts to enable stronger-typed version of the dynamic member lookup by extending functionality of an existing `@dynamicMemberLookup` attribute with key path based variants. + +[Swift Evolution Pitch](https://forums.swift.org/t/pitch-key-path-member-lookup/21579) + +## Motivation + +Dynamic member lookup allows a type to opt in to extending member lookup ("dot" syntax) for arbitrary member names, turning them into a string that can then be resolved at runtime. Dynamic member lookup allows interoperability with dynamic languages where the members of a particular instance can only be determined at runtime... but no earlier. Dynamic member lookups, therefore, tend to work with type-erased wrappers around foreign language objects (e.g., `PyVal` for an arbitrary Python object), which don't provide much static type information. + +On the other hand, key paths provide a dynamic representation of a property that can be used to read or write the referenced property. Key paths maintain static type information about the type of the property being accessed, making them a good candidate for abstractly describing a reference to data that is modeled via Swift types. However, key paths can be cumbersome to create and apply. Consider a type `Lens` that abstractly refers to some value of type `T`, through which one can read (and possibly write) the value of that `T`: + +```swift +struct Lens { + let getter: () -> T + let setter: (T) -> Void + + var value: T { + get { + return getter() + } + set { + setter(newValue) + } + } +} +``` + +Given some `Lens`, we would like to produce a new `Lens` referring to a property of the value produced by the lens. Key paths allow us to write such a projection function directly: + +```swift +extension Lens { + func project(_ keyPath: WritableKeyPath) -> Lens { + return Lens( + getter: { self.value[keyPath: keyPath] }, + setter: { self.value[keyPath: keyPath] = $0 }) + } +} +``` + +As an example, consider a `Lens`: + +```swift +struct Point { + var x, y: Double +} + +struct Rectangle { + var topLeft, bottomRight: Point +} + +func projections(lens: Lens) { + let topLeft = lens.project(\.topLeft) // inferred type is Lens + let top = lens.project(\.topLeft.y) // inferred type is Lens +} +``` + +Forming the projection is a bit unwieldy: it's a call to `project` in which we need to use `\.` to then describe the key path. Why not support the most direct syntax to form a lens referring to some part of the stored value, e.g., `lens.topLeft` or `lens.topLeft.y`, respectively? + +## Proposed solution + +Augment existing `@dynamicMemberLookup` attribute to support key path based dynamic member lookup by rewriting "dot" and "subscript" syntax into a call to a special subscript whose argument is a key path describing the member. Here, we reimplement `Lens` in terms of new `@dynamicMemberLookup` capabilities: + + +```swift +@dynamicMemberLookup +struct Lens { + let getter: () -> T + let setter: (T) -> Void + + var value: T { + get { + return getter() + } + set { + setter(newValue) + } + } + + subscript(dynamicMember keyPath: WritableKeyPath) -> Lens { + return Lens( + getter: { self.value[keyPath: keyPath] }, + setter: { self.value[keyPath: keyPath] = $0 }) + } +} +``` + +Given a `Lens` named `lens`, the expression `lens.topLeft` will be evaluated as `lens[dynamicMember: \.topLeft]`, allowing normal member accesses on a `Lens` to produce a new `Lens`. + +The formation of the key path follows a "single step" approach where each key path component is split into a separate `[dynamicMember: KeyPath]` invocation. For example, the expression `lens.topLeft.y` will be evaluated as `lens[dynamicMember: \.topLeft][dynamicMember: \.y]`, producing a `Lens`. + +## Detailed design + +Proposed solution builds on existing functionality of the `@dynamicMemberLookup` attribute. It adopts restrictions associated with existing string-based design as well as a couple of new ones: + +* Key path member lookup only applies when the `@dynamicMemberLookup` type does not contain a member with the given name. This privileges the members of the `@dynamicMemberLookup` type (e.g., `Lens`), hiding those of whatever type is that the root of the keypath (e.g., `Rectangle`). +* `@dynamicMemberLookup` can only be written directly on the definition of a type, not an an extension of that type. +* A `@dynamicMemberLookup` type must define a subscript with a single, non-variadic parameter whose argument label is `dynamicMember` and that accepts one of the key path types (e.g., `KeyPath`, `WritableKeyPath`). +* In case both string-based and keypath-based overloads match, keypath takes priority as one carrying more typing information. + +## Source compatibility + +This is an additive proposal, which makes ill-formed syntax well-formed but otherwise does not affect existing code. First, only types that opt in to `@dynamicMemberLookup` will be affected. Second, even for types that adopt `@dynamicMemberLookup`, the change is source-compatible because the transformation to use `subscript(dynamicMember:)` is only applied when there is no member of the given name. + +## Effect on ABI stability + +This feature is implementable entirely in the type checker, as (effectively) a syntactic transformation on member access expressions. It, therefore, has no impact on the ABI. + +## Effect on API resilience + +Adding `@dynamicMemberLookup` is a resilient change to a type, as is the addition of the subscript. + +## Alternatives considered + +The main alternative would be to not do this at all. + +Another alternative would be to use a different attribute to separate this feature from `@dynamicMemberLookup`, e.g. `@keyPathMemberLookup` since string based design doesn't, at the moment, provide any static checking for member access. We recognize this as a valid concern, but at the same time consider both to be fundamentally the same feature with different amount of static checking. Using the same attribute allows us to adhere to "fewer conceptular features" concept, as well as, enables powerful combinations where string based dynamic lookup could be used as a fallback when key path dynamic lookup fails. From d04827a310d664e4bdf7007654044dc7ba612aa4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 26 Mar 2019 23:52:17 -0700 Subject: [PATCH 1074/4563] Allow accessors to be explicitly specified (get/set/didSet/willSet) --- proposals/NNNN-property-delegates.md | 30 +++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 66c53dc411..31f1f9f245 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -542,6 +542,35 @@ print($x) // okay to refer to compiler-defined $x let $y = 17 // error: cannot declare entity with $-prefixed name '$y' ``` +### Accessors + +A property with a delegate can declare accessors explicitly (`get`, `set`, `didSet`, `willSet`). If either `get` or `set` is missing, it will be implicitly created by accessing the storage property (e.g., `$foo`). For example: + +```swift +var x by Lazy = 17 { + set { + $x.value = newValue * 2 + } + + // implicitly synthesized... + get { return $x.value } +} + + +var y by Lazy = 42 { + didSet { + print("Overrode lazy value with \(oldValue)") + } + + // implicitly synthesized... + set { + let oldValue = $y.value + $y.value = newValue + didSet(oldValue) + } +} +``` + ### Restrictions on the use of property delegates There are a number of restrictions on the use of property delegates when defining a property: @@ -551,7 +580,6 @@ There are a number of restrictions on the use of property delegates when definin * An instance property may not be declared in an `enum`. * A property with a delegate that is declared within a class must be `final` and cannot override another property. -* A property with a delegate may not declare any accessors. * A property with a delegate cannot be `lazy`, `@NSCopying`, `@NSManaged`, `weak`, or `unowned`. * A property with a delegate must be the only property declared within its enclosing declaration (e.g., `var (x, y) by Lazy = /* ... */` is ill-formed). * The `value` property and (if present) `init(initialValue:)` of a property delegate type shall have the same access as the property delegate type. From 0c7efcd997293e13c1156df84745bd4263486e14 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 26 Mar 2019 23:56:03 -0700 Subject: [PATCH 1075/4563] Add link to the pitch --- proposals/NNNN-property-delegates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 31f1f9f245..3979f284cc 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -15,7 +15,7 @@ these patterns to be defined as libraries. This is an alternative approach to some of the problems intended to be addressed by the [2015-2016 property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md). Some of the examples are the same, but this proposal takes a completely different approach designed to be simpler, easier to understand for users, and less invasive in the compiler implementation. There is a section that discusses the substantive differences from that design at the end of this proposal. -[Pitch](TODO)
    +[Pitch](https://forums.swift.org/t/pitch-property-delegates/21895)
    ## Motivation From c0d372369d62496eb5421b3dff51d1b25db8be4a Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 27 Mar 2019 09:27:10 -0700 Subject: [PATCH 1076/4563] Update 0243-codepoint-and-character-literals.md --- proposals/0243-codepoint-and-character-literals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0243-codepoint-and-character-literals.md b/proposals/0243-codepoint-and-character-literals.md index f39f8a7e9c..975a6225fe 100644 --- a/proposals/0243-codepoint-and-character-literals.md +++ b/proposals/0243-codepoint-and-character-literals.md @@ -3,7 +3,7 @@ * Proposal: [SE-0243](0243-codepoint-and-character-literals.md) * Authors: [Kelvin Ma (“Taylor Swift”)](https://github.com/kelvin13), [Chris Lattner](https://github.com/lattner), [John Holdsworth](https://github.com/johnno1962) * Review manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active review (March 4 - March 12, 2019)** +* Status: **Rejected** ([Rationale](https://forums.swift.org/t/se-0243-codepoint-and-character-literals/21188/341)) * Implementation: [apple/swift#21873](https://github.com/apple/swift/pull/21873) * Threads: [1](https://forums.swift.org/t/prepitch-character-integer-literals/10442) From 03fc9d0005f868e717934892ab455e8f884d273a Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Wed, 27 Mar 2019 15:54:16 -0700 Subject: [PATCH 1077/4563] Add the callable proposal. (#1009) * Add the callable proposal. --- proposals/0252-callable.md | 598 +++++++++++++++++++++++++++++++++++++ 1 file changed, 598 insertions(+) create mode 100644 proposals/0252-callable.md diff --git a/proposals/0252-callable.md b/proposals/0252-callable.md new file mode 100644 index 0000000000..8502235c96 --- /dev/null +++ b/proposals/0252-callable.md @@ -0,0 +1,598 @@ +# Introduce callables + +* Proposal: [SE-0252](https://github.com/apple/swift-evolution/blob/master/proposals/0252-callable.md) +* Authors: [Richard Wei](https://github.com/rxwei), [Dan Zheng](https://github.com/dan-zheng) +* Review Manager: TBD +* Status: Implementation in progress +* Implementation: https://github.com/apple/swift/pull/23517 + +## Introduction + +Note: throughout this document, let "call-syntax" refer to the syntax of applying a function to arguments: `f(x, y, z)`. + +This proposal introduces [callables](https://en.wikipedia.org/wiki/Callable_object) to Swift. Callables are values that define function-like behavior and can be applied using function application syntax. + +In a nutshell, we propose to introduce a new declaration syntax with the keyword `call` : + +```swift +struct Adder { + var base: Int + call(_ x: Int) -> Int { + return base + x + } +} +``` + +Values that have a `call` member can be applied like functions, forwarding arguments to the `call` member. + +```swift +let add3 = Adder(base: 3) +add3(10) // => 13 +``` + +## Motivation + +Currently, in Swift, only a few kinds of values are syntactically callable: +- Values with function types. +- Type names (e.g. `T` can be called like `T(...)`, which is desugared to `T.init(...)`). +- Values with a `@dynamicCallable` type. + +However, call-syntax can also be useful for other values, primarily those that behave like functions. This includes: +- Values that represent functions: mathematical functions, function expressions, etc. +- Values that have one main use and want to provide a simple call-syntax interface: neural network layers, parsers, efficient bound closures, etc. + +Here are some concrete sources of motivation. + +### Values representing functions + +Values of some nominal types exactly represent functions: in the mathematical sense (a mapping from inputs to outputs), or in the context of programming languages. + +Here are some examples: + +```swift +/// Represents a polynomial function, e.g. `2 + 3x + 4x²`. +struct Polynomial { + /// Represents the coefficients of the polynomial, starting from power zero. + let coefficients: [Float] +} +``` + +Since these types represent functions, naturally they can be applied to inputs. However, currently in Swift, the "function application" functionality must be defined as a method. + +```swift +extension Polynomial { + func evaluated(at input: Float) -> Float { + var result: Float = 0 + for (i, c) in coefficients.enumerated() { + result += c * pow(input, Float(i)) + } + return result + } +} + +let polynomial = Polynomial(coefficients: [2, 3, 4]) +print(polynomial.evaluated(at: 2)) // => 24 +``` + +The mathematical notation for function application is simply `output = f(input)`. Using subscript methods achieve a similar application syntax `f[x]`, but subscripts and square brackets typically connote "indexing into a collection", which is not the behavior here. + +```swift +extension Polynomial { + subscript(input: Float) -> Float { + ... + } +} +let polynomial = Polynomial(coefficients: [2, 3, 4]) +// Subscript syntax, may be confusing. +print(polynomial[2]) // => 24 +``` + +The proposed feature enables the same call syntax as the mathematical notation: +```swift +extension Polynomial { + call(_ input: Float) -> Float { + ... + } +} +let polynomial = Polynomial(coefficients: [2, 3, 4]) +// Callable syntax. +print(polynomial(2)) // => 24 +``` + +#### Bound closures + +Variable-capturing closures can be modeled explicitly as structs that store the bound variables. This representation is more performant and avoids the type-erasure of closure contexts. + +```swift +// Represents a nullary function capturing a value of type `T`. +struct BoundClosure { + var function: (T) -> Void + var value: T + + call() { return function(value) } +} + +let x = "Hello world!" +let closure = BoundClosure(function: { print($0) }, value: x) +closure() // prints "Hello world!" +``` + +A call syntax sugar would enable `BoundClosure` instances to be applied like normal functions. + +### Nominal types with one primary method + +Some nominal types have a "primary method" that performs their main use. For example: calculators *calculate*, parsers *parse*, neural network layers *apply to inputs*, types representing functions *apply to arguments*, etc. + +For example: +* Calculators *calculate*: `calculator.calculating(query)`. +* Parsers *parse*: `parser.parsing(text)`. +* Neural network layers *apply to inputs*: `layer.applied(to: input)`. +* Types representing functions *apply to arguments*: `function.applied(to: arguments)`. + +Types that have a primary method usually call that method frequently. Thus, it may be desirable to sugar applications of the main method with call syntax to reduce noise. + +Let’s explore neural network layers and string parsers in detail. + +#### Neural network layers + +[Machine learning](https://en.wikipedia.org/wiki/Machine_learning) models often represent a function that contains an internal state called "trainable parameters", and the function takes an input and predicts the output. In code, models are often represented as a data structure that stores trainable parameters, and a method that defines the transformation from an input to an output in terms of these trained parameters. Here’s an example: + +```swift +struct Perceptron { + var weight: Vector + var bias: Float + + func applied(to input: Vector) -> Float { + return weight • input + bias + } +} +``` + +Stored properties `weight` and `bias` are considered as trainable parameters, and are used to define the transformation from model inputs to model outputs. Models can be [trained ](https://developers.google.com/machine-learning/glossary/#training), during which parameters like `weight` are updated, thus changing the behavior of `applied(to:)`. When a model is used, the call site looks just like a function call. + +```swift +let model: Perceptron = ... +let ŷ = model.applied(to: x) +``` + +Many deep learning models are composed of layers, or layers of layers. In the definition of those models, repeated calls to `applied(to:)` significantly complicate the look of the program and reduce the clarity of the resulting code. + +```swift +struct Model { + var conv = Conv2D(filterShape: (5, 5, 3, 6)) + var maxPool = MaxPool2D(poolSize: (2, 2), strides: (2, 2)) + var flatten = Flatten() + var dense = Dense(inputSize: 36 * 6, outputSize: 10) + + func applied(to input: Tensor) -> Tensor { + return dense.applied(to: flatten.applied(to: maxPool.applied(to: conv.applied(to: input)))) + } +} +``` + +These repeated calls to `applied(to:)` harm clarity and makes code less readable. If `model` could be called like a function, which it mathematically represents, the definition of `Model` becomes much shorter and more concise. The proposed feature [promotes clear usage by omitting needless words](https://swift.org/documentation/api-design-guidelines/#promote-clear-usage). + +```swift +struct Model { + var conv = Conv2D(filterShape: (5, 5, 3, 6)) + var maxPool = MaxPool2D(poolSize: (2, 2), strides: (2, 2)) + var flatten = Flatten() + var dense = Dense(inputSize: 36 * 6, outputSize: 10) + + call(_ input: Tensor) -> Tensor { + // Callable syntax. + return dense(flatten(maxPool(conv(input)))) + } +} + +let model: Model = ... +let ŷ = model(x) +``` + +There are more ways to further simplify model definitions, but making models callable like functions is a good first step. + +#### Domain specific languages + +DSL constructs like string parsers represent functions from inputs to outputs. Parser combinators are often implemented as higher-order functions operating on parser values, which are themselves data structures—some implementations store closures, while some other [efficient implementations ](https://infoscience.epfl.ch/record/203076/files/p637-jonnalagedda.pdf) store an expression tree. They all have an "apply"-like method that performs an application of the parser (i.e. parsing). + +```swift +struct Parser { + // Stored state... + + func applied(to input: String) throws -> Output { + // Using the stored state... + } + + func many() -> Parser<[Output]> { ... } + func many(separatedBy separator: Parser) -> Parser<[Output]> { ... } +} +``` + +When using a parser, one would need to explicitly call `applied(to:)`, but this is a bit cumbersome—the naming this API often repeats the type. Since parsers are like functions, it would be cleaner if the parser itself were callable. + +```swift +call(_ input: String) throws -> Output { + // Using the stored state... +} +``` + +```swift +let sexpParser: Parser = … +// Callable syntax. +let sexp = sexpParser("(+ 1 2)") +``` + +### A static counterpart to `@dynamicCallable` + +[SE-0216 ](https://github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md) introduced user-defined dynamically callable values. In its [alternatives considered](https://github.com/apple/swift-evolution/blob/master/proposals/0216-dynamic-callable.md#alternatives-considered) section, it was requested that we design and implement the "static callable" version of this proposal in conjunction with the dynamic version proposed. See its [pitch thread](https://forums.swift.org/t/pitch-3-introduce-user-defined-dynamically-callable-types/12232) for discussions about "static callables". + +### Prior art + +Many languages offer the call syntax sugar: + +* Python: [ `object.__call__(self[, args...])` ](https://docs.python.org/3/reference/datamodel.html#emulating-callable-objects) +* C++: [ `operator()` (function call operator)](https://en.cppreference.com/w/cpp/language/operators) +* Scala: [ `def apply(...)` (apply methods) ](https://twitter.github.io/scala_school/basics2.html#apply) + +### Unifying compound types and nominal types + +A long term goal with the type system is to unify compound types (e.g. function types and tuple types) and nominal types, to allow compound types to conform to protocols and have members. When function types can have members, it will be most natural for them to have a `call` member, which can help unify the compiler's type checking rules for call expressions. + +## Proposed design + +We propose to introduce a new keyword `call` and a new declaration syntax–the call declaration syntax. + +```swift +struct Adder { + var base: Int + call(_ x: Int) -> Int { + return base + x + } +} +``` + +Values that have a `call` member can be called like a function, forwarding arguments to the `call` member. + +```swift +let add3 = Adder(base: 3) +add3(10) // => 13 +``` + +## Detailed design + +### `call` member declarations + +`call` members can be declared in structure types, enumeration types, class types, protocols, and extensions thereof. + +A `call` member declaration is similar to `subscript` in the following ways: + +* It does not take a name. +* It must be an instance member of a type. + +But it is more similar to a `func` declaration in that: + +* It does not allow `get` and `set` declarations inside the body. +* When a parameter has a name, it is treated as the argument label. +* It can throw. +* It can be referenced directly by name, e.g. `foo.call`. + +The rest of the `call` declaration grammar and semantics is identical to that of function declarations–same syntax for access level, generics, argument labels, return types, throwing, mutating, `where` clause, etcs. They can be overloaded based on argument and result types. Attributes that can be applied to function declarations can also be applied to `call` declarations. + +To support source compatibility, `call` is treated as a keyword only when parsing members of a nominal type. Otherwise, it is treated as a normal identifier. See the source compatibility section below. + +``` +call-declaration → call-head generic-parameter-clause? function-signature generic-where-clause? function-body? +call-head → attributes? declaration-modifiers? 'call' +``` + +#### Examples + +```swift +struct Adder { + var base: Int + + call(_ x: Int) -> Int { + return base + x + } + + call(_ x: Float) -> Float { + return Float(base) + x + } + + call(_ x: T, bang: Bool) throws -> T where T: BinaryInteger { + if bang { + return T(Int(exactly: x)! + base) + } else { + return T(Int(truncatingIfNeeded: x) + base) + } + } + + // This is a normal function, not a `call` member. + func call(x: Int) {} +} +``` + +### Call expressions + +When type-checking a call expression, the type checker will try to resolve the callee. Currently, the callee can be a value with a function type, a type name, or a value of a `@dynamicCallable` type. This proposal adds a fourth kind of a callee: a value with a matching `call` member. + +```swift +let add1 = Adder(base: 1) +add1(2) // => 3 +try add1(4, bang: true) // => 5 +``` + +When type-checking fails, error messages look like those for function calls. When there is ambiguity, the compiler will show relevant `call` member candidates. + +```swift +add1("foo") +// error: cannot invoke ‘add1’ with an argument list of type '(String)' +// note: overloads for 'call' exist with these partially matching parameter lists: (Float), (Int) +add1(1, 2, 3) +// error: cannot invoke 'add1' with an argument list of type '(Int, Int, Int)' +``` + +### When the type is also `@dynamicCallable` + +A type can both have `call` members and be declared with `@dynamicCallable` . When type-checking a call expression, the type checker will first try to resolve the call to a function or initializer call, then a `call` member call, and finally a dynamic call. + +### Direct reference to a `call` member + +Like methods and initializers, a `call` member can be directly referenced, either through the base name and the contextual type, or through the full name. + +```swift +let add1 = Adder(base: 1) +let f: (Int) -> Int = add1.call +f(2) // => 3 +[1, 2, 3].map(add1.call) // => [2, 3, 4] +``` + +When a type has both an instance method named "call" and a `call` member with the exact same type signature, a redeclaration error is produced. + +```swift +struct S { + func call() {} + call() {} +} +``` + +```console +test.swift:3:5: error: invalid redeclaration of 'call()' + call() {} + ^ +test.swift:2:10: note: 'call()' previously declared here + func call() {} + ^ +``` + +When a type does not have a `call` member but has an instance method or an instance property named "call", a direct reference to `call` gets resolved to that member. + +```swift +struct S { + var call: Int = 0 +} +S().call // resolves to the property +``` + +A value cannot be implicitly converted to a function when the destination function type matches the type of the `call` member. + +```swift +let h: (Int) -> Int = add1 // error: cannot convert value of type `Adder` to expected type `(Int) -> Int` +``` + +Implicit conversions are generally problematic in Swift, and as such we would like to get some experience with this base proposal before considering adding such capability. + +## Source compatibility + +The proposed feature adds a `call` keyword. Normally, this would require existing identifiers named "call" to be escaped as `` `call` ``. However, this would break existing code using `call` identifiers, e.g. `func call`. + +To maintain source compatibility, we propose making `call` a contextual keyword: that is, it is a keyword only in declaration contexts and a normal identifier elsewhere (e.g. in expression contexts). This means that `func call` and `call(...)` (apply expressions) continue to parse correctly. + +Here’s a comprehensive example of parsing `call` in different contexts: + +```swift +struct Callable { + // declaration + call(_ body: () -> Void) { + // expression + call() {} + // expression + call {} + + struct U { + // declaration + call(x: Int) {} + + // declaration + call(function: (Int) -> Void) {} + + // error: expression in declaration context + // expected '(' for 'call' member parameters + call {} + } + + let u = U() + // expression + u { x in } + } +} + +// expression +call() {} +// expression +call {} +``` + +## Effect on ABI stability + +This proposal is about a syntactic sugar and has no ABI breaking changes. + +## Effect on API resilience + +This proposal is about a syntactic sugar and has no API breaking changes. + +## Alternatives considered + +### Alternative ways to denote call-syntax delegate methods + +#### Use unnamed `func` declarations to mark call-syntax delegate methods + +```swift +struct Adder { + var base: Int + // Option: unnamed `func`. + func(_ x: Int) -> Int { + return base + x + } + // Option: `call` declaration modifier on unnamed `func` declarations. + // Makes unnamed `func` less weird and clearly states "call". + call func(_ x: Int) -> Int { … } +} +``` + +This approach represents call-syntax delegate methods as unnamed `func` declarations instead of creating a new `call` declaration kind. + +One option is to use `func(...)` without an identifier name. Since the word "call" does not appear, it is less clear that this denotes a call-syntax delegate method. Additionally, it’s not clear how direct references would work: the proposed design of referencing `call` declarations via `foo.call` is clear and consistent with the behavior of `init` declarations. + +To make unnamed `func(...)` less weird, one option is to add a `call` declaration modifier: `call func(...)`. The word `call` appears in both this option and the proposed design, clearly conveying "call-syntax delegate method". However, declaration modifiers are currently also treated as keywords, so with both approaches, parser changes to ensure source compatibility are necessary. `call func(...)` requires additional parser changes to allow `func` to sometimes not be followed by a name. The authors lean towards `call` declarations for terseness. + +#### Use an attribute to mark call-syntax delegate methods + +```swift +struct Adder { + var base: Int + @callDelegate + func addingWithBase(_ x: Int) -> Int { + return base + x + } +} +``` + +This approach achieves a similar effect as `call` declarations, except that methods can have a custom name and be directly referenced by that name. This is useful for types that want to make use of the call syntax sugar, but for which the name "call" does not accurately describe the callable functionality. + +However, we feel that using a `@callableMethod` method attribute is more noisy. Introducing a `call` declaration kind makes the concept of "callables" feel more first-class in the language, just like subscripts. `call` is to `()` as `subscript` is to `[]`. + +For reference: other languages with callable functionality typically require call-syntax delegate methods to have a particular name (e.g. `def __call__` in Python, `def apply` in Scala). + +#### Use `func` with a special name to mark call-syntax delegate methods + +```swift +struct Adder { + var base: Int + // Option: specially-named `func` declarations. + func _(_ x: Int) -> Int + func self(_ x: Int) -> Int +} +``` + +This approach represents call-syntax delegate methods as `func` declarations with a special name instead of creating a new `call` declaration kind. However, such `func` declarations do not convey "call-syntax delegate method" as clearly as the `call` keyword. + +Also, we want to support direct references to call-syntax delegate methods via `foo.call`. This makes more sense when call-syntax delegate methods are declared with the `call` keyword, and is consistent with `init` declarations and direct references (e.g. `foo.init`). + +#### Use a type attribute to mark types with call-syntax delegate methods + +```swift +@staticCallable // alternative name `@callable`; similar to `@dynamicCallable` +struct Adder { + var base: Int + // Informal rule: all methods with a particular name (e.g. `func call`) are deemed call-syntax delegate methods. + func call(_ x: Int) -> Int { + return base + x + } +} +``` + +We feel this approach is not ideal because: + +* A marker type attribute is not particularly meaningful. The call-syntax delegate methods of a type are what make values of that type callable - a type attribute means nothing by itself. In fact, there’s an edge case that needs to be explicitly handled: if a `@staticCallable` type defines no call-syntax delegate methods, an error must be produced. +* The name for call-syntax delegate methods (e.g. `func call` ) is not first-class in the language, while their call site syntax is. + +#### Use a `Callable` protocol to represent callable types + +```swift +// Compiler-known `Callable` marker protocol. +struct Adder: Callable { + var base: Int + // Informal rule: all methods with a particular name (e.g. `func call`) are deemed call-syntax delegate methods. + // `StringInterpolationProtocol` has a similar informal requirement for `func appendInterpolation` methods. + // https://github.com/apple/swift-evolution/blob/master/proposals/0228-fix-expressiblebystringinterpolation.md#proposed-solution + func call(_ x: Int) -> Int { + return base + x + } +} +``` + +We feel this approach is not ideal for the same reasons as the marker type attribute. A marker protocol by itself is not meaningful and the name for call-syntax delegate methods is informal. Additionally, protocols should represent particular semantics, but "callable" behavior has no inherent semantics. + +In comparison, `call` declarations have a formal representation in the language and exactly indicate callable behavior (unlike a marker attribute or protocol). + +### Property-like `call` with getter and setter + +In C++, `operator()` can return a reference, which can be used on the left hand side of an assignment expression. This is used by some DSLs such as [Halide](http://halide-lang.org/docs/class_halide_1_1_func.html): + +```swift +Halide::Func foo; +Halide::Var x, y; +foo(x, y) = x + y; +``` + +This can be achieved via Swift’s subscripts, which can have a getter and a setter. + +```swift +foo[x, y] = x + y +``` + +Since the proposed `call` declaration syntax is like `subscript` in many ways, it’s in theory possible to allow `get` and `set` in a `call` declaration’s body. + +```swift +call(x: T) -> U { + get { + ... + } + set { + ... + } +} +``` + +However, we do not believe `call` should behave like a storage accessor like `subscript` . Instead, `call` ’s appearance should be as close to function calls as possible. Function call expressions today are not assignable because they can't return an l-value reference, so a call to a `call` member should not be assignable either. + +### Static `call` members + +Static `call` members could in theory look like initializers at the call site. + +```swift +extension Adder { + static call(base: Int) -> Int { + ... + } + static call(_ x: Int) -> Int { + ... + } +} +Adder(base: 3) // error: ambiguous static member; do you mean `init(base:)` or `call(base:)`? +Adder(3) // okay, returns an `Int`, but it looks really like an initializer that returns an `Adder`. +``` + +We believe that the initializer call syntax in Swift is baked tightly into programmers' mental model, and thus do not think overloading that is a good idea. + +We could also make it so that static `call` members can only be called via call expressions on metatypes. + +```swift +Adder.self(base: 3) // okay +``` + +But since this would be an additive feature on top of this proposal and that `subscript` cannot be `static` yet, we'd like to defer this feature to future discussions. + +### Unify callable functionality with `@dynamicCallable` + +Both `@dynamicCallable` and the proposed `call` members involve syntactic sugar related to function applications. However, the rules of the sugar are different, making unification difficult. In particular, `@dynamicCallable` provides a special sugar for argument labels that is crucial for usability. + +```swift +// Let `PythonObject` be a `@dynamicMemberLookup` type with callable functionality. +let np: PythonObject = ... +// `PythonObject` with `@dynamicCallable. +np.random.randint(-10, 10, dtype: np.float) +// `PythonObject` with `call` members. The empty strings are killer. +np.random.randint(["": -10, "": 10, "dtype": np.float]) +``` From cd96f7963fa169c69dbbf346a0ceffaadc0a3e74 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 27 Mar 2019 17:15:58 -0700 Subject: [PATCH 1078/4563] rename 0252-callable -> 0253 --- proposals/{0252-callable.md => 0253-callable.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{0252-callable.md => 0253-callable.md} (100%) diff --git a/proposals/0252-callable.md b/proposals/0253-callable.md similarity index 100% rename from proposals/0252-callable.md rename to proposals/0253-callable.md From 1e1457e495d79f804ca5f7d328ff5a0ea90555e9 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Wed, 27 Mar 2019 17:26:11 -0700 Subject: [PATCH 1079/4563] Updates. --- proposals/0253-callable.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0253-callable.md b/proposals/0253-callable.md index 8502235c96..9885b568b8 100644 --- a/proposals/0253-callable.md +++ b/proposals/0253-callable.md @@ -1,9 +1,9 @@ # Introduce callables -* Proposal: [SE-0252](https://github.com/apple/swift-evolution/blob/master/proposals/0252-callable.md) +* Proposal: [SE-0253](https://github.com/apple/swift-evolution/blob/master/proposals/0253-callable.md) * Authors: [Richard Wei](https://github.com/rxwei), [Dan Zheng](https://github.com/dan-zheng) -* Review Manager: TBD -* Status: Implementation in progress +* Review Manager: [Chris Lattner](https://github.com/lattner) +* Status: **Active review (March 27 - April 5, 2019)** * Implementation: https://github.com/apple/swift/pull/23517 ## Introduction From 293691043db2c70bfa909894ccf749dcd9d81139 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 27 Mar 2019 20:43:03 -0400 Subject: [PATCH 1080/4563] Accept SE-0246 --- proposals/0246-mathable.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proposals/0246-mathable.md b/proposals/0246-mathable.md index 23b0afaa1b..eddb6af07c 100644 --- a/proposals/0246-mathable.md +++ b/proposals/0246-mathable.md @@ -3,9 +3,12 @@ * Proposal: [SE-0246](0246-mathable.md) * Author: [Stephen Canon](https://github.com/stephentyrone) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (March 11 – 25th, 2019)** +* Status: **Accepted with modifications** * Implementation: [apple/swift#23140](https://github.com/apple/swift/pull/23140) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/b5bbc5ae1f53189641951acfd50870f5b886859e/proposals/0246-mathable.md) [2](https://github.com/apple/swift-evolution/blob/3afc4c68a4062ff045415f5eafb9d4956b30551b/proposals/0246-mathable.md) +* Review: [review](https://forums.swift.org/t/se-0246-generic-math-s-functions/21479) [acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0246-generic-math-functions/22244) + +**This proposal was accepted with modifications which have not yet been applied to this document. Please see the acceptance post for further details.** ## Introduction From cad41b4059699de42e7fcbb1160180f5e8c07c52 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 27 Mar 2019 20:43:34 -0400 Subject: [PATCH 1081/4563] parenthesize links in SE-0246 --- proposals/0246-mathable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0246-mathable.md b/proposals/0246-mathable.md index eddb6af07c..22126e4380 100644 --- a/proposals/0246-mathable.md +++ b/proposals/0246-mathable.md @@ -6,7 +6,7 @@ * Status: **Accepted with modifications** * Implementation: [apple/swift#23140](https://github.com/apple/swift/pull/23140) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/b5bbc5ae1f53189641951acfd50870f5b886859e/proposals/0246-mathable.md) [2](https://github.com/apple/swift-evolution/blob/3afc4c68a4062ff045415f5eafb9d4956b30551b/proposals/0246-mathable.md) -* Review: [review](https://forums.swift.org/t/se-0246-generic-math-s-functions/21479) [acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0246-generic-math-functions/22244) +* Review: ([review](https://forums.swift.org/t/se-0246-generic-math-s-functions/21479)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0246-generic-math-functions/22244)) **This proposal was accepted with modifications which have not yet been applied to this document. Please see the acceptance post for further details.** From 8987fee5d3514ccfde1479214fceedbc5a83bba3 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Wed, 27 Mar 2019 20:59:49 -0700 Subject: [PATCH 1082/4563] Fix typos in SE-0253. (#1010) * Fix typos in SE-0253. * Improve comment formatting, use ASCII quotes. * Remove extra whitespace. * Use ASCII ellipsis. * Abbreviate implementation link. * Use ASCII quotes. Co-Authored-By: dan-zheng --- proposals/0253-callable.md | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/proposals/0253-callable.md b/proposals/0253-callable.md index 9885b568b8..28d081ea6e 100644 --- a/proposals/0253-callable.md +++ b/proposals/0253-callable.md @@ -4,7 +4,7 @@ * Authors: [Richard Wei](https://github.com/rxwei), [Dan Zheng](https://github.com/dan-zheng) * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Active review (March 27 - April 5, 2019)** -* Implementation: https://github.com/apple/swift/pull/23517 +* Implementation: [apple/swift#23517](https://github.com/apple/swift/pull/23517) ## Introduction @@ -12,7 +12,7 @@ Note: throughout this document, let "call-syntax" refer to the syntax of applyin This proposal introduces [callables](https://en.wikipedia.org/wiki/Callable_object) to Swift. Callables are values that define function-like behavior and can be applied using function application syntax. -In a nutshell, we propose to introduce a new declaration syntax with the keyword `call` : +In a nutshell, we propose to introduce a new declaration syntax with the keyword `call`: ```swift struct Adder { @@ -121,9 +121,8 @@ A call syntax sugar would enable `BoundClosure` instances to be applied like nor ### Nominal types with one primary method -Some nominal types have a "primary method" that performs their main use. For example: calculators *calculate*, parsers *parse*, neural network layers *apply to inputs*, types representing functions *apply to arguments*, etc. +Some nominal types have a "primary method" that performs their main use. For example: -For example: * Calculators *calculate*: `calculator.calculating(query)`. * Parsers *parse*: `parser.parsing(text)`. * Neural network layers *apply to inputs*: `layer.applied(to: input)`. @@ -131,11 +130,11 @@ For example: Types that have a primary method usually call that method frequently. Thus, it may be desirable to sugar applications of the main method with call syntax to reduce noise. -Let’s explore neural network layers and string parsers in detail. +Let's explore neural network layers and string parsers in detail. #### Neural network layers -[Machine learning](https://en.wikipedia.org/wiki/Machine_learning) models often represent a function that contains an internal state called "trainable parameters", and the function takes an input and predicts the output. In code, models are often represented as a data structure that stores trainable parameters, and a method that defines the transformation from an input to an output in terms of these trained parameters. Here’s an example: +[Machine learning](https://en.wikipedia.org/wiki/Machine_learning) models often represent a function that contains an internal state called "trainable parameters", and the function takes an input and predicts the output. In code, models are often represented as a data structure that stores trainable parameters, and a method that defines the transformation from an input to an output in terms of these trained parameters. Here's an example: ```swift struct Perceptron { @@ -217,7 +216,7 @@ call(_ input: String) throws -> Output { ``` ```swift -let sexpParser: Parser = … +let sexpParser: Parser = ... // Callable syntax. let sexp = sexpParser("(+ 1 2)") ``` @@ -326,7 +325,7 @@ When type-checking fails, error messages look like those for function calls. Whe ```swift add1("foo") -// error: cannot invoke ‘add1’ with an argument list of type '(String)' +// error: cannot invoke 'add1' with an argument list of type '(String)' // note: overloads for 'call' exist with these partially matching parameter lists: (Float), (Int) add1(1, 2, 3) // error: cannot invoke 'add1' with an argument list of type '(Int, Int, Int)' @@ -334,7 +333,7 @@ add1(1, 2, 3) ### When the type is also `@dynamicCallable` -A type can both have `call` members and be declared with `@dynamicCallable` . When type-checking a call expression, the type checker will first try to resolve the call to a function or initializer call, then a `call` member call, and finally a dynamic call. +A type can both have `call` members and be declared with `@dynamicCallable`. When type-checking a call expression, the type checker will first try to resolve the call to a function or initializer call, then a `call` member call, and finally a dynamic call. ### Direct reference to a `call` member @@ -388,7 +387,7 @@ The proposed feature adds a `call` keyword. Normally, this would require existin To maintain source compatibility, we propose making `call` a contextual keyword: that is, it is a keyword only in declaration contexts and a normal identifier elsewhere (e.g. in expression contexts). This means that `func call` and `call(...)` (apply expressions) continue to parse correctly. -Here’s a comprehensive example of parsing `call` in different contexts: +Here's a comprehensive example of parsing `call` in different contexts: ```swift struct Callable { @@ -446,13 +445,13 @@ struct Adder { } // Option: `call` declaration modifier on unnamed `func` declarations. // Makes unnamed `func` less weird and clearly states "call". - call func(_ x: Int) -> Int { … } + call func(_ x: Int) -> Int { ... } } ``` This approach represents call-syntax delegate methods as unnamed `func` declarations instead of creating a new `call` declaration kind. -One option is to use `func(...)` without an identifier name. Since the word "call" does not appear, it is less clear that this denotes a call-syntax delegate method. Additionally, it’s not clear how direct references would work: the proposed design of referencing `call` declarations via `foo.call` is clear and consistent with the behavior of `init` declarations. +One option is to use `func(...)` without an identifier name. Since the word "call" does not appear, it is less clear that this denotes a call-syntax delegate method. Additionally, it's not clear how direct references would work: the proposed design of referencing `call` declarations via `foo.call` is clear and consistent with the behavior of `init` declarations. To make unnamed `func(...)` less weird, one option is to add a `call` declaration modifier: `call func(...)`. The word `call` appears in both this option and the proposed design, clearly conveying "call-syntax delegate method". However, declaration modifiers are currently also treated as keywords, so with both approaches, parser changes to ensure source compatibility are necessary. `call func(...)` requires additional parser changes to allow `func` to sometimes not be followed by a name. The authors lean towards `call` declarations for terseness. @@ -496,6 +495,10 @@ Also, we want to support direct references to call-syntax delegate methods via ` struct Adder { var base: Int // Informal rule: all methods with a particular name (e.g. `func call`) are deemed call-syntax delegate methods. + // + // `StringInterpolationProtocol` has a similar informal requirement for + // `func appendInterpolation` methods. + // https://github.com/apple/swift-evolution/blob/master/proposals/0228-fix-expressiblebystringinterpolation.md#proposed-solution func call(_ x: Int) -> Int { return base + x } @@ -504,7 +507,7 @@ struct Adder { We feel this approach is not ideal because: -* A marker type attribute is not particularly meaningful. The call-syntax delegate methods of a type are what make values of that type callable - a type attribute means nothing by itself. In fact, there’s an edge case that needs to be explicitly handled: if a `@staticCallable` type defines no call-syntax delegate methods, an error must be produced. +* A marker type attribute is not particularly meaningful. The call-syntax delegate methods of a type are what make values of that type callable - a type attribute means nothing by itself. In fact, there's an edge case that needs to be explicitly handled: if a `@staticCallable` type defines no call-syntax delegate methods, an error must be produced. * The name for call-syntax delegate methods (e.g. `func call` ) is not first-class in the language, while their call site syntax is. #### Use a `Callable` protocol to represent callable types @@ -514,8 +517,6 @@ We feel this approach is not ideal because: struct Adder: Callable { var base: Int // Informal rule: all methods with a particular name (e.g. `func call`) are deemed call-syntax delegate methods. - // `StringInterpolationProtocol` has a similar informal requirement for `func appendInterpolation` methods. - // https://github.com/apple/swift-evolution/blob/master/proposals/0228-fix-expressiblebystringinterpolation.md#proposed-solution func call(_ x: Int) -> Int { return base + x } @@ -536,13 +537,13 @@ Halide::Var x, y; foo(x, y) = x + y; ``` -This can be achieved via Swift’s subscripts, which can have a getter and a setter. +This can be achieved via Swift's subscripts, which can have a getter and a setter. ```swift foo[x, y] = x + y ``` -Since the proposed `call` declaration syntax is like `subscript` in many ways, it’s in theory possible to allow `get` and `set` in a `call` declaration’s body. +Since the proposed `call` declaration syntax is like `subscript` in many ways, it's in theory possible to allow `get` and `set` in a `call` declaration's body. ```swift call(x: T) -> U { @@ -555,7 +556,7 @@ call(x: T) -> U { } ``` -However, we do not believe `call` should behave like a storage accessor like `subscript` . Instead, `call` ’s appearance should be as close to function calls as possible. Function call expressions today are not assignable because they can't return an l-value reference, so a call to a `call` member should not be assignable either. +However, we do not believe `call` should behave like a storage accessor like `subscript`. Instead, `call`'s appearance should be as close to function calls as possible. Function call expressions today are not assignable because they can't return an l-value reference, so a call to a `call` member should not be assignable either. ### Static `call` members From 5395e2dab534bc1f5317564f677ba1036ec4b1c5 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 27 Mar 2019 22:21:34 -0700 Subject: [PATCH 1083/4563] Add a short "future directions" section on delegating to an existing property --- proposals/NNNN-property-delegates.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 3979f284cc..3af35ec628 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -795,3 +795,14 @@ public struct MyStruct { There are a few issues here: first, subscripts don't allow `inout` parameters in the first place, so we would have to figure out how to implement support for such a feature. Second, passing `self` as `inout` while performing access to the property `self.myVar` violates Swift's exclusivity rules ([generalized accessors](https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md#generalized-accessors) might help address this). Third, property delegate types that want to support `subscript(instanceSelf:)` for both value and reference types would have to overload on `inout` or would have to have a different subscript name (e.g., `subscript(mutatingInstanceSelf:)`). So, while we feel that support for accessing the enclosing type's `self` is useful and as future direction, and this proposal could be extended to accommodate it, the open design questions are significant enough that we do not want to tackle them all in a single proposal. + +### Delegating to an existing property + +When specifying a delegate for a property, a backing storage property is implicitly created. However, it is possible that there already exists a property that can provide the backing. This allows greater control over the declaration of the backing property, e.g., to specify that it is `lazy` or `@objc` or to give it a more custom implementation: + +```swift +lazy var fooBacking: SomeDelegate +var foo: Int by var fooBacking +``` + +One could express this either by naming the property directly (as with the `by var` formulation above) or, for an even more general solution, by providing a keypath such as `\.someProperty.someOtherProperty`. From a4373feea90958edddc280446ad181e9b6385376 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Fri, 29 Mar 2019 00:38:32 -0700 Subject: [PATCH 1084/4563] Suspect SE-0250. The conversation needs to be re-adjusted to better frame the discussion for the community. --- proposals/0250-swift-style-guide-and-formatter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0250-swift-style-guide-and-formatter.md b/proposals/0250-swift-style-guide-and-formatter.md index 4f026b102a..d73191a9fb 100644 --- a/proposals/0250-swift-style-guide-and-formatter.md +++ b/proposals/0250-swift-style-guide-and-formatter.md @@ -3,7 +3,7 @@ * Proposal: [SE-0250](0250-swift-style-guide-and-formatter.md) * Authors: [Tony Allevato](https://github.com/allevato), [Dave Abrahams](https://github.com/dabrahams) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active review (March 19 - April 10, 2019)** +* Status: **Returned for revision** * Discussion thread: [An Official Style Guide and Formatter for Swift](https://forums.swift.org/t/pitch-an-official-style-guide-and-formatter-for-swift/21025) ## Introduction From 6fcf8116de3061fe8a84173ade1551d2cc4d18e0 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 29 Mar 2019 14:14:58 -0500 Subject: [PATCH 1085/4563] Mark key-path expressions proposal as accepted --- proposals/0249-key-path-literal-function-expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0249-key-path-literal-function-expressions.md b/proposals/0249-key-path-literal-function-expressions.md index ce651f7fdb..55cb32335d 100644 --- a/proposals/0249-key-path-literal-function-expressions.md +++ b/proposals/0249-key-path-literal-function-expressions.md @@ -3,7 +3,7 @@ * Proposal: [SE-0249](0249-key-path-literal-function-expressions.md) * Authors: [Stephen Celis](https://github.com/stephencelis), [Greg Titus](https://github.com/gregomni) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active review (March 19...27, 2019)** +* Status: **Accepted** * Implementation: [apple/swift#19448](https://github.com/apple/swift/pull/19448) ## Introduction From ea7837b9f90f7711b7915f39f4fd806586213c41 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 1 Apr 2019 17:35:59 +0300 Subject: [PATCH 1086/4563] Create 0000-where-on-contextually-generic.md --- .../0000-where-on-contextually-generic.md | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 proposals/0000-where-on-contextually-generic.md diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md new file mode 100644 index 0000000000..2f14de8f10 --- /dev/null +++ b/proposals/0000-where-on-contextually-generic.md @@ -0,0 +1,76 @@ +# `where` clauses on contextually generic declaractions + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Anthony Latsis](https://github.com/AnthonyLatsis) +* Review Manager: TBD +* Status: **Ongoing Discussion** +* Implementation: [apple/swift#23489](https://github.com/apple/swift/pull/23489) + +## Introduction + +The objective of this proposal is to lift the restriction on attaching `where` clauses to generic declarations that themselves +do not declare generic parameters, but inherit the surrounding generic environment. Simply put, this means you no longer have +to worry about the `'where' clause cannot be attached` error within generic contexts. + +```swift +struct Box { + func sequence() -> [Box] where Wrapped: Sequence { ... } +} + +``` + +> The described enhancement only applies to declarations that *can* be generic and already support being constrained via +> an extension. Properties and new constraint kinds are out +> of scope for this document. For example, the following remains an error: +> ```swift +> protocol P { +> // error: Instance method requirement 'foo(arg:)' cannot add constraint 'Self: Equatable' on 'Self' +> func foo() where Self: Equatable +> } +> + +Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/) + +## Motivation + +Today, `where` clauses on contextually generic declarations can only be expressed indirectly by placing them on conditional +extensions. Unless the constraints are identical, every such declaration requires a separate extension. This apparent +dependence on extensions is an obstacle to stacking up constraints or grouping semantically related APIs and usually +becomes a pain point in heavily generic code that unnecessarily complicates the structure of a program for the developer +and the compiler. + +Leaving ergonomic shortcomings behind, it is only natural for a `where` clause to work anywhere a constraint can be +meaningfully imposed, meaning both of the these layout variants should be possible: +```swift +struct Foo // 'Foo' can be any kind of nominal type declaration. For a protocol, 'T' would be an associatedtype. + +extension Foo where T: Sequence, T.Element: Equatable { + func slowFoo() { ... } +} +extension Foo where T: Sequence, T.Element: Hashable { + func optimizedFoo() { ... } +} +extension Foo where T: Sequence, T.Element == Character { + func specialCaseFoo() { ... } +} + +extension Foo where T: Sequence, T.Element: Equatable { + func slowFoo() { ... } + + func optimizedFoo() where T.Element: Hashable { ... } + + func specialCaseFoo() where T.Element == Character { ... } +} +``` +A move towards «untying» generic parameter lists and `where` clauses is an obvious and farsighted improvement to the generics +system with numerous future applications, including contextually generic computed properties, [opaque types](https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md), [generalized +existentials](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials) and constrained protocol requirements. + +## Source compatibility and ABI stability + +This is an additive change with no impact on the ABI and existing code. + +## Effect on API resilience + +For public declarations in resilient libraries, switching between a constrained extension and a «direct» `where` clause +will not be a source-breaking change, but it most likely will break the ABI due to subtle mangling differences. From 1391db5a6b4ce16113c51b206c862a62e9d34709 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 1 Apr 2019 17:47:37 +0300 Subject: [PATCH 1087/4563] Update 0000-where-on-contextually-generic.md --- proposals/0000-where-on-contextually-generic.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index 2f14de8f10..c701c9b1e8 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -8,7 +8,7 @@ ## Introduction -The objective of this proposal is to lift the restriction on attaching `where` clauses to generic declarations that themselves +The objective of this proposal is to lift the restriction on attaching `where` clauses to declarations that themselves do not declare generic parameters, but inherit the surrounding generic environment. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error within generic contexts. @@ -19,8 +19,8 @@ struct Box { ``` -> The described enhancement only applies to declarations that *can* be generic and already support being constrained via -> an extension. Properties and new constraint kinds are out +> Only declarations that can already be generic and support being constrained via a conditional +> extension are covered by the described enhancement. Properties and new constraint kinds are out > of scope for this document. For example, the following remains an error: > ```swift > protocol P { From 5d6d9a166a55f9372cb4dbd875cfbd4879cff6f0 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 1 Apr 2019 17:56:16 +0300 Subject: [PATCH 1088/4563] Update 0000-where-on-contextually-generic.md --- proposals/0000-where-on-contextually-generic.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index c701c9b1e8..7d9e0c2940 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -9,8 +9,7 @@ ## Introduction The objective of this proposal is to lift the restriction on attaching `where` clauses to declarations that themselves -do not declare generic parameters, but inherit the surrounding generic environment. Simply put, this means you no longer have -to worry about the `'where' clause cannot be attached` error within generic contexts. +do not carry a generic parameter list explicitly, but inherit the surrounding generic environment. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error within generic contexts. ```swift struct Box { From fb55401bbcdfb34ac8ec388d863ee6ec6a3d7aae Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 1 Apr 2019 18:21:26 +0300 Subject: [PATCH 1089/4563] Update 0000-where-on-contextually-generic.md --- proposals/0000-where-on-contextually-generic.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index 7d9e0c2940..b04210a0b7 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -1,4 +1,4 @@ -# `where` clauses on contextually generic declaractions +# `where` clauses on contextually generic declarations * Proposal: [SE-NNNN](NNNN-filename.md) * Authors: [Anthony Latsis](https://github.com/AnthonyLatsis) @@ -28,7 +28,7 @@ struct Box { > } > -Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/) +Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/where-clauses-on-contextually-generic-declaractions/22449) ## Motivation From 6c132c654aed1fc40f5aa1317755d89c1a8ad0d9 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Tue, 2 Apr 2019 14:23:25 +0300 Subject: [PATCH 1090/4563] Update 0000-where-on-contextually-generic.md --- proposals/0000-where-on-contextually-generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index b04210a0b7..3eea5dec17 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -39,7 +39,7 @@ becomes a pain point in heavily generic code that unnecessarily complicates the and the compiler. Leaving ergonomic shortcomings behind, it is only natural for a `where` clause to work anywhere a constraint can be -meaningfully imposed, meaning both of the these layout variants should be possible: +meaningfully imposed, meaning both of these layout variants should be possible: ```swift struct Foo // 'Foo' can be any kind of nominal type declaration. For a protocol, 'T' would be an associatedtype. From f2a33fecfd6380117c26e5a9957e6c100e4723ac Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Tue, 2 Apr 2019 10:32:04 -0700 Subject: [PATCH 1091/4563] Proposal: Static and class subscripts (#1007) * Add static subscripts proposal * Remove key paths from the static subscripts proposal --- proposals/0253-static-subscripts.md | 132 ++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 proposals/0253-static-subscripts.md diff --git a/proposals/0253-static-subscripts.md b/proposals/0253-static-subscripts.md new file mode 100644 index 0000000000..c96be5dbe7 --- /dev/null +++ b/proposals/0253-static-subscripts.md @@ -0,0 +1,132 @@ +# Static and class subscripts + +* Proposal: [SE-0253](0253-static-subscripts.md) +* Authors: [Brent Royal-Gordon](https://github.com/brentdax) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#23358](https://github.com/apple/swift/pull/23358) + +## Introduction + +We propose allowing `static subscript` and, in classes, `class subscript` declarations. These could be used through either `TypeName[index]` or `TypeName.self[index]` and would have all of the capabilities you would expect of a subscript. We also propose extending dynamic member lookup to static properties by using static subscripts. + +Swift-evolution thread: [Static Subscript](https://forums.swift.org/t/static-subscript/1229) (2016), [Pitch: Static and class subscripts](https://forums.swift.org/t/pitch-static-and-class-subscripts/21850) + +## Motivation + +Subscripts have a unique and useful combination of features. Like functions, they can take arguments to change their behavior and generic parameters to support many types; like properties, they are permitted as lvalues so their results can be set, modified, and passed as inout. This is a powerful feature set, which is why they are used for features like key paths and `@dynamicMemberLookup`. + +Unfortunately, unlike functions and properties, Swift only supports subscripts on regular types, not metatypes. This not only makes the language inconsistent, it also prevents us from supporting important language features on metatypes. + +>
    +> (Wait, what the heck is a "metatype"?) +> +> A type like `Int` has many instances, like `0` and `-42`. But Swift also creates a special instance representing the `Int` type itself, as opposed to any specific `Int` belonging to that type. This special instance can be directly accessed by writing `Int.self`; it is also returned by `type(of:)` and used in various other places. In fact, static members of `Int` are instance members of `Int.self`, so you use it any time you call one of those. +> +> Since `Int.self` is an instance, it must have a type, but the type of `Int.self` is not `Int`; after all, `Int.self` cannot do the things an `Int` can do, like arithmetic and comparison. Instead, `Int.self` is an instance of the type `Int.Type`. Because `Int.Type` is the type of a type, it is called a "metatype". +>
    + +And occasionally a subscript on a type is truly the best way to represent an operation. For instance, suppose you're offering access to the process's environment variables. Since the environment is global and environment variables can be both retrieved and set, a static subscript would be an excellent representation of them. Without them, users must either introduce a singleton instance or [use static properties or subscripts to expose the same operations with less fidelity](https://github.com/apple/swift-package-manager/blob/master/Sources/Basic/ProcessEnv.swift#L15). + +Swift originally omitted static subscripts for a good reason: They conflicted with an early sugar syntax for arrays, `Element[]`. But we have long since changed that syntax to `[Element]` and we aren't going back. There is no longer a technical obstacle to supporting them, and there never was a philosophical one. The only obstacle to this feature is inertia. + +It's time we gave it a little push. + +## Proposed solution + +In any place where it was previously legal to declare a `subscript`, it will now be legal to declare a `static subscript` as well. In classes it will also be legal to declare a `class subscript`. + +```swift +public enum Environment { + public static subscript(_ name: String) -> String? { + get { + return getenv(name).map(String.init(cString:)) + } + set { + guard let newValue = newValue else { + unsetenv(name) + return + } + setenv(name, newValue, 1) + } + } +} +``` + +The static and class subscripts on a type `T` can be used on any expression of type `T.Type`, including `T.self[...]` and plain `T[...]`. + +```swift +Environment["PATH"] += ":/some/path" +``` + +A static subscript with the parameter label `dynamicMember` can also be used to look up static properties on types marked with `@dynamicMemberLookup`. + +```swift +@dynamicMemberLookup +public enum Environment { + public static subscript(_ name: String) -> String? { + // as before + } + + public static subscript(dynamicMember name: String) -> String? { + get { return self[name] } + set { self.name = newValue } + } +} + +Environment.PATH += ":/some/path" +``` + +We do not currently propose to add support for metatype key paths, but this proposal is a necessary prerequisite for any future work on them. + +One concern brought up during the pitch phase was discoverability. We think that code completion changes will help with this, but those are outside the scope of an Evolution proposal. + +## Detailed design + +### Static subscripts + +Static and class subscripts can be declared everywhere static and class computed properties can be, with analogous language rules. In particular, static and class subscript accessors are implicitly `nonmutating` and cannot be made `mutating`, just like static and class computed property accessors. + +If a static or class subscript is declared on a type `T`, it can be applied to any value of type `T`, including `T.self`, `T`, and variables or other expressions evaluating to a value of type `T.Type`. + +Objective-C class methods with the same selectors as instance subscript methods (like `+objectAtIndexedSubscript:`) will not be imported to Swift as class subscripts; Objective-C technically allows them but doesn't make them usable in practice, so this is no worse than the native experience. Likewise, it will be an error to mark a static or class subscript with `@objc`. + +### Dynamic member lookup + +`@dynamicMemberLookup` can be applied to any type with an appropriate `subscript(dynamicMember:)` or `static subscript(dynamicMember:)` (or `class subscript(dynamicMember:)`, of course). If `subscript(dynamicMember:)` is present, it will be used to find instance members; if `static subscript(dynamicMember:)` is present, it will be used to find static members. A type can provide both. + +## Source compatibility + +This proposal is purely additive; it does not change any prevously existing behavior. All syntax it will add support for was previously illegal. + +## ABI compatibility and backwards deployment + +Static subscripts are an additive change to the ABI. They do not require any runtime support; the Swift 5.0 runtime should even demangle their names correctly. Dynamic member lookup is implemented in the type checker and has no backwards deployment concerns. + +## Effect on API resilience + +The rules for the resilience of static and class subscripts will be the same as the rules of their instance subscript equivalents. Dynamic member lookup does not impact resilience. + +## Alternatives considered + +### Leave our options open + +The main alternative is to defer this feature again, leaving this syntax unused and potentially available for some other purpose. + +The most compelling suggestion we've seen is using `Element[n]` as type sugar for fixed-size arrays, but we don't think we would want to do that anyway. If fixed-size arrays need a sugar at all, we would want one that looked like an extension of the existing `Array` sugar, like `[Element * n]`. We can't really think of any other possibilities, so we feel confident that we won't want the syntax back in a future version of Swift. + +## Future directions + +### Metatype key paths + +Swift does not currently allow you to form keypaths to or through static properties. This was no loss before static subscripts, since you wouldn't have been able to apply them to a metatype anyway. But now that we have static subscripts, metatype keypaths could be supported. + +Metatype key paths were left out of this proposal because they are more complex than dynamic member lookup: + +1. Making them backwards deploy requires certain compromises, such as always using opaque accessors. Are these worth the cost? + +2. If we allow users to form key paths to properties in resilient libraries built before static key paths are supported, their `Equatable` and `Hashable` conformances may return incorrect results. Should we accept that as a minor issue, or set a minimum deployment target? + +3. Metatypes have kinds of members not seen in instances; should we allow you to form key paths to them? (Forming a key path to a no-argument case may be particularly useful.) + +These issues both require more implementation effort and deserve more design attention than metatype key paths could get as part of this proposal, so it makes sense to defer them. Nevertheless, this proposal is an important step in the right direction. From a94a51e7774184e73b5b87a05acaa1686709250d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 2 Apr 2019 10:37:22 -0700 Subject: [PATCH 1092/4563] Initiate review of SE-0254 "Static and class subscripts" --- ...253-static-subscripts.md => 0254-static-subscripts.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{0253-static-subscripts.md => 0254-static-subscripts.md} (95%) diff --git a/proposals/0253-static-subscripts.md b/proposals/0254-static-subscripts.md similarity index 95% rename from proposals/0253-static-subscripts.md rename to proposals/0254-static-subscripts.md index c96be5dbe7..00de13e95a 100644 --- a/proposals/0253-static-subscripts.md +++ b/proposals/0254-static-subscripts.md @@ -1,10 +1,10 @@ # Static and class subscripts -* Proposal: [SE-0253](0253-static-subscripts.md) +* Proposal: [SE-0254](0254-static-subscripts.md) * Authors: [Brent Royal-Gordon](https://github.com/brentdax) -* Review Manager: TBD -* Status: **Awaiting review** -* Implementation: [apple/swift#23358](https://github.com/apple/swift/pull/23358) +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (April 2...9, 2019)** +* Implementation: [apple/swift#23358](https://github.com/apple/swift/pull/23358), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/264//artifact/branch-master/swift-PR-23358-264-osx.tar.gz), [Ubuntu Linux 16.04 toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/205//artifact/branch-master/swift-PR-23358-205-ubuntu16.04.tar.gz) ## Introduction From 196c4966bceb5baa11873170a19007f9fdb6136e Mon Sep 17 00:00:00 2001 From: nate-chandler <46721658+nate-chandler@users.noreply.github.com> Date: Tue, 2 Apr 2019 13:23:38 -0700 Subject: [PATCH 1093/4563] [SE-0255] Implicit Returns from Single-Expression Functions (#1015) * [SE-NNNN] Implicit Returns from Single-Expression Functions --- proposals/0255-omit-return.md | 361 ++++++++++++++++++++++++++++++++++ 1 file changed, 361 insertions(+) create mode 100644 proposals/0255-omit-return.md diff --git a/proposals/0255-omit-return.md b/proposals/0255-omit-return.md new file mode 100644 index 0000000000..d901e6808d --- /dev/null +++ b/proposals/0255-omit-return.md @@ -0,0 +1,361 @@ +# Implicit returns from single-expression functions + +* Proposal: [SE-0255](NNNN-omit-return.md) +* Author: [Nate Chandler](https://github.com/nate_chandler) +* Review Manager: TBD +* Status: **Active review (April 2 - April 11, 2019)** +* Implementation: [apple/swift#23251](https://github.com/apple/swift/pull/23251) +* Previous Proposal: [SE-NNNN](https://github.com/DevAndArtist/swift-evolution/blob/single_expression_optional_return/proposals/nnnn-single-expression-optional-return.md) + +## Introduction + +Swift provides a pleasant shorthand for short closures: if a closure contains just a single expression, that expression is implicitly returned--the `return` keyword can be omitted. We should provide this shorthand for functions as well. + +Swift-evolution thread: [Pitch: Implicit Returns from Single-Expression Functions](https://forums.swift.org/t/pitch-implicit-returns-from-single-expression-functions/21898) + +## Motivation + +Consider the following implementation of the popular `Collection.sum()` extension: + +```swift +extension Sequence where Element == Int { + + func sum() -> Element { + return reduce(0, +) + } + +} +``` + +The implementation is extremely brief, weighing in at 19 characters. Of those 19, however, 7 are consumed by the `return` keyword. + +## Proposed solution + +Here's how that same property would look if the single-expression in the body is implicitly returned: + +```swift +func sum() -> Element { + reduce(0, +) +} +``` + +The expression which is being returned is front and center, not tucked away behind the `return` keyword. + +For readers previously exposed to single-expression closure syntax, this will feel completely familiar. + +Even to readers without such exposure, though, the meaning will be clear: When reading the implementation--after the `var` keyword and name--you first encounter the return type and then the single expression within the body. Since you've just read the return type, can see that there's only one expression in the body, and are told by the compiler that the code is legal, you are forced to conclude that the expression must indeed be returned. + +In fact, exposure to functions--whose types are always stated--without an explicit `return` keyword will help prepare new Swift users to understand code like + +```swift +let ages = persons.map { $0.name } +``` + +Their prior exposure to functions which implicitly return the single expression in their bodies will lead them to conclude that the closure being passed to map is returning the expression `$0.name` and that the return type of the closure is `String` (the type of `name`, here). + +## Detailed design + +Interpret the bodies of function-like entities which consist of a single expression as returning that expression--unless the entity's return type is `Void` or the single expression's type is uninhabited. + +#### Function-like entities + +The following are the function-like entities eligible to implicitly return the single expression in their bodies: + +- Functions. For example: +```swift +func add(lhs: Int, rhs: Int) -> Int { lhs + rhs } +``` + +- Property accessors. + +With an implicit getter: + +```swift +var location: Location { .init(latitude: lat, longitude: long) } +``` + +With an explicit getter and setter: + +```swift +var location: Location { + get { + .init(latitude: lat, longitude: long) + } + set { + self.lat = newValue.latitude + self.long = newValue.longitude + } +} +``` + +Since only the `get` accessor may return a value, implicit returns from single-expression accessors will only affect them. + +- Subscript accessors. + +With an implicit getter: + +```swift +struct Echo { + subscript(_ value: T) -> T { value } +} +``` + +With an explicit getter and setter: + +```swift +struct GuaranteedDictionary { + var storage: [Key : Value] + var fallback: Value + subscript(key: Key) -> Value { + get { + storage[key] ?? fallback + } + set { + storage[key] = newValue + } + } +} +``` + +As with property accessors, since only the `get` accessor may return a value, implicit returns only affect them. + +- Initializers. + +```swift +class Derived : Base { + required init?() { nil } +} +``` + +The only legal return from an initializer is `nil`, and that only in the context of a failable initializer. As a result, that is the only place where an implicit return from an initializer can occur. + +#### Exceptions + +When a function-like entity's body consists of a single expression, there are two cases where no implicit return will be inserted: + +- `Void` return. In the following code + +```swift +func foo() { + logAndReturn("foo was called") +} + +@discardableResult +func logAndReturn(_ string: String) -> String { ... } +``` + +adding an implicit return to `foo` would result in a type error, namely, `unexpected non-void return in a void function`. It is reasonable to be able to call a function (here, `logAndReturn`) which returns a value as the only operation performed by another function (here `foo`) which does not return a value. Moreover, `foo` as written is legal code, so we want to avoid treating this as a type error since doing so would result in source breakage. + +- Uninhabited expressions. In the following code + +```swift +func vendAnInteger() -> Int { + fatalError() +} +``` + +adding an implicit return would result in the analogous type error (`cannot convert return expression of type 'Never' to return type 'Int'`). Functions which return values but whose implementations consist solely of a single call to a `Never` returning function are an established practice in Swift--they allow users to put off defining their functions until they are ready to (or forever). With implicit returns, this function's implementation will have the same meaning as it has today: The code will compile. No implicit return will be inserted. And at runtime the call to `fatalError()` will never return. Source compatibility will be preserved. + +There is one exception, as described in the section below: + +## Source compatibility + +For the most part, the change is additive, making legal code that is currently illegal. It does, however, break source compatibility in one case. + +In current Swift, when the following code is compiled + +``` +func bad(value: Int = 0) -> Int { return 0 } +func bad() -> Never { return fatalError() } + +func callBad() -> Int { bad() } +``` + +the call to `bad()` in `callBad()` resolves to the second overload of that name (whose signature is `() -> Never`). With implicit return, the call will instead resolve to the first overload. + +The large downside of breaking source-compatibility is mitigated by the fact that overload sets of which only one member returns `Never` are very rare: Extensive source compatibility tests have been run against this change without issue. + +## Effect on ABI stability + +None. Implementation is only in the parser and type checker. + +## Effect on API resilience + +None. + +## Alternatives considered + +- **Maintain source compatibility.** + +Maintaining source compatibility entails teaching the overload resolution system a special case for single-expression functions. It is possible to do this but would require complicating the type checker. Far worse it would complicate the language model: + +If source compatibility were maintained, the following two functions + +``` +func callBad_1() -> Int { bad() } + + +func callBad_2() -> Int { return bad() } +``` + +would have different behaviors: `callBad_1` would trap and `callBad_2` would return an `Int`. + +In a Swift with implicit return for single-expression functions, the mental model for users should be that a `return` can be omitted in certain cases and that it doesn't matter whether one is included or not. Preserving source-compatibility in this case would break that mental model. + +  + +- **Permit implicit return for a subset of declarations.** + +This document proposes allowing `return` to be omitted from the following declarations: +- functions +- properties +- subscripts +- initializers + +An alternative would be to allow that omission in only a subset of these. + +Concretely, several reasons were given for allowing it in only get-only computed properties: + +(1) Unlike functions, get-only properties already have one shorthand, the omission of `get`. By analogy to the situation with closures, that indicates that they are eligible for the further shorthand of omitting `return`. + +Response: This argument applies equally to subscripts which support the same shorthand as properties. If the reason to permit the `return` to be omitted from properties is that `get` can already be omitted, then that reason leads also to permitting `return` to be omitted from get-only subscripts. + +The differences between get-only subscripts and functions are already few and may be getting fewer ( https://forums.swift.org/t/pitch-static-and-class-subscripts/21850 , https://forums.swift.org/t/draft-throwing-properties-and-subscripts/1786 ). It would amount to a language inconsistency to allow get-only subscripts but not functions to omit `return`. + +(2) Unlike functions, get-only properties always have a return type. + +Response: In standard usage, it is much more common to encounter functions which return `Void` than properties. However, while that usage is far more common, the following is still part of the language: + +```swift +var doWork: Void { + work() +} +``` + +  + +- **Making uninhabited types be bottom types.** + +As currently implemented, an implicit conversion from an uninhabited type to any arbitrary type is permitted only if the uninhabited type is the type of the expression in a single-argument function and the arbitrary type is the the result type of that function. If every uninhabited type were a subtype of every type, this implicit conversion could be applied across the board without special casing for the single-argument return scenario. + +While such a feature can be implemented (see the [uninhabited-upcast](https://github.com/nate-chandler/swift/tree/nate/uninhabited-upcast) branch), it doesn't maintain source compatibility or otherwise relate to this feature except in terms of the compiler's implementation. + +  + +- **Use braceless syntax for single-expression functions.** + +Some other languages such as Scala and Kotlin allow single-expression functions to be declared without braces. In Kotlin, this looks like + +```kotlin +fun squareOf(x: Int): Int = x * x +``` + +and in Scala, it looks almost identical (the only difference being the use of `def` instead of `fun`). + +```scala +def squareOf(x: Int): Int = x * x +``` + +Those languages' syntax suggests a similar approach be taken in Swift: + +```swift +func square(of x: Int) -> Int = x * x +``` + +For functions, this might be fine. For Swift to be self-consistent, a somewhat similar would be needed for properties and subscripts. + +```swift +var value: Int { + get = _storedValue + set { _storedValue = newValue } +} +``` + +Unfortunately, this begins moving into ambiguous territory: + +```swift +var get: () +var value: Void { + get = () +} +``` + +In this example, it's unclear whether the braces of `value` either (1) enclose an explicit getter for `value` whose implementation is a single-expression function returning `()` or alternatively (2) enclose the body of an implicit getter whose implementation sets the `get` property to `()`. + +  + +- **Allow implicit return of the last expression even from bodies which consist of more than a single expression.** + +Rust, for example, permits this. Given functions `foo`, `bar`, and `baz`, all which return integers, the following is a legal function in Rust: + +```rust +fn call() -> i64 { + foo(); + bar(); + baz() +} +``` + +While this could be permitted in Swift, doing so would lead to asymmetry in code resulting from the fact that Swift is not expression-oriented as Rust is. Consider a function with some basic branching: + +```swift +func evenOrOdd(_ int: Int) -> EvenOrOdd { + if int % 2 == 0 { + return .even + } + .odd +} +``` + +Here `.even` is returned for even `Int`s and `.odd` for odd. Notice that only one of the two returns from the function uses the return keyword! The same unpleasant function could be written in Rust: + +```rust +fn even_or_odd(i: i64) -> EvenOrOdd { + if i % 2 == 0 { + return EvenOrOdd::Even + } + EvenOrOdd::Odd +} +``` + +In Rust, though, the asymmetry could be resolved by implicitly returning the entire `if` expression: + +```rust +fn even_or_odd(i: i64) -> EvenOrOdd { + if i % 2 == 0 { + EvenOrOdd::Even + } else { + EvenOrOdd::Odd + } +} +``` + +That option is not open to us in Swift because conditionals are statements, not expressions in Swift. Changing Swift into an expression-oriented language would be a radical transformation to the language and is beyond the scope of this change. + +  + +- **Allow the return type to be omitted from the function declarations.** + +Scala, for example, permits this. In the following code + +```scala +def squareOf(x: Int) = x * x +``` + +the compiler infers that the type of `squareOf` is `(Int) -> Int`. + +Haskell takes this further, permitting functions to be written without either explicit inputs or outputs: + +```haskell +{-# LANGUAGE PartialTypeSignatures #-} +fac :: _ +fac 0 = 1 +fac n = n * fac (n - 1) +``` + +While these features are arguably nice, they greatly increase the complexity of type inference, and are out of scope for this change. + +## Acknowledgments + +A special thanks to Adrian Zubarev for his prior exploration of the design space culminating in his [proposal](https://github.com/DevAndArtist/swift-evolution/blob/single_expression_optional_return/proposals/nnnn-single-expression-optional-return.md). + From 5c2f592fd295d48afd5cb9e4e8beeb10db6a810b Mon Sep 17 00:00:00 2001 From: Bas Broek Date: Tue, 2 Apr 2019 22:38:31 +0200 Subject: [PATCH 1094/4563] Update proposal link and author (#1016) --- proposals/0255-omit-return.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0255-omit-return.md b/proposals/0255-omit-return.md index d901e6808d..277834279a 100644 --- a/proposals/0255-omit-return.md +++ b/proposals/0255-omit-return.md @@ -1,7 +1,7 @@ # Implicit returns from single-expression functions -* Proposal: [SE-0255](NNNN-omit-return.md) -* Author: [Nate Chandler](https://github.com/nate_chandler) +* Proposal: [SE-0255](0255-omit-return.md) +* Author: [Nate Chandler](https://github.com/nate-chandler) * Review Manager: TBD * Status: **Active review (April 2 - April 11, 2019)** * Implementation: [apple/swift#23251](https://github.com/apple/swift/pull/23251) From c5b615c1eb0abb7f983aa62db036c544f2432636 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 2 Apr 2019 15:10:12 -0700 Subject: [PATCH 1095/4563] Ben Cohen is the review manager for SE-0255. --- proposals/0255-omit-return.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0255-omit-return.md b/proposals/0255-omit-return.md index 277834279a..ff1a40982c 100644 --- a/proposals/0255-omit-return.md +++ b/proposals/0255-omit-return.md @@ -2,7 +2,7 @@ * Proposal: [SE-0255](0255-omit-return.md) * Author: [Nate Chandler](https://github.com/nate-chandler) -* Review Manager: TBD +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Active review (April 2 - April 11, 2019)** * Implementation: [apple/swift#23251](https://github.com/apple/swift/pull/23251) * Previous Proposal: [SE-NNNN](https://github.com/DevAndArtist/swift-evolution/blob/single_expression_optional_return/proposals/nnnn-single-expression-optional-return.md) From 4f7c52e7eb50111d3f5125076fefecdb8a2200c1 Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Tue, 2 Apr 2019 22:18:53 -0700 Subject: [PATCH 1096/4563] Clarify SE-0253: introducing callables. (#1014) * Clarify SE-0253: introducing callables. - Clearly state supported/unsupported modifiers/attributes for `call` declarations. - Add an early blurb stating that alternative syntaxes are explored in the "Alternatives considered" section. - Other minor changes. * Minor rephrasing. --- proposals/0253-callable.md | 106 +++++++++++++++++++++++++++++++++---- 1 file changed, 96 insertions(+), 10 deletions(-) diff --git a/proposals/0253-callable.md b/proposals/0253-callable.md index 28d081ea6e..4b897c1f53 100644 --- a/proposals/0253-callable.md +++ b/proposals/0253-callable.md @@ -95,7 +95,7 @@ extension Polynomial { } } let polynomial = Polynomial(coefficients: [2, 3, 4]) -// Callable syntax. +// Call syntax. print(polynomial(2)) // => 24 ``` @@ -179,7 +179,7 @@ struct Model { var dense = Dense(inputSize: 36 * 6, outputSize: 10) call(_ input: Tensor) -> Tensor { - // Callable syntax. + // Call syntax. return dense(flatten(maxPool(conv(input)))) } } @@ -217,7 +217,7 @@ call(_ input: String) throws -> Output { ```swift let sexpParser: Parser = ... -// Callable syntax. +// Call syntax. let sexp = sexpParser("(+ 1 2)") ``` @@ -257,6 +257,10 @@ let add3 = Adder(base: 3) add3(10) // => 13 ``` +Note: there are many alternative syntaxes for marking "call-syntax delegate +methods". These are listed and explored in the ["Alternatives +considered"](#alternative-ways-to-denote-call-syntax-delegate-methods) section. + ## Detailed design ### `call` member declarations @@ -266,18 +270,94 @@ add3(10) // => 13 A `call` member declaration is similar to `subscript` in the following ways: * It does not take a name. -* It must be an instance member of a type. +* It must be an instance member of a type. (Though there is [a pitch to add + static and class subscripts][static-and-class-subscripts].) But it is more similar to a `func` declaration in that: * It does not allow `get` and `set` declarations inside the body. -* When a parameter has a name, it is treated as the argument label. -* It can throw. +* When a parameter has a name, the name is treated as the argument label. * It can be referenced directly by name, e.g. `foo.call`. -The rest of the `call` declaration grammar and semantics is identical to that of function declarations–same syntax for access level, generics, argument labels, return types, throwing, mutating, `where` clause, etcs. They can be overloaded based on argument and result types. Attributes that can be applied to function declarations can also be applied to `call` declarations. - -To support source compatibility, `call` is treated as a keyword only when parsing members of a nominal type. Otherwise, it is treated as a normal identifier. See the source compatibility section below. +The rest of the `call` declaration grammar and semantics is identical to that of +function declarations – it supports the same syntax for: +- Access levels. +- Generic parameter clauses. +- Argument labels. +- Return types. +- `throws` and `rethrows`. +- `mutating`. +- `where` clauses. + +`call` declarations can be overloaded based on argument and result types. +`call` declarations are inherited from superclasses, just like other class members. +Most modifiers/attributes that can be applied to function declarations can also be applied to +`call` declarations. + +
    +Click here for a comprehensive list of modifiers/attributes supported by call declarations. + +
    + +Preface: `call` declarations are implemented as a `CallDecl` class, inheriting +from `FuncDecl`, which in tern inherits from `AbstractFunctionDecl`. + +### Supported modifiers/attributes on `call` declarations + +The following attributes are supported on `AbstractFunctionDecl` or all +declarations, and thus by default are supported on `call` declarations. +(Disabling these attributes on `call` declarations is possible, but may require +ad-hoc implementation changes.) + +* `fileprivate`, `internal`, `public`, `open` +* `@available` +* `@objc` +* `@inlinable` +* `@inline` +* `@usableFromInline` +* `@_alwaysEmitIntoClient` +* `@_dynamicReplacement` +* `@_effects` +* `@_forbidSerializingReference`` +* `@_optimize` +* `@_silgen_name` +* `@_semantics` +* `@__raw_doc_comment` + +The following attributes are supported on `FuncDecl`, and are also are supported on `call` declarations. + +* `final` +* `optional` +* `dynamic` +* `__consuming` +* `mutating` +* `nonmutating` +* `override` +* `private` +* `rethrows` +* `@discardableResult` +* `@nonobjc` +* `@_cdecl` +* `@_implements` +* `@_implicitly_unwrapped_optional` +* `@_nonoverride` +* `@_specialize_` +* `@_transparent` +* `@_weakLinked` + +### Notable unsupported modifiers/attributes on `call` declarations + +* `@warn_unqualified_access` + * It would be weird to require qualified access (e.g. `self.call` in another + member) for `call` members, given their purpose as a call-syntax delegate + method. + +
    +
    + +To support source compatibility, `call` is treated as a keyword only when +parsing members of a nominal type. Otherwise, it is treated as a normal +identifier. See the source compatibility section below. ``` call-declaration → call-head generic-parameter-clause? function-signature generic-where-clause? function-body? @@ -507,7 +587,11 @@ struct Adder { We feel this approach is not ideal because: -* A marker type attribute is not particularly meaningful. The call-syntax delegate methods of a type are what make values of that type callable - a type attribute means nothing by itself. In fact, there's an edge case that needs to be explicitly handled: if a `@staticCallable` type defines no call-syntax delegate methods, an error must be produced. +* A marker type attribute is not particularly meaningful. The call-syntax + delegate methods of a type are what make values of that type callable - a type + attribute means nothing by itself. There's an unforunate edge case that must + be explicitly handled: if a `@staticCallable` type defines no call-syntax + delegate methods, an error must be produced. * The name for call-syntax delegate methods (e.g. `func call` ) is not first-class in the language, while their call site syntax is. #### Use a `Callable` protocol to represent callable types @@ -597,3 +681,5 @@ np.random.randint(-10, 10, dtype: np.float) // `PythonObject` with `call` members. The empty strings are killer. np.random.randint(["": -10, "": 10, "dtype": np.float]) ``` + +[static-and-class-subscripts]: https://forums.swift.org/t/pitch-static-and-class-subscripts/21850 From 96556269d8fa9329e8d797b70a1ce00210c7fed7 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 2 Apr 2019 23:39:23 -0700 Subject: [PATCH 1097/4563] ContiguousCollection protocol revisited (#990) * First cut * Switch to vDSP_vadd * reindent * Typo in vDSP_vadd * Update proposals/NNNN-contiguous-collection.md Co-Authored-By: airspeedswift * Add default implementation of withContiguousStorageIfAvailable * Flesh out alternatives section * Array caveats * Update NNNN-contiguous-collection.md --- proposals/NNNN-contiguous-collection.md | 223 ++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 proposals/NNNN-contiguous-collection.md diff --git a/proposals/NNNN-contiguous-collection.md b/proposals/NNNN-contiguous-collection.md new file mode 100644 index 0000000000..1d96f8ca22 --- /dev/null +++ b/proposals/NNNN-contiguous-collection.md @@ -0,0 +1,223 @@ +# Introduce `{Mutable}ContiguousCollection` protocol + +* Proposal: [SE-NNNN](NNNN-contiguous-collection.md) +* Authors: [Ben Cohen](https://github.com/airspeedswift) +* Review Manager: TBD +* Status: **Awaiting review** +* Previous Proposal: [SE-0237](0237-contiguous-collection.md) +* Implementation: [apple/swift#23616](https://github.com/apple/swift/pull/23616) + +## Introduction + +This proposal introduces two new protocols: `ContiguousCollection`, which +refines `Collection`, and `MutableContiguousCollection`, which refines +`MutableCollection`. Both provide guaranteed access to an underlying +unsafe buffer. + +## Motivation + +[SE-0237](https://github.com/apple/swift-evolution/blob/master/proposals/0237-contiguous-collection.md) +introduced two new methods, `withContiguous{Mutable}StorageIfAvailable`, to +allow generic algorithms to test for and use an underlying unsafe buffer when +it is available. This has significant performance benefit for certain +algorithms, such as `sort`, which can achieve big speedups when they +have access to the unsafe buffer, but can still operate without that fast path +if needed. + +There is another class of operation that _only_ wants to be available when +there is a fast path. A good example would be a Swift-friendly wrapper for +the `vDSP` suite of algorithms. + +For example, you might want to write a convenient wrapper for `vDSP_vadd`. + +```swift +// note this is **not** a proposal about vDSP wrappers, this is just a +// simplified example :) +func dspAdd( + _ a: A, _ b: B, _ result: inout [Float] +) where A.Element == Float, B.Element == Float { + let n = a.count + // try accessing contiguous underlying buffers: + let wasContiguous: ()?? = + a.withContiguousStorageIfAvailable { abuf in + b.withContiguousStorageIfAvailable { bbuf in + vDSP_vadd(abuf.baseAddress!, 1, bbuf.baseAddress!, 1, &result, 1, UInt(n)) + } + } + // if they weren't contiguous, create two arrays try again + if wasContiguous == nil || wasContiguous! == nil { + dspAdd(Array(a), Array(b), &result) + } +} +``` + +This follows a similar pattern to `sort`: provide a fast path when available, +but fall back to a slower path when it isn't. + +But in the case of functions like `vDSP_vsaddi` this is very much the wrong +thing to do. These functions often operate on a thin (but very material) +performance edge over their open-coded equivalent, and allocating and +initializing two arrays purely to be able to call it would probably vastly +outweigh the speed benefits gained by using the function instead of a regular +loop. This encourages misuse by the caller, who might not realize they are +getting worse performance than if they reorganized their code. + +Trapping on non-contiguous inputs would flag the problem more clearly to the +user, who could realize immediately on first use, that types like `Range` +should not be used with this API. But ideally we would use Swift's type system +to enforce this instead, guiding the user to a better solution. + +## Proposed solution + +Introduce two new protocols which guarantee access to a contiguous underlying +buffer. + +```swift +/// A collection that supports access to its underlying contiguous storage. +public protocol ContiguousCollection: Collection +where SubSequence: ContiguousCollection { + /// Calls a closure with a pointer to the array's contiguous storage. + func withUnsafeBufferPointer( + _ body: (UnsafeBufferPointer) throws -> R + ) rethrows -> R +} + +/// A collection that supports mutable access to its underlying contiguous +/// storage. +public protocol MutableContiguousCollection: ContiguousCollection, MutableCollection +where SubSequence: MutableContiguousCollection { + /// Calls the given closure with a pointer to the array's mutable contiguous + /// storage. + mutating func withUnsafeMutableBufferPointer( + _ body: (inout UnsafeMutableBufferPointer) throws -> R + ) rethrows -> R +} +``` + +Conformances will be added for the following types: + +- `Array`, `ArraySlice` and `ContiguousArray` will conform to `MutableContiguousCollection` +- `UnsafeBufferPointer` will conform to `ContiguousCollection` +- `UnsafeMutableBufferPointer` will conform to `MutableContiguousCollection` +- `Slice` will conditionally conform: + - to `ContiguousCollection where Base: ContiguousCollection` + - to `MutableContiguousCollection where Base: MutableContiguousCollection` + +Conforming to to `ContiguousCollection` should also provide types with a default +implementation of `Collection.withContiguousStorageIfAvailable`, via an extension +that calls `withUnsafeBufferPointer`. Same for `MutableContiguousCollection` and +`Collection.withMutableContiguousStorageIfAvailable`. + +## Detailed design + +The introduction of these protocols allows an appropriate constraint that would +prevent a user passing a `Range` or `Repeating` collection into our `dspAdd` +function. It also allows an easy path to a generic result buffer instead of a +concrete array; this is important as often these functions are used in a tiled +mode where you would want to repeatedly pass in an array slice. As a nice +side-benefit, it also cleans up the function implementation: + +```swift +func dspAdd( + _ a: A, _ b: B, _ result: inout R +) where A.Element == Float, B.Element == Float, R.Element == Float { + let n = a.count + a.withUnsafeBufferPointer { abuf in + b.withUnsafeBufferPointer { bbuf in + result.withUnsafeMutableBufferPointer { rbuf in + vDSP_vadd(abuf.baseAddress!, 1, bbuf.baseAddress!, 1, rbuf.baseAddress!, 1, UInt(n)) + } + } + } +} +``` + +## Source compatibility + +These are additive changes and do not affect source compatibility. + +## Effect on ABI stability + +These are additive changes of new protocols and so can be introduced in an +ABI-stable way. On platforms that have declared ABI stability, they will need +to have availability annotations. + +## Effect on API resilience + +N/A + +## Alternatives considered + +This is a re-pitch of these protocols. They originally appeared in SE-0237, but +were deferred pending further use cases. This proposal is motivated by such +cases. + +As mentioned in the motivation, there are some alternatives available in the +absense of this protocol: + +- Libraries can add their own version of the protocol, and retroactively + conform standard library types to it. This is a workable solution, but has some downsides: + + - Non-standard types will not conform. Callers will need to conform these types to the + library's protocol themselves manually. A standard library protocol is more likely to + be adopted by other types. + + - If this pattern proves common, it will lead to multiple libraries declaring + the same protocol. + +- Libraries can use the `IfAvailable` variant, and document that using types + without contiguous storage is inefficient. This leaves enforcing the correct + usage to the user. This is not always possible in a generic context, where + the calling function does not know exactly what concrete type they are using. + +- Libraries can use the `IfAvailable` variant, and trap when not available. + This could alert callers to inefficient usage on first use. For example, if + you ever pass in a `Range`, your call will always fail, detectable by any + testing. Some types, however, may respond to the `IfAvailable` call in some + cases but not others. For example, a ring buffer might often not have wrapped + around, so can provide a single contiguous buffer sometimes, but not always. + Trapping then would lead to unpredictable crashes. + +### `Array` and lazy bridging + +The conformance of `Array` to these protocols presents a particular concern. + +`Array` at its core is a contiguously stored block of memory, and so naturally +lends itself to conformance to `ContiguousCollection`. However, this has one +specific carved-out exception on Darwin platforms for the purposes of +Objective-C interop. When an `NSArray` of classes is bridged from Objective-C +code, it remains as an `NSArray`, and the `Array` forwards element accesses to +that bridged `NSArray`. + +This is very different from `NSArray` itself, which abstracts away the storage +to a much greater degree, giving it flexibility to present an "array-like" +interface to multiple different backings. + +Here's a run-down of when Array will be contiguously stored: + +- Arrays created in Swift will **always** be contiguously stored; + +- Arrays of structs and enums will **always** be contiguously stored; + +- Arrays on platforms without an Objective-C runtime (i.e. non-Darwin + platforms) are **always** contiguously stored; + +- The only time an array **won't** be contiguously stored is if it is of + classes and has been bridged from an `NSArray`. Even then, in many cases, the + NSArray will be contiguously stored and could present a pointer at no or + amortized cost. + +These caveats should be documented clearly on both the protocol and on `Array`. +Note that in use cases such as the `vDSP` family of functions, this is not a +concern as the element types involved are structs. The documented caveat +approach has precedent in several places already in Swift: the conformance of +floating-point types to `Comparable`, the complexity of operations like `first` +and `last` on some lazy random-access types, and of the existing implementation +of `withUnsafeBufferPointer` on `Array` itself. + +Note that these caveats only apply to the un-mutable variant. The +first thing an array does when you call a mutating method is ensure that it's +uniquely referenced and contiguous, so even lazily bridged arrays will become +contiguous at that point. This copying occurs naturally in other cases, such +as multiply-referenced CoW buffers. + From d53a0847a587721d20535c2f00a0326f3ddba2e6 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 2 Apr 2019 23:41:55 -0700 Subject: [PATCH 1098/4563] Start review for SE-0256. --- proposals/NNNN-contiguous-collection.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/NNNN-contiguous-collection.md b/proposals/NNNN-contiguous-collection.md index 1d96f8ca22..dbfb53233f 100644 --- a/proposals/NNNN-contiguous-collection.md +++ b/proposals/NNNN-contiguous-collection.md @@ -1,9 +1,9 @@ # Introduce `{Mutable}ContiguousCollection` protocol -* Proposal: [SE-NNNN](NNNN-contiguous-collection.md) +* Proposal: [SE-0256](0256-contiguous-collection.md) * Authors: [Ben Cohen](https://github.com/airspeedswift) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Status: **Active review (April 3 - April 12, 2019)** * Previous Proposal: [SE-0237](0237-contiguous-collection.md) * Implementation: [apple/swift#23616](https://github.com/apple/swift/pull/23616) From 26c9c2c2330e71c042c260cb4f5c87ac1188a5ce Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 2 Apr 2019 23:42:23 -0700 Subject: [PATCH 1099/4563] Rename NNNN-contiguous-collection.md to 0256-contiguous-collection.md --- ...NNN-contiguous-collection.md => 0256-contiguous-collection.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{NNNN-contiguous-collection.md => 0256-contiguous-collection.md} (100%) diff --git a/proposals/NNNN-contiguous-collection.md b/proposals/0256-contiguous-collection.md similarity index 100% rename from proposals/NNNN-contiguous-collection.md rename to proposals/0256-contiguous-collection.md From 3ed554f44c0f21b330f4389bde7fefb5ad29df1f Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 2 Apr 2019 23:49:40 -0700 Subject: [PATCH 1100/4563] SE-0252 has been accepted. --- proposals/0252-keypath-dynamic-member-lookup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0252-keypath-dynamic-member-lookup.md b/proposals/0252-keypath-dynamic-member-lookup.md index 2623fe5f14..b7bff29eb7 100644 --- a/proposals/0252-keypath-dynamic-member-lookup.md +++ b/proposals/0252-keypath-dynamic-member-lookup.md @@ -3,7 +3,7 @@ * Proposal: [SE-0252](0252-keypath-dynamic-member-lookup.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active review (March 26 - April 5, 2019)** +* Status: **Accepted** * Implementation: [PR #23436](https://github.com/apple/swift/pull/23436) ## Introduction From 0a9d712ea482d4f9cc8156ff0116ca28f8ac8fd1 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 3 Apr 2019 15:32:08 +0100 Subject: [PATCH 1101/4563] [SE-0248] Update status to implemented (Swift 5.1) --- proposals/0248-string-gaps-missing-apis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0248-string-gaps-missing-apis.md b/proposals/0248-string-gaps-missing-apis.md index 9e1426cd08..75b250aa83 100644 --- a/proposals/0248-string-gaps-missing-apis.md +++ b/proposals/0248-string-gaps-missing-apis.md @@ -3,7 +3,7 @@ * Proposal: [SE-0248](0248-string-gaps-missing-apis.md) * Author: [Michael Ilseman](https://github.com/milseman) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Accepted** +* Status: **Implemented (Swift 5.1)** * Implementation: [apple/swift#22869](https://github.com/apple/swift/pull/22869) * Bugs: [SR-9955](https://bugs.swift.org/browse/SR-9955) From 7de4294a189c11a0805cd88091ae4e92739ae6fa Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 3 Apr 2019 18:18:48 +0100 Subject: [PATCH 1102/4563] [SE-0242] Update status to implemented (Swift 5.1) --- proposals/0242-default-values-memberwise.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0242-default-values-memberwise.md b/proposals/0242-default-values-memberwise.md index 252b1877c2..4b6522f683 100644 --- a/proposals/0242-default-values-memberwise.md +++ b/proposals/0242-default-values-memberwise.md @@ -3,7 +3,7 @@ * Proposal: [SE-0242](0242-default-values-memberwise.md) * Author: [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Accepted** +* Status: **Implemented (Swift 5.1)** * Decision Notes: [Rationale](https://forums.swift.org/t/se-0242-synthesize-default-values-for-the-memberwise-initializer/20618/98) * Implementation: [apple/swift#19743](https://github.com/apple/swift/pull/19743) @@ -129,4 +129,4 @@ As the memberwise initializer is only synthesized as an internal initializer, th ## Alternatives considered -We could simply not do this and save this proposal for a solution much larger in regards to fixing more problems the memberwise initializer has. The downside is that we put off obvious changes like this for much longer because of wanting to solve a bigger problem. I agree we should solve the bigger problems, but by solving problems like this it aids in the solution of the larger problem. \ No newline at end of file +We could simply not do this and save this proposal for a solution much larger in regards to fixing more problems the memberwise initializer has. The downside is that we put off obvious changes like this for much longer because of wanting to solve a bigger problem. I agree we should solve the bigger problems, but by solving problems like this it aids in the solution of the larger problem. From 228f58cb04db37836caebae4f2553c096c88c892 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Thu, 4 Apr 2019 12:01:25 -0400 Subject: [PATCH 1103/4563] Update 0251 (simd) to reflect what was discussed in core team. (#1017) * Update 0251 (simd) to reflect what was discussed in core team. Specifically, make permuting indexes wrap on vector length (even for SIMD3), and make min/max free functions instead of statics. --- proposals/0251-simd-additions.md | 62 +++++++++++++------------------- 1 file changed, 25 insertions(+), 37 deletions(-) diff --git a/proposals/0251-simd-additions.md b/proposals/0251-simd-additions.md index 36efa6ab9f..39f4f6890a 100644 --- a/proposals/0251-simd-additions.md +++ b/proposals/0251-simd-additions.md @@ -8,9 +8,9 @@ ## Introduction -A few teams within Apple have requested additions to the new SIMD types -and protocols to better support their use cases. In addition, there are some features -we punted out of the original review because we were up against a hard time +Early adopters of SIMD types and protocols have encountered a few missing things as +they've started to write more code that uses them. In addition, there are some +features we punted out of the original review because we were up against a hard time deadline to which we would like to give further consideration. This is a bit of a grab bag of SIMD features, so I'm deviating from the usual proposal @@ -120,7 +120,7 @@ heavily used. For Swift, we want to restrict the feature somewhat, but also make it more powerful. In shader languages and clang extensions, you can even use swizzled vectors as *lvalues*, so long as the same element does not appear twice. I proposed to define general -permutes[†] as get-only subscripts. By restricting them from appearing as setters, we +permutes as get-only subscripts. By restricting them from appearing as setters, we gain the flexibility to not require they be compile-time constants: ```swift extension SIMD { @@ -134,12 +134,11 @@ extension SIMD { /// /// Because of this, the index is always in-range and no trap /// can occur. - public subscript(masking: SIMD2) -> SIMD2 + public subscript(index: SIMD2) -> SIMD2 where Index: FixedWidthInteger { var result = SIMD2() - var masked = masking & Index(scalarCount - 1) for i in result.indices { - result[i] = self[Int(masked[i])] + result[i] = self[Int(index[i]) % scalarCount] } return result } @@ -149,14 +148,6 @@ let v = SIMD4(1,2,3,4) let xyz = SIMD3(2,1,0) let w = v[xyz] // SIMD3(3,2,1) ``` -There's one special case, which is when the vector being subscripted has three elements. -In this case, we can't just mask the index to log2(scalarCount) bits. Instead, we extend -the vector being subscripted to a four-element vector (x, y, z, z) and mask to two bits. -This is expected to be an exceptionally rare case--power-of-two dictionaries are the -norm when dynamic indices are used; the principle concern is merely that we define the -result to be *something understandable and easily computable* rather than leaving it -undefined. - ### Alternatives Considered 1. We might want an explicit label on this subscript, but as with the extending inits, I believe that its use is generally clear enough in context. @@ -190,10 +181,10 @@ for computational geometry. ```swift extension SIMD where Scalar: Comparable { /// The least element in the vector. - public var min: Scalar + public func min() -> Scalar /// The greatest element in the vector. - public var max: Scalar + public func max() -> Scalar } extension SIMD where Scalar: FixedWidthInteger { @@ -201,12 +192,12 @@ extension SIMD where Scalar: FixedWidthInteger { /// wrapping addition. /// /// Equivalent to indices.reduce(into: 0) { $0 &+= self[$1] }. - public var wrappedSum: Scalar + public func wrappedSum() -> Scalar } extension SIMD where Scalar: FloatingPoint { /// Returns the sum of the scalars in the vector. - public var sum: Scalar + public func sum() -> Scalar } ``` @@ -239,11 +230,11 @@ if any(x .< 0) { // handle negative x } `any` and `all` are free functions: ```swift public func any(_ mask: SIMDMask) -> Bool { - return mask._storage.min < 0 + return mask._storage.min() < 0 } public func all(_ mask: SIMDMask) -> Bool { - return mask._storage.max < 0 + return mask._storage.max() < 0 } ``` @@ -287,18 +278,6 @@ We're also missing `clamp` to restrict values to a specified range. ### Detailed design ```swift extension SIMD where Scalar: Comparable { - /// The lanewise minimum of two vectors. - /// - /// Each element of the result is the minimum of the corresponding - /// elements of the inputs. - public static func min(_ lhs: Self, _ rhs: Self) -> Self - - /// The lanewise maximum of two vectors. - /// - /// Each element of the result is the maximum of the corresponding - /// elements of the inputs. - public static func max(_ lhs: Self, _ rhs: Self) -> Self - /// Replace any values less than lowerBound with lowerBound, and any /// values greater than upperBound with upperBound. /// @@ -316,13 +295,22 @@ extension SIMD where Scalar: Comparable { return Self.min(upperBound, Self.max(lowerBound, self)) } } + +/// The lanewise minimum of two vectors. +/// +/// Each element of the result is the minimum of the corresponding +/// elements of the inputs. +public func min(_ lhs: V, _ rhs: V) -> V where V: SIMD, V.Scalar: Comparable + +/// The lanewise maximum of two vectors. +/// +/// Each element of the result is the maximum of the corresponding +/// elements of the inputs. +public func max(_ lhs: V, _ rhs: V) -> V where V: SIMD, V.Scalar: Comparable ``` ### Alternatives Considered -These could be free functions, but that introduces an ambiguity if someone retroactively -conforms SIMD types to Comparable because they want lexicographic ordering. - -They could be spelled out `lanewiseMaximum` or similar, to clarify that they operate +These could be spelled out `lanewiseMaximum` or similar, to clarify that they operate lanewise (Chris suggested this in the pitch thread), but we don't spell out `+` as "lanewise-plus", so it seems weird to do it here. The default assumption is that SIMD operations are lanewise. From 66e3ea345fb31ba944b242d65406a76c753130be Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 4 Apr 2019 23:23:49 +0100 Subject: [PATCH 1104/4563] [SE-0247] Update status to implemented (Swift 5.1) --- proposals/0247-contiguous-strings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0247-contiguous-strings.md b/proposals/0247-contiguous-strings.md index b7aa80c3f8..69527d9163 100644 --- a/proposals/0247-contiguous-strings.md +++ b/proposals/0247-contiguous-strings.md @@ -3,7 +3,7 @@ * Proposal: [SE-0247](0247-contiguous-strings.md) * Author: [Michael Ilseman](https://github.com/milseman) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 5.1)** * Implementation: [apple/swift#23051](https://github.com/apple/swift/pull/23051) * Bugs: [SR-6475](https://bugs.swift.org/browse/SR-6475) From a2b796ecb2fac046e74519d2c67288b0cadaabd2 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Fri, 5 Apr 2019 20:54:27 -0400 Subject: [PATCH 1105/4563] Update 0251-simd-additions.md --- proposals/0251-simd-additions.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/proposals/0251-simd-additions.md b/proposals/0251-simd-additions.md index 39f4f6890a..06e1ba733c 100644 --- a/proposals/0251-simd-additions.md +++ b/proposals/0251-simd-additions.md @@ -125,15 +125,10 @@ gain the flexibility to not require they be compile-time constants: ```swift extension SIMD { /// Extracts the scalars at specified indices to form a SIMD2. - /// - /// The low-order log2(scalarCount) bits of each element of the - /// `masking` index vector are used to index self. E.g.: - /// - /// let x = SIMD4(0, 1, 2, 3) - /// let y = x[SIMD2(5,2)] // (1, 2) - /// - /// Because of this, the index is always in-range and no trap - /// can occur. + /// + /// The elements of the index vector are wrapped modulo the count of elements + /// in this vector. Because of this, the index is always in-range and no trap + /// can occur. public subscript(index: SIMD2) -> SIMD2 where Index: FixedWidthInteger { var result = SIMD2() From 47d70b1d2b2223ee260d98537b483d6ec29d160f Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Sun, 7 Apr 2019 08:07:31 +0100 Subject: [PATCH 1106/4563] Update SE-0253: introducing callables. (#1019) - Update: direct references to `call` members are no longer supported. - Add "direct references" section under "alternatives considered". - Expand on function-type conversion future direction. - Start filling in ABI stability/resilience sections. --- proposals/0253-callable.md | 67 ++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/proposals/0253-callable.md b/proposals/0253-callable.md index 4b897c1f53..ad4e20e5de 100644 --- a/proposals/0253-callable.md +++ b/proposals/0253-callable.md @@ -415,35 +415,30 @@ add1(1, 2, 3) A type can both have `call` members and be declared with `@dynamicCallable`. When type-checking a call expression, the type checker will first try to resolve the call to a function or initializer call, then a `call` member call, and finally a dynamic call. -### Direct reference to a `call` member +### Using `call` members -Like methods and initializers, a `call` member can be directly referenced, either through the base name and the contextual type, or through the full name. +Currently, `call` members cannot be directly referenced; create a closure to +call them instead. ```swift let add1 = Adder(base: 1) -let f: (Int) -> Int = add1.call +let f: (Int) -> Int = { x in add1(x) } f(2) // => 3 -[1, 2, 3].map(add1.call) // => [2, 3, 4] +[1, 2, 3].map { x in add1(x) } // => [2, 3, 4] ``` -When a type has both an instance method named "call" and a `call` member with the exact same type signature, a redeclaration error is produced. +`call` members are represented as instance methods with a special-case name and +not as instance methods with the actual name "call". + +Thus, no redeclaration error is produced for types that define an instance method named "call" and a `call` member with the exact same type signature. ```swift struct S { - func call() {} - call() {} + func call() {} // ok + call() {} // ok } ``` -```console -test.swift:3:5: error: invalid redeclaration of 'call()' - call() {} - ^ -test.swift:2:10: note: 'call()' previously declared here - func call() {} - ^ -``` - When a type does not have a `call` member but has an instance method or an instance property named "call", a direct reference to `call` gets resolved to that member. ```swift @@ -459,7 +454,16 @@ A value cannot be implicitly converted to a function when the destination functi let h: (Int) -> Int = add1 // error: cannot convert value of type `Adder` to expected type `(Int) -> Int` ``` -Implicit conversions are generally problematic in Swift, and as such we would like to get some experience with this base proposal before considering adding such capability. +Implicit conversions impact the entire type system and require runtime support +to work with dynamic casts; thus, further exploration is necessary for a formal +proposal. This base proposal is self-contained; incremental proposals involving +conversion can come later. + +A less controversial future direction may be to support explicit conversion via `as`: + +```swift +let h = add1 as (Int) -> Int +``` ## Source compatibility @@ -504,11 +508,13 @@ call {} ## Effect on ABI stability -This proposal is about a syntactic sugar and has no ABI breaking changes. +Adding a new `call` declaration is an additive change to the ABI. + +`call` declarations will not be supported when deploying to a Swift 5.0 runtime. ## Effect on API resilience -This proposal is about a syntactic sugar and has no API breaking changes. +`call` declarations will not be supported when deploying to a Swift 5.0 runtime. ## Alternatives considered @@ -592,7 +598,8 @@ We feel this approach is not ideal because: attribute means nothing by itself. There's an unforunate edge case that must be explicitly handled: if a `@staticCallable` type defines no call-syntax delegate methods, an error must be produced. -* The name for call-syntax delegate methods (e.g. `func call` ) is not first-class in the language, while their call site syntax is. +* The name for call-syntax delegate methods (e.g. `func call` ) is not + first-class in the language, while their call site syntax is. #### Use a `Callable` protocol to represent callable types @@ -607,7 +614,7 @@ struct Adder: Callable { } ``` -We feel this approach is not ideal for the same reasons as the marker type attribute. A marker protocol by itself is not meaningful and the name for call-syntax delegate methods is informal. Additionally, protocols should represent particular semantics, but "callable" behavior has no inherent semantics. +We feel this approach is not ideal for the same reasons as the marker type attribute. A marker protocol by itself is not meaningful and the name for call-syntax delegate methods is informal. Additionally, protocols should represent particular semantics, but call-*syntax* behavior has no inherent semantics. In comparison, `call` declarations have a formal representation in the language and exactly indicate callable behavior (unlike a marker attribute or protocol). @@ -669,6 +676,24 @@ Adder.self(base: 3) // okay But since this would be an additive feature on top of this proposal and that `subscript` cannot be `static` yet, we'd like to defer this feature to future discussions. +### Direct references to `call` members + +Direct references to `call` members are intentionally not supported. An earlier +version of the proposal included support for direct references to `call` members +via `foo.call` (like referring to an instance method with the name "call"). + +However, the direction for "callable values" is not to support direct `call` +member references, but to support conversions of callable values to +function-typed values. Supporting explicit conversion via `as` seems relatively +non-controversial in forum discussions. Implicit conversion was a highly +requested feature, but it likely has type-system-wide impact and requires more +exploration. + +`call` members should be thought of and represented as "instance methods with a +special-case name", not "instance methods with the name 'call'". For now, +without support for conversion to function-typed values, create thunks like `{ +foo(...) }` instead. + ### Unify callable functionality with `@dynamicCallable` Both `@dynamicCallable` and the proposed `call` members involve syntactic sugar related to function applications. However, the rules of the sugar are different, making unification difficult. In particular, `@dynamicCallable` provides a special sugar for argument labels that is crucial for usability. From b49de728bda5083a5601d9138022713e1d547a7a Mon Sep 17 00:00:00 2001 From: John McCall Date: Sun, 7 Apr 2019 23:06:38 -0400 Subject: [PATCH 1107/4563] Accept SE-0251 with modifications already applied --- proposals/0251-simd-additions.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proposals/0251-simd-additions.md b/proposals/0251-simd-additions.md index 06e1ba733c..0d5fefcdbb 100644 --- a/proposals/0251-simd-additions.md +++ b/proposals/0251-simd-additions.md @@ -3,8 +3,11 @@ * Proposal: [SE-0251](0251-simd-additions.md) * Author: [Stephen Canon](https://github.com/stephentyrone) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (March 22nd...April 1st, 2019)** +* Status: **Accepted with Modifications** * Implementation: [apple/swift#23421](https://github.com/apple/swift/pull/23421) +* Review: ([review](https://forums.swift.org/t/se-0251-simd-additions/21957)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0251-simd-additions/22801)) + + ## Introduction From f7170eefdbbed3941a780e9c508bda444762481b Mon Sep 17 00:00:00 2001 From: Dan Zheng Date: Mon, 8 Apr 2019 10:00:06 +0200 Subject: [PATCH 1108/4563] Update SE-0253: introducing callables. (#1021) * Update SE-0253: introducing callables. - Use "closure" instead of "thunk". - Remove blurb about direct member references. * More updates. - Remove more blurbs about direct member references. - Minor stylistic changes. --- proposals/0253-callable.md | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/proposals/0253-callable.md b/proposals/0253-callable.md index ad4e20e5de..46abddd301 100644 --- a/proposals/0253-callable.md +++ b/proposals/0253-callable.md @@ -8,8 +8,6 @@ ## Introduction -Note: throughout this document, let "call-syntax" refer to the syntax of applying a function to arguments: `f(x, y, z)`. - This proposal introduces [callables](https://en.wikipedia.org/wiki/Callable_object) to Swift. Callables are values that define function-like behavior and can be applied using function application syntax. In a nutshell, we propose to introduce a new declaration syntax with the keyword `call`: @@ -277,7 +275,6 @@ But it is more similar to a `func` declaration in that: * It does not allow `get` and `set` declarations inside the body. * When a parameter has a name, the name is treated as the argument label. -* It can be referenced directly by name, e.g. `foo.call`. The rest of the `call` declaration grammar and semantics is identical to that of function declarations – it supports the same syntax for: @@ -348,9 +345,7 @@ The following attributes are supported on `FuncDecl`, and are also are supported ### Notable unsupported modifiers/attributes on `call` declarations * `@warn_unqualified_access` - * It would be weird to require qualified access (e.g. `self.call` in another - member) for `call` members, given their purpose as a call-syntax delegate - method. + * Qualified access is not possible because direct references to `call` declarations is not supported.

    @@ -439,7 +434,7 @@ struct S { } ``` -When a type does not have a `call` member but has an instance method or an instance property named "call", a direct reference to `call` gets resolved to that member. +When a type does not have `call` members, but has instance methods or an instance properties named "call", direct references to `call` are resolved via existing overload resolution logic. There's nothing new here. ```swift struct S { @@ -459,7 +454,7 @@ to work with dynamic casts; thus, further exploration is necessary for a formal proposal. This base proposal is self-contained; incremental proposals involving conversion can come later. -A less controversial future direction may be to support explicit conversion via `as`: +A less controversial future direction is to support explicit conversion via `as`: ```swift let h = add1 as (Int) -> Int @@ -572,8 +567,6 @@ struct Adder { This approach represents call-syntax delegate methods as `func` declarations with a special name instead of creating a new `call` declaration kind. However, such `func` declarations do not convey "call-syntax delegate method" as clearly as the `call` keyword. -Also, we want to support direct references to call-syntax delegate methods via `foo.call`. This makes more sense when call-syntax delegate methods are declared with the `call` keyword, and is consistent with `init` declarations and direct references (e.g. `foo.init`). - #### Use a type attribute to mark types with call-syntax delegate methods ```swift @@ -691,8 +684,8 @@ exploration. `call` members should be thought of and represented as "instance methods with a special-case name", not "instance methods with the name 'call'". For now, -without support for conversion to function-typed values, create thunks like `{ -foo(...) }` instead. +without support for conversion to function-typed values, create closures like +`{ foo(...) }` instead. ### Unify callable functionality with `@dynamicCallable` From af0a9fb0542c2e1b9f3f5375d425ac8f12e812ae Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Tue, 9 Apr 2019 05:47:49 +0300 Subject: [PATCH 1109/4563] Update 0000-where-on-contextually-generic.md --- proposals/0000-where-on-contextually-generic.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index 3eea5dec17..2e0ed409cb 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -9,7 +9,7 @@ ## Introduction The objective of this proposal is to lift the restriction on attaching `where` clauses to declarations that themselves -do not carry a generic parameter list explicitly, but inherit the surrounding generic environment. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error within generic contexts. +do not introduce type variables explicitly, but inherit the surrounding generic environment. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error within generic contexts. ```swift struct Box { @@ -26,7 +26,7 @@ struct Box { > // error: Instance method requirement 'foo(arg:)' cannot add constraint 'Self: Equatable' on 'Self' > func foo() where Self: Equatable > } -> +> ``` Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/where-clauses-on-contextually-generic-declaractions/22449) @@ -40,6 +40,7 @@ and the compiler. Leaving ergonomic shortcomings behind, it is only natural for a `where` clause to work anywhere a constraint can be meaningfully imposed, meaning both of these layout variants should be possible: + ```swift struct Foo // 'Foo' can be any kind of nominal type declaration. For a protocol, 'T' would be an associatedtype. @@ -62,7 +63,7 @@ extension Foo where T: Sequence, T.Element: Equatable { } ``` A move towards «untying» generic parameter lists and `where` clauses is an obvious and farsighted improvement to the generics -system with numerous future applications, including contextually generic computed properties, [opaque types](https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md), [generalized +system with numerous future applications, including [opaque types](https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md), [generalized existentials](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials) and constrained protocol requirements. ## Source compatibility and ABI stability From 06ab8a01ce030a579f6bb88803a42fd35d514b9c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 9 Apr 2019 00:52:34 -0700 Subject: [PATCH 1110/4563] Revise property delegates proposal to use custom attributes --- proposals/NNNN-property-delegates.md | 374 ++++++++++++++++++++------- 1 file changed, 280 insertions(+), 94 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 3af35ec628..aac6f3d232 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: **TBD** * Status: **Awaiting review** -* Implementation: [PR #23440](https://github.com/apple/swift/pull/23440) +* Implementation: [PR #23701](https://github.com/apple/swift/pull/23701) ## Introduction @@ -13,7 +13,7 @@ Rather than hardcode a fixed set of patterns into the compiler, we should provide a general "property delegate" mechanism to allow these patterns to be defined as libraries. -This is an alternative approach to some of the problems intended to be addressed by the [2015-2016 property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md). Some of the examples are the same, but this proposal takes a completely different approach designed to be simpler, easier to understand for users, and less invasive in the compiler implementation. There is a section that discusses the substantive differences from that design at the end of this proposal. +This is an alternative approach to some of the problems intended to be addressed by the [2015-2016 property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md). Some of the examples are the same, but this proposal takes a completely different approach designed to be simpler, easier to understand for users, and less invasive in the compiler implementation. There is a section that discusses the substantive differences from that design near the end of this proposal. [Pitch](https://forums.swift.org/t/pitch-property-delegates/21895)
    @@ -87,13 +87,11 @@ class Foo { ## Proposed solution We propose the introduction of **property delegates**, which allow a -`var` declaration to state which **delegate** is used to implement -it. Borrowing from [Kotlin's delegated -properties](https://kotlinlang.org/docs/reference/delegated-properties.html), -we propose the `by` keyword to indicate the delegate: +property declaration to state which **delegate** is used to implement +it. The delegate is described via an attribute: ```swift -var foo by Lazy = 1738 +@Lazy var foo = 1738 ``` This implements the property `foo` in a way described by the *property delegate type* for `Lazy`: @@ -127,13 +125,14 @@ enum Lazy { ``` A property delegate type provides the storage for a property that -names it after `by`. The `value` property provides the actual +uses it as a delegate. The `value` property of the delegate type +provides the actual implementation of the delegate, while the (optional) `init(initialValue:)` enables initialization of the storage from a value of the property's type. The property declaration ```swift -var foo by Lazy = 1738 +@Lazy var foo = 1738 ``` translates to: @@ -163,15 +162,6 @@ extension Lazy { $foo.reset(42) ``` -Like other declarations, the synthesized storage property will be -`internal` by default, or the access level of the original property if it is less than `internal`. However, it can be given more lenient access by putting the -access level after `by`, e.g., - -```swift -// both foo and $foo are publicly visible -public var foo: Int by public Lazy = 1738 -``` - The property delegate instance can be initialized directly by providing the initializer arguments in parentheses after the name. This could be used, for example, when a particular property delegate requires more setup to provide access to a value (example courtesy of Harlan Haskins): ```swift @@ -191,8 +181,11 @@ struct UserDefault { } enum GlobalSettings { - static var isFooFeatureEnabled: Bool by UserDefault(key: "FOO_FEATURE_ENABLED", defaultValue: false) - static var isBarFeatureEnabled: Bool by UserDefault(key: "BAR_FEATURE_ENABLED", defaultValue: false) + @UserDefault(key: "FOO_FEATURE_ENABLED", defaultValue: false) + static var isFooFeatureEnabled: Bool + + @UserDefault(key: "BAR_FEATURE_ENABLED", defaultValue: false) + static var isBarFeatureEnabled: Bool } ``` @@ -268,7 +261,7 @@ This enables multi-phase initialization, like this: ```swift class Foo { - var x: Int by DelayedImmutable + @DelayedImmutable var x: Int init() { // We don't know "x" yet, and we don't have to set it @@ -293,7 +286,7 @@ on new objects when the property is set. We can turn this into a delegate: ```swift @propertyDelegate -stuct Copying { +struct Copying { private var _value: Value init(initialValue value: Value) { @@ -314,10 +307,108 @@ stuct Copying { This implementation would address the problem detailed in [SE-0153](https://github.com/apple/swift-evolution/blob/master/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md). Leaving the `copy()` out of `init(initialValue:)` implements the pre-SE-0153 semantics. -### `Unsafe(Mutable)Pointer` +### `Atomic` + +Support for atomic operations (load, store, increment/decementer, compare-and-exchange) is a commonly-requested Swift feature. While the implementation details for such a feature would involve compiler and standard library magic, the interface itself can be nicely expressed as a property delegate type: + + +```swift +@propertyDelegate +struct Atomic { + private var _value: Value + + init(initialValue: Value) { + self._value = initialValue + } + + var value: Value { + get { return load() } + set { store(newValue: newValue) } + } + + func load(order: MemoryOrder = .relaxed) { ... } + mutating func store(newValue: Value, order: MemoryOrder = .relaxed) { ... } + mutating func increment() { ... } + mutating func decrement() { ... } +} + +extension Atomic where Value: Equatable { + mutating func compareAndExchange(oldValue: Value, newValue: Value, order: MemoryOrder = .relaxed) -> Bool { + ... + } +} + +enum MemoryOrder { + case relaxed, consume, acquire, release, acquireRelease, sequentiallyConsistent +}; +``` + +Here are some simple uses of `Atomic`. With atomic types, it's fairly common +to weave lower-level atomic operations (`increment`, `load`, `compareAndExchange`) where we need specific semantics (such as memory ordering) with simple queries, so both the property and the synthesized storage property are used often: + +```swift +@Atomic var counter: Int + +if thingHappened { + $counter.increment() +} +print(counter) + +@Atomic var initializedOnce: Int? +if initializedOnce == nil { + let newValue: Int = /*computeNewValue*/ + if !$initializedOnce.compareAndExchange(oldValue: nil, newValue: newValue) { + // okay, someone else initialized it. clean up if needed + } +} +print(initializedOnce) +``` + +### `CopyOnWrite` -The `Unsafe(Mutable)Pointer` types could be augmented to be property delegate types, allowing one to access the referenced value directly using the `by` syntax. For example: +With some work, property delegates can provide copy-on-write wrappers (example courtesy of Brent Royal-Gordon): +```swift +protocol Copyable: AnyObject { + func copy() -> Self +} + +@propertyDelegate +struct CopyOnWrite { + init(initialValue: Value) { + value = initialValue + } + + private(set) var value: Value + + var unique: Value { + mutating get { + if !isKnownUniquelyReferenced(&value) { + value = value.copy() + } + return value + } + set { + value = newValue + } + } +} +``` + +The usage of the property delegate above is as follows: + +```swift +@CopyOnWrite var storage: MyStorageBuffer + +// Non-modfying access: +let index = storage.index(of: …) + +// For modification, use the `unique` property of the underlying storage:$storage.unique.append(…) +``` + +### Property delegate types in the wild + +There are a number of existing types that already provide the basic structure of a property delegate type. One fun case is `Unsafe(Mutable)Pointer`, which we could augment to allow easy access to the pointed-to value: ```swift @propertyDelegate @@ -326,12 +417,33 @@ struct UnsafeMutablePointer { var value: Pointee { get { return pointee } - set { pointee = newValue + set { pointee = newValue } } } +``` + +From a user perspective, this allows us to set up the unsafe mutable pointer's address once, then mostly refer to the pointed-to value: -var someInt: Int by UnsafeMutablePointer(mutating: addressOfAnInt) -someInt = 17 // equivalent to someInt.value = 17 +``` +@UnsafeMutablePointer(mutating: addressOfAnInt) +var someInt: Int + +someInt = 17 // equivalent to someInt.pointee = 17 +print(someInt) + +$someInt.deallocate() +``` + +RxSwift's [`BehaviorRelay`](https://github.com/ReactiveX/RxSwift/blob/master/RxCocoa/Traits/BehaviorRelay.swift) replays the most recent value provided to it for each of the subscribed observers. It is created with an initial value, has `value` property to access the current value, as well as API to `subscribe` a new observer: (Thanks to Adrian Zubarev for pointing this out) + +```swift +@BehaviorRelay +var myValue: Int = 17 + +let observer = $myValue.subscribe(...) // subscribe an observer +$myValue.accept(42) // set a new value via the synthesized storage property + +print(myValue) // print the most recent value ``` ## Detailed design @@ -339,18 +451,15 @@ someInt = 17 // equivalent to someInt.value = 17 ### Property delegate types A *property delegate type* is a type that can be used as a property -delegate. There are three basic requirements for a property delegate +delegate. There are two basic requirements for a property delegate type: 1. The property delegate type must be defined with the attribute `@propertyDelegate`. The attribute indicates that the type is meant to be used as a property delegate type, and provides a point at which the -compiler can verify the other consistency rules. -2. The property delegate type must be generic, with a single generic -type parameter. The type parameter will filled in with the type of the -variable that uses the property delegate type. -3. The property delegate type must have a property named `value` whose -type is that of the single generic type parameter. This is the +compiler can verify any other consistency rules. +2. The property delegate type must have a property named `value`, whose +access level is the same as that of the type itself. This is the property used by the compiler to access the underlying value on the delegate instance. @@ -358,19 +467,20 @@ delegate instance. Introducing a property delegate to a property makes that property computed (with a getter/setter) and introduces a stored property whose -type uses the delegate type. That stored property can be initialized +type is the delegate type. That stored property can be initialized in one of two ways: -1. Via a value of the original property's type (e.g., `Int` in `var - foo: Int by Lazy`, using the the property delegate type's +1. Via a value of the original property's type (e.g., `Int` in `@Lazy var + foo: Int`, using the the property delegate type's `init(initialValue:)` initializer. That initializer must have a single - parameter of the property delegate type's generic type parameter (or - be an `@autoclosure` thereof). When `init(initialValue:)` is present, + parameter of the same type as the `value` property (or + be an `@autoclosure` thereof) and have the same access level as the + property delegate type itself. When `init(initialValue:)` is present, is is always used for the initial value provided on the property declaration. For example: ```swift - var foo by Lazy = 17 + @Lazy var foo = 17 // ... implemented as var $foo: Lazy = Lazy(initialValue: 17) @@ -378,11 +488,14 @@ in one of two ways: ``` -2. Via a value of the property delegate type, by placing the call arguments after the property delegate type: +2. Via a value of the property delegate type, by placing the initializer + arguments after the property delegate type: ```swift var addressOfInt: UnsafePointer = ... - var someInt: Int by UnsafeMutablePointer(mutating: addressOfInt) + + @UnsafeMutablePointer(mutating: addressOfInt) + var someInt: Int // ... implemented as var $someInt: UnsafeMutablePointer = UnsafeMutablePointer(mutating: addressOfInt) @@ -397,46 +510,96 @@ type, using the initialization of the synthesized stored property. For example: ```swift -var foo by Lazy = 17 +@Lazy var foo = 17 // type inference as in... var $foo: Lazy = Lazy(initialValue: 17) // infers the type of 'foo' to be 'Int' ``` -The same applies when directly initialize the property delegate type, e.g., +The same applies when directly initializing the delegate instance, e.g., ```swift -var someInt by UnsafeMutablePointer(mutating: addressOfInt) +@UnsafeMutablePointer(mutating: addressOfInt) +var someInt // type inference as in... var $someInt: UnsafeMutablePointer = UnsafeMutablePointer.init(mutating: addressOfInt) // infers the type of 'someInt' to be 'Int' ``` -### Using delegates in property declarations +The type of the `value` property of the property delegate type must coincide with that of the original property using that delegate type. Some examples: -A property declaration can specify its delegate following the `by` -keyword: +```swift +@Lazy var foo: Int // okay +@Lazy var bar: Double // error: Lazy.value is of type Int, not Double +``` -```text -pattern-initializer ::= pattern property-delegate[opt] initializer[opt] +If there is no initializer for the delegate instance, and the property delegate type takes a single generic parameter, the corresponding generic argument can be omitted: -property-delegate ::= 'by' access-level-modifier[opt] type property-delegate-init[opt] +```swift +@Lazy var foo: Int // okay: equivalent to @Lazy var foo: Int +``` + +### Custom Attributes + +Property delegates are a form of custom attribute, where the attribute syntax +is used to refer to entities declared in Swift. Grammatically, the use of property delegates is described as follows: + +``` +attribute ::= '@' type-identifier expr-paren? +``` -property-delegate-init ::= parenthesized-expression - ::= tuple-expression +The *type-identifier* must refer to a property delegate type, which can include generic arguments. Note that this allows for qualification of the attribute names, e.g., + +```swift +@Swift.Lazy var foo = 1742 ``` -The *type* in a *property-delegate* must refer to a 'property delegate -type'_ without specifying a generic argument. The -*access-level-modifier* can be any of `private`, `fileprivate`, -`internal`, or `public`, but cannot be less restrictive than the -property declaration itself. +The *expr-paren*, if present, provides the initialization arguments for the delegate instance. + +This formulation of custom attributes fits in with a [larger proposal for custom attributes](https://github.com/hartbit/swift-evolution/blob/static-custom-attributes/proposals/XXXX-static-custom-attributes.md), which uses the same custom attribute syntax as the above but allows for other ways in which one can define a type to be used as an attribute. In this scheme, `@propertyDelegate` is just one kind of custom attribute: there will be other kinds of custom attributes that are available only at compile time (e.g., for tools) or runtime (via some reflection capability). ### Mutability of properties with delegates -A property with a delegate must be introduced with the `var` keyword. -If the `value` property of the behavior type lacks a setter (or the setter is inaccessible), `value` will not have a setter. However, the -synthesized storage property could still be mutated. +Generally, a property that has a property delegate will have both a getter and a setter. However, there are several reasons for which the setter may be missing: + +* The `value` property of the property delegate type lacks a setter, or its setter is inaccessible +* The property is declared as a `let` + +The synthesized getter will be `mutating` if the property delegate type's `value` property is `mutating` and the property is part of a `struct`. Similarly, the synthesized setter will be `nonmutating` if either the property delegate type's `value` property has a `nonmutating` setter or the property delegate type is a `class`. For example: + +```swift +struct MutatingGetterDelegate { + var value: Value { + mutating get { ... } + set { ... } + } +} + +struct NonmutatingSetterDelegate { + var value: Value { + get { ... } + nonmutating set { ... } + } +} + +class ReferenceDelegate { + var value: Value +} + +struct UseDelegates { + // x's getter is mutating + // x's setter is mutating + @MutatingGetterDelegate var x: Int + + // y's getter is nonmutating + // y's setter is nonmutating + @NonmutatingSetterDelegate var y: Int + + // z's getter is nonmutating + // z's setter is nonmutating + @ReferenceDelegate var z: Int +} +``` ### Out-of-line initialization of properties with delegates @@ -447,7 +610,7 @@ example: ```swift -let x: Int by Lazy +@Lazy let x: Int // ... x = 17 // okay, treated as $x = .init(initialValue: 17) ``` @@ -456,7 +619,7 @@ The synthesized storage property can also be initialized directly, e.g., ```swift -var y: Int by UnsafeMutable +@UnsafeMutable var y: Int // ... $y = UnsafeMutable(pointer: addressOfInt) // okay ``` @@ -467,7 +630,7 @@ apply to properties that have delegates. Let's expand the example of `x` above to include a re-assignment and use `var`: ```swift -var x2: Int by Lazy +@Lazy var x2: Int // ... x2 = 17 // okay, treated as $x2 = .init(initialValue: 17) // ... @@ -476,17 +639,7 @@ x2 = 42 // okay, treated as x2 = 42 (calls the Lazy.value setter) ### Access to the storage property -By default, the synthesized storage property will have `internal` access or the access of the original property, whichever is more restrictive. An alternative access can be provided for the synthesized storage property by specifying the access level after `by`, e.g., - -```swift -// both foo and $foo are publicly visible -public var foo: Int by public Lazy = 1738 - -// bar is publicly visible, $bar is privately visible -public var bar: Int by private Lazy = 1738 -``` - -The specified access level cannot be greater than `public` and cannot be greater than the original property. +By default, the synthesized storage property will have `internal` access or the access of the original property, whichever is more restrictive. ### Memberwise initializers @@ -503,22 +656,17 @@ initializer for a property with a delegate is determined as follows: * If the delegate type contains an `init(initialValue:)`, the parameter type is the original type of the property. * When the delegate type does not contain an `init(initialValue:)`, - the parameter type is the type of the synthesized storage property - (i.e., a specialization of the delegate type). In this case, the - access level of the implicit initializer may need to be adjusted to - account for a visibility of the delegate: if the delegate is private - (e.g., `var x: Int by private UnsafeMutablePointer`), then the implicit memberwise - initializer will be `private`. + the parameter type is the type of the synthesized storage property. For example: ```swift struct Foo { - var x: Int by fileprivate UnsafeMutable - var y: Int by Lazy = 17 + @UnsafeMutable var x: Int + @Lazy var y: Int = 17 // implicit memberwise initializer: - fileprivate init(x: UnsafeMutable, y: Int = 17) { + init(x: UnsafeMutable, y: Int = 17) { self.$x = x self.$y = .init(initialValue: y) } @@ -547,7 +695,7 @@ let $y = 17 // error: cannot declare entity with $-prefixed name '$y' A property with a delegate can declare accessors explicitly (`get`, `set`, `didSet`, `willSet`). If either `get` or `set` is missing, it will be implicitly created by accessing the storage property (e.g., `$foo`). For example: ```swift -var x by Lazy = 17 { +@Lazy var x = 17 { set { $x.value = newValue * 2 } @@ -557,7 +705,7 @@ var x by Lazy = 17 { } -var y by Lazy = 42 { +@Lazy var y = 42 { didSet { print("Overrode lazy value with \(oldValue)") } @@ -581,7 +729,7 @@ There are a number of restrictions on the use of property delegates when definin * A property with a delegate that is declared within a class must be `final` and cannot override another property. * A property with a delegate cannot be `lazy`, `@NSCopying`, `@NSManaged`, `weak`, or `unowned`. -* A property with a delegate must be the only property declared within its enclosing declaration (e.g., `var (x, y) by Lazy = /* ... */` is ill-formed). +* A property with a delegate must be the only property declared within its enclosing declaration (e.g., `@Lazy var (x, y) = /* ... */` is ill-formed). * The `value` property and (if present) `init(initialValue:)` of a property delegate type shall have the same access as the property delegate type. ## Impact on existing code @@ -632,6 +780,25 @@ protocol, we don't know of any useful generic algorithms or data structures that seem to be implemented in terms of only `PropertyDelegate`. + +### Kotlin-like `by` syntax + +A previous iteration of this proposal (and its [implementation](https://github.com/apple/swift/pull/23440)) used `by` syntax similar to that of [Kotlin's delegated +properties](https://kotlinlang.org/docs/reference/delegated-properties.html), where the `by` followed the variable declaration. For example: + +```swift +var foo by Lazy = 1738 + +static var isFooFeatureEnabled: Bool by UserDefault(key: "FOO_FEATURE_ENABLED", defaultValue: false) +``` + +There are some small advantages to this syntax over the attribute formulation: + +* For cases like `UserDefault` where the delegate instance is initialized directly, the initialization happens after the original variable declaration, which reads better because the variable type and name come first, and how it's implemented come later. (Counter point: Swift developers are already accustomed to reading past long attributes, which are typically placed on the previous line) +* The `by DelegateType` formulation leaves syntactic space for add-on features like specifying the access level of the delegate instance (`by private DelegateType`) or delegating to an existing property (`by someInstanceProperty`). + +The main problem with `by` is its novelty: there isn't anything else in Swift quite like the `by` keyword above, and it is unlikely that the syntax would be re-used for any other feature. As a keyword, `by` is quite meaningless, and brainstorming during the [initial pitch](https://forums.swift.org/t/pitch-property-delegates/21895) didn't find any clearly good names for this functionality. + ### The 2015-2016 property behaviors design Property delegates address a similar set of use cases to *property behaviors*, which were [proposed and @@ -643,7 +810,7 @@ feature and narrow the feature set. Some substantive differences from the prior proposal are: * Behaviors were introduced into a property with the `[behavior]` - syntax, rather than the `by delegate` syntax described here. See the + syntax, rather than the attribute syntax described here. See the property behaviors proposal for more information. * Delegates are always expressed by a (generic) type. Property behaviors had a new kind of declaration (introduced by the @@ -665,6 +832,21 @@ the prior proposal are: ## Future Directions +### Specifying access for the storage property + +Like other declarations, the synthesized storage property will be +`internal` by default, or the access level of the original property if it is less than `internal`. However, we could provide separate access control for the storage property to make it more or less accessible using a syntax similar to that of `private(set)`: + +```swift +// both foo and $foo are publicly visible +@Lazy +public public(storage) var foo: Int = 1738 + +// bar is publicly visible, $bar is privately visible +@Atomic +public private(storage) var bar: Int = 1738 +``` + ### Referencing the enclosing 'self' in a delegate type Manually-written getters and setters for properties declared in a type often refer to the `self` of their enclosing type. For example, this can be used to notify clients of a change to a property's value: @@ -720,7 +902,7 @@ However, this means that one would have to manually call `register(_:)` in the i ```swift public class MyClass: Superclass { - public var myVar: Int by Observable = 17 + @Observable public var myVar: Int = 17 init() { // self.$myVar gets initialized with Observable(initialValue: 17) here @@ -760,7 +942,7 @@ The (generic) subscript gets access to the enclosing `self` type via its subscri ```swift public class MyClass: Superclass { - public var myVar: Int by Observable = 17 + @Observable public var myVar: Int = 17 // desugars to... internal var $myVar: Observable = Observable(initialValue: 17) @@ -781,7 +963,7 @@ The main challenge with this design is that it doesn't directly work when the en ```swift public struct MyStruct { - public var myVar: Int by Observable = 17 + @Observable public var myVar: Int = 17 // desugars to... internal var $myVar: Observable = Observable(initialValue: 17) @@ -798,11 +980,15 @@ So, while we feel that support for accessing the enclosing type's `self` is usef ### Delegating to an existing property -When specifying a delegate for a property, a backing storage property is implicitly created. However, it is possible that there already exists a property that can provide the backing. This allows greater control over the declaration of the backing property, e.g., to specify that it is `lazy` or `@objc` or to give it a more custom implementation: +When specifying a delegate for a property, the synthesized storage property is implicitly created. However, it is possible that there already exists a property that can provide the storage. One could provide a form of property delegation that creates the getter/setter to forward to an existing property, e.g.: ```swift lazy var fooBacking: SomeDelegate -var foo: Int by var fooBacking +@delegate(to: fooBacking) var foo: Int ``` -One could express this either by naming the property directly (as with the `by var` formulation above) or, for an even more general solution, by providing a keypath such as `\.someProperty.someOtherProperty`. +One could express this either by naming the property directly (as above) or, for an even more general solution, by providing a keypath such as `\.someProperty.someOtherProperty`. + +## Acknowledgments + +This proposal was greatly improved throughout its [first pitch](https://forums.swift.org/t/pitch-property-delegates/21895) by many people. Harlan Haskins, Brent Royal-Gordon, Adrian Zubarev, Jordan Rose and others provided great examples of uses of property delegates (several of which are in this proposal). Adrian Zubarev and Kenny Leung helped push on some of the core assumptions and restrictions of the original proposal, helping to make it more general. Vini Vendramini and David Hart helped tie this proposal together with custom attributes, which drastically reduced the syntactic surface area of this proposal. From 2d4c00c97cc331a14704ce5177b116cd115af7b1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 9 Apr 2019 01:36:32 -0700 Subject: [PATCH 1111/4563] Delegation for the storage property --- proposals/NNNN-property-delegates.md | 59 +++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index aac6f3d232..37c0b3dc37 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -366,7 +366,7 @@ print(initializedOnce) ### `CopyOnWrite` -With some work, property delegates can provide copy-on-write wrappers (example courtesy of Brent Royal-Gordon): +With some work, property delegates can provide copy-on-write wrappers (original example courtesy of Brent Royal-Gordon): ```swift protocol Copyable: AnyObject { @@ -381,7 +381,7 @@ struct CopyOnWrite { private(set) var value: Value - var unique: Value { + var storageValue: Value { mutating get { if !isKnownUniquelyReferenced(&value) { value = value.copy() @@ -395,15 +395,16 @@ struct CopyOnWrite { } ``` -The usage of the property delegate above is as follows: +`storageValue` provides delegation for the synthesized storage property, allowing the copy-on-write wrapper to be used directly: ```swift @CopyOnWrite var storage: MyStorageBuffer -// Non-modfying access: +// Non-modifying access: let index = storage.index(of: …) -// For modification, use the `unique` property of the underlying storage:$storage.unique.append(…) +// For modification, access $storage, which goes through `storageValue`: +$storage.append(…) ``` ### Property delegate types in the wild @@ -719,6 +720,54 @@ A property with a delegate can declare accessors explicitly (`get`, `set`, `didS } ``` +### Delegating access to the storage property + +A property delegate type delegate access to the storage property (`$foo`) by +providing a property named `storageValue`. As with the `value` property and (also optional) `init(initialValue:)`, the `storageValue` property must have the +same access level as its property delegate type. When present, `storageValue` is used to delegate accesses to the synthesized storage property. For example: + +```swift +class StorageManager { + func allocate(_: T.Type) -> UnsafeMutablePointer { ... } +} + +@propertyDelegate +struct LongTermStorage { + let pointer: UnsafeMutablePointer + + init(manager: StorageManager, _ initialValue: Value) { + pointer = manager.allocate(Value.self) + pointer.initialize(to: initialValue) + } + + var value: Value { + get { return pointer.pointee } + set { pointer.pointee = newValue } + } + + var storageValue: UnsafeMutablePointer { + return pointer + } +} +``` + +When we use the `LongTermStorage` delegate, it handles the coordination with the `StorageManager` and provides either direct access or an `UnsafeMutablePointer` with which to manipulate the value: + +```swift +let manager = StorageManager(...) + +@LongTermStorage(manager: manager, initialValue: "Hello") +var someValue: String + +print(someValue) // prints "Hello" +someValue = "World" // update the value in storage to "World" + +// $someValue accesses the storageValue property of the delegate instance, which +// is an UnsafeMutablePointer +let world = $someValue.move() // take value directly from the storage +$someValue.initialize(to: "New value") +``` + ### Restrictions on the use of property delegates There are a number of restrictions on the use of property delegates when defining a property: From 362b6bd34eea6f5adce81ff04275b7a67758aa09 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 9 Apr 2019 01:39:00 -0700 Subject: [PATCH 1112/4563] Point at the custom attributes "pitch" I made, for now --- proposals/NNNN-property-delegates.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 37c0b3dc37..dd0885ebcd 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -540,7 +540,7 @@ If there is no initializer for the delegate instance, and the property delegate @Lazy var foo: Int // okay: equivalent to @Lazy var foo: Int ``` -### Custom Attributes +### Custom attributes Property delegates are a form of custom attribute, where the attribute syntax is used to refer to entities declared in Swift. Grammatically, the use of property delegates is described as follows: @@ -557,7 +557,7 @@ The *type-identifier* must refer to a property delegate type, which can include The *expr-paren*, if present, provides the initialization arguments for the delegate instance. -This formulation of custom attributes fits in with a [larger proposal for custom attributes](https://github.com/hartbit/swift-evolution/blob/static-custom-attributes/proposals/XXXX-static-custom-attributes.md), which uses the same custom attribute syntax as the above but allows for other ways in which one can define a type to be used as an attribute. In this scheme, `@propertyDelegate` is just one kind of custom attribute: there will be other kinds of custom attributes that are available only at compile time (e.g., for tools) or runtime (via some reflection capability). +This formulation of custom attributes fits in with a [larger proposal for custom attributes](https://forums.swift.org/t/pitch-introduce-custom-attributes/21335/47), which uses the same custom attribute syntax as the above but allows for other ways in which one can define a type to be used as an attribute. In this scheme, `@propertyDelegate` is just one kind of custom attribute: there will be other kinds of custom attributes that are available only at compile time (e.g., for tools) or runtime (via some reflection capability). ### Mutability of properties with delegates From 3d306180d948f61c64d94e7a75e429f15ced33b0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 9 Apr 2019 01:47:04 -0700 Subject: [PATCH 1113/4563] Add second pitch thread --- proposals/NNNN-property-delegates.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index dd0885ebcd..c49f758190 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -15,7 +15,8 @@ these patterns to be defined as libraries. This is an alternative approach to some of the problems intended to be addressed by the [2015-2016 property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md). Some of the examples are the same, but this proposal takes a completely different approach designed to be simpler, easier to understand for users, and less invasive in the compiler implementation. There is a section that discusses the substantive differences from that design near the end of this proposal. -[Pitch](https://forums.swift.org/t/pitch-property-delegates/21895)
    +[Pitch #1](https://forums.swift.org/t/pitch-property-delegates/21895)
    +[Pitch #2](https://forums.swift.org/t/pitch-2-property-delegates-by-custom-attributes/22855)
    ## Motivation From 35ac844268f8e2e5aa5b82a4333d7fe2e465deef Mon Sep 17 00:00:00 2001 From: nate-chandler <46721658+nate-chandler@users.noreply.github.com> Date: Tue, 9 Apr 2019 16:41:30 -0700 Subject: [PATCH 1114/4563] [SE-NNNN] Eliding commas from multiline expression lists. (#1023) --- proposals/NNNN-elide-comma.md | 668 ++++++++++++++++++++++++++++++++++ 1 file changed, 668 insertions(+) create mode 100644 proposals/NNNN-elide-comma.md diff --git a/proposals/NNNN-elide-comma.md b/proposals/NNNN-elide-comma.md new file mode 100644 index 0000000000..e5c1ffbe00 --- /dev/null +++ b/proposals/NNNN-elide-comma.md @@ -0,0 +1,668 @@ +# Eliding commas from multiline expression lists + +* Proposal: [SE-NNNN](NNNN-elide-commas.md) +* Author: [Nate Chandler](https://github.com/nate-chandler), [Matthew Johnson](https://github.com/anandabits) +* Review Manager: TBD +* Status: **Implemented** +* Implementation: [apple/swift#21876](https://github.com/apple/swift/pull/22714) +* Previous Pitch: [Trailing commas in all expression lists](https://forums.swift.org/t/trailing-commas-in-all-expression-lists/19527) +* Previous Pitch: [SE-0084 spinoff: Newlines as item separators](https://forums.swift.org/t/se-0084-spinoff-newlines-as-item-separators/2659) + +# Introduction + +Swift requires a semicolon "`;`" to separate statements unless those statements are separated by newlines, in which case the semicolon can be elided. Currently, Swift requires a comma "`,`" to separate expressions even when those statements are separated by newlines. We should ease this restriction, allowing the comma between two expressions to be elided when they are separated by a newline. + +* Implementation: [apple/swift#21876](https://github.com/apple/swift/pull/22714) +* Previous Pitch: [SE-0084 spinoff: Newlines as item separators](https://forums.swift.org/t/se-0084-spinoff-newlines-as-item-separators/2659) + +## Reducing visual clutter + +When writing a list of expressions, you insert commas to tell the compiler where one expression ends and the next begins. When you add a newline between the expressions, though, you provide that same information by way of the newline characters. When newlines are present, commas provide clarity neither to human readers nor to the compiler. + +In these cases, at best, commas can be overlooked by human readers. At worst, they cause visual clutter and obscure the meaning you are trying to communicate in our code. Consider the following sample, taken from [Alamofire](https://github.com/Alamofire/Alamofire/blob/4df5912df4ebb138454aeba78f1470acc52dd4ae/Source/Request.swift#L649-L655): + +```swift +let protectionSpace = URLProtectionSpace( + host: host, + port: url.port ?? 0, + protocol: url.scheme, + realm: host, + authenticationMethod: NSURLAuthenticationMethodHTTPBasic +) +``` + +The commas here are not communicating anything. The writer has to put them in, the compiler has to observe that they're there and move along, and the reader has to filter them out. They're noise for all parties involved. Compare that to the following: + +```swift +let protectionSpace = URLProtectionSpace( + host: host + port: url.port ?? 0 + protocol: url.scheme + realm: host + authenticationMethod: NSURLAuthenticationMethodHTTPBasic +) +``` + +The difference is small, but significant: +1. **There are no spurious characters between the parentheses**. You are presented with a list of the names of the ingredients being used to construct the `URLProtectionSpace` on the left hand side of the colons and on the right hand side you see the values which are serving as those ingredients. +2. **The lines are symmetric**. the last line, lacking a comma, looks no different from the others, because they all lack a comma. +3. **Each line stands on its own**. Because they appear in single line argument lists, a comma at the end of a line has the effect of drawing your eye down to the next line. Without the commas, you have a moment to breathe at the end of the line, maybe to glance back at the argument label before moving on to the next line. + +Let's take a look at a couple more examples. + +To begin with, let's look at constructing another [type](https://github.com/apple/swift/blob/master/benchmark/utils/DriverUtils.swift#L59-L98) which takes many arguments. Here it is with commas: + +```swift +let config = TestConfig( + delim: "abc", + sampleTime: 42.0, + numIters: nil, + numSamples: nil, + quantile: nil, + delta: true, + verbose: false, + logMemory: true, + afterRunSleep: nil +) +``` + +and without: + +```swift +let config = TestConfig( + delim: "abc" + sampleTime: 42.0 + numIters: nil + numSamples: nil + quantile: nil + delta: true + verbose: false + logMemory: true + afterRunSleep: nil +) +``` + +Once again, the result is cleaner. All the characters that you see are relevant and meaningful. Each line you see is like the others. You're not drawn from one line to the next by the comma, you're free to scan through the items at your leisure. + +These same improvements are visible in expression lists besides the arguments to an initializer. Consider the following [function calls](https://github.com/apple/swift/blob/master/benchmark/single-source/StringComparison.swift), first with commas: + +```swift +StringTests.test("AssociatedTypes-UTF8View") { + typealias View = String.UTF8View + expectCollectionAssociatedTypes( + collectionType: View.self, + iteratorType: View.Iterator.self, + subSequenceType: Substring.UTF8View.self, + indexType: View.Index.self, + indicesType: DefaultIndices.self) +} + +StringTests.test("AssociatedTypes-UTF16View") { + typealias View = String.UTF16View + expectCollectionAssociatedTypes( + collectionType: View.self, + iteratorType: View.Iterator.self, + subSequenceType: Substring.UTF16View.self, + indexType: View.Index.self, + indicesType: View.Indices.self) +} +``` + +and then without: + + +```swift +StringTests.test("AssociatedTypes-UTF8View") { + typealias View = String.UTF8View + expectCollectionAssociatedTypes( + collectionType: View.self + iteratorType: View.Iterator.self + subSequenceType: Substring.UTF8View.self + indexType: View.Index.self + indicesType: DefaultIndices.self) +} + +StringTests.test("AssociatedTypes-UTF16View") { + typealias View = String.UTF16View + expectCollectionAssociatedTypes( + collectionType: View.self + iteratorType: View.Iterator.self + subSequenceType: Substring.UTF16View.self + indexType: View.Index.self + indicesType: View.Indices.self) +} +``` + +The difference is subtle but striking. + +## Making domain-specific languages first-class + +One case where the problem of visual clutter is especially pronounced is in domain-specific languages embedded (EDSLs) within Swift. Particularly when those EDSLs are "declarative". + +Consider an EDSL for specifying a table in a database: + +```swift +let table = Table( + name: "Employees", + columns: + guid ("record_id", isPrimaryKey: true, nullable: false), + guid ("manager_id", isPrimaryKey: false, nullable: true), + string ("name", length: 1024, nullable: false), + int64 ("employee_id", nullable: false), + date ("start_date", nullable: false) +) +``` + +Beyond merely defining a table, the intent is to do so in a way that feels natural in this context, to define a table "in its own terms". The majority of defining the table is taken up providing a list of columns, declaring them type first, in C style. + +The corresponding declaration in native Swift would look something like this: + +```swift +let recordID = SQLGUIDColumn(isPrimaryKey: true, nullable: false); +let managerID = SQLGUIDColumn(isPrimaryKey: true, nullable: true); +let name = SQLStringColumn(length: 1024, nullable: false); +let employeeID = SQLInt64Column(nullable: false); +let startDate = SQLDateColumn(nullable: false); +``` + +Note in particular the use of semicolons at the end. While the semicolons are not a huge problem, they are unpleasant. The main reason that they are unpleasant is that they are superfluous. We are merely going through some ceremony that is of service to nobody: not the writer, not the reader, and not the compiler. For that reason, semicolons are not required to end statements that are terminated by line endings in Swift. Instead, Swift allows you to write + +```swift +let recordID = SQLGUIDColumn(isPrimaryKey: true, nullable: false) +let managerID = SQLGUIDColumn(isPrimaryKey: true, nullable: true) +let name = SQLStringColumn(length: 1024, nullable: false) +let employeeID = SQLInt64Column(nullable: false) +let startDate = SQLDateColumn(nullable: false) +``` + +The situation with definition of the table in the EDSL is the same. The commas are providing information to nothing and nobody. They are ceremony for its own sake. Moreover, they are a constant visual reminder that you are looking at a list of arguments being passed to a function rather than a list of declarations in the EDSL. + +Just as Swift allows semicolons to be omitted, it should also allow the commas to be omitted, permitting + +```swift +let table = Table( + name: "Employees" + columns: + guid ("record_id", isPrimaryKey: true, nullable: false) + guid ("manager_id", isPrimaryKey: false, nullable: true) + string ("name", length: 1024, nullable: false) + int64 ("employee_id", nullable: false) + date ("start_date", nullable: false) +) +``` + +With the commas present, the reader is saddled with a constant reminder that she is "just" reading a list of arguments, not language uses that stand on their own. Once they are removed, the author can express her intent directly, that the table is defined by a series of declarations, *not* by passing a series of arguments to a function. That fact, while still visible, is reduced from its current overbearing status to an implementation detail of the EDSL. + +Without the commas, each column can be viewed as intended: *as a **declaration** in the EDSL*. + +By allowing statement separators to be omitted when a newline is present while requiring expression separators Swift exhibits a subtle bias, making imperative code look clean where declarative code must be cluttered with punctuation. By providing the same affordance for declarative style, this bias towards imperative style is lifted, allowing declarative EDSLs to feel just as natural as imperative Swift code. + +This example EDSL is not taken from a real library. There are, despite the current limitation, many shipping Swift libraries that make extensive use of EDSLs. Let's take a look at the EDSLs from those in several different domains to see how they would be improved by comma elision: + +### Networking EDSL case study: HTTP Requests + +HTTP requests are pervasive in modern applications. To avoid dealing directly with networking-layer API, higher-level abstractions are often used to specify requests. Here's an example taken from some real-world code: + +```swift +Request( + method: .get, + host: .someHost, + path: "/path/to/model", + query: [ + "page": 10, + "order": "ascending", + ] +) +``` + +A request is specified by a series of declarations. The request will use the GET verb. It will be made against `someHost` at "/path/to/model". The content should be ordered ascending and there should be ten entities per page. + +The commas here add no value. At the least, they are line noise. At worst, they obscure the fact that there is a series of declarations, forcing the reader back into viewing the each line as *just* an argument to an initializer. + +Here's the same code without commas: + +```swift +Request( + method: .get + host: .someHost + path: "/path/to/model" + query: [ + "page": 10 + "order": "ascending" + ] +) +``` + +As you read the code, first you see that a Request is being initialized. In that context, you see a number of declarations about the request, that it uses the GET verb and so on. The elision of commas allows you to focus on what's important, the declarations that make up the definition of the `Request`. You are not constantly reminded that each line is an argument passed to an initializer but are instead free to think of each as a declaration in the language in which HTTP requests are specified. + +### App Routing EDSL case study: Jason Prasad's Routing library + +The [routing](https://github.com/jjgp/Routing) library provides a convenient API for specifying which screen of an app should be displayed when the app is launched via a URL. Here's an example usage taken from the project's README: + +```swift +router.map("routingexample://present/login", + source: .storyboard(storyboard: "Main", identifier: "LoginViewController", bundle: nil), + style: .inNavigationController(.present(animated: true)), + setup: presentationSetup) +``` + +This code specifies that when the app is launched with the `present/login` path, the `LoginViewController` from the `Main` storyboard will be presented in a navigation controller. + +Here's how that code looks without commas and with newlines added instead: + +```swift +router.map( + "routingexample://present/login" + source: .storyboard(name: "Main" + identifier: "LoginViewController" + bundle: nil) + style: .inNavigationController(.present(animated: true)) + setup: presentationSetup +) +``` + +Without the commas, and with newlines added to the storyboard example, the code looks much cleaner. Moreover, while in Swift an instance of the `ControllerSource` enum is being instantiated via the `.storyboard` implicit member access, in this code snippet, you are free to ignore those mechanics and focus instead on the declaration of a location from which to obtain a view controller in a storyboard named "Main" under the identifier "LoginViewController". + +## Improving the editing experience + +Another, more minor point is that commas in these positions cause problems when editing code. In Swift today, you can easily add or remove any item--even the last--from a collection literal by commenting it in or out: + +```swift +let colors = [ + "red", + "green", + "blue", +// "cerulean" +] +``` + +Unfortunately that convenience is not available fully in the other expression lists. For example, in a multiline function call, it is a breeze to comment out any argument + +```swift +print( + "red", +// "green", // ok + "blue", + "cerulean" +) +``` + +*except* the last; commenting it out raises an error: + + +```swift +print( + "red", + "green", + "blue", // error: unexpected ',' separator +// "cerulean" +) +``` + +The reason for these inconsistent behaviors is that trailing commas are permitted in collection literals but not in any other expression list. + +One solution would be to allow trailing commas in all expression lists. That change, however, only addresses part of the problem. The visual noise that the commas cause not only remains but is magnified: to get this convenience, we would be incentivized to write our code with trailing commas in all multiline expression lists. + +Instead, we should allow commas to be elided from multiline expression lists entirely. Without commas, the original function call would instead look like + +```swift +print( + "red" + "green" + "blue" + "cerulean" +) +``` + +with its arguments untarnished by commas. We would be free to comment out any line of it, including the last + +```swift +print( + "red" + "green" + "blue" +// "cerulean" +) +``` + +because what remains is again a multiline expression list with commas elided. + +## Proposed solution + +Rather than allowing comma elision in just some expression lists in an ad hoc fashion, this document proposes allowing commas to be elided uniformly in all multiline expression lists. + +### When will you still use commas? + +When parsing an expression, the compiler keeps going until it can't any longer, following the [maximal munch](https://en.wikipedia.org/wiki/Maximal_munch) principle. Sometimes, though, you want one expression to end and the next to begin before the parser would otherwise stop. In those situations, you will use a comma to communicate that intent. + +There are two scenarios where you will use commas to clarify that one expression is ending and the next is beginning: + +#### Implicit members + +When parsing a multiline expression list featuring a member expression which appears after a newline + +```swift +foo( + bar + .baz +) +``` + +the member expression will be interpreted as modifying the expression that preceded it + +```swift +foo(bar.baz) +``` + +rather than as a separate expression + +```swift +foo(bar, .baz) +``` + +If you actually want `.baz` to be as an implicit member, an expression in its own right, you will add a comma: + +```swift +foo( + bar, + .baz +) +``` + +##### Diagnostics + +When this situation comes up, the issue can be ameliorated via a good diagnostic experience. If you attempt to compile the original function call + +```swift +foo( + bar + .baz +) +``` + +in a context where `baz` is not an instance member of `bar`'s type but is a static member of the type of the second parameter accepted by `foo` + +```swift +enum E { + case baz +} + +func foo(_ bar: Bar, _ e: E) {...} +``` + +the error can be diagnosed with a fixit to insert a comma at the end of the line before `.baz`, right after `bar`, leaving you with the code you intended: + +```swift +foo( + bar, + .baz +) +``` + +If you attempt to compile the original function call + +```swift +foo( + bar + .baz +) +``` + +in a context where both `foo(bar.baz)` and `foo(bar, .baz)` are legal, a warning can be emitted with several fixits to clarify the intent by doing one of the following: + +1. inserting a comma + +```swift +foo( + bar, + .baz +) +``` + +2. eliminating the newline + +```swift +foo( + bar.baz +) +``` + +3. indenting the second expression + +```swift +foo( + bar + .baz +) +``` + +as is suggested today for an expression on the line after the `return` keyword at its same indentation level. + +#### Closures + +In a similar vein, when parsing a multiline expression list featuring a closure which appears after a newline + +```swift +foo( + bar + { print("baz") } +) +``` + +the closure will be interpreted as a trailing closure passed as an argument to the expression that preceded it. + +```swift +foo(bar { print("baz") }) +``` + +rather than as a separate expression + +```swift +foo(bar, { print("baz") } +``` + +If you actually want the closure to stand on its own, to be its own expression, you will add a comma to separate it from the preceding expression. + +```swift +foo( + bar, + { print("baz") } +) +``` + +##### Diagnostics + +As with implicit members, we will be able to provide a good diagnostic experience here including both + +1. an error with a fixit to insert a comma before the closure which is being parsed as a trailing closure in the case where using the closure as a trailing closure doesn't typecheck +2. a warning with multiple fixits to insert a comma or change whitespace in the case where the closure could be used both as a trailing closure and as a top level expression in the list + +#### Alternatives to adding commas + +These situations may sound familiar--they are exactly the same situations where we need to use semicolons to separate items in statement lists, even in the presence of newlines. In practice, you will need to use commas more often than semicolons because it is more often for these expressions to appear in expression lists than in statement lists. + +That said, you will need to use them less often than it might at first seem. + +Consider closures: Trailing closure syntax means that most of the time, closures appear after the end of the expression list. Typically, the above example would actually be written + +```swift +foo( + bar +) +{ + print("baz") +} +``` + +What about implicit members? Consider a function call like this: + +```swift +buyTreat( + .sweet + .orange +) +``` + +This would be parsed as `.sweet.orange`, which may not be what you want. Even to a human, reader, though, it's not clear what is meant. To make code obvious to readers, you often use argument labels (`flavor: .orange`) to provide a hint to readers of what the implicit member may be a member of: + +```swift +buyTreat( + .sweet + flavor: .orange +) +``` + +If you would prefer to leave out an argument label, you could also provide the type `Flavor.orange` in order to provide a reader with that context: + +```swift +buyTreat( + .sweet + Flavor.orange +) +``` + +If you don't want to use either of those approaches, only then will you end the prior expression with a comma. + +Without this change, you are forced to use commas everywhere. In multiline expression lists, they are reduced to line noise and meaninglessness. A comma is a character to be ignored. With this change, if you omit commas whenever possible, when you write a comma, you will mean something: "without this, the previous expression would keep going; I want it to end here." + +## Detailed design + +Swift will allow the comma separating two expressions in an expression list to be elided provided that there is a newline separating the expressions. + +The grammatical productions from The Swift Programming Language will be modified as follows: + +
    +expression-list -> expression | expression , expression-list | expression \n expression-list 
    +function-call-argument-list -> function-call-argument | function-call-argument , function-call-argument-list | function-call-argument \n function-call-argument-list
    +tuple-element-list -> tuple-element | tuple-element , tuple-element-list | tuple-element \n tuple-element-list
    +
    + +With these few changes to the grammatical productions, comma elision will be accepted in the following positions: + +- array literals +```swift +[ + "red" + "green" +] +``` +- dictionary literals +```swift +[ + "red" : 4 + "green" : 8 +] +``` +- free function calls +```swift +print( + "red" + "green" +) +``` +- method calls +```swift +foo.print( + "red" + "green" +) +``` +- initializer calls +```swift +let instance = Widget( + "red" + "green" +) +``` +- subscript reads +```swift +foo[ + "red" + "green" +] +``` +- subscript writes +```swift +foo[ + "red" + "green" +] = "yellow" +``` +- super method calls +```swift +super.print( + "red" + "green" +) +``` +- super initializer calls +```swift +super.init( + "red" + "green" +) +``` +- super subscript reads +```swift +super[ + "red" + "green" +] +``` +- super subscript writes +```swift +super[ + "red" + "green" +] = "yellow" +``` +- enum instantiations +```swift +let e = E.foo( + "red" + "green" +) +``` +- tuple instantiations +```swift +let t = ( + "red" + "green" +) +``` +- key-path subscripts +```swift +let path = \Gadget[ + 0 + 1 +] +``` + +## Source compatibility + +This is not a source-breaking change. Extensive compatibility tests have been run against the change. + +This document does *not* propose removing commas from the language. All code that is legal today will continue to be legal. This document proposes easing a restriction, making more code legal. + +@blangmuir looked into SourceKit's under the change and determined everything just works without any other changes. Autocomplete continues to function as before. + +Because statement separator (i.e. semicolon) elision has been in the language for so long, all the engineering problems for expression separator (i.e. comma) elision have already been solved. + +## Effect on ABI stability + +N/A + +## Effect on API resilience + +N/A + +## Alternatives considered + +- Allow trailing commas in expression lists. + +While trailing commas in expression lists would provide the same improvements in the editing experience that comma elision does, they do not bring the same readability improvements to the language as comma elision. + +- Base interpretation of arguments off of semantic information. + +The two cases where commas will still be written listed above may seem less than ideal. It is tempting to ask whether we could decide the number of expressions in the expression list based on the context in which it appears. Swift does not currently do this sort of interpretation based on semantic information and doing so massively complicates the language. + From 257a6b803149432e415d064110b4658dca63d80e Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 9 Apr 2019 16:44:05 -0700 Subject: [PATCH 1115/4563] Initiate review for SE-0257. --- proposals/{NNNN-elide-comma.md => 0257-elide-comma.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-elide-comma.md => 0257-elide-comma.md} (99%) diff --git a/proposals/NNNN-elide-comma.md b/proposals/0257-elide-comma.md similarity index 99% rename from proposals/NNNN-elide-comma.md rename to proposals/0257-elide-comma.md index e5c1ffbe00..d81b5efcc6 100644 --- a/proposals/NNNN-elide-comma.md +++ b/proposals/0257-elide-comma.md @@ -1,9 +1,9 @@ # Eliding commas from multiline expression lists -* Proposal: [SE-NNNN](NNNN-elide-commas.md) +* Proposal: [SE-0257](0257-elide-commas.md) * Author: [Nate Chandler](https://github.com/nate-chandler), [Matthew Johnson](https://github.com/anandabits) -* Review Manager: TBD -* Status: **Implemented** +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Status: **Active review (April 9 - April 18, 2019)** * Implementation: [apple/swift#21876](https://github.com/apple/swift/pull/22714) * Previous Pitch: [Trailing commas in all expression lists](https://forums.swift.org/t/trailing-commas-in-all-expression-lists/19527) * Previous Pitch: [SE-0084 spinoff: Newlines as item separators](https://forums.swift.org/t/se-0084-spinoff-newlines-as-item-separators/2659) From 049f070cbf8cd15612282cf2ebfcf97188a7e427 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 9 Apr 2019 19:12:13 -0700 Subject: [PATCH 1116/4563] Fix dashboard link. --- proposals/0257-elide-comma.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0257-elide-comma.md b/proposals/0257-elide-comma.md index d81b5efcc6..870d740c84 100644 --- a/proposals/0257-elide-comma.md +++ b/proposals/0257-elide-comma.md @@ -1,6 +1,6 @@ # Eliding commas from multiline expression lists -* Proposal: [SE-0257](0257-elide-commas.md) +* Proposal: [SE-0257](0257-elide-comma.md) * Author: [Nate Chandler](https://github.com/nate-chandler), [Matthew Johnson](https://github.com/anandabits) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Active review (April 9 - April 18, 2019)** From 534421abe2b9ac6756ae854e2efda68670929ae1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 10 Apr 2019 16:05:47 -0700 Subject: [PATCH 1117/4563] Accept SE-0254: Static and class subscripts --- proposals/0254-static-subscripts.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0254-static-subscripts.md b/proposals/0254-static-subscripts.md index 00de13e95a..bfc20f7aeb 100644 --- a/proposals/0254-static-subscripts.md +++ b/proposals/0254-static-subscripts.md @@ -3,8 +3,9 @@ * Proposal: [SE-0254](0254-static-subscripts.md) * Authors: [Brent Royal-Gordon](https://github.com/brentdax) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (April 2...9, 2019)** +* Status: **Accepted** * Implementation: [apple/swift#23358](https://github.com/apple/swift/pull/23358), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/264//artifact/branch-master/swift-PR-23358-264-osx.tar.gz), [Ubuntu Linux 16.04 toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/205//artifact/branch-master/swift-PR-23358-205-ubuntu16.04.tar.gz) +* Review: ([review](https://forums.swift.org/t/se-0254-static-and-class-subscripts/22537), [acceptance](https://forums.swift.org/t/accepted-se-0254-static-and-class-subscripts/22941)) ## Introduction From 58f698c7fa10b03f5a11133d970fb8dcc761ec3e Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 10 Apr 2019 16:09:56 -0700 Subject: [PATCH 1118/4563] Revise SE-244 for re-review (#1018) * Revise SE-244 for re-review * Update status and resume review. --- proposals/0244-opaque-result-types.md | 536 ++++++++------------------ 1 file changed, 152 insertions(+), 384 deletions(-) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index b992b64163..ae9d653300 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -3,49 +3,17 @@ * Proposal: [SE-0244](0244-opaque-result-types.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Returned for revision** +* Status: **Active review (April 11 - April 17, 2019)** * Implementation: [apple/swift#22072](https://github.com/apple/swift/pull/22072) * Toolchain: https://github.com/apple/swift/pull/21137#issuecomment-468118328 -* Review thread: https://forums.swift.org/t/se-0244-opaque-result-types/21252 +* Previous revisions: ([1](https://github.com/apple/swift-evolution/commit/e60bac23bf0d6f345ddb48fbf64ea8324fce79a9)) +* Previous review threads: https://forums.swift.org/t/se-0244-opaque-result-types/21252 ## Introduction -This proposal introduces the ability to hide the specific result type of a function from its callers. The result type is described only by its capabilities, for example, to say that a function returns a `Collection` without specifying which specific collection. Clients can use the resulting values freely, but the underlying concrete type is known only to the implementation of the function and need not be spelled out explicitly. +This proposal is the first part of a group of changes we're considering in a [design document for improving the UI of the generics model](https://forums.swift.org/t/improving-the-ui-of-generics/22814). We'll try to make this proposal stand alone to describe opaque return types, their design, and motivation, but we also recommend reading the design document for more in-depth exploration of the relationships among other features we're considering. We'll link to relevant parts of that document throughout this proposal. -Consider an operation like the following: - -```swift -let resultValues = values.lazy.map(transform).filter { $0 != nil }.map { $0! } -``` - -If we want to encapsulate that code in another method, we're going to have to write out the type of that lazy-map-filter chain, which is rather ugly: - -```swift -extension LazyMapCollection { - public func compactMap( - _ transform: @escaping (Elements.Element) -> ElementOfResult? - ) -> LazyMapSequence>, ElementOfResult> { - return self.map(transform).filter { $0 != nil }.map { $0! } - } -} -``` - -The author of `compactMap(_:)` doesn't want to have to figure out that type, and the clients of `compactMap(_:)` don't want to have to reason about it. `compactMap(_:)` is the subject of [SE-0222](https://github.com/apple/swift-evolution/blob/master/proposals/0222-lazy-compactmap-sequence.md), which introduces a new standard library type `LazyCompactMapCollection` for the sole purpose of describing the result type of `compactMap(_:)`: - -```swift -extension LazyMapCollection { - public func compactMap( - _ transform: @escaping (Element) -> ElementOfResult? - ) -> LazyCompactMapCollection { - return LazyCompactMapCollection(/* ... */) - } -} -``` - -This is less verbose, but requires the introduction of a new, public type solely to describe the result of this function, increasing the surface area of the standard library and requiring a nontrivial amount of implementation. - -Other libraries may want to provide libraries of composable generic components in a similar manner. -A graphics library could provide primitives for shapes: +This specific proposal addresses the problem of [type-level abstraction for returns](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--missing-type-level-abstraction). Many libraries consist of composable generic components. For example, a graphics library might provide primitive types for basic shapes: ```swift protocol Shape { @@ -58,7 +26,7 @@ struct Rectangle: Shape { /* ... */ } struct Circle: Shape { /* ... */ } ``` -along with composable transformations to combine and modify primitive shapes: +along with composable transformations to combine and modify primitive shapes into more complex ones: ```swift struct Union: Shape { @@ -78,7 +46,7 @@ struct Transformed: Shape { } ``` -One could compose these transformations by using the existential type `Shape` instead of generic arguments, but doing so would imply more dynamism and runtime overhead than may be desired. By composing generic containers, generic specialization can optimize together the composed operations like it does for `lazy` collections. A game or graphics app may want to define objects in terms of their shapes: +One could compose these transformations by using the existential type `Shape` instead of generic arguments, but doing so would imply more dynamism and runtime overhead than may be desired. If we directly compose the generic containers, maintaining the concrete types, then generic specialization can more readily optimize together the composed operations together, and the type system can also be used . A game or graphics app may want to define objects in terms of their shapes: ```swift protocol GameObject { @@ -89,7 +57,7 @@ protocol GameObject { } ``` -However, this means that implementers of the `GameObject` protocol would now be burdened with writing out long, explicit types for their shapes: +However, users of the `GameObject` protocol would now be burdened with writing out long, explicit types for their shapes: ```swift struct EightPointedStar: GameObject { @@ -99,7 +67,13 @@ struct EightPointedStar: GameObject { } ``` -Opaque result types allow the method to state the capabilities of its result type without tying it down to a concrete type. For example, the above could instead be written: +This is unsightly because it's verbose, but it's also not very helpful for someone reading this declaration. The exact return type doesn't really matter, only the fact that it conforms to `Shape`. Spelling out the return type also effectively reveals most of the implementation of `shape`, making the declaration brittle; clients of `EightPointedStar` could end up relying on its exact return type, making it harder if the author of `EightPointedStar` wants to change how they implement its shape, such as if a future version of the library provides a generic `NPointedStar` primitive. Right now, if you want to abstract the return type of a declaration from its signature, existentials or manual type erasure are your only options, and these [come with tradeoffs](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--limits-of-existentials) that are not always acceptable. + +## Proposed solution + +Instead of declaring the specific return type of `EightPointedStar.shape`'s current implementation, +all we really want to say is that it returns something that conforms to `Shape`. We propose +the syntax `some Protocol`: ```swift struct EightPointedStar: GameObject { @@ -109,61 +83,106 @@ struct EightPointedStar: GameObject { } ``` -to declare that an `EightPointedStar` has some `Shape` without having to specify exactly what shape that is. The underlying concrete type is hidden, and can even change from one version of the library to the next without breaking those clients, because the actual type identity was never exposed. This allows the library to provide a potentially-more-efficient design without expanding the surface area of the library or burdening implementors of the library's protocols with impractical type compositions. +to declare that an `EightPointedStar` has some `Shape` without having to specify exactly what shape that is. The underlying concrete type is hidden, and can even change from one version of the library to the next without breaking those clients, because the underlying type identity is never exposed to clients. Unlike an existential, though, clients still have access to the type identity, so values This allows the library to provide a potentially-more-efficient design that leverages Swift's type system, without expanding the surface area of the library or making implementors of the library's protocols rely on exposing verbose implementation types. -Swift-evolution thread: [Opaque result types](https://forums.swift.org/t/opaque-result-types/15645) +An opaque type behaves like a ["reverse generic"](https://forums.swift.org/t/reverse-generics-and-opaque-result-types/21608). In a traditional generic function, the caller decides what types get bound to the callee's generic arguments: -## Proposed solution +```swift +func generic() -> T { ... } + +let x: Rectangle = generic() // T == Rectangle, chosen by caller +let x: Circle = generic() // T == Circle, chosen by caller +``` -This proposal introduces syntax that can be used to describe an opaque result type for a function. Instead of specifying a specific return type, a function can declare that it returns `some` type that satisfies a set of constraints, such as protocol requirements. This `some` specifier can only be used in the result type of a function, the type of a property, or the element type of a subscript declaration. The return type is backed by some specific concrete type, but that type is only known to the implementation of that function/property/subscript. Everywhere else, the type is opaque, and is described only by its characteristics and originating function/property/subscript. For example, a function can declare that it produces something that's a `MutableCollection` and `RangeReplaceableCollection`: +An opaque return type can be thought of as putting the generic signature "to the right" of the function arrow; instead of being a type chosen by the caller that the callee sees as abstracted, the return type is chosen by the callee, and comes back to the caller as abstracted: ```swift -func makeMeACollection(with element: T) -> some MutableCollection & RangeReplaceableCollection { - return [element] // ok: an array of T satisfies all of the requirements +// Strawman syntax +func reverseGeneric() -> T { return Rectangle(...) } + +let x = reverseGeneric() // abstracted type chosen by reverseGeneric's implementation +``` + +Reverse generics are a great mental model for understanding opaque return types, but the notation +is admittedly awkward. We expect the common use case for this feature to be a single return value behind a set of protocol conformances, so we're proposing to start with the more concise `some Shape` syntax: + +```swift +// Proposed syntax +func reverseGeneric() -> some Shape { return Rectangle(...) } + +let x = reverseGeneric() // abstracted type chosen by reverseGeneric's implementation +``` + +Following the `some` keyword is a set of constraints on the implicit generic type variable: a class, protocol, `Any`, `AnyObject`, or some composition thereof (joined with `&`). +This `some Protocol` sugar [can be generalized to generic arguments and structural positions in return types](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--directly-expressing-constraints) in the future, and we could also eventually support a fully general generic signature for opaque returns. To enable incremental progress on the implementation, we propose starting by only supporting the `some` syntax in return position. + +### Type identity + +Generics give us an idea of what to expect from opaque return types. The return values from different calls to the same function have the same return type, like two variables of the same generic argument type: + +```swift +func foo(x: T, y: T) -> some Equatable { + let condition = x == y // OK to use ==, x and y are the same generic type T + return condition ? 1738 : 679 } + +let x = foo("apples", "bananas") +let y = foo("apples", "some fruit nobody's ever heard of") + +print(x == y) // also OK to use ==, x and y are the same opaque return type ``` -Following the `some` keyword is a class, protocol, `Any`, `AnyObject`, or composition thereof (joined with `&`). A caller to `makeMeACollection(_:)` can rely on the result type satisfying all of the requirements listed. For example: +If the opaque type exposes associated types, those associated types' identities are also maintained. This allows the full API of protocols like the `Collection` family to be used: ```swift +func makeMeACollection(with: T) -> some RangeReplaceableCollection & MutableCollection { ... } + var c = makeMeACollection(with: 17) c.append(c.first!) // ok: it's a RangeReplaceableCollection c[c.startIndex] = c.first! // ok: it's a MutableCollection print(c.reversed()) // ok: all Collection/Sequence operations are available func foo(_ : C) { } -foo(c) // ok: C inferred to opaque result type of makeMeACollection() +foo(c) // ok: C inferred to opaque result type of makeMeACollection ``` -Moreover, opaque result types to be used freely with other generics, e.g., forming a collection of the results: +Moreover, opaque result types preserve their identity when composed into other types, such as when forming a collection of the results: ```swift var cc = [c] -cc.append(c) // ok: cc's Element == the result type of makeMeACollection +cc.append(c) // ok: cc's Element == the result type of makeMeACollection var c2 = makeMeACollection(with: 38) -cc.append(c2) // ok: Element == the result type of makeMeACollection +cc.append(c2) // ok: Element == the result type of makeMeACollection ``` -### Type identity +The opaque return type can however depend on the generic arguments going into the function when +it's called, so the return types of the same function invoked with different generic arguments are +different: + +```swift +var d = makeMeACollection(with: "seventeen") +c = d // error: types or makeMeACollection and makeMeACollection are different +``` -An opaque result type is not considered equivalent to its underlying type by the static type system: +Like a generic argument, the static type system does not consider the opaque type to be statically equivalent to the type it happens to be bound to: ```swift -var intArray = [Int]() -cc.append(intArray) // error: [Int] is not known to equal the result type of makeMeACollection +func foo() -> some BinaryInteger { return 219 } +var x = foo() +x = 912 // error: Int is not known to be the same as the return type as foo() ``` -However, as with generic type parameters, one can inspect an opaque type's underlying type at runtime. For example, a conditional cast could determine whether the result of `makeMeACollection` is of a particular type: +However, one can inspect an opaque type's underlying type at runtime using dynamic casting: ```swift -if let arr = makeMeACollection(Int.self) as? [Int] { - print("It's an [Int], \(arr)\n") +if let x = foo() as? Int { + print("It's an Int, \(x)\n") } else { print("Guessed wrong") } ``` -In other words, opaque result types are only opaque to the static type system. They don't exist at runtime. +In other words, like generic arguments, opaque result types are only opaque to the static type system. They don't have an independent existence at runtime. ### Implementing a function returning an opaque type @@ -268,6 +287,8 @@ func f9b() -> some P { } ``` +This restriction on non-terminating functions could be something we lift in the future, by synthesizing bottom-type conformances to the protocols required by the opaque type. We leave that as a future extension. + ### Properties and subscripts Opaque result types can also be used with properties and subscripts: @@ -331,6 +352,7 @@ vendor[0] = vendor[2] // ok: can move elements around ``` ### Associated type inference + While one can use type inference to declare variables of the opaque result type of a function, there is no direct way to name the opaque result type: ```swift @@ -339,7 +361,7 @@ func f1() -> some P { /* ... */ } let vf1 = f1() // type of vf1 is the opaque result type of f1() ``` -However, the type inference used to satisfy associated type requirements can deduce an opaque result type as the associated type of the protocol: +However, type inference can deduce an opaque result type as the associated type of the protocol: ```swift protocol GameObject { @@ -360,134 +382,6 @@ pos = Player().shape // ok: returns the same opaque result type Note that having a name for the opaque result type still doesn't give information about the underlying concrete type. For example, the only way to create an instance of the type `S.SomeType` is by calling `S.someValue()`. -### Opaque result types vs. existentials - -On the surface, opaque result types are quite similar to (generalized) existential types: in each case, the specific concrete type is unknown to the static type system, and can be manipulated only through the stated capabilities (e.g., protocol and superclass constraints). There are some similarities between the two features for code where the identity of the return type does not matter. For example: - -```swift -protocol P - func foo() -} - -func anyP() -> P { /* ... */ } -func someP() -> some P { /* ... */ } - -anyP().foo() // ok -someP().foo() // ok -``` - -However, the fundamental difference between opaque result types and existentials revolves around type identity. All instances of an opaque result type are guaranteed to have the same type at run time, whereas different instances of an existential type may have different types at run time. It is this aspect of existential types that makes their use so limited in Swift. For example, consider a function that takes two values of (existential) type `Equatable` and tries to compare them: - -```swift -protocol Equatable { - static func ==(lhs: Self, rhs: Self) -> Bool -} - -func isEqual(_ x: Equatable, y: Equatable) -> Bool { - return x == y -} -``` - -The `==` operator is meant to take two values of the same type and compare them. It's clear how that could work for a call like `isEqual(1, 2)`, because both `x` and `y` store values of type `Int`. - -But what about a call `isEqual(1, "one")`? Both `Int` and `String` are `Equatable`, so the call to `isEqual` should be well-formed. However, how would the evaluation of `==` work? There is no operator `==` that works with an `Int` on the left and a `String` on the right, so it would fail at run-time with a type mismatch. - -Swift rejects the example with the following diagnostic: - -``` -error: protocol 'Equatable' can only be used as a generic constraint because it has Self or associated type requirements -``` - -The generalized existentials proposal dedicates quite a bit of its design space to [ways to check whether two instances of existential type contain the same type at runtime](https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md#real-types-to-existentials-associated-types) to cope with this aspect of existential types. Generalized existentials can make it *possible* to cope with values of `Equatable` type, but it can't make it easy. The following is a correct implementation of `isEqual` using generalized existentials: - -```swift -func isEqual(_ x: Equatable, y: Equatable) -> Bool { - if let yAsX = y as? x.Self { - return x == yAsX - } - - if let xAsY = x as? y.Self { - return xAsY == y - } - - return false -} -``` - -Note that the user must explicitly cope with the potential for run-time type mismatches, because the Swift language will not implicitly defer type checking to run time. - -Existentials also interact poorly with generics, because a value of existential type does not conform to its own protocol. For example: - -```swift -protocol P { } - -func acceptP(_: T) { } -func provideP(_ p: P) { - acceptP(p) // error: protocol type 'P' cannot conform to 'P' because only - // concrete types can conform to protocols -} -``` - -[Hamish](https://stackoverflow.com/users/2976878/hamish) provides a [complete explanation on StackOverflow](https://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself) as to why an existential of type `P` does not conform to the protocol `P`. The following example from that answer demonstrates the point with an initializer requirement: - -```swift -protocol P { - init() -} - -struct S: P {} -struct S1: P {} - -extension Array where Element: P { - mutating func appendNew() { - // If Element is P, we cannot possibly construct a new instance of it, as you cannot - // construct an instance of a protocol. - append(Element()) - } -} - -var arr: [P] = [S(), S1()] - -// error: Using 'P' as a concrete type conforming to protocol 'P' is not supported -arr.appendNew() -``` - -Hamish notes that: - -> We cannot possibly call `appendNew()` on a `[P]`, because `P` (the `Element`) is not a concrete type and therefore cannot be instantiated. It must be called on an array with concrete-typed elements, where that type conforms to `P`. - -The major limitations that Swift places on existentials are, fundamentally, because different instances of existential type may have different types at run time. Generalized existentials can lift some restrictions (e.g., they can allow values of type `Equatable` or `Collection` to exist), but they cannot make the potential for run-time type conflicts disappear without weakening the type-safety guarantees provided by the language (e.g., `x == y` for `Equatable` `x` and `y` will still be an error) nor make existentials as powerful as concrete types (existentials still won't conform to their own protocols). - -Opaque result types have none of these limitations, because an opaque result type is a name for some fixed-but-hidden concrete type. If a function returns `some Equatable` result type, one can compare the results of successive calls to the function with `==`: - -```swift -func getEquatable() -> some Equatable { - return Int.random(in: 0..<10) -} - -let x = getEquatable() -let y = getEquatable() -if x == y { // ok: calls to getEquatable() always return values of the same type - print("Bingo!") -} -``` - -Opaque result types *do* conform to the protocols they name, because opaque result types are another name for some concrete type that is guaranteed to conform to those protocols. For example: - -```swift -func isEqualGeneric(_ lhs: T, _ rhs: T) -> Bool { - return lhs == rhs -} - -let x = getEquatable() -let y = getEquatable() -if isEqual(x, y) { // ok: the opaque result of getEquatable() conforms to Equatable - print("Bingo!") -} -``` - -(Generalized) existentials are well-suited for use in heterogeneous collections, or other places where one expects the run-time types of values to vary and there is little need to compare two different values of existential type. However, they don't fit the use cases outlined for opaque result types, which require the types that result from calls to compose well with generics and provide the same capabilities as a concrete type. - ## Detailed design ### Grammar of opaque result types @@ -500,7 +394,7 @@ type ::= opaque-type opaque-type ::= 'some' type ``` -The `type` following the `'some'` keyword is semantically restricted to be a class or existential type, meaning it must consist only of `Any`, `AnyObject`, protocols, or base classes, possibly composed using `&`. +The `type` following the `'some'` keyword is semantically restricted to be a class or existential type, meaning it must consist only of `Any`, `AnyObject`, protocols, or base classes, possibly composed using `&`. This type is used to describe the constraints on the implicit "reverse generic" parameter. ### Restrictions on opaque result types @@ -512,7 +406,9 @@ func f(flip: Bool) -> (some P)? { // error: `some P` is not the entire return ty } ``` -Opaque result types cannot be used in the requirements of a protocol: +This restriction could be lifted in the future. + +More fundamentally, opaque result types cannot be used in the requirements of a protocol: ```swift protocol Q { @@ -520,7 +416,7 @@ protocol Q { } ``` -Associated types provide a better way to model the same problem, and the requirements can then be satisfied by a function that produces an opaque result type. +Associated types provide a better way to model the same problem, and the requirements can then be satisfied by a function that produces an opaque result type. (There might be an interesting shorthand feature here, where using `some` in a protocol requirement implicitly introduces an associated type, but we leave that for future language design to explore.) Similarly to the restriction on protocols, opaque result types cannot be used for a non-`final` declaration within a class: @@ -531,6 +427,8 @@ class C { } ``` +This restriction could conceivably be lifted in the future, but it would mean that override implementations would be constrained to returning the same type as their super implementation, meaning they must call `super.method()` to produce a valid return value. + ### Uniqueness of opaque result types Opaque result types are uniqued based on the function/property/subscript and any generic type arguments. For example: @@ -578,7 +476,7 @@ We could allow an API originally specified using an opaque result type to later ## Rust's `impl Trait` -The proposed Swift feature is largely based on Rust's `impl Trait` language feature, described by [Rust RFC 1522](https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md) and extended by [Rust RFC 1951](https://github.com/rust-lang/rfcs/blob/master/text/1951-expand-impl-trait.md). There are only a small number of differences between this feature as expressed in Swift vs. Rust's `impl Trait` as described in RFC 1522: +The proposed Swift feature is largely inspired by Rust's `impl Trait` language feature, described by [Rust RFC 1522](https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md) and extended by [Rust RFC 1951](https://github.com/rust-lang/rfcs/blob/master/text/1951-expand-impl-trait.md). There are only a small number of differences between this feature as expressed in Swift vs. Rust's `impl Trait` as described in RFC 1522: * Swift's need for a stable ABI necessitates translation of opaque result types as resilient types, which is unnecessary in Rust's model, where the concrete type can always be used for code generation. * Swift's opaque result types are fully opaque, because Swift doesn't have pass-through protocols like Rust's `Send` trait, which simplifies the type checking problem slightly. @@ -586,170 +484,96 @@ The proposed Swift feature is largely based on Rust's `impl Trait` language feat ## Alternatives considered -The main design question here is the keyword to use to introduce an opaque -return type. This proposal suggests the word `some`, since it has the right connotation by analogy to `Any` which is used to describe dynamically type-erased containers (and has been proposed to be a general way of referring to existential types)--a function that has an opaque return type returns *some* specific type -that conforms to the given constraints, whereas an existential can contain *any* type dynamically at any point in time. There are nonetheless reasonable objections to this keyword: +### Return type inference -- `some` is already used in the language as one of the case names for `Optional`; it is rare to need to spell `Optional.some` or `.some` explicitly, but it could nonetheless introduce confusion. -- In spoken language, `some type` and `sum type` sound the same. -- `swift some` could be a difficult term to search for. (However, on Google it currently gives [reasonable results](https://www.google.com/search?client=safari&rls=en&q=swift+some&ie=UTF-8&oe=UTF-8) about Optional in Swift.) - -Another obvious candidate is `opaque`, following the title of this very proposal. The word "opaque" is itself an overloaded term with existing meaning in many domains, which is unfortunate: +Part of the motivation of this feature is to avoid having to spell out elaborate return types. This proposal achieves that for types that can be abstracted behind protocols, but in doing so introduces complexity in the form of a new kind of "reverse generic" type. Meanwhile, there are kinds of verbose return types that can't be effectively hidden behind protocol interfaces, like deeply nested collections: ```swift -protocol Shape {} - -func translucentRectangle() -> opaque Shape { /* ... */ } +func jsonBlob() -> [String: [String: [[String: Any]]]] { ... } ``` -The term "opaque" is also fairly heavily overloaded in the Swift implementation (albeit not so much the source-level programming model). It may be that there is a better term, such as "abstract return types", to refer to this feature in its entirety. - -## Future Directions - -### Opaque types in structural position - -This proposal only allows for the entire return type of a declaration to be -made opaque. It would be reasonable to eventually generalize this to allow -for opaque types to appear structurally, as part of an optional, array, or -other generic type: +We could theoretically address the verbosity problem in its full generality and without introducing new type system features by allowing return types to be inferred, like C++14's or D's `auto` return types: +```swift +func jsonBlob() -> auto { ... } ``` -func collection(or not: Bool) -> (some Collection)? { - if not { return nil } - return [1, 2, 3] -} -``` - -Furthermore, there could conceivably be multiple opaque parts of a compound -return type: -``` -func twoCollections() -> (some Collection, some Collection) { - return ([1, 2, 3], ["one": 1, "two": 2, "three": 3]) -} -``` +Although this would superficially address the problem of verbose return types, that isn't really the primary problem this proposal is trying to solve, which is to allow for *more precise description of interfaces*. The case of a verbose composed generic adapter type is fundamentally different from a deeply nested collection; in the former case, the concrete type is not only verbose, but it's largely irrelevant, because clients should only care about the common protocols the type conforms to. For a nested collection, the verbose type *is* the interface: the full type is necessary to fully describe how someone interacts with that collection. -### `where` constraints on associated types of opaque types +Return type inference also has several undesirable traits as a language feature: -This proposal does not yet provide a syntax for specifying constraints on -associated types implied by an opaque type's protocol constraints. This is important for the feature to be useful with many standard library protocols such as `Collection`, since nearly every use case where someone wants to return `some Collection` would also want to specify the type of the `Element`s in that collection. The design of this feature is itself a fairly involved implementation effort and language design discussion, so it makes sense to split it out as a separate proposal. Normally in -Swift these constraints are expressed in `where` clauses; however, this is -not directly possible for opaque types because there is no way to name the -underlying type. This is similar to the spelling problem for [generalized existentials](https://github.com/austinzheng/swift-evolution/blob/az-existentials/proposals/XXXX-enhanced-existentials.md), and we'd want to find a syntax solution that works well for both. Possibilities include: +- It violates separate compilation of function bodies, since the body of the function must be + type-checked to infer the return type. Swift already has type inference for + stored property declarations, and we consider this a mistake, since it has + been an ongoing source of implementation complexity and performance problems + due to the need to type-check the property initializer across files. This is + not a problem for opaque return types, because callers only interface with the + declaration through the opaque type's constraints. Code can be compiled against a + function with an opaque return type without having to know what the + underlying type is; it is "just an optimization" to specialize away the + opaque type when the underlying type is known. +- Similarly, inferred return types wouldn't provide any semantic abstraction once the type is inferred. Code that calls the function would still see the full concrete type, allowing clients to rely on unintentional details of the concrete type, and the implementation would be bound by ABI and source compatibility constraints if it needed to change the return type. Module interfaces and documentation would also still expose the full return type, meaning they don't get the benefit of the shorter notation. -- Allowing a placeholder like `_` to refer to the unnamed opaque type in the where clause: +We see opaque return types as not only sugar for syntactically heavy return types, but also a tool for writing clearer, more resilient APIs. Return type inference would achieve the former but not the latter, while also introducing another compile-time performance footgun into the language. - ```swift - func foo() -> some Collection where _.Element == Int { ... } - ``` +### Syntax for opaque return types - Possible issues with this approach include: +This proposal suggests the word `some` to introduce opaque return types, since it has the right connotation by analogy to `Any` which is used to describe dynamically type-erased containers (and has been proposed to be a general way of referring to existential types)--a function that has an opaque return type returns *some* specific type +that conforms to the given constraints, whereas an existential can contain *any* type dynamically at any point in time. The spelling would also work well if [generalized to implicit generic arguments](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--directly-expressing-constraints) in the future. There are nonetheless reasonable objections to this keyword: - - The specification for the opaque type is no longer syntactically self-contained, being spread between the return type of the function and the `where` constraints, which is an implementation and readability challenge. - - This overloads the `_` token, something Swift has thus far managed to avoid for the most part (and a common complaint about Scala in particular). - - This only scales to one opaque/existential type. Future extensions of opaque result types could conceivably allow multiple opaque types to be in a declaration. +- `some` is already used in the language as one of the case names for `Optional`; it is rare to need to spell `Optional.some` or `.some` explicitly, but it could nonetheless introduce confusion. +- In spoken language, `some type` and `sum type` sound the same. +- `swift some` could be a difficult term to search for. (However, on Google it currently gives [reasonable results](https://www.google.com/search?client=safari&rls=en&q=swift+some&ie=UTF-8&oe=UTF-8) about Optional in Swift.) -- Adding a shorthand similar to Rust's `Trait` syntax to allow associated constraints to be spelled together with the core protocol type. With opaque types, this could look something like this: +Another obvious candidate is `opaque`, following the title of this very proposal. The word "opaque" is itself an overloaded term with existing meaning in many domains, which is unfortunate: - ```swift - // same type constraint - func foo() -> some Collection<.Element == Int> { ... } - // associated type protocol constraint - func foo() -> some Collection<.Element: SignedInteger> { ... } - ``` +```swift +protocol Shape {} - This could be a generally useful shorthand syntax for declaring a protocol constraint with associated type constraints, usable in `where` clauses, `some` clauses, and generalized existentials, at the risk of complicating the grammar and introducing More Than One Way To Do It for writing generic constraints. +func translucentRectangle() -> opaque Shape { /* ... */ } +``` -### Conditional conformances +The term "opaque" is also fairly heavily overloaded in the Swift implementation (albeit not so much the source-level programming model). It may be that there is a better term, such as "abstract return types", to refer to this feature in its entirety. `opaque` is also not as good a fit for generalization to implicit generic arguments. -When a generic function returns an adapter type, it's not uncommon for the adapter to use [conditional conformances](https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md) to reflect the capabilities of its underlying type parameters. For example, consider the `reversed()` operation: +In swift-evolution discussion, several other names came up, including: -```swift -extension BidirectionalCollection { - public func reversed() -> ReversedCollection { - return ReversedCollection(...) - } -} ``` +func translucentRectangle() -> unspecified Shape { ... } -`ReversedCollection` is always a `BidirectionalCollection`, with a conditional conformance to `RandomAccessCollection`: +func translucentRectangle() -> anyOld Shape { ... } -```swift -public struct ReversedCollection: BidirectionalCollection { - // ... -} +func translucentRectangle() -> hazyButSpecific Shape { ... } -extension ReversedCollection: RandomAccessCollection where C: RandomAccessCollection { - // ... -} -``` +func translucentRectangle() -> someSpecific Shape { ... } -What happens if we hid the `ReversedCollection` adapter type behind an opaque result type? +func translucentRectangle() -> someConcrete Shape { ... } -```swift -extension BidirectionalCollection { - public func reversed() -> some BidirectionalCollection<.Element == Element> { - return ReversedCollection(/* ... */) - } -} -``` +func translucentRectangle() -> nonspecific Shape { ... } -Now, clients that call `reversed()` on a `RandomAccessCollection` would get back something that is only known to be a `BidirectionalCollection`, and there would be no way to treat it as a `RandomAccessCollection`. The library could provide another overload of `reversed()`: +func translucentRectangle() -> unclear Shape { ... } -```swift -extension RandomAccessCollection { - public func reversed() -> some RandomAccessCollection<.Element == Element> { - return ReversedCollection(/* ... */) - } -} -``` +func translucentRectangle() -> arbitrary Shape { ... } -However, doing so is messy, and the client would have no way to know that the type returned by the two `reversed()` functions are, in fact, the same. To express the conditional conformance behavior, we could eventually extend the syntax of opaque result types to describe additional capabilities of the resulting type that depend on extended requirements. For example, we could state that the result of `reversed()` is *also* a `RandomAccessCollection` when `Self` is a `RandomAccessCollection`. One possible syntax: +func translucentRectangle() -> someThing Shape { ... } -```swift -extension BidirectionalCollection { - public func reversed() - -> some BidirectionalCollection<.Element == Element> - -> some RandomAccessCollection where Self: RandomAccessCollection - { - return ReversedCollection(/* ... */) - } -} -``` +func translucentRectangle() -> anonymized Shape { ... } -Here, we add a second return type and `where` clause that states additional information about the opaque result type (it is a `RandomAccessCollection`) as well as the requirements under which that capability is available (the `where Self: RandomAccessCollection`). One could have many conditional clauses, e.g., - -```swift -extension BidirectionalCollection { - public func reversed() - -> some BidirectionalCollection<.Element == Element> - -> some RandomAccessCollection where Self: RandomAccessCollection - -> some MutableCollection where Self: MutableCollection - { - return ReversedCollection(/* ... */) - } -} +func translucentRectangle() -> nameless Shape { ... } ``` -Here, the opaque result type conforms to `MutableCollection` when the `Self` type conforms to `MutableCollection`. This conditional result is independent of whether the opaque result type conforms to `RandomAccessCollection`. - -Note that Rust [didn't tackle the issue of conditional constraints](https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md#compatibility-with-conditional-trait-bounds) in their initial design of `impl Trait` types. +In our opinion, most of these are longer and not much clearer, and many wouldn't work well as generalized sugar for arguments and returns. ### Opaque type aliases -Opaque result types are tied to a specific declaration. They offer no way to state that two related APIs with opaque result types produce the *same* underlying concrete type. For example, the `LazyCompactMapCollection` type proposed in [SE-0222](https://github.com/apple/swift-evolution/blob/master/proposals/0222-lazy-compactmap-sequence.md) is used to describe four different-but-related APIs: lazy `compactMap`, `filter`, and `map` on various types. - -Opaque type aliases would allow us to provide a named type with stated capabilities, for which the underlying implementation type is still hidden from the API and ABI of clients like an opaque result type. For example: +As proposed, opaque result types are tied to a specific declaration. They offer no way to state that two related APIs with opaque result types produce the *same* underlying concrete type. The idea of "reverse generics" could however be decoupled from function declarations, if you could write an "opaque typealias" that describes the abstracted interface to a type, while giving it a name, then you could express that relationship across declarations. For example: ```swift public typealias LazyCompactMapCollection - : some Collection<.Element == ElementOfResult> + -> C where C.Element == ElementOfResult = LazyMapSequence>, ElementOfResult> ``` -In this strawman syntax, the opaque result type following the `:` is how clients see `LazyCompactMapCollection`. The underlying concrete type, spelled after the `=`, is visible only to the implementation (see below for more details). - -With this feature, multiple APIs could be described as returning a `LazyCompactMapCollection`: +In this strawman syntax, the "reverse generic" signature following the `->` is how clients see `LazyCompactMapCollection`. The underlying concrete type, spelled after the `=`, is visible only to the implementation (in some way that would have to be designed). With this feature, multiple APIs could be described as returning a `LazyCompactMapCollection`: ```swift extension LazyMapCollection { @@ -772,85 +596,29 @@ if Bool.random() { } ``` -The underlying concrete type of an opaque type alias would have restricted visibility, its access being the more restrictive of the access level below the type alias's access (e.g., `internal` for a `public` opaque type alias, `private` for an `internal` opaque type alias) and the access levels of any type mentioned in the underlying concrete type. For the opaque type alias `LazyCompactMapCollection` above, this would be the most restrictive of `internal` (one level below `public`) and the types involved in the underlying type (`LazyMapSequence`, `LazyFilterSequence`), all of which are public. Therefore, the access of the underlying concrete type is `internal`. - -If, instead, the concrete underlying type of `LazyCompactMapCollection` involved a private type, e.g., +This would be a great feature to explore as a future direction, since it has some important benefits relative to opaque result types. However, we don't think it's the best place to start with this feature. Compare a declaration using opaque typealiases like: ```swift -private struct LazyCompactMapCollectionImpl { - // ... -} +func foo() -> ReturnTypeOfFoo { return 1 } -public typealias LazyCompactMapCollection - : some Collection<.Element == ElementOfResult> - = LazyCompactMapCollectionImpl +opaque typealias ReturnTypeOfFoo -> P = Int ``` -then the access of the underlying concrete type would be `private`. - -The access of the underlying concrete type only affects the type checking of function bodies. If the function body has access to the underlying concrete type, then the opaque typealias and its underlying concrete type are considered to be equivalent. Extending the example above: +to one using opaque return types: ```swift -extension LazyMapCollection { - public func compactMap(_ transform: @escaping (Element) -> U?) -> LazyCompactMapCollection { - // ok so long as we are in the same file as the opaque type alias LazyCompactMapCollection, - // because LazyCompactMapCollectionImpl and - // LazyCompactMapCollection are known to be identical - return LazyCompactMapCollectionImpl(elements, transform) - } -} +func foo() -> some P { return 1 } ``` -Opaque type aliases might also offer an alternative solution to the issue with conditional conformance. Instead of annotating the `opaque` result type with all of the possible conditional conformances, we can change `reversed()` to return an opaque type alias `Reversed`, giving us a name for the resulting type: +The one using opaque typealiases requires an intermediate name, which one must read and follow to its definition to understand the interface of `foo`. The definition of `ReturnTypeOfFoo` also needs to spell out the underlying concrete return type of `foo`, and the two declarations are tightly coupled; a change to `foo` will likely require a lockstep change to `ReturnTypeOfFoo`. We expect that, in the common use case for this feature, the types being abstracted are going to be tied to specific declarations, and there wouldn't be any better name to really give than "return type of (decl)," so making opaque type aliases the only way of expressing return type abstraction would introduce a lot of obscuring boilerplate. -```swift -public typealias Reversed - : some BidirectionalCollection<.Element == Base.Element> - = ReversedCollection -``` - -Then, we can describe conditional conformances on `Reversed`: - -```swift -extension Reversed: RandomAccessCollection where Element == Base.Element { } -``` - -The conditional conformance must be satisfied by the underlying concrete type (here, `ReversedCollection`), and the extension must be empty: `Reversed` would be the same as `ReversedCollection` at runtime, so one could not add any API to `Reversed` beyond what `ReversedCollection` supports. - -### Opaque argument types - -The idea of opaque result types could be analogized to argument types, as an alternative shorthand to writing simple generic functions without explicit type arguments. [Rust RFC 1951](https://github.com/rust-lang/rfcs/blob/master/text/1951-expand-impl-trait.md) makes the argument for extending `impl Trait` to arguments in Rust. In Swift, this would mean that: - -```swift -func foo(x: some P) { /* ... */ } -``` - -would be sugar for: - -```swift -func foo(x: T) { /* ... */ } -``` - -And a more involved example like this: - -```swift -func concatenate( - _ x: C, - _ y: D -) -> some Collection<.Element == C.Element> where C.Element == D.Element { - return LazyConcat(x, y) -} -``` +## Future Directions -could be simplified to: +As noted in the introduction, this proposal is the first part of a group of changes we're considering in a [design document for improving the UI of the generics model](https://forums.swift.org/t/improving-the-ui-of-generics/22814). That design document lays out a number of related directions we can go based on the foundation establised by this proposal, including: -```swift -func concatenate( - _ x: some Collection<.Element == E>, - _ y: some Collection<.Element == E> -) -> some Collection<.Element == E> { - return LazyConcat(x, y) -} -``` +- allowing [fully generalized reverse generics](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--reverse-generics) +- [generalizing the `some` syntax](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--directly-expressing-constraints) as shorthand for generic arguments, and allowing structural use in generic returns +- [more compact constraint syntax](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--directly-expressing-constraints) that can also work with generalized existentials +- introducing [`any` as a dual to `some`](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--clarifying-existentials) for explicitly spelling existential types -Unlike opaque return types, this doesn't add any additional expressivity to the language, but it can make the declarations of many generic functions look simpler and more streamlined, reducing the "angle bracket blindness" caused by the syntactic overhead and indirection that traditional generic argument declaration syntax imposes. +We recommend reading that document for a more in-depth exploration of these related ideas. From b045f1e4facb926a6a188629160dd34d928ceaeb Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 11 Apr 2019 09:16:52 -0700 Subject: [PATCH 1119/4563] Fix typo --- proposals/0244-opaque-result-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index ae9d653300..f52bb5d79d 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -83,7 +83,7 @@ struct EightPointedStar: GameObject { } ``` -to declare that an `EightPointedStar` has some `Shape` without having to specify exactly what shape that is. The underlying concrete type is hidden, and can even change from one version of the library to the next without breaking those clients, because the underlying type identity is never exposed to clients. Unlike an existential, though, clients still have access to the type identity, so values This allows the library to provide a potentially-more-efficient design that leverages Swift's type system, without expanding the surface area of the library or making implementors of the library's protocols rely on exposing verbose implementation types. +to declare that an `EightPointedStar` has some `Shape` without having to specify exactly what shape that is. The underlying concrete type is hidden, and can even change from one version of the library to the next without breaking those clients, because the underlying type identity is never exposed to clients. Unlike an existential, though, clients still have access to the type identity. This allows the library to provide a potentially-more-efficient design that leverages Swift's type system, without expanding the surface area of the library or making implementors of the library's protocols rely on exposing verbose implementation types. An opaque type behaves like a ["reverse generic"](https://forums.swift.org/t/reverse-generics-and-opaque-result-types/21608). In a traditional generic function, the caller decides what types get bound to the callee's generic arguments: From f8bf17d928e6132339d9ad409690f0904903de4c Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 11 Apr 2019 12:04:32 -0700 Subject: [PATCH 1120/4563] SE-244: Clarify that associated type inference doesn't work with generic requirements --- proposals/0244-opaque-result-types.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index f52bb5d79d..d0fe2f0364 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -382,6 +382,24 @@ pos = Player().shape // ok: returns the same opaque result type Note that having a name for the opaque result type still doesn't give information about the underlying concrete type. For example, the only way to create an instance of the type `S.SomeType` is by calling `S.someValue()`. +Associated type inference can only infer an opaque result type for a non-generic requirement, because the opaque type is parameterized by the function's own generic arguments. For instance, in: + +```swift +protocol P { + associatedtype A: P + func foo(x: T) -> A +} + +struct Foo: P { + func foo(x: T) -> some P { + return x + } +} +``` + +there is no single underlying type to infer `A` to, because the return type +of `foo` is allowed to change with `T`. + ## Detailed design ### Grammar of opaque result types From 999997c35ddcfa2db08a7f6d513c43e42bcf278b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 11 Apr 2019 21:03:57 -0700 Subject: [PATCH 1121/4563] Fix semantic description of the memberwise initializer to account for SE-0242. Also s/RxSwift/RxCocoa. --- proposals/NNNN-property-delegates.md | 50 +++++++++++++++++----------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index c49f758190..b3fd410804 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -436,7 +436,7 @@ print(someInt) $someInt.deallocate() ``` -RxSwift's [`BehaviorRelay`](https://github.com/ReactiveX/RxSwift/blob/master/RxCocoa/Traits/BehaviorRelay.swift) replays the most recent value provided to it for each of the subscribed observers. It is created with an initial value, has `value` property to access the current value, as well as API to `subscribe` a new observer: (Thanks to Adrian Zubarev for pointing this out) +RxCocoa's [`BehaviorRelay`](https://github.com/ReactiveX/RxSwift/blob/master/RxCocoa/Traits/BehaviorRelay.swift) replays the most recent value provided to it for each of the subscribed observers. It is created with an initial value, has `value` property to access the current value, as well as API to `subscribe` a new observer: (Thanks to Adrian Zubarev for pointing this out) ```swift @BehaviorRelay @@ -648,37 +648,47 @@ By default, the synthesized storage property will have `internal` access or the Structs implicitly declare memberwise initializers based on the stored properties of the struct. With a property that has a delegate, the property is technically computed because it's the synthesized property -(of the delegate's type) that is stored. However, the delegate itself -might be an implementation detail that should not affect the form of -the memberwise initializer. - -The parameter type that is introduced into an implicit memberwise -initializer for a property with a delegate is determined as follows: - -* If the delegate type contains an `init(initialValue:)`, the - parameter type is the original type of the property. -* When the delegate type does not contain an `init(initialValue:)`, - the parameter type is the type of the synthesized storage property. - -For example: +(of the delegate's type) that is stored. Instance properties that have a +property delegate will have a corresponding parameter in the memberwise +initializer, whose type will either be the original property type or +the delegate type, depending on the delegate type and the initial value +(if provided). Specifically, the memberwise initializer parameter for +an instance property with a property delegate will have the original +property type if either of the following is true: + +* The corresponding property has an initial value specified with the +`=` syntax, e.g., `@Lazy var i = 17`, or +- The corresponding property has no initial value, but the property +delegate type has an `init(initialValue:)`. + +Otherwise, the memberwise initializer parameter will have the same +type as the delegate. For example: ```swift struct Foo { - @UnsafeMutable var x: Int + @UserDefault(key: "FOO_FEATURE_ENABLED", defaultValue: false) + static var x: Bool @Lazy var y: Int = 17 + @Lazy(closure: { getBool() }) var z: Bool + @CopyOnWrite var w: Image // implicit memberwise initializer: - init(x: UnsafeMutable, y: Int = 17) { + init(x: UserDefault = UserDefault(key: "FOO_FEATURE_ENABLED", defaultValue: false), + y: Int = 17, + z: Lazy = Lazy(closure: { getBool() }), + w: Image) { self.$x = x - self.$y = .init(initialValue: y) + self.$y = Lazy(initialValue: y) + self.$z = z + self.$w = CopyOnWrite(initialValue: w) } } ``` Synthesis for `Encodable`, `Decodable`, `Hashable`, and `Equatable` -follows the same rules, using the underlying `value` of the property -delegate type contains an `init(initialValue:)` and the synthesized -storage property's type otherwise. +using the underlying `value` of the property when the property +delegate type contains an `init(initialValue:)` and the delegate's +type otherwise. ### $ identifiers From 4ec6115dc1a44604802b7064d3b9f25d2aeab287 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 11 Apr 2019 21:27:55 -0700 Subject: [PATCH 1122/4563] Add Ref/Box and ThreadSpecific examples --- proposals/NNNN-property-delegates.md | 120 ++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index b3fd410804..a190d0d668 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -365,7 +365,55 @@ if initializedOnce == nil { print(initializedOnce) ``` -### `CopyOnWrite` +### Thread-specific storage + +Thread-specific storage (based on pthreads) can be implemented as a property delegate, too (example courtesy of Daniel Delwood): + +```swift +@propertyDelegate +final class ThreadSpecific { + private var key = pthread_key_t() + private let initialValue: T + + init(key: pthread_key_t, initialValue: T) { + self.key = key + self.initialValue = initialValue + } + + init(initialValue: T) { + self.initialValue = initialValue + pthread_key_create(&key) { + // 'Any' erasure due to inability to capture 'self' or + $0.assumingMemoryBound(to: Any.self).deinitialize(count: 1) + $0.deallocate() + } + } + + deinit { + fatalError("\(ThreadSpecific.self).deinit is unsafe and would leak") + } + + private var box: UnsafeMutablePointer { + if let pointer = pthread_getspecific(key) { + return pointer.assumingMemoryBound(to: Any.self) + } else { + let pointer = UnsafeMutablePointer.allocate(capacity: 1) + pthread_setspecific(key, UnsafeRawPointer(pointer)) + pointer.initialize(to: initialValue as Any) + return pointer + } + } + + var value: T { + get { return box.pointee as! T } + set (v) { + box.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee = v } + } + } +} +``` + +### Copy-on-write With some work, property delegates can provide copy-on-write wrappers (original example courtesy of Brent Royal-Gordon): @@ -408,6 +456,76 @@ let index = storage.index(of: …) $storage.append(…) ``` +### `Ref` / `Box` + +We can define a property delegate type `Ref` that is an abstracted reference +to some value that can be get/set, which is effectively a programmatic computed +property: + +```swift +@propertyDelegate +struct Ref { + let read: () -> Value + let write: (Value) -> Void + + var value: Value { + get { return read() } + nonmutating set { write(newValue) } + } + + subscript(dynamicMember keyPath: WritableKeyPath) -> Ref { + return Ref( + read: { self.value[keyPath: keyPath] }, + write: { self.value[keyPath: keyPath] = $0 }) + } +} +``` + +The subscript is using [SE-0252 "Key Path Member Lookup"](https://github.com/apple/swift-evolution/blob/master/proposals/0252-keypath-dynamic-member-lookup.md) so that a `Ref` instance provides access to the properties of its value. Building on the example from SE-0252: + +```swift +@Ref(read: ..., write: ...) +var rect: Rectangle + +print(rect) // accesses the Rectangle +print(rect.topLeft) // accesses the topLeft component of the rectangle + +let rect2 = $rect // get the Ref +let topLeft2 = $rect.topLeft // get a Ref referring to the Rectangle's topLeft +``` + +The `Ref` type encapsulates read/write, and making it a property delegate lets +us primarily see the underlying value. Often, one does not want to explicitly +write out the getters and setters, and it's fairly common to have a `Box` type that boxes up a value and can vend `Ref` instances referring into that box. We can do so with another property delegate: + +```swift +@propertyDelegate +class Box { + var value: Value + + init(initialValue: Value) { + self.value = initialValue + } + + var storageValue: Ref { + return Ref(read: { self.value }, write: { self.value = $0 }) + } +} +``` + +Now, we can define a new `Box` directly: + +```swift +@Box var rectangle: Rectangle = ... + +print(rectangle) // access the rectangle +print(rectangle.topLeft) // access the top left coordinate of the rectangle +let rect2 = $rectangle // through storageValue, produces a Ref +let topLeft2 = $rectangle.topLeft // through storageValue, produces a Ref +``` + +The use of `storageValue` hides the box from the client, providing direct access to the value in the box (the common case) as well as access to the box contents via Ref. + ### Property delegate types in the wild There are a number of existing types that already provide the basic structure of a property delegate type. One fun case is `Unsafe(Mutable)Pointer`, which we could augment to allow easy access to the pointed-to value: From e1a6eacdb6ecd998a3ef1f143d3e97e7798b7e77 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 11 Apr 2019 21:34:07 -0700 Subject: [PATCH 1123/4563] Add a future direction for composition --- proposals/NNNN-property-delegates.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index a190d0d668..8ecdf496bf 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -1008,6 +1008,7 @@ the prior proposal are: can use the `$`-prefixed name to refer to the storage property. These were future directions in the property behaviors proposal. + ## Future Directions ### Specifying access for the storage property @@ -1025,6 +1026,16 @@ public public(storage) var foo: Int = 1738 public private(storage) var bar: Int = 1738 ``` +### Composition of property delegates + +Only one property delegate can currently be attached to a given property, so code such as the following is in error: + +```swift +@UnsafeMutablePointer @Atomic var x: Int // error: two attached property delegates +``` + +The [property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md#composing-behaviors) describes some of the concerns surrounding composing behaviors/delegates in this way, which would need to be addressed by a future proposal. Matthew Johnson [sketched out a potential direction](https://forums.swift.org/t/pitch-property-delegates/21895/103) for extending property delegates to support composition of property delegates. + ### Referencing the enclosing 'self' in a delegate type Manually-written getters and setters for properties declared in a type often refer to the `self` of their enclosing type. For example, this can be used to notify clients of a change to a property's value: From 58880f22f4aa0458576024d427de9f4c72525cf3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 11 Apr 2019 23:34:46 -0700 Subject: [PATCH 1124/4563] Clarify storageValue somewhat --- proposals/NNNN-property-delegates.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 8ecdf496bf..39de473f11 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -851,9 +851,8 @@ A property with a delegate can declare accessors explicitly (`get`, `set`, `didS ### Delegating access to the storage property -A property delegate type delegate access to the storage property (`$foo`) by -providing a property named `storageValue`. As with the `value` property and (also optional) `init(initialValue:)`, the `storageValue` property must have the -same access level as its property delegate type. When present, `storageValue` is used to delegate accesses to the synthesized storage property. For example: +A property delegate type can choose to hide its instance entirely by providing a property named `storageValue`. As with the `value` property and`init(initialValue:)`, the `storageValue` property must have the +same access level as its property delegate type. When present, the synthesized storage property is hidden completely and the property `$foo` becomes a computed property accessing the storage property's `storageValue`. For example: ```swift class StorageManager { From fd9221ebdcf4d61248e74b1ab6cb5d392e6d4854 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 12 Apr 2019 11:59:49 +0100 Subject: [PATCH 1125/4563] [SE-0254] Update status to implemented (Swift 5.1) --- proposals/0254-static-subscripts.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0254-static-subscripts.md b/proposals/0254-static-subscripts.md index bfc20f7aeb..e9e133c13b 100644 --- a/proposals/0254-static-subscripts.md +++ b/proposals/0254-static-subscripts.md @@ -1,10 +1,10 @@ # Static and class subscripts * Proposal: [SE-0254](0254-static-subscripts.md) -* Authors: [Brent Royal-Gordon](https://github.com/brentdax) +* Author: [Brent Royal-Gordon](https://github.com/brentdax) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** -* Implementation: [apple/swift#23358](https://github.com/apple/swift/pull/23358), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/264//artifact/branch-master/swift-PR-23358-264-osx.tar.gz), [Ubuntu Linux 16.04 toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/205//artifact/branch-master/swift-PR-23358-205-ubuntu16.04.tar.gz) +* Status: **Implemented (Swift 5.1)** +* Implementation: [apple/swift#23358](https://github.com/apple/swift/pull/23358) * Review: ([review](https://forums.swift.org/t/se-0254-static-and-class-subscripts/22537), [acceptance](https://forums.swift.org/t/accepted-se-0254-static-and-class-subscripts/22941)) ## Introduction From 315548f61ba7ca89fc0a8cc4a5145b72b0c78bfa Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Fri, 12 Apr 2019 10:52:48 -0700 Subject: [PATCH 1126/4563] First go with @frozen naming --- proposals/NNNN-library-evolution.md | 374 ++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 proposals/NNNN-library-evolution.md diff --git a/proposals/NNNN-library-evolution.md b/proposals/NNNN-library-evolution.md new file mode 100644 index 0000000000..8a270d0b8c --- /dev/null +++ b/proposals/NNNN-library-evolution.md @@ -0,0 +1,374 @@ +# Library Evolution for Stable ABIs + +* Proposal: [SE-NNNN](NNNN-non-exhaustive-enums.md) +* Authors: [Jordan Rose](https://github.com/jrose-apple) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: Implemented in Swift 5 for standard library use +* Pre-review discussion: (TODO) + + + +## Introduction + +One of Swift's goals is to be a good language for libraries with binary compatibility concerns, such as those shipped as part of Apple's OSs. This includes giving library authors the flexibility to add to their public interface, and to change implementation details, without breaking binary compatibility. At the same time, it's important that library authors be able to opt out of this flexibility in favor of performance. + +This proposal introduces: + +- a "library evolution" build mode for libraries that are declaring ABI stability, which preserves the ability to make certain changes to types without breaking the library's ABI; and +- an attribute for such libraries to opt out of this flexibility on a per-type basis, allowing certain compile-time optimizations. + +The mechanisms for this are already in place, and were used to stabilize the +ABI of the standard library. This proposal makes them features for use by +any 3rd-party library that wishes to declare itself ABI stable. + +**This build mode will have no impact on libraries built and distributed with an app**. Such libraries will receive the same optimization that they have in previous versions of Swift. + +_Note: this proposal will use the word "field" to mean "stored instance property"._ + +## Motivation + +Starting in Swift 5, libraries are able to declare a stable ABI, allowing a library binary to be replaced with a newer version without requiring client programs to be recompiled. + +What consitutes the ABI of a library differs from language to language. In C and C++, the public ABI for a library includes information that ideally would be kept purely as an implementation detail. For example, the _size_ of a struct is fixed as part of the ABI, and is known to the library user at compile time. This prevents adding new fields to that type in later releases once the ABI is declared stable. If direct access to fields is allowed, the _layout_ of the struct can also become part of the ABI, so fields cannot then be reordered. + +This often leads to manual workarounds. A common technique is to have the struct hold only a pointer to an "impl" type, which holds the actual stored properties. All access to these properties is made via function calls, which can be updated to handle changes to the layout of the "impl" type. This has some obvious downsides: + +- it introduces indirection and function call overhead when accessing fields; +- it means the fields of the struct must be stored on the heap even when they could otherwise be stored on the stack, with that heap allocation needing to be managed via reference counting or some other mechanism; +- it means that the fields of the struct data are not stored in contiguous memory when placed in an array; and +- the implementation of all this must be provided by the library author. + +A similar challenge occurs with enums. As discussed in [SE-0192][], introducing new cases to an enum can break source compatability. There are also ABI consequences to this: adding new enum cases sometimes means increasing the storage needed for the enum, which can affect the size and layout of an enum in the same way adding fields to a struct can. + +The goal of this proposal is to reduce the burden on library developers by building into the compiler an automatic mechanism to reserve the flexibility to alter the internal representation of structs and enums, without manual workarounds. This mechanism can be implemented in such a way that optimizations such as stack allocation or contiguous inline storage of structs can still happen, while leaving the size of the type to be determined at runtime. + + [SE-0192]: https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md + +## Proposed solution + +The compiler will gain a new mode, called "library evolution mode". This mode will be off by default. A library compiled with this mode enabled is said to be _ABI-stable_. + +When a library is compiled with library evolution mode enabled, it is not an ABI-breaking change to modify the fields in a struct (to add, remove, or reorder them), and to add new enum cases (including with associated values). This implies that clients must manipulate fields and enum cases indirectly, via non-inlable function calls. Information such as the size of the type, and the layout of its fields, becomes something that can only be determined at runtime. Types that reserve this flexibility are referred to as "resilient types". + +A new `@frozen` keyword will be introduced to allow library authors to opt out of this flexibility on a per-type basis. This keyword promises that stored instance properties within the struct will not be *added,* *removed,* or *reordered*. The compiler will use this for optimization purposes when compiling clients of the struct. The precise set of allowed changes is defined below. + +This attribute has no effect when binary stability mode is off. In that case, the compiler acts as if *every* struct or enum in the library being compiled is "frozen". + +## Detailed design + +### Library evolution mode + +A new command-line argument, `-enable-library-evolution`, will enable this new mode. + +Turning on library evolution mode will have the following affects on source code: + +- The default behavior for enums will change to be non-frozen. This is the only change visible to _users_ of the library, who will need to use the `@unknown default:` technique described in SE-0192. +- To ensure that all fields are initialized, inlinable `init`s of the type must delegate to a non-inlinable `init`. + +### `@frozen` on `struct` types + +When a library author is certain that there will never be a need to add fields to a struct in future, they may mark that type as `@frozen`. + +This will allow the compiler to optimize away at compile time some calls that would otherwise need to be made at runtime (for example, it might access ). + +When compiling with binary stability mode on, a struct can be marked `@frozen` as long as it meets all of the following conditions: + +- The struct is *ABI-public* (see [SE-0193][]), i.e. `public` or marked `@abiPublic`. +- Every class, enum, struct, protocol, or typealias mentioned in the types of the struct's fields is ABI-public. +- No fields have observing accessors (`willSet` or `didSet`). +- If a field has an initial value, the expression computing the initial value does not reference any types or functions that are not ABI-public. + +```swift +@frozen +public struct Point { + public var x: Double + public var y: Double + + public init(_ x: Double, _ y: Double) { + self.x = x + self.y = y + } +} +``` + +This affects what changes to the struct's fields affect the ABI of the containing library: + +| Change | Normal struct | `@frozen` struct +|---|:---:|:---: +| Adding fields | Allowed | **Affects ABI** +| Reordering fields | Allowed | **Affects ABI** +| Removing ABI-public fields | Affects ABI | Affects ABI +| Removing non-ABI-public fields | Allowed | **Affects ABI** +| Changing the type of an ABI-public field | Affects ABI | Affects ABI +| Changing the type of a non-ABI-public field | Allowed | **Affects ABI** +| Changing a stored instance property to computed | Allowed | **Affects ABI** +| Changing a computed instance property to stored | Allowed | **Affects ABI** +| Changing the access of a non-ABI-public field | Allowed | Allowed +| Marking an `internal` field as `@abiPublic` | Allowed | Allowed +| Changing an `internal` ABI-public field to be `public` | Allowed | Allowed + + +> Note: This proposal is implemented already and in use by the standard library, albeit under different names. The command-line flag is `-enable-resilience`; the attribute is `@_fixed_layout` for structs, and `@frozen` for enums. + + [SE-0193]: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md + +#### Guarantees + +Marking a struct `@frozen` only guarantees that its stored instance properties won't change. This allows the compiler to perform certain optimizations, like ignoring properties that are never accessed, or eliminating redundant loads from the same instance property. However, it does not provide a handful of other guarantees that a C struct might: + +- **It is not guaranteed to be "trivial"** ([in the C++ sense][trivial]). A frozen struct containing a class reference or closure still requires reference-counting when copied and when it goes out of scope. + +- **It is not guaranteed to be stored in registers.** Promoting structs to be stored in registers is considered an optimization in Swift, and as such the compiler is never *required* to do so. In particular, a struct with a `weak` reference in it must always be stored in memory if the compiler cannot prove that the weak reference is unused. + +- **It does not necessarily have a known size or alignment.** A generic frozen struct's layout might depend on the generic argument provided at run time. + +- **Even concrete instantiations may not have a known size or alignment.** A frozen struct with a field that's a *non-*frozen, has a size that may not be known until run time. + +- **It is not guaranteed to use the same layout as a C struct with a similar "shape".** If such a struct is necessary, it should be defined in a C header and imported into Swift. + +- **The fields are not guaranteed to be laid out in declaration order.** The compiler may choose to reorder fields to minimize padding while satisfying alignment requirements, for example, . + +That said, the compiler is allowed to use its knowledge of the struct's contents and layout to derive any of these properties. For instance, the compiler can statically prove that copying `Point` is "trivial", because each of its members has a statically-known type that is "trivial". However, depending on this at the language level is not supported, with two exceptions: + +1. The run-time memory layout of a struct with a single field is always identical to the layout of the instance property on its own, whether the struct is declared `@frozen` or not. This has been true since Swift 1. (This does not extend to the calling convention, however.) + +2. The representation of `nil` for any type that is "nullable" in C / Objective-C is the same as the representation of `nil` or `NULL` in those languages. This includes class references, class-bound protocol references, class type references, unsafe-pointers, `@convention(c)` functions, `@convention(block)` functions, OpaquePointer, Selector, and NSZone. This has been true since Swift 3 ([SE-0055][]). + +This proposal does not change either of these guarantees. + + [trivial]: https://docs.microsoft.com/en-us/cpp/cpp/trivial-standard-layout-and-pod-types + [SE-0055]: https://github.com/apple/swift-evolution/blob/master/proposals/0055-optional-unsafe-pointers.md + +### `@frozen` on `enum` types + +Marking an enum as `@frozen` will also allow the compiler to optimize away runtime calls. + +In addition, marking an enum as frozen restores the ability of a library user to exhaustively switch over that enum without an `@unknown default:`, because it guarantees no further cases will be added. + + +## Naming + +`@frozen` was used here, as originally used in [SE-0192][], for both fixed-layout structs and enums. + +- While they share a fixing of their layout going forward, declaring an enum as frozen has additional meaning to _users_ of the library, whereas a frozen struct is an implementation detail of the library. + +- It would be reasonable to use `@frozen` to describe an enum without associated values that promises not to *add* any cases with associated values. This would allow for similar optimization as `@frozen` on a struct, while still not declaring that the enum is frozen. + +- Since this feature is only relevant for libraries with binary compatibility concerns, it may be more useful to tie its syntax to `@available` from the start, as with [SE-0193][]. In that case neither `@frozen` nor `@frozen` would be appropriate. + +But that said, `@frozen` still has the right *connotations* to describe a type whose stored instance properties or cases can no longer be changed, and that is *similar* to what `@frozen` does for enums even if the consequences are a little different. So the final candidates for names are: + +- `@frozen`, to match [SE-0192][] +- `@fixedContents`, or something else specific to structs +- `@available(*, frozen)`, to leave space for e.g. "frozen as of dishwasherOS 5" + +Other names are discussed in "Alternatives considered" + +## Comparison with other languages + +### C + +C structs have a very simple layout algorithm, which guarantees that fields are laid out in source order with adjustments to satisfy alignment restrictions. C has no formal notion of non-public fields; if used in an ABI-stable interface, the total size and alignment of a struct and its fields must not change. On the other hand, because the layout algorithm is so well-known and all fields are "trivial", C struct authors can often get away with adding one or more "reserved" fields to the end of their struct, and then renaming and possibly splitting those fields later to get the effect of adding new fields. A fixed-contents struct in Swift can get the same effect with a private field that backs public computed properties. + +When a C library author wants to reserve the right to *arbitrarily* change the layout of a struct, they will usually not provide a definition for the struct at all. Instead, they will forward-declare the type and vend pointers to instances of the struct, usually allocated on the heap. Clients have to use "getter" and "setter" functions to access the fields of the struct. Non-fixed-contents Swift structs work in much the same way, except that the compiler can be a little more efficient about it. + +Finally, C has an interesting notion of a *partially* fixed-contents struct. If a developer knows that two C structs start with the same fields and have the same alignment, they can reinterpret a pointer to one struct as a pointer to the other, as long as they only access those shared fields. This can be used to get fast access to some fields while going through indirect "getter" functions for others. Swift does not have any equivalent feature at this time. + + +### C++ + +C++ shares many of the same features as C, except that C++ does not guarantee that all fields are laid out in order unless they all have the same access control. C++ access control does not otherwise affect the ABI of a type, at least not with regards to layout; if a private field is removed from a struct (or class), the layout changes. + +C++ structs may also contain non-trivial fields. Rather than client code being able to copy them element-by-element, C++ generates a *copy constructor* that must be called whenever a struct value is copied (including when it is passed to a function). Copy constructors may also be written explicitly, in which case they may run arbitrary code; this affects what optimizations a C++ compiler can do. However, if the compiler can *see* that the copy constructor has not been customized, it may be able to optimize copies similarly to Swift. + +(This is overly simplified, but sufficient for this comparison.) + + +### Rust + +Rust likewise has a custom layout algorithm for the fields of its structs. While Rust has not put too much effort into having a stable ABI (other than interoperation with C), their current design behaves a lot like C's in practice: layout is known at compile-time, and changing a struct's fields will change the ABI. + +(there is an interesting post from last year about [optimizing the layout of Rust structs, tuples, and enums][rust].) + +Like C++ (and Swift), the fields of a Rust struct may be non-trivial to copy; unlike C++ (or Swift), Rust simply does not provide an implicit copy operation if that is the case. In fact, in order to preserve source compatibility, the author of the struct must *promise* that the type will *always* be trivial to copy by implementing a particular trait (protocol), if they want to allow clients to copy the struct through simple assignment. + +Rust also allows explicitly opting into C's layout algorithm. We could add that to Swift too if we wanted; for now we continue encouraging people to define such structs in C instead. + +[rust]: http://camlorn.net/posts/April%202017/rust-struct-field-reordering.html + + +### VM/JIT/Interpreted languages + +Languages where part of the compilation happens at run time are largely immune to these issues. In theory, the layout of a "struct" can be changed at any time, and any client code will automatically be updated to deal with that, because the runtime system will know either to handle the struct indirectly, or to emit fresh JITed code that can dynamically access the struct directly. + +### Objective-C + +Objective-C uses C structs for its equivalent of fixed-contents structs, but *non-*fixed-contents structs are instead represented using classes, often immutable classes. (An immutable class sidesteps any questions about value semantics as long as you don't test its identity, e.g. with `===`.) The presence of these classes in Apple's frameworks demonstrates the need for non-fixed-contents structs in Swift. + +Old versions of Objective-C (as in, older than iOS) had the same restrictions for the instance variables of a class as C does for the fields of a struct: any change would result in a change in layout, which would break any code that accessed instance variables directly. This was largely acceptable for classes that didn't need to be subclassed, but those that did had to keep their set of instance variables fixed. (This led to the same sort of "reserved" tricks used by C structs.) This restriction was lifted at the cost of some first-use set-up time with Apple's "non-fragile Objective-C runtime", introduced with iOS and with the 64-bit version of Mac OS X; it is now also supported in the open-source GNUstep runtime. + + +### Other languages + +Many other languages allow defining structs, but most of them either don't bother to define a stable ABI or don't allow modifying the fields of a struct once it becomes part of an ABI. Haskell and Ocaml fall into this bucket. + + +## Source compatibility + +This change does not impact source compatibility. It has always been a source-compatible change to modify a struct's non-public stored instance properties, and to add a new public stored instance property, as long as the struct's existing API does not change (including any initializers). + + +## Effect on ABI stability + +Currently, the layout of a public struct is known at compile time in both the defining library and in its clients. For a library concerned about binary compatibility, the layout of a non-fixed-contents struct must not be exposed to clients, since the library may choose to add new stored instance properties that do not fit in that layout in its next release, or even remove existing properties as long as they are not public. + +These considerations should not affect libraries shipped with their clients, including SwiftPM packages. These libraries should always have binary stability mode turned off, indicating that the compiler is free to optimize based on the layout of a struct (because the library won't change). + + +## Effect on Library Evolution + +The set of binary-compatible changes to a struct's stored instance properties is described above. + +Taking an existing struct and marking it `@frozen` is something we'd like to support without breaking binary compatibility, but there is no design for that yet. + +Removing `@frozen` from a struct is not allowed; this would break any existing clients that rely on the struct's existing layout. + + +### Breaking the contract + +Because the compiler uses the set of fields in a fixed-contents struct to determine its in-memory representation and calling convention, adding a new field or removing `@frozen` from a struct in a library will result in "undefined behavior" from any client apps that have not been recompiled. This means a loss of memory-safety and type-safety on par with a misuse of "unsafe" types, which would most likely lead to crashes but could lead to code unexpectedly being executed or skipped. In short, things would be very bad. + +Some ideas for how to prevent library authors from breaking the rules accidentally are discussed in "Compatibility checking" under "Future directions". + + +## Future directions + +### Compatibility checking + +Of course, the compiler can't stop a library author from modifying the fields of a fixed-contents struct, even though that will break binary compatibility. We already have two ideas on how we could catch mistakes of this nature: + +- A checker that can compare APIs across library versions, using swiftmodule files or similar. + +- Encoding the layout of a type in a symbol name. Clients could link against this symbol so that they'd fail to launch if it changes, but even without that an automated system could check the list of exported symbols to make sure nothing was removed. + +This is the same kind of checking described in [SE-0192][] "Non-exhaustive Enums". + + [SE-0192]: https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md + + +### Allowing observing accessors + +The limitation on observing accessors for stored instance properties is an artificial one; clients could certainly access the field directly for "read" accesses but go through a setter method for "write" accesses. However, the expected use for `@frozen` is for structs that need to be as efficient as C structs, and that includes being able to do direct stores into public instance properties. The effect of observing accessors can be emulated in a fixed-contents struct with a private stored property and a public computed property. + +If we wanted to add this later, the way we would probably implement it is by saying that accessors for stored instance properties of `@frozen` structs must be marked `@inlinable`, with all of the implications that has for being able to make changes in the future. + + +## Alternatives considered + +### Annotation syntax + +#### Naming + +`@frozen` comes from [SE-0192][], where it was originally used as a term for for enums that will not gain new cases. + +Other than `@frozen`, most names have revolved around the notion of "fixed" somehow: `@fixedContents`, `@fixedLayout`, or even just `@fixed`. We ultimately chose `@frozen` as there is a strong similarity between this feature for both enums and structs. + +`final` was suggested at one point, but the meaning is a little far from that of `final` on classes. + +#### Modifier or attribute? + +This proposal suggests a new *attribute* for structs, `@fixedContents`; it could also be a modifier `fixedContents` (or `fixed`), implemented as a context-sensitive keyword. Because this annotation only affects the struct's *implementation* rather than anything about its use, an attribute seemed more appropriate. + + +### Command-line flag naming + +As mentioned above, the current spelling of the `-enable-library-evolution` flag is `-enable-resilience`. The term "resilience" has been an umbrella name for the Swift project's efforts to support evolving an API without breaking binary compatibility, but it isn't well-known outside of the Swift world. It's better to have this flag be more self-explanatory. + +Alternate bikeshed colors: + +- `-enable-abi-stability` +- `-enable-stable-abi` +- just `-stable-abi` +- (your suggestion here) + +In practice, the precise name of this flag won't be very important, because (1) the number of people outside of Apple making libraries with binary stability concerns won't be very high, and (2) most people will be building those libraries through Xcode or SwiftPM, which will have its own name for this mode. But naming the *feature* is still important. + +### "No reordering" vs. "No renaming" + +As written, this proposal disallows *reordering* field of a fixed-contents struct. This matches the restrictions of C structs, but is different from everything else in Swift, which freely allows reordering. Why? + +Let's step back and remember our goal: a stable, unchanging ABI for a fixed-contents struct. That means that whatever changes are made to the struct, the in-memory representation and the rest of the ABI must be consistent across library versions. We can think of this as a function that takes fields and produces a layout: + +```swift +struct Field { + var name: String + var type: ValueType +} +typealias Offset = Int +func layoutForFixedContentsStruct(fields: [Field]) -> [Offset: Field] +``` + +To make things simple, let's assume all the fields in the struct have the same type, like this PolarPoint: + +```swift +@fixedContents public struct PolarPoint { + // Yes, these have different access levels. + // You probably wouldn't do that in real life, + // but it's needed for this contrived example. + public var radius: Double + private var angle: Double + + public init(x: Double, y: Double) { + self.radius = sqrt(x * x, y * y) + self.angle = atan2(y, x) + } + + public var x: Double { + return radius * cos(angle) + } + public var y: Double { + return radius * sin(angle) + } +} +``` + +We have two choices for how to lay out the PolarPoint struct: + +- `[0: radius, 8: angle]` +- `[0: angle, 8: radius]` + +*It's important that everyone using the struct agrees on which layout we're using.* Otherwise, an angle could get misinterpreted as a radius when someone outside the module tries to access the `radius` property. + +So how do we decide? We have three choices: + +1. Sort the fields by declaration order. +2. Sort the fields by name. +3. Do something clever with access levels. + +Which translates to three possible restrictions: + +1. Fields of a fixed-contents struct may not be reordered, even non-public ones. +2. Fields of a fixed-contents struct may not be renamed, even non-public ones. +3. Copying the struct requires calling a function because we can't see the private fields. + +(3) is a non-starter, since it defeats the performance motivation for `@fixedContents`. That leaves (1) and (2), and we decided it would be *really weird* if renaming the private property `angle` to `theta` changed the layout of the struct. So the restriction on reordering fields was considered the least bad solution. It helps that it matches the behavior of C, Rust, and other languages. + + +### Fixed-contents by default + +No major consideration was given towards making structs fixed-contents by default, with an annotation to make them "changeable". While this is the effective behavior of other languages, the library design philosophy in Swift is that changes that don't break source compatibility shouldn't break binary compatibility whenever possible. Furthermore, while this may be the behavior of other languages, it's also been a longstanding *complaint* in other languages like C. We want to make structs in ABI-stable libraries as easy to evolve over time as classes are in Objective-C, while still providing an opt-out for structs that have high performance requirements. + + +### Binary stability mode by default + +Rather than introduce a new compiler flag, Swift could just treat *all* library code as if it defined ABI-stable libraries. However, this could result in a noticeable performance hit when it isn't really warranted; if a library does *not* have binary-compatibility concerns (say, if it is distributed with the app that uses it), there is no benefit to handling structs and enums indirectly. In practice, it's likely we'd start seeing people put `@frozen` in their source packages. Keeping this as a flag means that the notion of a "frozen" type can be something that most users and even most library authors don't have to think about. + +(Note that we'd love to have this be true for inlinable functions too, but that's a lot trickier on the implementation side, which is why `@inlinable` was considered worth adding in [SE-0193][] despite the additional complexity.) From 93489e1da18a8283b3962a39297a7076c28a27c8 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Fri, 12 Apr 2019 18:17:19 -0700 Subject: [PATCH 1127/4563] Responding to Jordan's feedback --- proposals/NNNN-library-evolution.md | 56 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/proposals/NNNN-library-evolution.md b/proposals/NNNN-library-evolution.md index 8a270d0b8c..185e137316 100644 --- a/proposals/NNNN-library-evolution.md +++ b/proposals/NNNN-library-evolution.md @@ -1,7 +1,7 @@ # Library Evolution for Stable ABIs -* Proposal: [SE-NNNN](NNNN-non-exhaustive-enums.md) -* Authors: [Jordan Rose](https://github.com/jrose-apple) +* Proposal: [SE-NNNN](NNNN-library-evolution.md) +* Authors: [Jordan Rose](https://github.com/jrose-apple), [Ben Cohen](https://github.com/airspeedswift) * Review Manager: TBD * Status: **Awaiting review** * Implementation: Implemented in Swift 5 for standard library use @@ -35,7 +35,7 @@ _Note: this proposal will use the word "field" to mean "stored instance property ## Motivation -Starting in Swift 5, libraries are able to declare a stable ABI, allowing a library binary to be replaced with a newer version without requiring client programs to be recompiled. +As of Swift 5, libraries are able to declare a stable ABI, allowing a library binary to be replaced with a newer version without requiring client programs to be recompiled. What consitutes the ABI of a library differs from language to language. In C and C++, the public ABI for a library includes information that ideally would be kept purely as an implementation detail. For example, the _size_ of a struct is fixed as part of the ABI, and is known to the library user at compile time. This prevents adding new fields to that type in later releases once the ABI is declared stable. If direct access to fields is allowed, the _layout_ of the struct can also become part of the ABI, so fields cannot then be reordered. @@ -46,7 +46,7 @@ This often leads to manual workarounds. A common technique is to have the struct - it means that the fields of the struct data are not stored in contiguous memory when placed in an array; and - the implementation of all this must be provided by the library author. -A similar challenge occurs with enums. As discussed in [SE-0192][], introducing new cases to an enum can break source compatability. There are also ABI consequences to this: adding new enum cases sometimes means increasing the storage needed for the enum, which can affect the size and layout of an enum in the same way adding fields to a struct can. +A similar challenge occurs with Swift enums. As discussed in [SE-0192][], introducing new cases to an enum can break source compatibility. There are also ABI consequences to this: adding new enum cases sometimes means increasing the storage needed for the enum, which can affect the size and layout of an enum in the same way adding fields to a struct can. The goal of this proposal is to reduce the burden on library developers by building into the compiler an automatic mechanism to reserve the flexibility to alter the internal representation of structs and enums, without manual workarounds. This mechanism can be implemented in such a way that optimizations such as stack allocation or contiguous inline storage of structs can still happen, while leaving the size of the type to be determined at runtime. @@ -56,11 +56,11 @@ The goal of this proposal is to reduce the burden on library developers by build The compiler will gain a new mode, called "library evolution mode". This mode will be off by default. A library compiled with this mode enabled is said to be _ABI-stable_. -When a library is compiled with library evolution mode enabled, it is not an ABI-breaking change to modify the fields in a struct (to add, remove, or reorder them), and to add new enum cases (including with associated values). This implies that clients must manipulate fields and enum cases indirectly, via non-inlable function calls. Information such as the size of the type, and the layout of its fields, becomes something that can only be determined at runtime. Types that reserve this flexibility are referred to as "resilient types". +When a library is compiled with library evolution mode enabled, it is not an ABI-breaking change to modify the fields in a struct (to add, remove, or reorder them), and to add new enum cases (including with associated values). This implies that clients must manipulate fields and enum cases indirectly, via non-inlinable function calls. Information such as the size of the type, and the layout of its fields, becomes something that can only be determined at runtime. Types that reserve this flexibility are referred to as "resilient types". -A new `@frozen` keyword will be introduced to allow library authors to opt out of this flexibility on a per-type basis. This keyword promises that stored instance properties within the struct will not be *added,* *removed,* or *reordered*. The compiler will use this for optimization purposes when compiling clients of the struct. The precise set of allowed changes is defined below. +A new `@frozen` keyword will be introduced to allow library authors to opt out of this flexibility on a per-type basis. This keyword promises that stored instance properties within the struct will not be *added,* *removed,* or *reordered*, and that an enum will never *add,* *remove,* or *reorder* its cases (note removing, and sometimes reordering, cases can already be source breaking, not just ABI breaking). The compiler will use this for optimization purposes when compiling clients of the type. The precise set of allowed changes is defined below. -This attribute has no effect when binary stability mode is off. In that case, the compiler acts as if *every* struct or enum in the library being compiled is "frozen". +This attribute has no effect when library evolution mode is off. In that case, the compiler acts as if *every* struct or enum in the library being compiled is "frozen". ## Detailed design @@ -81,7 +81,7 @@ This will allow the compiler to optimize away at compile time some calls that wo When compiling with binary stability mode on, a struct can be marked `@frozen` as long as it meets all of the following conditions: -- The struct is *ABI-public* (see [SE-0193][]), i.e. `public` or marked `@abiPublic`. +- The struct is *ABI-public* (see [SE-0193][]), i.e. `public` or marked `@usableFromInline`. - Every class, enum, struct, protocol, or typealias mentioned in the types of the struct's fields is ABI-public. - No fields have observing accessors (`willSet` or `didSet`). - If a field has an initial value, the expression computing the initial value does not reference any types or functions that are not ABI-public. @@ -115,8 +115,7 @@ This affects what changes to the struct's fields affect the ABI of the containin | Marking an `internal` field as `@abiPublic` | Allowed | Allowed | Changing an `internal` ABI-public field to be `public` | Allowed | Allowed - -> Note: This proposal is implemented already and in use by the standard library, albeit under different names. The command-line flag is `-enable-resilience`; the attribute is `@_fixed_layout` for structs, and `@frozen` for enums. +> Note: This proposal is implemented already and in use by the standard library, albeit under different names. The command-line flag is `-enable-library-evolution`; the attribute is `@_fixed_layout` for structs, and `@_frozen` for enums. [SE-0193]: https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md @@ -126,15 +125,13 @@ Marking a struct `@frozen` only guarantees that its stored instance properties w - **It is not guaranteed to be "trivial"** ([in the C++ sense][trivial]). A frozen struct containing a class reference or closure still requires reference-counting when copied and when it goes out of scope. -- **It is not guaranteed to be stored in registers.** Promoting structs to be stored in registers is considered an optimization in Swift, and as such the compiler is never *required* to do so. In particular, a struct with a `weak` reference in it must always be stored in memory if the compiler cannot prove that the weak reference is unused. - - **It does not necessarily have a known size or alignment.** A generic frozen struct's layout might depend on the generic argument provided at run time. - **Even concrete instantiations may not have a known size or alignment.** A frozen struct with a field that's a *non-*frozen, has a size that may not be known until run time. - **It is not guaranteed to use the same layout as a C struct with a similar "shape".** If such a struct is necessary, it should be defined in a C header and imported into Swift. -- **The fields are not guaranteed to be laid out in declaration order.** The compiler may choose to reorder fields to minimize padding while satisfying alignment requirements, for example, . +- **The fields are not guaranteed to be laid out in declaration order.** The compiler may choose to reorder fields, for example to minimize padding while satisfying alignment requirements. That said, the compiler is allowed to use its knowledge of the struct's contents and layout to derive any of these properties. For instance, the compiler can statically prove that copying `Point` is "trivial", because each of its members has a statically-known type that is "trivial". However, depending on this at the language level is not supported, with two exceptions: @@ -149,10 +146,11 @@ This proposal does not change either of these guarantees. ### `@frozen` on `enum` types -Marking an enum as `@frozen` will also allow the compiler to optimize away runtime calls. +Marking an enum as `@frozen` will similarly allow the compiler to optimize away runtime calls. In addition, marking an enum as frozen restores the ability of a library user to exhaustively switch over that enum without an `@unknown default:`, because it guarantees no further cases will be added. +Once frozen, all changes made to an enum's cases affect its ABI. ## Naming @@ -162,12 +160,12 @@ In addition, marking an enum as frozen restores the ability of a library user to - It would be reasonable to use `@frozen` to describe an enum without associated values that promises not to *add* any cases with associated values. This would allow for similar optimization as `@frozen` on a struct, while still not declaring that the enum is frozen. -- Since this feature is only relevant for libraries with binary compatibility concerns, it may be more useful to tie its syntax to `@available` from the start, as with [SE-0193][]. In that case neither `@frozen` nor `@frozen` would be appropriate. +- Since this feature is only relevant for libraries with binary compatibility concerns, it may be more useful to tie its syntax to `@available` from the start, as with [SE-0193][]. -But that said, `@frozen` still has the right *connotations* to describe a type whose stored instance properties or cases can no longer be changed, and that is *similar* to what `@frozen` does for enums even if the consequences are a little different. So the final candidates for names are: +But that said, `@frozen` still has the right *connotations* to describe a type whose stored instance properties or cases can no longer be changed. So the final candidates for names are: -- `@frozen`, to match [SE-0192][] -- `@fixedContents`, or something else specific to structs +- `@frozen` for both, to match the term used in [SE-0192][] +- `@frozen` for enums but `@fixedContents`, or something else, specific to structs - `@available(*, frozen)`, to leave space for e.g. "frozen as of dishwasherOS 5" Other names are discussed in "Alternatives considered" @@ -196,7 +194,7 @@ C++ structs may also contain non-trivial fields. Rather than client code being a Rust likewise has a custom layout algorithm for the fields of its structs. While Rust has not put too much effort into having a stable ABI (other than interoperation with C), their current design behaves a lot like C's in practice: layout is known at compile-time, and changing a struct's fields will change the ABI. -(there is an interesting post from last year about [optimizing the layout of Rust structs, tuples, and enums][rust].) +(there is an interesting post a couple of years ago about [optimizing the layout of Rust structs, tuples, and enums][rust].) Like C++ (and Swift), the fields of a Rust struct may be non-trivial to copy; unlike C++ (or Swift), Rust simply does not provide an implicit copy operation if that is the case. In fact, in order to preserve source compatibility, the author of the struct must *promise* that the type will *always* be trivial to copy by implementing a particular trait (protocol), if they want to allow clients to copy the struct through simple assignment. @@ -230,21 +228,22 @@ This change does not impact source compatibility. It has always been a source-co Currently, the layout of a public struct is known at compile time in both the defining library and in its clients. For a library concerned about binary compatibility, the layout of a non-fixed-contents struct must not be exposed to clients, since the library may choose to add new stored instance properties that do not fit in that layout in its next release, or even remove existing properties as long as they are not public. -These considerations should not affect libraries shipped with their clients, including SwiftPM packages. These libraries should always have binary stability mode turned off, indicating that the compiler is free to optimize based on the layout of a struct (because the library won't change). +These considerations should not affect libraries shipped with their clients, including SwiftPM packages. These libraries should always have library evolution mode turned off, indicating that the compiler is free to optimize based on the layout of a type (because the library won't change). ## Effect on Library Evolution -The set of binary-compatible changes to a struct's stored instance properties is described above. +Both structs and enums can gain new protocol conformances or methods, even when `@frozen`. Binary compatability only affects additions of fields or cases. -Taking an existing struct and marking it `@frozen` is something we'd like to support without breaking binary compatibility, but there is no design for that yet. +The set of binary-compatible changes to a struct's stored instance properties is described above. There are no binary-compatible changes to an enum's cases. -Removing `@frozen` from a struct is not allowed; this would break any existing clients that rely on the struct's existing layout. +Taking an existing struct or enum and marking it `@frozen` is something we'd like to support without breaking binary compatibility, but there is no design for that yet. +Removing `@frozen` from a type is not allowed; this would break any existing clients that rely on the existing layout. ### Breaking the contract -Because the compiler uses the set of fields in a fixed-contents struct to determine its in-memory representation and calling convention, adding a new field or removing `@frozen` from a struct in a library will result in "undefined behavior" from any client apps that have not been recompiled. This means a loss of memory-safety and type-safety on par with a misuse of "unsafe" types, which would most likely lead to crashes but could lead to code unexpectedly being executed or skipped. In short, things would be very bad. +Because the compiler uses the set of fields in a fixed-contents struct to determine its in-memory representation and calling convention, adding a new field or removing `@frozen` from a type in a library will result in "undefined behavior" from any client apps that have not been recompiled. This means a loss of memory-safety and type-safety on par with a misuse of "unsafe" types, which would most likely lead to crashes but could lead to code unexpectedly being executed or skipped. In short, things would be very bad. Some ideas for how to prevent library authors from breaking the rules accidentally are discussed in "Compatibility checking" under "Future directions". @@ -259,10 +258,6 @@ Of course, the compiler can't stop a library author from modifying the fields of - Encoding the layout of a type in a symbol name. Clients could link against this symbol so that they'd fail to launch if it changes, but even without that an automated system could check the list of exported symbols to make sure nothing was removed. -This is the same kind of checking described in [SE-0192][] "Non-exhaustive Enums". - - [SE-0192]: https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md - ### Allowing observing accessors @@ -290,14 +285,15 @@ This proposal suggests a new *attribute* for structs, `@fixedContents`; it could ### Command-line flag naming -As mentioned above, the current spelling of the `-enable-library-evolution` flag is `-enable-resilience`. The term "resilience" has been an umbrella name for the Swift project's efforts to support evolving an API without breaking binary compatibility, but it isn't well-known outside of the Swift world. It's better to have this flag be more self-explanatory. +As mentioned above, the current spelling of the flag`-enable-library-evolution`. + +The term "resilience" has been an umbrella name for the Swift project's efforts to support evolving an API without breaking binary compatibility, so for some time the flag was flag `-enable-resilience`. But it isn't well-known outside of the Swift world. It's better to have this flag be more self-explanatory. Alternate bikeshed colors: - `-enable-abi-stability` - `-enable-stable-abi` - just `-stable-abi` -- (your suggestion here) In practice, the precise name of this flag won't be very important, because (1) the number of people outside of Apple making libraries with binary stability concerns won't be very high, and (2) most people will be building those libraries through Xcode or SwiftPM, which will have its own name for this mode. But naming the *feature* is still important. From d71e3d6896b58a508a160b56155f4ddd2b943f36 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 12 Apr 2019 22:05:34 -0700 Subject: [PATCH 1128/4563] Clarify that a 'let' constant cannot have a property delegate. --- proposals/NNNN-property-delegates.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 39de473f11..43d1f4456c 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -680,10 +680,7 @@ This formulation of custom attributes fits in with a [larger proposal for custom ### Mutability of properties with delegates -Generally, a property that has a property delegate will have both a getter and a setter. However, there are several reasons for which the setter may be missing: - -* The `value` property of the property delegate type lacks a setter, or its setter is inaccessible -* The property is declared as a `let` +Generally, a property that has a property delegate will have both a getter and a setter. However, the setter may be missing if the `value` property of the property delegate type lacks a setter, or its setter is inaccessible. The synthesized getter will be `mutating` if the property delegate type's `value` property is `mutating` and the property is part of a `struct`. Similarly, the synthesized setter will be `nonmutating` if either the property delegate type's `value` property has a `nonmutating` setter or the property delegate type is a `class`. For example: @@ -730,7 +727,7 @@ example: ```swift -@Lazy let x: Int +@Lazy var x: Int // ... x = 17 // okay, treated as $x = .init(initialValue: 17) ``` @@ -815,7 +812,7 @@ Currently, identifiers starting with a `$` are not permitted in Swift programs. This proposal loosens these rules slightly: the Swift compiler will introduce identifiers that start with `$` (for the synthesized storage property), and Swift code can reference those properties. However, Swift code cannot declare any new entities with an identifier that begins with `$`. For example: ```swift -var x by Lazy = 17 +@Lazy var x = 17 print($x) // okay to refer to compiler-defined $x let $y = 17 // error: cannot declare entity with $-prefixed name '$y' ``` @@ -944,7 +941,7 @@ protocol PropertyDelegate { There are a few issues here. First, a single protocol `PropertyDelegate` cannot handle all of the variants of `value` that -are implied by the section 'Mutability of properties with delegates'_, +are implied by the section on mutability of properties with delegates, because we'd need to cope with `mutating get` as well as `set` and `nonmutating set`. Moreover, protocols don't support optional requirements, like `init(initialValue:)` (which also has two From 3ac1b1416717571f6d1491bb179490afcf4e7dea Mon Sep 17 00:00:00 2001 From: NevinBR Date: Sat, 13 Apr 2019 05:49:51 -0700 Subject: [PATCH 1129/4563] Update proposals/NNNN-library-evolution.md Co-Authored-By: airspeedswift --- proposals/NNNN-library-evolution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-library-evolution.md b/proposals/NNNN-library-evolution.md index 185e137316..3ccfa0d1c1 100644 --- a/proposals/NNNN-library-evolution.md +++ b/proposals/NNNN-library-evolution.md @@ -68,7 +68,7 @@ This attribute has no effect when library evolution mode is off. In that case, t A new command-line argument, `-enable-library-evolution`, will enable this new mode. -Turning on library evolution mode will have the following affects on source code: +Turning on library evolution mode will have the following effects on source code: - The default behavior for enums will change to be non-frozen. This is the only change visible to _users_ of the library, who will need to use the `@unknown default:` technique described in SE-0192. - To ensure that all fields are initialized, inlinable `init`s of the type must delegate to a non-inlinable `init`. From 473ab24677bdb55c7d22d9a2b10ec06055b174b2 Mon Sep 17 00:00:00 2001 From: NevinBR Date: Sat, 13 Apr 2019 05:51:06 -0700 Subject: [PATCH 1130/4563] Update proposals/NNNN-library-evolution.md Co-Authored-By: airspeedswift --- proposals/NNNN-library-evolution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-library-evolution.md b/proposals/NNNN-library-evolution.md index 3ccfa0d1c1..482dffeeee 100644 --- a/proposals/NNNN-library-evolution.md +++ b/proposals/NNNN-library-evolution.md @@ -355,7 +355,7 @@ Which translates to three possible restrictions: 2. Fields of a fixed-contents struct may not be renamed, even non-public ones. 3. Copying the struct requires calling a function because we can't see the private fields. -(3) is a non-starter, since it defeats the performance motivation for `@fixedContents`. That leaves (1) and (2), and we decided it would be *really weird* if renaming the private property `angle` to `theta` changed the layout of the struct. So the restriction on reordering fields was considered the least bad solution. It helps that it matches the behavior of C, Rust, and other languages. +(3) is a non-starter, since it defeats the performance motivation for `@frozen`. That leaves (1) and (2), and we decided it would be *really weird* if renaming the private property `angle` to `theta` changed the layout of the struct. So the restriction on reordering fields was considered the least bad solution. It helps that it matches the behavior of C, Rust, and other languages. ### Fixed-contents by default From 28721fcfc0c179072cf38043bb0bad3d7f39bdb4 Mon Sep 17 00:00:00 2001 From: NevinBR Date: Sat, 13 Apr 2019 05:51:42 -0700 Subject: [PATCH 1131/4563] Update proposals/NNNN-library-evolution.md Co-Authored-By: airspeedswift --- proposals/NNNN-library-evolution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-library-evolution.md b/proposals/NNNN-library-evolution.md index 482dffeeee..1b1d7ae089 100644 --- a/proposals/NNNN-library-evolution.md +++ b/proposals/NNNN-library-evolution.md @@ -315,7 +315,7 @@ func layoutForFixedContentsStruct(fields: [Field]) -> [Offset: Field] To make things simple, let's assume all the fields in the struct have the same type, like this PolarPoint: ```swift -@fixedContents public struct PolarPoint { +@frozen public struct PolarPoint { // Yes, these have different access levels. // You probably wouldn't do that in real life, // but it's needed for this contrived example. From 2d4356862bda7d3cf1bfe5354b840b87dcd63bcd Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Sat, 13 Apr 2019 07:29:58 -0700 Subject: [PATCH 1132/4563] Address Nevin's feedback --- proposals/NNNN-library-evolution.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/proposals/NNNN-library-evolution.md b/proposals/NNNN-library-evolution.md index 1b1d7ae089..171793f7d3 100644 --- a/proposals/NNNN-library-evolution.md +++ b/proposals/NNNN-library-evolution.md @@ -25,9 +25,7 @@ This proposal introduces: - a "library evolution" build mode for libraries that are declaring ABI stability, which preserves the ability to make certain changes to types without breaking the library's ABI; and - an attribute for such libraries to opt out of this flexibility on a per-type basis, allowing certain compile-time optimizations. -The mechanisms for this are already in place, and were used to stabilize the -ABI of the standard library. This proposal makes them features for use by -any 3rd-party library that wishes to declare itself ABI stable. +The mechanisms for this are already in place, and were used to stabilize the ABI of the standard library. This proposal makes them features for use by any 3rd-party library that wishes to declare itself ABI stable. **This build mode will have no impact on libraries built and distributed with an app**. Such libraries will receive the same optimization that they have in previous versions of Swift. @@ -77,7 +75,7 @@ Turning on library evolution mode will have the following effects on source code When a library author is certain that there will never be a need to add fields to a struct in future, they may mark that type as `@frozen`. -This will allow the compiler to optimize away at compile time some calls that would otherwise need to be made at runtime (for example, it might access ). +This will allow the compiler to optimize away at compile time some calls that would otherwise need to be made at runtime (for example, it might access fields directly without the indirection). When compiling with binary stability mode on, a struct can be marked `@frozen` as long as it meets all of the following conditions: @@ -233,7 +231,7 @@ These considerations should not affect libraries shipped with their clients, inc ## Effect on Library Evolution -Both structs and enums can gain new protocol conformances or methods, even when `@frozen`. Binary compatability only affects additions of fields or cases. +Both structs and enums can gain new protocol conformances or methods, even when `@frozen`. Binary compatibility only affects additions of fields or cases. The set of binary-compatible changes to a struct's stored instance properties is described above. There are no binary-compatible changes to an enum's cases. @@ -280,8 +278,7 @@ Other than `@frozen`, most names have revolved around the notion of "fixed" some #### Modifier or attribute? -This proposal suggests a new *attribute* for structs, `@fixedContents`; it could also be a modifier `fixedContents` (or `fixed`), implemented as a context-sensitive keyword. Because this annotation only affects the struct's *implementation* rather than anything about its use, an attribute seemed more appropriate. - +This proposal suggests a new *attribute*, `@frozen`; it could also be a modifier `frozen`, implemented as a context-sensitive keyword. Because this annotation only affects the type's *implementation* rather than anything about its use, an attribute seemed more appropriate. ### Command-line flag naming From 2dbd2bbe4eb5521b4fa6ef769d0719c0a2aa5597 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 13 Apr 2019 07:49:53 -0700 Subject: [PATCH 1133/4563] Add links to the latest toolchain --- proposals/NNNN-property-delegates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 43d1f4456c..32c74b411d 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: **TBD** * Status: **Awaiting review** -* Implementation: [PR #23701](https://github.com/apple/swift/pull/23701) +* Implementation: [PR #23701](https://github.com/apple/swift/pull/23701), [Ubuntu 16.04 Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/221/artifact/branch-master/swift-PR-23701-221-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/282/artifact/branch-master/swift-PR-23701-282-osx.tar.gz) ## Introduction From 3c41b8f1c87b764441617012430940d6487e7243 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Sun, 14 Apr 2019 08:16:24 -0700 Subject: [PATCH 1134/4563] Update NNNN-library-evolution.md --- proposals/NNNN-library-evolution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-library-evolution.md b/proposals/NNNN-library-evolution.md index 171793f7d3..95702f09fd 100644 --- a/proposals/NNNN-library-evolution.md +++ b/proposals/NNNN-library-evolution.md @@ -110,7 +110,7 @@ This affects what changes to the struct's fields affect the ABI of the containin | Changing a stored instance property to computed | Allowed | **Affects ABI** | Changing a computed instance property to stored | Allowed | **Affects ABI** | Changing the access of a non-ABI-public field | Allowed | Allowed -| Marking an `internal` field as `@abiPublic` | Allowed | Allowed +| Marking an `internal` field as `@usableFromInline` | Allowed | Allowed | Changing an `internal` ABI-public field to be `public` | Allowed | Allowed > Note: This proposal is implemented already and in use by the standard library, albeit under different names. The command-line flag is `-enable-library-evolution`; the attribute is `@_fixed_layout` for structs, and `@_frozen` for enums. From 016883a45c3833b4b8ba412b9eb2d31cb1986ae8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 14 Apr 2019 22:58:57 -0700 Subject: [PATCH 1135/4563] Fix DelayedMutable example --- proposals/NNNN-property-delegates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index 32c74b411d..df40034670 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -262,7 +262,7 @@ This enables multi-phase initialization, like this: ```swift class Foo { - @DelayedImmutable var x: Int + @DelayedImmutable() var x: Int init() { // We don't know "x" yet, and we don't have to set it From 5905632ae1688057ee1a527dff807cfea17fb170 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 15 Apr 2019 08:58:44 -0700 Subject: [PATCH 1136/4563] Update link to newer macOS toolchain --- proposals/NNNN-property-delegates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index df40034670..e6eb9e988b 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: **TBD** * Status: **Awaiting review** -* Implementation: [PR #23701](https://github.com/apple/swift/pull/23701), [Ubuntu 16.04 Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/221/artifact/branch-master/swift-PR-23701-221-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/282/artifact/branch-master/swift-PR-23701-282-osx.tar.gz) +* Implementation: [PR #23701](https://github.com/apple/swift/pull/23701), [Ubuntu 16.04 Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/221/artifact/branch-master/swift-PR-23701-221-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/287//artifact/branch-master/swift-PR-23701-287-osx.tar.gz) ## Introduction From 1b7101031d69bb7d9daeda865102199a45f4c296 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 15 Apr 2019 09:01:24 -0700 Subject: [PATCH 1137/4563] Rename 'storageValue' to 'delegateValue' --- proposals/NNNN-property-delegates.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index e6eb9e988b..f5cad0afd9 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -430,7 +430,7 @@ struct CopyOnWrite { private(set) var value: Value - var storageValue: Value { + var delegateValue: Value { mutating get { if !isKnownUniquelyReferenced(&value) { value = value.copy() @@ -444,7 +444,7 @@ struct CopyOnWrite { } ``` -`storageValue` provides delegation for the synthesized storage property, allowing the copy-on-write wrapper to be used directly: +`delegateValue` provides delegation for the synthesized storage property, allowing the copy-on-write wrapper to be used directly: ```swift @CopyOnWrite var storage: MyStorageBuffer @@ -452,7 +452,7 @@ struct CopyOnWrite { // Non-modifying access: let index = storage.index(of: …) -// For modification, access $storage, which goes through `storageValue`: +// For modification, access $storage, which goes through `delegateValue`: $storage.append(…) ``` @@ -507,7 +507,7 @@ class Box { self.value = initialValue } - var storageValue: Ref { + var delegateValue: Ref { return Ref(read: { self.value }, write: { self.value = $0 }) } } @@ -520,11 +520,11 @@ Now, we can define a new `Box` directly: print(rectangle) // access the rectangle print(rectangle.topLeft) // access the top left coordinate of the rectangle -let rect2 = $rectangle // through storageValue, produces a Ref -let topLeft2 = $rectangle.topLeft // through storageValue, produces a Ref +let rect2 = $rectangle // through delegateValue, produces a Ref +let topLeft2 = $rectangle.topLeft // through delegateValue, produces a Ref ``` -The use of `storageValue` hides the box from the client, providing direct access to the value in the box (the common case) as well as access to the box contents via Ref. +The use of `delegateValue` hides the box from the client, providing direct access to the value in the box (the common case) as well as access to the box contents via Ref. ### Property delegate types in the wild @@ -848,8 +848,8 @@ A property with a delegate can declare accessors explicitly (`get`, `set`, `didS ### Delegating access to the storage property -A property delegate type can choose to hide its instance entirely by providing a property named `storageValue`. As with the `value` property and`init(initialValue:)`, the `storageValue` property must have the -same access level as its property delegate type. When present, the synthesized storage property is hidden completely and the property `$foo` becomes a computed property accessing the storage property's `storageValue`. For example: +A property delegate type can choose to hide its instance entirely by providing a property named `delegateValue`. As with the `value` property and`init(initialValue:)`, the `delegateValue` property must have the +same access level as its property delegate type. When present, the synthesized storage property is hidden completely and the property `$foo` becomes a computed property accessing the storage property's `delegateValue`. For example: ```swift class StorageManager { @@ -870,7 +870,7 @@ struct LongTermStorage { set { pointer.pointee = newValue } } - var storageValue: UnsafeMutablePointer { + var delegateValue: UnsafeMutablePointer { return pointer } } @@ -887,7 +887,7 @@ var someValue: String print(someValue) // prints "Hello" someValue = "World" // update the value in storage to "World" -// $someValue accesses the storageValue property of the delegate instance, which +// $someValue accesses the delegateValue property of the delegate instance, which // is an UnsafeMutablePointer let world = $someValue.move() // take value directly from the storage $someValue.initialize(to: "New value") From 3f2e3586e6a45738fd4a22e073e95703e357d51b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 15 Apr 2019 09:07:56 -0700 Subject: [PATCH 1138/4563] Update Linux toolchain --- proposals/NNNN-property-delegates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-property-delegates.md b/proposals/NNNN-property-delegates.md index f5cad0afd9..a803e4c5d8 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/NNNN-property-delegates.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: **TBD** * Status: **Awaiting review** -* Implementation: [PR #23701](https://github.com/apple/swift/pull/23701), [Ubuntu 16.04 Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/221/artifact/branch-master/swift-PR-23701-221-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/287//artifact/branch-master/swift-PR-23701-287-osx.tar.gz) +* Implementation: [PR #23701](https://github.com/apple/swift/pull/23701), [Ubuntu 16.04 Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/223/artifact/branch-master/swift-PR-23701-223-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/287//artifact/branch-master/swift-PR-23701-287-osx.tar.gz) ## Introduction From 2283cf897f3baf7682dbdba836640714e21bdb85 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 15 Apr 2019 12:27:38 -0400 Subject: [PATCH 1139/4563] Assign the property delegates proposal SE-0258 and put it in review --- ...NNN-property-delegates.md => 0258-property-delegates.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-property-delegates.md => 0258-property-delegates.md} (99%) diff --git a/proposals/NNNN-property-delegates.md b/proposals/0258-property-delegates.md similarity index 99% rename from proposals/NNNN-property-delegates.md rename to proposals/0258-property-delegates.md index a803e4c5d8..c2a3a3b00e 100644 --- a/proposals/NNNN-property-delegates.md +++ b/proposals/0258-property-delegates.md @@ -1,9 +1,9 @@ # Property Delegates -* Proposal: [SE-NNNN](NNNN-property-delegates.md) +* Proposal: [SE-0258](0258-property-delegates.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) -* Review Manager: **TBD** -* Status: **Awaiting review** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (April 15...23, 2019)** * Implementation: [PR #23701](https://github.com/apple/swift/pull/23701), [Ubuntu 16.04 Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/223/artifact/branch-master/swift-PR-23701-223-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/287//artifact/branch-master/swift-PR-23701-287-osx.tar.gz) ## Introduction From a3a3aacbe69174605cbfc75922369c99448dca1a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 15 Apr 2019 13:47:05 -0700 Subject: [PATCH 1140/4563] [SE-0258] Clarify that property delegates cannot start with a lowercase letter. --- proposals/0258-property-delegates.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0258-property-delegates.md b/proposals/0258-property-delegates.md index c2a3a3b00e..b5023e057a 100644 --- a/proposals/0258-property-delegates.md +++ b/proposals/0258-property-delegates.md @@ -571,14 +571,15 @@ print(myValue) // print the most recent value ### Property delegate types A *property delegate type* is a type that can be used as a property -delegate. There are two basic requirements for a property delegate +delegate. There are three basic requirements for a property delegate type: 1. The property delegate type must be defined with the attribute `@propertyDelegate`. The attribute indicates that the type is meant to be used as a property delegate type, and provides a point at which the compiler can verify any other consistency rules. -2. The property delegate type must have a property named `value`, whose +2. The name of the property delegate type must not match the regular expression `_*[a-z].*`. Such names are reserved for compiler-defined attributes. +3. The property delegate type must have a property named `value`, whose access level is the same as that of the type itself. This is the property used by the compiler to access the underlying value on the delegate instance. From fa7a07eb8a97fc8c9d4f93ea11ec3fdfdc1a3d31 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 15 Apr 2019 22:15:08 -0700 Subject: [PATCH 1141/4563] Fix DelayedImmutable example. Thanks, Alejandro! (#1028) --- proposals/0258-property-delegates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-delegates.md b/proposals/0258-property-delegates.md index b5023e057a..817d16506d 100644 --- a/proposals/0258-property-delegates.md +++ b/proposals/0258-property-delegates.md @@ -235,7 +235,7 @@ a `let`: ```swift @propertyDelegate -struct DelayedImmutable: Value { +struct DelayedImmutable { private var _value: Value? = nil var value: Value { From 708ed108d2731b0e931e9a2279fd732b1e507cb8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 15 Apr 2019 23:41:49 -0700 Subject: [PATCH 1142/4563] Delayed immutable fix 2 (#1029) * Fix DelayedImmutable example. Thanks, Alejandro! * Fix another typo, thanks Alejandro! --- proposals/0258-property-delegates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-delegates.md b/proposals/0258-property-delegates.md index 817d16506d..06ce130bea 100644 --- a/proposals/0258-property-delegates.md +++ b/proposals/0258-property-delegates.md @@ -252,7 +252,7 @@ struct DelayedImmutable { if _value != nil { fatalError("property initialized twice") } - _value = initialValue + _value = newValue } } } From 1265875f9dfbfed68e188e11c2fb063c440381e7 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 16 Apr 2019 10:14:23 -0700 Subject: [PATCH 1143/4563] SE-244: Update link to toolchain --- proposals/0244-opaque-result-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index d0fe2f0364..82540b6d7b 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -5,7 +5,7 @@ * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Active review (April 11 - April 17, 2019)** * Implementation: [apple/swift#22072](https://github.com/apple/swift/pull/22072) -* Toolchain: https://github.com/apple/swift/pull/21137#issuecomment-468118328 +* Toolchain: https://github.com/apple/swift/pull/22072#issuecomment-483495849 * Previous revisions: ([1](https://github.com/apple/swift-evolution/commit/e60bac23bf0d6f345ddb48fbf64ea8324fce79a9)) * Previous review threads: https://forums.swift.org/t/se-0244-opaque-result-types/21252 From 9de82a7e452188e5dee639c70f1a9fc6a61f722b Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 16 Apr 2019 16:27:08 -0700 Subject: [PATCH 1144/4563] SE-0252 is implemented in Swift 5.1. --- proposals/0252-keypath-dynamic-member-lookup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0252-keypath-dynamic-member-lookup.md b/proposals/0252-keypath-dynamic-member-lookup.md index b7bff29eb7..25a174e7c0 100644 --- a/proposals/0252-keypath-dynamic-member-lookup.md +++ b/proposals/0252-keypath-dynamic-member-lookup.md @@ -3,7 +3,7 @@ * Proposal: [SE-0252](0252-keypath-dynamic-member-lookup.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Accepted** +* Status: **Implemented (Swift 5.1)** * Implementation: [PR #23436](https://github.com/apple/swift/pull/23436) ## Introduction From 6f4b46896001e650c05f76ce4846958d4f0db671 Mon Sep 17 00:00:00 2001 From: Ling Wang Date: Wed, 17 Apr 2019 13:35:54 -0500 Subject: [PATCH 1145/4563] =?UTF-8?q?Foo.x=20shouldn=E2=80=99t=20be=20stat?= =?UTF-8?q?ic=20(#1030)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proposals/0258-property-delegates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-delegates.md b/proposals/0258-property-delegates.md index 06ce130bea..3dc5a5e280 100644 --- a/proposals/0258-property-delegates.md +++ b/proposals/0258-property-delegates.md @@ -783,7 +783,7 @@ type as the delegate. For example: ```swift struct Foo { @UserDefault(key: "FOO_FEATURE_ENABLED", defaultValue: false) - static var x: Bool + var x: Bool @Lazy var y: Int = 17 @Lazy(closure: { getBool() }) var z: Bool @CopyOnWrite var w: Image From a9f9ceb067b76b5ca272565c5fbd02b608102fad Mon Sep 17 00:00:00 2001 From: Ling Wang Date: Wed, 17 Apr 2019 16:01:06 -0500 Subject: [PATCH 1146/4563] initialValue should have label (#1031) --- proposals/0258-property-delegates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-delegates.md b/proposals/0258-property-delegates.md index 3dc5a5e280..85446f75d8 100644 --- a/proposals/0258-property-delegates.md +++ b/proposals/0258-property-delegates.md @@ -861,7 +861,7 @@ class StorageManager { struct LongTermStorage { let pointer: UnsafeMutablePointer - init(manager: StorageManager, _ initialValue: Value) { + init(manager: StorageManager, initialValue: Value) { pointer = manager.allocate(Value.self) pointer.initialize(to: initialValue) } From 690e0df4133b9109bc1af62e96ac1429f381fb1b Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Wed, 17 Apr 2019 22:01:38 -0700 Subject: [PATCH 1147/4563] Rejected SE-0256. --- proposals/0256-contiguous-collection.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0256-contiguous-collection.md b/proposals/0256-contiguous-collection.md index dbfb53233f..9e28630cb4 100644 --- a/proposals/0256-contiguous-collection.md +++ b/proposals/0256-contiguous-collection.md @@ -3,9 +3,10 @@ * Proposal: [SE-0256](0256-contiguous-collection.md) * Authors: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active review (April 3 - April 12, 2019)** +* Status: **Rejected** * Previous Proposal: [SE-0237](0237-contiguous-collection.md) * Implementation: [apple/swift#23616](https://github.com/apple/swift/pull/23616) +* Decision Notes: [Rationale](https://forums.swift.org/t/se-0256-introduce-mutable-contiguouscollection-protocol/22569/8) ## Introduction From f21ebc127550ac091f802c4fac31bd81a082b80c Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Wed, 17 Apr 2019 22:05:59 -0700 Subject: [PATCH 1148/4563] SE-0244 has been accepted. --- proposals/0244-opaque-result-types.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index 82540b6d7b..0a8e4b7070 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -3,11 +3,12 @@ * Proposal: [SE-0244](0244-opaque-result-types.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active review (April 11 - April 17, 2019)** +* Status: **Accepted** * Implementation: [apple/swift#22072](https://github.com/apple/swift/pull/22072) * Toolchain: https://github.com/apple/swift/pull/22072#issuecomment-483495849 * Previous revisions: ([1](https://github.com/apple/swift-evolution/commit/e60bac23bf0d6f345ddb48fbf64ea8324fce79a9)) * Previous review threads: https://forums.swift.org/t/se-0244-opaque-result-types/21252 +* Decision Notes: [Rationale](https://forums.swift.org/t/se-0244-opaque-result-types-reopened/22942/57) ## Introduction From 36ea8be09508db9380949954d0c7a101fdb15226 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Wed, 17 Apr 2019 22:08:56 -0700 Subject: [PATCH 1149/4563] Mark SE-0253 as returned for revision --- proposals/0253-callable.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0253-callable.md b/proposals/0253-callable.md index 46abddd301..b92b05a200 100644 --- a/proposals/0253-callable.md +++ b/proposals/0253-callable.md @@ -3,8 +3,9 @@ * Proposal: [SE-0253](https://github.com/apple/swift-evolution/blob/master/proposals/0253-callable.md) * Authors: [Richard Wei](https://github.com/rxwei), [Dan Zheng](https://github.com/dan-zheng) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Active review (March 27 - April 5, 2019)** +* Status: **Returned for revision** * Implementation: [apple/swift#23517](https://github.com/apple/swift/pull/23517) +* Decision Notes: [Rationale](https://forums.swift.org/t/returned-for-revision-se-0253-static-callables/23290) ## Introduction From 8d4832bbe35bb1170c1b6cdc1b184ffbcaf6be57 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Thu, 18 Apr 2019 14:49:11 -0700 Subject: [PATCH 1150/4563] Return SE-0257 for revision. --- proposals/0257-elide-comma.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0257-elide-comma.md b/proposals/0257-elide-comma.md index 870d740c84..e071726400 100644 --- a/proposals/0257-elide-comma.md +++ b/proposals/0257-elide-comma.md @@ -3,10 +3,11 @@ * Proposal: [SE-0257](0257-elide-comma.md) * Author: [Nate Chandler](https://github.com/nate-chandler), [Matthew Johnson](https://github.com/anandabits) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active review (April 9 - April 18, 2019)** +* Status: **Returned for revision** * Implementation: [apple/swift#21876](https://github.com/apple/swift/pull/22714) * Previous Pitch: [Trailing commas in all expression lists](https://forums.swift.org/t/trailing-commas-in-all-expression-lists/19527) * Previous Pitch: [SE-0084 spinoff: Newlines as item separators](https://forums.swift.org/t/se-0084-spinoff-newlines-as-item-separators/2659) +* Decision Notes: [Rationale](https://forums.swift.org/t/se-0257-eliding-commas-from-multiline-expression-lists/22889/191) # Introduction From 38cb820613e0f99675cc8a96b2c015d022b12d2f Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 22 Apr 2019 20:03:13 +0300 Subject: [PATCH 1151/4563] Some minor corrections (#1033) --- proposals/0244-opaque-result-types.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index 0a8e4b7070..9529057aa7 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -47,7 +47,7 @@ struct Transformed: Shape { } ``` -One could compose these transformations by using the existential type `Shape` instead of generic arguments, but doing so would imply more dynamism and runtime overhead than may be desired. If we directly compose the generic containers, maintaining the concrete types, then generic specialization can more readily optimize together the composed operations together, and the type system can also be used . A game or graphics app may want to define objects in terms of their shapes: +One could compose these transformations by using the existential type `Shape` instead of generic arguments, but doing so would imply more dynamism and runtime overhead than may be desired. If we directly compose the generic containers, maintaining the concrete types, then generic specialization can more readily optimize the composed operations together, and the type system can also be used. A game or graphics app may want to define objects in terms of their shapes: ```swift protocol GameObject { @@ -162,7 +162,7 @@ different: ```swift var d = makeMeACollection(with: "seventeen") -c = d // error: types or makeMeACollection and makeMeACollection are different +c = d // error: types of makeMeACollection and makeMeACollection are different ``` Like a generic argument, the static type system does not consider the opaque type to be statically equivalent to the type it happens to be bound to: @@ -230,7 +230,7 @@ func f6(_: T.Type) -> some P { These rules guarantee that there is a single concrete type produced by any call to the function. The concrete type can depend on the generic type arguments (as in the `f6()` example), but must be consistent across all `return` statements. -Note that recursive calls *are* allowed, and are known to produce a value of the same concrete type, but that the concrete type itself is not known: +Note that recursive calls *are* allowed, and are known to produce a value of the same concrete type, but the concrete type itself is not known: ```swift func f7(_ i: Int) -> some P { From 9b6c1cacee59e6bd793eff1fd5920c12dcbc3ead Mon Sep 17 00:00:00 2001 From: y-okudera Date: Tue, 23 Apr 2019 14:43:31 +0900 Subject: [PATCH 1152/4563] Updated the release date of Swift 5. (#1034) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a40d722c3..a2a21d3170 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ features actually shipped for a given version. Those are documented in each vers ## Development major version: Swift 5.0 -Expected release date: Early 2019 +Released on March 25, 2019 ### Primary Focus: ABI Stability From 13f2a1cd7132978b4bfb41cd28391b5f98305e34 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 23 Apr 2019 10:35:43 -0700 Subject: [PATCH 1153/4563] [SE-0258] Update links to newer toolchains --- proposals/0258-property-delegates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-delegates.md b/proposals/0258-property-delegates.md index 85446f75d8..102718ad3b 100644 --- a/proposals/0258-property-delegates.md +++ b/proposals/0258-property-delegates.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (April 15...23, 2019)** -* Implementation: [PR #23701](https://github.com/apple/swift/pull/23701), [Ubuntu 16.04 Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/223/artifact/branch-master/swift-PR-23701-223-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/287//artifact/branch-master/swift-PR-23701-287-osx.tar.gz) +* Implementation: [PR #23701](https://ci.swift.org/job/swift-PR-toolchain-Linux/233/artifact/branch-master/swift-PR-23701-233-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/300/artifact/branch-master/swift-PR-23701-300-osx.tar.gz) ## Introduction From 36add39bbda915e5ec90c6514b830c5c30c223eb Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 23 Apr 2019 11:34:34 -0700 Subject: [PATCH 1154/4563] SE-0258 fix toolchain links --- proposals/0258-property-delegates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-delegates.md b/proposals/0258-property-delegates.md index 102718ad3b..20d7c52f1a 100644 --- a/proposals/0258-property-delegates.md +++ b/proposals/0258-property-delegates.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (April 15...23, 2019)** -* Implementation: [PR #23701](https://ci.swift.org/job/swift-PR-toolchain-Linux/233/artifact/branch-master/swift-PR-23701-233-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/300/artifact/branch-master/swift-PR-23701-300-osx.tar.gz) +* Implementation: [PR #23701](https://github.com/apple/swift/pull/23701), [Ubuntu 16.04 Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/233/artifact/branch-master/swift-PR-23701-233-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/300/artifact/branch-master/swift-PR-23701-300-osx.tar.gz) ## Introduction From 4409b020aae126e8709804e5518f364ce5e3c959 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 24 Apr 2019 12:34:49 -0400 Subject: [PATCH 1155/4563] Add isAlmostEqual(to:) and isAlmostZero() to FloatingPoint. (#1020) * PR for approximate equality. * Add implementation link. * Kick off review --- proposals/0259-approximately-equal.md | 259 ++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 proposals/0259-approximately-equal.md diff --git a/proposals/0259-approximately-equal.md b/proposals/0259-approximately-equal.md new file mode 100644 index 0000000000..f678f61d5b --- /dev/null +++ b/proposals/0259-approximately-equal.md @@ -0,0 +1,259 @@ +# Approximate Equality for Floating Point + +* Proposal: [SE-0259](0259-approximately-equal.md) +* Author: [Stephen Canon](https://github.com/stephentyrone) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Active Review (April 24 – May 1)** +* Implementation: [apple/swift#23839](https://github.com/apple/swift/pull/23839) + +## Introduction + +The internet is full advice about what not to do when comparing floating-point values: + +- "Never compare floats for equality." +- "Always use an epsilon." +- "Floating-point values are always inexact." + +Much of this advice is false, and most of the rest is technically correct but misleading. +Almost none of it provides specific and correct recommendations for what you *should* +do if you need to compare floating-point numbers. + +There is no uniformly correct notion of "approximate equality", and there is no uniformly +correct tolerance that can be applied without careful analysis, but we can define +approximate equality functions that are better than what most people will come up with +without assistance from the standard library. + +Pitch thread: [Approximate equality for floating-point](https://forums.swift.org/t/approximate-equality-for-floating-point/22420) + +## Motivation + +Almost all floating-point computations incur rounding error, and many additionally need +to account for model errors (numerical solutions for physical problems are often best +analyzed as solving a *related* equation exactly, rather than approximately solving the +original equation, for example). Because of this, it is frequently desirable to consider two +results to be equivalent if they are equal within an approximate tolerance. + +However, people tend to blindly apply this notion without careful analysis, and without +much understanding of the mechanics of floating-point. This leads to a lot of C-family +code that looks like: +```C +if (fabs(x - y) < DBL_EPSILON) { + // equal enough! +} +``` +This is almost surely incorrect, however. One of the key features of floating-point arithmetic +is *scale invariance*, which means that comparing with an *absolute* tolerance will +essentially always be incorrect. Further, in the most likely scaling (x and y are both of +roughly unit scale), the tolerance `DBL_EPSILON` is *far* too small for any nontrivial +computation. + +Even though these are relatively simple things to account for, people get them wrong more +often than they get them right, so it's a great opportunity for the standard library to help +improve software quality with a small well-considered API. + +## Proposed solution +```Swift +if x.isAlmostEqual(to: y) { + // equal enough! +} +``` +This predicate is reflexive (except for NaN, like all floating-point comparisons) and +symmetric (many implementations of approximately equality you'll find on the internet +are not, which sets people up for hard to find bugs or broken tests). It gracefully handles +subnormal numbers in the only sensible fashion possible, and uses a tolerance that is +acceptable for most computations. For cases that want to manually specify another +tolerance, we have: +```Swift +if x.isAlmostEqual(to: y, tolerance: 0.001) { + // equal enough! +} +``` +Both of the aforementioned functions use a *relative* tolerance. There is a single value +for which that's unsuitable--comparison with zero (because no number will ever +compare equal to zero with a sensible relative tolerance applied). To address this, we +provide one additional function: +```Swift +if x.isAlmostZero( ) { + // zero enough! +} +``` +## Detailed design +```swift +extension FloatingPoint { + /// Test approximate equality with relative tolerance. + /// + /// Do not use this function to check if a number is approximately + /// zero; no reasoned relative tolerance can do what you want for + /// that case. Use `isAlmostZero` instead for that case. + /// + /// The relation defined by this predicate is symmetric and reflexive + /// (except for NaN), but *is not* transitive. Because of this, it is + /// often unsuitable for use for key comparisons, but it can be used + /// successfully in many other contexts. + /// + /// The internet is full advice about what not to do when comparing + /// floating-point values: + /// + /// - "Never compare floats for equality." + /// - "Always use an epsilon." + /// - "Floating-point values are always inexact." + /// + /// Much of this advice is false, and most of the rest is technically + /// correct but misleading. Almost none of it provides specific and + /// correct recommendations for what you *should* do if you need to + /// compare floating-point numbers. + /// + /// There is no uniformly correct notion of "approximate equality", and + /// there is no uniformly correct tolerance that can be applied without + /// careful analysis. This function considers two values to be almost + /// equal if the relative difference between them is smaller than the + /// specified `tolerance`. + /// + /// The default value of `tolerance` is `sqrt(.ulpOfOne)`; this value + /// comes from the common numerical analysis wisdom that if you don't + /// know anything about a computation, you should assume that roughly + /// half the bits may have been lost to rounding. This is generally a + /// pretty safe choice of tolerance--if two values that agree to half + /// their bits but are not meaningfully almost equal, the computation + /// is likely ill-conditioned and should be reformulated. + /// + /// For more complete guidance on an appropriate choice of tolerance, + /// consult with a friendly numerical analyst. + /// + /// - Parameters: + /// - other: the value to compare with `self` + /// - tolerance: the relative tolerance to use for the comparison. + /// Should be in the range (.ulpOfOne, 1). + /// + /// - Returns: `true` if `self` is almost equal to `other`; otherwise + /// `false`. + @inlinable + public func isAlmostEqual( + to other: Self, + tolerance: Self = Self.ulpOfOne.squareRoot() + ) -> Bool { + // tolerances outside of [.ulpOfOne,1) yield well-defined but useless results, + // so this is enforced by an assert rathern than a precondition. + assert(tolerance >= .ulpOfOne && tolerance < 1, "tolerance should be in [.ulpOfOne, 1).") + // The simple computation below does not necessarily give sensible + // results if one of self or other is infinite; we need to rescale + // the computation in that case. + guard self.isFinite && other.isFinite else { + return rescaledAlmostEqual(to: other, tolerance: tolerance) + } + // This should eventually be rewritten to use a scaling facility to be + // defined on FloatingPoint suitable for hypot and scaled sums, but the + // following is good enough to be useful for now. + let scale = max(abs(self), abs(other), .leastNormalMagnitude) + return abs(self - other) < scale*tolerance + } + + /// Test if this value is nearly zero with a specified `absoluteTolerance`. + /// + /// This test uses an *absolute*, rather than *relative*, tolerance, + /// because no number should be equal to zero when a relative tolerance + /// is used. + /// + /// Some very rough guidelines for selecting a non-default tolerance for + /// your computation can be provided: + /// + /// - If this value is the result of floating-point additions or + /// subtractions, use a tolerance of `.ulpOfOne * n * scale`, where + /// `n` is the number of terms that were summed and `scale` is the + /// magnitude of the largest term in the sum. + /// + /// - If this value is the result of floating-point multiplications, + /// consider each term of the product: what is the smallest value that + /// should be meaningfully distinguished from zero? Multiply those terms + /// together to get a tolerance. + /// + /// - More generally, use half of the smallest value that should be + /// meaningfully distinct from zero for the purposes of your computation. + /// + /// For more complete guidance on an appropriate choice of tolerance, + /// consult with a friendly numerical analyst. + /// + /// - Parameter absoluteTolerance: values with magnitude smaller than + /// this value will be considered to be zero. Must be greater than + /// zero. + /// + /// - Returns: `true` if `abs(self)` is less than `absoluteTolerance`. + /// `false` otherwise. + @inlinable + public func isAlmostZero( + absoluteTolerance tolerance: Self = Self.ulpOfOne.squareRoot() + ) -> Bool { + assert(tolerance > 0) + return abs(self) < tolerance + } + + /// Rescales self and other to give meaningful results when one of them + /// is infinite. We also handle NaN here so that the fast path doesn't + /// need to worry about it. + @usableFromInline + internal func rescaledAlmostEqual(to other: Self, tolerance: Self) -> Bool { + // NaN is considered to be not approximately equal to anything, not even + // itself. + if self.isNaN || other.isNaN { return false } + if self.isInfinite { + if other.isInfinite { return self == other } + let scaledSelf = Self(signOf: self, magnitudeOf: 1) + let scaledOther = Self(sign: other.sign, exponent: -1, + significand: other.significand) + return scaledSelf.isAlmostEqual(to: scaledOther, tolerance: tolerance) + } + // If self is finite and other is infinite, flip order and use scaling + // defined above, since this relation is symmetric. + return other.rescaledAlmostEqual(to: self, tolerance: tolerance) + } +} +``` + +## Source compatibility + +This is a purely additive change. These operations may collide with existing extensions +that users have defined with the same names, but the user-defined definitions will win, so +there is no ambiguity introduced, and it will not change the behavior of existing programs. +Those programs can opt-in to the stdlib notion of approximate equality by removing their +existing definitions. + +## Effect on ABI stability + +Additive change only. + +## Effect on API resilience + +This introduces two leaf functions that will become part of the public API for the +FloatingPoint protocol, and one `@usableFromInline` helper function. We may make small +changes to these implementations over time for the purposes of performance optimization, +but the APIs are expected to be stable. + +## Alternatives considered + +**Why not change the behavior of `==`?** +Approximate equality is useful to have, but is a terrible default because it isn't transitive, +and does not imply substitutability under any useful model of floating-point values. This +breaks all sorts of implicit invariants that programs depend on. + +**Why not introduce a fancy new operator like `a ≅ b` or `a == b ± t`?** +There's a bunch of clever things that one might do in this direction, but it's not at all +obvious which (if any) of them is actually a good idea. Defining good semantics for +approximate equality via a method in the standard library makes it easy for people +to experiment with clever operators if they want to, and provides very useful functionality +now. + +**Why is a relative tolerance used?** +Because--as a library--we do not know the scaling of user's inputs a priori, a relative +tolerance is the only sensible option. Any absolute tolerance will be wrong more often +than it is right. + +**Why do we need a special comparison for zero?** +Because zero is the one and only value with which a relative comparison tolerance is +essentially never useful. For well-scaled values, `x.isAlmostEqual(to: 0, tolerance: t)` +will be false for any valid `t`. + +**Why not a single function using a hybrid relative/absolute tolerance?** +Because--as a library--we do not know the scaling of user's inputs a priori, this will be +wrong more often than right. The scale invariance (up to underflow) of the main +`isAlmostEqual` function is a highly-desirable property, because it mirrors the way +the rest of the floating-point number system works. From b3d0482b072d8b9c9c0c94736953f4b11964c146 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 24 Apr 2019 09:55:01 -0700 Subject: [PATCH 1156/4563] Reflect feedback from pitch --- proposals/NNNN-library-evolution.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/proposals/NNNN-library-evolution.md b/proposals/NNNN-library-evolution.md index 95702f09fd..473c75b627 100644 --- a/proposals/NNNN-library-evolution.md +++ b/proposals/NNNN-library-evolution.md @@ -5,7 +5,7 @@ * Review Manager: TBD * Status: **Awaiting review** * Implementation: Implemented in Swift 5 for standard library use -* Pre-review discussion: (TODO) +* Pre-review discussion: [Forum thread](https://forums.swift.org/t/pitch-library-evolution-for-stable-abis/) +* Review: ([review](https://forums.swift.org/t/se-0260-library-evolution-for-stable-abis/24260)) ([acceptance](https://forums.swift.org/t/accepted-se-0260-library-evolution-for-stable-abis/24845)) ## Introduction From 45cd354843860e9cc29c333b40028aab624503ad Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 May 2019 16:45:16 -0700 Subject: [PATCH 1182/4563] [SE-0258] Rename the feature to "property wrappers" --- ...delegates.md => 0258-property-wrappers.md} | 299 +++++++++--------- 1 file changed, 152 insertions(+), 147 deletions(-) rename proposals/{0258-property-delegates.md => 0258-property-wrappers.md} (71%) diff --git a/proposals/0258-property-delegates.md b/proposals/0258-property-wrappers.md similarity index 71% rename from proposals/0258-property-delegates.md rename to proposals/0258-property-wrappers.md index 8bf50527dc..8927c34f30 100644 --- a/proposals/0258-property-delegates.md +++ b/proposals/0258-property-wrappers.md @@ -1,17 +1,18 @@ -# Property Delegates +# Property Wrappers -* Proposal: [SE-0258](0258-property-delegates.md) +* Proposal: [SE-0258](0258-property-wrappers.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Returned for revision** * Implementation: [PR #23701](https://github.com/apple/swift/pull/23701), [Ubuntu 16.04 Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/233/artifact/branch-master/swift-PR-23701-233-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/300/artifact/branch-master/swift-PR-23701-300-osx.tar.gz) -* Review: ([review](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) +* Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) +* Previous versions: [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) ## Introduction There are property implementation patterns that come up repeatedly. Rather than hardcode a fixed set of patterns into the compiler, -we should provide a general "property delegate" mechanism to allow +we should provide a general "property wrapper" mechanism to allow these patterns to be defined as libraries. This is an alternative approach to some of the problems intended to be addressed by the [2015-2016 property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md). Some of the examples are the same, but this proposal takes a completely different approach designed to be simpler, easier to understand for users, and less invasive in the compiler implementation. There is a section that discusses the substantive differences from that design near the end of this proposal. @@ -88,18 +89,18 @@ class Foo { ## Proposed solution -We propose the introduction of **property delegates**, which allow a -property declaration to state which **delegate** is used to implement -it. The delegate is described via an attribute: +We propose the introduction of **property wrappers**, which allow a +property declaration to state which **wrapper** is used to implement +it. The wrapper is described via an attribute: ```swift @Lazy var foo = 1738 ``` -This implements the property `foo` in a way described by the *property delegate type* for `Lazy`: +This implements the property `foo` in a way described by the *property wrapper type* for `Lazy`: ```swift -@propertyDelegate +@propertyWrapper enum Lazy { case uninitialized(() -> Value) case initialized(Value) @@ -126,10 +127,10 @@ enum Lazy { } ``` -A property delegate type provides the storage for a property that -uses it as a delegate. The `value` property of the delegate type +A property wrapper type provides the storage for a property that +uses it as a wrapper. The `value` property of the wrapper type provides the actual -implementation of the delegate, while the (optional) +implementation of the wrapper, while the (optional) `init(initialValue:)` enables initialization of the storage from a value of the property's type. The property declaration @@ -149,7 +150,7 @@ var foo: Int { The use of the prefix `$` for the synthesized storage property name is deliberate: it provides a predictable name for the backing storage, -so that delegate types can provide API. For example, we could provide +so that wrapper types can provide API. For example, we could provide a `reset(_:)` operation on `Lazy` to set it back to a new value: ```swift @@ -164,10 +165,10 @@ extension Lazy { $foo.reset(42) ``` -The property delegate instance can be initialized directly by providing the initializer arguments in parentheses after the name. This could be used, for example, when a particular property delegate requires more setup to provide access to a value (example courtesy of Harlan Haskins): +The property wrapper instance can be initialized directly by providing the initializer arguments in parentheses after the name. This could be used, for example, when a particular property wrapper requires more setup to provide access to a value (example courtesy of Harlan Haskins): ```swift -@propertyDelegate +@propertyWrapper struct UserDefault { let key: String let defaultValue: T @@ -191,16 +192,16 @@ enum GlobalSettings { } ``` -Property delegates can be applied to properties at global, local, or type scope. +Property wrappers can be applied to properties at global, local, or type scope. ## Examples Before describing the detailed design, here are some more examples of -delegates. +wrappers. ### Delayed Initialization -A property delegate can model "delayed" initialization delegate, where +A property wrapper can model "delayed" initialization, where the definite initialization (DI) rules for properties are enforced dynamically rather than at compile time. This can avoid the need for implicitly-unwrapped optionals in multi-phase initialization. We can @@ -208,7 +209,7 @@ implement both a mutable variant, which allows for reassignment like a `var`: ```swift -@propertyDelegate +@propertyWrapper struct DelayedMutable { private var _value: Value? = nil @@ -224,7 +225,7 @@ struct DelayedMutable { } } - /// "Reset" the delegate so it can be initialized again. + /// "Reset" the wrappe so it can be initialized again. mutating func reset() { _value = nil } @@ -235,7 +236,7 @@ and an immutable variant, which only allows a single initialization like a `let`: ```swift -@propertyDelegate +@propertyWrapper struct DelayedImmutable { private var _value: Value? = nil @@ -283,11 +284,11 @@ class Foo { Many Cocoa classes implement value-like objects that require explicit copying. Swift currently provides an `@NSCopying` attribute for properties to give -them delegate like Objective-C's `@property(copy)`, invoking the `copy` method -on new objects when the property is set. We can turn this into a delegate: +them behavior like Objective-C's `@property(copy)`, invoking the `copy` method +on new objects when the property is set. We can turn this into a wrappe: ```swift -@propertyDelegate +@propertyWrapper struct Copying { private var _value: Value @@ -311,11 +312,11 @@ This implementation would address the problem detailed in ### `Atomic` -Support for atomic operations (load, store, increment/decementer, compare-and-exchange) is a commonly-requested Swift feature. While the implementation details for such a feature would involve compiler and standard library magic, the interface itself can be nicely expressed as a property delegate type: +Support for atomic operations (load, store, increment/decementer, compare-and-exchange) is a commonly-requested Swift feature. While the implementation details for such a feature would involve compiler and standard library magic, the interface itself can be nicely expressed as a property wrapper type: ```swift -@propertyDelegate +@propertyWrapper struct Atomic { private var _value: Value @@ -368,10 +369,10 @@ print(initializedOnce) ### Thread-specific storage -Thread-specific storage (based on pthreads) can be implemented as a property delegate, too (example courtesy of Daniel Delwood): +Thread-specific storage (based on pthreads) can be implemented as a property wrapper, too (example courtesy of Daniel Delwood): ```swift -@propertyDelegate +@propertyWrapper final class ThreadSpecific { private var key = pthread_key_t() private let initialValue: T @@ -416,14 +417,14 @@ final class ThreadSpecific { ### Copy-on-write -With some work, property delegates can provide copy-on-write wrappers (original example courtesy of Brent Royal-Gordon): +With some work, property wrappers can provide copy-on-write wrappers (original example courtesy of Brent Royal-Gordon): ```swift protocol Copyable: AnyObject { func copy() -> Self } -@propertyDelegate +@propertyWrapper struct CopyOnWrite { init(initialValue: Value) { value = initialValue @@ -431,7 +432,7 @@ struct CopyOnWrite { private(set) var value: Value - var delegateValue: Value { + var wrapperValue: Value { mutating get { if !isKnownUniquelyReferenced(&value) { value = value.copy() @@ -445,7 +446,7 @@ struct CopyOnWrite { } ``` -`delegateValue` provides delegation for the synthesized storage property, allowing the copy-on-write wrapper to be used directly: +`wrapperValue` provides delegation for the synthesized storage property, allowing the copy-on-write wrapper to be used directly: ```swift @CopyOnWrite var storage: MyStorageBuffer @@ -453,18 +454,18 @@ struct CopyOnWrite { // Non-modifying access: let index = storage.index(of: …) -// For modification, access $storage, which goes through `delegateValue`: +// For modification, access $storage, which goes through `wrapperValue`: $storage.append(…) ``` ### `Ref` / `Box` -We can define a property delegate type `Ref` that is an abstracted reference +We can define a property wrapper type `Ref` that is an abstracted reference to some value that can be get/set, which is effectively a programmatic computed property: ```swift -@propertyDelegate +@propertyWrapper struct Ref { let read: () -> Value let write: (Value) -> Void @@ -495,12 +496,12 @@ let rect2 = $rect // get the Ref let topLeft2 = $rect.topLeft // get a Ref referring to the Rectangle's topLeft ``` -The `Ref` type encapsulates read/write, and making it a property delegate lets +The `Ref` type encapsulates read/write, and making it a property wrapper lets us primarily see the underlying value. Often, one does not want to explicitly -write out the getters and setters, and it's fairly common to have a `Box` type that boxes up a value and can vend `Ref` instances referring into that box. We can do so with another property delegate: +write out the getters and setters, and it's fairly common to have a `Box` type that boxes up a value and can vend `Ref` instances referring into that box. We can do so with another property wrapper: ```swift -@propertyDelegate +@propertyWrapper class Box { var value: Value @@ -508,7 +509,7 @@ class Box { self.value = initialValue } - var delegateValue: Ref { + var wrapperValue: Ref { return Ref(read: { self.value }, write: { self.value = $0 }) } } @@ -521,18 +522,18 @@ Now, we can define a new `Box` directly: print(rectangle) // access the rectangle print(rectangle.topLeft) // access the top left coordinate of the rectangle -let rect2 = $rectangle // through delegateValue, produces a Ref -let topLeft2 = $rectangle.topLeft // through delegateValue, produces a Ref +let rect2 = $rectangle // through wrapperValue, produces a Ref +let topLeft2 = $rectangle.topLeft // through wrapperValue, produces a Ref ``` -The use of `delegateValue` hides the box from the client, providing direct access to the value in the box (the common case) as well as access to the box contents via Ref. +The use of `wrapperValue` hides the box from the client, providing direct access to the value in the box (the common case) as well as access to the box contents via Ref. -### Property delegate types in the wild +### Property wrapper types in the wild -There are a number of existing types that already provide the basic structure of a property delegate type. One fun case is `Unsafe(Mutable)Pointer`, which we could augment to allow easy access to the pointed-to value: +There are a number of existing types that already provide the basic structure of a property wrapper type. One fun case is `Unsafe(Mutable)Pointer`, which we could augment to allow easy access to the pointed-to value: ```swift -@propertyDelegate +@propertyWrapper struct UnsafeMutablePointer { var pointee: Pointee { ... } @@ -569,35 +570,35 @@ print(myValue) // print the most recent value ## Detailed design -### Property delegate types +### Property wrapper types -A *property delegate type* is a type that can be used as a property -delegate. There are three basic requirements for a property delegate +A *property wrapper type* is a type that can be used as a property +wrapper. There are three basic requirements for a property wrapper type: -1. The property delegate type must be defined with the attribute -`@propertyDelegate`. The attribute indicates that the type is meant to -be used as a property delegate type, and provides a point at which the +1. The property wrapper type must be defined with the attribute +`@propertyWrapper`. The attribute indicates that the type is meant to +be used as a property wrapper type, and provides a point at which the compiler can verify any other consistency rules. -2. The name of the property delegate type must not match the regular expression `_*[a-z].*`. Such names are reserved for compiler-defined attributes. -3. The property delegate type must have a property named `value`, whose +2. The name of the property wrapper type must not match the regular expression `_*[a-z].*`. Such names are reserved for compiler-defined attributes. +3. The property wrapper type must have a property named `value`, whose access level is the same as that of the type itself. This is the property used by the compiler to access the underlying value on the -delegate instance. +wrapper instance. ### Initialization of synthesized storage properties -Introducing a property delegate to a property makes that property +Introducing a property wrapper to a property makes that property computed (with a getter/setter) and introduces a stored property whose -type is the delegate type. That stored property can be initialized +type is the wrapper type. That stored property can be initialized in one of two ways: 1. Via a value of the original property's type (e.g., `Int` in `@Lazy var - foo: Int`, using the the property delegate type's + foo: Int`, using the the property wrapper type's `init(initialValue:)` initializer. That initializer must have a single parameter of the same type as the `value` property (or be an `@autoclosure` thereof) and have the same access level as the - property delegate type itself. When `init(initialValue:)` is present, + property wrapper type itself. When `init(initialValue:)` is present, is is always used for the initial value provided on the property declaration. For example: @@ -610,8 +611,8 @@ in one of two ways: ``` -2. Via a value of the property delegate type, by placing the initializer - arguments after the property delegate type: +2. Via a value of the property wrapper type, by placing the initializer + arguments after the property wrapper type: ```swift var addressOfInt: UnsafePointer = ... @@ -624,10 +625,10 @@ in one of two ways: var someInt: Int { /* access via $someInt.value */ } ``` -### Type inference with delegates +### Type inference with wrappers -Type inference for properties with delegates involves both the type -annotation of the original property (if present) and the delegate +Type inference for properties with wrappers involves both the type +annotation of the original property (if present) and the wrapper type, using the initialization of the synthesized stored property. For example: @@ -638,7 +639,7 @@ var $foo: Lazy = Lazy(initialValue: 17) // infers the type of 'foo' to be 'Int' ``` -The same applies when directly initializing the delegate instance, e.g., +The same applies when directly initializing the wrapper instance, e.g., ```swift @UnsafeMutablePointer(mutating: addressOfInt) @@ -648,14 +649,14 @@ var $someInt: UnsafeMutablePointer = UnsafeMutablePointer.init(mutating: address // infers the type of 'someInt' to be 'Int' ``` -The type of the `value` property of the property delegate type must coincide with that of the original property using that delegate type. Some examples: +The type of the `value` property of the property wrapper type must coincide with that of the original property using that wrapper type. Some examples: ```swift @Lazy var foo: Int // okay @Lazy var bar: Double // error: Lazy.value is of type Int, not Double ``` -If there is no initializer for the delegate instance, and the property delegate type takes a single generic parameter, the corresponding generic argument can be omitted: +If there is no initializer for the wrapper instance, and the property wrapper type takes a single generic parameter, the corresponding generic argument can be omitted: ```swift @Lazy var foo: Int // okay: equivalent to @Lazy var foo: Int @@ -663,67 +664,67 @@ If there is no initializer for the delegate instance, and the property delegate ### Custom attributes -Property delegates are a form of custom attribute, where the attribute syntax -is used to refer to entities declared in Swift. Grammatically, the use of property delegates is described as follows: +Property wrappers are a form of custom attribute, where the attribute syntax +is used to refer to entities declared in Swift. Grammatically, the use of property wrappers is described as follows: ``` attribute ::= '@' type-identifier expr-paren? ``` -The *type-identifier* must refer to a property delegate type, which can include generic arguments. Note that this allows for qualification of the attribute names, e.g., +The *type-identifier* must refer to a property wrapper type, which can include generic arguments. Note that this allows for qualification of the attribute names, e.g., ```swift @Swift.Lazy var foo = 1742 ``` -The *expr-paren*, if present, provides the initialization arguments for the delegate instance. +The *expr-paren*, if present, provides the initialization arguments for the wrapper instance. -This formulation of custom attributes fits in with a [larger proposal for custom attributes](https://forums.swift.org/t/pitch-introduce-custom-attributes/21335/47), which uses the same custom attribute syntax as the above but allows for other ways in which one can define a type to be used as an attribute. In this scheme, `@propertyDelegate` is just one kind of custom attribute: there will be other kinds of custom attributes that are available only at compile time (e.g., for tools) or runtime (via some reflection capability). +This formulation of custom attributes fits in with a [larger proposal for custom attributes](https://forums.swift.org/t/pitch-introduce-custom-attributes/21335/47), which uses the same custom attribute syntax as the above but allows for other ways in which one can define a type to be used as an attribute. In this scheme, `@propertyWrapper` is just one kind of custom attribute: there will be other kinds of custom attributes that are available only at compile time (e.g., for tools) or runtime (via some reflection capability). -### Mutability of properties with delegates +### Mutability of properties with wrappers -Generally, a property that has a property delegate will have both a getter and a setter. However, the setter may be missing if the `value` property of the property delegate type lacks a setter, or its setter is inaccessible. +Generally, a property that has a property wrapper will have both a getter and a setter. However, the setter may be missing if the `value` property of the property wrapper type lacks a setter, or its setter is inaccessible. -The synthesized getter will be `mutating` if the property delegate type's `value` property is `mutating` and the property is part of a `struct`. Similarly, the synthesized setter will be `nonmutating` if either the property delegate type's `value` property has a `nonmutating` setter or the property delegate type is a `class`. For example: +The synthesized getter will be `mutating` if the property wrapper type's `value` property is `mutating` and the property is part of a `struct`. Similarly, the synthesized setter will be `nonmutating` if either the property wrapper type's `value` property has a `nonmutating` setter or the property wrapper type is a `class`. For example: ```swift -struct MutatingGetterDelegate { +struct MutatingGetterwrapper { var value: Value { mutating get { ... } set { ... } } } -struct NonmutatingSetterDelegate { +struct NonmutatingSetterwrapper { var value: Value { get { ... } nonmutating set { ... } } } -class ReferenceDelegate { +class Referencewrapper { var value: Value } -struct UseDelegates { +struct Usewrappers { // x's getter is mutating // x's setter is mutating - @MutatingGetterDelegate var x: Int + @MutatingGetterwrapper var x: Int // y's getter is nonmutating // y's setter is nonmutating - @NonmutatingSetterDelegate var y: Int + @NonmutatingSetterwrapper var y: Int // z's getter is nonmutating // z's setter is nonmutating - @ReferenceDelegate var z: Int + @Referencewrapper var z: Int } ``` -### Out-of-line initialization of properties with delegates +### Out-of-line initialization of properties with wrappers -A property that has a delegate can be initialized after it is defined, -either via the property itself (if the delegate type has an +A property that has a wrapper can be initialized after it is defined, +either via the property itself (if the wrapper type has an `init(initialValue:)`) or via the synthesized storage property. For example: @@ -745,7 +746,7 @@ $y = UnsafeMutable(pointer: addressOfInt) // okay Note that the rules of [definite initialization](https://developer.apple.com/swift/blog/?id=28) (DI) -apply to properties that have delegates. Let's expand the example of +apply to properties that have wrappers. Let's expand the example of `x` above to include a re-assignment and use `var`: ```swift @@ -763,23 +764,23 @@ By default, the synthesized storage property will have `internal` access or the ### Memberwise initializers Structs implicitly declare memberwise initializers based on the stored -properties of the struct. With a property that has a delegate, the +properties of the struct. With a property that has a wrapper, the property is technically computed because it's the synthesized property -(of the delegate's type) that is stored. Instance properties that have a -property delegate will have a corresponding parameter in the memberwise +(of the wrapper's type) that is stored. Instance properties that have a +property wrapper will have a corresponding parameter in the memberwise initializer, whose type will either be the original property type or -the delegate type, depending on the delegate type and the initial value +the wrapper type, depending on the wrapper type and the initial value (if provided). Specifically, the memberwise initializer parameter for -an instance property with a property delegate will have the original +an instance property with a property wrapper will have the original property type if either of the following is true: * The corresponding property has an initial value specified with the `=` syntax, e.g., `@Lazy var i = 17`, or - The corresponding property has no initial value, but the property -delegate type has an `init(initialValue:)`. +wrapper type has an `init(initialValue:)`. Otherwise, the memberwise initializer parameter will have the same -type as the delegate. For example: +type as the wrapper. For example: ```swift struct Foo { @@ -804,7 +805,7 @@ struct Foo { Synthesis for `Encodable`, `Decodable`, `Hashable`, and `Equatable` using the underlying `value` of the property when the property -delegate type contains an `init(initialValue:)` and the delegate's +wrapper type contains an `init(initialValue:)` and the wrapper's type otherwise. ### $ identifiers @@ -821,7 +822,7 @@ let $y = 17 // error: cannot declare entity with $-prefixed name '$y' ### Accessors -A property with a delegate can declare accessors explicitly (`get`, `set`, `didSet`, `willSet`). If either `get` or `set` is missing, it will be implicitly created by accessing the storage property (e.g., `$foo`). For example: +A property with a wrapper can declare accessors explicitly (`get`, `set`, `didSet`, `willSet`). If either `get` or `set` is missing, it will be implicitly created by accessing the storage property (e.g., `$foo`). For example: ```swift @Lazy var x = 17 { @@ -850,15 +851,15 @@ A property with a delegate can declare accessors explicitly (`get`, `set`, `didS ### Delegating access to the storage property -A property delegate type can choose to hide its instance entirely by providing a property named `delegateValue`. As with the `value` property and`init(initialValue:)`, the `delegateValue` property must have the -same access level as its property delegate type. When present, the synthesized storage property is hidden completely and the property `$foo` becomes a computed property accessing the storage property's `delegateValue`. For example: +A property wrapper type can choose to hide its instance entirely by providing a property named `wrapperValue`. As with the `value` property and`init(initialValue:)`, the `wrapperValue` property must have the +same access level as its property wrapper type. When present, the synthesized storage property is hidden completely and the property `$foo` becomes a computed property accessing the storage property's `wrapperValue`. For example: ```swift class StorageManager { func allocate(_: T.Type) -> UnsafeMutablePointer { ... } } -@propertyDelegate +@propertyWrapper struct LongTermStorage { let pointer: UnsafeMutablePointer @@ -872,13 +873,13 @@ struct LongTermStorage { set { pointer.pointee = newValue } } - var delegateValue: UnsafeMutablePointer { + var wrapperValue: UnsafeMutablePointer { return pointer } } ``` -When we use the `LongTermStorage` delegate, it handles the coordination with the `StorageManager` and provides either direct access or an `UnsafeMutablePointer` with which to manipulate the value: +When we use the `LongTermStorage` wrapper, it handles the coordination with the `StorageManager` and provides either direct access or an `UnsafeMutablePointer` with which to manipulate the value: ```swift let manager = StorageManager(...) @@ -889,61 +890,61 @@ var someValue: String print(someValue) // prints "Hello" someValue = "World" // update the value in storage to "World" -// $someValue accesses the delegateValue property of the delegate instance, which +// $someValue accesses the wrapperValue property of the wrapper instance, which // is an UnsafeMutablePointer let world = $someValue.move() // take value directly from the storage $someValue.initialize(to: "New value") ``` -### Restrictions on the use of property delegates +### Restrictions on the use of property wrappers -There are a number of restrictions on the use of property delegates when defining a property: +There are a number of restrictions on the use of property wrappers when defining a property: -* A property with a delegate may not declared inside a protocol. -* An instance property with a delegate may not declared inside an extension. +* A property with a wrapper may not declared inside a protocol. +* An instance property with a wrapper may not declared inside an extension. * An instance property may not be declared in an `enum`. -* A property with a delegate that is declared within a class must be +* A property with a wrapper that is declared within a class must be `final` and cannot override another property. -* A property with a delegate cannot be `lazy`, `@NSCopying`, `@NSManaged`, `weak`, or `unowned`. -* A property with a delegate must be the only property declared within its enclosing declaration (e.g., `@Lazy var (x, y) = /* ... */` is ill-formed). -* The `value` property and (if present) `init(initialValue:)` of a property delegate type shall have the same access as the property delegate type. +* A property with a wrapper cannot be `lazy`, `@NSCopying`, `@NSManaged`, `weak`, or `unowned`. +* A property with a wrapper must be the only property declared within its enclosing declaration (e.g., `@Lazy var (x, y) = /* ... */` is ill-formed). +* The `value` property and (if present) `init(initialValue:)` of a property wrapper type shall have the same access as the property wrapper type. ## Impact on existing code By itself, this is an additive feature that doesn't impact existing -code. However, with some of the property delegates suggested, it can +code. However, with some of the property wrappers suggested, it can potentially obsolete existing, hardcoded language features. `@NSCopying` could be completely replaced by a `Copying` -property delegate type introduced in the `Foundation` module. `lazy` +property wrapper type introduced in the `Foundation` module. `lazy` cannot be completely replaced because it's initial value can refer to the `self` of the enclosing type; see 'deferred evaluation of initialization expressions_. However, it may still make sense to -introduce a `Lazy` property delegate type to cover many of the common +introduce a `Lazy` property wrapper type to cover many of the common use cases, leaving the more-magical `lazy` as a backward-compatibility feature. ## Backward compatibility -The property delegates language feature as proposed has no impact on the ABI or runtime. Binaries that use property delegates can be backward-deployed to the Swift 5.0 runtime. +The property wrappers language feature as proposed has no impact on the ABI or runtime. Binaries that use property wrappers can be backward-deployed to the Swift 5.0 runtime. ## Alternatives considered -### Using a formal protocol instead of `@propertyDelegate` +### Using a formal protocol instead of `@propertyWrapper` -Instead of a new attribute, we could introduce a `PropertyDelegate` -protocol to describe the semantic constraints on property delegate +Instead of a new attribute, we could introduce a `Propertywrapper` +protocol to describe the semantic constraints on property wrapper types. It might look like this: ```swift -protocol PropertyDelegate { +protocol Propertywrapper { associatedtype Value var value: Value { get } } ``` There are a few issues here. First, a single protocol -`PropertyDelegate` cannot handle all of the variants of `value` that -are implied by the section on mutability of properties with delegates, +`Propertywrapper` cannot handle all of the variants of `value` that +are implied by the section on mutability of properties with wrappers, because we'd need to cope with `mutating get` as well as `set` and `nonmutating set`. Moreover, protocols don't support optional requirements, like `init(initialValue:)` (which also has two @@ -951,16 +952,16 @@ forms: one accepting a `Value` and one accepting an `@autoclosure () -> Value`) and `init()`. To cover all of these cases, we would need a several related-but-subtly-different protocols. -The second issue that, even if there were a single `PropertyDelegate` +The second issue that, even if there were a single `Propertywrapper` protocol, we don't know of any useful generic algorithms or data structures that seem to be implemented in terms of only -`PropertyDelegate`. +`Propertywrapper`. ### Kotlin-like `by` syntax -A previous iteration of this proposal (and its [implementation](https://github.com/apple/swift/pull/23440)) used `by` syntax similar to that of [Kotlin's delegated -properties](https://kotlinlang.org/docs/reference/delegated-properties.html), where the `by` followed the variable declaration. For example: +A previous iteration of this proposal (and its [implementation](https://github.com/apple/swift/pull/23440)) used `by` syntax similar to that of [Kotlin's wrapperd +properties](https://kotlinlang.org/docs/reference/wrapperd-properties.html), where the `by` followed the variable declaration. For example: ```swift var foo by Lazy = 1738 @@ -970,14 +971,14 @@ static var isFooFeatureEnabled: Bool by UserDefault(key: "FOO_FEATURE_ENABLED", There are some small advantages to this syntax over the attribute formulation: -* For cases like `UserDefault` where the delegate instance is initialized directly, the initialization happens after the original variable declaration, which reads better because the variable type and name come first, and how it's implemented come later. (Counter point: Swift developers are already accustomed to reading past long attributes, which are typically placed on the previous line) -* The `by DelegateType` formulation leaves syntactic space for add-on features like specifying the access level of the delegate instance (`by private DelegateType`) or delegating to an existing property (`by someInstanceProperty`). +* For cases like `UserDefault` where the wrapper instance is initialized directly, the initialization happens after the original variable declaration, which reads better because the variable type and name come first, and how it's implemented come later. (Counter point: Swift developers are already accustomed to reading past long attributes, which are typically placed on the previous line) +* The `by wrapperType` formulation leaves syntactic space for add-on features like specifying the access level of the wrapper instance (`by private wrapperType`) or delegating to an existing property (`by someInstanceProperty`). The main problem with `by` is its novelty: there isn't anything else in Swift quite like the `by` keyword above, and it is unlikely that the syntax would be re-used for any other feature. As a keyword, `by` is quite meaningless, and brainstorming during the [initial pitch](https://forums.swift.org/t/pitch-property-delegates/21895) didn't find any clearly good names for this functionality. ### The 2015-2016 property behaviors design -Property delegates address a similar set of use cases to *property behaviors*, which were [proposed and +Property wrappers address a similar set of use cases to *property behaviors*, which were [proposed and reviewed](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md) in late 2015/early 2016. The design did not converge, and the proposal was deferred. This proposal picks up the thread, using much of the @@ -988,21 +989,21 @@ the prior proposal are: * Behaviors were introduced into a property with the `[behavior]` syntax, rather than the attribute syntax described here. See the property behaviors proposal for more information. -* Delegates are always expressed by a (generic) type. Property behaviors +* wrappers are always expressed by a (generic) type. Property behaviors had a new kind of declaration (introduced by the `behavior` keyword). Having a new kind of declaration allowed for the introduction of specialized syntax, but it also greatly increased the surface area (and implementation cost) of the - proposal. Using a generic type makes property delegates more of a + proposal. Using a generic type makes property wrappers more of a syntactic-sugar feature that is easier to implement and explain. -* Delegates cannot declare new kinds of accessors (e.g., the +* wrappers cannot declare new kinds of accessors (e.g., the `didChange` example from the property behaviors proposal). -* Delegates used for properties declared within a type cannot refer to +* wrappers used for properties declared within a type cannot refer to the `self` of their enclosing type. This eliminates some use cases - (e.g., implementing a `Synchronized` property delegate type that + (e.g., implementing a `Synchronized` property wrapper type that uses a lock defined on the enclosing type), but simplifies the design. -* Delegates can be initialized out-of-line, and one +* wrappers can be initialized out-of-line, and one can use the `$`-prefixed name to refer to the storage property. These were future directions in the property behaviors proposal. @@ -1024,17 +1025,17 @@ public public(storage) var foo: Int = 1738 public private(storage) var bar: Int = 1738 ``` -### Composition of property delegates +### Composition of property wrappers -Only one property delegate can currently be attached to a given property, so code such as the following is in error: +Only one property wrapper can currently be attached to a given property, so code such as the following is in error: ```swift -@UnsafeMutablePointer @Atomic var x: Int // error: two attached property delegates +@UnsafeMutablePointer @Atomic var x: Int // error: two attached property wrappers ``` -The [property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md#composing-behaviors) describes some of the concerns surrounding composing behaviors/delegates in this way, which would need to be addressed by a future proposal. Matthew Johnson [sketched out a potential direction](https://forums.swift.org/t/pitch-property-delegates/21895/103) for extending property delegates to support composition of property delegates. +The [property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md#composing-behaviors) describes some of the concerns surrounding composing behaviors/wrappers in this way, which would need to be addressed by a future proposal. Matthew Johnson [sketched out a potential direction](https://forums.swift.org/t/pitch-property-delegates/21895/103) for extending property wrappers to support composition of property wrappers. -### Referencing the enclosing 'self' in a delegate type +### Referencing the enclosing 'self' in a wrapper type Manually-written getters and setters for properties declared in a type often refer to the `self` of their enclosing type. For example, this can be used to notify clients of a change to a property's value: @@ -1053,14 +1054,14 @@ public class MyClass: Superclass { } ``` -This "broadcast a notification that the value has changed" implementation cannot be cleanly factored into a property behavior type, because it needs access to both the underlying storage value (here, `backingMyVar`) and the `self` of the enclosing type. We could require a separate call to register the `self` instance with the delegate type, e.g., +This "broadcast a notification that the value has changed" implementation cannot be cleanly factored into a property behavior type, because it needs access to both the underlying storage value (here, `backingMyVar`) and the `self` of the enclosing type. We could require a separate call to register the `self` instance with the wrapper type, e.g., ```swift protocol Observed { func broadcastValueChanged(oldValue: T, newValue: T) } -@propertyDelegate +@propertyWrapper public struct Observable { public var stored: Value var observed: Observed? @@ -1101,11 +1102,11 @@ public class MyClass: Superclass { This isn't as automatic as we would like, and it requires us to have a separate reference to the `self` that is stored within `Observable`. -Instead, we could extend the ad hoc protocol used to access the storage property of a `@propertyDelegate` type a bit further. Instead of (or in addition to) a `value` property, a property delegate type could provide a `subscript(instanceSelf:)` and/or `subscript(typeSelf:)` that receive `self` as a parameter. For example: +Instead, we could extend the ad hoc protocol used to access the storage property of a `@propertyWrapper` type a bit further. Instead of (or in addition to) a `value` property, a property wrapper type could provide a `subscript(instanceSelf:)` and/or `subscript(typeSelf:)` that receive `self` as a parameter. For example: ```swift -@propertyDelegate +@propertyWrapper public struct Observable { public var stored: Value @@ -1140,11 +1141,11 @@ public class MyClass: Superclass { } ``` -This change is backward-compatible with the rest of the proposal. Property delegate types could provide any (non-empty) subset of the three ways to access the underlying value: +This change is backward-compatible with the rest of the proposal. Property wrapper types could provide any (non-empty) subset of the three ways to access the underlying value: * For instance properties, `subscript(instanceSelf:)` as shown above. * For static or class properties, `subscript(typeSelf:)`, similar to the above but accepting a metatype parameter. -* For global/local properties, or when the appropriate `subscript` mentioned above isn't provided by the delegate type, the `value` property would be used. +* For global/local properties, or when the appropriate `subscript` mentioned above isn't provided by the wrapper type, the `value` property would be used. The main challenge with this design is that it doesn't directly work when the enclosing type is a value type and the property is settable. In such cases, the parameter to the subscript would get a copy of the entire enclosing value, which would not allow mutation, On the other hand, one could try to pass `self` as `inout`, e.g., @@ -1161,21 +1162,25 @@ public struct MyStruct { } ``` -There are a few issues here: first, subscripts don't allow `inout` parameters in the first place, so we would have to figure out how to implement support for such a feature. Second, passing `self` as `inout` while performing access to the property `self.myVar` violates Swift's exclusivity rules ([generalized accessors](https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md#generalized-accessors) might help address this). Third, property delegate types that want to support `subscript(instanceSelf:)` for both value and reference types would have to overload on `inout` or would have to have a different subscript name (e.g., `subscript(mutatingInstanceSelf:)`). +There are a few issues here: first, subscripts don't allow `inout` parameters in the first place, so we would have to figure out how to implement support for such a feature. Second, passing `self` as `inout` while performing access to the property `self.myVar` violates Swift's exclusivity rules ([generalized accessors](https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md#generalized-accessors) might help address this). Third, property wrapper types that want to support `subscript(instanceSelf:)` for both value and reference types would have to overload on `inout` or would have to have a different subscript name (e.g., `subscript(mutatingInstanceSelf:)`). So, while we feel that support for accessing the enclosing type's `self` is useful and as future direction, and this proposal could be extended to accommodate it, the open design questions are significant enough that we do not want to tackle them all in a single proposal. ### Delegating to an existing property -When specifying a delegate for a property, the synthesized storage property is implicitly created. However, it is possible that there already exists a property that can provide the storage. One could provide a form of property delegation that creates the getter/setter to forward to an existing property, e.g.: +When specifying a wrapper for a property, the synthesized storage property is implicitly created. However, it is possible that there already exists a property that can provide the storage. One could provide a form of property delegation that creates the getter/setter to forward to an existing property, e.g.: ```swift -lazy var fooBacking: SomeDelegate -@delegate(to: fooBacking) var foo: Int +lazy var fooBacking: Somewrapper +@wrapper(to: fooBacking) var foo: Int ``` One could express this either by naming the property directly (as above) or, for an even more general solution, by providing a keypath such as `\.someProperty.someOtherProperty`. +## Changes from the first reviewed version + +* The name of the feature has been changed from "property delegates" to "property wrappers" to better communicate how they work and avoid the existing uses of the term "delegate" in the Apple developer community + ## Acknowledgments -This proposal was greatly improved throughout its [first pitch](https://forums.swift.org/t/pitch-property-delegates/21895) by many people. Harlan Haskins, Brent Royal-Gordon, Adrian Zubarev, Jordan Rose and others provided great examples of uses of property delegates (several of which are in this proposal). Adrian Zubarev and Kenny Leung helped push on some of the core assumptions and restrictions of the original proposal, helping to make it more general. Vini Vendramini and David Hart helped tie this proposal together with custom attributes, which drastically reduced the syntactic surface area of this proposal. +This proposal was greatly improved throughout its [first pitch](https://forums.swift.org/t/pitch-property-delegates/21895) by many people. Harlan Haskins, Brent Royal-Gordon, Adrian Zubarev, Jordan Rose and others provided great examples of uses of property wrappers (several of which are in this proposal). Adrian Zubarev and Kenny Leung helped push on some of the core assumptions and restrictions of the original proposal, helping to make it more general. Vini Vendramini and David Hart helped tie this proposal together with custom attributes, which drastically reduced the syntactic surface area of this proposal. From 8c24d2b0ba043b12e1e3421811a92f6eee90826d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 May 2019 20:53:59 -0700 Subject: [PATCH 1183/4563] [SE-0258] Add implicit initialization via wrapper's init(). David Hart suggested this a while ago, and it makes a lot of sense. Existing examples (like DelayedMutable and DelayedImmutable) benefit, as do other future-looking ideas (e.g., replacing things like @IBOutlet). --- proposals/0258-property-wrappers.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 8927c34f30..91bd8db554 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -225,7 +225,7 @@ struct DelayedMutable { } } - /// "Reset" the wrappe so it can be initialized again. + /// "Reset" the wrapper so it can be initialized again. mutating func reset() { _value = nil } @@ -264,7 +264,7 @@ This enables multi-phase initialization, like this: ```swift class Foo { - @DelayedImmutable() var x: Int + @DelayedImmutable var x: Int init() { // We don't know "x" yet, and we don't have to set it @@ -591,7 +591,7 @@ wrapper instance. Introducing a property wrapper to a property makes that property computed (with a getter/setter) and introduces a stored property whose type is the wrapper type. That stored property can be initialized -in one of two ways: +in one of three ways: 1. Via a value of the original property's type (e.g., `Int` in `@Lazy var foo: Int`, using the the property wrapper type's @@ -625,6 +625,16 @@ in one of two ways: var someInt: Int { /* access via $someInt.value */ } ``` +3. Implicitly, when no initializer is provided and the property wrapper type provides no-parameter initializer (`init()`). In such cases, the wrapper type's `init()` will be invoked to initialize the stored property. + + ```swift + @DelayedMutable var x: Int + + // ... implemented as + var $x: DelayedMutable = DelayedMutable() + var x: Int { /* access via $x.value */ } + ``` + ### Type inference with wrappers Type inference for properties with wrappers involves both the type @@ -1171,7 +1181,7 @@ So, while we feel that support for accessing the enclosing type's `self` is usef When specifying a wrapper for a property, the synthesized storage property is implicitly created. However, it is possible that there already exists a property that can provide the storage. One could provide a form of property delegation that creates the getter/setter to forward to an existing property, e.g.: ```swift -lazy var fooBacking: Somewrapper +lazy var fooBacking: SomeWrapper @wrapper(to: fooBacking) var foo: Int ``` @@ -1180,6 +1190,7 @@ One could express this either by naming the property directly (as above) or, for ## Changes from the first reviewed version * The name of the feature has been changed from "property delegates" to "property wrappers" to better communicate how they work and avoid the existing uses of the term "delegate" in the Apple developer community +* When a property wrapper type has a no-parameter `init()`, properties that use that wrapper type will be implicitly initialized via `init()`. ## Acknowledgments From 9eaae5dd100b719241583c19a6e97a63cd32c130 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 May 2019 22:00:30 -0700 Subject: [PATCH 1184/4563] [SE-0258] Add support for composition --- proposals/0258-property-wrappers.md | 54 +++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 91bd8db554..5ee06c11db 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -568,6 +568,27 @@ $myValue.accept(42) // set a new value via the synthesized storage property print(myValue) // print the most recent value ``` +## Composition of property wrappers + +When multiple property wrappers are provided for a given property, +the wrappers are composed together to get both effects. For example, consider the composition of `DelayedMutable` and `Copying`: + +```swift +@DelayedMutable @Copying var path: UIBezierPath +``` + +Here, we have a property for which we can delay initialization until later. When we do set a value, it will be copied via `NSCopying`'s `copy` method. + +Composition is implemented by nesting later wrapper types inside earlier wrapper types, where the innermost nested type is the original property's type. For the example above, the backing storage will be of type `DelayedMutable>`, and the synthesized getter/setter for `path` will look through both levels of `.value`: + +```swift +var $path: DelayedMutable> = .init() +var path: UIBezierPath { + get { return $path.value.value } + set { $path.value.value = newValue } +} +``` + ## Detailed design ### Property wrapper types @@ -609,7 +630,15 @@ in one of three ways: var $foo: Lazy = Lazy(initialValue: 17) var foo: Int { /* access via $foo.value as described above */ } ``` - + When there are multiple, composed property wrappers, all of them must provide an `init(initialValue:)`, and the resulting initialization will wrap each level of call: + + ```swift + @Lazy @Copying var path = UIBezierPath() + + // ... implemented as + var $path: Lazy> = .init(initialValue: .init(initialValue: UIBezierPath())) + var path: UIBezierPath { /* access via $path.value.value as described above */ } + ``` 2. Via a value of the property wrapper type, by placing the initializer arguments after the property wrapper type: @@ -625,6 +654,8 @@ in one of three ways: var someInt: Int { /* access via $someInt.value */ } ``` + When there are multiple, composed property wrappers, only the first (outermost) wrapper may have initializer arguments. + 3. Implicitly, when no initializer is provided and the property wrapper type provides no-parameter initializer (`init()`). In such cases, the wrapper type's `init()` will be invoked to initialize the stored property. ```swift @@ -635,6 +666,8 @@ in one of three ways: var x: Int { /* access via $x.value */ } ``` + When there are multiple, composed property wrappers, only the first (outermost) wrapper needs to have an `init()`. + ### Type inference with wrappers Type inference for properties with wrappers involves both the type @@ -999,21 +1032,21 @@ the prior proposal are: * Behaviors were introduced into a property with the `[behavior]` syntax, rather than the attribute syntax described here. See the property behaviors proposal for more information. -* wrappers are always expressed by a (generic) type. Property behaviors +* Wrappers are always expressed by a (generic) type. Property behaviors had a new kind of declaration (introduced by the `behavior` keyword). Having a new kind of declaration allowed for the introduction of specialized syntax, but it also greatly increased the surface area (and implementation cost) of the proposal. Using a generic type makes property wrappers more of a syntactic-sugar feature that is easier to implement and explain. -* wrappers cannot declare new kinds of accessors (e.g., the +* Wrappers cannot declare new kinds of accessors (e.g., the `didChange` example from the property behaviors proposal). -* wrappers used for properties declared within a type cannot refer to +* Wrappers used for properties declared within a type cannot refer to the `self` of their enclosing type. This eliminates some use cases (e.g., implementing a `Synchronized` property wrapper type that uses a lock defined on the enclosing type), but simplifies the design. -* wrappers can be initialized out-of-line, and one +* Wrappers can be initialized out-of-line, and one can use the `$`-prefixed name to refer to the storage property. These were future directions in the property behaviors proposal. @@ -1035,16 +1068,6 @@ public public(storage) var foo: Int = 1738 public private(storage) var bar: Int = 1738 ``` -### Composition of property wrappers - -Only one property wrapper can currently be attached to a given property, so code such as the following is in error: - -```swift -@UnsafeMutablePointer @Atomic var x: Int // error: two attached property wrappers -``` - -The [property behaviors proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md#composing-behaviors) describes some of the concerns surrounding composing behaviors/wrappers in this way, which would need to be addressed by a future proposal. Matthew Johnson [sketched out a potential direction](https://forums.swift.org/t/pitch-property-delegates/21895/103) for extending property wrappers to support composition of property wrappers. - ### Referencing the enclosing 'self' in a wrapper type Manually-written getters and setters for properties declared in a type often refer to the `self` of their enclosing type. For example, this can be used to notify clients of a change to a property's value: @@ -1191,6 +1214,7 @@ One could express this either by naming the property directly (as above) or, for * The name of the feature has been changed from "property delegates" to "property wrappers" to better communicate how they work and avoid the existing uses of the term "delegate" in the Apple developer community * When a property wrapper type has a no-parameter `init()`, properties that use that wrapper type will be implicitly initialized via `init()`. +* Support for property wrapper composition has been added, using a "nesting" model. ## Acknowledgments From 5f2ae62a5f629ee93348d234517706d83deec154 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 20 May 2019 10:50:26 -0700 Subject: [PATCH 1185/4563] A property with a wrapper cannot declare an explicit get or set. --- proposals/0258-property-wrappers.md | 33 ++++------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 5ee06c11db..bee6faad88 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -863,35 +863,6 @@ print($x) // okay to refer to compiler-defined $x let $y = 17 // error: cannot declare entity with $-prefixed name '$y' ``` -### Accessors - -A property with a wrapper can declare accessors explicitly (`get`, `set`, `didSet`, `willSet`). If either `get` or `set` is missing, it will be implicitly created by accessing the storage property (e.g., `$foo`). For example: - -```swift -@Lazy var x = 17 { - set { - $x.value = newValue * 2 - } - - // implicitly synthesized... - get { return $x.value } -} - - -@Lazy var y = 42 { - didSet { - print("Overrode lazy value with \(oldValue)") - } - - // implicitly synthesized... - set { - let oldValue = $y.value - $y.value = newValue - didSet(oldValue) - } -} -``` - ### Delegating access to the storage property A property wrapper type can choose to hide its instance entirely by providing a property named `wrapperValue`. As with the `value` property and`init(initialValue:)`, the `wrapperValue` property must have the @@ -950,7 +921,10 @@ There are a number of restrictions on the use of property wrappers when defining `final` and cannot override another property. * A property with a wrapper cannot be `lazy`, `@NSCopying`, `@NSManaged`, `weak`, or `unowned`. * A property with a wrapper must be the only property declared within its enclosing declaration (e.g., `@Lazy var (x, y) = /* ... */` is ill-formed). +* A property with a wrapper shall not define a getter or setter. * The `value` property and (if present) `init(initialValue:)` of a property wrapper type shall have the same access as the property wrapper type. +* The `wrapperValue` property, if present, shall have the same access as the property wrapper type. +* The `init()` initializer, if present, shall have the same access as the property wrapper type. ## Impact on existing code @@ -1215,6 +1189,7 @@ One could express this either by naming the property directly (as above) or, for * The name of the feature has been changed from "property delegates" to "property wrappers" to better communicate how they work and avoid the existing uses of the term "delegate" in the Apple developer community * When a property wrapper type has a no-parameter `init()`, properties that use that wrapper type will be implicitly initialized via `init()`. * Support for property wrapper composition has been added, using a "nesting" model. +* A property with a wrapper can no longer have an explicit `get` or `set` declared, to match with the behavior of existing, similar features (`lazy`, `@NSCopying`). ## Acknowledgments From a1054c39bd54040b0fc59b10efd6018e5682aa8a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 23 May 2019 14:06:30 -0700 Subject: [PATCH 1186/4563] [SE-0258] Make private(wrapper) / public(wrapper) part of the proposal --- proposals/0258-property-wrappers.md | 31 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index bee6faad88..1afdba7534 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -724,6 +724,21 @@ The *expr-paren*, if present, provides the initialization arguments for the wrap This formulation of custom attributes fits in with a [larger proposal for custom attributes](https://forums.swift.org/t/pitch-introduce-custom-attributes/21335/47), which uses the same custom attribute syntax as the above but allows for other ways in which one can define a type to be used as an attribute. In this scheme, `@propertyWrapper` is just one kind of custom attribute: there will be other kinds of custom attributes that are available only at compile time (e.g., for tools) or runtime (via some reflection capability). +### Specifying access for the backing storage property + +Like other declarations, the synthesized storage property will be +`internal` by default, or the access level of the original property if it is less than `internal`. However, one can adjust the access of the backing storage property (the "wrapper") to make it more or less accessible using a syntax similar to that of `private(set)`: + +```swift +// both foo and $foo are publicly visible +@Lazy +public public(wrapper) var foo: Int = 1738 + +// bar is publicly visible, $bar is privately visible +@Atomic +public private(wrapper) var bar: Int = 1738 +``` + ### Mutability of properties with wrappers Generally, a property that has a property wrapper will have both a getter and a setter. However, the setter may be missing if the `value` property of the property wrapper type lacks a setter, or its setter is inaccessible. @@ -1027,21 +1042,6 @@ the prior proposal are: ## Future Directions -### Specifying access for the storage property - -Like other declarations, the synthesized storage property will be -`internal` by default, or the access level of the original property if it is less than `internal`. However, we could provide separate access control for the storage property to make it more or less accessible using a syntax similar to that of `private(set)`: - -```swift -// both foo and $foo are publicly visible -@Lazy -public public(storage) var foo: Int = 1738 - -// bar is publicly visible, $bar is privately visible -@Atomic -public private(storage) var bar: Int = 1738 -``` - ### Referencing the enclosing 'self' in a wrapper type Manually-written getters and setters for properties declared in a type often refer to the `self` of their enclosing type. For example, this can be used to notify clients of a change to a property's value: @@ -1190,6 +1190,7 @@ One could express this either by naming the property directly (as above) or, for * When a property wrapper type has a no-parameter `init()`, properties that use that wrapper type will be implicitly initialized via `init()`. * Support for property wrapper composition has been added, using a "nesting" model. * A property with a wrapper can no longer have an explicit `get` or `set` declared, to match with the behavior of existing, similar features (`lazy`, `@NSCopying`). +* Added support for adjusting the accessibility of the backing storage property via, e.g., `private(wrapper)` or `public(wrapper)`. This was part of "future directions." ## Acknowledgments From 24eba64003700456af160612a3cb326dae20bd87 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 23 May 2019 14:39:39 -0700 Subject: [PATCH 1187/4563] [SE-0258] Removed the restriction banning property wrappers from having names that match the regular expression `_*[a-z].*`. --- proposals/0258-property-wrappers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 1afdba7534..2333a3341c 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -601,8 +601,7 @@ type: `@propertyWrapper`. The attribute indicates that the type is meant to be used as a property wrapper type, and provides a point at which the compiler can verify any other consistency rules. -2. The name of the property wrapper type must not match the regular expression `_*[a-z].*`. Such names are reserved for compiler-defined attributes. -3. The property wrapper type must have a property named `value`, whose +2. The property wrapper type must have a property named `value`, whose access level is the same as that of the type itself. This is the property used by the compiler to access the underlying value on the wrapper instance. @@ -1191,6 +1190,7 @@ One could express this either by naming the property directly (as above) or, for * Support for property wrapper composition has been added, using a "nesting" model. * A property with a wrapper can no longer have an explicit `get` or `set` declared, to match with the behavior of existing, similar features (`lazy`, `@NSCopying`). * Added support for adjusting the accessibility of the backing storage property via, e.g., `private(wrapper)` or `public(wrapper)`. This was part of "future directions." +* Removed the restriction banning property wrappers from having names that match the regular expression `_*[a-z].*`. ## Acknowledgments From 21c60933ac201fe919cd1799677538cd853594cf Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 23 May 2019 14:44:40 -0700 Subject: [PATCH 1188/4563] [SE-0258] Minor cleanups to the proposal --- proposals/0258-property-wrappers.md | 42 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 2333a3341c..f7c7bdfae0 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -723,21 +723,6 @@ The *expr-paren*, if present, provides the initialization arguments for the wrap This formulation of custom attributes fits in with a [larger proposal for custom attributes](https://forums.swift.org/t/pitch-introduce-custom-attributes/21335/47), which uses the same custom attribute syntax as the above but allows for other ways in which one can define a type to be used as an attribute. In this scheme, `@propertyWrapper` is just one kind of custom attribute: there will be other kinds of custom attributes that are available only at compile time (e.g., for tools) or runtime (via some reflection capability). -### Specifying access for the backing storage property - -Like other declarations, the synthesized storage property will be -`internal` by default, or the access level of the original property if it is less than `internal`. However, one can adjust the access of the backing storage property (the "wrapper") to make it more or less accessible using a syntax similar to that of `private(set)`: - -```swift -// both foo and $foo are publicly visible -@Lazy -public public(wrapper) var foo: Int = 1738 - -// bar is publicly visible, $bar is privately visible -@Atomic -public private(wrapper) var bar: Int = 1738 -``` - ### Mutability of properties with wrappers Generally, a property that has a property wrapper will have both a getter and a setter. However, the setter may be missing if the `value` property of the property wrapper type lacks a setter, or its setter is inaccessible. @@ -745,36 +730,39 @@ Generally, a property that has a property wrapper will have both a getter and a The synthesized getter will be `mutating` if the property wrapper type's `value` property is `mutating` and the property is part of a `struct`. Similarly, the synthesized setter will be `nonmutating` if either the property wrapper type's `value` property has a `nonmutating` setter or the property wrapper type is a `class`. For example: ```swift -struct MutatingGetterwrapper { +@propertyWrapper +struct MutatingGetterWrapper { var value: Value { mutating get { ... } set { ... } } } -struct NonmutatingSetterwrapper { +@propertyWrapper +struct NonmutatingSetterWrapper { var value: Value { get { ... } nonmutating set { ... } } } -class Referencewrapper { +@propertyWrapper +class ReferenceWrapper { var value: Value } struct Usewrappers { // x's getter is mutating // x's setter is mutating - @MutatingGetterwrapper var x: Int + @MutatingGetterWrapper var x: Int // y's getter is nonmutating // y's setter is nonmutating - @NonmutatingSetterwrapper var y: Int + @NonmutatingSetterWrapper var y: Int // z's getter is nonmutating // z's setter is nonmutating - @Referencewrapper var z: Int + @ReferenceWrapper var z: Int } ``` @@ -816,7 +804,17 @@ x2 = 42 // okay, treated as x2 = 42 (calls the Lazy.value setter) ### Access to the storage property -By default, the synthesized storage property will have `internal` access or the access of the original property, whichever is more restrictive. +By default, the synthesized storage property will have `internal` access or the access of the original property, whichever is more restrictive. However, one can adjust the access of the backing storage property (the "wrapper") to make it more or less accessible using a syntax similar to that of `private(set)`: + +```swift +// both foo and $foo are publicly visible +@Lazy +public public(wrapper) var foo: Int = 1738 + +// bar is publicly visible, $bar is privately visible +@Atomic +public private(wrapper) var bar: Int = 1738 +``` ### Memberwise initializers From d933aa6d929c947411d2c1390bb4ae8b44a89731 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 23 May 2019 14:57:20 -0700 Subject: [PATCH 1189/4563] [SE-0258] Make Codable/Hashable/Equatable synthesis based on backing storage This model is simpler and gives more control to property wrapper type authors. --- proposals/0258-property-wrappers.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index f7c7bdfae0..4d02810bc2 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -858,10 +858,10 @@ struct Foo { } ``` +### Codable, Hashable, and Equatable synthesis + Synthesis for `Encodable`, `Decodable`, `Hashable`, and `Equatable` -using the underlying `value` of the property when the property -wrapper type contains an `init(initialValue:)` and the wrapper's -type otherwise. +use the backing storage property. This allows property wrapper types to determine their own serialization and equality behavior. ### $ identifiers @@ -1189,6 +1189,7 @@ One could express this either by naming the property directly (as above) or, for * A property with a wrapper can no longer have an explicit `get` or `set` declared, to match with the behavior of existing, similar features (`lazy`, `@NSCopying`). * Added support for adjusting the accessibility of the backing storage property via, e.g., `private(wrapper)` or `public(wrapper)`. This was part of "future directions." * Removed the restriction banning property wrappers from having names that match the regular expression `_*[a-z].*`. +* `Codable`, `Hashable`, and `Equatable` synthesis are now based on the backing storage properties, which is a simpler model that gives more control to the authors of property wrapper types. ## Acknowledgments From 95eaa3c1aa88be89d098fed4aa68d57c058f7cb8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 23 May 2019 15:59:21 -0700 Subject: [PATCH 1190/4563] [SE-0258] Fix more typos --- proposals/0258-property-wrappers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 4d02810bc2..a8e6031ee4 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -960,19 +960,19 @@ The property wrappers language feature as proposed has no impact on the ABI or r ### Using a formal protocol instead of `@propertyWrapper` -Instead of a new attribute, we could introduce a `Propertywrapper` +Instead of a new attribute, we could introduce a `PropertyWrapper` protocol to describe the semantic constraints on property wrapper types. It might look like this: ```swift -protocol Propertywrapper { +protocol PropertyWrapper { associatedtype Value var value: Value { get } } ``` There are a few issues here. First, a single protocol -`Propertywrapper` cannot handle all of the variants of `value` that +`PropertyWrapper` cannot handle all of the variants of `value` that are implied by the section on mutability of properties with wrappers, because we'd need to cope with `mutating get` as well as `set` and `nonmutating set`. Moreover, protocols don't support optional @@ -981,10 +981,10 @@ forms: one accepting a `Value` and one accepting an `@autoclosure () -> Value`) and `init()`. To cover all of these cases, we would need a several related-but-subtly-different protocols. -The second issue that, even if there were a single `Propertywrapper` +The second issue that, even if there were a single `PropertyWrapper` protocol, we don't know of any useful generic algorithms or data structures that seem to be implemented in terms of only -`Propertywrapper`. +`PropertyWrapper`. ### Kotlin-like `by` syntax From fdcdd544ea3e39046de21e5705a2991f6269218d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 28 May 2019 17:00:04 -0700 Subject: [PATCH 1191/4563] [SE-0258] Add more discussion of composition alternatives --- proposals/0258-property-wrappers.md | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index a8e6031ee4..f7a753a18a 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -589,6 +589,15 @@ var path: UIBezierPath { } ``` +Note that this design means that property wrapper composition is not commutative, because the order of the attributes affects how the nesting is performed: + +```swift +@DelayedMutable @Copying var path1: UIBezierPath // $path1 has type DelayedMutable> +@Copying @DelayedMutable var path2: UIBezierPath // error: $path2 has ill-formed type Copying> +``` + +In this case, the type checker prevents the second ordering, because `DelayedMutable` does not conform to the `NSCopying` protocol. This won't always be the case: some semantically-bad compositions won't necessarily by caught by the type system. Alternatives to this approach to composition are presented in "Alternatives considered." + ## Detailed design ### Property wrapper types @@ -958,6 +967,52 @@ The property wrappers language feature as proposed has no impact on the ABI or r ## Alternatives considered +### Composition + +Composition was left out of the [first revision](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) of this proposal, because one can manually compose property wrapper types. For example, the composition `@A @B` could be implemented as an `AB` wrapper: + +```swift +@propertyWrapper +struct AB { + private var storage: A> + + var value: Value { + get { storage.value.value } + set { storage.value.value = newValue } + } +} +``` + +The main benefit of this approach is its predictability: the author of `AB` decides how to best achieve the composition of `A` and `B`, names it appropriately, and provides the right API and documentation of its semantics. On the other hand, having to manually write out each of the compositions is a lot of boilerplate, particularly for a feature whose main selling point is the elimination of boilerplate. It is also unfortunate to have to invent names for each composition---when I try the compose `A` and `B` via `@A @B`, how do I know to go look for the manually-composed property wrapper type `AB`? Or maybe that should be `BA`? + +### Composition via nested type lookup +One proposed approach to composition addresses only the last issue above directly, treating the attribute-composition syntax `@A @B` as a lookup of the nested type `B` inside `A` to find the wrapper type: + +```swift +@propertyWrapper +struct A { + var value: Value { ... } +} + +extension A { + typealias B = AB +} +``` + +This allows the natural composition syntax `@A @B` to work, redirecting to manually-written property wrappers that implement the proper semantics and API. Additionally, this scheme allows one to control which compositions are valid: if there is no nested type `B` in `A`, the composition is invalid. If both `A.B` and `B.A` exist, we have a choice: either enforce commutative semantics as part of the language (`B.A` and `A.B` must refer to the same type or the composition `@A @B` is ill-formed), or allow them to differ (effectively matching the semantics of this proposal). + +This approach addresses the syntax for composition while maintaining control over the precise semantics of composition via manually-written wrapper types. However, it does not address the boilerplate problem. + +### Composition without nesting + +There has been a desire to effect composition of property wrappers without having to wrap one property wrapper type in the other. For example, to have `@A @B` apply the policies of both `A` and `B` without producing a nested type like `A>`. This would make potentially make composition more commutative, at least from the type system perspective. However, this approach does not fit with the "wrapper" approach taken by property wrappers. In a declaration + +```swift +@A @B var x: Int +``` + +the `Int` value is conceptually wrapped by a property wrapper type, and the property wrapper type's `value` property guards access to that (conceptual) `Int` value. That `Int` value cannot be wrapped both by instances of both `A` and `B` without either duplicating data (both `A` and `B` have a copy of the `Int`) or nesting one of the wrappers inside the other. With the copying approach, one must maintain consistency between the copies (which is particularly hard when value types are involved) and there will still be non-commutative compositions. Nesting fits better with the "wrapper" model of property wrappers. + ### Using a formal protocol instead of `@propertyWrapper` Instead of a new attribute, we could introduce a `PropertyWrapper` From bf6c1ac0f6e2e53bcbc3da9ba1837abfb03a70a3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 29 May 2019 09:21:12 -0700 Subject: [PATCH 1192/4563] [SE-0258] Note that this is a hidden feature on 'master' --- proposals/0258-property-wrappers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index f7a753a18a..81ac64f80f 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Returned for revision** -* Implementation: [PR #23701](https://github.com/apple/swift/pull/23701), [Ubuntu 16.04 Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/233/artifact/branch-master/swift-PR-23701-233-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/300/artifact/branch-master/swift-PR-23701-300-osx.tar.gz) +* Implementation: Available as a "hidden" feature in [master snapshots](https://swift.org/download/#releases) with the attribute name `@_propertyDelegate` and using `delegateValue` in lieu of `wrapperValue`. * Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) * Previous versions: [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) From e7390faa80ddbb9091558385ff1a656545f0cc7a Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 29 May 2019 00:31:31 -0400 Subject: [PATCH 1193/4563] Editorial fixes --- proposals/0258-property-wrappers.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 81ac64f80f..ee800aeae3 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -285,7 +285,7 @@ class Foo { Many Cocoa classes implement value-like objects that require explicit copying. Swift currently provides an `@NSCopying` attribute for properties to give them behavior like Objective-C's `@property(copy)`, invoking the `copy` method -on new objects when the property is set. We can turn this into a wrappe: +on new objects when the property is set. We can turn this into a wrapper: ```swift @propertyWrapper @@ -662,9 +662,9 @@ in one of three ways: var someInt: Int { /* access via $someInt.value */ } ``` - When there are multiple, composed property wrappers, only the first (outermost) wrapper may have initializer arguments. + When there are multiple, composed property wrappers, only the first (outermost) wrapper may have initializer arguments. -3. Implicitly, when no initializer is provided and the property wrapper type provides no-parameter initializer (`init()`). In such cases, the wrapper type's `init()` will be invoked to initialize the stored property. +3. Implicitly, when no initializer is provided and the property wrapper type provides a no-parameter initializer (`init()`). In such cases, the wrapper type's `init()` will be invoked to initialize the stored property. ```swift @DelayedMutable var x: Int From c2a444c57f6e3e7dee0de95f4efc9d991625c717 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 29 May 2019 09:25:28 -0700 Subject: [PATCH 1194/4563] [SE-0258] Fix link to snapshots --- proposals/0258-property-wrappers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index ee800aeae3..f6a258ffa9 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Returned for revision** -* Implementation: Available as a "hidden" feature in [master snapshots](https://swift.org/download/#releases) with the attribute name `@_propertyDelegate` and using `delegateValue` in lieu of `wrapperValue`. +* Implementation: Available as a "hidden" feature in [master snapshots](https://swift.org/download/#snapshots) with the attribute name `@_propertyDelegate` and using `delegateValue` in lieu of `wrapperValue`. * Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) * Previous versions: [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) From d0854a606c68d7b6e0796502ca016ed9d49585cd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 29 May 2019 14:13:04 -0700 Subject: [PATCH 1195/4563] [SE-0258] Make it clear that property wrappers can have willSet/didSet. --- proposals/0258-property-wrappers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index f6a258ffa9..74754a13b3 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -192,7 +192,7 @@ enum GlobalSettings { } ``` -Property wrappers can be applied to properties at global, local, or type scope. +Property wrappers can be applied to properties at global, local, or type scope. Those properties can have observing accessors (`willSet`/`didSet`), but not explicitly-written getters or setters. ## Examples From 643ed7b6c31c47752bf93d9fb30f60d170a8d266 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 29 May 2019 14:13:36 -0700 Subject: [PATCH 1196/4563] [SE-0258] Count properly. --- proposals/0258-property-wrappers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 74754a13b3..35bb9e5304 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -603,7 +603,7 @@ In this case, the type checker prevents the second ordering, because `DelayedMut ### Property wrapper types A *property wrapper type* is a type that can be used as a property -wrapper. There are three basic requirements for a property wrapper +wrapper. There are two basic requirements for a property wrapper type: 1. The property wrapper type must be defined with the attribute From f4b4943bbc7904eddd57030e95e5b625ce14de69 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 29 May 2019 14:32:41 -0700 Subject: [PATCH 1197/4563] [SE-0258] Lower default access of synthesized storage to 'private'. Move other access-control bits to "future directions." It's too much to tackle in this first proposal. --- proposals/0258-property-wrappers.md | 45 ++++++++++++++++++----------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 35bb9e5304..b31d372998 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -811,20 +811,6 @@ x2 = 17 // okay, treated as $x2 = .init(initialValue: 17) x2 = 42 // okay, treated as x2 = 42 (calls the Lazy.value setter) ``` -### Access to the storage property - -By default, the synthesized storage property will have `internal` access or the access of the original property, whichever is more restrictive. However, one can adjust the access of the backing storage property (the "wrapper") to make it more or less accessible using a syntax similar to that of `private(set)`: - -```swift -// both foo and $foo are publicly visible -@Lazy -public public(wrapper) var foo: Int = 1738 - -// bar is publicly visible, $bar is privately visible -@Atomic -public private(wrapper) var bar: Int = 1738 -``` - ### Memberwise initializers Structs implicitly declare memberwise initializers based on the stored @@ -1094,6 +1080,31 @@ the prior proposal are: ## Future Directions +### Access to the storage property + +By default, the synthesized storage property will have `private` access. However, there are various circumstances where it would be beneficial to expose the synthesized storage property. This could be performed "per-property", e.g., by introducing a syntax akin to `private(set)`: + +```swift +// both foo and $foo are publicly visible +@Atomic +public public(wrapper) var foo: Int = 1738 +``` + +One could also consider having the property wrapper types themselves declare that the synthesized storage properties for properties using those wrappers should have the same access as the original property. For example: + +```swift +@propertyWrapper(wrapperIsAccessible: true) +struct Atomic { + var value: T { ... } +} + +// both bar and $bar are publicly visible +@Atomic +public var bar: Int = 1738 +``` + +The two features could also be combined, allowing property wrapper types to provide the default behavior and the `access-level(wrapper)` syntax to change the default. The current proposal's `private`-by-default is meant to be a conservative first step to allow a separate exploration into expanding the visibility of the backing storage. + ### Referencing the enclosing 'self' in a wrapper type Manually-written getters and setters for properties declared in a type often refer to the `self` of their enclosing type. For example, this can be used to notify clients of a change to a property's value: @@ -1192,7 +1203,7 @@ public class MyClass: Superclass { @Observable public var myVar: Int = 17 // desugars to... - internal var $myVar: Observable = Observable(initialValue: 17) + private var $myVar: Observable = Observable(initialValue: 17) public var myVar: Int { get { return $myVar[instanceSelf: self] } set { $myVar[instanceSelf: self] = newValue } @@ -1213,7 +1224,7 @@ public struct MyStruct { @Observable public var myVar: Int = 17 // desugars to... - internal var $myVar: Observable = Observable(initialValue: 17) + private var $myVar: Observable = Observable(initialValue: 17) public var myVar: Int { get { return $myVar[instanceSelf: self] } set { $myVar[instanceSelf: &self] = newValue } @@ -1242,7 +1253,7 @@ One could express this either by naming the property directly (as above) or, for * When a property wrapper type has a no-parameter `init()`, properties that use that wrapper type will be implicitly initialized via `init()`. * Support for property wrapper composition has been added, using a "nesting" model. * A property with a wrapper can no longer have an explicit `get` or `set` declared, to match with the behavior of existing, similar features (`lazy`, `@NSCopying`). -* Added support for adjusting the accessibility of the backing storage property via, e.g., `private(wrapper)` or `public(wrapper)`. This was part of "future directions." +* Reduced the visibility of the synthesized storage property to `private`, and expanded upon potential future directions to making it more visible. * Removed the restriction banning property wrappers from having names that match the regular expression `_*[a-z].*`. * `Codable`, `Hashable`, and `Equatable` synthesis are now based on the backing storage properties, which is a simpler model that gives more control to the authors of property wrapper types. From 07d9771121cd436e551a7155e318a77a039c4806 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 29 May 2019 14:34:42 -0700 Subject: [PATCH 1198/4563] [SE-0258] Clarify that the $ isn't part of the name for Codable. --- proposals/0258-property-wrappers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index b31d372998..d721e4950b 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -856,7 +856,7 @@ struct Foo { ### Codable, Hashable, and Equatable synthesis Synthesis for `Encodable`, `Decodable`, `Hashable`, and `Equatable` -use the backing storage property. This allows property wrapper types to determine their own serialization and equality behavior. +use the backing storage property. This allows property wrapper types to determine their own serialization and equality behavior. For `Encodable` and `Decodable`, the name used for keyed archiving is that of the original property declaration (without the `$`). ### $ identifiers From 77d5d35b587c606ce55b0d035deda548082b40f7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 29 May 2019 14:35:41 -0700 Subject: [PATCH 1199/4563] [SE-0258] A property with a wrapper need not be 'final'. --- proposals/0258-property-wrappers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index d721e4950b..1525062684 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -924,8 +924,7 @@ There are a number of restrictions on the use of property wrappers when defining * A property with a wrapper may not declared inside a protocol. * An instance property with a wrapper may not declared inside an extension. * An instance property may not be declared in an `enum`. -* A property with a wrapper that is declared within a class must be -`final` and cannot override another property. +* A property with a wrapper that is declared within a class cannot override another property. * A property with a wrapper cannot be `lazy`, `@NSCopying`, `@NSManaged`, `weak`, or `unowned`. * A property with a wrapper must be the only property declared within its enclosing declaration (e.g., `@Lazy var (x, y) = /* ... */` is ill-formed). * A property with a wrapper shall not define a getter or setter. @@ -1253,6 +1252,7 @@ One could express this either by naming the property directly (as above) or, for * When a property wrapper type has a no-parameter `init()`, properties that use that wrapper type will be implicitly initialized via `init()`. * Support for property wrapper composition has been added, using a "nesting" model. * A property with a wrapper can no longer have an explicit `get` or `set` declared, to match with the behavior of existing, similar features (`lazy`, `@NSCopying`). +* A property with a wrapper does not need to be `final`. * Reduced the visibility of the synthesized storage property to `private`, and expanded upon potential future directions to making it more visible. * Removed the restriction banning property wrappers from having names that match the regular expression `_*[a-z].*`. * `Codable`, `Hashable`, and `Equatable` synthesis are now based on the backing storage properties, which is a simpler model that gives more control to the authors of property wrapper types. From 2fab76a6a622b3a05a3e304db971aea9ed28dd2d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 29 May 2019 15:25:52 -0700 Subject: [PATCH 1200/4563] [SE-0258] Add a little more justification for wrapperValue. --- proposals/0258-property-wrappers.md | 30 +++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 1525062684..49aa3599fa 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -917,6 +917,36 @@ let world = $someValue.move() // take value directly from the storage $someValue.initialize(to: "New value") ``` +There could then be other kinds of storage (e.g., some arena-based storage) described as property wrappers that *also* vend their wrapper types as `UnsafeMutablePointer`: + +```swift +@propertyWrapper +struct ArenaStorage { + let pointer: UnsafeMutablePointer + + init(arena: StorageArena, initialValue: Value) { + pointer = arena.allocate(Value.self) + pointer.initialize(to: initialValue) + } + + var value: Value { + get { return pointer.pointee } + set { pointer.pointee = newValue } + } + + var wrapperValue: UnsafeMutablePointer { + return pointer + } +} +``` + +The `someValue` variable from the previous example could be switched over to use arena-based storage without changing any of the clients of `someValue` or its wrapper property `$someValue`: + +```swift +@ArenaStorage(arena: currentConnectionArena, initialValue: "Hello") +var someValue: String +``` + ### Restrictions on the use of property wrappers There are a number of restrictions on the use of property wrappers when defining a property: From eaebbbd98ac98b30dca11debece0752c8ce1663e Mon Sep 17 00:00:00 2001 From: Ding Ye Date: Thu, 30 May 2019 14:47:42 +1000 Subject: [PATCH 1201/4563] Init proposal for adding 'isPower(of:)' to 'BinaryInteger' --- proposals/0000-binaryinteger-ispower.md | 265 ++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 proposals/0000-binaryinteger-ispower.md diff --git a/proposals/0000-binaryinteger-ispower.md b/proposals/0000-binaryinteger-ispower.md new file mode 100644 index 0000000000..cc308494de --- /dev/null +++ b/proposals/0000-binaryinteger-ispower.md @@ -0,0 +1,265 @@ + +# Adding `isPower(of:)` to `BinaryInteger` + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Ding Ye](https://github.com/dingobye) +* Review Manager: TBD +* Status: [apple/swift#24766](https://github.com/apple/swift/pull/24766) + + +## Introduction + +Checking some mathematical properties of integers (e.g. parity, divisibility, etc.) is widely used in scientific and engineering applications. Swift brings a lot of convenience when performing such checks, owing to the relevant methods (e.g. `isMultiple(of:)`) provided by the standard library. However there are still some other cases not yet supported. One of those useful checks that are currently missing is to tell if an integer is power of another, of which the implementation is non-trivial. Apart from inconvenience, user-implemented code can bring inefficiency, poor readability, and even incorrectness. To address this problem, this proposal would like to add a public API `isPower(of:)`, as an extension method, to the `BinaryInteger` protocol. + +Swift-evolution thread: [Pitch](https://forums.swift.org/t/adding-ispowerof2-to-binaryinteger/24087) + + +## Motivation + +Checking whether an integer is power of a given base is a typical integral query in a wide range of applications, and is especially common when the base value is two. A question about [How to check if a number is a power of 2](https://stackoverflow.com/questions/600293/how-to-check-if-a-number-is-a-power-of-2) on Stack Overflow grows for a decade with ~200K views. Since there are public demands for such functionality, it has been or will be officially supported by the libraries of many popular languages, such as [C++20](https://en.cppreference.com/w/cpp/numeric/ispow2), [D Programming Language](https://dlang.org/library/std/math/is_power_of2.html), and [.NET Framework](https://docs.microsoft.com/en-us/dotnet/api/system.numerics.biginteger.ispoweroftwo?view=netframework-4.8). + +Swift, as a general-purpose programming language, is also experiencing such demands covering a variety of domains, including numerical and mathematical libraries (e.g. [#1](https://github.com/jsbean/ArithmeticTools/blob/cb6dae327baf53cdf614d26e630833efa00eda3f/ArithmeticTools/IntegerExtensions.swift#L48), [#2](https://github.com/Orbifold/XAct/blob/9acad78e5571aa93fb52d88f539459effab1d5f7/XAct/Numbers.swift#L49), [#3](https://github.com/donald-pinckney/SwiftNum/blob/b92e3b964268ebf62d99f488fcdf438574974f0d/Sources/SignalProcessing/IntExtensions.swift#L12), [#4](https://github.com/dn-m/Math/blob/d1284e043377c0b924cba2ffa2ab0b9aa9dd246f/Sources/Math/IntegerExtensions.swift#L48)), programming language and compiler components (e.g. [#1](https://github.com/kai-language/kai/blob/41268660a01e0d6d1f0ac8de743d91700707135e/Sources/Core/Helpers/Helpers.swift#L205), [#2](https://github.com/llvm-swift/LLVMSwift/blob/162f1632e017349b17146e33c5905f88148e55f1/Sources/LLVM/Units.swift#L125)), image/video manipulation (e.g. [#1](https://github.com/schulz0r/CoreImage-HDR/blob/c7f5264929338beebfcfbf2e420594aa2952ef6c/CoreImage-HDR/Convenience/extensions.swift#L20), [#2](https://github.com/OpsLabJPL/MarsImagesIOS/blob/f2109f38b31bf1ad2e7b5aae6916da07d2d7d08e/MarsImagesIOS/Math.swift#L17), [#3](https://github.com/chingf/CarmenaCamera/blob/bfa928ca1595770c3b99bb8aa95dc5340a0f3284/VideoCapture/Common.swift#L22), [#4](https://github.com/dehancer/IMProcessing/blob/7a7d48edb7ceeb2635219c8139aa6fb8dbf1525d/IMProcessing/Classes/Common/IMPNumericTypes.swift#L132)), and other applications and utilities such as [geography kit](https://github.com/tokyovigilante/CesiumKit/blob/7983bd742a85982d9c3303cdacc039dcb44c8a42/CesiumKit/Core/Math.swift#L597), [quantum circuit simulator](https://github.com/indisoluble/SwiftQuantumComputing/blob/008e82e0f38792372df1a428884cccb74c2732b3/SwiftQuantumComputing/Extension/Int%2BIsPowerOfTwo.swift#L24), [tournament](https://github.com/eaheckert/Tournament/blob/c09c6b3634da9b2666b8b1f8990ff62bdc4fd625/Tournament/Tournament/ViewControllers/TCreateTournamentVC.swift#L215), [blockchain](https://github.com/yeeth/BeaconChain.swift/blob/954bcb6e47b51f90eff16818719320a228afe891/Sources/BeaconChain/Extensions/Int.swift#L5), [3D-engine](https://github.com/xuzhao-nick/3DEngine/blob/c3aab94f2bce5e29f7988b0d7c1e075d74076ad7/3DEngine/3DEngine/MoreMath.swift#L50), etc. + +It would be beneficial if we have it supported by the standard library, and let us discuss the impacts in the following aspects: + +### Readability + +A classic approach to check if an integer `n` is power of two is to test whether the condition `n > 0 && n & (n - 1) == 0` holds. Although such code is efficient, its underlying functionality is not intuitive to many people. As a result, programmers would have to put additional information somewhere (e.g. in the comments nearby) to make it clear. + +Below is an example when making an assertion that `bucketCount` is power of two. The classic approach is applied, followed by an additional error message for necessary clarification purpose. However, if we had the proposed API available, the code would become more fluent and concise. +```swift +// Example (1) - apple/swift/stdlib/public/core/HashTable.swift +internal struct _HashTable { + internal init(words: UnsafeMutablePointer, bucketCount: Int) { + _internalInvariant(bucketCount > 0 && bucketCount & (bucketCount - 1) == 0, + "bucketCount must be a power of two") +// _internalInvariant(bucketCount.isPower(of: 2)) --- proposed solution + ... + } +} +``` + +### Efficiency + +The user-implemented code may be less performant (e.g. [converting to floating point type and then performing logarithm](https://github.com/OpsLabJPL/MarsImagesIOS/blob/f2109f38b31bf1ad2e7b5aae6916da07d2d7d08e/MarsImagesIOS/Math.swift#L17)), since some developers are not aware of the classic approach as described above. + +The example below shows a controversial approach, which employs the `llvm.ctpop` intrinsic to count the number of 1 bits. It can be expensive when the hardware does not have relevant `popcount` instruction support. +```swift +// Example (2) - apple/swift/stdlib/public/core/Integers.swift +extension BinaryInteger { + internal func _description(radix: Int, uppercase: Bool) -> String { + // Bit shifting can be faster than division when `radix` is a power of two + let isRadixPowerOfTwo = radix.nonzeroBitCount == 1 + ... + } +} +``` + +### Abstraction + +Some developers, especially those unfamiliar to the integer type hierarchy, may have their own implementation targeting inappropriate types. + +The following example presents very similar implementation individually for `UInt` and `Int`. Such code duplication could be avoided if it targeted `BinaryInteger`. +```swift +// Example (3) - apple/swift/stdlib/public/core/Misc.swift +func _isPowerOf2(_ x: UInt) -> Bool { + // implementation +} +func _isPowerOf2(_ x: Int) -> Bool { + // implementation very similar to above +} +``` + +### Discoverability + +IDEs can aid discoverability by suggesting `isPower(of:)` as part of autocompletion on integer types. Notably, it works as a companion to other existing integral query APIs (e.g. `isMultiple(of:)`), and they can help improve the discoverability of each other. + + +## Proposed solution + +Our solution is to introduce a public API `isPower(of:)`, as an extension method, to the `BinaryInteger` protocol. It provides a standard implementation, which can be adopted by any type that conforms to this protocol. + +```swift +// In the standard library +extension BinaryInteger { + @inlinable public func isPower(of base: Self) -> Bool { + // implementation described in Detailed Design section + } +} + +// In user code +let x: Int = ... +x.isPower(of: 2) +``` + + +## Detailed design + +A reference implementation can be found in [pull request #24766](https://github.com/apple/swift/pull/24766). + +### Overall Design + +To make the API efficient for most use cases, the implementation is based on a fast-/slow-path pattern. We presents a generic implementation, which is suitable for all inputs, as the slow path; and meanwhile provide some particularly optimized implementation for frequently used inputs (e.g. 2) individually as the fast paths. The high-level solution is illustrated below. + +```swift +extension BinaryInteger { + @inlinable public func isPower(of base: Self) -> Bool { + // Fast path when base is one of the common cases. + if base == common_base_A { return self._isPowerOfCommonBaseA } + if base == common_base_B { return self._isPowerOfCommonBaseB } + if base == common_base_C { return self._isPowerOfCommonBaseC } + ... + // Slow path for other bases. + return self._slowIsPower(of: base) + } + + @inlinable internal var _isPowerOfCommonBaseA: Bool { /* optimized implementation */ } + @inlinable internal var _isPowerOfCommonBaseB: Bool { /* optimized implementation */ } + @inlinable internal var _isPowerOfCommonBaseC: Bool { /* optimized implementation */ } + ... + @usableFromInline internal func _slowIsPower(of base: Self) -> Bool { /* generic implementation */ } +} +``` +The public API `isPower(of: commonBaseK)` is expected to be as performant as the optimized version `_isPowerOfCommonBaseK`, if `commonBaseK` is a constant and the type `Self` is obvious enough (e.g. built-in integers) to the compiler to apply **constant-folding** to the `base == commonBaseK` expression, followed by a **simplify-cfg** transformation. + +### Fast path when base is two + +As for this fast path, It is **not** recommended to directly apply the classic approach to any type that conforms to `BinaryInteger` like this: +```swift +extension BinaryInteger { + @inlinable internal var _isPowerOfTwo: Bool { + return self > 0 && self & (self - 1) == 0 + } +} +``` +Because when `Self` is some complicated type, the arithmetic, bitwise and comparison operations can be expensive and thus lead to poor performance. In this case, the `BinaryInteger.words`-based implementation below is preferred, where word is supported by the hardware and the operations are expected to be efficient. +```swift +extension BinaryInteger { + @inlinable internal var _isPowerOfTwo: Bool { + let words = self.words + guard !words.isEmpty else { return false } + + // If the value is represented in a single word, perform the classic check. + if words.count == 1 { + return self > 0 && self & (self - 1) == 0 + } + + // Return false if it is negative. Here we only need to check the most + // significant word (i.e. the last element of `words`). + if Self.isSigned && Int(bitPattern: words.last!) < 0 { + return false + } + + // Check if there is exactly one non-zero word and it is a power of two. + var found = false + for word in words { + if word != 0 { + if found || word & (word - 1) != 0 { return false } + found = true + } + } + return found + } +} +``` + +### Fast path when base itself is a power of two + +As long as we have `_isPowerOfTwo` ready, it becomes easy to implement such fast path when the base itself is power of two. It takes advantages of the existing APIs `isMultiple(of:)` and `trailingZeroBitCount`. The code can be written as below. + +```swift +extension BinaryInteger { + @inlinable internal func _isPowerOf(powerOfTwo base: Self) -> Bool { + _precondition(base._isPowerOfTwo) + guard self._isPowerOfTwo else { return false } + return self.trailingZeroBitCount.isMultiple(of: base.trailingZeroBitCount) + } +} +``` + +### Fast path when base is ten + +Unfortunately, there isn't any known super efficient algorithm to test if an integer is power of ten. Some optimizations are discussed in the forum to help boost the performance to some extent. Since the optimizations are conceptually non-trivial, they are not described here. Please refer to the [pitch thread](https://forums.swift.org/t/adding-ispowerof2-to-binaryinteger/24087/38) for details. + + +### Slow path + +The slow path is generic for any input base, and the algorithm is standard. It initially handles some corner cases when `base.magnitude <= 1`; then it repeatedly multiplies `base` to see if it can be equal to `self` at some point. Attentions are required to avoid overflow of the multiplications. + +```swift +extension BinaryInteger { + @usableFromInline internal func _slowIsPower(of base: Self) -> Bool { + // If self is 1, return true. + if self == 1 { return true } + + // Here if base is 0, 1 or -1, return true iff self equals base. + if base.magnitude <= 1 { return self == base } + + // At this point, we have base.magnitude >= 2. Repeatedly perform + // multiplication by a factor of base, and check if it can equal self. + let (bound, remainder) = self.quotientAndRemainder(dividingBy: base) + guard remainder == 0 else { return false } + var x: Self = 1 + while x.magnitude < bound.magnitude { x *= base } + return x == bound + } +} +``` + +## Source compatibility + +The proposed solution is an additive change. + +## Effect on ABI stability + +The proposed solution is an additive change. + + +## Effect on API resilience + +The proposed solution is an additive change. + + +## Alternatives considered + +### Making `isPower(of:)` as a protocol requirement + +Making the public API as a protocol requirement instead of an extension methods reserves future flexibility. It can give users a chance to provide their own optimization on some custom types where the default implementation is inefficient. However, it would increase the interface complexity of the heavily-used `BinaryInteger` protocol, so it may not be worthy. + +### `isPowerOfTwo` instead of `isPower(of:)` + +In fact, [the pitch](https://forums.swift.org/t/adding-ispowerof2-to-binaryinteger/24087) originally intended to add an API to check if an integer is power of two only. However, the more generic API `isPower(of:)` is favored by the community. This case is similar to [SE-0225](https://github.com/apple/swift-evolution/blob/master/proposals/0225-binaryinteger-iseven-isodd-ismultiple.md), which initially proposed `isEven` and `isOdd` as well, but ended up with `isMultiple(of:)` accepted only. + +### Choices for fast paths + +In the Detailed Design section, there are three specific cases of fast paths presented. Of course, we can have other choices. The fast path for base two is essential, since it is the most common case in real world applications and should always be kept as a fast path. We may remove the fast path for base ten if it is considered introducing too much complexity to the standard library. + +### Alternative optimizations for `_isPowerOfTwo` + +Apart from the classic approach, there are other optimization schemes to check if an integer is power of two. Two candidates are `ctpop`-based and `cttz`-based approaches, whose implementation is shown below. + +```swift +extension FixedWidthInteger { + internal var _isPowerOfTwo_ctpop: Bool { + return self > 0 && self.nonzeroBitCount == 1 + } +} +extension BinaryInteger { + internal var _isPowerOfTwo_cttz: Bool { + return (self > 0) && (self == (1 as Self) << self.trailingZeroBitCount) + } +} +``` +As per [the experimental results](https://github.com/apple/swift/pull/24766#issuecomment-492237146), they are overall less performant than the proposed solution on the types and platforms tested. In addition, the `ctpop`-based approach narrows down the scope from `BinaryInteger` to `FixedWidthInteger`. + +## Acknowledgments + +This proposal has been greatly improved by the community. Below are some cases of significant help. + +- Steve Canon followed the pitch all the way through and provided a lot of valuable comments to keep the work on the right track. +- Jens Persson showed some inspiration in [an earlier thread](https://forums.swift.org/t/even-and-odd-integers/11774/117), and discussed some use cases as well as its extendability to floating point types. +- Nevin Brackett-Rozinsky gave many details to optimize the implementation, and discovered [SR-10657](https://bugs.swift.org/browse/SR-10657) during the discussion. +- Michel Fortin provided an efficient solution for checking if an integer is power of 10, together with thorough explanation. +- Jordan Rose gave prompt and continued comments on the PR, and advised the API should better be an extension rather than a protocol requirement. +- Erik Strottmann suggested a better naming `_isPowerOfTwo` instead of `_isPowerOf2`. + From 98f8b409efa1db051f25a0beb73639ac91daf9e2 Mon Sep 17 00:00:00 2001 From: Ding Ye Date: Thu, 30 May 2019 15:25:38 +1000 Subject: [PATCH 1202/4563] Minor improvements on wording. --- proposals/0000-binaryinteger-ispower.md | 26 ++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/proposals/0000-binaryinteger-ispower.md b/proposals/0000-binaryinteger-ispower.md index cc308494de..94ed85dbd9 100644 --- a/proposals/0000-binaryinteger-ispower.md +++ b/proposals/0000-binaryinteger-ispower.md @@ -9,7 +9,7 @@ ## Introduction -Checking some mathematical properties of integers (e.g. parity, divisibility, etc.) is widely used in scientific and engineering applications. Swift brings a lot of convenience when performing such checks, owing to the relevant methods (e.g. `isMultiple(of:)`) provided by the standard library. However there are still some other cases not yet supported. One of those useful checks that are currently missing is to tell if an integer is power of another, of which the implementation is non-trivial. Apart from inconvenience, user-implemented code can bring inefficiency, poor readability, and even incorrectness. To address this problem, this proposal would like to add a public API `isPower(of:)`, as an extension method, to the `BinaryInteger` protocol. +Checking some mathematical properties of integers (e.g. parity, divisibility, etc.) is widely used in scientific and engineering applications. Swift brings a lot of convenience when performing such checks, thanks to the relevant methods (e.g. `isMultiple(of:)`) provided by the standard library. However there are still some other cases not yet supported. One of those useful checks that are currently missing is to tell if an integer is power of another, of which the implementation is non-trivial. Apart from inconvenience, user-implemented code can bring inefficiency, poor readability, and even incorrectness. To address this problem, this proposal would like to add a public API `isPower(of:)`, as an extension method, to the `BinaryInteger` protocol. Swift-evolution thread: [Pitch](https://forums.swift.org/t/adding-ispowerof2-to-binaryinteger/24087) @@ -33,7 +33,9 @@ internal struct _HashTable { internal init(words: UnsafeMutablePointer, bucketCount: Int) { _internalInvariant(bucketCount > 0 && bucketCount & (bucketCount - 1) == 0, "bucketCount must be a power of two") + // _internalInvariant(bucketCount.isPower(of: 2)) --- proposed solution + ... } } @@ -41,7 +43,7 @@ internal struct _HashTable { ### Efficiency -The user-implemented code may be less performant (e.g. [converting to floating point type and then performing logarithm](https://github.com/OpsLabJPL/MarsImagesIOS/blob/f2109f38b31bf1ad2e7b5aae6916da07d2d7d08e/MarsImagesIOS/Math.swift#L17)), since some developers are not aware of the classic approach as described above. +The user-implemented code may be less performant (e.g. [#1](https://github.com/OpsLabJPL/MarsImagesIOS/blob/f2109f38b31bf1ad2e7b5aae6916da07d2d7d08e/MarsImagesIOS/Math.swift#L17), [#2](https://github.com/eaheckert/Tournament/blob/c09c6b3634da9b2666b8b1f8990ff62bdc4fd625/Tournament/Tournament/ViewControllers/TCreateTournamentVC.swift#L215)), since some developers are not aware of the classic approach as described above. The example below shows a controversial approach, which employs the `llvm.ctpop` intrinsic to count the number of 1 bits. It can be expensive when the hardware does not have relevant `popcount` instruction support. ```swift @@ -65,6 +67,7 @@ The following example presents very similar implementation individually for `UIn func _isPowerOf2(_ x: UInt) -> Bool { // implementation } + func _isPowerOf2(_ x: Int) -> Bool { // implementation very similar to above } @@ -120,11 +123,11 @@ extension BinaryInteger { @usableFromInline internal func _slowIsPower(of base: Self) -> Bool { /* generic implementation */ } } ``` -The public API `isPower(of: commonBaseK)` is expected to be as performant as the optimized version `_isPowerOfCommonBaseK`, if `commonBaseK` is a constant and the type `Self` is obvious enough (e.g. built-in integers) to the compiler to apply **constant-folding** to the `base == commonBaseK` expression, followed by a **simplify-cfg** transformation. +Calling the public API `isPower(of: commonBaseK)` is expected to be as performant as directly calling the optimized version `_isPowerOfCommonBaseK`, if argument `commonBaseK` is a constant and the type `Self` is obvious enough (e.g. built-in integers) to the compiler to apply **constant-folding** to the `base == commonBaseK` expression, followed by a **simplify-cfg** transformation. ### Fast path when base is two -As for this fast path, It is **not** recommended to directly apply the classic approach to any type that conforms to `BinaryInteger` like this: +As for this fast path, it is **not** recommended to directly apply the classic approach to any type that conforms to `BinaryInteger` like this: ```swift extension BinaryInteger { @inlinable internal var _isPowerOfTwo: Bool { @@ -165,7 +168,7 @@ extension BinaryInteger { ### Fast path when base itself is a power of two -As long as we have `_isPowerOfTwo` ready, it becomes easy to implement such fast path when the base itself is power of two. It takes advantages of the existing APIs `isMultiple(of:)` and `trailingZeroBitCount`. The code can be written as below. +As long as we have `_isPowerOfTwo` available, it becomes easy to implement such fast path when the base itself is power of two. It takes advantages of the existing APIs `isMultiple(of:)` and `trailingZeroBitCount`. The code can be written as below. ```swift extension BinaryInteger { @@ -184,7 +187,7 @@ Unfortunately, there isn't any known super efficient algorithm to test if an int ### Slow path -The slow path is generic for any input base, and the algorithm is standard. It initially handles some corner cases when `base.magnitude <= 1`; then it repeatedly multiplies `base` to see if it can be equal to `self` at some point. Attentions are required to avoid overflow of the multiplications. +The slow path is generic for any input base, and the algorithm is standard. It handles some corner cases when `base.magnitude <= 1` in the first place; and then it repeatedly multiplies `base` to see if it can be equal to `self` at some point. Attentions are required to avoid overflow of the multiplications. ```swift extension BinaryInteger { @@ -222,13 +225,13 @@ The proposed solution is an additive change. ## Alternatives considered -### Making `isPower(of:)` as a protocol requirement +### `isPower(of:)` as a protocol requirement Making the public API as a protocol requirement instead of an extension methods reserves future flexibility. It can give users a chance to provide their own optimization on some custom types where the default implementation is inefficient. However, it would increase the interface complexity of the heavily-used `BinaryInteger` protocol, so it may not be worthy. ### `isPowerOfTwo` instead of `isPower(of:)` -In fact, [the pitch](https://forums.swift.org/t/adding-ispowerof2-to-binaryinteger/24087) originally intended to add an API to check if an integer is power of two only. However, the more generic API `isPower(of:)` is favored by the community. This case is similar to [SE-0225](https://github.com/apple/swift-evolution/blob/master/proposals/0225-binaryinteger-iseven-isodd-ismultiple.md), which initially proposed `isEven` and `isOdd` as well, but ended up with `isMultiple(of:)` accepted only. +In fact, [the pitch](https://forums.swift.org/t/adding-ispowerof2-to-binaryinteger/24087) originally intended to add an API to check if an integer is power of two only. However, the more generic form `isPower(of:)` is favored by the community. This case is similar to [SE-0225](https://github.com/apple/swift-evolution/blob/master/proposals/0225-binaryinteger-iseven-isodd-ismultiple.md), which initially proposed `isEven` and `isOdd` as well, but ended up with `isMultiple(of:)` accepted only. ### Choices for fast paths @@ -244,6 +247,7 @@ extension FixedWidthInteger { return self > 0 && self.nonzeroBitCount == 1 } } + extension BinaryInteger { internal var _isPowerOfTwo_cttz: Bool { return (self > 0) && (self == (1 as Self) << self.trailingZeroBitCount) @@ -259,7 +263,7 @@ This proposal has been greatly improved by the community. Below are some cases o - Steve Canon followed the pitch all the way through and provided a lot of valuable comments to keep the work on the right track. - Jens Persson showed some inspiration in [an earlier thread](https://forums.swift.org/t/even-and-odd-integers/11774/117), and discussed some use cases as well as its extendability to floating point types. - Nevin Brackett-Rozinsky gave many details to optimize the implementation, and discovered [SR-10657](https://bugs.swift.org/browse/SR-10657) during the discussion. -- Michel Fortin provided an efficient solution for checking if an integer is power of 10, together with thorough explanation. -- Jordan Rose gave prompt and continued comments on the PR, and advised the API should better be an extension rather than a protocol requirement. -- Erik Strottmann suggested a better naming `_isPowerOfTwo` instead of `_isPowerOf2`. +- Michel Fortin provided an efficient solution to the fast path for base 10 (i.e. checking if an integer is power of ten), together with thorough explanation. +- Jordan Rose gave prompt and continued comments on the PR, and advised the API should better be an extension method rather than a protocol requirement. +- Erik Strottmann suggested a more appropriate naming `_isPowerOfTwo` instead of `_isPowerOf2`. From 6c2fd7433b3b1db6e8ee450f145c23b55c48d4cf Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 30 May 2019 11:18:56 -0700 Subject: [PATCH 1203/4563] [SE-0258] Improve type inference behavior, courtesy of John McCall. --- proposals/0258-property-wrappers.md | 38 +++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 49aa3599fa..40949cc193 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -679,15 +679,15 @@ in one of three ways: ### Type inference with wrappers Type inference for properties with wrappers involves both the type -annotation of the original property (if present) and the wrapper -type, using the initialization of the synthesized stored property. For -example: +annotation of the original property (if present), the wrapper +type, and the initial value (if present). The complete wrapper type is inferred, and it's `value` property is used to provide the type for the original property. For example, providing an initial value to the original property uses the `initialValue` initializer to infer the wrapper type: ```swift @Lazy var foo = 17 // type inference as in... var $foo: Lazy = Lazy(initialValue: 17) -// infers the type of 'foo' to be 'Int' +// infers the type of '$foo' to be 'Lazy', then +// assigns the type of `foo` to `Int` ``` The same applies when directly initializing the wrapper instance, e.g., @@ -697,20 +697,37 @@ The same applies when directly initializing the wrapper instance, e.g., var someInt // type inference as in... var $someInt: UnsafeMutablePointer = UnsafeMutablePointer.init(mutating: addressOfInt) -// infers the type of 'someInt' to be 'Int' +// infers the type of `$someInt` to be `UnsafeMutablePointer`, then +// assigns the type of 'someInt' to be 'Int' ``` -The type of the `value` property of the property wrapper type must coincide with that of the original property using that wrapper type. Some examples: +If there is no initial value, type inference compares the type of the wrapper type's `value` property against the type annotation of the original property (if present). This can infer generic arguments of the wrapper type (if any). ```swift -@Lazy var foo: Int // okay -@Lazy var bar: Double // error: Lazy.value is of type Int, not Double +@propertyWrapper +struct Function { + var value: (T) -> U? { ... } +} + +@Function var f: (Int) -> Float? // infers T=Int, U=Float ``` -If there is no initializer for the wrapper instance, and the property wrapper type takes a single generic parameter, the corresponding generic argument can be omitted: +It can also "fill in" generic arguments omitted from the original property's type annotation: ```swift -@Lazy var foo: Int // okay: equivalent to @Lazy var foo: Int +@propertyWrapper +struct StringDictionary { + var value: [String: String] +} + +@StringDictionary var d: Dictionary // infers +``` + +The type of the `value` property of the property wrapper type must always concide with the type of the original property, regardless of whether inference was used to determine those types. Some examples: + +```swift +@Lazy var foo: Int // okay +@Lazy var bar: Double // error: Lazy.value is of type Int, not Double ``` ### Custom attributes @@ -1286,6 +1303,7 @@ One could express this either by naming the property directly (as above) or, for * Reduced the visibility of the synthesized storage property to `private`, and expanded upon potential future directions to making it more visible. * Removed the restriction banning property wrappers from having names that match the regular expression `_*[a-z].*`. * `Codable`, `Hashable`, and `Equatable` synthesis are now based on the backing storage properties, which is a simpler model that gives more control to the authors of property wrapper types. +* Improved type inference for property wrapper types and clarified that the type of the `value` property is used as part of this inference. See the "Type inference" section. ## Acknowledgments From da63f45087fdbaa03f28af092c1ddd16f26bdd2d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 30 May 2019 16:50:37 -0700 Subject: [PATCH 1204/4563] [SE-0258] Clarify type inference --- proposals/0258-property-wrappers.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 40949cc193..c67f2d3197 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -678,19 +678,31 @@ in one of three ways: ### Type inference with wrappers -Type inference for properties with wrappers involves both the type +Type inference for properties with wrappers involves the type annotation of the original property (if present), the wrapper -type, and the initial value (if present). The complete wrapper type is inferred, and it's `value` property is used to provide the type for the original property. For example, providing an initial value to the original property uses the `initialValue` initializer to infer the wrapper type: +type, and the initial value (if present). In this process, the complete wrapper type is inferred (potentially determining generic arguments for the wrapper type), as is the type of the original property (either determining generic arguments that were omitted or even providing the complete property type). At the end of the process, the type of the original property must match the wrapper type's `value` property type. + +When an initial value is present, it will be used to form the inference. For example, providing an initial value to the original property uses the `initialValue` initializer to infer the wrapper type: ```swift @Lazy var foo = 17 // type inference as in... var $foo: Lazy = Lazy(initialValue: 17) // infers the type of '$foo' to be 'Lazy', then -// assigns the type of `foo` to `Int` +// assigns the type of 'foo' to 'Lazy.value', i.e., 'Int' +``` + +When multiple properties are composed, inference is based on the nested `init(initialValue:)` calls (as discussed in the previous section), and the resulting inferred type is based on the appropriate number of `.value` references, e.g., + +```swift +@A @B var bar = 42 +// type inference as in ... +var $bar = A(initialValue: B(initialValue: 42)) +// infers the type of '$bar' to be 'A', then +// assigns the type of 'var' to 'A.value.value, i.e., 'Int' ``` -The same applies when directly initializing the wrapper instance, e.g., +Type inference also applies when directly initializing the wrapper instance, but is only valid when there is a single property wrapper applied to the original property: ```swift @UnsafeMutablePointer(mutating: addressOfInt) @@ -701,7 +713,7 @@ var $someInt: UnsafeMutablePointer = UnsafeMutablePointer.init(mutating: address // assigns the type of 'someInt' to be 'Int' ``` -If there is no initial value, type inference compares the type of the wrapper type's `value` property against the type annotation of the original property (if present). This can infer generic arguments of the wrapper type (if any). +If there is no initial value, type inference compares the type of the wrapper type's `value` property (multiple `.value` indirections when property wrappers are composed) against the type annotation of the original property (if present). This can infer generic arguments of the wrapper type (if any). ```swift @propertyWrapper @@ -712,7 +724,7 @@ struct Function { @Function var f: (Int) -> Float? // infers T=Int, U=Float ``` -It can also "fill in" generic arguments omitted from the original property's type annotation: +It can also "fill in" generic arguments omitted from the original property's type annotation or an omitted type: ```swift @propertyWrapper @@ -721,9 +733,11 @@ struct StringDictionary { } @StringDictionary var d: Dictionary // infers +@StringDictionary var d2 // infers Dictionary ``` -The type of the `value` property of the property wrapper type must always concide with the type of the original property, regardless of whether inference was used to determine those types. Some examples: + +As previously noted, the type of the `value` property of the property wrapper type must always concide with the type of the original property, regardless of whether inference was used to determine those types. Some examples: ```swift @Lazy var foo: Int // okay From 6490a519763953c2ab1507508b3c30eb46f17e28 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 May 2019 13:07:30 -0700 Subject: [PATCH 1205/4563] [SE-0258] Improve discussion of type inference behavior. Big thanks to John McCall for rewriting most of this! --- proposals/0258-property-wrappers.md | 84 ++++++++++++++--------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index c67f2d3197..979538c4a9 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -676,55 +676,57 @@ in one of three ways: When there are multiple, composed property wrappers, only the first (outermost) wrapper needs to have an `init()`. -### Type inference with wrappers +### Type inference with property wrappers -Type inference for properties with wrappers involves the type -annotation of the original property (if present), the wrapper -type, and the initial value (if present). In this process, the complete wrapper type is inferred (potentially determining generic arguments for the wrapper type), as is the type of the original property (either determining generic arguments that were omitted or even providing the complete property type). At the end of the process, the type of the original property must match the wrapper type's `value` property type. +If the first property wrapper type is generic, its generic arguments must either be given explicitly in the attribute or Swift must be able to deduce them from the variable declaration. That deduction proceeds as follows: -When an initial value is present, it will be used to form the inference. For example, providing an initial value to the original property uses the `initialValue` initializer to infer the wrapper type: +* If the variable has an initial value expression `E`, then the first wrapper type is constrained to equal the type resulting from a call to `A(initialValue: E)`, where `A` is the written type of the attribute. For example: -```swift -@Lazy var foo = 17 -// type inference as in... -var $foo: Lazy = Lazy(initialValue: 17) -// infers the type of '$foo' to be 'Lazy', then -// assigns the type of 'foo' to 'Lazy.value', i.e., 'Int' -``` + ```swift + @Lazy var foo = 17 + // type inference as in... + var $foo: Lazy = Lazy(initialValue: 17) + // infers the type of '$foo' to be 'Lazy' + ``` -When multiple properties are composed, inference is based on the nested `init(initialValue:)` calls (as discussed in the previous section), and the resulting inferred type is based on the appropriate number of `.value` references, e.g., + If there are multiple wrapper attributes, the argument to this call will instead be a nested call to `B(initialValue: E)` for the written type of the next attribute, and so on recursively. For example: + + ```swift + @A @B var bar = 42 + // type inference as in ... + var $bar = A(initialValue: B(initialValue: 42)) + // infers the type of '$bar' to be 'A' + ``` -```swift -@A @B var bar = 42 -// type inference as in ... -var $bar = A(initialValue: B(initialValue: 42)) -// infers the type of '$bar' to be 'A', then -// assigns the type of 'var' to 'A.value.value, i.e., 'Int' -``` +* Otherwise, if the first wrapper attribute has direct initialization arguments `E...`, the outermost wrapper type is constrained to equal the type resulting from `A(E...)`, where `A` is the written type of the first attribute. Wrapper attributes after the first may not have direct initializers. For example: -Type inference also applies when directly initializing the wrapper instance, but is only valid when there is a single property wrapper applied to the original property: + ```swift + @UnsafeMutablePointer(mutating: addressOfInt) + var someInt + // type inference as in... + var $someInt: UnsafeMutablePointer = UnsafeMutablePointer.init(mutating: addressOfInt) + // infers the type of `$someInt` to be `UnsafeMutablePointer` + ``` -```swift -@UnsafeMutablePointer(mutating: addressOfInt) -var someInt -// type inference as in... -var $someInt: UnsafeMutablePointer = UnsafeMutablePointer.init(mutating: addressOfInt) -// infers the type of `$someInt` to be `UnsafeMutablePointer`, then -// assigns the type of 'someInt' to be 'Int' -``` +* Otherwise, if there is no initialization, and the original property has a type annotation, the type of the `value` property in the last wrapper type is constrained to equal the type annotation of the original property. For example: + + ```swift + @propertyWrapper + struct Function { + var value: (T) -> U? { ... } + } -If there is no initial value, type inference compares the type of the wrapper type's `value` property (multiple `.value` indirections when property wrappers are composed) against the type annotation of the original property (if present). This can infer generic arguments of the wrapper type (if any). + @Function var f: (Int) -> Float? // infers T=Int, U=Float + ``` -```swift -@propertyWrapper -struct Function { - var value: (T) -> U? { ... } -} +In any case, the first wrapper type is constrained to be a specialization of the first attribute's written type. Furthermore, for any secondary wrapper attributes, the type of the value property of the previous wrapper type is constrained to be a specialization of the attribute's written type. Finally, if a type annotation is given, the type of the value property of the last wrapper type is constrained to equal the type annotation. If these rules fail to deduce all the type arguments for the first wrapper type, or if they are inconsistent with each other, the variable is ill-formed. For example: -@Function var f: (Int) -> Float? // infers T=Int, U=Float +```swift +@Lazy var foo: Int // okay +@Lazy var bar: Double // error: Lazy.value is of type Int, not Double ``` -It can also "fill in" generic arguments omitted from the original property's type annotation or an omitted type: +The deduction can also provide a type for the original property (if a type annotation was omitted) or deduce generic arguments that have omitted from the type annotation. For example: ```swift @propertyWrapper @@ -736,14 +738,6 @@ struct StringDictionary { @StringDictionary var d2 // infers Dictionary ``` - -As previously noted, the type of the `value` property of the property wrapper type must always concide with the type of the original property, regardless of whether inference was used to determine those types. Some examples: - -```swift -@Lazy var foo: Int // okay -@Lazy var bar: Double // error: Lazy.value is of type Int, not Double -``` - ### Custom attributes Property wrappers are a form of custom attribute, where the attribute syntax From dc548926fca6ba081e316cc02bd634a831901e0a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 May 2019 13:08:46 -0700 Subject: [PATCH 1206/4563] [SE-0258] Flip the order of the examples to fit the prose --- proposals/0258-property-wrappers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 979538c4a9..d8a55af9da 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -734,8 +734,8 @@ struct StringDictionary { var value: [String: String] } -@StringDictionary var d: Dictionary // infers -@StringDictionary var d2 // infers Dictionary +@StringDictionary var d1. // infers Dictionary +@StringDictionary var d2: Dictionary // infers ``` ### Custom attributes From 5c4a5d79bc4a040eab8fcdbc2dc21ae9b361a78a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 May 2019 13:11:21 -0700 Subject: [PATCH 1207/4563] [SE-0258] Fix typos --- proposals/0258-property-wrappers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index d8a55af9da..1c7f4a6b05 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -976,8 +976,8 @@ var someValue: String There are a number of restrictions on the use of property wrappers when defining a property: -* A property with a wrapper may not declared inside a protocol. -* An instance property with a wrapper may not declared inside an extension. +* A property with a wrapper may not be declared inside a protocol. +* An instance property with a wrapper may not be declared inside an extension. * An instance property may not be declared in an `enum`. * A property with a wrapper that is declared within a class cannot override another property. * A property with a wrapper cannot be `lazy`, `@NSCopying`, `@NSManaged`, `weak`, or `unowned`. From 9eea195bd96111575fc6180a6f7595bd1ab8e817 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 May 2019 13:36:47 -0700 Subject: [PATCH 1208/4563] [SE-0258] A little more commentary on wrapperValue --- proposals/0258-property-wrappers.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 1c7f4a6b05..9f1143c7bc 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -972,6 +972,9 @@ The `someValue` variable from the previous example could be switched over to use var someValue: String ``` +Each of the property wrapper types could have different implementations with +different data, but all of them present the same interface through `$someValue` and `someValue`. + ### Restrictions on the use of property wrappers There are a number of restrictions on the use of property wrappers when defining a property: From 1b522ba0fdec78fd852e2930d0dcaa504f278379 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 31 May 2019 13:45:35 -0700 Subject: [PATCH 1209/4563] [SE-0258] Note how wrapperValue can be used to have get-only $ variables --- proposals/0258-property-wrappers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 9f1143c7bc..0a782ae986 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -973,7 +973,7 @@ var someValue: String ``` Each of the property wrapper types could have different implementations with -different data, but all of them present the same interface through `$someValue` and `someValue`. +different data, but all of them present the same interface through `$someValue` and `someValue`. Note also that the `$someValue` is not writable, because `wrapperValue` is a get-only property. ### Restrictions on the use of property wrappers From a2f478616d071cc6038c93af2e7a2d8a106eb1cd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 14 Jun 2019 00:33:37 -0700 Subject: [PATCH 1210/4563] [SE-0258] Rename 'value' to 'wrappedValue'. --- proposals/0258-property-wrappers.md | 91 +++++++++++++++-------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 0a782ae986..3c4ca7aaa0 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -109,7 +109,7 @@ enum Lazy { self = .uninitialized(initialValue) } - var value: Value { + var wrappedValue: Value { mutating get { switch self { case .uninitialized(let initializer): @@ -128,7 +128,7 @@ enum Lazy { ``` A property wrapper type provides the storage for a property that -uses it as a wrapper. The `value` property of the wrapper type +uses it as a wrapper. The `wrappedValue` property of the wrapper type provides the actual implementation of the wrapper, while the (optional) `init(initialValue:)` enables initialization of the storage from a @@ -173,7 +173,7 @@ struct UserDefault { let key: String let defaultValue: T - var value: T { + var wrappedValue: T { get { return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue } @@ -213,7 +213,7 @@ implement both a mutable variant, which allows for reassignment like a struct DelayedMutable { private var _value: Value? = nil - var value: Value { + var wrappedValue: Value { get { guard let value = _value else { fatalError("property accessed before being initialized") @@ -240,7 +240,7 @@ a `let`: struct DelayedImmutable { private var _value: Value? = nil - var value: Value { + var wrappedValue: Value { get { guard let value = _value else { fatalError("property accessed before being initialized") @@ -297,7 +297,7 @@ struct Copying { self._value = value.copy() as! Value } - var value: Value { + var wrappedValue: Value { get { return _value } set { // Copy the value on reassignment. @@ -324,7 +324,7 @@ struct Atomic { self._value = initialValue } - var value: Value { + var wrappedValue: Value { get { return load() } set { store(newValue: newValue) } } @@ -406,7 +406,7 @@ final class ThreadSpecific { } } - var value: T { + var wrappedValue: T { get { return box.pointee as! T } set (v) { box.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee = v } @@ -427,20 +427,20 @@ protocol Copyable: AnyObject { @propertyWrapper struct CopyOnWrite { init(initialValue: Value) { - value = initialValue + wrappedValue = initialValue } - private(set) var value: Value + private(set) var wrappedValue: Value var wrapperValue: Value { mutating get { - if !isKnownUniquelyReferenced(&value) { - value = value.copy() + if !isKnownUniquelyReferenced(&wrappedValue) { + wrappedValue = value.copy() } - return value + return wrappedValue } set { - value = newValue + wrappedValue = newValue } } } @@ -470,7 +470,7 @@ struct Ref { let read: () -> Value let write: (Value) -> Void - var value: Value { + var wrappedValue: Value { get { return read() } nonmutating set { write(newValue) } } @@ -503,7 +503,7 @@ write out the getters and setters, and it's fairly common to have a `Box` type t ```swift @propertyWrapper class Box { - var value: Value + var wrappedValue: Value init(initialValue: Value) { self.value = initialValue @@ -537,7 +537,7 @@ There are a number of existing types that already provide the basic structure of struct UnsafeMutablePointer { var pointee: Pointee { ... } - var value: Pointee { + var wrappedValue: Pointee { get { return pointee } set { pointee = newValue } } @@ -556,7 +556,7 @@ print(someInt) $someInt.deallocate() ``` -RxCocoa's [`BehaviorRelay`](https://github.com/ReactiveX/RxSwift/blob/master/RxCocoa/Traits/BehaviorRelay.swift) replays the most recent value provided to it for each of the subscribed observers. It is created with an initial value, has `value` property to access the current value, as well as API to `subscribe` a new observer: (Thanks to Adrian Zubarev for pointing this out) +RxCocoa's [`BehaviorRelay`](https://github.com/ReactiveX/RxSwift/blob/master/RxCocoa/Traits/BehaviorRelay.swift) replays the most recent value provided to it for each of the subscribed observers. It is created with an initial value, has `wrappedValue` property to access the current value, as well as API to `subscribe` a new observer: (Thanks to Adrian Zubarev for pointing this out) ```swift @BehaviorRelay @@ -579,7 +579,7 @@ the wrappers are composed together to get both effects. For example, consider th Here, we have a property for which we can delay initialization until later. When we do set a value, it will be copied via `NSCopying`'s `copy` method. -Composition is implemented by nesting later wrapper types inside earlier wrapper types, where the innermost nested type is the original property's type. For the example above, the backing storage will be of type `DelayedMutable>`, and the synthesized getter/setter for `path` will look through both levels of `.value`: +Composition is implemented by nesting later wrapper types inside earlier wrapper types, where the innermost nested type is the original property's type. For the example above, the backing storage will be of type `DelayedMutable>`, and the synthesized getter/setter for `path` will look through both levels of `.wrappedValue`: ```swift var $path: DelayedMutable> = .init() @@ -610,7 +610,7 @@ type: `@propertyWrapper`. The attribute indicates that the type is meant to be used as a property wrapper type, and provides a point at which the compiler can verify any other consistency rules. -2. The property wrapper type must have a property named `value`, whose +2. The property wrapper type must have a property named `wrappedValue`, whose access level is the same as that of the type itself. This is the property used by the compiler to access the underlying value on the wrapper instance. @@ -625,7 +625,7 @@ in one of three ways: 1. Via a value of the original property's type (e.g., `Int` in `@Lazy var foo: Int`, using the the property wrapper type's `init(initialValue:)` initializer. That initializer must have a single - parameter of the same type as the `value` property (or + parameter of the same type as the `wrappedValue` property (or be an `@autoclosure` thereof) and have the same access level as the property wrapper type itself. When `init(initialValue:)` is present, is is always used for the initial value provided on the property @@ -708,22 +708,22 @@ If the first property wrapper type is generic, its generic arguments must either // infers the type of `$someInt` to be `UnsafeMutablePointer` ``` -* Otherwise, if there is no initialization, and the original property has a type annotation, the type of the `value` property in the last wrapper type is constrained to equal the type annotation of the original property. For example: +* Otherwise, if there is no initialization, and the original property has a type annotation, the type of the `wrappedValue` property in the last wrapper type is constrained to equal the type annotation of the original property. For example: ```swift @propertyWrapper struct Function { - var value: (T) -> U? { ... } + var wrappedValue: (T) -> U? { ... } } @Function var f: (Int) -> Float? // infers T=Int, U=Float ``` -In any case, the first wrapper type is constrained to be a specialization of the first attribute's written type. Furthermore, for any secondary wrapper attributes, the type of the value property of the previous wrapper type is constrained to be a specialization of the attribute's written type. Finally, if a type annotation is given, the type of the value property of the last wrapper type is constrained to equal the type annotation. If these rules fail to deduce all the type arguments for the first wrapper type, or if they are inconsistent with each other, the variable is ill-formed. For example: +In any case, the first wrapper type is constrained to be a specialization of the first attribute's written type. Furthermore, for any secondary wrapper attributes, the type of the wrappedValue property of the previous wrapper type is constrained to be a specialization of the attribute's written type. Finally, if a type annotation is given, the type of the wrappedValue property of the last wrapper type is constrained to equal the type annotation. If these rules fail to deduce all the type arguments for the first wrapper type, or if they are inconsistent with each other, the variable is ill-formed. For example: ```swift @Lazy var foo: Int // okay -@Lazy var bar: Double // error: Lazy.value is of type Int, not Double +@Lazy var bar: Double // error: Lazy.wrappedValue is of type Int, not Double ``` The deduction can also provide a type for the original property (if a type annotation was omitted) or deduce generic arguments that have omitted from the type annotation. For example: @@ -731,7 +731,7 @@ The deduction can also provide a type for the original property (if a type annot ```swift @propertyWrapper struct StringDictionary { - var value: [String: String] + var wrappedValue: [String: String] } @StringDictionary var d1. // infers Dictionary @@ -759,14 +759,14 @@ This formulation of custom attributes fits in with a [larger proposal for custom ### Mutability of properties with wrappers -Generally, a property that has a property wrapper will have both a getter and a setter. However, the setter may be missing if the `value` property of the property wrapper type lacks a setter, or its setter is inaccessible. +Generally, a property that has a property wrapper will have both a getter and a setter. However, the setter may be missing if the `wrappedValue` property of the property wrapper type lacks a setter, or its setter is inaccessible. -The synthesized getter will be `mutating` if the property wrapper type's `value` property is `mutating` and the property is part of a `struct`. Similarly, the synthesized setter will be `nonmutating` if either the property wrapper type's `value` property has a `nonmutating` setter or the property wrapper type is a `class`. For example: +The synthesized getter will be `mutating` if the property wrapper type's `wrappedValue` property is `mutating` and the property is part of a `struct`. Similarly, the synthesized setter will be `nonmutating` if either the property wrapper type's `wrappedValue` property has a `nonmutating` setter or the property wrapper type is a `class`. For example: ```swift @propertyWrapper struct MutatingGetterWrapper { - var value: Value { + var wrappedValue: Value { mutating get { ... } set { ... } } @@ -774,7 +774,7 @@ struct MutatingGetterWrapper { @propertyWrapper struct NonmutatingSetterWrapper { - var value: Value { + var wrappedValue: Value { get { ... } nonmutating set { ... } } @@ -782,7 +782,7 @@ struct NonmutatingSetterWrapper { @propertyWrapper class ReferenceWrapper { - var value: Value + var wrappedValue: Value } struct Usewrappers { @@ -897,7 +897,7 @@ let $y = 17 // error: cannot declare entity with $-prefixed name '$y' ### Delegating access to the storage property -A property wrapper type can choose to hide its instance entirely by providing a property named `wrapperValue`. As with the `value` property and`init(initialValue:)`, the `wrapperValue` property must have the +A property wrapper type can choose to hide its instance entirely by providing a property named `wrapperValue`. As with the `wrappedValue` property and`init(initialValue:)`, the `wrapperValue` property must have the same access level as its property wrapper type. When present, the synthesized storage property is hidden completely and the property `$foo` becomes a computed property accessing the storage property's `wrapperValue`. For example: ```swift @@ -914,7 +914,7 @@ struct LongTermStorage { pointer.initialize(to: initialValue) } - var value: Value { + var wrappedValue: Value { get { return pointer.pointee } set { pointer.pointee = newValue } } @@ -954,7 +954,7 @@ struct ArenaStorage { pointer.initialize(to: initialValue) } - var value: Value { + var wrappedValue: Value { get { return pointer.pointee } set { pointer.pointee = newValue } } @@ -986,7 +986,7 @@ There are a number of restrictions on the use of property wrappers when defining * A property with a wrapper cannot be `lazy`, `@NSCopying`, `@NSManaged`, `weak`, or `unowned`. * A property with a wrapper must be the only property declared within its enclosing declaration (e.g., `@Lazy var (x, y) = /* ... */` is ill-formed). * A property with a wrapper shall not define a getter or setter. -* The `value` property and (if present) `init(initialValue:)` of a property wrapper type shall have the same access as the property wrapper type. +* The `wrappedValue` property and (if present) `init(initialValue:)` of a property wrapper type shall have the same access as the property wrapper type. * The `wrapperValue` property, if present, shall have the same access as the property wrapper type. * The `init()` initializer, if present, shall have the same access as the property wrapper type. @@ -1019,7 +1019,7 @@ Composition was left out of the [first revision](https://github.com/apple/swift- struct AB { private var storage: A> - var value: Value { + var wrappedValue: Value { get { storage.value.value } set { storage.value.value = newValue } } @@ -1034,7 +1034,7 @@ One proposed approach to composition addresses only the last issue above directl ```swift @propertyWrapper struct A { - var value: Value { ... } + var wrappedValue: Value { ... } } extension A { @@ -1054,7 +1054,7 @@ There has been a desire to effect composition of property wrappers without havin @A @B var x: Int ``` -the `Int` value is conceptually wrapped by a property wrapper type, and the property wrapper type's `value` property guards access to that (conceptual) `Int` value. That `Int` value cannot be wrapped both by instances of both `A` and `B` without either duplicating data (both `A` and `B` have a copy of the `Int`) or nesting one of the wrappers inside the other. With the copying approach, one must maintain consistency between the copies (which is particularly hard when value types are involved) and there will still be non-commutative compositions. Nesting fits better with the "wrapper" model of property wrappers. +the `Int` value is conceptually wrapped by a property wrapper type, and the property wrapper type's `wrappedValue` property guards access to that (conceptual) `Int` value. That `Int` value cannot be wrapped both by instances of both `A` and `B` without either duplicating data (both `A` and `B` have a copy of the `Int`) or nesting one of the wrappers inside the other. With the copying approach, one must maintain consistency between the copies (which is particularly hard when value types are involved) and there will still be non-commutative compositions. Nesting fits better with the "wrapper" model of property wrappers. ### Using a formal protocol instead of `@propertyWrapper` @@ -1065,12 +1065,12 @@ types. It might look like this: ```swift protocol PropertyWrapper { associatedtype Value - var value: Value { get } + var wrappedValue: Value { get } } ``` There are a few issues here. First, a single protocol -`PropertyWrapper` cannot handle all of the variants of `value` that +`PropertyWrapper` cannot handle all of the variants of `wrappedValue` that are implied by the section on mutability of properties with wrappers, because we'd need to cope with `mutating get` as well as `set` and `nonmutating set`. Moreover, protocols don't support optional @@ -1152,7 +1152,7 @@ One could also consider having the property wrapper types themselves declare tha ```swift @propertyWrapper(wrapperIsAccessible: true) struct Atomic { - var value: T { ... } + var wrappedValue: T { ... } } // both bar and $bar are publicly visible @@ -1201,7 +1201,7 @@ public struct Observable { self.observed = observed } - public var value: Value { + public var wrappedValue: Value { get { return stored } set { if newValue != stored { @@ -1229,7 +1229,7 @@ public class MyClass: Superclass { This isn't as automatic as we would like, and it requires us to have a separate reference to the `self` that is stored within `Observable`. -Instead, we could extend the ad hoc protocol used to access the storage property of a `@propertyWrapper` type a bit further. Instead of (or in addition to) a `value` property, a property wrapper type could provide a `subscript(instanceSelf:)` and/or `subscript(typeSelf:)` that receive `self` as a parameter. For example: +Instead, we could extend the ad hoc protocol used to access the storage property of a `@propertyWrapper` type a bit further. Instead of (or in addition to) a `wrappedValue` property, a property wrapper type could provide a `subscript(instanceSelf:)` and/or `subscript(typeSelf:)` that receive `self` as a parameter. For example: ```swift @@ -1272,7 +1272,7 @@ This change is backward-compatible with the rest of the proposal. Property wrapp * For instance properties, `subscript(instanceSelf:)` as shown above. * For static or class properties, `subscript(typeSelf:)`, similar to the above but accepting a metatype parameter. -* For global/local properties, or when the appropriate `subscript` mentioned above isn't provided by the wrapper type, the `value` property would be used. +* For global/local properties, or when the appropriate `subscript` mentioned above isn't provided by the wrapper type, the `wrappedValue` property would be used. The main challenge with this design is that it doesn't directly work when the enclosing type is a value type and the property is settable. In such cases, the parameter to the subscript would get a copy of the entire enclosing value, which would not allow mutation, On the other hand, one could try to pass `self` as `inout`, e.g., @@ -1314,7 +1314,8 @@ One could express this either by naming the property directly (as above) or, for * Reduced the visibility of the synthesized storage property to `private`, and expanded upon potential future directions to making it more visible. * Removed the restriction banning property wrappers from having names that match the regular expression `_*[a-z].*`. * `Codable`, `Hashable`, and `Equatable` synthesis are now based on the backing storage properties, which is a simpler model that gives more control to the authors of property wrapper types. -* Improved type inference for property wrapper types and clarified that the type of the `value` property is used as part of this inference. See the "Type inference" section. +* Improved type inference for property wrapper types and clarified that the type of the `wrappedValue` property is used as part of this inference. See the "Type inference" section. +* Renamed the `value` property to `wrappedValue` to avoid conflicts. ## Acknowledgments From 4b5f57d0910e95457542ee38d0514684eda84127 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 14 Jun 2019 00:35:39 -0700 Subject: [PATCH 1211/4563] [SE-0258] Update implementation and pitch #3 links --- proposals/0258-property-wrappers.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 3c4ca7aaa0..9411267d49 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Returned for revision** -* Implementation: Available as a "hidden" feature in [master snapshots](https://swift.org/download/#snapshots) with the attribute name `@_propertyDelegate` and using `delegateValue` in lieu of `wrapperValue`. +* Implementation: Available in [master snapshots](https://swift.org/download/#snapshots). * Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) * Previous versions: [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) @@ -19,6 +19,7 @@ This is an alternative approach to some of the problems intended to be addressed [Pitch #1](https://forums.swift.org/t/pitch-property-delegates/21895)
    [Pitch #2](https://forums.swift.org/t/pitch-2-property-delegates-by-custom-attributes/22855)
    +[Pitch #3](https://forums.swift.org/t/pitch-3-property-wrappers-formerly-known-as-property-delegates/24961)
    ## Motivation From 7e453fbaa01d558d3a8f5f6ea5bb77d65b092241 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 14 Jun 2019 00:50:11 -0700 Subject: [PATCH 1212/4563] [SE-0258] Extend initialValue: support to explicit arguments. Introduce the "Clamping" example illustrating both initial values and explicit initializer arguments, e.g., @Clamping(min: 0, max: 255) var red: Int = 127 --- proposals/0258-property-wrappers.md | 67 +++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 9411267d49..df3bdda83d 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -529,6 +529,64 @@ let topLeft2 = $rectangle.topLeft // through wrapperValue, produces a Ref { + var value: V + let min: V + let max: V + + init(initialValue: V, min: V, max: V) { + value = initialValue + self.min = min + self.max = max + assert(value >= min && value <= max) + } + + var wrappedValue: V { + get { return value } + set { + if newValue < min { + value = min + } else if newValue > max { + value = max + } else { + value = newValue + } + } + } +} +``` + +Most interesting in this example is how `@Clamping` properties can be +initialized given both an initial value and initializer arguments. In such cases, the `initialValue:` argument is placed first. For example, this means we can define a `Color` type that clamps all values in the range [0, 255]: + +```swift +struct Color { + @Clamping(min: 0, max: 255) var red: Int = 127 + @Clamping(min: 0, max: 255) var green: Int = 127 + @Clamping(min: 0, max: 255) var blue: Int = 127 + @Clamping(min: 0, max: 255) var alpha: Int = 255 +} +``` + +The synthesized memberwise initializer demonstrates how the initialization itself is formed: + +```swift +init(red: Int = 127, green: Int = 127, blue: Int = 127, alpha: Int = 255) { + $red = Clamping(initialValue: red, min: 0, max: 255) + $green = Clamping(initialValue: green, min: 0, max: 255) + $blue = Clamping(initialValue: blue, min: 0, max: 255) + $alpha = Clamping(initialValue: alpha, min: 0, max: 255) +} +``` + +(Example [courtesy of Avi](https://forums.swift.org/t/pitch-3-property-wrappers-formerly-known-as-property-delegates/24961/65)) + ### Property wrapper types in the wild There are a number of existing types that already provide the basic structure of a property wrapper type. One fun case is `Unsafe(Mutable)Pointer`, which we could augment to allow easy access to the pointed-to value: @@ -681,7 +739,7 @@ in one of three ways: If the first property wrapper type is generic, its generic arguments must either be given explicitly in the attribute or Swift must be able to deduce them from the variable declaration. That deduction proceeds as follows: -* If the variable has an initial value expression `E`, then the first wrapper type is constrained to equal the type resulting from a call to `A(initialValue: E)`, where `A` is the written type of the attribute. For example: +* If the variable has an initial value expression `E`, then the first wrapper type is constrained to equal the type resulting from a call to `A(initialValue: E, argsA...)`, where `A` is the written type of the attribute and `argsA` are the arguments provided to that attribute. For example: ```swift @Lazy var foo = 17 @@ -690,12 +748,12 @@ If the first property wrapper type is generic, its generic arguments must either // infers the type of '$foo' to be 'Lazy' ``` - If there are multiple wrapper attributes, the argument to this call will instead be a nested call to `B(initialValue: E)` for the written type of the next attribute, and so on recursively. For example: + If there are multiple wrapper attributes, the argument to this call will instead be a nested call to `B(initialValue: E, argsB...)` for the written type of the next attribute, and so on recursively. For example: ```swift - @A @B var bar = 42 + @A @B(name: "Hello") var bar = 42 // type inference as in ... - var $bar = A(initialValue: B(initialValue: 42)) + var $bar = A(initialValue: B(initialValue: 42, name: "Hello")) // infers the type of '$bar' to be 'A' ``` @@ -1317,6 +1375,7 @@ One could express this either by naming the property directly (as above) or, for * `Codable`, `Hashable`, and `Equatable` synthesis are now based on the backing storage properties, which is a simpler model that gives more control to the authors of property wrapper types. * Improved type inference for property wrapper types and clarified that the type of the `wrappedValue` property is used as part of this inference. See the "Type inference" section. * Renamed the `value` property to `wrappedValue` to avoid conflicts. +* Initial values and explicitly-specified initializer arguments can both be used together; see the `@Clamping` example. ## Acknowledgments From 358bfb148ab0035ee93062b76753e1b1dac60df6 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 14 Jun 2019 07:36:52 -0700 Subject: [PATCH 1213/4563] [SE-0258] Bump access of $foo to 'internal' when wrapperValue exists. Also, specify the name of the backing storage property to use $$. --- proposals/0258-property-wrappers.md | 30 ++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index df3bdda83d..ca87c01410 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -527,7 +527,7 @@ let rect2 = $rectangle // through wrapperValue, produces a Ref let topLeft2 = $rectangle.topLeft // through wrapperValue, produces a Ref ``` -The use of `wrapperValue` hides the box from the client, providing direct access to the value in the box (the common case) as well as access to the box contents via Ref. +The use of `wrapperValue` hides the box from the client (the storage variable is renamed to `$$rectangle` and remains private), providing direct access to the value in the box (the common case) as well as access to the box contents via Ref (referenced as `$rectangle`). ### "Clamping" a value within bounds @@ -957,7 +957,7 @@ let $y = 17 // error: cannot declare entity with $-prefixed name '$y' ### Delegating access to the storage property A property wrapper type can choose to hide its instance entirely by providing a property named `wrapperValue`. As with the `wrappedValue` property and`init(initialValue:)`, the `wrapperValue` property must have the -same access level as its property wrapper type. When present, the synthesized storage property is hidden completely and the property `$foo` becomes a computed property accessing the storage property's `wrapperValue`. For example: +same access level as its property wrapper type. When present, the synthesized storage property is hidden further (the private variable is named, e.g., `$$foo`) and the property `$foo` becomes a computed property accessing the storage property's `wrapperValue`. For example: ```swift class StorageManager { @@ -1001,6 +1001,29 @@ let world = $someValue.move() // take value directly from the storage $someValue.initialize(to: "New value") ``` +The `$` variable's access level will be the more restrictive of either `internal` or the access level of the original property. For example: + +```swift +@LongTermStorage(manager: manager, initialValue: "Hello") +public var someValue: String +``` + +is translated into: + +```swift +private var $$someValue: LongTermStorage = LongTermStorage(manager: manager, initialValue: "Hello") + +internal var $someValue: UnsafeMutablePointer { + get { return $$someValue.wrapperValue } + set { $$someValue.wrapperValue = newValue } +} + +public var someValue: String { + get { return $$someValue.wrappedValue } + set { $$someValue.wrappedValue = newValue } +} +``` + There could then be other kinds of storage (e.g., some arena-based storage) described as property wrappers that *also* vend their wrapper types as `UnsafeMutablePointer`: ```swift @@ -1370,7 +1393,8 @@ One could express this either by naming the property directly (as above) or, for * Support for property wrapper composition has been added, using a "nesting" model. * A property with a wrapper can no longer have an explicit `get` or `set` declared, to match with the behavior of existing, similar features (`lazy`, `@NSCopying`). * A property with a wrapper does not need to be `final`. -* Reduced the visibility of the synthesized storage property to `private`, and expanded upon potential future directions to making it more visible. +* Reduced the visibility of the synthesized storage property to `private`. +* When a wrapper type provides `wrapperValue`, the (computed) `$` variable is `internal` (at most) and the backing storage variable gets the prefix `$$` (and remains private). * Removed the restriction banning property wrappers from having names that match the regular expression `_*[a-z].*`. * `Codable`, `Hashable`, and `Equatable` synthesis are now based on the backing storage properties, which is a simpler model that gives more control to the authors of property wrapper types. * Improved type inference for property wrapper types and clarified that the type of the `wrappedValue` property is used as part of this inference. See the "Type inference" section. From 5fa31ee69f440bc93179775bbbbdbce3d0bfbca9 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 14 Jun 2019 10:35:38 -0700 Subject: [PATCH 1214/4563] Put SE-0258 back into review --- proposals/0258-property-wrappers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index ca87c01410..c420feb48d 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -3,9 +3,9 @@ * Proposal: [SE-0258](0258-property-wrappers.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Returned for revision** +* Status: **Active Review (June 14th...24th, 2019)** * Implementation: Available in [master snapshots](https://swift.org/download/#snapshots). -* Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) +* Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) ([review #2](https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843)) * Previous versions: [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) ## Introduction From 57abdc12e1de549de1d229f5d1f3c53174ee079b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 14 Jun 2019 13:55:40 -0700 Subject: [PATCH 1215/4563] [SE-0258] value -> wrappedValue cleanups, toolchain links --- proposals/0258-property-wrappers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index c420feb48d..49b314ad3a 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active Review (June 14th...24th, 2019)** -* Implementation: Available in [master snapshots](https://swift.org/download/#snapshots). +* Implementation: [Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/243//artifact/branch-master/swift-PR-25464-243-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/317//artifact/branch-master/swift-PR-25464-317-osx.tar.gz), and [master or 5.1 snapshots](https://swift.org/download/#snapshots) after June 14, 2019. * Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) ([review #2](https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843)) * Previous versions: [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) @@ -478,8 +478,8 @@ struct Ref { subscript(dynamicMember keyPath: WritableKeyPath) -> Ref { return Ref( - read: { self.value[keyPath: keyPath] }, - write: { self.value[keyPath: keyPath] = $0 }) + read: { self.wrappedValue[keyPath: keyPath] }, + write: { self.wrappedValue[keyPath: keyPath] = $0 }) } } ``` @@ -507,11 +507,11 @@ class Box { var wrappedValue: Value init(initialValue: Value) { - self.value = initialValue + self.wrappedValue = initialValue } var wrapperValue: Ref { - return Ref(read: { self.value }, write: { self.value = $0 }) + return Ref(read: { self.wrappedValue }, write: { self.wrappedValue = $0 }) } } ``` From 4ace0c28bb8cec025ca58724f764443797491a28 Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Sat, 15 Jun 2019 02:23:46 -0700 Subject: [PATCH 1216/4563] Fix a typo in SE-0246. Missing backquotes before and after a comma. --- proposals/0246-mathable.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0246-mathable.md b/proposals/0246-mathable.md index 22126e4380..69fc1d0eee 100644 --- a/proposals/0246-mathable.md +++ b/proposals/0246-mathable.md @@ -315,8 +315,8 @@ create a bit of a mess. ### Future expansion The following functions recommended by IEEE 754 are not provided at this point (because implementations are not widely available), but are planned for future expansion, -possibly with implementation directly in Swift: `cospi`, `sinpi`, `tanpi`, `acospi`, `asinpi, -atanpi`, `exp2m1`, `exp10m1`, `log2p1`, `log10p1`, `compound` (these are the names +possibly with implementation directly in Swift: `cospi`, `sinpi`, `tanpi`, `acospi`, `asinpi`, +`atanpi`, `exp2m1`, `exp10m1`, `log2p1`, `log10p1`, `compound` (these are the names used by IEEE 754; Swift can use different names if we like). ### Functions not defined on ElementaryFunctions From bb8709c2ddca25c21a3c1e0298ce9457911dbfba Mon Sep 17 00:00:00 2001 From: Adrian Z Date: Sat, 15 Jun 2019 17:45:34 +0200 Subject: [PATCH 1217/4563] Fixing old renamed property name. (#1051) --- proposals/0258-property-wrappers.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 49b314ad3a..0d064c2a63 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -144,8 +144,8 @@ translates to: ```swift var $foo: Lazy = Lazy(initialValue: 1738) var foo: Int { - get { return $foo.value } - set { $foo.value = newValue } + get { return $foo.wrappedValue } + set { $foo.wrappedValue = newValue } } ``` @@ -643,8 +643,8 @@ Composition is implemented by nesting later wrapper types inside earlier wrapper ```swift var $path: DelayedMutable> = .init() var path: UIBezierPath { - get { return $path.value.value } - set { $path.value.value = newValue } + get { return $path.wrappedValue.wrappedValue } + set { $path.wrappedValue.wrappedValue = newValue } } ``` @@ -695,7 +695,7 @@ in one of three ways: // ... implemented as var $foo: Lazy = Lazy(initialValue: 17) - var foo: Int { /* access via $foo.value as described above */ } + var foo: Int { /* access via $foo.wrappedValue as described above */ } ``` When there are multiple, composed property wrappers, all of them must provide an `init(initialValue:)`, and the resulting initialization will wrap each level of call: @@ -704,7 +704,7 @@ in one of three ways: // ... implemented as var $path: Lazy> = .init(initialValue: .init(initialValue: UIBezierPath())) - var path: UIBezierPath { /* access via $path.value.value as described above */ } + var path: UIBezierPath { /* access via $path.wrappedValue.wrappedValue as described above */ } ``` 2. Via a value of the property wrapper type, by placing the initializer @@ -718,7 +718,7 @@ in one of three ways: // ... implemented as var $someInt: UnsafeMutablePointer = UnsafeMutablePointer(mutating: addressOfInt) - var someInt: Int { /* access via $someInt.value */ } + var someInt: Int { /* access via $someInt.wrappedValue */ } ``` When there are multiple, composed property wrappers, only the first (outermost) wrapper may have initializer arguments. @@ -730,7 +730,7 @@ in one of three ways: // ... implemented as var $x: DelayedMutable = DelayedMutable() - var x: Int { /* access via $x.value */ } + var x: Int { /* access via $x.wrappedValue */ } ``` When there are multiple, composed property wrappers, only the first (outermost) wrapper needs to have an `init()`. @@ -892,7 +892,7 @@ apply to properties that have wrappers. Let's expand the example of // ... x2 = 17 // okay, treated as $x2 = .init(initialValue: 17) // ... -x2 = 42 // okay, treated as x2 = 42 (calls the Lazy.value setter) +x2 = 42 // okay, treated as x2 = 42 (calls the Lazy.wrappedValue setter) ``` ### Memberwise initializers @@ -1102,8 +1102,8 @@ struct AB { private var storage: A> var wrappedValue: Value { - get { storage.value.value } - set { storage.value.value = newValue } + get { storage.wrappedValue.wrappedValue } + set { storage.wrappedValue.wrappedValue = newValue } } } ``` From 5daad12061ebbfb81d2ad317eba8838e6ffc21c8 Mon Sep 17 00:00:00 2001 From: Scott Perry Date: Wed, 19 Jun 2019 19:29:49 -0700 Subject: [PATCH 1218/4563] [amendment] Add `inverse()` to SE-0240 for API parity with Foundation (#1052) * [amendment] Add `inverse()` to SE-0240 for API parity with -[NSOrderedCollectionDifference inverseDifference] * Kick off amendment review --- proposals/0240-ordered-collection-diffing.md | 26 ++++---------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/proposals/0240-ordered-collection-diffing.md b/proposals/0240-ordered-collection-diffing.md index f6f7f51f33..cce9fbb9f3 100644 --- a/proposals/0240-ordered-collection-diffing.md +++ b/proposals/0240-ordered-collection-diffing.md @@ -2,8 +2,9 @@ * Proposal: [SE-0240](0240-ordered-collection-diffing.md) * Authors: [Scott Perry](https://github.com/numist), [Kyle Macomber](https://github.com/kylemacomber) -* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Review Manager: [Doug Gregor](https://github.com/DougGregor), [Ben Cohen](https://github.com/AirspeedSwift) * Status: **Implemented (Swift 5.1)** +* Amendment status: **Active Review (June 19 - 25 2019)** * Implementation: [apple/swift#21845](https://github.com/apple/swift/pull/21845) * Decision notes: [Rationale](https://forums.swift.org/t/accepted-with-modifications-se-0240-ordered-collection-diffing/20008) @@ -140,6 +141,9 @@ public struct CollectionDifference { /// The `.remove` changes contained by this difference, from lowest offset to highest public var removals: [Change] { get } + + /// Produces a difference that is the functional inverse of `self` + public func inverse() -> CollectionDifference } /// A CollectionDifference is itself a Collection. @@ -290,26 +294,6 @@ The name `CollectionDifference` gives us the opportunity to build a family of re This API allows for more interesting functionality that is not included in this proposal. -For example, this propsal could have included a `inverted()` function on the difference type that would return a new difference that would undo the application of the original. - -The lack of additional conveniences and functionality is intentional; the goal of this proposal is to lay the groundwork that such extensions would be built upon. - -In the case of `inverted()`, clients of the API in this proposal can use `Collection.map()` to invert the case of each `Change` and feed the result into `CollectionDifference.init(_:)`: - -``` swift -let diff: CollectionDifference = /* ... */ -let inverted = CollectionDifference( - diff.map({(change) -> CollectionDifference.Change in - switch change { - case .insert(offset: let o, element: let e, associatedWith: let a): - return .remove(offset: o, element: e, associatedWith: a) - case .remove(offset: let o, element: let e, associatedWith: let a): - return .insert(offset: o, element: e, associatedWith: a) - } - }) -)! -``` - ### `mutating apply(_:)` There is no mutating applicator because there is no algorithmic advantage to in-place application. From 8535bdc15938cba41d26156d76878c7994815176 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 26 Jun 2019 17:47:20 -0700 Subject: [PATCH 1219/4563] [SE-0258] Revision #3 of the proposal, addressing feedback from review #2 Changes from the second reviewed version * The synthesized storage property is always named with a leading `_` and is always `private`. * The `wrapperValue` property has been renamed to `projectedValue` to make it sufficiently different from `wrappedValue`. This also gives us the "projection" terminology to talk about the `$` property. * The projected property (e.g., `$foo`) always has the same access as the original wrapped property, rather than being artificially limited to `internal`. This reflects the idea that, for property wrapper types that have a projection, the projection is equal in importance to the wrapped value. --- proposals/0258-property-wrappers.md | 344 +++++++++++++++++++--------- 1 file changed, 242 insertions(+), 102 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 0d064c2a63..b9a9bf5a11 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -3,15 +3,15 @@ * Proposal: [SE-0258](0258-property-wrappers.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (June 14th...24th, 2019)** -* Implementation: [Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/243//artifact/branch-master/swift-PR-25464-243-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/317//artifact/branch-master/swift-PR-25464-317-osx.tar.gz), and [master or 5.1 snapshots](https://swift.org/download/#snapshots) after June 14, 2019. +* Status: **Awaiting Review** +* Implementation: [Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/251//artifact/branch-master/swift-PR-25781-251-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/327//artifact/branch-master/swift-PR-25781-327-osx.tar.gz) * Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) ([review #2](https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843)) -* Previous versions: [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) +* Previous versions: [Revision #2](https://github.com/apple/swift-evolution/blob/bb8709c2ddca25c21a3c1e0298ce9457911dbfba/proposals/0258-property-wrappers.md), [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) ## Introduction There are property implementation patterns that come up repeatedly. -Rather than hardcode a fixed set of patterns into the compiler, +Rather than hardcode a fixed set of patterns into the compiler (as we have done for `lazy` and `@NSCopying`), we should provide a general "property wrapper" mechanism to allow these patterns to be defined as libraries. @@ -142,17 +142,18 @@ value of the property's type. The property declaration translates to: ```swift -var $foo: Lazy = Lazy(initialValue: 1738) +private var _foo: Lazy = Lazy(initialValue: 1738) var foo: Int { - get { return $foo.wrappedValue } - set { $foo.wrappedValue = newValue } + get { return _foo.wrappedValue } + set { _foo.wrappedValue = newValue } } ``` -The use of the prefix `$` for the synthesized storage property name is -deliberate: it provides a predictable name for the backing storage, -so that wrapper types can provide API. For example, we could provide -a `reset(_:)` operation on `Lazy` to set it back to a new value: +The use of the prefix `_` for the synthesized storage property name is +deliberate: it provides a predictable name for the synthesized storage property that +fits established conventions for `private` stored properties. For example, +we could provide a `reset(_:)` operation on `Lazy` to set it back to a new +value: ```swift extension Lazy { @@ -163,37 +164,126 @@ extension Lazy { } } -$foo.reset(42) +_foo.reset(42) ``` -The property wrapper instance can be initialized directly by providing the initializer arguments in parentheses after the name. This could be used, for example, when a particular property wrapper requires more setup to provide access to a value (example courtesy of Harlan Haskins): +The backing storage property can also be explicitly initialized. For example: + +```swift +extension Lazy { + init(body: @escaping () -> Value) { + self = .uninitialized(body) + } +} + +func createAString() -> String { ... } + +@Lazy var bar: String // not initialized yet +_bar = Lazy(body: createAString) +``` + +The property wrapper instance can be initialized directly by providing the initializer arguments in parentheses after the name. The above code can be written equivalently in a single declaration as: + +```swift +@Lazy(body: createAString) var bar: String +``` + +Property wrappers can be applied to properties at global, local, or type scope. Those properties can have observing accessors (`willSet`/`didSet`), but not explicitly-written getters or setters. + +The `Lazy` property wrapper has little or no interesting API outside of its initializers, so it is not important to export it to clients. However, property wrappers can also describe rich relationships that themselves have interesting API. For example, we might have a notion of a property wrapper that references a database field established by name (example inspired by [Tanner](https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843/14)): ```swift @propertyWrapper -struct UserDefault { - let key: String - let defaultValue: T +public struct Field { + public let name: String + private var record: DatabaseRecord? + private var cachedValue: Value? - var wrappedValue: T { - get { - return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue + public init(name: String) { + self.name = name + } + + public func configure(record: DatabaseRecord) { + self.record = record + } + + public var wrappedValue: Value { + mutating get { + if cachedValue == nil { fetch() } + return cachedValue! } + set { - UserDefaults.standard.set(newValue, forKey: key) + cachedValue = newValue } } + + public func flush() { + if let value = cachedValue { + record!.flush(fieldName: name, value) + } + } + + public mutating func fetch() { + cachedValue = record!.fetch(fieldName: name, type: Value.self) + } } +``` -enum GlobalSettings { - @UserDefault(key: "FOO_FEATURE_ENABLED", defaultValue: false) - static var isFooFeatureEnabled: Bool +We could define our model based on the `Field` property wrapper: + +```swift +public struct Person: DatabaseModel { + @Field(name: "first_name") public var firstName: String + @Field(name: "last_name") public var lastName: String + @Field(name: "date_of_birth") public var birthdate: Date +} +``` + +`Field` itself has API that is important to users of `Person`: it lets us flush existing values, fetch new values, and retrieve the name of the corresponding field in the database. However, the underscored variables for each of the properties of our model (`_firstName`, `_lastName`, and `_birthdate`) are `private`, so our clients cannot manipulation them directly. + +To vend API, the property wrapper type `Field` can provide a *projection* that allows us to manipulate the relationship of the field to the database. Projection properties are prefixed with a `$`, so the projection of the `firstName` property is called `$firstName` and is visible wherever `firstName` is visible. Property wrapper types opt into provided a projection by defining a `projectedValue` property: + +```swift +@propertyWrapper +public struct Field { + // ... API as before ... - @UserDefault(key: "BAR_FEATURE_ENABLED", defaultValue: false) - static var isBarFeatureEnabled: Bool + public var projectedValue: Self { + get { self } + set { self = newValue } + } } ``` -Property wrappers can be applied to properties at global, local, or type scope. Those properties can have observing accessors (`willSet`/`didSet`), but not explicitly-written getters or setters. +When `projectedValue` is present, the projection variable is created as a wrapper around `projectedValue`. For example, the following property: + +```swift +@Field(name: "first_name") public var firstName: String +``` + +expands to: + +```swift +private var _firstName: Field = Field(name: "first_name") + +public var firstName: String { + get { _firstName.wrappedValue } + set { _firstName.wrappedValue = newValue } +} + +public var $firstName: Field { + get { _firstName.projectedValue } + set { _firstName.projectedValue = newValue } +} +``` + +This allows clients to manipulate both the property and its projection, e.g., + +```swift +somePerson.firstName = "Taylor" +$somePerson.flush() +``` ## Examples @@ -354,14 +444,14 @@ to weave lower-level atomic operations (`increment`, `load`, `compareAndExchange @Atomic var counter: Int if thingHappened { - $counter.increment() + _counter.increment() } print(counter) @Atomic var initializedOnce: Int? if initializedOnce == nil { let newValue: Int = /*computeNewValue*/ - if !$initializedOnce.compareAndExchange(oldValue: nil, newValue: newValue) { + if !_initializedOnce.compareAndExchange(oldValue: nil, newValue: newValue) { // okay, someone else initialized it. clean up if needed } } @@ -416,6 +506,39 @@ final class ThreadSpecific { } ``` + +### User defaults + +Property wrappers can be used to provide typed properties into for +string-keyed data, such as [user defaults](https://developer.apple.com/documentation/foundation/userdefaults) (example courtesy of Harlan Haskins), +encapsulating the mechanism for extracting that data in the wrapper type. +For example: + +```swift +@propertyWrapper +struct UserDefault { + let key: String + let defaultValue: T + + var wrappedValue: T { + get { + return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue + } + set { + UserDefaults.standard.set(newValue, forKey: key) + } + } +} + +enum GlobalSettings { + @UserDefault(key: "FOO_FEATURE_ENABLED", defaultValue: false) + static var isFooFeatureEnabled: Bool + + @UserDefault(key: "BAR_FEATURE_ENABLED", defaultValue: false) + static var isBarFeatureEnabled: Bool +} +``` + ### Copy-on-write With some work, property wrappers can provide copy-on-write wrappers (original example courtesy of Brent Royal-Gordon): @@ -433,7 +556,7 @@ struct CopyOnWrite { private(set) var wrappedValue: Value - var wrapperValue: Value { + var projectedValue: Value { mutating get { if !isKnownUniquelyReferenced(&wrappedValue) { wrappedValue = value.copy() @@ -447,7 +570,7 @@ struct CopyOnWrite { } ``` -`wrapperValue` provides delegation for the synthesized storage property, allowing the copy-on-write wrapper to be used directly: +`projectedValue` provides delegation for the synthesized storage property, allowing the copy-on-write wrapper to be used directly: ```swift @CopyOnWrite var storage: MyStorageBuffer @@ -455,15 +578,14 @@ struct CopyOnWrite { // Non-modifying access: let index = storage.index(of: …) -// For modification, access $storage, which goes through `wrapperValue`: +// For modification, access $storage, which goes through `projectedValue`: $storage.append(…) ``` ### `Ref` / `Box` We can define a property wrapper type `Ref` that is an abstracted reference -to some value that can be get/set, which is effectively a programmatic computed -property: +to some value that can be get/set, which is effectively a programmatic computed property: ```swift @propertyWrapper @@ -510,7 +632,7 @@ class Box { self.wrappedValue = initialValue } - var wrapperValue: Ref { + var projectedValue: Ref { return Ref(read: { self.wrappedValue }, write: { self.wrappedValue = $0 }) } } @@ -523,11 +645,11 @@ Now, we can define a new `Box` directly: print(rectangle) // access the rectangle print(rectangle.topLeft) // access the top left coordinate of the rectangle -let rect2 = $rectangle // through wrapperValue, produces a Ref -let topLeft2 = $rectangle.topLeft // through wrapperValue, produces a Ref +let rect2 = $rectangle // through projectedValue, produces a Ref +let topLeft2 = $rectangle.topLeft // through projectedValue, produces a Ref ``` -The use of `wrapperValue` hides the box from the client (the storage variable is renamed to `$$rectangle` and remains private), providing direct access to the value in the box (the common case) as well as access to the box contents via Ref (referenced as `$rectangle`). +The use of `projectedValue` hides the box from the client (`_rectangle` remains private), providing direct access to the value in the box (the common case) as well as access to the box contents via `Ref` (referenced as `$rectangle`). ### "Clamping" a value within bounds @@ -578,10 +700,10 @@ The synthesized memberwise initializer demonstrates how the initialization itsel ```swift init(red: Int = 127, green: Int = 127, blue: Int = 127, alpha: Int = 255) { - $red = Clamping(initialValue: red, min: 0, max: 255) - $green = Clamping(initialValue: green, min: 0, max: 255) - $blue = Clamping(initialValue: blue, min: 0, max: 255) - $alpha = Clamping(initialValue: alpha, min: 0, max: 255) + _red = Clamping(initialValue: red, min: 0, max: 255) + _green = Clamping(initialValue: green, min: 0, max: 255) + _blue = Clamping(initialValue: blue, min: 0, max: 255) + _alpha = Clamping(initialValue: alpha, min: 0, max: 255) } ``` @@ -612,10 +734,10 @@ var someInt: Int someInt = 17 // equivalent to someInt.pointee = 17 print(someInt) -$someInt.deallocate() +_someInt.deallocate() ``` -RxCocoa's [`BehaviorRelay`](https://github.com/ReactiveX/RxSwift/blob/master/RxCocoa/Traits/BehaviorRelay.swift) replays the most recent value provided to it for each of the subscribed observers. It is created with an initial value, has `wrappedValue` property to access the current value, as well as API to `subscribe` a new observer: (Thanks to Adrian Zubarev for pointing this out) +RxCocoa's [`BehaviorRelay`](https://github.com/ReactiveX/RxSwift/blob/master/RxCocoa/Traits/BehaviorRelay.swift) replays the most recent value provided to it for each of the subscribed observers. It is created with an initial value, has `wrappedValue` property to access the current value and a `projectedValue` to expose a projection providing API to `subscribe` a new observer: (Thanks to Adrian Zubarev for pointing this out) ```swift @BehaviorRelay @@ -627,6 +749,11 @@ $myValue.accept(42) // set a new value via the synthesized storage property print(myValue) // print the most recent value ``` +[Combine's `Published`](https://developer.apple.com/documentation/combine/published) property wrapper is similar in spirit, allowing clients to subscribe to `@Published` properties (via the `$` projection) to receive updates when the value changes. + +[SwiftUI](https://developer.apple.com/xcode/swiftui/) makes extensive use of +property wrappers to declare local state (`@State`) and express data dependencies on other state that can effect the UI (`@EnvironmentObject`, `@Environment`, `@ObjectBinding`). It makes extensive use of projections to the [`Binding`](https://developer.apple.com/documentation/swiftui/binding) property wrapper to allow controlled mutation of the state that affects UI. + ## Composition of property wrappers When multiple property wrappers are provided for a given property, @@ -641,18 +768,18 @@ Here, we have a property for which we can delay initialization until later. When Composition is implemented by nesting later wrapper types inside earlier wrapper types, where the innermost nested type is the original property's type. For the example above, the backing storage will be of type `DelayedMutable>`, and the synthesized getter/setter for `path` will look through both levels of `.wrappedValue`: ```swift -var $path: DelayedMutable> = .init() +private var _path: DelayedMutable> = .init() var path: UIBezierPath { - get { return $path.wrappedValue.wrappedValue } - set { $path.wrappedValue.wrappedValue = newValue } + get { return _path.wrappedValue.wrappedValue } + set { _path.wrappedValue.wrappedValue = newValue } } ``` Note that this design means that property wrapper composition is not commutative, because the order of the attributes affects how the nesting is performed: ```swift -@DelayedMutable @Copying var path1: UIBezierPath // $path1 has type DelayedMutable> -@Copying @DelayedMutable var path2: UIBezierPath // error: $path2 has ill-formed type Copying> +@DelayedMutable @Copying var path1: UIBezierPath // _path1 has type DelayedMutable> +@Copying @DelayedMutable var path2: UIBezierPath // error: _path2 has ill-formed type Copying> ``` In this case, the type checker prevents the second ordering, because `DelayedMutable` does not conform to the `NSCopying` protocol. This won't always be the case: some semantically-bad compositions won't necessarily by caught by the type system. Alternatives to this approach to composition are presented in "Alternatives considered." @@ -694,8 +821,8 @@ in one of three ways: @Lazy var foo = 17 // ... implemented as - var $foo: Lazy = Lazy(initialValue: 17) - var foo: Int { /* access via $foo.wrappedValue as described above */ } + private var _foo: Lazy = Lazy(initialValue: 17) + var foo: Int { /* access via _foo.wrappedValue as described above */ } ``` When there are multiple, composed property wrappers, all of them must provide an `init(initialValue:)`, and the resulting initialization will wrap each level of call: @@ -703,8 +830,8 @@ in one of three ways: @Lazy @Copying var path = UIBezierPath() // ... implemented as - var $path: Lazy> = .init(initialValue: .init(initialValue: UIBezierPath())) - var path: UIBezierPath { /* access via $path.wrappedValue.wrappedValue as described above */ } + private var _path: Lazy> = .init(initialValue: .init(initialValue: UIBezierPath())) + var path: UIBezierPath { /* access via _path.wrappedValue.wrappedValue as described above */ } ``` 2. Via a value of the property wrapper type, by placing the initializer @@ -717,8 +844,8 @@ in one of three ways: var someInt: Int // ... implemented as - var $someInt: UnsafeMutablePointer = UnsafeMutablePointer(mutating: addressOfInt) - var someInt: Int { /* access via $someInt.wrappedValue */ } + private var _someInt: UnsafeMutablePointer = UnsafeMutablePointer(mutating: addressOfInt) + var someInt: Int { /* access via _someInt.wrappedValue */ } ``` When there are multiple, composed property wrappers, only the first (outermost) wrapper may have initializer arguments. @@ -729,8 +856,8 @@ in one of three ways: @DelayedMutable var x: Int // ... implemented as - var $x: DelayedMutable = DelayedMutable() - var x: Int { /* access via $x.wrappedValue */ } + private var _x: DelayedMutable = DelayedMutable() + var x: Int { /* access via _x.wrappedValue */ } ``` When there are multiple, composed property wrappers, only the first (outermost) wrapper needs to have an `init()`. @@ -744,8 +871,8 @@ If the first property wrapper type is generic, its generic arguments must either ```swift @Lazy var foo = 17 // type inference as in... - var $foo: Lazy = Lazy(initialValue: 17) - // infers the type of '$foo' to be 'Lazy' + private var _foo: Lazy = Lazy(initialValue: 17) + // infers the type of '_foo' to be 'Lazy' ``` If there are multiple wrapper attributes, the argument to this call will instead be a nested call to `B(initialValue: E, argsB...)` for the written type of the next attribute, and so on recursively. For example: @@ -753,8 +880,8 @@ If the first property wrapper type is generic, its generic arguments must either ```swift @A @B(name: "Hello") var bar = 42 // type inference as in ... - var $bar = A(initialValue: B(initialValue: 42, name: "Hello")) - // infers the type of '$bar' to be 'A' + private var _bar = A(initialValue: B(initialValue: 42, name: "Hello")) + // infers the type of '_bar' to be 'A' ``` * Otherwise, if the first wrapper attribute has direct initialization arguments `E...`, the outermost wrapper type is constrained to equal the type resulting from `A(E...)`, where `A` is the written type of the first attribute. Wrapper attributes after the first may not have direct initializers. For example: @@ -763,8 +890,8 @@ If the first property wrapper type is generic, its generic arguments must either @UnsafeMutablePointer(mutating: addressOfInt) var someInt // type inference as in... - var $someInt: UnsafeMutablePointer = UnsafeMutablePointer.init(mutating: addressOfInt) - // infers the type of `$someInt` to be `UnsafeMutablePointer` + private var _someInt: UnsafeMutablePointer = UnsafeMutablePointer.init(mutating: addressOfInt) + // infers the type of `_someInt` to be `UnsafeMutablePointer` ``` * Otherwise, if there is no initialization, and the original property has a type annotation, the type of the `wrappedValue` property in the last wrapper type is constrained to equal the type annotation of the original property. For example: @@ -870,7 +997,7 @@ example: ```swift @Lazy var x: Int // ... -x = 17 // okay, treated as $x = .init(initialValue: 17) +x = 17 // okay, treated as _x = .init(initialValue: 17) ``` The synthesized storage property can also be initialized directly, @@ -879,7 +1006,7 @@ e.g., ```swift @UnsafeMutable var y: Int // ... -$y = UnsafeMutable(pointer: addressOfInt) // okay +_y = UnsafeMutable(pointer: addressOfInt) // okay ``` Note that the rules of [definite @@ -890,7 +1017,7 @@ apply to properties that have wrappers. Let's expand the example of ```swift @Lazy var x2: Int // ... -x2 = 17 // okay, treated as $x2 = .init(initialValue: 17) +x2 = 17 // okay, treated as _x2 = .init(initialValue: 17) // ... x2 = 42 // okay, treated as x2 = 42 (calls the Lazy.wrappedValue setter) ``` @@ -929,10 +1056,10 @@ struct Foo { y: Int = 17, z: Lazy = Lazy(closure: { getBool() }), w: Image) { - self.$x = x - self.$y = Lazy(initialValue: y) - self.$z = z - self.$w = CopyOnWrite(initialValue: w) + self._x = x + self._y = Lazy(initialValue: y) + self._z = z + self._w = CopyOnWrite(initialValue: w) } } ``` @@ -940,24 +1067,25 @@ struct Foo { ### Codable, Hashable, and Equatable synthesis Synthesis for `Encodable`, `Decodable`, `Hashable`, and `Equatable` -use the backing storage property. This allows property wrapper types to determine their own serialization and equality behavior. For `Encodable` and `Decodable`, the name used for keyed archiving is that of the original property declaration (without the `$`). +use the backing storage property. This allows property wrapper types to determine their own serialization and equality behavior. For `Encodable` and `Decodable`, the name used for keyed archiving is that of the original property declaration (without the `_`). ### $ identifiers Currently, identifiers starting with a `$` are not permitted in Swift programs. Today, such identifiers are only used in LLDB, where they can be used to name persistent values within a debugging session. -This proposal loosens these rules slightly: the Swift compiler will introduce identifiers that start with `$` (for the synthesized storage property), and Swift code can reference those properties. However, Swift code cannot declare any new entities with an identifier that begins with `$`. For example: +This proposal loosens these rules slightly: the Swift compiler will introduce identifiers that start with `$` (for the projection property), and Swift code can reference those properties. However, Swift code cannot declare any new entities with an identifier that begins with `$`. For example: ```swift -@Lazy var x = 17 -print($x) // okay to refer to compiler-defined $x -let $y = 17 // error: cannot declare entity with $-prefixed name '$y' +@CopyOnWrite var x = UIBezierPath() +print($x) // okay to refer to compiler-defined $x +let $y = UIBezierPath() // error: cannot declare entity with $-prefixed name '$y' ``` -### Delegating access to the storage property +### Projections -A property wrapper type can choose to hide its instance entirely by providing a property named `wrapperValue`. As with the `wrappedValue` property and`init(initialValue:)`, the `wrapperValue` property must have the -same access level as its property wrapper type. When present, the synthesized storage property is hidden further (the private variable is named, e.g., `$$foo`) and the property `$foo` becomes a computed property accessing the storage property's `wrapperValue`. For example: +A property wrapper type can choose to provide a projection property (e.g., `$foo`) to expose more API for each wrapped property by defining a `projectedValue` property. +As with the `wrappedValue` property and `init(initialValue:)`, the `projectedValue` property must have the +same access level as its property wrapper type. For example: ```swift class StorageManager { @@ -978,7 +1106,7 @@ struct LongTermStorage { set { pointer.pointee = newValue } } - var wrapperValue: UnsafeMutablePointer { + var projectedValue: UnsafeMutablePointer { return pointer } } @@ -995,14 +1123,13 @@ var someValue: String print(someValue) // prints "Hello" someValue = "World" // update the value in storage to "World" -// $someValue accesses the wrapperValue property of the wrapper instance, which +// $someValue accesses the projectedValue property of the wrapper instance, which // is an UnsafeMutablePointer let world = $someValue.move() // take value directly from the storage $someValue.initialize(to: "New value") ``` -The `$` variable's access level will be the more restrictive of either `internal` or the access level of the original property. For example: - +The projection property has the same access level as the original property: ```swift @LongTermStorage(manager: manager, initialValue: "Hello") public var someValue: String @@ -1011,16 +1138,16 @@ public var someValue: String is translated into: ```swift -private var $$someValue: LongTermStorage = LongTermStorage(manager: manager, initialValue: "Hello") +private var _someValue: LongTermStorage = LongTermStorage(manager: manager, initialValue: "Hello") -internal var $someValue: UnsafeMutablePointer { - get { return $$someValue.wrapperValue } - set { $$someValue.wrapperValue = newValue } +public var $someValue: UnsafeMutablePointer { + get { return _someValue.projectedValue } + set { _someValue.projectedValue = newValue } } public var someValue: String { - get { return $$someValue.wrappedValue } - set { $$someValue.wrappedValue = newValue } + get { return _someValue.wrappedValue } + set { _someValue.wrappedValue = newValue } } ``` @@ -1041,13 +1168,13 @@ struct ArenaStorage { set { pointer.pointee = newValue } } - var wrapperValue: UnsafeMutablePointer { + var projectedValue: UnsafeMutablePointer { return pointer } } ``` -The `someValue` variable from the previous example could be switched over to use arena-based storage without changing any of the clients of `someValue` or its wrapper property `$someValue`: +The `someValue` variable from the previous example could be switched over to use arena-based storage without changing any of the clients of `someValue` or its projection property `$someValue`: ```swift @ArenaStorage(arena: currentConnectionArena, initialValue: "Hello") @@ -1055,7 +1182,7 @@ var someValue: String ``` Each of the property wrapper types could have different implementations with -different data, but all of them present the same interface through `$someValue` and `someValue`. Note also that the `$someValue` is not writable, because `wrapperValue` is a get-only property. +different data, but all of them present the same interface through `$someValue` and `someValue`. Note also that the `$someValue` is not writable, because `projectedValue` is a get-only property. ### Restrictions on the use of property wrappers @@ -1069,7 +1196,7 @@ There are a number of restrictions on the use of property wrappers when defining * A property with a wrapper must be the only property declared within its enclosing declaration (e.g., `@Lazy var (x, y) = /* ... */` is ill-formed). * A property with a wrapper shall not define a getter or setter. * The `wrappedValue` property and (if present) `init(initialValue:)` of a property wrapper type shall have the same access as the property wrapper type. -* The `wrapperValue` property, if present, shall have the same access as the property wrapper type. +* The `projectedValue` property, if present, shall have the same access as the property wrapper type. * The `init()` initializer, if present, shall have the same access as the property wrapper type. ## Impact on existing code @@ -1185,6 +1312,11 @@ There are some small advantages to this syntax over the attribute formulation: The main problem with `by` is its novelty: there isn't anything else in Swift quite like the `by` keyword above, and it is unlikely that the syntax would be re-used for any other feature. As a keyword, `by` is quite meaningless, and brainstorming during the [initial pitch](https://forums.swift.org/t/pitch-property-delegates/21895) didn't find any clearly good names for this functionality. +### Alternative spellings for the `$` projection property + +The prefix `$` spelling for the projection property has been the source of +much debate. A number of alternatives have been proposed, including longer `#`-based spellings (e.g., `#storage(of: foo)`) and postfix `$` (e.g., `foo$`). The postfix `$` had the most discussion, based on the idea that it opens up more extension points in the future (e.g., `foo$storage` could refer to the backing storage, `foo$databaseHandle` could refer to a specific "database handle" projection for certain property wrappers, etc.). However, doing so introduces yet another new namespace of names to the language ("things that follow `$`) and isn't motivated by enough strong use cases. + ### The 2015-2016 property behaviors design Property wrappers address a similar set of use cases to *property behaviors*, which were [proposed and @@ -1219,30 +1351,30 @@ the prior proposal are: ## Future Directions -### Access to the storage property +### Finer-grained access control -By default, the synthesized storage property will have `private` access. However, there are various circumstances where it would be beneficial to expose the synthesized storage property. This could be performed "per-property", e.g., by introducing a syntax akin to `private(set)`: +By default, the synthesized storage property will have `private` access, and the projection property (when available) will have the same access as the original wrapped property. However, there are various circumstances where it would be beneficial to expose the synthesized storage property. This could be performed "per-property", e.g., by introducing a syntax akin to `private(set)`: ```swift -// both foo and $foo are publicly visible -@Atomic -public public(wrapper) var foo: Int = 1738 +// both foo and _foo are publicly visible, $foo remains private +@SomeWrapper +public public(storage) private(projection) var foo: Int = 1738 ``` One could also consider having the property wrapper types themselves declare that the synthesized storage properties for properties using those wrappers should have the same access as the original property. For example: ```swift -@propertyWrapper(wrapperIsAccessible: true) -struct Atomic { +@propertyWrapper(storageIsAccessible: true) +struct SomeWrapper { var wrappedValue: T { ... } } -// both bar and $bar are publicly visible -@Atomic +// both bar and _bar are publicly visible +@SomeWrapper public var bar: Int = 1738 ``` -The two features could also be combined, allowing property wrapper types to provide the default behavior and the `access-level(wrapper)` syntax to change the default. The current proposal's `private`-by-default is meant to be a conservative first step to allow a separate exploration into expanding the visibility of the backing storage. +The two features could also be combined, allowing property wrapper types to provide the default behavior and the `access-level(...)` syntax to change the default. The current proposal's rules are meant to provide the right defaults while allowing for a separate exploration into expanding the visibility of the synthesized properties. ### Referencing the enclosing 'self' in a wrapper type @@ -1386,7 +1518,15 @@ lazy var fooBacking: SomeWrapper One could express this either by naming the property directly (as above) or, for an even more general solution, by providing a keypath such as `\.someProperty.someOtherProperty`. -## Changes from the first reviewed version +## Revisions + +### Changes from the second reviewed version + +* The synthesized storage property is always named with a leading `_` and is always `private`. +* The `wrapperValue` property has been renamed to `projectedValue` to make it sufficiently different from `wrappedValue`. This also gives us the "projection" terminology to talk about the `$` property. +* The projected property (e.g., `$foo`) always has the same access as the original wrapped property, rather than being artificially limited to `internal`. This reflects the idea that, for property wrapper types that have a projection, the projection is equal in importance to the wrapped value. + +### Changes from the first reviewed version * The name of the feature has been changed from "property delegates" to "property wrappers" to better communicate how they work and avoid the existing uses of the term "delegate" in the Apple developer community * When a property wrapper type has a no-parameter `init()`, properties that use that wrapper type will be implicitly initialized via `init()`. From 1bc7bdca9e5d5b43534770e454490fd555135341 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 26 Jun 2019 18:22:31 -0700 Subject: [PATCH 1220/4563] Fix typo --- proposals/0258-property-wrappers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index b9a9bf5a11..3bb16d67dd 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -240,7 +240,7 @@ public struct Person: DatabaseModel { } ``` -`Field` itself has API that is important to users of `Person`: it lets us flush existing values, fetch new values, and retrieve the name of the corresponding field in the database. However, the underscored variables for each of the properties of our model (`_firstName`, `_lastName`, and `_birthdate`) are `private`, so our clients cannot manipulation them directly. +`Field` itself has API that is important to users of `Person`: it lets us flush existing values, fetch new values, and retrieve the name of the corresponding field in the database. However, the underscored variables for each of the properties of our model (`_firstName`, `_lastName`, and `_birthdate`) are `private`, so our clients cannot manipulate them directly. To vend API, the property wrapper type `Field` can provide a *projection* that allows us to manipulate the relationship of the field to the database. Projection properties are prefixed with a `$`, so the projection of the `firstName` property is called `$firstName` and is visible wherever `firstName` is visible. Property wrapper types opt into provided a projection by defining a `projectedValue` property: From f5f5727dc210cac51635a32b384a228595dcf2fa Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 27 Jun 2019 10:48:55 -0700 Subject: [PATCH 1221/4563] [SE-0258] Fix a few typos --- proposals/0258-property-wrappers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 3bb16d67dd..96b5bf4d00 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -375,7 +375,7 @@ class Foo { Many Cocoa classes implement value-like objects that require explicit copying. Swift currently provides an `@NSCopying` attribute for properties to give -them behavior like Objective-C's `@property(copy)`, invoking the `copy` method +them behave like Objective-C's `@property(copy)`, invoking the `copy` method on new objects when the property is set. We can turn this into a wrapper: ```swift @@ -1395,7 +1395,7 @@ public class MyClass: Superclass { } ``` -This "broadcast a notification that the value has changed" implementation cannot be cleanly factored into a property behavior type, because it needs access to both the underlying storage value (here, `backingMyVar`) and the `self` of the enclosing type. We could require a separate call to register the `self` instance with the wrapper type, e.g., +This "broadcast a notification that the value has changed" implementation cannot be cleanly factored into a property wrapper type, because it needs access to both the underlying storage value (here, `backingMyVar`) and the `self` of the enclosing type. We could require a separate call to register the `self` instance with the wrapper type, e.g., ```swift protocol Observed { From 19569f38835db9afe5ac03accfe4f79ac9a3a1d0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 27 Jun 2019 13:16:48 -0700 Subject: [PATCH 1222/4563] [SE-0258] Provide a more feasible future design for instance properties of classes --- proposals/0258-property-wrappers.md | 67 +++++++++++++++-------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 96b5bf4d00..28e962d59b 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -1387,7 +1387,7 @@ public class MyClass: Superclass { get { return backingMyVar } set { if newValue != backingMyVar { - self.broadcastValueChanged(oldValue: backingMyVar, newValue: newValue) + self.broadcastValueWillChange(newValue: newValue) } backingMyVar = newValue } @@ -1399,7 +1399,7 @@ This "broadcast a notification that the value has changed" implementation cannot ```swift protocol Observed { - func broadcastValueChanged(oldValue: T, newValue: T) + func broadcastValueWillChange(newValue: T) } @propertyWrapper @@ -1419,7 +1419,7 @@ public struct Observable { get { return stored } set { if newValue != stored { - observed?.broadcastValueChanged(oldValue: stored, newValue: newValue) + observed?.broadcastValueWillChange(newValue: newValue) } stored = newValue } @@ -1441,27 +1441,34 @@ public class MyClass: Superclass { } ``` -This isn't as automatic as we would like, and it requires us to have a separate reference to the `self` that is stored within `Observable`. +This isn't as automatic as we would like, and it requires us to have a separate reference to the `self` that is stored within `Observable`. Moreover, it is hiding a semantic problem: the observer code that runs in the `broadcastValueWillChange(newValue:)` must not access the synthesized storage property in any way (e.g., to read the old value through `myVal` or subscribe/unsubscribe an observer via `$myVal`), because doing so will trigger a [memory exclusivity](https://swift.org/blog/swift-5-exclusivity/) violation (because we are calling `broadcastValueWillChange(newValue:)` from within the a setter for the same synthesized storage property). -Instead, we could extend the ad hoc protocol used to access the storage property of a `@propertyWrapper` type a bit further. Instead of (or in addition to) a `wrappedValue` property, a property wrapper type could provide a `subscript(instanceSelf:)` and/or `subscript(typeSelf:)` that receive `self` as a parameter. For example: +To address these issues, we could extend the ad hoc protocol used to access the storage property of a `@propertyWrapper` type a bit further. Instead of a `wrappedValue` property, a property wrapper type could provide a static `subscript(instanceSelf:wrapped:storage:)`that receives `self` as a parameter, along with key paths referencing the original wrapped property and the backing storage property. For example: ```swift @propertyWrapper public struct Observable { - public var stored: Value + privatew var stored: Value public init(initialValue: Value) { self.stored = initialValue } - public subscript(instanceSelf observed: OuterSelf) -> Value { - get { return stored } + public static subscript( + instanceSelf observed: OuterSelf, + wrapped wrappedKeyPath: ReferenceWritableKeyPath, + storage storageKeyPath: ReferenceWritableKeyPath + ) -> Value { + get { + observed[keyPath: storageKeyPath].stored + } set { - if newValue != stored { - observed.broadcastValueChanged(oldValue: stored, newValue: newValue) + let oldValue = observed[keyPath: storageKeyPath].stored + if newValue != oldValue { + observed.broadcastValueWillChange(newValue: newValue) } - stored = newValue + observed[keyPath: storageKeyPath].stored = newValue } } } @@ -1474,39 +1481,35 @@ public class MyClass: Superclass { @Observable public var myVar: Int = 17 // desugars to... - private var $myVar: Observable = Observable(initialValue: 17) + private var _myVar: Observable = Observable(initialValue: 17) public var myVar: Int { - get { return $myVar[instanceSelf: self] } - set { $myVar[instanceSelf: self] = newValue } + get { Observable[instanceSelf: self, wrapped: \MyClass.myVar, storage: \MyClass._myVar] } + set { Observable[instanceSelf: self, wrapped: \MyClass.myVar, storage: \MyClass._myVar] = newValue } } } ``` -This change is backward-compatible with the rest of the proposal. Property wrapper types could provide any (non-empty) subset of the three ways to access the underlying value: +The design uses a `static` subscript and provides key paths to both the original property declaration (`wrapped`) and the synthesized storage property (`storage`). A call to the static subscript's getter or setter does not itself constitute an access to the synthesized storage property, allowing us to address the memory exclusivity violation from the early implementation. The subscript's implementation is given the means to access the synthesized storage property (via the enclosing `self` instance and `storage` key path). In our `Observable` property wrapper, the static subscript setter performs two distinct accesses to the synthesized storage property via `observed[keyPath: storageKeyPath]`: + +1. The read of the old value +2. A write of the new value + +In between these operations is the broadcast operation to any observers. Those observers are permitted to read the old value, unsubscribe themselves from observation, etc., because at the time of the `broadcastValueWillChange(newValue:)` call there is no existing access to the synthesized storage property. -* For instance properties, `subscript(instanceSelf:)` as shown above. -* For static or class properties, `subscript(typeSelf:)`, similar to the above but accepting a metatype parameter. -* For global/local properties, or when the appropriate `subscript` mentioned above isn't provided by the wrapper type, the `wrappedValue` property would be used. +There is a secondary benefit to providing the key paths, because it allows the property wrapper type to reason about its different instances based on the identity of the `wrapped` key path. -The main challenge with this design is that it doesn't directly work when the enclosing type is a value type and the property is settable. In such cases, the parameter to the subscript would get a copy of the entire enclosing value, which would not allow mutation, On the other hand, one could try to pass `self` as `inout`, e.g., +This extension is backward-compatible with the rest of the proposal. Property wrapper types could opt in to this behavior by providing a `static subscript(instanceSelf:wrapped:storage:)`, which would be used in cases where the property wrapper is being applied to an instance property of a class. If such a property wrapper type is applied to a property that is not an instance property of a class, or for any property wrapper types that don't have such a static subscript, the existing `wrappedValue` could be used. One could even allow `wrappedValue` to be specified to be unavailable within property wrapper types that have the static subscript, ensuring that such property wrapper types could only be applied to instance properties of a class: ```swift -public struct MyStruct { - @Observable public var myVar: Int = 17 - - // desugars to... - private var $myVar: Observable = Observable(initialValue: 17) - public var myVar: Int { - get { return $myVar[instanceSelf: self] } - set { $myVar[instanceSelf: &self] = newValue } - } +@availability(*, unavailable) +var wrappedValue: Value { + get { fatalError("only works on instance properties of classes") } + set { fatalError("only works on instance properties of classes") } } ``` -There are a few issues here: first, subscripts don't allow `inout` parameters in the first place, so we would have to figure out how to implement support for such a feature. Second, passing `self` as `inout` while performing access to the property `self.myVar` violates Swift's exclusivity rules ([generalized accessors](https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md#generalized-accessors) might help address this). Third, property wrapper types that want to support `subscript(instanceSelf:)` for both value and reference types would have to overload on `inout` or would have to have a different subscript name (e.g., `subscript(mutatingInstanceSelf:)`). - -So, while we feel that support for accessing the enclosing type's `self` is useful and as future direction, and this proposal could be extended to accommodate it, the open design questions are significant enough that we do not want to tackle them all in a single proposal. - +The same model could be extended to static properties of types (passing the metatype instance for the enclosing `self`) as well as global and local properties (no enclsoing `self`), although we would also need to extend key path support to static, global, and local properties to do so. + ### Delegating to an existing property When specifying a wrapper for a property, the synthesized storage property is implicitly created. However, it is possible that there already exists a property that can provide the storage. One could provide a form of property delegation that creates the getter/setter to forward to an existing property, e.g.: From 007274e0b730e4ead3cde88e6c11325ce19b51e2 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 27 Jun 2019 13:20:25 -0700 Subject: [PATCH 1223/4563] [SE-0258] Fix more typos / grammaros --- proposals/0258-property-wrappers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 28e962d59b..3f37fd78e5 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -375,7 +375,7 @@ class Foo { Many Cocoa classes implement value-like objects that require explicit copying. Swift currently provides an `@NSCopying` attribute for properties to give -them behave like Objective-C's `@property(copy)`, invoking the `copy` method +them behavior like Objective-C's `@property(copy)`, invoking the `copy` method on new objects when the property is set. We can turn this into a wrapper: ```swift @@ -403,7 +403,7 @@ This implementation would address the problem detailed in ### `Atomic` -Support for atomic operations (load, store, increment/decementer, compare-and-exchange) is a commonly-requested Swift feature. While the implementation details for such a feature would involve compiler and standard library magic, the interface itself can be nicely expressed as a property wrapper type: +Support for atomic operations (load, store, increment/decrement, compare-and-exchange) is a commonly-requested Swift feature. While the implementation details for such a feature would involve compiler and standard library magic, the interface itself can be nicely expressed as a property wrapper type: ```swift From d5f1851f1e154aff3dea62bbfba04a1e6cb82e97 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 27 Jun 2019 13:21:31 -0700 Subject: [PATCH 1224/4563] [SE-0258] Fix link to Kotlin's delegated properties --- proposals/0258-property-wrappers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 3f37fd78e5..301ccb631e 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -1296,8 +1296,8 @@ structures that seem to be implemented in terms of only ### Kotlin-like `by` syntax -A previous iteration of this proposal (and its [implementation](https://github.com/apple/swift/pull/23440)) used `by` syntax similar to that of [Kotlin's wrapperd -properties](https://kotlinlang.org/docs/reference/wrapperd-properties.html), where the `by` followed the variable declaration. For example: +A previous iteration of this proposal (and its [implementation](https://github.com/apple/swift/pull/23440)) used `by` syntax similar to that of [Kotlin's delegated +properties](https://kotlinlang.org/docs/reference/delegated-properties.html), where the `by` followed the variable declaration. For example: ```swift var foo by Lazy = 1738 From 73e0bb7aef3166367a0d6f3a86d6ced8d1fb8a34 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 28 Jun 2019 00:05:46 -0400 Subject: [PATCH 1225/4563] Start the third review of SE-0258 --- proposals/0258-property-wrappers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 301ccb631e..6bd6def288 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -3,9 +3,9 @@ * Proposal: [SE-0258](0258-property-wrappers.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Awaiting Review** +* Status: **Active Review (June 27th...July 3rd, 2019)** * Implementation: [Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/251//artifact/branch-master/swift-PR-25781-251-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/327//artifact/branch-master/swift-PR-25781-327-osx.tar.gz) -* Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) ([review #2](https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843)) +* Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) ([review #2](https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843)) ([review #3](https://forums.swift.org/t/se-0258-property-wrappers-third-review/26399)) * Previous versions: [Revision #2](https://github.com/apple/swift-evolution/blob/bb8709c2ddca25c21a3c1e0298ce9457911dbfba/proposals/0258-property-wrappers.md), [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) ## Introduction From 9975e43b89a9980acdf8cc8ac6cbb9af47cf97e5 Mon Sep 17 00:00:00 2001 From: Farzad Sharbafian Date: Fri, 28 Jun 2019 19:38:05 +0430 Subject: [PATCH 1226/4563] Fix typo in 0258-property-wrappers.md (#1055) * Fix typo in 0258-property-wrappers * Fix Typo in 0258-property-wrappers.md In **Referencing the enclosing `self` in a rapper type** section, `$myVar` was used instead of `_myVar` `private` was written as `privatew` --- proposals/0258-property-wrappers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 6bd6def288..6418c6958f 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -731,7 +731,7 @@ From a user perspective, this allows us to set up the unsafe mutable pointer's a @UnsafeMutablePointer(mutating: addressOfAnInt) var someInt: Int -someInt = 17 // equivalent to someInt.pointee = 17 +someInt = 17 // equivalent to _someInt.pointee = 17 print(someInt) _someInt.deallocate() @@ -1434,14 +1434,14 @@ public class MyClass: Superclass { @Observable public var myVar: Int = 17 init() { - // self.$myVar gets initialized with Observable(initialValue: 17) here + // self._myVar gets initialized with Observable(initialValue: 17) here super.init() - self.$myVar.register(self) // register as an Observable + self._myVar.register(self) // register as an Observable } } ``` -This isn't as automatic as we would like, and it requires us to have a separate reference to the `self` that is stored within `Observable`. Moreover, it is hiding a semantic problem: the observer code that runs in the `broadcastValueWillChange(newValue:)` must not access the synthesized storage property in any way (e.g., to read the old value through `myVal` or subscribe/unsubscribe an observer via `$myVal`), because doing so will trigger a [memory exclusivity](https://swift.org/blog/swift-5-exclusivity/) violation (because we are calling `broadcastValueWillChange(newValue:)` from within the a setter for the same synthesized storage property). +This isn't as automatic as we would like, and it requires us to have a separate reference to the `self` that is stored within `Observable`. Moreover, it is hiding a semantic problem: the observer code that runs in the `broadcastValueWillChange(newValue:)` must not access the synthesized storage property in any way (e.g., to read the old value through `myVal` or subscribe/unsubscribe an observer via `_myVal`), because doing so will trigger a [memory exclusivity](https://swift.org/blog/swift-5-exclusivity/) violation (because we are calling `broadcastValueWillChange(newValue:)` from within the a setter for the same synthesized storage property). To address these issues, we could extend the ad hoc protocol used to access the storage property of a `@propertyWrapper` type a bit further. Instead of a `wrappedValue` property, a property wrapper type could provide a static `subscript(instanceSelf:wrapped:storage:)`that receives `self` as a parameter, along with key paths referencing the original wrapped property and the backing storage property. For example: @@ -1449,7 +1449,7 @@ To address these issues, we could extend the ad hoc protocol used to access the ```swift @propertyWrapper public struct Observable { - privatew var stored: Value + private var stored: Value public init(initialValue: Value) { self.stored = initialValue From b1caeccf5d273a1cc53b331d41ea169f77294272 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 28 Jun 2019 16:33:49 +0100 Subject: [PATCH 1227/4563] [SE-0258] Add a table of contents (#1056) --- proposals/0258-property-wrappers.md | 47 +++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 6418c6958f..dbc2395f90 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -8,6 +8,53 @@ * Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) ([review #2](https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843)) ([review #3](https://forums.swift.org/t/se-0258-property-wrappers-third-review/26399)) * Previous versions: [Revision #2](https://github.com/apple/swift-evolution/blob/bb8709c2ddca25c21a3c1e0298ce9457911dbfba/proposals/0258-property-wrappers.md), [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) +## Contents + ++ [Introduction](#introduction) ++ [Motivation](#motivation) ++ [Proposed solution](#proposed-solution) ++ [Examples](#examples) + - [Delayed Initialization](#delayed-initialization) + - [`NSCopying`](#nscopying) + - [`Atomic`](#atomic) + - [Thread-specific storage](#thread-specific-storage) + - [User defaults](#user-defaults) + - [Copy-on-write](#copy-on-write) + - [`Ref` / `Box`](#ref--box) + - ["Clamping" a value within bounds](#clamping-a-value-within-bounds) + - [Property wrapper types in the wild](#property-wrapper-types-in-the-wild) ++ [Composition of property wrappers](#composition-of-property-wrappers) ++ [Detailed design](#detailed-design) + - [Property wrapper types](#property-wrapper-types) + - [Initialization of synthesized storage properties](#initialization-of-synthesized-storage-properties) + - [Type inference with property wrappers](#type-inference-with-property-wrappers) + - [Custom attributes](#custom-attributes) + - [Mutability of properties with wrappers](#mutability-of-properties-with-wrappers) + - [Out-of-line initialization of properties with wrappers](#out-of-line-initialization-of-properties-with-wrappers) + - [Memberwise initializers](#memberwise-initializers) + - [Codable, Hashable, and Equatable synthesis](#codable-hashable-and-equatable-synthesis) + - [$ identifiers](#-identifiers) + - [Projections](#projections) + - [Restrictions on the use of property wrappers](#restrictions-on-the-use-of-property-wrappers) ++ [Impact on existing code](#impact-on-existing-code) ++ [Backward compatibility](#backward-compatibility) ++ [Alternatives considered](#alternatives-considered) + - [Composition](#composition) + - [Composition via nested type lookup](#composition-via-nested-type-lookup) + - [Composition without nesting](#composition-without-nesting) + - [Using a formal protocol instead of `@propertyWrapper`](#using-a-formal-protocol-instead-of-propertywrapper) + - [Kotlin-like `by` syntax](#kotlin-like-by-syntax) + - [Alternative spellings for the `$` projection property](#alternative-spellings-for-the--projection-property) + - [The 2015-2016 property behaviors design](#the-2015-2016-property-behaviors-design) ++ [Future Directions](#future-directions) + - [Finer-grained access control](#finer-grained-access-control) + - [Referencing the enclosing 'self' in a wrapper type](#referencing-the-enclosing-self-in-a-wrapper-type) + - [Delegating to an existing property](#delegating-to-an-existing-property) ++ [Revisions](#revisions) + - [Changes from the second reviewed version](#changes-from-the-second-reviewed-version) + - [Changes from the first reviewed version](#changes-from-the-first-reviewed-version) ++ [Acknowledgments](#acknowledgments) + ## Introduction There are property implementation patterns that come up repeatedly. From 0ac1f69ae790fcb15b8977d62798450c31b3c335 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 2 Jul 2019 17:31:44 -0700 Subject: [PATCH 1228/4563] Update 0260-library-evolution.md --- proposals/0260-library-evolution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0260-library-evolution.md b/proposals/0260-library-evolution.md index 3bd87c18f1..fbd6bc4724 100644 --- a/proposals/0260-library-evolution.md +++ b/proposals/0260-library-evolution.md @@ -3,7 +3,7 @@ * Proposal: [SE-0260](0260-library-evolution.md) * Authors: [Jordan Rose](https://github.com/jrose-apple), [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5.1)** * Implementation: Implemented in Swift 5 for standard library use. PR with renamed attributes [here](https://github.com/apple/swift/pull/24185). * Pre-review discussion: [Forum thread](https://forums.swift.org/t/pitch-library-evolution-for-stable-abis/) * Review: ([review](https://forums.swift.org/t/se-0260-library-evolution-for-stable-abis/24260)) ([acceptance](https://forums.swift.org/t/accepted-se-0260-library-evolution-for-stable-abis/24845)) From f28a81b9c9da1d61752a0572a3c3151c8ed203fd Mon Sep 17 00:00:00 2001 From: Matthew Johnson Date: Wed, 3 Jul 2019 16:21:00 -0500 Subject: [PATCH 1229/4563] Add Identifiable proposal (#1057) * Add Identifiable proposal * Launch review * Make proposal UI-framework-agnostic * Tweak title --- proposals/0261-identifiable.md | 262 +++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 proposals/0261-identifiable.md diff --git a/proposals/0261-identifiable.md b/proposals/0261-identifiable.md new file mode 100644 index 0000000000..62f8afbf94 --- /dev/null +++ b/proposals/0261-identifiable.md @@ -0,0 +1,262 @@ +# Identifiable Protocol + +* Proposal: [SE-0261](0261-identifiable.md) +* Authors: [Matthew Johnson](https://github.com/anandabits), [Kyle Macomber](https://github.com/kylemacomber) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Active Review (July 3rd...July 12th)** + +## Introduction + +This proposal introduces an `Identifiable` protocol, a general concept that is broadly useful— +for diff algorithms, user interface libraries, and other generic code—to +correlate snapshots of the state of an entity in order to identify changes. It +is a fundamental notion that deserves representation in the standard library. + +Swift-evolution thread: [Move SwiftUI's `Identifiable` and related types into +the standard library](https://forums.swift.org/t/move-swiftuis-identifiable-protocol-and-related-types-into-the-standard-library/25713) + +## Motivation + +There are many use cases for identifying distinct values as belonging to a +single logical entity. Consider a `Contact` record: + +```swift +struct Contact { + var id: Int + var name: String +} + +let john = Contact(id: 1000, name: "John Appleseed") +var johnny = john +johnny.name = "Johnny Appleseed" +``` + +Snapshots of a `Contact`, like `john` and `johnny`, refer to the same logical +person, even though that person may change their name over time and at any +moment, may share any number of other details with distinct persons. Being able +to determine that two such snapshots belong to the same logical entity is a +broadly useful capability. + +Representing such identity as simply the `ObjectIdentifier` of a class instance +(or using `===` directly) sometimes works, but there are cases, such as when the +instances are persistent or distributed across processes, where it simply +doesn't, and even when it does work, allocating class instances to represent +identity of value types is needlessly costly. + +### Diffing + +User interfaces often involve collections of elements, each of which represents +an entity. Consider a list of favorite contacts: + +```swift +struct FavoriteContactList: View { + var favorites: [Contact] + + var body: some View { + List(favorites) { contact in + FavoriteCell(contact) + } + } +} +``` + +In order to provide a high quality user experience when updating such a user +interface with new content it is necessary to distinguish between the identity +of the represented entity and the representation of the state of the entity that +is presented to the user. Content in an interface representing an entity whose +state has changed but identity has not should be updated in place (rather than +resorting to removing the old content and inserting the new content). + +A user interface component is capable of making such a distinction if its +represented entities are `Identifiable`: + +```swift +struct List { + init( + _ data: Data, + @ViewBuilder rowContent: @escaping (Data.Element) -> RowContent + ) where Data.Element: Identifiable +} +``` + +`Identifiable` supports diff algorithms that are able to report entity insertions, +moves and removals. These algorithms are also able to detect _changes_ to the +state of an entity that is represented in both collections. This can include +changes to the state of an entity that _also_ moves in the collection. + +While diffs are often applied to the user interface layer of a program the diff +algorithm does not necessarily need to run in the user interface layer. It can +be desirable to compute a diff in the model layer. For example, the model layer +updates may be processed in the background and the diff can be computed before +moving back to the main thread to apply the changes to the UI. There may also +be more than one simultaneous presentation of the same data in the UI, in which +case computing the diff in the UI layer is redundant. + +Model layer code that performs these computations often has no dependencies +outside the standard library itself. It is unlikely to accept a dependency on +a UI framework that defines its own `Identifiable` protocol. If `Identifiable` +doesn't move to the standard library Swift programmers +will need to continue using their own variation of this protocol and will need +to ensure it is able co-exist with other similar definitions found in other frameworks higher +up the dependency stack. Unfortunately none these variations +are likey to be compatible with one another. + +## Proposed solution + +The proposed solution is to define a new `Identifiable` protocol: + +```swift +/// A class of types whose instances hold the value of an entity with stable identity. +protocol Identifiable { + + /// A type representing the stable identity of the entity associated with `self`. + associatedtype ID: Hashable + + /// The stable identity of the entity associated with `self`. + var id: ID { get } +} +``` + +This protocol will be used by diff algorithms, user interface libraries and other +generic code to correlate snapshots of the state of an entity in order to identify +changes to that state from one snapshot to another. + +An example conformance follows: + +```swift +struct Contact: Identifiable { + var id: Int + var name: String +} +``` + +There are a variety of considerations (value or reference semantics, persisted, +distributed, performance, convenience, etc.) to weigh when choosing the +appropriate representation of identity for an entity. `ID` is an associatedtype +because no single concrete type of identifier is appropriate in all cases. + +`id` was chosen as the name of the requirement over the unabbreviated form +because it is a [frequently used](https://www.swiftbysundell.com/posts/type-safe-identifiers-in-swift) +term of art that will allow easy conformance. + +## Detailed design + +### Object identifiability + +In order to make it as convenient as possible to conform to `Identifiable`, a +default `id` is provided for all class instances: + +```swift +extension Identifiable where Self: AnyObject { + var id: ObjectIdentifier { + return ObjectIdentifier(self) + } +} +``` + +Then, a class whose instances are identified by their object identities need not +explicitly provide an `id`: + +```swift +final class Contact: Identifiable { + var name: String + + init(name: String) { + self.name = name + } +} +``` + +Note, a class may provide a custom implementation of `id`: + +```swift +final class Contact: Identifiable { + let id: Int + let name: String + + init(id: Int, name: String) { + self.id = id + self.name = name + } +} +``` + +## Source compatibility + +This is a purely additive change. + +## Effect on ABI stability + +This is a purely additive change. + +## Effect on API resilience + +This has no impact on API resilience which is not already captured by other +language features. + +## Alternatives considered + +### Per-use identification + +Instead of constraining a collection's elements to an `Identifiable` protocol, +generic code could take an additional parameter that projects the identity of an +entity from its representation: + +```swift +struct FavoriteContactList: View { + var favorites: [Contact] + + var body: some View { + List(favorites, id: \.id) { contact in + FavoriteCell(contact) + } + } +} + +struct List { + public init( + _ data: Data, + id: KeyPath, + @ViewBuilder rowContent: @escaping (Data.Element) -> RowContent + ) +} +``` + +This is undesirable because a type generally has a single, canonical identity, +but this approach unnecessarily re-defines an entity's identity at every use +site, which is error-prone. + +Furthermore, this isn't a practical alternative because there is evidence that +if Swift doesn't define an `Identifiable` concept, libraries will opt to define +their own rather than take an identifier at the use-site. + +### Concrete conformances + +The purpose of `Identifiable` is to distinguish the identity of an entity from +the state of an entity. Concrete types like `UUID`, `Int`, and `String` are +commonly _used as identifiers_, however they do not _have an identifier_, so +they should not conform to `Identifiable`. + +## Future directions + +### Collection diffing + +Today there is a collection diffing convenience for `Equatable` elements: + +```swift +extension BidirectionalCollection where Element: Equatable { + func difference( + from other: C + ) -> CollectionDifference where C.Element == Self.Element +} +``` + +It may be desirable to add a similar convenience for `Identifiable` elements +(and prefer use of `Identifiable` to `Equatable` when a type conforms to both). +This is omitted from the immediate proposal in order to keep it focused. + +### Conditional conformances + +It may be desirable to provide the conditional conformance +`Optional: Identifiable where Wrapped: Identifiable`. This is omitted from the +immediate proposal in order to keep it focused. From e99ae69370f56ae84256b78902ab377cb8249cdd Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Tue, 9 Jul 2019 16:12:56 -0700 Subject: [PATCH 1230/4563] Fix indentation of multi-paragraph list items. (#1058) --- proposals/0258-property-wrappers.md | 77 +++++++++++++++-------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index dbc2395f90..2f80510916 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -856,47 +856,48 @@ type is the wrapper type. That stored property can be initialized in one of three ways: 1. Via a value of the original property's type (e.g., `Int` in `@Lazy var - foo: Int`, using the the property wrapper type's - `init(initialValue:)` initializer. That initializer must have a single - parameter of the same type as the `wrappedValue` property (or - be an `@autoclosure` thereof) and have the same access level as the - property wrapper type itself. When `init(initialValue:)` is present, - is is always used for the initial value provided on the property - declaration. For example: - - ```swift - @Lazy var foo = 17 - - // ... implemented as - private var _foo: Lazy = Lazy(initialValue: 17) - var foo: Int { /* access via _foo.wrappedValue as described above */ } - ``` - When there are multiple, composed property wrappers, all of them must provide an `init(initialValue:)`, and the resulting initialization will wrap each level of call: - - ```swift - @Lazy @Copying var path = UIBezierPath() - - // ... implemented as - private var _path: Lazy> = .init(initialValue: .init(initialValue: UIBezierPath())) - var path: UIBezierPath { /* access via _path.wrappedValue.wrappedValue as described above */ } - ``` + foo: Int`, using the the property wrapper type's + `init(initialValue:)` initializer. That initializer must have a single + parameter of the same type as the `wrappedValue` property (or + be an `@autoclosure` thereof) and have the same access level as the + property wrapper type itself. When `init(initialValue:)` is present, + is is always used for the initial value provided on the property + declaration. For example: + + ```swift + @Lazy var foo = 17 + + // ... implemented as + private var _foo: Lazy = Lazy(initialValue: 17) + var foo: Int { /* access via _foo.wrappedValue as described above */ } + ``` + + When there are multiple, composed property wrappers, all of them must provide an `init(initialValue:)`, and the resulting initialization will wrap each level of call: + + ```swift + @Lazy @Copying var path = UIBezierPath() + + // ... implemented as + private var _path: Lazy> = .init(initialValue: .init(initialValue: UIBezierPath())) + var path: UIBezierPath { /* access via _path.wrappedValue.wrappedValue as described above */ } + ``` 2. Via a value of the property wrapper type, by placing the initializer arguments after the property wrapper type: - - ```swift - var addressOfInt: UnsafePointer = ... - - @UnsafeMutablePointer(mutating: addressOfInt) - var someInt: Int - - // ... implemented as - private var _someInt: UnsafeMutablePointer = UnsafeMutablePointer(mutating: addressOfInt) - var someInt: Int { /* access via _someInt.wrappedValue */ } - ``` - When there are multiple, composed property wrappers, only the first (outermost) wrapper may have initializer arguments. - + ```swift + var addressOfInt: UnsafePointer = ... + + @UnsafeMutablePointer(mutating: addressOfInt) + var someInt: Int + + // ... implemented as + private var _someInt: UnsafeMutablePointer = UnsafeMutablePointer(mutating: addressOfInt) + var someInt: Int { /* access via _someInt.wrappedValue */ } + ``` + + When there are multiple, composed property wrappers, only the first (outermost) wrapper may have initializer arguments. + 3. Implicitly, when no initializer is provided and the property wrapper type provides a no-parameter initializer (`init()`). In such cases, the wrapper type's `init()` will be invoked to initialize the stored property. ```swift @@ -907,7 +908,7 @@ in one of three ways: var x: Int { /* access via _x.wrappedValue */ } ``` - When there are multiple, composed property wrappers, only the first (outermost) wrapper needs to have an `init()`. + When there are multiple, composed property wrappers, only the first (outermost) wrapper needs to have an `init()`. ### Type inference with property wrappers From 51122e96662acc22a0a0667eb26378a211f99f44 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 10 Jul 2019 11:39:22 -0700 Subject: [PATCH 1231/4563] [SE-0258] Rename init(initialValue:) to init(wrappedValue:). --- proposals/0258-property-wrappers.md | 100 +++++++++++++++------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 2f80510916..940d2974b7 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -6,7 +6,7 @@ * Status: **Active Review (June 27th...July 3rd, 2019)** * Implementation: [Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/251//artifact/branch-master/swift-PR-25781-251-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/327//artifact/branch-master/swift-PR-25781-327-osx.tar.gz) * Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) ([review #2](https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843)) ([review #3](https://forums.swift.org/t/se-0258-property-wrappers-third-review/26399)) -* Previous versions: [Revision #2](https://github.com/apple/swift-evolution/blob/bb8709c2ddca25c21a3c1e0298ce9457911dbfba/proposals/0258-property-wrappers.md), [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) +* Previous versions: [Revision #3](https://github.com/apple/swift-evolution/blob/e99ae69370f56ae84256b78902ab377cb8249cdd/proposals/0258-property-wrappers.md), [Revision #2](https://github.com/apple/swift-evolution/blob/bb8709c2ddca25c21a3c1e0298ce9457911dbfba/proposals/0258-property-wrappers.md), [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) ## Contents @@ -153,8 +153,8 @@ enum Lazy { case uninitialized(() -> Value) case initialized(Value) - init(initialValue: @autoclosure @escaping () -> Value) { - self = .uninitialized(initialValue) + init(wrappedValue: @autoclosure @escaping () -> Value) { + self = .uninitialized(wrappedValue) } var wrappedValue: Value { @@ -179,7 +179,7 @@ A property wrapper type provides the storage for a property that uses it as a wrapper. The `wrappedValue` property of the wrapper type provides the actual implementation of the wrapper, while the (optional) -`init(initialValue:)` enables initialization of the storage from a +`init(wrappedValue:)` enables initialization of the storage from a value of the property's type. The property declaration ```swift @@ -189,7 +189,7 @@ value of the property's type. The property declaration translates to: ```swift -private var _foo: Lazy = Lazy(initialValue: 1738) +private var _foo: Lazy = Lazy(wrappedValue: 1738) var foo: Int { get { return _foo.wrappedValue } set { _foo.wrappedValue = newValue } @@ -430,7 +430,7 @@ on new objects when the property is set. We can turn this into a wrapper: struct Copying { private var _value: Value - init(initialValue value: Value) { + init(wrappedValue value: Value) { // Copy the value on initialization. self._value = value.copy() as! Value } @@ -446,7 +446,7 @@ struct Copying { ``` This implementation would address the problem detailed in -[SE-0153](https://github.com/apple/swift-evolution/blob/master/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md). Leaving the `copy()` out of `init(initialValue:)` implements the pre-SE-0153 semantics. +[SE-0153](https://github.com/apple/swift-evolution/blob/master/proposals/0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md). Leaving the `copy()` out of `init(wrappedValue:)` implements the pre-SE-0153 semantics. ### `Atomic` @@ -458,8 +458,8 @@ Support for atomic operations (load, store, increment/decrement, compare-and-exc struct Atomic { private var _value: Value - init(initialValue: Value) { - self._value = initialValue + init(wrappedValue: Value) { + self._value = wrappedValue } var wrappedValue: Value { @@ -515,13 +515,13 @@ final class ThreadSpecific { private var key = pthread_key_t() private let initialValue: T - init(key: pthread_key_t, initialValue: T) { + init(key: pthread_key_t, wrappedValue: T) { self.key = key - self.initialValue = initialValue + self.initialValue = wrappedValue } - init(initialValue: T) { - self.initialValue = initialValue + init(wrappedValue: T) { + self.initialValue = wrappedValue pthread_key_create(&key) { // 'Any' erasure due to inability to capture 'self' or $0.assumingMemoryBound(to: Any.self).deinitialize(count: 1) @@ -597,8 +597,8 @@ protocol Copyable: AnyObject { @propertyWrapper struct CopyOnWrite { - init(initialValue: Value) { - wrappedValue = initialValue + init(wrappedValue: Value) { + self.wrappedValue = wrappedValue } private(set) var wrappedValue: Value @@ -675,8 +675,8 @@ write out the getters and setters, and it's fairly common to have a `Box` type t class Box { var wrappedValue: Value - init(initialValue: Value) { - self.wrappedValue = initialValue + init(wrappedValue: Value) { + self.wrappedValue = wrappedValue } var projectedValue: Ref { @@ -709,8 +709,8 @@ struct Clamping { let min: V let max: V - init(initialValue: V, min: V, max: V) { - value = initialValue + init(wrappedValue: V, min: V, max: V) { + value = wrappedValue self.min = min self.max = max assert(value >= min && value <= max) @@ -732,7 +732,7 @@ struct Clamping { ``` Most interesting in this example is how `@Clamping` properties can be -initialized given both an initial value and initializer arguments. In such cases, the `initialValue:` argument is placed first. For example, this means we can define a `Color` type that clamps all values in the range [0, 255]: +initialized given both an initial value and initializer arguments. In such cases, the `wrappedValue:` argument is placed first. For example, this means we can define a `Color` type that clamps all values in the range [0, 255]: ```swift struct Color { @@ -747,10 +747,10 @@ The synthesized memberwise initializer demonstrates how the initialization itsel ```swift init(red: Int = 127, green: Int = 127, blue: Int = 127, alpha: Int = 255) { - _red = Clamping(initialValue: red, min: 0, max: 255) - _green = Clamping(initialValue: green, min: 0, max: 255) - _blue = Clamping(initialValue: blue, min: 0, max: 255) - _alpha = Clamping(initialValue: alpha, min: 0, max: 255) + _red = Clamping(wrappedValue: red, min: 0, max: 255) + _green = Clamping(wrappedValue: green, min: 0, max: 255) + _blue = Clamping(wrappedValue: blue, min: 0, max: 255) + _alpha = Clamping(wrappedValue: alpha, min: 0, max: 255) } ``` @@ -857,10 +857,10 @@ in one of three ways: 1. Via a value of the original property's type (e.g., `Int` in `@Lazy var foo: Int`, using the the property wrapper type's - `init(initialValue:)` initializer. That initializer must have a single + `init(wrappedValue:)` initializer. That initializer must have a single parameter of the same type as the `wrappedValue` property (or be an `@autoclosure` thereof) and have the same access level as the - property wrapper type itself. When `init(initialValue:)` is present, + property wrapper type itself. When `init(wrappedValue:)` is present, is is always used for the initial value provided on the property declaration. For example: @@ -868,17 +868,17 @@ in one of three ways: @Lazy var foo = 17 // ... implemented as - private var _foo: Lazy = Lazy(initialValue: 17) + private var _foo: Lazy = Lazy(wrappedValue: 17) var foo: Int { /* access via _foo.wrappedValue as described above */ } ``` - When there are multiple, composed property wrappers, all of them must provide an `init(initialValue:)`, and the resulting initialization will wrap each level of call: + When there are multiple, composed property wrappers, all of them must provide an `init(wrappedValue:)`, and the resulting initialization will wrap each level of call: ```swift @Lazy @Copying var path = UIBezierPath() // ... implemented as - private var _path: Lazy> = .init(initialValue: .init(initialValue: UIBezierPath())) + private var _path: Lazy> = .init(wrappedValue: .init(wrappedValue: UIBezierPath())) var path: UIBezierPath { /* access via _path.wrappedValue.wrappedValue as described above */ } ``` @@ -914,21 +914,21 @@ in one of three ways: If the first property wrapper type is generic, its generic arguments must either be given explicitly in the attribute or Swift must be able to deduce them from the variable declaration. That deduction proceeds as follows: -* If the variable has an initial value expression `E`, then the first wrapper type is constrained to equal the type resulting from a call to `A(initialValue: E, argsA...)`, where `A` is the written type of the attribute and `argsA` are the arguments provided to that attribute. For example: +* If the variable has an initial value expression `E`, then the first wrapper type is constrained to equal the type resulting from a call to `A(wrappedValue: E, argsA...)`, where `A` is the written type of the attribute and `argsA` are the arguments provided to that attribute. For example: ```swift @Lazy var foo = 17 // type inference as in... - private var _foo: Lazy = Lazy(initialValue: 17) + private var _foo: Lazy = Lazy(wrappedValue: 17) // infers the type of '_foo' to be 'Lazy' ``` - If there are multiple wrapper attributes, the argument to this call will instead be a nested call to `B(initialValue: E, argsB...)` for the written type of the next attribute, and so on recursively. For example: + If there are multiple wrapper attributes, the argument to this call will instead be a nested call to `B(wrappedValue: E, argsB...)` for the written type of the next attribute, and so on recursively. For example: ```swift @A @B(name: "Hello") var bar = 42 // type inference as in ... - private var _bar = A(initialValue: B(initialValue: 42, name: "Hello")) + private var _bar = A(wrappedValue: B(wrappedValue: 42, name: "Hello")) // infers the type of '_bar' to be 'A' ``` @@ -1038,14 +1038,14 @@ struct Usewrappers { A property that has a wrapper can be initialized after it is defined, either via the property itself (if the wrapper type has an -`init(initialValue:)`) or via the synthesized storage property. For +`init(wrappedValue:)`) or via the synthesized storage property. For example: ```swift @Lazy var x: Int // ... -x = 17 // okay, treated as _x = .init(initialValue: 17) +x = 17 // okay, treated as _x = .init(wrappedValue: 17) ``` The synthesized storage property can also be initialized directly, @@ -1065,7 +1065,7 @@ apply to properties that have wrappers. Let's expand the example of ```swift @Lazy var x2: Int // ... -x2 = 17 // okay, treated as _x2 = .init(initialValue: 17) +x2 = 17 // okay, treated as _x2 = .init(wrappedValue: 17) // ... x2 = 42 // okay, treated as x2 = 42 (calls the Lazy.wrappedValue setter) ``` @@ -1086,7 +1086,7 @@ property type if either of the following is true: * The corresponding property has an initial value specified with the `=` syntax, e.g., `@Lazy var i = 17`, or - The corresponding property has no initial value, but the property -wrapper type has an `init(initialValue:)`. +wrapper type has an `init(wrappedValue:)`. Otherwise, the memberwise initializer parameter will have the same type as the wrapper. For example: @@ -1105,9 +1105,9 @@ struct Foo { z: Lazy = Lazy(closure: { getBool() }), w: Image) { self._x = x - self._y = Lazy(initialValue: y) + self._y = Lazy(wrappedValue: y) self._z = z - self._w = CopyOnWrite(initialValue: w) + self._w = CopyOnWrite(wrappedValue: w) } } ``` @@ -1132,7 +1132,7 @@ let $y = UIBezierPath() // error: cannot declare entity with $-prefixed name ' ### Projections A property wrapper type can choose to provide a projection property (e.g., `$foo`) to expose more API for each wrapped property by defining a `projectedValue` property. -As with the `wrappedValue` property and `init(initialValue:)`, the `projectedValue` property must have the +As with the `wrappedValue` property and `init(wrappedValue:)`, the `projectedValue` property must have the same access level as its property wrapper type. For example: ```swift @@ -1243,7 +1243,7 @@ There are a number of restrictions on the use of property wrappers when defining * A property with a wrapper cannot be `lazy`, `@NSCopying`, `@NSManaged`, `weak`, or `unowned`. * A property with a wrapper must be the only property declared within its enclosing declaration (e.g., `@Lazy var (x, y) = /* ... */` is ill-formed). * A property with a wrapper shall not define a getter or setter. -* The `wrappedValue` property and (if present) `init(initialValue:)` of a property wrapper type shall have the same access as the property wrapper type. +* The `wrappedValue` property and (if present) `init(wrappedValue:)` of a property wrapper type shall have the same access as the property wrapper type. * The `projectedValue` property, if present, shall have the same access as the property wrapper type. * The `init()` initializer, if present, shall have the same access as the property wrapper type. @@ -1331,7 +1331,7 @@ There are a few issues here. First, a single protocol are implied by the section on mutability of properties with wrappers, because we'd need to cope with `mutating get` as well as `set` and `nonmutating set`. Moreover, protocols don't support optional -requirements, like `init(initialValue:)` (which also has two +requirements, like `init(wrappedValue:)` (which also has two forms: one accepting a `Value` and one accepting an `@autoclosure () -> Value`) and `init()`. To cover all of these cases, we would need a several related-but-subtly-different protocols. @@ -1455,8 +1455,8 @@ public struct Observable { public var stored: Value var observed: Observed? - public init(initialValue: Value) { - self.stored = initialValue + public init(wrappedValue: Value) { + self.stored = wrappedValue } public func register(_ observed: Observable) { @@ -1482,7 +1482,7 @@ public class MyClass: Superclass { @Observable public var myVar: Int = 17 init() { - // self._myVar gets initialized with Observable(initialValue: 17) here + // self._myVar gets initialized with Observable(wrappedValue: 17) here super.init() self._myVar.register(self) // register as an Observable } @@ -1499,8 +1499,8 @@ To address these issues, we could extend the ad hoc protocol used to access the public struct Observable { private var stored: Value - public init(initialValue: Value) { - self.stored = initialValue + public init(wrappedValue: Value) { + self.stored = wrappedValue } public static subscript( @@ -1529,7 +1529,7 @@ public class MyClass: Superclass { @Observable public var myVar: Int = 17 // desugars to... - private var _myVar: Observable = Observable(initialValue: 17) + private var _myVar: Observable = Observable(wrappedValue: 17) public var myVar: Int { get { Observable[instanceSelf: self, wrapped: \MyClass.myVar, storage: \MyClass._myVar] } set { Observable[instanceSelf: self, wrapped: \MyClass.myVar, storage: \MyClass._myVar] = newValue } @@ -1573,6 +1573,10 @@ One could express this either by naming the property directly (as above) or, for ### Changes from the second reviewed version +* `init(initialValue:)` has been renamed to `init(wrappedValue:)` to match the name of the property. + +### Changes from the second reviewed version + * The synthesized storage property is always named with a leading `_` and is always `private`. * The `wrapperValue` property has been renamed to `projectedValue` to make it sufficiently different from `wrappedValue`. This also gives us the "projection" terminology to talk about the `$` property. * The projected property (e.g., `$foo`) always has the same access as the original wrapped property, rather than being artificially limited to `internal`. This reflects the idea that, for property wrapper types that have a projection, the projection is equal in importance to the wrapped value. From 6cfb1be5e2475892533193cc48d7df203c9a6460 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 10 Jul 2019 12:01:38 -0700 Subject: [PATCH 1232/4563] [SE-0258] Clean up "detailed" discussion of projectedValue. Remove "motivation"-ish discussion from here, fix example, and clarify what happens when multiple property wrappers are composed. --- proposals/0258-property-wrappers.md | 36 +++-------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 940d2974b7..6dadbd5b71 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -617,7 +617,7 @@ struct CopyOnWrite { } ``` -`projectedValue` provides delegation for the synthesized storage property, allowing the copy-on-write wrapper to be used directly: +`projectedValue` provides projection for the synthesized storage property, allowing the copy-on-write wrapper to be used directly: ```swift @CopyOnWrite var storage: MyStorageBuffer @@ -1190,7 +1190,6 @@ private var _someValue: LongTermStorage = LongTermStorage(manager: manag public var $someValue: UnsafeMutablePointer { get { return _someValue.projectedValue } - set { _someValue.projectedValue = newValue } } public var someValue: String { @@ -1199,38 +1198,9 @@ public var someValue: String { } ``` -There could then be other kinds of storage (e.g., some arena-based storage) described as property wrappers that *also* vend their wrapper types as `UnsafeMutablePointer`: +Note that, in this example, `$someValue` is not writable, because `projectedValue` is a get-only property. -```swift -@propertyWrapper -struct ArenaStorage { - let pointer: UnsafeMutablePointer - - init(arena: StorageArena, initialValue: Value) { - pointer = arena.allocate(Value.self) - pointer.initialize(to: initialValue) - } - - var wrappedValue: Value { - get { return pointer.pointee } - set { pointer.pointee = newValue } - } - - var projectedValue: UnsafeMutablePointer { - return pointer - } -} -``` - -The `someValue` variable from the previous example could be switched over to use arena-based storage without changing any of the clients of `someValue` or its projection property `$someValue`: - -```swift -@ArenaStorage(arena: currentConnectionArena, initialValue: "Hello") -var someValue: String -``` - -Each of the property wrapper types could have different implementations with -different data, but all of them present the same interface through `$someValue` and `someValue`. Note also that the `$someValue` is not writable, because `projectedValue` is a get-only property. +When multiple property wrappers are applied to a given property, only the outermost property wrapper's `projectedValue` will be considered. ### Restrictions on the use of property wrappers From 6bf657ebe913182e40af99d85b6bc2ec0c7ed8f9 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 10 Jul 2019 16:24:21 -0400 Subject: [PATCH 1233/4563] Accept SE-0258 --- proposals/0258-property-wrappers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 6dadbd5b71..90ae3a76b7 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -3,9 +3,9 @@ * Proposal: [SE-0258](0258-property-wrappers.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (June 27th...July 3rd, 2019)** +* Status: **Accepted** * Implementation: [Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/251//artifact/branch-master/swift-PR-25781-251-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/327//artifact/branch-master/swift-PR-25781-327-osx.tar.gz) -* Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) ([review #2](https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843)) ([review #3](https://forums.swift.org/t/se-0258-property-wrappers-third-review/26399)) +* Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) ([review #2](https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843)) ([review #3](https://forums.swift.org/t/se-0258-property-wrappers-third-review/26399)) ([acceptance](https://forums.swift.org/t/accepted-with-modification-se-0258-property-wrappers/26828)) * Previous versions: [Revision #3](https://github.com/apple/swift-evolution/blob/e99ae69370f56ae84256b78902ab377cb8249cdd/proposals/0258-property-wrappers.md), [Revision #2](https://github.com/apple/swift-evolution/blob/bb8709c2ddca25c21a3c1e0298ce9457911dbfba/proposals/0258-property-wrappers.md), [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) ## Contents From fe8f41794cb96bfd1942d7b7c1030851cc440ab5 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 11 Jul 2019 13:18:51 +0100 Subject: [PATCH 1234/4563] [SE-0258] Fix typo in sub-heading --- proposals/0258-property-wrappers.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 90ae3a76b7..b25c507cd7 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -51,6 +51,7 @@ - [Referencing the enclosing 'self' in a wrapper type](#referencing-the-enclosing-self-in-a-wrapper-type) - [Delegating to an existing property](#delegating-to-an-existing-property) + [Revisions](#revisions) + - [Changes from the third reviewed version](#changes-from-the-third-reviewed-version) - [Changes from the second reviewed version](#changes-from-the-second-reviewed-version) - [Changes from the first reviewed version](#changes-from-the-first-reviewed-version) + [Acknowledgments](#acknowledgments) @@ -1541,7 +1542,7 @@ One could express this either by naming the property directly (as above) or, for ## Revisions -### Changes from the second reviewed version +### Changes from the third reviewed version * `init(initialValue:)` has been renamed to `init(wrappedValue:)` to match the name of the property. From 710d54f4f88baa0babd31b46f19558fec5adc815 Mon Sep 17 00:00:00 2001 From: Azoy Date: Mon, 15 Jul 2019 13:23:57 -0400 Subject: [PATCH 1235/4563] Add demangle func to stdlib --- proposals/nnnn-demangle.md | 145 +++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 proposals/nnnn-demangle.md diff --git a/proposals/nnnn-demangle.md b/proposals/nnnn-demangle.md new file mode 100644 index 0000000000..26adcf8e5a --- /dev/null +++ b/proposals/nnnn-demangle.md @@ -0,0 +1,145 @@ +# Demangle Function + +* Proposal: [SE-NNNN](NNNN-demangle.md) +* Author: [Alejandro Alonso](https://github.com/Azoy) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#25314](https://github.com/apple/swift/pull/25314) + +## Introduction + +Introduce a new standard library function, `demangle`, that takes a mangled Swift symbol, like `$sSS7cStringSSSPys4Int8VG_tcfC`, and output the human readable Swift symbol, like `Swift.String.init(cString: Swift.UnsafePointer) -> Swift.String`. + +Swift-evolution thread: [Demangle Function](https://forums.swift.org/t/demangle-function/25416) + +## Motivation + +Currently in Swift, if a user is given an unreadable mangled symbol, they're most likely to use the `swift-demangle` tool to get the demangled version. However, this is a little awkward when you want to demangle a symbol in-process in Swift. One could create a new `Process` from Foundation and set it up to launch a new process within the process to use `swift-demangle`, but the standard library can do better and easier. + +## Proposed solution + +The standard library will add the following 3 new functions. + +```swift +// Given a mangled Swift symbol, return the demangled symbol. +public func demangle(_ input: String) -> String? + +// Given a mangled Swift symbol in a buffer and a preallocated buffer, +// write the demangled symbol into the buffer. +public func demangle( + _ mangledNameBuffer: UnsafeBufferPointer, + into buffer: UnsafeMutableBufferPointer +) -> DemangleResult + +// Given a mangled Swift symbol and a preallocated buffer, +// write the demangle symbol into the buffer. +public func demangle( + _ input: String, + into buffer: UnsafeMutableBufferPointer +) -> DemangleResult +``` + +as well as the following enum to indicate success or the different forms of failure: + +```swift +public enum DemangleResult: Equatable { + // The demangle was successful + case success + + // The result was truncated. Payload contains the number of bytes + // required for the complete demangle. + case truncated(Int) + + // The given Swift symbol was invalid. + case invalidSymbol +} +``` + +Examples: + +```swift +print(demangle("$s8Demangle3FooV")!) // Demangle.Foo + +// Demangle.Foo is 13 characters + 1 null terminator +let buffer = UnsafeMutableBufferPointer.allocate(capacity: 14) +defer { buffer.deallocate() } + +let result = demangle("$s8Demangle3BarV", into: buffer) + +guard result == .success else { + // Handle failure here + switch result { + case let .truncated(required): + print("We need \(required - buffer.count) more bytes!") + case .invalidSymbol: + print("I was given a faulty symbol?!") + default: + break + } + + return +} + +print(String(cString: buffer.baseAddress!)) // Demangle.Foo +``` + +## Detailed design + +If one were to pass a string that wasn't a valid Swift mangled symbol, like `abc123`, then the `(String) -> String?` would simply return nil to indicate failure. With the `(String, into: UnsafeMutableBufferPointer) -> DemangleResult` version and the buffer input version, we wouldn't write the passed string into the buffer if it were invalid. + +This proposal includes a trivial `(String) -> String?` version of the function, as well as a version that takes a buffer. In addition to the invalid input error case, the buffer variants can also fail due to truncation. This occurs when the output buffer doesn't have enough allocated space for the entire demangled result. In this case, we return `.truncated(Int)` where the payload is equal to the total number of bytes required for the entire demangled result. We're still able to demangle a truncated version of the symbol into the buffer, but not the whole symbol if the buffer is smaller than needed. E.g. + +```swift +// Swift.Int requires 10 bytes = 9 characters + 1 null terminator +// Give this 9 to excercise truncation +let buffer = UnsafeMutableBufferPointer.allocate(capacity: 9) +defer { buffer.deallocate() } + +if case let .truncated(required) = demangle("$sSi", into: buffer) { + print(required) // 10 (this is the amount needed for the full Swift.Int) + let difference = required - buffer.count + print(difference) // 1 (we only need 1 more byte in addition to the 9 we already allocated) +} + +print(String(cString: buffer.baseAddress!)) // Swift.In (notice the missing T) +``` + +This implementation relies on the Swift runtime function `swift_demangle` which accepts symbols that start with `_T`, `_T0`, `$S`, and `$s`. + +## Source compatibility + +These are completely new standard library functions, thus source compatibility is unaffected. + +## Effect on ABI stability + +These are completely new standard library functions, thus ABI compatibility is unaffected. + +## Effect on API resilience + +These are completely new standard library functions, thus API resilience is unaffected. + +## Alternatives considered + +We could choose to only provide one of the proposed functions, but each of these brings unique purposes. The trivial take a string and return a string version is a very simplistic version in cases where maybe you're not worried about allocating new memory, and the buffer variants where you don't want to alloc new memory and want to pass in some memory you've already allocated. + +## Future Directions + +The `swift_demangle` runtime function has an extra `flags` parameter, but currently it is not being used for anything. In the future if that function ever supports any flags, it would make sense to introduce new overloads or something similar to expose those flags to the standard library as well. E.g. + +```swift +public func demangle(_ input: String, flags: DemangleFlags) -> String? + +public func demangle( + _ mangledNameBuffer: UnsafeBufferPointer, + into buffer: UnsafeMutableBufferPointer, + flags: DemangleFlags +) -> DemangleResult + +public func demangle( + _ input: String, + into buffer: UnsafeMutableBufferPointer, + flags: DemangleFlags +) -> DemangleResult +``` + +where `DemangleFlags` could be an enum, `OptionSet`, `[DemangleFlag]`, etc. \ No newline at end of file From 89b6497a7ad9233961378f41621b0f905d0f2949 Mon Sep 17 00:00:00 2001 From: Gregory Ganley Date: Tue, 16 Jul 2019 13:18:02 -0400 Subject: [PATCH 1236/4563] Add syntax highlighting to Real stub (#1062) --- proposals/0246-mathable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0246-mathable.md b/proposals/0246-mathable.md index 69fc1d0eee..a8fe90b477 100644 --- a/proposals/0246-mathable.md +++ b/proposals/0246-mathable.md @@ -96,7 +96,7 @@ type would also conform. `SIMD` types do not conform themselves, but the operati are defined on them when their scalar type conforms to the protocol. The second piece of the proposal is the protocol `Real`: -``` +```swift public protocol Real: FloatingPoint, ElementaryFunctions { /// `atan(y/x)` with quadrant fixup. From 773b5993294833e315d6a1f0c8eab85258105e3f Mon Sep 17 00:00:00 2001 From: linqingmo Date: Thu, 18 Jul 2019 02:25:24 +0800 Subject: [PATCH 1237/4563] Change Lens value set to nonmutating. (#1048) * Change Lens value set to nonmutating. Change Lens value set to nonmutating. * Change Lens value set to nonmutating. Change Lens value set to nonmutating. --- proposals/0252-keypath-dynamic-member-lookup.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0252-keypath-dynamic-member-lookup.md b/proposals/0252-keypath-dynamic-member-lookup.md index 25a174e7c0..b0ef216db5 100644 --- a/proposals/0252-keypath-dynamic-member-lookup.md +++ b/proposals/0252-keypath-dynamic-member-lookup.md @@ -27,7 +27,7 @@ struct Lens { get { return getter() } - set { + nonmutating set { setter(newValue) } } @@ -80,7 +80,7 @@ struct Lens { get { return getter() } - set { + nonmutating set { setter(newValue) } } From 4eb4ba755a66cab84322116ae3f1dce8c4df7d86 Mon Sep 17 00:00:00 2001 From: PF93mc8y7erq92qTmTjJBysALa Date: Wed, 17 Jul 2019 20:29:27 +0200 Subject: [PATCH 1238/4563] Update 0212-compiler-version-directive.md (#1035) --- proposals/0212-compiler-version-directive.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0212-compiler-version-directive.md b/proposals/0212-compiler-version-directive.md index c5dd2776bd..a05a4e06a0 100644 --- a/proposals/0212-compiler-version-directive.md +++ b/proposals/0212-compiler-version-directive.md @@ -12,7 +12,7 @@ This proposal introduces a `compiler` directive that is syntactically equivalent ## Motivation -The `#if swift` check allows conditionally compiling code depending on the version of the language. Prior to Swift 4, the version of the compiler and the language were one and the same. But since Swift 4, the compiler can run in a compatibility mode for previous Swift versions, introducing an new version dimension. To support code across multiple compiler versions and compatibility modes, extra language versions are regularly introduced to represent old language versions running under compatibility mode. +The `#if swift` check allows conditionally compiling code depending on the version of the language. Prior to Swift 4, the version of the compiler and the language were one and the same. But since Swift 4, the compiler can run in a compatibility mode for previous Swift versions, introducing a new version dimension. To support code across multiple compiler versions and compatibility modes, extra language versions are regularly introduced to represent old language versions running under compatibility mode. For example, the release of Swift 4 introduced a Swift 3.2 language version representing the Swift 4 compiler in version 3 compatibility mode. Here is the current language matrix, as well as guesses as to what those versions will be for Swift 5.0 and 5.1. From 750598b888ca6a6096485a373a8460d7ba5b5ea3 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 17 Jul 2019 15:22:29 -0700 Subject: [PATCH 1239/4563] Demangler proposal is SE-0262 --- proposals/{nnnn-demangle.md => 0262-demangle.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{nnnn-demangle.md => 0262-demangle.md} (97%) diff --git a/proposals/nnnn-demangle.md b/proposals/0262-demangle.md similarity index 97% rename from proposals/nnnn-demangle.md rename to proposals/0262-demangle.md index 26adcf8e5a..67d36acc77 100644 --- a/proposals/nnnn-demangle.md +++ b/proposals/0262-demangle.md @@ -1,9 +1,9 @@ # Demangle Function -* Proposal: [SE-NNNN](NNNN-demangle.md) +* Proposal: [SE-0262](0262-demangle.md) * Author: [Alejandro Alonso](https://github.com/Azoy) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Joe Groff](https://github.com/jckarter) +* Status: **Scheduled for review (July 22...July 29, 2019)** * Implementation: [apple/swift#25314](https://github.com/apple/swift/pull/25314) ## Introduction @@ -142,4 +142,4 @@ public func demangle( ) -> DemangleResult ``` -where `DemangleFlags` could be an enum, `OptionSet`, `[DemangleFlag]`, etc. \ No newline at end of file +where `DemangleFlags` could be an enum, `OptionSet`, `[DemangleFlag]`, etc. From d613e118d7777120fad99eba2295149be6908c40 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 22 Jul 2019 09:19:04 -0700 Subject: [PATCH 1240/4563] start review for SE-0262 --- proposals/0262-demangle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0262-demangle.md b/proposals/0262-demangle.md index 67d36acc77..34e1a3c9ce 100644 --- a/proposals/0262-demangle.md +++ b/proposals/0262-demangle.md @@ -3,7 +3,7 @@ * Proposal: [SE-0262](0262-demangle.md) * Author: [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Scheduled for review (July 22...July 29, 2019)** +* Status: **Active Review (July 22...July 29, 2019)** * Implementation: [apple/swift#25314](https://github.com/apple/swift/pull/25314) ## Introduction From 410bc51fa008d0b74e63d0295a5170d2d91728e8 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 22 Jul 2019 10:04:10 -0700 Subject: [PATCH 1241/4563] First draft of preview package proposal --- proposals/NNNN-stdlib-preview-package.md | 172 +++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 proposals/NNNN-stdlib-preview-package.md diff --git a/proposals/NNNN-stdlib-preview-package.md b/proposals/NNNN-stdlib-preview-package.md new file mode 100644 index 0000000000..6feefd2ec0 --- /dev/null +++ b/proposals/NNNN-stdlib-preview-package.md @@ -0,0 +1,172 @@ + +# Standard Library Preview Package + +Proposal: SE-NNNN +Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev) +Review Manager: TBD +Status: Awaiting review + +## Introduction + +We propose the addition of a new package, `SwiftPreview`, which will form the initial landing spot for certain additions to the Swift Standard Library. + +Adding this package serves the goal of allowing for rapid adoption of new standard library features, enabling sooner real-world feedback, and allowing for an initial period of time where that feedback can lead to source- and ABI-breaking changes if needed. + +As a secondary benefit, it will reduce technical challenges for new community members implementing new features in the standard library. + +In the first iteration, this package will take the following: + +- free functions and methods, subscripts, and computed properties via extensions, that do not require access to the internal implementation of standard library types and that could reasonably be emitted into the client +- new types (for example, a sorted dictionary, or useful property wrapper implementations) +- new protocols, with conformance of existing library types to those protocols as appropriate + +The package will not include features that need to be matched with language changes, nor functions that cannot practically be implemented without access to existing type internals or other non-public features such as LLVM builtins. + +For the purposes of this document, we will refer to the proposed standard library preview package as "the package" and the standard library that ships with the toolchain as "the library". + +## Motivation + +### Facilitating Rapid Adoption and Feedback + +It is common for a feature proposed and accepted through Swift Evolution and added to the Standard Library to wait for months before it gets any real life usage. Take [SE-0199](https://github.com/apple/swift-evolution/blob/master/proposals/0199-bool-toggle.md) (the introduction of `toggle`) for example. It was [accepted](https://github.com/apple/swift-evolution/blob/master/proposals/0199-bool-toggle.md) almost 6 month before Swift 5 was released. + +Even though all additions go through a thorough review, there is no substitute for feedback from real-world usage in production. Sometimes we discover that the API is not quite as good as it might have been. + +While the toolchains downloadable from swift.org are useful for experimentation, they cannot be used to ship applications, so are rarely used for much more than "kicking the tires" on a feature. The beta period for Xcode provides slightly more real usage, but is still relatively short, with feedback often coming too late to be applied, needing, as it should, a further review on Swift evolution as well as time to integrate into the converging release. And the nature of standard library additions are such that you do not always have an immediate need early in the beta to try out a feature such as partial sort or a min heap. + +Once a feature ships as part of a Swift release, any future changes resulting from feedback from real usage must clear the very high bar of source and ABI stability. Even if the change is merited and a source break is justified, the absolute need for ABI stability can rule out certain changes entirely on technical grounds. Furthermore, for performance reasons, standard library types that rely on specialization need to expose some of their internal implementation as part of their ABI, closing off future optimizations or performance fixes that are only discovered through subsequent usage and feedback. + +### Technical Challenges for Contributors + +The requirements for contributing to the standard library can be prohibitive. Not everybody has the time and resources to build a whole stack including LLVM, Clang, and the Swift compiler itself just to change a part of the Standard Library. Additionally, Xcode and XCTest cannot easily be used to maintain and test standard library code. + +Integrating changes into the standard library requires knowledge of non-public features and idioms relating to ABI-stability. In particular, implementing a performant, specializable, ABI-stable collection type while preserving partial future flexibility is _significantly_ harder than writing one that is only source-stable. Harder still is knowing what internal implementation details can be changed after such a partially-transparent type has been published as ABI. + +It would be of great benefit to the community to allow proposals and contributions to the standard library without these requirements, leaving integration of the final ABI-stable version to a later date and possibly other contributors with more experience maintaining ABI-stable code. + +## Proposed solution + +We propose the introduction of a "Standard Library Preview" SwiftPM package. + +It will be a standalone project hosted on Github under the Apple organization, and will be part of the overall Swift project. + +Proposals for additions to the standard library will have the option of first landing in the package for a period before migrating to the library. A PR against the preview package will be sufficient to fulfill the implementation requirement for an evolution proposal. + +Additions to the package will **continue to use the Swift Evolution process.** All additions to the package should be made on the assumption that they will in time migrate to the ABI-stable standard library module that ships as part of the Swift toolchain. Proposals first landing in the package should be given the same level of scrutiny currently applied to standard library additions today. + +However, requirements around source stability of the package will not be the same as those of the Swift standard library. Follow-up proposals and proposal amendments will be able to make source-breaking changes. Unlike the very high bar for source-breaking changes, and the absolute rules around ABI stability, the bar for changes to API currently only in the package will be that of any other evolution proposal of a new API. + +Starting life in the package will not be mandatory. Proposals can opt instead to go straight into the standard library, especially if they do not meet the criteria for suitable package additions (see detailed design section). Functionality that are already available elsewhere, and have been proven in real-life use in that way, and is being "sunk" down into the standard library, would also be good candidates to skip the preview stage. + +Since the package will not be ABI-stable, it will not ship as a binary on any platform, or be a dependency of any ABI-stable package. This allows for changes to the internal implementation of any type, and the change/removal of any function, as part of implementation changes, follow-on proposal amendments, or subsequent proposals. + +## Detailed design + +The following additive evolution proposals could be made using the preview package: + +- New algorithms implemented as extensions on library protocols +- Utility types (e.g. wrapper types returned from functions like the `Lazy` types underlying `.lazy`) +- New protocols, including conformances by standard library types +- New collection types +- Property wrappers, such as a late-initialized wrapper + +The following are examples of changes that would _not_ go into the package: + +- Types introduced as part of and inseparable from language features (like `Optional` or `Error`) +- Implementations that rely on builtins for a reasonable implementation (like atomics or SIMD types) +- Implementations that require access to other types internals to be performant +- Changes that can't be done via a package, like adding customization points to protocols + +Some of these cases will require a judgement call. For example, making an extension method a customization point may bring a minor performance optimization — and so not prevent initial deployment in the package — or it may be a major part of the implementation, making the difference between an `O(1)` and `O(n)` implementation. Whether a proposal should go into the preview package should be part of the evolution pitch and review discussions. + +### Migration + +An important aspect of the design is the experience of users of the package when features migrate from the package to the library. This is handled differently for functions and types. + +#### Types + +The preview package will have a different module name (`SwiftPreview`) to that of the standard library (`Swift`). As such, every type it declares will be distinct from the type once it migrates into the standard library, and can co-exist with it. Because the preview package is "above" the `Swift` module in the stack, users of the package will get the package version of the type by default in source files that have imported the preview package. They will be able to specify the library version instead by prepending `Swift.` to the type name. This has the benefit that addition of the type into the library is source-compatible with code already using the package version. + +It does have a downside for code size. Package users do not benefit from the code-size wins of not including the new type in their app and instead using a version in the OS (though, given that most types in the standard library are generic and need to be specialized, this is not as big a problem as it might be). It would also mean package users miss out on optimizations that may be possible with the library implementation. Once inside the library, fast paths could benefit from internals of other types. For example, a ring buffer might be implemented in terms of the same storage as an `Array`, and conversion from one type to another could just be a question of copying a reference to the other's storage. + +In these cases where the standard library's version of the type would be better, the user can easily switch to it by adding a `typealias TheType = Swift.TheType` to their code, or by prefixing the module on individual declarations if needed. + +#### Functions + +Unlike types, methods cannot be disambiguated. That is, you cannot write something like `myCollection.SwiftPreview.partialSort`. Swift does not have this feature yet (and while desirable, it should not be a dependency of this proposal). So there is no way similar to the typealias approach above to prefer the standard library's implementation of a protocol extension over the packages. + +Since Swift 5.1, the standard library has had an internal feature that allows it to force-emit the body of a function into the client code, as a way of back-deploying critical bug fixes. This `@_alwaysEmitIntoClient` attribute can be used from within the standard library to deploy functions only to prior platforms, at the cost of binary size of the client (when they use them). Again, this cost is mitigated in the case of protocol extensions by the fact that the specialized implementation is already inlined. + +This allows use of `#if compiler` directives to simultaneously obsolete implementations in the package for the latest version of Swift when introducing new functions into the standard library. As long as the new library definitions are marked as `@_alwaysEmitIntoClient` the source compatibility of this should not be noticeable. + +#### Type/Function Combinations + +A common pattern used in the standard library is to return a type from a function, with the type driving much of the functionality initiated by the function call. For example, `myArray.lazy.map` returns a `LazySequence` which maps elements on the fly using the supplied closure. + +Since types cannot be emitted into client code, these combination function/type features will follow the pattern for types. This will mean that the package version will be used by default, and type context will be needed to force a call to the standard library if desired. + +#### Retirement from the Package + +In order to keep the package size and maintainability manageable, implementations will be removed from the package in a version update **1 year** after migration to the standard library. This is a necessary balance between maintainability and convenience. Since the package is open-source, it should be possible to copy source for a specific feature if a user badly needs to keep it while also wanting to upgrade to a newer major version _and_ not upgrade to the latest compiler. + +### Evolution + +Introduction of the package does not change much in the process of Swift Evolution. Changes to the Standard Library API surface should go through a well developed pitch - discussion - implementation - proposal - decision life-cycle. + +The main difference is that the final result will not land straight into the standard library. Instead, at a later point, a new kind of review will have to be run — one to promote parts of the package to the library. This should be done via an amendment to the proposal, and should be kept lightweight. A timeline for this secondary review should be set at proposal acceptance. The duration of time spent in the package should reflect the complexity of the proposal, and thus the amount of "bake time" it might need. + +No proposal should be accepted into the package on a provisional basis: it should be assumed that every proposal will be migrated as-is unless feedback while in the package stage reveals that it was a clear misadventure. The review manager of the migration amendment will be responsible for ensuring that the review discussion focuses on new feedback from real-world use, and not purely relitigation of previously settled decisions made during the original review. + +### Testing + +The standard library currently uses a combination of `StdlibUnittest` tests and raw `lit` tests. This works well, but is unfamiliar to most developers. + +The preferred style of test for Swift packages is `XCTest`, and the package should adopt this approach. This has the benefit of familiarity, and integrates well with Xcode. The work to convert tests from this format to lit can be done at the point of migration into the library. + +Certain features of `StdlibUnittest` – in particular crashLater and `StdlibCollectionUnittest`'s `checkCollection`, as well as the helper types like the minimal collections – should be ported over to `XCTest` if possible. This would make for an excellent starter bug, or could be done as part of introducing the first full-featured collection to the package that had a significant need for this kind of testing, but are not a requirement for accepting this proposal. + +### GYB + +There will be no use of [gyb](https://github.com/apple/swift/blob/master/utils/gyb.py) in the package. Gyb is a powerful and useful tool, but it can cause code that is difficult to read and write, and does not interact well with Xcode, so runs counter one of the goals of this package: to simplify contribution. + +The need for gyb has been gradually reducing over time as language features like conditional conformance and improved optimization eliminated the need for it. Generally speaking, most uses of gyb indicate missing language or library features, and so implementations of proposals should probably start by proposing those features instead. For example, it is unlikely we would accept further proposals to splat out multiple operators for different sizes of tuple like we do with `==`. Instead, protocol conformance for non-nominal types and variadic generics should solve this problem more generally. + +### Versioning + +The fundamental goals of the preview package are somewhat at odds with the usual goals of semantic versioning. Source-breaking changes, including retirements after migrating into the standard library, or source-breaking changes resulting from evolution proposals prior to migration, will be common. Unlike many packages, the preview package will not converge over time, reducing the frequency of its version bumps. + +As such, the preview package will remain at major version `0` **permanently**. Minor version bumps will be able to include source-breaking changes. Patch updates should not break source and will just be used for bug fixes. + +The use of a `0` major version at all times will act as a signifier that adopters need to be comfortable with the requirement to continuously update +to the latest version of the package. It should not be taken as an indication that the package shouldn't be used for real-world code: the code should be considered production-worthy. But it is a preview, and not source stable. Where practical, deprecated shims could be used to preserve source compatability between versions, but this may not always be practical. + +The decision when to tag minor versions, and potentially to coalesce multiple changes into a single version bump, will be made by the release manager for the standard library as chosen by the Swift project lead. + +### Source compatibility + +None on Swift itself. The intention of the package is to facilitate long-term source stability by detecting issues with APIs prior to their integration into the library. + +# Effect on ABI stability + +None. The package will not be declared ABI stable. ABI stability will only happen when proposals are migrated to the standard library. This means that the preview package may not be used within binary frameworks. + +Because we do not have cross-module optimization, package implementations should make use of `@inlinable` annotations. However, these annotations should be in the context of source stability only and should be fully reevaluated by the library integrator when stabilizing the ABI. + +## Alternatives considered + +### Single versus Multiple Packages + +An alternative to a single monolithic preview package would be multiple packages. For example, each accepted evolution proposal could have its own package. + +This would trade convenience for flexibility. In particular, multiple packages would avoid the source-breaking nature of proposal revisions (and the lesser problem of any additions being technically source breaking). But it would mean that adopters of the different previews would need to look up and add individual proposals rather then get them all in one go, and would rarely "discover" new features also in the package they're already importing. + +### Simultaneous Preview and Library Addition + +If the goal was to facilitate rapid release only, but not a longer feedback period, then the additional review step would not be necessary. Instead, new features would be integrated directly into the package and library simultaneously. + +However, experience has shown that proposals are not always perfect. Recently, significant if manageable issues were discovered with the API of both [SE-180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md) and [SE-202](https://github.com/apple/swift-evolution/blob/master/proposals/0202-random-unification.md), and in a near-miss [SE-220](https://github.com/apple/swift-evolution/blob/master/proposals/0220-count-where.md) had to be reverted late during 5.0 convergence due to typechecker performance impact. These issues would likely have been caught by a more prolonged exposure in a package. + +### Is a Second Review Necessary? + +Whether or not a proposal accepted and integrated into the package actually needs a second review is also worth considering. An alternative would be to set a timer, and then to automatically migrate the proposal into the library when the timer expires, without need for a second review. Changes could still be initiated to the preview package, but a new proposal would be required to interrupt the otherwise automatic migration timetable. + From 28186534224edd4522efaad7b0badc246075424b Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 27 Jul 2019 04:13:24 +0100 Subject: [PATCH 1242/4563] [SE-0261] Update status to implemented (Swift 5.1) --- proposals/0261-identifiable.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/0261-identifiable.md b/proposals/0261-identifiable.md index 62f8afbf94..48fcc60f47 100644 --- a/proposals/0261-identifiable.md +++ b/proposals/0261-identifiable.md @@ -3,7 +3,9 @@ * Proposal: [SE-0261](0261-identifiable.md) * Authors: [Matthew Johnson](https://github.com/anandabits), [Kyle Macomber](https://github.com/kylemacomber) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (July 3rd...July 12th)** +* Status: **Implemented (Swift 5.1)** +* Implementation: [apple/swift#26022](https://github.com/apple/swift/pull/26022) +* Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0261-identifiable-protocol/27358) ## Introduction From b1cade4092e7aeed6c3dd80e97ccd19e136bc123 Mon Sep 17 00:00:00 2001 From: David Smith Date: Mon, 29 Jul 2019 23:07:00 -0700 Subject: [PATCH 1243/4563] Add a String Initializer with Access to Uninitialized Storage (#1022) * Add a String Initializer with Access to Uninitialized Storage * Updates based on review comments and further thought --- .../0257-string-uninitialized-initializer.md | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 proposals/0257-string-uninitialized-initializer.md diff --git a/proposals/0257-string-uninitialized-initializer.md b/proposals/0257-string-uninitialized-initializer.md new file mode 100644 index 0000000000..b578895210 --- /dev/null +++ b/proposals/0257-string-uninitialized-initializer.md @@ -0,0 +1,139 @@ +# Add a String Initializer with Access to Uninitialized Storage + +* Proposal: [SE-0257](0257-string-uninitialized-initializer.md) +* Author: [David Smith](https://github.com/Catfish-Man) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#23409](https://github.com/apple/swift/pull/23409) +* Bug: [SR-10288](https://bugs.swift.org/browse/SR-10288) + +## Introduction + +This proposal suggests a new initializer for `String` that provides access to a String's uninitialized storage buffer. + +## Motivation + +`String` today is well-suited to interoperability with raw memory buffers when a contiguous buffer is already available, such as when dealing with `malloc`ed C strings. However, there are quite a few situations where no such buffer is available, requiring a temporary one to be allocated and copied into. One example is bridging `NSString` to `String`, which currently uses standard library internals to get good performance when using `CFStringGetBytes`. Another, also from the standard library, is `Int` and `Float`, which currently create temporary stack buffers and do extra copying. We expect libraries like SwiftNIO will also find this useful for dealing with streaming data. + +## Proposed solution + +Add a new `String` initializer that lets a program work with an uninitialized +buffer. + +The new initializer takes a closure that operates on an +`UnsafeMutableBufferPointer` and an `inout` count of initialized elements. This +closure has access to the uninitialized contents of the newly created String's +storage, and returns the number of initialized buffer elements, or 0. + +```swift +let myCocoaString = NSString("The quick brown fox jumps over the lazy dog") as CFString +var myString = String(uninitializedCapacity: CFStringGetMaximumSizeForEncoding(myCocoaString, …)) { buffer in + var initializedCount = 0 + CFStringGetBytes( + myCocoaString, + buffer, + …, + &initializedCount + ) + return initializedCount +} +// myString == "The quick brown fox jumps over the lazy dog" +``` + +Without this initializer we would have had to heap allocate an `UnsafeMutableBufferPointer`, copy the `NSString` contents into it, and then copy the buffer again as we initialized the `String`. + +## Detailed design + +```swift + /// Creates a new String with the specified capacity in UTF-8 code units then + /// calls the given closure with a buffer covering the String's uninitialized + /// memory. + /// + /// The closure should return the number of initialized code units, + /// or 0 if it couldn't initialize the buffer (for example if the + /// requested capacity was too small). + /// + /// This method replaces ill-formed UTF-8 sequences with the Unicode + /// replacement character (`"\u{FFFD}"`); This may require resizing + /// the buffer beyond its original capacity. + /// + /// The following examples use this initializer with the contents of two + /// different `UInt8` arrays---the first with well-formed UTF-8 code unit + /// sequences and the second with an ill-formed sequence at the end. + /// + /// let validUTF8: [UInt8] = [67, 97, 102, -61, -87, 0] + /// let s = String(uninitializedCapacity: validUTF8.count, + /// initializingUTF8With: { ptr in + /// ptr.initializeFrom(validUTF8) + /// return validUTF8.count + /// }) + /// // Prints "Café" + /// + /// let invalidUTF8: [UInt8] = [67, 97, 102, -61, 0] + /// let s = String(uninitializedCapacity: invalidUTF8.count, + /// initializingUTF8With: { ptr in + /// ptr.initializeFrom(invalidUTF8) + /// return invalidUTF8.count + /// }) + /// // Prints "Caf�" + /// + /// let s = String(uninitializedCapacity: invalidUTF8.count, + /// initializingUTF8With: { ptr in + /// ptr.initializeFrom(invalidUTF8) + /// return 0 + /// }) + /// // Prints "" + /// + /// - Parameters: + /// - capacity: The number of UTF-8 code units worth of memory to allocate + /// for the String. + /// - initializer: A closure that initializes elements and sets the count of + /// the new String + /// - Parameters: + /// - buffer: A buffer covering uninitialized memory with room for the + /// specified number of UTF-8 code units. + public init( + uninitializedCapacity capacity: Int, + initializingUTF8With initializer: ( + _ buffer: UnsafeMutableBufferPointer, + ) throws -> Int + ) rethrows +``` + +### Specifying a capacity + +The initializer takes the specific capacity that a user wants to work with as a +parameter. The buffer passed to the closure has a count that is at least the +same as the specified capacity, even if the ultimate size of the new `String` is larger. + +### Guarantees after throwing + +Because `UTF8.CodeUnit` is a trivial type, there are no special considerations about the state of the buffer when an error is thrown, unlike `Array`. + +## Source compatibility + +This is an additive change to the standard library, +so there is no effect on source compatibility. + +## Effect on ABI stability + +The new initializer will be part of the ABI, and will result in calls to a new @usableFromInline symbol being inlined into client code. Use of the new initializer is gated by @availability though, so there's no back-deployment concern. + +## Effect on API resilience + +The additional APIs will be a permanent part of the standard library, +and will need to remain public API. + +## Alternatives considered + +### Taking an inout count in the initializer rather than returning the new count + +Consistency with `Array` (which has to use the inout count for correctness) has some appeal here, but ultimately we decided that the value of consistency lies in allowing skill transfer and in repeated use, and these highly specialized initializers don't really allow for that. + +### Returning a `Bool` to indicate success from the closure + +Requiring people to either `throw` or check in the caller for an empty `String` return if the initializing closure fails is slightly awkward, but making the initializer failable is at least as awkward, and would be inconsistent with `Array`. + +### Validating UTF-8 instead of repairing invalid UTF-8 + +Matching the behavior of most other `String` initializers here also makes it more ergonomic to use, since it can be non-failable this way. From 93abb54833e2d9ee7ee842882f6104a867de3069 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Mon, 29 Jul 2019 23:11:01 -0700 Subject: [PATCH 1244/4563] Update and rename 0257-string-uninitialized-initializer.md to 0263-string-uninitialized-initializer.md Kick off review for SE-0263. --- ...tializer.md => 0263-string-uninitialized-initializer.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{0257-string-uninitialized-initializer.md => 0263-string-uninitialized-initializer.md} (97%) diff --git a/proposals/0257-string-uninitialized-initializer.md b/proposals/0263-string-uninitialized-initializer.md similarity index 97% rename from proposals/0257-string-uninitialized-initializer.md rename to proposals/0263-string-uninitialized-initializer.md index b578895210..c7c4e57cfc 100644 --- a/proposals/0257-string-uninitialized-initializer.md +++ b/proposals/0263-string-uninitialized-initializer.md @@ -1,9 +1,9 @@ # Add a String Initializer with Access to Uninitialized Storage -* Proposal: [SE-0257](0257-string-uninitialized-initializer.md) +* Proposal: [SE-0263](0263-string-uninitialized-initializer.md) * Author: [David Smith](https://github.com/Catfish-Man) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Status: **Active Review (July 29...August 9, 2019)** * Implementation: [apple/swift#23409](https://github.com/apple/swift/pull/23409) * Bug: [SR-10288](https://bugs.swift.org/browse/SR-10288) From 7220584b0faf8c2405b5f41d70b9687c763ce0dd Mon Sep 17 00:00:00 2001 From: Ryan Yoo Date: Mon, 5 Aug 2019 11:54:02 +0900 Subject: [PATCH 1245/4563] Fix typo in 0258-property-wrappers.md (#1066) --- proposals/0258-property-wrappers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index b25c507cd7..9020dc8f96 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -440,7 +440,7 @@ struct Copying { get { return _value } set { // Copy the value on reassignment. - _value = newValue().copy() as! Value + _value = newValue.copy() as! Value } } } From 0a1bf2b3cb93eb7c9620e3544614a5cbd40839a0 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 13 Aug 2019 10:19:59 -0700 Subject: [PATCH 1246/4563] Update 0240-ordered-collection-diffing.md --- proposals/0240-ordered-collection-diffing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0240-ordered-collection-diffing.md b/proposals/0240-ordered-collection-diffing.md index cce9fbb9f3..c897c4d8e2 100644 --- a/proposals/0240-ordered-collection-diffing.md +++ b/proposals/0240-ordered-collection-diffing.md @@ -4,7 +4,7 @@ * Authors: [Scott Perry](https://github.com/numist), [Kyle Macomber](https://github.com/kylemacomber) * Review Manager: [Doug Gregor](https://github.com/DougGregor), [Ben Cohen](https://github.com/AirspeedSwift) * Status: **Implemented (Swift 5.1)** -* Amendment status: **Active Review (June 19 - 25 2019)** +* Amendment status: **Accepted** * Implementation: [apple/swift#21845](https://github.com/apple/swift/pull/21845) * Decision notes: [Rationale](https://forums.swift.org/t/accepted-with-modifications-se-0240-ordered-collection-diffing/20008) From f90d777f3158c1d466e2e0d3a1c8e1034348387b Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 13 Aug 2019 10:20:12 -0700 Subject: [PATCH 1247/4563] Update 0240-ordered-collection-diffing.md --- proposals/0240-ordered-collection-diffing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0240-ordered-collection-diffing.md b/proposals/0240-ordered-collection-diffing.md index c897c4d8e2..9e64b5a25f 100644 --- a/proposals/0240-ordered-collection-diffing.md +++ b/proposals/0240-ordered-collection-diffing.md @@ -4,7 +4,7 @@ * Authors: [Scott Perry](https://github.com/numist), [Kyle Macomber](https://github.com/kylemacomber) * Review Manager: [Doug Gregor](https://github.com/DougGregor), [Ben Cohen](https://github.com/AirspeedSwift) * Status: **Implemented (Swift 5.1)** -* Amendment status: **Accepted** +* Amendment status: **Implemented (Swift 5.1)** * Implementation: [apple/swift#21845](https://github.com/apple/swift/pull/21845) * Decision notes: [Rationale](https://forums.swift.org/t/accepted-with-modifications-se-0240-ordered-collection-diffing/20008) From 612151a6147fcc65bc2bcaab59745c296791644a Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Fri, 23 Aug 2019 18:41:16 -0700 Subject: [PATCH 1248/4563] Return SE-262 for review --- proposals/0262-demangle.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0262-demangle.md b/proposals/0262-demangle.md index 34e1a3c9ce..7113692afe 100644 --- a/proposals/0262-demangle.md +++ b/proposals/0262-demangle.md @@ -3,8 +3,9 @@ * Proposal: [SE-0262](0262-demangle.md) * Author: [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Active Review (July 22...July 29, 2019)** +* Status: **Returned for revision** * Implementation: [apple/swift#25314](https://github.com/apple/swift/pull/25314) +* Decision Notes: [Returned for revision](https://forums.swift.org/t/returned-for-revision-se-0262-demangle-function/28186) ## Introduction From 87a6d06582b2779fc3dbac4707798788d742ea82 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Fri, 23 Aug 2019 18:44:35 -0700 Subject: [PATCH 1249/4563] SE-42 was never implemented --- proposals/0042-flatten-method-types.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0042-flatten-method-types.md b/proposals/0042-flatten-method-types.md index 3fb1350444..69caf43466 100644 --- a/proposals/0042-flatten-method-types.md +++ b/proposals/0042-flatten-method-types.md @@ -3,8 +3,9 @@ * Proposal: [SE-0042](0042-flatten-method-types.md) * Author: [Joe Groff](https://github.com/jckarter) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Accepted** -* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160321/013251.html) +* Status: **Rejected** +* Decision Notes: [Original acceptance](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160321/013251.html). The proposal was not implemented in time for Swift 3, and is now source-breaking. + * Bug: [SR-1051](https://bugs.swift.org/browse/SR-1051) ## Introduction From 93004e0ae06f0c75b5574ed2d13b5c77c19a642d Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sat, 24 Aug 2019 07:11:27 -0700 Subject: [PATCH 1250/4563] Stdlib preview package proposal is SE-0264 Pitch thread is here: https://forums.swift.org/t/pitch-standard-library-preview-package/27202 --- ...ib-preview-package.md => 0264-stdlib-preview-package.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-stdlib-preview-package.md => 0264-stdlib-preview-package.md} (99%) diff --git a/proposals/NNNN-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md similarity index 99% rename from proposals/NNNN-stdlib-preview-package.md rename to proposals/0264-stdlib-preview-package.md index 6feefd2ec0..31ab54f14b 100644 --- a/proposals/NNNN-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -1,10 +1,10 @@ # Standard Library Preview Package -Proposal: SE-NNNN +Proposal: [SE-0264](0264-stdlib-preview-package.md) Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev) -Review Manager: TBD -Status: Awaiting review +Review Manager: [Dave Abrahams](https://github.com/dabrahams) +Status: **Scheduled for review (Sept 2...Sept 9, 2019)** ## Introduction From 3f89efffabe68bb937486a887ef0ac7c8c5411cb Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Sat, 24 Aug 2019 07:20:23 -0700 Subject: [PATCH 1251/4563] Add missing bullets --- proposals/0264-stdlib-preview-package.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 31ab54f14b..6460220d97 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -1,10 +1,10 @@ # Standard Library Preview Package -Proposal: [SE-0264](0264-stdlib-preview-package.md) -Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev) -Review Manager: [Dave Abrahams](https://github.com/dabrahams) -Status: **Scheduled for review (Sept 2...Sept 9, 2019)** +* Proposal: [SE-0264](0264-stdlib-preview-package.md) +* Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev) +* Review Manager: [Dave Abrahams](https://github.com/dabrahams) +* Status: **Scheduled for review (Sept 2...Sept 9, 2019)** ## Introduction From 098152eafbfbd7faa74d81e1443231bd7caabc45 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 24 Aug 2019 18:12:32 +0100 Subject: [PATCH 1252/4563] [SE-0264] Fix "missing or invalid dates" warning --- proposals/0264-stdlib-preview-package.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 6460220d97..9234c5a61e 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -1,10 +1,9 @@ - # Standard Library Preview Package * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) -* Status: **Scheduled for review (Sept 2...Sept 9, 2019)** +* Status: **Scheduled for review (September 2...September 9, 2019)** ## Introduction From f1573a96e69c3729408d4eaed422e210c1f51494 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 28 Aug 2019 11:37:24 -0700 Subject: [PATCH 1253/4563] Add offset indexing and slicing proposal --- proposals/nnnn-offset-indexing-and-slicing.md | 637 ++++++++++++++++++ 1 file changed, 637 insertions(+) create mode 100644 proposals/nnnn-offset-indexing-and-slicing.md diff --git a/proposals/nnnn-offset-indexing-and-slicing.md b/proposals/nnnn-offset-indexing-and-slicing.md new file mode 100644 index 0000000000..f0c6e28590 --- /dev/null +++ b/proposals/nnnn-offset-indexing-and-slicing.md @@ -0,0 +1,637 @@ +# Offset Indexing and Slicing + +* Proposal: SE-NNNN +* Author: [Michael Ilseman](https://github.com/milseman) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#24296](https://github.com/apple/swift/pull/24296) + +## Introduction +This pitch introduces `OffsetBound`, which can represent a position in a collection specified as an offset from either the start or end of the collection (i.e. the collection’s “bounds”). This covers an expressivity gap in collection APIs by providing easy and safe means to fetch an index or element from such an offset as well as convenient slicing. + +If you would like to try it out, you can just copy-paste from [this gist](https://gist.github.com/milseman/1461e4f3e195974a5d1ad76cefdd6961), which includes the functionality as well as test cases and examples. This work is the culmination of prior discussion from an [earlier thread](https://forums.swift.org/t/pitch-offsetting-indices-and-relative-ranges/23837), the [thread before that](https://forums.swift.org/t/call-for-users-and-authors-offset-indexing-pitch/21444), and [@Letan](https://forums.swift.org/u/letan) ’s [original thread](https://forums.swift.org/t/shorthand-for-offsetting-startindex-and-endindex/9397). The latest pitch thread can be found [TBD](h). + +Swift-evolution thread: [TBD](TBD) + + +## Motivation + +### Easily and Safely Getting Indices, Elements, and Slices from Offsets + +`Collection`’s current index manipulation methods are meant to represent the lowest level programming interface, and as such they impose requirements on their use which are important for performance. Violations of these requirements are treated as irrecoverable logic errors, trapping whenever they lead to a potential memory safety issue. But, `Collection` lacks higher-level APIs that allow the programmer to treat such violations as recoverable domain errors. This proposal addresses the gap. + +Extracting elements and slices from offsets into a collection is an onerous task, requiring manually advancing indices. This proposal offers an ergonomic approach to offsetting from the start or end of collections, including their slices. + +This commonly comes up with casual `String` usage, and aligns with the [String Essentials](https://forums.swift.org/t/string-essentials/21909) effort. + +For a simple example taken from [Advent of Code 2018 Day 7](https://adventofcode.com/2018/day/7) , we want a function taking a string where each line is of the form `Step C must be finished before step A can begin.` , and returns an array representing the requirement `(finish: "C", before: "A")`. + +```swift +func parseRequirements(_ s: String) -> [(finish: Character, before: Character)] { + s.split(separator: "\n").map { line in + let finishIdx = line.index(line.startIndex, offsetBy: 5) // 5 after first + let beforeIdx = line.index(line.endIndex, offsetBy: -12) // 11 before last + return (line[finishIdx], line[beforeIdx]) + } +} +``` + +By taking a detour through forming SubSequences, we could alternatively express this as: + +```swift +func parseRequirements(_ s: String) -> [(finish: Character, before: Character)] { + s.split(separator: "\n").map { line in + (line.dropFirst(5).first!, line.dropLast(11).last!) + } +} +``` + +Instead, this proposal provides a way to directly extract elements from known offsets. + + +### Common Bugs and Assumptions in Int-Indexed Collections + +When a collection’s index type happens to be `Int`, it’s a common mistake to assume that such indices start from zero. For an example from [this forum post](https://forums.swift.org/t/subscripting-a-range-seems-unintuitive/23278), an `Int` range’s indices are the very elements themselves; they don’t start at zero. + +```swift +print((3..<10)[5...]) // 5..<10 +``` + +Slices share the same indices with the base collection. Assuming indices start from zero can be especially pernicious in generic code and when working with self-sliced types such as `Data`. + +```swift +func fifth(_ c: C) -> C.Element? where C.Index == Int { + return c.count >= 5 ?? c[4] : nil +} + +let array = [1,2,3,4,5,6,7,8,9] +print(fifth(array)!) // 5 +print(fifth(array[2...])!) // still `5`, but `7` would be the real fifth item + +func fifth(_ data: Data) -> UInt8? { + return data.count >= 5 ?? data[4] : nil +} + +var data = Data([1, 2, 3, 4, 5, 6, 7, 8, 9]) +print(fifth(data)!) // 5 + +data = data.dropFirst(2) +print(fifth(data)!) // still `5`, but `7` is the real fifth item +``` + +Common advice when working with `Data` is to index by adding to the start index, as in `data[data.startIndex + 4]`. However, even this approach is not valid in a generic context (even for random access collections). Fetching an index and then performing integer arithmetic is different than advancing a position: + +```swift +struct EveryOther: RandomAccessCollection { + internal var storage: C + + var startIndex: C.Index { storage.startIndex } + var endIndex: C.Index { + if storage.count % 2 == 0 { return storage.endIndex } + return storage.index(before: storage.endIndex) + } + + subscript(position: C.Index) -> C.Element { storage[position] } + + func index(before i: C.Index) -> C.Index { storage.index(i, offsetBy: -2) } + func index(after i: C.Index) -> C.Index { storage.index(i, offsetBy: 2) } + // ... and override `distance`, `index(_:offsetBy:)` for performance ... +} + +let everyOther = EveryOther(storage: [1,2,3,4,5,6,7,8]) +print(everyOther.contains(2)) // false +print(everyOther.contains(3)) // true + +let startIdx = everyOther.startIndex +print(everyOther[startIdx + 1]) // 2, but everyOther doesn't even contain 2! +print(everyOther[everyOther.index(after: startIdx)]) // 3 +``` + +This proposal provides a way to perform offset-based indexing for such collections, with similar expressivity but with explicit start/end bounds for clarity. + + +## Proposed solution + +We propose convenient subscripts for slicing, single-element retrieval, and fetching an index from an offset: + +```swift +let str = "abcdefghijklmnopqrstuvwxyz" +print(str[.start + 3 ..< .start + 6]) // "def" +print(str[.start + 3 ..< .end - 3]) // "defghijklmnopqrstuvw" +print(str[.start + 3 ..< .end - 23]) // "", +print(str[.last]) // Optional("z") +print(str[.last - 1]) // Optional("y") +print(str[.start + 26]) // nil +print(str[(.last - 3)...]) // "wxyz" + +print(str.index(at: .last - 1)) // Optional(... index of "y") +print(str.index(at: .end - 26)) // Optional(... index of "a") +print(str.index(at: .end - 27)) // nil +``` + +The `parseRequirements` example from above can be written as: + +```swift +func parseRequirements(_ s: String) -> [(finish: Character, before: Character)] { + s.split(separator: "\n").map { line in + (line[.first + 5]!, line[.last - 11]!) + } +} +``` + +This is available on all Collections, allowing a more general solution. The Advent of Code exercise only requires the extraction (and later comparison) of the elements at the corresponding positions: + +```swift +func parseRequirements( + _ c: C, lineSeparator: C.Element +) -> [(finish: C.Element, before: C.Element)] { + c.split(separator: lineSeparator).map { line in + (line[.first + 5]!, line[.last - 11]!) + } +} +``` + +Here, the `line[.last - 11]` will run in constant-time if the passed collection conforms to `BidirectionalCollection`, or in linear-time if it does not (since we have to count from the front). This algorithmic guarantee is part of this proposal, and without this generalization cannot be done. + +These also address the expressivity issues and assumptions with Int-indexed Collections above: + +```swift +print((3..<10)[(.start + 5)...]) // 8..<10 + +func fifth(_ c: C) -> C.Element? { return c[.first + 4] } + +let array = [1,2,3,4,5,6,7,8,9] +print(fifth(array)!) // 5 +print(fifth(array[2...])!) // 7 + +var data = Data([1, 2, 3, 4, 5, 6, 7, 8, 9]) +print(fifth(data)!) // 5 +data = data.dropFirst(2) +print(fifth(data)!) // 7 + +let everyOther = EveryOther(storage: [1,2,3,4,5,6,7,8]) +print(everyOther[.start + 1]!) // 3 +``` + + +## Detailed design + +This proposal adds an `OffsetBound` struct, representing a position at an offset from the start or end of a collection. + +```swift +/// A position in a collection specified as an offset from either the start +/// or the end of the collection (i.e. the collection's bounds). +/// +/// You can use an `OffsetBound` to access an index or element of a collection +/// as well as extract a slice. For example: +/// +/// let str = "abcdefghijklmnopqrstuvwxyz" +/// print(str[.last]) // Optional("z") +/// print(str[.last - 2]) // Optional("x") +/// print(str[.start + 26]) // nil +/// print(str[.start + 3 ..< .start + 6]) // "def" +/// print(str[.start + 3 ..< .end - 3]) // "defghijklmnopqrstuvw" +/// +/// `OffsetBound`s also provide a convenient way of working with slice types +/// over collections whose index type is `Int`. Slice types share indices with +/// their base collection, so `0` doesn't always mean the first element. For +/// example: +/// +/// let array = [1,2,3,4,5,6] +/// print(array[2...][3) // 4 +/// print(array[2...][.start + 3]!) // 6 +/// +public struct OffsetBound { + /* internally stores an enum, not ABI/API */ + + /// The position corresponding to `startIndex` and the first element of a + /// nonempty collection. + public static var start: OffsetBound + + /// The position corresponding to `endIndex`, which is "past the end"---that + /// is, the position one greater than the last valid subscript argument. + public static var end: OffsetBound + + /// The position corresponding to `startIndex` and the first element of a + /// nonempty collection. This is equivalent to `start`. + public static var first: OffsetBound + + /// The position corresponding to the last element of an nonempty + /// collection. This is equivalent to `.end - 1`. + public static var last: OffsetBound + + /// Returns a bound that offsets the given bound by the specified distance. + /// + /// For example: + /// + /// .first + 2 // The position of the 3rd element + /// .last + 1 // One past the last element, equivalent to `.end` + /// + public static func +(_ lhs: OffsetBound, _ rhs: Int) -> OffsetBound + + /// Returns a bound that offsets the given bound by the specified distance + /// backwards. + /// + /// For example: + /// + /// .last - 2 // Two positions before the last element's position + /// .end - 1 // The position before the (open) upper-bound, i.e. `.last` + /// + public static func -(_ lhs: OffsetBound, _ rhs: Int) -> OffsetBound +} +``` + +`OffsetBound` is `Comparable`, and as such can be used as a bound type for `RangeExpression`s. + +```swift +extension OffsetBound: Comparable { + /// Compare the positions represented by two `OffsetBound`s. + /// + /// Offsets relative to `.start` are always less than those relative to + /// `.end`, as there are arbitrarily many offsets between the two extremities. + /// Offsets from the same bound are ordered by their corresponding positions. + /// For example: + /// + /// .start + n < .end - m // true for all values of n and m + /// .start + n < .start + m // equivalent to n < m + /// .end - n < .end - m // equivalent to n > m + /// + public static func < (_ lhs: OffsetBound, _ rhs: OffsetBound) -> Bool + + /// Compare two `OffsetBound`s to see if they represent equivalent positions. + /// + /// This is only true if both offset the same bound by the same amount. For + /// example: + /// + /// .last == .end - 1 // true + /// .start + n == .end - m // false for all values of n and m + /// .start + n == .start + m // equivalent to n == m + /// + public static func == (_ lhs: OffsetBound, _ rhs: OffsetBound) -> Bool +} +``` + +`Collection` gets an API to retrieve an index from an `OffsetBound` if it exists, a subscript to retrieve an element from an `OffsetBound` if it exists, and a slicing subscript to extract a range. + +```swift +extension Collection { + /// Returns the corresponding index for the provided offset, if it exists, + /// else returns nil. + /// + /// - Complexity: + /// - O(1) if the collection conforms to `RandomAccessCollection`. + /// - O(*k*) where *k* is equal to the offset if the collection conforms to + /// `BidirectionalCollection`. + /// - O(*k*) if the offset is positive or zero. + /// - Otherwise, O(*n*) where *n* is the length of the collection. + /// Note that `.last` represents `.end - 1`, therefore it has a negative + /// offset + public func index(at bound: OffsetBound) -> Index? + + /// Returns the corresponding element for the provided offset, if it exists, + /// else returns nil. + /// + /// Example: + /// + /// let str = "abcdefghijklmnopqrstuvwxyz" + /// print(str[.last]) // Optional("z") + /// print(str[.last - 2]) // Optional("x") + /// print(str[.start + 26]) // nil + /// + /// - Complexity: + /// - O(1) if the collection conforms to `RandomAccessCollection`. + /// - O(*k*) where *k* is equal to the offset if the collection conforms to + /// `BidirectionalCollection`. + /// - O(*k*) if the offset is positive or zero. + /// - Otherwise, O(*n*) where *n* is the length of the collection. + /// Note that `.last` represents `.end - 1`, therefore it has a negative + /// offset + public subscript(bound: OffsetBound) -> Element? + + /// Returns the contiguous subrange of elements corresponding to the provided + /// offsets. + /// + /// Example: + /// + /// let str = "abcdefghijklmnopqrstuvwxyz" + /// print(str[.start + 3 ..< .start + 6]) // "def" + /// print(str[.start + 3 ..< .end - 3]) // "defghijklmnopqrstuvw" + /// + /// - Complexity: + /// - O(1) if the collection conforms to `RandomAccessCollection`. + /// - O(*k*) where *k* is equal to the larger offset if the collection + /// conforms to `BidirectionalCollection`. + /// - O(*k*) if both offsets are positive or zero. + /// - Otherwise, O(*n*) where *n* is the length of the collection. + /// Note that `.last` represents `.end - 1`, therefore it has a negative + /// offset + public subscript( + range: ORE + ) -> SubSequence where ORE.Bound == OffsetBound +} +``` + +This proposal adds a new “internal” (i.e. underscored) customization hook to `Collection` to apply a reverse offset to a given index. Unlike `index(_:offsetBy:limitedBy:)` which will trap if the collection is not bidirectional, this will instead advance `startIndex`. + +```swift +public protocol Collection: Sequence { + /// Returns an index `distance` positions prior to `i` if it exists. + /// + /// Other methods such as `index(_:offetBy:)` must not be passed a negative + /// offset if the collection is bidirectional. This method will perform a + /// negative offset even if the collection is not bidirectional, by using a + /// less efficient means. `BidirectionalCollection` customizes this with a + /// more efficient implementation. + /// + /// - Parameters + /// - i: a valid index of the collection. + /// - distance: The distance to offset `i` backwards. `distance` must be + /// positive or zero. + /// - Returns: The index `distance` positions prior to `i` if in bounds, else + /// `nil`. + /// + /// - Complexity: + /// - O(1) if the collection conforms to `RandomAccessCollection`. + /// - O(*k*), where *k* is equal to `distance` if the collection conforms + /// to `BidirectionalCollection`. + /// - Otherwise, O(*n*), where *n* is the length of the collection. + func _reverseOffsetIndex(_ i: Index, by distance: Int) -> Index? +} + +extension Collection { + // Scans from the start + public func _reverseOffsetIndex(_ i: Index, by distance: Int) -> Index? +} +extension BidirectionalCollection { + // Reverse offsets + public func _reverseOffsetIndex(_ i: Index, by distance: Int) -> Index? +} +``` + +Change `Collection.suffix()` and `Collection.dropLast()` to use this hook, which will improve algorithmic complexity in generic code when the collection happens to be bidirectional. + +```diff +extension Collection { + /// Returns a subsequence containing all but the specified number of final + /// elements. + /// + /// If the number of elements to drop exceeds the number of elements in the + /// collection, the result is an empty subsequence. + /// + /// let numbers = [1, 2, 3, 4, 5] + /// print(numbers.dropLast(2)) + /// // Prints "[1, 2, 3]" + /// print(numbers.dropLast(10)) + /// // Prints "[]" + /// + /// - Parameter k: The number of elements to drop off the end of the + /// collection. `k` must be greater than or equal to zero. + /// - Returns: A subsequence that leaves off the specified number of elements + /// at the end. + /// +- /// - Complexity: O(1) if the collection conforms to +- /// `RandomAccessCollection`; otherwise, O(*n*), where *n* is the length of +- /// the collection. ++ /// - Complexity: ++ /// - O(1) if the collection conforms to `RandomAccessCollection`. ++ /// - O(*k*), where *k* is equal to `distance` if the collection conforms ++ /// to `BidirectionalCollection`. ++ /// - Otherwise, O(*n*), where *n* is the length of the collection. + @inlinable + public __consuming func dropLast(_ k: Int = 1) -> SubSequence + + /// Returns a subsequence, up to the given maximum length, containing the + /// final elements of the collection. + /// + /// If the maximum length exceeds the number of elements in the collection, + /// the result contains all the elements in the collection. + /// + /// let numbers = [1, 2, 3, 4, 5] + /// print(numbers.suffix(2)) + /// // Prints "[4, 5]" + /// print(numbers.suffix(10)) + /// // Prints "[1, 2, 3, 4, 5]" + /// + /// - Parameter maxLength: The maximum number of elements to return. The + /// value of `maxLength` must be greater than or equal to zero. + /// - Returns: A subsequence terminating at the end of the collection with at + /// most `maxLength` elements. + /// +- /// - Complexity: O(1) if the collection conforms to +- /// `RandomAccessCollection`; otherwise, O(*n*), where *n* is the length of +- /// the collection. ++ /// - Complexity: ++ /// - O(1) if the collection conforms to `RandomAccessCollection`. ++ /// - O(*k*), where *k* is equal to `maxLength` if the collection conforms ++ /// to `BidirectionalCollection`. ++ /// - Otherwise, O(*n*), where *n* is the length of the collection. + @inlinable + public __consuming func suffix(_ maxLength: Int) -> SubSequence +} +``` + +Finally, `BidirectionalCollection`’s overloads are obsoleted in new versions as they are fully redundant with `Collection`’s. + + +## Source compatibility + +This change preserves source compatibility. + +## Effect on ABI stability + +This does not change any existing ABI. + +## Effect on API resilience + +All additions are versioned. The `_reverseOffsetIndex` customization hook is `@inlinable`, while all other added ABI are fully resilient. + +## Alternatives considered + + +### Drop `.last` and `.first` + +This proposal adds the static members `.start`, `.end`, `.first`, and `.last` to `OffsetBound`. The last two can be derived from the first two, but they can lend clarity to code. For example, sometimes “one before last” is easier to comprehend than “two from the end”, i.e. `.last - 1` and `.end - 2` respectively. `OffsetBound.first` is included for symmetry with `OffsetBound.last`, as well as symmetry with `Collection.first` and `Collection.last`, for which single-element subscript is an equivalent expression. + +### Don’t add the customization hook + +An alternative to the customization hook is adding overloads to both `Collection` and `BidirectionalCollection` for every API. This would result in slower code in a generic context over `Collection` even if that collection happened to also conform to `BidirectionalCollection`, as this is statically dispatched. Since this is a frequent enough access pattern in the standard library, we feel a customization hook to share and accelerate the functionality is warranted. + +This proposal adapts Collection’s `suffix` and `dropLast` to use the new hook, improving their performance as well. + + +### Offset Arbitrary Indices + +More general than offsetting from the start or end of a Collection is offsetting from a given index. Relative indices, that is indices with offsets applied to them, could be expressed in a `RelativeBound` struct, where `Bound` is the index type of the collection it will be applied to (phantom-typed for pure-offset forms). The prior pitch proposed this feature alongside the `++`/`--` operators, but doing this presents problems in `RangeExpression` conformance as well as type-checker issues with generic operator overloading. + +These issues can be worked around (as shown below), but each workaround comes with its own drawbacks. All in all, offsetting from an arbitrary index isn’t worth the tradeoffs and can be mimicked with slicing (albeit with more code). + +#### The Trouble with RangeExpression + +`RangeExpression` requires the ability to answer whether a range contains a bound, where `Bound` is constrained to be `Comparable`. Pure-offset ranges can answer this similarly to other partial ranges by putting the partial space between `.start` and `.end`. + +Unlike pure-offset ranges, containment cannot be consistently answered for a range of relative indices without access to the original collection. Similarly, `RelativeBound` cannot be comparable. + +A workaround could be to introduce a new protocol `IndexRangeExpression` without these requirements and add new overloads for it. Some day in the future when the compiler supports it, `RangeExpression` can conform to it and the existing `RangeExpression` overloads would be deprecated. + +This would also require a new `RelativeRange` type and new generic overloads for range operators `..<` and `...` producing it. This is a hefty amount of additional machinery and would complicate type checking of all ranges, as explained next. + +#### The Trouble with Generic Operator Overloading + +Overloading an operator for a type with a generic parameter complicates type checking for that operator. The type checker has to open a type variable and associated constraints for that parameter, preventing the constraint system from being split. This increases the complexity of type checking *all* expressions involving that operator, not just those using the overload. + +This increased complexity may be tolerable for a brand new operator, such as the previously pitched `++` and `--`, but it is a downside of overloading an existing-but-narrowly-extended operator such as `..<` and `...`. It is a total non-starter for operators such as `+` and `-`, which already have complex resolution spaces. + + +### Other Syntax + +#### `idx++2` or `idx-->2` + +The original pitch introduced `++` and `--` whose left-hand side could be omitted. Alternatives included symmetric `-->` and `<--`. This syntax was used for alternatives that offset existing indices, omitting a side for an implied start or end, and thus did not introduce a generic overload of `+`. However, in addition to the issues mentioned above in “Offset Arbitrary Indices”, these operators were met with considerable resistance: `++/--` carries C-baggage for many developers, and `++/--` and `-->/<--` are both new glyphs that don’t carry their weight when limited to the start or end of a collection. + +#### Use an `offset:` label and literal convention + +An alternative syntax (prototyped in [this gist](https://gist.github.com/milseman/7f7cf3b764618ead6011700fdce2ad83)) is to have `OffsetBound` be `ExpressibleByIntegerLiteral` with the convention that a negative literal produces an offset from the end. E.g.: + +```swift +// Proposed +"abc"[.start + 1 ..< .end - 1] // "b" + +// Offset label +"abc"[offset: 1..<(-1)] // "b" +``` + +Negative values meaning from-the-end is only a convention on top of literals, i.e. this would *not* provide wrap-around semantics for a value that happens to be negative. + +In the end, we believe that the proposed syntax is more readable. There is less cognitive load in reading `.end - 1` than mentally mapping the literal convention of `-1`, and that literal convention would be inconsistent with run-time values. + + +#### No Syntax + +Alternative approaches include avoiding any syntax such as `+` or the use of the range operators `..<` and `...` by providing labeled subscript overloads for all combinations. + +```swift +// Proposed +collection[.start + 5 ..< .end - 2] + +// No ranges +collection[fromStart: 5, upToEndOffsetBy: -2] +``` + +We feel range-less variants are not in the direction and spirit of Swift. Swift uses range syntax for subscripts rather than multiple parameter labels: + +```swift +// Existing Swift style +collection[lowerIdx ..< upperIdx] // Up-to +collection[lowerIdx ... upperIdx] // Up-through + +// Not Swift style +collection[from: lowerIdx, upTo: upperIdx] +collection[from: lowerIdx, upThrough: upperIdx] +``` + + + +### Don’t Make it Easy + +One objection to this approach is that it makes it less obnoxious to write poorly performing formulations of simple iteration patterns. Advancing the start index in every iteration of a loop can be a quadratic formulation of an otherwise linear algorithm. Currently, writing the quadratic formulation requires significantly more complex and unwieldy code compared to efficient approaches. The fear is that ergonomic improvements to legitimate use cases in this pitch would also improve the ergonomics of inefficient code. + +```swift +// Linear, for when you want `element` +for element in collection { ... } + +// Linear, for when you want `idx` and `element` +for idx in collection.indices { + let element = collection[idx] + ... +} + +// Linear time and linear space, for when you want `element`, `i`, and random-access to `indices`. +let indices = Array(collection.indices) +for i in 0.. Element` must be passed a valid index less than `endIndex`. + +A violation of the above requirements leading to a potential memory safety issue causes a trap. I.e. such violations are [irrecoverable logic errors](https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#logic-failures) and the process is safely taken down. + +Additionally, such low-level index-manipulation-heavy code is most often written within a context where indices are known to be valid, e.g. because the indices were vended by the collection itself. Subscript taking an index is similarly non-optional, as an index is an assumed-valid “key” to a specific element. Swift, being a memory-safe language, will still bounds-check the access, but this is not surfaced to the programmer’s code and bounds checks can be eliminated by the optimizer if safe to do so. Again, invalid indices for these operations represent irrecoverable logic errors, so a trap is issued. + +#### Optional Returning APIs + +In contrast, higher-level operations are often written in a context where the existence of a desired element is not known. Whether there is or is not such an element represents cases that should be handled explicitly by code, most often through the use of an optional result. That is, non-existence is not a logic error but a simple domain error, for which Optional is [the best tool for the job](https://github.com/apple/swift/blob/master/docs/ErrorHandlingRationale.rst#simple-domain-errors). + +For example, Dictionary has the regular `subscript(_:Index) -> Element` trapping subscript for indices derived from the dictionary itself, but additionally provides a `subscript(_:Key) -> Value?`, which returns `nil` if the key is not present in the dictionary. A missing key is not necessarily a logic error, just another case to handle in code. Similarly, `first`, `last`, `.randomElement()`, `min()`, etc., all return optionals, where emptiness is not necessarily a logic error, but a case to be handled in code. + +Optional handling is a strength of Swift and known-valid access is an acceptable use of `!`, which has the effect of converting a simple domain error into an irrecoverable logic error. + +Single element offset-based subscripting follows this pattern by returning `nil` if the offset is not valid for the collection. While this does introduce optional-handling code at the use site, handling such access would be necessary anyways outside of a context where the offset is known to be less than the count. Otherwise, in these contexts, the programmer would have to remember to guard a trapping subscript’s use site by an explicit check against `count`. Offset ranges are clamped, resulting in empty slice return values, just like other partial ranges. + +This means that `collection.last` matches `collection.index(at: .last)` and `collection[.last]` in optionality, and the latter two can be offset. + +#### Example of Known-Valid Offset Context + +For an example of a known-valid-offset context highlighting the differences between the lowest level and higher level APIs, consider the below binary-search algorithm on Collection. This formulation is constant in space and linear in indexing complexity if the collection is not random-access (and logarithmic if it is). Fetching `idx` in the loop body could be written using either `index(_:offsetBy:)`, `index(atOffset:)`, or `index(_:offsetBy:limitedBy:)`, all of which have the same complexity. However, `index(_:offsetBy)` is the lowest-level API available and assumes the given offset is valid (a trap will be issued only for those misuses that lead to a memory safety violation). That is, it treats an invalid offset as a logic error and doesn’t surface this distinction in code, unlike the higher level `index(atOffset:)` and `index(_:offsetBy:limitedBy)` APIs which return optionals. This allows its implementation to skip branches checking the past-the-end condition on every loop iteration, and it allows the caller to skip a branch checking if there was a result. It is unreasonable (at least in general) to expect an optimizer to perform this transformation automatically based on scalar evolution of `count` and an understanding of the implementation of the customizable indexing APIs (which may be dynamically dispatched). + +```swift +extension Collection { + func binarySearch(for element: Element) -> Index? { + assert(self.elementsEqual(self.sorted()), "only valid if sorted") + + var slice = self[...] + var count = self.count // O(n) if non-RAC + while count > 1 { + defer { assert(slice.count == count) } + + let middle = count / 2 + + // Either of the below formulations sum to a total of O(n) index + // advancement operations across all loop iterations if non-RAC. + let idx = slice.index(slice.startIndex, offsetBy: middle) + // let idx = slice.index(at: .start + middle)! + // let idx = slice.index( + // slice.startIndex, offsetBy: middle, limitedBy: slice.endIndex)! + + let candidate = self[idx] + if candidate == element { return idx } + if candidate < element { + slice = slice[idx...] + count = count &- middle // because division truncates + } else { + slice = slice[.. Date: Thu, 29 Aug 2019 12:21:36 -0700 Subject: [PATCH 1254/4563] Nate/preview package revision (#1072) * Revise stdlib preview package proposal * Add link to pitch; audit versioning details --- proposals/0264-stdlib-preview-package.md | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 9234c5a61e..072362ce6f 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -1,13 +1,14 @@ # Standard Library Preview Package * Proposal: [SE-0264](0264-stdlib-preview-package.md) -* Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev) +* Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) * Status: **Scheduled for review (September 2...September 9, 2019)** +* Pitch Discussion: [Pitch: Standard Library Preview Package](https://forums.swift.org/t/pitch-standard-library-preview-package/27202) ## Introduction -We propose the addition of a new package, `SwiftPreview`, which will form the initial landing spot for certain additions to the Swift Standard Library. +We propose the addition of a new package, `SwiftPreview`, which will form the initial landing spot for certain additions to the Swift standard library. Adding this package serves the goal of allowing for rapid adoption of new standard library features, enabling sooner real-world feedback, and allowing for an initial period of time where that feedback can lead to source- and ABI-breaking changes if needed. @@ -27,17 +28,17 @@ For the purposes of this document, we will refer to the proposed standard librar ### Facilitating Rapid Adoption and Feedback -It is common for a feature proposed and accepted through Swift Evolution and added to the Standard Library to wait for months before it gets any real life usage. Take [SE-0199](https://github.com/apple/swift-evolution/blob/master/proposals/0199-bool-toggle.md) (the introduction of `toggle`) for example. It was [accepted](https://github.com/apple/swift-evolution/blob/master/proposals/0199-bool-toggle.md) almost 6 month before Swift 5 was released. +It is common for a feature proposed and accepted through Swift Evolution and added to the standard library to wait for months before it gets any real life usage. Take [SE-0199](https://github.com/apple/swift-evolution/blob/master/proposals/0199-bool-toggle.md) (the introduction of `toggle`) for example. It was [accepted](https://github.com/apple/swift-evolution/blob/master/proposals/0199-bool-toggle.md) almost 6 months before Swift 5 was released. Even though all additions go through a thorough review, there is no substitute for feedback from real-world usage in production. Sometimes we discover that the API is not quite as good as it might have been. -While the toolchains downloadable from swift.org are useful for experimentation, they cannot be used to ship applications, so are rarely used for much more than "kicking the tires" on a feature. The beta period for Xcode provides slightly more real usage, but is still relatively short, with feedback often coming too late to be applied, needing, as it should, a further review on Swift evolution as well as time to integrate into the converging release. And the nature of standard library additions are such that you do not always have an immediate need early in the beta to try out a feature such as partial sort or a min heap. +While the toolchains downloadable from swift.org are useful for experimentation, they cannot be used to ship applications, so are rarely used for much more than "kicking the tires" on a feature. The beta period for Xcode provides slightly more real usage, but is still relatively short, with feedback often coming too late to be applied, given that any changes would require a further review on Swift evolution as well as time to integrate into the converging release. And the nature of standard library additions are such that you do not always have an immediate need early in the beta to try out a feature such as partial sort or a min heap. Once a feature ships as part of a Swift release, any future changes resulting from feedback from real usage must clear the very high bar of source and ABI stability. Even if the change is merited and a source break is justified, the absolute need for ABI stability can rule out certain changes entirely on technical grounds. Furthermore, for performance reasons, standard library types that rely on specialization need to expose some of their internal implementation as part of their ABI, closing off future optimizations or performance fixes that are only discovered through subsequent usage and feedback. ### Technical Challenges for Contributors -The requirements for contributing to the standard library can be prohibitive. Not everybody has the time and resources to build a whole stack including LLVM, Clang, and the Swift compiler itself just to change a part of the Standard Library. Additionally, Xcode and XCTest cannot easily be used to maintain and test standard library code. +The requirements for contributing to the standard library can be prohibitive. Not everybody has the time and resources to build a whole stack including LLVM, Clang, and the Swift compiler itself just to change a part of the standard library. Additionally, Xcode and XCTest cannot easily be used to maintain and test standard library code. Integrating changes into the standard library requires knowledge of non-public features and idioms relating to ABI-stability. In particular, implementing a performant, specializable, ABI-stable collection type while preserving partial future flexibility is _significantly_ harder than writing one that is only source-stable. Harder still is knowing what internal implementation details can be changed after such a partially-transparent type has been published as ABI. @@ -55,7 +56,7 @@ Additions to the package will **continue to use the Swift Evolution process.** A However, requirements around source stability of the package will not be the same as those of the Swift standard library. Follow-up proposals and proposal amendments will be able to make source-breaking changes. Unlike the very high bar for source-breaking changes, and the absolute rules around ABI stability, the bar for changes to API currently only in the package will be that of any other evolution proposal of a new API. -Starting life in the package will not be mandatory. Proposals can opt instead to go straight into the standard library, especially if they do not meet the criteria for suitable package additions (see detailed design section). Functionality that are already available elsewhere, and have been proven in real-life use in that way, and is being "sunk" down into the standard library, would also be good candidates to skip the preview stage. +Starting life in the package will not be mandatory. Proposals can opt instead to go straight into the standard library, especially if they do not meet the criteria for suitable package additions (see detailed design section). Functionality that is already available elsewhere, and has been proven in real-life use in that way, and is being "sunk" down into the standard library, would also be a good candidate to skip the preview stage. Since the package will not be ABI-stable, it will not ship as a binary on any platform, or be a dependency of any ABI-stable package. This allows for changes to the internal implementation of any type, and the change/removal of any function, as part of implementation changes, follow-on proposal amendments, or subsequent proposals. @@ -78,6 +79,16 @@ The following are examples of changes that would _not_ go into the package: Some of these cases will require a judgement call. For example, making an extension method a customization point may bring a minor performance optimization — and so not prevent initial deployment in the package — or it may be a major part of the implementation, making the difference between an `O(1)` and `O(n)` implementation. Whether a proposal should go into the preview package should be part of the evolution pitch and review discussions. +### Evolution + +The introduction of the `SwiftPreview` package does not change much in the process of Swift Evolution. Changes to the standard library API surface should go through a well-developed pitch - discussion - implementation - proposal - decision life-cycle. + +The main difference from the existing process is that the final result will not land straight into the standard library. Instead, the new functionality will become available for general use immediately in a new version of the preview package. As users have real-world experience with the accepted functionality, any proposed amendments to the proposal should go through the same evolution process as an original proposal. + +At a later point, with a timeline set at proposal acceptance, a new kind of review will be run — one to promote parts of the package to the library. This promotion review should be done via an amendment to the proposal, and should not include any changes to the promoted API. The duration of time spent in the package should reflect the complexity of the proposal, and thus the amount of "bake time" it might need. + +No proposal should be accepted into the package on a provisional basis: Reviewers should assume that every proposal will be migrated as-is unless feedback while in the package stage reveals that it was a clear misadventure. The review manager of the promotion amendment will be responsible for ensuring that the review discussion focuses on feedback from real-world use, and not relitigation of previously settled decisions made during the original review. + ### Migration An important aspect of the design is the experience of users of the package when features migrate from the package to the library. This is handled differently for functions and types. @@ -106,15 +117,7 @@ Since types cannot be emitted into client code, these combination function/type #### Retirement from the Package -In order to keep the package size and maintainability manageable, implementations will be removed from the package in a version update **1 year** after migration to the standard library. This is a necessary balance between maintainability and convenience. Since the package is open-source, it should be possible to copy source for a specific feature if a user badly needs to keep it while also wanting to upgrade to a newer major version _and_ not upgrade to the latest compiler. - -### Evolution - -Introduction of the package does not change much in the process of Swift Evolution. Changes to the Standard Library API surface should go through a well developed pitch - discussion - implementation - proposal - decision life-cycle. - -The main difference is that the final result will not land straight into the standard library. Instead, at a later point, a new kind of review will have to be run — one to promote parts of the package to the library. This should be done via an amendment to the proposal, and should be kept lightweight. A timeline for this secondary review should be set at proposal acceptance. The duration of time spent in the package should reflect the complexity of the proposal, and thus the amount of "bake time" it might need. - -No proposal should be accepted into the package on a provisional basis: it should be assumed that every proposal will be migrated as-is unless feedback while in the package stage reveals that it was a clear misadventure. The review manager of the migration amendment will be responsible for ensuring that the review discussion focuses on new feedback from real-world use, and not purely relitigation of previously settled decisions made during the original review. +In order to keep the package size and maintainability manageable, implementations will be removed from the package in a version update **1 year** after migration to the standard library. This is a necessary balance between maintainability and convenience. Since the package is open-source, it should be possible to copy source for a specific feature if a user badly needs to keep it while also wanting to upgrade to the latest version of the package _without_ upgrading to the latest compiler. ### Testing @@ -126,18 +129,15 @@ Certain features of `StdlibUnittest` – in particular crashLater and `StdlibCol ### GYB -There will be no use of [gyb](https://github.com/apple/swift/blob/master/utils/gyb.py) in the package. Gyb is a powerful and useful tool, but it can cause code that is difficult to read and write, and does not interact well with Xcode, so runs counter one of the goals of this package: to simplify contribution. - -The need for gyb has been gradually reducing over time as language features like conditional conformance and improved optimization eliminated the need for it. Generally speaking, most uses of gyb indicate missing language or library features, and so implementations of proposals should probably start by proposing those features instead. For example, it is unlikely we would accept further proposals to splat out multiple operators for different sizes of tuple like we do with `==`. Instead, protocol conformance for non-nominal types and variadic generics should solve this problem more generally. +There will be no use of [gyb](https://github.com/apple/swift/blob/master/utils/gyb.py) in the package. The need for gyb has been gradually reducing over time as language features like conditional conformance and improved optimization eliminated the need for it. Gyb is a powerful and useful tool, but it can cause code that is difficult to read and write, and does not interact well with Xcode, so runs counter one of the goals of this package: to simplify contribution. ### Versioning -The fundamental goals of the preview package are somewhat at odds with the usual goals of semantic versioning. Source-breaking changes, including retirements after migrating into the standard library, or source-breaking changes resulting from evolution proposals prior to migration, will be common. Unlike many packages, the preview package will not converge over time, reducing the frequency of its version bumps. +The fundamental goals of the preview package are somewhat at odds with the usual goals of semantic versioning. Source-breaking changes, including retirements after promoting into the standard library, or source-breaking changes resulting from evolution proposals prior to promotion, will be common. Unlike many packages, the preview package will not converge over time, which normally reduces the frequency of a package's version bumps. As such, the preview package will remain at major version `0` **permanently**. Minor version bumps will be able to include source-breaking changes. Patch updates should not break source and will just be used for bug fixes. -The use of a `0` major version at all times will act as a signifier that adopters need to be comfortable with the requirement to continuously update -to the latest version of the package. It should not be taken as an indication that the package shouldn't be used for real-world code: the code should be considered production-worthy. But it is a preview, and not source stable. Where practical, deprecated shims could be used to preserve source compatability between versions, but this may not always be practical. +The use of a `0` major version at all times will act as a signifier that adopters need to be comfortable with the requirement to continuously update to the latest version of the package. It should not be taken as an indication that the package shouldn't be used for real-world code: the code should be considered production-worthy. But it is a preview, and not source stable. Where practical, deprecated shims could be used to preserve source compatability between versions, but this may not always be practical. The decision when to tag minor versions, and potentially to coalesce multiple changes into a single version bump, will be made by the release manager for the standard library as chosen by the Swift project lead. From 84245c51c0446c565885d7cb358e486e83db9639 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 30 Aug 2019 11:26:18 +0100 Subject: [PATCH 1255/4563] [SE-0264] Fix typos * `Swift 5` -> `Swift 4.2` * `compatability` -> `compatibility` * `rather then` -> `rather than` --- proposals/0264-stdlib-preview-package.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 072362ce6f..bf7f3502ab 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -28,7 +28,7 @@ For the purposes of this document, we will refer to the proposed standard librar ### Facilitating Rapid Adoption and Feedback -It is common for a feature proposed and accepted through Swift Evolution and added to the standard library to wait for months before it gets any real life usage. Take [SE-0199](https://github.com/apple/swift-evolution/blob/master/proposals/0199-bool-toggle.md) (the introduction of `toggle`) for example. It was [accepted](https://github.com/apple/swift-evolution/blob/master/proposals/0199-bool-toggle.md) almost 6 months before Swift 5 was released. +It is common for a feature proposed and accepted through Swift Evolution and added to the standard library to wait for months before it gets any real life usage. Take [SE-0199](https://github.com/apple/swift-evolution/blob/master/proposals/0199-bool-toggle.md) (the introduction of `toggle`) for example. It was [accepted](https://forums.swift.org/t/accepted-se-199-add-toggle-to-bool/10681) almost 6 months before Swift 4.2 was released. Even though all additions go through a thorough review, there is no substitute for feedback from real-world usage in production. Sometimes we discover that the API is not quite as good as it might have been. @@ -137,7 +137,7 @@ The fundamental goals of the preview package are somewhat at odds with the usual As such, the preview package will remain at major version `0` **permanently**. Minor version bumps will be able to include source-breaking changes. Patch updates should not break source and will just be used for bug fixes. -The use of a `0` major version at all times will act as a signifier that adopters need to be comfortable with the requirement to continuously update to the latest version of the package. It should not be taken as an indication that the package shouldn't be used for real-world code: the code should be considered production-worthy. But it is a preview, and not source stable. Where practical, deprecated shims could be used to preserve source compatability between versions, but this may not always be practical. +The use of a `0` major version at all times will act as a signifier that adopters need to be comfortable with the requirement to continuously update to the latest version of the package. It should not be taken as an indication that the package shouldn't be used for real-world code: the code should be considered production-worthy. But it is a preview, and not source stable. Where practical, deprecated shims could be used to preserve source compatibility between versions, but this may not always be practical. The decision when to tag minor versions, and potentially to coalesce multiple changes into a single version bump, will be made by the release manager for the standard library as chosen by the Swift project lead. @@ -157,7 +157,7 @@ Because we do not have cross-module optimization, package implementations should An alternative to a single monolithic preview package would be multiple packages. For example, each accepted evolution proposal could have its own package. -This would trade convenience for flexibility. In particular, multiple packages would avoid the source-breaking nature of proposal revisions (and the lesser problem of any additions being technically source breaking). But it would mean that adopters of the different previews would need to look up and add individual proposals rather then get them all in one go, and would rarely "discover" new features also in the package they're already importing. +This would trade convenience for flexibility. In particular, multiple packages would avoid the source-breaking nature of proposal revisions (and the lesser problem of any additions being technically source breaking). But it would mean that adopters of the different previews would need to look up and add individual proposals rather than get them all in one go, and would rarely "discover" new features also in the package they're already importing. ### Simultaneous Preview and Library Addition From 1f73fd97138c331f7247654d0876239a4d05bd26 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Thu, 5 Sep 2019 23:10:20 -0700 Subject: [PATCH 1256/4563] Return SE-0263 for minor revision. --- proposals/0263-string-uninitialized-initializer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0263-string-uninitialized-initializer.md b/proposals/0263-string-uninitialized-initializer.md index c7c4e57cfc..61e82dea82 100644 --- a/proposals/0263-string-uninitialized-initializer.md +++ b/proposals/0263-string-uninitialized-initializer.md @@ -3,7 +3,7 @@ * Proposal: [SE-0263](0263-string-uninitialized-initializer.md) * Author: [David Smith](https://github.com/Catfish-Man) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active Review (July 29...August 9, 2019)** +* Status: [Returned for revision](https://forums.swift.org/t/se-0263-add-a-string-initializer-with-access-to-uninitialized-storage/27417/32) * Implementation: [apple/swift#23409](https://github.com/apple/swift/pull/23409) * Bug: [SR-10288](https://bugs.swift.org/browse/SR-10288) From d32cf1020ee523b959ef5e0634dad197bc2e957a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 12 Sep 2019 11:08:10 -0700 Subject: [PATCH 1257/4563] SE-0258 Property Wrappers is implemented in Swift 5.1 --- proposals/0258-property-wrappers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index 9020dc8f96..e9b1ab56b9 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -3,7 +3,7 @@ * Proposal: [SE-0258](0258-property-wrappers.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5.1)** * Implementation: [Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/251//artifact/branch-master/swift-PR-25781-251-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/327//artifact/branch-master/swift-PR-25781-327-osx.tar.gz) * Review: ([review #1](https://forums.swift.org/t/se-0258-property-delegates/23139)) ([revision announcement #1](https://forums.swift.org/t/returned-for-revision-se-0258-property-delegates/24080)) ([review #2](https://forums.swift.org/t/se-0258-property-wrappers-second-review/25843)) ([review #3](https://forums.swift.org/t/se-0258-property-wrappers-third-review/26399)) ([acceptance](https://forums.swift.org/t/accepted-with-modification-se-0258-property-wrappers/26828)) * Previous versions: [Revision #3](https://github.com/apple/swift-evolution/blob/e99ae69370f56ae84256b78902ab377cb8249cdd/proposals/0258-property-wrappers.md), [Revision #2](https://github.com/apple/swift-evolution/blob/bb8709c2ddca25c21a3c1e0298ce9457911dbfba/proposals/0258-property-wrappers.md), [Revision #1](https://github.com/apple/swift-evolution/commit/8c3499ec5bc22713b150e2234516af3cb8b16a0b) From 79e8e46651103da429ecf7ac47cc746fb5931118 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Mon, 16 Sep 2019 09:08:08 -0700 Subject: [PATCH 1258/4563] Update 0264-stdlib-preview-package.md --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index bf7f3502ab..ff1b3402d5 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,7 +3,7 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) -* Status: **Scheduled for review (September 2...September 9, 2019)** +* Status: **On hold awaiting revision by author** * Pitch Discussion: [Pitch: Standard Library Preview Package](https://forums.swift.org/t/pitch-standard-library-preview-package/27202) ## Introduction From 7691de1768826a9f88011d393f83afd3e08da446 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Mon, 16 Sep 2019 12:11:55 -0500 Subject: [PATCH 1259/4563] Drop intermediate review from preview package proposal. --- proposals/0264-stdlib-preview-package.md | 39 +++++++++--------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index ff1b3402d5..14b2e75ffa 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -46,17 +46,15 @@ It would be of great benefit to the community to allow proposals and contributio ## Proposed solution -We propose the introduction of a "Standard Library Preview" SwiftPM package. +We propose the introduction of a `SwiftPreview` SwiftPM package. The package will be a standalone project hosted on GitHub under the Apple organization, and will be part of the overall Swift project. -It will be a standalone project hosted on Github under the Apple organization, and will be part of the overall Swift project. +Whenever possible, proposals for additions to the standard library will land first in the package before migrating to the standard library. A PR against the preview package will be sufficient to fulfill the implementation requirement for an evolution proposal. Proposals that are accepted will land in the package immediately, and then be integrated into the standard library. -Proposals for additions to the standard library will have the option of first landing in the package for a period before migrating to the library. A PR against the preview package will be sufficient to fulfill the implementation requirement for an evolution proposal. +All additions to the standard library will **continue to use the Swift Evolution process**, no matter whether they first land in the package or not. All additions to the package should be made with the understanding that they will migrate to the ABI-stable standard library module that ships as part of the Swift toolchain. -Additions to the package will **continue to use the Swift Evolution process.** All additions to the package should be made on the assumption that they will in time migrate to the ABI-stable standard library module that ships as part of the Swift toolchain. Proposals first landing in the package should be given the same level of scrutiny currently applied to standard library additions today. +If usage after acceptance of a proposal reveals unanticipated problems, a follow-up proposal amendments will be able to make source-breaking changes. Unlike the very high bar for source-breaking changes, and the absolute rules around ABI stability, the bar for changes to API that haven't shipped as part of a Swift release will be that of any other evolution proposal of a new API. -However, requirements around source stability of the package will not be the same as those of the Swift standard library. Follow-up proposals and proposal amendments will be able to make source-breaking changes. Unlike the very high bar for source-breaking changes, and the absolute rules around ABI stability, the bar for changes to API currently only in the package will be that of any other evolution proposal of a new API. - -Starting life in the package will not be mandatory. Proposals can opt instead to go straight into the standard library, especially if they do not meet the criteria for suitable package additions (see detailed design section). Functionality that is already available elsewhere, and has been proven in real-life use in that way, and is being "sunk" down into the standard library, would also be a good candidate to skip the preview stage. +Starting life in the package will not be mandatory. Proposed additions can instead go straight into the standard library, if they do not meet the criteria for suitable package additions. See the detailed design section for an expanded discussion of these criteria. Since the package will not be ABI-stable, it will not ship as a binary on any platform, or be a dependency of any ABI-stable package. This allows for changes to the internal implementation of any type, and the change/removal of any function, as part of implementation changes, follow-on proposal amendments, or subsequent proposals. @@ -83,15 +81,15 @@ Some of these cases will require a judgement call. For example, making an extens The introduction of the `SwiftPreview` package does not change much in the process of Swift Evolution. Changes to the standard library API surface should go through a well-developed pitch - discussion - implementation - proposal - decision life-cycle. -The main difference from the existing process is that the final result will not land straight into the standard library. Instead, the new functionality will become available for general use immediately in a new version of the preview package. As users have real-world experience with the accepted functionality, any proposed amendments to the proposal should go through the same evolution process as an original proposal. +The main difference from the existing process is that the final result will become available for general use immediately in a new version of the preview package. As users have real-world experience with the accepted functionality, any proposed amendments to the proposal must go through the same evolution process as an original proposal. -At a later point, with a timeline set at proposal acceptance, a new kind of review will be run — one to promote parts of the package to the library. This promotion review should be done via an amendment to the proposal, and should not include any changes to the promoted API. The duration of time spent in the package should reflect the complexity of the proposal, and thus the amount of "bake time" it might need. +To provide time for feedback from real-world use of new additions, the Swift Evoluton process should favor landing new features in a window immediately after branching for a major release. This doesn’t mean that important and valuable proposals can’t be added at different times, but they’ll be subject to increased scrutiny. -No proposal should be accepted into the package on a provisional basis: Reviewers should assume that every proposal will be migrated as-is unless feedback while in the package stage reveals that it was a clear misadventure. The review manager of the promotion amendment will be responsible for ensuring that the review discussion focuses on feedback from real-world use, and not relitigation of previously settled decisions made during the original review. +No proposal should be accepted into the package on a provisional basis: Reviewers should assume that every proposal will be migrated as-is unless feedback reveals that it was a clear misadventure. The review manager for any revising proposals will be responsible for ensuring that the review discussion focuses on feedback from real-world use, and not relitigation of previously settled decisions made during the original review. ### Migration -An important aspect of the design is the experience of users of the package when features migrate from the package to the library. This is handled differently for functions and types. +An important aspect of the design is the experience of users of the package when features that have been available from the package become available in the standard library in a new Swift or platform release. This is handled differently for functions and types. #### Types @@ -139,13 +137,13 @@ As such, the preview package will remain at major version `0` **permanently**. M The use of a `0` major version at all times will act as a signifier that adopters need to be comfortable with the requirement to continuously update to the latest version of the package. It should not be taken as an indication that the package shouldn't be used for real-world code: the code should be considered production-worthy. But it is a preview, and not source stable. Where practical, deprecated shims could be used to preserve source compatibility between versions, but this may not always be practical. -The decision when to tag minor versions, and potentially to coalesce multiple changes into a single version bump, will be made by the release manager for the standard library as chosen by the Swift project lead. +The decision when to tag minor versions will be made by the release manager for the standard library as chosen by the Swift project lead. ### Source compatibility None on Swift itself. The intention of the package is to facilitate long-term source stability by detecting issues with APIs prior to their integration into the library. -# Effect on ABI stability +## Effect on ABI stability None. The package will not be declared ABI stable. ABI stability will only happen when proposals are migrated to the standard library. This means that the preview package may not be used within binary frameworks. @@ -153,19 +151,12 @@ Because we do not have cross-module optimization, package implementations should ## Alternatives considered -### Single versus Multiple Packages - -An alternative to a single monolithic preview package would be multiple packages. For example, each accepted evolution proposal could have its own package. - -This would trade convenience for flexibility. In particular, multiple packages would avoid the source-breaking nature of proposal revisions (and the lesser problem of any additions being technically source breaking). But it would mean that adopters of the different previews would need to look up and add individual proposals rather than get them all in one go, and would rarely "discover" new features also in the package they're already importing. - -### Simultaneous Preview and Library Addition +### Additional Review Before Library Integration -If the goal was to facilitate rapid release only, but not a longer feedback period, then the additional review step would not be necessary. Instead, new features would be integrated directly into the package and library simultaneously. +A previous version of this proposal prescribed a window of time before promoting accepted features into the standard library, to provide additional bake time and an additional review pointfor the feature. However, this approach delays some important feedback that can only be gained from integration into the standard library, where overload resolution and performance can have different outcomes than the same APIs shipped in a package. -However, experience has shown that proposals are not always perfect. Recently, significant if manageable issues were discovered with the API of both [SE-180](https://github.com/apple/swift-evolution/blob/master/proposals/0180-string-index-overhaul.md) and [SE-202](https://github.com/apple/swift-evolution/blob/master/proposals/0202-random-unification.md), and in a near-miss [SE-220](https://github.com/apple/swift-evolution/blob/master/proposals/0220-count-where.md) had to be reverted late during 5.0 convergence due to typechecker performance impact. These issues would likely have been caught by a more prolonged exposure in a package. +### No Semantic Versioning -### Is a Second Review Necessary? +An alternative to keeping the semver major version at `0` is to not version the package at all. With this approach, the package could only be included by packages by specifying a branch-based dependendcy. However, this would be too restricting at this time, as a branch-based dependency can only be imported by other branch-based dependencies. This would effectively limit use of the `SwiftPreview` package to top-level applications only. -Whether or not a proposal accepted and integrated into the package actually needs a second review is also worth considering. An alternative would be to set a timer, and then to automatically migrate the proposal into the library when the timer expires, without need for a second review. Changes could still be initiated to the preview package, but a new proposal would be required to interrupt the otherwise automatic migration timetable. From 39789e1cd18ee7eb367d6e137ad3657aac9e4cc7 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 18 Sep 2019 19:07:52 -0700 Subject: [PATCH 1260/4563] [OffsetBound] v2 of proposal * Removed `.start` and `.end`, now all `OffsetBound`s are built from `.first` and `.last`. * Simplifies usage and learnability. `.last + 1` representing the end is mostly only relevant to documentation. * Better delineates `OffsetBound` abstraction and terminology from indices. * Added RangeReplaceableCollection convenience overloads, as well as subscript setters. * `.insert(at:)`, `.remove(at:)`, etc., * Makes `OffsetBound` broadly useful, fleshes out abstraction --- proposals/nnnn-offset-indexing-and-slicing.md | 358 ++++++++++++++---- 1 file changed, 276 insertions(+), 82 deletions(-) diff --git a/proposals/nnnn-offset-indexing-and-slicing.md b/proposals/nnnn-offset-indexing-and-slicing.md index f0c6e28590..ecba68a187 100644 --- a/proposals/nnnn-offset-indexing-and-slicing.md +++ b/proposals/nnnn-offset-indexing-and-slicing.md @@ -1,17 +1,16 @@ -# Offset Indexing and Slicing +# Offset-Based Access to Indices, Elements, and Slices * Proposal: SE-NNNN * Author: [Michael Ilseman](https://github.com/milseman) * Review Manager: TBD * Status: **Awaiting review** * Implementation: [apple/swift#24296](https://github.com/apple/swift/pull/24296) - + ## Introduction -This pitch introduces `OffsetBound`, which can represent a position in a collection specified as an offset from either the start or end of the collection (i.e. the collection’s “bounds”). This covers an expressivity gap in collection APIs by providing easy and safe means to fetch an index or element from such an offset as well as convenient slicing. -If you would like to try it out, you can just copy-paste from [this gist](https://gist.github.com/milseman/1461e4f3e195974a5d1ad76cefdd6961), which includes the functionality as well as test cases and examples. This work is the culmination of prior discussion from an [earlier thread](https://forums.swift.org/t/pitch-offsetting-indices-and-relative-ranges/23837), the [thread before that](https://forums.swift.org/t/call-for-users-and-authors-offset-indexing-pitch/21444), and [@Letan](https://forums.swift.org/u/letan) ’s [original thread](https://forums.swift.org/t/shorthand-for-offsetting-startindex-and-endindex/9397). The latest pitch thread can be found [TBD](h). +This proposal introduces `OffsetBound`, which can represent a position in a collection specified as an offset from either the beginning or end of the collection (i.e. the collection’s “bounds”). Corresponding APIs provide a more convenient abstraction over indices. The goal is to alleviate an expressivity gap in collection APIs by providing easy and safe means to access elements, indices, and slices from such offsets. -Swift-evolution thread: [TBD](TBD) +If you would like to try it out, you can just copy-paste from [this gist](https://gist.github.com/milseman/1461e4f3e195974a5d1ad76cefdd6961), which includes the functionality as well as test cases and examples. This work is the culmination of prior discussion from an [earlier thread](https://forums.swift.org/t/pitch-offsetting-indices-and-relative-ranges/23837), the [thread before that](https://forums.swift.org/t/call-for-users-and-authors-offset-indexing-pitch/21444), and [@Letan](https://forums.swift.org/u/letan) ’s [original thread](https://forums.swift.org/t/shorthand-for-offsetting-startindex-and-endindex/9397). The latest pitch thread can be found [here](https://forums.swift.org/t/offset-indexing-and-slicing/28333). ## Motivation @@ -36,7 +35,9 @@ func parseRequirements(_ s: String) -> [(finish: Character, before: Character)] } ``` -By taking a detour through forming SubSequences, we could alternatively express this as: +Advancing indices by hand through `line.index(line.startIndex, offsetBy: 5)` is fairly obnoxious and distracts from the intent of the code. + +Alternatively, we could take a detour through forming `SubSequence`s: ```swift func parseRequirements(_ s: String) -> [(finish: Character, before: Character)] { @@ -46,6 +47,8 @@ func parseRequirements(_ s: String) -> [(finish: Character, before: Character)] } ``` +This results in less boilerplate code, but the detour through slicing APIs increases the cognitive load. Anyone reading the code has to jump through mental hoops, and the code author has more details to reason through (when we first wrote this example, we had an off-by-one error). + Instead, this proposal provides a way to directly extract elements from known offsets. @@ -107,7 +110,7 @@ print(everyOther[startIdx + 1]) // 2, but everyOther doesn't even contain 2! print(everyOther[everyOther.index(after: startIdx)]) // 3 ``` -This proposal provides a way to perform offset-based indexing for such collections, with similar expressivity but with explicit start/end bounds for clarity. +This proposal provides a way to have offset-based element access for such collections, with similar expressivity but with explicit bounds for clarity. ## Proposed solution @@ -116,17 +119,17 @@ We propose convenient subscripts for slicing, single-element retrieval, and fetc ```swift let str = "abcdefghijklmnopqrstuvwxyz" -print(str[.start + 3 ..< .start + 6]) // "def" -print(str[.start + 3 ..< .end - 3]) // "defghijklmnopqrstuvw" -print(str[.start + 3 ..< .end - 23]) // "", +print(str[.first + 3 ..< .first + 6]) // "def" +print(str[.first + 3 ..< .last - 2]) // "defghijklmnopqrstuvw" +print(str[.first + 3 ..< .last - 22]) // "", print(str[.last]) // Optional("z") print(str[.last - 1]) // Optional("y") -print(str[.start + 26]) // nil +print(str[.first + 26]) // nil print(str[(.last - 3)...]) // "wxyz" print(str.index(at: .last - 1)) // Optional(... index of "y") -print(str.index(at: .end - 26)) // Optional(... index of "a") -print(str.index(at: .end - 27)) // nil +print(str.index(at: .last - 25)) // Optional(... index of "a") +print(str.index(at: .last - 26)) // nil ``` The `parseRequirements` example from above can be written as: @@ -139,24 +142,24 @@ func parseRequirements(_ s: String) -> [(finish: Character, before: Character)] } ``` -This is available on all Collections, allowing a more general solution. The Advent of Code exercise only requires the extraction (and later comparison) of the elements at the corresponding positions: +These APIs are available on all Collections, allowing a more general solution. The Advent of Code exercise only requires the extraction and comparison of the elements at the corresponding positions, so we can generalize to: ```swift func parseRequirements( _ c: C, lineSeparator: C.Element -) -> [(finish: C.Element, before: C.Element)] { +) -> [(finish: C.Element, before: C.Element)] where C.Element: Comparable { c.split(separator: lineSeparator).map { line in (line[.first + 5]!, line[.last - 11]!) } } ``` -Here, the `line[.last - 11]` will run in constant-time if the passed collection conforms to `BidirectionalCollection`, or in linear-time if it does not (since we have to count from the front). This algorithmic guarantee is part of this proposal, and without this generalization cannot be done. +Here, the `line[.last - 11]` will run in constant-time if `c` conforms to `BidirectionalCollection`, or in linear-time if it does not (since we have to count from the front). This algorithmic guarantee is added as part of this proposal, without which this generalization cannot be done. These also address the expressivity issues and assumptions with Int-indexed Collections above: ```swift -print((3..<10)[(.start + 5)...]) // 8..<10 +print((3..<10)[(.first + 5)...]) // 8..<10 func fifth(_ c: C) -> C.Element? { return c[.first + 4] } @@ -170,17 +173,18 @@ data = data.dropFirst(2) print(fifth(data)!) // 7 let everyOther = EveryOther(storage: [1,2,3,4,5,6,7,8]) -print(everyOther[.start + 1]!) // 3 +print(everyOther[.first + 1]!) // 3 ``` + ## Detailed design This proposal adds an `OffsetBound` struct, representing a position at an offset from the start or end of a collection. ```swift -/// A position in a collection specified as an offset from either the start -/// or the end of the collection (i.e. the collection's bounds). +/// A position in a collection specified as an offset from either the first +/// or last element of the collection (i.e. the collection's bounds). /// /// You can use an `OffsetBound` to access an index or element of a collection /// as well as extract a slice. For example: @@ -188,9 +192,9 @@ This proposal adds an `OffsetBound` struct, representing a position at an offset /// let str = "abcdefghijklmnopqrstuvwxyz" /// print(str[.last]) // Optional("z") /// print(str[.last - 2]) // Optional("x") -/// print(str[.start + 26]) // nil -/// print(str[.start + 3 ..< .start + 6]) // "def" -/// print(str[.start + 3 ..< .end - 3]) // "defghijklmnopqrstuvw" +/// print(str[.first + 26]) // nil +/// print(str[.first + 3 ..< .first + 6]) // "def" +/// print(str[.first + 3 ..< .last - 2]) // "defghijklmnopqrstuvw" /// /// `OffsetBound`s also provide a convenient way of working with slice types /// over collections whose index type is `Int`. Slice types share indices with @@ -199,25 +203,17 @@ This proposal adds an `OffsetBound` struct, representing a position at an offset /// /// let array = [1,2,3,4,5,6] /// print(array[2...][3) // 4 -/// print(array[2...][.start + 3]!) // 6 +/// print(array[2...][.first + 3]!) // 6 /// public struct OffsetBound { /* internally stores an enum, not ABI/API */ - /// The position corresponding to `startIndex` and the first element of a - /// nonempty collection. - public static var start: OffsetBound - - /// The position corresponding to `endIndex`, which is "past the end"---that - /// is, the position one greater than the last valid subscript argument. - public static var end: OffsetBound - - /// The position corresponding to `startIndex` and the first element of a - /// nonempty collection. This is equivalent to `start`. + /// The position of the first element of a nonempty collection, corresponding + /// to `startIndex`. public static var first: OffsetBound - /// The position corresponding to the last element of an nonempty - /// collection. This is equivalent to `.end - 1`. + /// The position of the last element of a nonempty collection, corresponding + /// to `index(before: endIndex)`. public static var last: OffsetBound /// Returns a bound that offsets the given bound by the specified distance. @@ -225,7 +221,7 @@ public struct OffsetBound { /// For example: /// /// .first + 2 // The position of the 3rd element - /// .last + 1 // One past the last element, equivalent to `.end` + /// .last + 1 // One past the last element, corresponding to `endIndex` /// public static func +(_ lhs: OffsetBound, _ rhs: Int) -> OffsetBound @@ -235,26 +231,26 @@ public struct OffsetBound { /// For example: /// /// .last - 2 // Two positions before the last element's position - /// .end - 1 // The position before the (open) upper-bound, i.e. `.last` /// public static func -(_ lhs: OffsetBound, _ rhs: Int) -> OffsetBound } ``` + `OffsetBound` is `Comparable`, and as such can be used as a bound type for `RangeExpression`s. ```swift extension OffsetBound: Comparable { /// Compare the positions represented by two `OffsetBound`s. /// - /// Offsets relative to `.start` are always less than those relative to - /// `.end`, as there are arbitrarily many offsets between the two extremities. - /// Offsets from the same bound are ordered by their corresponding positions. - /// For example: + /// Offsets relative to `.first` are always less than those relative to + /// `.last`, as there are arbitrarily many offsets between the two + /// extremities. Offsets from the same bound are ordered by their + /// corresponding positions. For example: /// - /// .start + n < .end - m // true for all values of n and m - /// .start + n < .start + m // equivalent to n < m - /// .end - n < .end - m // equivalent to n > m + /// .first + n < .last - m // true for all values of n and m + /// .first + n < .first + m // equivalent to n < m + /// .last - n < .last - m // equivalent to n > m /// public static func < (_ lhs: OffsetBound, _ rhs: OffsetBound) -> Bool @@ -263,14 +259,14 @@ extension OffsetBound: Comparable { /// This is only true if both offset the same bound by the same amount. For /// example: /// - /// .last == .end - 1 // true - /// .start + n == .end - m // false for all values of n and m - /// .start + n == .start + m // equivalent to n == m + /// .first + n == .last - m // false for all values of n and m + /// .first + n == .first + m // equivalent to n == m /// public static func == (_ lhs: OffsetBound, _ rhs: OffsetBound) -> Bool } ``` + `Collection` gets an API to retrieve an index from an `OffsetBound` if it exists, a subscript to retrieve an element from an `OffsetBound` if it exists, and a slicing subscript to extract a range. ```swift @@ -282,55 +278,249 @@ extension Collection { /// - O(1) if the collection conforms to `RandomAccessCollection`. /// - O(*k*) where *k* is equal to the offset if the collection conforms to /// `BidirectionalCollection`. - /// - O(*k*) if the offset is positive or zero. + /// - O(*k*) if `position` is `.first + n` for any n, or `.last + 1`. /// - Otherwise, O(*n*) where *n* is the length of the collection. - /// Note that `.last` represents `.end - 1`, therefore it has a negative - /// offset - public func index(at bound: OffsetBound) -> Index? + public func index(at position: OffsetBound) -> Index? /// Returns the corresponding element for the provided offset, if it exists, /// else returns nil. /// /// Example: /// - /// let str = "abcdefghijklmnopqrstuvwxyz" - /// print(str[.last]) // Optional("z") - /// print(str[.last - 2]) // Optional("x") - /// print(str[.start + 26]) // nil + /// let abcs = "abcdefg" + /// print(abcs[.last]) // Optional("g") + /// print(abcs[.last - 2]) // Optional("e") + /// print(abcs[.first + 8]) // nil /// /// - Complexity: /// - O(1) if the collection conforms to `RandomAccessCollection`. /// - O(*k*) where *k* is equal to the offset if the collection conforms to /// `BidirectionalCollection`. - /// - O(*k*) if the offset is positive or zero. + /// - O(*k*) if `position` is `.first + n` for any n, or `.last + 1`. /// - Otherwise, O(*n*) where *n* is the length of the collection. - /// Note that `.last` represents `.end - 1`, therefore it has a negative - /// offset - public subscript(bound: OffsetBound) -> Element? + public subscript(position: OffsetBound) -> Element? /// Returns the contiguous subrange of elements corresponding to the provided /// offsets. /// /// Example: /// - /// let str = "abcdefghijklmnopqrstuvwxyz" - /// print(str[.start + 3 ..< .start + 6]) // "def" - /// print(str[.start + 3 ..< .end - 3]) // "defghijklmnopqrstuvw" + /// let abcs = "abcdefg" + /// print(abcs[.first + 1 ..< .first + 6]) // "bcdef" + /// print(abcs[.first + 1 ..< .last - 1]) // "bcde" /// /// - Complexity: /// - O(1) if the collection conforms to `RandomAccessCollection`. /// - O(*k*) where *k* is equal to the larger offset if the collection /// conforms to `BidirectionalCollection`. - /// - O(*k*) if both offsets are positive or zero. + /// - O(*k*) if the offsets are `.first + n` for any n or `.last + 1`. /// - Otherwise, O(*n*) where *n* is the length of the collection. - /// Note that `.last` represents `.end - 1`, therefore it has a negative - /// offset public subscript( range: ORE ) -> SubSequence where ORE.Bound == OffsetBound } ``` + +RangeReplaceableCollection gets corresponding APIs in terms of OffsetBound, as well as subscript setters. + +```swift +extension RangeReplaceableCollection { + /// Replaces the specified subrange of elements with the given collection. + /// + /// This method has the effect of removing the specified range of elements + /// from the collection and inserting the new elements at the same location. + /// The number of new elements need not match the number of elements being + /// removed. + /// + /// In this example, two characters in the middle of a string are + /// replaced by the three elements of a `Repeated` instance. + /// + /// var animals = "🐕🐈🐱🐩" + /// let dogFaces = repeatElement("🐶" as Character, count: 3) + /// animals.replaceSubrange(.first + 1 ... .last - 1, with: dogFaces) + /// print(animals) + /// // Prints "🐕🐶🐶🐶🐩" + /// + /// If you pass a zero-length range as the `subrange` parameter, this method + /// inserts the elements of `newElements` at `subrange.startIndex`. Calling + /// the `insert(contentsOf:at:)` method instead is preferred. + /// + /// Likewise, if you pass a zero-length collection as the `newElements` + /// parameter, this method removes the elements in the given subrange + /// without replacement. Calling the `removeSubrange(_:)` method instead is + /// preferred. + /// + /// Calling this method may invalidate any existing indices for use with this + /// collection. + /// + /// - Parameters: + /// - subrange: The subrange of the collection to replace, specified as + /// offsets from the collection's bounds. + /// - newElements: The new elements to add to the collection. + /// + /// - Complexity: O(*n* + *m*), where *n* is length of this collection and + /// *m* is the length of `newElements`. If the call to this method simply + /// appends the contents of `newElements` to the collection, the complexity + /// is O(*m*). + public mutating func replaceSubrange( + _ subrange: R, with newElements: __owned C + ) where C.Element == Element, R.Bound == OffsetBound + + /// Inserts a new element into the collection at the specified position. + /// + /// The new element is inserted before the element currently at the specified + /// offset. If you pass `.last + 1` as the `position` parameter, corresponding + /// to the collection's `endIndex`, the new element is appended to the + /// collection. + /// + /// var numbers = "12345" + /// numbers.insert("Ⅸ", at: .first + 1) + /// numbers.insert("𐄕", at: .last + 1) + /// + /// print(numbers) + /// // Prints "1Ⅸ2345𐄕" + /// + /// Calling this method may invalidate any existing indices for use with this + /// collection. + /// + /// - Parameter newElement: The new element to insert into the collection. + /// - Parameter `position`: The position at which to insert the new element, + /// specified as offsets from the collection's bounds + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. If + /// `position == .last + 1`, this method is equivalent to `append(_:)`. + public mutating func insert( + _ newElement: __owned Element, at position: OffsetBound + ) + + /// Inserts the elements of a sequence into the collection at the specified + /// position. + /// + /// The new elements are inserted before the element currently at the + /// specified offset. If you pass `.last + 1` as the `position` parameter, + /// corresponding to the collection's `endIndex`, the new elements are + /// appended to the collection. + /// + /// Here's an example of inserting vulgar fractions in a string of numbers. + /// + /// var numbers = "12345" + /// numbers.insert(contentsOf: "↉⅖⅑", at: .first + 2) + /// print(numbers) + /// // Prints "12↉⅖⅑345" + /// + /// Calling this method may invalidate any existing indices for use with this + /// collection. + /// + /// - Parameter newElements: The new elements to insert into the collection. + /// - Parameter `position`: The position at which to insert the new elements, + /// specified as offsets from the collection's bounds + /// + /// - Complexity: O(*n* + *m*), where *n* is length of this collection and + /// *m* is the length of `newElements`. If `position == .last + 1`, this + /// method is equivalent to `append(contentsOf:)`. + public mutating func insert( + contentsOf newElements: __owned S, at position: OffsetBound + ) where S.Element == Element + + /// Removes and returns the element at the specified position, if it exists, + /// else returns nil. + /// + /// All the elements following the specified position are moved to close the + /// gap. + /// + /// Example: + /// var measurements = [1.2, 1.5, 2.9, 1.2, 1.6] + /// let removed = measurements.remove(at: .last - 2) + /// print(measurements) + /// // Prints "[1.2, 1.5, 1.2, 1.6]" + /// print(measurements.remove(at: .first + 4)) + /// // Prints nil + /// + /// Calling this method may invalidate any existing indices for use with this + /// collection. + /// + /// - Parameter position: The position of the element to remove, specified as + /// an offset from the collection's bounds. + /// - Returns: The removed element if it exists, else nil + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + public mutating func remove(at position: OffsetBound) -> Element? + + /// Removes the elements in the specified subrange from the collection. + /// + /// All the elements following the specified position are moved to close the + /// gap. This example removes two elements from the middle of a string of + /// rulers. + /// + /// var rulers = "📏🤴👑📐" + /// rulers.removeSubrange(.first + 1 ... .last - 1) + /// print(rulers) + /// // Prints "📏📐" + /// + /// Calling this method may invalidate any existing indices for use with this + /// collection. + /// + /// - Parameter range: The range of the collection to be removed, specified + /// as offsets from the collection's bounds. + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + public mutating func removeSubrange( + _ range: R + ) where R.Bound == OffsetBound + + /// Accesses the element corresponding to the provided offset. If no element + /// exists, `nil` is returned from the getter. Similarly, setting an element + /// to `nil` will remove the element at that offset. + /// + /// Example: + /// + /// let abcs = "abcdefg" + /// print(abcs[.last]) // Optional("g") + /// print(abcs[.last - 2]) // Optional("e") + /// print(abcs[.first + 8]) // nil + /// abcs[.first + 2] = "©" + /// print(abcs) // "ab©defg" + /// abcs[.last - 1] = nil + /// print(abcs) // "ab©deg" + /// + /// - Complexity (get): + /// - O(1) if the collection conforms to `RandomAccessCollection`. + /// - O(*k*) where *k* is equal to the offset if the collection conforms to + /// `BidirectionalCollection`. + /// - O(*k*) if `position` is `.first + n` for any n, or `.last + 1`. + /// - Otherwise, O(*n*) where *n* is the length of the collection. + /// + /// - Complexity (set): + /// - O(*n*) where *n* is the length of the collection. + public subscript(position: OffsetBound) -> Element? { get set } + + /// Accesses the contiguous subrange of elements corresponding to the provided + /// offsets. + /// + /// Example: + /// + /// var abcs = "abcdefg" + /// print(abcs[.first + 1 ..< .first + 6]) // "bcdef" + /// print(abcs[.first + 1 ..< .last - 1]) // "bcde" + /// abcs[.first ... .first + 3] = "🔡" + /// print(abcs) // "🔡efg" + /// + /// - Complexity (get): + /// - O(1) if the collection conforms to `RandomAccessCollection`. + /// - O(*k*) where *k* is equal to the larger offset if the collection + /// conforms to `BidirectionalCollection`. + /// - O(*k*) if the offsets are `.first + n` for any n or `.last + 1`. + /// - Otherwise, O(*n*) where *n* is the length of the collection. + /// + /// - Complexity (set): + /// - O(*n*) where *n* is the length of the collection. + public subscript( + range: ORE + ) -> SubSequence where ORE.Bound == OffsetBound { get set } +``` + + This proposal adds a new “internal” (i.e. underscored) customization hook to `Collection` to apply a reverse offset to a given index. Unlike `index(_:offsetBy:limitedBy:)` which will trap if the collection is not bidirectional, this will instead advance `startIndex`. ```swift @@ -368,6 +558,7 @@ extension BidirectionalCollection { } ``` + Change `Collection.suffix()` and `Collection.dropLast()` to use this hook, which will improve algorithmic complexity in generic code when the collection happens to be bidirectional. ```diff @@ -432,7 +623,6 @@ extension Collection { Finally, `BidirectionalCollection`’s overloads are obsoleted in new versions as they are fully redundant with `Collection`’s. - ## Source compatibility This change preserves source compatibility. @@ -447,10 +637,12 @@ All additions are versioned. The `_reverseOffsetIndex` customization hook is `@i ## Alternatives considered +### Add a `.start` and `.end` + +The previous version of this pitch also had a `.start` and `.end`, where `.start == .first && .end == .last + 1`. Having `.end` made it easier to refer to an open upper bound corresponding to the collection’s `endIndex`, which mostly manifests in documentation. However, in actual usage, `.first` and `.last` are almost always clearer. -### Drop `.last` and `.first` +We chose to eschew `.start` and `.end` members, simplifying the programming model and further distinguishing `OffsetBound` as an element-position abstraction more akin to working with `Collection.first/last` than `Collection.startIndex/endIndex`. -This proposal adds the static members `.start`, `.end`, `.first`, and `.last` to `OffsetBound`. The last two can be derived from the first two, but they can lend clarity to code. For example, sometimes “one before last” is easier to comprehend than “two from the end”, i.e. `.last - 1` and `.end - 2` respectively. `OffsetBound.first` is included for symmetry with `OffsetBound.last`, as well as symmetry with `Collection.first` and `Collection.last`, for which single-element subscript is an equivalent expression. ### Don’t add the customization hook @@ -461,13 +653,19 @@ This proposal adapts Collection’s `suffix` and `dropLast` to use the new hook, ### Offset Arbitrary Indices -More general than offsetting from the start or end of a Collection is offsetting from a given index. Relative indices, that is indices with offsets applied to them, could be expressed in a `RelativeBound` struct, where `Bound` is the index type of the collection it will be applied to (phantom-typed for pure-offset forms). The prior pitch proposed this feature alongside the `++`/`--` operators, but doing this presents problems in `RangeExpression` conformance as well as type-checker issues with generic operator overloading. +More general than offsetting from the beginning or end of a Collection is offsetting from a given index. Relative indices, that is indices with offsets applied to them, could be expressed in a `RelativeBound` struct, where `Bound` is the index type of the collection it will be applied to (phantom-typed for pure-offset forms). The prior pitch proposed this feature alongside the `++`/`--` operators, but doing this presents problems in `RangeExpression` conformance as well as type-checker issues with generic operator overloading. These issues can be worked around (as shown below), but each workaround comes with its own drawbacks. All in all, offsetting from an arbitrary index isn’t worth the tradeoffs and can be mimicked with slicing (albeit with more code). +#### The Trouble with Generic Operator Overloading + +Overloading an operator for a type with a generic parameter complicates type checking for that operator. The type checker has to open a type variable and associated constraints for that parameter, preventing the constraint system from being split. This increases the complexity of type checking *all* expressions involving that operator, not just those using the overload. + +This increased complexity may be tolerable for a brand new operator, such as the previously pitched `++` and `--`, but it is a downside of overloading an existing-but-narrowly-extended operator such as `..<` and `...`. It is a total non-starter for operators such as `+` and `-`, which already have complex resolution spaces. + #### The Trouble with RangeExpression -`RangeExpression` requires the ability to answer whether a range contains a bound, where `Bound` is constrained to be `Comparable`. Pure-offset ranges can answer this similarly to other partial ranges by putting the partial space between `.start` and `.end`. +`RangeExpression` requires the ability to answer whether a range contains a bound, where `Bound` is constrained to be `Comparable`. Pure-offset ranges can answer this similarly to other partial ranges by putting the partial space between `.first` and `.last`. Unlike pure-offset ranges, containment cannot be consistently answered for a range of relative indices without access to the original collection. Similarly, `RelativeBound` cannot be comparable. @@ -475,12 +673,6 @@ A workaround could be to introduce a new protocol `IndexRangeExpression` without This would also require a new `RelativeRange` type and new generic overloads for range operators `..<` and `...` producing it. This is a hefty amount of additional machinery and would complicate type checking of all ranges, as explained next. -#### The Trouble with Generic Operator Overloading - -Overloading an operator for a type with a generic parameter complicates type checking for that operator. The type checker has to open a type variable and associated constraints for that parameter, preventing the constraint system from being split. This increases the complexity of type checking *all* expressions involving that operator, not just those using the overload. - -This increased complexity may be tolerable for a brand new operator, such as the previously pitched `++` and `--`, but it is a downside of overloading an existing-but-narrowly-extended operator such as `..<` and `...`. It is a total non-starter for operators such as `+` and `-`, which already have complex resolution spaces. - ### Other Syntax @@ -494,7 +686,7 @@ An alternative syntax (prototyped in [this gist](https://gist.github.com/milsema ```swift // Proposed -"abc"[.start + 1 ..< .end - 1] // "b" +"abc"[.first + 1 ..< .last] // "b" // Offset label "abc"[offset: 1..<(-1)] // "b" @@ -502,7 +694,7 @@ An alternative syntax (prototyped in [this gist](https://gist.github.com/milsema Negative values meaning from-the-end is only a convention on top of literals, i.e. this would *not* provide wrap-around semantics for a value that happens to be negative. -In the end, we believe that the proposed syntax is more readable. There is less cognitive load in reading `.end - 1` than mentally mapping the literal convention of `-1`, and that literal convention would be inconsistent with run-time values. +In the end, we believe that the proposed syntax is more readable. There is less cognitive load in reading `.last` than mentally mapping the literal convention of `-1`, and that literal convention would be inconsistent with run-time values. #### No Syntax @@ -511,7 +703,7 @@ Alternative approaches include avoiding any syntax such as `+` or the use of the ```swift // Proposed -collection[.start + 5 ..< .end - 2] +collection[.first + 5 ..< .last - 1] // No ranges collection[fromStart: 5, upToEndOffsetBy: -2] @@ -560,7 +752,7 @@ for i in 0.. Date: Thu, 19 Sep 2019 14:40:47 -0500 Subject: [PATCH 1261/4563] Update proposals/0264-stdlib-preview-package.md Co-Authored-By: Ben Rimmington --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 14b2e75ffa..5bac21c61e 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -83,7 +83,7 @@ The introduction of the `SwiftPreview` package does not change much in the proce The main difference from the existing process is that the final result will become available for general use immediately in a new version of the preview package. As users have real-world experience with the accepted functionality, any proposed amendments to the proposal must go through the same evolution process as an original proposal. -To provide time for feedback from real-world use of new additions, the Swift Evoluton process should favor landing new features in a window immediately after branching for a major release. This doesn’t mean that important and valuable proposals can’t be added at different times, but they’ll be subject to increased scrutiny. +To provide time for feedback from real-world use of new additions, the Swift Evolution process should favor landing new features in a window immediately after branching for a major release. This doesn’t mean that important and valuable proposals can’t be added at different times, but they’ll be subject to increased scrutiny. No proposal should be accepted into the package on a provisional basis: Reviewers should assume that every proposal will be migrated as-is unless feedback reveals that it was a clear misadventure. The review manager for any revising proposals will be responsible for ensuring that the review discussion focuses on feedback from real-world use, and not relitigation of previously settled decisions made during the original review. From a095e485c15e914461e552cff3d06f1f47c5dbbf Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Thu, 19 Sep 2019 14:40:54 -0500 Subject: [PATCH 1262/4563] Update proposals/0264-stdlib-preview-package.md Co-Authored-By: Ben Rimmington --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 5bac21c61e..8362e59891 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -153,7 +153,7 @@ Because we do not have cross-module optimization, package implementations should ### Additional Review Before Library Integration -A previous version of this proposal prescribed a window of time before promoting accepted features into the standard library, to provide additional bake time and an additional review pointfor the feature. However, this approach delays some important feedback that can only be gained from integration into the standard library, where overload resolution and performance can have different outcomes than the same APIs shipped in a package. +A previous version of this proposal prescribed a window of time before promoting accepted features into the standard library, to provide additional bake time and an additional review point for the feature. However, this approach delays some important feedback that can only be gained from integration into the standard library, where overload resolution and performance can have different outcomes than the same APIs shipped in a package. ### No Semantic Versioning From 08d049b7286ca0d50be9ade22eae3ec5cb097f99 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Thu, 19 Sep 2019 14:41:04 -0500 Subject: [PATCH 1263/4563] Update proposals/0264-stdlib-preview-package.md Co-Authored-By: Ben Rimmington --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 8362e59891..d69e6fd610 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -157,6 +157,6 @@ A previous version of this proposal prescribed a window of time before promoting ### No Semantic Versioning -An alternative to keeping the semver major version at `0` is to not version the package at all. With this approach, the package could only be included by packages by specifying a branch-based dependendcy. However, this would be too restricting at this time, as a branch-based dependency can only be imported by other branch-based dependencies. This would effectively limit use of the `SwiftPreview` package to top-level applications only. +An alternative to keeping the semver major version at `0` is to not version the package at all. With this approach, the package could only be included by packages by specifying a branch-based dependency. However, this would be too restricting at this time, as a branch-based dependency can only be imported by other branch-based dependencies. This would effectively limit use of the `SwiftPreview` package to top-level applications only. From e559e935e24e45b0d753f5eae9984454aeb470ee Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Fri, 20 Sep 2019 13:50:20 -0700 Subject: [PATCH 1264/4563] Schedule Stdlib-preview-package --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index d69e6fd610..9697f6d0eb 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,7 +3,7 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) -* Status: **On hold awaiting revision by author** +* Status: **Review Scheduled (Sept 23...Sept 30, 2019)** * Pitch Discussion: [Pitch: Standard Library Preview Package](https://forums.swift.org/t/pitch-standard-library-preview-package/27202) ## Introduction From df7d32b712ffdb54f82c1d56d352327b4040ef76 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 23 Sep 2019 02:44:12 +0300 Subject: [PATCH 1265/4563] Update 0000-where-on-contextually-generic.md --- .../0000-where-on-contextually-generic.md | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index 2e0ed409cb..d894bbfb61 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -8,8 +8,8 @@ ## Introduction -The objective of this proposal is to lift the restriction on attaching `where` clauses to declarations that themselves -do not introduce type variables explicitly, but inherit the surrounding generic environment. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error within generic contexts. +The objective of the proposal is to lift the restriction on attaching `where` clauses to declarations that themselves +do not introduce new generic parameters, but inherit the surrounding generic environment. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error inside a generic context. ```swift struct Box { @@ -18,9 +18,9 @@ struct Box { ``` -> Only declarations that can already be generic and support being constrained via a conditional -> extension are covered by the described enhancement. Properties and new constraint kinds are out -> of scope for this document. For example, the following remains an error: +> Only declarations that already support genericity and being constrained via a conditional +> extension fall under this enhancement. Properties and hitherto unsupported constraint kinds are out +> of scope for the proposal. For instance, the following remains an error: > ```swift > protocol P { > // error: Instance method requirement 'foo(arg:)' cannot add constraint 'Self: Equatable' on 'Self' @@ -32,14 +32,11 @@ Swift-evolution thread: [Discussion thread topic for that proposal](https://foru ## Motivation -Today, `where` clauses on contextually generic declarations can only be expressed indirectly by placing them on conditional -extensions. Unless the constraints are identical, every such declaration requires a separate extension. This apparent -dependence on extensions is an obstacle to stacking up constraints or grouping semantically related APIs and usually -becomes a pain point in heavily generic code that unnecessarily complicates the structure of a program for the developer -and the compiler. +Today, `where` clauses on contextually generic declarations are expressed indirectly by placing them inside conditional extensions. Unless constraints are identical, every such declaration requires a separate extension. +This dependence on extensions can be an obstacle to grouping semantically related APIs, stacking up constraints and, +sometimes, the legibility of heavily generic interfaces. -Leaving ergonomic shortcomings behind, it is only natural for a `where` clause to work anywhere a constraint can be -meaningfully imposed, meaning both of these layout variants should be possible: +It is reasonable to expect a `where` clause to work anywhere a constraint can be meaningfully imposed, meaning both of these should be possible: ```swift struct Foo // 'Foo' can be any kind of nominal type declaration. For a protocol, 'T' would be an associatedtype. @@ -62,7 +59,7 @@ extension Foo where T: Sequence, T.Element: Equatable { func specialCaseFoo() where T.Element == Character { ... } } ``` -A move towards «untying» generic parameter lists and `where` clauses is an obvious and farsighted improvement to the generics +A step towards "untying" generic parameter lists and `where` clauses is an obvious and farsighted improvement to the generics system with numerous future applications, including [opaque types](https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md), [generalized existentials](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials) and constrained protocol requirements. From e72224466d7be8923eeea98df7d1d821c90ec2ad Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 23 Sep 2019 02:46:15 +0300 Subject: [PATCH 1266/4563] Update 0000-where-on-contextually-generic.md --- proposals/0000-where-on-contextually-generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index d894bbfb61..e4910a9e14 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -36,7 +36,7 @@ Today, `where` clauses on contextually generic declarations are expressed indire This dependence on extensions can be an obstacle to grouping semantically related APIs, stacking up constraints and, sometimes, the legibility of heavily generic interfaces. -It is reasonable to expect a `where` clause to work anywhere a constraint can be meaningfully imposed, meaning both of these should be possible: +It is reasonable to expect a `where` clause to work anywhere a constraint can be meaningfully imposed, meaning both of these structuring styles should be available to the user: ```swift struct Foo // 'Foo' can be any kind of nominal type declaration. For a protocol, 'T' would be an associatedtype. From ae1260c2634dbea3917357dc5ad8d1d78bda5b47 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 23 Sep 2019 16:37:35 +0300 Subject: [PATCH 1267/4563] Update 0000-where-on-contextually-generic.md --- proposals/0000-where-on-contextually-generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index e4910a9e14..9c87cc4199 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -8,7 +8,7 @@ ## Introduction -The objective of the proposal is to lift the restriction on attaching `where` clauses to declarations that themselves +This proposal is about lifting the restriction on attaching `where` clauses to declarations that themselves do not introduce new generic parameters, but inherit the surrounding generic environment. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error inside a generic context. ```swift From c7a62f3839182fef47bcdd5e14d92f4d15c1bea5 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Mon, 23 Sep 2019 07:03:34 -0700 Subject: [PATCH 1268/4563] Start review of 0264 --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 9697f6d0eb..cc42b1b0bb 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,7 +3,7 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) -* Status: **Review Scheduled (Sept 23...Sept 30, 2019)** +* Status: **Active Review (Sept 23...Sept 30, 2019)** * Pitch Discussion: [Pitch: Standard Library Preview Package](https://forums.swift.org/t/pitch-standard-library-preview-package/27202) ## Introduction From dc5e0fd032e220a3000a698010af88a7f14c597c Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Mon, 23 Sep 2019 07:04:50 -0700 Subject: [PATCH 1269/4563] Attempt to get NaNs off the dashboard Does the parser demand a year in each date endpoint? --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index cc42b1b0bb..1aded64aab 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,7 +3,7 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) -* Status: **Active Review (Sept 23...Sept 30, 2019)** +* Status: **Active Review (Sept 23, 2019...Sept 30, 2019)** * Pitch Discussion: [Pitch: Standard Library Preview Package](https://forums.swift.org/t/pitch-standard-library-preview-package/27202) ## Introduction From 8f13e276e42311bd20b058f6a63f1f9664477160 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Mon, 23 Sep 2019 07:08:08 -0700 Subject: [PATCH 1270/4563] Try again to fix the NaNs --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 1aded64aab..7e38fb3cc6 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,7 +3,7 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) -* Status: **Active Review (Sept 23, 2019...Sept 30, 2019)** +* Status: **Active Review (Sept 23...30, 2019)** * Pitch Discussion: [Pitch: Standard Library Preview Package](https://forums.swift.org/t/pitch-standard-library-preview-package/27202) ## Introduction From 7a299e194340166d4668c499152a2ac33e9d0642 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Mon, 23 Sep 2019 11:44:28 -0700 Subject: [PATCH 1271/4563] Spell out the full month name --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 7e38fb3cc6..83924df7ba 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,7 +3,7 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) -* Status: **Active Review (Sept 23...30, 2019)** +* Status: **Active Review (September 23...30, 2019)** * Pitch Discussion: [Pitch: Standard Library Preview Package](https://forums.swift.org/t/pitch-standard-library-preview-package/27202) ## Introduction From e25dba23e3551986a4c5ad01146b4104c466db05 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Wed, 25 Sep 2019 12:28:54 -0500 Subject: [PATCH 1272/4563] Create 0000-synthesized-comparable-for-enumerations.md (#1053) * Create 0000-synthesized-comparable-for-enumerations.md * Update 0000-synthesized-comparable-for-enumerations.md * implementation * monospace font * title case * un-sister * stdlib style * clarify detailed design * support associated values * Schedule review - tweak format to match standard library style - update SE link - set review date and manager --- ...synthesized-comparable-for-enumerations.md | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 proposals/0000-synthesized-comparable-for-enumerations.md diff --git a/proposals/0000-synthesized-comparable-for-enumerations.md b/proposals/0000-synthesized-comparable-for-enumerations.md new file mode 100644 index 0000000000..6aa7281e07 --- /dev/null +++ b/proposals/0000-synthesized-comparable-for-enumerations.md @@ -0,0 +1,146 @@ +# Synthesized `Comparable` conformance for simple `enum` types + +* Proposal: **SE-0265** +* Authors: **Kelvin Ma** (*[taylorswift](https://forums.swift.org/u/taylorswift/summary)*) +* Review manager: [Ben Cohen](https://github.com/airspeedswift/) +* Implementation: [`kelvin13:comparable-enums`](https://github.com/kelvin13/swift/tree/comparable-enums) +* Status: **Awaiting Review** (Sept 30 – Oct 9) + +## Introduction + +[SE-185](https://github.com/apple/swift-evolution/blob/master/proposals/0185-synthesize-equatable-hashable.md) introduced synthesized, opt-in `Equatable` and `Hashable` conformances for eligible types. Their sibling protocol `Comparable` was left out at the time, since it was less obvious what types ought to be eligible for a synthesized `Comparable` conformance and where a comparison order might be derived from. This proposal seeks to allow users to opt-in to synthesized `Comparable` conformances for `enum` types without raw values or associated values not themselves conforming to `Comparable`, a class of types which I believe make excellent candidates for this feature. The synthesized comparison order would be based on the declaration order of the `enum` cases, and then the lexicographic comparison order of the associated values for an `enum` case tie. + +## Motivation + +Oftentimes, you want to define an `enum` where the cases have an obvious semantic ordering: + +```swift +enum Membership { + case premium // < + case preferred // < + case general +} +``` +```swift +enum Brightness { + case low // < + case medium // < + case high +} +``` + +However, implementing it requires a lot of boilerplate code which is error-prone to write and difficult to maintain. Some commonly used workarounds include: + +* Declaring a raw `enum`, with an `Int` backing, and implementing the comparison using `self.rawValue`. This has the downside of associating and exposing a meaningless numeric value on your `enum` API, as well as requiring a copy-and-paste `<` implementation. Such an `enum` would also receive the built-in `init(rawValue:)` initializer, which may be unwanted. + +```swift +enum Membership: Int, Comparable { + case premium + case preferred + case general + + static func < (lhs: Self, rhs: Self) -> Bool { + return lhs.rawValue < rhs.rawValue + } +} +``` + +* Manually implementing the `<` operator with a private `minimum(_:_:)` helper function. This is the “proper” implementation, but is fairly verbose and error-prone to write, and does not scale well with more enumeration cases. + +```swift +enum Brightness: Comparable { + case low + case medium + case high + + private static func minimum(_ lhs: Self, _ rhs: Self) -> Self { + switch (lhs, rhs) { + case (.low, _), (_, .low ): + return .low + case (.medium, _), (_, .medium): + return .medium + case (.high, _), (_, .high ): + return .high + } + } + + static func < (lhs: Self, rhs: Self) -> Bool { + return (lhs != rhs) && (lhs == Self.minimum(lhs, rhs)) + } +} +``` + +As the second workaround is non-obvious to many, users also often attempt to implement “private” integer values for enumeration cases by manually numbering them. Needless to say, this approach scales very poorly, and incurs a high code maintenance cost as simple tasks like adding a new enumeration case require manually re-numbering all the other cases. Workarounds for the workaround, such as numbering by tens (to “make room” for future cases) or using `Double` as the key type (to allow [numbering “on halves”](https://youtu.be/KWcxgrg4eQI?t=113)) reflect poorly on the language. + +```swift +enum Membership: Comparable { + case premium + case preferred + case general + + private var comparisonValue: Int { + switch self { + case .premium: + return 0 + case .preferred: + return 1 + case .general: + return 2 + } + } + + static func < (lhs: Self, rhs: Self) -> Bool { + return lhs.comparisonValue < rhs.comparisonValue + } +} +``` + +## Proposed solution + +Enumeration types which opt-in to a synthesized `Comparable` conformance would compare according to case declaration order, with later cases comparing greater than earlier cases. Only `enum` types with no associated values and `enum` types with only `Comparable` associated values would be eligible for synthesized conformances. No `enum` types with raw values would qualify. + +While basing behavior off of declaration order is unusual for Swift, as we generally hew to the “all fields are reorderable by the compiler” principle, it is not a foreign concept to `enums`. For example, reordering cases in a numeric-backed raw `enum` already changes its runtime behavior, since the case declaration order is taken to be meaningful in that context. I also believe that `enum` cases and `struct`/`class` fields are sufficiently distinct concepts that making enumeration case order meaningful would not make the language incoherent. + +Later cases will compare greater than earlier cases, as Swift generally views sort orders to be “ascending” by default. It also harmonizes with the traditional C/C++ paradigm where a sequence of enumeration cases is merely a sequence of incremented integer values. + +## Detailed design + +Synthesized `Comparable` conformances for eligible types will work exactly the same as synthesized `Equatable`, `Hashable`, and `Codable` conformances today. A conformance will not be synthesized if a type is ineligible (has raw values or non recursively-conforming associated values) or already provides an explicit `<` implementation. + +```swift +enum Membership: Comparable { + case premium(Int) + case preferred + case general +} + +([.preferred, .premium(1), .general, .premium(0)] as [Membership]).sorted() +// [Membership.premium(0), Membership.premium(1), Membership.preferred, Membership.general] +``` + +## Source compatibility + +This feature is strictly additive. + +## ABI compatibility + +This feature does not affect the ABI. + +## API stability + +This feature does not affect the standard library. + +## Alternatives considered + +* Basing comparison order off of raw values or `RawRepresentable`. This alternative is inapplicable, as enumerations with “raw” representations don’t always have an obvious sort order anyway. Raw `String` backings are also commonly (ab)used for debugging and logging purposes making them a poor source of intent for a comparison-order definition. + +```swift +enum Month: String, Comparable { + case january + case february + case march + case april + ... +} +// do we compare alphabetically or by declaration order? +``` From 07b1820708c0a7a551a7cb7c97b0f0b7d40592cb Mon Sep 17 00:00:00 2001 From: Mattt Date: Wed, 25 Sep 2019 10:31:10 -0700 Subject: [PATCH 1273/4563] Ignore HTML, CSS, and JavaScript Files in Repository Language Determination (#1074) * Add .gitattributes Ignore HTML, CSS, and JavaScript files in repository language determination * Consider Markdown for repository language determination Remove directives to ignore HTML / CSS / JavaScript for repository language determination --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..a158744534 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.md linguist-detectable=true +*.md linguist-documentation=false From ecdf5f07904ff36cbf53241ccf61942279d2fab9 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 25 Sep 2019 13:36:04 -0400 Subject: [PATCH 1274/4563] Assign SE-0265 and schedule review --- ...g-and-slicing.md => 0265-offset-indexing-and-slicing.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{nnnn-offset-indexing-and-slicing.md => 0265-offset-indexing-and-slicing.md} (99%) diff --git a/proposals/nnnn-offset-indexing-and-slicing.md b/proposals/0265-offset-indexing-and-slicing.md similarity index 99% rename from proposals/nnnn-offset-indexing-and-slicing.md rename to proposals/0265-offset-indexing-and-slicing.md index ecba68a187..2ad4790daf 100644 --- a/proposals/nnnn-offset-indexing-and-slicing.md +++ b/proposals/0265-offset-indexing-and-slicing.md @@ -1,9 +1,9 @@ # Offset-Based Access to Indices, Elements, and Slices -* Proposal: SE-NNNN +* Proposal: SE-0265 * Author: [Michael Ilseman](https://github.com/milseman) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Awaiting review (October 7th – October 21st, 2019)** * Implementation: [apple/swift#24296](https://github.com/apple/swift/pull/24296) ## Introduction From 59b271b4b7d7a179a9523c865de27d65dd77248d Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 25 Sep 2019 13:37:31 -0400 Subject: [PATCH 1275/4563] editorial --- proposals/0265-offset-indexing-and-slicing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0265-offset-indexing-and-slicing.md b/proposals/0265-offset-indexing-and-slicing.md index 2ad4790daf..f80c7e2bc6 100644 --- a/proposals/0265-offset-indexing-and-slicing.md +++ b/proposals/0265-offset-indexing-and-slicing.md @@ -1,9 +1,9 @@ # Offset-Based Access to Indices, Elements, and Slices -* Proposal: SE-0265 +* Proposal: [SE-0265](0265-offset-indexing-and-slicing.md) * Author: [Michael Ilseman](https://github.com/milseman) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Awaiting review (October 7th – October 21st, 2019)** +* Status: **Awaiting review (October 7th – 21st, 2019)** * Implementation: [apple/swift#24296](https://github.com/apple/swift/pull/24296) ## Introduction From f6f6a2312ada202e5a4753b5239c6b70d74506a3 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 25 Sep 2019 19:17:45 +0100 Subject: [PATCH 1276/4563] [SE-0266] Update proposal ID (0265 is unavailable) --- ...=> 0266-synthesized-comparable-for-enumerations.md} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename proposals/{0000-synthesized-comparable-for-enumerations.md => 0266-synthesized-comparable-for-enumerations.md} (94%) diff --git a/proposals/0000-synthesized-comparable-for-enumerations.md b/proposals/0266-synthesized-comparable-for-enumerations.md similarity index 94% rename from proposals/0000-synthesized-comparable-for-enumerations.md rename to proposals/0266-synthesized-comparable-for-enumerations.md index 6aa7281e07..d99ac2a3fa 100644 --- a/proposals/0000-synthesized-comparable-for-enumerations.md +++ b/proposals/0266-synthesized-comparable-for-enumerations.md @@ -1,10 +1,10 @@ # Synthesized `Comparable` conformance for simple `enum` types -* Proposal: **SE-0265** -* Authors: **Kelvin Ma** (*[taylorswift](https://forums.swift.org/u/taylorswift/summary)*) -* Review manager: [Ben Cohen](https://github.com/airspeedswift/) -* Implementation: [`kelvin13:comparable-enums`](https://github.com/kelvin13/swift/tree/comparable-enums) -* Status: **Awaiting Review** (Sept 30 – Oct 9) +* Proposal: [SE-0266](0266-synthesized-comparable-for-enumerations.md) +* Author: [Kelvin Ma](https://forums.swift.org/u/taylorswift) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Scheduled for review (September 30...October 9)** +* Implementation: [apple/swift#25696](https://github.com/apple/swift/pull/25696) ## Introduction From c1b60f0b20caa3d2081ff3ce89b093cbbe2fe5aa Mon Sep 17 00:00:00 2001 From: Kyle Murray Date: Wed, 25 Sep 2019 13:34:38 -0700 Subject: [PATCH 1277/4563] [SE-0265] Update status to "scheduled". --- proposals/0265-offset-indexing-and-slicing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0265-offset-indexing-and-slicing.md b/proposals/0265-offset-indexing-and-slicing.md index f80c7e2bc6..38d54630ab 100644 --- a/proposals/0265-offset-indexing-and-slicing.md +++ b/proposals/0265-offset-indexing-and-slicing.md @@ -3,7 +3,7 @@ * Proposal: [SE-0265](0265-offset-indexing-and-slicing.md) * Author: [Michael Ilseman](https://github.com/milseman) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Awaiting review (October 7th – 21st, 2019)** +* Status: **Scheduled for review (October 7th – 21st, 2019)** * Implementation: [apple/swift#24296](https://github.com/apple/swift/pull/24296) ## Introduction From efa20d9f91ed51129c01c628f7aabf32a93bd94e Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 26 Sep 2019 02:55:54 +0100 Subject: [PATCH 1278/4563] [SE-0266] Restore username in Author field --- proposals/0266-synthesized-comparable-for-enumerations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0266-synthesized-comparable-for-enumerations.md b/proposals/0266-synthesized-comparable-for-enumerations.md index d99ac2a3fa..d5bb6a73dd 100644 --- a/proposals/0266-synthesized-comparable-for-enumerations.md +++ b/proposals/0266-synthesized-comparable-for-enumerations.md @@ -1,7 +1,7 @@ # Synthesized `Comparable` conformance for simple `enum` types * Proposal: [SE-0266](0266-synthesized-comparable-for-enumerations.md) -* Author: [Kelvin Ma](https://forums.swift.org/u/taylorswift) +* Author: [Kelvin Ma (taylorswift)](https://forums.swift.org/u/taylorswift) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Scheduled for review (September 30...October 9)** * Implementation: [apple/swift#25696](https://github.com/apple/swift/pull/25696) From 7a7be70965ac04c14eac812ddb765a93f291b156 Mon Sep 17 00:00:00 2001 From: David Smith Date: Thu, 26 Sep 2019 01:06:15 -0700 Subject: [PATCH 1279/4563] Rename as requested by the core team (cherry picked from commit d038fe39c6778e47c588f5468ff26d794fde7aa5) --- proposals/0263-string-uninitialized-initializer.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/0263-string-uninitialized-initializer.md b/proposals/0263-string-uninitialized-initializer.md index 61e82dea82..5fd703a4cb 100644 --- a/proposals/0263-string-uninitialized-initializer.md +++ b/proposals/0263-string-uninitialized-initializer.md @@ -27,7 +27,7 @@ storage, and returns the number of initialized buffer elements, or 0. ```swift let myCocoaString = NSString("The quick brown fox jumps over the lazy dog") as CFString -var myString = String(uninitializedCapacity: CFStringGetMaximumSizeForEncoding(myCocoaString, …)) { buffer in +var myString = String(unsafeUninitializedCapacity: CFStringGetMaximumSizeForEncoding(myCocoaString, …)) { buffer in var initializedCount = 0 CFStringGetBytes( myCocoaString, @@ -62,7 +62,7 @@ Without this initializer we would have had to heap allocate an `UnsafeMutableBuf /// sequences and the second with an ill-formed sequence at the end. /// /// let validUTF8: [UInt8] = [67, 97, 102, -61, -87, 0] - /// let s = String(uninitializedCapacity: validUTF8.count, + /// let s = String(unsafeUninitializedCapacity: validUTF8.count, /// initializingUTF8With: { ptr in /// ptr.initializeFrom(validUTF8) /// return validUTF8.count @@ -70,14 +70,14 @@ Without this initializer we would have had to heap allocate an `UnsafeMutableBuf /// // Prints "Café" /// /// let invalidUTF8: [UInt8] = [67, 97, 102, -61, 0] - /// let s = String(uninitializedCapacity: invalidUTF8.count, + /// let s = String(unsafeUninitializedCapacity: invalidUTF8.count, /// initializingUTF8With: { ptr in /// ptr.initializeFrom(invalidUTF8) /// return invalidUTF8.count /// }) /// // Prints "Caf�" /// - /// let s = String(uninitializedCapacity: invalidUTF8.count, + /// let s = String(unsafeUninitializedCapacity: invalidUTF8.count, /// initializingUTF8With: { ptr in /// ptr.initializeFrom(invalidUTF8) /// return 0 @@ -93,7 +93,7 @@ Without this initializer we would have had to heap allocate an `UnsafeMutableBuf /// - buffer: A buffer covering uninitialized memory with room for the /// specified number of UTF-8 code units. public init( - uninitializedCapacity capacity: Int, + unsafeUninitializedCapacity capacity: Int, initializingUTF8With initializer: ( _ buffer: UnsafeMutableBufferPointer, ) throws -> Int @@ -117,7 +117,7 @@ so there is no effect on source compatibility. ## Effect on ABI stability -The new initializer will be part of the ABI, and will result in calls to a new @usableFromInline symbol being inlined into client code. Use of the new initializer is gated by @availability though, so there's no back-deployment concern. +The new initializer will be part of the ABI, and may result in calls to a new @usableFromInline symbol being inlined into client code. Use of the new initializer is gated by @availability though, so there's no back-deployment concern. ## Effect on API resilience From 21e8cef3549f24402bf9778590406219eef21800 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Fri, 27 Sep 2019 01:01:50 +0300 Subject: [PATCH 1280/4563] Update 0000-where-on-contextually-generic.md --- proposals/0000-where-on-contextually-generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index 9c87cc4199..fb8d34434e 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -8,7 +8,7 @@ ## Introduction -This proposal is about lifting the restriction on attaching `where` clauses to declarations that themselves +This proposal aims to lift the (mostly artificial) restriction on attaching `where` clauses to declarations that themselves do not introduce new generic parameters, but inherit the surrounding generic environment. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error inside a generic context. ```swift From ea8a617a8c26c81def735a857a8096926d2fcc1a Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Fri, 27 Sep 2019 01:02:32 +0300 Subject: [PATCH 1281/4563] Update 0000-where-on-contextually-generic.md --- proposals/0000-where-on-contextually-generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index fb8d34434e..bc53fbb895 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -9,7 +9,7 @@ ## Introduction This proposal aims to lift the (mostly artificial) restriction on attaching `where` clauses to declarations that themselves -do not introduce new generic parameters, but inherit the surrounding generic environment. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error inside a generic context. +do not introduce new generic parameters, but inherit the surrounding generic environment. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error inside most generic contexts. ```swift struct Box { From 69c8513541b76813a8326d9ebb08c93b5dc87fd2 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Fri, 27 Sep 2019 01:06:20 +0300 Subject: [PATCH 1282/4563] Update 0000-where-on-contextually-generic.md --- proposals/0000-where-on-contextually-generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index bc53fbb895..8475da10ae 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -3,7 +3,7 @@ * Proposal: [SE-NNNN](NNNN-filename.md) * Authors: [Anthony Latsis](https://github.com/AnthonyLatsis) * Review Manager: TBD -* Status: **Ongoing Discussion** +* Status: **Awaiting a Review Manager** * Implementation: [apple/swift#23489](https://github.com/apple/swift/pull/23489) ## Introduction From d9c5c8dd113fb52ada4a11f1d72d78afe62a9f8a Mon Sep 17 00:00:00 2001 From: Ding Ye Date: Fri, 27 Sep 2019 10:44:55 +1000 Subject: [PATCH 1283/4563] Add an example usage to support queries of non-2 bases. --- proposals/0000-binaryinteger-ispower.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/0000-binaryinteger-ispower.md b/proposals/0000-binaryinteger-ispower.md index 94ed85dbd9..1b33092934 100644 --- a/proposals/0000-binaryinteger-ispower.md +++ b/proposals/0000-binaryinteger-ispower.md @@ -20,7 +20,9 @@ Checking whether an integer is power of a given base is a typical integral query Swift, as a general-purpose programming language, is also experiencing such demands covering a variety of domains, including numerical and mathematical libraries (e.g. [#1](https://github.com/jsbean/ArithmeticTools/blob/cb6dae327baf53cdf614d26e630833efa00eda3f/ArithmeticTools/IntegerExtensions.swift#L48), [#2](https://github.com/Orbifold/XAct/blob/9acad78e5571aa93fb52d88f539459effab1d5f7/XAct/Numbers.swift#L49), [#3](https://github.com/donald-pinckney/SwiftNum/blob/b92e3b964268ebf62d99f488fcdf438574974f0d/Sources/SignalProcessing/IntExtensions.swift#L12), [#4](https://github.com/dn-m/Math/blob/d1284e043377c0b924cba2ffa2ab0b9aa9dd246f/Sources/Math/IntegerExtensions.swift#L48)), programming language and compiler components (e.g. [#1](https://github.com/kai-language/kai/blob/41268660a01e0d6d1f0ac8de743d91700707135e/Sources/Core/Helpers/Helpers.swift#L205), [#2](https://github.com/llvm-swift/LLVMSwift/blob/162f1632e017349b17146e33c5905f88148e55f1/Sources/LLVM/Units.swift#L125)), image/video manipulation (e.g. [#1](https://github.com/schulz0r/CoreImage-HDR/blob/c7f5264929338beebfcfbf2e420594aa2952ef6c/CoreImage-HDR/Convenience/extensions.swift#L20), [#2](https://github.com/OpsLabJPL/MarsImagesIOS/blob/f2109f38b31bf1ad2e7b5aae6916da07d2d7d08e/MarsImagesIOS/Math.swift#L17), [#3](https://github.com/chingf/CarmenaCamera/blob/bfa928ca1595770c3b99bb8aa95dc5340a0f3284/VideoCapture/Common.swift#L22), [#4](https://github.com/dehancer/IMProcessing/blob/7a7d48edb7ceeb2635219c8139aa6fb8dbf1525d/IMProcessing/Classes/Common/IMPNumericTypes.swift#L132)), and other applications and utilities such as [geography kit](https://github.com/tokyovigilante/CesiumKit/blob/7983bd742a85982d9c3303cdacc039dcb44c8a42/CesiumKit/Core/Math.swift#L597), [quantum circuit simulator](https://github.com/indisoluble/SwiftQuantumComputing/blob/008e82e0f38792372df1a428884cccb74c2732b3/SwiftQuantumComputing/Extension/Int%2BIsPowerOfTwo.swift#L24), [tournament](https://github.com/eaheckert/Tournament/blob/c09c6b3634da9b2666b8b1f8990ff62bdc4fd625/Tournament/Tournament/ViewControllers/TCreateTournamentVC.swift#L215), [blockchain](https://github.com/yeeth/BeaconChain.swift/blob/954bcb6e47b51f90eff16818719320a228afe891/Sources/BeaconChain/Extensions/Int.swift#L5), [3D-engine](https://github.com/xuzhao-nick/3DEngine/blob/c3aab94f2bce5e29f7988b0d7c1e075d74076ad7/3DEngine/3DEngine/MoreMath.swift#L50), etc. -It would be beneficial if we have it supported by the standard library, and let us discuss the impacts in the following aspects: +Apart from the *is-power-of-two* usage, queries on *non-2* bases may also be practical, though they are much less common. In signal processing, for example, the efficient radix-4 algorithms can be applied when the FFT size is a power of 4. + +As a result, it would be beneficial if we could have such an API supported in the standard library. To be more specific, it is an extension method in the form of `isPower(of:)` to the `BinaryInteger` protocol, checking whether the `BinaryInteger` *self* is a power of the base specified by the parameter. Let us discuss the impacts in the following aspects: ### Readability @@ -266,4 +268,4 @@ This proposal has been greatly improved by the community. Below are some cases o - Michel Fortin provided an efficient solution to the fast path for base 10 (i.e. checking if an integer is power of ten), together with thorough explanation. - Jordan Rose gave prompt and continued comments on the PR, and advised the API should better be an extension method rather than a protocol requirement. - Erik Strottmann suggested a more appropriate naming `_isPowerOfTwo` instead of `_isPowerOf2`. - +- Antoine Coeur had valuable discussions. From 7f8bfb2ca62c9604c742ef318687ca1165b8c392 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 1 Oct 2019 11:48:32 +0100 Subject: [PATCH 1284/4563] [Status] Add Swift 5.2 to Language Version filter --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index a2c75b4496..1490806354 100644 --- a/index.js +++ b/index.js @@ -19,7 +19,7 @@ var proposals * To be updated when proposals are confirmed to have been implemented * in a new language version. */ -var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4', '4.1', '4.2', '5', '5.1'] +var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4', '4.1', '4.2', '5', '5.1', '5.2'] /** Storage for the user's current selection of filters when filtering is toggled off. */ var filterSelection = [] From 5108215de26b29214f2e850292715df58437a84a Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 1 Oct 2019 11:54:19 +0100 Subject: [PATCH 1285/4563] [SE-0253] Update status to implemented (Swift 5.2) --- proposals/0253-callable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0253-callable.md b/proposals/0253-callable.md index 80db02595a..a4c540af0b 100644 --- a/proposals/0253-callable.md +++ b/proposals/0253-callable.md @@ -3,7 +3,7 @@ * Proposal: [SE-0253](0253-callable.md) * Authors: [Richard Wei](https://github.com/rxwei), [Dan Zheng](https://github.com/dan-zheng) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Accepted with Revision** +* Status: **Implemented (Swift 5.2)** * Implementation: [apple/swift#24299](https://github.com/apple/swift/pull/24299) * Previous Revisions: [[1]](https://github.com/apple/swift-evolution/blob/36ea8be09508db9380949954d0c7a101fdb15226/proposals/0253-callable.md), From 9141c40101696261e05281acb4d83778d016076e Mon Sep 17 00:00:00 2001 From: Joseph Twomey Date: Tue, 1 Oct 2019 16:50:21 -0700 Subject: [PATCH 1286/4563] Scheduling SE-0263 for review. --- proposals/0263-string-uninitialized-initializer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0263-string-uninitialized-initializer.md b/proposals/0263-string-uninitialized-initializer.md index 5fd703a4cb..70eadf1720 100644 --- a/proposals/0263-string-uninitialized-initializer.md +++ b/proposals/0263-string-uninitialized-initializer.md @@ -3,7 +3,7 @@ * Proposal: [SE-0263](0263-string-uninitialized-initializer.md) * Author: [David Smith](https://github.com/Catfish-Man) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: [Returned for revision](https://forums.swift.org/t/se-0263-add-a-string-initializer-with-access-to-uninitialized-storage/27417/32) +* Status: **Scheduled for Review (October 8...October 11)** * Implementation: [apple/swift#23409](https://github.com/apple/swift/pull/23409) * Bug: [SR-10288](https://bugs.swift.org/browse/SR-10288) From cf59796a24cae0612daccba47fddc54aeb4fee11 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 1 Oct 2019 16:59:04 -0700 Subject: [PATCH 1287/4563] Update 0266-synthesized-comparable-for-enumerations.md --- proposals/0266-synthesized-comparable-for-enumerations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0266-synthesized-comparable-for-enumerations.md b/proposals/0266-synthesized-comparable-for-enumerations.md index d5bb6a73dd..0dc23ee60d 100644 --- a/proposals/0266-synthesized-comparable-for-enumerations.md +++ b/proposals/0266-synthesized-comparable-for-enumerations.md @@ -3,7 +3,7 @@ * Proposal: [SE-0266](0266-synthesized-comparable-for-enumerations.md) * Author: [Kelvin Ma (taylorswift)](https://forums.swift.org/u/taylorswift) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Scheduled for review (September 30...October 9)** +* Status: **Active Review (October 1...October 10)** * Implementation: [apple/swift#25696](https://github.com/apple/swift/pull/25696) ## Introduction From c1895a701c1bbba09ecec6351c5470647b3e2216 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 1 Oct 2019 17:02:37 -0700 Subject: [PATCH 1288/4563] Remove "simple" from SE-0266 since it now does associated values too --- proposals/0266-synthesized-comparable-for-enumerations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0266-synthesized-comparable-for-enumerations.md b/proposals/0266-synthesized-comparable-for-enumerations.md index 0dc23ee60d..23f24d9e6f 100644 --- a/proposals/0266-synthesized-comparable-for-enumerations.md +++ b/proposals/0266-synthesized-comparable-for-enumerations.md @@ -1,4 +1,4 @@ -# Synthesized `Comparable` conformance for simple `enum` types +# Synthesized `Comparable` conformance for `enum` types * Proposal: [SE-0266](0266-synthesized-comparable-for-enumerations.md) * Author: [Kelvin Ma (taylorswift)](https://forums.swift.org/u/taylorswift) From a4df77c75e9acc2d7ce243940e49ca31a23c7631 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 7 Oct 2019 11:59:09 -0400 Subject: [PATCH 1289/4563] Mark SE-0265 as being in active review --- proposals/0265-offset-indexing-and-slicing.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0265-offset-indexing-and-slicing.md b/proposals/0265-offset-indexing-and-slicing.md index 38d54630ab..c1557adfbf 100644 --- a/proposals/0265-offset-indexing-and-slicing.md +++ b/proposals/0265-offset-indexing-and-slicing.md @@ -3,8 +3,9 @@ * Proposal: [SE-0265](0265-offset-indexing-and-slicing.md) * Author: [Michael Ilseman](https://github.com/milseman) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Scheduled for review (October 7th – 21st, 2019)** +* Status: **Active review (October 7th – 21st, 2019)** * Implementation: [apple/swift#24296](https://github.com/apple/swift/pull/24296) +* Review: ([review](https://forums.swift.org/t/se-0265-offset-based-access-to-indices-elements-and-slices/29596)) ## Introduction From 8ef15b18fb45dcc22931e453a074da60b730f0bc Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Wed, 9 Oct 2019 07:06:20 -0700 Subject: [PATCH 1290/4563] Update 0263-string-uninitialized-initializer.md Resume review of SE-0263. --- proposals/0263-string-uninitialized-initializer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0263-string-uninitialized-initializer.md b/proposals/0263-string-uninitialized-initializer.md index 70eadf1720..e72d1c38b2 100644 --- a/proposals/0263-string-uninitialized-initializer.md +++ b/proposals/0263-string-uninitialized-initializer.md @@ -3,7 +3,7 @@ * Proposal: [SE-0263](0263-string-uninitialized-initializer.md) * Author: [David Smith](https://github.com/Catfish-Man) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Scheduled for Review (October 8...October 11)** +* Status: **Active Review (October 9...October 14)** * Implementation: [apple/swift#23409](https://github.com/apple/swift/pull/23409) * Bug: [SR-10288](https://bugs.swift.org/browse/SR-10288) From 4470c51a884ea4b03033ea533841457a830f19d7 Mon Sep 17 00:00:00 2001 From: David Smith Date: Wed, 9 Oct 2019 23:21:06 -0700 Subject: [PATCH 1291/4563] Restore missed change to SE-0263 --- proposals/0263-string-uninitialized-initializer.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/proposals/0263-string-uninitialized-initializer.md b/proposals/0263-string-uninitialized-initializer.md index e72d1c38b2..c194c0f67c 100644 --- a/proposals/0263-string-uninitialized-initializer.md +++ b/proposals/0263-string-uninitialized-initializer.md @@ -21,9 +21,8 @@ Add a new `String` initializer that lets a program work with an uninitialized buffer. The new initializer takes a closure that operates on an -`UnsafeMutableBufferPointer` and an `inout` count of initialized elements. This -closure has access to the uninitialized contents of the newly created String's -storage, and returns the number of initialized buffer elements, or 0. +`UnsafeMutableBufferPointer` referencing the uninitialized contents of the newly created +String's storage, and returns a count of initialized elements or 0. ```swift let myCocoaString = NSString("The quick brown fox jumps over the lazy dog") as CFString From 099e71b0550e51c0683b54d7f12ce1a2edafc7e5 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Fri, 11 Oct 2019 23:28:48 +0300 Subject: [PATCH 1292/4563] Update 0000-where-on-contextually-generic.md --- proposals/0000-where-on-contextually-generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index 8475da10ae..bf1c2493d9 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -8,7 +8,7 @@ ## Introduction -This proposal aims to lift the (mostly artificial) restriction on attaching `where` clauses to declarations that themselves +This proposal aims to lift the mostly artificial restriction on attaching `where` clauses to declarations that themselves do not introduce new generic parameters, but inherit the surrounding generic environment. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error inside most generic contexts. ```swift From 0ac14cc387df453207b3a6e48474722c56c0c443 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Fri, 11 Oct 2019 23:31:41 +0300 Subject: [PATCH 1293/4563] Update 0000-where-on-contextually-generic.md --- proposals/0000-where-on-contextually-generic.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0000-where-on-contextually-generic.md index bf1c2493d9..2e9863f638 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0000-where-on-contextually-generic.md @@ -8,8 +8,7 @@ ## Introduction -This proposal aims to lift the mostly artificial restriction on attaching `where` clauses to declarations that themselves -do not introduce new generic parameters, but inherit the surrounding generic environment. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error inside most generic contexts. +This proposal aims to lift the mostly artificial restriction on attaching `where` clauses to declarations that reference only outer generic parameters. Simply put, this means you no longer have to worry about the `'where' clause cannot be attached` error inside most generic contexts. ```swift struct Box { @@ -59,7 +58,7 @@ extension Foo where T: Sequence, T.Element: Equatable { func specialCaseFoo() where T.Element == Character { ... } } ``` -A step towards "untying" generic parameter lists and `where` clauses is an obvious and farsighted improvement to the generics +A step towards generalizing `where` clause usage is an obvious and farsighted improvement to the generics system with numerous future applications, including [opaque types](https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md), [generalized existentials](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials) and constrained protocol requirements. From 74b1185f65327d7c08a55438e63572ba4e7eea77 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Fri, 11 Oct 2019 14:45:19 -0700 Subject: [PATCH 1294/4563] Schedule #1011 for review as SE-0267 --- ...lly-generic.md => 0267-where-on-contextually-generic.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{0000-where-on-contextually-generic.md => 0267-where-on-contextually-generic.md} (95%) diff --git a/proposals/0000-where-on-contextually-generic.md b/proposals/0267-where-on-contextually-generic.md similarity index 95% rename from proposals/0000-where-on-contextually-generic.md rename to proposals/0267-where-on-contextually-generic.md index 2e9863f638..d584f6057a 100644 --- a/proposals/0000-where-on-contextually-generic.md +++ b/proposals/0267-where-on-contextually-generic.md @@ -1,9 +1,9 @@ # `where` clauses on contextually generic declarations -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0267](0267-filename.md) * Authors: [Anthony Latsis](https://github.com/AnthonyLatsis) -* Review Manager: TBD -* Status: **Awaiting a Review Manager** +* Review Manager: [Joe Groff](https://github.com/jckarter) +* Status: **Scheduled for review (October 21...October 31)** * Implementation: [apple/swift#23489](https://github.com/apple/swift/pull/23489) ## Introduction From 9c5985e86468df8a4950d3576f321522da9305f1 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 11 Oct 2019 23:03:21 +0100 Subject: [PATCH 1295/4563] [SE-0267] Fix proposal ID link to match filename --- proposals/0267-where-on-contextually-generic.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0267-where-on-contextually-generic.md b/proposals/0267-where-on-contextually-generic.md index d584f6057a..2bf132e4ad 100644 --- a/proposals/0267-where-on-contextually-generic.md +++ b/proposals/0267-where-on-contextually-generic.md @@ -1,7 +1,7 @@ # `where` clauses on contextually generic declarations -* Proposal: [SE-0267](0267-filename.md) -* Authors: [Anthony Latsis](https://github.com/AnthonyLatsis) +* Proposal: [SE-0267](0267-where-on-contextually-generic.md) +* Author: [Anthony Latsis](https://github.com/AnthonyLatsis) * Review Manager: [Joe Groff](https://github.com/jckarter) * Status: **Scheduled for review (October 21...October 31)** * Implementation: [apple/swift#23489](https://github.com/apple/swift/pull/23489) From 17095c83fc4dfa1afb58121069ee3847381b84fd Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 15 Oct 2019 18:58:52 -0700 Subject: [PATCH 1296/4563] Update 0266-synthesized-comparable-for-enumerations.md --- proposals/0266-synthesized-comparable-for-enumerations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0266-synthesized-comparable-for-enumerations.md b/proposals/0266-synthesized-comparable-for-enumerations.md index 23f24d9e6f..e60b89a55f 100644 --- a/proposals/0266-synthesized-comparable-for-enumerations.md +++ b/proposals/0266-synthesized-comparable-for-enumerations.md @@ -3,7 +3,7 @@ * Proposal: [SE-0266](0266-synthesized-comparable-for-enumerations.md) * Author: [Kelvin Ma (taylorswift)](https://forums.swift.org/u/taylorswift) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (October 1...October 10)** +* Status: **Acceptedx** * Implementation: [apple/swift#25696](https://github.com/apple/swift/pull/25696) ## Introduction From 69bb477889ed42d4001ebc5dd9894fda68168bb8 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 15 Oct 2019 18:59:24 -0700 Subject: [PATCH 1297/4563] Update 0266-synthesized-comparable-for-enumerations.md --- proposals/0266-synthesized-comparable-for-enumerations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0266-synthesized-comparable-for-enumerations.md b/proposals/0266-synthesized-comparable-for-enumerations.md index e60b89a55f..833af82c46 100644 --- a/proposals/0266-synthesized-comparable-for-enumerations.md +++ b/proposals/0266-synthesized-comparable-for-enumerations.md @@ -3,7 +3,7 @@ * Proposal: [SE-0266](0266-synthesized-comparable-for-enumerations.md) * Author: [Kelvin Ma (taylorswift)](https://forums.swift.org/u/taylorswift) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Acceptedx** +* Status: **Accepted** * Implementation: [apple/swift#25696](https://github.com/apple/swift/pull/25696) ## Introduction From 1547e503376bca2c64c57c96b1f87d5e01a094c3 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Wed, 16 Oct 2019 09:34:14 -0700 Subject: [PATCH 1298/4563] SE-0264 is returned for revision --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 83924df7ba..9fe3edc28e 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,7 +3,7 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) -* Status: **Active Review (September 23...30, 2019)** +* Status: **[Returned for Revision](https://forums.swift.org/t/returned-for-revision-se-0264-standard-library-preview-package/29865)** * Pitch Discussion: [Pitch: Standard Library Preview Package](https://forums.swift.org/t/pitch-standard-library-preview-package/27202) ## Introduction From dfaee0e2f612b66962090507253c90e23fb58750 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 18 Oct 2019 09:25:03 -0500 Subject: [PATCH 1299/4563] Switch status format --- proposals/0264-stdlib-preview-package.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 9fe3edc28e..f9aec08c43 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,8 +3,9 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) -* Status: **[Returned for Revision](https://forums.swift.org/t/returned-for-revision-se-0264-standard-library-preview-package/29865)** +* Status: **Returned for Revision** * Pitch Discussion: [Pitch: Standard Library Preview Package](https://forums.swift.org/t/pitch-standard-library-preview-package/27202) +* Decision Notes: [Returned for Revision](https://forums.swift.org/t/returned-for-revision-se-0264-standard-library-preview-package/29865) ## Introduction From 1623125ea53386e1e7ae994391f0dd01aee77a14 Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Sat, 19 Oct 2019 01:41:08 +0330 Subject: [PATCH 1300/4563] added number of proposals on filter --- index.js | 53 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 1490806354..c7216ad9d8 100644 --- a/index.js +++ b/index.js @@ -35,62 +35,75 @@ var REPO_PROPOSALS_BASE_URL = GITHUB_BASE_URL + 'apple/swift-evolution/blob/mast * * `className`: Mapping of states in the proposals JSON to the CSS class names used * to manipulate and display proposals based on their status. + * + * `count`: Number of proposals that determine after all proposals is loaded */ var states = { '.awaitingReview': { name: 'Awaiting Review', shortName: 'Awaiting Review', - className: 'awaiting-review' + className: 'awaiting-review', + count:0 }, '.scheduledForReview': { name: 'Scheduled for Review', shortName: 'Scheduled', - className: 'scheduled-for-review' + className: 'scheduled-for-review', + count:0 }, '.activeReview': { name: 'Active Review', shortName: 'Active Review', - className: 'active-review' + className: 'active-review', + count:0 }, '.returnedForRevision': { name: 'Returned for Revision', shortName: 'Returned', - className: 'returned-for-revision' + className: 'returned-for-revision', + count:0 }, '.withdrawn': { name: 'Withdrawn', shortName: 'Withdrawn', - className: 'withdrawn' + className: 'withdrawn', + count:0 }, '.deferred': { name: 'Deferred', shortName: 'Deferred', - className: 'deferred' + className: 'deferred', + count:0 }, '.accepted': { name: 'Accepted', shortName: 'Accepted', - className: 'accepted' + className: 'accepted', + count:0 }, '.acceptedWithRevisions': { name: 'Accepted with revisions', shortName: 'Accepted', - className: 'accepted-with-revisions' + className: 'accepted-with-revisions', + count:0 }, '.rejected': { name: 'Rejected', shortName: 'Rejected', - className: 'rejected' + className: 'rejected', + count:0 }, '.implemented': { name: 'Implemented', shortName: 'Implemented', - className: 'implemented' + className: 'implemented', + count:0 }, '.error': { name: 'Error', shortName: 'Error', - className: 'error' + className: 'error', + count:0 } } @@ -114,6 +127,9 @@ function init () { }) proposals = proposals.reverse() + // for display on filter + determineNumberOfProposals(proposals) + render() addEventListeners() @@ -173,6 +189,16 @@ function html (elementType, attributes, children) { return element } +function determineNumberOfProposals(proposals) { + proposals.forEach(function (proposal) { + states[proposal.status.state].count += 1 + }); + + // .acceptedWithRevisions proposals are combined in the filtering UI + // with .accepted proposals. + states['.accepted'].count += states['.acceptedWithRevisions'].count +} + /** * Adds the dynamic portions of the page to the DOM, primarily the list * of proposals and list of statuses used for filtering. @@ -201,7 +227,7 @@ function renderNav () { return html('li', null, [ html('input', { type: 'checkbox', className: 'filtered-by-status', id: 'filter-by-' + className, value: className }), html('label', { className: className, tabindex: '0', role: 'button', 'for': 'filter-by-' + className }, [ - states[state].name + states[state].name + ' (' + states[state].count + ')' ]) ]) }) @@ -982,6 +1008,9 @@ function updateFilterDescription (selectedStateNames) { } else if (selectedStateNames.length === 0) { container.innerText = 'All Statuses' } else { + selectedStateNames = selectedStateNames.map(function (selectedStateName) { + return selectedStateName.replace(/ *\([^)]*\) */g, '') + }) container.innerText = selectedStateNames.join(' or ') } } From c9b396672a4c96605d8cc8f0af9ae17eba5c26c7 Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Sat, 19 Oct 2019 01:43:32 +0330 Subject: [PATCH 1301/4563] clean code --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index c7216ad9d8..04055f8764 100644 --- a/index.js +++ b/index.js @@ -189,7 +189,7 @@ function html (elementType, attributes, children) { return element } -function determineNumberOfProposals(proposals) { +function determineNumberOfProposals (proposals) { proposals.forEach(function (proposal) { states[proposal.status.state].count += 1 }); From 9dd913a66177b553f62126c25886d91b6e07a305 Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Sat, 19 Oct 2019 03:10:56 +0330 Subject: [PATCH 1302/4563] added space after colon --- index.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/index.js b/index.js index 04055f8764..2f07851831 100644 --- a/index.js +++ b/index.js @@ -43,67 +43,67 @@ var states = { name: 'Awaiting Review', shortName: 'Awaiting Review', className: 'awaiting-review', - count:0 + count: 0 }, '.scheduledForReview': { name: 'Scheduled for Review', shortName: 'Scheduled', className: 'scheduled-for-review', - count:0 + count: 0 }, '.activeReview': { name: 'Active Review', shortName: 'Active Review', className: 'active-review', - count:0 + count: 0 }, '.returnedForRevision': { name: 'Returned for Revision', shortName: 'Returned', className: 'returned-for-revision', - count:0 + count: 0 }, '.withdrawn': { name: 'Withdrawn', shortName: 'Withdrawn', className: 'withdrawn', - count:0 + count: 0 }, '.deferred': { name: 'Deferred', shortName: 'Deferred', className: 'deferred', - count:0 + count: 0 }, '.accepted': { name: 'Accepted', shortName: 'Accepted', className: 'accepted', - count:0 + count: 0 }, '.acceptedWithRevisions': { name: 'Accepted with revisions', shortName: 'Accepted', className: 'accepted-with-revisions', - count:0 + count: 0 }, '.rejected': { name: 'Rejected', shortName: 'Rejected', className: 'rejected', - count:0 + count: 0 }, '.implemented': { name: 'Implemented', shortName: 'Implemented', className: 'implemented', - count:0 + count: 0 }, '.error': { name: 'Error', shortName: 'Error', className: 'error', - count:0 + count: 0 } } From 84cc2f002093692afa8102742b3bfb525c46cfcd Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Sat, 19 Oct 2019 04:12:28 +0330 Subject: [PATCH 1303/4563] update number of proposals with search --- index.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 2f07851831..eab8426e35 100644 --- a/index.js +++ b/index.js @@ -190,6 +190,11 @@ function html (elementType, attributes, children) { } function determineNumberOfProposals (proposals) { + // reset count + Object.keys(states).forEach(function (state){ + states[state].count = 0 + }) + proposals.forEach(function (proposal) { states[proposal.status.state].count += 1 }); @@ -226,7 +231,7 @@ function renderNav () { return html('li', null, [ html('input', { type: 'checkbox', className: 'filtered-by-status', id: 'filter-by-' + className, value: className }), - html('label', { className: className, tabindex: '0', role: 'button', 'for': 'filter-by-' + className }, [ + html('label', { className: className, tabindex: '0', role: 'button', 'for': 'filter-by-' + className, 'data-state-key': state }, [ states[state].name + ' (' + states[state].count + ')' ]) ]) @@ -234,7 +239,7 @@ function renderNav () { var expandableArea = html('div', { className: 'filter-options expandable' }, [ html('h5', { id: 'filter-options-label' }, 'Status'), - html('ul', { className: 'filter-by-status' }) + html('ul', { id: 'filter-options', className: 'filter-by-status' }) ]) nav.querySelector('.nav-contents').appendChild(expandableArea) @@ -670,6 +675,9 @@ function filterProposals () { _applyFilter(intersection) _updateURIFragment() + + determineNumberOfProposals(intersection) + updatedFilterStatus(); } /** @@ -789,7 +797,7 @@ function _applyFilter (matchingProposals) { filteredElements.forEach(function (element) { element.classList.add('hidden') }) }) - updateProposalsCount(matchingProposals.length) + updateProposalsCount(matchingProposals.length) } /** @@ -1020,3 +1028,10 @@ function updateProposalsCount (count) { var numberField = document.querySelector('#proposals-count-number') numberField.innerText = (count.toString() + ' proposal' + (count !== 1 ? 's' : '')) } + +function updatedFilterStatus () { + var labels = [].concat.apply([], document.querySelector('#filter-options').querySelectorAll('label')) + labels.forEach(function (label) { + label.innerText = label.innerText.replace(/ *\([^)]*\) */g, '') + ' (' + states[label.getAttribute('data-state-key')].count + ')' + }) +} \ No newline at end of file From 64816389370df89f38faf70dbfa27545b8546d2b Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Sat, 19 Oct 2019 04:14:50 +0330 Subject: [PATCH 1304/4563] clean code --- index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index eab8426e35..51c72c3d1c 100644 --- a/index.js +++ b/index.js @@ -1017,7 +1017,7 @@ function updateFilterDescription (selectedStateNames) { container.innerText = 'All Statuses' } else { selectedStateNames = selectedStateNames.map(function (selectedStateName) { - return selectedStateName.replace(/ *\([^)]*\) */g, '') + return cleanNumberFromState(selectedStateName) }) container.innerText = selectedStateNames.join(' or ') } @@ -1032,6 +1032,10 @@ function updateProposalsCount (count) { function updatedFilterStatus () { var labels = [].concat.apply([], document.querySelector('#filter-options').querySelectorAll('label')) labels.forEach(function (label) { - label.innerText = label.innerText.replace(/ *\([^)]*\) */g, '') + ' (' + states[label.getAttribute('data-state-key')].count + ')' + label.innerText = cleanNumberFromState(label.innerText) + ' (' + states[label.getAttribute('data-state-key')].count + ')' }) +} + +function cleanNumberFromState (state) { + return state.replace(/ *\([^)]*\) */g, '') } \ No newline at end of file From 91b2347a410bf6894be4eac96e72e2af70cef11c Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Sat, 19 Oct 2019 04:23:27 +0330 Subject: [PATCH 1305/4563] clean code --- index.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 51c72c3d1c..341f2c8e15 100644 --- a/index.js +++ b/index.js @@ -232,7 +232,7 @@ function renderNav () { return html('li', null, [ html('input', { type: 'checkbox', className: 'filtered-by-status', id: 'filter-by-' + className, value: className }), html('label', { className: className, tabindex: '0', role: 'button', 'for': 'filter-by-' + className, 'data-state-key': state }, [ - states[state].name + ' (' + states[state].count + ')' + addNumberToState(states[state].name, states[state].count) ]) ]) }) @@ -1016,9 +1016,7 @@ function updateFilterDescription (selectedStateNames) { } else if (selectedStateNames.length === 0) { container.innerText = 'All Statuses' } else { - selectedStateNames = selectedStateNames.map(function (selectedStateName) { - return cleanNumberFromState(selectedStateName) - }) + selectedStateNames = selectedStateNames.map(cleanNumberFromState) container.innerText = selectedStateNames.join(' or ') } } @@ -1032,10 +1030,16 @@ function updateProposalsCount (count) { function updatedFilterStatus () { var labels = [].concat.apply([], document.querySelector('#filter-options').querySelectorAll('label')) labels.forEach(function (label) { - label.innerText = cleanNumberFromState(label.innerText) + ' (' + states[label.getAttribute('data-state-key')].count + ')' + var count = states[label.getAttribute('data-state-key')].count + var cleanedLabel = cleanNumberFromState(label.innerText) + label.innerText = addNumberToState(cleanedLabel, count) }) } function cleanNumberFromState (state) { return state.replace(/ *\([^)]*\) */g, '') +} + +function addNumberToState (state, count) { + return state + ' (' + count + ')' } \ No newline at end of file From a1c621b5320127e59202e64cd04e0914bcd8e16c Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Sat, 19 Oct 2019 04:25:30 +0330 Subject: [PATCH 1306/4563] removed space --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 341f2c8e15..80c5fd7a7d 100644 --- a/index.js +++ b/index.js @@ -797,7 +797,7 @@ function _applyFilter (matchingProposals) { filteredElements.forEach(function (element) { element.classList.add('hidden') }) }) - updateProposalsCount(matchingProposals.length) + updateProposalsCount(matchingProposals.length) } /** From aea78fd7fbe04259bef54859a429012b827477c1 Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Sat, 19 Oct 2019 04:28:49 +0330 Subject: [PATCH 1307/4563] remvoe additional call function --- index.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/index.js b/index.js index 80c5fd7a7d..dd076cf429 100644 --- a/index.js +++ b/index.js @@ -125,10 +125,7 @@ function init () { proposals.sort(function compareProposalIDs (p1, p2) { return parseInt(p1.id.match(/\d\d\d\d/)[0]) - parseInt(p2.id.match(/\d\d\d\d/)[0]) }) - proposals = proposals.reverse() - - // for display on filter - determineNumberOfProposals(proposals) + proposals = proposals.reverse() render() addEventListeners() From 876d1799226ae893d04c941a78859a9a453841c4 Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Sat, 19 Oct 2019 05:03:05 +0330 Subject: [PATCH 1308/4563] removed space --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index dd076cf429..840769052c 100644 --- a/index.js +++ b/index.js @@ -125,7 +125,7 @@ function init () { proposals.sort(function compareProposalIDs (p1, p2) { return parseInt(p1.id.match(/\d\d\d\d/)[0]) - parseInt(p2.id.match(/\d\d\d\d/)[0]) }) - proposals = proposals.reverse() + proposals = proposals.reverse() render() addEventListeners() From 27b442b8907350ce4b3bf1a5f303e0d0edd756f0 Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Sat, 19 Oct 2019 05:12:53 +0330 Subject: [PATCH 1309/4563] remove semicolons --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 840769052c..4bdb473b3d 100644 --- a/index.js +++ b/index.js @@ -194,7 +194,7 @@ function determineNumberOfProposals (proposals) { proposals.forEach(function (proposal) { states[proposal.status.state].count += 1 - }); + }) // .acceptedWithRevisions proposals are combined in the filtering UI // with .accepted proposals. @@ -674,7 +674,7 @@ function filterProposals () { _updateURIFragment() determineNumberOfProposals(intersection) - updatedFilterStatus(); + updatedFilterStatus() } /** From 95a507db15c84305040a92d07ffa88f24bee8f85 Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Sat, 19 Oct 2019 06:06:23 +0330 Subject: [PATCH 1310/4563] clean code --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 4bdb473b3d..d5b8117e4c 100644 --- a/index.js +++ b/index.js @@ -1025,7 +1025,7 @@ function updateProposalsCount (count) { } function updatedFilterStatus () { - var labels = [].concat.apply([], document.querySelector('#filter-options').querySelectorAll('label')) + var labels = [].concat.apply([], document.querySelectorAll('#filter-options label')) labels.forEach(function (label) { var count = states[label.getAttribute('data-state-key')].count var cleanedLabel = cleanNumberFromState(label.innerText) From e9912d2f78bb7d025b40f686083d424473f1088d Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Mon, 21 Oct 2019 17:21:34 +0100 Subject: [PATCH 1311/4563] Add the didSet Semantics proposal (#1068) * Squash changes into single commit * Kick off review --- proposals/0268-didset-semantics.md | 168 +++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 proposals/0268-didset-semantics.md diff --git a/proposals/0268-didset-semantics.md b/proposals/0268-didset-semantics.md new file mode 100644 index 0000000000..abb9d458bf --- /dev/null +++ b/proposals/0268-didset-semantics.md @@ -0,0 +1,168 @@ +# didSet Semantics + +* Proposal: [SE-0268](0268-didset-semantics.md) +* Author: [Suyash Srijan](https://www.github.com/theblixguy) +* Review Manager: [Ben Cohen](https://www.github.com/airspeedswift) +* Status: **Active Review (21-31 October 2019)** +* Implementation: [apple/swift#26632](https://github.com/apple/swift/pull/26632) + +## Introduction + +Introduce two changes to `didSet` semantics - + +1. If a `didSet` observer does not reference the `oldValue` in its body, then the call to fetch the `oldValue` will be skipped. We refer to this as a "simple" didSet. +2. If we have a "simple" `didSet` and no `willSet`, then we could allow modifications to happen in-place. + +Swift-evolution thread: [didSet Semantics](https://forums.swift.org/t/pitch-didset-semantics/27858) + +## Motivation + +Currently, Swift always calls the property's getter to get the `oldValue` if we have a `didSet` observer, even if the observer does not refer to the `oldValue` in its body. For example: + +```swift +class Foo { + var bar: Int { + didSet { print("didSet called") } + } + + init(bar: Int) { self.bar = bar } +} + +let foo = Foo(bar: 0) +// This calls the getter on 'bar' to get +// the 'oldValue', even though we never +// refer to the oldValue inside bar's 'didSet' +foo.bar = 1 +``` + +This might look harmless, but it is doing redundant work (by allocating storage and loading a value which isn't used). It could also be expensive if the getter performs some non-trivial task and/or returns a large value. + +For example: + +```swift +struct Container { + var items: [Int] = .init(repeating: 1, count: 100) { + didSet { + // Do some stuff, but don't access oldValue + } + } + + mutating func update() { + for index in 0.. { + var wrappedValue: Value { + get { + guard let value = value else { + preconditionFailure("Property \(String(describing: self)) has not been set yet") + } + return value + } + + set { + guard value == nil else { + preconditionFailure("Property \(String(describing: self)) has already been set") + } + value = newValue + } + } + + private var value: Value? +} + +class Foo { + @Delayed var bar: Int { + didSet { print("didSet called") } + } +} + +let foo = Foo() +foo.bar = 1 +``` + +However, this code will currently crash when we set `bar`'s value to be `1`. This is because Swift will fetch the `oldValue`, which is `nil` initially and thus will trigger the precondition in the getter. + +## Proposed Solution + +The property's getter is no longer called if we do not refer to the `oldValue` inside the body of the `didSet`. + +```swift +class Foo { + var bar = 0 { + didSet { print("didSet called") } + } + + var baz = 0 { + didSet { print(oldValue) } + } +} + +let foo = Foo() +// This will not call the getter to fetch the oldValue +foo.bar = 1 +// This will call the getter to fetch the oldValue +foo.baz = 2 +``` + +This also resolves some pending bugs such as [SR-11297](https://bugs.swift.org/browse/SR-11297), [SR-11280](https://bugs.swift.org/browse/SR-11280) and [SR-5982](https://bugs.swift.org/browse/SR-5982). + +As a bonus, if the property has a "simple" `didSet` and no `willSet`, then we could allow for modifications to happen in-place. For example: + +```swift +// This is how we currently synthesize the _modify coroutine +_modify { + var newValue = underlyingStorage + yield &newValue + // Call the setter, which then calls + // willSet (if present) and didSet + observedStorage = newValue +} + +// This is how we're going to synthesize it instead +_modify { + // Since we don't have a willSet and + // we have a "simple" didSet, we can + // yield the storage directly and + // call didSet + yield &underlyingStorage + didSet() +} +``` + +This will provide a nice performance boost in some cases (for example, in the earlier array copying example). + +## Source compatibility + +This does not break source compatibility, _unless_ someone is explicitly relying on the current buggy behavior (i.e. the property's getter being called even if the `oldValue` isn't referenced). However, I think the possibility of that is very small. + +## Effect on ABI stability + +This does not affect the ABI as observers are not a part of it. + +## Effect on API resilience + +This does not affect API resilience - library authors can freely switch between a `didSet` which does not refer to the `oldValue` in its body and one which does and freely add or remove `didSet` from the property. + +## Alternatives considered + +Leave the existing behavior as is. + +## Future Directions + +We can apply the same treatment to `willSet` i.e. not pass the `newValue` if it does not refer to it in its body, although it wouldn't provide any real benefit as not passing `newValue` to `willSet` does not avoid anything, where as not passing `oldValue` to `didSet` avoids loading it. + +We can also deprecate the implicit `oldValue` and request users to explicitly provide `oldValue` in parenthesis (`didSet(oldValue) { ... }`) if they want to use it in the body of the observer. This will make the new behavior more obvious and self-documenting. From cc2806be1831097b9e53925a42290ba720353c91 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 21 Oct 2019 09:22:53 -0700 Subject: [PATCH 1312/4563] Update 0268-didset-semantics.md --- proposals/0268-didset-semantics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0268-didset-semantics.md b/proposals/0268-didset-semantics.md index abb9d458bf..88a0fb7cb7 100644 --- a/proposals/0268-didset-semantics.md +++ b/proposals/0268-didset-semantics.md @@ -1,4 +1,4 @@ -# didSet Semantics +# Redfine `didSet` Semantics * Proposal: [SE-0268](0268-didset-semantics.md) * Author: [Suyash Srijan](https://www.github.com/theblixguy) From d83afb4d1f75ebcf23836a0c85a6f70bbc480638 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 21 Oct 2019 10:03:45 -0700 Subject: [PATCH 1313/4563] Kick off SE-267 --- proposals/0267-where-on-contextually-generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0267-where-on-contextually-generic.md b/proposals/0267-where-on-contextually-generic.md index 2bf132e4ad..4f50636512 100644 --- a/proposals/0267-where-on-contextually-generic.md +++ b/proposals/0267-where-on-contextually-generic.md @@ -3,7 +3,7 @@ * Proposal: [SE-0267](0267-where-on-contextually-generic.md) * Author: [Anthony Latsis](https://github.com/AnthonyLatsis) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Scheduled for review (October 21...October 31)** +* Status: **Active review (October 21...October 31)** * Implementation: [apple/swift#23489](https://github.com/apple/swift/pull/23489) ## Introduction From ecf7b782b07e55a3a58ac1f320806c696a229918 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 21 Oct 2019 18:05:29 +0100 Subject: [PATCH 1314/4563] [SE-0268] Fix typo --- proposals/0268-didset-semantics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0268-didset-semantics.md b/proposals/0268-didset-semantics.md index 88a0fb7cb7..8975b4d5e0 100644 --- a/proposals/0268-didset-semantics.md +++ b/proposals/0268-didset-semantics.md @@ -1,4 +1,4 @@ -# Redfine `didSet` Semantics +# Refine `didSet` Semantics * Proposal: [SE-0268](0268-didset-semantics.md) * Author: [Suyash Srijan](https://www.github.com/theblixguy) From a7943c647a109f34a6b2e98384402a867efa419c Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Mon, 21 Oct 2019 12:45:08 -0700 Subject: [PATCH 1315/4563] Mark SE-0263 as accepted. --- proposals/0263-string-uninitialized-initializer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0263-string-uninitialized-initializer.md b/proposals/0263-string-uninitialized-initializer.md index c194c0f67c..e0f7bed605 100644 --- a/proposals/0263-string-uninitialized-initializer.md +++ b/proposals/0263-string-uninitialized-initializer.md @@ -3,7 +3,7 @@ * Proposal: [SE-0263](0263-string-uninitialized-initializer.md) * Author: [David Smith](https://github.com/Catfish-Man) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active Review (October 9...October 14)** +* Status: **Accepted** * Implementation: [apple/swift#23409](https://github.com/apple/swift/pull/23409) * Bug: [SR-10288](https://bugs.swift.org/browse/SR-10288) From d7ed96701e59b2fe3ebe3a68a40acc96f89943da Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Tue, 22 Oct 2019 19:00:37 +0300 Subject: [PATCH 1316/4563] Minor rewordings and clarifications --- .../0267-where-on-contextually-generic.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/proposals/0267-where-on-contextually-generic.md b/proposals/0267-where-on-contextually-generic.md index 4f50636512..69070f5042 100644 --- a/proposals/0267-where-on-contextually-generic.md +++ b/proposals/0267-where-on-contextually-generic.md @@ -17,8 +17,8 @@ struct Box { ``` -> Only declarations that already support genericity and being constrained via a conditional -> extension fall under this enhancement. Properties and hitherto unsupported constraint kinds are out +> Only declarations that already support a generic parameter list and being constrained via a conditional +> extension fall under this enhancement. Properties and hitherto unsupported constraint *kinds* are out > of scope for the proposal. For instance, the following remains an error: > ```swift > protocol P { @@ -26,19 +26,27 @@ struct Box { > func foo() where Self: Equatable > } > ``` +> Whereas placing a constraint on an extension member rather than the extension itself becomes possible: +> ```swift +> extension P { +> func bar() where Self: Equatable { } +> } +> ``` Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/where-clauses-on-contextually-generic-declaractions/22449) ## Motivation -Today, `where` clauses on contextually generic declarations are expressed indirectly by placing them inside conditional extensions. Unless constraints are identical, every such declaration requires a separate extension. +Today, `where` clauses on contextually generic declarations, including protocol extension members, are expressed indirectly by placing them inside conditional extensions. Unless constraints are identical, every such declaration requires a separate extension. This dependence on extensions can be an obstacle to grouping semantically related APIs, stacking up constraints and, sometimes, the legibility of heavily generic interfaces. It is reasonable to expect a `where` clause to work anywhere a constraint can be meaningfully imposed, meaning both of these structuring styles should be available to the user: ```swift -struct Foo // 'Foo' can be any kind of nominal type declaration. For a protocol, 'T' would be an associatedtype. +// 'Foo' can be any kind of nominal type declaration. +// For a protocol, 'T' would be an associatedtype. +struct Foo extension Foo where T: Sequence, T.Element: Equatable { func slowFoo() { ... } @@ -68,5 +76,4 @@ This is an additive change with no impact on the ABI and existing code. ## Effect on API resilience -For public declarations in resilient libraries, switching between a constrained extension and a «direct» `where` clause -will not be a source-breaking change, but it most likely will break the ABI due to subtle mangling differences. +For public declarations in resilient libraries, moving a constraint from an extension to a member and vice versa will not be a source-breaking change, but will break the ABI due to subtle mangling differences. From 2efe59e5bc0e5856681cf3f0ccee68f186892d98 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Tue, 22 Oct 2019 19:05:14 +0300 Subject: [PATCH 1317/4563] Update 0267-where-on-contextually-generic.md --- proposals/0267-where-on-contextually-generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0267-where-on-contextually-generic.md b/proposals/0267-where-on-contextually-generic.md index 69070f5042..e4e9e580b4 100644 --- a/proposals/0267-where-on-contextually-generic.md +++ b/proposals/0267-where-on-contextually-generic.md @@ -45,7 +45,7 @@ It is reasonable to expect a `where` clause to work anywhere a constraint can be ```swift // 'Foo' can be any kind of nominal type declaration. -// For a protocol, 'T' would be an associatedtype. +// For a protocol, 'T' would be Self or an associatedtype. struct Foo extension Foo where T: Sequence, T.Element: Equatable { From 72b1a567e65d0d1106df0642677ab167d1f40c7c Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Thu, 24 Oct 2019 13:10:38 +0300 Subject: [PATCH 1318/4563] [SE-0267] Some more legibility improvements --- proposals/0267-where-on-contextually-generic.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/proposals/0267-where-on-contextually-generic.md b/proposals/0267-where-on-contextually-generic.md index e4e9e580b4..1f9bc54708 100644 --- a/proposals/0267-where-on-contextually-generic.md +++ b/proposals/0267-where-on-contextually-generic.md @@ -17,9 +17,9 @@ struct Box { ``` -> Only declarations that already support a generic parameter list and being constrained via a conditional -> extension fall under this enhancement. Properties and hitherto unsupported constraint *kinds* are out -> of scope for the proposal. For instance, the following remains an error: +> Only declarations that already support a generic parameter list will be allowed to carry a `where` clause inside a generic +> context. Generic properties and constraints on protocol requirements are out of scope for the current proposal. +> For instance, the following remains an error: > ```swift > protocol P { > // error: Instance method requirement 'foo(arg:)' cannot add constraint 'Self: Equatable' on 'Self' @@ -38,10 +38,9 @@ Swift-evolution thread: [Discussion thread topic for that proposal](https://foru ## Motivation Today, `where` clauses on contextually generic declarations, including protocol extension members, are expressed indirectly by placing them inside conditional extensions. Unless constraints are identical, every such declaration requires a separate extension. -This dependence on extensions can be an obstacle to grouping semantically related APIs, stacking up constraints and, -sometimes, the legibility of heavily generic interfaces. +This dependence on extensions can be an obstacle to grouping semantically related APIs, stacking up constraints, and sometimes the legibility of heavily generic interfaces. -It is reasonable to expect a `where` clause to work anywhere a constraint can be meaningfully imposed, meaning both of these structuring styles should be available to the user: +It is reasonable to expect a `where` clause to work anywhere a constraint can be meaningfully imposed, that is, both of these structuring styles should be available to the user: ```swift // 'Foo' can be any kind of nominal type declaration. @@ -76,4 +75,4 @@ This is an additive change with no impact on the ABI and existing code. ## Effect on API resilience -For public declarations in resilient libraries, moving a constraint from an extension to a member and vice versa will not be a source-breaking change, but will break the ABI due to subtle mangling differences. +For public declarations in resilient libraries such as the Standard Library, moving a constraint from an extension to a member and vice versa will break the ABI due to subtle mangling differences as of the current implementation. From 9850ba8c2456c4cb3bdc085586c201c09773a66d Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Thu, 24 Oct 2019 17:04:38 +0300 Subject: [PATCH 1319/4563] [SE-0267] Mention generic properties as a future direction --- proposals/0267-where-on-contextually-generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0267-where-on-contextually-generic.md b/proposals/0267-where-on-contextually-generic.md index 1f9bc54708..5fe93a429c 100644 --- a/proposals/0267-where-on-contextually-generic.md +++ b/proposals/0267-where-on-contextually-generic.md @@ -66,7 +66,7 @@ extension Foo where T: Sequence, T.Element: Equatable { } ``` A step towards generalizing `where` clause usage is an obvious and farsighted improvement to the generics -system with numerous future applications, including [opaque types](https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md), [generalized +system with numerous future applications, including generic properties, [opaque types](https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md), [generalized existentials](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials) and constrained protocol requirements. ## Source compatibility and ABI stability From 69778a88ceb5a2d0da73ddc2e257398e053f1016 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 30 Oct 2019 01:20:45 -0400 Subject: [PATCH 1320/4563] Add proposal for allowing implicit self in more circumstances (#1024) * Add proposal for allowing implicit self when captured explicitly * Update proposal for recent changes, add to motivation section * Fix detailed design section * Update with new diagnostic messages * Update proposal for current state of implementation * Update NNNN-implicit-self-explicit-capture.md * Add rules for value types * Merge remote branch * Fix typo Co-Authored-By: Ben Rimmington * Refer to self having value type instead of value semantics Co-Authored-By: Dave Abrahams * Refer to self having value type instead of value semantics Co-Authored-By: Dave Abrahams * Add apostrophe Co-Authored-By: Dave Abrahams * Improve wording for first section of proposed solution Co-Authored-By: Dave Abrahams * Refer to self having value type instead of value semantics Co-Authored-By: Dave Abrahams * Address review comments --- .../NNNN-implicit-self-explicit-capture.md | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 proposals/NNNN-implicit-self-explicit-capture.md diff --git a/proposals/NNNN-implicit-self-explicit-capture.md b/proposals/NNNN-implicit-self-explicit-capture.md new file mode 100644 index 0000000000..a08fd0de9f --- /dev/null +++ b/proposals/NNNN-implicit-self-explicit-capture.md @@ -0,0 +1,195 @@ +# Increase availability of implicit `self` in `@escaping` closures when reference cycles are unlikely to occur + +* Proposal: [SE-NNNN](NNNN-implicit-self-explicit-capture.md) +* Authors: [Frederick Kellison-Linn](https://github.com/jumhyn) +* Review Manager: TBD +* Status: **Awaiting Review** +* Implementation: [apple/swift#23934](https://github.com/apple/swift/pull/23934/) +* Bug: [SR-10218](https://bugs.swift.org/browse/SR-10218) +* Forum threads: [Discussion](https://forums.swift.org/t/review-capture-semantics-of-self/22017), [Pitch](https://forums.swift.org/t/allow-implicit-self-in-escaping-closures-when-self-is-explicitly-captured/22590) + +## Introduction + +Modify the rule that all uses of `self` in escaping closures must be explicit by allowing for implicit uses of `self` in situations where the user has already made their intent explicit, or where strong reference cycles are otherwise unlikely to occur. There are two situations covered by this proposal. The first is when the user has explicitly captured `self` in the closure's capture list, so that the following would compile without error: + +```swift +class Test { + var x = 0 + func execute(_ work: @escaping () -> Void) { + work() + } + func method() { + execute { [self] in + x += 1 + } + } +} +``` + +Secondly, this proposal would make implicit `self` available in escaping closures when `self` is a value type, so that the following would become valid: + +```swift +struct Test { + var x = 0 + func execute(_ work: @escaping () -> Void) { + work() + } + func method() { + execute { + x += 1 + } + } +} +``` + +## Motivation + +In order to prevent users from inadvertently creating retain cycles, the Swift compiler today requires all uses of `self` in escaping closures to be explicit. Attempting to reference a member `x` of `self` without the `self` keyword gives the error: + +``` +error: reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit +``` + +In codebases that choose to omit `self` where possible, this can result in a lot of unwanted noise, if many properties of `self` are used in a row: + +```swift +execute { + let foo = self.doFirstThing() + performWork(with: self.bar) + self.doSecondThing(with: foo) + self.cleanup() +} +``` + +It also results in a lot of unnecessary repetition. The motivation for requiring explicit usage of `self` is to force the user to make the intent to capture `self` explicit, but that goal is accomplished after the first explicit usage of `self` and is not furthered by any of the subsequent usages. + +In codebases that make heavy use of asynchronous code, such as clients of [PromiseKit](https://github.com/mxcl/PromiseKit), or even Apple's new [Combine](https://developer.apple.com/documentation/combine) and [SwiftUI](https://developer.apple.com/documentation/swiftui) libraries, maintainers must either choose to adopt style guides which require the use of explicit `self` in all cases, or else resign to the reality that explicit usage of `self` will appear inconsistently throughout the code. Given that Apple's preferred/recommended style is to omit `self` where possible (as evidenced by examples throughout [The Swift Programming Language](https://docs.swift.org/swift-book/) and the [SwiftUI Tutorials](https://developer.apple.com/tutorials/swiftui)), having any asynchronous code littered with `self.` is a suboptimal state of affairs. + +The error is also overly conservative—it will prevent the usage of implicit `self` even when it is very unlikely that the capture of `self` will somehow cause a reference cycle. With function builders in SwiftUI, the automatic resolution to the "make capture semantics explicit" error (and indeed, the fix-it that is currently supplied to the programmer) is just to slap a `self.` on wherever the compiler tells you to. While this likely fine when `self` is a value type, building this habit could cause users to ignore the error and apply the fix-it in cases where it is not in fact appropriate. + +There are also cases that are just as (if not more) likely to cause reference cycles that the tool currently misses. Once such instance is discussed in **Future Directions** below. These false negatives are not addressed in this proposal, but improving the precision of this diagnostic will make the tool more powerful and less likely to be dismissed if (or when) new diagnostic cases are introduced. + +## Proposed solution + +First, allow the use of implicit `self` when it appears in the closure's capture list. The above code could then be written as: + +```swift +execute { [self] in + let foo = doFirstThing() + performWork(with: bar) + doSecondThing(with: foo) + cleanup() +} +``` + +This change still forces code which captures `self` to be explicit about its intentions, but reduces the cost of that explicitness to a single declaration. With this change explicit capture of `self` would be one of *two* ways to get rid of the error, with the current method of adding `self.` to each property/method access (without adding `self` to the capture list) remaining as a viable option. + +The compiler would also offer an additional fix-it when implicit `self` is used: + +```swift +execute { // <- Fix-it: capture 'self' explicitly to enable implicit 'self' in this closure. Fix-it: insert '[self] in' + let foo = doFirstThing() + performWork(with: bar) + doSecondThing(with: foo) + cleanup() +} +``` + +Second, if `self` is a value type, we will not require any explicit usage of `self` (at the call/use site or in the capture list), so that if `self` were a `struct` or `enum` then the above could be written as simply: + +```swift +execute { + let foo = doFirstThing() + performWork(with: bar) + doSecondThing(with: foo) + cleanup() +} +``` + +## Detailed design + +Whenever `self` is declared explicitly in an escaping closure's capture list, or its type is a value type, any code inside that closure can use names which resolve to members of the enclosing type, without specifying `self.` explicitly. In nested closures, the *innermost* escaping closure must capture `self`, so the following code would be invalid: + +```swift +execute { [self] in + execute { // Fix-it: capture 'self' explicitly to enable implicit 'self' in this closure. + x += 1 // Error: reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit. Fix-it: reference 'self.' explicitly. + } +} +``` + +This new behavior will also be available for closures with `unowned(safe)` and `unowned(unsafe)` captures of `self` since the programmer has clearly declared their intent to capture `self` (as for strong captures). For `weak` captures of `self`, it's not immediately clear what bare reference to an instance property even *means* when the local `self` is bound weakly—should it be treated as though the programmer had written `self?.`? Should there be a special syntax available in this context, such as `?.x`? Should we offer a diagnostic offering to insert `self?.`? Or should we instead suggest that the programmer re-bind `self` strongly via a `guard` statement? There are enough open questions here that have not been sufficiently discussed, so this proposal leaves the handling of `weak self` captures as a future direction worthy of further consideration. + +The existing errors and fix-its have their language updated accordingly to indicate that there are now multiple ways to resolve the error. In addition to the changes noted above, we will also have: + +``` +Error: call to method in closure requires explicit use of 'self' to make capture semantics explicit. +``` + +The new fix-it for explicitly capturing `self` will take slightly different forms depending on whether there is a capture list already present ("insert '`self, `'"), whether there are explicit parameters ("insert '`[self]`'"), and whether the user the user has already captured a variable with the name `self` (in which case the fix-it would not be offered). Since empty capture lists are allowed (`{ [] in ... }`), the fix-it will cover this case too. + +This new rule would only apply when `self` is captured directly, and with the name `self`. This includes captures of the form `[self = self]` but would still not permit implicit `self` if the capture were `[y = self]`. In the unusual event that the user has captured another variable with the name `self` (e.g. `[self = "hello"]`), we will offer a note that this does not enable use of implicit `self` (in addition to the existing error attached to the attempted use of implicit `self`): + +```swift +Note: variable other than 'self' captured here under the name 'self' does not enable implicit 'self'. +``` + +If the user has a capture of `weak self` already, we offer a special diagnostic note indicating that `weak` captures of `self` do not enable implicit `self`: + +```swift +Note: weak capture of 'self' here does not enable implicit 'self'. +``` + +If either of the two above notes are present, we will not offer the usual fix-its for resolving this error, since the code inserted would be erroneous. + +## Source compatibility + +This proposal makes previously illegal syntax legal, and has no effect on source compatibility. + +## Effect on ABI stability + +This is an entirely frontend change and has no effect on ABI stability. + +## Effect on API resilience + +This is not an API-level change, and has no effect on API resilience. + +## Future Directions + +### Bound method references + +While this proposal opens up implicit `self` in situations where we can be reasonably sure that we will not cause a reference cycle, there are other cases where implicit `self` is currently allowed that we may want to disallow in the future. One of these is allowing bound method references to be passed into escaping contexts without making it clear that such a reference captures `self`. For example: + +```swift +class Test { + var x = 0 + func execute(_ work: @escaping () -> Void) { + work() + } + func method() { + execute(inc) // self is captured, but no error! + execute { inc() } // error + } + func inc() { + x += 1 + } +} +``` + +This would help reduce the false negatives of the "make capture semantics explicit" error, making it more useful and hopefully catching reference cycles that the programmer was previously unaware of. + +### Weak captures of `self` + +It might be desirable to add improved diagnostic information (or similarly do away with the error) in the case where the programmer has explicitly captured `self`, but only as a weak reference. For the reasons noted in this proposal, this was not pursued, but answering the lingering questions and issuing a follow-up proposal would fit nicely with this direction. + +## Alternatives considered + +### Always require `self` in the capture list + +The rule requiring the use of explicit `self` is helpful when the code base does not already require `self` to be used on all instance accesses. However, many code bases have linters or style guides that require `self` to be used explicitly always, making the capture semantics opaque. Always requiring `self` to be captured in the capture list explicitly would ensure that there are no `self` captures that the programmer is unaware of, even if they naturally use `self` for instance accesses. This would be a more drastic, source breaking change (and is not ruled out by adopting this change), so it was not seriously pursued as part of this proposal. + +### Eliminate the former fix-it + +A less extreme solution to the problem described above is to simply stop offering the current fix-it that suggests adding the explicit `self.` at the point of reference in favor of only recommending the explicit capture list fix-it, when possible. + + From 637d6d28347e8214d9debb16d60cb10ce7717c90 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Tue, 29 Oct 2019 22:22:57 -0700 Subject: [PATCH 1321/4563] Schedule review for SE-0269 --- ...icit-capture.md => 0269-implicit-self-explicit-capture.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{NNNN-implicit-self-explicit-capture.md => 0269-implicit-self-explicit-capture.md} (98%) diff --git a/proposals/NNNN-implicit-self-explicit-capture.md b/proposals/0269-implicit-self-explicit-capture.md similarity index 98% rename from proposals/NNNN-implicit-self-explicit-capture.md rename to proposals/0269-implicit-self-explicit-capture.md index a08fd0de9f..f9ba38d04c 100644 --- a/proposals/NNNN-implicit-self-explicit-capture.md +++ b/proposals/0269-implicit-self-explicit-capture.md @@ -2,8 +2,8 @@ * Proposal: [SE-NNNN](NNNN-implicit-self-explicit-capture.md) * Authors: [Frederick Kellison-Linn](https://github.com/jumhyn) -* Review Manager: TBD -* Status: **Awaiting Review** +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) +* Status: **Scheduled for review (October 31 – November 8, 2019)** * Implementation: [apple/swift#23934](https://github.com/apple/swift/pull/23934/) * Bug: [SR-10218](https://bugs.swift.org/browse/SR-10218) * Forum threads: [Discussion](https://forums.swift.org/t/review-capture-semantics-of-self/22017), [Pitch](https://forums.swift.org/t/allow-implicit-self-in-escaping-closures-when-self-is-explicitly-captured/22590) From 35537af5e63a140c93521d78fdac3c9e2d9ad349 Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Wed, 30 Oct 2019 14:32:00 +0000 Subject: [PATCH 1322/4563] [SE-0268] Add some clarifications to the proposal text based on feedback on the review thread (#1082) --- proposals/0268-didset-semantics.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/proposals/0268-didset-semantics.md b/proposals/0268-didset-semantics.md index 8975b4d5e0..7429adedc1 100644 --- a/proposals/0268-didset-semantics.md +++ b/proposals/0268-didset-semantics.md @@ -81,7 +81,7 @@ struct Delayed { } } - private var value: Value? + var value: Value? } class Foo { @@ -118,6 +118,8 @@ foo.bar = 1 foo.baz = 2 ``` +This applies to a `didSet` on an overridden property as well - the call to the superclass getter will be skipped if the `oldValue` is not referenced in the body of the overridden property's `didSet`. + This also resolves some pending bugs such as [SR-11297](https://bugs.swift.org/browse/SR-11297), [SR-11280](https://bugs.swift.org/browse/SR-11280) and [SR-5982](https://bugs.swift.org/browse/SR-5982). As a bonus, if the property has a "simple" `didSet` and no `willSet`, then we could allow for modifications to happen in-place. For example: @@ -149,6 +151,25 @@ This will provide a nice performance boost in some cases (for example, in the ea This does not break source compatibility, _unless_ someone is explicitly relying on the current buggy behavior (i.e. the property's getter being called even if the `oldValue` isn't referenced). However, I think the possibility of that is very small. +However, it is possible to preserve the old behavior by either: + +1. Explicitly providing the `oldValue` argument to `didSet`: +```swift +didSet(oldValue) { + // The getter is called to fetch + // the oldValue, even if it's not + // used in this body. +} +``` +2. Forcing the getter to be called by simply ignoring its value in the body of the `didSet`: +```swift +didSet { + // Calls the getter, but the value + // is ignored. + _ = oldValue +} +``` + ## Effect on ABI stability This does not affect the ABI as observers are not a part of it. @@ -159,7 +180,9 @@ This does not affect API resilience - library authors can freely switch between ## Alternatives considered -Leave the existing behavior as is. +- Explicitly require an `oldValue` parameter to use it, such as `didSet(oldValue) { ... }`, otherwise it is an error to use `oldValue` in the `didSet` body. This will be a big source breaking change. It will also cause a regression in usability and create an inconsistency with other accessors, such as `willSet` or `set`, which can be declared with or without an explicit parameter. The source compatibility problem can be mitigated by deprecating the use of implicit `oldValue` and then making it an error in the next language version, however the usability regression would remain. +- Introduce a new `didSet()` syntax that will surpress the read of the `oldValue` (and it will be an error to use `oldValue` in the `didSet` body). This will prevent any breakage since it's an additive change, but will reduce the positive performance gain (of not calling the getter when `oldValue` is not used) to zero unless people opt-in to the new syntax. Similar to the previous solution, it will create an inconsistency in the language, since it will be the only accessor that can be declared with an empty parameter list and will become yet another thing to explain to a newcomer. +- Leave the existing behavior as is. ## Future Directions From e9703b5d248f120af559dbc21801c4d24c49e10f Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Wed, 30 Oct 2019 10:14:29 -0700 Subject: [PATCH 1323/4563] Adjust representation of range of review dates. --- proposals/0269-implicit-self-explicit-capture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0269-implicit-self-explicit-capture.md b/proposals/0269-implicit-self-explicit-capture.md index f9ba38d04c..2c6768a640 100644 --- a/proposals/0269-implicit-self-explicit-capture.md +++ b/proposals/0269-implicit-self-explicit-capture.md @@ -3,7 +3,7 @@ * Proposal: [SE-NNNN](NNNN-implicit-self-explicit-capture.md) * Authors: [Frederick Kellison-Linn](https://github.com/jumhyn) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Scheduled for review (October 31 – November 8, 2019)** +* Status: **Scheduled for review (October 31...November 8, 2019)** * Implementation: [apple/swift#23934](https://github.com/apple/swift/pull/23934/) * Bug: [SR-10218](https://bugs.swift.org/browse/SR-10218) * Forum threads: [Discussion](https://forums.swift.org/t/review-capture-semantics-of-self/22017), [Pitch](https://forums.swift.org/t/allow-implicit-self-in-escaping-closures-when-self-is-explicitly-captured/22590) From 453eee2ae34846b0a4c68b351ca600f37d34e4d4 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Wed, 30 Oct 2019 11:51:33 -0700 Subject: [PATCH 1324/4563] Further tweak representation of scheduled for review --- proposals/0269-implicit-self-explicit-capture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0269-implicit-self-explicit-capture.md b/proposals/0269-implicit-self-explicit-capture.md index 2c6768a640..e575bda3ef 100644 --- a/proposals/0269-implicit-self-explicit-capture.md +++ b/proposals/0269-implicit-self-explicit-capture.md @@ -3,7 +3,7 @@ * Proposal: [SE-NNNN](NNNN-implicit-self-explicit-capture.md) * Authors: [Frederick Kellison-Linn](https://github.com/jumhyn) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Scheduled for review (October 31...November 8, 2019)** +* Status: **Scheduled for review (October 31...November 8)** * Implementation: [apple/swift#23934](https://github.com/apple/swift/pull/23934/) * Bug: [SR-10218](https://bugs.swift.org/browse/SR-10218) * Forum threads: [Discussion](https://forums.swift.org/t/review-capture-semantics-of-self/22017), [Pitch](https://forums.swift.org/t/allow-implicit-self-in-escaping-closures-when-self-is-explicitly-captured/22590) From a620fa1a20ea93616ddb13e2afbcafcecc70af60 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 30 Oct 2019 23:02:54 +0000 Subject: [PATCH 1325/4563] [SE-0269] Fix proposal ID link to match filename --- proposals/0269-implicit-self-explicit-capture.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0269-implicit-self-explicit-capture.md b/proposals/0269-implicit-self-explicit-capture.md index e575bda3ef..f12943f6da 100644 --- a/proposals/0269-implicit-self-explicit-capture.md +++ b/proposals/0269-implicit-self-explicit-capture.md @@ -1,10 +1,10 @@ # Increase availability of implicit `self` in `@escaping` closures when reference cycles are unlikely to occur -* Proposal: [SE-NNNN](NNNN-implicit-self-explicit-capture.md) -* Authors: [Frederick Kellison-Linn](https://github.com/jumhyn) +* Proposal: [SE-0269](0269-implicit-self-explicit-capture.md) +* Author: [Frederick Kellison-Linn](https://github.com/jumhyn) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Scheduled for review (October 31...November 8)** -* Implementation: [apple/swift#23934](https://github.com/apple/swift/pull/23934/) +* Implementation: [apple/swift#23934](https://github.com/apple/swift/pull/23934) * Bug: [SR-10218](https://bugs.swift.org/browse/SR-10218) * Forum threads: [Discussion](https://forums.swift.org/t/review-capture-semantics-of-self/22017), [Pitch](https://forums.swift.org/t/allow-implicit-self-in-escaping-closures-when-self-is-explicitly-captured/22590) From 32c7a40435d5c2290c30d5afa84eef2c50e0041a Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Thu, 31 Oct 2019 14:55:21 -0700 Subject: [PATCH 1326/4563] Initiate review for SE-0269 --- proposals/0269-implicit-self-explicit-capture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0269-implicit-self-explicit-capture.md b/proposals/0269-implicit-self-explicit-capture.md index f12943f6da..11cbf713f6 100644 --- a/proposals/0269-implicit-self-explicit-capture.md +++ b/proposals/0269-implicit-self-explicit-capture.md @@ -3,7 +3,7 @@ * Proposal: [SE-0269](0269-implicit-self-explicit-capture.md) * Author: [Frederick Kellison-Linn](https://github.com/jumhyn) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Scheduled for review (October 31...November 8)** +* Status: **Active review (October 31...November 12)** * Implementation: [apple/swift#23934](https://github.com/apple/swift/pull/23934) * Bug: [SR-10218](https://bugs.swift.org/browse/SR-10218) * Forum threads: [Discussion](https://forums.swift.org/t/review-capture-semantics-of-self/22017), [Pitch](https://forums.swift.org/t/allow-implicit-self-in-escaping-closures-when-self-is-explicitly-captured/22590) From 6413ff9f83dc19222486b55556c6215675eef003 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 4 Nov 2019 13:05:31 -0800 Subject: [PATCH 1327/4563] SE-0267 was accepted --- proposals/0267-where-on-contextually-generic.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/proposals/0267-where-on-contextually-generic.md b/proposals/0267-where-on-contextually-generic.md index 5fe93a429c..8807482a29 100644 --- a/proposals/0267-where-on-contextually-generic.md +++ b/proposals/0267-where-on-contextually-generic.md @@ -3,7 +3,7 @@ * Proposal: [SE-0267](0267-where-on-contextually-generic.md) * Author: [Anthony Latsis](https://github.com/AnthonyLatsis) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Active review (October 21...October 31)** +* Status: **Accepted** * Implementation: [apple/swift#23489](https://github.com/apple/swift/pull/23489) ## Introduction @@ -18,13 +18,18 @@ struct Box { ``` > Only declarations that already support a generic parameter list will be allowed to carry a `where` clause inside a generic -> context. Generic properties and constraints on protocol requirements are out of scope for the current proposal. +> context. Generic properties and constraints on protocol requirements or nonfinal class methods are out of scope for the current proposal. > For instance, the following remains an error: > ```swift > protocol P { > // error: Instance method requirement 'foo(arg:)' cannot add constraint 'Self: Equatable' on 'Self' > func foo() where Self: Equatable > } +> +> class C { +> // error: Nonfinal method 'foo(arg:)' cannot add constraint 'Self: Equatable' on 'Self' +> func foo() where Self: Equatable +> } > ``` > Whereas placing a constraint on an extension member rather than the extension itself becomes possible: > ```swift From cbb73367484a24a15e59f1d96885f637f4207b5d Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 5 Nov 2019 12:02:01 -0500 Subject: [PATCH 1328/4563] Return SE-0265 for revision --- proposals/0265-offset-indexing-and-slicing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0265-offset-indexing-and-slicing.md b/proposals/0265-offset-indexing-and-slicing.md index c1557adfbf..fcb605aee2 100644 --- a/proposals/0265-offset-indexing-and-slicing.md +++ b/proposals/0265-offset-indexing-and-slicing.md @@ -3,9 +3,9 @@ * Proposal: [SE-0265](0265-offset-indexing-and-slicing.md) * Author: [Michael Ilseman](https://github.com/milseman) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (October 7th – 21st, 2019)** +* Status: **Returned for revision** * Implementation: [apple/swift#24296](https://github.com/apple/swift/pull/24296) -* Review: ([review](https://forums.swift.org/t/se-0265-offset-based-access-to-indices-elements-and-slices/29596)) +* Review: ([first review](https://forums.swift.org/t/se-0265-offset-based-access-to-indices-elements-and-slices/29596)) ([revision announcement](https://forums.swift.org/t/returned-for-revision-se-0265-offset-based-access-to-indices-elements-and-slices/30503)) ## Introduction From 3da48f15361be0c1f1a92c36f603475a6741105e Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 6 Nov 2019 07:49:48 +0300 Subject: [PATCH 1329/4563] SE-0267: Minor semantic corrections and expand a bit on overrides --- .../0267-where-on-contextually-generic.md | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/proposals/0267-where-on-contextually-generic.md b/proposals/0267-where-on-contextually-generic.md index 8807482a29..437dae184d 100644 --- a/proposals/0267-where-on-contextually-generic.md +++ b/proposals/0267-where-on-contextually-generic.md @@ -18,7 +18,7 @@ struct Box { ``` > Only declarations that already support a generic parameter list will be allowed to carry a `where` clause inside a generic -> context. Generic properties and constraints on protocol requirements or nonfinal class methods are out of scope for the current proposal. +> context. Generic properties, conditional protocol requirements and `Self` constraints on class methods are out of scope for the proposed changes the current proposal. > For instance, the following remains an error: > ```swift > protocol P { @@ -27,7 +27,7 @@ struct Box { > } > > class C { -> // error: Nonfinal method 'foo(arg:)' cannot add constraint 'Self: Equatable' on 'Self' +> // error: type 'Self' in conformance requirement does not refer to a generic parameter or associated type > func foo() where Self: Equatable > } > ``` @@ -38,7 +38,7 @@ struct Box { > } > ``` -Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/where-clauses-on-contextually-generic-declaractions/22449) +[Swift-evolution thread](https://forums.swift.org/t/where-clauses-on-contextually-generic-declaractions/22449) ## Motivation @@ -74,6 +74,22 @@ A step towards generalizing `where` clause usage is an obvious and farsighted im system with numerous future applications, including generic properties, [opaque types](https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md), [generalized existentials](https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials) and constrained protocol requirements. +## Detailed design +### Overrides + +Like their generic analogues, contextually generic methods can be overriden as long as Liskov substitutability is respected, which is to say the generic signature of the override must be *at least* as general as that of the overriden method. + +```swift +class Base { + func foo() where T: Comparable { ... } +} + +class Derived: Base { + // OK, the substitutions for are a superset of those for + override func foo() where T: Equatable { ... } +} +``` + ## Source compatibility and ABI stability This is an additive change with no impact on the ABI and existing code. From 921af7b00dc3d971c723ddf9a186d7ed9bc0e19c Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 6 Nov 2019 07:53:01 +0300 Subject: [PATCH 1330/4563] Update 0267-where-on-contextually-generic.md --- proposals/0267-where-on-contextually-generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0267-where-on-contextually-generic.md b/proposals/0267-where-on-contextually-generic.md index 437dae184d..8054d55943 100644 --- a/proposals/0267-where-on-contextually-generic.md +++ b/proposals/0267-where-on-contextually-generic.md @@ -18,7 +18,7 @@ struct Box { ``` > Only declarations that already support a generic parameter list will be allowed to carry a `where` clause inside a generic -> context. Generic properties, conditional protocol requirements and `Self` constraints on class methods are out of scope for the proposed changes the current proposal. +> context. Generic properties, conditional protocol requirements and `Self` constraints on class methods are out of scope for the current proposal. > For instance, the following remains an error: > ```swift > protocol P { From 08aabe4d657e51177e8124f92af4af8e39f5ab7c Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 6 Nov 2019 20:43:52 +0300 Subject: [PATCH 1331/4563] Update 0267-where-on-contextually-generic.md --- proposals/0267-where-on-contextually-generic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0267-where-on-contextually-generic.md b/proposals/0267-where-on-contextually-generic.md index 8054d55943..af35452c5f 100644 --- a/proposals/0267-where-on-contextually-generic.md +++ b/proposals/0267-where-on-contextually-generic.md @@ -85,7 +85,7 @@ class Base { } class Derived: Base { - // OK, the substitutions for are a superset of those for + // OK, the possible substitutions for are a superset of those for override func foo() where T: Equatable { ... } } ``` From fa68c1cbe65b3e54e7d5cc0c5c5c7f571f5440ad Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Thu, 7 Nov 2019 13:26:27 -0800 Subject: [PATCH 1332/4563] Update 0246-mathable.md --- proposals/0246-mathable.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/proposals/0246-mathable.md b/proposals/0246-mathable.md index a8fe90b477..7e41a0ee59 100644 --- a/proposals/0246-mathable.md +++ b/proposals/0246-mathable.md @@ -8,7 +8,13 @@ * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/b5bbc5ae1f53189641951acfd50870f5b886859e/proposals/0246-mathable.md) [2](https://github.com/apple/swift-evolution/blob/3afc4c68a4062ff045415f5eafb9d4956b30551b/proposals/0246-mathable.md) * Review: ([review](https://forums.swift.org/t/se-0246-generic-math-s-functions/21479)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0246-generic-math-functions/22244)) -**This proposal was accepted with modifications which have not yet been applied to this document. Please see the acceptance post for further details.** +**This proposal is accepted, but currently not implemented in Swift due to source breaking consequences relating +to typechecker performance and shadowing rules. These are expected to be resolved in a future release +of Swift, at which point this proposal can be implemented.** + +In the mean-time, users can make use of the [Swift Numerics](https://github.com/apple/swift-numerics) package, +which also provides this functionality. + ## Introduction @@ -128,22 +134,18 @@ adopt a clearer naming scheme for the protocol that most users see, while also giving `ElementaryFunctions` a more precise name, suitable for sophisticated uses. -The third piece of the proposal is the `Math` module. This module sits next -to the standard library, and provides generic free function implementations -of math operations. Unlike the previous two additions, the availability of -these functions is gated on Swift 5.1. +The third piece of the proposal is the introduction of generic free function +implementations of math operations: + ```swift -(swift) import Math (swift) exp(1.0) // r0 : Float = 2.7182817 ``` Finally, we will update the platform imports to obsolete existing functions -covered by the new free functions in the `Math` module, and also remove the +covered by the new free functions, and also remove the imports of the suffixed functions (which were actually never -intended to be available in Swift). The Platform module will re-export the -Math module, which allows most source code to migrate without any changes -necessary. Updates will only be necessary with functions like `atan2(y: x:)` +intended to be available in Swift). Updates will only be necessary with functions like `atan2(y: x:)` where we are adding argument labels or `logGamma( )` where we have new function names. In these cases we will deprecate the old functions instead of obsoleting them to allow users time to migrate. @@ -329,8 +331,7 @@ The following functions are exported by , but will not be defined on Ele Most of these are not defined on ElementaryFunctions because they are inherently bound to the semantics of `FloatingPoint` or `BinaryFloatingPoint`, and so cannot be defined for types such as Complex or Decimal. Equivalents to many of them are already defined on -`[Binary]FloatingPoint` anyway--in those cases free functions are defined by -the Math module, but will be generic over `FloatingPoint` or `BinaryFloatingPoint`: +`[Binary]FloatingPoint` anyway--in those cases free functions are defined as generic over `FloatingPoint` or `BinaryFloatingPoint`: ```swift @available(swift, introduced: 5.1) @_alwaysEmitIntoClient @@ -436,13 +437,12 @@ syntactically) with an obvious name for logging facilities. However, depending o As an assistance, we will add `ln` in the `Math` module but mark it unavailable, referring users to `log`. -5. We could put the free functions into the standard library instead of a separate module. +5. We could put the free functions into a separate module instead of the standard library. Having them in a separate module helps avoid adding stuff to the global namespace unless you're actually using it, which is generally nice, and the precedent from other -languages is pretty strong here: `#include `, `import math`, etc. Having the -implementation hooks defined in the standard library makes them available in modules that -only need them in a few places or want to use them in inlinable functions but don't want -to have them in the global namespace or re-export them. +languages is pretty strong here: `#include `, `import math`, etc. However, +the general utility of having these functions available in the global namespace is felt +to outweigh this. 6. We could define an operator like `^` or `**` for one or both definitions of `pow`. I have opted to keep new operators out of this proposal, in the interests of focusing on From d7acc695476f1db720fd7526f29cf51c00b740be Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Fri, 8 Nov 2019 22:14:44 +0330 Subject: [PATCH 1333/4563] fixed typo --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index d5b8117e4c..81ee4e4f58 100644 --- a/index.js +++ b/index.js @@ -674,7 +674,7 @@ function filterProposals () { _updateURIFragment() determineNumberOfProposals(intersection) - updatedFilterStatus() + updateFilterStatus() } /** @@ -1024,7 +1024,7 @@ function updateProposalsCount (count) { numberField.innerText = (count.toString() + ' proposal' + (count !== 1 ? 's' : '')) } -function updatedFilterStatus () { +function updateFilterStatus () { var labels = [].concat.apply([], document.querySelectorAll('#filter-options label')) labels.forEach(function (label) { var count = states[label.getAttribute('data-state-key')].count From fed0f117d7ed93c8b36e2459490d64dbbf92885c Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 8 Nov 2019 16:25:58 -0600 Subject: [PATCH 1334/4563] Add proposal for discontiguous collection operations --- ...NNNN-rangeset-and-collection-operations.md | 616 ++++++++++++++++++ 1 file changed, 616 insertions(+) create mode 100644 proposals/NNNN-rangeset-and-collection-operations.md diff --git a/proposals/NNNN-rangeset-and-collection-operations.md b/proposals/NNNN-rangeset-and-collection-operations.md new file mode 100644 index 0000000000..a00ec4ca2c --- /dev/null +++ b/proposals/NNNN-rangeset-and-collection-operations.md @@ -0,0 +1,616 @@ +# Add Collection Operations on Noncontiguous Elements + +* Proposal: [SE-NNNNNNNN](NNNN-rangeset-and-friends.md) +* Author: [Nate Cook](https://github.com/natecook1000) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) + +## Introduction + +We can use a `Range` to refer to a group of consecutive positions in a collection, but the standard library doesn't currently provide a way to refer to discontiguous positions in an arbitrary collection. I propose the addition of a `RangeSet` type that can store any number of positions, along with collection algorithms that operate on those positions. + +Swift forums discussion: https://forums.swift.org/t/pitch-add-rangeset-and-related-collection-operations/29961 + +## Motivation + +There are varied uses for tracking multiple elements in a collection, such as maintaining the selection in a list of items, or refining a filter or search result set after getting more input from a user. + +The Foundation data type most suited for this purpose, `IndexSet`, uses integers only, which limits its usefulness to arrays and other random-access collection types. The standard library is missing a collection that can efficiently store ranges of indices, and is missing the operations that you might want to perform with such a collection. These operations themselves can be challenging to implement correctly, and have performance traps as well — see last year's [Embracing Algorithms](https://developer.apple.com/videos/wwdc/2018/?id=223) WWDC talk for a demonstration. + +## Proposed solution + +This proposal adds a `RangeSet` type for representing multiple, noncontiguous ranges, as well as a variety of collection operations for creating and working with range sets. + +```swift +var numbers = Array(1...15) + +// Find the indices of all the even numbers +let indicesOfEvens = numbers.indices(where: { $0.isMultiple(of: 2) }) + +// Perform an operation with just the even numbers +let sumOfEvens = numbers[indicesOfEvens].reduce(0, +) +// sumOfEvens == 56 + +// You can gather the even numbers at the beginning +let rangeOfEvens = numbers.gather(indicesOfEvens, justBefore: numbers.startIndex) +// numbers == [2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15] +// numbers[rangeOfEvens] == [2, 4, 6, 8, 10, 12, 14] + +// Reset `numbers` +numbers = Array(1...15) + +// You can also build range sets by hand using array literals... +let notTheMiddle: RangeSet = [0..<5, 10..<15] +print(Array(numbers[notTheMiddle])) +// Prints [1, 2, 3, 4, 5, 11, 12, 13, 14, 15] + +// ...or by using set operations +let smallEvens = indicesOfEvens.intersection( + numbers.indices(where: { $0 < 10 })) +print(Array(numbers[smallEvens])) +// Prints [2, 4, 6, 8] +``` + +These are just a few examples; the complete proposal includes operations like inverting a range set and inserting and removing ranges, which are covered in the next section. + +## Detailed design + +`RangeSet` is generic over any `Comparable` type, and supports fast containment checks for ranges and individual values, as well as adding and removing ranges of that type. + +```swift +/// A set of ranges of any comparable value. +public struct RangeSet { + /// Creates an empty range set. + public init() {} + + /// Creates a range set containing the given range. + public init(_ range: Range) + + /// Creates a range set containing the given ranges. + public init(_ ranges: S) where S.Element == Range + + /// A Boolean value indicating whether the range set is empty. + public var isEmpty: Bool { get } + + /// Returns a Boolean value indicating whether the given value is + /// contained by the ranges in the range set. + /// + /// - Complexity: O(log *n*), where *n* is the number of ranges in the + /// range set. + public func contains(_ value: Bound) -> Bool + + /// Returns a Boolean value indicating whether the given range is + /// contained by the ranges in the range set. + /// + /// This method returns `true` if and only if one of the ranges that + /// comprises the range set fully contains `range`. This example shows + /// ranges that are and are not contained by a range set: + /// + /// let set: RangeSet = [0..<5, 10..<15] + /// print(set.contains(1..<4)) + /// // Prints "true" + /// print(set.contains(4..<7)) + /// // Prints "false" + /// print(set.contains(4..<12)) + /// // Prints "false" + /// + /// - Complexity: O(log *n*), where *n* is the number of ranges in the + /// range set. + public func contains(_ range: Range) -> Bool + + /// Inserts the given range into the range set. + public mutating func insert(_ range: Range) + + /// Removes the given range from the range set. + public mutating func remove(_ range: Range) +} +``` + +`RangeSet` conforms to `Equatable`, to `CustomStringConvertible`, and, when its `Bound` type conforms to `Hashable`, to `Hashable`. `RangeSet` also has `ExpressibleByArrayLiteral` conformance, using arrays of ranges as its literal type. + +#### `SetAlgebra`-like methods + +`RangeSet` implements a subset of the `SetAlgebra` protocol. Because of its efficient representation for contiguous ranges, there is no way to insert a single element into a `RangeSet` . + +```swift +extension RangeSet { + public func union(_ other: RangeSet) -> RangeSet + public mutating func formUnion(_ other: RangeSet) + + public func intersection(_ other: RangeSet) -> RangeSet + public mutating func formIntersection(_ other: RangeSet) + + public func symmetricDifference(_ other: RangeSet) -> RangeSet + public mutating func formSymmetricDifference(_ other: RangeSet) + + public func subtracting(_ other: RangeSet) -> RangeSet + public mutating func subtract(_ other: RangeSet) + + public func isSubset(of other: RangeSet) -> Bool + public func isSuperset(of other: RangeSet) -> Bool + public func isStrictSubset(of other: RangeSet) -> Bool + public func isStrictSuperset(of other: RangeSet) -> Bool +} +``` + +#### Collection APIs + +Working with collection indices is a primary use case for `RangeSet`. To support this, `RangeSet` includes initializers and insertion and removal methods for working with individual indices and range expressions, as well as a way to "invert" a range set with respect to a collection. + +Calling one of these methods with an individual index is equivalent to calling the related `RangeSet` method with a range that contains only that index. In this example, the two ways of inserting a range containing `indexOfI` into the range set are equivalent: + +```swift +let str = "Fresh cheese in a breeze" +var set = str.indices(where: { $0 == "e" }) +let indexOfI = str.firstIndex(of: "i")! + +// (1) Creating a range, then inserting it into 'set' +let rangeOfI = indexOfI ..< str.index(after: indexOfI) +set.insert(rangeOfI) + +// (2) Inserting the index with the convenience method +set.insert(indexOfI, within: str) +``` + +The initializers and methods are listed below: + +```swift +extension RangeSet { + /// Creates a new range set containing a range that contains only the + /// specified index in the given collection. + public init(_ index: Bound, within collection: C) + where C: Collection, C.Index == Bound + + /// Creates a new range set containing ranges that contain only the + /// specified indices in the given collection. + public init(_ indices: S, within collection: C) + where S: Sequence, C: Collection, S.Element == C.Index, C.Index == Bound + + /// Creates a new range set containing the range represented by the + /// specified range expression. + public init(_ range: R, within collection: C) + where C: Collection, C.Index == Bound, R: RangeExpression, R.Bound == Bound + + /// Inserts a range that contains only the specified index into the range + /// set. + public mutating func insert(_ index: Bound, within collection: C) + where C: Collection, C.Index == Bound + + /// Inserts the range represented by the specified range expression into + /// the range set. + public mutating func insert(_ range: R, within collection: C) + where C: Collection, C.Index == Bound, R: RangeExpression, R.Bound == Bound + + /// Removes a range that contains only the specified index from the range + /// set. + public mutating func remove(_ index: Bound, within collection: C) + where C: Collection, C.Index == Bound + + /// Removes the range represented by the specified range expression from + /// the range set. + public mutating func remove(_ range: R, within collection: C) + where C: Collection, C.Index == Bound, R: RangeExpression, R.Bound == Bound + + /// Returns a range set that represents all the elements in the given + /// collection that aren't represented by this range set. + /// + /// The following example finds the indices of the vowels in a string, and + /// then inverts the range set to find the non-vowels parts of the string. + /// + /// let str = "The rain in Spain stays mainly in the plain." + /// let vowels = "aeiou" + /// let vowelIndices = str.indices(where: { vowels.contains($0) }) + /// print(String(str[vowelIndices])) + /// // Prints "eaiiaiaaiieai" + /// + /// let nonVowelIndices = vowelIndices.inverted(within: str) + /// print(String(str[nonVowelIndices])) + /// // Prints "Th rn n Spn stys mnly n th pln." + /// + /// - Parameter collection: The collection that the range set is relative + /// to. + /// - Returns: A new range set that represents the elements in `collection` + /// that aren't represented by this range set. + public func inverted(within collection: C) -> RangeSet + where C: Collection, C.Index == Bound +} +``` + +#### Accessing Underlying Ranges and Individual Indices + +`RangeSet` provides access to its ranges as a random-access collection via the `ranges` property. +You can access the individual indices represented by the range set by using it as a subscript parameter to a collection's `indices` property. + +```swift +extension RangeSet { + public struct Ranges: RandomAccessCollection { + public var startIndex: Int { get } + public var endIndex: Int { get } + public subscript(i: Int) -> Range + } + + /// A collection of the ranges that make up the range set. + public var ranges: Ranges { get } +} +``` + +The ranges that are exposed through this collection are always in ascending order, are never empty, and never overlap or adjoin. For this reason, inserting an empty range or a range that is subsumed by the ranges already in the set has no effect on the range set. Inserting a range that adjoins an existing range simply extends that range. + +```swift +var set: RangeSet = [0..<5, 10..<15] +set.insert(7..<7) +set.insert(11.<14) +// Array(set.ranges) == [0..<5, 10..<15] + +set.insert(5..<7) +// Array(set.ranges) == [0..<7, 10..<15] + +set.insert(7..<10) +// Array(set.ranges) == [0..<15] +``` + +#### Storage + +`RangeSet` will store its ranges in a custom type that will optimize the storage for known, simple `Bound` types. This custom type will avoid allocating extra storage for common cases of empty or single-range range sets. + +### New `Collection` APIs + +#### Finding multiple elements + +Akin to the `firstIndex(...)` and `lastIndex(...)` methods, this proposal introduces `indices(where:)` and `indices(of:)` methods that return a range set with the indices of all matching elements in a collection. + +```swift +extension Collection { + /// Returns the indices of all the elements that match the given predicate. + /// + /// For example, you can use this method to find all the places that a + /// vowel occurs in a string. + /// + /// let str = "Fresh cheese in a breeze" + /// let vowels: Set = ["a", "e", "i", "o", "u"] + /// let allTheVowels = str.indices(where: { vowels.contains($0) }) + /// // str[allTheVowels].count == 9 + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + public func indices(where predicate: (Element) throws -> Bool) rethrows + -> RangeSet +} + +extension Collection where Element: Equatable { + /// Returns the indices of all the elements that are equal to the given + /// element. + /// + /// For example, you can use this method to find all the places that a + /// particular letter occurs in a string. + /// + /// let str = "Fresh cheese in a breeze" + /// let allTheEs = str.indices(of: "e") + /// // str[allTheEs].count == 7 + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + public func indices(of element: Element) -> RangeSet +} +``` + +#### Accessing elements via `RangeSet` + +When you have a `RangeSet` describing a group of indices for a collection, you can access those elements via a new subscript. This new subscript returns a new `DiscontiguousSlice` type, which couples the collection and range set to provide access. + +```swift +extension Collection { + /// Accesses a view of this collection with the elements at the given + /// indices. + /// + /// - Complexity: O(1) + public subscript(indices: RangeSet) -> DiscontiguousSlice { get } +} + +extension MutableCollection { + /// Accesses a mutable view of this collection with the elements at the + /// given indices. + /// + /// - Complexity: O(1) to access the elements, O(*m*) to mutate the + /// elements at the positions in `indices`, where *m* is the number of + /// elements indicated by `indices`. + public subscript(indices: RangeSet) -> DiscontiguousSlice { get set } +} + +/// A collection wrapper that provides access to the elements of a collection, +/// indexed by a set of indices. +public struct DiscontiguousSlice: Collection { + /// The collection that the indexed collection wraps. + public var base: Base { get set } + + /// The set of index ranges that are available through this collection. + public var ranges: RangeSet { get set } + + /// A position in an `DiscontiguousSlice`. + struct Index: Comparable { + // ... + } + + public var startIndex: Index { get } + public var endIndex: Index { set } + public subscript(i: Index) -> Base.Element { get } + public subscript(bounds: Range) -> Self { get } +} +``` + +`DiscontiguousSlice` conforms to `Collection`, and conditionally conforms to `BidirectionalCollection` and `MutableCollection` if its base collection conforms. + +#### Moving elements + +Within a mutable collection, you can gather the elements represented by a range set, inserting them in a contiguous range before the element at a specific index, and otherwise preserving element order. This proposal also adds a method for gathering all the elements matched by a predicate, and methods for shifting a contiguous region of elements, denoted by a single index, range, or range expression, to a specific insertion point. + +The elements that you shift or gather don't necessarily end up at exactly the specified insertion point, because other elements slide over to fill gaps left by the elements that move. For that reason, these gathering and shifting methods return the new range or index of the elements that are moved. + +To visualize these operations, divide a collection into two parts, before and after the insertion point. The elements to gather or shift are collected in order in that gap, and the resulting range (or index, for single element moves) is returned. As an example, consider a shift from a range at the beginning of an array of letters to a position further along in the array: + +```swift +var array = ["a", "b", "c", "d", "e", "f", "g"] +let newSubrange = array.shift(from: 0..<3, to: 5) +// array == ["d", "e", "a", "b", "c", "f", "g"] +// newSubrange = 2..<5 + +// ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐ +// │ a │ b │ c │ d │ e │ f │ g │ +// └─────┴─────┴─────┴─────┴─────┴─────┴─────┘ +// ^^^^^^^^^^^^^^^^^ ^ +// source insertion +// point +// +// ┌─────┬─────┬─────┐┌─────┬─────┐┌─────┬─────┐ +// │ a │ b │ c ││ d │ e ││ f │ g │ +// └─────┴─────┴─────┘└─────┴─────┘└─────┴─────┘ +// ^^^^^^^^^^^^^^^^^ ^ +// source insertion +// point +// +// ┌─────┬─────┐┌─────┬─────┬─────┐┌─────┬─────┐ +// │ d │ e ││ a │ b │ c ││ f │ g │ +// └─────┴─────┘└─────┴─────┴─────┘└─────┴─────┘ +// ^^^^^^^^^^^^^^^^^ +// newSubrange == 2..<5 +``` + +When shifting a single element, this can mean that the element ends up at the insertion point (when moving backward), or ends up at a position one before the insertion point (when moving forward, because the elements in between move forward to make room). + +```swift +var array = ["a", "b", "c", "d", "e", "f", "g"] +let newPosition = array.shift(from: 1, to: 5) +// array == ["a", "c", "d", "e", "b", "f", "g"] +// newPosition == 4 + +// ┌─────┬─────┬─────┬─────┬─────┐┌─────┬─────┐ +// │ a │ b │ c │ d │ e ││ f │ g │ +// └─────┴─────┴─────┴─────┴─────┘└─────┴─────┘ +// ^ ^ +// source insertion +// point +// +// ┌─────┬─────┬─────┬─────┐┌─────┐┌─────┬─────┐ +// │ a │ c │ d │ e ││ b ││ f │ g │ +// └─────┴─────┴─────┴─────┘└─────┘└─────┴─────┘ +// ^ +// newPosition == 4 +``` + +The new gathering and shifting methods are listed below. + +```swift +extension MutableCollection { + /// Collects the elements at the given indices just before the element at + /// the specified index. + /// + /// This example finds all the uppercase letters in the array and gathers + /// them between `"i"` and `"j"`. + /// + /// var letters = Array("ABCdeFGhijkLMNOp") + /// let uppercase = letters.indices(where: { $0.isUppercase }) + /// let rangeOfUppercase = letters.gather(uppercase, justBefore: 10) + /// // String(letters) == "dehiABCFGLMNOjkp" + /// // rangeOfUppercase == 4..<13 + /// + /// - Parameters: + /// - indices: The indices of the elements to move. + /// - insertionPoint: The index to use as the destination of the elements. + /// - Returns: The new bounds of the moved elements. + /// + /// - Complexity: O(*n* log *n*) where *n* is the length of the collection. + @discardableResult + public mutating func gather( + _ indices: RangeSet, at insertionPoint: Index + ) -> Range + + /// Collects the elements that satisfy the given predicate just before the + /// element at the specified index. + /// + /// This example gathers all the uppercase letters in the array between + /// `"i"` and `"j"`. + /// + /// var letters = Array("ABCdeFGhijkLMNOp") + /// let rangeOfUppercase = letters.gather(justBefore: 10) { $0.isUppercase } + /// // String(letters) == "dehiABCFGLMNOjkp" + /// // rangeOfUppercase == 4..<13 + /// + /// - Parameters: + /// - predicate: A closure that returns `true` for elements that should + /// move to `destination`. + /// - insertionPoint: The index to use as the destination of the elements. + /// - Returns: The new bounds of the moved elements. + /// + /// - Complexity: O(*n* log *n*) where *n* is the length of the collection. + @discardableResult + public mutating func gather( + at insertionPoint: Index, where predicate: (Element) -> Bool + ) -> Range + + /// Shifts the elements in the given range to just before the element at + /// the specified index. + /// + /// - Parameters: + /// - range: The range of the elements to move. + /// - insertionPoint: The index to use as the insertion point for the + /// elements. `insertionPoint` must be a valid index of the collection. + /// - Returns: The new bounds of the moved elements. + /// + /// - Returns: The new bounds of the moved elements. + /// - Complexity: O(*n*) where *n* is the length of the collection. + @discardableResult + public mutating func shift( + from range: Range, to insertionPoint: Index + ) -> Range + + /// Shifts the elements in the given range expression to just before the + /// element at the specified index. + /// + /// - Parameters: + /// - range: The range of the elements to move. + /// - insertionPoint: The index to use as the insertion point for the + /// elements. `insertionPoint` must be a valid index of the collection. + /// - Returns: The new bounds of the moved elements. + /// + /// - Complexity: O(*n*) where *n* is the length of the collection. + @discardableResult + public mutating func shift( + from range: R, to insertionPoint: Index + ) -> Range where R.Bound == Index + + /// Moves the element at the given index to just before the element at the + /// specified index. + /// + /// This method moves the element at position `i` to immediately before + /// `insertionPoint`. This example shows moving elements forward and + /// backward in an array of integers. + /// + /// var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + /// let newIndexOfNine = numbers.shift(from: 9, toJustBefore: 7) + /// // numbers == [0, 1, 2, 3, 4, 5, 6, 9, 7, 8, 10] + /// // newIndexOfNine == 7 + /// + /// let newIndexOfOne = numbers.shift(from: 1, toJustBefore: 4) + /// // numbers == [0, 2, 3, 1, 4, 5, 6, 9, 7, 8, 10] + /// // newIndexOfOne == 3 + /// + /// To move an element to the end of a collection, pass the collection's + /// `endIndex` as `insertionPoint`. + /// + /// numbers.shift(from: 0, toJustBefore: numbers.endIndex) + /// // numbers == [2, 3, 1, 4, 5, 6, 9, 7, 8, 10, 0] + /// + /// - Parameters: + /// - source: The index of the element to move. `source` must be a valid + /// index of the collection that isn't `endIndex`. + /// - insertionPoint: The index to use as the destination of the element. + /// `insertionPoint` must be a valid index of the collection. + /// - Returns: The resulting index of the element that began at `source`. + /// + /// - Complexity: O(*n*) where *n* is the length of the collection. + @discardableResult + public mutating func shift( + from source: Index, to insertionPoint: Index + ) -> Index +} +``` + +#### Removing elements + +Within a range-replaceable collection, you can remove the elements represented by a range set. `removeAll(at:)` is a new `RangeReplaceableCollection` requirement with a default implementation, along with additional overload for collections that also conform to `MutableCollection`. + +```swift +extension RangeReplaceableCollection { + /// Removes the elements at the given indices. + /// + /// For example, this code sample finds the indices of all the vowel + /// characters in the string, and then removes those characters. + /// + /// var str = "The rain in Spain stays mainly in the plain." + /// let vowels: Set = ["a", "e", "i", "o", "u"] + /// let vowelIndices = str.indices(where: { vowels.contains($0) }) + /// + /// str.removeAll(at: vowelIndices) + /// // str == "Th rn n Spn stys mnly n th pln." + /// + /// - Parameter indices: The indices of the elements to remove. + /// + /// - Complexity: O(*n*), where *n* is the length of the collection. + public mutating func removeAll(at indices: RangeSet) +} + +extension Collection { + /// Returns a collection of the elements in this collection that are not + /// represented by the given range set. + /// + /// For example, this code sample finds the indices of all the vowel + /// characters in the string, and then retrieves a collection that omits + /// those characters. + /// + /// let str = "The rain in Spain stays mainly in the plain." + /// let vowels: Set = ["a", "e", "i", "o", "u"] + /// let vowelIndices = str.indices(where: { vowels.contains($0) }) + /// + /// let disemvoweled = str.removingAll(at: vowelIndices) + /// print(String(disemvoweled)) + /// // Prints "Th rn n Spn stys mnly n th pln." + /// + /// - Parameter indices: A range set representing the elements to remove. + /// - Returns: A collection of the elements that are not in `indices`. + public func removingAll(at indices: RangeSet) -> DiscontiguousSlice +} +``` + +## Other considerations + +### Source compatibility + +These additions preserve source compatibility. + +### Effect on ABI stability + +This proposal only makes additive changes to the existing ABI. + +### Effect on API resilience + +All the proposed additions are versioned. + +## Alternatives considered + +### `SetAlgebra` Conformance + +An earlier version of this proposal included `SetAlgebra` conformance when the `Bound` type was `Stridable` with an integer `Stride`. The idea behind this constraint was that with an integer-strideable bound, `RangeSet` could translate an individual value into a `Range` which contained just that value. This translation enabled the implementation of `SetAlgebra` methods like insertion and removal of individual elements. + +However, when working with collection indices, there is no guarantee that the correct stride distance is the same for all integer-stridable types. For example, when working with a collection `C` that uses even integers as its indices, removing a single integer from a `RangeSet` could leave an odd number as the start of one of the constitutive ranges: + +```swift +let numbers = (0..<20).lazy.filter { $0.isMultiple(of: 2) } +var set = RangeSet(0..<10) +set.remove(4) +// set.ranges == [0..<4, 5..<10] +``` + +Since `5` is not a valid index of `numbers`, it's an error to use it when subscripting the collection. + +One way of avoiding this issue would be to change the internal representation of `RangeSet` to store an enum of single values, ranges, and fully open ranges: + +```swift +enum _RangeSetValue { + case single(Bound) + case halfOpen(Range) + case fullyOpen(lower: Bound, upper: Bound) +} +``` + +This implementation would allow correct `SetAlgebra` conformance, but lose the ability to always have a maximally efficient representation of ranges and a single canonical empty state. Through additions and removals, you could end up with a series of a fully-open ranges, none of which actually contain any values in the `Bound` type. + +```swift +var set: RangeSet = [1..<4] +set.remove(2) +set.remove(3) +set.remove(4) +// set.ranges == [.fullyOpen(1, 2), .fullyOpen(2, 3), .fullyOpen(3, 4)] +``` + +### `Elements` View + +In an earlier version of the proposal, `RangeSet` included a collection view of the indices, conditionally available when the `Bound` type was strideable with an integer stride. This collection view was removed for the same reasons as covered in the section above. Users can access these indices by using a `RangeSet` as a subscript for the source collection's `indices` property. From 4c6c36b6c8c3e32e43be69f08976b688b212f19f Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Mon, 11 Nov 2019 11:58:51 -0800 Subject: [PATCH 1335/4563] Launch review of SE-0270 --- ...ations.md => 0270-rangeset-and-collection-operations.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-rangeset-and-collection-operations.md => 0270-rangeset-and-collection-operations.md} (99%) diff --git a/proposals/NNNN-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md similarity index 99% rename from proposals/NNNN-rangeset-and-collection-operations.md rename to proposals/0270-rangeset-and-collection-operations.md index a00ec4ca2c..f6896d8479 100644 --- a/proposals/NNNN-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -1,9 +1,9 @@ # Add Collection Operations on Noncontiguous Elements -* Proposal: [SE-NNNNNNNN](NNNN-rangeset-and-friends.md) +* Proposal: [SE-0270](0270-rangeset-and-collection-operations.md) * Author: [Nate Cook](https://github.com/natecook1000) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: Dave Abrahams +* Status: **Active review (Novembr 11...November 18)** * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) ## Introduction From 6f69bab5bbd003eb70893204c35eaa1091aa4e02 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Mon, 11 Nov 2019 11:59:43 -0800 Subject: [PATCH 1336/4563] Fix typo --- proposals/0270-rangeset-and-collection-operations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index f6896d8479..c1d3dbb5df 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -3,7 +3,7 @@ * Proposal: [SE-0270](0270-rangeset-and-collection-operations.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: Dave Abrahams -* Status: **Active review (Novembr 11...November 18)** +* Status: **Active review (November 11...November 18)** * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) ## Introduction From a563e65c1754b2cd6e3a922474d0e3d4142fd928 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Mon, 11 Nov 2019 12:03:07 -0800 Subject: [PATCH 1337/4563] Change SE-0270 review end date at Kyle's request --- proposals/0270-rangeset-and-collection-operations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index c1d3dbb5df..28ca57acb3 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -3,7 +3,7 @@ * Proposal: [SE-0270](0270-rangeset-and-collection-operations.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: Dave Abrahams -* Status: **Active review (November 11...November 18)** +* Status: **Active review (November 11...November 21)** * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) ## Introduction From 9b7e7c389cf25337cd3c186252ef9408775e7823 Mon Sep 17 00:00:00 2001 From: Ankit Aggarwal Date: Tue, 12 Nov 2019 15:24:21 -0800 Subject: [PATCH 1338/4563] Package resources proposal (#1094) * Add initial package resources proposal * Replace SPMResources with an extension of Bundle * Don't use column limit since discourse doesn't like it * Respell accessors * Set review date and SE number --- proposals/0271-package-manager-resources.md | 235 ++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 proposals/0271-package-manager-resources.md diff --git a/proposals/0271-package-manager-resources.md b/proposals/0271-package-manager-resources.md new file mode 100644 index 0000000000..f3a2f95ce2 --- /dev/null +++ b/proposals/0271-package-manager-resources.md @@ -0,0 +1,235 @@ +# Package Manager Resources + +* Proposal: [SE-0271](0271-package-manager-resources.md) +* Authors: [Anders Bertelrud](https://github.com/abertelrud), [Ankit Aggarwal](https://github.com/aciidb0mb3r) +* Review Manager: [Boris Buegling](https://github.com/neonichu) +* Status: **Active review (11 12...11 19)** + +## Introduction + +Packages should be able to contain images, data files, and other resources needed at runtime. This proposal describes SwiftPM support for specifying such package resources, and introduces a consistent way of accessing them from the source code in the package. + +## Motivation + +Packages consist primarily of source code intended to be compiled and linked into client executables. Sometimes, however, the code needs additional resources that are expected to be available at runtime. Such resources could include images, sounds, user interface specifications, OpenGL or Metal shaders, and other typical runtime resources. During the build, some package resources might be copied verbatim into the product, while others might need to be processed in some way. + +Resources aren't always intended for use by clients of the package; one use of resources might include test fixtures that are only needed by unit tests. Such resources would not be incorporated into clients of the package along with the library code, but would only be used while running the package's tests. + +One of the fundamental principles behind SwiftPM is that packages should be as platform-independent and client-agnostic as possible: in particular, packages should make as few assumptions as possible about the details of how they will be incorporated into a particular client on a particular platform. + +For example, a package product might in one case be built as a dynamic library or a framework that is embedded into an application bundle, and might in another case be statically linked into the client executable. These differences might be due to requirements of the platform for which the package is being built, or might be a result of deployment packaging choices made by the client. + +Packages should therefore be able to specify resources in a platform-independent way, and SwiftPM should provide a consistent way to access those resources without requiring the source code to make assumptions about exactly where the resources will be at runtime. For example, the source code cannot assume that the resources will be in the same bundle as the compiled code (if bundles even exist as separate entities on a given platform). + +Build systems (such as those in SwiftPM, Xcode, etc) are then free to build resources for runtime use in any way they choose, as long as the API for accessing those resources at runtime continues to work as described in this document. + +## Goals + +The most important goals of this design include: + +* Making it easy to add resource files in a package. + +* Avoiding unintentionally copying files not intended to be resources (e.g. design documents) into the product. + +* Supporting platform-specific resource types for packages written using specific APIs (e.g. Storyboards, XIBs, and Metal shaders on Apple platforms). + +* Supporting localization. + +* Letting package authors put resources where they like in their source directories (to allow functional organization of large code bases). + +## Proposed Solution + +We propose the following to support resources in Swift packages: + +- Scope resources to targets i.e. resource files will be part of a target just like source files. + +- Extend SwiftPM's file detection capabilities and apply a "rule" to every file in the target. SwiftPM will emit an error for any file in a target for which it is not able to automatically determine the rule. + +- Add two new rules "copy" and "process" to existing list of rules. At this time, there will be no additional built-in file type that use these two new rules. + +- Vend an API in libSwiftPM to allow its clients to register additional file types supported by those clients. SwiftPM will automatially detect the matching files in a target and report them in the package model data structure. Similar to source files, package authors will not need to explicitly declare these files in the package manifest. This ability is useful for clients like Xcode to support platform-specific file types (such as `.metal`) without having to bake in a hardcoded list in SwiftPM’s codebase. + +- Add a new `resources` parameter in `target` and `testTarget` APIs to allow declaring resource files explicitly. + +- Create a bundle for each module with resources and generate a `Resources` struct for each module it compiles for accessing the bundle. + +## Detailed Design + +### Declaring Resources + +The `target` and `testTarget` function in the PackageDescription API will be extended to have an optional `resources` parameter: + +```swift +public static func target( + name: String, + dependencies: [Target.Dependency] = [], + path: String? = nil, + exclude: [String] = [], + sources: [String]? = nil, + resources: [Resource]? = nil, // <=== NEW + publicHeadersPath: String? = nil, + cSettings: [CSetting]? = nil, + cxxSettings: [CXXSetting]? = nil, + swiftSettings: [SwiftSetting]? = nil, + linkerSettings: [LinkerSetting]? = nil +) -> Target +``` + +Where the `Resource` type is defined as: + +```swift +/// Represents an individual resource file. +public struct Resource { + /// Apply the platform-specific rule to the given path. + /// + /// Matching paths will be processed according to the platform for which this + /// target is being built. For example, image files might get optimized when + /// building for platforms that support such optimizations. + /// + /// By default, a file will be copied if there is no specialized processing + /// for its file type. + /// + /// If path is a directory, the rule is applied recursively to each file in the + /// directory. + public static func process(_ path: String) -> Resource + + /// Apply the copy rule to the given path. + /// + /// Matching paths will be copied as-is and will be at the top-level + /// in the bundle. The structure is retained for if path is a directory. + public static func copy(_ path: String) -> Resource +} +``` + +The exact API in `libSwiftPM` for registering additional resource file types will be an implementation detail since it currently does not have a stable API. However, there will be *some* API that provides this functionality. Such file types will be automatically picked up by SwiftPM and will not require explicit declaration using the `resources` parameter. SwiftPM contributors will iterate on this API with existing and future clients. For example, if Xcode registers `.metal` as a file type and there is a metal file in a target, the package will not need to declare that file in the resources list. However, the package can override the default rule for this file by adding it to the exclude list or by specifying the "copy" rule for this file. + +Packages will also be able to explicitly declare the resource files using the two new APIs in `PackageDescription`. These APIs have some interesting behavior when the given path is a directory: + +- The `.copy` API allows copying directories as-is which is useful if a package author wants to retain the directory structure. + +- The `.process` API allows applying the rule recursively to files inside a directory. This is useful for packages that have all of its resources contained in directories for organization. + +Each file in a target will be required to have a rule. SwiftPM will emit an error if it is not able to automatically determine the rule for a file. For example, if there is a `README.md` in the target, the package will need to make an explicit decision about this file by either adding to the exclude list or in the resources list. + +### Resources Bundle + +For each target that defines at least one resource file, SwiftPM will produce a Foundation-style bundle whose name and identifier are derived from an implementation-defined unique combination of the package name and the target name (the package source code should not concern itself with the exact name). On Linux, each such resource bundle will be located next to the built executable that is the ultimate client of the target containing the resources. On macOS and related platforms, each such bundle is located at a place of the build system's choosing (typically nested inside the ultimate client's main bundle). + +Note that the construction of a bundle to hold the resources does not necessarily mean that the code is in the same bundle, i.e. it is not necessarily a framework. Whether or not the code is colocated with the resources is an implementation detail. The key part of this proposal is that the package's code not make any assumptions that limits the build system's choices in where to put the resources, as long as it can make them accessible to the package code at runtime. + +### Runtime Access to Resources Bundle + +SwiftPM will generate an internal static extension on `Bundle` for each module it compiles: + +```swift +extension Bundle { + /// The bundle associated with the current Swift module. + static let module: Bundle = { ... }() +} +``` + +Because this is an internal static property, it would be visible only to code within the same module, and the implementations for each module would not interfere with each other. The implementation generated by SwiftPM would use information about the layout of the built product to instantiate and cache the bundle that contains the resources. + +The first access to the `module` property would cause the bundle to be instantiated. Modules without any resources would not have resource bundles, and for such modules, no declaration would be created. + +Some examples: + +```swift +// Get path to DefaultSettings.plist file. +let path = Bundle.module.path(forResource: "DefaultSettings", ofType: "plist") + +// Load an image that can be in an asset archive in a bundle. +let image = UIImage(named: "MyIcon", in: Bundle.module, compatibleWith: UITraitCollection(userInterfaceStyle: .dark)) + +// Find a vertex function in a compiled Metal shader library. +let shader = try mtlDevice.makeDefaultLibrary(bundle: Bundle.module).makeFunction(name: "vertexShader") + +// Load a texture. +let texture = MTKTextureLoader(device: mtlDevice).newTexture(name: "Grass", scaleFactor: 1.0, bundle: Bundle.module, options: options) +``` + +Because the name of the accessor would always be `module`, code could be moved between modules (as long as the resources were moved as well) without requiring any source code changes. + +This would be an improvement over the status quo in Cocoa code, which involves either `Bundle.init(for:)` or `Bundle.init(identifier:)`. The former is problematic because it assumes that the resources will necessarily be in the same bundle as the code (which won't be true for statically linked code), and the latter is problematic because it hardcodes the identifier name (and also because it doesn't automatically cause the bundle to be loaded if it hasn't been loaded already). + +This approach also allows creating codeless resource bundles. Any package target that really wants to just vend the bundle of resources could implement a single property to publicly expose the bundle to clients. It seems reasonable that the package authors have to explicitly vend them. + +For Objective-C, the build system will add a preprocessor define called `SWIFTPM_MODULE_BUNDLE` which can be used to access the bundle from any `.m` file. + +### Localization Support + +Localization support will build on top of the resources feature described in this proposal. We think resources and localization support are separable and it would be better to discuss localization in its own proposal. + +## Rationale + +This section describes the rationale for some of the design decisions taken in this proposal. + +### Scoping of Resources to targets + +Resources are most commonly referenced by the code that needs them, so it seems natural to consider source files and resource files as conceptually being a part of the same target. Any client that depends on the target's code also needs the associated resources to be available at runtime (though it usually doesn't access them directly, but instead through the code that makes use of them). + +Furthermore, the processing of some types of resources might result in the generation of more source code, which needs to be compiled into the same module as the code that needs the resource. For this reason, too, it is natural to consider the resources (and any generated code) as conceptually being a part of the same target as the code that uses it. + +Scoping resources by target also aligns naturally with how targets are currently built in other development environments that support resources: in Xcode, for example, each target is built into a CFBundle (usually a framework or an application), and any resources associated with the target are copied into that bundle. Since a bundle provides a namespace for the resources in it, scoping resources by target is natural. + +If resources were not scoped by target, there would need to be some way to associate the resources with the source code that needs them. This could take the form of defining a separate resource target, and making the code target depend on that resource target. A library that needs just one or two resources would then require the use of two separate targets (one for the code and another for the resources). This seems needlessly complicated, and is therefore not the approach used by this proposal. + +### Rules for determining resource files + +SwiftPM uses file system conventions for determining the set of source files that belongs to each target in a package: specifically, a target's source files are those that are located underneath the designated "target directory" for the target. By default this is a directory that has the same name as the target and is located in "Sources" (for a regular target) or "Tests" (for a test target), but this location can be customized in the package manifest. + +Given that resources are conceptually associated with targets (as discussed in the previous section), then it seems logical for those resources to also reside inside the directories of the targets to which they belong. + +The question then becomes how to determine which of the target files to treat as resources and which to treat as source files. + +A flexible approach would be to treat resource files the same as source files, using rules to determine how to process each file inside the target directory. Just as for source files, each resource file's role would be determined by its file type (as indicated by its filename suffix). + +For example, SwiftPM already recognizes files with suffixes such as `.swift`, `.c`, `.s`, etc as source files, and has built-in rules to determine which build tool to invoke for each known type of source file. + +This could be viewed as simply broadening the notion of what constitutes a "source file" beyond just source code to be compiled and linked into the executable part of the product; the executable code is, after all, just one aspect of the built artifact. In this broadened view, Storyboards, XIBs, Metal shaders, and Xcode Asset Catalogs are all just compiled to produce other kinds of files that then get incorporated into the built product. + +Processing resource files the same way as source files is especially natural for file types that don't fit neatly into a source file / resource file dichotomy, such as Metal shaders and CoreData models. Metal shaders, for example, are compiled as source code, and linked together to produce binary resource files that are loaded at runtime. CoreData models are also compiled, and may generate source code as part of that compilation. + +Specialized file types such as Storyboards, XIBs, CoreData models, or Metal shaders doesn't introduce much risk of mischaracterizing the intent of having the file in the target; these are specialized file types with unambiguous filename suffixes and a clear purpose. + +But multi-purpose file types such as `.md`, `.png`, `.jpg`, `.txt`, or `.pdf` are more problematic: should a Markdown file or a PDF found in a source directory be treated as a resource to be copied into the built product, or is it just a document describing internal implementation details? Similarly, is any given PNG file an artwork resource, or does it perhaps belong to a Markdown file containing internal design notes? And files of an unknown type altogether might or might not be resources, without any clear way for SwiftPM to tell. + +On several occasions, developers have accidentally ended up shipping internal design documents or other files because they were automatically included as resources in their apps based on their types. + +This design aims to reduce that risk as much as possible by limiting the additional built-in rules to only those file types where the intent is clear (Storyboards, XIBs, Metal shaders, CoreData models, Xcode Asset Catalogs, etc). Resource files of more ambiguous types have to be explicitly designated as resources. + +## Impact on existing packages + +The new APIs and behavior described in this proposal will be guarded against the tools version this proposal is implemented in. Packages that want to use this feature will need to update their tools version. + +Once a package updates its tools version, it might need to make some changes to make explicit decisions about any files that were previously being ignored implicitly. + +## Alternatives considered + +### Requiring all resources to be in a special directory + +While an approach involving a magical directory (such as one in a particular location and with a particular name) might be simpler, it would cause problems with adding package support for the large number of CocoaPods and other projects that don't have all of their resources confined to a particular directory. + +One possible approach would be consider resource files as being completely disjoint from source files, requiring all of a target's resources to be located in a separate "Resources" directory inside the target directory. + +While this might be appropriate for some source package layouts, many existing source hierarchies are not structured this way. For example, many CocoaPods have resource files interspersed with source files; this is also common in Xcode projects. + +This practice is especially common for types of resource files that are closely associated with source files; for example, Storyboards and XIB files are commonly located together with the source code that loads those Storyboards and XIB files, grouped by functional component. + +A variation of this would be to have a new top-level "Resources" directory next to "Sources" and "Targets", and to rely on naming conventions to associate a target's resources with a target's sources, but that moves the resources even further from the existing source hierarchy for the target. + +## Future Directions + +### Type-safe access to individual resource files + +Historically, macOS and iOS resources have been accessed primarily by name, using untyped Bundle APIs such as `path(forResource:ofType:)` that assume that each resource is stored as a separate file. Missing resources or mismatched types (e.g. trying to load a font as an image) have historically resulted in runtime errors rather than being detected at build time. + +In the long run, we would like to do better. Because SwiftPM knows the names and types of resource files, it should be able provide type-safe access to those resources by, for example, generating the right declarations that the package's authored code could reference. Missing or mismatched resources would produce build-time errors, leading to early detection of problems. + +This would also serve to separate the referencing of a resource from the details about how that resource is stored in the built artifact; instead of getting the path to an image, for example, and then loading that image using a separate API (which assumes that the image is stored in a separate file on disk), the image accessor could do whatever is needed to load the image based on the platform and build-time processing that was done, so that the package code doesn't have to be aware of such details. + +In the short term, however, we want to keep things simple and allow existing code to work without major modifications. Therefore, the short-term approach suggested here is to stay with Bundle APIs for the actual resource access, and to provide a very simple way for code in a package to access the bundle associated with that code. A future proposal could introduce typed resource references as additional functionality. + +### Filename patterns + +As a separate enhancement, we would like to consider enhancing sources, exclude and resources array to accept a glob pattern using `fnmatch()` semantics. The semantics involving the presence or absence of a path separator are useful in that they provide great expressibility in a manner that is intuitive for anyone familiar with shell name-vs-path lookup semantics etc. From d613d47a1246850288e8cb88eb334aa69b07708f Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 12 Nov 2019 23:48:41 +0000 Subject: [PATCH 1339/4563] =?UTF-8?q?[SE-0271]=20Fix=20status=20page=20"Sc?= =?UTF-8?q?heduled:=20NaN=20=E2=80=93=20NaN"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proposals/0271-package-manager-resources.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0271-package-manager-resources.md b/proposals/0271-package-manager-resources.md index f3a2f95ce2..5f25214920 100644 --- a/proposals/0271-package-manager-resources.md +++ b/proposals/0271-package-manager-resources.md @@ -3,7 +3,7 @@ * Proposal: [SE-0271](0271-package-manager-resources.md) * Authors: [Anders Bertelrud](https://github.com/abertelrud), [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Boris Buegling](https://github.com/neonichu) -* Status: **Active review (11 12...11 19)** +* Status: **Active review (November 12...November 19)** ## Introduction From d4b49cefe126c5c112ad47cb60e6f903f2629722 Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Wed, 13 Nov 2019 20:19:08 +0100 Subject: [PATCH 1340/4563] [Proposal] Binary dependencies for SwiftPM (#1093) * initial draft * Revert "initial draft" This reverts commit b95a42c00aa196cb1db0f73f97bf4e6634999995. * Update 0000-swiftpm-binary-dependencies.md * [binary dependencies] Add prose describing the motivation and alternatives in depth. * [binary dependencies] Start work on more detailed proposal information. * Update 0000-swiftpm-binary-dependencies.md * Update 0000-swiftpm-binary-dependencies.md * Update proposal * Adapt example to new artifact condition * Small typo * Work in latest feedback and restructure proposal a bit * Add missing word * Prepare proposal for review * Apply suggestions from code review Co-Authored-By: Ben Rimmington * Update SE number --- proposals/0272-swiftpm-binary-dependencies.md | 427 ++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 proposals/0272-swiftpm-binary-dependencies.md diff --git a/proposals/0272-swiftpm-binary-dependencies.md b/proposals/0272-swiftpm-binary-dependencies.md new file mode 100644 index 0000000000..10e86a8ab5 --- /dev/null +++ b/proposals/0272-swiftpm-binary-dependencies.md @@ -0,0 +1,427 @@ +# Package Manager Binary Dependencies + +* Proposal: [SE-0272](0272-swiftpm-binary-dependencies.md) +* Authors: [Braden Scothern](https://github.com/bscothern), [Daniel Dunbar](https://github.com/ddunbar), [Franz Busch](https://github.com/FranzBusch) +* Review Manager: [Boris Bügling](https://github.com/neonichu) +* Status: **Active review (November 13...November 20)** + +## Contents + ++ [Introduction](#introduction) ++ [Motivation](#motivation) ++ [Proposed solution](#proposed-solution) ++ [Detailed design](#detailed-design) ++ [New `PackageDescription` API](#new-packagedescription-api) ++ [New `Package.resolved` Behavior](#new-packageresolved-behavior) ++ [Binary Target Artifact Format](#binary-target-artifact-format) ++ [Security](#security) ++ [Impact on existing packages](#impact-on-existing-packages) ++ [Future directions](#future-directions) ++ [Alternatives considered](#alternatives-considered) + +## Introduction + +SwiftPM currently supports source-only packages for several languages, and with +a very proscriptive build model which considerably limits exactly how the +compilation of the source can be performed. While this makes packages consistent +and to some extent "simple", it limits their use in several important cases: +* Software vendors who wish to provide easy integration with the package + manager, but do not deliver source code, cannot integrate. +* Existing code bases which would like to integrate "simply" with SwiftPM, but + require more complicated build processes, have no recourse. + +For example, consider these use cases: + + * Someone wants to create a Swift package for + generating [LLVM](https://llvm.org) code. However, LLVM's build process is + far more complex than can be currently fit into SwiftPM's build model. This + makes building an *easy to use* package difficult. + * A third-party wants to provide a Swift SDK for easily integrating their + service with server-side Swift applications. The SDK itself relies on + substantial amounts of internal infrastructure the company does not want to + make available as open source. + * A large company has an internal team which wants to deliver a Swift package + for use in their iOS applications, but for for business reasons cannot publish + the source code. + +This proposal defines a new SwiftPM feature to allow SwiftPM to accept some +forms of "binary packages". This proposal is intentionally written to +address the above use cases *explicitly*, it **does not** define a general +purpose "binary artifact" mechanism intended to address other use cases (such as +accelerating build performance). The motivations for this are discussed in more +detail below. + +Swift-evolution thread: [\[PITCH\] Support for binary dependencies](https://forums.swift.org/t/pitch-support-for-binary-dependencies/27620) + +## Motivation + +SwiftPM has a large appeal to certain developer communities, like the iOS +ecosystem, where it is currently very common to rely on closed source +dependencies such as Firebase, GoogleAnalytics, Adjust and many more. Existing +package managers like Cocoapods support these use cases. By adding such support +to SwiftPM, we will unblock substantially more adoption of SwiftPM within those +communities. + +Prior to Swift 5.1, the Swift compiler itself did not expose all of the features +(like ABI compatibility) required to build a workable solution. Now that those +features are present, it makes sense to re-evaluate the role of binary packages. + +The goal of this proposal is to make *consumption* of binary packages as +described above *easy*, *intuitive*, *safe*, and *consistent*. This proposal +**does not** attempt to provide any affordances for the creation of the binary +package itself. The overall intent of this proposal is to allow consumption of +binary packages *where necessary*, but not to encourage their use or facilitate a +transition from the existing source-based ecosystem to a binary one. + +This proposal is also focused at packages which come exclusively in binary form, +it explicitly **does not** introduce a mechanism which allows a package to be +present in either source or binary form. See alternatives considered for more +information on this choice. + +## Proposed solution + +To enable binary dependencies we have to make changes in the `Package.swift` manifest file. First, we propose to add a new target type which describes a binary target. Such a target needs to declare where to retrieve the artifact from and the checksum of the expected artifact. An example of such a package can be seen below: + +```swift +let package = Package( + name: "SomePackage", + platforms: [ + .macOS(.v10_10), .iOS(.v8), .tvOS(.v9), .watchOS(.v2), + ], + products: [ + .library(name: "SomePackage", targets: ["SomePackageLib"]) + ], + targets: [ + .binaryTarget( + name: "SomePackageLib", + artifacts: [ + .artifact( + source: .url("https://github.com/some/package/releases/download/1.0.0/SomePackage-1.0.0.zip", + checksum: "839F9F30DC13C30795666DD8F6FB77DD0E097B83D06954073E34FE5154481F7A")), + ] + ) + ] +) +``` + + +Secondly we propose to add a new configuration point to the package description that allows packages to opt-out of binary dependencies. This will enforce that nothing in its transitive dependencies brings a binary dependency with it. + +```swift +let package = Package( + name: "SomeOtherPackage", + disallowsBinaryDependencies: true, + products: [ + .library(name: "SomeOtherPackage", targets: ["SomeOtherPackageLib"]) + ], + targets: [ + .target(name: "SomeOtherPackageLib") + ] +) +``` + +Packages are allowed to contain a mix of binary and source targets. This is +useful when, for example, providing a pre-built or closed source C library +alongside an open source set of Swift bindings for the library. + +When a package is built that depends upon any product with a binary target, the +package manager will search the `artifacts` declaration list to find an artifact +which matches the current build conditions. This list +will be searched in order, and the first matching artifact will be used. It is +the job of the package author/publisher to provide an appropriate set of +artifacts for the use cases the package wishes to support. This use case will be limited to Apple platforms in the beginning. In the future, we can add support for other platforms. A potential approach is outlined in the future directions section. + +## Detailed design + +The design consists of the following key points: +* New `PackageDescription` API for defining a binary target. +* New parameter on a package declaration level to opt-out of binary dependencies. +* New requirements for the `Package.resolved` file when using binary packages. +* A new mechanism for downloading binary target artifacts. + +Terminology: + +* Technically, a *target* is binary or not. However, we anticipate that often a + single package will consist of either exclusively source or binary targets. We + will use the term *binary package* to refer to any package which contains at + least one binary product. Similarly, a *binary product* is one which contains + at least one binary target. + +Our design attempts to optimize for the following goals: + +* Ease of use for clients +* Ease of implementation in existing SwiftPM +* Ease of maintenance in the face of an evolving SwiftPM +* Understandable composition with current and upcoming SwiftPM features +* Support existing well-known occurrences of binary artifacts in the existing + (often iOS focused) target developer market. + +while keeping the following as non-goals: + +* Ease of production of binary packages +* Simplicity of binary artifact distribution mechanism +* Widespread use of binary packages + +## New `PackageDescription` API + +### BinaryTarget +Since a binary target is different compared to a source only target, we propose to introduce a new struct `Artifact`. This struct defines a target's associated artifacts. + +We propose to support local and remote artifacts from the beginning. In the alternatives considered section is larger collection of potential artifact stores. However we opted to simplify the initial implementation by just supporting a url and a path based definition. Later, we can implement different types of providers with different authentication methods. + +```swift +public struct Artifact { + public enum Source { + case url(String, checksum: String) + case path + } + + public let source: Source +} +``` + +Furthermore, we propose to add a new `artifacts: [Artifacts]?` property to the `Target`, as well as extend the initializer with this parameter and create a new static method called `.binaryTarget()`. Lastly, we propose to extend the `TargetType` enum with a new case called `binary`. + +### PackageDescription +To opt out of binary packages we propose a new configuration point inside the package description. + +```swift +public final class Package { + ... + /// This disallows any binary dependency or any transitive binary dependency. + public var disallowsBinaryDependencies: Bool + ... +} +``` + +## New `Package.resolved` Behavior + +For binary targets we store the checksum of the artifact in the `Package.resolved`. This lets us check for errors during resolution where a package's version did not change but the checksum did. In this case we will throw an error alerting the user about this. + +### Resolution + +Package resolution and dependency expression will not be impacted by this change (except where explicitly noted). + +#### Multiple references to same artifact +During resolution SwiftPM will check that all references to an artifact in a dependency graph have the same checksum. + +## Binary Target Artifact Format + +SwiftPM currently supports multiple platforms; however, this proposal only adds support for binary targets on Apple platforms. The reason for this is that Apple platforms provide ABI guarantees and an already existing format we can leverage to simplify the initial implementation. For Apple platforms we propose to use the `XCFramework` format for artifacts. This format already supports dynamic and static linking. Furthermore, it can contain products for every individual Apple platform at once. + +## Security + +When adding new external dependencies, it is always important to consider the security implication that it will bring with it. Comparing the trust level of a source-based to a binary-based dependency the first thought is that the trust level of the source-based dependency is higher since on can inspect its source code. However, there is no difference between a binary and source dependency since source-based dependencies can have security issues as well. One should have better reasons to trust a dependency than source being inspectable. + +There is still a significant difference between having a dependency with zero vs. any binary dependency. For example, the portability of a library with binary dependencies is far worse than the one with only source-based dependencies. For this reason, we propose to add an additional configuration point in the manifest that allows package authors to opt-out of binary dependencies. + +However, there are still some security related aspects when it comes to binary artifacts that we should mitigate. For example, when declaring a `binaryTarget` the hash of the artifact is required similar to Homebrew. By doing this an attacker needs to compromise both the server which provides the artifact as well as the git repository which provides the package manifest. A secondary reason is that the server providing the binary might be out of the package author's control and this way we can ensure that the expected binary is used. + +Lastly, the hash of the binary is stored in the package resolved to avoid that the vendor changes the artifact behind a version without anyone noticing. + +## Impact on existing packages + +No current package should be affected by this change since this is only an additive change in enabling SwiftPM to use binary dependencies. + +## Future directions + +### Support for non-Apple platforms +Non-Apple platforms provide non-trivial challenges since they are not always giving guarantees of the ABI of the platform. Additionally, further conditions such as the corelibs-foundation ABI or if the hardware supports floating points need to be taken into consideration when declaring a package for non-Apple platforms. Various other communities tried to solve this, e.g. Python's [manylinux](https://www.python.org/dev/peps/pep-0600/). + +In the future, we could add an `ArtifactCondition ` to SwiftPM which provides the possibility to declare under which conditions a certain artifact can be used. Below is a potential `ArtifactCondition` struct which does **not** include a complete set of conditions that need to be taken into consideration. + +```swift +public struct ArtifactCondition: Encodable { + public struct LLVMTriplet: Encodable { + // Should be only the subset that Swift supports + enum ArchType: String, Encodable { + case arm5 + case arm7 + case x86 + case x86_64 + // And the rest + } + + // Should be only the subset that Swift supports + enum Vendor: String, Encodable { + case apple + case ibm + case bgp + case suse + // And the rest + } + + // Should be only the subset that Swift supports + enum OSType: String, Encodable { + case linux + case openBSD + case win32 + case darwin + case iOS + case macOSX + // And the rest + } + + let archType: ArchType + let vendor: Vendor + let osType: OSType + // Do we need the LLVM environment here? + public init(archType: ArchType, vendor: Vendor, osType: OSType) { + self.archType = archType + self.vendor = vendor + self.osType = osType + } + } + + private let llvmTriplets: [LLVMTriplet] + + private init(llvmTriplets [LLVMTriplet]) { + self.llvmTriplets = llvmTriplets + } + + /// Create an artifact condition. + /// + /// - Parameters: + /// - llvmTriplets: The llvm triplets for which this condition will be applied. + public static func when( + llvmTriplets: [LLVMTriplet] + ) -> ArtifactCondition { + return ArtifactCondition(llvmTriplets: llvmTriplets) + } +} +``` + +## Alternatives considered + +### General Approach + +There are three popular use cases for binary packages (terminology courtesy +of +[Tommaso Piazza](https://forums.swift.org/t/spm-support-for-binaries-distribution/25549/32)). They +are all related, but for the purposes of this proposal we will distinguish them: + +1. "Vendored binaries" (no source available, or cannot be built from source) +2. "Artifact cache" (pre-built version of packages which are available in source form) +3. "Published & tagged binaries" (the package manager heavily depends on + published and tagged binary artifacts) + +In the first case, binary packages are used because there is no other viable +alternative. In the second case, binary artifacts are used to either accelerate +development (by eliminating existing build or analysis steps), or to simplify +cognitive load (e.g. by removing uninteresting sources from display in an IDE +with package integration). In the third case, the very mechanism the package +manager uses to resolve dependencies is deeply integrated with the publishing of +a binary artifact. While the third approach is popular in certain ecosystems and +package managers like Maven, we consider it out of scope given SwiftPM's current +decentralized architecture, and we will ignore it for the remained of this +proposal. + +The proposal explicit sets out to solve the first use case; a natural question +is should the second use case be supported by the same feature. In this +proposal, we chose not to go that route, for the following reasons: + +* When used as a build or space optimization, artifact caching is a general + purpose strategy which can be applied to *any* package. SwiftPM was explicitly + designed in order to allow the eventual implementation of performant, + scalable, and even distributed caches for package artifacts. Artifact caching + is something we would like to "just work" in order to give the best possible + user experience. + + In particular, when artifact is employed "manually" to achieve the above + goals, it often introduces certain amounts of ambiguity or risk. From the + user's perspective, when the source of a package is available, then one would + typically like to think of the artifact cache as a perfect reproduction of + "what would have been built, if I built it myself". However, leveraging a + binary package like mechanism instead of explicit tool support for this often + means: + + * There is almost no enforcement that the consumed binary artifact matches the + source. The above presumption of equivalence makes such artifact caches a + ripe opportunity for embedding malware into an ecosystem. + + * The consumer does not always have control over the artifact production. This + interacts adversely with potential future SwiftPM features which would allow + the build of a package to be more dependent on its consumer (e.g. allowing + compile-time configuration "knobs & switches"). + + * The artifact cache "optimization" may not apply to all packages, or may + require substantial manual effort to maintain. + +* When used as a workflow improvement (e.g. to reduce the scope of searches), + our position is that the user would ultimately have a better user experience + by explicitly enumerating and designing features (either in SwiftPM, or in + related tools) to address these use cases. When analyzed, it may become clear + that there is more nuance to the solution than an artifact caching scheme + could reasonably support. + +* The choice to support both source and binary packages in the same mechanism + imposes certain requirements on the design which make it more complex than the + existing proposal. In particular, it means that the metadata about how the + source and artifacts are mapped must be kept somewhere adjacent to but + distinct from the package description (since a source package needs to define + its source layout). However, such a mechanism must also be defined in a way + that works when no source layout is present to support binary only packages. + + Finally, since it would be a feature with user-authored metadata, such a + mechanism would need to be updated when any other SwiftPM enhancement + introduces or changes the nature of the source layout specification. + +Taken together, the above points led us to focus on a proposal focused at +"vendored binaries", while our hope is that artifact caching eventually becomes +a built-in and automatic feature of the package manager which applies to all +packages. + +### Binary Signatures + +We considered adding signature checks during the checkout of binary dependencies but when these have transitive dependencies it gets complicated expressing that in the `Package.swift`. + +``` + let package = Package( + name: "Paper", + products: [...], + dependencies: [ + .package(url: "http://some/other/lib", .exact("1.2.3"), binarySignature: .any), + .package(url: "http://some/other/lib", .exact("1.2.3"), binarySignature: .gpg("XXX")"), + ], + targets: [...] + ) +``` + +### Binary target vs. binary product +During the discussion, it was brought up whether a binary dependency should be declared as a target or a product. Below is a list of what we took into consideration when deciding between target and product. In the end, a target seems like a better choice for a binary dependency. + +- Targets allow configuring linker/compiler flags; this ability might be necessary for static libraries +- There are already `systemLibraryTargets` which are essentially dependencies on pre-existing binaries on the host system +- Targets represent a single module, the same is true for one XCFramework +- Currently there is no way to depend on products, so mixed binary and source packages might be harder if we go with a binary product approach +- Analogy with what currently is being produced by source packages (dylibs => product) + +### .o file format +During the discussion of the proposal, the idea of using `.o` files was brought up. This would follow what SwiftPM creates for source-based dependencies right now; therefore, making the integration potentially easier. The main benefit of using `.o` files would be that the product linking the binary artifact can decide whether to link it dynamically or statically. However, further discussion needs to happen here how this would work. For now XCFrameworks are the initial format. XCFrameworks will allow current framework authors to package their existing XCFrameworks and distribute them via SwiftPM. + +### Avoiding duplicate symbols with static libraries/frameworks +When multiple products depend on a static library or framework it will result in duplicated symbols. SwiftPM could be smart enough to figure this out from the manifest and provide an error. This is a potential future improvement, but would require SwiftPM to know the linkage type of the artifact. + +### Opt-in to allow binaries +In the beginning, we considered making binary support an opt-in feature so that only when somebody explicitly allows it then SwiftPM tried to use them. However, after discussion in the Swift forum we came to the conclusion that the trust one has to extend to a dependency is no different between a source-based and binary-based one; therefore, we removed the opt-in behavior but added an opt-out behavior. + +Using an opt-in mechanism for binary dependencies would also mean that any package that adds a binary dependencies would need to do a major version bump, because it will require any client to change something in their manifests. + +### Whitelist for allowed URLs for binary dependencies +During the discussion of this proposal another solution to the `allowsBinary` flag was brought up. That is to create a whitelist for URLs that are allowed origins for binary artifacts. This way one can still control from where binary dependencies come but it doesn't require to allow them for a complete dependency tree; therefore, giving more fine-grained control. However, we propose an opt-out mechanism instead. + +### Opt-out configuration in separate file +During the discussion of this proposal it was decided that an opt-out mechanism was good to give package users and vendors an escape hatch. However, it was discussed whether this configuration should live inside the manifest or a separate configuration file. In this proposal, we opted to keep the configuration inside the manifest file. + +### Support for various artifact stores +Initially, we considered the various artifact stores on the market and how we can integrate with them. We decided to support a URL based artifact definition for the first implementation since the various providers require each their own method of authentication. However, we wanted to keep the possibility for future additions of providers open; therefore, we made the source of an artifact an enum which can be extended. + +Possible artifact stores we considered: +- Github releases +- Github packages +- Gitlab +- Bitbucket +- Artifactory, Nexus etc. + +### Conditional Linkage +During the discussion of this proposal it was brought up to support conditional linkage of binary targets. This is in itself a very useful feature; however, it applies to binary and source based targets. In the end, conditional linkage is an orthogonal feature which can be pitched separately. From e0a3d620549f39c5089e5dd6ead1cd9d7d1d5331 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Thu, 14 Nov 2019 14:37:58 -0600 Subject: [PATCH 1341/4563] Fix some missed 'justBefore' parameter names --- proposals/0270-rangeset-and-collection-operations.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index 28ca57acb3..871a5d1c59 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -33,7 +33,7 @@ let sumOfEvens = numbers[indicesOfEvens].reduce(0, +) // sumOfEvens == 56 // You can gather the even numbers at the beginning -let rangeOfEvens = numbers.gather(indicesOfEvens, justBefore: numbers.startIndex) +let rangeOfEvens = numbers.gather(indicesOfEvens, at: numbers.startIndex) // numbers == [2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15] // numbers[rangeOfEvens] == [2, 4, 6, 8, 10, 12, 14] @@ -408,7 +408,7 @@ extension MutableCollection { /// /// var letters = Array("ABCdeFGhijkLMNOp") /// let uppercase = letters.indices(where: { $0.isUppercase }) - /// let rangeOfUppercase = letters.gather(uppercase, justBefore: 10) + /// let rangeOfUppercase = letters.gather(uppercase, at: 10) /// // String(letters) == "dehiABCFGLMNOjkp" /// // rangeOfUppercase == 4..<13 /// @@ -430,7 +430,7 @@ extension MutableCollection { /// `"i"` and `"j"`. /// /// var letters = Array("ABCdeFGhijkLMNOp") - /// let rangeOfUppercase = letters.gather(justBefore: 10) { $0.isUppercase } + /// let rangeOfUppercase = letters.gather(at: 10) { $0.isUppercase } /// // String(letters) == "dehiABCFGLMNOjkp" /// // rangeOfUppercase == 4..<13 /// @@ -485,18 +485,18 @@ extension MutableCollection { /// backward in an array of integers. /// /// var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - /// let newIndexOfNine = numbers.shift(from: 9, toJustBefore: 7) + /// let newIndexOfNine = numbers.shift(from: 9, to: 7) /// // numbers == [0, 1, 2, 3, 4, 5, 6, 9, 7, 8, 10] /// // newIndexOfNine == 7 /// - /// let newIndexOfOne = numbers.shift(from: 1, toJustBefore: 4) + /// let newIndexOfOne = numbers.shift(from: 1, to: 4) /// // numbers == [0, 2, 3, 1, 4, 5, 6, 9, 7, 8, 10] /// // newIndexOfOne == 3 /// /// To move an element to the end of a collection, pass the collection's /// `endIndex` as `insertionPoint`. /// - /// numbers.shift(from: 0, toJustBefore: numbers.endIndex) + /// numbers.shift(from: 0, to: numbers.endIndex) /// // numbers == [2, 3, 1, 4, 5, 6, 9, 7, 8, 10, 0] /// /// - Parameters: From 3fd6f62c07fc1844fb5dff89cc733140723c063e Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 15 Nov 2019 16:54:26 -0600 Subject: [PATCH 1342/4563] Give function types in key-path literal example --- proposals/0249-key-path-literal-function-expressions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0249-key-path-literal-function-expressions.md b/proposals/0249-key-path-literal-function-expressions.md index 155f24028c..f4b1b99327 100644 --- a/proposals/0249-key-path-literal-function-expressions.md +++ b/proposals/0249-key-path-literal-function-expressions.md @@ -127,8 +127,8 @@ func makeIndex() -> Int { return nextIndex } -let getFirst = \Array.[makeIndex()] // Calls makeIndex(), gets 0, forms \Array.[0] -let getSecond = \Array.[makeIndex()] // Calls makeIndex(), gets 1, forms \Array.[1] +let getFirst: ([Int]) -> Int = \Array.[makeIndex()] // Calls makeIndex(), gets 0, forms \Array.[0] +let getSecond: ([Int]) -> Int = \Array.[makeIndex()] // Calls makeIndex(), gets 1, forms \Array.[1] assert(getFirst([1, 2, 3]) == 1) // No matter how many times assert(getFirst([1, 2, 3]) == 1) // you call getFirst(), From c1718396e4ab7cb751d550771b41911f3db25f9a Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Thu, 21 Nov 2019 21:35:23 -0600 Subject: [PATCH 1343/4563] Revise Swift Preview package proposal (#1092) * Add detail about individual packages to the Preview Package proposal * Add links to implementation * Provide text for monolithic package alternative --- proposals/0264-stdlib-preview-package.md | 69 ++++++++++++++---------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index f9aec08c43..fe5bba06d2 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,15 +3,20 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) -* Status: **Returned for Revision** +* Status: **Awaiting Review** +* Implementation: + 1. [apple/swift-evolution#1089](https://github.com/apple/swift-evolution/pull/1089) + 2. [apple/swift-evolution#1090](https://github.com/apple/swift-evolution/pull/1090) + 3. [apple/swift-evolution#1091](https://github.com/apple/swift-evolution/pull/1091) * Pitch Discussion: [Pitch: Standard Library Preview Package](https://forums.swift.org/t/pitch-standard-library-preview-package/27202) -* Decision Notes: [Returned for Revision](https://forums.swift.org/t/returned-for-revision-se-0264-standard-library-preview-package/29865) +* Previous Review: [SE-0264 — Standard Library Preview Package](https://forums.swift.org/t/se-0264-standard-library-preview-package/29068) +* Previous Decision: [Returned for Revision](https://forums.swift.org/t/returned-for-revision-se-0264-standard-library-preview-package/29865) ## Introduction -We propose the addition of a new package, `SwiftPreview`, which will form the initial landing spot for certain additions to the Swift standard library. +We propose changing the Swift Evolution process to publish accepted proposals as individual SwiftPM packages, as well as a `SwiftPreview` package that bundles these proposal packages together. This group of packages will form the initial landing spot for certain additions to the Swift standard library. -Adding this package serves the goal of allowing for rapid adoption of new standard library features, enabling sooner real-world feedback, and allowing for an initial period of time where that feedback can lead to source- and ABI-breaking changes if needed. +Adding these packages serves the goal of allowing for rapid adoption of new standard library features, enabling sooner real-world feedback, and allowing for an initial period of time where that feedback can lead to source- and ABI-breaking changes if needed. As a secondary benefit, it will reduce technical challenges for new community members implementing new features in the standard library. @@ -21,9 +26,9 @@ In the first iteration, this package will take the following: - new types (for example, a sorted dictionary, or useful property wrapper implementations) - new protocols, with conformance of existing library types to those protocols as appropriate -The package will not include features that need to be matched with language changes, nor functions that cannot practically be implemented without access to existing type internals or other non-public features such as LLVM builtins. +These packages will not include features that need to be matched with language changes, nor functions that cannot practically be implemented without access to existing type internals or other non-public features such as LLVM builtins. -For the purposes of this document, we will refer to the proposed standard library preview package as "the package" and the standard library that ships with the toolchain as "the library". +For the purposes of this document, we will refer to the individual packages and the `SwiftPreview` package as "the preview packages" and the standard library that ships with the toolchain as "the library". ## Motivation @@ -47,21 +52,21 @@ It would be of great benefit to the community to allow proposals and contributio ## Proposed solution -We propose the introduction of a `SwiftPreview` SwiftPM package. The package will be a standalone project hosted on GitHub under the Apple organization, and will be part of the overall Swift project. +We propose introducing individual SwiftPM packages for each approved proposal that meets the criteria above, as well as a `SwiftPreview` package that imports and re-exports each of the individual proposal packages. These packages will be standalone projects hosted on GitHub under the Apple organization, and will be part of the overall Swift project. -Whenever possible, proposals for additions to the standard library will land first in the package before migrating to the standard library. A PR against the preview package will be sufficient to fulfill the implementation requirement for an evolution proposal. Proposals that are accepted will land in the package immediately, and then be integrated into the standard library. +Whenever possible, proposals for additions to the standard library will land first as a package before migrating to the standard library. A PR against the `SwiftPreview` package will be sufficient to fulfill the implementation requirement for an evolution proposal. Proposals that are accepted will be published as packages immediately, and then be integrated into the standard library. For more detail on this process, see _Evolution_ below. -All additions to the standard library will **continue to use the Swift Evolution process**, no matter whether they first land in the package or not. All additions to the package should be made with the understanding that they will migrate to the ABI-stable standard library module that ships as part of the Swift toolchain. +All additions to the standard library will **continue to use the Swift Evolution process**, no matter whether they first land as a package or not. All proposed additions to the standard library should be made with the understanding that they will migrate to the ABI-stable standard library module that ships as part of the Swift toolchain. -If usage after acceptance of a proposal reveals unanticipated problems, a follow-up proposal amendments will be able to make source-breaking changes. Unlike the very high bar for source-breaking changes, and the absolute rules around ABI stability, the bar for changes to API that haven't shipped as part of a Swift release will be that of any other evolution proposal of a new API. +If usage after acceptance of a proposal reveals unanticipated problems, a follow-up proposal or amendment will be able to make source-breaking changes. Unlike the very high bar for source-breaking changes to the standard library, and the absolute rules around ABI stability, the bar for changes to API that haven't shipped as part of a Swift release will be that of any other proposal of a new API. -Starting life in the package will not be mandatory. Proposed additions can instead go straight into the standard library, if they do not meet the criteria for suitable package additions. See the detailed design section for an expanded discussion of these criteria. +Starting life as a package will not be mandatory. Proposed additions can instead go straight into the standard library, if they do not meet the criteria for suitable package additions. See the detailed design section for an expanded discussion of these criteria. -Since the package will not be ABI-stable, it will not ship as a binary on any platform, or be a dependency of any ABI-stable package. This allows for changes to the internal implementation of any type, and the change/removal of any function, as part of implementation changes, follow-on proposal amendments, or subsequent proposals. +Since the packages will not be ABI-stable, they will not ship as a binary on any platform, or be a dependency of any ABI-stable package. This allows for changes to the internal implementation of any type, and the change/removal of any function, as part of implementation changes, follow-on proposal amendments, or subsequent proposals. ## Detailed design -The following additive evolution proposals could be made using the preview package: +The following additive evolution proposals could be made using the preview package process: - New algorithms implemented as extensions on library protocols - Utility types (e.g. wrapper types returned from functions like the `Lazy` types underlying `.lazy`) @@ -69,32 +74,34 @@ The following additive evolution proposals could be made using the preview packa - New collection types - Property wrappers, such as a late-initialized wrapper -The following are examples of changes that would _not_ go into the package: +The following are examples of changes that would _not_ be published in the preview packages: - Types introduced as part of and inseparable from language features (like `Optional` or `Error`) - Implementations that rely on builtins for a reasonable implementation (like atomics or SIMD types) - Implementations that require access to other types internals to be performant - Changes that can't be done via a package, like adding customization points to protocols -Some of these cases will require a judgement call. For example, making an extension method a customization point may bring a minor performance optimization — and so not prevent initial deployment in the package — or it may be a major part of the implementation, making the difference between an `O(1)` and `O(n)` implementation. Whether a proposal should go into the preview package should be part of the evolution pitch and review discussions. +Some of these cases will require a judgement call. For example, making an extension method a customization point may bring a minor performance optimization — and so not prevent initial deployment in the package — or it may be a major part of the implementation, making the difference between an `O(1)` and `O(n)` implementation. Whether a proposal will be published as a package should be part of the evolution pitch and review discussions. ### Evolution -The introduction of the `SwiftPreview` package does not change much in the process of Swift Evolution. Changes to the standard library API surface should go through a well-developed pitch - discussion - implementation - proposal - decision life-cycle. +The introduction of the preview packages does not change much in the process of Swift Evolution. Changes to the standard library API surface should go through a well-developed pitch - discussion - implementation - proposal - decision life-cycle. -The main difference from the existing process is that the final result will become available for general use immediately in a new version of the preview package. As users have real-world experience with the accepted functionality, any proposed amendments to the proposal must go through the same evolution process as an original proposal. +Proposal authors will provide the required implementation that accompanies their proposal by opening a pull request against a new `swift-evolution-staging` repository. This implementation PR will be merged into its own branch before the start of a review to facilitate experimentation during the review process. Upon completion of the review, the proposed package will be moved into its own repository, or removed from the `swift-evolution-staging` repository if the proposal was rejected. + +The main difference from the existing process is that the final result will become available for general use immediately in the preview packages. As users have real-world experience with the accepted functionality, any proposed amendments to the proposal must go through the same evolution process as an original proposal. To provide time for feedback from real-world use of new additions, the Swift Evolution process should favor landing new features in a window immediately after branching for a major release. This doesn’t mean that important and valuable proposals can’t be added at different times, but they’ll be subject to increased scrutiny. -No proposal should be accepted into the package on a provisional basis: Reviewers should assume that every proposal will be migrated as-is unless feedback reveals that it was a clear misadventure. The review manager for any revising proposals will be responsible for ensuring that the review discussion focuses on feedback from real-world use, and not relitigation of previously settled decisions made during the original review. +No proposal should be accepted into the preview packages on a provisional basis: Reviewers should assume that every proposal will be migrated as-is unless feedback reveals that it was a clear misadventure. The review manager for any revising proposals will be responsible for ensuring that the review discussion focuses on feedback from real-world use, and not relitigation of previously settled decisions made during the original review. ### Migration -An important aspect of the design is the experience of users of the package when features that have been available from the package become available in the standard library in a new Swift or platform release. This is handled differently for functions and types. +An important aspect of the design is the experience of users of the preview packages when features that have been available from the packages become available in the standard library in a new Swift or platform release. This is handled differently for functions and types. #### Types -The preview package will have a different module name (`SwiftPreview`) to that of the standard library (`Swift`). As such, every type it declares will be distinct from the type once it migrates into the standard library, and can co-exist with it. Because the preview package is "above" the `Swift` module in the stack, users of the package will get the package version of the type by default in source files that have imported the preview package. They will be able to specify the library version instead by prepending `Swift.` to the type name. This has the benefit that addition of the type into the library is source-compatible with code already using the package version. +The preview packages will have different module names (e.g. `SwiftPreview`, `SE250_LeftPad`) than that of the standard library (`Swift`). As such, every type it re-exports will be distinct from the type once it migrates into the standard library, and can co-exist with it. Because the preview packages are "above" the `Swift` module in the stack, users of the packages will get the package version of the type by default in source files that have imported a preview package. They will be able to specify the library version instead by prepending `Swift.` to the type name. This has the benefit that addition of the type into the library is source-compatible with code already using the package version. It does have a downside for code size. Package users do not benefit from the code-size wins of not including the new type in their app and instead using a version in the OS (though, given that most types in the standard library are generic and need to be specialized, this is not as big a problem as it might be). It would also mean package users miss out on optimizations that may be possible with the library implementation. Once inside the library, fast paths could benefit from internals of other types. For example, a ring buffer might be implemented in terms of the same storage as an `Array`, and conversion from one type to another could just be a question of copying a reference to the other's storage. @@ -114,39 +121,41 @@ A common pattern used in the standard library is to return a type from a functio Since types cannot be emitted into client code, these combination function/type features will follow the pattern for types. This will mean that the package version will be used by default, and type context will be needed to force a call to the standard library if desired. -#### Retirement from the Package +#### Retirement from the `SwiftPreview` Package -In order to keep the package size and maintainability manageable, implementations will be removed from the package in a version update **1 year** after migration to the standard library. This is a necessary balance between maintainability and convenience. Since the package is open-source, it should be possible to copy source for a specific feature if a user badly needs to keep it while also wanting to upgrade to the latest version of the package _without_ upgrading to the latest compiler. +In order to keep the size of the `SwiftPreview` package manageable, re-exported proposal packages will be removed from the bundling package in a version update **1 year** after migration to the standard library. This is a necessary balance between maintainability and convenience. Since the proposal packages will still be available, it should be possible for users of the `SwiftPreview` package to import an individual pacakge if they need to keep it while also wanting to upgrade to the latest version of the `SwiftPreview` package _without_ upgrading to the latest compiler. ### Testing The standard library currently uses a combination of `StdlibUnittest` tests and raw `lit` tests. This works well, but is unfamiliar to most developers. -The preferred style of test for Swift packages is `XCTest`, and the package should adopt this approach. This has the benefit of familiarity, and integrates well with Xcode. The work to convert tests from this format to lit can be done at the point of migration into the library. +The preferred style of test for Swift packages is `XCTest`, and the preview packages will adopt this approach. This has the benefit of familiarity, and integrates well with Xcode. The work to convert tests from this format to lit can be done at the point of migration into the library. Certain features of `StdlibUnittest` – in particular crashLater and `StdlibCollectionUnittest`'s `checkCollection`, as well as the helper types like the minimal collections – should be ported over to `XCTest` if possible. This would make for an excellent starter bug, or could be done as part of introducing the first full-featured collection to the package that had a significant need for this kind of testing, but are not a requirement for accepting this proposal. ### GYB -There will be no use of [gyb](https://github.com/apple/swift/blob/master/utils/gyb.py) in the package. The need for gyb has been gradually reducing over time as language features like conditional conformance and improved optimization eliminated the need for it. Gyb is a powerful and useful tool, but it can cause code that is difficult to read and write, and does not interact well with Xcode, so runs counter one of the goals of this package: to simplify contribution. +There will be no use of [gyb](https://github.com/apple/swift/blob/master/utils/gyb.py) in the preview packages. The need for gyb has been gradually reducing over time as language features like conditional conformance and improved optimization eliminated the need for it. Gyb is a powerful and useful tool, but it can cause code that is difficult to read and write, and does not interact well with Xcode, so runs counter one of the goals of this package: to simplify contribution. ### Versioning -The fundamental goals of the preview package are somewhat at odds with the usual goals of semantic versioning. Source-breaking changes, including retirements after promoting into the standard library, or source-breaking changes resulting from evolution proposals prior to promotion, will be common. Unlike many packages, the preview package will not converge over time, which normally reduces the frequency of a package's version bumps. +Individual proposal packages will use semantic versioning, with source-breaking changes only allowed at major version revisions. Once accepted via the Swift Evolution process, each proposal package will be published at version `1.0.0`. + +The fundamental goals of the bundling `SwiftPreview` package, on the other hand, are somewhat at odds with the usual goals of semantic versioning. The `SwiftPreview` package will always re-export the latest versions of the most recent proposals, so source-breaking changes, including retirements after promoting into the standard library, or source-breaking changes resulting from evolution proposals prior to promotion, will be common. Unlike many packages, including the individual proposal packages, the `SwiftPreview` package will not converge over time, which normally reduces the frequency of a package's version bumps. -As such, the preview package will remain at major version `0` **permanently**. Minor version bumps will be able to include source-breaking changes. Patch updates should not break source and will just be used for bug fixes. +As such, the `SwiftPreview` package will remain at major version `0` **permanently**. Minor version bumps will be able to include source-breaking changes. Patch updates should not break source and will just be used for bug fixes. The use of a `0` major version at all times will act as a signifier that adopters need to be comfortable with the requirement to continuously update to the latest version of the package. It should not be taken as an indication that the package shouldn't be used for real-world code: the code should be considered production-worthy. But it is a preview, and not source stable. Where practical, deprecated shims could be used to preserve source compatibility between versions, but this may not always be practical. -The decision when to tag minor versions will be made by the release manager for the standard library as chosen by the Swift project lead. +The decision when to tag minor versions, largely whenever proposal packages are added or removed, will be made by the release manager for the standard library as chosen by the Swift project lead. ### Source compatibility -None on Swift itself. The intention of the package is to facilitate long-term source stability by detecting issues with APIs prior to their integration into the library. +None on Swift itself. The intention of the preview packages is to facilitate long-term source stability by detecting issues with APIs prior to their integration into the library. ## Effect on ABI stability -None. The package will not be declared ABI stable. ABI stability will only happen when proposals are migrated to the standard library. This means that the preview package may not be used within binary frameworks. +None. The preview packages will not be declared ABI stable. ABI stability will only happen when proposals are migrated to the standard library. This means that the preview packages may not be used within binary frameworks. Because we do not have cross-module optimization, package implementations should make use of `@inlinable` annotations. However, these annotations should be in the context of source stability only and should be fully reevaluated by the library integrator when stabilizing the ABI. @@ -160,4 +169,6 @@ A previous version of this proposal prescribed a window of time before promoting An alternative to keeping the semver major version at `0` is to not version the package at all. With this approach, the package could only be included by packages by specifying a branch-based dependency. However, this would be too restricting at this time, as a branch-based dependency can only be imported by other branch-based dependencies. This would effectively limit use of the `SwiftPreview` package to top-level applications only. +### A Monolithic `SwiftPreview` Package +A previous version of this proposal described a single preview package that included all of the approved proposal implementations. A monolithic package like that one poses a challenge to adopters attempting to reason about the size and scope of their dependencies. This is particularly an issue for packages like `SwiftPreview`, which is expected to break source compatibility as proposals are added and removed. Providing individual, versioned packages for each approved proposal, in addition to the umbrella `SwiftPreview` package, provides more control to adopters of preview functionality. From ae4560009b1b42f6ee5f6ca511ee401a4f3d968b Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Thu, 21 Nov 2019 19:37:17 -0800 Subject: [PATCH 1344/4563] Update 0264-stdlib-preview-package.md Schedule SE-0264 for second review --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index fe5bba06d2..46600542ed 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,7 +3,7 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Dave Abrahams](https://github.com/dabrahams) -* Status: **Awaiting Review** +* Status: **Scheduled for review (December 2...December 10)** * Implementation: 1. [apple/swift-evolution#1089](https://github.com/apple/swift-evolution/pull/1089) 2. [apple/swift-evolution#1090](https://github.com/apple/swift-evolution/pull/1090) From 32bfaca30487543a48fa80fe9ac2adfdce733a06 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Thu, 21 Nov 2019 22:31:06 -0800 Subject: [PATCH 1345/4563] Update 0264-stdlib-preview-package.md Ted is the review manager for the second run of SE-0264. --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 46600542ed..3f66f2dc42 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -2,7 +2,7 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) -* Review Manager: [Dave Abrahams](https://github.com/dabrahams) +* Review Manager: [Ted Kremenek](https://github.com/tkremenek) * Status: **Scheduled for review (December 2...December 10)** * Implementation: 1. [apple/swift-evolution#1089](https://github.com/apple/swift-evolution/pull/1089) From 138da0fbc58f85919a4e9066a00afb583fda953a Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Fri, 22 Nov 2019 22:51:02 -0800 Subject: [PATCH 1346/4563] Update 0269-implicit-self-explicit-capture.md Accept SE-0269 --- proposals/0269-implicit-self-explicit-capture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0269-implicit-self-explicit-capture.md b/proposals/0269-implicit-self-explicit-capture.md index 11cbf713f6..42779c554f 100644 --- a/proposals/0269-implicit-self-explicit-capture.md +++ b/proposals/0269-implicit-self-explicit-capture.md @@ -3,7 +3,7 @@ * Proposal: [SE-0269](0269-implicit-self-explicit-capture.md) * Author: [Frederick Kellison-Linn](https://github.com/jumhyn) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active review (October 31...November 12)** +* Status: **Accepted** * Implementation: [apple/swift#23934](https://github.com/apple/swift/pull/23934) * Bug: [SR-10218](https://bugs.swift.org/browse/SR-10218) * Forum threads: [Discussion](https://forums.swift.org/t/review-capture-semantics-of-self/22017), [Pitch](https://forums.swift.org/t/allow-implicit-self-in-escaping-closures-when-self-is-explicitly-captured/22590) From 715ee0b969d035d6471e90b3b53b896c6473ae0f Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Fri, 22 Nov 2019 22:52:01 -0800 Subject: [PATCH 1347/4563] Update 0269-implicit-self-explicit-capture.md Add link to review thread. --- proposals/0269-implicit-self-explicit-capture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0269-implicit-self-explicit-capture.md b/proposals/0269-implicit-self-explicit-capture.md index 42779c554f..93d5703fe2 100644 --- a/proposals/0269-implicit-self-explicit-capture.md +++ b/proposals/0269-implicit-self-explicit-capture.md @@ -6,7 +6,7 @@ * Status: **Accepted** * Implementation: [apple/swift#23934](https://github.com/apple/swift/pull/23934) * Bug: [SR-10218](https://bugs.swift.org/browse/SR-10218) -* Forum threads: [Discussion](https://forums.swift.org/t/review-capture-semantics-of-self/22017), [Pitch](https://forums.swift.org/t/allow-implicit-self-in-escaping-closures-when-self-is-explicitly-captured/22590) +* Forum threads: [Discussion](https://forums.swift.org/t/review-capture-semantics-of-self/22017), [Pitch](https://forums.swift.org/t/allow-implicit-self-in-escaping-closures-when-self-is-explicitly-captured/22590), [Review thread](https://forums.swift.org/t/se-0269-increase-availability-of-implicit-self-in-escaping-closures-when-reference-cycles-are-unlikely-to-occur/30376) ## Introduction From 497f3166c9baac4480a2400a795f2ddfdf67cd5a Mon Sep 17 00:00:00 2001 From: "Yilei (Dolee) Yang" Date: Fri, 29 Nov 2019 08:41:51 -0800 Subject: [PATCH 1348/4563] Fix a typo in the example code. (#1096) --- proposals/0258-property-wrappers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0258-property-wrappers.md b/proposals/0258-property-wrappers.md index e9b1ab56b9..0daf17aa17 100644 --- a/proposals/0258-property-wrappers.md +++ b/proposals/0258-property-wrappers.md @@ -1430,7 +1430,7 @@ public struct Observable { self.stored = wrappedValue } - public func register(_ observed: Observable) { + public func register(_ observed: Observed) { self.observed = observed } From 4733d6a971f535b5296f2213f7d30bb493fa2424 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Mon, 2 Dec 2019 10:48:16 -0800 Subject: [PATCH 1349/4563] Update 0264-stdlib-preview-package.md SE-0264 is now back in active review. --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 3f66f2dc42..c1231197e0 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,7 +3,7 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Scheduled for review (December 2...December 10)** +* Status: **Active review (December 2...December 10)** * Implementation: 1. [apple/swift-evolution#1089](https://github.com/apple/swift-evolution/pull/1089) 2. [apple/swift-evolution#1090](https://github.com/apple/swift-evolution/pull/1090) From 4b943a4fd35345bb148e59d324bf3a4d99e7ed4d Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Mon, 2 Dec 2019 18:25:39 -0800 Subject: [PATCH 1350/4563] Update 0264-stdlib-preview-package.md Add link to previous revision. --- proposals/0264-stdlib-preview-package.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index c1231197e0..4faca22905 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -9,6 +9,7 @@ 2. [apple/swift-evolution#1090](https://github.com/apple/swift-evolution/pull/1090) 3. [apple/swift-evolution#1091](https://github.com/apple/swift-evolution/pull/1091) * Pitch Discussion: [Pitch: Standard Library Preview Package](https://forums.swift.org/t/pitch-standard-library-preview-package/27202) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/1547e503376bca2c64c57c96b1f87d5e01a094c3/proposals/0264-stdlib-preview-package.md) * Previous Review: [SE-0264 — Standard Library Preview Package](https://forums.swift.org/t/se-0264-standard-library-preview-package/29068) * Previous Decision: [Returned for Revision](https://forums.swift.org/t/returned-for-revision-se-0264-standard-library-preview-package/29865) From ddf57fa96d87d79d263e70220a618c36889ae5df Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 3 Dec 2019 18:12:36 +0000 Subject: [PATCH 1351/4563] [SE-0271] Update status: "Accepted with revisions" --- proposals/0271-package-manager-resources.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0271-package-manager-resources.md b/proposals/0271-package-manager-resources.md index 5f25214920..47e4aa1f81 100644 --- a/proposals/0271-package-manager-resources.md +++ b/proposals/0271-package-manager-resources.md @@ -3,7 +3,8 @@ * Proposal: [SE-0271](0271-package-manager-resources.md) * Authors: [Anders Bertelrud](https://github.com/abertelrud), [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Boris Buegling](https://github.com/neonichu) -* Status: **Active review (November 12...November 19)** +* Status: **Accepted with revisions** +* Decision Notes: [Rationale](https://forums.swift.org/t/accepted-with-modifications-se-0271-package-manager-resources/31021) ## Introduction From eb3b122a11f77b42131558c34605aba1accf068b Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 3 Dec 2019 18:18:54 +0000 Subject: [PATCH 1352/4563] [SE-0272] Update status: "Returned for revision" --- proposals/0272-swiftpm-binary-dependencies.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0272-swiftpm-binary-dependencies.md b/proposals/0272-swiftpm-binary-dependencies.md index 10e86a8ab5..0ef1163368 100644 --- a/proposals/0272-swiftpm-binary-dependencies.md +++ b/proposals/0272-swiftpm-binary-dependencies.md @@ -3,7 +3,8 @@ * Proposal: [SE-0272](0272-swiftpm-binary-dependencies.md) * Authors: [Braden Scothern](https://github.com/bscothern), [Daniel Dunbar](https://github.com/ddunbar), [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **Active review (November 13...November 20)** +* Status: **Returned for revision** +* Decision Notes: [Rationale](https://forums.swift.org/t/returned-for-revision-se-0272-package-manager-binary-dependencies/30994) ## Contents From 8f49dfe1a7590b55be8d4d1e2061cbb753ea04a4 Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Thu, 5 Dec 2019 23:44:29 +0000 Subject: [PATCH 1353/4563] [SE-0268] Amend proposal to include the deprecation of implicit oldValue (#1097) * [SE-0268] Amend proposal to include the deprecation of implicit oldValue * Update 0268-didset-semantics.md * Kick off re-review --- proposals/0268-didset-semantics.md | 34 +++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/proposals/0268-didset-semantics.md b/proposals/0268-didset-semantics.md index 7429adedc1..28f74205a7 100644 --- a/proposals/0268-didset-semantics.md +++ b/proposals/0268-didset-semantics.md @@ -3,8 +3,9 @@ * Proposal: [SE-0268](0268-didset-semantics.md) * Author: [Suyash Srijan](https://www.github.com/theblixguy) * Review Manager: [Ben Cohen](https://www.github.com/airspeedswift) -* Status: **Active Review (21-31 October 2019)** +* Status: **Active Review (5-9 December 2019)** * Implementation: [apple/swift#26632](https://github.com/apple/swift/pull/26632) +* Bug: [SR-5982](https://bugs.swift.org/browse/SR-5982) ## Introduction @@ -120,7 +121,32 @@ foo.baz = 2 This applies to a `didSet` on an overridden property as well - the call to the superclass getter will be skipped if the `oldValue` is not referenced in the body of the overridden property's `didSet`. -This also resolves some pending bugs such as [SR-11297](https://bugs.swift.org/browse/SR-11297), [SR-11280](https://bugs.swift.org/browse/SR-11280) and [SR-5982](https://bugs.swift.org/browse/SR-5982). +This also resolves some pending bugs such as [SR-11297](https://bugs.swift.org/browse/SR-11297) and [SR-11280](https://bugs.swift.org/browse/SR-11280). + +In order to avoid confusion as to when the getter is called or not, the implicit `oldValue` is now deprecated. If we do refer to the `oldValue` in the `didSet` body, then the compiler will generate a warning along with a fix-it to insert `oldValue` in parenthesis i.e. `didSet(oldValue) { ... }`. For example: + +```swift +class Foo { + var bar = 0 { // Before + didSet { + guard oldValue != bar else { return } // This will now trigger a compiler warning to + // remind the user that implicit oldValue is + // now deprecated, along with a fix-it to + // insert 'oldValue' in parenthesis on the + // `didSet`. + tableView.reloadData() + } + } + + var bar = 0 { // After + didSet(oldValue) { + guard oldValue != bar else { return } // Okay + tableView.reloadData() + } + } +} +``` + As a bonus, if the property has a "simple" `didSet` and no `willSet`, then we could allow for modifications to happen in-place. For example: @@ -151,7 +177,7 @@ This will provide a nice performance boost in some cases (for example, in the ea This does not break source compatibility, _unless_ someone is explicitly relying on the current buggy behavior (i.e. the property's getter being called even if the `oldValue` isn't referenced). However, I think the possibility of that is very small. -However, it is possible to preserve the old behavior by either: +It would still be possible to preserve the old behavior by either: 1. Explicitly providing the `oldValue` argument to `didSet`: ```swift @@ -187,5 +213,3 @@ This does not affect API resilience - library authors can freely switch between ## Future Directions We can apply the same treatment to `willSet` i.e. not pass the `newValue` if it does not refer to it in its body, although it wouldn't provide any real benefit as not passing `newValue` to `willSet` does not avoid anything, where as not passing `oldValue` to `didSet` avoids loading it. - -We can also deprecate the implicit `oldValue` and request users to explicitly provide `oldValue` in parenthesis (`didSet(oldValue) { ... }`) if they want to use it in the body of the observer. This will make the new behavior more obvious and self-documenting. From 9b5957c00e7483ab8664afe921f989ed1394a666 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 9 Dec 2019 02:15:11 +0000 Subject: [PATCH 1354/4563] [SE-0270] Update status: "Returned for revision" --- proposals/0270-rangeset-and-collection-operations.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index 871a5d1c59..8071490769 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -2,9 +2,10 @@ * Proposal: [SE-0270](0270-rangeset-and-collection-operations.md) * Author: [Nate Cook](https://github.com/natecook1000) -* Review Manager: Dave Abrahams -* Status: **Active review (November 11...November 21)** +* Review Manager: [Dave Abrahams](https://github.com/dabrahams) +* Status: **Returned for revision** * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) +* Decision Notes: [Rationale](https://forums.swift.org/t/returned-for-revision-se-0270-add-collection-operations-on-noncontiguous-elements/31484) ## Introduction From e725553b5e49869e2fe36d27d17811ac32335126 Mon Sep 17 00:00:00 2001 From: hfhbd <22521688+hfhbd@users.noreply.github.com> Date: Mon, 9 Dec 2019 20:36:30 +0100 Subject: [PATCH 1355/4563] [SE-0264] Fix typo (#1100) changed pacakge to package --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 4faca22905..7c09bae19b 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -124,7 +124,7 @@ Since types cannot be emitted into client code, these combination function/type #### Retirement from the `SwiftPreview` Package -In order to keep the size of the `SwiftPreview` package manageable, re-exported proposal packages will be removed from the bundling package in a version update **1 year** after migration to the standard library. This is a necessary balance between maintainability and convenience. Since the proposal packages will still be available, it should be possible for users of the `SwiftPreview` package to import an individual pacakge if they need to keep it while also wanting to upgrade to the latest version of the `SwiftPreview` package _without_ upgrading to the latest compiler. +In order to keep the size of the `SwiftPreview` package manageable, re-exported proposal packages will be removed from the bundling package in a version update **1 year** after migration to the standard library. This is a necessary balance between maintainability and convenience. Since the proposal packages will still be available, it should be possible for users of the `SwiftPreview` package to import an individual package if they need to keep it while also wanting to upgrade to the latest version of the `SwiftPreview` package _without_ upgrading to the latest compiler. ### Testing From d61957df1af9fb283da8c0b3108dbea5e3f3b732 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Thu, 12 Dec 2019 19:42:37 -0600 Subject: [PATCH 1356/4563] Revise SE-270 proposal for re-review. These changes are in response to feedback from the SE-270 proposal review. Removed the `RangeSet` methods that used a collection to convert individual indexes or range expressions into concrete ranges before performing operations. Added parameter labels to `RangeSet.insert` and `remove` to reduce confusion around semantics. Removed the `shift...` methods; these will be considered in a future proposal. --- ...0270-rangeset-and-collection-operations.md | 368 ++++-------------- 1 file changed, 85 insertions(+), 283 deletions(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index 8071490769..68b9d86b35 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -2,17 +2,16 @@ * Proposal: [SE-0270](0270-rangeset-and-collection-operations.md) * Author: [Nate Cook](https://github.com/natecook1000) -* Review Manager: [Dave Abrahams](https://github.com/dabrahams) -* Status: **Returned for revision** +* Status: **Awaiting review** * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) -* Decision Notes: [Rationale](https://forums.swift.org/t/returned-for-revision-se-0270-add-collection-operations-on-noncontiguous-elements/31484) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/9b5957c00e7483ab8664afe921f989ed1394a666/proposals/0270-rangeset-and-collection-operations.md) +* Previous Review: [SE-0270: Add Collection Operations on Noncontiguous Elements](https://forums.swift.org/t/se-0270-add-collection-operations-on-noncontiguous-elements/30691) +* Previous Decision:: [Returned for revision](https://forums.swift.org/t/returned-for-revision-se-0270-add-collection-operations-on-noncontiguous-elements/31484) ## Introduction We can use a `Range` to refer to a group of consecutive positions in a collection, but the standard library doesn't currently provide a way to refer to discontiguous positions in an arbitrary collection. I propose the addition of a `RangeSet` type that can store any number of positions, along with collection algorithms that operate on those positions. -Swift forums discussion: https://forums.swift.org/t/pitch-add-rangeset-and-related-collection-operations/29961 - ## Motivation There are varied uses for tracking multiple elements in a collection, such as maintaining the selection in a list of items, or refining a filter or search result set after getting more input from a user. @@ -27,7 +26,7 @@ This proposal adds a `RangeSet` type for representing multiple, noncontiguous ra var numbers = Array(1...15) // Find the indices of all the even numbers -let indicesOfEvens = numbers.indices(where: { $0.isMultiple(of: 2) }) +let indicesOfEvens = numbers.ranges(where: { $0.isMultiple(of: 2) }) // Perform an operation with just the even numbers let sumOfEvens = numbers[indicesOfEvens].reduce(0, +) @@ -41,183 +40,63 @@ let rangeOfEvens = numbers.gather(indicesOfEvens, at: numbers.startIndex) // Reset `numbers` numbers = Array(1...15) -// You can also build range sets by hand using array literals... -let notTheMiddle: RangeSet = [0..<5, 10..<15] +// You can also build range sets by hand... +let notTheMiddle = RangeSet([0..<5, 10..<15]) print(Array(numbers[notTheMiddle])) // Prints [1, 2, 3, 4, 5, 11, 12, 13, 14, 15] // ...or by using set operations let smallEvens = indicesOfEvens.intersection( - numbers.indices(where: { $0 < 10 })) + numbers.ranges(where: { $0 < 10 })) print(Array(numbers[smallEvens])) // Prints [2, 4, 6, 8] ``` -These are just a few examples; the complete proposal includes operations like inverting a range set and inserting and removing ranges, which are covered in the next section. +These are just a few examples; the complete proposal includes operations like inverting a range set and adding and removing ranges, which are covered in the next section. ## Detailed design `RangeSet` is generic over any `Comparable` type, and supports fast containment checks for ranges and individual values, as well as adding and removing ranges of that type. ```swift -/// A set of ranges of any comparable value. +/// A set of values of any comparable value, represented by ranges. public struct RangeSet { /// Creates an empty range set. public init() {} - /// Creates a range set containing the given range. + /// Creates a range set containing the values in the given range. public init(_ range: Range) - /// Creates a range set containing the given ranges. + /// Creates a range set containing the values in the given ranges. public init(_ ranges: S) where S.Element == Range /// A Boolean value indicating whether the range set is empty. public var isEmpty: Bool { get } /// Returns a Boolean value indicating whether the given value is - /// contained by the ranges in the range set. + /// contained by the range set. /// /// - Complexity: O(log *n*), where *n* is the number of ranges in the /// range set. public func contains(_ value: Bound) -> Bool - /// Returns a Boolean value indicating whether the given range is - /// contained by the ranges in the range set. - /// - /// This method returns `true` if and only if one of the ranges that - /// comprises the range set fully contains `range`. This example shows - /// ranges that are and are not contained by a range set: - /// - /// let set: RangeSet = [0..<5, 10..<15] - /// print(set.contains(1..<4)) - /// // Prints "true" - /// print(set.contains(4..<7)) - /// // Prints "false" - /// print(set.contains(4..<12)) - /// // Prints "false" + /// Adds the values represented by the given range to the range set. /// - /// - Complexity: O(log *n*), where *n* is the number of ranges in the - /// range set. - public func contains(_ range: Range) -> Bool - - /// Inserts the given range into the range set. - public mutating func insert(_ range: Range) + /// If `range` overlaps or adjoins any existing ranges in the set, the + /// ranges are merged together. Empty ranges are ignored. + public mutating func insert(contentsOf range: Range) - /// Removes the given range from the range set. - public mutating func remove(_ range: Range) + /// Removes the given range of values from the range set. + /// + /// The values represented by `range` are removed from this set. This may + /// result in one or more ranges being truncated or removed, depending on + /// the overlap between `range` and the set's existing ranges. + public mutating func remove(contentsOf range: Range) } ``` `RangeSet` conforms to `Equatable`, to `CustomStringConvertible`, and, when its `Bound` type conforms to `Hashable`, to `Hashable`. `RangeSet` also has `ExpressibleByArrayLiteral` conformance, using arrays of ranges as its literal type. -#### `SetAlgebra`-like methods - -`RangeSet` implements a subset of the `SetAlgebra` protocol. Because of its efficient representation for contiguous ranges, there is no way to insert a single element into a `RangeSet` . - -```swift -extension RangeSet { - public func union(_ other: RangeSet) -> RangeSet - public mutating func formUnion(_ other: RangeSet) - - public func intersection(_ other: RangeSet) -> RangeSet - public mutating func formIntersection(_ other: RangeSet) - - public func symmetricDifference(_ other: RangeSet) -> RangeSet - public mutating func formSymmetricDifference(_ other: RangeSet) - - public func subtracting(_ other: RangeSet) -> RangeSet - public mutating func subtract(_ other: RangeSet) - - public func isSubset(of other: RangeSet) -> Bool - public func isSuperset(of other: RangeSet) -> Bool - public func isStrictSubset(of other: RangeSet) -> Bool - public func isStrictSuperset(of other: RangeSet) -> Bool -} -``` - -#### Collection APIs - -Working with collection indices is a primary use case for `RangeSet`. To support this, `RangeSet` includes initializers and insertion and removal methods for working with individual indices and range expressions, as well as a way to "invert" a range set with respect to a collection. - -Calling one of these methods with an individual index is equivalent to calling the related `RangeSet` method with a range that contains only that index. In this example, the two ways of inserting a range containing `indexOfI` into the range set are equivalent: - -```swift -let str = "Fresh cheese in a breeze" -var set = str.indices(where: { $0 == "e" }) -let indexOfI = str.firstIndex(of: "i")! - -// (1) Creating a range, then inserting it into 'set' -let rangeOfI = indexOfI ..< str.index(after: indexOfI) -set.insert(rangeOfI) - -// (2) Inserting the index with the convenience method -set.insert(indexOfI, within: str) -``` - -The initializers and methods are listed below: - -```swift -extension RangeSet { - /// Creates a new range set containing a range that contains only the - /// specified index in the given collection. - public init(_ index: Bound, within collection: C) - where C: Collection, C.Index == Bound - - /// Creates a new range set containing ranges that contain only the - /// specified indices in the given collection. - public init(_ indices: S, within collection: C) - where S: Sequence, C: Collection, S.Element == C.Index, C.Index == Bound - - /// Creates a new range set containing the range represented by the - /// specified range expression. - public init(_ range: R, within collection: C) - where C: Collection, C.Index == Bound, R: RangeExpression, R.Bound == Bound - - /// Inserts a range that contains only the specified index into the range - /// set. - public mutating func insert(_ index: Bound, within collection: C) - where C: Collection, C.Index == Bound - - /// Inserts the range represented by the specified range expression into - /// the range set. - public mutating func insert(_ range: R, within collection: C) - where C: Collection, C.Index == Bound, R: RangeExpression, R.Bound == Bound - - /// Removes a range that contains only the specified index from the range - /// set. - public mutating func remove(_ index: Bound, within collection: C) - where C: Collection, C.Index == Bound - - /// Removes the range represented by the specified range expression from - /// the range set. - public mutating func remove(_ range: R, within collection: C) - where C: Collection, C.Index == Bound, R: RangeExpression, R.Bound == Bound - - /// Returns a range set that represents all the elements in the given - /// collection that aren't represented by this range set. - /// - /// The following example finds the indices of the vowels in a string, and - /// then inverts the range set to find the non-vowels parts of the string. - /// - /// let str = "The rain in Spain stays mainly in the plain." - /// let vowels = "aeiou" - /// let vowelIndices = str.indices(where: { vowels.contains($0) }) - /// print(String(str[vowelIndices])) - /// // Prints "eaiiaiaaiieai" - /// - /// let nonVowelIndices = vowelIndices.inverted(within: str) - /// print(String(str[nonVowelIndices])) - /// // Prints "Th rn n Spn stys mnly n th pln." - /// - /// - Parameter collection: The collection that the range set is relative - /// to. - /// - Returns: A new range set that represents the elements in `collection` - /// that aren't represented by this range set. - public func inverted(within collection: C) -> RangeSet - where C: Collection, C.Index == Bound -} -``` - #### Accessing Underlying Ranges and Individual Indices `RangeSet` provides access to its ranges as a random-access collection via the `ranges` property. @@ -239,27 +118,52 @@ extension RangeSet { The ranges that are exposed through this collection are always in ascending order, are never empty, and never overlap or adjoin. For this reason, inserting an empty range or a range that is subsumed by the ranges already in the set has no effect on the range set. Inserting a range that adjoins an existing range simply extends that range. ```swift -var set: RangeSet = [0..<5, 10..<15] -set.insert(7..<7) -set.insert(11.<14) +var set = RangeSet([0..<5, 10..<15]) +set.insert(contentsOf: 7..<7) +set.insert(contentsOf: 11.<14) // Array(set.ranges) == [0..<5, 10..<15] -set.insert(5..<7) +set.insert(contentsOf: 5..<7) // Array(set.ranges) == [0..<7, 10..<15] -set.insert(7..<10) +set.insert(contentsOf: 7..<10) // Array(set.ranges) == [0..<15] ``` +#### `SetAlgebra`-like methods + +`RangeSet` implements a subset of the `SetAlgebra` protocol. + +```swift +extension RangeSet { + public func union(_ other: RangeSet) -> RangeSet + public mutating func formUnion(_ other: RangeSet) + + public func intersection(_ other: RangeSet) -> RangeSet + public mutating func formIntersection(_ other: RangeSet) + + public func symmetricDifference(_ other: RangeSet) -> RangeSet + public mutating func formSymmetricDifference(_ other: RangeSet) + + public func subtracting(_ other: RangeSet) -> RangeSet + public mutating func subtract(_ other: RangeSet) + + public func isSubset(of other: RangeSet) -> Bool + public func isSuperset(of other: RangeSet) -> Bool + public func isStrictSubset(of other: RangeSet) -> Bool + public func isStrictSuperset(of other: RangeSet) -> Bool +} +``` + #### Storage -`RangeSet` will store its ranges in a custom type that will optimize the storage for known, simple `Bound` types. This custom type will avoid allocating extra storage for common cases of empty or single-range range sets. +`RangeSet` stores its ranges in a custom type that will optimize the storage for known, simple `Bound` types. This custom type will avoid allocating extra storage for common cases of empty or single-range range sets. ### New `Collection` APIs #### Finding multiple elements -Akin to the `firstIndex(...)` and `lastIndex(...)` methods, this proposal introduces `indices(where:)` and `indices(of:)` methods that return a range set with the indices of all matching elements in a collection. +Akin to the `firstIndex(...)` and `lastIndex(...)` methods, this proposal introduces `ranges(where:)` and `ranges(of:)` methods that return a range set with the indices of all matching elements in a collection. ```swift extension Collection { @@ -270,11 +174,11 @@ extension Collection { /// /// let str = "Fresh cheese in a breeze" /// let vowels: Set = ["a", "e", "i", "o", "u"] - /// let allTheVowels = str.indices(where: { vowels.contains($0) }) + /// let allTheVowels = str.ranges(where: { vowels.contains($0) }) /// // str[allTheVowels].count == 9 /// /// - Complexity: O(*n*), where *n* is the length of the collection. - public func indices(where predicate: (Element) throws -> Bool) rethrows + public func ranges(where predicate: (Element) throws -> Bool) rethrows -> RangeSet } @@ -286,11 +190,11 @@ extension Collection where Element: Equatable { /// particular letter occurs in a string. /// /// let str = "Fresh cheese in a breeze" - /// let allTheEs = str.indices(of: "e") + /// let allTheEs = str.ranges(of: "e") /// // str[allTheEs].count == 7 /// /// - Complexity: O(*n*), where *n* is the length of the collection. - public func indices(of element: Element) -> RangeSet + public func ranges(of element: Element) -> RangeSet } ``` @@ -323,7 +227,8 @@ public struct DiscontiguousSlice: Collection { /// The collection that the indexed collection wraps. public var base: Base { get set } - /// The set of index ranges that are available through this collection. + /// The set of index ranges that are available through this indexing + /// collection. public var ranges: RangeSet { get set } /// A position in an `DiscontiguousSlice`. @@ -340,64 +245,9 @@ public struct DiscontiguousSlice: Collection { `DiscontiguousSlice` conforms to `Collection`, and conditionally conforms to `BidirectionalCollection` and `MutableCollection` if its base collection conforms. -#### Moving elements - -Within a mutable collection, you can gather the elements represented by a range set, inserting them in a contiguous range before the element at a specific index, and otherwise preserving element order. This proposal also adds a method for gathering all the elements matched by a predicate, and methods for shifting a contiguous region of elements, denoted by a single index, range, or range expression, to a specific insertion point. - -The elements that you shift or gather don't necessarily end up at exactly the specified insertion point, because other elements slide over to fill gaps left by the elements that move. For that reason, these gathering and shifting methods return the new range or index of the elements that are moved. - -To visualize these operations, divide a collection into two parts, before and after the insertion point. The elements to gather or shift are collected in order in that gap, and the resulting range (or index, for single element moves) is returned. As an example, consider a shift from a range at the beginning of an array of letters to a position further along in the array: - -```swift -var array = ["a", "b", "c", "d", "e", "f", "g"] -let newSubrange = array.shift(from: 0..<3, to: 5) -// array == ["d", "e", "a", "b", "c", "f", "g"] -// newSubrange = 2..<5 - -// ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐ -// │ a │ b │ c │ d │ e │ f │ g │ -// └─────┴─────┴─────┴─────┴─────┴─────┴─────┘ -// ^^^^^^^^^^^^^^^^^ ^ -// source insertion -// point -// -// ┌─────┬─────┬─────┐┌─────┬─────┐┌─────┬─────┐ -// │ a │ b │ c ││ d │ e ││ f │ g │ -// └─────┴─────┴─────┘└─────┴─────┘└─────┴─────┘ -// ^^^^^^^^^^^^^^^^^ ^ -// source insertion -// point -// -// ┌─────┬─────┐┌─────┬─────┬─────┐┌─────┬─────┐ -// │ d │ e ││ a │ b │ c ││ f │ g │ -// └─────┴─────┘└─────┴─────┴─────┘└─────┴─────┘ -// ^^^^^^^^^^^^^^^^^ -// newSubrange == 2..<5 -``` - -When shifting a single element, this can mean that the element ends up at the insertion point (when moving backward), or ends up at a position one before the insertion point (when moving forward, because the elements in between move forward to make room). +#### Gathering elements -```swift -var array = ["a", "b", "c", "d", "e", "f", "g"] -let newPosition = array.shift(from: 1, to: 5) -// array == ["a", "c", "d", "e", "b", "f", "g"] -// newPosition == 4 - -// ┌─────┬─────┬─────┬─────┬─────┐┌─────┬─────┐ -// │ a │ b │ c │ d │ e ││ f │ g │ -// └─────┴─────┴─────┴─────┴─────┘└─────┴─────┘ -// ^ ^ -// source insertion -// point -// -// ┌─────┬─────┬─────┬─────┐┌─────┐┌─────┬─────┐ -// │ a │ c │ d │ e ││ b ││ f │ g │ -// └─────┴─────┴─────┴─────┘└─────┘└─────┴─────┘ -// ^ -// newPosition == 4 -``` - -The new gathering and shifting methods are listed below. +Within a mutable collection, you can gather the elements represented by a range set, moving them to be in a contiguous range before the element at a specific index, and otherwise preserving element order. This proposal also adds a method for gathering all the elements matched by a predicate. When gathering elements, other elements slide over to fill gaps left by the elements that move. For that reason, these gathering methods return the new range of the elements that are moved. ```swift extension MutableCollection { @@ -408,7 +258,7 @@ extension MutableCollection { /// them between `"i"` and `"j"`. /// /// var letters = Array("ABCdeFGhijkLMNOp") - /// let uppercase = letters.indices(where: { $0.isUppercase }) + /// let uppercase = letters.ranges(where: { $0.isUppercase }) /// let rangeOfUppercase = letters.gather(uppercase, at: 10) /// // String(letters) == "dehiABCFGLMNOjkp" /// // rangeOfUppercase == 4..<13 @@ -446,78 +296,12 @@ extension MutableCollection { public mutating func gather( at insertionPoint: Index, where predicate: (Element) -> Bool ) -> Range - - /// Shifts the elements in the given range to just before the element at - /// the specified index. - /// - /// - Parameters: - /// - range: The range of the elements to move. - /// - insertionPoint: The index to use as the insertion point for the - /// elements. `insertionPoint` must be a valid index of the collection. - /// - Returns: The new bounds of the moved elements. - /// - /// - Returns: The new bounds of the moved elements. - /// - Complexity: O(*n*) where *n* is the length of the collection. - @discardableResult - public mutating func shift( - from range: Range, to insertionPoint: Index - ) -> Range - - /// Shifts the elements in the given range expression to just before the - /// element at the specified index. - /// - /// - Parameters: - /// - range: The range of the elements to move. - /// - insertionPoint: The index to use as the insertion point for the - /// elements. `insertionPoint` must be a valid index of the collection. - /// - Returns: The new bounds of the moved elements. - /// - /// - Complexity: O(*n*) where *n* is the length of the collection. - @discardableResult - public mutating func shift( - from range: R, to insertionPoint: Index - ) -> Range where R.Bound == Index - - /// Moves the element at the given index to just before the element at the - /// specified index. - /// - /// This method moves the element at position `i` to immediately before - /// `insertionPoint`. This example shows moving elements forward and - /// backward in an array of integers. - /// - /// var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - /// let newIndexOfNine = numbers.shift(from: 9, to: 7) - /// // numbers == [0, 1, 2, 3, 4, 5, 6, 9, 7, 8, 10] - /// // newIndexOfNine == 7 - /// - /// let newIndexOfOne = numbers.shift(from: 1, to: 4) - /// // numbers == [0, 2, 3, 1, 4, 5, 6, 9, 7, 8, 10] - /// // newIndexOfOne == 3 - /// - /// To move an element to the end of a collection, pass the collection's - /// `endIndex` as `insertionPoint`. - /// - /// numbers.shift(from: 0, to: numbers.endIndex) - /// // numbers == [2, 3, 1, 4, 5, 6, 9, 7, 8, 10, 0] - /// - /// - Parameters: - /// - source: The index of the element to move. `source` must be a valid - /// index of the collection that isn't `endIndex`. - /// - insertionPoint: The index to use as the destination of the element. - /// `insertionPoint` must be a valid index of the collection. - /// - Returns: The resulting index of the element that began at `source`. - /// - /// - Complexity: O(*n*) where *n* is the length of the collection. - @discardableResult - public mutating func shift( - from source: Index, to insertionPoint: Index - ) -> Index } ``` #### Removing elements -Within a range-replaceable collection, you can remove the elements represented by a range set. `removeAll(at:)` is a new `RangeReplaceableCollection` requirement with a default implementation, along with additional overload for collections that also conform to `MutableCollection`. +Within a range-replaceable collection, you can remove the elements represented by a range set. `removeAll(at:)` is a new `RangeReplaceableCollection` requirement with a default implementation, along with an overload for collections that also conform to `MutableCollection`. ```swift extension RangeReplaceableCollection { @@ -528,7 +312,7 @@ extension RangeReplaceableCollection { /// /// var str = "The rain in Spain stays mainly in the plain." /// let vowels: Set = ["a", "e", "i", "o", "u"] - /// let vowelIndices = str.indices(where: { vowels.contains($0) }) + /// let vowelIndices = str.ranges(where: { vowels.contains($0) }) /// /// str.removeAll(at: vowelIndices) /// // str == "Th rn n Spn stys mnly n th pln." @@ -549,7 +333,7 @@ extension Collection { /// /// let str = "The rain in Spain stays mainly in the plain." /// let vowels: Set = ["a", "e", "i", "o", "u"] - /// let vowelIndices = str.indices(where: { vowels.contains($0) }) + /// let vowelIndices = str.ranges(where: { vowels.contains($0) }) /// /// let disemvoweled = str.removingAll(at: vowelIndices) /// print(String(disemvoweled)) @@ -561,6 +345,20 @@ extension Collection { } ``` +#### `range(at:)` helper method + +The proposal also adds an individual `Collection` method to get the range for an individual index. This streamlines adding individual indices to or removing them from a `RangeSet`. + +```swift +extension Collection { + /// Returns a range that contains the single given index with respect to + /// this collection. + public func range(at position: Index) -> Range { + position.. Date: Fri, 13 Dec 2019 12:54:08 -0500 Subject: [PATCH 1357/4563] Update 0270-rangeset-and-collection-operations.md Take over review management and put SE-0270 back in review. --- proposals/0270-rangeset-and-collection-operations.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index 68b9d86b35..511cd51ae3 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -2,7 +2,8 @@ * Proposal: [SE-0270](0270-rangeset-and-collection-operations.md) * Author: [Nate Cook](https://github.com/natecook1000) -* Status: **Awaiting review** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (December 13th...19th, 2019)** * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/9b5957c00e7483ab8664afe921f989ed1394a666/proposals/0270-rangeset-and-collection-operations.md) * Previous Review: [SE-0270: Add Collection Operations on Noncontiguous Elements](https://forums.swift.org/t/se-0270-add-collection-operations-on-noncontiguous-elements/30691) From 2b0321625a84985f02cb544f5e8a493170f360bd Mon Sep 17 00:00:00 2001 From: David Hart Date: Sat, 14 Dec 2019 01:06:29 +0100 Subject: [PATCH 1358/4563] Package Manager Conditional Target Dependencies (#1098) * First iteration of the conditional target dependencies proposal * Resolve PR comments * Update and rename NNNN-swiftpm-conditional-target-dependencies.md to 0273-swiftpm-conditional-target-dependencies.md --- ...swiftpm-conditional-target-dependencies.md | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 proposals/0273-swiftpm-conditional-target-dependencies.md diff --git a/proposals/0273-swiftpm-conditional-target-dependencies.md b/proposals/0273-swiftpm-conditional-target-dependencies.md new file mode 100644 index 0000000000..377e41d3ac --- /dev/null +++ b/proposals/0273-swiftpm-conditional-target-dependencies.md @@ -0,0 +1,112 @@ +# Package Manager Conditional Target Dependencies + +* Proposal: [SE-0273](0273-swiftpm-conditional-target-dependencies.md) +* Authors: [David Hart](https://github.com/hartbit) +* Review Manager: [Boris Buegling](https://github.com/neonichu) +* Status: **Active review (12 13...12 20)** +* Implementation: [apple/swift-package-manager#2428](https://github.com/apple/swift-package-manager/pull/2428) + +## Introduction + +This proposal introduces the ability for Swift package authors to conditionalize target dependencies on platform and configuration with a similar syntax to the one introduced in [SE-0238](0238-package-manager-build-settings.md) for build settings. This gives developers more flexibility to describe complex target dependencies to support multiple platforms or different configuration environments. + +## Motivation + +This proposal resolves two use cases that the current version of the Package Manager doesn't support very well. In the first scenario, packages that span multiple platforms may need to depend on different libraries depending on the platform, as can be the case for low-level, platform-specific code. In a second scenario, packages may want to link against libraries only in certain configurations, for example when importing debug libraries, which do not make sense to build and link in release builds, or when importing instrumentation logic, which only make sense in release builds when the developer can not benefit from debugging. + +This proposal attempts to bring solutions to those use cases by allowing package authors to define under what build environments dependencies need to be built and linked against targets. + +## Proposed solution + +To allow package authors to append conditions to target dependencies, we introduce new APIs to the `Package.swift` manifest library. The `.target`, `.product`, and `.byName` will be optionally configurable with a `condition` argument of the same format as for build settings, to specify the platforms and configuration under which that dependency should be enabled. For example: + +```swift +// swift-tools-version:5.3 + +import PackageDescription + +let package = Package( + name: "BestPackage", + dependencies: [ + .package(url: "https://github.com/pureswift/bluetooth", .branch("master")), + .package(url: "https://github.com/pureswift/bluetoothlinux", .branch("master")), + ], + targets: [ + .target( + name: "BestExecutable", + dependencies: [ + .product(name: "Bluetooth", condition: .when(platforms: [.macOS])), + .product(name: "BluetoothLinux", condition: .when(platforms: [.linux])), + .target(name: "DebugHelpers", condition: .when(configuration: .debug)), + ] + ), + .target(name: "DebugHelpers") + ] +) +``` + +It is important to note that this proposal has no effect on dependency resolution, but only affects which targets are built and linked against each other during compilation. In the previous example, both the `Bluetooth` and `BluetoothLinux` packages will be resolved regardless of the platform, but when building, the Package Manager will avoid building the disabled dependency and will link the correct library depending on the platform. It will also build and link against the `DebugHelpers` target, but only in debug builds. + +## Detailed design + +### New `PackageDescription` API + +All the cases of the `Target.Dependency` enum will gain a new optional `BuildSettingCondition` argument. The current static factory functions for initializing those enums will be obsoleted in the version of the tools this proposal will appear in, and new functions will take their place, introducing a new optional argument: + +```swift +extension Target.Dependency { + /// Creates a dependency on a target in the same package. + /// + /// - Parameters: + /// - name: The name of the target. + /// - condition: The condition under which the dependency is exercised. + @available(_PackageDescription, introduced: 5.3) + public static func target( + name: String, + condition: BuildSettingCondition? = nil + ) -> Target.Dependency { + // ... + } + + /// Creates a dependency on a product from a package dependency. + /// + /// - Parameters: + /// - name: The name of the product. + /// - package: The name of the package. + /// - condition: The condition under which the dependency is exercised. + @available(_PackageDescription, introduced: 5.3) + public static func product( + name: String, + package: String? = nil, + condition: BuildSettingCondition? = nil + ) -> Target.Dependency { + // ... + } + + /// Creates a by-name dependency that resolves to either a target or a product but + /// after the package graph has been loaded. + /// + /// - Parameters: + /// - name: The name of the dependency, either a target or a product. + /// - condition: The condition under which the dependency is exercised. + @available(_PackageDescription, introduced: 5.3) + public static func byName( + name: String, + condition: BuildSettingCondition? = nil + ) -> Target.Dependency { + // ... + } +} +``` + +## Security + +This proposal has no impact on security, safety, or privacy. + +## Impact on existing packages + +Current packages will not be impacted by this change as all `PackageDescription` changes will be gated by a new tools version. As always, the Package Manager will support package hierarchies with heterogeneous tools versions, so authors will be able to adopt those new APIs with minimal impact to end-users. + +## Alternatives considered + +No alternatives were considered for now. From 8cab81d6d2fa8b6a6aad374d2af4201b6634246d Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Sat, 14 Dec 2019 01:22:25 +0100 Subject: [PATCH 1359/4563] [SE-0272] Review feedback: Package Manager Binary Dependencies (#1099) * Work in review feedback * Update resolution behaviour * Small edits to the proposal - removed note about "Multiple references to same artifact" because that case already shouldn't be possible in practice - updated "name" note for artifacts to talk about module name - removed leftover mention of opt-out for binaries - replaced `--artifact-url` with `--original-url` option which is supposed to work for both package as well as artifact URLs --- proposals/0272-swiftpm-binary-dependencies.md | 157 +++++++++++------- 1 file changed, 99 insertions(+), 58 deletions(-) diff --git a/proposals/0272-swiftpm-binary-dependencies.md b/proposals/0272-swiftpm-binary-dependencies.md index 0ef1163368..0cb787fb79 100644 --- a/proposals/0272-swiftpm-binary-dependencies.md +++ b/proposals/0272-swiftpm-binary-dependencies.md @@ -3,7 +3,7 @@ * Proposal: [SE-0272](0272-swiftpm-binary-dependencies.md) * Authors: [Braden Scothern](https://github.com/bscothern), [Daniel Dunbar](https://github.com/ddunbar), [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **Returned for revision** +* Status: **Active review (12 13...12 20)** * Decision Notes: [Rationale](https://forums.swift.org/t/returned-for-revision-se-0272-package-manager-binary-dependencies/30994) ## Contents @@ -95,29 +95,14 @@ let package = Package( targets: [ .binaryTarget( name: "SomePackageLib", - artifacts: [ - .artifact( - source: .url("https://github.com/some/package/releases/download/1.0.0/SomePackage-1.0.0.zip", - checksum: "839F9F30DC13C30795666DD8F6FB77DD0E097B83D06954073E34FE5154481F7A")), - ] + url: "https://github.com/some/package/releases/download/1.0.0/SomePackage-1.0.0.zip", + checksum: "839F9F30DC13C30795666DD8F6FB77DD0E097B83D06954073E34FE5154481F7A" + ), + .binaryTarget( + name: "SomeLibOnDisk", + path: "artifacts/SomeLibOnDisk.zip" ) - ] -) -``` - - -Secondly we propose to add a new configuration point to the package description that allows packages to opt-out of binary dependencies. This will enforce that nothing in its transitive dependencies brings a binary dependency with it. - -```swift -let package = Package( - name: "SomeOtherPackage", - disallowsBinaryDependencies: true, - products: [ - .library(name: "SomeOtherPackage", targets: ["SomeOtherPackageLib"]) - ], - targets: [ - .target(name: "SomeOtherPackageLib") - ] + ] ) ``` @@ -125,20 +110,16 @@ Packages are allowed to contain a mix of binary and source targets. This is useful when, for example, providing a pre-built or closed source C library alongside an open source set of Swift bindings for the library. -When a package is built that depends upon any product with a binary target, the -package manager will search the `artifacts` declaration list to find an artifact -which matches the current build conditions. This list -will be searched in order, and the first matching artifact will be used. It is -the job of the package author/publisher to provide an appropriate set of -artifacts for the use cases the package wishes to support. This use case will be limited to Apple platforms in the beginning. In the future, we can add support for other platforms. A potential approach is outlined in the future directions section. +The use case will be limited to Apple platforms in the beginning. In the future, we can add support for other platforms. A potential approach is outlined in the future directions section. ## Detailed design The design consists of the following key points: * New `PackageDescription` API for defining a binary target. -* New parameter on a package declaration level to opt-out of binary dependencies. * New requirements for the `Package.resolved` file when using binary packages. +* A new command to compute a checksum for a file. * A new mechanism for downloading binary target artifacts. +* Support for artifact mirroring. Terminology: @@ -166,60 +147,86 @@ while keeping the following as non-goals: ## New `PackageDescription` API ### BinaryTarget -Since a binary target is different compared to a source only target, we propose to introduce a new struct `Artifact`. This struct defines a target's associated artifacts. - -We propose to support local and remote artifacts from the beginning. In the alternatives considered section is larger collection of potential artifact stores. However we opted to simplify the initial implementation by just supporting a url and a path based definition. Later, we can implement different types of providers with different authentication methods. +Since a binary target is different compared to a source only target, we propose to introduce two new static method on `Target` to declare a binary target. We propose to support local and remote artifacts from the beginning. In the alternatives considered section is a larger collection of potential artifact stores. However we opted to simplify the initial implementation by just supporting a url and a path based definition. Later, we can implement different types of providers with different authentication methods. ```swift -public struct Artifact { - public enum Source { - case url(String, checksum: String) - case path - } - - public let source: Source +extension Target { + /// Declare a binary target with the given url. + public static func binaryTarget( + name: String, + url: String, + checksum: String + ) -> Target + + /// Declare a binary target with the given path on disk. + public static func binaryTarget( + name: String, + path: String + ) -> Target } ``` -Furthermore, we propose to add a new `artifacts: [Artifacts]?` property to the `Target`, as well as extend the initializer with this parameter and create a new static method called `.binaryTarget()`. Lastly, we propose to extend the `TargetType` enum with a new case called `binary`. - -### PackageDescription -To opt out of binary packages we propose a new configuration point inside the package description. - -```swift -public final class Package { - ... - /// This disallows any binary dependency or any transitive binary dependency. - public var disallowsBinaryDependencies: Bool - ... -} -``` +## Checksum computation +We propose to add a new command to SwiftPM `swift package compute-checksum ` which is going to be used to compute the checksum of individual files. This implementation can then evolve in the future and is tied to the tools version of the package to avoid breaking compatibility with older tools. ## New `Package.resolved` Behavior -For binary targets we store the checksum of the artifact in the `Package.resolved`. This lets us check for errors during resolution where a package's version did not change but the checksum did. In this case we will throw an error alerting the user about this. +For binary targets we will validate the commit hashes from the resolved file for any dependencies from now on to ensure the checksums of binaries cannot be changed for a specific version. This lets us check for errors during resolution where a package's version did not change but the checksum did. In this case we will throw an error alerting the user about this. ### Resolution Package resolution and dependency expression will not be impacted by this change (except where explicitly noted). -#### Multiple references to same artifact -During resolution SwiftPM will check that all references to an artifact in a dependency graph have the same checksum. +#### Exported product with binary dependency that specifies a type +SwiftPM will emit an error during resolution when a product that directly exports a binary dependency declares a type, e.g.: `.product(name: "MyBinaryLib", type: .static, targets: ["MyBinaryLib"])`. + +#### Resolution on non-Apple platforms +When resolving a package that contains a binary dependency on non-Apple platforms, SwiftPM will throw an error and explicitly state that this dependency is not valid for the current platform. During the review it was brought up that we could ignore these dependencies but that would make the behavior of SwiftPM very unexpected. In the future, when properly supporting other platforms this can be solved easily with a proper condition mechanism. ## Binary Target Artifact Format SwiftPM currently supports multiple platforms; however, this proposal only adds support for binary targets on Apple platforms. The reason for this is that Apple platforms provide ABI guarantees and an already existing format we can leverage to simplify the initial implementation. For Apple platforms we propose to use the `XCFramework` format for artifacts. This format already supports dynamic and static linking. Furthermore, it can contain products for every individual Apple platform at once. +SwiftPM expects url-based artifacts to be packaged inside a `.zip` file where the artifact is lying at the root of the archive. Furthermore, the artifact needs to have the same module name as the target name provided inside the manifest file. + +For path-based artifact SwiftPM supports artifacts as a `.zip` and as a raw `XCFramework`. + +During resolution SwiftPM won't do any verification of the format of the artifact. This is up to the vendor to provide correct and valid artifact. In the future, this can be extended and further validation, such as checking that the module name matches, can be implemented. + ## Security When adding new external dependencies, it is always important to consider the security implication that it will bring with it. Comparing the trust level of a source-based to a binary-based dependency the first thought is that the trust level of the source-based dependency is higher since on can inspect its source code. However, there is no difference between a binary and source dependency since source-based dependencies can have security issues as well. One should have better reasons to trust a dependency than source being inspectable. -There is still a significant difference between having a dependency with zero vs. any binary dependency. For example, the portability of a library with binary dependencies is far worse than the one with only source-based dependencies. For this reason, we propose to add an additional configuration point in the manifest that allows package authors to opt-out of binary dependencies. +There is still a significant difference between having a dependency with zero vs. any binary dependency. For example, the portability of a library with binary dependencies is far worse than the one with only source-based dependencies. However, there are still some security related aspects when it comes to binary artifacts that we should mitigate. For example, when declaring a `binaryTarget` the hash of the artifact is required similar to Homebrew. By doing this an attacker needs to compromise both the server which provides the artifact as well as the git repository which provides the package manifest. A secondary reason is that the server providing the binary might be out of the package author's control and this way we can ensure that the expected binary is used. Lastly, the hash of the binary is stored in the package resolved to avoid that the vendor changes the artifact behind a version without anyone noticing. +## Mirroring support +Binary artifacts can also be mirrored. We propose to deprecate the existing `--package-url` option and to replace it with a `--original-url` option which will work for both package URLs as well as artifact URLs: + +``` +$ swift package config set-mirror \ + --original-url \ + --mirror-url + +# Example: + +$ swift package config set-mirror \ + --original-url https://github.com/Core/core/releases/download/1.0.0/core.zip \ + --mirror-url https://mygithub.com/myOrg/core/releases/download/1.0.0/core.zip +``` + +Additionally, we propose to add a command to unset a mirror URL for an artifact: + +``` +$ swift package config unset-mirror \ + --original-url https://github.com/Core/core/releases/download/1.0.0/core.zip +``` + +The other unset command options `--mirror-url` and `--all` will be working the same for artifacts as they do for packages. + ## Impact on existing packages No current package should be affected by this change since this is only an additive change in enabling SwiftPM to use binary dependencies. @@ -229,9 +236,18 @@ No current package should be affected by this change since this is only an addit ### Support for non-Apple platforms Non-Apple platforms provide non-trivial challenges since they are not always giving guarantees of the ABI of the platform. Additionally, further conditions such as the corelibs-foundation ABI or if the hardware supports floating points need to be taken into consideration when declaring a package for non-Apple platforms. Various other communities tried to solve this, e.g. Python's [manylinux](https://www.python.org/dev/peps/pep-0600/). -In the future, we could add an `ArtifactCondition ` to SwiftPM which provides the possibility to declare under which conditions a certain artifact can be used. Below is a potential `ArtifactCondition` struct which does **not** include a complete set of conditions that need to be taken into consideration. +In the future, we could add an `Artifact` struct and `ArtifactCondition`s to SwiftPM which provides the possibility to declare under which conditions a certain artifact can be used. Below is a potential `Artifact` and `ArtifactCondition` struct which does **not** include a complete set of conditions that need to be taken into consideration. ```swift +public struct Artifact { + public enum Source { + case url(String, checksum: String) + case path + } + + public let source: Source +} + public struct ArtifactCondition: Encodable { public struct LLVMTriplet: Encodable { // Should be only the subset that Swift supports @@ -414,6 +430,31 @@ During the discussion of this proposal another solution to the `allowsBinary` fl ### Opt-out configuration in separate file During the discussion of this proposal it was decided that an opt-out mechanism was good to give package users and vendors an escape hatch. However, it was discussed whether this configuration should live inside the manifest or a separate configuration file. In this proposal, we opted to keep the configuration inside the manifest file. +### Opt-out in package manifest +In the first round, we proposed to add a configuration flag in the manifest to opt-out of binary dependencies; however, during the review it became apparent that this flag doesn't provide as much value and can make some dependencies actually more restricted when they add this flag. Therefor, we opted to not include such a configuration flag and let workflow tooling provide this functionality if needed. + +```swift +public final class Package { + ... + /// This disallows any binary dependency or any transitive binary dependency. + public var disallowsBinaryDependencies: Bool + ... +} +``` + +```swift +let package = Package( + name: "SomeOtherPackage", + disallowsBinaryDependencies: true, + products: [ + .library(name: "SomeOtherPackage", targets: ["SomeOtherPackageLib"]) + ], + targets: [ + .target(name: "SomeOtherPackageLib") + ] +) +``` + ### Support for various artifact stores Initially, we considered the various artifact stores on the market and how we can integrate with them. We decided to support a URL based artifact definition for the first implementation since the various providers require each their own method of authentication. However, we wanted to keep the possibility for future additions of providers open; therefore, we made the source of an artifact an enum which can be extended. From 21136e3321292da4e474d97c12f2733b2dffbeca Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 14 Dec 2019 12:35:57 +0000 Subject: [PATCH 1360/4563] Use full month name to avoid "NaN" on status page (#1103) * Update process.md * Update 0272-swiftpm-binary-dependencies.md * Update 0273-swiftpm-conditional-target-dependencies.md --- process.md | 4 ++-- proposals/0272-swiftpm-binary-dependencies.md | 2 +- proposals/0273-swiftpm-conditional-target-dependencies.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/process.md b/process.md index 3c3eccbdbb..56921110be 100644 --- a/process.md +++ b/process.md @@ -89,10 +89,10 @@ A given proposal can be in one of several states: * **Awaiting review**: The proposal is awaiting review. Once known, the dates for the actual review will be placed in the proposal document. When the review period begins, the review manager will update the state to *Active review*. -* **Scheduled for review (MONTH DAY...MONTH DAY)**: The public review of the proposal +* **Scheduled for review (FULL_MONTH_NAME DAY...FULL_MONTH_NAME DAY)**: The public review of the proposal in the [Swift forums][proposal-reviews] has been scheduled for the specified date range. -* **Active review (MONTH DAY...MONTH DAY)**: The proposal is undergoing public review +* **Active review (FULL_MONTH_NAME DAY...FULL_MONTH_NAME DAY)**: The proposal is undergoing public review in the [Swift forums][proposal-reviews]. The review will continue through the specified date range. * **Returned for revision**: The proposal has been returned from review diff --git a/proposals/0272-swiftpm-binary-dependencies.md b/proposals/0272-swiftpm-binary-dependencies.md index 0cb787fb79..ea5c35bb38 100644 --- a/proposals/0272-swiftpm-binary-dependencies.md +++ b/proposals/0272-swiftpm-binary-dependencies.md @@ -3,7 +3,7 @@ * Proposal: [SE-0272](0272-swiftpm-binary-dependencies.md) * Authors: [Braden Scothern](https://github.com/bscothern), [Daniel Dunbar](https://github.com/ddunbar), [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **Active review (12 13...12 20)** +* Status: **Active review (December 13...December 20)** * Decision Notes: [Rationale](https://forums.swift.org/t/returned-for-revision-se-0272-package-manager-binary-dependencies/30994) ## Contents diff --git a/proposals/0273-swiftpm-conditional-target-dependencies.md b/proposals/0273-swiftpm-conditional-target-dependencies.md index 377e41d3ac..45fd5d1a2c 100644 --- a/proposals/0273-swiftpm-conditional-target-dependencies.md +++ b/proposals/0273-swiftpm-conditional-target-dependencies.md @@ -3,7 +3,7 @@ * Proposal: [SE-0273](0273-swiftpm-conditional-target-dependencies.md) * Authors: [David Hart](https://github.com/hartbit) * Review Manager: [Boris Buegling](https://github.com/neonichu) -* Status: **Active review (12 13...12 20)** +* Status: **Active review (December 13...December 20)** * Implementation: [apple/swift-package-manager#2428](https://github.com/apple/swift-package-manager/pull/2428) ## Introduction From e16d6136928069bb12ed453b39cfc5f0bc14e676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Fri, 20 Dec 2019 15:23:25 +0100 Subject: [PATCH 1361/4563] Update 0272-swiftpm-binary-dependencies.md --- proposals/0272-swiftpm-binary-dependencies.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0272-swiftpm-binary-dependencies.md b/proposals/0272-swiftpm-binary-dependencies.md index ea5c35bb38..8ee1769062 100644 --- a/proposals/0272-swiftpm-binary-dependencies.md +++ b/proposals/0272-swiftpm-binary-dependencies.md @@ -3,8 +3,8 @@ * Proposal: [SE-0272](0272-swiftpm-binary-dependencies.md) * Authors: [Braden Scothern](https://github.com/bscothern), [Daniel Dunbar](https://github.com/ddunbar), [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **Active review (December 13...December 20)** -* Decision Notes: [Rationale](https://forums.swift.org/t/returned-for-revision-se-0272-package-manager-binary-dependencies/30994) +* Status: **Accepted with revisions** +* Decision Notes: [First Review](https://forums.swift.org/t/returned-for-revision-se-0272-package-manager-binary-dependencies/30994), [Second Review](https://forums.swift.org/t/accepted-with-modifications-se-0272-package-manager-binary-dependencies/31926) ## Contents From 143928862e265c6bf4d0ffd3deef653d970696ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Fri, 20 Dec 2019 17:16:10 +0100 Subject: [PATCH 1362/4563] Update 0273-swiftpm-conditional-target-dependencies.md --- proposals/0273-swiftpm-conditional-target-dependencies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0273-swiftpm-conditional-target-dependencies.md b/proposals/0273-swiftpm-conditional-target-dependencies.md index 45fd5d1a2c..a00bcabad1 100644 --- a/proposals/0273-swiftpm-conditional-target-dependencies.md +++ b/proposals/0273-swiftpm-conditional-target-dependencies.md @@ -3,7 +3,7 @@ * Proposal: [SE-0273](0273-swiftpm-conditional-target-dependencies.md) * Authors: [David Hart](https://github.com/hartbit) * Review Manager: [Boris Buegling](https://github.com/neonichu) -* Status: **Active review (December 13...December 20)** +* Status: **Accepted** * Implementation: [apple/swift-package-manager#2428](https://github.com/apple/swift-package-manager/pull/2428) ## Introduction From 3b2a1e80d552ca8e321048d78b1d6fb4126b1b12 Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Sat, 21 Dec 2019 16:54:45 +0000 Subject: [PATCH 1363/4563] [SE-0268] Revert oldValue deprecation text based on core team decision (#1104) --- proposals/0268-didset-semantics.md | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/proposals/0268-didset-semantics.md b/proposals/0268-didset-semantics.md index 28f74205a7..f1c0f23928 100644 --- a/proposals/0268-didset-semantics.md +++ b/proposals/0268-didset-semantics.md @@ -3,7 +3,7 @@ * Proposal: [SE-0268](0268-didset-semantics.md) * Author: [Suyash Srijan](https://www.github.com/theblixguy) * Review Manager: [Ben Cohen](https://www.github.com/airspeedswift) -* Status: **Active Review (5-9 December 2019)** +* Status: **Accepted** * Implementation: [apple/swift#26632](https://github.com/apple/swift/pull/26632) * Bug: [SR-5982](https://bugs.swift.org/browse/SR-5982) @@ -123,31 +123,6 @@ This applies to a `didSet` on an overridden property as well - the call to the s This also resolves some pending bugs such as [SR-11297](https://bugs.swift.org/browse/SR-11297) and [SR-11280](https://bugs.swift.org/browse/SR-11280). -In order to avoid confusion as to when the getter is called or not, the implicit `oldValue` is now deprecated. If we do refer to the `oldValue` in the `didSet` body, then the compiler will generate a warning along with a fix-it to insert `oldValue` in parenthesis i.e. `didSet(oldValue) { ... }`. For example: - -```swift -class Foo { - var bar = 0 { // Before - didSet { - guard oldValue != bar else { return } // This will now trigger a compiler warning to - // remind the user that implicit oldValue is - // now deprecated, along with a fix-it to - // insert 'oldValue' in parenthesis on the - // `didSet`. - tableView.reloadData() - } - } - - var bar = 0 { // After - didSet(oldValue) { - guard oldValue != bar else { return } // Okay - tableView.reloadData() - } - } -} -``` - - As a bonus, if the property has a "simple" `didSet` and no `willSet`, then we could allow for modifications to happen in-place. For example: ```swift @@ -213,3 +188,5 @@ This does not affect API resilience - library authors can freely switch between ## Future Directions We can apply the same treatment to `willSet` i.e. not pass the `newValue` if it does not refer to it in its body, although it wouldn't provide any real benefit as not passing `newValue` to `willSet` does not avoid anything, where as not passing `oldValue` to `didSet` avoids loading it. + +We can also deprecate the implicit `oldValue` and request users to explicitly provide `oldValue` in parenthesis (`didSet(oldValue) { ... }`) if they want to use it in the body of the observer. This will make the new behavior more obvious and self-documenting. \ No newline at end of file From 19124b956bedf9b1b39a05725937de079a6f7727 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 21 Dec 2019 16:59:18 +0000 Subject: [PATCH 1364/4563] [SE-0268] Fix typo `surpress` -> `suppress` --- proposals/0268-didset-semantics.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0268-didset-semantics.md b/proposals/0268-didset-semantics.md index f1c0f23928..e333958686 100644 --- a/proposals/0268-didset-semantics.md +++ b/proposals/0268-didset-semantics.md @@ -182,11 +182,11 @@ This does not affect API resilience - library authors can freely switch between ## Alternatives considered - Explicitly require an `oldValue` parameter to use it, such as `didSet(oldValue) { ... }`, otherwise it is an error to use `oldValue` in the `didSet` body. This will be a big source breaking change. It will also cause a regression in usability and create an inconsistency with other accessors, such as `willSet` or `set`, which can be declared with or without an explicit parameter. The source compatibility problem can be mitigated by deprecating the use of implicit `oldValue` and then making it an error in the next language version, however the usability regression would remain. -- Introduce a new `didSet()` syntax that will surpress the read of the `oldValue` (and it will be an error to use `oldValue` in the `didSet` body). This will prevent any breakage since it's an additive change, but will reduce the positive performance gain (of not calling the getter when `oldValue` is not used) to zero unless people opt-in to the new syntax. Similar to the previous solution, it will create an inconsistency in the language, since it will be the only accessor that can be declared with an empty parameter list and will become yet another thing to explain to a newcomer. +- Introduce a new `didSet()` syntax that will suppress the read of the `oldValue` (and it will be an error to use `oldValue` in the `didSet` body). This will prevent any breakage since it's an additive change, but will reduce the positive performance gain (of not calling the getter when `oldValue` is not used) to zero unless people opt-in to the new syntax. Similar to the previous solution, it will create an inconsistency in the language, since it will be the only accessor that can be declared with an empty parameter list and will become yet another thing to explain to a newcomer. - Leave the existing behavior as is. ## Future Directions We can apply the same treatment to `willSet` i.e. not pass the `newValue` if it does not refer to it in its body, although it wouldn't provide any real benefit as not passing `newValue` to `willSet` does not avoid anything, where as not passing `oldValue` to `didSet` avoids loading it. -We can also deprecate the implicit `oldValue` and request users to explicitly provide `oldValue` in parenthesis (`didSet(oldValue) { ... }`) if they want to use it in the body of the observer. This will make the new behavior more obvious and self-documenting. \ No newline at end of file +We can also deprecate the implicit `oldValue` and request users to explicitly provide `oldValue` in parenthesis (`didSet(oldValue) { ... }`) if they want to use it in the body of the observer. This will make the new behavior more obvious and self-documenting. From 9dc1748245edfdc09db630e19ce5735a6f5793cc Mon Sep 17 00:00:00 2001 From: Alfredo Delli Bovi Date: Fri, 3 Jan 2020 16:51:19 +0100 Subject: [PATCH 1365/4563] Proposal: Allow more characters (like whitespaces and punctuations) for escaped identifiers --- ...nd-punctuations-for-escaped-identifiers.md | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 proposals/NNNN-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md diff --git a/proposals/NNNN-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md b/proposals/NNNN-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md new file mode 100644 index 0000000000..f9e38a4dad --- /dev/null +++ b/proposals/NNNN-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md @@ -0,0 +1,75 @@ +# Allow more characters (like whitespaces and punctuations) for escaped identifiers + +* Proposal: [SE-XXXX] + +* Authors: [Alfredo Delli Bovi](https://github.com/adellibovi) + +* Review Manager: TBD + +* Status: **Awaiting review** + +* Implementation: [apple/swift#28966](https://github.com/apple/swift/pull/28966) + +## Introduction +Swift has a beautiful concise yet expressive syntax. +As part of that, escaped identifiers are adopted to allow usage of reserved keywords. +This proposal wants to extend the character allowance for escaped identifiers with more Unicode scalars, like whitespace and punctuation. +It will enable to have method names (or other identifiers) with a more readable and natural language like the following: +```swift +func `test validation should succeed when input is less then ten`() +``` +## Motivation +Naming could be hard and having descriptive methods, like in tests, may result in declarations that are hard to read because of its lack of whitespace and punctuations or other symbols. Enabling natural language would improve readability. + +Maintainers of different projects under the [Swift Source Compatibility](https://swift.org/source-compatibility/#current-list-of-projects) uses, instead of Swift's method declaration, testing frameworks, like [Quick](https://github.com/Quick/Quick), because (among other reasons) how they can elegantly express tests descriptions. + +Other modern languages like [F#](https://fsharp.org) and [Kotlin](https://kotlinlang.org) saw the value in supporting natural language for escaped identifiers. Today, naming methods with spaces and punctuation are, for those languages, a standard for tests, widely adopted and supported by different test runners and reporting tools. + +## Proposed solution +This proposal wants to extend the current grammar for every escaped identifier (properties, methods, types etc...) by allowing every Unicode scalar. + +A declaration to an escaped identifier will follow the existing back-ticked syntax. +```swift +func `test validation should succeed when input is less then ten`() +var `some var` = 0 +``` + +As per referencing. +```swift +`test validation should succeed when input is less then ten`() +foo.`property with space` +``` +In fact, by allowing a larger set of characters, we will remove current limitations and, as an example, we will enable us to reference an operator, which currently produces an error. +```swift +let add = Int.`+` +``` + +### Grammar +This proposal wants to replace the following grammar: +``` +identifier → ` identifier-head identifier-characters opt ` +``` +with: +``` +identifier → ` escaped-identifier ` +escaped-identifier -> Any Unicode scalar value except U+000A or U+000D or U+0060 +``` + +### Objective-C Interoperability +Objective-C declarations do not support every type of Unicode scalar value. +If willing to expose an escaped identifier that includes a not supported Objective-C character, we can sanitize it using the existing `@objc` annotation like the following: +```swift +@objc(sanitizedName) +``` + +## Source compatibility +This feature is strictly additive. + +## Effect on ABI stability +This feature does not affect the ABI. + +## Effect on API resilience +This feature does not affect the API. + +## Alternatives considered +It was considered to extend the grammars for methods declaration only, this was later discarded because we want to keep usage consistency and it would be hard to explain why an escaped identifier may support a certain set of characters in a context and a different one in another context. From d16d623fb439cae1ec833d88d7ef8ef5976589d6 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 8 Jan 2020 17:43:31 -0500 Subject: [PATCH 1366/4563] Clarify that SE-0270 is no longer in active review The author and Core Team are discussing what to do with the rather extensive feedback. --- proposals/0270-rangeset-and-collection-operations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index 511cd51ae3..c60cbc8378 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -3,7 +3,7 @@ * Proposal: [SE-0270](0270-rangeset-and-collection-operations.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (December 13th...19th, 2019)** +* Status: **Pending Core Team feedback** * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/9b5957c00e7483ab8664afe921f989ed1394a666/proposals/0270-rangeset-and-collection-operations.md) * Previous Review: [SE-0270: Add Collection Operations on Noncontiguous Elements](https://forums.swift.org/t/se-0270-add-collection-operations-on-noncontiguous-elements/30691) From 5c67f68da1e90e6f5a3b8d1538962ed46901b227 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Wed, 8 Jan 2020 17:32:01 -0800 Subject: [PATCH 1367/4563] Proposal: "Concise magic file names" (#1101) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add “concise magic file names” proposal --- proposals/0274-magic-file.md | 188 +++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 proposals/0274-magic-file.md diff --git a/proposals/0274-magic-file.md b/proposals/0274-magic-file.md new file mode 100644 index 0000000000..1d234a19fc --- /dev/null +++ b/proposals/0274-magic-file.md @@ -0,0 +1,188 @@ +# Concise magic file names + +* Proposal: [SE-0274](0274-magic-file.md) +* Authors: [Brent Royal-Gordon](https://github.com/brentdax), [Dave DeLong](https://github.com/davedelong) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift/) +* Status: **Active Review (8-16 January 2020)** +* Implementation: [apple/swift#25656](https://github.com/apple/swift/pull/25656); merged behind `-enable-experimental-concise-pound-file` + +## Introduction + +Today, `#file` evaluates to a string literal containing the full path to the current source file. We propose to instead have it evaluate to a human-readable string containing the filename and module name, while preserving the existing behavior in a new `#filePath` expression. + +Swift-evolution thread: [Concise Magic File Names](https://forums.swift.org/t/concise-magic-file-names/31297), [We need `#fileName`](https://forums.swift.org/t/we-need-filename/19781) + +## Motivation + +In Swift today, the magic identifier `#file` evaluates to a string literal containing the full path (that is, the path passed to `swiftc`) to the current file. It's a nice way to trace the location of logic occurring in a Swift process, but its use of a full path has a lot of drawbacks: + +* It can inadvertently reveal private or sensitive information. The full path to a source file may contain a developer's username, hints about the configuration of a build farm, proprietary versions or identifiers, or the Sailor Scout you named an external disk after. Developers probably don't know that this information is embedded in their binaries and may not want it to be there. And most uses of `#file` are in default arguments, which makes this information capture invisible at the use site. The information leaks here are quite serious; if other languages hadn't already normalized this, I doubt we would think that `#file` was an acceptable design. + +* It bloats binaries produced by the Swift compiler. In testing with the Swift benchmark suite, a shorter `#file` string reduced code size by up to 5%. The large code also impacts runtime performance—in the same tests, a couple dozen benchmarks ran noticeably faster, with several taking 22% less time. While we didn't benchmark app launch times, it's likely that they are also adversely affected by lengthy `#file` strings. + +* It introduces artificial differences between binaries built on different machines. For instance, the same code built in two different environments might produce different binaries with different hashes. This makes life difficult for anyone trying to cache build artifacts, find the differences between two binaries, or otherwise hoping to benefit from reproducible builds. + +Meanwhile, the benefits you might expect from using a full path aren't really there: + +* The paths are not portable between machines, so you can't use it to automatically match an error to a line of source code unless your machine has the same directory layout as the machine that built the binary. + +* The paths are not guaranteed to be absolute—XCBuild and SwiftPM happen to use absolute paths, but other build systems like Bazel don't—so you can't be sure that you can resolve paths to a file on disk. + +* The paths are not even guaranteed to be unique within a process because the same file could be included in several different modules, or different projects could be located at the same paths on different machines or at different times. + +For `#file`'s most important use case, it seems like the current string computed by `#file` is suboptimal and a better default would improve Swift in several ways. + +### Other uses of `#file` + +While `#file` is primarily intended for developer-facing errors, in practice it is also used in other ways. In particular, Swift tests sometimes use `#file` to compute paths to fixtures relative to the source file that uses them. This has historically been necessary in SwiftPM because it did not support resources, but [SE-0271][] has now added a much better solution to this problem than `#file`-based path computation. + + [SE-0271]: https://github.com/apple/swift-evolution/blob/master/proposals/0271-package-manager-resources.md + +It's worth reiterating that these uses are inherently fragile and unreliable. Not only do they rely on the build system choosing to pass absolute paths, they also rely on a full checkout of the project being present on disk at the same path as when the binary was built. That means these tests will probably fail when run anywhere but on the machine they were built on, including on attached iOS/tvOS/watchOS devices. + +Nevertheless, we know that some projects in the wild *do* use `#file` this way; the question is how common these uses are. We attempted to answer this question by analyzing the Swift Source Compatibility Suite in three ways: + +1. We used regular expressions to automatically examine the 1,073 uses of `#file` in all 108 projects, trying to categorize them as being either for human display or machine consumption. We concluded that approximately 91–96% of uses are for human display.[1] + +2. We manually examined 172 of those uses in sixteen projects. We concluded that approximately 95% of the uses we looked at would benefit from the new `#file` behavior. + +3. We ran tests for the 21 projects which had configured test targets and passed in at least one language version mode. Three of these projects—`Html`, `Lark`, and `swift-nio-http2`—showed new failures with a shorter `#file` string. In all three cases, the broken code looked up test fixtures relative to `#file`; the projects themselves appeared to function normally. We concluded that a small number of projects use `#file` in ways that require a full path, but only in their tests. + +These three data points together indicate that most code is not sensitive to the exact string generated by `#file` and that adopting SwiftPM resources would probably address the main reason that code might be sensitive to it. However, we should not merely tell projects like these that "your tests are now broken and fixing them will require you to totally redesign how you access fixtures"—we should provide a drop-in solution for projects that are currently using `#file` in these ways. + +
    +[1] Details of regular expression-based analysis + +We applied several regular expressions to the source code of the projects in the Source Compatibility Suite to try to classify uses of `#file`. We identified four patterns that we believe represent display to humans: + +1. `StaticString = #file`: Since `StaticString` is fairly inconvenient to use compared to `String`, we assume these will be passed to one of the few APIs that take them. Most of those—like `precondition(_:_:file:line:)` or `XCTAssertEqual(_:_:_:file:line:)`—are APIs that should show a concise string. + +2. ` = #file`: A couple of projects use a custom typealias for `StaticString`. We manually confirmed that these projects generally pass those values to something like an `XCTAssert` function which should show a concise string. + +3. `String = #file`, but with `#line` on the same line: We take this to indicate that a file and line are being captured for display together. + +4. `#file` interpolated into a string: Since `#file` is usually an absolute path that wouldn't need to be concatenated, we take these to be formatting for display. + +We also identified two patterns that we conservatively assume represent eventual I/O or other machine-oriented processing (although in many cases they may represent path computation for display): + +1. `String = #file`, no `#line` nearby: We assume this will be passed to an API like `URL.init(fileURLWithPath:)` which will then be used to further manipulate the path or perform I/O. + +2. `#file` used in a parenthesized list, but not an interpolation: We assume this is being passed to an API like `URL.init(fileURLWithPath:)`. + +When we matched these patterns against the source compatibility suite, we got the following results: + +| Pattern | Occurrences | Percentage | +| -------- | --------------- | ------------- | +| **Human display patterns** | | | +| `StaticString = #file` | 419 | 39.0% | +| ` = #file` | 281 | 26.1% | +| `#file` and `#line` captured together | 148 | 13.8% | +| `#file` interpolated into string | 132 | 12.3% | +| All human display patterns | 980 | 91.3% | +| **Machine consumption patterns** | | | +| `String = #file`, no `#line` | 10 | 0.9% | +| `#file` passed to function | 31 | 2.9% | +| All machine consumption patterns | 41 | 3.8% | +| **None of the above patterns** | 52 | 4.8% | + +We therefore estimate that about 6% (±3%) of uses actually want a full path so they can process it, while 94% (±3%) would be better served by a more succinct string designed for human display. + +
    + +## Proposed solution + +We propose changing the string that `#file` evaluates to. To preserve implementation flexibility, we do not specify the exact format of this string; we merely specify that it must: + +1. Be unique to the file and module it refers to. That is, every unique `fileprivate` scope should have a different `#file` value. + +2. Be easy for a human to read and map to the matching source file. + +`#file` will otherwise behave as it did before, including its special behavior in default arguments. Standard library assertion functions will continue to use `#file`, and we encourage developers to use it in test helpers, logging functions, and most other places where they use `#file` today. + +For those rare cases where developers actually need a full path, we propose adding a `#filePath` magic identifier with the same behavior that `#file` had in previous versions of Swift. That is, it contains the path to the file as passed to the Swift compiler. + +## Detailed design + +We do not specify the exact string produced by `#file` because it is not intended to be machine-parseable and we want to preserve flexibility as the compiler's capabilities change. In the current implementation of this proposal, however, a file at `/Users/brent/Desktop/0274-magic-file.swift` in a module named `MagicFile` with this content: + +```swift +print(#file) +print(#filePath) +fatalError("Something bad happened!") +``` + +Would produce this output: + +``` +0274-magic-file.swift (MagicFile) +/Users/brent/Desktop/0274-magic-file.swift +Fatal error: Something bad happened!: file 0274-magic-file.swift (MagicFile), line 3 +``` + +This string is currently sufficient to uniquely identify the file's `fileprivate` scope because the Swift compiler does not allow identically-named files in different directories to be included in the same module.[2] + +This implementation is actually already in master and swift-5.2-branch behind a compiler flag; use the `-enable-experimental-concise-pound-file` flag to try it out. + +> [2] This limitation ensures that identically-named `private` and `fileprivate` declarations in different files will have unique mangled names. A future version of the Swift compiler could lift this limitation. If this happens, not fully specifying the format of the `#file` string preserves flexibility for that version to adjust its `#file` strings to include additional information, such as selected parent directory names, sufficient to distinguish the two files. + +## Source compatibility + +All existing source code will continue to compile with this change, and `#file`'s documentation never specified precisely what its contents were; in one of the pitch threads, [Ben Cohen](https://forums.swift.org/t/concise-magic-file-names/31297/19) said that this is sufficient to satisfy Swift's source compatibility requirements. However, the proposal *will* cause behavior to change in existing code, and in some cases it will change in ways that cause existing code to behave incorrectly when run. Code that is adversely affected by this change can access the previous behavior by using `#filePath` instead of `#file`. + +## Effect on ABI stability + +None. `#file` is a compile-time feature; existing binaries will continue to work as they did before. + +## Effect on API resilience + +As with any addition to Swift's syntax, older compilers won't be able to use module interface files that have `#filePath` in default arguments or inlinable code. Otherwise, none. + +## Alternatives considered + +### Deprecate `#file` and introduce two new syntaxes + +Rather than changing the meaning of `#file`, we could keep its existing behavior, deprecate it, and provide two alternatives: + +* `#filePath` would continue to use the full path. +* `#fileName` would use the new concise string suggested by this proposal. + +This is a more conservative approach that would avoid breaking any existing uses. We choose not to propose it for three reasons: + +1. The name `#fileName` is misleading because it sounds like the string only contains the file name, but it also contains the module name. `#file` is more vague, so we're more comfortable saying that it's "a string that identifies the file". + +2. This alternative will force developers to update every use of `#file` to one or the other option. We feel this is burdensome and unnecessary given how much more frequently the `#fileName` behavior would be appropriate. + +3. This alternative gives users no guidance on which feature developers ought to use. We feel that giving `#file` a shorter name gives them a soft push towards using it when they can, while resorting to `#filePath` only when necessary. + +4. Since all uses of `#file`—not just ones that require a full path—would change, all uses of `#file` in module interfaces—not just ones that require a full path—would become stumbling blocks for backwards compatibility. This includes uses in the swiftinterface files for the standard library and XCTest. + +However, if the core team feels that changing `#file`'s behavior will cause unacceptable behavior changes, this ready-made alternative would accomplish most of the goals of this proposal. + +### Support more than two `#file` variants + +We considered introducing additional `#file`-like features to generate other strings, selecting between them either with a compiler flag or with different magic identifiers. The full set of behaviors we considered included: + +1. Path as written in the compiler invocation +2. Guaranteed-absolute path +3. Path relative to the Xcode `SOURCE_DIR` value, or some equivalent +4. Last component of the path (file name only) +5. File name plus module name +6. Empty string (sensible as a compiler flag) + +We ultimately decided that supporting only 1 (as `#filePath`) and 5 (as `#file`) would adequately cover the use cases for `#file`. Five different syntaxes would devote a lot of language surface area to a small niche, and controlling the behavior with a compiler flag would create six language dialects that might break some code. Some of these behaviors would also require introducing new concepts into the compiler or would cause trouble for distributed build systems. + +### Make `#filePath` always absolute + +While we're looking at this area of the language, we could change `#filePath` to always generate an absolute path. This would make `#filePath` more stable and useful, but it would cause problems for distributed builds unless it respected `-debug-prefix-map` or something similar. It would also mean that there'd be no simple way to get the *exact* same behavior as Swift previously provided, which would make it more difficult to adapt code to this change. + +Ultimately, we think changing to an absolute path is severable from this proposal and that, if we want to do this, we should consider it separately. + +### Other alternatives + +We considered making `#file`'s behavior change conditional on enabling a new language version mode. While we're not opposed to that in principle, we don't think the breakage from this change will be severe enough to justify delaying this proposal's implementation until the next release with a language version mode. + +We considered introducing a new alternative to `#file` (e.g. `#fileName`) while preserving the existing meaning of `#file`. However, a great deal of code already uses `#file` and would in practice probably never be converted to `#fileName`. The vast majority of this code would benefit from the new behavior, so we think it would be better to automatically adopt it. (Note that clang supports a `__FILE_NAME__` alternative, but most code still uses `__FILE__` anyway.) + +We considered switching between the old and new `#file` behavior with a compiler flag. However, this creates a language dialect, and compiler flags are not a natural interface for users. + +Finally, we could change the behavior of `#file` without offering an escape hatch. However, we think that the existing behavior is useful in rare circumstances and should not be totally removed. From 98f79456d614c9e24870914bc217a206a8eb9942 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 9 Jan 2020 11:25:10 -0800 Subject: [PATCH 1368/4563] Schedule SE-0275 Allow more characters (like whitespaces and punctuations) for escaped identifiers --- ...whitespaces-and-punctuations-for-escaped-identifiers.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md => 0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md} (95%) diff --git a/proposals/NNNN-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md similarity index 95% rename from proposals/NNNN-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md rename to proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md index f9e38a4dad..91e7382e79 100644 --- a/proposals/NNNN-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md +++ b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md @@ -1,12 +1,12 @@ # Allow more characters (like whitespaces and punctuations) for escaped identifiers -* Proposal: [SE-XXXX] +* Proposal: [SE-0275] * Authors: [Alfredo Delli Bovi](https://github.com/adellibovi) -* Review Manager: TBD +* Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Awaiting review** +* Status: **Scheduled for review (January 13...January 20, 2020)** * Implementation: [apple/swift#28966](https://github.com/apple/swift/pull/28966) From 3f5786e33c4d3a896826efe761b4d284ebeab03a Mon Sep 17 00:00:00 2001 From: Thiago Holanda Date: Fri, 10 Jan 2020 18:22:09 +0100 Subject: [PATCH 1369/4563] SE-0275: Fix link to proposal markdown file (#1106) --- ...like-whitespaces-and-punctuations-for-escaped-identifiers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md index 91e7382e79..e78a060d7c 100644 --- a/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md +++ b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md @@ -1,6 +1,6 @@ # Allow more characters (like whitespaces and punctuations) for escaped identifiers -* Proposal: [SE-0275] +* Proposal: [SE-0275](0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md) * Authors: [Alfredo Delli Bovi](https://github.com/adellibovi) From 8b46761911eba85c0eeec6091b14d7113a4e1332 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 10 Jan 2020 18:15:33 +0000 Subject: [PATCH 1370/4563] [SE-0268] Fix warnings from status page --- proposals/0268-didset-semantics.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0268-didset-semantics.md b/proposals/0268-didset-semantics.md index e333958686..6d99bea812 100644 --- a/proposals/0268-didset-semantics.md +++ b/proposals/0268-didset-semantics.md @@ -1,10 +1,10 @@ # Refine `didSet` Semantics * Proposal: [SE-0268](0268-didset-semantics.md) -* Author: [Suyash Srijan](https://www.github.com/theblixguy) -* Review Manager: [Ben Cohen](https://www.github.com/airspeedswift) +* Author: [Suyash Srijan](https://github.com/theblixguy) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Accepted** -* Implementation: [apple/swift#26632](https://github.com/apple/swift/pull/26632) +* Implementation: [apple/swift#26632](https://github.com/apple/swift/pull/26632) * Bug: [SR-5982](https://bugs.swift.org/browse/SR-5982) ## Introduction From b17d85fcaf38598fd2ea19641d0e9c26c96747ec Mon Sep 17 00:00:00 2001 From: Bas Broek Date: Sun, 12 Jan 2020 19:27:09 +0100 Subject: [PATCH 1371/4563] Remove a double colon (#1108) --- proposals/0270-rangeset-and-collection-operations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index c60cbc8378..b44645879a 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -7,7 +7,7 @@ * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/9b5957c00e7483ab8664afe921f989ed1394a666/proposals/0270-rangeset-and-collection-operations.md) * Previous Review: [SE-0270: Add Collection Operations on Noncontiguous Elements](https://forums.swift.org/t/se-0270-add-collection-operations-on-noncontiguous-elements/30691) -* Previous Decision:: [Returned for revision](https://forums.swift.org/t/returned-for-revision-se-0270-add-collection-operations-on-noncontiguous-elements/31484) +* Previous Decision: [Returned for revision](https://forums.swift.org/t/returned-for-revision-se-0270-add-collection-operations-on-noncontiguous-elements/31484) ## Introduction From 0c709d72bcda5946b02067745c87217a7e3c48c8 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 13 Jan 2020 11:46:04 -0800 Subject: [PATCH 1372/4563] Kick off SE-275 review --- ...like-whitespaces-and-punctuations-for-escaped-identifiers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md index e78a060d7c..37ce197af7 100644 --- a/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md +++ b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md @@ -6,7 +6,7 @@ * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Scheduled for review (January 13...January 20, 2020)** +* Status: **Active Review (January 13...January 20, 2020)** * Implementation: [apple/swift#28966](https://github.com/apple/swift/pull/28966) From aae215e901fb399c65db888ccb8cf117f8cef694 Mon Sep 17 00:00:00 2001 From: Jeffrey Macko Date: Mon, 13 Jan 2020 23:38:25 +0100 Subject: [PATCH 1373/4563] [SE-0249] Update status to implemented (#1107) --- proposals/0249-key-path-literal-function-expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0249-key-path-literal-function-expressions.md b/proposals/0249-key-path-literal-function-expressions.md index f4b1b99327..6874a75bcf 100644 --- a/proposals/0249-key-path-literal-function-expressions.md +++ b/proposals/0249-key-path-literal-function-expressions.md @@ -3,7 +3,7 @@ * Proposal: [SE-0249](0249-key-path-literal-function-expressions.md) * Authors: [Stephen Celis](https://github.com/stephencelis), [Greg Titus](https://github.com/gregomni) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** +* Status: **Implemented (Swift 5.2)** * Implementation: [apple/swift#19448](https://github.com/apple/swift/pull/19448) ## Introduction From 007141902a6e585fa4378c51f38d64219767ef56 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 13 Jan 2020 22:40:57 +0000 Subject: [PATCH 1374/4563] [SE-0249] Update the implementation link --- proposals/0249-key-path-literal-function-expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0249-key-path-literal-function-expressions.md b/proposals/0249-key-path-literal-function-expressions.md index 6874a75bcf..2986b4b772 100644 --- a/proposals/0249-key-path-literal-function-expressions.md +++ b/proposals/0249-key-path-literal-function-expressions.md @@ -4,7 +4,7 @@ * Authors: [Stephen Celis](https://github.com/stephencelis), [Greg Titus](https://github.com/gregomni) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Implemented (Swift 5.2)** -* Implementation: [apple/swift#19448](https://github.com/apple/swift/pull/19448) +* Implementation: [apple/swift#26054](https://github.com/apple/swift/pull/26054) ## Introduction From 48abe1717e956206b40546be351b47b87e359c90 Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Wed, 15 Jan 2020 11:23:32 -0600 Subject: [PATCH 1375/4563] Add Proposal: Multi-Pattern Catch Clauses (#1084) * Add Proposal: Multi-Pattern and Conditionally Compiled Catch Clauses * Refocus proposal to only include multi-pattern catches * Update proposals/NNNN-multi-pattern-catch-clauses.md Co-Authored-By: Ben Rimmington * Update NNNN-multi-pattern-catch-clauses.md Co-authored-by: Ben Rimmington --- proposals/NNNN-multi-pattern-catch-clauses.md | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 proposals/NNNN-multi-pattern-catch-clauses.md diff --git a/proposals/NNNN-multi-pattern-catch-clauses.md b/proposals/NNNN-multi-pattern-catch-clauses.md new file mode 100644 index 0000000000..45f7dbe947 --- /dev/null +++ b/proposals/NNNN-multi-pattern-catch-clauses.md @@ -0,0 +1,126 @@ +# Multi-Pattern Catch Clauses + +* Proposal: [SE-NNNN](NNNN-multi-pattern-catch-clauses.md) +* Authors: [Owen Voorhees](https://github.com/owenv) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: + - [apple/swift#27776](https://github.com/apple/swift/pull/27776) + +## Introduction + +Currently, each catch clause in a do-catch statement may only contain a single pattern and where clause. This is inconsistent with the behavior of cases in switch statements, which provide similar functionality. It also makes some error handling patterns awkward to express. This proposal extends the grammar of catch clauses to support a comma-separated list of patterns (with optional where clauses), resolving this inconsistency. + +Swift-evolution thread: [Thread](https://forums.swift.org/t/multi-pattern-and-conditionally-compiled-catch-clauses/30246) + +## Motivation + +Currently, Swift only allows up to one pattern and where clause for each catch clause in a do-catch statement, so the following code snippet is not allowed: + +```swift +do { + try performTask() +} catch TaskError.someRecoverableError { // OK + recover() +} catch TaskError.someFailure(let msg), + TaskError.anotherFailure(let msg) { // Not currently valid + showMessage(msg) +} +``` + +Because the above snippet is not valid today, developers frequently end up duplicating code between catch clauses, or writing something like the following instead: + +```swift +do { + try performTask() +} catch let error as TaskError { + switch error { + case TaskError.someRecoverableError: + recover() + case TaskError.someFailure(let msg), + TaskError.anotherFailure(let msg): + showMessage(msg) + } +} +``` + +Nesting the switch inside of the catch clause is awkward and defeats the purpose of supporting pattern matching in catch clauses. Splitting the code up into multiple catch clauses requires duplicating the body, which is also undesirable. Supporting a multi-pattern catch clause would allow for code which is both clearer and more concise. + +## Proposed solution + +Catch clauses should allow the user to specify a comma-separated list of patterns. If an error thrown at runtime in a do block matches any of the patterns in a corresponding catch clause, that catch clause's body should be executed. Similar to switch cases, a user should be able to bind a variable in all patterns of a catch clause and then use it in the body. + +With this change, the code snippet from the motivation section is now valid: + +```swift +do { + try performTask() +} catch TaskError.someRecoverableError { // OK + recover() +} catch TaskError.someFailure(let msg), + TaskError.anotherFailure(let msg) { // Also Allowed + showMessage(msg) +} +``` + +Now, if `performTask` throws either `TaskError.someFailure("message")` or `TaskError.anotherFailure("message")`, the body of the second catch clause will be executed and `showMessage` will be called. + +## Detailed design + +### Grammar Changes + +The revised catch clause grammar is as follows: + +``` +catch-clauses -> catch-clause catch-clauses? + +catch-clause -> 'catch' catch-item-list? code-block + +catch-item-list -> catch-item | + catch-item ',' catch-item-list + +catch-item -> pattern where-clause? | + where-clause +``` + +Note: Expressions with trailing closures are not allowed in any of a catch clause's items to avoid parsing ambiguity. This differs from the behavior of switch cases. + +### Semantics + +If a catch clause has multiple patterns, then its body will be executed if a thrown error matches any one of those patterns, and has not already matched a pattern from a preceding catch clause. Similar to switch cases, catch clauses with multiple patterns may still contain value bindings. However, those bindings must have the same name and type in each pattern. + +## Source compatibility + +This proposal maintains source compatibility. It will only result in code compiling which was considered invalid by older compiler versions. + +## Effect on ABI stability + +This feature has no ABI impact. + +## Effect on API resilience + +This proposal does not introduce any new features which could become part of a public API. + +## Alternatives considered + +### Do nothing + +This is a relatively minor addition to the language, and arguably would see rather limited usage. However, in the cases where it's needed, it helps the user avoid hard-to-maintain error handling code which either duplicates functionality or nests control flow statements in a confusing manner. This proposal also simplifies Swift's pattern matching model by unifying some of the semantics of switch and do-catch statements. + +## Future Directions + +There are a number of possible future directions which could increase the expressiveness of catch clauses. + +### Implicit `$error` or `error` binding for all catch clauses + +Currently, only catch clauses without a pattern have an implicit `error: Error` binding. However, there are some cases where it would be useful to have this binding in all catch clauses to make, for example, re-throwing errors easier. However, using `error` as the identifier for this binding would be a medium-to-large source-breaking change. Instead, we could continue the trend of compiler defined identifiers and use `$error`. `error` in empty catch clauses could then be deprecated and eventually removed in future language versions, a smaller source break. + +This change was not included in this proposal because it is source-breaking and orthogonal. If there is interest in this feature, we should probably consider it as an independent improvement which deserves its own proposal. + +### `fallthrough` support in catch clauses + +Allowing `fallthrough` statements in catch clauses would further unify the semantics of switch cases and catches. However, it is currently undesirable for a number of reasons. First, it would be a source-breaking change for existing do-catch statements which trigger `fallthrough`s in an enclosing switch. Also, there is no historical precedent from other languages for supporting `fallthrough`s in catch statements, unlike switches. Finally, there have not yet been any identified examples of code which would benefit from this functionality. + +### Conditional compilation blocks around catch clauses + +An earlier draft of this proposal also added support for wrapping catch clauses in conditional compilation blocks, similar to the existing support for switch cases. This was removed in favor of keeping this proposal focused on a single topic, and leaving room for a more comprehensive redesign of conditional compilation in the future, as described in [https://forums.swift.org/t/allow-conditional-inclusion-of-elements-in-array-dictionary-literals/16171](https://forums.swift.org/t/allow-conditional-inclusion-of-elements-in-array-dictionary-literals/16171/29). From fc161e86a413b5b4f82df528d59fba4eff97db5f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Jan 2020 09:26:51 -0800 Subject: [PATCH 1376/4563] Initiate review of SE-0276: Multi-Pattern Catch Clauses --- ...catch-clauses.md => 0276-multi-pattern-catch-clauses.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-multi-pattern-catch-clauses.md => 0276-multi-pattern-catch-clauses.md} (97%) diff --git a/proposals/NNNN-multi-pattern-catch-clauses.md b/proposals/0276-multi-pattern-catch-clauses.md similarity index 97% rename from proposals/NNNN-multi-pattern-catch-clauses.md rename to proposals/0276-multi-pattern-catch-clauses.md index 45f7dbe947..d961cac250 100644 --- a/proposals/NNNN-multi-pattern-catch-clauses.md +++ b/proposals/0276-multi-pattern-catch-clauses.md @@ -1,9 +1,9 @@ # Multi-Pattern Catch Clauses -* Proposal: [SE-NNNN](NNNN-multi-pattern-catch-clauses.md) +* Proposal: [SE-0276](0276-multi-pattern-catch-clauses.md) * Authors: [Owen Voorhees](https://github.com/owenv) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active Review (January 15...January 22, 2020)** * Implementation: - [apple/swift#27776](https://github.com/apple/swift/pull/27776) From 8474ae31328bbc7469a7d6138fadfcc71a11e0ee Mon Sep 17 00:00:00 2001 From: Bas Broek Date: Wed, 15 Jan 2020 22:22:02 +0100 Subject: [PATCH 1377/4563] Fix a typo (#1112) --- ...-whitespaces-and-punctuations-for-escaped-identifiers.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md index 37ce197af7..c41ef73628 100644 --- a/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md +++ b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md @@ -16,7 +16,7 @@ As part of that, escaped identifiers are adopted to allow usage of reserved keyw This proposal wants to extend the character allowance for escaped identifiers with more Unicode scalars, like whitespace and punctuation. It will enable to have method names (or other identifiers) with a more readable and natural language like the following: ```swift -func `test validation should succeed when input is less then ten`() +func `test validation should succeed when input is less than ten`() ``` ## Motivation Naming could be hard and having descriptive methods, like in tests, may result in declarations that are hard to read because of its lack of whitespace and punctuations or other symbols. Enabling natural language would improve readability. @@ -30,13 +30,13 @@ This proposal wants to extend the current grammar for every escaped identifier ( A declaration to an escaped identifier will follow the existing back-ticked syntax. ```swift -func `test validation should succeed when input is less then ten`() +func `test validation should succeed when input is less than ten`() var `some var` = 0 ``` As per referencing. ```swift -`test validation should succeed when input is less then ten`() +`test validation should succeed when input is less than ten`() foo.`property with space` ``` In fact, by allowing a larger set of characters, we will remove current limitations and, as an example, we will enable us to reference an operator, which currently produces an error. From 25dbdde3b29daf3f698ef980546c40264ffc223b Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 16 Jan 2020 00:07:01 +0000 Subject: [PATCH 1378/4563] [SE-0276] Move implementation link for status page --- proposals/0276-multi-pattern-catch-clauses.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/proposals/0276-multi-pattern-catch-clauses.md b/proposals/0276-multi-pattern-catch-clauses.md index d961cac250..77039e4fc4 100644 --- a/proposals/0276-multi-pattern-catch-clauses.md +++ b/proposals/0276-multi-pattern-catch-clauses.md @@ -1,11 +1,10 @@ # Multi-Pattern Catch Clauses * Proposal: [SE-0276](0276-multi-pattern-catch-clauses.md) -* Authors: [Owen Voorhees](https://github.com/owenv) +* Author: [Owen Voorhees](https://github.com/owenv) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active Review (January 15...January 22, 2020)** -* Implementation: - - [apple/swift#27776](https://github.com/apple/swift/pull/27776) +* Implementation: [apple/swift#27776](https://github.com/apple/swift/pull/27776) ## Introduction From d803bcd2d836ba80da0aef253151e57d0115a4ea Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Tue, 21 Jan 2020 15:32:54 -0600 Subject: [PATCH 1379/4563] Update SE-0270 proposal with 3rd revision --- ...0270-rangeset-and-collection-operations.md | 200 ++++++++++-------- 1 file changed, 108 insertions(+), 92 deletions(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index b44645879a..69e951436a 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -5,13 +5,13 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Pending Core Team feedback** * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) -* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/9b5957c00e7483ab8664afe921f989ed1394a666/proposals/0270-rangeset-and-collection-operations.md) -* Previous Review: [SE-0270: Add Collection Operations on Noncontiguous Elements](https://forums.swift.org/t/se-0270-add-collection-operations-on-noncontiguous-elements/30691) -* Previous Decision: [Returned for revision](https://forums.swift.org/t/returned-for-revision-se-0270-add-collection-operations-on-noncontiguous-elements/31484) +* Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/9b5957c00e7483ab8664afe921f989ed1394a666/proposals/0270-rangeset-and-collection-operations.md), [2](https://github.com/apple/swift-evolution/blob/b17d85fcaf38598fd2ea19641d0e9c26c96747ec/proposals/0270-rangeset-and-collection-operations.md) +* Previous Reviews: [1](https://forums.swift.org/t/se-0270-add-collection-operations-on-noncontiguous-elements/30691), [2](https://forums.swift.org/t/se-0270-review-2-add-collection-operations-on-noncontiguous-elements/31653) +* Previous Decision: [1](https://forums.swift.org/t/returned-for-revision-se-0270-add-collection-operations-on-noncontiguous-elements/31484) ## Introduction -We can use a `Range` to refer to a group of consecutive positions in a collection, but the standard library doesn't currently provide a way to refer to discontiguous positions in an arbitrary collection. I propose the addition of a `RangeSet` type that can store any number of positions, along with collection algorithms that operate on those positions. +We can use a `Range` to refer to a group of consecutive positions in a collection, but the standard library doesn't currently provide a way to refer to discontiguous positions in an arbitrary collection. I propose the addition of a `RangeSet` type that can represent any number of positions, along with collection algorithms that operate on those positions. ## Motivation @@ -27,33 +27,18 @@ This proposal adds a `RangeSet` type for representing multiple, noncontiguous ra var numbers = Array(1...15) // Find the indices of all the even numbers -let indicesOfEvens = numbers.ranges(where: { $0.isMultiple(of: 2) }) +let indicesOfEvens = numbers.subranges(where: { $0.isMultiple(of: 2) }) // Perform an operation with just the even numbers let sumOfEvens = numbers[indicesOfEvens].reduce(0, +) // sumOfEvens == 56 // You can gather the even numbers at the beginning -let rangeOfEvens = numbers.gather(indicesOfEvens, at: numbers.startIndex) +let rangeOfEvens = numbers.moveSubranges(indicesOfEvens, to: numbers.startIndex) // numbers == [2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15] // numbers[rangeOfEvens] == [2, 4, 6, 8, 10, 12, 14] - -// Reset `numbers` -numbers = Array(1...15) - -// You can also build range sets by hand... -let notTheMiddle = RangeSet([0..<5, 10..<15]) -print(Array(numbers[notTheMiddle])) -// Prints [1, 2, 3, 4, 5, 11, 12, 13, 14, 15] - -// ...or by using set operations -let smallEvens = indicesOfEvens.intersection( - numbers.ranges(where: { $0 < 10 })) -print(Array(numbers[smallEvens])) -// Prints [2, 4, 6, 8] ``` -These are just a few examples; the complete proposal includes operations like inverting a range set and adding and removing ranges, which are covered in the next section. ## Detailed design @@ -96,9 +81,54 @@ public struct RangeSet { } ``` -`RangeSet` conforms to `Equatable`, to `CustomStringConvertible`, and, when its `Bound` type conforms to `Hashable`, to `Hashable`. `RangeSet` also has `ExpressibleByArrayLiteral` conformance, using arrays of ranges as its literal type. +`RangeSet` conforms to `Equatable`, to `CustomStringConvertible`, and, when its `Bound` type conforms to `Hashable`, to `Hashable`. + +#### Conveniences for working with collection indices + +Although a range set can represent a set of values of any `Comparable` type, the primary intended use case is to maintain a set of indices into a collection. To streamline this workflow, `RangeSet` includes an additional initializer and methods for inserting and removing individual indices. + +```swift +extension RangeSet { + /// Creates a new range set containing ranges that contain only the + /// specified indices in the given collection. + /// + /// - Parameters: + /// - index: The index to include in the range set. `index` must be a + /// valid index of `collection` that isn't the collection's `endIndex`. + /// - collection: The collection that contains `index`. + public init(_ indices: S, within collection: C) + where S: Sequence, C: Collection, S.Element == C.Index, C.Index == Bound + + /// Inserts a range that contains only the specified index into the range + /// set. + /// + /// - Parameters: + /// - index: The index to insert into the range set. `index` must be a + /// valid index of `collection` that isn't the collection's `endIndex`. + /// - collection: The collection that contains `index`. + /// + /// - Complexity: O(*n*), where *n* is the number of ranges in the range + /// set. + public mutating func insert(_ index: Bound, within collection: C) + where C: Collection, C.Index == Bound + + /// Removes the range that contains only the specified index from the range + /// set. + /// + /// - Parameters: + /// - index: The index to remove from the range set. `index` must be a + /// valid index of `collection` that isn't the collection's `endIndex`. + /// - collection: The collection that contains `index`. + /// + /// - Complexity: O(*n*), where *n* is the number of ranges in the range + /// set. + public mutating func remove(_ index: Bound, within collection: C) + where C: Collection, C.Index == Bound +} +``` + -#### Accessing Underlying Ranges and Individual Indices +#### Accessing underlying ranges `RangeSet` provides access to its ranges as a random-access collection via the `ranges` property. You can access the individual indices represented by the range set by using it as a subscript parameter to a collection's `indices` property. @@ -133,7 +163,8 @@ set.insert(contentsOf: 7..<10) #### `SetAlgebra`-like methods -`RangeSet` implements a subset of the `SetAlgebra` protocol. +`RangeSet` implements a subset of the `SetAlgebra` protocol, +for working with more than one `RangeSet`. ```swift extension RangeSet { @@ -158,13 +189,13 @@ extension RangeSet { #### Storage -`RangeSet` stores its ranges in a custom type that will optimize the storage for known, simple `Bound` types. This custom type will avoid allocating extra storage for common cases of empty or single-range range sets. +`RangeSet` stores its ranges in a custom type that optimizes the storage for known, simple `Bound` types. This custom type will avoid allocating extra storage for the common cases of empty and single-range sets. ### New `Collection` APIs #### Finding multiple elements -Akin to the `firstIndex(...)` and `lastIndex(...)` methods, this proposal introduces `ranges(where:)` and `ranges(of:)` methods that return a range set with the indices of all matching elements in a collection. +Akin to the `firstIndex(...)` and `lastIndex(...)` methods, this proposal introduces `subranges(where:)` and `subranges(of:)` methods that return a range set with the indices of all matching elements in a collection. ```swift extension Collection { @@ -175,11 +206,11 @@ extension Collection { /// /// let str = "Fresh cheese in a breeze" /// let vowels: Set = ["a", "e", "i", "o", "u"] - /// let allTheVowels = str.ranges(where: { vowels.contains($0) }) + /// let allTheVowels = str.subranges(where: { vowels.contains($0) }) /// // str[allTheVowels].count == 9 /// /// - Complexity: O(*n*), where *n* is the length of the collection. - public func ranges(where predicate: (Element) throws -> Bool) rethrows + public func subranges(where predicate: (Element) throws -> Bool) rethrows -> RangeSet } @@ -191,17 +222,17 @@ extension Collection where Element: Equatable { /// particular letter occurs in a string. /// /// let str = "Fresh cheese in a breeze" - /// let allTheEs = str.ranges(of: "e") + /// let allTheEs = str.subranges(of: "e") /// // str[allTheEs].count == 7 /// /// - Complexity: O(*n*), where *n* is the length of the collection. - public func ranges(of element: Element) -> RangeSet + public func subranges(of element: Element) -> RangeSet } ``` #### Accessing elements via `RangeSet` -When you have a `RangeSet` describing a group of indices for a collection, you can access those elements via a new subscript. This new subscript returns a new `DiscontiguousSlice` type, which couples the collection and range set to provide access. +When you have a `RangeSet` describing a group of indices for a collection, you can access those elements via subscript. This subscript returns a new `DiscontiguousSlice` type, which couples the collection and range set to provide access. ```swift extension Collection { @@ -209,7 +240,7 @@ extension Collection { /// indices. /// /// - Complexity: O(1) - public subscript(indices: RangeSet) -> DiscontiguousSlice { get } + public subscript(subranges: RangeSet) -> DiscontiguousSlice { get } } extension MutableCollection { @@ -217,9 +248,9 @@ extension MutableCollection { /// given indices. /// /// - Complexity: O(1) to access the elements, O(*m*) to mutate the - /// elements at the positions in `indices`, where *m* is the number of - /// elements indicated by `indices`. - public subscript(indices: RangeSet) -> DiscontiguousSlice { get set } + /// elements at the positions in `subranges`, where *m* is the number of + /// elements indicated by `subranges`. + public subscript(subranges: RangeSet) -> DiscontiguousSlice { get set } } /// A collection wrapper that provides access to the elements of a collection, @@ -230,7 +261,7 @@ public struct DiscontiguousSlice: Collection { /// The set of index ranges that are available through this indexing /// collection. - public var ranges: RangeSet { get set } + public var subranges: RangeSet { get set } /// A position in an `DiscontiguousSlice`. struct Index: Comparable { @@ -246,9 +277,9 @@ public struct DiscontiguousSlice: Collection { `DiscontiguousSlice` conforms to `Collection`, and conditionally conforms to `BidirectionalCollection` and `MutableCollection` if its base collection conforms. -#### Gathering elements +#### Moving elements -Within a mutable collection, you can gather the elements represented by a range set, moving them to be in a contiguous range before the element at a specific index, and otherwise preserving element order. This proposal also adds a method for gathering all the elements matched by a predicate. When gathering elements, other elements slide over to fill gaps left by the elements that move. For that reason, these gathering methods return the new range of the elements that are moved. +Within a mutable collection, you can move the elements represented by a range set to be in a contiguous range before the element at a specific index, while otherwise preserving element order. When moving elements, other elements slide over to fill gaps left by the elements that move. For that reason, this method returns the new range of the elements that are moved. ```swift extension MutableCollection { @@ -259,50 +290,27 @@ extension MutableCollection { /// them between `"i"` and `"j"`. /// /// var letters = Array("ABCdeFGhijkLMNOp") - /// let uppercase = letters.ranges(where: { $0.isUppercase }) - /// let rangeOfUppercase = letters.gather(uppercase, at: 10) + /// let uppercaseRanges = letters.subranges(where: { $0.isUppercase }) + /// let rangeOfUppercase = letters.moveSubranges(uppercaseRanges, to: 10) /// // String(letters) == "dehiABCFGLMNOjkp" /// // rangeOfUppercase == 4..<13 /// /// - Parameters: - /// - indices: The indices of the elements to move. + /// - subranges: The indices of the elements to move. /// - insertionPoint: The index to use as the destination of the elements. /// - Returns: The new bounds of the moved elements. /// /// - Complexity: O(*n* log *n*) where *n* is the length of the collection. @discardableResult - public mutating func gather( - _ indices: RangeSet, at insertionPoint: Index - ) -> Range - - /// Collects the elements that satisfy the given predicate just before the - /// element at the specified index. - /// - /// This example gathers all the uppercase letters in the array between - /// `"i"` and `"j"`. - /// - /// var letters = Array("ABCdeFGhijkLMNOp") - /// let rangeOfUppercase = letters.gather(at: 10) { $0.isUppercase } - /// // String(letters) == "dehiABCFGLMNOjkp" - /// // rangeOfUppercase == 4..<13 - /// - /// - Parameters: - /// - predicate: A closure that returns `true` for elements that should - /// move to `destination`. - /// - insertionPoint: The index to use as the destination of the elements. - /// - Returns: The new bounds of the moved elements. - /// - /// - Complexity: O(*n* log *n*) where *n* is the length of the collection. - @discardableResult - public mutating func gather( - at insertionPoint: Index, where predicate: (Element) -> Bool + public mutating func moveSubranges( + _ subranges: RangeSet, to insertionPoint: Index ) -> Range } ``` #### Removing elements -Within a range-replaceable collection, you can remove the elements represented by a range set. `removeAll(at:)` is a new `RangeReplaceableCollection` requirement with a default implementation, along with an overload for collections that also conform to `MutableCollection`. +Within a range-replaceable collection, you can remove the elements represented by a range set. `removeSubranges(_:)` is a new `RangeReplaceableCollection` requirement with a default implementation, along with an overload for collections that also conform to `MutableCollection`. ```swift extension RangeReplaceableCollection { @@ -313,15 +321,15 @@ extension RangeReplaceableCollection { /// /// var str = "The rain in Spain stays mainly in the plain." /// let vowels: Set = ["a", "e", "i", "o", "u"] - /// let vowelIndices = str.ranges(where: { vowels.contains($0) }) + /// let vowelIndices = str.subranges(where: { vowels.contains($0) }) /// - /// str.removeAll(at: vowelIndices) + /// str.removeSubranges(vowelIndices) /// // str == "Th rn n Spn stys mnly n th pln." /// - /// - Parameter indices: The indices of the elements to remove. + /// - Parameter subranges: A range set representing the elements to remove. /// /// - Complexity: O(*n*), where *n* is the length of the collection. - public mutating func removeAll(at indices: RangeSet) + public mutating func removeSubranges(_ subranges: RangeSet) } extension Collection { @@ -334,29 +342,15 @@ extension Collection { /// /// let str = "The rain in Spain stays mainly in the plain." /// let vowels: Set = ["a", "e", "i", "o", "u"] - /// let vowelIndices = str.ranges(where: { vowels.contains($0) }) + /// let vowelIndices = str.subranges(where: { vowels.contains($0) }) /// - /// let disemvoweled = str.removingAll(at: vowelIndices) + /// let disemvoweled = str.removingSubranges(vowelIndices) /// print(String(disemvoweled)) /// // Prints "Th rn n Spn stys mnly n th pln." /// - /// - Parameter indices: A range set representing the elements to remove. + /// - Parameter subranges: A range set representing the elements to remove. /// - Returns: A collection of the elements that are not in `indices`. - public func removingAll(at indices: RangeSet) -> DiscontiguousSlice -} -``` - -#### `range(at:)` helper method - -The proposal also adds an individual `Collection` method to get the range for an individual index. This streamlines adding individual indices to or removing them from a `RangeSet`. - -```swift -extension Collection { - /// Returns a range that contains the single given index with respect to - /// this collection. - public func range(at position: Index) -> Range { - position..) -> DiscontiguousSlice } ``` @@ -411,10 +405,32 @@ set.remove(4) // set.ranges == [.fullyOpen(1, 2), .fullyOpen(2, 3), .fullyOpen(3, 4)] ``` -### `Elements` View +### `Elements` view In an earlier version of the proposal, `RangeSet` included a collection view of the indices, conditionally available when the `Bound` type was strideable with an integer stride. This collection view was removed for the same reasons as covered in the section above. Users can access these indices by using a `RangeSet` as a subscript for the source collection's `indices` property. -### Helpers for working with individual `Collection` indices -In an earlier version of this proposal, `RangeSet` included several methods that accepted an individual index or a range expression as a parameter, along with the matching collection, so that the `RangeSet` could convert the index or range expression into a concrete range. These methods have been removed; instead, use the regular `Range`-based operations for creating sets or adding and removing ranges of values. +### More helpers for working with individual `Collection` indices + +In an earlier version of this proposal, `RangeSet` included several methods that accepted range expressions as a parameter, along with the matching collection, so that the `RangeSet` could convert the range expression into a concrete range. These methods have been removed; instead, convert range expressions to concrete ranges before using `RangeSet` methods. + +There has been some concern that any APIs that take both an index and a collection represent a violation of concerns. However, these kinds of methods are already well-represented in the standard library (such as `RangeExpression.relative(to:)`), and are necessary for building readable interfaces that work with the design of Swift's collections and indices. + + +### A predicate-based `gather` + +This proposal originally included a predicate-based `gather` method (at a time when `moveSubranges(_:to:)` was named `gather(in:at:)`). This predicate-based method has been removed to allow design work to continue on the larger issue of predicate-based mutating collection methods. + +In particular, the issue with this method stems from the fact that a collection user may sometimes want to use a predicate that operates on collection elements (e.g. to check for even elements), and may sometimes want to use a predicate that operates on collection indices (e.g. to test for indices that are part of a known group). For non-mutating methods, this poses no problem, as one can call the predicate-based method on the collection's `indices` property instead. However, with mutating methods, this kind of access poses issues with copy-on-write and exclusivity. + +One potential solution is to offer an `(Index, Element) -> Bool` predicate for mutating methods instead of the currently standard `(Element) -> Bool` predicate. This kind of change should be considered for existing mutating methods, like `removeAll(where:)` and `partition(by:)`, as well as any future additions. + + +### Other bikeshedding + +The review garnered several alternative names for the `RangeSet` type. Some were too tied to the index use case (such as `IndexRangeSet`, `DiscontiguousIndices`, `SomeIndices`), while others didn't represent enough of an obvious improvement to supplant the proposed name (such as `SparseRange`, `DiscontiguousRange`, or `RangeBasedSet`). + +There were also a suggestion that the methods for inserting and removing ranges of values should be aligned with the `SetAlgebra` methods `formUnion` and `subtract`. Instead, to keep the `RangeSet` API aligned with user expectations, these operations will keep the names `insert(contentsOf:)` and `remove(contentsOf:)`. + +As a result of other feedback, some of the collection operations have been renamed. Instead of `removeAll(in:)` to match `removeAll(where:)`, removing the elements represented by a `RangeSet` is now `removeSubranges(_:)`, as a partner to `removeSubrange(_:)`. Similarly, the `gather(in:at:)` method has been renamed to `moveSubranges(_:to:)`. These names do better at continuing the naming scheme set up by `RangeReplaceableCollection`. + From 542cc9ce70908063ec508c7763686e01faabfd55 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 21 Jan 2020 23:03:21 -0800 Subject: [PATCH 1380/4563] Put SE-0270 back in active review --- proposals/0270-rangeset-and-collection-operations.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index 69e951436a..733df61729 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -3,9 +3,10 @@ * Proposal: [SE-0270](0270-rangeset-and-collection-operations.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Pending Core Team feedback** +* Status: **Active review (January 21st...28th, 2020)** * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/9b5957c00e7483ab8664afe921f989ed1394a666/proposals/0270-rangeset-and-collection-operations.md), [2](https://github.com/apple/swift-evolution/blob/b17d85fcaf38598fd2ea19641d0e9c26c96747ec/proposals/0270-rangeset-and-collection-operations.md) +* Review: [current](https://forums.swift.org/t/se-0270-review-3-add-collection-operations-on-noncontiguous-elements/32839) * Previous Reviews: [1](https://forums.swift.org/t/se-0270-add-collection-operations-on-noncontiguous-elements/30691), [2](https://forums.swift.org/t/se-0270-review-2-add-collection-operations-on-noncontiguous-elements/31653) * Previous Decision: [1](https://forums.swift.org/t/returned-for-revision-se-0270-add-collection-operations-on-noncontiguous-elements/31484) From 31f954b2c242d7bfa2523783e5bae4b874d99526 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 21 Jan 2020 23:07:56 -0800 Subject: [PATCH 1381/4563] Editorial changes to the header fields --- proposals/0270-rangeset-and-collection-operations.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index 733df61729..e60efeb67d 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -6,9 +6,8 @@ * Status: **Active review (January 21st...28th, 2020)** * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/9b5957c00e7483ab8664afe921f989ed1394a666/proposals/0270-rangeset-and-collection-operations.md), [2](https://github.com/apple/swift-evolution/blob/b17d85fcaf38598fd2ea19641d0e9c26c96747ec/proposals/0270-rangeset-and-collection-operations.md) -* Review: [current](https://forums.swift.org/t/se-0270-review-3-add-collection-operations-on-noncontiguous-elements/32839) -* Previous Reviews: [1](https://forums.swift.org/t/se-0270-add-collection-operations-on-noncontiguous-elements/30691), [2](https://forums.swift.org/t/se-0270-review-2-add-collection-operations-on-noncontiguous-elements/31653) -* Previous Decision: [1](https://forums.swift.org/t/returned-for-revision-se-0270-add-collection-operations-on-noncontiguous-elements/31484) +* Reviews: [1](https://forums.swift.org/t/se-0270-add-collection-operations-on-noncontiguous-elements/30691), [2](https://forums.swift.org/t/se-0270-review-2-add-collection-operations-on-noncontiguous-elements/31653), [3](https://forums.swift.org/t/se-0270-review-3-add-collection-operations-on-noncontiguous-elements/32839) +* Decision Notes: [1](https://forums.swift.org/t/returned-for-revision-se-0270-add-collection-operations-on-noncontiguous-elements/31484), [2](https://forums.swift.org/t/revised-se-0270-add-collection-operations-on-noncontiguous-elements/32840) ## Introduction From 04ec243a4175a90ccfa31fc38dc9a99f32041ca7 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Fri, 24 Jan 2020 00:57:08 -0600 Subject: [PATCH 1382/4563] clarify lexicographic behavior when comparing enums with associated values (#1117) --- proposals/0266-synthesized-comparable-for-enumerations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0266-synthesized-comparable-for-enumerations.md b/proposals/0266-synthesized-comparable-for-enumerations.md index 833af82c46..5ee3b775a9 100644 --- a/proposals/0266-synthesized-comparable-for-enumerations.md +++ b/proposals/0266-synthesized-comparable-for-enumerations.md @@ -97,7 +97,7 @@ enum Membership: Comparable { ## Proposed solution -Enumeration types which opt-in to a synthesized `Comparable` conformance would compare according to case declaration order, with later cases comparing greater than earlier cases. Only `enum` types with no associated values and `enum` types with only `Comparable` associated values would be eligible for synthesized conformances. No `enum` types with raw values would qualify. +Enumeration types which opt-in to a synthesized `Comparable` conformance would compare according to case declaration order, with later cases comparing greater than earlier cases. Only `enum` types with no associated values and `enum` types with only `Comparable` associated values would be eligible for synthesized conformances. The latter kind of `enum`s will compare by case declaration order first, and then lexicographically by payload values. No `enum` types with raw values would qualify. While basing behavior off of declaration order is unusual for Swift, as we generally hew to the “all fields are reorderable by the compiler” principle, it is not a foreign concept to `enums`. For example, reordering cases in a numeric-backed raw `enum` already changes its runtime behavior, since the case declaration order is taken to be meaningful in that context. I also believe that `enum` cases and `struct`/`class` fields are sufficiently distinct concepts that making enumeration case order meaningful would not make the language incoherent. From 932e437955c7f77870310d6f4e3270b4732fc8bf Mon Sep 17 00:00:00 2001 From: Alfredo Delli Bovi Date: Fri, 24 Jan 2020 07:57:43 +0100 Subject: [PATCH 1383/4563] Amend changes to SE-0275 (#1113) * Amend changes to 0275 * Update 0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md * Update 0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md --- ...nd-punctuations-for-escaped-identifiers.md | 79 +++++++++++++++++-- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md index c41ef73628..518f8f005f 100644 --- a/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md +++ b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md @@ -14,19 +14,41 @@ Swift has a beautiful concise yet expressive syntax. As part of that, escaped identifiers are adopted to allow usage of reserved keywords. This proposal wants to extend the character allowance for escaped identifiers with more Unicode scalars, like whitespace and punctuation. -It will enable to have method names (or other identifiers) with a more readable and natural language like the following: -```swift -func `test validation should succeed when input is less than ten`() -``` + ## Motivation + Naming could be hard and having descriptive methods, like in tests, may result in declarations that are hard to read because of its lack of whitespace and punctuations or other symbols. Enabling natural language would improve readability. +```swift +func `test validation should succeed when input is less than ten`() // currently not possible +// vs +func testValidationShouldSucceedWhenInputIsLessThanTen() // camelCase +func test_Validation_Should_Succeed_When_Input_Is_Less_Than_Ten() // camel_Case_Mixed_Snake_Case +func test_validationShouldSucceed_whenInputIs_lessThanTen() //camelCase_Mixed_SnakeCase_Grouped +``` + Maintainers of different projects under the [Swift Source Compatibility](https://swift.org/source-compatibility/#current-list-of-projects) uses, instead of Swift's method declaration, testing frameworks, like [Quick](https://github.com/Quick/Quick), because (among other reasons) how they can elegantly express tests descriptions. Other modern languages like [F#](https://fsharp.org) and [Kotlin](https://kotlinlang.org) saw the value in supporting natural language for escaped identifiers. Today, naming methods with spaces and punctuation are, for those languages, a standard for tests, widely adopted and supported by different test runners and reporting tools. +Another limitation of identifiers is that they can include digits (0-9), but they can not start with one. +There are certain scenarios where it would be beneficial to be able to start with a digit. +```swift +enum Version { + case `1` // currently not possible + case `1.2` // currently not possible +} +enum HTTPStatus { + case `300` // currently not possible +} +``` +Code generators are also affected, as they are required to mangle invalid code points in identifiers in order to produce correct Swift code. +As an example, asset names to typed values tools, like [R.swift](https://github.com/mac-cain13/R.swift), can not express identifiers like `10_circle` (and others from Apple's SF Symbols), without losing a 1 to 1 map to their original asset names. Having to prefix (i.e.: `_10_circle`), replace (i.e.: `ten_circle`) or strip (i.e.: `_circle`) will affect discoverability of those. + +Non-English idioms, like French, heavily rely on apostrophes or hyphens and trying to replace those code points in an identifier will likely result in a less readable version. + ## Proposed solution -This proposal wants to extend the current grammar for every escaped identifier (properties, methods, types etc...) by allowing every Unicode scalar. +This proposal wants to extend the current grammar for every escaped identifier (properties, methods, types, etc...) by allowing every Unicode scalar except every type of line terminators and back-ticks. A declaration to an escaped identifier will follow the existing back-ticked syntax. ```swift @@ -39,9 +61,20 @@ As per referencing. `test validation should succeed when input is less than ten`() foo.`property with space` ``` -In fact, by allowing a larger set of characters, we will remove current limitations and, as an example, we will enable us to reference an operator, which currently produces an error. + +It is important to clarify how escaped identifiers will behave. +In general, an escaped identifier will respect any meaning the non-escaping version may have, if rappresentable. +This means that any current semantic restriction will be respected: +* Dollar identifiers are compiler-reserved names and defining ``` `$identifierNames` ``` will produce an error, as it is already happening. +* Escaped identifiers that can be expressed, within their context, without back-ticks as operators, will be considered operators, therefore they will respect operators semantics. +```swift +static func `+`(lhs: Int, rhs: Int) -> Int // Is an operator +func `test +`() // Is not an operator but a valid method +``` + +The proposal, by allowing a larger set of characters will remove other limitations as, for example, referencing to operators. ```swift -let add = Int.`+` +let add = Int.`+` // currently not possible ``` ### Grammar @@ -52,7 +85,7 @@ identifier → ` identifier-head identifier-characters opt ` with: ``` identifier → ` escaped-identifier ` -escaped-identifier -> Any Unicode scalar value except U+000A or U+000D or U+0060 +escaped-identifier -> Any Unicode scalar value except U+000A (line feed), U+000B (vertical tab), U+000C (form feed), U+000D (carriage return), U+0085 (next line), U+2028 (line separator), U+2029 (paragraph separator) or U+0060 (back-tick) ``` ### Objective-C Interoperability @@ -73,3 +106,33 @@ This feature does not affect the API. ## Alternatives considered It was considered to extend the grammars for methods declaration only, this was later discarded because we want to keep usage consistency and it would be hard to explain why an escaped identifier may support a certain set of characters in a context and a different one in another context. + +It was suggested, as an alternative, for the testing method names use case to add a method attribute: +``` +@test("test validation should succeed when input is less than ten") +func testValidationShouldSuccedWhenInputIsLessThanTen() {} +``` +It was not considered a valid option for few reasons: +* it introduces information redudancy +* it is not applicable for the rest of the issues mentioned above +* adding a new attribute would likely to introduce more complexity to the compiler and to the test runner + +Swift currently treats Unicode values individually, this means that a character that can have different representations (like a-grave: U+00E0 'à' or U+0061 U+0300 'a' + combining grave accent) will be treated as different identifiers. Swift also supports unprintable characters like zero-width character (U+200B) that allows identifiers that look the same be treated as different. +```swift +let ​ = 3 // Currently valid, zero-width character identifier +let space​Here = 3 // Currently valid, with zero-width character between `space` and `Here` +let spaceHere = 3 // Currently valid, does not conflict from the above because represented differently, no zero width character +let à = 3 // U+00E0 // Currently valid +let à = 3 // U+0061 U+0300 // Currently valid, does not conflict from the above because represented differently +``` +While this issue can be related to escaped identifiers too, we believe it should be addressed separately as it is an existing issue that is affecting non-escaping identifiers and other grammars tokens. + +## Future direction +It may be possible, if relevant, to support new lines or back-ticks using a similar raw string literals approach. +```swift +func #`this has a ` back-tick`#() +func ###`this has a +new line`###() +``` + +It was considered that the proposal, by including code points like `<`, `>`, `.` may confuse a possible future runtime API for type retrieval by a string (i.e.: `typeByName("Foo.Bar")`). In that hypothetical scenario using back-ticks as part of string could be sufficient in order to resolve ambiguity. From 6b9e81c7628b667f54375be491c1e8b68c6931d4 Mon Sep 17 00:00:00 2001 From: Brent Royal-Gordon Date: Tue, 28 Jan 2020 09:41:28 -0800 Subject: [PATCH 1384/4563] Revise SE-0274 with feedback from first review (#1118) * Revise SE-0274 with feedback from first review * Improve wording of "changes since" section * Fix typo * Kick off re-review Co-authored-by: Ben Cohen --- proposals/0274-magic-file.md | 156 ++++++++++++++++++++++------------- 1 file changed, 99 insertions(+), 57 deletions(-) diff --git a/proposals/0274-magic-file.md b/proposals/0274-magic-file.md index 1d234a19fc..a5ea1dcf8a 100644 --- a/proposals/0274-magic-file.md +++ b/proposals/0274-magic-file.md @@ -3,8 +3,9 @@ * Proposal: [SE-0274](0274-magic-file.md) * Authors: [Brent Royal-Gordon](https://github.com/brentdax), [Dave DeLong](https://github.com/davedelong) * Review Manager: [Ben Cohen](https://github.com/airspeedswift/) -* Status: **Active Review (8-16 January 2020)** -* Implementation: [apple/swift#25656](https://github.com/apple/swift/pull/25656); merged behind `-enable-experimental-concise-pound-file` +* Original review: [Returned for revision](https://forums.swift.org/t/se-0274-concise-magic-file-names/32373/50) +* Status: **Active Re-review (28 January-4 February 2020)** +* Implementation: Prototype in master behind `-Xfrontend -enable-experimental-concise-pound-file`; revisions in [apple/swift#29412](https://github.com/apple/swift/pull/29412) ## Introduction @@ -12,6 +13,18 @@ Today, `#file` evaluates to a string literal containing the full path to the cur Swift-evolution thread: [Concise Magic File Names](https://forums.swift.org/t/concise-magic-file-names/31297), [We need `#fileName`](https://forums.swift.org/t/we-need-filename/19781) +## Changes since the first review + +* We now specify that the `#file` string will have the format `/` (with a second form for future expansion) and discuss how to parse it. The previous revision left this format as a compiler implementation detail that tools should always treat as opaque. + +* We now discuss the behavior of `#sourceLocation`'s `file` parameter and provide for a warning when it creates conflicts. The previous revision did not discuss these topics. + +* We now mention the need for tooling to map `#file` strings back to paths. + +* We now provide for a warning when a wrapper around a `#filePath`-defaulting function passes it `#file` instead, or vice versa. The previous revision did not discuss this. + +* We have added several suggestions from the first review to the "alternatives considered" section to explain why we aren't proposing them. + ## Motivation In Swift today, the magic identifier `#file` evaluates to a string literal containing the full path (that is, the path passed to `swiftc`) to the current file. It's a nice way to trace the location of logic occurring in a Swift process, but its use of a full path has a lot of drawbacks: @@ -32,98 +45,109 @@ Meanwhile, the benefits you might expect from using a full path aren't really th For `#file`'s most important use case, it seems like the current string computed by `#file` is suboptimal and a better default would improve Swift in several ways. -### Other uses of `#file` +## Proposed solution -While `#file` is primarily intended for developer-facing errors, in practice it is also used in other ways. In particular, Swift tests sometimes use `#file` to compute paths to fixtures relative to the source file that uses them. This has historically been necessary in SwiftPM because it did not support resources, but [SE-0271][] has now added a much better solution to this problem than `#file`-based path computation. +We propose changing the string that `#file` evaluates to—instead of evaluating to the full path, it will now have the format `/`. For those applications which still need a full path, we will provide a new magic identifier, `#filePath`. Both of these features will otherwise behave the same as the old `#file`, including capturing the call site location when used in default arguments. The standard library's assertion and error functions will continue to use `#file`. - [SE-0271]: https://github.com/apple/swift-evolution/blob/master/proposals/0271-package-manager-resources.md - -It's worth reiterating that these uses are inherently fragile and unreliable. Not only do they rely on the build system choosing to pass absolute paths, they also rely on a full checkout of the project being present on disk at the same path as when the binary was built. That means these tests will probably fail when run anywhere but on the machine they were built on, including on attached iOS/tvOS/watchOS devices. +With this proposal, a file at `/Users/brent/Desktop/0274-magic-file.swift` in a module named `MagicFile` with this content: -Nevertheless, we know that some projects in the wild *do* use `#file` this way; the question is how common these uses are. We attempted to answer this question by analyzing the Swift Source Compatibility Suite in three ways: +```swift +print(#file) +print(#filePath) +fatalError("Something bad happened!") +``` -1. We used regular expressions to automatically examine the 1,073 uses of `#file` in all 108 projects, trying to categorize them as being either for human display or machine consumption. We concluded that approximately 91–96% of uses are for human display.[1] +Would produce this output: -2. We manually examined 172 of those uses in sixteen projects. We concluded that approximately 95% of the uses we looked at would benefit from the new `#file` behavior. +```text +MagicFile/0274-magic-file.swift +/Users/brent/Desktop/0274-magic-file.swift +Fatal error: Something bad happened!: file MagicFile/0274-magic-file.swift, line 3 +``` -3. We ran tests for the 21 projects which had configured test targets and passed in at least one language version mode. Three of these projects—`Html`, `Lark`, and `swift-nio-http2`—showed new failures with a shorter `#file` string. In all three cases, the broken code looked up test fixtures relative to `#file`; the projects themselves appeared to function normally. We concluded that a small number of projects use `#file` in ways that require a full path, but only in their tests. +The Swift compiler does not currently allow two files with the same name (regardless of path) to be compiled into the same module.[1] This means that `/` does a better job of uniquely identifying a file than the full path without taking up nearly as much space. It is also easy for a human to read and map back to a source file; tools can automatically map these strings back to paths if they know which files are in each module, which compiler tooling could help them to determine. -These three data points together indicate that most code is not sensitive to the exact string generated by `#file` and that adopting SwiftPM resources would probably address the main reason that code might be sensitive to it. However, we should not merely tell projects like these that "your tests are now broken and fixing them will require you to totally redesign how you access fixtures"—we should provide a drop-in solution for projects that are currently using `#file` in these ways. +> [1] This limitation ensures that identically-named `private` and `fileprivate` declarations in different files will have unique mangled names. A future version of the Swift compiler could lift this rule. -
    -[1] Details of regular expression-based analysis +## Detailed design -We applied several regular expressions to the source code of the projects in the Source Compatibility Suite to try to classify uses of `#file`. We identified four patterns that we believe represent display to humans: +### Specification of the `#file` string format -1. `StaticString = #file`: Since `StaticString` is fairly inconvenient to use compared to `String`, we assume these will be passed to one of the few APIs that take them. Most of those—like `precondition(_:_:file:line:)` or `XCTAssertEqual(_:_:_:file:line:)`—are APIs that should show a concise string. +The formal grammar of the string generated by `#file` is: -2. ` = #file`: A couple of projects use a custom typealias for `StaticString`. We manually confirmed that these projects generally pass those values to something like an `XCTAssert` function which should show a concise string. +```text +file-string → module-name "/" file-name +file-string → module-name "/" disambiguator "/" file-name // Reserved for future use -3. `String = #file`, but with `#line` on the same line: We take this to indicate that a file and line are being captured for display together. +module-name → [same as Swift identifier] +disambiguator → [all characters except U+0000 NULL] +file-name → [all characters except U+002F SOLIDUS and U+0000 NULL] +``` -4. `#file` interpolated into a string: Since `#file` is usually an absolute path that wouldn't need to be concatenated, we take these to be formatting for display. +`"/"` means `"/"` in this grammar, even on systems with a different path separator. -We also identified two patterns that we conservatively assume represent eventual I/O or other machine-oriented processing (although in many cases they may represent path computation for display): +Note that *disambiguator* may contain `"/"` characters. To parse *file-string* correctly, developers should split the string on `"/"` characters and use the first element for the module name, or the last element for the file name, without assuming that there are exactly two elements. Alternatively, they may use the index of the first `"/"` as the end of the module name and the index after the last `"/"` as the start of the file name. -1. `String = #file`, no `#line` nearby: We assume this will be passed to an API like `URL.init(fileURLWithPath:)` which will then be used to further manipulate the path or perform I/O. +### Computation of *file-string* components -2. `#file` used in a parenthesized list, but not an interpolation: We assume this is being passed to an API like `URL.init(fileURLWithPath:)`. +The components of *file-string* are currently computed from the module name and `#filePath` at the source location. Specifically: -When we matched these patterns against the source compatibility suite, we got the following results: +* *module-name* is the name of the module being compiled. -| Pattern | Occurrences | Percentage | -| -------- | --------------- | ------------- | -| **Human display patterns** | | | -| `StaticString = #file` | 419 | 39.0% | -| ` = #file` | 281 | 26.1% | -| `#file` and `#line` captured together | 148 | 13.8% | -| `#file` interpolated into string | 132 | 12.3% | -| All human display patterns | 980 | 91.3% | -| **Machine consumption patterns** | | | -| `String = #file`, no `#line` | 10 | 0.9% | -| `#file` passed to function | 31 | 2.9% | -| All machine consumption patterns | 41 | 3.8% | -| **None of the above patterns** | 52 | 4.8% | +* *file-name* is the substring of the `#filePath` string falling after the last path separator. "Path separator" here is either `"/"` or the host's path separator character (e.g. `"\"` on Windows). -We therefore estimate that about 6% (±3%) of uses actually want a full path so they can process it, while 94% (±3%) would be better served by a more succinct string designed for human display. +* *disambiguator* is currently always omitted. -
    +Future compilers may also use the other potential `#filePath` strings in the module as an input to this function and use them to compute a *disambiguator* field to distinguish between `#filePath`s that would otherwise produce the same *file-string*. We do not currently specify how they will do this; we simply constrain future compilers to fit this information into the *disambiguator* field. -## Proposed solution +### Interaction with `#sourceLocation` -We propose changing the string that `#file` evaluates to. To preserve implementation flexibility, we do not specify the exact format of this string; we merely specify that it must: +The `file` parameter of a `#sourceLocation` directive will specify the content of `#filePath`. Since *file-string* is calculated from the `#filePath`, this means that the last component of the `file` parameter will be used as the *file-name*. We do not provide a way to change the *module-name* or to directly specify the *file-string*. -1. Be unique to the file and module it refers to. That is, every unique `fileprivate` scope should have a different `#file` value. +In current compilers, a `#sourceLocation` directive may produce `#file` strings which collide with the `#file` strings produced by other `#sourceLocation`-introduced paths or physical files. The compiler will warn about this. Future compilers may use the *disambiguator* field to differentiate them instead. -2. Be easy for a human to read and map to the matching source file. +### Tooling -`#file` will otherwise behave as it did before, including its special behavior in default arguments. Standard library assertion functions will continue to use `#file`, and we encourage developers to use it in test helpers, logging functions, and most other places where they use `#file` today. +SourceKit will provide facilities for mapping `#file` strings back to their matching `#filePath`s. -For those rare cases where developers actually need a full path, we propose adding a `#filePath` magic identifier with the same behavior that `#file` had in previous versions of Swift. That is, it contains the path to the file as passed to the Swift compiler. +### Default argument mismatch diagnostics -## Detailed design +To help users who are wrapping a `#file` or `#filePath`-defaulting function avoid using the wrong default argument in their wrappers, the compiler will emit a warning when a parameter which captures one of the magic identifiers is passed to a default argument which captures a different one. This warning will also apply to existing magic identifiers, like `#file` vs. `#function` or `#line` vs. `#column`. -We do not specify the exact string produced by `#file` because it is not intended to be machine-parseable and we want to preserve flexibility as the compiler's capabilities change. In the current implementation of this proposal, however, a file at `/Users/brent/Desktop/0274-magic-file.swift` in a module named `MagicFile` with this content: +We do not specify the exact diagnostics in this proposal, but as an illustration, the mismatch in this code: ```swift -print(#file) -print(#filePath) -fatalError("Something bad happened!") +func fn1(file: String = #filePath) { ... } +func fn2(file: String = #file) { + fn1(file: file) +} ``` -Would produce this output: +Might be diagnosed like: ``` -0274-magic-file.swift (MagicFile) -/Users/brent/Desktop/0274-magic-file.swift -Fatal error: Something bad happened!: file 0274-magic-file.swift (MagicFile), line 3 +sample.swift:3: warning: parameter 'file' with default argument '#file' passed to parameter 'file', whose default argument is '#filePath' + fn1(file: file) + ^~~~ +sample.swift:2: note: did you mean for parameter 'file' to default to '#filePath'? +func fn2(file: String = #file) { + ^~~~~ + #filePath +sample.swift:3: note: add parentheses to silence this warning + fn1(file: file) + ^ ^ + ( ) ``` -This string is currently sufficient to uniquely identify the file's `fileprivate` scope because the Swift compiler does not allow identically-named files in different directories to be included in the same module.[2] +### Implementation status -This implementation is actually already in master and swift-5.2-branch behind a compiler flag; use the `-enable-experimental-concise-pound-file` flag to try it out. +A prototype of this feature is already in master; it can be enabled by passing `-Xfrontend -enable-experimental-concise-pound-file` to the Swift compiler. This prototype does not include some of the revisions for the second review: -> [2] This limitation ensures that identically-named `private` and `fileprivate` declarations in different files will have unique mangled names. A future version of the Swift compiler could lift this limitation. If this happens, not fully specifying the format of the `#file` string preserves flexibility for that version to adjust its `#file` strings to include additional information, such as selected parent directory names, sufficient to distinguish the two files. +* The specified `#file` string format (the prototype uses `file-name (module-name)` instead). +* The warning for colliding `#sourceLocation`s. +* Proof-of-concept for tooling support, in the form of a table printed into comments in `-emit-sil` output. + +These are implemented in the unmerged [apple/swift#29412](https://github.com/apple/swift/pull/29412). ## Source compatibility @@ -177,11 +201,29 @@ While we're looking at this area of the language, we could change `#filePath` to Ultimately, we think changing to an absolute path is severable from this proposal and that, if we want to do this, we should consider it separately. +### Provide separate `#moduleName` and `#fileName` magic identifiers + +Rather than combining the module and file names into a single string, we could provide separate ways to retrieve each of them. There are several reasons we chose not to do this: + +* `#fileName` is so ambiguous between modules that good uses for it by itself are few and far between. You really need either the module name or the path to tell which file by that name you're talking about. The full path can be so verbose that it's better to truncate to the filename and deal with the ambiguity, but module names are short enough that module name + filename doesn't really have this problem. + +* Existing clients, like `fatalError(_:file:line:)`, have only one parameter for filename information. Adding a second would break ABI, and there would not be a way to concatenate the caller's `#moduleName` and `#fileName` into a single string in a default argument. (Magic identifiers only give you the caller's location if they are the only thing in the default argument; something like `file: String = "\(#moduleName)/\(#fileName)"` would capture the callee's location instead.) + +* `#file` provides a standard format for this information so that different tools are all on the same page. + +While we don't see many practical uses for `#fileName`, we *do* think there are reasonable uses for `#moduleName`. However, this information can now be easily parsed out of the `#file` string. If we want to also provide a separate `#moduleName`, we can consider that in a separate proposal. + ### Other alternatives +We considered leaving the *file-string* unspecified rather than specifying it with an unused *disambiguator* field for future expansion. Feedback from the first review convinced us that clients will want to interpret this string, which requires a specified format. + +We considered renaming the `file` parameter of `#sourceLocation` to `filePath`. This would make its meaning clearer, but it seems like unnecessary churn, and `#sourceLocation` is begging for a redesign to address its other shortcomings anyway. + +We considered adding a new magic identifier like `#context` which represents several pieces of contextual information simultaneously. This would not give us most of the privacy and code size improvements we seek because, when passed across optimization barriers like module boundaries, we would have to conservatively generate all of the information the callee might want to retrieve from `#context`. + We considered making `#file`'s behavior change conditional on enabling a new language version mode. While we're not opposed to that in principle, we don't think the breakage from this change will be severe enough to justify delaying this proposal's implementation until the next release with a language version mode. -We considered introducing a new alternative to `#file` (e.g. `#fileName`) while preserving the existing meaning of `#file`. However, a great deal of code already uses `#file` and would in practice probably never be converted to `#fileName`. The vast majority of this code would benefit from the new behavior, so we think it would be better to automatically adopt it. (Note that clang supports a `__FILE_NAME__` alternative, but most code still uses `__FILE__` anyway.) +We considered introducing a new alternative to `#file` (e.g. `#fileName`) while preserving the existing meaning of `#file`. However, a great deal of code already uses `#file` and would in practice probably never be converted to `#fileName`. Most of this code would benefit from the new behavior, so we think it would be better to automatically adopt it. (Note that clang supports a `__FILE_NAME__` alternative, but most code still uses `__FILE__` anyway.) We considered switching between the old and new `#file` behavior with a compiler flag. However, this creates a language dialect, and compiler flags are not a natural interface for users. From c2e42eec430083e000244322b3f62d1fbe256e0f Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 28 Jan 2020 20:25:31 +0000 Subject: [PATCH 1385/4563] [SE-0275] Update status: **Rejected** --- ...itespaces-and-punctuations-for-escaped-identifiers.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md index 518f8f005f..0129be339a 100644 --- a/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md +++ b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md @@ -1,14 +1,11 @@ # Allow more characters (like whitespaces and punctuations) for escaped identifiers * Proposal: [SE-0275](0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md) - -* Authors: [Alfredo Delli Bovi](https://github.com/adellibovi) - +* Author: [Alfredo Delli Bovi](https://github.com/adellibovi) * Review Manager: [Joe Groff](https://github.com/jckarter) - -* Status: **Active Review (January 13...January 20, 2020)** - +* Status: **Rejected** * Implementation: [apple/swift#28966](https://github.com/apple/swift/pull/28966) +* Decision Notes: [Rationale](https://forums.swift.org/t/se-0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers/32538/46) ## Introduction Swift has a beautiful concise yet expressive syntax. From 4aa0adf6a87a214123bd0e45b6f8d014af33d0ff Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 29 Jan 2020 09:34:05 -0800 Subject: [PATCH 1386/4563] Accept SE-0276 "Multi-Pattern Catch Clauses" --- proposals/0276-multi-pattern-catch-clauses.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0276-multi-pattern-catch-clauses.md b/proposals/0276-multi-pattern-catch-clauses.md index 77039e4fc4..7c2c02f174 100644 --- a/proposals/0276-multi-pattern-catch-clauses.md +++ b/proposals/0276-multi-pattern-catch-clauses.md @@ -3,8 +3,9 @@ * Proposal: [SE-0276](0276-multi-pattern-catch-clauses.md) * Author: [Owen Voorhees](https://github.com/owenv) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review (January 15...January 22, 2020)** +* Status: **Accepted** * Implementation: [apple/swift#27776](https://github.com/apple/swift/pull/27776) +* Decision Notes: [Review](https://forums.swift.org/t/accepted-se-0276-multi-pattern-catch-clauses/33220) ## Introduction From d487f336673c8616b3d689e3c4e5fe9b1f7766b6 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 30 Jan 2020 14:11:18 -0800 Subject: [PATCH 1387/4563] Accept SE-0270 --- proposals/0270-rangeset-and-collection-operations.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index e60efeb67d..1be2c837db 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -3,11 +3,11 @@ * Proposal: [SE-0270](0270-rangeset-and-collection-operations.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (January 21st...28th, 2020)** +* Status: **Accepted** * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/9b5957c00e7483ab8664afe921f989ed1394a666/proposals/0270-rangeset-and-collection-operations.md), [2](https://github.com/apple/swift-evolution/blob/b17d85fcaf38598fd2ea19641d0e9c26c96747ec/proposals/0270-rangeset-and-collection-operations.md) * Reviews: [1](https://forums.swift.org/t/se-0270-add-collection-operations-on-noncontiguous-elements/30691), [2](https://forums.swift.org/t/se-0270-review-2-add-collection-operations-on-noncontiguous-elements/31653), [3](https://forums.swift.org/t/se-0270-review-3-add-collection-operations-on-noncontiguous-elements/32839) -* Decision Notes: [1](https://forums.swift.org/t/returned-for-revision-se-0270-add-collection-operations-on-noncontiguous-elements/31484), [2](https://forums.swift.org/t/revised-se-0270-add-collection-operations-on-noncontiguous-elements/32840) +* Decision Notes: [1](https://forums.swift.org/t/returned-for-revision-se-0270-add-collection-operations-on-noncontiguous-elements/31484), [2](https://forums.swift.org/t/revised-se-0270-add-collection-operations-on-noncontiguous-elements/32840), [3](https://forums.swift.org/t/accepted-se-0270-add-collection-operations-on-noncontiguous-elements/33270) ## Introduction From d1b36802371800db4d3189c4477e06674d6ce6d9 Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Sun, 2 Feb 2020 20:48:08 +0000 Subject: [PATCH 1388/4563] [Proposal] Add the 'Enum cases as protocol witnesses' proposal --- .../0xxx-enum-cases-as-protocol-witnesses.md | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 proposals/0xxx-enum-cases-as-protocol-witnesses.md diff --git a/proposals/0xxx-enum-cases-as-protocol-witnesses.md b/proposals/0xxx-enum-cases-as-protocol-witnesses.md new file mode 100644 index 0000000000..814db17aba --- /dev/null +++ b/proposals/0xxx-enum-cases-as-protocol-witnesses.md @@ -0,0 +1,211 @@ +# Enum cases as protocol witnesses + +* Proposal: [SE-XXXX](xxxx-enum-cases-as-protocol-witnesses.md) +* Author: [Suyash Srijan](https://www.github.com/theblixguy) +* Review Manager: **TBD** +* Status: **Awaiting Review** +* Implementation: [apple/swift#28916](https://github.com/apple/swift/pull/28916) +* Bug: [SR-3170](https://bugs.swift.org/browse/SR-3170) +* Toolchain: [macOS](https://ci.swift.org/job/swift-PR-toolchain-osx/477/artifact/branch-master/swift-PR-28916-477-osx.tar.gz) & [Linux](https://ci.swift.org/job/swift-PR-toolchain-Linux/346/artifact/branch-master/swift-PR-28916-346-ubuntu16.04.tar.gz) + +## Introduction + +The aim of this proposal is to lift an existing restriction, which is that enum cases cannot participate in protocol witness matching. + +Swift-evolution thread: [Enum cases as protocol witnesses](https://forums.swift.org/t/enum-cases-as-protocol-witnesses/) + +## Motivation + +Currently, Swift has a very restrictive protocol witness matching model where a protocol witness has to match _exactly_ with the requirement, with some exceptions (see [Protocol Witness Matching Manifesto](https://forums.swift.org/t/protocol-witness-matching-mini-manifesto/32752)). + +For example, if one writes a protocol with static requirements: + +```swift +protocol DecodingError { + static var fileCorrupted: Self { get } + static func keyNotFound(_ key: String) -> Self +} +``` + +and attempts to conform an enum to it, then writing a case with the same name (and arguments) is not considered a match: + +```swift +enum JSONDecodingError: DecodingError { + case fileCorrupted // error, because it is not a match + case keyNotFound(_ key: String) // error, because it is not a match +} +``` + +This is quite surprising, because even though cases are not _written_ as a `static var` or `static func`, they do _behave_ like one both syntactically and semantically throughout the language. For example: + +```swift +enum Foo { + case bar(_ value: Int) + case baz +} + +let f = Foo.bar // `f` is a function of type (Int) -> Foo +let bar = f(2) // Returns Foo +let baz = Foo.baz // Returns Foo +``` + +is the same as: + +```swift +struct Foo { + static func bar(_ value: Int) -> Self { ... } + static var baz: Self { ... } +} + +let f = Foo.bar // `f` is a function of type (Int) -> Foo +let bar = f(2) // Returns Foo +let baz = Foo.baz // Returns Foo +``` + +Such "spelling" exceptions exist when matching other kinds of requirements as well, for example: + +```swift +protocol Foo { + var somePropertyA: Self { get } +} + +struct ImplementsFoo: Foo { + // This can be a 'let' because even though the + // keywords don't match, and a variable and a + // constant are two different things, the + // *semantics* of 'var ... { get }' and 'let' + // do match. + let somePropertyA: Self + // and you can write it as a 'var' if you want + // and still keep the semantics the same. + var somePropertyA: Self +} +``` + +Now, because enum cases are not considered as a "witness" for static protocol requirements, one has to provide a manual implementation instead: + +```swift +enum JSONDecodingError: DecodingError { + case _fileCorrupted + case _keyNotFound(_ key: String) + static var fileCorrupted: Self { return ._fileCorrupted } + static func keyNotFound(_ key: String) -> Self { + return ._keyNotFound(key) + } +} +``` + +This leads to some rather unfortunate consequences: + +1. The ability to write a case with the same name as the requirement is lost. Now, you can rename the case to something different, but it might not always be ideal, especially because naming things right is a really hard problem. In most cases, you expect the case to be named the same as the requirement. +2. The namespace of the enum is now polluted with both cases and requirements (for example, in the snippet above we have `_fileCorrupted` and `fileCorrupted`), which can be confusing during code completion. +3. There's extra code that now has to be maintained and which arguably should not exist in the first place. + +In almost every corner of the language, enum cases and static properties/functions are indistinguishable from each other, *except* when it comes to matching protocol requirements, which is very inconsistent, so it is not unreasonable to think of a enum case without associated values as a `static`, get-only property that returns `Self` or an enum case with associated values as a `static` function (with arguments) that returns `Self`. + +Doing so can also lead to other improvements, for example, one can conform `DispatchTimeInterval` direcly to Combine's `SchedulerTypeIntervalConvertible` instead of having to go through a much more complicated type like `DispatchQueue.SchedulerTimeType.Stride`: + +```swift +extension DispatchTimeInterval: SchedulerTimeIntervalConvertible { + public static func seconds(_ s: Double) -> Self { + return DispatchTimeInterval.seconds(Int((s * 1000000000.0).rounded())) + } + // Remaining requirements already satisfied by cases +} +``` + +## Proposed Solution + +The current restriction is lifted and the compiler allows a static protocol requirement to be witnessed by an enum case, under the following rules: + +1. A static, get-only protocol requirement having an enum type or `Self` type can be witnessed by an enum case with no associated values. +2. A static function requirement with arguments and returning an enum type or `Self` type can be witnessed by an enum case with associated values having the same argument list as the function's. + +This means the example from the motivation section will successfully compile: + +```swift +enum JSONDecodingError: DecodingError { + case fileCorrupted // okay + case keyNotFound(_ key: String) // okay +} +``` + +This also means the mental model of an enum case will now be _more_ consistent with static properties/methods and an inconsistency in the language will be removed. + +You will still be able to implement the requirement manually if you want and code that currently compiles today (with the manual implementation) will continue to compile. However, you will now have the option to let the case satisfy the requirement directly. + +Here are a few more examples that demonstrate how cases will be matched with the requirements: + +```swift +protocol Foo { + static var zero: FooEnum { get } + static var one: Self { get } + static func two(arg: Int) -> FooEnum + static func three(_ arg: Int) -> Self + static func four(_ arg: String) -> Self + static var five: Self { get } + static func six(_: Int) -> Self + static func seven(_ arg: Int) -> Self + static func eight() -> Self +} + +enum FooEnum: Foo { + case zero // okay + case one // okay + case two(arg: Int) // okay + case three(_ arg: Int) // okay + case four(arg: String) // not a match + case five(arg: Int) // not a match + case six(Int) // okay + case seven(Int) // okay + case eight // not a match +} +``` + +The last one is intentional - there is no way to declare a `case eight()` today (and even when you could in the past, it actually had a different type). In this case, the requirement `static func eight()` can infact be better expressed as a `static var eight`. In the future, this limitation may be lifted when other kinds of witness matching is considered. + +## Source compatibility + +This does not break source compatibility since it's a strictly additive change and allows code that previously did not compile to now compile and run successfully. + +## Effect on ABI stability + +This does not affect the ABI and does not require new runtime support. + +## Effect on API resilience + +Library authors can freely switch between having the enum case satisfy the requirement or implementing the requirement directly (and returning a different case). However, while the return _type_ will stay the same, the return _value_ will be different in both cases and so it can break code that is relying on the exact returned value. + +For example: + +```swift +protocol Foo { + static var bar: Self { get } +} + +enum FooEnum: Foo { // #1 + case bar +} + +enum FooEnum: Foo { // #2 + case _bar + static var bar: Self { return ._bar } +} + +func takesFoo(value: T) { ... } +let direct = FooEnum.bar // #1 +let asProperty = FooEnum.bar // #2 + +takesFoo(value: direct) // This one recieves .bar +takesFoo(value: asProperty) // This one recieves ._bar +``` + +## Alternatives considered + +- Allow protocol requirements to be declared as `case` instead of `static var` or `static func` - the obvious downside of doing this would be that only enums would be able to adopt such a protocol, which would be unreasonably restrictive beacuse other types like classes and structs having satisfying witnesses would no longer be able to adopt such a protocol. +- Only allow enum cases without associated values to participate out of the box. Ones with associated values will be disallowed unless explicitly marked with a specific annotation to allow them to be used as "factories". It seems unnecessarily restrictive to impose another syntactic barrier, one which does not exist in other types. The semantics of a protocol requirement is up to the author to document and for clients to read and verify before implementing, so adding another annotation does not provide any language improvements. +- Leave the existing behaviour as-is. + +## Future directions + +We can allow for more kinds of witness matching, as described in the [Protocol Witness Matching Manifesto](https://forums.swift.org/t/protocol-witness-matching-mini-manifesto/32752), such as subtyping and default arguments. \ No newline at end of file From f4ed31d7ade9a4c281058d1061da7b25c51124d3 Mon Sep 17 00:00:00 2001 From: Suyash Srijan Date: Mon, 3 Feb 2020 09:06:17 +0000 Subject: [PATCH 1389/4563] [Proposal] Apply review feedback (and one wording tweak of motivation section) --- proposals/0xxx-enum-cases-as-protocol-witnesses.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/0xxx-enum-cases-as-protocol-witnesses.md b/proposals/0xxx-enum-cases-as-protocol-witnesses.md index 814db17aba..0fd2667d25 100644 --- a/proposals/0xxx-enum-cases-as-protocol-witnesses.md +++ b/proposals/0xxx-enum-cases-as-protocol-witnesses.md @@ -1,7 +1,7 @@ # Enum cases as protocol witnesses * Proposal: [SE-XXXX](xxxx-enum-cases-as-protocol-witnesses.md) -* Author: [Suyash Srijan](https://www.github.com/theblixguy) +* Author: [Suyash Srijan](https://github.com/theblixguy) * Review Manager: **TBD** * Status: **Awaiting Review** * Implementation: [apple/swift#28916](https://github.com/apple/swift/pull/28916) @@ -12,7 +12,7 @@ The aim of this proposal is to lift an existing restriction, which is that enum cases cannot participate in protocol witness matching. -Swift-evolution thread: [Enum cases as protocol witnesses](https://forums.swift.org/t/enum-cases-as-protocol-witnesses/) +Swift-evolution thread: [Enum cases as protocol witnesses](https://forums.swift.org/t/enum-cases-as-protocol-witnesses/32753) ## Motivation @@ -103,7 +103,7 @@ This leads to some rather unfortunate consequences: In almost every corner of the language, enum cases and static properties/functions are indistinguishable from each other, *except* when it comes to matching protocol requirements, which is very inconsistent, so it is not unreasonable to think of a enum case without associated values as a `static`, get-only property that returns `Self` or an enum case with associated values as a `static` function (with arguments) that returns `Self`. -Doing so can also lead to other improvements, for example, one can conform `DispatchTimeInterval` direcly to Combine's `SchedulerTypeIntervalConvertible` instead of having to go through a much more complicated type like `DispatchQueue.SchedulerTimeType.Stride`: +Lifting this restriction can also lead to other improvements, for example, one can conform `DispatchTimeInterval` directly to Combine's `SchedulerTypeIntervalConvertible` instead of having to go through a much more complicated type like `DispatchQueue.SchedulerTimeType.Stride`: ```swift extension DispatchTimeInterval: SchedulerTimeIntervalConvertible { @@ -162,7 +162,7 @@ enum FooEnum: Foo { } ``` -The last one is intentional - there is no way to declare a `case eight()` today (and even when you could in the past, it actually had a different type). In this case, the requirement `static func eight()` can infact be better expressed as a `static var eight`. In the future, this limitation may be lifted when other kinds of witness matching is considered. +The last one is intentional - there is no way to declare a `case eight()` today (and even when you could in the past, it actually had a different type). In this case, the requirement `static func eight()` can in fact be better expressed as a `static var eight`. In the future, this limitation may be lifted when other kinds of witness matching is considered. ## Source compatibility @@ -196,13 +196,13 @@ func takesFoo(value: T) { ... } let direct = FooEnum.bar // #1 let asProperty = FooEnum.bar // #2 -takesFoo(value: direct) // This one recieves .bar -takesFoo(value: asProperty) // This one recieves ._bar +takesFoo(value: direct) // This one receives .bar +takesFoo(value: asProperty) // This one receives ._bar ``` ## Alternatives considered -- Allow protocol requirements to be declared as `case` instead of `static var` or `static func` - the obvious downside of doing this would be that only enums would be able to adopt such a protocol, which would be unreasonably restrictive beacuse other types like classes and structs having satisfying witnesses would no longer be able to adopt such a protocol. +- Allow protocol requirements to be declared as `case` instead of `static var` or `static func` - the obvious downside of doing this would be that only enums would be able to adopt such a protocol, which would be unreasonably restrictive because other types like classes and structs having satisfying witnesses would no longer be able to adopt such a protocol. - Only allow enum cases without associated values to participate out of the box. Ones with associated values will be disallowed unless explicitly marked with a specific annotation to allow them to be used as "factories". It seems unnecessarily restrictive to impose another syntactic barrier, one which does not exist in other types. The semantics of a protocol requirement is up to the author to document and for clients to read and verify before implementing, so adding another annotation does not provide any language improvements. - Leave the existing behaviour as-is. From 01ab53b33640a011be1e675a1e7d4eb92b0e4fd8 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Mon, 3 Feb 2020 23:13:35 -0800 Subject: [PATCH 1390/4563] Mark SE-0264 as accepted --- proposals/0264-stdlib-preview-package.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index 7c09bae19b..f0b1b9bb88 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,7 +3,8 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Active review (December 2...December 10)** +* Status: **Accepted** +* Decision Notes: [Rationale](https://forums.swift.org/t/se-0264-review-2-standard-library-preview-package/31288/16) * Implementation: 1. [apple/swift-evolution#1089](https://github.com/apple/swift-evolution/pull/1089) 2. [apple/swift-evolution#1090](https://github.com/apple/swift-evolution/pull/1090) From b874d4b5e3748ef575921944479783f30e93c7be Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Fri, 7 Feb 2020 01:34:42 +0100 Subject: [PATCH 1391/4563] Float16 (#1120) * Float16 proposal * Kick off Float16 review --- proposals/0000-float16.md | 125 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 proposals/0000-float16.md diff --git a/proposals/0000-float16.md b/proposals/0000-float16.md new file mode 100644 index 0000000000..2658c78b83 --- /dev/null +++ b/proposals/0000-float16.md @@ -0,0 +1,125 @@ +# Float16 + +* Proposal: [SE-0276](0276-float16.md) +* Author: [Stephen Canon](https://github.com/stephentyrone) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Pitch thread: [Add Float16](https://forums.swift.org/t/add-float16/33019) +* Implementation: [Implement Float16](https://github.com/apple/swift/pull/21738) +* Status: **Active review (6–17 February 2020)** + +## Introduction + +Introduce the `Float16` type conforming to the `BinaryFloatingPoint` and `SIMDScalar` +protocols, binding the IEEE 754 *binary16* format (aka *float16*, *half-precision*, or *half*), +and bridged by the compiler to the C `_Float16` type. + +* Old pitch thread: [Add `Float16`](https://forums.swift.org/t/add-float16/19370). + +## Motivation + +The last decade has seen a dramatic increase in the use of floating-point types smaller +than (32-bit) `Float`. The most widely implemented is `Float16`, which is used +extensively on mobile GPUs for computation, as a pixel format for HDR images, and as +a compressed format for weights in ML applications. + +Introducing the type to Swift is especially important for interoperability with shader-language +programs; users frequently need to set up data structures on the CPU to +pass to their GPU programs. Without the type available in Swift, they are forced to use +unsafe mechanisms to create these structures. + +In addition, C APIs that use these types simply cannot be imported, making them +unavailable in Swift. + +## Proposed solution + +Add `Float16` to the standard library. + +## Detailed design + +There is shockingly little to say here. We will add: +``` +@frozen +struct Float16: BinaryFloatingPoint, SIMDScalar, CustomStringConvertible { } +``` +The entire API falls out from that, with no additional surface outside that provided by those +protocols. `Float16` will provide exactly the operations that `Float` and `Double` and `Float80` +do for their conformance to these protocols. + +We also need to ensure that the parameter passing conventions followed by the compiler +for `Float16` are what we want; these values should be passed and returned in the +floating-point registers, and vectors should be passed and returned in SIMD registers. + +On platforms that do not have native arithmetic support, we will convert `Float16` to +`Float` and use the hardware support for `Float` to perform the operation. This is +correctly-rounded for every operation except fused multiply-add. A software sequence +will be used to emulate fused multiply-add in these cases (the easiest option is to convert +to `Double`, but other options may be more efficient on some architectures, especially +for vectors). + +## Source compatibility + +N/A + +## Effect on ABI stability + +There is no change to existing ABI. We will be introducing a new type, which will have +appropriate availability annotations. + +## Effect on API resilience + +The `Float16` type would become part of the public API. It will be `@frozen`, so no +further changes will be possible, but its API and layout are entirely constrained by +IEEE 754 and conformance to `BinaryFloatingPoint`, so there are no alternatives +possible anyway. + +## Alternatives considered + +Q: Why isn't it called `Half`? + +A: `FloatN` is the more consistent pattern. Swift already has `Float32`, +`Float64` and `Float80` (with `Float` and `Double` as alternative spellings of `Float32` +and `Float64`, respectively). At some future point we will add `Float128`. Plus, the C +language type that this will bridge to is named `_Float16`. + +`Half` is not completely outrageous as an alias, but we shouldn't add aliases unless +there's a really compelling reason. + +During the pitch phase, feedback was fairly overwhelmingly in favor of `Float16`, though +there are a few people who would like to have both names available. Unless significantly +more people come forward, however, we should make the "opinionated" choice to have a single +name for the type. An alias could always be added with a subsequent minor proposal if +necessary. + +Q: What about ARM's ["alternative half precision"](https://en.wikipedia.org/wiki/Half-precision_floating-point_format#ARM_alternative_half-precision)? +What about [bfloat16](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format)? + +A: Alternative half-precision is no longer supported; ARMv8.2-FP16 only uses the IEEE 754 +encoding. Bfloat is something that we should *also* support eventually, but it's a separate +proposal. and we should wait a little while before doing it. Conformance to IEEE 754 fully +defines the semantics of `Float16` (and several hardware vendors, including Apple have +been shipping processors that implement those semantics for a few years). By contrast, +there are a few proposals for hardware that implements bfloat16; ARM and Intel designed +different multiply-add units for it, and haven't shipped yet (and haven't defined any +operations *other* than a non-homogeneous multiply-add). Other companies have HW in +use, but haven't (publicly) formally specified their arithmetic. It's a moving target, and it +would be a mistake to attempt to specify language bindings today. + +Q: Do we need conformance to `BinaryFloatingPoint`? What if we made it a storage-only format? + +A: We could make it a type that can only be used to convert to/from `Float` and `Double`, +forcing all arithmetic to be performed in another format. However, this would mean that +it would be much harder, in some cases, to get the same numerical result on the CPU and +GPU (when GPU computation is performed in half-precision). It's a very nice convenience +to be able to do a computation in the REPL and see what a GPU is going to do. + +Q: Why not put it in Swift Numerics? + +A: The biggest reason to add the type is for interoperability with C-family and GPU programs +that want to use their analogous types. In order to maximize the support for that interoperability, +and to get the calling conventions that we want to have in the long-run, it makes more sense to put +this type in the standard library. + +Q: What about math library support? + +A: If this proposal is approved, I will add conformance to `Real` in Swift Numerics, providing +the math library functions (by using the corresponding `Float` implementations initially). From 5c32466a7bd2a1a406ba683609d99bff9fe539f1 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Thu, 6 Feb 2020 16:35:14 -0800 Subject: [PATCH 1392/4563] Rename 0000-float16.md to 0276-float16.md --- proposals/{0000-float16.md => 0276-float16.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{0000-float16.md => 0276-float16.md} (100%) diff --git a/proposals/0000-float16.md b/proposals/0276-float16.md similarity index 100% rename from proposals/0000-float16.md rename to proposals/0276-float16.md From 3a44151a0a9f818e2262dff56a357a468efa6bae Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Thu, 6 Feb 2020 16:36:05 -0800 Subject: [PATCH 1393/4563] Renumber Float16 to 0277, oops... --- proposals/{0276-float16.md => 0277-float16.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{0276-float16.md => 0277-float16.md} (99%) diff --git a/proposals/0276-float16.md b/proposals/0277-float16.md similarity index 99% rename from proposals/0276-float16.md rename to proposals/0277-float16.md index 2658c78b83..e53f19dfba 100644 --- a/proposals/0276-float16.md +++ b/proposals/0277-float16.md @@ -1,6 +1,6 @@ # Float16 -* Proposal: [SE-0276](0276-float16.md) +* Proposal: [SE-0277](0277-float16.md) * Author: [Stephen Canon](https://github.com/stephentyrone) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Pitch thread: [Add Float16](https://forums.swift.org/t/add-float16/33019) From cd277a5cf1a1cd4a0e31264d2db0bb5d1b563cd5 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Fri, 7 Feb 2020 21:02:43 -0800 Subject: [PATCH 1394/4563] Record SE-110 as implemented in its current state (#1121) Update the proposal text to describe the special case for function arguments. --- proposals/0110-distingish-single-tuple-arg.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/proposals/0110-distingish-single-tuple-arg.md b/proposals/0110-distingish-single-tuple-arg.md index cd006d24ec..43bbb8a666 100644 --- a/proposals/0110-distingish-single-tuple-arg.md +++ b/proposals/0110-distingish-single-tuple-arg.md @@ -3,9 +3,10 @@ * Proposal: [SE-0110](0110-distingish-single-tuple-arg.md) * Authors: Vladimir S., [Austin Zheng](https://github.com/austinzheng) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Accepted** +* Status: **Implemented** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-July/000215.html), [Additional Commentary](https://lists.swift.org/pipermail/swift-evolution-announce/2017-June/000386.html) * Bug: [SR-2008](https://bugs.swift.org/browse/SR-2008) +* Previous Revision: [Originally Accepted Proposal](https://github.com/apple/swift-evolution/blob/9e44932452e1daead98f2bc2e58711eb489e9751/proposals/0110-distingish-single-tuple-arg.md) ## Introduction @@ -47,6 +48,15 @@ We propose that this behavior should be fixed in the following ways: We understand that this may be a departure from the current convention that a set of parentheses enclosing a single object are considered semantically meaningless, but it is the most natural way to differentiate between the two situations described above and would be a clearly-delineated one-time-only exception. +Existing Swift code widely takes advantage of the ability to pass a multi-parameter closure or function value to a higher-order function that operates on tuples, particularly with collection operations: + +``` +zip([1, 2, 3], [3, 2, 1]).filter(<) // => [(1, 3)] +zip([1, 2, 3], [3, 2, 1]).map(+) // => [4, 4, 4] +``` + +Without the implicit conversion, this requires invasive changes to explicitly destructure the tuple argument. In order to gain most of the type system benefits of distinguishing single-tuple-argument functions from multiple-argument functions, while maintaining the fluidity of functional code like the above, arguments of type `(T, U, ...) -> V` in call expressions are allowed to be converted to parameters of the corresponding single-tuple parameter type `((T, U, ...)) -> V`, so the two examples above will continue to be accepted. + ## Impact on existing code Minor changes to user code may be required if this proposal is accepted. @@ -54,3 +64,7 @@ Minor changes to user code may be required if this proposal is accepted. ## Alternatives considered Don't make this change. + +## Revision history + +The [original proposal as reviewed](https://github.com/apple/swift-evolution/blob/9e44932452e1daead98f2bc2e58711eb489e9751/proposals/0110-distingish-single-tuple-arg.md) did not include the special-case conversion from `(T, U, ...) -> V` to `((T, U, ...)) -> V` for function arguments. In response to community feedback, [this conversion was added](https://lists.swift.org/pipermail/swift-evolution-announce/2017-June/000386.html) as part of the Core Team's acceptance of the proposal. From 864def63ea152685322de2daf02d4c343f756a21 Mon Sep 17 00:00:00 2001 From: David Hart Date: Thu, 13 Feb 2020 16:27:35 +0100 Subject: [PATCH 1395/4563] Update SE-0226 to implemented in Swift 5.2 --- proposals/0226-package-manager-target-based-dep-resolution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0226-package-manager-target-based-dep-resolution.md b/proposals/0226-package-manager-target-based-dep-resolution.md index a95cbd060d..4deb9a0fbb 100644 --- a/proposals/0226-package-manager-target-based-dep-resolution.md +++ b/proposals/0226-package-manager-target-based-dep-resolution.md @@ -3,7 +3,7 @@ * Proposal: [SE-0226](0226-package-manager-target-based-dep-resolution.md) * Authors: [Ankit Aggarwal](https://github.com/aciidb0mb3r) * Review Manager: [Boris Bügling](https://github.com/neonichu) -* Status: **Accepted** +* Status: **Partially implemented (Swift 5.2):** Implemented the manifest API to disregard targets not concerned by any dependency products, which avoids building dependency test targets. * Bug: [SR-8658](https://bugs.swift.org/browse/SR-8658) ## Introduction From f2de7d4ead5c0b4c1a313c44a07ff5b344042bbf Mon Sep 17 00:00:00 2001 From: Owen Voorhees Date: Sun, 16 Feb 2020 14:55:14 -0800 Subject: [PATCH 1396/4563] Add proposal: Allow Multiple Variadic Parameters in Functions, Subscripts, and Initializers --- .../NNNN-multiple-variadic-parameters.md | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 proposals/NNNN-multiple-variadic-parameters.md diff --git a/proposals/NNNN-multiple-variadic-parameters.md b/proposals/NNNN-multiple-variadic-parameters.md new file mode 100644 index 0000000000..04dc218fa9 --- /dev/null +++ b/proposals/NNNN-multiple-variadic-parameters.md @@ -0,0 +1,165 @@ +# Allow Multiple Variadic Parameters in Functions, Subscripts, and Initializers + +* Proposal: [SE-NNNN](NNNN-multiple-variadic-parameters.md) +* Author: [Owen Voorhees](https://github.com/owenv) +* Review Manager: +* Status: +* Implementation: [apple/swift#29735](https://github.com/apple/swift/pull/29735) + +## Introduction + +Currently, variadic parameters in Swift are subject to two main restrictions: + +- Only one variadic parameter is allowed per parameter list +- If present, the parameter which follows a variadic parameter must be labeled + +This proposal seeks to remove the first restriction while leaving the second in place, allowing a function, subscript, or initializer to have multiple variadic parameters so long as every parameter which follows a variadic one has a label. + +Swift-evolution thread: [Lifting the 1 variadic param per function restriction](https://forums.swift.org/t/lifting-the-1-variadic-param-per-function-restriction/33787?u=owenv) + +## Motivation + +Variadic parameters allow programmers to write clear, succinct APIs which operate on a variable, but compile-time fixed number of inputs. One prominent example is the standard library's `print` function. However, restricting each function to a single variadic parameter can sometimes be limiting. For example, consider the following example from the `swift-driver` project: + +```swift +func assertArgs( + _ args: String..., + parseTo driverKind: DriverKind, + leaving remainingArgs: ArraySlice, + file: StaticString = #file, line: UInt = #line + ) throws { /* Implementation Omitted */ } + +try assertArgs("swift", "-foo", "-bar", parseTo: .interactive, leaving: ["-foo", "-bar"]) +``` + +Currently, the `leaving:` parameter cannot be variadic because of the preceding unnamed variadic parameter. This results in an odd inconsistency, where the first list of arguments does not require brackets, but the second does. By allowing multiple variadic parameters, it could be rewritten like so: + +```swift +func assertArgs( + _ args: String..., + parseTo driverKind: DriverKind, + leaving remainingArgs: String..., + file: StaticString = #file, line: UInt = #line + ) throws { /* Implementation Omitted */ } + +try assertArgs("swift", "-foo", "-bar", parseTo: .interactive, leaving: "-foo", "-bar") +``` + +This results in a cleaner, more consistent interface. + +Multiple variadic parameters can also be used to streamline lightweight DSL-like functions. For example, one could write a simple autolayout wrapper like the following: + +```swift +extension UIView { + func addSubviews(_ views: UIView..., constraints: NSLayoutConstraint...) { + views.forEach { + addSubview($0) + $0.translatesAutoresizingMaskIntoConstraints = false + } + constraints.forEach { $0.isActive = true } + } +} + +myView.addSubviews(v1, v2, constraints: v1.widthAnchor.constraint(equalTo: v2.widthAnchor), + v1.heightAnchor.constraint(equalToConstant: 40), + /* More Constraints... */) +``` + +## Proposed solution + +Lift the arbitrary restriction on variadic parameter count and allow a function/subscript/initializer to have any number of them. Leave in place the restriction which requires any parameter following a variadic one to have a label. + +## Detailed design + +A variadic parameter can already appear anywhere in a parameter list, so the behavior of multiple variadic parameters in functions and intializers is fully specified by the existing language rules. + +```swift +// Note the label on the second parameter is required because it follows a variadic parameter. +func twoVarargs(_ a: Int..., b: Int...) { } +twoVarargs(1, 2, 3, b: 4, 5, 6) + +// Variadic parameters can be omitted because they default to []. +twoVarargs(1, 2, 3) +twoVarargs(b: 4, 5, 6) +twoVarargs() + +// The third parameter does not require a label because the second isn't variadic. +func splitVarargs(a: Int..., b: Int, _ c: Int...) { } +splitVarargs(a: 1, 2, 3, b: 4, 5, 6, 7) +// a is [1, 2, 3], b is 4, c is [5, 6, 7]. +splitVarargs(b: 4) +// a is [], b is 4, c is []. + +// Note the third parameter doesn't need a label even though the second has a default expression. This +// is consistent with the current behavior, which allows a variadic parameter followed by a labeled, +// defaulted parameter, followed by an unlabeled required parameter. +func varargsSplitByDefaultedParam(_ a: Int..., b: Int = 42, _ c: Int...) { } +varargsSplitByDefaultedParam(1, 2, 3, b: 4, 5, 6, 7) +// a is [1, 2, 3], b is 4, c is [5, 6, 7]. +varargsSplitByDefaultedParam(b: 4, 5, 6, 7) +// a is [], b is 4, c is [5, 6, 7]. +varargsSplitByDefaultedParam(1, 2, 3) +// a is [1, 2, 3], b is 42, c is []. +// Note: it is impossible to call varargsSplitByDefaultedParam providing a value for the third parameter +// without also providing a value for the second. +``` + +This proposal also allows subscripts to have more than one variadic parameter. Like in functions and initializers, a subscript parameter which follows a variadic parameter must have an external label. However, the syntax differs slightly because of the existing labeling rules for subscript parameters: + +```swift +struct HasSubscript { + // Not allowed because the second parameter does not have an external label. + subscript(a: Int..., b: Int...) -> [Int] { a + b } + + // Allowed + subscript(a: Int..., b b: Int...) -> [Int] { a + b } +} +``` + +Note that due to a long-standing bug, the following subscript declarations are accepted by the current compiler: + +```swift +struct HasBadSubscripts { + // Shouldn't be allowed because the second parameter follows a variadic one and has no + // label. Is accepted by the current compiler but can't be called. + subscript(a: Int..., b: String) -> Int { 0 } + + // Shouldn't be allowed because the second parameter follows a variadic one and has no + // label. Is accepted by the current compiler and can be called, but the second + // parameter cannot be manually specified. + subscript(a: Int..., b: String = "hello, world!") -> Bool { false } +} +``` + +This proposal makes both declarations a compile time error. This is a source compatibility break, but a very small one which only affects declarations with no practical use. This bug also affects closure parameter lists: + +```swift +// Currently allowed, but impossible to call. +let closure = {(a: Int..., b: Int) in} +``` + +Under this proposal, the above code also becomes a compile-time error. Note that because closures do not allow external parameter labels, they cannot support multiple variadic parameters. + +## Source compatibility + +As noted above, this proposal is source-breaking for any program which has a subscript declaration or closure having an unlabeled parameter following a variadic parameter. With the exception of very specific subscript declarations making use of default parameters, this only affects parameter lists which are syntactically impossible to fulfill. As a result, the break should have no impact on the vast majority of existing codebases. It does not cause any failures in the source compatibility suite. + +If this source-breaking change is considered unacceptable, there are two alternatives. One would be to make the error a warning instead for subscripts and closures. The other would be to preserve the buggy behavior and emit no diagnostics. In both cases, multiple variadic parameters would continue to be supported by subscripts, but users would retain the ability to write parameter lists which can't be fulfilled in some contexts. + +## Effect on ABI stability + +This proposal does not require any changes to the ABI. The current ABI representation of variadic parameters already supports more than one per function/subscript/intializer. + +## Effect on API resilience + +An ABI-public function may not add, remove, or reorder parameters, whether or not they have default arguments or are variadic. This rule is unchanged and applies to all variadic parameters. + +## Alternatives considered + +Two alternative labeling rules were considered. + +1. If a parameter list has more than one variadic parameter, every variadic parameter must have a label. +2. If a parameter list has more than one variadic parameter, every variadic parameter except for the first must have a label. + +Both alternatives are more restrictive in terms of the declarations they allow. This increases complexity and makes the parameter labeling rules harder to reason about. However, they might make it more difficult to write confusing APIs which mix variadic, defaulted, and required parameters. Overall, it seems better to trust programmers with greater flexibility, while also minimizing the number of rules they need to learn. + From 277df94c5b4617ff46b36d923e3b406e02eb79e9 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Tue, 18 Feb 2020 15:20:55 -0600 Subject: [PATCH 1397/4563] Preview package implementation [1/3] (#1089) * Revise the proposal template and process documentation re: SwiftPreview * Fix naming consistency for the preview package --- process.md | 11 +++++++++-- proposal-templates/0000-swift-template.md | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/process.md b/process.md index 56921110be..31c146fa18 100644 --- a/process.md +++ b/process.md @@ -56,14 +56,20 @@ for the upcoming Swift release will not be brought up for review. If you can't r * **Request a review**: initiate a pull request to the [swift-evolution repository][swift-evolution-repo] to indicate to the core team that you would like the proposal to be reviewed. When the proposal is sufficiently detailed and clear, and addresses feedback from earlier discussions of the idea, the pull request will be accepted. The proposal will be assigned a proposal number as well as a core team member to manage the review. * **Address feedback**: in general, and especially [during the review period][proposal-status], be responsive to questions and feedback about the proposal. +## Preparing an implementation + +When you are ready to request a review, a pull request with an implementation is required in addition to your proposal. Proposals that can ship as part of the [Standard Library Preview package] should be paired with a pull request against the [swift-evolution-staging repository][swift-evolution-staging]. All other proposals should be paired with an implementation pull request against the [main Swift repository](https://github.com/apple/swift). + +The preview package can accept new types, new protocols, and extensions to existing types and protocols that can be implemented without access to standard library internals or other non-public features. For more information about the kinds of changes that can be implemented in the preview package, see [SE-0264](https://github.com/apple/swift-evolution/blob/master/proposals/0264-stdlib-preview-package.md). + ## Review process The review process for a particular proposal begins when a member of the core team accepts a pull request of a new or updated proposal into the [swift-evolution repository][swift-evolution-repo]. That core team member becomes the *review manager* for the proposal. The proposal -is assigned a proposal number (if it is a new proposal), then enters -the review queue. +is assigned a proposal number (if it is a new proposal), and then enters +the review queue. If your proposal's accompanying implementation takes the form of a package, the review manager will merge your pull request into a new branch in the [swift-evolution-staging repository][swift-evolution-staging]. The review manager will work with the proposal authors to schedule the review. Reviews usually last a single week, but can run longer for @@ -113,6 +119,7 @@ A given proposal can be in one of several states: write the version number for which the implementation will be complete. [swift-evolution-repo]: https://github.com/apple/swift-evolution "Swift evolution repository" +[swift-evolution-staging]: https://github.com/apple/swift-evolution-staging "Swift evolution staging repository" [proposal-reviews]: https://forums.swift.org/c/evolution/proposal-reviews "'Proposal reviews' category of the Swift forums" [proposal-status]: https://apple.github.io/swift-evolution/ diff --git a/proposal-templates/0000-swift-template.md b/proposal-templates/0000-swift-template.md index 43190333b0..822517de46 100644 --- a/proposal-templates/0000-swift-template.md +++ b/proposal-templates/0000-swift-template.md @@ -7,7 +7,7 @@ *During the review process, add the following fields as needed:* -* Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) +* Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) or [apple/swift-evolution-staging#NNNNN](https://github.com/apple/swift-evolution-staging/pull/NNNNN) * Decision Notes: [Rationale](https://forums.swift.org/), [Additional Commentary](https://forums.swift.org/) * Bugs: [SR-NNNN](https://bugs.swift.org/browse/SR-NNNN), [SR-MMMM](https://bugs.swift.org/browse/SR-MMMM) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md) From a8b623d15326e24346947cc33e8ecffdd152838f Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Tue, 18 Feb 2020 18:25:48 -0500 Subject: [PATCH 1398/4563] Mark SE-0269 as implemented (#1114) * Mark SE-0269 as implemented * Update Swift implemented version --- proposals/0269-implicit-self-explicit-capture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0269-implicit-self-explicit-capture.md b/proposals/0269-implicit-self-explicit-capture.md index 93d5703fe2..770c3c8a81 100644 --- a/proposals/0269-implicit-self-explicit-capture.md +++ b/proposals/0269-implicit-self-explicit-capture.md @@ -3,7 +3,7 @@ * Proposal: [SE-0269](0269-implicit-self-explicit-capture.md) * Author: [Frederick Kellison-Linn](https://github.com/jumhyn) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Accepted** +* Status: **Implemented (Swift 5.3)** * Implementation: [apple/swift#23934](https://github.com/apple/swift/pull/23934) * Bug: [SR-10218](https://bugs.swift.org/browse/SR-10218) * Forum threads: [Discussion](https://forums.swift.org/t/review-capture-semantics-of-self/22017), [Pitch](https://forums.swift.org/t/allow-implicit-self-in-escaping-closures-when-self-is-explicitly-captured/22590), [Review thread](https://forums.swift.org/t/se-0269-increase-availability-of-implicit-self-in-escaping-closures-when-reference-cycles-are-unlikely-to-occur/30376) From 2f063f30e8b22b0a846b1056bfe041c114975c8b Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 18 Feb 2020 15:33:22 -0800 Subject: [PATCH 1399/4563] Update SE-0155 status to reflect reality Of the proposed functionality, there is still outstanding work to implement support for cases with names that only differ in argument labels or number of arguments: https://bugs.swift.org/browse/SR-12206 https://bugs.swift.org/browse/SR-12229 The proposed changes to pattern matching behavior have also not been implemented either as originally proposed or as described in the acceptance revisions, and it would be source-breaking to tighten the screws further, so it seems prudent to push that out of the accepted proposal. --- ...0155-normalize-enum-case-representation.md | 141 ++++++++++++------ 1 file changed, 95 insertions(+), 46 deletions(-) diff --git a/proposals/0155-normalize-enum-case-representation.md b/proposals/0155-normalize-enum-case-representation.md index 900033849a..5ede6b537e 100644 --- a/proposals/0155-normalize-enum-case-representation.md +++ b/proposals/0155-normalize-enum-case-representation.md @@ -4,9 +4,9 @@ * Authors: [Daniel Duan][], [Joe Groff][] * Review Manager: [John McCall][] * Status: **Accepted with revisions** -* Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170417/035972.html) -* Previous Revision: [1][Revision 1] -* Bug: [SR-4691](https://bugs.swift.org/browse/SR-4691) +* Decision Notes: [Rationale][] +* Previous Revision: [1][Revision 1], [Originally Accepted Proposal][] +* Bugs: [SR-4691](https://bugs.swift.org/browse/SR-4691), [SR-12206](https://bugs.swift.org/browse/SR-12206), [SR-12229](https://bugs.swift.org/browse/SR-12229) ## Introduction @@ -181,58 +181,88 @@ enum Tree { } ``` -### Pattern Consistency +### Disambiguating pattern matches -*(The following enum will be used throughout code snippets in this section).* +Swift's original pattern matching rules for enums treated each associated value +as a tuple, and as such, allowed for multiple elements of an associated value to +be pattern matched as a single value, or destructured into individual elements +and matched independently: -```swift -indirect enum Expr { - case variable(name: String) - case lambda(parameters: [String], body: Expr) +``` +enum Buffalo { + case buffalo(buffalo: String, buffalo: String) +} + +switch buffalo { +case .buffalo(_): // ok +case .buffalo(_, _): // ok } ``` -Compared to patterns in Swift 3, matching against enum cases will follow -stricter rules. This is a consequence of no longer relying on tuple patterns. +To maintain source compatibility, this behavior cannot be changed. However, it +poses an ambiguity when combined with the added functionality above, where +multiple cases share the same base name, but have different argument labels +or number of arguments: -When an associated value has a label, the sub-pattern must include the label -exactly as declared. There are two variants that should look familiar to Swift -3 users. Variant 1 allows user to bind the associated value to arbitrary name in -the pattern by requiring the label: +``` +enum Buffalo { + case buffalo(String) + case buffalo(bill: String) + case buffalo(buffalo: String) + case buffalo(buffalo: String, buffalo: String) +} -```swift -case .variable(name: let x) // okay -case .variable(x: let x) // compile error; there's no label `x` -case .lambda(parameters: let params, body: let body) // Okay -case .lambda(params: let params, body: let body) // error: 1st label mismatches +switch buffalo { +case .buffalo(_): // Ambiguous under the original rules, could refer to any of the above +} ``` -User may choose not to use binding names that differ from labels. In this -variant, the corresponding value will bind to the label, resulting in this -shorter form: +To resolve the ambiguity, single-element patterns shall favor matching single- +element associated values over tuples. Explicit destructuring must be used to +favor a multiple-element associated value: -```swift -case .variable(let name) // okay, because the name is the same as the label -case .lambda(let parameters, let body) // this is okay too, same reason. -case .variable(let x) // compiler error. label must appear one way or another. -case .lambda(let params, let body) // compiler error, same reason as above. ``` +enum Buffalo { + case buffalo(String) + case buffalo(buffalo: String, buffalo: String) +} -Only one of these variants may appear in a single pattern. Swift compiler will -raise a compile error for mixed usage. +switch buffalo { +case .buffalo(_): // Favors `buffalo(_:)` +case .buffalo(_, _): // Favors `buffalo(buffalo:buffalo:)` +} +``` + + +If multiple enum cases agree in arity but have different argument labels, then +an unlabeled pattern favors matching an unlabeled associated value: -```swift -case .lambda(parameters: let params, let body) // error, can not mix the two. ``` +enum Buffalo { + case buffalo(String, String) + case buffalo(buffalo: String, buffalo: String) +} -Some patterns will no longer match enum cases. For example, all associated -values can bind as a tuple in Swift 3, this will no longer work after this -proposal: +switch buffalo { +case .buffalo(_, _): // Favors `buffalo(_:_:)` +case .buffalo(_, _): // Favors `buffalo(buffalo:buffalo:)` +} +``` -```swift -// deprecated: matching all associated values as a tuple -if case let .lambda(f) = anLambdaExpr { - evaluateLambda(parameters: f.parameters, body: f.body) +If there are no unlabeled associated values and there are multiple labeled +associated values with the same arity, then an unlabeled pattern is ambiguous. +Explicit labels can be used to disambiguate: + +``` +enum Buffalo { + case buffalo(bill: String) + case buffalo(buffalo: String) +} + +switch buffalo { +case .buffalo(_): // Error, ambiguous +case .buffalo(bill: _): // Favors `buffalo(bill:)` +case .buffalo(buffalo: _): // Favors `buffalo(buffalo:)` } ``` @@ -244,12 +274,6 @@ Case Declaration". Syntax for case constructor at use site remain source-compatible. -A large portion of pattern matching syntax for enum cases with associated values -remain unchanged. But patterns for matching all values as a tuple, patterns that -elide the label and binds to names that differ from the labels, patterns that -include labels for some sub-patterns but the rest of them are deprecated by this -proposal. Therefore this is a source breaking change. - ## Effect on ABI stability and resilience After this proposal, enum cases may have compound names. This means the standard @@ -290,7 +314,9 @@ be introduced with alternative syntax (perhaps related to splats) later without source-breakage. And the need to implement `Equatable` may also disappear with auto-deriving for `Equatable` conformance. -The previous revision of this proposal mandated that the labeled form of +## Revision History + +The [first revision of this proposal][Revision 1] mandated that the labeled form of sub-pattern (`case .elet(locals: let x, body: let y)`) be the only acceptable pattern. Turns out the community considers this to be too verbose in some cases. @@ -298,6 +324,27 @@ A drafted version of this proposal considered allowing "overloaded" declaration of enum cases (same full-name, but with associated values with different types). We ultimately decided that this feature is out of the scope of this proposal. +The [second revision of this proposal][Originally Accepted Proposal] was accepted with revisions. As originally written, the proposal required that pattern matching against an enum match either by the name of the bound variables, or by explicit labels on the parts of the associated values: + +``` +enum Foo { + case foo(bar: Int) +} + +func switchFoo(x: Foo) { + switch x { + case .foo(let bar): // ok + case .foo(bar: let bar): // ok + case .foo(bar: let bas): // ok + case .foo(let bas): // not ok + } +} +``` + +However, it was decided in review that this was still too restrictive and +source-breaking, and so the core team [accepted the proposal][Rationale] with the modification that pattern matches only had to match the case declaration in arity, and case labels could be either provided or elided in their entirety, unless there was an ambiguity. Even then, as of Swift 5.2, this part of the proposal has not been implemented, and it would be a source breaking change to do so. Therefore, the "Pattern Consistency" section of the original proposal has been removed, and replaced with the "Disambiguating pattern matches" section above, which provides a minimal disambiguation rule for pattern matching cases that share a +base name. Pattern matching otherwise still follows the vintage Swift 2 rules, where the payload can be matched either as a single tuple, or as individual tuple elements. + [SE-0155]: 0155-normalize-enum-case-representation.md [SE-0111]: 0111-remove-arg-label-type-significance.md [Daniel Duan]: https://github.com/dduan @@ -306,3 +353,5 @@ We ultimately decided that this feature is out of the scope of this proposal. [TJs comment]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030614.html [Revision 1]: https://github.com/apple/swift-evolution/blob/43ca098355762014f53e1b54e02d2f6a01253385/proposals/0155-normalize-enum-case-representation.md [Normalize Enum Case Representation (rev. 2)]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170306/033626.html +[Originally Accepted Proposal]: https://github.com/apple/swift-evolution/blob/4cbb1f1fa836496d4bfba95c4b78a9754690956d/proposals/0155-normalize-enum-case-representation.md +[Rationale]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170417/035972.html From 08f7ea4605f47cb31c08491567131be7358a0025 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 19 Feb 2020 05:39:23 +0000 Subject: [PATCH 1400/4563] Add Swift 5.3 to the Language Version filter --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 81ee4e4f58..3df1218514 100644 --- a/index.js +++ b/index.js @@ -19,7 +19,7 @@ var proposals * To be updated when proposals are confirmed to have been implemented * in a new language version. */ -var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4', '4.1', '4.2', '5', '5.1', '5.2'] +var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4', '4.1', '4.2', '5', '5.1', '5.2', '5.3'] /** Storage for the user's current selection of filters when filtering is toggled off. */ var filterSelection = [] @@ -1039,4 +1039,4 @@ function cleanNumberFromState (state) { function addNumberToState (state, count) { return state + ' (' + count + ')' -} \ No newline at end of file +} From a2930eca7000f65a40de5b626e1b5585b324c6ce Mon Sep 17 00:00:00 2001 From: David Hart Date: Tue, 18 Feb 2020 20:46:12 +0100 Subject: [PATCH 1401/4563] Amendment to the Conditional Target Dependency proposal --- proposals/0273-swiftpm-conditional-target-dependencies.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0273-swiftpm-conditional-target-dependencies.md b/proposals/0273-swiftpm-conditional-target-dependencies.md index a00bcabad1..535e115628 100644 --- a/proposals/0273-swiftpm-conditional-target-dependencies.md +++ b/proposals/0273-swiftpm-conditional-target-dependencies.md @@ -51,7 +51,7 @@ It is important to note that this proposal has no effect on dependency resolutio ### New `PackageDescription` API -All the cases of the `Target.Dependency` enum will gain a new optional `BuildSettingCondition` argument. The current static factory functions for initializing those enums will be obsoleted in the version of the tools this proposal will appear in, and new functions will take their place, introducing a new optional argument: +All the cases of the `Target.Dependency` enum will gain a new optional `TargetDependencyCondition` argument, a type with the same API as `BuildSettingCondition`. Creating a new type allows the APIs to evolve independently, if ever they have to, to support different condition types. The current static factory functions for initializing those enums will be obsoleted in the version of the tools this proposal will appear in, and new functions will take their place, introducing a new optional argument: ```swift extension Target.Dependency { @@ -63,7 +63,7 @@ extension Target.Dependency { @available(_PackageDescription, introduced: 5.3) public static func target( name: String, - condition: BuildSettingCondition? = nil + condition: TargetDependencyCondition? = nil ) -> Target.Dependency { // ... } @@ -78,7 +78,7 @@ extension Target.Dependency { public static func product( name: String, package: String? = nil, - condition: BuildSettingCondition? = nil + condition: TargetDependencyCondition? = nil ) -> Target.Dependency { // ... } @@ -92,7 +92,7 @@ extension Target.Dependency { @available(_PackageDescription, introduced: 5.3) public static func byName( name: String, - condition: BuildSettingCondition? = nil + condition: TargetDependencyCondition? = nil ) -> Target.Dependency { // ... } From 07c2727fb467a8c537bf88fd62df18d8796a87c0 Mon Sep 17 00:00:00 2001 From: David Hart Date: Thu, 20 Feb 2020 00:15:59 +0100 Subject: [PATCH 1402/4563] New Package Manager Localization proposal (#1116) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * New Package Manager Localization proposal * Add an optional localization parameter to the process rule * Minor improvements * Update proposals/XXXX-package-manager-localizations.md Co-Authored-By: Ben Rimmington * Update proposals/XXXX-package-manager-localizations.md Co-Authored-By: Ben Rimmington * Update proposals/XXXX-package-manager-localizations.md Co-Authored-By: Ben Rimmington * Update file name to proposal name * localization to locale * New Package Manager Localization proposal * Apply feedback * Prepare for review Co-authored-by: Ben Rimmington Co-authored-by: Boris Bügling --- ...278-package-manager-localized-resources.md | 315 ++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 proposals/0278-package-manager-localized-resources.md diff --git a/proposals/0278-package-manager-localized-resources.md b/proposals/0278-package-manager-localized-resources.md new file mode 100644 index 0000000000..ed79be5840 --- /dev/null +++ b/proposals/0278-package-manager-localized-resources.md @@ -0,0 +1,315 @@ +# Package Manager Localized Resources + +* Proposal: [SE-0278](0278-package-manager-localized-resources.md) +* Authors: [David Hart](https://github.com/hartbit) +* Implementation: [SwiftPM #2535](https://github.com/apple/swift-package-manager/pull/2535) +* Review Manager: [Boris Buegling](https://github.com/neonichu) +* Status: Active review (02 19...02 26) + +## Introduction + +This proposal builds on top of the [Package Manager Resources](0271-package-manager-resources.md) proposal to allow defining localized versions of resources in the SwiftPM manifest and have them automatically accessible at runtime using the same APIs. + +## Motivation + +The recently accepted [Package Manager Resources](0271-package-manager-resources.md) proposal allows SwiftPM users to define resources (images, data file, etc...) in their manifests and have them packaged inside a bundle to be accessible at runtime using the Foundation `Bundle` APIs. Bundles support storing different versions of resources for different localizations and can retrieve the version which makes most sense depending on the runtime environment, but SwiftPM currently offers no way to define those localized variants. + +While it is technically possible to benefit from localization today by setting up a resource directory structure that the `Bundle` API expects and specifying it with a `.copy` rule in the manifest (to have SwiftPM retain the structure), this comes at a cost: it bypasses any platform-custom processing that comes with `.process`, and doesn't allow SwiftPM to provide diagnostics when localized resources are mis-configured. + +Without a way to define localized resources, package authors are missing out on powerful Foundation APIs to have their applications, libraries and tools adapt to different regions and languages. + +## Goals + +The goals of this proposal builds on those of the [Package Manager Resources](0271-package-manager-resources.md) proposal: + +* Making it easy to add localized variants of resources with minimal change to the manifest. + +* Avoiding unintentionally copying files not intended to be localized variants into the product. + +* Supporting platform-specific localized resource types for packages written using specific APIs (e.g. Storyboards, XIBs, strings, and stringsdict files on Apple platforms). + +## Proposed Solution + +The proposed solution for supporting localized resources in Swift packages is to: + +* Add a new optional `defaultLocalization` parameter to the `Package` initializer to define the default localization for the resource bundle. The default localization will be used as a fallback when no other localization for a resource fits the runtime environment. SwiftPM will require that parameter be set if the package contains localized resources. + +* Require localized resources to be placed in directories named after the [IETF Language Tag](https://en.wikipedia.org/wiki/IETF_language_tag) they represent followed by an `.lproj` suffix, or in a special `Base.lproj` directory to open up future support for [Base Internationalization](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/InternationalizingYourUserInterface/InternationalizingYourUserInterface.html#//apple_ref/doc/uid/10000171i-CH3-SW2) on Apple platforms. While Foundation supports several localization directories which are not valid IETF Language Tags, like `English` or `en_US`, it is recommended to use `en-US` style tags with a two-letter ISO 639-1 or three-letter ISO 639-2 language code, followed by optional region and/or dialect codes separated by a hyphen (see the [CFBundleDevelopmentRegion documentation](https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundledevelopmentregion#)). + +* Add an optional `localization` parameter to the `Resource.process` factory function to allow declaring files outside of `.lproj` directories as localized for the default or base localization. + +* Have SwiftPM diagnose incoherent resource configurations. For example, if a resource has both an un-localized and a localized variant, the localized variant can never be selected by `Foundation` (see the documentation on [The Bundle Search Pattern](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/AccessingaBundlesContents/AccessingaBundlesContents.html#//apple_ref/doc/uid/10000123i-CH104-SW7)). + +* Have SwiftPM copy the localized resource to the resource bundle in the right locations for the `Foundation` APIs to find and use them, and generate a `Info.plist` for the resources bundle containing the `CFBundleDevelopmentRegion` key set to the `defaultLocalization`. + +## Detailed Design + +### Declaring Localized Resources + +The `Package` initializer in the `PackageDescription` API gains a new optional `defaultLocalization` parameter with type `LocalizationTag` and a default value of `nil`: + +```swift +public init( + name: String, + defaultLocalization: LocalizationTag = nil, // New defaultLocalization parameter. + pkgConfig: String? = nil, + providers: [SystemPackageProvider]? = nil, + products: [Product] = [], + dependencies: [Dependency] = [], + targets: [Target] = [], + swiftLanguageVersions: [Int]? = nil, + cLanguageStandard: CLanguageStandard? = nil, + cxxLanguageStandard: CXXLanguageStandard? = nil +) +``` +`LocalizationTag` is a wrapper around a [IETF Language Tag](https://en.wikipedia.org/wiki/IETF_language_tag), with a `String` initializer and conforming to `Hashable`, `RawRepresentable`, `CustomStringConvertible` and `ExpressibleByStringLiteral`. While a `String` would suffice for now, the type allows for future expansion. + +```swift +/// A wrapper around a [IETF Language Tag](https://en.wikipedia.org/wiki/IETF_language_tag). +public struct LocalizationTag: Hashable { + + /// A IETF language tag. + public let tag: String + + /// Creates a `LocalizationTag` from its IETF string representation. + public init(_ tag: String) { + self.tag = tag + } +} + +extension LocalizationTag: RawRepresentable, ExpressibleByStringLiteral, CustomStringConvertible { + // Implementation. +} +``` + +To allow marking files outside of `.lproj` directories as localized, the `Resource.process` factory function gets a new optional `localization` parameter typed as an optional `LocalizationType`, an enum with two cases: `.default` for declaring a default localized variant, and `.base` for declaring a base-localized resource: + +```swift +public struct Resource { + public enum LocalizationType { + case `default` + case base + } + + public static func process(_ path: String, localization: LocalizationType? = nil) -> Resource +} +``` + +### Localized Resource Discovery + +SwiftPM will only detect localized resources if they are defined with the `.process` rule. When scanning for files with that rule, SwiftPM will tag files inside directories with an `.lproj` suffix as localized variants of a resource. The name of the directory before the `.lproj` suffix identifies which localization they correspond to. For example, an `en.lproj` directory contains resources localized to English, while a `fr-CH.lproj` directory contains resources localized to French for Swiss speakers. + +Files in those special directories represent localized variants of a "virtual" resource with the same name in the parent directory, and the manifest must use that path to reference them. For example, the localized variants in `Resources/en.lproj/Icon.png` and `Resources/fr.lproj/Icon.png` are english and french variants of the same "virtual" resource with the `Resources/Icon.png` path, and a reference to it in the manifest would look like: + +```swift +let package = Package( + name: "BestPackage", + defaultLocalization: "en", + targets: [ + .target(name: "BestTarget", resources: [ + .process("Resources/Icon.png"), + ]) + ] +) +``` + +To support SwiftPM clients for Apple platform-specific resources, SwiftPM will also recognize resources located in `Base.lproj` directories as resources using [Base Internationalization](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/InternationalizingYourUserInterface/InternationalizingYourUserInterface.html#//apple_ref/doc/uid/10000171i-CH3-SW2) and treat them as any other localized variants. + +In addition to localized resources detected by scanning `.lproj` directories, SwiftPM will also take into account processed resources declared with a `localization` parameter in the manifest. This allows package authors to mark files outside of `.lproj` directories as localized, for example to keep localized and un-localized resources together. Separate post-processing done outside of SwiftPM can provide additional localizations in this case. + +### Validating Localized Resources + +SwiftPM can help package authors by diagnosing mis-configurations of localized resources and other inconsistencies that may otherwise only show up at runtime. To illustrate the diagnostics described below, we define a `Package.swift` manifest with a default localization of `"en"`, and two resource paths with the `.process` rule an one with the `.copy` rule: + +```swift +let package = Package( + name: "BestPackage", + defaultLocalization: "en", + targets: [ + .target(name: "BestTarget", resources: [ + .process("Resources/Processed"), + .copy("Resources/Copied"), + ]) + ] +``` + +#### Sub-directory in Localization Directory + +To avoid overly-complex and ambiguous resource directory structures, SwiftPM with emit an error when a localization directory in a `.process` resource path contains a sub-directory. For example, the following directory structure: + +``` +BestPackage +|-- Sources +| `-- BestTarget +| |-- Resources +| | |-- Processed +| | | `-- en.lproj +| | | `-- directory +| | | `-- file.txt +| | `-- Copied +| | `-- en.lproj +| | `-- directory +| | `-- file.txt +| `-- main.swift +`-- Package.swift +``` + +will emit the following diagnostic: + +``` +error: localization directory `Resources/Processed/en.lproj` in target `BestTarget` contains sub-directories, which is forbidden +``` + +#### Missing Default Localized Variant + +When a localized resource is missing a variant for the default localization, `Foundation` may not be able to find the resource depending on the run environment. SwiftPM will emit a warning to warn against it. For example, the following directory structure: + +``` +BestPackage +|-- Sources +| `-- BestTarget +| |-- Resources +| | |-- Processed +| | | `-- fr.lproj +| | | `-- Image.png +| | `-- Copied +| | `-- fr.lproj +| | `-- Image.png +| `-- main.swift +`-- Package.swift +``` + +will emit the following diagnostic: + + +``` +warning: resource 'Image.png' in target 'BestTarget' is missing a localization for the default localization 'en'; the default localization is used as a fallback when no other localization matches +``` + +#### Un-localized and Localized Variants + +When there exists both an un-localized and localized variant of the same resource, SwiftPM will emit a warning to let users know that the localized variants will never be chosen at runtime, due to the search pattern of `Foundation` APIs (see the documentation on [The Bundle Search Pattern](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/AccessingaBundlesContents/AccessingaBundlesContents.html#//apple_ref/doc/uid/10000123i-CH104-SW7)). For example, the following directory structure: + +``` +BestPackage +|-- Sources +| `-- BestTarget +| |-- Resources +| | |-- Processed +| | | |-- en.lproj +| | | | `-- Image.png +| | | `-- Image.png +| | `-- Copied +| | |-- en.lproj +| | | `-- Image.png +| | `-- Image.png +| `-- main.swift +`-- Package.swift +``` + +will emit the following diagnostic: + +``` +warning: resource 'Image.png' in target 'BestTarget' has both localized and un-localized variants; the localized variants will never be chosen +``` + +### Missing Default Localization + +The `defaultLocalization` property is optional and has a default value of `nil`, but its required to provide a valid `LocalizationTag` in the presence of localized resources. SwiftPM with emit an error if that is not the case. For example, the following directory structure: + +``` +BestPackage +|-- Sources +| `-- BestTarget +| |-- Resources +| | `-- en.lproj +| | `-- Localizable.strings +| `-- main.swift +`-- Package.swift +``` + +with the following manifest: + +```swift +let package = Package( + name: "BestPackage", + targets: [ + .target(name: "BestTarget", resources: [ + .process("Resources"), + ]) + ] +``` + +will emit the following diagnostic: + +``` +error: missing manifest property 'defaultLocalization'; it is required in the presence of localized resources +``` + +#### Explicit Localization Resource in Localization Directory + +Explicit resource localization declarations exist to avoid placing resources in localization directories. To avoid any ambiguity, SwiftPM will emit an error when a resource with an explicit localization declaration is inside a localization directory. + +``` +BestPackage +|-- Sources +| `-- BestTarget +| |-- Resources +| | `-- en.lproj +| | `-- Storyboard.storyboard +| `-- main.swift +`-- Package.swift +``` + +with the following manifest: + +```swift +let package = Package( + name: "BestPackage", + defaultLocalization: "en", + targets: [ + .target(name: "BestTarget", resources: [ + .process("Resources", localization: .base), + ]) + ] +``` + +will emit the following diagnostic: + +``` +error: resource 'Storyboard.storyboard' in target 'BestTarget' is in a localization directory and has an explicit localization declaration; choose one or the other to avoid any ambiguity +``` + +### Resource Bundle Generation + +SwiftPM will copy localized resources into the correct locations of the resources bundle for them to be picked up by Foundation. It will also generate a `Info.plist` for that bundle with the `CFBundleDevelopmentRegion` value declared in the manifest: + +```xml + + + + + CFBundleDevelopmentRegion + fr-CH + + +``` + +### Runtime Access + +The Foundation APIs already used to load resources will automatically pick up the correct localization: + +```swift +// Get path to a file, which can be localized. +let path = Bundle.module.path(forResource: "TOC", ofType: "md") + +// Load an image from the bundle, which can be localized. +let image = UIImage(named: "Sign", in: .module, with: nil) +``` + +And other APIs will now work as expected on all platforms Foundation is supported on: + +```swift +// Get localization out of strings files. +var localizedGreeting = NSLocalizedString("greeting", bundle: .module) +``` From 23769674c62b9aa0491fe91b89cd2d3c936ab599 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 19 Feb 2020 15:26:53 -0800 Subject: [PATCH 1403/4563] Update SE-0155 to reflect reality harder --- proposals/0155-normalize-enum-case-representation.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/proposals/0155-normalize-enum-case-representation.md b/proposals/0155-normalize-enum-case-representation.md index 5ede6b537e..361e2082f1 100644 --- a/proposals/0155-normalize-enum-case-representation.md +++ b/proposals/0155-normalize-enum-case-representation.md @@ -235,7 +235,8 @@ case .buffalo(_, _): // Favors `buffalo(buffalo:buffalo:)` If multiple enum cases agree in arity but have different argument labels, then -an unlabeled pattern favors matching an unlabeled associated value: +an unlabeled pattern favors matching an unlabeled associated value. Specifying +labels in the pattern can be used to favor declarations matching those label: ``` enum Buffalo { @@ -245,7 +246,9 @@ enum Buffalo { switch buffalo { case .buffalo(_, _): // Favors `buffalo(_:_:)` -case .buffalo(_, _): // Favors `buffalo(buffalo:buffalo:)` +case .buffalo(buffalo: _, buffalo: _): // Favors `buffalo(buffalo:buffalo:)` +case .buffalo(_, buffalo: _): // Favors `buffalo(buffalo:buffalo:)` +case .buffalo(buffalo: _, _): // Favors `buffalo(buffalo:buffalo:)` } ``` From bf5e6bb1523c1673be676699daa01db2032cf34e Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 20 Feb 2020 08:21:41 +0000 Subject: [PATCH 1404/4563] [SE-0266] Update status: "Implemented (Swift 5.3)" --- proposals/0266-synthesized-comparable-for-enumerations.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0266-synthesized-comparable-for-enumerations.md b/proposals/0266-synthesized-comparable-for-enumerations.md index 5ee3b775a9..670c3d802a 100644 --- a/proposals/0266-synthesized-comparable-for-enumerations.md +++ b/proposals/0266-synthesized-comparable-for-enumerations.md @@ -3,8 +3,9 @@ * Proposal: [SE-0266](0266-synthesized-comparable-for-enumerations.md) * Author: [Kelvin Ma (taylorswift)](https://forums.swift.org/u/taylorswift) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** +* Status: **Implemented (Swift 5.3)** * Implementation: [apple/swift#25696](https://github.com/apple/swift/pull/25696) +* Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0266-synthesized-comparable-conformance-for-enum-types/29854) ## Introduction From 0c626b4793e5a58b38f778588afb605f649998b2 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 20 Feb 2020 08:42:44 +0000 Subject: [PATCH 1405/4563] [SE-0278] Fix "status not found" parser warning --- proposals/0278-package-manager-localized-resources.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0278-package-manager-localized-resources.md b/proposals/0278-package-manager-localized-resources.md index ed79be5840..65a7ed4c2b 100644 --- a/proposals/0278-package-manager-localized-resources.md +++ b/proposals/0278-package-manager-localized-resources.md @@ -1,10 +1,10 @@ # Package Manager Localized Resources * Proposal: [SE-0278](0278-package-manager-localized-resources.md) -* Authors: [David Hart](https://github.com/hartbit) -* Implementation: [SwiftPM #2535](https://github.com/apple/swift-package-manager/pull/2535) +* Author: [David Hart](https://github.com/hartbit) * Review Manager: [Boris Buegling](https://github.com/neonichu) -* Status: Active review (02 19...02 26) +* Status: **Active review (February 19...February 26)** +* Implementation: [apple/swift-package-manager#2535](https://github.com/apple/swift-package-manager/pull/2535) ## Introduction From faf0e45e2b95f54c0dd8218e627335d71a6f160f Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 24 Feb 2020 10:58:33 +0000 Subject: [PATCH 1406/4563] [SE-0270] Update status: "Implemented (Swift 5.3)" --- proposals/0270-rangeset-and-collection-operations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0270-rangeset-and-collection-operations.md b/proposals/0270-rangeset-and-collection-operations.md index 1be2c837db..05c1dc9413 100644 --- a/proposals/0270-rangeset-and-collection-operations.md +++ b/proposals/0270-rangeset-and-collection-operations.md @@ -3,7 +3,7 @@ * Proposal: [SE-0270](0270-rangeset-and-collection-operations.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5.3)** * Implementation: [apple/swift#28161](https://github.com/apple/swift/pull/28161) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/9b5957c00e7483ab8664afe921f989ed1394a666/proposals/0270-rangeset-and-collection-operations.md), [2](https://github.com/apple/swift-evolution/blob/b17d85fcaf38598fd2ea19641d0e9c26c96747ec/proposals/0270-rangeset-and-collection-operations.md) * Reviews: [1](https://forums.swift.org/t/se-0270-add-collection-operations-on-noncontiguous-elements/30691), [2](https://forums.swift.org/t/se-0270-review-2-add-collection-operations-on-noncontiguous-elements/31653), [3](https://forums.swift.org/t/se-0270-review-3-add-collection-operations-on-noncontiguous-elements/32839) From 15b73cdb78f366798ba8f1c007a64571e53125d8 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 26 Feb 2020 09:08:07 -0800 Subject: [PATCH 1407/4563] Update 0274-magic-file.md --- proposals/0274-magic-file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0274-magic-file.md b/proposals/0274-magic-file.md index a5ea1dcf8a..08c84c11e2 100644 --- a/proposals/0274-magic-file.md +++ b/proposals/0274-magic-file.md @@ -4,7 +4,7 @@ * Authors: [Brent Royal-Gordon](https://github.com/brentdax), [Dave DeLong](https://github.com/davedelong) * Review Manager: [Ben Cohen](https://github.com/airspeedswift/) * Original review: [Returned for revision](https://forums.swift.org/t/se-0274-concise-magic-file-names/32373/50) -* Status: **Active Re-review (28 January-4 February 2020)** +* Status: **Accepted** * Implementation: Prototype in master behind `-Xfrontend -enable-experimental-concise-pound-file`; revisions in [apple/swift#29412](https://github.com/apple/swift/pull/29412) ## Introduction From e8251ff22d8c633eb826f751f19e85446fcef11f Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 26 Feb 2020 10:04:39 -0800 Subject: [PATCH 1408/4563] Update 0264-stdlib-preview-package.md --- proposals/0264-stdlib-preview-package.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0264-stdlib-preview-package.md b/proposals/0264-stdlib-preview-package.md index f0b1b9bb88..a95e3eec1c 100644 --- a/proposals/0264-stdlib-preview-package.md +++ b/proposals/0264-stdlib-preview-package.md @@ -3,7 +3,7 @@ * Proposal: [SE-0264](0264-stdlib-preview-package.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Max Moiseev](https://github.com/moiseev), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Accepted** +* Status: **Implemented** * Decision Notes: [Rationale](https://forums.swift.org/t/se-0264-review-2-standard-library-preview-package/31288/16) * Implementation: 1. [apple/swift-evolution#1089](https://github.com/apple/swift-evolution/pull/1089) From ef2b3d3fa8259f7eb74fa17637ff036e65c5eefe Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 27 Feb 2020 10:05:10 +0000 Subject: [PATCH 1409/4563] [SE-0277] Update status: "Accepted" --- proposals/0277-float16.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/0277-float16.md b/proposals/0277-float16.md index e53f19dfba..30e23d6953 100644 --- a/proposals/0277-float16.md +++ b/proposals/0277-float16.md @@ -3,9 +3,9 @@ * Proposal: [SE-0277](0277-float16.md) * Author: [Stephen Canon](https://github.com/stephentyrone) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Pitch thread: [Add Float16](https://forums.swift.org/t/add-float16/33019) -* Implementation: [Implement Float16](https://github.com/apple/swift/pull/21738) -* Status: **Active review (6–17 February 2020)** +* Status: **Accepted** +* Implementation: [apple/swift#21738](https://github.com/apple/swift/pull/21738) +* Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0277-float16/34121) ## Introduction @@ -14,6 +14,7 @@ protocols, binding the IEEE 754 *binary16* format (aka *float16*, *half-precisio and bridged by the compiler to the C `_Float16` type. * Old pitch thread: [Add `Float16`](https://forums.swift.org/t/add-float16/19370). +* New pitch thread: [Add Float16](https://forums.swift.org/t/add-float16/33019) ## Motivation From c3d1e99512e788380f23cd178491fecc89d719aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Thu, 27 Feb 2020 10:32:29 -0800 Subject: [PATCH 1410/4563] Update proposal status for acceptance --- proposals/0278-package-manager-localized-resources.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0278-package-manager-localized-resources.md b/proposals/0278-package-manager-localized-resources.md index 65a7ed4c2b..4d88fd1ce8 100644 --- a/proposals/0278-package-manager-localized-resources.md +++ b/proposals/0278-package-manager-localized-resources.md @@ -3,7 +3,7 @@ * Proposal: [SE-0278](0278-package-manager-localized-resources.md) * Author: [David Hart](https://github.com/hartbit) * Review Manager: [Boris Buegling](https://github.com/neonichu) -* Status: **Active review (February 19...February 26)** +* Status: **Accepted** * Implementation: [apple/swift-package-manager#2535](https://github.com/apple/swift-package-manager/pull/2535) ## Introduction From ebce814c3daae033df9a3d7cf0f21058bd213b47 Mon Sep 17 00:00:00 2001 From: Azoy Date: Mon, 13 Jan 2020 21:49:00 -0500 Subject: [PATCH 1411/4563] Tuples are Equatable, Comparable, and Hashable proposal Add Comparable and Hashable conformance --- ...uples-are-equatable-comparable-hashable.md | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 proposals/nnnn-tuples-are-equatable-comparable-hashable.md diff --git a/proposals/nnnn-tuples-are-equatable-comparable-hashable.md b/proposals/nnnn-tuples-are-equatable-comparable-hashable.md new file mode 100644 index 0000000000..e4c8b46b93 --- /dev/null +++ b/proposals/nnnn-tuples-are-equatable-comparable-hashable.md @@ -0,0 +1,157 @@ +# Tuples Conform to `Equatable`, `Comparable`, and `Hashable` + +* Proposal: [SE-NNNN](NNNN-tuples-are-equatable-comparable-hashable.md) +* Author: [Alejandro Alonso](https://github.com/Azoy) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation Equatable: [apple/swift#28833](https://github.com/apple/swift/pull/28833) +* Implementation Comparable: *Coming Soon* +* Implementation Hashable: *Coming Soon* + +## Introduction + +Introduce `Equatable`, `Comparable`, and `Hashable` conformance for all tuples whose elements are themselves `Equatable`, `Comparable`, and `Hashable`. + +Swift-evolution thread: [Tuples Conform to Equatable, Comparable, and Hashable](https://forums.swift.org/t/tuples-conform-to-equatable-comparable-and-hashable/34156) + +## Motivation + +Tuples in Swift currently lack the ability to conform to protocols. This has led many users to stop using tuples altogether in favor of structures that they can them conform protocols to. The shift from tuples to structures have made tuples almost feel like a second class type in the language because of them not being able to do simple operations that should *just* work. + +Consider the following snippet of code that naively tries to use tuples for simple operations, but instead is faced with ugly errors. + +```swift +let points = [(x: 128, y: 316), (x: 0, y: 0), (x: 100, y: 42)] +let origin = (x: 0, y: 0) + +// error: type '(x: Int, y: Int)' cannot conform to 'Equatable'; +// only struct/enum/class types can conform to protocols +if points.contains(origin) { + // do some serious calculations here +} + +// error: type '(x: Int, y: Int)' cannot conform to 'Comparable'; +// only struct/enum/class types can conform to protocols +let sortedPoints = points.sorted() + +// error: type '(x: Int, y: Int)' cannot conform to 'Hashable'; +// only struct/enum/class types can conform to protocols +let uniquePoints = Set(points) +``` + +This also creates friction when one needs to conditionally conform to a type, or if a type is just trying to get free conformance synthesis for protocols like `Equatable` or `Hashable`. + +```swift +struct Restaurant { + let name: String + let location: (latitude: Int, longitude: Int) +} + +// error: type 'Restaurant' does not conform to protocol 'Equatable' +extension Restaurant: Equatable {} + +// error: type 'Restaurant' does not conform to protocol 'Hashable' +extension Restaurant: Hashable {} +``` + +These are simple and innocent examples of trying to use tuples in one's code, but currently the language lacks the means to get these examples working and prevents the user from writing this code. + +After all the errors, one decides to give in and create a structure to mimic the tuple layout. From a code size perspective, creating structures to mimic each unique tuple need adds a somewhat significant amount of size to one's binary. + +## Proposed solution + +Introduce `Equatable`, `Comparable`, and `Hashable` conformance for all tuples whose elements themselves conform to said protocols. While this isn't a general purpose conform any tuple to any protocol proposal, `Equatable`, `Comparable`, and `Hashable` are crucial protocols to conform to because it allows for all of the snippets above in Motivation to compile and run as expected along with many other standard library operations to work nicely with tuples. + +### Equatable + +The rule is simple: if all of the tuple elements are themselves `Equatable` then the overall tuple itself conforms to `Equatable`. + +```swift +// Ok, Int is Equatable thus the tuples are Equatable +(1, 2, 3) == (1, 2, 3) // true + +struct EmptyStruct {} + +// error: type '(EmptyStruct, Int, Int)' does not conform to protocol 'Equatable' +// note: value of type 'EmptyStruct' does not conform to protocol 'Equatable', +// preventing conformance of '(EmptyStruct, Int, Int)' to 'Equatable' +(EmptyStruct(), 1, 2) == (EmptyStruct(), 1, 2) +``` + +It's also important to note that this conformance does not take into account the tuple labels in consideration for equality. If both tuples have the same element types, then they can be compared for equality. This mimics the current behavior of the operations introduced in [SE-0015](https://github.com/apple/swift-evolution/blob/master/proposals/0015-tuple-comparison-operators.md). + +```swift +// We don't take into account the labels for equality. +(x: 0, y: 0) == (0, 0) // true +``` + +### Comparable + +Comparable conformance for tuples works just like `Equatable`, if all the elements themselves are `Comparable`, then the tuple itself is `Comparable`. Comparing a tuple to a tuple works elementwise: + +> Look at the first element, if they are equal move to the second element. +Repeat until we find elements that are not equal and compare them. + +If all of the elements are equal, we cannot compare them, thus the result is `false`. Of course if we're using `<=` or `>=` and the tuples are exactly the same then the output would be `true`. + +```swift +let origin = (x: 0, y: 0) +let randomPoint = (x: Int.random(in: 1 ... 10), y: Int.random(in: 1 ... 10)) + +// In this case, the first element of origin is 0 and the first element +// of randomPoint is somewhere between 1 and 10, so they are not equal. +// origin's element is less than randomPoint's, thus true. +print(origin < randomPoint) // true +``` + +Just like in `Equatable`, the comparison operations do not take tuple labels into consideration when determining comparability. This mimics the current behavior of the operations introduced in [SE-0015](https://github.com/apple/swift-evolution/blob/master/proposals/0015-tuple-comparison-operators.md). + +```swift +// We don't take into account the labels for comparison. +(x: 0, y: 0) < (1, 0) // true +``` + +### Hashable + +The same rule applies to `Hashable` as it does for `Comparable` and `Equatable`, if all the elements are `Hashable` then the tuple itself is `Hashable`. When hashing a value of a tuple, all of the elements are combined into the hasher to produce the tuple's hash value. Now that tuples are `Hashable`, one can make a set of tuples or create dictionaries with tuple keys: + +```swift +let points = [(x: 0, y: 0), (x: 1, y: 2), (x: 0, y: 0)] +let uniquePoints = Set(points) + +// Create a grid system to hold game entities. +var grid = [(x: Int, y: Int): Entity]() + +for point in uniquePoints { + grid[point]?.move(up: 10) +} +``` + +Once again, `Hashable` doesn't take tuple element labels into consideration when evaluating the hash value of a tuple. Because of this, one is able to index into a set or dictionary with an unlabled tuple and retrieve elements whose keys were labeled tuples: + +```swift +// We don't take into account the labels for hash value. +(x: 0, y: 0).hashValue == (0, 0).hashValue // true + +grid[(x: 100, y: 200)] = Entity(name: "Pam") + +print(grid[(100, 200)]) // Entity(name: "Pam") +``` + +## Source compatibility + +These are completely new conformances to tuples, thus source compatibilty is unaffected as they were previously not able to conform to protocols. + +## Effect on ABI stability + +The conformances to `Equatable`, `Comparable`, and `Hashable` are all additive to the ABI. While at the time of writing this, there is no way to spell a new conformance to an existing type. However, these conformances are being implemented within the runtime which allows us to backward deploy these conformance to Swift 5.0, 5.1, and 5.2 clients. Because these are special conformances being added before other language features allows us to create real conformances, there is a level of runtime support needed to enable these conformances to work properly. Going forward this means we'll need to keep the entry points needed for these to work even after tuples are able to properly conform to protocols. + +## Alternatives considered + +Besides not doing this entirely, the only alternative here is whether or not we should hold off on this before we get proper protocol conformances for tuples which allow them to conform to any protocol. Doing this now requires a lot of builtin machinery in the compiler which some may refer to as technical debt. While I agree with this statement, I don't believe we should be holding off on features like this that many are naturally reaching for until bigger and more complex proposals that allow this feature to natively exist in Swift. I also believe it is none of the user's concern for what technical debt is added to the compiler that allows them to write the Swift code that they feel comfortable writing. In any case, the technical debt to be had here should only be the changes to the runtime (or at least the symbols needed) which allow this feature to work. + +## Future Directions + +With this change, other conformances such as `Codable` might make sense for tuples as well. It also makes sense to implement other conformances for other structural types in the language such as metatypes being `Hashable`, existentials being `Equatable` and `Hashable`, etc. + +In the future when we have proper tuple extensions along with variadic generics and such, implementing these conformances for tuples will be trivial and I imagine the standard library will come with these conformances for tuples. When that happens all future usage of those conformances will use the standard library's implementation, but older clients that have been compiled with this implementation will continue using it as normal. \ No newline at end of file From 024b6146f0ed51d46cd88c4a952c1694b8525383 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 2 Mar 2020 10:40:36 -0800 Subject: [PATCH 1412/4563] Update SE-57 to reflect reality The mechanism for ObjC classes to provide generic argument information to Swift was never implemented. --- proposals/0057-importing-objc-generics.md | 169 ++++------------------ 1 file changed, 29 insertions(+), 140 deletions(-) diff --git a/proposals/0057-importing-objc-generics.md b/proposals/0057-importing-objc-generics.md index 4d816aa8b3..2413747287 100644 --- a/proposals/0057-importing-objc-generics.md +++ b/proposals/0057-importing-objc-generics.md @@ -5,6 +5,8 @@ * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://lists.swift.org/pipermail/swift-evolution-announce/2016-April/000097.html) +* Previous Revision: [Originally Accepted Proposal](https://github.com/apple/swift-evolution/blob/3abbed3edd12dd21061181993df7952665d660dd/proposals/0057-importing-objc-generics.md) + ## Introduction @@ -51,13 +53,15 @@ requirements on the generic type parameters in Swift: The following Objective-C code: - @interface MySet> : NSObject - -(MySet *)unionWithSet:(MySet *)otherSet; - @end +``` +@interface MySet> : NSObject +-(MySet *)unionWithSet:(MySet *)otherSet; +@end - @interface MySomething : NSObject - - (MySet *)valueSet; - @end +@interface MySomething : NSObject +- (MySet *)valueSet; +@end +``` will be imported as: @@ -72,6 +76,7 @@ class MySomething : NSObject { ``` ### Importing unspecialized types + When importing an unspecialized Objective-C type into Swift, we will substitute the bounds for the type arguments. For example: @@ -131,143 +136,11 @@ share a metaclass). This leads to several restrictions: } ``` -### Opting in to type argument discovery - -Some Objective-C parameterized classes do carry information about -their type arguments. When this is the case, it is possible to lift -some of the restrictions described in the above section. There are two -distinct cases: - -* *Abstract parameterized classes whose concrete subclasses are not - parameterized*: - [`NSLayoutAnchor`](https://developer.apple.com/library/mac/documentation/AppKit/Reference/NSLayoutAnchor_ClassReference/index.html#//apple_ref/occ/cl/NSLayoutAnchor) - is one such example: it is parameterized on the anchor type, but - there is a fixed set of such anchor types that are represented by - subclasses: `NSLayoutXAxisAnchor` subclasses - `NSLayoutAnchor`, `NSLayoutDimension` - subclasses `NSLayoutAnchor`, etc. Therefore, - the type arguments can be recovered by looking at the actual - metaclass. - -* *Parameterized classes that store their type arguments in - instances*: - [`GKComponentSystem`](https://developer.apple.com/library/ios/documentation/GameplayKit/Reference/GKComponentSystem_Class/) - is one such example: it is parameterized on the component type it - stores, but it's initializer (`-initWithComponentClass:`) requires - one to pass the component type's metaclass. Therefore, every - *instance* of `GKComponentSystem` knows its type arguments. - -A parameterized Objective-C class can opt in to providing information -about its type argument by implementing a method -`classForGenericArgumentAtIndex:` either as a class method (for the first case -described above) or as an instance method (for the second case -described above). The method returns the metaclass for the type -argument at the given, zero-based index. - -For example, `NSLayoutAnchor` would provide a class method -`classForGenericArgumentAtIndex:` that must be implemented by each of its -subclasses: - - @interface NSLayoutAnchor (SwiftSupport) - /// Note: must be implemented by each subclass - +(nonnull Class)classForGenericArgumentAtIndex:(NSUInteger)index; - @end - - @implementation NSLayoutAnchor - +(nonnull Class)classForGenericArgumentAtIndex:(NSUInteger)index { - NSAssert(false, @"subclass must override +classForGenericArgumentAtIndex:"); - } - @end - - @implementation NSLayoutXAxisAnchor (SwiftSupport) - +(nonnull Class)classForGenericArgumentAtIndex:(NSUInteger)index { - return [NSLayoutXAxisAnchor class]; - } - @end - - @implementation NSLayoutYAxisAnchor (SwiftSupport) - +(nonnull Class)classForGenericArgumentAtIndex:(NSUInteger)index { - return [NSLayoutYAxisAnchor class]; - } - @end - - @implementation NSLayoutDimension (SwiftSupport) - +(nonnull Class)classForGenericArgumentAtIndex:(NSUInteger)index { - return [NSLayoutDimension class]; - } - @end - -On the other hand, `GKComponentSystem` would implement an instance -method `classForGenericArgumentAtIndex:`: - - @interface GKComponentSystem (SwiftSupport) - - (nonnull Class)classForGenericArgumentAtIndex:(NSUInteger)index; - @end - - @implementation GKComponentSystem (SwiftSupport) - - (nonnull Class)classForGenericArgumentAtIndex:(NSUInteger)index { - return self.componentClass; - } - @end - -Note that many parameterized Objective-C classes cannot provide either -of these methods, because they don't carry enough information in their -instances. For example, an `NSMutableArray` has no record of what the -element type of the array is intended to be. - -However, when a parameterized class does provide this information, we -can lift some of the restrictions from the previous section: - -* If the parameterized class provides an instance method - `classForGenericArgumentAtIndex:`, the extension can use the type arguments - in its instance methods, including accessors for instance properties - and subscripts. For example: - - ```swift - extension GKComponentSystem { - var reversedComponents: [ComponentType] { - return components.reversed() - } - - static func notifyComponents(components: [ComponentType]) { - // error: cannot use `ComponentType` in a static method - } - } - ``` - -* If the parametized class provides a class method - `classForGenericArgumentAtIndex:`, the extension can use type arguments - anywhere. - - ```swift - extension NSLayoutAnchor { - func doSomething(x: AnchorType) { ... } - class func doSomethingClassy(x: AnchorType) { ... } - } - ``` - ### Subclassing parameterized Objective-C classes from Swift When subclassing a parameterized Objective-C class from Swift, the -Swift compiler will define `+classForGenericArgumentAtIndex:` and -`-classForGenericArgumentAtIndex:`. The Swift compiler has the -complete type metadata required, because it is stored in the (Swift) -type metadata, so these definitions will be correct. For example: - -```swift -class Employee : NSObject { ... } - -class EmployeeArray : NSMutableArray { - // +[EmployeeArray classForGenericArgumentAtIndex:] always returns - // ObjC type metadata for Employee -} - -class MyMutableArray : NSMutableArray { - // +[MyMutableArray classForGenericArgumentAtIndex:] returns the - // ObjC type metadata for T, extracted from the Swift metatype for - // `self`. -} -``` +Swift compiler has the complete type metadata required, because it is +stored in the (Swift) type metadata. ## Impact on existing code @@ -314,3 +187,19 @@ conceptual burden as well as a high implementation cost. The proposed solution implies less implementation cost and puts the limitations on what one can express when working with parameterized Objective-C classes without fundamentally changing the Swift model. + +## Revision history + +The [originally accepted proposal](https://github.com/apple/swift-evolution/blob/3abbed3edd12dd21061181993df7952665d660dd/proposals/0057-importing-objc-generics.md) +included a mechanism by which Objective-C generic classes could implement +an informal protocol to provide reified generic arguments to Swift clients: + +> A parameterized Objective-C class can opt in to providing information +> about its type argument by implementing a method +> `classForGenericArgumentAtIndex:` either as a class method (for the first case +> described above) or as an instance method (for the second case +> described above). The method returns the metaclass for the type +> argument at the given, zero-based index. + +As of Swift 5, this feature has not been implemented, so it has been withdrawn +from the proposal. From 18cc3bf792fa850161bc9c85b0086e93678cf6d8 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 3 Mar 2020 02:21:17 +0000 Subject: [PATCH 1413/4563] [SE-0263] Update status: "Implemented (Swift 5.3)" --- proposals/0263-string-uninitialized-initializer.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0263-string-uninitialized-initializer.md b/proposals/0263-string-uninitialized-initializer.md index e0f7bed605..826f27abe9 100644 --- a/proposals/0263-string-uninitialized-initializer.md +++ b/proposals/0263-string-uninitialized-initializer.md @@ -3,8 +3,9 @@ * Proposal: [SE-0263](0263-string-uninitialized-initializer.md) * Author: [David Smith](https://github.com/Catfish-Man) * Review Manager: [Ted Kremenek](https://github.com/tkremenek) -* Status: **Accepted** -* Implementation: [apple/swift#23409](https://github.com/apple/swift/pull/23409) +* Status: **Implemented (Swift 5.3)** +* Implementation: [apple/swift#26007](https://github.com/apple/swift/pull/26007), + [apple/swift#30106](https://github.com/apple/swift/pull/30106) * Bug: [SR-10288](https://bugs.swift.org/browse/SR-10288) ## Introduction From 4500ae38bac33407157c4b38a7263a3498cd3c8a Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 2 Mar 2020 21:02:45 -0800 Subject: [PATCH 1414/4563] [Proposal] New Syntax for Multiple Trailing Closures (#1128) * [Proposal] New Syntax for Multiple Trailing Closures Swift currently supports a special syntax for a single trailing closure which makes it possible to pass a closure as function's final argument after parentheses as a block without a label. This is very useful when the closure expression is long. We propose to extend this functionality to cover multiple closures instead of just one. * Improve narrative flow for multiple trailing closures proposal * Fix some wacky whitespace * Apply suggestions from code review Co-Authored-By: Ben Rimmington * Assign number, kick off review --- proposals/0279-multiple-trailing-closures.md | 377 +++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100644 proposals/0279-multiple-trailing-closures.md diff --git a/proposals/0279-multiple-trailing-closures.md b/proposals/0279-multiple-trailing-closures.md new file mode 100644 index 0000000000..e4bfc68c16 --- /dev/null +++ b/proposals/0279-multiple-trailing-closures.md @@ -0,0 +1,377 @@ +# Multiple Trailing Closures + +* Proposal: [SE-0279](0279-multiple-trailing-closures.md) +* Authors: [Pavel Yaskevich](https://github.com/xedin), [Doug Gregor](https://github.com/douggregor) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Active Review (March 2–11)** +* Implementation: [apple/swift#29745](https://github.com/apple/swift/pull/29745) + +## Introduction + +Since its inception, Swift has supported _trailing closure_ syntax, which is a bit of syntactic sugar +that makes passing closures more ergonomic. Trailing closures have always had two restrictions that limited their +applicability. First, that any call is limited to a single trailing closure, making the feature awkward or even +unusable when an API has more than one callback. This limitation was noticed +[very early on in Swift's lifetime](https://www.natashatherobot.com/swift-trailing-closure-syntax/) as "the" +problem with trailing closure syntax. +Second, that a trailing closure argument does not provide an +argument label, which can lead to call sites that are less clear. This proposal removes both restrictions +by providing an unambiguous syntax for providing multiple, labeled trailing closures in a call, leading to +clearer and more consistent code. + +Swift-evolution thread: [Pitch: Multiple Trailing Closures](https://forums.swift.org/t/pitch-multiple-trailing-closures/33688) + +## Motivation + +Trailing closure syntax helps give more structure to call sites, separating "normal" function arguments, +which describe what a function should do, from "callback" function arguments, which provide user-specified +actions. Consider this example usage of a trailing closure with +[`UIView.animate(withDuration:animations:)`](https://developer.apple.com/documentation/uikit/uiview/1622418-animate), +from Paul Hudson's +"[What is trailing closure syntax?](https://www.hackingwithswift.com/example-code/language/what-is-trailing-closure-syntax)": + +```swift +UIView.animate(withDuration: 1) { [unowned self] in + self.view.backgroundColor = UIColor.red +} +``` + +This is equivalent to the "desugared" version of the call, which does not use trailing closure syntax: + +```swift +UIView.animate(withDuration: 1, animations: { [unowned self] in + self.view.backgroundColor = UIColor.red +}) +``` + +The version with trailing closure syntax separates out the "configuration" aspects of the animation +(its duration) from the "action" to be taken (update the background color). It also eliminates the unsightly +`})` that can become a major nuisance when nesting calls with callbacks +(example from [Anupam Chugh](https://www.journaldev.com/22104/ios-uiview-animations)): + +```swift +func toggle() { + UIView.animate(withDuration: 1, animations: { + self.myView.backgroundColor = UIColor.green + self.myView.frame.size.width += 50 + self.myView.frame.size.height += 20 + self.myView.center.x += 20 + }, completion: { _ in + UIView.animate(withDuration: 1, delay: 0.25, options: [.autoreverse, .repeat], animations: { + self.myView.frame.origin.y -= 20 + }) + }) +} +``` + +This example brings in more of the family of `UIView.animate` APIs that show the limits of trailing closure +syntax. It uses a related API +[`UIView.animate(withDuration:delay:options:animations:completion:)`](https://developer.apple.com/documentation/uikit/uiview/1622451-animate) +that also accepts a completion block. Let's consider that API in isolation, and try to use it +with a trailing closure: + +```swift +UIView.animate(withDuration: 0.7, delay: 1.0, options: .curveEaseOut, animations: { + self.view.layoutIfNeeded() +}) { finished in + print("Basket doors opened!") +} +``` + +It is not at all clear that the trailing closure here is meant to be the completion block, especially +because the other `UIView.animate` uses a trailing closure for `animations`. +Concerns about call-site confusion have led to rules about [not using trailing closures when there +are multiple parameters of function type](https://rules.sonarsource.com/swift/RSPEC-2958) and, indeed, +[Ehab Yosry Amer's tutorial on `UIView` animation](https://www.raywenderlich.com/5255-basic-uiview-animation-tutorial-getting-started), +where this code came from, avoids using trailing closures entirely: + +```swift +UIView.animate(withDuration: 0.7, delay: 1.0, options: .curveEaseOut, animations: { + self.view.layoutIfNeeded() +}, completion: { finished in + print("Basket doors opened!") +}) +``` + +This problem affects many APIs, because having multiple parameters of function type is fairly common. +SwiftUI's `Button`, for example contains an initializer +[`init(action:label:)`](https://developer.apple.com/documentation/swiftui/button/3283501-init): + +```swift +struct Button
    - +

    Swift Evolution

    From eed485d9fac87b7dce6475de87e26ec6ccc4eeb1 Mon Sep 17 00:00:00 2001 From: Behnam Mohammadi Date: Fri, 27 Mar 2020 17:17:40 +0430 Subject: [PATCH 1425/4563] reload page after click on title --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 123dcf4e56..808ed26cff 100644 --- a/index.html +++ b/index.html @@ -32,7 +32,7 @@
    -

    Swift Evolution

    +

    Swift Evolution

    + + + +### Complex string processing + +Even with the API additions, more complex string processing quickly becomes unwieldy. String processing in the modern world involves dealing with localization, standards-conforming validation, and other concerns for which a dedicated parser is required. + +Consider parsing the date field `"Date: Wed, 16 Feb 2022 23:53:19 GMT"` in an HTTP header as a `Date` type. The naive approach is to search for a substring that looks like a date string (`16 Feb 2022`), and attempt to post-process it as a `Date` with a date parser: + +```swift +let regex = Regex { + Capture { + OneOrMore(.digit) + " " + OneOrMore(.word) + " " + OneOrMore(.digit) + } +} + +let dateParser = Date.ParseStrategy(format: "\(day: .twoDigits) \(month: .abbreviated) \(year: .padded(4))" +if let dateMatch = header.firstMatch(of: regex)?.0 { + let date = try? Date(dateMatch, strategy: dateParser) +} +``` + +This requires writing a simplistic pre-parser before invoking the real parser. The pre-parser will suffer from being out-of-sync and less featureful than what the real parser can do. + +Or consider parsing a bank statement to record all the monetary values in the last column: + +```swift +let statement = """ +CREDIT 04/06/2020 Paypal transfer $4.99 +CREDIT 04/03/2020 Payroll $69.73 +DEBIT 04/02/2020 ACH transfer ($38.25) +DEBIT 03/24/2020 IRX tax payment ($52,249.98) +""" +``` + +Parsing a currency string such as `$3,020.85` with regex is also tricky, as it can contain localized and currency symbols in addition to accounting conventions. This is why Foundation provides industrial-strength parsers for localized strings. + + +## Proposed solution + +### Complex string processing + +We propose a `CustomConsumingRegexComponent` protocol which allows types from outside the standard library participate in regex builders and `RegexComponent` algorithms. This allows types, such as `Date.ParseStrategy` and `FloatingPointFormatStyle.Currency`, to be used directly within a regex: + +```swift +let dateRegex = Regex { + Capture(dateParser) +} + +let date: Date = header.firstMatch(of: dateRegex).map(\.result.1) + +let currencyRegex = Regex { + Capture(.localizedCurrency(code: "USD").sign(strategy: .accounting)) +} + +let amount: [Decimal] = statement.matches(of: currencyRegex).map(\.result.1) +``` + +### String algorithm additions + +We also propose the following regex-powered algorithms as well as their generic `Collection` equivalents. See the Detailed design section for a complete list of variation and overloads . + +|Function | Description | +|--- |--- | +|`contains(_:) -> Bool` | Returns whether the collection contains the given sequence or `RegexComponent` | +|`starts(with:) -> Bool` | Returns whether the collection contains the same prefix as the specified `RegexComponent` | +|`trimPrefix(_:)`| Removes the prefix if it matches the given `RegexComponent` or collection | +|`firstRange(of:) -> Range?` | Finds the range of the first occurrence of a given sequence or `RegexComponent`| +|`ranges(of:) -> some Collection` | Finds the ranges of the all occurrences of a given sequence or `RegexComponent` within the collection | +|`replace(:with:subrange:maxReplacements)`| Replaces all occurrences of the sequence matching the given `RegexComponent` or sequence with a given collection | +|`split(by:)`| Returns the longest possible subsequences of the collection around elements equal to the given separator | +|`firstMatch(of:)`| Returns the first match of the specified `RegexComponent` within the collection | +|`wholeMatch(of:)`| Matches the specified `RegexComponent` in the collection as a whole | +|`prefixMatch(of:)`| Matches the specified `RegexComponent` against the collection at the beginning | +|`matches(of:)`| Returns a collection containing all matches of the specified `RegexComponent` | + +We also propose an overload of `~=` allowing regexes to be used in `case` expressions: + +```swift + switch "abcde" { + case /a.*f/: // never taken + case /abc/: // never taken + case /ab.*e/: return "success" + default: // never taken + } + + switch "2022-04-22" { + case decimalParser: // never taken + + case OneOrMore { + CharacterClass.whitespace + }: // never taken + + case #/\d{2}/\d{2}/\d{4}/# // never taken + + case dateParser: return "success" + + default: // never taken + } +``` + + +## Detailed design + +### `CustomConsumingRegexComponent` + +`CustomConsumingRegexComponent` inherits from `RegexComponent` and satisfies its sole requirement. Conformers can be used with all of the string algorithms generic over `RegexComponent`. + +```swift +/// A protocol allowing custom types to function as regex components by +/// providing the raw functionality backing `prefixMatch`. +public protocol CustomConsumingRegexComponent: RegexComponent { + /// Process the input string within the specified bounds, beginning at the given index, and return + /// the end position (upper bound) of the match and the produced output. + /// - Parameters: + /// - input: The string in which the match is performed. + /// - index: An index of `input` at which to begin matching. + /// - bounds: The bounds in `input` in which the match is performed. + /// - Returns: The upper bound where the match terminates and a matched instance, or `nil` if + /// there isn't a match. + func consuming( + _ input: String, + startingAt index: String.Index, + in bounds: Range + ) throws -> (upperBound: String.Index, match: Match)? +} +``` + +
    +Example for protocol conformance + +We use Foundation `FloatingPointFormatStyle.Currency` as an example for protocol conformance. It would implement the `match` function with `Match` being a `Decimal`. It could also add a static function `.localizedCurrency(code:)` as a member of `RegexComponent`, so it can be referred as `.localizedCurrency(code:)` in the `Regex` result builder: + +```swift +extension FloatingPointFormatStyle.Currency : CustomConsumingRegexComponent { + public func consuming( + _ input: String, + startingAt index: String.Index, + in bounds: Range + ) -> (upperBound: String.Index, match: Decimal)? +} + +extension RegexComponent where Self == FloatingPointFormatStyle.Currency { + public static func localizedCurrency(code: Locale.Currency) -> Self +} +``` + +Matching and extracting a localized currency amount, such as `"$3,020.85"`, can be done directly within a regex: + +```swift +let regex = Regex { + Capture(.localizedCurrency(code: "USD")) +} +``` + +
    + + +### String and Collection algorithm additions + +#### Contains + +We propose a `contains` variant over collections that tests for subsequence membership. The second algorithm allows for specialization using e.g. the [two way search algorithm](https://en.wikipedia.org/wiki/Two-way_string-matching_algorithm). + +```swift +extension Collection where Element: Equatable { + /// Returns a Boolean value indicating whether the collection contains the + /// given sequence. + /// - Parameter other: A sequence to search for within this collection. + /// - Returns: `true` if the collection contains the specified sequence, + /// otherwise `false`. + public func contains(_ other: C) -> Bool + where S.Element == Element +} +extension BidirectionalCollection where Element: Comparable { + /// Returns a Boolean value indicating whether the collection contains the + /// given sequence. + /// - Parameter other: A sequence to search for within this collection. + /// - Returns: `true` if the collection contains the specified sequence, + /// otherwise `false`. + public func contains(_ other: C) -> Bool + where S.Element == Element +} +``` + +We propose a regex-taking variant over string types (those that produce a `Substring` upon slicing). + +```swift +extension Collection where SubSequence == Substring { + /// Returns a Boolean value indicating whether the collection contains the + /// given regex. + /// - Parameter regex: A regex to search for within this collection. + /// - Returns: `true` if the regex was found in the collection, otherwise + /// `false`. + public func contains(_ regex: some RegexComponent) -> Bool +} + +// In RegexBuilder module +extension Collection where SubSequence == Substring { + /// Returns a Boolean value indicating whether this collection contains a + /// match for the regex, where the regex is created by the given closure. + /// + /// - Parameter content: A closure that returns a regex to search for within + /// this collection. + /// - Returns: `true` if the regex returned by `content` matched anywhere in + /// this collection, otherwise `false`. + public func contains( + @RegexComponentBuilder _ content: () -> some RegexComponent + ) -> Bool +} +``` + +#### Starts with + +We propose a regex-taking `starts(with:)` variant for string types: + +```swift +extension Collection where SubSequence == Substring { + /// Returns a Boolean value indicating whether the initial elements of the + /// sequence are the same as the elements in the specified regex. + /// - Parameter regex: A regex to compare to this sequence. + /// - Returns: `true` if the initial elements of the sequence matches the + /// beginning of `regex`; otherwise, `false`. + public func starts(with regex: some RegexComponent) -> Bool +} + +// In RegexBuilder module +extension Collection where SubSequence == Substring { + /// Returns a Boolean value indicating whether the initial elements of this + /// collection are a match for the regex created by the given closure. + /// + /// - Parameter content: A closure that returns a regex to match at + /// the beginning of this collection. + /// - Returns: `true` if the initial elements of this collection match + /// regex returned by `content`; otherwise, `false`. + public func starts( + @RegexComponentBuilder with content: () -> some RegexComponent + ) -> Bool +} +``` + +#### Trim prefix + +We propose generic `trimmingPrefix` and `trimPrefix` methods for collections that trim elements matching a predicate or a possible prefix sequence. + +```swift +extension Collection { + /// Returns a new collection of the same type by removing initial elements + /// that satisfy the given predicate from the start. + /// - Parameter predicate: A closure that takes an element of the sequence + /// as its argument and returns a Boolean value indicating whether the + /// element should be removed from the collection. + /// - Returns: A collection containing the elements of the collection that are + /// not removed by `predicate`. + public func trimmingPrefix(while predicate: (Element) throws -> Bool) rethrows -> SubSequence +} + +extension Collection where SubSequence == Self { + /// Removes the initial elements that satisfy the given predicate from the + /// start of the sequence. + /// - Parameter predicate: A closure that takes an element of the sequence + /// as its argument and returns a Boolean value indicating whether the + /// element should be removed from the collection. + public mutating func trimPrefix(while predicate: (Element) throws -> Bool) rethrows +} + +extension RangeReplaceableCollection { + /// Removes the initial elements that satisfy the given predicate from the + /// start of the sequence. + /// - Parameter predicate: A closure that takes an element of the sequence + /// as its argument and returns a Boolean value indicating whether the + /// element should be removed from the collection. + public mutating func trimPrefix(while predicate: (Element) throws -> Bool) rethrows +} + +extension Collection where Element: Equatable { + /// Returns a new collection of the same type by removing `prefix` from the + /// start. + /// - Parameter prefix: The collection to remove from this collection. + /// - Returns: A collection containing the elements that does not match + /// `prefix` from the start. + public func trimmingPrefix(_ prefix: Prefix) -> SubSequence + where Prefix.Element == Element +} + +extension Collection where SubSequence == Self, Element: Equatable { + /// Removes the initial elements that matches `prefix` from the start. + /// - Parameter prefix: The collection to remove from this collection. + public mutating func trimPrefix(_ prefix: Prefix) + where Prefix.Element == Element +} + +extension RangeReplaceableCollection where Element: Equatable { + /// Removes the initial elements that matches `prefix` from the start. + /// - Parameter prefix: The collection to remove from this collection. + public mutating func trimPrefix(_ prefix: Prefix) + where Prefix.Element == Element +} +``` + +We propose regex-taking variants for string types: + +```swift +extension Collection where SubSequence == Substring { + /// Returns a new subsequence by removing the initial elements that matches + /// the given regex. + /// - Parameter regex: The regex to remove from this collection. + /// - Returns: A new subsequence containing the elements of the collection + /// that does not match `prefix` from the start. + public func trimmingPrefix(_ regex: some RegexComponent) -> SubSequence +} + +// In RegexBuilder module +extension Collection where SubSequence == Substring { + /// Returns a subsequence of this collection by removing the elements + /// matching the regex from the start, where the regex is created by + /// the given closure. + /// + /// - Parameter content: A closure that returns the regex to search for at + /// the start of this collection. + /// - Returns: A collection containing the elements after those that match + /// the regex returned by `content`. If the regex does not match at + /// the start of the collection, the entire contents of this collection + /// are returned. + public func trimmingPrefix( + @RegexComponentBuilder _ content: () -> some RegexComponent + ) -> SubSequence +} + +extension RangeReplaceableCollection where SubSequence == Substring { + /// Removes the initial elements that matches the given regex. + /// - Parameter regex: The regex to remove from this collection. + public mutating func trimPrefix(_ regex: some RegexComponent) +} + +// In RegexBuilder module +extension RangeReplaceableCollection where SubSequence == Substring { + /// Removes the initial elements matching the regex from the start of + /// this collection, if the initial elements match, using the given closure + /// to create the regex. + /// + /// - Parameter content: A closure that returns the regex to search for + /// at the start of this collection. + public mutating func trimPrefix( + @RegexComponentBuilder _ content: () -> some RegexComponent + ) +} +``` + +#### First range + +We propose a generic collection algorithm for finding the first range of a given subsequence: + +```swift +extension Collection where Element: Equatable { + /// Finds and returns the range of the first occurrence of a given sequence + /// within the collection. + /// - Parameter sequence: The sequence to search for. + /// - Returns: A range in the collection of the first occurrence of `sequence`. + /// Returns nil if `sequence` is not found. + public func firstRange(of other: C) -> Range? + where C.Element == Element +} + +extension BidirectionalCollection where Element: Comparable { + /// Finds and returns the range of the first occurrence of a given sequence + /// within the collection. + /// - Parameter other: The sequence to search for. + /// - Returns: A range in the collection of the first occurrence of `sequence`. + /// Returns `nil` if `sequence` is not found. + public func firstRange(of other: C) -> Range? + where C.Element == Element +} +``` + +We propose a regex-taking variant for string types. + +```swift +extension Collection where SubSequence == Substring { + /// Finds and returns the range of the first occurrence of a given regex + /// within the collection. + /// - Parameter regex: The regex to search for. + /// - Returns: A range in the collection of the first occurrence of `regex`. + /// Returns `nil` if `regex` is not found. + public func firstRange(of regex: some RegexComponent) -> Range? +} + +// In RegexBuilder module +extension Collection where SubSequence == Substring { + /// Returns the range of the first match for the regex within this collection, + /// where the regex is created by the given closure. + /// + /// - Parameter content: A closure that returns a regex to search for. + /// - Returns: A range in the collection of the first occurrence of the first + /// match of if the regex returned by `content`. Returns `nil` if no match + /// for the regex is found. + public func firstRange( + @RegexComponentBuilder of content: () -> some RegexComponent + ) -> Range? +} +``` + +#### Ranges + +We propose a generic collection algorithm for iterating over all (non-overlapping) ranges of a given subsequence. + +```swift +extension Collection where Element: Equatable { + /// Finds and returns the ranges of the all occurrences of a given sequence + /// within the collection. + /// - Parameter other: The sequence to search for. + /// - Returns: A collection of ranges of all occurrences of `other`. Returns + /// an empty collection if `other` is not found. + public func ranges(of other: C) -> some Collection> + where C.Element == Element +} + +extension BidirectionalCollection where Element: Comparable { + /// Finds and returns the ranges of the all occurrences of a given sequence + /// within the collection. + /// - Parameter other: The sequence to search for. + /// - Returns: A collection of ranges of all occurrences of `other`. Returns + /// an empty collection if `other` is not found. + public func ranges(of other: C) -> some Collection> + where C.Element == Element +} +``` + +And of course regex-taking versions for string types: + +```swift +extension Collection where SubSequence == Substring { + /// Finds and returns the ranges of the all occurrences of a given sequence + /// within the collection. + /// - Parameter regex: The regex to search for. + /// - Returns: A collection or ranges in the receiver of all occurrences of + /// `regex`. Returns an empty collection if `regex` is not found. + public func ranges(of regex: some RegexComponent) -> some Collection> +} + +// In RegexBuilder module +extension Collection where SubSequence == Substring { + /// Returns the ranges of the all non-overlapping matches for the regex + /// within this collection, where the regex is created by the given closure. + /// + /// - Parameter content: A closure that returns a regex to search for. + /// - Returns: A collection of ranges of all matches for the regex returned by + /// `content`. Returns an empty collection if no match for the regex + /// is found. + public func ranges( + @RegexComponentBuilder of content: () -> some RegexComponent + ) -> some Collection> +} +``` + +#### Match + +We propose algorithms for extracting a `Match` instance from a given regex from the start, anywhere in the middle, or over the entire `self`. + +```swift +extension Collection where SubSequence == Substring { + /// Returns the first match of the specified regex within the collection. + /// - Parameter regex: The regex to search for. + /// - Returns: The first match of `regex` in the collection, or `nil` if + /// there isn't a match. + public func firstMatch(of regex: R) -> Regex.Match? + + /// Match a regex in its entirety. + /// - Parameter regex: The regex to match against. + /// - Returns: The match if there is one, or `nil` if none. + public func wholeMatch(of regex: R) -> Regex.Match? + + /// Match part of the regex, starting at the beginning. + /// - Parameter regex: The regex to match against. + /// - Returns: The match if there is one, or `nil` if none. + public func prefixMatch(of regex: R) -> Regex.Match? +} + +// In RegexBuilder module +extension Collection where SubSequence == Substring { + /// Returns the first match for the regex within this collection, where + /// the regex is created by the given closure. + /// + /// - Parameter content: A closure that returns the regex to search for. + /// - Returns: The first match for the regex created by `content` in this + /// collection, or `nil` if no match is found. + public func firstMatch( + @RegexComponentBuilder of content: () -> R + ) -> Regex.Match? + + /// Matches a regex in its entirety, where the regex is created by + /// the given closure. + /// + /// - Parameter content: A closure that returns a regex to match against. + /// - Returns: The match if there is one, or `nil` if none. + public func wholeMatch( + @RegexComponentBuilder of content: () -> R + ) -> Regex.Match? + + /// Matches part of the regex, starting at the beginning, where the regex + /// is created by the given closure. + /// + /// - Parameter content: A closure that returns a regex to match against. + /// - Returns: The match if there is one, or `nil` if none. + public func prefixMatch( + @RegexComponentBuilder of content: () -> R + ) -> Regex.Match? +} +``` + +#### Matches + +We propose an algorithm for iterating over all (non-overlapping) matches of a given regex: + +```swift +extension Collection where SubSequence == Substring { + /// Returns a collection containing all matches of the specified regex. + /// - Parameter regex: The regex to search for. + /// - Returns: A collection of matches of `regex`. + public func matches(of regex: R) -> some Collection.Match> +} + +// In RegexBuilder module +extension Collection where SubSequence == Substring { + /// Returns a collection containing all non-overlapping matches of + /// the regex, created by the given closure. + /// + /// - Parameter content: A closure that returns the regex to search for. + /// - Returns: A collection of matches for the regex returned by `content`. + /// If no matches are found, the returned collection is empty. + public func matches( + @RegexComponentBuilder of content: () -> R + ) -> some Collection.Match> +} +``` + +#### Replace + +We propose generic collection algorithms that will replace all occurences of a given subsequence: + +```swift +extension RangeReplaceableCollection where Element: Equatable { + /// Returns a new collection in which all occurrences of a target sequence + /// are replaced by another collection. + /// - Parameters: + /// - other: The sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - subrange: The range in the collection in which to search for `other`. + /// - maxReplacements: A number specifying how many occurrences of `other` + /// to replace. Default is `Int.max`. + /// - Returns: A new collection in which all occurrences of `other` in + /// `subrange` of the collection are replaced by `replacement`. + public func replacing( + _ other: C, + with replacement: Replacement, + subrange: Range, + maxReplacements: Int = .max + ) -> Self where C.Element == Element, Replacement.Element == Element + + /// Returns a new collection in which all occurrences of a target sequence + /// are replaced by another collection. + /// - Parameters: + /// - other: The sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - maxReplacements: A number specifying how many occurrences of `other` + /// to replace. Default is `Int.max`. + /// - Returns: A new collection in which all occurrences of `other` in + /// `subrange` of the collection are replaced by `replacement`. + public func replacing( + _ other: C, + with replacement: Replacement, + maxReplacements: Int = .max + ) -> Self where C.Element == Element, Replacement.Element == Element + + /// Replaces all occurrences of a target sequence with a given collection + /// - Parameters: + /// - other: The sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - maxReplacements: A number specifying how many occurrences of `other` + /// to replace. Default is `Int.max`. + public mutating func replace( + _ other: C, + with replacement: Replacement, + maxReplacements: Int = .max + ) where C.Element == Element, Replacement.Element == Element +} +extension RangeReplaceableCollection where Self: BidirectionalCollection, Element: Comparable { + /// Returns a new collection in which all occurrences of a target sequence + /// are replaced by another collection. + /// - Parameters: + /// - other: The sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - subrange: The range in the collection in which to search for `other`. + /// - maxReplacements: A number specifying how many occurrences of `other` + /// to replace. Default is `Int.max`. + /// - Returns: A new collection in which all occurrences of `other` in + /// `subrange` of the collection are replaced by `replacement`. + public func replacing( + _ other: C, + with replacement: Replacement, + subrange: Range, + maxReplacements: Int = .max + ) -> Self where C.Element == Element, Replacement.Element == Element + + /// Returns a new collection in which all occurrences of a target sequence + /// are replaced by another collection. + /// - Parameters: + /// - other: The sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - maxReplacements: A number specifying how many occurrences of `other` + /// to replace. Default is `Int.max`. + /// - Returns: A new collection in which all occurrences of `other` in + /// `subrange` of the collection are replaced by `replacement`. + public func replacing( + _ other: C, + with replacement: Replacement, + maxReplacements: Int = .max + ) -> Self where C.Element == Element, Replacement.Element == Element + + /// Replaces all occurrences of a target sequence with a given collection + /// - Parameters: + /// - other: The sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - maxReplacements: A number specifying how many occurrences of `other` + /// to replace. Default is `Int.max`. + public mutating func replace( + _ other: C, + with replacement: Replacement, + maxReplacements: Int = .max + ) where C.Element == Element, Replacement.Element == Element +} +``` + +We propose regex-taking variants for string types as well as variants that take a closure which will generate the replacement portion from a regex match (e.g. by reading captures). + +```swift +extension RangeReplaceableCollection where SubSequence == Substring { + /// Returns a new collection in which all occurrences of a sequence matching + /// the given regex are replaced by another collection. + /// - Parameters: + /// - regex: A regex describing the sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - subrange: The range in the collection in which to search for `regex`. + /// - maxReplacements: A number specifying how many occurrences of the + /// sequence matching `regex` to replace. Default is `Int.max`. + /// - Returns: A new collection in which all occurrences of subsequence + /// matching `regex` in `subrange` are replaced by `replacement`. + public func replacing( + _ r: some RegexComponent, + with replacement: Replacement, + subrange: Range, + maxReplacements: Int = .max + ) -> Self where Replacement.Element == Element + + /// Returns a new collection in which all occurrences of a sequence matching + /// the given regex are replaced by another collection. + /// - Parameters: + /// - regex: A regex describing the sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - maxReplacements: A number specifying how many occurrences of the + /// sequence matching `regex` to replace. Default is `Int.max`. + /// - Returns: A new collection in which all occurrences of subsequence + /// matching `regex` are replaced by `replacement`. + public func replacing( + _ r: some RegexComponent, + with replacement: Replacement, + maxReplacements: Int = .max + ) -> Self where Replacement.Element == Element + + /// Replaces all occurrences of the sequence matching the given regex with + /// a given collection. + /// - Parameters: + /// - regex: A regex describing the sequence to replace. + /// - replacement: The new elements to add to the collection. + /// - maxReplacements: A number specifying how many occurrences of the + /// sequence matching `regex` to replace. Default is `Int.max`. + public mutating func replace( + _ r: some RegexComponent, + with replacement: Replacement, + maxReplacements: Int = .max + ) where Replacement.Element == Element + + /// Returns a new collection in which all occurrences of a sequence matching + /// the given regex are replaced by another regex match. + /// - Parameters: + /// - regex: A regex describing the sequence to replace. + /// - subrange: The range in the collection in which to search for `regex`. + /// - maxReplacements: A number specifying how many occurrences of the + /// sequence matching `regex` to replace. Default is `Int.max`. + /// - replacement: A closure that receives the full match information, + /// including captures, and returns a replacement collection. + /// - Returns: A new collection in which all occurrences of subsequence + /// matching `regex` are replaced by `replacement`. + public func replacing( + _ regex: R, + subrange: Range, + maxReplacements: Int = .max, + with replacement: (Regex.Match) throws -> Replacement + ) rethrows -> Self where Replacement.Element == Element + + /// Returns a new collection in which all occurrences of a sequence matching + /// the given regex are replaced by another collection. + /// - Parameters: + /// - regex: A regex describing the sequence to replace. + /// - maxReplacements: A number specifying how many occurrences of the + /// sequence matching `regex` to replace. Default is `Int.max`. + /// - replacement: A closure that receives the full match information, + /// including captures, and returns a replacement collection. + /// - Returns: A new collection in which all occurrences of subsequence + /// matching `regex` are replaced by `replacement`. + public func replacing( + _ regex: R, + maxReplacements: Int = .max, + with replacement: (Regex.Match) throws -> Replacement + ) rethrows -> Self where Replacement.Element == Element + + /// Replaces all occurrences of the sequence matching the given regex with + /// a given collection. + /// - Parameters: + /// - regex: A regex describing the sequence to replace. + /// - maxReplacements: A number specifying how many occurrences of the + /// sequence matching `regex` to replace. Default is `Int.max`. + /// - replacement: A closure that receives the full match information, + /// including captures, and returns a replacement collection. + public mutating func replace( + _ regex: R, + maxReplacements: Int = .max, + with replacement: (Regex.Match) throws -> Replacement + ) rethrows where Replacement.Element == Element +} + +// In RegexBuilder module +extension Collection where SubSequence == Substring { + /// Returns a new collection in which all matches for the regex + /// are replaced, using the given closure to create the regex. + /// + /// - Parameters: + /// - replacement: The new elements to add to the collection in place of + /// each match for the regex, using `content` to create the regex. + /// - subrange: The range in the collection in which to search for + /// the regex. + /// - maxReplacements: A number specifying how many occurrences of + /// the regex to replace. + /// - content: A closure that returns the collection to search for + /// and replace. + /// - Returns: A new collection in which all matches for regex in `subrange` + /// are replaced by `replacement`, using `content` to create the regex. + public func replacing( + with replacement: Replacement, + subrange: Range, + maxReplacements: Int = .max, + @RegexComponentBuilder content: () -> some RegexComponent + ) -> Self where Replacement.Element == Element + + /// Returns a new collection in which all matches for the regex + /// are replaced, using the given closure to create the regex. + /// + /// - Parameters: + /// - replacement: The new elements to add to the collection in place of + /// each match for the regex, using `content` to create the regex. + /// - maxReplacements: A number specifying how many occurrences of regex + /// to replace. + /// - content: A closure that returns the collection to search for + /// and replace. + /// - Returns: A new collection in which all matches for regex in `subrange` + /// are replaced by `replacement`, using `content` to create the regex. + public func replacing( + with replacement: Replacement, + maxReplacements: Int = .max, + @RegexComponentBuilder content: () -> some RegexComponent + ) -> Self where Replacement.Element == Element + + /// Replaces all matches for the regex in this collection, using the given + /// closure to create the regex. + /// + /// - Parameters: + /// - replacement: The new elements to add to the collection in place of + /// each match for the regex, using `content` to create the regex. + /// - maxReplacements: A number specifying how many occurrences of + /// the regex to replace. + /// - content: A closure that returns the collection to search for + /// and replace. + public mutating func replace( + with replacement: Replacement, + maxReplacements: Int = .max, + @RegexComponentBuilder content: () -> some RegexComponent + ) where Replacement.Element == Element + + /// Returns a new collection in which all matches for the regex + /// are replaced, using the given closures to create the replacement + /// and the regex. + /// + /// - Parameters: + /// - subrange: The range in the collection in which to search for the + /// regex, using `content` to create the regex. + /// - maxReplacements: A number specifying how many occurrences of + /// the regex to replace. + /// - content: A closure that returns the collection to search for + /// and replace. + /// - replacement: A closure that receives the full match information, + /// including captures, and returns a replacement collection. + /// - Returns: A new collection in which all matches for regex in `subrange` + /// are replaced by the result of calling `replacement`, where regex + /// is the result of calling `content`. + public func replacing( + subrange: Range, + maxReplacements: Int = .max, + @RegexComponentBuilder content: () -> R, + with replacement: (Regex.Match) throws -> Replacement + ) rethrows -> Self where Replacement.Element == Element + + /// Returns a new collection in which all matches for the regex + /// are replaced, using the given closures to create the replacement + /// and the regex. + /// + /// - Parameters: + /// - maxReplacements: A number specifying how many occurrences of + /// the regex to replace, using `content` to create the regex. + /// - content: A closure that returns the collection to search for + /// and replace. + /// - replacement: A closure that receives the full match information, + /// including captures, and returns a replacement collection. + /// - Returns: A new collection in which all matches for regex in `subrange` + /// are replaced by the result of calling `replacement`, where regex is + /// the result of calling `content`. + public func replacing( + maxReplacements: Int = .max, + @RegexComponentBuilder content: () -> R, + with replacement: (Regex.Match) throws -> Replacement + ) rethrows -> Self where Replacement.Element == Element + + /// Replaces all matches for the regex in this collection, using the + /// given closures to create the replacement and the regex. + /// + /// - Parameters: + /// - maxReplacements: A number specifying how many occurrences of + /// the regex to replace, using `content` to create the regex. + /// - content: A closure that returns the collection to search for + /// and replace. + /// - replacement: A closure that receives the full match information, + /// including captures, and returns a replacement collection. + public mutating func replace( + maxReplacements: Int = .max, + @RegexComponentBuilder content: () -> R, + with replacement: (Regex.Match) throws -> Replacement + ) rethrows where Replacement.Element == Element +} +``` + +#### Split + +We propose a generic collection `split` that can take a subsequence separator: + +```swift +extension Collection where Element: Equatable { + /// Returns the longest possible subsequences of the collection, in order, + /// around elements equal to the given separator collection. + /// + /// - Parameters: + /// - separator: A collection of elements to be split upon. + /// - maxSplits: The maximum number of times to split the collection, + /// or one less than the number of subsequences to return. + /// - omittingEmptySubsequences: If `false`, an empty subsequence is + /// returned in the result for each consecutive pair of separator + /// sequences in the collection and for each instance of separator + /// sequences at the start or end of the collection. If `true`, only + /// nonempty subsequences are returned. + /// - Returns: A collection of subsequences, split from this collection's + /// elements. + public func split( + separator: C, + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true + ) -> some Collection where C.Element == Element +} +extension BidirectionalCollection where Element: Comparable { + /// Returns the longest possible subsequences of the collection, in order, + /// around elements equal to the given separator collection. + /// + /// - Parameters: + /// - separator: A collection of elements to be split upon. + /// - maxSplits: The maximum number of times to split the collection, + /// or one less than the number of subsequences to return. + /// - omittingEmptySubsequences: If `false`, an empty subsequence is + /// returned in the result for each consecutive pair of separator + /// sequences in the collection and for each instance of separator + /// sequences at the start or end of the collection. If `true`, only + /// nonempty subsequences are returned. + /// - Returns: A collection of subsequences, split from this collection's + /// elements. + public func split( + separator: C, + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true + ) -> some Collection where C.Element == Element +} +``` + +And a regex-taking variant for string types: + +```swift +extension Collection where SubSequence == Substring { + /// Returns the longest possible subsequences of the collection, in order, + /// around subsequence that match the given separator regex. + /// + /// - Parameters: + /// - separator: A regex to be split upon. + /// - maxSplits: The maximum number of times to split the collection, + /// or one less than the number of subsequences to return. + /// - omittingEmptySubsequences: If `false`, an empty subsequence is + /// returned in the result for each consecutive pair of matches + /// and for each match at the start or end of the collection. If + /// `true`, only nonempty subsequences are returned. + /// - Returns: A collection of substrings, split from this collection's + /// elements. + public func split( + separator: some RegexComponent, + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true + ) -> some Collection +} + +// In RegexBuilder module +extension Collection where SubSequence == Substring { + /// Returns the longest possible subsequences of the collection, in order, + /// around subsequence that match the regex created by the given closure. + /// + /// - Parameters: + /// - maxSplits: The maximum number of times to split the collection, + /// or one less than the number of subsequences to return. + /// - omittingEmptySubsequences: If `false`, an empty subsequence is + /// returned in the result for each consecutive pair of matches + /// and for each match at the start or end of the collection. If + /// `true`, only nonempty subsequences are returned. + /// - separator: A closure that returns a regex to be split upon. + /// - Returns: A collection of substrings, split from this collection's + /// elements. + public func split( + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true, + @RegexComponentBuilder separator: () -> some RegexComponent + ) -> some Collection +} +``` + +**Note:** We plan to adopt the new generics features enabled by [SE-0346][] for these proposed methods when the standard library adopts primary associated types, [pending a forthcoming proposal][stdlib-pitch]. For example, the first method in the _Replacement_ section above would instead be: + +```swift +extension RangeReplaceableCollection where Element: Equatable { + /// Returns a new collection in which all occurrences of a target sequence + /// are replaced by another collection. + public func replacing( + _ other: some Collection, + with replacement: some Collection, + subrange: Range, + maxReplacements: Int = .max + ) -> Self +} +``` + +#### Searching for empty strings and matches + +Empty matches and inputs are an important edge case for several of the algorithms proposed above. For example, what is the result of `"123.firstRange(of: /[a-z]*/)`? How do you split a collection separated by an empty collection, as in `"1234".split(separator: "")`? For the Swift standard library, this is a new consideration, as current algorithms are `Element`-based and cannot be passed an empty input. + +Languages and libraries are nearly unanimous about finding the location of an empty string, with Ruby, Python, C#, Java, Javascript, etc, finding an empty string at each index in the target. Notably, Foundation's `NSString.range(of:)` does _not_ find an empty string at all. + +The methods proposed here follow the consensus behavior, which makes sense if you think of `a.firstRange(of: b)` as returning the first subrange `r` where `a[r] == b`. If a regex can match an empty substring, like `/[a-z]*/`, the behavior is the same. + +```swift +let hello = "Hello" +let emptyRange = hello.firstRange(of: "") +// emptyRange is equivalent to '0..<0' (integer ranges shown for readability) +``` + +Because searching again at the same index would yield that same empty string, we advance one position after finding an empty string or matching an empty pattern when finding all ranges. This yields the position of every valid index in the string. + +```swift +let allRanges = hello.ranges(of: "") +// allRanges is equivalent to '[0..<0, 1..<1, 2..<2, 3..<3, 4..<4, 5..<5]' +``` + +Splitting with an empty separator (or a pattern that matches empty string), uses this same behavior, resulting in a collection of single-element substrings. Interestingly, a couple languages make different choices here. C# returns the original string instead of its parts, and Python rejects an empty separator (though it permits regexes that match empty strings). + +```swift +let parts = hello.split(separator: "") +// parts == ["h", "e", "l", "l", "o"] + +let moreParts = hello.split(separator: "", omittingEmptySubsequences: false) +// parts == ["", "h", "e", "l", "l", "o", ""] +``` + +Finally, searching for an empty string within an empty string yields, as you might imagine, the empty string: + +```swift +let empty = "" +let range = empty.firstRange(of: empty) +// empty == empty[range] +``` + +### Language-level pattern matching via `~=` + +We propose allowing any regex component be used in case statements by overloading the `~=` operator for matching against the entire input: + +```swift +extension RegexComponent { + public static func ~=(regex: Self, input: String) -> Bool + + public static func ~=(regex: Self, input: Substring) -> Bool +} +``` + +[SE-0346]: https://github.com/apple/swift-evolution/blob/main/proposals/0346-light-weight-same-type-syntax.md +[stdlib-pitch]: https://forums.swift.org/t/pitch-primary-associated-types-in-the-standard-library/56426 + +## Alternatives considered + +### Extend `Sequence` instead of `Collection` + +Most of the proposed algorithms are necessarily on `Collection` due to the use of indices or mutation. `Sequence` does not support multi-pass iteration, so even `trimmingPrefix` would problematic on `Sequence` because it needs to look one `Element` ahead to know when to stop trimming and would need to return a wrapper for the in-progress iterator instead of a subsequence. + +### Cross-proposal API naming consistency + +The regex work is broken down into 6 proposals based on technical domain, which is advantageous for deeper technical discussions and makes reviewing the large body of work manageable. The disadvantage of this approach is that relatively-shallow cross-cutting concerns, such as API naming consistency, are harder to evaluate until we've built up intuition from multiple proposals. + +We've seen the [Regex type and overview](https://github.com/apple/swift-evolution/blob/main/proposals/0350-regex-type-overview.md), the [Regex builder DSL](https://github.com/apple/swift-evolution/blob/main/proposals/0351-regex-builder.md), and here we present lots of ways to use regex. Now's a good time to go over API naming consistency. + +(The other proposal with a significant amount of API is [Unicode for String Processing](https://forums.swift.org/t/pitch-unicode-for-string-processing/56907), which is in the pitch phase. It is a technical niche and less impactful on these naming discussions. We'll still want to design those names for consistency, of course.) + + +```swift +protocol RegexComponent { + associatedtype RegexOutput +} +``` + +The associatedtype name is "RegexOutput" to help libraries conform their parsers to this protocol (e.g. via `CustomConsumingRegexComponent`). Regex's capture representation is regexy: it has the overall matched portion as the first capture and the regex builders know how to combine these kinds of capture lists together. This could be different than how e.g. a parser combinator library's output types might be represented. Thus, we chose a more specific name to avoid any potential conflicts. + +The name "RegexComponent" accentuates that any conformer can be used as part of a larger regex, while it de-emphasizes that `Regex` instances themselves can be used directly. We propose methods that are generic over `RegexComponent` and developers will be considering whether they should make their functions that otherwise take a `Regex` also be generic over `RegexComponent`. + +It's possible there might be some initial confusion around the word "component", i.e. a developer may have a regex and not be sure how to make it into a component or how to get the component out. The word "component" carries a lot of value in the context of the regex DSL. An alternative name might be `RegexProtocol`, which implies that a Regex can be used at the site and would be clearly the way to make a function taking a concrete `Regex` generic. But, it's otherwise a naming workaround that doesn't carry the additional regex builder connotations. + +The protocol requirement is `var regex: Regex`, i.e. any type that can produce a regex or hook into the engine's customization hooks (this is what `consuming` does) can be used as a component of the DSL and with these generic API. An alternative name could be "CustomRegexConvertible", but we don't feel that communicates component composability very well, nor is it particularly enlightening when encountering these generic API. + +Another alternative is to have a second protocol just for generic API. But without a compelling semantic distinction or practical utility, we'd prefer to avoid adding protocols just for names. If a clearly superior name exists, we should just choose that. + + +```swift +protocol CustomConsumingRegexComponent { + func consuming(...) +} +``` + +This is not a normal developer-facing protocol or concept; it's an advanced library-extensibility feature. Explicit, descriptive, and careful names are more important than concise names. The "custom" implies that we're not just vending a regex directly ourselves, we're instead customizing behavior by hooking into the run-time engine directly. + +Older versions of the pitch had `func match(...) -> (String.Index, T)?` as the protocol requirement. As [Regex type and overview](https://github.com/apple/swift-evolution/blob/main/proposals/0350-regex-type-overview.md) went through review, naming convention settled on using the word "match" as a noun and in context with operations that produce a `Match` instance. Since this is the engine's customization hook, it produces the value and position to resume execution from directly, and hence different terminology is apt and avoids confusion or future ambiguities. "Consuming" is the nomenclature we're going with for something that chews off the front of its input in order to produces a value. + +This protocol customizes the basic consume-from-the-front functionality. A protocol for customizing search is future work and involves accommodating different kinds of state and ways that a searcher may wish to speed up subsequent searches. Alternative names for the protocol include `CustomRegexComponent`, `CustomConsumingRegex`, etc., but we don't feel brevity is the key consideration here. + + +### Why `where SubSequence == Substring`? + +A `Substring` slice requirement allows the regex engine to produce indicies in the original collection by operating over a portion of the input. Unfortunately, this is not one of the requirements of `StringProtocol`. + +A new protocol for types that can produce a `Substring` on request (e.g. from UTF-8 contents) would have to eagerly produce a `String` copy first and would need requirements to translate indices. When higher-level algorithms are implemented via multiple calls to the lower-level algorithms, these copies could happen many times. Shared strings are future work but a much better solution to this. + + +### Matches-anywhere semantics for `~=` + +Some scripting languages use semantics akin to `contains` for this purpose. But in Swift, this would deviate from how switch normally behaves, such as when switching over an Array. + +Matches-anywhere can be perilous when the input is dynamic and unpredictable: new inputs might just happen to trigger an earlier case in non-obvious ways. Matches-anywhere can be opted into by quantifying around the regex, and a modifier on regex to make this more convenient is future work. + + +## Future directions + +### Backward algorithms + +It would be useful to have algorithms that operate from the back of a collection, including ability to find the last non-overlapping range of a pattern in a string, and/or that to find the first range of a pattern when searching from the back, and trimming a string from both sides. They are deferred from this proposal as the API that could clarify the nuances of backward algorithms are still being explored. + +
    + Nuances of backward algorithms + +There is a subtle difference between finding the last non-overlapping range of a pattern in a string, and finding the first range of this pattern when searching from the back. + +The currently proposed algorithm that finds a pattern from the front, e.g. `"aaaaa".ranges(of: "aa")`, produces two non-overlapping ranges, splitting the string in the chunks `aa|aa|a`. It would not be completely unreasonable to expect to introduce a counterpart, such as `"aaaaa".lastRange(of: "aa")`, to return the range that contains the third and fourth characters of the string. This would be a shorthand for `"aaaaa".ranges(of: "aa").last`. Yet, it would also be reasonable to expect the function to return the first range of `"aa"` when searching from the back of the string, i.e. the range that contains the fourth and fifth characters. + +Trimming a string from both sides shares a similar story. For example, `"ababa".trimming("aba")` can return either `"ba"` or `"ab"`, depending on whether the prefix or the suffix was trimmed first. +
    + +### Split preserving the separator + +Future work is a split variant that interweaves the separator with the separated portions. For example, when splitting over `\p{punctuation}` it might be useful to be able to preserve the punctionation as a separate entry in the returned collection. + +### Future API + +Some common string processing functions are not currently included in this proposal, such as trimming the suffix from a string/collection, and finding overlapping ranges of matched substrings. This pitch aims to establish a pattern for using `RegexComponent` with string processing algorithms, so that further enhancement can to be introduced to the standard library easily in the future, and eventually close the gap between Swift and other popular scripting languages. From f10c114b7c67325f043fb305680ab767898d1d57 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 9 May 2022 16:27:15 -0700 Subject: [PATCH 2542/4563] Update 0350-regex-type-overview.md --- proposals/0350-regex-type-overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0350-regex-type-overview.md b/proposals/0350-regex-type-overview.md index cc704ea934..26fd373a69 100644 --- a/proposals/0350-regex-type-overview.md +++ b/proposals/0350-regex-type-overview.md @@ -3,7 +3,7 @@ * Proposal: [SE-0350](0350-regex-type-overview.md) * Authors: [Michael Ilseman](https://github.com/milseman) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (4 - 28 April 2022)** +* Status: **Accepted** * Implementation: https://github.com/apple/swift-experimental-string-processing * Available in nightly toolchain snapshots with `import _StringProcessing` From 2c6be689b9ab899b2bc58d488d0026879ef2c18b Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Mon, 9 May 2022 18:22:50 -0700 Subject: [PATCH 2543/4563] Add primary associated types for distributed actor protocols Also, reorder protocol lists to match in the various sections. --- ...nnnn-primary-associated-types-in-stdlib.md | 125 ++++++++++-------- 1 file changed, 70 insertions(+), 55 deletions(-) diff --git a/proposals/nnnn-primary-associated-types-in-stdlib.md b/proposals/nnnn-primary-associated-types-in-stdlib.md index 07ceb6834e..99c65da692 100644 --- a/proposals/nnnn-primary-associated-types-in-stdlib.md +++ b/proposals/nnnn-primary-associated-types-in-stdlib.md @@ -7,8 +7,10 @@ * Implementation: [apple/swift#41843](https://github.com/apple/swift/pull/41843) * Review: ([pitch](https://forums.swift.org/t/pitch-primary-associated-types-in-the-standard-library/56426/)) * Related Proposals: + - [SE-0023] API Design Guidelines - [SE-0346] Lightweight same-type requirements for primary associated types - + +[SE-0023]: https://github.com/apple/swift-evolution/blob/main/proposals/0023-api-guidelines.md [SE-0346]: https://github.com/apple/swift-evolution/blob/main/proposals/0346-light-weight-same-type-syntax.md ## Introduction @@ -23,6 +25,8 @@ See [SE-0346] for several motivating examples for these changes. ## General API Design Guidelines +(The contents of this section are to be integrated into the Swift API Design Guidelines document, introduced in [SE-0023].) + Primary associated types add a new facet to the design of protocols. For every public protocol with associated type requirements, we need to carefully consider which of them (if any) we want to mark as primary. On the one hand, we want to allow people to use the shorthand syntax whenever possible; on the other hand, we only get one chance to decide this: once a protocol gains a primary associated type annotation, most subsequent changes would be source-breaking. 1. **Let usage inform your design.** @@ -85,76 +89,80 @@ associated type, as well as a list of other associated types. [note]: #alternatives-considered -| Protocol | Primary | Others | -|------------------------------------------------------|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Identifiable` | `ID` | -- | -| `Sequence` | `Element` | `Iterator` | -| `IteratorProtocol` | `Element` | -- | -| `Collection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | -| `MutableCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | -| `BidirectionalCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | -| `RandomAccessCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | -| `RangeReplaceableCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | -| `LazySequenceProtocol` | -- [(1)][note] | `Element`, `Iterator`, `Elements` | -| `LazyCollectionProtocol` | -- [(1)][note] | `Element`, `Index`, `Iterator`, `SubSequence`, `Indices`, `Elements` | -| `SetAlgebra` | `Element` | `ArrayLiteralElement` | -| `OptionSet` | `Element` | `ArrayLiteralElement`, `RawValue` | -| `RawRepresentable` | `RawValue` | -- | -| `RangeExpression` | `Bound` | -- | -| `Strideable` | `Stride` | -- | -| `Numeric` | -- | `IntegerLiteralType`, `Magnitude` | -| `SignedNumeric` | -- | `IntegerLiteralType`, `Magnitude` | -| `BinaryInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | -| `UnsignedInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | -| `SignedInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | -| `FixedWidthInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | -| `FloatingPoint` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Exponent` | -| `BinaryFloatingPoint` | -- | `IntegerLiteralType`, `FloatLiteralType`, `Magnitude`, `Stride`, `Exponent`, `RawSignificand`, `RawExponent` | -| `SIMD` | `Scalar` | `ArrayLiteralElement`, `MaskStorage` | -| `SIMDStorage` | -- | `Scalar` | -| `SIMDScalar` | -- | `SIMDMaskScalar`, `SIMD2Storage`, `SIMD4Storage`, ..., `SIMD64Storage` | -| `Clock` | `Duration` | `Instant` | -| `InstantProtocol` | `Duration` | -- | -| `AsyncIteratorProtocol` | -- [(2)][note] | `Element` | -| `AsyncSequence` | -- [(2)][note] | `AsyncIterator`, `Element` | -| `GlobalActor` | -- | `ActorType` | -| `KeyedEncodingContainerProtocol` | -- | `Key` | -| `KeyedDecodingContainerProtocol` | -- | `Key` | -| `ExpressibleByIntegerLiteral` | -- | `IntegerLiteralType` | -| `ExpressibleByFloatLiteral` | -- | `FloatLiteralType` | -| `ExpressibleByBooleanLiteral` | -- | `BooleanLiteralType` | -| `ExpressibleByUnicodeScalarLiteral` | -- | `UnicodeScalarLiteralType` | -| `ExpressibleByExtended-`
    `GraphemeClusterLiteral` | -- | `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType` | -| `ExpressibleByStringLiteral` | -- | `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType`, `StringLiteralType` | -| `ExpressibleByStringInterpolation` | -- | `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType`, `StringLiteralType`, `StringInterPolation` | -| `ExpressibleByArrayLiteral` | -- | `ArrayLiteralElement` | -| `ExpressibleByDictionaryLiteral` | -- | `Key`, `Value` | -| `StringInterpolationProtocol` | -- | `StringLiteralType` | -| `Unicode.Encoding` | -- | `CodeUnit`, `EncodedScalar`, `ForwardParser`, `ReverseParser` | -| `UnicodeCodec` | -- | `CodeUnit`, `EncodedScalar`, `ForwardParser`, `ReverseParser` | -| `Unicode.Parser` | -- | `Encoding` | -| `StringProtocol` | -- | `Element`, `Index`, `Iterator`, `SubSequence`, `Indices`, `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType`, `StringLiteralType`, `StringInterPolation`, `UTF8View`, `UTF16View`, `UnicodeScalarView` | -| `CaseIterable` | -- | `AllCases` | +| Protocol | Primary | Others | +|------------------------------------------------------|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Sequence` | `Element` | `Iterator` | +| `IteratorProtocol` | `Element` | -- | +| `Collection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | +| `MutableCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | +| `BidirectionalCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | +| `RandomAccessCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | +| `RangeReplaceableCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | +| `LazySequenceProtocol` | -- [(1)][note] | `Element`, `Iterator`, `Elements` | +| `LazyCollectionProtocol` | -- [(1)][note] | `Element`, `Index`, `Iterator`, `SubSequence`, `Indices`, `Elements` | +| `Identifiable` | `ID` | -- | +| `RawRepresentable` | `RawValue` | -- | +| `RangeExpression` | `Bound` | -- | +| `Strideable` | `Stride` | -- | +| `SetAlgebra` | `Element` | `ArrayLiteralElement` | +| `OptionSet` | `Element` | `ArrayLiteralElement`, `RawValue` | +| `Numeric` | -- | `IntegerLiteralType`, `Magnitude` | +| `SignedNumeric` | -- | `IntegerLiteralType`, `Magnitude` | +| `BinaryInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | +| `UnsignedInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | +| `SignedInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | +| `FixedWidthInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | +| `FloatingPoint` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Exponent` | +| `BinaryFloatingPoint` | -- | `IntegerLiteralType`, `FloatLiteralType`, `Magnitude`, `Stride`, `Exponent`, `RawSignificand`, `RawExponent` | +| `SIMD` | `Scalar` | `ArrayLiteralElement`, `MaskStorage` | +| `SIMDStorage` | -- | `Scalar` | +| `SIMDScalar` | -- | `SIMDMaskScalar`, `SIMD2Storage`, `SIMD4Storage`, ..., `SIMD64Storage` | +| `KeyedEncodingContainerProtocol` | -- | `Key` | +| `KeyedDecodingContainerProtocol` | -- | `Key` | +| `ExpressibleByIntegerLiteral` | -- | `IntegerLiteralType` | +| `ExpressibleByFloatLiteral` | -- | `FloatLiteralType` | +| `ExpressibleByBooleanLiteral` | -- | `BooleanLiteralType` | +| `ExpressibleByUnicodeScalarLiteral` | -- | `UnicodeScalarLiteralType` | +| `ExpressibleByExtended-`
    `GraphemeClusterLiteral` | -- | `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType` | +| `ExpressibleByStringLiteral` | -- | `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType`, `StringLiteralType` | +| `ExpressibleByStringInterpolation` | -- | `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType`, `StringLiteralType`, `StringInterPolation` | +| `ExpressibleByArrayLiteral` | -- | `ArrayLiteralElement` | +| `ExpressibleByDictionaryLiteral` | -- | `Key`, `Value` | +| `StringInterpolationProtocol` | -- | `StringLiteralType` | +| `Unicode.Encoding` | -- | `CodeUnit`, `EncodedScalar`, `ForwardParser`, `ReverseParser` | +| `UnicodeCodec` | -- | `CodeUnit`, `EncodedScalar`, `ForwardParser`, `ReverseParser` | +| `Unicode.Parser` | -- | `Encoding` | +| `StringProtocol` | -- | `Element`, `Index`, `Iterator`, `SubSequence`, `Indices`, `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType`, `StringLiteralType`, `StringInterPolation`, `UTF8View`, `UTF16View`, `UnicodeScalarView` | +| `CaseIterable` | -- | `AllCases` | +| `Clock` | `Duration` | `Instant` | +| `InstantProtocol` | `Duration` | -- | +| `AsyncIteratorProtocol` | -- [(2)][note] | `Element` | +| `AsyncSequence` | -- [(2)][note] | `AsyncIterator`, `Element` | +| `GlobalActor` | -- | `ActorType` | +| `DistributedActor` | `ActorSystem` | `ID`, `SerializationRequirement` | +| `DistributedActorSystem` | `SerializationRequirement` | `ActorID`, `InvocationEncoder`, `InvocationDecoder`, `ResultHandler` | +| `DistributedTargetInvocationEncoder` | `SerializationRequirement` | | +| `DistributedTargetInvocationDecoder` | `SerializationRequirement` | | +| `DistributedTargetInvocationResultHandler` | `SerializationRequirement` | | As of Swift 5.6, the following public protocols don't have associated type requirements, so they are outside of the scope of this proposal. ```swift Equatable, Hashable, Comparable, Error, AdditiveArithmetic, -DurationProtocol, Sendable, UnsafeSendable, Actor, AnyActor, Executor, -SerialExecutor, Encodable, Decodable, Encoder, Decoder, +DurationProtocol, Encodable, Decodable, Encoder, Decoder, UnkeyedEncodingContainer, UnkeyedDecodingContainer, SingleValueEncodingContainer, SingleValueDecodingContainer, ExpressibleByNilLiteral, CodingKeyRepresentable, CustomStringConvertible, LosslessStringConvertible, TextOutputStream, TextOutputStreamable, CustomPlaygroundDisplayConvertible, CustomReflectable, CustomLeafReflectable, MirrorPath, -RandomNumberGenerator, CVarArg +RandomNumberGenerator, CVarArg, Sendable, UnsafeSendable, Actor, +AnyActor, Executor, SerialExecutor, DistributedActorSystemError ``` ## Detailed design ```swift -public protocol Identifiable public protocol Sequence public protocol IteratorProtocol public protocol Collection: Sequence @@ -163,6 +171,7 @@ public protocol BidirectionalCollection: Collection public protocol RandomAccessCollection: BidirectionalCollection public protocol RangeReplaceableCollection: Collection +public protocol Identifiable public protocol RawRepresentable public protocol RangeExpression public protocol Strideable: Comparable @@ -174,6 +183,12 @@ public protocol SIMD: ... public protocol Clock: Sendable public protocol InstantProtocol: Comparable, Hashable, Sendable + +public protocol DistributedActor: AnyActor, Identifiable, Hashable +public protocol DistributedActorSystem: Sendable +public protocol DistributedTargetInvocationEncoder +public protocol DistributedTargetInvocationDecoder +public protocol DistributedTargetInvocationResultHandler ``` ## Source compatibility From 7b2a1be6428445bb0783799747903e4a542c918e Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Mon, 9 May 2022 20:17:43 -0700 Subject: [PATCH 2544/4563] Revert primary associated type declarations in the Distributed module (for now) --- ...nnnn-primary-associated-types-in-stdlib.md | 118 +++++++++--------- 1 file changed, 57 insertions(+), 61 deletions(-) diff --git a/proposals/nnnn-primary-associated-types-in-stdlib.md b/proposals/nnnn-primary-associated-types-in-stdlib.md index 99c65da692..dd18a4dfdc 100644 --- a/proposals/nnnn-primary-associated-types-in-stdlib.md +++ b/proposals/nnnn-primary-associated-types-in-stdlib.md @@ -89,61 +89,61 @@ associated type, as well as a list of other associated types. [note]: #alternatives-considered -| Protocol | Primary | Others | -|------------------------------------------------------|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Sequence` | `Element` | `Iterator` | -| `IteratorProtocol` | `Element` | -- | -| `Collection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | -| `MutableCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | -| `BidirectionalCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | -| `RandomAccessCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | -| `RangeReplaceableCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | -| `LazySequenceProtocol` | -- [(1)][note] | `Element`, `Iterator`, `Elements` | -| `LazyCollectionProtocol` | -- [(1)][note] | `Element`, `Index`, `Iterator`, `SubSequence`, `Indices`, `Elements` | -| `Identifiable` | `ID` | -- | -| `RawRepresentable` | `RawValue` | -- | -| `RangeExpression` | `Bound` | -- | -| `Strideable` | `Stride` | -- | -| `SetAlgebra` | `Element` | `ArrayLiteralElement` | -| `OptionSet` | `Element` | `ArrayLiteralElement`, `RawValue` | -| `Numeric` | -- | `IntegerLiteralType`, `Magnitude` | -| `SignedNumeric` | -- | `IntegerLiteralType`, `Magnitude` | -| `BinaryInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | -| `UnsignedInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | -| `SignedInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | -| `FixedWidthInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | -| `FloatingPoint` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Exponent` | -| `BinaryFloatingPoint` | -- | `IntegerLiteralType`, `FloatLiteralType`, `Magnitude`, `Stride`, `Exponent`, `RawSignificand`, `RawExponent` | -| `SIMD` | `Scalar` | `ArrayLiteralElement`, `MaskStorage` | -| `SIMDStorage` | -- | `Scalar` | -| `SIMDScalar` | -- | `SIMDMaskScalar`, `SIMD2Storage`, `SIMD4Storage`, ..., `SIMD64Storage` | -| `KeyedEncodingContainerProtocol` | -- | `Key` | -| `KeyedDecodingContainerProtocol` | -- | `Key` | -| `ExpressibleByIntegerLiteral` | -- | `IntegerLiteralType` | -| `ExpressibleByFloatLiteral` | -- | `FloatLiteralType` | -| `ExpressibleByBooleanLiteral` | -- | `BooleanLiteralType` | -| `ExpressibleByUnicodeScalarLiteral` | -- | `UnicodeScalarLiteralType` | -| `ExpressibleByExtended-`
    `GraphemeClusterLiteral` | -- | `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType` | -| `ExpressibleByStringLiteral` | -- | `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType`, `StringLiteralType` | -| `ExpressibleByStringInterpolation` | -- | `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType`, `StringLiteralType`, `StringInterPolation` | -| `ExpressibleByArrayLiteral` | -- | `ArrayLiteralElement` | -| `ExpressibleByDictionaryLiteral` | -- | `Key`, `Value` | -| `StringInterpolationProtocol` | -- | `StringLiteralType` | -| `Unicode.Encoding` | -- | `CodeUnit`, `EncodedScalar`, `ForwardParser`, `ReverseParser` | -| `UnicodeCodec` | -- | `CodeUnit`, `EncodedScalar`, `ForwardParser`, `ReverseParser` | -| `Unicode.Parser` | -- | `Encoding` | -| `StringProtocol` | -- | `Element`, `Index`, `Iterator`, `SubSequence`, `Indices`, `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType`, `StringLiteralType`, `StringInterPolation`, `UTF8View`, `UTF16View`, `UnicodeScalarView` | -| `CaseIterable` | -- | `AllCases` | -| `Clock` | `Duration` | `Instant` | -| `InstantProtocol` | `Duration` | -- | -| `AsyncIteratorProtocol` | -- [(2)][note] | `Element` | -| `AsyncSequence` | -- [(2)][note] | `AsyncIterator`, `Element` | -| `GlobalActor` | -- | `ActorType` | -| `DistributedActor` | `ActorSystem` | `ID`, `SerializationRequirement` | -| `DistributedActorSystem` | `SerializationRequirement` | `ActorID`, `InvocationEncoder`, `InvocationDecoder`, `ResultHandler` | -| `DistributedTargetInvocationEncoder` | `SerializationRequirement` | | -| `DistributedTargetInvocationDecoder` | `SerializationRequirement` | | -| `DistributedTargetInvocationResultHandler` | `SerializationRequirement` | | +| Protocol | Primary | Others | +|------------------------------------------------------|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `Sequence` | `Element` | `Iterator` | +| `IteratorProtocol` | `Element` | -- | +| `Collection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | +| `MutableCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | +| `BidirectionalCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | +| `RandomAccessCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | +| `RangeReplaceableCollection` | `Element` | `Index`, `Iterator`, `SubSequence`, `Indices` | +| `LazySequenceProtocol` | -- [(1)][note] | `Element`, `Iterator`, `Elements` | +| `LazyCollectionProtocol` | -- [(1)][note] | `Element`, `Index`, `Iterator`, `SubSequence`, `Indices`, `Elements` | +| `Identifiable` | `ID` | -- | +| `RawRepresentable` | `RawValue` | -- | +| `RangeExpression` | `Bound` | -- | +| `Strideable` | `Stride` | -- | +| `SetAlgebra` | `Element` | `ArrayLiteralElement` | +| `OptionSet` | `Element` | `ArrayLiteralElement`, `RawValue` | +| `Numeric` | -- | `IntegerLiteralType`, `Magnitude` | +| `SignedNumeric` | -- | `IntegerLiteralType`, `Magnitude` | +| `BinaryInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | +| `UnsignedInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | +| `SignedInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | +| `FixedWidthInteger` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Words` | +| `FloatingPoint` | -- | `IntegerLiteralType`, `Magnitude`, `Stride`, `Exponent` | +| `BinaryFloatingPoint` | -- | `IntegerLiteralType`, `FloatLiteralType`, `Magnitude`, `Stride`, `Exponent`, `RawSignificand`, `RawExponent` | +| `SIMD` | `Scalar` | `ArrayLiteralElement`, `MaskStorage` | +| `SIMDStorage` | -- | `Scalar` | +| `SIMDScalar` | -- | `SIMDMaskScalar`, `SIMD2Storage`, `SIMD4Storage`, ..., `SIMD64Storage` | +| `KeyedEncodingContainerProtocol` | -- | `Key` | +| `KeyedDecodingContainerProtocol` | -- | `Key` | +| `ExpressibleByIntegerLiteral` | -- | `IntegerLiteralType` | +| `ExpressibleByFloatLiteral` | -- | `FloatLiteralType` | +| `ExpressibleByBooleanLiteral` | -- | `BooleanLiteralType` | +| `ExpressibleByUnicodeScalarLiteral` | -- | `UnicodeScalarLiteralType` | +| `ExpressibleByExtended-`
    `GraphemeClusterLiteral` | -- | `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType` | +| `ExpressibleByStringLiteral` | -- | `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType`, `StringLiteralType` | +| `ExpressibleByStringInterpolation` | -- | `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType`, `StringLiteralType`, `StringInterPolation` | +| `ExpressibleByArrayLiteral` | -- | `ArrayLiteralElement` | +| `ExpressibleByDictionaryLiteral` | -- | `Key`, `Value` | +| `StringInterpolationProtocol` | -- | `StringLiteralType` | +| `Unicode.Encoding` | -- | `CodeUnit`, `EncodedScalar`, `ForwardParser`, `ReverseParser` | +| `UnicodeCodec` | -- | `CodeUnit`, `EncodedScalar`, `ForwardParser`, `ReverseParser` | +| `Unicode.Parser` | -- | `Encoding` | +| `StringProtocol` | -- | `Element`, `Index`, `Iterator`, `SubSequence`, `Indices`, `UnicodeScalarLiteralType`, `ExtendedGraphemeClusterLiteralType`, `StringLiteralType`, `StringInterPolation`, `UTF8View`, `UTF16View`, `UnicodeScalarView` | +| `CaseIterable` | -- | `AllCases` | +| `Clock` | `Duration` | `Instant` | +| `InstantProtocol` | `Duration` | -- | +| `AsyncIteratorProtocol` | -- [(2)][note] | `Element` | +| `AsyncSequence` | -- [(2)][note] | `AsyncIterator`, `Element` | +| `GlobalActor` | -- | `ActorType` | +| `DistributedActor` | -- [(3)][note] | `ID`, `ActorSystem`, `SerializationRequirement` | +| `DistributedActorSystem` | -- [(3)][note] | `ActorID`, `SerializationRequirement`, `InvocationEncoder`, `InvocationDecoder`, `ResultHandler` | +| `DistributedTargetInvocationEncoder` | -- [(3)][note] | `SerializationRequirement` | +| `DistributedTargetInvocationDecoder` | -- [(3)][note] | `SerializationRequirement` | +| `DistributedTargetInvocationResultHandler` | -- [(3)][note] | `SerializationRequirement` | As of Swift 5.6, the following public protocols don't have associated type requirements, so they are outside of the scope of this proposal. @@ -183,12 +183,6 @@ public protocol SIMD: ... public protocol Clock: Sendable public protocol InstantProtocol: Comparable, Hashable, Sendable - -public protocol DistributedActor: AnyActor, Identifiable, Hashable -public protocol DistributedActorSystem: Sendable -public protocol DistributedTargetInvocationEncoder -public protocol DistributedTargetInvocationDecoder -public protocol DistributedTargetInvocationResultHandler ``` ## Source compatibility @@ -222,3 +216,5 @@ this proposal once this ships in a Standard Library release. (2) `AsyncSequence` and `AsyncIteratorProtocol` logically ought to have `Element` as their primary associated type. However, we have [ongoing evolution discussions][rethrows] about adding a precise error type to these. If those discussions bear fruit, then it's possible we may want to _also_ mark the potential new `Error` associated type as primary. To prevent source compatibility complications, adding primary associated types to these two protocols is deferred to a future proposal. [rethrows]: https://forums.swift.org/t/se-0346-lightweight-same-type-requirements-for-primary-associated-types/55869/70 + +(3) Declaring primary associated types on the distributed actor protocols would be desirable, but it was [deferred to a future proposal](https://forums.swift.org/t/pitch-primary-associated-types-in-the-standard-library/56426/47), to prevent interfering with potential future language improvements that would make them more useful in this use case. From ec76d9ed51cb0ab945830d340e296beae7bebf82 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Mon, 9 May 2022 20:19:28 -0700 Subject: [PATCH 2545/4563] Remove hard line breaks to improve typesetting with some markdown processors --- ...nnnn-primary-associated-types-in-stdlib.md | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/proposals/nnnn-primary-associated-types-in-stdlib.md b/proposals/nnnn-primary-associated-types-in-stdlib.md index dd18a4dfdc..102f7e0b41 100644 --- a/proposals/nnnn-primary-associated-types-in-stdlib.md +++ b/proposals/nnnn-primary-associated-types-in-stdlib.md @@ -83,9 +83,7 @@ Primary associated types add a new facet to the design of protocols. For every p ## Proposed solution -The table below lists all public protocols in the Standard Library -with associated type requirements, along with their proposed primary -associated type, as well as a list of other associated types. +The table below lists all public protocols in the Standard Library with associated type requirements, along with their proposed primary associated type, as well as a list of other associated types. [note]: #alternatives-considered @@ -187,27 +185,19 @@ public protocol InstantProtocol: Comparable, Hashable, Sendable ## Source compatibility -None. The new annotations enable new ways to use these protocols, but -they are tied to new syntax, and they do not affect existing code. +None. The new annotations enable new ways to use these protocols, but they are tied to new syntax, and they do not affect existing code. ## Effect on ABI stability -None. The annotations aren't ABI impacting, and the new capabilities -deploy back to any previous Swift Standard Library release. +None. The annotations aren't ABI impacting, and the new capabilities deploy back to any previous Swift Standard Library release. ## Effect on API resilience -Once introduced, primary associated types cannot be removed from a -protocol or reordered without breaking source compatibility. +Once introduced, primary associated types cannot be removed from a protocol or reordered without breaking source compatibility. -[SE-0346] requires usage sites to always list every primary associated -type defined by a protocol. Until/unless this restriction is lifted, -adding a new primary associated type to a protocol that already has -some will also be a source breaking change. +[SE-0346] requires usage sites to always list every primary associated type defined by a protocol. Until/unless this restriction is lifted, adding a new primary associated type to a protocol that already has some will also be a source breaking change. -Therefore, we will not be able to make any changes to the list of -primary associated types of any of the protocols that are affected by -this proposal once this ships in a Standard Library release. +Therefore, we will not be able to make any changes to the list of primary associated types of any of the protocols that are affected by this proposal once this ships in a Standard Library release. ## Alternatives considered From 6b119b215a6e390626e6c8029f6841685fbce339 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Tue, 10 May 2022 10:03:32 -0700 Subject: [PATCH 2546/4563] Update 0357-regex-string-processing-algorithms.md --- proposals/0357-regex-string-processing-algorithms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0357-regex-string-processing-algorithms.md b/proposals/0357-regex-string-processing-algorithms.md index 867bb2f477..023cfff334 100644 --- a/proposals/0357-regex-string-processing-algorithms.md +++ b/proposals/0357-regex-string-processing-algorithms.md @@ -221,7 +221,7 @@ public protocol CustomConsumingRegexComponent: RegexComponent { _ input: String, startingAt index: String.Index, in bounds: Range - ) throws -> (upperBound: String.Index, match: Match)? + ) throws -> (upperBound: String.Index, match: RegexOutput)? } ``` From 7741017763f528dfbdfa54c6d11f559918ab53e4 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Tue, 10 May 2022 10:07:00 -0700 Subject: [PATCH 2547/4563] Update 0357-regex-string-processing-algorithms.md --- proposals/0357-regex-string-processing-algorithms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0357-regex-string-processing-algorithms.md b/proposals/0357-regex-string-processing-algorithms.md index 023cfff334..bdb67911df 100644 --- a/proposals/0357-regex-string-processing-algorithms.md +++ b/proposals/0357-regex-string-processing-algorithms.md @@ -221,7 +221,7 @@ public protocol CustomConsumingRegexComponent: RegexComponent { _ input: String, startingAt index: String.Index, in bounds: Range - ) throws -> (upperBound: String.Index, match: RegexOutput)? + ) throws -> (upperBound: String.Index, output: RegexOutput)? } ``` From 13ead59b158b8bd33d3790becb3b49a9b367011f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Harck=20T=C3=B8nning?= Date: Tue, 10 May 2022 19:12:22 +0200 Subject: [PATCH 2548/4563] Update 0352-implicit-open-existentials.md (#1645) Fixes inconsistent example --- proposals/0352-implicit-open-existentials.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0352-implicit-open-existentials.md b/proposals/0352-implicit-open-existentials.md index 9eb2d5321b..d2a52a77a5 100644 --- a/proposals/0352-implicit-open-existentials.md +++ b/proposals/0352-implicit-open-existentials.md @@ -79,7 +79,7 @@ func checkFinaleReadiness(costumes: [any Costume]) -> Bool { } } - return false + return true } ``` From 897025db5c20323bc928f5542e9560b7e473bad1 Mon Sep 17 00:00:00 2001 From: linjiansi <55640271+linjiansi@users.noreply.github.com> Date: Wed, 11 May 2022 02:40:09 +0900 Subject: [PATCH 2549/4563] [SE-0353] update code blocks (#1633) --- .../0353-constrained-existential-types.md | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/proposals/0353-constrained-existential-types.md b/proposals/0353-constrained-existential-types.md index a37a87a688..973da2bb34 100644 --- a/proposals/0353-constrained-existential-types.md +++ b/proposals/0353-constrained-existential-types.md @@ -10,7 +10,7 @@ Existential types complement the Swift type system’s facilities for abstraction. Like generics, they enable a function to take and return multiple possible types. Unlike generic parameter types, existential types need not be known up front when passed as inputs to a function. Further, concrete types can be *erased* (hidden behind the interface of a protocol) when returned from a function. There has been a flurry of activity in this space with[SE-0309](https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md#covariant-erasure-for-associated-types) unblocking the remaining restrictions on using protocols with associated types as existential types, and [SE-0346](https://github.com/apple/swift-evolution/blob/main/proposals/0346-light-weight-same-type-syntax.md) paving the way for a lightweight constraint syntax for the associated types of protocols. Building directly upon those ideas, this proposal seeks to re-use the syntax of lightweight associated type constraints in the context of existential types. -``` +```swift any Collection ``` @@ -22,7 +22,7 @@ Swift-evolution pitch thread: https://forums.swift.org/t/pitch-constrained-exist Though [SE-0309](https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md#covariant-erasure-for-associated-types) provides the ability to use protocols with associated types freely, it does not leave any room for authors to further constrain the associated types of those protocols, creating a gap in expressiveness between generics and existentials. Consider the implementation of a type-erased stack of event producers and consumers: -``` +```swift protocol Producer { associatedtype Event @@ -38,18 +38,20 @@ protocol Consumer { If a hypothetical event system type wishes to accept an arbitrary mix of `Producer`s and an arbitrary mix of `Consumer`s, it is free to do so with existential types: -``` +```swift struct EventSystem { var producers: [any Producer] var consumers: [any Consumer] - mutating func add(_ producer: any Producer) { self.producers.append(producer) } + mutating func add(_ producer: any Producer) { + self.producers.append(producer) + } } ``` However, we run into trouble when trying to compose producers and consumers with one another. As any given `Producer` yields data of an unspecified and unrelated `Event` type when `poll`’ed, Swift will (rightly) tell us that none of our consumers can safely accept any events. One solution would be to make `EventSystem` generic over the type of events and require `Producer` and `Consumer` instances to only return those events. As it stands, this also means restricting the producers and consumers to be concrete, with the added downside of requiring us to homogenize their types - ad-hoc type erasure strikes again: -``` +```swift struct EventSystem { var producers: [AnyProducer] var consumers: [AnyConsumer] @@ -64,7 +66,7 @@ struct EventSystem { In this example, we have sacrificed quite a lot for type safety - and also have to maintain two extra type erasing wrappers for producers and consumers. Really, what is missing is the ability to express the fact that the producer and consumer types don’t matter (existential types) but the data they operate on *does* (generic constraints). This is where constrained existential types shine. When combined with the power of primary associated types from [SE-0346](https://github.com/apple/swift-evolution/blob/main/proposals/0346-light-weight-same-type-syntax.md), it allows us to write the code we wanted to in the first place: -``` +```swift struct EventSystem { var producers: [any Producer] var consumers: [any Consumer] @@ -79,7 +81,7 @@ struct EventSystem { Existential types will be augmented with the ability to specify constraints on their primary associated types. When an existential type appears with such constraints, they will be converted into same-type requirements. -``` +```swift protocol P { } var xs: [any P] // "Equivalent" to [any P] where P.T == B, P.U == N, P.V == J @@ -91,7 +93,7 @@ The syntax of existential types will be updated to accept constraint clauses. Ty The Swift type system and runtime will accept casts from parameterized existential types to non-parameterized existential types and vice versa, as well as casts that refine any constrained primary associated types. Upcasts and downcasts to, from, and between existential types will be updated to take these additional constraints into account: -``` +```swift var x: any Sequence _ = x as any Sequence // trivially true _ = x as! any Sequence // requires examining Sequence.Element at runtime @@ -109,13 +111,13 @@ Substitutions of constrained protocol types written with the same basic “shape One primary use-case for constrained existential types is their the Swift Standard Library’s Collection types. The Standard Library’s *concrete* collection types have built-in support for covariant coercions. For example, -``` +```swift func up(from values: [NSView]) -> [Any] { return values } ``` At first blush, it would seem like constrained existential types should support variance as well: -``` +```swift func up(from values: any Collection) -> any Collection { return values } ``` @@ -133,7 +135,7 @@ It is worth noting that this feature requires revisions to the Swift runtime and Aside from the obvious of not accepting this proposal, we could imagine many different kinds of spellings to introduce same-type requirements on associated types. For example, a where-clause based approach as in: -``` +```swift any (Collection where Self.Element == Int) ``` @@ -145,7 +147,7 @@ Syntax like this is hard to read and use in context and the problem becomes wors This proposal intentionally does not take a position on the generalized constraint syntax considered during the review of [SE-0341](https://github.com/apple/swift-evolution/blob/main/proposals/0341-opaque-parameters.md#constraining-the-associated-types-of-a-protocol). To take one spelling: -``` +```swift any Collection<.Index == String.Index> ``` @@ -155,7 +157,7 @@ Though when and if such a syntax is available we expect it to apply to constrain One particularly interesting construction is the composition of opaque types and constrained existential types. This combo allows for a particularly powerful form of type abstraction: -``` +```swift any Collection ``` @@ -165,13 +167,13 @@ This type describes any value that implements the `Collection` protocol but whos Constraints on existing primary associated types are hardly the only thing existential types can express. Swift’s type system can be given the ability to open arbitrary (constrained) type parameters into scope via an existential. This enables not just top-level usages as in -``` +```swift any Collection ``` But also nested usages as in -``` +```swift any Collection Collection> ``` From 69c5a1deb42b0a9cca013e82488504e6f0b2a210 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 10 May 2022 17:12:34 -0700 Subject: [PATCH 2550/4563] SE-0352: Specify that parenthesizing "as any P" prevents opening suppression Addresses feedback from the second review. --- proposals/0352-implicit-open-existentials.md | 33 ++++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/proposals/0352-implicit-open-existentials.md b/proposals/0352-implicit-open-existentials.md index d2a52a77a5..cc5995f3f0 100644 --- a/proposals/0352-implicit-open-existentials.md +++ b/proposals/0352-implicit-open-existentials.md @@ -462,20 +462,43 @@ Swift 6 will be a major language version change that can incorporate some semant ### Suppressing explicit opening with `as any P` / `as! any P` -If for some reason one wants to suppress the implicit opening of an existential value, one can explicitly write a coercion or forced cast to an existential type. For example: +If for some reason one wants to suppress the implicit opening of an existential value, one can explicitly write a coercion or forced cast to an existential type directly on the call argument. For example: ```swift func f1(_: T) { } // #1 func f1(_: T) { } // #2 func test(p: any P) { - f1(p) // opens p and calls #1, which is more specific - f1(p as any P) // suppresses opening of 'p', calls #2 which is the only valid candidate + f1(p) // opens p and calls #1, which is more specific + f1(p as any P) // suppresses opening of 'p', calls #2 which is the only valid candidate + f1((p as any P)) // parentheses disable this suppression mechanism, so this opens p and calls #1 } ``` Given that implicit opening of existentials is defined to occur in those cases where a generic function would not otherwise be callable, this suppression mechanism should not be required often in Swift 5. In Swift 6, where implicit opening will be more eagerly performed, it can be used to provide the Swift 5 semantics. +An extra set of parentheses will disable this suppression mechanism, which can be important when `as any P` is required for some other reason. For example, because it acknowledges when information is lost from the result type due to type erasure. This can help break ambiguities when both meanings of `as` could apply: + +```swift +protocol P { + associatedtype A +} +protocol Q { + associatedtype B: P where B.A == Int +} + +func getP(_ p: T) +func getBFromQ(_ q: T) -> T.B { ... } + +func eraseQAssoc(q: any Q) { + getP(getBFromQ(q)) // error, must specify "as any P" due to loss of constraint T.B.A == Int + getP(getBFromQ(q) as any P) // suppresses error above, but also suppresses opening, so it produces + // error: now "any P does not conform to P" and op + getP((getBFromQ(q) as any P)) // okay! original error message should suggest this +} + +``` + ## Source compatibility This proposal is defined specifically to avoid most impacts on source compatibility, especially in Swift 5. Some calls to generic functions that would previously have been ill-formed (e.g., they would fail because `any P` does not conform to `P`) will now become well-formed, and existing code will behavior in the same manner as before. As with any such change, it's possible that overload resolution that would have succeeded before will continue to succeed but will now pick a different function. For example: @@ -626,6 +649,10 @@ This approach is much more complex because it introduces value tracking into the ## Revisions +Fifth revision: + +* Note that parentheses disable the `as any P` suppression mechanism, avoiding the problem where `as any P` is both required (because type erasure lost information from the return type) and also has semantic effect (suppressing opening). + Fourth revision: * Add discussion about type erasure losing constraints and the new requirement to introduce an explicit `as` coercion when the upper bound loses information. From 89ee413cf740f5537b05a8e3f01c292429a77b04 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 11 May 2022 10:52:45 -0700 Subject: [PATCH 2551/4563] Extend SE-0352 by a week to review revision --- proposals/0352-implicit-open-existentials.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0352-implicit-open-existentials.md b/proposals/0352-implicit-open-existentials.md index cc5995f3f0..2c47bd52e5 100644 --- a/proposals/0352-implicit-open-existentials.md +++ b/proposals/0352-implicit-open-existentials.md @@ -3,7 +3,7 @@ * Proposal: [SE-0352](0352-implicit-open-existentials.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Active Review (April 21...May 4, 2022)** +* Status: **Active Review (April 21...May 18, 2022)** * Implementation: [apple/swift#41996](https://github.com/apple/swift/pull/41996), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-macos/120/artifact/branch-main/swift-PR-41996-120-osx.tar.gz) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/77374319a7d70c866bd197faada46ecfce461645/proposals/0352-implicit-open-existentials.md) * Previous Review: [First review](https://forums.swift.org/t/se-0352-implicitly-opened-existentials/56557/52) From 6eb8598df65b211ff71331e5a499b83cab59dcd7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 11 May 2022 12:23:24 -0700 Subject: [PATCH 2552/4563] SE-0353: Add "Covariant Erasure with Constrained Existentials" --- .../0353-constrained-existential-types.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/proposals/0353-constrained-existential-types.md b/proposals/0353-constrained-existential-types.md index 973da2bb34..89031b0661 100644 --- a/proposals/0353-constrained-existential-types.md +++ b/proposals/0353-constrained-existential-types.md @@ -125,6 +125,56 @@ But this turns out to be quite a technical feat. There is a naive implementation Constrained existential types will behave as normal generic types with respect to variance - that is, they are *invariant -* and the code above will be rejected. +### Covariant Erasure with Constrained Existentials + +[SE-0309](https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md) specifies that one can use a member on a value of existential type only when references to `Self` or its associated types are in *covariant* positions, such as the return type of a method defined a protocol. In such positions, its associated type is *erased* to its upper bound, which is the type that most closely describes the capabilities of that associated type. For example, consider a use of the `first` property on a collection. + +```swift +extension Collection {} + var first: Element? { get } +} + +func test(collection: any Collection, stringCollection: any Collection) { + let x = collection.first // Previously an error. With SE-0309, erases to 'Any?' + let y = stringCollection.first // With SE-0309, relies on Element == String to produce 'String?'' +} +``` + +However, when `Self` or its associated type occurs in an *invariant* position (defined in SE-0309), one cannot use the member of an existential type unless the concrete type is known. SE-0309 provides the following example: + +```swift +var collection: any RangeReplaceableCollection = [1, 2, 3] +// error: member 'append' cannot be used on value of protocol type 'RangeReplaceableCollection' +// because it references associated type 'Element' in contravariant position; use a conformance +// constraint instead. +collection.append(4) +``` + +With constrained existentials, one could append to a `RangeReplaceableCollection`: + +```swift +var intCollection: any RangeReplaceableCollection = [1, 2, 3] +collection.append(4) // okay: the Element type is concrete (Int) within the existential +``` + +The principle here is that a use of an associated type in the member is not considered invariant if that associated type has been made concrete by the existential type. This allows the use of `append` above, because `Element` has been made concrete by the type `any RangeReplaceableCollection`. Additionally, this means that the existential type that results from erasing an associated type can make use of constrained existentials. For example: + +```swift +extension Sequence { + func eagerFilter(_ isIncluded: @escaping (Element) -> Bool) -> [Element] { ... } + func lazyFilter(_ isIncluded: @escaping (Element) -> Bool) -> some Sequence { ... } +} + +func doFilter(sequence: any Sequence, intSequence: any Sequence) { + let e1 = sequence.eagerFilter { _ in true } // error: 'Element' is used in an invariant position + let e2 = intSequence.eagerFilter { _ in true } // okay, returns '[Int]' + let l1 = sequence.lazyFilter { _ in true } // error: 'Element' is used in an invariant position + let l2 = intSequence.lazyFilter { _ in true } // okay, returns 'any Sequence'' +} +``` + +The same erasure effects with the implicitly opened existentials introduced in [SE-0352](https://github.com/apple/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md). + ## Effect on ABI stability As constrained existential types are an entirely additive concept, there is no impact upon ABI stability. From 438e5850fa4850f7fed323647e2042ef7f5b3702 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 11 May 2022 14:05:46 -0700 Subject: [PATCH 2553/4563] Fix typos --- proposals/0353-constrained-existential-types.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0353-constrained-existential-types.md b/proposals/0353-constrained-existential-types.md index 89031b0661..cb57f6c76d 100644 --- a/proposals/0353-constrained-existential-types.md +++ b/proposals/0353-constrained-existential-types.md @@ -136,7 +136,7 @@ extension Collection {} func test(collection: any Collection, stringCollection: any Collection) { let x = collection.first // Previously an error. With SE-0309, erases to 'Any?' - let y = stringCollection.first // With SE-0309, relies on Element == String to produce 'String?'' + let y = stringCollection.first // With SE-0309, relies on Element == String to produce 'String?' } ``` @@ -169,7 +169,7 @@ func doFilter(sequence: any Sequence, intSequence: any Sequence) { let e1 = sequence.eagerFilter { _ in true } // error: 'Element' is used in an invariant position let e2 = intSequence.eagerFilter { _ in true } // okay, returns '[Int]' let l1 = sequence.lazyFilter { _ in true } // error: 'Element' is used in an invariant position - let l2 = intSequence.lazyFilter { _ in true } // okay, returns 'any Sequence'' + let l2 = intSequence.lazyFilter { _ in true } // okay, returns 'any Sequence' } ``` From e5054b6dbb34f2693dda50f1d9003356f69b66ce Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 11 May 2022 14:56:50 -0700 Subject: [PATCH 2554/4563] Extend SE-0353 for a week for new revision --- proposals/0353-constrained-existential-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0353-constrained-existential-types.md b/proposals/0353-constrained-existential-types.md index cb57f6c76d..734bd2f589 100644 --- a/proposals/0353-constrained-existential-types.md +++ b/proposals/0353-constrained-existential-types.md @@ -3,7 +3,7 @@ * Proposal: [SE-0353](0353-constrained-existential-types.md) * Authors: [Robert Widmann](https://github.com/codafi) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Active review (April 20...May 3, 2022)** +* Status: **Active review (April 20...May 18, 2022)** * Implementation: implemented in `main` branch, under flag `-enable-parameterized-existential-types` ## Introduction From 223a28bd17e5c398a5473d1e1c50d53d264ef902 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 12 May 2022 13:00:54 +0100 Subject: [PATCH 2555/4563] Update 0355-regex-syntax-run-time-construction.md Fix a couple of spelling errors and clarify that `Pattern_White_Space` is the Unicode property matched for extended syntax. --- proposals/0355-regex-syntax-run-time-construction.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0355-regex-syntax-run-time-construction.md b/proposals/0355-regex-syntax-run-time-construction.md index f59a896a97..a5b457ebe9 100644 --- a/proposals/0355-regex-syntax-run-time-construction.md +++ b/proposals/0355-regex-syntax-run-time-construction.md @@ -194,7 +194,7 @@ extension AnyRegexOutput { } ``` -Finally, we propose API for creating a regex containing literal string content. This produces an equivalent regex to a string literal embededd in the result builder DSL. As this is much less common than run-time compilation or an embedded literal in the DSL, it has an explicit argument label. +Finally, we propose API for creating a regex containing literal string content. This produces an equivalent regex to a string literal embedded in the result builder DSL. As this is much less common than run-time compilation or an embedded literal in the DSL, it has an explicit argument label. ```swift extension Regex { @@ -239,7 +239,7 @@ Concatenation -> (!'|' !')' ConcatComponent)* A regex may be prefixed with a sequence of [global matching options](#pcre-global-matching-options). Its contents can be empty or a sequence of alternatives separated by `|`. -Alternatives are a series of expressions concatenated together. The concatentation ends with either a `|` denoting the end of the alternative or a `)` denoting the end of a recursively parsed group. +Alternatives are a series of expressions concatenated together. The concatenation ends with either a `|` denoting the end of the alternative or a `)` denoting the end of a recursively parsed group. Alternation has a lower precedence than concatenation or other operations, so e.g `abc|def` matches against `abc` or `def`. @@ -950,7 +950,7 @@ Various regex engines offer an "extended syntax" where whitespace is treated as Oniguruma, Java, and ICU however enable the more broad behavior under `(?x)`. We therefore propose following this behavior, with `(?x)` and `(?xx)` being treated the same. -Different regex engines also have different rules around what characters are considered non-semantic whitespace. When compiled with Unicode support, PCRE considers the following whitespace: +Different regex engines also have different rules around what characters are considered non-semantic whitespace. When compiled with Unicode support, PCRE follows the `Pattern_White_Space` Unicode property, which consists of the following scalars: - The space character `U+20` - Whitespace characters `U+9...U+D` @@ -960,7 +960,7 @@ Different regex engines also have different rules around what characters are con - Line separator `U+2028` - Paragraph separator `U+2029` -This is a subset of the scalars matched by `UnicodeScalar.isWhitespace`. Additionally, in a custom character class, PCRE only considers the space and tab characters as whitespace. Other engines do not differentiate between whitespace characters inside and outside custom character classes, and appear to follow a subset of this list. Therefore we propose supporting exactly the characters in this list for the purposes of non-semantic whitespace parsing. +This is the same set of scalars matched by `UnicodeScalar.Properties.isPatternWhitespace`. Additionally, in a custom character class, PCRE only considers the space and tab characters as whitespace. Other engines do not differentiate between whitespace characters inside and outside custom character classes, and appear to follow a subset of this list. Therefore we propose supporting exactly the characters in this list for the purposes of non-semantic whitespace parsing. ### Group numbering From fb7341b06cacaca5e5e270361a3e05e04f318249 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 12 May 2022 13:00:54 +0100 Subject: [PATCH 2556/4563] Update to include a couple of extra syntactic details - Add scalar sequences `\u{A B C}` - Mention `<{...}>` is reserved - Clarify that `[\7]` is rejected - Reject `[]]` --- .../0355-regex-syntax-run-time-construction.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/proposals/0355-regex-syntax-run-time-construction.md b/proposals/0355-regex-syntax-run-time-construction.md index a5b457ebe9..820399498f 100644 --- a/proposals/0355-regex-syntax-run-time-construction.md +++ b/proposals/0355-regex-syntax-run-time-construction.md @@ -246,10 +246,11 @@ Alternation has a lower precedence than concatenation or other operations, so e. ### Concatenated subexpressions ``` -ConcatComponent -> Trivia | Quote | Quantification +ConcatComponent -> Trivia | Quote | Interpolation | Quantification Trivia -> Comment | NonSemanticWhitespace Comment -> '(?#' (!')')* ')' | EndOfLineComment +Interpolation -> '<{' (!'}>')* '}>' (extended syntax only) EndOfLineComment -> '#' (! .)* (extended syntax only) NonSemanticWhitespace -> + @@ -264,6 +265,8 @@ In-line comments, similarly to C, are lexical and are not recursively nested lik For example, `\Q^[xy]+$\E`, is treated as the literal characters `^[xy]+$` rather than an anchored quantified character class. `\Q\\E` is a literal `\`. +An interpolation sequence `<{...}>` is syntax that is reserved for a potential future interpolation feature. As such, the details surrounding it are future work, and it will currently be rejected for both literals and run-time compiled patterns. It may however be made available in the future as the literal characters. + ### Quantified subexpressions ``` @@ -309,7 +312,7 @@ Atom -> Anchor | '\'? ``` -Atoms are the smallest units of regex syntax. They include escape sequences, metacharacters, backreferences, etc. The most basic form of atom is a literal character. A metacharacter may be treated as literal by preceding it with a backslash. Other literal characters may also be preceded by a backslash, in which case it has no effect, e.g `\%` is literal `%`. However this does not apply to either non-whitespace Unicode characters, or to unknown ASCII letter character escapes, e.g `\I` is invalid and would produce an error. +Atoms are the smallest units of regex syntax. They include escape sequences, metacharacters, backreferences, etc. The most basic form of atom is a literal character. A metacharacter may be treated as literal by preceding it with a backslash. Other literal characters may also be preceded by a backslash, in which case it has no effect, e.g `\%` is literal `%`. However this does not apply to either non-whitespace Unicode characters, or to unknown ASCII letter and number character escapes, e.g `\I` is invalid and would produce an error. `(...)[\1]` is similarly invalid, as a backreference may not appear in a custom character class. #### Anchors @@ -375,7 +378,7 @@ Precise definitions of character classes is discussed in [Unicode for String Pro #### Unicode scalars ``` -UnicodeScalar -> '\u{' HexDigit{1...} '}' +UnicodeScalar -> '\u{' UnicodeScalarSequence '}' | '\u' HexDigit{4} | '\x{' HexDigit{1...} '}' | '\x' HexDigit{0...2} @@ -383,6 +386,9 @@ UnicodeScalar -> '\u{' HexDigit{1...} '}' | '\o{' OctalDigit{1...} '}' | '\0' OctalDigit{0...3} +UnicodeScalarSequence -> * UnicodeScalarSequencElt+ +UnicodeScalarSequencElt -> HexDigit{1...} * + HexDigit -> [0-9a-fA-F] OctalDigit -> [0-7] @@ -392,6 +398,8 @@ ScalarName -> 'U+' HexDigit{1...8} | [\s\w-]+ These sequences define a unicode scalar value using hexadecimal or octal notation. +In addition to a regular scalar literal e.g `\u{65}`, `\u{...}` also supports a scalar sequence syntax. This is syntactic sugar that implicitly expands a whitespace separated list of scalars e.g `\u{A B C}` into `\u{A}\u{B}\u{C}`. Such a sequence is currently only valid outside of a custom character class, their behavior within a custom character class is left as future work. + `\x`, when not followed by any hexadecimal digit characters, is treated as `\0`, matching PCRE's behavior. `\N{...}` allows a specific Unicode scalar to be specified by name or hexadecimal code point. @@ -573,7 +581,7 @@ Custom characters classes introduce their own sublanguage, in which most regular Atoms may be used to compose other character class members, including ranges, quoted sequences, and even nested custom character classes `[[ab]c\d]`. Adjacent members form an implicit union of character classes, e.g `[[ab]c\d]` is the union of the characters `a`, `b`, `c`, and digit characters. -Custom character classes may not be empty, e.g `[]` is forbidden. A custom character class may begin with the `]` character, in which case it is treated as literal, e.g `[]a]` is the custom character class of `]` and `a`. +Custom character classes may not be empty, e.g `[]` is forbidden. Quoted sequences may be used to escape the contained characters, e.g `[a\Q]\E]` is also the character class of `[` and `a`. From 30ec2951fbd51e1cebbc1fe843674274aaeb1ff8 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 13 May 2022 14:05:19 -0600 Subject: [PATCH 2557/4563] [SE-0349] Mark as implemented --- proposals/0349-unaligned-loads-and-stores.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0349-unaligned-loads-and-stores.md b/proposals/0349-unaligned-loads-and-stores.md index c13fecbbf2..26c9b30d14 100644 --- a/proposals/0349-unaligned-loads-and-stores.md +++ b/proposals/0349-unaligned-loads-and-stores.md @@ -3,7 +3,7 @@ * Proposal: [SE-0349](0349-unaligned-loads-and-stores.md) * Authors: [Guillaume Lessard](https://github.com/glessard), [Andrew Trick](https://github.com/atrick) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Implementation: [apple/swift#41033](https://github.com/apple/swift/pull/41033) * Review: ([pitch](https://forums.swift.org/t/55036/)) ([review](https://forums.swift.org/t/se-0349-unaligned-loads-and-stores-from-raw-memory/56423)) ([acceptance](https://forums.swift.org/t/accepted-se-0349-unaligned-loads-and-stores-from-raw-memory/56748)) From 2aa36a2260ce89920c41457a2f7028f452501b97 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 13 May 2022 14:12:41 -0600 Subject: [PATCH 2558/4563] Update 0334-pointer-usability-improvements.md Mark 0334 as implemented --- proposals/0334-pointer-usability-improvements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0334-pointer-usability-improvements.md b/proposals/0334-pointer-usability-improvements.md index a5b202301b..1aa50646d2 100644 --- a/proposals/0334-pointer-usability-improvements.md +++ b/proposals/0334-pointer-usability-improvements.md @@ -3,7 +3,7 @@ * Proposal: [SE-0334](0334-pointer-usability-improvements.md) * Authors: [Guillaume Lessard](https://github.com/glessard), [Andrew Trick](https://github.com/atrick) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Decision notes: [Acceptance](https://forums.swift.org/t/54700) * Implementation: [Draft pull request][draft-pr] * Bugs: [rdar://64342031](rdar://64342031), [SR-11156](https://bugs.swift.org/browse/SR-11156) ([rdar://53272880](rdar://53272880)), [rdar://22541346](rdar://22541346) From c9e71c97ef5ba23af0cb729b094c336ac82082de Mon Sep 17 00:00:00 2001 From: Mo Ramezanpoor Date: Sun, 15 May 2022 12:09:49 +0100 Subject: [PATCH 2559/4563] [SE-0355] Fix typo in character class interpretation. --- proposals/0355-regex-syntax-run-time-construction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0355-regex-syntax-run-time-construction.md b/proposals/0355-regex-syntax-run-time-construction.md index f59a896a97..154afd79d2 100644 --- a/proposals/0355-regex-syntax-run-time-construction.md +++ b/proposals/0355-regex-syntax-run-time-construction.md @@ -575,7 +575,7 @@ Atoms may be used to compose other character class members, including ranges, qu Custom character classes may not be empty, e.g `[]` is forbidden. A custom character class may begin with the `]` character, in which case it is treated as literal, e.g `[]a]` is the custom character class of `]` and `a`. -Quoted sequences may be used to escape the contained characters, e.g `[a\Q]\E]` is also the character class of `[` and `a`. +Quoted sequences may be used to escape the contained characters, e.g `[a\Q]\E]` is also the character class of `]` and `a`. Ranges of characters may be specified with `-`, e.g `[a-z]` matches against the letters from `a` to `z`. Only unicode scalars and literal characters are valid range operands. If `-` cannot be used to form a range, it is interpreted as literal, e.g `[-a-]` is the character class of `-` and `a`. `[a-c-d]` is the character class of `a`...`c`, `-`, and `d`. From 8e1e5a5c50b7691a9ed687b77b9dcfcabe487a3e Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Sun, 15 May 2022 12:19:18 +0100 Subject: [PATCH 2560/4563] Update proposals/0355-regex-syntax-run-time-construction.md Co-authored-by: Mo Ramezanpoor --- proposals/0355-regex-syntax-run-time-construction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0355-regex-syntax-run-time-construction.md b/proposals/0355-regex-syntax-run-time-construction.md index 820399498f..f316113d54 100644 --- a/proposals/0355-regex-syntax-run-time-construction.md +++ b/proposals/0355-regex-syntax-run-time-construction.md @@ -583,7 +583,7 @@ Atoms may be used to compose other character class members, including ranges, qu Custom character classes may not be empty, e.g `[]` is forbidden. -Quoted sequences may be used to escape the contained characters, e.g `[a\Q]\E]` is also the character class of `[` and `a`. +Quoted sequences may be used to escape the contained characters, e.g `[a\Q]\E]` is the character class of `]` and `a`. Ranges of characters may be specified with `-`, e.g `[a-z]` matches against the letters from `a` to `z`. Only unicode scalars and literal characters are valid range operands. If `-` cannot be used to form a range, it is interpreted as literal, e.g `[-a-]` is the character class of `-` and `a`. `[a-c-d]` is the character class of `a`...`c`, `-`, and `d`. From 6a506a519dc4029d3321f14e9914528cffeb684f Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Mon, 16 May 2022 15:27:22 +0100 Subject: [PATCH 2561/4563] [SE-0354] Update regex literal proposal (#1651) Update to specify the new parsing rules. --- proposals/0354-regex-literals.md | 169 +++++++++++++++---------------- 1 file changed, 79 insertions(+), 90 deletions(-) diff --git a/proposals/0354-regex-literals.md b/proposals/0354-regex-literals.md index 8c76c1cb49..df24598103 100644 --- a/proposals/0354-regex-literals.md +++ b/proposals/0354-regex-literals.md @@ -139,7 +139,7 @@ let regex = #/ usr/lib/modules/ # Prefix (? [^/]+) /vmlinuz # The kernel -#/ +/# // regex: Regex<(Substring, subpath: Substring)> ``` @@ -185,7 +185,7 @@ In such a literal, [extended regex syntax][extended-regex-syntax] `(?x)` is enab This mode is supported with any (non-zero) number of `#` characters in the delimiter. Similar to multi-line strings introduced by [SE-0168], the closing delimiter must appear on a new line. To avoid parsing confusion, such a literal will not be parsed if a closing delimiter is not present. This avoids inadvertently treating the rest of the file as regex if you only type the opening. -### Ambiguities with comment syntax +### Ambiguities of `/.../` with comment syntax Line comment syntax `//` and block comment syntax `/*` will continue to be parsed as comments. An empty regex literal is not a particularly useful thing to express, but can be written as `#//#` if desired. `*` would be an invalid starting character of a regex, and therefore does not pose an issue. @@ -200,13 +200,13 @@ A parsing conflict does however arise when a block comment surrounds a regex lit In this case, the block comment prematurely ends on the second line, rather than extending all the way to the third line as the user would expect. This is already an issue today with `*/` in a string literal, though it is more likely to occur in a regex given the prevalence of the `*` quantifier. This issue can be avoided in many cases by using line comment syntax `//` instead, which it should be noted is the syntax that Xcode uses when commenting out multiple lines. -### Ambiguity with infix operators +### Ambiguity of `/.../` with infix operators There is a minor ambiguity when infix operators are used with regex literals. When used without whitespace, e.g `x+/y/`, the expression will be treated as using an infix operator `+/`. Whitespace is therefore required for regex literal interpretation, e.g `x + /y/`. Alternatively, extended literals may be used, e.g `x+#/y/#`. -### Regex syntax limitations +### Regex syntax limitations in `/.../` -In order to help avoid further parsing ambiguities, a `/.../` regex literal will not be parsed if it starts with a space or tab character. This restriction may be avoided by using the extended `#/.../#` literal. +In order to help avoid a parsing ambiguity, a `/.../` regex literal will not be parsed if it starts with a space or tab character. This restriction may be avoided by using the extended `#/.../#` literal. #### Rationale @@ -226,9 +226,7 @@ let regex = Regex { Instead of being parsed as 3 result builder elements, the second of which being a regex literal, this is instead parsed as a single operator chain with the operands `digit`, `[+-]`, and `digit`. This will therefore be diagnosed as semantically invalid. -To avoid this issue, a regex literal may not start with a space or tab character. This takes advantage of the fact that infix operators require consistent spacing on either side. - -If a space or tab is needed as the first character, it must be either escaped, e.g: +To avoid this issue, a regex literal may not start with a space or tab character. If a space or tab is needed as the first character, it must be either escaped, e.g: ```swift let regex = Regex { @@ -238,7 +236,7 @@ let regex = Regex { } ``` -or extended literal must be used, e.g: +or an extended literal must be used, e.g: ```swift let regex = Regex { @@ -248,132 +246,123 @@ let regex = Regex { } ``` -### Language changes required - -In addition to ambiguity listed above, there are also a couple of parsing ambiguities that require the following language changes in a new language mode: - -- Deprecation of prefix operators containing the `/` character. -- Parsing an unapplied infix operator containing `/` as the start of a regex literal if a closing `/` is found, and the starting character is valid. - -#### Prefix operators containing `/` - -Prefix operators starting with `/` require banning to avoid ambiguity with cases such as: +This restriction takes advantage of the fact that infix operators require consistent spacing on either side. This includes both space characters as well as newlines. For example: ```swift -let x = /0; let y = 1/ -let z = /^x^/ +let a = 0 + 1 // Valid +let b = 0+1 // Also valid +let c = 0 ++ 1 // Valid operator chain because the newline before '+' is whitespace. + +let d = 0 +1 // Not valid, '+' is treated as prefix, which cannot then appear next to '0'. +let e = 0+ 1 // Same but postfix +let f = 0 ++1 // Not a valid operator chain, same as 'd', except '+1' is no longer sequenced with '0'. ``` -Prefix operators containing `/` more generally also need banning, in order to allow prefix operators to be used with regex literals in an unambiguous way, e.g: - +In much the same way as `f`, by requiring the first character of a regex literal not to be space or tab, we ensure it cannot be treated as an operator chain: + ```swift -let x = !/y / .foo() +let g = 0 +/1 + 2/ // Must be a regex ``` -Today, this is interpreted as the prefix operator `!/` on `y`. With the banning of prefix operators containing `/`, it becomes prefix `!` on a regex literal, with a member access `.foo`. +### How `/.../` is parsed -Postfix `/` operators do not require banning, as they'd only be treated as regex literal delimiters if we are already trying to lex as a regex literal. +A `/.../` regex literal will be parsed when an opening `/` is encountered in expression position, and there is a closing `/` present. As such, the following will continue to parse as normal: -#### Unapplied infix operators containing `/` +```swift +// Infix '/' is never in an expression position in valid code (unless unapplied). +let a = 1 / 2 / 3 -An ambiguity arises with Swift's ability to pass an unapplied operator reference as an argument to a function or subscript, for example: +// None of these '/^/' cases are in expression position. +infix operator /^/ +func /^/ (lhs: Int, rhs: Int) -> Int { 0 } +let b = 0 /^/ 1 -```swift -let arr: [Double] = [2, 3, 4] -let x = arr.reduce(1, /) / 5 +// Also fine. +prefix operator / +prefix func / (_ x: Int) -> Int { x } +let c = /0 // No closing '/', so not a regex literal. The '//' of this comment doesn't count either. ``` -The `/` in the call to `reduce` is in a valid expression context, and as such could be parsed as a regex literal. This is also applicable to operators in tuples and parentheses. To help mitigate this ambiguity, a regex literal will not be parsed if the first character is `)`. This should have minimal impact, as this would not be valid regex syntax anyway. This joins the existing space and tab starting character rule. +But `let r = /^/` will be parsed as a regex. -However this does not mitigate the ambiguity when the next character is a comma, `]`, or another valid operator character such as `^`. These are all valid regex starting characters, with comma and `^` in particular being quite common. In these cases, a regex literal will be parsed if a closing `/` is found. +A regex literal may be used with a prefix operator, e.g `let r = ^^/x/` is parsed as `let r = ^^(/x/)`. In this case, when encountering operator characters containing `/` in an expression position, the characters up to the first `/` are split into a prefix operator, and regex literal parsing continues as normal. -For example, all of the following will be parsed as regex literals instead of unapplied operators: +As already discussed, a regex literal may not begin with a space or tab. However this is not sufficient to disambiguate cases such as: ```swift -func foo(_ x: (Int, Int) -> Int, _ y: (Int, Int) -> Int) {} - -foo(/, /) // Will become the regex literal `/, /` -foo(/^, /) // Will become the regex literal `/^, /` -foo(!/, /) // Will become prefix `!` on the regex literal `/, /` +// Unapplied '/' in a call to 'reduce': +let x = array.reduce(1, /) / 5 +let y = array.reduce(1, /) + otherArray.reduce(1, /) -// Also affects cases where the closing '/' is outside the argument list. -func bar(_ fn: (Int, Int) -> Int, _ x: Int) -> Int { 0 } -bar(/, 2) + bar(/, 3) // Will become the (invalid) regex literal `/, 2) + bar(/` +// Prefix '/' with a potential closing '/'. +foo(/x) / 2 -// Ambiguity with right square bracket: -struct S { - subscript(_ fn: (Int, Int) -> Int) -> Int { 0 } -} -func baz(_ x: S) -> Int { - x[/] + x[/] // Will become the (invalid) regex literal `/] + x[/` -} - -// Ambiguity with an unapplied operator with two `/` characters: -func baz(_ x: (Int, Int) -> Int) {} -baz(/^/) // Will become the regex literal `/^/` +// Unapplied '!/' with a potential closing '/'. +bar(!/, 1) / 2 ``` -To disambiguate the cases with the `/` operator, you may surround at least the opening `/` with parentheses, e.g: +In all of these cases, the opening `/` appears in expression position, and there is a potential closing `/`. To avoid source breakage for such cases, one further heuristic is employed. A regex literal will not be parsed if it contains an unbalanced `)`. This takes both escapes and custom character classes into consideration, and therefore only applies to syntax that would already be invalid for a regex. As such, all of the above cases will continue to be parsed as normal. + +This additional heuristic also allows for straightforward disambiguation in source breaking cases where the regex is valid. For example, the following cases will become regex literals: ```swift -foo((/), /) -bar((/), 2) + bar(/, 3) +baz(/, /) // Will become the regex literal '/, /' +baz(/^, /) // Will become the regex literal '/^, /' +baz(!/, /) // Will become prefix '!' on the regex literal '/, /' -func baz(_ x: S) -> Int { - x[(/)] + x[/] -} -``` +// Will become the regex literal '/] /' +let d = hasSubscript[/] / 2 -This takes advantage of the fact that a regex literal will not be parsed if the first character is `)`. +let e = /0; let f = 1/ // Will become the regex literal '/0; let y = 1/' +let g = /^x/ // Will become the regex literal '/^x/' +let h = !/y / .foo() // Will become the regex literal '/y /' with prefix '!' and method call '.foo()'. +``` -To disambiguate other operator cases, e.g `/^`, `!/`, and `/^/`, you may turn the expression into a closure, e.g: +However they can be readily disambiguated by inserting parentheses: ```swift -foo({ $0 /^ $1 }, /) -foo({ $0 !/ $1 }, /) -baz({ $0 /^/ $1 }) -``` +// These are now all unapplied operators +baz((/), /) +baz((/^), /) +baz((!/), /) -This takes advantage of the fact that a regex literal will not be parsed in an infix operator position. - -In most cases, you may also factor the operator out of the call, e.g: +// Unapplied operator in a subscript +let d = hasSubscript[(/)] / 2 -```swift -let op = (/^) -foo(op, /) +let e = (/0); let f = 1/ // Two variables, with prefix '/' and postfix '/' +let g = (/^x)/ // Prefix '/^' with postfix '/' +let h = (!/y) / .foo() // Prefix '!/' with infix '/' and member argument '.foo()' ``` -Or even split the argument list over multiple lines, e.g: +We however expect these cases will be fairly uncommon. A similar case is the use of an unapplied infix operator with two `/` characters, for example: ```swift -foo(/^, - /) +baz(/^/) // Will become the regex literal '/^/' rather than an unapplied operator ``` -### Summary of `/.../` parsing - -When enabled, the forward slash syntax will be parsed when an opening `/` is encountered in expression position. Because this only affects syntax in an expression position, the following will continue to parse as normal: +This cannot be disambiguated with parentheses, however it can be disambiguated using a closure. For example: ```swift -infix operator /^/ -func /^/ (lhs: Int, rhs: Int) -> Int { 0 } -let i = 0 /^/ 1 +baz({ $0 /^/ $1 }) // Is now infix '/^/' ``` -But `let r = /^/` will be parsed as a regex. - -A regex literal may not begin with space, tab or `)`. Though the latter is already invalid regex syntax. In many cases, we have sufficient context to know that an opening `/` must be a regex literal. In these cases, an error will be emitted if either a closing `/` is not found or an invalid starting character is present. However, within parentheses, tuples, and argument lists, there is an ambiguity with unapplied infix operators. In these cases, a regex literal will only be parsed if a closing `/` is present, and the starting character is valid. - -A regex literal may be used with a prefix operator, e.g `let r = ^^/x/` is parsed as `let r = ^^(/x/)`. In this case, when encountering operator characters containing `/` in an expression position, the characters up to the first `/` are split into a prefix operator, and regex literal parsing continues as normal. +This takes advantage of the fact that a regex literal will not be parsed in an infix operator position. ## Source Compatibility -As explored above, two source breaking changes are needed for `/.../` syntax: +As explored above, the parsing of `/.../` does have potential to break source in cases where all of the following hold: + +- `/` appears in an expression position. +- There is a closing `/` on the same line. +- The first character following the opening `/` is not space or tab. +- There are no unbalanced `)` characters between the two `/` characters. -- Deprecation of prefix operators containing the `/` character. -- Parsing an unapplied infix operator containing `/` as the start of a regex literal if a closing `/` is found, and the starting character is valid. +However we expect these cases will be uncommon, and can be disambiguated with parentheses or closures if needed. -As such, both these changes and the `/.../` syntax will be introduced in Swift 6 mode. However, projects will be able to adopt the syntax earlier by passing the compiler flag `-enable-bare-regex-syntax`. Note this does not affect the extended delimiter syntax `#/.../#`, which will be usable immediately. +To accommodate the cases where source may be broken, `/.../` regex literals will be introduced in Swift 6 mode. However, projects may adopt the syntax earlier by passing the compiler flag `-enable-bare-regex-syntax`. Note this does not affect the extended delimiter syntax `#/.../#`, which will be usable immediately. ## Future Directions From 8b6d7e423d913d1764cf66ce60204eb867d991fd Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 16 May 2022 07:28:03 -0700 Subject: [PATCH 2562/4563] Update 0354-regex-literals.md --- proposals/0354-regex-literals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0354-regex-literals.md b/proposals/0354-regex-literals.md index df24598103..c593712672 100644 --- a/proposals/0354-regex-literals.md +++ b/proposals/0354-regex-literals.md @@ -3,7 +3,7 @@ * Proposal: [SE-0354](0354-regex-literals.md) * Authors: [Hamish Knight](https://github.com/hamishknight), [Michael Ilseman](https://github.com/milseman), [David Ewing](https://github.com/DaveEwing) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (April 28 – May 10 2022)** +* Status: **Active Review (May 16 – May 23 2022)** * Implementation: https://github.com/apple/swift-experimental-string-processing * Available in nightly toolchain snapshots with `-enable-bare-slash-regex` From 9ad72bd86310af94029df11789bc1ac970042851 Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Tue, 17 May 2022 22:29:11 +0200 Subject: [PATCH 2563/4563] [SE-0348] Fix typo (#1654) --- proposals/0348-buildpartialblock.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0348-buildpartialblock.md b/proposals/0348-buildpartialblock.md index b64bb342c0..62f6bf220f 100644 --- a/proposals/0348-buildpartialblock.md +++ b/proposals/0348-buildpartialblock.md @@ -49,7 +49,7 @@ When `buildPartialBlock(first:)` and `buildPartialBlock(accumulated:next:)` are } ``` -The primary goal of this feature is to reduce the code bloat casued by overloading `buildBlock` for multiple arities, allowing libraries to define builder-based generic DSLs with joy and ease. +The primary goal of this feature is to reduce the code bloat caused by overloading `buildBlock` for multiple arities, allowing libraries to define builder-based generic DSLs with joy and ease. ## Motivation From 33a935721eeb1e95feb4d01d3e6587a1a027a33f Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 17 May 2022 16:34:28 -0700 Subject: [PATCH 2564/4563] Added an entry on alias overriding Updated moduleAliases parameter order --- ...0339-module-aliasing-for-disambiguation.md | 60 +++++++++++++------ 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/proposals/0339-module-aliasing-for-disambiguation.md b/proposals/0339-module-aliasing-for-disambiguation.md index 0bda385b17..a2a467ffeb 100644 --- a/proposals/0339-module-aliasing-for-disambiguation.md +++ b/proposals/0339-module-aliasing-for-disambiguation.md @@ -64,13 +64,13 @@ Since `App` depends on these two `Utils` modules, we have a conflict, thus we ne .executableTarget( name: "App", dependencies: [ - .product(name: "Game", moduleAliases: ["Utils": "GameUtils"], package: "swift-game"), + .product(name: "Game", package: "swift-game", moduleAliases: ["Utils": "GameUtils"]), .product(name: "Utils", package: "swift-draw"), ]) ] ``` -The setting `moduleAliases` will rename `Utils` from the `swift-game` package as `GameUtils` and alias all its references in the source code to be compiled as `GameUtils`. Since renaming one of the `Utils` modules will resolve the conflict, it is not necessary to rename the other `Utils` module. The references to `Utils` in the `Game` module will be built as `GameUtils` without requiring any source changes. If `App` needs to reference both `Utils` modules in its source code, it can do so via the unique names: +The setting `moduleAliases` will rename `Utils` from the `swift-game` package as `GameUtils` and alias all its references in the source code to be compiled as `GameUtils`. Since renaming one of the `Utils` modules will resolve the conflict, it is not necessary to rename the other `Utils` module. The references to `Utils` in the `Game` module will be built as `GameUtils` without requiring any source changes. If `App` needs to reference both `Utils` modules in its source code, it can do so by directly including the aliased name: ``` [App] @@ -87,8 +87,8 @@ Module aliasing relies on being able to change the namespace of all declarations Most use cases should just require setting `moduleAliases` in a package manifest. However, it may be helpful to understand how that setting changes the compiler invocations under the hood. In our example scenario, those invocations will change as follows: -1. First, we need to take the `Utils` module from `swift-game` and rename it `GameUtils`. To do this, we will compile the module as if it was actually named `GameUtils`, while treating any references to `Utils` in its source files as references to `GameUtils`. - 1. The first part (renaming) can be achieved by passing the new module name (`GameUtils`) to `-module-name`. The new module name will also need to be used in any flags specifying output paths, such as `-o`, `-emit-module-path`, or `-emit-module-interface-path`. For example, the binary module file should be built as `GameUtils.swiftmodule` instead of `Utils.swiftmodule`. +1. First, we need to take the `Utils` module from `swift-game` and rename it `GameUtils`. To do this, we will compile the module as if it were actually named `GameUtils`, while treating any references to `Utils` in its source files as references to `GameUtils`. + 1. The first part (renaming) can be achieved by passing the new module name (`GameUtils`) to `-module-name`. The new module name will also need to be used in any flags specifying output paths, such as `-o`, `-emit-module-path`, or `-emit-module-interface-path`. For example, the binary module file should be built as `GameUtils.swiftmodule` instead of `Utils.swiftmodule`. 2. The second part (treating references to `Utils` in source files as `GameUtils`) can be achieved with a new compiler flag `-module-alias [name]=[new_name]`. Here, `name` is the module name that appears in source files (`Utils`), while `new_name` is the new, unique name (`GameUtils`). So in our example, we will pass `-module-alias Utils=GameUtils`. Putting these steps together, the compiler invocation command would be `swiftc -module-name GameUtils -emit-module-path /path/to/GameUtils.swiftmodule -module-alias Utils=GameUtils ...`. @@ -98,29 +98,29 @@ Most use cases should just require setting `moduleAliases` in a package manifest 3. We don't need any build changes when building `App` because the source code in `App` does not expect to use the `Utils` module from `swift-game` under its original name. If `App` tries to import a module named `Utils`, that will refer to the `Utils` module from `swift-draw`, which has not been renamed. If `App` does need to import the `Utils` module from `swift-game`, it must use `import GameUtils`. -The arguments to the `-module-alias` flag will be validated against reserved names, invalid identifiers, wrong format or ordering (`-module-alias Utils=GameUtils` is correct but `-module-alias GameUtils=Utils` is not). The flag can be repeated to allow multiple aliases, e.g. `-module-alias Utils=GameUtils -module-alias Logging=SwiftLogging`, and will be checked against duplicates. Diagnostics and fix-its will contain the name Utils in the error messages as opposed to GameUtils to be consistent with the names appearing to users. +The arguments to the `-module-alias` flag will be validated against reserved names, invalid identifiers, a wrong format or ordering (`-module-alias Utils=GameUtils` is correct but `-module-alias GameUtils=Utils` is not). The flag can be repeated to allow multiple aliases, e.g. `-module-alias Utils=GameUtils -module-alias Logging=GameLogging`, and will be checked against duplicates. Diagnostics and fix-its will contain the name `Utils` in the error messages as opposed to `GameUtils` to be consistent with the names appearing to users. -The validated map of aliases will be stored in the AST context and used for dependency scanning/resolution and module loading; from the above scenario, if Game is built with `-module-alias Utils=GameUtils` and has `import Utils` in source code, `GameUtils.swiftmodule` should be loaded instead of `Utils.swiftmodule` during import resolution. +The validated map of aliases will be stored in the AST context and used for dependency scanning/resolution and module loading; from the above scenario, if `Game` is built with `-module-alias Utils=GameUtils` and has `import Utils` in source code, `GameUtils.swiftmodule` should be loaded instead of `Utils.swiftmodule` during import resolution. -While the name Utils appears in source files, the actual binary name will be used for name lookup, semantic analysis, symbol mangling (e.g. `$s9GameUtils5Level`), and serialization. Since the binary names will be stored during serialization, the aliasing flag will only be needed to build the conflicting modules and their immediate consuming modules; building non-immediate consuming modules will not require the flag. +While the name `Utils` appears in source files, the actual binary name `GameUtils` will be used for name lookup, semantic analysis, symbol mangling (e.g. `$s9GameUtils5Level`), and serialization. Since the binary names will be stored during serialization, the aliasing flag will only be needed to build the conflicting modules and their immediate consuming modules; building non-immediate consuming modules will not require the flag. -The module alias map will also be used to disallow any references to the binary module names in source files; only the name Utils should appear in source files, not the binary name GameUtils. This is true only if the `-module-alias` was used to build the module (if the renamed modules were directly imported, those names (binary module names) can appear in source files). This restriction is useful as it can make it easier to rename the module again later if needed, e.g. from GameUtils to SwiftGameUtils. +Direct references to the renamed modules should only be allowed in source code if multiple conflicting modules need to be imported; in such case, a direct reference in an import statement, e.g. `import GameUtils`, is allowed. Otherwise, the original name `Utils` should be used in source code instead of the binary name `GameUtils`. The module alias map will be used to track when to disallow direct references to the binary module names in source files, and an attempt to use the binary name will result in an error along with a fix-it. This restriction is useful as it can make it easier to rename the module again later if needed, e.g. from `GameUtils` to `SwiftGameUtils`. -Unlike source files, the generated interface module (.swiftinterface) will contain the binary module name in all its references. The binary module name will also be stored for indexing and debugging, and treated as the source of truth. +Unlike source files, the generated interface (.swiftinterface) will contain the binary module name in all its references. The binary module name will also be stored for indexing and debugging, and treated as the source of truth. ### Changes to Code Assistance / Indexing -The compiler arguments including the new flag `-module-alias` will be available to SourceKit and indexing. The aliases will be stored in the AST context and used to fetch the right results for code completion and other code assistance features. They will also be stored for indexing so features such as jump to definition can navigate to decls under the binary module names. +The compiler arguments including the new flag `-module-alias` will be available to SourceKit and indexing. The aliases will be stored in the AST context and used to fetch the right results for code completion and other code assistance features. They will also be stored for indexing so features such as `Jump to Definition` can navigate to declarations scoped to the binary module names. Generated documentation, quick help, and other assistance features will contain the binary module names, which will be treated as the source of truth. ### Changes to Swift Driver -The module aliasing arguments will be used during dependency scan for both implicit and explicit build modes; the resolved dependency graph will contain the binary module names. In case of the explicit build mode, the dependency input passed to the frontend will contain the binary module names in its json file. Similar to the frontend, validation of the aliasing arguments will be performed at the driver. +The module aliasing arguments will be used during the dependency scan for both implicit and explicit build modes; the resolved dependency graph will contain the binary module names. In case of the explicit build mode, the dependency input passed to the frontend will contain the binary module names in its json file. Similar to the frontend, validation of the aliasing arguments will be performed at the driver. ### Changes to SwiftPM -To make module aliasing more accessible, we will introduce new build configs which can map to the compiler flags for aliasing described above. Let’s go over how they can be adopted by SwiftPM with the above scenario (copied below). +To make module aliasing more accessible, we will introduce new build configs which can map to the compiler flags for aliasing described above. Let’s go over how they can be adopted by SwiftPM with the above scenario (copied here). ``` App |— Module Game (from package ‘swift-game’) @@ -128,7 +128,7 @@ App |— Module Utils (from package ‘swift-draw’) ``` -Here are the example manifests for `swift-game` and `swift-draw`. +Here are the manifest examples for `swift-game` and `swift-draw`. ``` { @@ -139,7 +139,7 @@ Here are the example manifests for `swift-game` and `swift-draw`. .library(name: "Utils", targets: ["Utils"]), ], targets: [ - .target(name: "Game", targets: ["Utils"]), + .target(name: "Game", dependencies: ["Utils"]), .target(name: "Utils", dependencies: []) ] } @@ -173,14 +173,38 @@ The `App` manifest needs to explicitly define unique names for the conflicting m .executableTarget( name: "App", dependencies: [ - .product(name: "Game", moduleAliases: ["Utils": "GameUtils"], package: "swift-game"), + .product(name: "Game", package: "swift-game", moduleAliases: ["Utils": "GameUtils"]), .product(name: "Utils", package: "swift-draw"), ]) ] } ``` -SwiftPM will check validations on `moduleAliases`; for each entry in `moduleAliases`, it will check if the specified module meets the requirements described in the **Requirements/Limitations** section below, such as whether the module is a pure Swift module. If validations pass, it will trigger a build with `-module-alias` for the module as described earlier. Note that only one new name can be given to a conflicting module and it should be a unique name. +SwiftPM will perform validations when it parses `moduleAliases`; for each entry, it will check whether the given alias is a unique name, whether there is a conflict among aliases, whether the specified module is built from source (pre-compiled modules cannot be rebuilt to respect the rename), and whether the module is a pure Swife module (see **Requirements/Limitations** section for more details). + +It will also check if any aliases are defined in upstream packages and override them if necessary. For example, if the `swift-game` package were modified per below and defined its own alias `SwiftUtils` for module `Utils` from a dependency package, the alias defined in `App` will override it, thus the `Utils` module from `swift-utils` will be built as `GameUtils`. + +``` +{ + name: "swift-game", + dependencies: [ + .package(url: https://.../swift-utils.git), + ], + products: [ + .library(name: "Game", targets: ["Game"]), + ], + targets: [ + .target(name: "Game", + dependencies: [ + .product(name: "UtilsProduct", + package: "swift-utils", + moduleAliases: ["Utils": "SwiftUtils"]), + ]) + ] +} +``` + +Once the validation and alias overriding steps pass, dependency resolution will take place using the new module names, and the `-module-alias [name]=[new_name]` flag will be passed to the build execution. ### Resources @@ -191,7 +215,7 @@ Tools invoked by a build system to compile resources should be modified to handl When module aliasing is used, the binary module name will be stored in mangled symbols, e.g. `$s9GameUtils5Level` instead of `$s5Utils5Level`, which will be stored in Debuginfo. -For evaluating an expression, the name Utils can be used as it appears in source files (that were already compiled with module aliasing); however, the result of the evaluation will contain the binary module name. +For evaluating an expression, the name `Utils` can be used as it appears in source files (which were already compiled with module aliasing); however, the result of the evaluation will contain the binary module name. If a module were to be loaded directly into lldb, the binary module name should be used, i.e. `import GameUtils` instead of `import Utils`, since it does not have access to the aliasing flag. @@ -208,7 +232,7 @@ To allow module aliasing, the following requirements need to be met, which come * Higher chance of running into the following existing issues: * [Retroactive conformance](https://forums.swift.org/t/retroactive-conformances-vs-swift-in-the-os/14393): this is already not a recommended practice and should be avoided. * Extension member “leaks”: this is [considered a bug](https://bugs.swift.org/browse/SR-3908) which hasn’t been fixed yet. More discussions [here](https://forums.swift.org/t/pre-pitch-import-access-control-a-modest-proposal/50087). -* Code size increase will be more implicit and requires a caution, although module aliasing will be opt-in and a size threshold could be added to provide a warning. +* Code size increase will be more implicit thus requires a caution, although module aliasing will be opt-in and a size threshold could be added to provide a warning. ## Source compatibility This is an additive feature. Currently when there are duplicate module names, it does not compile at all. This feature requires explicitly opting in to allow and use module aliaisng via package manifests or compiler invocation commands and does not require source code changes. From e7e4cd2819c6d17b69b65db812c396207f5c994a Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 17 May 2022 16:54:06 -0700 Subject: [PATCH 2565/4563] status update --- proposals/0339-module-aliasing-for-disambiguation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0339-module-aliasing-for-disambiguation.md b/proposals/0339-module-aliasing-for-disambiguation.md index a2a467ffeb..8f68460799 100644 --- a/proposals/0339-module-aliasing-for-disambiguation.md +++ b/proposals/0339-module-aliasing-for-disambiguation.md @@ -3,7 +3,7 @@ * Proposal: [SE-0339](0339-module-aliasing-for-disambiguation.md) * Authors: [Ellie Shin](https://github.com/elsh) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** (with modifications not yet applied; see acceptance) +* Status: **Accepted** * Pitch: [Module Aliasing](https://forums.swift.org/t/pitch-module-aliasing/51737) * Implementation: ([toolchain](https://github.com/apple/swift/pull/40899)), [apple/swift-package-manager#4023](https://github.com/apple/swift-package-manager/pull/4023), others From 63908535f0beaa4475b82f1e0734c1a754a09582 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 18 May 2022 16:34:08 -0400 Subject: [PATCH 2566/4563] Assign SE-0358 to the primary associated types adoption proposal and put it in review --- ...n-stdlib.md => 0358-primary-associated-types-in-stdlib.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{nnnn-primary-associated-types-in-stdlib.md => 0358-primary-associated-types-in-stdlib.md} (99%) diff --git a/proposals/nnnn-primary-associated-types-in-stdlib.md b/proposals/0358-primary-associated-types-in-stdlib.md similarity index 99% rename from proposals/nnnn-primary-associated-types-in-stdlib.md rename to proposals/0358-primary-associated-types-in-stdlib.md index 102f7e0b41..542528cb7b 100644 --- a/proposals/nnnn-primary-associated-types-in-stdlib.md +++ b/proposals/0358-primary-associated-types-in-stdlib.md @@ -1,9 +1,9 @@ # Primary Associated Types in the Standard Library -* Proposal: [SE-NNNN](NNNN-primary-associated-types-in-stdlib.md) +* Proposal: [SE-0358](0358-primary-associated-types-in-stdlib.md) * Authors: [Karoy Lorentey](https://github.com/lorentey) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Pitch** +* Status: **Active review (May 18...30, 2022)** * Implementation: [apple/swift#41843](https://github.com/apple/swift/pull/41843) * Review: ([pitch](https://forums.swift.org/t/pitch-primary-associated-types-in-the-standard-library/56426/)) * Related Proposals: From a5578af60903dbb346d50cc697693b99fe5c9ad9 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 18 May 2022 16:40:08 -0400 Subject: [PATCH 2567/4563] Link SE-0358 to its review thread --- proposals/0358-primary-associated-types-in-stdlib.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0358-primary-associated-types-in-stdlib.md b/proposals/0358-primary-associated-types-in-stdlib.md index 542528cb7b..46eea787f9 100644 --- a/proposals/0358-primary-associated-types-in-stdlib.md +++ b/proposals/0358-primary-associated-types-in-stdlib.md @@ -5,7 +5,7 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (May 18...30, 2022)** * Implementation: [apple/swift#41843](https://github.com/apple/swift/pull/41843) -* Review: ([pitch](https://forums.swift.org/t/pitch-primary-associated-types-in-the-standard-library/56426/)) +* Review: ([pitch](https://forums.swift.org/t/pitch-primary-associated-types-in-the-standard-library/56426/)) ([review](https://forums.swift.org/t/se-0358-primary-associated-types-in-the-standard-library/57432)) * Related Proposals: - [SE-0023] API Design Guidelines - [SE-0346] Lightweight same-type requirements for primary associated types From 6aa573de61d4d1653a0288e1ebb6f6f1a27ccd7a Mon Sep 17 00:00:00 2001 From: Ashley Garland Date: Thu, 19 May 2022 13:44:27 -0700 Subject: [PATCH 2568/4563] [SE-0356] Add named snippet slices to future directions --- proposals/0356-swift-snippets.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/proposals/0356-swift-snippets.md b/proposals/0356-swift-snippets.md index 8a0d0943aa..fb22cc73d7 100644 --- a/proposals/0356-swift-snippets.md +++ b/proposals/0356-swift-snippets.md @@ -334,6 +334,27 @@ An important part of snippets’ utility is the shared convention and terminolog **Multi-file snippets.** This could manifest in a couple ways. First, requiring several files to build a snippet already exists in the form sample target or project, so this is probably not a future goal. However, for snippets embedded within existing multi-file projects, it may be possible extract those snippets during build time. This will likely require that the snippet extraction move down to the compiler. +**Snippet slices.** In order to support prose that explains a snippet a small bit at a time, snippets in the future could be cut into multiple named slices. Each slice could then be inlined and author would be free to put prose in between slices. For example: + +```markdown +Paragraph discussing setup... +... + +@Snippet(path: "snippets/ErrorHandling", slice: "setup") + +Paragraph describing throwing an error... +... + +@Snippet(path: "snippets/ErrorHandling", slice: "throw") + +Paragraph describing catching an error... +... + +@Snippet(path: "snippets/ErrorHandling", slice: "catch") +``` + +This would be achieved by adding a start/end marker with a slice name in the snippet itself, likely with a new comment marker. The snippet would still build and run as a single unit, but this would allow authors to insert tangential discussions and links in between slices. + **Extract snippets while building.** To facilitate some of the above future possibilities and others, the `snippet-build` tool may move down to the `SymbolGraphGen` library that coverts modules into Symbol Graph JSON. Since snippets are communicated with the same Symbol Graph format, moving the implementation down to the compiler will allow utilizing shared implementation and semantic information for future enhancements. This would allow snippets to be pulled from different kinds of sources: from libraries, unit tests, larger sample projects, etc. **Build snippets when building documentation.** The current Swift-DocC implementation only requires reading snippet source files when rendering documentation, so building is not required. Depending on whether the implementation is moved down to the compiler, this could be implemented by having the Swift-DocC plugin request snippet builds before generating documentation, or implicitly as the compiler builds snippets to generate symbol graphs. From ef0068368ffaaaae8ed60af158846a7de8790994 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Tue, 24 May 2022 10:33:24 -0700 Subject: [PATCH 2569/4563] Update 0317-async-let (#1661) The behavior of an un-awaited `async let` using the `async let _ =` syntax did not make it clear whether the task was run and awaited with or without cancellation. `swift_asyncLet_finishImpl` will cancel any async let that is not yet awaited. --- proposals/0317-async-let.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0317-async-let.md b/proposals/0317-async-let.md index 6d3a3b6e22..1416501ce1 100644 --- a/proposals/0317-async-let.md +++ b/proposals/0317-async-let.md @@ -327,7 +327,7 @@ func go2() async { The duration of the `go2()` call remains the same, it is always `time(go2) == max(time(f), time(s))`. -Special attention needs to be given to the `async let _ = ...` form of declarations. This form is interesting because it creates a child-task of the right-hand-side initializer, however it actively chooses to ignore the result. Such a declaration (and the associated child-task) will run and be awaited-on implicitly, as the scope it was declared in is about to exit — the same way as an unused `async let` declaration would be. +Special attention needs to be given to the `async let _ = ...` form of declarations. This form is interesting because it creates a child-task of the right-hand-side initializer, however it actively chooses to ignore the result. Such a declaration (and the associated child-task) will run and be cancelled and awaited-on implicitly, as the scope it was declared in is about to exit — the same way as an unused `async let` declaration would be. ### `async let` and closures From 1953c44eed8c7a58f7dbea4d39c7ce1f380d26ff Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 24 May 2022 13:56:38 -0700 Subject: [PATCH 2570/4563] Proposal: Opaque result types with limited availability The same-type `return` requirement is unnecessarily strict when it comes to availability conditions. SE-0244 states that it should be possible to change the underlying type in the future version of the library, but that would only work with pre-existing types. In other words, the same-type condition does not have to apply across executions of the same program, the same way that `Hashable` must produce the same output for the same value during program execution, but may produce a different value in the next execution. `#available` is special because it's a checkable form of that: dynamic availability will not change while the program is running, but may be different the next time the program runs. --- ...N-opaque-result-types-with-availability.md | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 proposals/NNNN-opaque-result-types-with-availability.md diff --git a/proposals/NNNN-opaque-result-types-with-availability.md b/proposals/NNNN-opaque-result-types-with-availability.md new file mode 100644 index 0000000000..115e68223c --- /dev/null +++ b/proposals/NNNN-opaque-result-types-with-availability.md @@ -0,0 +1,146 @@ +# [Pitch] Opaque result types with limited availability + +* Proposal: [SE-NNNN](NNNN-opaque-result-types-with-availability.md) +* Authors: [Pavel Yaskevich](https://github.com/xedin) +* Review Manager: TBD +* Implementation: [apple/swift#42072](https://github.com/apple/swift/pull/42072), [apple/swift#42104](https://github.com/apple/swift/pull/42104), and [apple/swift#42167](https://github.com/apple/swift/pull/42167) +* Status: **Awaiting discussion** + +## Introduction + +Since their introduction in [SE-0244](https://github.com/apple/swift-evolution/blob/main/proposals/0244-opaque-result-types.md), opaque result types have become a powerful tool of type-level abstraction that allows library authors to hide implementation details of their APIs. + +Under the rules described in SE-0244 - a function returning an opaque result type *must return a value of the same concrete type `T`* from each `return` statement, and `T` must meet all of the constraints stated on the opaque type. + +The same-type `return` requirement is unnecessarily strict when it comes to availability conditions. SE-0244 states that it should be possible to change the underlying type in the future version of the library, but that would only work with pre-existing types. In other words, the same-type condition does not have to apply across executions of the same program, the same way that `Hashable` must produce the same output for the same value during program execution, but may produce a different value in the next execution. `#available` is special because it's a checkable form of that: dynamic availability will not change while the program is running, but may be different the next time the program runs. + +Current model and implementation limits usefulness of opaque result types as an abstraction mechanism, because it prevents frameworks from introducing new types and using them as underlying types in existing APIs. To bridge this usability gap, I propose to relax same-type restriction for `return`s inside of availability conditions. + +Swift-evolution thread: [ +[Pitch] Opaque result types with limited availability](https://forums.swift.org/t/pitch-opaque-result-types-with-limited-availability/57286) + +## Motivation + +To illustrate the problematic interaction between opaque result types and availability conditions, let's consider a framework that already has a `Shape` protocol and a `Square` type that conforms to the `Shape` protocol. + +``` +protocol Shape { + func draw(to: Surface) +} + +struct Square : Shape { + ... +} +``` + +In a new version of the framework, the library authors decided to introduce a new shape - `Rectangle` with limited availability: + +``` +@available(macOS 100, *) +struct Rectangle : Shape { + ... +} +``` + +Since a `Rectangle` is generalization of a `Square` it makes sense to allow transforming a `Square` into a `Rectangle` but that currently requires extension with limited availability: + +``` +@available(macOS 100, *) +extension Square { + func asRectangle() -> some Shape { + return Rectangle(...) + } +} +``` + +The fact that the new method has to be declared in availability context to return `Rectangle` limits its usefulness because all uses of `asRectangle` would have be encapsulated into `if #available` blocks. + +If `asRectangle` already existed in the original version of the framework, it wouldn’t be possible to use a new type at all without declaring `if #available` block in its body: + +``` +struct Square { + func asRectangle() -> some Shape { + if #available(macOS 100, *) { + return Rectangle(...) + } + + return self + } +} +``` + +But doing so is not allowed because all of the `return` statements in the body of the `asRectangle` function have to return the same concrete type: + +``` + error: function declares an opaque return type 'some Shape', but the return statements in its body do not have matching underlying types + func asRectangle() -> some Shape { + ^ ~~~~~~~~~~ +note: return statement has underlying type 'Rectangle' + return Rectangle() + ^ +note: return statement has underlying type 'Square' + return Square() + ^ +``` + +This is a dead-end for the library author although SE-0244 states that it should be possible to change underlying result type in the future version of the library/framework but that assumes that the type already exists so it could be used in all `return` statements. + +## Proposed solution + +To bridge this usability gap I propose to relax same-type restriction for `return`s inside of availability conditions as follows: + +A function returning an opaque result type is allowed to return values of different concrete types from conditionally available `if #available` branches without any other dynamic conditions, if and only if, all universally available `return` statements in its body return a value of the same concrete type `T`. All returned values regardless of their location must meet all of the constraints stated on the opaque type. + +## Detailed design + +Proposed changes allow to: + +* Use multiple different `if #available` conditions to return types based on their availability e.g. from the most to least available. +* Safely fallback to a **single** universally available type if none of the conditions are met. + +Note that although it is possible to use multiple availability conditions, mixing conditional availability with dynamic checks would result in `return`s being considered universally available. The following declarations would still be unsupported: + +``` +func asRectangle() -> some Shape { + if , #available(macOS 100, *) { ❌ + return Rectangle() + } + return self +} +``` + +or + +``` +func asRectangle() -> some Shape { + if #available(macOS 100, *) { + if cond { ❌ + return Rectangle() + } else { + return self + } + } + return self +} +``` + +In both of this examples `self` and `Rectangle` would have to be same concrete type which is consistent with existing behavior. + +This semantic adjustment fits well into the existing model because it makes sure that there is always a single underlying type per platform and universally. + +## Source compatibility + +Proposed changes do not break source compatibility and allow previously incorrect code to compile. + +## Effect on ABI stability + +No ABI impact since this is an additive change. + +## Effect on API resilience + +All of the resilience rules associated with opaque result types are preserved. + +## Alternatives considered + +* Only alternative is to change the API patterns used in the library, e.g. by exposing the underlying result type and overloading the method. + From 6b175ff16b8da7c10656a5251da3a706954af5ee Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 24 May 2022 16:09:03 -0700 Subject: [PATCH 2571/4563] SE-0352 and 0353 are accepted --- proposals/0352-implicit-open-existentials.md | 3 ++- proposals/0353-constrained-existential-types.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/0352-implicit-open-existentials.md b/proposals/0352-implicit-open-existentials.md index 2c47bd52e5..7b2dae029c 100644 --- a/proposals/0352-implicit-open-existentials.md +++ b/proposals/0352-implicit-open-existentials.md @@ -3,8 +3,9 @@ * Proposal: [SE-0352](0352-implicit-open-existentials.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Active Review (April 21...May 18, 2022)** +* Status: **Accepted** * Implementation: [apple/swift#41996](https://github.com/apple/swift/pull/41996), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-macos/120/artifact/branch-main/swift-PR-41996-120-osx.tar.gz) +* Decision Notes: [Acceptance](https://forums.swift.org/t/accepted-se-0352-implicitly-opened-existentials/57553) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/77374319a7d70c866bd197faada46ecfce461645/proposals/0352-implicit-open-existentials.md) * Previous Review: [First review](https://forums.swift.org/t/se-0352-implicitly-opened-existentials/56557/52) diff --git a/proposals/0353-constrained-existential-types.md b/proposals/0353-constrained-existential-types.md index 734bd2f589..f4c33704c0 100644 --- a/proposals/0353-constrained-existential-types.md +++ b/proposals/0353-constrained-existential-types.md @@ -3,8 +3,9 @@ * Proposal: [SE-0353](0353-constrained-existential-types.md) * Authors: [Robert Widmann](https://github.com/codafi) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Active review (April 20...May 18, 2022)** +* Status: **Accepted** * Implementation: implemented in `main` branch, under flag `-enable-parameterized-existential-types` +* Decision Notes: [Acceptance](https://forums.swift.org/t/accepted-se-0353-constrained-existential-types/57560) ## Introduction From eacf14b3932e0281a74a2c29da7d8caea58db964 Mon Sep 17 00:00:00 2001 From: YR Chen Date: Wed, 25 May 2022 08:21:02 +0800 Subject: [PATCH 2572/4563] Mark SE-0343 as implemented (#1657) --- proposals/0343-top-level-concurrency.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0343-top-level-concurrency.md b/proposals/0343-top-level-concurrency.md index 148aa99f90..47ab917535 100644 --- a/proposals/0343-top-level-concurrency.md +++ b/proposals/0343-top-level-concurrency.md @@ -3,7 +3,7 @@ * Proposal: [SE-0343](0343-top-level-concurrency.md) * Authors: [Evan Wilde](https://github.com/etcwilde) * Review Manager: [Saleem Abdulrasool](https://github.com/compnerd) -* Status: **Accepted (2022-03-13)** +* Status: **Implemented (Swift 5.7)** * Implementation: [Fix top-level global-actor isolation crash](https://github.com/apple/swift/pull/40963), [Add `@MainActor @preconcurrency` to top-level variables](https://github.com/apple/swift/pull/40998), [Concurrent top-level inference](https://github.com/apple/swift/pull/41061) ## Introduction From 3ff86446fc6c99fe9e4c43973a918b36ec31892b Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Tue, 24 May 2022 21:20:26 -0700 Subject: [PATCH 2573/4563] [Proposal] Build-Time Constant Value Attribute (#1622) * SE-0353: Build-Time Constant Values * Fix placement of '@const' attribute on parameters type * Fixup URL forum thread URL * Fix Typos * Qualify supported types: add booleans and tuples * Add discssion on propagation rules, memory placement and type annotation * Small changes to formatting and adding link to second pitch thread * Add early motivating examples and clarify proposed design examples with valid/invalid inputs * Move and re-title the future-design sections further in the document * Markdown formatting fixes * Add alternative names considered sub-section * Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Update proposals/0358-build-time-constant-values.md Co-authored-by: Ben Rimmington Co-authored-by: Ben Rimmington --- proposals/0358-build-time-constant-values.md | 327 +++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 proposals/0358-build-time-constant-values.md diff --git a/proposals/0358-build-time-constant-values.md b/proposals/0358-build-time-constant-values.md new file mode 100644 index 0000000000..f134288448 --- /dev/null +++ b/proposals/0358-build-time-constant-values.md @@ -0,0 +1,327 @@ +# Build-Time Constant Values + +* Proposal: [SE-NNNN](NNNN-build-time-constant-values.md) +* Authors: [Artem Chikin](https://github.com/artemcm), [Ben Cohen](https://github.com/airspeedswift), [Xi Ge](https://github.com/nkcsgexi) +* Review Manager: Doug Gregor +* Status: **Awaiting Review** +* Implementation: Implemented on `main` as `_const` + +## Introduction + +A Swift language feature for requiring certain values to be knowable at compile-time. This is achieved through an attribute, `@const`, constraining properties and function parameters to have compile-time knowable values. Such information forms a foundation for richer compile-time features in the future, such as extraction and validation of values at compile time. + +Related forum threads: + +* [[Pitch] Compile-Time Constant Values](https://forums.swift.org/t/pitch-compile-time-constant-values/53606) +* [[Pitch #2] Build-Time Constant Values](https://forums.swift.org/t/pitch-2-build-time-constant-values/56762) + +## Motivation + +Compile-time constant values are values that can be known or computed during compilation and are guaranteed to not change after compilation. Use of such values can have many purposes, from enforcing desirable invariants and safety guarantees to enabling users to express arbitrarily-complex compile-time algorithms. + +The first step towards building out support for compile-time constructs in Swift is a basic primitives consisting of an attribute to declare function parameters and properties to require being known at *compile-time*. While this requirement explicitly calls out the compiler as having additional guaranteed information about such declarations, it can be naturally extended into a broader sense of *build-time* known values - with the compiler being able to inform other tools with type-contextual value information. For an example of the latter, see the “Declarative Package Manifest” motivating use-case below. + +## Proposed Solution + +The Swift compiler will recognize declarations of properties, local variables, and function parameters declared with a `@const` attribute as having an additional requirement to be known at compile-time. If a `@const` property or variable is initialized with a runtime value, the compiler will emit an error. Similarly, if a runtime value is passed as an argument to a `@const` function parameter, the compiler will emit an error. Aside from participating in name mangling, the attribute has no runtime effect. + +For example, a `@const` property can provide the compiler and relevant tooling build-time knowledge of a type-specific value: + +```swift +struct DatabaseParams { + @const let encoding: String = "utf-8" + @const let capacity: Int = 256 +} +``` + +A `@const` parameter provides a guarantee of a build-time-known argument being specified at all function call-sites, allowing future APIs to enforce invariants and provide build-time correctness guarantees: + +```swift +func acceptingURLString(@const _ url: String) +``` + +And a `@const static let` protocol property requirement allows protocol authors to enforce get the benefits of build-time known properties for all conforming types: + +```swift +protocol DatabaseSerializableWithKey { + @const static let key: String +} +``` + +## Detailed Design + +### Property `@const` attribute + +A stored property on a `struct` or a `class` can be marked with a `@const` attribute to indicate that its value is known at compile-time. +```swift +struct Foo { + @const let title: String = "foo" +} +``` +The value of such a property must be default-initialized with a compile-time-known value, unlike a plain `let` property, which can also be assigned a value in the type's initializer. + +```swift +struct Foo { + // 👍 + @const let superTitle: String = "Encyclopedia" + // ❌ error: `title` must be initialized with a const value + @const let title: String + // ❌ error: `subTitle` must be initialized with a const value + @const let subTitle: String = bar() +} +``` + +Similarly to Implicitly-Unwrapped Optionals, the mental model for semantics of this attribute is that it is a flag on the declaration that guarantees that the compiler is able to know its value as shared by all instance of the type. For now, `@const let` and `@const static let` are equivalent in what information the `@const` attribute conveys to the compiler. + +### Parameter `@const` attribute + +A function parameter can be marked with a `@const` keyword to indicate that values passed to this parameter at the call-site must be compile-time-known values. + +```swift +func foo(@const input: Int) {...} +``` + +Passing in a runtime value as an argument to `foo` will result in a compilation error: +```swift +foo(11) // 👍 + +let x: Int = computeRuntimeCount() +foo(x) // ❌ error: 'greeting' must be initialized with a const value +``` + +### Protocol `@const` property requirement + +A protocol author may require conforming types to default initialize a given property with a compile-time-known value by specifying it as `@const static let` in the protocol definition. For example: + +```swift +protocol NeedsConstGreeting { + @const static let greeting: String +} +``` +Unlike other property declarations on protocols that require the use of `var` and explicit specification of whether the property must provide a getter `{ get }` or also a setter `{ get set }`, using `var` for build-time-known properties whose values are known to be fixed at runtime is counter-intuitive. Moreover, `@const` implies the lack of a run-time setter and an implicit presence of the value getter. + +If a conforming type initializes `greeting` with something other than a compile-time-known value, a compilation error is produced: + +```swift +struct Foo: NeedsConstGreeting { + // 👍 + static let greeting = "Hello, Foo" +} +struct Bar: NeedsConstGreeting { + // error: ❌ 'greeting' must be initialized with a const value + static let greeting = "\(Bool.random ? "Hello" : "Goodbye"), Bar" +} +``` + +### Supported Types + +The requirement that values of `@const` properties and parameters be known at compile-time restricts the allowable types for such declarations. The current scope of the proposal includes: + +* Enum cases with no associated values +* Certain standard library types that are expressible with literal values +* Integer and Floating-Point types (`(U)?Int(\d*)`, `Float`, `Double`, `Half`), `String` (excluding interpolated strings), `Bool`. +* `Array` and `Dictionary` literals consisting of literal values of above types. +* Tuple literals consisting of the above list items. + +This list will expand in the future to include more literal-value kinds or potential new compile-time valued constructs. + +## Motivating Example Use-Cases + +### Enforcement of Compile-Time Attribute Parameters + +Attribute definitions can benefit from additional guarantees of compile-time constant values. +For example, a `@const` version of the [@Clamping](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md#clamping-a-value-within-bounds) property wrapper that requires that lower and upper bounds be compile-time-known values can ensure that the clamp values are fixed and cannot change for different instantiations of wrapped properties which may occur if runtime values are used to specify the bounds: + +```swift +@propertyWrapper +struct Clamping { + var value: V + let min: V + let max: V + + init(wrappedValue: V, @const min: V, @const max: V) { + value = wrappedValue + self.min = min + self.max = max + assert(value >= min && value <= max) + } + ... +``` +It could also allow the compiler to generate more-efficient comparison and clamping code, in the future. + +Or imagine a property wrapper that declares a property is to be serialized and that it must be stored/retrieved using a specific string key. `Codable` requires users to provide a `CodingKeys` `enum` boilerplate, relying on the `enum`’s `String` raw values. Alternatively, such key can be specified on the property wrapper itself: + +```swift +struct Foo { + @SpecialSerializationSauce(key: "title") + var someSpecialTitleProperty: String +} + +@propertyWrapper +struct SpecialSerializationSauce { + init(@const key: String) {...} +} +``` + +Having the compiler enforce the compile-time constant property of the `key` parameter eliminates the possibility of an error where a run-time value is specified which can cause serialized data to not be able to be deserialized, for example. + +Enforcing compile-time constant nature of the parameters is also the first step to allowing attribute/library authors to be able to check uses by performing compile-time sanity checking and having the capability to emit custom build-time error messages. + +### Enforcement of Non-Failable Initializers + +Ergonomics of the recently-pitched [Foundation.URL](https://forums.swift.org/t/foundation-url-improvements/54057) would benefit greatly from the ability to require the string argument to be compile-time constant. With evolving compile-time evaluation facilities, Swift may even gain an ability to perform compile-time validation of such URLs even though the user may never be able to express a fully compile-time constant `Foundation.URL` type because this type is a part of an ABI-stable SDK. While a type like `StaticString` may be used to require that the argument string must be static, which string is chosen can still be determined at runtime, e.g.: + +```swift +URL(Bool.random() ? "https://valid.url.com" : "invalid url . com") +``` + +### Facilitate Compile-time Extraction of Values + +The [Result Builder-based SwiftPM Manifest](https://forums.swift.org/t/pre-pitch-swiftpm-manifest-based-on-result-builders/53457) pre-pitch outlines a proposal for a manifest format that encodes package model/structure using Swift’s type system via Result Builders. Extending the idea to use the builder pattern throughout can result in a declarative specification that exposes the entire package structure to build-time tools, for example: + +```swift +let package = Package { + Modules { + Executable("MyExecutable", public: true, include: { + Internal("MyDataModel") + }) + Library("MyLibrary", public: true, include: { + Internal("MyDataModel", public: true) + }) + Library("MyDataModel") + Library("MyTestUtilities") + Test("MyExecutableTests", for: "MyExecutable", include: { + Internal("MyTestUtilities") + External("SomeModule", from: "some-package") + }) + Test("MyLibraryTests", for: "MyLibrary") + } + Dependencies { + SourceControl(at: "https://git-service.com/foo/some-package", upToNextMajor: "1.0.0") + } +} +``` + +A key property of this specification is that all the information required to know how to build this package is encoded using compile-time-known concepts: types and literal (and therefore compile-time-known) values. This means that for a category of simple packages where such expression of the package’s model is possible, the manifest does not need to be executed in a sandbox by the Package Manager - the required information can be extracted at manifest *build* time. + +To *ensure* build-time extractability of the relevant manifest structure, a form of the above API can be provided that guarantees the compile-time known properties. For example, the following snippet can guarantee the ability to extract complete required knowledge at build time: + +```swift +Test("MyExecutableTests", for: "MyExecutable", include: { + Internal("MyTestUtilities") + External("SomeModule", from: "some-package") + }) +``` +By providing a specialized version of the relevant types (`Test`, `Internal`, `External`) that rely on parameters relevant to extracting the package structure being `const`: + +```swift +struct Test { + init(@const _ title: String, @const for: String, @DependencyBuilder include: ...) {...} +} +struct Internal { + init(@const _ title: String) +} +struct External { + init(@const _ title: String, @const from: String) +} +``` +This could, in theory, allow SwiftPM to build such packages without executing their manifest. Some packages, of course, could still require run-time (execution at package build-time) Swift constructs. More-generally, providing the possibility of declarative APIs that can express build-time-knowable abstractions can both eliminate (in some cases) the need for code execution - reducing the security surface area - and allow for further novel use-cases of Swift’s DSL capabilities (e.g. build-time extractable database schema, etc.). + +### Guaranteed Optimization Hints + +Similarly, ergonomics of numeric intrinsics can benefit from allowing only certain function parameters to be required to be compile-time known. For example, requiring a given numeric operation to specify a `@const` parameter for the rounding mode of an operation as an enum case, while allowing the operands of the operation be runtime values, allowing the compiler to generate more-efficient code. + +## Source compatibility + +This is a purely additive change and has no source compatibility impacts. + +## Effect on ABI stability and API resilience + +The new function parameter attribute is a part of name mangling. The *value* of `public @const` properties is a part of a module's ABI. See discussion on [*Memory placement*](#memory-placement-and-runtime-initialization) for details. + +## Effect on SwiftPM packages + +There is no impact on SwiftPM packages. + +## Alternatives Considered + +### Using a keyword or an introducer instead of an attribute +`@const` being an attribute, as opposed to a keyword or a new introducer (such as `const` instead of `let`), is an approach that is more amenable to applying to a greater variety of constructs in the futures, in addition to property and parameter declarations, such as `@const func`. In addition, as described in comparison to Implicitly-Unwrapped Optionals above, this attribute does not fundamentally change the behavior of the declaration, rather it restricts its handling by the compiler, similar to `@objc`. + +### Difference to `StaticString`-like types +As described in the **Enforcement of Non-Failable Initializers**, the key difference to types like `StaticString` that require a literal value is the `@const` attribute's requirement that the exact value be known at compile-time. `StaticString` allows for a runtime selection of multiple compile-time known values. + +### Placing `@const` on the declaration type +One alternative to declaring compile-time known values as proposed here with the declaration attribute: + +```swift +@const let x = 11 +``` +Is to instead shift the annotation to declared property's type: + +```swift +let x: @const Int = 11 +``` +This shifts the information conveyed to the compiler about this declaration to be carried by the declaration's type. Semantically, this departs from, and widely broadens the scope from what we intend to capture: the knowability of the declared *value*. Encoding the compile-time property into the type system would force us to reckon with a great deal of complexity and unintended consequences. Consider the following example: + +```swift +typealias CI = @const Int +let x: CI? +``` +What is the type of `x`? It appears to be Optional<@const Int>, which is not a meaningful or useful type, and the programmer most likely intended to have a @const Optional. And although today Implicitly-Unwrapped optional syntax conveys an additional bit of information about the declared value using a syntactic indicator on the declared type, without affecting the declaration's type, the [historical context](https://www.swift.org/blog/iuo/) of that feature makes it a poor example to justify requiring consistency with it. + +### Alternative attribute names +More-explicit spellings of the attribute's intent were proposed in the form of `@buildTime`/`@compileTime`/`@comptime`, and the use of `const` was also examined as a holdover from its use in C++. + +While build-time knowability of values this attribute covers is one of the intended semantic takeaways, the potential use of this attribute for various optimization purposes also lends itself to indicate the additional immutability guarantees on top of a plain `let` (which can be initialized with a dynamic value), as well as capturing the build-time evaluation/knowledge signal. For example, in the case of global variables, thread-safe lazy initialization of `@const` variables may no longer be necessary, in which case the meaning of the term `const` becomes even more explicit. + +Similarly with the comparison to C++, where the language uses the term `const` to describe a runtime behaviour concept, rather than convey information about the actual value. The use of the term `const` is more in line with the mathematical meaning of having the value be a **defined** constant. + +## Forward-Looking Design Aspects and Future Directions + +### Future Inference/Propagation Rules +Though this proposal does **not** itself introduce rules of `@const` inference, their future existence is worth discussing in this design. Consider the example: + +```swift +@const let i = 1 +let j = i +``` +While not valid under this proposal, our intent is to allow the use of `i` where `@const` values are expected in the future, for example `@const let k = i` or `f(i)` where `f` is `func f(@const _: Int)`. It is therefore important to consider whether `@const` is propagated to values like `j` in the above example, which determines whether or not statements like `f(j)` and `@const let k = j` are valid code. While it is desirable to allow such uses of the value within the same compilation unit, if `j` is `public`, automatically inferring it to be `@const` is problematic at the module boundary: it creates a contract with the module's clients that the programmer may not have intended. Therefore, `public` properties must explicitly be marked `@const` in order to be accessible as such outside the defining module. This is similar in nature to `Sendable` inference - `internal` or `private` entities can automatically be inferred by the compiler as `Sendable`, while `public` types must explicitly opt-in. + +### Memory placement and runtime initialization +Effect on runtime placement of `@const` values is an implementation detail that this proposal does not cover beyond indicating that today this attribute has no effect on memory layout of such values at runtime. It is however a highly desirable future direction for the implementation of this feature to allow the use of read-only memory for `@const` values. With this in mind, it is important to allow semantics of this attribute to allow such implementation in the future. For example, a global `@const let`, by being placed into read-only memory removes the need for synchronization on access to such data. Moreover, using read-only memory reduces memory pressure that comes from having to maintain all mutable state in-memory at a given program point - read-only data can be evicted on-demand to be read back later. These are desirable traits for optimization of existing programs which become increasingly important for enabling of low-level system programs to be written in Swift. + +In order to allow such implementation in the future, this proposal makes the *value* of `public` `@const` values/properties a part of a module's ABI. That is, a resilient library that vends `@const let x = 11` changing the value of `x` is considered an ABI break. This treatment allows `public` `@const` data to exist in a single read-only location shared by all library clients, without each client having to copy the value or being concerned with possible inconsistency in behavior across library versions. + +## Future Directions + +### Constant-propagation +Allow default-initialization of `@const` properties using other `@const` values and allow passing `@const` values to `@const` parameters. The [Future Inference/Propagation Rules](#future-inferencepropagation-rules) section discusses a direction for enabling inference of the attribute on values. This is a necessary next building-block to generalizing the use of compile-time values. + +```swift +func foo(@const i: Int) { + @const let j = i +} +``` + +### Toolchain support for extracting compile-time values at build time. +The current proposal covers an attribute that allows clients to build an API surface that is capable of carrying semantic build-time information that may be very useful to build-time tooling, such as [SwiftPM plugins](https://github.com/apple/swift-evolution/blob/main/proposals/0303-swiftpm-extensible-build-tools.md). The next step towards this goal would include toolchain support for tooling that extracts such information in a client-agnostic fashion so that it can be adopted equally by use-cases like the manifest example in [Facilitate Compile-time Extraction of Values](#facilitate-compile-time-extraction-of-values) and others. + +### Compile-time expressions and functions +Building on propagation and inference of `@const`, some of the most interesting use-cases for compile-time-known values emerge with the ability to perform operations on them that result in other compile-time-known values. For example, the [Compiler Diagnostic Directives](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) could be expanded to trigger conditionally based on a value of a compile-time-known input expression: + +```swift +func foo(@const input: Int) { + #const_assert(input <= 0, "'foo()' expects a positive input") +} +``` +Which would require that it is possible to evaluate the `input <= 0` expression at compile-time, which would also require that certain functions can be evaluated at compile-time if their arguments are known at compile-time. For example: + + +```swift +func <=(@const lhs: Int, @const rhs: Int) -> Bool +``` +Inference on which functions can or cannot be evaluated at compile time will be defined in a future proposal and can follow similar ideas to those described in [Future Inference/Propagation Rules](#future-inferencepropagation-rules) section. + +### Compile-time types +Finally, the flexibility of build-time values and code that operates on them can be greatly expanded by allowing entire user-defined types to form compile-time-known values via either custom literal syntax or having a `@const` initializer. \ No newline at end of file From cb605f259eeb2718434e15a32beb0ba30dedcbe8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 24 May 2022 21:23:26 -0700 Subject: [PATCH 2574/4563] Dub Build-Time Constant Values proposal as SE-0359 and initiate review (#1665) --- ...onstant-values.md => 0359-build-time-constant-values.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{0358-build-time-constant-values.md => 0359-build-time-constant-values.md} (99%) diff --git a/proposals/0358-build-time-constant-values.md b/proposals/0359-build-time-constant-values.md similarity index 99% rename from proposals/0358-build-time-constant-values.md rename to proposals/0359-build-time-constant-values.md index f134288448..2cc5d80ac2 100644 --- a/proposals/0358-build-time-constant-values.md +++ b/proposals/0359-build-time-constant-values.md @@ -1,9 +1,9 @@ # Build-Time Constant Values -* Proposal: [SE-NNNN](NNNN-build-time-constant-values.md) +* Proposal: [SE-0359](0359-build-time-constant-values.md) * Authors: [Artem Chikin](https://github.com/artemcm), [Ben Cohen](https://github.com/airspeedswift), [Xi Ge](https://github.com/nkcsgexi) -* Review Manager: Doug Gregor -* Status: **Awaiting Review** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active Review (May 24...June 6, 2022)** * Implementation: Implemented on `main` as `_const` ## Introduction From 45dbab252a504c5402e7b7de16da155b465b858c Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 25 May 2022 09:08:34 -0700 Subject: [PATCH 2575/4563] Mark a few accepted proposals as implemented. (#1666) --- proposals/0329-clock-instant-duration.md | 2 +- proposals/0346-light-weight-same-type-syntax.md | 2 +- proposals/0347-type-inference-from-default-exprs.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0329-clock-instant-duration.md b/proposals/0329-clock-instant-duration.md index 12f26c2ab2..743e21a61d 100644 --- a/proposals/0329-clock-instant-duration.md +++ b/proposals/0329-clock-instant-duration.md @@ -3,7 +3,7 @@ * Proposal: [SE-0329](0329-clock-instant-duration.md) * Author: [Philippe Hausler](https://github.com/phausler) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Implementation: [apple/swift#40609](https://github.com/apple/swift/pull/40609) * Review: ([first review](https://forums.swift.org/t/se-0329-clock-instant-date-and-duration/53309)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0329-clock-instant-date-and-duration/53635)) ([second review](https://forums.swift.org/t/se-0329-second-review-clock-instant-and-duration/54509)) ([third review](https://forums.swift.org/t/se-0329-third-review-clock-instant-and-duration/54727)) ([acceptance](https://forums.swift.org/t/accepted-se-0329-clock-instant-and-duration/55324)) diff --git a/proposals/0346-light-weight-same-type-syntax.md b/proposals/0346-light-weight-same-type-syntax.md index 929af4535b..a4c47112f5 100644 --- a/proposals/0346-light-weight-same-type-syntax.md +++ b/proposals/0346-light-weight-same-type-syntax.md @@ -3,7 +3,7 @@ * Proposal: [SE-0346](0346-light-weight-same-type-syntax.md) * Authors: [Pavel Yaskevich](https://github.com/xedin), [Holly Borla](https://github.com/hborla), [Slava Pestov](https://github.com/slavapestov) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Implementation: Principally [#40714](https://github.com/apple/swift/pull/40714), [#41640](https://github.com/apple/swift/pull/41640); in `main`, enabled by the experimental `-Xfrontend -enable-parameterized-protocol-types` flag * Previous Revisions: [1st](https://github.com/apple/swift-evolution/blob/5d86d57cfd6d803df4da90b196682d495e5de9b9/proposals/0346-light-weight-same-type-syntax.md) * Review: ([first pitch](https://forums.swift.org/t/pitch-light-weight-same-type-constraint-syntax/52889)) ([second pitch](https://forums.swift.org/t/pitch-2-light-weight-same-type-requirement-syntax/55081)) ([first review](https://forums.swift.org/t/se-0346-lightweight-same-type-requirements-for-primary-associated-types/55869)) ([second review](https://forums.swift.org/t/se-0346-second-review-lightweight-same-type-requirements-for-primary-associated-types/56414)) ([acceptance](https://forums.swift.org/t/accepted-se-0346-lightweight-same-type-requirements-for-primary-associated-types/56747)) diff --git a/proposals/0347-type-inference-from-default-exprs.md b/proposals/0347-type-inference-from-default-exprs.md index 71c4e3bf80..1f1b90eda3 100644 --- a/proposals/0347-type-inference-from-default-exprs.md +++ b/proposals/0347-type-inference-from-default-exprs.md @@ -3,7 +3,7 @@ * Proposal: [SE-0347](0347-type-inference-from-default-exprs.md) * Authors: [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0347-type-inference-from-default-expressions/56558) * Implementation: [apple/swift#41436](https://github.com/apple/swift/pull/41436) From 1dce1f1a2babe507f75ab5394062e0b77b5b27fb Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 25 May 2022 10:00:10 -0700 Subject: [PATCH 2576/4563] Mark distributed actor proposals as implemented. (#1667) --- proposals/0336-distributed-actor-isolation.md | 2 +- proposals/0344-distributed-actor-runtime.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0336-distributed-actor-isolation.md b/proposals/0336-distributed-actor-isolation.md index d17110b3a4..6b641d4b0e 100644 --- a/proposals/0336-distributed-actor-isolation.md +++ b/proposals/0336-distributed-actor-isolation.md @@ -3,7 +3,7 @@ * Proposal: [SE-0336](0336-distributed-actor-isolation.md) * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [Pavel Yaskevich](https://github.com/xedin), [Doug Gregor](https://github.com/DougGregor), [Kavon Farvardin](https://github.com/kavon) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Decision Notes: [Acceptance](https://forums.swift.org/t/accepted-se-0336-distributed-actor-isolation/54726) * Implementation: * Partially available in [recent `main` toolchain snapshots](https://swift.org/download/#snapshots) behind the `-enable-experimental-distributed` feature flag. diff --git a/proposals/0344-distributed-actor-runtime.md b/proposals/0344-distributed-actor-runtime.md index 4355002842..9b831d70be 100644 --- a/proposals/0344-distributed-actor-runtime.md +++ b/proposals/0344-distributed-actor-runtime.md @@ -3,7 +3,7 @@ * Proposal: [SE-0344](0344-distributed-actor-runtime.md) * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [Pavel Yaskevich](https://github.com/xedin), [Doug Gregor](https://github.com/DougGregor), [Kavon Farvardin](https://github.com/kavon), [Dario Rexin](https://github.com/drexin), [Tomer Doron](https://github.com/tomerd) * Review Manager: [Joe Groff](https://github.com/jckarter/) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Implementation: * Partially available in [recent `main` toolchain snapshots](https://swift.org/download/#snapshots) behind the `-enable-experimental-distributed` feature flag. * This flag also implicitly enables `-enable-experimental-concurrency`. From 6aa955c992edda372f8750434fd58620fd79bf2e Mon Sep 17 00:00:00 2001 From: tomer doron Date: Thu, 26 May 2022 12:35:05 -0700 Subject: [PATCH 2577/4563] mark SE-0339 as implemented --- proposals/0339-module-aliasing-for-disambiguation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0339-module-aliasing-for-disambiguation.md b/proposals/0339-module-aliasing-for-disambiguation.md index 8f68460799..bdf4754ea5 100644 --- a/proposals/0339-module-aliasing-for-disambiguation.md +++ b/proposals/0339-module-aliasing-for-disambiguation.md @@ -3,7 +3,7 @@ * Proposal: [SE-0339](0339-module-aliasing-for-disambiguation.md) * Authors: [Ellie Shin](https://github.com/elsh) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Pitch: [Module Aliasing](https://forums.swift.org/t/pitch-module-aliasing/51737) * Implementation: ([toolchain](https://github.com/apple/swift/pull/40899)), [apple/swift-package-manager#4023](https://github.com/apple/swift-package-manager/pull/4023), others From 450b57c81a9b5cc1d4fa86019074b07abc26bb61 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 27 May 2022 09:21:52 -0600 Subject: [PATCH 2578/4563] Rework as methods to initializers or other names --- ...0355-regex-syntax-run-time-construction.md | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/proposals/0355-regex-syntax-run-time-construction.md b/proposals/0355-regex-syntax-run-time-construction.md index f316113d54..d78c0cac18 100644 --- a/proposals/0355-regex-syntax-run-time-construction.md +++ b/proposals/0355-regex-syntax-run-time-construction.md @@ -99,21 +99,23 @@ We propose `AnyRegexOutput` for capture types not known at compilation time, alo ```swift /// A type-erased regex output public struct AnyRegexOutput { - /// Creates a type-erased regex output from an existing output. + /// Creates a type-erased regex output from an existing match. /// - /// Use this initializer to fit a regex with strongly typed captures into the + /// Use this initializer to fit a regex with statically-typed captures into the /// use site of a dynamic regex, i.e. one that was created from a string. public init(_ match: Regex.Match) - /// Returns a typed output by converting the underlying value to the specified - /// type. + /// Returns a statically-typed output by converting dynamic values to the specified type. /// /// - Parameter type: The expected output type. /// - Returns: The output, if the underlying value can be converted to the - /// output type, or nil otherwise. - public func `as`(_ type: Output.Type) -> Output? + /// output type; otherwise `nil`. + public func extractValues( + as type: Output.Type = Output.self + ) -> Output? } extension AnyRegexOutput: RandomAccessCollection { + /// An individual output value. public struct Element { /// The range over which a value was captured. `nil` for no-capture. public var range: Range? { get } @@ -123,6 +125,9 @@ extension AnyRegexOutput: RandomAccessCollection { /// The captured value. `nil` for no-capture. public var value: Any? { get } + + /// The name of this capture, if it has one, otherwise `nil`. + public var name: String? } // Trivial collection conformance requirements @@ -150,14 +155,6 @@ extension Regex.Match where Output == AnyRegexOutput { /// Use this initializer to fit a regex match with strongly typed captures into the /// use site of a dynamic regex match, i.e. one that was created from a string. public init(_ match: Regex.Match) - - /// Returns a typed match by converting the underlying values to the specified - /// types. - /// - /// - Parameter type: The expected output type. - /// - Returns: A match generic over the output type if the underlying values can be converted to the - /// output type. Returns `nil` otherwise. - public func `as`(_ type: Output.Type) -> Regex.Match? } extension Regex where Output == AnyRegexOutput { @@ -165,14 +162,16 @@ extension Regex where Output == AnyRegexOutput { /// /// Use this initializer to fit a regex with strongly typed captures into the /// use site of a dynamic regex, i.e. one that was created from a string. - public init(_ match: Regex) + public init(_ regex: Regex) +} - /// Returns a typed regex by converting the underlying types. +extension Regex { + /// Creates a strongly-typed regex from a dynamic regex. /// - /// - Parameter type: The expected output type. - /// - Returns: A regex generic over the output type if the underlying types can be converted. - /// Returns `nil` otherwise. - public func `as`(_ type: Output.Type) -> Regex? + /// Use this initializer to create a strongly typed regex from + /// one that was created from a string. Returns `nil` if the types + /// don't match. + public init?(_ dynamic: Regex) } ``` @@ -1043,6 +1042,11 @@ Regex syntax will become part of Swift's source and binary-compatibility story, Even though it is more work up-front and creates a longer proposal, it is less risky to support the full intended syntax. The proposed superset maximizes the familiarity benefit of regex syntax. +### Future: Capture descriptions on Regex + +Future API could include a description of the capture list that a regex contains, provided as a collection of optionally-named captures and their types. This would further enhance dynamic regexes. + + -We would like to use the verb `update` instead of `assign`, in order to better communicate the intent of the API. It is currently a common programmer error to use one of the existing `assign` functions for uninitialized memory; using the verb `update` instead would express the precondition in the API itself. +We would like to use the verb `update` instead of `assign`, in order to better communicate the intent of the API. It is currently a common programmer error to use one of the existing `assign` functions for uninitialized memory; using the verb `update` instead would express the precondition in the API name itself. The methods that initialize or update from a `Collection` will have forgiving semantics, and copy the number of elements that they can, be that every available element or none, and then return the index in the buffer that follows the last element copied, which is cheaper than returning an iterator and a count. Unlike the existing `Sequence` functions, they include no preconditions beyond having a valid `Collection` and valid buffer, with the understanding that if a user needs stricter behaviour, it can be composed from these functions. -The above changes include a method to update a single element. Evidently that is a synonym for the `subscript(_ i: Index)` setter. We hope that documenting the update action specifically will help clarify the requirements of that action, namely that the buffer element must already be initialized. Experience shows that the initialization requirement of the subscript setter is frequently missed by users in the current situation, where it is only documented along with the subscript getter. +The above changes include a method to update a single element. Evidently that is a synonym for the `subscript(_ i: Index)` setter. We hope that documenting the update action specifically will help clarify the requirements of that action, namely that the buffer element must already be initialized. Experience shows that the initialization requirement of the subscript setter is frequently not noticed by users in the current situation, where it is only documented along with the subscript getter. ##### `UnsafeMutablePointer` @@ -1604,9 +1604,9 @@ The proposal includes the renaming of four existing functions from `assign` to ` ## Effect on ABI stability -The functions proposed here are generally small wrappers around existing functionality. We expect to implement them as `@_alwaysEmitIntoClient` functions, which means they would have no ABI impact. +The functions proposed here are generally small wrappers around existing functionality. They are implemented as `@_alwaysEmitIntoClient` functions, which means they have no ABI impact. -The renamed functions can reuse the existing symbol, while the deprecated functions can forward using an `@_alwaysEmitIntoClient` stub to support the functionality under its previous name. This means they would have no ABI impact. +The renamed functions can reuse the existing symbol, while the deprecated functions can forward using an `@_alwaysEmitIntoClient` stub to support the functionality under its previous name. The renamings would therefore have no ABI impact. ## Effect on API resilience @@ -1620,18 +1620,18 @@ All functionality implemented as `@_alwaysEmitIntoClient` will back-deploy. Rena The single-element update functions, `UnsafeMutablePointer.update(to:)` and `UnsafeMutableBufferPointer.updateElement(at:to:)`, are synonyms for the setters of `UnsafeMutablePointer.pointee` and `UnsafeMutableBufferPointer.subscript(_ i: Index)`, respectively. Clearly we can elect to not add them. -The setters in question, like the update functions, have a required precondition that the memory they refer to must be initialized. Somehow this precondition is often overlooked and leads to bug reports. The proposed names and cross-references should help clarify the requirements to users. +The setters in question, like the update functions, have a required precondition that the memory they refer to must be initialized. This precondition is often overlooked and leads to programmer errors and bug reports. The proposed names and cross-references should help clarify the requirements to users. ##### Renaming `assign` to `update` -The renaming of `assign` to `update` could be omitted entirely, although we believe that `update` communicates intent much better than `assign` does. In _The Swift Programming Language_, the `=` symbol is named "the assignment operator", and its function is described as to either initialize or to update a value. The current name (`assign`) is not as clear as the documentation in *TSPL*, while the proposed name (`update`) builds on it. +The renaming of `assign` to `update` could be omitted entirely, although we believe the word "update" communicates the API's intent much better than does the word "assign". In _The Swift Programming Language_ (_TSPL_,) the `=` symbol is named "the assignment operator", and its function is described as to either "initialize" or "update" a value. The current name (`assign`) seemingly conflates the two roles of `=` as described in *TSPL*, while the proposed name (`update`) builds on _TSPL_. There are only four current symbols to be renamed by this proposal, and their replacements are easily migrated by a fixit. For context, this renaming would change only 6 lines of code in the standard library, outside of the function definitions. If the renaming is omitted, the four new functions proposed in the family should use the name `assign` as well. The two single-element versions would be `assign(_ value:)` and `assignElement(at:_ value:)`. ##### Element-by-element copies from `Collection` inputs -The initialization and updating functions that copy from `Collection` inputs use the argument label `fromElements`. This is different from the pre-existing functions that copy from `Sequence` inputs. We could use the same argument label (`from`) as with the `Sequence` inputs, but that would mean that we must return the `Iterator` for the `Collection` versions, and that is generally not desirable, especially if a particular `Iterator` cannot be copied cheaply. If we did not return `Iterator`, then the `Sequence` and `Collection` versions of the `initialize(from:)` would be overloaded by their return type, and that would be source-breaking: -an existing use of the current function that doesn't destructure the returned tuple on assignment could now pick up the `Collection` overload, which would have a return value incompatible with the existing code which assumes that the return value is of type `(Iterator, Int)`. +The initialization and updating functions that copy from `Collection` inputs use the argument label `fromElements`. This is a different label than used by the pre-existing functions that copy from `Sequence` inputs. We could use the same argument label (`from`) as with the `Sequence` inputs, but that would mean that we must return the `Iterator` for the `Collection` versions, and that is not necessarily desirable, especially if a particular `Iterator` cannot be copied cheaply. If we used the same argument label (`from`) and did not return `Iterator`, then the `Sequence` and `Collection` versions of the `initialize(from:)` would be overloaded by their return type, and that would be source-breaking: +an existing use of the current function that doesn't destructure the returned tuple on assignment could now pick up the `Collection` overload, which would have a return value incompatible with the subsequent code which assumes that the return value is of type `(Iterator, Int)`. ## Acknowledgments From 9ad10fbebfe61666b547c4b05c6424ce20736673 Mon Sep 17 00:00:00 2001 From: Mykola Pokhylets Date: Tue, 12 Jul 2022 23:50:38 +0200 Subject: [PATCH 2631/4563] Added section about ObjC interaction and updated considerations about task-local values --- proposals/NNNN-isolated-synchronous-deinit.md | 73 ++++++++++++++++--- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/proposals/NNNN-isolated-synchronous-deinit.md b/proposals/NNNN-isolated-synchronous-deinit.md index 0b49d8aee5..64e35fbe0a 100644 --- a/proposals/NNNN-isolated-synchronous-deinit.md +++ b/proposals/NNNN-isolated-synchronous-deinit.md @@ -96,7 +96,9 @@ actor Clicker { ### Runtime -Proposal introduces new runtime function that is used to schedule task-less block of synchronous code on the executor by wrapping it into an ad hoc task. If no switching is needed, block is executed immediately on the current thread. It does not do any reference counting and can be safely used even with references that were released for the last time but not deallocated yet. +Proposal introduces new runtime function that is used to schedule task-less block of synchronous code on the executor by wrapping it into an ad hoc task. It does not do any reference counting and can be safely used even with references that were released for the last time but not deallocated yet. + +If no switching is needed, block is executed immediately on the current thread. Otherwise, task-less job copies priority and task-local values from the task/thread that released the last strong reference to the object. ```cpp using AdHocWorkFunction = SWIFT_CC(swift) void (void *); @@ -142,7 +144,7 @@ actor MyActor { } ``` -If there is explicit `deinit`, then isolation is computed following usual rules for isolation of class instance members. It takes into account isolation attributes on the parent class, deinit itself, and allows `nonisolated` keyword to be used to supress isolation of the parent class: +If there is an explicit `deinit`, then isolation is computed following usual rules for isolation of class instance members. It takes into account isolation attributes on the `deinit` itself, isolation of the `deinit` in the superclass, and isolation attributes on the containing class. If deinit belongs to an actor or GAIT, but isolation of the `deinit` is undesired, it can be supressed using `nonisolated` attribute: ```swift @MainActor @@ -198,10 +200,47 @@ class Derived3: IsolatedBase { } ``` +Note that this is opposite from the rules for overriding functions. That's because in case of `deinit`, isolation applies to the non-deallocating `deinit`, while overriding happens for `__deallocating_deinit`. `__deallocating_deinit` is always non-isolated, and is resposible for switching to correct executor before calling non-deallocting `deinit`. Non-deallocating `deinit` of the subclass calls non-deallocating `deinit` of the superclass. And it is allowed to call nonisolated function from isolated one. + +### Interaction with ObjC + +Implemented mechanism is not available to the ObjC code, so marking ObjC classes as isolated on global actor using `__attribute__((swift_attr(..)))` has no effect on behaviour of the ObjC code. +Such classes are imported into Swift as having non-isolated `deinit`. + +But if `__attribute__((swift_attr(..)))` is used in `@interface` to explicitly mark `dealloc` method as isolated on global actor, then it is imported as isolated `deinit`. Marking `dealloc` as isolated means that `dealloc` must be called only on that executor. It is assumed that ObjC implementation of such class ensures this by overriding `retain/release`. `deinit` of the Swift subclasses is generated as an override of the `dealloc` method. Any pre-conditions which hold for the base class will be true for Swift subclasses as well. In this case `deinit` of the Swift subclass is type-checked as isolated, but isolation thunk is not generated for code size and performance optimization. + +If `deinit` isolation was intoduced into hierarchy of the `@objc` Swift classes by a class implemented in Swift, then `retain/release` are not overriden, `dealloc` can be called from any thread, but isolation happens inside `dealloc` implementation. In this case, isolation thunks will be generated for each isolated `deinit` in the hierarchy. Only the most derived one may can do actual switching. The rest will be called already on the correct executor, and will follow the fast path in `swift_task_performOnExecutor()`. + +ObjC classes that isolate `dealloc` by overriding `retain/release` SHOULD mark `dealloc` as isolated. This not only allows Swift subclasses to fully benefit from isolation, but also prevents them from isolating their `deinit/dealloc` (including the call to `[super dealloc]`) on a different actor. + +On the other hand, if ObjC classes implement isolation by switching executors inside `dealloc`, they SHOULD NOT mark `dealloc` as isolated. Such `dealloc` can be called from any thread, and does not prevent Swift subclasses from isolating on different actor. And skipping isolation thunk in the Swift subclasses would be incorrect. + +```objc +// Non-ARC code + +// Executes on main queue/thread +- (void)dealloc_impl { + ... + [super dealloc]; +} + +static void dealloc_impl_helper(void *ctx) { + [(MyClass*)ctx dealloc_impl]; +} + +// SHOULD NOT be marked as isolated! +// Executes on any thread +- (void)dealloc { + dispatch_async_f(dispatch_get_main_queue(), self, dealloc_impl_helper); +} +``` + ## Source compatibility Proposal makes previously invalid code valid. +Proposal may alter behaviour of existing GAIT and actors when last release happens on the different actor. It is unlikely that `deinit` of GAIT or an actor would have a synchronous externally-observable side effect that can be safely executed on a different actor. + ## Effect on ABI stability Proposal does not change ABI of the existing language features, but introduces new runtime function. @@ -218,20 +257,32 @@ Changing deinitializer from isolated to nonisolated does not break ABI. Any unmo ## Future Directions -### Managing priority of the deinitialization job - -Currently job created for the isolated `deinit` inherits priority of the task/thread that performed last release. If there are references from different tasks/threads, value of this priority is racy. It is impossible to reason about which tasks/threads can reference the object based on local analysis, especially since object can be referenced by other objects. Ideally deinitialization should happen with a predictable priority. Such priority could be set using attributes on the deinitializer or be provided by the isolating actor. - -### Clearing task local values - -Current implementation preserves task local values if last release happened on the desired executor, or runs deinitializer without any task local values. Ideally deinitialization should happen with a predictable state of task local values. This could be achieved by blocking task-local values even if deinitializer is immediately executed. This could implemented as adding a node that stops lookup of the task-local values into the linked list. - ### Asynchronous `deinit` -Similar approach can be used to start a detached task for executing `async` deinit, but this is out of scope of this proposal. +Similar approach can be used to start an unstructured task for executing `async` deinit, but this is out of scope of this proposal. ## Alternatives considered ### Placing hopping logic in `swift_release()` instead. `UIView` and `UIViewController` implement hopping to the main thread by overriding `release` method. But in Swift there are no vtable/wvtable slots for releasing, and adding them would also affect a lot of code that does not need isolated deinit. + +### Deterministic priority and task local values + +When switching executors, current implementation copies priority and task-local values from the task/thread where the last release happened. This minimises differences between isolated and nonisolated `deinit`'s. + +If there are references from different tasks/threads, values of the priority and task-local values observed by the `deinit` are racy, both for isolated and nonsiolated `deinit`'s. + +One way of making task-local values predictable would be to clear them for the duration of the `deinit` execution. This can be implemented efficiently, but would be too restrictive. If object is referenced in several tasks which all have a common parent, then `deinit` can reliably use task-local values which are known to be set in the parent task and not overriden in the child tasks. + +If there is a demand for resetting task-local values, it can be implemented separately as an API: + +```swift +// Temporary resets all task-local values to their defaults +// by appending a stop-node to the linked-list of task-locals +func withoutTaskLocalValues(operation: () throws -> Void) rethrows +``` + +Making priority deterministic would require switching jobs even when last release happened on the desired executor. For now, there is no evidence that deterministic priority wouild be worth taking a performance hit. + +### Explicit opt-in into deinit isolation From 84b627b6b0425659c006af15714967095b6a55ad Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 13 Jul 2022 10:03:14 -0700 Subject: [PATCH 2632/4563] SE-0353 and SE-0361 are implemented in Swift 5.7. (#1711) --- proposals/0353-constrained-existential-types.md | 2 +- proposals/0361-bound-generic-extensions.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0353-constrained-existential-types.md b/proposals/0353-constrained-existential-types.md index f4c33704c0..bb89a02536 100644 --- a/proposals/0353-constrained-existential-types.md +++ b/proposals/0353-constrained-existential-types.md @@ -3,7 +3,7 @@ * Proposal: [SE-0353](0353-constrained-existential-types.md) * Authors: [Robert Widmann](https://github.com/codafi) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Implementation: implemented in `main` branch, under flag `-enable-parameterized-existential-types` * Decision Notes: [Acceptance](https://forums.swift.org/t/accepted-se-0353-constrained-existential-types/57560) diff --git a/proposals/0361-bound-generic-extensions.md b/proposals/0361-bound-generic-extensions.md index caed685f66..a33895fa04 100644 --- a/proposals/0361-bound-generic-extensions.md +++ b/proposals/0361-bound-generic-extensions.md @@ -3,7 +3,7 @@ * Proposal: [SE-0361](0361-bound-generic-extensions.md) * Authors: [Holly Borla](https://github.com/hborla) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Implementation: [apple/swift#41172](https://github.com/apple/swift/pull/41172), gated behind the frontend flag `-enable-experimental-bound-generic-extensions` * Review: ([pitch](https://forums.swift.org/t/pitch-extensions-on-bound-generic-types/57535/)) ([review](https://forums.swift.org/t/se-0361-extensions-on-bound-generic-types/58366)) ([acceptance](https://forums.swift.org/t/accepted-se-0361-extensions-on-bound-generic-types/58716)) From d15aa588558a1e04db8868b4a9d3e52b84c0b51d Mon Sep 17 00:00:00 2001 From: Harlan Haskins Date: Wed, 13 Jul 2022 12:26:19 -0600 Subject: [PATCH 2633/4563] Warning for Retroactive Conformances of External Types in Resilient Libraries (#1678) * Initial retroactive conformances pitch * Rename XXXX-retroactive-conformance-warning.md to 0364-retroactive-conformance-warning.md * Update 0364-retroactive-conformance-warning.md Co-authored-by: Stephen Canon --- .../0364-retroactive-conformance-warning.md | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 proposals/0364-retroactive-conformance-warning.md diff --git a/proposals/0364-retroactive-conformance-warning.md b/proposals/0364-retroactive-conformance-warning.md new file mode 100644 index 0000000000..06a7780981 --- /dev/null +++ b/proposals/0364-retroactive-conformance-warning.md @@ -0,0 +1,100 @@ +# Warning for Retroactive Conformances of External Types in Resilient Libraries + +* Proposal: [SE-0364](0364-retroactive-conformance-warning.md) +* Author: [Harlan Haskins](https://github.com/harlanhaskins) +* Review Manager: [Steve Canon](https://github.com/stephentyrone) +* Status: **Active Review (13 July - 27 July, 2022)** +* Implementation: [apple/swift#36068](https://github.com/apple/swift/pull/36068) + +## Introduction + +Many Swift libraries vend currency protocols, like Equatable, Hashable, Codable, +among others, that unlock worlds of common functionality for types that conform +to them. Sometimes, if a type from another module does not conform to a common +currency protocols, developers will declare a conformance of that type to that +protocol within their module. However, protocol conformances are globally unique +within a process in the Swift runtime, and if multiple modules declare the same +conformance, it can cause major problems for library clients and hinder the +ability to evolve libraries over time. + +## Motivation + +Consider a library that, for one of its core APIs, declares a conformance of +`Date` to `Identifiable`, in order to use it with an API that diffs elements +of a collection by their identity. + +```swift +// Not a great implementation, but I suppose it could be useful. +extension Date: Identifiable { + public var id: TimeInterval { timeIntervalSince1970 } +} +``` + +Now that this client has declared this conformance, if Foundation decides to +add this conformance in a later revision, this client will fail to build. + +If this is an app client, that might be okay --- the breakage will be confined +to their process, and it's their responsibility to remove their conformance, +rebuild, and resubmit their app or redeploy their service. + +However, if this is a library target, this conformance propagates down to every +client that imports the library. This is especially bad for frameworks that +are built with library evolution enabled, as their clients link against +binary frameworks and usually are not aware these conformances don't come from +the actual owning module. + +## Proposed solution + +This proposal adds a warning when library evolution is enabled that explicitly +calls out this pattern as problematic and unsupported. + +```swift +/tmp/retro.swift:3:1: warning: extension declares a conformance of imported type 'Date' to imported protocol 'Identifiable'; this is not supported when library evolution is enabled +extension Date: Identifiable { +^ +``` + +If absolutely necessary, clients can silence this warning by explicitly +module-qualifying both of the types in question, to explicitly state that they +are intentionally declaring this conformance: + +``` +extension Foundation.Date: Swift.Identifiable { + // ... +} +``` + +## Detailed design + +This warning is intentionally scoped to attempt to prevent a common mistake that +has bad consequences for ABI-stable libraries. + +This warning does not trigger for conformances of external types to protocols +defined within the current module, as those conformances are safe. + +## Source compatibility + +This proposal is just a warning addition, and doesn't affect source +compatibility. + +## Effect on ABI stability + +This proposal has no effect on ABI stability. + +## Effect on API resilience + +This proposal has no effect on API resilience. + +## Alternatives considered + +#### Enabling this warning always + +This pattern is technically never a good idea, since it subjects your code to +runtime breakage in the future. However, I believe the risk to individual apps +is much lower than the risk of shipping one of these retroactive conformances in +an ABI-stable framework. + +#### Putting it behind a flag + +This warning could very well be enabled by a flag, but there's not much +precedent in Swift for flags to disable individual warnings. From 7213fdc2c2b9ad5824e97bc597bad33ce35105fa Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 13 Jul 2022 21:30:36 -0400 Subject: [PATCH 2634/4563] Update links to go to SE-0355 --- proposals/0363-unicode-for-string-processing.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0363-unicode-for-string-processing.md b/proposals/0363-unicode-for-string-processing.md index ca545d7e0e..b588817f33 100644 --- a/proposals/0363-unicode-for-string-processing.md +++ b/proposals/0363-unicode-for-string-processing.md @@ -1320,10 +1320,10 @@ let regex1 = /\p{name=latin lowercase a}/.extendUnicodeProperty(\.name, by: .fir ``` [repo]: https://github.com/apple/swift-experimental-string-processing/ -[option-scoping]: https://github.com/apple/swift-experimental-string-processing/blob/main/Documentation/Evolution/RegexSyntaxRunTimeConstruction.md#matching-options -[internals]: https://github.com/apple/swift-experimental-string-processing/blob/main/Documentation/Evolution/RegexSyntaxRunTimeConstruction.md -[internals-properties]: https://github.com/apple/swift-experimental-string-processing/blob/main/Documentation/Evolution/RegexSyntaxRunTimeConstruction.md#character-properties -[internals-charclass]: https://github.com/apple/swift-experimental-string-processing/blob/main/Documentation/Evolution/RegexSyntaxRunTimeConstruction.md#custom-character-classes +[option-scoping]: https://github.com/apple/swift-evolution/blob/main/proposals/0355-regex-syntax-run-time-construction.md#matching-options +[internals]: https://github.com/apple/swift-evolution/blob/main/proposals/0355-regex-syntax-run-time-construction.md +[internals-properties]: https://github.com/apple/swift-evolution/blob/main/proposals/0355-regex-syntax-run-time-construction.md#character-properties +[internals-charclass]: https://github.com/apple/swift-evolution/blob/main/proposals/0355-regex-syntax-run-time-construction.md#custom-character-classes [level1-word-boundaries]:https://unicode.org/reports/tr18/#Simple_Word_Boundaries [level2-word-boundaries]:https://unicode.org/reports/tr18/#RL2.3 From 5ae31355ad37aad44b157b6c6b0e5ee38f2b022f Mon Sep 17 00:00:00 2001 From: Mykola Pokhylets Date: Thu, 14 Jul 2022 10:40:10 +0200 Subject: [PATCH 2635/4563] Added sections about inlining and explicit opt-in --- proposals/NNNN-isolated-synchronous-deinit.md | 94 ++++++++++++++++--- 1 file changed, 79 insertions(+), 15 deletions(-) diff --git a/proposals/NNNN-isolated-synchronous-deinit.md b/proposals/NNNN-isolated-synchronous-deinit.md index 64e35fbe0a..383cbb9f71 100644 --- a/proposals/NNNN-isolated-synchronous-deinit.md +++ b/proposals/NNNN-isolated-synchronous-deinit.md @@ -3,7 +3,7 @@ * Proposal: [SE-NNNN](NNNN-isolated-synchronous-deinit.md) * Authors: [Mykola Pokhylets](https://github.com/nickolas-pohilets) * Review Manager: TBD -* Status: **WIP** +* Status: **Awaiting review** ## Introduction @@ -13,7 +13,7 @@ This feature lifts restrictions on `deinit` of actors and GAITs imposed by [SE-0 Combination of automatics reference counting and deterministic deinitialization makes `deinit` in Swift a powerful tool for resource management. It greatly reduces need for `close()`-like methods (`unsubscribe()`, `cancel()`, `shutdown()`, etc.) in the public API. Such methods not only clutter public API, but also introduce a state where object is already unusable but is still referencable. -Restrictions imposed by [SE-0327](https://github.com/apple/swift-evolution/blob/main/proposals/0327-actor-initializers.md) reduce usefullness of explicit `deinit`s in actors and GAITs. Workarounds for these limitations may involve creation of `close()`-like methods, or even manual reference counting, if API should be able to serve several clients. +Restrictions imposed by [SE-0327](https://github.com/apple/swift-evolution/blob/main/proposals/0327-actor-initializers.md) reduce usefulness of explicit `deinit`s in actors and GAITs. Workarounds for these limitations may involve creation of `close()`-like methods, or even manual reference counting, if API should be able to serve several clients. In cases when `deinit` belongs to a subclass of `UIView` or `UIViewController` which are known to call deinitializer on the main thread, developers may be tempted to silence the diagnostic by adopting `@unchecked Sendable` in types that are not actually sendable. This undermines concurrency checking by the compiler, and may lead to data races when using incorrectly marked types in other places. @@ -144,7 +144,7 @@ actor MyActor { } ``` -If there is an explicit `deinit`, then isolation is computed following usual rules for isolation of class instance members. It takes into account isolation attributes on the `deinit` itself, isolation of the `deinit` in the superclass, and isolation attributes on the containing class. If deinit belongs to an actor or GAIT, but isolation of the `deinit` is undesired, it can be supressed using `nonisolated` attribute: +If there is an explicit `deinit`, then isolation is computed following usual rules for isolation of class instance members. It takes into account isolation attributes on the `deinit` itself, isolation of the `deinit` in the superclass, and isolation attributes on the containing class. If deinit belongs to an actor or GAIT, but isolation of the `deinit` is undesired, it can be suppressed using `nonisolated` attribute: ```swift @MainActor @@ -200,16 +200,16 @@ class Derived3: IsolatedBase { } ``` -Note that this is opposite from the rules for overriding functions. That's because in case of `deinit`, isolation applies to the non-deallocating `deinit`, while overriding happens for `__deallocating_deinit`. `__deallocating_deinit` is always non-isolated, and is resposible for switching to correct executor before calling non-deallocting `deinit`. Non-deallocating `deinit` of the subclass calls non-deallocating `deinit` of the superclass. And it is allowed to call nonisolated function from isolated one. +Note that this is opposite from the rules for overriding functions. That's because in case of `deinit`, isolation applies to the non-deallocating `deinit`, while overriding happens for `__deallocating_deinit`. `__deallocating_deinit` is always non-isolated, and is responsible for switching to correct executor before calling non-deallocating `deinit`. Non-deallocating `deinit` of the subclass calls non-deallocating `deinit` of the superclass. And it is allowed to call nonisolated function from isolated one. -### Interaction with ObjC +### Importing ObjC code -Implemented mechanism is not available to the ObjC code, so marking ObjC classes as isolated on global actor using `__attribute__((swift_attr(..)))` has no effect on behaviour of the ObjC code. +Implemented mechanism is not available to the ObjC code, so marking ObjC classes as isolated on global actor using `__attribute__((swift_attr(..)))` has no effect on behavior of the ObjC code. Such classes are imported into Swift as having non-isolated `deinit`. But if `__attribute__((swift_attr(..)))` is used in `@interface` to explicitly mark `dealloc` method as isolated on global actor, then it is imported as isolated `deinit`. Marking `dealloc` as isolated means that `dealloc` must be called only on that executor. It is assumed that ObjC implementation of such class ensures this by overriding `retain/release`. `deinit` of the Swift subclasses is generated as an override of the `dealloc` method. Any pre-conditions which hold for the base class will be true for Swift subclasses as well. In this case `deinit` of the Swift subclass is type-checked as isolated, but isolation thunk is not generated for code size and performance optimization. -If `deinit` isolation was intoduced into hierarchy of the `@objc` Swift classes by a class implemented in Swift, then `retain/release` are not overriden, `dealloc` can be called from any thread, but isolation happens inside `dealloc` implementation. In this case, isolation thunks will be generated for each isolated `deinit` in the hierarchy. Only the most derived one may can do actual switching. The rest will be called already on the correct executor, and will follow the fast path in `swift_task_performOnExecutor()`. +If `deinit` isolation was introduced into hierarchy of the `@objc` Swift classes by a class implemented in Swift, then `retain/release` are not overridden, `dealloc` can be called from any thread, but isolation happens inside `dealloc` implementation. In this case, isolation thunks will be generated for each isolated `deinit` in the hierarchy. Only the most derived one does the actual switching. The rest will be called already on the correct executor, and will follow the fast path in `swift_task_performOnExecutor()`. ObjC classes that isolate `dealloc` by overriding `retain/release` SHOULD mark `dealloc` as isolated. This not only allows Swift subclasses to fully benefit from isolation, but also prevents them from isolating their `deinit/dealloc` (including the call to `[super dealloc]`) on a different actor. @@ -235,11 +235,19 @@ static void dealloc_impl_helper(void *ctx) { } ``` +### Exporting to ObjC + +`deinit` isolation is relevant only when subclassing, but ObjC code cannot subclass Swift classes. Generated `*-Swift.h` files contain no information about `deinit` isolation. + +### Interaction with distributed actors + +`deinit` declared in the code of the distributed actor applies only to the local actor and can be isolated as described above. Remote proxy has an implicit compiler-generated `deinit` which is never isolated. + ## Source compatibility Proposal makes previously invalid code valid. -Proposal may alter behaviour of existing GAIT and actors when last release happens on the different actor. It is unlikely that `deinit` of GAIT or an actor would have a synchronous externally-observable side effect that can be safely executed on a different actor. +Proposal may alter behavior of existing GAIT and actors when last release happens on the different actor. But it is very unlikely that `deinit` of GAIT or an actor would have a synchronous externally-observable side effect that can be safely executed on a different actor. ## Effect on ABI stability @@ -249,11 +257,15 @@ Proposal does not change ABI of the existing language features, but introduces n Isolation attributes of the `deinit` become part of the public API, but it matters only when inheriting from the class. -Changing deinitializer from nonisolated to isolated is allowed on final classes. But for non-final classes it may effect how deinitializers of the subclasses are generated. +Changing deinitializer from nonisolated to isolated is allowed on final Swift classes. But for non-final classes it may effect how deinitializers of the subclasses are generated. The same is true for changing identity of the isolating actor. -Changing deinitializer from isolated to nonisolated does not break ABI. Any unmodified subclasses will keep calling `deinit` of the superclass on the original actor. +Changing deinitializer from isolated to nonisolated does not break ABI for Swift classes, including `@objc`-classes. Any non-recompiled subclasses will keep calling `deinit` of the superclass on the original actor. + +Adding isolation annotation to `dealloc` method of the imported Objective-C classes is a breaking change. Existing Swift subclasses may have `deinit` isolated on different actor, and without recompilation will be calling `[super deinit]` on that actor. When recompiled, subclasses isolating on different actor will produce compilation error. Subclasses that had non-isolated deinitializer, or isolated on the same actor remain ABI compatible. It is possible to add isolation annotation to UIKit classes, because currently all their subclasses have nonisolated deinitializers. + +Removing isolation annotation from the `dealloc` method (together with `retain/release` overrides) is a breaking change. Any existing subclasses would be type-checked as isolated but compiled without isolation thunks. After changes in the base class, subclass deinitializers could be called on the wrong executor. ## Future Directions @@ -261,19 +273,61 @@ Changing deinitializer from isolated to nonisolated does not break ABI. Any unmo Similar approach can be used to start an unstructured task for executing `async` deinit, but this is out of scope of this proposal. +### Asserting that `self` does not escape from `deinit`. + +It is not legal for `self` to escape from `deinit`. Any strong references remaining after `swift_deallocObject()` is executed cannot be even released safely. Dereferencing dangling references can manifest itself as a crash, reading garbage data or corruption of unrelated memory. It can be hard to connect symptoms with the origin of the problem. Better solution would be to deterministically produce `fatalError()` in `swift_deallocObject()` if there are additional strong references. Ideal solution be to detect escaping `self` using compile-time analysis. + +### Improving de-virtualization and inlining of the executor access. + +Consider the following example: + +```swift +import Foundation + +@_silgen_name("do_it") +@MainActor func doIt() + +public class Foo { + @MainActor + public func foo() async { + doIt() + } + @MainActor + deinit {} +} +``` + +Currently both method and `deinit` entry points produce two calls to access the `MainActor.shared.unownedExecutor`, with the second one even using dynamic dispatch. +These two calls could be replaced with a single call to the statically referenced `swift_task_getMainExecutor()`. + +```llvm +%1 = tail call swiftcc %swift.metadata_response @"type metadata accessor for Swift.MainActor"(i64 0) #6 +%2 = extractvalue %swift.metadata_response %1, 0 +%3 = tail call swiftcc %TScM* @"static Swift.MainActor.shared.getter : Swift.MainActor"(%swift.type* swiftself %2) +%4 = tail call i8** @"lazy protocol witness table accessor for type Swift.MainActor and conformance Swift.MainActor : Swift.Actor in Swift"() #6 +%5 = bitcast %TScM* %3 to %objc_object* +%6 = tail call swiftcc { i64, i64 } @"dispatch thunk of Swift.Actor.unownedExecutor.getter : Swift.UnownedSerialExecutor"(%objc_object* swiftself %5, %swift.type* %2, i8** %4) +``` + +### Making fast path inlinable + +For this to be useful compiler first needs to be able to reason about the value of the current executor based on the isolation of the surrounding function. Then inlinable pre-check for `swift_task_isCurrentExecutor()` can be inserted allowing isolated deallocating deinit to be inlined into non-isolated one. + ## Alternatives considered ### Placing hopping logic in `swift_release()` instead. `UIView` and `UIViewController` implement hopping to the main thread by overriding `release` method. But in Swift there are no vtable/wvtable slots for releasing, and adding them would also affect a lot of code that does not need isolated deinit. -### Deterministic priority and task local values +### Deterministic task local values -When switching executors, current implementation copies priority and task-local values from the task/thread where the last release happened. This minimises differences between isolated and nonisolated `deinit`'s. +When switching executors, current implementation copies priority and task-local values from the task/thread where the last release happened. This minimizes differences between isolated and nonisolated `deinit`'s. If there are references from different tasks/threads, values of the priority and task-local values observed by the `deinit` are racy, both for isolated and nonsiolated `deinit`'s. -One way of making task-local values predictable would be to clear them for the duration of the `deinit` execution. This can be implemented efficiently, but would be too restrictive. If object is referenced in several tasks which all have a common parent, then `deinit` can reliably use task-local values which are known to be set in the parent task and not overriden in the child tasks. +One way of making task-local values predictable would be to clear them for the duration of the `deinit` execution. +This can be implemented efficiently, but would be too restrictive. +If object is referenced in several tasks which all have a common parent, then `deinit` can reliably use task-local values which are known to be set in the parent task and not overridden in the child tasks. If there is a demand for resetting task-local values, it can be implemented separately as an API: @@ -283,6 +337,16 @@ If there is a demand for resetting task-local values, it can be implemented sepa func withoutTaskLocalValues(operation: () throws -> Void) rethrows ``` -Making priority deterministic would require switching jobs even when last release happened on the desired executor. For now, there is no evidence that deterministic priority wouild be worth taking a performance hit. +### Don't isolate empty explicit `deinit` + +Empty explicit deinitializers are also eligible to be nonisolated as an optimization. But that would mean that interface of the declaration depends on its implementation. Currently, Swift infers function signature only for closure literals, but never for named functions. + +### Explicit opt-in into `deinit` isolation + +This would eliminate any possibility of unexpected changes in behavior of the existing code, but would penalize future users, creating inconsistency between isolation inference rules for `deinit` and regular methods. Currently there is syntax for opt-out from isolation for actor instance members, but there is no syntax for opt-in. Having opt-in for deinitializers would require introducing such syntax, and it would be used only for actor deinitializers. There is already `isolated` keyword, but it is applied to function arguments, not to function declarations. + +Classes whose deinitializers have nonisolated synchronous externally-visible side effects, like `AnyCancellable`, are unlikely to be isolated on global actors or be implemented as an actor. + +Proposal preserves behavior of deinitializers that have synchronous externally-visible side effects only under assumption that they are always released on the isolating actor. -### Explicit opt-in into deinit isolation +Deinitializers that explicitly notify about completion of their side effect continue to satisfy their contract even if proposal changes their behavior. From aaa7b55e008e7f202d15f9bba1398ec641329576 Mon Sep 17 00:00:00 2001 From: Mykola Pokhylets Date: Thu, 14 Jul 2022 14:32:04 +0200 Subject: [PATCH 2636/4563] Added link to implementation PR --- proposals/NNNN-isolated-synchronous-deinit.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/NNNN-isolated-synchronous-deinit.md b/proposals/NNNN-isolated-synchronous-deinit.md index 383cbb9f71..ad69b01d57 100644 --- a/proposals/NNNN-isolated-synchronous-deinit.md +++ b/proposals/NNNN-isolated-synchronous-deinit.md @@ -4,6 +4,7 @@ * Authors: [Mykola Pokhylets](https://github.com/nickolas-pohilets) * Review Manager: TBD * Status: **Awaiting review** +* Implementation: [apple/swift#60057](https://github.com/apple/swift/pull/60057) ## Introduction From 2e41b3e8f8601af28f6f0e24ab098a22d5f2e87c Mon Sep 17 00:00:00 2001 From: Mykola Pokhylets Date: Fri, 15 Jul 2022 16:27:07 +0200 Subject: [PATCH 2637/4563] Added extended stack trace to future improvements and only async deinit to alternatives considered. --- proposals/NNNN-isolated-synchronous-deinit.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/proposals/NNNN-isolated-synchronous-deinit.md b/proposals/NNNN-isolated-synchronous-deinit.md index ad69b01d57..b35d86c958 100644 --- a/proposals/NNNN-isolated-synchronous-deinit.md +++ b/proposals/NNNN-isolated-synchronous-deinit.md @@ -314,6 +314,10 @@ These two calls could be replaced with a single call to the statically reference For this to be useful compiler first needs to be able to reason about the value of the current executor based on the isolation of the surrounding function. Then inlinable pre-check for `swift_task_isCurrentExecutor()` can be inserted allowing isolated deallocating deinit to be inlined into non-isolated one. +### Improving extended stack trace support + +Developers who put breakpoint in the isolated deinit might want to see the call stack that lead to the least release of the object. Currently if switching of executors was involved, release call stack won't be shown in the debugger. + ## Alternatives considered ### Placing hopping logic in `swift_release()` instead. @@ -351,3 +355,7 @@ Classes whose deinitializers have nonisolated synchronous externally-visible sid Proposal preserves behavior of deinitializers that have synchronous externally-visible side effects only under assumption that they are always released on the isolating actor. Deinitializers that explicitly notify about completion of their side effect continue to satisfy their contract even if proposal changes their behavior. + +### Use asynchronous deinit as the only tool for `deinit` isolation + +Synchronous deinit has an efficient fast path that jumps right into deinit implementation without context switching, or any memory allocations. But asynchronous deinit would require creation of task in all cases. From 12dc32ef9752628c76cea3b396b67f00c6cbf51a Mon Sep 17 00:00:00 2001 From: Mykola Pokhylets Date: Fri, 15 Jul 2022 17:08:36 +0200 Subject: [PATCH 2638/4563] Added synchronous scheduling to the future directions --- proposals/NNNN-isolated-synchronous-deinit.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/proposals/NNNN-isolated-synchronous-deinit.md b/proposals/NNNN-isolated-synchronous-deinit.md index b35d86c958..582ddff20c 100644 --- a/proposals/NNNN-isolated-synchronous-deinit.md +++ b/proposals/NNNN-isolated-synchronous-deinit.md @@ -318,6 +318,31 @@ For this to be useful compiler first needs to be able to reason about the value Developers who put breakpoint in the isolated deinit might want to see the call stack that lead to the least release of the object. Currently if switching of executors was involved, release call stack won't be shown in the debugger. +### Implementing API for synchronously scheduling arbitrary work on the actor + +Introduced runtime functions has calling convention optimized for deinit use case, but using similar runtime function with a slightly different signature, one could implement an API for synchronously scheduling arbitrary work on the actor: + +```swift +extension Actor { + /// Adds a job to the actor queue that calls `work` passing `self` as an argument. + nonisolated func enqueue(_ work: __owned @Sendable @escaping (isolated Self) -> Void) + + /// If actor's executor is already the current one - executes work immediately + /// Otherwise adds a job to the actor's queue. + nonisolated func executeOrEnqueue(_ work: __owned @Sendable @escaping (isolated Self) -> Void) +} + +actor MyActor { + var k: Int = 0 + func inc() { k += 1 } +} + +let a = MyActor() +a.enqueue { aIsolated in + aIsolated.inc() // no await +} +``` + ## Alternatives considered ### Placing hopping logic in `swift_release()` instead. From 0ec3fc648eeebe362f99f3df965e15b0614fa413 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Fri, 15 Jul 2022 09:04:41 -0700 Subject: [PATCH 2639/4563] Add proposal for allowing implicit self for weak self captures (#1506) * Add proposal for allowing implicit self for weak self captures * Link to SE thread * Address cases where 'self' doesn't necessarily refer to the closure's 'self' capture * clean up --- proposals/NNNN-implicit-self-weak-capture.md | 141 +++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 proposals/NNNN-implicit-self-weak-capture.md diff --git a/proposals/NNNN-implicit-self-weak-capture.md b/proposals/NNNN-implicit-self-weak-capture.md new file mode 100644 index 0000000000..cab51590ba --- /dev/null +++ b/proposals/NNNN-implicit-self-weak-capture.md @@ -0,0 +1,141 @@ +# Allow implicit `self` for `weak self` captures, after `self` is unwrapped + +* Proposal: [SE-NNNN](NNNN-implicit-self-weak-capture.md) +* Authors: [Cal Stephens](https://github.com/calda) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#40702](https://github.com/apple/swift/pull/40702) + +## Introduction + +As of [SE-0269](https://github.com/apple/swift-evolution/blob/main/proposals/0269-implicit-self-explicit-capture.md), implicit `self` is permitted in closures when `self` is written explicitly in the capture list. We should extend this support to `weak self` captures, and permit implicit `self` as long as `self` has been unwrapped. + +```swift +class ViewController { + let button: Button + + func setup() { + button.tapHandler = { [weak self] in + guard let self else { return } + dismiss() + } + } + + func dismiss() { ... } +} +``` + +Swift-evolution thread: [Allow implicit `self` for `weak self` captures, after `self` is unwrapped](https://forums.swift.org/t/allow-implicit-self-for-weak-self-captures-after-self-is-unwrapped/54262) + +## Motivation + +Explicit `self` has historically been required in closures, in order to help prevent users from inadvertently creating retain cycles. [SE-0269](https://github.com/apple/swift-evolution/blob/main/proposals/0269-implicit-self-explicit-capture.md) relaxed these rules in cases where implicit `self` is unlikely to introduce a hidden retain cycle, such as when `self` is explicitly captured in the closure's capture list: + +```swift +button.tapHandler = { [self] in + dismiss() +} +``` + +SE-0269 left the handling of `weak self` captures as a future direction, so explicit `self` is currently required in this case: + +```swift +button.tapHandler = { [weak self] in + guard let self else { return } + self.dismiss() +} +``` + +Since `self` has already been captured explicitly, there is limited value in requiring authors to use explicit `self`. This is inconsistent, and adds unnecessary visual noise to the body of closures using `weak self` captures. + +## Proposed solution + +We should permit implicit `self` for `weak self` captures, once `self` has been unwrapped. + +This code would now be allowed to compile: + +```swift +class ViewController { + let button: Button + + func setup() { + button.tapHandler = { [weak self] in + guard let self else { return } + dismiss() + } + } + + func dismiss() { ... } +} +``` + +## Detailed design + +Like with implicit `self` for `strong` and `unowned` captures, the compiler will synthesize an implicit `self.` for calls to properties / methods on `self` inside a closure that uses `weak self`. + +If `self` has not been unwrapped yet, the following error will be emitted: + +```swift +button.tapHandler = { [weak self] in + // error: explicit use of 'self' is required when 'self' is optional, + // to make control flow explicit + // fix-it: reference 'self?.' explicitly + dismiss() +} +``` + +Following the precedent of [SE-0269](https://github.com/apple/swift-evolution/blob/main/proposals/0269-implicit-self-explicit-capture.md), additional closures nested inside the `[weak self]` closure most capture `self` explicitly in order to use implicit `self`. + +```swift +button.tapHandler = { [weak self] in + guard let self else { return } + + execute { + // error: call to method 'method' in closure requires + // explicit use of 'self' to make capture semantics explicit + dismiss() + } +} +``` + +Also following [SE-0269](https://github.com/apple/swift-evolution/blob/main/proposals/0269-implicit-self-explicit-capture.md), implicit `self` will only be permitted if the `self` optional binding specifically, and exclusively, refers the closure's `self` capture: + +```swift +button.tapHandler = { [weak self] in + guard let self = self ?? someOptionalWithSameTypeOfSelf else { return } + + // error: call to method 'method' in closure requires explicit use of 'self' + // to make capture semantics explicit + method() +} +``` + +## Source compatibility + +This change is purely additive and does not break source compatibility of any valid existing Swift code. + +## Effect on ABI stability + +This change is purely additive, and is a syntactic transformation to existing valid code, so has no effect on ABI stability. + +## Effect on API resilience + +This change is purely additive, and is a syntactic transformation to existing valid code, so has no effect on API resilience. + +## Alternatives considered + +It is technically possible to also support implicit `self` _before_ `self` has been unwrapped, like: + +```swift +button.tapHandler = { [weak self] in + dismiss() // as in `self?.dismiss()` +} +``` + +That would effectively add implicit control flow, however. `dismiss()` would only be executed when `self` is not nil, without any indication that it may not run. We could create a new way to spell this that still implies optional chaining, like `?.dismiss()`, but that is not meaningfully better than the existing `self?.dismiss()` spelling. + +## Acknowledgments + +Thanks to the authors of [SE-0269](https://github.com/apple/swift-evolution/blob/main/proposals/0269-implicit-self-explicit-capture.md) for laying the foundation for this proposal. + +Thanks to Kyle Sluder for [the suggestion](https://forums.swift.org/t/allow-implicit-self-for-weak-self-captures-after-self-is-unwrapped/54262/2) to not permit implicit `self` in cases where the unwrapped `self` value doesn't necessarily refer to the closure's `self` capture, like in `let self = self ?? C.global`. From 143858cb45ab822d8a59c1f512a82d913f565a81 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 15 Jul 2022 09:11:32 -0700 Subject: [PATCH 2640/4563] SE-0365: christen SE-0365 and schedule for review (#1713) --- ...f-weak-capture.md => 0365-implicit-self-weak-capture.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-implicit-self-weak-capture.md => 0365-implicit-self-weak-capture.md} (96%) diff --git a/proposals/NNNN-implicit-self-weak-capture.md b/proposals/0365-implicit-self-weak-capture.md similarity index 96% rename from proposals/NNNN-implicit-self-weak-capture.md rename to proposals/0365-implicit-self-weak-capture.md index cab51590ba..61ad06bb73 100644 --- a/proposals/NNNN-implicit-self-weak-capture.md +++ b/proposals/0365-implicit-self-weak-capture.md @@ -1,9 +1,9 @@ # Allow implicit `self` for `weak self` captures, after `self` is unwrapped -* Proposal: [SE-NNNN](NNNN-implicit-self-weak-capture.md) +* Proposal: [SE-0365](NNNN-implicit-self-weak-capture.md) * Authors: [Cal Stephens](https://github.com/calda) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Saleem Abdulrasool](https://github.com/compnerd) +* Status: **Scheduled for reveiw (July 18, 2022...August 1, 2022)** * Implementation: [apple/swift#40702](https://github.com/apple/swift/pull/40702) ## Introduction From 5d9c6fd6b849cde891cb659727cf5a9a9ef30e91 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 15 Jul 2022 09:38:05 -0700 Subject: [PATCH 2641/4563] SE-0359 "Build-Time Constant Values" is returned for revision (#1714) --- proposals/0359-build-time-constant-values.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0359-build-time-constant-values.md b/proposals/0359-build-time-constant-values.md index 2cc5d80ac2..2742a8e9a8 100644 --- a/proposals/0359-build-time-constant-values.md +++ b/proposals/0359-build-time-constant-values.md @@ -3,7 +3,8 @@ * Proposal: [SE-0359](0359-build-time-constant-values.md) * Authors: [Artem Chikin](https://github.com/artemcm), [Ben Cohen](https://github.com/airspeedswift), [Xi Ge](https://github.com/nkcsgexi) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review (May 24...June 6, 2022)** +* Status: **Returned for revision** +* Decision notes: [First review rationale](https://forums.swift.org/t/returned-for-revision-se-0359-build-time-constant-values/58976) * Implementation: Implemented on `main` as `_const` ## Introduction From b99a2e964178ff94e26c7d619b45aaab488261b5 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 15 Jul 2022 12:03:53 -0700 Subject: [PATCH 2642/4563] [SE-0362] Clarify that `enableExperimentalFeature` is part of SwiftPM manifest break (#1715) Thanks to James Dempsey for the suggestion --- proposals/0362-piecemeal-future-features.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0362-piecemeal-future-features.md b/proposals/0362-piecemeal-future-features.md index c76d2d3330..9730e74ef4 100644 --- a/proposals/0362-piecemeal-future-features.md +++ b/proposals/0362-piecemeal-future-features.md @@ -132,7 +132,7 @@ Experimental features are still to be considered unstable, and should not be ava For the language itself, `hasFeature` is the only addition, and it occurs in a constrained syntactic space (`#if`) where there are no user-definable functions. Therefore, there is no source-compatibility issue in the traditional sense, where a newer compiler rejects existing, well-formed code. -For SwiftPM, the addition of the `enableUpcomingFeature` function to `SwiftSetting` represents a one-time break in the manifest file format. Packages that wish to adopt this function and support tools versions that predate the introduction of `enableUpcomingFeature` can use versioned manifest, e.g., `Package@swift-5.6.swift`, to adopt the feature for newer tools versions. Once `enableUpcomingFeature` has been added, adopting additional features this way won't require another copy of the manifest file. +For SwiftPM, the addition of the `enableUpcomingFeature` and `enableExperimentalFeature` functions to `SwiftSetting` represents a one-time break in the manifest file format. Packages that wish to adopt these functions and support tools versions that predate the introduction of `enableUpcomingFeature` and `enableExperimentalFeature` can use versioned manifest, e.g., `Package@swift-5.6.swift`, to adopt the feature for newer tools versions. Once `enableUpcomingFeature` and `enableExperimentalFeature` have been added, adopting additional features this way won't require another copy of the manifest file. ## Alternatives considered From e5ecbf32048014099101efed01cff26286d9ff50 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 15 Jul 2022 23:30:00 +0100 Subject: [PATCH 2643/4563] [SE-0365] Update the proposal ID link (#1716) --- proposals/0365-implicit-self-weak-capture.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0365-implicit-self-weak-capture.md b/proposals/0365-implicit-self-weak-capture.md index 61ad06bb73..d6a7707637 100644 --- a/proposals/0365-implicit-self-weak-capture.md +++ b/proposals/0365-implicit-self-weak-capture.md @@ -1,9 +1,9 @@ # Allow implicit `self` for `weak self` captures, after `self` is unwrapped -* Proposal: [SE-0365](NNNN-implicit-self-weak-capture.md) +* Proposal: [SE-0365](0365-implicit-self-weak-capture.md) * Authors: [Cal Stephens](https://github.com/calda) * Review Manager: [Saleem Abdulrasool](https://github.com/compnerd) -* Status: **Scheduled for reveiw (July 18, 2022...August 1, 2022)** +* Status: **Scheduled for review (July 18, 2022...August 1, 2022)** * Implementation: [apple/swift#40702](https://github.com/apple/swift/pull/40702) ## Introduction From cac28968a39fd9d11650b0828be69246ec204b85 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 17 Jul 2022 00:42:57 +0100 Subject: [PATCH 2644/4563] [SE-0351] Update status and review fields (#1717) --- proposals/0351-regex-builder.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/proposals/0351-regex-builder.md b/proposals/0351-regex-builder.md index 3ced3602ce..34b42d52cc 100644 --- a/proposals/0351-regex-builder.md +++ b/proposals/0351-regex-builder.md @@ -5,7 +5,12 @@ * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Implementation: [apple/swift-experimental-string-processing](https://github.com/apple/swift-experimental-string-processing/tree/main/Sources/RegexBuilder) * Available in nightly toolchain snapshots with `import _StringProcessing` -* Status: **Active Review (6 - 14 July 2022)** +* Status: **Accepted** +* Review: ([pitch](https://forums.swift.org/t/pitch-regex-builder-dsl/56007)) + ([first review](https://forums.swift.org/t/se-0351-regex-builder-dsl/56531)) + ([revision](https://forums.swift.org/t/returned-for-revision-se-0351-regex-builder-dsl/57224)) + ([second review](https://forums.swift.org/t/se-0351-second-review-regex-builder-dsl/58721)) + ([acceptance](https://forums.swift.org/t/accepted-se-0351-regex-builder-dsl/58972)) **Table of Contents** - [Regex builder DSL](#regex-builder-dsl) From 0823dc6fc678e3a91a812a49b1830f61abc2e04a Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 17 Jul 2022 01:15:16 +0100 Subject: [PATCH 2645/4563] [SE-0354] Update status and review fields (#1718) --- proposals/0354-regex-literals.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/proposals/0354-regex-literals.md b/proposals/0354-regex-literals.md index 0049d1f4b1..87acd682a5 100644 --- a/proposals/0354-regex-literals.md +++ b/proposals/0354-regex-literals.md @@ -3,9 +3,15 @@ * Proposal: [SE-0354](0354-regex-literals.md) * Authors: [Hamish Knight](https://github.com/hamishknight), [Michael Ilseman](https://github.com/milseman), [David Ewing](https://github.com/DaveEwing) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (May 16 – May 23 2022)** +* Status: **Accepted** * Implementation: https://github.com/apple/swift-experimental-string-processing * Available in nightly toolchain snapshots with `-enable-bare-slash-regex` +* Review: ([first pitch](https://forums.swift.org/t/pitch-regular-expression-literals/52820)) + ([second pitch](https://forums.swift.org/t/pitch-2-regex-literals/56736)) + ([first review](https://forums.swift.org/t/se-0354-regex-literals/57037)) + ([revision](https://forums.swift.org/t/returned-for-revision-se-0354-regex-literals/57366)) + ([second review](https://forums.swift.org/t/se-0354-second-review-regex-literals/57367)) + ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0354-regex-literals/58537)) ## Introduction From a4adf9a5a1fd7658d60efbb04f62c6f9ae99fe83 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Mon, 18 Jul 2022 11:23:53 +0100 Subject: [PATCH 2646/4563] Update uses of `matchWhole` -> `wholeMatch` Update the name of the method being used, and add the correct argument label. --- proposals/0350-regex-type-overview.md | 4 ++-- proposals/0354-regex-literals.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0350-regex-type-overview.md b/proposals/0350-regex-type-overview.md index 26fd373a69..12f834051c 100644 --- a/proposals/0350-regex-type-overview.md +++ b/proposals/0350-regex-type-overview.md @@ -212,7 +212,7 @@ func processEntry(_ line: String) -> Transaction? { // amount: Substring // )> - guard let match = try? regex.wholeMatch(line), + guard let match = line.wholeMatch(of: regex), let kind = Transaction.Kind(match.kind), let date = try? Date(String(match.date), strategy: dateParser), let amount = try? Decimal(String(match.amount), format: decimalParser) @@ -269,7 +269,7 @@ func processEntry(_ line: String) -> Transaction? { } // regex: Regex<(Substring, Transaction.Kind, Date, Substring, Decimal)> - guard let match = regex.matchWhole(line) else { return nil } + guard let match = line.wholeMatch(of: regex) else { return nil } return Transaction( kind: match[kind], diff --git a/proposals/0354-regex-literals.md b/proposals/0354-regex-literals.md index 0049d1f4b1..b266f506ca 100644 --- a/proposals/0354-regex-literals.md +++ b/proposals/0354-regex-literals.md @@ -113,7 +113,7 @@ func matchHexAssignment(_ input: String) -> (String, Int)? { let regex = /(?[[:alpha:]]\w*) = (?[0-9A-F]+)/ // regex: Regex<(Substring, identifier: Substring, hex: Substring)> - guard let match = regex.matchWhole(input), + guard let match = input.wholeMatch(of: regex), let hex = Int(match.hex, radix: 16) else { return nil } From add1491ac01638aa23c0a48aedca08371686a528 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 18 Jul 2022 08:40:39 -0700 Subject: [PATCH 2647/4563] SE0365: kick off review (#1721) --- proposals/0365-implicit-self-weak-capture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0365-implicit-self-weak-capture.md b/proposals/0365-implicit-self-weak-capture.md index d6a7707637..b021efc4a6 100644 --- a/proposals/0365-implicit-self-weak-capture.md +++ b/proposals/0365-implicit-self-weak-capture.md @@ -3,7 +3,7 @@ * Proposal: [SE-0365](0365-implicit-self-weak-capture.md) * Authors: [Cal Stephens](https://github.com/calda) * Review Manager: [Saleem Abdulrasool](https://github.com/compnerd) -* Status: **Scheduled for review (July 18, 2022...August 1, 2022)** +* Status: **Active review (July 18, 2022...August 1, 2022)** * Implementation: [apple/swift#40702](https://github.com/apple/swift/pull/40702) ## Introduction From d30c961931653fa7cd7087a1f0093b98f1bc4c8e Mon Sep 17 00:00:00 2001 From: Harlan Haskins Date: Tue, 19 Jul 2022 09:45:09 -0600 Subject: [PATCH 2648/4563] Update retroactive conformances proposal (#1722) This updates the proposal to apply to all modules, not just resilient libraries. It also updates the warning text to more clearly explain what the consequences are of such a construct. --- .../0364-retroactive-conformance-warning.md | 57 ++++++++++++------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/proposals/0364-retroactive-conformance-warning.md b/proposals/0364-retroactive-conformance-warning.md index 06a7780981..9e00840ad4 100644 --- a/proposals/0364-retroactive-conformance-warning.md +++ b/proposals/0364-retroactive-conformance-warning.md @@ -1,4 +1,4 @@ -# Warning for Retroactive Conformances of External Types in Resilient Libraries +# Warning for Retroactive Conformances of External Types * Proposal: [SE-0364](0364-retroactive-conformance-warning.md) * Author: [Harlan Haskins](https://github.com/harlanhaskins) @@ -32,12 +32,14 @@ extension Date: Identifiable { Now that this client has declared this conformance, if Foundation decides to add this conformance in a later revision, this client will fail to build. - -If this is an app client, that might be okay --- the breakage will be confined -to their process, and it's their responsibility to remove their conformance, -rebuild, and resubmit their app or redeploy their service. - -However, if this is a library target, this conformance propagates down to every +Before the client removes their conformance and rebuilds, however, their +application will exhibit undefined behavior, as it is indeterminate which +definition of this conformance will "win". Foundation may well have defined +it to use `Date.timeIntervalSinceReferenceDate`, and if the client had persisted +these IDs to a database or some persistent storage beyond the lifetime of the process, +then their dates will have completely different IDs. + +Worse, if this is a library target, this conformance propagates down to every client that imports the library. This is especially bad for frameworks that are built with library evolution enabled, as their clients link against binary frameworks and usually are not aware these conformances don't come from @@ -45,11 +47,11 @@ the actual owning module. ## Proposed solution -This proposal adds a warning when library evolution is enabled that explicitly -calls out this pattern as problematic and unsupported. +This proposal adds a warning that explicitly calls out this pattern as +problematic and unsupported. ```swift -/tmp/retro.swift:3:1: warning: extension declares a conformance of imported type 'Date' to imported protocol 'Identifiable'; this is not supported when library evolution is enabled +/tmp/retro.swift:3:1: warning: extension declares a conformance of imported type 'Date' to imported protocol 'Identifiable'; this will not behave correctly if the owners of 'Foundation' introduce this conformance in the future extension Date: Identifiable { ^ ``` @@ -66,11 +68,25 @@ extension Foundation.Date: Swift.Identifiable { ## Detailed design -This warning is intentionally scoped to attempt to prevent a common mistake that -has bad consequences for ABI-stable libraries. +This warning will appear only if all of the following conditions are met, with a few exceptions. + +- The type being extended was declared in a different module from the extension. +- The protocol for which the extension introduces the conformance is declared in a different + module from the extension. + +The following exceptions apply: + +- If the type is declared in a Clang module, and the extension in question is declared in a Swift + overlay, this is not considered a retroactive conformance. +- If the type is declared or transitively imported in a bridging header or through the + `-import-objc-header` flag, and the type does not belong to any other module, the warning is not + emitted. This could be a retroactive conformance, but since these are added to an implicit module + called `__ObjC`, we have to assume the client takes responsibility for these declaration. -This warning does not trigger for conformances of external types to protocols -defined within the current module, as those conformances are safe. +For clarification, the following are still valid, safe, and allowed: +- Conformances of external types to protocols defined within the current module. +- Extensions of external types that do not introduce a conformance. These do not introduce runtime conflicts, since the + module name is mangled into the symbol. ## Source compatibility @@ -83,16 +99,17 @@ This proposal has no effect on ABI stability. ## Effect on API resilience -This proposal has no effect on API resilience. +This proposal has direct effect on API resilience, but has the indirect effect of reducing +the possible surface of client changes introduced by the standard library adding new conformances. ## Alternatives considered -#### Enabling this warning always +#### Enabling this warning only for resilient libraries -This pattern is technically never a good idea, since it subjects your code to -runtime breakage in the future. However, I believe the risk to individual apps -is much lower than the risk of shipping one of these retroactive conformances in -an ABI-stable framework. +A previous version of this proposal proposed enabling this warning only for resilient libraries, as those +are meant to be widely distributed and such a conformance is much more difficult to remove from clients +who expect ABI stability. However, review feedback showed a clear preference to enable this warning always, +to give library authors more freedom to introduce conformances. #### Putting it behind a flag From b88bf8da7eae137eb3bcb0b1bf88732863faadab Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 19 Jul 2022 10:41:44 -0700 Subject: [PATCH 2649/4563] Accept SE-0362 (#1725) --- proposals/0362-piecemeal-future-features.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0362-piecemeal-future-features.md b/proposals/0362-piecemeal-future-features.md index 9730e74ef4..ec7ec95a9a 100644 --- a/proposals/0362-piecemeal-future-features.md +++ b/proposals/0362-piecemeal-future-features.md @@ -3,9 +3,9 @@ * Proposal: [SE-0362](0362-piecemeal-future-features.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (June 22nd...July 11th, 2022)** +* Status: **Accepted** * Implementation: [apple/swift#59055](https://github.com/apple/swift/pull/59055), [apple/swift-package-manager#5632](https://github.com/apple/swift-package-manager/pull/5632) -* Review: ([pitch](https://forums.swift.org/t/piecemeal-adoption-of-swift-6-improvements-in-swift-5-x/57184)) ([review](https://forums.swift.org/t/se-0362-piecemeal-adoption-of-future-language-improvements/58384)) +* Review: ([pitch](https://forums.swift.org/t/piecemeal-adoption-of-swift-6-improvements-in-swift-5-x/57184)) ([review](https://forums.swift.org/t/se-0362-piecemeal-adoption-of-future-language-improvements/58384)) ([acceptance](https://forums.swift.org/t/accepted-se-0362-piecemeal-adoption-of-future-language-improvements/59076)) ## Introduction From 321c396df8d0e130cff9c8e15dea4dfe482b0479 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Wed, 20 Jul 2022 20:49:37 +0100 Subject: [PATCH 2650/4563] [SE-0354] Update to mark as implemented (#1723) * [SE-0354] Mark as implemented * [SE-0354] Fix source compatibility section Update to say the first and last character may not be space or tab. --- proposals/0354-regex-literals.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0354-regex-literals.md b/proposals/0354-regex-literals.md index 73e3b14969..279359cf49 100644 --- a/proposals/0354-regex-literals.md +++ b/proposals/0354-regex-literals.md @@ -3,9 +3,9 @@ * Proposal: [SE-0354](0354-regex-literals.md) * Authors: [Hamish Knight](https://github.com/hamishknight), [Michael Ilseman](https://github.com/milseman), [David Ewing](https://github.com/DaveEwing) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** -* Implementation: https://github.com/apple/swift-experimental-string-processing - * Available in nightly toolchain snapshots with `-enable-bare-slash-regex` +* Status: **Implemented (Swift 5.7)** +* Implementation: [apple/swift#42119](https://github.com/apple/swift/pull/42119), [apple/swift#58835](https://github.com/apple/swift/pull/58835) + * Bare slash syntax `/.../` available with `-enable-bare-slash-regex` * Review: ([first pitch](https://forums.swift.org/t/pitch-regular-expression-literals/52820)) ([second pitch](https://forums.swift.org/t/pitch-2-regex-literals/56736)) ([first review](https://forums.swift.org/t/se-0354-regex-literals/57037)) @@ -403,8 +403,8 @@ As explored above, the parsing of `/.../` does have potential to break source in - `/` appears in an expression position. - There is a closing `/` on the same line. -- The first character following the opening `/` is not space or tab. -- There are no unbalanced `)` characters between the two `/` characters. +- The first and last character of the literal is not space or tab. +- There are no unbalanced `)` characters within the literal. However we expect these cases will be uncommon, and can be disambiguated with parentheses or closures if needed. From e655bc1782bc5c58ebf263dc78d2b6a6a00845f5 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Fri, 22 Jul 2022 12:44:58 -0700 Subject: [PATCH 2651/4563] Proposal to add a move(x) function for ending local variable lifetimes (#1693) * Proposal to add a move(x) function for ending local variable lifetimes * Editorial feedback * Editorial feedback ][ * Assign SE-0366 to `move` and schedule for review. * Typo fix and more editorial changes Co-authored-by: Holly Borla --- proposals/0366-move-function.md | 460 ++++++++++++++++++++++++++++++++ 1 file changed, 460 insertions(+) create mode 100644 proposals/0366-move-function.md diff --git a/proposals/0366-move-function.md b/proposals/0366-move-function.md new file mode 100644 index 0000000000..dd4969acfe --- /dev/null +++ b/proposals/0366-move-function.md @@ -0,0 +1,460 @@ +# Move Function + "Use After Move" Diagnostic + +* Proposal: [SE-0366](0366-move-function.md) +* Authors: [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Joe Groff](https://github.com/jckarter) +* Review Manager: [Holly Borla](https://github.com/hborla) +* Status: **Active Review (July 25...August 8, 2022)** +* Implementation: Implemented on main as stdlib SPI (`_move` instead of `move`) +* Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic)) + +## Introduction + +In this document, we propose adding a new function called `move` to the swift +standard library, which ends the lifetime of a specific local `let`, +local `var`, or `consuming` function parameter, and which enforces this +by causing the compiler to emit a diagnostic upon any uses that are after the +move function. This allows for code that relies on **forwarding ownership** +of values for performance or correctness to communicate that requirement to +the compiler and to human readers. As an example: + +```swift +useX(x) // do some stuff with local variable x + +// Ends lifetime of x, y's lifetime begins. +let y = move(x) // [1] + +useY(y) // do some stuff with local variable y +useX(x) // error, x's lifetime was ended at [1] + +// Ends lifetime of y, destroying the current value +// since it is explicitly thrown away assigning to `_` +_ = move(y) // [2] +useX(x) // error, x's lifetime was ended at [1] +useY(y) // error, y's lifetime was ended at [2] +``` + +## Motivation + +Swift uses reference counting and copy-on-write to allow for developers to +write code with value semantics, without normally having to worry too much +about performance or memory management. However, in performance sensitive code, +developers want to be able to control the uniqueness of COW data structures and +reduce retain/release calls in a way that is future-proof against changes to +the language implementation or source code. Consider the following array/uniqueness +example: + +```swift +func test() { + var x: [Int] = getArray() + + // x is appended to. After this point, we know that x is unique. We want to + // preserve that property. + x.append(5) + + // We create a new variable y so we can write an algorithm where we may + // change the value of y (causing a COW copy of the buffer shared with x). + var y = x + longAlgorithmUsing(&y) + consumeFinalY(y) + + // We no longer use y after this point. Ideally, x would be guaranteed + // unique so we know we can append again without copying. + x.append(7) +} +``` + +In the example above, `y`'s formal lifetime extends to the end of +scope. When we go back to using `x`, although the compiler may optimize +the actual lifetime of `y` to release it after its last use, there isn't +a strong guarantee that it will. Even if the optimizer does what we want, +programmers modifying this code in the future +may introduce new references to `y` that inadvertently extend its lifetime +and break our attempt to keep `x` unique. There isn't any indication in the +source code that that the end of `y`'s use is important to the performance +characteristics of the code. + +Swift-evolution pitch thread: [https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic) + +## Proposed solution: Move Function + "Use After Move" Diagnostic + +That is where the `move` function comes into play. The `move` function consumes +a **movable binding**, which is either +an unescaped local `let`, unescaped local `var`, or function argument, with +no property wrappers or get/set/read/modify/etc. accessors applied. It then + provides a compiler guarantee that the binding will +be unable to be used again locally. If such a use occurs, the compiler will +emit an error diagnostic. We can modify the previous example to use `move` to +explicitly end the lifetime of `y` when we're done with it: + +```swift +func test() { + var x: [Int] = getArray() + + // x is appended to. After this point, we know that x is unique. We want to + // preserve that property. + x.append(5) + + // We create a new variable y so we can write an algorithm where we may + // change the value of y (causing a COW copy of the buffer shared with x). + var y = x + longAlgorithmUsing(&y) + // We no longer use y after this point, so move it when we pass it off to + // the last use. + consumeFinalY(move(y)) + + // x will be unique again here. + x.append(7) +} +``` + +This addresses both of the motivating issues above: `move` guarantees the +lifetime of `y` ends at the given point, allowing the compiler to generate +code to clean up or transfer ownership of `y` without relying on optimization. +Furthermore, if a future maintainer modifies the code in a way that extends +the lifetime of `y` past the expected point, then the compiler will raise an +error. For instance, if a maintainer later introduces an additional use of +`y` after the move, it will raise an error: + +```swift +func test() { + var x: [Int] = getArray() + + // x is appended to. After this point, we know that x is unique. We want to + // preserve that property. + x.append(5) + + // We create a new variable y so we can write an algorithm where we may + // change the value of y (causing a COW copy of the buffer shared with x). + var y = x + longAlgorithmUsing(&y) + // We think we no longer use y after this point... + consumeFinalY(move(y)) + + // ...and x will be unique again here... + x.append(7) + + // ...but this additional use of y snuck in: + useYAgain(y) // error: 'y' used after being moved +} +``` + +`move` only ends the lifetime of a specific movable binding. It is not tied to +the lifetime of the value of the binding at the time of the move, or to any +particular object instance. If we declare another local constant `other` with +the same value of `x`, we can use that other binding after we end the lifetime +of `x`, as in: + +```swift +func useX(_ x: SomeClassType) -> () {} +func consumeX(_ x: __owned SomeClassType) -> () {} + +func f() { + let x = ... + useX(x) + let other = x // other is a new binding used to extend the lifetime of x + _ = move(x) // x's lifetime ends + useX(other) // other is used here... no problem. + consumeX(other) // other is used here... no problem. +} +``` + +In fact, each movable binding's lifetime is tracked independently, and gets a +separate diagnostic if used after move. We can move `other` independently +of `x`, and get separate diagnostics for both variables: + +```swift +func useX(_ x: SomeClassType) -> () {} +func consumeX(_ x: __owned SomeClassType) -> () {} + +func f() { + let x = ... + useX(x) + let other = x + _ = move(x) + useX(move(other)) + consumeX(other) // error: 'other' used after being moved + useX(x) // error: 'x' used after being moved +} +``` + +If a local `var` is moved, then a new value can be assigned into +the variable after an old value has been moved out. One can +begin using the var again after one re-assigns to the var: + +```swift +func f() { + var x = getValue() + let _ = move(x) + useX(x) // error: no value in x + x = getValue() + useX(x) // ok, x has a new value here +} +``` + +This follows from move being applied to the binding (`x`), not the value in the +binding (the value returned from `getValue()`). + +We also support applying the move operation to consuming function arguments: + +```swift +func f(_ x: __owned SomeClassType) { + let _ = move(x) + useX(x) // !! Error! Use of x after move +} +``` + +Normal arguments are passed by borrow, meaning that the lifetime of the value +is managed by the caller. Although we could allow `move` on these arguments, +shortening the syntactic lifetime of the variable, doing so would have no +practical effect on the value's lifetime at runtime, so we choose to leave this +disallowed for now, in order to avoid potentially misleading developers who +might expect the value to be destroyed at the point of the move. + +On the other hand, one can `move` out of an `inout` +function argument. Like a `var`, the `inout` argument can be reassigned after +being moved from and used again; however, since the final value of an +`inout` argument is passed back to the caller, an `inout` argument *must* be +reassigned by the callee before it returns. This will raise an error because +`buffer` doesn't have a value at the point of return: + +```swift +func f(_ buffer: inout Buffer) { // error: 'buffer' not reinitialized after move! + let b = move(buffer) // note: move was here + b.deinitialize() + ... write code ... +} // note: return without reassigning inout argument `buffer` +``` + +But we can reinitialize `buffer` by writing the following code: + +```swift +func f(_ buffer: inout Buffer) { + let b = move(buffer) + b.deinitialize() + // ... write code ... + // We re-initialized buffer before end of function so the checker is satisfied + buffer = getNewInstance() +} +``` + +`defer` can also be used to reinitialize an `inout` or `var` after a move, +in order to ensure that reassignment happens on any exit from scope, including +thrown errors or breaks out of loops. So we can also write: + +```swift +func f(_ buffer: inout Buffer) { + let b = move(buffer) + // Ensure the buffer is reinitialized before we exit. + defer { buffer = getNewInstance() } + try b.deinitializeOrError() + // ... write code ... +} +``` + +## Detailed design + +We declare `move` as follows: + +```swift +/// This function ends the lifetime of the passed in binding. +func move(_ value: __owned T) -> T +``` + +At runtime, the function returns `value` unmodified back to its caller. +However, at compile time, the presence of a call to `move` forces +ownership of the argument to be transferred out of the binding at the given +point, and triggers diagnostics that prove that it is safe to do so, +by flagging any proceeding uses of the binding that are reachable from the move. +The argument to `move` is required to be a reference to a *movable binding*. +The following kinds of declarations can currently be referenced as movable +bindings: + +- a local `let` constant in the immediately-enclosing function, +- a local `var` variable in the immediately-enclosing function, +- one of the immediately-enclosing function's parameters that + has the `__owned` or `inout` ownership modifier, or +- the `self` parameter in a `mutating` or `__consuming` method. + +A movable binding also must satisfy the following requirements: + +- it cannot be captured by an `@escaping` closure or nested function, +- it cannot have any property wrappers applied, +- it cannot have any accessors attached, such as `get`, `set`, + `didSet`, `willSet`, `_read`, or `_modify`, +- it cannot be an `async let`. + +Possible extensions to the set of movable bindings are discussed under +Future Directions. It is an error to pass `move` an argument that doesn't +reference a movable binding. + +Given a valid movable binding, the compiler ensures that there are no other +references to the binding after it is moved. The analysis is +flow sensitive, so one is able to end the lifetime of a value conditionally: + +```swift +if condition { + let y = move(x) + // I can't use x anymore here! + useX(x) // !! ERROR! Use after move. +} else { + // I can still use x here! + useX(x) // OK +} +// But I can't use x here. +useX(x) // !! ERROR! Use after move. +``` + +If the binding is a `var`, the analysis additionally allows for code to +conditionally reinitialize the var and thus be able to use it in positions +that are dominated by the reinitialization. Consider the +following example: + +```swift +if condition { + let _ = move(x) + // I can't use x anymore here! + useX(x) // !! ERROR! Use after move. + x = newValue + // But now that I have re-assigned into x a new value, I can use the var + // again. + useX(x) // OK +} else { + // I can still use x here, since it wasn't moved on this path! + useX(x) // OK +} +// Since I reinitialized x along the `if` branch, and it was never moved +// from on the `else` branch, I can use it here too. +useX(x) // OK +``` + +Notice how in the above, we are able to use `x` both in the true block AND the +code after the `if` block, since over both paths through the `if`, `x` ends up +with a valid value before proceeding. + +For an `inout` parameter, the analysis behaves the same as for a `var`, except +that all exits from the function (whether by `return` or by `throw`) are +considered to be uses of the parameter. Correct code therefore *must* reassign +inout parameters after they are moved from. + +## Source compatibility + +This is additive. If a user already in their module has a function called +"move", they can call the Stdlib specific move by calling Swift.move. + +## Effect on ABI stability + +`move` will use the `@_alwaysEmitIntoClient` attribute, so that it adds no +ABI requirements to the standard library or clients. + +## Effect on API resilience + +None, this is additive. + +## Alternatives considered + +### Alternative spellings + +As a function, `move` is rather unusual, since it only accepts certain forms of +expression as its argument, and it doesn't really have any runtime behavior +of its own, acting more as a marker for the compiler to perform additional +analysis. As such, many have suggested alternative spellings that make `move`'s +special nature more syntactically distinct, including: + +- a contextual keyword operator, like `useX(move x)` +- an expression attribute, like `useX(@move x)` +- a compiler directive, like `useX(#move(x))` + +There are also potentially other names besides `move` that we could use. We're +proposing using the name `move` because it is an established term of art in +other programming language communities including C++ and Rust, as well as a +term that has already been used in other Swift standard library APIs such as +the `UnsafeMutablePointer.move*` family of methods that move a value out of +memory referenced by a pointer. + +Declaring `move` as a function also minimizes the potential impact to the +language syntax. We've introduced new contextual keywords without breaking +compatibility before, like `some` and `any` for types. But to do so, we've had +to impose constraints on their use, such as not allowing the constraints +modified by `some` or `any` to be parenthesized to avoid the result looking +like a function call. Although that would be acceptable for `move` given its +current constraints, it might be premature to assume we won't expand the +capabilities of `move` to include more expression forms. + +### `drop` function + +We could also introduce a separate `drop` function like languages like Rust does +that doesn't have a result like `move` does. We decided not to go with this +since in Swift the idiomatic way to throw away a value is to assign to `_` +implying that the idiomatic way to write `drop` would be: + +```swift +_ = move(x) +``` + +suggesting adding an additional API would not be idiomatic. We do not propose +making `move` use the `@discardableResult` attribute, so that this kind of +standalone drop is syntactically explicit in client code. + +## Future directions + +### Dynamic enforcement of `move` for other kinds of bindings + +In the future, we may expand the set of movable bindings to include globals, +escaped local variables, and class stored properties, although doing so in full +generality would require dynamic enforcement in addition to static checking to +ensure that shared state is not read from once it is moved, similar to how we +need to dynamically enforce exclusivity when accessing globals and class stored +properties. Since this dynamic enforcement turns misuse of `move`s into runtime +errors rather than compile-time guarantees, we might want to make those dynamic +cases syntactically distinct, to make the possibility of runtime errors clear. + +### Piecewise `move` of frozen structs and tuples + +For frozen structs and tuples, both aggregates that the compiler can statically +know the layout of, we could do finer-grained analysis and allow their +individual fields to be moved independently: + +```swift +struct TwoStrings { + var first: String + var second: String +} + +func foo(x: __owned TwoStrings) { + use(move(x.first)) + // ERROR! part of x was moved out of + use(x) + // OK, this part wasn't + use(x.second) +} +``` + +### `move` of computed properties, property wrappers, properties with accessors, etc. + +It would potentially be useful to be able to move variables and properties with +modified access behavior, such as computed properties, properties with +didSet/willSet observers, property wrappers, and so on. Although we could do +move analysis on these properties, we wouldn't be able to get the full +performance benefits from consuming a computed variable without allowing +for some additional accessors to be defined, such as a "consuming getter" that +can consume its `self` in order to produce the property value, and an +initializer to reinitialize `self` on reassignment after a `move`. + +### Suppressing implicit copying + +Another useful tool for programmers is to be able to suppress Swift's usual +implicit copying rules for a type, specific values, or a scope. The `move` function +as proposed is not intended to be a replacement for move-only types or for +"no-implicit-copy" constraints on values or scopes. The authors believe that +there is room in the language for both features; `move` is a useful incremental +annotation for code that is value type- or object-oriented which needs +minor amounts of fine control for performance. Suppressing implicit copies can +ultimately achieve the same goal, but requires adapting to a stricter +programming model and controlling ownership in order to avoid the need for +explicit copies or to eliminate copies entirely. That level of control +definitely has its place, but requires a higher investment than we expect +`move` to. + +## Acknowledgments + +Thanks to Nate Chandler, Tim Kientzle, and Holly Borla for their help with this! From 79b690a294e74ae9ca51759789847570f490ddce Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 23 Jul 2022 14:48:19 -0700 Subject: [PATCH 2652/4563] Fix minor wording issue --- proposals/nnnn-conditional-attributes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-conditional-attributes.md b/proposals/nnnn-conditional-attributes.md index 2f8d5e87bf..dd32ce2538 100644 --- a/proposals/nnnn-conditional-attributes.md +++ b/proposals/nnnn-conditional-attributes.md @@ -34,7 +34,7 @@ This is unsatisfactory for at least two reasons. First, it's a lot of code dupli I propose two related changes to make it easier to adopt new attributes in existing code: -* Allow `#if` checks to surround attributes wherever they appear, eliminating the need to clone a declaration just to adopt a new attribute. +* Allow `#if` checks to surround attributes on a declaration wherever they appear, eliminating the need to clone a declaration just to adopt a new attribute. * Add a conditional directive `hasAttribute(AttributeName)` that evalutes `true` when the compiler has support for the attribute with the name `AttributeName` in the current language mode. The first two of these can be combined to make the initial example less repetitive and more descriptive: From 638b77609223f1afeaec929059dbcdd221b6824d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 23 Jul 2022 22:23:36 -0400 Subject: [PATCH 2653/4563] Add implementation link --- proposals/nnnn-conditional-attributes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-conditional-attributes.md b/proposals/nnnn-conditional-attributes.md index dd32ce2538..e42cad5374 100644 --- a/proposals/nnnn-conditional-attributes.md +++ b/proposals/nnnn-conditional-attributes.md @@ -5,7 +5,7 @@ * Review Manager: TBD * Status: **Awaiting review** -* Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) +* Implementation: [apple/swift#60208](https://github.com/apple/swift/pull/60208) * Swift-evolution thread: [Pitch](https://forums.swift.org/t/pitch-conditional-compilation-for-attributes-and-modifiers/58339) ## Introduction From 567fb1a66c784bcc5394491d24f72a3cb393674f Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 25 Jul 2022 09:04:58 -0700 Subject: [PATCH 2654/4563] Link the SE-0366 review. (#1726) --- proposals/0366-move-function.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0366-move-function.md b/proposals/0366-move-function.md index dd4969acfe..39d2dc6e00 100644 --- a/proposals/0366-move-function.md +++ b/proposals/0366-move-function.md @@ -5,7 +5,7 @@ * Review Manager: [Holly Borla](https://github.com/hborla) * Status: **Active Review (July 25...August 8, 2022)** * Implementation: Implemented on main as stdlib SPI (`_move` instead of `move`) -* Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic)) +* Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic)) ([review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) ## Introduction From 52d09f0d10e7fc803337d48264ace2ab1bbebfa7 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 26 Jul 2022 22:38:14 +0100 Subject: [PATCH 2655/4563] [SE-0357] Update status; remove `~=` operator (#1719) --- ...0357-regex-string-processing-algorithms.md | 52 ++----------------- 1 file changed, 5 insertions(+), 47 deletions(-) diff --git a/proposals/0357-regex-string-processing-algorithms.md b/proposals/0357-regex-string-processing-algorithms.md index bdb67911df..539cf18fb2 100644 --- a/proposals/0357-regex-string-processing-algorithms.md +++ b/proposals/0357-regex-string-processing-algorithms.md @@ -3,9 +3,13 @@ * Proposal: [SE-0357](0357-regex-string-processing-algorithms.md) * Authors: [Tina Liu](https://github.com/itingliu), [Michael Ilseman](https://github.com/milseman), [Nate Cook](https://github.com/natecook1000), [Tim Vermeulen](https://github.com/timvermeulen) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (9 - 23 May 2022)** +* Status: **Accepted** * Implementation: [apple/swift-experimental-string-processing](https://github.com/apple/swift-experimental-string-processing/) * Available in nightly toolchain snapshots with `import _StringProcessing` +* Review: ([pitch](https://forums.swift.org/t/pitch-regex-powered-string-processing-algorithms/55969)) + ([review](https://forums.swift.org/t/se-0357-regex-string-processing-algorithms/57225)) + ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0357-regex-string-processing-algorithms/58706)) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/7741017763f528dfbdfa54c6d11f559918ab53e4/proposals/0357-regex-string-processing-algorithms.md) ## Introduction @@ -173,32 +177,6 @@ We also propose the following regex-powered algorithms as well as their generic |`prefixMatch(of:)`| Matches the specified `RegexComponent` against the collection at the beginning | |`matches(of:)`| Returns a collection containing all matches of the specified `RegexComponent` | -We also propose an overload of `~=` allowing regexes to be used in `case` expressions: - -```swift - switch "abcde" { - case /a.*f/: // never taken - case /abc/: // never taken - case /ab.*e/: return "success" - default: // never taken - } - - switch "2022-04-22" { - case decimalParser: // never taken - - case OneOrMore { - CharacterClass.whitespace - }: // never taken - - case #/\d{2}/\d{2}/\d{4}/# // never taken - - case dateParser: return "success" - - default: // never taken - } -``` - - ## Detailed design ### `CustomConsumingRegexComponent` @@ -1096,18 +1074,6 @@ let range = empty.firstRange(of: empty) // empty == empty[range] ``` -### Language-level pattern matching via `~=` - -We propose allowing any regex component be used in case statements by overloading the `~=` operator for matching against the entire input: - -```swift -extension RegexComponent { - public static func ~=(regex: Self, input: String) -> Bool - - public static func ~=(regex: Self, input: Substring) -> Bool -} -``` - [SE-0346]: https://github.com/apple/swift-evolution/blob/main/proposals/0346-light-weight-same-type-syntax.md [stdlib-pitch]: https://forums.swift.org/t/pitch-primary-associated-types-in-the-standard-library/56426 @@ -1161,15 +1127,7 @@ This protocol customizes the basic consume-from-the-front functionality. A proto A `Substring` slice requirement allows the regex engine to produce indicies in the original collection by operating over a portion of the input. Unfortunately, this is not one of the requirements of `StringProtocol`. A new protocol for types that can produce a `Substring` on request (e.g. from UTF-8 contents) would have to eagerly produce a `String` copy first and would need requirements to translate indices. When higher-level algorithms are implemented via multiple calls to the lower-level algorithms, these copies could happen many times. Shared strings are future work but a much better solution to this. - - -### Matches-anywhere semantics for `~=` - -Some scripting languages use semantics akin to `contains` for this purpose. But in Swift, this would deviate from how switch normally behaves, such as when switching over an Array. -Matches-anywhere can be perilous when the input is dynamic and unpredictable: new inputs might just happen to trigger an earlier case in non-obvious ways. Matches-anywhere can be opted into by quantifying around the regex, and a modifier on regex to make this more convenient is future work. - - ## Future directions ### Backward algorithms From 7eecbe0d1761343cc245668d63d7e6b69ad484ab Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Tue, 26 Jul 2022 22:38:53 +0100 Subject: [PATCH 2656/4563] [SE-0355] Update to say that .NET subtraction is not supported (#1697) We do not plan on supporting this in the interests of reducing confusion. --- proposals/0355-regex-syntax-run-time-construction.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/proposals/0355-regex-syntax-run-time-construction.md b/proposals/0355-regex-syntax-run-time-construction.md index 8a07978c9b..86c412f32e 100644 --- a/proposals/0355-regex-syntax-run-time-construction.md +++ b/proposals/0355-regex-syntax-run-time-construction.md @@ -566,7 +566,7 @@ Set -> Member+ Member -> CustomCharClass | Quote | Range | Atom Range -> RangeElt `-` RangeElt RangeElt -> | UnicodeScalar | EscapeSequence -SetOp -> '&&' | '--' | '~~' | '-' +SetOp -> '&&' | '--' | '~~' ``` Custom characters classes introduce their own sublanguage, in which most regular expression metacharacters become literal. The basic element in a custom character class is an `Atom`, though only some atoms are considered valid: @@ -591,12 +591,9 @@ Operators may be used to apply set operations to character class members. The op - `&&`: Intersection of the LHS and RHS. - `--`: Subtraction of the RHS from the LHS. - `~~`: Symmetric difference of the RHS and LHS. -- `-`: .NET's spelling of subtracting the RHS from the LHS. These operators have a lower precedence than the implicit union of members, e.g `[ac-d&&a[d]]` is an intersection of the character classes `[ac-d]` and `[ad]`. -To avoid ambiguity between .NET's subtraction syntax and range syntax, .NET specifies that a subtraction will only be parsed if the right-hand-side is a nested custom character class. We propose following this behavior. - Note that a custom character class may begin with the `:` character, and only becomes a POSIX character property if a closing `:]` is present. For example, `[:a]` is the character class of `:` and `a`. ### Matching options @@ -848,7 +845,7 @@ Unlike other engines, .NET supports the use of `-` to denote both a range as wel We propose supporting the operators `&&`, `--`, and `~~`. This means that any regex literal containing these sequences in a custom character class while being written for an engine not supporting that operation will have a different semantic meaning in our engine. However this ought not to be a common occurrence, as specifying a character multiple times in a custom character class is redundant. -In the interests of compatibility, we also propose supporting the `-` operator, though we will likely want to emit a warning and encourage users to switch to `--`. +In order to help avoid confusion between engines, we will reject the use of .NET style `-` for subtraction. Users will be required to write `--` instead, or escape with `\-`. ### Nested custom character classes From df772ae58ba2720422890bb53da24cc059443f86 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 26 Jul 2022 23:22:45 +0100 Subject: [PATCH 2657/4563] [SE-0355] Update status and review fields (#1727) --- proposals/0355-regex-syntax-run-time-construction.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/proposals/0355-regex-syntax-run-time-construction.md b/proposals/0355-regex-syntax-run-time-construction.md index 86c412f32e..ed5b7770db 100644 --- a/proposals/0355-regex-syntax-run-time-construction.md +++ b/proposals/0355-regex-syntax-run-time-construction.md @@ -1,12 +1,15 @@ - # Regex Syntax and Run-time Construction * Proposal: [SE-0355](0355-regex-syntax-run-time-construction.md) * Authors: [Hamish Knight](https://github.com/hamishknight), [Michael Ilseman](https://github.com/milseman) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (April 28 – May 10 2022)** +* Status: **Accepted** * Implementation: https://github.com/apple/swift-experimental-string-processing * Available in nightly toolchain snapshots with `import _StringProcessing` +* Review: ([first pitch](https://forums.swift.org/t/pitch-regex-syntax/55711)) + ([second pitch](https://forums.swift.org/t/pitch-2-regex-syntax-and-run-time-construction/56624)) + ([review](https://forums.swift.org/t/se-0355-regex-syntax-and-runtime-construction/57038)) + ([acceptance](https://forums.swift.org/t/accepted-se-0355-regex-syntax-and-runtime-construction/59232)) ## Introduction From eea9038891c336ba1523f008a5ea5ce48b74cc46 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Fri, 29 Jul 2022 09:15:15 -0700 Subject: [PATCH 2658/4563] Revisions in response to review feedback: (#1729) - `move x` is now proposed as a contextual keyword, instead of a magic function `move(x)`. - The proposal no longer mentions `__owned` or `__shared` parameters, which are currently an experimental language feature, and leaves discussion of them as a future direction. `move x` is allowed to be used on all function parameters. - `move x` is allowed as a statement on its own, ignoring the return value, to release the current value of `x` without forwarding ownership without explicitly assigning `_ = move x`. --- proposals/0366-move-function.md | 239 +++++++++++++++++++------------- 1 file changed, 142 insertions(+), 97 deletions(-) diff --git a/proposals/0366-move-function.md b/proposals/0366-move-function.md index 39d2dc6e00..c2c95faf15 100644 --- a/proposals/0366-move-function.md +++ b/proposals/0366-move-function.md @@ -1,19 +1,21 @@ -# Move Function + "Use After Move" Diagnostic +# Move Operation + "Use After Move" Diagnostic * Proposal: [SE-0366](0366-move-function.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Joe Groff](https://github.com/jckarter) * Review Manager: [Holly Borla](https://github.com/hborla) * Status: **Active Review (July 25...August 8, 2022)** -* Implementation: Implemented on main as stdlib SPI (`_move` instead of `move`) +* Implementation: Implemented on main as stdlib SPI (`_move` function instead of `move` keyword) * Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic)) ([review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md) ## Introduction -In this document, we propose adding a new function called `move` to the swift -standard library, which ends the lifetime of a specific local `let`, -local `var`, or `consuming` function parameter, and which enforces this -by causing the compiler to emit a diagnostic upon any uses that are after the -move function. This allows for code that relies on **forwarding ownership** +In this document, we propose adding a new operation, marked by the +context-sensitive keyword `move`, to the +language. `move` ends the lifetime of a specific local `let`, +local `var`, or `consuming` function parameter, and enforces this +by causing the compiler to emit a diagnostic upon any use after the +move. This allows for code that relies on **forwarding ownership** of values for performance or correctness to communicate that requirement to the compiler and to human readers. As an example: @@ -21,14 +23,13 @@ the compiler and to human readers. As an example: useX(x) // do some stuff with local variable x // Ends lifetime of x, y's lifetime begins. -let y = move(x) // [1] +let y = move x // [1] useY(y) // do some stuff with local variable y useX(x) // error, x's lifetime was ended at [1] -// Ends lifetime of y, destroying the current value -// since it is explicitly thrown away assigning to `_` -_ = move(y) // [2] +// Ends lifetime of y, destroying the current value. +move y // [2] useX(x) // error, x's lifetime was ended at [1] useY(y) // error, y's lifetime was ended at [2] ``` @@ -75,9 +76,9 @@ characteristics of the code. Swift-evolution pitch thread: [https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic) -## Proposed solution: Move Function + "Use After Move" Diagnostic +## Proposed solution: Move Operation + "Use After Move" Diagnostic -That is where the `move` function comes into play. The `move` function consumes +That is where the `move` operation comes into play. `move` consumes a **movable binding**, which is either an unescaped local `let`, unescaped local `var`, or function argument, with no property wrappers or get/set/read/modify/etc. accessors applied. It then @@ -100,7 +101,7 @@ func test() { longAlgorithmUsing(&y) // We no longer use y after this point, so move it when we pass it off to // the last use. - consumeFinalY(move(y)) + consumeFinalY(move y) // x will be unique again here. x.append(7) @@ -128,7 +129,7 @@ func test() { var y = x longAlgorithmUsing(&y) // We think we no longer use y after this point... - consumeFinalY(move(y)) + consumeFinalY(move y) // ...and x will be unique again here... x.append(7) @@ -146,15 +147,14 @@ of `x`, as in: ```swift func useX(_ x: SomeClassType) -> () {} -func consumeX(_ x: __owned SomeClassType) -> () {} func f() { let x = ... useX(x) let other = x // other is a new binding used to extend the lifetime of x - _ = move(x) // x's lifetime ends + move x // x's lifetime ends useX(other) // other is used here... no problem. - consumeX(other) // other is used here... no problem. + useX(other) // other is used here... no problem. } ``` @@ -164,15 +164,14 @@ of `x`, and get separate diagnostics for both variables: ```swift func useX(_ x: SomeClassType) -> () {} -func consumeX(_ x: __owned SomeClassType) -> () {} func f() { let x = ... useX(x) let other = x - _ = move(x) - useX(move(other)) - consumeX(other) // error: 'other' used after being moved + move x + useX(move other) + useX(other) // error: 'other' used after being moved useX(x) // error: 'x' used after being moved } ``` @@ -184,7 +183,7 @@ begin using the var again after one re-assigns to the var: ```swift func f() { var x = getValue() - let _ = move(x) + move x useX(x) // error: no value in x x = getValue() useX(x) // ok, x has a new value here @@ -194,32 +193,24 @@ func f() { This follows from move being applied to the binding (`x`), not the value in the binding (the value returned from `getValue()`). -We also support applying the move operation to consuming function arguments: +We also support `move` of function arguments: ```swift -func f(_ x: __owned SomeClassType) { - let _ = move(x) +func f(_ x: SomeClassType) { + move x useX(x) // !! Error! Use of x after move } ``` -Normal arguments are passed by borrow, meaning that the lifetime of the value -is managed by the caller. Although we could allow `move` on these arguments, -shortening the syntactic lifetime of the variable, doing so would have no -practical effect on the value's lifetime at runtime, so we choose to leave this -disallowed for now, in order to avoid potentially misleading developers who -might expect the value to be destroyed at the point of the move. - -On the other hand, one can `move` out of an `inout` -function argument. Like a `var`, the `inout` argument can be reassigned after -being moved from and used again; however, since the final value of an -`inout` argument is passed back to the caller, an `inout` argument *must* be -reassigned by the callee before it returns. This will raise an error because -`buffer` doesn't have a value at the point of return: +This includes `inout` function arguments. Like a `var`, an `inout` argument can +be reassigned after being moved from and used again; however, since the final +value of an `inout` argument is passed back to the caller, an `inout` argument +*must* be reassigned by the callee before it returns. This will raise an error +because `buffer` doesn't have a value at the point of return: ```swift func f(_ buffer: inout Buffer) { // error: 'buffer' not reinitialized after move! - let b = move(buffer) // note: move was here + let b = move buffer // note: move was here b.deinitialize() ... write code ... } // note: return without reassigning inout argument `buffer` @@ -229,7 +220,7 @@ But we can reinitialize `buffer` by writing the following code: ```swift func f(_ buffer: inout Buffer) { - let b = move(buffer) + let b = move buffer b.deinitialize() // ... write code ... // We re-initialized buffer before end of function so the checker is satisfied @@ -243,7 +234,7 @@ thrown errors or breaks out of loops. So we can also write: ```swift func f(_ buffer: inout Buffer) { - let b = move(buffer) + let b = move buffer // Ensure the buffer is reinitialized before we exit. defer { buffer = getNewInstance() } try b.deinitializeOrError() @@ -253,26 +244,18 @@ func f(_ buffer: inout Buffer) { ## Detailed design -We declare `move` as follows: - -```swift -/// This function ends the lifetime of the passed in binding. -func move(_ value: __owned T) -> T -``` - -At runtime, the function returns `value` unmodified back to its caller. -However, at compile time, the presence of a call to `move` forces +At runtime, `move value` evaluates to the current value bound to `value`. +However, at compile time, the presence of a `move` forces ownership of the argument to be transferred out of the binding at the given -point, and triggers diagnostics that prove that it is safe to do so, -by flagging any proceeding uses of the binding that are reachable from the move. -The argument to `move` is required to be a reference to a *movable binding*. -The following kinds of declarations can currently be referenced as movable -bindings: +point, and triggers diagnostics that prove that it is safe to do so. +The compiler flags any proceeding uses of the binding that are reachable from +the move. The operand to `move` is required to be a reference to a *movable +binding*. The following kinds of declarations can currently be referenced as +movable bindings: - a local `let` constant in the immediately-enclosing function, - a local `var` variable in the immediately-enclosing function, -- one of the immediately-enclosing function's parameters that - has the `__owned` or `inout` ownership modifier, or +- one of the immediately-enclosing function's parameters, or - the `self` parameter in a `mutating` or `__consuming` method. A movable binding also must satisfy the following requirements: @@ -293,7 +276,7 @@ flow sensitive, so one is able to end the lifetime of a value conditionally: ```swift if condition { - let y = move(x) + let y = move x // I can't use x anymore here! useX(x) // !! ERROR! Use after move. } else { @@ -311,7 +294,7 @@ following example: ```swift if condition { - let _ = move(x) + move x // I can't use x anymore here! useX(x) // !! ERROR! Use after move. x = newValue @@ -338,13 +321,23 @@ inout parameters after they are moved from. ## Source compatibility -This is additive. If a user already in their module has a function called -"move", they can call the Stdlib specific move by calling Swift.move. +`move` behaves as a contextual keyword. In order to avoid interfering +with existing code that calls functions named `move`, the operand to +`move` must begin with another identifier, and must consist of an +identifier or postfix expression: + +``` +move x // OK +move [1, 2, 3] // Syntax error +move(x) // Call to global function `move`, not a move operation +move x.y.z // Syntactically OK (although x.y.z is not currently a movable binding) +move x[0] // Syntactically OK (although x[0] is not currently a movable binding) +move x + y // Parses as (move x) + y +``` ## Effect on ABI stability -`move` will use the `@_alwaysEmitIntoClient` attribute, so that it adds no -ABI requirements to the standard library or clients. +`move` requires no ABI additions. ## Effect on API resilience @@ -354,15 +347,23 @@ None, this is additive. ### Alternative spellings -As a function, `move` is rather unusual, since it only accepts certain forms of -expression as its argument, and it doesn't really have any runtime behavior -of its own, acting more as a marker for the compiler to perform additional -analysis. As such, many have suggested alternative spellings that make `move`'s -special nature more syntactically distinct, including: +The [first reviewed revision](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md) +of this proposal offered `move(x)` as a special +function with semantics recognized by the compiler. Based on initial feedback, +we pivoted to the contextual keyword spelling. +As a function, `move` would be rather unusual, since it only accepts certain +forms of expression as its argument, and it doesn't really have any runtime +behavior of its own, acting more as a marker for the compiler to perform +additional analysis. + +Many have suggested alternative spellings that +also make `move`'s special nature more syntactically distinct, including: -- a contextual keyword operator, like `useX(move x)` - an expression attribute, like `useX(@move x)` -- a compiler directive, like `useX(#move(x))` +- a compiler directive, like `useX(#move x)` +- an operator, like `useX(<-x)` + +### The name `move` There are also potentially other names besides `move` that we could use. We're proposing using the name `move` because it is an established term of art in @@ -371,30 +372,6 @@ term that has already been used in other Swift standard library APIs such as the `UnsafeMutablePointer.move*` family of methods that move a value out of memory referenced by a pointer. -Declaring `move` as a function also minimizes the potential impact to the -language syntax. We've introduced new contextual keywords without breaking -compatibility before, like `some` and `any` for types. But to do so, we've had -to impose constraints on their use, such as not allowing the constraints -modified by `some` or `any` to be parenthesized to avoid the result looking -like a function call. Although that would be acceptable for `move` given its -current constraints, it might be premature to assume we won't expand the -capabilities of `move` to include more expression forms. - -### `drop` function - -We could also introduce a separate `drop` function like languages like Rust does -that doesn't have a result like `move` does. We decided not to go with this -since in Swift the idiomatic way to throw away a value is to assign to `_` -implying that the idiomatic way to write `drop` would be: - -```swift -_ = move(x) -``` - -suggesting adding an additional API would not be idiomatic. We do not propose -making `move` use the `@discardableResult` attribute, so that this kind of -standalone drop is syntactically explicit in client code. - ## Future directions ### Dynamic enforcement of `move` for other kinds of bindings @@ -420,8 +397,8 @@ struct TwoStrings { var second: String } -func foo(x: __owned TwoStrings) { - use(move(x.first)) +func foo(x: TwoStrings) { + use(move x.first) // ERROR! part of x was moved out of use(x) // OK, this part wasn't @@ -455,6 +432,74 @@ explicit copies or to eliminate copies entirely. That level of control definitely has its place, but requires a higher investment than we expect `move` to. +### `shared` and `owned` argument modifiers + +The ownership convention used when passing arguments by value is usually +indefinite; the compiler initially tries passing arguments by borrow, so +that the caller is made to keep the value alive on the callee's behalf for +the duration of the call, with the exception of setters and initializers, where +it defaults to transferring ownership of arguments from the caller to the callee. +The optimizer may subsequently adjust these decisions if it sees opportunities +to reduce overall ARC traffic. Using `move` on an argument that ends up +passed by borrow can syntactically shorten the lifetime of the argument binding, +but can't actually shorten the lifetime of the argument at runtime, since the +borrowed value remains owned by the caller. + +In order to guarantee the forwarding of a value's ownership across function +calls, `move` is therefore not sufficient on its own. We would also need to +guarantee the calling convention for the enclosing function transfers ownership +to the callee. We could add annotations which behave similar to `inout`. These +are currently implemented internally in the compiler as `__shared` and `__owned`, +and we could expose these as official language features: + +```swift +func test() { + var x: [Int] = getArray() + + // x is appended to. After this point, we know that x is unique. We want to + // preserve that property. + x.append(5) + + // We create a new variable y so we can write an algorithm where we may + // change the value of y (causing a COW copy of the buffer shared with x). + var y = x + longAlgorithmUsing(&y) + // We no longer use y after this point, so move it when we pass it off to + // the last use. + consumeFinalY(move y) + + // x will be unique again here. + x.append(7) +} + +// consumeFinalY declares its argument `owned`, ensuring it takes ownership +// of the argument from the caller. +func consumeFinalY(y: owned [Int]) { + consumeYSomewhereElse(move y) +} +``` + +The presence of a `move` could be used as an optimizer hint to infer that +`owned` convention is desired within a module, but since the choice of `shared` +or `owned` affects ABI, we would need an explicit annotation for public API +to specify the desired ABI. There are also reasons to expose these +conventions beyond `move`. We leave it to a future dedicated proposal to +delve deeper into these modifiers. + ## Acknowledgments Thanks to Nate Chandler, Tim Kientzle, and Holly Borla for their help with this! + +## Revision history + +Changes from the [first revision](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md): + +- `move x` is now proposed as a contextual keyword, instead of a magic function + `move(x)`. +- The proposal no longer mentions `__owned` or `__shared` parameters, which + are currently an experimental language feature, and leaves discussion of them + as a future direction. `move x` is allowed to be used on all function + parameters. +- `move x` is allowed as a statement on its own, ignoring the return value, + to release the current value of `x` without forwarding ownership without + explicitly assigning `_ = move x`. From bd390be1b91ee14c5db2eeeb082bc95c4eb07e96 Mon Sep 17 00:00:00 2001 From: Mykola Pokhylets Date: Fri, 29 Jul 2022 20:33:43 +0200 Subject: [PATCH 2659/4563] Added paragraph about and optimization for default actors and updated function names --- proposals/NNNN-isolated-synchronous-deinit.md | 26 ++++++++++-------- .../isolated-deinit.png | Bin 0 -> 400571 bytes .../last-release.png | Bin 0 -> 261619 bytes 3 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 proposals/NNNN-isolated-synchronous-deinit/isolated-deinit.png create mode 100644 proposals/NNNN-isolated-synchronous-deinit/last-release.png diff --git a/proposals/NNNN-isolated-synchronous-deinit.md b/proposals/NNNN-isolated-synchronous-deinit.md index 582ddff20c..7660e92f73 100644 --- a/proposals/NNNN-isolated-synchronous-deinit.md +++ b/proposals/NNNN-isolated-synchronous-deinit.md @@ -102,27 +102,27 @@ Proposal introduces new runtime function that is used to schedule task-less bloc If no switching is needed, block is executed immediately on the current thread. Otherwise, task-less job copies priority and task-local values from the task/thread that released the last strong reference to the object. ```cpp -using AdHocWorkFunction = SWIFT_CC(swift) void (void *); +using DeinitWorkFunction = SWIFT_CC(swift) void (void *); SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -void swift_task_performOnExecutor(void *context, AdHocWorkFunction *work, ExecutorRef newExecutor); +void swift_task_deinitOnExecutor(void *object, DeinitWorkFunction *work, ExecutorRef newExecutor); ``` ```swift -@_silgen_name("swift_task_performOnExecutor") +@_silgen_name("swift_task_deinitOnExecutor") @usableFromInline -internal func _performOnExecutor(_ ctx: __owned AnyObject, - _ work: @convention(thin) (__owned AnyObject) -> Void, - _ executor: UnownedSerialExecutor) +internal func _deinitOnExecutor(_ object: __owned AnyObject, + _ work: @convention(thin) (__owned AnyObject) -> Void, + _ executor: UnownedSerialExecutor) ``` -If `deinit` is isolated, code that normally is emitted into `__deallocating_init` gets emitted into new entity - `__isolated_deallocating_init`. And `__deallocating_init` is emitted as a thunk that reads executor from `self` (for actors) or global actor (for GAITs) and calls `swift_task_performOnExecutor` passing `self`, `__isolated_deallocating_init` and desired executor. +If `deinit` is isolated, code that normally is emitted into `__deallocating_init` gets emitted into new entity - `__isolated_deallocating_init`. And `__deallocating_init` is emitted as a thunk that reads executor from `self` (for actors) or global actor (for GAITs) and calls `swift_task_deinitOnExecutor` passing `self`, `__isolated_deallocating_init` and desired executor. -![Last release from background thread](https://aws1.discourse-cdn.com/swift/original/3X/a/0/a06988cfffdacaef2387da035789f965c2345c5b.png) +![Last release from background thread](./NNNN-isolated-synchronous-deinit/last-release.png) -![Dealloc on main thread](https://aws1.discourse-cdn.com/swift/original/3X/d/8/d8098cdc104b2c87072d63a22060106c4dc5b4e7.png) +![Deinit on main thread](./NNNN-isolated-synchronous-deinit/isolated-deinit.png) -Non-deallocting deinit is not affected by the changes. +Non-deallocating deinit is not affected by the changes. ### Rules for computing isolation @@ -210,7 +210,7 @@ Such classes are imported into Swift as having non-isolated `deinit`. But if `__attribute__((swift_attr(..)))` is used in `@interface` to explicitly mark `dealloc` method as isolated on global actor, then it is imported as isolated `deinit`. Marking `dealloc` as isolated means that `dealloc` must be called only on that executor. It is assumed that ObjC implementation of such class ensures this by overriding `retain/release`. `deinit` of the Swift subclasses is generated as an override of the `dealloc` method. Any pre-conditions which hold for the base class will be true for Swift subclasses as well. In this case `deinit` of the Swift subclass is type-checked as isolated, but isolation thunk is not generated for code size and performance optimization. -If `deinit` isolation was introduced into hierarchy of the `@objc` Swift classes by a class implemented in Swift, then `retain/release` are not overridden, `dealloc` can be called from any thread, but isolation happens inside `dealloc` implementation. In this case, isolation thunks will be generated for each isolated `deinit` in the hierarchy. Only the most derived one does the actual switching. The rest will be called already on the correct executor, and will follow the fast path in `swift_task_performOnExecutor()`. +If `deinit` isolation was introduced into hierarchy of the `@objc` Swift classes by a class implemented in Swift, then `retain/release` are not overridden, `dealloc` can be called from any thread, but isolation happens inside `dealloc` implementation. In this case, isolation thunks will be generated for each isolated `deinit` in the hierarchy. Only the most derived one does the actual switching. The rest will be called already on the correct executor, and will follow the fast path in `swift_task_deinitOnExecutor()`. ObjC classes that isolate `dealloc` by overriding `retain/release` SHOULD mark `dealloc` as isolated. This not only allows Swift subclasses to fully benefit from isolation, but also prevents them from isolating their `deinit/dealloc` (including the call to `[super dealloc]`) on a different actor. @@ -240,6 +240,10 @@ static void dealloc_impl_helper(void *ctx) { `deinit` isolation is relevant only when subclassing, but ObjC code cannot subclass Swift classes. Generated `*-Swift.h` files contain no information about `deinit` isolation. +### Isolated deinit of default actors + +When deinitializing an instance of default actors, `swift_task_deinitOnExecutor()` attempts to take actor's lock and execute deinit on the current thread. + ### Interaction with distributed actors `deinit` declared in the code of the distributed actor applies only to the local actor and can be isolated as described above. Remote proxy has an implicit compiler-generated `deinit` which is never isolated. diff --git a/proposals/NNNN-isolated-synchronous-deinit/isolated-deinit.png b/proposals/NNNN-isolated-synchronous-deinit/isolated-deinit.png new file mode 100644 index 0000000000000000000000000000000000000000..591c5cc02689e151f195ae4922c1faebc1ad80ff GIT binary patch literal 400571 zcmZ_02UJr{w?B-cAYDO3x`2xGDjfo%^p1ekC_S{$I|KwldN0yJiii+;hk#V+9qCYiVCl_aBy%JaByz_ zyoYx~=@2dV#la!AhsermD9Xw{(Qt-ZLhLMXa1_Fl^zh$l|9X^V7#|xua{HbFK?{o( zt^z^s-B^=5v`hp~6CYE23~%9NuXlgY60!iON(uBx;~rjR)a;GpobUK`-xXy(>sXg$$f z7QMf#^daCrTzA>-*3cqCL z!%0)SkADRV zYiidZEkYu-cqzog^io{IEOl@xde4@81Fv?o{0BjJQ)d@;Yho z(RTiwB%W#^ev(5UkTL?lh*?Pk-PD+3_H{k1Pk6?JPV)1?LC$;Pk3p%BL+^UG+ z6y{U15csap8%d-O1`cfl=cLutB_1O~_hPB1d!hma4sKz}kEFL|C~RiF*Hh|zK(}G)h+BPV z*u^V3sGpIs;K%Lz8x^nQtcZ5zeLF{p{cyp1!qi)TKALI}CI`KIFZr9eHqiJzkI229HciLd8Mmm~Egb3R zKQ$FmG~bb2rr(mi9~?Y#U(ED#D$~?wmemm6IGm>s!{o?g1)t*nlI#3T_a$&ZUMViA z*V~*!>l2+cO)N#PS3Z41l*H@CSm9pQhqA@rxNSbp=CEiy?|b!i{a9+#fwh2DJ0XmoN)W@_DU1JrTD&d1amCE^lke9U6thls> zyS-odEuZmu+zIc1*u(KoR^;i?FBGRQK`X=3r|H$AqY{*CGd`e{^B|HS`VRPZC~rut zYU-;O&l4ZG-zTL|>!_Mg^pt-}ejZl)8tVCI{Ih81cIUIss7}95OH=V@&TLD~lCVWSp3o9GIA&_&G5)*=ch+(Kq2*K{6=zY z>#ZFHQ8ZOA*#&K0mRUECJ+(EktrTRnegsK|FpM?l&y>EIsvI@7b*iz1=vcqBUYOXK zEVPZ8h@KppHpy!-yl%d5p^uM_Qu{^aLL1=+Nl3tRmG#anb;YhAFW@se-@S>6Lckf1|kul?xuPExYqOC zU^z#}A}z7-J^uTT6Cdvh)H@vgiv94|Ce^n7XZg@k>jQ`N_Ya|hUDgSskgo?TjUAYf z(~uNmkO(@O&t~s^#(Rkc&vd5}CwN$~VtrVrdFC(01P5)u@h9VSOuBOkXW*W=FmF(D@5ck$SEp}^#q4Wliiee}qi zrDwAiS<0FHB@c92#^irIc5?63(^H?8nBFh*kl%@nq!Wdjuk{K$kUB`z5!Trr2qJlr zGY97fa7jAJI}GU(4qjuD@;=;WcE{+|js0im-;Z}rYEOoa7MJ}Jiu)tYxIbi_GNJBV z4_t`uoQ_WS*R>&Y4zvA-{2Tn6Fj+B`^v@pG_)VC>h+t)aE)b0)CoR{O91tyiD(&#z zV)^Tn{wMw;(K%L}KDwl~WV<@$w&j8-$0MIw9-rq;qIZZ0$k+%VM3V%nq{fU_PX?Gd z#eazXV4WB6_4ahR!^F&P`leGgnjo~Nc=hbHxBO6~1m&A3rAPzD^B1#B1~dyIH1zbM z-jiECd{ca@sg3XM_lFr%`hbRn9GvIlJWYL#eBYqQgX0JU$cLVfQLsKgQ1eZBm{HBS z?NDi4Dbeukjev!bl1|_CMC?RTF};~wTQx7)7)m8a31>LloIyJR8w^R4Ty$07AAc?M_#Bot6lT=$jW3F@?GSRG;9X|tp zhW{kf)+(e+`!1;?QR9}@QQbA3Mfr&m!%v?*l)lK{;oCh=O=eYT>5%zppf6*Rf6n>h z<4mT@&&|Cwt$U3jGQ{AC!TmKz&_uc12e>gqS+9>tOE}E{f)pR1vBL|aU9&#=l z!@X9|g;n@7bUaZ(S%F1CcFIw*5z1?Qcmssw88n!!9k87#-00j`-4NEjQ}(+?%g180 zuct%~{@r-+opI$A{3XS72G8k>x#r|b-PYQ#5{=FNnUjqhyHaqfGK9$kqY7xyaYz^K zB6JpTzD}O(lu52#7%kFP*0pvwSnf(;{0gqy{V%ap`23{vB9Gn5 zH}0Qm48o@n4k9WbBdv%DwUYf}iYd2=qRBep*Fx2TS1%c)R-Mf^HBY}%_MAm?iK~gW zE_=eXHMOBt`3={vFT#4bC=3s>=v+=_i&5J4MPJpj#VOqXtm{wB6uMwHqqC5bOOAnF zxd(aEU9-BwQymB|tb6@nN$EvAvf!#;Dus4VzJ?J&?zz!98Czmi=2T|f%;3_3kvoYp z6z*BrtM5Xbqum$xr#%P{&lWOWVjSV?ty7pv{BAOKSRLR84%O?a2xTHOWPB#oc%6zw zZBWc43}+H$jb$ZE3i#-r&+lK3mX(+F^K`dv`LcP-csKo*xsC-9jRp}|phdpbegEiy6xx=vBbB(~!d>6Z6SUz_5 zCjUsbc&BKos*1yYQ@)3D_ZB%0?oH{|%?IZe4bESVn-Y%FE!zJsYu$SBZ=TyYIH3@n zJOAdn$prqnV*ma7U*EX#AvpLq6!M!pIOq0%apNxJ;Qm+n_RqgK8Esib#hY(!GiM76 z2N!Fo>#Y}~);9zKM+JQs9Gpkb{@%9~wVoc`Bu~E~Z{NATQ&kZ&gW7X{Fo&92aC_Q2 z{%r?G!c**~Xm8>A;fbfcor8;*rzG<~9AY=+zhoZfC;zax+DbCNQ`LAP3w5@5BE-$h z&C4uB_~gkG31@RlF|F5f|AycEl4Q1ab#)Zu;qmbB;Pw#UhB{mE@QI3w^6>KW@bh!s zaB#VJIk^V)_3A`&;ub*gxa?mpF;PjfrVMJT2_ry@uG|O!ZCI zr1*Gw1ttEG^Z%*(pPv2;s^eneEDN>2LApx)&tm->{GXNo8~Be*{r{0E%q#MrBLAc2 zKahXNAf{#E0=0AdJBKPx7tD@+Q^9S#bx7S!&p@3chyeV z-t?}%IL*)~?F(^^5rv z^VRSt+y|?KzSvD^u--AjlV|Ui>zbx-XSN{&j6HOG$wQy#+T6($5DE63e%V@VacPS- z0BSIk=kNqVv+d(Scur z{eNT@9xoXRVaXTH)AsGvcgL^Xp41eXFn9|z4%O1wIcnoYIm*8H;Ovq{a*!($BR+|1 zou6{vLtJ~p77+z*FkM!0z-Wu8&nH-x5o~4?NONM%h-BoCWeg#{lmudHR5fvIr}xDxuKGI927ByA}=Spp(Z7bGwcZcLS9jz}9PZq#hPbVBn2F@$;Gk z*z{V=AT8_@^8G;im>7m{OE(oiI=CS;Ufh;%aGy7{d_Ybyb;!ZhT9v2iX`xS5`iWwD z^e^Q`OmNmM;o`)J>iXxO5qPa-lsUP6uSfJ(l~8eIl&xc*tu^#kBh92F6-cB(I2w5$ z)vrD>o)PI~9uKNhE=emT3^P|TwXQDGwH7>z6Y+>gbf(zlSxZJL3?d=zUu)+{5^dGHq1FAI1wm#YOV!z)k9nhxt`@?UDNMu}Tw;K&Z z-qSZHF+o0Q#{Q4<$p?y6gRC7M6JBS{#Rmv7bh&pvnDqRvqUB6mv>t(qT9bWxQrk_% zMQ#_sBh?fYBe1W8Rb4UsPJ@_&CEWB#ZyMBaSF|n!ygvrq=>7$QQvBCGJxL)_?-#Ok z4$+7yo`X>oDwTmxpx7l_AS1hz&r35jRUpq~=#=3_u_7{rJJf{9&1;0DJgNhaU zn2~Mq2l}OdDQw4h;*m{sX`fsF)JE!(F{Wf6UW=H;?p*0zXAd62CiY`>kjh%ByR@2CdOD_M*6R1$$Z259yS|o_*i`y7#bJ+ z6k)LE%Rmy_@sReiBS*N4cv`CEjbdv4440SBjJ0zF%e;fEWKa>G^V>)SkDrOw@x^0vPR zm9asO1SEz?*?GUXFX{CC96Xmz(*8$t8sr;wmdj4Q80E4Ln`d(NdA2cuO(CRkADzHX z?yG-2dF^AS`l)v3LcXzh4vZAd%j)Ic!OAZHZa(B|F^zyG60`;D>eZh6=2WGYUJTK$ z=|0$q%IscHj0h3I8)$Tgniva-8d!eEi1HY}VKA`o8kO_9pNS93qnP{gMxF=ncSVQj zgk&$J%wFQ|M@AJn69sa{?5Ar#8)#6Sm%S(kT&wWqFRbPFe}_>P5|`co`84)Ahp=Ed zf?oJf**!{qkp^MS5Via5oG0D7_RGK4it$>xDeP$I z-{!~dYl93BI?@O{Xel{*RQ+9F@4(Fh_C1H3Vbl$PAHZ(5>yTUF0nFhCupKDel(K8u za-p{ZhHL1%>57Oml9-=97HeW6tD~eopz*kqnthZ+ZJQKg74Jn%h@&p6*p>4R)+wq! znZ#yy(A*&U_+=IMo=`MI5X7kt1xLv1T-#>U#oN%)W z?3v%grEYw{6p`PrEno^W9A3N?mLd0$A)$Uas>M;Tn|P!40ES^gBIKgziusFRY`DD^ zhqHOMOZ=l&bJ9e?@5$)+_$z>O5i0)t!R^NYdjlZxso$!D8J>`^z%dZ9;*?E+%Cs+k z^1d9$mEmqDYuo1s?dRnMsq?ab6sxG+1VInTM9T()p&7hm03lnjg%h8 z<)04m$K^^E4H9s;GB0jL$$jsJpYxDBG#s0$O;e4~wuiZ4ZY)ssi@)o`C|u`uZHNRZ zrT^pAtQLUiXlBGYD#*kI@?vf8+T#Kh5%=B=-CzGgE;Nx17xx&htq>?3`7kl$y}tyz zUKvR@f-mo=)qLnjkA#?Qq4?)cYX;`V&}af#hSe*bclsuay||z&d2h8ZMDn(Bl10)vfZ-Ala7eo@ikzFV8V>gl$*F4+Z( zUg2W*7U7rp0(A(BO zV|X!QPWY{CHJ!p?c&!f-hmxd0y*fv#>5QjohmnlFGK=!&gp2nyuJELgeI4Twx+nOp zc`9bIz8`IU*=ED<87HrPJCAYND&Kb;iYfU!zl?I4> zH`>0774|PUfHf|i%g+FjG|TvTHfE`z7Y7@QT`?Zzl%Ji^s~(GS^JSron69BkwcPY4 z-Mx)_T52ATDAvMoM)`b5HJ8=}^eN>`t?qeO;7J|LFgKU7UjBpx+WU&Pl^x53zCbmX z56SGPp$KlREAJ6TMsYbz7jCtS_;{!{YuL}o``(gX|2L;& zGNQXKx$y2E23c*=ei)h6DO7DgI2Cu?w0hVcvYj7QVgIB~Y1qZ903m%#WNLb}Z zYqgyVJ*n4NmUP8ktll@Z?_)CIe_i)Y=vO?q1Zujhwz8N*{Bo+d%~%1r&xgp`GJ?Ebte6U)hGk9q-XoB@$kCNYHNqTYi?^kF%7NU$!R z!EjpjjMuUL)aMk`zr3%?{YnD+`cb_>TR-1Rr-+vYEyin8GU?(h0URzQl3!L%H4 ziAwJAq(`+kS^tc5xH>yX^4^A94KF5_;=liB1IBmuF{pA^HE43TH>mQ?v}~E)#HU}^ zp*edm`q9H*7GMGjkJ2|hyg7bxx{l#$=Zes|Khr``ZGCx)OSEv%u88x=8=LPG76Q75K)u)*(omK0mTZzyOWFO84{X zq@uloVRb}^grSwdiPmg7Ld0fCE`Tx#8mXSjMx9NoIrJ_^|eRn+8oZGw}TAazY=0#b_Nawv2c1R2LaC8m2IK`&*>dtB9D3fr8ZJG9P!$cCDEoNEeYMkc!qt9gVeHtg|z{p;CHfiM8^*z52Q2Rc`mN0im&uU_`AA25Nnia3#%7 zX?iFr7z33p=9B-pol)$Yk|6b{l12<0m&XD&_<0{RqayL2bj%G1YJGE@M&Ftj+|_UZ zd+l>_ae;VOU;3c#x%D(0Y$&9qeL7{m`_|7zl#e%(&l-YF{Bd)XTTmI=zx%?7>_w)g za;J*~t}+>KCj9Hi$3Z_VrUgQH8m%I!QJ*U2QI@H`C+}I0-h2%#kf3IKx>|zD=fK-J z!t>AfV?n)t=!+Yr?afCF=~Y^)e2f`WD8nL!zbV#K0A{oMMUox z(k`mK7p|(!XBw-`w`oX*ri);$HacR5$uGBUd(KqxTWm|J?Y86dpBnEV!oib3ax9{I zsq#W^7cm7SpZ&|P&4CY;?EeX5P3y)7v#=#*v3i_dYZtxblKv(h8IWrsPa+U+qX^l0PU){ zj=4@=EK_3fV+&4ANuLW9Tzpa$z-uk-Y)SaX6h(BcU=Da#&~kSXG9s;el2ScnFU6uF zCgiWAoB~&_+ALM;*ZYyOI9)VMH_PLR>{R->YP9z_m(mjAp3h#KrU;EZo98wD9}K=!XW!AU6!rZt>rwuxail z*CH!*SinwWvde=SF;TM`bqE^h+is8>@O4(p5!18}tFfJz16g7k-DoKI9k}tc!aWkH zjkn2kKW0j33FW~~}HT}liDXt+QO+^Q?2O4IEV%PnCNr2CKmuC+un0&-wz| zb2kx=h~7;5Bgl=}{3oaTx4kelMeEdo5G5C(GPKY7%%AB+MNLRNJ^Retz4BThz)i%z zje2XyCRTxLDM;bqp`ubU2>B3##GXp#KYkW3r~HFthzqG!bavUg>{7Tieymo9%Aqh9 zTc`XI$>^bEbo5gDA&M}N@#JB%E~=1-aw90*+~yOMwfupv;7e;9f%%g0bAc(F(34t0 z$Pc_ZT*2R@C;E8OoRYbh%(pX!nwepSG@{k&JJWUY-XEs=To*95v-(bPO)ifm2{atqLesy(6^c+hdslaLNGCo6|@?xDP zw#IfUe;1B0v9VTf+N)Gf6LcDz3kRPe*e<6_!3mGJ3{0g$?Owj7MHPF`6~2{AhXAF0 zFl`jV_PXR)78hEF@tDTex?a{5xowBgLdTl4cgH#j62{cT_fhf?Kwaw6ywUG!A99we zx&65#MnUqNeO$Z(3pza;-!8Vwb~1z=wd3y+F}Bd|A2K!qUVEcGW6C_OiQeDrNX5@a ztlw~8Vh;L%He6(psLP*1KC|w^1>|oXok>#Atl8)0AT*GmE}2xgBqbR|^HPl7sq;Z` zoh}|;6YT|}kp=Mf+t}z%cX*p^?w2OwWIDTTEN*~(6Y;2^>7aR>Lx4=|=n2&FTCEzm zc}{;O{2W$|2SZ`tODPvt%5|`ptvjRQ13-giNjQIqMRGcy<3UmnPSaQNl-#!>N2@sW zaN2vL$Fi*o9}FI)UU%=+QUq%U;ORwnw7bh$krE!4IBIQFKl`zqFo=s(U8Zf5&i5#k z$Z@}5He%t{IwJb{XAL<`x=|-Jm4#JS?xWsPr@5dbbEX2C;S+UF_;26(_`_fpNG6~*_;Wm7hm2tf?i!PptS zwtz3fBfgLjVK6WRb*iMVivMGk$|;3D2MxlKsO89N$I;ahvt(F_Owf2)(|+>BqE)t_Yp?E~ zrSl`C3INT=3eN%QYyeF*O|oB5;s-d>U1&7mC4Po*w{OBrAK#zW-LGNauU;c1VZ3%< z9$#)^Ky+cPT=vM8TqY^?wq6!kYdz z&g4OPc}pi3(@B`qhs)s{@V84A5bu>sst9g&NI1&8c)&Kuppo-G9z?*Ku+u z3DT44UdeEc+)uZZ5)|M9*K(GQ;+~TEZFL+fT*tg3`?d5LeJBWAtJNYvkANi|Ovy$J zmdO6s+$?rwln#Jur@!b# z>%&vpzG}TGgiBMh2W0|)4Bix87j;l00yP=|7X^pDT z&+Jo`${dPViWZeWE_q@X0DXkp9~r-7r2H20!z25wgG29K*0I5(17zI?RcC!(M2Vs| z{m=DDRJ1GsJ;mnl>C6mCw0ZExA2az2#i~DLYM^>kx5hIV$q4_cPTC+7$rE03uZB%R z(R*Z?51G-lu+#Kv8y#X4Moq}8I-`{dB&tgIC&zRL(zjj=@xWWS<0ANL$rwElc@93} z!{SM~a$J$2V;?v~#z-691``K!N07g&o4WrXeFW&!=^sY`OTY-{Jc%yXiF&Gt44J@% zr}hcx7-hCSrp!Gq=j;8OMQXW6D5W13hPO`q>Y#pi$aO$IMy;usPImJMUX9?nS{mB` zuR&+7kaA~m2@`q?#Mrks80vz@W1v0Cl$J`X%% z=wb~TakWu1wV7J+bv(weVSHY!~QG`xGi^ZrcUCT1kHSo z@U+hld8kA-csF9Iu;FLD^;Ozg81h1uLzlOg%!~GF7|Ha`>Jm9(*F#9s_<=S0qS}37 z_N`z{h5uG%jAe`HBK!4HA{w!K zrMHG4xrk8}ngJif4xeID8XscGIAXkauFR~?eRg0%+CUF%73B(GkVT~%MDD*o_{20U zB?5CQ=rOFsapxN3q6Ge2D*JNftJK6VpaIh}xDs)~g9$mfLU`n1nDEYXd)2R}k^q<4 z`H)#Pm)0A{_uDL{a8uBXc=4LlJNrs&tL!ViOdM^;5;+#AUyGhrf?rekMbQ^-pI0kg zQnk40`KN%y#se3&;#6yy?{4`IFLndzW3J1<0#k4w_tiX=t~qS z8n0c2F)?21$jSU36$(43;?^DGJEBdqwjHWWFruw)H{1w;NH{U%MvZoA?7iB{?_0}% z8g09AEE^^lT;=-o(Fd*BIK8n*>KS3lB8xvDgqM!kcAE_3O_OCQ#AZN!3sHqO|8wsW zdj$mw=V`Lr=ff@f$HQ1N?PxeXq@qIDsz0E6+n`fiDc$2_ZKPzuj(I;{MKz!mO^&`A zRbfk6-q^ndM)VHw)Rx_rDGd8u#!nE`Xw#x)ds6H(DscP|rmF$6Z?G@MbAgq1rZGs^biwwEc~Ch`7W`|wWEjiu!7DEb67(Txc~Y4OdDymR>JN+@G8cm49uGc*xT zPVd?7Zke)nk*X?7NQ@)q+HVw==%SmdHbK&VDU<|@Vmx0`g5mEn_AloiF{S}CM%T3B z{&rVKB9_Tt%a+L(BY`<1G|4&KS+n8}{ku`gg^2f@t4c|=RXS<&Can}`AUy>pi<$dW z;Z(Bm$Ie1k zP}ut=QCDg_Mfu+iXSkW4>rzF#UuWQPXBfO6(*Cxg(kWJS$fBBSPVi9mt>Xwy_Ml)0 zDLtV_clcR5Z9{HamkZ4|E`xGj5_+M|iiP&Wg%S_%^-t%J75+ibQ+fwzMj_?x+XB0J z1pb^e__utSgi~d17qB74470J%J`y`NnpFGbxBAVZ=?_D}2HAztTgAwvSlr8BJs^t} zgUgJUrt}?>p%v-p+0>31Cmsbr&i%yII<8Xv7WNj@Nk3$7iw|ow`)7F=%nR-(+bpoJ zosq-szSVAc7uJxhFywC^eKuW2jU0sp2$k3xV`=-;7xk-5Chdlg1nssQcfaamM`%wh zA+84HN9Tebi@T8B($J!nj*tjP-EZ28b8*${_ec^tf!7?YtTUpU8D9@e;N#a5vSZnp z%WPYhqA>5!V|N^vpGKCSXjUjMqmp(N92L!dt_BB%3zC}Ib2@|a>nK28m2U6=H5BHW zOo27SvpJ=f$EcdObUYFnHNBLoCb4qIg$Q0!ZE8BXGih)zt<3QUidT+9^U6k@pgh(hlcBR&%E~0IztUve?uS?-=IX*n z`=rWim2T7rb3Tpv6jEWxAfuw!GZVKy?odB$bZ;G&{)5tkOK9hedG6obZz~v z=(TbB-k{RFs?rcrr_g4rbRjxzX+13506Sxv*euCql-KfbB9NZApXl{x?`|lkT*|B&ToUg*x-x0@tPnWYY9L9>8YmsxK^Pen7E(y96r_NU z2{Y|$zbxa2sGe&YvxyfhO}G00FA=PIqyz|(%0dQ zCXtBV5IR~8;5(W_w@iVj8I{L7TMccG4%1N^sp4KK_!wgs@rYXmZqs$kjdT&eWKWUa z7k|UjSBT~H;-|idi`sY)V`=l^vhL$vD)JCCH_@jxm#fd&FHfl9-{`26p%knt<={f> zDsFkdOyx@15!G()2^~rNv^80pX65P4=DTg-C2R*gFpm2ZvP*jDF)6<0G4B;>Vc{Z! zw=h+)XRmz{bRoBo&>Zl*N$3v5x@Hh4An#(C_$IX@BR=P+zHghYpaKQW0F z_(g8g?vdM;LLS?VXskcVBN|^*m-uq=+l36#Pe@rm%tnKHfA^JP<3S^Mw|Hw|_O7ez z)lVuZxZ}1t1!}L>dw1;$Fa0sK#5a#_d28A2q-}QS13cEqKH0)$anI?RyjfY%xZkc5 z5KRUp>vAc{+LLp4``KEdea~++LiXgQ3j_#u7+c17tav~CEVnrX-I;O{%3=FM-+z-J zQxbEQu#Lc;BFM$#E1QJ3S+^xOzDl8T&*I2>IF;MM)t;JS`{PCWg*@0_+23Td#tKr5 zvn(st4Mc&xVXOqb|L+eZ=%Ord{!G6M(d~9>vQ%!!GIZM5Loxg6tf(a{on+?K*R|N} z=5cEBde=bKyEQV^ySQ;?*S) z_azX?3i49&K5V_FS@PA8JL+g4$bO8~J4b9X_!J^g{fLn^SSntDOD{Ew*Yc^A#<$AG zWm+vTtM@Z$r>*s13N;nkc_(6Cc_ zbGF^+G8{KhH8#}|S_Jn!FCmq^r#*}Ufp+)KX7{GY6x_5BXGJiY3d&1HudNkV42iJy z=Gmv@?4cK0_64`5R=n#RsFG-#o!=R30~wLxM!3;%|MOwGIZj}PJw{J}+rp}~Z4z__ z9LdJy)ADnp(;9I?wwQWyD~pUh=4w`8gi@L9tXZA|WR8U@Uu+Dv+8Zu^x!!DPB?Y+k z50z;n7gbZQ%`3Mo_IpN?hQ2+c-X2*pO!5^llyN~Qu^!k@)%*2j^Q*gg)Da85F=8jZ zf)+aya_`y^f6_|EJG@nMitsAHm>s=p=T!+Zx>&F|X6WF7JY5;`5mJq(`N+3X?s2n$ zM(>(%?KJt?nv|1~X`7k8!EYGHWeWUVvW4HkJM=@z7@C)3f-5Z(pjCM5)y~-#OH6OK zPd?laW4kTpcf8z=Z)KqM6c09%bIX6znQW5eh3+}*{N=RLVkqa~bSpMxdm7QIcFFh_ zd;c*y(0inlJ$j*r_2cQEE2zO+B@X#_eOj~GT39kkv_j)?_pV^-yY=RFO+%W>N!^Psa63@3oJ2G@nw@I>l+4? z$k}UFL=`i9dgh z7B)W|9e6uNeLLxy?>(k*bAed)3FZod0`bFE4$MVi`Z%GaU0AGwuzBzDndW7&{W&H1 zG<6p_cpdgy$Qg4koZoe_c_hQFWmiypTHZlQ1}j_E!k(Vv?6O*6OUCJ)dvnKKAC{2M$QWc-I84^OlF-h#+- zM>6oQ$siBk);#0rkAv)?oY<#tK^ho1C5y37`j6-rRDtQtD|N4zb0CQ{@ys-gdK6FQ z#MLks{uK!8O1ug+hW@~c>ryM-y!FG={23!tfc-kmWega7s}@})%I>!1rNM6T3*$UA zaZTY16j^P39IVx!`{Z*=ik6F~BBSN24D74BuX4c;2o`!@%TlZq%4F%HKKS)1a~jM) zY}FP#VuY;{Z2BApQg180^o;`>>7S4s!<$!~{Y+Q6H}v7{T_rYl$s)srM?Tiw%ek9V zX`+@5kj^!kH%9~UNM_jm*YZ~hCY67DpOfJiSxY-ht98*glQE1aUncNQ7i-f9NUqOu zV!q-Y8Hea#b{i96DOW|l3Ha}qFhj@*CNC9She1mbKOgVof)v4$E7*7qq8YD@W@C8S z-1u4ySb#4y!My*dW1)Ic%tk;a?pC3d6=I99&7u} z{`~p6Rp>F)e=XfW%`ecZLLneal;>Uj6IFzn_f=q31#kz~gtUyox+D?fP`Kc<>Rv1zMBG>_%e~@N{|Vg9y{V z_C%f+X@z5(zQc&|SXI*A(;C$I+k5{hw#q|ul~hNnwkhjvz>hQtHsBzKUS@mA(qXKT z^REI1N8vRd{dy5&_nCriHj=Z7B^!(jb-*tK_tD+mlNTG%&Pd=)`)A9;`8Ooj94Lu( zz5@zZwLxoK;AaH4LW+^AqL?8W`N0&P3vCC_1wJ=3d@_Lk+WR%GupP|-eUSp*>nf{Z zHPH3Uu*4H#hg#4{rVo~9tRqJ9hXi3h_7dC+@;I>FMxPFAXL`QLT@_B`8k5NeTb2)t zA4-s%{1Gck*bc{s>e=$TmAH3XQq1?=Z3bMNE>BpCRZULP#X@;RVG zzuhCK@gQ!1+=8-scv58_hS@`mD}O)Lx#qyee5pKzIr*m*-x6qk+YkF@VV@b& zPPPa-(c2OmY|er$-W`fT=f<~L;ka&4>aha?;enDx_vRi3y>b!p#+yS_RupApkdpTgHsC+ zJtHr##?vzKF>k-s-g;`-M&AZEpqJlob9w633zIw@@N6@adgXuO+#mF2Z@-HQ{D}7F zSBEWVOJ8$1l))I_mC(;Q8W!KA+Md*3k*B?)BGD~Dqc^P}Zh`L2{3#15h~-eM_68fV%AlVKY@}UBO-Wu==J=*L`s}m&YAmT)7z-_i}-FI>(l)>*^ITJ#C>cd175sw6zOlHcx5_WINaSb}wMPJx>R?5`?`~$esil?bymKTh4Z(7plcCGbfT#Yd z=D#Ez=Icq0;<8mc{Y&*?b!~K;!__Z&lEG5Qn-`REi38G)vNbb_VGLviALTV;=Le+0 z?w1zZ+pO6_QfQ~QyGt#|c^2Bu-1sv^&7a?}FJR}l6W<`Y@lM=(hdaiVx#gxmc&xLZ zz(-}%=O+BYKc;KGZ-Bd4{i-m50=jUMz|)f-DK(YB#`6Yi=_w97h*@mAg$|bVnuu;o zEXuKDf2j}+7ie%*!qUB>AHD!5Eukr<%Wsb2PvGa^y`owKmmy#t=KQ&u@rSaOY*9rW zHk7Pj)wI;WrbyObLA}tm9Y;%&{b0XdBu?*jsw&fOC(@oIjx5HqoZR4iHZP2tFhOE* zrbqgBlq6y6YOk_7DOzDM=hliY%6BYW?MuAv(M66{O@Y~&;2dg$O5)S$uXt(!%QPWs z_a%FEu1{n9YK(!0ajrzZ?lReRiLAS9=lhv%Yran+o<1f|Jn*6;98Sp?A-C$!)*hkX zWx`O&AKcORmprPG`opEkUKl|ch9(ia+s%Y?hfr|&Mcyg=oIZE?D~t7)o5!kI;6|4( zk1#<+dmdYoY*|v-PTkv!Ga1i%1gkeQ{K_m}>TaJJ09m9{McsJB?`Ir)-}M1Q;s;8Y zt20GLsjIJ7(Fbaf@oVFN6G@+fwHOe$yT$$t%L1Hrlgj3( zKeF2cfy_L^9Kyiri|5ooI6+JIVOL+af~yjClJiXiN-t2RzW$AjaRR`Ne%z=wnjavb%;0ql8mgYzM1C2IikG|}U;bF<^X6g78 z0;)@{j^}+#{m62_2wOmkxR~8@UFo6{%jm`iV8|54YsarYHTo;{UlyuoJNV>Qxpr?C z6=@nQ;**dbnd#@a#x2>~ae>^~+Bwb-Uvfz{Yh-@FOGJePU5G!iZ_2dveY?`uqFUc( zlo$Bt=7@IX@MoThWkZ(VpN2NQwkjIp{SqBw+8#aU57oA@eqV&X`k16sIoY+G-<$ch zi1nS9;M(+85&s4H9;z{r5B8dzgf#8fPOC(>%GyeCtMd4~pZKHys|D$;o|ZGCo-7P1 zj3Txg@>RgCsoBl|XOxJYdyP(Ua5rF?`nEjl~OEkc;yDZTNi`*&|9gKefG2j#+C$I|v5_yS1=#mmVKrb+*zKMpZvhxnT*c zg_sLJxSm*ZUi(j3vca=}6ntZ1=9uXd%?gOV=v?R)(t^v7f#3YwSv53`-=b0qQl~&G zES2$&I#9hC6|X!-1p>UdaHrx82^DIaO*|Qbx6n7wJ-9o#g4)-g z8NG9+BPJDrpHzt0T3xKAuO^Y$ech))zaqFNa1>REl+BUiN76~7g2LkJ*7y1RuIyLA zK_I*H$l>o9c~zupdRWGvWw2W-jlbfrSV%$hLAf*E83(ik#zl5F^bH)u`;~vjOw?7e z0nfZ&u6SRT-XsZJrIco5k|!E8JjTgau%=c>vx)^C_BB_^B3Ph~Q#0$OtuA*^(YR8J zw)bD^8rSc(j7Q+Ti&yt!EIKVYVQN0SPD?fh0rL@8XHze?Q_W%?sTL#x#*O9;;JZJu zbJhIIj)Ti;U-)PCI|aN&98+e+g#`Ri74l=(oAi4HJ6j=vAfajv@8GUD7Wo3?UQ6Bo zqvw1>s%IyZl;t&!ttQOMAnA=GOn5*x#vEqQ@_L5{H_`o6`s+NERY%8mr|o;LXMl^Jwtai! z5{tpZAp&@N{00$5yu?u8OZoPoTWY@lwM5SG>hBBab91Aa1|Ga2rp`h105%X7dqu`D zT?02GizTm!XOi&5hpVkQfGSTc;w}D@JOur3Lu9?;D$(BsU9rigr%`$4NVH42|7*p` zW+6mb=h-Qrva(5@S(F)r&NBU1A0abt(#=Vyx~<5mkWrIdVY)X}TTB`jaJhJ;9z(`I zZf<>IL!;d}8R;IT73E!Q<66Y`)@O!Ts1x373$xjI@h}8M-&!T5r=p#5^c=RXj?vZFraEhaqKQw>n{%`pnk!&|xMmOrptt99= zuJ8;4k%9C9G(Xx|1jf^c zv|jGk0gXOtQN4&c^IZ%Mq)=+T)|p%oDfVlz9hE^<=rST8l?mKqQ`^`BfbL#KuVRO! zaq-%WB(@2x{{63nb;hj6r^h^bzBtt3q5%8w#|=Q4O}-+PZd=|VDKY3`0hBu5a=_DN zjeP4+p<V|cbhRMo^g{ig=}4{>wr ztg5iYXNfY;0PCD-prWQj`vj2?>{RZ=tG z0lZla@_X$(5;n%D;J@G5g$m_4Xk3jo2zY zr`cHexo=hfbutx5;Z4Ag(o19{y)luz`m*<4UV8a3RZQ*Gl+ru$N|l*93&`bxuBN)l zd2Qh+n3Q309o1OM)jS%RCE$|)vrb&Q5B=OBSU1mk4Ke%B z#9TFwMP1dN#d_5trFA||e1Df)toG;3wF9+jIRj`#qcQgxe!P~?e{vnLIt191yQ`7q zYv5mw5a<$>XP=nW*>Y)Hn_pne0NJ7Z@xHS=BP`E@j~5e5yRm+tPj#mD%E!ztrdU!K zK2%1(ZJzk)u1N5rfUl{8RsqpGm!^c#Six077u+8LR1T8L@ddJ`Y8X5WkMd^4Y>>Lh z@58z|GKs_AA`<7H_;3qVwS})+x-2}gLNqy}1f&OUr=w)P0FOp_!r6}tkr0@bu+tae zSishB@oXThqpp^7=-$#{2vH;biEq6z6$N3}EC2GbbTcO);ydnpnj&5sA7qEg4;c11 zBSzri6Ap-&Vzipz9X&Wh#gkpK~Ur0i>>U!3bgnaZ9bq-*ceR&xkaYU@rW# z7PEJ&3RInRoyC~V8~j03C8-mJjZC20yYZHF^^oMBgh|qe`<&3rSN$qn_HLvUNztsL z&b(cBg+;KJu;9v-*2_i%Y?2ccu_sdYq;*v2%g$bt*2V=T%Dt=mEi_mH@A#-Zn}FtxQCA42kYE%^;`iheoN;7C21GLW&AqL01@iw zkX8sR5lu%qYff_WqKpwt?)layd_P`@CaU;F=&}GfOZz|N{jyb!A`$nj5?~Yp37Xc` zn2CGM^l-8b7)egL-7GE`$TS;y@!-=YGVGq}f5CoyO^O+|7!viz{}_j#+ij>X(+k^) zqkkkiZcKF9w$-&Cj`Fb__%2XyCidw(?FbX>mf9u-&qu6l$~`(_6HwIE6f1aw|3>wf zkJWDNU&$aR=!#9C%1pbchJ*#9MfORxa3y|(N_t=92<&km2AFo245l@ofk3k!W51x% zKZvY!o-cKK8Pn2IFrjoM_j|%SoijV^@`IPdPkRhM2!PhVuB)6GDr?7PJ17K;N)cuD z{;>?8TXdLq8zlUeI4>s`>O=}a#!btbzA0eeKgm2*%NXwR-LsldoZKPMv}P%8?`_A( ztbYEE{D#p~!cmvu#B{LQnOWZhR4hj7sJHh`Hn#gXcC4E9H4`g`cM4dXEGe^ikPQ-lc%be0(@zJ2*cgBGMbR??BgjQksiF? zzFF^)DM^R*Y~T%4t;JJbHWUroAR;ZjJ?(XDQ-mIj_KGJApT+RW^8&-tI;o z>ZKfb)%DP^RWg3KIAONnb2O@Q8LupZ&_1w=b3nkO!vE6N;_pf#qB)@k;mwlaWETu* zfbl!&Qd)Xvw!u5uNF@{yg%kgl2^e7&vStTxz4)%`Cwd`6s&$_fC>pY&Eqq8d0UWN<$v9#sW=0f&{Pz6t zz2mC!*Kd*g;9tdw5(a0bGTU-+=ZP;*Mjv0+7?fMLjB|Hcn;wFC_cnR1K=B5oos|or z)tb~H8cI{4fAC$1%F9=`j5#iE6&_$Y@kFFdKTdyhtyE^>R5ho6BEex6mx(98>bHzC|3oa4r*;_`Ro-R-_3#n0kdwC!78$;@-NKy6DPuV|ItmZHY|D!nZZTFR+ zul}DscCsA%iChU?s;!Jtn^)nDkFBUE2Bz=5^F}~u@l9vl0Wc%t=U=#Y0Upm8gW@ma z_*%CRs}UIeTQ>pFk*vqlTfyDL{bbS6|LWi@Cn`O}OMPN*sztMlqKGve(n)fV4I>ON zW@Y?y^7R#I@#|aoyhH!pPQ&{M-ERqVm7FZb1H{KYb{CUW!%Bh>4s$tcw}&zfk?Un@ z`6(}}j=Ax+x1=C3X|3B6{y!I9%yrgJ$(QWq*A!qf0=YfyC^d1#)?5iISH;@>F@B<@Ux ze%&%OH)iH$@G;wu0f;RBk8beGVzk5v1_;_g-zdq+;im~Dadn4WwlFz&q}UVQ&{IM{ zRevHP+tRI4gTL<^Vf#7PqB+abX_|}vaUW=5^6bB~Ft?{GZyDQPx;-k^P&8+u3-Ao< zNs@fZuVbg>^-0LqeTE;oJO1QmiQzdvv;%ya4kF%O>`}sNrL_8iNayiEWDV&yXEo&Gyj1RV$~eCS4Y+o z(_B)ADTTvklQnQ_-=&_js!9HPVx%xho=>w6m;o8LVN-|qNEY^qZ>i41Wr$s$Oypy; zS$$qJ+8f^od|cBBj=o^seEbP-SpDhWi?}DUBHse1x)2W(!aMFS1^>SbU?p@a5ED^f z?bl`dMf%wU+RnYVK>{KCjD!{6&lmiG?j3EHaB2{|mo=mw^-bo?HRbVjTULP)D|8tIzOG6J(sc`_&q} zQkD-AoAyuoqnP&XBR0doxEYYe$)L=8^j6*%QDkc0Jzh;-;0vlJ?LMggm|5$i^J-rJ z#wrP64HY|3=F@+TD#AXFH3iQ%h#G4*gK}9dpqlv2IEsK8^qH0BlykIvcV=|Lhq;^WYapkm)y) z4eAtqd{exiceQ>2(iG<$Jj^zm<7EIBoPL$y@42UJrmNx3=(QGi{h=ImEm9*igIJ5j zyab=m0CuUz*~|ytJLj6e*{a9ALNxU29Pv{&E+$#8x0ng~jkk8FXPF5>YY%_fR$51cB;hueF%D#9)9t1?Xw5RX&K;(|YbhZv7l$-ujt-Ou|5 zUW!$31#2;SNh5pIJ}Ne@U4I@av!(I+APMGIz7KdGE(cd39qZ(jw5rNKC`BE$@HLn* z-;Mcaq^C1Yo!p+hz{C^&;JjbWk7>Tx_5J-9G!I6rf5K_hNNrk3u0+Hk zlWAT%k(8fO+noH6jKeWH7E{`EgL#;JHBaJJ@P)`gDf7d0cm1(-Q?%d9{0{sYxIDZr<_NozD;4`VzIC z(M0{o^Rw>nh{SMwt?^k>A{p|Wr#OhkEmW;{*5>6a$6NlkxD&eC6V*BEsds)N$aHv* z;fEtni#;v2smYyb^r5TX!~3B#r-(c}8aVaqDQbjV0MB7G?fQ+1;#WXAz0nBlW8S!B zfMymv64U+=Pq!zV05h~5ss`5Ocj>!DM#k~Ol^vBb<-{!R{-`sO&Uhr>D=yQ95;oGu zTLVW`FE_fXBP+H{Ae7N)J9?81VuN&WD7>a^y{vTJ*S}u729%W-i-A9~c|vS2^7O1u z8O5Qru|noOAuw6}{QK~31w&UUJu(0gor@Q2F(5(Zj4#4PL`bh%D6?xnqu>*e~`g!7MLm42ij z%$awWW|UWs|4K8#K8#b)=GA}G?N_ZPSlsKJA$AM;*V#7E?3Ulj5W%Mi*j38}I-}I(kO+`k& zOg`TmvoH&~(#O4{8<~-B!jy=)E#d5(HYf$*B6?Mp?Tz@yjj(UQkG@pC3?-yBh;W8N z_^7-xKYkKdM^y(DNsAO1Sv@tZmZ@D(6X>{na^zRRA_*(~E8q+BPc)ZSaaiGj&gKU? z{K>l38@XFsX4J*RR*au%mZraZMp_??czNLY$L+uje&(vu{%J*-`mOyq z&9G=gK15EZZT?%v#<4W>gU~&LvKPGb4i`nwyp)u~Oar*(9_p05MQs$Y^}YfQO7Fd6 zH0kTSD;J+T*YM>xNs8o|WNBNz&_#vNudBq}7~T!FIWLD^HM2Y4>WV(r@ZgIpG*?W0bJ_D;CEqGMh9eF zhNE^pOca`ui_KAar9oAVUt-qg4;SmT8HxXr+BwQgZ29{-r6b<;z~Z+#l~K~W+1JN8`N5?*nCoTZ2Q(`{$(C^O3wN9~w}%?&&69XEt$*7TOLkkl(>nt|iQe1& z!Hu)upq*X-E<5i=A^E6t&@`yRw;E>;JR_8b3!KscW9_$9t49CUI_SR8ft)iryy*&+@}-XMsZ06`mB$k zLg?Z!P4QWV(NcYdwg$-0VHSS@G%A%5jQe=ZLxH*5gTz#o2Whp2CJdFOjaa=5;LR;+ zFMs07LPeo@*iB}bBO54F<$H~fp68a|;;%#Sm{EaP8D9=FXC-q3O}l+j+!h&N=Jw=o z#LhS`T2zzPbPtYktcH30r3rI+vwb|1Y2_D1y#5k->u42m*X6yxJN%GY(M>4;pPlNz z6{vj?SpuF^<}5FaxWWJ}W}?Ww&)%UWX2kL`?EySXZ8F_-idsk!5_pl0+Pc(dhA_(o4_DDq>(cv{f?Xw~iKhB+>wlTA zQ4bbR`f%2nJ}MO=whPy^o$otRF{^gB*5J4;Jo40A{w0ro$=0J@Lg7F$=SW>m`0YrK z%uDmTb?&wf4)UlK5FU#?kQSP)BU_R#@M5&dXSqQC|M%gr|$X@!qaz;Wnxk0TTSwy>GkIHr}Th)N|Kr zs}auo1+<~`kh|lp(>{?)r}Yu5&F)>#!ll}-&fG~YDta-B`>hFC+vM()i=9&_nSk6) zNtM0g=UQ7ovSR7ohvdUYQ>pqG#3d|*WmHb$hN}R)dY#E_u~RjPY=;A(tO?9tghHoo zmp5d7PC&(xgSBE0=!yYuKJXwGdUiYQ?R|#mAj2t5dc$lVXbR9OA zY++-WM^n>$E0qg$y_eqLe02mvwu}9=+UC0`XYEFf?^(o=mi4_<&p*E5_aD0bLwN2n z@}+8bDsY2IL1MLWC*r>*mWI4YEZqsW+Q{Abhyxhd0C9^&jef6e)c$UigD6 z)5xzZogJ-nOn9(DD>-cr7;hqR%bg=p3r?n ztZUKV}LxTxLGWTH!@LZ?rE>bpfb=FEubCkyAh2V zg#UIMtE}9Vw!XS$R^cg48C=B3HPm%)h}AduaP+xbN<`d-h3D1Z0~yf6b~v5QT`9KS z1yg=zaLHQ9_fyW7aHCQpSwI)7%)r?^;c#@WUa!; z-$;hXAHWXMYzuu_L5LVGwVd8{u7q2KY#}3@DiyaX%Azz&PNvts(g@d+cumJynE^RZ zPq=hn+Q{)r^m8`6^5FvGhral~)SJ`# ztsk7}3lu`aJW$jQlperGTAwCAtp56HjiLYGk?%DdP4G10sXfR`HhqJwZ_4!X;JB|gUY-AbEp*y{S+O3ic31O@MFPR zT_A{m6$x#Y-C_30@1EBDrJDQYL9fjxIm!p`y_&-dCVggCm6%3UT{|pi+B%+B;HurB zqX2q<>Gi*jR1g%Y>=bok-7Gh)t<0R2uzSHq<40^^m=iN4R_U8R%}b$w+e7@f=+bDDrXcimoo=jl@|!`t8ZyOI6#ew#Xs zF_;g&`F#7rRk)+rqkh}TaCgc#p?Gcn)jfn{6t`O|&cEgk5c|G_1Ay@PMrHcBwX}OV zEAViK=U<5Lte`l)XrC$Nz1NUK(oI;#1m~GLd*KaK(yzvzQSD?s#dXzK44tc+6)-X=i_)tuEM$o}v*oc<&+vyxghD&)#U?wo- zIDVQB|DSh8T-7tm*Qk@a1ZYyH#LWN5Yc4f83RK5;iyf+%!Wv%E_Z9LuE711PV4Ni4 zBG0qD9P{eR?L6h%fg$?dT+0GZr{lQ$jy45yYt6_W5!0xHuR`)M=NA*b%DZXkBizgs z7EzNthZe-;c#gbfuOM8J`F#P?aU&6;A}zxwUqvV}M_PYrRAc(N5o3JGnD3DKLle z!27JsKUAlM)#EdgU@9gXzVA$vYy3!^5if&04y7GT(utaWmg0J0o>MXlJ<6>b2x6-5 zuPtMu1(A%&7GC339x1-52*YEDa4;LE;Movq12>)n&>TZWNyNL5OAJFrKaq$!Fuk z@V}PW3hc}Y+wW6sMi`=iKS-maoq5JH9{g&6GbMbA;}H``7_NTXyNcyDurY+^5@3-h3Y3%)-J z-(T0`TQ8%#A;3TBgce+|wUYhCPE^rulxhP=7V-GS^Dj}}gLR`+6v#mQEtSv2w;@B@ zuH4`XITpzf%DnzWCpsTT(HCQ4ji_{C=Yx#NUp~CL80@%{jSg7f z!%{x+<8#32>hA;eQJt6!gW>ycO&`&tmxNOINA&GkkXR6&6n0jEf5X6+RrbshRon#U ze3g6GaDjuY7K(1004Ma}_I_Rj^v5rxRuFE`dEXLWZi6x3yZ3N6`SFhX77B}eM`8ib zz7oLs+f=Ka6GDpm!;<;cRlL~xKJ#4F^;BR_rnyI!%#?Sg&qK%<*rr&1(5^Q2-U1#i zIe}}0Hi(bn5=klPEPxH7%dw#`fMj_}UL64cjv%^jtC(TOT{KO<0Uri)#y`9gxKtNg zdmel?Pu%sF;?^1}VW8U2brvpqgkQfs{a7bBce6zl&W+DGPseANxK6-6Q2zJah6En= z&|ixt4{ZgC9Ywyh10n!*v<&*dOJHF}cb!>XI(;8&)sk8QC6JfghI^d+FeOSm0LlL7 z5=1DPi(G1&HxDM@@Q7{J^0N`!ZhSZ^;WezuFMCUL{H52PneV7=vyi+OTURKKmW%^! zUxBawNk|`ZO(P`0b^1XUyd)d&HJcAbq&nfCGi+M#-L)rW@?85NZY&-sMf-U`Oxo|a z!CX4?0Dlt{*Z@Io01sD}nc4g1f?X)^XZXOTvqsZ)uAtA_#w5%qp%{pux$7# z9CVQ;#iY`!>_QiV1K_~;V&+-UP7rk^^7UxK;e8Mr6<~s$ml&?U415Br)s*%y;|F!? zzy%R|Fzo5g><#DxGtaj&&kUFl+owNx5xB~8jP32w-_$my?M?Ek%3fy(4wQoSmXx}Ix5*NmeH|bCvRRp##qNOi!x`%4bnHvEiJJZ}bt%Jd#M(aedu7z!XXM~g zGGMu`aVqCvmZYCp8K)HdqUQG-+7TG{PqhkK+^e_4LuY{Jd7!bw%ViFJX|rFVF47{B zj9HAE{j#?2W4S-&qQ0MURg4ujKi|{Jkpyirc>nj7SEM?sKA@a7o1T+}QUY0X2+~DJ zC5CK9fGi_lrd04~DCF#l!JdzOTcVZmkt=6AmjC^_^}WOx>p$9dZX_R3=S_DY*8d0i z=U2NMBcB4h1&jP{3%~RG*}Z=Q%Ookq;j*5SSFRmrhgm1y3?rt6ngbLw2-A#Vekk87 zm^}eLYML979qX&wXQjw3I{VjnaqU4N5RAJ3mn$y!rfUi}lw1()c3}`>(Dt`S!}O|~ z&l%Dj3=zH+pi%O*ugEzg9NBjvmr}eHi28D3F6d_6FlT$!L6XYepXJQ?tm{lB02WjN zNtDu(au9@a>3j%55p_g)9h`~qHt&OSZ4JKT>gH1{NsRbn1_u{`@CUZ2@CMAonk4+R zJmpMB*OY&Fwv;Pmjd8`Lyz~ed8x=Zw(x$$E@TGV*&@*{9Yqz!Wa%?IE_WtITi^0J7 z6!Ltr0_-Mi+Zn!$O;M0$|711 z+8jvCVCIHa+YI7|52)A|8(36%^?j*ike@B~wq%9T#HqaL4Q?y5(VLvJgbH3#8O*1{ z?jPvuso-Vo=qbTUF=DLLE*lW=zDY^&Q<&OBycMb5gr| zO@vca^!x#1x7b8v@J3VCVlXg(f7{r2(@(Lbx)ZV5${pGbI2$dZFRsOb;5dwP5s;IE8aEv zRFX)ux@5W>Dn7>(xVGkQiWx<;X2A){oidzdtny2nLe3ojdndA zC#Lvl1NY9IrNfdlR;s!3?rYzK9$!m(aL*PfiPdD6dA~kO${7r8%`2nt4sBeye{g-U zWbiG1LhF=0{F2c6Jx?dTFT^dYSxPXh7+C3~lG^MWgMJQXYu{VYrhIIC2=340L}%A!Y4J{=oA!g6^Lzk91kQbuzxS5#ap1efDmOj_F?@~A%^*dBYFWvI-p zJNV7=^3kg2C|QyA4p+wWQcRxc7{K%@gn};lRy8QYiHaY}!f7GnkN9%Sy0O9b6a7+G zSa0~wR3YCL*H%4wL^zYeD?OT|^C5fBzMy8EZP&kDw9CKI5d7YrxyD58)-gNc=WFFN?zg8xat2(Pg>LaQ9z3pdKam3JieyEn9I!x<*UgQc{V3)osloS0da$YMsIknjv(9njQ2P z(3vE!{hfzNeJ0FYK=J5A^Yxi4&G99}kq4;rpQ9SZ@}K6LeckiF8ww~S2Miw~UdDR; zcP?@IRd{vut2p~$648fp`Uhvc@~;$WO-3-bh@5(ID7JGaj35uCnC0-W?EkMRy z66p-udyubgyZv>pnCl)$OF^x8}WYzZ*3z6+vtiyx#vYEWEd%=W70NAlGY_ z3u?5NTk4y)P&)K?CNPfnenuF;v;loJwsRZdF zA};W3hdvc&Qt^IF9B>akr>l9@h;>ADXmI+>zd*%O{w{LesLG1~zo)}j1(ruPFzp4d zeRjLx12m(W8HC|q;VPWn^6xHsvmCkStD1s3>~h94b*`pw@{|wqd}L~N)!W%9so>E> zhxyX)%QSK-rAhK<$j{dl7=<4-r?!zuaDly2?7-6sO;{+>xttOYoqAV_@#Qg5#zrY= zP9)^*V*+4oU?Hs~euNd4%3>xTXlVP+`L}Ln6GO-#C zuZ*;{j}x<+A}L44<6bm=S0G1t{$Zi7t$z9qa#S>F;*h_mjb#Le$d}KOTdgI}qyz4p-u?Z^}o#T6D@qwO( z=5h0*%7^&2RGAiW-EcLye-z3Pf-XI|WrQJZ8rU^N5-drLZE4$l5?DaxCozHmY-3w8zGQPlB31pQGMQ7 z-nvQEI@991m-`mU!Hg8EsAXAROz{_b?bd!3?i4M@Welp4Glv})SdJ*2q#xG^NThm!6fkA734`sod^l%%n$ zAnE?I<`RumOzD{-i`$Qj(r=urvR2~KujXd6ugy`+w>X(Mf0FvgXB9Xm`+n^|< zvpqs4C>Y%oHE{Yvu(MH!->h^=`+~MJ&!;S-5M1#h7pMHmp6Gl73k8^^Yv=;EwU9a| z(YRIKDkd+qWe`-Z4~{sX*devQ*o|`cdb!eK-8lc^OK`_5KuWn^fZ2Q{ZdNQ4L`DCE z;@KGI7pq8Qb$Uy_3WnWkTQEt$gjR)aQyp1G{L)a#o^M<} zAMl7wNVdki^Luxh)}d5Cxc^jQv!LqB7Mk8^A5phhwHf;i09?!k4?piMPEh@yrKFp5 zEbw4rS1?2I-Iuqw>n?2@Q4!i2T>0+6WqmZu&n0Y6;w7IqZmwJds-z#Vpc#RwaVpB6tGDqMjhzD`(10cKy!dG?5bze9yvZ`939v653yu+_3s z6HFg3f7XFyz0}7f(sE&9sJR?$#&{fxB+nlyO#Q)AtUGE+ZoOWrWC32zw-tQH@vwaf zVYy{JZkk%3{P@oCpwMhhf)kY3N^zCg`R;R)&KKXu7W|-{vu_sZEGQ;3tysj~=EUu~ zZ#~I|@zb0uiyi!2Mfvc$2%~`eJBH0tZ2#0}!`a9y_Ib%o;i?sOkZEWl6I|89FV?w8 zO<~Y5jS~JISBZNV+yKe*0UGutd@2pWEHX)K{uSwYIdg+?BH=wR&_Iwda8 zEAld0?8O{t@u;HrkYGsK6YV|8hCdKq_>B*z)Rfsxb;!)~ea!f879k&D_;XSmd%39f zm^rr9JL@X(gFedS3REB`d~dfL9P|o$ngos-2jjQsMEZjYuGco?!YI9pn8sZ-XA4kq z!ZPKg4+C8`@Xxfsn2}U{<(;WK3O2KY0jZg&?J0fyt&TF8YYOaIcLp$P^Pb{XnJevu zhZTMw?Fj~Os!ywOv5?#=FRD2Tx}h-B#YI?WzP8TU%)PQ*EyL|O2mMR#QWN)CbiK`; zVnVfVxYZKAwPb+TN+I3~KY`;y{6)S(7~@+v>IuC8_*o^ho;&m0N3T8?Ui{6iK0fJV z$|up6oL+$c4Xn_L8Th~*RdBu7#~XJYw(+CXK+1rLLqm5HBVzjDxtDQKfuczS+F5V3 z|1AtTnQtD`6T(Ox>;6?A&MAiU7_eFeCP{89Z~mbEmMpoID+(1r#vX3G*w!w%4^vco zIDMBDSBady+pUqO|=g-SA8A)i{2rqgUZ!Ww(G%n3C@8jcBT zhAvB z?MrDJBPpe>T<$P7Ojy!b{IU-fEe}{b0~OVU5|hN>Ete$%Gaw79i)>wEH{}*+YATHJ zNa6(FbVvKqeC^e&g^XsC1R)A+7_Zg>R6KOfn4kU)XI$XFoN8;%3T3J$A{oo-M{N1( zUJuP`n?LLUypbQSJ|8>=Q#?8t@J~_=>hHdq3g%hBt8ZSk-)*cl?-~V9GYBc1sk~Qd z)HQ7Hb6b!q`c5aPQq?S?$Ak=30?RB@TM3^R``E@Wixt7tw>cf2&#CNox#nFLCV_0G zq3M|Kbo}QCPje$~nBUod;B*cuCm-r;7;YZ+pK5dp%DYSR`x(RQ0x;{~=T8#^c=@A1 zm-zKlEu0$aLn7X~6`U03ivs<7v>RS1GJ)a|K- z?T+Rmzft1{aI$d?sYKUo5_|up><6F3cpaX0ZjcPcD8ljBZ4tg196e`-t(&vWJmw5N zf&UgGsgm`!!LOy`_wW~hVE%9EZ!K=e>Kv$rY^zNc2+5hX?r1(b?&U|^n%z+(Zb^Z< zW1Hxl?Qam|qX2or=_pt_?Lwqhj03-XB_E@m#qK!5M(ZV;?XzkCuhSy8F$ z?fSQ-<84acTq$ib7h2ywtMVxI{VW+x5Zza4eTrbYW|~Lb2FFV z!~Xkg7Le&!EXA?o7NxmZn1+|#5bnv9Hc1e*$^^a5&fNwd58RflPnp-1Cit#h zQS%E!X0rEr0YaMD+$;j)MK!%+iB0dGC`%bmW2hymzFk-0RbB~u#RvDY{>0LCmts*X zGXaUKSCHX4AzrUED<>2cePw8!yJ<$k7}xTnf$$zX!oZn8OVA^V>fUC6t(#u~H8OWw z6Yx*?6Y$AXl=l4R^WKNMRu(_xh6H#ndh2U2)?9Jw)*V^r2?(A5m#8y@FyJen^NWie z9$mTl(22xopQ}3VG(=RaFjbiUo%w-Gnpp9*j?g zikcghn4fiExa;$9x2dd(g;hNZ-&~i|K_CRxMOzKeROlF9b~Dw)R-Q{ODG1uKdt+4Y zajA=wZ=SVsJ7R5`JXU$~gI)S+82RLBo@hca{G)%{Avm3rY>Ja8PZRd=CHW;UB$ROqzov-@wT>gaFUOtlr^q+HaMoQgE!Bkj7Rb72~{Fxg5 z_+RqDtZs8S98Hmb@|E%<$;t*DmNc(%H4*dl_Rg@yd9gW*D;jcM#t3a$FqeNZ&qwj` zL!AKfd6Q}2TQ+rLpN z2YGDQi+okHn~iO~{Fi;=&^yb2fKtqvZ4z`?VJs^URV=PgK#yZV;&Y&NC#FLB@5#o?E2X|@`*e{#&IZCL znkZ&zual|Pl|fS%3$ZA9Ea1mAWF8`Kf?robLvYJS+O1-&fvUV&tBWNe@=G`>Qmos8 z9rkqAj+ve}FK}HVW%Co|sXl0Jy4NvDa zsKaqlb3ZLMK(n5%JD}?j5YJH%I_}`0EXQ@;ZoGL#?1a57#y0@F*OXcg_K5YGONjiG-&2$ z_$#%ByG&uV(Dfk&FW=rRXea2*Mz6*x(=Y$!$-2b7Rz#N{ZM}LGbnzU4lzkVB}w_QHxQ*zJ24P&qyDzBx8XRSFEvZWiXjrVFv+_kuZwPBBI3FoNc z5n+;$5z1^v*y+V>8s>vP3tNgK|IbYHgIXoM@{k?xd@JyQ!2 zzpuF0Q35EPdtY7Pc^`LR4}oLWoiBrjVFPwUUwnF8v_1vz@*edfpA0_P)hF$hwCjq#29>8>{c}_5OW+|LpIb?RoBVpZmV9>tPqSxceoz zam5hO2dZ3mF9whaD1;pV0Ma8Yvco42Rk(o|r4;|d=vY;+XZz_&gyb0R+ZatZH?I9S#{EFJzqF+WC5 zL273Hee275!q-Q4K_9@CHH%UNrUE;7>98HVm^A3n$IBUI|ASPzij*NoE#&`0Z3&ZY zC%za^v~y^gcz+{0x+gU1hUSj=+P7=)XAIANb1}Ea$WiwSWkow9vLf7>hf*f8rzl?- zVE2N|`?xb<5L{cEzZwb*JD6vI7=C`btitHm4|(k`#=13XqDKyg;4tN9UV|>}f_vpt z$mdNre*yr;=@Gpe$RxROI*;wgKmNyutC-EZU21#XPyo(GD*y1D8(tVUacGExdk|;X z4fob@eAOCTY&z$_P{1%<`C~(~*=p9MexX3O^%A&e<;CxwIe&8SkljT?{lbE1GAVUIgT@H#K9_hXWAh@TI?LL8WnO8USm#XG{hMHgr_xqp1 z>O;-)ms-b7vZW3$+jmO&epqHK(UUyz0PUu$N}YMAzL7j=CpbnxJ@sP}R;H*I<>>5b zgWuu>0WZ@lF#%2B5=b+WFF`*?_337jcJR3uB{8CZre4ln7yU^pKatwjWuN^Ah^vc+ zY~utQQ$-6ZFZ&Q3OlkYMsGM(~R zM&{X|mt~VG5@8YIYI-&kaHS}PF*2BJ%@2nMzG(guiui`{v0(?a;?2_YZm09Mz43)wq5iAg?bJaTTV$OI&Qcj^U<)jw4W7tmW{#Oev^o$7{FHdtaUk(-qGzsaa>_-}>Lz&BIUUBiU;~IFtZ{q-mtBJ9j^Mbj@+@=R;v=#j{`M4OZT$ zde-xaxJCZl->-hUHRS+MUL5)ta~WN`Wmo0S_n5lmEeC3RzchSxl8_~MYvnb5Fp(VW z)Lx)IJRqy{@fV%-JUUbM9+so=JJ79|GH#SmK{DNUc|i36ZA@Xn-;cC&D}^^q{?KHo z1TOWzLp&O!4Br|OLQHh;=l=(erR1FahnITGFjp`|min;iNQ{vod;3L0Y8_`&r^=wo zl4Y}n3YD@%NHT7T1SM)yj^aPeHw@OZo%G5_2=v4@@C}gKGi1GdYuvY-+iTEj*Sli0 zRX~1`;vbLtFixCz@)%*cBanIbh&QWU47Nx4QCH%2UhL^Tc6m?z1gi48-te!+^K_AS zL6Y;a&o1wJ%NDHkym@Nd#`o&yWwsgG{0KQ$CWtDX!jppyg{$@GAScrmJnwKE(>_VW z7esuQkmep2PdLF{S>%TCda)pj46$k{ry`UeaEUe4R-@J=|7i`t$pOrLuLz2Cy?aW@ zEoYz_o_*e0)Z)K~AbL5eTO25ltH5R39Rs((Q4c5FaeQY4OHmAENJJJhv|H3K1wP9m z**ni;xh*t|Hd9xfxU`t=-zg=sJ-?xG)5~$DaxhZu#zjVzw^rl7Ln798gWpA6p!%rA z#dWcS&tbm#o&zd(#+B+IQv$9zu-Uu>UC5H;;Yr61M|-TY<}L{) zj>jD_BEcdX&>;XsR0Z~16L1AT^ap7RuUOq8lJ3E$8Z*3Hbq`BJ=9A!)5C_{uAj=rI z#rt>l33_|*Jqmyq?FMBE6|F)dpK?=PTy}kp!h<}B_!AJ^5mo=tVZJhCdH6eNaRn~f zzu3E1-eVVR7`9qk(O_m2g8S9B_63oNMcJ3*=XbB!c~!G%>NjT?SJf(~U_}$3r`Ucn z9LN7%a0!n|9?jr8|NK*Y*xgJ5fiX)oPdy6rZGT>c-_vR1}7H zmaju^#%}wSQfOw#6DNlM08ab%(yxH;8`Evs@O|%iQ`bV6t&w(y+IO)VXMqi->G0mSI*X7t?CzHIt$XUM1z)vN zH*lEu-L_)a2f92RQd!3_eODTQRVnx1g6mH$|E7vO@RYurCoPavtijZp;T+Zef^z786GK@sA_>b94NKav#5Nby9mY(=nOF^;)h>ToF&ZkNnMsN@f!h)fz>< zU3QIK1Cko_4s(db$H0yB-w1J*WRznBBnN_Np5i z`kNyCjWq1Jr&epxeIk)tEZ8_Pha4+NI*VbN4;gF3s+fEX@t-e1}IcYV7*xi_78Ih#HSI<|cZ;j!7`wa9#G097vz ztz%!p-FHB?wx4z|>si%!Fx!lT|I8J;zxg=KeW7c*CC2%J3$-TAIsfWTS)`{kof&|D zU06a(NZWc~fAif6-Ef~f?NVv;U(ksB6;igui~MAK$X))^z#qfxGp!8rUs<7TscH9m z4aZ4CpkyY5NTgC3*XgK37`^T4)(cY=`duHD1>^BYVhsZ<+?bt;z;&-DXUB^ax_h@y zjX+ixyJJ_s_kw$#w?2prN81@60}9ItUF@<2ybk$q8X)Ld@qfKK==kz~U7SjHWZn*L z^$Q35;kW~QTD(EPD2DRj&5INZLki#TBU*j;)GI7ZK3jL&w67})yVp+s9B0W5zc)sS z_qq#h@DDk^x^^Sbz8tBjRp$v|)7DK)h!f7}{q9z#{^iN=kR&6x=zH6ieP?L&>u#5} zbEq3s5M0OjCAWvQ-81(}E0pRW2N|cLc!0p?^)CEh7QkBL=ymqr2~+FS?60pq@m0ay5W80LOHlx*4rhi@gU85; z`HNQYb1wn*GvtGs0{{^JSI>aHYoGn5MFENuyzwh`bXA8sMr94xgre}ylhN-D9Uq|$ zD4{Z%5ivg(P8MCtb}FW%QRl{2Iw{$jHu@Coju!|jJn%QDpZ%&O z>-Cpv^07T2yNtH6U+F2`bjHcbuCwm64!owe%n#L|yiiy=FFcOV@J0R7`$QXi^$Hjl zENyia2eUS_y$BI(zg0-R*eVa}=2?!2i}u8z8IcftTMU<&n{e1Y;-TB{r7o48a&>Gr zXkVK5_G!AWgvq@3;_UKmwQbj-jdLc%DeD{O%Z_-KK=gQr4x;z>2L{-i6r}c-a3OQk;;z%bZhSQG-xCD1+VnZ3=%fI(h@x;gQvfV8p_~$Bxs21mJtd9? zJM_r;x}JNh^k#=0z7sC?fCO!)xL)zL&FB0q8C4--qV`xcQQGv-srk?s)#zbzQ{kOb zrZ9`TYPGsFWbU!}+`)di8Gh+Jw_!bet?<2QrAqLH+kN}+Tmfs+9ooZ?I9z+s?0wi@ z^*>r9+fljO=aSwxpVI3qFz2NCGwCEjV0TK7#cs9or@F?`>BUc?EG}X<0 z9%aB68f*tPAI4{S;Y~IJ#vpbxuV8B{q?oMsyD?>oA-5HKYmW;Gbtwj|4P%6R%@w=H z)ry>w>CV5yw$iJHUB@0=9P+!5yv}qD3C!IZM6465lL~f6c6b$=K7TfKSW_vV2)h`F z=eWL9v-%KH`{c4>>CTZVZahpPw;P+Igp-eQ__a7sl|J;NFKdvL)tj+0JjTJNFyClF8eO zi(Ny^z}EN{FzRpmwR;?~i&|vOG)W);3JI1c_jlKflRRCh!Fd)jvLa>yldpil%jieh z;~FAAV?m_JigahR(9L9ahTdv zg@A#)J}yuGP!1lySSM^dmY}v($f2TE+YrCUwf^nwTEjhL!q#5hVrHf<#ugsGS-)zf zocra=4%b*pzDCA7(trXiiKkJ6o?o2l?!qhvjQjah!HmLNok7mJtu2T1URw?wuNofG zPOEb?F!?eDghj;fto8B@tMd)ZDo-?!zjh{_AuYOxu%%9LL3zrY+uwrMJ8R*BB6Z^} zdemovaMJYQca0Lou3sWvfe#!6hN&o(r{+mk!1b12%1?*Is{2QB;R{^9Q}%` zb>+TIMIWQY+m37bMGdspzcyZM28%6^yg9`)w*x#%`*Q*XoP5tXe)VkF(B5D#l6SKU zO<8*LjGjjS!!L`c?yIM=^+_r~yp9nem=pMk0vZncO4}ZdKrx z->9y(A?Ix674WF)S@Ed+j2^iq^);V6O|rx!QS0$eyu|66+jueEMu9aCnI?={Gr{4j z>1o-^Hn{dW6bTDBTY-`-0d}=;inBqFfF-6c{@xat#j8J5Apy`z(1)hbHquMQlH>4X z#6kg*Mz;>~zTDODchmlvTI0?TqNfOQKLDo)(z57|5}zey!PlT?XT@7P@FNcgv3ec< zdtq2b=@R1>nNsr>frqQDHp?sSD(Nj|vVH|0zio{V=G{4u#T zW1V@d7?tMKJX6*9zIq94P7FV0y3fQa6x@&6_L9MfzVCUNh^HCnFUS-)R^AUMUG7tT zVaZWQ6xODLtvwRZ3gZRut--NuG5H|!W@x3g59G>b;kOs{c9TD<&ju%bKOQLgo>H;p z+m|;yZ*!i2Mv#1fMC5Tg*ZK$^YaS2xRB4Mwo|8y1j~E_mVe@8H+~7r#Ecnlknx66- zRFsk$^Ra)a1WjeNPgNurJ;rN&ADN!`hs4WdCA8&PPj?znEx}A_uLKk{?oS*w%zs4Jr z5?F93U&`RYg_pd<_`D5Cn|idb3~(~kLT(W2B^e$ELNUO=`D5l*)?p32M5xTzIyb$Q zsa5Ydf+=Wzi48xyloR0?5*lo`@bYI+8IN0)x_%bp+N3-Y2 zMvvTeDS$cy+_&_I@t@1gQuYhduEC#k`FL=xTi=BQrtjP0 ztHw4*bH>8~e6_giiuOVJpkLr4r_K82{y?k@Nr4gT|@sR^1K2>HG<@SWO~xH4lPlH4v2 zRUh=qwZ1zmDz8z0cv0%O%kbtln{SHOik#s~Tu#h1)K9aj7pR7$6YQW9j5lD{0;J8C zako)5&!3wM4}t;Io>f`+ZgZ@efl|6p7e{6Qe5bT-%w8Qj4l2H{d)8p}+fWFa>DY37 z{qP)slH&b(Tuy}J+Cgdsnfal#>#^w{Zc3{#={K(bkwV=#Q!%IdP8ZFe@xq;myb|TT}7ZM zjFps-$fv1&A-ZGAkx2hiX9{?|02)<%BiDJ-PUKG9xa*%i}viD7o7)(cfEi1XQ7 zx39iY7YX7#bT|9F=>FUSuBKiOP<&+p%dNsD_L=~Ve#MCgNw|K_hJkjzon z-}%I*&S4*yu98dQ5^R6hgv7;u4os%-jepwVijN*_N=d%pBr|vaut-1qZTs_(0qN~; z4c57QAR>U7de9fpQT;|j{jD?Eb5wI-eQ`Ioic~!Qm>t zm|ngX;>o_>zGfY4<@#@~bTR^d7=oRn1Al4mmMd@C|LlG`4>;?ZBwHvXkm|54b-dGn zI421(cuNKB*TH9xZGnVCtxSIme+#nIOI*K|&CK1U5z}{?cY7>AdPw$^h@CdpZB`UV z{{#E^QfTz8#%`l>6;@3frfjzkn|+RSlX*OEq?qi$n=qkt>uA*454GiQRqz*t+1azx zH83JF^zw=?po~|mEp8SvO?-BnKLbeW*eZtN?NGH2CeJ8nK1dnp_AT6;R;Jv&0OC|k z^82BO2nL!3+x9;YB5reQ}#3KTxwf7AQR!c3dA?zH3@mP?Ihb)QHq6Tksmj@w`Y|Ox>RO$)@F>as z<8Ct$LAjUY^DFJTVi&0YTY&JLbSg5y%U1o~6`2?Jkaqc_?5>)+-0hBaZz@FSD=gbv{ye=sf1|NPG}tDVxhc5%L`n6gTmz;(&XgRo_hucw zTcHwlp6CZ|5{ep$1T-tF6BSR{Jl5Bf&9T?8foo2k@IyRXXPQjHSb@TEk^5ANb*@XQ zn#zxy7zKg(vtE;R+CdLuIE&!8z~IWASAq(zPmmt71}#9*Ti6Sb6dunH{184>Ml7-r z;{Qy;Sz{K`Uh0)tT5d}Drh^doiq6Q_L@5X0D=nk{nw`{DcfjUltVj%kp-uh8=N60! zNs@ZA&kI+2EE6DU?xo%qM$e}3sg~wv+r_p=Ny6D-7^#dDe$6f^rjRVX1Eaqr?D%kQ zvbnDsTC~4nr+}n$q7BK>?%<#Q_AqDs6}m~6>3ZWE(*jmMMh>-1L)H79&E&^{Td6*<8vAXaFHHtp=s5zPWFNg&2}KJT*0E~}$g`NLH=pEaum47Z#XmTqQ+UAVv5LSdT6+?JkG^lF0; zN~+c(;q|vEIWbCR_hUGxoUWp4Y=-7l_ux)a8*AH7g3m)JifRcX4HGUo6 zXP6yOJRPzby75+K0OZ?=-wM1)yK!f+hj8D?(#Wq@vqQ=y{f_Um0%5emP8Ttd&+S{C z>)NeuHvh`&{Tt%1y7+|g+fn7YMnlxFV&Xl>Cd~yw*c<JL)nHTsoU+urTr6ukaIJ$?!$WAB-LJlZ$;v8$n2;@IQ88v)ByY`F*p-1mNXh*h9VI^iqw6(wR_BR=Qy zDqlXoasD8KOf+%pb`%!lc=)-@l@#yPAr zQylBbzI*TG2abo&{n$abF3p>Z42H)rTGh`cP@G!|xR14m^0_on-4~hUV)v00a`r3A zg4?kCR%X+qogdyjFHB*bEQ~5c1qh$}^Q5nco;VX;e|Oenr!SJGvg(Y0DP-|#|Jdvj zfE`w@A}p#OA<~>$bPcAWwn6Hs@|F`B>EWuJyQU?hoV)d`B{k7_Drm|!?Xd5IC(qZOSs zkirLV88q=|4Dk2H80tKJK_VUzKxN{Xo|Z0pFf%A zBippMjLrt_X?Q>HM9?=nb@EZT_-t9p?0a+v+r{O1Qc1`x%_H;aolD|lAkra-U$Rf> zncWO=3R^%~N3J#JWAPANvuvLyrf!O2iTa2xlSNB_J>`+>Nhay#pR{?-wju7?N^2E8 z!x_m@w9}5Cz@(NbVX8 zA|wB8$_Hz8gS5L4?suJ#hoM!Pe>q;^rEe_r_}KhQUKy0on1)2+*$m%Czb#g?&v zgeu$0hn2c4!TwZ6hne^2|K>|n1+Ires)@j~w|-@1NlWE*orLdPhlEep?gC?-s-%Ny z8c)+{boI%gR7V}JX1W+}h9B_CQTT8*gX`GSHh;fRny=thx$vmaf?a5`p4Sw-N8THs zBEQhABOZ7ym_ye}rVZ>LPPXpF`vIK261Y{4_Q?JpQm?jBE?V^~pXP*>^9~V){f9=z zZjG-!ZilXim%T!0r~AD-GelC#KOu%naZV2gDMSQREA5fu6>Wm^{)06Y0r@nW76EIb z8-kFIz24uw9B`=2_Ouk@%E4hL47W0c!nQ1zy>dr=5irz5QGR4!busa$t4SZXlqtfG zEGwT*1RP&#JVmYw<-vs0T4e>C zd+Yy{y}X;VttD@oo{P~DuejO?Sr1eAH08o0sGYxGSl4BJi3?3=U>sk&b%e9n5US` zKvHLljw^B4L%idaFH&s3^jK&ISx#gDQCXT9k~Yp1ql%9Idt>_mQKuO0mG89&-UN18 z;M2)0%QS>Bg>e_xeO*C#6`k$=Tzmflfs-;rX;*lbZ2RhUM&mw)zIugrW2yDw(jd1z zO$^5UpcVaY@0|kr{oXsqM4At0ma)m>KGinH-kq^i9J!v(v6p2pnua?8zwCM3`sn>3 zo~`u0>A1|;`aV--v{OMGkdI;>I&=Kn>*q=uj`ceW^lkFr zTIRKs9ubLQUI>^}8LzhZ``~dm%eCIK2tTK~cC)b(pQB@JgrB2!@;r`HS69bSMP~K) zPKIx%J>3|u7fk->iY>=P#6&mHvm6b2FS48+nxABFU`HuR0p)4_dZDUSvUoc3?u?sU z1cRX@r4c)(zIMZrmPM5eNWU#mExt$hg(nU0h@4?RGg#+(>M>V}dBENz_WGbgYnkQz zLYS#~8D7ai`bz7OP-!d>+cccik{?ymdNhyD*G#K9s&n`3B#6{4j4S}#_4J`@pri^4 zX_3@YyahB*av44+9QoXjWELu{PY-61W?cK3?rlwrdxKriw=!_R&?YYw$Gsu`DFs zU_`Ybr1()pv|a#!E6Lvyx55zHX=Hzf{#2{N>sem8F-C zW%nY?Spd_{LORVY>K3c?K+<>wUJJv?@|He__;%{sqdEMRsi~|TYuC6p89eBZzK*v7TcT9;74Cvt&d?Z zx;K_>L_Xv6g&QG$UPF%hf(h5_<^@7snj5qc3W`kqS}p-^x6Ymi$dX1?;F)C&C&uGD zH&mu5K%Ue$@-hfm8F;IUx6HdX7cudEW>QB_UJO4PPW~VxD)M01$DJiCl#jB9Buxt$ zgk}&Kj0Je217gf?vzOo7pU$<2p8WilgULLB;&NssdwF;QLv|aQY-cx*Jjh8oOCNI^+hhC z$b1?JTAf~NU*fu~^Lmu_TA|w@M)7ZJ;ukM~LN9C-NAma)U8WlVRCpd|^&xbX{XGdi zP3C?=a4Xz{b{3ek5Qb~^jZugl!0q-ZFK`wyn}>Zog0uL3qJ4%U|8Ff12qYiY{h;}y zgI6T3Q};#NGwkF0*y zNJYmMcJ9tEmOCMetD*_z2j0JP-#XXH-8$0yX5{ucVyxbf&fVpX(sEnZ?w_46r(uCh zpemKz8=mgXoSl|fO&oBI!eP0 zzK~6(9s1xolEd!|9&M-n6env4n^>_nQqE{3bYpYkWwxj7Q9V)q8l#L$1FcQ+Vbs3A ztu+uSB28AH*4eJsPOs;l zc7o3q3$@b@=Utxk3anG@q=-kqu*fr1*_^8*vG`jhruo{h2!=sMJH2LSs4~4$)Cp+- z0WI|rVYcxjq;s@|P5CRfDG(gr+>*^%#Ldj}-1 zQ9MIIV|1FhbDtF+Ai|-?NNYe$3e3*JNu?*D(Qi&qkSOSBb`7P7gr0pzWL`zf)gC>L zVgAY(BgOHgv*$9wunWN&`9S3;PSk7@LQ`~{;aD(kbP?=`-8ePzU{C4*%8DrajOCBgz2CjjG3#t+(S*;>QBd*oEJ?|-jKXw>BO>5)Ixi@vNc5`p< z4)QBAhf;g>+c^fH1ubhh6F|QN{LPE|)d}Z1>mA#D1Cn;p@|>_$O{wGG2yuH)U;pFP zg@K*&R;;1zNr};!|75c}@vz%o4MRWr=~-rP1S6e#?PXe`MYPQQe+1bf5wvWT9BgzY zWQ(Omc7WOuY^#Q)fv;N~elH87!lW5*2*D5j&gFPbOxt_>rXB~mVKqpSr?+f6t&{CB zPm|2yq(`#zYvEC?j{&}jMN9?b*%Y!YtDQ(Dj2R{i?CoUAi|oxeGL4Qs?B=>Rvyf&v z`)$Egc!pKvMTEIg2~ruwK-$H&rNNciZ|jV{XhR-kp9ZInB$AJM+g=qL*hx#m`>O~1 z8;-G~fC}9K#ad)?SZAyKF)u!JaH7`cUVBn0D#KLvvtF5~yY%<=PYCf}?b1BoRegR5 zQ2L-#NU6>$CHX10rW3?!$PAc!Z!Sl=KR-XMNH@#4w?1yfMP9;x{a&5-67&*GaM1;c5DrR@|H#`!5v|HL8 z)DuRTBx==JSr*;U($YAIQu|x2%X)C6CDVp{_)Px`P3zt3iDVj)CZVFz@R3oFS)R#M$c&iozZjzHZV|4mS9YL{ke(tT}+LdHp%;N^+8T3f_^ zr%~BCl&YqBU+*}MUwpi&IxdyJqeIC3XkZwAXM>bj?Rk7<`YZ7!MEPpyy3@*O0p+)g zK)*v`s%dlHX;_qnRm(9Fw%0gOC4O@?p}EQ1D4)*T$-gpQ(HOg!$6cchIl^Mygp*;f zhSnIQ=3ugalES-lv z-DAke_!((m!jY@0Q3jPj(Qje%@2oyuZ=_`q|0!4JY z_JR6+)5WKeXB2Aj$+;r3=46*Mml4Vw_hHBYwOuKd^!1TmYXyCQNxxzBKc336$e9-* zqRXbyRxf}1$U>dOu>Hz-%q+>aN$&x!$Fq|~)R9XNOLkSPEyz^mQ>_I&*f}%g^x0?$ z%f2=Dy-7pajP)$xPvtnvmBT$Jn7Vqep=}q5s-=LW>O1VW09Y@>jf!a47N`9V)ssX< zo93uwsy}ANhP7i%977&tD3<=wLT`%%y=~0aMo~6(a#-@ODj$l}9)laRDF*o28c4gP zzj%e}lAheVAC!X?n`tsY6QijXf>nIwDa|`J?|lNp4xbFWZtjcLK$?1-j1(!XFeyyv zb}SoJClpM8uE(HW_9GvXUnYjLMrF!*@4Lzlz-oJ?~LKfO@qv&lu559lMF6{r{2Klis zc5n5oKbwW|wQB=U9yT_OoqsNQ!{BBehcmQ3j(Kb|Q-eQK;{I3h6|<@U5Kc2xc1-F( zc|3eL-0|_AaI1^+=LD(fNLGBGw0rcq19?dNJ4pW`sP)*=#`>4f(%08J1|r((=Dxa3 z;!gJrXr!kS`G6{Ma?{ zTW;9oNRQ(hLc+H4pi_~8hq)H&HK6s#{^&o6`tJT?>W~=@Od>GUzja|Gupg9aA#r`c z4>fJ>)pGc@(fjaSfiP~s7q%^>kf?B2p`ELzE!(N?U!?cFY_VQp`BuOoxV3Pw%)m-X znf?MC6=}QmH_fSbIFtzMHrDX#ML*Aaz0EM^MndmS+&BGINZy+WqKy`E2(~6>2%vQ- zq>KYoN@H~xn@=$4aEbE=6IqMTvn2VHjW|;F8mmW{xlOjRs_RYsf`CV#c6+?{-S5*JP}lxcdSO`(vR#E-VZqgh&) zXoAXxw!bSVMOAwCcy)UDWo77-(8{FpfA9QH|NMU?5m-DdCap4cY4NCb(`H4&<=@#v zQd}2{bb^DFwgnyCZxCoe@B&$Ct~(EFXyw@Tex!edQqMH|c@I3K_n-H_8Fb^bOU15k z;uTTxPhCCKG1FDtYODh>ZSrW+^QSh;KJPnl88H1=@$aDfE%itA$w$`v(!-j>Ut#X0 zxVUl!+#UPQ;Tgw1=#5)41pcNawv0!IQM@wIW(TEAn51>UoXZ+Po?Q|zwEjMDTLgcT zIOL9zPTo2)Xn*=p`DKbe`c>6iHh$MuF{TTj897t_X1Lj=QZ}_C=kJsh^55c%s?Jhf zy}9VJcQ#Eq_WmF6y^L-Q)6E>ZIrIegdS~gXJjin#W5d z451!1JeM-w5J#w+?|`k-U3}jq+w4!T;C7&bu%wK2qI%`BR|%@kd7-oBamzYnA7`Yi+=I_=1$=_%2O*@h zA1N$xpNT5AY7IGgn8Z|IE6rq9J%waBBm0cbXat<}7j;_Xd+wSyVr!}`tm_sYL32)j z`-FU?2Ia<-Yn1-uOlQ}|%``h`b}`*Mh4&55#R%nu3Ua;`6lM>Q}~Bap;G>`sx= zWqTA#ZnMs#LvrQY#KEU_XX2h%4sI~b%sE6_AtT6caU6nf(~4g=j<3E66B~)^8P`|% zn!h8Iu^!55^d!=E$m2RZ;)J1|WU{M@<{~80N{;XjikyTJQ{f|4&#xRM9^86>i@#y2 zeBuCqd722-*DBUKC$u9pRi!Q$`rHrpiwM8Tkd!`AipVOj9hg7=?2}|#B-T}&zlm`{q{5X@77Ui ze{YGY6j}UzlhmZQXyVEAVLvzQv748qsFJ?;$YVjjTRRVoqM&2(M(@6CEJ$f_icc|H z#Bho+s&brvawXvBR15EK=|v`-v|yV;KNE5P!0al`RY->tY|Fs@*C?1q{m6PSLfb@H zb*R{a^M$|!<1zpBVHu+)on)QldJLG6c{h*vF94ow4qtuLa*-JS6TfkDS&$2Zz(nmw zKC;A2r03~^T{S>N9bOe*Tilfv5b@VVreN`}{lIFWKe<%&riaDuw~D5vx+%y4!s=_~ zKWWF}U-E~k5YJxoJE4NBRTd{eIMXC{vfTfk{15Hunqv4;xlxWb;`Ud8(U$>QKO9IZ z=zwiXVO&nlcvDh{pktV`*Jj$RNQeRkY+kL3=M7MivAfS$!r0s6RhKhYos!-v_QZej z1bBC$h8J^2rlfjA`!1}`|3aQ6maDav=R#F$Y1!z!>y`*rCJiKp@r#+aX#E|pz~Yd= z>Tn19BJg_n@Lh<_`8>s@QFW0{CT`Ta*`!JgPpK?72SPg^4?GSvIdG&IP*U;{Uczo! zGGK$~8DKt`X8O4FMtsQq7*mXhwBH3e;?gj&K}-@&$G$GGgYes^&?E(2#!wDI7f<k*-}1l$Xz{z$Rx&0}w>7alhX`FUNjGO4WZ`-`0mJxC)G(Lkd&lMo zZN|gDVSMR@)r}|KI)cvmRuTHZLs`zrZSrR-Q zUQX3V6qo1>K2b@(?Cxu^T=h3F2_NCLn)88vb430trCMBz?&JcgqBQ_eXPEROPJN_o zPU7V>5kI`fNLo^AcP-M+LW!G*6kRZj!3=oRs~lNm)KQ8miSj15#cFo$<76F5y0QpH zRn~+H#Jsb=SooPXg{ZDn0Yke*Te#eI)& z2CS55lXj+rj)3$|J|J%)df0#Td0d>E9;}Mx75WTbEorUzoNF8nr}$~TI_RZ>GI`^c z5n+6Yk*}*I8DnOARL<2_jaj$L{Useb(;k|bAK;U4QpjWBmj0G@f1}UP18v<|o_x#U zH3Bf=i8I*Y9J@D71)AA!p}_i?$T}*6KO?CiKQDq5_D~RnSJNt*JcQeHDN_ji>^MTX z3XVY^Xvn9bYgUeaE?G0FIUe52NY1URhJU-^FTP7d(RE;Cvy2pT5E zQR~Us%dJc>DYSvC*#p}*{_Dj|u!4`xVc)##`p{hHPuhRKEPrXP?$hS|qvwyg{%$XP zcu86^jV|%_+gmC;ncfEkl>dwVppEoYRt1Wx?^YPi_#vQ>(vpX!6{kpS0;fd_kcQ~X z9PX-*@A_;MOq=`Mm&cDVbzE_NqW`Q-w^#M66HdqI(J_;6biaEk2DwA@0*2JSq7HLE z`6~v_{?RTZHyU*prFb$k13X)k92X1LR=}Cb_TwT<&o0d&h@MJHv;3jBziE!FNT0=$AN7@LUiQ^-Z58UTT0%0lOM0>xD=Zj z$3oBzg*w{VpR(Y>QOS%T{f4IfFY-*mwRTqc(i;0}Q-ZSm!P~|2eb4&|+WHOe*9TH$ zyKy%E1^3CIo83satQsUc{cv$AVfFt-ZHg$F-vK3&(%he3xX$CxEbv)4 zsPxTZoYipZ*FX1C<9LEBzdani82X6|D*XNl~Dl-LAwrl$c~tun4n^z#Y<_jOM7fui*9rVYC+(uuP~##176 z?q1w$qwlml1`=&g58D}cI3Kr593y4%p+8#v0c^E8oU{%u;7-l+Bjee2-B}ovq)&3` z76RVNtgq>Pf|m=$@;^DArh(UTRS(1d){WJ*k~EPBd%W_DF~u9_I{9ikH$tR2b}cGL zPyB~rGdf_4W^r2a3aR}Nww>(S>J}oFttB+AfNnl|gZi~Wn4Ue>JOG3RGJ7Lv(&@bQ zLM}i1gjrG*e0LR;>{e_x_sZ&Cv_^+Vg7FRg0~Ly1wLK1?$fdRt?`?hPKEbX;fb)~F z$q{B@iCX8_S~q@UGwIew(oDV*j-^cbR04u{lm_aKh`)lEs9?4U@s_z(Xs4zhsEh}359FpPBa3x~6KYR+A_jg*>T5-x0uZqg26?ZF% z!noi{ccR6a>7VK?Jw-`o1f|R?Z=+h8v%k>?N%ULvD`itaJT(CXKuMql2{UWP+>&Rg z_I(tE#<5wgYD<30sJHh6@l!HeZU|U-^A$PH=RG$<0kh0hP^6c?XH266e)Oom)voEnO-U` z$SJ>UU4851VfPsNEgRK-} zFW|Edo!4s2KM`5hcsA85g5bTioXra@6J!WV zwTUd%OtzGnNq=+Rg@e`8 zroHI8<}=j5LeS6eFTdYWeIda7Kt!OaiVvOY&W%ZJiPV*1{l(>RhZ#N~77+%{P*~kb zjhg)6$ii`fCg%GW?9y!9Fz)Sl<-I4j+?f~uUiKfw+q9vlez9T9F|y#G_E463gG$*i zto~w^oWwhDS^w99f?2%%n^&JH!htUQQY&oiY_fk$fMJAh@bQqZ*>RVkEh#lI?yf5L zzMcr&y)Q4hawoYmn@c(}#`6aIvNZM0xXpq&R2!DDIyiJ+ zGu=jLqeYprk@5d%I_s#Y+VAbF2nYy>fHa682uMixpmdjX3kX9 zuH2yE%%LyRr*A;FT|e*@>%DISR%j7P%EH+^jzANhTk=A6^a+U^$)Hr=Bf ze95nrNepxej|EwNYb!;lT^J>1MhuD4_B~J!H$aQsn#GTQLpHhr1CkaY1Ml&-0!FP@ zqhAB-y-x%77-RjE^KBE*^iFj=IxX7DTlR~&RWqFjB^TOhQhtE7j`Uh$7T9?PM}|aT zN6fdTTi?|(v<_Nt98CKJJggr~(VyhJ7_UcOWEoEc$|9xsxlSz7om|P;sToGjN2i3Y zQ2$2Zgxe)G4@G(v3uwZ{GU)^dhG3hG10mu-*9xE}>oUknkHOSRM=P%Ad?#`Ysa<71 zh!V@#oZGPKK;D&2?F{OBINga0d3X0a`}6L40avruU5%)8<~Ic4huUQfHm;{j7=kH6UX4;7t0TL3u zi}F=$5&3Mklot>fo|QIrW0P?$1icUf2l7_>7o}^N`;*x@%uJ|@mgGSkwOcPaqzXKD zxcRpae;RUQztJn0ogcwg5p3~l?0o%BzB}72ZQ%VQ%gCAe*IZ&_74+Eln9MZ#bH+-?;T&BXX3jJ!J( zD5{-x06%X2yW}9FI-BG~>cO?y51M$hstf(r4Q_v3RW^lC->%#)+@25vqcq{;nCG$bQOslA@_Ykmnd_Oyk5=I%zIH) z{MU1;hK*;f(_chm0#uSlK4y(39z}1oa@xk(#MPkKXatn=QOFw$1dR5uenr=`FV(vm zE^;2s=EC#r4pSXj>a1O)MKfbr??$asc}C@8k&~E0BKf=yd8B+NKKJg~X)x$~gL~@$ zLap<2{oLar{%mh}6xlo_qH`s(Xe9k^*m(2A8u(3?4Q@g3xIo)O7XAKyXRPuS?#JXV0~LoCfx)wo!bhSAM-SSzl`N>@roYIw&%>! z^WE_054MkS`uJ>wySntcx+jj?wIo0{#^)^Ekb8A;R9eNJ9P>7Bfl73+h-Y#+cGe%i zLROZG!ik?6>b?tj`a-JgsAl{IIRMNfsDHf6mgFr?`+f zcrijV7I)(`rT3`T=Cp_KI6Md5o_HUWhh|<#^Y#bhjpT7foCjgQF&I-w;eKm7fuW6> znyZWDIebzjCW{szB`}TL7BK%Kif%qaK&_2E-XnH9^!1O9?uf>w1t<{|c|DYDbvr`k zK@fY3(u}UZr!hDG1V&|LcYon?9$w}8+nqC*0q0t;Nc}k18$5MRx10Q?#DE!H!lJ9NrI7k$q1ErwJC^)F_i>W9L zUXBOa)=9AL2WbBA9>D5wDwIm@yfnC8! zXll^1fsTRyb5$(@*Qh>dWlM84^#5GDqe6qoZhBTu>4QRe;~A+-(ix*^?aGE7{;&8z z2WCc)F+m)gw%9&x*&DKRj5ulOT^xa4l!Lv7?88jA?Rk z?Tx0Nu0hWu{&#Eb8nU5cYYKn5^*;165+8hLpVp5ZBewW># z_Ri)xm0ZLDIp#^&?*%EZyrEO{7BclYm7VWibV8L&J#Tk9kmKIvz@V~_atqEv5l<;w z7v)aN*ZY2a0>M1IuqT8DeH{_RlVgy0+1)-fdgj|18gC*ejx9zy)-4zbp!Kay_6<|! zrZxK@L=16)k=f)dHV9qE*G5V=A?x*Bopf_6@D$)Hd^z*m7txA~lT*MB{zS1Jgwa;y z<{ZreVukRMo(0{Ipv}SPS`iv|n8y(7yICYO62gtNxuqC4Ou1>kLGdUEK>@n-);VWV zOH8{jZjDMk@lr8F=O;oH2#V#`*WYxK*EGV9{Kt&8^IYX67Kk^$v^^eTwz3GU+lgZ~m;P4xGEV~-P zvut|mpVC`}*=?h(vyhQZ!pd*)hR?-cDr?4LzTGcSa165dbv`0HGu~>T9#rl#zDCDj zsy0lw?Yq#dFQMFf90&iJa3&65YWZTfy4`7c4{REvPWynk5x{G3S0dZIlphLeO8+s> zFK9+n2)}~JTd(MErw@a>xu<_1rdw^tW>S)ual*zPw{dLT0irh-&Sv4-NI4X%VDWV2 zoAB-mQ=Hra_&|A-|0Ir4!`qhC`T;24t)wq^$M{dXeEDA3 zZ7cg%+nB~^(78Okgq~{E%MD@M`owh zP5#H)q+3)mo$8|wOD_w$*t7bG-R?=S?bF1cOky+LDtsluU@emAa%ew`*?Y8}i#~@L zSjz|_8*7k~L?x$de;9LoXR`a~6CgeQQB0q~MFwj$Bwzw0%q66qpf+g3#mn8ip5*-Y z&M@I0b%33C_gOiFLiBdN=D1T{zIy*k=$a%dPZl^i$&VbSuzrZc_^Bu^Yx3~F9;Xk= zl??nKV_il8)4H{}q*Bf4Nfy?^&3o#u$sx06T2zn(j7`Dw>m!ImQ)2(dAZ(OBUnP7s9cdp@I40 zt=nH5YVAqf&*-2MfgGKlzfV_0@k&Ha0}Qx)HuF}cUr8|Dztin`pxOul_ehI=Vzo7# zQCg7&>$n9NsU>ma@^sV_!wwfVaK2=o-i_D^c{lKGl8Bni@V@vJIi<617+gVYNo)}z zM%qMMW=W*;3ESv6z*<};Vk?0*#9wWLO z@8^d?`wE5wy|%b3R?_p#C#hF>)J~5SZGrvV75W%f~XN(>GjoronE|$ zmR*4ugk^J~KLCn7ugw>Ol8WMN4XewL2Ga*6o!C_?i(hXS?%lRgYR`>GAww>@pIxeT z;$;V~Bj}q7vd=U;Wk53bUh_PMroOuiGUoV^-(WH0%)^f(WI6R9sU}LhEjh6IC{+k`h6!K>5 z25O4Jx8_Pm;6d;KRO&k_zxX!G;Wvkqt=5aTg9NPYaevPlbp*mu$yf+iYB%`%$vQml zQ~hMt>+&rQ^e#Am&?1>hlZ8M%9_7ggQ>nraxl#hGvf)p&Eh>6C^O|PS)z9I{RhIYb zaWldp8*hXvH=pw=;dF(caP)kXk}KS5fu5$j#iMrGmgOimV#%ACd#y!SMl8z!5*HS*Zsz8kD1J6)g__T6exN=`(d3A_z;f=E!YFilO zX6VsF49;ll68X~I|48*YR&OL(>Q{wK6O|_^liLLN0sRX6zw*}N^mF^dWjFdF<-3A$ z2QykRn9Y>BMUaO?voS6c-Pzy6DZ0o%(J6$cKcH~FhZL$E=rmRr>qRwq?)E|OA);d} z(dx|@cK$~>AatTTwz0A9M@L-knq`Rw#*=Gh{ zfC9Xvg|u@a5;8OWneInqO?)$rBx(&*vO-YkBb+j~koDJ)Y`<0t$bV!fDuCx9?;jLV zgcLc;(b^^QZE23%^-#NPY{WW$ZNK8@|E*~`c{12)gJyfiZwsu;@sc@ZTkiL3i27YAHs=C9e6)~qa~^NyycuiW=RetAmm zhn7cLK|Y5TBO;KaJURc&E@cSRR>Pd+;I`{kRU|M?9{B>mJgHSrg}Cg+6(6ds@YKl1 zb$3kzYiUdqrtgq$_NgJ0J(yp!$$A~(s}b7k4vtcPR=1T);vWzh!} zhifoi3BjB=-AF%Wfg&754oU|{4&Od7JyQDWzTBFA+WF+Ug>E6Wz*G|$JNfKMgw1^R zNPRE55tG-Nkccpyo+T|&Z-uZ4%i9ev6?C+trNx*tm!!$3s#0_4nPz3TKgNqzyLu0< zsXp@0hY*nu_H<;iZB*0KVt-auB9th*-Ad{HKbhFl8#sr&?I*k}Oiq1r zB25#jzyG?G&p0kl_JuIz3_X@~npokQncTP}BOkwazEv2zp>R65XH@$Kn&OS?kByq7 z+X362(Wbbs)s9SE#lS!Al64gXvjgcIo*LZ*0(z!IT>KZz9&(-LQ^EZw5YDTXTjyE` zTbw?J*EH}MwHAN`Tj5#{Ad2(_VD?Oz3Yb2F33gg&0|57yDI%aRMY#wVS?LmYco9!vt zbKF-(O1@AOW{5^0{^R)^s$v>pfRLz~8DHlej`Yy}W))xN*1O3+JY}QWu zALn)fm^s6o8AI*drp`!m&-~z^Sppqg+czs^ABa!f7Wck=<6ywxUzmF5K94qf^R)3| z_~txdOOEApM6|s}h|O-?0FlqeOqNpy%LPJwd0GnClK+j*`3@o5-! zx-=IyDNc8*27bU#yW!vOi;?3JQ~$k!EZ2n=d;Z*;7XZJD&C+(k1Pt8gRUt=}c{j_n zM`$*Ul!#J2V%MO(`B2W^cv6grx>S3BRT56c{P(OFIgucar2qJFQvs*KU2950waL2t z%T-<;Lxv@Up@6GImue)oM4*p3qPNs}Fub}n!ou}}=ubq{t$Ur$<=XDV$C$()MI4B( z`JKcAf12d*8jm?sLAA#@pK-mb9o<)(@rNi|ahm(E{Z5S2({Ri=?AJvbQdIFyB9wi7 z%;2#*{wLl!#+VZCWlISSUPCIM3A-QRW!7v{w2PA5p*>dYHWA#VdQtV?ssUP%wuQ`P z9`}qEmP0P)r>Z=p>&TBFt{I+k`*%B#SV8G<)XGqcDPk_KcLd#+fQfS5sQ=SV+fhNx z^s;+j8%&|tl@?Thluwr0W=38laIyQur{)40Nd)vo@--)MVNBZ8n@4PIt-%-m_G-_4 zF~gj%r7xDd3)dR@5;j0SdGZ3GT5x(LJq^39L1dZt3JXPEL<0*fT$AkpCYKZsU^HgI@+ZpMvCpB+hTJe_SC%e$K z>C?H6?S#FM@N+PW&~vjZ(_z6Ia|nEnsqVXja=+h~AoDS&CCTSMT0!h??&~jt7*Rcn z8zCm!t1yQ~>ZAKf5vO8TUf`t2X?u2l?A1d5ZH!QoQ zeYUBB@ms(Q4yU8sqT2`-4X`uv4my4XicaI&Vm%iCuS>k0s!b?`Jvkhb7d0>7m(5;t)x~8fVm!yXajRL2D2zyb-e#Y$xBmXZ+Of|G9d`|@(BLzwy?W&8V4H6B zHY?C6+(4~=Cx52}|5pPo0k2tP6Ak3G3t}9q0A@V${%&e%v2Z4oshm!t@1i}2I_FjG zqCD=S{6KBig?cfKbC((&ZEt*U~b%9XO`3u$+QQfakC1Z?@l` z;SRlg8_%>9EC>@GXTGs_b_leM@&B1R$jBR?3E2?? z`aCtoqT9K4eeO=JCZniRty$QmS_}+0K0e>yRjHfUjQ#wYZ#TvNg&XqObZG2P()8SD zB1S{Jz{2B(k zx(zq72x0UOrph;mjA~kMQ^3l3yYnsDqymj}{>y=YrQXxTn6mN!nGZkduzg+N6joTn zy3#$Pm2%a*J_x)C5WZ`XxNDAns93>4V^ zQa|%2abAzJFm?fxk0gh&HLj!3x&KSZ;ESod{W{*x*RZ?9>ulA-XQwn`-Nx0Xt|J^Q zmD8_RV7KZ7`VRMFe|9lPoVnWC!AK_O#}4UBch_Ayu*taNv!3>&WvC*sO4w&Z&9^8C zl*o$NLekFl|E$Uajx<}665GDdHbGPO6}PqUsQ7!B$C?JXZ-%w#017-5oW1MKy3&#q zy<{4_+J=t5m@*)bC5|_f%OK4N`-i3DScK8mr z?q>QqZG6wT$HPzev-ijKg`V6?aCqYb21#D7~{jAr}Yv}Il`(7UkVCp<-JlHxhUbhe8 z0uQSy+JFqn*FsH9k7_8Z5X#zOy*T}Ug=-2}InX>Ubln1-X8pK&2Rpw}-{rlw!+S-< zo3-t~VKFN*gk{28HWrpGzGkrv-v?#j9O9aE8{RJDEDGfWtIzx6M?F3zE}}cVvZ{+R zIL+QA0l8r}ckr3I4GctWVZKNE3SY7~VTPUwY(d{E??TaO(mVA)qIYjL8J}rrQwj4q zSS0R+Os#P~m5?AYaI#JjzTgkK&f`mBmBj~ktvl2l-cooTz5W?e(-g^xyW9U1HA3T% zWO}kSJ|KE}-uLL8z3*N326*dd0{A{Wd2gtCq|!!kFXSBb;@++YGAo5+U3S z5wyeZc5((rcNdnTXlbaZw9F=8+O|kqDAi3NeXY**_X4n%1=L#476j8E8|!HrQI_9{ z5CXa=_lL>lR<32A8T z7tji%Ai)zzl$bkg&jR$Ilfvs`)wI2Dtv0KonRciLG`lR$IM1fL<)hp(`6wV1eY$-o zWhhfdI7)a9!pnzC0ywW)(hPk){Q7dV^nE4w_sKmarl1|+03Ien+B9pQ>K^HaxWC%V z4yJZBWNX}v<+hr7S0B0#*=5*wDgjXVjbcv~%c0$~llF}hk#RuYssdl+QYD4gwrSyD z8r(mY?f=OlTd-{wzJxQH&u8dGSEf@fh^s2ncD!Ic(XX0|e{k)_p8&KiK+$xNxTtQj z`c@?9l~=_4-iXpqN{;u(ihY5P-kLUR(2XY^Jxfzk#J5B<_3Xh^RuIs{Y+h$hU zl*Bwo*{qOR6wiI_Y56MO4PIgfD3ga-p$pKB@cFWD=|!eBbNfy>3oR+-)~J7_%@(B%{W5 zaEAM2GcGFR99H9D>bYxyKhF?FDk?mj!9_tJLTxz>$wlmVt_b9@L{JIwDw6WCp@4|r z1g{%5y1RPBzx3-xwpIf*MJL`j&EUyiTma;?^Z1*&%l3oIs>;(mx>m}czeBRVe5~~2 zQLSRQrcb1gOIG-*_H~=V=_Fm2lS`Swg_DpgE|kf&s>h$_8siN%vtr&&!KL9ba07wX z-iQVV5XpLMZwFN0AT>Q6_D^(K{+V`Mjuu3yT&Bbp0~`)5?z|q#>L&xS2%q{`e0j1L zW(3c<%Qsy;mh1ot6Xol+92y{m+>To=hpI<=L?}re#>zhqN<$`)fWhGnZlF|N2>FA-lLpwhG`yTnd zdFXk)5WG1K6tvTUNh5c_XCvdZGc3Q{~|ZBr#g0AuhS``Wi419SWL~veE zbzxq8pXk@v)-%|EDlcI+lNj{|-u#(PY%*O34e4!78lu=}Vv7Z3F)rj;{p!;FJ>GwW z;+{bVOEU<5umSp9$0c$-XDT&OrC#VfG%n2o9G}e=Lgc^U-vb=*hfW~#l+Ko0e=evE zpoh#K&?F;x8g`p%Y~wUrXj%rz2WT`R$P1_xQ}Zya`8KmY*Se}(T}Bzn=(+sy5Kp)H z=5t&C2bAl8Uh=ebhu;3nu>kynGx8QnE;E_^sVdX5`xCKKsOPX1eqHoMO09Kiuv+?F zJv{0wG6M}5>c@XUJ8v_{zM^Nz1Y)0VMz{(81$WWK^`@IXazvCxGZrWAPZ09s zRz;`yVx>s{c4>&!RD&!l0$LWjP^4a{_UlMv0ab>kiqxfX8&VfEce&V&+7vSQ*#Azr z{WcKboxbeEF+UlENv}b!tOkztP4%k34xi*B5t#g8S2#-6fv>NU9=>o_L}=e-i?u zNaZ3khiADOZtO@p+z*3ZFQ)sFz$J)DuQWno$1@UJj2F<%+w5;zNOW)ZQ_T4srE}Qd z*qS~6g>>*(WmNvLPqM0f5lodaKLZh|6oKEl^}&hoCI9&?mpb%gmwH@5!$#gzHDKI$ z!6y@ENUolj|7^4H6RChD`<+q!H*wEZKK#7o>XkU`fpflb^ks8IK#Ca3KMo#XIkYR* z*WVe+D8u`tA$Yf`9#3>YtD!=fNt?*Oq^#<4seFhWFd7S#)2;+S-;qEdpnMJ(%p0K& zYQO5rjN*zR55I?pX!LkgT~#y|#tRvEehD`G|1lF;F=x$+NA>f4y6+45L+Do3Zs0Lfpl^`Zm|sCT$U^ADH{Cz( zvR0ld>bR#PJlumS(GZ%WiU+2S5RZ;XE~MODz3n7#t@*N=2>u}gda1s(%?UJUc8J-8lCWFzj;9cZc$WcJ+z;G>jU-7Uj} zND)_=Y?%+Vy=%a-TVbJO7)k!u1>TOV=F7tb1lA`L;F`KdxLV={il~)V^aI2eGCiu1 zgYYC2Nem((n184K0AL;fhjS?8@L<;-!J4&&Yuv zSp!<}jVq4T=mwBK!0*25C)bil%U2)eemweENA8@fi7QU5lur0V70F8-OcJ1ht^zaW z@b1gi&=e2-K4B5b-VwP*;1TkLHvum&NB)+MVS`ahxu<;zoG^+!BlAU{j`92aGf=1P ziWq9O!AF3O%rPR`LtxoE?iB@ON||ZF(b+8C`9kR7kBTE`md8fk)KevbcGhX{Fp9D< zhL`Ut%@30oN$q#)ze{_5-MRSQ+#l_EN&RcZbL+!!_fsX@ohzdAC)xg8X6;Z5kiY`qC6Uyo9#^nq!#EV0Tn?STI;+=L1#oz z{7Sf=UB6cc6nsaB919J6MLwT&9?bAJzwi?Pi3P3CIo&N9J(M)99XJ%hDSvu2t`iL3^Rg08~!{K8pVG#)B|I)sjQdh^wnKmfT9<@BYg(>Zm2 zecC0fjlqgLdZG-7CeoYMw0`{f)_1o^t}u!+Ir6oB&_c~7lAyTO9` zO+~OT0*{CEHEr&bFQ*nn1V2dkAn3qdwG69glj~k7^db#rkM|u}QbJL%)}@5~YZcRn z_705}_%-*c7X2`)6XcOyHi5*iLHPWFbNQ;D36(KyZ59ldU9~;TInq)XEWrBIH#x9%L@}MfN^rUq$Xkd+CZnQyVIcjPb&0}o~r{#@}9Z+8_2^$e`q zmF*9qf_tk`47#=hC_m@ZGvvTlY?tF1?7hqJ!dspg%CLzyA!4l0Cf$0Wh7b5!<=T71 z?Y$A?g>{b2W;69{z%E33A!DX&x@ayIg0olbN1hvQM0R;NH4{h`BUQKD(&#*;0+y4~ z=>?FOC6g0x!+ge63M13$Y0k=a^?|8isOW=UgyH3??!30ANvw4v#^*t;@-eyDj=dN2 z;)FD5=*OJMA&^Nt)uHj<*nIFqo$Y=gO{}hU{?N|XcO%X*wUq#muEY9ci`@O*iRDOc zBhUGFd_0F0K8x2g9=&xY8@&>YkN}(VxH{dMgfga&9D1w4k#flSnJVx3msBf%20qt= zrv<4d?xLqE<#t%2F;^ki_~h<(3ey#>#Yyhu@AZ;hxl>~5415iW+jf$ezzOW2=~2<- zI(EHZ#clTiHPzs-8t}z-P4Yp14XifE<~jU;XTHvCUahYRsExxDv(Ywa?K_7mI_hPg zw_~q&4C@s{rVAY zkA&SYWhV5P*EVk!FL*bLX+Z6lgr$uZa(-07C*Z(Sz423BB2}&3Y{yXu=`-p}FcN`5 z(qYp6+ykTHr>Y4G?3yHK^$=+bF1b`NXcLE7Zz@$}=Clg(SeFL`QL3+!xL4Y$wB-*b z6}tRk*{{Pp5+X7eIgMkMFNp<)Q|lXiwM*^`g08=~#fPWxYWY|+1R-!fZmMR{yjyIA z7wM7eyTzpL4ub$mPa{k^Kfoki=scetkH{+}5tqW;5Ef`*S7X(i9VF_S3F^HzH2u;HUj^ zd`SzGc}+uX+t^*mdb`Er3oRL_&qG~cm`I?*cM42RjrCxRiL>+r&|nxxxD-I=ss;ac zqWNCFM1#hJixvM>Yp}D*qK8WBb<$e5Tl}V?6@HRj1FPJBdx$kwE-l(-G9+ zRY+koJ{c!DKL1N%R4 zQ!kxz?=3saJZ+j+OOr%$xh76awGh#RG*`4P_s5$?Ns!IT(QBgtw8Zw$Ou#4HmFhNq z7Y-Mu9pka>8uezAuQ+!nB)-{j4ul1~5s4-psuUkw3CNsEH`6+1edzz0a6F+pyfYh zH;7H}B%F1SUfY?%xS!Y3TxRC?0yiSn4bQu~+_KC5AhAlLQHRLUUw0V{7_PrERuK0y zERgH*a1ZBjyrN(Gez8SK!{0MUF7lHM3kct8eE3B^PLD*fx0Ao!M`g~nNk@{>-f0w) zWI#TmO-0Bhyk&B`R{v3`zB!@fZGhPLdfWf&19%{w=`n@J9?7kcRlD#Kg?ekCxQnSm;}l0Hsh3C@*@#2uKN{ky@xIPhr2L# zq=(gFWQPr{>REu{_#PB-3tJ+s(Fl0xVfVqB0uTL?o)9`|+3>qHLT-^vQlUZ{5{tUZ zY__3Q!p}wo4@$W==S*9pE@M7K$%zUI<9Kr5Bd$P~mCk?jNlGrV#BDCWso}s=YuHHz zDsjL2F=N?dNg(?Xht<`TZ}9F$J?&hZHtH55fY>m0rL7-CKi3MFhuv>looAO1`Bsue z9p)J))}$#7nHIZ z;~&f?$}}I;BAi`3slCR;Vmb5z8HAN)LZO8joXfab}vOnv)atkFR%z!T7{9D(0_mDHGL^ZV&t4HI5zAo zGtRYFTU8<3n-=#%S>2_SJrRCS+=3ygLIGufatVm!R}0lCEaBKO$#pgG^hXrI4k>H>tW4QzP9-{KW?~B7;sE^ zPqES7jY`=l+56jyhKc?e#Fb4_M-1Q6-#A@j0wFLvpo@|Y$IQtxaa z?ZzA)f6bE$B9Cfw^v&K14`y=qlf68Fh^1@QK?AsxoiE4xWUlF!{?e9DA>UmvC~}9# zw8nvieZLoZD%34PA%JrSI?3;@#m}BNp*e!5ZsW}Km3&q&pC&D-D+}llB)9{bIc?~_(|ct&hrzg|`5WS)k4@|mOiGCZeg0Fd0u!PhF=?{Z)L z@O@6s&7badJzrF{I}){(_cWC|#CrSfA=BTlkzb1rNu>amdgm4)e?`$XB&<9*r9xR(6iyG4HXQg042z z!JQO#@5V3;Gucc|H{Lr>gjJg9gwhtd6%;Q{A7jGx74)fKO#iz;rZG?f+= zslJ|)hSNn!p`eR#b4GXjNt1+8pyU-#YqdwLqGUwrC=VdG^025J5#eE`;3<7Y&wJB2 z>La@BvXf3d_oj-0{5i19ha5S_q_cIF<@FrBqCqR~2#B0%d~8u@B^>6@tri&m7IKfj znj)-WTRGtJ4f31h*I~=5|AmOG{Pw^-J~I2HzJ_2zw*5JRs`PI(S2qb^xXS?ul_7R? zV!dIgo{A_Xc~Y5Q3M8h)=TqM7sZWWZ$LRnkRxH@A3*|10Q&K(@r&{MxaP6UnlgInr zcg2gJdyNg5z)qNZb*WAx0$PMz3UxvCyND!%?U zQnPL}89U(`a$s8zo-nRrw1l1~REXK$mkk^o*eQ%v6kpyL0Znywyb3S?d4r&?{jEvgb#Uq*Y|bkO zVawuY^iMEu2cHZ^ycl>lhx1mu=N&$Ws@NT|RQU^Ifx8a`O3G%XAe;{-u?v9 zcU3gElKnLCfKPkW*e!cTZU}h0Mq_cRafg7~Ug#y+t{sYlxz{5NNyzYB1x;4p0PkCG zPb-LCmALnZ2G5VO@gOw7HL46BpPw=-Wz(%5mLR=uj^e(xob`Tb$9;qxyfLZ2CrK3P z&%|T3D#+PgXq1=T5tDjlJzFZzw|p5#ddfrhowaB=ox z5QA0p8LA&`c^#*O1O8KcGWPwp=W@wuH2%|{VcW^#b}F~!WKzpU7YScgYXLxQc$3Rm zzeR>kl^Bj=D%=SIPtfBkWqh`ftbec=3@TG4(U6#P85STGxJZI-si0=c}RNyu~lmTxSeJuMR_K{nX?a@G|MWym{RY5!yTSy$S{=w-Uag%Ea9nlHbeLn;3z# zFdbPs*$aGv^3S)d0ZoFnI*sj@m1>YA8=LjT{a@Hd`EY^xE7T}Wj?3a1BfkH;O{ivo z493eivge*EIlG@!qFWWV_LBI3H^X{ac*NhYk_OkGyYwG7->h7=-EJ1egU&rP2+7=* ze1kR9>Lir1w@hlU%7vtxs|UNOI~w6!(doRl#RI3y`%qggce4ThjtN;byEYP5{arr` zgFALq@D<{+AhSTa`$7?PS0uux;Nh29#(%TyWYn}Z3su^17Ia!=-<5VvhU_)I1Q`+m zcbP!Kwq<+7{&(50r>6383~kBCgB{R?oK8Nx9&Eq%XW%IsS-p{koa=81xb9$2zAS(3VVG(KbUdnluU z!CjP>ZQ8(;6B{JqIw~S^I&e7vA5jnvZ8zv-7jBM&prEy##jBx$8bTYH#Z6-l?cbHk zHeo*Xhc-VuRY{dYIF90Po?*UM{V}eL1tsh-p)Z>P(Kf=BQ*Jdq+J>sZ9D?#Q?p(l#(G-wy9W4FqYMglh z+ySYB+NN;$BCfFZkT4lWmqCyj$+a7FzqE?Hp~{o=_6hLHgA0D0T!_Bma-qirNaDO| zpcF0rTafCTBdB1TNb_759+iktfxzz*^>DpNkO}V13@94HMLG9h!cg2623*+}5Za_$Mr%A;=U;5bO;BWOIwOESnepwh2hIr29B zt}yK@9OpA1ERT1K7NubC%C?^k28Q$r$Ww+$Am5U_n10JzccmO&Y(eP zTe4K3gg$AsHq{0-5u>r`cXNeh9+Km6kaHkW&qn2Cx((L!;L1%;s& zaBq2~{P}|RsMRN_oC&8Fi7`Hw*!oNf1~7D2xt6Ty`G>u$7j((uGS>2{i3 z|BRQu-3*Z>?Y-lOhuJ!NKiV|S5BZtP@|xB93J8_>Wxv@p zh;(-1Y1rK4-#L-=uee$9w3?FVsD}%{pWM9H9B$CGEMeYRw1cu5K)SBl);q&VzKG$K zYIBH>V!ykAt-s|k={!jJ=sWzyYirt}Q)r4U9xaOWSBCqcLoJOi>o2$xx$hs%8U2-R z$!Aj~&lj%Tm7hKTo$z_Kk6U>s6U00{ zHD&qaYlKkc&_Wy_S>{NUtL}{U%s+|O&zkN@Th7mO<*WeWritq@nQQwW)?+Orgp%?} ztg041(w=%H^ajnYB*G5afP500?9K07JIWl$u58yP-s0#6d~aW(6+R))nVG(CzZ_TI zeC!Ehm7A$;YQD8N!pK)H^PqLH5E4J*o)YtTCvvug zKkZ6*eG4H zRla->uA@|Z;;L9}G5k`QS?fG)&m;OUI~oN3N|J6b(}VB|+ULx4E_d5}q^JX+ex#}c z8m|Ncs}XhxHC>ehEkEE2YooOjkF1Z<%USTMX0nwWWW>T(8qcb^6D)W{H4TY(`jfc- z9%J9rLL*hMq;PPbIpP1&bk_fL|Nk4;Im|HA-HeH8N4~{yv`yO>hUt!@b7FFuVY=IJ zn3?YG?(TDr6W{as;rk!FUbj~~pU>-YUH5CgBICBT#?-**Qcc{fPU_vN(hbFV5|2m% z>p#&8AmZr|bWQEPZlHRRD6M>`0mJ}VRE2~*OJb$qm#Ug!>wFu}*R%!JKTOOZmoll= zzI=a7@KjJ7bWi4(?lOm4tZ`Ey8<27OZdTA$Y0oe6P;vHO!fZfXfML~UJUiE%?cXp< zwN-bN5VSaJ46x6H@G;8bAcNTjq!j!_2EFG~_PElU0o;MQ&a0d7i8NcMLJO*51&nxA z6FQl%^nnh0RyVz$VOUOEi!R%rS)FhuPnk2z)Mg!b_PXpdbr? z)b)eNjVqzQOOJvD~vmQGwxyYGV5cqK{McNxVu2zlq5ts z>LhB{oT;Hx#_@q(ODf^EHJ7JyFh8y8&11?TvS1bO5#-$|*W17Mmm=EV*14%wODj)m zJ>2;UQ2VyAxb>ZvF+q8xp6Te>DVncrX6Vz}HN);VS|GQL zAcOz2#V|R3q8D8L0|?^6ThVWUY0-_PS0P|`Ej|o)y5RMIOZ(K39Rdq1M6;_RSAMK! zEujt8wuP3O*<;q}e&uUHHd%Bq!5#dN6JGx7Kj+pzAC!Gfdjt8zJ$Z;97$1^`pJQaB)lJ9+0RFYpMPN5*Wp2=z`@>)Lv9buT;qc?)Oc zG|%t7%Wq}~nv|to6Kr|+KvK{>O?Z{B$!#Q>7^S)3>nj$IZvTckC@9zK83ZS%e~sO5f}SMne|{!9U&iC9d^p zFI+X60<@WxdyN%?-;m<_gtkI-qbMjp!{az3zyAmXpAmw|Uy)~#C-WBCpo7lM zFrQo&+9~87WA=UGVPkKYmRrX=li$3$+eZpR>BA4hypie#n>o!VzuE=Fu7IgMFl9%| zf$Y*c`d&Qm(!Rd;WCL=&yB;g#x_8QCqF|5^0Nu|lv-p!!;IU^h$ozGLGVfyr)!Pdx zK)Wq?uK9z$7cE(;>5;)Dmubbs7c?F4-M&ke(e5-Iw|>PQImRH#@JHV3L6*MYE}TQizQ#lZ*(XVDOA^O?)^H=?Ci@9p$!ue8N~jG zdc^?ITFY)fbh))c%AUN~DgfRaC!K?LDS$jx(Z7C%n+hA+%-@U4PJi=F}hrQkh zk@h*>3%#;m7U7esrkol^E)B~48hg&)X6HvGq}#VKpj-U(;{ zwLFxXKUGwnSC&;bu4~giztaLdEl-MC*PB$Crp^#(4YfEi+UAPN(C25Y0xt9uJN3!6 zbvQC_`p+90gPs=Vip-;z9?kz2%_?*brq5QWgZ6qcf6_P)ggY1Wl_*)&q8d=g?VG0# z8}P!r>)VN>;V;XeOF6Mh3OQ@>-kQ}`YE#=ZbHA(hB5YFr2Re(O`T3QoX+532&bmfg zuLbWqn2SzcWc8B!3y2EgT}ams<^^&N?1b8K_}Rxw_Qbl{oI04!D6|xD9}ctKDT!?b zd3uhEEyB*Q_g+YyurVOko^rT`^&wTfj*SK%Tz9AGE9iriB1vy;^Q zheSCngtpc%Pwk*)lGDQ*wNG11{jH=**xIdtmUp=)7R3V3moQ=H#lyi5XTlAM z#v|_pyMNT`Ai@KOK<#U;6`+ouwA8vRE0G|D&r&z#p%0>AlM*Gw`LX=Ip&*2#+4_6e zI{tY_b5W?rj}Zw*qFGT_8>|rZG>?5!dN?1oD@0%E{cKsA=e_zl>ML5^!Nb4%YRJ&n z?;SD=WOCT>wF|3AxzUkrfvF`C+t9(c6n_d6}fsKf2-7o0S z{JzHgpawSgL}^7GoWXl(duH`ETY0%Q_T~XLyO+?%o%-a1ZJxBH2XHNHP2`zyU)hCV ze|UGhC3th?*f24Y^=sfI#HFa`!tl;(86;SJaXNyec`w{ID#@~Ism}yNkh~h+ce%Wx z_T5$?^CJj-X`z`DkpyR;>f=DLiDrsXTxxi7$Wu)Rij4hUeGsWF#efkY2*wSJAWz756}4e2G5 ziw;#48iB-%?;6uyQU9IV{@g-@c_mGhwh_vQJRNko!t4)Y1xN@72QBsi!{iCqSQ+4S zTy9XGy_$GHAsaqK@aj}c86qNghi4!CfuO`-0dk&Z^U@N-jT>&v&fzD+-W)|d^@ktt z+orC~%qHeefd;t_+G^V#R<~6EEQqMp9G}yz03xX^hDPL*9iX>#>?wdJAA8r$?7NID zr>T=Ef3E{C=5I$KBA=$o))qbiZJpuRc=Sbv`>sqV`HTN(P(LH%RVrP%x6DW;vVu4# zAc$^}JW!t>Exq<5@6#V#Dc86-Y-A)2|J8_-kD%AvMuaUAe<(e$5pVq{ElA}9Qq^IS zu4!Q_OW4a@N+n<^?4#JTrj1*RuZ6Q&lXpA~C^~R((@yGjG{P|Ual`vNE=qpiwwEN* zU+lF}?Q%SBQk54*94IDJ#hr!>PP-IzMn7C+yT`W&lzMRJu2R5y8o!nsf{EMfo#Kn$ zPShnQi+vK+F4Qc+0Ps}OHP+GhR0gK6y)o%1+ntbh(4MKhP|t4h;UbSgo8m%h;8?4M z;eRc}#G>}eu&BhXdNH)3>@6#jy99m2wRDsF!v`Dzw-w3pib}N#1sm05QWCB$-#!Ec z;d2p%as}1@oRAL@{hRvJPD?N=gsAbA)q@0Cd%UJxZ)9xdKA+JqvV|er)La6+J66Yy z4&pRSpJ60_e4bRF?+<*VYQ;e>PL0|-5)RWVmzkmfAZa&$LiSb8oa?kOD?)UR*=-ew zbop}UBPG3ca9MJ~#Wjx2snzi)_bq@UGZS&e#4FE(-< z4l2oLa=a|&oO{*Vqtaj7C*bu*;UaB&MWD@_!+Eu*M&{WBCF)OXPaG_>dy(4Qw}I=w z%4VXNGBqVXyN0l@JbX||DUXne?ENww$f4D5{T7p(Ef+>nnO0T$z;Zk#E8@})?VUV& zdgXn1B)o6Z+Q<@mV;QiYth7#Ks6nk`Rywf_J&q{iD*2U=U0FPaA)!Q!z!xNXD3<8s zRA~iq3ceg!&yUS3zpV0fHFE^|?s=1_EdlssF~!iQ#5J@sw?mx^;B~Vir|>1-GbeAQ zycye{ETM!ejUyMp2g?3L`5_bfIVe}-XYgF&P;IsE`;(ael@)k@fhCv)Y-hrbrj*E9 zdVa9uT1clS&1%a!Q$JnSd8#tWN^}>6)}C61iY0|H8HC`iaq`>GI&vW0Nwt{m_0nK2 z>nkn!R-V?+J>tu6<6wvUUjKEZt_eXXQRCU-6l9G}`1?jR^(u|;?D>ief1GFCd0q2` zgj&{RTO{s=ioq<~ND+<`|Z|8y3*YAAK+Hk=aSPDv1_>#zoqegwI1;VQstn!!y5s0Yh89 zp}sXu%qc@W>*RJh()P(vXx}u@a=vGks6$WZ+}^VdP)yw<$?TKDyw2kOb#Ee*eeG3`M{k8iL&*3K!w z`Sg`_e;(ckgPX~|_=RTHAM#|1icwh$Q3+zzAL?XhD}Nt2Vs!=p=VPDqtJR1O`?PSf z)r0C1dB5S3O{`{BV(!aux=%Oo+H*!7#K{#hRf&A%w8v#|q30Yh86L$9^swXWw(?5T zybcz%++kzfZbsk-ZB6bMcqY#Hlp@Vh++#9v2V?rlmc48S0BQRduu%|#>i4RTE||*n z^|o=?t!?~KXFoZqwdl2Vg4?rk2!QUv0yS8*C~Oze0NF=anoyR#RZiS{+BtH|+K7cSY3TRPB+nWb(+XXM_rp z%r}P-xGp3WcSE}cSa%DYC({^p?{TkcqY`KJ^(+F zCI*sWC%W*(`HD}Yr^1(6-HVc~iKG-!$7Rlzlc|b7-DX&sXC|$e&FCr{XiC6+3N&Wr zhR?}ZkXcEUDFM_RrtMb^0zz?KN5@624;Q44#wsJnMNi{Mk~0DqT4UpV&?MyZeZI_q z0zv$r+u8B)e*Y}FH!QKvuoNz8&41s#gvS{fPyCtHEBi?sX(hszd%Y?^-buA=SmjVs zQHA>V#YXBRgi!nE1oktcLhB#FHDM{#5=6$zgdjP$EDrC7PTQx`#kwTv{B~U}KfAa~ z{%D#jU|uzq9p=teVrX^;zA?eO2F~Q4(RA$C(qW2iQ|J9ExiL}7RX!zLy4EOx_uFjU zf3)In9p?kyU}uKRcgH)Cn}@zWTnu0vKbbAs*&@U9=_L%qNhWecSP>%HRY zqJmv6S7rK6V`yCJixR#;sor{C?tibyG|~t-n!LE|HDZi;BWndqz99>~EqC!eDbt49 zSt&~`?XpApxi%22fw`WejFu1HO_T1s2jgzSO({Ptm-fVynSt`Ym)}UT2Zd4eCWEo( zrfpw{!;KO|P+Ox9lf#v{xn#NgAkH5Pm8lq3W-G4Z3HT9_?|Nr7eB1k0#~^NT>=`jye)80P4c69=7<5mc z3OrCqgeISLiDM;cq59(CKQDJ@{;eEK*1HMjDpPU1JAZ?Gk>FEH1%?(eTw!(etB45y zaBS|QV$%n@IT`2W1T?!p##e4TM=6cU-y?V=DDw<6M&&2`0f;22d!a8%nz7)`#|^|C7DpcR~7vrAjux9i!(fTXKaD@ zBxuob>WlJsT867drB#qs{R=>uynZx!AC5J;4DLK`)4vt@>0=Zj;i$S-CfYnG`y&v$ z&Gl6*NzI5Uv9>hAGcx_OAR(LGtSWr4(6ND&=#`nY;8Yy^Xeywa3d2&`+esho^i3Um z+CV0K5S0Bk`Rb76A7=Ky$?7@jO@e zH4>-pGwQ?`tl6#n3FIfK;5hivtLrQDlFvARl;P7UHU4Phew2U|{n-#k?ywLka`z#V zM&z^aDLqA=Zsth8;)YJqJ7umSsxK78uZfqDeuqVYY6L2b*BXU4MVx5|Fs?8H?t8Eq zGMz@EWFOsNG~S7;Ve@NlARgTQW`HaV%x$xCFC!GC=^zkVqPGWd?f(KcElG zjbgjOfsBRWwkeiKc$R-@6|ZT0`bPr2IE^B|+c7M^`@u4nC{ZF69{zuzxY}=AP`NE? z(L2H;UZ0OR@n>UBluJ-jc;LZZD3cQJt`BebpbrP?I~jkRh+L4whbS(Q7INbqV|T#) z7WK5;+P2blIGH8b1)t2W5rBEelJ2$WYk^F+9;d*b9;jR{hA#gysClbK$3jueRL542#Xz%P} zY=u;=YUUWQx{ ztfW{hH8B(0RjW=;t{NUXEOEDPW<^RM5R*xh1-b`?>Qbg>$vO~JLirjlSP9>#64%!P6)-3Gg3Kedb$?vVIi!3(adPIwX78JQ3i z2xx(>H9@0?EE*2qz3%*rp);k=Q7teQ>s?9a{AuR3Q>EQ3Yb%`9-D(rAMo=p0sA z%=N>W0*fDp4_2hxb}#GHrNP0e8LoR+_zAqIT*ZmFz0X>lL4}47Z_iL+-oxL54sz|} zx#GD-@hlWk$u&Cmi$1M@6BKFQ-pGaPPVR`SPZ zJ&WSZelPNxV~zU}^328M3S0V=3uJt1gUAdGCVF?Z&MOE@s_*-uFF(1BejQr))_OzPial^t z;TFG?_(f1t4xIN4klpnia)zM-5AhR6kOZd83=8Ack#rk4>_8&aLzCQ+2eiw#y(xkk z+6<)CR}-D3Ip^@K*IQKJi8qxtYm)QL*&*1kq2>jq-HMsjYL(FzSL$`z?YEKn4pFroI^9@rX9rL(27|C}Eu#%Jd ztcgZ#DNFzD=qj(%S|us?-wY*aStZk~ItW4b<5x@NuH=#9nq!?`Vq4qF8OtEOqYt7J z?h&rG0Gj1YI88@o9p)Yj#~8B=d|7ask8QHc2@f}Iy&Aq5WbnzYI~?5y%h)gU_;-A3 z+6o!Og8cSAKWm(Cb79K6kW*=^8Jm+7AM{J|O}ovjaM8L_hN>Z+)?Bqg#=le3;mByu8A0OF`9}>RZD?1b@1kX@5477ZSM-(qO&hJ%vm1~@8_WB+ zQDh4I&${m_Zi{M&`Wn}+PxWXROhxknJqmG8(LoAje299Pg_R_yknS8ITeJN7t-~dW zuGe)^&*|&UR8t+7%+heLGU1D#)e>Vy`;JCKPKwwd<}glXS{}kL+m8VX0zX8MUpgv2 zq9m!+Lntn7k&*(r(Go`d;{b-0aBJr^>=YZ!e^q zKjJ;xshq#YD8T+-vV_sV-~=4OBt=OQ7sC2D9IEPX`SQtGvc$3DBAk=4VwyWSyVMjo)#jq_99r1&vMGpN{C+R*$>?57Pk zn$_kMRV?hGUVyyu-@Bt}f$j=tnz1{3OXIS0(BoxA<$>!hFzfm80>wpS7V5X>(dzFrhH$d!;g`|j z`&i@Y+|}&kSt4bB0kO%dE%tBugIouNfGl}Cs|xogmfY|VaJEo8!i(H0MofQ z9Qb^8^v^{dcG%CM;QzuW9uAlU@kT)MINAt^BwSEUPqoP8F-XanJwXsLvCm}j*oLgx z0~qM$fHtOdGl-*!U)rG45zjfvCYD*}jPSt<6n36CqWgY$J1ldwbK*@ zhp^oko=ChfxB+tRM)M$iaE+U#$1xAfxo1{#v;>>&uv%K z`t@T$($Up4nIj;tvFAT(6*2d==5E%vRSRT#|83mr4}+!{9AfJJUg{*L+2mAs78>ie zJhfWD>7QpZbt$o@g!MN%nIZYxgo8(asl8kO0p$Xf%m`jj%GV|T@YU}S&E*9yggJ5T z8y$D))ut73U=uP@9SR+a#3zoyeLg?M-+Y)m0Qi<6KY_e*7M$nMfXx49&Vf(TuD$Y1 zZ=W(IX12C~oWs3joF_u0J@l0#A0}jO`QOhdJ?CbNuR7h`H@`6B?1HMOjgrzU3jkzZ#hlts{W?Xz!H~GoZUq=Qi zMd*8r;L9&d!*LD&dm$_=D-Y|w0%2Uw4u(4YSPb7F#@|#F+IsP+h*#42W$Y=@(!pi3 zia!TI;Q*P@$Gi6xFlO1;g`~65)L+=UzVW#J=O2unK<{02W6Be0{TV*A`R4Z){(sg6 zxX8v6N;W;SBC6bs!kjWhADN0w76`j?0OSnUaM@9Md%tT;`hnXO>Je;npg80!CPb@` zd0JoWMOL=<_EvwSEF=)n)}O|+!)n{|Ab1aerQJ<9f(AxG47clW*AC_ z|I`G)Q-Rot-_l{x!HP9rpa0|~``E=u*u@)Y8}|X8>KX zyXIZ1Z;Gnva6waAMjU$onNH`1#me3^TP@gge+9SL|K2S_?ya*z!@>d(*g^T1X; zWQ`v$C4HieZ^#&A-hBv7LArf%srmQ&8(?EVITOx}oFrhpuQH)zw9r7Mvc?@} zd1Rl7HQ>`8ftNw{u>j<-RGx@$2({`j6HjS8b<2B2r6Xtio6?U0k)Euat<1<{d@r)SR%dw)#$|V2d zpA?L1?fxq=il(&xwrH~``ULp;k~U^4bO09=wl>i=#PGrgz5NY(ZSKEpJ5nK4Wx6P8 zSyj90kykqLt6`yU5{DQ_G0)-IEFfF@#!LHM^${fszZNOR;rc!vgjdmi_9D20^zEK2 zi^2PacYEOqPFQl^x$xC*ay)&xUwvgjP@GDXqUzD06sU14Xsu07ox-3O2T*oMy>dtR zNH3?3xK;O^VsXf*sED^DDko@CNk59_J0gRgQvAEa31(sIRh)K_-6DI6&Sw3FZokFP zWnl>OX#s@u=p|cLsF#6Sm3FCZwx8+s*4f1EeZ7QT`OHMbtTL7MYl{Rc9rk;kj7Y7# z+npJyMc=AF={jC8r?DhvmmEB*27a}vYxf}yYW!XnRu{kNt=9(su*CxEdgpIXjDNQ< zKz1JC@q>mhEF+VLxo#%h5~Y?3?z?ovC+_P6>s&GMXcgJUJ>q#`GuL14l$p;Q4%cM9 zK@sJhhdN$wEP#gagBSwNl07&4n87Bsc3k=V2C zv-zvC66oR)*Q#DChP!`nOTHDPJ{DoUab9Xy2qHMIsU1YR23?lb(Bz)>;^5ub1h62$ z0wy*)*<*+K0Soxop~SB{4!k43V*C*CN#x=`HYQ&zvjmhKmHBR<^CzLL>&b~_K6MtI z&Qa>aQ?ar7zRSoOuQHX7uR$gA`W2uVY=ELlYH&&%cnshmjC!m3@nqZbgLga#_%Ipj z{x8NFlWRkU{8iApbLFM+%jn+oIPeT|_2{+NtYZ(+`zKRm$X&n|IJth0X=NBL9msTz z-!}seDOCD3YJfPgV=+rp6_bA-ykmY8S9rAd%7Z7*_;328TVvXbii>2-BsjshO_)A< zYY%3zp@!3Beewt{kNAF!NUr6$Ywe_xM@mp0eG!^N?2ZErB5NdlA z2jmWzxsQ}zy6tNZfri`$SMvBd{lnayTn=)xKB`~-o&|^U2DicR<(TiR>*+0uCwgWF zWk>Fh3uJBE=@qfd#O>fiw6TYHo<0xwnOP;BH%$DGx>vg&wN=AUWw}AvEsDb%tzkO5 zcwo0au$NgVlSw0j?{&$4l@LQ>fFF6P^^xiKGcs}K7gN>CO;~kT20NBu1>f2mt;17JU$JqCyw0?x`1~u z)uQ|%ZQYdIeTF9fniGi{n#mcSn?J68A+vSp^7ieIeV4#191(e?a`^cO2pMw=HEb<*$P~d~fdttn6NW~-+8h0#$GVw? zXcsLKlQtj;O2s&iAxKj)#nRNfqgrbw8j7?$F$Q;IA~#Ye-8WB3h$E%Jw~)r|1gvY<4;`!H0m@duO*d<$E=@nf>v z{}}Lsfyhb914@TPCnlPZf-3K=XB!7wh}82sMbw&jE*b2$S4@WvU(yY?!R&VBUzZCXBV^#XU}P#|q|*V_+so(*)uf7+RUgP+m; z9pEW<)r;`1iRk?@4{oT5giB@pt~v>}5rE%rD>zg~`|pwQpH+LvrYIB@+Yg>i-A@jOP&!J2*(!&Em?FKd#21lk3)wPw7c#A0 z%yh4CHTnQI`x=sAMH`K7B_k~_F&g357nOC$4^5w2P5~#qewC+N;K;6ed$wK`gs~;G z>Mxk=Xb9Ne^RD#tUv~K5FzmgnwT3MdN2YH;&{h5X%_dc^d4CFfrmk#~w%)`@F zyz)b2LXItHo4Ar9Js;E)oNO71d>XFFd+_OoIj&8GF-lYmEP}Q^*U9=xV&)Ty=A-4P zrinOmnW$Qv1OHg0V=PAFRT0|{UbwA|EOC?Z$_}_3^ z+_CiP>cmG7-RIMCEm46C!@pU&6>_WxZ~GjqK*AIJuh6{=MC+ht$f&y@J6|?2^qm~> zXE#NM#A!_6>fALY0CyC5ZXe{dOxyIKi)|N;(mD!=K0W4+NK|0>+>nq%q9`t>jn>ChYJuSe+``8Wy<0O5p}K z^NoPLpE2u8sNUj4168`uDcapZLtNfrpk%XAKImpCe0~u0FG-WcO?)5d_WdV6${tP# zJRasMxKZ#>y2ZChyD91-tL2?}hjFYUqo4(2@_~i^-&#Bx~&_~>ZfU6TTEduFDm5&F-F4~6^^F0Cc z5)9If8VvhP)Nhaelc5j80oo!{wLcUT&hB^xgy_#@0Bqy;(m>j@lW;reY?kld_M4s& zyOCxdVMzjGnu|HHtJ8CIru<7wP-*bBjo+!DE`Ym?-fi*ZnX!sAJQRQrVDez1@r#%>;*&%yq@ay zM*e|zlG}Wg)CRxe_n+eO%}v1GOos%$uh=m*e2 z*@0Xfq^C_U{}#nPPlgUsMCsQK#NlaXuThcF0P>m7?t?YG3nmH6|Dr;j_Rv|%1rmnc zz(@miOZp@&(BZzDDUCDi|I#*>8np+46M9Yw>f3CaPx~oW3N~Krp3c{ zXI*cDwM6a3AnC@Igt9zATv|J(31J^Lymiy_gAQn%qx(k7zZLY|1wF1G!d%uMZk1e; z%>s9urRCC+%4f%A1qKqpB|3t)4$Lrgta|Bw^D+1;XfyTd#1lYMNrAZ`7h6|+1IaB+lQ zpbN8LsRs?*Ao{Kqou~Bgs8K=iS$mH=kB_u9LZmkld-mUmT&v3=#(bWM)+KQR$5Yc7 zp%8_TVJmE z2GsP)=|40vEa9T^GQ{92386Ke4?zb*SOwl!Bb9Mcr*q=|$LX1~qhIXAU5u9RDINyd zVItrl6GqorY)~I~Uq2Kun*kM^2i*^YwGqZ>K?tH{R|MK!qK2!0-%@hYWy0rK-*I!w zM0{$|WqT#$hNSuLqoF*bgGvS>2I?n}{a1@5XdO{vH_o zxpa!EDEGxw4F%aT!1u(Yb_eZ!2FPZgV+Au7ejjc=7YnvC7^vtHW)FAO;XuNmn~rdn z*pLK*ZKlrfj|4tu$3pD5nt@Pq9z&7*6%k3hm595GznOYT$ne>aPpT4i?vN~DWShgq zx7Z5QR;s>WO`_lrgtq_F{2&Lk?~gXvNZ;u)j)M@y_9hK6v>02d3W$t*qy7CO z9QIG_)gtc$e{dl-?aIhG_h^CAYhh;t_V#LbU!q5fCh_Q0hv|lOjV5}>?dV}etgqyb zyJXX8`!3lfDAIFM^+6_3?%Gd-2Es+>wvC-9q2ncC`yZ9v3u|28g4*WI%ao$?U-?sB zbi(mHk9xg@k%~woLZ+7e&7Xenu9r;{t^>aIs!jj*L?fBLG$X(RoFY*o+yXPgt!-HF zZ_ooP;g=9~3fD%2+X|QaE6btn3{#i+UKID_i@JF^-V5CPQQn`ziknjuKQCPOX16JDT}w*qf=t>A|JdAJ zkSV|O)I`L7Knog4olmX7puN!Uc2Y(0AA%Bo`#boF?}UY`))H9@c+S0V2~_(JztX-n zFwAVJy!et)*5tx}5Ry%MmeK#Q9bfjd;& z<_e9jHRnYHV`meU)US61lVFJAiknWfr*YD!*%snWHFd74TlshWC}a_-u{5`s)c$V8 zbb=+&tq}^&lp-sU{(4?H7ren&agJ{0^bBKyUX$Rb?S?mN%>VXb@(Jk>x!3Br%rP!< zM-(^91SZOSikNWzQ9+Ticsf$(Ku9OE6VordWn<_(v)Ybe?c%j0rZn3Lt`x484crye z`BaJ?Wp@sw8m)`I`OC}nus_jVFY5F@JyGJU-h40VRHVMBST>sdB!{aFBw{zA`MBXz zcUlnJsR|qIlza9x@!)eARP42%N$NQZ_-ZO~Kc|l!h4J&$ik6rsnD?7>g5-7He585` z)nrJ2lG?fFdlq~K+1s(%hQ_MqBPx&d;bV-Fu+7DP0m%VqV1fPV0CT3H*>ss!o{@W= zQ*n)u67ZGDA0`>17^HMEW)2cG2WzXj*sv&fmbyZreSbGp7Hj>hRmOt1lP9vq^gVLH zfjiCnmZ%$JhvZu5$wJF-X|P$&P(+^E_1{J7=-mZ@8TKx6Hf>{uF}X44FZdgG4hx7U zrZ06EQq61AjQ&Kwi3JqZ>)-RBd^Untqm5of9&Zo)&EknP+PbDJJH9}f-R_58qjYiGw$oDJ-L#sI zr3lNw0)bhW--O(g=6dY9TFIYqZpvpiTj+*cCfIm?tYdz8l|)uv!0Bd?OVsS!>7LzL z)!bj3`(0<$pA}>4V=K#0uu)50$+-=uK9!=!4QZ*?Z0_Jsx}*Y%JLyC1_o+n4+TWMj z;rOI_rmOS!c&5K4TC&PIT1lVLz18#!Gb(?bd}M0MMIMoFh7>{00s)2_Tuo|8D$Wg>@P>+wyB*~4W|b3OEmqY_B)Z`xlZowk-wHVmv@w)Z=`qO--Vy0z{<8A}ij z#Z>qGhDo+?w#gV_tjW4AVIvB~s+MTUm6-XCgsBB?Zn>0BoK|)edXaP}=x~UrP?@-V zB^UTZTfjI+06GiwRXCT09XOGbO7IO^3ZG5&n5(1WC9UepLDh2m$JJ8U9~y}+FiTzO zi=@6Ihl%b5@`JOb-pCN(l?U1)hiBinRml(XYdJ-99}BHWLHt0+1JuvMp)P!n4z0uF zz2HK_@6VNg!fV7`-=D)mk?2De8R}n^R36pmeIrgk(D3KMZ+`6axxRc-BcIjUU9XsrU{6U; zMo&?=^ch{nGh3+76lQQoQk#}IWX*ro*Tv(hrnT!um>COc(XK;{l5Ws_t_Z~+ zmIB^@pRsy4-Ig!A6C^1=I4}m?k>96#CB`GC@nz7t^*cV!4Hra<%zS?%WaEbd7B?Qc%ZtSnQsI02J!N90r z(%52QfZUr~qd1W1nGr3cmd;Q81G%Yhn-C1L_Vp}SAnGUmLY>p)1o-go`xnPab>?T5 zeaezWH43rqyAZR~3qm*M7tLz=&wn($WcpRc8+7)G3D~yo z{adPFb)F@srf=41*7`+S62+TLY6~1R_CMX{YC3$*0sU>)X4ngL7^Odc`U}I9Bg_($ zdzaUC5@n*Ia`F6cbg~`XW=RE!gASP+pa{P9ftDU z1u>okj=21$42*hDltN1K9U)iyq}L)UhH;G1+oFmD|`ex^lEs}V?847RcPC5zbJ zpS|iN2l)Z}@>j()A)9`&hahlO>HtmGRiO=9u%Bw&EkN;~!~X%rOg&>BPe|u%##dLU z<1>dwO|Jd-ZEO516cqty^PiFm>aK*hi)=AkQ&vw5jm`6dE2^4YhjU7`hpRs{sgyDj z(moaQGggE@;#D@8*<>zN&PX2SpUxl9h-};w(c>p)QQT?D(fZ0}nz>ihnmTQ77D->7 zj>ve$dKnu{1hZ6;AG(SPRRB4Rqiy5My>A^yMJ< z(X+AiYniIwWY(+Ne36=g9w}r5danV?NJ;vOA1s4-!3r-4(&v*Zs=v4^diRDp3Od;0 zU%5I!U<1pq7R@{oCfS+)K!)o?S-JFBjU$87VAN2iqC~hUqv&9Z7aH&ofiB#0=`pDU zO-wUm*Jb*M`C0)vQ{6%tvX#?cDFHLpRnIaxpFu?=-Oy50Wq2iNfvZ+#A{sVadp!9t zS%PEB3cl1vn(_VZ?L9Mn>1s)b8m5Kh%6*K8%(^c)zcicw-lt@3cfhhob3<)2?OWly z<^Ru#dCHkFff!dgZC8l`HdpKk-&Fu^ak>1vgiU^rfP!bJj#V(!n~*fsu0vFKBMYU2 zjG!^T1k0xtV~$yEslKe(%Ih$reN!ciz-`+@e3|Ex#9Lv+ujhzklx6qTP!i*iIKEm9 zaL>N6w;;$kb;qpVU=bNb%0Np&tpcrbM5)?O{VidBbF{5=ME3K>@mEf8I3U9wJz#gu8YG3Yib0$q)VWCJ zZI)txMuWaH^|GlZQUV=I+Syxn$tkbBnC}mTu6gB3?2>AYDWJVgl?|=Ikzf(DTL&TP79k!wnjG_S zN5M%I^$y2fi{YlfDY7f*>7GT35#tFk1_8?vY}KKa6ji&x#Q_&d?yi~%yc71vGmg=W z_HUX~HX?7e0kN^tGXiZvv>Ho|U832INlZ&W(~){u9xRYNUyUw^)4pCx`xHO zj5**ekw+d3#VlZwK&!HB7#4Rkh`4N~o<$G&m5wd^hJ_kPd$&Y{k&H*8!(zR?X^v|oqx} za78H?dF1VdW1p8s*~(jEuoOr^tS644oMfBiMu1@Z|F*rZqalvl;)7wO&54onEK1DR z@r%8N_FjTlsgIA*Op*G;_{R!u`)3&K+l1K*LG_x(q2ssGxF7dt{5oGg+ZBWe&U{RU z5()(p{Rdw(hL>9+Lmh!PcC9ZFrnpZms9^W=2Gw%N)>MKfeHSx|SdczlTk_n%C$5MZ z@dr20RBAWqX+*5N7sw=aS}?0sEMKWJREqXz?ta0i^)2~`@BfdiI$p5zJ(mrcmufg# zDp3CwjDS_#`q%L339`~A<8jd9{A07vMjV8uYQWv3Dim!~;k7_y^^2|lzCek^!!y1y z@$#L6683Jv*_D#&MlyXMbka#VTWz&!w52vtsy4N1i&m{FilTN^QG4$ZifZlFDoX6w zd#}`}S+#eGO>B`Mk>vdId;ZV!=Df&@oa9{Bx$kS;pYN^-bjY*`kJ+8qWE;_J7vnts z-GecXlUEM>EK8eHga7TA3ZCs>DxM!*?fL9zX>w=J=;H&pxNW6m840X2dl||&=`2ci z>(~>8%K^x3do@R@$NZ#enluvGaIdu9y0NlV z|00qh_9ZMUAOp$CF$@*xt8CdS|eem!x>W3J`^>evsleupAU1J5AesiMwv z!dp?>A#T(Ry>hWB9yHBiT@Zp5JbAEaVbKqtxPHww7c%5DqtWBQUHNNt)o<1kk|h^z z43L_!QP3>G=2^>gCYmcs$$OwumR&)o?}XzPqWIE~&i3hoW7UbbnlZ6jlL=Nw4JbMr zTNx8CO@ZemfhcL8)#5ur0rE%eyq~J`h`F(5KJ(l~^8%0J)mRH$IT#7`_dTq4MUFnV zqU>IScf0TCYgTX=EPxM-wg(>nl~24yoxEgPRl9@^I46*j><~a3+1PHPF^VVyMDJZ?J<3qJoG-zrCcyf7oad}&kCL~ zHYsF1{3_*HChaEhk}Urz(xkyZYr3my1H1^Tgc+4-9a>cttbm81ty{6*>3KN6T~Jpbwcm6f;^?pSWbhMZIBS>41xWhiI|tZ`v#j&+Y@isB6FV6L~V$U8yEr8XqJekg-G5F`)hab zoV}={nFhz!-Jvow`2E*|U}x4Qx1SF?vzR@8mW{=IY+K3GwNU7jWFcyh1F!NhDZ#;q zby7tZk_uXu+#z1>zf+7pVoVk_@S*a@b*%wgOm?kA=km#}-0T5T(lgHBd^XslJ@ZJw z`I#|*zSm+c-y}dPfNf62p_;)U-vGhO<%FS)_eRl(aB`+>1U{yUh5%l z^rN1pbH)uioKZC6hdZ)5Qd&7A{8$(lP9+qVu=_7T@ZxCFw5(4XgyP|mjn{88@V&*; zn0i2S+21)p+elU<7J^D*T*Pkhh@AEsS}*<6Pc$yWv#Dg}J+Lt#&#sp*$gdpZlKYD3 z&#gTk4e_s34+qSM#~d;?7kq1K>2XlOJZ0c`pq(UlbU$0hdsD|_VKkSAD#nAnyPGJ) z(vUPe(N9b1cT4ZO0Ww|>o}3B(^@ZF-t@5`3qn!QD6U=O>!l1`}_UAYLWh(>}T66@; zFdYE*F!gZEAV_g{t!e|5p=BNhuLaV)&5ePr>LJ|di|D^JJ(jT(g4V~mnWbNo9`j}9 z`8^B4hq5jc7Q-U$a>$qFV6A$ieFOl$&Me*d?%^gmA zzAAB9@wqUK1+kEAsStAV&^0#AM^RK`*y~rXbSG$L-?_hfgLq+2*|x${e;@Mq`$^Jl zU3L6xli(iU@Ob@x9S{CSQ|MEKl4CwacoUSc4vQ_YgNIo$M4x)m)sW2TmQ=jj>()TpTw%2*IZ`aqtO#dSSsh19!-Q zqh(C=oq%`$B8(+WwB8CxjZk@5!E1F{e=q zWk>IYDW#c`Hdx$J;1Vf0io?rn22~h#&1FUom!`byARKOhY_G1*sU0Vzxbf1R>v%@0 zWU?dR0)w9Hn@-VJo4A9__SWsO8Ou0UDGX@+-PA-l1&Tq@r<}8gX7>SRq|=EuKP}G1@6n;ZO&b@_#IV=W_x| z1Dv;S8-G|j2>~Ove*y$VW?)k0U5;@C7IDe4Ex&ENEfa{Jsjr|1In=j4N!%!WGR}4O zJbfrs`x|J9j&QDuc(N9Qc<$V0KdUGISBw`?!yRP434)$jZ}~M$FyNG@jqU7R+=B~2 zwGl*1ZETw!^jaQ6bXH3~r>X;eItIbdWIWMqQvK_A5c-%#`#K>ToiHTx$98bdrgk&! zjqv$759(v)O7ve%v%uY2Q4|9!28rmv+qrU!9V5A9Sp>1(y0%@OiCt;+H=|WS!BWv8)tt zt01y5<-70sLauui!c`Xy)nSjRj(tT)5v{|mPHVUmR;*#vq+K#0`1Ch@7_t(04hl&f zQvJC8@bq=oqtW{W??D}Go2`MP0}{HI_UV;ZG2pgrp%F4k3C($Cn1fc2_mKU}TGfEG6GNpo4F6)WwNhKP?vyLv3 zJefeS$>|~dRUE8ce;G$N@@|t`ir)W01!e=hK7{=_dKzcI6{LKSkp3lip^PE=s~4}0 znlFx35u3xj_VN4l_5JYS$kOJcBFMD6P@DsrNp|X1*$yq?sW|V7k;luir9>1tyJ~Er zOXQtP-yw7&ap@E!ARxWjk{kZad~RMES44j8HpXKJ@zy>Ov#g2Lj4IwQl+YNo(1E5k zpj~amuSmZD1i@<79>31Ih-_A5`&DYVJOgwBWzW=W?zr5e1OH>L=PXIY0qe8_NHAIm z%s{Bp39(%ig#Qr1cK(y8P7vpOWtmnD{?#@Q*(w{Lk+&dAR%@ClH~9t$KhXKGQ;tdR zoT!G&{)<$k!Jz8C-x>~05XL6>PJqk%0^a4>_o|;Ut%F+YU%$9fNrO%Jf}lDMcZ z8mbmuINv=Jb*iX<4EVSLp><)6>WX~qhR@maS03E08&dYg5hOJgRWUELh(CRnhT*>^ zG$7WymN2-X0Y;M0G}*Z&YLa{0p31Bysrp0w*I{w7EM?cV?2|em6yMPk*NIarRH6Fvaei zw7e=i=o@PhfoS9XInhU{F9WVu`@gueTAnx-v%>tKDk5UQy@BUiJ_{ip1A%8+p%O+xQ(EN_>{kAo?XyXhUj>x{%_H0p+|EZ=TcFNGY`rF)B@-oZ z(m}7B!@Q!;BGWeQK3AYD4TuAGOHpZ0RisfW6pjQSlb$J=nfv0Hub+@Ae@~eLIHt@Q!+dfx)>Gzel`qWAt~%VuOyAo%iAW`r3JvKE z@JIhfs!poCj0liHRY4U^>|wNju3V*Ssh?b(g8I=Q@*v&H&|`X>-PM-mchmbmyP$!) z;QFfBjI!LYYiPD>y1x-0kRm}{N+j0wh$GFJZ#1>%yAPg?ok`N(rP`04#@_Jw8CM7I zoP>~!r5N>M$>UT=_P~J6Wf1&&cwyNu;OE5!j01k3AXx_=c}S4*#k#UIn6|n|BjAT# zR04*u&mtkx4I@9Z6m+&i^R&#ajwJskf*+vF|xT65F1FQF!1eYiCClOt*ZzT zS^yW~vHC@$y?3HOsFtQO$fYTyGWw>dhJFg%D)VipRzoSoq7IiP@g*eAqr&j!n7P9b zSxg9KcFKMI1}Kgb$$aGX34P?-zsU!eqX_nq>Pt7B2!uoUEtE=dFn< zT)9PeK}-F%8hSJjZbvmFg`Q}#%JK7ZP7*k{DYoW%?&{AHrekT3%L#8@cvv(qyIB1JApWbRR5ybJS-}rAH z-DRE}d#vCuv6uHeD9txGiO)Mdsk&#?L+ZXGb+U?R%j`xme5F5^<0z}@iF?R?kE%Su zH|X67$Vx6b`d~M(L8Rh0xt`nWo0rTQ0^he&s^KFV#90&I0ioJ|f28s+5vCQW1oIz7 zDehP^D1I{$ZsUE2Y6$*v78{I>Dsg`$Q>}J6bJ)TsNraDb+xG66Uq;lF5b6*wsd`TN zdNVb!$~btjxUdVH;8PZ=6Xb5$h_0hx&c0#W@-QMi?Cw z-+}LOhtmORMsR>@y?UUOK$4Vyntvujc)4)>XH@P{HnI{kkiqw6G^X?EjW5J?9Vzx8 zZ?fzw=PBL^;jQ(L6ar+flDqikd2TC@4|AUG>a>*$t4_+EV-VynRGUh}*N?P(gVH+*Tb+@GXog zNN+FS)t}?GTPHQBtLai~^!sg$@**`Z;7eEham9Gmv#vMq#0mdi+tNsR{bD7C)t)j- zkYrImsJ~ODy=v7u#`Eh3`WjnZO(QXEfe~xzNQ%+8rpwS(`w(Rmv`J&G^EWt`a)q`SFF&c}DvOFp&LJ;*PSZkTF5rcg|IBsuda`6uo3?a8 zXrU|Ry%iK$VZ7O&Ps(%d?|)R@WYEmND&EZ;yqvdDG^y47BJV}N*gJ)uV)2{unj(*L zXv}P>C_MtwqV9l~S47sio;&L(iJ&_oisKa-yKYmTOGN&C@QRJ_%TcLkW>Um`#6|=i zIZvl#Hcn%nFS0LURtO9cOYu!M$>7>4SHRk#b2a0q@A{15OQ_sJ%nTFY6otg9FR*0tdi#o&ZAC zTo&y5of^>LMEw0*C{zOGexhEHf3Gh}+tKE=Do`Z0)bZ1_g1tUnki}z)}KeezJV@MYk?K~I;i7-$=-A)Om4fHv=1$wZRhzw${jOaE<==fRPQ$4(^TYE5$= zqxjN0YLlXF71a3)aKKb3*@wT%pZv;ebalWR_xxzko0)v)+F)MylwsMD9V1-Te$^0! zj$im$Qvr&nq42CK_^Bkq!nd>~y7BD9T5lbW=ar;fqnvX4FQ1%&052S}eIO^%ZKY62 zJHWs}9{Wc~huDfEx60cfeUaCyNxY^xfrYz6xfEAb3W(C$WK>5B)#A%g@S*mPFgZT1 zXTjdW+j^TMnRzb3rHqmxxKJ}bAB=PjEz?;T(m31=nr)r*ie0)sfHd1X{%&yVHTly| zVpm4k!MpdJqn=RXkNMdwEMg|mPVer;s9Fz31eE2|MCX9qvVSl0;=Jkwa4E!?FvByW zO)y19Dc*m2*z)E}Fm7|I5xDxHJHzoOA;{sfyQRb&aUm3+G-66zH7z-a(2p_tXFy(W zb{sc#LJ%oa)SK54?%y}s?`vjge4 zNvQgP&cDowXZ^@i1tDPuGpUx?>n{p?fH#;b)b~@v zX=2F&NVih&lao6}Cz<%Zoga9uVn9r@RdL}`e;Be5L8zKz7PQfi*h;r)`1GPr9BlP1 z&n~TpYUeD5D&@zo(^1R8(2#FU(T^0ieod{++XsH3Pzcr+CqBXNPsVkQlMg9Qj6Sjy z$5l1p9|SL2rx2@jiDmm$wcyf7#bjV)_YnlQSK^$OmCrsfTLdeDHA0BPQ#H2U48WE? zB7K2)He8Z#iHucqOG?d_3OY0|u^yM76-psq76WR4m)KUh;u2k%p9{p!1Pg~MG z42bQYH(fR1$!MnPMvcX`PTsU4HFk{(04FNM<|<&(DFyO-vwN|9RB)C{)^;KF_h)8? zqNkz5Y{|C5EZ6{RISnx&ST;aP&n%~j`TDvTuoHO;)Bpo7X50KI|Bk&%9T0bT2bvLL z&WIwdoMK;CT|G!TI%QV9M-qS0E5>^!Cu~{o-%ZuUkGrpZxMr*PefCT2mABoSqf~ax zj+5too>94<)0Dv%w_9)Mx}ufeHJ9vYlKjj$Cxu{ubzL8Zm*SBXc-J|Hy{VIyQ(DAo zr7vrC<5n4jD}VTb9<@_Oy`#g$d5qp-gga)X#Og$|1bYXh?tH$?tC#C4RgwHrH?Wvv zPoJQl0`NBe&&%cjyg7iD64dP9V!)+mt}cpBZvdl(VewI_`e*+hyTe#|qMzS{WE$JU zHqPJPexCn={qtSA1R3@()_ndKOnR#~S0f>EvIo(Q5pRxgUSQdD1Y|CGWqhAWi?4u9wx#hfKw=d{I_M{GJrDtIqWS_f|4m! zw)2=UrC58KWHohLtLa}NDXGwr_i_J|*o5zq(%(Db@jbEQHaw*P1ibMp2-Hh%{)AF| z$q=*@uPf{p7~N=ebWl}_C+G`8_+G?#zd^P29dnjZ?Lpog0HwgBDUifUPcSxI8$a@f zwbA_^Zxj0Mapkm+*MdRqRg=BfxV0T6zw|Qi7FQ_Gm(_uPk&^9UG1S2kvpeJ_S7M#l zk>m~0A%zg9l3LP}GT78{`Q)m)>Dp8Mt%Puj&}3WmTB=(L8ZodRN$7kzbFe!l>x5m| z)?btXoy|YQgpC*hkG=7WYI-Kv@BjVms$E1WLH9qer?L6)-vb95Ty9Uh0Es7Wh_A1g zD7+`=WCg&R-wl~owWjHKr|5jXy2uML3ul%)VV8~u>-Vbmy-r^Z;!Zm)f~a4AbzyUX zA9=EVUAaiP{1=<~h3fhZ3jXoG(NY|nD{|~XLTFB(F&^O>wyJCf|G9)f5j0s_?zP4h zWI?LT9bvIP+2SvzI}8?oB)sm01(KqSH;;CbA3k{iyyN)8`#hRPLN=GP{NfF|hn}A> zY*O9!U#Jnq*50?NbNQ301bOF3i%Qc-MKral!c|thp+M6;Da3AE-i4^ z2e-MGm`628QnCmErFa1w#SGq211v+4ZVIIK%LoSmpV`z=f!AB|J#;I)VcY=LWVlhNNDry!;A@!lTzAwDS<8Eak**`Rryi405g z*V~iWxuCJ}l69yT$6{h--A3H#h=vx#*KpVGOfgEaT=R0T3QDM{s75oXsPmUb`PP6F#P2nUSQRHfhz%Na{{RqNOCTG}=Aerajd<`M{ zmU=xzwE>V|%ZkJs|3g!(={%Ufdq$W7k`XSlw#ORP+3QX zR^2i>GbW#q%jb4pNvCxBD1-OpA9l$Je|tPjTKM*xTibRBR(hTB)jLmG6WTp}?d~2a zJ^1)EBZNnUMaMGMNrIsD5V$!u`ciqD{Z%!J@O~+e41XewQyXD_U4jI^5qAB~X>rA) zS#NHixA29)ng`XK(PM(v82TZ#c~4Cs zTB?PC+uNz{fyOT{iQl&%PDq5be^dR|5mCG_#JmhvbOhL$coM2aaScCpo&rYNPZ4d7 zbES<__-VN2ff2yESG5lx0vf{`7C{DBBT~{RZh!ISfM&M3UTmuN_*4M#pZlf&MCntI zH&x4*E|YB3t0s&6XTsL-2AB5y@Z!AVfhQNmq6OM_v!prJ=3Fr=nGt$I&(_us2Az{` z3!eFI57M>%siem#shYj}!>?F$l!>PZ_GiAi{Dn-;qV|e3SdCJUI!*1~@?E-0rpIn% zEX~a6%Tyl|DdewoB?i_N>Cx!xfU1{wlpTZ7TrLBaJ2ar8beD(3zYK`7ou4iY^0@3x z*KHRj{FPicZ!z9p(N$4)u~#iJRLWvkh-;?|(h=#0vw9&vigyIYpDdUt?$&=ZS|IWh zjLyy-w%}*vE;=JXk*iOW-fJ6}N6W!F@FT<0_xTAjV3x?It>D^u=4(#C%mhpzOI8)S zw*xc_H)Mx^>>(;36@`L8cv8ct2treyjp--|K0mtG; z(zjh|PBy~a$gaQXvhFYqag+YMqVz}noJs>=RqM_9ooSB>EgO#tIIiJF!oOJQCMI-I z^p?&;xFOmYenJh66B!xb=N`|uuWSH?4WO@sTEkJ1D1r-N3>eaWU^F?~`NQO>rR2h^ zqil2BW0^s3Zyq!>bV%t!%(5K>2-YTzot*Ll1Wg*=Tz*Iyu0Q=*V@~%V2Etds$)d6s zR$WRw3cO@sLy7Et4=3}BXUr)Pb-n_&Cg5BE!yOXod?L4_axZo8)m z?jz%w91ELNU!o3OZkICL(M-Qf^XG}F-zeQJ`Va5KKALwuzruDm%6CoWn_pPwrx=Ct z&o!>U{Gk$VzdrbpF)epJ~mUc6uG#Y0}plAp(9@3etp$XM>d1U;bVj=u3C8I7%2fjlj59mqosF%P*cckbfn^~Coubq^7 zi4QPhc<2-7=h&{>BhiXqjgatKI5-YJ>$7*HH!Ou7ae1ICIXjWOpm;hxRUYr}T8a6) zr@ubiSxus2EhZ!+jYP%oH{cWgJ>ZUud6AZ8fF%{v3<&zuq>E;{=`Z3aSjr*z-iLGA0*+AU%fdlBvRAT1akc-kCId6W# zc_F@J|0p3B?~>R;kR)*rQPkLfm~hTsIYJIflI=tvH0V5qipps} zg&cl0qa4zvgHJ))o!=5p`BQ+k8h${X59OT#m?u~_%{f-i#;^gpjS&9JDimazSVl^# zi}6P9bpIW%YcTTD`7na9`qzh!+{{i6c5rq??V(tSBK_b?2gET#ZRGm$H)7 zD7?ewDB`!~V8^7AyTXs=^2$ff-sFjWHt@o3?@#8ni16PLLMTnm<352>Rz^zok#3nzKH|I6`wTqS12d>#t_Agn%Suxmm7FdhwSS zphcw>Z9wGKq=!j5&J={0V(LWCih%5m{_hsTg!`aNQ&KS_fw-Ok{uXkYi71fq+%C{a zdR+9_(DI$9HxlUn#4tfdBmd*Db^8#Vb6CcGKId-T`+N=<&V|?D5N}YfU(wKj-4d_u zCEp_CBn)=oGiPC6A)n>Uvhd>Mj$T%FQZ*u_ta<2A0*+Pre~UnRyZ_>v0_oO z-rn!H5dk|3rq+n-c|(7;!fc@G!y2Nu+kEsm7zi|`%Cp+cn95b2KlHb#UukXWQR5Y#JdJ@r0@bMGRWO7m<~%r_j;kBUeyV z^wG5AJ{EGa_8Zxk>GE>L6?=4F0Q%bUgPKIHs znz!{bsr?x8i~rFE!%fkt9PV2Ku5laaftPiVQ4+dlXHbmK;sq5J6o{^oI0&=1?~7%! z$BIb?-H`Cg%(V~n4x)w-R_&*DK0fJ@`sDN%rxp?d$6=}_QJce)v37O^&OEQwFL$Os zOympzf9ghy{u_8?JD8rKbkE4loD8`<_XS&PWO3HZzBr%M^x{FOn?q#*x`0v z?A|wE7EX+76l+iXOuh7&FrT^nf$iD2Pi`}dCY1UQThr3v3t4pHY+>p{LM+Rl;BlZZ z%{-MJ-Ygx*D8t!t5(i6qkLw7+igchS>feUzir!-KU>0#|aVm5Fag&NUc^Q}5L>Di0 zo51WdsXRH)?}#4PK5pp5BPzrH5Z#7_wp=l#2KKY&z9S~r`f-zRWzA}rmQ9*e$3j^i zeOAk<$n*YF(@A1aAB>iMg!;pZX^kvNKHkOwfE^X}9r%=EXTcJ2j7bY{6u1O~0AwZ0 zPE5>_s6Wc>v;tcC3<}VRO3USP6oM;3F{5Y+Jc5MaPx`-Zjsaeh>aLww+dT#uhch)R zP6iD|zG&}G;_)mj%XguLmOT`+0Z&UJ7UJ(H{#m+N2oL?e*SCn+dI=$Zx`B*eJW{7o zKV(9#z;l{|$o>E6n#Lw9wg2#(OhW5l5vYjO5 zHUAoM0V6+7%vy=9j#>ao;aNNBu@;4e#pB>zW4Z!KT*=IJfN3c-QAHILw?SZu3-(Yu=#-XcWewQ4-~Z!YkpuqM=26|mmj>LF#GK)>m8>n zdc7d&CS~thiEF=d#tS| z=v67}?WO`_EfwG+T@ zWwQU|uIPFy#WOYqn>2b^E!Z5*!Zh0060Ncw=iK3II5bb$R3LY75p3)zO*r^NLQ?%fs|ED{FOWW;WGvK24J_L zzUxdE1R7}$t!xSVm~YUjLr0uzED@S@;$(8s-CaNOG$zKdLd|q}SRJTdsRlz!R_J0R zdZ*g9fQEykdAn@gZ9jVx;sk0SQ`@U+Fcm*Az4MFy0>=p^lh8~PEK&LWbafiF73fcz zp{!o9=pmA8h?}6IzM92p{2qvzobpv1 z6!0-Qb_O6z&R6TzQ9Z^C*Yr# z#+xJ9WDxdRC*rWEnO0?;8*qpqeKJZ|fp}m?z z$Zs{$3m}}s!f9k4W%;drCGHLne}m}n0$BgkiIEBJrwst{QX{PthUWSjL%D>3^C9y^ z_R8m4c9xaqG2Qt3X+zVpq=H(3z3If8^mk9ZxPbqpji{NWe4G}1;e@Q;fdUY^9y&`Z zLjELuioLymvak^0GIw4WEi$PjBCruU=TJtp@6Yminbd&#?XO?^cQW)^V?3$TOPDP9 zn)K+JwCtvD08`a;;(H6*lcoZ`w*Buu2zY}(Rv#G#sYWbZ|i2(G+t{cI< zx3z3Kc5MBdX|MbX*morj-%zB&^WZjnU`oT8f`~^nll^+IHb!4Yoed*OL9;iA)}cF= z6ciLDo%6{GR4*wdzhz{3umVN125EZ^*FQIeN|8k%$T zj<3Ul#BBF>ybj3~6EiXzlIOM63*f(F=wVUvdowvqz`+zUyLo=mvI8Hi0mL;xVmbCl zTBb%bISF#o>en6y_4sgs=Mw58c^h5cKQc7Jg%~vtQ;IjcTppF-3*E+-k zcG+~DDZguy#xVmuv1{QpG$Oz^gmJdc&>|8_^aC;b6W72$3vfypuyR zp3(I52$h2?*;KYvcWlf`&Jd9Y{ht1m8CiCK-PY^r2ZX%Qi_AASTPKcU@V)$~Dovp8 zUpRiuJKz)V-yw%fhE$;n;$9h% zp`)2sp_s`AHAuavrB2JEwO>cbMJ%HrmdIIjl~12@6CxwT7c*_8(>?NMVL9-y!fR2L z%ul2!UEHqFxXw;?>9BqMOyUUJg3LM!GP2mhF)u*i?D2Pe$bjB&QTj}%Bft3sQ9BT6+Y32*pF4kg@UF4weN^BM~ z(*6@0fAI+AOH(ZCGCzJRm@B4Ye%Meo<+_q)i@=G$~f>#CRSvI)g zi=FQK>!>VxRO97D&=nJX#J5?DllpH1)~&%4_a6kVwnT6&9qTo(@}C#WlT@Lc-ib1M zNGW<81(&e?;TVluV%XbCJKU9-`;*^TC3n&U&|=Rg84kbiQZ4NS4nf$^xi{2Qh&hO_ z2})z+v)ER49Gm}SzB@fO@*zd*FRPyuH_$B8_RBtEY%Z)1HP~Dv**8O}q0RhZ4Y1Za zaq76+a)()maLh}HX1p<4FY)GNbXw$TMJ6#Y3Fs_2&k%$ChOaxj>c~0P-*fpAa=b^a z#b6oa9$DTpkAal zjqJu_MFuiM3v9yM3XA$|>n5@~n^e?u=b)3*9(-H$9AJ+;O(WI&bn~@ecLpc&8Nsx? z28=~M=4r$SG=A&kDm4&)moEfw7*w+rTxL0W{(iGE^iPaKzm%eBipOk9LG3w&OA&WFK~wiv0X zNCJxzdOV!`;gfO~R00Sr9LG|%nad%MPMm6WuyN?@^qgsRAOAi@Y&kDmC*)5xQw7!s z`Ut_GStkA3W3YarsJ+Zfs$z{e#2Lg-=OvV&`f}mZWLAi+)ZW*RYer5k(P$AAOXeOl z7IFPWK_!t-R`Iaedh#%^`olzO-mM)$Gx@6})l_4UTzE?w{NZLR`Y56X|L8vMR>!7e z6Mohl$b3$W6cK(kYY~{`&*6AbpgnE6jM^@N#Mnv1TBmoU2oi#sk?w{Igq{*+YD)P$se?O@m3%S{ z?~SORZjDYmNs%>8P>DI`ov9V`SXxG+wx>0K1`J5Cn9aCH+S^-irV_%5s(YVV;ZC0& zU~;oiTgp(gPesO9^6;M8)x>bFFATqZQY)GA+Y6MQv)U@q&YFx+@pjV*grbY>?upFp zB5B2;wYG{^IS>Ar2z{2PDv~3dzCy6Fq{4m}$KEvK3AW371+w(C!uk)!rDfnzd*3>_ zRDa(Nez(u;Znp)xx z;15JP3gUwu9j16seue#cA4zz4e=fD^ZR&!lH*@Vx@|zB$PQEySH26lxe5@D^tMr(e z8{^-h=`sU$siLlM^7#b9qJV|Ts<8`N#e}3jS-_n!&art@$&@-I)jpjU`5q@CG>KY! z`%>ZEUWLA(NS|KlIlznMaL^zF9D5j;ES)tB)R+mQa_~GqU`@ITszZXn%Sd(~Z z*Dkz-*T7TzWNH}mlXn6=N+4Q6q-&-G^N%!oC6i&uH=6N>y-p+mvxgl{#Pa0(j*$9r zs?f9t5b_ej_C@l2!Df{X#E7x4H3|tLY$Fbu#U%Oe-Q6bbYuDZ2~*^4YmAuFvN8wh0#Weur9XP($}1uf}clxlq12$F$UiTm3z* z&$P@FQ-xqfNx3UZ@}!1Hp@FEt^!V1;^Rq@6O^7zz(Ss7~OlD}kqgk~&+qS2Ee;Op2 zIXSzdWh*>E%bC;x+O<^NHAY>Vm9{oCAq@H!+yLQV5)Oc}`C~eq?PF=`r=BE-nI;AL~JcfoK$sMtw?HeC&O}obscaaR2cQz>m(|`g} zPXu#9Crv@{ku*t*2Ist;ej)R@PSS=j`LvqtjnuRM5y2O{>GBRg{Tsv z`EDgpV{ccmNJ+T`zp0AvPINur=F!Y&{kBm2Q7q#eFEH;=<26{@>oK`nswUlZl{LLX z7;RKxhJ0XBsl#jfn_eK(b|fP!gC%o9xa}~nIZp9zb%lAj?DP7#p&~GFDg`5*G-a|^ zAOxQ+nPUE4IanfmRhrhFN-YmQFJ!mORHTwVgwR>WH84Z82sc3Tt zL*vide!BHHu2w^UTXxu9qUd!BxMD{SGo_bO@tc^s$9B0A?3hBUB^Y;@3Z#_!(@!N@^&rdgo2~qL`Pajux`(`XI?CHkakO7OoDY9)D=f+c zUATeDzf`I({z|_uKFWGclexz;7)x{MB$t;po+Ar~;o;9QDeJSF@D$<6H=l3wU<7_v z881Cv5}Br#M}>Njj^Ot6(wB{dE}E2atB-E3*&*Mf>gSUWM2ze_Yn&RmsQv{cPPE#0 zZ0ZCPt5Vv6%OO8=yfU}{uwU#%+dzqf87;!t9;w? z{6KzauF!rU@uB^n9np+8dJ0P;#2lc~w?mw;O4Ed-j9h#J=L48jrET;BKPIB)tm6$7 zwA!xofBO}9&|%j~I9t>@7p*8@6vA)i2osctY}_;4R~&s8VLLDDSVQV zOi$MIP;nQYCsK$>BxWWN!@HcswXH|JD(T!`HT& zH7a8qazdTGpm)_y@n#$ybmf{O7nR>mxYPi}Uz#f)aA!MNAyhm!slT%C&`xMrqLrXF zhbf)+XHcZU2k6pf6-;D8B&HMl;>rQIt6F}MMpYDKb8D1h6vt@{>NxMKKZp-3t}c8} zV|RD3$tvs=Y$mK#hZ&dglww4H@? z6QK8)LPF4t+NjSK;Vcmt+>u5|ch{9**{f;h0mknE5vO!1WRb2<>*(@jL6VNlJQeT$ z5f7M*{r^^;AH zK_%?-+&F0=RLJOIk@NeSA7qchLCMs@+A$Y@TRCfe_r;;aSRL}sYN_6WXU#Cxz^>uI zZ@^a`s5-dq!tUvprcR8IY-~iZF57~B6Gt$q8-O}>?`Pju9o3Rr{%KgE|HiZ1`(jnC zAU|IAC2stSWfQvparfuzyt4{~x+jvCB1qsnvxt$IyGlUBH^+yPRc=Dq{^H3X68pd( z!Q$b?2T9KP{6I$54L*GiJ!(A_mRc5U;zS$eUmSBKsH^RS!oL%_frnCzzsgKUh)C(+ z^~?;m2ZZ%_@Ju-KeqDa<-Ha&%rd>?;V3>HE1Eq>dBwjW3B=7by17UU>;Domn=Z`9$ zlEiFZMBx%kL!|pz}T-MvZ1OtfKpNE2yz!c&= zA`5Mt?LYm%2s?#%4x%2P3?pZMkfe;GPTFXn zIM40E#!)y}k}!Ou+ zsh@el`3~v~*chUbLuee+P*j7#O}Q6OUOY_DN*CkK3c<0Y*fDL;?z(J(!bRb@X^}}M z? zbWQmv#CO@-ZePK$eaWkOc5(TPIt?;MqE>lq$~4{>_md1X!!(r{p0?|fzo8mqUp$T; zdk$i0JzH?}((d8h`;1n5d~@S$mVMDWK_8o|9XV-frK1h4q~1}P8O}5iKIrJTkiCq4q40~Y5@O%kj1yB433Sx zx;PG&WphrGleFCx-av`vt+*K}xIH#O*Dk-XgnP;FRO@?|2eae-$4sQ4n>9OLdQH8b zXNi}girC?$@g(3LfyK357m$)+$4ot&DfA4#`TuA-%eW@ow~G@JDvf|3FpwXiq=Ymh zq*GBkRX|Ewkk}{zrKJ%VNJ}?TqoiSU3Zr2(IBNOq|KfSet9|&~yLVsLIp=#sO^XC; zTBoB;zT|a_V4mK5N)wh9MJ7&c#l!Tf(WQ?2=8}g&I`EEo=VCH_5W+WS6+sV z=+Gf4WeOL5!+*(e`~!6In>C!5{peNu=27SM8e-en(7>|E&EXb&kpiLoSVAGAge^DR zX{5Sk;te@%hx^Nivl8YD)XdsrqYFSI2m=Hf_@p&c9f2`X^)zUT&ul!@$f?d(f zJrtpLBc)2x?v)4FRWj*3GyT0sRlUo_wswiDqB(tAzS=Urn$9n}#Pco7?WRl^BhA#? z{d2KzAB{4?$q=gT*&y3Vc(OMhL zt$xR+dE1FJoOiaxv4OXTGw=%@mi4IPqSO7=9Zg~;tlu&`Js+?2@klhPSJ!oz91*z} z{ySxTNv2n;7sj*q(BKZ6NPV;(bst~zrtCY%2a%efadL_WW6)&>hH2&8{$JGvIpEOAjtef&|S28{0;p zt5++NAyY6A8j0nlW95vwaHa8~F`C|>$B4BqcY&K}$>!DahF&WmM$_*JmmcHTp?_hEBmSGRE7X>fjgB_J3zh`ag02kNTmSRZh)Mo~nqPfQws2d7>8J2fKI z@MtJhYgE$G4D$_Bfn3VA^uR_OowDqf40#YV896%NJZ2%gtp9=wbXSqRTn`CMxax?m zcfIxSkOr#$Dspc^FEe6hqB_|dk|x9(>BYofC!~fT|K6D zP9OB|E<4iacE(~PIr<%4!9LJ+Qji6NYJdpD?^deYet2P`P&RWT;fFyODbJ$#_3(zk zf4*aZPa}Ezas9>|A-=k(Gbh}ClPhrC6|(CZD%yz;Io0KnT(LkVViH~K)~9umX`~;* z2I4WiidHhA`Xl_wwKB6b@XhMf{k;H=CvM4eVg+}ryeH#MnhwgszJz^C zk^0aoLI{>E_Lr7f;uY$yR}SX>wR*Xc9qNHM6@aG4G5-oVdP-@gTn4zjQ+JGKIDh%P z{C^gJ{{;S{1rJsgI*b3P`C*<3cf-SZh5gygn2&zGM24_M8p_zd&0kf$c@ff7Tm<&r zej(LQVI5N#)B|6|@kXsD3GMqQ13aQhdqjt$oB~5J(rhvK0WersZlU|jwtpGX2TjZ- z*jW=}*oIAzk8Gx?d3a&~AnBZruF!+j%v+U+`iN=W(K)oWPsO`iXorpCE;2Hn=zXGR z4HUl3GW%`q#8E33+x-gn5-*lCxK8!A+gKZ~`=$kd77lQc(sJI@fo9=E!Pp9L;v-p51Ut|IxIolM#d~TF<8A6z{N}s?GeEf; zN(ge8HzDCUNJx;gfnX)^+S`UF5=`&O$<6_yvc?(*PC)hVUV*Thu=_flLq-bk3fkTO zdsp8h)2yW<{hZC56NjN9=Rc=7?=JMzTCBx9AhJe8zodAGB!-=5T7;`|5tk!Y7K4fn zH;M068K5>+v;jQQHa$#8ZeTKx&F`xhkRe5a>|!fP($e%_f5(%&v-Up)rk;I0J^USt z@oS&zd%0@@&OB}sd>7)OTCukIh2@Q}7Gv)_mbQD`ILK`_UJr@px1RbfKCv_W=V)N) zZ}rdTcANFZhcl12;YKJ@r{6Xum<5y%<9E#=K9|Tjw^;A7;7cy(O(;%Ljyk>AV(4=> z2why5s5s99fsF@?SQk#gf1~v7q^(DwN_Ldc$A>RZet7);^55KtQwisstHTFa;Q1`$ zPl0}W5tP!I@iPg?CD52>eZJgIMz&FMLd)r)$yiLybsvrVSi@0VHUW$3GJ9|E<+6N# zjI4P5*bhUs)8`Hg$fEQa+s`|11fFvgEUky|QXnMEC>*{#mYXJ^JF7Gi7Dee54C@UN z?>gl|9YLDeC!VR_k&18xB1w&A-e>-Y?zI*_IrGEeRnvyNcY_rv^t{lY^wmTE@u@3Q z7MgOB&^mL_0H@)m(JZ&NX0=8rkMqjCtK0VMF|D=kstP-obcemi`Q(X>|8_NVpcAHbG;5}PwzPYU9S?+q%b8DF1P}bjd&9Le_LLwIoilviKW)L|PRyt$r+>3%C;v8S05uHX*8yKO9=F4~5H+}qK9{q`(pZr$ ztqLaQgU~F;@jwt|S`jFlSju6_wwm`Kc(d&F1wT7jb=iLNg)}Jd`|+CBUhX_AdONd; zqnwni>TO&a*gjiYlr2l%HE6gCeN<|GOpjRF%bOBfUajA`RcgBZJ}RQ^)U}oSlTj|R zD@z4;MA3X^{DBZ6z7Cn0#;$N=uVInn&YMrAk{Vp6g@RUqU>T_p9A-8@?9bZ&{K4Vm zd|j7+W`iRfuj_d_4J^;x+AbI1+M-WAXw!t5u(s8tpKo$#6)sk7FF18e_QvvD+aZPI z*oLRBphDsX3EUA(>vbs*&RrVTZZsN^?3|kZ6X3hvqjbkPOzy97sll5iu~ky05B@FEA22+)7w9KGM( z>~s$G5zrpFzXH)fqV~D0{dHWalEa3VqbjLNm@T zNQX^j=6oNT{;((~Tr;*;Jx?(WEHtwR{7=-|KNF@qyD<>G^@&Dyn+uZI%dl5k+%7-+ z^6ipz^x(7Dz=lZT~im|5DLKlEcK} zpX&DGUvox=E^nsNcJVPh2{DE?2kuGD%nz^<{|r%T-a=Oe|Ck4UrJy@T@>X6`D2SrF zLDfcivRnF2W*|oa09dtI;L*m6@%6p}*%9gBg25u&7ZJlby?>JhZgU^Bz?Y@~FQ@nh zJLdhA@50%~@Bffj3;hti1YE!th=eXA1m&z84E?e2;;^}l*;|rp5XDhOg8c4>wAEaP zJ$C93A{NMS+NWD|W(Q#Mk2jr{Hnn(2wN^=>+ncVzwDTn^k zZh{8oDSc$Ahqm3cLD)mZ`*Aj)%R}4czX?hQ`;Dc`^oz`hYK>MCe}Rw}dn1B1l7@?N zJ3>oNbkWm}m_|msd$+dJS9E;x1N(N*XBN^hdnnBf808h={_^{4$03ljhBUo^>^#P+ z=n+krhJFQZzQkL34~xFGD(85Ts79kCdHncy%lz|irI0|rRnXpX{ZcU^EE>=#T=(k^ zdoGOvwev)dd37o6OlcqAaqGLhub|fwaH?<80nEX17c9}3EdD6~VHZESKT1}=k-WUt zok4mvz42wmU7p)I2rFe12xD;&VjW|631;MrGyUP(hH;R$V6E-z=Aaz2A{(=~V2M(1 znUvO{W5{@9K)ZTeL*PH)qh|4Bih(uzg?qJ7?~vn5i2JhpM-!LY(iK(>*okjaq?1xQ1RY{G^qO`6wA8`8r?( z`C0m3qf5@zVP~8f;9QvW`U=1}rG$lcoh5xm({~`fp*1vM)q8SqhtJ zc=6lADV+*6ztw|gsmQF(zBlNAVCNDcb{z^j`4fQtVT1=&Sg$S$ex4vfa0;cwrAPhI z=7;1gisEYJg2pbU&)6DMkfkyqqXZ&v3Cvn%Zz3y2CM#9vYv-*~uAd3jtC*mtYt~J^ z2|DLJ(VS()g>(@(o_#eSxvzozaeO)rHf6UDj-!$GH9zmU zFdTt$t3|OzMG44QG>&=ni}~>}l`aBs|8}8CeEZu@`=_1PHXl<*EcGOZY^m852VNOV z&CU`zobrKoAB7>qtNA}d)#U!UJE4S#ZrH14Fr`QJ6()p)x(R8eo}m)%MRtYxsu$xx zmPzuD*7XJ~qC6HCVWf{wbg3s4eJORaSn=xF|{_(zPdA_%nCs4Hz%S8{#eG{PoDnuk3U|=%IplLU;QbR^$-! zuuwDpP4tISB*FRta}v;-3+?tA%%jVFg24Y~m5?&i_+6^lY{3w8WqxY1NH?k({l#P0 z=B)63M;sHi#%%?EaUzo{&X_Gfx1t(VHNCF!kCkAo%cK(uEc60hwZq*l+J9SJ);x%y~9j{iHBWn*)XCf#`^?8WmW#hPN2@tXlVa9;~+`h?P+n^btqr-wmk zL=2%227cb7er?!kThEz6PCK=pebb8AwQNK}<}|1ge$gBUF#9Jz;rwnbRr0)W{Mz-{ zh4nJ0XD0cs!01=2eRLB4i<%4oYuK+*l$_3K{3D_X(koC!qY2u!5#+9R*&5{+^W|^&*^Z{{VR1(#B6; zM{t#ck*v5zZeU+Z*kyDKcd^Pb%f&|QVJZYO*v#u_c~^0>jE8ZwKZ2F|rmPUCt$8x@l!^b4Y?2hK{h*lhrNaC7KEAt9IpH)4I*X> zj%$cVLC!-McNYxxyz+*ayS6g<;6iR#M!E4RqsPeo#la<~(khB?%{L`_B@wr)1P>27 znWYq+>Xb0|>A`{gghRm0kF}yVa*J;(+`UZG?l(w!8h|v^dsUc%19xI^BR8dxywTz0mdpX$zL-+nFze#65mvHTe~nq14mE22qU8 zGyDmk8$8@z?!&>6*NrPUCOMlZW4bA+-;4XZ?f`q}s06!627xUJzOe@rQosCT zz>&?agnGle@O%4Sa*Dwh2`MwzU3f+6pN)a*Ct6FFL`y<6-+A#8D%}}x`A+})!P~}= zC|>%zSRH=RrP%ZYvKBl#j;Qp{ORIC8+&t@(Y;E~*_@85M9r4u>ps459>h)|-3hIku z=9?{E{GSbH?o;vU4za@0rcp=|Ff+WW=~(mvTtxkAsTQYsR;NT@cJ!4zx5W143I1AV zbTxXzHlV!G7iTm4up{NjN}#A#`J>XJmFUB2IDz98%Kg^*q1Ur!HXEZX8oU13Grt*% zyJnX)-@-Dgk$o^R>;3!HFl3r@U>X5hwV_8<&5JK7_UKgA5LczwG)dg6w1-9}8W!wL z$-R8>a69`F;Bj>iA6%urvDzrEWD_(C+z=}{Q`*N*Xsas%H5cZ?_`N4y@f;}b1bfE4Qg=^STgcz^xYv+H1#KS$oSR1@snJRk70f=@V7Wi)j&?n_HEU!rgr97P;*iI$w#=^gKl;8~B1LbE4V3;+ z)ZdA#%T@}aaE@D-&}UoAds&E~8g)_NGz8hk+AU%a81Hv1b-hX0a42=Ac!`0vBtv_xQqvIzXp%Df{-{ z-b!NMhjbA~Wz91jqPv3G??3fO#TE&vb|VjPpQIYH+CMjK0t8#Ka3+8v$JK0+K}Aa!Ri?#sIbrR6qdKsU=RYj7`a zl3~=5!gZi_4JT7JDMy-N*ZSZVo43Pn_nW*ab9}3Pw(_Z+vs|>9J^$i_Rmk_cMcV?P zYQ!D>9M7MKn(47C2aB~~|F|ijAaLz0%DCW{e+u*Ix|jnbt+we(w>P8?s&Y`v9y0dq zO07gToS*NRqiYb=1?{6}LL7uxz{TC1Kez#pcK1Mn+Lx1{a52;faoqoDMXt*qLPubL zj6hTYm>$Sxk3^gkik{Eomg#mQmK3I^?wwoNqe6bSZ3WI+ntKvvv5!_Z@{BM?(OPkD zC1+zD2Il$?_kAeMCkX|v2$oy2{D2-?e^ktdk@fC}T6*bM(UF`!v(Nf#BP0I-KmCBmeOl8_8?zbAOJ9t~ge*Z!}U40Z!d zBCaQiY#Vvs#xr& zOeiEW92X|aTBwwm%>X5iHTUBlvD^DJsg~`-J0f)LDL8`dmXibJpe(oMpa0nBP1-GW z5rp}=N%Dnp#VQl;S_p!l;O{|1h0~7d%0}gOq^Xj6GZBn; z0s9$9(E#cv?TD|SWVk@jPV_fidL{U~m|MgHen>+{$C?9A-1dOjdxb{2Z4c7!xCgOQ zb#LEGJ;Xd9cVW>`5Xgfw4H)jU!Sx3OzCB^ko**75%b+}b6ry>e0}S$;X3^LmRZAfY z-}#-_MS{|T?4PlKJ5I*rX|3lvSpkpVD}mxWKQ|XFbFz+ zeMB4A3j7)2o{6=owd*o0&nuWZm}7I`HwIKh@kk`U?lyF}LMsdV6~5Hj30pDO{nKr{ zEp4+cszEj<1eE*qNz4iA6A~y1!rNP< zL#Bq{kgJ*x^YrK48{|LeB7#=#{#^Qk&o@+aX=HmY;WXy^E-l5IwTEA6)O{?G&U06} zpPwi?G%i%SSDnbB<&d&~NJxP57c9NI}N)?tI{{qGsqfp?Zx z$q;np-4!*XhjNI2YfvzFOOwq2N#6XLi9nc5$=qn0<`=ggE%GqEkxnrKtXMT?=oBlx#ao>fxNm)#a8~6*3F4vCcze_GbF4eAlTU%U2;VgJSXuHAb znH_Tn>@%nt`j~{>Jyk1l(ec$bgcrT2er3P*AW@h&@s|xqC&J<=B8uORRiW7DwP*C5 zO9ZXNW?eLL5OVBP8A$TOP->yG9mj8&lX~(-7S?q^XjrFMR+1RDy|g=hPJyteT}2qq zy9hTwtMiUACcH8dKJ~MWFWyt&@Hu*BvkT)QIi}pUtildbJv7FhmVLV8`F5~AJLG|e zj%+9wQ9X`>56JKew|bd~`7+2raZ620J?NzD?z$8+@^+}7ci#!)R;+4D2yeyP5^d0py)_bMzmI%ULvZDK{SMD zgQp3#ZTQA}tc6pX0m!84Y{^8oL^p;Msz3qd`!5jZ9bGN}>+08G3ac{2bsw3jTyGh`DbElSdg1O)u?B9X6gNHGk_L zHkoKQi+Uq;{@6pUZKLv?UACC1je#K`{`MB}^S-FBCd(#&uJ^0uRf*8=>cE?mSe)Uk z9yJ7F@9{X-|B74jA5uc>U}-<3(f=|(g7(5&@P-1N@JlG(w&S0$M!NZD?abb04xma< zJbB=YOKy(J2s|+bXMSIv+!=zH{6duFLtmHgImt&ktKJXv^nufFN2hA-t)#apJ7xH8SjtTJ?zDCt6Ra^h(cociV#l$tY5=@)RZ>CJcU2IP>u=p0U-e1}S>(|` z4=9{>7Zs-&)tcunP7c*}gURk$-!{UJ21mY3dPFE8N(NdmKOX&X@9QSi_?dQOTzC|* z^@#p@cK6<96c69Uq5dd0?M#>1h@t$G{$b+AUhUY*=WidXb>a-cTdasf{N<$uf9z?9 zkB(%SzP>NFL7pJ826`T)XG^yTkswwe6Y6-@M?)g_i#L*Sspq93RP+{rF>s<=_RN2HIYF0=T1SagaKY+ zpc z9-52qpJ_~aLNcF7+xnqs(P}_CLVCgVRih1Kz+B8h2343281q77%>1c$`` z_qIkKLim$LC=vL?T~pYc!gffQz2{>MlF80=J5kuw^HBxBsF(1#B*{-^O0z^i(ueW^ zDaS3o1+ zh5yb+G;3>iZ7Aa2KYVuJ)Y|Zx?W=XUN3h`>f0;={b48&Kmtz=p;3Iw@r1G{-?r+6w z*6_Vc&5pI8pCnEOf;Qq5L-B5x7xLC*J=Yv?bl!EKF9yUBpjJHS-!O%E6<@pNJvy$` zgURuS9P@tOj~JRI;tGN%4C^j)jgXFEVt;lU=@RN)SF2rxr*FjxVf>M&F`W=^+E!cRoO+ zR_@iOv^cpT+2}lDx8A>NroH3dMFCI2uzw({ydAAoiAoumQ+{joLR^xVaparbTW(V1 z??r^j<51Sw>wFJ^J~^HJ1Q|N=mg*i?4iQ!5a=EID_J zY+UL^xQQ#Be^!^(S+wOx?dwC&YM(|H|H$-GW~|!7157xGn=yE7@?!YD-=al@qw~H0 zf~3sl^l>~BdVm*9=Q=1JzcT*AmpzII$PFfo)UUEZF7ygI137C$*0exl?z>%7Zlc%F zdB}ca!xHBiPNOc+V`1yuXPF>TBrz??~4V`ZWa##WxVxH zw8{P~%B#o#C8DXS{3kZEJB&XcStgTHW_^@s=+4Sw$BVyTw(In*KO+$n{h?Sj8J@cy z_j?;UanX`u%A>|tyB|e0?t*@R^%-gCdjUVLq0)`3mjryiarDy$|KkrG^F9-~X+lI4 zYw`RR57`z)-u*L|0W4OGewKohVlNHtipiGyg4H7oOp2J(&0GzP<3=e|u!C?BvOWN{}yGrtz$g`_< z{z0w}6(Sm7O8n$jkr_QUk9PSp^VDCVY46o?Q3=7mZ0$%Qkm zW#K^7dY35wLCc3#26itZ0W$93$c=H6uCs(mIqX6PFf}&8PT||1 z_>FV;E>hv3E7Iv8f8hJBEZa-q>RTMccY0Ai)}#!s>N>KHnbspidpqSYlGp-qs?y%& z!Uz2(%1_EjO$DY*DFr(wqZ!nRksNu{iu+r}hPGb%z(JCEB-26`GhWr!@ZMprl4mzz zSWHf;a6-i#2KXU`q37;G*s%n!a1Opn<5H@g^cW{p`?2l9294+Ar);^$naB5Ot67y> zKynz@kuJ@E=5`5%JekzHb>=0H*ym<408Q6{+hA?ox_wX4xt1m`c4hCsUW4X83<65Q zliUi^z_Q=@5sf!h3qbyw`Z$7@KuVs6V|=E5+?n?8>FG`NybAv)v*^DTVOb%;Bhk-% zL19lvOXmvAn%&B;hkufHC4UQG_YAizOc0x*~>LIkd>DR8pu(~g>KQSu{B0|=`ik&L@7BJgPPZ=6 zn>v~laS0X_)dI04U;hnCq{W*5+9{X*>#2tF+4+)hKeo%Abl)@=w}H3eNZ1x8o(7fz z0Trb6nSbuyofVk-PAK95cd=d3mVSRmu9BbclmO2ymSf2i@ zfh^=O;kpeOy%Em6J*cJ}u0mm4a>Sr(`Xz<5-~{Yx(RM~VjyM|h(220x3R8)PSAeqS zKX6s;tO95g{j45c=ALSB-^&GjcvQaCWXl63q^4LUf4tYNNi<)#rb?j#F<5fQz`i1ts8i$cz8OFz2f*5 z*lE%*nIM2%dN-Ew;-+K&)iKu3Mv8NpcItKjx0v@_4KX|-_x^{Z^IIjOS9ac7_rVGU zkANuQLt8JMTTRQygF~L-8~nGr)?@+thKED=Fn(KmW%{6caN($f1$9liD#fiNm!RzG z1Pjf6L^S5j1;FnKF|W{jNyi=0Dz197bFvspiGystf6QYu84M4{$_?@PiQ_D&S?(`U zGtNBQrMYaGTXZ^Zg-+=E0;Oi6SJyg?cwxd}7DUx&j$7etS&T=X(MQI&xACFHvyOt@ z8~VC)iza48IM|oM+LAEg^yJcwR^F60tdraw5!N3k zFJv=R3<)N9qLJ+9OvN6=K}q158QRq^9rIr_S&Hr#!)e;tKZuUr=W?6-J<%F4@CZfW zIekL$bl8n1VAiUgIMUWLcR#7Z2H!afqe3R)_Xb`5dL}VL;$^j>?Q)Oy^wV%LVkQ(m zIi478L>~aaSLwUBBR$SoS75*9C5&)J>oLc zm%G^C!W`3F1^nEpkqp1+vXLS&X39MyMxtLdin z5c!tEcb%|Me9z!F0+kuZJQC=Z#eDQa!J$}T>}3sDR~r5^D6D`H#0yq_nLN<-vdUmm z<;lm_^!Wi}srv;`IbpIKy7zwFFl@Gv=|H{v&nTc5Sy%qEB3i0+`d%kZOx!1*6C$r} z*pu!sf$sO164v@i00U)wilYiI?FzJeffdwhb`NN`eU@lsclTWnbV`QYlOVpLby;_< zNYf2oF<0_X_i#hv-kpMZ4^(^GGD%o8grO8wEm(4I-ouhWr%+43q zaITl*m9kyoPr|<%{{g-nU5@}uP5YAgOn{X&#sUySV5f`IGH|zD?e>`(jV?eDYl%2g z2`V9*j<#3`xa4&nhT|;`n~wac&!j@m<7R`Wohn6}=!5juMH-hd~Tf27vb8k7C4{ z7J#`*)rz^Ck_=Qdnr_jyjWX0m={>;c!=1U8l_zbOi&1@5qx{GyuDcIvnVp-K=1gsN zf<{k`SKqHxnyt27>xJx}39fBFfw5GOr>EnKv>63anj9ug!1eDH8@Bn|ssyg{Z41!5 zdKc_|yX6+``3Cbt>v>G*>lDA;Fg24NTznT=L=;Mn54v83MVpb(Sii6R$qb#!4DE&u zFC6$Wb$mU@zA!Bne$y#wM_$wA^O^U?$$_u5hfo|T+I98?mhyW5Chkq-fXT^=N6MUT zb)iq6;d3ChQ{81At=ezjQ4`v94PBvfK}X5Jy%FVG@f}+(vvk7Ih^fO3lVV3NY&*2j zBX`ZoZd*q2G&jE+(4yd%*4P$;+qE;{W+|H1`^+cQJso`gan%?ClH1nnhG;s;!ipJh zB|apt0DgSrTXMJ)_?Sm&FcI>eJz(2THDjMIyF6U~XP2Dlmo#ct{J{m(6Xe_0lXBR; zg8(2!Dln#_wkiSqm=xG(3eY$8h8K9hjc^_g!22fX3;cXCa9fanSPvoWlc{bT8E$dT z|Juta=qJ+`i)LZ8OekH@{vgrUN*2u7TgL**`nMX7vEh{a3a(Uikp7o!%6D)vnj3$b zHzDB9)enI#Rhg4-yto2`Hffnv?{)O$@>1%uEd_aPwtjk-%>5grRDSh+XTj>@dZ5FG50y&kLocB)0qKXImJYW1TcVNQ}|FiHT zumAb}d1I<#ohG#-K_UJ5bBfv79B;qC1*F(I-Xf3!*Dm#)##ZQw6FR&@h!=c#ycTpi zqeq!$W=UY7JkLD@Sj{D9%pL$A&$a-_KtCVFCe^{%FmWZ`0H*<3`JulIuRsa~4pq`N z;6g*wKS0$jS>4h~I0${*Ww@kU=yFD}>97?odQw`oz@TI&-gY`iC3QnEe-^j<0;uE) zYn%FLJ$51d)#Tt*JPGvcR{?8Z$o1&K10SieO!QWlAHkOIT%`P{RvNAsUAr%w0cIN= zBJ-comoCj{atUipsFa3>Rko{S-KAJ7`7!8bIRUnFxFzT^Zfw{4tt1vqKz{~k^&7hW zzhHL|l@6M+}NBI|Lmoo_1iIP7MS&&q*S;1!`o zWu2(NH0V-y&#lYVqVlKhA)6@1)#4)YzH4VXdd>H5>8nwMlaOEi=dFmg^xbxdp1Y6J z>us3dbKuPeD5i3ve@7?8b z)J%@p$6|rpuc+~N9I*WJx(AhOpcVH4s69A0s_L*fK?xNy0DCfKYV)}`qo179B;&Vm@IoOx(6DNxkbZ4W zC>%0!V!Br3uA@@_x7NhOps_YnOMJr{L;(eWrdhrYxoM|j?QvJJGy+_8ofO+aoO=D4r_ z_&b8b=2$@iWjlacS$spTvI#6`@{bl1&zX;rNi%o>zhxmz4VHSbd1afrg+6H?M1>`I zN?A?dGoG@ZIcAfk{y+L~a>N45gkm|4;VbMstZt`Xv7G(+R0@A| z7{$3ve-`>~>GJ^X-yHmZgq!dy1s*4ndUdf^bTYP*y2t;hrMqbq9OJa|H(vU$QBDwj zVA{ElWPdhi77%{la|4rrrZa=NsC zbh#jBfa&)CLE(P-h9kUd2ItM8d?%t}89&Gw(tqh&h50(LdiKwEPv(YIpsu0^Zh$|p zY3+t*VPu1PkTGaZw%WT-A~Eut(UVrVA)R)qeh;xt06iC;0le=#cwtM4cIU?Dzd;GD zD(?k6-_&-rTo^ukym#9Sc=vGNAK@i0hC?+2+#|UyG z;bsP&LZ6X(P=WR%!CUnYlfXl)pB2SXa(^brrq(&8`%5{sU~wF|&5MC79MzwUzNmA> z1+AweF%9pUih|(;a^(r)%U8pqI)9cZ`Y?;`?Xb`T#^3?t2mpTP+>m0Q;E#V3U7zW0 zvaG~LzzH2pk8g-`+;#tAaE;-^Zh$Gu< z43|whbmrgA0GZ^sT?;I!T94X*Xiax<>)6k>IDs?S@uxPWq!m$AOXAeicaPXZ$#CZv z(4+d%F}xbBVyXdtd7*JZE4r7+6d_{zFuIfb&eczjZYFkf{5_2N>yUGkKQPOMYW=o_ z1_yE~)82UV7r}l1R0&13T=FEug#E?_gTeOo!VN{g;toQoBwOS{oMDGG|B3TQW z$DYCz(QzbAlGffzT9#Zc=P5en5uA$Sz%0u?;6}gqTDT_UX0ax~!sdpHN0BGzALTAW z?5F(WYCAS&1X=Wj=I5gVSN%^5Jsw2QJl-QqZ(s#UypRoS4WG=DYcH(x%)o1@^NR2k$>*h~}HK88ReAJp3pW$1} z7~imT5bT$YA0XNy-+Ds^9M9^f!{cTO(W^x+MO2sZ^ocaaLnrp0 z19NIr11Ke1N@6Bp(u>Htz?JQ@mdkuRogWb8f=(Id*Vh-e1#;`l+}h6cN!VUzTb5QsEPAn)>}^`e-3WneefOW(wPJa}xn_ zuXxEBBO=$fO%(JaxJ3ha6cojY^&_F5Q0v;0bY-+%7f@27_N}f;CCW&<= zdo<%~25+1^2rRaYkb`$tqRxUtiq~ruKZs|;#5A?*7cD)O=8Cbja?0Mbpm)dmy9P%K zEFIO1s20DF0imZm!*fZL3P-hSA>?TD5T8V09O$U%5dM57^hs_rKPOlx@K+e4EKLe~iG1O( zXfFX&bvmzoqqLj(PQk3JNv`5Alj4b&FFFlrh}cw#9-)a9(NcqaF~m@nrd{z$GkP$lq&9#MdEcWR%q-RtVe zuFajK)SzMQcfjadvo%Qv-EZOX#=@SrD09^9M<%ideFiN#Y zId^~$Yom^O^WXhlU8gyFF!qvc4K<_gE=C?}eO2641Ux$Hs~R2Z#`Kwsi4rh$Z+?}^ zE`gFRG6GTMPI(3}#eYI|uZ-WZlbwwXn*;RXDe!kup)VA|vydW$B zc+Y_|LM?i3C8a-kC^>bYS9$5>i$5yoq|E1cO3Ut&yv8saJs5ff_U1T?hJZNCbN`V* zYXzzmu%@wauBSJbJ2EDA*x5~KH;|s|xdX<)Ve%^e1-aW(CTKW>a&!ZplnOGxA?}pB z(T*|-bzS5H`OMMEmYf#I*XYvD+>(FAIU4+UAZk93+D!GX77oOOtxRvd`{>g#boWBc z?h&WL2+s`{xz^*|Z#PcldrPJ7A2M8@7(2Jeggz2F( z@f~)yItoBQ?b8fbf^J3KQuhwZetGt{hhoNs>X6{A&0hw7`;vnq5b4 zMBgv%rL>t#na1K3{Q!S_T6jBjLp@F5JRIy?zAPS7`ZeoCM=Jd*Rn!byq26f@(vlsn4glT~(!}9@n zYS?b$=oiwmAo)ddXgePe3&=?kA8&>|-y3)v=H`4d!j^3O`MHe2L-CV0CT0_z;^{Zh z84_DR^WC_|j%=QRHu>@NIZ5C#noi@SXM<`|iyj^F&vxkBw&DGcTDv?y`rJPERj)^i z68dr;I-danx7tmEYAG)e(L~BmU*lHawlm^C%N5Rr;50tuXZ*@weH`SSl3f_lD+WAdyz?(!|Rs8&qdjO z5yERu`PItE0jU3KEQfe>`Q{(>dzRvkx7;STJ?^h)vdprNst%;p9cTAI-&y&EzJ_;q z$|lBFY#YAi_}Ttm_jpPHFdm@(F#WzEQKrfeiN8pRdf9m#s9VM0i*=q^nD7S#$>67f zG8Bf$l4+M!riBLmUkaAHE+S@T8fOPX4Ex9+C@-7&&eZ|Ge>ZiX7h2}Xo$HXd?sctI zq@z{VYdsCf)O9rv$2wWXBgZ^%B@YM4Ibv3con;{j`XyR)K64jF7qDTIIh8Lsq}La~ zTqTmX;fkm|R_Hsl9&tHLWlm76|c(n1#K%W=zw$nkVs_x>$Z6 zsjnm>5+!o{^Xf1Egmv~_kK4kkhfns=p3%2SC38EXG;u2j@4Hm1t65yFe0{cp1m;<_ zbKNt3d~X{y-m7zHbQS4N)ix9+_A1FsE#qxBzcJ4vMG|nqn#=+=+@YJF;dnDjTNDHY zUHWqGymVhdYPl7&jQ@z6h)Cs6ng$)5EL2w~-H~5)`V&G1B}C*cd?VZ=gR2928DYl# z*E>{dOHrN!kNXU|sekEcv70M+UolI=c3$s{M=O*1{`UeXJkH)?C=ZOBZ`#YNuvQ8b zlhyzpaH{XE7^&e$Gu@pNtr~o2$E%QDb+2gKdO3ViQ9m(nUOe`{kb0ffvTm8tkyr+= z>WnV&FOGam;r2%0XAHs;qlDI9{DAGG)xL1lTA$idZ!Al4Q1e^@wfuUp_%0Ht|peyTo`1pa@2Oo9{I*Aoj~S` z;;g)n*}P>lWR9~+k`2?iij49CazD=k0wMNjXa$^tk}DjW`RYwzpA*#d7+BWDklNLz zFDnMn!Fbbl+F=xp#B!ku+5yG&5>J%ZbDwZ9BvP2mNxT>=-xvbt&p+7i_jn@k~R;7A#Nm;T^ zJ5yZHTHg@y1NEjk-7@A#UNM;BBsp}(C$)_#RsK93N3tbIE^7~zRDzq@qJ!za#HJXF zoKz`Ra~=YQWIu`Aa$oP2?WG;TT3poUNpKU)#8|FVb=d5x|;MVpaE$0MvCD zOX(~JP3EFO@oRMt!;Ob!F(S1+0rQ2mpZFHJJHU}T)9!yE8=6y5D^`wUcOH7gvTq+g z+*fZ`J7`$!w28>c4|z{9xvj0kp`-9V|LA<+IF*V9vB>njeS?CjlRKfYH~P&cC@aXu zJj58!Cd2|`d$9mNlX$&DL4NRFSh)}-*MuP+L3SaHT8LBk;0gvJy9rtiPayom&%AnNP%K#u1mu>YO=r$hTiv7~FlbhzV&E>+5R z>ye_o$Da|XDK~F68B5!}EtbN8O(7#{+Ths^E2LaLi&1?-disiesJjcV4E_w`H|^04 z+J(xvF}wKWx`v`?)kSpp{%Mn$BpF#^9$8ki+-zvY$yA%RIr@c3PRBuzu_5HsJ=T4B zo5CRFvD@`5KV8Jm*UPr-gRib+rn7BT??;CB6a>eWinDG~8!9co=N2bHnp!g4wiIS= z{^oR(7W@Q#emCPW@;Y0$_zzQ767G3Dx_k^0qEQZfBek|cPV7Re#BM!SD+%l0`;_r} z9k7HoltWUf#g8XtU!+qX6{R~o{FWDu0&>KHD}rDzjO36qzzhC3s~Xk&zg80ES+8?4 z9)}s!L}#O%*8?*yv#)mei9#8xTs+$eUvc0k#kqeB&NL!#GWiUtvzN|=L@s5yYy=V& z1lKAN0RPzbEgwpk9L+pp&r12OMU+m>H3{YdA^`kBzb^jc8y$1W$fB5}>kHf=79Fa$ut@HXhY`$~$e!LEjdzp`((I?f#OiOgS@&KKjFeNBw_ zVYwEw>~^U#cY)DgGUl%+n1B=E=@~M54|N-6{@2td>F@W5*|T-6g4G|_)(S!wYeDA! z^){AD_1uQ&>!(gS=*BKwA%o&5r&dzIMli@h>#e^NohqF1y4ZhDV9vZZ02T>(G8N@EwkgxYifDY1y6?T~a>pi**vU{2ID;%_bJ+ z(4o_|^xR9vi+ErKOwl?E{CwJh15vowYfV!%PXbAhfSf__SgPsg)7LyhrU6}Q zIAVC8SR0n|96^n&Az%KTpR7g=-4WT}pEhSB&sZc^wD4bd{k`)D8QJf=KPhh`=sPf> z29@K0Fc7VJsS zaRm?}{`BR&-`oX0^Cfotv9D(nOzvRQ!sq@kpQ0uBbk{i}IY>~glZtY+IJ#^!=0PT2 zxr@3F2>wj5rMNd-@4P7TXDo8;$T@ya9dAA_@n2SMg{UT5W^kU<&@+u8T1ny-{|9Sy zD198c$_>332J3|92!9e3&&%ho0X7dqgxCXm0^%dz6QRhTRB+(LzMA~drmyGtwU4vr zP@}yY!tGCd^ZPuWEft8!ru41o$?gO`<BNo$S*1)6zoh}f-!AbCB=1kRaA8m>WGb$t7B*s!}iBAw>#3% z0&nH%_sibv+@%)~46BVyN?+;r0=zQ2a3J&M_gNw` zk(?Kah7=5lJD6~OzkDZlf%pQBl^TZU&B&p(YypGSuD?``n%PoE#jmdPlq+}u^ReM; zC$4y)NX;cb7nU&E9c?Un;8aV_XJhvb4xj%~^bN#OyHa;Dkr#St4^9)9?5X=VP$W;) z`T~{lA`;^~M>ARn+aXA!UVx6(RgO(demtuB;WLEKmoF58Gcv}o`m0ml$n8I0CYZ{C z!_t6O11QM?qT7f%DiC3e6Ye$Qf2!zj=kZximx<}wqfcB((2{Gu%f+PyQ{MZPN~7CZ zTR*Z_zp|-^q5SAzfO;5oInnsF(?<1f%B1|4*fvxSzL)eEPAWcSWRZFI)!+`wi4b3c zobz7k&scAt{p-9nc-1uUjtl09`-T1VW$nj9NsxWFQL z9ok{9GF6(I8km;@@ofu=`>mDz_-C<8%0MkiW0rtjd$SSC4LcPXS5L`b2JvE+LZhca zFS%ka=-meLmE|b;{HGVTC^(7f!gIDXB8ghx+B=lNxN3H&^Ywo}q`=vY&c*|Q8)1Qu zrI5l-BEQ~$jB+AVW@5Y`qP`$e%G~t0BS+j@Ur`%mAkk_M6^!WG=N`u0N29NPuZ`0N z|N14;u4wpM?c?Myf2ZsVfoxI*jaQ)2$2{(BP>&=@tFf*BGJh(R;fz)LvglV2l%5F( z0bRyh(JNfL;6pG^s0Gr9@qWg8u(9;wZ+6I39raAKu4jY){z9qwKb}Ra!mNytHK!@C z1<|sCg^)F)qC+9-$3x>wK$>?|l#zmInK|nMYBfxn11l=b0q_EQ%`I37LrZ(!vTB^?lzjaC@$DjA+%1tqx}(^FD!+SwwW5^mAYhgB1*f1r6!zy^T&;pm;??h%OmlwQE|V_LciCoT?e!>=O&?%mq^k09 z;1X9l(0s4}JwU9Cydt#Aj22j@v)Kz->B?5dA2YUX@H7IyV8<>Kn*bDo7l zc)teg=f2%$R{s+9jL&zQnE~q+DUgezVDHb5l=t3!H4FarU>gz&33>Xb?Z2%4-I^G> z^E#_>noik)cAb?!`j{k{Vdz}XuHex#Z16MYA_5MKb3rUH@f{;ylpo&Q*=GUnVSf6z zj@f&5V<7i%H=S8pup+mX8{0T%i+rZt8k`BLRC#0#vyZe*LHL#dH-nnXv%h#~uxFDU zGLV|5{WDvfN08A6i)M1p5~DZKavEp@xqH{0*syXco&9vG=W{ag7D%p9a1$upjiiUW zc}4m4OrZZCq^n%U8YH*BDmcFrK z=dz@v!R3tH@EWEGiw%l{C~9YZhKGsy;kd^^=J)5FZ&0aE<=(-^&xssq-+%ZDV%uig z2_j_WLYkqfe!kEV_)W4wz=)hA@{D-*5O+v}cqH&kvBHD74L!FTxAb|s@t@Et1V!qA z<0R!y5@I0|lL((Elt>QO1lC^fLQmkJ;sK?JC{k|!RWHm0wD3nHQR5|eO5rHNx->F= z%$>qne4&zsTNGVf>K{@y*c107mMpz&V(Bi7`Hk(r))$`(;Eo8Qt7%N)4X;sA1?7qD zaD`m1T1dz8mD-6hShzXX;oUz2y{nF8#DrJWs~wL==7~$$xmp+Zs~BF_MWi%=*$O75f z(y`SyqOvXEqx#F6I*(Vs23O?&a7(5snOs2|+fn-eM>f^ILk?hW7qkzdcJ1Q*zc&PL zvY@1wAO7)#cn7L6<9@vq);3CM+TVCc+}p;Kop>nxuVt&5kJtVZti%mMD%woC3!h|+ zzp}$z!Adb36jgO-NbzX)U8`obyC#r`D&MO}II-dW>7RI0-2nOa!2w9R0WGz&P$g0b zhPd8C?I7~`>(Dz`1rVGI`v-8GoRuwu-J4|8*-5=I*VW8+k0Sy8f*> zH1BDq17CLs_{M0)K0jX>Pe$vsg@-wL2mZIkHS#dPG+EfXocW3KPa+X-oaF6v9bc2eLqx8^+a$2uT#8YoFy|N*jUE zdZ;}FqhZr7=i$H5GEa6_(8iRRd3f)jzy0TN^{D8g@9;Y&)oXUYLL9uvLNxCYG>qL{{YWgU=v03(kEY&=z zd}0o|#s=b@gPI@FSp~J9{<7js$@B@U%_)Ql=TRQYE`4N`e#zTKm@+p2F6@6<`PBcS zc?q#V2(V!^G1F_pm8ir!y+d#*#J$*GT=2{z$0nw%JUiE3a9xtEaeRBEPsgb(xEZDd z`Z&h!TUWwWZk=A$Do_aA8J5k=X?)iXu|R7rL8mcBMA~%o&iD0iAcg4HN6b7++HQ{_KO;LCM@KlG+DH*YmoBebT-_Jk(@M(=jG(H^ju%)61hYi~o zKjgUa;HCfBf7}#!G+KiXE@0~_;Cwa3`kC9`?3qR&@6gaikbj(ajPz9iE5F*(NB~ak z4qlqSijS{MD|Ia))JUCLDoB27c8f?p_UmgD?K1paVTtpYl$2Z%H4?9hp^h9AGdyIH zOLXDVOM=?*Dc*lc|0PL&&qQ3ua9>?6Ib98nr8|WRWBb6k(;YJC{M~fRj&j8<^`->Z zU0rPuClpJWcH}>kSd0pq9MM8^*QX0ubjt~78hh1t%dX};M?f7~s|gsn0WV?_898kJ z+Oe+xK3Vrw@JPRLZ$4z^!A$uGUC3$8QJZLge@h)j7!WBLNkhlu<~iF^IZ)F~-~MCZ zTjh34mi`oORC7-_GpqY=&;ALKAQ8x+)%8ViEx!NcW~go0Iul$w=i5H`JDdN9P>Gna zvK>gQd}%BTSb{5$dpG(6iv1Dnm+U|xCUG}<`f*hQC?T#Vse`(ho@9T)yL#4V+cd057hAlVW{3B*ux7bWQi~EeiM!AS?5OgdK&*j!bM7Ok zyD^Y>qSh!huZz?Q?>yMwK{6MWtgaMiM_4(aE+241C0yT6+-)7T$mPAT?9=g;aaZie ztrFi*#OJvkH2MA811Va7LtxseM+SZT(l@SSLDPrrv*&!4LiuU171YW!5|4IKnz2ZA$BaGqed3tjh!4{6Zn6zpTVMn1_ylU ztqBHU3$1lpU+G2*a=&V-OVZ`(iF(BBGL{uP_M5{RdUqVt_Z^Wl8*!Jb@p5#%z13Y5 zg2*fTeAaH43zGN6h+J2b4Vn%%j4B+O04ar7Abqw^z}Aqp|L&J(1q&U~YBq~D4+Yix z1tso{Z7>tOg-v^G)X+q~(sieJZD8h7YqZAvByN`5PE`dYCE2**P0)D6IgeE-P!^<` zREcS`5%ww*umK6%&VM+;n(?Luh zyGG0}sNE+>!kPF0M~49u1LpSU=(a%Z)!<|BB0duW zv^<|ATLNB{y{LXT@F;*D!B2kvpokL?t>}xcAby0kS{4I^nAVN2Cr-3=Y6cj^`~2rY z%^xcZ(Dp`ec?+@K%!0~Ubc#BtrsFmv8`NI4wt@O)W#0DJ%XL3q!KgO-(h#L6Nftrh z<576DcTodifi`LajoheVi_QtOCfwq_*$(KOTL!dGk9X%e+On1wDj{+cNq?!3-XUU_ zT#2u$WD&o9^ZGn_{RE8p;6apb)x{!*TNv<54S_4-(ef+4_Dv+{cVd3UQ_NefK-Qdq z{J#I2sl+yocSUXqyVW&ni!Q^em8od16EzLnkPB|N;}c8BQQNJe{W|iC&8nMpD1|BA zbMLp@$lc~E-MP5$lu5VU!+T(i`96C(*w5MKzJEb;c`H;fhUC-Jk}VCl4<#Ee21WWb z&Mv}*6+juS#yk)ww}|0f&K6jzALFP{#+Ht2$SgIgtn>9U$krHGntw6=U&V>4vv=#B z|1NAwo94sZQkr$uU93Eodw$mU?!Wt|n9%IF0m&so^OpiN@MD1?GNLD0V@09Zd{MGl z+SAnUd|0g3D=xfR64dmg_gsia`;g;B!dT4msCvS5x0Ft1LzFb0?60@JUgiB~jse5# z!&kbSxm!$|A8;Uq>$Lv7V-u7N{H{a}41ov&Y{|)h6rw7HzJ<+lA3+W*N+CSLkJbM- zy76O2OASzaSIdI7Ghe0Im zwGmXw{pcv`Tv3urV+0QVUlCH^D#hNJlMiW87HGYBh~oz|&2B{nB&KU*9@>_&984oH zpL*di^?EN2Ry1??Mw2LUoa{_sqnG-SzJ3v&C+(B;cE1c&cHbT-M~rtp2$zH&>4~%# zRG$lzcFd&c6GMx~KPi<^R^mW5-q~~YP*msHTBGvVE};ma79V1&e``lnl^$eG% z*P#4Tni7boOyzDP!CkuW3{r$~*%bVs>yP(nlbYqaOPf<7I@q{HW^k575fh#N(4 z(TE6+R6SQBj3HiS^B;b$Hv+l>|JFDJf9u0S#OqMp7J%YCm3Kn|8shVBebzCjitOIi z=4iDuc*S#D(0W)DuwT>NFE&r76k9|wWZHGg{Mm8m$3B4b3TG6piMx)}OH5cLaH1A= zV`UBLjcQlH>bYwWcA$~~;Z~HbV6zH2=+G<=%IW<=F&}aTfpN@C{eE65X8Z)3Imz$8 z!p;6min@f{hf;+iv^9D9upu@J43%SAj)jFN7|Z%7ZG^=M$74J4UIW;bgT%d760{Cg zX^bpqsxh!Zk7sKdRrAbRQZIOpSy&HSK+1fp50%gXD&g^C&GyL+4uPsdz%Z=zx~Y8U zjP8=G1fqoIy@u>wW$W8Q(f_B1uyyl<>6%)S^-tA(wqp$X!sV=_ezl|TyA*c43ZKfd zafddp7^|ggdtD^NLnO7jZU$wGWU`}nO(lkDh47s+JWW5@{)Z{;(cAJJ5gAUw6~>Cm z8nR!WS93>=Oc~0|E6;UM>N4}%=a2q_InPz^*;!g4j-Kh4 zbTd7n3%|gp20a@_JR)l}paY>E`*|T43pYU9XUoma&JQNcN^tmis$a#>P`~&T9Hbnt zIjkl6)$1}!xempJ_*h6$OAqH&?^^h)w>bLjISOvNOIbjhjBne6M{^g7(n|(1fODO# z|8MH9Dc{eC!BS_>(KC-Tw+t8HzjzA;?(A4cO)>_eU&DPj7V^^zKXDB7ujQzZ^(ARW7@|JR zLe51(#bQsRXp8(lRzlyBolFsG|+1XX{lT^pDwzfpT%kSwY2a7ZWaR=Zu;>=3^}?0k{{J=|t< zp_m}Jr0Fb0rDq6hVA%XiWnTjv$_c_xUi|)vUORw;57&*9fW!MMHSXoJB)Zd17Ffs= zm~=~9Ek!|6r=KQz@}zEoQ}RdvZ>?s(yR@}RbWZZ!(|BWNGPP)>O06tq>sx$x!D8Ys zrI4!80$T6+dB7Hm;T~N#D#vP^<_d`7eK8-5IIjWXsB)j}1JTe;b*k|BdaUn9%|BbV z^kqJU^zYe?8`VtKVa?R=wcWSQ|oK)K+iW=VbrAKeh zsa3)=nx?1!^ToU_oYuV%mwX#4yXMUz(FtRW>8USMWq;U5iD|7lwn z?oJggdhbW0B^oQ<;%$K8LvVH}7CFs*_DxkHp~5*{OtIgs-KfJ}#bS__Sv=6TxBes< zktw^Q^cwPrW!5t@0jddneLMnh)<)X;ZFSEgwyO`Xob4<(zv>%xqRvX4q4{+aHj?RQ zZ8GfXz`iDXdb6FVNTDMjG*`K>;Ix3M1qkiaA2C znh(3{h22y+nOLp>0?E|7cdK>ySH*gbJM8TU3TcBkvuhY?el)AST2R{EaTc3BEhS)X zvqrfRF&7KJ3B`}|&lXGl!TI54MuD=wWq|>@k@_k}H&EP1Y%46lCV>tmC#_DbP~<*_ zpvh~DWRfC5$}*2h@M(5_jmz4hXlJ$^3#k#v`sgdJxkEse z{A2jz$jClq!yy?6O;J${1i_&X-|DEJKTU)TLs9`Qd+g%{KDs&M*2f$+Eei@lPS{@y zT9&`+eU<*nsuhV@uE{Gup}Q4*(dd8HyaZaqY&89N6tB733`#NtXMFQPSCs;5LCCYE z!_{dF$1WJF6VX;ar{f8gXLWZV3pC(R0wmi4Ft=9LU8Y-Gx~b=4+Q-Y|`H2FI2M;xx zK5koK>kC7DKgMdcTwZ-ge@l5^W$ghzYmFG6KeYtGMie5Glb{j*W18Knmf zIMPuW=1G4-sa!^S%Fc_fi4+IfdD>mg6ypa$mNH$D)ZrfwyPyWB^k(eKA2M>3u8zUJ zcSeD_V=#K$r4z9}d@1q>NTh38XY4heg|@m4B`H~#KV8dCyt_To1rm4F4F2zCVHJf18r%fo zyxwR#gjA}csrEGQt+au4An=zu2 z`Sd|yYuvya1>cwwP21s)8QfsC{_rP#IPas5UShVw0;8QAzhc7Unp;rZY+tR+f12)u zIaM5aSH)tIQOQWAw8q1mXiek8^>h==;jIK#YNDWx)jH`$TD=p{#I(i-ek%VWfk1)4t7LaRzo6mY@%+3|WS%+U_jIx9?U=JV*(+I)b*O`8%FC z0?4|mX7%EM`T6AsKRE3*7KkqiuK3XgY$Hf4cMjH2sG|>zpQXeR?`A z%YU!*YAc+#NDu5M=x%MJ7{>Tr|8OmTpKcp$fHrFRX5O}&lpYCm+;<3*J(u_=)>(v# z&K8l1gnVxwaWeT|b2+wOGr14}UcSSg$tLbHp%wmAwkE=%dq_aF2W##KxmrQ&8l$3KwV%S}wO`Trxa-He$Sm%nKZAji2_9 zm6|ec@13D}1Rcf>3+7ziCFf_XVUSPUP_%>1GxP-Ke`6)WS;(1L2|vX$&;`i|PeP|^ zdz43~%K))OgOV0a=r66%N^2y9{Q%&fkcvO;G!o-C%Xd344=X78QB-&c=$U554$z3z z>xe;RJHm2S*96~7gd}T{d9V{6pj8K3u%zBXf2I{U5==i%`oEK1~~~g zC>gy^d(Dlize`KMWlaPjXRM&8BY}f4}vb=u2$tnhDE?rGpeI)wY9|@Aa(^ zuCg3eFZth{T+onp1|%#}nQc~$56PBm=u8K-v0{Q4-s78RVdnR)U9L-TAK5uq zG?PBp00r9=7r)Or3nt%}$j%fKvkgmzlTM+&RR zhB>5uUkm-=7?{&VcEX7C=zM&l5(nYN3=T-8J!jw_c%Le6rFsViW1y)nB%Q5|qG)v8 z=uEttq)q}gqEzX={9;))GjVPhtEqS(1OwQk$$tvZDKjY)EnknN-x}Uc(?EX~EIb2V zp1j5awS4{pL$Rz{$)+|nbanq}s9x<$%sCFin}yD+-vIx$@LZ^+ynPo*=N`7}G9*J( zJSM#UIde;ROD6#c`9(F|am&HWRvzmUsU6lB`$pTpsTtI~;-*C>eCo`2@K2c0?Y#Tr zylAHc+K2}3s6z;Lz1D#>BCCss&ix z;-BO8@O8!oQ0P;S9$+L1dM6vDH|XPUBX+C2y;bZGE4BtgK}&bEGh?0y z%RtC_l(#8Jq5rp@d*3mtE2(m2Y)To?c?Ml)U6CLB3F>< zolmf!OKi@+iZfe=M0x?g+t>L}G%d*7x6O5yqvV}RE~GVU{6>)D2Np`byuipk7_U%? zxilB0whwXF0i^HS8lXvup5TS+32Hr*q~AS zwGQta!|l7~e2hwLRhCCLvd-~<6U9#ULU5$>B%{s!BLea8A#%lsZOS5tr)=rSE@S65ABVf$OzzvA;AMu6}qE_6*{CKjew~ z!Zh<%N=|IUYAGp_N$lrDKE6M@p^$%gsN1CBhA{AhXl1S&9P=eboq%%a^{DLGO;W}1 zQ_;nnhV3ENnIhRHC93Ra@mYSyRSmfOD{pye{?j}Zb_%~*T1mA|r@Sxh?)|Q!#V!>) zu<9z?<@)z9S}k4Ag|10PgWDfzzDk11ya2f$Uc&Oic~mdwn19?jxo3H07WzP$n1p#$ zG6dc+UUq)bWaYPWVF9}K2t(pKknk{`f`_6e+OZcqF)c+^CfDzu>s>-t{yD>b6}vT3 zSU+hRE=n&5q_Ta z5JIye<*d&QuKQyoUe&Uw^KYV98)J4X^o2PA$|{gKv+&xnj?Ue!C3Yhhn_f;{Uf7C< zu4i5|!<0hWN=WCqAJWehhOdeL1GuW&H8YF*Kl7E6e%IUosziaXKto`K$=#z2H&|p}612{}fsiR=W+c5aj!EN@r04kVeo0g4HbA2}N`Z-^6Jm_(&^uam>)F@3 zEDCnlUyjzaaVYKj)_#^X%eS47(21nq2U#S*RnSVN2yv0{^~5(oe0Fw*{JNLkl6QLL z%=u<7F5ToB9unGqXJv2~Cebz`G!8j&9}UC(6+?#24>GbcO!^q*KAl>HB|A4M8aap- z#C$%741yF2Y5A=RPFMe6nLkn^77NSdD3v7(roQ_`MyEKk}_RXsuFNWYIN{CQhA^Cs0e=uG?hv0D_4&VdCXCP&R zDVmlk3v5EXyveym+sb+pik~&wKe1?m-O4y7H+xV%LJYl{4;mV6I@4Z0k?1B#XT?`- zfqbNiD(ikZ6xFsf&}&* zGi%n9LgK|dtBnaIPF;QW&_NdBb%lYJFmv*?@B6cp|6&H#kQ9D2PON2zRY8v>%UDi! zp>Hr&CGREfE9&Q7%@&?3j=dS4WRaa6K8`i@-?b3j8btPZ%9|TZd;gspOfmU(kN^3!0=Y&68rO)>OKRZ)3m;^pM6Iz= zNTGK2wx1T(ojUp@&3egZYRr3sErWe+hX7foJ2UU|T!rZ$^jB&|jvbAJcx=e`z@8GY z|99R!EYCYX5P!x>>M~V(;^wI<9XvbV1%=ptSJHJD!Xir;_Jkv55z|wh>6*AhZj^*g zpn{}XW<6!M%&9L=Z)R@vJ8PJ|%ddZY->}dpc^G~b96=?XfA&+0dPj-DgFuMCu4GkF( z@@*4eRbcdTYed&RM(7!1Lh~k}SoC$_+RI zki6V|_^tCbic5G7W2gSd6$@=nys6;(ka^(+(;@r&W@Bfs-9zXT zYqDeLNqSODJ{P^cz}rmk!_u^_n5sD7TSeDd1ve$uspjL24eZU~L;q6q*VL!3d3vsZ ze4Mp(b89#Sp=>6+xn0cx92;*Y%Koi;gkee}G^DSxdj`7aW?x{22#9hi0~J&CNP`r# zb`|!Q$==2Nw}q$FeO}!D^8=?>X_ta;yUT+x5&w;~hB>wdH1EwQvz@s1Aje;# zwZ&F^&gwnH4sv8QgCpK?;M5=S{3*QT72)MJXn!eLXz@>h>00WD^fRdhK~wWjlng>%YWt(~G><+-idz4`Ax4^X~l*U*l#!utYjA2nTd-Cq8RS4-gIds>VN0n^x z8#OkXb_g}|Caiq#gZs=l0rC!Soa2jnfL2ChDEK@dg|D7Nxk9gxvAJXl(YeC&UM8xU&ymI+5i_ae#Uvr7tmKX(9=HN!o7w4!h!Vav6lsRqSeLoXBlaR zvlk8rpqeeH7%v&!+sX_(4E!(g&pTSz>7jBUCX2D92%J=yF z0|KD>8vqKzTdJ}DQTRjbd!Hp!#{t*G*J+^|AKBw*Gp=lMsdF>36$kKee-7I*xY7P| zow8yj1{!}-eJKMU6yj3IfIKwIyo^3b1kiW4O6-}wahDNiFT$pM$+lLUdLvEu#{4?4 zQ`eT%G>>21bc)3IQ~^( zGB_=Z@Ze4GWTas4SrER#f2#6Wtmpb#2m&;v`xWx@+c&NaXkYXS+ma^6Q4~#5narrZ zR+4~(sW#WWNid>S=)BmKa3dyeHqEOL$;Ho{sC|=Zz_@krsunMRP7rTLazIYZZ0qM^ z0xibJTj$*S>gZ?C0BVZ`mZ}wEk^k3-PD`qeCsalhfTOMC@zN$56p=SIIH9;T;S?KZ zU+A=bE59A~5h2VIj*AS-uzsN8br&n}?caEmj`fVRLuA#3KPEMdK#5yt{~_*9 zU{(*l)OM}oMzo#25O8efdG*dj^wmQBg7>LYee0f)LU_$$>AEdN#A`f8hCza_{ zzs;yl{P1$~MFg+jhX8tr>VA+C|6^X2>^(_kdV2|V#5@gBM=aGEf^J0K990EAOnQvR zy$>PTw0YLcUq9~5krKOHEywS7xi?-f@z8O$f&#I~y85csBOaVwwZ!`xjio2Ge!`N& zLc;~}*;?NPgCH2ZZi5nXPm{yB^5okr%?lx6-X`N*M~7Ob2>d z0{C^c$wKq+?Cb2qoGtYPJP2YA_O`3IviM&xrMQh*HAgfpgc}HV@7idv=Jk>%U|ezv z#_bucaM5{snHY9l>7ed^eN501s`W&={x*iM8^?rqF)Hfrwr!_pO^&eBH-)~fS>LP4 zEEjdixtom+ClSDxuo^dJd5HR}JDwDEDQEEG`^dh;7|!A^$a#H(p5e(Q4PDv?tPvxj z%mfwzsBf4R#Hkhg@(c2srqG?WusIzxW9CFlum2R=4BQ`Uum1t}L)=Xdi5!xaL>5sB zU{$gS2_xL8mI7|Stx$!TIeK#H3sQlC2D5Iw#l_@Grj7+Lh-~sNM9)64mTM>b64=Ia zsZKkOPTiW5Duj>Twn#7sN*3UIP6iT{_A>4gn(8%UWIc*U{h?TPcxm=HNzJ%fF>&4y z#PTvtN0+{ETXOzr_isre$l|42UK(J4*`c?H=q-G|pJ3YBs_*rFu{8Pl&3lS|*?Pi& zDWYTNl-FaXijc)>5?~PnEwX?yB(&;Z{2JQKaV@k;C?#a%T$B|mehLkwFZai**?w=- z?;Zm^0yn6?-DXnPt${N{{wG4PF#Ql{G>{fzo)eKQd9^ z?0P|yH&eE5Gk|Qyy9~+)ZiH8lL6nEMc%ccF)=%41!cf>X@Ia{tlO?Pi;U%lf9Yo zMMmW5u4<2A#h-BDVsgDt77M@2B8K`*d>c)9)4gau$T$a^r=@st2D8eVdhANLZDSm> z8U;D`Xn>|xTkK-ny_eNWA|`rFI_{uJz+-4Zpfm{@Y;tcj6~G51a{{(yKn14e)ac?m zzekp2h2RN(G>Cw>mn!vFm~x8bCx%}PpD;WTiu*x1B2Rp`z9|2q8x=3%>3R$$c;aYHW zV$$c@ue3cQYN?Rx9zX}|`$>2a>(l=NW{b$z?_mGFM5!2AgQ^^%I38yzN7qgAC}-hNIIzty&kO04CI& zFWz^uJFLWcG6{1;Gp7}9z*6&Vv*&6QjOO#4JoA9wXI_Dz7$#wy%xJh#g(6xHifY`v zRhL6XRs*);sL?Hm!S2K_Hqb`>#4!{?i@2SE?GiD%%3n~X z<}Bdi3oC~)efK=}J7ESUK_lLoYPOvhcYmhwDI{{_wcR1sdSnjF6CJnaasfDpG=oM1 z6SfJ!IamV&_P};|eHp1H+(aMmOC9;9N*YCqcUwm9HUID<34pa0nC+py{MbmZPEE5%ub%lkE|E{;+7A69CoBF&g^y+u@N~NCx?nobl1Rc^ z<3&5@^h(jKFNIgAZV`c;eCgV*b%QFkb#vOTqK~RnqOGBVnWr<*@7`^&f%NHr3WJ=0 zTnPq0dw{dp`>g#^^)4z|JnFl6aO@#*2Q-!>SSgOBb=!@-!lg+4C| zvdC*#I&i0wEr-)_&QicRx?c=yqs%E51B36E7BVrG7pO^D3ej;W77_kOM#6jc+u6)3 z#tPDP_#B zC`F);DN=rv{4HmrbTC_cKi7(m&-3Rf6UeL@$T&Tkmp0z+@CY2JGUcDCTo)H?MDH7v zRuvNxFLcFrZbm?hlGc6M1y|e^2p;2a4t7XLjdh?XU;~i~B@t`9tMSEHpmbZw-{bQ5 zgWGtrnPQ~JXTKU6H>Jf%XT$Fx+ou*00*V}im_4aO(q~cc>o7DUEYi3j1bdp6IJe`F zYm^u-1n@Z^?cQ{>Myl7dMoSo57~Wq}qd#Yo@a-bsTXp^#moC?Q0tZIHm^91q0dIG) zPUM)QcTMC*1c04b?zBa&er;|)+=N$sUA+&wCsP6TOzTOU)sm^{R1FWpckuCMlx!nwMs($LaA2{|%D~|HCmq z;MM=)cyZ95g7~(+1C1z&zHZvwgiQYV03T$rQoe3{|)XFZ~f)0CX>b*%b z1p%y-1>H25T)P(lRvJ=&jS(E-YQW5kmJ9oy)tn9Hz$WYA} zggsUI^EBvPOADRNxU-APyp-TSNe5tkh_JtBvd}s3KN3 z{7Mcs@OHcD4y4qY+TW3+X=g$kcYc4Z$ej2(lqZv$Pty*`{(x^!aw7p)QSe~EG~6;k z4>_?WFj-I&3EEJr8wOn@SeS_Mr2oS;etR5NIGX|t%71gHhub4>$d*YUA{0ea z3ZX&~9D1kVb>l}Sfogfl>sE8@ZaZ5N!KtMg7)khj<3v8mZgSaHciTE9biSDk!c#}e z&J}_dw*RmBIfZY2h{^?idPQ2=IX{D}WLfqZuw417S=l#L6?m!ga_L}M zC)Rz8C^B&eVFxuQ!)@`uweD#w5h9}YK>f#Ihg5C|jD6>dCQwhKvndzDu9S1JMvTpPnkW#F;J#v6U9?LRgvmNosa*ylEJ9mBF+t z<<{JJ@sKwB_aYeydWvZN=b_<$)3)K=NmGKX=*peTgzXwK_PBJ z`@Y~Md&+-T3YK4S>uPLTzV<&N8M~f?DXBEGGK5n*>gL}3yIl!rKDWEmFtP0t=|B|z zSr2PMYKwj&+=5K1(3hyO2kEgLU!O{hotXuV>F*m4Xa69}IdHL#|8ueT4r`{|>+Sn= z!7Djd06o94`{@uLT8U@-M`p4naVAzIX~2uYoohqpjwA>uD1@CY(W7l$)dZvOWZ%#N zoOY_B6lsruVo?+s>mFFKqOoc|1Ti(!JCYs`OX9CqNL?H)cH}u^QbF%&*gQWjWWLe;}PTTh+P|^tN4_02>+tLw2_3<@)mM$`?=tb6-lC6 z^f;?YZJf@}&Y#VMaDO#bFH6L=)1S7{{!+YIc%&~EtM!J2NhyK^m>KxGxlA{li3UCC2$#Tg7h85h1}CkxMpWs zI;ZAR8NHZ~aNjiFpl(gMP$MRH5x&A5S3z_hn4OetrhFOmvZfL@tQ0|Pj?fp<&-GVd z76I9*@WR&P?iaGi6p!!84`{gs;-eF%k{H%x-wekW9SkrmuMfPObI8oMBzU4nq*xe< zccK3K`6oBloNzr z2;fV3I|@Hu7(j1ik9r-tpJmOu>?&zlWBpXJE{DA~xk0<58EIz$r+4z(WcMOlSZ`#f zUVyRyoGXw9)zlsl!yb_kq#>gEF;4GD#J=?4xwo*9Rk$VaBQBII@b_1=%sTLS@Go)g zkwZ9k+w@gK1H-Nj zOkBE!f0Uh$G1xDQN>Z_9^d{LAge|=Bb1c0KG*?qD`*!CE3|QKwEzKnD4Dhj(Mu+;_ z;31bvd8+S_%J3_NX<gIAx;?ZkGg4Pu zTEabpn%}(%4SR8{BC@{fG|?$5)~_%qwp4@*w$=Un`%uEEz94OYa>=Seot)$zgZke7 z@E&9w*S(Tvk$YWPm%Hqc{@6sm2ID~ZKAr2FDNW($I&)7pq|@g+w1a;bL^bc31xOjy zI;x$eBxw9|x1C<*;8UTz)2zzHCuIW{toKfJzIgvsuAIKy*8h$iw~y6DRHk2Bw{x*A&93vng{_b-Y{gsr)+qJVK|FHDgJ9X~ch2ArYdm%P62VFPOGZ&j8 zr9{WEi8jhqajw^UQ+v`SYnp)#p&9tvAoL6uI#3V;4JT*pYEH1Bs1^F zl;VVKuS@xWts$glTeyZz=(KXm0kFeu7d>aHA+^d{Q9}}f=OhEePfn>Rsw5Bva zs)yJAR`BbV72`*wOJ)UPG6xE8t!`&LKIVb&28fkR8BnN)n0LCbuC6uepPfzbb-c}3 z3gq7(QW}Aq+w1v-->9XD#Bf&Q_*^apGw^FMHvEoDwXl{+7%>sc4#gIDMB~7BN4uyz zqeBNeilpw87mtF4xG?v|9E&DQDVRQC{`Upl^j7^kr}nBy9(z>*eS>MF?fQkWj0=O# zN>RvqXy2($0hh)yaeK63ap9P+-$P6o-XrZ2>^i)rjmwG!UE&$M+umN6c;Sc5Sr&xI zj*OaLQ|sWjYVvVihr-j4)}vcI7|6kOWl8PjpFFKe(%YPO!aj2Ca!PH(!~&m{CLaJp zKK%+P{6w@b=+0jJ${3r9(pIY4W^X}nqYOc)ZW^9Av~L*6d%Y4~qwy#Fh>5nuauSE$`eDZ^#}O)tQYN$>uIU!AChI0$44uiQ*jK$ zhU}_7lN=N!NF%39tKHyRC8OyG-gt;2GAI^(*AdR{6(~Xjc^&!XPXz6z$Q1cAu>SmG z7!3m~fx*rWK_DFbfE4!n8}XFo7qcNI7A!s(VEo53ME^H?lCD4bU|2Ebz8gJylSb>#NsbeJtF*C~IhFEmk16F%~Cu6}XsmmpJzBJGqdtq& z3d4NT*xXcwWGoA})Jejp*42~KMRV|9&OO!(;2*ud2{yL{wQqu;)vVz+Y#6WGTX{$O zOw-+g51nM;U*_zg4?PD#C#UHs?BA76V7`^gQ<5qK_64x1kc%K>FFBm zx0nHi3S?+f=Em|Vjt{5uovi6WNn0-{Ur-rF{fk2!R_H#A{ppoNqs`1htWZvK@8y?~ zP*-K4Gyd zqUcAaaRP%7>PGF)eXvWa@kL1i!aWU|V7aJcdxLg?~*<-J0w9%1F zoGisNrQ5TesRKdNnReKo$EB4&qZ9-X1)%eLL#F+d6Ifa(%V*!IA@*dd&qwOa<(|C~ zhV*7`;MeI`$W;-$hN-i>I&yO7YhFAAy}3^hLx}XmE;eu&Gt~dv6X*MEo!);XI+UqM z^;_}-cXY{LiVNAA`~0@^r?Zt|MR7A6%nJ`gkn$=LojJ8{Ldg+dUNAp^zj*oH<{K~Q z^;z~4`RkeKDad}JK~=|T#%%?aP9)b)v&%3VRM}zU>*VABdi!7GIoS>zUk?xe6ER6D zwKqum7PJ#eLA+*gK}W3zHNR^(VREV zv{!XMVw7goYk#?K+o0~-&s9{X7k#TUx7&R|;+b`TNU+R;G;#2DTbdfjHHS#7xq)c8 zQ|>RgWki1F*>@V*<6D=z9ak@x1v^~=$x1iMP2S=(_DcNUmKdFyooaP~)4$Vlyl~D7 za`LVBgBn_B9^!78D~^|88p_@v5ntqLQE%$s5Sde!TrGe@^mp{_q7$?+7a+-8*(;~Y z%2WKeqnM}DE?#ch$7Dmp!a!@Ex3}+bcow##Td62FWgQR)hTlEieGeG+f(Uurj*zz) z{TK83bDp>D`I8!htm^UoytkBE-t`h6H>$`}4Yz=RX&=O6s?gFm2TyIAuMJEEs4}cS zuDWLWd_A0NimoL-Oy4w_6 zp6l?FWq;7%EP6NBhb56s$?o8=Myx+^1ZsJoZ~rEknSyw4Jz3-7LgSRC2O0=P0LjQ9 zWasWn-!BG$c@?2=>7CB-i^dHFcvljc>RIrV9e<3=R_~s2Gvc6pk5FxB8=2M)xlb9MqXA7Rwd`$UoHImm)%3CL>&O@fC`CRwi zzWuE}v%;hhWJB~z??n(v0S)Rr8CdHnV&y8i`-uYX)3Vm&FEe>qx!eB_0s){Sd%18* zf8P#ixsZABZK&yq+V9W*hQE0p=A_`QL-EYvEChX8R>R1Am_vCfvT@3Q z&wj9i1R1>dU#!EEd+67mJ13mn*g8$tm}pHfKDb@MDF9S`epPv;rjq5TC0@hQo#~JB zt0B+Gn2ujXf$mzu+4KIhzt-)KPcWx=^obZrP&DkK9y$o-_ROD-ikg?kF~USm1^gpm^m-1*)N?%%twcDYu%6pP!Q-dE z)Prc(RO_;tUe^>Ize|{UNf~*2;LL&{EA7`m&pwnPVuxNlfIJnopDuDM8}TBI%h^8; zhU?Wn14kZ4!e96J@gi6uga~r?tkqHA3*{`5V$?~_V5SW&f#Bx@NMLe^%AYuDaLzu- z{fHDY;BSiL)u*Q_GwSaZNRin|49EP8sv#BnzHmhQD&)KE)9@Y#|&BiL>Ctoy##TdQZ3MtXCd0d>%4 znmN^(r=$)8tQnNkLt{*wYCCQKJzpoLd-u6sM8|!1-$zrEHDHwudz#>XA&^7fEny0Q zAxQC$|CHZ!n|?*JQUjt9)#3_u41ynFLSp{G`MPSzP0WII{A}tg?Er&6=c&KeOhojt zncfVAozQNB`tE{sb!mi9v?|v2YMZeb#~n&T`v<0kIHaoTz4o_Cw%Rg-Un_%i+$;sF$Abhr{6 zA(l(djfiqd302nboBw{MP26b2?y%`e$YaQjCiMqiZkhVx2&Nd8VoA>(3L)5Bo)U(9 zP{{_v?PCsb!x1#8xqnR5OJQ&|M+MuKU^Q5mcmRPBP6v+LAdy37qu-%y1J$8D71K2~ z69{rD;%*!MnGM&eE**Qba+{9PDc4`f9W7-ugNVSR;^h05EN2u@Q!2)T%(*v$gugg4 zQXStUKhu0t3xKzdfK5eM=h$XfJMUgGt-k_@9PJa~b-L!RmoS5mfn-<$m{o`dwh7%! z4t~(ni=dnbqJ+_zNIA?EB#||{gpQ2qg-%E$*BAfY{Nhe8i`U6FZ*I?fbTl@J+f9Zm zj^SXa$@nWCiFbjk7HIME>PGaIK*c#6%+2Yr z3s@04N;!}yFSuQuSU-H++2AK+hL{)Xm9#-9zA?#f1CeIsbCu?RZcThmp(wo=#~d6T z@-LWcG79v7#0wpmRJ6>pCGhi;n4{A+%h^MPWfjec!JH2=yhbY^Yz(59Rbxp zxWGO2Ua~Tm`Q^E;No;XD_r!x@H#=J8K-6NoXg9J*`jx>sl1 z-YhQ%^rsN4ObuwiC0(G415*SDPd&Fok@&VyhO38=f47eJgHd~$7cN5PJ?ne^ZYaDY zL;Jsn|0v{40?f1aokbSa8H?HZk#>F&!mUr1-3#CfzQt7lZ{;uU# z?_oP3hsP|P-p@C5&xK6W7&C z$U|^4vT_w0<;wQ8?jv?Y0DO2(YTS$?*!4a?mk9bH`a0Gy{Iq^P7gKce7H3+8QPJ7h zej?y#U7XY~U6TFbz$%f{8(#i)kgiUdgf5r+KV4vy+A%$(_d8bijS#`<>U!yh#~3EeG!JiGkN)qXI>Cxo*5u9hymU43da|PV z(L{m7-C<HRQx;$PjBR?>{X8|$OYX9$u=+pRsBs5XTrr(_!?c@*X*AIm(_V)B z0&Sp0%wN9-Y!vhmYGOs_5MrwmY4dbzS9RaAS1FBpLU$Kod~BgfDiwQH)Obkx9(*CR zK8oGb%*7PaP(|67PqDbFm;hzqVNuwb zhyqi`MVL0j!cY!RbI~=|rn^vsE%d$-yWfnC$$vmK0_5zfx&tm#Oilx+EfU>b*7414 zn=3|;)KEj87xo}F9wwp1rU<>({<3aZ_E?9{!W`2pJh3@I8G|D395fy%jnT|JGbM&a zqy0k@mnN{bj(58aA2yRsVpGhgbA5}OaLCD-(;}RMa38FBzMu73@r?-fRTKE64lq_N zRXsC~#i67ej-j|9CMj@^MNCDEu<2u&=T1LtS_w_IpaqPlj3%P%EJsPW>kH+0a)85E z=JbehB+zDOt^O@)Sp{Q%Cgh}>b8AQ8Izj#6Yur>u~6%LZ!vj8vh*5c93 zP5g~Bz_#$Ys2TIgd(J)0?>H5f+&e6e9;zumCJ(e7%U%JlP3PEs;mmZ%5f9hJS%PT3 zWJXp^K-e@H&-O5-vRHUdgdhdN;4c^JcB%8AU$m1qH+j-_05@6L?a|YhK2lqggSo7m9*|W?)leO z4|Sjg3bguX++iT%=dWuGUMTK&`=UAa!eZX-Y=#KQuPv8}ffd3)M{fJ~llsTR4%!-h!vF#Mgz^4XNO z(9?iV<}>?H*9Y-{1N8xst4r_d!Fb4(^~BqM-gm4Hm1#vD)r4A+9%qRo7n{-48i8>1 zj#Pm4Vf`R%jl zR~av1sb(5SXZf){l!zkFm_|Eo@Fy+Xk_pzj(c{=(g=Ie$uAplo1bisLKEO+rQcf2& zO2ec^&34X=ZXX8qQgeBZNE0|od(wKE-~Z}+6JOP(Keran;iI?B{87EV*Y3q@yD;v& z{~VG9Z0{Tq#$a0mZhjuc{5^|z`wA12M3RUf_oqI2|8g(g;!Vfl*S)2;YbnJ(y7S%R zkT2~uA>e-1m9*-bla!OKJt+;`AH69d(BF-GGv?8IsMGoGQl7-00pMjC_VHtVr~|) zeRTH zZbVtTY7nGtX~u5B1E(n+jt%7#QJCvpj-bhLnQX`o%ds;yYR2nWpA~I#Rlm!O%dM$hc~xtVq8AiJ2nSclu!EhOwPS~l*dZrI&$F+nvW`jNum>C z$05rf-w%icu0NHRju|*3H4XjEhn`sk`Cf}O4npva$ur~owIz#R&xoY0m+Hji1=6s) z0w8_X+F0iB*1OvFyOInVod{gYO>$($5-FPO=xKVax(l)0c|Ka?%0l!g^ujChIl$tx zsU%oR37aLjL0|jdGUCP)>#2rQtG5lTB@9K83!TM#DQhq|Y$m4CT`>a+GQ>Y^sS7Vj z$7L4hZYD#yAPej+HhJ6s!BHq`LEiAHCr!zqz;B--w`o{&3U99RZoB3M?s7nkP)a5Z zj0Q@0ayZN__nP|qbX|!$`SrzY)%CmbN5l#}z#MSd!Oxf2kiUGn=Nbxn6mtPuaj}!L2mN_BtjpRm!$!KEU2;R?2;@NUQ zIqX_4pz95DapZF+K}x2VvSa=$#`_t%tI#<4ZG!#C`A#+Pva0yNcKi11{!il5vs-wM zvh%5+z!M|4=VwO)Jb_XvxWjZvOxi-%er(oC>Gcd8pDvv4!zz$IRi0lzt#EyY+i}H0 z#v)mdGeeXH;-;PZ|4>!4bgt7rXEPf@$%Hz)2}BEY-2Gsg{QM z_qm?iZo$pu&u&uH5O;A=hwolUQ3i$vd0fEnh`&Z%OlHNu?GC;tm|!nSY=&^xYXZ_w zxt#<~4v*?o;9B#CFg7T?cRxMhE}d@#6f`uq#a?bx#c>gT`o!J*9Qo@^?o?U>UI49i zHS)b}o=H=w*!t%$nSB@6wOd@IKKzDeq)siC>GyItSdx3X9aOUFCnjaWHDdGlu zsqg-MCc5yyl1deXQA?|A(Du!8F@61;Grtkubri_{#ETb*z!FDUhnHgZpx)bcx5n(? z*Aos;T+f&rnH)|s?aFv`8o6RoE?uaLnTwitXJ_yz{0p$F8jj z^GQ|*$bV^0hq%R0%rI+Xo< zopYy_y_}LT~FlK=XCUr9Q`%)gxz=OmscxZY({gg zy`=omf2QCYQW8ssaWw*`-IE{s@V?;P-=qd?o+w4X3MlNjcr!Fd`ob(fw_|zsUbw_V z{io$lSYqstg5D{?AUp3c>?>2RT7U_Qd0*n&vyPiKr_T0WhR&vM%PB+2B_f}q7A1VF zRIvDL#DJ;uigtEabXRU$pT-|NsVm2qi&sTPTgR@}0=yC}n`HMBunT(=eh}C6J6b%U zKj`KwX)}6u5maCTMH2#FdG}N2{D@U2XZS~G2hbjU(Bvq6$rTjwD;Ge%vfj`j>F_ZP z-d@v&=Ip={R3AR5F=8k?rOT!ABO-eRC^FFa;rA@YywaVg^Yuvun(jG4w_>6a&d>0{ z0Y8IELTi?5KlP}Okc3OcdT6WwVj(7Ttp_v*6t@zgPa!OBM@uml7fMJVo(ig|Fk9A0 zh!7Kc(UkuMPs+L_xU#Lk0n-sW>iV)BMJz9rt(&hlZJ4dP#!=-v^QiH**z}tdE^5aGcs`?FqYlO{2KJ(FCD}N$GIOM z!4{{++M-?V{E>my_tqp|XSSOb15ry;^%o8oTUsKNmXW!yk<>_z>T8Iz zncLG}1Z}B6rGmpCY__Q2Tm|m@&vncx$Fx_x_F~t2HCWT$^6f?jrt{#SYFhAk*uoXp zNE+Dn^_^?L-9Cy%zyNr51mJ?VP^v&IAF)Mp1LL^w)fjFph&Wbpk&3%dxbj2R_wTn4 z17C!bF-d-WttN3N_5d*#17!(^3U9mSq@@Iuq;1(jB6W%iMINEa!OH$Oj5QBa%x~u} zoZ`&LzvA6OGV8Y=aay70(P58oGm7H1ZXGTGUskF$|5DA6#l0eZdNy#9Z{5L>dUSUT zgG#;$P$upwJ~YM+I_Y5^+77}QL3})VZD#uI>@V%z%Fv*B>>6b-_D_8&j%`cIksd?S z%*Gza;~h4;3<{5rn{UG5<(y{B;!bLvMQJraVK&ewMQBb~cW5s@qzA{oVtvwgnOIR?v zJ03{pD%Xe60b7%5ju-QX5JLE>lY%BTo|&FtCkwqmf_&Cb4Mf`?uUcG`pc{05rJs^R zHA6Bagb-eC`Nw=Xs}`LL-MRW$+rX*!Qy=VHnuE&{IBwCEp}O*MXo%}jP8w6!w93J2Wu?dOq;%ktTc?ue z9{Jalth-UbGGpbb-ktDklobC~Ob_iiXt1uNOL<0=Km`Ee4+xf2>Un<;ffBd{KbgFF zZa2URF++_|gWngaArEkyl{5#JkV^UeZn?TtUa3{DqI0CJzo1&=5F`PB*|r%cp#FF0 zS-%duY|M=^=#3FyqXERe;M)!r{C7|%k2~C+2VkF;{j&E3 z)T?$jKdz8Kz$^&s8iRg0^Z5W4BLY6`C2s0M&u)Tz$0YWn{ImgKq=OBR6T6Q-OnEce2XGz@t>EL`2}TaMsV8=Y8nZFm3t0 zi2-G!Y5U&q?~YOOrxj{;cW-EKKKm)UMmF>=9CN9&YJ@M2Fec;$8o^hxREY}T@q<$T zv#=o3+obcO5fy_ABPU)0ZPlV6$#Lr+eI6J>aV(-9+xmt>$!M2(ThU4PrT19e#_n#J z2zZ@*e*||8T4flHIXsJ~XKp)==XQ8i^+9+Uu1EQ!C#&cZlt9udjMPZ_rW|JM+o`5#;GOnQm)Ea#jF$KE`Lg|_aE%+ z6r>Jo+f!;m)U7q($>ocaz=jCghJQt!!(cr zNVq<33pj8Tw0>EiIRUZQEjtWc{~LJ;HDk@sZCZ4Cpy7crx5GI%&ebK-(;V-&In`aK zqLv+ok*?V~{-4{{RTn!x7auw-Dc>`6g;;4MfOEJd&~d2r_dE-NT+N(y>9DBn5f8!j zyt>Vbwk39!lO9{}oheMba)8)3YP_}!eE~!gJ{ZsqX{M6j7p!_*@QCIE4S)SZE$;0y zHfYcr;rV;?oW?_cMsMEKL2}uywf&JWa^W+bkVoA%9ZMk3|8Q{Zj+A_Qn-dnE&&}~a z5PRtact=crb7hpyS4~L8@ONXy(&hf|XfWMAXLu_%yK188)pB;K{q4V^i9wQNbWzhT zF&>N;SLT&x)Ro{qG-b0tXZl&!UY5o}1TqQq!?Ve&YSW<%g7N8PQ`32P*zNW->$U2n3(r#LnLmOlDm= zxmHObrIpw~40Za+0pyY^myxpCA2Yw{+XOo~>j`DXzhdgn1Te5C$JFipbC?U|?MM`d z<5#}Cha;JzFr6b!!%N-|v?k{I!DhB}Zx`c+-`Ycha@-QZQ;lOuFMQX;&Tac#xWL7f zomFJw_nZf*5lv+>wXJ{PeDTmmC;u(1>2zo_DEZktIIpB(!|DT{@mPquwyalKS_*sK zv+-9Pa@T`LUi02R{UR>sE{cdTq-DjO-$Y)Yy$Mkn1UyH^W3%rhO5}Xt<|7)e*TUs+ z)ST3GPXt>f>+1oB7xI5&;V#Xql~fC|0!2`!;!tl{-H(W78rMK2Eg?g=!+=*E(@832 z)$|1TrUBoRm86`V{`bW{2rg*p;m+H#T-E~P-*2BsQ^U-m~iuz}o>6cI&XYKOKxVu!qb3E@o-j^II z?l1-DSSRESZdp#i4l%2_h18?lL=eu-15_tc2WXcrbKq&jF3&ilZU3I1(6ZM+u>ON2 z`>nxsz}!y$$BgqKbVAJQ60h?jcGU*n4QWrU z*7J0=fAaCibC>;zzISX1E-UL(9kUn%BXhPR>;~C87T=SszLJD1KRSH2c{y~?)y=TR zq(ZEc@qN9{^k)Z|eo-f)odOo@fuYCBP&W!xQIKbSq3t=oE)%(LS`ZC$Jewk9|E$M-%7b6 za7vP!v5kNHn}`e=;&SL({3Ye-&$|L{%%@@{rG*@lAY<56)uW*Pm=!BeA)YpsfCe-i zXP!_0dEwE(VrLKZ(+@N_UhZaQq-gPTC}RqH!068(O_*cg}@!)QGalJB2)C4 z14=db*kOi|FY?z{$qZdaBP^zu9os!1?y>X$3NiQFp8dT;m^K^r@~N-d9lhG+dQGzW zCz^D#i$ih0si&vkCN3N#<&hJ=-k6z-gX*_?w7xX z46qbwbF!WHr_P&0YQ~j7Tl&7CM3j?T-5Xvt7B9}DsQioAcrKlgc(}!H-rfv@!184m17Yr%ZM-J4?(YNWV|a&2SQr4rpd_ zB=Rw={Hms?0;YWk@p&bWxtg9PVUKzccmH}>1w8c(jSnBq=Qi0jG2hgej7+Py8rVZoaj#vYcunFvXN_5+rE5P7N8$TwkM& z-3Nf}?I;&^9M>iU`)$%i_Y^@J)fWSz@`J-4C;b&`b8zL;trc!Bt|Ha;+L)1J;#L`w zH(eaDC~gmz%jg8?!F^WmRQB0Wqh9)EErfqafv$~-s9~`cM6W%^W_0b0XzXHt^6ICy zQfQZh%R9);9JI}nKf<|@rrC@U! zGNgYv4ybY;-Bau-lVgzb2?Tt^VdjS*UCUsj(9b^*dD%1+2j@YK_qJ2bX9zWos|Vu4 z@@Y(jJ%>zFUNhz1=bJH$9w!yJecZq15OA#V0=JLM)3Nh8{twf6Qk>l337A}j=I=pb ziAG1#gx(PRnkJ+A5;dkY!A1I(akTQD$WPsFDwKG5Zj0+2x$p(rWb6Rsc|2>ZmI%cV zUunC_A~i#FHRXA}>T%FU`QNcF*aZm;1Z?92M;SZpsa@MH88RMsk+^Y0KvirW@eRv{*?d%5* zO9Y`${3eJ>nmfMeTLG4#){m7%RqV3kUK2~L(C4kQB1(B{Hkzx5+I)2)qJwYtn(?vw zgW<3HMeN87tYmKZk(8t&azh zhAqcaBL2*U`ur@wZ`isH^xLrxa?|P?Xe-obCa>HSlPsN!SfXikSSLVz#)f-!5_yXG zWai=ZT(tq2{U+YhZH@GUlC4mH{wKdSlhD^6ir!9lB(&mOFDKMNR~wy<+NYDCb>lPNsh>6!f1}Y|?f|KnctLHsp$eYL z_<{K3(a=#iDkbkn_6O_{FN!Kq@2C5PYdJa{%l>OY7uWuqddv_dvh8}<1We5`kw}cr z3^Z#g>|S>Wo!8{-=S{1MObQ{EOD!eHPjh(BcKwczaK3@^-$*ZAE#n9~HF{rT>y`Pr z`AjT(AnVr$mDwlf+fYBn>95)VJegfdk_N1OaVy$Ix5iGcUx)kk=!v2=vgW~1fsMva z*>0X6OCP^r$?gs?FHJbEe}Qd_PCA{Agw9ymY>7&MmFK^JU@c4LWZWnd)c=5g|1~Rr zW2mjWk*&!H2Nit)c=&Zj!KgX6lq1`8sP zl=Yj5uM=IkN26$-mEPuFTd*<~oz857ptz%M%>h>6r;pEs9u<(IE0##g9!mbp6pAPs?nBE@V2t z2Y0j-^5(q}L+D^be&3Emkn^cPB59URpUBm2>Y1{8X`pkVbcXYb^%${x-TVUw?a1*8p#_2%`fRAShq-q-j{)&<5RFlvOLqiKOy5T z#M)cJYJJ#ANW(#H63$}7I6|9c%Yk+s+Ic-Ob)T4JZDqbjO6gwT8&JS$Q<<;xGw0?N zTZGCJ#Vvtud_!`^HoN%8ggABG2)Mse9e=Ldzw>>!Al>NBS8?d?RBDxhof;y)`ajpX zCT_cGoc2Psgnp`mW$I#8+mEG@jKT)MLXn6e=n3+S`Lq2hxt}~ zdo8{NJEtSXGz>9!3_`U0%Lk}i3$ZKu-iRTVReq2aeHtV-86W|8BNq7onN(48<%;&wS?Bh$Drnb z9NYTC|D)+UgPQuju2mEe1Vm9lN(4ki1Vnm`p!5>yQlnJq(mPRUB2A=8M|u$=y%T!x zolrvWy(S^aee?T2^M1KAcQTX7%{^!Dv)9^tt#gRD6k%r79+vv)bD`}5R3YF-TdLz>E(9t@txD?krtG!1Df&BPcd1_x!i00BFru9Qzhw8 zD#U-MFu(o|5uA+Wf)J}q?87;5(((x$>XDj3G@U03yTlV-z_P7ocF&`wH64@wpcJj@lmftK_1}leRCk+}*O{6qWrZz=(Y8v+)oFI}KOBNwVJlIXkmSg1M63e03 zn_?2I_&zQDKomjIA3yg1*v`*5{sQtF(kH8AMJy5;>|n(0v>ik*bbb*{2*(8_oDf5U zhIJH|u)zsM1}@r#bMovxa93yRIX;_mPyJEr&wn|IGlU)xg+fl~#5RG5HoNpGF)Mqp z*Jb}IJZ-;OFmg9oG|fYt86f`&zIMh_`{OH>Ty!f>=}JzYF*A3ec;<&&*%A@V^nOLF zJ~*!*ZnSp=DSx!2K=JJlaBcKXG>!$g=a28ylW(#H4WZGKtMrSvGTewq_qz*D7lV05 ze)^6~E&L3voJWe0YpJ~@Q2o>tq$7jn<-k08#SNW(_@k+SqF^P%^0e5ej(Z8ozCx{B;xq% zxzTo&ajr?tcv~%w!B?YZuwcmN2q@jC5&kj=MUO#P4cdyL?~H99IqQ9D;yC{b&Ecwi z!(RZO0Y1+vgWYqShYSATH)@(K4MoPpcYbHr$7fi6@2~Q6h+J&bzA&h+iA-<_*=-4P zbISmJk84r&O>L3af5irU*s6YGq93#|S5-KBYO`)T_@bX6HQv(2{cnYz>7Tnz^lLuE z3m%rzL93}y=o-8RRw+<{8N`|{6-vt6=hpm45$#CC}hrc!u2*rj>9#4Lh*u1Y!T(7X}BOw z04?dUUBDxgA~HCw(c7N^J&5G+923y9pJ3Q_m3>_m)m^opz@v2vOH0%0R&o8EKNXEP zvRyp+{v45;5h!+Z`pXMn>h6iI_w-(aXnY zLxd|NA@wd`Z%oif_kqiu?(d>TPW$@_(4CWMee_nsD&sb4jD8hbdi%|CM0eI5h2>Kt z>4lTGjy?z4F~>auS?up}&|I*s2Mo)!#irLz@6lX|n~t=^dDTDT(`h?(4P&`AoSw&~ z9@L9q@_WBF;umZha3u82EIDs!^X2K(@Y7|o1oKvH1~wI)*cj^1f(i);QMB8;B^Jcv z=i04jX+&G{uOPR^?5dd&a-lYkKOM!#>}pgGv7-LuG54v`3VP}Ij>aN6gW`eS4BuM{ z6GJECKcmbyEyzeidd`2>$v=^{M5%RazuPWl@{7VS#4;Hu_Db{>y7t{*8zZF&4Kq=A zbz{5BT>F>WU}}xR2@uz^AX4MTd!)xssmh*-f_jv+-E#ok~ZO^3$^T1$;w^i*$hvGWGH|f$Lh+SIhCITnmLfbv9_zWsXJc+Myj@ z5FMs_E0f)bOy*KxsmsX(lOkNZ)k_F#4e#QP<>Gm+)9UMgG5xtErf~gDhT0(XO0Drp zaF}U<5Wc%xyB|!jwp~1Z?S~yGb8E63sGUG#qH{vx?e`v(ZnxGI?t@SHC8v{BoTu_GqwNL4)zf6Qe&N zTr2Tms3j@ZQ^9ZvRz#Sr!JX-qVNC;%q(cTL*NUyp*yuf{`hAYLE7VC0yy=+R4=9?G zyt;hEm`X(Mz>iD2$@bPWf$KZt_QT@HIy^@t6S2g`42NgTx^C3km%mMmz45~l8g(Lx zNNXv{$H5`M_iwwUgyJ(0vdq!+={kIP55kYp2{M^t2by9Y-Pp_(d>?6>R!}JfN)2O=)RC*%#Dv6EM=r%2sreegMwsY7!l=`Q~RCqC@IKvus3hNZ-H&s!)0g~xgOWTuK! z2xkxwBewjbt3u^u?^*pxKl(#(tKz4_W7V?4ZhbAE4*I712pPXz(asn0tAqr+x__4#R7GDVm$JamNzo_LUd3V38>o)?M{a@I4xf z6Ba&W$z}7GUt-zbd!Xic{COrUiPDw^pIyLu2BI?n zI^OF8=vJJ_s*9bZHAtzlbcN!0;pL&ny@7})?`0(mJ+eFkz4cf|JjB~1~8lklO zv>(t~6=n93vmG5UKCMA8VC5(6jI>t~uqRc7aeeJ^KHueW>r#Vm#cIM;T_JuPq!vj@ zKaCdQd;jZ^%rPIYFQ8?MbVqfli8_8ERaxz-Xsqu8`UsYH{f&XfiS@(yFR)7!PrDEp zW4t%9mgG_}Sm?n5@sAOP+|#-V>qPg9XSW{6JS|9IzdUxK;V|^HrUg4LOP&Mlz#1aH z3W)h|o)>rA(-N0wRqq~7qtDI0)Bp0_-#Eu4*m^KLF@i&!kn0k;SjC#OOA(Lh(*D@4%HxK z8@3w5Wu^03(zeE6=&ebfx-UBrJ9K*if?{zIsVVTaf${4Bimd(?NGI4}3(sv$IgD=d zdW^gl;rWY*s#qMp-$j=E`Bk{z9g0)j$QP^cNM9~y zb8PUPdX`7}?LMbJ7e=?Oin~-mjs?Q#?O+Cdl*-PJ);V19SZT=FqSYUvU-p^#PddUoKKfulr33KH-Wo<&%njl z=_WE8=XN~2h|rEK!bTt8L%9f&pNKBh+l~w>*a|%3BY8p7x~9KqEEpJq{jF^IeS=Y0 zs`8I2V}wy_mig;k&05&A9KHh9fBoYLcvZm^fzDFeUDtq^mx89-Mht#Sn@Dt;e5`w9C$t72lwvAtLGg;^~AMYCAr4Y7PWiEOn&r|n|CmpT zufo3}-V5fBScw=J6MjWepl081XGoY+CLsCIiXbh-iD z?xof$^?eDct0Tw#=;mL^d^np4yt|(fwK%(Qz}C3m@4pGM>Y)KtS%!$vilM#gl?QZ>x)HlV? z1su?8pE~i&4J6w!l{wg=Yh1NgpY5wOGR`CWVe!d`^I%60k&I+yRfTu^hkbk2TjN`B z3%ve0je+qbfyk>aueFzBzz>RgmYChG^sZI(t`y@)Y@3AN&4O;vcxWg{ z{q+-(pB$69SC-uD46U}mOU1sn(cVgTM|;gkuKI3QDL=Rmz7=wlO_{BkBCAU<26WG9a=&6vCryBA*eO)&bUo34em&ozA*N6^B~zH1Nw*Pu zhJ3Y|Fo6z^_V}-&sTSbSy6Q%xk5Nmmi&U1X!;y$0e9Z&m>mCz*%aRM*TKwpC*L61R zUl9WaTz6<_{7_;YJWBww2gQ;P4-t7z1sr?8LRcO?6cC?a+3AXy|_|wfE*&n3B50s?OKSM8wbrlo>(W1} z`q4j>!%jc2`gMyoH?7Nx5$cQF>b3REpWpuQ*l)->f`Uq7*47XBpAAlCk^8v`9-h=LV>*Ps_*V@X;xA}U3J-9 ze%G)+SoqH5s8UL6l??v9%Hph_GS|U=U=0zr2a=Ml_I5=oZGnl-tbZ$R+Qf};1wDrq z5uj&PlVy>8#))H_y#Cfds7iNW`gowk1IMvz`Kbq*VpY%RCyMWc`WSv)wZt?aQ0~?+we!=ZNISVLc=R;$fJo{dO~79R(~iwnNP89zGr&G0$*wmsWl#q zEoNNeP$a+B3Ql*29^Hm7x2w=}JV(!;TH~JWEWgRQqr_0k3f6iu95I~c)#VCWrjMY2 z`otHXWt_j;i|Fnn7`KeR)g_cm>zqR}CIBOOd@+5$V6>V;^pJ8SWH?*ypWXXoKij7L zA92)RZsD`Jr&kvXxz1Zh6EAnq3*9f=3pG>h9!_VP4iA>QHi*J17J86tP3kG-GNu=c%Vu#|d~o)pISn+|+1ykYJ1 zqO@QKDK7P?{8)DNtAiqg^D^jjYsD9`RdOg;MvdL^68%Ky=I#NnHhO&gEj zhis?r)8t$T7uyE-d$+&v8Mj7c7*b2*2+loBdxM=E&b@#JsABZ1V7gcTyFjN)3#+%9 zf&aZ92Pt(_DW;|wEu@MecQ@n=W8xJLePA~9v0LM)ogT5HtVcGjwDf8(2_*& zebA7~BzI>f{}a?4HAUy1ek((Hv5l*z6HX1rR|4Dnu1mwSP4h=XOy*z=|P@0aPN^|0{0;oQD;#BW&PUBlRJFNjd*b>sL(=QC5GCt-soY0EU<3T zC4O}e8vADFc*-KPSBm=mh0Omd5&Sn)H9veZQ>Q#hU}A1X(Io>TdY9adCeBRv-wc_FH*~ z3FKOPAcH{}SQ|jL`Wf&2xvPuq9-FFy`SDy8_kpTM>t5)dL*l{lv(2^#+Ea{UKt#d@~?ZT|Ig?{&T#s!=o0rGj6?Lp=IsO10PQR8U{g(D3qrcHhobr4M7w&Cz%p5*&dvmH>3MHKztrBJ_!kgwAAkQJtT?i76v*BKvL zE2rS%g)26A&O7IK)$~2d%XE)>5NMB2CRdP(Ph3qlr_K&h7CBJc3*XESV<$r}? z&zE;!l~?i)p22-^xITq71p#f>f24t``nVD6OZ18RSUzlTNj`GJYkG(C$s5$=+RvA4yw}aqb<54 zsnGdjx0b7gjrc~;#34U{dkv|E*zZrws~+}$COqVyBPi$k#ul^0rM3D8KFA4wGaZjt z*44%McE%A?eF+j(1pJXuw;FNB$!Fgc26zyJeDG*`VtmaOGSk3GVbn$7Wi^EhINME( zVZIiWMQwl6^2Q2U5Bx`xc8e6;s1lmkJs)$ux1%qZL8$XX61jz1YpDDZSsLPe=RrVv zEc~bhgO(kQC-7XgQxi9_iS{Hhwp^*$!N&fL7UDCSW08t?qHMu{2yi@BdErY0DW|7t z_UsqYNr$-!Bs2#D$!UYYreNXzHz)mla8p_lA-DJ$ago3)v0PX{7f2CM7beJ3c4{6r z`E-HUs`|cmWk!hCPVJkPei*#g{VbKyobWv>DIR~iXXoAaztv@K|#%J1%IXPQ~OgPjwly#-!xslcZ@Y{(bPP@O^9XM z$Hg3)mZgpSz36M_=qmcV!t*}3S|~U`x$N#o<7HL0|DLtD52_33ob&Vckj`bWz0sZv zP|OS2;wXCVOE1cgOqU%BMp7m4fSS z|B4<~Ga^B-0IHk1?*l3xG8Oj1H90^{uy2KrUyQiPt&IHFg7-#*{ZMm?cX%x# zo3egdneBA{wy>^COytPUXd$D=PzF$6SJ%Luaw=9t9|{I6{RM&9gK@WD9X-qPoGWi_ z*XXvclp_2|`o9{KE}0vSIn0s|Q)33@3lnk8`gEJ|r9)f-CY7Jc&H=4nKx-G#mgw;8 zv=|4o=_6)+B1V*ltCAKF6~>=lX|@~m_p&Qvo`NW6fT6H|$=IdB$>%>s01q5plWE*_!24^-RClJS6% z$g}LS1O-H$f>ue#Qlv-JqoNmKN7oEZ1Lk&$0~nA3Gb9To=A{$gdAc(ruZy>~4y8U= zvQ?$~x*7v}`!etwgkw$={W;={+!4=Ub^(pw9nEOH#+-$ zKFiz}Ad_5Ow}Ewze6@?(beel0O@5woD9Q&wKVlUr<8jr&&jHaqLh&BhmiFv5i_Ro4 zMS{%S$t84Z=4Ynwp{Dntt3ic@>wEBgq0Cc*1$8h%!|-Y@L*jPIX+CBvR(p3AsBGpyoBR<=rk8fm>WYE5oZe~wmva$I0od?bOggnDqOHRV<^&|9#_M==rH0jq0 z?k(QdUOL68JG3?+eROiBlr_TPmr5FW;UDG4Zo$_2|f6!Zp7r|ZzMi#@E@(*JTW%4W+ zk@AgVMHd@R0RmOVzBb25`{}&nSnYQDfKiLMU)w-0NL9_6v))O!$RR(qGp9@2Fk%p0Cp=k5Jf$nt}qtd(Q6+ua<{%1E3N{+Vg7#smO+>`_C84 z)qx4AQzc)A(u}S@$i%e@6oeN_2%R9xEwH>q&Nia-!Ld^^S30&*n7>=TZZ^R4t2yjG z|0_Qt_;i2;>k{-aPz9F%{f7b%vlP7RE@97VB39!xSSWkn|L*p4e%A!KvnrvNZ7DHI zMz0t-gUbr3t^d3=)o!Mc?qXTcGI{(bBzpC8FS`D1MD45ag!`6CjLeuhcSv(g7S&r*HbN#7({dd4G zL7e6fvP9ADM`3^h>iZjsIMeNR_$C3R`!|1Z&nzN7C@7iPC+OodRL=>ddAYd$q zp0mrLg0RZuydEScCuerP3w}8`!nEq!&vyHDqhnpZ=(m|@1Yj#gz#>Vg^v8+`yTCqmaz*(V>Ua|Nl; z45r!5NuKm%Yas##XW7f84a_n5-6x7Sd_n=2F@R`o^Sd;*$sTMK>V`Y+&U-pXDUR9s ziTd@N$;9zI#q~^=A85Lqk4^VF>7B^os31Ca{Qi_^rx3HB{i&E{7rX-}P8OS9h&^Sw z+yp7(XLTDbR8;M3aky@+-FzZcSV`ya+CZFQNj2Nd~#$Kd}H6F?QNLTopto!^_S;8(sGOxkCa-ED2nPy z>F!ARx73t=o)CO-BLiLA^Vbl$c0W}zARHnsqZDNnN@JqT-?!B=-01U_rYoVv(!nhLjOxlDl?$oG6lQ`!pL05E3Q`L-$d~9Qif8cx?UxwHQ%wu^v)?wb4J;md{#+ z;C<`YMr4?Mxamt-#y0**WJHbvKh{;pZ_yhU=tjn%+TH1py-sEv8H?*M{~scxus0Zl z%lhdy(Tk{N@B;Mmb>Km7!cWWJhspH)OH=8}umwT#C<^ zG4W#F&dFwdk_}cUNfa;D*CxOrgsVf}#|z$M#77A9 zW#Pw3O5xNz-$RIlHA1k;AIDTEyUZt`Y45TvyR%3HLy7PzsBey^3BPSf=shnY$n(VG z^)C>XeP9m#OQy2Ad1ITsJhIDj6V|3;Vd*s&i^P6zaMss|!yICynfgaxW#y;BU-aG$a(UykK+qMnh@t z4D^`7<%5a-VL$%oGRL@Gl&?{;_e7!1&*qxblUp~=6lJD_W5(Xn0D2gdsYQ@FFj7$)Ee&|R&t%@6}m{0%{ zv49oq=i0)aW6G9PfOvc_^_Ee++;}bx7J4?9JuJ%|9`pKriaOsk@iT|86Me&HhnV=& zq}$`4*Y4RRGFog!zgXrcHLV{LUz)w3y*wh%tCJmIjBs)=7x0qJ2bZH#Dlu#kY!y@@ zOBo)WRb; z*KM*NhBR9Ibs_lyI76;Nl*aQ8<=XHyRkNsHxvFES#Qxh%e*};BNq=dF&JtW#-6o?wk6`B{%n<>8Ih~7dU7JE-pHGPVO9ZA$I!3EV`i< z>EO4TRJhK2Si~8e60)hEe>Mz@G0X_~8w+gsd_rnw9QiFP7P_D0XxydZnabIKz@Ak> zK@G)D&4g~A3W7lfKHh4Y0ws5GX4D*ad$I*%m|I3P}gC%S{npYbbA(~)s zw}kSn62z<}uY8wGM><24p7cc(r$_1^t+xx#L_JMDIO?ao-cRGdG+CCn?3G{wU@PlxkW`5SZ^k>l6=J)0ws5=!3lZJ^zzU^OS9Sl`I# zznzPIzdt%~KL+)P;Lmc(3)9lYU zm|=WvPC-UbCbw=~DMBv(JxehIR=c7h=H1a1N$)_xdASehpl2EWySo|9ZP9Ws8ubea z(CQPOVdxKB_d|zhEwz$2LGg%ne4!-~*21Mw+OOh-_@GHhnRCL#1#!eQaxSt2<5Zf` z<)D}oGHhdg%J2U|80Df(Hel$kL)aWg&KjdyDk?Avl>k40t$7ac2jiWLuTY-nKz}u% z5SfvfLnzscw#8451I{qW%GlJ2H$6)~IxmH^cB+H>@r2eXFUxDPwI~iM5UI|JZ2Om# zO+$0X8|dDQ&kF+7WRm3WgUl2lZA;_|abCDQvPJaER|?;w>;^ki>6QDL2$>c|H%fap ziPajr*JDk%-R|hhKc4QtUbQ6a4(D$;kXrnE;Wjk?E9lzpPcT_H8FS!|)WC1+6UF-c z(=6Ff$tA{2+qn{O%HKNQFfYX66ajJXq{m*yCv&KrNjsrH_G+$lG}3LAO7zo6Q(NCP z)b*Qf{msOg!JkTCn)!25l+<$CIbtJNczfG=BY(X=G2bDSTHU-WLW6{qn(i@qTaR*4 zH}tfP(q?jJvQXJ6F63EMY{K|^e!y(-3HbDE+|YEM-vQA+Bjnce!TF8>g-nb-+%(p7 zq|3p+N5t#sv1Vod1MG$i0fz~rTBu&|c>k!O{eqf{fL()DyU7zgt`& z_F5tP+9-fc-DRtQSdd4NtXS_Og^^-Ag%e{Za*x0P-z+8%FpLNVD=K9buh!UWbv}4F zB;Y8K*GNTf>~(LMIDHXS`N z-&(qSx}bCABD{VcM5N@{wQrhj2z4D_Wv}m-wjpuV-K7WjZsn(S;Aek^LD=~E ziW@whm20@M!2>+xGcNE$i9|9=OPqsVevVx``~kYX(*xVot_FgKdDSR0jMgIQ6e;Z{ zo^31KQ96ATKd#_#B5a}L7Y&K3v~BNv@EpEnwUx+c|34y^SX#%46D^T+hh5E@WImpvkIEn-2Dy{#Udlk{?| zlI@C8Yt#xy%~F%6ITxvstr&feM|-`)HDkYveBXuv7j%UX3@Q)XI`QQ8kPs;0^&DMFJwT(WJ^Y(sV`y>EX&g zU_IM=sESYIR}L&C#MXw`8J9~{`JXfu?n8&wBRLf44+lNYhc;lqdgTQfk(uKsSdvA> zp$_vma}vCxb3p1c)^`!%`bMenY={jNz zai%p3)s!n>pN2`jyH~DgH5DTL^K->P!(*y?x!3khRU9uD6r?MP5 z!QjzLA{9B?w_$zZYS#0j^8|)}U2vT&qA^ii^v=+?aLRB5r3m?lqrrjmrL8{idrKO& zQAS7oA6^~F9<+#lVv+fHp?yzcb(0JC2ktP)YpUgyP0HRiEgej23v1xPG<1t9e-QuE z;rTZLB&9^-d2Ge@slMGapA|D52$~PaF=nXUm3t^!Wpm`ZE-zsw-}oqvNrCIR1#Rz1 z;%06A(&VSd7XJ=9CL-fUl!tC98J}`%@vmF2KK9t-G||r2Q1{($(yw2}GL85>5zVjI zLKtB*efW>lKDJS$iRSQBNn)y|O?R)`w%Y0_o@FMH<@#TItl(KooOfw{52e~J@8?sk zS@220%Q#F#y$wsEcr`z?c}ld0IHoKfb&2Mr!8`Zu(rYPeS;Vi&cmV=}&n<0UxPgS51O%M?`Zrtte3 z5#?yNNhr#RH8VHIeu6Haq)2y@+- zk0l)CgY!CB|HkQXQc;lR_+KvYv-l5;E(SB222I(^*YI!Uws|A4AAKwiIDE#rnC@Rc ziDnRHPjflpMjERXlPbxB)&N&Z`W$$`{;t_{dXzYpfl3cK=00sqjBS~n&uP&)VX55?7j&$w?=1$1#meCWGxf5>ymP@ZXRWQFsooC>|a@bqcfMlm#@ zy%%;($rPpKQL`rXX4)VeYi?_Dq+@%vm8hL< zihHZ@^G(`$G}v3`1Pq?o0knXN2|{}`u4ui`dpp#J0y?14TctXIdw3)alos)+H0$c4pKptCKvuWdAd)diRO+^1So@u2 z(AbSQZ91D_!V$;4xW$Cwt6LV93ja-;#v zO}S^V)kx7i9zMi;(gO*)W=Eva-1vF2KTJpRN+FbIlgEY!1GlWFF3x2Uk@YMS`QBi{ zVt6G6v@gELl&J}JfF%wU6a0y}x_B1!HwLtqL0GfiKITdv5k$V`YQ6Lxd<*Qfg^!K2 zz8M>mgy{ZjYtc^zh#Ds7_3N|WwC~({`?%tSS-UWI~>BheSp- z*|82|RdRFeujqiP&#Sw|w}}cSi0wp%XL%%0=CgnAEDsts{Y2+hZ+3GD{K;iUG}6+) zUHIu_amwFykP?-4`4;IAZ!g`@W&LpHS=prz#xwAOcJ^K=W=7l0`V>Hi@=2!b=1zCd z&f<0pozJ{-L6Vw3etr!)e(dk;?7{Jbv6u|J>s;&zG?W1<0VlQp^#~WAKqq1R8Cd8P z!}-1aXd^Dz`2JJTX}$Up?7ac>qjwPv8?Kdij!;ajCp}}a}$gx3rgIsd4GFZ5u}`GT6Ea=5$?1~BlXz|X^B0Xfc6K10?;4R zT1#Ye5`vAoQkj(+5<#%BY|iC=y-%du9=>;GJ^oqmJ%yz2Aj0iQ=3ptoGgU0CKu0+7*(bE)}7^Dr8p^*fqV*- zrQemC{vw^H9E*As#OiWnH%4t}h49*5IScJ4{M?Whk7Pxb$8S3y>z(+s^P{! zu+q(KzxYw#4Oh> zQd(nV)Q>D*GkG%oa-z}gGWDOLsmEd_eMB5$DLN{Zd#I*162}IrAgxmK*R219jIjyj ztQha<26@_|ZG28_SU4}fPMmEY=8%?<69b4hd^%@p*k7~?b^+H~W%rl(#lnC6QDud9 zKML@)O|DjCeH#m~Z?zI)MlOCtp4H*Edj*p0})fwf>O1 z23F-UvhP2mGLcgGBWuF4xCmzRT<-Nk0X@68jgK4yo?SGn6aHLDI%P4yr&L&^6Lg%- zF7F)`uI2J1-Svg*Qn!%V-jID*tmafFmxpsxtLM%Q!3t+&wV9vQ(1`d1G^O-}EB9 zKjZ|#Ht6ePT93(_p|eg+fH!Zn1a@Sr5_TDDI8T7&;?jhDGQzSVbApD%_LD(Y$Z^tK z1$9PlZfCv)ZSL}4J1&jpo{ZRJ8;La{8A8@Jl#^u;3W~{7Psww7ca=-ap^(!q#2~FF z7*F(@P~!-pnjSliXSskpw94}f#6{I07D)b`K2xr|@>sgf#f%W!49TLf}TM=_OW3S8zWSLJfegD#lN3e}}l8^40_~ zzx?ZJF7*B5g=bf3L*6n+NIrM$(<|>3E0zpSq_?5S9hfn61B~iHtg;+V)Xn(X7VdkP z$_$hr+xJZYbMdA4s+|uFn%QG(y|LqrL`9E4UafY~%5x0D5OoaELFU_aV{4lB9c=0o zi{{ zaMH{ zVusUOjtd{9$C2(zde1*^l4@hME{GDt?>o6-+J-Rpp}M?#oL_y|gmfRx}wj~WiJ+!N1XFb`4=Yk-$ePs4W)w;af*e9Fe;HjaF`;xNd72XI4 z>}3@(3?wss1l}GP6I_ydA3}cjkA(NYvJ(7)e5;X$^*nxsa_c2rSH)*eJo3u^T7d4w zre5}Qm;s3{{>>=T)9rX~Bns1d&R|w|ocK&`aXawGPV(Un3Y!=}U$hpjog|(w&zW#~pnlC?HTMswT z);RzM@dmqC(rt|Vl^7`x8LXEFUHk`?h$2YOCs690Smpu zec#LgH@BqYqHgfsNPDFPW?|a|%Tz!$u9o6%bY#uqp3{7Laq;__av31nylp`hB7VPd zLA8!jCJ-&ydAM*Wp-uAJ-wqwK6H=LP1y>fWt9vIn0Gkk0+xn-b0fKQwLF z$w++GnDg5my`0V+?Nl8u9A@}VkmmQc!Tbn_`O#P=Ti_UAekgTqzo_U80ac~>N+&0$ zx}{$4Ccm8}L`%6e!oR0i5$(?*FId{>j1vF z>ayvwsqqOv%TcN)ikm1S(p)uV+K^T;EJH-01FIgMBqt^CPfJdJu5qZUcuKK6q2DRz z(ed3x9beHz`bOFs?{oYD0}@!_5%}3r|2K<}#)hK7@xj{c3#J%9O(2vH;}P2tqSg)J@OAi~%$RpG z;r+IIG$v9#G+3Kt+G~ zh;&kr8BPHJ?;W)ZO&U>B??f{NRYzYiPm^XCyxJN9jtyV9RQySvyg$FRWuV|R)^PCg zGSxb&mtZ#X?>3~{0AMdwDyjGj*bo7cau7l6A8>8_cm4?iQRnPPEE? zJppu=UE7^fab?POayhUopPd<-#0UckU1UXBxpzP8lcnn<*Mdli z{o+N{a9$LO@Yu~T7??ii_u%igXPVQKbk$`uXskylDkvo6)Qp z0;ixy9qb-P%>O-B5vSg0;|O|Qob#^phdueLC$7D8YuiV<78LbuH-Ef+6{gR8Gn`!< zJA)yg=V)N7N|3E^wvp*U7=hg=-G~H)b^_CVd4*MK(@)Mt)zAC1OpfWoXpP z*7Z81px6ntob>!$pK&RrE-$A<&Z>)@J>R!Gy1n53$G6Il z{u`jzFK)!VU-HTDknM=Ybu`3GM$lGxh5U+b}rANcT+eiu{K9CH0N};m;CI>o89UKqqh&6~PfIM(_=K zcmhg2_*+63d*BG|ES!7uZlL;;gs~QihW5JuddCR5>@^9U5XynCIyDZt=N=XSluI77 za)K;Z&6#ufvb$bN80c2)vl_RfYnbF~5n^QdNXT z_8Yg6k*?EEPoIwU@3W3k&4i4!LrjNQ1|Lm~Geed#1ljtr?328wJA3g&wL3yX&ARw1 zaCh^m^4Hg&O(aqJKg!;eTZJfBfCM%FlSdUxiY419B8G%FEYqfqUIRKvn=Q{H$O}581NV(+7*#kg<>bd>f1;zyLBU!$!fvINY6aAR;?#>Q2iA&B}Q7 z`Av2LXC^Oau3lNQtIu9XfgVo;N{RM6qlex2!DELc%@(vWShK`n8szZ9&jHlnBV zdI|_QwXxHfc2|=N z(L~W7QM!Bx6vJkNSnUf6Dugj>RT8LyZuHqktnO?%-Xp|hrmTD0$vZT%AKHokdv9y& zGyAP}2Lv0gAo|8@0J;Fz2GQ%B*lf2)K?{l6^UjZ4BS&Ci7Ytv{a> z{_@d^0rnY1!6cMzfO$W*xE0;-9}qwgtaRH+EmSqq#F0Hp@C-=@I88N>chru)JB~*H zIAich)vGGm0T0!skP~{b=cd6*Xv*)Dv&M!8gN@zK^bt6#bWR{ZP6z?!AJZF+9TrX0 zZ{<6Gpm=~dZ5N@UL44^bTYWOrP|r}=dPo2ZT1EuU?~ZYFkz`p(S*zXFh48G?{MTYN z{>EFzNh$T48znI}yWU}asE7fqj%SOH^qExZtYhW<+fDQ_0j%H#S^-+#*1J-9#3g}H z5BX+mm9|dT8>m9N-U|&%IA=XWP9}fO?4(6jIUCJz^1Z>yN#DbFHV6sMUK;87-s0Hj z^D<~~!z;rl3$3VHh>rqKa+KQ3m5FcQtxEfn8DNYj8REO?Q(|B0mDdKy>2k>r=m4Kq zbpTo<*%a2oBHVb*oN!Ck66gnfTu345f%({j9AIJwNmurYg4Lf@vE?~XlQcqGxDPvx zB{Km|2}NQ4ZdLM(#mHf;hA-FjcdTZfC_hj#KZ2!9N4UD1Kx|H}AcSH+bUVcR$!zwj zxl-;}$4@)>+zlIE6jWk{#j#M)uk;O8iTXemtz86Lo<6Ie=++2GL-;FlZE&dW-jkto zRY&@t1de^V3mV+4)}KK%AFNoRqn~&fb2^v%%#sPDGUv1x5~R}zaXm! zTSmMEHG$>fDSo2%xVrGu`4v>DMvmDY)qP#VtM(Aa1DzDFJ7Uyx|J+2z5v z^yOcPsF&IDxF3+qoLmvM(DqHdjw~8 z=O$ zKX^I^c+In7=8B81cGwquna@|8=dbj!&SV|?+3jVx><>3RREP{FRMj6{+cE?izDMf+ zcy=$C4@SBXMkuQhR0E;0{4Xk1v3Fv7_nra``TOSYp_e%>Z#GP7=XFUL*xmg4t$G(L zO3v!#qd24&b1eXj-tuD%rQ@A}s$D_%{f9cX^w@}g5 zk-Ewmu`*mhuaFn_pP1i6q$d7pqrYK!OSrwBeP!i#iK+fJ*-M4=D;-x#Q}m)uUYB;bP}b6{#E#gw*+kQ; zJ8g4X_rW-gmsLE+m?8d8B9RL+EmU{ zZX9L#F&mc#y*j|Tfnv@;V(py2T9wCR0iDXXx%_-8fqGzr|0+ z?{ACt_dFb&{4G8UIfY{0)Tx)TJ${Qz4hlpWr){#X8HylVZNWEKqC?nndeEJc z#L~6U|C%=f9YRf*3>4iEof09>t za2vH^n4T+STr>hg&SvE@^ILyIpZ!#59$TFJWU}TFq#p)X<5^vJM*q5j0X%pHa_>H? zAt+v>MW-Pl`;1E3H-bu@o4BLtyN``nT&X(^+`Bq_LfZzUim@D9#2D#7*s1ufsG;S6?I3mSS{23s| z&7mECr)j_zpm9im`j&j$UELDH*p+%M>e#hip(oTU#3UrPtUR#M&&I$_u)8DKBg9nA zdzZ3X(|cVkV3{mdoZ?4<2)wMehAtiKM#U z_)%rHoq)vcV>J=`{ZK6|!yQ=ZfeqC0HQB@jI3#V7fdoe@Q6KJX z6vbrhg@P|3uW(b;EBS!_G>wA2)RYdjR)P_<1|$fBYL>y?nD#J~vjJ??54Ojwq5flE zDy{Sau|zB$ALqXWpS9pwU{p%DtXEmL9sVr=c(CE1%$ehPQ%I|dJT?FX2Og6(QD_Vh z+Wz{`F^z;K=52vw;A|EfDY&BpnC{=+s68N@t$xju4815-p1|fL$KDZaiEWF>5>N)m@G)1&(fVbPOcUt8bF%1j;0EwY(oZyy|7*TPcHT zXPn{J=O^6y6IxeRq-I?&e)Iw@yU_l=y#2=R{U^cHZYWfr_AV4hpq4Z^;iMSf2_GKc zNnSmG#N*-xGV4NO9$Kuw?fW71rrzk?QGZb>P7~wxRUrH9q<)8mFYw2U;U#u>DZ&hk zcqaAvL6D`!UZonJ22Pzc=Kb5jXbt%iiFH=bc!_lCZa1ShX_URK0&UuNhF65GMu;w_ z<1KmcrN6tpsf*`poGi)+sGHgq7*-yy_#}Z~K1yZqcroU0hk22ndAWSH?s_pK8VWt* zh@bjNVI5A(xc;k^6Xx@r+3&AQGNocih8l^F!v@~0TuCoxVFfLcdX`YE^vFqe4UNtvZcqM0xxGQhj@Cne{gx_vfvi*n=`JEnj)`v3* zORnmxz9^dw3;$Zm-Cn8pZ1nJZP+0%%GszfqY2&e~%aw^U_%W;II?1l9C_$!@Pb!g& zg+#~lwjx)yX1WDZw`s0TVeMcYFt724i~%<|r4z<}V9bkJ?elJ(&m76#ILsVs9`F`(oong6_ao$tKXR4UScj4rw&TT;rq5OPgEMoka0XSMM0MjKWC5L}D;pBGB zD(J;S6_csbfHs9)ESOqy*#E@7@bK8nNwiFhCV1IYA*{m~a&br_eP-;k@B{lI#oEhk z{+Yz`OK6IqRq1U|s()U_Pf{H$2PON;PjZX=7#<6oNif^j9dQTwao;?Qyu|6bQ*Lh1 zpiD@~RoKAzd7He>!rlpwEaTR!l)Z;7vOP+t$3P~@=y;NW!P0p7j}aWXSB-Yi%Gr)x z(YpX$zPxpMI<^8YcWp+uQO5YER&KL6@-k3#7*+KSYY-1AOfSTS z7FL=Dv|Jvn^iI$pU9^6KG(7wf=IFX47ubIGn2ABcRKmLafoqOky6Obl-^R5?or?6~ zA^nYbmwbfR-nX&5!MHI>**95Wr;NC1Jn{fqAHho@NU+dqIEYzi%rksBs8n%Bvmp}s z2&_QB>J{5Qk6s&43V7RdU{Nz*Ok5M zszVf^|BCMJStq)%s8oHU=P3GjZhu}?`I5FWYbws1J`)e!emR;&kq1L>_F&zGV31F1 zwW#CdJCYY`CT@HTgm=}MpFj3_4!>D0LH~Jj`ZCM1gWBFM5$JBOj6g`AugI|7RTWKE ziK*Ae#;~2rcN*TMn9FF^!nW;{c1N+6$?l(D&UW@l53R_QdgKk;(vzUnp!VbxK0jP0 z%q?H1uVo9ADSUrmx*2LtSsDvOX+QGr;8_Im-7HwaguIF>82Cq2D3*bb zruEDgeRp|Zu_YZ&l!1*IGLm_UkB8;#K-e>_akgNl;$&&e#HG!|(Z*?sUg~M@6{yUe z$z$|8HVyTuWb1T;70Y$Rap-WBdh0$U=DE8TJdS?@daVA`;QgW-#P>XmWbGoZX%^@& zDpMtiPt!Z2<1<~qqG9pfj498fo-Ji-a1-0F>B%mLg^e~)sZ9*kEfyy`xL`^=m)Ft+ zA&$5XZ?X>u0oMDlBJobu@-4k5yJXxsJPB!4?8tlSfVkfvUQWsg>~c=K@GKoB^E44v z@81mm5qz0f@^f&m<@;0!-_;S#(ivou;Zd$mgZJgp1s0!Z*XAY?JB`?g8elz{vLnxQ zW@-4|yZo7z-LjTO`K<;QlU`G0`@hG_j`E7_u?ng?K05tf576abO#rW3&H|V2 z9wm%J-qa@iduoO=m1-1{h(#M4ibIXydU%PVm-)aETXP(Ao^riErYH@=mr`^NFGT+% zE4}thhoRm+r(>+a<`g%iH;gu@dqvW>3l%w|xNUoPy6M-rE@VENN%!j#-Bo<>(3p+# zi#j}vb2T!UNcc6|RK<_jiL1`w$#?;qs}o{H|76zGke?K(qU>*re}FCItqIER!np`r zR*nLKWxZB;DF2YO?2)jI^6bLRPN~QnA9!a-GG`n>y))V$d0Bl}TKjuFv!eJ)x0A|N z`XMsD-G%>atnLgJ`CpHs^QOa)#cVeh;~OYjV|Z2*kKcFxrE^yFl?5QMkH#_F3-&6( zXt~E4eg-$%&1J@}cfD~|DsL$4%8Oo;jPF9(5(U{Z75$lSre5Esd7oTa1^?bhHWt@Kt4~}X+|IY>~`#%FOW0=o4yR(yb(Q?q0>P!qL z5sPy=;TFX{jjRvv;UlBtiS4fL?& zi8^PRM<$G)9l7lV!W37HsN$ri6qkL4mT7X*iUH0NZ?*{UZ~2xPG|6ePUL9v=wDk#o z{P>@#%vaM>alWmnz=JIGqanleZ$Yn}Da4tcEeQk-u{C}jH^h>>w0}|L{^GE1WEqux zzoBom;5~t;4Po1m=#o9@dH#E?_BtkUofA2`m*0MB8AUfkerwqwC7*4`&m?2FGg8&u z5`vKslYLmMmISwS*-dDk1}u%%_Te?$dNlM!7jQQ8Pq(mRxW|gunwXMChCuUrl~prHcayag6-`2jNjEV=Xc@PhgQA`a8@+5R^XIA zZ@L<++b3*`!lYewAX8XHD=WVVqmSyAWSHG1?d&Q)i*(mHWWzkK+pxIAC@eR6it?pq} z-ZMFWG@>CI-iznWls9+tP;uM3=oS<+fA2^G+9OmAAKGx&U}gnYCXpvV^f<|4S5(Pu zFj(sdLzq2nzHI0(GOk_J4Zq;9xt(6f(=W46bEB0wNrgz|X{BM{W!a^8@|C~FtAjBd zvEDN))BEz+4k_blu`WR1zqK0o;UIiggJFm4>$3lqeqj3q#B%@2KTi0#8ti{Id`w4+ zrS4Dx9?|xCn{S<|)G2}tRBkPkuN4v*ls?l4z2zdGzN3(JHvt61K_ zB~bg}gD+`?^+rBom#rhRZJX2!GE{#urT&vaWv^*T7QYBCf-LiNPWlKgqmF61Li{m*4zE+Mu}Sr z5F%QyyW2ja>RcOk)>ik3$XBhQTFN zUpcC>WPyw~npT~AbR)D0hX+}7%UkY=XnJ~h)B$@JWxWa`GIwXNDHM(#dr13IQ$y>OMJ_S)A9v z#Cxl>_|j%Y(G_17_hLec@Zti7y%O1=g=CcHGt zzBv#`w_wg`@K0u3O#g_dko#KzpMb2@shic3iT1NRr>jL)(`wc%?|u^(V+5j?7GGiP zfT<}r^;#2W>De@S4PzZg-ESjXsQch-YCFSlr$o7l_m0SV%h9Z$PEW!@cJ4!c5$GG9 zWVp1O?}v%?y2-=)ec;Qwr{x)PVU{6wlVF2`57@glD0B4xyM5<;a&F82Cy#3?-UoZv zEqrF6>6Y{o@;Yh0*^^C`$hW<_%O${`vv(%kiM$b(b&NZ&i5;!ba&6q z#h5%!-gsDaQ?XJcd*j?CVlQqZ{{}Ppcb9VAifaE%!rOhHW3TkUFE#DA2i#I>M;GLZ zKAcM;i5yLS0}V8+cK%}2q;MjjO1eXLt50gfO~vO!A}|+*LD_vQpZlD2?g)L_)7aF0 zqf?T$taT#qG&~f37%LJj+nLVazla0dka!^nC#2Gt$gN~wnO!FUaP$`6L;!`LTLKiS^zQ+GI6!X6=uXQVVzpT0cls;j99nw{-S`g*toAm05k z_>s^|xZ6BETS>$1I|kEg9ZHrQ!B;V5wzi9q4cpsF#NA+-jkLXMTVhhG)Z6!eo=<(F zh;;iF#?{rhxc_Okd`KGe$A_a)MB%n7tv!iPl5fn_G8Z+Y9}ZT=ln^1)%S2y{nLtrN z4%L}haqa96p@86<4%To0o(}oDW798KR8BGWUw{vm)Q7MRvNF&(r?E!^LkBui@~`qG z-{UVY9JTb?AI!8XX^5$)_n47UnI=L(cDs9gN=iq(H;V(lfoeo9j;{TyGO<)yQ;k+5 z<2!#O$KOhmv8ESHEytDfVSB#$mi9k|`7pk6E$^Rs{9YUt}Oip zcD#g_pE1?A4JC*L)Py7U!dWeokwR~I2%An2N6g0fEe3{%{8xf z<=|m&M@O_jH}RY??8>Z zW&Y#%E!jXK72PzVTUuT?(?CMpg2!I12|H)s?5uE^_FLL->9?%7&mnvVO@` z9M)Uycrn=c99krpv3S5!vFykUKXr#pAOizPm`Wvc) zb)4qUTb1e|qa}~ugzL%gMRjJrQu%$fVA0Gb;YDSFR{EKxUz2dPO_0-zK$b1hgf@>l zoK=20nMN?kyeenfyw_F61lQ0iSK6g{+os@}8S#z>2~D*$4CgGtH>PhYHUhrm6K3NX z+Ys1k&x<7RA8b*I@z9FT++j7HxXQ-1lfVsj{4oJ~r$TndFu{rUaLQ+?KI45hE0`PI z!TGfrk5IwnP6|@6gvg~16Fz$AL9%>mX4_Z094PoSJR+L!Fe<&}@y8l0^jEI*37l1g zcpht%;a8oeKI5TCTH!^($`Fa<6V2>3=%NKq${JQF?z~rs zWeod88(9vCYW?!`oAa6e?+q7u8bqMbdU*1g+1(%O*-YG76L0=nuf|&%i_5r<-uRy= zwAG2C=>_m~d+M;;b+w51Ocx5z1EWo{VO?Xl@6r?|yua3d%K~;a9)6{^C0XE73kxF- zM_+!U6gNw{ntAVF_X&?J2&EZue;*;kgHWx=xZ(uA7kcre703>tEodL1Y^H*fG8EtY zQwBCSYFEj6O+8;q_4;oqM<{QgzeDRQ*>w4>Dz{0HM*!>lJLBD*xFnbkt+`JUH#4mP zy#;4s7z@PsZnfrhx>L1I!%5v0LPq_@u&``mQ<$Mge^*AVEHITJnZr)bAkts$bpiakbCh zO7-r%f$qRDYCwh&QdEX>3oTGA`!V@Hm7U%arN_cV!QLjMmnoqa5m&sZDgbSxoo*6oTZb_QF3I=p2B}GDZ265Pl8+ z@qYRm_z3aco|c%Czy>izl%%Ssf1eNrc~&J8Gu-KX3Dxq!FTD$TK=mP{;y=IpCqJUF zJZ*IN2h;huUp8MLXHFy3ZEIhsV~*u&;q9UA-yW^t5qH?O(fOA{U3uM((EXm7vz2ag z-=0WBKd(K$Ua>~1eVb{yuG#yBPaaL*@cMD*K(Yb&rk*{lm92%h23eUq@`Io_=9fHO z0@t;iNjt|QI?6lQ6Jopem_6_zxPPF#`+u);%-Ep-GB^@lZ)tIONb1ZDSvG?b{kp&T zQXxpwV8OA`>T)rrV%FYc7kDw(v2nWn(?9DC~qyP6x zL(XStR<6RQx$og$E&=6Gz)u@DsSdv~0h~So2jLiFQ_K%~4`|t1^3uS(?gZ3_s|LEw zSm2h|%s-{Hlw>%0w9=B000q`Lc3uw3k0mK8~9MDlZ(uuF*8h#Fp+uJ8$XXfX?5v2E*+z%>*Tjmg&bwq!?eEA3m z1is~7Jk%->=G~Cq^U?4SqL&HxOIMYuUY_(qOxaM4xd~9Do<*F{_r50hAwDf3Zu)0Y zw#KZ^+JhSG5h#_euwmH4SNTWeLjU%KnznO(iC(Kp zGR9bxn(o03d>XUl(l?sXB|M?rl63hCz5opD-rM)mhE3o}}*+|M4}dAU}WU(L{^_^7gxsxh3b@?@v>{zX-q| zt5oC^B%Ea7SF$D>KtKBVK3Ym5U=i~2;*@U#zwnH3y~9u|!7by0evm2`_{mDwP}--H zq}Ps_TEO-UXJ2fB!9= z9sFi{61^3b^X%o_*}ra)Q^>o;_N7kI21gVLq20~4&?2nz6rl6v<4^4?+-j~cTmru_ z#oBex|alo|=z73eXbUq33wu)5)LfE`3%J4-I=vj`3bs&U~DIDxmW( zgUiH$vbAz#-INM&fwbpofT~=w-8C{=X+5e%h}H@2Ri89Jk}zk^o(fyg2Rr}jpIUl2QB8VGh`t=Jb`5CuaesG14Pfgz zg4~((J3Ki0Y&j$4WVSDH(_G+k?Sc~@uo~Aca0~nHtxr~9YM08JD{rl^B5q4(oWW&9 zSi_2a=sOJPMzRoVFojzTSRoaVgj^Z>kKgp!!JqPlbL!} z>=3rIkQ-^e<}jXNlkLMT{P zr^^{m19X}cQWS}gBM`*0;b{mmB?oek^tkNot}-s2r~p)s$|Q3nYmqDtbiSN!zB?}aS+2eH zyKhFbGT`GgSgkE6A34DD-na&2_z`mhFS^Fzq>HW-X)|ZeC^90mrAH5DqqQl0Ey@S# zi5epz9#5RhOKm*ZHH2hS>}Fql_M|^D7t#qc4}?*rM@sH70_T?`@?DPObKxn$oz zv$fH%n^d<-T0RY0wDK7%6~^{omYwpTZg6;900+Z%%iOOJF2(Jxyqjwr%^ECp#aFc{ zg9w^zar)mQ2}-;9Abks5`q-1MG^&MjY9Uae8u$)O=!Op>Qu(B0#x%B}kpt~6)(fpAFzd3P6vZHlK2dt*nLG^Air40PwkQE7qs>jybwEs%bxo7s_XUEuBgS+ ziI%z^n!6e=z6z;d=hdu>h;ud*DxUBnCZzvRfAD*Q>zk}L-><;7nC|D1hjs|!@CRQ$ zc%$?0@u2&$e{oFx92FciKo9uaUV?+{V=~sd9*Ov%7RD2jKwnMwXXV4=gzaByNySTm zZm^Rz_BQgn-5>|awU-4OSwLz7vd#_P$#b=B^0Kc=ccyrcYI)}$I5?ft@ZbMQN07G< z)x(s1wc9Vt1A;b+(7FWH<#w1`O^DR_bk4D6mv>d>h39B@|3+XJq``BEf#qq_HH%oC zYIPUQ{&+>60lq>L1xt721tQyUJ4h^MRLUPhy5^ZEl~4I-@vEf{zJF#c6QxIt#Azsp z@LpqI=AGa#AMVR=lVsYEz$B79Mb}+gUZL65%oZ5^taZfR;i66Wj@!+P57ChtaX(L! z8cNtT>Umk---2rmc=h#eW0iaFMO@)lL!mE>8W;C+$nSf{xpOnL3YS6?nxn^eMpRx# zX%Q28j(lv|T6D_Eojwj&c2r>ec}A8BHaQS&vK z0=XCAj^cj{Trj_qXa&fJ=Xvg(KPxd^J`S1`O41wB+8O~ZL5|m7eb`(bqH?tOwBt3t zDI_AhZL2#O!`nXBq5lQ>g{Arm@4xVw{Jc71^yc&O-3W z8SYiz+MNKS63+5{?&)Epxw4{JPfWJlv%-6N{rWXtcRXY0rH`;O=pQ83Sj0S|yb~+u zxc?EHw<&=@=~_siZg5MUDH3iPGy1%Zt{uMr$loQ%$++`nH$U-beXPNC4`DnorKa(J z{u(n)n4)=Dd@UTGD5q4*G+LsLMMLo2g3)m^=S$ED*v$lY|D9BlwpsSEu)ST6((UnE zT9MTnkkDPY-N}vQ;oFx7^7?`JZRju#N6rZVI<+^Eb3oqn3$8v{dSD2Db zx;DvCHB%C1agmCAJEQ)-`0t-$CLScFV}{)6tMcium-l{DQ^70(r%nZf1XjVP4~O%5 zU%)JdIKYe}uU>QLBAAA0zhj6I?tz0nh;6c`2LHJ;95J4Dya+oX19PW%Jw(*P^GD(B zbr%*zeP81-NBZ%A-ajmGAu_SH^Jqw|9HW9ylv^4QrMWj(7ikz4pl0hx=!WG~F^7XCu0vq{8C zcDh+*yFN9_y*tI7>A1ywtcGS4W+*} zh~3Gny_1|$vjA6<6(hh|&j`D5v5%L0T72Z@G}XTi+-ajPt zq)|B5WmLQA6fR=Iq%&-R!t0Z=LX58aj=N0cK2N2Wc#)^~M8=%pup9grKNXR03gUhh zQI6~h=bZB^8~HBPyJVE10?ylN%4v^1CceDXdLgKKT4C`WygSs}WIfr*KX7nC=+l}4 z%T&~NS~PhrGbWy(CjAT2^XFu(ovxJ3^|azA(?eqqBTknunb8C-yT7;@Z47>lm<&#> z8RxPfIz(6gQ+!bt^gI>H+{_{+@mM)S_Rc{LKS^$?U-!=41xpsHwxDg}4@l zbJV1s)se9!A6IXL_EcBi66sznvhXBRF#eXPuJoKhEam^sbWj}p;itFO|j*T*Ps7>i5*nfm3v8n(;|^Y7@rzWrBh#d!=|8F_QmfEfQ`9ascq+)_}flfwBZ z&JH6!jJA>AZB$UpUR}Zw1ed9g_iryaE@}ME^;E-Ab4OH6Af_j{Xg&u-ck7`}G!3VK z0wna9NB2HLKYGz0Nq>?b1uev$crgRLwR|;5W%dOD+YLUGeU+QL-h|y1s9o!xv5C!Y zeyXXKDCU(Rk$T8kr*YnyPTjK8c)e6KUM(~A^NT1BtqhE77`baCm*HB&r+Cwl7AOMxC# zii<2cd&(X3)eS+=@%j<8^VBS1qY>sC9HpqQ7BMR`rirBU8BgT8`1H-8@t{zu-(8|- z+C4%RiYj0g@5AvpU-5lfV@wfEz^*>%{I{K!7*A~$jr>9bgb)Do1r&Euyfpo{8>X7g zFbVMCP>SqYD1eGfm4=sqh z90#&5T`OmE(_GK9li~BRj_i-36t&IE*dMki#EFFcZhuH?aOff%_~Dyo5wp+i-{Aru zllG5?c@JRl|KxiYBH^}x^8=~CmrR5!s|}uc2HDq9X=P8_U2YW20_*&4ifnGL4EF6OoHQ^* zxzk!c(lx9%KaSgXmE0Gi;PDN-Xo{ijta$gL_gMvKaIVMw8+hS0=!1BoVZ55BSWs1* zWh&0pXEq-fuCVX}iNvTwn&&T!9tN&=Ft$mU=T-5?rx6__8Dj6hBp4(MwGnK-mwvX~ zIcC^C0ug@5coJr18dj>oTK2`jusb;HfhDnJ8|1I0j(qo6`Y$zQSmjROo9wBKRSxOo zEWs8V_Rb2ssHmnt-eh1yo-VrPAPO}OqH(t#gWnGVQs2wTnp-Z#nTIVn_B3;t5Hw!oDWDmM(gY1pF#6%S*!SxDd-YH-^^}maK6)|}hM<3T0B%M8jQSFn z@O_W4i|>}5QLVe2bo2|kJLzAJ&$HWE<7;J-Jj|y_6XUx_?wZCYf0ezi4pw5Wr4O%c zTEDTeRbBf@zgGThr72;~^T+h7HrsB9FDccTT^zt?gEBzk5}aEo&Q%BLWIhQDp_mfp zfxQdvvj(F%i3Xj68JXDm6Rg_(xy_zhnw10z>{U2VslAMuoGo&5L3idwiM=TfTaUV- zK8EW3Gf2YOjZ9(7G*Bb_CAu4&!d54^j~Eo&Aaxxnkg&4#xti(3)kVHGRQnOW9tD?M zDJvtX*iQ0%Mb{XCE)`WVKiRndv-WDbCp$71cgCesKbQ?Y0>AWLRg+m#{K4AsY2vfO zu=l$B>WR-ptL$SRtg2jArmm<@saXSi%kQh!DA|{j+4q-k*@>uWMFkbg|7-Er52pwo zg|AXDiM=jP8ef_L=DCs&ba@!?L{}xnYOYx0u6?$~Y*sil)$xPvkFha&G*1`!a~2$L zFqe_C%1M$gNs*s6xQ-jm8_O4@d%V0mG&| zlaUdqspHe)P>wWm!miMvPt`1DR#XRo`FOZ+7r55mreQm2B`?{j<*CCn=-j99HQK1m zT9GZN#+hgh_MzWo)sK5O86)p z2hx~OJ<96IPLOmQq?gF&8}&ie6qwI6>s%x((FqmNI4o9ewxoh!rjLW zuz3v`3ka)@YbaR#3Q%i7+esb0*XV!cx(1up2N70FoZpSrP9RznxHB+D)fE(MGMdo; zEvsIEX>ENccsFJIlv>GL?yE>@r|#D3bcj~JgiJM(YP0(A{PBcEL*$ufg8G--AT?Kh zuXRZpL7B|fm#6j4H2ECF?|;RwN}9Q*v;_wRFr0M-ugvP}dR>rRj+$74EbpD(y6ogoU@t&HK#*<1Z0)$CB^IuE3y--3 z)1e^TBfwW0t&F5`k_MHEg=1u(rn|+5)vZ#fIacd0RIcHoRPqqG*cnj+5Ly7jAO}QX z>CnH@fWWnx0ESb^L`Xr?(i6SLaT30!X!jL217UM*K>d$|@RHZT&^5e+T>J^y(adKc zIWsx5Zm*kdfh~|?YE{d!${4jXuWmN!@UE}ZDHF-b{PhF!Xat(1`A3{U*xYzJEeVd` zE7%ZRdGk^d&Q9bwFF)|tM3)mcXK{>Qu(fAp*ItfG3OQF|{hCYW=f4~Ghl#i|2z*kQ z|GeNZQQBX%WTGW<397_!YW+!ZY+6$SD;~fd6`MzOW zMZhmgiGYAG6$NRfLzsYsG*Z$a-6;)IKsqI)ksRF}8x7JWFlux+1Gcev_j}*Z`zLJs z?0%m6y3XS`$%)8~VnCP%i7;uwM@o6GUOdW6T%DnKFfs3XeJ9SAS^yagB_E2*P4oRH zG82{5b5GY(>cLhw*;68&!ONG#$89D_2nMFUayg ztU0d~x*n5x7}>%9>^w$6l)d6(XX#OV#8ZyVn?v}-W7ZeDOcMA*99-nq0+O>`jl_SO zUrDeY^OGW8O)(2_0BtFJtu z;h}bg<1F;ICh+{2AK7`Qklk9g6ux`*9OeS1gT!NBE@U)6P07GrvX^3^z$^pyWxb>l zxrep6xV}KUv$NxAo`P0tShDqaMv=IWSKH0%?Q~xFv#dC=orT%ye0El)Z9f_I%bf{z zb(O##Y|Up}w#$ynQu^VO&ynov(;`cb0)ss^Rs?l-KYo*h1`<;zj;5ba!4YotIDl3D z9KH$-sA3bg#ws{%`9(1ph);w#>#UgEGfX5yyp13mB|6!Ag|_2Ry_>BeH@p9_I;XxI z7f`sN<;2ISwvc}gCIq=OJ!d~!>-~yz>0+;B%~x>#Ax7Nb9gx3g6#DEgQ!KUZj!UXO zy}-S0nUC*T^>b?tg`|kZ-Zactuca28?{^_zwUCTd0O`X_9tP|yvPXfXpP?zAWqASj z_8P{Jl6UPFWMpi=vKqJAC-Zz&aH&^jyc%Gh?jvPHXtUFDP4u4YVG>68yB5;E=FqiXpfKBe4HTt&Ks8t8_*=c$xH> zt6AJ{mZ%1~%PD)O63orZ_lGB-|K)r$q@4U1MH8fNA|WqQg-jG~qoeDD#3K?{7Th{f zc)w`?gNP>z?l93gO@O?%`8;OslCpd+_=s)h-NwHz1aZJYl_+Q}V#2#>nYRU3Xb^jE zx9q{~Ewmf2L@tx=jq4a%0wP+iW}WTY-x(H30$N!6<{);F#qOY!Q?0>WXr@6EERPPt zq5cKdalGtOyiFhQq7k=VmhqfNdp2#~x6@vEaWba0MFWZJx+ zlZ%WXj>V2#8>YqEl;8BGxZoQn18ItRBkz`F z(AS^!t-NGv-|s89&2Z2@A(HPwJ$x`VFwwA6fE>-cYx=2ueSSs!OwQhhJ@WKvXqIbA z*N1us0}iSC2`^QcfikjPJ_Z^HQ?a1JanJ6Cuy}3)ivRso#a*VNklf)-HsX@AG-75k zBkny)vNV@u)hZfHE6t_q?h4uAkD25ld3jOaTJ)N*MWD93q_&tj26cS29V&f=*-a9f z%Dv0!7aX)$3NF&@fRCs$t(b8HttvF$(if&?4qri<;aOjF?kUVEXF9qdq5`Na@N@ClTDiCPfj-xL>{-x^ck`@} zFOeQc^<{*}`nxk<`)>=>;hz}qA2{PF^GeY}1V2f!M|06ZsR-;=szfzM@kKV0e8~eE{8~pbNx7F2zr2&4Z z;Fpd061_6sM(YO3iFwn0R7zTQ5zcH*$Spkc@jw~S9#$bb7D66+U${v+{lh})`X`T6<#RdrE9}e!S-6V(C&s6pj>zlf8b6T&@dTe(7 z>u0I{%$HsRGKDuIj9#1qPh>Rj%k7Q|@XD$^lHOI_|IMxcr(FP+Gx|XzE$REI-L+DQ z#~;zUt73fc&*=AEY(XV-zYGL!!(&zur1s=zdN%l7n{kzTq5k;z`TuFMV;S>Ge_XR( z0GCP@ICt^)oL?!P-z6e>RZMwPjL&MVzk1>uMn4R*N9++=0W_96!8m0>TGv*(6}!2l z*lyV?3P=sXhM!hEk(QHsfE_i@U~5USzSGduy^Uh+R z(7e}Ypez@Mw1<>J|`>+wUV_5jk(5cAr5#GQT#iimdg~-Faqov6sA)gcR zY_yhcM+Be=2GC#q%F`u03Pl8a-_ngT%Cy7B&F`d@uu_v#p5A+BSWImgo3alr4UhU>MmYI_={$ZwKh!;zvQ5Z|`ZT9j>4;j6 z*_}~AZB34a6>AnWCC78%{g80<1>WkSdSAf$OI)6;u(F{N{)eB;pfKoe-F_*9)GD3S zYNGhG3=f&yDh12?SH<1eLwE|F{ASngd$-eT-Li58--P^-05qRThoTpKQ$XNe!V3e8 z!-$}zz=~)?m7>NCRIO5XY^=SD`H0DMVN4J;v=JEH_t2k`MexhES`fG;XB)J>@iP(5 zNhBNg==vo{HvE8#Vsb~EX8b;Z>*2lreX!rUKT+hHvELPJV=*A#%7F_v&p7MaN@&^${S)5S2@2u7fjy5VpQ`65N^+TI_y)V&n$( z67x}iG)Ge2#j-k8#ebyk7Q7>LXUg?3tCA{0^kj5^#&yVCp2k492h*U-F8JiZEDgYb zUOrzSJ9q8z)c1)Zk*P1Wj8fCJKc%>O`P>yk#@Al%eS(pjE;F91=2FMh1>=}V02VLx zYU#DQTskoRPd~3SexP<)Z>+Xzh?TWnjhNveEpiyZiLnJ+xM2UA|Ia8dnCAMl92ZT; zQv*B;Z2=NJa7ry+XnlcirLL(x!e^1FLOU@c1Y3J<<4WBCOIo(iMr3O37Lb0b1tJ~*@c4}%x4@I;?c9Ybl#EwhY1!K{szW@I1 z=aHVwlb7xw5Z{W^i`gi**&VT0%>JYA>~aHd+!k}LO$+ai>gbtfv-s|r zFM3ba&_q!bG(AgS^+YqwjRf1@08MWV3graGUg?-CV_}h8B&2&CK zgc6vBBUqt}U~P$J>3zQ`7XRE0nGQO5#uA9oERF1@^<6B$)S|mWD5Xkh;UMr2C*OY` zP8)0!S|_CZ!8GtQzvx=2VYteQD-taN;RrI`f3vNu;P2mtO3j?Uuo`*yv)OHPphzU= zS|0wk^?K1^#>sm*=L<5{2eB(Z%%l#oShNFYyZSE;UBz!C;LEtGl?WYB5u`(In`9YP zf}e@D1k`}WMB7u>H*3W|u6j`Wm`mtaoWippA8`Xz1dKr=a5K{nT@-C$;;(J5i)uL! zW11#(89iY#tatwM?EHXrx||^Da1Prmjy_Ue%t5JZoI`W2^;GsW>c8{NT+N&2{*S?H za70~X;021VCVMHAZ%>;JB{roUgsabuw9)drWN=aPKWep!BgYb+Fx<=K)3TKR z>3MOVqH5I!Puh<%+=O`O0ppGjgZ|=$8u79hw?Hf^o-t;;R39+cFo8!xh5y_! zlxBmg$fCA4vkC?EB!saFDONN%4M8i8zW)-dmBPz&dgmmsO!Jv^^**z(j9pRq-7h-G zE`G= zub_4NkdWT`272=nyaT%wN(fXxz^IdVgvXZrbU8?xBZ^mH#s^6spyMfvcYNEZvzvFo zU~pA@cD5I00bfi2;)Ery5L$MN2)i72$k-Rj+QBdS_^+}nnTM(P8qRG>3!xog>ggCx z2*?-2THri--P{u&plQt!om~IF&6XCdGUZ{EB(30yG-}>-0{y$DPG=i9JQZDw^N>)M zJi7kv*L1DsJubz0*X5Z9D_Lz1t9*L3_PfF`YG8kQ-kT@j=R~115mJ}+n-QT7y-)K6 zU$YHUPkGAPElRB?K}drr$~L1}kz^XCBU_^GeXBihQ1FLfI$V702uK9w^kDg%N|1RZy924yZ)!X>_g#F^gMQ*%*9-GcQI zQ@GJwI;IZm(QwIU3Bg2hA<}ExX{ikvlJmxbQ4gp4>5a2i|48@a4qV$U3N6prAj;|o zyscX{2cDA0eK5lS$QR*u%*6=S(ixoXMG-LQnSR;~!7`7jXejV5QK-7|pZUJL?K}km zv%k&h+euXZa=^2*yJk@>A?5JXtvtwh`-4qB(7Ez$=~ei*IZz~th6voV9xfYjX*jM( zgr@wES7SMlD{fUr_g{t?cFhj>*wg>E&U1dHmJD+!bQZ^R6vIsnFm8jv-$&SJReAqjf^f8We3;Kqrv z;U4fn0~NdCZx=J1j^R0e{KFqQWCf@RJsk@b0>Va-I1S6&`7_#A51e56ZIG+ErRi^N z?~?yc4Fr36Hh>9CiG0cgn5=Q(PdRaunrjp$UQiDDA<>a>iy>anE%k;Gx4$#+x=u1$ zMmoJdTHrEhD=}Qi!RC|S?b5{fX+szv%0FyF*KsuymkbbSsHl;3e*Aa73yUh+|FqqS zr96)KNW45!0`n zdO+hYVwc+8;5oqH5(j$|WwF!S486?mQ*8)V&*)OUsNLPRfH%i$rp9szok25V7@(>6 zq0H8Fn`__}H}0m~6fZ70GNw@T48VbWzmbbWD*Qeq)N8&Sp+@sfI)Y&E z$)-Eg(O-HsbE@p(4`-jL*o@a}ao!-`4U=A|+q0*kDNA}#f6jOmZtk9LcZPp!xPs$- znGVai2G#`pG$rKjfl)3xxSsnyM2#vsc!Vx~gJVi$eaOHz`@Ard6J~wMOV8o>MjS*J z6}sBfqP@&`XeaP9KD2*$SNvVN&kpNy;DJf}xa6^FiXMxL(1yU<(7B4l5kU&gKdtxS zCWO@Eb6i4--Iuw7xYIDUO;Qq#7cZPNtC7|b8pNi9&d4ZD4H1k1DJf3!{I&M`)KI(J z^O5gNZ!h|Ho}AK27TG&r7ZT?dC@5!|%2hZ~p+*!(eH{C<#f`RxPdFG!o;i1lnw0wEr{{B2;m)^WGR6ad!8{x-(aAl2 z*+oQ&TgmZRZHLaChe2srfc~fo=U1EI`|4LU zq(qmbLJOTIXr}l5*zxugm2iMti;KHi?6<$5x3#QGj+GBa4<8NM)`)u@eNoNKPOX|v z8mCW%B;@#_?SP*Ss`ZHCByggEP@ndO4=C@k(`xFKM<^ScHbRxq^xV=J*7O2sm*81X zA{#x%WGbeetH1Ggv%#kLwbVo5PwECvHsrQ}9w9M2+wjsJ(+U8D$W%h>H|g?_dveZ6 z>{5{bzsRwDXV~T3l1pv;YJP{r)}-u~UIvL%Zy5fn2m^DndR*i>T$%(fc0gqNo~6_h zoT;6kecPfht$tx}bD1uxTAl@cnIp|qt+ICM((G2Qz4-NK{ONseaij}qu2pE2XMc)b zA-n!heYb)uzn*SV<_eB}!@oN-fvjd3agSoEJ1&MjhdIC`4mBmah(S69<6ij1zw;M& zGpUknZhKEZb#grnf9JQo@h61h@yQpymp+G1&9>i3Pm+F}4tpN_1-Gd%jm30@=gN|8 znw-mSv!Ui@{wKGg0es?OWiBB`cxkonSL;7XVlS)M8(0n?H(*!I%6IqpJzOyyi*uf4 z@9dV*dpIsV<7&f~cyojIXBUi7B6+!bM{d}1N#Uhkh$zC($nRP;k z|8nBgp&~*c+-<2Z)%H$*tq|q>ZVzOLkgWM-a9T~^yiN90WFe?l!L=ihqAELNtS4+> z`XB7+qL$Sfpc4f;2VM+ab6ioPa*hE_TJIsXblCV`gbUe4K{9kBoosOu3mwKKhYaJ( zFK(teK#i%(Ti_TkioTg@$x&DuTdk9eP z)6r>>iQ+5>_{F`js}{^zwR%RK7IzPxdtLmnuzY=&j87xl&lc=~wIO$BO)lsDtLCE~ zFyp5;4_zzBZcbSq6a zR+)ZZboaG-OLy~8VIogDdc<=D<_9>-KJ^JNHW|3>JJW91Yd<^gnI8mr&gP}iht-$wD$_*8Q|?c@6(=1A%nE_lZX6txZE}3Aoj8$SI&Vgk z4Rbud7Fa3&E?N@0BeDK1<5%_-NV;Pno@p0s1-Ab)ZF))`^6u+aM8n}pdvSFDs-wZ^ z)cLY6o!l;b`-ZJ`{jQgxgW;l#yONR9i-$I#)^mBuL~C>{WV>AL>U?mYBZhaV*QpsM zFMje1;=TTJ#}`~|cgeigVlKxLLTsfp)OfR+5#@MitRW#VZF;=1n4a+1U^^E8=ezZXXQ9raOVVe7=_46>MRZkDH~LYX)Ch z@;SS{$HrWke*HqqOc-};A&&Nk$P`LWFshD zaK`v`fYFF(sB|xbfaL1MTyr7wKs@_oKs<0=bmc)>E=z!-h*2_LKrrHI8_nUpS>G0% z&|u9M$=$xryYTnqS<~dhTCxt_$a29I(#Xmg;tD1rm5<_hwDn z->h|$b3?Z)Tz8i33FTjD183`H@6*nF=g;X+b5_qqT;~0u9l+Hs@9*BrzPq05#Poae zGXY~UEh5a79LF7pu}xDN#O~z4TcG&7@DbO&w@7pRE@B)Vh<-+M4d(te7}C%tz_cB> zo9(Rx$DVl47_F?yKrB2rEoxkxBYsXlg;Oywv~xOffQaGd#cKNlO!#~zk>k|uNm*@Y z1Y$b-RqcX&h}H1ePfn3+bo*tTL|{FcKH-_z;+AX`aHQR78Xvz;ON}~lmN?BgvdDPR z^VKjGsn7o(Q{7M8LQk*-*`eviG3Y*L&a-VaLL@uKYgY9gu|YHew(mYvTy{U?xrSys z6YguEooh8BJ?FQCXGNCt96}wer0e|VjzVXBwDa45?G8S$Rhb00LpMDeZctTkjZ*9= z!87aN$q2hP#^ram_a}JgS$h+`h;1zZOMIKdrSjv=U4Fvx!rJzu>~6=jN$u@ByIbP; z!XNQK-tpST_oxQQZ)|hvemy;#E)A&GQPqzJOxRBpUY`cNXvC}^w0+@psRZKu45Xaw z?Stl_N(wnqggNG?=~Lo=aVqEz`IJ{781t?8cHoeko6S%&?V!-lYweP?i-Df+zq z_fMvZF7br)>#lFL*PefThotXl9?@=C7|nd}D2?h3-TBRBF;!Zx>#xLPQk2hY_sO37 zyK&_2J3U13^VWRI0Ga%a@L`()N@vfD{C8BOc28;j&*(!pb>k?R1N^$ZUm{MK;npvV zi6B=Z(8m6WjWqVpFG4+2JGm)#DN-x{(wT2l$zWe`sWlMZ1RN2$^-*Mx0v-97NZSme z79)XE#b@Ii&z1BB=NaTlObyks>1xgf=YT@uHj~@MdGueec9O@QL(a2FrgY~S?>5CK zBUZ6FH}?U8)P~VBfADRKptc;JFofzp!GLo1-H=4ibt%Q0dt$O-;1ZgD)ASJHS%08^ znBO_`yz+sepxF*1mSOf3ipQ18g>d+o?e@ltzGOZxV~wGHag-fuoBPan%K^jt>h7NY znY!e0GGhBvlAw=Cap|(aN26D^O6__>grN z{=agHRX^{g`6w<(d$?3l|DVibm<_mS<%**z2Pp^gMsOqShjWhsPnTSPysjhDPxoH>17A2d)QRzDN>i}e*5 z%fhUU7=HayP888O%JKnaA)>@X{9lM3>h|hnQUk zotGmwzRFuArZYN^3OXV}UgCWMJq?1Y^{Hh2K(~>Uy?b~kJ-yOH#RT3ppkujU?dVtF z*!wkjY+K%c93M=%QqcC-x73=Z=KHFdrqnBr+qDE!28s#Uk$fM?WzHLcUqTc_ z!oRX=NPWVM$hY8X2bnk1)vmRy`1Og%2L^EqymhP8i7TSsO`!XGpFxd|#}aEX47CYabCzcV*HBkJOQqDD6Tl%DGGelUlJPyqRTOvAuM zLU9~m)>R{ocl2S`dkyvuY66K%`o-rk&a|g}y#Ul?(>H%^{=>PvhmT+WkiM(?#j6K_ zo+i+Cvz)k}Z=ciz73)0xY^X;=45`=Ppq)CLOLuZ;pL>HY#y_qkBVQU5bV6s{bk0Wy z)21c*wTT8{kcbcfri+X6(laq7C!8XheWLRq-eeUW2A{!oa49MYuXY}f;Z&66S5cRO zXOEe)7Y%TS;-BmFY#8nxzvuZPoqYif7s|$9E+^C>JRZ?|@ICl678?7y$X z$?x@3if-NeAsHfG+%51e>0hyNQ%mGMXiPk4N;43L()S~4)IRm$JHPkIGvE+qy}=Dl z%M3}lZ)rk~n~=z?W%Qgz961S<$TN0#_ZTkVX$@+~!n*Yph5^eT6;U?t{C%L)*N`O8MXLvTob4ba`ZByIX%ScUEVr*AoO^zrAa?LBWu?4Ex zrqf(CfF5v)slLe?Kv#$32q~Z>Q*PSS3psV#%*;knrbmnjFs{I8IZs-|{ju>Si1-f8 zg@m5R2g(4=1iWaDjWWmGdIlLq$*Z~M2w!F*lkVp}b-#o<{v({{n6@~_8X(8l!#F_4 z>u^N4;W^Zf@gsfC%r_}h2QJq!-7_LRy$g`-y=D^}`UGUaD$8Yi`R&eG^Qzb3mq(Ad zLuzGD3j5ct%hnG+oBuFf1emy_zXlrR&%Zc(H;lkiu303mHocAwdgN(Lxu36Gz-K7` zy$H*L4$+#{V!vOO^Y(J_6J-D)`SJKqo4shZ8+46L;8;dH)B$@z&lbVg)EaptPl zrFeXzlU+#N_m{E@Pg(pD{xbLT5<8^o{oIBlUw-A)l}-Qfjwx2VnScVEwCkM~5fn8+ z7{h5(19_4qx;;k=Gws}8_|Sur{@u21JmJGzDxMt+h`IDYUw3mJ0Jfq%Q^hY*-_C9> zOybr+`mC=*zBh=+hKzF(vgPh17-@(mCjSDM@{soTG*0LmR99-HTJG*Hqzz{i--ABerS(6Tn0HO-hV7ZOC|HbF1IWku)Thxy&i*VW;<0JK2D?5S83^ zD*onnZdB}c+5hApE!}goc+ZJdf9o;yoI$_lw_>_&%^^=_vd_0}y+%xK^-t zh8!*r2tk+*B2y`|AFbrNdNVwgeI-y;!${IyFU1`zOWN+>sC`fgYV0`7QV9R%`eOtrd%RedtZfA~UK zyL!r9S9tJi>OX1Bna=bSPe;B!jy&LV1+Z0#CD*?A-2_#X^`doVn)4P8KnR``oEykb z6k#>kDSS=ue(v%%;*#2OFve|IJlls?-fddS&BxyfaOC|6<8ahQQ0jYRydL>n!qcv# zY~j*zEhBd_CXBk9wT{2Wu0G(KOnhJ>d|EIwpt@9 z<*8hcbZFCa-w+K2wU6^%l+3+1*&9~p#|ZXs8P==W)?un*3{*X0uY%p*1WZ49^*HJS zKJA+H&H4ow?}S}cA`SvGj~X)bzYweK`VaF|`w2y!DOXUT9O}^M)gEP4|0G|Ft8DYL zQi}n0U17^RY9>eUK1wl#4LsouTC(0J)1Lh{AO8VQn)|g34XAJGn0D(`EgN~XJoYBY z{J>p4`Q9H3r1p?AUSP9^7)>hGTFaaIJQUZLhqp!-=5df90is7zK@*bFH=!)4HF%Qq zg^jZE*X4^MB%3%dkGZ8NeG#|*1lRdQQVwbaXZ)i0$ScgB|8msyR)1W3OUUVSR7|qd zZH_+c(9F?c1#tO7kg9>=N34)fVw|r4pFu5BIhY!vP!4muw8Y zh4GCnPqF)?(_yE#fI0=+fUQIYdmXA9s?t1YiLtrFFlg3xY=?CJ5BgLE_jjAGtqiYO zOc4dJ1z{{-;Z9>@8-PMVwI7^72ZvqP)z{EATqny?ZpoE>>AwDwjp)hcftm}>qo9wMWRZ)G+cvN+vs=MtZza;H|5WlJj@}LX&IrGr)u-bd7;sD_$F48o4CDqr@49k zS}h8Cbqram8W%7^#Xo^gGH4-dwZubo#*{{!jolX%9|nBv3q%ULN(7WdVKO$)sEzaS`T9dztS?`E{kA}L(P~4aW1Qf3oEDxRiv?@lwsDGrt^@>r7dhPe8yuY$&=KZg%YF6`f1Zf6bjwBxp?5!9cJyM5)p2^$~A{Ge!ZA-;K=Y@i~XD9xnpD7({HoeEH4>dCU*_`7B1= zGxe86cYM!BTOP0tZLs?<7HHo9Lp7tyJevbIkm5kG9;a>aGwNtLNjZC zPT1AX;gVw1sJiy`rwMzhw zfw%LqEmzxx4s&s}^{CQCm{F!cYS;LVM9w*-UAwBc;!S*Rp7-wH5MV%%(3t}EL3!X= zxyz$#Xnrp&-bY{gGU2&i$-*3VwrxG#7lf=ccP0#3zNBsuC=76ZAV(A8 z=pzm0Cdz3Fk(&yk!Sqm*Y$Q3pq&W=BJ^xwaBZsbzs(!r8snUGp81*sVP8Pt#`n!@bmZ z^pOLRe9=*2njgnJhpAT6664}>!z5q>8kXaoBv{y&15GDIlIQL43s_AsY5P}K*DeKn zlJ=j0vahJ0x-l<+F2}${g9wusnJ8}KHf<(HRpRAg-DCXLjJj{}9W(FiKR>=F5I4k9 zJ@(&^$o`TV)U~vF0$j}tLazH}Fn3BzBso~{T3OK_Ng_I(Acu}YQKPMrig5cwCo=!_ zH$>@$KqeVYAgso=C-l=ty8-`F@%?vH%Nk52%pON>c9~91n!9N#Tgt2dWlW@f5?9!I zV)|36Z;z76g=2T3FTW^)Fekz^e&S*;meg^DP<#)q$ zzOM)nJuehgt9I>rf_f`m3r&3XN$j4|W3+4*#`xR|T|LPT9kuP~EWBfC&-UDi zT%BU+>9UJ=$>!EtGZd~;FvE|bMvdMS26Ael*JgQc%mHXL60#R>qm?AyJgKT*e<8fT6)IR2Pqb^9A?3B-Sdr)bAbxJFU8g zZsnX8v=g{=3@zDe?k*RlP3{$Z@#%T8 zPYD?o8JTfB*Okd|Dp&C(YVzCFw%rv8xL7k6`8m?SC2kdlae=`w4;NusdjGxjtU6H# z_#CZzPCY@1<~>P3Jo|ztc)n7TYaP+l$XuuQS=jDIY=6VRAmjDOB2HAl9gjLf|X?uJ$c@n_2!qy`-E-lv9Z*%>9fmNana~~hi7mRgUsIC>J5?zdDxNK1BZQW z_M(aaYLaVizH4t+B5LqFveu3I2*$eOzG<(s@-b zYp^h_4XZ%jk}SUVwdu@7PQkG&4TR{rAi@+G!Sd0Sm%SLt$+=KKoz_o4Rs&lcrS3~zQRWPgg=8p90cZE;DYh5w7&wxNo%;kL2w+b> z@n-jb?X=D1pyt&8hc@IcebESSyHUu;*tZpr0?bI7BQ_RWzDiNSKfd&)`%K*r3(=3p zu9H^oEuu`v%}u6op^rk&i0_RnF6N>Z(*HcVJ)lt#xGK9tDO`rU{220O&Csjr&kKOK z`9t=KO5Crk09R=<0Cp)@T}AjISbPGxw&UQny{cGEXsS|Z$S+i&>|fl+pM_A098u;5 zPuzKET!RD7To%fhj~XT+2vwRD6OMdCuP`bO<1fS3Q)lqs0jkGYS3zin^gJk`)y z4cfBz^r@(=?^C9QE`JA9Pv&(!^2! zd~eafvGA&q!!{CMIeiO19>x=Pi=Tk=yRWNT-aBd`n!AQOmsr|;b+MwyNa$3{0zL%o z4J-OBc-DQGR#$4>ffu+^pDhkcnZ%Yy&m5fM0$*EW#bUF^@H|(~+LDfxRI|y5y01wceo@c`@AY`?|AEXWr2q>FLBV5TH$S=YNn2A zHb3mT<91x4ll8po!nE-8qp90A?al~)jM$R3a7q$MA-XtNFN8dh!6ZdU?H34#wQeCJ8z4+&ubS)dv5Vt^>$@bw-33-6?Ns_R$ ziEEHGtbRXr$=S{UWUUvC`VnGr=e>6)hN z+&q|b&*fsw8DW_AFoCd*ceomn-M=?>-?WpNuLSmGQJ)5 zM{x7fwT{>{Y@6w_g2FBtNC?O=_#uO_x%2XVyV=#k#PUs|Y0PzFy*X3R^|r$q$s6;` zFVieK>ijPqa)WFII}1jeZk!MbE!g%#swr@T7~-GLTQakLB$k z^^#?(wR&VYHj-!Cg>@-a>{i?sWFPRzbP4*uJ09$wOX&6B4X|<8jmD$?V0#<#snw#F zh*9WKYxV$};K}>rf_KH;5%Q7`^4E8b#+uR&f?kllkpmR&SP|W_J7)zcIf&b6T*urM zM1D*Hno>pr?n{$O=BK%gPpB&6GtHkv1fH694LF7a0?eO0$H>I!4Cx^NVp3m!W}8tH z6(_DQH3fUWcp3h5K{vy$Fd(s8+Wl%udRu(7f`4oVT-!_;QD5Vey641SBJO!^*0S9!0O)X>31)SuQLqh6*-1=KsZBcM6t2Oot_yw+% z1HMr-^;s9|6d~BoA-X(Ag%fm6h2CtthwEL;i&-UWr%<@z1=sVaI+gV#MVoao@jpDs z2edeV%~P3ksXdzy*ziHd5z}n1%ev|)7FXAZiu=v4X#@J4Nz(L~yNr%&tlXvp3=1^*cER{uTdc(&g0#KIBR-qoAL%j=NrM z)ce)dgfBltv@k@w_>!JG;q=Dm^sdQ|1-0CrjU<;*d;UFP-#Pffmx!&?EcGc_L0x0+ zxj(3e8A$c%n^Q279P{Cr&=RXCSuW(yOR^>FZ74IJY4>wL>h>hbdFoGUHla}36u&*s z-8Ac6u5afy(dk`&vciL2*m4ce_KLZnNqSeHfLdp6QBLKa8&02@$^1ETQ;|Cf?yqXO z;7pN=e=FeHkIOueIKCyjv0krRpT7xhzh&{azd2%9><{|Zc9Y|eO~_kwhW5spuPC7g zC3z3OR)#g>yTgYDE_Z#q{Hm|-B60h0b&2De5a{w*#9^%0s;+*|HqUMO>K;H~=?R~3 zyKHVKV|fajfXz6f84d{8>-Nkvq5iwdL$;k!u)nBj^u~re+2s!3nRO_^C%&@JIYQV*Yi2sesXlDH7wl4&seKJscT!Sh z!U4D^V}G<%g+jMe1*X@-0XaT@f^qM+((3*uH?$6S&f??t4U1O!u+Z77ZMZV?L^Xvp zVkz)?-@qPX&Um#NFT61aPSR8edVA$4%=!&CW7=@y$eVeyA9y8oJi99PsagT%_6Edo zr(Sy)WQGm@4np3;4YpFWvAJAxjDsHHtIvsuSz z{{$yF_XLrBF3F$;_~&%t7MZ(P!)rK@XUaQk*FIqcoDph>o=UY%*Q$0?Q{Bj6AM{^hKtWtRWx?9k_$FR&!Y7TiNeiYU~S& zeXs4+JHe`?$v2|Ef$i^&|Ew(hCvDEraMu6~DL_}vR{juqhcxBT6XwV$iW8|fJ9@AT zlKuPeZ6?*XBAP3;gO=DYoD3zKjfwQ|t{sWx0I6O3PQGzd>J$b8FLMPprP994ysuuL zZGP*kuDRX2<6$$o--R< ztqhylxV7m>wIZClEVFw8ik*Hn-(sz2t02V-`kCi{$1_#3%ZmQ_sWU!-k#u0--*p0z zTy;MtbZ7xf)4249Jsb<_q6hKFp+u{>6co%XOo$lSr*~y@=8u>=1)th%aPaQ=aiV z2nQTI0S$~Xa9)RRob^)7_(La(NdZ0|MmBQEjrt`Ny^dtD5<6)(4x4p^{&Gim3 zmiuWQm49(G>?(-c{s6LCg>uCmhik8#Yp(}%YXdM|Le|7<#YrUkT@dR(E2rna;j7zL z^p_4>rt@ftYv~c$#8LyhvqH%4G1)to4#*g>;m5q;j{$GDc&%u%A~dMtH}HfbDM`x1 zCmv33QzV0QQH(4(^@rbnc8rJ=x{|xsDBWHzwVX#PI2-Ozom5dqlMKvL7MPg4&G!0Q z?~))c?KDiPHtrO~GWotDoMs`cLNnEMPjMlPT*#OtsvFnZr$Vp2j=w#S*o=3WM;f|h zmwT0oV3DH$F#I;qsiGK5KO~1L=GiS=#b3FU9?q&4$I*6LdhdyE8Z!FCs}@j5^N&`PTA($JDipxy!sB@PAtg)@}c_67QVsv}>~taAY5Z zwrx`PwNXiG<%{uCXcqCai$EYh+QX@81tq8Kre^Fn`^NG;ErWAy&3P(5YlipnDSv)3 zs*@3Le>cS}m7F^}xp+c-gmj%NDUx(^=AGv+F@Yk#A zSTosus1LFJLE(SX-WhX~GTWJ6|NmV8HTXB=X6jP=MEe`27^4wtp?0>bDE!!}mzTIo zHln<`Rq51HDu1l*BCfL)v!As@H=XMuMGrO$sfW*a5Ai`U{FLKJHmAwbV7p@H%l(;Y z^HcFrcqb3R?LqqREMDS7bPRKesy-Adu^WHtoDbXeW_Gt->>1JJc%V{JfH~bOSpCgL zNO`JuI8Pr@D@%slT4y?OX0q%s&oDgVPa{9|uA~Z~2!uE)Yy&x0D!kvojN*#2h>j
    gne#4hT-i;j|HCeGUTb`fo*5G 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 z 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 literal 0 HcmV?d00001 diff --git a/proposals/NNNN-isolated-synchronous-deinit/last-release.png b/proposals/NNNN-isolated-synchronous-deinit/last-release.png new file mode 100644 index 0000000000000000000000000000000000000000..6daaeee432daa757ba6d07c96c2a9fa6e96567d7 GIT binary patch literal 261619 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�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 cFxh@|LOx6x{9`u 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

    +Revision history + +| | | +| ---------- | ------------------------------------------------- | +| 2022-01-10 | Initial pitch. | +| 2022-02-01 | Updated with an "ABI-neutral" abstraction. | +| 2022-03-03 | Implemented the `SIMDWordsInteger` prototype. | +| 2022-04-23 | Updated with an "infinitely-sign-extended" model. | + +
    + +## Introduction + +Integer literals in Swift source code can express an arbitrarily large value. However, types outside of the standard library which conform to `ExpressibleByIntegerLiteral` are restricted in practice in how large of a literal value they can be built with, because the value passed to `init(integerLiteral:)` must be of a type supported by the standard library. This makes it difficult to write new integer types outside of the standard library. + +## Motivation + +Types in Swift that want to be buildable with an integer literal can conform to the following protocol: + +```swift +public protocol ExpressibleByIntegerLiteral { + associatedtype IntegerLiteralType: _ExpressibleByBuiltinIntegerLiteral + init(integerLiteral value: IntegerLiteralType) +} +``` + +The value passed to `init(integerLiteral:)` must have a type that knows how to manage the primitive interaction with the Swift compiler so that it can be built from an arbitrary literal value. That constraint is expressed with the `_ExpressibleByBuiltinIntegerLiteral` protocol, which cannot be implemented outside of the standard library. All of the integer types in the standard library conform to `_ExpressibleByBuiltinIntegerLiteral` as well as `ExpressibleByIntegerLiteral`. A type outside of the standard library must select one of those types as the type it takes in `init(integerLiteral:)`. As a result, such types cannot be built from an integer literal if there isn't a type in the standard library big enough to express that integer. + +For example, if larger fixed-width integers (such as `UInt256`) were added to the [Swift Numerics][] package, they would currently have to use smaller literals (such as `UInt64`). + +```swift +let value: UInt256 = 0x1_0000_0000_0000_0000 +// ^ +// error: integer literal '18446744073709551616' overflows when stored into 'UInt256' +``` + +## Proposed solution + +We propose adding a new type to the standard library called `StaticBigInt` which is capable of expressing any integer value. This can be used as the associated type of an `ExpressibleByIntegerLiteral` conformance. For example: + +```swift +extension UInt256: ExpressibleByIntegerLiteral { + + public init(integerLiteral value: StaticBigInt) { + precondition( + value.signum() >= 0 && value.bitWidth <= Self.bitWidth + 1, + "integer literal '\(value)' overflows when stored into '\(Self.self)'" + ) + self.words = Words() + for index in 0.. Self + + /// Returns `-1`, `0`, or `+1` to indicate whether this value is less than, + /// equal to, or greater than zero. + /// + /// The return type is `Int` rather than `Self`. + public func signum() -> Int + + /// Returns the minimal number of bits in this value's binary representation, + /// including the sign bit, and excluding the sign extension. + /// + /// The following examples show the least significant byte of each value's + /// binary representation, separated into excluded and included bits. + /// + /// * `-4` (`0b11111_100`) is 3 bits. + /// * `-3` (`0b11111_101`) is 3 bits. + /// * `-2` (`0b111111_10`) is 2 bits. + /// * `-1` (`0b1111111_1`) is 1 bit. + /// * `+0` (`0b0000000_0`) is 1 bit. + /// * `+1` (`0b000000_01`) is 2 bits. + /// * `+2` (`0b00000_010`) is 3 bits. + /// * `+3` (`0b00000_011`) is 3 bits. + public var bitWidth: Int { get } + + /// Returns an element of this value's binary representation. + /// + /// The elements are ordered from least significant to most significant, with + /// an infinite sign extension. Negative values are in two's complement. + /// + /// let value: StaticBigInt = -0x8000000000000000_0000000000000000 + /// value.bitWidth //-> 128 + /// value[0] as UInt64 //-> 0x0000000000000000 + /// value[1] as UInt64 //-> 0x8000000000000000 + /// value[2] as UInt64 //-> 0xFFFFFFFFFFFFFFFF + /// + /// let value: StaticBigInt = -1 + /// value.bitWidth //-> 1 + /// value[0] as UInt64 //-> 0xFFFFFFFFFFFFFFFF + /// value[1] as UInt64 //-> 0xFFFFFFFFFFFFFFFF + /// value[2] as UInt64 //-> 0xFFFFFFFFFFFFFFFF + /// + /// let value: StaticBigInt = +0 + /// value.bitWidth //-> 1 + /// value[0] as UInt64 //-> 0x0000000000000000 + /// value[1] as UInt64 //-> 0x0000000000000000 + /// value[2] as UInt64 //-> 0x0000000000000000 + /// + /// let value: StaticBigInt = +0x7FFFFFFFFFFFFFFF_FFFFFFFFFFFFFFFF + /// value.bitWidth //-> 128 + /// value[0] as UInt64 //-> 0xFFFFFFFFFFFFFFFF + /// value[1] as UInt64 //-> 0x7FFFFFFFFFFFFFFF + /// value[2] as UInt64 //-> 0x0000000000000000 + /// + /// - Parameter index: A nonnegative zero-based index. + /// - Returns: A fixed-width unsigned integer. Word-sized types are preferred. + public subscript(_ index: Int) -> Element + where + Element: _ExpressibleByBuiltinIntegerLiteral, + Element: FixedWidthInteger, + Element: UnsignedInteger + { get } +} +``` + +## Effect on ABI stability + +This feature adds to the ABI of the standard library, and it won't back-deploy (by default). + +The integer literal type has to be selected statically as the associated type. There is currently no way to conditionally use a different integer literal type depending on the execution environment. Types will not be able to adopt this and use the most flexible possible literal type dynamically available. + +## Alternatives considered + +- Modeling the original source text instead of a mathematical value would allow this type to support a wide range of use cases, such as fractional values, decimal values, and other things such as arbitrary binary strings expressed in hexadecimal. However, it is not a goal of Swift's integer literals design to support these use cases. Supporting them would burden integer types with significant code size, dynamic performance, and complexity overheads. For example, either the emitted code would need to contain both the original source text and a more optimized representation used by ordinary integer types, or ordinary integer types would need to fall back on parsing numeric values from source at runtime. + +- Along similar lines, it is intentional that `StaticBigInt` cannot represent fractional values. Integer types should not be constructible with fractional literals, and allowing that simply adds unnecessary costs and introduces a new way for construction to fail. It is still a language goal for Swift to someday support dynamically flexible floating-point literals the way it does for integer literals, but that is a separable project from introducing `StaticBigInt`. + +- A prior design had a `words` property, initially as a contiguous buffer, subsequently as a custom collection. John McCall requested an "ABI-neutral" abstraction, and suggested the current "infinitely-sign-extended" model. This new design is ideal for initializing fixed-width types. Do we also need an API to compute the minimal number of elements — including and excluding the sign bit? + +- Xiaodi Wu [suggested](https://forums.swift.org/t/staticbigint/54545/23) that a different naming scheme and API design be chosen to accommodate other similar types, such as IEEE 754 interchange formats. However, specific alternatives weren't put forward for consideration. Using non-arithmetic types for interchange formats would seem to be a deliberate choice; whereas for `StaticBigInt` it's because of an inherent limitation. + +## Future directions + +- It may be possible to diagnose integer literal overflow at compile-time, if constant evaluation is added to the language. + +- A mutable `BigInt: SignedInteger` type (in the standard library) would either complement or [obsolete][] `StaticBigInt`: + + > … there is very little reason to use `StaticString` these days over a regular `String`, as the regular `String` is initialized with a pointer to a string in the const section and won't perform any reference counting. + +- `StaticBigInt` (or a similar type) might be useful for [auto-generated][] constant data, if we also had *multiline* integer literals: + + ```swift + let _swift_stdlib_graphemeBreakProperties: StaticBigInt = (((0x_ + 0x____________________________3DEE0100_0FEE0080_2BEE0020_03EE0000_B701F947_ + 0x_8121F93C_85C1F90C_8A21F8AE_80E1F888_80A1F85A_80E1F848_8061F80C_8541F7D5_ + /* [74 lines redacted] */ + 0x_2280064B_0000061C_21400610_40A00600_200005C7_202005C4_202005C1_200005BF_ + 0x_25800591_20C00483_2DE00300_800000AE_000000AD_800000A9_0400007F_03E00000_ + ))) + ``` + +- The unary `+` operator function isn't sufficient for all use cases: + + ```swift + let a: StaticBigInt = -42 // OK + let b: StaticBigInt = +42 // OK + + let c = -42 as StaticBigInt // OK + let d = +42 as StaticBigInt // OK + + let e = StaticBigInt(-42) // OK + let f = StaticBigInt(+42) // error + ``` + + Could the plus sign be added to the language grammar of integer (and floating-point) literals? + +## Acknowledgments + +John McCall implemented arbitrary-precision integer literals (in Swift 5.0). `StaticBigInt` is a thin wrapper around the existing [`Builtin.IntLiteral`][] type. + + + +[auto-generated]: + +[`Builtin.IntLiteral`]: + +[numeric protocols]: + +[obsolete]: + +[Swift Numerics]: From b72cdd3eb35ecf45984eb29d2d299be9c878b84a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 2 Aug 2022 15:49:57 -0700 Subject: [PATCH 2666/4563] Initiate review of SE-0368 "StaticBigInt" (#1734) --- .../{NNNN-staticbigint.md => 0368-staticbigint.md} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename proposals/{NNNN-staticbigint.md => 0368-staticbigint.md} (98%) diff --git a/proposals/NNNN-staticbigint.md b/proposals/0368-staticbigint.md similarity index 98% rename from proposals/NNNN-staticbigint.md rename to proposals/0368-staticbigint.md index 9b643f6f68..3ea55eeaf3 100644 --- a/proposals/NNNN-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -1,9 +1,9 @@ # StaticBigInt -* Proposal: [SE-NNNN](NNNN-staticbigint.md) +* Proposal: [SE-0368](0368-staticbigint.md) * Author: [Ben Rimmington](https://github.com/benrimmington) -* Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Pitch** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active Review (August 2...16, 2022)** * Implementation: [apple/swift#40722](https://github.com/apple/swift/pull/40722) * Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) @@ -185,10 +185,10 @@ The integer literal type has to be selected statically as the associated type. T ```swift let a: StaticBigInt = -42 // OK let b: StaticBigInt = +42 // OK - + let c = -42 as StaticBigInt // OK let d = +42 as StaticBigInt // OK - + let e = StaticBigInt(-42) // OK let f = StaticBigInt(+42) // error ``` From 0a2c896afff39097e90b6818db341bf412e7f6e3 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 3 Aug 2022 00:25:43 +0100 Subject: [PATCH 2667/4563] [SE-0368] Update the review field (#1735) --- proposals/0368-staticbigint.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index 3ea55eeaf3..e3f2e3652b 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -6,6 +6,7 @@ * Status: **Active Review (August 2...16, 2022)** * Implementation: [apple/swift#40722](https://github.com/apple/swift/pull/40722) * Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) + ([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) @@ -58,8 +59,8 @@ extension UInt256: ExpressibleByIntegerLiteral { "integer literal '\(value)' overflows when stored into '\(Self.self)'" ) self.words = Words() - for index in 0.. 128 - /// value[0] as UInt64 //-> 0x0000000000000000 - /// value[1] as UInt64 //-> 0x8000000000000000 - /// value[2] as UInt64 //-> 0xFFFFFFFFFFFFFFFF + /// let negative: StaticBigInt = -0x0011223344556677_8899AABBCCDDEEFF + /// negative.signum() //-> -1 + /// negative.bitWidth //-> 118 + /// negative[0] //-> 0x7766554433221101 + /// negative[1] //-> 0xFFEEDDCCBBAA9988 + /// negative[2] //-> 0xFFFFFFFFFFFFFFFF /// - /// let value: StaticBigInt = -1 - /// value.bitWidth //-> 1 - /// value[0] as UInt64 //-> 0xFFFFFFFFFFFFFFFF - /// value[1] as UInt64 //-> 0xFFFFFFFFFFFFFFFF - /// value[2] as UInt64 //-> 0xFFFFFFFFFFFFFFFF + /// let positive: StaticBigInt = +0x0011223344556677_8899AABBCCDDEEFF + /// positive.signum() //-> +1 + /// positive.bitWidth //-> 118 + /// positive[0] //-> 0x8899AABBCCDDEEFF + /// positive[1] //-> 0x0011223344556677 + /// positive[2] //-> 0x0000000000000000 /// - /// let value: StaticBigInt = +0 - /// value.bitWidth //-> 1 - /// value[0] as UInt64 //-> 0x0000000000000000 - /// value[1] as UInt64 //-> 0x0000000000000000 - /// value[2] as UInt64 //-> 0x0000000000000000 - /// - /// let value: StaticBigInt = +0x7FFFFFFFFFFFFFFF_FFFFFFFFFFFFFFFF - /// value.bitWidth //-> 128 - /// value[0] as UInt64 //-> 0xFFFFFFFFFFFFFFFF - /// value[1] as UInt64 //-> 0x7FFFFFFFFFFFFFFF - /// value[2] as UInt64 //-> 0x0000000000000000 - /// - /// - Parameter index: A nonnegative zero-based index. - /// - Returns: A fixed-width unsigned integer. Word-sized types are preferred. - public subscript(_ index: Int) -> Element - where - Element: _ExpressibleByBuiltinIntegerLiteral, - Element: FixedWidthInteger, - Element: UnsignedInteger - { get } + /// - Parameter wordIndex: A nonnegative zero-based offset. + public subscript(_ wordIndex: Int) -> UInt { get } } ``` @@ -157,7 +143,7 @@ The integer literal type has to be selected statically as the associated type. T - Along similar lines, it is intentional that `StaticBigInt` cannot represent fractional values. Integer types should not be constructible with fractional literals, and allowing that simply adds unnecessary costs and introduces a new way for construction to fail. It is still a language goal for Swift to someday support dynamically flexible floating-point literals the way it does for integer literals, but that is a separable project from introducing `StaticBigInt`. -- A prior design had a `words` property, initially as a contiguous buffer, subsequently as a custom collection. John McCall requested an "ABI-neutral" abstraction, and suggested the current "infinitely-sign-extended" model. This new design is ideal for initializing fixed-width types. Do we also need an API to compute the minimal number of elements — including and excluding the sign bit? +- A prior design had a `words` property, initially as a contiguous buffer, subsequently as a custom collection. John McCall requested an "ABI-neutral" abstraction, and suggested the current "infinitely-sign-extended" model. Xiaodi Wu convincingly argued for a "non-generic" subscript, rather than over-engineering a choice of element type. - Xiaodi Wu [suggested](https://forums.swift.org/t/staticbigint/54545/23) that a different naming scheme and API design be chosen to accommodate other similar types, such as IEEE 754 interchange formats. However, specific alternatives weren't put forward for consideration. Using non-arithmetic types for interchange formats would seem to be a deliberate choice; whereas for `StaticBigInt` it's because of an inherent limitation. @@ -186,10 +172,10 @@ The integer literal type has to be selected statically as the associated type. T ```swift let a: StaticBigInt = -42 // OK let b: StaticBigInt = +42 // OK - + let c = -42 as StaticBigInt // OK let d = +42 as StaticBigInt // OK - + let e = StaticBigInt(-42) // OK let f = StaticBigInt(+42) // error ``` @@ -198,7 +184,7 @@ The integer literal type has to be selected statically as the associated type. T ## Acknowledgments -John McCall implemented arbitrary-precision integer literals (in Swift 5.0). `StaticBigInt` is a thin wrapper around the existing [`Builtin.IntLiteral`][] type. +John McCall made significant improvements to this proposal; and (in Swift 5.0) implemented arbitrary-precision integer literals. `StaticBigInt` is a thin wrapper around the existing [`Builtin.IntLiteral`][] type. From 98a7881df14f5db85dac50af7c3b1cc00913edd6 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 18 Aug 2022 14:44:20 -0700 Subject: [PATCH 2691/4563] Extend SE-0368 review (#1749) --- proposals/0368-staticbigint.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index 9accdbaa18..d0a11c3fa8 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -3,7 +3,7 @@ * Proposal: [SE-0368](0368-staticbigint.md) * Author: [Ben Rimmington](https://github.com/benrimmington) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review (August 2...16, 2022)** +* Status: **Active Review (August 2...23, 2022)** * Implementation: [apple/swift#40722](https://github.com/apple/swift/pull/40722) * Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) ([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) @@ -172,10 +172,10 @@ The integer literal type has to be selected statically as the associated type. T ```swift let a: StaticBigInt = -42 // OK let b: StaticBigInt = +42 // OK - + let c = -42 as StaticBigInt // OK let d = +42 as StaticBigInt // OK - + let e = StaticBigInt(-42) // OK let f = StaticBigInt(+42) // error ``` From e5d2d1d18287a2915a0128b1bcc2a5643c1ffa5d Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 18 Aug 2022 19:04:07 -0400 Subject: [PATCH 2692/4563] Isolated synchronous deinit is SE-0371 --- ...chronous-deinit.md => 0371-isolated-synchronous-deinit.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{NNNN-isolated-synchronous-deinit.md => 0371-isolated-synchronous-deinit.md} (99%) diff --git a/proposals/NNNN-isolated-synchronous-deinit.md b/proposals/0371-isolated-synchronous-deinit.md similarity index 99% rename from proposals/NNNN-isolated-synchronous-deinit.md rename to proposals/0371-isolated-synchronous-deinit.md index 27397febc2..274bd16be9 100644 --- a/proposals/NNNN-isolated-synchronous-deinit.md +++ b/proposals/0371-isolated-synchronous-deinit.md @@ -1,9 +1,9 @@ # Isolated synchronous deinit -* Proposal: [SE-NNNN](NNNN-isolated-synchronous-deinit.md) +* Proposal: [SE-0371](0371-isolated-synchronous-deinit.md) * Author: [Mykola Pokhylets](https://github.com/nickolas-pohilets) * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) -* Status: **Awaiting review** +* Status: **Active review(August 18...August 31, 2022)** * Implementation: [apple/swift#60057](https://github.com/apple/swift/pull/60057) * Review: ([pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) From 5f522f4680e4c9b64844fbf098a0977eb4b99fa9 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 18 Aug 2022 19:17:32 -0400 Subject: [PATCH 2693/4563] Add review link --- proposals/0371-isolated-synchronous-deinit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0371-isolated-synchronous-deinit.md b/proposals/0371-isolated-synchronous-deinit.md index 274bd16be9..661aeb0915 100644 --- a/proposals/0371-isolated-synchronous-deinit.md +++ b/proposals/0371-isolated-synchronous-deinit.md @@ -5,7 +5,7 @@ * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) * Status: **Active review(August 18...August 31, 2022)** * Implementation: [apple/swift#60057](https://github.com/apple/swift/pull/60057) -* Review: ([pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) +* Review: ([pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) ([review](https://forums.swift.org/t/se-0371-isolated-synchronous-deinit/59754)) ## Introduction From a084b133b7051d48df9887eb8c521180972bd48f Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 18 Aug 2022 17:43:44 -0700 Subject: [PATCH 2694/4563] SE-0367 is accepted --- proposals/0367-conditional-attributes.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0367-conditional-attributes.md b/proposals/0367-conditional-attributes.md index 4e5218a407..376fa16baf 100644 --- a/proposals/0367-conditional-attributes.md +++ b/proposals/0367-conditional-attributes.md @@ -3,10 +3,11 @@ * Proposal: [SE-0367](0367-conditional-attributes.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Active review (August 1...August 15, 2022)** +* Status: **Accepted** * Implementation: [apple/swift#60208](https://github.com/apple/swift/pull/60208) * Swift-evolution thread: [Pitch](https://forums.swift.org/t/pitch-conditional-compilation-for-attributes-and-modifiers/58339) +* Decision Notes: [Acceptance](https://forums.swift.org/t/accepted-se-0367-conditional-compilation-for-attributes/59756) ## Introduction From 40832191fb350db6f914e88f6e5083d05aced951 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 19 Aug 2022 18:02:17 +0100 Subject: [PATCH 2695/4563] [SE-0368] StaticBigInt: fix broken link (#1752) --- proposals/0368-staticbigint.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index d0a11c3fa8..3fe070ece3 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -192,7 +192,7 @@ John McCall made significant improvements to this proposal; and (in Swift 5.0) i [`Builtin.IntLiteral`]: -[numeric protocols]: +[numeric protocols]: [obsolete]: From 83ad816197f01265d380c05e80bd89bd9b7eddf3 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 18 Aug 2022 11:48:55 -0600 Subject: [PATCH 2696/4563] [SE-0370] replace some tabs with spaces --- ...nter-family-initialization-improvements.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index bb642ef78f..b1f21eca38 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -186,21 +186,21 @@ extension UnsafeMutableRawBufferPointer { as type: T.Type, repeating repeatedValue: T ) -> UnsafeMutableBufferPointer - func initializeMemory( + func initializeMemory( as type: S.Element.Type, from source: S ) -> (unwritten: S.Iterator, initialized: UnsafeMutableBufferPointer) where S: Sequence +++ func initializeMemory( as type: C.Element.Type, fromElements: C - ) -> UnsafeMutableBufferPointer where C: Collection + ) -> UnsafeMutableBufferPointer where C: Collection +++ func moveInitializeMemory( - as type: T.Type, fromElements: UnsafeMutableBufferPointer - ) -> UnsafeMutableBufferPointer + as type: T.Type, fromElements: UnsafeMutableBufferPointer + ) -> UnsafeMutableBufferPointer +++ func moveInitializeMemory( - as type: T.Type, fromElements: Slice> - ) -> UnsafeMutableBufferPointer + as type: T.Type, fromElements: Slice> + ) -> UnsafeMutableBufferPointer } ``` @@ -212,15 +212,15 @@ The first addition will initialize raw memory from a `Collection` and have simil extension UnsafeMutableRawPointer { +++ func initializeMemory(as type: T.Type, to value: T) -> UnsafeMutablePointer - func initializeMemory( + func initializeMemory( as type: T.Type, repeating repeatedValue: T, count: Int ) -> UnsafeMutablePointer - func initializeMemory( + func initializeMemory( as type: T.Type, from source: UnsafePointer, count: Int ) -> UnsafeMutablePointer - func moveInitializeMemory( + func moveInitializeMemory( as type: T.Type, from source: UnsafeMutablePointer, count: Int ) -> UnsafeMutablePointer } @@ -275,7 +275,7 @@ extension Slice> { func withMemoryRebound( to type: T.Type, _ body: (UnsafeMutableBufferPointer) throws -> Result - ) rethrows -> Result + ) rethrows -> Result } ``` @@ -296,7 +296,7 @@ extension Slice { ```swift extension Slice { - func copyMemory(from source: UnsafeRawBufferPointer) + func copyMemory(from source: UnsafeRawBufferPointer) func copyBytes(from source: C) where C.Element == UInt8 func initializeMemory( From 53ebafe19c083b3b9d96ac6c4d8c30afd83f62a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EC=9E=AC=ED=98=B8?= Date: Mon, 22 Aug 2022 02:57:52 +0900 Subject: [PATCH 2697/4563] [SE-0084] Fix minor typo (#1753) --- proposals/0084-trailing-commas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0084-trailing-commas.md b/proposals/0084-trailing-commas.md index f651d39336..d930b4a84d 100644 --- a/proposals/0084-trailing-commas.md +++ b/proposals/0084-trailing-commas.md @@ -95,4 +95,4 @@ The acceptance of SE-0084 will not affect existing code. * Chris Lattner: A narrower way to solve the same problem would be to allow a comma before the `)`, but *only* when there is a newline between them. -* Vlad S suggests introducing "newlines as separators for any comma-separated list, not limited by funcs/typles but also array/dicts/generic type list etc." +* Vlad S suggests introducing "newlines as separators for any comma-separated list, not limited by funcs/tuples but also array/dicts/generic type list, etc." From 401b62fb5d36dc3842c8950d84678fb71344475e Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 23 Aug 2022 06:10:43 -0600 Subject: [PATCH 2698/4563] [SE-0370] update labels of returned tuples --- ...nter-family-initialization-improvements.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index b1f21eca38..19a4265fd3 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -127,11 +127,12 @@ We propose to modify `UnsafeMutableBufferPointer` as follows: ```swift extension UnsafeMutableBufferPointer { func initialize(repeating repeatedValue: Element) - func initialize(from source: S) -> (S.Iterator, Index) where S: Sequence, S.Element == Element ++++ func initialize(from source: S) -> (unwritten: S.Iterator, index: Index) where S: Sequence, S.Element == Element +--- func initialize(from source: S) -> (S.Iterator, Index) where S: Sequence, S.Element == Element +++ func initialize(fromElements: C) -> Index where C: Collection, C.Element == Element --- func assign(repeating repeatedValue: Element) +++ func update(repeating repeatedValue: Element) -+++ func update(from source: S) -> (unwritten: S.Iterator, updated: Index) where S: Sequence, S.Element == Element ++++ func update(from source: S) -> (unwritten: S.Iterator, index: Index) where S: Sequence, S.Element == Element +++ func update(fromElements: C) -> Index where C: Collection, C.Element == Element +++ func moveInitialize(fromElements: UnsafeMutableBufferPointer) -> Index +++ func moveInitialize(fromElements: Slice) -> Index @@ -245,7 +246,7 @@ extension Slice> { extension Slice> { func initialize(repeating repeatedValue: Element) - func initialize(from source: S) -> (S.Iterator, Index) + func initialize(from source: S) -> (unwritten: S.Iterator, index: Index) where S.Element == Element func initialize(fromElements: C) -> Index @@ -255,7 +256,7 @@ extension Slice> { func update( from source: S - ) -> (iterator: S.Iterator, updated: Index) where S.Element == Element + ) -> (unwritten: S.Iterator, index: Index) where S.Element == Element func update( fromElements: C @@ -381,7 +382,7 @@ extension UnsafeMutableBufferPointer { /// the buffer's contents. /// - Returns: An iterator to any elements of `source` that didn't fit in the /// buffer, and the index one past the last updated element in the buffer. - public func update(from source: S) -> (unwritten: S.Iterator, assigned: Index) + public func update(from source: S) -> (unwritten: S.Iterator, index: Index) where S: Sequence, S.Element == Element /// Updates the buffer's initialized memory with the given elements. @@ -789,7 +790,7 @@ extension Slice { /// buffer, and an index to the next uninitialized element in the buffer. public func initialize( from source: S - ) -> (S.Iterator, Index) + ) -> (unwritten: S.Iterator, index: Index) where S: Sequence, Base == UnsafeMutableBufferPointer /// Initializes the buffer slice's memory with the given elements. @@ -839,7 +840,7 @@ extension Slice { /// buffer, and the index one past the last updated element in the buffer. public func update( from source: S - ) -> (unwritten: S.Iterator, updated: Index) + ) -> (unwritten: S.Iterator, index: Index) where S: Sequence, Base == UnsafeMutableBufferPointer /// Updates the buffer slice's initialized memory with the given elements. @@ -1607,6 +1608,10 @@ There are only four current symbols to be renamed by this proposal, and their re The initialization and updating functions that copy from `Collection` inputs use the argument label `fromElements`. This is a different label than used by the pre-existing functions that copy from `Sequence` inputs. We could use the same argument label (`from`) as with the `Sequence` inputs, but that would mean that we must return the `Iterator` for the `Collection` versions, and that is not necessarily desirable, especially if a particular `Iterator` cannot be copied cheaply. If we used the same argument label (`from`) and did not return `Iterator`, then the `Sequence` and `Collection` versions of the `initialize(from:)` would be overloaded by their return type, and that would be source-breaking: an existing use of the current function that doesn't destructure the returned tuple on assignment could now pick up the `Collection` overload, which would have a return value incompatible with the subsequent code which assumes that the return value is of type `(Iterator, Int)`. +##### Returned tuple labels + +Some of the pre-existing returned tuples do not have element labels. This proposal adds labels to them. This is technically source-breaking, in that if existing source uses exactly the proposed labels in a different position, then the returned tuple value would be shuffled. The chosen labels have sufficiently pointed names that the risk is very small. + ## Acknowledgments [Kelvin Ma](https://github.com/kelvin13) (aka [Taylor Swift](https://forums.swift.org/u/taylorswift/summary))'s initial versions of the pitch that became SE-0184 included more functions to manipulate initialization state. These were deferred, but much of the deferred functionality has not been pitched again until now. From 2078b49f64ea5f6a0221ea8323c8b1c618e27253 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 23 Aug 2022 06:29:00 -0600 Subject: [PATCH 2699/4563] [SE-0370] rename fromElements to fromContentsOf - add missing parameter labels - and also fix some doc-comments --- ...nter-family-initialization-improvements.md | 166 +++++++++--------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index 19a4265fd3..0b288e5c82 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -97,14 +97,14 @@ mutating func replaceSubrange(_ subrange: Range, with newElements: C) assert(newCount < buffer.count) let oldTail = subrange.upperBound..(from source: S) -> (unwritten: S.Iterator, index: Index) where S: Sequence, S.Element == Element --- func initialize(from source: S) -> (S.Iterator, Index) where S: Sequence, S.Element == Element -+++ func initialize(fromElements: C) -> Index where C: Collection, C.Element == Element ++++ func initialize(fromContentsOf source: C) -> Index where C: Collection, C.Element == Element --- func assign(repeating repeatedValue: Element) +++ func update(repeating repeatedValue: Element) +++ func update(from source: S) -> (unwritten: S.Iterator, index: Index) where S: Sequence, S.Element == Element -+++ func update(fromElements: C) -> Index where C: Collection, C.Element == Element -+++ func moveInitialize(fromElements: UnsafeMutableBufferPointer) -> Index -+++ func moveInitialize(fromElements: Slice) -> Index -+++ func moveUpdate(fromElements: `Self`) -> Index -+++ func moveUpdate(fromElements: Slice<`Self`>) -> Index ++++ func update(fromContentsOf source: C) -> Index where C: Collection, C.Element == Element ++++ func moveInitialize(fromContentsOf source: UnsafeMutableBufferPointer) -> Index ++++ func moveInitialize(fromContentsOf source: Slice) -> Index ++++ func moveUpdate(fromContentsOf source: `Self`) -> Index ++++ func moveUpdate(fromContentsOf source: Slice<`Self`>) -> Index +++ func deinitialize() -> UnsafeMutableRawBufferPointer +++ func initializeElement(at index: Index, to value: Element) @@ -192,20 +192,20 @@ extension UnsafeMutableRawBufferPointer { ) -> (unwritten: S.Iterator, initialized: UnsafeMutableBufferPointer) where S: Sequence +++ func initializeMemory( - as type: C.Element.Type, fromElements: C + as type: C.Element.Type, fromContentsOf source: C ) -> UnsafeMutableBufferPointer where C: Collection +++ func moveInitializeMemory( - as type: T.Type, fromElements: UnsafeMutableBufferPointer + as type: T.Type, fromContentsOf source: UnsafeMutableBufferPointer ) -> UnsafeMutableBufferPointer +++ func moveInitializeMemory( - as type: T.Type, fromElements: Slice> + as type: T.Type, fromContentsOf source: Slice> ) -> UnsafeMutableBufferPointer } ``` -The first addition will initialize raw memory from a `Collection` and have similar behaviour as `UnsafeMutableBufferPointer.initialize(fromElements:)`, described above. The other two initialize raw memory by moving data from another range of memory, leaving that other range of memory deinitialized. +The first addition will initialize raw memory from a `Collection` and have similar behaviour as `UnsafeMutableBufferPointer.initialize(fromContentsOf:)`, described above. The other two initialize raw memory by moving data from another range of memory, leaving that other range of memory deinitialized. ##### `UnsafeMutableRawPointer` @@ -249,7 +249,7 @@ extension Slice> { func initialize(from source: S) -> (unwritten: S.Iterator, index: Index) where S.Element == Element - func initialize(fromElements: C) -> Index + func initialize(fromContentsOf source: C) -> Index where C.Element == Element func update(repeating repeatedValue: Element) @@ -259,13 +259,13 @@ extension Slice> { ) -> (unwritten: S.Iterator, index: Index) where S.Element == Element func update( - fromElements: C + fromContentsOf source: C ) -> Index where C.Element == Element - func moveInitialize(fromElements source: UnsafeMutableBufferPointer) -> Index - func moveInitialize(fromElements source: Slice>) -> Index - func moveUpdate(fromElements source: UnsafeMutableBufferPointer) -> Index - func moveUpdate(fromElements source: Slice>) -> Index + func moveInitialize(fromContentsOf source: UnsafeMutableBufferPointer) -> Index + func moveInitialize(fromContentsOf source: Slice>) -> Index + func moveUpdate(fromContentsOf source: UnsafeMutableBufferPointer) -> Index + func moveUpdate(fromContentsOf source: Slice>) -> Index func deinitialize() -> UnsafeMutableRawBufferPointer @@ -309,15 +309,15 @@ extension Slice { ) -> (unwritten: S.Iterator, initialized: UnsafeMutableBufferPointer) func initializeMemory( - as type: C.Element.Type, fromElements: C + as type: C.Element.Type, fromContentsOf source: C ) -> UnsafeMutableBufferPointer func moveInitializeMemory( - as type: T.Type, fromElements: UnsafeMutableBufferPointer + as type: T.Type, fromContentsOf source: UnsafeMutableBufferPointer ) -> UnsafeMutableBufferPointer func moveInitializeMemory( - as type: T.Type, fromElements: Slice> + as type: T.Type, fromContentsOf source: Slice> ) -> UnsafeMutableBufferPointer func bindMemory(to type: T.Type) -> UnsafeMutableBufferPointer @@ -342,7 +342,7 @@ extension UnsafeMutableBufferPointer { /// /// Initializes the buffer's memory with the given elements. /// - /// Prior to calling the `initialize(fromElements:)` method on a buffer, + /// Prior to calling the `initialize(fromContentsOf:)` method on a buffer, /// the memory referenced by the buffer must be uninitialized, /// or the `Element` type must be a trivial type. After the call, /// the memory referenced by the buffer up to, but not including, @@ -350,16 +350,16 @@ extension UnsafeMutableBufferPointer { /// /// The returned index is the position of the next uninitialized element /// in the buffer, which is one past the last element written. - /// If `fromElements` contains no elements, the returned index is equal to - /// the buffer's `startIndex`. If `fromElements` contains an equal or greater + /// If `source` contains no elements, the returned index is equal to + /// the buffer's `startIndex`. If `source` contains an equal or greater /// number of elements than the buffer can hold, the returned index is equal /// to the buffer's `endIndex`. /// - /// - Parameter fromElements: A collection of elements to be used to + /// - Parameter source: A collection of elements to be used to /// initialize the buffer's storage. /// - Returns: An index to the next uninitialized element in the buffer, /// or `endIndex`. - func initialize(fromElements source: C) -> Index + func initialize(fromContentsOf source: C) -> Index where C: Collection, C.Element == Element /// Updates every element of this buffer's initialized memory. @@ -390,11 +390,11 @@ extension UnsafeMutableBufferPointer { /// The buffer's memory must be initialized or the buffer's `Element` type /// must be a trivial type. /// - /// - Parameter fromElements: A collection of elements to be used to update + /// - Parameter source: A collection of elements to be used to update /// the buffer's contents. /// - Returns: An index one past the last updated element in the buffer, /// or `endIndex`. - public func update(fromElements source: C) -> Index + public func update(fromContentsOf source: C) -> Index where C: Collection, C.Element == Element /// Moves instances from an initialized source buffer into the @@ -404,7 +404,7 @@ extension UnsafeMutableBufferPointer { /// The region of memory starting at this pointer and covering `source.count` /// instances of the buffer's `Element` type must be uninitialized, or /// `Element` must be a trivial type. After calling - /// `moveInitialize(fromElements:)`, the region is initialized and the memory + /// `moveInitialize(fromContentsOf:)`, the region is initialized and the memory /// region underlying `source` is uninitialized. /// /// - Parameter source: A buffer containing the values to copy. The memory region @@ -412,7 +412,7 @@ extension UnsafeMutableBufferPointer { /// referenced by `source` and this buffer may overlap. /// - Returns: An index to the next uninitialized element in the buffer, /// or `endIndex`. - public func moveInitialize(fromElements: Self) -> Index + public func moveInitialize(fromContentsOf source: Self) -> Index /// Moves instances from an initialized source buffer slice into the /// uninitialized memory referenced by this buffer, leaving the source memory @@ -421,7 +421,7 @@ extension UnsafeMutableBufferPointer { /// The region of memory starting at this pointer and covering `source.count` /// instances of the buffer's `Element` type must be uninitialized, or /// `Element` must be a trivial type. After calling - /// `moveInitialize(fromElements:)`, the region is initialized and the memory + /// `moveInitialize(fromContentsOf:)`, the region is initialized and the memory /// region underlying `source[..) -> Index + public func moveInitialize(fromContentsOf source: Slice) -> Index /// Updates this buffer's initialized memory initialized memory by moving /// all the elements from the source buffer, leaving the source memory /// uninitialized. /// /// The region of memory starting at this pointer and covering - /// `fromElements.count` instances of the buffer's `Element` type + /// `source.count` instances of the buffer's `Element` type /// must be initialized, or `Element` must be a trivial type. After calling - /// `moveUpdate(fromElements:)`, the memory region underlying + /// `moveUpdate(fromContentsOf:)`, the memory region underlying /// `source` is uninitialized. /// /// - Parameter source: A buffer containing the values to move. @@ -446,16 +446,16 @@ extension UnsafeMutableBufferPointer { /// memory regions referenced by `source` and this pointer must not overlap. /// - Returns: An index one past the last updated element in the buffer, /// or `endIndex`. - public func moveUpdate(fromElements: `Self`) -> Index + public func moveUpdate(fromContentsOf source: `Self`) -> Index /// Updates this buffer's initialized memory initialized memory by moving /// all the elements from the source buffer slice, leaving the source memory /// uninitialized. /// /// The region of memory starting at this pointer and covering - /// `fromElements.count` instances of the buffer's `Element` type + /// `fromContentsOf.count` instances of the buffer's `Element` type /// must be initialized, or `Element` must be a trivial type. After calling - /// `moveUpdate(fromElements:)`, the memory region underlying + /// `moveUpdate(fromContentsOf:)`, the memory region underlying /// `source[..) -> Index + public func moveUpdate(fromContentsOf source: Slice<`Self`>) -> Index /// Deinitializes every instance in this buffer. /// @@ -605,27 +605,27 @@ extension UnsafeMutableRawBufferPointer { /// Initializes the buffer's memory with the given elements, binding the /// initialized memory to the elements' type. /// - /// When calling the `initializeMemory(as:fromElements:)` method on a buffer + /// When calling the `initializeMemory(as:fromContentsOf:)` method on a buffer /// `b`, the memory referenced by `b` must be uninitialized, or initialized /// to a trivial type. `b` must be properly aligned for accessing `C.Element`. /// - /// This method initializes the buffer with the contents of `fromElements` - /// until `fromElements` is exhausted or the buffer runs out of available - /// space. After calling `initializeMemory(as:fromElements:)`, the memory + /// This method initializes the buffer with the contents of `source`, + /// until `source` is exhausted or the buffer runs out of available + /// space. After calling `initializeMemory(as:fromContentsOf:)`, the memory /// referenced by the returned `UnsafeMutableBufferPointer` instance is bound /// and initialized to type `C.Element`. This method does not change /// the binding state of the unused portion of `b`, if any. /// /// - Parameters: /// - type: The type of element to which this buffer's memory will be bound. - /// - fromElements: A collection of elements to be used to + /// - source: A collection of elements to be used to /// initialize the buffer's storage. /// - Returns: A typed buffer containing the initialized elements. /// The returned buffer references memory starting at the same /// base address as this buffer, and its count indicates /// the number of elements copied from the collection `elements`. func initializeMemory( - as: C.Element.Type, fromElements: C + as: C.Element.Type, fromContentsOf source: C ) -> UnsafeMutableBufferPointer where C: Collection @@ -638,14 +638,14 @@ extension UnsafeMutableRawBufferPointer { /// trivial type. `b` must be properly aligned for accessing `C.Element`. /// /// The region of memory starting at this pointer and covering - /// `fromElements.count` instances of the buffer's `Element` type + /// `source.count` instances of the buffer's `Element` type /// must be uninitialized, or `Element` must be a trivial type. After /// calling `moveInitialize(as:from:)`, the region is initialized and the /// memory region underlying `source` is uninitialized. /// /// - Parameters: /// - type: The type of element to which this buffer's memory will be bound. - /// - fromElements: A buffer containing the values to copy. + /// - source: A buffer containing the values to copy. /// The memory region underlying `source` must be initialized. /// The memory regions referenced by `source` and this buffer may overlap. /// - Returns: A typed buffer of the initialized elements. The returned @@ -654,7 +654,7 @@ extension UnsafeMutableRawBufferPointer { /// `source`. func moveInitializeMemory( as type: T.Type, - fromElements: UnsafeMutableBufferPointer + fromContentsOf source: UnsafeMutableBufferPointer ) -> UnsafeMutableBufferPointer /// Moves instances from an initialized source buffer slice into the @@ -662,14 +662,14 @@ extension UnsafeMutableRawBufferPointer { /// uninitialized and this buffer's memory initialized. /// /// The region of memory starting at this pointer and covering - /// `fromElements.count` instances of the buffer's `Element` type + /// `source.count` instances of the buffer's `Element` type /// must be uninitialized, or `Element` must be a trivial type. After /// calling `moveInitialize(as:from:)`, the region is initialized and the /// memory region underlying `source[..( as type: T.Type, - fromElements: Slice> + fromContentsOf source: Slice> ) -> UnsafeMutableBufferPointer } ``` @@ -795,7 +795,7 @@ extension Slice { /// Initializes the buffer slice's memory with the given elements. /// - /// Prior to calling the `initialize(fromElements:)` method on a buffer slice, + /// Prior to calling the `initialize(fromContentsOf:)` method on a buffer slice, /// the memory it references must be uninitialized, /// or the `Element` type must be a trivial type. After the call, /// the memory referenced by the buffer slice up to, but not including, @@ -803,17 +803,17 @@ extension Slice { /// /// The returned index is the position of the next uninitialized element /// in the buffer slice, which is one past the last element written. - /// If `fromElements` contains no elements, the returned index is equal to - /// the buffer's `startIndex`. If `fromElements` contains an equal or greater + /// If `source` contains no elements, the returned index is equal to + /// the buffer's `startIndex`. If `source` contains an equal or greater /// of elements than the buffer slice can hold, the returned index is equal to /// to the buffer's `endIndex`. /// - /// - Parameter fromElements: A collection of elements to be used to + /// - Parameter source: A collection of elements to be used to /// initialize the buffer's storage. /// - Returns: An index to the next uninitialized element in the buffer, /// or `endIndex`. public func initialize( - fromElements source: C + fromContentsOf source: C ) -> Index where C : Collection, Base == UnsafeMutableBufferPointer @@ -848,12 +848,12 @@ extension Slice { /// The buffer slice's memory must be initialized or the buffer's `Element` type /// must be a trivial type. /// - /// - Parameter fromElements: A collection of elements to be used to update + /// - Parameter source: A collection of elements to be used to update /// the buffer's contents. /// - Returns: An index one past the last updated element in the buffer, /// or `endIndex`. public func update( - fromElements source: C + fromContentsOf source: C ) -> Index where C: Collection, Base == UnsafeMutableBufferPointer @@ -864,7 +864,7 @@ extension Slice { /// The region of memory starting at the beginning of this buffer and /// covering `source.count` instances of its `Element` type must be /// uninitialized, or `Element` must be a trivial type. After calling - /// `moveInitialize(fromElements:)`, the region is initialized and + /// `moveInitialize(fromContentsOf:)`, the region is initialized and /// the region of memory underlying `source` is uninitialized. /// /// - Parameter source: A buffer containing the values to copy. The memory @@ -873,7 +873,7 @@ extension Slice { /// - Returns: An index to the next uninitialized element in the buffer, /// or `endIndex`. public func moveInitialize( - fromElements source: UnsafeMutableBufferPointer + fromContentsOf source: UnsafeMutableBufferPointer ) -> Index where Base == UnsafeMutableBufferPointer @@ -884,7 +884,7 @@ extension Slice { /// The region of memory starting at the beginning of this buffer slice and /// covering `source.count` instances of its `Element` type must be /// uninitialized, or `Element` must be a trivial type. After calling - /// `moveInitialize(fromElements:)`, the region is initialized and + /// `moveInitialize(fromContentsOf:)`, the region is initialized and /// the region of memory underlying `source` is uninitialized. /// /// - Parameter source: A buffer containing the values to copy. The memory @@ -893,7 +893,7 @@ extension Slice { /// - Returns: An index one past the last replaced element in the buffer, /// or `endIndex`. public func moveInitialize( - fromElements source: Slice> + fromContentsOf source: Slice> ) -> Index where Base == UnsafeMutableBufferPointer @@ -902,9 +902,9 @@ extension Slice { /// leaving the source memory uninitialized. /// /// The region of memory starting at the beginning of this buffer slice and - /// covering `fromElements.count` instances of its `Element` type must be + /// covering `source.count` instances of its `Element` type must be /// initialized, or `Element` must be a trivial type. After calling - /// `moveUpdate(fromElements:)`, + /// `moveUpdate(fromContentsOf:)`, /// the region of memory underlying `source` is uninitialized. /// /// - Parameter source: A buffer containing the values to move. @@ -913,7 +913,7 @@ extension Slice { /// - Returns: An index one past the last updated element in the buffer, /// or `endIndex`. public func moveUpdate( - fromElements source: UnsafeMutableBufferPointer + fromContentsOf source: UnsafeMutableBufferPointer ) -> Index where Base == UnsafeMutableBufferPointer @@ -922,9 +922,9 @@ extension Slice { /// leaving the source memory uninitialized. /// /// The region of memory starting at the beginning of this buffer slice and - /// covering `fromElements.count` instances of its `Element` type must be + /// covering `source.count` instances of its `Element` type must be /// initialized, or `Element` must be a trivial type. After calling - /// `moveUpdate(fromElements:)`, + /// `moveUpdate(fromContentsOf:)`, /// the region of memory underlying `source` is uninitialized. /// /// - Parameter source: A buffer containing the values to move. @@ -933,7 +933,7 @@ extension Slice { /// - Returns: An index one past the last updated element in the buffer, /// or `endIndex`. public func moveUpdate( - fromElements source: Slice> + fromContentsOf source: Slice> ) -> Index where Base == UnsafeMutableBufferPointer @@ -1308,20 +1308,20 @@ extension Slice where Base == UnsafeMutableRawBufferPointer { /// Initializes the buffer's memory with the given elements, binding the /// initialized memory to the elements' type. /// - /// When calling the `initializeMemory(as:fromElements:)` method on a buffer + /// When calling the `initializeMemory(as:fromContentsOf:)` method on a buffer /// `b`, the memory referenced by `b` must be uninitialized, or initialized /// to a trivial type. `b` must be properly aligned for accessing `C.Element`. /// - /// This method initializes the buffer with the contents of `fromElements` - /// until `fromElements` is exhausted or the buffer runs out of available - /// space. After calling `initializeMemory(as:fromElements:)`, the memory + /// This method initializes the buffer with the contents of `source` + /// until `source` is exhausted or the buffer runs out of available + /// space. After calling `initializeMemory(as:fromContentsOf:)`, the memory /// referenced by the returned `UnsafeMutableBufferPointer` instance is bound /// and initialized to type `C.Element`. This method does not change /// the binding state of the unused portion of `b`, if any. /// /// - Parameters: /// - type: The type of element to which this buffer's memory will be bound. - /// - fromElements: A collection of elements to be used to + /// - source: A collection of elements to be used to /// initialize the buffer's storage. /// - Returns: A typed buffer of the initialized elements. The returned /// buffer references memory starting at the same base address as this @@ -1329,26 +1329,26 @@ extension Slice where Base == UnsafeMutableRawBufferPointer { /// the collection `elements`. func initializeMemory( as type: C.Element.Type, - fromElements source: C + fromContentsOf source: C ) -> UnsafeMutableBufferPointer /// Moves instances from an initialized source buffer into the /// uninitialized memory referenced by this buffer, leaving the source memory /// uninitialized and this buffer's memory initialized. /// - /// When calling the `moveInitializeMemory(as:from:)` method on a buffer `b`, + /// When calling the `moveInitializeMemory(as:fromContentsOf:)` method on a buffer `b`, /// the memory referenced by `b` must be uninitialized, or initialized to a /// trivial type. `b` must be properly aligned for accessing `C.Element`. /// /// The region of memory starting at this pointer and covering - /// `fromElements.count` instances of the buffer's `Element` type + /// `source.count` instances of the buffer's `Element` type /// must be uninitialized, or `Element` must be a trivial type. After /// calling `moveInitialize(as:from:)`, the region is initialized and the /// memory region underlying `source` is uninitialized. /// /// - Parameters: /// - type: The type of element to which this buffer's memory will be bound. - /// - fromElements: A buffer containing the values to copy. + /// - source: A buffer containing the values to copy. /// The memory region underlying `source` must be initialized. /// The memory regions referenced by `source` and this buffer may overlap. /// - Returns: A typed buffer of the initialized elements. The returned @@ -1357,7 +1357,7 @@ extension Slice where Base == UnsafeMutableRawBufferPointer { /// `source`. func moveInitializeMemory( as type: T.Type, - fromElements source: UnsafeMutableBufferPointer + fromContentsOf source: UnsafeMutableBufferPointer ) -> UnsafeMutableBufferPointer /// Moves instances from an initialized source buffer slice into the @@ -1365,14 +1365,14 @@ extension Slice where Base == UnsafeMutableRawBufferPointer { /// uninitialized and this buffer's memory initialized. /// /// The region of memory starting at this pointer and covering - /// `fromElements.count` instances of the buffer's `Element` type + /// `source.count` instances of the buffer's `Element` type /// must be uninitialized, or `Element` must be a trivial type. After /// calling `moveInitialize(as:from:)`, the region is initialized and the /// memory region underlying `source[..( as type: T.Type, - fromElements source: Slice> + fromContentsOf source: Slice> ) -> UnsafeMutableBufferPointer /// Binds this buffer’s memory to the specified type and returns a typed buffer @@ -1605,8 +1605,8 @@ There are only four current symbols to be renamed by this proposal, and their re ##### Element-by-element copies from `Collection` inputs -The initialization and updating functions that copy from `Collection` inputs use the argument label `fromElements`. This is a different label than used by the pre-existing functions that copy from `Sequence` inputs. We could use the same argument label (`from`) as with the `Sequence` inputs, but that would mean that we must return the `Iterator` for the `Collection` versions, and that is not necessarily desirable, especially if a particular `Iterator` cannot be copied cheaply. If we used the same argument label (`from`) and did not return `Iterator`, then the `Sequence` and `Collection` versions of the `initialize(from:)` would be overloaded by their return type, and that would be source-breaking: -an existing use of the current function that doesn't destructure the returned tuple on assignment could now pick up the `Collection` overload, which would have a return value incompatible with the subsequent code which assumes that the return value is of type `(Iterator, Int)`. +The initialization and updating functions that copy from `Collection` inputs use the argument label `fromContentsOf`. This is a different label than used by the pre-existing functions that copy from `Sequence` inputs. We could use the same argument label (`from`) as with the `Sequence` inputs, but that would mean that we must return the `Iterator` for the `Collection` versions, and that is not necessarily desirable, especially if a particular `Iterator` cannot be copied cheaply. If we used the same argument label (`from`) and did not return `Iterator`, then the `Sequence` and `Collection` versions of the `initialize(from:)` would be overloaded by their return type, and that would be source-breaking: +an existing use of the current function that doesn't destructure the returned tuple on assignment could now pick up the `Collection` overload, which would have a return value incompatible with the subsequent code which assumes that the return value is of type `(Iterator, Index)`. ##### Returned tuple labels From f078b5a3156b662946385e18f0b9925008467b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EC=9E=AC=ED=98=B8?= Date: Wed, 24 Aug 2022 04:39:24 +0900 Subject: [PATCH 2700/4563] [SE-0255] Fix typo and colon spacing (#1755) --- proposals/0255-omit-return.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0255-omit-return.md b/proposals/0255-omit-return.md index 719198b39d..7e3cf214a8 100644 --- a/proposals/0255-omit-return.md +++ b/proposals/0255-omit-return.md @@ -104,8 +104,8 @@ struct Echo { With an explicit getter and setter: ```swift -struct GuaranteedDictionary { - var storage: [Key : Value] +struct GuaranteedDictionary { + var storage: [Key: Value] var fallback: Value subscript(key: Key) -> Value { get { @@ -123,7 +123,7 @@ As with property accessors, since only the `get` accessor may return a value, im - Initializers. ```swift -class Derived : Base { +class Derived: Base { required init?() { nil } } ``` @@ -237,7 +237,7 @@ var doWork: Void { - **Making uninhabited types be bottom types.** -As currently implemented, an implicit conversion from an uninhabited type to any arbitrary type is permitted only if the uninhabited type is the type of the expression in a single-argument function and the arbitrary type is the the result type of that function. If every uninhabited type were a subtype of every type, this implicit conversion could be applied across the board without special casing for the single-argument return scenario. +As currently implemented, an implicit conversion from an uninhabited type to any arbitrary type is permitted only if the uninhabited type is the type of the expression in a single-argument function and the arbitrary type is the result type of that function. If every uninhabited type were a subtype of every type, this implicit conversion could be applied across the board without special casing for the single-argument return scenario. While such a feature can be implemented (see the [uninhabited-upcast](https://github.com/nate-chandler/swift/tree/nate/uninhabited-upcast) branch), it doesn't maintain source compatibility or otherwise relate to this feature except in terms of the compiler's implementation. From 6ea5ba71b47ed893e63f76713237c8d5dfa761f5 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 23 Aug 2022 17:25:42 -0600 Subject: [PATCH 2701/4563] [SE-0370] improve source-compatibility explanation --- proposals/0370-pointer-family-initialization-improvements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index 0b288e5c82..c6ab4781b0 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -1610,7 +1610,7 @@ an existing use of the current function that doesn't destructure the returned tu ##### Returned tuple labels -Some of the pre-existing returned tuples do not have element labels. This proposal adds labels to them. This is technically source-breaking, in that if existing source uses exactly the proposed labels in a different position, then the returned tuple value would be shuffled. The chosen labels have sufficiently pointed names that the risk is very small. +One of the pre-existing returned tuples does not have element labels, and the original version of the proposal did not change that. New labels are now proposed for this case. This is technically source-breaking, in that if existing source uses exactly the proposed labels in a different position, then the returned tuple value would be shuffled. The chosen labels have sufficiently pointed names that the risk is very small. ## Acknowledgments From 6073772ffecf4d3f3104f13b7bbf1d3f63db28ae Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 24 Aug 2022 03:26:57 +0100 Subject: [PATCH 2702/4563] [SE-0364] Fix status error by adding Review field (#1756) --- proposals/0364-retroactive-conformance-warning.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/proposals/0364-retroactive-conformance-warning.md b/proposals/0364-retroactive-conformance-warning.md index 7c901aca99..b35a29e06c 100644 --- a/proposals/0364-retroactive-conformance-warning.md +++ b/proposals/0364-retroactive-conformance-warning.md @@ -3,9 +3,12 @@ * Proposal: [SE-0364](0364-retroactive-conformance-warning.md) * Author: [Harlan Haskins](https://github.com/harlanhaskins) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Review Thread: https://forums.swift.org/t/se-0364-warning-for-retroactive-conformances-of-external-types/58922 -* Status: [**Returned for Revision**](https://forums.swift.org/t/returned-for-revision-se-0364-warning-for-retroactive-conformance-of-external-types/59729) +* Status: **Returned for Revision** * Implementation: [apple/swift#36068](https://github.com/apple/swift/pull/36068) +* Review: ([first pitch](https://forums.swift.org/t/warning-for-retroactive-conformances-if-library-evolution-is-enabled/45321)) + ([second pitch](https://forums.swift.org/t/pitch-warning-for-retroactive-conformances-of-external-types-in-resilient-libraries/56243)) + ([first review](https://forums.swift.org/t/se-0364-warning-for-retroactive-conformances-of-external-types/58922)) + ([revision](https://forums.swift.org/t/returned-for-revision-se-0364-warning-for-retroactive-conformance-of-external-types/59729)) ## Introduction From 0cf3ffa7e120693d315c2247a8c780eee853b143 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 24 Aug 2022 10:05:21 -0600 Subject: [PATCH 2703/4563] [SE-0370] add minor headings --- ...nter-family-initialization-improvements.md | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index c6ab4781b0..c1d7829f70 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -229,7 +229,7 @@ extension UnsafeMutableRawPointer { The addition here initializes a single value. -##### Slices of BufferPointer +##### Slices of `BufferPointer` We propose to extend slices of `Unsafe[Mutable][Raw]BufferPointer` with all the `BufferPointer`-specific methods of their `Base`. The following declarations detail the additions, which are all intended to behave exactly as the functions on the base BufferPointer types: @@ -336,6 +336,8 @@ extension Slice { ## Detailed design +##### `UnsafeMutableBufferPointer` + ```swift extension UnsafeMutableBufferPointer { /// Initializes the buffer's memory with the given elements. @@ -524,6 +526,8 @@ extension UnsafeMutableBufferPointer { } ``` +##### `UnsafeMutablePointer` + ```swift extension UnsafeMutablePointer { /// Update this pointer's initialized memory with the specified number of @@ -565,6 +569,8 @@ extension UnsafeMutablePointer { public func moveUpdate(from source: UnsafeMutablePointer, count: Int) ``` +##### `UnsafeMutableRawPointer` + ```swift extension UnsafeMutableRawPointer { /// Initializes the memory referenced by this pointer with the given value, @@ -600,6 +606,8 @@ extension UnsafeMutableRawPointer { } ``` +##### `UnsafeMutableRawBufferPointer` + ```swift extension UnsafeMutableRawBufferPointer { /// Initializes the buffer's memory with the given elements, binding the @@ -685,9 +693,10 @@ extension UnsafeMutableRawBufferPointer { -For `Slice`, the functions need to add an additional generic parameter, which is immediately restricted in the `where` clause. This is necessary because "parameterized extensions" are not yet a Swift feature. Eventually, these functions should be able to have exactly the same generic signatures as the counterpart function on their `UnsafeBufferPointer`-family base. This change will be neither source-breaking nor ABI-breaking. +For `Slice` of typed buffers, the functions need to add an additional generic parameter, which is immediately restricted in the `where` clause. This is necessary because "parameterized extensions" are not yet a Swift feature. Eventually, these functions should be able to have exactly the same generic signatures as the counterpart function on their `UnsafeBufferPointer`-family base. This change will be neither source-breaking nor ABI-breaking. + +##### `Slice` -Changes to `Slice`: ```swift extension Slice { /// Executes the given closure while temporarily binding the memory referenced @@ -752,7 +761,8 @@ extension Slice { } ``` -Changes for `Slice>`: +##### `Slice>` + ```swift extension Slice { /// Initializes every element in this buffer slice's memory to @@ -1061,7 +1071,8 @@ extension Slice { } ``` -Changes for `Slice`: +##### `Slice` + ```swift extension Slice where Base: UnsafeRawBufferPointer { @@ -1216,7 +1227,8 @@ extension Slice where Base: UnsafeRawBufferPointer { } ``` -Changes for `Slice`: +##### `Slice` + ```swift extension Slice where Base == UnsafeMutableRawBufferPointer { From a2aa4526deae16f87a1aabd3f3a3d90fba683bf7 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 24 Aug 2022 10:06:25 -0600 Subject: [PATCH 2704/4563] [SE-0370] remove `updateElement` from detailed design --- ...370-pointer-family-initialization-improvements.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index c1d7829f70..bd05595544 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -490,18 +490,6 @@ extension UnsafeMutableBufferPointer { /// - index: The index of the element to initialize public func initializeElement(at index: Index, to value: Element) - /// Updates the initialized buffer element at `index` with the given value. - /// - /// The destination element must be initialized, or - /// `Element` must be a trivial type. This method is equivalent to: - /// - /// self[index] = value - /// - /// - Parameters: - /// - value: The value used to update the buffer element's memory. - /// - index: The index of the element to update - public func updateElement(at index: Index, to value: Element) - /// Retrieves and returns the buffer element at `index`, /// leaving that element's memory uninitialized. /// From c9b2149b196e525240b53ca96620e9a6e5390390 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 24 Aug 2022 12:27:50 -0600 Subject: [PATCH 2705/4563] [SE-0370] add some missing `public` keywords --- ...nter-family-initialization-improvements.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index bd05595544..68b986d460 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -235,7 +235,7 @@ We propose to extend slices of `Unsafe[Mutable][Raw]BufferPointer` with all the ```swift extension Slice> { - public func withMemoryRebound( + func withMemoryRebound( to type: T.Type, _ body: (UnsafeBufferPointer) throws -> Result ) rethrows -> Result @@ -1127,7 +1127,7 @@ extension Slice where Base: UnsafeRawBufferPointer { /// the return value for the `withMemoryRebound(to:capacity:_:)` method. /// - buffer: The buffer temporarily bound to instances of `T`. /// - Returns: The return value, if any, of the `body` closure parameter. - func withMemoryRebound( + public func withMemoryRebound( to type: T.Type, _ body: (UnsafeBufferPointer) throws -> Result ) rethrows -> Result @@ -1147,7 +1147,7 @@ extension Slice where Base: UnsafeRawBufferPointer { /// /// - Parameter to: The type `T` that the memory has already been bound to. /// - Returns: A typed pointer to the same memory as this raw pointer. - func assumingMemoryBound(to type: T.Type) -> UnsafeBufferPointer + public func assumingMemoryBound(to type: T.Type) -> UnsafeBufferPointer /// Returns a new instance of the given type, read from the /// specified offset into the buffer pointer slice's raw memory. @@ -1178,7 +1178,7 @@ extension Slice where Base: UnsafeRawBufferPointer { /// with `type`. /// - Returns: A new instance of type `T`, copied from the buffer pointer /// slice's memory. - func load(fromByteOffset offset: Int = 0, as type: T.Type) -> T + public func load(fromByteOffset offset: Int = 0, as type: T.Type) -> T /// Returns a new instance of the given type, read from the /// specified offset into the buffer pointer slice's raw memory. @@ -1211,7 +1211,7 @@ extension Slice where Base: UnsafeRawBufferPointer { /// with `type`. /// - Returns: A new instance of type `T`, copied from the buffer pointer's /// memory. - func loadUnaligned(fromByteOffset offset: Int = 0, as type: T.Type) -> T + public func loadUnaligned(fromByteOffset offset: Int = 0, as type: T.Type) -> T } ``` @@ -1236,7 +1236,7 @@ extension Slice where Base == UnsafeMutableRawBufferPointer { /// /// - Parameter source: A buffer of raw bytes. `source.count` must /// be less than or equal to this buffer slice's `count`. - func copyMemory(from source: UnsafeRawBufferPointer) + public func copyMemory(from source: UnsafeRawBufferPointer) /// Copies from a collection of `UInt8` into this buffer slice's memory. /// @@ -1447,7 +1447,7 @@ extension Slice where Base == UnsafeMutableRawBufferPointer { /// the return value for the `withMemoryRebound(to:capacity:_:)` method. /// - buffer: The buffer temporarily bound to instances of `T`. /// - Returns: The return value, if any, of the `body` closure parameter. - func withMemoryRebound( + public func withMemoryRebound( to type: T.Type, _ body: (UnsafeMutableBufferPointer) throws -> Result ) rethrows -> Result @@ -1467,7 +1467,7 @@ extension Slice where Base == UnsafeMutableRawBufferPointer { /// /// - Parameter to: The type `T` that the memory has already been bound to. /// - Returns: A typed pointer to the same memory as this raw pointer. - func assumingMemoryBound(to type: T.Type) -> UnsafeMutableBufferPointer + public func assumingMemoryBound(to type: T.Type) -> UnsafeMutableBufferPointer /// Returns a new instance of the given type, read from the /// specified offset into the buffer pointer slice's raw memory. @@ -1498,7 +1498,7 @@ extension Slice where Base == UnsafeMutableRawBufferPointer { /// with `type`. /// - Returns: A new instance of type `T`, copied from the buffer pointer /// slice's memory. - func load(fromByteOffset offset: Int = 0, as type: T.Type) -> T + public func load(fromByteOffset offset: Int = 0, as type: T.Type) -> T /// Returns a new instance of the given type, read from the /// specified offset into the buffer pointer slice's raw memory. @@ -1531,7 +1531,7 @@ extension Slice where Base == UnsafeMutableRawBufferPointer { /// with `type`. /// - Returns: A new instance of type `T`, copied from the buffer pointer's /// memory. - func loadUnaligned(fromByteOffset offset: Int = 0, as type: T.Type) -> T + public func loadUnaligned(fromByteOffset offset: Int = 0, as type: T.Type) -> T /// Stores a value's bytes into the buffer pointer slice's raw memory at the /// specified byte offset. @@ -1568,7 +1568,7 @@ extension Slice where Base == UnsafeMutableRawBufferPointer { /// - type: The type to use for the newly constructed instance. The memory /// must be initialized to a value of a type that is layout compatible /// with `type`. - func storeBytes(of value: T, toByteOffset offset: Int = 0, as type: T.Type) + public func storeBytes(of value: T, toByteOffset offset: Int = 0, as type: T.Type) } ``` From 3ba9151395667e9e0ed29e1cde5ded38e480dd5a Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 24 Aug 2022 12:33:15 -0600 Subject: [PATCH 2706/4563] [SE-0370] update docs for `fromContentsOf` methnods --- ...nter-family-initialization-improvements.md | 494 +++++++++++------- 1 file changed, 318 insertions(+), 176 deletions(-) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index 68b986d460..dc47bba267 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -150,7 +150,7 @@ extension UnsafeMutableBufferPointer { We would like to use the verb `update` instead of `assign`, in order to better communicate the intent of the API. It is currently a common programmer error to use one of the existing `assign` functions for uninitialized memory; using the verb `update` instead would express the precondition in the API name itself. -The methods that initialize or update from a `Collection` will have forgiving semantics, and copy the number of elements that they can, be that every available element or none, and then return the index in the buffer that follows the last element copied, which is cheaper than returning an iterator and a count. Unlike the existing `Sequence` functions, they include no preconditions beyond having a valid `Collection` and valid buffer, with the understanding that if a user needs stricter behaviour, it can be composed from these functions. +The methods that initialize or update from a `Collection` will have consistent (and strict) semantics, and require the destination buffer (or slice) to have enough storage to copy the entire source collection, and then return the index in the destination that follows the last element copied. The storage available precondition will be strictly enforced, resulting in a consistent behaviour. The existing `Sequence` functions intended such a precondition, but in practice cannot enforce it. We also add functions to manipulate the initialization state for single elements of the buffer. There is no `buffer.updateElement(at index: Index, to value: Element)`, because it can already be expressed as `buffer[index] = value`. @@ -342,20 +342,46 @@ extension Slice { extension UnsafeMutableBufferPointer { /// Initializes the buffer's memory with the given elements. /// - /// Initializes the buffer's memory with the given elements. + /// Prior to calling the `initialize(from:)` method on a buffer, + /// the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer up to, but not including, + /// the returned index is initialized. + /// The buffer must contain sufficient memory to accommodate + /// `source.underestimatedCount`. + /// + /// The returned index is the position of the next uninitialized element + /// in the buffer, which is one past the last element written. + /// If `source` contains no elements, the returned index is equal to + /// the buffer's `startIndex`. If `source` contains an equal or greater number + /// of elements than the buffer can hold, the returned index is equal to + /// the buffer's `endIndex`. + /// + /// - Parameter source: A sequence of elements with which to initialize the + /// buffer. + /// - Returns: An iterator to any elements of `source` that didn't fit in the + /// buffer, and an index to the next uninitialized element in the buffer. + public func initialize( + from source: S + ) -> (unwritten: S.Iterator, index: Index) where S.Element == Element + + /// Initializes the buffer's memory with every element of the source. /// /// Prior to calling the `initialize(fromContentsOf:)` method on a buffer, /// the memory referenced by the buffer must be uninitialized, /// or the `Element` type must be a trivial type. After the call, /// the memory referenced by the buffer up to, but not including, /// the returned index is initialized. + /// The buffer must reference enough memory to accommodate + /// `source.count` elements. /// /// The returned index is the position of the next uninitialized element - /// in the buffer, which is one past the last element written. - /// If `source` contains no elements, the returned index is equal to - /// the buffer's `startIndex`. If `source` contains an equal or greater - /// number of elements than the buffer can hold, the returned index is equal - /// to the buffer's `endIndex`. + /// in the buffer, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// can hold, the returned index is equal to the buffer's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` /// /// - Parameter source: A collection of elements to be used to /// initialize the buffer's storage. @@ -387,84 +413,129 @@ extension UnsafeMutableBufferPointer { public func update(from source: S) -> (unwritten: S.Iterator, index: Index) where S: Sequence, S.Element == Element - /// Updates the buffer's initialized memory with the given elements. + /// Updates the buffer's initialized memory with every element of the source. /// - /// The buffer's memory must be initialized or the buffer's `Element` type - /// must be a trivial type. + /// Prior to calling the `update(fromContentsOf:)` method on a buffer, + /// the first `source.count` elements of the buffer's memory must be + /// initialized, or the buffer's `Element` type must be a trivial type. + /// The buffer must reference enough initialized memory to accommodate + /// `source.count` elements. + /// + /// The returned index is one past the index of the last element updated. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// can hold, the returned index is equal to the buffer's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` /// /// - Parameter source: A collection of elements to be used to update /// the buffer's contents. - /// - Returns: An index one past the last updated element in the buffer, - /// or `endIndex`. + /// - Returns: An index one past the index of the last element updated. public func update(fromContentsOf source: C) -> Index where C: Collection, C.Element == Element - /// Moves instances from an initialized source buffer into the + /// Moves every element of an initialized source buffer into the /// uninitialized memory referenced by this buffer, leaving the source memory /// uninitialized and this buffer's memory initialized. /// - /// The region of memory starting at this pointer and covering `source.count` - /// instances of the buffer's `Element` type must be uninitialized, or - /// `Element` must be a trivial type. After calling - /// `moveInitialize(fromContentsOf:)`, the region is initialized and the memory - /// region underlying `source` is uninitialized. + /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a buffer, + /// the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer up to, but not including, + /// the returned index is initialized. The memory referenced by + /// `source` is uninitialized after the function returns. + /// The buffer must reference enough memory to accommodate + /// `source.count` elements. /// - /// - Parameter source: A buffer containing the values to copy. The memory region - /// underlying `source` must be initialized. The memory regions + /// The returned index is the position of the next uninitialized element + /// in the buffer, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// can hold, the returned index is equal to the buffer's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Parameter source: A buffer containing the values to copy. The memory + /// region underlying `source` must be initialized. The memory regions /// referenced by `source` and this buffer may overlap. /// - Returns: An index to the next uninitialized element in the buffer, /// or `endIndex`. public func moveInitialize(fromContentsOf source: Self) -> Index - /// Moves instances from an initialized source buffer slice into the + /// Moves every element of an initialized source buffer into the /// uninitialized memory referenced by this buffer, leaving the source memory /// uninitialized and this buffer's memory initialized. /// - /// The region of memory starting at this pointer and covering `source.count` - /// instances of the buffer's `Element` type must be uninitialized, or - /// `Element` must be a trivial type. After calling - /// `moveInitialize(fromContentsOf:)`, the region is initialized and the memory - /// region underlying `source[..= `source.count` /// /// - Parameter source: A buffer containing the values to copy. The memory /// region underlying `source` must be initialized. The memory regions /// referenced by `source` and this buffer may overlap. - /// - Returns: An index one past the last replaced element in the buffer, + /// - Returns: An index to the next uninitialized element in the buffer, /// or `endIndex`. public func moveInitialize(fromContentsOf source: Slice) -> Index - /// Updates this buffer's initialized memory initialized memory by moving - /// all the elements from the source buffer, leaving the source memory - /// uninitialized. + /// Updates this buffer's initialized memory initialized memory by + /// moving every element from the source buffer slice, + /// leaving the source memory uninitialized. /// - /// The region of memory starting at this pointer and covering - /// `source.count` instances of the buffer's `Element` type - /// must be initialized, or `Element` must be a trivial type. After calling - /// `moveUpdate(fromContentsOf:)`, the memory region underlying - /// `source` is uninitialized. + /// Prior to calling the `moveUpdate(fromContentsOf:)` method on a buffer, + /// the first `source.count` elements of the buffer's memory must be + /// initialized, or the buffer's `Element` type must be a trivial type. + /// The memory referenced by `source` is uninitialized after the function + /// returns. The buffer must reference enough initialized memory + /// to accommodate `source.count` elements. + /// + /// The returned index is one past the index of the last element updated. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// can hold, the returned index is equal to the buffer's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` /// /// - Parameter source: A buffer containing the values to move. /// The memory region underlying `source` must be initialized. The /// memory regions referenced by `source` and this pointer must not overlap. - /// - Returns: An index one past the last updated element in the buffer, - /// or `endIndex`. + /// - Returns: An index one past the index of the last element updated. public func moveUpdate(fromContentsOf source: `Self`) -> Index - /// Updates this buffer's initialized memory initialized memory by moving - /// all the elements from the source buffer slice, leaving the source memory - /// uninitialized. + /// Updates this buffer's initialized memory initialized memory by + /// moving every element from the source buffer slice, + /// leaving the source memory uninitialized. /// - /// The region of memory starting at this pointer and covering - /// `fromContentsOf.count` instances of the buffer's `Element` type - /// must be initialized, or `Element` must be a trivial type. After calling - /// `moveUpdate(fromContentsOf:)`, the memory region underlying - /// `source[..= `source.count` + /// + /// - Parameter source: A buffer slice containing the values to move. /// The memory region underlying `source` must be initialized. The /// memory regions referenced by `source` and this pointer must not overlap. - /// - Returns: An index one past the last updated element in the buffer, - /// or `endIndex`. + /// - Returns: An index one past the index of the last element updated. public func moveUpdate(fromContentsOf source: Slice<`Self`>) -> Index /// Deinitializes every instance in this buffer. @@ -598,19 +669,21 @@ extension UnsafeMutableRawPointer { ```swift extension UnsafeMutableRawBufferPointer { - /// Initializes the buffer's memory with the given elements, binding the - /// initialized memory to the elements' type. + /// Initializes the buffer's memory with every element of the source, + /// binding the initialized memory to the elements' type. /// - /// When calling the `initializeMemory(as:fromContentsOf:)` method on a buffer - /// `b`, the memory referenced by `b` must be uninitialized, or initialized - /// to a trivial type. `b` must be properly aligned for accessing `C.Element`. + /// When calling the `initializeMemory(as:fromContentsOf:)` method, + /// the memory referenced by the buffer must be uninitialized, or initialized + /// to a trivial type. The buffer must reference enough memory to store + /// `source.count` elements, and its `baseAddress` must be properly aligned + /// for accessing `C.Element`. /// - /// This method initializes the buffer with the contents of `source`, - /// until `source` is exhausted or the buffer runs out of available - /// space. After calling `initializeMemory(as:fromContentsOf:)`, the memory + /// This method initializes the buffer with the contents of `source` + /// until `source` is exhausted. + /// After calling `initializeMemory(as:fromContentsOf:)`, the memory /// referenced by the returned `UnsafeMutableBufferPointer` instance is bound - /// and initialized to type `C.Element`. This method does not change - /// the binding state of the unused portion of `b`, if any. + /// to the type `C.Element` and is initialized. This method does not change + /// the binding state of the unused portion of the buffer, if any. /// /// - Parameters: /// - type: The type of element to which this buffer's memory will be bound. @@ -618,61 +691,72 @@ extension UnsafeMutableRawBufferPointer { /// initialize the buffer's storage. /// - Returns: A typed buffer containing the initialized elements. /// The returned buffer references memory starting at the same - /// base address as this buffer, and its count indicates - /// the number of elements copied from the collection `elements`. - func initializeMemory( + /// base address as this buffer, and its count is equal to `source.count` + public func initializeMemory( as: C.Element.Type, fromContentsOf source: C ) -> UnsafeMutableBufferPointer where C: Collection - /// Moves instances from an initialized source buffer into the + /// Moves every element of an initialized source buffer into the /// uninitialized memory referenced by this buffer, leaving the source memory /// uninitialized and this buffer's memory initialized. /// - /// When calling the `moveInitializeMemory(as:from:)` method on a buffer `b`, - /// the memory referenced by `b` must be uninitialized, or initialized to a - /// trivial type. `b` must be properly aligned for accessing `C.Element`. - /// - /// The region of memory starting at this pointer and covering - /// `source.count` instances of the buffer's `Element` type - /// must be uninitialized, or `Element` must be a trivial type. After - /// calling `moveInitialize(as:from:)`, the region is initialized and the + /// When calling the `moveInitializeMemory(as:fromContentsOf:)` method, + /// the memory referenced by the buffer must be uninitialized, or initialized + /// to a trivial type. The buffer must reference enough memory to store + /// `source.count` elements, and its `baseAddress` must be properly aligned + /// for accessing `C.Element`. After the method returns, + /// the memory referenced by the returned buffer is initialized and the /// memory region underlying `source` is uninitialized. /// + /// This method initializes the buffer with the contents of `source` + /// until `source` is exhausted. + /// After calling `initializeMemory(as:fromContentsOf:)`, the memory + /// referenced by the returned `UnsafeMutableBufferPointer` instance is bound + /// to the type `C.Element` and is initialized. This method does not change + /// the binding state of the unused portion of the buffer, if any. + /// /// - Parameters: /// - type: The type of element to which this buffer's memory will be bound. /// - source: A buffer containing the values to copy. /// The memory region underlying `source` must be initialized. /// The memory regions referenced by `source` and this buffer may overlap. - /// - Returns: A typed buffer of the initialized elements. The returned - /// buffer references memory starting at the same base address as this - /// buffer, and its count indicates the number of elements copied from - /// `source`. - func moveInitializeMemory( + /// - Returns: A typed buffer referencing the initialized elements. + /// The returned buffer references memory starting at the same + /// base address as this buffer, and its count is equal to `source.count`. + public func moveInitializeMemory( as type: T.Type, fromContentsOf source: UnsafeMutableBufferPointer ) -> UnsafeMutableBufferPointer - /// Moves instances from an initialized source buffer slice into the + /// Moves every element of an initialized source buffer slice into the /// uninitialized memory referenced by this buffer, leaving the source memory /// uninitialized and this buffer's memory initialized. /// - /// The region of memory starting at this pointer and covering - /// `source.count` instances of the buffer's `Element` type - /// must be uninitialized, or `Element` must be a trivial type. After - /// calling `moveInitialize(as:from:)`, the region is initialized and the - /// memory region underlying `source[..( + /// - Returns: A typed buffer referencing the initialized elements. + /// The returned buffer references memory starting at the same + /// base address as this buffer, and its count is equal to `source.count`. + public func moveInitializeMemory( as type: T.Type, fromContentsOf source: Slice> ) -> UnsafeMutableBufferPointer @@ -791,7 +875,7 @@ extension Slice { ) -> (unwritten: S.Iterator, index: Index) where S: Sequence, Base == UnsafeMutableBufferPointer - /// Initializes the buffer slice's memory with the given elements. + /// Initializes the buffer slice's memory with with every element of the source. /// /// Prior to calling the `initialize(fromContentsOf:)` method on a buffer slice, /// the memory it references must be uninitialized, @@ -799,17 +883,19 @@ extension Slice { /// the memory referenced by the buffer slice up to, but not including, /// the returned index is initialized. /// - /// The returned index is the position of the next uninitialized element - /// in the buffer slice, which is one past the last element written. + /// The returned index is the index of the next uninitialized element + /// in the buffer slice, one past the index of the last element written. /// If `source` contains no elements, the returned index is equal to - /// the buffer's `startIndex`. If `source` contains an equal or greater - /// of elements than the buffer slice can hold, the returned index is equal to - /// to the buffer's `endIndex`. + /// the buffer's `startIndex`. If `source` contains as many elements + /// as the buffer slice can hold, the returned index is equal to + /// to the slice's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` /// /// - Parameter source: A collection of elements to be used to - /// initialize the buffer's storage. - /// - Returns: An index to the next uninitialized element in the buffer, - /// or `endIndex`. + /// initialize the buffer's storage. + /// - Returns: An index to the next uninitialized element in the + /// buffer slice, or `endIndex`. public func initialize( fromContentsOf source: C ) -> Index @@ -841,15 +927,24 @@ extension Slice { ) -> (unwritten: S.Iterator, index: Index) where S: Sequence, Base == UnsafeMutableBufferPointer - /// Updates the buffer slice's initialized memory with the given elements. + /// Updates the buffer slice's initialized memory with every element of the source. /// - /// The buffer slice's memory must be initialized or the buffer's `Element` type - /// must be a trivial type. + /// Prior to calling the `update(fromContentsOf:)` method on a buffer + /// slice, the first `source.count` elements of the referenced memory must be + /// initialized, or the buffer's `Element` type must be a trivial type. + /// The buffer must reference enough initialized memory to accommodate + /// `source.count` elements. + /// + /// The returned index is one past the index of the last element updated. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// slice can hold, the returned index is equal to the slice's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` /// /// - Parameter source: A collection of elements to be used to update - /// the buffer's contents. - /// - Returns: An index one past the last updated element in the buffer, - /// or `endIndex`. + /// the buffer's contents. + /// - Returns: An index one past the index of the last element updated. public func update( fromContentsOf source: C ) -> Index @@ -859,17 +954,28 @@ extension Slice { /// uninitialized memory referenced by this buffer slice, leaving the /// source memory uninitialized and this buffer slice's memory initialized. /// - /// The region of memory starting at the beginning of this buffer and - /// covering `source.count` instances of its `Element` type must be - /// uninitialized, or `Element` must be a trivial type. After calling - /// `moveInitialize(fromContentsOf:)`, the region is initialized and - /// the region of memory underlying `source` is uninitialized. + /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a + /// buffer slice, the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer slice up to, but not including, + /// the returned index is initialized. The memory referenced by + /// `source` is uninitialized after the function returns. + /// The buffer slice must reference enough memory to accommodate + /// `source.count` elements. /// - /// - Parameter source: A buffer containing the values to copy. The memory - /// region underlying `source` must be initialized. The memory regions - /// referenced by `source` and this buffer may overlap. + /// The returned index is the position of the next uninitialized element + /// in the buffer slice, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// slice's `startIndex`. If `source` contains as many elements as the slice + /// can hold, the returned index is equal to the slice's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Parameter source: A buffer containing the values to copy. + /// The memory region underlying `source` must be initialized. The memory + /// regions referenced by `source` and this buffer may overlap. /// - Returns: An index to the next uninitialized element in the buffer, - /// or `endIndex`. + /// or `endIndex`. public func moveInitialize( fromContentsOf source: UnsafeMutableBufferPointer ) -> Index @@ -879,17 +985,28 @@ extension Slice { /// uninitialized memory referenced by this buffer slice, leaving the /// source memory uninitialized and this buffer slice's memory initialized. /// - /// The region of memory starting at the beginning of this buffer slice and - /// covering `source.count` instances of its `Element` type must be - /// uninitialized, or `Element` must be a trivial type. After calling - /// `moveInitialize(fromContentsOf:)`, the region is initialized and - /// the region of memory underlying `source` is uninitialized. + /// Prior to calling the `moveInitialize(fromContentsOf:)` method on a + /// buffer slice, the memory it references must be uninitialized, + /// or its `Element` type must be a trivial type. After the call, + /// the memory referenced by the buffer slice up to, but not including, + /// the returned index is initialized. The memory referenced by + /// `source` is uninitialized after the function returns. + /// The buffer slice must reference enough memory to accommodate + /// `source.count` elements. /// - /// - Parameter source: A buffer containing the values to copy. The memory - /// region underlying `source` must be initialized. The memory regions - /// referenced by `source` and this buffer may overlap. - /// - Returns: An index one past the last replaced element in the buffer, - /// or `endIndex`. + /// The returned index is the position of the next uninitialized element + /// in the buffer slice, one past the index of the last element written. + /// If `source` contains no elements, the returned index is equal to the + /// slice's `startIndex`. If `source` contains as many elements as the slice + /// can hold, the returned index is equal to the slice's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Parameter source: A buffer slice containing the values to copy. + /// The memory region underlying `source` must be initialized. The memory + /// regions referenced by `source` and this buffer slice may overlap. + /// - Returns: An index to the next uninitialized element in the buffer, + /// or `endIndex`. public func moveInitialize( fromContentsOf source: Slice> ) -> Index @@ -905,11 +1022,17 @@ extension Slice { /// `moveUpdate(fromContentsOf:)`, /// the region of memory underlying `source` is uninitialized. /// + /// The returned index is one past the index of the last element updated. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// slice can hold, the returned index is equal to the slice's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// /// - Parameter source: A buffer containing the values to move. - /// The memory region underlying `source` must be initialized. The - /// memory regions referenced by `source` and this pointer must not overlap. - /// - Returns: An index one past the last updated element in the buffer, - /// or `endIndex`. + /// The memory region underlying `source` must be initialized. The memory + /// regions referenced by `source` and this buffer slice must not overlap. + /// - Returns: An index one past the index of the last element updated. public func moveUpdate( fromContentsOf source: UnsafeMutableBufferPointer ) -> Index @@ -925,11 +1048,17 @@ extension Slice { /// `moveUpdate(fromContentsOf:)`, /// the region of memory underlying `source` is uninitialized. /// - /// - Parameter source: A buffer containing the values to move. - /// The memory region underlying `source` must be initialized. The - /// memory regions referenced by `source` and this pointer must not overlap. - /// - Returns: An index one past the last updated element in the buffer, - /// or `endIndex`. + /// The returned index is one past the index of the last element updated. + /// If `source` contains no elements, the returned index is equal to the + /// buffer's `startIndex`. If `source` contains as many elements as the buffer + /// slice can hold, the returned index is equal to the slice's `endIndex`. + /// + /// - Precondition: `self.count` >= `source.count` + /// + /// - Parameter source: A buffer slice containing the values to move. + /// The memory region underlying `source` must be initialized. The memory + /// regions referenced by `source` and this buffer slice must not overlap. + /// - Returns: An index one past the index of the last element updated. public func moveUpdate( fromContentsOf source: Slice> ) -> Index @@ -1305,81 +1434,94 @@ extension Slice where Base == UnsafeMutableRawBufferPointer { as type: S.Element.Type, from source: S ) -> (unwritten: S.Iterator, initialized: UnsafeMutableBufferPointer) - /// Initializes the buffer's memory with the given elements, binding the - /// initialized memory to the elements' type. + /// Initializes the buffer slice's memory with every element of the source, + /// binding the initialized memory to the elements' type. /// - /// When calling the `initializeMemory(as:fromContentsOf:)` method on a buffer - /// `b`, the memory referenced by `b` must be uninitialized, or initialized - /// to a trivial type. `b` must be properly aligned for accessing `C.Element`. + /// When calling the `initializeMemory(as:fromContentsOf:)` method, + /// the memory referenced by the buffer slice must be uninitialized, + /// or initialized to a trivial type. The buffer slice must reference + /// enough memory to store `source.count` elements, and it + /// must be properly aligned for accessing `C.Element`. /// /// This method initializes the buffer with the contents of `source` - /// until `source` is exhausted or the buffer runs out of available - /// space. After calling `initializeMemory(as:fromContentsOf:)`, the memory + /// until `source` is exhausted. + /// After calling `initializeMemory(as:fromContentsOf:)`, the memory /// referenced by the returned `UnsafeMutableBufferPointer` instance is bound - /// and initialized to type `C.Element`. This method does not change - /// the binding state of the unused portion of `b`, if any. + /// to the type `C.Element` and is initialized. This method does not change + /// the binding state of the unused portion of the buffer, if any. /// /// - Parameters: /// - type: The type of element to which this buffer's memory will be bound. /// - source: A collection of elements to be used to /// initialize the buffer's storage. - /// - Returns: A typed buffer of the initialized elements. The returned - /// buffer references memory starting at the same base address as this - /// buffer, and its count indicates the number of elements copied from - /// the collection `elements`. - func initializeMemory( + /// - Returns: A typed buffer referencing the initialized elements. + /// The returned buffer references memory starting at the same + /// base address as this slice, and its count is equal to `source.count` + public func initializeMemory( as type: C.Element.Type, fromContentsOf source: C ) -> UnsafeMutableBufferPointer - /// Moves instances from an initialized source buffer into the - /// uninitialized memory referenced by this buffer, leaving the source memory - /// uninitialized and this buffer's memory initialized. - /// - /// When calling the `moveInitializeMemory(as:fromContentsOf:)` method on a buffer `b`, - /// the memory referenced by `b` must be uninitialized, or initialized to a - /// trivial type. `b` must be properly aligned for accessing `C.Element`. - /// - /// The region of memory starting at this pointer and covering - /// `source.count` instances of the buffer's `Element` type - /// must be uninitialized, or `Element` must be a trivial type. After - /// calling `moveInitialize(as:from:)`, the region is initialized and the + /// Moves every element of an initialized source buffer into the + /// uninitialized memory referenced by this buffer slice, leaving + /// the source memory uninitialized and this slice's memory initialized. + /// + /// When calling the `moveInitializeMemory(as:fromContentsOf:)` method, + /// the memory referenced by the buffer slice must be uninitialized, + /// or initialized to a trivial type. The buffer slice must reference + /// enough memory to store `source.count` elements, and it must be properly + /// aligned for accessing `C.Element`. After the method returns, + /// the memory referenced by the returned buffer is initialized and the /// memory region underlying `source` is uninitialized. /// + /// This method initializes the buffer slice with the contents of `source` + /// until `source` is exhausted. + /// After calling `initializeMemory(as:fromContentsOf:)`, the memory + /// referenced by the returned `UnsafeMutableBufferPointer` instance is bound + /// to the type `C.Element` and is initialized. This method does not change + /// the binding state of the unused portion of the buffer slice, if any. + /// /// - Parameters: /// - type: The type of element to which this buffer's memory will be bound. - /// - source: A buffer containing the values to copy. + /// - source: A buffer referencing the values to copy. /// The memory region underlying `source` must be initialized. - /// The memory regions referenced by `source` and this buffer may overlap. - /// - Returns: A typed buffer of the initialized elements. The returned - /// buffer references memory starting at the same base address as this - /// buffer, and its count indicates the number of elements copied from - /// `source`. - func moveInitializeMemory( + /// The memory regions referenced by `source` and this slice may overlap. + /// - Returns: A typed buffer referencing the initialized elements. + /// The returned buffer references memory starting at the same + /// base address as this slice, and its count is equal to `source.count`. + public func moveInitializeMemory( as type: T.Type, fromContentsOf source: UnsafeMutableBufferPointer ) -> UnsafeMutableBufferPointer - /// Moves instances from an initialized source buffer slice into the - /// uninitialized memory referenced by this buffer, leaving the source memory - /// uninitialized and this buffer's memory initialized. + /// Moves every element from an initialized source buffer slice into the + /// uninitialized memory referenced by this buffer slice, leaving + /// the source memory uninitialized and this slice's memory initialized. /// - /// The region of memory starting at this pointer and covering - /// `source.count` instances of the buffer's `Element` type - /// must be uninitialized, or `Element` must be a trivial type. After - /// calling `moveInitialize(as:from:)`, the region is initialized and the - /// memory region underlying `source[..( + /// - Returns: A typed buffer referencing the initialized elements. + /// The returned buffer references memory starting at the same + /// base address as this slice, and its count is equal to `source.count`. + public func moveInitializeMemory( as type: T.Type, fromContentsOf source: Slice> ) -> UnsafeMutableBufferPointer From c29c7a32136c7c5b05d2250bd3338e0533a58d30 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 24 Aug 2022 16:00:09 -0600 Subject: [PATCH 2707/4563] [SE-0370] notes about source and ABI impact of tuple labels --- proposals/0370-pointer-family-initialization-improvements.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index dc47bba267..5e07148efe 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -1720,6 +1720,7 @@ This proposal consists mostly of additions, which are by definition source compa The proposal includes the renaming of four existing functions from `assign` to `update`. The existing function names would be deprecated, producing a warning. A fixit will support an easy transition to the renamed versions of these functions. +The proposal also includes the addition of labels to the tuple return value of one function. This is technically source breaking. We have tested the change against the source compatibility suite and found no issue. ## Effect on ABI stability @@ -1727,6 +1728,7 @@ The functions proposed here are generally small wrappers around existing functio The renamed functions can reuse the existing symbol, while the deprecated functions can forward using an `@_alwaysEmitIntoClient` stub to support the functionality under its previous name. The renamings would therefore have no ABI impact. +In the case of the added tuple labels, we can avoid an ABI impact by reusing the mangled symbol of the previously-existing function that had an unlabeled return tuple type. ## Effect on API resilience From b216264744a380a39ddf2f3192bd80751f81a10f Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 25 Aug 2022 11:37:22 +0100 Subject: [PATCH 2708/4563] [SE-0368] StaticBigInt: update revision history (#1758) --- proposals/0368-staticbigint.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index 3fe070ece3..77d4d588cb 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -15,9 +15,8 @@ | ---------- | ------------------------------------------------- | | 2022-01-10 | Initial pitch. | | 2022-02-01 | Updated with an "ABI-neutral" abstraction. | -| 2022-03-03 | Implemented the `SIMDWordsInteger` prototype. | | 2022-04-23 | Updated with an "infinitely-sign-extended" model. | -| 2022-08-08 | Updated with a "non-generic" subscript. | +| 2022-08-18 | Updated with a "non-generic" subscript. |
    Revision history From 7a6fb065f74a4d5ba05944817f471370d0c5e9fd Mon Sep 17 00:00:00 2001 From: "Mykola (Nickolas) Pokhylets" Date: Thu, 4 Aug 2022 19:01:49 +0200 Subject: [PATCH 2668/4563] Apply suggestions from code review Co-authored-by: Frederick Kellison-Linn --- proposals/NNNN-isolated-synchronous-deinit.md | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/proposals/NNNN-isolated-synchronous-deinit.md b/proposals/NNNN-isolated-synchronous-deinit.md index 7660e92f73..cafc6acefb 100644 --- a/proposals/NNNN-isolated-synchronous-deinit.md +++ b/proposals/NNNN-isolated-synchronous-deinit.md @@ -12,19 +12,19 @@ This feature lifts restrictions on `deinit` of actors and GAITs imposed by [SE-0 ## Motivation -Combination of automatics reference counting and deterministic deinitialization makes `deinit` in Swift a powerful tool for resource management. It greatly reduces need for `close()`-like methods (`unsubscribe()`, `cancel()`, `shutdown()`, etc.) in the public API. Such methods not only clutter public API, but also introduce a state where object is already unusable but is still referencable. +The combination of automatic reference counting and deterministic deinitialization makes `deinit` in Swift a powerful tool for resource management. It greatly reduces need for `close()`-like methods (`unsubscribe()`, `cancel()`, `shutdown()`, etc.) in the public API. Such methods not only clutter the public API, but also introduce a state where object is already unusable but is still able to be referenced. -Restrictions imposed by [SE-0327](https://github.com/apple/swift-evolution/blob/main/proposals/0327-actor-initializers.md) reduce usefulness of explicit `deinit`s in actors and GAITs. Workarounds for these limitations may involve creation of `close()`-like methods, or even manual reference counting, if API should be able to serve several clients. +Restrictions imposed by [SE-0327](https://github.com/apple/swift-evolution/blob/main/proposals/0327-actor-initializers.md) reduce the usefulness of explicit `deinit`s in actors and GAITs. Workarounds for these limitations may involve creation of `close()`-like methods, or even manual reference counting if the API should be able to serve several clients. -In cases when `deinit` belongs to a subclass of `UIView` or `UIViewController` which are known to call deinitializer on the main thread, developers may be tempted to silence the diagnostic by adopting `@unchecked Sendable` in types that are not actually sendable. This undermines concurrency checking by the compiler, and may lead to data races when using incorrectly marked types in other places. +In cases when `deinit` belongs to a subclass of `UIView` or `UIViewController` which are known to call `dealloc` on the main thread, developers may be tempted to silence the diagnostic by adopting `@unchecked Sendable` in types that are not actually sendable. This undermines concurrency checking by the compiler, and may lead to data races when using incorrectly marked types in other places. ## Proposed solution -... is to allow execution of `deinit` and object deallocation to be the scheduled on the executor, if needed. +Allow execution of `deinit` and object deallocation to be the scheduled on the executor of the containing type (either that of the actor itself or that of the relevant global actor), if needed. Let's consider [examples from SE-0327](https://github.com/apple/swift-evolution/blob/main/proposals/0327-actor-initializers.md#data-races-in-deinitializers): -In case of several instances with shared data isolated on common actor problem is completely eliminated: +In the case of several instances with shared data isolated on a common actor, the problem is completely eliminated: ```swift class NonSendableAhmed { @@ -48,9 +48,9 @@ class Maria { } deinit { - // Used to be a potential data race. - // Now deinit is also isolated on the MainActor. - // So this code is perfectly correct. + // Used to be a potential data race. Now, deinit is also + // isolated on the MainActor, so this code is perfectly + // correct. friend.state += 1 } } @@ -99,7 +99,7 @@ actor Clicker { Proposal introduces new runtime function that is used to schedule task-less block of synchronous code on the executor by wrapping it into an ad hoc task. It does not do any reference counting and can be safely used even with references that were released for the last time but not deallocated yet. -If no switching is needed, block is executed immediately on the current thread. Otherwise, task-less job copies priority and task-local values from the task/thread that released the last strong reference to the object. +If no switching is needed, the block is executed immediately on the current thread. Otherwise, the task-less job copies priority and task-local values from the task/thread that released the last strong reference to the object. ```cpp using DeinitWorkFunction = SWIFT_CC(swift) void (void *); @@ -208,7 +208,7 @@ Note that this is opposite from the rules for overriding functions. That's becau Implemented mechanism is not available to the ObjC code, so marking ObjC classes as isolated on global actor using `__attribute__((swift_attr(..)))` has no effect on behavior of the ObjC code. Such classes are imported into Swift as having non-isolated `deinit`. -But if `__attribute__((swift_attr(..)))` is used in `@interface` to explicitly mark `dealloc` method as isolated on global actor, then it is imported as isolated `deinit`. Marking `dealloc` as isolated means that `dealloc` must be called only on that executor. It is assumed that ObjC implementation of such class ensures this by overriding `retain/release`. `deinit` of the Swift subclasses is generated as an override of the `dealloc` method. Any pre-conditions which hold for the base class will be true for Swift subclasses as well. In this case `deinit` of the Swift subclass is type-checked as isolated, but isolation thunk is not generated for code size and performance optimization. +However if `__attribute__((swift_attr(..)))` is used in the class' `@interface` to explicitly mark the `dealloc` method as isolated on a global actor, then it is imported as an isolated `deinit`. Marking `dealloc` as isolated means that `dealloc` must be called only on that executor. It is assumed that the ObjC implementation of such class ensures this by overriding `retain`/`release`. The `deinit` of Swift subclasses of the Objective-C class is generated as an override of the `dealloc` method. Any pre-conditions which hold for the base class will be true for Swift subclasses as well. In this case `deinit` of the Swift subclass is type-checked as isolated, but an isolation thunk is not generated for code size and performance optimization. If `deinit` isolation was introduced into hierarchy of the `@objc` Swift classes by a class implemented in Swift, then `retain/release` are not overridden, `dealloc` can be called from any thread, but isolation happens inside `dealloc` implementation. In this case, isolation thunks will be generated for each isolated `deinit` in the hierarchy. Only the most derived one does the actual switching. The rest will be called already on the correct executor, and will follow the fast path in `swift_task_deinitOnExecutor()`. @@ -238,7 +238,7 @@ static void dealloc_impl_helper(void *ctx) { ### Exporting to ObjC -`deinit` isolation is relevant only when subclassing, but ObjC code cannot subclass Swift classes. Generated `*-Swift.h` files contain no information about `deinit` isolation. +`deinit` isolation is relevant only when subclassing since ObjC code cannot subclass Swift classes. The generated `*-Swift.h` files contain no information about `deinit` isolation. ### Isolated deinit of default actors @@ -250,37 +250,37 @@ When deinitializing an instance of default actors, `swift_task_deinitOnExecutor( ## Source compatibility -Proposal makes previously invalid code valid. +This proposal makes previously invalid code valid. -Proposal may alter behavior of existing GAIT and actors when last release happens on the different actor. But it is very unlikely that `deinit` of GAIT or an actor would have a synchronous externally-observable side effect that can be safely executed on a different actor. +This proposal may alter behavior of existing GAIT and actors when the last release happens on a different actor. But it is very unlikely that `deinit` of GAIT or an actor would have a synchronous externally-observable side effect that can be safely executed on a different actor. ## Effect on ABI stability -Proposal does not change ABI of the existing language features, but introduces new runtime function. +This proposal does not change the ABI of existing language features, but does introduce a new runtime function. ## Effect on API resilience Isolation attributes of the `deinit` become part of the public API, but it matters only when inheriting from the class. -Changing deinitializer from nonisolated to isolated is allowed on final Swift classes. But for non-final classes it may effect how deinitializers of the subclasses are generated. +Changing a `deinit` from nonisolated to isolated is allowed on final Swift classes. But for non-final classes it may effect how `deinit`s of the subclasses are generated. The same is true for changing identity of the isolating actor. -Changing deinitializer from isolated to nonisolated does not break ABI for Swift classes, including `@objc`-classes. Any non-recompiled subclasses will keep calling `deinit` of the superclass on the original actor. +Changing a `deinit` from isolated to nonisolated does not break ABI for Swift classes, including `@objc`-classes. Any non-recompiled subclasses will keep calling `deinit` of the superclass on the original actor. -Adding isolation annotation to `dealloc` method of the imported Objective-C classes is a breaking change. Existing Swift subclasses may have `deinit` isolated on different actor, and without recompilation will be calling `[super deinit]` on that actor. When recompiled, subclasses isolating on different actor will produce compilation error. Subclasses that had non-isolated deinitializer, or isolated on the same actor remain ABI compatible. It is possible to add isolation annotation to UIKit classes, because currently all their subclasses have nonisolated deinitializers. +Adding an isolation annotation to a `dealloc` method of an imported Objective-C class is a breaking change. Existing Swift subclasses may have `deinit` isolated on different actor, and without recompilation will be calling `[super deinit]` on that actor. When recompiled, subclasses isolating on a different actor will produce a compilation error. Subclasses that had a non-isolated `deinit` (or a `deinit` isolated on the same actor) remain ABI compatible. It is possible to add isolation annotation to UIKit classes, because currently all their subclasses have nonisolated `deinit`s. -Removing isolation annotation from the `dealloc` method (together with `retain/release` overrides) is a breaking change. Any existing subclasses would be type-checked as isolated but compiled without isolation thunks. After changes in the base class, subclass deinitializers could be called on the wrong executor. +Removing an isolation annotation from the `dealloc` method (together with `retain`/`release` overrides) is a breaking change. Any existing subclasses would be type-checked as isolated but compiled without isolation thunks. After changes in the base class, subclass `deinit`s could be called on the wrong executor. ## Future Directions ### Asynchronous `deinit` -Similar approach can be used to start an unstructured task for executing `async` deinit, but this is out of scope of this proposal. +A similar approach can be used to start an unstructured task for executing `async` deinit, but this is out of scope of this proposal. ### Asserting that `self` does not escape from `deinit`. -It is not legal for `self` to escape from `deinit`. Any strong references remaining after `swift_deallocObject()` is executed cannot be even released safely. Dereferencing dangling references can manifest itself as a crash, reading garbage data or corruption of unrelated memory. It can be hard to connect symptoms with the origin of the problem. Better solution would be to deterministically produce `fatalError()` in `swift_deallocObject()` if there are additional strong references. Ideal solution be to detect escaping `self` using compile-time analysis. +It is not legal for `self` to escape from `deinit`. Any strong references remaining after `swift_deallocObject()` is executed cannot be even released safely. Dereferencing dangling references can manifest itself as a crash, reading garbage data or corruption of unrelated memory. It can be hard to connect symptoms with the origin of the problem. A better solution would be to deterministically produce `fatalError()` in `swift_deallocObject()` if there are additional strong references. The ideal solution would be to detect escaping `self` using compile-time analysis. ### Improving de-virtualization and inlining of the executor access. @@ -302,7 +302,7 @@ public class Foo { } ``` -Currently both method and `deinit` entry points produce two calls to access the `MainActor.shared.unownedExecutor`, with the second one even using dynamic dispatch. +Currently both the `foo` and `deinit` entry points produce two calls to access the `MainActor.shared.unownedExecutor`, with the second one even using dynamic dispatch. These two calls could be replaced with a single call to the statically referenced `swift_task_getMainExecutor()`. ```llvm @@ -316,15 +316,15 @@ These two calls could be replaced with a single call to the statically reference ### Making fast path inlinable -For this to be useful compiler first needs to be able to reason about the value of the current executor based on the isolation of the surrounding function. Then inlinable pre-check for `swift_task_isCurrentExecutor()` can be inserted allowing isolated deallocating deinit to be inlined into non-isolated one. +For this to be useful the compiler first needs to be able to reason about the value of the current executor based on the isolation of the surrounding function. Then, an inlinable pre-check for `swift_task_isCurrentExecutor()` can be inserted allowing the isolated deallocating `deinit` to be inlined into the non-isolated one. ### Improving extended stack trace support -Developers who put breakpoint in the isolated deinit might want to see the call stack that lead to the least release of the object. Currently if switching of executors was involved, release call stack won't be shown in the debugger. +Developers who put breakpoints in the isolated deinit might want to see the call stack that lead to the last release of the object. Currently, if switching of executors was involved, the release call stack won't be shown in the debugger. ### Implementing API for synchronously scheduling arbitrary work on the actor -Introduced runtime functions has calling convention optimized for deinit use case, but using similar runtime function with a slightly different signature, one could implement an API for synchronously scheduling arbitrary work on the actor: +The introduced runtime functions has calling convention optimized for the `deinit` use case, but using a similar runtime function with a slightly different signature, one could implement an API for synchronously scheduling arbitrary work on the actor: ```swift extension Actor { @@ -351,17 +351,17 @@ a.enqueue { aIsolated in ### Placing hopping logic in `swift_release()` instead. -`UIView` and `UIViewController` implement hopping to the main thread by overriding `release` method. But in Swift there are no vtable/wvtable slots for releasing, and adding them would also affect a lot of code that does not need isolated deinit. +`UIView` and `UIViewController` implement hopping to the main thread by overriding the `release` method. But in Swift there are no vtable/wvtable slots for releasing, and adding them would also affect a lot of code that does not need isolated deinit. ### Deterministic task local values -When switching executors, current implementation copies priority and task-local values from the task/thread where the last release happened. This minimizes differences between isolated and nonisolated `deinit`'s. +When switching executors, the current implementation copies priority and task-local values from the task/thread where the last release happened. This minimizes differences between isolated and nonisolated `deinit`s. -If there are references from different tasks/threads, values of the priority and task-local values observed by the `deinit` are racy, both for isolated and nonsiolated `deinit`'s. +If there are references from different tasks/threads, the values of the priority and task-local values observed by the `deinit` are racy, both for isolated and nonsiolated `deinit`s. One way of making task-local values predictable would be to clear them for the duration of the `deinit` execution. This can be implemented efficiently, but would be too restrictive. -If object is referenced in several tasks which all have a common parent, then `deinit` can reliably use task-local values which are known to be set in the parent task and not overridden in the child tasks. +If the object is referenced in several tasks which all have a common parent, then `deinit` can reliably use task-local values which are known to be set in the parent task and not overridden in the child tasks. If there is a demand for resetting task-local values, it can be implemented separately as an API: @@ -373,18 +373,18 @@ func withoutTaskLocalValues(operation: () throws -> Void) rethrows ### Don't isolate empty explicit `deinit` -Empty explicit deinitializers are also eligible to be nonisolated as an optimization. But that would mean that interface of the declaration depends on its implementation. Currently, Swift infers function signature only for closure literals, but never for named functions. +Empty explicit `deinit`s are also eligible to be nonisolated as an optimization. But that would mean that the interface of the declaration depends on its implementation. Currently, Swift infers function signature only for closure literals, but never for named functions. ### Explicit opt-in into `deinit` isolation -This would eliminate any possibility of unexpected changes in behavior of the existing code, but would penalize future users, creating inconsistency between isolation inference rules for `deinit` and regular methods. Currently there is syntax for opt-out from isolation for actor instance members, but there is no syntax for opt-in. Having opt-in for deinitializers would require introducing such syntax, and it would be used only for actor deinitializers. There is already `isolated` keyword, but it is applied to function arguments, not to function declarations. +This would eliminate any possibility of unexpected changes in behavior of the existing code, but would penalize future users, creating inconsistency between isolation inference rules for `deinit` and regular methods. Currently there is syntax for opt-out from isolation for actor instance members, but there is no syntax for opt-in. Having opt-in for `deinit`s would require introducing such syntax, and it would be used only for actor `deinit`s. There is already the `isolated` keyword, but it is applied to function arguments, not to function declarations. -Classes whose deinitializers have nonisolated synchronous externally-visible side effects, like `AnyCancellable`, are unlikely to be isolated on global actors or be implemented as an actor. +Classes whose `deinit`s have nonisolated synchronous externally-visible side effects, like `AnyCancellable`, are unlikely to be isolated on global actors or be implemented as an actor. -Proposal preserves behavior of deinitializers that have synchronous externally-visible side effects only under assumption that they are always released on the isolating actor. +This proposal preserves behavior of `deinit`s that have synchronous externally-visible side effects only under assumption that they are always released on the isolating actor. -Deinitializers that explicitly notify about completion of their side effect continue to satisfy their contract even if proposal changes their behavior. +`deinit`s that explicitly notify about completion of their side effect continue to satisfy their contract even if proposal changes their behavior. ### Use asynchronous deinit as the only tool for `deinit` isolation -Synchronous deinit has an efficient fast path that jumps right into deinit implementation without context switching, or any memory allocations. But asynchronous deinit would require creation of task in all cases. +Synchronous `deinit` has an efficient fast path that jumps right into the `deinit` implementation without context switching, or any memory allocations. But asynchronous `deinit` would require creation of task in all cases. From 44a52b3bf5f81cedbda934d909f996801424e80a Mon Sep 17 00:00:00 2001 From: Mykola Pokhylets Date: Thu, 4 Aug 2022 19:15:37 +0200 Subject: [PATCH 2669/4563] Applied review comments --- proposals/NNNN-isolated-synchronous-deinit.md | 72 +++++++++++++------ 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/proposals/NNNN-isolated-synchronous-deinit.md b/proposals/NNNN-isolated-synchronous-deinit.md index cafc6acefb..8743cad29b 100644 --- a/proposals/NNNN-isolated-synchronous-deinit.md +++ b/proposals/NNNN-isolated-synchronous-deinit.md @@ -8,7 +8,7 @@ ## Introduction -This feature lifts restrictions on `deinit` of actors and GAITs imposed by [SE-0327](https://github.com/apple/swift-evolution/blob/main/proposals/0327-actor-initializers.md) by providing runtime support for hopping onto executors in `__deallocating_deinit()`'s. +This feature allows `deinit`'s of actors and global-actor isolated types (GAITs) to access non-sendable isolated state, lifting restrictions imposed imposed by [SE-0327](https://github.com/apple/swift-evolution/blob/main/proposals/0327-actor-initializers.md). This is achieved by providing runtime support for hopping onto executors in `__deallocating_deinit()`'s. ## Motivation @@ -62,7 +62,7 @@ func example() async { } ``` -In case of escaping self, race condition is eliminated but problem of escaping `self` remains. This problem exists for synchronous code as well and is orthogonal to the concurrency features. +In the case of escaping `self`, the race condition is eliminated but the problem of dangling reference remains. This problem exists for synchronous code as well and is orthogonal to the concurrency features. ```swift actor Clicker { @@ -97,9 +97,9 @@ actor Clicker { ### Runtime -Proposal introduces new runtime function that is used to schedule task-less block of synchronous code on the executor by wrapping it into an ad hoc task. It does not do any reference counting and can be safely used even with references that were released for the last time but not deallocated yet. +This proposal introduces a new runtime function that is used to schedule `deinit`'s code on an executor by wrapping it into a task-less ad hoc job. It does not do any reference counting and can be safely used even with references that were released for the last time but not deallocated yet. -If no switching is needed, the block is executed immediately on the current thread. Otherwise, the task-less job copies priority and task-local values from the task/thread that released the last strong reference to the object. +If no switching is needed, then `deinit` is executed immediately on the current thread. Otherwise, the task-less job copies priority and task-local values from the task/thread that released the last strong reference to the object. ```cpp using DeinitWorkFunction = SWIFT_CC(swift) void (void *); @@ -116,17 +116,17 @@ internal func _deinitOnExecutor(_ object: __owned AnyObject, _ executor: UnownedSerialExecutor) ``` -If `deinit` is isolated, code that normally is emitted into `__deallocating_init` gets emitted into new entity - `__isolated_deallocating_init`. And `__deallocating_init` is emitted as a thunk that reads executor from `self` (for actors) or global actor (for GAITs) and calls `swift_task_deinitOnExecutor` passing `self`, `__isolated_deallocating_init` and desired executor. +If `deinit` is isolated, code that normally is emitted into `__deallocating_init` gets emitted into a new entity (`__isolated_deallocating_init`), and `__deallocating_init` is emitted as a thunk that reads the executor (from `self` for actors and from the global actor for GAITs) and calls `swift_task_performOnExecutor` passing `self`, `__isolated_deallocating_init` and the desired executor. ![Last release from background thread](./NNNN-isolated-synchronous-deinit/last-release.png) ![Deinit on main thread](./NNNN-isolated-synchronous-deinit/isolated-deinit.png) -Non-deallocating deinit is not affected by the changes. +Non-deallocating `deinit` is not affected by this proposal. ### Rules for computing isolation -Isolation of deinit comes with runtime and code size cost. Classes that don't perform custom actions in deinit and only need to release references don't need isolated deinit. Releasing child objects can be done from any thread. If those objects are concerned about isolation, they should adopt isolation themselves. +Isolation of `deinit` comes with runtime and code size cost. Types that don't perform custom actions in `deinit` and only need to release references don't need isolated `deinit`. Releasing child objects can be done from any thread. If those objects are concerned about isolation, they should adopt isolation themselves. ```swift @MainActor @@ -145,7 +145,7 @@ actor MyActor { } ``` -If there is an explicit `deinit`, then isolation is computed following usual rules for isolation of class instance members. It takes into account isolation attributes on the `deinit` itself, isolation of the `deinit` in the superclass, and isolation attributes on the containing class. If deinit belongs to an actor or GAIT, but isolation of the `deinit` is undesired, it can be suppressed using `nonisolated` attribute: +If there is an explicit `deinit`, then isolation is computed following usual rules for isolation of class instance members as defined by [SE-0313](https://github.com/gottesmm/swift-evolution/blob/move-function-pitch-v1/proposals/0313-actor-isolation-control.md) and [SE-0316](https://github.com/apple/swift-evolution/blob/main/proposals/0316-global-actors.md). It takes into account isolation attributes on the `deinit` itself, isolation of the `deinit` in the superclass, and isolation attributes on the containing class. If deinit belongs to an actor or GAIT, but isolation of the `deinit` is undesired, it can be suppressed using `nonisolated` attribute: ```swift @MainActor @@ -173,7 +173,8 @@ actor AnotherActor { } ``` -When inheritance is involved, classes can add isolation to the non-isolated `deinit` of the base class, but they cannot change (remove or change actor) existing isolation. +When inheritance is involved, computed isolation is then validated to be compatible with isolation of the `deinit` of the base class. +Classes can add isolation to the non-isolated `deinit` of the base class, but they cannot change (remove or change actor) existing isolation. ```swift @FirstActor @@ -201,20 +202,20 @@ class Derived3: IsolatedBase { } ``` -Note that this is opposite from the rules for overriding functions. That's because in case of `deinit`, isolation applies to the non-deallocating `deinit`, while overriding happens for `__deallocating_deinit`. `__deallocating_deinit` is always non-isolated, and is responsible for switching to correct executor before calling non-deallocating `deinit`. Non-deallocating `deinit` of the subclass calls non-deallocating `deinit` of the superclass. And it is allowed to call nonisolated function from isolated one. +Note that this is opposite from the validation rules for overriding functions. That's because in case of `deinit`, isolation applies to the non-deallocating `deinit`, while overriding happens for `__deallocating_deinit`. `__deallocating_deinit` is always non-isolated, and is responsible for switching to correct executor before calling non-deallocating `deinit`. Non-deallocating `deinit` of the subclass calls non-deallocating `deinit` of the superclass. And it is allowed to call nonisolated function from isolated one. -### Importing ObjC code +### Importing Objective-C code -Implemented mechanism is not available to the ObjC code, so marking ObjC classes as isolated on global actor using `__attribute__((swift_attr(..)))` has no effect on behavior of the ObjC code. +Objective-C compiler does not generate any code to make `dealloc` isolated and marking Objective-C classes as isolated on global actor using `__attribute__((swift_attr(..)))` has no effect on behavior of the ObjC code. Such classes are imported into Swift as having non-isolated `deinit`. -However if `__attribute__((swift_attr(..)))` is used in the class' `@interface` to explicitly mark the `dealloc` method as isolated on a global actor, then it is imported as an isolated `deinit`. Marking `dealloc` as isolated means that `dealloc` must be called only on that executor. It is assumed that the ObjC implementation of such class ensures this by overriding `retain`/`release`. The `deinit` of Swift subclasses of the Objective-C class is generated as an override of the `dealloc` method. Any pre-conditions which hold for the base class will be true for Swift subclasses as well. In this case `deinit` of the Swift subclass is type-checked as isolated, but an isolation thunk is not generated for code size and performance optimization. +However if `__attribute__((swift_attr(..)))` is used in the class' `@interface` to explicitly mark the `dealloc` method as isolated on a global actor, then it is imported as an isolated `deinit`. Marking `dealloc` as isolated means that `dealloc` must be called only on that executor. It is assumed that the Objective-C implementation of such class ensures this by overriding `retain`/`release`. The `deinit` of Swift subclasses of the Objective-C class is generated as an override of the `dealloc` method. Any pre-conditions which hold for the base class will be true for Swift subclasses as well. In this case `deinit` of the Swift subclass is type-checked as isolated, but an isolation thunk is not generated for code size and performance optimization. -If `deinit` isolation was introduced into hierarchy of the `@objc` Swift classes by a class implemented in Swift, then `retain/release` are not overridden, `dealloc` can be called from any thread, but isolation happens inside `dealloc` implementation. In this case, isolation thunks will be generated for each isolated `deinit` in the hierarchy. Only the most derived one does the actual switching. The rest will be called already on the correct executor, and will follow the fast path in `swift_task_deinitOnExecutor()`. +If `deinit` isolation was introduced into the hierarchy of the `@objc` Swift classes by a class implemented in Swift, then `retain`/`release` are not overridden and `dealloc` can be called from any thread, but isolation happens inside `dealloc` implementation. In this case, isolation thunks will be generated for each isolated `deinit` in the hierarchy. Only the `deinit` of the subclass furthest down the hierarchy does the actual switching. The rest will be called when already on the correct executor, and will follow the fast path in `swift_task_performOnExecutor()`. -ObjC classes that isolate `dealloc` by overriding `retain/release` SHOULD mark `dealloc` as isolated. This not only allows Swift subclasses to fully benefit from isolation, but also prevents them from isolating their `deinit/dealloc` (including the call to `[super dealloc]`) on a different actor. +Objective-C classes that isolate `dealloc` by overriding `retain/release` should mark `dealloc` as isolated. This not only allows Swift subclasses to fully benefit from isolation, but also prevents them from isolating their `deinit/dealloc` (including the call to `[super dealloc]`) on a different actor. -On the other hand, if ObjC classes implement isolation by switching executors inside `dealloc`, they SHOULD NOT mark `dealloc` as isolated. Such `dealloc` can be called from any thread, and does not prevent Swift subclasses from isolating on different actor. And skipping isolation thunk in the Swift subclasses would be incorrect. +On the other hand, if Objective-C classes implement isolation by switching executors inside `dealloc`, they should not mark `dealloc` as isolated. Such `dealloc` can be called from any thread, and does not prevent Swift subclasses from isolating on different actor. And skipping isolation thunk in the Swift subclasses would be incorrect. ```objc // Non-ARC code @@ -236,13 +237,13 @@ static void dealloc_impl_helper(void *ctx) { } ``` -### Exporting to ObjC +### Exporting to Objective-C -`deinit` isolation is relevant only when subclassing since ObjC code cannot subclass Swift classes. The generated `*-Swift.h` files contain no information about `deinit` isolation. +`deinit` isolation is relevant only when subclassing since Objective-C code cannot subclass Swift classes. The generated `*-Swift.h` files contain no information about `deinit` isolation. ### Isolated deinit of default actors -When deinitializing an instance of default actors, `swift_task_deinitOnExecutor()` attempts to take actor's lock and execute deinit on the current thread. +When deinitializing an instance of default actor, `swift_task_deinitOnExecutor()` attempts to take actor's lock and execute deinit on the current thread. If previous executor was another default actor, it remains locked. So potentially multiple actors can be locked at the same time. This does not lead to deadlocks, because (1) lock is acquired conditionally, without waiting; and (2) object cannot be deinitializer twice, so graph of the deinit calls has no cycles. ### Interaction with distributed actors @@ -262,7 +263,7 @@ This proposal does not change the ABI of existing language features, but does in Isolation attributes of the `deinit` become part of the public API, but it matters only when inheriting from the class. -Changing a `deinit` from nonisolated to isolated is allowed on final Swift classes. But for non-final classes it may effect how `deinit`s of the subclasses are generated. +Changing a `deinit` from nonisolated to isolated is allowed on final Swift classes. But for non-final classes it is not allowed, because this effects how `deinit`s of the subclasses are generated. The same is true for changing identity of the isolating actor. @@ -302,7 +303,7 @@ public class Foo { } ``` -Currently both the `foo` and `deinit` entry points produce two calls to access the `MainActor.shared.unownedExecutor`, with the second one even using dynamic dispatch. +Currently both the `foo()` and `deinit` entry points produce two calls to access the `MainActor.shared.unownedExecutor`, with the second one even using dynamic dispatch. These two calls could be replaced with a single call to the statically referenced `swift_task_getMainExecutor()`. ```llvm @@ -324,7 +325,7 @@ Developers who put breakpoints in the isolated deinit might want to see the call ### Implementing API for synchronously scheduling arbitrary work on the actor -The introduced runtime functions has calling convention optimized for the `deinit` use case, but using a similar runtime function with a slightly different signature, one could implement an API for synchronously scheduling arbitrary work on the actor: +The introduced runtime function has calling convention optimized for the `deinit` use case, but using a similar runtime function with a slightly different signature, one could implement an API for synchronously scheduling arbitrary work on the actor: ```swift extension Actor { @@ -347,6 +348,33 @@ a.enqueue { aIsolated in } ``` +### Isolated deinit for move-only types without isolation thunk + +> [Joe Groff](https://forums.swift.org/t/isolated-synchronous-deinit/58177/17): +> +> Classes with shared ownership are ultimately the wrong tool for the job—they may be the least bad tool today, though. Ultimately, when we have move-only types, then since those have unique ownership, we'd be able to reason more strongly about what context their deinit executes in, since it would either happen at the end of the value's original lifetime, or if it's moved to a different owner, when that new owner consumes it. Within move-only types, we could also have a unique modifier for class types, to indicate statically that an object reference is the only one to the object, and that its release definitely executes deinit. + +For move-only types as a tool for explicit resource management, it may be desired to have switching or not switching actor to execute isolating `deinit` to be explicit as well. Such types could have isolated `deinit` without isolating thunk, and instead compiler would check that value is dropped on the correct actor. + +```swift +@moveonly +struct Resource { + @MainActor + deinit { + ... + } +} + +@MainActor func foo() { + let r: Resource = ... + Task.detached { [move r] in + // error: expression is 'async' but is not marked with 'await' + // note: dropping move-only value with isolated deinit from outside of its actor context is implicitly asynchronous + drop(r) + } +} +``` + ## Alternatives considered ### Placing hopping logic in `swift_release()` instead. From de7e9c9cc7caeee3f1eb1b4052c10b3d117d93e4 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 8 Aug 2022 07:48:52 -0700 Subject: [PATCH 2670/4563] SE-0365: update status to accepted --- proposals/0365-implicit-self-weak-capture.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0365-implicit-self-weak-capture.md b/proposals/0365-implicit-self-weak-capture.md index c3a8c77cff..3f710ae8b7 100644 --- a/proposals/0365-implicit-self-weak-capture.md +++ b/proposals/0365-implicit-self-weak-capture.md @@ -3,7 +3,7 @@ * Proposal: [SE-0365](0365-implicit-self-weak-capture.md) * Author: [Cal Stephens](https://github.com/calda) * Review Manager: [Saleem Abdulrasool](https://github.com/compnerd) -* Status: **Active review (July 18, 2022...August 1, 2022)** +* Status: **Accepted (2022-08-08)** * Implementation: [apple/swift#40702](https://github.com/apple/swift/pull/40702) ## Introduction From ad8f688fd543499679af39a708c23fa6b7590701 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 9 Aug 2022 21:36:14 -0700 Subject: [PATCH 2671/4563] Implementing (parts of) the Swift compiler in Swift isn't so ridiculous now. (#1738) Remove the item that states that we won't rewrite the Swift compiler in Swift. While it is still not an explicit goal, we have already started to reimplement specific parts of the Swift compiler and toolchain in Swift, and are looking to [incorporate it further] (e.g., https://forums.swift.org/t/implementing-parts-of-the-swift-compiler-in-swift/59524), so it doesn't make sense for this page to dismiss the idea. --- commonly_proposed.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/commonly_proposed.md b/commonly_proposed.md index 274ad55131..039c4566aa 100644 --- a/commonly_proposed.md +++ b/commonly_proposed.md @@ -35,7 +35,4 @@ Several of the discussions below refer to "C family" languages. This is intended ## Miscellaneous * [Use garbage collection (GC) instead of automatic reference counting (ARC)](https://forums.swift.org/t/what-about-garbage-collection/1360): Mark-and-sweep garbage collection is a well-known technique used in many popular and widely used languages (e.g., Java and JavaScript) and it has the advantage of automatically collecting reference cycles that ARC requires the programmer to reason about. That said, garbage collection has a [large number of disadvantages](https://forums.swift.org/t/what-about-garbage-collection/1360/6) and using it would prevent Swift from successfully targeting a number of systems programming domains. For example, real-time systems (video or audio processing), deeply embedded controllers, and most kernels find GC to be generally unsuitable. Further, GC is only efficient when given 3–4× more memory to work with than the process is using at any time, and this tradeoff is not acceptable for Swift. - * [Disjunctions (logical ORs) in type constraints](https://forums.swift.org/t/returned-for-revision-se-0095-replace-protocol-p1-p2-syntax-with-any-p1-p2/2855): These include anonymous union-like types (e.g. `(Int | String)` for a type that can be inhabited by either an integer or a string). "[This type of constraint is] something that the type system cannot and should not support." - - * [Rewrite the Swift compiler in Swift](https://github.com/apple/swift/blob/2c7b0b22831159396fe0e98e5944e64a483c356e/www/FAQ.rst): This would be a lot of fun someday, but (unless you include rewriting all of LLVM) requires the ability to import C++ APIs into Swift. Additionally, there are lots of higher priority ways to make Swift better. From 163e488b350edb2c8d4a00db22258ae630f21581 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 10 Aug 2022 15:55:15 -0600 Subject: [PATCH 2672/4563] add missing links, change to inline links --- .../NNNN-pointer-family-initialization-improvements.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/proposals/NNNN-pointer-family-initialization-improvements.md b/proposals/NNNN-pointer-family-initialization-improvements.md index 732ec6f61a..9eb28acea5 100644 --- a/proposals/NNNN-pointer-family-initialization-improvements.md +++ b/proposals/NNNN-pointer-family-initialization-improvements.md @@ -1,17 +1,13 @@ # Pointer Family Initialization Improvements & Better Buffer Slices -* Proposal: [SE-NNNN Pointer Family Initialization Improvements & Better Buffer Slices][proposal] +* Proposal: [SE-NNNN Pointer Family Initialization Improvements & Better Buffer Slices](https://github.com/apple/swift-evolution/blob/pointer-family-initialization/proposals/NNNN-pointer-family-initialization-improvements.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: TBD * Status: pending -* Implementation: [Draft Pull Request][draft-pr] +* Implementation: [Draft Pull Request](https://github.com/apple/swift/pull/41608) * Bugs: [rdar://51817146](rdar://51817146) * Previous Revision: [pitch A](https://gist.github.com/glessard/3bb47dce974aa483fd6df072d265005c ), [pitch B](https://gist.github.com/glessard/cefa5686696b0e30ac18eb4899213c65) -[proposal]: https://github.com/apple/swift-evolution/blob/pointer-family-initialization/proposals/NNNN-pointer-family-initialization-improvements.md -[draft-pr]: https://github.com/apple/swift/pull/41608 -[pitch-thread]: https://forums.swift.org/t/55689 - ## Introduction @@ -25,7 +21,7 @@ Memory can be safely deallocated whenever it is uninitialized. We intend to round out initialization functionality for every relevant member of that family: `UnsafeMutablePointer`, `UnsafeMutableRawPointer`, `UnsafeMutableBufferPointer`, `UnsafeMutableRawBufferPointer`, `Slice` and `Slice`. The functionality will allow managing initialization state in a much greater variety of situations, including easier handling of partially-initialized buffers. -Swift-evolution thread: [Pitch thread][pitch-thread] +Swift-evolution thread: [Pitch thread](https://forums.swift.org/t/55689), previous pitch threads [A](https://forums.swift.org/t/53168), [B](https://forums.swift.org/t/53795) ## Motivation From 677536cf02114f7a1f8dc23dc54d7f9c1c0fb3ff Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 11 Aug 2022 22:55:57 -0400 Subject: [PATCH 2673/4563] Assign myself as the review manager --- ...N-pointer-family-initialization-improvements.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/proposals/NNNN-pointer-family-initialization-improvements.md b/proposals/NNNN-pointer-family-initialization-improvements.md index 9eb28acea5..fd713cec99 100644 --- a/proposals/NNNN-pointer-family-initialization-improvements.md +++ b/proposals/NNNN-pointer-family-initialization-improvements.md @@ -1,13 +1,11 @@ -# Pointer Family Initialization Improvements & Better Buffer Slices +# Pointer Family Initialization Improvements and Better Buffer Slices -* Proposal: [SE-NNNN Pointer Family Initialization Improvements & Better Buffer Slices](https://github.com/apple/swift-evolution/blob/pointer-family-initialization/proposals/NNNN-pointer-family-initialization-improvements.md) +* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/pointer-family-initialization/proposals/NNNN-pointer-family-initialization-improvements.md) * Author: [Guillaume Lessard](https://github.com/glessard) -* Review Manager: TBD -* Status: pending +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Awaiting Review** * Implementation: [Draft Pull Request](https://github.com/apple/swift/pull/41608) -* Bugs: [rdar://51817146](rdar://51817146) -* Previous Revision: [pitch A](https://gist.github.com/glessard/3bb47dce974aa483fd6df072d265005c ), [pitch B](https://gist.github.com/glessard/cefa5686696b0e30ac18eb4899213c65) - +* Review: ([first pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements/53168)) ([second pitch](https://forums.swift.org/t/pitch-buffer-partial-initialization-better-buffer-slices/53795)) ([third pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements-better-buffer-slices/55689)) ## Introduction @@ -1633,4 +1631,4 @@ an existing use of the current function that doesn't destructure the returned tu [Kelvin Ma](https://github.com/kelvin13) (aka [Taylor Swift](https://forums.swift.org/u/taylorswift/summary))'s initial versions of the pitch that became SE-0184 included more functions to manipulate initialization state. These were deferred, but much of the deferred functionality has not been pitched again until now. -Members of the Swift Standard Library team for valuable discussions. \ No newline at end of file +Members of the Swift Standard Library team for valuable discussions. From 3f39a2c3581d5ed07a9d3facea1101bd18aeea17 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 12 Aug 2022 18:58:00 -0600 Subject: [PATCH 2674/4563] fix inconsistency with load/store functions on slices --- ...NNN-pointer-family-initialization-improvements.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/NNNN-pointer-family-initialization-improvements.md b/proposals/NNNN-pointer-family-initialization-improvements.md index fd713cec99..853ec3e21e 100644 --- a/proposals/NNNN-pointer-family-initialization-improvements.md +++ b/proposals/NNNN-pointer-family-initialization-improvements.md @@ -284,7 +284,7 @@ extension Slice> { } ``` -Slices of `Unsafe[Mutable]RawBufferPointer` will add memory binding functions, memory initialization functions, and variants of `load`, `loadUnaligned` and `storeBytes` that always load from the beginning of the slice. +Slices of `Unsafe[Mutable]RawBufferPointer` will add memory binding functions, memory initialization functions, and variants of `load`, `loadUnaligned` and `storeBytes`. ```swift extension Slice { func bindMemory(to type: T.Type) -> UnsafeBufferPointer @@ -294,8 +294,8 @@ extension Slice { to type: T.Type, _ body: (UnsafeBufferPointer) throws -> Result ) rethrows -> Result - func load(as type: T.Type) -> T - func loadUnaligned(as type: T.Type) -> T + func load(fromByteOffset offset: Int = 0, as type: T.Type) -> T + func loadUnaligned(fromByteOffset offset: Int = 0, as type: T.Type) -> T } ``` @@ -332,9 +332,9 @@ extension Slice { _ body: (UnsafeMutableBufferPointer) throws -> Result ) rethrows -> Result - func load(as type: T.Type) -> T - func loadUnaligned(as type: T.Type) -> T - func storeBytes(of value: T, as type: T.Type) + func load(fromByteOffset offset: Int = 0, as type: T.Type) -> T + func loadUnaligned(fromByteOffset offset: Int = 0, as type: T.Type) -> T + func storeBytes(of value: T, toByteOffset offset: Int = 0, as type: T.Type) } ``` From 25fd3610be4aaf1766cc2c36b54ccc621d133bab Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 12 Aug 2022 18:59:07 -0600 Subject: [PATCH 2675/4563] fix wording and markup --- .../NNNN-pointer-family-initialization-improvements.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/NNNN-pointer-family-initialization-improvements.md b/proposals/NNNN-pointer-family-initialization-improvements.md index 853ec3e21e..e87f4f0cda 100644 --- a/proposals/NNNN-pointer-family-initialization-improvements.md +++ b/proposals/NNNN-pointer-family-initialization-improvements.md @@ -19,7 +19,7 @@ Memory can be safely deallocated whenever it is uninitialized. We intend to round out initialization functionality for every relevant member of that family: `UnsafeMutablePointer`, `UnsafeMutableRawPointer`, `UnsafeMutableBufferPointer`, `UnsafeMutableRawBufferPointer`, `Slice` and `Slice`. The functionality will allow managing initialization state in a much greater variety of situations, including easier handling of partially-initialized buffers. -Swift-evolution thread: [Pitch thread](https://forums.swift.org/t/55689), previous pitch threads [A](https://forums.swift.org/t/53168), [B](https://forums.swift.org/t/53795) +Swift-evolution thread: [Pitch thread](https://forums.swift.org/t/55689), previous pitch threads [A](https://forums.swift.org/t/53168), [B](https://forums.swift.org/t/53795) ## Motivation @@ -210,7 +210,7 @@ extension UnsafeMutableRawBufferPointer { The first addition will initialize raw memory from a `Collection` and have similar behaviour as `UnsafeMutableBufferPointer.initialize(fromElements:)`, described above. The other two initialize raw memory by moving data from another range of memory, leaving that other range of memory deinitialized. -##### UnsafeMutableRawPointer +##### `UnsafeMutableRawPointer` ```swift extension UnsafeMutableRawPointer { @@ -234,7 +234,7 @@ The addition here initializes a single value. ##### Slices of BufferPointer -We propose to add to slices of `Unsafe[Mutable][Raw]BufferPointer` all the `BufferPointer`-specific methods of their `Base`. The following declarations detail the additions, which are all intended to behave exactly as the functions on the base BufferPointer types: +We propose to extend slices of `Unsafe[Mutable][Raw]BufferPointer` with all the `BufferPointer`-specific methods of their `Base`. The following declarations detail the additions, which are all intended to behave exactly as the functions on the base BufferPointer types: ```swift extension Slice> { From 858a9dbca689ca89b9f1aaddab7015906e49acc9 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 12 Aug 2022 19:24:01 -0600 Subject: [PATCH 2676/4563] remove single-element update functions --- .../NNNN-pointer-family-initialization-improvements.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/proposals/NNNN-pointer-family-initialization-improvements.md b/proposals/NNNN-pointer-family-initialization-improvements.md index e87f4f0cda..026106fe77 100644 --- a/proposals/NNNN-pointer-family-initialization-improvements.md +++ b/proposals/NNNN-pointer-family-initialization-improvements.md @@ -142,7 +142,6 @@ extension UnsafeMutableBufferPointer { +++ func deinitialize() -> UnsafeMutableRawBufferPointer +++ func initializeElement(at index: Index, to value: Element) -+++ func updateElement(at index: Index, to value: Element) +++ func moveElement(from index: Index) -> Element +++ func deinitializeElement(at index: Index) } @@ -154,7 +153,7 @@ We would like to use the verb `update` instead of `assign`, in order to better c The methods that initialize or update from a `Collection` will have forgiving semantics, and copy the number of elements that they can, be that every available element or none, and then return the index in the buffer that follows the last element copied, which is cheaper than returning an iterator and a count. Unlike the existing `Sequence` functions, they include no preconditions beyond having a valid `Collection` and valid buffer, with the understanding that if a user needs stricter behaviour, it can be composed from these functions. -The above changes include a method to update a single element. Evidently that is a synonym for the `subscript(_ i: Index)` setter. We hope that documenting the update action specifically will help clarify the requirements of that action, namely that the buffer element must already be initialized. Experience shows that the initialization requirement of the subscript setter is frequently not noticed by users in the current situation, where it is only documented along with the subscript getter. +We also add functions to manipulate the initialization state for single elements of the buffer. There is no `buffer.updateElement(at index: Index, to value: Element)`, because it can already be expressed as `buffer[index] = value`. ##### `UnsafeMutablePointer` @@ -165,7 +164,6 @@ extension UnsafeMutablePointer { func initialize(to value: Pointee) func initialize(repeating repeatedValue: Pointee, count: Int) func initialize(from source: UnsafePointer, count: Int) -+++ func update(to value: Pointee) --- func assign(repeating repeatedValue: Pointee, count: Int) +++ func update(repeating repeatedValue: Pointee, count: Int) --- func assign(from source: UnsafePointer, count: Int) @@ -273,7 +271,6 @@ extension Slice> { func deinitialize() -> UnsafeMutableRawBufferPointer func initializeElement(at index: Index, to value: Element) - func updateElement(at index: Index, to value: Element) func moveElement(at index: Index) -> Element func deinitializeElement(at index: Index) @@ -1612,9 +1609,7 @@ All functionality implemented as `@_alwaysEmitIntoClient` will back-deploy. Rena ##### Single element update functions -The single-element update functions, `UnsafeMutablePointer.update(to:)` and `UnsafeMutableBufferPointer.updateElement(at:to:)`, are synonyms for the setters of `UnsafeMutablePointer.pointee` and `UnsafeMutableBufferPointer.subscript(_ i: Index)`, respectively. Clearly we can elect to not add them. - -The setters in question, like the update functions, have a required precondition that the memory they refer to must be initialized. This precondition is often overlooked and leads to programmer errors and bug reports. The proposed names and cross-references should help clarify the requirements to users. +An earlier version of this proposal included single-element update functions, `UnsafeMutablePointer.update(to:)` and `UnsafeMutableBufferPointer.updateElement(at:to:)`. These are synonyms for the setters of `UnsafeMutablePointer.pointee` and `UnsafeMutableBufferPointer.subscript(_ i: Index)`, respectively. They were intended to improve the documentation for that operation, in particular the often overlooked initialization requirement. ##### Renaming `assign` to `update` From c8a0d7d42019a95c8c88ca8d14a302a41245372f Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 15 Aug 2022 18:49:01 -0400 Subject: [PATCH 2677/4563] Update NNNN-pointer-family-initialization-improvements.md Remove pitch links from the main body, since they're in the `Review` header field. --- proposals/NNNN-pointer-family-initialization-improvements.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/proposals/NNNN-pointer-family-initialization-improvements.md b/proposals/NNNN-pointer-family-initialization-improvements.md index 026106fe77..d3dcf4dcd6 100644 --- a/proposals/NNNN-pointer-family-initialization-improvements.md +++ b/proposals/NNNN-pointer-family-initialization-improvements.md @@ -19,8 +19,6 @@ Memory can be safely deallocated whenever it is uninitialized. We intend to round out initialization functionality for every relevant member of that family: `UnsafeMutablePointer`, `UnsafeMutableRawPointer`, `UnsafeMutableBufferPointer`, `UnsafeMutableRawBufferPointer`, `Slice` and `Slice`. The functionality will allow managing initialization state in a much greater variety of situations, including easier handling of partially-initialized buffers. -Swift-evolution thread: [Pitch thread](https://forums.swift.org/t/55689), previous pitch threads [A](https://forums.swift.org/t/53168), [B](https://forums.swift.org/t/53795) - ## Motivation Memory allocated using `UnsafeMutablePointer`, `UnsafeMutableRawPointer`, `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer` is passed to the user in an uninitialized state. In the general case, such memory needs to be initialized before it is used in Swift. Memory can be "initialized" or "uninitialized". We hereafter refer to this as a memory region's "initialization state". From 43849aa9ae3e87c434866c5a5e389af67537ca26 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 15 Aug 2022 20:26:47 -0400 Subject: [PATCH 2678/4563] Return SE-0366 for revision. (#1740) --- proposals/0366-move-function.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0366-move-function.md b/proposals/0366-move-function.md index 905efcf088..bc4525ef30 100644 --- a/proposals/0366-move-function.md +++ b/proposals/0366-move-function.md @@ -3,9 +3,9 @@ * Proposal: [SE-0366](0366-move-function.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Joe Groff](https://github.com/jckarter) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (July 25...August 8, 2022)** +* Status: **Returned for revision** * Implementation: Implemented on main as stdlib SPI (`_move` function instead of `move` keyword) -* Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic)) ([review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) +* Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic)) ([review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0366-move-operation-use-after-move-diagnostic/59687)) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md) ## Introduction From 97e1966a624cca8489bbacd63e878d51efcf1bb1 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 3 Aug 2022 20:37:33 -0400 Subject: [PATCH 2679/4563] Assign review manager --- proposals/NNNN-isolated-synchronous-deinit.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-isolated-synchronous-deinit.md b/proposals/NNNN-isolated-synchronous-deinit.md index 8743cad29b..7efa860d3e 100644 --- a/proposals/NNNN-isolated-synchronous-deinit.md +++ b/proposals/NNNN-isolated-synchronous-deinit.md @@ -1,10 +1,11 @@ # Isolated synchronous deinit * Proposal: [SE-NNNN](NNNN-isolated-synchronous-deinit.md) -* Authors: [Mykola Pokhylets](https://github.com/nickolas-pohilets) -* Review Manager: TBD +* Author: [Mykola Pokhylets](https://github.com/nickolas-pohilets) +* Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) * Status: **Awaiting review** * Implementation: [apple/swift#60057](https://github.com/apple/swift/pull/60057) +* Review: ([pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) ## Introduction From a1be8494f8eee431cfc1cb8bfaa45abc8d04c741 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 16 Aug 2022 09:30:10 -0700 Subject: [PATCH 2680/4563] [SE-0211] Explain why `isDefined` was removed in Alternatives Considered. --- proposals/0211-unicode-scalar-properties.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/proposals/0211-unicode-scalar-properties.md b/proposals/0211-unicode-scalar-properties.md index 439ff1b641..7d0d1522ad 100644 --- a/proposals/0211-unicode-scalar-properties.md +++ b/proposals/0211-unicode-scalar-properties.md @@ -398,3 +398,13 @@ However, since these properties are intended for advanced users who are likely already somewhat familiar with the Unicode Standard and its definitions, we decided to keep the names directly derived from the Standard, which makes them more discoverable to the intended audience. + +### `isDefined` Property + +The original version of this proposal also included a Boolean `isDefined` +property that was equivalent to the `u_isdefined` function from ICU, which +would evaluate to true for exactly the code points that are assigned (those +with general category other than "Cn"). In Swift, however, this would be +redundant and was thus dropped from the final implementation; its value would +differ from `Unicode.Scalar.Properties.generalCategory != .unassigned` only +for surrogate code points, which cannot be created as `Unicode.Scalar` values. From edc9754b032f913e45ee5721483d78ede4fec2e4 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 16 Aug 2022 11:10:00 -0600 Subject: [PATCH 2681/4563] remove another single-element update function --- ...NN-pointer-family-initialization-improvements.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/proposals/NNNN-pointer-family-initialization-improvements.md b/proposals/NNNN-pointer-family-initialization-improvements.md index d3dcf4dcd6..54c2670435 100644 --- a/proposals/NNNN-pointer-family-initialization-improvements.md +++ b/proposals/NNNN-pointer-family-initialization-improvements.md @@ -525,19 +525,6 @@ extension UnsafeMutableBufferPointer { ```swift extension UnsafeMutablePointer { - /// Update this pointer's initialized memory. - /// - /// The range of memory starting at this pointer and covering one instance - /// of `Pointee` must be initialized, or `Pointee` must be a trivial type. - /// This method is equivalent to: - /// - /// self.pointee = value - /// - /// - Parameters: - /// - value: The value used to update this pointer's memory. - public func update(_ value: Pointee) -} - /// Update this pointer's initialized memory with the specified number of /// instances, copied from the given pointer's memory. /// From 241869e1cda08419b6158e832ce9d6627cbd4eb8 Mon Sep 17 00:00:00 2001 From: Ben Pious Date: Tue, 16 Aug 2022 14:58:11 -0700 Subject: [PATCH 2682/4563] [SE-0369] Add CustomDebugDescription conformance to AnyKeyPath (#1724) * add proposal * Apply suggestions from code review Co-authored-by: Ben Rimmington * Update proposals/NNNN-add-customdebugdescription-conformance-to-anykeypath.md Co-authored-by: Ben Rimmington * cr feedback * Update proposals/NNNN-add-customdebugdescription-conformance-to-anykeypath.md Co-authored-by: Ben Rimmington * Update proposals/NNNN-add-customdebugdescription-conformance-to-anykeypath.md Co-authored-by: Ben Rimmington * Update headers for pre-review * cr feedback from xiaodi * Update proposals/NNNN-add-customdebugdescription-conformance-to-anykeypath.md Co-authored-by: Ben Rimmington * Update proposals/NNNN-add-customdebugdescription-conformance-to-anykeypath.md Co-authored-by: Ben Rimmington * Update proposals/NNNN-add-customdebugdescription-conformance-to-anykeypath.md Co-authored-by: Ben Rimmington * Update proposals/NNNN-add-customdebugdescription-conformance-to-anykeypath.md Co-authored-by: Ben Rimmington * fix * [SE-0369] Prepare document for review Co-authored-by: Ben Rimmington Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- ...ugdescription-conformance-to-anykeypath.md | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md diff --git a/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md b/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md new file mode 100644 index 0000000000..aeb1d0cdd1 --- /dev/null +++ b/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md @@ -0,0 +1,160 @@ +# Add CustomDebugStringConvertible conformance to AnyKeyPath + +* Proposal: [SE-0369](0369-add-customdebugdescription-conformance-to-anykeypath.md) +* Author: [Ben Pious](https://github.com/benpious) +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Active Review (August 16...August 29, 2022)** +* Implementation: [apple/swift#60133](https://github.com/apple/swift/pull/60133) +* Review: ([pitch](https://forums.swift.org/t/pitch-add-customdebugdescription-conformance-to-anykeypath/58705)) + +## Introduction + +This proposal is to add conformance to the protocol `CustomDebugStringConvertible` to `AnyKeyPath`. + +## Motivation + +Currently, passing a keypath to `print()`, or to the `po` command in LLDB, yields the standard output for a Swift class. This is not very useful. For example, given +```swift +struct Theme { + + var backgroundColor: Color + var foregroundColor: Color + + var overlay: Color { + backgroundColor.withAlpha(0.8) + } +} +``` +`print(\Theme.backgroundColor)` would have an output of roughly +``` +Swift.KeyPath +``` +which doesn't allow `foregroundColor` to be distinguished from any other property on `Theme`. + +Ideally, the output would be +``` +\Theme.backgroundColor +``` +exactly as it was written in the program. + +## Proposed solution + +Take advantage of whatever information is available in the binary to implement the `debugDescription` requirement of `CustomDebugStringConvertible`. In the best case, roughly the output above will be produced, in the worst cases, other, potentially useful information will be output instead. + +## Detailed design + +### Implementation of `CustomDebugStringConvertible` + +Much like the `_project` functions currently implemented in `KeyPath.swift`, this function would loop through the keypath's buffer, handling each segment as follows: + +For offset segments, the implementation is simple: use `_getRecursiveChildCount`, `_getChildOffset`, and `_getChildMetadata` to get the string name of the property. I believe these are the same mechanisms used by `Mirror` today. + +For optional chain, force-unwrap, etc. the function appends a hard coded "?" or "!", as appropriate. + +For computed segments, call `swift::lookupSymbol()` on the result of `getter()` in the `ComputedAccessorsPtr`. Demangle the result to get the property name. + +### Changes to the Swift Runtime + +To implement descriptions for computed segments, it is necessary to make two changes to the runtime: + +1. Expose a Swift calling-convention function to call `swift::lookupSymbol()`. +2. Implement and expose a function to demangle keypath functions without the ornamentation the existing demangling functions produce. + +### Dealing with missing data + +There are two known cases where data might not be available: + +1. type metadata has not been emitted because the target was built using the `swift-disable-reflection-metadata` flag +2. the linker has stripped the symbol names we're trying to look up + +In these cases, we would print the following: + +#### Offset case +`` where `x` is the memory offset we read from the reflection metadata, and `typename` is the type that will be returned. +So +``` +print(\Theme.backgroundColor) // outputs "\Theme." +``` + +#### `lookupSymbol` failure case + +In this case we'll print the address-in-memory as hex, plus the type name: +``` +print(\Theme.overlay) // outputs \Theme. +``` + +As it might be difficult to correlate a memory address with the name of the function, the type name may be useful here to provide extra context. + +## Source compatibility + +Programs that extend `AnyKeyPath` to implement `CustomDebugStringConvertible` themselves will no longer compile and the authors of such code will have to delete the conformance. Based on a search of Github, there are currently no publicly available Swift projects that do this. + +Calling `print` on a KeyPath will, of course, produce different results than before. + +It is unlikely that any existing Swift program is depending on the existing behavior in a production context. While it is likely that someone, somewhere has written code in unit tests that depends on the output of this function, any issues that result will be easy for the authors of such code to identify and fix, and will likely represent an improvement in the readability of those tests. + +## Effect on ABI stability + +This proposal will add a new var & protocol conformance to the Standard Library's ABI. It will be availability guarded appropriately. + +The new debugging output will not be backdeployed, so Swift programs running on older ABI stable versions of the OS won't be able to rely on the new output. + +## Effect on API resilience + +The implementation of `debugDescription` might change after the initial work to implement this proposal is done. In particular, the output format will not be guaranteed to be stable. Here are a few different changes we might anticipate making: + +- As new features are added to the compiler, there may be new metadata available in the binary to draw from. One example would be lookup tables of KeyPath segment to human-readable-name or some other unique, stable identifier +- Whenever a new feature is added to `KeyPath`, it will need to be reflected in the output of this function. For example, the `KeyPath`s produced by [\_forEachFieldWithKeyPath](https://github.com/apple/swift/blob/main/stdlib/public/core/ReflectionMirror.swift#L324) are incomplete, in the sense that they merely set a value at in offset in memory and do not call `didSet` observers. If this function were ever publicly exposed, it would be useful if this was surfaced in the debugging information. +- The behavior of subscript printing might be changed: for example, we might always print out the value of the argument to the subscript, or we might do so only in cases where the output is short. We might also change from `.subscript()` to `[]` +- The Swift language workgroup may create new policies around debug descriptions and the output of this function might need to be updated to conform to them + +## Alternatives considered + +### Print fully qualified names or otherwise add more information to the output + +ex. `\ModuleName.MyType.myField`, `> \ModuleName.MyType.myField` `(writable) \Theme.backgroundColor` + +As this is just for debugging, it seems likely that the information currently being provided would be enough to resolve any ambiguities. If ambiguities arose during a debugging session, in most cases the user could figure out exactly which keypath they were dealing with simply by running `po myKeyPath == \MyType.myField` till they found the right one. + +### Modify KeyPaths to include a string description + +This is an obvious solution to this problem, and would likely be very easy to implement, as the compiler already produces `_kvcString`. + +It has the additional advantage of being 100% reliable, to the point where it arguably could be the basis for implementing `description` rather than `debugDescription`. + +However, it would add to the code size of the compiled code, perhaps unacceptably so. Furthermore, it precludes the possibility of someday printing out the arguments of subscript based keypaths, as +these can be created dynamically. It would also add overhead to appending keypaths, as the string would also have to be appended. + +An alternative implementation of this idea would be the output of additional metadata: a lookup table of function -> name. However, this would require a lot of additional work on the compiler for a relatively small benefit. + +I think that most users who might want this _really_ want to use it to build something else, like encodable keypaths. Those features should be provided opt-in on a per-keypath or per-type basis, which will make it much more useful (and in the context of encodable keypaths specifically, eliminate major potential security issues). Such a feature should also include the option to let the user configure this string, so that it can remain backwards compatible with older versions of the program. + +### Make Keypath functions global to prevent the linker from stripping them + +This would also potentially make it feasible to change this proposal from implementing `debugDescription` to implementing `description`. + +This would also potentially bloat the binary and would increase linker times. It could also be a security issue, as `dlysm` would now be able to find these functions. + +I am not very knowledgeable about linkers or how typical Swift builds strip symbols, but I think it might be useful to have this as an option in some IDEs that build Swift programs. But that is beyond the scope of this proposal. + +## Future Directions + +### Add LLDB formatters/summaries + +This would be a good augmentation to this proposal, and might improve the developer experience, as there [might be debug metadata available to the debugger](https://forums.swift.org/t/pitch-add-customdebugdescription-conformance-to-anykeypath/58705/2) that is not available in the binary itself. + +However, I think it might be very difficult to implement this. I see two options: + +1. Implement a public reflection API for KeyPaths in the Swift Standard Library that the formatter can interact with from Python. +2. The formatter parses the raw memory of the KeyPath, essentially duplicating the code in `debugDescription`. + +I think (1) is overkill, especially considering the limited potential applications of this API beyond its use by the formatter. If it's possible to implement this as an `internal` function in the Swift stdlib then this is a much more attractive option. +From personal experience trying to parse KeyPath memory from outside the Standard Library, I think (2) would be extremely difficult to implement, and unsustainable to maintain considering that the [memory layout of KeyPaths](https://github.com/apple/swift/blob/main/docs/ABI/KeyPaths.md) is not ABI stable. + +### Make Keypath functions global in DEBUG builds only + +This may be necessary to allow `swift::lookupSymbol` to function correctly on Windows, Linux and other platforms that use COFF or ELF-like formats. + +## Acknowledgments + +Thanks to Joe Groff for answering several questions on the initial pitch document, and Slava Pestov for answering some questions about the logistics of pitching this. From 5e6c137520eb9160451509dae707fe8d2699a653 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 16 Aug 2022 18:04:21 -0400 Subject: [PATCH 2683/4563] [SE-0369] Add review thread link (#1742) --- ...0369-add-customdebugdescription-conformance-to-anykeypath.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md b/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md index aeb1d0cdd1..85da4b05a9 100644 --- a/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md +++ b/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md @@ -5,7 +5,7 @@ * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Active Review (August 16...August 29, 2022)** * Implementation: [apple/swift#60133](https://github.com/apple/swift/pull/60133) -* Review: ([pitch](https://forums.swift.org/t/pitch-add-customdebugdescription-conformance-to-anykeypath/58705)) +* Review: ([pitch](https://forums.swift.org/t/pitch-add-customdebugdescription-conformance-to-anykeypath/58705)) ([review](https://forums.swift.org/t/se-0369-add-customdebugstringconvertible-conformance-to-anykeypath/59704)) ## Introduction From c3ddea79e2804825ff44fca3a4181fa18ae7c273 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 17 Aug 2022 17:04:39 -0400 Subject: [PATCH 2684/4563] Assign SE-0370 to the pointer API improvements proposal and put it in review --- ....md => 0370-pointer-family-initialization-improvements.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{NNNN-pointer-family-initialization-improvements.md => 0370-pointer-family-initialization-improvements.md} (99%) diff --git a/proposals/NNNN-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md similarity index 99% rename from proposals/NNNN-pointer-family-initialization-improvements.md rename to proposals/0370-pointer-family-initialization-improvements.md index 54c2670435..dd58d178ce 100644 --- a/proposals/NNNN-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -1,9 +1,9 @@ # Pointer Family Initialization Improvements and Better Buffer Slices -* Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/pointer-family-initialization/proposals/NNNN-pointer-family-initialization-improvements.md) +* Proposal: [SE-0370](0370-pointer-family-initialization-improvements.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Awaiting Review** +* Status: **Active Review (August 17...29th, 2022)** * Implementation: [Draft Pull Request](https://github.com/apple/swift/pull/41608) * Review: ([first pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements/53168)) ([second pitch](https://forums.swift.org/t/pitch-buffer-partial-initialization-better-buffer-slices/53795)) ([third pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements-better-buffer-slices/55689)) From 6589f7215046a61a630be0b325bf2097544c2ffe Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 17 Aug 2022 17:09:33 -0400 Subject: [PATCH 2685/4563] Link SE-0370 to its review thread --- proposals/0370-pointer-family-initialization-improvements.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index dd58d178ce..2438910de6 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -3,9 +3,9 @@ * Proposal: [SE-0370](0370-pointer-family-initialization-improvements.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (August 17...29th, 2022)** +* Status: **Active Review (August 17...29, 2022)** * Implementation: [Draft Pull Request](https://github.com/apple/swift/pull/41608) -* Review: ([first pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements/53168)) ([second pitch](https://forums.swift.org/t/pitch-buffer-partial-initialization-better-buffer-slices/53795)) ([third pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements-better-buffer-slices/55689)) +* Review: ([first pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements/53168)) ([second pitch](https://forums.swift.org/t/pitch-buffer-partial-initialization-better-buffer-slices/53795)) ([third pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements-better-buffer-slices/55689)) ([review](https://forums.swift.org/t/se-0370-pointer-family-initialization-improvements-and-better-buffer-slices/59724)) ## Introduction From ae81032e8b478b00de7cd1064ae6bb55235f189e Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 17 Aug 2022 19:09:20 -0400 Subject: [PATCH 2686/4563] Update 0211-unicode-scalar-properties.md (#1746) Add link to update. --- proposals/0211-unicode-scalar-properties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0211-unicode-scalar-properties.md b/proposals/0211-unicode-scalar-properties.md index 7d0d1522ad..8b445efa0d 100644 --- a/proposals/0211-unicode-scalar-properties.md +++ b/proposals/0211-unicode-scalar-properties.md @@ -5,7 +5,7 @@ * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Implemented (Swift 5)** * Implementation: [apple/swift#15593](https://github.com/apple/swift/pull/15593) -* Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0211-add-unicode-properties-to-unicode-scalar/13857) +* Decision Notes: [Acceptance](https://forums.swift.org/t/accepted-se-0211-add-unicode-properties-to-unicode-scalar/13857), [Update](https://forums.swift.org/t/update-se-0211-add-unicode-properties-to-unicode-scalar/59727) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/9b1c670206052f5c94bcb20df1c30c27a06e9755/proposals/0211-unicode-scalar-properties.md) ## Introduction From e0314702b4f036c8b4acd5523a89f383e1f1d376 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 17 Aug 2022 19:43:25 -0400 Subject: [PATCH 2687/4563] Update 0364-retroactive-conformance-warning.md (#1747) * Update 0364-retroactive-conformance-warning.md Update status * Update proposals/0364-retroactive-conformance-warning.md --- proposals/0364-retroactive-conformance-warning.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0364-retroactive-conformance-warning.md b/proposals/0364-retroactive-conformance-warning.md index 9e00840ad4..7c901aca99 100644 --- a/proposals/0364-retroactive-conformance-warning.md +++ b/proposals/0364-retroactive-conformance-warning.md @@ -3,7 +3,8 @@ * Proposal: [SE-0364](0364-retroactive-conformance-warning.md) * Author: [Harlan Haskins](https://github.com/harlanhaskins) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active Review (13 July - 27 July, 2022)** +* Review Thread: https://forums.swift.org/t/se-0364-warning-for-retroactive-conformances-of-external-types/58922 +* Status: [**Returned for Revision**](https://forums.swift.org/t/returned-for-revision-se-0364-warning-for-retroactive-conformance-of-external-types/59729) * Implementation: [apple/swift#36068](https://github.com/apple/swift/pull/36068) ## Introduction From 7559e98e5f660b9797d285316bd15c60d928502e Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 17 Aug 2022 20:43:40 -0400 Subject: [PATCH 2688/4563] [SE-0370] Add syntax highlighting for two code blocks --- proposals/0370-pointer-family-initialization-improvements.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index 2438910de6..bb642ef78f 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -48,7 +48,7 @@ Unfortunately, `UnsafeMutablePointer` is the only one of the list of types liste An example of partial initialization is the insertion of elements in the middle of a collection. This is one of the possible operations needed in an implementation of `RangeReplaceableCollection.replaceSubrange(_:with:)`. Given a `RangeReplaceableCollection` whose unique storage can be represented by a partially-initialized `UnsafeMutableBufferPointer`: -``` +```swift mutating func replaceSubrange(_ subrange: Range, with newElements: C) where C: Collection, Element == C.Element { @@ -84,7 +84,7 @@ mutating func replaceSubrange(_ subrange: Range, with newElements: C) ``` Here, we had to convert to `UnsafeMutablePointer` to use some of its API, as well as resort to element-by-element copying and initialization. With API enabling buffer operations on the slices of buffers, we could simplify things greatly: -``` +```swift mutating func replaceSubrange(_ subrange: Range, with newElements: C) where C: Collection, Element == C.Element { From 3f4ae9c218e4d3a118144dfe04e6db451540f30c Mon Sep 17 00:00:00 2001 From: Mykola Pokhylets Date: Thu, 18 Aug 2022 12:04:12 +0200 Subject: [PATCH 2689/4563] Removed images and updated section about async deinit --- proposals/NNNN-isolated-synchronous-deinit.md | 40 +++++++++++++++--- .../isolated-deinit.png | Bin 400571 -> 0 bytes .../last-release.png | Bin 261619 -> 0 bytes 3 files changed, 35 insertions(+), 5 deletions(-) delete mode 100644 proposals/NNNN-isolated-synchronous-deinit/isolated-deinit.png delete mode 100644 proposals/NNNN-isolated-synchronous-deinit/last-release.png diff --git a/proposals/NNNN-isolated-synchronous-deinit.md b/proposals/NNNN-isolated-synchronous-deinit.md index 7efa860d3e..27397febc2 100644 --- a/proposals/NNNN-isolated-synchronous-deinit.md +++ b/proposals/NNNN-isolated-synchronous-deinit.md @@ -119,10 +119,6 @@ internal func _deinitOnExecutor(_ object: __owned AnyObject, If `deinit` is isolated, code that normally is emitted into `__deallocating_init` gets emitted into a new entity (`__isolated_deallocating_init`), and `__deallocating_init` is emitted as a thunk that reads the executor (from `self` for actors and from the global actor for GAITs) and calls `swift_task_performOnExecutor` passing `self`, `__isolated_deallocating_init` and the desired executor. -![Last release from background thread](./NNNN-isolated-synchronous-deinit/last-release.png) - -![Deinit on main thread](./NNNN-isolated-synchronous-deinit/isolated-deinit.png) - Non-deallocating `deinit` is not affected by this proposal. ### Rules for computing isolation @@ -278,7 +274,41 @@ Removing an isolation annotation from the `dealloc` method (together with `retai ### Asynchronous `deinit` -A similar approach can be used to start an unstructured task for executing `async` deinit, but this is out of scope of this proposal. +Currently, if users need to initiate an asynchronous operation from `deinit`, they need to manually start a task. It is easy to accidentally capture `self` inside task's closure. Such `self` would become a dangling reference which may cause a crash, but is not guaranteed to reproduce during debugging. + +```swift +actor Service { + func shutdown() {} +} + +@MainActor +class ViewModel { + let service: Service + + deinit { + // Incorrect: + _ = Task { await service.shutdown() } + + // Corrected version: + _ = Task { [service] in await service.shutdown() } + } +} +``` + +A more developer-friendly approach would be to allow asynchronous deinit: + +```swift + ... + deinit async { + await service.shutdown() + + // Destroy stored properties and deallocate memory + // after asynchronous shutdown is complete + } +} +``` + +Similarly to this proposal, `__deallocating_deinit` can be used as a thunk that starts an unstructured task for executing async deinit. But such naïve approach can flood the task scheduler when deallocating large data structure with many async `deinit`s. More research is needed to understand typical usage of asynchronous operations in `deinit`, and applicable optimization methods. This is out of scope of this proposal. ### Asserting that `self` does not escape from `deinit`. diff --git a/proposals/NNNN-isolated-synchronous-deinit/isolated-deinit.png b/proposals/NNNN-isolated-synchronous-deinit/isolated-deinit.png deleted file mode 100644 index 591c5cc02689e151f195ae4922c1faebc1ad80ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 400571 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 z z2t^hLjrXmjXrSh-kU}hMg!hMQXvMjiz6TSS6UmRHM_X;Xj;e)V21p81a7WRwzpG&U z{#)qeH$d1Q?lSV= zei6^srj%5V=3T&W6AG`hOidkGu(b;In+q|(0IinDp4BBI5n}BXjT2@SA?X)oDK8l; z<-P70MfZOtZO7OQ_Wf&?_EN^3`z)ZBdhYN*Sb>nicTiPC*29jQEzlPawtK&|_95za z9SveAZ1YZS6MR>=qveR)@L^N1pn#Yx$Z-v%WWJM;Hs*TTZX0!>Q#rIv(tFz8X03-qfIMTYWr<>l$5VA{{`2RsRz94YUo*G zo(5QO-(*@2v1o8kbTy$8j1TiobTNE8RK<{487wfxjdb<8xyGiG?b1Q%F3LWh3*AYe zg=Wq(`pFsgiL~#VyiZq|8c0SW90UH`vv`3hgueIMm-j$T^?g8Pk5T2rXPqsFT~^)s zLJG=Bt;wODwfWs-!cN}(uTb`J(ClL)DV_=)4G%OOxegLx)P2A%EP&3h}z!%J3G`t`CtZP&nUk9PiqbYMft22 z+tk9~X?kGmQ@Fm@ru{HtI+35|>;X$o`>;h&b9JCSMR=tdrCQ;?Sz}E0SCf9d1TyP= zIjsJ0d!r79Z6*fVC8Tv=jxQjL4%w7)^+xMS(lh3+5q#`3%O{2ef&NCGJyGI0_Mz3N zSv9-xhG5SYwtdCSbmJk6bGFJrMs$e>SDzidGnuzEp2M-R#%K3UR|RSzGB}E#9W^Ho zed)8L$4mSBU-?eHw2xv^j4z_k--F`m;R zxJ2Hk_PpH~CM2XSoB-C1i4*h@y|UhFCX{zKVNO<9`Wc_oAhD$1ww!|nNKq6+Jxej0 ze(#l#>9Q|8%#jz$hKXMbPrL*Qy&SYQQ{&y2IfnQu*nW6oH3#|();|N=<{xu;UkH>u z-n%-T7jhU){Mw;=WGP7tMVrFc{#*v{>qu!6SXcJiZn06G{Z|t3HvSbk`A>!njym#` zZGVR*i}imE7~_T{HN2Yt=iL|`z}TfL_S3OS%pCJavEV?x#V8$UP#)0e^-BHgfPFK9wPwZ zKhX%dzR1-l)dLUY2bBsCLE9Tnxs)^>>_#=(_;~;u-{G#ygusvVmO;_@e^NJh+r$J! zE<})o|F%JQTW0`_O5u`WDWIV9CG3Q$uLdG+sAW;(Onm$PfLIo4H`>q`Mv>@lW=6Or zW4{+er)0JGrV4r0c2a5TL&B7rH(1mSw zL{Wt{ZPa!E`Gy_hcwO6Pk3H-=K%lo2Pk8l^zjC~Ev4vkL7V;xaXb_w3UhZ24jzZSz zo|_R%(p$vjr67Fhh~-=56M2Pj))3P=jGd}cr}VmxF6}f|`p?;lO}!(AcA0Y$V9KZC zI2OL8qgiX$PKww}OY(d?e8j5|vQ6FXxKS#wJY3dn*hC81BEHkvOa@kNWrt{=u>rYq z8%!4y|C|j36dVn7l_DO0=vkIMET-OCglkZN0^ibz_Aqr#EgE7)bNR@ITL$-jI zElBe1O;R^hS=Fh;9)(2-pDsgks8}rF&;1{2uuxvW(3$5sgBj}U!4ZTEn?#n;YhmG7 zV!mEitPJ*lpS)zv<2*vQg}Irq?*+n?*yCQ8_@7sidNmcJB4<=46fv~#mq#gS{W{hU z(yubGi$KHV4G4bG2wv<)F8bUZQ&E$yS-@_iLDegyAz^rtABOm$W@_Sqf5_QD;YV|) zOuX00bD#5cr-sN7Ju36<1BbrF)cE8TMy?MKYX18!2!0o+|DMcxx<9I(_z@%ybrBiK zD6BHPziO1Z4Z-y6E{tMzT~X!_dI8P?xy04G)&GK9V=MF;#_*+H)jB3~ZiF3wBITMB zs|d24c@VUm`{sE$$Wmt8;o~wUyst@fy%VJ7{amKqwzN0)$FhP;C%7JlPaUB4B}U}Z ze7VZIMf{>Opx)!=tLXiViq*`!7xIr(dB@ZYOW7HD6u@Ss=V<-zM@b7TVAIs$!lloh zcgL>gJ-M*%`Hmw&CU=Vzr}MF~MTZ|L_FD4%3)*Sv#sS3<+&*w{LelOoo=J3%t>-dh z%JCiid{=f+xLoFj4NCFaqqbHb`&JoX?ycO_Q%7eSev-PB z!sK+6kihs(Zc$J_FG_4^$eBm}!KS@Pv#y`)lmF+-wNIP4L}euJKH^{dts?gd|3k?a z?EUh&@hc9`M*J5veO&&PCLRg%143mGy1Pa%Mn?K43=)aZHxh?3UnvgeNhE~bMLrNh zC@INr#g@?^C%0JZ&Q5<&B!rqTzDX0J6Kt3hvn_qa^L)FV|GD&gBqNqUw(*}yZ=~JZ z4=iQ^2W^q|-5*qn9qag6J^K!ejs04Vf2OS?^-V;JlV-({q zI)CcOe)2AVTSe+C$@PE*$I~0Yi3Z50FEjH)Us6Xqu2vTEC5^*B1_4Y8>0sQdFqUt% z(KMoQvz)u&?URt z7pNb32at;pe0De$9qv~}w)v%+gvN5u7TcVw%?le+0oL3@#Ry-xChQ04hXv!in;3Td zK)7iAdF24D=Li{xw$*U*rpQv)UA1Y3sOv{@I^&QYtW@8o_RXANpNaCmJD@f?8TL&_ z-Ld`#=n9@@xG;L^x?=GH?5#$_A^Xz67!wRq+wHL1M0p&w^|2InokE}ClfuxXNk3~? zl;`R&9uICwC2tf<@4&&9#4nruyPqe)*Nc&ws@qrOeeTSzZ<=*@FRB%r4Kq3`vN!4x^x}3oXJE>~(}c zX@_SCvmUhWueUX#a`cT{EXJDh&g3oDX(J0e;uGy+0ZODOrYK#v8>Q!zk6W8aoreCIR``@yqi zNl}-8%U%NQ3ak%<<=pWS4bZ}$qm)cEIcv>kCQe7)N~19>RAsxQuhlk~`SF;vN^bI2 zw7*AL_;C^RE)RB1jMEUaBUKANaOu`g_#i@n3rQS0Lu9$qt){2iIyE+BZ4XCm0>nMY z>yC%{4snb`H#=2BbQlVYU}CC0!7OMSV9YB))#c-D21RteL6=ur%=&+*L;rCtrxr4h zY=ZY)>Q~yeTd!0S-RDlJQg0Bx(H5DD9itKE8Z{Cu)hEC(&xUltO}u6;hagKOc1Zu6 zM^boP7EO{D8SHSkoXMb!=MJrA`Ba`Wba`0}4Bu~a;1xO@U1Xfvs6;BumG%MG;=f_< z_v!EaCSLp@o$JjvKc?ao7%p!4iY8JM5PRP5CMS+nH$RCd>(IyvfhyDm+xa#!i68sPW2yV!CKXnJ!VFoIB!K(f&{kgIWQ54{B6|dlij@SaS-=Ba_93Nvwh4?`W5^{{e3bDb&sSB7tHY9|Im}FZE zv!G$RXUdEq#AjmZ@AUH62Au!Z0>Jwb>bf^l=(w{-NBv!lJd(aREr6#>k9>ederJJE zLW+9x#X43(=rtYDi5-(LwSK0e;lSov-A%3b|E9z7e5Mk{CN`^WjlRQ&d}iKpzVAf& z4;@a@$7BGfD8nsBirLuaT z`w|h2roetN>wKihOP-WLsdp?VAjB?P(z0_$JFWD`HkNkRbqN8FYxCo&qWCi1rl|sj zw1Tzf7ZJwn`6KklWdd)C^jsg$dPF^k&S~L1d_};@ZdJ39apHnjz)EOHsA_jOd2z-4 z%6fUO%%KMFU0L>)DnUSVZ5R?A`(Z~MdN_qH+XV{EEPqRF8rRlbnTN2)=!VzJ zYPn#)gD#OAZ7UE8Is&fl z=M?NyJ@}#{0$cf04|vC?DMmcu=uKKa9Ca^HG@zcyI<`utZ$jdCL?%IP2CWTyy=v>W zhrH=yM3FIb@b&t|`a54_b=R|9(rkNG;4VWA3mvx(_i)_lGCPgP)Rd88U9+ohuxdvf zm9hEH%zm-kz1A7CtlbT#W$h=bAd+>Nib555Z<{F_D7iPEwrwYUf4phNL2u%fGHEz3 zsPcNUg`+Qhw|SbY@&Us}z!Rn@j}cYbtmIV`ez^gdQ}P^}+Nb61G5qLkraL1Ux^)~; z>U2EL-i6|X1!_-vAgDL)6&lvR{JG6NqMgmWs}rGQv1rJ)<{ZF$RiNW<=C(vRZTvvc zH&G(b{jdRZAe%}&YaEL^1wPkfP?^9-V{(+$!o)Le}9ng+-l=+jw(qo}22?(_s z&TdV^z5(CY{?5;|QP;D_wAI|A1>bZa^OyU`qmrvt!xW>d z))Y9DRPk3vkswD2WVC}!ds-sOeRlmyN1l9?Q#6%XjCTs3h6B87#@_WN-I`$ny5_zJ zV0(gVnAl@00c~y?>2}a#x*1D$Pk}tfEvb0KtrsTqf75NMRnrB0h=w(TK{+Nsy)fOY&WpzmKkX)O$J`SA|;8wvQ zaPL2?Lk_v5h-4yuX+lwl57Z6|o~3$b9bwiWQGI;DA|bODpJS2bRu@0#qiA%g0g!-k zH>q7SVwKW2)amuNwYOs>Cv>F?R?meBY2~)llg)Cp&Ix)>n=%yjIKjn%MHzNmVRrRk z#t^s3E$05r?Q%O^<^ZdJ^xk9!R&o`>0mt5T#0F%tvcJ-9W&fUN}Cr$sH>JNTiJfXzzd#~Q>1|=lAZ7! zUVoullc)Sb3pLve>OKXx;yGB=_2G~&0@CSRU_>165&aJ;4lz9f2C`mDnuYCZ7h!~_{WI|Sdb-|uS>Ow|+XZ3EQ}ta|E)kyL8WI!n z687)sBpsVMoIs0;_FwxDS6YwIs)g^wyX$d}<9;*X&MZF{c$QQg z6ehh%zjO6I#|<%R^s95QFnz1<7Jae<^Zx_8Kt#VsE$>RIoJ)iHTOAssZ6%~@S6ShX zKa|DEt-`9#xBuRX!_mMFPIrn$uIhjOO)ETjpXmJggaTOgoEcsQ)j#w{%YET{maCf6 z*OEMCAzFBlVm0mDg4v#XG_LQeKzU04_P18R0$K`rT(ekYr|{bAczgYdrFZibtx&|vs^sgRvBGA*kBcv^iS)*6Exq81CSi8M zfy-a}pH{#+IOQ;Zf!V!xSUp}S=069=vC*P(dt=)&&%HR{O6&IRf3U(jIHwFOyR{oE z$Fh6;L99RVF)N(^eoqVA`j_!Zrtkf;6*g{F3`;%7{PP#$1=YP){Q6HVKOCJQA?%F~ zd#oQ#)c?YRccJI0-ilaUEBW5PKsJu83IS73Z1TNU|Nl^Ljy)~LYR{K!_Me}(d^P>k zPr{k}o|WG7AuIgu=T;m)-fUl;?iSY1y}lM&(>{%PJ#n8EFp@QfLb%q_ORs7(PB&$% zyYwSgeC$t_Te{puMFmaqQYHP+Kj471m4W-cYk!toE^LS3UdN6uTrJ+cPV9CXZw;9Lkn?hp@+B9bvZ$}Eu)$+);FJDlY>Qbm01B`By`gfD5{ zH25ei*^TxOz8=nLF9hs@<=}9mhdTzoQAGiG`5=;o!nja2b?Es{^5}UDL1j~62Kys{ zwr_#`XU?4A#f;t4==`DV_XNihAS7UgcD{8wR^?F-L8 zFLfyTxmavz1Rd_6nAgA5I$=2Yl~?|?4MGE+oHuOPV6!m{*9BwkbIWk+p?UaD4m3BI zvQm%6_~Xz^cmDb33yz!@VEAv;pF(Wky3N+1M~=OBp0F$aR`lp^T)z=L_Wn?a*WdPS zTcOW-%zfRAtd71F{ASOYZ8K)gur-(?%CfI@{js;zwMR}JLpTYxE56FUM)}ioUxCpK zEVpUXUbRJw7r{Bi@Kf-K_9x3)_>^|V@)d9vXW1*ryJYcV+lF4T-If1FlpV{jz8(z_ zh*?696h|{~#Kc_a%!S1o+dtxKPZfFX@Pxl$flz8TcHCII;DYlJ4=$+p3(&Z!u74ac zLHup7JMn3Au@NV?KVPnJxISO@xQ#&y+HG)}**m)#4aESB^4x}(Fmr4Mu9G!b6pS3i zn0$3eIZ|7?WNG>OJM+vlt!r0|sAz;gZtNI4_U-J|gN%ERE<-hbb@`VtYGwKI#mRzqI;yy<1|iQOWZJjs6(wfA=Ff+$pAI6hHHCmi+2IORu}% zivRR2EA_0sNtZH2=B-3BCJREMmh^W8#3QNy_UoEmW5y#bp zb3ghjZv<9<>H96k0!PV>k6HS|KNwfZ&#i?+^!W^ey1hdc4Bq#FIYVmDXb3lKl`RT+_;An-}^Uk+OMTkpAc&{LULDs$x7WiYxU*gmp$WT%8{1ogD)8Yrz_h0_=@JXR0Fnw#()O8v))l zWP;+^7#AUhK`H@AhZ}0SmsAa1&?C)xx%k==80iro0Lbt7N=NIjg3p+EPJE;YKjb2< z@WBC%W+aK8{G)75<5LdO*T7dM>Qx(GW?{Z6eCfYW_(2@;q*N55p$!#Nf~{FBNY}7c z$qHa6w~jqWT9-cKZPV(R>fGwud#vKh1fXLjLUGAPB6t5ijs3y~nfs9gW_Hppm!Q{?lj76z>GXyAM75Ff5dI z0y2;`(6RneT;@aXI-Qm4ug6e##60utvvROJ+4Uj@j~IxvCw$2aI*j-CdQN%qtA#&i zEQhlz5p&EJRNg;QtzOFT=uFef#vakALFhA%9|b-F-La zjKjIYVoO(l_Oo{5jlZ*{OVJ}kMW_$#(YJ43yZGXFut;>_Wf+By$_{0HHbh zzw*kf_8>D00l+cXhGgZu>wJi?$_WL)g|Lo3 z`Y3zXJKyCmXh0jwf7Pm$_TwM@7~E1U{C1Z0?%mt|bnl<^GNKxP^ysm6$tCZ$j+o~g z%U|FWK)HjiWKI4oc%-r17{U46QGd>lX5D5I^K#*?C9f^9o;`cor#|&*=SC1+e`J%| zuYdh(IkSCw_py(E^5e=!{`a7)UWT*Dkseon_G8`zriXPd(X&4jm%-xk#bJ#+Ivm|M`51lW(_+srHkg18?|K|tLIf{lG@c;Rr{}uO; zygmNdPr$Gm(= z^x&T#vbAg0mh~r5hYs!Sm}8E?{o*Wi;CRK3`b#gjdKq7ttIEID^-sQy@m&LK$s8|_ zax2ioSQQG#O{zLO_=ibdJ7Z5&sTUff44u&Euj-j9VVHK|zT1>k`og!YaMX!9W*y35 zXnFBV*IN4dD=m+g8}%5rT{!uj){;D8+Kf&9p?j_L==}{SlkN3dualAdJX8M5KedEI zL#sj(oQ2XAe`j@ZXemC?R>08Fo#;)?{rFqxC7)B3qjH#AgO^&TpJjFb_bZ6h)l*4q zV@JhVFIyhYD4n;u_kN-*Z?4~9vrm8B?5guH{C<^{mMyUyhRU{PSrzBJYI*c9@+C$c zhW5sg$MEF*Utev-rzTr&)fy|!#7m4(hg;Gf!%Sf70B zH{}K63ZsuO!*3^EEEFc+k5LN0Haq$x!EvZ_e)4@fuK(F~E(`iXaA8V1@v)i`)5n*8#=L|6ZR zK4NtkwjDzbJ*Xv)K=|DIERUDA#ku(8SQwG8x3-Nuo4c?7-4CsRdQ%)T+6w>heN=P5 z`RO~z=oQ?F7t*rzkH2j80}P2rDE{sTR=~AKN0xcwHODe7!YNCye!t}~%AvSqo<@-D ztqyytAA8cRgy&cC-J7f~d`CXlt3O_Bd=n!*FvS8d3W|&7S{~C5s&y$28esW<`(H~q z{R|gZKBw?XOz?AKOtrA={UyW>ZR6472mvVZ+4(0laBf zz35Om8L7&H2zT_D0(#DGWeRT|aKy=v=Saej8&5}!j)~xnyN39TZ-_5OVxz3$0~$ti zL)PTA3%=tieZS&UfCGc?wsU=TjJFHavP-Wq7&49<10yL;*e591(G&m9#-054HvW|V zhvQGcW-NB+B>UsAbFbmnwHKVj`1ur1M%&pLsnJMS1tM14zqbAZ2MiPnd)L^zMa)nl zI<7}!^QBiXhlac$(!l-$#JxG?KP&d4qx_FK=55Hgd;5ufU|5h_t_f7+SO95t)H{u%GmIldIE!k>W` z9=F|ki>*h`GSggc>n=rvKT~;cwRLc~BYxDshVrQBfS@*TDWHt}cJPeGxwF!zY{TFNS6P zna+_eH{X1#)?BF8pS(G>;E5-m#PxIoMqXeJeKkJu6oevqtL@*^^&b>LPUhnugIk#F zZQEv4HbwddJ#S6Tr>&xDQ-`I8aLv-OKw~sI+|>edo@Z&~7)7PeKdyaC@>KKO{W0o* zDiEmRK6DN+UvA-ItN-B_jlGZ!0vyod=f94Z0_Qe}=1J1qpHE5nYCvNG_VDLF%-&%y z;l}B0>KxBrhTNttSiv2`|Eod{tDL7l`;&T$bA1OYr8Ubchq^!;+ZVo5g&GcVBj#LR zg)p*fSE7b7$(`3LqIA(;8)a`C63oMAC`@}%{O`&RcVwBvi-h7i7sk`_z8=>IUwWjS+FPwp1@h%c{TQ^@5K4c( zwVbcDZChp>4)Txqi;9$=FXThBQOKR;BVo8+0v$(&!lQrIK7Hq9Wrtf4*yOS9LGgl1 zjiVPhg@s{%>mZP-P3hf#W4YjP$LRd_P6#uKaY_;r0l~jcGIW zr=As0_i5kK`q`3eVNZhYjeYjSt1YMVTGUjDC?uX6C5%S&2faT=JZW#9|I7|XdgG#y9yghfgPV0qVdK7n+9yWFrC*Jn|pyWG=gx>yo_e6iQX9k+Eam^QIS9?q8Aw( zNg*xblMkaD$G7^fHVEADbN-+T4OW~-_KopzP7aPCu_##?pAD)CKA9lYz^9^YNH&d+ zycBCc<4ZmP`4J_~p&>Eq?SF^fY&K{T45!3O)a;>Qpge)TLpLlfJUC;wXwWD>9zsQk;hNySiYe==fJV2DGO|7oY4suh*%G3=9$)auo1 z;1G<(Cyu&kSn{KfK8_*D3&m^b(4jU0jxl+V4Uyxvmi^bThl)9&KtBvqS7?OKvXusm zN8k{1NKLi~NNZ63AsVpF!3UMI0X`imPym&FGE)id`;1R{DfiLHw ziq<(b#9xMQ6?1s>amO8J>}6e!VYBz$_a`_O>rf}Q+oR|qeCIpQLskc3Zt|6LLS1AN zB-g;Nwm;|I-+Bw?x&(Pom^cxO{1kk)jYE~$o86^LXFK;?4DSo}UyLE- zoOi5+^?-By@e{E~));)strPOjv!|b)VjD3mm@C@z+tcrO$GL7F>K^oGc`n7L=NKj& zd6XTFMYFnqmaEkN6 z{=tCp9uNn;RDv`HqgDRgtB7?T@yXFcsv+6D4vxhz&c6NnXoxd=?boubFxulWmesqw ztjKo6@DcXZlTRu8)E8f}m8(|cGkeEiF}3kNQltSsa{!;G5y=>yd<80{Qq0nKaO`kr~rq7s$ zA@4=I1D^;Ref3x?9D*q*WpUlws;A(0@<}J#QAZtZi!ns~_SGBK{>#@t)9|uH^(X3$muMQ28GH5!*TzG-WPv11kDS<~ zr0K{*4TX+{mlis03-UC{yir2&Oe})b=#yb|fJ(qF&3NAOzebPUvs0|N?2~5S`91D~ z-oP$k5v>@C3ox4MwfUA}AuoGd{5R$a7!% zwAGIsjvMkMvm;Nm!k8m=aY9;BzD=n~=OoVS=P-Bnx~I!=Us}G{l2@L!Jm>lDMabdV zr?6w2)j#v_t~q%#z-TZI7cMTwf=L{~fW@5{((y;{N{^TXqZ^7C(UilzZ(l+Qda;+A zA$P=aHCZL2Cm_D!IFl&F!gktYjhp&sAJuW=O%jIaCeSH{*0ycr%YA9h3QM31Z&QV1 zPN_~RALmA3E;C;o{fN_T$^o~G z97pKx!}H&Hp{0W7Jlm4o%jxDtgv&%5Zy&fbpg1)ykOm^B!FC^X;+t%rPQJGAnV)*Z{-*ud)*;j{8(EG z$`JQ{to&8cT+!SulUS#2!>luU%r~y}UMrH3o=yi%L;Lf*Ir{hM*VjJziBH;{cimz0 z@J;K7A9=(c!B1s?ZD3<>$D$=?oq2}DY>xelXfSgQH1%(f;em9}q*Dva-{rvzhs~Sq z`DdS#mJ$v}6ju6zH$Po8VFHHGR_o7eRP_nc$Uxd4&h5l`!FSz#kMqR#@WYwk8N~V* z@rl`#{^~sxmn!?S-Ua-Tqet0$FTVmqx_M0|)~$Ot>)x}Q{p5eIkq%3iEJbb|N?u`7 zMjVtKiQVAK4#@w+8Bwthlx|pNa^|? z1&8~RcV8m;g^l6!!-fvCUtWiKr)XSFdFC0LbjIlzE{vhl0Yf_wv-}tr?eFoNZ@<9q zx$ADGs7Q}E0uNBpgWCUjCVAXyICDngsq5c4i)E}QBnfkE#E)fl`4v}a_%hSFb?dJ3 z`pHjNRwayBz#MMoQAlj`*wJ>%si*vZ_TB@`uB$v3{`NUD>b+O1TavqtEx8v7H*AbC zp}9f`5crc2!p$YQ_X+8LfO{dyPY5*z7g{i;*nqp_-ivIx$X2gb?{(&!{lD+~t#!_x znKPQAv5LLd%-LnFudlWDUf-%3UQPb1RD~I5 zi#@{Gz;*FO7uw7-PWN((w4=t3)0SQLVX6W55r6urC+)J!uMqz?G57oBm$AAn{y4ql zy6dhDEBtd8fDw3>j>jExs#<=~R*_*1N z*#OF}KHMq}SEfct*7^6)LQj4z_;k<^vjcRx)78sFntIjhg_kU~o*6XuFlb??ul_Gx zw|ZzcBA}~m+nir{)l#3Hf%3uWCba?=bt_@Y0@nmc*JHg8RL|Av|K_h!WAkOPMxqIfuGak9Rg@&`Fk9W0c3Fk4Wi8q;-JYE(q?#-)~7;A%ZiF5wZ^=Bfx(27bSqU#-F66zbo+i}E*5q9>u=h~bH=CJVDYAn(%p zKWfYvJM+x5?V*Pr1P1C$tKYPVXapc0`H~LLbCFjsyzqkT$}|pZ--4m?T!<|c8XG=z zIC@agKoV@D{30c0kbXQh<}X$ zVyijPp}>JZ=dt(3JlZE8f5JASOfjC*4%Q=Y*JJyYXE7|lPoF*->2MAlbXuia{>g~? z({?@W^2;ul-Lm}0U=+yNXPu2X({m-)8rg1w_^e)qMgZtGd;}LhJi}!p|DgZb==tWf z08TepwHoDvgz>+6g*P5h(n~M7*ao0+feBI@^my^!_rBML4}Y2iF65BYv<%drKOU;( zpOM6KTqclA%>T;s?}#dYY}jQW43FyR#}ys*3opD#apDn3a@F+rUv(91x+7u!j(FM~i@}}pE=!M_Vacd* zR^XgjHWvKl$H5JSM+Unpfw|!V%7T=A?U?<(CMWgYLVO|BC`j3 zm)f=olgtg?iLHe)77#6LaeWdxprO2Y9V))?TEq7EgQ7!51fOo1iD>0A&c#pBvB7U? zt&px;RV%vVah)ljoqZPCkgmPGVt3PijB|$cB*kBv*W8i&Y zLrGMIPe9M?Md&3=^kU5Lj|j3i1PD6tLYGm1AY2pvgb5Qh?0W}# z87S>&%=_eLs}_Ii;VQ$y2ku1RO0kOmiH$@rX9McJ5NRsL>6+L1z^aN-GeF&r{B77 zpSe^tQ93)DkO_U!xqtND!>mz5Z*(@QwQJ79ETb1W*^RvH zH^}OwLu=60c;-Bb`PA7CWq46{e|B z8GO(ae3P^FFMgX_)IZw%{Nv^yl90#g$O@EKxWxL!=E;7roSUOADWXt z!Yq3FJJx&91TCnwbITGt>C&&u_NwfY5$wMU&N(g9vp*_FVop?tcd4hX`L5#hwPV*c*W zFuZgPdNfy~z~6P(-F7*8zGVLz`o{;Lo~xmEug7rK2FxGE7EYLR!=c|EJpVgnf7%(X z7;F^y_ z`6CJXR~p5d@t4XVu?DMcNBxHn8yZwaOtbPo6lw9p%N4rGmkzY(;4ln5@Cr@z2RR5f zg!A($FLa`lM~!%h`tydXxD!?aFz+(_nZ`i=+)m74WYr4)K?4Sc{oqAiBNAASNb@)} zj$Db=-edkp{*-~%r~VYXHU5NPAb-LNns(QZco^A%A@dHzBOw5Z=btOT3!eIuOta^o z<)3`Vj~i#>$B$Dww}^Xv;R4O2U$t_TXuW_D7Zb*hw-Zh{p-RtL!FJ3W+>kzh8U90I zJ4pln92!o5mFVRrIAtL?S0Pv6O_YxWb&n|Yf^5WU>BCS4ILd+a>e0}|Ro7cDf381u zi~57K0(sMP9QhERePqxOHXIb47kAjDXx}V=4SfWg}OeLQ31mrvD$pYIkT*8?tN%Q3l3pd zQBmtroya#-qIASt~yKm~r3s*ozsFx#gAik&Y3aJaZUw8KWk+a(-#<9ad;!YbTmK z^t%?+ldv;DvTLt3zWTh+LvIGv_~k#d!u%Jkbkz-3f7K^YEW6&eS(b%?Ln{p&rH6iN zg-br1P1bdpcEc6d3n&i32noDM%Y87CamQ}6d3b)pG>(YKg$#?|_$Spl%iIt9E!H9O z?aWzDZI60wL05kKUMtMHvI&`V?{4JB(@o+!d(+wS*_mgdvogM2@EV_b!0K+hxrsXb z7AK=7_}8AF{LX&=L;ZvG5q2`L27%|pD+#<)-9Xg++Aje}NFB(X@c8jEz^X#VN)e-! z>i7Z?t`cxy#SOTDmhM<-33MWVukeZ#q1(Y9lxy+_^Mk@4nj9qlvLDUq86YEZ2;|Q7 zi)<%)sjGpOV{Vjf;ZxtT*B zpXJ{JW%GpNPqaV!_$Rd0mG``-whYf-mcNw8K`XKcTFv>>t}+wM#_hbwpKY(Qfe54+ zsWRF|@`sAj;y*GV2|1EIKcLUa9zBVDO_d->B~nerF7*5ps>{A(@$?c;xCX`gOV zNe4836#w;ajg^AKJrvO^z?GKis5JceKU#>}c% z_|8ApUN|cPH9zCsz-@^dFm%88jek%o_tdUC`7DPVJRe%#sN3byG4mi4dn~DZIC81Y8ncySn4kWLd5XZw^#uOI?I4;>hr=|3j zKUX=)##gT6JYp>9)g*lQGrBD<^zE~UMLrPToEm2>KwhdOL+#y!0mQm)S4Y*>He z!@tu$-3it&DV}|aqaTQEokE+((iI#H0bOpt(YQ!lHfYhfdw!`Xu6XS)a`@9%UZ=Ri z6LVVP{Pdgda1ZQfn)7`iI82I3Q4m=KNV%mj0dK zPY%ubyX>G@gTLV2i~@68HsvqzsW(E@pTLsSiEFMvfq+mZrSM24u*0}5c5YmZ;peXh zjMTQ`dM6f1>oW)~y$nE6&p$3mOTD|yf2K(YIx)N!C{8YTMP?`g$iIXOY z9x*)t-pQzkdMp+&TDokh?ZzCc&Dct609Nd*rN5*2aq{HJ8qQ5P4o&Ck=M0ove563t z{tf%~*@F+wMFRsC8SCHIW@6=R0u!vF3vQ%2%3oXj9S681Ap28W#ybBISXAtU6OOl+ zUVPCSFu!}w0}t308uAMOKt}y}N34Bl;m?-8EdO2D`;t0IFCB>iekfdoBj?VPKb9+Z zR=w=B#y^%nI#gbu6U-A|A0(j$P5E<(F$JSsBQZ~#cAIml6e+RGsU1u@>l zwpre*4O}enCF%f*`m;B39bhUr4pGs69e9%;|3{)XGroWPIRtPJBFmo(z;Q_NOY>jS z0&ox9|A6+(CJlzDzfeJlD~z|se-C<|oj;Lr1ti}4ouYKe@{hE_;G&>Ez<^XjBg9Jf z&_W0wP0?2g5ywS@{qe$_J!`f-|J?IZk;D3H@n5SR>}vTVex${@;S99@2+ZGR*-{?h zOnKk+u6NmcFTXsaPgOJNEL!C1fqTQRT(R6rh~RL3j)3q`n}4?aQFlE9K#=`;#*bx+ zAd|F!%1k6Je^|B{e~1&1tum)zrP=08SY zH2(Qlto~m^)uhpRYqmZd-WwuUOFe|5bzoH<)?g;%iJIi^ZA9(z3Wm@j9w;imsg(?Ny{CNoZ)!jsnyBJMAGGovKe7VW=qUdMuFt;I7r{ese*r!EMT|TvVD5q-^)M}BV& zefnAb#lg`zeumj^u)rM}MjQSCBhpU0!0IvdT0Qfp&d_S~g}?c&zV~ldch_&M@s0VGVnuf>@L>haZ!hfJWhG3v;dFxo z4Ru%jp_Ly*&+rE9xbnY0VU4GpVK!#GrQEOl*{7{E9gF($`DhjrmKW&5RrVYI5{(4d z2O3*6pmzd2-!IM+bmP^ZvU<$TkD;@+jreVy>#$W=`F>0}z&`5f7x9eDJP*1|#A@&h zt?=xlnv%f%$m=e`beRz9F1tp({6%bON}R@%W?G5{uH-f7!6_;D@TGtH_YoXrv1`jm zg>V;Ka4Wg;WA|BM4Za`$<63K&I$czYucQ2OGy@hi^+ptZ06NXFv*oih&qAk#Y$qZY zux!NWJT2DvHSFa+5xbOp_8%r-0WNv6xe8Zd{u?#JJ4;&Aih(D*0+WW|9)D*Uqb zKd*nVZo#Yee*am=Ky3h(0gfn@)6L3(qDh^#1Oj-B&IR~gvEt*2NM#K%MEa>Io{Ah2 zPdOE&qu=e@wH~|BU}z6-*~%33CtupSkc4bU zGPf8jmG&YwC@rLFB~q3o9hxSFcIdBOQRqXJHgMi4LbN}xqW*O0H*Vx`Gv{9ydPXBl zoom>C8a~WVJpLrfYRjSt6DHdD@#7^u<0x)6{~v$+aeM61#~F)li%xd{T)VRxxV6Ln zq^+8JEdM|ywjV)H*G_)c1+%qvR+^S<-V5{O=+l{DYSh1nTKzF!8gos{@J{G>1XD-{ zF7CE{3s!569ti0N`=^}OLH<-WBL(j&`s>D}%GzJophNvDQn#Uh&z`-s7#6p-;(Yu@ z^jvYMe{=rJmMx)%5{$i~EPuU#I}lC<=NqqBzEVAnb;9*BNij)i85&W7N&R_MGO!-F*soe4GgGn#TU(7dBE_Fsrzs9{(v&LJolA<+nk zDJFb2*#m#vF~?%=_gEoEIy@`?@fxcH>Q4rI|KieNtH&nxCz9-sogOf(e-NgDc==t9 z71{S_M2qr@aLTOIo_p?D%$3JGv|CR*7gJ)mkeZSR!_bQTl_UTcFZ=!>fmr^S8uicG zpD^53sNvIpW%b|wGV}|?&>vu5B^jRySLU6e5;a!K`?nW%?RQM+5>B4C;{-O$4 z$HV^et5!z`D-$|{lR@9_C5uRtfHGSiG|&|$v6Ji z3LCKi=pDaMLW&`!jaOZ3_2@-vxcO^V_q$(O-JQ2v<9IlSt!y0>X&$a;&POf%^}k!` zKmG=eC7jdSZfPQFdC~;4&;EUrdAnVeS9RC@xizB4tL~}Ct%!FmK8||6%6#_!;wzS* zp|SigpOakWU*BvNobDo~C2$}3A{q+nUV6z&+)|J8oe}E)=hv;_zrSO3zr5Az9+`_6 z{t&R}RpYzgW5tjDKSoC;8|p9mkToK%@U4Hb;-bZ9y~huMtEZ=bj}d{tywT9&i89in zBxT98dCe8Q3;*XQR{HAaBA; zjztI@*wADRucN6Hwb1^O9kH1uD zSYslG?iauEBWwJRFIn*ktQYX;gB6_e&_QU3`-0USKeH0oSsnIQJ}Sfy@FJeE`7JCD8)E4d*WS8oZhWJmw9b_<|KqK2voHKJ~0s#QI+awjUj2#U@062B24r{22LH--T7FojUQzKb&#$tET)VXy6~}Wsav~>-;tM4|q-a2VN2& zTptjB5+WUhpg*-_J(K)V55x1#Il{0BGZXSiXB=0~zs<%T_hHQQ9b&x)POv&GlGeCq zJ5tKnM|6PoLL6!D+r7zFzI;2bTiRdK6agcPQBjbuegaLTP)P~*?BRsN6ZC)f z+2^o7_jDT_dpiL|RMLLv!H2ZS7D0QWg3mp?8JskA;!Tylv;;7Fw#T7Gbvb&{sQ)i+ zyUjlR$Jno2_<{nSpI?3DReR*oM?nbpC_Ja1eumSR5YjN%Pt15|lfU$*k;sD&4le_i zZgAayKtC*eb(;2%W=`F6&)s$%ou)`1XzU4I#J!<0w0Hje`Ph5c{%d*7AlMaevWirEse#&l7=G5S3BrDN+E@`!UjE9JVDJh4RA?tf4*Mm%*8*{^Q0T z6;f1ma7}3tGUZS5No>6NP`;v$&BGWC002M$Nkl)`fKm4H&c^U|_Y%qWC)V6Wx_!~L|(_+r8!5=n*-Lm#?!as0}vhh}OWh89kI_cdM-PcaeU=ce>;CzsW-(1~vu!s`Q~ZuY z&CCDsD=VQFjzb}nDaTuJ{Ly+J5rTUPm(g>VqKCGQE1EOzJs&m0ua-@;5Z)nP46p73 zpEAR*xvl8b+1G!LeX?IPZUQbUS9G_3MIK?#>y*9m7!g;1-o+D72a;YGLXU^e z*tWIuTPyF#C&4pa|HXf|@`g25V*YW+FdCnVc+Tsyc^HKv4jLZnVNWi=R$4gU4CiFj zcx>M{QMS&2Y6?JCmXFrTe-hpyEv@L%6u*0yE}G{*%VGT={!cUfv`rW6QG|XB4Zr@O z&KC34?>c?#RQc@Sv(T0HX^mF>1@E&mcZk501om(@*%6`B*6BE60~X6HeD}*%?>&yi?MY0 z{1}i}sbI}Ph;Yh7IxBsQawt~m{K}n0kZ=#rX(5RuoImL^R4XPV>DW9o-H%bCEPqu% zi3^0tpKw+FxFa#~541YLKP%B&$-k;c&N;|e4!i+WE&jyizo$B!;=~^kB8|hUH*mB;MHxVoA(5a9o&3{In_)fC zvwXn?7obOUF?vgP+jGy)!~Ex6*xPujb;GLjoKi6F1ytaO3j&v1bP3|o6Hfl1Mcl=3 z9QW+rr7gR5?%HKjr(mcN7R6h=3b^wyWDboFG~q=TUW`??yGabvk_AYYWfkYeix$~U zH{W8@r%%&f-j4qQ(485#Gvl0m;WCOMF@SiYu2Z*v_W9@D;V(S@yd2ZTi#@L40#mnfu`q7VnjFsU}w29-#>-lHdUcPLFo_{X1HVt_x{UJ8> zujZQD-=9^f;~b!?!x7kZb^C{Ro8lyv${(R-&wjTpMfv6X;#D*x+;a1+HtpEy)(68j zIm+V2c`rhKK!Wxe*rVIGOKF9F;F*y~_7^i*A{aPzv80Ily!M!HQsF8%#@0&!28#%X}qaWw#*8*x5r~GaMzB74N%l>*^ zJNnErk7%b>ZPiR6+;+~kE$QqprZ5D%kl@#mAX}fn27|Uk=XBVA`DmN#{vjIgz0J~X zo2`h(MQw36pgIg$m~5kx#} z>ZMlx{+BT_;l&o*(;h`DeeVB74>uN1!$%pPey>NV9#8PjfB%W8dJDh#UL9cL8g4jD zm4OvnRH_v&`xxT-!ulwG6?O1E|gT`9|hwP$8EkCHj6P(3?AFw=$v4m6trPzcV z*{IPZL?nY^E}nLoe#R--k=`VGk7k<&8Azgi7f5GW(Ra9O61tX20b=42#&93UXw6+4 za5;n)uU#yeqyNBydt0r}7Ydl^pSD`A5sn11M3H5^lLTrlnu)JOwx#fLt*}#BI#=X} z&>lwln+2pODmqOAsqcb*!#ht-IgC-iKni3%Lk=bbdVPwzS+{QdWzmz6Vld|9J2dFB zy_O`ML$WT+GO7a2(}k53#%9=GTm6*RDyWB7O_N5R*Mexb|MR2P`1Z;s9qpQ};pak9 zCi+0Z@QYKbFEd~6ai?RgTLSPWKBA-NM;tto4VSYyc(RTCfR)N-9He881dZW5EY+t> zgd0&ELX#S$1^L+%aszL?PXxOw2xvr>YdaokpSGvOO`I%CdywX&kC^j{wlq?^H^OtH z;T@)1JLQ=9oQEw)k*cb*AGslpiP=@h?OU4E*n{_YL6$QzyZ91F<}zhDh5{$kZN)&F z{}HL_3#<^D>9=U|6$3dkL{DG3RDQuU62rjRh5-oXMhA$6FXdf(2ue>QK7CH_Eq2@_ zPnph8dej&hN3pnf>Y0+odeFUWV_T@lmQ4`veb-#2e69-PJk#ag$>rch7cY>)O!jL^ z&I?_2fviXU1m5LO$#PL(EXd~4cJ*t2hD58OhIKO7+g8au=cd_xlo9+{(XGf1%Cjpl1F67}49Kv4^ z-O4=iGO;)qsdBnMemyJEE|8c}GMfBAx87*X*5yY- znI3n5+NLNk{>537THd^9wgI$qX#tGKq~mwncj{9v4Y+HRRp`YscA@{;LY7by=$l3H zyt!vQpV~f7vx&)lL1iN?2vl4xq=26Xy}fZ9q{YZ9ooV6O+3}48J(j)_IO}kNol+&r z4g^TJlXkDIi`fLCx)Y;gVZgPD!Jc=0mk$nb`@>g*j#Y`kifMoWnm^A znQ*&(M-gp3_|8T;Xs`G~8lFS!D0)c4Rh0g*SybXr(08?WIX|Y_>jIbQD&iEKOTKB9 zlR`eED=Z1-Xx9=Nt-@<#Y~QykYmc_LH!=pC>Ke>1m8#WZIIthj1pKDA_%a;;{wFaB zo^Le~L<|uWrO|sb|2Bjt(}{1$LC19du_enSs7wgV;Ax|x3|T%Z)%6+o9>K2p>uoD+ zB^aDpe=X)@Js21+M_ogX;xaLwc4vBG%oP{2E=%@(41x1D7Lzj1-a({Fetsq5?rfCI zrlT9KyzL5bpKLD#i-d;X#wFd&IDe}-wb)2?QSBP!5qad-w7KTNK~ITa!DA!p_EPmp z4UXw5%3hWUJJ}ML&nZgM$_Qa*%`Yz=yyMveZZI`m*q8JKr!d4F}|K0R?ocOz|lVJe6+AtW`FP7*}8E)eGTors2oyiIv%t8u1I|n zm_?+20h`H_)aw~un+bT1N*t|PIy?M{C6H|7r_8mZ+CpK-3hk2D&A)8YkAbTj#tfbB-u z^G3kXl^SCza1kkqajmGq$cU@dK)emu{FDgll`8>twv4%Z5z5x(evK+rJ8z4+=5Of7 zf{(^gGv-APtfJj60XGsrgcu=CQnXTE7jzjR{kHR_z0V>2 zCz1fkugrI%* z3q_P*<(uMR;^+;M=!_i2)WLkp&jg%6<-u}2mbZ2KG3Y-luh==qdxIDwPyCWJgu4Ac zr2-%rlOkK~)Fers*q9pXc2cY_<4IEpb4)zq!m?T(^xD)D$Ro;Vzu8It$xXI0e=3sBpQa49Sz+K{OkcV z5`?svr=wQi$B;eoxC1jJ>?kzVsIdf1-`P}(Mt}31sQ-IvsR_hw0KkI;Fx<{M3BJ2c z@+H5pp3B=d?&~B}%IoTfyIexzV4KZO!nCj6{KcC7ti$U|F=5E7^|S!uc^J9cw0MH_ zg?}{jD7ZV1wD`M`tyc#&+czJ2Ayr?*0+H8Wiqm%HkE(Rrh{KG(C@Dh!OC|NBq->04 zFoB3lq&owEpl^rF-TR!8#5JkgjjC>fM4I3r{J~FMK0fO))DJ@wE3o@}m^j#S0zS?o za^=nt&L|t$y*~qJDaPyo$X2+xD+r8B(qGqM5%r@PZa?$X2;W(Ptb4v(1AcQoSqhw> zNrUML^WQoX(PFh)^GF}y6{=~IIO@P58!cFP!-AV}oE1H~+Cc*wE+E9i_P?meg*z*K zT&nVuV=zc~A1F?#xBG};wwTjht5 zanSMrTrRV~T&fXNq_Z*X-c%8nEK@B0FO`=r{GJtRUjRjX{e!WfUCzzY-0fD(U-D27 zw+TI1HD0)i2zb`4ZR5-(aT*&mD#T_tr#PP|M`)fpcHshtQvq8TxTDVS1-KTvF$#*$ z=*X9PwS>WVjYn}*Z10#*rZn!#Ffm(dHPy6+7eK;&MgSopN+1TiDKqPt5OmYz3CWVk zMEt$Ul2~*BKmo)Am{VUJ>{lQCFcC>Bh0_NyB-Dmy9XrtFH*oQ#LLSHD!y~VLV_KPH zR0B4o;(E2jISb;b`?fbGcKX84I^Sz=ljIqzf3D_A1up+;&Zz>Pzi*i=d10%XJQVY_ zfnke(s@su^ZFXS|uXUi`0iR(Gghgu<$hvux5D9LSodKA#zo@7By=FPnpt%_EBn>qB zDGSTZ0&uURC(2rcXhhE#pIw2SMD#~5-@(lSjleb2KswotG+^f#Z9T|iy~eW5Za~aP zj!`f;iHEJ?LDYsj!rZszkB5(~NYWllo4jk-8d?V?;TqZpr)LCQ3vVoAiFc{r4U_kc z@gUmg00|7rUPdI%Vl6q+w;prn#BFq>nq4K}oh6UuwWQ{BH+A9VD?*#-V?Z@q^C}{Y zxw_(Ft8`nSGa~~>(yTgFo)7{i^oWVec=M*5o1HtoS|&B+g~C-hVWFON1}07n8JSsp zEnz2#6whn#a1B_QhV=(B__nr0=ypoLIF7%R3?>2K z=7byA-!g2uR|VtZA2ooYx6uI6BqlOp`x5Vf(BY(szbU-His|4Yco^Wk50x+|GOm`=WL>eOjYO>)6{-%mgD_ z6u17GHx&wA@WxvfE@1R5oc1;SxFs(r0QwnxJ))M?x^Nv=^#DIRd;$|6fZ_|0SCI$` zZP4sE(tGCUxy|(Q4r1EqLnYi!n!WJbekrSZ5!9A3GnA=81#}1Dy)#I;j{h(Io_Kqd zjtgk{ChE0sOC;sH_}+bJnjF7}t>F@F{P7gM!p~?1>hPFDaWr4ULfMBNzgi%4r|_^k zJH0KpyoE9DaWlB3-u_Cz+^rqYFnT}Ofj{yOx+cK=Mey7ev z)<`#O-pud~)p=aKM5;Q~{mJ)oikFRtYW@Gu+Y^tzmQuaVtz|eG_<3wix}p7W{;>Bi zd7BKi?5a`l8g5hVAg%N=4ps)p7+uQv7JxGx$bG_O@pV7%oN8YozUu$_vK2I06ojZ>VZbm%Qv>HL04}-%gMHu;l=+<>>ndh)N8gmtiZy%arHX zfS{_Y`ynsvt=V>5<8IbhXpTRPsA21u_~BgnON3Mb*jOGWaKRH+pc8g8a!LE+6_DwB zJrJI%mWi;MG|8af8^Smymhdkaf5!+xnKybTxPYF-Ia*4IL!W0TW5gTgXuexdbDhW# zCm507Cvm+g{N~Px)JkTF{<|9KNi|#$QR?CehtNNH@gH^&O&gGWmviY%WCFp@yiNh5#GHu z91Kr*A}}Uz@IFO4ASdtaOqw7`AuyS}|5svM!v-eNj%v>g17UKSOz`|aJ$KxcWGcyH zA&O2{7(A9CUn%NqOiM6La3!Ay8XYJ zYCJCF5Bk-9xe`YkT?|LY2MrtLdx1E}5j5FRw1tOht*&7d(1p}8TQ1t^GDRI_A(#gL zMB%;Swr+FIcb_a3REg(~LFGL70ySbiKc%4wri)U}_;r}z7i=FituT~<|AlZU$z1MA${BjO~pKx;A;!=2f7G$wMMaNyHjF06I|!f<2e|b%B5j<=MX8_cPV! z4NsGI=b&(#Ta`(K+;p1oM^lo4zu$gmyA?+Nzhp4V=N`lRZlinSO?E}T)pHtatD4Ki zTLkt9BhNNcrRM;TO;FpLIu`yD9x7^3U(O`0-~H*oWhgp|1V zXtgHihzhDgX;5mYgAo#2_zlCGyDIL%%z(v)59D z+cQ9FY>tbZtiW9do&;H2WYJL!do_can~ELkpabk({1Qq`%gimMzmt2Fy!ZJsS;_W( zWYgYr%nSL}9EpfJo^`pc5{x~aSTf1rMzks)xDWwQCCT>+aXMd`Z8*)i?GunuF|I9% zv=htb0>TLqVE!Rj!nS%itCP+S%l5&kozd8l7Px15h@|r_X+e#*)gVzIFcO0 zWvuW@+7b*I5UPS1gjEnKnEc?BpZ);*!?XSZ7`v~W_dykr2e@WyLmqwxK(DV-06_n- zBZEv3@amqQHw6kWJ2Cw5IO|pu19gvV$+atJCEQ2NCi8>+%=iGV) z^4M25zNRviUB3u9@)v$Eb0yT^3DglMCOuV9Q`4ym8K5h38!LMAx>i(;R#_A#Ge|bL zy|oq|wJSvEUWs4k{p%~+k+v9@kiZ7CszL+CLQnM`O;?gO42V*36XR!=@4m(A{wqu< zQ6ypl=?x_Cx|wq}wGO|6G9wdaAA=99(ENa1#PbEMR*joBEBzNcFuUk2oNG8ik7vB# zIy%+s$!HvC0e^TGfeZ}vDSGo8>^U9_jY#RXq1WIHqSJZfqsfG{G&{uPuihlpy#o$o zHH-K*mvcT%Psi~E;oIa(6S!xd z%IdcMP_KgQvbCsqEN=F)d~ak5pPi8VBM# zBXM}J(V|cAp#+1yAC?F|eL`@IxFG6Fx8|REv{Xi=@~I%|)K?6Fx6Aray)fkZPf{56 z74Nn6Sc{?_x1P=5BfyRO=vRAKk|HruFjq=46%o8F%gs!p(DQ& zt}YB8Onj8~Nlk}K0^3X{kp;;@v2h>34}@N4fq*dCzKQ|Mx^@S0%N6W7Ip|Z{@O?$Y z+#9?C@!b1t3b5>_NQx?_VZumHgj}Zf_fJRZyL>smjT9AD88_#$(#mXD)Bi6c4@@A- zJ%eG$E;|qCE}Vw`l=TNM`&r&M^Er&o9ibn*83N{G+7b)6dUIB$mUWQH)nP=RcvAwS z>y4l7t><5ncl=+hH)NEf89x~h1hp3nop5-jQyfu{2Z*0*HMM0{rC%#)Nf4NN)ph+O zn|?#6k%DDC=NabF;K`{{cvKuM5&E82@17vRIZdLX<>JDRe|+3SpyEc90K#l!=ClNL?1vlFDSOf#exx1*Ut@SrlL&z z0Y@H_BSD?(@eP>gA&PyxL`*jOu?xJ#Y=j)$0;o&SwTo9D$KC#yoZQ=uL%XcfDSK?V z@|=Tge^VS<)omVbej~*F+3{}#X%9TOz}M%<>7onuy#sD7G$a;+gaSK{f=erc816TzTJxm9l04Tyu>#n$$!f=4Ujn1Vs_n{F@2+6;ymQ1#zf_L zpb?Y4tD6-jle1gkE`;sY_Z2h#659l>9h=5HtAHik9~7F9N;qE$IFdS9ffdZ*()0^6kbRetsnTL3>Xy6Er04G)vF-zwFe9<}_fz!*A($l!l2MbXtuy zidWZvG8u`Eao}6&u`0*;hE#fJ{3jx@tX62H0r}mB=Oqsn@{aevB)k9~o|MyDB;nOs zOh5h57B>e_f2H_fa1<_FCArd=%Aa@O)xx0t2XTmj7u$*<7mHqa$6l8AY3?AuWGBq8 zpqAW^6PpaCEeWnRBmFk*us2i53;Z$E+qVx(hOx=2%Jb?}g&Rp=#f9jQMilERk3p}s zQJwa5m_P7NY+io4&N2uc#*aY?FPCztA{#?js*kiTlhTp@0cKmL(L#VR3lOmf**wPD zPXPj#e}PCdr0_MgC?}ixS@AW4$$GZ)?*|N+)Uk-_uPQx0u8F-M3_wltx1JmfuJwn# zIspF#r=cKkV69Swuam9NN0hh$d>e+S~myl~k(Ji~+H! zi6Qt94SO2UVOi%q+K2N#bE(qnBk*9x{2C4FemP0v^zKX+)306rn0pkJzEud|Cz4qc zelzXRPK8UzTP|$wBPWShM}LTJlCw&J#e$ndesLUS@q; zOia0J;hDAj?c<6B2FFaDyK-Kw(g&Y*Np6vcsv8Niq*fyH(bV+7T!lUc^9pX_;q7Fv zej3yA{Y(9Du9ru!BkK!Q%okHhfLbbJO2dy`D1*bd|&P-NGSQ2CHS)bMkjEgV9pjal{%5B{5z z$ZmuQ8su&)h$?AtAq2~JMPYx{%LbHZ>pN;0E;Pg5my(2SDWHog_wD+mwU^ny4*0cZbiH9H?lH*X*j`GJ(xG(lS|lcBoo{X)3fW-^HKSaD>Tk{E&?K#<`N((I-mMVB z;mRVRyid}f;yljqdl2=czTS9^a5W3WvHx?tZ@Nw#;)O!rza>MwB$=X&pCp+tk;LcL`FZ*a zdC@CB{?2jGdm%KSrFztx5~|j!XVa1_8}`xTer{gI*VAfx2Ns ziuXyu@H8%CvZVnOhXyeMYJO8}yXDDXg8-{nBi!d4rolfp|HgQs zfxD(_Vs-rMKmRGjBjo;+={cNIOX$3x1SY+fe>Z3k7w}R>k=eSCk|||HxpZgftGD#k ziic@LZ=Jh8>OOr!mnU=~gKJ?azTC_MoMW=U_om+ML$n*&i~@^4UpRae?(u;a>1rZg zA3&}+?)G#x=tM6+693!^R426fxa+M%jvI?H(~h#+@TrhFX~iIXJufC7o+{l>p{xEC zRV*yT70Aqp@xSS0035sz0K%{DH(3G^OMb$1$=gVD6tQe)_Q_emG@7x*KyyE0^^L^7 z_wbUn_O5oBk5E0SnOVzl0wCx&W=3-RUuaC z`h}Q$T$Iz>1tc4kyFP%S_J#&%n#uw=su%;y|X@H)`g~%52o$vx>H14KQ=Og?i;;P zRDbrN>U7lShA8pU2#{+(3TVC{RH>5;TokMpCi?He)4!sWpD7Nk5U*|Wz7A}9E3x|> zi~(2;-#FsSt*N)oVyBjxY@0TvnYXyjBxDLR7)b74A%94uj(%!zuf803`g-dIRos5& z2yD9ug*=Nk& z9i_?iigLC;AXRNvr{^qwTGW7&5Kmj)_>aW=fXNc7p$QuI`VaBw zR%?hPm1$_a7*U$&S}dqu`9lLqo)hZt{=)m1x~mb<2bJf1fN_kfCOqLBA6SB_>#fms zPdqvFE@5K&gj1|8153o{mEkM<5BvQdTR+g_q_(hG7ac}^7`c<2Q4_n@xntjCG5$BO zY?ilx6Fxq10O1cdlbb-b`6s~9*Dp3tRxRrqBZ|(5s7vXJ&e^0h!{H0qfDT6s6@V360fA0)H43OCN9{i`5^n`Jb6V=4=j0H`` zquc*K+b2SkP*cZp>I3tQI53^IFz|?ySGg5O`_@omE=H%-wE+VXWd~+lj8u9CDVa&( zm_^^u%j07*#W8-?vet-B?vo7G-eho{%`suEpEmslfJo7-*4u8?%LZ^7H`t zgkVF!Cq@f88Ucy)XO^o7+kqDjp4Ww8mdr>v$McaH8yarc%}y?y9ot(hJZ#+0l1TU} z-&(TZ4GVF|8#S5k_FlE0{Vxdk`=`}%DoM#N^gUS#%Eh*I!cE}3`X+F0w=jTF*g}uQ zdz%T?yWy!CqGSm%f615Z+GX=u#_710=e_zsqF??i)I zg4}$9jS!m7r`=fwwn}zP=41g@RJ5$fP|8PKpdI;{qRecC_RC?0e@!tD;CdxNFRz4QFp_ma5sJA-wzXK{^jai?Cx%a6{ zTW->#LF#JYFwmJLfH6Jy7RN&MFsQN0OE zs8k>5Ct3Q9!m-`Ym*4*KFIfM#t2P)kgHxL8{)&4?y#OSS+ySI{sCdu$5{JiHMAZ35 z)4(yIJF`{k10rE-cHB5|b)%)-oWqrdW|fW0HCWF?ZCa&XrE z>|Xr)MnFfG*&Y{Pzl$MaIO+w`AwVMZ9DqU*A)n{HS_%?J!R&wE%8n4S+f2%G{@F11 z!n!J3VB>{Dzm%);7oG=*4k8ceia0I(mIe6m)h(tr2!0K&yS;aV!;eOvu*yjXekX!T zb!JPyKf^CpO*RrOgeO%00^vF5%RE+jw9}Jd&XbMB_l_%Fa== zqXKiojo7QIO)-h7hi$2Ukz6b}{y#Xj3&3_uu9|mNbAo?AvrMpf6y^R4_4>5{cZAW3 zkOQhuLT?!ol0^UdtG}>L#9E^YEJ;nUlCj#5ep07z1tuO@BLcIilk21$sk1mZ~K;AVu0{ zqmp|yxdxZUf#danr!0XsS9m=CKRq3Q)91e>R|Bf(7l(g)x)K?MSBAr95*8dj6k7Di z&cSa5r8BIbLlo7N$G2f;^F zKNCg4~6h0Fi%j z1)js!j-SIVI_)^N9&7@6(82lVp#$NCG=XTVG!F;1@04WkWOah96r>Le-UFp}yth2G zyLJl(Hnz>E>vAa1J7(NS&&huHomgx5!PL<^(xL~J(GE$tAPPBsv(5uK(~V~;b%N8h zwLQ7_50~9^3!J{&BVG^esB$ccveZ)zZcHJ@TPb?{gtjeQ-w$+4T~lm6VF6!Ytt{F1 z2I8CLq9%Ge=V=~y8N~_uyniN{0a&62iw{12&iW0!dc;b=LuXP(#OG8miCpO$xm-D5 z>amvS*9$~!@AG2gpNs4!VdR|8Esl}kuw@R$7%^55CD`D+v3ozrMJ_?1oZLT`UxNKN z0+7?^v_ol|s(J$J;neJAuC9&!6;!6_0A{w0b=HIj^7eikn2{d0d=%M*G%g)jY9bC` zDoNwa%Fh~l?tKDzJiRr49wI6>L|$!S572u)=Ma^=d=~JZj!lyr{`qw0OzlbdVcrrG z*NHS-_MrQfTLJ743b}KL1A}@owpo9OL7-HpOP~&P=5$;Z`n%Kh&;L=z$~T`2roPKg zOv{{T@dNwdEjM*Tz@=#hU!)oH%6IXlw9e%>+@q`YZgqT-O)Ykpq`Bg51z#T6m|9ca zL)h!c$Y71-6D}uW_zESmy~rg6n%;d1PETOaXTa@dqfZr|pUKY>2I?chdyDLq8 zuCqffy;CB&2Q0%G3V#xGa#XS^n+YxG=Q{Xwi_g;%H)2ocUzVb8ZCAbR6X94H81(fH z!i)G?gAhCJa?YV+c;x9F{e2$cIe0Jw#G#6PTOczecT=2g6vrA`Z*x?+mxJxI2FEf5 z9_Y<}Fp>mjqpB90a|Z+v7lPK{5aK7>)HZS@_4tQN9um!5%aWVRo^ZX6gyQ2>F=&c! zwXc@LTJ)$*jP#D^lD>n~p}Pfbm5y)>V_v9Y?y1Y{Ol$wo<8Wradfea-(LG@&4a|(d z_0DbDO^v*xylbhWD8|6o*&|G~KB}die@KQQnCP%F>|Ck_8)283j9d>s|6vw$C&46& zDH{?WqMNLp$w2*niM1elJ6rmBeTD7loK1o+Mb@G2kRMyCWrkL-*zP6sQULZ+JMJ~+ z9ydb0viFzvlAP9n1ECP=nyj6?Ejhk`?(^6Yil>3(C7cj0RnlX#4mBmhOWLhHA%0W6*Src^8s$&QK%f9?r~)E12MSH0@e3+i`AlhwvX0dhP#iN#h0{u*POH` z4#k(LB=!{T9?Ff^QWk{U>#gUB*w}SLx{tw@ILFhS1%hK9h>yxBQnM-mF$Kg-L%rng*7XOxw66S?qSjYYa*Xd8pHG+n0XeF_bcy{NH*#&O`D~eKkVoy*ZjPaxD;W19wmAP0fLO!3 z%aw+pNE!KRopRp%&@Pan*=#N0Q`F%JV$xLag8UA0ncY$l`?BKgj?#p$+2A?^?-E3u zAzh;7=+!5Wa7@Ilca-S>A$f9fZnY*3@majB>yA$AtuDFGTf0R3jHv6MO56D*@N>PQBDZ-t`2%7bVmZ=^?wrQ>_eMvDxCMXNb8P zK;!4y4q6`^eP%)r=t2{UY$a9FYhUT{LX=W?bP>$&JAk-c*qcGfj`N4Ol|;E>xYV%R zYP*x3W3Yf}`-M*zCV}LVg;&iVKPC3sj4Lo6PrY+W%Bp}dX*cm#jB>#`4-%+eno>SO z5_qtfdJlKdsOknLm4W&B5l=w)(H;SH)VLh?Gg|TQ^P;WddbP_ZLq0G=oXwEu^>1)) zn4zm}Am17;@$2suZN8f_a+Zz&01?e*}O%sbqEbK zL0!UuNREeZZeH}<)irKjN)30teMY{)s>k_l8pHfa=+Bv>Ou9IK zL*gDAW7&!MIHGB8);&nyg-npVCUcAtk_z)ur*^iM(y656cO5Q9nkZH-EV81Ml!irq z-r>7dD*64CVW9A|Iz@GQIemK~l8>ivJa@SzYt5ai1%4YERb&FE_gqN>Ri=n2w!hMy z?;ET8ehlILu*-Cm5^)Vqz2&_EN4JIk#r}|=5tPJ9%uhc;|Acz7K}RUwKRq?!yy+V3 zFPc)3pgLU>cOvWF?Qw zHzG^Y8(L7D+CCtW9CsQMY;HBe7*y@v1hWJ_6FmK?3vuddh`|aPvb1cUO_F_4b)@== z0^a*e87Zn|#q%g^Dx-h4d;fYLJ`S)amz#KNKu{#D3rHg@{dJ=PCC&hj`!d6Ra=HW~ zm)blc7wcxaz_}?W01w&K*)Q=tIJ%d6=|!+~NOaZ^x>0wA8q6`UxSP0!&OmHTJR9hF zh9WUz=78ex9LT19g8Q(7{^2FD?BZH#FZGNr5q1OYn(LysLgx3`ZR$eLZv?7h%A0Wz zu#u7ws|gOBZUCJAN>0jBS~2$Oj{FMO{!@r`!OTqVabSRDVErU#g8)*$^xm8&K@ z9JKJY{B7JF7{px}^KeO#@d7A2LwPSfj6W;CjggHd&Uw9rVC(KJ(7S zq*AtMk)89t66PnXr$GFmlYdPW1q}xOLnE~U38t3;)kQN}ux*Pjys z(J|lN>C8f21c};6Y&_ncIFq&BA0-t>4MJupf=geUz*Ce_2fqpY$P+rurz}8}W*9*l z3QJR?6nRyYrUV_CV;_SoSeWWP-T9ldNA8Ih6g1qWQwtNRDUGAI(|RPAQd^a-Ebw<% z@yOE1l0Z`S@%}Pl7@+5d3uuj?ZZ7YOGEUQQ>#NZKO+WO3I&&_N~h8UI3(sSJ`$QA z%sTK>Fc(G-{6XWyGEBCxPk4MIxyQ8W80;<>&9o^PQnzasB7#w#*G7c(U3Of9$byZ~ z-7JQP$#wE_q$Ic+%$&G@slTBJ-K7gJvmoiT8g!nt-^hH0vS4<%)E)7rocnNprX0K z*vnsPs5&HvZ_eZ&gfgJV*5blwxdBmA7ywXA61$~7*TqBH+b3qXJjxFHP`uR(uD zWihn1RA9J0vr73$R!Rx1poXdiN@2kVy=x6Ls7HvmHIPD{B-|60CG|h((O-95nE%-c zCEN*oUDD@1`|?D5&vCW&lEC`1bBX!xg|z*O=0j12&0+XO2KN!nR#P%Zd!-S$`slV1 zVePg^uDoD(y0i|MMrd^5t|zU z6+N)4-~@lpYKD=SHJe&;rlE|$M#pYAl!;Aff0G6K zErIDVg7iVo9r8@Qvgxf*z(>Kd&HjA+czj9vDMjJw!d8ty*`6KG0CH2t8w{5 zZ7nDp)Mqp}3LLX60=Z~O9~!OQ;m&r@17HZ*{m-caQM&18tAw=n)m9J96s>H0Q=}e- z(Tj@ZmIPS9$kuV-hikAK?zl=`U7Pd2_kC*5ZqJv=i|)eyvsO>v0MFmvK)pH(R@>G* zg=xuBj2tf>CLI8H!ApqgO}L@gFrFhb6b1%FcR&8@iIsWVpiZEPf6F9gQ`rOgVzhFo zi-cx&maFi_?^VoS){-#XkcU|CvSH$V_A{hNA~!G%RlQ{#Fcg2|Jq_g6tp^1xegoUjDt|L^@533f-FPSw7ZzJx1hd<@iPuSj964Tz3#xhdXhZ~mxkBXlY5W~8_VV^HymBOo zR=}KIaw5P(=XYk^L|zqhfz|645}O|YGR6MxP2Zx!g;{i*WWjFgT={bgu$TegpQNEs@F)N!#^G_c~#TU3yF$6 zNRq^qi(02g13z5Es9?#xARtlVHAbc{7C!Aq-0@72IXbWYp}j2fXRA%O`y?W2u-Jf!k=|_42a_rPCG>Q3!b0l&{cF-zM-0p`_Lnsz)8AN5~5*j{>&ab!wi4?owsKN!B(GvqX78&6p zQFfb5Ktflz|ru|?0Go2VxM;JEY z_KK*PR_#yt$sF19fT?RE;CTU{M8xwU_v`bJ4!yhrENlrE*e2c4Se?_*x0?lOU*AuQ zAl1LyGjy3L(fzuBSKE2~3`+{!KuLxoR;qe?p0fl*f2yFu)?#Eu3Q^B3^m{jee+;(v zp)^pkI0@%G6Jdw9Z{c`?$}kY{Zqs+2K)m;K(suOi%PVl0y`s35l1xa!`6F4nK*Uvb zY~U`hSwGp_rLyKy7_n8}4^D#zEbdC|%+nEo;x0D*wft}fTB6T#;r$OL_^jN$1276R z?4Raz89wlhHpM7Pm?x56g}k1<0b}l?lWeEq50zJv|Hy|HULyrspQ`YSF*^CWnfLek zGYNoyzjp(#z;(kYkFR+>l5I08t$!)mb zNL#jLdq4nazF+el(Q-P&SFElZR`Cit6CIu!EMM~T1DJ?~8y2h7$PK71njpIZZuduK zT#2J7#jvAqvjPh)v-p5~H&{eVRKYQ;35eXPC7Z=w@}Q+IZ6(N^fNA`nHOMgRc8aL2`DKbAxKU_QW!|*q(K^yhSA+2%?N4f&JofzdK=r`zkR;H z=XqYQ=P%g3_v_r}KIb~uxvo5(rGQsoeEZ{`?o=s&KF&w(V781EkgM-)xy+>zjiFQAJ#rCT`{OOUSV#3vu4G)V&F4q zB6EAhafS5Z?ITKQqFBtIRz`TT3hAN(E{3w%@+y32bhS6vB;}=XJ66bjIJu;~yvvo( zb93cG(~A{OXH;L2bzwdnYFP7}_72}dxu0P7MLe*go+|xuC*90CLwd%ghq1ucW8~ff zVjE#J(ar|L-5Gn==;xa{-F9Jl5F>ZqHTO-P;8>%z_p9FK;H3y#>A8$t2qi69_tps+ zp=}O?i>mcF-8>_D1#a^`Aro~EW@}-GOS%atW+Czh!Lx;wgmK-(d;@4J|JlPGqF-rD z@?MYUF)g~I$OjXyuV85CvBG)>C+W25?6bKW{(|2C8$Bx8sSwE&OE?ArW8Ydao2e#R z&Nlq8;I~v~U!&ER8cLA{ri1CnI{)I9?qtE7wR=!J~GPR-K&D{?6Vu0)~5a(2W7kzgllVCfl zx}|pgUmr25|{a$%70vc=)QxGDGx=tBJ0!A`I*xx4;L7dfOwaDuYJ&bRL_F!kwXlUGuiHw}6 zxe)SifLBBJ&!EhzR(%xFd*M3rWtc$#SJO6CK!S?o^S2I3a_sl@CSKjxFn~KFS?oN4 zo9x2J3WpNCWwK8+-wU3DdU`(T&op^X@!G}x7Qyc!2x2}u@IBKqJ}jd3@Zc;J0&uos zdiLb2Hvae~w%xrWtr=)Mmwz)Zq!ZbL6*Q84;ThjnQz+y{X@KzyKTMN%^r}Mi|1k@n zj$QH`ETi=8s9SNf;;7*mU;L@|GU2%2P2dmE=5?fgdZzkqT=5tcL z*TW|HE}2^89l?TsbwQipRSmqPT&_A}u+w+l>82<1)xvuyBC0H4bpnbC=3SG5kwI(FtS@#-TQ zKo$$w*PkthyFxL?i|m9$vN3n3nhtFBE*c-kPZxi(rA&|B%J3>r*yd>~BAC&BZSvgM zeI3rx&<*WTn90X+mFoed{OIhU#WOD+AfYZ113BiQG+D3#^a(cjE*kmtLIPJk-4rl= zQScj42gF`Mq`!zNiSkwK40rL7lN*t{++z%^=8TJC4l~6v_=vfPZdb3Woc|xC{o1PUKJBofze)yB@DS+h*5#(aT`c z&4ebeH+ez&mcN%OZ%6cv^@Dnk15F?7DINQtGZ**S&>QRDAZLifaL6Rn6ewRcRm8gK z)L^F>Kp7oXtG3xM_6fU(7u9=n2*3HF#oxwJPASFW7g<#j|Kw-NJ`w^=<00w00p0o? zB&0Id)o^)1c8x*v>SkLWH>TK2mD-Fb;*OOo(C#$I^E)V>DoGpHWPn#Ix8%$2Q4-A$ zeM|R#TB&YStaK^lDrdfJvf8E&Mak?%slQ!Omq;l(m4B#_yY=U?FxFNwbt;skRD4wQ zD}yE3WgxX#f3qGuWv)S^d7SrHLqYWkdv@1UjANPlG^M>!l zC#=LARCOo1`|ZO8eR^^+W$T)JYjKi`orxPcO28eaw9A=%+gb9))Z^#`R@}<_SZLAT zp}rkE^}=)5`R)|fU}LYdt6IfaJH5nuPuJKn=){0|`ZM7tS+can__TxS28vY-z1L>%83wF{UKy+R?8Cg(a`MP zQ0Ch(kD43NxIIbasL@lyUrNs?-u0G;i;?Y4GJEIMnuQcdZ1l|rfwq72<79Zw{M6;w zR&F2>P0>z5zPSPLHD_4@k?9mfSUP@pn3D|4FL3N=@%lrJk@{Y_cnjp5#|8O|5aRdU z_p(M6T9Ys2K^+8D3%pMZenb8kU~4}z7dcX}bqGwy)=$g46_iE+d42)CI};`(1`Kv6$K#tAi~O_Wem;y%HecH^BLCGZHTXK@Z4!c zsz4M;(RBVU?4*&0C?4FrS1}}V-qVP1o&D#ewAmrW$(C`2fC$^Yr18UQ!fLnpE3DD^ZiP3)wl zw3&{W#vbGZgo!aIwbL9d(pJST+;1bRy#p7<%9VC_H<1`W??0!0<4Wt+kSx5Tt`A+L zX+Ry1k%Gw04STR&V_85$)w-&hMAoL7_Dap5XJC!5o}_d5nrdsE0JdtgrQif?-(n#f z_{jLG#fxCu)BdS!wZ7fxB3jKbWH~!pH$E1Z$(4Ke#2@VW&uZgW+j;UHXm44xkj3%u zM`NGO33vj9-Vai$%eBuw=M%S~Q}7mw#=jr#w7alvM;NS6I3!f%5iY07Ykxm=R>i3A z;)`HI==jtXj^(&kt}8O5?urYf0j9wDz!=cSM=Qq-5+C6@!m9z5bMO?9K!~0fw~@DY z8)(ELcVRi7K8H{{03qe34;+lFu0Ef4F+rQGS#}cNK?M1fdtVYl)_h-4(pWf6-s>%5N@e_eLvgiv8X3?^*oz*pzjbDA+HiNAixA?*7~0+O;nKN`KH9J981M zWr{z1{jUKycmI{|XK}jMzlN2(I!B1jy1u+EZP+s-b#%-1fM>o+c{^21QeX8;C^Mat z-fga54X3lkLq``JsHR~fuTK$HpE)Z++9hVsy-%BFoY_mzJ4#}*>Wu|&GVeJ*Y3EL3 ziQ}gT1Crl~;(NCz)Bq!Rji;7YIOgyLa)7PasJ1r}1V>NKRAzMv(_!qL%-rlo%CB9c zEF()3A7xl+2Z?w$AV1x|OmN-K`1UtmG4H!i9hm5!yEC+z>G#6jrQdpm-qvlEmM%}x za&EhNP*9yK&B0$hHlbsa64@-Hn{G{Hn&hc^It|p<+9~gR9%Sch*Af2r`$v|N98B@e zUA#*e1I>f@;hajXODvgNf&aV$37EdE9r{D)NO8sn*7kz6ue9VcR0AES@^1*E?!l`7 z@wsu!-UqWEMul?YStbD&+#OOjESTO~*vFoyT8!Vj6ICHGp_Z1|^|jj`-K@Q=Lh!Qi z*SbX(ch3#;B{6TE*@KQb)#u0y>9nXK*p^h@fFpzt@?h*PI>PHe ztD*)wM$zpt5^HK%hUTETQTUtKTSJG~`b}5|HTIbSH9BFTB{pYG_iE-ucMl{T%<(!b zUGu+CYI;9;QD6iVoPn!dvdijQZLapDdqRqU4vOAj#1jB>KVV5gOh@8tp4pDQiTeWS zty|9|ynj&c6>;;F`R`1wCOD~&tsY+EZF9zD-}_Odp%gpO@M#wtv!*iK#ez(C`Sw8} zId;YNlB#ilu|xb&*vMPSv|W{VA7OHyU2BbBdv8+R_i}p-Jo5Zea6x2lfRyf4D!}SV z=k<52H}|eU!y}{1Zo#~g@pRQ2z*8pouK zSnrBwb4&X%y{XX$Qu0VymxN5){B!3TMYSn1VXJ4DlRzMo`1 zY}{-yV)pt^)~)TguiXGZmH)sDrg4i)X*e&6-=O~C9gV?i;#XA1E>Nw}T*#Teu<9V2 zaeWEJ-;^Z*E&r*}(?jh|>f=v5UUq-JNF%?h_*C_B`;X09pwgymyomDMVOHyn;$Kv> z8qy)oeaF+}s#hZQQNm;H8^iZg1e<*>|G{SKSQ#IgwT*}*}4`_mPG~MQxyd{p^-!88 z5ARXXt%yZFc#ZaJ7i@W7ct*}oS=TvOt0DTX41J4Y?uL~hx^G;?yK;Vw&u0t8)in{2h% zY4+_~RlYuF#7$cUye3kG&`sQL-yE7CHS2UC)XxILl81jcDzs9KRr_C>OS$OQKGV}g zMW;JGdszRT`ZY@7=yhws$t8+|x_}Ji`&rF#%aj0(XX7ennD6FEqDPDsWFasi>50w`OFOK9UEB1J zk;`sTdu?eZtw%A!sIiXrr4vx#xL;_C!t!Vd9msyzOxFT=T(gCPuDI?S2Pnqo~=clYr1rX*y7#_kV>+%gMBH51LCpzxIT zM~^MJYGzNvEksVVSILJj-gygx-nm~ps5=%`21#QcUz&sqdVPjS9Hrqn!dyD)PUW{m zjnN-HL;UKugCkNhup5z6A5gdLl0@7mwNE|V_U6y#oxn5B5GQ|wZ8*lsGG{i}Z*Bnx zDj#t__SsD|PJ!Fci2j^Gl)b7kS!vhkPepw8vmz=^klCO-I>Wd~eFgWy9s}{h9w#qM z!wl1R@>A`{SDb0@?>X!Y1|(ArqI$^yD{oG>G_PUtgHvSMg&@!$U<&wyHB*z)J*MXp zLa4Tl+z3<}KJh1Dlg^ua>c_r^L0fJU0I`Mo8D)BS0y@8*ZesS{$Z6B6+wwWR7p?FV z!aur2t?!bg2x#R4XzT7UJ)15=#@II>=qw-@fnVL>6LQp2_r){GsA$iF4c?Xm3Ofr2 zh7}Nm^wPI0yR7dG@9;;870=iYx`ss)7*YAm>7}245*C&{xR%aJAUp!oeGhQ&QjSsE zX3;;}0ZujZrAE4g6L7o?Yd4|kr8NA&^x~H5XKn8pBR82TN~FhA5A^by(+J|;vFe3o z@5OWIO7CY06zg?YYIM(2rv7fBs6%w1cr+d8frRL4{UES(K9c4!9(RhEMXvcF!?C)@ z>C97vI^4Tgye<<{wwGAsHt?@_X&2_X2eZZZ2I&C0c<4Fe?4iw-;D}P9YOl#0Fc$t{ z=A-Xqhpap_w`V1Si~D9=>|yMD)lLR*lV*des5yx9)b?Xf?{2%ezIE1y!jYEJqqZj( zAG^d1{}_D9zLB}IkUsu6nT*1F?jcU^h=3CMno&2wTD`%X)k(7*7PRz2ewbw4zCUOPxM0?jsUyaW-XG0*)U@*vpOxz zL_J>hUIJvU56b;#;^FOJ8Wk4KVCFN}rK1a>SiAPqJ^V!g^uuBlY zp6+xZjx|0tFJqUMwQbYoBsCSZ`u>kAP}zN4iOWXeDAp9j{tk8?v=iEvwjmXZs@MzsSo$@ zx=eSQxZmIS!Q++fbSXDbeTSN*5X4fy>|JvjN_NNK6|N#9GNGbYYNsXzpoK$-J%35Gm zgv#)bKh=}5$N6AGX}L|~2O4Xv(8bJO4&-+~t)g$-81n=1=dUlUin?3eQC$8J{dKH> z5@o!qa(IYlw=5wHp{McJQ2dr-;Pr)j_m}a{0?c${zGb|r@KSvl-4fT@XHQ*NRU%T! zd(kTp`{Rkl4GvIpyfoC3klArP^f*MX+FD=1SVeV zJ~G*U7;we9ZNOHdCqxkyz^?9iq$t4@02k#fQ)WHC@` zGkeQ-f8T84Z*t8zUqwr4UFQPru^;Fg1=DNA$7f@rf9knko2HfA9%!WteQgsGP4x~& ziulc!Ewa=6nb2A5&>_2bcfrDYj{_eqWv|9msJhwHw+X@4fK`BE)}3VIHixkrQnMfQ zH8t|93Ett@uTs~n%V9*BIVA{TJaHo0x3pHjtL<}^1HDatz;mOc*pI-l_|c*v+XIb# zq&C6gZ&JA`CLve1?ghwlZ?gSJu@~f(MmkE*TlIFp!#}+gfr)p}>o8qN8i#(6064Y^ znvcTHFWq8wg)DR1unKs%kY&`NrH5Qm~&=Lxktz|_SI>-|d?2B5LxjC~Il*fa+Gb2NTURj|h$;4nIZf}XV) z#%BM_zlM)4iuQ<+xn`pF)TxPN`w4ki@$E;)hi}GLpI-BnNW=ER4x3lyN`Xd&{ir;~ znD;x14;BN4pRN36Zlig>)L!57+@?DO+WxR~c|GJk*4v`Lif%ye+rT#hSwYws=X9iK;F*C;$VYMkU3|l^l z4Jfs2lWq2dI1Rm;LtWVeJU&wxV0#s$ctgA8V~yy-VMfGPHAk6%^~n0-XgX!kj60 z;vZ;Bz8!5w4H?$Q2W0axDA;PXfUiz;U$-Lvk@KPD>Po*eQYJzi8!-y5p(RRZtcU#y z1%kGt!{gs|a73zz!~tERk^Q#)IrSqv8SF>GL+_99>{CxiA}W3gCu&M!_X}tWn0@77 zK|(10ix8@wV-e}RnqRtdAr$PR$P?q*52t;VId=9Wl z@2<=L7ToXWUr@&&6)>zn-eSJ?ScbUmL%uGl0*0JpV6jfd#O!tMbCkqZ||;j@KAKfJYnVW29Fa^Qa~Wf zsQrzo_D_8<4BEe_e?b@^d_Pv|ZL!RVwfM2tv+;N_@CdUyI(Hc0HI;Gldoo=&@8YxC zY71rOr>|^5gU5Cz*GX$%i|h2~a4IxrhyHb%>Yf8br9;r{j>+iTBGOXW(*lPAyqdd~$x z%wIrzUEFE;#K72^63UlneP)d=rGo8K3-7n08NHs+9p8?K=qALDR)14+26czHS~c_8 z8P;6eBx=#`P}W&Zx0FGxj|vEqZu`n5wI$~Ue2L+1aABJ>?y|Y`!kyj}*)*Y|?Ffnu zCU5Ppf&16t^*elZSLfLCd2Usb@v-?c+r_}+6f6q^TLpTkn=l6wztDv_?^6=y>ul=+ zF3N?yx9|DXc;5?corcK2Jihu!nBN)EK6b(ONVu?hU|0-`fPW^#`u%mygoWDUS&5C0 zAXUiL)qWQyHzg372-xaWD8YdAqrTwujC^0eWplYfVK)&$Im-k}6y;?=9vFGKTvU(u zCXdp)7Sc%&U@>pr*F2{7#Z+7*4!!XD4_spPzbKd{rjBBuh;UhH2m6<4#e`YNi;Sxa zh|5phYD+5QcXvx(ZrZOIK(*%5!J^8NBZt*9%i)1v@N?1DTxIz8(pK=P}od>D( zJCD|=8_L<5{8?6rFp~2a4;Rgkx2DJRTFCq6`G2^-WM~e4X{pkzEg!0h;bq632T~V= z>fSgTY1sJMr{g`#de1juRHPqmWwjbMVL~BL%V*5Z;F*O0_BsO2E@hJ9*tLe|v$Z@6FIv>fr#KE^ zRo|<^jX$YqUarR>-MqDr$0`x-`(_yCGM?E_@V_S>pfi{tO3p71@Tv#C`;m;hOU;Jf z&z*dj?u;c43aI!;rcIEKWJ;&w ztbdlCCpuxg4a-E6Edh^EzSFQ|g}JXeXX5~`-(8I~FD-y(r1$zH;`3-euF>bi*C}AM z4pxVoBBC^qGMMYx$rvVLWVsBeXlOkLE2?R&T3FN*V0JK@D;0K$c0$OYu7J6KO-fdA zjij8}G4X0^|ODqt2*W>mlz4y>G~)jS62_j}1B|XbDGlk=|(z6j~0X@|ge}kDoXm;HOerFa$4ORyn1^jnol0hGVv*Kg})WE z^zOu?uEpLy?O?>5qQyeduR>H}#?6*z&iz zYJS7}{baDtC-QFXpi?}ccn=Y8y+#Cc-s%m)_b_c&kW#|~pkSGxP3(4HcEI999OCCl z$-)2rTFwRHVibs=`h}H01XY#l>z-~(<#pZ+Q7H@(f+7bta{E zq-cgR+ln`GIgU!i9%joi^E6sy7CPsPz5?&oj27~!K3T8FxqZSpuaY^^`<^B5gsM=w z-l5Wn041(L7u!Rm$XFPCF9Jak{?j)Y-&{U?IPAC-0fKJP+0&Ui*s_ez8>gjL^ZQoi zSeGh}OKis`#ENAyUp$j1QCnqmMn6Uc=#EMXkQi8+-?ot1^coN9cFyB)Hf+|hZBp_I zJ*(%Y1LUj$JA*nQJpQui(}hwmngwS!AZ9i2PR+;&c56dNv_(;Knfsmp2VTj}$?BHVqJCW{hB zN+)|ueSOQ1+_s@Zrsg(vf)OI?RNs!ph5K9(6J1Kowshc9;{(Vb@kXx-9mifYqanP( zQbqR8ODM!)kV2YLT2IkSm({5$LhUIq1rWQ31*F^zP<&qTUpltIFC-EKRbd6B^AR#` z)-W!DwW??&?9^a&o263iWptM6^>kULrCXkKxo5Nl2BqFdF&&Sbuep~#<0b9JlFV8! zPB|cMhOvzBknKIuA8&`WMk`JjF0y7hbU$|xreYv}(N^Ai!O*lK=<3xGLglH(kA9eG z>75$ntcspS;x6dH;bs|%Q_0iiMj#0%Ta)h8jEpn-WY|}=HD{Xi_Dp`PlkRx7P_m$D5P zEJ5ArKWmNiP4Z%m6dhXYcoU<(9{0Fs>p{}r45xf3Azr6NmD=JKSU%PW-SLD$rXQ>J z)?#JI?OW?k>QTqz@a(w@l51leG$9&jYizu(kZ*()9h?lFTS=<(xFM#65xmd%SL&X9 zr>>R!AtMqe`?BMfQ8#so_WuBMxVP*%*mZf1VhRu;rfa;0K}UXx#MFhZMIk;~9;moL z*Z@MFlgBC*<3(yr&p1`0LJ(p!ad=c2}T%zYfbZ>p)l3QzBq( zPf=i&5cEOUrd>Id!+Glkca~b=huiFr3x(Ym6J+-SvuQq=>r?nEjYd$1 zw^K{Nl&uh`^&+coLB_YF|Mz^5b1>R(7iMj1b43iLdB9<$-KTyYD8`S>0|Iq#5(^4`ikKR5~r4Vu#%KkivD9ENJ3QD|4HBsAV6U_9Dw`oLm{K zo0!!HVP$~W*e;+fr-=s>1q>R6%Qvu6P?#<8aKzq(IDc?Y3RnUv2nASp*93sxlc;Tt z91cZH5SNAiG_rG2ZFT&F)K4_6FwawzE_pj0AQ4+HlasXWH~$~2U@RZdyMmzm z=ZOo!P%NbjuEV_~0=%Q_h}?D%|9%V&Ogsz z{-LVn<6oS}kPj4MjFbAjy{Wu$q3MUU?x|M(4VR-IUz^@$7AtMFfAY|-gY2ZWBgBk2 z2&~wsm`CjNHxDwZmo0P@V} z{CNVps&5%OI7nxIf*MbONy=WPdivGpKh6){XI_fKIhgh;H+Xj!mXb*v) z*drSUSCe6lNWQl5x^CUh0DnO#-Zs@GDdKP1Vw~zmsM=o$Gsu>jHmyC zi{mVR{EU>i)>hf^QXZXs9w3(&9iV|IZ%6TjMn*e(j`wl(uoV5g_0Z=@7_y!EmJ%yE z_RoVapO2j13DE4Ko`YwFQTT|N0K8AHLbVPq#^|K3Llkd};`dGZQi0XQ{YQ4muAZfT z`*a3c2AcvJ9?Y_?e_Nkz#ao{fUylLJI=rLfoB@zSWWeCd4xn~0$A@)*pXlGnFPbQR2S2d@j$^ZllSBnfF)=O?sN3j3l;ji;$l5t)`e2GKy;|X-5eqRW z*ejShfc#gfhF26FQ2+XmkV8CN>N2wFwoNop+39b35rtP)P6e~Bb zrxRXLg`^Hx>m3f^&RX^j%zfUb-~BleHy+mT{4*+wU0lW?I&q9f2gMN#;m)q|zefl> zF+imoUj{YhMmn!3Fx~$(nI^u%cFzymEPNwuml3YY`5KHo>nQB&HIk8hj_Xv|dieja z+D=642&+$=oiV))Bfdx9aiFy7`ci_zLjnHVhYeWxeENOzh9@xnmChwNguTAh=59L#|&q{P+2fk-O;Hl3;n)=nV~dTq_!9r-){ zBo5blTu?vuwjZFi%qd#LKWQyd!r!c&6};uN?G&We%p~_tf9XqaGxMweeLtP1;>)D= zqy^*k5cym1+%mKg`&9_&Mn|3spkXG@EB4Q|Qh@)?lMU`M38$UNS+U2DWcgj%wqn!8flFJc(ataOYisY*PsJACnYTTT&W;8EIWypgdOk1m zW3Zu(4PoX4vIjdS#=EF~^ElA{qz)fW`dI%i{e2*waAUZpfS|kcakgVqP2LX1qfLx7 zeDpkkeRK|X^kjosONpK*R_U-bsf)+lW-)8{yXfp5_>_KIO~*Agw}XA&qo*#w+4`R8 z3f+_Jad28N!Q=V1`}odx3&e59T~6tpZ^+}k3E%Yzcb2!fH2TjypP3wxv%eQ<)V!+@ zr<&9%P5U6u`blvQa|uk&>l*QRtb_wPskmVB#LC93-JRw1(*SPbr zha>!J>Mrd0+jE4C3O9L3CEjH8ZFo=D@r%UIDfk-GfkU?4D8? z;L4bED;!;chRo1kAq9^7p?8kTo-duX;miIjZ+2mYQugp7>pDQ=3esKoU&{l(Y{h?c z8Qds+9etc)&*V+|^}UEC=$lPmSK3pGt_Pa{_P9^Yw#lV`z#iiM<{^(^M(gdQVL!dF z=R)+YKewU+$aci-IxY?3N!yT;qNt(DJ4jZVH|_oR$$Z;{+GLpwa_&OR`laKdqwdbU zu%pChP53-)vLXrgNJ;d4#=X<=Awn>do5z_)I*^PHZ6V>#__N^~tF^&#fY%su|1{bu zU|aahrV`OT+vP^pu<_d{+uP~qKfJobmMLS-{TRG&{{H+WP4%t*$%)hS3vEt60#zP? zp>|CYWVqia35f|I%fkJy?EeqWs3rf_P1Z`R`TzGN{>!ZeSS0m_D-I3jCFe!@9DaZ8 zh^f$GjT-fw4}017QUhNw#tVCo3RyY7Jv`(SYa(=n%w`_P zUiU@HhBZ^wy0!PkVtp*bN-PXT=VJ>xop_6dG0v9?r_uhV9AQCO!kK$NLKfZPa_G^@ zVO1teS+7Wl>_2Ebr#Jec=UB%w3E&TEI6!f*;@+CQ#wWF)?&6!*!xt5VfcJt~mr*L$qG#rm)_x?}PVq@*6`2 z#_PNY;pj)(2%oEqTKlCYVRO_9QGfN`!u2m@$1?4`49yEL0EP5$Gr;1HeBnA3M(G0R zzFXGVzdOyp5Jt_edAIHwD-R~{t3xY{Qn9R#Z<|hXbn`m`$XM@RAri3)t@HIK(Nc$v zTN$-ZTf@S{3A^)RmD6(q*-PM3;XfVBE>`{-*qM^4C|(+%>4smGdQlQ za3AiwDW@;P055brf+*>jP-ZZ`shYa+GdQWj2Y(KO%<&N4 zjbCR2e1d#@lH=XXA$(--glGIYX;?7~a{aoX_Q?}!| zIZ?_)zO`u%u(f;1We5?7)^~3pCaQtS)h}D5?jYftfNIzVU9;F&@VbB%8A(C%XUtxOVzt zfugG+HxG{4pMJ?5U==hEE)eJWD|`#wdtJs<^MVOnnMR(Wyp=1pYKgxjgEL-3MM3Vp zawVD2#nQOb`x1$fk+u{K?2MAWHcZmr4Chf7#hD*J1ti|w{cv<0-+`iz=(3`MLSZNl zc&QdMJ`r)Q({y#%JV8a7d$sL1pj(bGW>$~yrjWnOu*(0bfqThoWND|YUMJM?-&(s& zz__?2{O7RvR+{kV_U|zxNpZ{ABS!GOYF@I>0DcM}I^3$NzsBV9H?JO0?o<%46-~du zqzQr6_k58IIeZ6L!hHm!Fn@lQE9}jbsNp}BYZ1R3d%CXy4lv#YdrG51u2OL@ zcg0-;2&QzEBe;M#bty<@`lp`k{g?d|e3VESIsmzPdyI9S5t$Nsx)2}!gW0N9laV%` ztNi|6mh@qC9$Cu5Pm1(;u$Cu80d$d8trB8Q;uDU5KjvF2r)aI0O>? z`b2SerUH^uihXX1Mv)Z5$P57a1F8b{Ij=3)4?^oP43uyJm{bt64(eFr*J8NTJ|20c z54;++c)#=!kUVi~dX@A$b(Jf%uOv{&@kOTU_VdQ46BeE{Hz&Mfk(vtEDY|Mlle$`7 zyY|+-GyKdeur7DYH(7Q~m_5rnwC*t}T_T0;A!l%```*8Lrcp^BH}47V!}ep9-(O^^ z9X)&KeQK>_kM2P3tGqB}e&DS0@<{ry^^y3^WLZ(JjMHQ1zh~0RC*cql;jNlbhTo-qC&btNk>Dd~!1PNhJ;U|0!Lev*4zc&#kRiy%o)=G_5 z;~*VssS}4L18A%Q36SUau`NE)3?5o^4o4H;jsD0n6i&PmpJ0G z-9=wg*IoJ}rGcOnyXhaGhfg2iLhJrEDS%HVtJ2EOGR_6jBxLc|MNg)`pv5%7!=h!1KW@Oqi1nKQo^c@>r0m_09Jz#Citd5T|(ai zTg0zQYsOzFfNFZNkO5#qUHX^tMCQ^0E`5s7(YK0FRj>9@7wSS!>o|N6mt}!l|TTxcRV7w<~?A%Lcb`N2kU_8 z);Red52mj1TTO!iD?UZQNYX7gPX#iC)T9*PDiHKJmcZn9Ea71N0KNq98U}oh5oC8^ ztrivbEMoud~F2z^+B4 zW^UFHBo#>5MoVQXuByMoVpL)es$ ztX5jRF(+pr1jY{ANiPkRz_SQnX-OOQcoxB&2q4(nv2|BIr8U0ke20S4>L4Oz09m~; zC~IqCc!jZ@IOa-=abkCCF>emKOYcdnj=qyF5jPjT%86p#NHwBedfF+#;FA5{u1<1a zxSSp#y|?k^VFf-M@S{I>gY6>y{sHhCZDsPT`Oo{VOA*ktY|fY3fC^^IWMN%h$^^Dh z34U!j?%Y#3$xoi5M@yRL)Gv-K$FWuvl`@OkpWKh182ONHa{}ZFpbZbh@g0;J65~l@ zIcFALf&Nco&Sr@^W_!=InvZ|l)=~T`0OR$V&ZoxOLBw7K+Z&yStZ+s4k+VIln6U#47%= zZ02SkVueN@#ol+u%8%Y|<-%Dp4Uy+d++Mx;kb%J8_vcSar__p@=a|VhvYkGJ;a&*U z=as*2_HtxSH6|DxoEVHq>|0$%WAm&6f4;zzxl;C29ghiZ=3BBgsp6G~Ufffc)YN1m zXZ0SNCLEl>K0?91m_bM`arukMZ8j$Beu-TAzsQ8XU5z)@__6)V6is`(Kv;iwVm?uk z@e#@#SdWqt@VRUTVqBG_C9XkjmQHh(Xw!iMl%3z@JfWLnlke3NyEFBsMA$y^2QoUU z-(yRXvcC+htp^YlZ5AeprKL*E*5ANJsuP(4O~P$<{{GNqGVgyp)@jwM80xk-qZ|4J z9o#+L-CG5hOo&PPU5GA*p1ayYabZ1k?QZid5VH+qSWP^6b6xX}$7;~ldBWv@u+_?E z%ji?i-bS1lTw=WQR`_^6WolZ_7|JiRh|t{r1$&4QY+~C-)vVS*eORY z{UU;iOj>kars#5Qar${QTo$>21B(#~EU)UjE>AyMhquMk+xjN$AqLO6zJ=})ElbW~ z74pI@;#pq2p*FUN&1)-Q%Q-mi1tk&*p{^sJy>Q39=k2adk2CLgn3N-~;Dv~2LV*#% z>{sROf@0VZ5gG*lJ%=+coYC(;Kd#w*f>9M6W+!cTXA>un4rL_$AEB~4QqH9Z-XQcx z+?2%;zeAu3@nGoN~piS!IEq$R7rDSO%v6rJS z*>jxBbfE+%O#05jSSQTSd(tS)deoC*f~i>N`2n}X?a9=%^Mv%GMSNXPStG7S;MY)K zQi7-=wIKF#rbj5@_1C%@r-E_l@3gP}(-lWFO;Kc2+U;?qH>R5o7wd#tPlt6hoE7tZ zs~1F)O;Xu%&#Jx}2%CNG9X_zWx?s&7o%G%~moGE91!p9etL--wlBPEGEQTEnfLiK# zD@1n6Ow7M3%(r;dSTYno4+Tai>p|x{AJ9*oL=-RR^xZ}zMvm8e^I~)4i>6gNTUQVz zE_i5b-6E7nA(AbvV5MJg}GgUn+ib4K#1S1jycqIIl^dJW|CCCiq}j)0nisr#o3NP+?u3Ok=EI-=nX&;ux$|0(2hF-L1WEbM}|9p?`af}LygH?=JF zkE!bjP(-V=ROwThVe7d~|7gP`wW;sT+sLo~B|vr~Ukvt4lJa-|PyyVNH{D}1+1h`X zT%o{wwTF`VOC!}(R+cBU>E-HUjx%}Q`|KoIGiigb(=X%M8ldjirik$wM3jDT-SZ_ltV0bi$I#@~k92 zTK{LC1eCt52S~6{eHPPgqsM6bmx(Y3lKHY1<>d1H(FD9IlCKW^J@$c2>Rq0vrtf}j zu0{;F1V~~@Ku;-XuwS!6j;#uV`K9KMDNWtH2dKpjFP!@sQFqr2)gMn__}?-FxF^Ys zS%>m)EyXxw6Q*&<-%OmpYhvo-qemO2o+;ZmAswh#1;SU!u979j_m(>f>N;JYi178n zHbt$!tS__NoL}Pd7Xxp2DN}5*o&1-#flD>uJa}tJH{D<1mROFl_+|edm3Xl;dZi7L zJ{&_SR{&S^Zh&Ve`AGcD@l`)R37qP7LGweUHl}NTGLYZw+I6u=t-D0%+LNvHh*ll1Q6Fw_d%KPiNEw@E+mXxTm@792n z#>@V-hT`=>@MK*IEY&1Hvz9x^R`vqk6AeS3!%lup+Kw7)ii0i-@y?5X#Q#;6{N(Qt z0;2M}2dTdhYAgl{$H91aW<`j-A)YPpK=V-A1xn&Wk;IbW^DN*(cdj5^u&$>;=k}}+ zg+uUL5wqvLiR!A*o8_Gm*!-x7uA7Kh%W=+4EWuW~DMRvVv(E0K6h)1=j<4`QeoeJA zTL4{q!Y-wKNVPGYCOc(W#y*pMY(5Qp)Q1&~|9+bIoxY;ZiQ|#N3R36Ec#aAOxi1}n zCvLS=9mtP3e=T_bxb*RI=V*gd1A$q(`tu*lsHjuIm_ngr1%V6~GBk78h&bxf{l%rh zNVGZ5rI%{rl|HpUD4@j^kVwLdz#GnR<0L^XCY2KRrLP)S+*JJVVLQTq_cQU^&UnL? z(|%~-S7$!b{!7LfA*bbe6w+QMn}bc5yE}6cumsg$kuP>S1g#3D-R#X(f)E5ewN&R) zLDvFL3}dml`*s@QHuVkn?l`B8e7V4X!3!;}YY2>B`x8u@q6;VCdP}Eti@n2Bc*?AZl zJAqzKKP{i1*(hIhpGcTX*kXPU@w1P=7>Y%4YS{L`#_fs^>HZPkm_8~i=bOHBx+98T zcDvpA+1G1sxV~x~wJ1w@wCtCO?L1#x)(T-Ds3e$a4}`Ex5s$wk{pOEzJ_Cv(0^I#H z8;}z{(}WFv!hg6W7_OH(Lz6BChdHf}h7`fa#d>Z4%P#;QjjwuTG3FB6BQhd^N9WLs zPR6C@jFJ%qoHu?&!r1G&(DopW9e(J&oEuT(w`!p#<~ejVqWnKA+|;{3%9jrGmjfIV z&q*V;)^f-f2w%5A-@Wb3UK0vgHeaLV2Nbv^`Mq6im7Y?*2DuUosxU zGG3W=XIV$=KSKCUy?d;3=^flvqU_URRzoTtCj;gm6n>KO#zbWG^grac?sHCbq|YA0 zt4(t(Kmgw?A&MlA+TcV^CtcA~xs8;2Sy;3w_-9TKhTC1jz_}VLFYjGtm;Y<_>?(Tp zZOR!;QL>_^OrM~4F)K3!R>&v?8Pgtfa};8=Q)2MSQEZdXpV7CWHzxGyi|7G3a+#OQ z!W-T*ut07?m$i35%rAhYl8HMQUgb2^saX4j>`fgQ#c<48)y~N#^GiGBhx7GW5k?$;EykG?a+?B}^VlV_? zdCE73zXzPa7jkMHZQs8f`5w5L-~d!dRA>kIdJa6vqS}7=UGJGlP^h?V=NkW;33}v%CjtTAy#qKXu#Bi@zxBao zoGk-&H=@*yPh7muff2d*^5M9A!EE1t{YjPG_u!?EK3}LPn}Lg-n~(8UvwG+4+(7X96-AHk*9aTnpUT9B?Kia#j! zwPMuKBzQT1MgO_&zh3-x9E`ekKz5*sed*&E)8V~es~_>Xkvf@?r~YY@kj=r#NZPj>!D1W zY~B+=(bBhpn|#^d+STQ$PmW6P!W*JHFdCkY*EHAKhoBl=eY*|tt*qiUqy||Is)PBqVbd#;Do0l|cOPqRJYd4xV#Cy!r&lS2VsEN{_3LQ11 zYRLo<)NFCgfqpUXTh?bQOj2^1jzy6nWgSkKPDNzFxr|f$r;ZviY0Ql&7oWuIewu`0 z^fvZ54lgK-d%OF=@4F}7F!No#mC=9k#?9hB@cvBeokZCT6yxDv+We{gs*a-)Kh?On zlKdA*PJa!~U<-KF%%Hsz-+n-;ZTFnfMTw9nMu)ON)NbH}ab^v+r28$q6(oJ3L%B|Z zhI1Z_+$`FwIJ!GO;=;F1H~aU2@P(ly-Cpn=WEynr~N8rX%O z4CA}FJg6cpnYQ-)Wm%&4GZa#6O(uk~eKM0Q>VMm{#!ooopSfO655hPTQfBuK^}+2k zxN*V_*i{CwR+9E&F%(ZSd@0&dnK_JL+Is`=Uv{;vk@&yH=^v7H+|vqE+{IZ|z7cjC zFHz=0*FDro_**fX2VQ;jfEn4a61JvW73m9V-p@URycNZY+qlEx*j^5}KW65g)oup0Z&!WXC|2#i}`3W71bfuN|N4Q|9Lc9*J+^ z@=%p4Uus9p&o=FoIjB}zWC|lhP~#DH%!e~`0m{@6oqnM>v>fG^iG*_Nfql5T%nY@s zx)k!I+D4$FplE}?_J81r<-G2CZ3NULKVH;R&v|zHX<4K1e->!K0i@v%smMM8{H{gS zS*utz`-LyYC;GSt($72+!4}As9eA{mK_$ui$K)P%?fBShbJt@w-Fjbg zHxY;Xms*DnC8eSmaqhTgsM0(d_RtcdXTz9h-rmpUsL8imWu$W3vSo~&>v_F4SzsqV zdrS)-*Xa5|stO=l%whK8x5wK4eF__X zlN#k(%6?{^=9d?Oh5<4S8TWKYJ zOJZ;0CRvo$)A<6$kg!iI|9t_WcE}&PD#oy(ICIhgpL8yL4Zr@Gr@=kV*@qrZ8xWpF zZcE6T*HkaPZd^Y26U|Haa#-9iQc@EUX;LXwXw+61;d{Uk6tnUI9K9v~ia;bZ^z!Yl zjCzYxcVj?@o>aAj4Z*qjqA_SGmSA2X11;NJn&zpq{620GOxUxp@|Eu^79uKK1D9m} z>{8~rH5zDdC=4D?>@ zvhWYhIemTxCX3|I+yw5BQD%5u@IQ(g8+>7W?fLml1^UTP)!&z?5B?W;0(o}Hum`B1 zx33ln->;(ak!)T=nKhBzPm8np9({gzpYE3|{q*xaUiYHfe=auLjH<09et8cVs>ia0 zPH9@F?yPB#l50hx?N|2=?gm#ya$tYiNATmn*h&4&|*MySbeVo$})tqp=h~UXoP)*rA&y00<(9W#P~y5ud$V2z36b;$8O&+JImEc)%=&H z?N;>OY)`A-|8$HXe(uqOXV*{VV0p&6uSW`+)DhQe*aj-S&rN&_&OQB0Am?lQ%hE+7 zX&^}l1+ORwMIser8201ptJ4@pR>MJeUQe#xW{Vwdx1y76vAw4+U9IhFNfty31%zeoWDA43%r@6d^8zDW8;BzXfnsD)fW zg;TYKS6P06+5!!-(ZP${Y*q?;Yl8>kweUt5&XM|OYU%l+5n(}}?fD>W%F~eyVREHq zC27L)xp4ynvWs{?qyn*H-gSGJmyeyuQu&|uG1lp?)^w0S{tSl)%MlXQ)62xT<#6uT zbF7ViL_ygyGrj$CEy9Dzi_5X1mqGibxCftjO%uXLnXPVMg?}g*e*PIo>A?cp*{Dj? z2MKD^tLrb-2)Oj3FZ`dYHK+j%1-DMMF1JU=PX}**eDwhGJ*D$KCdJwyFRra5_9cRE zdm*9}Lo};zonsqx+cenhDS))|oPezP_y0jGH@oXrss~5mCF-3F5M7_xRtTNAqCj1H z75!m*xmMhVaR4VTtw;fWG);g#@U&$g`yCP8G1o2t&z%mn3<=1>E%)Lym{WR}6Vu0t&d74yxY2 zUrNO94ir`O@hHG@Puo+Y3)MK%5eN0$!+zNXlhYoWZo`m(#(NwDT8r=~TiP>iLq-*&n+s(SmZt)FG=M-(IFOZ4)NoBm1e1BcBT zA!4R0r0@7%sbCzlfI-t7c`EmUU(>VY^hL#ka2BVNgXH2c|ELsc3P2I2@Ui^RP%||< z_7Q^|x23rLR@N)EA6oq~Hw)tho{N!3qoTSDeYPz{uHa89>KKpe)bN!0SV=AFDA9s} zI^aE#0PWWPbF<7Qqn}Meux3u^OGF{*-KQ^dgo3m(Bz)~KI{d)%Qg;%LY1z4u6 zGG&JOpZ)cYq_Ttkvd7~i-E-E089 zX}y5`QGG&5d8I*k*wJ@NNpW3Oce6`Sa*~>|!UdY1I>vk9sbg3b2|ZSo*ZenH+hV^b z>#@z9ffM^c^+77ZHoEJ*4(c(QodLrsm(?{2V`7vnyIkMu=7k*Jqe&lPHI8rnCUrdV zbtldz^%U1*vFt(P=r;>=9cdq`SvpXA!W|j^*e(z(9qZKhWEiTmQA96Dv%{MBMB+3E zKlMNUB)i5Io9^o${fZ@e_<=>KN%+$W(a{-&>JW>I^Nban#{sY{1{uUowuooxXAcgUgfI650ypC3$^o|4w$bf! z3MSlYpWE*9_{v4*Js$rSI`I^qk3aet{ct}MKyjG|KHfvD3=`@~wBd7Agp7_vyly*E z%25>CY>;Yv*6)n{q5UCbT;AdDO(jYF%1G&RZ)J9QDv;@QZPh&9;nxmVI)z_Q7NKR+ zah%_I8l~vi)>~wYKx5|Wy>zH$S7dQjnhI&honT}Ozu)Y8BGa=IE4)~{tNg(043qI~ z_viT{b%{YQT$g{os@kJ;QI&j+ffj@II zE3{Wi7oV{4GloCPpZ2ZF0C65SCiBzSqFnLnQsvK&9@e|hb^cNc^_dhf@THfEM{~ zw7ck&FT!|y2ZP%)4-wJ)TJ3>5&kIGGrptfNVk076$&nyj7|$+@2sCuBUL3BRv~Iy3 z(c=Wa^bM^1-^|MCfv6qMz7AN)c&SQBi0BccA%vzMwz;&R*|XC`=RG%7$*#gPwRY}) z35)PgV(}#E%I?Mi;h$R$7L!P}3pR*>_|u6Oq}Gw^57jLF$VvD$GaB^J!FPY3c8ff)n7{6E+L&)CT@V>5@gS9)%8q42?$l$vvY3ToVu&c~KrzslsdtJnMe zTaBC~4No)V`5x9MKdIasgw`%r*Uq`p1c!~swI`VnS_#Qdl2p?rX6!3*@+54}A3yRF zb@CFnj<8*^%kfN+P>J-x5(C#{klB{@VBti(iIwhJcc?wd8{KtqZ={!hQR~wcYv_7Gg2Xm~;7? zL0^!)-alic0uW=KA>mjmdOxFF`1l~e2`;(-?f$BLp9Q23zD&2KlkBm`b`IEJ@hi*w zS8OaMxMebI>WuP!K!11g?XMho86)n)3)upY!TqloO&u^=b4k+* zs{_LDW5`p%vDTNs5=yG^xp&!qBr?u(2(xee0sIRShHoe=_oKNRLMrK@Zhfn^=i6#I zp51k47a4h`n-eN3La%DZM~L46=3bA*4;gue?Cs{G7>o%9hmg3>bE7>}T(ufS7lEB8 zWz}d$J;J;+03nxw&v^d6y`hZ>TBpqcdVr(sf&8YH#pxS_R!oGAOJ3>_9HHgVs${F4 z{)^AFeQoS8{tG(nY1J1PaOd>*Gtr;taw^nr@ zd{pN2BcnzL+DZ{Xmj{)AO0OxE(%N)PUvD? zs9H#0BY5}yVoBY8Ls&g|nWb*zYv>UnrUT3iqcUZF9QS5+V`22GOi?WzWQ-?|xL5p1zAM8=xc7+~1yq<@i{`%wXBRJQ6?C!h^0TmHtnVNlzZ1~*i zTAX5sTT$xFyV#}LI4Fln61vW5LFm9Tq@u!}qb@R-W;PM0U}wB!Efjb>N5I>kt`uhX zirO2mSRiRD9=xPD+Ha-@*NF`6Az&VP6XGB14?Qe!?rh0t&NLolXtK$2VBz1o7m;np zL&efaY6TyW6%Mmywjrp1oFQ6tgJ*g_b|LJlUKAyd;Frs*`@z@CkP${(pG+06XLLPT zrv)uqe=XX2pEu~MtwEbl6#Pvf-cqf8dz5?sx)te6FDd-$CffAXJNma~>Ujst-IS=?%lYv97+~5GG{aaMl2y+D{czcV8IxZ9l7elTg5dm=F zzUx@H`kd47fP9Msp_y5Oa8%i9w5?A*UisIix97RxC);$l?D{(5O^$3(0n=Nd6ur7_ zuxZ83@eot73!Z-trQ3b&B&A(0o`>bu;l0`N>ND^_W&80B zVFGOJOXJa0Sz$@C))xWZhth7uHNjAx|H`Z>nUX9>Ztf*{mdhsI( zaMBT_%b@8g3}kvg@*e&4zZ1C_C#k3TBWqx^^?fd=KJCl3sf^np-rQx~DMwD;ZV@<0 zaN+=5L1Z+-z9itjO?>GSX#70nm*8*L=BMK$RK+tUFWZn#PZf)%X^ot{`kIarYabE{ZZ+ih>X4U;>} zb>QW$%Px!grvK-OcpO4>0qpah(y3vjrdFKU|MlavBT3_%(dJ_$3!R+xCw2Yzy>5_= zD>|Z|yJUQ>UnP89&UG}zHWgWj+>JBaGk>$4Cd7nyW`0ih{5{b*{)fa)6BL{Ldq0Zn zPjsG=*Du4XZ*W?hIP`Lm5m;|W3=KU3?p`#x*A8@>-VnEe#Odpv59X~ z{Q6#Cabmwfe3z_35jpVM6Z|&z@u+iCMeW}eUDAEwz+(SmFA(LSid{BSnBr#}BGArH zLb@LqB`Sz}%o;!a>qeNeWkEk#l)Og1vK7C%zqG$1B9j?=T2%U+&Z~OYNU4DXU=Lx=ZSIDXZvA!vmM*Ot1UevHlxR5W&t@XtEYp^v&Lta32x(8?Y{nl zSIP=@etgqt#^=Nay=w zh`v{<{DH<`^hgrr7j#W*fzXEuaeAQ%49A!3cS=d|Vgo!RBBw+(M_X4|Gug(%*Tjf& zX7sWErNO?-{d?oI?DfX=di6D9yVkgxs7e*Q=sx5t7mrBRc+1>tTNRvjes499?BwCn-dh3I zfS)TT@n5#dYyOtNNI;+WZ6MUT9Nz?>qr=^ zvyHvK34Ds-qaxRzfD+4C2Ph=!+RQf%Z3v|s3VRT(tlWsyGRh;uy z&pWy35{OwkwFpl>rk=s1Z}uxO3e3B)>*peqP08u_>UjIB%hJU=0n z^>DyDcrzMXR%_0bhMH zXp7D#DI%9ky~ES7s^G91!OnGii~KMojaIhDW<4y94Z*tSEBSPhmhw0jJZo8X=Sb2f z@5oz)({+q~XyFLhcMiIVf%XZEcXAf!N6@G=a2R?#^)`Ti%lunTeaZT^?I6!l3uk#R z+lO6pCy5-}(At|K*s9hO(Ky~!JsjSLzYliCgff-LR==UfUKAPH7rb9xtq^dQVnq}H zofFZq52Ze9vu?@XQl#x`)N?Gyw?5!^U`D^i<@G+5Hu4L5lbu^hlJRN3XVwbC1eYML zGM_(`!!uTR)hL%c-VBs2qDz#|9w2SMorOXtLi|&=^5exzCr>vs3v)rc3fI&)V_^+J zlzkHHZvt5{b_>mcd+>m~Tf9h21?)WPdY9@=BZR}7}=7Af76QZ0_M4o2|`!@`o@;Pf<9n_BR8IFT&_bx@8@t##H zHpaL3wk+78NQlpG3JW8QA_`1xQoV~_pN}4!+R4`Q+rRdK2AO|r-y*D_Op8e+*j!Zb z!3IYzhdaQkwwxD|y;kzO7l!Ry;63gN(^+B6wWCRAOr@ZJS+mBi4*#5A(NOqR_OZx$ z{|)8gRQ!to%$}thH=r#0A2;pyY_}n96Zq)W+(HuMtnowby-vyN=|o3zT1<}nrp_QZ zxYfZ`D;Av)KYF(ZA6e>;qa6?}F@@hriQApsJveD%Om(3Wzj*QVHE9bBi9?_9zl zhCO>e>%cHzgH1JbGG1QzS_rMhG=^AHN> zI;C>Ni-Nw2y^*LN4&bG(CNSz(GxDGE{0wD$N+y9tK`StW9Lb8!W@}g>-#s1TnZVtX zOawLp`%ZOmXK6RTjz|WY{6X$uoW!~i=V?NlOIX5bFvfIp5~6cP={*1I>hIbvmo;fH z5)^#woh_2fL;WRNYd+27U0jlUMuIZXXI?Dy>EYPzn0Vr zLXLO*UU=z1^CTeu;JsXV({rvZP-;@L*xQc0XnZ)=m3n63J8`)e-r9CGwA4$K*{eg4 z-M92pA6=4keZGN~O3HS@$fW!12HK~3nsvwvLu2SBDUxix>iVtR z?BCv$!d+10$pYOSs?hqoue68NEEc}~H$|%99k8jPRV-f|z>^~f-cJ^O*1#GxNVv); z=$;P#s+20f)Ju2dzV-KRd4-&s>bU6Y2EZd~@fM*zxRLeQ-;zR%UZx{WPIXMtBYbL9 zpprK^bh4wtH?(m$;rk%)8_Pgef#qRMNt3JSAt737NuSST|I+m6WY%a^-jek`t98}- zCq~)T+XZlHT_erec)f8G2Dw8@3Upw^oUyc%FQ{B*zBObuqUsHv@Ou6%zOrt4z_{cI zJRT^kzrFsWWTS4Fc%A=@&#U4R=z?2dZTahc9C#%G9(Za#J&Oc#A=flSFtC;9KQTtg6sOenY z{C)De6+?p0jimV_C@AV|_G?qqK*~JeX&EX15U@+)VNe-V;K2$+faFxN)t|)#%Z8cRW;oS{rb5n zVIQm%h;kwNH-FKVjpy8t+j~=y3%gChM2?$A*+r3fUoDr7@1`|sv6|tmtN#Y$DHs-h zjSxApNOY&#!Q1}IY35ob-oB&)^zglO>Z)KoWwHPzhrmOEiM2%s0or~=byL=I6woN~ z-|uf4@5$PK-nn~E>6xE12}MHg4!a3! zMUsM2i-o}XU4v$~uPvaRI7GhfhP$VfY1aUCP_A477zM-$4P|$N)w7+)3X0gL^s1?PlXN-VH&K{{gF-ZnDnv{ zh}6B6EM*LWK0xdv_MC>hFGQt3gv~au1gwAr^@@LH;DynNpg*>763p-YRwAgfSceYS zfbW0pg631Q)y8=8UPJVsur2^mhfh^L6F)BWMO+z9mmsS^Vsxumu*zH_n*0 zO_Ej^y<7MZL>j1oPKiaWURuwJJlS$-x(^A*?@sX%=Fs)HeU#n~tX&iTyDm?vJMBN; zCp|ivbo}(*tr`cGP_F5j1|Gi436`zf_uCHz>D40O#RFy&r?fp`zDcrJRm#xr+GvR3 zDk*E2sKt4h2>Re*kYyHv9-HTl{lb30US_5&dq zYmm1tfa!2vd8es$=NrBC|Ii18VSt3P~b<**YAKpzpK=0o|ijY0A|BRY; zM}F`!M~y)ApEoODV$iUy+03b1PB9zx?>I2nVydV5CT~+7gZ%Roj za+~mY`MlF`NqBJAeemXAO__!$HH)0t^kHJFL}CB`fM!Zlrc%#eHc>e;S7RB*s7(9# zF^TwAJ7sCK1GdiPIb}Z?InZ=z6UGr#G(r8&Wvwv~zt4-G2f&(}*(Yi7R)-_)pE9F$ zPv>@*=Q*K1%V!ou!$}w5o5TAJK-ZHpNhbsebn+uId zFP#M9lk0{({~jSn=IGbQt^xePM7yfOi?46WY{I?{WjZ&D<7P~85Y#`IE;BsAbq1w? z^&hGra&yR#4<^e#950Wmu>gs{=7hfdMfqRb74<2`9oY_;Q5gRzs<^}MK4Bb0!Fq@R~t?OtcHpCz35x^TH>$Y>4F=~Rn`W!r+-=pfi{UFtMX^(ibH zfYwpoYYuT1_`y2psf%I^rMgX4Hn3gl`Bm*viv@KF6zu-R$BwP!>Qh&>o!Jo)ZWYk+ z3GYvB7zrn`BBq$+Hp+lzRtF6A+$lJpyXU2-n_H#iuzmMgwv=`x_l^xJ-ilB?9V+%L zNBlT&dRLPY2hmtOi4~tzhsvmVDF3C$U3Z>`q@2)%^?g|VHezM5YC&i{Zc*7O?jzi|t=fdxti-Yg;1Z|i6v(mwmgk->{uniVWQBu;BoH2$IGiu`*4*YWG%G?!36 z`<9)f)~V>iq4U?*5bP-euhj&;PDL(Q=g>!2Dq~~;w<=F7%#IqE9w*CU8&zwYkKJNT z%>#E0yyCk?CscEYjQLe`!E~>^hpn1*PtxD_oIyg4wE>C*w=RZ?~sde+@n z8G(tps=6nyn~X#pSAL&qy1pdIxvSfi>fP}&HSL!kH-T)TQ*ZtgHvSCS^N!5(ca?(2 zn0j@VvWWy;7v)O}!ktKWL+ zknY_!{4hm`rtVSf1Qkpt`tO!K<0&~J1XA5}Xdhtt5p1CkJ^*<2n`m<&UOmhnUhvY& zzzx+R>q`>hlLI+fG!}UCufmh4LNXMs> z1)i16&tJDnMKnn^5GH#RY;@#zK9=PMY^aGApN~oMvj)~lJou0|iTy2a+w>#YbD1l4 z&&cB!ARVPB)Uqk2v47QeH~HHnlnNf%AOh)6Mtg_S|62{lnLW0DjiULp{%HpAsFJl1 z?2=wYfsi~xCqoXIg=}2xSQe}rL#X99fdahfraqp|AiF+(KiZ(#Tj|dP>w<_DTtxxh zlr}jK>ot%V^|o<2Ah9^D*2FtkzI69j#phCz)U&+P_he7vY^W2ots$MNGuMruoerWn zOU(nm&kph{QA?w$H!@sqr2~0HFTNR4Et-{?XrXJ>xpNviB6_76Q48#|tC7Os7I@#{VV62fW3>=eXX8LeU- z4*v1$zdH37ei<^38E`I-)v6hVZ(k)C{Zg#jtK}PZ#}`I-vOF6;SYm(T%_1I;QRp)W zMwprbJTNMs;^S^2z-WXrgaz(b2p%iU@mc%`{(g`|zhh7XExinu0NKkCcPydrJS1I;`1^mDI9CUT?EtzwzwZ_MIMANIf%v%m3_|=B$1a4*izj^NQ&JlDBbQeT znCvKFQX|F&b%Fou-OIX_QU0B;KwC|A&Sm1shkuxV zwxkH(WzDxv7@QD~X^(Cz5K`{pn<`UA7a_k#+}$#J7ArZA6EK>$#x z@+aQlRKHkSZb0|70m{js-R|rEv>TMHBHs;K$;-O_PR~~UtN+CEi+5YYaiYgc{b^Jf zyk;Oi0lHbT3(k`7kQUZ=o2nLJg6kk2lSa5$rH#a`_mvCPeZ zJ94kA%8T7&y%#<;f)s=s9IlVms5_Wh$|`>r5>w94HfhbGw=jMs|Lc7K@)cuQVnE~O z@;W8lhWOJVD1d6lU`Sh7w|N)hQ<(dinQ9%U`~O`4XKN+K*PQA?GW)@zWzPfPaiYE! z!d}Dm;SY`KfwnI#|8?L4F;6qpXN9}~Y7Mq38RZvqiJZU{MR4pQ)l@`Cm<*pHR*~xu zOB<(+@wtxY5R$7>i@8;W-I6W$xy%KGD^BQ=7Q9?@TmKM6ogWjiZ?suBobtKz$hg(s z?lP?$W}|3`iNJ5GeCAf1tp=%JSrYF7%x=4TO!?Y3wMG{|Z&AI1zlR2#MWLn>K^ZSt z)NDJfF}<9XHAm(Hs~QIYgQ#NS&Irh>tIG~OK>sY6b(#7-F&WmyRtS`+x#08#X-Jrh zYCBZiqQx52R;Yi^`Co}UM6Di`A)XC)BnmXHn|>ehG-GujzyPI zNB$=@t7`{^q@;=X$ib+4ruZ|xx~_lYo(}8&c&_aQ>I^578Q_prp@_JNKkmX8-c}C8 z44MJj_wrY`AdtuIR^#dp@{s~rl^@?~WLtR2L@ZwpXiyD{FX-)Yb?dHJ{y-=tayW;A zbWDm9i7+k&pZf56roPF*{~QhD^_?}(gtp>es z1X|tSaVx*wS??SrARPAsZYWDY*(~H^Yxu~H4k8&Q4gnxX^`M1qGsu_cYN4UP$7vrxms2u%7_K|XsUysVhTiQFGEwPv+ zMJ%FR>ev9=*a%Lo6p&s#CHI(~2!1`nrz|*U>YwdJ3p8Z^`%is2$mZ%XBLITqV9@VjqP670MCrq3>s z2uccfH3J^e>CAU>80Wq5X~(og`~tdcl_%|DKt#j)D|5ZWq$bANdKVOM#+*G`ULXHr z-JSgHSB5RxvMuafFKOed?0hsdv|!wXpkHMz^KqbOV)FGRCxKcw0$XN!)8jF%*;Ch* zh0^@;j$MLnCo3>z&QLNoO}z3I`_%LClD%q+4RZ;W7M0W@JX)|@eHpu^2g{4kDAKmZ zb%l#Enm3gqk4qsYTcas1)-Ah%SN{g`PYn)XJQ()F;>urRR9wubz-ca-ZaHo;*+>L@_rn<={Y5M?1abKRDy0 zC$rB(OkaZ+eACdRzvc(DRiy!&=8Jar6UcqwpM#kl|tYRG@}y|ID^4GDy841pdOBdZZRbjL@V9(v@f*)cmp9kH+CC*rR?;DChrLpMOsm_sW(cv zPcWMS=uSY{X$ZTJj`T6Dm7}$dylTl=s+)7I1IW4H;v1T5#k(_DQRP~>%9-s*+3>e? zuagfb5<}$5GorU`+{z)?bDYXoKU@vrj4t7a-I9nx6 z?lCxcI<9qhAG+}r-xeCfce=z&AmiXCc|se3k(VrJ+`|}91SU+M~)tX#QIxZ?Xin^PmFf_ z?{fs62b@Rj=C9WG6Sf6Pf2(^u`OEWKJy)gR_e5UPkX%}jZhT3?I+=S(EQI;>x_*3? zhdJlmL4~Qi)?J<#VKbJBw8OAzqv|>OwFCOFkllsPgPAt-2+M7~zT!_M>pU(+pM1Vg z$eELO@H_y!zq7m67a@Jqah>#``99*Ai^w<`QA`Nne2 zMCtImBIv{v=8)DfSxzLE%Bzj1^=?jisLtPS-xBtn(pqIiNOxM0TeN~c?Zz**G-v~c zB~Hl+Vt(u=kXTLVn1Bj*s}dRIBeNW;g)~}usb?{r?ZGe;_=sM6<6_+5U5WJw%1wg} z82b%i7hSbp>jjylVfM{#^-@-;w--v#JJYJsRwBe36-PzhHq>fAap)BCWtLC}y(Ljl zcAT$Pa`147l}Iwu4UmW2QpY9SLxw2Y?*cYf_5cV#pgiGZpI-alxGW)Alq|U5SjY=r(_d=u({SKWk2T`v?7S~^%MjZB3JMF9PkxQL;CJH|3$YunoESRsTUZa$KF9@l)^Snc zT#{AL=9~gPASUR6FMzu7cBJ6INV6W5%2_nT`yD+)uBSKfuW z`|>>VsTzP7&#d(RcXw*+VYu64cPaqIpG$nM4}E<1H|49;@7;RKAzAe3*fnMM5jQqoc?z(TQ(Ut$X zH4>1b?ZZOW7dc_$&Be^D*APESr?Xs(|F)O+cKgrB>ORx$=T^p2_G9q#0&ZD1wz5ma z4F*1F9#{_#O}JtUqOO=>-L(Ci%Z>qmUWd)Y{JpcG8HZ;nB0c}ZsuMS&Y}`C()1$R< z-bS>Bui7O_MAn50{}&)J0j#*_z1VH5kCEtMt`DYSV5(S!FIXGcG!4&~I{q3?VnTF{ z1RJV@F~7W5}g5l5Qp--J^4K*T^vz z-~7Gr`~9;&$oqNj=Q;N|=eo{y#p6896+5W?oE9S1I=M=VKsK9+=LzAnQQX6IQ&z!7 zvTIh~`o3JJD2s+|L`kwm-#u&Dx~d3mON5Ud&QnDVp5BOuE>09Zer3**?f-d7Ba+?$ zxJ@;%$OXNu!|EfcK46WBrcehT;&Cz^K$$@y2bN@I_&LC11>R8p8PU%QEc;|8ev z*5M^uyIBrqE5W*o$0ah zTd*3~ZMZ=4{8n*+h?h;rPG5L;QTlwv~8 zhEVo?5+$XAcSf_bOK4rG5)-^Z2s(pT#~aYuFnnUvccY8Iw0v=C(ayh&DFa!nU!AKumx}ZoDG0g~m*^lqI zT1wD8^6z9eXn5tWm7r6a)sz@o>o3I8nqWIzKtx4DpRoLAR5$iZ4a~p3SAo82`U-!U zg<;GMufqW^;?xU5OGpHbQ+;+(2q5w(Iq{{h^47^nK*rJ~Au$>?wP&B3t8rgqI&c)2 zwd~|dI0G}1*}RW&b_f`S-!)cMV3)7F9cX#9q9ru70lX+5fR?IHLx<0+WY?>kuz>hw z$Z;(wJ9T|cnz5vD+%QD2{;-gvYWbbdaIw=mwF@A4B_~3ZQ$l}sZ*+9uUj*ZfE8hd8Qju7(aXq)dZPJ=ruo-7=Pv^kORFm!aVk1T`dUbRdYu0&TGD1@WH4&DVJ7;4e!P0vM~?su(bpcnqSlCdd5mI`1a}K4{DRsZrTK_(QeWc1bv2qYgOa{wCbA1{}oE4!Y&Df+SkJVsf&<|Xe^T(j; zwrRZi!QfE&HdYu=Z@u2@bkbmdL*n}di8RFwA`Zp>VSr1oVrsAgsSStbKWVbW?xCK- zxO}`m4!}N(b1V}kEpum%^E1OAc<~qDS^Wad?+y!FjCIsIVr9!ijPJLb?cCg3+`}Xc zq_tW>-}5>?LJ*DNN|Bb=(>rR7U+PaXQs}uKF3#8i#x43|6B6zB6?^Do4;^bz;+`R* zfF^ijjM8oHCxh}j0)Nhw@i7wMdgF&8;u|;an9o{>nA-;go8M{Dk-}})4FtQMCXi#^ zemdTHH8+Ao?PU3mRExse5kf0K?@{;$srR2i*LG1`H|HM+(j+8LZ#RKRuZ#4{H^zju z$a>N661aAqHRXfcbd5$-bYUGFE7s7~?&Ru0S>n-{UUR3-F`IxaqR6&@f?G-92u4YJ zvdax6f*;Ax4MxauVEIhOR(=Xg?!E0F2SqpzoS*9{Uq0>g*p56x;043KrQvolx(OiX zEB$GZGgiMIw0of&OVqX}htH|SA~dd>ZJ^v6LUU8j*Ue|XE))1nLRjF*s~(z5P2`AT z-ib)7rim3E+GR4D{v}|vtuz%uQFl9USY(hM5J^c%)9@_U!#;wpd6_wYDR(2pY&+;} zYaHd02+E(-;;&@y_G?p&3qWp7mKmiov8lx)-NJ|>ttD7j$iMQBcEkANT1X({CL_n> zxFwLiN^)DYB4cmXxWwHj z-eh;HK~WwtBCy+K66Lplm`45+kH?Lz9j+}53JivE3EEtWuq-tS5a`Uy*JsJhk~tJF^}8ejTJ*@OZxay=i~c2|gLZBN$W zGWH|R36U1=J&Ww6C=x)LF`p-=-$l!dsK2+X-KEi(hB4nPoCi8zm(>2-{Jjg;MSM`i+eQ3b8m_<-(Byuc zbW`|TesYIX;2nu**zT0VA#or%_Cpigrf54)xvx9VVDzmKubyB+#odNr5Do$#I^4)3 z^3s8f-Mdfm%`=QjouIf5SY2!97-4|bLD{1xb}ELUv0oaWM+>;LVg`(gHwZg1v0-b7 zf2*^{ouKdJ&{0W{5W0|G#Ffm$@}f%T!OquvoeMZk$EVYdNXC*xosB+O3#J}8b~EAI zNE+h7sDDGU2uXkJ8y=6y3=#^jqPNRW_$Y^8H1j+nC|*e?=@da+slcUOCi-5DHLIR) z+t|JOepcgYZCHWNFO>0wWq}mOgQK)_RcO!VjL`qVZP^Q1k_)XT{BT`E92XU`LIeMA ziMXkM>zp{}7kc58dAg23!$ozY7B6;+u@p&F)dx$2i zMm_BPbsvZSN#JwMmkNuxTwx8pBOzWVBc_Gj3)h`#8gjAOF94l-=j)s#hmbS{@iP$P ztWwUxz0XF^rI0+l$#>V^h^!$ z{ltx}(GTTov3>#K-=P5#tQO;w2N_LR-TzL!Mcjhf!O;R;ZqQP3xYe`D$(;{*ZSQ2^ z`>5TQ23=9EjYdWy6f9zEJuOZYfeDm+5T_ucN5=3sjE8*vf1fVNWNk}}if4uSZjEs0 z@1*J?CA3PqJC6S(fKce7Recu{ilqx8P5(xad>-An=y%`qrXMiR0^gO%>J=2|ssY0D`B?B238=x+p zBH#C&mra=A{&{6WQmb1omn&|Trk00=$b;YB7~+wrB`-bXSV=H{i(z}h!U0ltm~?&u7Gk?7F$&d1s_34L zpooaisu#n|W9Wh{&Y8cC8<{Gvm>y*W62-hoCDzW`1u%F zY(Aky95lCCK(x`2Z^$smTc*8@ z^n`SSKUyJ3W!vYHl6xHhG1uOK=&z$eev;c?9?J2fgU#@NWOQc@hTbQtMkFzT#I?I) zQ__sI+Fj2E${f2#`r7|Krl2=uvAe^LX^d1KqKQbhQQnM@lxP0cGn^ zjiTZQhrP+<3%{YG?nBaP_Oc;#7or11nV^RMxkfJbiK~iO*;w1_JWSa@MiZv&|7WH} zdk8r(^=X)dIaK{aL)NNHCzku*vnot=QSiLD#q?bok8A&1S$xEBJoL2L)BW(rmg=XE zlJuc7?6Zxh#K6R{03JFUiqyddTAXyKDrMufx%O_t9|iD6@uu?51O5w}f? z7&H0REDum8k+m0zZ9z7lfb6Yyeyt!tPXzq|K3wV zN_P}kfXNfhqbb2lD%xOQq%O2QkSxh3@a2Twbc+RZ>I>bV_tB0H^U26=2vzEzeLu69)fc0#ws{;c~t9x&Y6BcWuU1 z?1A!*BhVn&(*qcy3l%B-5LuiYBVDnoSzAbEhCPipkE4ti9nKD+)ethB5RQdaw~{+8 zxQFsI9H;KA>(+f@ABz1wCP2liTylTDvj-5Q8ouOS_(M|L>q~q|h+5_*ox3^j2U(Yi zFiM`q#Xt4CZFffKRVcmI&+dSwY6w$#$ASl{$1AmtnCA&ycfBrQXWOVKiSkcuQkjm? z4_VK_LWH=RN(q!)h~!d;jab1 z?JNhVh&s3_9Tu|x`XCtVN&?-jDcZT(%Ba@l(JfVgUI1~W09>lOBS97P;y>-5get2RLgph}xu%zn4cf$KW7bQo0oeT%Y6g-YT!mm*`eZ)Y~GQ{{CTy z&k&?z1JCxIU-xHt7eTq83ecn0dU2`CeMgJkST2jM;<$qoM3(n;Zj8yPpejh}T<2-a z1PnNV)4}|13KgHlFBd}vemZ0CPk;#j$MU+~7OGx2>QelV(5XP;eBD{*Xcp3hiFxc& zfGJ?gswf5>!Sa6ZIvLARc+MwtxEG1X6_7MKn%xlxvTtTTd%*T)fps8fh3vtN)gA>K z<5)fBcj|#zF4G$le@^&OE6k{i5n99LwFk7oEbQCB7m-aT)GQ2s4ySXv4R`Qt^B2f0 zzx0epUL-R32&BgJ3ip35zcl|`e(mH*_q8X1WMd)Fx9YTD$k}Jt zO#BDy1pI`SS^){ri^Y0r@X*^JD=_c-U+8kFIthV&k9*1Mw#Pg0{y^00@0hRH@dY9c z1#7SSfxDkjfEaaJ4|d_0pQ^GkR@i1!gbz11VXMA}2=2_=D0K2i~PR$q^-4~F`~O_5=(w4$73&u-l)UK|zs-vSi!o0_eh=(U z9_0~K(NwFH}5lo&gGWd8_Lj}nOTA@EHaIsaIF2;NeTR~sndT~q6mK+ zv9dLjp4<{skWiBKrYEVqL7zcLl7-X1cxj<9c#xB-F=kzopjxwe3pdo4q|H7Y2)9{eBRK2?q&2I|nG3yrC95a; zG&#)n&w@rzYsk?^dT-3RRTux^!ExTm#qrpU`9o5Wkj&%TgXlo<+tYCY7G8!k$=zwE z$n~Mgb?1GQBRpu0Gxv}oiHlCMu{+&92{WQ1MY&_bXKl=x%V*e}TNJqnczGx3NdHel z$-$hRtu6jw2PPL@116|x?og;?$<2Sof2l6RX%#4|xGs9@&SlDZX5LE6n^J2R`*JbD zNtjWnd>X04?p!Mu`XM*?q1r%)aeeF(_!>f(O`+}2Bj~Xd*m`02>Oc}*F`OupMS?n5 ze$m!Zp_Z3I)9!A`MH8-~@i2mc*}}rzE)JnsYRw-S`U+}6;0?N&DNPF!Kk1blNTY|L~U(tDu+h&p6%+C2z7bn zbs=&ie`8sUqG=pohLZ_Do2Cwz!DppMgS)XJkrJIP?vbzcTBHYEddtA zP41}i1`@ueKaU{E#%gRqCkI}7SwIhuE)YlgSjYLE@Y$J$xilNHG0m*e#_Y+^cK2My z<)SHv9HZ4ByICF}bt2hM&YwXfV?`b09;swL-h&{!?c6|cdmxpn&%;Yqh9T!SoUjX2 z%dCda-;^IZUact|^j{p48vDHd#$!i;VouIKBC{pBNfLfvKSG>5ewIEO!G#H0`joc0 zlB4!r9&VUcBr|(PnliT1O=DE9YB5t7Gd|IG8KfLhcZckbwQ?;Ltvy!@L(a%({Wi#R zD%EMPOte7wsN!A6M1dM`%t`ggB0SSRJZC0msLuSu--F#$wG@$|lh@Go-#Ku7TV7J3 z%BQkyd*!PV*x-6$J0`Qr@Dz8h&nY`p_YFIKNZP%eWBv=>n&3$DToUu4GF^k~9lFxZ zbLN(;MS`TAcd}!y>r!w}{I)LwaLb-Tk1axiJQeYTSx4i6XaoOQ$LpiJZ8_UFcA<8w zF^|8|7>*D`GzkoyNK{f-RVxOc8n4r!(0euVm^;v6lWrhrb8!IL86o)+Pz2(8(%-00 z_2RF9VE4#I*Wo%omJ;aZhF(o*Yf!;~TX$Q}_8mv^dzd{3uiX27aioWzZd7=QIgdR^ zYW3rvZFGskUBuzMm|Ct6Jtb3uPrhtNv7!r)Nz(Oz0%}*kg}vX}LkYZkcMp*7(&aqj z`$ea~&hMLn2Y0TI4DeK1SFr1yiYchs4lDxN-_jfXt>GXJHdCnztx_+CW`}Q01ejo; zYP%VPN@>T|^%R^dUMm~>D9#wWrz+ajTJ2DN?7k*dC7RS6{b7T17c=bSSnqnKL(X8z zCQlSdKPeqw^P364jI*OC?%_NKd>*9A18W*Xy%OE+~b$Y%GXRyN6kn)B7aZ5)ROdxtc2} z@dHZF;uven-REi&l;?9iFV6@x@eYdRBPbq7Bw11^fr`fck~0>`4IoEonBiN6mVbA` zspaoY{Cr(`Q>)!{)TY^lt(gu9vCwv9viP3mAgFL%dP@xSmh z+B=KyLM21QzKuo^-2HCsRSIG@26UfMKFTj&Y|3NxBPoy$Waci7I0cHlrNpt3jjGt7 zVfN$sH7-M*$@f_#%%7CIRxBlXU7k^G)O8_h+_m(oju&g3cB-5TzcFQhB9-zmPkP-= z)mNv2&qV~xRWG;dl#Ck%QjIF?v*h{kMLb;RT+s9On(N)JzyXSdJLbnTW?552ZN~-! z%Zt=(O^ck~TTWM~>`j+l%6;(mP%?W=3?YBiVlwCRemoEW`Iv{H_4)R*e7Q5Q0dl$M zD*UxoX2-#iLHw+Dq5>voIY8S^^Rq>bwA>1sw?+qc6EO4|dsh?+f9568k3XdwR0@66 zx)*f^2+jI=_TdrB(+_G-heJ!^rTB@IN7RP-)Dc{1~ zcB;;Suk`&M?PPdzs96S**1m$Mi?2tutP^cSkuqN0O>GF;&4#)tdZW)3>A+r<5?PlP zAz~i9A73CWGe%ymDew8sdvx{v4Byi-vp$~Ew-21}-{8R_qfktOrh{*a@Um2(?ApuZ zF_zGaDTa#?g8cljKDE9W&V(+$-Rn?rx&Vq5Ald{~2)!S9%OUmhBA6!iB{!tr{O^3T09G5JjVNZ@i)=+TFr8imAx1pev0 z4;zs;Ek>i>6kAMA2`CDJb&$~(;AyKmzTMXIDsmQVYQ?hnws0;Y`6n@HSJLA1`qcdZpqU%po1rwCzQFOMWKijJ>Wb9M72lOrTeOC`b&oTBi_ zIs*q2%|}Kemd7CyTEt*?qhFtcI-?jNhvuUDL9}**#B)iw?8CVt%O*_!!1#_?s?8v2 z@Wqegsr7gLufC&>2@VXPH1y?O#-q0CQG&@HOSH5r02-HXi-62qTcqdvzBG9m<)0mB zPcS&+sX&{Hzul8*-2C2I$7s{g-nT;5=Sh$!*4H8Vl`dE?qFmZ8cS6qJpe|tv_P75= zMouVYu;*u|;05!O5|B%6evF#aQ%o}yYgp17bZL_CIiAxpcfYcSGdR+}7l}Xaq$&tx zhA8l*!2{?%$MUtHe9u~8*Ak+X?CizWjSyq>5`v)9*jj6_tPe$Q9USYQW(3F!-JTi( zUGXKHxgs7y)$s3HRFj8c(%1u6KM;@thtY?Qsp1pN29;eAj*-*j9fI)QsfkwkOQ&QJ zou+-bQkPEAsJIWY#`_V;b7haWdvJ^Ubk}S4h4~wh)nv6?W4TH>dWZgd5O%DX+iy~k z`&cD*hXSNRzRCLnhl){CCD=R^SoJJU0`G=m-Mzr!*My-yo*}Od+BYSx!M^tuKZ%Ss z;m~{InQ#2o{nfMXzC2}J7s(5~nxj#}QtXjZSU9)9XXGndG?iX`JwOtajZ-(x4_<`f z*Ae(45O@2q?i3IJyMzT@t62PCxc*m!YOm{$FTxsmR5Ao)fpiiD_-hajyhE^+VB__= zw!Tvzwdwvhvwerbdq(`EHw)5J!hjc~#kG?FwavaJwd4}v-^t4F#%if*FnwmjUCG{* z>7zP~y_rjmgTY*^lf~9HoT{cz$NCT+J~vEn4r4C~Q&-0ewS3QKx@x~GW#z0O2ol9=GD4~ zeShdqnBYi_&0unq7Fsz&V3F}1ox7C*6E@=|J-P* z)2AW3dSyN!u9T`5%9M_4D%|`VPOaj|r%X^8cJbeM@w{q@qb7X^mFXVVE(-guJjBCp zrVMFZaAZNc*XyEO7^4m9lZXEGk9)7IUZclB9EhNWD1~rt{c$z?c>(T*ggWpG4yA^B zwMyT%{VZr_+${GRh_TjdWHU8R^0`&*cx{qk7_Tb+NwDwzEsaEOX(U1qSGyYcM)3I$ z((X8^RjK$b_J^P8t*rUR0lta(@l=i-%d zbCOaWR2MMhwB9RF)eTs15 zMYY`F*hS?KKY6v13Bqqn!J$(Kp=~NY{sZIzV0a-Mcty3~#*aQCMQF0g5rOhZfF3Uh z&Io0CoSD&>M;Hn;{h#jl_21W;pFcY8#uFlazNZj(Lj)zE*|RKIjH}LA%3NIB$rQWc zb2dGZL8W-1IYS{1Iry1@h}zsEB1305!Dr5b)gYSdxBH#$6NUy{b?}o6X}D|r>E*87 zh_+i^oD16Zy)p+b0eZD1q{iOxvp1wIhZ=u`U%MP9Xc;p_9z3CrnhQ1z$o`O=$ie%+;DMX1t5 zFm5aoH;bf@rn}0>DRGI#Ro>p~JHj7enQ8fJj#&^O5%*v4(@s0fZ7SQhomtC7jjiad zRVBVD^Y5a2Fa>!m!Qio`y@Se*dPYSQks4ppY5iP4^5c0%4pqe7`4|N@$%NoEJa%vb zoy2Vis>}v{MFmkqj9hvdZGW2u>I}KJ6ASU$%Xk#-9=-g;tcrhgBf6;09DpVKvD_K~ z6`aEW@~9>UBX$~>Al!wO8^WvJp-mBhFIkbWPnExBOspcQuO;^}J*9uYhvdvgPiK0MQ%L}956rCf(rf!;SR((hp3B+7Z%GsY~&E32O^y zTSHU6GW_jf$+i2M%&djtY+71IfB2`ztnbZwf3XxazQ#~wLJ7vpIb8{3J63x0vZ8c9a|tR1=Brn`<5eVU4WUNl+XTG z{aGfojl<>!L$JilNCufBp8c~0_WFXjP&$T4H^avDH2?WK!0V8QzB#YYo~B)CoEp@8 z`QWjMOL0gw6_VAA6Ggh_<12Gy(=RUMUo`rY84*ALW`bAXCbtWACm#HK!?-XMjwQI%k>| zH0~A(TK`ggby1*o{4+xO1Pw*%qlV{$Ekc+$zOlX&G+Sbd{?45(XGcQG^b9bn5#Hg} zFQ@#UUv&g*P57nwh}JfJLetWiVdaduFCw3mpT(Jq%~Fh6j!D3r)Hg4VN6o zrM#vn;lpuH^ZYtgYF-av4+Bq)yKSnX#!^s_Owtd}oC(VoRL&?=yZj!Y_GK zs!V%f){3kro~gh-Cz-Zj96Z#uG_MwVf>yW+(z7qJ#7*EpmUWY4nol&J!*Dod`I4a& zQ6LadB*Nz{GeE@#A!X;0s1CI8zkB^q8db{!T{oTo1BR+eA{z?63sMk*-O5gB^@yT8 z6U5EKb7g)fFM;4fG;J}rOK@&Zi)h1{J5CJsv$7Hap976%VqWByt_Bx?W`D!#1IfpsBPLAajjR~R?ypG*Qt(i(`-1wnh_-+$VTR$*g0H3`NPlyBJs~3GQUFQ zAfM{~IConV=u|$GXZe;qx9T1*Uj3iNTcQNXm#jy?5-VmIqTMHD1OhJ3P!?Ff#V5(0G0N6X0MPzTOu(FP&P9 zBxs0pP;g3zK6nh2w$E~Y*!jR(4Ic*HkHqEkUsd>>P5&uEn%Ka#|3W+YRGn+F1S_-@ z#rj+VVk|t6A&%!#o7{+;hw%iLUp7y@an*Lk;H<8=0ZBzUE--v z&Cf%DqAzbaeiixx`5A4Iv+T&B)Z7uE_2lORoRn}B5P%5IJX#>;}0jFKWU zI|13OpW4KWVyd5)88842GdZ1}I=9f}3zsBV45f+uLF!nQYJ}y;&ElRk5UYg^c)Gmc zU(Ilx2OGHRK=%z^xE4Z}d zJCl1$X5Z9K6kx^?Qk+9c67yP&bo}nR!7iq=Cat&yYc)}B$wM`T4|~D!kl~`B?jlrHQy>~5$r2#F-||*xAV{211+RH zKJpy~xYQp|aaP~`n-EFSJDL}YvQ+u)&So3*>k+K^383uL4Flyq&_?VG{|^*nzT+>h zRN$D?xc@rHd2ada+v)51dUL_AGTt?tVlV(>Pi2lmUm04oB-2*ScVD=g~t8^h4mUm*&R?Fq( z?aE@L4`yNntm8XTrX0_et!-$2Q+LOb? zfi#N+V%ZFXqU)C(YF=5_TGtNnYhZuJ>b!2!%rH`H zv*D~0iet^)Nixe&ArvZg+)P(8w;>B~i`}=N(=hRJ2Y0!xS5W1FXB}!c1q$VuGp6M% zx8X;lZ96l@ky;9UK~dr4NfPcfO)7w|Cq#e(=DE`wZA7)vkN48?Xj|OeQ~E)pDaG!< zggayjQ6mr^pw{fWyhxLOUW}Uh^9q%*892wk)7;*q9-nfKwY>&4UtNrTd`KBui0592 z!!e+>E^cbH13lJXKvQK!q+et~b1)C3-pEBT#YtaYjqI5V4kz1AykB*&+ZhQI7A5}1 zBb__5Bryiwb#HuENx$()tcZOfx8i`|!uBn!mhKkvYv@;s`<3=j4?=%GYt21v=lXqZ z>E1<$KY;1rG-oqIBMvK*rI4~i%%8H>7W0D+tCaY3050Y;U?9aH?R6)B)e-YhnlG!b zEQIQeJyNn%vK-6%)@fry@3T|gaO~DQq__+)Ah93!Lwf2&WA9TQwghw`Ks58=i}o7W zz$GZMjg94b6A{|zJJf%DhWLuZs|*St%)Hpf4wb5N8{shil# zdxCwz{U4Zx9vFZFs>9cRP;yd^Zi6L}`QKx1xKJ>AoarzyeYHm0g};BCZ2C;9xhf%) z;q=};8s@i}JtzgnZ$23r_SKRn^~2>$45)Zfa`15x>XHVyqKA~dh#(kNKg3w6TCnYw zf+Ts(M3miKlwbYmD>JK9+6F5tR!jCUp$y{LZ*;t$r2w63TXv5m2{}^n89|lekfDWu zJ^tWuM%e7_PuU(t13&&x3xJTw6okBg#JauK#*X2$ip~F{oZzjb^(*pgf9|XMRTbi? zU{nXpVwYFm4Un^QOeO6GTihBD=cwApdO^gSd4JF5Qr+HPOtk02e z?)lxvI|a$`-P~D3-g;Jn9*Bfg0ji8;6W*icU2zRec+BpIWO&E;;T+J$K;^oV#%rN? z2e=>2K7V$Zmx?3%tnen$p1~QSZ8;G<;VR5QZ?UYsuNEX$ZR%Nfld*;tOX$KgMRLx{ z%~BhltTR;Zt;@aW84|$K<`XRDK71vZE;77?s$-2@C~{2D!u${c5wOR(W6+dwbu*@B zXI#=cC1=UQLPhFk6IVE^IEB3&RMFUgEMGVi+j?DXU3~eK&Qxe{LIK2A8ZLc=IaP_A zl&m!X`Yyjv&FMc5W~A~D+IwEcb6DCzjnBq^v3}7d@7onxdvl^{qdWAsK1a&bpS=w` z@8bq#L0aEPnG+VyePYN#L50qYD;D{#xM0}n%epQc*vkYE&Mq6;$k$i!uL3hRy7zPbJSiiYV)9vo!p7YTIq75(4^X2exq zG(KhQH*wE-O_H~M+f4~bo~=dylPPI#Vuja0=?V&;sjRxXcWUJ1y7$4m@U1*GaYc}$ zmCh<6{Y2DuC6H8ai(fzX#a=AuE%3_QJEM19?xYUO9lGOEl=$ATBcM}bPQXLKU9cZA1RtWQ~}J{LvG3Zy*iDI@jd z0vI?U1H^xxOvhjGjzx+&g>}3pqO!>TJ=MPYBa)xtCzf4I1ZlsOQ)D2NT^{W`|=!8YGd(tV8Ylt9y6ug zJ=wL`l&cC==(jR){JS=SGLD@yq-X$GK;1v$4aUhitgmqk?h3(&;*5X5(>{K0nhBIk zB!^rtb1~@k&I&;00v%0QsOX3{gTH4nXH;RGMd{+TfsY2l&hG8m5x$@Ffc{H7UvBSv z2B4h7q#%BIf9EWoi)`;t4-ZBk$tZ8ccvBoHiIIe$L0si`2UkC&c zNt6T41;%?EE4UzhWQFFis#8PB1#Luu-?1*t8*(Owc}BPRmzHS~NPVsCM?II1f^p#u zB8Qawh$yAsDn>Ft)0V4=<1DIEM6DI^Cpo|pYH2nAbOFgc#w6($CH`_tx3f$2yedgBpd$ zfofI$XLi4M$m()IGk@lD6k1{}pQ2@dUYWNfn<$}s>XUGp!^Af2qEv2*_;ZH4tE8eL z0R56Ni+p@}FC*nU^TTMHMBl?z0+_zD?g+bF6uR)QtQt}1Y9C`zF*Vy#R_oze%Xvh1 z1RHG{%1dqv9shJ_2hJ)`{;hj%yDF&TRw>Q=0^RxnXf;)_n7K17-c31^%6#r`G_IhM z0LDpLSXIMkA~p5(Q{*j@;L3Vm*UCR!13d>T=V+S>#D7`VUbr`jFK+cw`b4G$Jr|!? zV@Odm39p`e4HULYMCuP@K(OehwU#%?x6l5X(Cthz^u6^lBI{ATYOiF#r?tpIEE!5( ze3bq}mJ2-{*eS?3JcGTFFoX#maH!~j3^9K-zKm?tE;-dbRh67fJ^uYfgoX1hccL(o z&XPfdF{^CyaDT=-ZGTvi`1rYttuF3CDoT{c=F0aYek_K>({IEN^AW9aENVT`3-gn^ z()vcWx?JXYyj-7$(=QWlyU<1-md~<{CI{M~dY<1NxnH#0*}VL)As0_n24ck0V#OGE z&z|vJ)xU%8k0=rwRpdSvOa{Pz`&wnJ-NMUZbkC@idMo}KQ(o7_W9_JSt$B04+y;0i zr=8rT4;5xOL?whHAf84Hj{Awvi7W44Y}E_nSVN$wKG7b~Q>)k*#z)`7rK^PpNgfOU zQ9LzkplP-h+o@8T3)a~fG}Y21ivJ2R7+?m)?mr_e#?njvD+jLAW(FSr1m_lXn+ExL z9x=tWp_YbwMv;9x@^ALmKR7{+OBQvIiL zSKzc~7M~G26H0xL&I?4~jRx5bY&eXHzIqUn@JoOwL++h&AGz&|Dm9nh1|TW$!vXS5 zRz~Dfsjz9a;MWbLCo~!j$R8_bC6I*1`G;0mCK!f8rQ_1Q*XwHDYvl)D$wtyAt2Q5x zzp!;}#SHpc^kp|%OjdO{6gUpY9o)%$Yhm6Ph)&7)c&B32kJ!H~FNH_Jf9joIk3Zz* zytX~!E{-;tt>I%&+u$_mlbfa^$z?FH&31d8hus;!RHu_aMm#891?KF8@_(OWV_)2E z`V6=sa@4V`Z2j%_DWpu^^mYXM?~n6+;&!==())#fX*L?7XuXqPCOm~ySFBOD?ic7w zVEz1#uDP+)WfGlc?3K+m!w%`9c>`6Cl7iXfV{Co_Q|!;Dt$fui&(0F+vG7Btf{U6d zB2SO1mq*(gJ-NYpB;FmtPkUiSAR}vXWL77@b}n}VDD3p zUoe+fv(rwhDuXDW6n0|VrbTeo17#HG`Tfdrt}7*!qIZT>{2NOwU+eL3Pc}39%gU>m z#UNP2t6i0x;&5m~u~xP)yzQ^){XKP#cbm_^h6~&m2ngEGsYEU_@{$27oUDm0&Ckv# z=h`wTp@BW;x&bYfixqR}XHCV)y4oq}zEb9s<>YH~_e& z0l|Lo^-ul#Xwu>rp@;1gx16Sw$xmYhbMsfGMnP^S8OKykVs~Cb)CJfqy=Z^^6+Wcx zqxd86QOx98>P6?q7dS$GG|OlIb&`m5rT^{lhAI`kvmV^hwo|aG_FR+44t+&t52Z&{ z_45bO4P`jWxN+M~oFef2o9q9YPA1&@Oy!I?jqR-!U^cK8isfaXlG`tR@u zTQE-ODK$a?@AD{m8Z}!?FZ|qK9<Kl_ED+GJi%AGqPXI-W_Oge+)p-9aRZqMS zPZ-^DrUr*u->cZMnR;1F%I;dMuKNZ|-7(x%(cV!jm)DW?GAf0rSj)V-AwcVPg*MBV z3TreVZo+Q&N<2N-I$6w+EJMN5z-;0Bbybsk(80D(0e91t`?P z$xAH`J8Wg+pQ9rfT_L;qIjO(tlP`A1L56PUy5rY+o#FZwZGmRk>WK&5Yx&L=hkIIZ z&NKm@2l9_T8g%{iFfW{)sCy-1Sd0XBJZc<@dPNl#Wb7-B(?6hco*MbJ$J;41Cf;op zvYP-qF;SWI)=Du_nKXT6>qcVr>ZQj^_md|F4w4+$d47mSim8R6HQ`6EV(Gw61<9vI z34DgDH3P}J1hS4edvnT&@aH0pNKxT~)S-+=g=5{$VR8Zj>SKtc2Ui?I4s?p{-*?sS zn=jv|nLMMjS+PlT)gZv}dIxI97pPThFYKnIepf+`w_gN)MWOM}Q>k-@f`0i6h0Uua z_5Ltt%U#e#ZspuF@172mJg_kJDao#mUpJ&q>!63>b((h_s-C%CBadyfGuwOL=u z2TSEpWQ~659!wa&vz2bkT)IOHz13rnd@J3D9%zRkS-DYGCD8mT=9K{&H&=C&Q{GJ~ zmFu&>B@S$7BLm*Z#O)3z`$pJT5PCZYy?k6gB|LH|XU_jSe!gyYcg*%Z^XV<6d(jdG z%M*$zAFyn#XzuEFXPmHMLkUj+id9byq2=y6l{!ntj82Fe{Z;TK78>TM70poEecOf_aRJWI~X$3>wnYGcE zllN|{Z>*MH8IA#I>5BXgTR0c5Er{=X7zKW~o-Za-pM)7+$-nA+i145xN)iT$IelbT z{(BGN@e8QcGMj7j=SF^~=@$KDl za>=xnW=YJub?{y71)N|hl&zTFDvw!xD-C9+C`R(0ug9lQq?Vn%1~p1w0Ke=LUZaW{HMxiw-S_7ryK2N z75TF_NT+FNv|ysJ$$!Xw%7`K4rX2|lJNx73`?857&+?y4Q$10c0>ASF8)?xZ;qRq# zPyI>@ks`Z2nd3h#VkqAk@3cfamHR4Z{=9ygJ-qWV7z=ian_!d1-jcYfZOa0jo-wdKls-qkJNI@ ziL<}sT`E21qAyJaTzUL{I^fIrbC~!D{Gnk81*9%$d%($)?NkNFuQLi-oZ|uh13w*P zf=DV!oN68X8e(D2S)l^k{W%&xHgB3pvS%%3UAir0`}iVyy+>+9wNFs7n-{VrDp3X?1arSK&jT^n^hcy+| zZW!y|wf!+E0WWPF5Igivyc)Qr7LzS3V~~q*(2Ns{ndYuxNLQ8UdCNLy{Z|hQKW2Y9IKl{<^_E_A^&m z34~O*{rS$D#z%mA7{3?|h3n@qJjFFqsyNs_I<-Z72p0Gz@ybp2LY#2pi5gm*tp`?d zUFY9)P?YBfVx*S(213JlVr`iC3}$qH^r>lL1vhOq-?5BBEGy=r`J?{zjDEqQjGv#+ zM)}PQJJ=x-m%(Lsx4BJ7UgX8`zMT?L&9bJIG(4VEu`AkKwP1m7N(Y>lHe~MzMRLp5 zXjzv#Dk+_J#PaMj)*U%?n{!5svdN~3yj=N?rMY8SzNIgLtlPkuVoFOkJZsiUf1Pg_*b>PT(Gg6R;;hRGyxYgX+-aKF@U?D9!WMO@G1%P9HEa5d+GYd^0stnP0gTrb4L*( zP4a}bR#fs#CEj2KQ2={(T+}c&ZOU5LL0aHPrqGm!3Kni8Afma;3J`NMSL8;@mk5+k z_?O2(ba(3f<{<;#NEwi!DaU*#P>+`M2isak42j7ZI*yE551ue!A_unwQ0H^LNDgXA zS~;lTNpOxIF;m)O9%I3x^ZL_}5`W`i#@XAEdoIfnFknTYM)vH*WU0wG(|j25N&+}y8Zmxa_qzBSD!zRgp_ec;M&hkk?;g^4!MXb2=k<)lJLTp z=ub5c183IZ_$aXnGh?5NwS+D96c;JC^{_9;yuz0;6enWadCOwkha)5EI1KyumQ93) zJ<8y8BZZ^>{VehK+h9Ao#^1okElr2_leEAs20qi4Z%JeIeGJek8`TJ2+}&9_SX^F_ zakWnp_MjL^fscCYus)b_5Scd@3d?k)K=hbLy`K(=BvEV6knk$UXE0AWfC*I|kl;6Q zMy>cI29OUHc$}8Ta@;AMLX2^-qUdvY@Et*_d~D<8xI+XwHB*4Zi^xXVkQIxUs#T0H zqKHw{90}hsZC{<#`bC*0pL~=Zd@xxWIGTN3QQ(&;&znUThF|=}-&Y>FFojQzJ-iuz z9U2y)h2gDecbjvE_!Ib+>8NqX7?E(sa(3jT@@&4>(3iT|cZ{Nrvt>ih9_(qL-E8!q zRqNazaPrDeKZL15z%sNP)ywD`l?E@c56>82l}LUy@i3U8xp&&a#7TEu2Y?ob#Wo+K z5YhX!XsVYtd8ecPgm&p!`=ve1N4^Pbt4JWu?B0^|5Hqg+J?K`IYS8eT0UyO7ms65b zUtsoE&)zNJNfEkBHZnrsS(DmRZ>0E=-ws@B%>(A8eZ}nHURz_z%~gkqGqf_|mU}IRO-;zqkfKLC>89 zeeNf5XrCTrx(~?!T~wWiAGKyaWq}#go*8{^l@Y7o5h`@iBqfV&aTEqsx?d~F!h;RA zVX})?F)F$(7jjY10Vr_6ckwbf1bPbb(b}uYis>MKEea^R_M2h;cjD5789wvJYVeSm zA<<6Qc~9^m1uLjlzd;JHOFB9a*K^+XszhaR#l~&vdf$%@y9W$Z<%5^%hYqjX;gm98 z*B#XFs5H1wX`Q*CXGTf=eHJ%`^Z-8n0<&jp>){nFl7lD1H_nf&cE73gd;|Gh(0d9} zwS#PIz9^tZz;n-cP)q=s08?uZ><&a>krwcpiOWb5^i{!i4#QG~Y!juhMQhD(8iu8p z3uyuE%fBn2FdWJ1e2;i?0y9|KSnx#gy#5}pfPfRna~&vlU3QB9=6KH@KQfTrK$=*d zDiUCUs(xyf@6P_DTXD{ag=0<1?Xrgh{^nch$YtfFG3R6+g%!xxk=XJ{;ut#Bw|Q=p z@DH#H6!8^d?JpuF71{h>CsCS$+8rM~kYjsYp$h!iWeA9rI8p0)mOF{vX#dYivFf1r z+H7TJ42+XFWorre!p;$47gI386zc}UC=9T>KuQtVxj$|`1%yxKeo`I7!ksUpR6Jq; zyG!RJVx=PK+x>&O_}1aG-|P5iy)v+>-4Q=9R+*rse@@KlIbUHqjSC2kd8gc&_<{e{ zi+65s>0)D3ueP+4sYgwlsGJ9E;5=z!bvAt7%D2Sa^5y{rAts&DzOE*i z`aAKxg?^I@`{n*hFp=zugD+&VdRFq1ocx>>9qceIxeHqUL2cxk^y-4IG=X)}<6R=5 zAmgb1K4gF_?d>o zR7o9_7I8%zTQEs^-itjI69My<~mWR!?TD!f@fQCnu zC-fzh)~iUk6h+iN_!DQ7yBZ9Fs&R~B*mU!oYDNj7N z^2j~sNuf&Vv(#CX>-tIe#nSO{;)w|gV-Ig13^H(Zbc`cwO4Z?#pF6jWxybnvl5^k9 za7=t#9(+9h+BPBQmhgmH(JB_KZ|(K^QO<)kCeenMDTUASlRXQyGOHU)vlR5{lvU$T9zVmP8&>o0J9l9TqROhmWH;n2X5{8cmr$z{cDd-XM&gERliq2s#kTsZ-l4M(3 z09O3(`Hy|gYNy6rskRpjagldR?TgPs^-+-nqm0)%Y0*pR$=36t<;O2~NF@;Qv-c^l zC3`NfNSvgNO@GQ+i#}5sZ<3Tkev*q|iv84d5tM^GQhyY2BKE<9!;$V+jIOm9gwC!^*fc66X-7mt+adCHp^A{DDp<-(dwVnyYvXP4ZEG!ElE>bPGO>`hOS z5G`8J*EgHeIUUHxB8{K3Uchx4ztln43BBnBu)Bc`TQ^&-#b0xi22^l2%ETnawrIea z?VtQ~4XTeM9S;RfSYPcOT*d_F=fkRhK($OF}p2!oGew`vERSBsX+W;(B-CZ^x zX7$bVDvxA5cB-8(7EF#{4bIY$D|5c<&9nYO z8S65IG9H7=J}dID0d7I3T{o~{<|dFC&RG`&5DnRj)6QphUz7(OO~c4Pag};Xxkc+QscBcW>~t%7R|^k=4*wt3ng6Be(f<17@cFTs)}PJCC(_zWvi7Nyca6l3{lhG`E+cN>!8IhmCv_!b`bZ@ywqA8qx)lNuQ5!bg1-GNKM!1FqL+ zmcF;JmA|*E#qydG)`Pm5qA|As+5qfmUYh<(|ro^iN&jO#STt|NQ$Q-+zXbMF~qp><5C9?9vUmpaN9D~dT=sXOU0k__jzm@Z{Btkh!5NQ`^ zK5y2U;@(Mu9Y7C!`%o~9B9!)q$kbt8qJDj@tnwq0SqSv6^k$M9W|YP7@RYc7A{?(SXn^D_&TI>ZsrVW!PJ{pTke z+r_{=huK<%qIc_)0OaAN{WfWs8*}WlycFSD+AQQ!N(=eiYLD$^@l*1?%#N3R_B`)F z8jKmp>@qR@(uK;Jgn7w>IJls*;BJ#4#0l6nc$rST*=GcY01-+J-K`T^{KO^HXm+A3 zZakf< z#ioy!U{?eU^`!4EE;y)0Q7j)d>r3pwSN&`gE-#PWT^U08pNb+w)i1wMc61Vq*V-W$m} zQl1mqYx11m=VOm_PrTShjdsY_+HUhdhZ6?0iQefiq&K^;TB zW!>++k+LJ?|5Ut}@9>Jrg6aDcb2RTU=ofEGu5(G<`sLaMi7^pu89fU6Uh<15VeFgt z`|Tyzg$#!I-MJPG#|CzP z_1uv~TztC~|6#fB4o;cJeKK*h1c~u2 zAN_SSZfWB$?9ww?;A{>Axn!ztk3bJ1bP|a+VSbCnf#;bZf{o~hUaMo+z7u5$Cbwzt zpgD@*oxsHG;dmCjj$P}%T=Q3q9HC=Bq8|i?5=9jwGZD%+iQ@BIXE59^&gX1r-2im6 z9W=UtF&SNOAzT-uZ_|@5nx~L>twIXyMc2Yh%_*bP)Rg#3rNNCZ-9@v38)uDl!aU% z4fS}rXE&?o?CVb5@fYn1FP~OpPvc&aRYQH!+0zd{v8l7s)7*S^{T2J|Fp83ki6`r& zSJYFwR@B~#>H4Ro6_}0JEZN&W_zu!Bg222%ikn0KjXT^yV6vxs$)o;o8IQnXJB04Hbtic>plKz2 zyTMaN;xf+auSi0agov|mMqMWOdF{tSuLDPC$9}6U6dbshBGIdOmAyxjQnvn31g+QoSW8~Z|)ZRK4xs0iU_%O*)F+cK`9We{jFZ8FApoOjh- z8)EI&V-n>eeZB*=Rf$x zK(6w_X3!{NIWilZN5JxL4H9-HOL&mhDpSK>29KvW({3BCyURC}9&(Q4T?7vbD?VXc zlbjZU>*E)R-TL{kX*gU4Vr-Epx3AZ|Q2@KoYnTR?hg3k&J0{jGo6^zJCu1$BGu54! z4mbj}Kx3qNu@|QGI>@W*Qb54mvy%9VShJIIH7t@Bw|y1r+=}-m{;CKc#B_m=U4q%N zADT)1h;Nv1SpXBSpMs@KQ&LBk$zfIS!qPXCM_}}_=koPo!0eH2yAidx)AnJ%DiW2|yiURd+nf7;*5yS&De&bCQc+n!*{FBrPnjDS~wHBXK z(_r;zFY*u&6m^TDU{Mf!KOTf}ydf9@5#-ZY#y<9EVK4`|i-t zxTn@dja5mCfpv2i+JXsA`Vyt(>qxLC#iLuUponSp#_{nVY&Sr&> zn9m+a@c7Hd1vRilyg`BwROrbM?j)JGFsY2pI+w8GQ{enW+dmIEoY{1tT=FHw zy-*`TVH%v3rSUx{{igd$#tvo|q~$+6SFrJX!ZXSq^fBvMyED$u7X#guUB4iu zgNMmxw}g9XA_dc)F_(6{lQ=ZcLl4s^(FB@_ljVG8kT0O1u77x&(lRRuXnmiX@xTFg zsBB|=r=8(*osjq)m1zD=Nty+rR965=Sl`qcLa&lWS6fKA6T^S9Jpk2 zy)0vA=1p>EEWLEaW0*V6=Y5^MSEGVYTuZpZJ5^^=zWj!MQ`EU+0__!%(wY|x1gTg8 zU{?-Yo)}C*IQ?5!ODVU02sZuIlZ+Yz`YG#(GI$0a2o9tgd4i}TTH*^&A3@r+I!)Za zYY>IqF{+QBhlmMcF#hvn<0<<$O8my(d>+fLs4=_0o&3f{_GoSME}y%LJEtT4pnd2Y zS*~VsMc-KefpSL(U?+J%*SY?xl&H1P|*dHw%7_nn8$1gMO?7a9l zlfuoz@7^{|KEvDG7KK;@bkV20n)3p0MLPT}{kzwEI_6rk_;_g3s%zBbq#kBbpW2{y znzFd6m4vD-B8uPbhQR>Do@0F6@zBqM@6?50p|8UZeg`abN^5wSZtq#7Q~fB4%YtQC z(#xq1?-y^cr1-E)h;^$jFXghoX|<$??@2dUN{SHK?Zo<6vxZo8(e1gp!7|QPH#u@z zJ7Ku32jg5aHUXHRXW*{lp>LJjEMr8E#0tuqQK3)x?g92siTVKO9lOp-F2N1o^km)S zRHk+_ zh`?S8r(9>aQBMfrdE9c6i>Mb$;noviYF<$9%>5qy*0?8=jMlJ0;h6rlP>NiE9=~DU ztJ}66OUpG{IJSC}&GvnI)PKQ&Z8r(b&38rvs3V7-H18oDZfo+q>uAN2Q8%i`4tj1i z+PKo6EFFyVcwJ%`wlB*tPbI4y*eNE@-XWLiw2R@%zDdU7{e|Lw)sZhE96vOXW%AdC zF5Y*wKK{Wi2CZbA4N{C=(!m&QjQP}fo;nlY*wF)9;F>*tAzyeD1-*2%sxf|At;;Xm z@CF>Y+C3wF?18_4qu~i~A{W5~htRd6ehx4+IMWFq+79|cT@BH3L5zeRq-->C?))2z)#%~n)5h^Q?I zG;IZ1Mk^lp9@@@yGWh@KYWs`cZ_RZaaqXI$_vmDJ{=F-=IdoV?(U@eFsML83=CI>KCKl8T~@l5IXKyhxfDYv(~j-P8L{(1H&5~^ zjlSsbFegP2v`1_e&J`lcaj?h}Rs1G({eR+rFXGR@4@r#b#m};$=rAOw{XpENteEQS zAsI=c9BX4gei#Esn`C}V|9uQ5y$9G}V15{449EeLfux;bvPsluHTOY}u-sD1Q7Mfl zyyksBW;qI|D1YL5Slw51!H+0c^Xy-4KB=X;_UJ*DM7@mG;TQj(7fhUZ4(qU^C7Qj~ zVCR;QA+Fgi1ct+cnGO{;Si?MdhSo%OGO+EkVjk@Zt^|U}r zOR#2Po_vbW)OCM_hx?t>#YMj{lD`I&d1XTFCg()zue_nRSZ;`#3w|7MXghbJ3ZWh? ztFD*Y1EF)m0T!4jX-iI9%u?q9gQr|ms4(y=Vu*Cofp#vw_fo9waSER6 zII<}Q)?3n8r9&OR!}UBNSi*R}<6}Zt7fAX=5Ie)5Et0ufc9ft};XZNURQ_s-s_FND z*Sy}|c;KW%lt|lKK#MIhN$y?vl_!Ppp!c_DHwUomX{>@V`A+wsQ^hOt&!eM_dK3oi z6Nd80P>@q>BiMVI9Q4fylUWsdVA=gBJRF3h?vjgJSn=fj5i?7RIEBq3B6tSrg3|vs zlXe?*|0=#x)$;2JmXspcg1Hf*oK3B&lYUgkx^w1yz*3!^u}fXQgOMu%*Ab^n*KtXh zV83fl8ACr4VU~D$DFJk(t|mTqt72iNH>_sA!(x@OfQiS+O|I;EzHy~W_$w#De1UZa zCG=oIj?HuPn}w10eqHmV{T!v;Cm(3G%u*Ly!6G}hYg)dNwEPCbNrIPsg*XhAe5VLu z=)EG){396|O6e25O-5)5aTJh>Q7_d$w3XMBbgjNEwR?60q?f4l6MlC7wGR`2T#EJ_ z1D3q^0+GO3DXfmTO8T$eB!18Xm=!(&p8%8Hy|w52739jCU%T`i{zlZKs7}~Y^PoR3Pf9PfKc1!PV&K2w(Hb8j zjgD4|%m(%kM>#M^A_4s`#RB%ee$f$O(y2*#0{gek&%&bX7t2k%6jizn09QiqsW3Y7 z-$r3fA-%a+%oH&k(W7MH_!|}%@JP~QY|qt0@MgrCWZQedLrD9HhH2hMmxySFJD$Pr z0^b7^`7$G(jH_)37~*a3gr$?-0#gsut?r*o?N@R}Ke=Lme;LF2N&_9Lm#QB44Q&4f zoXAgrmB75(3;`?hZcDnKh*!iOEC)KW=W$v4`=iJ((r+df*vr(mQnlf!ME51ndcx(b zQ)UVqYFx@ex6;C-f!Dg)c*|%0=?3y~Y$!9o)@@-`b0dQ-`GPy2t5yxetDj=O+lOcN z%q3N6-*YXHA*iY^d{aDkIpKr8v8%XYJ}dBSdi`C}8+r5jmSAB6`KMB2S2Jq|x?8Aa z)t&q))9mT4(QY(hq4N+*2oFVM(04=JR48iKhKYyv#ZUgsYyuix!2(li{KHrwGXz$m zAuWiy*?7I|cInl7&`IcD+^-Wvs(8R8!Vo4Fz%|Ki5MuOiliE3aZs49W32VtJ<8>vK zkt8Ht34^EcY8;QKn`Fa7_k0o(jSD8deZ4o7(Nr3``XU%(iVV7x=)&zyIAKh$D*c1| zfF##~7`5v~w#d)-!b8d#u3QP`^3OKzfCD>n-(KV1#e7b#)uw=pIn$%0%3Mzjrp>N^ zDD+*}@c+TV^lN>1q&UUdj@ z2y*|J1^i5pB2Ha<^y&YevQPNdf&`3C7d#}Z(0R8cfyOBK@8g0zi_St@Y(yBMaHhar zpC~x{DFN_GwwX$TnZMfj9CLi;x|Sw>0)0u`@~pQV^VO-PlH#lo3@&LO*{b2@Fv=3Q zxv8-cQT^3)Uwr9`h|Ya_g>qt^Thi=LwoR2E`E>jYpGO28m-u@#m7Qz%LVk5%UG`8J zLd~<=RHGCzY^gl*kSD1tNXA=kCapofSVY$$yVPFfR@H|a4J9;<_;R%P8T|d@1q#;-` zp0&kJcK62){W3fFL3Z7Gr(x~`ztmK(Avd8bu> zuh>5-wJ3Jwsm#Md+eMOXbn1lgJ?0JA*2gcFTF?7ixiBe0MqfWPnx!koW`53cBwl%= zE#Dz)aF7K&#QXH?ugbnG7c8S~?G%*6nhh__MN%6+j0^|K$0=2i8<8BfIh%e=u4rr3 z$gjL$^<(e`obq)Lp!jcLA7iF{W6h>2i7qpfC_`AAN*%E)uVrf;m-(`_?3cne`xHEk zM6i?V0wpp&fQi6Yt)D2o`@1EQ(XhD3mQJUcOra83%v&p>vjKT!s-^wke2Hqco3!A! zU#~(ETk9MYGKZ9(Uv0CMqgA>_iwvR;ZJ`!x-ch2^SUht=|F_rGQ3`+lxS5whoHFHX z7}G7%b1?<^fP-(#THST{P5i6(Hm>J?FDBP3^gOa%@b(Vi0W3Pvl8B@gcrs$o1C;gu z8p8Z{BqFx8|0nQkKf`H6nL$-A{o90SdODIi(=DI<4?Fy`^(|qKo<(}lbWFIRIwM+d zRI|6*VFe%DIKO_7c4KJv;2S^NB|Qwb1INw)mu|#0Y(FL*?FTkMHS_MYl>SZmwGsIWl%$8gn3u*0+BkAm8H&pz~j zQ|6Y5@2P*!zpeG|((nYs>JGDQ@zAUM#7ovvJE%)2{Q#ima0Y0SY5iwc|AO$&I1t_k z2NPdnfkn{&u+Ybkt{@D7*F<)*0jz53JK0~)B;=+mT_h8*;3?4hgnu>hmo&z4fAfU# zD#lCHXOnv40p0hP%jxMBtWQtHzI;4KJNT(@G0MAeOW9=}*k~%N*IHd+6e596ntk`e z9~Ib({_>>&MH~uRr?4zxjW^ZhS+}5X`3cd#tCKPiZC@u-e4gJWqr5U?4@Q=K)SP^# zbGu1@cn`neBWRlFC0tUW37NI6d7(d`xTO?y=6r>B{l?Le$nscLsS4CPh#G-5;_5Hb`x2%zHPc ziP(4Z-*DLL^HSu&JuVPk>-uxu&{{ ze1Dia?|;1V-at^B$4y-7o;pI8Qfo0Auoq{A9!C;YeB-F{3eCdv?dM>2Cy~@&X-!hn z_VZE_&*Fhz%v3^rV&>T+_e9DwSQs2!qLioU4D=AU~gcnxr!ITwg7uWcj$p_1F{h0&6FA*F-6o4ECIGE+uup$|&=igY%p+ z&R5#R5jqc#E&w}jK7ifTxcuFiYR82V^Y8Mk&Hr&-Y;^L|uiLGl8lbakMMqXXZP{E4 zevm?hi(O`_ZNmv5q-u1Bv}iZVz7tr-s{-*vFad^xJ*qb(xf|Aur1X{(p6F8AB0WL3QS^1J&h?rH5sDF}-79iXse0Jkf$E8uSUMX^LZh9Dt# zOmm92A~SLdgGAQ@k?ykKG^NVVhEq8?Swccj(ZcBZu8D*e{E}!lmF_bhP^03Yosjcx z8S7;bv&f1#IZiIVQwrmIDY^$o^M88eJM3w)Zv;di^AkYET>re8I^sG`Q6K>WigNEV zc>oJ%fR&s7?zTuS;8LQE9 z37hFN{ciht(=wjBkW=dDSzDf?4J7LdPi+&^VR6W5ypvA}qOPfo(MN+;j%u=TJ90Gh zdpO&Z>f$Xytol92OiDnp%QSz_scE4`@AStJ;%5vTtKbSL_ikykiNz6 z1)OwQz;K8YppLkPLtJ9zjsvm54;3(XX`&EFWz&u+jUVaBccpV-DT8Y{t_FUrx;qj3 z2_Evbz90PVeK{*`ZuiyVX7C*Ar0`~fzIVF=XL76$y#@QA%+tIVWixfoUN~75VwE3E1i<+PGkpZj|i{atx+F*FmJ#0U2saclN)t^<=$M;iQrCp z3jOS)D>nOCH#b&hGxG*cy7?XFsq1&F|hpfM`TezxqIyg3~ zc_@wMo#r|0(8m_J?uw3tYJDs*)J}!s@+*v-y^_)ceP^QN;-Xb0>KeD&gqa~3SV#P* z7wjD1-^Y8`5zgiukKOI3;!%VE)SkZ9nOAlM_&k;pwYRr?3B+0ggazOyh@+<@$C;?w z1)N9pl;c$+5M^ULt-u;~7&9y~aKX=$1u)b9OQK*8Chh^h^}HkK1gAdFc-HUibPHm) z4&~|R+VUJ83hA{uKa0<08I4DnP4>nrnVp?II{efQ|4LRW;`vrGGHV=h8Wua_(VF{9 zjPTRH*XOWU>6$$wci8XB1sSb?!4L>`BY!6%X>4RgxOodN>Yx9|^yX?fuZ?AY((*GJ6x*ziJ@w*sZ2@z}2Pxh^$ zCb1<#Rp^oeSVUM>iap(RkO_8X%gMK3=V(!Zj{>d3c6iY0`Sl?u>vL`V4JK|WwTTVU zfH@AlsQ{%^R~d26q@&}1Spewe%*GmHf*-G>Zk~S0y^oX@sou-J{i9sT>=h|%fx?Y- zEPi8pR%dVBY(EtW_`;#BR%;!ViyEJXQ_+cN6{&`Sf5T}CQ(7_0-o6Bf z&Td%+I8+)(4#9o*>vUT^A`hqnQ!FAU>W`hBmq`&VXLHw-@g`7(kgeXeiPzWw_=v)v zWFmlMpyJAL$wjN=IpUwcnf=d*>_pz5F@&*(?#^MQi8WAN$&93M!7ZoTOzF)|o;DnQ z>?xRU8xcS4r?ewwntbnku(IVqb_Umb+~LX4@|hfd6tTo^~IZx#;+TYdMHpYWIe33-YGtQ%62IHkfQ3ItMm6cX}e}GV|+ThXS@2 z&aBfK`hlr3p_lIrcCswjPZD6CsIWyo(PVW!Z1v$FR8^R@`&7SZFVH#k`m)w{^o?#m z>R*k14GjWLU%9PQsz=1U9+oIf_F7R`g8}VetkZ?xIliJ%N5Rt}0CWD6z_aoK?JNP= zA2Y$O-(KKa!0<<(g(3V2i1@=y=`EcuL8S$be<>A6JIQ+3A7>2GsoooIkxT(ZfkR-h zWN_$THdd}Nnv|$@E&&hn3xN}MA{k8fsIpG~u8FOhGzN#Ze6W3)GNYWwdROEISHjQm zn`v6Q45N7q6FVAz;-dxKUb{^seoes^J{tNKE-Ec?1Jzo}Ofi#%Sso3h{$wF}3&o)B z{dLJAdD2bX!-0qAprL*8aY6{))9oB88@s<5#);kg2o74Oydp5%{QYh4+LGHgcImww zxw1O*{5*oYJ%qCC5>%^t^IXSe>7uSnD6fttmy+1C0Os1NK;_uT)34B>UW7dJs zlz2ZS=e%RUxdKkioGdjl5psaM{C$%?8jtcl zkRf;*sVXF~$5`stJ)b>lXpu;@BYH;!8sqjWew?0wv()UHreNmz-vHVsR zP#)PKZey1PSV=4~TmEEUi8KT(3k_iw1D5zGOg!W%8=1+ho<56f83*o2Mr(fptMi$j zD*_soc);0>*SfW63=O(_U&9yWs4acF>wQUN@bRFNTgT1%8opR)lju-6_om!r-CnUz zqE7eL!={>Q*b>Lr*{6qZN%5E&bu(vRGdF$JH;mlPJ0@S3beiiMO2tdYfAV`VGY^G5A*O7b?vmZ>I&1V z`Vrn$G2C@(5Ai!pziqKJVMjGros=tin>1@k)yN)3K5qnuIK#w zlVz;W5a$gDo#vBl{#a|B<0j0T1W~t-b-O9?vByK;(juQP+I6_?gP4s}1IGpq^6TOk ztI_?zQYU8Mph(si(K}yU&zn4BVHf*|nDF1v)$kjJP1Ea%I9_m_T0o32rWiv3YT_HubA(|kWcyjaxq6jBg{5sB zsr{%gs^8d}jp{|<-TTqYcj#Zu_V}S;V`%E^;62#ryHxCneHg`qOdN{?a!6Zlc!KCm z;zJ|iObU3yuwEOsSa+%V&ouPV=jw7eG;tz+y&W1Wph<#?O1vk}gAN>RvmS%1K zFEnjH(lYg4gk#r0g4N8CX5V(M4!5YuT)9HHQQMD`;GS~e1+G+N-wS*n+A+pyX8AYw zz#)AT7T){`pyo;M%=<$3W>5SsCcmc764-!GEucdXmc7f>ysPaq@Iav7gjhkly-{@A z=WDlWwBNb7&JN$P;u>zp`RQDZqcl>T)FyMl-w%fF*{@lEF00jXCWXQks4qz6nrbHY zpNabZM@MnIYu(`H{Km5V??+JupzVVZ9bPg=snHh>{i2HLcU?+NQ_G0U{uWQl^gd~#0P1tE=pJ}2S}jGfcrEHskd{N#pg010yNlxNL?G3a5JAmH0bw5cU8T45>bw4qQb_$Iqmwwi=6P4@BXco0 zq|lMVLld4yYKZcSDxb?&X@D zW@BSrLe+R!A44i^%!&bVE?i%gfiB)~7?vWc)vYC#<~hb1_>6HHL9iiHPA4Ym-K`co z+p2WGMZSaxFBh6BU2^bGv!54qgZQbXuwST2O5Jzu6xyBfl^IX$!f#TMDjILKWYN;@_>KJ&@KFQ1G7KK z@A%wpgT;YRXF-Tga`rGtz3X*pb@04D(x}8~!I;fwi6r)3{5NJW$%NYz<)>P=`g@~q zr8AVbbTC*SB8)0lZ@Yf_@`Kz-w5@7<^r;Pz)ca>}e`o)!5;s!~BgP-ZGEA268W=C? zLr*f9Po5HegT}zvgK$i66X&uM4i(}(wFhIWLsTWL4K&0t z!Lk|~z1=R>g%RxaPhnFHi-PQUEVjokRydg$6BrqLxAKf}{ z^57Xo5u6bP!941VWl*EKi%ElUpaZSCo$bWM1eY6$6vaJ_ym&n8mgrHx7a-&Lr|?F-H;azgpEC=1v6fc3uxwn(pyR!5>aWE;N6# zY|6{>wrwUJ@A1ziAE;CE+?7Y<7Gg@kAF6(08n7IX7YrwzT{+z8M^UsT9m@XqO?}w~ z9kUtUz9{B8M4mc<%C=>sp{Sl)~cjue<;EGZkQ6f5w0IP3tA%$Nl>Eh0!1auU@Ix=`;P$BN{e3SEdWAU=ra zD9zq%1*3~9`%Z=DO`|@7`#JVT8I}S&>T8zRjbdJ_4rDcMDLcnJZxn5v{49#{H2H$f zs3391DteE(HRl$#QL{Qj(}-=<>M!|8)OFh*1a#yPw>ZwzQWc^Xr0na-I!WnEbq4*tDv{Td{o-oA@%9DVG;yt|ttYx;eO)@+*I&Nsh(uQ8qLiXKN_rMn~tB!+Gn8f54mau{Z2 z-_1Gid++D|2e9|-y`SG&-?g5jTv%NjVY0yRD?Epzdb{fY?3(;OTGP!+kgNy3R3A2= zme9>iu_$3V?){$zTYwBjKa+D*%AI<42dgP^_1a1fm-X9v21x6;CT4i=L{e`YP64AO z>L`?YRO8$XX%3k4IaIy*?FBsDige3DjF3RxV_uT3ry=v)v$r)j#}nU=%04bUZaNY+ zJKL?>d}qCf^$do;h?%h1=BL5`LVRqcUbg@-PelS>3@+07E4CsiD(R#Y-D;)W#)E{V z8^{3P_S#%00rXU|IF^ClN(Y|{IvS%u(=v-!%3PpsTH`NFF>@bfHv2iN6L6!Tf|wr=VPwjvHzS$?mBf~kmm zI#QQHdlKnce_J;@o!qtwI~umxYRu^n%#NsZIH0CMHN4$-#B%Q=b_KL4$KG4`B}w^R z-7J8PgziC#pTLJ%vNwnJm4deH@FGwFz6eBRz{Zq(D)IHr{8#HQGrq3yEu5dbtUsQ+ zG#)voEeAaR=a@P4Z~^zjrLY;*WVx{Q3xB^!eFv|h_|iK&>G7_wH@hp#HV0;E}X0;L$Rb(g4*y-8 z?ZZcVg;zqFW^&zp@goxn6=V$bvtxo0l{5?VW<~=`%0V8?bd>fns8(iA-ihiHrYmcb zH=5a>X-X~~UNZU3f5b=kXYzeqC(YBqTK5Oye?QI9j(+Ej`eSz5*qqz+H`wJMg*kBp zmoISoMSXQ$cqIwnFc`1Jeq`iA*{u_D>||E!C=#o%Us<8$G-FeERVl=w<% z&{ykg7+YR8Q+deL@iZ`uG)h1Ke-^eJ*6bUv+Q38mco_w0q`v7MR0>AwkW5)5f@{}aehgj?|1 zn@-pytO83ewHpOO*e0$wK%o7<=t;gM_~1-qDkpvdNt7;lEeCx1q39Y+B}(}`ljXal zP0X#PCiQ;DKGTO#hR)R-t&hR3YMga5n*2J^r`)a&epy)O+h03_$S;n18(fpUlec5r zmA2^SMXr!Z`cO=)=tER#I*}d9y()&0lP@Hf*h8vGQS2ugdX<&R#)ga5si8yDz#`iv zvr~nZ+BE)_)Jef+s@rwxYwYI757Ljvy6@RqnA`L%>22&j;e^=rjiZ+_3O#wQ-gi3I(5;T@oRv(#gpcocc|4&?+ufMvdwPIf19bIR?sgAE=3 zdLeK+$(>WdSbY~G;@dB`73Tu2e+LhaxbC$Oaj{h85VmA(tpG60zbUt7X7J$ofb3e@ zL5#-2u5?CWLM;`$R6LYW35^sJ#--PrIT8$eQ&`*j*6I(i9{JlkWZ;$(_?Q}?-%tCJ zx$&jiZJ-+c74gDG_6P-y@HyVAMNBy6FS0jV3b;%ie#ca_VykY}TI}->Sc09RDgG8} zrk*l%=8lj3MJG$L2j}&Nj3+4Gl-Z4U<>NTh z^!$_Z3D8EP@aDX42tAL$4ZsZlC?rjAq4z;hRxK+w>*ilF7``=PIJ_Wh72__&j!5T; z`cEBkhw zJVs4AC=L>LoNxD*y8s&?%yHyI+k83Ys^Zd(KQzQI>wDA?ivl7;Cqx5iii$nHUqNxT zaf39f@PJdU2OT?K>~k8}TjMwt1p@gYQ5;jz@!*kRb4(Y9?P)MHgr0qWbUJEHCw<+O zrH^+Q0dAfSfzq9)=F_g1gGs7&>GD0n1!6AzdB1v!g0Iop7$4LyQDiu>zfp?|BDpBD zi!FpeFW)l>LS|;lmPH?54q+890!|#{dNgpRmG?!_zP3|U&iZGVSf^`*K8as1`uOJ6 zEgQ1MiB@=1!|U=a3l;_#K5*;Mq3FJ6F3`RTSaJbuPT_+?McKAr4@J)xvi+C=E2yop z*a-+ZCZWOS9|tL&-+=Qm0xP(Qx*rmF*FnxXQea@`w zR<-pwnP+_5fKTLZOr{wZd^zLbFw1zAzro0~z1!9@g{wF7PxZ(40mDi4b{Jx!#YbK=A8;}_2_U&f)3 zI1<;&pf9fPYB#c_GXQNg!)3BiG!YSJa~_%(9v#66w-827dBv508Noa=fp6TD+g#^- zP1>X$?XGpZ`RnzCI|;GK{!yo8rezMr348bt6H1bg@^15Nm%6E_>3x7vqK`>lS6M!8G&ivA<#k+vF(Q z$~XzB4ksdtJ-3W2lUAx?`17S(Nw1f@M+hi@BWz$C?ep|i%4*oLGzW-t}5M`p%K_988NZt zn_Dl%cn+A_CNZSDS;Y>)h0e3(0Id2YR$0QVIB^z8pvLPFXws!Q3HQ|0-RJAp5`1@? z8{P+`WM22plK+u;qjF1n;dzYWX4e509UEC#^KLn0@4=yAWCmtUnlpmKZ}eU0SaxTo zOo)h5wgUbXgC)C8I7R*6GyVpAx*$y1Mm`5bpZ|Ro-DH?(884@I<}SI-t$Tj02N?wq zzn%Se9a{?}@>)84+JJW+BP#fr{;VsGDC}g^7N(J(^;w`xBGmF;LY*MdV^1yV6Ur`e{Uz`F;m>F+YD; z8VO%Nxb)E>n-lhYLRFLOnfm~G4Hw&wD)oN?y-LCsFsiY z3XIFmOoe$LEXd^z{!#KZ{1zV5FbuDU?`l9p?tF20H*FlnI+|NpWE`0HcIfqvFuehl z`7ll79pN%f0j+J_q_tg(Wz1nawU9Wv&FJ zE1A;GnCJ^VE?Iy-k`~89>MuI`JI=!l4;}#yRqP9|)8V4!qR_n45JEgO9$b7E4q*Q62D!eB{}+TqLFxkU#Xv78^?zAtb9g8W&U1j$@8Tt!@m&AS6OfyFZ2>XrLrc_cDI&`b zg3*yxN9ttna@IK>MEX@p{&L@0LTP9)=>E{lTYn+R73I`C!Oc6xsc1mJ?L|O5HaeWV zMP$FrIuxkLDy@MV)ZN$|@~^!%)ep8H{8-gdRzlibXFQrrK+4d8$qe>R^nm$gcPC&^ zkZ@W{K(=M(%S}X5qs%58@-fIvhY%$>DbGQb(RaSMnC6`w0J9I<0@;r?1~1yTLs4sh z3x4Y73V0z#8>?f>BPE3|OtCtm4LDXAx94w*6QfuPp@5tAvR%`30l4)n#+cZ_P)W2Y zI1p9Hea670-;pP|i=w1Vb22ZC5;zgSh?P*f))_pud0ZXLM6!Mq=6br8qLTe=^M%dV>6j;iTZ*vC)Kw zz|v*Qcgs5O1a24Idw-HCaB*1}GKVX-@V&&-Pm^nhAnSY~L2kf7t`-D}L7r2bn8^43 zjE1#aaR70b0e;(ZHXsJ2k8U8W=!TRs*gkh&Vk?=kEcRkc69)5=Es?>>RuM|*;iI?C#SJlq^LL>GFhqm3zr@qqa91Fk;D2q)oe2C zXx>l*T4#c4me|YgZpNQ3g>oBSUL3DsMs!PFgAkA{p>4bSUn(~iTBRQpPyM;8kSStt zZrt$wkq-QE%vh`R`;QsBj|JvnU>p|Gz)71o@39lV$48)>F*B^<_{!xU16Jf{?S7JBmf5Wu*$~kQHZF=Fi>QOf?r#G*!8HZ}SFdoW)WMwU4 ztH=k=XjWC8|0MTUmT{115k=8g8%u?04kU^ZF|o0at5&+gb%)@h=+yFm0uBLUL(Au3 zFLU_O+`GRs$v@00t?Xxx_RJ$W!t3H3%v_XwkM2X#vk(y$D+d2uoW-*ku~#X2Gf%a+p-qqKBN zo%4~xdwKyUw!tEHH-2Tg5b2(}n!A=lLbM%$=tj-LOR?E%W}AU}P7SL*cM=v8FK$c*}QmuSBIa_l-P+JS{<;s60`q@9Hru*oKz+tT3ucq(@ z?5YIqf{-k@4>R2%+u{~-zH!QQ!p9o_`TEH`n~eL-NkxJ8_T6i)h9!M@R=lMB zL zy>Scgx~6A%J`}n>WYe&G=-T&(m@NLwdl)?{^Nwtu4PB;C=>(D-2~ zOWsjhA*`BpLzPXv#SKstg z{zX~KlmCw}r6?wLcz$1sK)sLE4CS)C`{S{Y-F+#=ocos%&A$C(V9*mRw7I*|&`OhR zNsy_{;l#65#(Z?DxP&`hJlApzI@a;qPC7XyIlzOwG6)#P5?&tz8}K%^qQr(0ICc=X z##m5{03$D!MF5y7n0W-^wiVl|ZS$$EWj z$aw&GIB>1L1Ai}tbC3M0ooD=`p^3x`3gdGz*$3EvfupwZ=o64qy%4%EEM*khK;-3< z0DQuh&cz4k^E=Q%NCm9%!xLOCPJ^^7&BuIguYXt7#>FMXG0REYu;>+&n*h#J^E2M( zLOp-J=)VZHD2uuQ`$=3haWDUJymjLSBybY6&f-?zN`x&r?`H?y{vYNOb=lw-9Vff& z5&B#_fbX0EN$~>$eZzb};x~G_u@LKo2_1v}D>J;Va9gbtbp_4Hsc|3|NI>)*BU?!w zxnHxMMPwX9Q84z94{vM1WCP^FmHdLCXy!(XHolT1Psu$*ykoZ5ul$-*Gp#vsTrx+G z+P^oS7tJ^`F<$b3itF_z6KNr18ms9IlUaVxYnr}!Tf=xW56Js$_495t#fv3109=?3 zrVcJ9k|fFSl{*(4p%q8j-kT&4tZni}Cu58ypUBF;%FE&_Rt5 zykEfQ$KTe ziwQ3RZt%W%+Y18ynFCi}KYW8v!Z?*6SmEslEo|Z*#9YJnUiIa;<;nWd{-wS@q-0dQ z`ilnjH?lj&MWQbEjk%j{CJx0MrLG81*0-$b9$#BuxF4wrzk$DNVj&dtQ7oU5LtGLD zlBC8~c@{&;Gm=Ag_#WqYV%rjrNtF(MLi9*`dLd2>)XF*k6IvavyOls+h|6ukOAwYa z;>02D;UzH3A?ticV=Wou)S1aC*L`yi&jSWLpbEE6%* z=)Y==y6Zw)@en7x^5W)`qZPi9mx!v;2Nl^lKNT;(SN%Xj-SUve(wXaN3OphzI4OG1 zAo|Y*b(sGJqz-2&&U7y~Q!^qWrxnCg?#U=$dwPX|#Z^Wkz;0~+?R4ydFi0L=B~Mbq z@hS2M>_9Gre$SffvjySqGwDQta}d1H;id^7ZVHLLL>20Y|FZS(1--~+8Ef^w3HyeA zt6TQEO!a9etmbm;<@NK~S;mru4I{C{f?xw$mwgmz&F)@}l+~u)Rt1Bh!s*AF<0&$P zfP%WpPdINsjyd-I#t{3ACL2PqcNuf1D+hBLa`8N)*2nRa&_mIS3F?1U>p2JpcJT-= zgDS{ip%dXYsw4X#E+}n0ry=0(uwuQ-i|sL%=XWmQQRR;KK4xRRtY=cdK&>f-!#QQ? zH^0LtE>d4zvz?#1INi<0cyEgk4b9Ege+@tfmp9_|?>Hp)&t0WLI5_dFRWvNU-;K$Fnt?k{1kR3~)?XSc zbbQ`F^!$@BiDRfFDQl2~XfROxWruP2>t2w7SyJR;$@H9EA8lB)wVQ$P{rH zv6rDl@j`;lBA6&pNk3G`=BGV2QN>}=YOTNw3Ah(iIGtBydKg$D*R*sP0%gPnC4e4q zGGkOfOPjtVcy9RbWd-{7D&SplC3ItTDqlt{0Z~wD(7OOZGZ$nGCoX+A1KI1Gt*yDD z#xW4lwPB1!dHJOs72dTbg5Fv6)Xl8yw`g?3oOxl3%uB7SPqCBunb>M7VKcd|-v>JM zA8s7XEf#96jAO~0ALhSY0G05DO+*q#Zy@5@(`Tx%4{Veb(P760VxQHj-zl3W#C%HC zhZ$XC=eN5zbtxKLxXj32b?}|Fv6UqA?sVra&{TvdqlaG5`Tq@xbwsbT{$pdNyxTOx zG3ZQVYw*pa(+OvVi(jWs#xS5S`yrtI2pAAz@*4!@iosXr9 z=U79_66MH7Ip4(aj2iao-AMbaWIg@R1v#5ZE%whl)ixL9VbA;T%=UR0w0Cap zCDod?d^Z}U4nP-@{_xy$lg8(%sk69=`TeM{^y7U65=AQjzaGZ_rq(D}Ws?t`y)}GX zyu8SGoJJAQMiTm9pIJC<4gGIp&GyJAxY`wA$7G*3*FT*(?6fY70>On;Srr3=tfu>5{-K4ct@We~-i9hesStS_+~E z!zjY&rabl)h40wSZmhz5ugoMYTk)Dn`nq!GX4TWZHptET${+iA9@5Oe=xMgI*w*Rp zu(D-z#cCni!GY~UM=uKRjBAaRXmv596q>mdHON4cT#I_p=+@+s7QJ8(=eOrG7B9%` z`zNNLzkM`SmOirF8y0C#mXNQ+BRRf{1IBHEu?0?8|7XG;-R@baaFtl@CP)56_`!X% z^k)|!r?~EB^4rR%=uWFHUvKQ@)=ZQJAh@-RL;t0_H3N7?XY1z1Z*o4g~v-#*s98Y%QGhz8{B%@ z#6S&Q7&mnAGj2Rs3V9gr^r2|h&?aO1(W15-a{5V?21U|DMZ#g9!g~AEOYk{4+JM69dKj-KO9{VLI=e?9ZILI$uP; zcy&C<(n@FGA5d9rxEWu&EKnh6(Wx*lMUHO>S}i8|!PReCYjYOLyK^-p)7SHX8Ygz_ zWRaa-Rv?>0K(zV7wjb!PyF438qu`R_DyrBhr~!BSP4Ta9|2Zz^4^5h-AODpN$ckYl z<Tl?%+U! z{XIqB73lQqy>|*Q)?i#utL!cxVI#BM*fxhC$tIT#voe-?C34&cr$LFWLD3wUo$9eQ zWmbO9XU{<1mpHC)ekPq_8x0Ohx7Z2we( zR1arQgpT<~c2B>zDsKdvm)`Q-3z2iCzHr71mLmUtEcMxUU+<{hc8uVyre5W)B2VX^ z)=I_wB0-2rG-Ipsd~;|PYGaVC6!ylTC%#qMTTnOP*36Dizx&z2uD#fPzvDLj(&DGC z^p9=cHrOyf1L(=|{nNJn~EzTQ3RR~-C{{)c_z4}$) z^s5DE$bY?<%WEE8eApW+P>_tt`8xeGp#Y8a4u*l|(P)N9a+AVu48jjyQ`uE;5DPD* zZ(UE?B)zMBMdk@r@JXoSR?&m6GIpll`h)b-uc!643}rm~6j(0ql9_@r!OETQ3zu83 z2q{W@crXw3^MA&PiTyG zE-{p49a55KkwLi=|DSnWBhtM866PKP&8<3|^Ubv5107@v3k0zV`}&n)Tt{u1KX#S> z?}dGQ%W(d(My!af`ToEIr{U+{<=BN}V=YXfQIHB}kFLhH-qeLN%t4>kX;On@^DnT= zv1HTf0h_U}tAsS9eDpqUE7*T!HPuN3WqBS{;h(yj-KOJkZ;dj-^4w{rrWk(+1zhV0 zxzOXQq*QoeQKx%_M#Tbx#w9{Hi?f5_*nk|4TE8=0tnWKu%jtJXQGla+ikzpm_o?rE z&?ANsxDLjfVYyNVYUYR^rm7*ztfiTO8<6X1-U3Y@foH71+!+GC3R}|gBd%esvX-P? z6WBcntjl1kZ3R060rhUUslNd}WlN_M6jdXv!y}ycr?0*zKua)2b;MvT%ib+Wb}L#% zXA4}Cm6Q69uqPYh4`BReH`TsC_Q1=T zGbpa8aN=|^(j>SbEUJ&xb<>6nlfT&(OP_2{1`GYRZwj4c&z^|uei!uK6T41N|E?6! z+Vl7%EZ+wS0IxpO)Q^=k6q);%!)8$CcOx;dHYqzsj_P}cFz=_kQq$FimQz_C8+v>R z9T53uc`sD|N~;_^OKqUGkd*Kk^)RWV0w}{d`8iu%5Qe5)KI=*|g;sD`GKHZQZZYDs zvy)ncML@vXpzu%>t9WmIZgVq7v)9up5J~hlk^gN*9MMSaSwrjxADkmj&#j4#Z9+zs zzH;BzEGq#2(>Yj%&t~@n&zP^fo(S8=xV>T`tijQtZmXn0{(X$h>blFUGjb|pHzl77 z&ae(N{X`%hw|;nnYKQm`e-avqB`9F;3($X&qK21TOv>Qx=*6o2)$N2|y9)b@5v98- zs-vvCB=*|W-!w5$J>YiOebw{Co8y0X*uENpH6m~43;F4xsCxzB3vX|_&Tv1x^ykX0 zT;3!z9`n}EE@LgEOk;H$ppP7=JP@A=Z4)Fw||r%!8X z^|3X}D}!h~6-5W!k;_O5bHZ6p#OV<8vio{p0@) zaHAH$f)2oyF#4b>b7>(Bq=%?~pBW!gG(WIdda@doc>38>u8q6G>UrI*9`h_66PW{r zUG2wK0g}9T#ji&^%$t1kUk*!&R!DodN}KQYC99GC!!z&yX&1 zvYx8rEjakgONdUDN?m)(mu^skZDCw7TfiBa!l6!@H0HJ0b;RAe)7=Vxxmfn7_(+lZ zG9k-~o8+~jFQ=p4t_VBT4Y@5UVi$u}z?C~;omHEA@Na<<-vLyyZ>wjiZC{$HW$jUz zOV{nt5P#*ThqGnQ*?T8WQx*LO4m0J&MX=g|z`>zVPS>@8IaQ$8N1j+`n<;2NQNlFX z) zRoF$;y*EL$I=meSW!W+ql=9qd1+yS0yMfIZXbiC}8qOQ^ltZl_%BU}G2{hcBrYeIk z`96G^<&4+)-?rNgc+*59{gNoh$!CmC3H<0q3BU^6za~L_gn$zLwc4Tl8;?@rk{JRT zIjl7GKkPK*y@F5#kt5q@6$_};cB{UOy;2uxk|d=&*RFZ~Xw$qjJudf2ffYq=%@#6r ztoy#h@!F?nfvGT>0BhDq{1dHC6}OKjv07sL1P^yF78Q#0dSNVXO|FaN+QLz^uIdgo z(A-(eYb1_~RBLg<=OXmVdE>WvdzcW%YM+kP!PuZ+KM z#)aXWAlA;iXw>c2Zyo*k(`(2wHe*VyuwCZTv0@yLj)f}rbQ~fG9dKc@T8Hw9D5xdL zDKi|yEVYsrv=$E>!HIkZ2T!>1b58Kw*~8v*15q+lQNwPJNE(>A;?Z9k&)sPrajUo0 zmb9k}Kqlab*AVsh^~Q8C_d3)r{G#E)N)*Mz66V&2_khK3L!&)0@$4lHKg{86$M2+{ z&u8x?XTpiz%3CxGA$xIL=a8%Dg}W>+_>@GwUvaxvFM`!pV2=cn^}dXR+&%{TCVfW4 zpyNTy*I!x2ojBy=99Sjt@1)*sJ(m1=qf6>N{<5%Z9@_Ro8ud>og$}reDVv(Pzp*G@ z*szhcv!eGp9Q#K_CF{BOm>tHjP(A;iDFYJvLXp^z@J2PAX zOLzorw%pZDt|Y)*g6#}L{H5}Q?8eI+iZkRKwfr?kQ$f!&0r;qEuV9s1crb7APasE$<@$UBh6zKWeb3CG&Esm56ik89ZO6L zssY!i9(0HX{Ak=e=GPK`obnKJz#Xu1*VkTCK{E8M>opCtUb(Ta_BfEWJkz&cH$VSUS{%8rlx!>exfoyqbu4N9l#XjiuW%{Idd@sVhGi3N>tud>vYD)zL zZnA$Au&LQzg<`Sk-zd!MvAiKe-y~OlIfq~C2U{C=)gLn)k(yOGD8u%&^=)s@g$=r3mX(--#YZxlW=tDM?z=RVa(R*Lrb21ic?6)yfx*Ex>9G8mHZ0O%bR5fC|)pgw8Cfz1v7 z=|mgwQ2=bl(T!61p^9On4_ev_^HaSuLg!-HG0@Y@&3iIu?`3dzN1IcnRr6ltX4Ce= zA314-lP_LUK}YjNcEbvzFGtku@O8f!p?_^Wt>{O7C2%40d%V{Lb)oQ~KN2c}S{NG2{+00AD~3q6D@y(r1{-+SUB zq`OY4gPZ_r_+6e$ixK)Y8-9@?`78gdL}mhyO$+xdoxC?zDx7(q3jgl#Y=iGuL4;kY zzwsP=pW`GFe6}091G?`d#m8YEJp!0S%uPh<_qh-6maCG?#|(HDt#SG!*9}gXjip={ zKW=U39=1|Gd~LbD`7=o>$tp=ylw!m8*w_z@N#5!XR;7lyc*&S9SOcR_f;jXWaXmZs z8?5h++uJm^5*l4a;ugY4`{C{7$F~eq1BUUJSI*j2&8}RdQ8=RQE?0N2igobBoyNjn zXxZRDIu$hy0pU8LQ3}LtU>wQPc|*D|44_6g9>xz0S|C(U(-AZnxzusiET z-{H{pYQ@x}S$IGdo#BZ_CzKVCg!EA8C>KNDy?iko_v}l1yRgnrq{FL-N0!?LQ{Fe& zUsIfEpL1Xr%j6*|O;6b-$8tnMvh>NJWYnD~EdQE^+v6sjCkE%+^JvBOl8*Dm7sX+PZ2GT^`xcgh{H$;QKCRoX1H*F!+?UTi zA_hzTe^W(oe6wNxVy38csWtSW9zENKfhoo$cneOg`dRxRTBS2X(zj;UAoKZd;~{f1 zd-L1FCxoL(^TUs+(&-%jM`G3;5e)6ZH7syONUD)v61(J^a$e>2*xet^W>?$4bp+fv zZ<{b!G%F)Oi4MkFvFU$^QUjmSrJlRw6g&Fxlm|0PM3VpQ0>D4xWRhK%J%{#vxlkr? zP>!DjQlv@l2>1vVe))_+4uKX8#9{&SA{>#-<*bncp}tNaXg=5-4Hpmp#^b>8{`yOO=K8_ z^iOu8fIrt4%(3Zv4nF0RxZ|y!Y(dDlStCpc@YoW5_Igh!6SvIQQ-Km@U^gxQ_FCv% z6vZ>spK&_x>U1VrNX$y}na;O9J6ORvV#|qCyXD&ugs2nx)z5mW37~E*<1~96UxMv5 zvlJ0^2*}4jrmU5wd@4xnm>364{82NmMYecJa79rm=~2`~5C%B{-3~gkIC>h5h`B_+ zaM*Qsj)2uT7hn3%kQ{9Uz3^R}E$mt2gLzyiMfWjPJ$$p`r{w$%Sw05)4Y0YSKb9gf(!a2h8g#`wl7E8 z87>h%sQbrboT;ldM3%EbnKgCLoLOS>CVLI{)~k^F*D`kD7S&%DRRHPpLdk^1;}b*) zvI-i8o+>7&BN{903s{@8VgPGqjSCBSaNQf(nhAwlo-I;9 zO^AAk!~T}#pVp1t_=!}fanC8Wo(Qz@`tIq}%5iSmJH@<@v*jpO7o#wKniRiNvlZ!i zFRMC3$d&hFgHb=AR+pccjN`KHZAa^f`V_FQ{>`FX{_8fK*U}~_0!+A{y{rj6VQwIE zz{e^84!S9ytg0D^pcl7dZR;dE!zzILG2Lvg>sxA(m>7*JG z^TXF}_|obp9^c**k+cUg$v(Gee`&0Bz5N zOwmYYQN8TB8K7!(kY5ers(E(C2^IC6^Y^?cL>beJowe_i|Gwu6-Crd0>BIkVnUb5o zZ9P0kZ_3{Zv0=>tl7PRE9{1Axy82%!lA?*Wf5JCF}Q0r8yvD>@N#=g=qD%?99US8LQSdZ}Kstp?XR zPr&Z}Td!pUmTNKWilcS1hUbb9^8TCEsOzHI?V!K<;{{F*Rp3ZzaXwXkk zno3($&!9u;!Qj=FV{dj1a1HP{OC2j}0SLVI46uIzx-UZVU zaA{hZM!~?bZe`*i`Kwov$EX%m#CFqmfC*Bf(j2kzhMC;}Z%IHkm(|RceqzT5QGYX2 zELuQYHsej2o@VHLeB7{SPT(XEPj<4xg{vO<4?I#$7^3U4nGcE0SRY=^rM&h0%pewe z+vBn(7#F^w84Jg^@2r;(ZyX(X&&~=kekC%ci?^rgFn>667*cIoWK{{y> zu{~RJP7%L7UfkvqZu*C_J4AZ&XwfRRDTrpb@an?HC&_(~@3iYUSbRA$e%@^aC-BT+ z(JLsUn7hCRnEn*hV45?!$viS1t>9O`)^-f+p8zMG%@)l^r!XNHwqbB9x4tH33K%Rk zjb7w}SK17m)UDW{ShbB>B&?31PEhF1*l3TTv4!D)!l}7nOjT^L0clMBrk4{MK66sc zRoee+9oSc<;i2qW2|OGWL_$0p8FqKE%>8XNk<4iwV`I)aDbTU+VyZ76|2$B_4vWe4 zGEM#3K%AdRHxfKUp>=1OSgUBn5nPPON^FY)BWHhYeF!=Oz^29)?VsF=V;ra>y^4)O zn7kAxzIO)GpJ1&zaoqGiM^F?qTt|@@#@HpzyBz428o%~AB_t62ES2w0H_KK^DQlYS z-(T-^>wBJtqvWf{haZc@8QPD0!ci_WH-mtW{-DtyZ=^F?+xDg5&;qDh? zgv#EDVh(4$GumSY-QRPt^hh!%nr0$)A_m>J;oax_ZM$;4GxIbK>ptanll=A;Zely* zD7x97h31Hvh!?31*GSFP+Q96c!Is^Jtb5~A(6Zwv9)o-xDrDk&Q za1cxpLPC@^f&{@hHTX@v(g77Fssn{y0%(9OmCnJaHf5{ykD*!tg%N&hS!J$!Iv1Ly z>7Sb#Gk)0e85H1K?0&Rq#YPKR|Bv1%E*Cm$7Le4jRMz^MPOknU3!Rzu!x zss;IbMl$Z`B?_e>rC5Gk=&a*jUlo}nDUav=Tnm=;TKX`828#Vw=uL82Om?Vb11cX- zxzvtnb9v`{Yn zHy`m>(v!Feh}`Hrfp^uWsWQ!bmAwZ++@hg!AB}|@aG}tvV9Qt{*Rkbt=8TUfB{;$R z#UX;CR|aLawl6d~@={siXLnryl3g3L;JViY?v))lNB{^4E`L>-H;Px(BE53(2T6#d z>(gbUH=2gF!Tfyt!zbzlVUzm^c?{O7p5@*GZbJG(D$q|9vUuSYh5y7TsN3!rK&;H% z@||_?-|6_@Ps6;UtQ*huaj z!@B`PkEE_aHsT4+e4`1v$`Z6tQh8(JSAzL(V#U`Y884|zeGLxq)7WS&@nC4LRrJT+ zZdk6I{qd>|k^_e}PyGn2`305NV(yb$L%|PARmO$34f60?(!^0J7({C-UK-O?=qUls886BfAKkvSouX^@z-}crc*grIR z!m#q$i4rs0+IgJDJM!j7+P@Z&BsvPm=~o-s-E$2$Nw)SkMt3ksNL(YhDzR+F5nv45 z8us|R26f6tO3Y}^3V9t3s}*0Deu&k^60q5pvzslhBRNl7{DTF4bIAuj#_@c?R_E`I z0eO7Wuui^XET!-Ca?@$eN-wOYWLMjlWiMff;kQN^SWSUdwUr`?_uV)Z$-y?hMDT zht97&Ocq7ZGuCCVa&TVCepOR$HCcM`3f;ro7E8RAvZuV|r?GOZ&D`^E)-vIWMkDa(1#H0B zL@6?Raq{;PY+*8trgq0+PyX2Ar2d}%Elyeu0Z7Pq-`(xH6WT!YfNd>@FdPHJG1PNl zL&|aXp}$b^<=A8m%jKQm(t=E%&4%&^gd*X!q>*8;ob;wqD01ShS`A#$|9f%f36h32 z!+Z6-d;|_ETE0K3|I+#WjpN#Gbog2-;?9dKf-js5{%Lc7zgFzS4H886+la%&Nto|B zbgxVO)N#jx!=PaV%RtB+50T9poTo&?C!yPr9Z?(%n>p7G2xL6+wJr(_uU~=`kvzEe zT>U$ViPHTmK3t=VuwI7uHxhMc(GHvn=^D$gJ6~Q)jpNaon)Uv!%BG`nbZs`J_kJ3~ zaCtpK=15|cT zE3P&7e#^%vV^a9EzS{M4ltk^NRb0{jy_SJC^n|8)#>7x?$+pdhoR+IaPch(o%sP)H zT)JhKD<4!3Xs9L%dylO0tXm!k8F_s&m;xUq#Q?kMnRiiSvYjbA$ZG5P4kwrnPv(wNMVWb7|rUq!zw!IiPe zUB57zOW|5 z(d<=oUe6NBYv(saRySEjs=r3m$r*RcUpWqMG`ZWHq9B*rkoGKsi_AzV;8xF}w^i~} zZNsovyTm?(){Tql&afeA407DO`*bk*4i7T$J1SupeH| zv)}8n>=0H`;@Gf;6Z~K_l@`LbVvw?Xa2_1$o0RXA}E|=f!k>&+#ggJ5~IEKJd}EHgEZ(<|dp}zy8@vB;!GSJzaR^ z;b~8g$wOjz4e&?0mAk}Y9b7;*@porgXA)2)__mdfc~k**z?~=z{UjN1Su%d>(9jI%wX6Q0qP{bb&G7BMRcg~3wMyEe zHnnSpqA04UU7|)+?Y*V8nzd?+SygIpDz@6A_TF11W{~9h$M609-}hs_CApvbxz2U2 zbDa~&FV64d7s@!6s&}25az`h@w$t*pCCLhX-Qyp_b5ggp-%ts&e%pS^`z}5O zmhxAIQ>$$~o-8P?6;N$clSV&ZvKSOeI`p2?ol{;bUq@l-upDT|XfWcA zAlAiY=Oi{2+J%n#JDLiuQu}zq&6H!w%@^u&p~UTH7wE0Qr76Wb2{8HK~CWa@G4ZMLrx8W3hg~)fjYpE z;kO@%t_^+4p4{+kk_nu&4Cc-H)9;e4 z58s==q@ZdF(PU87>G5pYLuHd()_p#5`pJ#_F66sbl+XFtstDsgc#I&iS*(JFs&BON z^xu9^la!{q@SbO;aV+UDB%6>5yLCD|0W-fV%N?2Z{VgE%6BCNb$0z~+dq&+M#5x?q zg>?X0pa)kTEzpac~T0_uBewIr=_ z(w5d4v?xU$)%gW0(9L*fJ<;40ib+Yvq;;v?^+KOSJ)VGaE9_H+rGDXf%_TQ? zn0;O(>zx=-!%9WcTn%cN%7sqgj_N#S2lz*|^SlFKXAJ=^;)7%GEP5P-fCyyV*`{$c z@Uc-4_;LwqQW%6ThM&BAC?3~LFTOvn-{jxTNKDiowM@2E#TVgcy27!`w5_tiDEcu? z_4SkPia?zX#CB$?sd@z!EUU?1=r@Bb&njag(&gb~u?g)ZDo#2G^{gx0-w_;)= zh_W?9!5i68u9OtieW%6eZR-;W@$b~!HgtMMq=j2;<;ga_3b?n!g=GKMCZj(1l?hRX z94#<&A>Q|CW5O}N?UnxT!U$q}(5yGeq;wEB8f8ZImQg_~b%k0F{`J(?R?Hoa@dT^) zh>|_h2~mF1Sozk|Z&`nuc~2h8zGIqw;2p5(BB%hOqzcq2 zDSd^EW|+CvH;HZ;&qT_=UPziSz-+AiUUl%T>+oZWsE8rTqm~#M1>ykLK-OFb>E{9? zT_!gHNI$#XLB&m{51{)$^H+@N{-6?y)D!Kxx!-%=ID6H=x65j za;Y~L84h&+EU@=a%2T16DAp~Q?1wJ2@5-h4Z;s{7Ee&v)+X=7s1xM@~diQcbGd7k@89+D(U6z=SaSOw(lA1zO_E9t1G_Z@$Xde`W~R( z10SJdK-CBdzheFb@h-|!EDq_uG-VXUw#^9?Rgz)9+}$p?PhI%^G$}04$2$Fap=WVa zvPBX#=*34-CXSUXq;nv7!Xp7qLlsRn%2mVR%F(CE`~h6B+5+X7-(MYWq1prjb-x&W z<*?&t<=@DzUqbZ>{V7%N2uVZ8EN%{<(quTQ{E=rL1D@XdG*uRKl)lLo_30@Nlzrl} zV;{qgmgfPq*-6-&>d(s&Eqo@m&(Lxyhz)pn1p6Vp- zK;M;At2)=+tb!iel59y?;&JCrH}08o^eBh^JE25p(xCB61Holpo@Z{xznjSU6gZ;D zB&oBvhcYP7Rm^|lnVOCYN(rI_6RrGLGs}#apch6h@>#LGearbt)D{dby4mX|UhDyz`fvXSv zpKd{2iUfhsk%&wEL9l4H%Pl0zwPb5Ky$FS_B?Jkr6UF2(J$4Zfvg#RNtiD}Po{8+X zqu5dTzyee2b#bF`O6&ccdVHB)^Vg^8ond6(Y@|Ht3la!^?n*H+4gI;36Yu)8i{7R{ zn3U}C7fn6OCr5H0CXuR~#3TZFd9kpIE;X`H%B5jC_Ujc}h5%AjSDryaj2-jum0tBK z4vk9CEqGBl{%Bk%ie7GIAYyXwe*rTB{t(h&a~g%T=IH@&Kkk2SKo6R!Q?(1!84E}K zI%X+#@ckU&Ey+kD&4VXN_WE7Tc`oDM>4%6*RF;Jn`4;cct?^#ryTL6~bO-T8dXbv> z`;iRYh9n(nciBdfnhPGpey?7bcznYKg3fU@6eEBh8D@ZX0AT<*z;pv4QOjXjR_xCypc}`#pl3{uQ;oHhk%}(kD>n8D+ zcZXl^jhI!lO}F}=C@*KG!nMBXx1{kqq%n9uyQkUm5Vk4({(Ak|B%*C(l0t2xm-mhG zduW}0t-;|eKdxK6`95GYIkwSv7|t*tl=P}(;gX|s zXMShVsVg5}+G6G4u|!$?R~L-d2~EjK@g07<>9vr*N+#R_lx zJBvb&Jn(PFb7b3GT(t3^x{jxNFs6Y`?}?Y2*-GGZ=gc9DP zQ-sTf<*}NEKldRWLdZ(ig2AaxY-wEV_#_~k?fZb$t$S8}Wttd7crf=Oayib36s;vA>Ke>K?;(_MP*#8R((2GX- zOiS--X8%S6W-g1fx9*rQwr|r0kQkGL*h^b;ml&BCOUo!I=~gK4L46znnw}{@fU$l` zZM~;~wRL!jVq&1;l_NWLcnXXtsRx)Ru^rg=PzhR3u23N6T$wOxsWJ-#J;gnVW55EQ ztgeFZimDB^US=CI2IK)ZU{aa%;3KduL+RV|t%W>VVJL_oZY}!Ag7sF2%4sl}A=g8F zccrdd_nCr~B%eX`=Xc+})GR77%PUE89qj_gM;Kcb2{Ax|*!>7HX{yA2`LN=Qkx0>m zdL05%`k|P%Kd2B^@KY7$E${CGrMw(D=V?p9M{l34p1d;dD2g}8iym>Qb+4TgF%JrooIQko&_dwj|dk=+NNIc5?wms9EQTPUVJ;1+)5Uki4$Fh;Lut=#+xn*Tf~yo zq=kYZa5em6#1Jd-(5l6k^M**I_!j8nT|wYQFRra?xkGmQD2JiwIwlp`Bs!}4vm3%_ zVOgB@9MERYokDcEi~C6~AL)$_nWf_$JLKcpll0pvt?*YQOrZYOMtVKtl}wu4!wuX! zUY)#XSYr#`YCSHT*syJgLKgmaCQ9#1Q#_d0xfn#c=7d!WPSpBqc;d zGng#M|GRcY^wA5U@9Z&bc)l+iTE5!yBMkMJHt{P74~DfM?>X)qkI5ZvC*YmNu3t$^ zEAmV%q4)_5C9J+ltg8)&jF>^yPd+wZ^s!urGtD0)*K*!L-`5aTajX!`-zzQ7?b+L7 z|KjfMVQi}$S8I1rb;g%9HOHT8f6O9!o+hDjKGkVkT4)u_A`c>K9Ux&b)675=lw;MI zRiLvvg{oO1%pusGgM{SfI)aC^BRP!f-cd=V{AtjXeKZ0Y8~!z>4DVX zdpx3lf-b)xoO^;6m{%MGJl~=_N2|jj0==r@dd-{d7^Dkj0IjsJfyG}%vvg4B2NH2i z^n*xR`R9p^9me6{y8W8Zr7%3!bmf^x$GT2NHy6+oe;Og(a0P3Le^C_^}W1h2oaBb41rOtHnX`tqS(|ilA7<^>@G+d!^Q{G%2 zE$R1T#V@iD;6@l~jIdXFI0Vo=`GO=YOZxWb@JSEIC+R?8!}2eh=RYkfwmmz(m-dM3 zm6o>SvR1C^6+N^+z+*ldUehqghIcef1260ycpq^0&mt=SPQKbYuc|!fNy{-kTmL0% z;A#r@&==jXP~DKt_P9V!YNGz>D%uYTriIkLCK58pOD!gcWY3ngBDQB{@10ap&)Ind zwJDCjjL3aE(%H|qZF)-FsiYe1`f6n8f|o}Yv(o&8jV>~x`|;|@8$$n#jNUo@a+_)? z&~8@@Q8gDeqkRYJ?#9#eTh65CGjX!B1*eoIOJQ%FKCAn*JncK3OQ$eC|I}=EF>H7y zc2xS(7~V?YsfJ(|-whOE_fypyU*7*4sJqLKAs)>0hG;;i-%5OT-Q(K@p(;Mi+`Nvl zqpU3@KPgi%nuXimaBYG0vJL12Y$NF_3nuQV0-@VxIDQ?R(|{}emMw!*1hs^Z_BJ@hb~(z^OEoq>F@9wPs+Je z9+A~yNVcjz^6~ixIf{P?cbI3CRDxDsxjy>-G)?i3-P28qkpXENHYn9!uDu=kXQWBU zz7Ay8>ynwy4r_&ZENUW;Aub6(lzBAokG(GS%FxIV>UbJE-Md;CBkp}*o1;=k7u%wm z!$mWM_v&3lKu5#fj5;kZ# z$9m{tj`pw|2jvuBJaBB!4W)eZM(;wYf$yDA_Rv80kke(-05XkuT{h0j`xHiY;`}Pn zYS`E-VQW}Y-zn5h_tC}RL_1ior#Z4bB-S9C+;m{rbeOcz^?R^9pN&&b4A%lN ztUZECTFVn8$9YasO8TnLEQ@E=N6}=&Q7U;o-;X=x-a~=a>qPEEG37<`@55Py*!*^rkad+9!hB$A#qrak<#I3v71HIs z9nf_SYl0sL8eDs>ZLi?O=MLAMAgr0D%w+*?R@8C6c;W7WI^Fd^6LZW|_Od8jsCBj1 zKppkjKz(@7Y%Py^6zn@Xn7kGA1EQWnPK`fS1Z1Z=TFL3cogY8Abypch|pV- z1kYw9s*budq$F90gIrgy*=KDBHU=$%4uZSXzn*Se9JrswoDVUH2bshi3Rh~~uJ>|s zH$^f0(bYCBH@VB|o08$lO)UCJF-zn2NMU)+ytt!7tvF;RI)m2h;9*?ag*DU$@0%6o zsP)yAvIbz5vpRa{0Q@MLQJUK3WY?ChxJZ zt9_>}Xf%LgFO!}wBt&e(yu;v(+$z7$mb2J`$sIDyI9pV`$tW12ZP#)iQmtxUs#x1~ z1kFhEc9>E-9lJi{lEp0UyO-aD-`Uk`Un`9ilZ$2#-y+>@nGaCjrJ}Im;SoTW&nWHSXu-DELMOHX04R877S3OgNVf zPeu^n=4xQ9zq*$%b^7GJCH^8A;k`8*-KlaK^plTgtF*px!svCHJY~~Z3I=Je?`$9k z7CWJNeZu(kU>;_}q)nyBEW*z?cCAMsPe=O&9V%L`{l&sc% zw~d43tSI2@Ov7Wdd|D0mn?sa)pn7qpKc}!ZUJCd@I5}{&^6vcAT%f6#k`aI7MRttW zV2cL)(tpZ0J@B`xv?VK71{cpc-QCJIG%j&NXxM9{JJ{-31$l)jpCir)vZx#ZjRx!e-h-tX_i$t<;al;O7$mc{PDA^!b~=~|uo|tpiZBtT zp12kNI{{%hfKFhqyLVgsni|9ktkKEF6+K~OyHa(O>RUo>iO;n}Z5jG1H!u2+l+(IJ zNzE^p{eXTde+w;|0VCd{CPrL1=HmbJKgXRbAJJh2(3DJpBU&C4XjZg<9m_gsQF9PJ zL*WsO?WK=I(qK;ml~rPje-qjyfP4_#uxl=oXZ@U#(p-q5x}zGhSQMv3-wcKYWkTX2~=*ptpC2gLZ8%nMU;5i#m2e#Bc@9Y_Y=r&L(kGh6*|hx%jto4vKH9a(I5oD>IfkRqGXi2a0}qyJf|EJh(q) zA>hV3L-@d^4-UCz6<(FfSUTTMBtdWU497~my%simhdywvE4Iqd;Y?Hv`Y1V%huxdj zo6ReFFdG*0?9tBE6!1NU$-XaP+rE!ft)dD(c2?Me&abS?0q?@RJt}`0R?pc#x^p@O z(f6NQPS-6tnC%{!vE|WcKXsAME65io5G|;@(Q0q#LGq)a;DXZ$`aNXT9_F0|6XSMM-+P zdjo20^MFPuC9u9Eh8B2d9Svs0f##S?!ueZ%7K4C@TpL-c^63}Aqxe$5Hu4|o*-P;E z-T{eA9V$ZKOkwV#xI~yj7>OKsN|-}w;RFWrPA8-qE>5*eq-yaZpUUf%xBev3)s~#n zp_NEpU{)9h7W}TRo%n;h^{e6*%5=ke+O$N#ZX{hI-#|v4w=F`XWj}{HnH`H;dHO&p zgZg|Z>K*BSiaxm;3R!;R2>1U<8tqf+t!}g#R#8@I93dW3o#oY|bhL(a`G)Tw5b`|P zEN{cbekT2q%ubJpxWW7$#Wne}B2AmF^;MLO5aQ8t6}Iby3~^spB9Ys%3|eO!2&sba zEv3<&>)R}@ius(OCzi*&{2A- z7`iuMbDpt`e(7D_+SJh#2RhO;YB1~QKig391JnDh*%f1p3p&}1Dc%5qn!cf!-)D|X zz*XIUd&?hme@4b#QDO0Iiz+AxeF6P|IeJW5S<|Qob(Bb56PF5%5?z5#;udjX#2RVk zKM&-;z@Ccc_U;FrQy5ximS1aoYqhMgBIAJsZZE=0WkRrsD*r zV}nTT`y<|L%@NGnftpQ%2C9(8pUkPuUh3Woq#KSri?ZqcQnTF4x6gZOI`nDW~`6B~|ljf|?!Gl1}*!#DbJ1%@NYu zJACTvGa6=P=rH+n`oMS@TQ_8K9OTrnu2GPONkPPOGA=hC;+{_GfR3wP;L|yz)m)J28YR?wH=Rm_HtNK@WYLMqj!g6@tPX1vrx4cba|y`qV}l8?$Kf zZY}l4^~!#*Q_i;I$MScYwo^tH!6H=OgM@_iJA($@#^+}Kq^i#?89!0V+wNoe8o^$j z9Yc=&c*1QaG_+ju6c*|4wdp~9Z`c{18mWp_S>=>|8m*YrCgcA;~TJnrfpAFtinOU=6N5`_p_n zu6D4$i~@@}#8pzo(bq3~G|SYZyfY_fN)3e%1pt>iJe&)4cKfSi|8uE{6P#7V;oSu@ z2>=}z^eS(*s&L?*6SUK(36X#HV{{gkI_uoaVw5DbR))XyFZv=_FTx#BYSGS z;(XdjG{X=^aTk8KJ%?}+os&M0K7dp}PP`3tZerb&Q@6@_{i{JiVy}GdEsOWFmNmD6 z_&F};@R0K8_2Wr*B2=J8nrpVOQ{(To)cwniZkG-n?hyl0^)66MLH|n* zTyd3omvptjJFi@3Pc*WhwHwzFJ1}Q4pXc+GQyt>csSbaz#%aqs3VklD>*;ygY`N6! zwQx-|Ml7)CeA%Z7HRt&T=S6G!t?77>utr+w$>LFOT@qwc( zLLOPgIg&=sCSa~@fuxYLzI8LCF2MOTdk<!SNYd+lp89^o(FxBphz zvO^sk4?H-fqYqhhT*lmU0@X8Mzi?!8=6(#`M%))RzsJ5#Z9X+c|5nOrPQ=;ouDrh?K#It%DmU>+Gl>ugiF?yFG#HxY-*=)$BPSd_zqXxCJNTFoWC!hD?*Eq$;FkK|yKZOm`{}YfM0!Of{7mAt z|FZt*5PInekAhJdb7FwCBzOg@6Lrww)tZ#+^fGcW?^J*Jzv-k;Chi# z-roJ_8hQ%8z;RG!gpwW-r#IS~4uH%C%ZwE_H2ZimLJ##AL?v49_zX#2UY@=d=O9I` zN0$DRba{x_Z6OSB!-kExyEqy@CNiCW;`ohve&|ni|I2xmXC%Qw6EDhd z?U1SdDKgDs9_%D+?OBTi^L%&v{`GF?^^Sn)in8Q`35&yq4_01=>_vVBDQ9&0TYu*o zv02!hU`lNV2$~pWkXmrNq?QlGAZpi$6~@LBHR!4(5aRQT>GjPYrYRA9&g~8R_8=p! z=-%6G)K`7KH!g=pe#tB%Bzt|q(Ur+(j<&YdRtDr8uoxyW15)h2PS&lw?nqHVn_v(0 z%k%Ln)6%u!JCJ{knDu;QISY$?sL4mv9f8_ZytqC3c7f8 zV&xqN9y;&_3(L})5O$)-mdg7gt3}3lsu!1L-S{L&qg2IyfEehezPrR~N-E!}#)EDD zMmTpqD(Bs&KIS~BOMG1?x<9p=|A~~1D=u!OWF~<`@?lJy-@nYpGJ>)%({wQ6NrZ33 zp2Ypz2#~(Vy5K5HOUVI*qlnxVExfrlz#J?vi`Vx2M1!LczgWeu#iHDjYDMw32VcNQ zqt3AgqfS%-|L!OZ`;&4`UAny}5;J>`?S*6&wi@f;0OvoY6KuU3tK877XJpAR#K?$3 zOF0O?(XS)aj{nL=40l#!<&STRqSUvNrv?rzEd-I-y!_CiI>=h)e=nhx<^R90-*9=JnsFF+Ji^bdMV z%Y^ngceEneQT6$r@h2!|7MPlMsKtfT^sb4y@4dYqPl4k%PwKP;AW5$|x_-5fod2`E zQ94l00Qs0cyfGl`2sf*#Aa0)@zgLJ9xS7c#;F)Qc=;l4N@Xd7DEO5~VJydyj9q!Ph z^l^r$X%$V2SMcE1E<7ysiKyqTa%VRZ4{|cbhvEk5Og8kqk;W|QiaD!YC-OzLW}2@; zvlO;iEsvjQ3iaUhrae)C2#HjQd%NT%Gp^lE?Jw@mtC(dH$bWdUl0Bpxo<-*SWX16- zDl5yxV`9R`ccan67+y>bA}CSTO42;C(g|>(QRq7?;?nACb>-GR9?TLLww&A{VKiOTWNgwXTuR7qpI9Ri`@96r%HKTX`Fd#_oJRN4T zFL}E@w^APbSJX$nI)+h{5j1d;!Jnjn;??cg?w#|yLo{9;p_44<(E$sFZ$WtgCI|EweLFw7TUv6k`Xj2xUtGACO#M+$d05 z>?_PLbB=FawR?a5gXRN$ISY+kc|beW&8sr$9pQDDU&M!(Hy?U%DG~=_ClDXE zAp&}V_WE^jki(Mk3p{I@2r0D5Mp6&Ok#4D0yxS9XT(s|)O)*e=_ ze&RIHL0;Rz$%9hx53LXWqHy{l z3iC$ayxmuIfU%KP{35&^u+gc%r+Q}3kC#ZgWVP2 zI9ax3$|~l>8KvQXT#@Zdn}@j`nE)N25KPG`2LTC27hu08Y^_hMfc|q{N{`27 zg4Tdhx1Lj9QD1=n6*=94MIcfj$Q9Nw2Oio7&7(7iqwA8WnCXEF74d^R-*pVU%)#Gvel2tbMPU zJ}cy{%hPon{Q@J|DOD^1Hd3VF@a7h=FsIa|wpc;(8Hdcc4NvlZsHV@#GaWa{2pH<= z?Fh{@RVCbzR>R{~(8@M4w_0gLn&zuoMPZbm}wb zApb+#lv&dTi8oU)56hszmyex=3^0TpDT7pX_1Hj&4fOx!+?&rB)-`b&J^&{k0-pe~ zHwbh5btc$bdV|`x2dc8(;|4Nt3#_0c&>ro&Mby}D7JQ9*__-+9Af2zzd$wb~mV!6){1n;+POL8%us>(Ph{+Y7N!;284u62pNsS)=&l-<8 zI#hsu1I@7oO1?B1gk#CJVF%o@`CVW3A*F8BuFsZ6XMWnq63tYi;gLw=1>Qd*LU||e z6U|4rfUmE8EPZ6-`c#9nnX2Mb$KMXybn(%emub6e40E%+w(?8Kk(4E6-yPRdXJhi_ z(G+0-op!c4cK*EdU6!5-@No?PWiW5gGhH`d;7l&}sP*Jr`CM4`Yg!-Q`nK&uE6u}r zfv2K%pEQ_Qa{tx+?wOopaQsv;MZTY;^r;>WBaV~SdaXq2P;jg<<9|3J*)8@JJKS!g z^JNGT|7IuUa0JylWAJRXXp3Mra~{W%Tb+Oc{+Jrq_OxOSaYkHL19n@bhIJPks^@sJ zH6DxZ)3el?(zhcReg7IS;k}G?nuyN-RaZt4_r-Hwqkh41z4z#S@57V3{*8Xvpi7T4 zU40(SQwGpV7jD9Uu9ZQ^|ABGbrcX~K%kc;{Gv^Hj`aj2|0p0|aAvzlFHt?2 zZK(LKpAELc2ax@00k<~s@QLdwN%)p%CC_a&U_C?t= z7)Zo{*SmHy(=QG4*%6YE%3&@X4ZO3Y?iKk&TPQcraoSyR?LSjNbatB zsGT+>0m1C5T=){(`>uapVZPCvX21v$SXWqHx32|^47W#0>P=mCA1+-49AW|Y<>$i(%gm`Ab zk0+xJ)gV`S_6swSkFJec!u_*QBoIzfk3pu`O5qVd*m~gdPJI4B%|(kc9B0gaD0w~X z$?xfXOX*r%Ol;1Xr~qdHkV_JJsel#k2itF;#_S4FlPy1n@@h0nD0!E zE)s1zQ>$996skT}Nc-tY6G5 zVAVl*0l*;)m7u87=%F%lg2STvhd8|RAI0sFx)j+8Zpi3@9?Ji5mu`l_CKJc)cxDG_QVeqS--ScKl&x5=wQT@+3B{oOYPA_z?f%RQKo zT-gxPhQ2=-{dnypev7&&Bg?m+C`sJ7%8ok7%FpIqTuU?B8d^UzjCCksObEakT&qF5 zayZ@2H@(!Ep~=<&gOlex>59^I04nLF@BS&{d`R-yuPP{RkeGg0F5pI)1GVQ{(pS#C zGV+IN78Zsc5X?VL1PNFaJ;HfZpoP53fH_IcqY*4&Ye2e*+nBV})lPn*TPbyUf@h5^@A?`*UQE4N9wi0PCnMFaV3#tDbP+9772n=vPB4Xt?708V`|wwH7b*9207tJ* z-s^>JSzDcZpHuds3n-aGxc^=v5n@DLqvMRJ6mzyDU69Lb2z{Gf4>rthsvLQ)!F>H- zH+%*gHUS6)lvPt4AFjgIpE$p9%8 z979g#>XCFV7iHxtshX;Rw={8DrGAHy3?;MDef)4|r$$(ISGe`3CTb<>);DxNYU3ZO zK%Guf=qvkk9p>c$AsySDj&0o@4w(8f9VCo%Rf>7HGWX$GaiFY4dhd+Vbj>U?sU-XU z{!`Jp5a*cG>owm0O6)~?oJKvsyVP(8_#pj{Vt{f@RF-+n!JUpiHF&wL%7DGe%iR|^I zz}{qu-PzAclD6L1MTNuicK~L0#IqCPYhU}zpvIp9V z9A8jJT*aM(FmQS(@U91jfUe>?vtAV4I&=^kE(KUi_fDHi#BXT9Rcg)JUnqqbrEM%a zVlQ(yJ+3l03YQ9$6uH zN5!}M1s3E}APl)J>O}KAvi|RVquaC|qN45%YxjLeH<h!!K$}T#3!`Ef4J{Hc>}o1eM zhbgO&%nnOAs6SGShkm6l^>!cIo4d)D7tNpBg+Zp4d2*YuRRRoVt`4vDr(n;$nM(hQ zs}_34dah34AE9paW55oi1Mo5)rPpTsPn$!%UT*wo3ly9I?LqTwK_swWF1O1Bfeo^7 zuQ8)#^ zyQ~A`CxcQHdse%raZU_z=8}kyHN`&yCXRIm2K8SMz;4CD5Ra@MWE@|2b)TFD+~NRe zBAeJKlaZmuMnjzI({9!31h7!*X{x*QcyJ0$0+Bd}I$zj9rQ_1Xr}9$vud5uDUt?j^ z^A>M^i;4u5E$JsnN%A>5*z}m?b7q#tkqI%qGz^O{#bA>&8PtU-(dYE@Zpxp@B zJ&A5sSql2X!&3jN4L^@?LD#cx!_qs=dU4|@uT6Rrs<@QvSM@n$_ukTZEK9^u7CIh=~V@A!?4DsDLHUE^h>u&ZuAHMMsDCNl#(3B`+n$l2tOd&>UuqV$qXo+hu~nq2 zGe}zY@=XQyG-Xs3?;(}!NJVj}n)o}+9gqhZZfi+L*{n#L@?tpo>Zy(^aEcwFu^R(E z^3Og+$Hh^Ls5poPGeCu$9UwPlrjos{aU-mN$FyK-75}E!3Ik$rI&Fx7S;ioE^jlNq lT*#Tj{=c>76BYpIH4|XNQM}TDv)Q_(s`Og1?1fp-{{xv$-IxFX diff --git a/proposals/NNNN-isolated-synchronous-deinit/last-release.png b/proposals/NNNN-isolated-synchronous-deinit/last-release.png deleted file mode 100644 index 6daaeee432daa757ba6d07c96c2a9fa6e96567d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 261619 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�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 cFxh@|LOx6x{9`u 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%0Dq{JUt22qywCkUweC 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 Date: Thu, 18 Aug 2022 22:42:20 +0100 Subject: [PATCH 2690/4563] [SE-0368] StaticBigInt: use non-generic subscript (#1736) * [SE-0368] StaticBigInt: use non-generic subscript * [SE-0368] StaticBigInt: use multi-word examples * [SE-0368] StaticBigInt: rename subscript parameter --- proposals/0368-staticbigint.md | 64 +++++++++++++--------------------- 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index e3f2e3652b..9accdbaa18 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -17,6 +17,7 @@ | 2022-02-01 | Updated with an "ABI-neutral" abstraction. | | 2022-03-03 | Implemented the `SIMDWordsInteger` prototype. | | 2022-04-23 | Updated with an "infinitely-sign-extended" model. | +| 2022-08-08 | Updated with a "non-generic" subscript. |

    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%0Dq{JUt22qywCkUweC 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 Date: Mon, 1 Aug 2022 00:38:30 -0700 Subject: [PATCH 2660/4563] Fix typo in SE-0365 (#1728) --- proposals/0365-implicit-self-weak-capture.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0365-implicit-self-weak-capture.md b/proposals/0365-implicit-self-weak-capture.md index b021efc4a6..c3a8c77cff 100644 --- a/proposals/0365-implicit-self-weak-capture.md +++ b/proposals/0365-implicit-self-weak-capture.md @@ -1,7 +1,7 @@ # Allow implicit `self` for `weak self` captures, after `self` is unwrapped * Proposal: [SE-0365](0365-implicit-self-weak-capture.md) -* Authors: [Cal Stephens](https://github.com/calda) +* Author: [Cal Stephens](https://github.com/calda) * Review Manager: [Saleem Abdulrasool](https://github.com/compnerd) * Status: **Active review (July 18, 2022...August 1, 2022)** * Implementation: [apple/swift#40702](https://github.com/apple/swift/pull/40702) @@ -84,7 +84,7 @@ button.tapHandler = { [weak self] in } ``` -Following the precedent of [SE-0269](https://github.com/apple/swift-evolution/blob/main/proposals/0269-implicit-self-explicit-capture.md), additional closures nested inside the `[weak self]` closure most capture `self` explicitly in order to use implicit `self`. +Following the precedent of [SE-0269](https://github.com/apple/swift-evolution/blob/main/proposals/0269-implicit-self-explicit-capture.md), additional closures nested inside the `[weak self]` closure must capture `self` explicitly in order to use implicit `self`. ```swift button.tapHandler = { [weak self] in From 30fbdae2f0936c87105618fdae04bcd0692ecb8a Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 1 Aug 2022 10:46:16 -0700 Subject: [PATCH 2661/4563] [SE-289] Adjust selection statements examples to match reality (#1730) Examples made it look like `buildOptional` calls are injected after enclosing `if` when it doesn't have an else, which is incorrect - `buildOptional` happens inside the block and `else` is synthesized by the result builder transform. --- proposals/0289-result-builders.md | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/proposals/0289-result-builders.md b/proposals/0289-result-builders.md index 659478c598..95bdf2ca47 100644 --- a/proposals/0289-result-builders.md +++ b/proposals/0289-result-builders.md @@ -421,9 +421,23 @@ The first transformation pattern for selection statements turns the case into it ```swift var vCase0: String? if i == 0 { - vCase0 = "0" + var thenVar = "0" + var thenBlock = BuilderType.buildBlock(thenVar) + vCase0 = BuilderType.buildOptional(.some(thenBlock)) +} +``` + +If `if` statement doesn't have a corresponding `else` block, like in our example, the result builder transform is going create one implicitly and inject a call to `buildOptional(.none)` as follows: + +```swift +var vCase0: String? +if i == 0 { + var thenVar = "0" + var thenBlock = BuilderType.buildBlock(thenVar) + vCase0 = BuilderType.buildOptional(.some(thenBlock)) +} else { + vCase0 = BuilderType.buildOptional(.none) } -let v0 = BuilderType.buildOptional(vCase0) ``` The second transformation pattern produces a balanced binary tree of injections into a single partial result in the enclosing block. It supports `if`-`else` and `switch`. Consider the following code: @@ -443,13 +457,19 @@ Under this pattern, the example code becomes something like the following: ```swift let vMerged: PartialResult if i == 0 { - vMerged = BuilderType.buildEither(first: "0") + var firstVar = "0" + var firstBlock = BuilderType.buildBlock(firstVar) + vMerged = BuilderType.buildEither(first: firstBlock) } else if i == 1 { + var secondVar = "1" + var secondBlock = BuilderType.buildBlock(secondVar) vMerged = BuilderType.buildEither(second: - BuilderType.buildEither(first: "1")) + BuilderType.buildEither(first: secondBlock)) } else { + var elseVar = generateFibTree(i) + var elseBlock = BuilderType.buildBlock(elseVar) vMerged = BuilderType.buildEither(second: - BuilderType.buildEither(second: generateFibTree(i))) + BuilderType.buildEither(second: elseBlock)) } ``` From ca58b3af1fa7dd2832bbd3733e0f9d5932d31f13 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 1 Aug 2022 11:17:52 -0700 Subject: [PATCH 2662/4563] More precise language about future ownership modifiers (#1731) A callee receives a `shared` parameter by borrow, but whether it's really passed by borrow in the caller is currently ambiguous, since we will normally copy arguments in order to minimize access durations on the original storage. Receiving by borrow is the more interesting interaction with `move` so focus on that. --- proposals/0366-move-function.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/proposals/0366-move-function.md b/proposals/0366-move-function.md index c2c95faf15..905efcf088 100644 --- a/proposals/0366-move-function.md +++ b/proposals/0366-move-function.md @@ -435,20 +435,21 @@ definitely has its place, but requires a higher investment than we expect ### `shared` and `owned` argument modifiers The ownership convention used when passing arguments by value is usually -indefinite; the compiler initially tries passing arguments by borrow, so -that the caller is made to keep the value alive on the callee's behalf for -the duration of the call, with the exception of setters and initializers, where -it defaults to transferring ownership of arguments from the caller to the callee. -The optimizer may subsequently adjust these decisions if it sees opportunities -to reduce overall ARC traffic. Using `move` on an argument that ends up -passed by borrow can syntactically shorten the lifetime of the argument binding, -but can't actually shorten the lifetime of the argument at runtime, since the -borrowed value remains owned by the caller. +indefinite; the compiler initially tries having the function receive +parameters by borrow, so that the caller is made to keep the value alive on the +callee's behalf for the duration of the call (The exceptions are setters and +initializers, where the compiler defaults to transferring ownership of arguments from the +caller to the callee). Optimization may subsequently adjust the compiler's +initial decisions if it sees opportunities to reduce overall ARC traffic. Using +`move` on a parameter that was received as a borrow can syntactically shorten +the lifetime of the argument binding, but can't actually shorten the lifetime +of the argument at runtime, since the borrowed value remains owned by the +caller. In order to guarantee the forwarding of a value's ownership across function calls, `move` is therefore not sufficient on its own. We would also need to guarantee the calling convention for the enclosing function transfers ownership -to the callee. We could add annotations which behave similar to `inout`. These +to the callee, using annotations which behave similar to `inout`. These are currently implemented internally in the compiler as `__shared` and `__owned`, and we could expose these as official language features: From 470bbe9b83ddbfa151a4b36de3eceee85f628b43 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 1 Aug 2022 13:13:24 -0700 Subject: [PATCH 2663/4563] Begin review as SE-0367 --- ...itional-attributes.md => 0367-conditional-attributes.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{nnnn-conditional-attributes.md => 0367-conditional-attributes.md} (96%) diff --git a/proposals/nnnn-conditional-attributes.md b/proposals/0367-conditional-attributes.md similarity index 96% rename from proposals/nnnn-conditional-attributes.md rename to proposals/0367-conditional-attributes.md index e42cad5374..4e5218a407 100644 --- a/proposals/nnnn-conditional-attributes.md +++ b/proposals/0367-conditional-attributes.md @@ -1,9 +1,9 @@ # Conditional compilation for attributes -* Proposal: [SE-NNNN](nnnn-conditional-attributes.md) +* Proposal: [SE-0367](0367-conditional-attributes.md) * Authors: [Doug Gregor](https://github.com/DougGregor) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Joe Groff](https://github.com/jckarter) +* Status: **Active review (August 1...August 15, 2022)** * Implementation: [apple/swift#60208](https://github.com/apple/swift/pull/60208) * Swift-evolution thread: [Pitch](https://forums.swift.org/t/pitch-conditional-compilation-for-attributes-and-modifiers/58339) From b944185bba782642d95d71706c078145fcd74173 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 2 Aug 2022 13:35:21 -0400 Subject: [PATCH 2664/4563] Mark SE-0292 as implemented in Swift 5.7 --- proposals/0292-package-registry-service.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0292-package-registry-service.md b/proposals/0292-package-registry-service.md index df26faa93e..0dc81724bb 100644 --- a/proposals/0292-package-registry-service.md +++ b/proposals/0292-package-registry-service.md @@ -5,7 +5,7 @@ [Whitney Imura](https://github.com/whitneyimura), [Mattt Zmuda](https://github.com/mattt) * Review Manager: [Tom Doron](https://github.com/tomerd) -* Status: **Accepted (2021-06-23)** +* Status: **Implemented (Swift 5.7)** * Implementation: [apple/swift-package-manager#3023](https://github.com/apple/swift-package-manager/pull/3023) * Decision Notes: [Rationale](https://forums.swift.org/t/accepted-with-modifications-se-0292-package-registry-service/49849) * Review: From f373381493ae1a624210591b78d17e491d539d3d Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 2 Aug 2022 23:46:55 +0100 Subject: [PATCH 2665/4563] [stdlib] StaticBigInt (#1584) * [stdlib] StaticBigInt * [stdlib] StaticBigInt: add feedback * Assign myself as review manager and link to the pitch thread * [stdlib] StaticBigInt: suggestions by John McCall Co-authored-by: John McCall * [stdlib] StaticBigInt: suggestions by John McCall Co-authored-by: John McCall * [stdlib] StaticBigInt: generic subscript, etc. Co-authored-by: John McCall Co-authored-by: John McCall --- proposals/NNNN-staticbigint.md | 212 +++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 proposals/NNNN-staticbigint.md diff --git a/proposals/NNNN-staticbigint.md b/proposals/NNNN-staticbigint.md new file mode 100644 index 0000000000..9b643f6f68 --- /dev/null +++ b/proposals/NNNN-staticbigint.md @@ -0,0 +1,212 @@ +# StaticBigInt + +* Proposal: [SE-NNNN](NNNN-staticbigint.md) +* Author: [Ben Rimmington](https://github.com/benrimmington) +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Pitch** +* Implementation: [apple/swift#40722](https://github.com/apple/swift/pull/40722) +* Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) + +

    From 088167253668c5f1bc884c7d263e6c4af9f26ed5 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 29 Aug 2022 15:41:59 -0700 Subject: [PATCH 2709/4563] Accept SE-0368 "StaticBigInt" (#1759) * Accept SE-0368 "StaticBigInt" * Add acceptance link for SE-0368 --- proposals/0368-staticbigint.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index 77d4d588cb..0450d35e8f 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -3,10 +3,10 @@ * Proposal: [SE-0368](0368-staticbigint.md) * Author: [Ben Rimmington](https://github.com/benrimmington) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review (August 2...23, 2022)** +* Status: **Accepted** * Implementation: [apple/swift#40722](https://github.com/apple/swift/pull/40722) -* Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) - ([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) +* Review: ([pitch](https://forums.swift.org/t/staticbigint/54545))([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) +* Decision notes: [Acceptance](https://forums.swift.org/t/accepted-se-0368-staticbigint/59962)
    Revision history From 1a6161d99baa332b3ac55d397db9c7e662b7bbb8 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 30 Aug 2022 16:32:55 -0400 Subject: [PATCH 2710/4563] Update 0363-unicode-for-string-processing.md (#1760) --- proposals/0363-unicode-for-string-processing.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0363-unicode-for-string-processing.md b/proposals/0363-unicode-for-string-processing.md index b588817f33..98476004d1 100644 --- a/proposals/0363-unicode-for-string-processing.md +++ b/proposals/0363-unicode-for-string-processing.md @@ -3,7 +3,8 @@ * Proposal: [SE-0363](0363-unicode-for-string-processing.md) * Authors: [Nate Cook](https://github.com/natecook1000), [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (June 27...July 11, 2022)** +* Status: **Accepted** +* Review: https://forums.swift.org/t/se-0363-unicode-for-string-processing/58520 * Implementation: [apple/swift-experimental-string-processing][repo] ### Version History From 3c91019486c9ba57edc71f91f3cff43716c56586 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 30 Aug 2022 16:43:27 -0400 Subject: [PATCH 2711/4563] Mark 0363 implemented (#1761) --- proposals/0363-unicode-for-string-processing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0363-unicode-for-string-processing.md b/proposals/0363-unicode-for-string-processing.md index 98476004d1..43bbe84b05 100644 --- a/proposals/0363-unicode-for-string-processing.md +++ b/proposals/0363-unicode-for-string-processing.md @@ -3,7 +3,7 @@ * Proposal: [SE-0363](0363-unicode-for-string-processing.md) * Authors: [Nate Cook](https://github.com/natecook1000), [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Review: https://forums.swift.org/t/se-0363-unicode-for-string-processing/58520 * Implementation: [apple/swift-experimental-string-processing][repo] From 03c12e9c2c4aa9a3137ec37d6f55d95747810ba3 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 30 Aug 2022 16:44:53 -0400 Subject: [PATCH 2712/4563] Mark 0357 implemented in Swift 5.7 (#1762) --- proposals/0357-regex-string-processing-algorithms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0357-regex-string-processing-algorithms.md b/proposals/0357-regex-string-processing-algorithms.md index 539cf18fb2..44629f4b7f 100644 --- a/proposals/0357-regex-string-processing-algorithms.md +++ b/proposals/0357-regex-string-processing-algorithms.md @@ -3,7 +3,7 @@ * Proposal: [SE-0357](0357-regex-string-processing-algorithms.md) * Authors: [Tina Liu](https://github.com/itingliu), [Michael Ilseman](https://github.com/milseman), [Nate Cook](https://github.com/natecook1000), [Tim Vermeulen](https://github.com/timvermeulen) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Implementation: [apple/swift-experimental-string-processing](https://github.com/apple/swift-experimental-string-processing/) * Available in nightly toolchain snapshots with `import _StringProcessing` * Review: ([pitch](https://forums.swift.org/t/pitch-regex-powered-string-processing-algorithms/55969)) From 994aab4a8ab4fcd2875b2cb4a47b70a924b32468 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 30 Aug 2022 16:45:31 -0400 Subject: [PATCH 2713/4563] Mark SE-0355 implemented in Swift 5.7 (#1763) --- proposals/0355-regex-syntax-run-time-construction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0355-regex-syntax-run-time-construction.md b/proposals/0355-regex-syntax-run-time-construction.md index 13540e50ad..4efc27242a 100644 --- a/proposals/0355-regex-syntax-run-time-construction.md +++ b/proposals/0355-regex-syntax-run-time-construction.md @@ -3,7 +3,7 @@ * Proposal: [SE-0355](0355-regex-syntax-run-time-construction.md) * Authors: [Hamish Knight](https://github.com/hamishknight), [Michael Ilseman](https://github.com/milseman) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Implementation: https://github.com/apple/swift-experimental-string-processing * Available in nightly toolchain snapshots with `import _StringProcessing` * Review: ([first pitch](https://forums.swift.org/t/pitch-regex-syntax/55711)) From 5dbdd6f84619f5c5b7794e173416a81d4d42b4f1 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 30 Aug 2022 16:46:19 -0400 Subject: [PATCH 2714/4563] Mark SE-0355 implemented in Swift 5.7 (#1764) --- proposals/0351-regex-builder.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0351-regex-builder.md b/proposals/0351-regex-builder.md index 34b42d52cc..079da96df0 100644 --- a/proposals/0351-regex-builder.md +++ b/proposals/0351-regex-builder.md @@ -5,7 +5,7 @@ * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Implementation: [apple/swift-experimental-string-processing](https://github.com/apple/swift-experimental-string-processing/tree/main/Sources/RegexBuilder) * Available in nightly toolchain snapshots with `import _StringProcessing` -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Review: ([pitch](https://forums.swift.org/t/pitch-regex-builder-dsl/56007)) ([first review](https://forums.swift.org/t/se-0351-regex-builder-dsl/56531)) ([revision](https://forums.swift.org/t/returned-for-revision-se-0351-regex-builder-dsl/57224)) From e8b01452d45d9f7c654abc1361daf83f4846ef4e Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 30 Aug 2022 16:46:51 -0400 Subject: [PATCH 2715/4563] Mark SE-0350 implemented in Swift 5.7 (#1765) --- proposals/0350-regex-type-overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0350-regex-type-overview.md b/proposals/0350-regex-type-overview.md index 12f834051c..4db19c5ec1 100644 --- a/proposals/0350-regex-type-overview.md +++ b/proposals/0350-regex-type-overview.md @@ -3,7 +3,7 @@ * Proposal: [SE-0350](0350-regex-type-overview.md) * Authors: [Michael Ilseman](https://github.com/milseman) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Implementation: https://github.com/apple/swift-experimental-string-processing * Available in nightly toolchain snapshots with `import _StringProcessing` From 9b3c76f0a0fe5eef8a4e0dc41af1693bf1bdc777 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 30 Aug 2022 18:01:08 -0400 Subject: [PATCH 2716/4563] Accept SE-0369 (#1766) --- ...69-add-customdebugdescription-conformance-to-anykeypath.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md b/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md index 85da4b05a9..c437a56c2b 100644 --- a/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md +++ b/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md @@ -3,9 +3,9 @@ * Proposal: [SE-0369](0369-add-customdebugdescription-conformance-to-anykeypath.md) * Author: [Ben Pious](https://github.com/benpious) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active Review (August 16...August 29, 2022)** +* Status: **Accepted** * Implementation: [apple/swift#60133](https://github.com/apple/swift/pull/60133) -* Review: ([pitch](https://forums.swift.org/t/pitch-add-customdebugdescription-conformance-to-anykeypath/58705)) ([review](https://forums.swift.org/t/se-0369-add-customdebugstringconvertible-conformance-to-anykeypath/59704)) +* Review: ([pitch](https://forums.swift.org/t/pitch-add-customdebugdescription-conformance-to-anykeypath/58705)) ([review](https://forums.swift.org/t/se-0369-add-customdebugstringconvertible-conformance-to-anykeypath/59704)) ([acceptance](https://forums.swift.org/t/accepted-se-0369-add-customdebugstringconvertible-conformance-to-anykeypath/60001)) ## Introduction From 2a3ab2b154b11789ae894f683dc47b1fcf03450c Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 30 Aug 2022 20:16:20 -0400 Subject: [PATCH 2717/4563] Accept SE-0370 --- proposals/0370-pointer-family-initialization-improvements.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index 5e07148efe..3c5f0efac4 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -3,9 +3,9 @@ * Proposal: [SE-0370](0370-pointer-family-initialization-improvements.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (August 17...29, 2022)** +* Status: **Accepted** * Implementation: [Draft Pull Request](https://github.com/apple/swift/pull/41608) -* Review: ([first pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements/53168)) ([second pitch](https://forums.swift.org/t/pitch-buffer-partial-initialization-better-buffer-slices/53795)) ([third pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements-better-buffer-slices/55689)) ([review](https://forums.swift.org/t/se-0370-pointer-family-initialization-improvements-and-better-buffer-slices/59724)) +* Review: ([first pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements/53168)) ([second pitch](https://forums.swift.org/t/pitch-buffer-partial-initialization-better-buffer-slices/53795)) ([third pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements-better-buffer-slices/55689)) ([review](https://forums.swift.org/t/se-0370-pointer-family-initialization-improvements-and-better-buffer-slices/59724)) ([acceptance](https://forums.swift.org/t/accepted-se-0370-pointer-family-initialization-improvements-and-better-buffer-slices/60007)) ## Introduction From 62a8ebffd74750b53f4632e3bd44f52c738b46d2 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 2 Sep 2022 10:45:58 -0500 Subject: [PATCH 2718/4563] Return SE-0371 for revision --- proposals/0371-isolated-synchronous-deinit.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0371-isolated-synchronous-deinit.md b/proposals/0371-isolated-synchronous-deinit.md index 661aeb0915..7c5bc9d02d 100644 --- a/proposals/0371-isolated-synchronous-deinit.md +++ b/proposals/0371-isolated-synchronous-deinit.md @@ -3,9 +3,9 @@ * Proposal: [SE-0371](0371-isolated-synchronous-deinit.md) * Author: [Mykola Pokhylets](https://github.com/nickolas-pohilets) * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) -* Status: **Active review(August 18...August 31, 2022)** +* Status: **Returned for revision** * Implementation: [apple/swift#60057](https://github.com/apple/swift/pull/60057) -* Review: ([pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) ([review](https://forums.swift.org/t/se-0371-isolated-synchronous-deinit/59754)) +* Review: ([pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) ([review](https://forums.swift.org/t/se-0371-isolated-synchronous-deinit/59754)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0371-isolated-synchronous-deinit/60060)) ## Introduction From c4adce57df2b3b5727b3db7f41c7a30f3b3412ad Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 2 Sep 2022 14:56:26 -0500 Subject: [PATCH 2719/4563] Add 'Document Sorting as Stable' proposal --- proposals/0000-stable-sort.md | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 proposals/0000-stable-sort.md diff --git a/proposals/0000-stable-sort.md b/proposals/0000-stable-sort.md new file mode 100644 index 0000000000..6d6065d9e3 --- /dev/null +++ b/proposals/0000-stable-sort.md @@ -0,0 +1,78 @@ +# Document Sorting as Stable + +* Proposal: [SE-NNNN](0000-stable-sort.md) +* Author: [Nate Cook](https://github.com/natecook1000) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift PR #60936](https://github.com/apple/swift/pull/60936) + +## Introduction + +Swift's sorting algorithm was changed to be stable before Swift 5, but we've never updated the documentation to provide that guarantee. Let's commit to the sorting algorithm being stable so that people can rely on that behavior. + +Swift-evolution thread: [Pitch](https://forums.swift.org/t/pitch-document-sorting-as-stable/59880) + +## Motivation + +A *stable sort* is a sort that keeps the original relative order for any elements that compare as equal or unordered. For example, given this list of players that are already sorted by last name, a sort by first name preserves the original order of the two players named "Ashley": + +```swift +var roster = [ + Player(first: "Sam", last: "Coffey"), + Player(first: "Ashley", last: "Hatch"), + Player(first: "Kristie", last: "Mewis"), + Player(first: "Ashley", last: "Sanchez"), + Player(first: "Sophia", last: "Smith"), +] + +roster.sort(by: { $0.first < $1.first }) +// roster == [ +// Player(first: "Ashley", last: "Hatch"), +// Player(first: "Ashley", last: "Sanchez"), +// Player(first: "Kristie", last: "Mewis"), +// Player(first: "Sam", last: "Coffey"), +// Player(first: "Sophia", last: "Smith"), +// ] +``` + +For users who are unaware that many sorting algorithms aren't stable, an unstable sort can be surprising. Preserving relative order is an expectation set by software like spreadsheets, where sorting by one column, and then another, is a way to complete a sort based on multiple properties. + +Sort stability isn't always observable. When a collection is sorted based on the elements' `Comparable` conformance, like sorting an array of integers, "unordered" elements are typically indistinguishable. In general, sort stability is important when elements are sorted based on a subset of their properties. + +The standard library `sort()` has long been stable, but the documentation explicitly [doesn't make this guarantee](https://developer.apple.com/documentation/swift/array/sorted()): + +> The sorting algorithm is not guaranteed to be stable. A stable sort preserves the relative order of elements that compare as equal. + +This status quo is a problem — developers who are aware of what stability is and cannot rely on the current behavior, and developers who are unaware of stability could be surprised by unexpected bugs if the stability were to disappear. Guaranteeing stability would resolve both of these issues. + +## Proposed solution + +Let's change the documentation! Since all current versions of the Swift runtime include a stable sort (which was introduced before ABI stability), this change can be made to the standard library documentation only: + +```diff +- /// The sorting algorithm is not guaranteed to be stable. A stable sort ++ /// The sorting algorithm is guaranteed to be stable. A stable sort + /// preserves the relative order of elements that compare as equal. +``` + +## Source compatibility + +This change codifies the existing standard library behavior, so it is compatible with all existing source code. + +## Effect on ABI stability + +The change to make sorting stable was implemented before ABI stability, so all ABI-stable versions of Swift already provide this behavior. + +## Effect on API resilience + +Making this guarantee explicit requires that any changes to the sort algorithm maintain stability. + +## Alternatives considered + +### Providing an `unstableSort()` + +Discussing the *stability* of the current sort naturally brings up the question of providing an alternative sort that is *unstable*. An unstable sort by itself, however, doesn't provide any specific benefit to users — no one is asking for a sort that mixes up equivalent elements! Instead, users could be interested in sort algorithms that have other characteristics, such as using only an array's existing allocation, that are much faster to implement without guaranteeing stability. If and when proposals for those sort algorithms are introduced, the lack of stability can be addressed through documentation and/or API naming, and having the default sort be stable is still valuable for the reasons listed above. + +### Other sorting-related changes + +There are also a variety of other sorting-related improvements that could be interesting to pursue, including key-path or function-based sorting, sorted collection types or protocols, sort descriptors, and more. These ideas can be explored in future pitches and proposals. From 4f0726385513577f25a2533f1863af4d6093e61a Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Sat, 3 Sep 2022 05:54:54 +0200 Subject: [PATCH 2720/4563] [SE-0352] Fix typo (#1768) --- proposals/0352-implicit-open-existentials.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0352-implicit-open-existentials.md b/proposals/0352-implicit-open-existentials.md index f1dfd8fece..c69bbf86fe 100644 --- a/proposals/0352-implicit-open-existentials.md +++ b/proposals/0352-implicit-open-existentials.md @@ -122,7 +122,7 @@ func checkFinaleReadinessMember(costumes: [any Costume]) -> Bool { In that sense, implicitly opening existentials for calls to generic functions is a generalization of this existing behavior to all generic parameters. It isn't strictly more expressive: as the `hasBellsMember` example shows, one *can* always write a member in a protocol extension to get this opening behavior. This proposal aims to make implicit opening of existentials more uniform and more ergonomic, by making it more general. -Let's consider one last implementation of our "readiness" check, where want to "open code" the check for bells without putting the logic into a separatae generic function `hasBells`: +Let's consider one last implementation of our "readiness" check, where want to "open code" the check for bells without putting the logic into a separate generic function `hasBells`: ```swift func checkFinaleReadinessOpenCoded(costumes: [any Costume]) -> Bool { From eb5ac9536fcf82ee29cfe9618db4f343e945114f Mon Sep 17 00:00:00 2001 From: Wade Tregaskis Date: Sun, 4 Sep 2022 11:25:19 -0700 Subject: [PATCH 2721/4563] =?UTF-8?q?Fixed=20typo=20(=E2=80=9Cat=20an=20of?= =?UTF-8?q?fset=E2=80=9D,=20not=20=E2=80=9Cat=20in=20offset=E2=80=9D).=20(?= =?UTF-8?q?#1771)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0369-add-customdebugdescription-conformance-to-anykeypath.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md b/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md index c437a56c2b..bd7b7cd117 100644 --- a/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md +++ b/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md @@ -104,7 +104,7 @@ The new debugging output will not be backdeployed, so Swift programs running on The implementation of `debugDescription` might change after the initial work to implement this proposal is done. In particular, the output format will not be guaranteed to be stable. Here are a few different changes we might anticipate making: - As new features are added to the compiler, there may be new metadata available in the binary to draw from. One example would be lookup tables of KeyPath segment to human-readable-name or some other unique, stable identifier -- Whenever a new feature is added to `KeyPath`, it will need to be reflected in the output of this function. For example, the `KeyPath`s produced by [\_forEachFieldWithKeyPath](https://github.com/apple/swift/blob/main/stdlib/public/core/ReflectionMirror.swift#L324) are incomplete, in the sense that they merely set a value at in offset in memory and do not call `didSet` observers. If this function were ever publicly exposed, it would be useful if this was surfaced in the debugging information. +- Whenever a new feature is added to `KeyPath`, it will need to be reflected in the output of this function. For example, the `KeyPath`s produced by [\_forEachFieldWithKeyPath](https://github.com/apple/swift/blob/main/stdlib/public/core/ReflectionMirror.swift#L324) are incomplete, in the sense that they merely set a value at an offset in memory and do not call `didSet` observers. If this function were ever publicly exposed, it would be useful if this was surfaced in the debugging information. - The behavior of subscript printing might be changed: for example, we might always print out the value of the argument to the subscript, or we might do so only in cases where the output is short. We might also change from `.subscript()` to `[]` - The Swift language workgroup may create new policies around debug descriptions and the output of this function might need to be updated to conform to them From 2d09432f9f1b3a119beb9c4cb36e4988209af2ac Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Mon, 5 Sep 2022 22:50:04 +0200 Subject: [PATCH 2722/4563] [SE-0334] Fix typos (#1772) --- proposals/0334-pointer-usability-improvements.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0334-pointer-usability-improvements.md b/proposals/0334-pointer-usability-improvements.md index 1aa50646d2..ffae2587ad 100644 --- a/proposals/0334-pointer-usability-improvements.md +++ b/proposals/0334-pointer-usability-improvements.md @@ -88,12 +88,12 @@ Calculating the offset between the start of the data structure to the field of t We propose to add a function to help perform this operation on raw pointer types: ```swift extension UnsafeRawPointer { - public func alignedUp(for: T.type) -> Self + public func alignedUp(for: T.Type) -> Self } ``` This function will round the current pointer up to the next address properly aligned to access an instance of `T`. -When applied to a `self` already aligned for `T`, `UnsafeRawPointer.aligned(for:)` will return `self`. +When applied to a `self` already aligned for `T`, `UnsafeRawPointer.alignedUp(for:)` will return `self`. The new function would make identifying the storage location of `T` much more straightforward than in the example above: ```swift From 3adb955e7197fdbf4758f82c1e2fa4c880e44fd6 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Wed, 7 Sep 2022 10:23:29 -0500 Subject: [PATCH 2723/4563] Update sort stability proposal based on feedback --- proposals/0000-stable-sort.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0000-stable-sort.md b/proposals/0000-stable-sort.md index 6d6065d9e3..40419e5d1e 100644 --- a/proposals/0000-stable-sort.md +++ b/proposals/0000-stable-sort.md @@ -39,11 +39,11 @@ For users who are unaware that many sorting algorithms aren't stable, an unstabl Sort stability isn't always observable. When a collection is sorted based on the elements' `Comparable` conformance, like sorting an array of integers, "unordered" elements are typically indistinguishable. In general, sort stability is important when elements are sorted based on a subset of their properties. -The standard library `sort()` has long been stable, but the documentation explicitly [doesn't make this guarantee](https://developer.apple.com/documentation/swift/array/sorted()): +The standard library `sort()` has long been stable, but the documentation explicitly [doesn't make this guarantee](https://github.com/apple/swift/blob/release/5.7/stdlib/public/core/Sort.swift#L40-L41): > The sorting algorithm is not guaranteed to be stable. A stable sort preserves the relative order of elements that compare as equal. -This status quo is a problem — developers who are aware of what stability is and cannot rely on the current behavior, and developers who are unaware of stability could be surprised by unexpected bugs if the stability were to disappear. Guaranteeing stability would resolve both of these issues. +This status quo is a problem — developers who are aware of what stability is cannot rely on the current behavior, and developers who are unaware of stability could be surprised by unexpected bugs if stability were to disappear. Guaranteeing stability would resolve both of these issues. ## Proposed solution @@ -73,6 +73,6 @@ Making this guarantee explicit requires that any changes to the sort algorithm m Discussing the *stability* of the current sort naturally brings up the question of providing an alternative sort that is *unstable*. An unstable sort by itself, however, doesn't provide any specific benefit to users — no one is asking for a sort that mixes up equivalent elements! Instead, users could be interested in sort algorithms that have other characteristics, such as using only an array's existing allocation, that are much faster to implement without guaranteeing stability. If and when proposals for those sort algorithms are introduced, the lack of stability can be addressed through documentation and/or API naming, and having the default sort be stable is still valuable for the reasons listed above. -### Other sorting-related changes +## Future directions -There are also a variety of other sorting-related improvements that could be interesting to pursue, including key-path or function-based sorting, sorted collection types or protocols, sort descriptors, and more. These ideas can be explored in future pitches and proposals. +There are a variety of other sorting-related improvements that could be interesting to pursue, including key-path or function-based sorting, sorted collection types or protocols, sort descriptors, and more. These ideas can be explored in future pitches and proposals. From 9c9ca3fd99805216347b956816261ecbde870279 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 7 Sep 2022 09:16:14 -0700 Subject: [PATCH 2724/4563] Assign as SE-0372 and begin review --- ...stable-sort.md => 0372-document-sorting-as-stable.md} | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) rename proposals/{0000-stable-sort.md => 0372-document-sorting-as-stable.md} (94%) diff --git a/proposals/0000-stable-sort.md b/proposals/0372-document-sorting-as-stable.md similarity index 94% rename from proposals/0000-stable-sort.md rename to proposals/0372-document-sorting-as-stable.md index 40419e5d1e..a86becfdd2 100644 --- a/proposals/0000-stable-sort.md +++ b/proposals/0372-document-sorting-as-stable.md @@ -1,17 +1,16 @@ # Document Sorting as Stable -* Proposal: [SE-NNNN](0000-stable-sort.md) +* Proposal: [SE-0372](0372-document-sorting-as-stable.md) * Author: [Nate Cook](https://github.com/natecook1000) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Tony Allevato](https://github.com/allevato) +* Status: **Active review (September 7, 2022...September 20, 2022)** * Implementation: [apple/swift PR #60936](https://github.com/apple/swift/pull/60936) +* Review: ([pitch](https://forums.swift.org/t/pitch-document-sorting-as-stable/59880)) ## Introduction Swift's sorting algorithm was changed to be stable before Swift 5, but we've never updated the documentation to provide that guarantee. Let's commit to the sorting algorithm being stable so that people can rely on that behavior. -Swift-evolution thread: [Pitch](https://forums.swift.org/t/pitch-document-sorting-as-stable/59880) - ## Motivation A *stable sort* is a sort that keeps the original relative order for any elements that compare as equal or unordered. For example, given this list of players that are already sorted by last name, a sort by first name preserves the original order of the two players named "Ashley": From caa8408a0ca04d165ebd60fae446146f40d221ae Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 7 Sep 2022 09:20:51 -0700 Subject: [PATCH 2725/4563] Add link to review thread --- proposals/0372-document-sorting-as-stable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0372-document-sorting-as-stable.md b/proposals/0372-document-sorting-as-stable.md index a86becfdd2..985224060f 100644 --- a/proposals/0372-document-sorting-as-stable.md +++ b/proposals/0372-document-sorting-as-stable.md @@ -5,7 +5,7 @@ * Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Active review (September 7, 2022...September 20, 2022)** * Implementation: [apple/swift PR #60936](https://github.com/apple/swift/pull/60936) -* Review: ([pitch](https://forums.swift.org/t/pitch-document-sorting-as-stable/59880)) +* Review: ([pitch](https://forums.swift.org/t/pitch-document-sorting-as-stable/59880)) ([review](https://forums.swift.org/t/se-0372-document-sorting-as-stable/60165)) ## Introduction From 798ea40c22cf6a33e869472983eb347963cffcce Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Sun, 11 Sep 2022 10:16:37 -0700 Subject: [PATCH 2726/4563] Add more thorough examples to SE-0365 proposal body --- proposals/0365-implicit-self-weak-capture.md | 100 +++++++++++++++++-- 1 file changed, 94 insertions(+), 6 deletions(-) diff --git a/proposals/0365-implicit-self-weak-capture.md b/proposals/0365-implicit-self-weak-capture.md index 3f710ae8b7..6a90111521 100644 --- a/proposals/0365-implicit-self-weak-capture.md +++ b/proposals/0365-implicit-self-weak-capture.md @@ -71,6 +71,46 @@ class ViewController { ## Detailed design +### Enabling implicit `self` + +All of the following forms of optional unwrapping are supported, and enable implicit self for the following scope where `self` is non-optional: + +```swift +button.tapHandler = { [weak self] in + guard let self else { return } + dismiss() +} + +button.tapHandler = { [weak self] in + guard let self = self else { return } + dismiss() +} + +button.tapHandler = { [weak self] in + if let self { + dismiss() + } +} + +button.tapHandler = { [weak self] in + if let self = self { + dismiss() + } +} + +button.tapHandler = { [weak self] in + while let self { + dismiss() + } +} + +button.tapHandler = { [weak self] in + while let self = self { + dismiss() + } +} +``` + Like with implicit `self` for `strong` and `unowned` captures, the compiler will synthesize an implicit `self.` for calls to properties / methods on `self` inside a closure that uses `weak self`. If `self` has not been unwrapped yet, the following error will be emitted: @@ -84,17 +124,65 @@ button.tapHandler = { [weak self] in } ``` +### Nested closures + +Nested closures can be a source of subtle retain cycles, so have to be handled more carefully. For example, if the following code was allowed to compile, then the implicit `self.bar()` call could introduce a hidden retain cycle: + +```swift +couldCauseRetainCycle { [weak self] in + guard let self else { return } + foo() + + couldCauseRetainCycle { + bar() + } +} +``` + Following the precedent of [SE-0269](https://github.com/apple/swift-evolution/blob/main/proposals/0269-implicit-self-explicit-capture.md), additional closures nested inside the `[weak self]` closure must capture `self` explicitly in order to use implicit `self`. ```swift -button.tapHandler = { [weak self] in +// Not allowed: +couldCauseRetainCycle { [weak self] in + guard let self else { return } + foo() + + couldCauseRetainCycle { + // error: call to method 'method' in closure requires + // explicit use of 'self' to make capture semantics explicit + bar() + } +} + +// Allowed: +couldCauseRetainCycle { [weak self] in + guard let self else { return } + foo() + + couldCauseRetainCycle { [weak self] in guard let self else { return } + bar() + } +} - execute { - // error: call to method 'method' in closure requires - // explicit use of 'self' to make capture semantics explicit - dismiss() - } +// Also allowed: +couldCauseRetainCycle { [weak self] in + guard let self else { return } + foo() + + couldCauseRetainCycle { + self.bar() + } +} + +// Also allowed: +couldCauseRetainCycle { [weak self] in + guard let self else { return } + foo() + + couldCauseRetainCycle { [self] in + bar() + } } ``` From 9544e17966879e4a492f0924ab2e6a6e31748225 Mon Sep 17 00:00:00 2001 From: kyungpyoda <44656036+kyungpyoda@users.noreply.github.com> Date: Tue, 13 Sep 2022 12:18:37 +0900 Subject: [PATCH 2727/4563] Add Swift 5.7 released url in README (#1777) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a1fea38aa4..a027032055 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ This repository tracks the ongoing evolution of Swift. It contains: | Version | Announced | Released | | :-------- | :----------------------------------------------------------------------- | :------------------------------------------------------- | -| Swift 5.7 | [2022-03-29](https://forums.swift.org/t/swift-5-7-release-process/56316) | +| Swift 5.7 | [2022-03-29](https://forums.swift.org/t/swift-5-7-release-process/56316) | [2022-09-12](https://swift.org/blog/swift-5.7-released/) | | Swift 5.6 | [2021-11-10](https://forums.swift.org/t/swift-5-6-release-process/53412) | [2022-03-14](https://swift.org/blog/swift-5.6-released/) | | Swift 5.5 | [2021-03-12](https://forums.swift.org/t/swift-5-5-release-process/45644) | [2021-09-20](https://swift.org/blog/swift-5.5-released/) | | Swift 5.4 | [2020-11-11](https://forums.swift.org/t/swift-5-4-release-process/41936) | [2021-04-26](https://swift.org/blog/swift-5.4-released/) | From 11032ac5b06ff079bbbc0541fb2102e0a5447470 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 14 Sep 2022 15:13:43 -0400 Subject: [PATCH 2728/4563] SE-0369 is implemented (#1780) --- ...0369-add-customdebugdescription-conformance-to-anykeypath.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md b/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md index bd7b7cd117..6f58d7aff8 100644 --- a/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md +++ b/proposals/0369-add-customdebugdescription-conformance-to-anykeypath.md @@ -3,7 +3,7 @@ * Proposal: [SE-0369](0369-add-customdebugdescription-conformance-to-anykeypath.md) * Author: [Ben Pious](https://github.com/benpious) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Accepted** +* Status: **Implemented (Swift 5.8)** * Implementation: [apple/swift#60133](https://github.com/apple/swift/pull/60133) * Review: ([pitch](https://forums.swift.org/t/pitch-add-customdebugdescription-conformance-to-anykeypath/58705)) ([review](https://forums.swift.org/t/se-0369-add-customdebugstringconvertible-conformance-to-anykeypath/59704)) ([acceptance](https://forums.swift.org/t/accepted-se-0369-add-customdebugstringconvertible-conformance-to-anykeypath/60001)) From 9d8b1f84a7f72640f61c64835225fb0bd92346d6 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 14 Sep 2022 20:54:20 +0100 Subject: [PATCH 2729/4563] Add Swift 5.8 to the Language Version filter (#1781) --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index c0f0705ed0..3493110b48 100644 --- a/index.js +++ b/index.js @@ -19,7 +19,7 @@ var proposals * To be updated when proposals are confirmed to have been implemented * in a new language version. */ -var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4', '4.1', '4.2', '5', '5.1', '5.2', '5.3', '5.4', '5.5', '5.5.2', '5.6', '5.7', 'Next'] +var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4', '4.1', '4.2', '5', '5.1', '5.2', '5.3', '5.4', '5.5', '5.5.2', '5.6', '5.7', '5.8', 'Next'] /** Storage for the user's current selection of filters when filtering is toggled off. */ var filterSelection = [] From da373213843a30a39ef1908539ad509355857f0c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 14 Sep 2022 16:34:07 -0700 Subject: [PATCH 2730/4563] SE-0367 is implemented in Swift 5.8 (#1782) --- proposals/0367-conditional-attributes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0367-conditional-attributes.md b/proposals/0367-conditional-attributes.md index 376fa16baf..b6ca2bb8ec 100644 --- a/proposals/0367-conditional-attributes.md +++ b/proposals/0367-conditional-attributes.md @@ -3,7 +3,7 @@ * Proposal: [SE-0367](0367-conditional-attributes.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Accepted** +* Status: **Implemented (Swift 5.8)** * Implementation: [apple/swift#60208](https://github.com/apple/swift/pull/60208) * Swift-evolution thread: [Pitch](https://forums.swift.org/t/pitch-conditional-compilation-for-attributes-and-modifiers/58339) From e934fedc0d6c58ef112a3bc00f5e7614ce5d6148 Mon Sep 17 00:00:00 2001 From: Mihail Verenich <97mik@mail.ru> Date: Sun, 18 Sep 2022 02:18:48 +0300 Subject: [PATCH 2731/4563] [SE-0296] Async/await: fix broken link (#1783) --- proposals/0296-async-await.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0296-async-await.md b/proposals/0296-async-await.md index 889719958c..81ed1153c6 100644 --- a/proposals/0296-async-await.md +++ b/proposals/0296-async-await.md @@ -470,7 +470,7 @@ These two functions have different names and signatures, even though they share doSomething() // problem: can call either, unmodified Swift rules prefer the `async` version ``` -A similar problem exists for APIs that evolve into providing both a synchronous and an asynchronous version of the same function, with the same signature. Such pairs allow APIs to provide a new asynchronous function which better fits in the Swift asynchronous landscape, without breaking backward compatibility. New asynchronous functions can support, for example, cancellation (covered in the [Structured Concurrency](https://github.com/DougGregor/swift-evolution/blob/structured-concurrency/proposals/nnnn-structured-concurrency.md) proposal). +A similar problem exists for APIs that evolve into providing both a synchronous and an asynchronous version of the same function, with the same signature. Such pairs allow APIs to provide a new asynchronous function which better fits in the Swift asynchronous landscape, without breaking backward compatibility. New asynchronous functions can support, for example, cancellation (covered in the [Structured Concurrency](https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md) proposal). ```swift // Existing synchronous API From 34d00dc65202a3c085f96b6d7b58c248256afc70 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Tue, 20 Sep 2022 19:08:36 +0300 Subject: [PATCH 2732/4563] Status: Insert missing leading zeros in IDs passed to `proposal=` (#1779) --- index.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 3493110b48..840b9cf329 100644 --- a/index.js +++ b/index.js @@ -824,13 +824,13 @@ function _applyFilter (matchingProposals) { * * Syntax (a query string within a fragment): * fragment --> `#?` parameter-value-list - * parameter-value-list --> parameter-value | parameter-value-pair `&` parameter-value-list + * parameter-value-list --> parameter-value-pair | parameter-value-pair `&` parameter-value-list * parameter-value-pair --> parameter `=` value * parameter --> `proposal` | `status` | `version` | `search` * value --> ** Any URL-encoded text. ** * * For example: - * /#?proposal:SE-0180,SE-0123 + * /#?proposal=SE-0180,SE-0123 * /#?status=rejected&version=3&search=access * * Four types of parameters are supported: @@ -861,6 +861,17 @@ function _applyFragment (fragment) { value = value.split(',') } + if (action === 'proposal') { + value = value.flatMap(function (id) { + // filter out invalid identifiers. + const output = id.match(/^SE-([0-9]{1,4})$/i) + if (!output) return [] + + // insert missing leading zeros, e.g., 'SE-2' → 'SE-0002'. + return 'SE-' + output[1].padStart(4, '0') + }) + } + actions[action] = value } }) From 3d5b377a24fefdf01ad103365c079d8b374ceae7 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 20 Sep 2022 14:03:59 -0400 Subject: [PATCH 2733/4563] Merge changes inadvertently targeting 'master' into 'main' (#1784) * Fix code example in proposal SE-0235 (#1251) * Update expired portion of the proposal * Update 0174-filter-range-replaceable.md * Update 0153-compensate-for-the-inconsistency-of-nscopyings-behaviour.md * Update 0140-bridge-optional-to-nsnull.md (#1494) * Update 0211-unicode-scalar-properties.md (#1744) Add footnote about the status of `isDefined`. * Revert "Update 0211-unicode-scalar-properties.md (#1744)" (#1745) This reverts commit 3ae6c6d26173d172469e605fd081ee1eedf346f9. Co-authored-by: Jeroen Noten Co-authored-by: Ben Cohen Co-authored-by: dirtmelon <0xffdirtmelon@gmail.com> Co-authored-by: Stephen Canon --- proposals/0140-bridge-optional-to-nsnull.md | 2 ++ proposals/0235-add-result.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/0140-bridge-optional-to-nsnull.md b/proposals/0140-bridge-optional-to-nsnull.md index baaa3ca5b4..e5eca4d8a5 100644 --- a/proposals/0140-bridge-optional-to-nsnull.md +++ b/proposals/0140-bridge-optional-to-nsnull.md @@ -195,9 +195,11 @@ error, so should fail early at runtime: This point of view is understandable, but is inconsistent with how Swift itself dynamically treats Optionals inside Anys: +```swift let a: Int? = 3 let b = a as Any let c = a as! Int // Casts '3' out of the Optional as a non-optional Int +``` And while it's true that Cocoa uses `NSNull` sparingly, it *is* the standard sentinel used in the few places where a null-like object is expected, such as diff --git a/proposals/0235-add-result.md b/proposals/0235-add-result.md index f8cee0e3ad..970e0b7244 100644 --- a/proposals/0235-add-result.md +++ b/proposals/0235-add-result.md @@ -43,7 +43,7 @@ This can make it quite difficult to elegantly consume the results of these APIs: ```swift URLSession.shared.dataTask(with: url) { (data, response, error) in - guard error != nil else { return self.handleError(error!) } + guard error == nil else { return self.handleError(error!) } guard let data = data, let response = response else { return // Impossible? } From 1db9502186659f808d24dc93e89dfd3bbef7f056 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 20 Sep 2022 17:35:13 -0700 Subject: [PATCH 2734/4563] Update contributor name --- proposals/0003-remove-var-parameters.md | 2 +- proposals/0013-remove-partial-application-super.md | 2 +- proposals/0020-if-swift-version.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0003-remove-var-parameters.md b/proposals/0003-remove-var-parameters.md index a36158f05b..839f40c251 100644 --- a/proposals/0003-remove-var-parameters.md +++ b/proposals/0003-remove-var-parameters.md @@ -1,7 +1,7 @@ # Removing `var` from Function Parameters * Proposal: [SE-0003](0003-remove-var-parameters.md) -* Author: [David Farler](https://github.com/bitjammer) +* Author: [Ashley Garland](https://github.com/bitjammer) * Review Manager: [Joe Pamer](https://github.com/jopamer) * Status: **Implemented (Swift 3)** * Decision Notes: [Rationale](https://forums.swift.org/t/se-0003-removing-var-from-function-parameters-and-pattern-matching/1230) diff --git a/proposals/0013-remove-partial-application-super.md b/proposals/0013-remove-partial-application-super.md index ae3132c407..40cfaa459c 100644 --- a/proposals/0013-remove-partial-application-super.md +++ b/proposals/0013-remove-partial-application-super.md @@ -1,7 +1,7 @@ # Remove Partial Application of Non-Final Super Methods (Swift 2.2) * Proposal: [SE-0013](0013-remove-partial-application-super.md) -* Author: [David Farler](https://github.com/bitjammer) +* Author: [Ashley Garland](https://github.com/bitjammer) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Rejected** * Decision Notes: [Rationale](https://forums.swift.org/t/rejected-se-0013-remove-partial-application-of-non-final-super-methods/1157) diff --git a/proposals/0020-if-swift-version.md b/proposals/0020-if-swift-version.md index ec90962201..a59e6ad4dc 100644 --- a/proposals/0020-if-swift-version.md +++ b/proposals/0020-if-swift-version.md @@ -1,7 +1,7 @@ # Swift Language Version Build Configuration * Proposal: [SE-0020](0020-if-swift-version.md) -* Author: [David Farler](https://github.com/bitjammer) +* Author: [Ashley Garland](https://github.com/bitjammer) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 2.2)** * Implementation: [apple/swift@c32fb8e](https://github.com/apple/swift/commit/c32fb8e7b9a67907e8b6580a46717c6a345ec7c6) From e9fc16a0287998273dfd7f2df71f33d4c918487f Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 21 Sep 2022 07:34:55 -0700 Subject: [PATCH 2735/4563] Accept SE-0372. --- proposals/0372-document-sorting-as-stable.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0372-document-sorting-as-stable.md b/proposals/0372-document-sorting-as-stable.md index 985224060f..2449d0ae3c 100644 --- a/proposals/0372-document-sorting-as-stable.md +++ b/proposals/0372-document-sorting-as-stable.md @@ -3,9 +3,9 @@ * Proposal: [SE-0372](0372-document-sorting-as-stable.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Active review (September 7, 2022...September 20, 2022)** +* Status: **Implemented (Swift 5.8)** * Implementation: [apple/swift PR #60936](https://github.com/apple/swift/pull/60936) -* Review: ([pitch](https://forums.swift.org/t/pitch-document-sorting-as-stable/59880)) ([review](https://forums.swift.org/t/se-0372-document-sorting-as-stable/60165)) +* Review: ([pitch](https://forums.swift.org/t/pitch-document-sorting-as-stable/59880)) ([review](https://forums.swift.org/t/se-0372-document-sorting-as-stable/60165)) ([acceptance](https://forums.swift.org/t/accepted-se-0372-document-sorting-as-stable/60425)) ## Introduction From c1072851f337fc07f6c7e74cc4ca86011d644afb Mon Sep 17 00:00:00 2001 From: Mihail Verenich <97mik@mail.ru> Date: Wed, 21 Sep 2022 19:25:50 +0300 Subject: [PATCH 2736/4563] [SE-0304] Structured concurrency: fix typo (#1787) --- proposals/0304-structured-concurrency.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0304-structured-concurrency.md b/proposals/0304-structured-concurrency.md index 8cc9ab7173..7ead7e3140 100644 --- a/proposals/0304-structured-concurrency.md +++ b/proposals/0304-structured-concurrency.md @@ -1083,7 +1083,7 @@ This waiting can be performed either: - by the code within the task group itself (e.g., using `next()` repeatedly until it returns `nil`, described below), or - implicitly in the task group itself when returning from the `body`. -By default, the task group will schedule child tasks added to the group on the default global concurrent executor. In the future is is likely that it will be possible to customize the executor tasks are started on with an optional executor parameter to `addTask`. +By default, the task group will schedule child tasks added to the group on the default global concurrent executor. In the future it is likely that it will be possible to customize the executor tasks are started on with an optional executor parameter to `addTask`. ##### Creating TaskGroup child tasks @@ -1135,7 +1135,7 @@ extension ThrowingTaskGroup { The `addTask` operation always succeeds in adding a new child task to the group, even if the task running the group has been cancelled or the group was cancelled explicitly with `group.cancelAll`. In cases where the task group has already been cancelled, the new child task will be created in the `cancelled` state. -To avoid this, the `saddTaskUnlessCancelled` function checks if a group is cancelled before attempting to create the task, and returns a `Bool` that is true if +To avoid this, the `addTaskUnlessCancelled` function checks if a group is cancelled before attempting to create the task, and returns a `Bool` that is true if the task was successfully created. This allows for simple implementation of groups which should "keep creating tasks until cancelled". Cancelling a specific task group child task does _not_ cancel the entire group or any of its siblings. From 2348d2ade30e121b0df04741094746200d82ba4a Mon Sep 17 00:00:00 2001 From: Sanjaiyan Parthipan Date: Thu, 22 Sep 2022 06:56:57 +0530 Subject: [PATCH 2737/4563] sanjaiyan: social media --- index.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.html b/index.html index ac99710440..d73ecb26d0 100644 --- a/index.html +++ b/index.html @@ -28,6 +28,9 @@ Swift Evolution + + +
    From 07aaf6d1d9208e68f1baa59d89f35f3820f46cda Mon Sep 17 00:00:00 2001 From: Sanjaiyan Parthipan Date: Fri, 23 Sep 2022 00:31:22 +0530 Subject: [PATCH 2738/4563] Update index.html Co-authored-by: Kavon Farvardin --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index d73ecb26d0..e0d2b87d06 100644 --- a/index.html +++ b/index.html @@ -29,7 +29,7 @@ Swift Evolution - +
    From 81bd41e65b13ae972e924926efd647008f96817d Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Thu, 22 Sep 2022 16:18:11 -0700 Subject: [PATCH 2739/4563] clarify that actors use a priority queue --- proposals/0306-actors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0306-actors.md b/proposals/0306-actors.md index ab087afa76..9fcfec9644 100644 --- a/proposals/0306-actors.md +++ b/proposals/0306-actors.md @@ -110,9 +110,9 @@ With actors, the attempt to reference `other.balance` triggers a compiler error, A reference to an actor-isolated declaration from outside that actor is called a *cross-actor reference*. Such references are permissible in one of two ways. First, a cross-actor reference to immutable state is allowed from anywhere in the same module as the actor is defined because, once initialized, that state can never be modified (either from inside the actor or outside it), so there are no data races by definition. The reference to `other.accountNumber` is allowed based on this rule, because `accountNumber` is declared via a `let` and has value-semantic type `Int`. -The second form of permissible cross-actor reference is one that is performed with an asynchronous function invocation. Such asynchronous function invocations are turned into "messages" requesting that the actor execute the corresponding task when it can safely do so. These messages are stored in the actor's "mailbox", and the caller initiating the asynchronous function invocation may be suspended until the actor is able to process the corresponding message in its mailbox. An actor processes the messages in its mailbox sequentially, so that a given actor will never have two concurrently-executing tasks running actor-isolated code. This ensures that there are no data races on actor-isolated mutable state, because there is no concurrency in any code that can access actor-isolated state. For example, if we wanted to make a deposit to a given bank account `account`, we could make a call to a method `deposit(amount:)` on another actor, and that call would become a message placed in the actor's mailbox and the caller would suspend. When that actor processes messages, it will eventually process the message corresponding to the deposit, executing that call within the actor's isolation domain when no other code is executing in that actor's isolation domain. +The second form of permissible cross-actor reference is one that is performed with an asynchronous function invocation. Such asynchronous function invocations are turned into "messages" requesting that the actor execute the corresponding task when it can safely do so. These messages are stored in the actor's "mailbox", and the caller initiating the asynchronous function invocation may be suspended until the actor is able to process the corresponding message in its mailbox. An actor processes the messages in its mailbox one-at-a-time, so that a given actor will never have two concurrently-executing tasks running actor-isolated code. This ensures that there are no data races on actor-isolated mutable state, because there is no concurrency in any code that can access actor-isolated state. For example, if we wanted to make a deposit to a given bank account `account`, we could make a call to a method `deposit(amount:)` on another actor, and that call would become a message placed in the actor's mailbox and the caller would suspend. When that actor processes messages, it will eventually process the message corresponding to the deposit, executing that call within the actor's isolation domain when no other code is executing in that actor's isolation domain. -> **Implementation note**: At an implementation level, the messages are partial tasks (described by the [Structured Concurrency][sc] proposal) for the asynchronous call, and each actor instance contains its own serial executor (also in the [Structured Concurrency][sc] proposal). The serial executor is responsible for running the partial tasks sequentially. This is conceptually similar to a serial [`DispatchQueue`](https://developer.apple.com/documentation/dispatch/dispatchqueue), but the actual implementation in the actor runtime uses a lighter-weight implementation that takes advantage of Swift's `async` functions. +> **Implementation note**: At an implementation level, the messages are partial tasks (described by the [Structured Concurrency][sc] proposal) for the asynchronous call, and each actor instance contains its own serial executor (also in the [Structured Concurrency][sc] proposal). The default serial executor is responsible for running the partial tasks one-at-a-time. This is conceptually similar to a serial [`DispatchQueue`](https://developer.apple.com/documentation/dispatch/dispatchqueue), but with an important difference: tasks awaiting an actor are **not** guaranteed to be run in the same order they originally awaited that actor. Swift's runtime system aims to avoid priority inversions whenever possible, using techniques like priority escalation. Thus, the runtime system considers a task's priority when selecting the next task to run on the actor from its queue. This is in contrast with a serial DispatchQueue, which are strictly first-in-first-out. In addition, Swift's actor runtime uses a lighter-weight queue implementation than Dispatch to take full advantage of Swift's `async` functions. Compile-time actor-isolation checking determines which references to actor-isolated declarations are cross-actor references, and ensures that such references use one of the two permissible mechanisms described above. This ensures that code outside of the actor does not interfere with the actor's mutable state. From 7732d88b1498d4803d6cf3d91e1e358340091803 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Sat, 24 Sep 2022 23:24:44 -0700 Subject: [PATCH 2740/4563] Update the endpoint for proposal.json --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 840b9cf329..c675b3b3af 100644 --- a/index.js +++ b/index.js @@ -151,7 +151,7 @@ function init () { }) document.querySelector('#proposals-count-number').innerHTML = 'Loading ...' - req.open('get', 'https://data.swift.org/swift-evolution/proposals') + req.open('get', 'https://download.swift.org/swift-evolution/proposals.json') req.send() } From f64c699afeac2ed15c4a6c36a200a56cc9fb1ef5 Mon Sep 17 00:00:00 2001 From: Mihail Verenich <97mik@mail.ru> Date: Sun, 25 Sep 2022 21:54:49 +0300 Subject: [PATCH 2741/4563] [SE-0302] Fix typo (#1794) --- proposals/0302-concurrent-value-and-concurrent-closures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0302-concurrent-value-and-concurrent-closures.md b/proposals/0302-concurrent-value-and-concurrent-closures.md index b766a47515..946ebd7d7e 100644 --- a/proposals/0302-concurrent-value-and-concurrent-closures.md +++ b/proposals/0302-concurrent-value-and-concurrent-closures.md @@ -193,7 +193,7 @@ Swift has [hard coded conformances for tuples](https://github.com/apple/swift-ev #### Metatype conformance to `Sendable` -Metatypes (such as` Int.Type`, the type produced by the expression `Int.self`) always conform to `Sendable`, because they are immutable. +Metatypes (such as `Int.Type`, the type produced by the expression `Int.self`) always conform to `Sendable`, because they are immutable. #### `Sendable` conformance checking for structs and enums @@ -374,7 +374,7 @@ list = await contactList.filteredElements { $0.firstName != "Max" } // Capturing a 'searchName' string is ok, because String conforms // to Sendable. searchName is captured by value implicitly. -list = await contactList.filteredElements { $0.firstName==searchName } +list = await contactList.filteredElements { $0.firstName == searchName } // @Sendable is part of the type, so passing a compatible // function declaration works as well. From f0c5f1d88d2f2b8b4f00a17df46e6d9d1fc197b0 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 26 Sep 2022 15:17:17 -0700 Subject: [PATCH 2742/4563] Proposal: Lift all limitations on variables in result builders Implementation of result builder transform (introduced by [SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md)) places a number of limitations on local variable declarations, specifically: all declarations should have initializer expression, cannot be computed, have observers, or attached property wrappers. None of the uses described above are explicitly restricted by SE-0289. --- ...-vars-without-limits-in-result-builders.md | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 proposals/NNNN-vars-without-limits-in-result-builders.md diff --git a/proposals/NNNN-vars-without-limits-in-result-builders.md b/proposals/NNNN-vars-without-limits-in-result-builders.md new file mode 100644 index 0000000000..5fd18ba08e --- /dev/null +++ b/proposals/NNNN-vars-without-limits-in-result-builders.md @@ -0,0 +1,116 @@ +# Lift all limitations on variables in result builders + +* Authors: [Pavel Yaskevich](https://github.com/xedin) +* Implementation: [apple/swift#60839](https://github.com/apple/swift/pull/60839) +* Review Manager: TBD +* Status: **Awaiting discussion** + +## Introduction + +Implementation of result builder transform (introduced by [SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md)) places a number of limitations on local variable declarations, specifically: all declarations should have initializer expression, cannot be computed, have observers, or attached property wrappers. None of the uses described above are explicitly restricted by SE-0289. + +Swift-evolution thread: [Pitch thread topic for this proposal](https://forums.swift.org/t/pitch-lift-all-limitations-on-variables-in-result-builders/60460) + +## Motivation + +Result builder proposal [describes how individual components in a result builder body are transformed](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#the-result-builder-transform), and it states that local declaration statements are unaffected by the transformation, which implies that all declarations allowed in context should be supported but that is not the case under current implementation that requires that declarations to have a simple name, storage, and an initializing expression. + +In certain circumstances it's useful to be able to declare a local variable that, for example, declares multiple variables, has default initialization, or an attached property wrapper (with or without initializer). Let's take a look at a simple example: + + +``` +func compute() -> (String, Error?) { ... } + +func test(@MyBuilder builder: () -> Int?) { + ... +} + +test { + let (result, error) = compute() + + let outcome: Outcome + + if let error { + // error specific logic + outcome = .failure + } else { + // complex computation + outcome = .success + } + + switch outcome { + ... + } +} +``` + + +Both declarations are currently rejected because result builders only allow simple (with just one name) stored properties with an explicit initializer expression. + +Local variable declarations with property wrappers (with or w/o explicit initializer) could be utilized for a variety of use-cases, including but not limited to: + +* Verification and/or formatting of the user-provided input + +``` +import SwiftUI + +struct ContentView: View { + var body: some View { + GeometryReader { proxy in + @Clamped(10...100) var width = proxy.size.width + Text("\(width)") + } + } +} +``` + +* Interacting with user defaults + +``` +import SwiftUI + +struct AppIntroView: View { + var body: some View { + @UserDefault(key: "user_has_ever_interacted") var hasInteracted: Bool + ... + Button("Browse Features") { + ... + hasInteracted = true + } + Button("Create Account") { + ... + hasInteracted = true + } + } +} +``` + + + +## Proposed solution + +I propose to treat local variable declarations in the result builder bodies as-if they appear in a function or a multi-statement closure without any additional restrictions. + +## Detailed design + +The change is purely semantic, without any new syntax. It allows declaring: + +* uninitialized +* default initialized +* computed +* observed +* property wrapped +* lazy + +properties in the result builder bodies and treat them just like they are treated in regular functions and closures, which means all of the semantics checks to verify their validity would still be performed and invalid (based on existing rules) declarations are still going to be rejected by the compiler. + +Uninitialized variables are of particular interest because they require special support in the result builder as stated in [SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#assignments) otherwise there is no way to initialize them. + +## Source compatibility + +This is an additive change which should not affect existing source code. + + +## Effect on ABI stability and API resilience + +These changes do not require support from the language runtime or standard library. From 0da1476513fc64f9a5acb656a5ef286e5aadf732 Mon Sep 17 00:00:00 2001 From: Mihail Verenich <97mik@mail.ru> Date: Tue, 27 Sep 2022 18:46:29 +0300 Subject: [PATCH 2743/4563] [SE-0311] Fix typos --- proposals/0311-task-locals.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0311-task-locals.md b/proposals/0311-task-locals.md index de95b5b0f8..72645b2454 100644 --- a/proposals/0311-task-locals.md +++ b/proposals/0311-task-locals.md @@ -413,7 +413,7 @@ await Lib.$sugar.withValue(.noSugar) { As expected, because the *detached task* completely discards any contextual information from the creating task, no `.sugar` preferences were automatically carried through to it. This is similar to task priority, which also is never automatically inherited in detached tasks. -If necessary, it is possible is possible to make a detached task carry a specific priority, executor preference and even task-local value by handling the propagation manually: +If necessary, it is possible to make a detached task carry a specific priority, executor preference and even task-local value by handling the propagation manually: ```swift let sugarPreference = Lib.sugar // store the sugar preference in task-1 @@ -473,7 +473,7 @@ withTaskGroup(of: String.self) { group in } ``` -This is an an un-supported pattern because the purpose of `group.spawn` (and `group.spawnUnlessCancelled`) is explicitly to spawn off a child-task and return immediately. While the _structure_ of these child-tasks is upheld by no child-task being allowed to escape the task group, the child-tasks do "escape" the scope of the `withValue` — which causes trouble for the internal workings of task locals, which are allocated using an efficient task-local allocation mechanism. +This is an un-supported pattern because the purpose of `group.spawn` (and `group.spawnUnlessCancelled`) is explicitly to spawn off a child-task and return immediately. While the _structure_ of these child-tasks is upheld by no child-task being allowed to escape the task group, the child-tasks do "escape" the scope of the `withValue` — which causes trouble for the internal workings of task locals, which are allocated using an efficient task-local allocation mechanism. At the same time, the just shown pattern can be seen as simply wrong usage of the API and programmer error, violating the structured nature of child-tasks. Instead, what the programmer should do in this case is either, set the value for the entire task group, such that all children inherit it: @@ -963,7 +963,7 @@ Building complex server side systems is hard, especially as they are highly conc #### Contextual Logging -Developers instrument their server side systems using logging, metrics and distributed tracing to gain some insight into how such systems are performing. Improving such observability of back-end systems is crucial to their success, yet also very tedious to manually propagate the context.x +Developers instrument their server side systems using logging, metrics and distributed tracing to gain some insight into how such systems are performing. Improving such observability of back-end systems is crucial to their success, yet also very tedious to manually propagate the context. Today developers must pass context explicitly, and with enough cooperation of libraries it is possible to make this process relatively less painful, however it adds a large amount of noise to the already noisy asynchronous functions: From 34d4dba978ad663e44f086f278addbc056079988 Mon Sep 17 00:00:00 2001 From: Mihail Verenich <97mik@mail.ru> Date: Tue, 27 Sep 2022 19:44:17 +0300 Subject: [PATCH 2744/4563] [SE-0311] Rename `spawn` --- proposals/0311-task-locals.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/proposals/0311-task-locals.md b/proposals/0311-task-locals.md index 72645b2454..f530c3dc84 100644 --- a/proposals/0311-task-locals.md +++ b/proposals/0311-task-locals.md @@ -214,7 +214,7 @@ The same property holds for child tasks. For example, if we used a task group to ```swift await MyLibrary.$requestID.withValue("1234-5678") { await withTaskGroup(of: String.self) { group in - group.spawn { // spawns child task running this closure + group.addTask { // add child task running this closure MyLibrary.requestID // returns "1234-5678", which was bound by the parent task } @@ -352,9 +352,9 @@ And finally, if we wanted to set the `withWasabi` reference for most of the task ```swift await Lib.$wasabiPreference.withValue(.withWasabi) { - spawn let firstMeal = cookDinner() - spawn let secondMeal = cookDinner() - spawn let noWasabiMeal = Lib.$wasabiPreference.withValue(.withoutWasabi) { + async let firstMeal = cookDinner() + async let secondMeal = cookDinner() + async let noWasabiMeal = Lib.$wasabiPreference.withValue(.withoutWasabi) { cookDinner() } await firstMeal, secondMeal, noWasabiMeal @@ -454,7 +454,7 @@ Please note that what is copied here are only the bindings, i.e. if a reference One other situation where a task might out-live the `withValue` lexical-scope is a specific anti-pattern within task groups. This situation is reliabily detected at runtime and cause a crash when it is encountered, along with a detailed explanation of the issue. -This one situation where a `withValue` scope is not enough to encapsulate the lifetime of a child-task is if the binding is performed _exactly_ around a TaskGroup's `group.spawn`, like this: +This one situation where a `withValue` scope is not enough to encapsulate the lifetime of a child-task is if the binding is performed _exactly_ around a TaskGroup's `group.addTask`, like this: ```swift withTaskGroup(of: String.self) { group in @@ -463,33 +463,33 @@ withTaskGroup(of: String.self) { group in // error: task-local value: detected illegal task-local value binding at Example.swift:68. // <... more details ... > - group.spawn { + group.addTask { Trace.name } } // end-withValue - // the spawned child-task lives until it is pulled out of the group by next() here: + // the added child-task lives until it is pulled out of the group by next() here: return group.next()! } ``` -This is an un-supported pattern because the purpose of `group.spawn` (and `group.spawnUnlessCancelled`) is explicitly to spawn off a child-task and return immediately. While the _structure_ of these child-tasks is upheld by no child-task being allowed to escape the task group, the child-tasks do "escape" the scope of the `withValue` — which causes trouble for the internal workings of task locals, which are allocated using an efficient task-local allocation mechanism. +This is an un-supported pattern because the purpose of `group.addTask` (and `group.addTaskUnlessCancelled`) is explicitly to add off a child-task and return immediately. While the _structure_ of these child-tasks is upheld by no child-task being allowed to escape the task group, the child-tasks do "escape" the scope of the `withValue` — which causes trouble for the internal workings of task locals, which are allocated using an efficient task-local allocation mechanism. At the same time, the just shown pattern can be seen as simply wrong usage of the API and programmer error, violating the structured nature of child-tasks. Instead, what the programmer should do in this case is either, set the value for the entire task group, such that all children inherit it: ```swift await Trace.$name.withValue("some(func:)") { // OK! await withTaskGroup(...) { group in - group.spawn { ... } + group.addTask { ... } } } ``` -or, set it _within_ the spawned child-task, as then the task-local allocation will take place inside the child-task, and the lifetime of the value will be correct again, i.e. bounded by the closure lifetime of the spawned child-task: +or, set it _within_ the added child-task, as then the task-local allocation will take place inside the child-task, and the lifetime of the value will be correct again, i.e. bounded by the closure lifetime of the added child-task: ```swift await withTaskGroup(...) { group in - group.spawn { + group.addTask { await Trace.$name.withValue("some(func:)") { // OK! ... } @@ -533,13 +533,13 @@ func simple() async { The same would work if the second `print` would be multiple asynchronous function calls "deeper" from the `withValue` invocation. -The same mechanism also works with tasks spawned in task groups or async let declarations, because those also construct child tasks, which then inherit the bound task-local values of the outer scope. +The same mechanism also works with tasks added in task groups or async let declarations, because those also construct child tasks, which then inherit the bound task-local values of the outer scope. ```swift await Lib.$number.withValue(42) { await withTaskGroup(of: Int.self) { group in - group.spawn { + group.addTask { Lib.number // task group child-task sees the "42" value } return group.next()! // 42 @@ -1254,7 +1254,7 @@ We have specific designs in mind with regards to `Progress` monitoring types how - v4: Changed surface API to be focused around `@TaskLocal` property wrapper-style key definitions. - introduce API to bind task-local values in synchronous functions, through `UnsafeCurrentTask` - allude to `async` (or `send`) as the way to carry task-local values rather than forcing them into a detached task - - explain an anti-pattern that will be detected and cause a crash if used around wrapping a `group.spawn` with a task local binding. Thank you to @Lantua over on the Swift Forums for noticing this specific issue. + - explain an anti-pattern that will be detected and cause a crash if used around wrapping a `group.addTask` with a task local binding. Thank you to @Lantua over on the Swift Forums for noticing this specific issue. - v3.2: Cleanups as the proposal used outdated wordings and references to proposals that since have either changed or been accepted already. - No semantic changes in any of the mechanisms proposed. - Change mentions of `ConcurrentValue` to `Sendable` as it was since revised and accepted. From 7be05f1adeb25f8eb61c3b8e4daabeb702d631d1 Mon Sep 17 00:00:00 2001 From: Mihail Verenich <97mik@mail.ru> Date: Tue, 27 Sep 2022 19:47:36 +0300 Subject: [PATCH 2745/4563] [SE-0311] Rename `await try` to `try await` --- proposals/0311-task-locals.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0311-task-locals.md b/proposals/0311-task-locals.md index f530c3dc84..d8aad76f83 100644 --- a/proposals/0311-task-locals.md +++ b/proposals/0311-task-locals.md @@ -987,8 +987,8 @@ func makeDinner(context: LoggingContext) async throws -> Meal { async let meat = marinateMeat(context: context) async let oven = preheatOven(temperature: 350, context: context) - let dish = Dish(ingredients: await try [veggies, meat]) - return await try oven.cook(dish, duration: .hours(3), context: context) + let dish = Dish(ingredients: try await [veggies, meat]) + return try await oven.cook(dish, duration: .hours(3), context: context) } ``` @@ -1098,12 +1098,12 @@ If Swift were to get "function wrappers", tracing a set of asynchronous function @Traced func makeDinner() async throws -> Meal { - async let veggies = await try chopVegetables() + async let veggies = try await chopVegetables() async let meat = await marinateMeat() - async let oven = await try preheatOven(temperature: 350) + async let oven = try await preheatOven(temperature: 350) let dish = Dish(ingredients: await [veggies, meat]) - return await try oven.cook(dish, duration: .hours(3)) + return try await oven.cook(dish, duration: .hours(3)) } ``` From 5b2c7fbb6950517f89494c146c6ec3bb2bc8064f Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 28 Sep 2022 14:01:42 -0400 Subject: [PATCH 2746/4563] Editorial updates, assign myself as review manager --- ...-vars-without-limits-in-result-builders.md | 52 +++++++++---------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/proposals/NNNN-vars-without-limits-in-result-builders.md b/proposals/NNNN-vars-without-limits-in-result-builders.md index 5fd18ba08e..c185050c38 100644 --- a/proposals/NNNN-vars-without-limits-in-result-builders.md +++ b/proposals/NNNN-vars-without-limits-in-result-builders.md @@ -1,24 +1,23 @@ # Lift all limitations on variables in result builders -* Authors: [Pavel Yaskevich](https://github.com/xedin) -* Implementation: [apple/swift#60839](https://github.com/apple/swift/pull/60839) -* Review Manager: TBD +* Proposal: [SE-NNNN](NNNN-vars-without-limits-in-result-builders.md) +* Author: [Pavel Yaskevich](https://github.com/xedin) +* Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Awaiting discussion** +* Implementation: [apple/swift#60839](https://github.com/apple/swift/pull/60839) +* Review: ([pitch](https://forums.swift.org/t/pitch-lift-all-limitations-on-variables-in-result-builders/60460)) ## Introduction -Implementation of result builder transform (introduced by [SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md)) places a number of limitations on local variable declarations, specifically: all declarations should have initializer expression, cannot be computed, have observers, or attached property wrappers. None of the uses described above are explicitly restricted by SE-0289. - -Swift-evolution thread: [Pitch thread topic for this proposal](https://forums.swift.org/t/pitch-lift-all-limitations-on-variables-in-result-builders/60460) +The implementation of the result builder transform (introduced by [SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md)) places a number of limitations on local variable declarations in the transformed function. Specifically, local variables need to have an initializer expression, they cannot be computed, they cannot have observers, and they cannot have attached property wrappers. None of these restrictions were explicit in the SE-0289 proposal, but they are a *de facto* part of the current feature. ## Motivation -Result builder proposal [describes how individual components in a result builder body are transformed](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#the-result-builder-transform), and it states that local declaration statements are unaffected by the transformation, which implies that all declarations allowed in context should be supported but that is not the case under current implementation that requires that declarations to have a simple name, storage, and an initializing expression. +The result builder proposal [describes how the result builder transform handles each individual component in a function body](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#the-result-builder-transform). It states that local declarations are unaffected by the transformation, which implies that any declaration allowed in that context should be supported. That is not the case under the current implementation, which requires that local variables declarations must have a simple name, storage, and an initializing expression. -In certain circumstances it's useful to be able to declare a local variable that, for example, declares multiple variables, has default initialization, or an attached property wrapper (with or without initializer). Let's take a look at a simple example: +In certain circumstances, it's useful to be able to declare a local variable that, for example, declares multiple variables, has default initialization, or has an attached property wrapper (with or without an initializer). Let's take a look at a simple example: - -``` +```swift func compute() -> (String, Error?) { ... } func test(@MyBuilder builder: () -> Int?) { @@ -44,14 +43,13 @@ test { } ``` +Both declarations are currently rejected because result builders only allow simple (with just one name) stored variables with an explicit initializer expression. -Both declarations are currently rejected because result builders only allow simple (with just one name) stored properties with an explicit initializer expression. - -Local variable declarations with property wrappers (with or w/o explicit initializer) could be utilized for a variety of use-cases, including but not limited to: +Local variable declarations with property wrappers (with or without an explicit initializer) can be utilized for a variety of use-cases, including but not limited to: * Verification and/or formatting of the user-provided input -``` +```swift import SwiftUI struct ContentView: View { @@ -66,7 +64,7 @@ struct ContentView: View { * Interacting with user defaults -``` +```swift import SwiftUI struct AppIntroView: View { @@ -86,31 +84,29 @@ struct AppIntroView: View { ``` - ## Proposed solution -I propose to treat local variable declarations in the result builder bodies as-if they appear in a function or a multi-statement closure without any additional restrictions. +I propose to treat local variable declarations in functions transformed by result builders as if they appear in an ordinary function without any additional restrictions. ## Detailed design -The change is purely semantic, without any new syntax. It allows declaring: +The change is purely semantic, without any new syntax. It allows variables of all of these kinds to be declared in a function that will be transformed by a result builder: -* uninitialized -* default initialized -* computed -* observed -* property wrapped -* lazy +* uninitialized variables +* default-initialized variables (e.g. variables with optional type) +* computed variables +* observed variables +* variables with property wrappers +* `lazy` variables -properties in the result builder bodies and treat them just like they are treated in regular functions and closures, which means all of the semantics checks to verify their validity would still be performed and invalid (based on existing rules) declarations are still going to be rejected by the compiler. +These variables will be treated just like they are treated in regular functions. All of the ordinary semantic checks to verify their validity will still be performed, and invalid declarations (based on the standard rules) will still be rejected by the compiler. -Uninitialized variables are of particular interest because they require special support in the result builder as stated in [SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#assignments) otherwise there is no way to initialize them. +Uninitialized variables are of particular interest because they require special support in the result builder as stated in [SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#assignments); otherwise, there is no way to initialize them. ## Source compatibility This is an additive change which should not affect existing source code. - ## Effect on ABI stability and API resilience -These changes do not require support from the language runtime or standard library. +These changes do not require support from the language runtime or standard library, and they do not change anything about the external interface to the transformed function. From 627c6d6bbb3630b114f2560a395b944d72ae11ee Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 30 Sep 2022 21:38:18 -0400 Subject: [PATCH 2747/4563] Assign "SE-0373" to the result builder locals generalization and put it in review --- ...ders.md => 0373-vars-without-limits-in-result-builders.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{NNNN-vars-without-limits-in-result-builders.md => 0373-vars-without-limits-in-result-builders.md} (97%) diff --git a/proposals/NNNN-vars-without-limits-in-result-builders.md b/proposals/0373-vars-without-limits-in-result-builders.md similarity index 97% rename from proposals/NNNN-vars-without-limits-in-result-builders.md rename to proposals/0373-vars-without-limits-in-result-builders.md index c185050c38..f6ca2d272b 100644 --- a/proposals/NNNN-vars-without-limits-in-result-builders.md +++ b/proposals/0373-vars-without-limits-in-result-builders.md @@ -1,9 +1,9 @@ # Lift all limitations on variables in result builders -* Proposal: [SE-NNNN](NNNN-vars-without-limits-in-result-builders.md) +* Proposal: [SE-0373](0373-vars-without-limits-in-result-builders.md) * Author: [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Awaiting discussion** +* Status: **Active Review (September 30...October 10th, 2022)** * Implementation: [apple/swift#60839](https://github.com/apple/swift/pull/60839) * Review: ([pitch](https://forums.swift.org/t/pitch-lift-all-limitations-on-variables-in-result-builders/60460)) From 06c716259aebc5eaf77e61b8536cdd4586414c48 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 30 Sep 2022 21:43:14 -0400 Subject: [PATCH 2748/4563] Link SE-0373 to its review --- proposals/0373-vars-without-limits-in-result-builders.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0373-vars-without-limits-in-result-builders.md b/proposals/0373-vars-without-limits-in-result-builders.md index f6ca2d272b..b40cb3ea22 100644 --- a/proposals/0373-vars-without-limits-in-result-builders.md +++ b/proposals/0373-vars-without-limits-in-result-builders.md @@ -5,7 +5,7 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active Review (September 30...October 10th, 2022)** * Implementation: [apple/swift#60839](https://github.com/apple/swift/pull/60839) -* Review: ([pitch](https://forums.swift.org/t/pitch-lift-all-limitations-on-variables-in-result-builders/60460)) +* Review: ([pitch](https://forums.swift.org/t/pitch-lift-all-limitations-on-variables-in-result-builders/60460)) ([review](https://forums.swift.org/t/se-0373-lift-all-limitations-on-variables-in-result-builders/60592)) ## Introduction From 767727186e7b02acddfd695a13ff8cb87b1de98e Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 6 Oct 2022 10:15:23 -0700 Subject: [PATCH 2749/4563] [SE-0373] Clarify how uninitialized variables are handled --- proposals/0373-vars-without-limits-in-result-builders.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0373-vars-without-limits-in-result-builders.md b/proposals/0373-vars-without-limits-in-result-builders.md index b40cb3ea22..dfdbae519a 100644 --- a/proposals/0373-vars-without-limits-in-result-builders.md +++ b/proposals/0373-vars-without-limits-in-result-builders.md @@ -92,7 +92,7 @@ I propose to treat local variable declarations in functions transformed by resul The change is purely semantic, without any new syntax. It allows variables of all of these kinds to be declared in a function that will be transformed by a result builder: -* uninitialized variables +* uninitialized variables (only if supported by the builder, see below for more details) * default-initialized variables (e.g. variables with optional type) * computed variables * observed variables @@ -101,7 +101,8 @@ The change is purely semantic, without any new syntax. It allows variables of al These variables will be treated just like they are treated in regular functions. All of the ordinary semantic checks to verify their validity will still be performed, and invalid declarations (based on the standard rules) will still be rejected by the compiler. -Uninitialized variables are of particular interest because they require special support in the result builder as stated in [SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#assignments); otherwise, there is no way to initialize them. +There is one notable exception from this general rule - declaration of an uninitialized variable is going to be rejected by the compiler if the result builder doesn't support `buildExpression(_: Void)` operation as stated by [SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#assignments). + ## Source compatibility From bb6faf8fc91b29046c9d1427384106a4dc73ab9b Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 6 Oct 2022 12:02:09 -0700 Subject: [PATCH 2750/4563] Update proposals/0373-vars-without-limits-in-result-builders.md Co-authored-by: John McCall --- proposals/0373-vars-without-limits-in-result-builders.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0373-vars-without-limits-in-result-builders.md b/proposals/0373-vars-without-limits-in-result-builders.md index dfdbae519a..346135e82f 100644 --- a/proposals/0373-vars-without-limits-in-result-builders.md +++ b/proposals/0373-vars-without-limits-in-result-builders.md @@ -101,7 +101,7 @@ The change is purely semantic, without any new syntax. It allows variables of al These variables will be treated just like they are treated in regular functions. All of the ordinary semantic checks to verify their validity will still be performed, and invalid declarations (based on the standard rules) will still be rejected by the compiler. -There is one notable exception from this general rule - declaration of an uninitialized variable is going to be rejected by the compiler if the result builder doesn't support `buildExpression(_: Void)` operation as stated by [SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#assignments). +There is one notable exception to this general rule. Initializing a variable after its declaration requires writing an assignment to it, and assignments require the result builder to support `Void` results, as described in [SE-0289](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md#assignments). If the result builder does not support `Void` results (whether with an explicit `buildExpression` or just by handling them in `buildBlock`), transformed functions will not be allowed to contain uninitialized declarations. ## Source compatibility From 0e1615663d4788789b222562845e5795167fa27b Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 7 Oct 2022 19:10:38 -0400 Subject: [PATCH 2751/4563] Return SE-0018 for revision --- proposals/0018-flexible-memberwise-initialization.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/proposals/0018-flexible-memberwise-initialization.md b/proposals/0018-flexible-memberwise-initialization.md index 42432c781a..9b1646ff2f 100644 --- a/proposals/0018-flexible-memberwise-initialization.md +++ b/proposals/0018-flexible-memberwise-initialization.md @@ -3,15 +3,13 @@ * Proposal: [SE-0018](0018-flexible-memberwise-initialization.md) * Author: [Matthew Johnson](https://github.com/anandabits) * Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Deferred** -* Decision Notes: [Rationale](https://forums.swift.org/t/review-se-0018-flexible-memberwise-initialization/939/22) +* Status: **Returned for revision** +* Review: ([pitch](https://forums.swift.org/t/proposal-draft-flexible-memberwise-initialization/698)) ([review](https://forums.swift.org/t/review-se-0018-flexible-memberwise-initialization/939)) ([deferral](https://forums.swift.org/t/review-se-0018-flexible-memberwise-initialization/939/22)) ([return for revision](https://forums.swift.org/t/returning-or-rejecting-all-the-deferred-evolution-proposals/60724)) ## Introduction The Swift compiler is currently able to generate a memberwise initializer for use in some circumstances, however there are currently many limitations to this. This proposal builds on the idea of a compiler generated memberwise initializer, making the capability available to any initializer that opts in. -Swift-evolution thread: [Proposal Draft: flexible memberwise initialization](https://forums.swift.org/t/proposal-draft-flexible-memberwise-initialization/698) - ## Motivation When designing initializers for a type we are currently faced with the unfortunate fact that the more flexibility we wish to offer users the more boilerplate we are required to write and maintain. We usually end up with more boilerplate and less flexibility than desired. There have been various strategies employed to mitigate this problem, including: From f331854e8b1d30ca1cdbc16f9182232b37888aff Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 7 Oct 2022 19:16:13 -0400 Subject: [PATCH 2752/4563] Return SE-0078 for revision --- proposals/0078-rotate-algorithm.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0078-rotate-algorithm.md b/proposals/0078-rotate-algorithm.md index ddec37005a..233cab708f 100644 --- a/proposals/0078-rotate-algorithm.md +++ b/proposals/0078-rotate-algorithm.md @@ -3,9 +3,9 @@ * Proposal: [SE-0078](0078-rotate-algorithm.md) * Authors: [Nate Cook](https://github.com/natecook1000), [Sergey Bolshedvorsky](https://github.com/bolshedvorsky) * Review Manager: [Chris Lattner](http://github.com/lattner) -* Status: **Deferred** -* Decision Notes: [Rationale](https://forums.swift.org/t/deferred-se-0078-implement-a-rotate-algorithm-equivalent-to-std-rotate-in-c/2744) +* Status: **Returned for revision** * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/f5936651da1a08e2335a4991831db61da29aba15/proposals/0078-rotate-algorithm.md), [2](https://github.com/apple/swift-evolution/blob/8d45024ed7baacce94e22080d74f136bebc5c075/proposals/0078-rotate-algorithm.md) +* Review: ([pitch](https://forums.swift.org/t/proposal-implement-a-rotate-algorithm-equivalent-to-std-rotate-in-c/491)) ([review](https://forums.swift.org/t/review-se-0078-implement-a-rotate-algorithm-equivalent-to-std-rotate-in-c/2440)) ([return for revision](https://forums.swift.org/t/review-se-0078-implement-a-rotate-algorithm-equivalent-to-std-rotate-in-c/2440/3)) ([immediate deferral](https://forums.swift.org/t/deferred-se-0078-implement-a-rotate-algorithm-equivalent-to-std-rotate-in-c/2744)) ([return for revision #2](https://forums.swift.org/t/returning-or-rejecting-all-the-deferred-evolution-proposals/60724)) ## Introduction From 3f3bf267ecb04d609719e339dd0b3163c75a333f Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 7 Oct 2022 19:22:55 -0400 Subject: [PATCH 2753/4563] Return SE-0090 for revision --- proposals/0090-remove-dot-self.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0090-remove-dot-self.md b/proposals/0090-remove-dot-self.md index 3bf2f5166f..8f491503f1 100644 --- a/proposals/0090-remove-dot-self.md +++ b/proposals/0090-remove-dot-self.md @@ -6,6 +6,7 @@ * Status: **Deferred** * Decision Notes: [Rationale](https://forums.swift.org/t/deferred-se-0090-remove-self-and-freely-allow-type-references-in-expressions/2781) * Revision: 2 +* Review: ([pitch](https://forums.swift.org/t/making-self-after-type-optional/1737)) ([review](https://forums.swift.org/t/review-se-0090-remove-self-and-freely-allow-type-references-in-expressions/2664)) ([deferral](https://forums.swift.org/t/deferred-se-0090-remove-self-and-freely-allow-type-references-in-expressions/2781)) ([return for revision](https://forums.swift.org/t/returning-or-rejecting-all-the-deferred-evolution-proposals/60724)) ## Introduction @@ -15,8 +16,6 @@ for `T`, one must refer to the special member `T.self`. I propose allowing type references to appear freely in expressions and removing the `.self` member from the language. -Swift-evolution thread: [Making `.self` After `Type` Optional](https://forums.swift.org/t/making-self-after-type-optional/1737) - ## Motivation The constructor-or-member restriction on type references exists to provide From 67825e986c85f9712cd28c7d74d85ac646276d77 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 7 Oct 2022 19:25:23 -0400 Subject: [PATCH 2754/4563] Reject SE-0026 --- proposals/0026-abstract-classes-and-methods.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/proposals/0026-abstract-classes-and-methods.md b/proposals/0026-abstract-classes-and-methods.md index d042e646c1..c86badb14a 100644 --- a/proposals/0026-abstract-classes-and-methods.md +++ b/proposals/0026-abstract-classes-and-methods.md @@ -3,8 +3,8 @@ * Proposal: [SE-0026](0026-abstract-classes-and-methods.md) * Author: David Scrève * Review Manager: [Joe Groff](https://github.com/jckarter/) -* Status: **Deferred** -* Decision Notes: [Rationale](https://forums.swift.org/t/deferred-se-0026-abstract-classes-and-methods/1705) +* Status: **Rejected** +* Review: ([pitch](https://forums.swift.org/t/proposal-draff-abstract-classes-and-methods/965)) ([review](https://forums.swift.org/t/review-se-0026-abstract-classes-and-methods/1580)) ([deferral](https://forums.swift.org/t/deferred-se-0026-abstract-classes-and-methods/1705)) ([rejection](https://forums.swift.org/t/returning-or-rejecting-all-the-deferred-evolution-proposals/60724)) ## Introduction @@ -14,8 +14,6 @@ they cannot have attributes as classes have. A partial class combines the behavior of a class with the requirement of implementing methods in inherited class like protocols. -[Swift-Evolution Discussion](https://forums.swift.org/t/proposal-draff-abstract-classes-and-methods/965) - ## Motivation like pure virtual methods in C++ and abtract classes in Java and C#, frameworks development From 9f739d26046a214ffed1aee4c723a445f9ffd6a9 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 7 Oct 2022 19:27:32 -0400 Subject: [PATCH 2755/4563] Reject SE-0058 --- proposals/0058-objectivecbridgeable.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/proposals/0058-objectivecbridgeable.md b/proposals/0058-objectivecbridgeable.md index 8ae8771038..6461ecec58 100644 --- a/proposals/0058-objectivecbridgeable.md +++ b/proposals/0058-objectivecbridgeable.md @@ -3,16 +3,13 @@ * Proposal: [SE-0058](0058-objectivecbridgeable.md) * Authors: [Russ Bishop](https://github.com/russbishop), [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Deferred** -* Decision Notes: [Rationale](https://forums.swift.org/t/deferred-se-0058-allow-swift-types-to-provide-custom-objective-c-representations/2167) +* Status: **Rejected** +* Review: ([pitch](https://forums.swift.org/t/idea-objectivecbridgeable/1559)) ([review](https://forums.swift.org/t/review-se-0058-allow-swift-types-to-provide-custom-objective-c-representations/2054)) ([deferral](https://forums.swift.org/t/deferred-se-0058-allow-swift-types-to-provide-custom-objective-c-representations/2167)) ([rejection](https://forums.swift.org/t/returning-or-rejecting-all-the-deferred-evolution-proposals/60724)) ## Introduction Provide an `ObjectiveCBridgeable` protocol that allows a Swift type to control how it is represented in Objective-C by converting into and back from an entirely separate `@objc` type. This frees library authors to create truly native Swift APIs while still supporting Objective-C. -Swift-evolution thread: [\[Idea\] ObjectiveCBridgeable](https://forums.swift.org/t/idea-objectivecbridgeable/1559) - - ## Motivation There is currently no good way to define a Swift-y API that makes use of generics, enums with associated values, structs, protocols with associated types, and other Swift features while still exposing that API to Objective-C. From 42a70438383f5cb29b2ab08676dec076f1a7d1cf Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 7 Oct 2022 19:29:33 -0400 Subject: [PATCH 2756/4563] Reject SE-0083 --- proposals/0083-remove-bridging-from-dynamic-casts.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/proposals/0083-remove-bridging-from-dynamic-casts.md b/proposals/0083-remove-bridging-from-dynamic-casts.md index 8e6e110b03..4f224f911c 100644 --- a/proposals/0083-remove-bridging-from-dynamic-casts.md +++ b/proposals/0083-remove-bridging-from-dynamic-casts.md @@ -3,8 +3,8 @@ * Proposal: [SE-0083](0083-remove-bridging-from-dynamic-casts.md) * Author: [Joe Groff](https://github.com/jckarter) * Review Manager: [Chris Lattner](http://github.com/lattner) -* Status: **Deferred** -* Decision Notes: [Rationale](https://forums.swift.org/t/deferred-to-later-in-swift-3-se-0083-remove-bridging-conversion-behavior-from-dynamic-casts/2780) +* Status: **Rejected** +* Review: ([pitch](https://forums.swift.org/t/pitch-reducing-the-bridging-magic-in-dynamic-casts/2398)) ([review](https://forums.swift.org/t/review-se-0083-remove-bridging-conversion-behavior-from-dynamic-casts/2544)) ([deferral](https://forums.swift.org/t/deferred-to-later-in-swift-3-se-0083-remove-bridging-conversion-behavior-from-dynamic-casts/2780)) ([rejection](https://forums.swift.org/t/returning-or-rejecting-all-the-deferred-evolution-proposals/60724)) ## Introduction @@ -16,8 +16,6 @@ easier to understand. To replace this functionality, initializers should be added to bridged types, providing an interface for these conversions that's more consistent with the conventions of the standard library. -Swift-evolution thread: [Reducing the bridging magic in dynamic casts](https://forums.swift.org/t/pitch-reducing-the-bridging-magic-in-dynamic-casts/2398) - ## Motivation When we introduced Swift, we wanted to provide value types for common From 5f5b7b4e9edd23451315ac4f05db37f49b2e367e Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 8 Oct 2022 00:56:00 +0100 Subject: [PATCH 2757/4563] =?UTF-8?q?[SE-0090]=20"Deferred"=20=E2=86=92=20?= =?UTF-8?q?"Returned=20for=20revision"=20(#1807)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proposals/0090-remove-dot-self.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/proposals/0090-remove-dot-self.md b/proposals/0090-remove-dot-self.md index 8f491503f1..ccbcc17a46 100644 --- a/proposals/0090-remove-dot-self.md +++ b/proposals/0090-remove-dot-self.md @@ -3,9 +3,7 @@ * Proposal: [SE-0090](0090-remove-dot-self.md) * Authors: [Joe Groff](https://github.com/jckarter), [Tanner Nelson](https://github.com/tannernelson) * Review Manager: [Chris Lattner](http://github.com/lattner) -* Status: **Deferred** -* Decision Notes: [Rationale](https://forums.swift.org/t/deferred-se-0090-remove-self-and-freely-allow-type-references-in-expressions/2781) -* Revision: 2 +* Status: **Returned for revision** * Review: ([pitch](https://forums.swift.org/t/making-self-after-type-optional/1737)) ([review](https://forums.swift.org/t/review-se-0090-remove-self-and-freely-allow-type-references-in-expressions/2664)) ([deferral](https://forums.swift.org/t/deferred-se-0090-remove-self-and-freely-allow-type-references-in-expressions/2781)) ([return for revision](https://forums.swift.org/t/returning-or-rejecting-all-the-deferred-evolution-proposals/60724)) ## Introduction From bf06fb146c47f7f426a7ffe19bd77e815f19d2f9 Mon Sep 17 00:00:00 2001 From: taylorswift Date: Sun, 9 Oct 2022 12:10:42 -0500 Subject: [PATCH 2758/4563] fix typo "swife" -> "swift" --- proposals/0339-module-aliasing-for-disambiguation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0339-module-aliasing-for-disambiguation.md b/proposals/0339-module-aliasing-for-disambiguation.md index bdf4754ea5..eba07d45c1 100644 --- a/proposals/0339-module-aliasing-for-disambiguation.md +++ b/proposals/0339-module-aliasing-for-disambiguation.md @@ -180,7 +180,7 @@ The `App` manifest needs to explicitly define unique names for the conflicting m } ``` -SwiftPM will perform validations when it parses `moduleAliases`; for each entry, it will check whether the given alias is a unique name, whether there is a conflict among aliases, whether the specified module is built from source (pre-compiled modules cannot be rebuilt to respect the rename), and whether the module is a pure Swife module (see **Requirements/Limitations** section for more details). +SwiftPM will perform validations when it parses `moduleAliases`; for each entry, it will check whether the given alias is a unique name, whether there is a conflict among aliases, whether the specified module is built from source (pre-compiled modules cannot be rebuilt to respect the rename), and whether the module is a pure Swift module (see **Requirements/Limitations** section for more details). It will also check if any aliases are defined in upstream packages and override them if necessary. For example, if the `swift-game` package were modified per below and defined its own alias `SwiftUtils` for module `Utils` from a dependency package, the alias defined in `App` will override it, thus the `Utils` module from `swift-utils` will be built as `GameUtils`. From a0ff2a64b2d52668e5c3c03173c561851f6e0780 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 11 Oct 2022 17:25:51 +0100 Subject: [PATCH 2759/4563] SE-0305: add a link to SE-0272 for referenced type (#1811) --- proposals/0305-swiftpm-binary-target-improvements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0305-swiftpm-binary-target-improvements.md b/proposals/0305-swiftpm-binary-target-improvements.md index 4efdd03dba..eefe754cc9 100644 --- a/proposals/0305-swiftpm-binary-target-improvements.md +++ b/proposals/0305-swiftpm-binary-target-improvements.md @@ -16,7 +16,7 @@ This proposal extends SwiftPM binary targets to also support other kinds of preb ## Motivation -The Swift Package Manager’s `binaryTarget` type lets packages vend libraries that either cannot be built in Swift Package Manager for technical reasons, or for which the source code cannot be published for legal or other reasons. +The Swift Package Manager’s [`binaryTarget` type](https://github.com/apple/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md) lets packages vend libraries that either cannot be built in Swift Package Manager for technical reasons, or for which the source code cannot be published for legal or other reasons. In the current version of SwiftPM, binary targets only support libraries in an Xcode-oriented format called *XCFramework*, and only for Apple platforms. From de97b1d8c751c08626d1d32f5d847d264041b7eb Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 11 Oct 2022 17:58:45 +0100 Subject: [PATCH 2760/4563] SE-0325: link to SE-0303 when referenced first time (#1812) --- proposals/0325-swiftpm-additional-plugin-apis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0325-swiftpm-additional-plugin-apis.md b/proposals/0325-swiftpm-additional-plugin-apis.md index e4dbaa8f51..2cb066de8d 100644 --- a/proposals/0325-swiftpm-additional-plugin-apis.md +++ b/proposals/0325-swiftpm-additional-plugin-apis.md @@ -10,7 +10,7 @@ ## Introduction -SE-0303 introduced the ability to define *build tool plugins* in SwiftPM, allowing custom tools to be invoked while building a package. In support of this, SE-0303 introduced a minimal initial API through which plugins can access information about the target for which they are invoked. +[SE-0303](https://github.com/apple/swift-evolution/blob/main/proposals/0303-swiftpm-extensible-build-tools.md) introduced the ability to define *build tool plugins* in SwiftPM, allowing custom tools to be invoked while building a package. In support of this, SE-0303 introduced a minimal initial API through which plugins can access information about the target for which they are invoked. This proposal extends the plugin API to provide more context, including a richer representation of the package graph. This is in preparation for supporting new kinds of plugins in the future. From 5f4db2962df7781b50d53389cba54343e6053466 Mon Sep 17 00:00:00 2001 From: Brandon Williams <135203+mbrandonw@users.noreply.github.com> Date: Tue, 11 Oct 2022 13:07:10 -0400 Subject: [PATCH 2761/4563] Add sleep(for:) to Clock (#1786) Update and rename NNNN-clock-sleep-for.md to 0374-clock-sleep-for.md Co-authored-by: Stephen Celis Co-authored-by: Stephen Canon --- proposals/0374-clock-sleep-for.md | 169 ++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 proposals/0374-clock-sleep-for.md diff --git a/proposals/0374-clock-sleep-for.md b/proposals/0374-clock-sleep-for.md new file mode 100644 index 0000000000..34265ce2c0 --- /dev/null +++ b/proposals/0374-clock-sleep-for.md @@ -0,0 +1,169 @@ +# Add sleep(for:) to Clock + +* Proposal: [SE-0374](0374-clock-sleep-for.md) +* Authors: [Brandon Williams](https://github.com/mbrandonw), [Stephen Celis](https://github.com/stephencelis) +* Review Manager: [Steve Canon](https://github.com/stephentyrone) +* Status: **Active Review (Oct 11 ... 25, 2022)** +* Implementation: [apple/swift#61222](https://github.com/apple/swift/pull/61222) + + +## Introduction + +The `Clock` protocol introduced in Swift 5.7 provides a way to suspend until a future instant, but +does not provide a way to sleep for a duration. This differs from the static `sleep` methods on +`Task`, which provide both a way to sleep until an instant or for a duration. + +This imbalance in APIs might be reason enough to add a `sleep(for:)` method to all clocks, but the +real problem occurs when dealing with `Clock` existentials. Because the `Instant` associated type +is fully erased, and only the `Duration` is preserved via the primary associated type, any API +that deals with instants is inaccessible to an existential. This means one cannot invoke +`sleep(until:)` on an existential clock, and hence you can't really do anything with an existential +clock. + +Swift-evolution thread: https://forums.swift.org/t/pitch-clock-sleep-for/60376 + +## Motivation + +Existentials provide a convenient way to inject dependencies into features so that you can use one +kind of dependency in production, and another kind in tests. The most prototypical version of this +is API clients. When you run your feature in production you want the API client to make real life +network requests, but when run in tests you may want it to just return some mock data. + +Due to the current design of `Clock`, it is not possible to inject a clock existential into a +feature so that you can use a `ContinuousClock` in production, but some other kind of controllable +clock in tests. + +For example, suppose you have an observable object for the logic of some feature that wants to show +a welcoming message after waiting 5 seconds. That might look like this: + +```swift +class FeatureModel: ObservableObject { + @Published var message: String? + func onAppear() async { + do { + try await Task.sleep(until: .now.advanced(by: .seconds(5))) + self.message = "Welcome!" + } catch {} + } +} +``` + +If you wrote a test for this, your test suite would have no choice but to wait for 5 real life +seconds to pass before it could make an assertion: + +```swift +let model = FeatureModel() + +XCTAssertEqual(model.message, nil) +await model.onAppear() // Waits for 5 seconds +XCTAssertEqual(model.message, "Welcome!") +``` + +This affects people who don't even write tests. If you put your feature into an Xcode preview, then +you would have to wait for 5 full seconds to pass before you get to see the welcome message. That +means you can't quickly iterate on the styling of that message. + +The solution to these problems is to not reach out to the global, uncontrollable `Task.sleep`, and +instead inject a clock into the feature. And that is typically done using an existential, but +unfortunately that does not work: + +```swift +class FeatureModel: ObservableObject { + @Published var message: String? + let clock: any Clock + + func onAppear() async { + do { + try await self.clock.sleep(until: self.clock.now.advanced(by: .seconds(5))) // 🛑 + self.message = "Welcome!" + } catch {} + } +} +``` + +One cannot invoke `sleep(until:)` on a clock existential because the `Instant` has been fully +erased, and so there is no way to access `.now` and advance it. + +For similar reasons, one cannot invoke `Task.sleep(until:clock:)` with a clock existential: + +```swift +try await Task.sleep(until: self.clock.now.advanced(by: .seconds(5)), clock: self.clock) // 🛑 +``` + +What we need instead is the `sleep(for:)` method on clocks that allow you to sleep for a duration +rather than sleeping until an instant: + +```swift +class FeatureModel: ObservableObject { + @Published var message: String? + let clock: any Clock + + func onAppear() async { + do { + try await self.clock.sleep(for: .seconds(5)) // ✅ + self.message = "Welcome!" + } catch {} + } +} +``` + +Without a `sleep(for:)` method on clocks, one cannot use a clock existential in the feature, and +that forces you to introduce a generic: + +```swift +class FeatureModel>: ObservableObject { + @Published var message: String? + let clock: C + + func onAppear() async { + do { + try await self.clock.sleep(until: self.clock.now.advanced(by: .seconds(5))) + self.message = "Welcome!" + } catch {} + } +} +``` + +But this is problematic. This will force any code that touches `FeatureModel` to also introduce a +generic if you want that code to be testable and controllable. And it's strange that the class +is statically announcing its dependence on a clock when its mostly just an internal detail of the +class. + +By adding a `sleep(for:)` method to `Clock` we can fix all of these problems, and give Swift users +the ability to control time-based asynchrony in their applications. + +## Proposed solution + +A single extension method will be added to the `Clock` protocol: + +```swift +extension Clock { + /// Suspends for the given duration. + /// + /// Prefer to use the `sleep(until:tolerance:)` method on `Clock` if you have access to an + /// absolute instant. + public func sleep( + for duration: Duration, + tolerance: Duration? = nil + ) async throws { + try await self.sleep(until: self.now.advanced(by: duration), tolerance: tolerance) + } +} +``` + + + +This will allow one to sleep for a duration with a clock rather than sleeping until an instant. + +## Detailed design + +## Source compatibility, effect on ABI stability, effect on API resilience + +As this is an additive change, it should not have any compatibility, stability or resilience +problems. The only potential problem would be if someone has already run into this shortcoming +and decided to define their own `sleep(for:)` method on clocks. + +## Alternatives considered + +We could leave things as is, and not add this method to the standard library, as it is possible for +people to define it themselves. From c4e74554a8e01d5654e7adf6e3a0565f720aab9a Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 11 Oct 2022 13:12:54 -0400 Subject: [PATCH 2762/4563] Update 0374-clock-sleep-for.md (#1813) --- proposals/0374-clock-sleep-for.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0374-clock-sleep-for.md b/proposals/0374-clock-sleep-for.md index 34265ce2c0..32a8d2d025 100644 --- a/proposals/0374-clock-sleep-for.md +++ b/proposals/0374-clock-sleep-for.md @@ -5,6 +5,7 @@ * Review Manager: [Steve Canon](https://github.com/stephentyrone) * Status: **Active Review (Oct 11 ... 25, 2022)** * Implementation: [apple/swift#61222](https://github.com/apple/swift/pull/61222) +* Review: ([pitch](https://forums.swift.org/t/pitch-clock-sleep-for/60376)) ([review](https://forums.swift.org/t/se-0374-add-sleep-for-to-clock/60787)) ## Introduction From 3e6d741ad1942c0865426b0d09fafa22a67bf609 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 11 Oct 2022 19:38:21 -0700 Subject: [PATCH 2763/4563] Add proposal "Opening existential arguments to optional parameters" (#1799) * Add proposal "Opening existential arguments to optional parameters" * Update 0375-opening-existential-optional.md Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- .../0375-opening-existential-optional.md | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 proposals/0375-opening-existential-optional.md diff --git a/proposals/0375-opening-existential-optional.md b/proposals/0375-opening-existential-optional.md new file mode 100644 index 0000000000..e69053e748 --- /dev/null +++ b/proposals/0375-opening-existential-optional.md @@ -0,0 +1,71 @@ +# Opening existential arguments to optional parameters + +* Proposal: [SE-0375](0375-opening-existential-optional.md) +* Authors: [Doug Gregor](https://github.com/DougGregor) +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Active Review (October 11...25, 2022)** +* Implementation: [apple/swift#61321](https://github.com/apple/swift/pull/61321) +* Review: ([pitch](https://forums.swift.org/t/mini-pitch-for-se-0352-amendment-allow-opening-an-existential-argument-to-an-optional-parameter/60501)) + +## Introduction + +[SE-0352 "Implicitly Opened Existentials"](https://github.com/apple/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md) has a limitation that prevents the opening of an existential argument when the corresponding parameter is optional. This proposal changes that behavior, so that such a call will succeed when a (non-optional) existential argument is passed to a parameter of optional type: + +```swift +func acceptOptional(_ x: T?) { } +func test(p: any P, pOpt: (any P)?) { + acceptOptional(p) // SE-0352 does not open "p"; this proposal will open "p" and bind "T" to its underlying type + acceptOptional(pOpt) // does not open "pOpt", because there is no "T" to bind to when "pOpt" is "nil" +} +``` + +The rationale for not opening the existential `p` in the first call was to ensure consistent behavior with the second call, in an effort to avoid confusion. SE-0352 says: + +> The case of optionals is somewhat interesting. It's clear that the call `cannotOpen6(pOpt)` cannot work because `pOpt` could be `nil`, in which case there is no type to bind `T` to. We *could* choose to allow opening a non-optional existential argument when the parameter is optional, e.g., +> +> ``` +> cannotOpen6(p1) // we *could* open here, binding T to the underlying type of p1, but choose not to +> ``` +> +> but this proposal doesn't allow this because it would be odd to allow this call but not the `cannotOpen6(pOpt)` call. + +However, experience with implicitly-opened existentials has shown that opening an existential argument in the first case is important, because many functions accept optional parameters. It is possible to work around this limitation, but doing so requires a bit of boilerplate, using a generic function that takes a non-optional parameter as a trampoline to the one that takes an optional parameter: + +```swift +func acceptNonOptionalThunk(_ x: T) { + acceptOptional(x) +} + +func test(p: any P) { + acceptNonOptionalThunk(p) // workaround for SE-0352 to get a call to acceptOptional with opened existential +} +``` + +## Proposed solution + +Allow an argument of (non-optional) existential type to be opened to be passed to an optional parameter: + +```swift +func openOptional(_ value: T?) { } + +func testOpenToOptional(p: any P) { + openOptional(p) // okay, opens 'p' and binds 'T' to its underlying type +} +``` + +## Source compatibility + +Generally speaking, opening an existential argument in one more case will make code that would have been rejected by the compiler (e.g., with an error like "`P` does not conform to `P`") into code that is accepted, because the existential is opened. This can change the behavior of overload resulting, in the same manner as was [discussed in SE-0352](https://github.com/apple/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md#source-compatibility). Experience with SE-0352's integration into Swift 5.7 implies that the practical effect of these changes is quite small. + +## Effect on ABI stability + +This proposal changes the type system but has no ABI impact whatsoever. + +## Effect on API resilience + +This proposal changes the use of APIs, but not the APIs themselves, so it doesn't impact API resilience per se. + +## Alternatives considered + +Describe alternative approaches to addressing the same problem, and +why you chose this approach instead. From 240bb0da34b4a3edf7fa91541bcfe774468ddb89 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 11 Oct 2022 22:48:17 -0400 Subject: [PATCH 2764/4563] Update 0375-opening-existential-optional.md (#1814) --- proposals/0375-opening-existential-optional.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0375-opening-existential-optional.md b/proposals/0375-opening-existential-optional.md index e69053e748..07d8823570 100644 --- a/proposals/0375-opening-existential-optional.md +++ b/proposals/0375-opening-existential-optional.md @@ -5,7 +5,7 @@ * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Active Review (October 11...25, 2022)** * Implementation: [apple/swift#61321](https://github.com/apple/swift/pull/61321) -* Review: ([pitch](https://forums.swift.org/t/mini-pitch-for-se-0352-amendment-allow-opening-an-existential-argument-to-an-optional-parameter/60501)) +* Review: ([pitch](https://forums.swift.org/t/mini-pitch-for-se-0352-amendment-allow-opening-an-existential-argument-to-an-optional-parameter/60501)) ([review](https://forums.swift.org/t/se-0375-opening-existential-arguments-to-optional-parameters/60802)) ## Introduction From ddd346091c45f671a036a9d3888aa2fe824895f8 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Sun, 16 Oct 2022 15:04:38 -0700 Subject: [PATCH 2765/4563] [se-0370] Mark as implemented --- proposals/0370-pointer-family-initialization-improvements.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index 3c5f0efac4..077e99b3a1 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -3,8 +3,8 @@ * Proposal: [SE-0370](0370-pointer-family-initialization-improvements.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** -* Implementation: [Draft Pull Request](https://github.com/apple/swift/pull/41608) +* Status: **Implemented (Swift 5.8)** +* Implementation: [apple/swift#41608](https://github.com/apple/swift/pull/41608) * Review: ([first pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements/53168)) ([second pitch](https://forums.swift.org/t/pitch-buffer-partial-initialization-better-buffer-slices/53795)) ([third pitch](https://forums.swift.org/t/pitch-pointer-family-initialization-improvements-better-buffer-slices/55689)) ([review](https://forums.swift.org/t/se-0370-pointer-family-initialization-improvements-and-better-buffer-slices/59724)) ([acceptance](https://forums.swift.org/t/accepted-se-0370-pointer-family-initialization-improvements-and-better-buffer-slices/60007)) ## Introduction From 8abf51666e6e8ff3dcf64f8e244a60045b328b49 Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Wed, 15 Jun 2022 13:06:50 +0100 Subject: [PATCH 2766/4563] This proposal introduces a new level of Reflection metadata emission and allows to express a requirement on Reflection metadata in APIs --- proposals/NNNN-opt-in-reflection-metadata.md | 132 +++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 proposals/NNNN-opt-in-reflection-metadata.md diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md new file mode 100644 index 0000000000..dcb61ea6ed --- /dev/null +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -0,0 +1,132 @@ + +# Swift Opt-In Reflection Metadata + +* Proposal: [SE-NNNN](NNNN-opt-in-reflection-metadata.md) +* Authors: [Max Ovtsin](https://github.com/maxovtsin) +* Review Manager: TBD +* Status: **Awaiting implementation** +* Implementation: [apple/swift#34199](https://github.com/apple/swift/pull/34199) + +## Introduction + +This proposal seeks to increase the safety and efficiency of Swift Reflection Metadata by improving the existing mechanism and providing the opportunity to express a requirement on Reflection Metadata in APIs that consume it. + +Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/pitch-2-opt-in-reflection-metadata/41696) + + +## Motivation + +APIs can use Reflection Metadata differently. Some like `print`, and `dump` will still work with disabled reflection, but the output will be limited. Others, like SwiftUI rely on it and won't work correctly if the reflection metadata is missing. +While the former can potentially benefit as well, the main focus of this proposal is on the latter. + +A developer can mistakenly turn off Reflection Metadata for a Swift module and won't be warned at compile-time if APIs that consume reflection are used by that module. An app with such a module won't behave as expected at runtime which may be challenging to notice and track down such bugs back to Reflection. For instance, SwiftUI implementation uses reflection metadata from user modules to trigger re-rendering of the view hierarchy when a state has changed. If for some reason a user module was compiled with metadata generation disabled, changing the state won't trigger that behaviour and will cause inconsistency between state and representation which will make such API less safe since it becomes a runtime issue rather than a compile-time one. + +On the other hand, excessive Reflection metadata may be preserved in a binary even if not used, because there is currently no way to statically determine its usage. There was an attempt to limit the amount of unused reflection metadata by improving its stripability by the Dead Code Elimination LLVM pass, but in many cases, it’s still preserved in the binary because it’s referenced by Full Type Metadata which prevents Reflection Metadata from stripping. + +Introducing a static compilation check potentially can help to solve both of mentioned issues by adding to the language a way to express the requirement to have Reflection metadata at runtime. + + +## Proposed solution + +Teaching the Type-checker to ensure Reflection metadata is preserved in a binary if reflection-consuming APIs are used, will help to move the issue from runtime to compile time. + +To achieve that, a new marker protocol `Reflectable` will be introduced. Firstly, APIs developers will gain an opportunity to express a dependency on Reflection Metadata through a generic requirement of their functions, which will make such APIs safer. Secondly, during IRGen, the compiler will be able to selectively emit Reflection symbols for the types that explicitly conform to the `Reflectable` protocol, which will reduce the overhead from reflection symbols for cases when reflection is emitted but not consumed. + + + +### Case Study 1: + +SwiftUI Framework: +```swift +protocol SwiftUI.View: Reflectable {} +class NSHostingView where Content : View { + init(rootView: Content) { ... } +} +``` +User module: +```swift +import SwiftUI + +struct SomeModel {} + +struct SomeView: SwiftUI.View { + var body: some View { + Text("Hello, World!") + .frame(...) + } +} + +window.contentView = NSHostingView(rootView: SomeView()) +``` +Reflection metadata for `SomeView` will be emitted because it implicitly conforms to `Reflectable` protocol, while for `SomeModel` Reflection metadata won't be emitted. If the user module gets compiled with the reflection metadata disabled, the compiler will emit an error. + + +### Case Study 2: + +Framework: +```swift +public func foo(_ t: T) { ... } +``` +User module: +```swift +struct Bar: Reflectable {} +foo(Bar()) +``` +Reflection metadata for `Bar` will be emitted because it explicitly conforms to Reflectable protocol. Without conformance to Reflectable, an instance of type Bar can't be used on function `foo`. If the user module gets compiled with the reflection metadata disabled, the compiler will emit an error. + + +## Detailed design + +Since Reflection symbols might be used by LLDB, there will be difference in emitted Reflection symbols across Debug and Release modes. +**Release mode**: if `-O`, `-Osize`, `-Ospeed` passed. +**Debug**: - if `-Onone` passed or if not set. + +One more level of reflection metadata will be introduced in addition to the existing ones: + + +1. Reflection Disabled (`disable-reflection-metadata`) + +- Do not emit reflection in Release and Debug modes. +- If there is a type in a module conforming to `Reflectable`, the compiler will emit an error. + +1. Enabled for the debugger support (`reflection-metadata-for-debugger-only`) + +- Emit Reflection metadata for all types in non-optimised mode while emitting nothing in optimised modes. +- If there is a type in a module conforming to `Reflectable`, the compiler will emit an error (even if in a non-opmised mode the metadata is actually emitted). + +1. Opt-in enabled (`-enable-opt-in-reflection-metadata`) + +- In optimised mode, emit only for types that conform to `Reflectable`. +- In non-optimised mode emit reflection in full. + +1. Fully enabled (current default level) + +- Emit reflection metadata for all types in optimised and non-optimised modes. + +Introducing a new flag to control the feature will allow us to safely roll it out and avoid breakages of the existing code. For those modules that get compiled with fully enabled metadata, nothing will change (all symbols will stay). For modules that have the metadata disabled, but are consumers of reflectable API, the compiler will emit the error enforcing the guarantee. + +For Swift 6, we propose to enable Opt-in behaviour by default, to make the user experience consistent and safe. + + +## Source compatibility + +The change won’t break source compatibility in versions prior to Swift 6, because of the gating by the new flag. If as proposed, it’s enabled by default in Swift 6, the code with types that has not been audited to conform to the `Reflectable` protocol will fail to compile if used with APIs that consume the reflection metadata. + + +## Effect on ABI stability + +`Reflectable` is a marker protocol, which doesn't have a runtime representation, has no requirements and doesn't affect ABI. + + + +## Effect on API resilience + +This proposal has no effect on API resilience. + + + +## Alternatives considered + +Dead Code Elimination and linker optimisations were also considered as a way to reduce the amount of present Reflection metadata in release builds. The optimiser could use a conformance to a `Reflectable` protocol as a hint about what reflection metadata should be preserved. However, turned out it was quite challenging to statically determine all usages of Reflection metadata even with hints. + +It was also considered to use an attribute `@reflectable` on nominal type declaration to express the requirement to have reflection metadata, however, a lot of logic had to be re-implemented outside of type-checker to ensure all guarantees are fulfilled. \ No newline at end of file From 54b3b441a6af7f3e84d5329dd7a11cf695f7063e Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Tue, 28 Jun 2022 17:03:01 +0100 Subject: [PATCH 2767/4563] added explicit description for Swift 6 + small fixes --- proposals/NNNN-opt-in-reflection-metadata.md | 27 ++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index dcb61ea6ed..cfbf987959 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -1,4 +1,5 @@ + # Swift Opt-In Reflection Metadata * Proposal: [SE-NNNN](NNNN-opt-in-reflection-metadata.md) @@ -81,31 +82,31 @@ Since Reflection symbols might be used by LLDB, there will be difference in emit **Release mode**: if `-O`, `-Osize`, `-Ospeed` passed. **Debug**: - if `-Onone` passed or if not set. -One more level of reflection metadata will be introduced in addition to the existing ones: - +One more level of reflection metadata will be introduced in addition to the existing ones: -1. Reflection Disabled (`disable-reflection-metadata`) +1. Reflection Disabled (`-disable-reflection-metadata`) - Do not emit reflection in Release and Debug modes. - If there is a type in a module conforming to `Reflectable`, the compiler will emit an error. -1. Enabled for the debugger support (`reflection-metadata-for-debugger-only`) +2. Enabled for the debugger support (`-reflection-metadata-for-debugger-only`) -- Emit Reflection metadata for all types in non-optimised mode while emitting nothing in optimised modes. -- If there is a type in a module conforming to `Reflectable`, the compiler will emit an error (even if in a non-opmised mode the metadata is actually emitted). +- Emit Reflection metadata for all types in Debug mode while emitting nothing in Release modes. +- If there is a type in a module conforming to `Reflectable`, the compiler will emit an error (even if in Debug mode the metadata is actually emitted). -1. Opt-in enabled (`-enable-opt-in-reflection-metadata`) +3. Opt-in enabled (`-enable-opt-in-reflection-metadata`) -- In optimised mode, emit only for types that conform to `Reflectable`. -- In non-optimised mode emit reflection in full. +- In Release mode, emit only for types that conform to `Reflectable`. +- In Debug mode emit reflection in full. -1. Fully enabled (current default level) +4. Fully enabled (current default level) -- Emit reflection metadata for all types in optimised and non-optimised modes. +- Emit reflection metadata for all types in Release and Debug modes. Introducing a new flag to control the feature will allow us to safely roll it out and avoid breakages of the existing code. For those modules that get compiled with fully enabled metadata, nothing will change (all symbols will stay). For modules that have the metadata disabled, but are consumers of reflectable API, the compiler will emit the error enforcing the guarantee. - -For Swift 6, we propose to enable Opt-in behaviour by default, to make the user experience consistent and safe. + +### Behaviour change for Swift 6 +For Swift 6, we propose to enable Opt-in behaviour by default, to make the user experience consistent and safe. To achieve that we will need to deprecate the compiler's options that can lead to missing reflection - `-reflection-metadata-for-debugger-only` and `-disable-reflection-metadata`. Starting with Swift 6, these arguments will be ignored in favour of the default opt-in mode. ## Source compatibility From 4596d1f04accef036134c3378f95ca546a423524 Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Mon, 11 Jul 2022 17:39:32 +0100 Subject: [PATCH 2768/4563] add section about conditional cast to Reflectable --- proposals/NNNN-opt-in-reflection-metadata.md | 26 ++++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index cfbf987959..ed43cbae54 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -1,5 +1,3 @@ - - # Swift Opt-In Reflection Metadata * Proposal: [SE-NNNN](NNNN-opt-in-reflection-metadata.md) @@ -73,8 +71,23 @@ User module: struct Bar: Reflectable {} foo(Bar()) ``` -Reflection metadata for `Bar` will be emitted because it explicitly conforms to Reflectable protocol. Without conformance to Reflectable, an instance of type Bar can't be used on function `foo`. If the user module gets compiled with the reflection metadata disabled, the compiler will emit an error. - +Reflection metadata for `Bar` will be emitted because it explicitly conforms to Reflectable protocol. Without conformance to Reflectable, an instance of type Bar can't be used on function `foo`. If the user module gets compiled with the reflection metadata disabled, the compiler will emit an error. + +### Conditional cast (`as? Reflectable`) +We also propose to allow a conditional cast to the `Reflectable` marker protocol, which would succeed only if Reflection Metadata related to a type is available at runtime. This would allow developers to explicitly check if reflection metadata is available and based on that fact branch the code accordingly. + +```swift +public func consume(_ t: Any) { + if let _t = t as? Reflectable { + // Use Mirror API to extract Reflection Metadata + } else { + // Back to default implementation + } +} +``` + +### Behaviour change for Swift 6 +For Swift 6, we propose to enable Opt-in behaviour by default, to make the user experience consistent and safe. To achieve that we will need to deprecate the compiler's options that can lead to missing reflection - `-reflection-metadata-for-debugger-only` and `-disable-reflection-metadata`. Starting with Swift 6, these arguments will be ignored in favour of the default opt-in mode. ## Detailed design @@ -103,10 +116,7 @@ One more level of reflection metadata will be introduced in addition to the exis - Emit reflection metadata for all types in Release and Debug modes. -Introducing a new flag to control the feature will allow us to safely roll it out and avoid breakages of the existing code. For those modules that get compiled with fully enabled metadata, nothing will change (all symbols will stay). For modules that have the metadata disabled, but are consumers of reflectable API, the compiler will emit the error enforcing the guarantee. - -### Behaviour change for Swift 6 -For Swift 6, we propose to enable Opt-in behaviour by default, to make the user experience consistent and safe. To achieve that we will need to deprecate the compiler's options that can lead to missing reflection - `-reflection-metadata-for-debugger-only` and `-disable-reflection-metadata`. Starting with Swift 6, these arguments will be ignored in favour of the default opt-in mode. +Introducing a new flag to control the feature will allow us to safely roll it out and avoid breakages of the existing code. For those modules that get compiled with fully enabled metadata, nothing will change (all symbols will stay). For modules that have the metadata disabled, but are consumers of reflectable API, the compiler will emit the error enforcing the guarantee. ## Source compatibility From 05fb17e8ab32cdb60f5e38c27d7726f7e9acbfb0 Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Mon, 17 Oct 2022 14:15:21 +0300 Subject: [PATCH 2769/4563] introduce reflectable casts + some improvements and fixes --- proposals/NNNN-opt-in-reflection-metadata.md | 124 +++++++++++-------- 1 file changed, 73 insertions(+), 51 deletions(-) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index ed43cbae54..8479c98d03 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -1,3 +1,4 @@ + # Swift Opt-In Reflection Metadata * Proposal: [SE-NNNN](NNNN-opt-in-reflection-metadata.md) @@ -8,34 +9,32 @@ ## Introduction -This proposal seeks to increase the safety and efficiency of Swift Reflection Metadata by improving the existing mechanism and providing the opportunity to express a requirement on Reflection Metadata in APIs that consume it. +This proposal seeks to increase the safety, efficiency and privacy of Swift Reflection Metadata by improving the existing mechanism and providing the opportunity to express a requirement on Reflection Metadata in APIs that consume it. -Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/pitch-2-opt-in-reflection-metadata/41696) +Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/pitch-3-opt-in-reflection-metadata/58852) ## Motivation -APIs can use Reflection Metadata differently. Some like `print`, and `dump` will still work with disabled reflection, but the output will be limited. Others, like SwiftUI rely on it and won't work correctly if the reflection metadata is missing. -While the former can potentially benefit as well, the main focus of this proposal is on the latter. +APIs can use Reflection Metadata differently. Some like `print`, and `dump` will still work with disabled reflection, but the output will be limited. Others, like SwiftUI, rely on it and won't work correctly if the reflection metadata is missing. +While the former can benefit as well, the main focus of this proposal is on the latter. + +A developer can mistakenly turn off Reflection Metadata for a Swift module and won't be warned at compile-time if APIs that consume reflection are used by that module. An app with such a module won't behave as expected at runtime which may be challenging to notice and track down such bugs back to Reflection. For instance, SwiftUI implementation uses reflection metadata from user modules to trigger the re-rendering of the view hierarchy when a state has changed. If for some reason a user module was compiled with metadata generation disabled, changing the state won't trigger that behavior and will cause inconsistency between state and representation which will make such API less safe since it becomes a runtime issue rather than a compile-time one. + +On the other hand, excessive Reflection metadata may be preserved in a binary even if not used, because there is currently no way to statically determine its usage. There was an attempt to limit the amount of unused reflection metadata by improving its stripability by the Dead Code Elimination LLVM pass, but in many cases, it’s still preserved in the binary because it’s referenced by Full Type Metadata which prevents Reflection Metadata from stripping. This unnecessarily increases the binary size and may affect privacy by storing more information about the code's semantics in a binary, which might be used for reverse-engineering. + +Introducing a static compilation check potentially can help to solve both of mentioned issues by adding to the language a way to express the requirement to have Reflection metadata at runtime. -A developer can mistakenly turn off Reflection Metadata for a Swift module and won't be warned at compile-time if APIs that consume reflection are used by that module. An app with such a module won't behave as expected at runtime which may be challenging to notice and track down such bugs back to Reflection. For instance, SwiftUI implementation uses reflection metadata from user modules to trigger re-rendering of the view hierarchy when a state has changed. If for some reason a user module was compiled with metadata generation disabled, changing the state won't trigger that behaviour and will cause inconsistency between state and representation which will make such API less safe since it becomes a runtime issue rather than a compile-time one. - -On the other hand, excessive Reflection metadata may be preserved in a binary even if not used, because there is currently no way to statically determine its usage. There was an attempt to limit the amount of unused reflection metadata by improving its stripability by the Dead Code Elimination LLVM pass, but in many cases, it’s still preserved in the binary because it’s referenced by Full Type Metadata which prevents Reflection Metadata from stripping. - -Introducing a static compilation check potentially can help to solve both of mentioned issues by adding to the language a way to express the requirement to have Reflection metadata at runtime. - ## Proposed solution -Teaching the Type-checker to ensure Reflection metadata is preserved in a binary if reflection-consuming APIs are used, will help to move the issue from runtime to compile time. - -To achieve that, a new marker protocol `Reflectable` will be introduced. Firstly, APIs developers will gain an opportunity to express a dependency on Reflection Metadata through a generic requirement of their functions, which will make such APIs safer. Secondly, during IRGen, the compiler will be able to selectively emit Reflection symbols for the types that explicitly conform to the `Reflectable` protocol, which will reduce the overhead from reflection symbols for cases when reflection is emitted but not consumed. - - +Teaching the Type-checker and IRGen to ensure Reflection metadata is preserved in a binary if reflection-consuming APIs are used, will help to move the issue from runtime to compile time. + +To achieve that, a new marker protocol `Reflectable` will be introduced. Firstly, APIs developers will gain an opportunity to express a dependency on Reflection Metadata through a generic requirement of their functions, which will make such APIs safer. Secondly, during IRGen, the compiler will be able to selectively emit Reflection symbols for the types that explicitly conform to the `Reflectable` protocol, which will reduce the overhead from reflection symbols for cases when reflection is emitted but not consumed. ### Case Study 1: -SwiftUI Framework: +SwiftUI Framework: ```swift protocol SwiftUI.View: Reflectable {} class NSHostingView where Content : View { @@ -57,12 +56,12 @@ struct SomeView: SwiftUI.View { window.contentView = NSHostingView(rootView: SomeView()) ``` -Reflection metadata for `SomeView` will be emitted because it implicitly conforms to `Reflectable` protocol, while for `SomeModel` Reflection metadata won't be emitted. If the user module gets compiled with the reflection metadata disabled, the compiler will emit an error. +Reflection metadata for `SomeView` will be emitted because it implicitly conforms to `Reflectable` protocol, while for `SomeModel` Reflection metadata won't be emitted. If the user module gets compiled with the reflection metadata disabled, the compiler will emit an error. ### Case Study 2: -Framework: +Framework: ```swift public func foo(_ t: T) { ... } ``` @@ -73,51 +72,78 @@ foo(Bar()) ``` Reflection metadata for `Bar` will be emitted because it explicitly conforms to Reflectable protocol. Without conformance to Reflectable, an instance of type Bar can't be used on function `foo`. If the user module gets compiled with the reflection metadata disabled, the compiler will emit an error. -### Conditional cast (`as? Reflectable`) -We also propose to allow a conditional cast to the `Reflectable` marker protocol, which would succeed only if Reflection Metadata related to a type is available at runtime. This would allow developers to explicitly check if reflection metadata is available and based on that fact branch the code accordingly. + +### Conditional and Force casts (`as? Reflectable`, `as! Reflectable`, `is Reflectable`) + +We also propose to allow conditional and force casts to the `Reflectable` protocol, which would succeed only if Reflection Metadata related to a type is available at runtime. This would allow developers to explicitly check if reflection metadata is present and based on that fact branch the code accordingly. ```swift -public func consume(_ t: Any) { - if let _t = t as? Reflectable { - // Use Mirror API to extract Reflection Metadata - } else { - // Back to default implementation - } +public func conditionalUse(_ t: T) { + if let _t = t as? Reflectable { // Consume Reflection metadata + } else { // Back to default implementation } +} + +public func forceUse(_ t: T) { + debugPrint(t as! Reflectable) // Will crash if reflection metadata isn't available +} + +public func testIsReflectable(_ t: T) -> Bool { + return t is Reflectable // returns True if reflection is available } ``` ### Behaviour change for Swift 6 -For Swift 6, we propose to enable Opt-in behaviour by default, to make the user experience consistent and safe. To achieve that we will need to deprecate the compiler's options that can lead to missing reflection - `-reflection-metadata-for-debugger-only` and `-disable-reflection-metadata`. Starting with Swift 6, these arguments will be ignored in favour of the default opt-in mode. +For Swift 6, we propose to enable Opt-In behaviour by default, to make the user experience consistent and safe. To achieve that we will need to deprecate the compiler's options that can lead to missing reflection - `-reflection-metadata-for-debugger-only` and `-disable-reflection-metadata`. Starting with Swift 6, these arguments will be ignored in favour of the default opt-in mode. ## Detailed design -Since Reflection symbols might be used by LLDB, there will be difference in emitted Reflection symbols across Debug and Release modes. -**Release mode**: if `-O`, `-Osize`, `-Ospeed` passed. -**Debug**: - if `-Onone` passed or if not set. - -One more level of reflection metadata will be introduced in addition to the existing ones: +Since Reflection symbols might be used by the debugger, there will be difference in emitted Reflection symbols across Debug and Release modes. +**Release mode**: if `-O`, `-Osize`, `-Ospeed` passed. +**Debug**: - if `-Onone` passed or if not set. -1. Reflection Disabled (`-disable-reflection-metadata`) +**Changes in flags** +To handle behavior change between Swift pre-6 and 6, we can introduce a new upcoming feature, which will allow to enable Opt-In mode explicitly for pre-6 Swift with `-enable-upcoming-feature OptInReflection` and will set this mode by default in Swift 6. -- Do not emit reflection in Release and Debug modes. -- If there is a type in a module conforming to `Reflectable`, the compiler will emit an error. +A new flag `-enable-full-reflection-metadata` will also have to be introduced to allow developers to enable reflection in full if they desire in Swift 6 and later. -2. Enabled for the debugger support (`-reflection-metadata-for-debugger-only`) +For Swift 6, flags `-disable-reflection-metadata` and `-emit-reflection-for-debugger` will be a no-op, to ensure the reflection metadata is always available when needed. -- Emit Reflection metadata for all types in Debug mode while emitting nothing in Release modes. -- If there is a type in a module conforming to `Reflectable`, the compiler will emit an error (even if in Debug mode the metadata is actually emitted). +1. Reflection Disabled (`-disable-reflection-metadata` and `-reflection-metadata-for-debugger-only`) +- Do not emit reflection in Release and Debug modes for Swift pre-6. +- A no-op in Swift 6 and later. +- If there is a type in a module conforming to `Reflectable`, the compiler will emit an error. -3. Opt-in enabled (`-enable-opt-in-reflection-metadata`) +2. Opt-In Reflection (`-enable-upcoming-feature OptInReflection`) +- In Release mode, emit only for types that conform to `Reflectable`. +- In Debug mode emit reflection in full. +- For Swift pre-6 will require an explicit flag, for Swift 6 will be enabled by default. -- In Release mode, emit only for types that conform to `Reflectable`. -- In Debug mode emit reflection in full. +3. Fully enabled (`-enable-full-reflection-metadata`) +- Emit reflection metadata for all types in Release and Debug modes. +- Conformance to Reflectable will be synthesized for all types to allow usage of reflection-consuming APIs. +- Current default level for Swift pre-6. -4. Fully enabled (current default level) +Introducing a new flag to control the feature will allow us to safely roll it out and avoid breakages of the existing code. For those modules that get compiled with fully enabled metadata, nothing will change (all symbols will stay present). For modules that have the metadata disabled, but are consumers of reflectable API, the compiler will emit the error enforcing the guarantee. -- Emit reflection metadata for all types in Release and Debug modes. +### Casts implementation +Casting might be a good way to improve the feature's ergonomics because currently there is no way to check if reflection is available at runtime. (`Mirror.children.count` doesn't really help because it doesn't distinguish between the absence of reflection metadata and the absence of fields on a type) + +To implement this feature, we propose to introduce a new runtime function `swift_reflectableCast`, and emit a call to it instead of `swift_dynamicCast`during IRGen if Reflectable is a target type. + +Because of the fact that the compiler emits a call to that function at compile-time, all casts must be statically visible. All other cases like implicit conversion to `Reflectable` must be banned. This could be done at CSSimplify, when a new conversion constraint is introduced between a type variable and `Reflectable` type, the compiler will emit an error. + +```swift +func cast(_ x: U) -> T { + return x as! T +} +let a = cast(1) as Reflectable // expression can't be implicitly converted to Reflectable; use 'as? Reflectable' or 'as! Reflectable' instead +let b: Reflectable = cast(1) // expression can't be implicitly converted to Reflectable; use 'as? Reflectable' or 'as! Reflectable' instead +``` +Some diagnostics and optimizations will also have to be disabled even if conformance is statically visible to the compiler because all casts will have to go through the runtime call. + +**Availability checks** +Since reflectable casting will require a new runtime function, it should be gated by availability checks. If a deployment target is lower than supported, an error will be emitted. -Introducing a new flag to control the feature will allow us to safely roll it out and avoid breakages of the existing code. For those modules that get compiled with fully enabled metadata, nothing will change (all symbols will stay). For modules that have the metadata disabled, but are consumers of reflectable API, the compiler will emit the error enforcing the guarantee. - ## Source compatibility @@ -126,18 +152,14 @@ The change won’t break source compatibility in versions prior to Swift 6, beca ## Effect on ABI stability -`Reflectable` is a marker protocol, which doesn't have a runtime representation, has no requirements and doesn't affect ABI. - - +`Reflectable` is a marker protocol, which doesn't have a runtime representation, has no requirements and doesn't affect ABI. ## Effect on API resilience This proposal has no effect on API resilience. - - ## Alternatives considered Dead Code Elimination and linker optimisations were also considered as a way to reduce the amount of present Reflection metadata in release builds. The optimiser could use a conformance to a `Reflectable` protocol as a hint about what reflection metadata should be preserved. However, turned out it was quite challenging to statically determine all usages of Reflection metadata even with hints. - + It was also considered to use an attribute `@reflectable` on nominal type declaration to express the requirement to have reflection metadata, however, a lot of logic had to be re-implemented outside of type-checker to ensure all guarantees are fulfilled. \ No newline at end of file From a266813a4ebe563556f3af422e396f817809f6ab Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 17 Oct 2022 18:26:23 +0100 Subject: [PATCH 2770/4563] Update process.md (#1815) * Change "Core Team" to "Language Workgroup". * Remove "YYYY-MM-DD" from "Accepted" states. * Remove "Deferred" state. * Add flowchart. --- process.md | 61 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/process.md b/process.md index e4a0815181..782d9fb27c 100644 --- a/process.md +++ b/process.md @@ -4,7 +4,7 @@ Swift is a powerful and intuitive programming language that is designed to make ## Scope -The Swift evolution process covers all changes to the Swift language and the public interface of the Swift standard library, including new language features and APIs (no matter how small), changes to existing language features or APIs, removal of existing features, and so on. Smaller changes, such as bug fixes, optimizations, or diagnostic improvements can be contributed via the normal contribution process; see [Contributing to Swift](https://swift.org/community/#contributing). +The Swift evolution process covers all changes to the Swift language and the public interface of the Swift standard library, including new language features and APIs (no matter how small), changes to existing language features or APIs, removal of existing features, and so on. Smaller changes, such as bug fixes, optimizations, or diagnostic improvements can be contributed via the normal contribution process; see [Contributing to Swift](https://www.swift.org/contributing/). ## Goals @@ -15,6 +15,11 @@ The Swift evolution process aims to leverage the collective ideas, insights, and There is a natural tension between these two goals. Open evolution processes are, by nature, chaotic. Yet, maintaining a coherent vision for something as complicated as a programming language requires some level of coordination. The Swift evolution process aims to strike a balance that best serves the Swift community as a whole. +## Community structure + +* [Core Team](https://www.swift.org/community/#core-team) members are responsible for the strategic direction of Swift. +* [Language Workgroup](https://www.swift.org/community/#language-workgroup) members initiate, participate in, and manage the public review of proposals and have the authority to accept or reject changes to Swift. + ## Participation Everyone is welcome to propose, discuss, and review ideas to improve @@ -22,11 +27,6 @@ the Swift language and standard library in the [Evolution section of the Swift forums](https://forums.swift.org/c/evolution). Before posting a review, please see the section "What goes into a review?" below. -The Swift [core team](https://swift.org/community/#core-team) is -responsible for the strategic direction of Swift. Core team members -initiate, participate in, and manage the public review of proposals -and have the authority to accept or reject changes to Swift. - ## What goes into a review? The goal of the review process is to improve the proposal under review @@ -53,20 +53,20 @@ of the upcoming Swift release. Proposals that are clearly out of scope for the upcoming Swift release will not be brought up for review. If you can't resist discussing a proposal that you know is out of scope, please include the tag `[Out of scope]` in the subject. * **Socialize the idea**: propose a rough sketch of the idea in the ["pitches" section of the Swift forums](https://forums.swift.org/c/evolution/pitches), the problems it solves, what the solution looks like, etc., to gauge interest from the community. * **Develop the proposal**: expand the rough sketch into a complete proposal, using the [proposal template](proposal-templates/0000-swift-template.md), and continue to refine the proposal on the forums. Prototyping an implementation and its uses along with the proposal is *required* because it helps ensure both technical feasibility of the proposal as well as validating that the proposal solves the problems it is meant to solve. -* **Request a review**: initiate a pull request to the [swift-evolution repository][swift-evolution-repo] to indicate to the core team that you would like the proposal to be reviewed. When the proposal is sufficiently detailed and clear, and addresses feedback from earlier discussions of the idea, the pull request will be accepted. The proposal will be assigned a proposal number as well as a core team member to manage the review. +* **Request a review**: initiate a pull request to the [swift-evolution repository][swift-evolution-repo] to indicate to the Language Workgroup that you would like the proposal to be reviewed. When the proposal is sufficiently detailed and clear, and addresses feedback from earlier discussions of the idea, the pull request will be accepted. The proposal will be assigned a proposal number as well as a Language Workgroup member to manage the review. * **Address feedback**: in general, and especially [during the review period][proposal-status], be responsive to questions and feedback about the proposal. ## Preparing an implementation When you are ready to request a review, a pull request with an implementation is required in addition to your proposal. Proposals that can ship as part of the [Standard Library Preview package][preview-package] should be paired with a pull request against the [swift-evolution-staging repository][swift-evolution-staging]. All other proposals should be paired with an implementation pull request against the [main Swift repository](https://github.com/apple/swift). -The preview package can accept new types, new protocols, and extensions to existing types and protocols that can be implemented without access to standard library internals or other non-public features. For more information about the kinds of changes that can be implemented in the preview package, see [SE-0264](https://github.com/apple/swift-evolution/blob/master/proposals/0264-stdlib-preview-package.md). +The preview package can accept new types, new protocols, and extensions to existing types and protocols that can be implemented without access to standard library internals or other non-public features. For more information about the kinds of changes that can be implemented in the preview package, see [SE-0264](https://github.com/apple/swift-evolution/blob/main/proposals/0264-stdlib-preview-package.md). ## Review process The review process for a particular proposal begins when a member of -the core team accepts a pull request of a new or updated proposal into -the [swift-evolution repository][swift-evolution-repo]. That core team +the Language Workgroup accepts a pull request of a new or updated proposal into +the [swift-evolution repository][swift-evolution-repo]. That Language Workgroup member becomes the *review manager* for the proposal. The proposal is assigned a proposal number (if it is a new proposal), and then enters the review queue. If your proposal's accompanying implementation takes the form of a package, the review manager will merge your pull request into a new branch in the [swift-evolution-staging repository][swift-evolution-staging]. @@ -82,38 +82,55 @@ reviews. To avoid delays, it is important that the proposal authors be available to answer questions, address feedback, and clarify their intent during the review period. -After the review has completed, the core team will make a decision on +After the review has completed, the Language Workgroup will make a decision on the proposal. The review manager is responsible for determining -consensus among the core team members, then reporting their decision +consensus among the Language Workgroup members, then reporting their decision to the proposal authors and forums. The review manager will update the proposal's state in the [swift-evolution repository][swift-evolution-repo] to reflect that decision. ## Proposal states + +```mermaid +flowchart LR + %% + + %% Nodes: + 1{{"Awaiting\nreview"}} + 2{{"Scheduled\nfor review"}} + 3{"Active\nreview"} + 4["Returned\nfor revision"] + 5(["Withdrawn"]) + 6(["Rejected"]) + 7_8["Accepted\n(with revisions)"] + 9[["Previewing"]] + 10(["Implemented"]) + + %% Links: + 1 ==> 3 ==> 7_8 ==> 10 + 1 -.-> 2 -.-> 3 -.-> 4 -.-> 5 & 1 + 3 -.-> 6 + 7_8 -.-> 9 -.-> 10 +``` + A given proposal can be in one of several states: * **Awaiting review**: The proposal is awaiting review. Once known, the dates for the actual review will be placed in the proposal document. When the review period begins, the review manager will update the state to *Active review*. -* **Scheduled for review (FULL_MONTH_NAME DAY...FULL_MONTH_NAME DAY)**: The public review of the proposal +* **Scheduled for review (...)**: The public review of the proposal in the [Swift forums][proposal-reviews] has been scheduled for the specified date range. -* **Active review (FULL_MONTH_NAME DAY...FULL_MONTH_NAME DAY)**: The proposal is undergoing public review +* **Active review (...)**: The proposal is undergoing public review in the [Swift forums][proposal-reviews]. The review will continue through the specified date range. * **Returned for revision**: The proposal has been returned from review for additional revision to the current draft. * **Withdrawn**: The proposal has been withdrawn by the original submitter. -* **Deferred**: Consideration of the proposal has been deferred - because it does not meet the [goals of the upcoming major Swift - release](README.md). Deferred proposals will be reconsidered when - scoping the next major Swift release. * **Rejected**: The proposal has been considered and rejected. -* **Accepted (YYYY-MM-DD)**: - The proposal has been accepted (on the specified date) and is either awaiting +* **Accepted**: The proposal has been accepted and is either awaiting implementation or is actively being implemented. -* **Accepted with revisions (YYYY-MM-DD)**: - The proposal has been accepted (on the specified date), +* **Accepted with revisions**: The proposal has been accepted, contingent upon the inclusion of one or more revisions. * **Previewing**: The proposal has been accepted and is available for preview in the [Standard Library Preview package][preview-package]. From b77fb3a63fa81792c0e7c3e0f9c88241bedc4fcd Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Sun, 23 Oct 2022 13:56:39 -0400 Subject: [PATCH 2771/4563] Assign SE-0376, review manager, update header --- ...loy-attribute.md => 0376-function-back-deployment.md} | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) rename proposals/{NNNN-back-deploy-attribute.md => 0376-function-back-deployment.md} (97%) diff --git a/proposals/NNNN-back-deploy-attribute.md b/proposals/0376-function-back-deployment.md similarity index 97% rename from proposals/NNNN-back-deploy-attribute.md rename to proposals/0376-function-back-deployment.md index bd94c1895b..1aeb4bb3b0 100644 --- a/proposals/NNNN-back-deploy-attribute.md +++ b/proposals/0376-function-back-deployment.md @@ -1,10 +1,11 @@ # Function Back Deployment -* Proposal: [SE-NNNN](NNNN-back-deploy.md) +* Proposal: [SE-0376](0376-function-back-deployment.md) * Author: [Allan Shortlidge](https://github.com/tshortli) * Implementation: [apple/swift#41271](https://github.com/apple/swift/pull/41271), [apple/swift#41348](https://github.com/apple/swift/pull/41348), [apple/swift#41416](https://github.com/apple/swift/pull/41416), [apple/swift#41612](https://github.com/apple/swift/pull/41612) as the underscored attribute `@_backDeploy` -* Review Manager: TBD -* Status: **Awaiting implementation** +* Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) +* Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) +* Status: **Review scheduled (Oct 25-Nov 7, 2022)** ## Introduction @@ -59,8 +60,6 @@ While `@_alwaysEmitIntoClient` can be used to back deploy APIs, there are some d 1. The API implementation from the original library is preferred at runtime when it is available. 2. Fallback copies of the API implementation are absent from clients binaries when they would never be used. -Swift-evolution thread: [Pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769) - ## Proposed solution Add a `@backDeploy(before: ...)` attribute to Swift that can be used to indicate that a copy of the function should be emitted into the client to be used at runtime when executing on an OS prior to a specific version. The attribute can be adopted by ToastKit's authors like this: From 4dba2690210e438013ea15696793b9b7832641d0 Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Mon, 24 Oct 2022 16:53:40 +0100 Subject: [PATCH 2772/4563] distinguish Core metadata from Reflection one --- proposals/NNNN-opt-in-reflection-metadata.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index 8479c98d03..923856f3ca 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -12,10 +12,12 @@ This proposal seeks to increase the safety, efficiency and privacy of Swift Reflection Metadata by improving the existing mechanism and providing the opportunity to express a requirement on Reflection Metadata in APIs that consume it. Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/pitch-3-opt-in-reflection-metadata/58852) - + ## Motivation +There are two kinds of Swift metadata - Core Metadata (type metadata record, nominal type descriptor, etc) and Reflection metadata. The former must constantly be emitted and may only be stripped if provenly not used. Core Metadata is not affected by this proposal. The latter contains optional information about the type's fields and their names, not used by the language's runtime features, and the emission may be skipped if not used by reflection-consuming APIs. + APIs can use Reflection Metadata differently. Some like `print`, and `dump` will still work with disabled reflection, but the output will be limited. Others, like SwiftUI, rely on it and won't work correctly if the reflection metadata is missing. While the former can benefit as well, the main focus of this proposal is on the latter. @@ -23,7 +25,7 @@ A developer can mistakenly turn off Reflection Metadata for a Swift module and w On the other hand, excessive Reflection metadata may be preserved in a binary even if not used, because there is currently no way to statically determine its usage. There was an attempt to limit the amount of unused reflection metadata by improving its stripability by the Dead Code Elimination LLVM pass, but in many cases, it’s still preserved in the binary because it’s referenced by Full Type Metadata which prevents Reflection Metadata from stripping. This unnecessarily increases the binary size and may affect privacy by storing more information about the code's semantics in a binary, which might be used for reverse-engineering. -Introducing a static compilation check potentially can help to solve both of mentioned issues by adding to the language a way to express the requirement to have Reflection metadata at runtime. +Introducing a static compilation check can help to solve both of mentioned issues by adding to the language a way to express the requirement to have Reflection metadata at runtime. ## Proposed solution From add8833f23c9ee3aefa85a49e5e9476b0e6dceb5 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Tue, 25 Oct 2022 09:06:09 -0400 Subject: [PATCH 2773/4563] Add review link to SE-0376 --- proposals/0376-function-back-deployment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index 1aeb4bb3b0..70a04ebfd0 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -4,8 +4,8 @@ * Author: [Allan Shortlidge](https://github.com/tshortli) * Implementation: [apple/swift#41271](https://github.com/apple/swift/pull/41271), [apple/swift#41348](https://github.com/apple/swift/pull/41348), [apple/swift#41416](https://github.com/apple/swift/pull/41416), [apple/swift#41612](https://github.com/apple/swift/pull/41612) as the underscored attribute `@_backDeploy` * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) -* Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) -* Status: **Review scheduled (Oct 25-Nov 7, 2022)** +* Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) +* Status: **Active review (Oct 25...Nov 7, 2022)** ## Introduction From 925341d36b9f26e0cf35d5f8c7a2a0037459d399 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 25 Oct 2022 10:35:42 -0700 Subject: [PATCH 2774/4563] `take` and `borrow` parameter ownership modifiers (#1739) * Michael's working draft from https://github.com/gottesmm/swift-evolution/blob/consuming-nonconsuming-pitch-v1/proposals/000b-consuming-nonconsuming.md * New revision based on feedback from SE-0366 * Revisions in response to pitch discussion: - Use `taking` and `borrowing func` modifiers to control `self`'s convention - Add "alternative considered" for whether `take`n parameters should be bound as mutable in the callee * More additions from pitch discussion - Mention that nonescaping closure arguments cannot be `take`-n - Mention "out" parameter conventions as a future direction - Include discussion of destructuring move-only values that normally would have destructors, without invoking the normal deinit * Remove "future direction" of destructuring methods This fits better thematically as a future direction discussion with the `take` operator proposal instead. * Based on initial WG guidance, start off with suggesting that takes are locally mutable * Clarify how witness matching works with move-only parameters: the ownership must match exactly * Update and rename NNNN-parameter-ownership-modifiers.md to 0377-parameter-ownership-modifiers.md Co-authored-by: Ben Cohen --- .../0377-parameter-ownership-modifiers.md | 506 ++++++++++++++++++ 1 file changed, 506 insertions(+) create mode 100644 proposals/0377-parameter-ownership-modifiers.md diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md new file mode 100644 index 0000000000..fe86365fea --- /dev/null +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -0,0 +1,506 @@ +# `borrow` and `take` parameter ownership modifiers + +* Proposal: [SE-0377](0377-parameter-ownership-modifiers.md) +* Authors: [Michael Gottesman](https://github.com/gottesmm), [Joe Groff](https://github.com/jckarter) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Active Review (Oct 25 - Nov 8, 2022)** +* Implementation: available using the internal names `__shared` and `__owned` +* Pitch v1: [https://github.com/gottesmm/swift-evolution/blob/consuming-nonconsuming-pitch-v1/proposals/000b-consuming-nonconsuming.md](https://github.com/gottesmm/swift-evolution/blob/consuming-nonconsuming-pitch-v1/proposals/000b-consuming-nonconsuming.md) + + + +## Introduction + +We propose new `borrow` and `take` parameter modifiers to allow developers to +explicitly choose the ownership convention that a function uses to receive +immutable parameters. This allows for fine-tuning of performance by reducing +the number of ARC calls or copies needed to call a function, and provides a +necessary prerequisite feature for move-only types to specify whether a function +consumes a move-only value or not. + +Pitch threads: + +- First pitch thread: [https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers) +- Second pitch thread: [https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581) + +## Motivation + +Swift uses automatic reference counting to manage the lifetimes of reference- +counted objects. There are two broad conventions that the compiler uses to +maintain memory safety when passing an object by value from a caller to a +callee in a function call: + +* The callee can **borrow** the parameter. The caller + guarantees that its argument object will stay alive for the duration of the + call, and the callee does not need to release it (except to balance any + additional retains it performs itself). +* The callee can **take** the parameter. The callee + becomes responsible for either releasing the parameter or passing ownership + of it along somewhere else. If a caller doesn't want to give up its own + ownership of its argument, it must retain the argument so that the callee + can take the extra reference count. + +These two conventions generalize to value types, where a "retain" +becomes an independent copy of the value, and "release" the destruction and +deallocation of the copy. By default Swift chooses which convention to use +based on some rules informed by the typical behavior of Swift code: +initializers and property setters are more likely to use their parameters to +construct or update another value, so it is likely more efficient for them to +*take* their parameters and forward ownership to the new value they construct. +Other functions default to *borrowing* their parameters, since we have found +this to be more efficient in most situations. + +These choices typically work well, but aren't always optimal. +Although the optimizer supports "function signature optimization" that can +change the convention used by a function when it sees an opportunity to reduce +overall ARC traffic, the circumstances in which we can automate this are +limited. The ownership convention becomes part of the ABI for public API, so +cannot be changed once established for ABI-stable libraries. The optimizer +also does not try to optimize polymorphic interfaces, such as non-final class +methods or protocol requirements. If a programmer wants behavior different +from the default in these circumstances, there is currently no way to do so. + +Looking to the future, as part of our [ongoing project to add ownership to Swift](https://forums.swift.org/t/manifesto-ownership/5212), we will eventually have +move-only values and types. Since move-only types do not have the ability to +be copied, the distinction between the two conventions becomes an important +part of the API contract: functions that *borrow* move-only values make +temporary use of the value and leave it valid for further use, like reading +from a file handle, whereas functions that *take* a move-only value consume +it and prevent its further use, like closing a file handle. Relying on +implicit selection of the parameter convention will not suffice for these +types. + +## Proposed solution + +We give developers direct control over the ownership convention of +parameters by introducing two new parameter modifiers `borrow` and `take`. + +## Detailed design + +`borrow` and `take` become contextual keywords inside parameter type +declarations. They can appear in the same places as the `inout` modifier, and +are mutually exclusive with each other and with `inout`. In a `func`, +`subscript`, or `init` declaration, they appear as follows: + +```swift +func foo(_: borrow Foo) +func foo(_: take Foo) +func foo(_: inout Foo) +``` + +In a closure: + +```swift +bar { (a: borrow Foo) in a.foo() } +bar { (a: take Foo) in a.foo() } +bar { (a: inout Foo) in a.foo() } +``` + +In a function type: + +```swift +let f: (borrow Foo) -> Void = { a in a.foo() } +let f: (take Foo) -> Void = { a in a.foo() } +let f: (inout Foo) -> Void = { a in a.foo() } +``` + +Methods can using the `taking` or `borrowing` modifier to indicate that they +take ownership of their `self` parameter, or they borrow it. These modifiers +are mutually exclusive with each other and with the existing `mutating` modifier: + +```swift +struct Foo { + taking func foo() // `take` ownership of self + borrowing func foo() // `borrow` self + mutating func foo() // modify self with `inout` semantics +} +``` + +`take` cannot be applied to parameters of nonescaping closure type, which by +their nature are always borrowed: + +```swift +// ERROR: cannot `take` a nonescaping closure +func foo(f: take () -> ()) { +} +``` + +`take` or `borrow` on a parameter do not affect the caller-side syntax for +passing an argument to the affected declaration, nor do `taking` or +`borrowing` affect the application of `self` in a method call. For typical +Swift code, adding, removing, or changing these modifiers does not have any +source-breaking effects. (See "related directions" below for interactions with +other language features being considered currently or in the near future which +might interact with these modifiers in ways that cause them to break source.) + +Protocol requirements can also use `take` and `borrow`, and the modifiers will +affect the convention used by the generic interface to call the requirement. +The requirement may still be satisfied by an implementation that uses different +conventions for parameters of copyable types: + +```swift +protocol P { + func foo(x: take Foo, y: borrow Foo) +} + +// These are valid conformances: + +struct A: P { + func foo(x: Foo, y: Foo) +} + +struct B: P { + func foo(x: borrow Foo, y: take Foo) +} + +struct C: P { + func foo(x: take Foo, y: borrow Foo) +} +``` + +Function values can also be implicitly converted to function types that change +the convention of parameters of copyable types among unspecified, `borrow`, +or `take`: + +```swift +let f = { (a: Foo) in print(a) } + +let g: (borrow Foo) -> Void = f +let h: (take Foo) -> Void = f + +let f2: (Foo) -> Void = h +``` + +Inside of a function or closure body, `take` parameters may be mutated, as can +the `self` parameter of a `taking func` method. These +mutations are performed on the value that the function itself took ownership of, +and will not be evident in any copies of the value that might still exist in +the caller. This makes it easy to take advantage of the uniqueness of values +after ownership transfer to do efficient local mutations of the value: + +``` +extension String { + // Append `self` to another String, using in-place modification if + // possible + taking func plus(_ other: String) -> String { + // Modify our owned copy of `self` in-place, taking advantage of + // uniqueness if possible + self += other + return self + } +} + +// This is amortized O(n) instead of O(n^2)! +let helloWorld = "hello ".plus("cruel ").plus("world") +``` + +## Source compatibility + +Adding `take` or `borrow` to a parameter in the language today does not +otherwise affect source compatibility. Callers can continue to call the +function as normal, and the function body can use the parameter as it already +does. A method with `take` or `borrow` modifiers on its parameters can still +be used to satisfy a protocol requirement with different modifiers. The +compiler will introduce implicit copies as needed to maintain the expected +conventions. This allows for API authors to use `take` and `borrow` annotations +to fine-tune the copying behavior of their implementations, without forcing +clients to be aware of ownership to use the annotated APIs. Source-only +packages can add, remove, or adjust these annotations on copyable types +over time without breaking their clients. + +This will change if we introduce features that limit the compiler's ability +to implicitly copy values, such as move-only types, "no implicit copy" values +or scopes, and `take` or `borrow` operators in expressions. Changing the +parameter convention changes where copies may be necessary to perform the call. +Passing an uncopyable value as an argument to a `take` parameter ends its +lifetime, and that value cannot be used again after it's taken. + +## Effect on ABI stability + +`take` or `borrow` affects the ABI-level calling convention and cannot be +changed without breaking ABI-stable libraries (except on "trivial types" +for which copying is equivalent to `memcpy` and destroying is a no-op; however, +`take` or `borrow` also has no practical effect on parameters of trivial type). + +## Effect on API resilience + +`take` or `borrow` break ABI for ABI-stable libraries, but are intended to have +minimal impact on source-level API. When using copyable types, adding or +changing these annotations to an API should not affect its existing clients. + +## Alternatives considered + +### Leaving `take` parameter bindings immutable inside the callee + +We propose that `take` parameters should be mutable inside of the callee, +because it is likely that the callee will want to perform mutations using +the value it has ownership of. There is a concern that some users may find this +behavior unintuitive, since those mutations would not be visible in copies +of the value in the caller. This was the motivation behind +[SE-0003](https://github.com/apple/swift-evolution/blob/main/proposals/0003-remove-var-parameters.md), +which explicitly removed the former ability to declare parameters as `var` +because of this potential for confusion. However, whereas `var` and `inout` +both suggest mutability, and `var` does not provide explicit directionality as +to where mutations become visible, `take` on the other hand does not +suggest any kind of mutability to the caller, and it explicitly states the +directionality of ownership transfer. Furthermore, with move-only types, the +chance for confusion is moot, because the transfer of ownership means the +caller cannot even use the value after the callee takes ownership anyway. + +Another argument for `take` parameters to remain immutable is to serve the +proposal's stated goal of minimizing the source-breaking impact of +parameter ownership modifiers. When `take` parameters are mutable, +changing a `take` parameter to `borrow`, or removing the +`take` annotation altogether, is potentially source-breaking. However, +any such breakage is purely localized to the callee; callers are still +unaffected (as long as copyable arguments are involved). If a developer wants +to change a `take` parameter back into a `borrow`, they can still assign the +borrowed value to a local variable and use that local variable for local +mutation. + +### Naming + +We have considered several alternative naming schemes for these modifiers: + +- The current implementation in the compiler uses `__shared` and `__owned`, + and we could remove the underscores to make these simply `shared` and + `owned`. These names refer to the way a borrowed parameter receives a + "shared" borrow (as opposed to the "exclusive" borrow on an `inout` + parameter), whereas a taken parameter becomes "owned" by the callee. + found that the "shared" versus "exclusive" language for discussing borrows, + while technically correct, is unnecessarily confusing for explaining the + model. +- A previous pitch used the names `nonconsuming` and `consuming`. The current + implementation also uses `__consuming func` to notate a method that takes + ownership of its `self` parameter. + +The names `take` and `borrow` arose during [the first review of +SE-0366](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202). +These names also work well as names for operators that explicitly +transfer ownership of a variable or borrow it in place, discussed below as the +`take` and `borrow` operators under Related Directions. We think it is helpful +to align the naming of those operators with the naming of these parameter +modifiers, since it helps reinforce the relationship between the calling +conventions and the expression operators: to explicitly transfer ownership +of an argument in a call site to a parameter in a function, use `foo(take x)` +at the call site, and use `func foo(_: take T)` in the function declaration. + +### Effect on call sites and uses of the parameter + +This proposal designs the `take` and `borrow` modifiers to have minimal source +impact when applied to parameters, on the expectation that, in typical Swift +code that isn't using move-only types or other copy-controlling features, +adjusting the convention is a useful optimization on its own without otherwise +changing the programming model, letting the optimizer automatically minimize +copies once the convention is manually optimized. + +It could alternatively be argued that explicitly stating the convention for +a value argument indicates that the developer is interested in guaranteeing +that the optimization occurs, and having the annotation imply changed behavior +at call sites or inside the function definition, such as disabling implicit +copies of the parameter inside the function, or implicitly taking an argument +to a `take` parameter and ending its lifetime inside the caller after the +call site. We believe that it is better to keep the behavior of the call in +expressions independent of the declaration (to the degree possible with +implicitly copyable values), and that explicit operators on the call site +can be used in the important, but relatively rare, cases where the default +optimizer behavior is insufficient to get optimal code. + +## Related directions + +### Caller-side controls on implicit copying + +There are a number of caller-side operators we are considering to allow for +performance-sensitive code to make assertions about call behavior. These +are closely related to the `take` and `borrow` parameter modifiers and so +share their names. See also the +[Selective control of implicit copying behavior](https://forums.swift.org/t/selective-control-of-implicit-copying-behavior-take-borrow-and-copy-operators-noimplicitcopy/60168) +thread on the Swift forums for deeper discussion of this suite of features + +#### `take` operator + +Currently under review as +[SE-0366](https://github.com/apple/swift-evolution/blob/main/proposals/0366-move-function.md), +it is useful to have an operator that explicitly ends the lifetime of a +variable before the end of its scope. This allows the compiler to reliably +destroy the value of the variable, or transfer ownership, at the point of its +last use, without depending on optimization and vague ARC optimizer rules. +When the lifetime of the variable ends in an argument to a `take` parameter, +then we can transfer ownership to the callee without any copies: + +```swift +func consume(x: take Foo) + +func produce() { + let x = Foo() + consume(x: take x) + doOtherStuffNotInvolvingX() +} +``` + +#### `borrow` operator + +Relatedly, there are circumstances where the compiler defaults to copying +when it is theoretically possible to borrow, particularly when working with +shared mutable state such as global or static variables, escaped closure +captures, and class stored properties. The compiler does +this to avoid running afoul of the law of exclusivity with mutations. In +the example below, if `callUseFoo()` passed `global` to `useFoo` by borrow +instead of passing a copy, then the mutation of `global` inside of `useFoo` +would trigger a dynamic exclusivity failure (or UB if exclusivity checks +are disabled): + +```swift +var global = Foo() + +func useFoo(x: borrow Foo) { + // We need exclusive access to `global` here + global = Foo() +} + +func callUseFoo() { + // callUseFoo doesn't know whether `useFoo` accesses global, + // so we want to avoid imposing shared access to it for longer + // than necessary, and we'll pass a copy of the value. This: + useFoo(x: global) + + // will compile more like: + + /* + let globalCopy = copy(global) + useFoo(x: globalCopy) + destroy(globalCopy) + */ +} +``` + +It is difficult for the compiler to conclusively prove that there aren't +potential interfering writes to shared mutable state, so although it may +in theory eliminate the defensive copy if it proves that `useFoo`, it is +unlikely to do so in practice. The developer may know that the program will +not attempt to modify the same object or global variable during a call, +and want to suppress this copy. An explicit `borrow` operator could allow for +this: + +```swift +var global = Foo() + +func useFooWithoutTouchingGlobal(x: borrow Foo) { + /* global not used here */ +} + +func callUseFoo() { + // The programmer knows that `useFooWithoutTouchingGlobal` won't + // touch `global`, so we'd like to pass it without copying + useFooWithoutTouchingGlobal(x: borrow global) +} +``` + +If `useFooWithoutTouchingGlobal` did in fact attempt to mutate `global` +while the caller is borrowing it, an exclusivity failure would be raised. + +#### Move-only types, uncopyable values, and related features + +The `take` versus `borrow` distinction becomes much more important and +prominent for values that cannot be implicitly copied. We have plans to +introduce move-only types, whose values are never copyable, as well as +attributes that suppress the compiler's implicit copying behavior selectively +for particular variables or scopes. Operations that borrow +a value allow the same value to continue being used, whereas operations that +take a value destroy it and prevent its continued use. This makes the +convention used for move-only parameters a much more important part of their +API contract, since it directly affects whether the value is still available +after the operation: + +```swift +moveonly struct FileHandle { ... } + +// Operations that open a file handle return new FileHandle values +func open(path: FilePath) throws -> FileHandle + +// Operations that operate on an open file handle and leave it open +// borrow the FileHandle +func read(from: borrow FileHandle) throws -> Data + +// Operations that close the file handle and make it unusable take +// the FileHandle +func close(file: take FileHandle) + +func hackPasswords() throws -> HackedPasswords { + let fd = try open(path: "/etc/passwd") + // `read` borrows fd, so we can continue using it after + let contents = try read(from: fd) + // `close` takes fd from us, so we can't use it again + close(fd) + + let moreContents = try read(from: fd) // compiler error: use after take + + return hackPasswordData(contents) +} +``` + +When protocol requirements have parameters of move-only type, the ownership +convention of the corresponding parameters in an implementing method will need to +match exactly, since they cannot be implicitly copied in a thunk: + +``` +protocol WritableToFileHandle { + func write(to fh: borrow FileHandle) +} + +extension String: WritableToFileHandle { + // error: does not satisfy protocol requirement, ownership modifier for + // parameter of move-only type `FileHandle` does not match + /* + func write(to fh: take FileHandle) { + ... + } + */ + + // This is OK: + func write(to fh: borrow FileHandle) { + ... + } +} +``` + +All generic and existential types in the language today are assumed to be +copyable, but copyability will need to become an optional constraint in order +to allow move-only types to conform to protocols and be used in generic +functions. When that happens, the need for strict matching of ownership +modifiers in protocol requirements would extend also to any generic parameter +types, associated types, and existential types that are not required to be +copyable, as well as the `Self` type in a protocol that does not require +conforming types to be copyable. + +### `set`/`out` parameter convention + +By making the `borrow` and `take` conventions explicit, we mostly round out +the set of possibilities for how to handle a parameter. `inout` parameters get +**exclusive access** to their argument, allowing them to mutate or replace the +current value without concern for other code. By contrast, `borrow` parameters +get **shared access** to their argument, allowing multiple pieces of code to +share the same value without copying, so long as none of them mutate the +shared value. A `take` parameter consumes a value, leaving nothing behind, but +there still isn't a parameter analog to the opposite convention, which would +be to take an uninitialized argument and populate it with a new value. Many +languages, including C# and Objective-C when used with the "Distributed +Objects" feature, have `out` parameter conventions for this, and the Val +programming language calls this `set`. + +In Swift up to this point, return values have been the preferred mechanism for +functions to pass values back to their callers. This proposal does not propose +to add some kind of `out` parameter, but a future proposal could. + +## Acknowledgments + +Thanks to Robert Widmann for the original underscored implementation of +`__owned` and `__shared`: [https://forums.swift.org/t/ownership-annotations/11276](https://forums.swift.org/t/ownership-annotations/11276). From bb09d1952efc22a3fc92d3d30b5d099d782fc1e5 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 25 Oct 2022 10:40:13 -0700 Subject: [PATCH 2775/4563] Revisions to SE-0366 from first round of review (#1775) * Revisions to SE-0366 from first round of review - `move` is renamed to `take`. - Dropping a value without using it now requires an explicit `_ = take x` assignment again. - "Movable bindings" are referred to as "bindings with static lifetime", since this term is useful and relevant to other language features. - Additional "alternatives considered" raised during review and pitch discussions were added. - Expansion of "related directions" section contextualizes the `take` operator among other planned features for selective copy control. - Now that [ownership modifiers for parameters](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581) are being pitched, this proposal ties into that one. Based on feedback during the first review, we have gone back to only allowing parameters to be used with the `take` operator if the parameter declaration is `take` or `inout`. * Change motivating example to not entangle multiple variable lifetimes As noted by @jumhyn and others in evolution discussion, code motion optimizations could still theoretically reorder operations in a way that breaks uniqueness that relies on sequential execution to hold. Use an example that only requires forwarding ownership of a single value to demonstrate the benefit of `take`. * Add future direction discussing destructuring methods on move-only types. It would be useful to be able to destructure move-only structs, but doing so in the face of a `deinit` requires suppressing the deinit logic, since deinit needs to start from a fully-initialized value to tear down. We could provide a special operator for methods on such a type to do destructuring in limited situations where it's appropriate for their API. * Schedule SE-0366 for re-review. Co-authored-by: Holly Borla --- proposals/0366-move-function.md | 752 +++++++++++++++++++++----------- 1 file changed, 498 insertions(+), 254 deletions(-) diff --git a/proposals/0366-move-function.md b/proposals/0366-move-function.md index bc4525ef30..0cc7de846d 100644 --- a/proposals/0366-move-function.md +++ b/proposals/0366-move-function.md @@ -1,21 +1,23 @@ -# Move Operation + "Use After Move" Diagnostic +# `take` operator to end the lifetime of a variable binding * Proposal: [SE-0366](0366-move-function.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Joe Groff](https://github.com/jckarter) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Returned for revision** -* Implementation: Implemented on main as stdlib SPI (`_move` function instead of `move` keyword) +* Status: **Active Review (Oct 25 - Nov 8, 2022)** +* Implementation: Implemented on main as stdlib SPI (`_move` instead of `take` keyword) * Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic)) ([review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0366-move-operation-use-after-move-diagnostic/59687)) -* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md) +* Previous Revisions: + * [1](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md) + * [2](https://github.com/apple/swift-evolution/blob/43849aa9ae3e87c434866c5a5e389af67537ca26/proposals/0366-move-function.md) ## Introduction -In this document, we propose adding a new operation, marked by the -context-sensitive keyword `move`, to the -language. `move` ends the lifetime of a specific local `let`, -local `var`, or `consuming` function parameter, and enforces this +In this document, we propose adding a new operator, marked by the +context-sensitive keyword `take`, to the +language. `take` ends the lifetime of a specific local `let`, +local `var`, or function parameter, and enforces this by causing the compiler to emit a diagnostic upon any use after the -move. This allows for code that relies on **forwarding ownership** +take. This allows for code that relies on **forwarding ownership** of values for performance or correctness to communicate that requirement to the compiler and to human readers. As an example: @@ -23,25 +25,25 @@ the compiler and to human readers. As an example: useX(x) // do some stuff with local variable x // Ends lifetime of x, y's lifetime begins. -let y = move x // [1] +let y = take x // [1] useY(y) // do some stuff with local variable y useX(x) // error, x's lifetime was ended at [1] // Ends lifetime of y, destroying the current value. -move y // [2] +_ = take y // [2] useX(x) // error, x's lifetime was ended at [1] useY(y) // error, y's lifetime was ended at [2] ``` ## Motivation -Swift uses reference counting and copy-on-write to allow for developers to -write code with value semantics, without normally having to worry too much +Swift uses reference counting and copy-on-write to allow developers to +write code with value semantics and not normally worry too much about performance or memory management. However, in performance sensitive code, developers want to be able to control the uniqueness of COW data structures and reduce retain/release calls in a way that is future-proof against changes to -the language implementation or source code. Consider the following array/uniqueness +the language implementation or source code. Consider the following example: ```swift @@ -52,40 +54,55 @@ func test() { // preserve that property. x.append(5) - // We create a new variable y so we can write an algorithm where we may - // change the value of y (causing a COW copy of the buffer shared with x). - var y = x - longAlgorithmUsing(&y) - consumeFinalY(y) - - // We no longer use y after this point. Ideally, x would be guaranteed - // unique so we know we can append again without copying. - x.append(7) + // Pass the current value of x off to another function, that could take + // advantage of its uniqueness to efficiently mutate it further. + doStuffUniquely(with: x) + + // Reset x to a new value. Since we don't use the old value anymore, + // it could've been uniquely referenced by the callee. + x = [] + doMoreStuff(with: &x) } -``` -In the example above, `y`'s formal lifetime extends to the end of -scope. When we go back to using `x`, although the compiler may optimize -the actual lifetime of `y` to release it after its last use, there isn't -a strong guarantee that it will. Even if the optimizer does what we want, -programmers modifying this code in the future -may introduce new references to `y` that inadvertently extend its lifetime -and break our attempt to keep `x` unique. There isn't any indication in the -source code that that the end of `y`'s use is important to the performance -characteristics of the code. +func doStuffUniquely(with value: [Int]) { + // If we received the last remaining reference to `value`, we'd like + // to be able to efficiently update it without incurring more copies. + var newValue = value + newValue.append(42) -Swift-evolution pitch thread: [https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic) - -## Proposed solution: Move Operation + "Use After Move" Diagnostic + process(newValue) +} +``` -That is where the `move` operation comes into play. `move` consumes -a **movable binding**, which is either -an unescaped local `let`, unescaped local `var`, or function argument, with +In the example above, a value is built up in the variable `x` and then +handed off to `doStuffUniquely(with:)`, which makes further modifications to +the value it receives. `x` is then set to a new value. It should be possible for +the caller to **forward ownership** of the value of `x` to `doStuffUniquely`, +since it no longer uses the value as is, to avoid unnecessary retains or +releases of the array buffer and unnecessary copy-on-writes of the array +contents. `doStuffUniquely` should in turn be able to move its parameter into +a local mutable variable and modify the unique buffer in place. The compiler +could make these optimizations automatically, but a number of analyses have to +align to get the optimal result. The programmer may want to guarantee that this +series of optimizations occurs, and receive diagnostics if they wrote the code +in a way that would interfere with these optimizations being possible. + +Swift-evolution pitch threads: + +- [https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic) +- [https://forums.swift.org/t/selective-control-of-implicit-copying-behavior-take-borrow-and-copy-operators-noimplicitcopy/60168](https://forums.swift.org/t/selective-control-of-implicit-copying-behavior-take-borrow-and-copy-operators-noimplicitcopy/60168) + +## Proposed solution: `take` operator + +That is where the `take` operator comes into play. `take` consumes +the current value of a **binding with static lifetime**, which is either +an unescaped local `let`, unescaped local `var`, or function parameter, with no property wrappers or get/set/read/modify/etc. accessors applied. It then - provides a compiler guarantee that the binding will + provides a compiler guarantee that the current value will be unable to be used again locally. If such a use occurs, the compiler will -emit an error diagnostic. We can modify the previous example to use `move` to -explicitly end the lifetime of `y` when we're done with it: +emit an error diagnostic. We can modify the previous example to use `take` to +explicitly end the lifetime of `x`'s current value when we pass it off to +`doStuffUniquely(with:)`: ```swift func test() { @@ -95,55 +112,81 @@ func test() { // preserve that property. x.append(5) - // We create a new variable y so we can write an algorithm where we may - // change the value of y (causing a COW copy of the buffer shared with x). - var y = x - longAlgorithmUsing(&y) - // We no longer use y after this point, so move it when we pass it off to - // the last use. - consumeFinalY(move y) + // Pass the current value of x off to another function, that + doStuffUniquely(with: take x) - // x will be unique again here. - x.append(7) + // Reset x to a new value. Since we don't use the old value anymore, + x = [] + doMoreStuff(with: &x) } ``` -This addresses both of the motivating issues above: `move` guarantees the -lifetime of `y` ends at the given point, allowing the compiler to generate -code to clean up or transfer ownership of `y` without relying on optimization. -Furthermore, if a future maintainer modifies the code in a way that extends -the lifetime of `y` past the expected point, then the compiler will raise an +The `take x` operator syntax deliberately mirrors the +proposed [ownership modifier](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581) +parameter syntax, `(x: take T)`, because the caller-side behavior of `take` +operator is analogous to a callee’s behavior receiving a `take` parameter. +`doStuffUniquely(with:)` can use the `take` operator, combined with +the `take` parameter modifier, to preserve the uniqueness of the parameter +as it moves it into its own local variable for mutation: + +```swift +func doStuffUniquely(with value: take [Int]) { + // If we received the last remaining reference to `value`, we'd like + // to be able to efficiently update it without incurring more copies. + var newValue = take value + newValue.append(42) + + process(newValue) +} +``` + +This takes the guesswork out of the optimizations discussed above: in the +`test` function, the final value of `x` before reassignment is explicitly +handed off to `doStuffUniquely(with:)`, ensuring that the callee receives +unique ownership of the value at that time, and that the caller can't +use the old value again. Inside `doStuffUniquely(with:)`, the lifetime of the +immutable `value` parameter is ended to initialize the local variable `newValue`, +ensuring that the assignment doesn't cause a copy. +Furthermore, if a future maintainer modifies the code in a way that breaks +this transfer of ownership chain, then the compiler will raise an error. For instance, if a maintainer later introduces an additional use of -`y` after the move, it will raise an error: +`x` after it's taken, but before it's reassigned, they will see an error: ```swift func test() { var x: [Int] = getArray() - - // x is appended to. After this point, we know that x is unique. We want to - // preserve that property. x.append(5) - // We create a new variable y so we can write an algorithm where we may - // change the value of y (causing a COW copy of the buffer shared with x). - var y = x - longAlgorithmUsing(&y) - // We think we no longer use y after this point... - consumeFinalY(move y) + doStuffUniquely(with: take x) - // ...and x will be unique again here... - x.append(7) + // ERROR: x used after being taken from + doStuffInvalidly(with: x) - // ...but this additional use of y snuck in: - useYAgain(y) // error: 'y' used after being moved + x = [] + doMoreStuff(with: &x) } ``` -`move` only ends the lifetime of a specific movable binding. It is not tied to -the lifetime of the value of the binding at the time of the move, or to any -particular object instance. If we declare another local constant `other` with -the same value of `x`, we can use that other binding after we end the lifetime -of `x`, as in: +Likewise, if the maintainer tries to access the original `value` parameter inside +of `doStuffUniquely` after being taken to initialize `newValue`, they will +get an error: + +``` +func doStuffUniquely(with value: take [Int]) { + // If we received the last remaining reference to `value`, we'd like + // to be able to efficiently update it without incurring more copies. + var newValue = take value + newValue.append(42) + + process(newValue) +} +``` + +`take` can also end the lifetime of local immutable `let` bindings, which become +unavailable after their value is taken since they cannot be reassigned. +Also note that `take` operates on bindings, not values. If we declare a +constant `x` and another local constant `other` with the same value, +we can still use `other` after we take the value from `x`, as in: ```swift func useX(_ x: SomeClassType) -> () {} @@ -152,15 +195,14 @@ func f() { let x = ... useX(x) let other = x // other is a new binding used to extend the lifetime of x - move x // x's lifetime ends + _ = take x // x's lifetime ends useX(other) // other is used here... no problem. useX(other) // other is used here... no problem. } ``` -In fact, each movable binding's lifetime is tracked independently, and gets a -separate diagnostic if used after move. We can move `other` independently -of `x`, and get separate diagnostics for both variables: +We can also take `other` independently of `x`, and get separate diagnostics for both +variables: ```swift func useX(_ x: SomeClassType) -> () {} @@ -169,48 +211,23 @@ func f() { let x = ... useX(x) let other = x - move x - useX(move other) - useX(other) // error: 'other' used after being moved - useX(x) // error: 'x' used after being moved + _ = take x + useX(take other) + useX(other) // error: 'other' used after being taken + useX(x) // error: 'x' used after being taken } ``` -If a local `var` is moved, then a new value can be assigned into -the variable after an old value has been moved out. One can -begin using the var again after one re-assigns to the var: - -```swift -func f() { - var x = getValue() - move x - useX(x) // error: no value in x - x = getValue() - useX(x) // ok, x has a new value here -} -``` - -This follows from move being applied to the binding (`x`), not the value in the -binding (the value returned from `getValue()`). - -We also support `move` of function arguments: +`inout` function parameters can also be used with `take`. Like a `var`, an +`inout` parameter can be reassigned after being taken from and used again; +however, since the final value of an `inout` parameter is passed back to the +caller, it *must* be reassigned by the callee before it +returns. So this will raise an error because `buffer` doesn't have a value at +the point of return: ```swift -func f(_ x: SomeClassType) { - move x - useX(x) // !! Error! Use of x after move -} -``` - -This includes `inout` function arguments. Like a `var`, an `inout` argument can -be reassigned after being moved from and used again; however, since the final -value of an `inout` argument is passed back to the caller, an `inout` argument -*must* be reassigned by the callee before it returns. This will raise an error -because `buffer` doesn't have a value at the point of return: - -```swift -func f(_ buffer: inout Buffer) { // error: 'buffer' not reinitialized after move! - let b = move buffer // note: move was here +func f(_ buffer: inout Buffer) { // error: 'buffer' not reinitialized after take! + let b = take buffer // note: take was here b.deinitialize() ... write code ... } // note: return without reassigning inout argument `buffer` @@ -220,7 +237,7 @@ But we can reinitialize `buffer` by writing the following code: ```swift func f(_ buffer: inout Buffer) { - let b = move buffer + let b = take buffer b.deinitialize() // ... write code ... // We re-initialized buffer before end of function so the checker is satisfied @@ -228,13 +245,13 @@ func f(_ buffer: inout Buffer) { } ``` -`defer` can also be used to reinitialize an `inout` or `var` after a move, +`defer` can also be used to reinitialize an `inout` or `var` after a take, in order to ensure that reassignment happens on any exit from scope, including thrown errors or breaks out of loops. So we can also write: ```swift func f(_ buffer: inout Buffer) { - let b = move buffer + let b = take buffer // Ensure the buffer is reinitialized before we exit. defer { buffer = getNewInstance() } try b.deinitializeOrError() @@ -244,21 +261,20 @@ func f(_ buffer: inout Buffer) { ## Detailed design -At runtime, `move value` evaluates to the current value bound to `value`. -However, at compile time, the presence of a `move` forces +At runtime, `take x` evaluates to the current value bound to `x`, just like the +expression `x` does. However, at compile time, the presence of a `take` forces ownership of the argument to be transferred out of the binding at the given -point, and triggers diagnostics that prove that it is safe to do so. -The compiler flags any proceeding uses of the binding that are reachable from -the move. The operand to `move` is required to be a reference to a *movable -binding*. The following kinds of declarations can currently be referenced as -movable bindings: +point so. Any ensuing use of the binding that's reachable from the `take` +is an error. The operand to `take` is required to be a reference +to a *binding with static lifetime*. The following kinds of declarations can +currently be referenced as bindings with static lifetime: - a local `let` constant in the immediately-enclosing function, - a local `var` variable in the immediately-enclosing function, - one of the immediately-enclosing function's parameters, or - the `self` parameter in a `mutating` or `__consuming` method. -A movable binding also must satisfy the following requirements: +A binding with static lifetime also must satisfy the following requirements: - it cannot be captured by an `@escaping` closure or nested function, - it cannot have any property wrappers applied, @@ -266,46 +282,46 @@ A movable binding also must satisfy the following requirements: `didSet`, `willSet`, `_read`, or `_modify`, - it cannot be an `async let`. -Possible extensions to the set of movable bindings are discussed under -Future Directions. It is an error to pass `move` an argument that doesn't -reference a movable binding. +Possible extensions to the set of operands that can be used with `take` are +discussed under Future Directions. It is an error to use `take` with an operand +that doesn't reference a binding with static lifetime. -Given a valid movable binding, the compiler ensures that there are no other -references to the binding after it is moved. The analysis is +Given a valid operand, `take` enforces that there are no other +references to the binding after it is taken. The analysis is flow sensitive, so one is able to end the lifetime of a value conditionally: ```swift if condition { - let y = move x + let y = take x // I can't use x anymore here! - useX(x) // !! ERROR! Use after move. + useX(x) // !! ERROR! Use after take. } else { // I can still use x here! useX(x) // OK } // But I can't use x here. -useX(x) // !! ERROR! Use after move. +useX(x) // !! ERROR! Use after take. ``` If the binding is a `var`, the analysis additionally allows for code to -conditionally reinitialize the var and thus be able to use it in positions +conditionally reinitialize the var and thus use it in positions that are dominated by the reinitialization. Consider the following example: ```swift if condition { - move x + _ = take x // I can't use x anymore here! - useX(x) // !! ERROR! Use after move. + useX(x) // !! ERROR! Use after take. x = newValue // But now that I have re-assigned into x a new value, I can use the var // again. useX(x) // OK } else { - // I can still use x here, since it wasn't moved on this path! + // I can still use x here, since it wasn't taken on this path! useX(x) // OK } -// Since I reinitialized x along the `if` branch, and it was never moved +// Since I reinitialized x along the `if` branch, and it was never taken // from on the `else` branch, I can use it here too. useX(x) // OK ``` @@ -317,27 +333,32 @@ with a valid value before proceeding. For an `inout` parameter, the analysis behaves the same as for a `var`, except that all exits from the function (whether by `return` or by `throw`) are considered to be uses of the parameter. Correct code therefore *must* reassign -inout parameters after they are moved from. +inout parameters after they are taken from. + +Using `take` on a binding without using the result raises a warning, +just like a function call that returns an unused non-`Void` result. +To "drop" a value without using it, the `take` can be assigned to +`_` explicitly. ## Source compatibility -`move` behaves as a contextual keyword. In order to avoid interfering -with existing code that calls functions named `move`, the operand to -`move` must begin with another identifier, and must consist of an +`take` behaves as a contextual keyword. In order to avoid interfering +with existing code that calls functions named `take`, the operand to +`take` must begin with another identifier, and must consist of an identifier or postfix expression: ``` -move x // OK -move [1, 2, 3] // Syntax error -move(x) // Call to global function `move`, not a move operation -move x.y.z // Syntactically OK (although x.y.z is not currently a movable binding) -move x[0] // Syntactically OK (although x[0] is not currently a movable binding) -move x + y // Parses as (move x) + y +take x // OK +take [1, 2, 3] // Subscript access into property named `take`, not a take operation +take (x) // Call to function `take`, not a take operation +take x.y.z // Syntactically OK (although x.y.z is not currently semantically valid) +take x[0] // Syntactically OK (although x[0] is not currently semantically valid +take x + y // Parses as (take x) + y ``` ## Effect on ABI stability -`move` requires no ABI additions. +`take` requires no ABI additions. ## Effect on API resilience @@ -350,46 +371,116 @@ None, this is additive. The [first reviewed revision](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md) of this proposal offered `move(x)` as a special function with semantics recognized by the compiler. Based on initial feedback, -we pivoted to the contextual keyword spelling. -As a function, `move` would be rather unusual, since it only accepts certain -forms of expression as its argument, and it doesn't really have any runtime -behavior of its own, acting more as a marker for the compiler to perform -additional analysis. +we pivoted to the contextual keyword spelling. As a function, this operation +would be rather unusual, since it only accepts certain forms of expression as +its argument, and it doesn't really have any runtime behavior of its own, +acting more as a marker for the compiler to perform additional analysis. + +The community reviewed the contextual keyword syntax, using the name `move x`, +and through further discussion the alternative name `take` arose. This name +aligns with the term used in the Swift compiler internals, and also reads well +as the analogous parameter ownership modifier, `(x: take T)`, so the authors +now favor this name. + +Many have suggested alternative spellings that also make `take`'s special +nature more syntactically distinct, including: + +- an expression attribute, like `useX(@take x)` +- a compiler directive, like `useX(#take x)` +- an operator, like `useX(<-x)` -Many have suggested alternative spellings that -also make `move`'s special nature more syntactically distinct, including: +### Use of scoping to end lifetimes -- an expression attribute, like `useX(@move x)` -- a compiler directive, like `useX(#move x)` -- an operator, like `useX(<-x)` +It is possible in the language today to foreshorten the lifetime of local +variables using lexical scoping: -### The name `move` +```swift +func test() { + var x: [Int] = getArray() + + // x is appended to. After this point, we know that x is unique. We want to + // preserve that property. + x.append(5) + + // We create a new variable y so we can write an algorithm where we may + // change the value of y (causing a COW copy of the buffer shared with x). + do { + var y = x + longAlgorithmUsing(&y) + consumeFinalY(y) + } + + // We no longer use y after this point. Ideally, x would be guaranteed + // unique so we know we can append again without copying. + x.append(7) +} +``` + +However, there are a number of reasons not to rely solely on lexical scoping +to end value lifetimes: -There are also potentially other names besides `move` that we could use. We're -proposing using the name `move` because it is an established term of art in -other programming language communities including C++ and Rust, as well as a -term that has already been used in other Swift standard library APIs such as -the `UnsafeMutablePointer.move*` family of methods that move a value out of -memory referenced by a pointer. +- Adding lexical scopes requires nesting, which can lead to "pyramid of doom" + situations when managing the lifetimes of multiple variables. +- Value lifetimes don't necessarily need to nest, and may overlap or interleave + with control flow. This should be valid: + + ```swift + let x = foo() + let y = bar() + // end x's lifetime before y's + consume(take x) + consume(take y) + ``` + +- Lexical scoping cannot be used by itself to shorten the lifetime of function + parameters, which are in scope for the duration of the function body. +- Lexical scoping cannot be used to allow for taking from and reinitializing + mutable variables or `inout` parameters. + +Looking outside of Swift, the Rust programming language originally only had +strictly scoped value lifetimes, and this was a significant ergonomic +problem until "non-lexical lifetimes" were added later, which allowed for +value lifetimes to shrinkwrap to their actual duration of use. ## Future directions -### Dynamic enforcement of `move` for other kinds of bindings +### Dynamic enforcement of `take` for other kinds of bindings -In the future, we may expand the set of movable bindings to include globals, -escaped local variables, and class stored properties, although doing so in full -generality would require dynamic enforcement in addition to static checking to -ensure that shared state is not read from once it is moved, similar to how we -need to dynamically enforce exclusivity when accessing globals and class stored -properties. Since this dynamic enforcement turns misuse of `move`s into runtime -errors rather than compile-time guarantees, we might want to make those dynamic -cases syntactically distinct, to make the possibility of runtime errors clear. +In the future, we may want to accommodate the ability to dynamically +take from bindings with dynamic lifetime, such as escaped local +variables, and class stored properties, although doing so in full +generality would require dynamic enforcement in addition to static +checking, similar to how we need to dynamically enforce exclusivity +when accessing globals and class stored properties. Since this +dynamic enforcement turns misuse of `take`s into runtime errors +rather than compile-time guarantees, we might want to make those +dynamic cases syntactically distinct, to make the possibility of +runtime errors clear. + +`Optional` and other types with a canonical "no value" or "empty" +state can use the static `take` operator to provide API that +dynamically takes ownership of the current value inside of them +while leaving them in their empty state: + +``` +extension Optional { + mutating func take() -> Wrapped { + switch take self { + case .some(let x): + self = .none + return x + case .none: + fatalError("trying to take from an empty Optional") + } + } +} +``` -### Piecewise `move` of frozen structs and tuples +### Piecewise `take` of frozen structs and tuples For frozen structs and tuples, both aggregates that the compiler can statically know the layout of, we could do finer-grained analysis and allow their -individual fields to be moved independently: +individual fields to be taken independently: ```swift struct TwoStrings { @@ -398,94 +489,229 @@ struct TwoStrings { } func foo(x: TwoStrings) { - use(move x.first) - // ERROR! part of x was moved out of + use(take x.first) + // ERROR! part of x was taken use(x) // OK, this part wasn't use(x.second) } ``` -### `move` of computed properties, property wrappers, properties with accessors, etc. - -It would potentially be useful to be able to move variables and properties with -modified access behavior, such as computed properties, properties with -didSet/willSet observers, property wrappers, and so on. Although we could do -move analysis on these properties, we wouldn't be able to get the full -performance benefits from consuming a computed variable without allowing -for some additional accessors to be defined, such as a "consuming getter" that -can consume its `self` in order to produce the property value, and an -initializer to reinitialize `self` on reassignment after a `move`. - -### Suppressing implicit copying - -Another useful tool for programmers is to be able to suppress Swift's usual -implicit copying rules for a type, specific values, or a scope. The `move` function -as proposed is not intended to be a replacement for move-only types or for -"no-implicit-copy" constraints on values or scopes. The authors believe that -there is room in the language for both features; `move` is a useful incremental -annotation for code that is value type- or object-oriented which needs -minor amounts of fine control for performance. Suppressing implicit copies can -ultimately achieve the same goal, but requires adapting to a stricter -programming model and controlling ownership in order to avoid the need for -explicit copies or to eliminate copies entirely. That level of control -definitely has its place, but requires a higher investment than we expect -`move` to. - -### `shared` and `owned` argument modifiers - -The ownership convention used when passing arguments by value is usually -indefinite; the compiler initially tries having the function receive -parameters by borrow, so that the caller is made to keep the value alive on the -callee's behalf for the duration of the call (The exceptions are setters and -initializers, where the compiler defaults to transferring ownership of arguments from the -caller to the callee). Optimization may subsequently adjust the compiler's -initial decisions if it sees opportunities to reduce overall ARC traffic. Using -`move` on a parameter that was received as a borrow can syntactically shorten -the lifetime of the argument binding, but can't actually shorten the lifetime -of the argument at runtime, since the borrowed value remains owned by the -caller. - -In order to guarantee the forwarding of a value's ownership across function -calls, `move` is therefore not sufficient on its own. We would also need to -guarantee the calling convention for the enclosing function transfers ownership -to the callee, using annotations which behave similar to `inout`. These -are currently implemented internally in the compiler as `__shared` and `__owned`, -and we could expose these as official language features: +### Destructuring methods for move-only types with `deinit` -```swift -func test() { - var x: [Int] = getArray() - - // x is appended to. After this point, we know that x is unique. We want to - // preserve that property. - x.append(5) - - // We create a new variable y so we can write an algorithm where we may - // change the value of y (causing a COW copy of the buffer shared with x). - var y = x - longAlgorithmUsing(&y) - // We no longer use y after this point, so move it when we pass it off to - // the last use. - consumeFinalY(move y) +Move-only types would allow for the possibility of value types with custom +`deinit` logic that runs at the end of a value of the type's lifetime. +Typically, this logic would run when the final owner of the value is finished +with it, which means that a function which `take`s an instance, or a +`taking func` method on the type itself, would run the deinit if it does not +forward ownership anywhere else: - // x will be unique again here. - x.append(7) +``` +moveonly struct FileHandle { + var fd: Int32 + + // close the fd on deinit + deinit { close(fd) } +} + +func dumpAndClose(to fh: take FileHandle, contents: Data) { + write(fh.fd, contents) + // fh implicitly deinit-ed here, closing it +} +``` + +However, this may not always be desirable, either because the function performs +an operation that invalidates the value some other way, making it unnecessary +or incorrect for `deinit` to run on it, or because the function wants to be able +to take ownership of parts away from the value: + +``` +extension FileHandle { + // Return the file descriptor back to the user for manual management + // and disable automatic management with the FileHandle value. + + taking func giveUp() -> Int32 { + return fd + // How do we stop the deinit from running here? + } +} +``` + +Rust has the magic function `mem::forget` to suppress destruction of a value, +though `forget` in Rust still does not allow for the value to be destructured +into parts. We could come up with a mechanism in Swift that both suppresses +implicit deinitialization, and allows for piecewise taking of its components. +This doesn't require a new parameter convention (since it fits within the +ABI of a `take T` parameter), but could be spelled as a new operator +inside of a `taking func`: + +``` +extension FileHandle { + // Return the file descriptor back to the user for manual management + // and disable automatic management with the FileHandle value. + + taking func giveUp() -> Int32 { + // `deinit fd` is strawman syntax for consuming a value without running + // its deinitializer. it is only allowed inside of `taking func` methods + // that have visibility into the type layout. + return (deinit self).fd + } } -// consumeFinalY declares its argument `owned`, ensuring it takes ownership -// of the argument from the caller. -func consumeFinalY(y: owned [Int]) { - consumeYSomewhereElse(move y) +moveonly struct SocketPair { + var input, output: FileHandle + + deinit { /* ... */ } + + // Break the pair up into separately-managed FileHandles + taking func split() -> (input: FileHandle, output: FileHandle) { + // Break apart the value without running the standard deinit + return ((deinit self).input, (deinit self).output) + } } ``` -The presence of a `move` could be used as an optimizer hint to infer that -`owned` convention is desired within a module, but since the choice of `shared` -or `owned` affects ABI, we would need an explicit annotation for public API -to specify the desired ABI. There are also reasons to expose these -conventions beyond `move`. We leave it to a future dedicated proposal to -delve deeper into these modifiers. +Suppressing the normal deinit logic for a type should be done carefully. +Although Rust allows `mem::forget` to be used anywhere, and considers it "safe" +based on the observation that destructors may not always run if a program +crashes or leaks a value anyway, that observation is less safe in the case +of values whose lifetime depends on a parent value, and where the child value's +deinit must be sequenced with operations on the parent. As such, I think +the ability should be limited to methods defined on the type itself. Also, +within the constraints of Swift's stable ABI and library evolution model, +code that imports a module may not always have access to the concrete layout +of a type, which would make partial destructuring impossible, which further +limits the contexts in which such an operator can be used. + +### `take` from computed properties, property wrappers, properties with accessors, etc. + +It would potentially be useful to be able to `take` from variables +and properties with modified access behavior, such as computed +properties, properties with didSet/willSet observers, property +wrappers, and so on. Although we could do lifetime analysis on these +properties, we wouldn't be able to get the full performance benefits +from consuming a computed variable without allowing for some +additional accessors to be defined, such as a "consuming getter" +that can consume its `self` in order to produce the property value, +and an initializer to reinitialize `self` on reassignment after a +`move`. + +### Additional selective controls for implicit copying behavior + +Pitch: [Selective control of implicit copying behavior](https://forums.swift.org/t/selective-control-of-implicit-copying-behavior-take-borrow-and-copy-operators-noimplicitcopy/60168) + +The `take` operator is one of a number of implicit copy controls +we're considering: + +- A value that isn't modified can generally be "borrowed" and + shared in-place by multiple bindings, or between a caller and + callee, without copying. However, the compiler will + pass shared mutable state by copying the current value, and + passing that copy to a callee. We do this to avoid + potential rule-of-exclusivity violations, since it is difficult to + know for sure whether a callee will go back and try to mutate the + same global variable, object, or other bit of shared mutable + state: + + ```swift + var global = Foo() + + func useFoo(x: Foo) { + // We would need exclusive access to `global` to do this: + + /* + global = Foo() + */ + } + + func callUseFoo() { + // callUseFoo doesn't know whether `useFoo` accesses global, + // so we want to avoid imposing shared access to it for longer + // than necessary. So by default the compiler will + // pass a copy instead, and this: + useFoo(x: global) + + // will compile more like: + + /* + let copyOfGlobal = copy(global) + useFoo(x: copyOfGlobal) + destroy(copyOfGlobal) + */ + } + ``` + + Although the compiler is allowed to eliminate the defensive copy + inside callUseFoo if it proves that useFoo doesn't try to write + to the global variable, it is unlikely to do so in practice. The + developer however knows that useFoo doesn't modify global, and + may want to suppress this copy in the call site. An explicit + `borrow` operator would let the developer communicate this to the + compiler: + + ``` + var global = Foo() + + func useFoo(x: Foo) { + /* global not used here */ + } + + func callUseFoo() { + // The programmer knows that `useFoo` won't + // touch `global`, so we'd like to pass it without copying + useFoo(x: borrow global) + } + ``` + +- `take` and `borrow` operators can eliminate copying in + common localized situations, but it is also useful to be able to + suppress implicit copying altogether for certain variables, types, + and scopes. We could define an attribute to specify that bindings + with static lifetime, types, or scopes should not admit implicit + copies: + + ``` + // we're not allowed to implicitly copy `x` + func foo(@noImplicitCopy x: String) { + } + + // we're not allowed to implicitly copy values (statically) of + // type Gigantic + @noImplicitCopy struct Gigantic { + var fee, fie, fo, fum: String + } + + // we're not allowed to implicitly copy inside this hot loop + for item in items { + @noImplicitCopy do { + } + } + ``` + +### `borrow` and `take` argument modifiers + +Pitch: [`borrow` and `take` parameter ownership modifiers](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581) + +Swift currently only makes an explicit distinction between +pass-by-value and pass-by-`inout` parameters, leaving the mechanism +for pass-by-value up to the implementation. But there are two broad +conventions that the compiler uses to pass by value: + +- The callee can **borrow** the parameter. The caller guarantees that + its argument object will stay alive for the duration of the call, + and the callee does not need to release it (except to balance any + additional retains it performs itself). +- The callee can **take** the parameter. The callee becomes + responsible for either releasing the parameter or passing + ownership of it along somewhere else. If a caller doesn't want to + give up its own ownership of its argument, it must retain the + argument so that the callee can take the extra reference count. + +In order to allow for manual optimization of code, and to support +move-only types where this distinction becomes semantically +significant, we plan to introduce explicit parameter modifiers to +let developers specify explicitly which convention a parameter +should use. ## Acknowledgments @@ -493,6 +719,24 @@ Thanks to Nate Chandler, Tim Kientzle, and Holly Borla for their help with this! ## Revision history +Changes from the [second revision](https://github.com/apple/swift-evolution/blob/43849aa9ae3e87c434866c5a5e389af67537ca26/proposals/0366-move-function.md): + +- `move` is renamed to `take`. +- Dropping a value without using it now requires an explicit + `_ = take x` assignment again. +- "Movable bindings" are referred to as "bindings with static lifetime", + since this term is useful and relevant to other language features. +- Additional "alternatives considered" raised during review + and pitch discussions were added. +- Expansion of "related directions" section contextualizes the + `take` operator among other planned features for selective copy + control. +- Now that [ownership modifiers for parameters](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581) + are being pitched, this proposal ties into that one. Based on + feedback during the first review, we have gone back to only allowing + parameters to be used with the `take` operator if the parameter + declaration is `take` or `inout`. + Changes from the [first revision](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md): - `move x` is now proposed as a contextual keyword, instead of a magic function From 657ef2fb37a9d2e53b03f859dfe7c9f3256f43c7 Mon Sep 17 00:00:00 2001 From: Les Pruszynski Date: Tue, 25 Oct 2022 22:35:55 +0100 Subject: [PATCH 2776/4563] Update 0377-parameter-ownership-modifiers.md (#1822) small grammatical change --- proposals/0377-parameter-ownership-modifiers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index fe86365fea..d20c39bf6a 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -112,7 +112,7 @@ let f: (take Foo) -> Void = { a in a.foo() } let f: (inout Foo) -> Void = { a in a.foo() } ``` -Methods can using the `taking` or `borrowing` modifier to indicate that they +Methods can use the `taking` or `borrowing` modifier to indicate that they take ownership of their `self` parameter, or they borrow it. These modifiers are mutually exclusive with each other and with the existing `mutating` modifier: From 89420a860581dcd2159a57fdcda12dab571330f2 Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Wed, 26 Oct 2022 10:44:19 +0100 Subject: [PATCH 2777/4563] rephrase the paragraph with kinds of metadata --- proposals/NNNN-opt-in-reflection-metadata.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index 923856f3ca..94ad9fe54b 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -16,7 +16,13 @@ Swift-evolution thread: [Discussion thread topic for that proposal](https://foru ## Motivation -There are two kinds of Swift metadata - Core Metadata (type metadata record, nominal type descriptor, etc) and Reflection metadata. The former must constantly be emitted and may only be stripped if provenly not used. Core Metadata is not affected by this proposal. The latter contains optional information about the type's fields and their names, not used by the language's runtime features, and the emission may be skipped if not used by reflection-consuming APIs. +There are two kinds of Swift metadata emitted be the compiler: + +1. Core Metadata (type metadata record, nominal type descriptor, etc). +2. Reflection metadata (reflection metadata field descriptor). + +Core metadata must constantly be emitted and may only be stripped if provenly not used. (This metadata isn't affected by this proposal) +Reflection metadata contains optional information about declarations' fields - their names and references to their types. This metadata isn't used by the language's runtime features, and the emission may be skipped if such types aren't passed to reflection-consuming APIs. APIs can use Reflection Metadata differently. Some like `print`, and `dump` will still work with disabled reflection, but the output will be limited. Others, like SwiftUI, rely on it and won't work correctly if the reflection metadata is missing. While the former can benefit as well, the main focus of this proposal is on the latter. From f1a677e5a9d1e1fe8522261dae55b588801b2c92 Mon Sep 17 00:00:00 2001 From: FranzBusch Date: Wed, 26 Oct 2022 11:16:55 +0100 Subject: [PATCH 2778/4563] Add proposal for `AsyncStream` convenience factories --- proposals/NNNN-async-stream-factory.md | 123 +++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 proposals/NNNN-async-stream-factory.md diff --git a/proposals/NNNN-async-stream-factory.md b/proposals/NNNN-async-stream-factory.md new file mode 100644 index 0000000000..d798bb4975 --- /dev/null +++ b/proposals/NNNN-async-stream-factory.md @@ -0,0 +1,123 @@ +# Convenience Async[Throwing]Stream.makeStream methods + +* Proposal: [SE-NNNN](NNNN-async-stream-factory.md) +* Authors: [Franz Busch](https://github.com/FranzBusch) +* Review Manager: TBD +* Status: **Awaiting implementation** + + +## Introduction + +With [SE-0314](https://github.com/apple/swift-evolution/blob/main/proposals/0314-async-stream.md) +we introduced `AsyncStream` and `AsyncThrowingStream` which act as a source +`AsyncSequence` that the standard library offers. + +## Motivation + +After having used `Async[Throwing]Stream` for some time, a common usage +is to pass the continuation and the `Async[Throwing]Stream` to different places. +This requires escaping the `Async[Throwing]Stream.Continuation` out of +the closure that is passed to the initialiser. +Escaping the continuation is slightly inconvenient since it requires a dance +around an implicitly unwrapped optional. + +## Proposed solution + +In order to fill this gap, I propose to add a new static method `makeStream` on +`AsyncStream` and `AsyncThrowingStream` that returns both the stream +and the continuation. + +## Detailed design + +I propose to add the following code to `AsyncStream` and `AsyncThrowingStream` +respectively. + +```swift +extension AsyncStream { + /// Initializes a new ``AsyncStream`` and an ``AsyncStream/Continuation``. + /// + /// - Parameters: + /// - elementType: The element type of the sequence. + /// - limit: The buffering policy that the stream should use. + /// - Returns: A tuple which contains the stream and its continuation. + @_alwaysEmitIntoClient + public static func makeStream( + of elementType: Element.Type = Element.self, + bufferingPolicy limit: Continuation.BufferingPolicy = .unbounded + ) -> (stream: AsyncStream, continuation: AsyncStream.Continuation) { + let storage: _Storage = .create(limit) + let stream = AsyncStream(storage: storage) + let continuation = Continuation(storage) + return (stream: stream, continuation: continuation) + } + + @_alwaysEmitIntoClient + init(storage: _Storage) { + self.context = _Context(storage: storage, produce: storage.next) + } +} + +extension AsyncThrowingStream { + /// Initializes a new ``AsyncThrowingStream`` and an ``AsyncThrowingStream/Continuation``. + /// + /// - Parameters: + /// - elementType: The element type of the sequence. + /// - failureType: The failure type of the stream. + /// - limit: The buffering policy that the stream should use. + /// - Returns: A tuple which contains the stream and its continuation. + @_alwaysEmitIntoClient + public static func makeStream( + of elementType: Element.Type = Element.self, + throwing failureType: Failure.Type = Failure.self, + bufferingPolicy limit: Continuation.BufferingPolicy = .unbounded + ) -> (stream: AsyncThrowingStream, continuation: AsyncThrowingStream.Continuation) { + let storage: _Storage = .create(limit) + let stream = AsyncThrowingStream(storage: storage) + let continuation = Continuation(storage) + return (stream: stream, continuation: continuation) + } + + @_alwaysEmitIntoClient + init(storage: _Storage) { + self.context = _Context(storage: storage, produce: storage.next) + } +} +``` + +## Source compatibility, Effect on ABI stability, Effect on API resilience + +As this is an additive change, it should not have any compatibility, stability or resilience problems. The only potential problem would be if someone has already run into this shortcoming and decided to define their own `makeStream` methods. + +## Alternatives considered + +### Return a concrete type instead of a tuple +My initial proposal was using a concrete type as the return paramter of the factory; +however, I walked back on it since back deployment issues were raised with introducing a new type. + +I still believe that there is value in providing a concrete type since +it is easier to handle than a tuple and documentation can be provided in a nice way. + +```swift +extension AsyncStream { + /// Simple struct for the return type of ``AsyncStream/makeStream(elementType:)``. + public struct NewStream { + /// The actual stream. + public let stream: AsyncStream + /// The continuation of the stream + public let continuation: AsyncStream.Continuation + + @inlinable + internal init( + stream: AsyncStream, + continuation: AsyncStream.Continuation + ) { + self.stream = stream + self.continuation = continuation + } + } +``` + +### Do nothing alternative +We could just leave the current creation of `Async[Throwing]Stream` as is; +however, since it is part of the standard library we should provide +a better method to create a stream and its continuation. \ No newline at end of file From 3f3aea93bb6a2cc7aafac9dceb0b9e8ad970886d Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Wed, 26 Oct 2022 11:56:22 +0100 Subject: [PATCH 2779/4563] expand Behavior changes for Swift 6 adn stdlib --- proposals/NNNN-opt-in-reflection-metadata.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index 94ad9fe54b..efaaf16b14 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -100,8 +100,18 @@ public func testIsReflectable(_ t: T) -> Bool { } ``` -### Behaviour change for Swift 6 -For Swift 6, we propose to enable Opt-In behaviour by default, to make the user experience consistent and safe. To achieve that we will need to deprecate the compiler's options that can lead to missing reflection - `-reflection-metadata-for-debugger-only` and `-disable-reflection-metadata`. Starting with Swift 6, these arguments will be ignored in favour of the default opt-in mode. +### Behavior change for Swift 6 +Starting with Swift 6, we propose to enable Opt-In mode by default, to make the user experience consistent and safe. However, if full reflection isn't enabled with a new flag (`-enable-full-reflection-metadata`), the emission of reflection metadata will be skipped for all types that don't conform to the `Reflectable` protocol. This may cause changes in the behavior of the code that wasn't audited to conform to Reflectable and uses reflection-consuming APIs. + +For instance, stdlib's APIs like `dump`, `debugPrint`, `String(describing:)` will be returning limited output. +Library authors will have to prepare their APIs for Swift 6 and introduce generic requirements on `Reflectable` in their APIs. + +We also propose to deprecate the compiler's options that can lead to missing reflection - `-reflection-metadata-for-debugger-only` and `-disable-reflection-metadata` and starting with Swift 6, ignore these arguments in favor of the default opt-in mode. + + +### Stdlib behavior changes +In Swift `Mirror(reflecting:)` is the only official way to access Reflection metadata, all other APIs are using it under the hood. We intentionally do not propose adding a Reflectable constraint on Mirror type, because it would impose restrictions on those developers who still don't want to require it. If the presence of reflection metadata is mandatory, the requirement on Reflectable protocol should be expressed in the signatures of calling functions. + ## Detailed design From 94c9decec740fd30d5316fe18c750f326eea854c Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Wed, 26 Oct 2022 16:50:33 +0100 Subject: [PATCH 2780/4563] use -g family of flag instead of -O to control reflection used for debugging --- proposals/NNNN-opt-in-reflection-metadata.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index efaaf16b14..6504d10484 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -115,9 +115,7 @@ In Swift `Mirror(reflecting:)` is the only official way to access Reflection met ## Detailed design -Since Reflection symbols might be used by the debugger, there will be difference in emitted Reflection symbols across Debug and Release modes. -**Release mode**: if `-O`, `-Osize`, `-Ospeed` passed. -**Debug**: - if `-Onone` passed or if not set. +Since Reflection metadata might be used by the debugger, we propose to always keep that metadata if full emission of debugging information is enabled (`-gdwarf-types` or `-g` flags). **Changes in flags** To handle behavior change between Swift pre-6 and 6, we can introduce a new upcoming feature, which will allow to enable Opt-In mode explicitly for pre-6 Swift with `-enable-upcoming-feature OptInReflection` and will set this mode by default in Swift 6. @@ -127,17 +125,17 @@ A new flag `-enable-full-reflection-metadata` will also have to be introduced to For Swift 6, flags `-disable-reflection-metadata` and `-emit-reflection-for-debugger` will be a no-op, to ensure the reflection metadata is always available when needed. 1. Reflection Disabled (`-disable-reflection-metadata` and `-reflection-metadata-for-debugger-only`) -- Do not emit reflection in Release and Debug modes for Swift pre-6. -- A no-op in Swift 6 and later. +- For Swift pre-6 emit Reflection metadata only if full debugging information is enabled. - If there is a type in a module conforming to `Reflectable`, the compiler will emit an error. +- A no-op in Swift 6 and later (Opt-in mode is enabled by default). 2. Opt-In Reflection (`-enable-upcoming-feature OptInReflection`) -- In Release mode, emit only for types that conform to `Reflectable`. -- In Debug mode emit reflection in full. +- If debugging is disabled, emit only for types that conform to `Reflectable`. +- Will emit reflection in full for all types if debugging is enabled. - For Swift pre-6 will require an explicit flag, for Swift 6 will be enabled by default. 3. Fully enabled (`-enable-full-reflection-metadata`) -- Emit reflection metadata for all types in Release and Debug modes. +- Always emit reflection metadata for all types regardless of debugging information level. - Conformance to Reflectable will be synthesized for all types to allow usage of reflection-consuming APIs. - Current default level for Swift pre-6. From 91dec07d89508fad767408af9e115326b2a58141 Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Wed, 26 Oct 2022 18:17:12 +0100 Subject: [PATCH 2781/4563] suggested fixes --- proposals/NNNN-opt-in-reflection-metadata.md | 43 ++++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index 6504d10484..0bbf7eaab1 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -16,20 +16,25 @@ Swift-evolution thread: [Discussion thread topic for that proposal](https://foru ## Motivation -There are two kinds of Swift metadata emitted be the compiler: +There are two kinds of Swift metadata emitted by the compiler: 1. Core Metadata (type metadata record, nominal type descriptor, etc). 2. Reflection metadata (reflection metadata field descriptor). -Core metadata must constantly be emitted and may only be stripped if provenly not used. (This metadata isn't affected by this proposal) -Reflection metadata contains optional information about declarations' fields - their names and references to their types. This metadata isn't used by the language's runtime features, and the emission may be skipped if such types aren't passed to reflection-consuming APIs. +Core metadata must constantly be emitted and may only be stripped if provenly not used. (This metadata isn't affected by this proposal.) +Reflection metadata contains optional information about declarations' fields - their names and references to their types. +This metadata isn't used by the language's runtime features, and the emission may be skipped if such types aren't passed to reflection-consuming APIs. -APIs can use Reflection Metadata differently. Some like `print`, and `dump` will still work with disabled reflection, but the output will be limited. Others, like SwiftUI, rely on it and won't work correctly if the reflection metadata is missing. +APIs can use Reflection Metadata differently. Some like `print`, and `dump` will still work with disabled reflection, but the output will be limited. +Others, like SwiftUI, rely on it and won't work correctly if the reflection metadata is missing. While the former can benefit as well, the main focus of this proposal is on the latter. -A developer can mistakenly turn off Reflection Metadata for a Swift module and won't be warned at compile-time if APIs that consume reflection are used by that module. An app with such a module won't behave as expected at runtime which may be challenging to notice and track down such bugs back to Reflection. For instance, SwiftUI implementation uses reflection metadata from user modules to trigger the re-rendering of the view hierarchy when a state has changed. If for some reason a user module was compiled with metadata generation disabled, changing the state won't trigger that behavior and will cause inconsistency between state and representation which will make such API less safe since it becomes a runtime issue rather than a compile-time one. +A developer can mistakenly turn off Reflection Metadata for a Swift module and won't be warned at compile-time if APIs that consume reflection are used by that module. An app with such a module won't behave as expected at runtime which may be challenging to notice and track down such bugs back to Reflection. +For instance, SwiftUI implementation uses reflection metadata from user modules to trigger the re-rendering of the view hierarchy when a state has changed. +If for some reason a user module was compiled with metadata generation disabled, changing the state won't trigger that behavior and will cause inconsistency between state and representation which will make such API less safe since it becomes a runtime issue rather than a compile-time one. -On the other hand, excessive Reflection metadata may be preserved in a binary even if not used, because there is currently no way to statically determine its usage. There was an attempt to limit the amount of unused reflection metadata by improving its stripability by the Dead Code Elimination LLVM pass, but in many cases, it’s still preserved in the binary because it’s referenced by Full Type Metadata which prevents Reflection Metadata from stripping. This unnecessarily increases the binary size and may affect privacy by storing more information about the code's semantics in a binary, which might be used for reverse-engineering. +On the other hand, excessive Reflection metadata may be preserved in a binary even if not used, because there is currently no way to statically determine its usage. There was an attempt to limit the amount of unused reflection metadata by improving its stripability by the Dead Code Elimination LLVM pass, but in many cases, it’s still preserved in the binary because it’s referenced by Full Type Metadata which prevents Reflection Metadata from stripping. +This unnecessarily increases the binary size and may simplify reverse-engineering. Introducing a static compilation check can help to solve both of mentioned issues by adding to the language a way to express the requirement to have Reflection metadata at runtime. @@ -38,7 +43,8 @@ Introducing a static compilation check can help to solve both of mentioned issue Teaching the Type-checker and IRGen to ensure Reflection metadata is preserved in a binary if reflection-consuming APIs are used, will help to move the issue from runtime to compile time. -To achieve that, a new marker protocol `Reflectable` will be introduced. Firstly, APIs developers will gain an opportunity to express a dependency on Reflection Metadata through a generic requirement of their functions, which will make such APIs safer. Secondly, during IRGen, the compiler will be able to selectively emit Reflection symbols for the types that explicitly conform to the `Reflectable` protocol, which will reduce the overhead from reflection symbols for cases when reflection is emitted but not consumed. +To achieve that, a new marker protocol `Reflectable` will be introduced. Firstly, APIs developers will gain an opportunity to express a dependency on Reflection Metadata through a generic requirement of their functions, which will make such APIs safer. +Secondly, during IRGen, the compiler will be able to selectively emit Reflection symbols for the types that explicitly conform to the `Reflectable` protocol, which will reduce the overhead from reflection symbols for cases when reflection is emitted but not consumed. ### Case Study 1: @@ -101,7 +107,10 @@ public func testIsReflectable(_ t: T) -> Bool { ``` ### Behavior change for Swift 6 -Starting with Swift 6, we propose to enable Opt-In mode by default, to make the user experience consistent and safe. However, if full reflection isn't enabled with a new flag (`-enable-full-reflection-metadata`), the emission of reflection metadata will be skipped for all types that don't conform to the `Reflectable` protocol. This may cause changes in the behavior of the code that wasn't audited to conform to Reflectable and uses reflection-consuming APIs. + +Starting with Swift 6, we propose to enable Opt-In mode by default, to make the user experience consistent and safe. +However, if full reflection isn't enabled with a new flag (`-enable-full-reflection-metadata`), the emission of reflection metadata will be skipped for all types that don't conform to the `Reflectable` protocol. +This may cause changes in the behavior of the code that wasn't audited to conform to Reflectable and uses reflection-consuming APIs. For instance, stdlib's APIs like `dump`, `debugPrint`, `String(describing:)` will be returning limited output. Library authors will have to prepare their APIs for Swift 6 and introduce generic requirements on `Reflectable` in their APIs. @@ -110,14 +119,17 @@ We also propose to deprecate the compiler's options that can lead to missing ref ### Stdlib behavior changes -In Swift `Mirror(reflecting:)` is the only official way to access Reflection metadata, all other APIs are using it under the hood. We intentionally do not propose adding a Reflectable constraint on Mirror type, because it would impose restrictions on those developers who still don't want to require it. If the presence of reflection metadata is mandatory, the requirement on Reflectable protocol should be expressed in the signatures of calling functions. + +In Swift `Mirror(reflecting:)` is the only official way to access Reflection metadata, all other APIs are using it under the hood. +We intentionally do not propose adding a Reflectable constraint on Mirror type, because it would impose restrictions on those developers who still don't want to require it. +If the presence of reflection metadata is mandatory, the requirement on Reflectable protocol should be expressed in the signatures of calling functions. ## Detailed design Since Reflection metadata might be used by the debugger, we propose to always keep that metadata if full emission of debugging information is enabled (`-gdwarf-types` or `-g` flags). -**Changes in flags** +### Changes in flags To handle behavior change between Swift pre-6 and 6, we can introduce a new upcoming feature, which will allow to enable Opt-In mode explicitly for pre-6 Swift with `-enable-upcoming-feature OptInReflection` and will set this mode by default in Swift 6. A new flag `-enable-full-reflection-metadata` will also have to be introduced to allow developers to enable reflection in full if they desire in Swift 6 and later. @@ -146,7 +158,9 @@ Casting might be a good way to improve the feature's ergonomics because currentl To implement this feature, we propose to introduce a new runtime function `swift_reflectableCast`, and emit a call to it instead of `swift_dynamicCast`during IRGen if Reflectable is a target type. -Because of the fact that the compiler emits a call to that function at compile-time, all casts must be statically visible. All other cases like implicit conversion to `Reflectable` must be banned. This could be done at CSSimplify, when a new conversion constraint is introduced between a type variable and `Reflectable` type, the compiler will emit an error. +Because of the fact that the compiler emits a call to that function at compile-time, all casts must be statically visible. +All other cases like implicit conversion to `Reflectable` must be banned. +This could be done at CSSimplify, when a new conversion constraint is introduced between a type variable and `Reflectable` type, the compiler will emit an error. ```swift func cast(_ x: U) -> T { @@ -163,7 +177,8 @@ Since reflectable casting will require a new runtime function, it should be gate ## Source compatibility -The change won’t break source compatibility in versions prior to Swift 6, because of the gating by the new flag. If as proposed, it’s enabled by default in Swift 6, the code with types that has not been audited to conform to the `Reflectable` protocol will fail to compile if used with APIs that consume the reflection metadata. +The change won’t break source compatibility in versions prior to Swift 6, because of the gating by the new flag. +If as proposed, it’s enabled by default in Swift 6, the code with types that has not been audited to conform to the `Reflectable` protocol will fail to compile if used with APIs that consume the reflection metadata. ## Effect on ABI stability @@ -176,6 +191,8 @@ This proposal has no effect on API resilience. ## Alternatives considered -Dead Code Elimination and linker optimisations were also considered as a way to reduce the amount of present Reflection metadata in release builds. The optimiser could use a conformance to a `Reflectable` protocol as a hint about what reflection metadata should be preserved. However, turned out it was quite challenging to statically determine all usages of Reflection metadata even with hints. +Dead Code Elimination and linker optimisations were also considered as a way to reduce the amount of present Reflection metadata in release builds. +The optimiser could use a conformance to a `Reflectable` protocol as a hint about what reflection metadata should be preserved. +However, turned out it was quite challenging to statically determine all usages of Reflection metadata even with hints. It was also considered to use an attribute `@reflectable` on nominal type declaration to express the requirement to have reflection metadata, however, a lot of logic had to be re-implemented outside of type-checker to ensure all guarantees are fulfilled. \ No newline at end of file From 218a1222d504ea5f4ca0190d9537725ef2058bc7 Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Wed, 26 Oct 2022 18:28:06 +0100 Subject: [PATCH 2782/4563] added acknowledgments section --- proposals/NNNN-opt-in-reflection-metadata.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index 0bbf7eaab1..be58cf8c53 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -130,6 +130,7 @@ If the presence of reflection metadata is mandatory, the requirement on Reflecta Since Reflection metadata might be used by the debugger, we propose to always keep that metadata if full emission of debugging information is enabled (`-gdwarf-types` or `-g` flags). ### Changes in flags + To handle behavior change between Swift pre-6 and 6, we can introduce a new upcoming feature, which will allow to enable Opt-In mode explicitly for pre-6 Swift with `-enable-upcoming-feature OptInReflection` and will set this mode by default in Swift 6. A new flag `-enable-full-reflection-metadata` will also have to be introduced to allow developers to enable reflection in full if they desire in Swift 6 and later. @@ -154,6 +155,7 @@ For Swift 6, flags `-disable-reflection-metadata` and `-emit-reflection-for-debu Introducing a new flag to control the feature will allow us to safely roll it out and avoid breakages of the existing code. For those modules that get compiled with fully enabled metadata, nothing will change (all symbols will stay present). For modules that have the metadata disabled, but are consumers of reflectable API, the compiler will emit the error enforcing the guarantee. ### Casts implementation + Casting might be a good way to improve the feature's ergonomics because currently there is no way to check if reflection is available at runtime. (`Mirror.children.count` doesn't really help because it doesn't distinguish between the absence of reflection metadata and the absence of fields on a type) To implement this feature, we propose to introduce a new runtime function `swift_reflectableCast`, and emit a call to it instead of `swift_dynamicCast`during IRGen if Reflectable is a target type. @@ -195,4 +197,9 @@ Dead Code Elimination and linker optimisations were also considered as a way to The optimiser could use a conformance to a `Reflectable` protocol as a hint about what reflection metadata should be preserved. However, turned out it was quite challenging to statically determine all usages of Reflection metadata even with hints. -It was also considered to use an attribute `@reflectable` on nominal type declaration to express the requirement to have reflection metadata, however, a lot of logic had to be re-implemented outside of type-checker to ensure all guarantees are fulfilled. \ No newline at end of file +It was also considered to use an attribute `@reflectable` on nominal type declaration to express the requirement to have reflection metadata, however, a lot of logic had to be re-implemented outside of type-checker to ensure all guarantees are fulfilled. + + +## Acknowledgments + +Thanks to [Joe Groff](https://github.com/jckarter) for various useful pieces of advice, general help along the journey, and for suggesting several useful features like Reflectable casts! \ No newline at end of file From 9e69a26f9a1d08cba31cdff407995619b3361bad Mon Sep 17 00:00:00 2001 From: Brandon Williams <135203+mbrandonw@users.noreply.github.com> Date: Wed, 26 Oct 2022 11:10:22 -0700 Subject: [PATCH 2783/4563] Update proposal from feedback. (#1825) --- proposals/0374-clock-sleep-for.md | 32 ++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/proposals/0374-clock-sleep-for.md b/proposals/0374-clock-sleep-for.md index 32a8d2d025..b25ef897c8 100644 --- a/proposals/0374-clock-sleep-for.md +++ b/proposals/0374-clock-sleep-for.md @@ -142,7 +142,9 @@ extension Clock { /// Suspends for the given duration. /// /// Prefer to use the `sleep(until:tolerance:)` method on `Clock` if you have access to an - /// absolute instant. + /// absolute instant. + @available(SwiftStdlib 5.7, *) + @_alwaysEmitIntoClient public func sleep( for duration: Duration, tolerance: Duration? = nil @@ -152,17 +154,37 @@ extension Clock { } ``` - - This will allow one to sleep for a duration with a clock rather than sleeping until an instant. +Further, to make the APIs between `clock.sleep` and `Task.sleep` similar, we will also add a `tolerance` argument to `Task.sleep(for:)`: + +```swift +/// Suspends the current task for the given duration on a continuous clock. +/// +/// If the task is cancelled before the time ends, this function throws +/// `CancellationError`. +/// +/// This function doesn't block the underlying thread. +/// +/// try await Task.sleep(for: .seconds(3)) +/// +/// - Parameter duration: The duration to wait. +@available(SwiftStdlib 5.7, *) +@_alwaysEmitIntoClient +public static func sleep( + for duration: Duration, + tolerance: C.Instant.Duration? = nil +) async throws { + try await sleep(until: .now + duration, tolerance: tolerance, clock: .continuous) +} +``` + ## Detailed design ## Source compatibility, effect on ABI stability, effect on API resilience As this is an additive change, it should not have any compatibility, stability or resilience -problems. The only potential problem would be if someone has already run into this shortcoming -and decided to define their own `sleep(for:)` method on clocks. +problems. ## Alternatives considered From 42e5b3b185247010a3e6fb6f4c6db93e4f49d31e Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 26 Oct 2022 14:16:40 -0400 Subject: [PATCH 2784/4563] Update 0374-clock-sleep-for.md (#1826) Keep specific availability and aeic annotations out of the proposal, as they're outside the purview of SE. --- proposals/0374-clock-sleep-for.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/proposals/0374-clock-sleep-for.md b/proposals/0374-clock-sleep-for.md index b25ef897c8..367f97a513 100644 --- a/proposals/0374-clock-sleep-for.md +++ b/proposals/0374-clock-sleep-for.md @@ -143,8 +143,6 @@ extension Clock { /// /// Prefer to use the `sleep(until:tolerance:)` method on `Clock` if you have access to an /// absolute instant. - @available(SwiftStdlib 5.7, *) - @_alwaysEmitIntoClient public func sleep( for duration: Duration, tolerance: Duration? = nil @@ -169,8 +167,6 @@ Further, to make the APIs between `clock.sleep` and `Task.sleep` similar, we wil /// try await Task.sleep(for: .seconds(3)) /// /// - Parameter duration: The duration to wait. -@available(SwiftStdlib 5.7, *) -@_alwaysEmitIntoClient public static func sleep( for duration: Duration, tolerance: C.Instant.Duration? = nil From 88ff07ec3b4d04f8f025573347f0f2df8488aaa4 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 26 Oct 2022 14:18:04 -0400 Subject: [PATCH 2785/4563] Update 0374-clock-sleep-for.md (#1827) --- proposals/0374-clock-sleep-for.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0374-clock-sleep-for.md b/proposals/0374-clock-sleep-for.md index 367f97a513..a4a64d54cf 100644 --- a/proposals/0374-clock-sleep-for.md +++ b/proposals/0374-clock-sleep-for.md @@ -3,11 +3,10 @@ * Proposal: [SE-0374](0374-clock-sleep-for.md) * Authors: [Brandon Williams](https://github.com/mbrandonw), [Stephen Celis](https://github.com/stephencelis) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active Review (Oct 11 ... 25, 2022)** +* Status: **Active Review (Oct 11 ... Nov 1, 2022)** * Implementation: [apple/swift#61222](https://github.com/apple/swift/pull/61222) * Review: ([pitch](https://forums.swift.org/t/pitch-clock-sleep-for/60376)) ([review](https://forums.swift.org/t/se-0374-add-sleep-for-to-clock/60787)) - ## Introduction The `Clock` protocol introduced in Swift 5.7 provides a way to suspend until a future instant, but From 8ded25b0e2eb2126c0452906fc29a27662861b34 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 26 Oct 2022 16:01:58 -0400 Subject: [PATCH 2786/4563] Accept SE-0373 --- proposals/0373-vars-without-limits-in-result-builders.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0373-vars-without-limits-in-result-builders.md b/proposals/0373-vars-without-limits-in-result-builders.md index 346135e82f..61594706b5 100644 --- a/proposals/0373-vars-without-limits-in-result-builders.md +++ b/proposals/0373-vars-without-limits-in-result-builders.md @@ -3,9 +3,9 @@ * Proposal: [SE-0373](0373-vars-without-limits-in-result-builders.md) * Author: [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (September 30...October 10th, 2022)** +* Status: **Accepted** * Implementation: [apple/swift#60839](https://github.com/apple/swift/pull/60839) -* Review: ([pitch](https://forums.swift.org/t/pitch-lift-all-limitations-on-variables-in-result-builders/60460)) ([review](https://forums.swift.org/t/se-0373-lift-all-limitations-on-variables-in-result-builders/60592)) +* Review: ([pitch](https://forums.swift.org/t/pitch-lift-all-limitations-on-variables-in-result-builders/60460)) ([review](https://forums.swift.org/t/se-0373-lift-all-limitations-on-variables-in-result-builders/60592)) ([acceptance](https://forums.swift.org/t/accepted-se-0373-lift-all-limitations-on-variables-in-result-builders/61041)) ## Introduction From 476eefc3b0426a901c4f736e0c476246e94a667e Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 26 Oct 2022 19:02:57 -0400 Subject: [PATCH 2787/4563] Accept SE-0375 (#1830) Also roll in a typo correction noted during review and delete an empty section --- proposals/0375-opening-existential-optional.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/proposals/0375-opening-existential-optional.md b/proposals/0375-opening-existential-optional.md index 07d8823570..97b6fc46ee 100644 --- a/proposals/0375-opening-existential-optional.md +++ b/proposals/0375-opening-existential-optional.md @@ -3,9 +3,9 @@ * Proposal: [SE-0375](0375-opening-existential-optional.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active Review (October 11...25, 2022)** +* Status: **Accepted** * Implementation: [apple/swift#61321](https://github.com/apple/swift/pull/61321) -* Review: ([pitch](https://forums.swift.org/t/mini-pitch-for-se-0352-amendment-allow-opening-an-existential-argument-to-an-optional-parameter/60501)) ([review](https://forums.swift.org/t/se-0375-opening-existential-arguments-to-optional-parameters/60802)) +* Review: ([pitch](https://forums.swift.org/t/mini-pitch-for-se-0352-amendment-allow-opening-an-existential-argument-to-an-optional-parameter/60501)) ([review](https://forums.swift.org/t/se-0375-opening-existential-arguments-to-optional-parameters/60802)) ([acceptance](https://forums.swift.org/t/accepted-se-0375-opening-existential-arguments-to-optional-parameters/61045)) ## Introduction @@ -55,7 +55,7 @@ func testOpenToOptional(p: any P) { ## Source compatibility -Generally speaking, opening an existential argument in one more case will make code that would have been rejected by the compiler (e.g., with an error like "`P` does not conform to `P`") into code that is accepted, because the existential is opened. This can change the behavior of overload resulting, in the same manner as was [discussed in SE-0352](https://github.com/apple/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md#source-compatibility). Experience with SE-0352's integration into Swift 5.7 implies that the practical effect of these changes is quite small. +Generally speaking, opening an existential argument in one more case will make code that would have been rejected by the compiler (e.g., with an error like "`P` does not conform to `P`") into code that is accepted, because the existential is opened. This can change the behavior of overload resolution, in the same manner as was [discussed in SE-0352](https://github.com/apple/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md#source-compatibility). Experience with SE-0352's integration into Swift 5.7 implies that the practical effect of these changes is quite small. ## Effect on ABI stability @@ -64,8 +64,3 @@ This proposal changes the type system but has no ABI impact whatsoever. ## Effect on API resilience This proposal changes the use of APIs, but not the APIs themselves, so it doesn't impact API resilience per se. - -## Alternatives considered - -Describe alternative approaches to addressing the same problem, and -why you chose this approach instead. From 8772b7a52376be32871ee35afc2f556f4409b56c Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 27 Oct 2022 10:36:45 +0100 Subject: [PATCH 2788/4563] SE-0337: fix broken `@available` declaration `@available(unavailable, *)` as currently stated in the proposal won't compile, the correct order of arguments to this attribute is `@available(*, unavailable)`. --- ...337-support-incremental-migration-to-concurrency-checking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0337-support-incremental-migration-to-concurrency-checking.md b/proposals/0337-support-incremental-migration-to-concurrency-checking.md index 0373e8171b..7eb12f0c81 100644 --- a/proposals/0337-support-incremental-migration-to-concurrency-checking.md +++ b/proposals/0337-support-incremental-migration-to-concurrency-checking.md @@ -192,7 +192,7 @@ A type can be described as having one of the following three `Sendable` conforma A type can be made explicitly non-`Sendable` by creating an unavailable conformance to `Sendable`, e.g., ```swift -@available(unavailable, *) +@available(*, unavailable) extension Point: Sendable { } ``` From 03ac8971e7cf2e360489f7de143892d163d1275b Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 27 Oct 2022 18:29:22 +0100 Subject: [PATCH 2789/4563] [SE-0374] Update `Task.sleep(for:tolerance:)` API (#1829) --- proposals/0374-clock-sleep-for.md | 38 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/proposals/0374-clock-sleep-for.md b/proposals/0374-clock-sleep-for.md index a4a64d54cf..f6a120f85a 100644 --- a/proposals/0374-clock-sleep-for.md +++ b/proposals/0374-clock-sleep-for.md @@ -3,7 +3,7 @@ * Proposal: [SE-0374](0374-clock-sleep-for.md) * Authors: [Brandon Williams](https://github.com/mbrandonw), [Stephen Celis](https://github.com/stephencelis) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active Review (Oct 11 ... Nov 1, 2022)** +* Status: **Active Review (October 11 ... November 1, 2022)** * Implementation: [apple/swift#61222](https://github.com/apple/swift/pull/61222) * Review: ([pitch](https://forums.swift.org/t/pitch-clock-sleep-for/60376)) ([review](https://forums.swift.org/t/se-0374-add-sleep-for-to-clock/60787)) @@ -20,8 +20,6 @@ that deals with instants is inaccessible to an existential. This means one canno `sleep(until:)` on an existential clock, and hence you can't really do anything with an existential clock. -Swift-evolution thread: https://forums.swift.org/t/pitch-clock-sleep-for/60376 - ## Motivation Existentials provide a convenient way to inject dependencies into features so that you can use one @@ -156,26 +154,26 @@ This will allow one to sleep for a duration with a clock rather than sleeping un Further, to make the APIs between `clock.sleep` and `Task.sleep` similar, we will also add a `tolerance` argument to `Task.sleep(for:)`: ```swift -/// Suspends the current task for the given duration on a continuous clock. -/// -/// If the task is cancelled before the time ends, this function throws -/// `CancellationError`. -/// -/// This function doesn't block the underlying thread. -/// -/// try await Task.sleep(for: .seconds(3)) -/// -/// - Parameter duration: The duration to wait. -public static func sleep( - for duration: Duration, - tolerance: C.Instant.Duration? = nil -) async throws { - try await sleep(until: .now + duration, tolerance: tolerance, clock: .continuous) +extension Task where Success == Never, Failure == Never { + /// Suspends the current task for the given duration on a continuous clock. + /// + /// If the task is cancelled before the time ends, this function throws + /// `CancellationError`. + /// + /// This function doesn't block the underlying thread. + /// + /// try await Task.sleep(for: .seconds(3)) + /// + /// - Parameter duration: The duration to wait. + public static func sleep( + for duration: Duration, + tolerance: Duration? = nil + ) async throws { + try await sleep(until: .now + duration, tolerance: tolerance, clock: .continuous) + } } ``` -## Detailed design - ## Source compatibility, effect on ABI stability, effect on API resilience As this is an additive change, it should not have any compatibility, stability or resilience From 644798165cf04084823b8ead8db52f3b3dd03a0d Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Thu, 27 Oct 2022 13:42:57 -0500 Subject: [PATCH 2790/4563] Fix the flag name for enabling bare slash literals --- proposals/0354-regex-literals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0354-regex-literals.md b/proposals/0354-regex-literals.md index 279359cf49..2390aea912 100644 --- a/proposals/0354-regex-literals.md +++ b/proposals/0354-regex-literals.md @@ -408,7 +408,7 @@ As explored above, the parsing of `/.../` does have potential to break source in However we expect these cases will be uncommon, and can be disambiguated with parentheses or closures if needed. -To accommodate the cases where source may be broken, `/.../` regex literals will be introduced in Swift 6 mode. However, projects may adopt the syntax earlier by passing the compiler flag `-enable-bare-regex-syntax`. Note this does not affect the extended delimiter syntax `#/.../#`, which will be usable immediately. +To accommodate the cases where source may be broken, `/.../` regex literals will be introduced in Swift 6 mode. However, projects may adopt the syntax earlier by passing the compiler flag `-enable-bare-slash-regex`. Note this does not affect the extended delimiter syntax `#/.../#`, which will be usable immediately. ## Future Directions From 7af91127d693bffcb01aa87978d75d5a3170c4d1 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 27 Oct 2022 21:38:50 +0100 Subject: [PATCH 2791/4563] [SE-0366] Fix "missing or invalid dates" warning (#1833) --- proposals/0366-move-function.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0366-move-function.md b/proposals/0366-move-function.md index 0cc7de846d..645357a748 100644 --- a/proposals/0366-move-function.md +++ b/proposals/0366-move-function.md @@ -3,9 +3,9 @@ * Proposal: [SE-0366](0366-move-function.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Joe Groff](https://github.com/jckarter) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (Oct 25 - Nov 8, 2022)** +* Status: **Active Review (October 25 - November 8, 2022)** * Implementation: Implemented on main as stdlib SPI (`_move` instead of `take` keyword) -* Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic)) ([review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0366-move-operation-use-after-move-diagnostic/59687)) +* Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic/53983)) ([first review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0366-move-operation-use-after-move-diagnostic/59687)) ([second review](https://forums.swift.org/t/se-0366-second-review-take-operator-to-end-the-lifetime-of-a-variable-binding/61021)) * Previous Revisions: * [1](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md) * [2](https://github.com/apple/swift-evolution/blob/43849aa9ae3e87c434866c5a5e389af67537ca26/proposals/0366-move-function.md) From 6ac60259221efccfac20e2197a12827191f73ae9 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 27 Oct 2022 21:49:15 +0100 Subject: [PATCH 2792/4563] [SE-0376] Fix "missing or invalid dates" warning --- proposals/0376-function-back-deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index 70a04ebfd0..33f9718909 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -5,7 +5,7 @@ * Implementation: [apple/swift#41271](https://github.com/apple/swift/pull/41271), [apple/swift#41348](https://github.com/apple/swift/pull/41348), [apple/swift#41416](https://github.com/apple/swift/pull/41416), [apple/swift#41612](https://github.com/apple/swift/pull/41612) as the underscored attribute `@_backDeploy` * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) * Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) -* Status: **Active review (Oct 25...Nov 7, 2022)** +* Status: **Active review (October 25 ... November 7, 2022)** ## Introduction From 1402adf3f1e7d6f4767166f43b14b5e1f9beb578 Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Fri, 28 Oct 2022 16:04:31 +0100 Subject: [PATCH 2793/4563] mention a possiblity to back-deploy reflectable casts --- proposals/NNNN-opt-in-reflection-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index be58cf8c53..358cce0bd3 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -174,7 +174,7 @@ let b: Reflectable = cast(1) // expression can't be implicitly converted to Refl Some diagnostics and optimizations will also have to be disabled even if conformance is statically visible to the compiler because all casts will have to go through the runtime call. **Availability checks** -Since reflectable casting will require a new runtime function, it should be gated by availability checks. If a deployment target is lower than supported, an error will be emitted. +Since reflectable casting will require a new runtime function, it should be gated by availability checks. If a deployment target is lower than supported, an error will be emitted. However, it might be possible to embed new runtime functions into a compatibility library for back deployment. ## Source compatibility From a556b6c9a0d89ddb52f4643e1b22a5ce47df09f9 Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Fri, 28 Oct 2022 16:28:32 +0100 Subject: [PATCH 2794/4563] Extensions can't declare conformance to Reflectable and transitive conformance is allowed --- proposals/NNNN-opt-in-reflection-metadata.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index 358cce0bd3..ca36744edd 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -127,6 +127,22 @@ If the presence of reflection metadata is mandatory, the requirement on Reflecta ## Detailed design +To decide when to emit reflection metadata IRGen will check the conformance of a type to the `Reflectable` protocol and if the type conforms, IRGen will emit reflection. + +Conformance to Reflectable should be only allowed at type declarations level, to avoid confusing behavior, when a developer adds conformance on an imported from another module type that doesn't have reflection enabled. + +Transitive conformance to Reflectable should be allowed to give API authors an opportunity to hide reflection logic from APIs users as implementation details. + +```swift +// Library +public protocol Foo: Reflectable {} +public func consume(_ t: T) {} + +// User +struct Bar: Foo {} // Reflection is emitted +consume(Bar()) +``` + Since Reflection metadata might be used by the debugger, we propose to always keep that metadata if full emission of debugging information is enabled (`-gdwarf-types` or `-g` flags). ### Changes in flags From ca68ce8e70b0f2eac18e5bb9c975ec66e3bb052c Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Fri, 28 Oct 2022 16:33:54 +0100 Subject: [PATCH 2795/4563] add title for debugging section --- proposals/NNNN-opt-in-reflection-metadata.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index ca36744edd..5a50f4349e 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -143,7 +143,9 @@ struct Bar: Foo {} // Reflection is emitted consume(Bar()) ``` -Since Reflection metadata might be used by the debugger, we propose to always keep that metadata if full emission of debugging information is enabled (`-gdwarf-types` or `-g` flags). +### Changes for debugging + +Since Reflection metadata might be used by the debugger, we propose to always keep that metadata if full emission of debugging information is enabled (with`-gdwarf-types` or `-g` flags). ### Changes in flags From 9b5f9e21f5fb58a51b61da5c50cf0802fbcd7a05 Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Mon, 31 Oct 2022 19:54:59 +0000 Subject: [PATCH 2796/4563] added Future directions section --- proposals/NNNN-opt-in-reflection-metadata.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index 5a50f4349e..8f95cc728d 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -217,6 +217,10 @@ However, turned out it was quite challenging to statically determine all usages It was also considered to use an attribute `@reflectable` on nominal type declaration to express the requirement to have reflection metadata, however, a lot of logic had to be re-implemented outside of type-checker to ensure all guarantees are fulfilled. +## Future directions + +Currently, there is only one kind of Reflection Metadata - Field Descriptor Metadata. In the future, it is possible that other kinds will be added (e.g methods, computed properties, etc) `Reflectable` should be able to cover all of them. + ## Acknowledgments From ec7dae5c18d3a3d3168e57340f7e86e3317b8df5 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Mon, 31 Oct 2022 19:35:56 -0700 Subject: [PATCH 2797/4563] Mark SE-0365 as implemented in Swift 5.8 (#1836) --- proposals/0365-implicit-self-weak-capture.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/0365-implicit-self-weak-capture.md b/proposals/0365-implicit-self-weak-capture.md index 6a90111521..a71d51d2c7 100644 --- a/proposals/0365-implicit-self-weak-capture.md +++ b/proposals/0365-implicit-self-weak-capture.md @@ -3,8 +3,8 @@ * Proposal: [SE-0365](0365-implicit-self-weak-capture.md) * Author: [Cal Stephens](https://github.com/calda) * Review Manager: [Saleem Abdulrasool](https://github.com/compnerd) -* Status: **Accepted (2022-08-08)** -* Implementation: [apple/swift#40702](https://github.com/apple/swift/pull/40702) +* Status: **Implemented (Swift 5.8)** +* Implementation: [apple/swift#40702](https://github.com/apple/swift/pull/40702), [apple/swift#61520](https://github.com/apple/swift/pull/61520) ## Introduction @@ -227,3 +227,5 @@ That would effectively add implicit control flow, however. `dismiss()` would onl Thanks to the authors of [SE-0269](https://github.com/apple/swift-evolution/blob/main/proposals/0269-implicit-self-explicit-capture.md) for laying the foundation for this proposal. Thanks to Kyle Sluder for [the suggestion](https://forums.swift.org/t/allow-implicit-self-for-weak-self-captures-after-self-is-unwrapped/54262/2) to not permit implicit `self` in cases where the unwrapped `self` value doesn't necessarily refer to the closure's `self` capture, like in `let self = self ?? C.global`. + +Many thanks to Pavel Yaskevich, John McCall, and Xiaodi Wu for providing significant feedback and advice regarding the implementation of this proposal. \ No newline at end of file From 98f8ae3e67b337bedca30dbe44bed6d431ea8b74 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 1 Nov 2022 21:19:48 +0000 Subject: [PATCH 2798/4563] [SE-0377] Fix "missing or invalid dates" warning (#1835) --- .../0377-parameter-ownership-modifiers.md | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index d20c39bf6a..bfb4854fd0 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -3,19 +3,9 @@ * Proposal: [SE-0377](0377-parameter-ownership-modifiers.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (Oct 25 - Nov 8, 2022)** +* Status: **Active Review (October 25 - November 8, 2022)** * Implementation: available using the internal names `__shared` and `__owned` -* Pitch v1: [https://github.com/gottesmm/swift-evolution/blob/consuming-nonconsuming-pitch-v1/proposals/000b-consuming-nonconsuming.md](https://github.com/gottesmm/swift-evolution/blob/consuming-nonconsuming-pitch-v1/proposals/000b-consuming-nonconsuming.md) - - +* Review: ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ## Introduction @@ -26,11 +16,6 @@ the number of ARC calls or copies needed to call a function, and provides a necessary prerequisite feature for move-only types to specify whether a function consumes a move-only value or not. -Pitch threads: - -- First pitch thread: [https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers) -- Second pitch thread: [https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581) - ## Motivation Swift uses automatic reference counting to manage the lifetimes of reference- From 7275aae2f4294d34da966c2fcd9cfdecb5d2895b Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Tue, 1 Nov 2022 18:22:59 -0700 Subject: [PATCH 2799/4563] [SE-0376] Update proposal to permit functions to have @backDeploy and @inlinable simultaneously. --- proposals/0376-function-back-deployment.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index 33f9718909..acdec169d8 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -138,7 +138,8 @@ There are rules that limit which declarations may have a `@backDeploy` attribute * Only functions that can be invoked with static dispatch are eligible to back deploy, so back deployed instance and class methods must be `final`. The `@objc` attribute also implies dynamic dispatch and therefore is incompatible with `@backDeploy`. * Explicit availability must be specified with `@available` on the same declaration for each of the platforms that the declaration is back deployed on. * The declaration should be available earlier than the platform versions specified in `@backDeploy` (otherwise the fallback functions would never be called). -* The `@_alwaysEmitIntoClient` and `@_transparent` attributes are incompatible with `@backDeploy` because they require that the function body to always be emitted into the client, defeating the purpose of `@backDeploy`. Declarations with `@inlinable` are also restricted from using `@backDeploy` since inlining behavior is dictated by the optimizer and use of the library function when it is available could be inconsistent as a result. +* The `@_alwaysEmitIntoClient` and `@_transparent` attributes are incompatible with `@backDeploy` because they require the function body to always be emitted into the client, defeating the purpose of `@backDeploy`. +* Declarations with `@inlinable` _may_ use `@backDeploy`. As usual with `@inlinable`, the bodies of these functions may be emitted into the client at the discretion of the optimizer. The copy of the function in the client may therefore be used even when a copy of the function is available in the library. ### Requirements for the bodies of back deployed functions From 7274e90a9a60d2a453af20c223137776619f1673 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 7 Nov 2022 16:38:44 -0800 Subject: [PATCH 2800/4563] Fix the spelling of Celsius --- proposals/0376-function-back-deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index acdec169d8..05ca80f327 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -83,7 +83,7 @@ extension Temperature { @available(toasterOS 1.0, ovenOS 1.0, *) @backDeploy(before: toasterOS 2.0, ovenOS 2.0) public var degreesFahrenheit: Double { - return (degreesCelcius * 9 / 5) + 32 + return (degreesCelsius * 9 / 5) + 32 } } From 33a8b3a6f31cfe874f56dd03a4e5b645d855c373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=80=E5=8D=93=E7=96=8C?= <55120045+WowbaggersLiquidLunch@users.noreply.github.com> Date: Wed, 9 Nov 2022 16:06:38 -0500 Subject: [PATCH 2801/4563] =?UTF-8?q?fix=20grammar=20"potentially"=20?= =?UTF-8?q?=E2=86=92=20"potential"=20(#1843)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proposals/0376-function-back-deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index 05ca80f327..03e98285e3 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -72,7 +72,7 @@ extension Toaster { } ``` -The API is now available on toasterOS 1.0 and later so clients may now reference `makeBatchOfToast(_:)` unconditionally. The compiler detects applications of `makeBatchOfToast(_:)` and generates code to automatically handle the potentially runtime unavailability of the API. +The API is now available on toasterOS 1.0 and later so clients may now reference `makeBatchOfToast(_:)` unconditionally. The compiler detects applications of `makeBatchOfToast(_:)` and generates code to automatically handle the potential runtime unavailability of the API. ## Detailed design From f5b786071e78e881a570d9d6ec0eb9962ac22a88 Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Sun, 13 Nov 2022 21:47:44 +0000 Subject: [PATCH 2802/4563] mentioned consistency between release and debug modes + various minor fixes --- proposals/NNNN-opt-in-reflection-metadata.md | 57 +++++++++++++------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/NNNN-opt-in-reflection-metadata.md index 8f95cc728d..5605efc9ce 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/NNNN-opt-in-reflection-metadata.md @@ -9,31 +9,46 @@ ## Introduction -This proposal seeks to increase the safety, efficiency and privacy of Swift Reflection Metadata by improving the existing mechanism and providing the opportunity to express a requirement on Reflection Metadata in APIs that consume it. +This proposal seeks to increase the safety, efficiency, and secrecy of Swift Reflection Metadata by improving the existing mechanism and providing the opportunity to express a requirement on Reflection Metadata in APIs that consume it. Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/pitch-3-opt-in-reflection-metadata/58852) ## Motivation -There are two kinds of Swift metadata emitted by the compiler: +There are two main kinds of Swift metadata emitted by the compiler: -1. Core Metadata (type metadata record, nominal type descriptor, etc). -2. Reflection metadata (reflection metadata field descriptor). +1. Core Metadata (type metadata records, nominal type descriptors, etc). +2. Reflection metadata (reflection metadata field descriptors). -Core metadata must constantly be emitted and may only be stripped if provenly not used. (This metadata isn't affected by this proposal.) -Reflection metadata contains optional information about declarations' fields - their names and references to their types. -This metadata isn't used by the language's runtime features, and the emission may be skipped if such types aren't passed to reflection-consuming APIs. +Core metadata must constantly be emitted and may only be stripped if provenly not used. (This kind of metadata isn't affected by this proposal.) +On the other hand, reflection metadata contains optional information about declaration fields - their names and references to their types. +The language's runtime features don't use this metadata, and the emission may be skipped if such types aren't passed to reflection-consuming APIs. -APIs can use Reflection Metadata differently. Some like `print`, and `dump` will still work with disabled reflection, but the output will be limited. + +Currently, there is no way to selectively enable the emission of reflectable metadata for a type or understand if an API consumes reflection metadata under the hood. +Moreover, compiler's flags exist that allow to completely disable emission. + +A developer has two ways right now - either +1. To just in case enable Reflection in full. +2. To try to guess which used APIs consume Reflection, and enable it only for modules that are users of such APIs. + +Both of those options have flaws. The first one leads to exsessive contribution of reflection metadta to binary size and might affects the secrecy of generated code. +The second one isn't safe because many APIs are black boxes if the guess is wrong, an app might behave not as expected at runtime. + +Furthermore, APIs can use Reflection Metadata differently. Some like `print`, `debugPrint`, and `dump` will still work with disabled reflection, but the output will be limited. Others, like SwiftUI, rely on it and won't work correctly if the reflection metadata is missing. While the former can benefit as well, the main focus of this proposal is on the latter. -A developer can mistakenly turn off Reflection Metadata for a Swift module and won't be warned at compile-time if APIs that consume reflection are used by that module. An app with such a module won't behave as expected at runtime which may be challenging to notice and track down such bugs back to Reflection. +A developer can mistakenly turn off Reflection Metadata for a Swift module and won't be warned at compile-time if APIs that consume reflection are used by that module. +An app with such a module won't behave as expected at runtime which may be challenging to notice and track down such bugs back to Reflection. For instance, SwiftUI implementation uses reflection metadata from user modules to trigger the re-rendering of the view hierarchy when a state has changed. -If for some reason a user module was compiled with metadata generation disabled, changing the state won't trigger that behavior and will cause inconsistency between state and representation which will make such API less safe since it becomes a runtime issue rather than a compile-time one. +If for some reason a user module was compiled with metadata generation disabled, changing the state won't trigger that behavior and will cause inconsistency +between state and representation which will make such API less safe since it becomes a runtime issue rather than a compile-time one. -On the other hand, excessive Reflection metadata may be preserved in a binary even if not used, because there is currently no way to statically determine its usage. There was an attempt to limit the amount of unused reflection metadata by improving its stripability by the Dead Code Elimination LLVM pass, but in many cases, it’s still preserved in the binary because it’s referenced by Full Type Metadata which prevents Reflection Metadata from stripping. +On the other hand, excessive Reflection metadata may be preserved in a binary even if not used, because there is currently no way to statically determine its usage. +There was an attempt to limit the amount of unused reflection metadata by improving its stripability by the Dead Code Elimination LLVM pass, but in many cases, +it’s still preserved in the binary because it’s referenced by Full Type Metadata which prevents Reflection Metadata from stripping. This unnecessarily increases the binary size and may simplify reverse-engineering. Introducing a static compilation check can help to solve both of mentioned issues by adding to the language a way to express the requirement to have Reflection metadata at runtime. @@ -41,14 +56,14 @@ Introducing a static compilation check can help to solve both of mentioned issue ## Proposed solution -Teaching the Type-checker and IRGen to ensure Reflection metadata is preserved in a binary if reflection-consuming APIs are used, will help to move the issue from runtime to compile time. +Teaching the Type-checker and IRGen to ensure Reflection metadata is preserved in a binary if reflection-consuming APIs are used, will help to move the problem from runtime to compile time. To achieve that, a new marker protocol `Reflectable` will be introduced. Firstly, APIs developers will gain an opportunity to express a dependency on Reflection Metadata through a generic requirement of their functions, which will make such APIs safer. Secondly, during IRGen, the compiler will be able to selectively emit Reflection symbols for the types that explicitly conform to the `Reflectable` protocol, which will reduce the overhead from reflection symbols for cases when reflection is emitted but not consumed. ### Case Study 1: -SwiftUI Framework: +SwiftUI: ```swift protocol SwiftUI.View: Reflectable {} class NSHostingView where Content : View { @@ -121,13 +136,13 @@ We also propose to deprecate the compiler's options that can lead to missing ref ### Stdlib behavior changes In Swift `Mirror(reflecting:)` is the only official way to access Reflection metadata, all other APIs are using it under the hood. -We intentionally do not propose adding a Reflectable constraint on Mirror type, because it would impose restrictions on those developers who still don't want to require it. +We intentionally do not propose adding a Reflectable constraint on Mirror type, because it would impose restrictions on those developers who still don't want to require it and consume Reflection optionally. If the presence of reflection metadata is mandatory, the requirement on Reflectable protocol should be expressed in the signatures of calling functions. ## Detailed design -To decide when to emit reflection metadata IRGen will check the conformance of a type to the `Reflectable` protocol and if the type conforms, IRGen will emit reflection. +To decide when to emit reflection metadata IRGen will check the conformance of a type to the `Reflectable` protocol and if the type conforms, IRGen will emit reflection symbols. Conformance to Reflectable should be only allowed at type declarations level, to avoid confusing behavior, when a developer adds conformance on an imported from another module type that doesn't have reflection enabled. @@ -145,11 +160,15 @@ consume(Bar()) ### Changes for debugging -Since Reflection metadata might be used by the debugger, we propose to always keep that metadata if full emission of debugging information is enabled (with`-gdwarf-types` or `-g` flags). +Since Reflection metadata might be used by the debugger, we propose to always keep that metadata +if full emission of debugging information is enabled (with `-gdwarf-types` or `-g` flags). +However, such Reflection metadata won't be accessible through the nominal type descriptor +which will allow to avoid inconsistencies between APIs' outputs in Release and Debug modes. ### Changes in flags -To handle behavior change between Swift pre-6 and 6, we can introduce a new upcoming feature, which will allow to enable Opt-In mode explicitly for pre-6 Swift with `-enable-upcoming-feature OptInReflection` and will set this mode by default in Swift 6. +To handle behavior change between Swift pre-6 and 6, we can introduce a new upcoming feature, +which will allow to enable Opt-In mode explicitly for Swift pre-6 with `-enable-upcoming-feature OptInReflection` and will set this mode by default in Swift 6. A new flag `-enable-full-reflection-metadata` will also have to be introduced to allow developers to enable reflection in full if they desire in Swift 6 and later. @@ -220,8 +239,8 @@ It was also considered to use an attribute `@reflectable` on nominal type declar ## Future directions Currently, there is only one kind of Reflection Metadata - Field Descriptor Metadata. In the future, it is possible that other kinds will be added (e.g methods, computed properties, etc) `Reflectable` should be able to cover all of them. - +If this proposal is approved, it will become easier and more native to migrate Codable to the usage of Reflection metadata for encoding/decoding logic instead of autogenerating code at compile time. ## Acknowledgments -Thanks to [Joe Groff](https://github.com/jckarter) for various useful pieces of advice, general help along the journey, and for suggesting several useful features like Reflectable casts! \ No newline at end of file +Thanks to [Joe Groff](https://github.com/jckarter) for various useful pieces of advice, general help along the way, and for suggesting several useful features like Reflectable casts! \ No newline at end of file From 8f79e41f8e9ec39f2e188847ebfe7a9ea41035b2 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Mon, 14 Nov 2022 13:38:41 -0800 Subject: [PATCH 2803/4563] SE-0378: Swift Package Registry Authentication (#1820) --- proposals/0378-package-registry-auth.md | 403 ++++++++++++++++++++++++ 1 file changed, 403 insertions(+) create mode 100644 proposals/0378-package-registry-auth.md diff --git a/proposals/0378-package-registry-auth.md b/proposals/0378-package-registry-auth.md new file mode 100644 index 0000000000..4b4a557a43 --- /dev/null +++ b/proposals/0378-package-registry-auth.md @@ -0,0 +1,403 @@ +# Package Registry Authentication + +* Proposal: [SE-0378](0378-package-registry-auth.md) +* Author: [Yim Lee](https://github.com/yim-lee) +* Review Manager: [Tom Doron](https://github.com/tomerd) +* Status: **Active Review (Nov 14 - Dec 2, 2022)** +* Implementation: [apple/swift-package-manager#5838](https://github.com/apple/swift-package-manager/pull/5838) +* Review: + * Pitch: https://forums.swift.org/t/pitch-package-registry-authentication/61047 + +## Introduction + +A package registry may require authentication for some or all of +its API in order to identify user performing the action and authorize +the request accordingly. + +## Motivation + +Common authentication methods used by web services include basic +authentication, access token, and OAuth. SwiftPM supports only basic +authentication today, which limits its abilities to interact with +package registry services. + +## Proposed solution + +We propose to modify the `swift package-registry` command and registry +configuration to add token authentication support. The changes should also +ensure there is flexibility to add other authentication methods in the future. + +The design draws inspiration from [`docker login`](https://docs.docker.com/engine/reference/commandline/login/) and [`npm login`](https://docs.npmjs.com/cli/v8/commands/npm-adduser), +in that there will be a single command for user to verify and persist +registry credentials. + +## Detailed design + +### Changes to `swift package-registry` command + +Instead of the `swift package-registry set` subcommand and the `--login` +and `--password` options as proposed in [SE-0292](0292-package-registry-service.md) originally, +we propose the new `login` and `logout` subcommands for adding/removing +registry credentials. + +#### New `login` subcommand + +Log in to a package registry. SwiftPM will verify the credentials using +the registry service's [`login` API](#login-api). If it returns a successful +response, credentials will be persisted to the operating system's +credential store if supported, or the user-level `.netrc` file otherwise. +The user-level configuration file located at `~/.swiftpm/configuration/registries.json` +will also be updated. + +```manpage +SYNOPSIS + swift package-registry login [options] +OPTIONS: + --username Username + --password Password + + --token Access token + + --no-confirm Allow writing to .netrc file without confirmation + --netrc-file Specify the .netrc file path +``` + +`url` should be the registry's base URL (e.g., `https://example-registry.com`). +In case the location of the `login` API is something other than `/login` +(e.g., `https://example-registry.com/api/v1/login`), provide the full URL. + +The URL must be HTTPS. + +The table below shows the supported authentication types and their +required option(s): + +| Authentication Method | Required Option(s) | +| --------------------- | -------------------------- | +| Basic | `--username`, `--password` | +| Token | `--token` | + +The tool will analyze the provided options to determine the authentication +type and prompt (i.e., interactive mode) for the password/token if it +is missing. For example, if only `--username` is present, the tool +assumes basic authentication and prompts for the password. + +For non-interactive mode, simply provide the `--password` or `--token` +option as required or make sure the secret is present in credential storage. + +If the operating system's credential store is not supported, the +tool will prompt user for confirmation before writing credentials +to the less secured `.netrc` file. Use `--no-confirm` to disable +this confirmation. + +##### Example: basic authentication (macOS, interactive) + +```console +> swift package-registry login https://example-registry.com \ + --username jappleseed +Enter password for 'jappleseed': + +Login successful. Credentials have been saved to the operating system's secure credential store. +``` + +An entry for `example-registry.com` would be added to Keychain. + +`registries.json` would be updated to indicate that `example-registry.com` +requires basic authentication: + +```json +{ + "authentication": { + "example-registry.com": { + "type": "basic" + }, + ... + }, + ... +} +``` + +##### Example: basic authentication (operating system's credential store not supported, interactive) + +```console +> swift package-registry login https://example-registry.com \ + --username jappleseed +Enter password for 'jappleseed': + +Login successful. + +WARNING: Secure credential storage is not supported on this platform. +Your credentials will be written out to .netrc. +Continue? (Y/N): Y + +Credentials have been saved to .netrc. +``` + +An entry for `example-registry.com` would be added to the `.netrc` file: + +``` +machine example-registry.com +login jappleseed +password alpine +``` + +`registries.json` would be updated to indicate that `example-registry.com` +requires basic authentication: + +```json +{ + "authentication": { + "example-registry.com": { + "type": "basic" + }, + ... + }, + ... +} +``` + +##### Example: basic authentication (operating system's credential store not supported, non-interactive) + +```console +> swift package-registry login https://example-registry.com \ + --username jappleseed \ + --password alpine \ + --no-confirm + +Login successful. Credentials have been saved to .netrc. +``` + +An entry for `example-registry.com` would be added to the `.netrc` file: + +``` +machine example-registry.com +login jappleseed +password alpine +``` + +`registries.json` would be updated to indicate that `example-registry.com` +requires basic authentication: + +```json +{ + "authentication": { + "example-registry.com": { + "type": "basic" + }, + ... + }, + ... +} +``` + + + +##### Example: basic authentication (operating system's credential store not supported, non-interactive, non-default `login` URL) + +```console +> swift package-registry login https://example-registry.com/api/v1/login \ + --username jappleseed \ + --password alpine \ + --no-confirm + +Login successful. Credentials have been saved to .netrc. +``` + +An entry for `example-registry.com` would be added to the `.netrc` file: + +``` +machine example-registry.com +login jappleseed +password alpine +``` + +`registries.json` would be updated to indicate that `example-registry.com` +requires basic authentication: + +```json +{ + "authentication": { + "example-registry.com": { + "type": "basic", + "loginAPIPath": "/api/v1/login" + }, + ... + }, + ... +} +``` + +##### Example: token authentication + +```console +> swift package-registry login https://example-registry.com \ + --token jappleseedstoken +``` + +An entry for `example-registry.com` would be added to the operating +system's credential store if supported, or the user-level `.netrc` +file otherwise: + +``` +machine example-registry.com +login token +password jappleseedstoken +``` + +`registries.json` would be updated to indicate that `example-registry.com` +requires token authentication: + +```json +{ + "authentication": { + "example-registry.com": { + "type": "token" + }, + ... + }, + ... +} +``` + +#### New `logout` subcommand + +Log out from a registry. Credentials are removed from the operating system's +credential store if supported, and the user-level configuration file +(`registries.json`). + +To avoid accidental removal of sensitive data, `.netrc` file needs to be +updated manually by the user. + +```manpage +SYNOPSIS + swift package-registry logout +``` + +### Changes to registry configuration + +We will introduce a new `authentication` key to the user-level +`registries.json` file, which by default is located at +`~/.swiftpm/configuration/registries.json`. Any package +registry that requires authentication must have a corresponding +entry in this dictionary. + +```json +{ + "registries": { + "[default]": { + "url": "https://example-registry.com" + } + }, + "authentication": { + "example-registry.com": { + "type": , // One of: "basic", "token" + "loginAPIPath": // Optional. Overrides the default API path (i.e., /login). + } + }, + "version": 1 +} +``` + +`type` must be one of the following: +* `basic`: username and password +* `token`: access token + +Credentials are to be specified in the native credential store +of the operating system if supported, otherwise in the user-level +`.netrc` file. (Only macOS Keychain will be supported in the +initial feature release; more might be added in the future.) + +See [credential storage](#credential-storage) for more details on configuring +credentials for each authentication type. + +### Credential storage + +SwiftPM will always use the most secure way to handle credentials +on the platform. In general, this would mean using the operating +system's credential store (e.g., Keychain on macOS). It falls +back to `.netrc` only if there is no other solution available. + +#### Basic Authentication + +##### macOS Keychain + +Registry credentials should be stored as "Internet password" +items in the macOS Keychain. The "item name" should be the +registry URL, including `https://` (e.g., `https://example-registry.com`). + +##### `.netrc` file (only if operating system's credential store is not supported) + +A `.netrc` entry for basic authentication looks as follows: + +``` +machine example-registry.com +login jappleseed +password alpine +``` + +By default, SwiftPM looks for `.netrc` file in the user's +home directory. A custom `.netrc` file can be specified using +the `--netrc-file` option. + +#### Token Authentication + +User can configure access token for a registry as similarly +done for basic authentication, but with `token` as the login/username +and the access token as the password. + +For example, a `.netrc` entry would look like: + +``` +machine example-registry.com +login token +password jappleseedstoken +``` + +### Additional changes in SwiftPM + +1. Only the user-level `.netrc` file will be used. Project-level `.netrc` file will not be supported. +2. SwiftPM will perform lookups in one credential store only. For macOS, it will be Keychain. For all other platforms, it will be the user-level `.netrc` file. +3. The `--disable-keychain` and `--disable-netrc` options will be removed. + +### New package registry service API + +A package registry that requires authentication must implement +the new API endpoint(s) covered in this section. + +#### `login` API + +SwiftPM will send a HTTP `POST` request to this API to validate +user credentials provided by the `login` subcommand. + +The default API path is `/login`, but this [can be overridden](#override-login-url) +by providing the full API URL to the `login` subcommand. + +The API request will include an `Authorization` HTTP header +constructed as follows: + +* Basic authentication: `Authorization: Basic ` +* Token authentication: `Authorization: Bearer ` + +The registry service must return HTTP status `200` in the +response if login is successful, and `401` otherwise. + +In case the registry service does not support an authentication method, +it should return HTTP status `501`. + +SwiftPM will persist user credentials to local credential store +if login is successful. + +## Security + +This proposal moves SwiftPM to use operating system's native credential +store (e.g., macOS Keychain) on supported platforms, which should yield +better security. + +We are also eliminating the use of project-level `.netrc` file. This should +prevent accidental checkin of `.netrc` file and thus leakage of sensitive +information. + +## Impact on existing packages + +This proposal eliminates the project-level `.netrc` file. There should be +no other impact on existing packages. + From 6a143baaa73c00dae2d810bd8a3e852af8802a40 Mon Sep 17 00:00:00 2001 From: Brandon Williams <135203+mbrandonw@users.noreply.github.com> Date: Tue, 15 Nov 2022 16:05:19 -0500 Subject: [PATCH 2804/4563] Add clock to Task.sleep(for:) (#1844) * Add clock to Task.sleep(for:) * Update 0374-clock-sleep-for.md * wip --- proposals/0374-clock-sleep-for.md | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/proposals/0374-clock-sleep-for.md b/proposals/0374-clock-sleep-for.md index f6a120f85a..7817cf9f6f 100644 --- a/proposals/0374-clock-sleep-for.md +++ b/proposals/0374-clock-sleep-for.md @@ -151,7 +151,7 @@ extension Clock { This will allow one to sleep for a duration with a clock rather than sleeping until an instant. -Further, to make the APIs between `clock.sleep` and `Task.sleep` similar, we will also add a `tolerance` argument to `Task.sleep(for:)`: +Further, to make the APIs between `clock.sleep(for:)` and `Task.sleep(for:)` similar, we will also add a `clock` and `tolerance` argument to `Task.sleep(for:)`: ```swift extension Task where Success == Never, Failure == Never { @@ -165,11 +165,26 @@ extension Task where Success == Never, Failure == Never { /// try await Task.sleep(for: .seconds(3)) /// /// - Parameter duration: The duration to wait. - public static func sleep( - for duration: Duration, - tolerance: Duration? = nil + public static func sleep( + for duration: C.Duration, + tolerance: C.Duration? = nil, + clock: C = ContinuousClock() + ) async throws { + try await sleep(until: clock.now.advanced(by: duration), tolerance: tolerance, clock: clock) + } +} +``` + +And we will add a default value for the `clock` argument of `Task.sleep(until:)`: + +```swift +extension Task where Success == Never, Failure == Never { + public static func sleep( + until deadline: C.Instant, + tolerance: C.Instant.Duration? = nil, + clock: C = ContinuousClock() ) async throws { - try await sleep(until: .now + duration, tolerance: tolerance, clock: .continuous) + try await clock.sleep(until: deadline, tolerance: tolerance) } } ``` From 43230c2fef3ea75c4f9946294f79ab0cedc2ed10 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 15 Nov 2022 16:54:11 -0500 Subject: [PATCH 2805/4563] Update 0374-clock-sleep-for.md (#1845) bump review end date to account for changes --- proposals/0374-clock-sleep-for.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0374-clock-sleep-for.md b/proposals/0374-clock-sleep-for.md index 7817cf9f6f..7b7e8b89e5 100644 --- a/proposals/0374-clock-sleep-for.md +++ b/proposals/0374-clock-sleep-for.md @@ -3,7 +3,7 @@ * Proposal: [SE-0374](0374-clock-sleep-for.md) * Authors: [Brandon Williams](https://github.com/mbrandonw), [Stephen Celis](https://github.com/stephencelis) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active Review (October 11 ... November 1, 2022)** +* Status: **Active Review (October 11 ... November 22, 2022)** * Implementation: [apple/swift#61222](https://github.com/apple/swift/pull/61222) * Review: ([pitch](https://forums.swift.org/t/pitch-clock-sleep-for/60376)) ([review](https://forums.swift.org/t/se-0374-add-sleep-for-to-clock/60787)) From aad9db8a0a774ff0752780b727891d0b6353dbf2 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 9 Nov 2022 14:00:17 +0000 Subject: [PATCH 2806/4563] Redirect to https://www.swift.org/swift-evolution/ --- 404.html | 9 + index.css | 652 ------------------------------- index.html | 100 ----- index.js | 1075 ---------------------------------------------------- 4 files changed, 9 insertions(+), 1827 deletions(-) create mode 100644 404.html delete mode 100644 index.css delete mode 100644 index.html delete mode 100644 index.js diff --git a/404.html b/404.html new file mode 100644 index 0000000000..13f44ec1f4 --- /dev/null +++ b/404.html @@ -0,0 +1,9 @@ + + + + +Swift Evolution diff --git a/index.css b/index.css deleted file mode 100644 index 164d1ec911..0000000000 --- a/index.css +++ /dev/null @@ -1,652 +0,0 @@ -/*===--- index.css - Swift Evolution --------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -*/ - -:root { - color-scheme: light dark; - - --background: white; - --text: #333; - --link: #666; - --proposal-link: #888; - --footer-background: #333; - --footer-text: #ccc; - --header-background: rgba(248, 248, 248, 0.7); - --nav-background: rgba(248, 248, 248, 0.7); - --border: rgb(230, 230, 230); - --filter-link: rgb(0, 136, 204); -} - -@media (prefers-color-scheme: dark) { - :root { - --background: black; - --text: white; - --link: rgb(9, 132, 255); - --proposal-link: rgba(235, 235, 245, 0.6); - --footer-background: rgb(28, 28, 30); - --footer-text: rgb(255, 255, 255); - --header-background: rgba(44, 44, 46, 0.7); - --nav-background: rgba(28, 28, 30, 0.7); - --border: rgba(84, 84, 88, 0.6); - } -} - -*:focus { - outline: none; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; - font-size: 18px; - -webkit-text-size-adjust: none; - line-height: 1.5; - color: var(--text); - background-color: var(--background); - margin: 0; -} - -h1 { - margin-left: 10px; - font-size: 54px; -} - -h4 { - margin-bottom: 0em; - font-size: 20px; - line-height: 1.2em; -} - -a:link { - color: var(--link); - text-decoration: none; -} - -a:visited { - color: var(--link); -} - -a:active { - color: var(--link); -} - -a:hover { - color: var(--link); - text-decoration: underline; -} - -ul { - list-style: none; - margin: 0; - padding: 0; -} - -main { - display: flex; - width: 95%; - flex: 1; - justify-content: center; -} - -header { - display: flex; - flex-wrap: wrap; - padding-top: 20px; - min-width: 100%; - height: 140px; - flex-direction: row; - text-align: left; - align-items: center; - justify-content: center; - background: var(--header-background); - -webkit-backdrop-filter: blur(10px); - backdrop-filter: blur(10px); - border-bottom: 1px solid var(--border); - padding-top: 0px; -} - -.header-contents { - flex-basis: 960px; -} - -#logo { - display: inline-block; - width: 60px; - height: 60px; - margin-bottom: -2px; - background-image: url("https://data.swift.org/swift-evolution/swift.svg"); -} - -#title { - display: inline-block; - line-height: 48px; - font-weight: 200; -} - -#title a:link { color: var(--text); } -#title a:active { color: var(--text); } -#title a:visited { color: var(--text); } -#title a:hover { text-decoration: none; } - -.app { - display: flex; - flex-direction: column; - align-items: center; - min-height: 100vh; -} - -nav { - top: 0px; - background: var(--nav-background); - -webkit-backdrop-filter: blur(10px); - backdrop-filter: blur(10px); - - border-bottom: 1px solid var(--border); - min-width: 100%; -} - -@supports ((position: sticky) or (position: -webkit-sticky)) { - nav { - position: -webkit-sticky; - position: sticky; - } -} - -.nav-contents { - width: 960px; - margin: 0 auto; - margin-top: 8px; - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-between; - min-height: 40px; -} - -footer { - background: var(--footer-background); - color: var(--footer-text); - font-size: 12px; - min-width: 100%; - justify-content: center; - display: flex; - flex-direction: row; -} - -footer a:link, footer a:active, footer a:visited, footer a:hover { - color: var(--footer-text); -} - -.footer-contents { - flex-basis: 960px; -} - -footer p { - margin: 1em 0em; -} - -article { - flex-basis: 960px; -} - - -section { - margin-bottom: 1.5em; -} - -section ul, section li { - display: inline; -} - -.section-title { - margin-top: 14px; -} - -.hidden { - display: none !important; -} - -#proposals-count { - line-height: 40px; - font-size: 16px; - color: var(--text); - padding: 4px 0px; -} - -/* - Individual proposals -*/ - -.proposal-id { - color: var(--proposal-link); - font-weight: 300; - font-size: 20px; - margin-right: 0.7em; - vertical-align: top; /* align with the proposal title */ - line-height: 1.2em; - display: inline-block; -} - -.proposal { - font-size: 14px; - display: flex; - flex-direction: row; - margin-bottom: 40px; -} - -.proposal-list, .proposal { - max-width: none; -} - -.proposal-header { - margin-bottom: 6px; -} - -.proposal-details { - -moz-column-count: 2; - column-count: 2; - -moz-column-width: 373px; - column-width: 373px; -} - -.proposal-detail { - break-inside: avoid; - display: flex; - max-width: 373px; -} - -.proposal-detail-label { - color: var(--proposal-link); - font-weight: 200; - display: inline-block; - padding-right: 6px; - white-space: nowrap; -} - -.proposal-detail-value { - display: inline-block; - color: var(--text); -} - -.proposal-title a:link, -.proposal-title a:visited, -.proposal-title a:active, -.proposal-detail-value a:link, -.proposal-detail-value a:visited, -.proposal-detail-value a:active { - color: var(--text); -} - -.authors .proposal-detail-value { - width: 280px; -} - -.authors a { - white-space: nowrap; -} - -.review-manager .proposal-detail-value { - width: 223px; -} - -.bug-list { - width: 310px; -} - -.bug-list a, .implementation-list a { - word-wrap: break-word; -} - -.proposal-title { - font-weight: 400; - margin-top: 0; - display: inline-block; - max-width: 650px; -} - -.status-pill-container { - margin-top: -2px; -} - -.status-pill { - display: inline-block; - border: 1px solid black; - border-radius: 4px; - padding: 0px; - white-space: nowrap; - font-size: 16px; - font-weight: 200; - line-height: 26px; - text-align: center; - width: 152px; - min-width: 152px; - max-width: 152px; - margin-right: 20px; -} - -/* - Filtering -*/ - -#search-filter { - background-color: var(--background); - border: 1px solid var(--border); - color: var(--text); -} - -.filter-by-status { - padding-bottom: 0.75em; - padding-top: 7px; - max-width: 800px; -} - -.filter-by-status li { - margin-right: 6px; - margin-bottom: 6px; -} - -.filter-by-status label { - border: 1px solid var(--proposal-link); - border-radius: 4px; - padding: 3px 16px; - font-size: 16px; - margin: 0 0px; - height: 28px; - cursor: pointer; -} - -.filter-by-status, .filter-by-status li { - display: inline-block; - -webkit-user-select: none; -} - -.filter-by-status input[type=checkbox] { - display: none; -} - -.filter-by-status input[type=checkbox] + label { - color: var(--proposal-link); -} - -.filter-by-status input[type=checkbox]:checked + label { - background: var(--proposal-link); - color: white; -} - -.filter-toggle a { - color: var(--filter-link); - font-weight: 400; - cursor: pointer; -} - -.filter-toggle-contents { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - line-height: 20px; - height: 30px; -} - -.filter-button { - cursor: pointer; - line-height: 40px; - order: 3; - - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; -} - -.filter-description { - font-size: 14px; - color: var(--proposal-link); - max-height: 70%; - margin-right: 10px; -} - -.toggle-filter-panel { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; -} - -.active .icon-line { - stroke: white; -} - -.active .icon-circle { - fill: var(--proposal-link); -} - -.expandable { - display: none; - max-height: 0px; - overflow: hidden; -} - -.expandable.expanded { - display: block; - flex-basis: 100%; - overflow: hidden; - max-height: 100vh; - order: 4; -} - -#filter-options-label, #version-options-label { - font-size: 14px; - font-weight: 300; - margin: 10px 0 0 0; -} - -#search-icon { - position: relative; - top: 4px; - left: 8px; - z-index: 1; -} - -#search-container { - flex-grow: 100; -} - -.filter { - -webkit-appearance: textfield; - font-size: 14px; - height: 28px; - max-height: 28px; - width: 200px; - padding: 0px 25px; - position: relative; - left: -20px; - font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif; -} - -::-webkit-search-cancel-button { - -webkit-appearance: none; -} - -#clear-button { - position: relative; - top: 2px; - left: -45px; - z-index: 1; - cursor: pointer; -} - -@media (max-width: 1000px) { - body { - font-size: 16px; - } - - header { - height: auto; - } - - #logo { - width: 40px; - height: 40px; - background-size: 40px; - } - - .header-contents { - flex-basis: auto; - } - - .nav-contents { - width: 90%; - justify-content: center; - } - - .footer-contents { - flex-basis: 90%; - } - - #title { - font-size: 34px; - line-height: 75px; - margin: 5px 5px; - } - - h3 { - font-size: 14pt; - } - - .filter { - padding: 0px 25px; - } - - #search-container { - flex-grow: 100; - margin-top: 3px; - height: 38px; - } - - .filter-button { - margin-right: 10px; - margin-top: 3px; - } - - #search-filter { - width: calc(100% - 37px); - } - - #search-icon { - top: 3px; - width: 14px; - height: 14px; - } - - #clear-button { - left: -50px; - } - - #proposals-count { - margin-left: 6px; - } - - h4 a { - white-space: normal; - display: inline-block; - line-height: 24px; - -webkit-hyphens: auto; - -ms-hyphens: auto; - hyphens: auto; - } - - .status-pill { - width: inherit; - max-width: inherit; - min-width: inherit; - padding: 0px 8px; - margin-bottom: 6px; - } - - .authors a { - white-space: normal; - } - - .proposal { - flex-direction: column; - max-width: 100%; - padding: 0px 10px; - } - - .proposal-header { - flex-direction: column; - } - - .proposal-detail { - display: block; - } - - .proposal-detail-label, .proposal-detail-value { - display: inline; - } -} - -@media (max-width: 768px) { - h1 { - font-size: 54px; - } -} - -@media (max-width: 414px){ - .filter-button { - order: 0; - } -} - -/* status label colors */ - -.color-awaiting-review { - color: rgb(255, 149, 0); - border-color: rgb(255, 149, 0); -} - -.color-scheduled-for-review { - color: rgb(255, 149, 0); - border-color: rgb(255, 149, 0); -} - -.color-active-review { - color: rgb(255, 149, 0); - border-color: rgb(255, 149, 0); -} -.color-returned-for-revision { - color: rgb(88, 86, 214); - border-color: rgb(88, 86, 214); -} - -.color-deferred { - color: rgb(88, 86, 214); - border-color: rgb(88, 86, 214); -} -.color-accepted,.color-accepted-with-revisions { - color: rgb(76, 217, 100); - border-color: rgb(76, 217, 100); -} -.color-rejected { - color: rgb(255, 59, 48); - border-color: rgb(255, 59, 48); -} -.color-previewing { - color: rgb(0, 190, 180); - border-color: rgb(0, 190, 180); -} -.color-implemented { - color: rgb(0, 122, 255); - border-color: rgb(0, 122, 255); -} -.color-withdrawn { - color: rgb(255, 59, 48); - border-color: rgb(255, 59, 48); -} diff --git a/index.html b/index.html deleted file mode 100644 index e0d2b87d06..0000000000 --- a/index.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - Swift Evolution - - - - - -
    -
    - - -

    Swift Evolution

    -
    -
    - -
    -
    -
    -
      - -
    -
    -
    -
    - -
    -
    - - - diff --git a/index.js b/index.js deleted file mode 100644 index c675b3b3af..0000000000 --- a/index.js +++ /dev/null @@ -1,1075 +0,0 @@ -// ===--- index.js - Swift Evolution --------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -// ===---------------------------------------------------------------------===// - -'use strict' - -/** Holds the primary data used on this page: metadata about Swift Evolution proposals. */ -var proposals - -/** - * To be updated when proposals are confirmed to have been implemented - * in a new language version. - */ -var languageVersions = ['2.2', '3', '3.0.1', '3.1', '4', '4.1', '4.2', '5', '5.1', '5.2', '5.3', '5.4', '5.5', '5.5.2', '5.6', '5.7', '5.8', 'Next'] - -/** Storage for the user's current selection of filters when filtering is toggled off. */ -var filterSelection = [] - -var GITHUB_BASE_URL = 'https://github.com/' -var REPO_PROPOSALS_BASE_URL = GITHUB_BASE_URL + 'apple/swift-evolution/blob/main/proposals' - -/** - * `name`: Mapping of the states in the proposals JSON to human-readable names. - * - * `shortName`: Mapping of the states in the proposals JSON to short human-readable names. - * Used for the left-hand column of proposal statuses. - * - * `className`: Mapping of states in the proposals JSON to the CSS class names used - * to manipulate and display proposals based on their status. - * - * `count`: Number of proposals that determine after all proposals is loaded - */ -var states = { - '.awaitingReview': { - name: 'Awaiting Review', - shortName: 'Awaiting Review', - className: 'awaiting-review', - count: 0 - }, - '.scheduledForReview': { - name: 'Scheduled for Review', - shortName: 'Scheduled', - className: 'scheduled-for-review', - count: 0 - }, - '.activeReview': { - name: 'Active Review', - shortName: 'Active Review', - className: 'active-review', - count: 0 - }, - '.returnedForRevision': { - name: 'Returned for Revision', - shortName: 'Returned', - className: 'returned-for-revision', - count: 0 - }, - '.withdrawn': { - name: 'Withdrawn', - shortName: 'Withdrawn', - className: 'withdrawn', - count: 0 - }, - '.deferred': { - name: 'Deferred', - shortName: 'Deferred', - className: 'deferred', - count: 0 - }, - '.accepted': { - name: 'Accepted', - shortName: 'Accepted', - className: 'accepted', - count: 0 - }, - '.acceptedWithRevisions': { - name: 'Accepted with revisions', - shortName: 'Accepted', - className: 'accepted-with-revisions', - count: 0 - }, - '.rejected': { - name: 'Rejected', - shortName: 'Rejected', - className: 'rejected', - count: 0 - }, - '.implemented': { - name: 'Implemented', - shortName: 'Implemented', - className: 'implemented', - count: 0 - }, - '.previewing': { - name: 'Previewing', - shortName: 'Previewing', - className: 'previewing', - count: 0 - }, - '.error': { - name: 'Error', - shortName: 'Error', - className: 'error', - count: 0 - } -} - -init() - -/** Primary entry point. */ -function init () { - var req = new window.XMLHttpRequest() - - req.addEventListener('load', function (e) { - proposals = JSON.parse(req.responseText) - - // don't display malformed proposals - proposals = proposals.filter(function (proposal) { - return !proposal.errors - }) - - // descending numeric sort based the numeric nnnn in a proposal ID's SE-nnnn - proposals.sort(function compareProposalIDs (p1, p2) { - return parseInt(p1.id.match(/\d\d\d\d/)[0]) - parseInt(p2.id.match(/\d\d\d\d/)[0]) - }) - proposals = proposals.reverse() - - render() - addEventListeners() - - // apply filters when the page loads with a search already filled out. - // typically this happens after navigating backwards in a tab's history. - if (document.querySelector('#search-filter').value.trim()) { - filterProposals() - } - - // apply selections from the current page's URI fragment - _applyFragment(document.location.hash) - }) - - req.addEventListener('error', function (e) { - document.querySelector('#proposals-count-number').innerText = 'Proposal data failed to load.' - }) - - document.querySelector('#proposals-count-number').innerHTML = 'Loading ...' - req.open('get', 'https://download.swift.org/swift-evolution/proposals.json') - req.send() -} - -/** - * Creates an Element. Convenience wrapper for `document.createElement`. - * - * @param {string} elementType - The tag name. 'div', 'span', etc. - * @param {string[]} attributes - A list of attributes. Use `className` for `class`. - * @param {(string | Element)[]} children - A list of either text or other Elements to be nested under this Element. - * @returns {Element} The new node. - */ -function html (elementType, attributes, children) { - var element = document.createElement(elementType) - - if (attributes) { - Object.keys(attributes).forEach(function (attributeName) { - var value = attributes[attributeName] - if (attributeName === 'className') attributeName = 'class' - element.setAttribute(attributeName, value) - }) - } - - if (!children) return element - if (!Array.isArray(children)) children = [children] - - children.forEach(function (child) { - if (!child) { - console.warn('Null child ignored during creation of ' + elementType) - return - } - if (Object.getPrototypeOf(child) === String.prototype) { - child = document.createTextNode(child) - } - - element.appendChild(child) - }) - - return element -} - -function determineNumberOfProposals (proposals) { - // reset count - Object.keys(states).forEach(function (state){ - states[state].count = 0 - }) - - proposals.forEach(function (proposal) { - states[proposal.status.state].count += 1 - }) - - // .acceptedWithRevisions proposals are combined in the filtering UI - // with .accepted proposals. - states['.accepted'].count += states['.acceptedWithRevisions'].count -} - -/** - * Adds the dynamic portions of the page to the DOM, primarily the list - * of proposals and list of statuses used for filtering. - * - * These `render` functions are only called once when the page loads, - * the rest of the interactivity is based on toggling `display: none`. - */ -function render () { - renderNav() - renderBody() -} - -/** Renders the top navigation bar. */ -function renderNav () { - var nav = document.querySelector('nav') - - // This list intentionally omits .acceptedWithRevisions and .error; - // .acceptedWithRevisions proposals are combined in the filtering UI - // with .accepted proposals. - var checkboxes = [ - '.awaitingReview', '.scheduledForReview', '.activeReview', '.accepted', - '.previewing', '.implemented', '.returnedForRevision', '.deferred', '.rejected', '.withdrawn' - ].map(function (state) { - var className = states[state].className - - return html('li', null, [ - html('input', { type: 'checkbox', className: 'filtered-by-status', id: 'filter-by-' + className, value: className }), - html('label', { className: className, tabindex: '0', role: 'button', 'for': 'filter-by-' + className, 'data-state-key': state }, [ - addNumberToState(states[state].name, states[state].count) - ]) - ]) - }) - - var expandableArea = html('div', { className: 'filter-options expandable' }, [ - html('h5', { id: 'filter-options-label' }, 'Status'), - html('ul', { id: 'filter-options', className: 'filter-by-status' }) - ]) - - nav.querySelector('.nav-contents').appendChild(expandableArea) - - checkboxes.forEach(function (box) { - nav.querySelector('.filter-by-status').appendChild(box) - }) - - // The 'Implemented' filter selection gets an extra row of options if selected. - var implementedCheckboxIfPresent = checkboxes.filter(function (cb) { - return cb.querySelector(`#filter-by-${states['.implemented'].className}`) - })[0] - - if (implementedCheckboxIfPresent) { - // add an extra row of options to filter by language version - var versionRowHeader = html('h5', { id: 'version-options-label', className: 'hidden' }, 'Language Version') - var versionRow = html('ul', { id: 'version-options', className: 'filter-by-status hidden' }) - - var versionOptions = languageVersions.map(function (version) { - return html('li', null, [ - html('input', { - type: 'checkbox', - id: 'filter-by-swift-' + _idSafeName(version), - className: 'filter-by-swift-version', - value: 'swift-' + _idSafeName(version) - }), - html('label', { - tabindex: '0', - role: 'button', - 'for': 'filter-by-swift-' + _idSafeName(version) - }, 'Swift ' + version) - ]) - }) - - versionOptions.forEach(function (version) { - versionRow.appendChild(version) - }) - - expandableArea.appendChild(versionRowHeader) - expandableArea.appendChild(versionRow) - } - - return nav -} - -/** Displays the main list of proposals that takes up the majority of the page. */ -function renderBody () { - var article = document.querySelector('article') - - var proposalAttachPoint = article.querySelector('.proposals-list') - - var proposalPresentationOrder = [ - '.awaitingReview', '.scheduledForReview', '.activeReview', '.accepted', '.acceptedWithRevisions', - '.previewing', '.implemented', '.returnedForRevision', '.deferred', '.rejected', '.withdrawn' - ] - - proposalPresentationOrder.map(function (state) { - var matchingProposals = proposals.filter(function (p) { return p.status && p.status.state === state }) - matchingProposals.map(function (proposal) { - var proposalBody = html('section', { id: proposal.id, className: 'proposal ' + proposal.id }, [ - html('div', { className: 'status-pill-container' }, [ - html('span', { className: 'status-pill color-' + states[state].className }, [ - states[proposal.status.state].shortName - ]) - ]), - html('div', { className: 'proposal-content' }, [ - html('div', { className: 'proposal-header' }, [ - html('span', { className: 'proposal-id' }, [ - proposal.id - ]), - html('h4', { className: 'proposal-title' }, [ - html('a', { - href: REPO_PROPOSALS_BASE_URL + '/' + proposal.link, - target: '_blank' - }, [ - proposal.title - ]) - ]) - ]) - ]) - ]) - - var detailNodes = [] - detailNodes.push(renderAuthors(proposal.authors)) - - if (proposal.reviewManager.name) detailNodes.push(renderReviewManager(proposal.reviewManager)) - if (proposal.trackingBugs) detailNodes.push(renderTrackingBugs(proposal.trackingBugs)) - if (state === '.implemented') detailNodes.push(renderVersion(proposal.status.version)) - if (state === '.previewing') detailNodes.push(renderPreview()) - if (proposal.implementation) detailNodes.push(renderImplementation(proposal.implementation)) - if (state === '.acceptedWithRevisions') detailNodes.push(renderStatus(proposal.status)) - - if (state === '.activeReview' || state === '.scheduledForReview') { - detailNodes.push(renderStatus(proposal.status)) - detailNodes.push(renderReviewPeriod(proposal.status)) - } - - if (state === '.returnedForRevision') { - detailNodes.push(renderStatus(proposal.status)) - } - - var details = html('div', { className: 'proposal-details' }, detailNodes) - - proposalBody.querySelector('.proposal-content').appendChild(details) - proposalAttachPoint.appendChild(proposalBody) - }) - }) - - // Update the "(n) proposals" text - updateProposalsCount(article.querySelectorAll('.proposal').length) - - return article -} - -/** Authors have a `name` and optional `link`. */ -function renderAuthors (authors) { - var authorNodes = authors.map(function (author) { - if (author.link.length > 0) { - return html('a', { href: author.link, target: '_blank' }, author.name) - } else { - return document.createTextNode(author.name) - } - }) - - authorNodes = _joinNodes(authorNodes, ', ') - - return html('div', { className: 'authors proposal-detail' }, [ - html('div', { className: 'proposal-detail-label' }, - authors.length > 1 ? 'Authors: ' : 'Author: ' - ), - html('div', { className: 'proposal-detail-value' }, authorNodes) - ]) -} - -/** Review managers have a `name` and optional `link`. */ -function renderReviewManager (reviewManager) { - return html('div', { className: 'review-manager proposal-detail' }, [ - html('div', { className: 'proposal-detail-label' }, 'Review Manager: '), - html('div', { className: 'proposal-detail-value' }, [ - reviewManager.link - ? html('a', { href: reviewManager.link, target: '_blank' }, reviewManager.name) - : reviewManager.name - ]) - ]) -} - -/** Tracking bugs linked in a proposal are updated via bugs.swift.org. */ -function renderTrackingBugs (bugs) { - var bugNodes = bugs.map(function (bug) { - return html('a', { href: bug.link, target: '_blank' }, [ - bug.id, - ' (', - bug.assignee || 'Unassigned', - ', ', - bug.status, - ')' - ]) - }) - - bugNodes = _joinNodes(bugNodes, ', ') - - return html('div', { className: 'proposal-detail' }, [ - html('div', { className: 'proposal-detail-label' }, [ - bugs.length > 1 ? 'Bugs: ' : 'Bug: ' - ]), - html('div', { className: 'bug-list proposal-detail-value' }, - bugNodes - ) - ]) -} - -/** Implementations are required alongside proposals (after Swift 4.0). */ -function renderImplementation (implementations) { - var implNodes = implementations.map(function (impl) { - return html('a', { - href: GITHUB_BASE_URL + impl.account + '/' + impl.repository + '/' + impl.type + '/' + impl.id - }, [ - impl.repository, - impl.type === 'pull' ? '#' : '@', - impl.id.substr(0, 7) - ]) - }) - - implNodes = _joinNodes(implNodes, ', ') - - var label = 'Implementation: ' - - return html('div', { className: 'proposal-detail' }, [ - html('div', { className: 'proposal-detail-label' }, [label]), - html('div', { className: 'implementation-list proposal-detail-value' }, - implNodes - ) - ]) -} - -/** For `.previewing` proposals, link to the stdlib preview package. */ -function renderPreview () { - return html('div', { className: 'proposal-detail' }, [ - html('div', { className: 'proposal-detail-label' }, [ - 'Preview: ' - ]), - html('div', { className: 'proposal-detail-value' }, [ - html('a', { href: 'https://github.com/apple/swift-standard-library-preview', target: '_blank' }, - 'Standard Library Preview' - ) - ]) - ]) -} - -/** For `.implemented` proposals, display the version of Swift in which they first appeared. */ -function renderVersion (version) { - return html('div', { className: 'proposal-detail' }, [ - html('div', { className: 'proposal-detail-label' }, [ - 'Implemented In: ' - ]), - html('div', { className: 'proposal-detail-value' }, [ - 'Swift ' + version - ]) - ]) -} - -/** For some proposal states like `.activeReview`, it helps to see the status in the same details list. */ -function renderStatus (status) { - return html('div', { className: 'proposal-detail' }, [ - html('div', { className: 'proposal-detail-label' }, [ - 'Status: ' - ]), - html('div', { className: 'proposal-detail-value' }, [ - states[status.state].name - ]) - ]) -} - -/** - * Review periods are ISO-8601-style 'YYYY-MM-DD' dates. - */ -function renderReviewPeriod (status) { - var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', - 'August', 'September', 'October', 'November', 'December' - ] - - var start = new Date(status.start) - var end = new Date(status.end) - - var startMonth = start.getUTCMonth() - var endMonth = end.getUTCMonth() - - var detailNodes = [months[startMonth], ' '] - - if (startMonth === endMonth) { - detailNodes.push( - start.getUTCDate().toString(), - '–', - end.getUTCDate().toString() - ) - } else { - detailNodes.push( - start.getUTCDate().toString(), - ' – ', - months[endMonth], - ' ', - end.getUTCDate().toString() - ) - } - - return html('div', { className: 'proposal-detail' }, [ - html('div', { className: 'proposal-detail-label' }, [ - 'Scheduled: ' - ]), - html('div', { className: 'proposal-detail-value' }, detailNodes) - ]) -} - -/** Utility used by some of the `render*` functions to add comma text nodes between DOM nodes. */ -function _joinNodes (nodeList, text) { - return nodeList.map(function (node) { - return [node, text] - }).reduce(function (result, pair, index, pairs) { - if (index === pairs.length - 1) pair.pop() - return result.concat(pair) - }, []) -} - -/** Adds UI interactivity to the page. Primarily activates the filtering controls. */ -function addEventListeners () { - var nav = document.querySelector('nav') - - // typing in the search field causes the filter to be reapplied. - nav.addEventListener('keyup', filterProposals) - nav.addEventListener('change', filterProposals) - - // clearing the search field also hides the X symbol - nav.querySelector('#clear-button').addEventListener('click', function () { - nav.querySelector('#search-filter').value = '' - nav.querySelector('#clear-button').classList.toggle('hidden') - filterProposals() - }) - - // each of the individual statuses needs to trigger filtering as well - ;[].forEach.call(nav.querySelectorAll('.filter-by-status input'), function (element) { - element.addEventListener('change', filterProposals) - }) - - var expandableArea = document.querySelector('.filter-options') - var implementedToggle = document.querySelector('#filter-by-implemented') - implementedToggle.addEventListener('change', function () { - // hide or show the row of version options depending on the status of the 'Implemented' option - ;['#version-options', '#version-options-label'].forEach(function (selector) { - expandableArea.querySelector(selector).classList.toggle('hidden') - }) - - // don't persist any version selections when the row is hidden - ;[].concat.apply([], expandableArea.querySelectorAll('.filter-by-swift-version')).forEach(function (versionCheckbox) { - versionCheckbox.checked = false - }) - }) - - document.querySelector('.filter-button').addEventListener('click', toggleFiltering) - - var filterToggle = document.querySelector('.filter-toggle') - filterToggle.querySelector('.toggle-filter-panel').addEventListener('click', toggleFilterPanel) - - // Behavior conditional on certain browser features - var CSS = window.CSS - if (CSS) { - // emulate position: sticky when it isn't available. - if (!(CSS.supports('position', 'sticky') || CSS.supports('position', '-webkit-sticky'))) { - window.addEventListener('scroll', function () { - var breakpoint = document.querySelector('header').getBoundingClientRect().bottom - var nav = document.querySelector('nav') - var position = window.getComputedStyle(nav).position - var shadowNav // maintain the main content height when the main 'nav' is removed from the flow - - // this is measuring whether or not the header has scrolled offscreen - if (breakpoint <= 0) { - if (position !== 'fixed') { - shadowNav = nav.cloneNode(true) - shadowNav.classList.add('clone') - shadowNav.style.visibility = 'hidden' - nav.parentNode.insertBefore(shadowNav, document.querySelector('main')) - nav.style.position = 'fixed' - } - } else if (position === 'fixed') { - nav.style.position = 'static' - shadowNav = document.querySelector('nav.clone') - if (shadowNav) shadowNav.parentNode.removeChild(shadowNav) - } - }) - } - } - - // on smaller screens, hide the filter panel when scrolling - if (window.matchMedia('(max-width: 414px)').matches) { - window.addEventListener('scroll', function () { - var breakpoint = document.querySelector('header').getBoundingClientRect().bottom - if (breakpoint <= 0 && document.querySelector('.expandable').classList.contains('expanded')) { - toggleFilterPanel() - } - }) - } -} - -/** - * Toggles whether filters are active. Rather than being cleared, they are saved to be restored later. - * Additionally, toggles the presence of the "Filtered by:" status indicator. - */ -function toggleFiltering () { - var filterDescription = document.querySelector('.filter-toggle') - var shouldPreserveSelection = !filterDescription.classList.contains('hidden') - - filterDescription.classList.toggle('hidden') - var selected = document.querySelectorAll('.filter-by-status input[type=checkbox]:checked') - var filterButton = document.querySelector('.filter-button') - - if (shouldPreserveSelection) { - filterSelection = [].map.call(selected, function (checkbox) { return checkbox.id }) - ;[].forEach.call(selected, function (checkbox) { checkbox.checked = false }) - - filterButton.setAttribute('aria-pressed', 'false') - } else { // restore it - filterSelection.forEach(function (id) { - var checkbox = document.getElementById(id) - checkbox.checked = true - }) - - filterButton.setAttribute('aria-pressed', 'true') - } - - document.querySelector('.expandable').classList.remove('expanded') - filterButton.classList.toggle('active') - - filterProposals() -} - -/** - * Expands or contracts the filter panel, which contains buttons that - * let users filter proposals based on their current stage in the - * Swift Evolution process. - */ -function toggleFilterPanel () { - var panel = document.querySelector('.expandable') - var button = document.querySelector('.toggle-filter-panel') - - panel.classList.toggle('expanded') - - if (panel.classList.contains('expanded')) { - button.setAttribute('aria-pressed', 'true') - } else { - button.setAttribute('aria-pressed', 'false') - } -} - -/** - * Applies both the status-based and text-input based filters to the proposals list. - */ -function filterProposals () { - var filterElement = document.querySelector('#search-filter') - var filter = filterElement.value - - var clearButton = document.querySelector('#clear-button') - if (filter.length === 0) { - clearButton.classList.add('hidden') - } else { - clearButton.classList.remove('hidden') - } - - var matchingSets = [proposals.concat()] - - // Comma-separated lists of proposal IDs are treated as an "or" search. - if (filter.match(/(SE-\d\d\d\d)($|((,SE-\d\d\d\d)+))/i)) { - var proposalIDs = filter.split(',').map(function (id) { - return id.toUpperCase() - }) - - matchingSets[0] = matchingSets[0].filter(function (proposal) { - return proposalIDs.indexOf(proposal.id) !== -1 - }) - } else if (filter.trim().length !== 0) { - // The search input treats words as order-independent. - matchingSets = filter.split(/\s/) - .filter(function (s) { return s.length > 0 }) - .map(function (part) { return _searchProposals(part) }) - } - - var intersection = matchingSets.reduce(function (intersection, candidates) { - return intersection.filter(function (alreadyIncluded) { return candidates.indexOf(alreadyIncluded) !== -1 }) - }, matchingSets[0] || []) - - _applyFilter(intersection) - _updateURIFragment() - - determineNumberOfProposals(intersection) - updateFilterStatus() -} - -/** - * Utility used by `filterProposals`. - * - * Picks out various fields in a proposal which users may want to key - * off of in their text-based filtering. - * - * @param {string} filterText - A raw word of text as entered by the user. - * @returns {Proposal[]} The proposals that match the entered text, taken from the global list. - */ -function _searchProposals (filterText) { - var filterExpression = filterText.toLowerCase() - - var searchableProperties = [ - ['id'], - ['title'], - ['reviewManager', 'name'], - ['status', 'state'], - ['status', 'version'], - ['authors', 'name'], - ['authors', 'link'], - ['implementation', 'account'], - ['implementation', 'repository'], - ['implementation', 'id'], - ['trackingBugs', 'link'], - ['trackingBugs', 'status'], - ['trackingBugs', 'id'], - ['trackingBugs', 'assignee'] - ] - - // reflect over the proposals and find ones with matching properties - var matchingProposals = proposals.filter(function (proposal) { - var match = false - searchableProperties.forEach(function (propertyList) { - var value = proposal - - propertyList.forEach(function (propertyName, index) { - if (!value) return - value = value[propertyName] - if (index < propertyList.length - 1) { - // For arrays, apply the property check to each child element. - // Note that this only looks to a depth of one property. - if (Array.isArray(value)) { - var matchCondition = value.some(function (element) { - return element[propertyList[index + 1]] && element[propertyList[index + 1]].toString().toLowerCase().indexOf(filterExpression) >= 0 - }) - - if (matchCondition) { - match = true - } - } else { - return - } - } else if (value && value.toString().toLowerCase().indexOf(filterExpression) >= 0) { - match = true - } - }) - }) - - return match - }) - - return matchingProposals -} - -/** - * Helper for `filterProposals` that actually makes the filter take effect. - * - * @param {Proposal[]} matchingProposals - The proposals that have passed the text filtering phase. - * @returns {Void} Toggles `display: hidden` to apply the filter. - */ -function _applyFilter (matchingProposals) { - // filter out proposals based on the grouping checkboxes - var allStateCheckboxes = document.querySelector('nav').querySelectorAll('.filter-by-status input:checked') - var selectedStates = [].map.call(allStateCheckboxes, function (checkbox) { return checkbox.value }) - - var selectedStateNames = [].map.call(allStateCheckboxes, function (checkbox) { return checkbox.nextElementSibling.innerText.trim() }) - updateFilterDescription(selectedStateNames) - - if (selectedStates.length) { - matchingProposals = matchingProposals - .filter(function (proposal) { - return selectedStates.some(function (state) { - return proposal.status.state.toLowerCase().indexOf(state.split('-')[0]) >= 0 - }) - }) - - // handle version-specific filtering options - if (selectedStates.some(function (state) { return state.match(/swift/i) })) { - matchingProposals = matchingProposals - .filter(function (proposal) { - return selectedStates.some(function (state) { - if (!(proposal.status.state === '.implemented')) return true // only filter among Implemented (N.N.N) - if (state === 'swift-swift-Next' && proposal.status.version === 'Next') return true // special case - - var version = state.split(/\D+/).filter(function (s) { return s.length }).join('.') - - if (!version.length) return false // it's not a state that represents a version number - if (proposal.status.version === version) return true - return false - }) - }) - } - } - - var filteredProposals = proposals.filter(function (proposal) { - return matchingProposals.indexOf(proposal) === -1 - }) - - matchingProposals.forEach(function (proposal) { - var matchingElements = [].concat.apply([], document.querySelectorAll('.' + proposal.id)) - matchingElements.forEach(function (element) { element.classList.remove('hidden') }) - }) - - filteredProposals.forEach(function (proposal) { - var filteredElements = [].concat.apply([], document.querySelectorAll('.' + proposal.id)) - filteredElements.forEach(function (element) { element.classList.add('hidden') }) - }) - - updateProposalsCount(matchingProposals.length) -} - -/** - * Parses a URI fragment and applies a search and filters to the page. - * - * Syntax (a query string within a fragment): - * fragment --> `#?` parameter-value-list - * parameter-value-list --> parameter-value-pair | parameter-value-pair `&` parameter-value-list - * parameter-value-pair --> parameter `=` value - * parameter --> `proposal` | `status` | `version` | `search` - * value --> ** Any URL-encoded text. ** - * - * For example: - * /#?proposal=SE-0180,SE-0123 - * /#?status=rejected&version=3&search=access - * - * Four types of parameters are supported: - * - proposal: A comma-separated list of proposal IDs. Treated as an 'or' search. - * - filter: A comma-separated list of proposal statuses to apply as a filter. - * - version: A comma-separated list of Swift version numbers to apply as a filter. - * - search: Raw, URL-encoded text used to filter by individual term. - * - * @param {string} fragment - A URI fragment to use as the basis for a search. - */ -function _applyFragment (fragment) { - if (!fragment || fragment.substr(0, 2) !== '#?') return - fragment = fragment.substring(2) // remove the #? - - // use this literal's keys as the source of truth for key-value pairs in the fragment - var actions = { proposal: [], search: null, status: [], version: [] } - - // parse the fragment as a query string - Object.keys(actions).forEach(function (action) { - var pattern = new RegExp(action + '=([^=]+)(&|$)') - var values = fragment.match(pattern) - - if (values) { - var value = values[1] // 1st capture group from the RegExp - if (action === 'search') { - value = decodeURIComponent(value) - } else { - value = value.split(',') - } - - if (action === 'proposal') { - value = value.flatMap(function (id) { - // filter out invalid identifiers. - const output = id.match(/^SE-([0-9]{1,4})$/i) - if (!output) return [] - - // insert missing leading zeros, e.g., 'SE-2' → 'SE-0002'. - return 'SE-' + output[1].padStart(4, '0') - }) - } - - actions[action] = value - } - }) - - // perform key-specific parsing and checks - - if (actions.proposal.length) { - document.querySelector('#search-filter').value = actions.proposal.join(',') - } else if (actions.search) { - document.querySelector('#search-filter').value = actions.search - } - - if (actions.version.length) { - var versionSelections = actions.version.map(function (version) { - return document.querySelector('#filter-by-swift-' + _idSafeName(version)) - }).filter(function (version) { - return !!version - }) - - versionSelections.forEach(function (versionSelection) { - versionSelection.checked = true - }) - - if (versionSelections.length) { - document.querySelector( - '#filter-by-' + states['.implemented'].className - ).checked = true - } - } - - // track this state specifically for toggling the version panel - var implementedSelected = false - - // update the filter selections in the nav - if (actions.status.length) { - var statusSelections = actions.status.map(function (status) { - var stateName = Object.keys(states).filter(function (state) { - return states[state].className === status - })[0] - - if (!stateName) return // fragment contains a nonexistent state - var state = states[stateName] - - if (stateName === '.implemented') implementedSelected = true - - return document.querySelector('#filter-by-' + state.className) - }).filter(function (status) { - return !!status - }) - - statusSelections.forEach(function (statusSelection) { - statusSelection.checked = true - }) - } - - // the version panel needs to be activated if any are specified - if (actions.version.length || implementedSelected) { - ;['#version-options', '#version-options-label'].forEach(function (selector) { - document.querySelector('.filter-options') - .querySelector(selector).classList - .toggle('hidden') - }) - } - - // specifying any filter in the fragment should activate the filters in the UI - if (actions.version.length || actions.status.length) { - toggleFilterPanel() - toggleFiltering() - } - - filterProposals() -} - -/** - * Writes out the current search and filter settings to document.location - * via window.replaceState. - */ -function _updateURIFragment () { - var actions = { proposal: [], search: null, status: [], version: [] } - - var search = document.querySelector('#search-filter') - - if (search.value && search.value.match(/(SE-\d\d\d\d)($|((,SE-\d\d\d\d)+))/i)) { - actions.proposal = search.value.toUpperCase().split(',') - } else { - actions.search = search.value - } - - var selectedVersions = document.querySelectorAll('.filter-by-swift-version:checked') - var versions = [].map.call(selectedVersions, function (checkbox) { - return checkbox.value.split('swift-swift-')[1].split('-').join('.') - }) - - actions.version = versions - - var selectedStatuses = document.querySelectorAll('.filtered-by-status:checked') - var statuses = [].map.call(selectedStatuses, function (checkbox) { - var className = checkbox.value - - var correspondingStatus = Object.keys(states).filter(function (status) { - if (states[status].className === className) return true - return false - })[0] - - return states[correspondingStatus].className - }) - - // .implemented is redundant if any specific implementation versions are selected. - if (actions.version.length) { - statuses = statuses.filter(function (status) { - return status !== states['.implemented'].className - }) - } - - actions.status = statuses - - // build the actual fragment string. - var fragments = [] - if (actions.proposal.length) fragments.push('proposal=' + actions.proposal.join(',')) - if (actions.status.length) fragments.push('status=' + actions.status.join(',')) - if (actions.version.length) fragments.push('version=' + actions.version.join(',')) - - // encoding the search lets you search for `??` and other edge cases. - if (actions.search) fragments.push('search=' + encodeURIComponent(actions.search)) - - if (!fragments.length) { - window.history.replaceState(null, null, './') - return - } - - var fragment = '#?' + fragments.join('&') - - // avoid creating new history entries each time a search or filter updates - window.history.replaceState(null, null, fragment) -} - -/** Helper to give versions like 3.0.1 an okay ID to use in a DOM element. (swift-3-0-1) */ -function _idSafeName (name) { - return 'swift-' + name.replace(/\./g, '-') -} - -/** - * Changes the text after 'Filtered by: ' to reflect the current status filters. - * - * After FILTER_DESCRIPTION_LIMIT filters are explicitly named, start combining the descriptive text - * to just state the number of status filters taking effect, not what they are. - * - * @param {string[]} selectedStateNames - CSS class names corresponding to which statuses were selected. - * Populated from the global `stateNames` array. - */ -function updateFilterDescription (selectedStateNames) { - var FILTER_DESCRIPTION_LIMIT = 2 - var stateCount = selectedStateNames.length - - // Limit the length of filter text on small screens. - if (window.matchMedia('(max-width: 414px)').matches) { - FILTER_DESCRIPTION_LIMIT = 1 - } - - var container = document.querySelector('.toggle-filter-panel') - - // modify the state names to clump together Implemented with version names - var swiftVersionStates = selectedStateNames.filter(function (state) { return state.match(/swift/i) }) - - if (swiftVersionStates.length > 0 && swiftVersionStates.length <= FILTER_DESCRIPTION_LIMIT) { - selectedStateNames = selectedStateNames.filter(function (state) { return !state.match(/swift|implemented/i) }) - .concat('Implemented (' + swiftVersionStates.join(', ') + ')') - } - - if (selectedStateNames.length > FILTER_DESCRIPTION_LIMIT) { - container.innerText = stateCount + ' Filters' - } else if (selectedStateNames.length === 0) { - container.innerText = 'All Statuses' - } else { - selectedStateNames = selectedStateNames.map(cleanNumberFromState) - container.innerText = selectedStateNames.join(' or ') - } -} - -/** Updates the `${n} Proposals` display just above the proposals list. */ -function updateProposalsCount (count) { - var numberField = document.querySelector('#proposals-count-number') - numberField.innerText = (count.toString() + ' proposal' + (count !== 1 ? 's' : '')) -} - -function updateFilterStatus () { - var labels = [].concat.apply([], document.querySelectorAll('#filter-options label')) - labels.forEach(function (label) { - var count = states[label.getAttribute('data-state-key')].count - var cleanedLabel = cleanNumberFromState(label.innerText) - label.innerText = addNumberToState(cleanedLabel, count) - }) -} - -function cleanNumberFromState (state) { - return state.replace(/ *\([^)]*\) */g, '') -} - -function addNumberToState (state, count) { - return state + ' (' + count + ')' -} From 182833e40f24cea91ceda78dcdd907277ecbf332 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 20 Nov 2022 00:14:51 +0000 Subject: [PATCH 2807/4563] Update README.md (#1849) * Add focus-areas and release-process goals. * Use `www` subdomain for blog post links. --- README.md | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a027032055..f30f516525 100644 --- a/README.md +++ b/README.md @@ -11,22 +11,24 @@ This repository tracks the ongoing evolution of Swift. It contains: ## Goals and Release Notes +* [Swift project focus areas in 2023](https://forums.swift.org/t/swift-project-focus-areas-in-2023/61522) * [On the road to Swift 6](https://forums.swift.org/t/on-the-road-to-swift-6/32862) * [CHANGELOG](https://github.com/apple/swift/blob/main/CHANGELOG.md) -| Version | Announced | Released | -| :-------- | :----------------------------------------------------------------------- | :------------------------------------------------------- | -| Swift 5.7 | [2022-03-29](https://forums.swift.org/t/swift-5-7-release-process/56316) | [2022-09-12](https://swift.org/blog/swift-5.7-released/) | -| Swift 5.6 | [2021-11-10](https://forums.swift.org/t/swift-5-6-release-process/53412) | [2022-03-14](https://swift.org/blog/swift-5.6-released/) | -| Swift 5.5 | [2021-03-12](https://forums.swift.org/t/swift-5-5-release-process/45644) | [2021-09-20](https://swift.org/blog/swift-5.5-released/) | -| Swift 5.4 | [2020-11-11](https://forums.swift.org/t/swift-5-4-release-process/41936) | [2021-04-26](https://swift.org/blog/swift-5.4-released/) | -| Swift 5.3 | [2020-03-25](https://swift.org/blog/5.3-release-process/) | [2020-09-16](https://swift.org/blog/swift-5.3-released/) | -| Swift 5.2 | [2019-09-24](https://swift.org/blog/5.2-release-process/) | [2020-03-24](https://swift.org/blog/swift-5.2-released/) | -| Swift 5.1 | [2019-02-18](https://swift.org/blog/5.1-release-process/) | [2019-09-20](https://swift.org/blog/swift-5.1-released/) | -| Swift 5.0 | [2018-09-25](https://swift.org/blog/5.0-release-process/) | [2019-03-25](https://swift.org/blog/swift-5-released/) | -| Swift 4.2 | [2018-02-28](https://swift.org/blog/4.2-release-process/) | [2018-09-17](https://swift.org/blog/swift-4.2-released/) | -| Swift 4.1 | [2017-10-17](https://swift.org/blog/swift-4.1-release-process/) | [2018-03-29](https://swift.org/blog/swift-4.1-released/) | -| Swift 4.0 | [2017-02-16](https://swift.org/blog/swift-4.0-release-process/) | [2017-09-19](https://swift.org/blog/swift-4.0-released/) | -| Swift 3.1 | [2016-12-09](https://swift.org/blog/swift-3.1-release-process/) | [2017-03-27](https://swift.org/blog/swift-3.1-released/) | -| Swift 3.0 | [2016-05-06](https://swift.org/blog/swift-3.0-release-process/) | [2016-09-13](https://swift.org/blog/swift-3.0-released/) | -| Swift 2.2 | [2016-01-05](https://swift.org/blog/swift-2.2-release-process/) | [2016-03-21](https://swift.org/blog/swift-2.2-released/) | +| Version | Announced | Released | +| :-------- | :----------------------------------------------------------------------- | :----------------------------------------------------------- | +| Swift 5.8 | [2022-11-19](https://forums.swift.org/t/swift-5-8-release-process/61540) | +| Swift 5.7 | [2022-03-29](https://forums.swift.org/t/swift-5-7-release-process/56316) | [2022-09-12](https://www.swift.org/blog/swift-5.7-released/) | +| Swift 5.6 | [2021-11-10](https://forums.swift.org/t/swift-5-6-release-process/53412) | [2022-03-14](https://www.swift.org/blog/swift-5.6-released/) | +| Swift 5.5 | [2021-03-12](https://forums.swift.org/t/swift-5-5-release-process/45644) | [2021-09-20](https://www.swift.org/blog/swift-5.5-released/) | +| Swift 5.4 | [2020-11-11](https://forums.swift.org/t/swift-5-4-release-process/41936) | [2021-04-26](https://www.swift.org/blog/swift-5.4-released/) | +| Swift 5.3 | [2020-03-25](https://www.swift.org/blog/5.3-release-process/) | [2020-09-16](https://www.swift.org/blog/swift-5.3-released/) | +| Swift 5.2 | [2019-09-24](https://www.swift.org/blog/5.2-release-process/) | [2020-03-24](https://www.swift.org/blog/swift-5.2-released/) | +| Swift 5.1 | [2019-02-18](https://www.swift.org/blog/5.1-release-process/) | [2019-09-20](https://www.swift.org/blog/swift-5.1-released/) | +| Swift 5.0 | [2018-09-25](https://www.swift.org/blog/5.0-release-process/) | [2019-03-25](https://www.swift.org/blog/swift-5-released/) | +| Swift 4.2 | [2018-02-28](https://www.swift.org/blog/4.2-release-process/) | [2018-09-17](https://www.swift.org/blog/swift-4.2-released/) | +| Swift 4.1 | [2017-10-17](https://www.swift.org/blog/swift-4.1-release-process/) | [2018-03-29](https://www.swift.org/blog/swift-4.1-released/) | +| Swift 4.0 | [2017-02-16](https://www.swift.org/blog/swift-4.0-release-process/) | [2017-09-19](https://www.swift.org/blog/swift-4.0-released/) | +| Swift 3.1 | [2016-12-09](https://www.swift.org/blog/swift-3.1-release-process/) | [2017-03-27](https://www.swift.org/blog/swift-3.1-released/) | +| Swift 3.0 | [2016-05-06](https://www.swift.org/blog/swift-3.0-release-process/) | [2016-09-13](https://www.swift.org/blog/swift-3.0-released/) | +| Swift 2.2 | [2016-01-05](https://www.swift.org/blog/swift-2.2-release-process/) | [2016-03-21](https://www.swift.org/blog/swift-2.2-released/) | From 81f8f83c1d7b6aa92efef32a658b0e3a5007e6d9 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 21 Nov 2022 01:25:46 +0000 Subject: [PATCH 2808/4563] Update README.md (#1850) Use reference-style links in the table. --- README.md | 69 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index f30f516525..c622e9ba59 100644 --- a/README.md +++ b/README.md @@ -15,20 +15,55 @@ This repository tracks the ongoing evolution of Swift. It contains: * [On the road to Swift 6](https://forums.swift.org/t/on-the-road-to-swift-6/32862) * [CHANGELOG](https://github.com/apple/swift/blob/main/CHANGELOG.md) -| Version | Announced | Released | -| :-------- | :----------------------------------------------------------------------- | :----------------------------------------------------------- | -| Swift 5.8 | [2022-11-19](https://forums.swift.org/t/swift-5-8-release-process/61540) | -| Swift 5.7 | [2022-03-29](https://forums.swift.org/t/swift-5-7-release-process/56316) | [2022-09-12](https://www.swift.org/blog/swift-5.7-released/) | -| Swift 5.6 | [2021-11-10](https://forums.swift.org/t/swift-5-6-release-process/53412) | [2022-03-14](https://www.swift.org/blog/swift-5.6-released/) | -| Swift 5.5 | [2021-03-12](https://forums.swift.org/t/swift-5-5-release-process/45644) | [2021-09-20](https://www.swift.org/blog/swift-5.5-released/) | -| Swift 5.4 | [2020-11-11](https://forums.swift.org/t/swift-5-4-release-process/41936) | [2021-04-26](https://www.swift.org/blog/swift-5.4-released/) | -| Swift 5.3 | [2020-03-25](https://www.swift.org/blog/5.3-release-process/) | [2020-09-16](https://www.swift.org/blog/swift-5.3-released/) | -| Swift 5.2 | [2019-09-24](https://www.swift.org/blog/5.2-release-process/) | [2020-03-24](https://www.swift.org/blog/swift-5.2-released/) | -| Swift 5.1 | [2019-02-18](https://www.swift.org/blog/5.1-release-process/) | [2019-09-20](https://www.swift.org/blog/swift-5.1-released/) | -| Swift 5.0 | [2018-09-25](https://www.swift.org/blog/5.0-release-process/) | [2019-03-25](https://www.swift.org/blog/swift-5-released/) | -| Swift 4.2 | [2018-02-28](https://www.swift.org/blog/4.2-release-process/) | [2018-09-17](https://www.swift.org/blog/swift-4.2-released/) | -| Swift 4.1 | [2017-10-17](https://www.swift.org/blog/swift-4.1-release-process/) | [2018-03-29](https://www.swift.org/blog/swift-4.1-released/) | -| Swift 4.0 | [2017-02-16](https://www.swift.org/blog/swift-4.0-release-process/) | [2017-09-19](https://www.swift.org/blog/swift-4.0-released/) | -| Swift 3.1 | [2016-12-09](https://www.swift.org/blog/swift-3.1-release-process/) | [2017-03-27](https://www.swift.org/blog/swift-3.1-released/) | -| Swift 3.0 | [2016-05-06](https://www.swift.org/blog/swift-3.0-release-process/) | [2016-09-13](https://www.swift.org/blog/swift-3.0-released/) | -| Swift 2.2 | [2016-01-05](https://www.swift.org/blog/swift-2.2-release-process/) | [2016-03-21](https://www.swift.org/blog/swift-2.2-released/) | +| Version | Announced | Released | +| :-------- | :------------- | :------------- | +| Swift 5.8 | [2022-11-19][] | | +| Swift 5.7 | [2022-03-29][] | [2022-09-12][] | +| Swift 5.6 | [2021-11-10][] | [2022-03-14][] | +| Swift 5.5 | [2021-03-12][] | [2021-09-20][] | +| Swift 5.4 | [2020-11-11][] | [2021-04-26][] | +| Swift 5.3 | [2020-03-25][] | [2020-09-16][] | +| Swift 5.2 | [2019-09-24][] | [2020-03-24][] | +| Swift 5.1 | [2019-02-18][] | [2019-09-20][] | +| Swift 5.0 | [2018-09-25][] | [2019-03-25][] | +| Swift 4.2 | [2018-02-28][] | [2018-09-17][] | +| Swift 4.1 | [2017-10-17][] | [2018-03-29][] | +| Swift 4.0 | [2017-02-16][] | [2017-09-19][] | +| Swift 3.1 | [2016-12-09][] | [2017-03-27][] | +| Swift 3.0 | [2016-05-06][] | [2016-09-13][] | +| Swift 2.2 | [2016-01-05][] | [2016-03-21][] | + + + +[2022-11-19]: +[2022-03-29]: +[2021-11-10]: +[2021-03-12]: +[2020-11-11]: +[2020-03-25]: +[2019-09-24]: +[2019-02-18]: +[2018-09-25]: +[2018-02-28]: +[2017-10-17]: +[2017-02-16]: +[2016-12-09]: +[2016-05-06]: +[2016-01-05]: + + + +[2022-09-12]: +[2022-03-14]: +[2021-09-20]: +[2021-04-26]: +[2020-09-16]: +[2020-03-24]: +[2019-09-20]: +[2019-03-25]: +[2018-09-17]: +[2018-03-29]: +[2017-09-19]: +[2017-03-27]: +[2016-09-13]: +[2016-03-21]: From 84d3470d9a89ef83013ccb20af93bacea3a0db72 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 22 Nov 2022 05:44:23 +0000 Subject: [PATCH 2809/4563] Update README.md (#1851) Use inline-style links, in case of duplicate dates. --- README.md | 69 ++++++++++++++----------------------------------------- 1 file changed, 17 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index c622e9ba59..f30f516525 100644 --- a/README.md +++ b/README.md @@ -15,55 +15,20 @@ This repository tracks the ongoing evolution of Swift. It contains: * [On the road to Swift 6](https://forums.swift.org/t/on-the-road-to-swift-6/32862) * [CHANGELOG](https://github.com/apple/swift/blob/main/CHANGELOG.md) -| Version | Announced | Released | -| :-------- | :------------- | :------------- | -| Swift 5.8 | [2022-11-19][] | | -| Swift 5.7 | [2022-03-29][] | [2022-09-12][] | -| Swift 5.6 | [2021-11-10][] | [2022-03-14][] | -| Swift 5.5 | [2021-03-12][] | [2021-09-20][] | -| Swift 5.4 | [2020-11-11][] | [2021-04-26][] | -| Swift 5.3 | [2020-03-25][] | [2020-09-16][] | -| Swift 5.2 | [2019-09-24][] | [2020-03-24][] | -| Swift 5.1 | [2019-02-18][] | [2019-09-20][] | -| Swift 5.0 | [2018-09-25][] | [2019-03-25][] | -| Swift 4.2 | [2018-02-28][] | [2018-09-17][] | -| Swift 4.1 | [2017-10-17][] | [2018-03-29][] | -| Swift 4.0 | [2017-02-16][] | [2017-09-19][] | -| Swift 3.1 | [2016-12-09][] | [2017-03-27][] | -| Swift 3.0 | [2016-05-06][] | [2016-09-13][] | -| Swift 2.2 | [2016-01-05][] | [2016-03-21][] | - - - -[2022-11-19]: -[2022-03-29]: -[2021-11-10]: -[2021-03-12]: -[2020-11-11]: -[2020-03-25]: -[2019-09-24]: -[2019-02-18]: -[2018-09-25]: -[2018-02-28]: -[2017-10-17]: -[2017-02-16]: -[2016-12-09]: -[2016-05-06]: -[2016-01-05]: - - - -[2022-09-12]: -[2022-03-14]: -[2021-09-20]: -[2021-04-26]: -[2020-09-16]: -[2020-03-24]: -[2019-09-20]: -[2019-03-25]: -[2018-09-17]: -[2018-03-29]: -[2017-09-19]: -[2017-03-27]: -[2016-09-13]: -[2016-03-21]: +| Version | Announced | Released | +| :-------- | :----------------------------------------------------------------------- | :----------------------------------------------------------- | +| Swift 5.8 | [2022-11-19](https://forums.swift.org/t/swift-5-8-release-process/61540) | +| Swift 5.7 | [2022-03-29](https://forums.swift.org/t/swift-5-7-release-process/56316) | [2022-09-12](https://www.swift.org/blog/swift-5.7-released/) | +| Swift 5.6 | [2021-11-10](https://forums.swift.org/t/swift-5-6-release-process/53412) | [2022-03-14](https://www.swift.org/blog/swift-5.6-released/) | +| Swift 5.5 | [2021-03-12](https://forums.swift.org/t/swift-5-5-release-process/45644) | [2021-09-20](https://www.swift.org/blog/swift-5.5-released/) | +| Swift 5.4 | [2020-11-11](https://forums.swift.org/t/swift-5-4-release-process/41936) | [2021-04-26](https://www.swift.org/blog/swift-5.4-released/) | +| Swift 5.3 | [2020-03-25](https://www.swift.org/blog/5.3-release-process/) | [2020-09-16](https://www.swift.org/blog/swift-5.3-released/) | +| Swift 5.2 | [2019-09-24](https://www.swift.org/blog/5.2-release-process/) | [2020-03-24](https://www.swift.org/blog/swift-5.2-released/) | +| Swift 5.1 | [2019-02-18](https://www.swift.org/blog/5.1-release-process/) | [2019-09-20](https://www.swift.org/blog/swift-5.1-released/) | +| Swift 5.0 | [2018-09-25](https://www.swift.org/blog/5.0-release-process/) | [2019-03-25](https://www.swift.org/blog/swift-5-released/) | +| Swift 4.2 | [2018-02-28](https://www.swift.org/blog/4.2-release-process/) | [2018-09-17](https://www.swift.org/blog/swift-4.2-released/) | +| Swift 4.1 | [2017-10-17](https://www.swift.org/blog/swift-4.1-release-process/) | [2018-03-29](https://www.swift.org/blog/swift-4.1-released/) | +| Swift 4.0 | [2017-02-16](https://www.swift.org/blog/swift-4.0-release-process/) | [2017-09-19](https://www.swift.org/blog/swift-4.0-released/) | +| Swift 3.1 | [2016-12-09](https://www.swift.org/blog/swift-3.1-release-process/) | [2017-03-27](https://www.swift.org/blog/swift-3.1-released/) | +| Swift 3.0 | [2016-05-06](https://www.swift.org/blog/swift-3.0-release-process/) | [2016-09-13](https://www.swift.org/blog/swift-3.0-released/) | +| Swift 2.2 | [2016-01-05](https://www.swift.org/blog/swift-2.2-release-process/) | [2016-03-21](https://www.swift.org/blog/swift-2.2-released/) | From fef60339945be0584b990cfc40b2f914dc3b57a4 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 23 Nov 2022 03:11:41 +0000 Subject: [PATCH 2810/4563] [SE-0378] Fix "missing or invalid dates" warning (#1852) --- proposals/0378-package-registry-auth.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0378-package-registry-auth.md b/proposals/0378-package-registry-auth.md index 4b4a557a43..2759381ed2 100644 --- a/proposals/0378-package-registry-auth.md +++ b/proposals/0378-package-registry-auth.md @@ -3,10 +3,11 @@ * Proposal: [SE-0378](0378-package-registry-auth.md) * Author: [Yim Lee](https://github.com/yim-lee) * Review Manager: [Tom Doron](https://github.com/tomerd) -* Status: **Active Review (Nov 14 - Dec 2, 2022)** +* Status: **Active Review (November 14 - December 2, 2022)** * Implementation: [apple/swift-package-manager#5838](https://github.com/apple/swift-package-manager/pull/5838) * Review: * Pitch: https://forums.swift.org/t/pitch-package-registry-authentication/61047 + * Review: https://forums.swift.org/t/se-0378-swift-package-registry-authentication/61436 ## Introduction From 18c422e0c6a2d6eeef15735c5a87b980484506fc Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Sun, 27 Nov 2022 19:20:42 -0800 Subject: [PATCH 2811/4563] [SE-0376] Rename `backDeploy(before:)` to `backDeployed(upTo:)`. --- proposals/0376-function-back-deployment.md | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index 03e98285e3..3747f57e71 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -62,35 +62,35 @@ While `@_alwaysEmitIntoClient` can be used to back deploy APIs, there are some d ## Proposed solution -Add a `@backDeploy(before: ...)` attribute to Swift that can be used to indicate that a copy of the function should be emitted into the client to be used at runtime when executing on an OS prior to a specific version. The attribute can be adopted by ToastKit's authors like this: +Add a `@backDeployed(upTo: ...)` attribute to Swift that can be used to indicate that a copy of the function should be emitted into the client to be used at runtime when executing on an OS prior to a specific version. The attribute can be adopted by ToastKit's authors like this: ```swift extension Toaster { @available(toasterOS 1.0, *) - @backDeploy(before: toasterOS 2.0) + @backDeployed(upTo: toasterOS 2.0) public func makeBatchOfToast(_ breadSlices: [BreadSlice]) -> [Toast] { ... } } ``` -The API is now available on toasterOS 1.0 and later so clients may now reference `makeBatchOfToast(_:)` unconditionally. The compiler detects applications of `makeBatchOfToast(_:)` and generates code to automatically handle the potential runtime unavailability of the API. +The API is now available on toasterOS 1.0 and later so clients may now reference `makeBatchOfToast(_:)` unconditionally. The compiler detects applications of `makeBatchOfToast(_:)` and generates code to automatically handle the potentially runtime unavailability of the API. ## Detailed design -The `@backDeploy` attribute may apply to functions, methods, and subscripts. Properties may also have the attribute as long as the they do not have storage. The attribute takes a comma separated list of one or more platform versions, so declarations that are available on more than one platform can be back deployed to multiple platforms with a single attribute. The following are examples of legal uses of the attribute: +The `@backDeployed` attribute may apply to functions, methods, and subscripts. Properties may also have the attribute as long as the they do not have storage. The attribute takes a comma separated list of one or more platform versions, so declarations that are available on more than one platform can be back deployed to multiple platforms with a single attribute. The following are examples of legal uses of the attribute: ```swift extension Temperature { @available(toasterOS 1.0, ovenOS 1.0, *) - @backDeploy(before: toasterOS 2.0, ovenOS 2.0) + @backDeployed(upTo: toasterOS 2.0, ovenOS 2.0) public var degreesFahrenheit: Double { - return (degreesCelsius * 9 / 5) + 32 + return (degreesCelcius * 9 / 5) + 32 } } extension Toaster { /// Returns whether the slot at the given index can fit a bagel. @available(toasterOS 1.0, *) - @backDeploy(before: toasterOS 2.0) + @backDeployed(upTo: toasterOS 2.0) public subscript(fitsBagelsAt index: Int) -> Bool { get { return index < 2 } } @@ -132,18 +132,18 @@ When the deployment target of the client app is at least toasterOS 2.0, the opti ### Restrictions on declarations that may be back deployed -There are rules that limit which declarations may have a `@backDeploy` attribute: +There are rules that limit which declarations may have a `@backDeployed` attribute: * The declaration must be `public` or `@usableFromInline` since it only makes sense to offer back deployment for declarations that would be used by other modules. -* Only functions that can be invoked with static dispatch are eligible to back deploy, so back deployed instance and class methods must be `final`. The `@objc` attribute also implies dynamic dispatch and therefore is incompatible with `@backDeploy`. +* Only functions that can be invoked with static dispatch are eligible to back deploy, so back deployed instance and class methods must be `final`. The `@objc` attribute also implies dynamic dispatch and therefore is incompatible with `@backDeployed`. * Explicit availability must be specified with `@available` on the same declaration for each of the platforms that the declaration is back deployed on. -* The declaration should be available earlier than the platform versions specified in `@backDeploy` (otherwise the fallback functions would never be called). -* The `@_alwaysEmitIntoClient` and `@_transparent` attributes are incompatible with `@backDeploy` because they require the function body to always be emitted into the client, defeating the purpose of `@backDeploy`. -* Declarations with `@inlinable` _may_ use `@backDeploy`. As usual with `@inlinable`, the bodies of these functions may be emitted into the client at the discretion of the optimizer. The copy of the function in the client may therefore be used even when a copy of the function is available in the library. +* The declaration should be available earlier than the platform versions specified in `@backDeployed` (otherwise the fallback functions would never be called). +* The `@_alwaysEmitIntoClient` and `@_transparent` attributes are incompatible with `@backDeployed` because they require the function body to always be emitted into the client, defeating the purpose of `@backDeployed`. +* Declarations with `@inlinable` _may_ use `@backDeployed`. As usual with `@inlinable`, the bodies of these functions may be emitted into the client at the discretion of the optimizer. The copy of the function in the client may therefore be used even when a copy of the function is available in the library. ### Requirements for the bodies of back deployed functions -The restrictions on the bodies of back deployed functions are the same as `@inlinable` functions. The body may only reference declarations that are accessible to the client, such as `public` and `@usableFromInline` declarations. Similarly, those referenced declarations must also be at least as available the back deployed function, or `if #available` must be used to handle potential unavailability. Type checking in `@backDeploy` function bodies must ignore the library's deployment target since the body will be copied into clients with unknown deployment targets. +The restrictions on the bodies of back deployed functions are the same as `@inlinable` functions. The body may only reference declarations that are accessible to the client, such as `public` and `@usableFromInline` declarations. Similarly, those referenced declarations must also be at least as available the back deployed function, or `if #available` must be used to handle potential unavailability. Type checking in `@backDeployed` function bodies must ignore the library's deployment target since the body will be copied into clients with unknown deployment targets. ## Source compatibility @@ -151,11 +151,11 @@ The introduction of this attribute to the language is an additive change and the ## Effect on ABI stability -The `@backDeploy` attribute has no effect on the ABI of Swift libraries. A Swift function with and without a `@backDeploy` attribute has the same ABI; the attribute simply controls whether the compiler automatically generates additional logic in the client module. The thunk and fallback functions that are emitted into the client do have a special mangling to disambiguate them from the original function in the library, but these symbols are never referenced across separately compiled modules. +The `@backDeployed` attribute has no effect on the ABI of Swift libraries. A Swift function with and without a `@backDeployed` attribute has the same ABI; the attribute simply controls whether the compiler automatically generates additional logic in the client module. The thunk and fallback functions that are emitted into the client do have a special mangling to disambiguate them from the original function in the library, but these symbols are never referenced across separately compiled modules. ## Effect on API resilience -By itself, adding a `@backDeploy` attribute to a declaration does not affect source compatibility for clients of a library, and neither does removing the attribute. However, adding a `@backDeploy` attribute would typically be done simultaneously with expanding the availability of the declaration. Expansion of the availability of an API is source compatible for clients, but reversing that expansion would not be. +By itself, adding a `@backDeployed` attribute to a declaration does not affect source compatibility for clients of a library, and neither does removing the attribute. However, adding a `@backDeployed` attribute would typically be done simultaneously with expanding the availability of the declaration. Expansion of the availability of an API is source compatible for clients, but reversing that expansion would not be. ## Alternatives considered From 57552fb946686fc25015a004fd320915b2f7d2c5 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Mon, 28 Nov 2022 17:01:17 -0500 Subject: [PATCH 2812/4563] Return SE-0376 for revision --- proposals/0376-function-back-deployment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index 3747f57e71..12b95cb32b 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -4,8 +4,8 @@ * Author: [Allan Shortlidge](https://github.com/tshortli) * Implementation: [apple/swift#41271](https://github.com/apple/swift/pull/41271), [apple/swift#41348](https://github.com/apple/swift/pull/41348), [apple/swift#41416](https://github.com/apple/swift/pull/41416), [apple/swift#41612](https://github.com/apple/swift/pull/41612) as the underscored attribute `@_backDeploy` * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) -* Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) -* Status: **Active review (October 25 ... November 7, 2022)** +* Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0376-function-back-deployment/61507)) +* Status: **Returned for revision** ## Introduction From fcfd58e8f479766aa8a4528c6573720cef127555 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Mon, 28 Nov 2022 17:18:41 -0500 Subject: [PATCH 2813/4563] Put SE-0376 back in review --- proposals/0376-function-back-deployment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index 12b95cb32b..c2d47a5624 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -4,8 +4,8 @@ * Author: [Allan Shortlidge](https://github.com/tshortli) * Implementation: [apple/swift#41271](https://github.com/apple/swift/pull/41271), [apple/swift#41348](https://github.com/apple/swift/pull/41348), [apple/swift#41416](https://github.com/apple/swift/pull/41416), [apple/swift#41612](https://github.com/apple/swift/pull/41612) as the underscored attribute `@_backDeploy` * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) -* Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0376-function-back-deployment/61507)) -* Status: **Returned for revision** +* Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0376-function-back-deployment/61507)) ([second review](https://forums.swift.org/t/se-0376-second-review-function-back-deployment/61671)) +* Status: **Active review (November 28 ... December 12, 2022)** ## Introduction From d969b4e08983c86bf037f3ed113a8610c881405d Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 30 Nov 2022 10:11:59 -0800 Subject: [PATCH 2814/4563] Christen SE-0379 for opt-in reflection, and kick off review --- ...ion-metadata.md => 0379-opt-in-reflection-metadata.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{NNNN-opt-in-reflection-metadata.md => 0379-opt-in-reflection-metadata.md} (98%) diff --git a/proposals/NNNN-opt-in-reflection-metadata.md b/proposals/0379-opt-in-reflection-metadata.md similarity index 98% rename from proposals/NNNN-opt-in-reflection-metadata.md rename to proposals/0379-opt-in-reflection-metadata.md index 5605efc9ce..98dd29c981 100644 --- a/proposals/NNNN-opt-in-reflection-metadata.md +++ b/proposals/0379-opt-in-reflection-metadata.md @@ -1,10 +1,10 @@ # Swift Opt-In Reflection Metadata -* Proposal: [SE-NNNN](NNNN-opt-in-reflection-metadata.md) +* Proposal: [SE-0379](0379-opt-in-reflection-metadata.md) * Authors: [Max Ovtsin](https://github.com/maxovtsin) -* Review Manager: TBD -* Status: **Awaiting implementation** +* Review Manager: [Joe Groff](https://github.com/jckarter) +* Status: **Active review (November 30...December 14, 2022)** * Implementation: [apple/swift#34199](https://github.com/apple/swift/pull/34199) ## Introduction @@ -243,4 +243,4 @@ If this proposal is approved, it will become easier and more native to migrate C ## Acknowledgments -Thanks to [Joe Groff](https://github.com/jckarter) for various useful pieces of advice, general help along the way, and for suggesting several useful features like Reflectable casts! \ No newline at end of file +Thanks to [Joe Groff](https://github.com/jckarter) for various useful pieces of advice, general help along the way, and for suggesting several useful features like Reflectable casts! From 2bc25b8bd4b180928ae6cb619ec27467f57d6a2d Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 30 Nov 2022 11:00:29 -0800 Subject: [PATCH 2815/4563] Update 0379-opt-in-reflection-metadata.md fix typos --- proposals/0379-opt-in-reflection-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0379-opt-in-reflection-metadata.md b/proposals/0379-opt-in-reflection-metadata.md index 98dd29c981..69a7586b37 100644 --- a/proposals/0379-opt-in-reflection-metadata.md +++ b/proposals/0379-opt-in-reflection-metadata.md @@ -33,7 +33,7 @@ A developer has two ways right now - either 1. To just in case enable Reflection in full. 2. To try to guess which used APIs consume Reflection, and enable it only for modules that are users of such APIs. -Both of those options have flaws. The first one leads to exsessive contribution of reflection metadta to binary size and might affects the secrecy of generated code. +Both of those options have flaws. The first one leads to excessive contribution of reflection metadata to binary size and might affects the secrecy of generated code. The second one isn't safe because many APIs are black boxes if the guess is wrong, an app might behave not as expected at runtime. Furthermore, APIs can use Reflection Metadata differently. Some like `print`, `debugPrint`, and `dump` will still work with disabled reflection, but the output will be limited. From 61aa2daa37e447a815d7075d11d79d166bbcf04d Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 30 Nov 2022 11:10:36 -0800 Subject: [PATCH 2816/4563] suggestions for readability. --- proposals/0379-opt-in-reflection-metadata.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0379-opt-in-reflection-metadata.md b/proposals/0379-opt-in-reflection-metadata.md index 69a7586b37..af49b7f384 100644 --- a/proposals/0379-opt-in-reflection-metadata.md +++ b/proposals/0379-opt-in-reflection-metadata.md @@ -47,11 +47,11 @@ If for some reason a user module was compiled with metadata generation disabled, between state and representation which will make such API less safe since it becomes a runtime issue rather than a compile-time one. On the other hand, excessive Reflection metadata may be preserved in a binary even if not used, because there is currently no way to statically determine its usage. -There was an attempt to limit the amount of unused reflection metadata by improving its stripability by the Dead Code Elimination LLVM pass, but in many cases, -it’s still preserved in the binary because it’s referenced by Full Type Metadata which prevents Reflection Metadata from stripping. +There was an attempt to limit the amount of unused reflection metadata by improving the Dead Code Elimination LLVM pass, but in many cases +it’s still preserved in the binary because it’s referenced by Full Type Metadata. This prevents Reflection Metadata from being stripped. This unnecessarily increases the binary size and may simplify reverse-engineering. -Introducing a static compilation check can help to solve both of mentioned issues by adding to the language a way to express the requirement to have Reflection metadata at runtime. +Introducing a static compilation check can help to solve both of mentioned issues by adding to the language a way to express the requirement to have Reflection metadata available at runtime. ## Proposed solution From 7593ddc0c092b74bb9172e31e18002172b243274 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 30 Nov 2022 11:29:25 -0800 Subject: [PATCH 2817/4563] suggestion for readability --- proposals/0379-opt-in-reflection-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0379-opt-in-reflection-metadata.md b/proposals/0379-opt-in-reflection-metadata.md index af49b7f384..4bd88dbe7f 100644 --- a/proposals/0379-opt-in-reflection-metadata.md +++ b/proposals/0379-opt-in-reflection-metadata.md @@ -163,7 +163,7 @@ consume(Bar()) Since Reflection metadata might be used by the debugger, we propose to always keep that metadata if full emission of debugging information is enabled (with `-gdwarf-types` or `-g` flags). However, such Reflection metadata won't be accessible through the nominal type descriptor -which will allow to avoid inconsistencies between APIs' outputs in Release and Debug modes. +which will avoid inconsistencies in API behavior between Release and Debug modes. ### Changes in flags From 943252dca0985e9a051e4ed99c642ce2dfeb7684 Mon Sep 17 00:00:00 2001 From: Max Ovtsin Date: Mon, 5 Dec 2022 11:38:49 +0000 Subject: [PATCH 2818/4563] opt-in reflection minor syntactical changes --- proposals/0379-opt-in-reflection-metadata.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0379-opt-in-reflection-metadata.md b/proposals/0379-opt-in-reflection-metadata.md index 4bd88dbe7f..5ba58050e9 100644 --- a/proposals/0379-opt-in-reflection-metadata.md +++ b/proposals/0379-opt-in-reflection-metadata.md @@ -30,11 +30,11 @@ Currently, there is no way to selectively enable the emission of reflectable met Moreover, compiler's flags exist that allow to completely disable emission. A developer has two ways right now - either -1. To just in case enable Reflection in full. -2. To try to guess which used APIs consume Reflection, and enable it only for modules that are users of such APIs. +1. To enable Reflection in full just in case. +2. To try to guess which used APIs consume Reflection, and enable it only for modules that use such APIs. Both of those options have flaws. The first one leads to excessive contribution of reflection metadata to binary size and might affects the secrecy of generated code. -The second one isn't safe because many APIs are black boxes if the guess is wrong, an app might behave not as expected at runtime. +The second one isn't safe because many APIs are black boxes. If the developer's guess is wrong, an app might behave not as expected at runtime. Furthermore, APIs can use Reflection Metadata differently. Some like `print`, `debugPrint`, and `dump` will still work with disabled reflection, but the output will be limited. Others, like SwiftUI, rely on it and won't work correctly if the reflection metadata is missing. @@ -133,7 +133,7 @@ Library authors will have to prepare their APIs for Swift 6 and introduce generi We also propose to deprecate the compiler's options that can lead to missing reflection - `-reflection-metadata-for-debugger-only` and `-disable-reflection-metadata` and starting with Swift 6, ignore these arguments in favor of the default opt-in mode. -### Stdlib behavior changes +### No stdlib behavior changes In Swift `Mirror(reflecting:)` is the only official way to access Reflection metadata, all other APIs are using it under the hood. We intentionally do not propose adding a Reflectable constraint on Mirror type, because it would impose restrictions on those developers who still don't want to require it and consume Reflection optionally. From 8ad52c367a52fa8a05a9a6b9bb8b90be6f0470db Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 6 Dec 2022 17:00:21 +0000 Subject: [PATCH 2819/4563] Update commonly_proposed.md (#1862) The deferred status is no longer used. Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- commonly_proposed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commonly_proposed.md b/commonly_proposed.md index 039c4566aa..2c2c2ba6a2 100644 --- a/commonly_proposed.md +++ b/commonly_proposed.md @@ -2,7 +2,7 @@ This is a list of changes to the Swift language that are frequently proposed but that are unlikely to be accepted. If you're interested in pursuing something in this space, please familiarize yourself with the discussions that we have already had. In order to bring one of these topics up, you'll be expected to add new information to the discussion, not just to say, "I really want this" or "this exists in some other language and I liked it there". -Additionally, proposals for out-of-scope changes will not be scheduled for review. The [readme file](README.md) identifies a list of priorities for the next major release of Swift, and the [proposal review status page](https://apple.github.io/swift-evolution/) includes a list of changes that have been deferred for future discussion because they were deemed to be out of scope at the time of review (in addition to a list of changes proposed and rejected after a formal review). +Additionally, proposals for out-of-scope changes will not be scheduled for review. The [readme file](README.md) identifies a list of priorities for the next major release of Swift, and the [dashboard](https://www.swift.org/swift-evolution/) includes a list of changes that have been rejected after a formal review. Several of the discussions below refer to "C family" languages. This is intended to mean the extended family of languages that resemble C at a syntactic level, such as C++, C#, Objective-C, Java, and Javascript. Swift embraces its C heritage. Where it deviates from other languages in the family, it does so because the feature was thought actively harmful (such as the pre/post-increment `++`) or to reduce needless clutter (such as `;` or parentheses in `if` statements). From 198801d9023f0f7d410bacd4b159ea6a06971dbf Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 6 Dec 2022 17:25:36 +0000 Subject: [PATCH 2820/4563] Update CONTRIBUTING.md Remove the "status page" guidelines. The new dashboard is governed by: --- CONTRIBUTING.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7817678224..8807a1a2ea 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,14 +3,3 @@ ## Participating in the Swift Evolution Process See [Participating in the Swift Evolution Process](https://swift.org/contributing/#participating-in-the-swift-evolution-process) on Swift.org and [process.md](process.md). - -## Contributing to the status page -The [status page](https://apple.github.io/swift-evolution/) shows community activity related to the Swift Evolution Process. Changes to it should focus on supporting the efforts of the community in that process. - -Before making a pull request to change the status page, consider the following steps: - -- **Socialize the idea**: Is there a broader desire in the Swift community for the feature? Ask. Ensure that the feature can be implemented using data from the Swift project itself, rather than depending externally derived data. - -- **Develop a working copy**: Ideas are easiest to understand with a complete implementation. A quick prototype may be good enough for early stage feedback. Once the idea is understood, clean up the code, test it, and format it according to [JavaScript Standard](http://standardjs.com) style. - -- **Request a review**: Initiate a pull request to the [swift-evolution repository](https://github.com/apple/swift-evolution) when a proposed change has a complete implementation. Include a link to a working copy, then assign the review to @krilnon. When everything looks good, the pull request will be merged. \ No newline at end of file From 0ffaed194e0dc2450287483f814122980e2ac628 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 6 Dec 2022 17:43:30 +0000 Subject: [PATCH 2821/4563] Update CONTRIBUTING.md Transfer the guidelines from README.md --- CONTRIBUTING.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8807a1a2ea..7acc2666a5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,8 @@ -# Contributing +# Contributing to Swift Evolution -## Participating in the Swift Evolution Process +Before you initiate a pull request, please read: -See [Participating in the Swift Evolution Process](https://swift.org/contributing/#participating-in-the-swift-evolution-process) on Swift.org and [process.md](process.md). +* [Swift Evolution Process](process.md) +* [Commonly Rejected Changes](commonly_proposed.md) + +Ideas should be thoroughly discussed on the [forums](https://forums.swift.org/c/evolution/18) first. From 381bd04b960c667bb451fd3b3a191b40d07dde1c Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 6 Dec 2022 17:46:17 +0000 Subject: [PATCH 2822/4563] Update README.md Transfer the guidelines to CONTRIBUTING.md --- README.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/README.md b/README.md index f30f516525..1a37cc4c8b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,6 @@ # Swift Programming Language Evolution -**Before you initiate a pull request**, please read the [process](process.md) document. -Ideas should be thoroughly discussed on the [swift-evolution forums](https://swift.org/community/#swift-evolution) first. - -This repository tracks the ongoing evolution of Swift. It contains: - -* The [status page](https://apple.github.io/swift-evolution/), tracking proposals to change Swift. -* The [process](process.md) document that governs the evolution of Swift. -* [Commonly Rejected Changes](commonly_proposed.md), proposals that have been denied in the past. +This repository tracks the ongoing evolution of Swift. ## Goals and Release Notes From 183450456a159deebb6db37fc384efdbca4b5893 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 6 Dec 2022 17:58:25 +0000 Subject: [PATCH 2823/4563] Update README.md Update the main heading and description. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1a37cc4c8b..7424e1a88d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -# Swift Programming Language Evolution +# Swift Evolution -This repository tracks the ongoing evolution of Swift. + + +This repository tracks the ongoing evolution of the Swift programming language, standard library, and package manager. ## Goals and Release Notes From dab882750005df7becedf537381e6b936fe2aef9 Mon Sep 17 00:00:00 2001 From: "Alexandre H. Saad" Date: Tue, 6 Dec 2022 18:34:43 +0000 Subject: [PATCH 2824/4563] Fixed code blocks with Swift highlighting (#1863) --- proposals/0377-parameter-ownership-modifiers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index bfb4854fd0..2a6209b8b1 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -171,7 +171,7 @@ and will not be evident in any copies of the value that might still exist in the caller. This makes it easy to take advantage of the uniqueness of values after ownership transfer to do efficient local mutations of the value: -``` +```swift extension String { // Append `self` to another String, using in-place modification if // possible @@ -436,7 +436,7 @@ When protocol requirements have parameters of move-only type, the ownership convention of the corresponding parameters in an implementing method will need to match exactly, since they cannot be implicitly copied in a thunk: -``` +```swift protocol WritableToFileHandle { func write(to fh: borrow FileHandle) } From 23508022083e02d439a25c5bd187655e849f869b Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 6 Dec 2022 22:36:23 +0000 Subject: [PATCH 2825/4563] [SE-0368] StaticBigInt: update the proposal status (#1866) --- proposals/0368-staticbigint.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index 0450d35e8f..90c1fc0495 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -3,10 +3,9 @@ * Proposal: [SE-0368](0368-staticbigint.md) * Author: [Ben Rimmington](https://github.com/benrimmington) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 5.8)** * Implementation: [apple/swift#40722](https://github.com/apple/swift/pull/40722) -* Review: ([pitch](https://forums.swift.org/t/staticbigint/54545))([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) -* Decision notes: [Acceptance](https://forums.swift.org/t/accepted-se-0368-staticbigint/59962) +* Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) ([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) ([acceptance](https://forums.swift.org/t/accepted-se-0368-staticbigint/59962))
    Revision history From 9d5f41253d4cc2010032cf7818bbc90db6906c9b Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 7 Dec 2022 09:46:40 -0800 Subject: [PATCH 2826/4563] if/switch expressions proposal (#1856) * First draft * factor in pitch feedback * Flesh out -> alternative, add source break * Update NNNN-if-switch-expressions.md * Update NNNN-if-switch-expressions.md * Update NNNN-if-switch-expressions.md * fix missing close-code-block * Assign SE-0380 to if/switch expressions and schedule for review. Co-authored-by: Holly Borla --- proposals/0380-if-switch-expressions.md | 473 ++++++++++++++++++++++++ 1 file changed, 473 insertions(+) create mode 100644 proposals/0380-if-switch-expressions.md diff --git a/proposals/0380-if-switch-expressions.md b/proposals/0380-if-switch-expressions.md new file mode 100644 index 0000000000..22a366b08a --- /dev/null +++ b/proposals/0380-if-switch-expressions.md @@ -0,0 +1,473 @@ +# `if` and `switch` expressions + +* Proposal: [SE-0380](0380-if-switch-expressions.md) +* Authors: [Ben Cohen](https://github.com/airspeedswift), [Hamish Knight](https://github.com/hamishknight) +* Review Manager: [Holly Borla](https://github.com/hborla) +* Status: **Active Review (Decembe 7...December 21, 2022)** +* Implementation: [apple/swift#612178](https://github.com/apple/swift/pull/62178), including a downloadable toolchain. + +## Introduction + +This proposal introduces the ability to use `if` and `switch` statements as expressions, for the purpose of: +- Returning values from functions, properties, and closures; +- Assigning values to variables; and +- Declaring variables. + +## Motivation + +Swift has always had a terse but readable syntax for closures, which allows the `return` to be omitted when the body is a single expression. [SE-0255: Implicit Returns from Single-Expression Functions](https://github.com/apple/swift-evolution/blob/master/proposals/0255-omit-return.md) extended this to functions and properties with a single expression as their body. + +This omission of the `return` keyword is in keeping with Swift's low-ceremony approach, and is in common with many of Swift's peer "modern" languages. However, Swift differs from its peers in the lack support for `if` and `switch` expressions. + +In some cases, this causes ceremony to make a return (ahem), for example: + +```swift +public static func width(_ x: Unicode.Scalar) -> Int { + switch x.value { + case 0..<0x80: return 1 + case 0x80..<0x0800: return 2 + case 0x0800..<0x1_0000: return 3 + default: return 4 + } +} +``` + +In other cases, a user might be tempted to lean heavily on the harder-to-read ternary syntax: + +```swift +let bullet = isRoot && (count == 0 || !willExpand) ? "" + : count == 0 ? "- " + : maxDepth <= 0 ? "▹ " : "▿ " +``` + +Opinions vary on this kind of code, from enthusiasm to horror, but it's accepted that it's reasonable to find this syntax _too_ terse. + +Another option is to use Swift's definite initialization feature: + +```swift +let bullet: String +if isRoot && (count == 0 || !willExpand) { bullet = "" } +else if count == 0 { bullet = "- " } +else if maxDepth <= 0 { bullet = "▹ " } +else { bullet = "▿ " } +``` + +Not only does this add the ceremony of explicit typing and assignment on each branch (perhaps tempting overly terse variable names), it is only practical when the type is easily known. It cannot be used with opaque types, and is very inconvenient and ugly if the type is a complex nested generic. + +Programmers less familiar with Swift might not know this technique, so they may be tempted to take the approach of `var bullet = ""`. This is more bug prone where the default value may not be desired in _any_ circumstances, but definitive initialization won't ensure that it's overridden. + +Finally, a closure can be used to simulate an `if` expression: + +```swift +let bullet = { + if isRoot && (count == 0 || !willExpand) { return "" } + else if count == 0 { return "- " } + else if maxDepth <= 0 { return "▹ " } + else { return "▿ " } +}() +``` + +This also requires `return`s, plus some closure ceremony. But here the `return`s are more than ceremony – they require extra cognitive load to understand they are returning from a closure, not the outer function. + +This proposal introduces a new syntax that avoids all of these problems: + +```swift +let bullet = + if isRoot && (count == 0 || !willExpand) { "" } + else if count == 0 { "- " } + else if maxDepth <= 0 { "▹ " } + else { "▿ " } +``` + +Similarly, the `return` ceremony could be dropped from the earlier example: + +```swift +public static func width(_ x: Unicode.Scalar) -> Int { + switch x.value { + case 0..<0x80: 1 + case 0x80..<0x0800: 2 + case 0x0800..<0x1_0000: 3 + default: 4 + } +} +``` + +Both these examples come from posts by [Nate Cook](https://forums.swift.org/t/if-else-expressions/22366/48) and [Michael Ilseman](https://forums.swift.org/t/omitting-returns-in-string-case-study-of-se-0255/24283), documenting many more examples where the standard library code would be much improved by this feature. + + +## Detailed Design + +`if` and `switch` statements will be usable as expressions, for the purpose of: + +- Returning values from functions, properties, and closures (either with implicit or explicit `return`); +- Assigning values to variables; and +- Declaring variables. + +There are of course many other places where an expression can appear, including as a sub-expression, or as an argument to a function. This is not being proposed at this time, and is discussed in the future directions section. + +For an `if` or `switch` to be used as an expression, it would need to meet these criteria: + +**Each branch of the `if`, or each `case` of the `switch`, must be a single expression.** + +Each of these expressions become the value of the overall expression if the branch is chosen. + +This does have the downside of requiring fallback to the existing techniques when, for example, a single expression has a log line above it. This is in keeping with the current behavior of `return` omission. + +An exception to this rule is if a branch either returns or throws, in which case no value for the overall expression need be produced. In these cases, multiple expressions could be executed on that branch prior to the `throw` or `return`. + +Within a branch, further `if` or `switch` expressions may be nested. + +**Each of those expressions, when type checked independently, must produce the same type.** + +This has two benefits: it dramatically simplifies the compiler's work in type checking the expression, and it makes it easier to reason about both individual branches and the overall expression. + +It has the effect of requiring more type context in ambiguous cases. The following code would _not_ compile: + +```swift +let x = if p { 0 } else { 1.0 } +``` + +since when type checked individually, `0` is of type `Int` and `1.0`. The fix would be to disambiguate each branch. In this case, either by rewriting `0` as `0.0`, or by providing type context e.g. `0 as Double`. + +This can be resolved by providing type context to each of the branches: + +```swift + let y: Float = switch x.value { + case 0..<0x80: 1 + case 0x80..<0x0800: 2.0 + case 0x0800..<0x1_0000: 3.0 + default: 4.5 + } +``` + +This decision is in keeping with other recent proposals such as [SE-0244: Opaque Result Types](https://github.com/apple/swift-evolution/blob/main/proposals/0244-opaque-result-types.md): + +```swift +// Error: Function declares an opaque return type 'some Numeric', but the +// return statements in its body do not have matching underlying types +func f() -> some Numeric { + if Bool.random() { + return 0 + } else { + return 1.0 + } +} +``` + +This rule will require explicit type context for declarations in order to determine the type of `nil` literals: + +```swift +// invalid: +let x = if p { nil } else { 2.0 } +// valid with required type context: +let x: Double? = if p { nil } else { 2.0 } +``` + +Of course, when returning from a function or assigning to an existing variable, this type context is always provided. + +It is also in keeping with [SE-0326: Enable multi-statement closure parameter/result type inference]( https://github.com/apple/swift-evolution/blob/main/proposals/0326-extending-multi-statement-closure-inference.md): + +```swift +func test(_: (Int?) -> T) {} + +// invalid +test { x in + guard let x { return nil } + return x +} + +// valid with required type context: +test { x -> Int? in + guard let x { return nil } + return x +} +``` + +It differs from the behavior of the ternary operator (`let x = p ? 0 : 1.0` compiles, with `x: Double`). + +However, the impact of bidirectional inference on the performance of the type checker would likely prohibit this feature from being implemented today, even if it were considered preferable. This is especially true in cases where there are many branches. This decision could be revisited in future: switching to full bidirectional type inference may be source breaking in theory, but probably not in practice (the proposal authors can't think of any examples where it would be). + +Bidirectional inference also makes it very difficult to reason about each of the branches individually, leading to sometimes unexpected results: + +```swift +let x = if p { + [1] +} else { + [1].lazy.map(expensiveOperation) +} +``` + +With full bidirectional inference, the `Array` in the `if` branch would force the `.lazy.map` in the `else` branch to be unexpectedly eager. + +The one exception to this rule is that some branches could produce a `Never` type. This would be allowed, so long as all non-`Never` branches are of the same type: + +```swift +// x is of type Int, discounting the type of the second branch +let x = if .random() { + 1 +} else { + fatalError() +} +``` + +**In the case of `if` statements, the branches must include an `else`** + +This rule is consistent with the current rules for definitive initialization and return statements with `if` e.g. + +```swift +func f() -> String { + let b = Bool.random() + if b == true { + return "true" + } else if b == false { // } else { here would compile + return "false" + } +} // Missing return in global function expected to return 'String' +``` + +This could be revisited in the future across the board (to DI, return values, and `if` expressions) if logic similar to that of exhaustive switches were applied, but this would be a separate proposal. + +**The expression is not part of a result builder expression** + +`if` and `switch` statements are already expressions when used in the context of a result builder, via the `buildEither` function. This proposal does not change this feature. + +The variable declaration form of an `if` will be allowed in result builders. + +**Pattern matching bindings may occur within an `if` or `case`** + +For example, returns could be dropped from + +```swift + private func balance() -> Tree { + switch self { + case let .node(.B, .node(.R, .node(.R, a, x, b), y, c), z, d): + .node(.R, .node(.B,a,x,b),y,.node(.B,c,z,d)) + case let .node(.B, .node(.R, a, x, .node(.R, b, y, c)), z, d): + .node(.R, .node(.B,a,x,b),y,.node(.B,c,z,d)) + case let .node(.B, a, x, .node(.R, .node(.R, b, y, c), z, d)): + .node(.R, .node(.B,a,x,b),y,.node(.B,c,z,d)) + case let .node(.B, a, x, .node(.R, b, y, .node(.R, c, z, d))): + .node(.R, .node(.B,a,x,b),y,.node(.B,c,z,d)) + default: + self + } + } +``` + +and optional unwrapping could be used with `if let`: + +```swift +// equivalent to let x = foo.map(process) ?? someDefaultValue +let x = if let foo { process(foo) } else { someDefaultValue } +``` + +## Future Directions + +This proposal chooses a narrow path of only enabling expressions in the 3 cases laid out at the start. This is intended to cover the vast majority of use cases, but could be followed up by expanded functionality covering many other use cases. Further cases could be added in later proposals once the community has had a chance to use this feature in practice – including source breaking versions introduced under a language variant. + +### Full Expressions + +A feel for the kind of expressions this could produce can be found in [this commit](https://github.com/apple/swift/compare/main...hamishknight:express-yourself#diff-7db38bc4b6f7872e5a631989c2925f5fac21199e221aa9112afbbc9aae66a2de) which adds this functionality to the parser. + +Full expressions would include various fairly conventional examples not proposed here: + +``` +let x = 1 + if .random() { 3 } else { 4 } +``` + +but also some pretty strange ones such as + +``` +for b in [true] where switch b { case true: true case false: false } {} +``` + +The strange examples can mostly be considered "weird but harmless" but there are some source breaking edge cases, in particular in result builders: + +```swift +var body: some View { + VStack { + if showButton { + Button("Click me!", action: clicked) + } else { + Text("No button") + } + .someStaticProperty + } +} +``` + +In this case, if `if` expressions were allowed to have postfix member expressions (which they aren't today, even in result builders), it would be ambiguous whether this should be parsed as a modifier on the `if` expression, or as a new expression. This could only be an issue for result builders, but the parser does not have the ability to specialize behavior for result builders. Note, this issue can happen today (and is why `One` exists for Regex Builders) but could introduce a new ambiguity for code that works this way today. + +### `do` Expressions + +`do` blocks could similarly be transformed into expressions, for example: + +```swift +let foo: String = do { + try bar() + } catch { + "Error \(error)" + } +``` + +### Support for `break` and `continue` + +Similar to `return`, statements that break or continue to a label, could be permitted in branches. There is likely a larger number of edge cases to consider here, possibly requiring enhancements to DI to ensure variables remain initialized on all paths. + +### Guard + +Often enthusiasm for `guard` leads to requests for `guard` to have parity with `if`. Returning a value from a `guard`'s else is very common, and could potentially be sugared as + +```swift + guard hasNativeStorage else { nil } +``` + +This is appealing, but is really a different proposal, of allowing omission `return` in `guard` statements. + +### Multi-statement branches + +The requirement that every branch be just a single expression leads to an unfortunate usability cliff: + +```swift +let decoded = + if isFastUTF8 { + Log("Taking the fast path") + withFastUTF8 { _decodeScalar($0, startingAt: i) } + } else + Log("Running error-correcting slow-path") + foreignErrorCorrectedScalar( + startingAt: String.Index(_encodedOffset: i)) + } +``` + +This is consistent with other cases, like multi-statement closures. But unlike in that case, where all that is needed is a `return` from the closure, this requires the user refactor the code back to the old mechanisms. + +The trouble is, there is no great solution here. The approach taken by some other languages such as rust is to allow a bare expression at the end of the scope to be the expression value for that scope. There are stylistic preferences for and against this. More importantly, this would be a fairly radical new direction for Swift, and if proposed should probably be considered for all such cases (like function and closure return values too). + +Alternatively, a new keyword could be introduced to make explicit that an expression value is being used as the value for this branch (Java uses `yield` for this in `switch` expressions). + +### Either + +As mentioned above, in result builders an `if` can be used to construct an `Either` type, which means the expressions in the branches could be of different types. + +This could be done with `if` expressions outside result builders too, and would be a powerful new feature for Swift. However, it is large in scope (including the introduction of a language-integrated `Either` type) and should be considered in a separate proposal, probably after the community has adjusted to the more vanilla version proposed here. + +## Alternatives Considered + +### Sticking with the Status Quo + +The list of [commonly rejected proposals](https://github.com/apple/swift-evolution/blob/main/commonly_proposed.md) includes the subject of this proposal: + +> **if/else and switch as expressions**: These are conceptually interesting things to support, but many of the problems solved by making these into expressions are already solved in Swift in other ways. Making them expressions introduces significant tradeoffs, and on balance, we haven't found a design that is clearly better than what we have so far. + +The motivation section above outlines why the alternatives that exist today fall short. One of the reasons this proposal is narrow in scope is to bring the majority of value while avoiding resolving some of these more difficult trade-offs. + +The lack of this feature puts Swift's [claim](https://www.swift.org/about/) to be a modern programming language under some strain. It is one of the few modern languages (Go being the other notable exception) not to support something along these lines. + +### Alternative syntax + +Instead of extending the current implicit return mechanism, where a single expression is treated as the returned value, this proposal could introduce a new syntax for expression versions of `if`/`switch`. For example, + +```java +var response = switch (utterance) { + case "thank you" -> "you’re welcome"; + case "atchoo" -> "gesundheit"; + case "fire!" -> { + log.warn("fire detected"); + yield "everybody out!"; // yield = value of multi-statement branch + }; + default -> { + throw new IllegalStateException(utterance) + } +} +``` + +A similar suggestion was made during [SE-0255: Implicit Returns from Single-Expression Functions](https://forums.swift.org/t/se-0255-implicit-returns-from-single-expression-functions/), where an alternate syntax for single-expression functions was discussed e.g. `func sum() -> Element = reduce(0, +)`. In that case, the core team did not consider introduction of a separate syntax for functions to be sufficiently motivated. + +The main benefit to the alternate `->` syntax is to make it more explicit, but comes at the cost of needing to know about two different kinds of switch syntax. Note that this is orthoganal to, and does not solve, the separate goal of providing a way of explicitly "yielding" an expression value in the case of multi-statement branches (also shown here in this java example) versus taking the "last expression" approach. + +Java did not introduce this syntax for `if` expressions. Since this is a goal for Swift, this implies: + +```swift +let x = + if .random() -> 1 + else -> fatalError() +``` + +However, this then poses an issue when evolving to multi-statement branches. Unlike with `switch`, these would require introducing braces, leaving a combination of both braces _and_ a "this is an expresion" sigil: + +```swift +let x = + if .random() -> { + let y = someComputation() + y * 2 + } else -> fatalError() +``` + +Unlike Java and C, this "braces for 2+ arguments" style of `if` is out of keeping in Swift. + +It is also not clear if the `->` would work well if expression status is brought to more kinds of statement e.g. + +```swift +let foo: String = + do -> + try bar() + catch ns as NSError -> + "Error \(error)" +``` + +or mixed branches with expressions and a return: + +``` +let x = + if .random() -> 1 + else -> return 2 +``` + +If a future direction of full expressions is considered, the `->` form may not work so well, especially when single-line expressions are desired e.g. + +``` +// is this (p ? 1 : 2) + 3 +// or p ? 1 : (2 + 3) +let x = if p -> 1 else -> 2 + 4 +``` + +## Source compatibility + +As proposed, this addition has one source incompatability, related to unreachable code. The following currently compiles, albeit with a warning that the `if` statement is unreachable (and the values in the branches unused): + +``` +func foo() { + return + if .random() { 0 } else { 0 } +} +``` + +but under this proposal, it would fail to compile with an error of "Unexpected non-void return value in void function" because it now parses as returning the `Int` expression from the `if`. This could be fixed in various ways (with `return;` or `return ()` or by `#if`-ing out the dead code explicitly). + +Another similar case can occur if constant evaluation leads the compiler to ignore dead code: + +``` +func foo() -> Int { + switch true { + case true: + return 0 + case false: + print("unreachable") + } +} +``` + +This currently _doesn't_ warn that the `false` case is unreachable (though probably should), but without special handling would after this proposal result in a type error that `()` does not match expected type `Int`. + +Given these examples all require dead code, it seems reasonable to accept this source break rather than gate this change under a language version or add special handling to avoid the break. + +## Effect on ABI stability + +This proposal has no impact on ABI stability. + +## Acknowledgments + +Much of this implementation layers on top of ground work done by [Pavel Yaskovich](https://github.com/xedin), particularly the work done to allow [multi-statement closure type inference](https://github.com/apple/swift-evolution/blob/main/proposals/0326-extending-multi-statement-closure-inference.md). + +Both [Nate Cook](https://forums.swift.org/t/if-else-expressions/22366/48) and [Michael Ilseman](https://forums.swift.org/t/omitting-returns-in-string-case-study-of-se-0255/24283) provided analysis of use cases in the standard library and elsewhere. Many community members have made a strong case for this change, most recently [Dave Abrahams](https://forums.swift.org/t/if-else-expressions/22366). From 48cad5998392c156a867ebe41757c36349178878 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 7 Dec 2022 09:50:37 -0800 Subject: [PATCH 2827/4563] Link to pitch and review for SE-0380. (#1868) --- proposals/0380-if-switch-expressions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0380-if-switch-expressions.md b/proposals/0380-if-switch-expressions.md index 22a366b08a..ac0f849f66 100644 --- a/proposals/0380-if-switch-expressions.md +++ b/proposals/0380-if-switch-expressions.md @@ -5,6 +5,7 @@ * Review Manager: [Holly Borla](https://github.com/hborla) * Status: **Active Review (Decembe 7...December 21, 2022)** * Implementation: [apple/swift#612178](https://github.com/apple/swift/pull/62178), including a downloadable toolchain. +* Review: ([pitch](https://forums.swift.org/t/pitch-if-and-switch-expressions/61149)), ([review](https://forums.swift.org/t/se-0380-if-and-switch-expressions/61899)) ## Introduction From 4ca64acd0364960d80ca519458d598c12d939ac9 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 7 Dec 2022 12:25:26 -0800 Subject: [PATCH 2828/4563] Revisions from first round of LWG review (#1858) * Revisions from first round of LWG review - `consume` is favored over `take`. - Round up other naming suggestions from review and pitch into Alternatives Considered * SE-0366: Rename operator to `consume` to match SE-0377 * Schedule SE-0366 for re-review. * Schedule SE-0377 for re-review. Co-authored-by: Holly Borla --- proposals/0366-move-function.md | 202 +++++++++--------- .../0377-parameter-ownership-modifiers.md | 176 ++++++++------- 2 files changed, 206 insertions(+), 172 deletions(-) diff --git a/proposals/0366-move-function.md b/proposals/0366-move-function.md index 645357a748..c4296cc88f 100644 --- a/proposals/0366-move-function.md +++ b/proposals/0366-move-function.md @@ -1,23 +1,24 @@ -# `take` operator to end the lifetime of a variable binding +# `consume` operator to end the lifetime of a variable binding * Proposal: [SE-0366](0366-move-function.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Joe Groff](https://github.com/jckarter) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (October 25 - November 8, 2022)** -* Implementation: Implemented on main as stdlib SPI (`_move` instead of `take` keyword) +* Status: **Active Review (December 7 - December 14, 2022)** +* Implementation: Implemented on main as stdlib SPI (`_move` instead of `consume` keyword) * Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic/53983)) ([first review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0366-move-operation-use-after-move-diagnostic/59687)) ([second review](https://forums.swift.org/t/se-0366-second-review-take-operator-to-end-the-lifetime-of-a-variable-binding/61021)) * Previous Revisions: * [1](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md) * [2](https://github.com/apple/swift-evolution/blob/43849aa9ae3e87c434866c5a5e389af67537ca26/proposals/0366-move-function.md) + * [3](https://github.com/apple/swift-evolution/blob/7af91127d693bffcb01aa87978d75d5a3170c4d1/proposals/0366-move-function.md) ## Introduction In this document, we propose adding a new operator, marked by the -context-sensitive keyword `take`, to the -language. `take` ends the lifetime of a specific local `let`, +context-sensitive keyword `consume`, to the +language. `consume` ends the lifetime of a specific local `let`, local `var`, or function parameter, and enforces this by causing the compiler to emit a diagnostic upon any use after the -take. This allows for code that relies on **forwarding ownership** +consume. This allows for code that relies on **forwarding ownership** of values for performance or correctness to communicate that requirement to the compiler and to human readers. As an example: @@ -25,13 +26,13 @@ the compiler and to human readers. As an example: useX(x) // do some stuff with local variable x // Ends lifetime of x, y's lifetime begins. -let y = take x // [1] +let y = consume x // [1] useY(y) // do some stuff with local variable y useX(x) // error, x's lifetime was ended at [1] // Ends lifetime of y, destroying the current value. -_ = take y // [2] +_ = consume y // [2] useX(x) // error, x's lifetime was ended at [1] useY(y) // error, y's lifetime was ended at [2] ``` @@ -92,15 +93,15 @@ Swift-evolution pitch threads: - [https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic) - [https://forums.swift.org/t/selective-control-of-implicit-copying-behavior-take-borrow-and-copy-operators-noimplicitcopy/60168](https://forums.swift.org/t/selective-control-of-implicit-copying-behavior-take-borrow-and-copy-operators-noimplicitcopy/60168) -## Proposed solution: `take` operator +## Proposed solution: `consume` operator -That is where the `take` operator comes into play. `take` consumes +That is where the `consume` operator comes into play. `consume` consumes the current value of a **binding with static lifetime**, which is either an unescaped local `let`, unescaped local `var`, or function parameter, with no property wrappers or get/set/read/modify/etc. accessors applied. It then provides a compiler guarantee that the current value will be unable to be used again locally. If such a use occurs, the compiler will -emit an error diagnostic. We can modify the previous example to use `take` to +emit an error diagnostic. We can modify the previous example to use `consume` to explicitly end the lifetime of `x`'s current value when we pass it off to `doStuffUniquely(with:)`: @@ -113,7 +114,7 @@ func test() { x.append(5) // Pass the current value of x off to another function, that - doStuffUniquely(with: take x) + doStuffUniquely(with: consume x) // Reset x to a new value. Since we don't use the old value anymore, x = [] @@ -121,19 +122,19 @@ func test() { } ``` -The `take x` operator syntax deliberately mirrors the +The `consume x` operator syntax deliberately mirrors the proposed [ownership modifier](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581) -parameter syntax, `(x: take T)`, because the caller-side behavior of `take` -operator is analogous to a callee’s behavior receiving a `take` parameter. -`doStuffUniquely(with:)` can use the `take` operator, combined with -the `take` parameter modifier, to preserve the uniqueness of the parameter +parameter syntax, `(x: consume T)`, because the caller-side behavior of `consume` +operator is analogous to a callee’s behavior receiving a `consume` parameter. +`doStuffUniquely(with:)` can use the `consume` operator, combined with +the `consume` parameter modifier, to preserve the uniqueness of the parameter as it moves it into its own local variable for mutation: ```swift -func doStuffUniquely(with value: take [Int]) { +func doStuffUniquely(with value: consume [Int]) { // If we received the last remaining reference to `value`, we'd like // to be able to efficiently update it without incurring more copies. - var newValue = take value + var newValue = consume value newValue.append(42) process(newValue) @@ -150,16 +151,16 @@ ensuring that the assignment doesn't cause a copy. Furthermore, if a future maintainer modifies the code in a way that breaks this transfer of ownership chain, then the compiler will raise an error. For instance, if a maintainer later introduces an additional use of -`x` after it's taken, but before it's reassigned, they will see an error: +`x` after it's consumed, but before it's reassigned, they will see an error: ```swift func test() { var x: [Int] = getArray() x.append(5) - doStuffUniquely(with: take x) + doStuffUniquely(with: consume x) - // ERROR: x used after being taken from + // ERROR: x used after being consumed doStuffInvalidly(with: x) x = [] @@ -168,25 +169,25 @@ func test() { ``` Likewise, if the maintainer tries to access the original `value` parameter inside -of `doStuffUniquely` after being taken to initialize `newValue`, they will +of `doStuffUniquely` after being consumed to initialize `newValue`, they will get an error: ``` -func doStuffUniquely(with value: take [Int]) { +func doStuffUniquely(with value: consume [Int]) { // If we received the last remaining reference to `value`, we'd like // to be able to efficiently update it without incurring more copies. - var newValue = take value + var newValue = consume value newValue.append(42) process(newValue) } ``` -`take` can also end the lifetime of local immutable `let` bindings, which become -unavailable after their value is taken since they cannot be reassigned. -Also note that `take` operates on bindings, not values. If we declare a +`consume` can also end the lifetime of local immutable `let` bindings, which become +unavailable after their value is consumed since they cannot be reassigned. +Also note that `consume` operates on bindings, not values. If we declare a constant `x` and another local constant `other` with the same value, -we can still use `other` after we take the value from `x`, as in: +we can still use `other` after we consume the value from `x`, as in: ```swift func useX(_ x: SomeClassType) -> () {} @@ -195,13 +196,13 @@ func f() { let x = ... useX(x) let other = x // other is a new binding used to extend the lifetime of x - _ = take x // x's lifetime ends + _ = consume x // x's lifetime ends useX(other) // other is used here... no problem. useX(other) // other is used here... no problem. } ``` -We can also take `other` independently of `x`, and get separate diagnostics for both +We can also consume `other` independently of `x`, and get separate diagnostics for both variables: ```swift @@ -211,23 +212,23 @@ func f() { let x = ... useX(x) let other = x - _ = take x - useX(take other) - useX(other) // error: 'other' used after being taken - useX(x) // error: 'x' used after being taken + _ = consume x + useX(consume other) + useX(other) // error: 'other' used after being consumed + useX(x) // error: 'x' used after being consumed } ``` -`inout` function parameters can also be used with `take`. Like a `var`, an -`inout` parameter can be reassigned after being taken from and used again; +`inout` function parameters can also be used with `consume`. Like a `var`, an +`inout` parameter can be reassigned after being consumed from and used again; however, since the final value of an `inout` parameter is passed back to the caller, it *must* be reassigned by the callee before it returns. So this will raise an error because `buffer` doesn't have a value at the point of return: ```swift -func f(_ buffer: inout Buffer) { // error: 'buffer' not reinitialized after take! - let b = take buffer // note: take was here +func f(_ buffer: inout Buffer) { // error: 'buffer' not reinitialized after consume! + let b = consume buffer // note: consume was here b.deinitialize() ... write code ... } // note: return without reassigning inout argument `buffer` @@ -237,7 +238,7 @@ But we can reinitialize `buffer` by writing the following code: ```swift func f(_ buffer: inout Buffer) { - let b = take buffer + let b = consume buffer b.deinitialize() // ... write code ... // We re-initialized buffer before end of function so the checker is satisfied @@ -245,13 +246,13 @@ func f(_ buffer: inout Buffer) { } ``` -`defer` can also be used to reinitialize an `inout` or `var` after a take, +`defer` can also be used to reinitialize an `inout` or `var` after a consume, in order to ensure that reassignment happens on any exit from scope, including thrown errors or breaks out of loops. So we can also write: ```swift func f(_ buffer: inout Buffer) { - let b = take buffer + let b = consume buffer // Ensure the buffer is reinitialized before we exit. defer { buffer = getNewInstance() } try b.deinitializeOrError() @@ -261,11 +262,11 @@ func f(_ buffer: inout Buffer) { ## Detailed design -At runtime, `take x` evaluates to the current value bound to `x`, just like the -expression `x` does. However, at compile time, the presence of a `take` forces +At runtime, `consume x` evaluates to the current value bound to `x`, just like the +expression `x` does. However, at compile time, the presence of a `consume` forces ownership of the argument to be transferred out of the binding at the given -point so. Any ensuing use of the binding that's reachable from the `take` -is an error. The operand to `take` is required to be a reference +point so. Any ensuing use of the binding that's reachable from the `consume` +is an error. The operand to `consume` is required to be a reference to a *binding with static lifetime*. The following kinds of declarations can currently be referenced as bindings with static lifetime: @@ -282,25 +283,25 @@ A binding with static lifetime also must satisfy the following requirements: `didSet`, `willSet`, `_read`, or `_modify`, - it cannot be an `async let`. -Possible extensions to the set of operands that can be used with `take` are -discussed under Future Directions. It is an error to use `take` with an operand +Possible extensions to the set of operands that can be used with `consume` are +discussed under Future Directions. It is an error to use `consume` with an operand that doesn't reference a binding with static lifetime. -Given a valid operand, `take` enforces that there are no other -references to the binding after it is taken. The analysis is +Given a valid operand, `consume` enforces that there are no other +references to the binding after it is consumed. The analysis is flow sensitive, so one is able to end the lifetime of a value conditionally: ```swift if condition { - let y = take x + let y = consume x // I can't use x anymore here! - useX(x) // !! ERROR! Use after take. + useX(x) // !! ERROR! Use after consume. } else { // I can still use x here! useX(x) // OK } // But I can't use x here. -useX(x) // !! ERROR! Use after take. +useX(x) // !! ERROR! Use after consume. ``` If the binding is a `var`, the analysis additionally allows for code to @@ -310,18 +311,18 @@ following example: ```swift if condition { - _ = take x + _ = consume x // I can't use x anymore here! - useX(x) // !! ERROR! Use after take. + useX(x) // !! ERROR! Use after consume. x = newValue // But now that I have re-assigned into x a new value, I can use the var // again. useX(x) // OK } else { - // I can still use x here, since it wasn't taken on this path! + // I can still use x here, since it wasn't consumed on this path! useX(x) // OK } -// Since I reinitialized x along the `if` branch, and it was never taken +// Since I reinitialized x along the `if` branch, and it was never consumed // from on the `else` branch, I can use it here too. useX(x) // OK ``` @@ -333,32 +334,32 @@ with a valid value before proceeding. For an `inout` parameter, the analysis behaves the same as for a `var`, except that all exits from the function (whether by `return` or by `throw`) are considered to be uses of the parameter. Correct code therefore *must* reassign -inout parameters after they are taken from. +inout parameters after they are consumed from. -Using `take` on a binding without using the result raises a warning, +Using `consume` on a binding without using the result raises a warning, just like a function call that returns an unused non-`Void` result. -To "drop" a value without using it, the `take` can be assigned to +To "drop" a value without using it, the `consume` can be assigned to `_` explicitly. ## Source compatibility -`take` behaves as a contextual keyword. In order to avoid interfering -with existing code that calls functions named `take`, the operand to -`take` must begin with another identifier, and must consist of an +`consume` behaves as a contextual keyword. In order to avoid interfering +with existing code that calls functions named `consume`, the operand to +`consume` must begin with another identifier, and must consist of an identifier or postfix expression: ``` -take x // OK -take [1, 2, 3] // Subscript access into property named `take`, not a take operation -take (x) // Call to function `take`, not a take operation -take x.y.z // Syntactically OK (although x.y.z is not currently semantically valid) -take x[0] // Syntactically OK (although x[0] is not currently semantically valid -take x + y // Parses as (take x) + y +consume x // OK +consume [1, 2, 3] // Subscript access into property named `consume`, not a consume operation +consume (x) // Call to function `consume`, not a consume operation +consume x.y.z // Syntactically OK (although x.y.z is not currently semantically valid) +consume x[0] // Syntactically OK (although x[0] is not currently semantically valid +consume x + y // Parses as (consume x) + y ``` ## Effect on ABI stability -`take` requires no ABI additions. +`consume` requires no ABI additions. ## Effect on API resilience @@ -380,13 +381,16 @@ The community reviewed the contextual keyword syntax, using the name `move x`, and through further discussion the alternative name `take` arose. This name aligns with the term used in the Swift compiler internals, and also reads well as the analogous parameter ownership modifier, `(x: take T)`, so the authors -now favor this name. +now favor this name. However, during review of SE-0377, reviewers found that +`take` was too generic, and could be confused with the common colloquial language +of talking about function calls as "taking their arguments". Of alternative +names reviewers offered, the language workgroup favors `consume`. -Many have suggested alternative spellings that also make `take`'s special +Many have suggested alternative spellings that also make `consume`'s special nature more syntactically distinct, including: -- an expression attribute, like `useX(@take x)` -- a compiler directive, like `useX(#take x)` +- an expression attribute, like `useX(@consume x)` +- a compiler directive, like `useX(#consume x)` - an operator, like `useX(<-x)` ### Use of scoping to end lifetimes @@ -428,8 +432,8 @@ to end value lifetimes: let x = foo() let y = bar() // end x's lifetime before y's - consume(take x) - consume(take y) + consume(consume x) + consume(consume y) ``` - Lexical scoping cannot be used by itself to shorten the lifetime of function @@ -444,43 +448,43 @@ value lifetimes to shrinkwrap to their actual duration of use. ## Future directions -### Dynamic enforcement of `take` for other kinds of bindings +### Dynamic enforcement of `consume` for other kinds of bindings In the future, we may want to accommodate the ability to dynamically -take from bindings with dynamic lifetime, such as escaped local +consume bindings with dynamic lifetime, such as escaped local variables, and class stored properties, although doing so in full generality would require dynamic enforcement in addition to static checking, similar to how we need to dynamically enforce exclusivity when accessing globals and class stored properties. Since this -dynamic enforcement turns misuse of `take`s into runtime errors +dynamic enforcement turns misuse of `consume`s into runtime errors rather than compile-time guarantees, we might want to make those dynamic cases syntactically distinct, to make the possibility of runtime errors clear. `Optional` and other types with a canonical "no value" or "empty" -state can use the static `take` operator to provide API that +state can use the static `consume` operator to provide API that dynamically takes ownership of the current value inside of them while leaving them in their empty state: ``` extension Optional { mutating func take() -> Wrapped { - switch take self { + switch consume self { case .some(let x): self = .none return x case .none: - fatalError("trying to take from an empty Optional") + fatalError("trying to consume an empty Optional") } } } ``` -### Piecewise `take` of frozen structs and tuples +### Piecewise `consume` of frozen structs and tuples For frozen structs and tuples, both aggregates that the compiler can statically know the layout of, we could do finer-grained analysis and allow their -individual fields to be taken independently: +individual fields to be consumed independently: ```swift struct TwoStrings { @@ -489,8 +493,8 @@ struct TwoStrings { } func foo(x: TwoStrings) { - use(take x.first) - // ERROR! part of x was taken + use(consume x.first) + // ERROR! part of x was consumed use(x) // OK, this part wasn't use(x.second) @@ -502,7 +506,7 @@ func foo(x: TwoStrings) { Move-only types would allow for the possibility of value types with custom `deinit` logic that runs at the end of a value of the type's lifetime. Typically, this logic would run when the final owner of the value is finished -with it, which means that a function which `take`s an instance, or a +with it, which means that a function which `consume`s an instance, or a `taking func` method on the type itself, would run the deinit if it does not forward ownership anywhere else: @@ -514,7 +518,7 @@ moveonly struct FileHandle { deinit { close(fd) } } -func dumpAndClose(to fh: take FileHandle, contents: Data) { +func dumpAndClose(to fh: consume FileHandle, contents: Data) { write(fh.fd, contents) // fh implicitly deinit-ed here, closing it } @@ -542,7 +546,7 @@ though `forget` in Rust still does not allow for the value to be destructured into parts. We could come up with a mechanism in Swift that both suppresses implicit deinitialization, and allows for piecewise taking of its components. This doesn't require a new parameter convention (since it fits within the -ABI of a `take T` parameter), but could be spelled as a new operator +ABI of a `consume T` parameter), but could be spelled as a new operator inside of a `taking func`: ``` @@ -583,9 +587,9 @@ code that imports a module may not always have access to the concrete layout of a type, which would make partial destructuring impossible, which further limits the contexts in which such an operator can be used. -### `take` from computed properties, property wrappers, properties with accessors, etc. +### `consume` from computed properties, property wrappers, properties with accessors, etc. -It would potentially be useful to be able to `take` from variables +It would potentially be useful to be able to `consume` variables and properties with modified access behavior, such as computed properties, properties with didSet/willSet observers, property wrappers, and so on. Although we could do lifetime analysis on these @@ -600,7 +604,7 @@ and an initializer to reinitialize `self` on reassignment after a Pitch: [Selective control of implicit copying behavior](https://forums.swift.org/t/selective-control-of-implicit-copying-behavior-take-borrow-and-copy-operators-noimplicitcopy/60168) -The `take` operator is one of a number of implicit copy controls +The `consume` operator is one of a number of implicit copy controls we're considering: - A value that isn't modified can generally be "borrowed" and @@ -663,7 +667,7 @@ we're considering: } ``` -- `take` and `borrow` operators can eliminate copying in +- `consume` and `borrow` operators can eliminate copying in common localized situations, but it is also useful to be able to suppress implicit copying altogether for certain variables, types, and scopes. We could define an attribute to specify that bindings @@ -688,9 +692,9 @@ we're considering: } ``` -### `borrow` and `take` argument modifiers +### `borrow` and `consume` argument modifiers -Pitch: [`borrow` and `take` parameter ownership modifiers](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581) +Pitch: [`borrow` and `consume` parameter ownership modifiers](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581) Swift currently only makes an explicit distinction between pass-by-value and pass-by-`inout` parameters, leaving the mechanism @@ -701,11 +705,11 @@ conventions that the compiler uses to pass by value: its argument object will stay alive for the duration of the call, and the callee does not need to release it (except to balance any additional retains it performs itself). -- The callee can **take** the parameter. The callee becomes +- The callee can **consume** the parameter. The callee becomes responsible for either releasing the parameter or passing ownership of it along somewhere else. If a caller doesn't want to give up its own ownership of its argument, it must retain the - argument so that the callee can take the extra reference count. + argument so that the callee can consume the extra reference count. In order to allow for manual optimization of code, and to support move-only types where this distinction becomes semantically @@ -719,6 +723,10 @@ Thanks to Nate Chandler, Tim Kientzle, and Holly Borla for their help with this! ## Revision history +Changes from the [third revision](https://github.com/apple/swift-evolution/blob/7af91127d693bffcb01aa87978d75d5a3170c4d1/proposals/0366-move-function.md): + +- `take` is renamed again to `consume`. + Changes from the [second revision](https://github.com/apple/swift-evolution/blob/43849aa9ae3e87c434866c5a5e389af67537ca26/proposals/0366-move-function.md): - `move` is renamed to `take`. diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index 2a6209b8b1..2bdb4ee28f 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -1,15 +1,16 @@ -# `borrow` and `take` parameter ownership modifiers +# `borrow` and `consume` parameter ownership modifiers * Proposal: [SE-0377](0377-parameter-ownership-modifiers.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (October 25 - November 8, 2022)** +* Status: **Active Review (December 7 - December 14, 2022)** * Implementation: available using the internal names `__shared` and `__owned` * Review: ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) +* Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) ## Introduction -We propose new `borrow` and `take` parameter modifiers to allow developers to +We propose new `borrow` and `consume` parameter modifiers to allow developers to explicitly choose the ownership convention that a function uses to receive immutable parameters. This allows for fine-tuning of performance by reducing the number of ARC calls or copies needed to call a function, and provides a @@ -27,11 +28,11 @@ callee in a function call: guarantees that its argument object will stay alive for the duration of the call, and the callee does not need to release it (except to balance any additional retains it performs itself). -* The callee can **take** the parameter. The callee +* The callee can **consume** the parameter. The callee becomes responsible for either releasing the parameter or passing ownership of it along somewhere else. If a caller doesn't want to give up its own ownership of its argument, it must retain the argument so that the callee - can take the extra reference count. + can consume the extra reference count. These two conventions generalize to value types, where a "retain" becomes an independent copy of the value, and "release" the destruction and @@ -39,7 +40,7 @@ deallocation of the copy. By default Swift chooses which convention to use based on some rules informed by the typical behavior of Swift code: initializers and property setters are more likely to use their parameters to construct or update another value, so it is likely more efficient for them to -*take* their parameters and forward ownership to the new value they construct. +*consume* their parameters and forward ownership to the new value they construct. Other functions default to *borrowing* their parameters, since we have found this to be more efficient in most situations. @@ -58,7 +59,7 @@ move-only values and types. Since move-only types do not have the ability to be copied, the distinction between the two conventions becomes an important part of the API contract: functions that *borrow* move-only values make temporary use of the value and leave it valid for further use, like reading -from a file handle, whereas functions that *take* a move-only value consume +from a file handle, whereas functions that *consume* a move-only value consume it and prevent its further use, like closing a file handle. Relying on implicit selection of the parameter convention will not suffice for these types. @@ -66,18 +67,18 @@ types. ## Proposed solution We give developers direct control over the ownership convention of -parameters by introducing two new parameter modifiers `borrow` and `take`. +parameters by introducing two new parameter modifiers `borrow` and `consume`. ## Detailed design -`borrow` and `take` become contextual keywords inside parameter type +`borrow` and `consume` become contextual keywords inside parameter type declarations. They can appear in the same places as the `inout` modifier, and are mutually exclusive with each other and with `inout`. In a `func`, `subscript`, or `init` declaration, they appear as follows: ```swift func foo(_: borrow Foo) -func foo(_: take Foo) +func foo(_: consume Foo) func foo(_: inout Foo) ``` @@ -85,7 +86,7 @@ In a closure: ```swift bar { (a: borrow Foo) in a.foo() } -bar { (a: take Foo) in a.foo() } +bar { (a: consume Foo) in a.foo() } bar { (a: inout Foo) in a.foo() } ``` @@ -93,47 +94,48 @@ In a function type: ```swift let f: (borrow Foo) -> Void = { a in a.foo() } -let f: (take Foo) -> Void = { a in a.foo() } +let f: (consume Foo) -> Void = { a in a.foo() } let f: (inout Foo) -> Void = { a in a.foo() } ``` -Methods can use the `taking` or `borrowing` modifier to indicate that they -take ownership of their `self` parameter, or they borrow it. These modifiers -are mutually exclusive with each other and with the existing `mutating` modifier: +Methods can use the `consuming` or `borrowing` modifier to indicate +respectively that they consume ownership of their `self` parameter or that they +borrow it. These modifiers are mutually exclusive with each other and with the +existing `mutating` modifier: ```swift struct Foo { - taking func foo() // `take` ownership of self + consuming func foo() // `consume` ownership of self borrowing func foo() // `borrow` self mutating func foo() // modify self with `inout` semantics } ``` -`take` cannot be applied to parameters of nonescaping closure type, which by +`consume` cannot be applied to parameters of nonescaping closure type, which by their nature are always borrowed: ```swift -// ERROR: cannot `take` a nonescaping closure -func foo(f: take () -> ()) { +// ERROR: cannot `consume` a nonescaping closure +func foo(f: consume () -> ()) { } ``` -`take` or `borrow` on a parameter do not affect the caller-side syntax for -passing an argument to the affected declaration, nor do `taking` or +`consume` or `borrow` on a parameter do not affect the caller-side syntax for +passing an argument to the affected declaration, nor do `consuming` or `borrowing` affect the application of `self` in a method call. For typical Swift code, adding, removing, or changing these modifiers does not have any source-breaking effects. (See "related directions" below for interactions with other language features being considered currently or in the near future which might interact with these modifiers in ways that cause them to break source.) -Protocol requirements can also use `take` and `borrow`, and the modifiers will +Protocol requirements can also use `consume` and `borrow`, and the modifiers will affect the convention used by the generic interface to call the requirement. The requirement may still be satisfied by an implementation that uses different conventions for parameters of copyable types: ```swift protocol P { - func foo(x: take Foo, y: borrow Foo) + func foo(x: consume Foo, y: borrow Foo) } // These are valid conformances: @@ -143,29 +145,29 @@ struct A: P { } struct B: P { - func foo(x: borrow Foo, y: take Foo) + func foo(x: borrow Foo, y: consume Foo) } struct C: P { - func foo(x: take Foo, y: borrow Foo) + func foo(x: consume Foo, y: borrow Foo) } ``` Function values can also be implicitly converted to function types that change the convention of parameters of copyable types among unspecified, `borrow`, -or `take`: +or `consume`: ```swift let f = { (a: Foo) in print(a) } let g: (borrow Foo) -> Void = f -let h: (take Foo) -> Void = f +let h: (consume Foo) -> Void = f let f2: (Foo) -> Void = h ``` -Inside of a function or closure body, `take` parameters may be mutated, as can -the `self` parameter of a `taking func` method. These +Inside of a function or closure body, `consume` parameters may be mutated, as can +the `self` parameter of a `consuming func` method. These mutations are performed on the value that the function itself took ownership of, and will not be evident in any copies of the value that might still exist in the caller. This makes it easy to take advantage of the uniqueness of values @@ -175,7 +177,7 @@ after ownership transfer to do efficient local mutations of the value: extension String { // Append `self` to another String, using in-place modification if // possible - taking func plus(_ other: String) -> String { + consuming func plus(_ other: String) -> String { // Modify our owned copy of `self` in-place, taking advantage of // uniqueness if possible self += other @@ -189,13 +191,13 @@ let helloWorld = "hello ".plus("cruel ").plus("world") ## Source compatibility -Adding `take` or `borrow` to a parameter in the language today does not +Adding `consume` or `borrow` to a parameter in the language today does not otherwise affect source compatibility. Callers can continue to call the function as normal, and the function body can use the parameter as it already -does. A method with `take` or `borrow` modifiers on its parameters can still +does. A method with `consume` or `borrow` modifiers on its parameters can still be used to satisfy a protocol requirement with different modifiers. The compiler will introduce implicit copies as needed to maintain the expected -conventions. This allows for API authors to use `take` and `borrow` annotations +conventions. This allows for API authors to use `consume` and `borrow` annotations to fine-tune the copying behavior of their implementations, without forcing clients to be aware of ownership to use the annotated APIs. Source-only packages can add, remove, or adjust these annotations on copyable types @@ -203,29 +205,29 @@ over time without breaking their clients. This will change if we introduce features that limit the compiler's ability to implicitly copy values, such as move-only types, "no implicit copy" values -or scopes, and `take` or `borrow` operators in expressions. Changing the +or scopes, and `consume` or `borrow` operators in expressions. Changing the parameter convention changes where copies may be necessary to perform the call. -Passing an uncopyable value as an argument to a `take` parameter ends its -lifetime, and that value cannot be used again after it's taken. +Passing an uncopyable value as an argument to a `consume` parameter ends its +lifetime, and that value cannot be used again after it's consumed. ## Effect on ABI stability -`take` or `borrow` affects the ABI-level calling convention and cannot be +`consume` or `borrow` affects the ABI-level calling convention and cannot be changed without breaking ABI-stable libraries (except on "trivial types" for which copying is equivalent to `memcpy` and destroying is a no-op; however, -`take` or `borrow` also has no practical effect on parameters of trivial type). +`consume` or `borrow` also has no practical effect on parameters of trivial type). ## Effect on API resilience -`take` or `borrow` break ABI for ABI-stable libraries, but are intended to have +`consume` or `borrow` break ABI for ABI-stable libraries, but are intended to have minimal impact on source-level API. When using copyable types, adding or changing these annotations to an API should not affect its existing clients. ## Alternatives considered -### Leaving `take` parameter bindings immutable inside the callee +### Leaving `consume` parameter bindings immutable inside the callee -We propose that `take` parameters should be mutable inside of the callee, +We propose that `consume` parameters should be mutable inside of the callee, because it is likely that the callee will want to perform mutations using the value it has ownership of. There is a concern that some users may find this behavior unintuitive, since those mutations would not be visible in copies @@ -234,20 +236,20 @@ of the value in the caller. This was the motivation behind which explicitly removed the former ability to declare parameters as `var` because of this potential for confusion. However, whereas `var` and `inout` both suggest mutability, and `var` does not provide explicit directionality as -to where mutations become visible, `take` on the other hand does not +to where mutations become visible, `consume` on the other hand does not suggest any kind of mutability to the caller, and it explicitly states the directionality of ownership transfer. Furthermore, with move-only types, the chance for confusion is moot, because the transfer of ownership means the caller cannot even use the value after the callee takes ownership anyway. -Another argument for `take` parameters to remain immutable is to serve the +Another argument for `consume` parameters to remain immutable is to serve the proposal's stated goal of minimizing the source-breaking impact of -parameter ownership modifiers. When `take` parameters are mutable, -changing a `take` parameter to `borrow`, or removing the -`take` annotation altogether, is potentially source-breaking. However, +parameter ownership modifiers. When `consume` parameters are mutable, +changing a `consume` parameter to `borrow`, or removing the +`consume` annotation altogether, is potentially source-breaking. However, any such breakage is purely localized to the callee; callers are still unaffected (as long as copyable arguments are involved). If a developer wants -to change a `take` parameter back into a `borrow`, they can still assign the +to change a `consume` parameter back into a `borrow`, they can still assign the borrowed value to a local variable and use that local variable for local mutation. @@ -259,28 +261,46 @@ We have considered several alternative naming schemes for these modifiers: and we could remove the underscores to make these simply `shared` and `owned`. These names refer to the way a borrowed parameter receives a "shared" borrow (as opposed to the "exclusive" borrow on an `inout` - parameter), whereas a taken parameter becomes "owned" by the callee. + parameter), whereas a consumed parameter becomes "owned" by the callee. found that the "shared" versus "exclusive" language for discussing borrows, while technically correct, is unnecessarily confusing for explaining the model. - A previous pitch used the names `nonconsuming` and `consuming`. The current implementation also uses `__consuming func` to notate a method that takes - ownership of its `self` parameter. - -The names `take` and `borrow` arose during [the first review of + ownership of its `self` parameter. `consuming` parallels `mutating` as a + method modifier for a method that consumes `self`, but we like the imperative + form `consume` for parameter modifiers as a parallel with the `consume` operator + in callers. +- The first reviewed revision used `take` instead of `consume`. Along with + `borrow`, `take` arose during [the first review of SE-0366](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202). -These names also work well as names for operators that explicitly -transfer ownership of a variable or borrow it in place, discussed below as the -`take` and `borrow` operators under Related Directions. We think it is helpful -to align the naming of those operators with the naming of these parameter -modifiers, since it helps reinforce the relationship between the calling -conventions and the expression operators: to explicitly transfer ownership -of an argument in a call site to a parameter in a function, use `foo(take x)` -at the call site, and use `func foo(_: take T)` in the function declaration. + These names also work well as names for operators that explicitly + transfer ownership of a variable or borrow it in place. However, + reviewers observed that `take` is possibly confusing, since it conflicts with + colloquial discussion of function calls "taking their arguments". `consume` + reads about as well while being more specific. +- Reviewers offered `use`, `own`, or `sink` as alternatives to `consume`. + +We think it is helpful to align the naming of these parameter modifiers with +the corresponding `consume` and `borrow` operators (discussed below under +Future Directions), since it helps reinforce the relationship between the +calling conventions and the expression operators: to explicitly transfer +ownership of an argument in a call site to a parameter in a function, use +`foo(consume x)` at the call site, and use `func foo(_: consume T)` in the +function declaration. Similarly, to explicitly pass an argument by borrow +without copying, use `foo(borrow x)` at the call site, and `func foo(_: borrow T)` +in the function declaration. + +Some review discussion explored the possibility of +using different verb forms for the various roles; since we're already using +`consuming func` and `borrowing func` as the modifiers for the `self` parameter, +we could also conjugate the parameter modifiers so you write +`foo(x: borrowed T)` or `bar(x: consumed T)`, while still using `foo(borrow x)` +and `bar(consume x)` as the call-site operators. ### Effect on call sites and uses of the parameter -This proposal designs the `take` and `borrow` modifiers to have minimal source +This proposal designs the `consume` and `borrow` modifiers to have minimal source impact when applied to parameters, on the expectation that, in typical Swift code that isn't using move-only types or other copy-controlling features, adjusting the convention is a useful optimization on its own without otherwise @@ -292,7 +312,7 @@ a value argument indicates that the developer is interested in guaranteeing that the optimization occurs, and having the annotation imply changed behavior at call sites or inside the function definition, such as disabling implicit copies of the parameter inside the function, or implicitly taking an argument -to a `take` parameter and ending its lifetime inside the caller after the +to a `consume` parameter and ending its lifetime inside the caller after the call site. We believe that it is better to keep the behavior of the call in expressions independent of the declaration (to the degree possible with implicitly copyable values), and that explicit operators on the call site @@ -305,12 +325,12 @@ optimizer behavior is insufficient to get optimal code. There are a number of caller-side operators we are considering to allow for performance-sensitive code to make assertions about call behavior. These -are closely related to the `take` and `borrow` parameter modifiers and so +are closely related to the `consume` and `borrow` parameter modifiers and so share their names. See also the [Selective control of implicit copying behavior](https://forums.swift.org/t/selective-control-of-implicit-copying-behavior-take-borrow-and-copy-operators-noimplicitcopy/60168) thread on the Swift forums for deeper discussion of this suite of features -#### `take` operator +#### `consume` operator Currently under review as [SE-0366](https://github.com/apple/swift-evolution/blob/main/proposals/0366-move-function.md), @@ -318,15 +338,15 @@ it is useful to have an operator that explicitly ends the lifetime of a variable before the end of its scope. This allows the compiler to reliably destroy the value of the variable, or transfer ownership, at the point of its last use, without depending on optimization and vague ARC optimizer rules. -When the lifetime of the variable ends in an argument to a `take` parameter, +When the lifetime of the variable ends in an argument to a `consume` parameter, then we can transfer ownership to the callee without any copies: ```swift -func consume(x: take Foo) +func consume(x: consume Foo) func produce() { let x = Foo() - consume(x: take x) + consume(x: consume x) doOtherStuffNotInvolvingX() } ``` @@ -394,13 +414,13 @@ while the caller is borrowing it, an exclusivity failure would be raised. #### Move-only types, uncopyable values, and related features -The `take` versus `borrow` distinction becomes much more important and +The `consume` versus `borrow` distinction becomes much more important and prominent for values that cannot be implicitly copied. We have plans to introduce move-only types, whose values are never copyable, as well as attributes that suppress the compiler's implicit copying behavior selectively for particular variables or scopes. Operations that borrow a value allow the same value to continue being used, whereas operations that -take a value destroy it and prevent its continued use. This makes the +consume a value destroy it and prevent its continued use. This makes the convention used for move-only parameters a much more important part of their API contract, since it directly affects whether the value is still available after the operation: @@ -415,18 +435,18 @@ func open(path: FilePath) throws -> FileHandle // borrow the FileHandle func read(from: borrow FileHandle) throws -> Data -// Operations that close the file handle and make it unusable take +// Operations that close the file handle and make it unusable consume // the FileHandle -func close(file: take FileHandle) +func close(file: consume FileHandle) func hackPasswords() throws -> HackedPasswords { let fd = try open(path: "/etc/passwd") // `read` borrows fd, so we can continue using it after let contents = try read(from: fd) - // `close` takes fd from us, so we can't use it again + // `close` consumes fd, so we can't use it again close(fd) - let moreContents = try read(from: fd) // compiler error: use after take + let moreContents = try read(from: fd) // compiler error: use after consume return hackPasswordData(contents) } @@ -445,7 +465,7 @@ extension String: WritableToFileHandle { // error: does not satisfy protocol requirement, ownership modifier for // parameter of move-only type `FileHandle` does not match /* - func write(to fh: take FileHandle) { + func write(to fh: consume FileHandle) { ... } */ @@ -468,13 +488,13 @@ conforming types to be copyable. ### `set`/`out` parameter convention -By making the `borrow` and `take` conventions explicit, we mostly round out +By making the `borrow` and `consume` conventions explicit, we mostly round out the set of possibilities for how to handle a parameter. `inout` parameters get **exclusive access** to their argument, allowing them to mutate or replace the current value without concern for other code. By contrast, `borrow` parameters get **shared access** to their argument, allowing multiple pieces of code to share the same value without copying, so long as none of them mutate the -shared value. A `take` parameter consumes a value, leaving nothing behind, but +shared value. A `consume` parameter consumes a value, leaving nothing behind, but there still isn't a parameter analog to the opposite convention, which would be to take an uninitialized argument and populate it with a new value. Many languages, including C# and Objective-C when used with the "Distributed @@ -489,3 +509,9 @@ to add some kind of `out` parameter, but a future proposal could. Thanks to Robert Widmann for the original underscored implementation of `__owned` and `__shared`: [https://forums.swift.org/t/ownership-annotations/11276](https://forums.swift.org/t/ownership-annotations/11276). + +## Revision history + +The [first reviewed revision](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) +of this proposal used `take` and `taking` instead of `consume` and `consuming` +as the name of the callee-destroy convention. From 9c9297e0449fb7189fa00e4c97753090b4a8aca7 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 7 Dec 2022 12:29:55 -0800 Subject: [PATCH 2829/4563] Link to the third review of SE-0366. (#1870) --- proposals/0366-move-function.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0366-move-function.md b/proposals/0366-move-function.md index c4296cc88f..7c11aedb99 100644 --- a/proposals/0366-move-function.md +++ b/proposals/0366-move-function.md @@ -5,7 +5,7 @@ * Review Manager: [Holly Borla](https://github.com/hborla) * Status: **Active Review (December 7 - December 14, 2022)** * Implementation: Implemented on main as stdlib SPI (`_move` instead of `consume` keyword) -* Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic/53983)) ([first review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0366-move-operation-use-after-move-diagnostic/59687)) ([second review](https://forums.swift.org/t/se-0366-second-review-take-operator-to-end-the-lifetime-of-a-variable-binding/61021)) +* Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic/53983)) ([first review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0366-move-operation-use-after-move-diagnostic/59687)) ([second review](https://forums.swift.org/t/se-0366-second-review-take-operator-to-end-the-lifetime-of-a-variable-binding/61021)) ([third review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) * Previous Revisions: * [1](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md) * [2](https://github.com/apple/swift-evolution/blob/43849aa9ae3e87c434866c5a5e389af67537ca26/proposals/0366-move-function.md) From 91d416306caa8df4c0e67699c31544f752fdc98f Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 7 Dec 2022 12:31:44 -0800 Subject: [PATCH 2830/4563] Link to the second review for SE-0377. (#1871) --- proposals/0377-parameter-ownership-modifiers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index 2bdb4ee28f..d0706ac1cc 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -5,8 +5,8 @@ * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Active Review (December 7 - December 14, 2022)** * Implementation: available using the internal names `__shared` and `__owned` -* Review: ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) -* Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) +* Review: ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) +* Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ## Introduction From e3966645cf07d6103561454574ab3e2cc2b48ee9 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 7 Dec 2022 12:48:53 -0800 Subject: [PATCH 2831/4563] SE-0377: Move second review link to the proper field. (#1872) --- proposals/0377-parameter-ownership-modifiers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index d0706ac1cc..8109fef758 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -5,8 +5,8 @@ * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Active Review (December 7 - December 14, 2022)** * Implementation: available using the internal names `__shared` and `__owned` -* Review: ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) -* Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) +* Review: ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) +* Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) ## Introduction From 2a825d1b158e6fe992c7e3f615d078b4bdc8906b Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 7 Dec 2022 22:59:17 +0000 Subject: [PATCH 2832/4563] Make syntax highlighting consistent in SE-0380 (#1869) --- proposals/0380-if-switch-expressions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0380-if-switch-expressions.md b/proposals/0380-if-switch-expressions.md index ac0f849f66..6599727c30 100644 --- a/proposals/0380-if-switch-expressions.md +++ b/proposals/0380-if-switch-expressions.md @@ -437,7 +437,7 @@ let x = if p -> 1 else -> 2 + 4 As proposed, this addition has one source incompatability, related to unreachable code. The following currently compiles, albeit with a warning that the `if` statement is unreachable (and the values in the branches unused): -``` +```swift func foo() { return if .random() { 0 } else { 0 } @@ -448,7 +448,7 @@ but under this proposal, it would fail to compile with an error of "Unexpected n Another similar case can occur if constant evaluation leads the compiler to ignore dead code: -``` +```swift func foo() -> Int { switch true { case true: From 129223c1d7e2d1a2b26d6540266bd052dcf577e5 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 9 Dec 2022 06:01:48 -0800 Subject: [PATCH 2833/4563] [SE-0380] Correct a name in Acknowledgments section (#1875) --- proposals/0380-if-switch-expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0380-if-switch-expressions.md b/proposals/0380-if-switch-expressions.md index 6599727c30..c6facca3ff 100644 --- a/proposals/0380-if-switch-expressions.md +++ b/proposals/0380-if-switch-expressions.md @@ -469,6 +469,6 @@ This proposal has no impact on ABI stability. ## Acknowledgments -Much of this implementation layers on top of ground work done by [Pavel Yaskovich](https://github.com/xedin), particularly the work done to allow [multi-statement closure type inference](https://github.com/apple/swift-evolution/blob/main/proposals/0326-extending-multi-statement-closure-inference.md). +Much of this implementation layers on top of ground work done by [Pavel Yaskevich](https://github.com/xedin), particularly the work done to allow [multi-statement closure type inference](https://github.com/apple/swift-evolution/blob/main/proposals/0326-extending-multi-statement-closure-inference.md). Both [Nate Cook](https://forums.swift.org/t/if-else-expressions/22366/48) and [Michael Ilseman](https://forums.swift.org/t/omitting-returns-in-string-case-study-of-se-0255/24283) provided analysis of use cases in the standard library and elsewhere. Many community members have made a strong case for this change, most recently [Dave Abrahams](https://forums.swift.org/t/if-else-expressions/22366). From 6d480ec4a3a6ba7bab0fe473e76147a574e6ede6 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 10 Dec 2022 23:15:08 +0000 Subject: [PATCH 2834/4563] Update 0380-if-switch-expressions.md (#1876) --- proposals/0380-if-switch-expressions.md | 42 ++++++++++++------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/proposals/0380-if-switch-expressions.md b/proposals/0380-if-switch-expressions.md index c6facca3ff..d3487e2b07 100644 --- a/proposals/0380-if-switch-expressions.md +++ b/proposals/0380-if-switch-expressions.md @@ -3,7 +3,7 @@ * Proposal: [SE-0380](0380-if-switch-expressions.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Hamish Knight](https://github.com/hamishknight) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (Decembe 7...December 21, 2022)** +* Status: **Active Review (December 7...December 21, 2022)** * Implementation: [apple/swift#612178](https://github.com/apple/swift/pull/62178), including a downloadable toolchain. * Review: ([pitch](https://forums.swift.org/t/pitch-if-and-switch-expressions/61149)), ([review](https://forums.swift.org/t/se-0380-if-and-switch-expressions/61899)) @@ -16,9 +16,9 @@ This proposal introduces the ability to use `if` and `switch` statements as expr ## Motivation -Swift has always had a terse but readable syntax for closures, which allows the `return` to be omitted when the body is a single expression. [SE-0255: Implicit Returns from Single-Expression Functions](https://github.com/apple/swift-evolution/blob/master/proposals/0255-omit-return.md) extended this to functions and properties with a single expression as their body. +Swift has always had a terse but readable syntax for closures, which allows the `return` to be omitted when the body is a single expression. [SE-0255: Implicit Returns from Single-Expression Functions](https://github.com/apple/swift-evolution/blob/main/proposals/0255-omit-return.md) extended this to functions and properties with a single expression as their body. -This omission of the `return` keyword is in keeping with Swift's low-ceremony approach, and is in common with many of Swift's peer "modern" languages. However, Swift differs from its peers in the lack support for `if` and `switch` expressions. +This omission of the `return` keyword is in keeping with Swift's low-ceremony approach, and is in common with many of Swift's peer "modern" languages. However, Swift differs from its peers in the lack of support for `if` and `switch` expressions. In some cases, this causes ceremony to make a return (ahem), for example: @@ -128,17 +128,17 @@ It has the effect of requiring more type context in ambiguous cases. The followi let x = if p { 0 } else { 1.0 } ``` -since when type checked individually, `0` is of type `Int` and `1.0`. The fix would be to disambiguate each branch. In this case, either by rewriting `0` as `0.0`, or by providing type context e.g. `0 as Double`. +since when type checked individually, `0` is of type `Int`, and `1.0` is of type `Double`. The fix would be to disambiguate each branch. In this case, either by rewriting `0` as `0.0`, or by providing type context e.g. `0 as Double`. This can be resolved by providing type context to each of the branches: ```swift - let y: Float = switch x.value { +let y: Float = switch x.value { case 0..<0x80: 1 case 0x80..<0x0800: 2.0 case 0x0800..<0x1_0000: 3.0 default: 4.5 - } +} ``` This decision is in keeping with other recent proposals such as [SE-0244: Opaque Result Types](https://github.com/apple/swift-evolution/blob/main/proposals/0244-opaque-result-types.md): @@ -272,13 +272,13 @@ A feel for the kind of expressions this could produce can be found in [this comm Full expressions would include various fairly conventional examples not proposed here: -``` +```swift let x = 1 + if .random() { 3 } else { 4 } ``` but also some pretty strange ones such as -``` +```swift for b in [true] where switch b { case true: true case false: false } {} ``` @@ -305,10 +305,10 @@ In this case, if `if` expressions were allowed to have postfix member expression ```swift let foo: String = do { - try bar() - } catch { - "Error \(error)" - } + try bar() +} catch { + "Error \(error)" +} ``` ### Support for `break` and `continue` @@ -320,7 +320,7 @@ Similar to `return`, statements that break or continue to a label, could be perm Often enthusiasm for `guard` leads to requests for `guard` to have parity with `if`. Returning a value from a `guard`'s else is very common, and could potentially be sugared as ```swift - guard hasNativeStorage else { nil } +guard hasNativeStorage else { nil } ``` This is appealing, but is really a different proposal, of allowing omission `return` in `guard` statements. @@ -367,7 +367,7 @@ The lack of this feature puts Swift's [claim](https://www.swift.org/about/) to b ### Alternative syntax -Instead of extending the current implicit return mechanism, where a single expression is treated as the returned value, this proposal could introduce a new syntax for expression versions of `if`/`switch`. For example, +Instead of extending the current implicit return mechanism, where a single expression is treated as the returned value, this proposal could introduce a new syntax for expression versions of `if`/`switch`. For example, in Java: ```java var response = switch (utterance) { @@ -378,9 +378,9 @@ var response = switch (utterance) { yield "everybody out!"; // yield = value of multi-statement branch }; default -> { - throw new IllegalStateException(utterance) - } -} + throw new IllegalStateException(utterance); + }; +}; ``` A similar suggestion was made during [SE-0255: Implicit Returns from Single-Expression Functions](https://forums.swift.org/t/se-0255-implicit-returns-from-single-expression-functions/), where an alternate syntax for single-expression functions was discussed e.g. `func sum() -> Element = reduce(0, +)`. In that case, the core team did not consider introduction of a separate syntax for functions to be sufficiently motivated. @@ -401,7 +401,7 @@ However, this then poses an issue when evolving to multi-statement branches. Unl let x = if .random() -> { let y = someComputation() - y * 2 + y * 2 } else -> fatalError() ``` @@ -419,7 +419,7 @@ let foo: String = or mixed branches with expressions and a return: -``` +```swift let x = if .random() -> 1 else -> return 2 @@ -427,10 +427,10 @@ let x = If a future direction of full expressions is considered, the `->` form may not work so well, especially when single-line expressions are desired e.g. -``` +```swift // is this (p ? 1 : 2) + 3 // or p ? 1 : (2 + 3) -let x = if p -> 1 else -> 2 + 4 +let x = if p -> 1 else -> 2 + 3 ``` ## Source compatibility From 7dba4c122fbe9e973ffa84f75dd401658ea9378e Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 12 Dec 2022 03:08:00 +0000 Subject: [PATCH 2835/4563] Update CONTRIBUTING.md Link to the "Code of Conduct" page. --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7acc2666a5..101ef4ab39 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,5 +4,6 @@ Before you initiate a pull request, please read: * [Swift Evolution Process](process.md) * [Commonly Rejected Changes](commonly_proposed.md) +* [Code of Conduct](https://www.swift.org/code-of-conduct/) Ideas should be thoroughly discussed on the [forums](https://forums.swift.org/c/evolution/18) first. From 930732f2d5a0c1b21e66b8068f00150973ba9ca6 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Mon, 12 Dec 2022 15:30:17 -0800 Subject: [PATCH 2836/4563] Add package-access-modifier proposal --- proposals/NNNN-package-access-modifier.md | 176 ++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 proposals/NNNN-package-access-modifier.md diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md new file mode 100644 index 0000000000..d8bff43920 --- /dev/null +++ b/proposals/NNNN-package-access-modifier.md @@ -0,0 +1,176 @@ +# New access modifier: `package` + +* Proposal: [SE-NNNN](NNNN-package-access-modifier.md) +* Authors: [Ellie Shin](https://github.com/elsh), [Alexis Laferriere](https://github.com/xymus) +* Review Manager: TBD +* Status: Awaiting review +* Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/61546) +* Review: [pitch](https://forums.swift.org/t/new-access-modifier-package/61459) + +## Introduction + +This proposal introduces `package` as a new access modifier. Currently, to access a symbol in another module, that symbol needs to be declared `public`. It allows access across modules within the same library but also outside of the library, which is undesirable. The new access modifier enables a more fine control over the visibility scope of such symbols. + +## Motivation + +Packages are often composed of multiple modules; packages exist as a way to organize modules in Swift, and organizing often involves splitting a module into smaller modules. For example, a module containing internal helper APIs can be split into a utility module only with the helper APIs and the other module(s) containing the rest. In order to access the helper APIs, however, the helper APIs need to be made public. The side effect of this is that they can “leak” to a client that should not have access to those symbols. Besides the scope of visibility, making them public also has an implication on the code size and performance. + +For example, here’s a scenario where App depends on modules from package 'gamePkg'. + +``` +App (Xcode project or appPkg) + |— Game (gamePkg) + |— Engine (gamePkg) +``` + +Here are source code examples. + +``` +[Engine] + +public struct MainEngine { + public init() { ... } + // Intended to be public + public var stats: String { ... } + // A helper function made public only to be accessed by Game + public func run() { ... } +} + +[Game] + +import Engine + +public func play() { + MainEngine().run() // Can access `run` as intended since it's within the same package +} + +[App] + +import Game +import Engine + +let engine = MainEngine() +engine.run() // Can access `run` from App even if it's not an intended behavior +Game.play() +print(engine.stats) // Can access `stats` as intended +``` + +In the above scenario, App can import Engine (a utility module in 'gamePkg') and access its helper API directly, even though the API is not intended to be used outside of its package. + + +## Proposed solution + +A current workaround for the above scenario is to use `@_spi`, `@_implemenationOnly`, or `@testable`. However, they have caveats. The `@_spi` requires a group name, which makes it verbose to use and harder to keep track of, and `@_implementationOnly` can be too limiting as we want to be able to restrict access to only portions of APIs. The `@testable` elevates all internal symbols to public, which leads to an increase of the binary size and the shared cache size. If there are multiple symbols with the same name from different modules, they will clash and require module qualifiers everywhere. It is hacky and is strongly discouraged for use. + +We want a solution that will clearly communicate that the visibility scope is somewhere between `internal` and `public`, and is limited to modules within a package, thus we propose a new access modifier `package`. + + +## Detailed design + + +### Declaration Site + +Using the scenario above, the helper API `run` can be declared `package`. + +``` +[Engine] + +public struct MainEngine { + public init() { ... } + public var stats: String { ... } + package func run() { ... } +} +``` + +The `package` access modifier can be added to any types where an existing access modifier can be added, e.g. `class`, `struct`, `enum`, `func`, `var`, `protocol`, etc. + + +### Use site + +The Game module can access the helper API `run` since it is in the same package as Engine. + +``` +[Game] + +import Engine + +public func play() { + MainEngine().run() // Can access `run` as it is a package symbol in the same package +} +``` + +However, if a client outside of the package tries to access the helper API, it will not be allowed. + +``` +[App] + +import Game +import Engine + +let engine = MainEngine() +engine.run() // Error: cannot find `run` in scope + +``` + +During type check a package name that's stored in an imported module binary will be looked up and compared with the current module's package name to allow or disallow access to a package symbol from that module. More details are explained below. + + +### Lookup by package name + +A new flag `-package-name` will be introduced to enable grouping modules per package. In Swift Package Manager, the input to the flag will be a package identity and will be passed down automatically. Each package identity is unique and can contain non-alphanumeric characters such as a hyphen and a dot (currently any URL valid characters are allowed); such characters will be transposed into a c99 identifier. Other build systems such as Xcode and Basel will need to pass a new command-line argument `-package-name` to the build command; the input may be Reverse-DNS package names so it can prevent potential clashes with other project or package names. + +``` +[Engine] swiftc -module-name Engine -package-name gamePkg ... +[Game] swiftc -module-name Game -package-name gamePkg ... +[App] swiftc App -package-name appPkg ... +``` + +When building the Engine module, the package name 'gamePkg' and package symbols will be stored in the Engine.swiftmodule. When building Game, the input to its package name 'gamePkg' will be compared with Engine's package name; since they match, access to package symbols is allowed. When building App, the name comparison shows 'appPkg' is different from its dependency module's so access to package symbols will be deined, which is what we want. + +If `-package-name` is not passed, but `package` access modifier is used, then an error will be thrown; Building files that do not have `package` symbols should continue to work without needing to pass in `-package-name`. + + +### Exportability + +The package symbols will be stored in .swiftmodule but not in .swiftinterface. The package name will be stored in both .swiftmodule and .swiftinterface, but only the swiftmodule will be loaded when building a same-package module; this will prevent loading a fall-back module (swiftinterface) which does not contain package symbols. + +The exportability rule at the declaration site will be similar to the the existing behavior; for example, a public class is not allowed to inherit a package class, and a public func is not allowed to have a package type in its signature. + +The `@inlinable` will be applicable to `package`; we plan to introduce`@usableFromPackageInline` to allow references from `@inlinable package` and not from `@inlinable public`. + +Package symbols will be exported in the final libraries/executables and emitted in llvm.used; we plan to introduce a build setting that lets users decide whether to hide package symbols if statically linked. + + +### Subclassing / Overrides + +A `package` class will not be allowed to be subclassed from another module, thus a `package` class member will not be allowed to be overidden either. + +This follows the behavior and optimization model for `public`; a `public` class has no subclasses outside of its defining module, and a `public` class member has no overrides outside of its defining module. The optimizer can take advantage of this information in whole-module builds to replace dynamic dispatch with static dispatch among other things. + +It also allows a linear progression from `private` through `open` where each step incrementally offers capabilities to a wider range of clients. If `package` were to allow subclassing/overrides outside of its defining modules, making a `package` class `public` would break subclasses defined in other modules within the same package. + +If a `package` class needs to be subclassed, it will need to be declared `open`, or be converted to a protocol, which is a recommended practice. + + +## Future Directions + +Limiting the scope of visibility per package can open up a whole lot of optimization opportunities. A package containing several modules can be treated as a resilience domain. + +Even with the library evolution enabled, modules within a package could be treated non-resilient. If same-package clients need access to swiftmodules, they don't need to be independently rebuildable and could have an unstable ABI; they could avoid resilience overhead and language rules such as the `@unknown default` requirement. + + +## Source compatibility + +The changes is additive, so it's not source breaking. + +## Effect on ABI stability + +Does not impact ABI stability. + +## Alternatives considered + +A current workaround is to use `@_spi`, `@_implemenationOnly`, or `@testable`. Each option has caveats. The `@_spi` requires a group name, which makes it verbose to use and harder to keep track of, and `@_implementationOnly` can be too limiting as we want to be able to restrict access to only portions of APIs. The `@testable` elevates all internal symbols to public, which leads to an increase of the binary size and the shared cache size. If there are multiple symbols with the same name from different modules, they will clash and require module qualifiers everywhere. It is hacky and is strongly discouraged for use. + +## Acknowledgments + +Alexis Laferriere, Doug Gregor, Becca Royal-Gordon, Allan Shortlidge, Artem Chikin, and Xi Ge provided helpful feedback and analysis. Alexis Laferriere has been part of the discussion from the beginning and reviewed implementation of the prototype. From 166b9af116cb4283b39634821a755fbb4ce91829 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 13 Dec 2022 14:57:20 +0000 Subject: [PATCH 2837/4563] Add a proposal for cross-compilation destinations --- .../NNNN-cross-compilation-destinations.md | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 proposals/NNNN-cross-compilation-destinations.md diff --git a/proposals/NNNN-cross-compilation-destinations.md b/proposals/NNNN-cross-compilation-destinations.md new file mode 100644 index 0000000000..8d1123b87c --- /dev/null +++ b/proposals/NNNN-cross-compilation-destinations.md @@ -0,0 +1,225 @@ +# Package Manager Feature name + +* Proposal: [SE-NNNN](NNNN-cross-compilation-destinations.md) +* Authors: [Max Desiatov](https://github.com/MaxDesiatov) +* Review Manager: TBD +* Status: **Awaiting implementation** +* Implementation: [apple/swift-package-manager#5911](https://github.com/apple/swift-package-manager/pull/5911), +[apple/swift-package-manager#5922](https://github.com/apple/swift-package-manager/pull/5922) + +## Introduction + +Cross-compilation is a common development use case. When cross-compiling, we need to refer to these two main concepts: + +* **host platform**, where developer's code is built; +* **target platform**, where developer's code is running. + +Another important term is **toolchain**, which is a set of executable binaries running on the host platform. Additionally, we define **SDK** as a set of dynamic and/or static libraries, headers, and other resources required to produce a binary for a target platform. Let’s call a toolchain and an SDK bundled together a **destination**. + +## Motivation + +Swift cross-compilation (CC) destinations are currently produced on an ad-hoc basis for different combinations of host and target platforms. For example, scripts that produce macOS → Linux CC destinations were created by both[ the Swift team ](https://github.com/apple/swift-package-manager/blob/swift-5.7-RELEASE/Utilities/build_ubuntu_cross_compilation_toolchain)and [the Swift community](https://github.com/SPMDestinations/homebrew-tap). At the same time, the distribution process of CC destinations is cumbersome. After building a destination tree on the file system, required metadata files rely on hardcoded absolute paths. Adding support for relative paths in destination's metadata and providing a unified way to distribute and install destinations as archives would clearly be an improvement to the multi-platform Swift ecosystem. + +The primary audience of this pitch are people who cross-compile from macOS to Linux. When deploying to single-board computers supporting Linux (e.g. Raspberry Pi), building on the target hardware may be too slow or run out of available memory. Quite naturally, users would prefer to cross-compile on their host machine when targeting these platforms. + +In other cases, building in a Docker container is not always the best solution for certain development workflows. For example, when working with Swift AWS Lambda Runtime, some developers may find that installing Docker just for building a project is a daunting step that shouldn’t be required. + +The solution described below is general enough to scale for any host/target platform combination. + +## Proposed solution + +Since CC destination is a collection of binaries arranged in a certain directory hierarchy, it makes sense to distribute it as an archive. We'd like to build on top of [SE-0305](https://github.com/apple/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md) and extend the `.artifactbundle` format to support this. + +Additionally, we propose introducing a new `swift destination` CLI command for installation and removal of CC destinations on the local filesystem. + +We introduce a notion of a top-level toolchain, which is the toolchain that handles user’s `swift destination` invocations. Parts of this top-level toolchain (linker, C/C++ compilers, and even the Swift compiler) can be overridden with tools supplied in `.artifactbundle` s installed by `swift destination` invocations. + +When the user runs `swift build` with the selected CC destination, the overriding tools from the corresponding bundle are invoked by `swift build` instead of tools from the top-level toolchain. + +## Detailed design + +### CC Destination Artifact Bundles + +As a quick reminder for a concept introduced in [SE-0305](https://github.com/apple/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md), an **artifact bundle** is a directory that has the filename suffix `.artifactbundle` and has a predefined structure with `.json` manifest files provided as metadata. + +The proposed structure of artifact bundles containing CC destinations looks like: + +``` +.artifactbundle +├ info.json +├ +│ ├ +│ │ ├ destination.json +│ │ └ +│ └ +│ ├ destination.json +│ └ +├ +│ └ +│ ├ destination.json +│ └ +├ +┆ └┄ +``` + +For example, a destination bundle allowing to cross-compile Swift 5.7 source code to recent versions of Ubuntu from macOS would look like this: + +``` +swift-5.7_ubuntu.artifactbundle +├ info.json +├ ubuntu_jammy +│ ├ arm64-apple-darwin +│ │ ├ destination.json +│ │ └ +│ └ x86_64-apple-darwin +│ ├ destination.json +│ └ +├ ubuntu_focal +│ └ x86_64-apple-darwin +│ ├ destination.json +│ └ +├ ubuntu_bionic +┆ └┄ +``` + +Here each artifact directory is dedicated to a specific CC destination, while binaries for a specific host platform are placed in `arm64-apple-darwin` and `x86_64-apple-darwin` subdirectories. + +Note the presence of `destination.json` files in each `` subdirectory. These files should contain a JSON dictionary with an evolved version of the schema of [existing destination.json files that SwiftPM already supports](https://github.com/apple/swift-package-manager/pull/1098) (hence `"version": 2` ) + +``` +{ + "version": 2, + "sdkRootDir": , + "toolchainBinDir": , + "runtimeDir": , + "hostTriples": [], + "targetTriples": [], + "extraSwiftCFlags": [], + "extraCCFlags": [], + "extraCXXFlags": [], + "extraLinkerFlags": [] +} +``` + +We propose that all relative paths in `destination.json` files should be validated not to "escape" the destination bundle for security reasons. That is, `../` components, if present in paths, will not be allowed to reference files and directories outside of a corresponding destination bundle. Symlinks will also be validated to prevent them from escaping out of the bundle. + +Lastly, `info.json` bundle manifests at the root of artifact bundles should specify `"type": "crossCompilationDestination"` for corresponding artifacts. Artifact identifiers in this manifest uniquely identify a CC destination. The rest of the properties of bundle manifests introduced in SE-0305 are preserved. + +### Destination Bundle Installation + +To manage CC destinations, we'd like to introduce a new `swift destination` command with three subcommands: + +* `swift destination install `, which downloads a given bundle if needed and installs it in a location discoverable by SwiftPM. For destinations installed from remote URLs an additional `--checksum` option is required, through which users of destinations can specify a checksum provided by publishers of destinations. The latter can produce a checksum by running `swift package compute-checksum` command (introduced in SE-0272) with the destination artifact bundle archive as an argument. +* `swift destination list`, which prints a list of already installed CC destinations with their identifiers. +* `swift destination delete ` will delete a given destination from the filesystem. + +### Using a CC Destination + +After a destination is installed, users can refer to it via its identifier passed to the `--destination` option, e.g. + +``` +swift build --destination ubuntu-jammy +``` + +We'd also like to make `--destination` flexible enough to recognize destination triples when there's only a single CC destination installed for such triple: + +``` +swift build --destination x86_64-unknown-linux-gnu +``` + +When multiple destinations support the same triple, an error message will be printed listing these destinations and asking the user to select a single one via its identifier. + +### CC Destination Bundle Generation + +CC destinations can be generated quite differently, depending on host and target platform combinations and user's needs. We intentionally don't specify how destination artifact bundles should be generated. + +Authors of this document intend to publish source code for a macOS → Linux CC destination generator, which community is welcome to fork and reuse for their specific needs. This generator will use Docker for setting up the build environment locally before copying it to the destination tree. Relying on Docker in this generator makes it easier to reuse and customize existing build environments. Important to clarify, that Docker is only used for bundle generation, and users of CC destinations do not need to have Docker installed on their machine to utilize it. + +As an example, destination publishers looking to add a library to an Ubuntu 22.04 destination environment would modify a `Dockerfile` similar to this one in CC destination generator source code: + +```docker +FROM swift:5.7-jammy + +apt-get install -y \ + # PostgreSQL library provided as an example. + libpq-dev + # Add more libraries as arguments to `apt-get install`. +``` + +Then to generate a new CC destinations, a generator executable delegates to Docker for downloading and installing required tools and libraries, including the newly added ones. After a Docker image with destination environment is ready, the generator copies files from the image to a corresponding `.artifactbundle` destination tree. + +## Security + +The proposed `--checksum` flag provides basic means of verifying destination bundle's validity. As a future direction, we'd like to consider sandboxing and codesigning toolchains running on macOS. + +## Impact on existing packages + +This is an additive change with no impact on existing packages. + +## Prior Art + +### Rust + +In the Rust ecosystem, its toolchain and standard library built for a target platform are managed by [the `rustup` tool](https://github.com/rust-lang/rustup). For example, artifacts required for cross-compilation to `aarch64-linux-unknown-gnu` are installed with [`rustup target add aarch64-linux-unknown-gnu`](https://rust-lang.github.io/rustup/cross-compilation.html). Then building for this target with Rust’s package manager looks like `cargo build --target=aarch64-linux-unknown-gnu` . + +Mainstream Rust tools don’t provide an easy way to create your own destinations/targets. You’re only limited to the list of targets provided by Rust maintainers. This likely isn’t a big problem per se for Rust users, as Rust doesn’t provide C/C++ interop on the same level as Swift. It means that Rust packages much more rarely than Swift expect certain system-provided packages to be available in the same way that SwiftPM allows with `systemLibrary` . + +Currently, Rust doesn’t supply all of the required tools when running `rustup target add`. It’s left to a user to specify paths to a linker that’s suitable for their host/target combination manually in a config file. We feel that this should be unnecessary, which is why destination bundles proposed for Swift can provide their own tools via `toolchainBinDir` property in `destination.json` . + +### Go + +Go’s standard library is famously self-contained and has no dependencies on C or C++ standard libraries. Because of this there’s no need to install additional targets and destinations. Cross-compiling in Go works out of the box by passing `GOARCH` and `GOOS` environment variables with chosen values, an example of this is `GOARCH=arm64 GOOS=linux go build` invocation. + +This would be a great experience for Swift, but it isn’t easily achievable as long as Swift standard library depends on C and C++ standard libraries. Any code interoperating with C and/or C++ would have to link with those libraries as well. When compared to Go, our proposed solution allows both dynamic and, at least on Linux when Musl is supported, full static linking. We’d like Swift to allow as much customization as needed for users to prepare their own destination bundles. + +## Alternatives Considered + +### Extensions Other Than `.artifactbundle` + +Some members of the community suggested that destination bundles should use a more specific extension. Since we're relying on the existing `.artifactbundle` format and extension, which is already used for binary targets, we think a specialized extension only for destinations would introduce an inconsistency. On the other hand, we think that specific extensions could make sense with a change applied at once. For example, we could consider `.binarytarget` and `.ccdestination` extensions for respective artifact types. But that would require a migration strategy for existing `.artifactbundle`s containing binary targets. + +### Building Applications in Docker Containers + +Instead of coming up with a specialized bundle format for destinations, users of Swift on macOS targeting Linux could continue to use Docker. But, as discussed in the [Motivation](#motivation) section, building applications in Docker doesn’t cover all of the possible use cases and complicates onboarding for new users. It also only supports Linux as a target platform, while we’re looking for a solution that can be generalized for all possible platforms. + +### Alternative Bundle Formats + +One alternative is to allow only a single host → target platform combination per bundle, but this may complicate distribution of destinations bundles in some scenarios. The existing `.artifactbundle` format is flexible enough to support bundles with a single or multiple combinations. + +Different formats of destination bundles can be considered, but we don't think those would be significantly different from the proposed one. If they were different, this would complicate bundle distribution scenarios for users who want to publish their own artifact bundles with executables, as defined in SE-0305. + +## Future Directions + +### Identifying Platforms with Dictionaries of Properties + +Platform triples are not specific enough in certain cases. For example, `aarch64-unknown-linux` host triple can’t prevent a user from installing a CC destination bundle on an unsupported Linux distribution. In the future we could deprecate `hostTriple` and `destinationTriple` JSON properties in favor of dictionaries with keys and values that describe aspects of platforms that are important for destinations. Such dictionaries could look like this: + +```json +"destination": { + "kernel": "Linux", + "libcFlavor": "Glibc", + "libcMinVersion": "2.36", + "cpuArchitecture": "aarch64" + // more platform capabilities defined here... +} +``` + +A toolchain providing this information could allow users to refer to these properties in their code for conditional compilation and potentially even runtime checks. + +### SwiftPM Plugins for Remote Running, Testing, Deployment, and Debugging + +After an application is built with a CC destination, there are other development workflow steps to be improved. We could introduce new types of plugins invoked by `swift run` and `swift test` for purposes of remote running, debugging, and testing. For Linux as a target platform, these plugins could delegate to Docker for running produced executables. + +### `swift destination select` subcommand + +While `swift destination select` subcommand or a similar one make sense for selecting a CC destination instead of passing `--destination` to `swift build` every time, users will expect `swift run` and `swift test` to also work for the target platform previously passed to `swift destination select`. That’s out of scope for this proposal on its own and depends on making plugins (from the previous subsection) or some other remote running and testing implementation to fully work. + +### SwiftPM and SourceKit-LSP improvements + +It is a known issue that SwiftPM can’t run multiple concurrent builds for different target platforms. This may cause issues when SourceKit-LSP is building a project for indexing purposes (for a host platform by default), while a user may be trying to build for a target platform for example for testing. One of these build processes will fail due to the process locking the build database. A potential solution would be to maintain separate build databases per platform. + +Another issue related to SourceKit-LSP is that [it always build and indexes source code for the host platform](https://github.com/apple/sourcekit-lsp/issues/601). Ideally, we want it to maintain indices for multiple platforms at the same time. Users should be able to select to target platforms and corresponding indices to enable semantic syntax highlighting, auto-complete, and other features for areas of code that are conditionally compiled with `#if` directives. + +### Source-Based CC Destinations + +One interesting solution is distributing source code of a minimal base destination, as explored by [Zig programming language](https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html). In this scenario, a cross-compilation destination binaries are produced on the fly when needed. We don't consider this option to be mutually exclusive with solutions proposed in this document, and so it could be explored in the future for Swift as well. However, this requires reducing the number of dependencies that Swift runtime and core libraries have. \ No newline at end of file From 400fc0962b01ed09aa29e71e701c637e60bbfa39 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 13 Dec 2022 22:24:48 -0700 Subject: [PATCH 2838/4563] Add Proposal to Deprecate Framework-Specific Attributes --- ...eprecate-framework-specific-entrypoints.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 proposals/deprecate-framework-specific-entrypoints.md diff --git a/proposals/deprecate-framework-specific-entrypoints.md b/proposals/deprecate-framework-specific-entrypoints.md new file mode 100644 index 0000000000..71f98bce74 --- /dev/null +++ b/proposals/deprecate-framework-specific-entrypoints.md @@ -0,0 +1,60 @@ +# Deprecate @UIApplicationMain and @NSApplicationMain + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Robert Widmann](https://github.com/codafi) +* Review Manager: TBD +* Status: **[Implemented](https://github.com/apple/swift/pull/62151)** + +## Introduction + +`@UIApplicationMain` and `@NSApplicationMain` used to be the standard way for iOS and macOS apps respectively to declare a synthesized platform-specific entrypoint for an app. These functions have since been obsoleted by [SE-0281](https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md) and the `@main` attribute, and now represent a confusing bit of duplication in the language. This proposal seeks to deprecate these alternative entrypoint attributes in favor of `@main` in Swift 5.8, and makes their use in Swift 6 a hard error. + +Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/deprecate-uiapplicationmain-and-nsapplicationmain/) + +## Motivation + +UIKit and AppKit have fully embraced the `@main` attribute and have made application adoption as simple as conforming to the `UIApplicationDelegate` and `NSApplicationDelegate` protocols. This now means that an author of an application is presented with two different, but ultimately needless, choices for an entrypoint: + +* Use hard coded framework-specific attributes +* Use the language’s general-purpose syntax for declaring an entrypoint + +The right choice is clearly the latter. Moreover, having two functionally identical ways to express the concept of an app-specific entrypoint is clutter at best and confusing at worst. This proposal seeks to complete the migration work implied by [SE-0281](https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md) by having the compiler push Swift authors towards the more general, unified solution. + +## Proposed solution + +The use of both `@UIApplicationMain` and `@NSApplicationMain` under Swift 5 language modes will unconditionally warn and offer to replace these attributes with the appropriate conformances. In Swift 6 language modes, usage of these attributes will result in a hard error. + +## Detailed design + +>Because `@UIApplicationMain` and `@NSApplicationMain` have remarkably similar usages in abstract, this portion of the document will only use examples of `@UIApplicationMain` and assume that the case for `@NSApplicationMain` follows similarly. + + +Issue a diagnostic asking the user to remove `@UIApplicationMain`. The existing conformance to `UIApplicationDelegate` will kick in when the code is next built and the general-purpose entrypoint will be added instead. + +``` +@UIApplicationMain // warning: '@UIApplicationMain' is deprecated in Swift 5 + // fixit: Change `@UIApplicationMain` to `@main` +final class MyApplication: UIResponder, UIApplicationDelegate { + /**/ +} +``` + +We can get away with this simple remedy because +- A Swift module can have at most one of: @UIApplicationMain, @main, and @NSApplicationMain +- A class annotated with @UIApplicationMain or @NSApplicationMain must conform directly to the corresponding delegate protocol anyways. They therefore inherit the `main` entrypoint [provided by these protocols](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/3656306-main), they just aren't using it! + +## Source compatibility + +This proposal is intentionally source-breaking, but that breakage is relegated to a new language mode and the diagnostics involved provide a clear and simple migration path for existing usages of these entrypoint attributes. + +## Effect on ABI stability + +This proposal has no impact on ABI. + +## Effect on API resilience + +None. + +## Alternatives considered + +We could choose not to do this. From 84815b447beaeb66221b766401958da55f908c1e Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Thu, 15 Dec 2022 18:16:59 +0000 Subject: [PATCH 2839/4563] Proposal for TaskGroup discardResults. (#1879) * Proposal for TaskGroup discardResults. * New PR --- proposals/NNNN-task-group-discard-results.md | 161 +++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 proposals/NNNN-task-group-discard-results.md diff --git a/proposals/NNNN-task-group-discard-results.md b/proposals/NNNN-task-group-discard-results.md new file mode 100644 index 0000000000..5edffadb37 --- /dev/null +++ b/proposals/NNNN-task-group-discard-results.md @@ -0,0 +1,161 @@ +# discardResults for TaskGroups + +* Proposal: [SE-NNNN](NNNN-task-group-discard-results.md) +* Authors: [Cory Benfield](https://github.com/Lukasa), [Konrad Malawski](https://github.com/ktoso) +* Review Manager: TBD +* Status: Pull request available at https://github.com/apple/swift/pull/62361 + +### Introduction + +We propose to introduce a new boolean parameter, `discardResults`, to `TaskGroup` and `ThrowingTaskGroup`. This parameter controls whether the `TaskGroup` retains the results of its completed child `Task`s for passing to `next()`, or whether it discards those results immediately. + +Pitch thread: [Task Pools](https://forums.swift.org/t/pitch-task-pools/61703). + +## Motivation + +Task groups are the building block of structured concurrency, allowing for the Swift runtime to relate groups of tasks together. This enables powerful features such as automatic cancellation propagation, correctly propagating errors, and ensuring well-defined lifetimes, as well as providing diagnostic information to programming tools. + +The version of Task Groups introduced in [SE-0304](https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md) provides all of these features. However, it also provides the ability to propagate return values to the user of the task group. This capability provides an unexpected limitation in some use-cases. + +As users of Task Groups are able to retrieve the return values of child tasks, it implicitly follows that the Task Group preserves at least the `Result` of any completed child task. As a practical matter, the task group actually preseves the entire `Task` object. This data is preserved until the user consumes it via one of the Task Group consumption APIs, whether that is `next()` or by iterating the Task Group. + +The result of this is that Task Groups are ill-suited to running for a potentially unbounded amount of time. An example of such a use-case is managing connections accepted from a listening socket. A simplified example of such a workload might be: + +```swift +try await withThrowingTaskGroup(of: Void.self) { group in + while let newConnection = try await listeningSocket.accept() { + group.addTask { + handleConnection(newConnection) + } + } +} +``` + +As written, this task group will leak all the child `Task` objects until the listening socket either terminates or throws. If this was written for a long-running server, it is entirely possible for this Task Group to survive for a period of days, leaking thousands of Task objects. For stable servers, this will eventually drive the process into memory exhaustion, forcing it to be killed by the OS. + +The current implementation of Task Groups do not provide a practical way to avoid this issue. Task Groups are (correctly) not `Sendable`, so neither the consumption of completed `Task` results nor the submission of new work can be moved to a separate `Task`. + +The most natural attempt to avoid this unbounded memory consumption would be to attempt to occasionally purge the completed task results. An example might be: + +```swift +try await withThrowingTaskGroup(of: Void.self) { group in + while let newConnection = try await listeningSocket.accept() { + group.addTask { + handleConnection(newConnection) + } + try await group.next() + } +} +``` + +Unfortunately, all of the methods for attempting to pop the queue of completed `Task`s will suspend if all currently live child `Task`s are executing. This means that the above pattern (or any similar pattern) is at risk of occasional livelocks, where pending connections could be accepted, but the `Task` is blocked waiting for existing work to complete. + +There is only one design pattern to avoid this issue, which involves forcibly bounding the maximum concurrency of the Task Group. This pattern looks something like the below: + +```swift +try await withThrowingTaskGroup(of: Void.self) { group in + // Fill the task group up to maxConcurrency + for _ in 0..( + of childTaskResultType: ChildTaskResult.Type, + returning returnType: GroupResult.Type = GroupResult.self, + discardResults: Bool = false, + body: (inout TaskGroup) async -> GroupResult +) async -> GroupResult { + +public func withThrowingTaskGroup( + of childTaskResultType: ChildTaskResult.Type, + returning returnType: GroupResult.Type = GroupResult.self, + discardResults: Bool = false, + body: (inout ThrowingTaskGroup) async throws -> GroupResult +) async rethrows -> GroupResult { +``` + +## Alternatives Considered + +### Introducing new types + +The [original pitch](https://forums.swift.org/t/pitch-task-pools/61703) introduced two new types, `TaskPool` and `ThrowingTaskPool`. These types were introduced in order to expose at the type system level the inability to iterate the pool for new tasks. This would avoid the `next()` behaviour introduced in this pitch, where `next()` always returns `nil`. This was judged a worthwhile change to justify introducing new types. + +Several reviewers of the pitch felt that this was not a sufficiently useful capability to justify the introduction of the new types, and that the pitched behaviour more properly belonged as a "mode" of operation on `TaskGroup`. In line with that feedback, this proposal has moved to using the `discardResults` option. + +### Error throwing behaviour + +The pitch proposes that `ThrowingTaskGroup` with `discardResults` set to `true` will throw only the _first_ error thrown by a child `Task`. This means that all subsequent errors will be discarded, which is an unfortunate loss of information. Two alternative behaviours could be chosen: we could not add `discardResults` to `ThrowingTaskGroup` at all, or we could throw an aggregate error that contains all errors thrown by the child `Task`s. + +Not allowing `discardResults` on `ThrowingTaskGroup` is a substantial ergonomic headache. Automatic error propagation is one of the great features of structured concurrency, and not being able to use it in servers or other long-running processes is an unnecessary limitation, especially as it's not particularly technically challenging to propagate errors. For this reason, we do not think it's wise to omit `discardResults` on `ThrowingTaskGroup`. + +The other alternative is to throw an aggregate error. This would require that `ThrowingTaskGroup` persist all (or almost all) errors thrown by child tasks and merge them together into a single error `struct` that is thrown. This idea is a mixed bag. + +The main advantage of throwing an aggregate error is that no information is lost. Programs can compute on all errors that were thrown, and at the very least can log or provide other metrics based on those errors. Avoiding data loss in this way is valuable, and gives programmers more flexibility. + +Throwing an aggregate error has two principal disadvantages. The first is that aggregate errors do not behave gracefully in `catch` statements. If a child task has thrown `MyModuleError`, programmers would like to write `catch MyModuleError` in order to handle it. Aggregate errors break this situation, even if only one error is thrown: programmers have to write `catch let error = error as? MyAggregateError where error.baseErrors.contains(where: { $0 is MyModuleError })`, or something else equally painful. + +The other main disadvantage is the storage bloat from `CancellationError`. The first thrown error will auto-cancel all child `Task`s. This is great, but that cancellation will likely manifest in a thrown `CancellationError`, which will presumably bubble to the top and be handled by the `ThrowingTaskGroup`. This means that a `ThrowingTaskGroup` will likely store a substantial collection of errors, where all but the first are `CancellationError`. This is a substantial regression in convenience for the mainline case, with additional costs in storage, without providing any more meaningful information. + +For these reasons we've chosen the middle behaviour, where only one error is thrown. We think there is merit in throwing an aggregate error, however, and we'd like community feedback on this alternative. + +### Child Task for reaping + +An alternative would be to have Task Group spin up a child `Task` that can be used to consume tasks from the group. The API surface would look something like this: + +```swift +withTaskGroupWithChildTask(of: Void.self) { group in + group.addTask { + handleConnection(newConnection) + } +} +consumer: { group in + for task in group { } +} +``` + +The advantage of this variant is that it is substantially more flexible, and allows non-`Void`-returning tasks. The downside of this variant is that it muddies the water on the question of whether Task Groups are `Sendable` (requiring a specific-exemption for this use-case) and forces users to understand the lifetime of a pair of different closures. + +## Future Directions + +### Error Handling + +A number of concerns were raised during the pitch process that the "throw the first error only" pattern may be insufficiently flexible. Community members were particularly interested in having some sort of error filter function that could be used to filter, accumulate, or discard errors as needed. + +The proposers feel that introducing this API surface in the first version of this feature adds significant complexity to this type. This requires us to be confident that the API surface proposed is going to serve the necessary use-cases, without adding unnecessary cognitive load. It's also not entirely clear where the line is between features that can be handled using `try`/`catch` and features that require a new error filter function. + +As a result, the proposrs have elected to defer implementing anything here until there are real-world examples to generalise from. Having some sort of error filter is likely to be valuable, and the implementation will preserve the capability to implement such a function, but for now the proposal is going to be kept relatively small. \ No newline at end of file From 5c13ac68803fbc74f98ef0af50634260eeeb5c03 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 15 Dec 2022 10:26:30 -0800 Subject: [PATCH 2840/4563] Initiate review of SE-0381 "discardResults for TaskGroups" (#1882) --- ...scard-results.md => 0381-task-group-discard-results.md} | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename proposals/{NNNN-task-group-discard-results.md => 0381-task-group-discard-results.md} (98%) diff --git a/proposals/NNNN-task-group-discard-results.md b/proposals/0381-task-group-discard-results.md similarity index 98% rename from proposals/NNNN-task-group-discard-results.md rename to proposals/0381-task-group-discard-results.md index 5edffadb37..4ebd7033e1 100644 --- a/proposals/NNNN-task-group-discard-results.md +++ b/proposals/0381-task-group-discard-results.md @@ -1,9 +1,10 @@ # discardResults for TaskGroups -* Proposal: [SE-NNNN](NNNN-task-group-discard-results.md) +* Proposal: [SE-0381](0381-task-group-discard-results.md) * Authors: [Cory Benfield](https://github.com/Lukasa), [Konrad Malawski](https://github.com/ktoso) -* Review Manager: TBD -* Status: Pull request available at https://github.com/apple/swift/pull/62361 +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active Review (December 15...December 29, 2022)** +* Implementation: https://github.com/apple/swift/pull/62361 ### Introduction From e42802f527510b06878aa1105e3f00053b44e5c6 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 16 Dec 2022 10:43:07 -0800 Subject: [PATCH 2841/4563] Expression macros (#1861) * Draft of expression macros proposal * Add some acknowledgments * Fix a typo * fix formatting typo in expression macros * Add link to pitch thread * Address a number of comments brought up in the pitch thread * Add generic where clause to macro declarations * Macros cannot produce opaque result types * Note that MacroEvaluationContext will grow to include build environment stuff * Revisions based on pitch discussion * Rename `MacroEvaluationContext` to `MacroExpansionContext`. * Remove `MacroResult` and instead allow macros to emit diagnostics via the macro expansion context. * Remove `sourceLocationConverter` from the macro expansion context; it provides access to the whole source file, which interferes with incremental builds. * Rename `ExpressionMacro.apply` to `expansion(of:in)` to make it clear that it's producing the expansion of a syntax node within a given context. * Remove the implementations of `#column`, as well as the implication that things like `#line` can be implemented with macros. Based on the above changes, they cannot. * Introduce a new section providing declarations of macros for the various `#` expressions that exist in the language, but will be replaced with (built-in) macros. * Replace the `external-macro-name` production for defining macros with the more-general `macro-expansion-expression` * Introduce SwiftPM manifest extensions to define macro plugins. * Reorganize and clean up the detailed design * Add some future directions and alternatives considered * Separate out the SwiftPM changes * Simplified the type signature of the `#externalMacro` built-in macro. * Link to the example macro repository * Added `@expression` to the macro to distinguish it from other kinds of macros that could come in the future. * Make `expansion(of:in:)` throwing, and have that error be reported back to the user. * Expand on how the various builtin standard library macros will work. * Update and rename nnnn-expression-macros.md to 0382-expression-macros.md Co-authored-by: Konrad `ktoso` Malawski Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- proposals/0382-expression-macros.md | 520 ++++++++++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100644 proposals/0382-expression-macros.md diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md new file mode 100644 index 0000000000..3f94e9aaee --- /dev/null +++ b/proposals/0382-expression-macros.md @@ -0,0 +1,520 @@ +# Expression Macros + +* Proposal: [SE-0382](0382-expression-macros.md) +* Authors: [Doug Gregor](https://github.com/DougGregor) +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Active review (December 16...30, 2022)** +* Implementation: Partial implementation is available in `main` under the experimental feature flag `Macros`. An [example macro repository](https://github.com/DougGregor/swift-macro-examples) provides a way to experiment with this feature. +* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861/16)) + +## Introduction + +Expression macros provide a way to extend Swift with new kinds of expressions, which can perform arbitary syntactic transformations on their arguments to produce new code. Expression macros make it possible to extend Swift in ways that were only previously possible by introducing new language features, helping developers build more expressive libraries and eliminate extraneous boilerplate. + +Swift-evolution thread: + +## Motivation + +Expression macros are one part of the [vision for macros in Swift](https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900), which lays out general motivation for introducing macros into the language. Expressions in particular are an area where the language already provides decent abstractions for factoring out runtime behavior, because one can create a function that you call as an expression from anywhere. However, with a few hard-coded exceptions like `#file` and `#line`, an expression cannot reason about or modify the source code of the program being compiled. Such use cases will require external source-generating tools, which don't often integrate cleanly with other tooling. + +## Proposed solution + +This proposal introduces the notion of expression macros, which are used as expressions in the source code (marked with `#`) and are expanded into expressions. Expression macros can have parameters and a result type, much like a function, which describes the effect of the macro expansion on the expression without requiring the macro to actually be expanded first. + +The actual macro expansion is implemented with source-to-source translation on the syntax tree: the expression macro is provided with the syntax tree for the macro expansion itself (e.g., starting with the `#` and ending with the last argument), which it can rewrite into the expanded syntax tree. That expanded syntax tree will be type-checked against the result type of the macro. + +As a simple example, let's consider a `stringify` macro that takes a single argument as input and produces a tuple containing both the original argument and also a string literal containing the source code for that argument. This macro could be used in source code as, for example: + +```swift +#stringify(x + y) +``` + +and would be expanded into + +```swift +(x + y, "x + y") +``` + +The type signature of a macro is part of its declaration, which looks a lot like a function: + +```swift +@expression macro stringify(_: T) -> (T, String) +``` + +### Type-checked macro arguments and results + +Macro arguments are type-checked against the parameter types of the macro prior to instantiating the macro. For example, the macro argument `x + y` will be type-checked; if it is ill-formed (for example, if `x` is an `Int` and `y` is a `String`), the macro will never be expanded. If it is well-formed, the generic parameter `T` will be inferred to the result of `x + y`, and that type is carried through to the result type of the macro. There are several benefits to this type-checked model: + +* Macro implementations are guaranteed to have well-typed arguments as inputs, so they don't need to be concerned about incorrect code being passed into the macro. +* Tools can treat macros much like functions, providing the same affordances for code completion, syntax highlighting, and so on, because the macro arguments follow the same rules as other Swift code. +* A macro expansion expression can be partially type-checked without having to expand the macro. This allows tools to still have reasonable results without performing macro expansion, as well as improving compile-time performance because the same macro will not be expanded repeatedly during type inference. + +When the macro is expanded, the expanded syntax tree is type-checked against the result type of the macro. In the `#stringify(x + y)` case, this means that if `x + y` had type `Int`, the expanded syntax tree (`(x + y, "x + y")`) is type-checked with a contextual type of `(Int, String)`. + +The type checking of macro expressions is similar to type-checking a call, allowing type inference information to flow from the macro arguments to the result type and vice-versa. For example, given: + +```swift +let (a, b): (Double, String) = #stringify(1 + 2) +``` + +the integer literals `1` and `2` would be assigned the type `Double`. + +### Syntactic translation + +Macro expansion is a syntactic operation, which takes as input a well-formed syntax tree consisting of the full macro expansion expression (e.g., `#stringify(x + y)`) and produces a syntax tree as output. The resulting syntax tree is then type-checked based on the macro result type. + +Syntactic translation has a number of benefits over more structured approaches such as direct manipulation of a compiler's Abstract Syntax Tree (AST) or internal representation (IR): + +* A macro expansion can use the full Swift language to express its effect. If it can be written as Swift source code at that position in the grammar, a macro can expand to it. +* Swift programmers understand Swift source code, so they can reason about the output of a macro when applied to their source code. This helps both when authoring and using macros. +* Source code that uses macros can be "expanded" to eliminate the use of the macro, for example to make it easier to reason about or debug, or make it work with an older Swift compiler that doesn't support macros. +* The compiler's AST and internal representation need not be exposed to clients, which would limit the ability of the compiler to evolve and improve due to backward-compatibility concerns. + +On the other hand, purely syntactic translations have a number of downsides, too: + +* Syntactic macro expansions are prone to compile-time failures, because we're effectively working with source code as strings, and it's easy to introduce (e.g.) syntax errors or type errors in the macro implementation. +* Syntactic macro expansions are re-parsed and re-type-checked, which incurs more compile-time overhead than an approach that (say) manipulated the AST or IR directly. +* Syntactic macros are not *hygienic*, meaning that the way in which a macro expansion is processed depends on the environment in which it is expanded, and can affect that environment. + +The proposed macro design attempts to mitigate these problems, but they are somewhat fundamental to the use of syntactic macros. On balance, the ease-of-use and easy-of-interpretation of syntactic macros outweighs these problems. + +### Macros defined as separate programs + +Macro definitions operate on syntax trees. Broadly speaking, there are two different ways in which a macro's expansion operation can be defined: + +* *A declarative set of transformations*: this involves extending the language with a special syntax that allows macros to define how the macro is expanded given the macro inputs, and the compiler applies those rules for each macro expansion. The C preprocessor employs a simplistic form of this, but Racket's [pattern-based macros](https://docs.racket-lang.org/guide/pattern-macros.html) and Rust's [declarative macros](https://doc.rust-lang.org/book/ch19-06-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming) offer more advanced rules that match the macro arguments to a pattern and then perform a a rewrite to new syntax as described in the macro. For Swift to adopt this approach, we would likely need to invent a pattern language for matching and rewriting syntax trees. +* *An executable program that transforms the source*: this involves running a program that manipulates the program syntax directly. How the program is executed depends a lot on the environment: [Scala 3 macros](https://docs.scala-lang.org/scala3/guides/macros/macros.html) benefit from the use of the JVM so they can intertwine target code (the program being generated) and host code (where the compiler is running), whereas Rust's [procedural macros](https://doc.rust-lang.org/reference/procedural-macros.html) are built into a separate crate that the compiler interacts with. For Swift to adopt this approach, we would either need to build a complete interpreter for Swift code in the compiler, or take an approach similar to Rust's and build macro definitions as separate programs that the compiler can interact with. + +We propose the latter approach, where a macro definition is a separate program that operates on Swift syntax trees using the [swift-syntax](https://github.com/apple/swift-syntax/) package. Expression macros are defined as types that conform to the `ExpressionMacro ` protocol: + +```swift +public protocol ExpressionMacro: Macro { + /// Expand a macro described by the given macro expansion expression + /// within the given context to produce a replacement expression. + static func expansion( + of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext + ) throws -> ExprSyntax +} +``` + +The `expansion(of:in:)` method takes as arguments the syntax node for the macro expansion expression (e.g., `#stringify(x + y)`) and a "context" that provides more information about the compilation context in which the macro is being expanded. It produces a macro result that includes the rewritten syntax tree. + +The specifics of `Macro`, `ExpressionMacro`, and `MacroExpansionContext` will follow in the Detailed Design section. + +### The `stringify` macro implementation + +Let's continue with the implementation of the `stringify` macro. It's a new type `StringifyMacro` that conforms to `ExpressionMacro`: + +```swift +import SwiftSyntax +import SwiftSyntaxBuilder +import _SwiftSyntaxMacros + +public struct StringifyMacro: ExpressionMacro { + public static func expansion( + of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext + ) -> ExprSyntax { + guard let argument = node.argumentList.first?.expression else { + fatalError("compiler bug: the macro does not have any arguments") + } + + return "(\(argument), \(literal: argument.description))" + } +} +``` + +The `expansion(of:in:)` function is fairly small, because the `stringify` macro is relatively simple. It extracts the macro argument from the syntax tree (the `x + y` in `#stringify(x + y)`) and then forms the resulting tuple expression by interpolating in the original argument both as a value and then as source code in [a string literal](https://github.com/apple/swift-syntax/blob/main/Sources/SwiftSyntaxBuilder/ConvenienceInitializers.swift#L259-L265). That string is then parsed as an expression (producing an `ExprSyntax` node) and returned as the result of the macro expansion. This is a simple form of quasi-quoting provided by the `SwiftSyntaxBuilder` module, implemented by making the major syntax nodes (`ExprSyntax` in this case, for expressions) conform to [`ExpressibleByStringInterpolation`](https://developer.apple.com/documentation/swift/expressiblebystringinterpolation), where existing syntax nodes can be interpolated into string literals containing the expanded Swift code. + +The `StringifyMacro` struct is the implementation for the `stringify` macro declared earlier. We will need to tie these together in the source code via some mechanism. We propose to provide a builtin macro that names the module and the `ExpressionMacro` type name within the macro declaration following an `=`, e.g., + +```swift +@expression macro stringify(_: T) -> (T, String) = + #externalMacro(module: "ExampleMacros", type: "StringifyMacro") +``` + +## Detailed design + +There are two major pieces to the macro design: how macros are declared and expanded within a program, and how they are implemented as separate programs. The following sections provide additional details. + +### Macro declarations + +A macro declaration is described by the following grammar: + +``` +declaration -> macro-declaration + +macro-declaration -> macro-head identifier generic-parameter-clause[opt] macro-signature macro-definition[opt] generic-where-clause[opt] + +macro-head -> attributes[opt] declaration-modifiers[opt] 'macro' + +macro-signature -> parameter-clause macro-function-signature-result[opt] +macro-signature -> ':' type + +macro-function-signature-result -> '->' type + +macro-definition -> '=' macro-expansion-expression +``` + +The signature of a macro is either function-like (`(_ argument: T) -> (T, String)`) or value-like (`: Int`), depending on the form of the `macro-signature`. The `@expression` attribute applies only to macros. It indicates that the macro is an expression macro. + +Macros can only be declared at file scope. They can be overloaded in the same way as functions, so long as the argument labels, parameter types, or result type differ. + +The `macro-definition` provides the implementation used to expand the macro. It is always a macro expansion expression, so all non-builtin macros are defined in terms of other macros, terminating in a builtin macro whose definition is provided by the compiler. The arguments provided within the `macro-expansion-expression` of the macro definition must either be direct references to the parameters of the enclosing macro or must be literals. The `macro-expansion-expression` is type-checked (to ensure that the argument and result types make sense), but no expansion is performed at the time of definition. Rather, expansion of the macro referenced by the `macro-definition` occurs when the macro being declared is expanded. See the following section on macro expansions for more information. + +Macro result types cannot include opaque result types. Macro parameters cannot have default arguments. + +### Macro expansion + +A macro expansion expression is described by the following grammar: + +``` +primary-expression -> macro-expansion-expression +macro-expansion-expression -> '#' identifier generic-argument-clause[opt] function-call-argument-clause[opt] trailing-closures[opt] +``` + +When either a `function-call-argument-clause` or a `trailing-closures` term is present, the identifier must refer to a function-like macro. When neither is present, the identifier must refer to a value-like macro. There is no such thing as a value of macro type. + +The `#` syntax for macro expansion expressions was specifically chosen because Swift already contains a number of a `#`-prefixed expressions that are macro-like in nature, some of which could be implemented directly as expression macros. The macro referenced by the `identifier` must be an an expression macro, as indicated by `@expression` on the corresponding macro declaration. + +When a macro expansion is encountered in the source code, it's expansion occurs in two phases. The first phase is the type-check phase, where the arguments to the macro are type-checked against the parameters of the named macro, and the result type of the named macro is checked against the context in which the macro expansion occurs. This type-checking is equivalent to that performed for a function call (for function-like macros) or a property reference (for value-like macros), and does not involve the macro definition. + +The second phase is the macro expansion phase, during which the syntax of the macro arguments is provided to the macro definition. For builtin-macro definitions, the behavior at this point depends on the semantics of the macro, e.g., the `externalMacro` macro invokes the external program and provides it with the source code of the macro expansion. For other macros, the arguments are substituted into the `macro-expansion-expression` of the definition. For example: + +```swift +@expression macro prohibitBinaryOperators(_ value: T, operators: [String]) -> T = + #externalMacro(module: "ExampleMacros", type: "ProhibitBinaryOperators") +@expression macro addBlocker(_ value: T) -> T = #prohibitBinaryOperators(value, operators: ["+"]) + +#addBlocker(x + y * z) +``` + +Here, the macro expansion of `#addBlocker(x + y * z)` will first expand to `#prohibitBinaryOperators(x + y * z, operators: ["+"])`. Then that expansion will be processed by the `ExampleMacros.ProhibitBinaryOperators`, which would be defined as a struct conforming to `ExpressionMacro`. + +Macro expansion produces new source code (in a syntax tree), which is then type-checked using the original macro result type as its contextual type. For example, the `stringify` example macro returned a `(T, String)`, so when given an argument of type `Int`, the result of expanding the macro would be type-checked as if it were on the right-hand side of + +```swift +let _: (Int, String) = +``` + +Macro expansion expressions can occur within the arguments to a macro. For example, consider: + +```swift +#addBlocker(#stringify(x + 1)) +``` + +The first phase of the macro type-check does not perform any macro expansion: the macro expansion expression `#stringify(1 + 2)` will infer that it's `T` is `Int`, and will produce a value of type `(Int, String)`. The `addBlocker` macro expansion expression will infer that it's `T` is `(Int, String)`, and the result is the same. + +The second phase of macro expansions occurs outside-in. First, the `addBlocker` macro is expanded, to `#prohibitBinaryOperators(#stringify(1 + 2), operators: ["+"])`. Then, the `prohibitBinaryOperators` macro is expanded given those (textual) arguments. The expansion result it produces will be type-checked, which will end up type-checking `#stringify(1 + 2)` again and, finally, expanding `#stringify(1 + 2)`. + +From an implementation perspective, the compiler reserves the right to avoid performing repeated type checking of the same macro arguments. For example, we type-checked `#stringify(1 + 2)` in the first phase of the expansion of `prohibitBinaryOperators`, and then again on the expanded result. When the compiler recognizes that the same syntax node is being re-used unmodified, it can re-use the types computed in the first phase. This is an important performance optimization for the type checker. + +### Macro implementation library + +Macro definitions will make use of the [swift-syntax](https://github.com/apple/swift-syntax) package, which provides the Swift syntax tree manipulation and parsing capabilities for Swift tools. The `SwiftSyntaxMacros` module will provide the functionality required to define macros. + +#### `Macro` protocols + +The `Macro` protocol is the root protocol for all kinds of macro definitions. At present, it does not have any requirements: + +```swift +public protocol Macro { } +``` + +The `ExpressionMacro` protocol is used to describe expression macros: + +```swift +public protocol ExpressionMacro: Macro { + /// Expand a macro described by the given macro expansion expression + /// within the given context to produce a replacement expression. + static func expansion( + of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext + ) throws -> ExprSyntax +} +``` + +The `MacroExpansionExprSyntax` type is the `swift-syntax` node describing the `macro-expansion-expression` grammar term from above, so it carries the complete syntax tree (including all whitespace and comments) of the macro expansion as it appears in the source code. + +Macro definitions should conform to the `ExpressionMacro` protocol and implement their syntactic transformation via `expansion(of:in:)`, returning the new expression as a syntax node. + +If the macro expansion cannot proceed for some reason, the `expansion(of:in:)` operation can throw an error rather than try to produce a new syntax node. The compiler will then report the error to the user. More detailed diagnostics can be provided via the macro expansion context. + +#### `MacroExpansionContext` + +The macro expansion context provides additional information about the environment in which the macro is being expanded. This context can be queried as part of the macro expansion: + +```swift +/// System-supplied structure that provides information about the context in +/// which a given macro is being expanded. +public struct MacroExpansionContext { + /// The name of the module in which the macro is being expanded. + public let moduleName: String + + /// The name of the file in which the macro is being expanded, without + /// any additional path information. + public let fileName: String + + /// Create a new macro expansion context. + public init(moduleName: String, fileName: String) + + /// Generate a unique local name for use in the macro. + public mutating func createUniqueLocalName() -> TokenSyntax + + /// Emit a diagnostic (i.e., warning or error) that indicates a problem with the macro + /// expansion. + public mutating func diagnose(_ diagnostic: Diagnostic) +} +``` + +The `createUniqueLocalName()` function allows one to create new, unique names so that the macro expansion can produce new declarations that won't conflict with any other declarations in the same scope. It produces an identifier token containing the unique name. This allows macros to be more hygienic, by not introducing new names that could affect the way that the code provided via macro expansion arguments is type-checked. + +It is intended that `MacroExpansionContext` will grow over time to include more information about the build environment in which the macro is being expanded. For example, information about the target platform (such as OS, architecture, and deployment version) and any compile-time definitions passed via `-D`, should be included as part of the context. + +The `diagnose` method allows a macro implementation to provide diagnostics that as part of macro expansion. The [`Diagnostic`](https://github.com/apple/swift-syntax/blob/main/Sources/SwiftDiagnostics/Diagnostic.swift) type used in the parameter is part of the swift-syntax library, and its form is likely to change over time, but it is able to express the different kinds of diagnostics a compiler or other tool might produce, such as warnings and errors, along with range highlights, Fix-Its, and attached notes to provide more clarity. A macro definition can introduce diagnostics if, for example, the macro argument successfully type-checked but used some Swift syntax that the macro implementation does not understand. The diagnostics will be presented by whatever tool is expanding the macro, such as the compiler. A macro that emits diagnostics is still expected to produce an expansion result unless it also throws an error, in which case both emitted diagnostics and the error will be reported. + +### Macros in the Standard Library + +#### `externalMacro` definition + +The builtin `externalMacro` macro is declared as follows: + +```swift +macro externalMacro(module: String, type: String) -> T +``` + +The arguments identify the module name and type name of the type that provides an external macro definition. Note that the `externalMacro` macro is special in that it can only be expanded to define another macro. It is an error to use it anywhere else, which is why it does not include an `@expression` attribute. + +#### Builtin macro declarations + +As previously noted, expression macros use the same leading `#` syntax as number of built-in expressions like `#line`. With the introduction of expression macros, we propose to subsume those built-in expressions into macros that come as part of the Swift standard library. The actual macro implementations are provided by the compiler, and may even involve things that aren't necessarily implementable with the pure syntactic macro. However, by providing macro declarations we remove special cases from the language and benefit from all of the tooling affordances provided for macros. + +We propose to introduce a number of macro declarations into the Swift standard library. There are several different kinds of such macros. + +##### Source-location macros + +```swift +// File and path-related information +@expression macro fileID: T +@expression macro file: T +@expression macro filePath: T + +// Current function +@expression macro function: T + +// Source-location information +@expression macro line: T +@expression macro column: T + +// Current shared object handle. +@expression macro dsohandle: UnsafeRawPointer +``` + +With the exception of `#fileID` (and `#file` when [SE-0274](https://github.com/apple/swift-evolution/blob/main/proposals/0274-magic-file.md) is enabled), the operations that provide information about the current location in source code are not implementable as `ExpressionMacro`-conforming types, because specific source-location information is intentionally unavailable to macro definitions. The type signatures of these macros capture most of the type system behavior of the existing `#file`, `#line`, etc., because they are treated like literals and therefore can pick up any contextual type that implements the proper `ExpressiblyBy*` protocol. However, the implementations above would fail to type-check code like this: + +```swift +let x = #file +``` + +with an error such as + +``` +error: generic parameter 'T' could not be inferred +``` + +To match the existing behavior of the built-in `#file`, `#line`, etc. would require a defaulting rule that matches what we get for literal types. At present, this requires special handling in the compiler, but a future extension to the language to enable default generic arguments would likely allow us to express this notion directly in the type system. + +##### Objective-C helper macros + +The Swift `#selector` and `#keyPath` expressions can have their syntax and type-checking behavior expressed in terms of macro declarations: + +```swift +@expression macro selector(_ method: T) -> Selector +@expression macro selector(getter property: T) -> Selector +@expression macro selector(setter property: T) -> Selector +@expression macro keyPath(property: T) -> String +``` + +These macros cannot be implemented in terms of `ExpressionMacro` based on the facilities in this proposal, because one would need to determine which declarations are referenced within the argument of a macro expansion such as `#selector(getter: Person.name)`. However, providing them with macro declarations that have built-in implementations makes them less special, removing some special cases from more of the language. + +##### Object literals + +```swift +@expression macro colorLiteral(red: Float, green: Float, blue: Float, alpha: Float) -> T +@expression macro imageLiteral(resourceName: String) -> T +@expression macro fileLiteral(resourceName: String) -> T +``` + +The object literals allow one to reference a resource in a program of various kinds. The three kinds of object literals (color, image, and file) can be described as expression macros. The type signatures provided above are not exactly how type checking currently works for object literals, because they aren't necessarily generic. Rather, when they are used, the compiler currently looks for a specially-named type (e.g., `_ColorLiteralType`) in the current module and uses that as the type of the corresponding color literal. To maintain that behavior, we propose to type-check macro expansions for object literals by performing the same lookup that is done today (e.g., for `_ColorLiteralType`) and then using that type as the generic argument for the corresponding macro. That way, the type checking behavior is unchanged when moving from special object literal expressions in the language to macro declarations with built-in implementations. + +### Sandboxing macro implementations + +The details of how macro implementation modules are built and provided to the compiler will be left to a separate proposal. However, it's important to call out here that macro implementations will be executed in a sandbox [like SwiftPM plugins](https://github.com/apple/swift-evolution/blob/main/proposals/0303-swiftpm-extensible-build-tools.md#security), preventing file system and network access. This is both a security precaution and a practical way of encouraging macros to not depend on any state other than the specific macro expansion node they are given to expand and its child nodes (but not its parent nodes), and the information specifically provided by the macro expansion context. If in the future macros need access to other information, this will be accomplished by extending the macro expansion context, which also provides a mechanism for the compiler to track what information the macro actually queried. + +## Tools for using and developing macros + +One of the primary concerns with macros is their ease of use and development: how do we know what a macro does to a program? How does one develop and debug a new macro? + +With the right tool support, the syntactic model of macro expansion makes it easy to answer the first question. The tools will need to be able to show the developer what the expansion of any use of a macro is. At a minimum, this should include flags that can be passed to the compiler to expand macros (the prototype provides `-Xfrontend -dump-macro-expansions` for this), and possibly include a mode to write out a "macro-expanded" source file akin to how C compilers can emit a preprocessed source file. Other tools such as IDEs should be able to show the expansion of a given use of a macro so that developers can inspect what a macro is doing. Because the result is always Swift source code, one can reason about it more easily than (say) inspecting the implementation of a macro that manipules an AST or IR. + +The fact that macro implementations are separate programs actually makes it easier to develop macros. One can write unit tests for a macro implementation that provides the input source code for the macro (say, `#stringify(x + y)`), expands that macro using facilities from swift-syntax, and verifies that the resulting code is free of syntax errors and matches the expected result. Most of the "builtin" macro examples were developed this way in the [syntax macro test file](https://github.com/apple/swift-syntax/blob/main/Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift). + +## Example expression macros + +There are many uses for expression macros beyond what has presented here. This section will collect several examples of macro implementations based on existing built-in `#` expressions as well as ones that come from the Swift forums and other sources of inspiration. Prototype implementations of a number of these macros are [available in the swift-syntax repository](https://github.com/apple/swift-syntax/blob/main/Sources/_SwiftSyntaxMacros/MacroSystem%2BBuiltin.swift) + +* The `#colorLiteral` macro provides syntax for declaring a color with a given red, green, blue, and alpha values. It can be declared and implemented as follows + + ```swift + // Declaration of #colorLiteral + @expression macro colorLiteral(red: Float, green: Float, blue: Float, alpha: Float) -> _ColorLiteralType + = SwiftBuiltinMacros.ColorLiteralMacro + + // Implementation of #colorLiteral + struct ColorLiteralMacro: ExpressionMacro { + /// Replace the label of the first element in the tuple with the given + /// new label. + func replaceFirstLabel( + of tuple: TupleExprElementListSyntax, with newLabel: String + ) -> TupleExprElementListSyntax{ + guard let firstElement = tuple.first else { + return tuple + } + + return tuple.replacing( + childAt: 0, with: firstElement.withLabel(.identifier(newLabel))) + } + + static func expansion( + of node: MacroExpansionExprSyntax, in context: MacroExpansionContext + ) -> MacroResult { + let argList = replaceFirstLabel( + of: node.argumentList, with: "_colorLiteralRed" + ) + let initSyntax: ExprSyntax = ".init(\(argList))" + if let leadingTrivia = node.leadingTrivia { + return MacroResult(initSyntax.withLeadingTrivia(leadingTrivia)) + } + return initSyntax + } + } + ``` + + The same approach can be used for file and image literals. + +* [Power assertions](https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900/87) by Kishikawa Katsumi: this assertion macro captures intermediate values within the assertion expression so that when the assertion fails, those values are displayed. The results are exciting! + + ```swift + #powerAssert(mike.isTeenager && john.age < mike.age) + | | | | | | | | + | true | | 42 | | 13 + | | | | Person(name: "Mike", age: 13) + | | | false + | | Person(name: "John", age: 42) + | false + Person(name: "Mike", age: 13) + ``` + +## Source compatibility + +Macros are a pure extension to the language, utilizing new syntax, so they don't have an impact on source compatibility. + +## Effect on ABI stability + +Macros are a source-to-source transformation tool that have no ABI impact. + +## Effect on API resilience + +Macros are a source-to-source transformation tool that have no effect on API resilience. + +## Alternatives considered + +### Asynchronous macro expansion + +The `expansion(of:in:)` operation for an expression macro could be marked as `async`, e.g., + +```swift + static func expansion( + of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext + ) async throws -> ExprSyntax +``` + +to allow it to perform asynchronous computations as part of macro expansion. This could be useful if certain macros were provided with access to asynchronous I/O or required some kind of non-blocking communication with a separate compiler process. + +## Future Directions + +There are a number of potential directions one could take macros, both by providing additional information to the macro implementations themselves and expanding the scope of macros. + +### Macro argument type information + +The arguments to a macro are fully type-checked before the macro implementation is invoked. However, information produced while performing that type-check is not provided to the macro, which only gets the original source code. In some cases, it would be useful to also have information determined during type checking, such as the types of the arguments and their subexpressions, the full names of the declarations referenced within those expressions, and any implicit conversions performed as part of type checking. For example, consider a use of a macro like the [power assertions](https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900/87) mentioned earlier: + +```swift +#assert(Color(parsing: "red") == .red) +``` + +The implementation would likely want to separate the two operands to `==` into local variables (with fresh names generated by `createUniqueLocalName`) to capture the values, so they can be printed later. For example, the assertion could be translated into code like the following: + +```swift +{ + let _unique1 = Color(parsing: "red") + let _unique2 = .red + if !(_unique1 == _unique2) { + fatalError("assertion failed: \(_unique1) != \(_unique2)") + } +}() +``` + +Note, however, that this code will not type check, because initializer for `_unique2` requires context information to determine how to resolve `.red`. If the macro implementation were provided with the types of the two subexpressions, `Color(parsing: "red")` and `.red`, it could have been translated into a something that will type-check properly: + +```swift +{ + let _unique1: Color = Color(parsing: "red") + let _unique2: Color = .red + if !(_unique1 == _unique2) { + fatalError("assertion failed: \(_unique1) != \(_unique2)") + } +}() +``` + +The macro expansion context could be extended with an operation to produce the type of a given syntax node, e.g., + +```swift +extension MacroExpansionContext { + func type(of node: ExprSyntax) -> Type? +} +``` + +When given one of the expression syntax nodes that is part of the macro expansion expression, this operation would produce a representation of the type of that expression. The `Type` would need to be able to represent the breadth of the Swift type system, including structural types like tuple and function types, and nominal types like struct, enum, actor, and protocol names. + +Additional information could be provided about the actual resolved declarations. For example, the syntax node for `.red` could be queried to produce a full declaration name `Color.red`, and the syntax node for `==` could resolve to the full name of the declaration of the `==` operator that compares two `Color` values. A macro could then distinguish between different `==` operator implementations. + +The main complexity of this future direction is in defining the APIs to be used by macro implementations to describe the Swift type system and related information. It would likely be a simplified form of a type checker's internal representation of types, but would need to maintain stable. Therefore, while we feel that the addition of type information is a highly valuable extension for expression macros, the scope of the addition means it would best be introduced as a follow-on proposal. + +### Additional kinds of macros + +Expressions are just one place in the language where macros could be valuable. Other places could include function or closure bodies (e.g., to add tracing or logging), within type or extension definitions (e.g., to add new member), or on protocol conformances (e.g., to synthesize a protocol conformance). A number of potential ideas are presented in the [vision for macros in Swift](https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900). For each of them, we assume that the basic `macro` declaration will stay roughly the same, but the contexts in which the macro can be used would be different, as might the spelling of the expansion (e.g., `@` might be more appropriate if the macro expansion occurs on a declaration), there would be an attribute on the `macro` declaration that indicates what type of macro it is, and there would be a corresponding protocol that inherits from `Macro` in the `SwiftSyntaxMacros` module. + +## Revision History + +* Revisions from the second pitch: + * Moved SwiftPM manifest changes to a separate proposal that can explore the building of macros in depth. This proposal will focus only on the language aspects. + * Simplified the type signature of the `#externalMacro` built-in macro. + * Added `@expression` to the macro to distinguish it from other kinds of macros that could come in the future. + * Make `expansion(of:in:)` throwing, and have that error be reported back to the user. + * Expand on how the various builtin standard library macros will work. + +* Revisions from the first pitch: + * Rename `MacroEvaluationContext` to `MacroExpansionContext`. + * Remove `MacroResult` and instead allow macros to emit diagnostics via the macro expansion context. + * Remove `sourceLocationConverter` from the macro expansion context; it provides access to the whole source file, which interferes with incremental builds. + * Rename `ExpressionMacro.apply` to `expansion(of:in)` to make it clear that it's producing the expansion of a syntax node within a given context. + * Remove the implementations of `#column`, as well as the implication that things like `#line` can be implemented with macros. Based on the above changes, they cannot. + * Introduce a new section providing declarations of macros for the various `#` expressions that exist in the language, but will be replaced with (built-in) macros. + * Replace the `external-macro-name` production for defining macros with the more-general `macro-expansion-expression`, and a builtin macro `externalMacro` that makes it far more explicit that we're dealing with external types that are looked up by name. This also provides additional capabilities for defining macros in terms of other macros. + * Add much more detail about how macro expansion works in practice. + * Introduce SwiftPM manifest extensions to define macro plugins. + * Added some future directions and alternatives considered. + + +## Acknowledgments + +Richard Wei implemented the compiler plugin mechanism on which the prototype implementation depends, as well as helping identify and explore additional use cases for macros. John McCall and Becca Royal-Gordon provided numerous insights into the design and practical implementation of macros. Tony Allevato provided additional feedback on building and sandboxing. From 781943b23e1d15ef3110d5934043b76a97c6c73c Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Fri, 16 Dec 2022 14:06:59 -0500 Subject: [PATCH 2842/4563] Update 0382-expression-macros.md (#1883) --- proposals/0382-expression-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index 3f94e9aaee..06ff99571e 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -5,7 +5,7 @@ * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Active review (December 16...30, 2022)** * Implementation: Partial implementation is available in `main` under the experimental feature flag `Macros`. An [example macro repository](https://github.com/DougGregor/swift-macro-examples) provides a way to experiment with this feature. -* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861/16)) +* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861/16)) ([review](https://forums.swift.org/t/se-0382-expression-macros/62090)) ## Introduction From c2392a811dbbe0007b1de0dc6a49126ecb55d722 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Fri, 16 Dec 2022 15:34:06 -0800 Subject: [PATCH 2843/4563] add 'Using Swift from C++' vision document --- visions/using-swift-from-c++.md | 139 ++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 visions/using-swift-from-c++.md diff --git a/visions/using-swift-from-c++.md b/visions/using-swift-from-c++.md new file mode 100644 index 0000000000..237dad82b5 --- /dev/null +++ b/visions/using-swift-from-c++.md @@ -0,0 +1,139 @@ +# Using Swift from C++ + +This vision document presents a high level overview of the "reverse" (i.e. Swift-to-C++) half of the C++ interoperability Swift language feature. It highlights the key principles and goals that determine how Swift APIs are exposed to C++ users. It also outlines the evolution process for this feature. This document does not present the final design for how Swift APIs get mapped to C++ language constructs. The final design of this feature will evolve as this feature goes through the Swift evolution process. This document does not cover the "forward" (i.e. using C++ APIs from Swift) aspect of the C++ interoperability, as it’s covered by a [sibling document](https://github.com/apple/swift/pull/60501/files). + +This document is an official feature vision document, as described in the [draft review management guidelines](https://github.com/rjmccall/swift-evolution/blob/057b2383102f34c3d0f5b257f82bba0f5b94683d/review_management.md#future-directions-and-roadmaps) of the Swift evolution process. The Language Workgroup has endorsed the goals and basic approach laid out in this document. This endorsement is not a pre-approval of any of the concrete proposals that may come out of this document. All proposals will undergo normal evolution review, which may result in rejection or revision from how they appear in this document. + +## Introduction + +Swift currently provides support for interoperability with C and Objective-C. Swift can import C and Objective-C APIs, and it can also expose its `@objc` types and functions to Objective-C. However, Swift currently does not provide support for interoperability with C++. Supporting bidirectional C++ interoperability in Swift would allow it to call into C++ APIs, and would make Swift APIs accessible to C++. Making Swift APIs accessible to C++ would simplify the process of adoption of Swift in existing C++ codebases, as it would allow them to incrementally adopt Swift. Furthermore, it would make Swift-only libraries available to C++ codebases. + +## Overview of Swift's interoperability support + +Swift uses two very different approaches for interoperability with C and Objective-C. For "forward" interoperability, Swift embeds a copy of the Clang compiler, which it uses to directly load C and Objective-C Clang modules and translate declarations into a native Swift representation. For "reverse" interoperability, Swift does not want to require Clang to embed Swift, and so Swift generates an Objective-C header that can be used to call into Swift APIs that are exposed in that header. Because Objective-C is restricted in what kinds of types it can work with, and because it requires code to be emitted in a special way that doesn't match the regular Swift ABI, methods and types must be marked as being exposed to Objective-C with the `@objc` attribute. + +Swift's support for C++ interoperability is modeled after its support for C and Objective-C interoperability. For "forward" interoperability, the embedded Clang compiler is used to directly load C++ Clang modules and translate declarations into a native Swift representation. The "forward" interoperability [vision document](https://github.com/apple/swift/pull/60501/files) provides more details about this model. For "reverse" interoperability, Swift generates a header that uses C++ language constructs to represent Swift APIs that are exposed by the Swift module. Because of the relatively high expressivity of C++ headers, the generated header is able to provide representation for most native Swift functions, methods, initializers, accessors and types without needing any extra code to be generated in the Swift module. This allows C++ programmers to call into Swift APIs using the familiar C++ function and member function call syntax. + +## C++ interoperability does not require Swift opt in + +The user model employed by Swift's C++ interoperability is different than the user model employed by Swift's Objective-C interoperability. All else being equal, it's best if supporting interoperation with a language doesn't require changing source code or emitting extra code eagerly from the compiler. This doesn't work for Objective-C interoperability because Objective-C requires specially-compatible classes and methods, the code for which cannot be emitted from an Objective-C header. Swift chose to require programs to opt in to Objective-C interoperability with the `@objc` attribute, both to make export more predictable and to avoid emitting extra code and metadata for all classes. In contrast, as long as the C++ compiler supports the Swift calling convention, a C++ header can call native Swift functions directly, and the C++ type system can be used to wrap most Swift types in a safe C++ representation. Because of this, there is no reason to require Swift module authors to opt in into C++ interoperability. Instead, any Swift module that can be imported into Swift can also be imported into C++, and most APIs will come across automatically. + +Some API authors will desire explicit control over the C++ API. Swift will provide an annotation such as the proposed [`@expose` attribute](https://forums.swift.org/t/formalizing-cdecl/40677/50) to allow precise control over which APIs get exposed to C++. The API authors will be able to specify that their module should only expose the annotated APIs to C++ when they desire to do so. + +## Goals + +The ultimate goal of allowing Swift APIs to be used from C++ is to remove a barrier to writing more code in Swift rather than C++. Some C++ programmers would like to adopt a memory-safe language like Swift in their codebase. These programmers would typically like to adopt Swift gradually instead of relying on rewriting all of their code in Swift at once. For instance, they might write a new component in Swift, which then needs to be made usable from the rest of their C++ codebase. Today this could be done via an Objective-C wrapper that relies on `@objc` annotations. However, maintaining `@objc` wrapper code is annoying and error-prone, and it often adds significant performance costs and expressivity restrictions because of the limitations of C and Objective-C. Allowing C++ to directly use Swift APIs with minimal restrictions or performance overhead would lower the burden of adding Swift to an existing C++ codebase. It also makes Swift an even better language for stable system libraries and APIs, as it allows C++ projects to consume most such libraries without restrictions. These libraries would also have the option of not providing an Objective-C interface for Objective-C clients, as these clients could use the Swift APIs from Objective-C++ instead. + +The primary goal of Swift-to-C++ interoperability is to expose every language feature of Swift for which a reasonable C++ API can be created to represent that feature. Swift and C++ are both feature rich programming languages. They share many similar features, but Swift has some features that lack close analogues in C++. For example, C++ does not have a concept of argument labels, but the argument labels can be integrated into the base name of a function to get a roughly similar effect. On the other hand, C++ does not have any feature that can represent a Swift feature like Swift macros in a reasonable manner. Therefore, macros must be dropped in the C++ interface to the module. + +It is a goal that Swift-to-C++ interoperability can be used by both mixed Swift/C++ language projects that want to interoperate within themselves, and by C++ projects that need to use a Swift library that wasn't developed with C++ interoperability in mind. + +It is a goal of Swift-to-C++ interoperability to expose Swift APIs in a safe, performant and ergonomic manner. The Swift compiler and the language specification should follow several key related principles that are presented below. + +### Safety + +Safety is a top priority for the Swift programming language. Swift code expects its callers to adhere to Swift’s type rules and Swift’s memory model, regardless of whether it’s called from Swift or C++. Thus, the C++ code that calls Swift should properly enforce Swift’s expected language contracts. The enforcement should be done automatically in the generated header. This kind of enforcement does not prevent all possible issues though, as it does not change C++'s safety model. C++ is unsafe by default, and the user is able to use regular C++ pointers to write to memory as if they were using Swift's `UnsafeMutablePointer` type. This means that bugs in C++ code can easily lead to violations of Swift's invariants. For instance, a bug in user’s C++ code could accidentally overwrite a Swift object stored on the heap, which could cause unexpected behavior (such as a segfault) in Swift code. The user is expected to obey Swift's memory model and type rules when calling into Swift from C++, and thus they bear the ultimate responsibility for avoiding bugs like this one. The user can use certain program analysis tools, such as Address Sanitizer, to help them catch bugs that violate Swift's memory model rules. + +Swift expects that values of correct types are passed to Swift APIs. For instance, for calls to generic functions, Swift expects the caller to pass a value of type that conforms to all of the required generic requirements. Type safety should be enforced from C++ as well. The C++ compiler should verify that correct types are passed to the Swift APIs as they’re invoked from C++. A program that tries to pass an incorrect Swift type into a Swift function should not compile. Select uses of specific Swift types might be incompatible with static type validation. For example, verification of generic requirements for a Swift opaque type value returned from a Swift function in C++ requires run-time validation. The program should report a fatal error when such run-time type validation fails, to ensure that the type invariants that Swift expects are not violated. The reported fatal error should clearly indicate why type validation failed and point to the location in the user's code which caused the run-time check. + +Memory safety is paramount for Swift code. Swift automatically manages the lifetime of value and reference types in Swift. These principles should translate to C++ as well. The lifetime of Swift values that are created or passed around in C++ should be managed automatically. For instance, the generated C++ code should increment the retain count of a Swift class instance when a Swift class type value is copied in C++, and it should decrement the retain count of a Swift class instance when a Swift class type value is destroyed in C++. The default convention for using Swift types in C++ should discourage dangling references and any other patterns that could lead to an invalid use of a Swift value. + +Swift’s memory safety model also requires exclusive access to a value in order to modify that value. For instance, the same value can not be passed to two `inout` parameters in the same function call. Swift enforces exclusivity using both compile-time and run-time checks. The generated run-time checks trap when an exclusivity violation is detected at runtime. Calls into Swift APIs from C++ should verify exclusivity for Swift values as well. + +### Performance + +Swift-to-C++ bridging should be as efficient as possible. The Swift compiler should avoid unnecessary overhead for calling into Swift functions from C++, or using Swift types from C++. Generally, the bridging code should not convert Swift types into their C++ counterparts automatically, as that can add a lot of overhead. For instance, a Swift function returning a `String` should still return a Swift `String` in C++, and not a `std::string`. Some specific "primitive" Swift types should be converted into their C++ counterparts automatically in order to improve their ergonomics in C++. The conversion for such primitive types should be zero-cost as their ABI should match in Swift and C++. For instance, a Swift function returning a `Float` can return a `float` in C++ as they use the same underlying primitive LLVM type to represent the floating point value. + +Some Swift features require additional overhead to be used in C++. Resilient value types are a good example of this; C++ expects types to have a statically-known layout, but Swift's resilient value types do not satisfy this, and so the generated C++ types representing those types may need to dynamically allocate memory internally. In cases like these, the C++ interface should at least strive to minimize the dynamic overhead, for example by avoiding allocations for sufficiently small types. + +### Achieving safety with performance in mind + +Certain aspects of Swift’s memory model impose certain restrictions that create tension between the goal of achieving safety and the goal of avoiding unnecessary overhead for calling into Swift from C++. Checking for exclusivity violations is a good example of this. The C++ compiler does not have a notion of exclusivity it can verify, so it is difficult to prove that a value is accessed exclusively in the C++ code that calls into Swift. This means that the C++ code that calls into Swift APIs will most likely require more run-time checks to validate exclusivity than similar Swift code that calls the same Swift APIs. + +The adherence to Swift’s type and memory safety rules should be prioritized ahead of performance when C++ calls into Swift, even if this means more run-time checks are required. For users seeking maximum performance, Swift provides additional compiler flags that avoid certain run-time checks. Those flags should be taken into account when the C++ header is generated. An example of such flag is `-enforce-exclusivity`. When `-enforce-exclusivity=none` is passed to Swift, the Swift compiler does not emit any run-time checks that check for exclusivity violations. A flag like this should also affect the generated C++ header, and the Swift compiler should not emit any run-time checks for exclusivity in the generated C++ header when this flag is used. + +Correct modeling of certain aspects of Swift type semantics in C++ requires additional overhead. This issue is most prominent when modeling Swift's `consume` operation, as it performs a destructive move, which C++ does not support. Thus, the `consume` operation has to be modeled using a non-destructive move in C++, which requires additional storage and runtime checks when a Swift value type is used in C++. In cases like this one, the design of how Swift language constructs are mapped to C++ should strive to be as ergonomic and as intuitive as possible, provided there's still a way to achieve appropriate performance using compiler optimizations or future extensions to the C++ language standard. + +### Ergonomics + +Swift APIs should be mapped over to C++ language features that have a direct correspondence to the Swift language feature. In cases where a direct correspondence does not exist, the Swift compiler should provide a reasonable approximation to the original Swift language feature using other C++ constructs. For example, Swift’s enum type can contain methods and nested types, and such constructs can’t be represented by a single C++ enum type in an idiomatic manner. Swift’s enum type can be mapped to a C++ class instead that allows both enum-like `switch` statement behavior and also enables the C++ user to invoke member functions on the Swift enum value and access its nested types from C++. + +The C++ representation of certain Swift types should be appropriately enhanced to allow them to be used in an idiomatic manner. For instance, it should be possible to use Swift’s `Array` type (or any type that conforms to `Collection`) in a ranged-based for loop in C++. Such enhancements should be done with safety in mind, to ensure that Swift’s memory model is not violated. + +There should be no differences on the C++ side between using libraries that opt-in into library evolution and libraries that don’t, except in specific required cases, like checking the unknown default case of a resilient enum. + +### Clear language mapping rules + +C++ is a very expressive language and it can provide representation for a lot of Swift language constructs. Not every Swift language construct will map to its direct C++ counterpart. For instance, Swift initializers might get bridged to static `init` member functions instead of constructors in C++, to allow C++ code to call failable initializers in a way that’s consistent with other initializer calls. Therefore, it’s important to provide documentation that describes how Swift language constructs get mapped to C++. It is a goal of C++ interoperability to provide a clear and well-defined mapping for how Swift language constructs are mapped to C++. Additionally, it is also a goal to clearly document which language constructs are not bridged to C++. In addition to documentation, compiler diagnostics should inform the user about types or functions that can’t be exposed to C++, when the user wants to expose them explicitly. It is a goal of C++ interoperability to add a set of clear diagnostics that let the user know when a certain Swift declaration is not exposed. It is not a goal to diagnose such cases when the user did not instruct the compiler to expose a declaration explicitly. For example, the Swift compiler might not diagnose when an exposed Swift type does not expose one of its public methods to C++ due to its return type not being exposed, if such method does not have an explicit annotation that instructs the compiler that this method must be exposed to C++. + +Some Swift APIs patterns will map to distinct C++ language constructs or patterns. For instance, an empty Swift enum with static members is commonly used in a namespace-like manner in Swift. This kind of enum can be mapped to a C++ namespace. It is a goal of C++ interoperability to provide a clear mapping for how Swift API patterns like this one are bridged to C++. + +The semantics of how Swift types behave should be preserved when they’re mapped to C++. For instance, in C++, there should still be a semantic difference between Swift value and reference types. Additionally, Swift’s copy-on-write data types like `Array` should still obey the copy-on-write semantics in C++. + +### Swift language evolution and API design should be unaffected + +Importing Swift APIs into C++ should not degrade the experience of programming in Swift. That means that Swift as a language should continue to evolve based on its [current goals](https://www.swift.org/about/). New additions to Swift should not restrict their expressivity or safety in order to provide a better experience for Swift APIs in C++. Such new features do not have to be exposed to C++ when it doesn't make sense to do so. + +Swift API authors should not change the way they write Swift code and design Swift APIs based on how specific Swift language constructs are exposed to C++. They should use the most effective Swift constructs for the task. It is a key goal of C++ interoperability that the exposed C++ interfaces are safe, performant, and ergonomic enough that programmers will not be tempted to make their Swift code worse just to make the C++ interfaces better. + +### Objective-C support + +The existing Swift to Objective-C bridging layer should still be supported even when C++ bindings are generated in the generated header. Furthermore, the generated C++ bindings should use appropriate Objective-C++ types or constructs in the generated C++ declarations where it makes sense to do so. For instance, a Swift function that returns an Objective-C class instance should be usable from an Objective-C++ compilation unit. + +## The approach + +The Swift compiler exposes Swift APIs to C++ by generating a header file that contains C++ declarations that wrap around the underlying calls into Swift functions that use the native Swift calling convention. The header also provides a suitable representation for Swift types. This generation can be done retroactively, starting from a Swift module interface file, and does not need to be requested when the Swift module is built from source. + +Currently the generated header file depends on several LLVM and Clang compiler features for the Swift calling convention support, and thus it can only be compiled by Clang. The header does not depend on any other Clang-specific C++ language extensions. The header can use some other optional Clang-only features that improve developer experience for the C++ users, like specific attributes that improve diagnostics. These Clang-only features are enabled only when the Clang that is being used supports them, so older versions of Clang would still be able to consume the generated header. + +The generated header file uses advanced C++ features that require a recent C++ language standard. C++20 is the recommended language standard to use for Swift APIs, however, the generated header should also be compatible with C++17 and C++14. + +The generated header file also contains the C and the Objective-C declarations that Swift exposes to C and Objective-C on platforms that support C or Objective-C interoperability. Thus a single header file can be used by codebases that mix C, Objective-C and C++. + +For the majority of Swift libraries, the generated header file is a build artifact generated by the client. This is different from Objective-C interoperability, where the generated header is always generated by the library owner and distributed with it. However, Swift library owners can also generate the header and distribute it with the library if they want the client to consume the C++ interface directly without having to run the Swift compiler on their end. + +On platforms with ABI stability, the generated C++ code for an ABI stable Swift interface is not tied to the Swift compiler that compiled the Swift code for that Swift module, as ABI stability is respected by the C++ code in the header. In all other cases the generated C++ code in the header is assumed to be tied to the Swift compiler that compiled the Swift module, and thus the header should be regenerated when the compiler changes. + + The next few sections provide a high level overview of how Swift types and some other language constructs get bridged to C++. The exact details of how Swift language constructs are bridged will be covered by Swift evolution proposals, and additional documentation, such as this preliminary [user guide document](https://github.com/apple/swift/blob/main/docs/CppInteroperability/UserGuide-CallingSwiftFromC%2B%2B.md). + +### Bridging Swift types + +The generated header contains C++ class types that represent the Swift struct, enum, and class types that are exposed by the Swift module. These types provide access to methods, properties and other members using either idiomatic C++ constructs or, in some cases, non-idiomatic C++ constructs that allow C++ to access more functionality in a consistent manner. The basic operations on these types follow the corresponding Swift semantics. For instance, a C++ value for a Swift class type is essentially a shared pointer that owns a reference to a Swift object, whereas a C++ value for a Swift struct type stores a Swift value of that type, and copying or destroying the C++ value copies or destroys the underlying Swift value. The C++ types also support C++ move semantics and translate them as appropriate to Swift's [`consume` operation](https://github.com/apple/swift-evolution/blob/main/proposals/0366-move-function.md). This enables high-performance use of Swift types from C++ and allows Swift's [non-copyable types](https://forums.swift.org/t/pitch-noncopyable-or-move-only-structs-and-enums/61903) to be exposed to C++. + +Protocol types also get exposed to C++. They provide access to their protocol interface to C++. The generated header also provides facilities to combine protocol types into a protocol composition type. The protocol composition type provides access to the combined protocol interface in C++. + +### Bridging generics + +Swift generic functions and types get bridged to C++ as C++ function and class templates. A generated C++ template instantiates a type-checked generic interface that uses the underlying polymorphic semantics that generics require when Swift APIs are called from the generated header. Type-checking is performed using the `requires` clause introduced in C++20. When C++17 and earlier is used, type-checking is performed using other legacy methods, like `enable_if` and `static_assert`. The two type-checking methods are compatible with the delayed template parsing compiler feature that Clang uses when building for Windows. + +To help achieve the performance goals outlined in the prior section, the generated class templates specialize the storage for the underlying Swift generic type when the Swift API that is exposed to C++ contains such a bounded Swift generic type. This ensures that non-resilient bounded generic values can be stored inline in a C++ type that represents the underlying Swift type, instead of being boxed on the heap. + +### Standard library support + +The Swift standard library contains a lot of useful functions and types that get bridged to C++. The generated standard library bindings enhance various Swift types like `Optional` and `Array` to provide idiomatic C++ APIs that allow the user to use such types in an idiomatic manner from C++. + +### Using Swift types in C++ templates + +The generated header is useful in mixed-language projects as C++ sources can include it, allowing C++ to call Swift APIs. However the C++ interoperability project also provides support for calling C++ APIs from Swift. In certain cases such C++ APIs contain function or class templates that need to be instantiated in Swift. Swift gives the user the ability to use Swift types in such cases, so the C++ templates have to be instantiated with Swift types. This means that the Swift types need to be translated into their C++ counterparts, which could then be used inside the instantiated C++ template specialization. This Swift-to-C++ type translation is performed using the same mechanism that’s used by the Swift compiler to generate the header with C++ interface for a Swift module. This means that a C++ template instantiated with a Swift type will see the same C++ representation of that Swift type regardless of whether it was instantiated from C++, or from Swift. + +## Evolution process + +The approach and the goals for how Swift APIs get bridged to C++ are outlined above. Each distinct Swift language construct that’s bridged to C++ will need to be covered by a detailed evolution proposal. These evolution proposals can refer to this vision document as a general context document for how Swift APIs should be bridged to C++. Every Swift API pattern that has a distinct mapping in C++ will also need a detailed and self-contained evolution proposal as well. The design for how each district language feature or API pattern is bridged to C++ is ratified only once its respective proposal goes through the Swift evolution process and is accepted by the Swift community. + +## The Swift ecosystem + +As a supported language feature, C++ and Swift interoperability must work well on every platform supported by Swift. In similar vein, tools in the Swift ecosystem should be updated to support C++ interoperability. The next few sections of this document provide specific recommendations for how various tools in the Swift ecosystem should be adapted to support C++ interoperability. The C++ interoperability workgroup intends to work on supporting most of these recommendations in the tools that are bundled with the Swift toolchain while working on the initial version of interoperability that could be adopted for production use cases. + +### Build tool support + +The Swift package manager (SwiftPM) is one of the most commonly used ways to build Swift code. Swift package manager can also build C and C++ code. SwiftPM should provide good support for bridging Swift APIs to C++ out of the box. It should generate a compatibility header for a Swift package when it determines that the C++ code in the same or dependent package includes such a header, and it should ensure that this header can be found when the C++ code is compiled. SwiftPM already has some support for generating a header file with Objective-C interface for a Swift module, and the C++ interoperability workgroup intends to reuse that support for supporting the generation of C++ bindings as well. + +CMake is a widely used tool used for configuring and building C++ code. CMake should provide good support for adding Swift code to C++ CMake targets. Swift’s ecosystem as a whole should ensure that it should be as straightforward as possible to add support for bridging Swift APIs to C++ within the same CMake project. The C++ interoperability workgroup should provide an example CMake project that shows how Swift and C++ can interoperate between each other, once the appropriate support for mixed-language Swift and C++ targets lands in CMake. + +### Debugging support + +Debugging support is critical for great user experience. LLDB should understand that C++ types in the generated header are just wrappers around Swift values. It should be able to display the underlying Swift value in the debugger when the user tries to inspect the C++ value that stores the Swift value in the debugger. In addition to that, the generated compatibility header should be correctly annotated to ensure that C++ inline thunks that call Swift APIs can be skipped when a user steps in or steps out into or from a call when debugging a program. + +### IDE support + +SourceKit-LSP is a language server that provides cross-platform IDE support for Swift code in the Swift ecosystem. It can also act as a language server for C, Objective-C and C++ as it can wrap around and redirect queries to clangd. This in turn allows SourceKit-LSP to provide support for mixed-language IDE queries, like jump-to-definition, that allows the IDE client to jump from an Objective-C method to call to its underlying Swift implementation. The C++ interoperability workgroup intends to reuse the current support for mixed-language Swift and Objective-C queries to add similar functionality for mixed-language Swift and C++ queries. This would allow IDE clients that use SourceKit-LSP to use features that can operate across the Swift/C++ language boundary. For instance, a client IDE would be able to support jump-to-definition from a Swift call expression to the called C++ function using SourceKit-LSP. This mixed-language query support in SourceKit-LSP is powered by the indexing data emitted by Clang. The C++ interoperability workgroup intends to extend Clang's indexing support to represent references to the wrapper C++ declarations from the generated header as references to the underlying Swift declarations. From 732835e9561c5adf9303ebcbf516aba898b6619e Mon Sep 17 00:00:00 2001 From: Alex Brown Date: Sat, 17 Dec 2022 09:32:44 -0800 Subject: [PATCH 2844/4563] Update 0302-concurrent-value-and-concurrent-closures.md Make it clearer that it's not necessary for both the rules to be true for a closure to be inferred as sendable --- proposals/0302-concurrent-value-and-concurrent-closures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0302-concurrent-value-and-concurrent-closures.md b/proposals/0302-concurrent-value-and-concurrent-closures.md index 946ebd7d7e..54c42f9ad1 100644 --- a/proposals/0302-concurrent-value-and-concurrent-closures.md +++ b/proposals/0302-concurrent-value-and-concurrent-closures.md @@ -398,9 +398,9 @@ The combination of `@Sendable` closures and `Sendable` types allows type safe co #### Inference of `@Sendable` for Closure Expressions -The inference rule for `@Sendable` attribute for closure expressions is similar to closure `@escaping` inference. A closure expression is inferred to be `@Sendable` if: +The inference rule for `@Sendable` attribute for closure expressions is similar to closure `@escaping` inference. A closure expression is inferred to be `@Sendable` if either: -* It is used in a context that expects a `@Sendable` function type (e.g. `parallelMap` or `Task.runDetached`). +* It is used in a context that expects a `@Sendable` function type (e.g. `parallelMap` or `Task.runDetached`). Or * When `@Sendable` is in the closure “in” specification. The difference from `@escaping` is that a context-less closure defaults to be non-`@Sendable`, but defaults to being `@escaping`: From dbeed03b65429e59721b6434022e72fa9a642746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EC=9E=AC=ED=98=B8?= Date: Sun, 18 Dec 2022 04:25:05 +0900 Subject: [PATCH 2845/4563] [SE-0269] Fix some typos (#1886) --- proposals/0269-implicit-self-explicit-capture.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0269-implicit-self-explicit-capture.md b/proposals/0269-implicit-self-explicit-capture.md index 56e6f7272f..272a4386ab 100644 --- a/proposals/0269-implicit-self-explicit-capture.md +++ b/proposals/0269-implicit-self-explicit-capture.md @@ -65,7 +65,7 @@ It also results in a lot of unnecessary repetition. The motivation for requiring In codebases that make heavy use of asynchronous code, such as clients of [PromiseKit](https://github.com/mxcl/PromiseKit), or even Apple's new [Combine](https://developer.apple.com/documentation/combine) and [SwiftUI](https://developer.apple.com/documentation/swiftui) libraries, maintainers must either choose to adopt style guides which require the use of explicit `self` in all cases, or else resign to the reality that explicit usage of `self` will appear inconsistently throughout the code. Given that Apple's preferred/recommended style is to omit `self` where possible (as evidenced by examples throughout [The Swift Programming Language](https://docs.swift.org/swift-book/) and the [SwiftUI Tutorials](https://developer.apple.com/tutorials/swiftui)), having any asynchronous code littered with `self.` is a suboptimal state of affairs. -The error is also overly conservative—it will prevent the usage of implicit `self` even when it is very unlikely that the capture of `self` will somehow cause a reference cycle. With function builders in SwiftUI, the automatic resolution to the "make capture semantics explicit" error (and indeed, the fix-it that is currently supplied to the programmer) is just to slap a `self.` on wherever the compiler tells you to. While this likely fine when `self` is a value type, building this habit could cause users to ignore the error and apply the fix-it in cases where it is not in fact appropriate. +The error is also overly conservative—it will prevent the usage of implicit `self` even when it is very unlikely that the capture of `self` will somehow cause a reference cycle. With function builders in SwiftUI, the automatic resolution to the "make capture semantics explicit" error (and indeed, the fix-it that is currently supplied to the programmer) is just to slap a `self.` on wherever the compiler tells you to. While this is likely fine when `self` is a value type, building this habit could cause users to ignore the error and apply the fix-it in cases where it is not in fact appropriate. There are also cases that are just as (if not more) likely to cause reference cycles that the tool currently misses. Once such instance is discussed in **Future Directions** below. These false negatives are not addressed in this proposal, but improving the precision of this diagnostic will make the tool more powerful and less likely to be dismissed if (or when) new diagnostic cases are introduced. @@ -126,7 +126,7 @@ The existing errors and fix-its have their language updated accordingly to indic Error: call to method in closure requires explicit use of 'self' to make capture semantics explicit. ``` -The new fix-it for explicitly capturing `self` will take slightly different forms depending on whether there is a capture list already present ("insert '`self, `'"), whether there are explicit parameters ("insert '`[self]`'"), and whether the user the user has already captured a variable with the name `self` (in which case the fix-it would not be offered). Since empty capture lists are allowed (`{ [] in ... }`), the fix-it will cover this case too. +The new fix-it for explicitly capturing `self` will take slightly different forms depending on whether there is a capture list already present ("insert '`self, `'"), whether there are explicit parameters ("insert '`[self]`'"), and whether the user has already captured a variable with the name `self` (in which case the fix-it would not be offered). Since empty capture lists are allowed (`{ [] in ... }`), the fix-it will cover this case too. This new rule would only apply when `self` is captured directly, and with the name `self`. This includes captures of the form `[self = self]` but would still not permit implicit `self` if the capture were `[y = self]`. In the unusual event that the user has captured another variable with the name `self` (e.g. `[self = "hello"]`), we will offer a note that this does not enable use of implicit `self` (in addition to the existing error attached to the attempted use of implicit `self`): @@ -140,7 +140,7 @@ If the user has a capture of `weak self` already, we offer a special diagnostic Note: weak capture of 'self' here does not enable implicit 'self'. ``` -If either of the two above notes are present, we will not offer the usual fix-its for resolving this error, since the code inserted would be erroneous. +If either of the two above notes is present, we will not offer the usual fix-its for resolving this error, since the code inserted would be erroneous. ## Source compatibility From bfebcd34a56fd91200604b3e15faef7e416b1f49 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 19 Dec 2022 19:22:26 -0500 Subject: [PATCH 2846/4563] editorial --- proposals/0302-concurrent-value-and-concurrent-closures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0302-concurrent-value-and-concurrent-closures.md b/proposals/0302-concurrent-value-and-concurrent-closures.md index 54c42f9ad1..17cd2009ac 100644 --- a/proposals/0302-concurrent-value-and-concurrent-closures.md +++ b/proposals/0302-concurrent-value-and-concurrent-closures.md @@ -400,8 +400,8 @@ The combination of `@Sendable` closures and `Sendable` types allows type safe co The inference rule for `@Sendable` attribute for closure expressions is similar to closure `@escaping` inference. A closure expression is inferred to be `@Sendable` if either: -* It is used in a context that expects a `@Sendable` function type (e.g. `parallelMap` or `Task.runDetached`). Or -* When `@Sendable` is in the closure “in” specification. +* it is used in a context that expects a `@Sendable` function type (e.g. `parallelMap` or `Task.runDetached`) or +* `@Sendable` is in the closure's `in` specification. The difference from `@escaping` is that a context-less closure defaults to be non-`@Sendable`, but defaults to being `@escaping`: From b728857a0f9eb1e77e66dc324a006cd67ef12863 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 20 Dec 2022 05:13:10 +0000 Subject: [PATCH 2847/4563] Update CONTRIBUTING.md Commit suggestion by John McCall. Co-authored-by: John McCall --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 101ef4ab39..7ba7b4b3ca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,4 +6,4 @@ Before you initiate a pull request, please read: * [Commonly Rejected Changes](commonly_proposed.md) * [Code of Conduct](https://www.swift.org/code-of-conduct/) -Ideas should be thoroughly discussed on the [forums](https://forums.swift.org/c/evolution/18) first. +Pull requests that make minor editorial and administrative changes to this repository are always welcome, including fixing typos and grammar mistakes, repairing links, and maintaining document and repository metadata. Pull requests that add new proposals must follow the Swift evolution process, including the proposal having first been pitched on the [evolution forums](https://forums.swift.org/c/evolution/pitches/5). Pull requests that make substantive changes to existing proposals should only be made with the permission of the proposal authors; furthermore, if the proposal review is complete, such changes are discouraged and require the approval of the appropriate evolution workgroup. Pull requests that add or change vision documents require the approval of the appropriate evolution workgroup. Pull requests that change the documented evolution process require the approval of the Core Team. From 7c75e4f9df1a404b91b99a67571ecf9afa417e15 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 20 Dec 2022 05:26:14 +0000 Subject: [PATCH 2848/4563] Update CONTRIBUTING.md Use separate paragraphs. Co-authored-by: John McCall --- CONTRIBUTING.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ba7b4b3ca..607d9bb74b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,4 +6,12 @@ Before you initiate a pull request, please read: * [Commonly Rejected Changes](commonly_proposed.md) * [Code of Conduct](https://www.swift.org/code-of-conduct/) -Pull requests that make minor editorial and administrative changes to this repository are always welcome, including fixing typos and grammar mistakes, repairing links, and maintaining document and repository metadata. Pull requests that add new proposals must follow the Swift evolution process, including the proposal having first been pitched on the [evolution forums](https://forums.swift.org/c/evolution/pitches/5). Pull requests that make substantive changes to existing proposals should only be made with the permission of the proposal authors; furthermore, if the proposal review is complete, such changes are discouraged and require the approval of the appropriate evolution workgroup. Pull requests that add or change vision documents require the approval of the appropriate evolution workgroup. Pull requests that change the documented evolution process require the approval of the Core Team. +Pull requests that make minor editorial and administrative changes to this repository are always welcome, including fixing typos and grammar mistakes, repairing links, and maintaining document and repository metadata. + +Pull requests that add new proposals must follow the Swift evolution process, including the proposal having first been pitched on the [evolution forums](https://forums.swift.org/c/evolution/pitches/5). + +Pull requests that make substantive changes to existing proposals should only be made with the permission of the proposal authors; furthermore, if the proposal review is complete, such changes are discouraged and require the approval of the appropriate evolution workgroup. + +Pull requests that add or change vision documents require the approval of the appropriate evolution workgroup. + +Pull requests that change the documented evolution process require the approval of the Core Team. From 702ae36d5fc4ff89351003f3eba7bd2c41c8e383 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 20 Dec 2022 00:51:11 -0500 Subject: [PATCH 2849/4563] One last wording change --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 607d9bb74b..887161d9fc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ Pull requests that make minor editorial and administrative changes to this repos Pull requests that add new proposals must follow the Swift evolution process, including the proposal having first been pitched on the [evolution forums](https://forums.swift.org/c/evolution/pitches/5). -Pull requests that make substantive changes to existing proposals should only be made with the permission of the proposal authors; furthermore, if the proposal review is complete, such changes are discouraged and require the approval of the appropriate evolution workgroup. +Pull requests that make substantive changes to existing proposals should only be made with the permission of the proposal authors. Substantive changes to a proposal in the Accepted or Rejected are discouraged and require the approval of the appropriate evolution workgroup. Substanative changes to a proposal in the Active Review state require the approval of the appropriate evolution workgroup and should be advertised in the review thread. Pull requests that add or change vision documents require the approval of the appropriate evolution workgroup. From 16aae42f3865da3c6dc599fa0bb2c008e5e6c073 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 20 Dec 2022 00:53:38 -0500 Subject: [PATCH 2850/4563] another last change --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 887161d9fc..373b8fa06a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,8 +10,8 @@ Pull requests that make minor editorial and administrative changes to this repos Pull requests that add new proposals must follow the Swift evolution process, including the proposal having first been pitched on the [evolution forums](https://forums.swift.org/c/evolution/pitches/5). -Pull requests that make substantive changes to existing proposals should only be made with the permission of the proposal authors. Substantive changes to a proposal in the Accepted or Rejected are discouraged and require the approval of the appropriate evolution workgroup. Substanative changes to a proposal in the Active Review state require the approval of the appropriate evolution workgroup and should be advertised in the review thread. +Pull requests that make substantive changes to existing proposals should only be made with the permission of the proposal authors. Substantive changes to a proposal in the Accepted or Rejected are discouraged and require the approval of the appropriate evolution workgroup. Substantive changes to a proposal in the Active Review state require the approval of the appropriate evolution workgroup and should be advertised in the review thread. -Pull requests that add or change vision documents require the approval of the appropriate evolution workgroup. +Pull requests that add or substantively change vision documents require the approval of the appropriate evolution workgroup. -Pull requests that change the documented evolution process require the approval of the Core Team. +Pull requests that substantively change the documented evolution process require the approval of the Core Team. From d512e114990b587347e604aac878e16b413ab6ac Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 20 Dec 2022 06:32:24 +0000 Subject: [PATCH 2851/4563] Update CONTRIBUTING.md * Remove the list of items. * Add a missing word. --- CONTRIBUTING.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 373b8fa06a..027d56ae1d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,16 +1,12 @@ # Contributing to Swift Evolution -Before you initiate a pull request, please read: - -* [Swift Evolution Process](process.md) -* [Commonly Rejected Changes](commonly_proposed.md) -* [Code of Conduct](https://www.swift.org/code-of-conduct/) + Pull requests that make minor editorial and administrative changes to this repository are always welcome, including fixing typos and grammar mistakes, repairing links, and maintaining document and repository metadata. -Pull requests that add new proposals must follow the Swift evolution process, including the proposal having first been pitched on the [evolution forums](https://forums.swift.org/c/evolution/pitches/5). +Pull requests that add new proposals must follow the [Swift evolution process](process.md), including the proposal having first been pitched on the [evolution forums](https://forums.swift.org/c/evolution/pitches/5). -Pull requests that make substantive changes to existing proposals should only be made with the permission of the proposal authors. Substantive changes to a proposal in the Accepted or Rejected are discouraged and require the approval of the appropriate evolution workgroup. Substantive changes to a proposal in the Active Review state require the approval of the appropriate evolution workgroup and should be advertised in the review thread. +Pull requests that make substantive changes to existing proposals should only be made with the permission of the proposal authors. Substantive changes to a proposal in the Accepted or Rejected states are discouraged and require the approval of the appropriate evolution workgroup. Substantive changes to a proposal in the Active Review state require the approval of the appropriate evolution workgroup and should be advertised in the review thread. Pull requests that add or substantively change vision documents require the approval of the appropriate evolution workgroup. From 8852b0b6603671a6fc87315ae307f7df7633c1ff Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 21 Dec 2022 18:33:50 -0500 Subject: [PATCH 2852/4563] Extend the SE-0380 review to December 30th. (#1890) --- proposals/0380-if-switch-expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0380-if-switch-expressions.md b/proposals/0380-if-switch-expressions.md index d3487e2b07..b5e8656495 100644 --- a/proposals/0380-if-switch-expressions.md +++ b/proposals/0380-if-switch-expressions.md @@ -3,7 +3,7 @@ * Proposal: [SE-0380](0380-if-switch-expressions.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Hamish Knight](https://github.com/hamishknight) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (December 7...December 21, 2022)** +* Status: **Active Review (December 7...December 30, 2022)** * Implementation: [apple/swift#612178](https://github.com/apple/swift/pull/62178), including a downloadable toolchain. * Review: ([pitch](https://forums.swift.org/t/pitch-if-and-switch-expressions/61149)), ([review](https://forums.swift.org/t/se-0380-if-and-switch-expressions/61899)) From cb72e219f374a8405f3170f2d406cc3071b98eb6 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 21 Dec 2022 20:08:05 +0900 Subject: [PATCH 2853/4563] update discarding groups --- proposals/0381-task-group-discard-results.md | 152 ++++++++++++++++--- 1 file changed, 132 insertions(+), 20 deletions(-) diff --git a/proposals/0381-task-group-discard-results.md b/proposals/0381-task-group-discard-results.md index 4ebd7033e1..e632076814 100644 --- a/proposals/0381-task-group-discard-results.md +++ b/proposals/0381-task-group-discard-results.md @@ -1,4 +1,4 @@ -# discardResults for TaskGroups +# DiscardingTaskGroups * Proposal: [SE-0381](0381-task-group-discard-results.md) * Authors: [Cory Benfield](https://github.com/Lukasa), [Konrad Malawski](https://github.com/ktoso) @@ -79,45 +79,157 @@ While this is workable, it forces users to determine a value for `maxConcurrency ## Proposed Solution -We propose adding a new Boolean option to `TaskGroup` and `ThrowingTaskGroup` factory functions (`withTaskGroup` and `withThrowingTaskGroup`), called `discardResults`. This option defaults to `false`, which causes `TaskGroup` and `ThrowingTaskGroup` to behave as they do today. When the user sets this value to `true`, the runtime behaviour of `TaskGroup` and `ThrowingTaskGroup` changes in the following ways: +We propose adding new `DiscardingTaskGroup` and `ThrowingDiscardingTaskGroup` group types (obtained by `withDiscardingTaskGroup` and `withThrowingDiscardingTaskGroup`). These groups, are somewhat similar to the normal `TaskGroups` implementations, however they differ in the following important ways: -1. `[Throwing]TaskGroup` automatically cleans up its child `Task`s when those `Task`s complete. -2. `[Throwing]TaskGroup` always returns `nil` from its `next()` method, behaving like an empty `AsyncSequence`. +1. `[Throwing]DiscardingTaskGroup` automatically cleans up its child `Task`s when those `Task`s complete. +2. `[Throwing]DiscardingTaskGroup` do not have a `next()` method, nor do they conform to `AsyncSequence`. -This has the effect of automatically discarding the return type from the child tasks. +These group types are _not_ parameterized with the `ChildTaskResult`, and it is assumed to be `Void`, because as the name implies, they are always _discarding the results_ of their child tasks. -In this mode, `[Throwing]TaskGroup` maintains many of the same behaviours as when `discardResults` is set to `false`, albeit in some cases with slightly different manifestations: +Cancellation and error propagation of `[Throwing]DiscardingTaskGroup` works the same way one comes to expect a task group to behave, however due to the inability to explicitly use `next()` to "re-throw" a child task error, the discarding task group types must handle this behavior implicitly by re-throwing the _first_ encountered error and cancelling the group. -1. `ThrowingTaskGroup`s are automatically cancelled when one of their child `Task`s terminates with a thrown error, as if `next()` had been immediately called in the parent `Task`. -2. `ThrowingTaskGroup`s that are cancelled in this way will, after awaiting all their child `Task`s, throw the error that originally caused them to be auto-cancelled, again as if `next()` had been immediately called in the parent `Task`. -3. Automatic cancellation propagation works as usual, so cancelling the `Task` that owns a `[Throwing]TaskGroup` automatically cancels all child `Task`s. +`[Throwing]DiscardingTaskGroup` is a structured concurrency primitive, the same way as `[Throwing]TaskGroup` and _must_ automatically await all submitted tasks before the body of the `[try] await with[Throwing]DiscardingTaskGroup { body }` returns. ### API Surface ```swift -public func withTaskGroup( - of childTaskResultType: ChildTaskResult.Type, +public func withDiscardingTaskGroup( returning returnType: GroupResult.Type = GroupResult.self, - discardResults: Bool = false, - body: (inout TaskGroup) async -> GroupResult -) async -> GroupResult { + body: (inout DiscardingTaskGroup) async -> GroupResult +) async -> GroupResult { ... } -public func withThrowingTaskGroup( - of childTaskResultType: ChildTaskResult.Type, +public func withThrowingDiscardingTaskGroup( returning returnType: GroupResult.Type = GroupResult.self, - discardResults: Bool = false, - body: (inout ThrowingTaskGroup) async throws -> GroupResult -) async rethrows -> GroupResult { + body: (inout ThrowingDiscardingTaskGroup) async throws -> GroupResult +) async rethrows -> GroupResult { ... } ``` +And the types themselfes, mostly mirroring the APIs of `TaskGroup`, except that they're missing `next()` and related functionality: + +```swift +public struct DiscardingTaskGroup { + + public mutating func addTask( + priority: TaskPriority? = nil, + operation: @Sendable @escaping () async -> Void + ) + + public mutating func addTaskUnlessCancelled( + priority: TaskPriority? = nil, + operation: @Sendable @escaping () async -> Void + ) -> Bool + + public mutating func waitForAll() async + + public var isEmpty: Bool + + public func cancelAll() + public var isCancelled: Bool +} +/// Task groups are by-design not sendable, this is expressed by the following: +@available(*, unavailable) +extension DiscardingTaskGroup: Sendable { } + +public struct ThrowingDiscardingTaskGroup { + + public mutating func addTask( + priority: TaskPriority? = nil, + operation: @Sendable @escaping () async throws -> Void + ) + + public mutating func addTaskUnlessCancelled( + priority: TaskPriority? = nil, + operation: @Sendable @escaping () async throws -> Void + ) -> Bool + + public mutating func waitForAll() async throws + + public var isEmpty: Bool + + public func cancelAll() + public var isCancelled: Bool +} +/// Task groups are by-design not sendable, this is expressed by the following: +@available(*, unavailable) +extension DiscardingThrowingTaskGroup: Sendable { } +``` + +## Detailed Design + +### Discarding results + +As indicated by the name a `[Throwing]DiscardingTaskGroup` will discard results of its child tasks _immediately_ and release the child task that produced the result. This allows for efficient and "running forever" request accepting loops such as HTTP or RPC servers. + +Specifically, the first example shown in the Motivation section of this proposal, _is_ safe to be expressed using a discarding task group, as follows: + +```swift +// GOOD, no leaks! +try await withThrowingDiscardingTaskGroup() { group in + while let newConnection = try await listeningSocket.accept() { + group.addTask { + handleConnection(newConnection) + } + } +} +``` + +This code–unlike the `withThrowingTaskGroup` version shown earlier–does not leak tasks and therefore is safe and the recommended way to express such handler loops. + +### Error propagation and group cancellation + +Throwing task groups rely on the `next()` (or `waitForAll()`) being throwing and end users consuming the child tasks this way in order to surface any error that the child tasks may have thrown. It is possible for a `ThrowingTaskGroup` to explicitly collect results (and failures), and react to them, like this: + +```swift +try await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { try boom() } + group.addTask { try boom() } + group.addTask { try boom() } + + try await group.next() // re-throws whichever error happened first +} // since body threw, the group and remaining tasks are immediately cancelled +``` + +The above snippet illustrates a simple case of the error propagation out of a child task, through `try await next()` (or `try await group.waitForAll()`) out of the `withThrowingTaskGroup` closure body. As soon as an error is thrown out of the closure body, the group cancels itself and all remaining tasks implicitly, finally proceeding to await all the pending tasks. + +This pattern is not possible with `ThrowingDiscardingTaskGroup` because the the results collecting methods are not available on discarding groups. In order to properly support the common use-case of discarding groups, the failure of a single task, should implicitly and _immediately_ cancel the group and all of its siblings. + +This can be seen as the implicit immediate consumption of the child tasks inspecting the task for failures, and "re-throwing" the failure automatically. The error is then also re-thrown out of the `withThrowingDiscardingTaskGroup` method, like this: + +```swift +try await withThrowingDiscardingTaskGroup() { group in + group.addTask { try boom(1) } + group.addTask { try boom(2) } + group.addTask { try boom(3) } + // whichever failure happened first, is collected, stored, and re-thrown out of the method when exiting. +} +``` + +In other words, discarding task groups follow the "one for all, and all for one" pattern for failure handling. A failure of a single child task, _immediately_ causes cancellation of the group and its siblings. + +Preventing this behavior can be done in two ways: + +- using `withDiscardingTaskGroup`, since the child tasks won't be allowed to throw, and must handle their errors in some other way, +- including normal `do {} catch {}` error handling logic inside the child-tasks, which only re-throws. + +We feel this is the right approach for this structured concurrency primitive, as we should be leaning on normal swift code patterns, rather than introduce special one-off ways to handle and deal with errors. Although, if it were necessary, we could introduce a "failure reducer" in the future. + ## Alternatives Considered -### Introducing new types +### Introducing new "TaskPool" type (initial pitch) The [original pitch](https://forums.swift.org/t/pitch-task-pools/61703) introduced two new types, `TaskPool` and `ThrowingTaskPool`. These types were introduced in order to expose at the type system level the inability to iterate the pool for new tasks. This would avoid the `next()` behaviour introduced in this pitch, where `next()` always returns `nil`. This was judged a worthwhile change to justify introducing new types. Several reviewers of the pitch felt that this was not a sufficiently useful capability to justify the introduction of the new types, and that the pitched behaviour more properly belonged as a "mode" of operation on `TaskGroup`. In line with that feedback, this proposal has moved to using the `discardResults` option. +### Extending [Throwing]TaskGroup with discardResults flag + +After feedback on the the initial pitch, we attempted to avoid introducing a new type, and instead handle it using a `discardResults: Bool` flag on `with[Throwing]TaskGroup()` this was fairly problematic because: + +- the group would have the `next()` method as well as `AsyncSequence` conformance present, but non-functional, i.e. always returning `nil` from `next()` which could lead to subtle bugs and confusion. +- we'd end up constraining this new option only to child task result types of `Void`, making access to this functionality a bit hard to discover + +The group would also have very different implicit cancellation behavior, ultimately leading us to conclude during the Swift Evolution review that these two behaviors should not be conflated into one type. + ### Error throwing behaviour The pitch proposes that `ThrowingTaskGroup` with `discardResults` set to `true` will throw only the _first_ error thrown by a child `Task`. This means that all subsequent errors will be discarded, which is an unfortunate loss of information. Two alternative behaviours could be chosen: we could not add `discardResults` to `ThrowingTaskGroup` at all, or we could throw an aggregate error that contains all errors thrown by the child `Task`s. From 7d5f9a2c164c631eac97b0928ac172e95a0e54d7 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 21 Dec 2022 20:08:59 +0900 Subject: [PATCH 2854/4563] cleanup --- proposals/0381-task-group-discard-results.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/proposals/0381-task-group-discard-results.md b/proposals/0381-task-group-discard-results.md index e632076814..40b93637f8 100644 --- a/proposals/0381-task-group-discard-results.md +++ b/proposals/0381-task-group-discard-results.md @@ -119,14 +119,11 @@ public struct DiscardingTaskGroup { operation: @Sendable @escaping () async -> Void ) -> Bool - public mutating func waitForAll() async - public var isEmpty: Bool public func cancelAll() public var isCancelled: Bool } -/// Task groups are by-design not sendable, this is expressed by the following: @available(*, unavailable) extension DiscardingTaskGroup: Sendable { } @@ -142,14 +139,11 @@ public struct ThrowingDiscardingTaskGroup { operation: @Sendable @escaping () async throws -> Void ) -> Bool - public mutating func waitForAll() async throws - public var isEmpty: Bool public func cancelAll() public var isCancelled: Bool } -/// Task groups are by-design not sendable, this is expressed by the following: @available(*, unavailable) extension DiscardingThrowingTaskGroup: Sendable { } ``` From e3632131908004d640fff2142f1159910d2f63e7 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 21 Dec 2022 20:25:13 +0900 Subject: [PATCH 2855/4563] amend alternate error handling to use current syntax --- proposals/0381-task-group-discard-results.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0381-task-group-discard-results.md b/proposals/0381-task-group-discard-results.md index 40b93637f8..f89bad0b67 100644 --- a/proposals/0381-task-group-discard-results.md +++ b/proposals/0381-task-group-discard-results.md @@ -224,19 +224,19 @@ After feedback on the the initial pitch, we attempted to avoid introducing a new The group would also have very different implicit cancellation behavior, ultimately leading us to conclude during the Swift Evolution review that these two behaviors should not be conflated into one type. -### Error throwing behaviour +### Alternate Error throwing behaviour -The pitch proposes that `ThrowingTaskGroup` with `discardResults` set to `true` will throw only the _first_ error thrown by a child `Task`. This means that all subsequent errors will be discarded, which is an unfortunate loss of information. Two alternative behaviours could be chosen: we could not add `discardResults` to `ThrowingTaskGroup` at all, or we could throw an aggregate error that contains all errors thrown by the child `Task`s. +The pitch proposes that `ThrowingDiscardingTaskGroup` will throw only the _first_ error thrown by a child `Task`. This means that all subsequent errors will be discarded, which is an unfortunate loss of information. Two alternative behaviours could be chosen: we could not provide `ThrowingDiscardingTaskGroup` at all, or we could throw an aggregate error that contains *all* errors thrown by the child `Task`s. -Not allowing `discardResults` on `ThrowingTaskGroup` is a substantial ergonomic headache. Automatic error propagation is one of the great features of structured concurrency, and not being able to use it in servers or other long-running processes is an unnecessary limitation, especially as it's not particularly technically challenging to propagate errors. For this reason, we do not think it's wise to omit `discardResults` on `ThrowingTaskGroup`. +Not allowing offering `ThrowingDiscardingTaskGroup` at all is a substantial ergonomic headache. Automatic error propagation is one of the great features of structured concurrency, and not being able to use it in servers or other long-running processes is an unnecessary limitation, especially as it's not particularly technically challenging to propagate errors. For this reason, we do not think it's wise to omit `discardResults` on `ThrowingDiscardingTaskGroup`. -The other alternative is to throw an aggregate error. This would require that `ThrowingTaskGroup` persist all (or almost all) errors thrown by child tasks and merge them together into a single error `struct` that is thrown. This idea is a mixed bag. +The other alternative is to throw an aggregate error. This would require that `ThrowingDiscardingTaskGroup` persist all (or almost all) errors thrown by child tasks and merge them together into a single error `struct` that is thrown. This idea is a mixed bag. The main advantage of throwing an aggregate error is that no information is lost. Programs can compute on all errors that were thrown, and at the very least can log or provide other metrics based on those errors. Avoiding data loss in this way is valuable, and gives programmers more flexibility. Throwing an aggregate error has two principal disadvantages. The first is that aggregate errors do not behave gracefully in `catch` statements. If a child task has thrown `MyModuleError`, programmers would like to write `catch MyModuleError` in order to handle it. Aggregate errors break this situation, even if only one error is thrown: programmers have to write `catch let error = error as? MyAggregateError where error.baseErrors.contains(where: { $0 is MyModuleError })`, or something else equally painful. -The other main disadvantage is the storage bloat from `CancellationError`. The first thrown error will auto-cancel all child `Task`s. This is great, but that cancellation will likely manifest in a thrown `CancellationError`, which will presumably bubble to the top and be handled by the `ThrowingTaskGroup`. This means that a `ThrowingTaskGroup` will likely store a substantial collection of errors, where all but the first are `CancellationError`. This is a substantial regression in convenience for the mainline case, with additional costs in storage, without providing any more meaningful information. +The other main disadvantage is the storage bloat from `CancellationError`. The first thrown error will auto-cancel all child `Task`s. This is great, but that cancellation will likely manifest in as series of `CancellationError`s, which will presumably bubble to the top and be handled by the `ThrowingDiscardingTaskGroup`. This means that a `ThrowingDiscardingTaskGroup` will likely store a substantial collection of errors, where all but the first are `CancellationError`. This is a substantial regression in convenience for the mainline case, with additional costs in storage, without providing any more meaningful information. For these reasons we've chosen the middle behaviour, where only one error is thrown. We think there is merit in throwing an aggregate error, however, and we'd like community feedback on this alternative. From ce04dcfe36dc1d9536b5c07294b85e814f98525b Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 21 Dec 2022 20:26:27 +0900 Subject: [PATCH 2856/4563] rethrows -> throws --- proposals/0381-task-group-discard-results.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0381-task-group-discard-results.md b/proposals/0381-task-group-discard-results.md index f89bad0b67..630c323871 100644 --- a/proposals/0381-task-group-discard-results.md +++ b/proposals/0381-task-group-discard-results.md @@ -101,7 +101,7 @@ public func withDiscardingTaskGroup( public func withThrowingDiscardingTaskGroup( returning returnType: GroupResult.Type = GroupResult.self, body: (inout ThrowingDiscardingTaskGroup) async throws -> GroupResult -) async rethrows -> GroupResult { ... } +) async throws -> GroupResult { ... } ``` And the types themselfes, mostly mirroring the APIs of `TaskGroup`, except that they're missing `next()` and related functionality: @@ -265,4 +265,4 @@ A number of concerns were raised during the pitch process that the "throw the fi The proposers feel that introducing this API surface in the first version of this feature adds significant complexity to this type. This requires us to be confident that the API surface proposed is going to serve the necessary use-cases, without adding unnecessary cognitive load. It's also not entirely clear where the line is between features that can be handled using `try`/`catch` and features that require a new error filter function. -As a result, the proposrs have elected to defer implementing anything here until there are real-world examples to generalise from. Having some sort of error filter is likely to be valuable, and the implementation will preserve the capability to implement such a function, but for now the proposal is going to be kept relatively small. \ No newline at end of file +As a result, the proposal authors have elected to defer implementing anything here until there are real-world examples to generalise from. Having some sort of error filter is likely to be valuable, and the implementation will preserve the capability to implement such a function, but for now the proposal is going to be kept relatively small. \ No newline at end of file From 238c13f9ec486d7a8af7577a449a6cc576eafceb Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Sat, 24 Dec 2022 15:25:07 +0900 Subject: [PATCH 2857/4563] Task groups: Update intro to match contents --- proposals/0381-task-group-discard-results.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0381-task-group-discard-results.md b/proposals/0381-task-group-discard-results.md index 630c323871..369debb90c 100644 --- a/proposals/0381-task-group-discard-results.md +++ b/proposals/0381-task-group-discard-results.md @@ -8,7 +8,7 @@ ### Introduction -We propose to introduce a new boolean parameter, `discardResults`, to `TaskGroup` and `ThrowingTaskGroup`. This parameter controls whether the `TaskGroup` retains the results of its completed child `Task`s for passing to `next()`, or whether it discards those results immediately. +We propose to introduce a new type of structured concurrency task group: `Discarding[Throwing]TaskGroup`. This type of group is similar to `TaskGroup` however it discards results of its child tasks immediately. It is specialized for potentially never-ending task groups, such as top-level loops of http or other kinds of rpc servers. Pitch thread: [Task Pools](https://forums.swift.org/t/pitch-task-pools/61703). @@ -265,4 +265,4 @@ A number of concerns were raised during the pitch process that the "throw the fi The proposers feel that introducing this API surface in the first version of this feature adds significant complexity to this type. This requires us to be confident that the API surface proposed is going to serve the necessary use-cases, without adding unnecessary cognitive load. It's also not entirely clear where the line is between features that can be handled using `try`/`catch` and features that require a new error filter function. -As a result, the proposal authors have elected to defer implementing anything here until there are real-world examples to generalise from. Having some sort of error filter is likely to be valuable, and the implementation will preserve the capability to implement such a function, but for now the proposal is going to be kept relatively small. \ No newline at end of file +As a result, the proposal authors have elected to defer implementing anything here until there are real-world examples to generalise from. Having some sort of error filter is likely to be valuable, and the implementation will preserve the capability to implement such a function, but for now the proposal is going to be kept relatively small. From 5281e9a964f52fc83cc9af50e4047cd7e075b714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EC=9E=AC=ED=98=B8?= Date: Mon, 26 Dec 2022 09:43:00 +0900 Subject: [PATCH 2858/4563] [SE-0289] Fix minor typo (#1892) --- proposals/0289-result-builders.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0289-result-builders.md b/proposals/0289-result-builders.md index 95bdf2ca47..a0313d6e00 100644 --- a/proposals/0289-result-builders.md +++ b/proposals/0289-result-builders.md @@ -1067,7 +1067,7 @@ let other = Def.otherThings let v1 = ThingBuilder.buildDeclaration(other) let v2 = -let v3 = +let v3 = let v4 = ThingBuilder.buildBlock(v0, v1, v2, v3) ``` From 5c33889ad17b408a1a38ec694b3f9484aad287cf Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 27 Dec 2022 11:39:33 +0900 Subject: [PATCH 2859/4563] SE-0381: add missing type parameter in listing --- proposals/0381-task-group-discard-results.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0381-task-group-discard-results.md b/proposals/0381-task-group-discard-results.md index 369debb90c..5a08c07f43 100644 --- a/proposals/0381-task-group-discard-results.md +++ b/proposals/0381-task-group-discard-results.md @@ -127,7 +127,7 @@ public struct DiscardingTaskGroup { @available(*, unavailable) extension DiscardingTaskGroup: Sendable { } -public struct ThrowingDiscardingTaskGroup { +public struct ThrowingDiscardingTaskGroup { public mutating func addTask( priority: TaskPriority? = nil, From 79d826d46536ea95b2ecc8ba8581dc45f3d88703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20G?= <101190846+spqw@users.noreply.github.com> Date: Wed, 28 Dec 2022 18:55:48 +0100 Subject: [PATCH 2860/4563] Update 0271-package-manager-resources.md --- proposals/0271-package-manager-resources.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0271-package-manager-resources.md b/proposals/0271-package-manager-resources.md index 25b34836a0..f83bbad6ef 100644 --- a/proposals/0271-package-manager-resources.md +++ b/proposals/0271-package-manager-resources.md @@ -52,7 +52,7 @@ We propose the following to support resources in Swift packages: - Add two new rules "copy" and "process" to existing list of rules. At this time, there will be no additional built-in file type that use these two new rules. -- Vend an API in libSwiftPM to allow its clients to register additional file types supported by those clients. SwiftPM will automatially detect the matching files in a target and report them in the package model data structure. Similar to source files, package authors will not need to explicitly declare these files in the package manifest. This ability is useful for clients like Xcode to support platform-specific file types (such as `.metal`) without having to bake in a hardcoded list in SwiftPM’s codebase. +- Vend an API in libSwiftPM to allow its clients to register additional file types supported by those clients. SwiftPM will automatically detect the matching files in a target and report them in the package model data structure. Similar to source files, package authors will not need to explicitly declare these files in the package manifest. This ability is useful for clients like Xcode to support platform-specific file types (such as `.metal`) without having to bake in a hardcoded list in SwiftPM’s codebase. - Add a new `resources` parameter in `target` and `testTarget` APIs to allow declaring resource files explicitly. From df3f001a11e715777446e72615f1031be7979354 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 3 Jan 2023 19:17:58 -0500 Subject: [PATCH 2861/4563] Preliminary review: fix headers --- ...-deprecate-uiapplicationmain-and-nsapplicationmain.md} | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) rename proposals/{deprecate-framework-specific-entrypoints.md => NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md} (91%) diff --git a/proposals/deprecate-framework-specific-entrypoints.md b/proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md similarity index 91% rename from proposals/deprecate-framework-specific-entrypoints.md rename to proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md index 71f98bce74..2e370e2382 100644 --- a/proposals/deprecate-framework-specific-entrypoints.md +++ b/proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md @@ -1,9 +1,11 @@ # Deprecate @UIApplicationMain and @NSApplicationMain -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-NNNN](NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md) * Authors: [Robert Widmann](https://github.com/codafi) -* Review Manager: TBD -* Status: **[Implemented](https://github.com/apple/swift/pull/62151)** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Awaiting review** +* Implementation: [PR 62151](https://github.com/apple/swift/pull/62151) +* Review: ([pitch](https://forums.swift.org/t/deprecate-uiapplicationmain-and-nsapplicationmain/61493)) ## Introduction From 4ba644664585e7ce24840dbfc8441ae2be9548d8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 3 Jan 2023 16:22:57 -0800 Subject: [PATCH 2862/4563] [Declaration macros] Initial version --- proposals/nnnn-declaration-macros.md | 360 +++++++++++++++++++++++++++ 1 file changed, 360 insertions(+) create mode 100644 proposals/nnnn-declaration-macros.md diff --git a/proposals/nnnn-declaration-macros.md b/proposals/nnnn-declaration-macros.md new file mode 100644 index 0000000000..64463e9a86 --- /dev/null +++ b/proposals/nnnn-declaration-macros.md @@ -0,0 +1,360 @@ +# Declaration Macros + +* Proposal: [SE-nnnn](nnnn-declaration-macros.md) +* Authors: [Doug Gregor](https://github.com/DougGregor) +* Review Manager: Unassigned +* Status: **Pending review** +* Implementation: Nothing yet +* Review: + +## Introduction + +Declaration macros provide a way to extend Swift by creating new declarations based on arbitrary syntactic transformations on their arguments. They make it possible to extend Swift in ways that were only previously possible by introducing new language features, helping developers build more expressive libraries and eliminate extraneous boilerplate. + +Swift-evolution thread: + +## Motivation + +Declaration macros are one part of the [vision for macros in Swift](https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900), which lays out general motivation for introducing macros into the language. They build on the ideas and motivation of [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) to cover a large new set of use cases; we will refer to that proposal often for the basic model of how macros integrate into the language. While expression macros are limited to generating a single expression, declaration macros can create new declarations---functions, properties, types, and so on---greatly expanding the expressiveness of the macro system. Here are a number of potential use cases for declaration macros, many of which will be explored in detail in this proposal: + +* Creating trampoline or wrapper functions, such as automatically creating a completion-handler version of an `async` function. +* Creating wrapper types from another type, such as forming an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset) from an enum containing flags. +* Creating accessors for a stored property or subscript, subsuming some of the behavior of [SE-0258 "Property Wrappers"](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md). +* Performing a non-trivial compile-time computation to produce an efficient implementation of a function, such as creating a [perfect hash function](https://en.wikipedia.org/wiki/Perfect_hash_function) for a fixed set of strings. +* Subsuming the `#warning` and `#error` directives introduced in [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) into macros. + +## Proposed solution + +The proposal adds declaration macros, which are expanded to create zero or more new declarations. These declarations can then be referenced from other Swift code, making declaration macros useful for many different kinds of code generation and manipulation. + +There are several different forms of declaration macros, which differ both based on the kinds of inputs to the macro and on the effects the macro can have on the resulting program. This proposal introduces the following kinds of declaration macros: + +* *Freestanding* declaration macros use the same `#`-prefixed syntax as [expression macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md), and can produce zero or more declarations as a result. Freestanding declaration macros can be used anywhere that a declaration can occur, e.g., at the top level, within types and extensions, and in function bodies. +* *Attached* declaration macros use custom attribute syntax (as in [property wrappers](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md) and [result builders](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md)) and are associated with a particular declaration. These macros can augment the declaration to which they are attached in limited ways, as well as introducing "peer" declarations alongside the declaration to which they are attached. + +Both freestanding and attached declaration macros are declared with `macro`, and have [type-checked macro arguments](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#type-checked-macro-arguments-and-results) in the same manner as expression macros, which allows their behavior to be customized. + +All declaration macros are implemented as types that conform to the `DeclarationMacro` protocol. Like the [`Macro` protocol](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-protocols), the `DeclarationMacro` protocol has no requirements, but is used to describe the role of macros more generally. + +```swift +public protocol DeclarationMacro: Macro { } +``` + +Each kind of declaration macro (freestanding or attached) will have its own protocol that inherits `DeclarationMacro`. + +### Freestanding macros + +Freestanding macros are the most like expression macros. For example, the `warning` directive introduced by [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) can be described as a freestanding macro as follows: + +```swift +/// Emits the given message as a warning, as in SE-0196. +@declaration(.freestanding) macro warning(_ message: String) +``` + +The `@declaration` attribute specifies that this is a declaration macro, which is also freestanding. Given this macro declaration, the syntax + +```swift +#warning("unsupported configuration") +``` + +can be used anywhere a declaration can occur. + +Freestanding macros are implemented as types that conform to the `FreestandingDeclarationMacro` protocol : + +```swift +public protocol FreestandingDeclarationMacro: DeclarationMacro { + /// Expand a macro described by the given freestanding macro expansion declaration + /// within the given context to produce a set of declarations. + static func expansion( + of node: MacroExpansionDeclSyntax, in context: inout MacroExpansionContext + ) throws -> [DeclSyntax] +} +``` + +The `MacroExpansionDeclSyntax` node provides the syntax tree for the use site (e.g., `#warning("unsupported configuration")`), and has the same grammar and members as the `MacroExpansionExprSyntax` node introduced in [SE-0382](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion). The grammar parallels `macro-expansion-expression`: + +``` +declaration -> macro-expansion-declaration +macro-expansion-declaration -> '#' identifier generic-argument-clause[opt] function-call-argument-clause[opt] trailing-closures[opt] +``` + +The implementation of a freestanding `warning` declaration macro extracts the string literal argument (producing an error if there wasn't one) and emits a warning. It returns an empty list of declarations: + +```swift +public struct WarningMacro: FreestandingDeclarationMacro { + public static func expansion( + of node: MacroExpansionDeclSyntax, in context: inout MacroExpansionContext + ) throws -> [DeclSyntax] { + guard let messageExpr = node.argumentList.first?.expression?.as(SpecializeExprSyntax.self), + messageExpr.segments.count == 1, + let firstSegment = messageExpr.segments.first, + case let .stringSegment(message) = firstSegment else { + throw SimpleError(node, "warning macro requires a non-interpolated string literal") + } + + context.diagnose(.warning(firstSegment.text)) + return [] + } +} +``` + +### Attached macros + +Attached macros are named as such because they are attached to a specific declaration. They are written using attribute syntax (e.g., `@addCompletionHandler`), and are able to make specific additions to both the declaration to which they are attached as well as introducing "peer" declarations alongside the declaration to which they are attached. + +For example, here is a declaration of a macro that introduces a completion-handler version of a given asynchronous function: + +```swift +@declaration(.attached, peers: [.overloaded]) macro addCompletionHandler +``` + +Again, this macro uses the `@declaration` to indicate that it is a declaration macro, but with `.attached`. The `peers` argument specifies that this macro will generate peer declarations, and how the names of those peer declarations are formed. In this case, our macro will produce a peer that is overloaded with the declaration to which it is attached, i.e., it has the same base name. Later parts of this proposal will go into more depth on the naming of generated declarations , as well as providing rationale for this up-front declaration of macro behavior. + +An attached macro like this can be used as an attribute. For example: + +```swift +@addCompletionHandler +func fetchAvatar(_ username: String) async -> Image? { ... } +``` + +Attached macros are implemented via types that conform to the `AttachedDeclarationMacro` protocol: + +```swift +public AttachedDeclarationMacro: DeclarationMacro { + /// Expand a macro described by the given custom attribute and + /// attached to the given declaration and evaluated within a + /// particular expansion context. + /// + /// The macro expansion can introduce a number of changes to + /// the given declaration, all of which must be represented by + /// the `AttachedDeclarationExpansion` result. + static func expansion( + of node: CustomAttributeSyntax, + attachedTo declaration: DeclSyntax, + in context: inout MacroExpansionContext + ) throws -> AttachedDeclarationExpansion +} +``` + +The effect of `addCompletionHandler` is to produce a new "peer" declaration with the same signature as the declaration it is attached but with `async` and the result type removed, and a completion handler argument added, e.g., + +```swift +/// Expansion of the macro produces the following. +func fetchAvatar(_ username: String, completionHandler: @escaping (Image?) -> Void ) { + Task.detached { + completionHandler(await fetchAvatar(username)) + } +} +``` + +The actual implementation of this macro involves a lot of syntax manipulation, so we settle for a pseudo-code definition here: + +```swift +public struct AddCompletionHandler: AttachedDeclarationMacro { + public static func expansion( + of node: CustomAttributeSyntax, + attachedTo declaration: DeclSyntax, + in context: inout MacroExpansionContext + ) throws -> AttachedDeclarationExpansion { + // make sure we have an async function to start with + // form a new function "completionHandlerFunc" by starting with that async function and + // - remove async + // - remove result type + // - add a completion-handler parameter + // - add a body that forwards arguments + // return the new peer function + return AttachedDeclarationExpansion(peers: [completionHandlerFunc]) + } +} +``` + +The full capabilities of `AttachedDeclarationExpansion` will be described later, and are expected to expand over time. However, another common use case involves creating member declarations within a type. For example, to define static members to ease the definition of an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset). Given: + +```swift +@optionSetMembers +struct MyOptions: OptionSet { + enum Option: Int { + case a + case b + case c + } +} +``` + +This struct should be expanded to contain both a `rawValue` field and static properties for each of the options, e.g., + +```swift +// Expands to... +struct MyOptions: OptionSet { + enum Option: Int { + case a + case b + case c + } + + // Synthesized code below... + var rawValue: Int = 0 + + static var a = MyOptions(rawValue: 1 << Option.a.rawValue) + static var b = MyOptions(rawValue: 1 << Option.b.rawValue) + static var c = MyOptions(rawValue: 1 << Option.c.rawValue) +} +``` + +The macro itself will be declared as an attached declaration macro that defines an arbitrary set of members: + +```swift +/// Create the necessary members to turn a struct into an option set. +@declaration(.attached, members: [.named("rawValue"), .arbitrary]) macro optionSetMembers +``` + +The `members` argument specifies that this macro will be defining new members of the declaration to which it is attached. In this case, while the macro knows it will define a member named `rawValue`, there is no way for the macro to predict the names of the static properties it is defining, so it also specifies `.arbitrary` to indicate that it will introduce members with arbitrarily-determined names. + +As a final example, property-wrapper-like behavior can be implemented via an attached declaration macro that introduces accessors. Consider a macro that can be applied to a stored property to instead access a dictionary keyed by the property name. Such a macro could be used like this: + +```swift +struct MyStruct { + var storage: [AnyHashable: Any] = [:] + + @dictionaryStorage + var name: String + + @dictionaryStorage(key: "birth_date") + var birthDate: Date? +} +``` + +The `dictionaryStorage` attached declaration macro would alter `MyStruct` as follows: + +```swift +struct MyStruct { + var storage: [String: Any] = [:] + + var name: String { + get { + storage["name"]! as! String + } + + set { + storage["name"] = newValue + } + } + + var birthDate: Date? { + get { + storage["birth_date"] as Date? + } + + set { + if let newValue { + storage["birth_date"] = newValue + } else { + storage.removeValue(forKey: "birth_date") + } + } + } +} +``` + +The macro can be declared as follows: + +```swift +@declaration(.attached, members: [.accessors]) macro dictionaryStorage +@declaration(.attached, members: [.accessors]) macro dictionaryStorage(key: String) +``` + +The implementation of the macro itself would create the accessor declarations and supply them via `AttachedDeclarationExpansion(members:)`. Property wrappers aren't great for this case, because they would still define a backing stored property (e.g., `_name`) of the property wrapper type. To mimic the full behavior of property wrappers, one could introduce both members (for the accessors) and peers (for the backing stored property). + +### Up-front declarations of newly-introduced macro names + +Declaration macros require one to declare the names of entities that will be declared. These can involve member and/or peer declaration names, and are provided as an array consisting of a few different kinds: + +* Declarations with a specific fixed name: `.named("rawValue")` +* Declarations that have the same base name as the declaration to which the macro is attached, and are therefore overloaded with it: `.overloaded`. +* Accessors of the declaration to which the macro is attached: `.accessors` +* Declarations whose name is formed by adding a prefix to the name of the declaration to which the macro is attached: `.prefixed("_")` +* Declarations whose name is formed by adding a suffix to the name of the declaration to which the macro is attached: `.suffixed("_docinfo")`. +* Declarations whose names cannot be described by any of the simple rules above: `.arbitrary`. + +A declaration macro can only introduce new declarations whose names are covered by the kinds provided, or have their names generated via `MacroExpansionContext.createUniqueLocalName`. This ensures that, in most cases (where `.arbitrary` is not specified) the Swift compiler and related tools can reason about the set of names that will introduced by a given use of a declaration macro without having to expand the macro, which can reduce the compile-time cost of macros and improve incremental builds. + +## Detailed design + +### `AttachedDeclarationExpansion` + +An attached declaration macro implementation returns an instance of the `AttachedDeclarationExpansion` structure to specify the changes. The structure is specified as follows: + +```swift +public struct AttachedDeclarationExpansion { + /// The set of peer declarations introduced by this macro, which will be introduced alongside the use of the + /// macro. + public var peers: [DeclSyntax] = [] + + /// The set of member declarations introduced by this macro, which are nested inside + public var members: [DeclSyntax] = [] + + /// For a function, body for the function. If non-nil, this will replace any existing function body. + public var functionBody: CodeBlockSyntax? = nil + + public init(peers: [DeclSyntax] = [], members: [DeclSyntax] = [], functionBody: CodeBlockSyntax? = nil) +} +``` + +This structure is expected to grow more capabilities over time. Changes that can affect how a particular declaration is used will likely require paired changes to the arguments of `@declaration`, and can be considered in subsequent proposals. + +### Macros in the Standard Library + +#### SE-0196 `warning` and `error` + +The `#warning` and `#error` directives introduced in [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md): can be implemented directly as freestanding macros: + +```swift +/// Emit a warning containing the given message. +@declaration(.freestanding) macro warning(_ message: String) + +/// Emit an error containing the given message +@declaration(.freestanding) macro error(_ message: String) +``` + +## Source compatibility + +Freestanding declaration macros use the same syntax introduced for expression macros, which were themselves a pure extension without an impact on source compatibility. There is a syntactic ambiguity between expression and freestanding declaration macros, i.e., `#warning("watch out")` within a function body could be either an expression or a declaration. The distinction will need to be determined semantically, by determining whether the named macro is either an expression or a freestanding declaration macro. + +Attached declaration macros use the same syntax introduced for custom attributes (such as property wrappers), and therefore do not have an impact on source compatibility. + +## Effect on ABI stability + +Macros are a source-to-source transformation tool that have no ABI impact. + +## Effect on API resilience + +Macros are a source-to-source transformation tool that have no effect on API resilience. + +## Alternatives considered + +### Mutating declarations rather than augmenting them + +Attached declaration macro implementations are provided with information about the macro expansion itself and the declaration to which they are attached. The implementation cannot directly make changes to the syntax of the declaration to which it is attached; rather, it must specify the additions or changes by packaging them in `AttachedDeclarationExpansion`. + +An alternative approach would be to allow the macro implementation to directly alter the declaration to which the macro is attached. This would provide a macro implementation with greater flexibility to affect the declarations it is attached to. However, it means that the resulting declaration could vary drastically from what the user wrote, and would preventing the compiler from making any determinations about what the declaration means before expanding the macro. This could have detrimental effects on compile-time performance (one cannot determine anything about a declaration until the macros have been run on it) and might also prevent macros from accessing information about the actual declaration in the future, such as the types of parameters. + +It might be possible to provide a macro implementation API that is expressed in terms of mutation on the original declaration, but restrict the permissible changes to those that can be handled by the implementation. For example, one could "diff" the syntax tree provided to the macro implementation and the syntax tree produced by the macro implementation to identify changes: those changes that are acceptable would be recorded into something like `AttachedDeclarationExpansion`, and any other differences would be reported as errors. Such a design could be layered on top of the design proposed here, e.g., with an `AttachedDeclarationExpansion` initializer that accepts the original declaration and the rewritten declaration. + +## Future directions + +### Extending `AttachedDeclarationExpansion` + +The set of changes that a macro can apply to the declaration to which is attached is (intentionally) limited by what can be expressed in `AttachedDeclarationExpansion`. Over time, we expect this structure to be extended to enable additional kinds of changes, such as adding attributes or modifiers to the declaration. These changes will likely be paired with changes to the `@declaration` syntax. For example, adding attributes to the declaration could mean introducing the following property into `AttachedDeclarationExpansion`: + +```swift +var attributes: [AttributeSyntax] = [] +``` + +but to minimize compile-time dependencies we would likely want to note when a macro may add attributes, e.g., + +```swift +@declaration(.attached, augmenting: [.attributes]) +macro sometimesDeprecated +``` + +This would ensure that the compiler knows when the attributes as they are written on a declaration are the complete set of attributes, vs. when it will have to expand the macro to determine what attributes are applied to the declaration. From cde46b7747a7c1ccdbc69eb8b4b9dd0121cdbe5b Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 3 Jan 2023 19:30:41 -0500 Subject: [PATCH 2863/4563] Remove some detritus I actually left in --- ...NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md b/proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md index 2e370e2382..ddb00369b2 100644 --- a/proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md +++ b/proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md @@ -11,8 +11,6 @@ `@UIApplicationMain` and `@NSApplicationMain` used to be the standard way for iOS and macOS apps respectively to declare a synthesized platform-specific entrypoint for an app. These functions have since been obsoleted by [SE-0281](https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md) and the `@main` attribute, and now represent a confusing bit of duplication in the language. This proposal seeks to deprecate these alternative entrypoint attributes in favor of `@main` in Swift 5.8, and makes their use in Swift 6 a hard error. -Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/deprecate-uiapplicationmain-and-nsapplicationmain/) - ## Motivation UIKit and AppKit have fully embraced the `@main` attribute and have made application adoption as simple as conforming to the `UIApplicationDelegate` and `NSApplicationDelegate` protocols. This now means that an author of an application is presented with two different, but ultimately needless, choices for an entrypoint: @@ -28,8 +26,7 @@ The use of both `@UIApplicationMain` and `@NSApplicationMain` under Swift 5 lang ## Detailed design ->Because `@UIApplicationMain` and `@NSApplicationMain` have remarkably similar usages in abstract, this portion of the document will only use examples of `@UIApplicationMain` and assume that the case for `@NSApplicationMain` follows similarly. - +Because `@UIApplicationMain` and `@NSApplicationMain` have remarkably similar usages in abstract, this portion of the document will only use examples of `@UIApplicationMain` and assume that the case for `@NSApplicationMain` follows similarly. Issue a diagnostic asking the user to remove `@UIApplicationMain`. The existing conformance to `UIApplicationDelegate` will kick in when the code is next built and the general-purpose entrypoint will be added instead. From 0351db0546505459b3d545b6d72b70504472647a Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Tue, 3 Jan 2023 20:30:12 -0500 Subject: [PATCH 2864/4563] Update 0376-function-back-deployment.md --- proposals/0376-function-back-deployment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index c2d47a5624..114246a2bd 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -4,8 +4,8 @@ * Author: [Allan Shortlidge](https://github.com/tshortli) * Implementation: [apple/swift#41271](https://github.com/apple/swift/pull/41271), [apple/swift#41348](https://github.com/apple/swift/pull/41348), [apple/swift#41416](https://github.com/apple/swift/pull/41416), [apple/swift#41612](https://github.com/apple/swift/pull/41612) as the underscored attribute `@_backDeploy` * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) -* Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0376-function-back-deployment/61507)) ([second review](https://forums.swift.org/t/se-0376-second-review-function-back-deployment/61671)) -* Status: **Active review (November 28 ... December 12, 2022)** +* Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0376-function-back-deployment/61507)) ([second review](https://forums.swift.org/t/se-0376-second-review-function-back-deployment/61671)) ([returned for revision (second review)](https://forums.swift.org/t/returned-for-revision-se-0376-second-review-function-back-deployment/62374)) +* Status: **Returned for revision** ## Introduction From ea9043e2fa79f88c514cb0cc7e3dfc1495f782ef Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 3 Jan 2023 19:49:25 -0700 Subject: [PATCH 2865/4563] Apply Editorial Remarks --- ...uiapplicationmain-and-nsapplicationmain.md | 105 ++++++++++++++---- 1 file changed, 81 insertions(+), 24 deletions(-) diff --git a/proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md b/proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md index ddb00369b2..814479ae9c 100644 --- a/proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md +++ b/proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md @@ -1,50 +1,111 @@ # Deprecate @UIApplicationMain and @NSApplicationMain -* Proposal: [SE-NNNN](NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md) +* Proposal: [SE-NNNN](NNNN-filename.md) * Authors: [Robert Widmann](https://github.com/codafi) -* Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Awaiting review** -* Implementation: [PR 62151](https://github.com/apple/swift/pull/62151) -* Review: ([pitch](https://forums.swift.org/t/deprecate-uiapplicationmain-and-nsapplicationmain/61493)) +* Review Manager: TBD +* Status: **[Implemented](https://github.com/apple/swift/pull/62151)** ## Introduction -`@UIApplicationMain` and `@NSApplicationMain` used to be the standard way for iOS and macOS apps respectively to declare a synthesized platform-specific entrypoint for an app. These functions have since been obsoleted by [SE-0281](https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md) and the `@main` attribute, and now represent a confusing bit of duplication in the language. This proposal seeks to deprecate these alternative entrypoint attributes in favor of `@main` in Swift 5.8, and makes their use in Swift 6 a hard error. +`@UIApplicationMain` and `@NSApplicationMain` used to be the standard way for +iOS and macOS apps respectively to declare a synthesized platform-specific +entrypoint for an app. These functions have since been obsoleted by +[SE-0281](https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md)'s +introduction of the `@main` attribute, and they now represent a confusing bit of +duplication in the language. This proposal seeks to deprecate these alternative +entrypoint attributes in favor of `@main` in pre-Swift 6, and it makes their use +in Swift 6 a hard error. + +Swift-evolution thread: [Discussion thread topic for that +proposal](https://forums.swift.org/t/deprecate-uiapplicationmain-and-nsapplicationmain/) ## Motivation -UIKit and AppKit have fully embraced the `@main` attribute and have made application adoption as simple as conforming to the `UIApplicationDelegate` and `NSApplicationDelegate` protocols. This now means that an author of an application is presented with two different, but ultimately needless, choices for an entrypoint: +UIKit and AppKit have fully embraced the `@main` attribute and have made +application adoption as simple as conforming to the `UIApplicationDelegate` and +`NSApplicationDelegate` protocols. This now means that an author of an +application is presented with two different, but ultimately needless, choices +for an entrypoint: -* Use hard coded framework-specific attributes -* Use the language’s general-purpose syntax for declaring an entrypoint +* Use one of the hard coded framework-specific attributes `@UIApplicationMain` +* or `@NSApplicationMain` Use the more general `@main` attribute -The right choice is clearly the latter. Moreover, having two functionally identical ways to express the concept of an app-specific entrypoint is clutter at best and confusing at worst. This proposal seeks to complete the migration work implied by [SE-0281](https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md) by having the compiler push Swift authors towards the more general, unified solution. +At runtime, the behavior of the `@main` attribute and the framework-specific +attributes is identical. Having two functionally identical ways to express the +concept of an app-specific entrypoint is clutter at best and confusing at worst. +This proposal seeks to complete the migration work implied by +[SE-0281](https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md) +by having the compiler push Swift authors towards the more general, unified +solution. ## Proposed solution -The use of both `@UIApplicationMain` and `@NSApplicationMain` under Swift 5 language modes will unconditionally warn and offer to replace these attributes with the appropriate conformances. In Swift 6 language modes, usage of these attributes will result in a hard error. +Using either `@UIApplicationMain` and `@NSApplicationMain` in a pre-Swift 6 +language mode will unconditionally warn and offer to replace these attributes +with the appropriate conformances. In Swift 6 language mode (and later), using +these attributes will result in a hard error. ## Detailed design -Because `@UIApplicationMain` and `@NSApplicationMain` have remarkably similar usages in abstract, this portion of the document will only use examples of `@UIApplicationMain` and assume that the case for `@NSApplicationMain` follows similarly. +> Because `@UIApplicationMain` and `@NSApplicationMain` are used in identical +> ways, this portion of the document will only discuss `@UIApplicationMain`. +> The design for `@NSApplicationMain` follows the exact same pattern. + +Framework-specific attributes were added to the language to automate the +boilerplate involved in declaring a standard application entrypoint. In UIKit +code, the entrypoint always ends with a call to `UIApplicationMain`. The last +parameter of this call is the name of a subclass of `UIApplicationDelegate`. +UIKit will search for and instantiate this delegate class so it can issue +application lifecycle callbacks. Swift, therefore, requires this attribute to +appear on a class that conforms to the `UIApplicationDelegate` protocol so it +can provide the name of that class to UIKit. + +But a conformance to `UIApplicationDelegate` comes with more than just lifecycle +callbacks. A default implementation of a `main` entrypoint is [provided for +free](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/3656306-main) +to a conforming type, but the `@UIApplicationMain` attribute suppresses it. This +fact is key to the migration path for existing users of the framework-specific +attribute. + +Under this proposal, when the compiler sees a use of `@UIApplicationMain`, it +will emit a diagnostic including a suggestion to replace the attribute with +`@main`. In Swift 6 and later language modes, this diagnostic will be an error; +otherwise it will be a warning. + +```swift +@UIApplicationMain // warning: '@UIApplicationMain' is deprecated in Swift 5 + // fixit: Change `@UIApplicationMain` to `@main` +final class MyApplication: UIResponder, UIApplicationDelegate { + /**/ +} +``` -Issue a diagnostic asking the user to remove `@UIApplicationMain`. The existing conformance to `UIApplicationDelegate` will kick in when the code is next built and the general-purpose entrypoint will be added instead. +Once the fixit has been applied, the result will be -``` -@UIApplicationMain // warning: '@UIApplicationMain' is deprecated in Swift 5 - // fixit: Change `@UIApplicationMain` to `@main` +```swift +@main final class MyApplication: UIResponder, UIApplicationDelegate { /**/ } ``` -We can get away with this simple remedy because -- A Swift module can have at most one of: @UIApplicationMain, @main, and @NSApplicationMain -- A class annotated with @UIApplicationMain or @NSApplicationMain must conform directly to the corresponding delegate protocol anyways. They therefore inherit the `main` entrypoint [provided by these protocols](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/3656306-main), they just aren't using it! +This simple migration causes the compiler to select the `main` entrypoint +inherited by the conformance to `UIApplicationDelegate`. No further source +changes are required. ## Source compatibility -This proposal is intentionally source-breaking, but that breakage is relegated to a new language mode and the diagnostics involved provide a clear and simple migration path for existing usages of these entrypoint attributes. +Current Swift libraries will continue to build becuase they compile under +pre-Swift 6 language modes. Under such language modes this proposal adds only an +unconditional warning when framework-specific entrypoints are used, and provides +diagnostics to avoid the warning by automatically migrating user code. + +In Swift 6 and later modes, this proposal is intentionally source-breaking as the +compiler will issue an unconditional error upon encountering a framework-specific +attribute. This source break will occur primarily in older application code, as +most libraries and packages do not use framework-specific attributes to define a main +entrypoint. Newer code, including templates for applications provided by Xcode 14 +and later, already use the `@main` attribute. ## Effect on ABI stability @@ -53,7 +114,3 @@ This proposal has no impact on ABI. ## Effect on API resilience None. - -## Alternatives considered - -We could choose not to do this. From 16ef90e00db779ba15c9c2f4f526e00c5324a89c Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 3 Jan 2023 22:03:12 -0500 Subject: [PATCH 2866/4563] Assign SE-0383 and put in review --- ...recate-uiapplicationmain-and-nsapplicationmain.md} | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) rename proposals/{NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md => 0383-deprecate-uiapplicationmain-and-nsapplicationmain.md} (92%) diff --git a/proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md b/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md similarity index 92% rename from proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md rename to proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md index 814479ae9c..aec1711c0d 100644 --- a/proposals/NNNN-deprecate-uiapplicationmain-and-nsapplicationmain.md +++ b/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md @@ -1,9 +1,11 @@ # Deprecate @UIApplicationMain and @NSApplicationMain -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0383](0383-deprecate-uiapplicationmain-and-nsapplicationmain.md) * Authors: [Robert Widmann](https://github.com/codafi) -* Review Manager: TBD -* Status: **[Implemented](https://github.com/apple/swift/pull/62151)** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (January 3rd...16th, 2023)** +* Implementation: [PR 62151](https://github.com/apple/swift/pull/62151) +* Review: ([pitch](https://forums.swift.org/t/deprecate-uiapplicationmain-and-nsapplicationmain/61493)) ## Introduction @@ -16,9 +18,6 @@ duplication in the language. This proposal seeks to deprecate these alternative entrypoint attributes in favor of `@main` in pre-Swift 6, and it makes their use in Swift 6 a hard error. -Swift-evolution thread: [Discussion thread topic for that -proposal](https://forums.swift.org/t/deprecate-uiapplicationmain-and-nsapplicationmain/) - ## Motivation UIKit and AppKit have fully embraced the `@main` attribute and have made From 333b7d7f37651b07c3a178ce0bcb1cccc161733b Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 3 Jan 2023 22:06:42 -0500 Subject: [PATCH 2867/4563] Link SE-0383 to its review thread --- .../0383-deprecate-uiapplicationmain-and-nsapplicationmain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md b/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md index aec1711c0d..174c8e1d8e 100644 --- a/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md +++ b/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md @@ -5,7 +5,7 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (January 3rd...16th, 2023)** * Implementation: [PR 62151](https://github.com/apple/swift/pull/62151) -* Review: ([pitch](https://forums.swift.org/t/deprecate-uiapplicationmain-and-nsapplicationmain/61493)) +* Review: ([pitch](https://forums.swift.org/t/deprecate-uiapplicationmain-and-nsapplicationmain/61493)) ([review](https://forums.swift.org/t/se-0383-deprecate-uiapplicationmain-and-nsapplicationmain/62375)) ## Introduction From dd11f933547771a154b644c4207b36c1c7927e2c Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 3 Jan 2023 23:33:09 -0500 Subject: [PATCH 2868/4563] Update SE-0382 to extend review (#1900) --- proposals/0382-expression-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index 06ff99571e..021206dcf5 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -3,7 +3,7 @@ * Proposal: [SE-0382](0382-expression-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (December 16...30, 2022)** +* Status: **Active review (December 16, 2022...January 15, 2023)** * Implementation: Partial implementation is available in `main` under the experimental feature flag `Macros`. An [example macro repository](https://github.com/DougGregor/swift-macro-examples) provides a way to experiment with this feature. * Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861/16)) ([review](https://forums.swift.org/t/se-0382-expression-macros/62090)) From 16f50aa400f9752b72f56e67a313d6008e1bd8ca Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 4 Jan 2023 01:18:43 -0500 Subject: [PATCH 2869/4563] Fix typo, nearby editorial changes --- ...ecate-uiapplicationmain-and-nsapplicationmain.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md b/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md index 174c8e1d8e..35e60c1a30 100644 --- a/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md +++ b/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md @@ -21,16 +21,17 @@ in Swift 6 a hard error. ## Motivation UIKit and AppKit have fully embraced the `@main` attribute and have made -application adoption as simple as conforming to the `UIApplicationDelegate` and -`NSApplicationDelegate` protocols. This now means that an author of an +adoption by applications as simple as conforming to the `UIApplicationDelegate` +and `NSApplicationDelegate` protocols. This now means that an author of an application is presented with two different, but ultimately needless, choices for an entrypoint: -* Use one of the hard coded framework-specific attributes `@UIApplicationMain` -* or `@NSApplicationMain` Use the more general `@main` attribute +* use one of the hard coded framework-specific attributes `@UIApplicationMain` or `@NSApplicationMain`, or +* use the more general `@main` attribute. -At runtime, the behavior of the `@main` attribute and the framework-specific -attributes is identical. Having two functionally identical ways to express the +At runtime, the behavior of the `@main` attribute on classes that conform to +one of the application delegate protocols above is identical to the corresponding +framework-specific attribute. Having two functionally identical ways to express the concept of an app-specific entrypoint is clutter at best and confusing at worst. This proposal seeks to complete the migration work implied by [SE-0281](https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md) From 4e87f6510649e7eb6171446ff77e4e0f49a650b3 Mon Sep 17 00:00:00 2001 From: Nuri Amari Date: Fri, 16 Dec 2022 14:26:59 -0800 Subject: [PATCH 2870/4563] Create NNNN-importing-forward-declared-objc-interfaces-and-protocols.md --- ...-declared-objc-interfaces-and-protocols.md | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 proposals/NNNN-importing-forward-declared-objc-interfaces-and-protocols.md diff --git a/proposals/NNNN-importing-forward-declared-objc-interfaces-and-protocols.md b/proposals/NNNN-importing-forward-declared-objc-interfaces-and-protocols.md new file mode 100644 index 0000000000..7cab1787d1 --- /dev/null +++ b/proposals/NNNN-importing-forward-declared-objc-interfaces-and-protocols.md @@ -0,0 +1,243 @@ +# Importing Forward Declared Objective-C Interfaces and Protocols + +* Proposal: [SE-NNNN](NNNN-importing-forward-declared-objc-interfaces-and-protocols.md) +* Authors: [Nuri Amari](https://github.com/NuriAmari) +* Review Manager: TBD +* Implementation: https://github.com/apple/swift/pull/61606 + +## Introduction + +This proposal seeks to improve the usability of existing Objective-C libraries from Swift by reducing the negative +impact forward declarations have on API visibility from Swift. We wish to start synthesizing placeholder types to +represent forward declared Objective-C interfaces and protocols in Swift. + +Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/pitch-importing-forward-declared-objective-c-classes-and-protocols/61926) + +## Motivation + +Forward declarations are very common in many existing Objective-C code bases, used often to break cyclic dependencies or to improve build performance. +Unfortunately, when it comes to "importability" into Swift they are quite detrimental. + +As it stands, the ClangImporter will fail to import any declaration that references a forward declared type in many common cases. This means a single +forward declared type can render larger portions of an Objective-C API unusable from Swift. For example, the following Objective-C API from the implementation PR +is empty from the Swift perspective (Swift textual interface generated via swift-ide-test): + +_Objective-C_ +```objective-c +#import + +@class ForwardDeclaredInterface; +@protocol ForwardDeclaredProtocol; + +@interface IncompleteTypeConsumer1 : NSObject +@property id propertyUsingAForwardDeclaredProtocol1; +@property ForwardDeclaredInterface *propertyUsingAForwardDeclaredInterface1; +- (id)init; +- (NSObject *)methodReturningForwardDeclaredProtocol1; +- (ForwardDeclaredInterface *)methodReturningForwardDeclaredInterface1; +- (void)methodTakingAForwardDeclaredProtocol1: + (id)param; +- (void)methodTakingAForwardDeclaredInterface1: + (ForwardDeclaredInterface *)param; +@end + +ForwardDeclaredInterface *CFunctionReturningAForwardDeclaredInterface1(); +void CFunctionTakingAForwardDeclaredInterface1( + ForwardDeclaredInterface *param); + +NSObject *CFunctionReturningAForwardDeclaredProtocol1(); +void CFunctionTakingAForwardDeclaredProtocol1( + id param); +``` + +_Swift_ +```swift +class IncompleteTypeConsumer1 : NSObject { + init!() +} +``` +It is possible to fix such issues by importing the definition of the type, either in the Objective-C header or in the consuming Swift file. However, such manual changes make consuming existing libraries more difficult and needlessly increase build times. We wish to make the experience of consuming an Objective-C API with forward declarations from Swift more consistent with the experience of consuming the API from Objective-C. + +We have done some work in the past to help diagnose such failures in the ClangImporter: + +https://forums.swift.org/t/pitch-improved-clangimporter-diagnostics/52687/17 + +https://forums.swift.org/t/pitch-lazy-clangimporter-diagnostics-enabled-by-default/54651 + +We want to build on this work, specifically with respect to forward declarations, this time +fixing a shortcoming of the ClangImporter, not just diagnosing it. + +## Proposed solution + +We propose the following representation for forward declared Objective-C interfaces and protocols in Swift: + +```swift +// @class Foo turns into +@available(*, unavailable, message: “This Objective-C class has only been forward declared; import its owning module to use it”) +class Foo : NSObject {} + +// @protocol Bar turns into +@available(*, unavailable, message: “This Objective-C protocol has only been forward declared; import its owning module to use it”) +protocol Bar : NSObjectProtocol {} +``` + +The idea is to introduce the minimal change that will make Objective-C APIs usable in a predictable safe manner. + +The aforementioned Objective-C API with this change looks like this from Swift: + +```swift +@available(*, unavailable, message: "This Objective-C class has only been forward-declared; import its owning module to use it") +class ForwardDeclaredInterface { +} +@available(*, unavailable, message: "This Objective-C protocol has only been forward-declared; import its owning module to use it") +protocol ForwardDeclaredProtocol : NSObjectProtocol { +} +class IncompleteTypeConsumer1 : NSObject { + var propertyUsingAForwardDeclaredProtocol1: ForwardDeclaredProtocol! + var propertyUsingAForwardDeclaredInterface1: ForwardDeclaredInterface! + init!() + func methodReturningForwardDeclaredProtocol1() -> ForwardDeclaredProtocol! + func methodReturningForwardDeclaredInterface1() -> ForwardDeclaredInterface! + func methodTakingAForwardDeclaredProtocol1(_ param: ForwardDeclaredProtocol!) + func methodTakingAForwardDeclaredInterface1(_ param: ForwardDeclaredInterface!) +} +func CFunctionReturningAForwardDeclaredInterface1() -> ForwardDeclaredInterface! +func CFunctionTakingAForwardDeclaredInterface1(_ param: ForwardDeclaredInterface!) +func CFunctionReturningAForwardDeclaredProtocol1() -> ForwardDeclaredProtocol! +func CFunctionTakingAForwardDeclaredProtocol1(_ param: ForwardDeclaredProtocol!) +``` + +More usage examples can be found in these tests introduced here: https://github.com/apple/swift/pull/61606 + +## Detailed design + +Modifications lie almost exclusively in `ClangImporter` -- specifically in `SwiftDeclConverter`. If asked to convert a +`clang::ObjCInterfaceDecl` or `clang::ObjCProtocolDecl` with no definition `SwiftDeclConverter` will now return a placeholder type instead of +bailing. These placeholder types are as described: + +```swift +// @class Foo turns into +@available(*, unavailable, message: “This Objective-C class has only been forward declared; import its owning module to use it”) +class Foo : NSObject {} + +// @protocol Bar turns into +@available(*, unavailable, message: “This Objective-C protocol has only been forward declared; import its owning module to use it”) +protocol Bar : NSObjectProtocol {} +``` + +Permitted usages of these types are intentionally limited. You will be able to use Objective-C and C declarations that refer to these types without issue. +You will be able to pass around instances of these incomplete types from Swift to Objective-C and vice versa. + +You’ll also be able to call the set of methods provided by `NSObject` or `NSObjectProtocol` on instances. Notably this assumes +that the backing Objective-C implementation does indeed inherit from / conform to `NSObject`. We are under the impression that +inheriting from `NSObject` is a requirement for Swift - Objective-C interop, but are not sure about protocols conforming to `NSObject`. +We could use some input on these assumptions, and will remove this feature if they turn out to be unsound. + +Using the type itself directly in Swift is forbidden by the attached unavailable attribute. This is to keep the impact of the change small and to prevent unsound declarations, +such as declaring a new Swift class that inherits from or conforms to such a type. You will also not be able to create new instances of these types in Swift. + +To make these limitations more intuitive to users, a new diagnostic has been added. This diagnostic helps guide users when they attempt to access a member on the incomplete synthesized +type, expecting the complete type. The new diagnostics are shown in the last 4 lines of this example: + +```swift +swift-client-fwd-declared.swift:6:7: error: value of type 'Foo' has no member 'sayHello' +myFoo.sayHello() +~~~~~ ^~~~~~~~ +:0: note: class 'Foo' is a placeholder for a forward declared Objective-C interface and may be missing members; import the definition to access the complete interface +foo-bar-consumer.h:3:1: note: interface 'Foo' forward declared here +@class Foo; +^ +``` + +The feature is gated behind a new frontend flag `-enable-import-objc-forward-declarations`. This flag is on by default for Swift version 6 and onwards. + +The flag is always disabled in the REPL, as allowing it currently leads to confusing behavior. In the REPL, the environment in terms of whether a complete definition of some type Foo is available can change during execution. These examples show some of the confusing behavior: + +```swift +import IncompleteFoo +let foo = FunctionReturningFoo() +FunctingTakingFoo(foo) +import CompleteFoo // This has no effect as far as Swift's view of type 'Foo' is concerned +let completeFoo = Foo() // error type 'IncompleteFoo.Foo' has no initializer +foo.methodOnCompleteFoo() // error no member 'methodOnCompleteFoo' on type 'IncompleteFoo.Foo' +``` + +```swift +import IncompleteFoo +import CompleteFoo +let completeFoo = Foo() // This time it works because the cache was not populated until after CompleteFoo was imported +foo.methodOnCompleteFoo() // Works as well +``` + +This happens because `ClangImporter` is lazy and caches imports, once the placeholder is synthesized (when only the incomplete definition is +available), the complete definition will never be imported. Without an understanding of `ClangImporter`, it is impossible to understand why +the statements between the two imports have an effect on those after. We have made attempts at solving these issues, but no satisfactory solution +was found. The details of these attempts are listed in the alternatives considered section. + +The [existing ClangImporter diagnostics](https://forums.swift.org/t/pitch-improved-clangimporter-diagnostics/52687) will still properly warn users about forward declaration limitations, with or with out the feature enabled. + +## Source compatibility + +This change does have the potential to break source compatibility, since +`ClangImporter` will now import declarations that were previously dropped. +That said, the impact should rare. The current implementation already passes +the source compatibility suite and the source kit stress test. + +As mentioned, to address source compatibility issues, the feature is gated behind +a flag for Swift 5 and before, and enabled by default in Swift 6. + +## Effect on ABI stability + +This change has no affect on ABI. + +## Effect on API resilience + +This change does not influence the ABI characteristics +of public APIs. + +## Alternatives considered + +The unavailable attribute attached to synthesized declarations might be more restrictive than necessary. +We considered the introduction of a new attribute to denote an incomplete type. This attribute would be used to prevent +operations that are not suitable for an incomplete type (ie. declaring a Swift type that conforms to an incomplete protocol). + +Allowing more use of the type however increases the complexity of the problem, and the likelihood of source compatibility issues. +For example, if the synthesized type is not file private, issues could arise with different files having different representations +of the type depending on which modules they import. + +We also attempted to make this implementation usable from the REPL. As mentioned, we had issues with `ClangImporter` caching +imports. We must invalidate the cache, at the very least to allow the new definition of type 'Foo' to be imported. This creates issues +where two incompatible representations of the type (`IncompleteFoo.Foo` and `CompleteFoo.Foo`) are in context at the same time. +To solve these issues we attempted the following: + +1. Invalidate all direct or indirect references to `IncompleteFoo.Foo` and replace them with `CompleteFoo.Foo`: + +Issues: + +- We do not know how to safely patch the type of existing instances at runtime (ex: local variable `foo` in the snippet above) +- Any imported declarations that depended upon a type, whose definition was incomplete at the time of import but is now complete, need reimporting + - We need new machinery in `ClangImporter` to support smart cache invalidation like this + - For all cached declarations, we need to keep track of the "completeness" of all types we depend on at the time of caching + - A simple cache invalidation heuristic such as "don't cache anything that depends upon an incomplete type" won't work either + - Besides the significant performance cost, the typechecker compares type declarations using pointer equality, expansion of the typechecker to recognize two imports of the same Clang declaration as equivalent would be required + +2. Convince the typechecker that `IncompleteFoo.Foo` and `CompleteFoo.Foo` are the same, *in all usages*: + +Issues: + +- It seems achievable to teach the typechecker that `IncompleteFoo.Foo` and `CompleteFoo.Foo` can be implicitly converted between one another, but that is not enough + - Any typechecking contraint that `CompleteFoo.Foo` satisfies must also be satisfied by `IncompleteFoo.Foo`. + - It might be possible to "inject" an extension into the REPL's context that adds any required methods, computed properties and protocol conformances to `IncompleteFoo.Foo`. However, it is not possible to add new inheritances. + +We believe that given these issues, it is better to disable the feature in the REPL entirely rather than provide a confusing experience. We believe that this +proposal should advance in some form regardless. Forward declarations are a huge problem for consumers of large Objective-C codebases looking to transition to Swift. +The LLDB / REPL experience already diverges from the compiled experience, is relatively niche, and we are not making the REPL experience any worse. Furthermore, +unlike in large scale projects, the existing solution of "import the definition" is perfectly reasonable for small REPL sessions. If this divergence between the +REPL and compiled experience is unacceptable, we think it could be reasonable to gate the feature behind a flag. This flag could potentially also be enabled in +the REPL, but with appropriate warnings and "at your own risk". + +That said, it would be great if this feature could come to the REPL eventually, but the REPL should not stand in the way of progress in the compiled experience. + +## Acknowledgments + +Thank you to @drodriguez and [@rmaz](https://github.com/rmaz) for helping develop the idea. From 813b82c471f2d84b286c699122d83aacd003746b Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 4 Jan 2023 14:34:05 -0800 Subject: [PATCH 2871/4563] Assign as SE-0384 and update review headers --- ...-forward-declared-objc-interfaces-and-protocols.md} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename proposals/{NNNN-importing-forward-declared-objc-interfaces-and-protocols.md => 0384-importing-forward-declared-objc-interfaces-and-protocols.md} (97%) diff --git a/proposals/NNNN-importing-forward-declared-objc-interfaces-and-protocols.md b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md similarity index 97% rename from proposals/NNNN-importing-forward-declared-objc-interfaces-and-protocols.md rename to proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md index 7cab1787d1..9d57de21d8 100644 --- a/proposals/NNNN-importing-forward-declared-objc-interfaces-and-protocols.md +++ b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md @@ -1,9 +1,11 @@ # Importing Forward Declared Objective-C Interfaces and Protocols -* Proposal: [SE-NNNN](NNNN-importing-forward-declared-objc-interfaces-and-protocols.md) -* Authors: [Nuri Amari](https://github.com/NuriAmari) -* Review Manager: TBD +* Proposal: [SE-0384](0384-importing-forward-declared-objc-interfaces-and-protocols.md) +* Author: [Nuri Amari](https://github.com/NuriAmari) +* Review Manager: Tony Allevato (https://github.com/allevato) +* Status: **Active review (January 4, 2023...January 18, 2023)** * Implementation: https://github.com/apple/swift/pull/61606 +* Review: ([pitch](https://forums.swift.org/t/pitch-importing-forward-declared-objective-c-classes-and-protocols/61926)) ## Introduction @@ -11,8 +13,6 @@ This proposal seeks to improve the usability of existing Objective-C libraries f impact forward declarations have on API visibility from Swift. We wish to start synthesizing placeholder types to represent forward declared Objective-C interfaces and protocols in Swift. -Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/pitch-importing-forward-declared-objective-c-classes-and-protocols/61926) - ## Motivation Forward declarations are very common in many existing Objective-C code bases, used often to break cyclic dependencies or to improve build performance. From e94e24dea2b77c8b4d249ddd9241826df2b9572e Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 4 Jan 2023 14:39:44 -0800 Subject: [PATCH 2872/4563] [SE-0384] Add link to review thread --- ...-importing-forward-declared-objc-interfaces-and-protocols.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md index 9d57de21d8..3d359328a9 100644 --- a/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md +++ b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md @@ -5,7 +5,7 @@ * Review Manager: Tony Allevato (https://github.com/allevato) * Status: **Active review (January 4, 2023...January 18, 2023)** * Implementation: https://github.com/apple/swift/pull/61606 -* Review: ([pitch](https://forums.swift.org/t/pitch-importing-forward-declared-objective-c-classes-and-protocols/61926)) +* Review: ([pitch](https://forums.swift.org/t/pitch-importing-forward-declared-objective-c-classes-and-protocols/61926)) ([review](https://forums.swift.org/t/se-0384-importing-forward-declared-objective-c-interfaces-and-protocols/62392)) ## Introduction From 018be97cbe10fe88569cd2b2f42860d8f7b9cbe1 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 4 Jan 2023 20:06:37 -0500 Subject: [PATCH 2873/4563] Update 0374-clock-sleep-for.md (#1905) --- proposals/0374-clock-sleep-for.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0374-clock-sleep-for.md b/proposals/0374-clock-sleep-for.md index 7b7e8b89e5..417238c812 100644 --- a/proposals/0374-clock-sleep-for.md +++ b/proposals/0374-clock-sleep-for.md @@ -3,7 +3,7 @@ * Proposal: [SE-0374](0374-clock-sleep-for.md) * Authors: [Brandon Williams](https://github.com/mbrandonw), [Stephen Celis](https://github.com/stephencelis) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active Review (October 11 ... November 22, 2022)** +* Status: **Implemented (Swift 5.8)** * Implementation: [apple/swift#61222](https://github.com/apple/swift/pull/61222) * Review: ([pitch](https://forums.swift.org/t/pitch-clock-sleep-for/60376)) ([review](https://forums.swift.org/t/se-0374-add-sleep-for-to-clock/60787)) From 0b4114f8569e9ad1033416e21337abf20107f3e6 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 4 Jan 2023 20:07:35 -0500 Subject: [PATCH 2874/4563] Update 0374-clock-sleep-for.md (#1906) --- proposals/0374-clock-sleep-for.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0374-clock-sleep-for.md b/proposals/0374-clock-sleep-for.md index 417238c812..9bd6d1fc79 100644 --- a/proposals/0374-clock-sleep-for.md +++ b/proposals/0374-clock-sleep-for.md @@ -5,7 +5,7 @@ * Review Manager: [Steve Canon](https://github.com/stephentyrone) * Status: **Implemented (Swift 5.8)** * Implementation: [apple/swift#61222](https://github.com/apple/swift/pull/61222) -* Review: ([pitch](https://forums.swift.org/t/pitch-clock-sleep-for/60376)) ([review](https://forums.swift.org/t/se-0374-add-sleep-for-to-clock/60787)) +* Review: ([pitch](https://forums.swift.org/t/pitch-clock-sleep-for/60376)) ([review](https://forums.swift.org/t/se-0374-add-sleep-for-to-clock/60787)) ([acceptance](https://forums.swift.org/t/accepted-se-0374-add-sleep-for-to-clock/62148)) ## Introduction From c42a47e9e631a5cc8cf89567f3bfa799d0dc5011 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 4 Jan 2023 17:33:40 -0800 Subject: [PATCH 2875/4563] [SE-0382] Updates based on review feedback (#1904) * [SE-0382] Make `MacroExpansionContext` a class * [SE-0382] Generalize grammar to reflect the implementation * [SE-0382] Allow macro parameters to have default arguments * [SE-0382] Clarify that macro expansion cannot be recursive * [SE-0382] Rename `createUniqueLocalName` to `createUniqueName` --- proposals/0382-expression-macros.md | 39 +++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index 021206dcf5..d9cd127686 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -11,8 +11,6 @@ Expression macros provide a way to extend Swift with new kinds of expressions, which can perform arbitary syntactic transformations on their arguments to produce new code. Expression macros make it possible to extend Swift in ways that were only previously possible by introducing new language features, helping developers build more expressive libraries and eliminate extraneous boilerplate. -Swift-evolution thread: - ## Motivation Expression macros are one part of the [vision for macros in Swift](https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900), which lays out general motivation for introducing macros into the language. Expressions in particular are an area where the language already provides decent abstractions for factoring out runtime behavior, because one can create a function that you call as an expression from anywhere. However, with a few hard-coded exceptions like `#file` and `#line`, an expression cannot reason about or modify the source code of the program being compiled. Such use cases will require external source-generating tools, which don't often integrate cleanly with other tooling. @@ -92,7 +90,7 @@ public protocol ExpressionMacro: Macro { /// Expand a macro described by the given macro expansion expression /// within the given context to produce a replacement expression. static func expansion( - of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext + of node: MacroExpansionExprSyntax, in context: MacroExpansionContext ) throws -> ExprSyntax } ``` @@ -112,7 +110,7 @@ import _SwiftSyntaxMacros public struct StringifyMacro: ExpressionMacro { public static func expansion( - of node: MacroExpansionExprSyntax, in context: inout MacroExpansionContext + of node: MacroExpansionExprSyntax, in context: MacroExpansionContext ) -> ExprSyntax { guard let argument = node.argumentList.first?.expression else { fatalError("compiler bug: the macro does not have any arguments") @@ -152,16 +150,18 @@ macro-signature -> ':' type macro-function-signature-result -> '->' type -macro-definition -> '=' macro-expansion-expression +macro-definition -> '=' expression ``` The signature of a macro is either function-like (`(_ argument: T) -> (T, String)`) or value-like (`: Int`), depending on the form of the `macro-signature`. The `@expression` attribute applies only to macros. It indicates that the macro is an expression macro. Macros can only be declared at file scope. They can be overloaded in the same way as functions, so long as the argument labels, parameter types, or result type differ. -The `macro-definition` provides the implementation used to expand the macro. It is always a macro expansion expression, so all non-builtin macros are defined in terms of other macros, terminating in a builtin macro whose definition is provided by the compiler. The arguments provided within the `macro-expansion-expression` of the macro definition must either be direct references to the parameters of the enclosing macro or must be literals. The `macro-expansion-expression` is type-checked (to ensure that the argument and result types make sense), but no expansion is performed at the time of definition. Rather, expansion of the macro referenced by the `macro-definition` occurs when the macro being declared is expanded. See the following section on macro expansions for more information. +The `macro-definition` provides the implementation used to expand the macro. It is parsed as a general expression, but must always be a `macro-expansion-expression`, so all non-builtin macros are defined in terms of other macros, terminating in a builtin macro whose definition is provided by the compiler. The arguments provided within the `macro-expansion-expression` of the macro definition must either be direct references to the parameters of the enclosing macro or must be literals. The `macro-expansion-expression` is type-checked (to ensure that the argument and result types make sense), but no expansion is performed at the time of definition. Rather, expansion of the macro referenced by the `macro-definition` occurs when the macro being declared is expanded. See the following section on macro expansion for more information. + +Macro result types cannot include opaque result types. -Macro result types cannot include opaque result types. Macro parameters cannot have default arguments. +Macro parameters may have default arguments, but those default arguments can only consist of literal expressions and other macro expansions. ### Macro expansion @@ -208,6 +208,8 @@ The second phase of macro expansions occurs outside-in. First, the `addBlocker` From an implementation perspective, the compiler reserves the right to avoid performing repeated type checking of the same macro arguments. For example, we type-checked `#stringify(1 + 2)` in the first phase of the expansion of `prohibitBinaryOperators`, and then again on the expanded result. When the compiler recognizes that the same syntax node is being re-used unmodified, it can re-use the types computed in the first phase. This is an important performance optimization for the type checker. +Macro expansion cannot be recursive: if the expansion of a given macro produces source code that expands that same macro, the program is ill-formed. This prevents unbounded macro expansion. + ### Macro implementation library Macro definitions will make use of the [swift-syntax](https://github.com/apple/swift-syntax) package, which provides the Swift syntax tree manipulation and parsing capabilities for Swift tools. The `SwiftSyntaxMacros` module will provide the functionality required to define macros. @@ -227,7 +229,7 @@ public protocol ExpressionMacro: Macro { /// Expand a macro described by the given macro expansion expression /// within the given context to produce a replacement expression. static func expansion( - of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext + of macro: MacroExpansionExprSyntax, in context: MacroExpansionContext ) throws -> ExprSyntax } ``` @@ -243,9 +245,9 @@ If the macro expansion cannot proceed for some reason, the `expansion(of:in:)` o The macro expansion context provides additional information about the environment in which the macro is being expanded. This context can be queried as part of the macro expansion: ```swift -/// System-supplied structure that provides information about the context in +/// System-supplied class that provides information about the context in /// which a given macro is being expanded. -public struct MacroExpansionContext { +public class MacroExpansionContext { /// The name of the module in which the macro is being expanded. public let moduleName: String @@ -256,16 +258,16 @@ public struct MacroExpansionContext { /// Create a new macro expansion context. public init(moduleName: String, fileName: String) - /// Generate a unique local name for use in the macro. - public mutating func createUniqueLocalName() -> TokenSyntax + /// Generate a unique name for use in the macro. + public func createUniqueName() -> TokenSyntax /// Emit a diagnostic (i.e., warning or error) that indicates a problem with the macro /// expansion. - public mutating func diagnose(_ diagnostic: Diagnostic) + public func diagnose(_ diagnostic: Diagnostic) } ``` -The `createUniqueLocalName()` function allows one to create new, unique names so that the macro expansion can produce new declarations that won't conflict with any other declarations in the same scope. It produces an identifier token containing the unique name. This allows macros to be more hygienic, by not introducing new names that could affect the way that the code provided via macro expansion arguments is type-checked. +The `createUniqueName()` function allows one to create new, unique names so that the macro expansion can produce new declarations that won't conflict with any other declarations in the same scope. It produces an identifier token containing the unique name. This allows macros to be more hygienic, by not introducing new names that could affect the way that the code provided via macro expansion arguments is type-checked. It is intended that `MacroExpansionContext` will grow over time to include more information about the build environment in which the macro is being expanded. For example, information about the target platform (such as OS, architecture, and deployment version) and any compile-time definitions passed via `-D`, should be included as part of the context. @@ -433,7 +435,7 @@ The `expansion(of:in:)` operation for an expression macro could be marked as `as ```swift static func expansion( - of macro: MacroExpansionExprSyntax, in context: inout MacroExpansionContext + of macro: MacroExpansionExprSyntax, in context: MacroExpansionContext ) async throws -> ExprSyntax ``` @@ -495,13 +497,18 @@ Expressions are just one place in the language where macros could be valuable. O ## Revision History +* Revisions based on review feedback: + * Make `MacroExpansionContext` a class, because the state involving diagnostics and unique names needs to be shared. + * Allow macro parameters to have default arguments, with restrictions on what can occur within a default argument. + * Clarify that macro expansion cannot be recursive. + * Rename `createUniqueLocalName` to `createUniqueName`; the names might not always be local in scope. + * Revisions from the second pitch: * Moved SwiftPM manifest changes to a separate proposal that can explore the building of macros in depth. This proposal will focus only on the language aspects. * Simplified the type signature of the `#externalMacro` built-in macro. * Added `@expression` to the macro to distinguish it from other kinds of macros that could come in the future. * Make `expansion(of:in:)` throwing, and have that error be reported back to the user. * Expand on how the various builtin standard library macros will work. - * Revisions from the first pitch: * Rename `MacroEvaluationContext` to `MacroExpansionContext`. * Remove `MacroResult` and instead allow macros to emit diagnostics via the macro expansion context. From a9c40ee83e8468ef4eeb5fc5c00bbb95ba2683a8 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Thu, 5 Jan 2023 12:26:23 -0800 Subject: [PATCH 2876/4563] Amend SE-0378 (#1902) - Per review feedback, provide a way for user to override default credential store and use netrc file instead. (`--netrc`) - Replace `.netrc` with "netrc file" since the filename doesn't have to be `.netrc`. - Update `login` subcommand outputs to match implementation. --- proposals/0378-package-registry-auth.md | 103 +++++++++++++++++------- 1 file changed, 75 insertions(+), 28 deletions(-) diff --git a/proposals/0378-package-registry-auth.md b/proposals/0378-package-registry-auth.md index 2759381ed2..2913970027 100644 --- a/proposals/0378-package-registry-auth.md +++ b/proposals/0378-package-registry-auth.md @@ -46,7 +46,7 @@ registry credentials. Log in to a package registry. SwiftPM will verify the credentials using the registry service's [`login` API](#login-api). If it returns a successful response, credentials will be persisted to the operating system's -credential store if supported, or the user-level `.netrc` file otherwise. +credential store if supported, or the user-level netrc file otherwise. The user-level configuration file located at `~/.swiftpm/configuration/registries.json` will also be updated. @@ -59,8 +59,9 @@ OPTIONS: --token Access token - --no-confirm Allow writing to .netrc file without confirmation - --netrc-file Specify the .netrc file path + --no-confirm Allow writing to netrc file without confirmation + --netrc-file Specify the netrc file path + --netrc Use netrc file even in cases where other credential stores are preferred ``` `url` should be the registry's base URL (e.g., `https://example-registry.com`). @@ -87,9 +88,12 @@ option as required or make sure the secret is present in credential storage. If the operating system's credential store is not supported, the tool will prompt user for confirmation before writing credentials -to the less secured `.netrc` file. Use `--no-confirm` to disable +to the less secured netrc file. Use `--no-confirm` to disable this confirmation. +To force usage of netrc file instead of the operating system's +credential store, pass the `--netrc` flag. + ##### Example: basic authentication (macOS, interactive) ```console @@ -97,7 +101,8 @@ this confirmation. --username jappleseed Enter password for 'jappleseed': -Login successful. Credentials have been saved to the operating system's secure credential store. +Login successful. +Credentials have been saved to the operating system's secure credential store. ``` An entry for `example-registry.com` would be added to Keychain. @@ -126,14 +131,54 @@ Enter password for 'jappleseed': Login successful. -WARNING: Secure credential storage is not supported on this platform. -Your credentials will be written out to .netrc. -Continue? (Y/N): Y +WARNING: Secure credential store is not supported on this platform. +Your credentials will be written out to netrc file. +Continue? (Yes/No): Yes -Credentials have been saved to .netrc. +Credentials have been saved to netrc file. ``` -An entry for `example-registry.com` would be added to the `.netrc` file: +An entry for `example-registry.com` would be added to the netrc file: + +``` +machine example-registry.com +login jappleseed +password alpine +``` + +`registries.json` would be updated to indicate that `example-registry.com` +requires basic authentication: + +```json +{ + "authentication": { + "example-registry.com": { + "type": "basic" + }, + ... + }, + ... +} +``` + +##### Example: basic authentication (use netrc file instead of operating system's credential store, interactive) + +```console +> swift package-registry login https://example-registry.com \ + --username jappleseed + --netrc +Enter password for 'jappleseed': + +Login successful. + +WARNING: You choose to use netrc file instead of the operating system's secure credential store. +Your credentials will be written out to netrc file. +Continue? (Yes/No): Yes + +Credentials have been saved to netrc file. +``` + +An entry for `example-registry.com` would be added to the netrc file: ``` machine example-registry.com @@ -164,10 +209,11 @@ requires basic authentication: --password alpine \ --no-confirm -Login successful. Credentials have been saved to .netrc. +Login successful. +Credentials have been saved to netrc file. ``` -An entry for `example-registry.com` would be added to the `.netrc` file: +An entry for `example-registry.com` would be added to the netrc file: ``` machine example-registry.com @@ -200,10 +246,11 @@ requires basic authentication: --password alpine \ --no-confirm -Login successful. Credentials have been saved to .netrc. +Login successful. +Credentials have been saved to netrc file. ``` -An entry for `example-registry.com` would be added to the `.netrc` file: +An entry for `example-registry.com` would be added to the netrc file: ``` machine example-registry.com @@ -235,7 +282,7 @@ requires basic authentication: ``` An entry for `example-registry.com` would be added to the operating -system's credential store if supported, or the user-level `.netrc` +system's credential store if supported, or the user-level netrc file otherwise: ``` @@ -265,7 +312,7 @@ Log out from a registry. Credentials are removed from the operating system's credential store if supported, and the user-level configuration file (`registries.json`). -To avoid accidental removal of sensitive data, `.netrc` file needs to be +To avoid accidental removal of sensitive data, netrc file needs to be updated manually by the user. ```manpage @@ -304,7 +351,7 @@ entry in this dictionary. Credentials are to be specified in the native credential store of the operating system if supported, otherwise in the user-level -`.netrc` file. (Only macOS Keychain will be supported in the +netrc file. (Only macOS Keychain will be supported in the initial feature release; more might be added in the future.) See [credential storage](#credential-storage) for more details on configuring @@ -315,7 +362,7 @@ credentials for each authentication type. SwiftPM will always use the most secure way to handle credentials on the platform. In general, this would mean using the operating system's credential store (e.g., Keychain on macOS). It falls -back to `.netrc` only if there is no other solution available. +back to netrc file only if there is no other solution available. #### Basic Authentication @@ -325,9 +372,9 @@ Registry credentials should be stored as "Internet password" items in the macOS Keychain. The "item name" should be the registry URL, including `https://` (e.g., `https://example-registry.com`). -##### `.netrc` file (only if operating system's credential store is not supported) +##### netrc file (if operating system's credential store is not supported) -A `.netrc` entry for basic authentication looks as follows: +A netrc entry for basic authentication looks as follows: ``` machine example-registry.com @@ -335,8 +382,8 @@ login jappleseed password alpine ``` -By default, SwiftPM looks for `.netrc` file in the user's -home directory. A custom `.netrc` file can be specified using +By default, SwiftPM looks for netrc file in the user's +home directory. A custom netrc file can be specified using the `--netrc-file` option. #### Token Authentication @@ -345,7 +392,7 @@ User can configure access token for a registry as similarly done for basic authentication, but with `token` as the login/username and the access token as the password. -For example, a `.netrc` entry would look like: +For example, a netrc entry would look like: ``` machine example-registry.com @@ -355,8 +402,8 @@ password jappleseedstoken ### Additional changes in SwiftPM -1. Only the user-level `.netrc` file will be used. Project-level `.netrc` file will not be supported. -2. SwiftPM will perform lookups in one credential store only. For macOS, it will be Keychain. For all other platforms, it will be the user-level `.netrc` file. +1. Only the user-level netrc file will be used. Project-level netrc file will not be supported. +2. SwiftPM will perform lookups in one credential store only. For macOS, it will be Keychain. For all other platforms, it will be the user-level netrc file. 3. The `--disable-keychain` and `--disable-netrc` options will be removed. ### New package registry service API @@ -393,12 +440,12 @@ This proposal moves SwiftPM to use operating system's native credential store (e.g., macOS Keychain) on supported platforms, which should yield better security. -We are also eliminating the use of project-level `.netrc` file. This should -prevent accidental checkin of `.netrc` file and thus leakage of sensitive +We are also eliminating the use of project-level netrc file. This should +prevent accidental checkin of netrc file and thus leakage of sensitive information. ## Impact on existing packages -This proposal eliminates the project-level `.netrc` file. There should be +This proposal eliminates the project-level netrc file. There should be no other impact on existing packages. From cf5eb1142d49551907b4df2652e0842ce7f1c1cc Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 10 Jan 2023 16:25:48 -0800 Subject: [PATCH 2877/4563] Revisions based on discussions and more experimentation * Split peer/member/accessor macro implementations into separate protocols and attribute spellings, so the compiler can query them in a more fine-grained manner. * Removed function-body macros... for the moment. We'll come back to them. * Add example showing composition of different macro roles for the same macro to effect property-wrappers behavior. --- proposals/nnnn-declaration-macros.md | 396 ++++++++++++++++++++++----- 1 file changed, 332 insertions(+), 64 deletions(-) diff --git a/proposals/nnnn-declaration-macros.md b/proposals/nnnn-declaration-macros.md index 64463e9a86..6989a92170 100644 --- a/proposals/nnnn-declaration-macros.md +++ b/proposals/nnnn-declaration-macros.md @@ -48,10 +48,10 @@ Freestanding macros are the most like expression macros. For example, the `warni ```swift /// Emits the given message as a warning, as in SE-0196. -@declaration(.freestanding) macro warning(_ message: String) +@declaration(freestanding) macro warning(_ message: String) ``` -The `@declaration` attribute specifies that this is a declaration macro, which is also freestanding. Given this macro declaration, the syntax +The `@declaration` attribute specifies that this is a declaration macro, which is also freestanding (the `freestanding` argument). Given this macro declaration, the syntax ```swift #warning("unsupported configuration") @@ -100,62 +100,63 @@ public struct WarningMacro: FreestandingDeclarationMacro { ### Attached macros -Attached macros are named as such because they are attached to a specific declaration. They are written using attribute syntax (e.g., `@addCompletionHandler`), and are able to make specific additions to both the declaration to which they are attached as well as introducing "peer" declarations alongside the declaration to which they are attached. +Attached macros are named as such because they are attached to a specific declaration. They are written using attribute syntax (e.g., `@addCompletionHandler`), and are able to reason about the declaration to which they are attached. There are a number of different forms of attached macros, each of which has a specific effect such as adding members to the declaration (e.g., if it's a type or extension thereof), adding "peer" members alongside the declaration, adding accessors to the declaration, and so on. Each of these kinds of macros is described in the following sections. + +#### Peer declaration macros -For example, here is a declaration of a macro that introduces a completion-handler version of a given asynchronous function: +Peer declaration macros produce new declarations alongside the declaration to which they are attached. For example, here is a declaration of a macro that introduces a completion-handler version of a given asynchronous function: ```swift -@declaration(.attached, peers: [.overloaded]) macro addCompletionHandler +@declaration(peers: [.overloaded]) macro addCompletionHandler: Void ``` -Again, this macro uses the `@declaration` to indicate that it is a declaration macro, but with `.attached`. The `peers` argument specifies that this macro will generate peer declarations, and how the names of those peer declarations are formed. In this case, our macro will produce a peer that is overloaded with the declaration to which it is attached, i.e., it has the same base name. Later parts of this proposal will go into more depth on the naming of generated declarations , as well as providing rationale for this up-front declaration of macro behavior. +Again, this macro uses the `@declaration` attribute to indicate that it is a declaration macro. The `peers` argument specifies that this macro will generate peer declarations, and how the names of those peer declarations are formed. In this case, our macro will produce a peer that is overloaded with the declaration to which it is attached, i.e., it has the same base name. Later parts of this proposal will go into more depth on the naming of generated declarations , as well as providing rationale for this up-front declaration of macro behavior. -An attached macro like this can be used as an attribute. For example: +The macro can be used like this, as an attribute: ```swift @addCompletionHandler func fetchAvatar(_ username: String) async -> Image? { ... } ``` -Attached macros are implemented via types that conform to the `AttachedDeclarationMacro` protocol: +Peer declaration macros are implemented via types that conform to the `PeerDeclarationMacro` protocol: ```swift -public AttachedDeclarationMacro: DeclarationMacro { - /// Expand a macro described by the given custom attribute and - /// attached to the given declaration and evaluated within a - /// particular expansion context. +public PeerDeclarationMacro: DeclarationMacro { + /// Expand a macro described by the given custom attribute to + /// produce "peer" declarations of the declaration to which it + /// is attached. /// - /// The macro expansion can introduce a number of changes to - /// the given declaration, all of which must be represented by - /// the `AttachedDeclarationExpansion` result. + /// The macro expansion can introduce "peer" declarations that + /// go alongside the given declaration. static func expansion( of node: CustomAttributeSyntax, - attachedTo declaration: DeclSyntax, + peersOf declaration: DeclSyntax, in context: inout MacroExpansionContext - ) throws -> AttachedDeclarationExpansion + ) throws -> [DeclSyntax] } ``` -The effect of `addCompletionHandler` is to produce a new "peer" declaration with the same signature as the declaration it is attached but with `async` and the result type removed, and a completion handler argument added, e.g., +The effect of `addCompletionHandler` is to produce a new "peer" declaration with the same signature as the declaration it is attached to, but with `async` and the result type removed in favor of a completion handler argument, e.g., ```swift /// Expansion of the macro produces the following. -func fetchAvatar(_ username: String, completionHandler: @escaping (Image?) -> Void ) { +func fetchAvatar(_ username: String, completionHandler: @escaping (Image?) -> Void) { Task.detached { completionHandler(await fetchAvatar(username)) } } ``` -The actual implementation of this macro involves a lot of syntax manipulation, so we settle for a pseudo-code definition here: +The actual implementation of this macro involves a lot of syntax manipulation, so we settle for a pseudo-code definition here and leave the complete implementation to the appendix: ```swift -public struct AddCompletionHandler: AttachedDeclarationMacro { +public struct AddCompletionHandler: PeerDeclarationMacro { public static func expansion( of node: CustomAttributeSyntax, - attachedTo declaration: DeclSyntax, + peersOf declaration: DeclSyntax, in context: inout MacroExpansionContext - ) throws -> AttachedDeclarationExpansion { + ) throws -> [DeclSyntax] { // make sure we have an async function to start with // form a new function "completionHandlerFunc" by starting with that async function and // - remove async @@ -163,12 +164,14 @@ public struct AddCompletionHandler: AttachedDeclarationMacro { // - add a completion-handler parameter // - add a body that forwards arguments // return the new peer function - return AttachedDeclarationExpansion(peers: [completionHandlerFunc]) + return [completionHandlerFunc] } } ``` -The full capabilities of `AttachedDeclarationExpansion` will be described later, and are expected to expand over time. However, another common use case involves creating member declarations within a type. For example, to define static members to ease the definition of an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset). Given: +#### Member declaration macros + +Member declaration macros allow one to introduce new members into the type or extension to which the macro is attached. For example, we can write a macro that defines static members to ease the definition of an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset). Given: ```swift @optionSetMembers @@ -201,16 +204,33 @@ struct MyOptions: OptionSet { } ``` -The macro itself will be declared as an attached declaration macro that defines an arbitrary set of members: +The macro itself will be declared as a member declaration macro that defines an arbitrary set of members: ```swift /// Create the necessary members to turn a struct into an option set. -@declaration(.attached, members: [.named("rawValue"), .arbitrary]) macro optionSetMembers +@declaration(members: [.named("rawValue"), .arbitrary]) macro optionSetMembers: Void ``` The `members` argument specifies that this macro will be defining new members of the declaration to which it is attached. In this case, while the macro knows it will define a member named `rawValue`, there is no way for the macro to predict the names of the static properties it is defining, so it also specifies `.arbitrary` to indicate that it will introduce members with arbitrarily-determined names. -As a final example, property-wrapper-like behavior can be implemented via an attached declaration macro that introduces accessors. Consider a macro that can be applied to a stored property to instead access a dictionary keyed by the property name. Such a macro could be used like this: +Member declaration macros are implemented with types that conform to the `MemberDeclarationMacro` protocol: + +```swift +protocol MemberDeclarationMacro: DeclarationMacro { + /// Expand a macro described by the given custom attribute to + /// produce additional members of the given declaration to which + /// the attribute is attached. + static func expansion( + of node: CustomAttributeSyntax, + membersOf declaration: DeclSyntax, + in context: inout MacroExpansionContext + ) throws -> [DeclSyntax] +} +``` + +#### Accessor macros + +Accessor macros allow a macro to add accessors to a property or subscript, for example by turning a stored property into a computed property. For example, consider a macro that can be applied to a stored property to instead access a dictionary keyed by the property name. Such a macro could be used like this: ```swift struct MyStruct { @@ -224,7 +244,7 @@ struct MyStruct { } ``` -The `dictionaryStorage` attached declaration macro would alter `MyStruct` as follows: +The `dictionaryStorage` attached declaration macro would alter the properties of `MyStruct` as follows, adding accessors to each: ```swift struct MyStruct { @@ -259,11 +279,171 @@ struct MyStruct { The macro can be declared as follows: ```swift -@declaration(.attached, members: [.accessors]) macro dictionaryStorage -@declaration(.attached, members: [.accessors]) macro dictionaryStorage(key: String) +@declaration(accessors) macro dictionaryStorage: Void +@declaration(accessors) macro dictionaryStorage(key: String) +``` + +Implementations of accessor macros conform to the AccessorMacro protocol, which is defined as follows: + +```swift +protocol AccessorMacro: DeclarationMacro { + /// Expand a macro described by the given custom attribute to + /// produce accessors for the given declaration to which + /// the attribute is attached. + static func expansion( + of node: CustomAttributeSyntax, + accessorsOf declaration: DeclSyntax, + in context: inout MacroExpansionContext + ) throws -> [AccessorDeclSyntax] +} +``` + +The implementation of the `dictionaryStorage` macro would create the accessor declarations shown above, using either the `key` argument (if present) or deriving the key name from the property name. The effect of this macro isn't something that can be done with a property wrapper, because the property wrapper wouldn't have access to `self.storage`. + +The presence of an accessor macro on a stored property removes the initializer. It's up to the implementation of the accessor macro to either diagnose the presence of the initializer (if it cannot be used) or incorporate it in the result. + +### Composing macro kinds + +A given macro can have several different roles, allowing the various macro features to be composed. Each of the roles is considered independently, so a single use of a macro in source code can result in different macro expansion functions being called. These calls are independent, and could even happen concurrently. As an example, let's define a macro that emulates property wrappers fairly closely. The property wrappers proposal has an example for a [clamping property wrapper](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md#clamping-a-value-within-bounds): + +```swift +@propertyWrapper +struct Clamping { + var value: V + let min: V + let max: V + + init(wrappedValue: V, min: V, max: V) { + value = wrappedValue + self.min = min + self.max = max + assert(value >= min && value <= max) + } + + var wrappedValue: V { + get { return value } + set { + if newValue < min { + value = min + } else if newValue > max { + value = max + } else { + value = newValue + } + } + } +} + +struct Color { + @Clamping(min: 0, max: 255) var red: Int = 127 + @Clamping(min: 0, max: 255) var green: Int = 127 + @Clamping(min: 0, max: 255) var blue: Int = 127 + @Clamping(min: 0, max: 255) var alpha: Int = 255 +} +``` + +Instead, let's implement this as a macro: + +```swift +@declaration(peers: [.prefixed("_")]) +@declaration(accessors) +macro Clamping(min: T, max: T) = #externalMacro(module: "MyMacros", type: "ClampingMacro") +``` + +The usage syntax is the same in both cases. As a macro, `Clamping` both defines a peer (a backing storage property with an `_` prefix) and also defines accessors (to check min/max). The peer declaration macro is responsible for defining the backing storage, e.g., + +```swift +private var _red: Int = { + let newValue = 127 + let minValue = 0 + let maxValue = 255 + if newValue < minValue { + return minValue + } + if newValue > maxValue { + return maxValue + } + return newValue +}() +``` + +Which is implemented by having `ClampingMacro` conform to `PeerDeclarationMacro`: + +```swift +enum ClampingMacro { } + +extension ClampingMacro: PeerDeclarationMacro { + static func expansion( + of node: CustomAttributeSyntax, + peersOf declaration: DeclSyntax, + in context: inout MacroExpansionContext + ) throws -> [DeclSyntax] { + // create a new variable declaration that is the same as the original, but... + // - prepend an underscore to the name + // - make it private + } +} +``` + + + +And introduces accesssors such as + +```swift + get { return _red } + + set(__newValue) { + let __minValue = 0 + let __maxValue = 255 + if __newValue < __minValue { + _red = __minValue + } else if __newValue > __maxValue { + _red = __maxValue + } else { + _red = __newValue + } + } +``` + +by conforming to `AccessorMacro`: + +```swift +extension ClampingMacro: AccessorMacro { + static func expansion( + of node: CustomAttributeSyntax, + accessorsOf declaration: DeclSyntax, + in context: inout MacroExpansionContext + ) throws -> [AccessorDeclSyntax] { + let originalName = /* get from declaration */, + minValue = /* get from custom attribute node */, + maxValue = /* get from custom attribute node */ + let storageName = "_\(originalName)", + newValueName = context.getUniqueName(), + maxValueName = context.getUniqueName(), + minValueName = context.getUniqueName() + return [ + """ + get { \(storageName) } + """, + """ + set(\(newValueName)) { + let \(minValueName) = \(minValue) + let \(maxValueName) = \(maxValue) + if \(newValueName) < \(minValueName) { + \(storageName) = \(minValueName) + } else if \(newValueName) > maxValue { + \(storageName) = \(maxValueName) + } else { + \(storageName) = \(newValueName) + } + } + """ + ] + } +} ``` -The implementation of the macro itself would create the accessor declarations and supply them via `AttachedDeclarationExpansion(members:)`. Property wrappers aren't great for this case, because they would still define a backing stored property (e.g., `_name`) of the property wrapper type. To mimic the full behavior of property wrappers, one could introduce both members (for the accessors) and peers (for the backing stored property). +This formulation of `@Clamping` offers some benefits over the property-wrapper version: we don't need to store the min and max values as part of the backing storage (so the presence of `@Clamping` doesn't add any storage), nor do we need to define a new type. More importantly, it demonstrates how the composition of different macro kinds together can produce interesting effects. ### Up-front declarations of newly-introduced macro names @@ -271,7 +451,6 @@ Declaration macros require one to declare the names of entities that will be dec * Declarations with a specific fixed name: `.named("rawValue")` * Declarations that have the same base name as the declaration to which the macro is attached, and are therefore overloaded with it: `.overloaded`. -* Accessors of the declaration to which the macro is attached: `.accessors` * Declarations whose name is formed by adding a prefix to the name of the declaration to which the macro is attached: `.prefixed("_")` * Declarations whose name is formed by adding a suffix to the name of the declaration to which the macro is attached: `.suffixed("_docinfo")`. * Declarations whose names cannot be described by any of the simple rules above: `.arbitrary`. @@ -280,28 +459,6 @@ A declaration macro can only introduce new declarations whose names are covered ## Detailed design -### `AttachedDeclarationExpansion` - -An attached declaration macro implementation returns an instance of the `AttachedDeclarationExpansion` structure to specify the changes. The structure is specified as follows: - -```swift -public struct AttachedDeclarationExpansion { - /// The set of peer declarations introduced by this macro, which will be introduced alongside the use of the - /// macro. - public var peers: [DeclSyntax] = [] - - /// The set of member declarations introduced by this macro, which are nested inside - public var members: [DeclSyntax] = [] - - /// For a function, body for the function. If non-nil, this will replace any existing function body. - public var functionBody: CodeBlockSyntax? = nil - - public init(peers: [DeclSyntax] = [], members: [DeclSyntax] = [], functionBody: CodeBlockSyntax? = nil) -} -``` - -This structure is expected to grow more capabilities over time. Changes that can affect how a particular declaration is used will likely require paired changes to the arguments of `@declaration`, and can be considered in subsequent proposals. - ### Macros in the Standard Library #### SE-0196 `warning` and `error` @@ -342,19 +499,130 @@ It might be possible to provide a macro implementation API that is expressed in ## Future directions -### Extending `AttachedDeclarationExpansion` +(nothing just yet) -The set of changes that a macro can apply to the declaration to which is attached is (intentionally) limited by what can be expressed in `AttachedDeclarationExpansion`. Over time, we expect this structure to be extended to enable additional kinds of changes, such as adding attributes or modifiers to the declaration. These changes will likely be paired with changes to the `@declaration` syntax. For example, adding attributes to the declaration could mean introducing the following property into `AttachedDeclarationExpansion`: +## Revision history -```swift -var attributes: [AttributeSyntax] = [] -``` +Revisions from the first pitch: -but to minimize compile-time dependencies we would likely want to note when a macro may add attributes, e.g., +* Split peer/member/accessor macro implementations into separate protocols and attribute spellings, so the compiler can query them in a more fine-grained manner. +* Removed function-body macros... for the moment. We'll come back to them. +* Add example showing composition of different macro roles for the same macro to effect property-wrappers behavior. + +## Appendix + +### Implementation of `addCompletionHandler` ```swift -@declaration(.attached, augmenting: [.attributes]) -macro sometimesDeprecated +public struct AddCompletionHandler: PeerDeclarationMacro { + public static func expansion( + of node: CustomAttributeSyntax, + peersOf declaration: DeclSyntax, + in context: inout MacroExpansionContext + ) throws -> [DeclSyntax] { + // Only on functions at the moment. We could handle initializers as well + // with a little bit of work. + guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else { + throw CustomError.message("@addCompletionHandler only works on functions") + } + + // This only makes sense for async functions. + if funcDecl.signature.asyncOrReasyncKeyword == nil { + throw CustomError.message( + "@addCompletionHandler requires an async function" + ) + } + + // Form the completion handler parameter. + let resultType: TypeSyntax? = funcDecl.signature.output?.returnType.withoutTrivia() + + let completionHandlerParam = + FunctionParameterSyntax( + firstName: .identifier("completionHandler"), + colon: .colonToken(trailingTrivia: .space), + type: "(\(resultType ?? "")) -> Void" as TypeSyntax + ) + + // Add the completion handler parameter to the parameter list. + let parameterList = funcDecl.signature.input.parameterList + let newParameterList: FunctionParameterListSyntax + if let lastParam = parameterList.last { + // We need to add a trailing comma to the preceding list. + newParameterList = parameterList.removingLast() + .appending( + lastParam.withTrailingComma( + .commaToken(trailingTrivia: .space) + ) + ) + .appending(completionHandlerParam) + } else { + newParameterList = parameterList.appending(completionHandlerParam) + } + + let callArguments: [String] = try parameterList.map { param in + guard let argName = param.secondName ?? param.firstName else { + throw CustomError.message( + "@addCompletionHandler argument must have a name" + ) + } + + if let paramName = param.firstName, paramName.text != "_" { + return "\(paramName.withoutTrivia()): \(argName.withoutTrivia())" + } + + return "\(argName.withoutTrivia())" + } + + let call: ExprSyntax = + "\(funcDecl.identifier)(\(raw: callArguments.joined(separator: ", ")))" + + // FIXME: We should make CodeBlockSyntax ExpressibleByStringInterpolation, + // so that the full body could go here. + let newBody: ExprSyntax = + """ + + Task.detached { + completionHandler(await \(call)) + } + + """ + + // Drop the @addCompletionHandler attribute from the new declaration. + let newAttributeList = AttributeListSyntax( + funcDecl.attributes?.filter { + guard case let .customAttribute(customAttr) = $0 else { + return true + } + + return customAttr != node + } ?? [] + ) + + let newFunc = + funcDecl + .withSignature( + funcDecl.signature + .withAsyncOrReasyncKeyword(nil) // drop async + .withOutput(nil) // drop result type + .withInput( // add completion handler parameter + funcDecl.signature.input.withParameterList(newParameterList) + .withoutTrailingTrivia() + ) + ) + .withBody( + CodeBlockSyntax( + leftBrace: .leftBraceToken(leadingTrivia: .space), + statements: CodeBlockItemListSyntax( + [CodeBlockItemSyntax(item: .expr(newBody))] + ), + rightBrace: .rightBraceToken(leadingTrivia: .newline) + ) + ) + .withAttributes(newAttributeList) + .withLeadingTrivia(.newlines(2)) + + return [DeclSyntax(newFunc)] + } +} ``` -This would ensure that the compiler knows when the attributes as they are written on a declaration are the complete set of attributes, vs. when it will have to expand the macro to determine what attributes are applied to the declaration. From da76d2806d59136153caf77ce98c39dbd0551bc1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 10 Jan 2023 17:40:12 -0800 Subject: [PATCH 2878/4563] Add body macros --- proposals/nnnn-declaration-macros.md | 46 ++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/proposals/nnnn-declaration-macros.md b/proposals/nnnn-declaration-macros.md index 6989a92170..1a630d934a 100644 --- a/proposals/nnnn-declaration-macros.md +++ b/proposals/nnnn-declaration-macros.md @@ -302,7 +302,47 @@ The implementation of the `dictionaryStorage` macro would create the accessor de The presence of an accessor macro on a stored property removes the initializer. It's up to the implementation of the accessor macro to either diagnose the presence of the initializer (if it cannot be used) or incorporate it in the result. -### Composing macro kinds +#### Body macros + +A body macro allows one to create or replace the body of a function, initializer, or closure through syntactic manipulation. Body macros are attached to one of these entities, e.g., + +```swift +@traced(logLevel: 2) +func myFunction(a: Int, b: Int) { ... } +``` + +where the `traced` macro is declared as something like: + +```swift +@declaration(body) macro traced(logLevel: Int = 0) +``` + +and implemented in a conformance to the `BodyMacro` protocol: + +```swift +protocol BodyMacro: Macro { + /// Expand a macro described by the given custom attribute to + /// produce or modify a body for the given entity to which + /// the attribute is attached. + static func expansion( + of node: CustomAttributeSyntax, + bodyOf entity: some WithCodeBlockSyntax, + in context: inout MacroExpansionContext + ) throws -> CodeBlockSyntax +} +``` + +The `WithCodeBlockSyntax` protocol describes all entities that have an optional "body". The `traced` macro could inject a log-level check and a call to log the values of each of the parameters, e.g., + +```swift +if shouldLog(atLevel: 2) { + log("Entering myFunction((a: \(a), b: \(b)))") +} +``` + +Body macros will only be applied when the body is required, e.g., to generate code. A body macro will be applied whether the entity has an existing body or not; if the entity does have an existing body, it will be type-checked before the macro is invoked, as with other macro arguments. + +### Composing macro roles A given macro can have several different roles, allowing the various macro features to be composed. Each of the roles is considered independently, so a single use of a macro in source code can result in different macro expansion functions being called. These calls are independent, and could even happen concurrently. As an example, let's define a macro that emulates property wrappers fairly closely. The property wrappers proposal has an example for a [clamping property wrapper](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md#clamping-a-value-within-bounds): @@ -443,7 +483,7 @@ extension ClampingMacro: AccessorMacro { } ``` -This formulation of `@Clamping` offers some benefits over the property-wrapper version: we don't need to store the min and max values as part of the backing storage (so the presence of `@Clamping` doesn't add any storage), nor do we need to define a new type. More importantly, it demonstrates how the composition of different macro kinds together can produce interesting effects. +This formulation of `@Clamping` offers some benefits over the property-wrapper version: we don't need to store the min and max values as part of the backing storage (so the presence of `@Clamping` doesn't add any storage), nor do we need to define a new type. More importantly, it demonstrates how the composition of different macro roles together can produce interesting effects. ### Up-front declarations of newly-introduced macro names @@ -506,7 +546,7 @@ It might be possible to provide a macro implementation API that is expressed in Revisions from the first pitch: * Split peer/member/accessor macro implementations into separate protocols and attribute spellings, so the compiler can query them in a more fine-grained manner. -* Removed function-body macros... for the moment. We'll come back to them. +* Added "body" macros as a separate macro role. * Add example showing composition of different macro roles for the same macro to effect property-wrappers behavior. ## Appendix From ec26fbde36037d8301ea3a2049848a51d6fc1f03 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 10 Jan 2023 22:09:48 -0800 Subject: [PATCH 2879/4563] Add default-witness macros --- proposals/nnnn-declaration-macros.md | 94 ++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/proposals/nnnn-declaration-macros.md b/proposals/nnnn-declaration-macros.md index 1a630d934a..1cb0c81da4 100644 --- a/proposals/nnnn-declaration-macros.md +++ b/proposals/nnnn-declaration-macros.md @@ -342,6 +342,99 @@ if shouldLog(atLevel: 2) { Body macros will only be applied when the body is required, e.g., to generate code. A body macro will be applied whether the entity has an existing body or not; if the entity does have an existing body, it will be type-checked before the macro is invoked, as with other macro arguments. +#### Default witness macros + +Swift provides default "witness" synthesis for a number of protocols in the standard library, including `Equatable`, `Hashable`, `Encodable`, and `Decodable`. This behavior is triggered when a type has a conformance to a known protocol, and there is no suitable implementation for one of the protocol's requirements, e.g., + +```swift +struct Point: Equatable { + var x: Int + var y: Int + + // no suitable function + // + // + // + // to satisfy the protocol requirement, so the compiler creates one like the following + // + +} +``` + +There is no suitable function to meet the protocol requirement + +```swift +static func ==(lhs: Self, rhs: Self) -> Bool +``` + +so the compiler as bespoke logic to synthesize the following: + +```swift + static func ==(lhs: Point, rhs: Point) -> Bool { + lhs.x == rhs.x && lhs.y == rhs.y + } +``` + +Default witness macros bring this capability to the macro system, by allowing a macro to define a witness to satisfy a requirement when there is no suitable witness. Consider an `equatableSyntax` macro declared as follows: + +```swift +@declaration(witness) +macro equatableSynthesis: Void +``` + +This default-witness macro would be written on the protocol requirement itself, i.e., + +```swift +protocol Equatable { + @equatableSynthesis + static func ==(lhs: Self, rhs: Self) -> Bool + + static func !=(lhs: Self, rhs: Self) -> Bool +} +``` + +The macro type would implement the following protocol: + +```swift +protocol DefaultWitnessMacro: DeclarationMacro { + /// Expand a macro described by the given custom attribute to + /// produce a witness definition for the requirement to which + /// the attribute is attached. + static func expansion( + of node: CustomAttributeSyntax, + witness: DeclSyntax, + conformingType: TypeSyntax, + storedProperties: [StoredProperty], + in context: inout MacroExpansionContext + ) throws -> DeclSyntax +} +``` + +The contract with the compiler here is interesting. The compiler would use the `@equatableSynthesis` macro to define the witness only when there is no other potential witness. The compiler will then produce a declaration for the witness based on the requirement, performing substitutions as necessary to (e.g.) replace `Self` with the conforming type, and provide that declaration via the `witness` parameter. For `Point`, the `==` declaration would look like this: + +```swift +static func ==(lhs: Point, rhs: Point) -> Bool +``` + +The `expansion` operation then augments the provided `witness` with a body, and could perform other adjustments if necessary, before returning it. The resulting definition will be inserted as a member into the conforming type (or extension), wherever the protocol conformance is declared. This approach gives a good balance between making macro writing easier, because the compiler is providing a witness declaration that will match the requirement, while still allowing the macro implementation freedom to alter that witness as needed. Its design also follows how witnesses are currently synthesized in the compiler today, which has fairly well-understood implementation properties (at least, to compiler implementers). + +The `conformingType` parameter provides the syntax of the conforming type, which can be used to refer to the type anywhere in the macro. The `storedProperties` parameter provides the set of stored properties of the conforming type, which are needed for many (most?) kinds of witness synthesis. The `StoredProperty` struct is defined as follows: + +```swift +struct StoredProperty { + /// The stored property syntax node. + var property: VariableDeclSyntax + + /// The original declaration from which the stored property was created, if the stored property was + /// synthesized. + var original: Syntax? +} +``` + +The `property` field is the syntax node for the stored property. Typically, this is the syntax node as written in the source code. However, some stored properties are formed in other ways, e.g., as the backing property of a property wrapper (`_foo`) or due to some other macro expansion (member, peer, freestanding, etc.). In these cases, `property` refers to the syntax of the generated property, and `original` refers to the syntax node that caused the stored property to be generated. This `original` value can be used, for example, to find information from the original declaration that can affect the synthesis of the default witness. + +Providing stored properties to this expansion method does require us to introduce a limitation on default-witness macro implementations, which is that they cannot themselves introduce stored properties. This eliminates a potential circularity in the language model, where the list of stored properties could grow due to expansion of a macro, thereby potentially invalidating the results of already-expanded macros that saw a subset of the stored properties. Note that directly preventing default-witness macros from defining stored properties isn't a complete solution, because one could (for example) have a default-witness macro produce a witness function that itself involves a peer-declaration macro that introduces a stored property. Such problems will be detected as a dependency cycle in the compiler and reported as an error. + ### Composing macro roles A given macro can have several different roles, allowing the various macro features to be composed. Each of the roles is considered independently, so a single use of a macro in source code can result in different macro expansion functions being called. These calls are independent, and could even happen concurrently. As an example, let's define a macro that emulates property wrappers fairly closely. The property wrappers proposal has an example for a [clamping property wrapper](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md#clamping-a-value-within-bounds): @@ -547,6 +640,7 @@ Revisions from the first pitch: * Split peer/member/accessor macro implementations into separate protocols and attribute spellings, so the compiler can query them in a more fine-grained manner. * Added "body" macros as a separate macro role. +* Added default-witness macros. * Add example showing composition of different macro roles for the same macro to effect property-wrappers behavior. ## Appendix From cb15ac98181fa69e3a63fd4e07bbe0f28149aa61 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 10 Jan 2023 22:37:47 -0800 Subject: [PATCH 2880/4563] Minor improvements --- proposals/nnnn-declaration-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-declaration-macros.md b/proposals/nnnn-declaration-macros.md index 1cb0c81da4..c38e3337e0 100644 --- a/proposals/nnnn-declaration-macros.md +++ b/proposals/nnnn-declaration-macros.md @@ -578,9 +578,9 @@ extension ClampingMacro: AccessorMacro { This formulation of `@Clamping` offers some benefits over the property-wrapper version: we don't need to store the min and max values as part of the backing storage (so the presence of `@Clamping` doesn't add any storage), nor do we need to define a new type. More importantly, it demonstrates how the composition of different macro roles together can produce interesting effects. -### Up-front declarations of newly-introduced macro names +### Up-front declarations of newly-introduced names -Declaration macros require one to declare the names of entities that will be declared. These can involve member and/or peer declaration names, and are provided as an array consisting of a few different kinds: +Declaration macros require one to declare the names of entities that will be declared by the expansion of the macro. These can involve member and/or peer declaration names, and are provided as an array consisting of a few different kinds: * Declarations with a specific fixed name: `.named("rawValue")` * Declarations that have the same base name as the declaration to which the macro is attached, and are therefore overloaded with it: `.overloaded`. From 13c3614615c9748972148236c8ec735f59db3f42 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 10 Jan 2023 22:57:41 -0800 Subject: [PATCH 2881/4563] Add member-attribute macros --- proposals/nnnn-declaration-macros.md | 58 ++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/proposals/nnnn-declaration-macros.md b/proposals/nnnn-declaration-macros.md index c38e3337e0..1ce9d41fe4 100644 --- a/proposals/nnnn-declaration-macros.md +++ b/proposals/nnnn-declaration-macros.md @@ -435,6 +435,63 @@ The `property` field is the syntax node for the stored property. Typically, this Providing stored properties to this expansion method does require us to introduce a limitation on default-witness macro implementations, which is that they cannot themselves introduce stored properties. This eliminates a potential circularity in the language model, where the list of stored properties could grow due to expansion of a macro, thereby potentially invalidating the results of already-expanded macros that saw a subset of the stored properties. Note that directly preventing default-witness macros from defining stored properties isn't a complete solution, because one could (for example) have a default-witness macro produce a witness function that itself involves a peer-declaration macro that introduces a stored property. Such problems will be detected as a dependency cycle in the compiler and reported as an error. +#### Member attribute macros + +Member declaration macros allow one to introduce new member declarations within the type or extension to which they apply. In contrast, member *attribute* macros allow one to modify the member declarations that were explicitly written within the type or extension by adding new attributes to them. Those new attributes could then refer to other macros, such as peer or accessor macros, or builtin attributes. As such, they are primarily a means of *composition*, since they have fairly little effect on their own. + +Member attribute macros allow a macro to provide similar behavior to how many built-in attributes work, where declaring the attribute on a type or extension will apply that attribute to each of the members. For example, a global actor `@MainActor` written on an extension applies to each of the members of that extension (unless they specify another global actor or `nonisolated`), an access specifier on an extension applies to each of the members of that extension, and so on. + +As an example, we'll define a macro that partially mimics the behavior of the `@objcMembers` attributes introduced long ago in [SE-0160](https://github.com/apple/swift-evolution/blob/main/proposals/0160-objc-inference.md#re-enabling-objc-inference-within-a-class-hierarchy). Our `myObjCMembers` macro is a member-attribute macro: + +```swift +@declaration(memberAttribute) +macro myObjCMembers: Void +``` + +The implementation of this macro will add the `@objc` attribute to every member of the type or extension, unless that member either already has an `@objc` macro or has `@nonobjc` on it. For example, this: + +```swift +@myObjCMembers extension MyClass { + func f() { } + + var answer: Int { 42 } + + @objc(doG) func g() { } + + @nonobjc func h() { } +} +``` + +would result in: + +```swift +extension MyClass { + @objc func f() { } + + @objc var answer: Int { 42 } + + @objc(doG) func g() { } + + @nonobjc func h() { } +} +``` + +Member-attribute macro implementations should conform to the `MemberAttributeMacro` protocol: + +```swift +protocol MemberAttributeMacro: Macro { + /// Expand a macro described by the given custom attribute to + /// produce additional attributes for the members of the type. + static func expansion( + of node: CustomAttributeSyntax, + memberAttributesOf declaration: DeclSyntax, + in context: inout MacroExpansionContext + ) throws -> [DeclSyntax: [AttributeSyntax]] +} +``` + +The `expansion` operation accepts the attribute syntax `node` for the spelling of the member attribute macro and the declaration to which that attribute is attached (i.e., the type or extension). The implementation of the macro can walk the members of the `declaration` to determine which members require additional attributes. The returned dictionary will map from those members to the additional attributes that should be added to each of the members. + ### Composing macro roles A given macro can have several different roles, allowing the various macro features to be composed. Each of the roles is considered independently, so a single use of a macro in source code can result in different macro expansion functions being called. These calls are independent, and could even happen concurrently. As an example, let's define a macro that emulates property wrappers fairly closely. The property wrappers proposal has an example for a [clamping property wrapper](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md#clamping-a-value-within-bounds): @@ -641,6 +698,7 @@ Revisions from the first pitch: * Split peer/member/accessor macro implementations into separate protocols and attribute spellings, so the compiler can query them in a more fine-grained manner. * Added "body" macros as a separate macro role. * Added default-witness macros. +* Added member-attribute macros. * Add example showing composition of different macro roles for the same macro to effect property-wrappers behavior. ## Appendix From da785f7b98dec9734f59cc703b8a2d526f2ecfa0 Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Thu, 12 Jan 2023 13:04:09 +0000 Subject: [PATCH 2882/4563] Update pitch --- proposals/NNNN-async-stream-factory.md | 137 ++++++++++++++++--------- 1 file changed, 89 insertions(+), 48 deletions(-) diff --git a/proposals/NNNN-async-stream-factory.md b/proposals/NNNN-async-stream-factory.md index d798bb4975..54127449fa 100644 --- a/proposals/NNNN-async-stream-factory.md +++ b/proposals/NNNN-async-stream-factory.md @@ -2,9 +2,20 @@ * Proposal: [SE-NNNN](NNNN-async-stream-factory.md) * Authors: [Franz Busch](https://github.com/FranzBusch) -* Review Manager: TBD -* Status: **Awaiting implementation** +* Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) +* Status: **Awaiting review** +* Pitch: [Convenience Async[Throwing]Stream.makeStream methods](https://forums.swift.org/t/pitch-convenience-async-throwing-stream-makestream-methods/61030) +* Implementation: [apple/swift#62968](https://github.com/apple/swift/pull/62968) +
    +Revision history + +| | | +| ---------- | ------------------------------------------------- | +| 2022-10-26 | Initial pitch. | +| 2023-01-12 | Switch to concrete return type | + +
    ## Introduction @@ -19,7 +30,8 @@ is to pass the continuation and the `Async[Throwing]Stream` to different places. This requires escaping the `Async[Throwing]Stream.Continuation` out of the closure that is passed to the initialiser. Escaping the continuation is slightly inconvenient since it requires a dance -around an implicitly unwrapped optional. +around an implicitly unwrapped optional. Furthermore, the closure implies +that the continuation lifetime is scoped to the closure which it isn't. ## Proposed solution @@ -34,52 +46,80 @@ respectively. ```swift extension AsyncStream { + /// Struct for the return type of ``AsyncStream/makeStream(elementType:limit:)``. + /// + /// This struct contains two properties: + /// 1. The ``continuation`` which should be retained by the producer and is used + /// to yield new elements to the stream and finish it. + /// 2. The ``stream`` which is the actual ``AsyncStream`` and + /// should be passed to the consumer. + public struct NewStream: Sendable { + /// The continuation of the ``AsyncStream`` used to yield and finish. + public let continuation: AsyncStream.Continuation + + /// The stream which should be passed to the consumer. + public let stream: AsyncStream + + public init(stream: AsyncStream, continuation: AsyncStream.Continuation) { + self.stream = stream + self.continuation = continuation + } + } + /// Initializes a new ``AsyncStream`` and an ``AsyncStream/Continuation``. /// /// - Parameters: - /// - elementType: The element type of the sequence. + /// - elementType: The element type of the stream. /// - limit: The buffering policy that the stream should use. - /// - Returns: A tuple which contains the stream and its continuation. - @_alwaysEmitIntoClient + /// - Returns: A ``NewStream`` struct which contains the stream and its continuation. public static func makeStream( of elementType: Element.Type = Element.self, bufferingPolicy limit: Continuation.BufferingPolicy = .unbounded - ) -> (stream: AsyncStream, continuation: AsyncStream.Continuation) { - let storage: _Storage = .create(limit) + ) -> NewStream { + let storage: _Storage = .create(limit: limit) let stream = AsyncStream(storage: storage) - let continuation = Continuation(storage) - return (stream: stream, continuation: continuation) - } - - @_alwaysEmitIntoClient - init(storage: _Storage) { - self.context = _Context(storage: storage, produce: storage.next) + let continuation = Continuation(storage: storage) + return .init(stream: stream, continuation: continuation) } } extension AsyncThrowingStream { + /// Struct for the return type of ``AsyncThrowingStream/makeStream(elementType:limit:)``. + /// + /// This struct contains two properties: + /// 1. The ``continuation`` which should be retained by the producer and is used + /// to yield new elements to the stream and finish it. + /// 2. The ``stream`` which is the actual ``AsyncThrowingStream`` and + /// should be passed to the consumer. + public struct NewStream: Sendable { + /// The continuation of the ``AsyncThrowingStream`` used to yield and finish. + public let continuation: AsyncThrowingStream.Continuation + + /// The stream which should be passed to the consumer. + public let stream: AsyncThrowingStream + + public init(stream: AsyncThrowingStream, continuation: AsyncThrowingStream.Continuation) { + self.stream = stream + self.continuation = continuation + } + } + /// Initializes a new ``AsyncThrowingStream`` and an ``AsyncThrowingStream/Continuation``. /// /// - Parameters: - /// - elementType: The element type of the sequence. + /// - elementType: The element type of the stream. /// - failureType: The failure type of the stream. /// - limit: The buffering policy that the stream should use. - /// - Returns: A tuple which contains the stream and its continuation. - @_alwaysEmitIntoClient + /// - Returns: A ``NewStream`` struct which contains the stream and its continuation. public static func makeStream( of elementType: Element.Type = Element.self, throwing failureType: Failure.Type = Failure.self, bufferingPolicy limit: Continuation.BufferingPolicy = .unbounded - ) -> (stream: AsyncThrowingStream, continuation: AsyncThrowingStream.Continuation) { - let storage: _Storage = .create(limit) + ) -> NewStream { + let storage: _Storage = .create(limit: limit) let stream = AsyncThrowingStream(storage: storage) - let continuation = Continuation(storage) - return (stream: stream, continuation: continuation) - } - - @_alwaysEmitIntoClient - init(storage: _Storage) { - self.context = _Context(storage: storage, produce: storage.next) + let continuation = Continuation(storage: storage) + return .init(stream: stream, continuation: continuation) } } ``` @@ -90,31 +130,32 @@ As this is an additive change, it should not have any compatibility, stability o ## Alternatives considered -### Return a concrete type instead of a tuple -My initial proposal was using a concrete type as the return paramter of the factory; -however, I walked back on it since back deployment issues were raised with introducing a new type. +### Return a tuple instead of a concrete type +My initial pitch was using a tuple as the return paramter of the factory; +however, I walked back on it since I think we can provide better documentation on +the concrete type. Furthermore, it makes it more discoverable as well. -I still believe that there is value in providing a concrete type since -it is easier to handle than a tuple and documentation can be provided in a nice way. +An implementation returning a tuple would look like this; ```swift -extension AsyncStream { - /// Simple struct for the return type of ``AsyncStream/makeStream(elementType:)``. - public struct NewStream { - /// The actual stream. - public let stream: AsyncStream - /// The continuation of the stream - public let continuation: AsyncStream.Continuation - - @inlinable - internal init( - stream: AsyncStream, - continuation: AsyncStream.Continuation - ) { - self.stream = stream - self.continuation = continuation - } +extension AsyncStream + /// Initializes a new ``AsyncStream`` and an ``AsyncStream/Continuation``. + /// + /// - Parameters: + /// - elementType: The element type of the stream. + /// - limit: The buffering policy that the stream should use. + /// - Returns: A tuple which contains the stream and its continuation. + @available(SwiftStdlib 5.8, *) + public static func makeStream( + of elementType: Element.Type = Element.self, + bufferingPolicy limit: Continuation.BufferingPolicy = .unbounded + ) -> (stream: AsyncStream, continuation: AsyncStream.Continuation) { + let storage: _Storage = .create(limit: limit) + let stream = AsyncStream(storage: storage) + let continuation = Continuation(storage: storage) + return (stream: stream, continuation: continuation) } +} ``` ### Do nothing alternative From f79a04be4149afd376c1c951e9d2be0add203080 Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Thu, 12 Jan 2023 13:05:43 +0000 Subject: [PATCH 2883/4563] Add the upside to the tuple based approach --- proposals/NNNN-async-stream-factory.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/NNNN-async-stream-factory.md b/proposals/NNNN-async-stream-factory.md index 54127449fa..e1247781ac 100644 --- a/proposals/NNNN-async-stream-factory.md +++ b/proposals/NNNN-async-stream-factory.md @@ -135,6 +135,8 @@ My initial pitch was using a tuple as the return paramter of the factory; however, I walked back on it since I think we can provide better documentation on the concrete type. Furthermore, it makes it more discoverable as well. +The upside of using a tuple based approach is that we can backdeploy it. + An implementation returning a tuple would look like this; ```swift From 49ec04b6d870ac2535d22f3380421d7b273174c7 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 21 Oct 2022 02:19:18 -0400 Subject: [PATCH 2884/4563] Clarify the scope of the evolution process. The old wording here is basically the scope of the Language Workgroup, and we've previously agreed that evolution covers more than that. This idea of the core tools necessary to build Swift programs captures the additional scope as I see it. I've also tried to clarify the wording around implementation vs. design without making it too stodgy. I don't know if I succeeded at that. --- process.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/process.md b/process.md index 782d9fb27c..1804c3fa7f 100644 --- a/process.md +++ b/process.md @@ -4,7 +4,19 @@ Swift is a powerful and intuitive programming language that is designed to make ## Scope -The Swift evolution process covers all changes to the Swift language and the public interface of the Swift standard library, including new language features and APIs (no matter how small), changes to existing language features or APIs, removal of existing features, and so on. Smaller changes, such as bug fixes, optimizations, or diagnostic improvements can be contributed via the normal contribution process; see [Contributing to Swift](https://www.swift.org/contributing/). +The Swift evolution process covers all design changes, no matter how small, to the Swift language, its core libraries, and the core tools necessary to build Swift programs. This includes additions, removals, and changes to: +- the features of the Swift language, +- the public interface of the Swift standard library, +- the configuration of the Swift compiler, and +- the core tools of the Swift package ecosystem, including the configuration of the [Swift package manager](https://www.swift.org/package-manager/) and the design of its manifest files. + +The design of other tools, such as IDEs, debuggers, and documentation generators, is not covered by the evolution process. The Core Team may create workgroups to guide and make recommendations about the development of these tools, but the output of those workgroups is not reviewed. + +The evolution process does not cover experimental features, which can be added, changed, or removed at any time. Implementors should take steps to prevent the accidental use of experimental features, such as by enabling them only under explicitly experimental options. Features should not be allowed to be remain perpetually experimental; a feature with no clear path for development into an official feature should be removed. + +Changes such as bug fixes, optimizations, or diagnostic improvements can be contributed via the normal contribution process; see [Contributing to Swift](https://www.swift.org/contributing/). Some bug fixes are effectively substantial changes to the design, even if they're just making the implementation match the official documentation; whether such a change requires evolution review is up to the appropriate evolution workgroup. + +Which parts of the Swift project are covered by the evolution process is ultimately up to the judgment of the Core Team. ## Goals From 215b41478194e885f3dd59d89ff75105fadb699a Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 21 Oct 2022 02:27:24 -0400 Subject: [PATCH 2885/4563] Talk about evolution workgroups. The Language Workgroup is the only current evolution workgroup, but I expect we'll eventually have a workgroup covering builds and the package ecosystem, and maybe there will be more. For now, this is what we've agreed to as I understand it. --- process.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/process.md b/process.md index 1804c3fa7f..dd1f32c667 100644 --- a/process.md +++ b/process.md @@ -29,8 +29,13 @@ There is a natural tension between these two goals. Open evolution processes are ## Community structure -* [Core Team](https://www.swift.org/community/#core-team) members are responsible for the strategic direction of Swift. -* [Language Workgroup](https://www.swift.org/community/#language-workgroup) members initiate, participate in, and manage the public review of proposals and have the authority to accept or reject changes to Swift. +The [Core Team](https://www.swift.org/community/#core-team) is responsible for the strategic direction of Swift. The Core Team creates workgroups focused on specific parts of the project. When the Core Team gives a workgroup authority over part of the evolution of the project, that workgroup is called an evolution workgroup. Evolution workgroups manage the evolution process for proposals under their authority, working together with other workgroups as needed. + +Currently, there is only one evolution workgroup: + +* The [Language Workgroup](https://www.swift.org/language-workgroup/#evolution-process) has authority over the evolution of the Swift language and its standard library. + +The Core Team manages (or delegates) the evolution process for proposals outside these areas. The Core Team also retains the power to override the evolution decisions of workgroups when necessary. ## Participation From 245ed5bd0995ff544be941a7eb7a63e443c4eec9 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 21 Oct 2022 02:30:03 -0400 Subject: [PATCH 2886/4563] Describe the role of roadmaps and visions in the evolution process. "Visions" are what we formerly called "manifestos", although not all of the old manifestos were approved by the Core Team; that approval requirement is new. --- process.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/process.md b/process.md index dd1f32c667..bf8d8cfdab 100644 --- a/process.md +++ b/process.md @@ -37,6 +37,20 @@ Currently, there is only one evolution workgroup: The Core Team manages (or delegates) the evolution process for proposals outside these areas. The Core Team also retains the power to override the evolution decisions of workgroups when necessary. +## Proposals, roadmaps, and visions + +There are three kinds of documents commonly used in the evolution process. + +* An evolution *proposal* describes a specific proposed change in detail. All evolution changes are advanced as proposals which will be discussed in the community and given a formal open review. + +* An evolution *roadmap* describes a concrete plan for how a complex change will be broken into separate proposals that can be individually pitched and reviewed. Considering large changes in small pieces allows the community to provide more focused feedback about each part of the change. A roadmap makes this organization easier for community members to understand. + + Roadmaps are planning documents that do not need to be reviewed. + +* An evolution *vision* describes a high-level design for a broad topic (for example, string processing or concurrency). A vision creates a baseline of understanding in the community for future conversations on that topic, setting goals and laying out a possible program of work. + + Visions must be approved by the appropriate evolution workgroup. This approval is an endorsement of the vision's basic ideas, but not of any of its concrete proposals, which must still be separately developed and reviewed. + ## Participation Everyone is welcome to propose, discuss, and review ideas to improve From 9e70310f036784d1c6b796add7fe12a93d4596de Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 21 Oct 2022 13:50:36 -0400 Subject: [PATCH 2887/4563] Apply suggestions from code review Co-authored-by: Ben Rimmington --- process.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/process.md b/process.md index bf8d8cfdab..7db543f8ed 100644 --- a/process.md +++ b/process.md @@ -4,7 +4,7 @@ Swift is a powerful and intuitive programming language that is designed to make ## Scope -The Swift evolution process covers all design changes, no matter how small, to the Swift language, its core libraries, and the core tools necessary to build Swift programs. This includes additions, removals, and changes to: +The Swift evolution process covers all design changes, no matter how small, to the Swift language, its standard library, and the core tools necessary to build Swift programs. This includes additions, removals, and changes to: - the features of the Swift language, - the public interface of the Swift standard library, - the configuration of the Swift compiler, and @@ -12,7 +12,7 @@ The Swift evolution process covers all design changes, no matter how small, to t The design of other tools, such as IDEs, debuggers, and documentation generators, is not covered by the evolution process. The Core Team may create workgroups to guide and make recommendations about the development of these tools, but the output of those workgroups is not reviewed. -The evolution process does not cover experimental features, which can be added, changed, or removed at any time. Implementors should take steps to prevent the accidental use of experimental features, such as by enabling them only under explicitly experimental options. Features should not be allowed to be remain perpetually experimental; a feature with no clear path for development into an official feature should be removed. +The evolution process does not cover experimental features, which can be added, changed, or removed at any time. Implementors should take steps to prevent the accidental use of experimental features, such as by enabling them only under explicitly experimental options. Features should not be allowed to remain perpetually experimental; a feature with no clear path for development into an official feature should be removed. Changes such as bug fixes, optimizations, or diagnostic improvements can be contributed via the normal contribution process; see [Contributing to Swift](https://www.swift.org/contributing/). Some bug fixes are effectively substantial changes to the design, even if they're just making the implementation match the official documentation; whether such a change requires evolution review is up to the appropriate evolution workgroup. From fead5cf27a39b9a624d6f5bfbd0a43044831dd7c Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sun, 15 Jan 2023 15:08:59 +0000 Subject: [PATCH 2888/4563] Update 0379-opt-in-reflection-metadata.md (#1907) * Update the Status field * Add a Review field --- proposals/0379-opt-in-reflection-metadata.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proposals/0379-opt-in-reflection-metadata.md b/proposals/0379-opt-in-reflection-metadata.md index 4bd88dbe7f..c5085ce9df 100644 --- a/proposals/0379-opt-in-reflection-metadata.md +++ b/proposals/0379-opt-in-reflection-metadata.md @@ -1,17 +1,16 @@ - # Swift Opt-In Reflection Metadata * Proposal: [SE-0379](0379-opt-in-reflection-metadata.md) * Authors: [Max Ovtsin](https://github.com/maxovtsin) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Active review (November 30...December 14, 2022)** +* Status: **Returned for revision** * Implementation: [apple/swift#34199](https://github.com/apple/swift/pull/34199) +* Review: ([first pitch](https://forums.swift.org/t/proposal-opt-in-reflection-metadata/40981)) ([second pitch](https://forums.swift.org/t/pitch-2-opt-in-reflection-metadata/41696)) ([third pitch](https://forums.swift.org/t/pitch-3-opt-in-reflection-metadata/58852)) ([review](https://forums.swift.org/t/se-0379-opt-in-reflection-metadata/61714)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0379-opt-in-reflection-metadata/62390)) + ## Introduction This proposal seeks to increase the safety, efficiency, and secrecy of Swift Reflection Metadata by improving the existing mechanism and providing the opportunity to express a requirement on Reflection Metadata in APIs that consume it. - -Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/t/pitch-3-opt-in-reflection-metadata/58852) ## Motivation From 462825e73cddab1c181070eaaceefce2387f0be4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 17 Jan 2023 09:59:31 -0800 Subject: [PATCH 2889/4563] Accept SE-0381: DiscardingTaskGroups (#1913) --- proposals/0381-task-group-discard-results.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0381-task-group-discard-results.md b/proposals/0381-task-group-discard-results.md index 5a08c07f43..2b1c0f8fc3 100644 --- a/proposals/0381-task-group-discard-results.md +++ b/proposals/0381-task-group-discard-results.md @@ -3,8 +3,9 @@ * Proposal: [SE-0381](0381-task-group-discard-results.md) * Authors: [Cory Benfield](https://github.com/Lukasa), [Konrad Malawski](https://github.com/ktoso) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review (December 15...December 29, 2022)** +* Status: **Accepted** * Implementation: https://github.com/apple/swift/pull/62361 +* Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0381-discardingtaskgroups/62615) ### Introduction From 2d44a5939fcf4fd9a2c65c777d5787533fbbff73 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 17 Jan 2023 14:11:38 -0800 Subject: [PATCH 2890/4563] Add proposal for freestanding macros --- proposals/nnnn-freestanding-macros.md | 236 ++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 proposals/nnnn-freestanding-macros.md diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md new file mode 100644 index 0000000000..3f23e69fe4 --- /dev/null +++ b/proposals/nnnn-freestanding-macros.md @@ -0,0 +1,236 @@ +# Freestanding Macros + +* Proposal: [SE-nnnn](nnnn-freestanding-macros.md) +* Authors: [Doug Gregor](https://github.com/DougGregor), [Richard Wei](https://github.com/rxwei), [Holly Borla](https://github.com/hborla) +* Review Manager: Unassigned +* Status: **Pending review** +* Implementation: On `main` behind the experimental flag `Macros` +* Review: + +## Introduction + + [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) introduces macros into Swift. The approach involves an explicit syntax for uses of macros (prefixed by `#`), type checking for macro arguments prior to macro expansion, and macro expansion implemented via separate programs that operate on the syntax tree of the arguments. + +This proposal generalizes the `#`-prefixed macro expansion syntax introduced for expression macros to also allow macros to generate declarations and statements, enabling a number of other use cases, including: + +* Subsuming the `#warning` and `#error` directives introduced in [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) into macros. +* Logging entry/exit of a function. + +## Proposed solution + +The proposal introduces "freestanding" macros, which are expanded to create zero or more new declarations, statements, and expressions. The generated declarations can be referenced from other Swift code, making freestanding macros useful for many different kinds of code generation and manipulation. + +All freestanding macros use the `#`-prefixed syntax introduced in [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) , building on and generalizing its design. Indeed, this proposal reclassifies expression macros as one form of freestanding macros, introducing two additional kinds of freestanding macro: + +* *Declaration macros* introduce zero or more declarations. These macros can be used anywhere where a declaration is permitted, including at the top level, in a function or closure body, or in a type definition or extension thereof. +* *Code item* macros introduce a mix of statements, expressions, and declarations. These macros can be used wherever one can write a statement, including at the top level (for Swift scripts or main files) and in the body of a function or closure. + +Freestanding macros are declared with the `macro` introducer, and have one or more `@freestanding` attributes applied to them. The `@freestanding` attribute always contains a macro *role* (expression, declaration, or code item) and, optionally, a set of *introduced names*. For example, a freestanding code item macro would have an attribute like this: + +```swift +@freestanding(codeItem) +``` + +whereas a declaration macro that introduced an enum named `CodingKeys` would have an attribute like this: + +```swift +@freestanding(declaration, names: named(CodingKeys)) +``` + +Implementations of freestanding macros are types that conform to the `FreestandingMacro` protocol, which is defined as follows: + +```swift +protocol FreestandingMacro: Macro { } +``` + +### Expression macros + +As previously noted, expression macros are one form of freestanding macro. As such, we revise the definition of the `ExpressionMacro` protocol provided in [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) by making it refine `FreestandingMacro`: + +```swift +protocol ExpressionMacro: FreestandingMacro { + // ... +} +``` + +Additionally, we replace the `@expression` attribute introduced in SE-0382 with `@freestanding(expression)`. For example, the `stringify` macro would be declared as follows: + +```swift +@freestanding(expression) macro stringify(_: T) -> (T, String) +``` + +Expression macros can be used anywhere that a declaration is permitted, e.g., within the body of a function or closure, or as a subexpression anywhere. Their implementations always produce another expression. + +### Declaration macros + +Declaration macros can be used anywhere that a declaration is permitted, e.g., in a function or closure body, at the top level, or within a type definition or extension thereof. Declaration macros produce zero or more declarations. The `warning` directive introduced by [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) can be described as a freestanding declaration macro as follows: + +```swift +/// Emits the given message as a warning, as in SE-0196. +@freestanding(declaration) macro warning(_ message: String) +``` + +Given this macro declaration, the syntax + +```swift +#warning("unsupported configuration") +``` + +can be used anywhere a declaration can occur. + +Freestanding macros are implemented as types that conform to the `DeclarationMacro` protocol : + +```swift +public protocol DeclarationMacro: FreestandingMacro { + /// Expand a macro described by the given freestanding macro expansion declaration + /// within the given context to produce a set of declarations. + static func expansion( + of node: MacroExpansionDeclSyntax, in context: any MacroExpansionContext + ) throws -> [DeclSyntax] +} +``` + +The `MacroExpansionDeclSyntax` node provides the syntax tree for the use site (e.g., `#warning("unsupported configuration")`), and has the same grammar and members as the `MacroExpansionExprSyntax` node introduced in [SE-0382](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion). The grammar parallels `macro-expansion-expression`: + +``` +declaration -> macro-expansion-declaration +macro-expansion-declaration -> '#' identifier generic-argument-clause[opt] function-call-argument-clause[opt] trailing-closures[opt] +``` + +The implementation of a `warning` declaration macro extracts the string literal argument (producing an error if there wasn't one) and emits a warning. It returns an empty list of declarations: + +```swift +public struct WarningMacro: DeclarationMacro { + public static func expansion( + of node: MacroExpansionDeclSyntax, in context: inout MacroExpansionContext + ) throws -> [DeclSyntax] { + guard let messageExpr = node.argumentList.first?.expression?.as(SpecializeExprSyntax.self), + messageExpr.segments.count == 1, + let firstSegment = messageExpr.segments.first, + case let .stringSegment(message) = firstSegment else { + throw SimpleError(node, "warning macro requires a non-interpolated string literal") + } + + context.diagnose(.warning(firstSegment.text)) + return [] + } +} +``` + +A macro that does introduce declarations needs to document the names it introduces. The `@freestanding` attribute has a `names` argument that provides the names introduced by a macro. For example, consider a macro that declares a `main` function suitable for use with the [`@main` attribute](https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md) but that handles an exit code, e.g., + +```swift +#main { + if hasBadArgument() { return -1 } + if noNetwork() { return -2 } + return 0 +} +``` + +will generate code such as: + +```swift +static func unique_name() -> Int { + if hasBadArgument() { return -1 } + if noNetwork() { return -2 } + return 0 +} + +static func main() { + guard let exit_code = unique_name(), exit_code == 0 else { + exit(exit_code) + } + + return 0 +} +``` + +The `main` attribute would be declared as follows: + +```swift +@freestanding(declaration, names: named(main)) +``` + +This specifies that the macro will produce a declaration named `main`. It is also allowed to produce declarations with names produced by `MacroExpansionContext.createUniqueName` (implied by `unique_name` in the example above) without documenting them, because they are not visible to other parts of the program not generated by the macro. The reasons for documenting macro names are provided within the detailed design. + +### Code item macros + +Code item macros can produce any mix of declarations, statements, and expressions, which are collectively called "code items" in the grammar. Code item macros can be used for top-level code and within the bodies of functions and closures. They are declaration with `@freestanding(codeItem)`. For example, we could declare a macro that logs when we are entering and exiting a function: + +```swift +@freestanding(declaration) macro logEntryExit(arguments: Any...) +``` + +Code item macros are implemented as types conforming to the `CodeItemMacro` protocol: + +```swift +public protocol CodeItemMacro: FreestandingMacro { + /// Expand a macro described by the given freestanding macro expansion declaration + /// within the given context to produce a set of code items, which can be any mix of + /// expressions, statements, and declarations. + static func expansion( + of node: MacroExpansionDeclSyntax, in context: any MacroExpansionContext + ) throws -> [CodeBlockItemSyntax] +} +``` + +The `logEntryExit` macro could introduce code such as: + +```swift +print("- Entering \(#function)(\(arguments))") +defer { + print("- Exiting \(#function)(\(arguments))") +} +``` + +## Detailed design + +### Up-front declarations of newly-introduced names + +Whenever a macro produces declarations that are visible to other Swift code, it is required to declare the names in advance. This enables the Swift compiler and related tools to better reason about the set of names that can be introduced by a given use of a macro without having to expand the macro (or type-check its arguments), which can reduce the compile-time cost of macros and improve incremental builds. All of the names need to be specified within the attribute declaring the macro role, using the following forms: + +* Declarations with a specific fixed name: `named()`. +* Declarations whose names cannot be described statically, for example because they are derived from other inputs: `arbitrary`. + +Multiple names can be provided after the `names` label, e.g., + +```swift +@freestanding(declarations, names: named(CodingKeys), named(init(coder:)), named(encode(with:))) +macro codable: Void +``` + +A macro can only introduce new declarations whose names are covered by the kinds provided, or have their names generated via `MacroExpansionContext.createUniqueName`. This ensures that, in most cases (where `.arbitrary` is not specified) the Swift compiler and related tools can reason about the set of names that will be introduced by a given use of a declaration macro without having to expand the macro, which can reduce the compile-time cost of macros and improve incremental builds. + +### Macros in the Standard Library + +#### SE-0196 `warning` and `error` + +The `#warning` and `#error` directives introduced in [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md): can be implemented directly as declaration macros: + +```swift +/// Emit a warning containing the given message. +@freestanding(declaration) macro warning(_ message: String) + +/// Emit an error containing the given message +@freestanding(declaration) macro error(_ message: String) +``` + +## Source compatibility + +Freestanding declaration macros use the same syntax introduced for expression macros, which were themselves a pure extension without an impact on source compatibility. There is a syntactic ambiguity between expression and freestanding declaration macros, i.e., `#warning("watch out")` within a function body could be either an expression or a declaration. The distinction will need to be determined semantically, by determining whether the named macro is either an expression or a freestanding declaration macro. + +Attached declaration macros use the same syntax introduced for custom attributes (such as property wrappers), and therefore do not have an impact on source compatibility. + +## Effect on ABI stability + +Macros are a source-to-source transformation tool that have no ABI impact. + +## Effect on API resilience + +Macros are a source-to-source transformation tool that have no effect on API resilience. + +## Alternatives considered + +## Future directions + +(nothing just yet) From c94cbddccdc93d23a1dc6a0f4932821f348cf780 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 18 Jan 2023 13:52:00 -0800 Subject: [PATCH 2891/4563] Describe some rules for name lookup --- proposals/nnnn-freestanding-macros.md | 66 ++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index 3f23e69fe4..f0e0044f59 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -158,7 +158,7 @@ This specifies that the macro will produce a declaration named `main`. It is als Code item macros can produce any mix of declarations, statements, and expressions, which are collectively called "code items" in the grammar. Code item macros can be used for top-level code and within the bodies of functions and closures. They are declaration with `@freestanding(codeItem)`. For example, we could declare a macro that logs when we are entering and exiting a function: ```swift -@freestanding(declaration) macro logEntryExit(arguments: Any...) +@freestanding(codeItem) macro logEntryExit(arguments: Any...) ``` Code item macros are implemented as types conforming to the `CodeItemMacro` protocol: @@ -183,6 +183,8 @@ defer { } ``` +Code item macros can only introduce new declarations that have unique names, created with `createUniqueName`. They cannot introduce named declarations, because doing so affects the ability to type-check without repeatedly expanding the macro with potentially complete information. See the section on the visibility of names used and introduced by macros. + ## Detailed design ### Up-front declarations of newly-introduced names @@ -201,7 +203,67 @@ macro codable: Void A macro can only introduce new declarations whose names are covered by the kinds provided, or have their names generated via `MacroExpansionContext.createUniqueName`. This ensures that, in most cases (where `.arbitrary` is not specified) the Swift compiler and related tools can reason about the set of names that will be introduced by a given use of a declaration macro without having to expand the macro, which can reduce the compile-time cost of macros and improve incremental builds. -### Macros in the Standard Library +### Visibility of names used and introduced by macros + +Declaration and code-item macros can introduce new names into the program. Whether and when those names are visible to other parts of the program, and in particular other macros, is a subtle design problem that has a number of interesting constraints. + +First, the arguments to a macro are type-checked before it can be determined which macro is being expanded, so they cannot depend on the expansion of that macro. For example, consider a freestanding macro use spelled `#myMacro(x)`: if it introduced a declaration named `x`, it would potentially invalidate the type-check of its own argument, or cause that macro expansion to choose a different `myMacro` that doesn't produce an `x`! + +Second, if the output of one macro expansion (say, `#myMacro1(x)`) introduces a name (say, `y`) that is then used in the argument to another macro expansion (say, `#myMacro2(y)`), then the order in which the macros are expanded can affect the semantics of the resulting program. It is not generally possible to introduce an ordering on macro expansions, nor would it be desirable, because Swift generally tries not to have order dependencies among declarations in a program. Incidental orderings based on the names introduced by the macro don't help, either, because names of declaration macros can be specified as `arbitrary` and therefore cannot be predicated. + +Third, macro expansion is a relatively expensive compile-time operation, involving serialization overhead to transfer parts of the program from the compiler/IDE to another program that expands the macro. Therefore, freestanding macros are [only expanded once per use site](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion), so their expansions cannot participate in the type checking of their arguments or of other surrounding statements or expressions. For example, consider a this (intentionally obtuse) Swift code: + +```swift +@freestanding(codeItem) macro myMacro(_: Int) +@freestanding(codeItem, names: named("y")) macro myMacro(_: Double) + +func f(_: Int, body: (Int) -> Void) +func f(_: Double, body: (Double) -> Void) + +f(1) { x in + #myMacro(x) + print(y) +} +``` + +The freestanding macro use `#myMacro(x)` provides different names depending on whether the argument is an `Int` or ` Double`. From the perspective of the call to `f`, both overloads of `f` are possible, and the only way to check beyond that macro use to the `print` expression that follows is to try to expand the macro... for the first `myMacro`, the `print(y)` expression will fail to type-check (there is no `y`) whereas the second would find the `y` generated by the second `myMacro` and will succeed. However, this approach does not scale, because it could involve expanding macros a large number of times during type checking. Moreover, the macro expansions might end up getting incomplete information if, for example, macros are someday provided with type information and that type information isn't known yet (because the expansion is happening during type inference). + +To address these issues, a name introduced by a macro expansion is not visible within macro arguments for macros the same scope as the macro expansion or any of its enclosing scopes. This is conceptually similar to [outside-in expansion of expression macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion), where one can imagine that all of the macros at a particular scope are type-checked and expanded simultaneously at the top-level scope. Then, macros within each type definition and its extensions are type-checked and expanded simultaneously: they can see the names introduced by top-level macro expansions, but not names introduced at other levels. The following annotated code example shows which names are visible: + +```swift +import OtherModule // declares names x, y, z + +#macro1(x) // uses OtherModule.x, introduces name y + +#macro2(y) // uses OtherModule.y, introduce name x + +struct S1 { + #macro3(x) // uses the name x introduced by #macro2, introduces the name z +} + +extension S1 { + #macro4(z) // uses OtherModule.z + + func f() { + print(z) // uses z introduced by #macro3 + } +} +``` + +These name lookup rules, while important for providing a reasonable scheme for macros to be expanded without interfering with each other, do introduce a new kind of name shadowing to the language. If `#macro2` were manually expanded into source code, the declaration `x` it produces would then become visible to the macro argument in `#macro1(x)`, changing the behavior of the program. The compiler might be able to detect such shadowing in practice, when a macro-introduced name at a particular scope would change the meaning of a lookup from that particular scope. + +Within function and closure bodies, the fact that names introduced by the macro expansion are not visible within the current scope means that our earlier example will never find a declaration `y` introduced by `#myMacro`: + +```swift +f(1) { x in + #myMacro(x) + print(y) // does not consider any names introduced by `#myMacro` +} +``` + +Therefore, a macro used within a closure or function body can only introduce declarations using names produced by `createUniqueName`. This maintains the [two-phase of checking macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion) where type checking and inference is performed without expanding the macro, then the macro is expanded and its result type-checked independently, with no ability to influence type inference further. + +### Macros in he Standard Library #### SE-0196 `warning` and `error` From 8b3474eff1909184d43c9d24a58c876453675e2f Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 18 Jan 2023 18:20:34 -0500 Subject: [PATCH 2892/4563] Accept SE-0383 --- .../0383-deprecate-uiapplicationmain-and-nsapplicationmain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md b/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md index 35e60c1a30..9f5848e43e 100644 --- a/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md +++ b/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md @@ -3,9 +3,9 @@ * Proposal: [SE-0383](0383-deprecate-uiapplicationmain-and-nsapplicationmain.md) * Authors: [Robert Widmann](https://github.com/codafi) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (January 3rd...16th, 2023)** +* Status: **Accepted** * Implementation: [PR 62151](https://github.com/apple/swift/pull/62151) -* Review: ([pitch](https://forums.swift.org/t/deprecate-uiapplicationmain-and-nsapplicationmain/61493)) ([review](https://forums.swift.org/t/se-0383-deprecate-uiapplicationmain-and-nsapplicationmain/62375)) +* Review: ([pitch](https://forums.swift.org/t/deprecate-uiapplicationmain-and-nsapplicationmain/61493)) ([review](https://forums.swift.org/t/se-0383-deprecate-uiapplicationmain-and-nsapplicationmain/62375)) ([acceptance](https://forums.swift.org/t/accepted-se-0383-deprecate-uiapplicationmain-and-nsapplicationmain/62645)) ## Introduction From ee58f84a64597c441c9f3ce9a0c227d6b9ecd14e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 18 Jan 2023 16:56:31 -0800 Subject: [PATCH 2893/4563] Focus proposal on attached macros --- ...tion-macros.md => nnnn-attached-macros.md} | 519 ++++++++---------- 1 file changed, 228 insertions(+), 291 deletions(-) rename proposals/{nnnn-declaration-macros.md => nnnn-attached-macros.md} (61%) diff --git a/proposals/nnnn-declaration-macros.md b/proposals/nnnn-attached-macros.md similarity index 61% rename from proposals/nnnn-declaration-macros.md rename to proposals/nnnn-attached-macros.md index 1ce9d41fe4..ed3f5fd32d 100644 --- a/proposals/nnnn-declaration-macros.md +++ b/proposals/nnnn-attached-macros.md @@ -1,138 +1,74 @@ -# Declaration Macros +# Attached Macros -* Proposal: [SE-nnnn](nnnn-declaration-macros.md) -* Authors: [Doug Gregor](https://github.com/DougGregor) +* Proposal: [SE-nnnn](nnnn-attached-macros.md) +* Authors: [Doug Gregor](https://github.com/DougGregor), [Holly Borla](https://github.com/hborla), [Richard Wei](https://github.com/rxwei) * Review Manager: Unassigned * Status: **Pending review** -* Implementation: Nothing yet +* Implementation: Partially implemented on GitHub `main` behind the experimental flag `Macros`. * Review: ## Introduction -Declaration macros provide a way to extend Swift by creating new declarations based on arbitrary syntactic transformations on their arguments. They make it possible to extend Swift in ways that were only previously possible by introducing new language features, helping developers build more expressive libraries and eliminate extraneous boilerplate. +Attached macros provide a way to extend Swift by creating and extending declarations based on arbitrary syntactic transformations on their arguments. They make it possible to extend Swift in ways that were only previously possible by introducing new language features, helping developers build more expressive libraries and eliminate extraneous boilerplate. -Swift-evolution thread: - -## Motivation - -Declaration macros are one part of the [vision for macros in Swift](https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900), which lays out general motivation for introducing macros into the language. They build on the ideas and motivation of [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) to cover a large new set of use cases; we will refer to that proposal often for the basic model of how macros integrate into the language. While expression macros are limited to generating a single expression, declaration macros can create new declarations---functions, properties, types, and so on---greatly expanding the expressiveness of the macro system. Here are a number of potential use cases for declaration macros, many of which will be explored in detail in this proposal: +Attached macros are one part of the [vision for macros in Swift](https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900), which lays out general motivation for introducing macros into the language. They build on the ideas and motivation of [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) and [freestanding macros](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md) to cover a large new set of use cases; we will refer to those proposals often for the basic model of how macros integrate into the language. While freestanding macros are designed as standalone entities introduced by `#`, attached macros are associated with a specific declaration in the program that they can augment and extend. This supports many new use cases, greatly expanding the expressiveness of the macro system: * Creating trampoline or wrapper functions, such as automatically creating a completion-handler version of an `async` function. -* Creating wrapper types from another type, such as forming an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset) from an enum containing flags. +* Creating members of a type based on its definition, such as forming an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset) from an enum containing flags. * Creating accessors for a stored property or subscript, subsuming some of the behavior of [SE-0258 "Property Wrappers"](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md). -* Performing a non-trivial compile-time computation to produce an efficient implementation of a function, such as creating a [perfect hash function](https://en.wikipedia.org/wiki/Perfect_hash_function) for a fixed set of strings. -* Subsuming the `#warning` and `#error` directives introduced in [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) into macros. +* Augmenting members of a type with a new attribute, such as applying a property wrapper to all stored properties of a type. ## Proposed solution -The proposal adds declaration macros, which are expanded to create zero or more new declarations. These declarations can then be referenced from other Swift code, making declaration macros useful for many different kinds of code generation and manipulation. - -There are several different forms of declaration macros, which differ both based on the kinds of inputs to the macro and on the effects the macro can have on the resulting program. This proposal introduces the following kinds of declaration macros: +The proposal adds *attached macros*, so-called because they are attached to a particular declaration. They are written using the custom attribute syntax (e.g., `@addCompletionHandler`) that was introduced for property wrappers, and is also used for result builders . Attached macros can reason about the declaration to which they are attached, and provide additions and changes based on one or more different macro *roles*. Each role has a specific purpose, such as adding members, creating accessors, or adding peers alongside the declaration. A given attached macro can inhabit several different roles, and as such will be expanded multiple times corresponding to do the different roles, which allows the various roles to be composed. For example, an attached macro emulating property wrappers might inhabit both the "peer" and "accessor" roles, allowing it to introduce a backing storage property and also synthesize a getter/setter that go through that backing storage property. Composition of macro roles will be discussed in more depth once the basic macro roles have been established. -* *Freestanding* declaration macros use the same `#`-prefixed syntax as [expression macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md), and can produce zero or more declarations as a result. Freestanding declaration macros can be used anywhere that a declaration can occur, e.g., at the top level, within types and extensions, and in function bodies. -* *Attached* declaration macros use custom attribute syntax (as in [property wrappers](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md) and [result builders](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md)) and are associated with a particular declaration. These macros can augment the declaration to which they are attached in limited ways, as well as introducing "peer" declarations alongside the declaration to which they are attached. - -Both freestanding and attached declaration macros are declared with `macro`, and have [type-checked macro arguments](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#type-checked-macro-arguments-and-results) in the same manner as expression macros, which allows their behavior to be customized. - -All declaration macros are implemented as types that conform to the `DeclarationMacro` protocol. Like the [`Macro` protocol](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-protocols), the `DeclarationMacro` protocol has no requirements, but is used to describe the role of macros more generally. +As with freestanding macros, attached declaration macros are declared with `macro`, and have [type-checked macro arguments](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#type-checked-macro-arguments-and-results) that allow their behavior to be customized. Attached macros are identified with the `@attached` attribute, which also provides the specific role as well as [any names they introduce](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md#up-front-declarations-of-newly-introduced-names). For example, the aforemented macro emulating property wrappers could be declared as follows: ```swift -public protocol DeclarationMacro: Macro { } +@attached(peer, names: prefixed(_)) +@attached(accesor) +macro wrapProperty() ``` -Each kind of declaration macro (freestanding or attached) will have its own protocol that inherits `DeclarationMacro`. - -### Freestanding macros - -Freestanding macros are the most like expression macros. For example, the `warning` directive introduced by [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) can be described as a freestanding macro as follows: +All attached macros are implemented as types that conform one of the protocols that inherits from the `AttachedMacro` protocol. Like the [`Macro` protocol](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-protocols), the `AttachedMacro` protocol has no requirements, but is used to organize macro implementations. ```swift -/// Emits the given message as a warning, as in SE-0196. -@declaration(freestanding) macro warning(_ message: String) +public protocol AttachedMacro: Macro { } ``` -The `@declaration` attribute specifies that this is a declaration macro, which is also freestanding (the `freestanding` argument). Given this macro declaration, the syntax +Each attached macro role will have its own protocol that inherits `AttachedMacro`. -```swift -#warning("unsupported configuration") -``` +#### Peer macros -can be used anywhere a declaration can occur. - -Freestanding macros are implemented as types that conform to the `FreestandingDeclarationMacro` protocol : +Peer macros produce new declarations alongside the declaration to which they are attached. For example, here is a declaration of a macro that introduces a completion-handler version of a given asynchronous function: ```swift -public protocol FreestandingDeclarationMacro: DeclarationMacro { - /// Expand a macro described by the given freestanding macro expansion declaration - /// within the given context to produce a set of declarations. - static func expansion( - of node: MacroExpansionDeclSyntax, in context: inout MacroExpansionContext - ) throws -> [DeclSyntax] -} +@attached(peer, names: overloaded) macro addCompletionHandler ``` -The `MacroExpansionDeclSyntax` node provides the syntax tree for the use site (e.g., `#warning("unsupported configuration")`), and has the same grammar and members as the `MacroExpansionExprSyntax` node introduced in [SE-0382](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion). The grammar parallels `macro-expansion-expression`: - -``` -declaration -> macro-expansion-declaration -macro-expansion-declaration -> '#' identifier generic-argument-clause[opt] function-call-argument-clause[opt] trailing-closures[opt] -``` +This macro uses the `@attached` attribute to indicate that it is an attached peer attribute. The `names` argument specifies that how the names of the peer declarations are created, using an extended form of the scheme introduced with [freestanding macros](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md#up-front-declarations-of-newly-introduced-names). In this case, `overloaded` means that this macro will produce a peer that is overloaded with the declaration to which it is attached, i.e., it has the same base name. -The implementation of a freestanding `warning` declaration macro extracts the string literal argument (producing an error if there wasn't one) and emits a warning. It returns an empty list of declarations: - -```swift -public struct WarningMacro: FreestandingDeclarationMacro { - public static func expansion( - of node: MacroExpansionDeclSyntax, in context: inout MacroExpansionContext - ) throws -> [DeclSyntax] { - guard let messageExpr = node.argumentList.first?.expression?.as(SpecializeExprSyntax.self), - messageExpr.segments.count == 1, - let firstSegment = messageExpr.segments.first, - case let .stringSegment(message) = firstSegment else { - throw SimpleError(node, "warning macro requires a non-interpolated string literal") - } - - context.diagnose(.warning(firstSegment.text)) - return [] - } -} -``` - -### Attached macros - -Attached macros are named as such because they are attached to a specific declaration. They are written using attribute syntax (e.g., `@addCompletionHandler`), and are able to reason about the declaration to which they are attached. There are a number of different forms of attached macros, each of which has a specific effect such as adding members to the declaration (e.g., if it's a type or extension thereof), adding "peer" members alongside the declaration, adding accessors to the declaration, and so on. Each of these kinds of macros is described in the following sections. - -#### Peer declaration macros - -Peer declaration macros produce new declarations alongside the declaration to which they are attached. For example, here is a declaration of a macro that introduces a completion-handler version of a given asynchronous function: - -```swift -@declaration(peers: [.overloaded]) macro addCompletionHandler: Void -``` - -Again, this macro uses the `@declaration` attribute to indicate that it is a declaration macro. The `peers` argument specifies that this macro will generate peer declarations, and how the names of those peer declarations are formed. In this case, our macro will produce a peer that is overloaded with the declaration to which it is attached, i.e., it has the same base name. Later parts of this proposal will go into more depth on the naming of generated declarations , as well as providing rationale for this up-front declaration of macro behavior. - -The macro can be used like this, as an attribute: +The macro can be used like this: ```swift @addCompletionHandler func fetchAvatar(_ username: String) async -> Image? { ... } ``` -Peer declaration macros are implemented via types that conform to the `PeerDeclarationMacro` protocol: +Peer macros are implemented via types that conform to the `PeerMacro` protocol: ```swift -public PeerDeclarationMacro: DeclarationMacro { - /// Expand a macro described by the given custom attribute to +public PeerMacro: AttachedMacro { + /// Expand a macro described by the given attribute to /// produce "peer" declarations of the declaration to which it /// is attached. /// /// The macro expansion can introduce "peer" declarations that /// go alongside the given declaration. static func expansion( - of node: CustomAttributeSyntax, - peersOf declaration: DeclSyntax, - in context: inout MacroExpansionContext + of node: AttributeSyntax, + providingPeersOf declaration: DeclSyntax, + in context: any MacroExpansionContext ) throws -> [DeclSyntax] } ``` @@ -148,7 +84,7 @@ func fetchAvatar(_ username: String, completionHandler: @escaping (Image?) -> Vo } ``` -The actual implementation of this macro involves a lot of syntax manipulation, so we settle for a pseudo-code definition here and leave the complete implementation to the appendix: +The actual implementation of this macro involves a bit of syntax manipulation, so we settle for a pseudo-code definition here and leave the complete implementation to the appendix: ```swift public struct AddCompletionHandler: PeerDeclarationMacro { @@ -169,9 +105,9 @@ public struct AddCompletionHandler: PeerDeclarationMacro { } ``` -#### Member declaration macros +#### Member macros -Member declaration macros allow one to introduce new members into the type or extension to which the macro is attached. For example, we can write a macro that defines static members to ease the definition of an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset). Given: +Member macros allow one to introduce new members into the type or extension to which the macro is attached. For example, we can write a macro that defines static members to ease the definition of an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset). Given: ```swift @optionSetMembers @@ -204,25 +140,25 @@ struct MyOptions: OptionSet { } ``` -The macro itself will be declared as a member declaration macro that defines an arbitrary set of members: +The macro itself will be declared as a member macro that defines an arbitrary set of members: ```swift /// Create the necessary members to turn a struct into an option set. -@declaration(members: [.named("rawValue"), .arbitrary]) macro optionSetMembers: Void +@attached(member, names: names(rawValue), arbitrary]) macro optionSetMembers ``` -The `members` argument specifies that this macro will be defining new members of the declaration to which it is attached. In this case, while the macro knows it will define a member named `rawValue`, there is no way for the macro to predict the names of the static properties it is defining, so it also specifies `.arbitrary` to indicate that it will introduce members with arbitrarily-determined names. +The `member` role specifies that this macro will be defining new members of the declaration to which it is attached. In this case, while the macro knows it will define a member named `rawValue`, there is no way for the macro to predict the names of the static properties it is defining, so it also specifies `arbitrary` to indicate that it will introduce members with arbitrarily-determined names. -Member declaration macros are implemented with types that conform to the `MemberDeclarationMacro` protocol: +Member macros are implemented with types that conform to the `MemberMacro` protocol: ```swift -protocol MemberDeclarationMacro: DeclarationMacro { - /// Expand a macro described by the given custom attribute to +protocol MemberMacro: AttachedMacro { + /// Expand a macro described by the given attribute to /// produce additional members of the given declaration to which /// the attribute is attached. static func expansion( - of node: CustomAttributeSyntax, - membersOf declaration: DeclSyntax, + of node: AttributeSyntax, + providingMembersOf declaration: DeclSyntax, in context: inout MacroExpansionContext ) throws -> [DeclSyntax] } @@ -244,7 +180,7 @@ struct MyStruct { } ``` -The `dictionaryStorage` attached declaration macro would alter the properties of `MyStruct` as follows, adding accessors to each: +The `dictionaryStorage` attached macro would alter the properties of `MyStruct` as follows, adding accessors to each: ```swift struct MyStruct { @@ -279,19 +215,18 @@ struct MyStruct { The macro can be declared as follows: ```swift -@declaration(accessors) macro dictionaryStorage: Void -@declaration(accessors) macro dictionaryStorage(key: String) +@attached(accessor) macro dictionaryStorage(key: String? = nil) ``` -Implementations of accessor macros conform to the AccessorMacro protocol, which is defined as follows: +Implementations of accessor macros conform to the `AccessorMacro` protocol, which is defined as follows: ```swift -protocol AccessorMacro: DeclarationMacro { - /// Expand a macro described by the given custom attribute to +protocol AccessorMacro: AttachedMacro { + /// Expand a macro described by the given attribute to /// produce accessors for the given declaration to which /// the attribute is attached. static func expansion( - of node: CustomAttributeSyntax, + of node: AttributeSyntax, accessorsOf declaration: DeclSyntax, in context: inout MacroExpansionContext ) throws -> [AccessorDeclSyntax] @@ -302,139 +237,6 @@ The implementation of the `dictionaryStorage` macro would create the accessor de The presence of an accessor macro on a stored property removes the initializer. It's up to the implementation of the accessor macro to either diagnose the presence of the initializer (if it cannot be used) or incorporate it in the result. -#### Body macros - -A body macro allows one to create or replace the body of a function, initializer, or closure through syntactic manipulation. Body macros are attached to one of these entities, e.g., - -```swift -@traced(logLevel: 2) -func myFunction(a: Int, b: Int) { ... } -``` - -where the `traced` macro is declared as something like: - -```swift -@declaration(body) macro traced(logLevel: Int = 0) -``` - -and implemented in a conformance to the `BodyMacro` protocol: - -```swift -protocol BodyMacro: Macro { - /// Expand a macro described by the given custom attribute to - /// produce or modify a body for the given entity to which - /// the attribute is attached. - static func expansion( - of node: CustomAttributeSyntax, - bodyOf entity: some WithCodeBlockSyntax, - in context: inout MacroExpansionContext - ) throws -> CodeBlockSyntax -} -``` - -The `WithCodeBlockSyntax` protocol describes all entities that have an optional "body". The `traced` macro could inject a log-level check and a call to log the values of each of the parameters, e.g., - -```swift -if shouldLog(atLevel: 2) { - log("Entering myFunction((a: \(a), b: \(b)))") -} -``` - -Body macros will only be applied when the body is required, e.g., to generate code. A body macro will be applied whether the entity has an existing body or not; if the entity does have an existing body, it will be type-checked before the macro is invoked, as with other macro arguments. - -#### Default witness macros - -Swift provides default "witness" synthesis for a number of protocols in the standard library, including `Equatable`, `Hashable`, `Encodable`, and `Decodable`. This behavior is triggered when a type has a conformance to a known protocol, and there is no suitable implementation for one of the protocol's requirements, e.g., - -```swift -struct Point: Equatable { - var x: Int - var y: Int - - // no suitable function - // - // - // - // to satisfy the protocol requirement, so the compiler creates one like the following - // - -} -``` - -There is no suitable function to meet the protocol requirement - -```swift -static func ==(lhs: Self, rhs: Self) -> Bool -``` - -so the compiler as bespoke logic to synthesize the following: - -```swift - static func ==(lhs: Point, rhs: Point) -> Bool { - lhs.x == rhs.x && lhs.y == rhs.y - } -``` - -Default witness macros bring this capability to the macro system, by allowing a macro to define a witness to satisfy a requirement when there is no suitable witness. Consider an `equatableSyntax` macro declared as follows: - -```swift -@declaration(witness) -macro equatableSynthesis: Void -``` - -This default-witness macro would be written on the protocol requirement itself, i.e., - -```swift -protocol Equatable { - @equatableSynthesis - static func ==(lhs: Self, rhs: Self) -> Bool - - static func !=(lhs: Self, rhs: Self) -> Bool -} -``` - -The macro type would implement the following protocol: - -```swift -protocol DefaultWitnessMacro: DeclarationMacro { - /// Expand a macro described by the given custom attribute to - /// produce a witness definition for the requirement to which - /// the attribute is attached. - static func expansion( - of node: CustomAttributeSyntax, - witness: DeclSyntax, - conformingType: TypeSyntax, - storedProperties: [StoredProperty], - in context: inout MacroExpansionContext - ) throws -> DeclSyntax -} -``` - -The contract with the compiler here is interesting. The compiler would use the `@equatableSynthesis` macro to define the witness only when there is no other potential witness. The compiler will then produce a declaration for the witness based on the requirement, performing substitutions as necessary to (e.g.) replace `Self` with the conforming type, and provide that declaration via the `witness` parameter. For `Point`, the `==` declaration would look like this: - -```swift -static func ==(lhs: Point, rhs: Point) -> Bool -``` - -The `expansion` operation then augments the provided `witness` with a body, and could perform other adjustments if necessary, before returning it. The resulting definition will be inserted as a member into the conforming type (or extension), wherever the protocol conformance is declared. This approach gives a good balance between making macro writing easier, because the compiler is providing a witness declaration that will match the requirement, while still allowing the macro implementation freedom to alter that witness as needed. Its design also follows how witnesses are currently synthesized in the compiler today, which has fairly well-understood implementation properties (at least, to compiler implementers). - -The `conformingType` parameter provides the syntax of the conforming type, which can be used to refer to the type anywhere in the macro. The `storedProperties` parameter provides the set of stored properties of the conforming type, which are needed for many (most?) kinds of witness synthesis. The `StoredProperty` struct is defined as follows: - -```swift -struct StoredProperty { - /// The stored property syntax node. - var property: VariableDeclSyntax - - /// The original declaration from which the stored property was created, if the stored property was - /// synthesized. - var original: Syntax? -} -``` - -The `property` field is the syntax node for the stored property. Typically, this is the syntax node as written in the source code. However, some stored properties are formed in other ways, e.g., as the backing property of a property wrapper (`_foo`) or due to some other macro expansion (member, peer, freestanding, etc.). In these cases, `property` refers to the syntax of the generated property, and `original` refers to the syntax node that caused the stored property to be generated. This `original` value can be used, for example, to find information from the original declaration that can affect the synthesis of the default witness. - -Providing stored properties to this expansion method does require us to introduce a limitation on default-witness macro implementations, which is that they cannot themselves introduce stored properties. This eliminates a potential circularity in the language model, where the list of stored properties could grow due to expansion of a macro, thereby potentially invalidating the results of already-expanded macros that saw a subset of the stored properties. Note that directly preventing default-witness macros from defining stored properties isn't a complete solution, because one could (for example) have a default-witness macro produce a witness function that itself involves a peer-declaration macro that introduces a stored property. Such problems will be detected as a dependency cycle in the compiler and reported as an error. - #### Member attribute macros Member declaration macros allow one to introduce new member declarations within the type or extension to which they apply. In contrast, member *attribute* macros allow one to modify the member declarations that were explicitly written within the type or extension by adding new attributes to them. Those new attributes could then refer to other macros, such as peer or accessor macros, or builtin attributes. As such, they are primarily a means of *composition*, since they have fairly little effect on their own. @@ -444,8 +246,8 @@ Member attribute macros allow a macro to provide similar behavior to how many bu As an example, we'll define a macro that partially mimics the behavior of the `@objcMembers` attributes introduced long ago in [SE-0160](https://github.com/apple/swift-evolution/blob/main/proposals/0160-objc-inference.md#re-enabling-objc-inference-within-a-class-hierarchy). Our `myObjCMembers` macro is a member-attribute macro: ```swift -@declaration(memberAttribute) -macro myObjCMembers: Void +@attached(memberAttribute) +macro myObjCMembers ``` The implementation of this macro will add the `@objc` attribute to every member of the type or extension, unless that member either already has an `@objc` macro or has `@nonobjc` on it. For example, this: @@ -479,19 +281,22 @@ extension MyClass { Member-attribute macro implementations should conform to the `MemberAttributeMacro` protocol: ```swift -protocol MemberAttributeMacro: Macro { +protocol MemberAttributeMacro: AttachedMacro { /// Expand a macro described by the given custom attribute to /// produce additional attributes for the members of the type. static func expansion( - of node: CustomAttributeSyntax, - memberAttributesOf declaration: DeclSyntax, - in context: inout MacroExpansionContext - ) throws -> [DeclSyntax: [AttributeSyntax]] + of node: AttributeSyntax, + attachedTo declaration: DeclSyntax, + providingAttributesFor member: DeclSyntax, + in context: any MacroExpansionContext + ) throws -> [AttributeSyntax] } ``` The `expansion` operation accepts the attribute syntax `node` for the spelling of the member attribute macro and the declaration to which that attribute is attached (i.e., the type or extension). The implementation of the macro can walk the members of the `declaration` to determine which members require additional attributes. The returned dictionary will map from those members to the additional attributes that should be added to each of the members. +## Detailed design + ### Composing macro roles A given macro can have several different roles, allowing the various macro features to be composed. Each of the roles is considered independently, so a single use of a macro in source code can result in different macro expansion functions being called. These calls are independent, and could even happen concurrently. As an example, let's define a macro that emulates property wrappers fairly closely. The property wrappers proposal has an example for a [clamping property wrapper](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md#clamping-a-value-within-bounds): @@ -535,8 +340,8 @@ struct Color { Instead, let's implement this as a macro: ```swift -@declaration(peers: [.prefixed("_")]) -@declaration(accessors) +@attached(peer, prefixed(_)) +@attached(accessor) macro Clamping(min: T, max: T) = #externalMacro(module: "MyMacros", type: "ClampingMacro") ``` @@ -557,7 +362,7 @@ private var _red: Int = { }() ``` -Which is implemented by having `ClampingMacro` conform to `PeerDeclarationMacro`: +Which is implemented by having `ClampingMacro` conform to `PeerMacro`: ```swift enum ClampingMacro { } @@ -565,8 +370,8 @@ enum ClampingMacro { } extension ClampingMacro: PeerDeclarationMacro { static func expansion( of node: CustomAttributeSyntax, - peersOf declaration: DeclSyntax, - in context: inout MacroExpansionContext + providingPeersOf declaration: DeclSyntax, + in context: any MacroExpansionContext ) throws -> [DeclSyntax] { // create a new variable declaration that is the same as the original, but... // - prepend an underscore to the name @@ -575,9 +380,7 @@ extension ClampingMacro: PeerDeclarationMacro { } ``` - - -And introduces accesssors such as +And introduces accessors such as ```swift get { return _red } @@ -601,7 +404,7 @@ by conforming to `AccessorMacro`: extension ClampingMacro: AccessorMacro { static func expansion( of node: CustomAttributeSyntax, - accessorsOf declaration: DeclSyntax, + providingAccessorsOf declaration: DeclSyntax, in context: inout MacroExpansionContext ) throws -> [AccessorDeclSyntax] { let originalName = /* get from declaration */, @@ -635,38 +438,24 @@ extension ClampingMacro: AccessorMacro { This formulation of `@Clamping` offers some benefits over the property-wrapper version: we don't need to store the min and max values as part of the backing storage (so the presence of `@Clamping` doesn't add any storage), nor do we need to define a new type. More importantly, it demonstrates how the composition of different macro roles together can produce interesting effects. -### Up-front declarations of newly-introduced names +### Specifying newly-introduced names -Declaration macros require one to declare the names of entities that will be declared by the expansion of the macro. These can involve member and/or peer declaration names, and are provided as an array consisting of a few different kinds: +The proposal for freestanding macros introduced the need to [specify macro-introduced names](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md#up-front-declarations-of-newly-introduced-names) as a way of isolating the effect that macros can have on the compilation process. Attached macros introduce a few more ways in which one can describe the names of declarations that are produced by a macro: -* Declarations with a specific fixed name: `.named("rawValue")` -* Declarations that have the same base name as the declaration to which the macro is attached, and are therefore overloaded with it: `.overloaded`. -* Declarations whose name is formed by adding a prefix to the name of the declaration to which the macro is attached: `.prefixed("_")` -* Declarations whose name is formed by adding a suffix to the name of the declaration to which the macro is attached: `.suffixed("_docinfo")`. -* Declarations whose names cannot be described by any of the simple rules above: `.arbitrary`. +* Declarations that have the same base name as the declaration to which the macro is attached, and are therefore overloaded with it: `overloaded`. +* Declarations whose name is formed by adding a prefix to the name of the declaration to which the macro is attached: `prefixed("_")` +* Declarations whose name is formed by adding a suffix to the name of the declaration to which the macro is attached: `suffixed("_docinfo")`. -A declaration macro can only introduce new declarations whose names are covered by the kinds provided, or have their names generated via `MacroExpansionContext.createUniqueLocalName`. This ensures that, in most cases (where `.arbitrary` is not specified) the Swift compiler and related tools can reason about the set of names that will introduced by a given use of a declaration macro without having to expand the macro, which can reduce the compile-time cost of macros and improve incremental builds. +A macro can only introduce new declarations whose names are covered by the kinds provided, or have their names generated via `MacroExpansionContext.createUniqueame`. This ensures that, in most cases (where `arbitrary` is not specified), the Swift compiler and related tools can reason about the set of names that will introduced by a given use of a macro without having to expand the macro, which can reduce the compile-time cost of macros and improve incremental builds. -## Detailed design - -### Macros in the Standard Library +### Ordering of macro expansions -#### SE-0196 `warning` and `error` +The freestanding macros proposal describes the [visibility of macro-introduced names](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md#visibility-of-names-used-and-introduced-by-macros), which provides "outside-in" ordering rules where macros in outer scopes are expanded before those in inner scopes. The same general rules apply to attached macros, so macros attached to a type or extension will be expanded before macros on the members of that type or extension are. -The `#warning` and `#error` directives introduced in [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md): can be implemented directly as freestanding macros: - -```swift -/// Emit a warning containing the given message. -@declaration(.freestanding) macro warning(_ message: String) - -/// Emit an error containing the given message -@declaration(.freestanding) macro error(_ message: String) -``` +When there are multiple attached macros on a single declaration (e.g., `@macro1 @macro2 func f()`), and those macros have the same role, the macros will be expanded in left-to-right order. ## Source compatibility -Freestanding declaration macros use the same syntax introduced for expression macros, which were themselves a pure extension without an impact on source compatibility. There is a syntactic ambiguity between expression and freestanding declaration macros, i.e., `#warning("watch out")` within a function body could be either an expression or a declaration. The distinction will need to be determined semantically, by determining whether the named macro is either an expression or a freestanding declaration macro. - Attached declaration macros use the same syntax introduced for custom attributes (such as property wrappers), and therefore do not have an impact on source compatibility. ## Effect on ABI stability @@ -685,21 +474,169 @@ Attached declaration macro implementations are provided with information about t An alternative approach would be to allow the macro implementation to directly alter the declaration to which the macro is attached. This would provide a macro implementation with greater flexibility to affect the declarations it is attached to. However, it means that the resulting declaration could vary drastically from what the user wrote, and would preventing the compiler from making any determinations about what the declaration means before expanding the macro. This could have detrimental effects on compile-time performance (one cannot determine anything about a declaration until the macros have been run on it) and might also prevent macros from accessing information about the actual declaration in the future, such as the types of parameters. -It might be possible to provide a macro implementation API that is expressed in terms of mutation on the original declaration, but restrict the permissible changes to those that can be handled by the implementation. For example, one could "diff" the syntax tree provided to the macro implementation and the syntax tree produced by the macro implementation to identify changes: those changes that are acceptable would be recorded into something like `AttachedDeclarationExpansion`, and any other differences would be reported as errors. Such a design could be layered on top of the design proposed here, e.g., with an `AttachedDeclarationExpansion` initializer that accepts the original declaration and the rewritten declaration. +It might be possible to provide a macro implementation API that is expressed in terms of mutation on the original declaration, but restrict the permissible changes to those that can be handled by the implementation. For example, one could "diff" the syntax tree provided to the macro implementation and the syntax tree produced by the macro implementation to identify changes, and return those changes that are acceptable to the compiler. ## Future directions -(nothing just yet) +### Additional attached macro roles + +There are numerous ways in which this proposal could be extended to provide new macro roles. Each new macro role would introduce a new role kind to the `@attached` attribute, along with a corresponding protocol. Here are several possibilities, omitted from this proposal primarily to reduce scope. + +#### Body macros + +A body macro would allow one to create or replace the body of a function, initializer, or closure through syntactic manipulation. Body macros are attached to one of these entities, e.g., + +```swift +@traced(logLevel: 2) +func myFunction(a: Int, b: Int) { ... } +``` -## Revision history +where the `traced` macro is declared as something like: -Revisions from the first pitch: +```swift +@attached(body) macro traced(logLevel: Int = 0) +``` -* Split peer/member/accessor macro implementations into separate protocols and attribute spellings, so the compiler can query them in a more fine-grained manner. -* Added "body" macros as a separate macro role. -* Added default-witness macros. -* Added member-attribute macros. -* Add example showing composition of different macro roles for the same macro to effect property-wrappers behavior. +and implemented in a conformance to the `BodyMacro` protocol: + +```swift +protocol BodyMacro: AttachedMacro { + /// Expand a macro described by the given custom attribute to + /// produce or modify a body for the given entity to which + /// the attribute is attached. + static func expansion( + of node: CustomAttributeSyntax, + providingBodyOf entity: some WithCodeBlockSyntax, + in context: inout MacroExpansionContext + ) throws -> CodeBlockSyntax +} +``` + +The `WithCodeBlockSyntax` protocol describes all entities that have an optional "body". The `traced` macro could inject a log-level check and a call to log the values of each of the parameters, e.g., + +```swift +if shouldLog(atLevel: 2) { + log("Entering myFunction((a: \(a), b: \(b)))") +} +``` + +Body macros will only be applied when the body is required, e.g., to generate code. A body macro will be applied whether the entity has an existing body or not; if the entity does have an existing body, it will be type-checked before the macro is invoked, as with other macro arguments. + +#### Conformance macros + +Conformance macros could introduce protocol conformances to the type or extension to which they are attached. For example, this could be useful when composed with macro roles that create other members, such as a macro that both adds a protocol conformance and also a stored property required by that conformance. For example, one could extend the `dictionaryStorage` macro from earlier in the proposal to introduce the dictionary when placed on the struct itself: + +```swift +protocol HasDictionaryStorage { + var storage: [AnyHashable: Any] { get set } +} + +@dictionaryStorage +struct MyStruct /* macro introduces conformance to HasDictionaryStorage */ { + // macro introduces this property, which satisfies the HasDictionarySrorage.storage requirement + // var storage: [AnyHashable: Any] = [:] + + // macro introduces @dictionaryStorage attribute on members that don't already have one + // @dictionaryStorage + var name: String + + @dictionaryStorage(key: "birth_date") + var birthDate: Date? +} +``` + +#### Default witness macros + +Swift provides default "witness" synthesis for a number of protocols in the standard library, including `Equatable`, `Hashable`, `Encodable`, and `Decodable`. This behavior is triggered when a type has a conformance to a known protocol, and there is no suitable implementation for one of the protocol's requirements, e.g., + +```swift +struct Point: Equatable { + var x: Int + var y: Int + + // no suitable function + // + // + // + // to satisfy the protocol requirement, so the compiler creates one like the following + // + +} +``` + +There is no suitable function to meet the protocol requirement + +```swift +static func ==(lhs: Self, rhs: Self) -> Bool +``` + +so the compiler as bespoke logic to synthesize the following: + +```swift + static func ==(lhs: Point, rhs: Point) -> Bool { + lhs.x == rhs.x && lhs.y == rhs.y + } +``` + +Default witness macros would bring this capability to the macro system, by allowing a macro to define a witness to satisfy a requirement when there is no suitable witness. Consider an `equatableSyntax` macro declared as follows: + +```swift +@declaration(witness) +macro equatableSynthesis +``` + +This default-witness macro would be written on the protocol requirement itself, i.e., + +```swift +protocol Equatable { + @equatableSynthesis + static func ==(lhs: Self, rhs: Self) -> Bool + + static func !=(lhs: Self, rhs: Self) -> Bool +} +``` + +The macro type would implement the following protocol: + +```swift +protocol DefaultWitnessMacro: AttachedMacro { + /// Expand a macro described by the given custom attribute to + /// produce a witness definition for the requirement to which + /// the attribute is attached. + static func expansion( + of node: CustomAttributeSyntax, + witness: DeclSyntax, + conformingType: TypeSyntax, + storedProperties: [StoredProperty], + in context: inout MacroExpansionContext + ) throws -> DeclSyntax +} +``` + +The contract with the compiler here is interesting. The compiler would use the `@equatableSynthesis` macro to define the witness only when there is no other potential witness. The compiler will then produce a declaration for the witness based on the requirement, performing substitutions as necessary to (e.g.) replace `Self` with the conforming type, and provide that declaration via the `witness` parameter. For `Point`, the `==` declaration would look like this: + +```swift +static func ==(lhs: Point, rhs: Point) -> Bool +``` + +The `expansion` operation then augments the provided `witness` with a body, and could perform other adjustments if necessary, before returning it. The resulting definition will be inserted as a member into the conforming type (or extension), wherever the protocol conformance is declared. This approach gives a good balance between making macro writing easier, because the compiler is providing a witness declaration that will match the requirement, while still allowing the macro implementation freedom to alter that witness as needed. Its design also follows how witnesses are currently synthesized in the compiler today, which has fairly well-understood implementation properties (at least, to compiler implementers). + +The `conformingType` parameter provides the syntax of the conforming type, which can be used to refer to the type anywhere in the macro. The `storedProperties` parameter provides the set of stored properties of the conforming type, which are needed for many (most?) kinds of witness synthesis. The `StoredProperty` struct is defined as follows: + +```swift +struct StoredProperty { + /// The stored property syntax node. + var property: VariableDeclSyntax + + /// The original declaration from which the stored property was created, if the stored property was + /// synthesized. + var original: Syntax? +} +``` + +The `property` field is the syntax node for the stored property. Typically, this is the syntax node as written in the source code. However, some stored properties are formed in other ways, e.g., as the backing property of a property wrapper (`_foo`) or due to some other macro expansion (member, peer, freestanding, etc.). In these cases, `property` refers to the syntax of the generated property, and `original` refers to the syntax node that caused the stored property to be generated. This `original` value can be used, for example, to find information from the original declaration that can affect the synthesis of the default witness. + +Providing stored properties to this expansion method does require us to introduce a limitation on default-witness macro implementations, which is that they cannot themselves introduce stored properties. This eliminates a potential circularity in the language model, where the list of stored properties could grow due to expansion of a macro, thereby potentially invalidating the results of already-expanded macros that saw a subset of the stored properties. Note that directly preventing default-witness macros from defining stored properties isn't a complete solution, because one could (for example) have a default-witness macro produce a witness function that itself involves a peer-declaration macro that introduces a stored property. Such problems will be detected as a dependency cycle in the compiler and reported as an error. ## Appendix @@ -709,7 +646,7 @@ Revisions from the first pitch: public struct AddCompletionHandler: PeerDeclarationMacro { public static func expansion( of node: CustomAttributeSyntax, - peersOf declaration: DeclSyntax, + providingPeersOf declaration: DeclSyntax, in context: inout MacroExpansionContext ) throws -> [DeclSyntax] { // Only on functions at the moment. We could handle initializers as well From 793ab00f687b6d2932bd402f3502ca3253f651a1 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 18 Jan 2023 15:05:14 -0800 Subject: [PATCH 2894/4563] Address feedback --- proposals/NNNN-package-access-modifier.md | 116 ++++++++++++++++------ 1 file changed, 83 insertions(+), 33 deletions(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index d8bff43920..728299eba5 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -2,20 +2,26 @@ * Proposal: [SE-NNNN](NNNN-package-access-modifier.md) * Authors: [Ellie Shin](https://github.com/elsh), [Alexis Laferriere](https://github.com/xymus) -* Review Manager: TBD -* Status: Awaiting review -* Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/61546) -* Review: [pitch](https://forums.swift.org/t/new-access-modifier-package/61459) +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Awaiting review** +* Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/62700), [apple/swift#62704](https://github.com/apple/swift/pull/62704), [apple/swift#62652](https://github.com/apple/swift/pull/62652) +* Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) ## Introduction -This proposal introduces `package` as a new access modifier. Currently, to access a symbol in another module, that symbol needs to be declared `public`. It allows access across modules within the same library but also outside of the library, which is undesirable. The new access modifier enables a more fine control over the visibility scope of such symbols. +This proposal introduces `package` as a new access modifier. Currently, to access a symbol in another module, that symbol needs to be declared `public`. However, a symbol being `public` allows it to be accessed from any module at all, both within a package and from outside of a package, which is sometimes undesirable. We need a new access modifier to enable a more fine control over the visibility scope of such symbols. ## Motivation -Packages are often composed of multiple modules; packages exist as a way to organize modules in Swift, and organizing often involves splitting a module into smaller modules. For example, a module containing internal helper APIs can be split into a utility module only with the helper APIs and the other module(s) containing the rest. In order to access the helper APIs, however, the helper APIs need to be made public. The side effect of this is that they can “leak” to a client that should not have access to those symbols. Besides the scope of visibility, making them public also has an implication on the code size and performance. +At the most basic level, every Swift program is just a collection of declarations: functions, types, variables, and so on. In principle, every level of organization above this is arbitrary; all of those declarations could be piled into a single file, compiled, and run. In reality, Swift programs are organized into separate files, directories, libraries, and so on. At each level, this organization reflects programmer judgment about relationships, both in the code and in how it is developed. -For example, here’s a scenario where App depends on modules from package 'gamePkg'. +As a language, Swift recognizes some of these levels. Modules are the smallest unit of library structure, with an independent interface and non-cyclic dependencies, and it makes sense for Swift to recognize that in both namespacing and access control. Files are the smallest grouping beneath that and are often used to collect tightly-related declarations, so they also make sense to respect in access control. + +Packages, as expressed by the Swift Package Manager, are a unit of code distribution. Some packages contain just a single module, but it's frequently useful to split a package's code into multiple modules. For example, when a module contains some internal helper APIs, those APIs can be split out into a utility module and maybe reused by other modules or packages. + +However, because Swift does not recognize organizations of code above the module level, it is not possible to create APIs like this that are purely internal to the package. To be usable from other modules within the package, the API must be public, but this means it can also be used outside of the package. This allows clients to form unwanted source dependencies on the API. It also means the built module has to export the API, which has negative implications for code size and performance. + +For example, here’s a scenario where a client (App) has access to a utility API from a package (gamePkg) it depends on. ``` App (Xcode project or appPkg) @@ -57,20 +63,31 @@ print(engine.stats) // Can access `stats` as intended In the above scenario, App can import Engine (a utility module in 'gamePkg') and access its helper API directly, even though the API is not intended to be used outside of its package. +These costs are particularly unfortunate given that packages are a unit of code distribution. Boundaries between packages often reflect divisions between development teams; different packages are developed on different schedules by different groups of people. These boundaries are therefore especially important to enforce, because accidental dependencies can require extra work and cooperation to detangle. -## Proposed solution -A current workaround for the above scenario is to use `@_spi`, `@_implemenationOnly`, or `@testable`. However, they have caveats. The `@_spi` requires a group name, which makes it verbose to use and harder to keep track of, and `@_implementationOnly` can be too limiting as we want to be able to restrict access to only portions of APIs. The `@testable` elevates all internal symbols to public, which leads to an increase of the binary size and the shared cache size. If there are multiple symbols with the same name from different modules, they will clash and require module qualifiers everywhere. It is hacky and is strongly discouraged for use. +## Proposed solution -We want a solution that will clearly communicate that the visibility scope is somewhere between `internal` and `public`, and is limited to modules within a package, thus we propose a new access modifier `package`. +Our goal is to introduce a mechanism to Swift to recognize a package as a unit in the aspect of access control. We proposed to do so by introducing a new access modifier `package`. The `package` access modifier will allow accessing symbols from outside of its defining module as long as they are within the same package. This will help set clear boundaries between packages. +This also paves the path to include a package name in the namespace in the future, which will help disambiguate modules with the same name. ## Detailed design +### `package` keyword + +A new keyword `package` is introduced. It is a contextual keyword, i.e. a symbol can be named `package`. +For example, the following is allowed. + +``` +package var package: String {...} +``` + +This is similar to `open`, which is also a contextual keyword. ### Declaration Site -Using the scenario above, the helper API `run` can be declared `package`. +The `package` keyword is added at the declaration site. Using the scenario above, the helper API `run` can be declared with the new access modifier like so: ``` [Engine] @@ -82,7 +99,7 @@ public struct MainEngine { } ``` -The `package` access modifier can be added to any types where an existing access modifier can be added, e.g. `class`, `struct`, `enum`, `func`, `var`, `protocol`, etc. +The `package` access modifier can be added to any types where an existing access modifier can be added, e.g. `class`, `struct`, `enum`, `func`, `var`, `protocol`, etc. It is less restrictive than `internal` and more restrictive than `public`. For example, a `public` function cannot have `internal` or `package` parameters or return type in its signature, and a `package` function cannot have `internal` parameters or return type in its signature. ### Use site @@ -112,65 +129,98 @@ engine.run() // Error: cannot find `run` in scope ``` -During type check a package name that's stored in an imported module binary will be looked up and compared with the current module's package name to allow or disallow access to a package symbol from that module. More details are explained below. +### Package names +A package name is a unique string with c99 identifier characters, and is passed down to Swift frontend via a new flag `-package-name`. It is then stored in the module binary and used to compare with package names of other modules to determine if they are part of the same package. -### Lookup by package name +If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. -A new flag `-package-name` will be introduced to enable grouping modules per package. In Swift Package Manager, the input to the flag will be a package identity and will be passed down automatically. Each package identity is unique and can contain non-alphanumeric characters such as a hyphen and a dot (currently any URL valid characters are allowed); such characters will be transposed into a c99 identifier. Other build systems such as Xcode and Basel will need to pass a new command-line argument `-package-name` to the build command; the input may be Reverse-DNS package names so it can prevent potential clashes with other project or package names. +Here's an example of how a package name is passed to a commandline invocation. ``` -[Engine] swiftc -module-name Engine -package-name gamePkg ... +swiftc -module-name Engine -package-name gamePkg ... [Game] swiftc -module-name Game -package-name gamePkg ... [App] swiftc App -package-name appPkg ... ``` -When building the Engine module, the package name 'gamePkg' and package symbols will be stored in the Engine.swiftmodule. When building Game, the input to its package name 'gamePkg' will be compared with Engine's package name; since they match, access to package symbols is allowed. When building App, the name comparison shows 'appPkg' is different from its dependency module's so access to package symbols will be deined, which is what we want. - -If `-package-name` is not passed, but `package` access modifier is used, then an error will be thrown; Building files that do not have `package` symbols should continue to work without needing to pass in `-package-name`. +When building the Engine module, the package name 'gamePkg' and package symbols is stored in the Engine binary. When building Game, the input to its package name 'gamePkg' is compared with Engine's package name; since they match, access to package symbols is allowed. When building App, the name comparison shows 'appPkg' is different from its dependency module's so access to package symbols is deined, which is what we want. +Swift Package Manager has a package identity per package, an identifier that's verified to be unique via a registry, and it will pass it down automatically. Other build systems such as Bazel can introduce a new build setting to set the value for a package name. Since it needs to be unique, a reverse-DNS name could be used to avoid clashing. ### Exportability -The package symbols will be stored in .swiftmodule but not in .swiftinterface. The package name will be stored in both .swiftmodule and .swiftinterface, but only the swiftmodule will be loaded when building a same-package module; this will prevent loading a fall-back module (swiftinterface) which does not contain package symbols. +Package symbols will be stored in .swiftmodule only, and not in the public interface file (.swiftinterface). We plan to introduce a .package.swiftinterface that contains package symbols, similar to a .private.swiftinterface which contains SPI symbols. We will store information on whether the .swiftmodule was built from a .package.swiftinterface into the .swiftmodule. We will also store the package name in both .swiftmodule and .swiftinterface. This is to enforce loading a .swiftmodule instead of .swiftinterface when building modules in the same package. -The exportability rule at the declaration site will be similar to the the existing behavior; for example, a public class is not allowed to inherit a package class, and a public func is not allowed to have a package type in its signature. +`package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the function: they must be `open`, `public`, `package`, or `@usableFromInline`. Note that `@usableFromInline` allows the use of a symbol from `@inlinable` functions whether they're `package` or `public`. `@usableFromPackageInline` will be introduced to export a symbol only for use by `@inlinable package` functions. -The `@inlinable` will be applicable to `package`; we plan to introduce`@usableFromPackageInline` to allow references from `@inlinable package` and not from `@inlinable public`. +We plan to introduce an option to hide package symbols. By default, all package symbols will be exported in the final library/executable. -Package symbols will be exported in the final libraries/executables and emitted in llvm.used; we plan to introduce a build setting that lets users decide whether to hide package symbols if statically linked. +### Resiliency +Library evolution makes modules resilient. We can incorporate a package name into a resiliency check and bypass it if modules are in the same package. This will remove the need for resilience overhead such as indirection and language requirements such as `@unknown default` for an unfrozen `enum`. ### Subclassing / Overrides -A `package` class will not be allowed to be subclassed from another module, thus a `package` class member will not be allowed to be overidden either. +Below is a matrix showing what access levels currently allow accessing or subclassing. -This follows the behavior and optimization model for `public`; a `public` class has no subclasses outside of its defining module, and a `public` class member has no overrides outside of its defining module. The optimizer can take advantage of this information in whole-module builds to replace dynamic dispatch with static dispatch among other things. +``` + Accessible in... + ___anywhere___|____module_____ + | anywhere | open | (illegal) +Subclassable | module | public | internal + in... | nowhere | public final | internal final -It also allows a linear progression from `private` through `open` where each step incrementally offers capabilities to a wider range of clients. If `package` were to allow subclassing/overrides outside of its defining modules, making a `package` class `public` would break subclasses defined in other modules within the same package. +``` + +With `package` as a new access modifier, the matrix is modified like so: + +``` + Accessible in... + ___anywhere____|_____package____|____module_____ + | anywhere | open | (illegal) | (illegal) +Subclassable | package | open | packageopen | (illegal) + in... | module | public | package | internal + | nowhere | public final | package final | internal final + +``` -If a `package` class needs to be subclassed, it will need to be declared `open`, or be converted to a protocol, which is a recommended practice. +Some of the boxes are naturally illegal: it of course doesn't make sense for a class to be subclassable when it can't access at all. +A `package` class is accessible from outside of its defining modules as long as they are within the same package. Similar to `public`, it can only be subclass-able within its defining module. If the `package` class needs to be subclassed outside of its defining module, a `package open` or `packageopen` (exact name TBD) needs to be used. This proposal focuses on `package`; the subclassing functionality (and the exact name) will be addressed in the future. + +The reason for not allowing both accessing and subclassing under `package` is as follows: + +We want to allow a linear progression from `private` through `open` where each step incrementally offers capabilities to a wider range of clients. If `package` were to allow subclassing/overrides outside of its defining modules, making a `package` class `public` would break subclasses defined in other modules in the same package. + +We want to follow the optimization model for `public`; a `public` class has no subclasses outside of its defining module, and a `public` class member has no overrides outside of its defining module. The optimizer can take advantage of this information in whole-module builds to replace dynamic dispatch with static dispatch among other things. ## Future Directions -Limiting the scope of visibility per package can open up a whole lot of optimization opportunities. A package containing several modules can be treated as a resilience domain. +Subclassing +* A separate access modifier will likely need to be introduced to allow subclassing and overriding within a package. The reason for a new access modifier is described in the Details section. -Even with the library evolution enabled, modules within a package could be treated non-resilient. If same-package clients need access to swiftmodules, they don't need to be independently rebuildable and could have an unstable ABI; they could avoid resilience overhead and language rules such as the `@unknown default` requirement. +Namespacing +* A package name might potentially be added to a mangled name. This could help disambiguate module names that clash. +Optimizations +* A package containing several modules can be treated as a resilience domain. If same-package clients need access to module binaries, they don't need to be independently rebuildable and could have an unstable ABI; they could avoid resilience overhead and unnecessary language rules. + +* By default package symbols are exported in the final libraries/executables. We plan to introduce a build setting that allows users to decide whether to hide package symbols for staticaly linked libraries. Enabling package symbols to be hidden would help with size optimizations. ## Source compatibility -The changes is additive, so it's not source breaking. +A new keyword `package` is added as a new access modifier. It is a contextual keyword and is an additive change. Symbols that are named `package` should not require renaming, and the source code as is should continue to work. ## Effect on ABI stability -Does not impact ABI stability. +Boundaries between separately-built modules within a package are still potentially ABI boundaries. The ABI for package symbols is not different from the ABI for public symbols, although in the future we plan to add an option to not export package symbols that can be resolved within an image. ## Alternatives considered -A current workaround is to use `@_spi`, `@_implemenationOnly`, or `@testable`. Each option has caveats. The `@_spi` requires a group name, which makes it verbose to use and harder to keep track of, and `@_implementationOnly` can be too limiting as we want to be able to restrict access to only portions of APIs. The `@testable` elevates all internal symbols to public, which leads to an increase of the binary size and the shared cache size. If there are multiple symbols with the same name from different modules, they will clash and require module qualifiers everywhere. It is hacky and is strongly discouraged for use. +A current workaround for the scenario in the Motivation is to use `@_spi` or `@_implemenationOnly`. Each option has caveats. The `@_spi` requires a group name, which makes it verbose to use and harder to keep track of, and `@_implementationOnly` can be too limiting as we want to be able to restrict access to only portions of APIs. There are also hacky workarounds such as `@testable` and the `-disable-access-control` flag. They elevate all `internal` (and `private` with the flag) symbols to `public`. These options are unstable and also quickly lead to an increase of the binary and the shared cache size, not to mention symbol name clashes. + +Instead of a package, submodules could have been considered. Introducing another "umbrella" module or dividing a module into "sub" modules, however, would require much more significant work and would be both source and ABI breaking. A package already exists as a unit corresponding to a group of modules, and treating it as a unit above a module in the grouping hierarchy enables a nautral transition into a submodule-like structure. ## Acknowledgments -Alexis Laferriere, Doug Gregor, Becca Royal-Gordon, Allan Shortlidge, Artem Chikin, and Xi Ge provided helpful feedback and analysis. Alexis Laferriere has been part of the discussion from the beginning and reviewed implementation of the prototype. +Doug Gregor, Becca Royal-Gordon, Allan Shortlidge, Artem Chikin, and Xi Ge provided helpful feedback and analysis as well as code reviews on the implementation. From 33951e74c833433546550dde4730a1019faaab11 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 18 Jan 2023 22:45:15 -0800 Subject: [PATCH 2895/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 728299eba5..7ba3e15b0c 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -205,7 +205,7 @@ Namespacing Optimizations * A package containing several modules can be treated as a resilience domain. If same-package clients need access to module binaries, they don't need to be independently rebuildable and could have an unstable ABI; they could avoid resilience overhead and unnecessary language rules. -* By default package symbols are exported in the final libraries/executables. We plan to introduce a build setting that allows users to decide whether to hide package symbols for staticaly linked libraries. Enabling package symbols to be hidden would help with size optimizations. +* By default, `package` symbols are exported in the final libraries/executables. We plan to introduce a build setting that allows users to decide whether to hide package symbols for statically linked libraries. Enabling package symbols to be hidden would help with size optimizations. ## Source compatibility From 02b3cbaf3eecb916a5f1570a5e8e94cc210957e8 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 18 Jan 2023 23:19:26 -0800 Subject: [PATCH 2896/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 7ba3e15b0c..3b56af6c05 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -186,7 +186,7 @@ Subclassable | package | open | packageopen | (illegal) Some of the boxes are naturally illegal: it of course doesn't make sense for a class to be subclassable when it can't access at all. -A `package` class is accessible from outside of its defining modules as long as they are within the same package. Similar to `public`, it can only be subclass-able within its defining module. If the `package` class needs to be subclassed outside of its defining module, a `package open` or `packageopen` (exact name TBD) needs to be used. This proposal focuses on `package`; the subclassing functionality (and the exact name) will be addressed in the future. +A `package` class is accessible from outside of its defining modules as long as they are within the same package. Similar to `public`, it can only be subclassed within its defining module. If the `package` class needs to be subclassed outside of its defining module, a `package open` or `packageopen` (exact name TBD) needs to be used. This proposal focuses on `package`; the subclassing functionality (and the exact name) will be addressed in the future. The reason for not allowing both accessing and subclassing under `package` is as follows: From 5e41759b2825db33abcd2dc51ac5c9310f32e1ec Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 18 Jan 2023 23:21:46 -0800 Subject: [PATCH 2897/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 3b56af6c05..e5ba5c17b3 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -159,7 +159,7 @@ We plan to introduce an option to hide package symbols. By default, all package Library evolution makes modules resilient. We can incorporate a package name into a resiliency check and bypass it if modules are in the same package. This will remove the need for resilience overhead such as indirection and language requirements such as `@unknown default` for an unfrozen `enum`. -### Subclassing / Overrides +### Subclassing and Overrides Below is a matrix showing what access levels currently allow accessing or subclassing. From 7511db8d5e4c394e92d9265d429e59053c9f6beb Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 18 Jan 2023 23:26:29 -0800 Subject: [PATCH 2898/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index e5ba5c17b3..ebac476bc6 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -161,7 +161,13 @@ Library evolution makes modules resilient. We can incorporate a package name int ### Subclassing and Overrides -Below is a matrix showing what access levels currently allow accessing or subclassing. +Access control in Swift usually doesn't distinguish between different kinds of use. If a program has access to a type, for example, that gives the programmer a broad set of privileges: the type name can be used in most places, values of the type can be borrowed, copied, and destroyed, members of the type can be accessed (up to the limits of their own access control), and so on. This is because access control is a tool for enforcing encapsulation and allowing the future evolution of code. Broad privileges are granted because restricting them more precisely usually doesn't serve that goal. + +However, there are two exceptions. The first is that Swift allows `var` and `subscript` to restrict mutating accesses more tightly than read-only accesses; this is done by writing a separate access modifier for the setter, e.g. `private(set)`. The second is that Swift allows classes and class members to restrict subclassing and overriding more tightly than normal references; this is done by writing `public` instead of `open`. Allowing these privileges to be separately restricted does serve the goal of encapsulation and evolution. + +Because setter access levels are controlled by writing a separate modifier from the primary access, the syntax naturally extends to allow `package(set)`. However, subclassing and overriding are different because they are controlled by writing a specific keyword as the primary access modifier. This proposal has to decide what `package` by itself means for classes and class members. It also has to decide whether to support the options not covered by `package` alone or to leave them as a possible future direction. + +Here is a matrix showing what access levels currently allow accessing or subclassing: ``` Accessible in... From 77377a9af11e3b4570ed68b1d1e2600d4c8b5e1f Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 18 Jan 2023 23:31:22 -0800 Subject: [PATCH 2899/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index ebac476bc6..227cd34735 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -21,7 +21,7 @@ Packages, as expressed by the Swift Package Manager, are a unit of code distribu However, because Swift does not recognize organizations of code above the module level, it is not possible to create APIs like this that are purely internal to the package. To be usable from other modules within the package, the API must be public, but this means it can also be used outside of the package. This allows clients to form unwanted source dependencies on the API. It also means the built module has to export the API, which has negative implications for code size and performance. -For example, here’s a scenario where a client (App) has access to a utility API from a package (gamePkg) it depends on. +For example, here’s a scenario where a client (`App`) has access to a utility API from a package (`gamePkg`) it depends on. ``` App (Xcode project or appPkg) From 01d474f1d6eb470ec1b61b1c48ff84d7d893b30d Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 18 Jan 2023 23:33:18 -0800 Subject: [PATCH 2900/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 227cd34735..804200eff1 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -76,7 +76,7 @@ This also paves the path to include a package name in the namespace in the futur ### `package` keyword -A new keyword `package` is introduced. It is a contextual keyword, i.e. a symbol can be named `package`. +`package` is introduced as an access modifier. It cannot be combined with other access modifiers. For example, the following is allowed. ``` From 8ad305b98e7da0da8606034affed9dbc43544eb5 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 18 Jan 2023 23:33:57 -0800 Subject: [PATCH 2901/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 804200eff1..24aeab62d6 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -77,7 +77,7 @@ This also paves the path to include a package name in the namespace in the futur ### `package` keyword `package` is introduced as an access modifier. It cannot be combined with other access modifiers. -For example, the following is allowed. +`package` is a contextual keyword, so existing declarations named `package` will continue to work. This follows the precedent of `open`, which was also added as a contextual keyword. For example, the following is allowed: ``` package var package: String {...} From 9cff4e46514de1652e38ee8fa2e79c275a5ed6a3 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Thu, 19 Jan 2023 01:02:06 -0800 Subject: [PATCH 2902/4563] updates --- proposals/NNNN-package-access-modifier.md | 137 +++++++++++++--------- 1 file changed, 83 insertions(+), 54 deletions(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 24aeab62d6..bbe94c4d01 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -9,40 +9,34 @@ ## Introduction -This proposal introduces `package` as a new access modifier. Currently, to access a symbol in another module, that symbol needs to be declared `public`. However, a symbol being `public` allows it to be accessed from any module at all, both within a package and from outside of a package, which is sometimes undesirable. We need a new access modifier to enable a more fine control over the visibility scope of such symbols. +This proposal introduces `package` as a new access modifier. Currently, to access a symbol in another module, that symbol needs to be declared `public`. However, a symbol being `public` allows it to be accessed from any module at all, both within a package and from outside of a package, which is sometimes undesirable. We need a new access modifier to enable a more fine control over the visibility scope of such symbols. ## Motivation -At the most basic level, every Swift program is just a collection of declarations: functions, types, variables, and so on. In principle, every level of organization above this is arbitrary; all of those declarations could be piled into a single file, compiled, and run. In reality, Swift programs are organized into separate files, directories, libraries, and so on. At each level, this organization reflects programmer judgment about relationships, both in the code and in how it is developed. +At the most basic level, every Swift program is just a collection of declarations: functions, types, variables, and so on. In principle, every level of organization above this is arbitrary; all of those declarations could be piled into a single file, compiled, and run. In reality, Swift programs are organized into separate files, directories, libraries, and so on. At each level, this organization reflects programmer judgment about relationships, both in the code and in how it is developed. -As a language, Swift recognizes some of these levels. Modules are the smallest unit of library structure, with an independent interface and non-cyclic dependencies, and it makes sense for Swift to recognize that in both namespacing and access control. Files are the smallest grouping beneath that and are often used to collect tightly-related declarations, so they also make sense to respect in access control. +As a language, Swift recognizes some of these levels. Modules are the smallest unit of library structure, with an independent interface and non-cyclic dependencies, and it makes sense for Swift to recognize that in both namespacing and access control. Files are the smallest grouping beneath that and are often used to collect tightly-related declarations, so they also make sense to respect in access control. -Packages, as expressed by the Swift Package Manager, are a unit of code distribution. Some packages contain just a single module, but it's frequently useful to split a package's code into multiple modules. For example, when a module contains some internal helper APIs, those APIs can be split out into a utility module and maybe reused by other modules or packages. +Packages, as expressed by the Swift Package Manager, are a unit of code distribution. Some packages contain just a single module, but it's frequently useful to split a package's code into multiple modules. For example, when a module contains some internal helper APIs, those APIs can be split out into a utility module and maybe reused by other modules or packages. -However, because Swift does not recognize organizations of code above the module level, it is not possible to create APIs like this that are purely internal to the package. To be usable from other modules within the package, the API must be public, but this means it can also be used outside of the package. This allows clients to form unwanted source dependencies on the API. It also means the built module has to export the API, which has negative implications for code size and performance. +However, because Swift does not recognize organizations of code above the module level, it is not possible to create APIs like this that are purely internal to the package. To be usable from other modules within the package, the API must be public, but this means it can also be used outside of the package. This allows clients to form unwanted source dependencies on the API. It also means the built module has to export the API, which has negative implications for code size and performance. -For example, here’s a scenario where a client (`App`) has access to a utility API from a package (`gamePkg`) it depends on. - -``` -App (Xcode project or appPkg) - |— Game (gamePkg) - |— Engine (gamePkg) -``` +For example, here’s a scenario where a client has access to a utility API from a package it depends on. The client `App` could be an executable or an Xcode project. It depends on a package called `gamePkg`, which contains two modules, `Game` and `Engine`. Here are source code examples. ``` -[Engine] +[Engine (a module in gamePkg)] public struct MainEngine { - public init() { ... } + public init() { ... } // Intended to be public - public var stats: String { ... } + public var stats: String { ... } // A helper function made public only to be accessed by Game - public func run() { ... } + public func run() { ... } } -[Game] +[Game (a module in gamePkg)] import Engine @@ -50,7 +44,7 @@ public func play() { MainEngine().run() // Can access `run` as intended since it's within the same package } -[App] +[App (an executable in `appPkg`)] import Game import Engine @@ -63,14 +57,12 @@ print(engine.stats) // Can access `stats` as intended In the above scenario, App can import Engine (a utility module in 'gamePkg') and access its helper API directly, even though the API is not intended to be used outside of its package. -These costs are particularly unfortunate given that packages are a unit of code distribution. Boundaries between packages often reflect divisions between development teams; different packages are developed on different schedules by different groups of people. These boundaries are therefore especially important to enforce, because accidental dependencies can require extra work and cooperation to detangle. +These costs are particularly unfortunate given that packages are a unit of code distribution. Boundaries between packages often reflect divisions between development teams; different packages are developed on different schedules by different groups of people. These boundaries are therefore especially important to enforce, because accidental dependencies can require extra work and cooperation to detangle. ## Proposed solution -Our goal is to introduce a mechanism to Swift to recognize a package as a unit in the aspect of access control. We proposed to do so by introducing a new access modifier `package`. The `package` access modifier will allow accessing symbols from outside of its defining module as long as they are within the same package. This will help set clear boundaries between packages. - -This also paves the path to include a package name in the namespace in the future, which will help disambiguate modules with the same name. +Our goal is to introduce a mechanism to Swift to recognize a package as a unit in the aspect of access control. We proposed to do so by introducing a new access modifier `package`. The `package` access modifier will allow accessing symbols from outside of its defining module as long as they are within the same package. This will help set clear boundaries between packages. ## Detailed design @@ -87,19 +79,19 @@ This is similar to `open`, which is also a contextual keyword. ### Declaration Site -The `package` keyword is added at the declaration site. Using the scenario above, the helper API `run` can be declared with the new access modifier like so: +The `package` keyword is added at the declaration site. Using the scenario above, the helper API `run` can be declared with the new access modifier like so: ``` [Engine] public struct MainEngine { - public init() { ... } - public var stats: String { ... } - package func run() { ... } + public init() { ... } + public var stats: String { ... } + package func run() { ... } } ``` -The `package` access modifier can be added to any types where an existing access modifier can be added, e.g. `class`, `struct`, `enum`, `func`, `var`, `protocol`, etc. It is less restrictive than `internal` and more restrictive than `public`. For example, a `public` function cannot have `internal` or `package` parameters or return type in its signature, and a `package` function cannot have `internal` parameters or return type in its signature. +The `package` access modifier can be added to any types where an existing access modifier can be added, e.g. `class`, `struct`, `enum`, `func`, `var`, `protocol`, etc. It is less restrictive than `internal` and more restrictive than `public`. For example, a `public` function cannot have `internal` or `package` parameters or return type in its signature, and a `package` function cannot have `internal` parameters or return type in its signature. ### Use site @@ -131,9 +123,9 @@ engine.run() // Error: cannot find `run` in scope ### Package names -A package name is a unique string with c99 identifier characters, and is passed down to Swift frontend via a new flag `-package-name`. It is then stored in the module binary and used to compare with package names of other modules to determine if they are part of the same package. +A package name is a unique string with c99 identifier characters, and is passed down to Swift frontend via a new flag `-package-name`. It is then stored in the module binary and used to compare with package names of other modules to determine if they are part of the same package. -If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. +If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. Here's an example of how a package name is passed to a commandline invocation. @@ -143,21 +135,21 @@ swiftc -module-name Engine -package-name gamePkg ... [App] swiftc App -package-name appPkg ... ``` -When building the Engine module, the package name 'gamePkg' and package symbols is stored in the Engine binary. When building Game, the input to its package name 'gamePkg' is compared with Engine's package name; since they match, access to package symbols is allowed. When building App, the name comparison shows 'appPkg' is different from its dependency module's so access to package symbols is deined, which is what we want. +When building the Engine module, the package name 'gamePkg' and package symbols is stored in the Engine binary. When building Game, the input to its package name 'gamePkg' is compared with Engine's package name; since they match, access to package symbols is allowed. When building App, the name comparison shows 'appPkg' is different from its dependency module's so access to package symbols is deined, which is what we want. -Swift Package Manager has a package identity per package, an identifier that's verified to be unique via a registry, and it will pass it down automatically. Other build systems such as Bazel can introduce a new build setting to set the value for a package name. Since it needs to be unique, a reverse-DNS name could be used to avoid clashing. +Swift Package Manager has a package identity per package, an identifier that's verified to be unique via a registry, and it will pass it down automatically. Other build systems such as Bazel can introduce a new build setting to set the value for a package name. Since it needs to be unique, a reverse-DNS name could be used to avoid clashing. ### Exportability -Package symbols will be stored in .swiftmodule only, and not in the public interface file (.swiftinterface). We plan to introduce a .package.swiftinterface that contains package symbols, similar to a .private.swiftinterface which contains SPI symbols. We will store information on whether the .swiftmodule was built from a .package.swiftinterface into the .swiftmodule. We will also store the package name in both .swiftmodule and .swiftinterface. This is to enforce loading a .swiftmodule instead of .swiftinterface when building modules in the same package. +Package symbols will be stored in .swiftmodule only, and not in the public interface file (.swiftinterface). We plan to introduce a .package.swiftinterface that contains package symbols, similar to a .private.swiftinterface which contains SPI symbols. We will store information on whether the .swiftmodule was built from a .package.swiftinterface into the .swiftmodule. We will also store the package name in both .swiftmodule and .swiftinterface. This is to enforce loading a .swiftmodule instead of .swiftinterface when building modules in the same package. -`package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the function: they must be `open`, `public`, `package`, or `@usableFromInline`. Note that `@usableFromInline` allows the use of a symbol from `@inlinable` functions whether they're `package` or `public`. `@usableFromPackageInline` will be introduced to export a symbol only for use by `@inlinable package` functions. +`package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the function: they must be `open`, `public`, `package`, or `@usableFromInline`. Note that `@usableFromInline` allows the use of a symbol from `@inlinable` functions whether they're `package` or `public`. `@usableFromPackageInline` will be introduced to export a symbol only for use by `@inlinable package` functions. -We plan to introduce an option to hide package symbols. By default, all package symbols will be exported in the final library/executable. +We plan to introduce an option to hide package symbols. By default, all package symbols will be exported in the final library/executable. ### Resiliency -Library evolution makes modules resilient. We can incorporate a package name into a resiliency check and bypass it if modules are in the same package. This will remove the need for resilience overhead such as indirection and language requirements such as `@unknown default` for an unfrozen `enum`. +Library evolution makes modules resilient. We can incorporate a package name into a resiliency check and bypass it if modules are in the same package. This will remove the need for resilience overhead such as indirection and language requirements such as `@unknown default` for an unfrozen `enum`. ### Subclassing and Overrides @@ -177,6 +169,38 @@ Subclassable | module | public | internal in... | nowhere | public final | internal final ``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Accessible in...
    anywheremodule
    anywhereopen(illegal)
    Subclassable in...modulepublicinternal
    nowherepublic finalinternal final
    With `package` as a new access modifier, the matrix is modified like so: @@ -184,48 +208,53 @@ With `package` as a new access modifier, the matrix is modified like so: Accessible in... ___anywhere____|_____package____|____module_____ | anywhere | open | (illegal) | (illegal) -Subclassable | package | open | packageopen | (illegal) +Subclassable | package | ? | ? | (illegal) in... | module | public | package | internal | nowhere | public final | package final | internal final ``` -Some of the boxes are naturally illegal: it of course doesn't make sense for a class to be subclassable when it can't access at all. - -A `package` class is accessible from outside of its defining modules as long as they are within the same package. Similar to `public`, it can only be subclassed within its defining module. If the `package` class needs to be subclassed outside of its defining module, a `package open` or `packageopen` (exact name TBD) needs to be used. This proposal focuses on `package`; the subclassing functionality (and the exact name) will be addressed in the future. - -The reason for not allowing both accessing and subclassing under `package` is as follows: +This proposal takes the position that `package` alone should not allow subclassing or overriding outside of the defining module. This is consistent with the behavior of `public` and makes `package` fit into a simple continuum of ever-expanding privileges. It also allows the normal optimization model of `public` classes and methods to still be applied to `package` classes and methods, implicitly making them `final` when they aren't subclassed or overridden, without requiring a new "whole package optimization" build mode. -We want to allow a linear progression from `private` through `open` where each step incrementally offers capabilities to a wider range of clients. If `package` were to allow subclassing/overrides outside of its defining modules, making a `package` class `public` would break subclasses defined in other modules in the same package. +However, this choice leaves no way to spell the two combinations marked in the table above with `?`. These are more complicated to design and implement and are discussed in Future Directions. -We want to follow the optimization model for `public`; a `public` class has no subclasses outside of its defining module, and a `public` class member has no overrides outside of its defining module. The optimizer can take advantage of this information in whole-module builds to replace dynamic dispatch with static dispatch among other things. ## Future Directions -Subclassing -* A separate access modifier will likely need to be introduced to allow subclassing and overriding within a package. The reason for a new access modifier is described in the Details section. +### Subclassing +The package/package entity marked with `?` from the matrix corresponds to allowing accessing and subclassing a class from outside of its defining module within the same package (`open` within a package and hidden from another package). The package/anywhere entity also marked with `?` means allowing accessing a class from outside of the package but subclassing only within the package (`open` within a package and `public` from another package). -Namespacing -* A package name might potentially be added to a mangled name. This could help disambiguate module names that clash. +We will most likely need to introduce a new access modifier for each or one of them with an option to export or hide the symbol from outside of its defining package (via an attribute). -Optimizations -* A package containing several modules can be treated as a resilience domain. If same-package clients need access to module binaries, they don't need to be independently rebuildable and could have an unstable ABI; they could avoid resilience overhead and unnecessary language rules. +### Package-private +If modules in a package only contain symbols that `package` or more restrictive, none of the symbols from the package are visible outside of it. -* By default, `package` symbols are exported in the final libraries/executables. We plan to introduce a build setting that allows users to decide whether to hide package symbols for statically linked libraries. Enabling package symbols to be hidden would help with size optimizations. +### Optimizations +* A package containing several modules can be treated as a resilience domain. If same-package clients need access to module binaries, they don't need to be independently rebuildable and could have an unstable ABI; they could avoid resilience overhead and unnecessary language rules. + +* By default, `package` symbols are exported in the final libraries/executables. We plan to introduce a build setting that allows users to decide whether to hide package symbols for statically linked libraries. Enabling package symbols to be hidden would help with size optimizations. ## Source compatibility -A new keyword `package` is added as a new access modifier. It is a contextual keyword and is an additive change. Symbols that are named `package` should not require renaming, and the source code as is should continue to work. +A new keyword `package` is added as a new access modifier. It is a contextual keyword and is an additive change. Symbols that are named `package` should not require renaming, and the source code as is should continue to work. ## Effect on ABI stability -Boundaries between separately-built modules within a package are still potentially ABI boundaries. The ABI for package symbols is not different from the ABI for public symbols, although in the future we plan to add an option to not export package symbols that can be resolved within an image. +Boundaries between separately-built modules within a package are still potentially ABI boundaries. The ABI for package symbols is not different from the ABI for public symbols, although in the future we plan to add an option to not export package symbols that can be resolved within an image. ## Alternatives considered -A current workaround for the scenario in the Motivation is to use `@_spi` or `@_implemenationOnly`. Each option has caveats. The `@_spi` requires a group name, which makes it verbose to use and harder to keep track of, and `@_implementationOnly` can be too limiting as we want to be able to restrict access to only portions of APIs. There are also hacky workarounds such as `@testable` and the `-disable-access-control` flag. They elevate all `internal` (and `private` with the flag) symbols to `public`. These options are unstable and also quickly lead to an increase of the binary and the shared cache size, not to mention symbol name clashes. +### Use current workarounds +A current workaround for the scenario in the Motivation is to use `@_spi` or `@_implemenationOnly`. Each option has caveats. The `@_spi` requires a group name, which makes it verbose to use and harder to keep track of, and `@_implementationOnly` can be too limiting as we want to be able to restrict access to only portions of APIs. There are also hacky workarounds such as `@testable` and the `-disable-access-control` flag. They elevate all `internal` (and `private` with the flag) symbols to `public`. These options are unstable and also quickly lead to an increase of the binary and the shared cache size, not to mention symbol name clashes. + +### Introduce submodules +Instead of adding a new package access level above modules, we could allow modules to contain other modules as components. This is an idea often called "submodules". Packages would then define an "umbrella" module that contains the package's modules as components. However, there are several weaknesses in this approach: + +* It doesn't actually solve the problem by itself. Submodule APIs would still need to be able to declare whether they're usable outside of the umbrella or not, and that would require an access modifier. It might be written in a more general way, like `internal(MyPackage)`, but that generality would also make it more verbose. +* Submodule structure would be part of the source language, so it would naturally be source- and ABI-affecting. For example, programmers could use the parent module's name to qualify identifiers, and symbols exported by a submodule would include the parent module's name. This means that splitting a module into submodules or adding an umbrella parent module would be much more impactful than desired; ideally, those changes would be purely internal and not change a module's public interface. It also means that these changes would end up permanently encoding package structure. +* The "umbrella" submodule structure doesn't work for all packages. Some packages include multiple "top-level" modules which share common dependencies. Forcing these to share a common umbrella in order to use package-private dependencies is not desirable. +* In a few cases, the ABI and source impact above would be desirable. For example, many packages contain internal Utility modules; if these were declared as submodules, they would naturally be namespaced to the containing package, eliminating spurious collisions. However, such modules are generally not meant to be usable outside of the package at all. It is a reasonable future direction to allow whole modules to be made package-private, which would also make it reasonable to automatically namespace them. -Instead of a package, submodules could have been considered. Introducing another "umbrella" module or dividing a module into "sub" modules, however, would require much more significant work and would be both source and ABI breaking. A package already exists as a unit corresponding to a group of modules, and treating it as a unit above a module in the grouping hierarchy enables a nautral transition into a submodule-like structure. ## Acknowledgments From e2431c19c66d7659e9fe7859ad923910fdcefe8b Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Thu, 19 Jan 2023 01:07:16 -0800 Subject: [PATCH 2903/4563] table format --- proposals/NNNN-package-access-modifier.md | 74 +++++++++++++---------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index bbe94c4d01..2214488c14 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -120,7 +120,6 @@ let engine = MainEngine() engine.run() // Error: cannot find `run` in scope ``` - ### Package names A package name is a unique string with c99 identifier characters, and is passed down to Swift frontend via a new flag `-package-name`. It is then stored in the module binary and used to compare with package names of other modules to determine if they are part of the same package. @@ -161,58 +160,71 @@ Because setter access levels are controlled by writing a separate modifier from Here is a matrix showing what access levels currently allow accessing or subclassing: -``` - Accessible in... - ___anywhere___|____module_____ - | anywhere | open | (illegal) -Subclassable | module | public | internal - in... | nowhere | public final | internal final - -``` - - - - + - - - + + - - - + + - - - + +
    Accessible in...
    Subclassable in... \ Accessible in... anywhere module
    anywhereopen(illegal)open(illegal)
    Subclassable in... modulepublicinternalpublicinternal
    nowherepublic finalinternal finalpublic finalinternal final
    With `package` as a new access modifier, the matrix is modified like so: -``` - Accessible in... - ___anywhere____|_____package____|____module_____ - | anywhere | open | (illegal) | (illegal) -Subclassable | package | ? | ? | (illegal) - in... | module | public | package | internal - | nowhere | public final | package final | internal final - -``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Subclassable in... \ Accessible in...anywherepackagemodule
    anywhereopen(illegal)(illegal)
    package??(illegal)
    modulepublicpackageinternal
    nowherepublic finalpackage finalinternal final
    This proposal takes the position that `package` alone should not allow subclassing or overriding outside of the defining module. This is consistent with the behavior of `public` and makes `package` fit into a simple continuum of ever-expanding privileges. It also allows the normal optimization model of `public` classes and methods to still be applied to `package` classes and methods, implicitly making them `final` when they aren't subclassed or overridden, without requiring a new "whole package optimization" build mode. From a3f04d8126a23349c3c3c117b3e908d7084ecd17 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 19 Jan 2023 10:44:43 -0800 Subject: [PATCH 2904/4563] Accept SE-0384. --- ...mporting-forward-declared-objc-interfaces-and-protocols.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md index 3d359328a9..eb9506ad98 100644 --- a/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md +++ b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md @@ -3,9 +3,9 @@ * Proposal: [SE-0384](0384-importing-forward-declared-objc-interfaces-and-protocols.md) * Author: [Nuri Amari](https://github.com/NuriAmari) * Review Manager: Tony Allevato (https://github.com/allevato) -* Status: **Active review (January 4, 2023...January 18, 2023)** +* Status: **Accepted** * Implementation: https://github.com/apple/swift/pull/61606 -* Review: ([pitch](https://forums.swift.org/t/pitch-importing-forward-declared-objective-c-classes-and-protocols/61926)) ([review](https://forums.swift.org/t/se-0384-importing-forward-declared-objective-c-interfaces-and-protocols/62392)) +* Review: ([pitch](https://forums.swift.org/t/pitch-importing-forward-declared-objective-c-classes-and-protocols/61926)) ([review](https://forums.swift.org/t/se-0384-importing-forward-declared-objective-c-interfaces-and-protocols/62392)) ([acceptance](https://forums.swift.org/t/accepted-se-0384-importing-forward-declared-objective-c-interfaces-and-protocols/62670)) ## Introduction From 03e083439051c902191f98bd49445d6a5b869447 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Thu, 19 Jan 2023 15:44:51 -0800 Subject: [PATCH 2905/4563] updates --- proposals/NNNN-package-access-modifier.md | 93 +++++++++++++---------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 2214488c14..8a86fcf157 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -4,7 +4,7 @@ * Authors: [Ellie Shin](https://github.com/elsh), [Alexis Laferriere](https://github.com/xymus) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Awaiting review** -* Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/62700), [apple/swift#62704](https://github.com/apple/swift/pull/62704), [apple/swift#62652](https://github.com/apple/swift/pull/62652) +* Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/62700), [apple/swift#62704](https://github.com/apple/swift/pull/62704), [apple/swift#62652](https://github.com/apple/swift/pull/62652), [apple/swift#62652](https://github.com/apple/swift/pull/62652) * Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) ## Introduction @@ -66,7 +66,7 @@ Our goal is to introduce a mechanism to Swift to recognize a package as a unit i ## Detailed design -### `package` keyword +### `package` Keyword `package` is introduced as an access modifier. It cannot be combined with other access modifiers. `package` is a contextual keyword, so existing declarations named `package` will continue to work. This follows the precedent of `open`, which was also added as a contextual keyword. For example, the following is allowed: @@ -94,7 +94,7 @@ public struct MainEngine { The `package` access modifier can be added to any types where an existing access modifier can be added, e.g. `class`, `struct`, `enum`, `func`, `var`, `protocol`, etc. It is less restrictive than `internal` and more restrictive than `public`. For example, a `public` function cannot have `internal` or `package` parameters or return type in its signature, and a `package` function cannot have `internal` parameters or return type in its signature. -### Use site +### Use Site The Game module can access the helper API `run` since it is in the same package as Engine. @@ -120,7 +120,7 @@ let engine = MainEngine() engine.run() // Error: cannot find `run` in scope ``` -### Package names +### Package Names A package name is a unique string with c99 identifier characters, and is passed down to Swift frontend via a new flag `-package-name`. It is then stored in the module binary and used to compare with package names of other modules to determine if they are part of the same package. @@ -158,32 +158,33 @@ However, there are two exceptions. The first is that Swift allows `var` and `su Because setter access levels are controlled by writing a separate modifier from the primary access, the syntax naturally extends to allow `package(set)`. However, subclassing and overriding are different because they are controlled by writing a specific keyword as the primary access modifier. This proposal has to decide what `package` by itself means for classes and class members. It also has to decide whether to support the options not covered by `package` alone or to leave them as a possible future direction. -Here is a matrix showing what access levels currently allow accessing or subclassing: +Here is a matrix showing where each current access level can be used or overridable: - - - + + + - - - + + + - - - + + + - - - + + + +
    Subclassable in... \ Accessible in...anywheremoduleUseOverride/Subclass
    anywhereopen(illegal)internalin-modulein-module
    modulepublicinternalpubliccross-modulein-module
    nowherepublic finalinternal finalopencross-modulecross-module
    @@ -192,37 +193,43 @@ With `package` as a new access modifier, the matrix is modified like so: - - - - + + + - - - - + + + - - - + + - - - - + + + - - - - + + + + + + + + + + + + + +
    Subclassable in... \ Accessible in...anywherepackagemoduleUseOverride/Subclass
    anywhereopen(illegal)(illegal)internalin-modulein-module
    package??(illegal)cross-module (in package)in-module
    modulepublicpackageinternal?(1)cross-module (in package)cross-module (in package)
    nowherepublic finalpackage finalinternal final?(2)cross-module (cross-package)cross-module (in package)
    publiccross-module (cross-package)in-module
    opencross-module (cross-package)cross-module (cross-package)
    @@ -233,13 +240,15 @@ However, this choice leaves no way to spell the two combinations marked in the t ## Future Directions -### Subclassing -The package/package entity marked with `?` from the matrix corresponds to allowing accessing and subclassing a class from outside of its defining module within the same package (`open` within a package and hidden from another package). The package/anywhere entity also marked with `?` means allowing accessing a class from outside of the package but subclassing only within the package (`open` within a package and `public` from another package). +### Subclassing and Overrides +The entities marked with `?(1)` and `?(2)` from the matrix above both require accessing and subclassing cross-modules in a package (`open` within a package). The only difference is that (1) hides the symbol from outside of the package and (2) makes it visible outside. Use cases involving (2) should be rare but its underlying flow should be the same as (1) except its symbol visibility. + +We will need to expand on the existing `open` access modifier or introduce a new access modifier. The exact name is TBD, but so far suggestions include `packageopen`, `package open`, `open(package)`, and `open package(set)`. The `open package(set)` might be a good candidate since we can utilize the existing flow between `open` and `public` that allows subclasses to be `public` and expand on it to control the visibility of the base class. If we were to use a new access modifier, we might need more than one, e.g. `packageopen` that corresponds to (1) and `public packageopen` that corresponds to (2), and will also need to handle the inheritance access level hirearchy, i.e. whether subclasses can be `public` when their base class is `packageopen`. -We will most likely need to introduce a new access modifier for each or one of them with an option to export or hide the symbol from outside of its defining package (via an attribute). +We will add a function that returns a two-dimensioned value for use and override. It will return the correct access level for use and indication on whether it's overridable. From the use aspect, the access level determined should just be `package` if not one of the existing access level. The overridable bit should return whether it's subclassable or overridable for all access levels. ### Package-private -If modules in a package only contain symbols that `package` or more restrictive, none of the symbols from the package are visible outside of it. +If a module in a package only contains symbols that are `package` or more restrictive, the whole module can be treated as private to the package. This "package-only" module can be useful for organizing modules (a utility module vs a public facing module) and enforcing the boundary with diagnostics. It can also allow module aliasing to apply automatically without the explicit module aliases parameter, which could be useful for multi-version dependencies of a package. ### Optimizations * A package containing several modules can be treated as a resilience domain. If same-package clients need access to module binaries, they don't need to be independently rebuildable and could have an unstable ABI; they could avoid resilience overhead and unnecessary language rules. @@ -256,10 +265,10 @@ Boundaries between separately-built modules within a package are still potential ## Alternatives considered -### Use current workarounds +### Use Current Workarounds A current workaround for the scenario in the Motivation is to use `@_spi` or `@_implemenationOnly`. Each option has caveats. The `@_spi` requires a group name, which makes it verbose to use and harder to keep track of, and `@_implementationOnly` can be too limiting as we want to be able to restrict access to only portions of APIs. There are also hacky workarounds such as `@testable` and the `-disable-access-control` flag. They elevate all `internal` (and `private` with the flag) symbols to `public`. These options are unstable and also quickly lead to an increase of the binary and the shared cache size, not to mention symbol name clashes. -### Introduce submodules +### Introduce Submodules Instead of adding a new package access level above modules, we could allow modules to contain other modules as components. This is an idea often called "submodules". Packages would then define an "umbrella" module that contains the package's modules as components. However, there are several weaknesses in this approach: * It doesn't actually solve the problem by itself. Submodule APIs would still need to be able to declare whether they're usable outside of the umbrella or not, and that would require an access modifier. It might be written in a more general way, like `internal(MyPackage)`, but that generality would also make it more verbose. From ffa9224d23307913bd21da5d03d2800c9fd37736 Mon Sep 17 00:00:00 2001 From: tomer doron Date: Fri, 20 Jan 2023 14:33:24 -0800 Subject: [PATCH 2906/4563] Accept SE-0378 (#1917) --- proposals/0378-package-registry-auth.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0378-package-registry-auth.md b/proposals/0378-package-registry-auth.md index 2913970027..6202f1b903 100644 --- a/proposals/0378-package-registry-auth.md +++ b/proposals/0378-package-registry-auth.md @@ -3,7 +3,7 @@ * Proposal: [SE-0378](0378-package-registry-auth.md) * Author: [Yim Lee](https://github.com/yim-lee) * Review Manager: [Tom Doron](https://github.com/tomerd) -* Status: **Active Review (November 14 - December 2, 2022)** +* Status: **Accepted** * Implementation: [apple/swift-package-manager#5838](https://github.com/apple/swift-package-manager/pull/5838) * Review: * Pitch: https://forums.swift.org/t/pitch-package-registry-authentication/61047 From 2f5358812fbda5e073b1ec9a3e068f5f5337f569 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Sat, 21 Jan 2023 11:14:05 -0500 Subject: [PATCH 2907/4563] Remove if/switch expressions from common rejections Obsoleted by https://github.com/apple/swift-evolution/blob/main/proposals/0380-if-switch-expressions.md :) --- commonly_proposed.md | 1 - 1 file changed, 1 deletion(-) diff --git a/commonly_proposed.md b/commonly_proposed.md index 2c2c2ba6a2..e766f67275 100644 --- a/commonly_proposed.md +++ b/commonly_proposed.md @@ -22,7 +22,6 @@ Several of the discussions below refer to "C family" languages. This is intended ## Control Flow, Closures, Optional Binding, and Error Handling - * [`if/else` and `switch` as expressions](https://forums.swift.org/t/control-flow-expressions/90/5): These are conceptually interesting things to support, but many of the problems solved by making these into expressions are already solved in Swift in other ways. Making them expressions introduces significant tradeoffs, and on balance, we haven't found a design that is clearly better than what we have so far. * [Replace `continue` keyword with synonyms from other scripting languages (e.g. next, skip, advance, etc)](https://forums.swift.org/t/replace-continue-keyword/764/2): Swift is designed to feel like a member of the C family of languages. Switching keywords away from C precedent without strong motivation is a non-goal. * [Remove support for `default:` in `switch` and just use `case _:`](https://forums.swift.org/t/remove-default-case-in-switch-case/360/4): `default` is widely used, `case _` is too magical, and `default` is widely precedented in many C family languages. * [Rename `guard` to `unless`](https://forums.swift.org/t/rename-guard-to-unless/934/7): It is a common request that `guard` be renamed `unless`. People requesting this change argue that `guard` is simply a logically inverted `if` statement, and therefore `unless` is a more obvious keyword. However, such requests stem from a fundamental misunderstanding of the functionality provided by `guard`. Unlike `if`, `guard` *enforces* that the code within its curly braces provides an early exit from the codepath. In other words, a `guard` block **must** `return`, `throw`, `break`, `continue` or call a function that does not return, such as `fatalError()`. This differs from `if` quite significantly, and therefore the parallels assumed between `guard` and `if` are not valid. From 3d3b051968f02f7ef1b9d333b46af9c14e8ab64e Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 23 Jan 2023 14:52:59 -0800 Subject: [PATCH 2908/4563] Update SE-0377 to use names accepted by language workgroup review (#1909) --- .../0377-parameter-ownership-modifiers.md | 135 +++++++++--------- 1 file changed, 65 insertions(+), 70 deletions(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index 8109fef758..654c2bfa5f 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -1,4 +1,4 @@ -# `borrow` and `consume` parameter ownership modifiers +# `borrowing` and `consuming` parameter ownership modifiers * Proposal: [SE-0377](0377-parameter-ownership-modifiers.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Joe Groff](https://github.com/jckarter) @@ -10,7 +10,7 @@ ## Introduction -We propose new `borrow` and `consume` parameter modifiers to allow developers to +We propose new `borrowing` and `consuming` parameter modifiers to allow developers to explicitly choose the ownership convention that a function uses to receive immutable parameters. This allows for fine-tuning of performance by reducing the number of ARC calls or copies needed to call a function, and provides a @@ -67,60 +67,60 @@ types. ## Proposed solution We give developers direct control over the ownership convention of -parameters by introducing two new parameter modifiers `borrow` and `consume`. +parameters by introducing two new parameter modifiers `borrowing` and `consuming`. ## Detailed design -`borrow` and `consume` become contextual keywords inside parameter type +`borrowing` and `consuming` become contextual keywords inside parameter type declarations. They can appear in the same places as the `inout` modifier, and are mutually exclusive with each other and with `inout`. In a `func`, `subscript`, or `init` declaration, they appear as follows: ```swift -func foo(_: borrow Foo) -func foo(_: consume Foo) +func foo(_: borrowing Foo) +func foo(_: consuming Foo) func foo(_: inout Foo) ``` In a closure: ```swift -bar { (a: borrow Foo) in a.foo() } -bar { (a: consume Foo) in a.foo() } +bar { (a: borrowing Foo) in a.foo() } +bar { (a: consuming Foo) in a.foo() } bar { (a: inout Foo) in a.foo() } ``` In a function type: ```swift -let f: (borrow Foo) -> Void = { a in a.foo() } -let f: (consume Foo) -> Void = { a in a.foo() } +let f: (borrowing Foo) -> Void = { a in a.foo() } +let f: (consuming Foo) -> Void = { a in a.foo() } let f: (inout Foo) -> Void = { a in a.foo() } ``` -Methods can use the `consuming` or `borrowing` modifier to indicate +Methods can also use the `consuming` or `borrowing` modifier to indicate respectively that they consume ownership of their `self` parameter or that they borrow it. These modifiers are mutually exclusive with each other and with the existing `mutating` modifier: ```swift struct Foo { - consuming func foo() // `consume` ownership of self - borrowing func foo() // `borrow` self + consuming func foo() // `consuming` self + borrowing func foo() // `borrowing` self mutating func foo() // modify self with `inout` semantics } ``` -`consume` cannot be applied to parameters of nonescaping closure type, which by +`consuming` cannot be applied to parameters of nonescaping closure type, which by their nature are always borrowed: ```swift // ERROR: cannot `consume` a nonescaping closure -func foo(f: consume () -> ()) { +func foo(f: consuming () -> ()) { } ``` -`consume` or `borrow` on a parameter do not affect the caller-side syntax for +`consuming` or `borrowing` on a parameter do not affect the caller-side syntax for passing an argument to the affected declaration, nor do `consuming` or `borrowing` affect the application of `self` in a method call. For typical Swift code, adding, removing, or changing these modifiers does not have any @@ -128,14 +128,14 @@ source-breaking effects. (See "related directions" below for interactions with other language features being considered currently or in the near future which might interact with these modifiers in ways that cause them to break source.) -Protocol requirements can also use `consume` and `borrow`, and the modifiers will +Protocol requirements can also use `consuming` and `borrowing`, and the modifiers will affect the convention used by the generic interface to call the requirement. The requirement may still be satisfied by an implementation that uses different conventions for parameters of copyable types: ```swift protocol P { - func foo(x: consume Foo, y: borrow Foo) + func foo(x: consuming Foo, y: borrowing Foo) } // These are valid conformances: @@ -145,28 +145,28 @@ struct A: P { } struct B: P { - func foo(x: borrow Foo, y: consume Foo) + func foo(x: borrowing Foo, y: consuming Foo) } struct C: P { - func foo(x: consume Foo, y: borrow Foo) + func foo(x: consuming Foo, y: borrowing Foo) } ``` Function values can also be implicitly converted to function types that change -the convention of parameters of copyable types among unspecified, `borrow`, -or `consume`: +the convention of parameters of copyable types among unspecified, `borrowing`, +or `consuming`: ```swift let f = { (a: Foo) in print(a) } -let g: (borrow Foo) -> Void = f -let h: (consume Foo) -> Void = f +let g: (borrowing Foo) -> Void = f +let h: (consuming Foo) -> Void = f let f2: (Foo) -> Void = h ``` -Inside of a function or closure body, `consume` parameters may be mutated, as can +Inside of a function or closure body, `consuming` parameters may be mutated, as can the `self` parameter of a `consuming func` method. These mutations are performed on the value that the function itself took ownership of, and will not be evident in any copies of the value that might still exist in @@ -191,13 +191,13 @@ let helloWorld = "hello ".plus("cruel ").plus("world") ## Source compatibility -Adding `consume` or `borrow` to a parameter in the language today does not +Adding `consuming` or `borrowing` to a parameter in the language today does not otherwise affect source compatibility. Callers can continue to call the function as normal, and the function body can use the parameter as it already -does. A method with `consume` or `borrow` modifiers on its parameters can still +does. A method with `consuming` or `borrowing` modifiers on its parameters can still be used to satisfy a protocol requirement with different modifiers. The compiler will introduce implicit copies as needed to maintain the expected -conventions. This allows for API authors to use `consume` and `borrow` annotations +conventions. This allows for API authors to use `consuming` and `borrowing` annotations to fine-tune the copying behavior of their implementations, without forcing clients to be aware of ownership to use the annotated APIs. Source-only packages can add, remove, or adjust these annotations on copyable types @@ -207,27 +207,27 @@ This will change if we introduce features that limit the compiler's ability to implicitly copy values, such as move-only types, "no implicit copy" values or scopes, and `consume` or `borrow` operators in expressions. Changing the parameter convention changes where copies may be necessary to perform the call. -Passing an uncopyable value as an argument to a `consume` parameter ends its +Passing an uncopyable value as an argument to a `consuming` parameter ends its lifetime, and that value cannot be used again after it's consumed. ## Effect on ABI stability -`consume` or `borrow` affects the ABI-level calling convention and cannot be +`consuming` or `borrowing` affects the ABI-level calling convention and cannot be changed without breaking ABI-stable libraries (except on "trivial types" for which copying is equivalent to `memcpy` and destroying is a no-op; however, -`consume` or `borrow` also has no practical effect on parameters of trivial type). +`consuming` or `borrowing` also has no practical effect on parameters of trivial type). ## Effect on API resilience -`consume` or `borrow` break ABI for ABI-stable libraries, but are intended to have +`consuming` or `borrowing` break ABI for ABI-stable libraries, but are intended to have minimal impact on source-level API. When using copyable types, adding or changing these annotations to an API should not affect its existing clients. ## Alternatives considered -### Leaving `consume` parameter bindings immutable inside the callee +### Leaving `consuming` parameter bindings immutable inside the callee -We propose that `consume` parameters should be mutable inside of the callee, +We propose that `consuming` parameters should be mutable inside of the callee, because it is likely that the callee will want to perform mutations using the value it has ownership of. There is a concern that some users may find this behavior unintuitive, since those mutations would not be visible in copies @@ -236,20 +236,20 @@ of the value in the caller. This was the motivation behind which explicitly removed the former ability to declare parameters as `var` because of this potential for confusion. However, whereas `var` and `inout` both suggest mutability, and `var` does not provide explicit directionality as -to where mutations become visible, `consume` on the other hand does not +to where mutations become visible, `consuming` on the other hand does not suggest any kind of mutability to the caller, and it explicitly states the directionality of ownership transfer. Furthermore, with move-only types, the chance for confusion is moot, because the transfer of ownership means the caller cannot even use the value after the callee takes ownership anyway. -Another argument for `consume` parameters to remain immutable is to serve the +Another argument for `consuming` parameters to remain immutable is to serve the proposal's stated goal of minimizing the source-breaking impact of -parameter ownership modifiers. When `consume` parameters are mutable, -changing a `consume` parameter to `borrow`, or removing the -`consume` annotation altogether, is potentially source-breaking. However, +parameter ownership modifiers. When `consuming` parameters are mutable, +changing a `consuming` parameter to `borrowing`, or removing the +`consuming` annotation altogether, is potentially source-breaking. However, any such breakage is purely localized to the callee; callers are still unaffected (as long as copyable arguments are involved). If a developer wants -to change a `consume` parameter back into a `borrow`, they can still assign the +to change a `consuming` parameter back into a `borrowing`, they can still assign the borrowed value to a local variable and use that local variable for local mutation. @@ -267,10 +267,9 @@ We have considered several alternative naming schemes for these modifiers: model. - A previous pitch used the names `nonconsuming` and `consuming`. The current implementation also uses `__consuming func` to notate a method that takes - ownership of its `self` parameter. `consuming` parallels `mutating` as a - method modifier for a method that consumes `self`, but we like the imperative - form `consume` for parameter modifiers as a parallel with the `consume` operator - in callers. + ownership of its `self` parameter. We think it is better to describe + `borrowing` in terms of what it means, rather than as the opposite of + the other convention. - The first reviewed revision used `take` instead of `consume`. Along with `borrow`, `take` arose during [the first review of SE-0366](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202). @@ -286,21 +285,14 @@ the corresponding `consume` and `borrow` operators (discussed below under Future Directions), since it helps reinforce the relationship between the calling conventions and the expression operators: to explicitly transfer ownership of an argument in a call site to a parameter in a function, use -`foo(consume x)` at the call site, and use `func foo(_: consume T)` in the +`foo(consuming x)` at the call site, and use `func foo(_: consuming T)` in the function declaration. Similarly, to explicitly pass an argument by borrow -without copying, use `foo(borrow x)` at the call site, and `func foo(_: borrow T)` +without copying, use `foo(borrow x)` at the call site, and `func foo(_: borrowing T)` in the function declaration. -Some review discussion explored the possibility of -using different verb forms for the various roles; since we're already using -`consuming func` and `borrowing func` as the modifiers for the `self` parameter, -we could also conjugate the parameter modifiers so you write -`foo(x: borrowed T)` or `bar(x: consumed T)`, while still using `foo(borrow x)` -and `bar(consume x)` as the call-site operators. - ### Effect on call sites and uses of the parameter -This proposal designs the `consume` and `borrow` modifiers to have minimal source +This proposal designs the `consuming` and `borrowing` modifiers to have minimal source impact when applied to parameters, on the expectation that, in typical Swift code that isn't using move-only types or other copy-controlling features, adjusting the convention is a useful optimization on its own without otherwise @@ -312,7 +304,7 @@ a value argument indicates that the developer is interested in guaranteeing that the optimization occurs, and having the annotation imply changed behavior at call sites or inside the function definition, such as disabling implicit copies of the parameter inside the function, or implicitly taking an argument -to a `consume` parameter and ending its lifetime inside the caller after the +to a `consuming` parameter and ending its lifetime inside the caller after the call site. We believe that it is better to keep the behavior of the call in expressions independent of the declaration (to the degree possible with implicitly copyable values), and that explicit operators on the call site @@ -325,7 +317,7 @@ optimizer behavior is insufficient to get optimal code. There are a number of caller-side operators we are considering to allow for performance-sensitive code to make assertions about call behavior. These -are closely related to the `consume` and `borrow` parameter modifiers and so +are closely related to the `consuming` and `borrowing` parameter modifiers and so share their names. See also the [Selective control of implicit copying behavior](https://forums.swift.org/t/selective-control-of-implicit-copying-behavior-take-borrow-and-copy-operators-noimplicitcopy/60168) thread on the Swift forums for deeper discussion of this suite of features @@ -342,7 +334,7 @@ When the lifetime of the variable ends in an argument to a `consume` parameter, then we can transfer ownership to the callee without any copies: ```swift -func consume(x: consume Foo) +func consume(x: consuming Foo) func produce() { let x = Foo() @@ -366,7 +358,7 @@ are disabled): ```swift var global = Foo() -func useFoo(x: borrow Foo) { +func useFoo(x: borrowing Foo) { // We need exclusive access to `global` here global = Foo() } @@ -398,7 +390,7 @@ this: ```swift var global = Foo() -func useFooWithoutTouchingGlobal(x: borrow Foo) { +func useFooWithoutTouchingGlobal(x: borrowing Foo) { /* global not used here */ } @@ -414,7 +406,7 @@ while the caller is borrowing it, an exclusivity failure would be raised. #### Move-only types, uncopyable values, and related features -The `consume` versus `borrow` distinction becomes much more important and +The `consuming` versus `borrowing` distinction becomes much more important and prominent for values that cannot be implicitly copied. We have plans to introduce move-only types, whose values are never copyable, as well as attributes that suppress the compiler's implicit copying behavior selectively @@ -433,11 +425,11 @@ func open(path: FilePath) throws -> FileHandle // Operations that operate on an open file handle and leave it open // borrow the FileHandle -func read(from: borrow FileHandle) throws -> Data +func read(from: borrowing FileHandle) throws -> Data // Operations that close the file handle and make it unusable consume // the FileHandle -func close(file: consume FileHandle) +func close(file: consuming FileHandle) func hackPasswords() throws -> HackedPasswords { let fd = try open(path: "/etc/passwd") @@ -458,20 +450,20 @@ match exactly, since they cannot be implicitly copied in a thunk: ```swift protocol WritableToFileHandle { - func write(to fh: borrow FileHandle) + func write(to fh: borrowing FileHandle) } extension String: WritableToFileHandle { // error: does not satisfy protocol requirement, ownership modifier for // parameter of move-only type `FileHandle` does not match /* - func write(to fh: consume FileHandle) { + func write(to fh: consuming FileHandle) { ... } */ // This is OK: - func write(to fh: borrow FileHandle) { + func write(to fh: borrowing FileHandle) { ... } } @@ -488,13 +480,13 @@ conforming types to be copyable. ### `set`/`out` parameter convention -By making the `borrow` and `consume` conventions explicit, we mostly round out +By making the `borrowing` and `consuming` conventions explicit, we mostly round out the set of possibilities for how to handle a parameter. `inout` parameters get **exclusive access** to their argument, allowing them to mutate or replace the -current value without concern for other code. By contrast, `borrow` parameters +current value without concern for other code. By contrast, `borrowing` parameters get **shared access** to their argument, allowing multiple pieces of code to share the same value without copying, so long as none of them mutate the -shared value. A `consume` parameter consumes a value, leaving nothing behind, but +shared value. A `consuming` parameter consumes a value, leaving nothing behind, but there still isn't a parameter analog to the opposite convention, which would be to take an uninitialized argument and populate it with a new value. Many languages, including C# and Objective-C when used with the "Distributed @@ -513,5 +505,8 @@ Thanks to Robert Widmann for the original underscored implementation of ## Revision history The [first reviewed revision](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) -of this proposal used `take` and `taking` instead of `consume` and `consuming` -as the name of the callee-destroy convention. +of this proposal used `take` and `taking` as the name of the callee-destroy convention. + +The [second reviewed revision](https://github.com/apple/swift-evolution/blob/e3966645cf07d6103561454574ab3e2cc2b48ee9/proposals/0377-parameter-ownership-modifiers.md) +used the imperative forms, `consume` and `borrow`, as parameter modifiers, +which were changed to the gerunds `consuming` and `borrowing` in review. From 72fad6de854c54a653fa935dc8b5ef8b851562ab Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 23 Jan 2023 15:59:53 -0800 Subject: [PATCH 2909/4563] [SE-0380] Apply revisions from review acceptance (#1919) * Apply revisions from review acceptance * Accept SE-0380 and link decision notes. Co-authored-by: Holly Borla --- proposals/0380-if-switch-expressions.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/proposals/0380-if-switch-expressions.md b/proposals/0380-if-switch-expressions.md index b5e8656495..582dba0f63 100644 --- a/proposals/0380-if-switch-expressions.md +++ b/proposals/0380-if-switch-expressions.md @@ -3,9 +3,9 @@ * Proposal: [SE-0380](0380-if-switch-expressions.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Hamish Knight](https://github.com/hamishknight) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (December 7...December 30, 2022)** +* Status: **Accepted** * Implementation: [apple/swift#612178](https://github.com/apple/swift/pull/62178), including a downloadable toolchain. -* Review: ([pitch](https://forums.swift.org/t/pitch-if-and-switch-expressions/61149)), ([review](https://forums.swift.org/t/se-0380-if-and-switch-expressions/61899)) +* Review: ([pitch](https://forums.swift.org/t/pitch-if-and-switch-expressions/61149)), ([review](https://forums.swift.org/t/se-0380-if-and-switch-expressions/61899)), ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0380-if-and-switch-expressions/62695)) ## Introduction @@ -114,7 +114,9 @@ Each of these expressions become the value of the overall expression if the bran This does have the downside of requiring fallback to the existing techniques when, for example, a single expression has a log line above it. This is in keeping with the current behavior of `return` omission. -An exception to this rule is if a branch either returns or throws, in which case no value for the overall expression need be produced. In these cases, multiple expressions could be executed on that branch prior to the `throw` or `return`. +An exception to this rule is if a branch either explicitly throws, or terminates the program (e.g. with `fatalError`), in which case no value for the overall expression need be produced. In these cases, multiple expressions could be executed on that branch prior to that point. + +In the case where a branch throws, either because a call in the expression throws (which requires a `try`) or with an explicit `throw`, there is no requirement to mark the overall expression with an additional `try` (e.g. before the `if`). Within a branch, further `if` or `switch` expressions may be nested. @@ -311,10 +313,6 @@ let foo: String = do { } ``` -### Support for `break` and `continue` - -Similar to `return`, statements that break or continue to a label, could be permitted in branches. There is likely a larger number of edge cases to consider here, possibly requiring enhancements to DI to ensure variables remain initialized on all paths. - ### Guard Often enthusiasm for `guard` leads to requests for `guard` to have parity with `if`. Returning a value from a `guard`'s else is very common, and could potentially be sugared as @@ -433,9 +431,15 @@ If a future direction of full expressions is considered, the `->` form may not w let x = if p -> 1 else -> 2 + 3 ``` +### Support for control flow + +An earlier version of this proposal allowed use of `return` in a branch. Similar to `return`, statements that `break` or `continue` to a label, were considered a future direction. + +Allowing new control flow out of expressions could be unexpected and error-prone. Swift currently only allows control flow out of expressions through thrown errors, which must be explicitly marked with `try` (or, in the case of `if` or `switch` branches, with `throw`) as an indication of the control flow to the programmer. Allowing other control flow out of expressions would undermine this principle. The control flow impact of nested return statements would become more difficult to reason about if we extend SE-0380 to support multiple-statement branches in the future. The use-cases for this functionality presented in the review thread were also fairly niche. Given the weak motivation and the potential problems introduced, the Language Workgroup accepts SE-0380 without this functionality. + ## Source compatibility -As proposed, this addition has one source incompatability, related to unreachable code. The following currently compiles, albeit with a warning that the `if` statement is unreachable (and the values in the branches unused): +As proposed, this addition has one source incompatibility, related to unreachable code. The following currently compiles, albeit with a warning that the `if` statement is unreachable (and the values in the branches unused): ```swift func foo() { From 8d2521cad379893d73fae1ec0fea93ca1ca0852c Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 23 Jan 2023 16:12:58 -0800 Subject: [PATCH 2910/4563] Accept SE-0366 and link to decision notes. (#1920) --- proposals/0366-move-function.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0366-move-function.md b/proposals/0366-move-function.md index 7c11aedb99..bcb9c2484e 100644 --- a/proposals/0366-move-function.md +++ b/proposals/0366-move-function.md @@ -3,9 +3,9 @@ * Proposal: [SE-0366](0366-move-function.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Joe Groff](https://github.com/jckarter) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (December 7 - December 14, 2022)** +* Status: **Accepted** * Implementation: Implemented on main as stdlib SPI (`_move` instead of `consume` keyword) -* Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic/53983)) ([first review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0366-move-operation-use-after-move-diagnostic/59687)) ([second review](https://forums.swift.org/t/se-0366-second-review-take-operator-to-end-the-lifetime-of-a-variable-binding/61021)) ([third review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) +* Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic/53983)) ([first review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0366-move-operation-use-after-move-diagnostic/59687)) ([second review](https://forums.swift.org/t/se-0366-second-review-take-operator-to-end-the-lifetime-of-a-variable-binding/61021)) ([third review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)), ([acceptance](https://forums.swift.org/t/accepted-se-0366-consume-operator-to-end-the-lifetime-of-a-variable-binding/62758)) * Previous Revisions: * [1](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md) * [2](https://github.com/apple/swift-evolution/blob/43849aa9ae3e87c434866c5a5e389af67537ca26/proposals/0366-move-function.md) From 55e4fedd8ee7bb92f62327d57d0054d68dde1533 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 24 Jan 2023 11:02:27 -0800 Subject: [PATCH 2911/4563] [SE-0366] Add swift syntax highlighting to a few code examples where this was missed. (#1922) --- proposals/0366-move-function.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/proposals/0366-move-function.md b/proposals/0366-move-function.md index bcb9c2484e..194be7e2f7 100644 --- a/proposals/0366-move-function.md +++ b/proposals/0366-move-function.md @@ -172,7 +172,7 @@ Likewise, if the maintainer tries to access the original `value` parameter insid of `doStuffUniquely` after being consumed to initialize `newValue`, they will get an error: -``` +```swift func doStuffUniquely(with value: consume [Int]) { // If we received the last remaining reference to `value`, we'd like // to be able to efficiently update it without incurring more copies. @@ -348,7 +348,7 @@ with existing code that calls functions named `consume`, the operand to `consume` must begin with another identifier, and must consist of an identifier or postfix expression: -``` +```swift consume x // OK consume [1, 2, 3] // Subscript access into property named `consume`, not a consume operation consume (x) // Call to function `consume`, not a consume operation @@ -466,7 +466,7 @@ state can use the static `consume` operator to provide API that dynamically takes ownership of the current value inside of them while leaving them in their empty state: -``` +```swift extension Optional { mutating func take() -> Wrapped { switch consume self { @@ -510,7 +510,7 @@ with it, which means that a function which `consume`s an instance, or a `taking func` method on the type itself, would run the deinit if it does not forward ownership anywhere else: -``` +```swift moveonly struct FileHandle { var fd: Int32 @@ -529,7 +529,7 @@ an operation that invalidates the value some other way, making it unnecessary or incorrect for `deinit` to run on it, or because the function wants to be able to take ownership of parts away from the value: -``` +```swift extension FileHandle { // Return the file descriptor back to the user for manual management // and disable automatic management with the FileHandle value. @@ -549,7 +549,7 @@ This doesn't require a new parameter convention (since it fits within the ABI of a `consume T` parameter), but could be spelled as a new operator inside of a `taking func`: -``` +```swift extension FileHandle { // Return the file descriptor back to the user for manual management // and disable automatic management with the FileHandle value. @@ -653,7 +653,7 @@ we're considering: `borrow` operator would let the developer communicate this to the compiler: - ``` + ```swift var global = Foo() func useFoo(x: Foo) { @@ -674,7 +674,7 @@ we're considering: with static lifetime, types, or scopes should not admit implicit copies: - ``` + ```swift // we're not allowed to implicitly copy `x` func foo(@noImplicitCopy x: String) { } From efef684c8501e7b82191dc6a24ecd6d448b7a7af Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 24 Jan 2023 11:21:19 -0800 Subject: [PATCH 2912/4563] [Proposal] Custom Reflection Metadata (#1921) In Swift, declarations are annotated with attributes to opt into both built-in language features (e.g. `@available`) and library functionality (e.g. `@RegexComponentBuilder`). This proposal introduces the ability to attach library-defined reflection metadata to declarations using custom attributes, which can then be queried by the library to opt client code into library functionality. --- proposals/NNNN-custom-reflection-metadata.md | 336 +++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 proposals/NNNN-custom-reflection-metadata.md diff --git a/proposals/NNNN-custom-reflection-metadata.md b/proposals/NNNN-custom-reflection-metadata.md new file mode 100644 index 0000000000..e6e5788fc2 --- /dev/null +++ b/proposals/NNNN-custom-reflection-metadata.md @@ -0,0 +1,336 @@ +# Custom Reflection Metadata + +* Proposal: [SE-NNNN](NNNN-custom-reflection-metadata.md) +* Authors: [Pavel Yaskevich](https://github.com/xedin), [Holly Borla](https://github.com/hborla), [Alejandro Alonso](https://github.com/Azoy), [Stuart Montgomery](https://github.com/stmontgomery) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [PR#1](https://github.com/apple/swift/pull/62426), [PR#2](https://github.com/apple/swift/pull/62738), [PR#3](https://github.com/apple/swift/pull/62818), [PR#4](https://github.com/apple/swift/pull/62850), [PR#5](https://github.com/apple/swift/pull/62920), [PR#6](https://github.com/apple/swift/pull/63057) + +## Introduction + +In Swift, declarations are annotated with attributes to opt into both built-in language features (e.g. `@available`) and library functionality (e.g. `@RegexComponentBuilder`). This proposal introduces the ability to attach library-defined reflection metadata to declarations using custom attributes, which can then be queried by the library to opt client code into library functionality. + +Previous Swift Forum discussions + +* [Custom attributes](https://forums.swift.org/t/custom-attributes/13976) +* [Pitch: introduce custom attributes](https://forums.swift.org/t/pitch-introduce-custom-attributes/21335) + +## Motivation + +There are some problem domains in which it can be beneficial for a library author to let a client annotate within their own code certain declarations that the library should be made aware of, since requiring the client call an explicit API instead would be too onerous, repetitive, or easy to forget. + +One classic example is **testing**: there is a common pattern in unit testing libraries where users define a type that extends one of the library's types, they annotate some of its methods as tests, and the library locates and runs all tests automatically, after initializing a distinct instance of the containing type for each. There is no official mechanism in Swift to implement this test discovery pattern today, however XCTest—the language's current de-facto standard test library—has longstanding workarounds: + +* On Apple platforms, XCTest relies on the Objective-C runtime to enumerate all subclasses of a known base class and their methods, and it considers all instance methods with a supported signature and name prefixed with "test" a test method. +* On other platforms, XCTest is typically used from a package, and the Swift Package Manager has special logic to introspect build-time indexer data, locate test methods, and explicitly pass the list of discovered tests to XCTest to run. + +XCTest's current approach has some drawbacks and limitations: + +* Users must adhere to a strict naming convention by prefixing all test methods with the word "test". This prefix can be redundant since all tests include it, and may surprise users if they accidentally use that prefix on a non-test method since the behavior is implicit. +* Since tests are declared implicitly, there is no way for a user to provide additional details about an individual test or group of tests. It would be useful to have a way to indicate whether a test is enabled, its requirements, or other metadata, for example, so that the testing library could use this information to inform how it executes tests and offer more powerful features. +* The lack of a built-in runtime discovery mechanism means that related tools (such as Swift Package Manager) require specialized discovery logic for each test library they support. This makes adding support for alternate test libraries to those tools very difficult and increases their implementation complexity. + +The general pattern of registering code to be discovered by a framework is common across Swift programs. For example, a program that uses a plugin architecture commonly uses a protocol for the interface of the plugin, which is then implemented on concrete types in clients. This pattern imposes error-prone registration boilerplate, where clients must explicitly supply a list of concrete plugin types or explicitly register individual plugin types to be used by the framework before the framework needs them. + +Java and C# programming languages both have a feature called “Java Annotations” and “C# attributes” respectively. It allows to add attributes on methods, variables, parameters, and, in case of Java, packages that are accessible to Java/C# compiler and at runtime via reflection APIs. We propose a similar addition to Swift called **custom reflection metadata**. + +## Proposed solution + +* A new built-in attribute `@reflectionMetadata` that can be applied to structs, enums, classes, and actors. +* Types annotated with this built-in attribute can be used as custom attributes on declarations that can be used as values. + * The custom attribute can have additional arguments; the custom attribute application will turn into an initializer call on the attribute type, passing in the declaration value as the first argument. +* A reflection API that can gather all declarations with a given custom attribute attached. + +## Detailed design + +### Declaring reflection metadata attributes + +Reflection metadata custom attributes are declared by attaching the built-in `@reflectionMetadata` attribute to a nominal type, i.e. a struct, enum, class, or actor: + +```swift +@reflectionMetadata +struct Example { ... } +``` + +A reflection metadata type must have a synchronous initializer of the form `init(attachedTo:)`. The type of the `attachedTo:` parameter dictates which types of declarations the custom attribute can be applied to, as described in the following section. + +### Applications of reflection metadata types + +Reflection metadata custom attributes can be applied to any declaration that can be used as a first-class value in Swift, including: + +* Types +* Global functions +* Static methods +* Instance methods, both non-mutating and mutating +* Instance properties + +Reflection metadata types opt into which kinds of declarations are supported based on their initializer overloads which begin with a parameter labeled `attachedTo:`. For an application of a reflection metadata attribute to be well-formed, the reflection metadata type must declare an initializer that accepts the appropriate value as the first argument. Applications of a reflection metadata type to a declaration will synthesize an initializer call with the attribute arguments, and the declaration value passed as the first initializer argument: + +* Types will pass a metatype. +* Global functions will pass an unapplied function reference. +* Static methods on a type `T` will pass a function which calls the method on the metatype `T.Type` passed as the first parameter. +* Instance methods on a type `T` will pass a function which calls the method on an instance `T` passed as the first parameter. The function will support `mutating` instance methods when the first parameter is declared `inout T`. +* Instance properties will pass a key-path. + +```swift +@reflectionMetadata +struct Flag { + // Initializer that accepts a metatype of a nominal type + init(attachedTo: T.Type) { + // ... + } + + // Initializer that accepts an unapplied reference to a global function + init(attachedTo: (Args) -> Result) { + // ... + } + + // Initializer that accepts a function which calls a static method + init(attachedTo: (T.Type, Args) -> Result) { + // ... + } + + // Initializer that accepts a function which calls an instance method + init(attachedTo: (T, Args) -> Result) { + // ... + } + + // Initializer that accepts a function which calls a mutating instance method + init(attachedTo: (inout T, Args) -> Result) { + // ... + } + + // Initializer that accepts a reference to an instance property + init(attachedTo: KeyPath, custom: Int) { + // ... + } +} + +// The compiler will synthesize the following initializer call +// -> Flag.init(attachedTo: doSomething) +@Flag func doSomething(_: Int, other: String) {} + +// The compiler will synthesize the following initializer call +// -> Flag.init(attachedTo: Test.self) +@Flag +struct Test { + // The compiler will synthesize the following initializer call + // -> Flag.init(attachedTo: { metatype in metatype.computeStateless() }) + @Flag static func computeStateless() {} + + // The compiler will synthesize the following initializer call + // -> Flag.init(attachedTo: { instance, values in instance.compute(values: values) }) + @Flag func compute(values: [Int]) {} + + var state = 1 + + // The compiler will synthesize the following initializer call + // -> Flag.init(attachedTo: { (instance: inout Test) in instance.incrementState() }) + @Flag mutating func incrementState() { + state += 1 + } + + // The compiler will synthesize the following initializer call + // -> Flag.init(attachedTo: \Test.answer, custom: 42) + @Flag(custom: 42) var answer: Int = 42 +} +``` + +#### Restrictions on custom reflection metadata application + +A given declaration can have multiple reflection metadata attributes as long as a given reflection metadata type only appears once: + +```swift +@Flag @Ignore func ignored() { 🟢 + // ... +} + +@Flag @Flag func specialFunction() { 🔴 + ^ error: duplicate reflection metadata attribute + // ... +} +``` + +Reflection metadata attributes must be applied at either the primary declaration of a type or in an extension of the type within the same module as the type’s primary declaration. Applying the attribute to a type in an extension outside its module is prohibited to prevent the same type from having multiple reflection metadata annotations of the same type. + +```swift +@Flag extension MyType [where ...] { 🔴 + ^ error: cannot associate reflection metadata @Flag with MyType in extension +} +``` + +Declarations with custom reflection metadata attributes must be fully concrete: + +```swift +struct GenericType { + @Flag + var genericValue: T 🔴 + ^ error +} + +extension GenericType where T == Int { + @Flag + var concreteValue: Int // okay +} +``` + +Generic declarations cannot be discovered through the Reflection query that gathers all instances of reflection metadata, because generic values cannot be represented in a higher-kinded way in Swift; generic values must always have substitutions at runtime. Generic declarations could be supported in the future by adding reflection queries for the other direction, e.g. a query to return the custom reflection metadata for a given key-path `\Generic.value`. + +### Inference of reflection metadata attributes + +A reflection metadata attribute can be applied to a protocol: + +```swift +@EditorCommandRecord +protocol EditorCommand { /* ... */ } +``` + +Conceptually, the reflection metadata attribute is applied to the generic `Self` type that represents the concrete conforming type. When a protocol conformance is written at the primary declaration of a concrete type, the reflection metadata attribute is inferred: + +```swift +// @EditorCommandRecord is inferred +struct SelectWordCommand: EditorCommand { /* ... */ } +``` + +If the protocol conformance is written in an extension on the conforming type, attribute inference is prohibited. A reflection metadata attribute applied to a protocol is a form of requirement, so such conformances declared in extensions are invalid unless the primary declaration already has the explicit reflection metadata attribute: + +```swift +// Error unless the primary declaration of 'SelectWordCommand' has '@EditorCommandRecord' +extension SelectWordCommand : EditorCommand { 🔴 + // ... +} +``` + +Reflection metadata attributes applied to protocols cannot have additional attribute arguments; attribute arguments must be explicitly written on the conforming type. + +A type which conforms to a protocol that has a reflection metadata attribute may specify the attribute explicitly. This can be useful if the reflection metadata type includes additional parameters in its `init(attachedTo: ...)` overload, since it allows the conforming type to pass arguments for those parameters: + +```swift +// Overrides the inferred `@EditorCommandRecord` attribute from `EditorCommand` +@EditorCommandRecord(keyboardShortcut: "j", modifier: .command) +struct SelectWordCommand: EditorCommand { /* ... */ } +``` + +### Accessing metadata through Reflection + +With the introduction of the new [Reflection](https://forums.swift.org/t/pitch-reflection/61438) module, we feel a natural place to reflectively retrieve these attributes is there. The following Reflection APIs provide the runtime query for custom reflection metadata: + +```swift +/// Get all the instances of a custom reflection attribute wherever it's attached to. +/// +/// - Parameters: +/// - type: The type of the attribute that is attached to various sources. +/// - Returns: A sequence of attribute instances of `type` in no particular +/// order. +public enum Attribute { + public static func allInstances(of type: T.Type) -> AttributeInstances +} + +/// A sequence wrapper over some runtime attribute instances. +/// +/// Instances of `AttributeInstances` are created with the +/// `Attribute.allInstances(of:)` function. +public struct AttributeInstances {} + +extension AttributeInstances: IteratorProtocol { + @inlinable + public mutating func next() -> T? +} + +extension AttributeInstances: Sequence {} +``` + +This API will retrieve all of the instances of your reflection attribute across all modules. Instances of metadata types are initialized in the Reflection query to gather the metadata. Attributes who are not available in the current running OS, i.e. because the `attachedTo` declaration is not available as described in the following section, will be excluded from the results. + +### API Availability + +Custom metadata attributes can be attached to declarations with limited availability. The Reflection query for an individual instance of the metadata attribute type will be gated on a matching availability condition and will return `nil` for instances which are unavailable at runtime. For example: + +```swift +@available(macOS 12, *) +@Flag +struct NewType { /* ... */ } +``` + +The Reflection query that produces the `Flag` instance attached to `NewType` will effectively execute the following code: + +```swift +if #available(macOS 12, *) { + return Flag(attachedTo: NewType.self) +} else { + return nil +} +``` + +and if `nil` is returned, there will not be a `Flag` instance representing `NewType` included in the collection returned by `Attribute.allInstances(of:)`. + +## Alternatives considered + +### Extend other language features + +Some reviewers of the original pitch suggested that the motivating use cases could be addressed through a combination of improved Reflection capabilities and enhancing existing language features. For example: + +* We could use existing protocol conformance metadata to allow discovering all types conforming to a protocol. +* We could allow property wrappers to be used to discover properties via reflection. + +These suggestions have some notable downsides, however. Supporting discovery of all types that conform to *any* protocol would be very expensive, and the majority of protocols do not need this reflection capability. Opting-in to this capability via an attribute on protocols which require it is an intentional aspect of this feature’s design intended to mitigate this cost. + +It’s also important to note that a reflection API which *only* allows discovering types that conform to a protocol would be insufficient to satisfy some of the use cases which motivate this feature because it would not allow including additional, custom values in the reflection metadata. For example, the `@EditorCommandRecord(keyboardShortcut: "j", modifier: .command)` example shown above includes custom values on a type conforming to a protocol, and the design of this feature includes a way for the reflection query to retrieve these values in addition to the declaration the attribute was attached to. For types conforming to a protocol, similar functionality could be provided through protocol requirements, but this strategy does not generalize to enable providing custom metadata on functions or computed properties. + +Regarding the use of property wrappers to represent metadata on properties: We feel that property wrappers are not an ideal tool for reflection metadata because they require an instance of the backing property to be stored for each instance, even though the wrapper is constant per-declaration. Property wrappers that are *only* used for reflection metadata don’t need to introduce any access indirection of the wrapped value, either. The value itself can simply be stored inline in the type, rather than synthesizing computed properties. + +### Using reflection types in the `init(attachedTo:)` signature + +We considered using types from the [Reflection](https://forums.swift.org/t/pitch-reflection/61438) module to represent declarations which have reflection attributes. For example, Reflection’s `Field` could be used as the type of the first parameter in `init(attachedTo:)` when a reflection attribute is attached to a property declaration. + +But this design would not allow constraining the types of the declaration(s) the reflection attribute can be attached to using techniques like generic requirements or additional parameters after `attachedTo:` in an initializer, since Reflection types do not expose the interface type of the declaration they represent. For example, `Field` is not parameterized on the field’s type, which would prevent compile-time enforcement of requirements. + +### Use static methods instead of `init(attachedTo:)` overloads + +We considered using static methods such as `buildMetadata(attachedTo:)` instead of overloads of `init(attachedTo:)` on reflection metadata types to generate metadata instances. This could potentially allow the overloads of `buildMetadata` to return a different type than `Self`, or even an associated type from some protocol. For example: + +```swift +// Defined in either the standard library or Reflection +protocol Attribute { + associatedtype Metadata +} + +// Example usage +@reflectionMetadata +struct Flag: Attribute { + static func buildMetadata(attachedTo: ...) -> Metadata { /* ... */ } +} +``` + +This alternative has a potential advantage of making it easier for `@propertyWrapper` types to also act as `@reflectionMetadata` types, because it would mean that the storage for any additional, custom values used for metadata purposes only (which are constant for every instance of the declared property) could be stored separately rather than having those values be stored redundantly in every instance of a property wrapper. + +### Alternative attribute names + +We considered several alternative spellings of the attribute used to declare a reflection metadata type: + +* `@runtimeMetadata` +* `@dynamicMetadata` +* `@metadata` +* `@runtimeAnnotation` +* `@runtimeAttribute` +* `@reflectionAnnotation` + +### Bespoke `@test` attribute + +A previous Swift Evolution discussion suggested [adding a built-in `@test` attribute](https://forums.swift.org/t/rfc-in-line-tests/12111) to the language. However, registration is a general code pattern that is also used outside of testing, so allowing libraries to declare their own domain-specific attributes is a more general approach that supports a wider set of use cases. + +## Revision history + +### Changes after first pitch + +* Changed the proposed function signature for reflection metadata type initializer overloads for instance methods to accept `T` as the first parameter, instead of an unapplied function reference, and allow `inout T` to support `mutating` instance methods. +* Changed the proposed function signature for reflection metadata type initializer overloads for static methods to accept `T.Type` as the first parameter, instead of an unapplied function reference. +* Changed the spelling of the proposed attribute from `@runtimeMetadata` to `@reflectionMetadata`. +* Added `@reflectionAnnotation` (suggested by @xedin) to the list of alternative attribute spellings considered. +* Updated the list of supported use cases in the "Applications of reflection metadata types" section by separating global functions and static methods into separate bullets, to describe their differing type signatures. In particular, the function parameter for static methods now has type `(T.Type, Args) -> Result`. +* Clarified paragraph describing where reflection metadata attribute can be applied, to mention it is allowed in extensions of a type within the same module as the type's primary declaration, just not in extensions outside the module. +* Mentioned the ability to explicitly specify a reflection attribute on a type conforming to a protocol with that attribute, and described how that can be useful for specifying additional custom values. Added a code example of this. +* Changed the proposed name and return type of the Reflection API to `func allInstances(of type: T.Type) -> AttributeInstances`, returning a custom `Sequence` type whose type is `T`. Clarified that the returned sequence will omit values which do not satisfy the API availability conditions at runtime, rather than including `nil` values for them. +* Added discussion of some alternatives that were considered involving extending Reflection capabilities and other existing language features. +* Added discussion of an alternative that was considered about using Reflection types as the parameters to `init(attachedTo:)`. +* Added discussion of an alternative that was considered about using static methods instead of `init(attachedTo:)` overloads. From 9414b46e7156aab6d9d5a79b20960b1e3a475a00 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 24 Jan 2023 11:27:21 -0800 Subject: [PATCH 2913/4563] Initiate review of SE-0385: Custom Reflection Metadata (#1923) --- ...ction-metadata.md => 0385-custom-reflection-metadata.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-custom-reflection-metadata.md => 0385-custom-reflection-metadata.md} (99%) diff --git a/proposals/NNNN-custom-reflection-metadata.md b/proposals/0385-custom-reflection-metadata.md similarity index 99% rename from proposals/NNNN-custom-reflection-metadata.md rename to proposals/0385-custom-reflection-metadata.md index e6e5788fc2..40939afd1e 100644 --- a/proposals/NNNN-custom-reflection-metadata.md +++ b/proposals/0385-custom-reflection-metadata.md @@ -1,9 +1,9 @@ # Custom Reflection Metadata -* Proposal: [SE-NNNN](NNNN-custom-reflection-metadata.md) +* Proposal: [SE-0385](385-custom-reflection-metadata.md) * Authors: [Pavel Yaskevich](https://github.com/xedin), [Holly Borla](https://github.com/hborla), [Alejandro Alonso](https://github.com/Azoy), [Stuart Montgomery](https://github.com/stmontgomery) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (January 24, 2023...February 7, 2023)** * Implementation: [PR#1](https://github.com/apple/swift/pull/62426), [PR#2](https://github.com/apple/swift/pull/62738), [PR#3](https://github.com/apple/swift/pull/62818), [PR#4](https://github.com/apple/swift/pull/62850), [PR#5](https://github.com/apple/swift/pull/62920), [PR#6](https://github.com/apple/swift/pull/63057) ## Introduction From 69604dbc442816e4e2ce1e2f9b2f8f673fd9b953 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 11:33:45 -0800 Subject: [PATCH 2914/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 8a86fcf157..d27d379967 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -57,7 +57,7 @@ print(engine.stats) // Can access `stats` as intended In the above scenario, App can import Engine (a utility module in 'gamePkg') and access its helper API directly, even though the API is not intended to be used outside of its package. -These costs are particularly unfortunate given that packages are a unit of code distribution. Boundaries between packages often reflect divisions between development teams; different packages are developed on different schedules by different groups of people. These boundaries are therefore especially important to enforce, because accidental dependencies can require extra work and cooperation to detangle. +Allowing this kind of unintended public access to package APIs is especially bad because packages are a unit of code distribution. Swift wants to encourage programs to be divided into modules with well-defined interfaces, so it enforces the boundaries between modules with access control. Despite being divided this way, it's not uncommon for closely-related modules to be written by closely-related (or even the same) people. Access control between such modules still serves a purpose — it promotes the separation of concerns — but if a module's interface needs to be fixed, that's usually easy to coordinate, maybe even as simple as a single commit. However, packages allow code to be shared much more broadly than a single small organization. The boundaries between packages often represent significant differences between programmers, making coordination around API changes much more difficult. For example, the developers of an open source package generally don't know most of their clients, and the standard recommendation is for such packages to only ever remove existing APIs in major-version releases. It's therefore particularly important to allow programmers to enforce these boundaries between packages. ## Proposed solution From 969e46d71fe7415de384ead06d4b050a30915439 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 11:34:54 -0800 Subject: [PATCH 2915/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index d27d379967..b051f767c9 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -62,7 +62,7 @@ Allowing this kind of unintended public access to package APIs is especially bad ## Proposed solution -Our goal is to introduce a mechanism to Swift to recognize a package as a unit in the aspect of access control. We proposed to do so by introducing a new access modifier `package`. The `package` access modifier will allow accessing symbols from outside of its defining module as long as they are within the same package. This will help set clear boundaries between packages. +Our goal is to introduce a mechanism to Swift to recognize a package as a unit in the aspect of access control. We propose to do so by introducing a new access modifier called `package`. The `package` access modifier allows symbols to be accessed from outside of their defining module, but only from other modules in the same package. This helps to set clear boundaries between packages. ## Detailed design From d56cd9b3f0c19115f047627e4c889bf4e9546b42 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 11:35:07 -0800 Subject: [PATCH 2916/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 1 - 1 file changed, 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index b051f767c9..185660796a 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -75,7 +75,6 @@ Our goal is to introduce a mechanism to Swift to recognize a package as a unit i package var package: String {...} ``` -This is similar to `open`, which is also a contextual keyword. ### Declaration Site From f7b348fad99e49de2d4bd2400f1dd798eb4fe509 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 11:37:19 -0800 Subject: [PATCH 2917/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 185660796a..a9b7b0a2af 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -90,7 +90,9 @@ public struct MainEngine { } ``` -The `package` access modifier can be added to any types where an existing access modifier can be added, e.g. `class`, `struct`, `enum`, `func`, `var`, `protocol`, etc. It is less restrictive than `internal` and more restrictive than `public`. For example, a `public` function cannot have `internal` or `package` parameters or return type in its signature, and a `package` function cannot have `internal` parameters or return type in its signature. +The `package` access modifier can be used anywhere that the existing access modifiers can be used, e.g. `class`, `struct`, `enum`, `func`, `var`, `protocol`, etc. + +Swift requires that the declarations used in certain places (such as the signature of a function) be at least as accessible as the containing declaration. For the purposes of this rule, `package` is less accessible than `open` and `public` and more accessible than `internal`, `fileprivate`, and `private`. For example, a `public` function cannot use a `package` type in its parameters or return type, and a `package` function cannot use an `internal` type in its parameters or return type. Similarly, an `@inlinable` `public` function cannot use a `package` declaration in its implementation, and an `@inlinable` `package` function cannot use an `internal` declaration in its implementation. ### Use Site From 5e49a8f71cd63162b5c9ced288e4afdc869f108d Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 11:40:18 -0800 Subject: [PATCH 2918/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index a9b7b0a2af..095cafc05d 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -135,7 +135,7 @@ swiftc -module-name Engine -package-name gamePkg ... [App] swiftc App -package-name appPkg ... ``` -When building the Engine module, the package name 'gamePkg' and package symbols is stored in the Engine binary. When building Game, the input to its package name 'gamePkg' is compared with Engine's package name; since they match, access to package symbols is allowed. When building App, the name comparison shows 'appPkg' is different from its dependency module's so access to package symbols is deined, which is what we want. +When building the Engine module, the package name 'gamePkg' is recorded in the built interface to the module. When building Game, its package name 'gamePkg' is compared with the package name recorded in Engine's built interface; since they match, Game is allowed to access Engine's `package` declarations. When building App, its package name 'appPkg' is different from `gamePkg`, so it is not allowed to access `package` symbols in either Engine or Game, which is what we want. Swift Package Manager has a package identity per package, an identifier that's verified to be unique via a registry, and it will pass it down automatically. Other build systems such as Bazel can introduce a new build setting to set the value for a package name. Since it needs to be unique, a reverse-DNS name could be used to avoid clashing. From ebf1acdb198dc9cbab480c9773776ad152e1ce82 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 11:42:37 -0800 Subject: [PATCH 2919/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 095cafc05d..402f5ffaa6 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -141,7 +141,7 @@ Swift Package Manager has a package identity per package, an identifier that's v ### Exportability -Package symbols will be stored in .swiftmodule only, and not in the public interface file (.swiftinterface). We plan to introduce a .package.swiftinterface that contains package symbols, similar to a .private.swiftinterface which contains SPI symbols. We will store information on whether the .swiftmodule was built from a .package.swiftinterface into the .swiftmodule. We will also store the package name in both .swiftmodule and .swiftinterface. This is to enforce loading a .swiftmodule instead of .swiftinterface when building modules in the same package. +When the Swift frontend builds a `.swiftmodule` file directly from source, the file will include the package name and all of the `package` declarations in the module. When the Swift frontend builds a `.swiftinterface` file from source, the file will include the package name, but it will put `package` declarations in a secondary `.package.swiftinterface` file. When the Swift frontend builds a `.swiftmodule` file from a `.swiftinterface` file that includes a package name, but it does not have the corresponding `.package.swiftinterface` file, it will record this in the `.swiftmodule`, and it will prevent this file from being used to build other modules in the same package. `package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the function: they must be `open`, `public`, `package`, or `@usableFromInline`. Note that `@usableFromInline` allows the use of a symbol from `@inlinable` functions whether they're `package` or `public`. `@usableFromPackageInline` will be introduced to export a symbol only for use by `@inlinable package` functions. From 6ff43c57d7b1b84953b73fc5333a08b3e74e3c4d Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 13:24:24 -0800 Subject: [PATCH 2920/4563] Updates --- proposals/NNNN-package-access-modifier.md | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 402f5ffaa6..fb8ec0c06a 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -123,9 +123,7 @@ engine.run() // Error: cannot find `run` in scope ``` ### Package Names -A package name is a unique string with c99 identifier characters, and is passed down to Swift frontend via a new flag `-package-name`. It is then stored in the module binary and used to compare with package names of other modules to determine if they are part of the same package. - -If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. +Two modules belong to the same package if they were built with the same package name. A package name must be unique and a valid C99 identifier, i.e. a string consisting of alphanumeric characters and an underscore, starting with a letter. It is passed to the Swift frontend via a new flag `-package-name`. Swift Package Manager has a package identity per package, an identifier that's verified to be unique via a registry, and it will pass the identifier down automatically. Note that the package identity can contain URL characters, and such character will be transposed to an underscore. Other build systems such as Bazel will need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing; a dot in such string will be transposed to an underscore. Here's an example of how a package name is passed to a commandline invocation. @@ -137,15 +135,13 @@ swiftc -module-name Engine -package-name gamePkg ... When building the Engine module, the package name 'gamePkg' is recorded in the built interface to the module. When building Game, its package name 'gamePkg' is compared with the package name recorded in Engine's built interface; since they match, Game is allowed to access Engine's `package` declarations. When building App, its package name 'appPkg' is different from `gamePkg`, so it is not allowed to access `package` symbols in either Engine or Game, which is what we want. -Swift Package Manager has a package identity per package, an identifier that's verified to be unique via a registry, and it will pass it down automatically. Other build systems such as Bazel can introduce a new build setting to set the value for a package name. Since it needs to be unique, a reverse-DNS name could be used to avoid clashing. - -### Exportability +If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. When the Swift frontend builds a `.swiftmodule` file directly from source, the file will include the package name and all of the `package` declarations in the module. When the Swift frontend builds a `.swiftinterface` file from source, the file will include the package name, but it will put `package` declarations in a secondary `.package.swiftinterface` file. When the Swift frontend builds a `.swiftmodule` file from a `.swiftinterface` file that includes a package name, but it does not have the corresponding `.package.swiftinterface` file, it will record this in the `.swiftmodule`, and it will prevent this file from being used to build other modules in the same package. -`package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the function: they must be `open`, `public`, `package`, or `@usableFromInline`. Note that `@usableFromInline` allows the use of a symbol from `@inlinable` functions whether they're `package` or `public`. `@usableFromPackageInline` will be introduced to export a symbol only for use by `@inlinable package` functions. +### Exportability -We plan to introduce an option to hide package symbols. By default, all package symbols will be exported in the final library/executable. +`package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the function: they must be `open`, `public`, `package`, or `@usableFromInline`. Note that `@usableFromInline` allows the use of a symbol from `@inlinable` functions whether they're `package` or `public`. `@usableFromPackageInline` is introduced to export a symbol only for use by `@inlinable package` functions. ### Resiliency @@ -254,7 +250,7 @@ If a module in a package only contains symbols that are `package` or more restri ### Optimizations * A package containing several modules can be treated as a resilience domain. If same-package clients need access to module binaries, they don't need to be independently rebuildable and could have an unstable ABI; they could avoid resilience overhead and unnecessary language rules. -* By default, `package` symbols are exported in the final libraries/executables. We plan to introduce a build setting that allows users to decide whether to hide package symbols for statically linked libraries. Enabling package symbols to be hidden would help with size optimizations. +* By default, `package` symbols are exported in the final libraries/executables. We plan to introduce a build setting that allows users to hide package symbols for statically linked libraries. Enabling package symbols to be hidden would help with code size optimizations. ## Source compatibility From 070891e2c5b3d7194d0525c5427a1810e20a9b40 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 15:59:07 -0800 Subject: [PATCH 2921/4563] Update package name requirements --- proposals/NNNN-package-access-modifier.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index fb8ec0c06a..0e803663f2 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -123,7 +123,7 @@ engine.run() // Error: cannot find `run` in scope ``` ### Package Names -Two modules belong to the same package if they were built with the same package name. A package name must be unique and a valid C99 identifier, i.e. a string consisting of alphanumeric characters and an underscore, starting with a letter. It is passed to the Swift frontend via a new flag `-package-name`. Swift Package Manager has a package identity per package, an identifier that's verified to be unique via a registry, and it will pass the identifier down automatically. Note that the package identity can contain URL characters, and such character will be transposed to an underscore. Other build systems such as Bazel will need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing; a dot in such string will be transposed to an underscore. +Two modules belong to the same package if they were built with the same package name. A package name must be a unique string with the following characters; `A-Z, a-z, 0-9, _, ., and -`. It is passed to the Swift frontend via a new flag `-package-name`. Here's an example of how a package name is passed to a commandline invocation. @@ -135,6 +135,8 @@ swiftc -module-name Engine -package-name gamePkg ... When building the Engine module, the package name 'gamePkg' is recorded in the built interface to the module. When building Game, its package name 'gamePkg' is compared with the package name recorded in Engine's built interface; since they match, Game is allowed to access Engine's `package` declarations. When building App, its package name 'appPkg' is different from `gamePkg`, so it is not allowed to access `package` symbols in either Engine or Game, which is what we want. +Swift Package Manager has a package identity per package, an identifier that's verified to be unique via a registry, and it will pass the identifier down automatically. Other build systems such as Bazel will need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. + If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. When the Swift frontend builds a `.swiftmodule` file directly from source, the file will include the package name and all of the `package` declarations in the module. When the Swift frontend builds a `.swiftinterface` file from source, the file will include the package name, but it will put `package` declarations in a secondary `.package.swiftinterface` file. When the Swift frontend builds a `.swiftmodule` file from a `.swiftinterface` file that includes a package name, but it does not have the corresponding `.package.swiftinterface` file, it will record this in the `.swiftmodule`, and it will prevent this file from being used to build other modules in the same package. From 271cea7ab43e0e48262a97e0a078bd9a149254e1 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 17:18:16 -0800 Subject: [PATCH 2922/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 0e803663f2..f3dbd3c0ee 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -123,7 +123,7 @@ engine.run() // Error: cannot find `run` in scope ``` ### Package Names -Two modules belong to the same package if they were built with the same package name. A package name must be a unique string with the following characters; `A-Z, a-z, 0-9, _, ., and -`. It is passed to the Swift frontend via a new flag `-package-name`. +Two modules belong to the same package if they were built with the same package name. A package name must be a unique string of US-ASCII alphanumeric characters, `_`, `.`, or `-`; that is, it must match the regular expression `\A[A-Za-z0-9_.-]+\z`. It is passed to the Swift frontend via a new flag `-package-name`. Here's an example of how a package name is passed to a commandline invocation. From 5128f03a1b02ee64ced508598e588df0cdf21ff3 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 17:18:30 -0800 Subject: [PATCH 2923/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index f3dbd3c0ee..92d3a0f32f 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -141,7 +141,7 @@ If `-package-name` is not given, the `package` access modifier is disallowed. S When the Swift frontend builds a `.swiftmodule` file directly from source, the file will include the package name and all of the `package` declarations in the module. When the Swift frontend builds a `.swiftinterface` file from source, the file will include the package name, but it will put `package` declarations in a secondary `.package.swiftinterface` file. When the Swift frontend builds a `.swiftmodule` file from a `.swiftinterface` file that includes a package name, but it does not have the corresponding `.package.swiftinterface` file, it will record this in the `.swiftmodule`, and it will prevent this file from being used to build other modules in the same package. -### Exportability +### Package Symbols and `@inlinable` Functions `package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the function: they must be `open`, `public`, `package`, or `@usableFromInline`. Note that `@usableFromInline` allows the use of a symbol from `@inlinable` functions whether they're `package` or `public`. `@usableFromPackageInline` is introduced to export a symbol only for use by `@inlinable package` functions. From 91df530482f7dcfc0deea051de85e7bb353f3e60 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 17:20:07 -0800 Subject: [PATCH 2924/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 92d3a0f32f..79f732141e 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -135,7 +135,7 @@ swiftc -module-name Engine -package-name gamePkg ... When building the Engine module, the package name 'gamePkg' is recorded in the built interface to the module. When building Game, its package name 'gamePkg' is compared with the package name recorded in Engine's built interface; since they match, Game is allowed to access Engine's `package` declarations. When building App, its package name 'appPkg' is different from `gamePkg`, so it is not allowed to access `package` symbols in either Engine or Game, which is what we want. -Swift Package Manager has a package identity per package, an identifier that's verified to be unique via a registry, and it will pass the identifier down automatically. Other build systems such as Bazel will need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. +The Swift Package Manager already has a concept of a package identity string for every package, as specified by [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md). This string is verified to be unique via a registry, and it always works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. From 829d6fe51423a15bbe056ae23fe5f7e759fc4391 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 17:20:49 -0800 Subject: [PATCH 2925/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 79f732141e..9cd38bd535 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -153,7 +153,7 @@ Library evolution makes modules resilient. We can incorporate a package name in Access control in Swift usually doesn't distinguish between different kinds of use. If a program has access to a type, for example, that gives the programmer a broad set of privileges: the type name can be used in most places, values of the type can be borrowed, copied, and destroyed, members of the type can be accessed (up to the limits of their own access control), and so on. This is because access control is a tool for enforcing encapsulation and allowing the future evolution of code. Broad privileges are granted because restricting them more precisely usually doesn't serve that goal. -However, there are two exceptions. The first is that Swift allows `var` and `subscript` to restrict mutating accesses more tightly than read-only accesses; this is done by writing a separate access modifier for the setter, e.g. `private(set)`. The second is that Swift allows classes and class members to restrict subclassing and overriding more tightly than normal references; this is done by writing `public` instead of `open`. Allowing these privileges to be separately restricted does serve the goal of encapsulation and evolution. +However, there are two exceptions. The first is that Swift allows `var` and `subscript` to restrict mutating accesses more tightly than read-only accesses; this is done by writing a separate access modifier for the setter, e.g. `private(set)`. The second is that Swift allows classes and class members to restrict subclassing and overriding more tightly than normal references; this is done by writing `public` instead of `open`. Allowing these privileges to be separately restricted serves the goals of promoting encapsulation and evolution. Because setter access levels are controlled by writing a separate modifier from the primary access, the syntax naturally extends to allow `package(set)`. However, subclassing and overriding are different because they are controlled by writing a specific keyword as the primary access modifier. This proposal has to decide what `package` by itself means for classes and class members. It also has to decide whether to support the options not covered by `package` alone or to leave them as a possible future direction. From 08ab5f4427d83b3fb28ee732cd019694a4bc9c95 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 17:22:02 -0800 Subject: [PATCH 2926/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 9cd38bd535..29ca021e95 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -155,7 +155,7 @@ Access control in Swift usually doesn't distinguish between different kinds of u However, there are two exceptions. The first is that Swift allows `var` and `subscript` to restrict mutating accesses more tightly than read-only accesses; this is done by writing a separate access modifier for the setter, e.g. `private(set)`. The second is that Swift allows classes and class members to restrict subclassing and overriding more tightly than normal references; this is done by writing `public` instead of `open`. Allowing these privileges to be separately restricted serves the goals of promoting encapsulation and evolution. -Because setter access levels are controlled by writing a separate modifier from the primary access, the syntax naturally extends to allow `package(set)`. However, subclassing and overriding are different because they are controlled by writing a specific keyword as the primary access modifier. This proposal has to decide what `package` by itself means for classes and class members. It also has to decide whether to support the options not covered by `package` alone or to leave them as a possible future direction. +Because setter access levels are controlled by writing a separate modifier from the primary access, the syntax naturally extends to allow `package(set)`. However, subclassing and overriding are controlled by choosing a specific keyword (`public` or `open`) as the primary access modifier, so the syntax does not extend to `package` the same way. This proposal has to decide what `package` by itself means for classes and class members. It also has to decide whether to support the options not covered by `package` alone or to leave them as a possible future direction. Here is a matrix showing where each current access level can be used or overridable: From 7e1d16316e5f68eb94546df9241aa6b4cacb9411 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 24 Jan 2023 19:59:20 -0800 Subject: [PATCH 2927/4563] Accept SE-0377 and link to decision notes. (#1926) --- proposals/0377-parameter-ownership-modifiers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index 654c2bfa5f..19ccf627fc 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -3,9 +3,9 @@ * Proposal: [SE-0377](0377-parameter-ownership-modifiers.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (December 7 - December 14, 2022)** +* Status: **Accepted** * Implementation: available using the internal names `__shared` and `__owned` -* Review: ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) +* Review: ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759)) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) ## Introduction From 00a498c5e0bffe939b9f50b0f5c15c0f4705e8e6 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 25 Jan 2023 11:16:56 -0800 Subject: [PATCH 2928/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 29ca021e95..3930f46d70 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -246,7 +246,7 @@ We will need to expand on the existing `open` access modifier or introduce a new We will add a function that returns a two-dimensioned value for use and override. It will return the correct access level for use and indication on whether it's overridable. From the use aspect, the access level determined should just be `package` if not one of the existing access level. The overridable bit should return whether it's subclassable or overridable for all access levels. -### Package-private +### Package-Private Modules If a module in a package only contains symbols that are `package` or more restrictive, the whole module can be treated as private to the package. This "package-only" module can be useful for organizing modules (a utility module vs a public facing module) and enforcing the boundary with diagnostics. It can also allow module aliasing to apply automatically without the explicit module aliases parameter, which could be useful for multi-version dependencies of a package. ### Optimizations From 8f33d73b98d9ba8f2a84024759f8b8b639db10da Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 25 Jan 2023 11:22:57 -0800 Subject: [PATCH 2929/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 3930f46d70..2a2313d528 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -247,7 +247,7 @@ We will need to expand on the existing `open` access modifier or introduce a new We will add a function that returns a two-dimensioned value for use and override. It will return the correct access level for use and indication on whether it's overridable. From the use aspect, the access level determined should just be `package` if not one of the existing access level. The overridable bit should return whether it's subclassable or overridable for all access levels. ### Package-Private Modules -If a module in a package only contains symbols that are `package` or more restrictive, the whole module can be treated as private to the package. This "package-only" module can be useful for organizing modules (a utility module vs a public facing module) and enforcing the boundary with diagnostics. It can also allow module aliasing to apply automatically without the explicit module aliases parameter, which could be useful for multi-version dependencies of a package. +Sometimes entire modules are meant to be private to the package that provides them. Allowing this to be expressed directly would allow these utility modules to be completely hidden outside of the package, avoiding unwanted dependencies on the existence of the module. It would also allow the build system to automatically namespace the module within the package, reducing the need for [explicit module aliases](https://github.com/apple/swift-evolution/blob/main/proposals/0339-module-aliasing-for-disambiguation.md) when utility modules of different packages share a name (such as `Utility`) or when multiple versions of a package need to be built into the same program. ### Optimizations * A package containing several modules can be treated as a resilience domain. If same-package clients need access to module binaries, they don't need to be independently rebuildable and could have an unstable ABI; they could avoid resilience overhead and unnecessary language rules. From 81f820e8d74971dc17065e1f984c83f69c5d486e Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 25 Jan 2023 11:31:14 -0800 Subject: [PATCH 2930/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 2a2313d528..019bc4e276 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -265,7 +265,25 @@ Boundaries between separately-built modules within a package are still potential ## Alternatives considered ### Use Current Workarounds -A current workaround for the scenario in the Motivation is to use `@_spi` or `@_implemenationOnly`. Each option has caveats. The `@_spi` requires a group name, which makes it verbose to use and harder to keep track of, and `@_implementationOnly` can be too limiting as we want to be able to restrict access to only portions of APIs. There are also hacky workarounds such as `@testable` and the `-disable-access-control` flag. They elevate all `internal` (and `private` with the flag) symbols to `public`. These options are unstable and also quickly lead to an increase of the binary and the shared cache size, not to mention symbol name clashes. +### `@_spi` + +One workaround for the scenario in the Motivation would be to use the `@_spi(groupName)` attribute, which allows part of the API of a module to be hidden unless it is imported in a special way that explicitly requests access to it. This is an unsatisfying alternative to package-level access control because it is designed around a very different situation. An SPI is a "hole" in the normal public interface, one meant for the use of a specific client. That client is typically outside of the module's normal code-distribution boundary, but the module authors still have a cooperative working relationship. This relationship is reflected in the design of `@_spi` in multiple ways: + +- First, access to the SPI is granted to a specific client by name. This is a clear and unavoidable communication of intent about who is meant to be using the SPI. Other clients can still pose as this client and use the SPI, but that would be a clear breach of trust with predictable consequences. + +- Second, clients must explicitly request the SPI by name. This means that clients must opt in to using the SPI in every file, which works to limit its accidental over-use even by the intended client. It also means that SPI use is obvious in the code, which code reviewers can see and raise questions about, and which SPI authors can easily find with a code search. + +The level of care implied by these properties is appropriate for a carefully-targeted hole in an API that must cross a code-distribution boundary and will therefore require equal amounts of care to ever modify or close. That rarely applies to two modules within the same package, where a package-level interface can ideally be changed with just a quick edit to a few different parts of a repository. The `@_spi` attribute is intentionally designed to not be as lightweight as a package-local change should be. + +`@_spi` would also not be easy to optimize. By design, clients of an SPI can be anywhere, making it effectively part of the public ABI of a module. To avoid exporting an SPI, the build system would have to know about that specific SPI group and promise the compiler that it was only used in the current built image. Recognizing that all of the modules in a package are being linked into the same image and can be optimized together is comparatively easy for a build system and so is a much more feasible future direction. + +### `@_implementationOnly` + +Another workaround for the scenario in the Motivation is to use the `@_implementationOnly` attribute on the import of a module. This attribute causes the module to be imported only for the use of the current module; clients of the current module don't implicitly transitively import the target module, and the symbols of the target module are restricted from appearing in the `public` API of the current module. This would prevent clients from accidentally using APIs from the target module. However, this is a very incomplete workaround for the lack of package-level access control. For one, it doesn't actually prevent access to the module, which can still be explicitly imported and used. For another, it only works on an entire module at a time, so a module cannot restrict some of its APIs to the package while making others available publicly. Taming transitive import would be a good future direction for Swift, but it does not solve the problems of package-level APIs. + +### Other workarounds + +There are a few other workarounds to the absence of package-level access control, such as using `@testable` or the `-disable-access-control` flag. These are hacky subversions of Swift's language design, and they severely undermine the use of module boundaries for encapsulation. `-disable-access-control` is also an unstable and unsupported feature that can introduce build failures by causing symbol name collisions. ### Introduce Submodules Instead of adding a new package access level above modules, we could allow modules to contain other modules as components. This is an idea often called "submodules". Packages would then define an "umbrella" module that contains the package's modules as components. However, there are several weaknesses in this approach: From 31f9be0e0fda3345e5807aca95e37e245fbd15f1 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 24 Jan 2023 18:47:25 -0800 Subject: [PATCH 2931/4563] back to prev table --- proposals/NNNN-package-access-modifier.md | 74 +++++++++++------------ 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 019bc4e276..f44fa4346d 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -157,33 +157,32 @@ However, there are two exceptions. The first is that Swift allows `var` and `su Because setter access levels are controlled by writing a separate modifier from the primary access, the syntax naturally extends to allow `package(set)`. However, subclassing and overriding are controlled by choosing a specific keyword (`public` or `open`) as the primary access modifier, so the syntax does not extend to `package` the same way. This proposal has to decide what `package` by itself means for classes and class members. It also has to decide whether to support the options not covered by `package` alone or to leave them as a possible future direction. -Here is a matrix showing where each current access level can be used or overridable: +Here is a matrix showing where symbols with each current access level can be used or overridden: - - - + + + - - - + + + - - - + + + - - - + + + -
    UseOverride/SubclassSubclassable in... \ Accessible in...anywheremodule
    internalin-modulein-moduleanywhereopen(illegal)
    publiccross-modulein-modulemodulepublicinternal
    opencross-modulecross-modulenowherepublic finalinternal final
    @@ -192,46 +191,41 @@ With `package` as a new access modifier, the matrix is modified like so: - - - + + + + - - - + + + + - - - - - - - + + + - - - + + + + - - - + + + + - - - - - -
    UseOverride/SubclassSubclassable in... \ Accessible in...anywherepackagemodule
    internalin-modulein-moduleanywhereopen(illegal)(illegal)
    packagecross-module (in package)in-module
    ?(1)cross-module (in package)cross-module (in package)?(a)?(b)(illegal)
    ?(2)cross-module (cross-package)cross-module (in package)modulepublicpackageinternal
    publiccross-module (cross-package)in-modulenowherepublic finalpackage finalinternal final
    opencross-module (cross-package)cross-module (cross-package)
    + This proposal takes the position that `package` alone should not allow subclassing or overriding outside of the defining module. This is consistent with the behavior of `public` and makes `package` fit into a simple continuum of ever-expanding privileges. It also allows the normal optimization model of `public` classes and methods to still be applied to `package` classes and methods, implicitly making them `final` when they aren't subclassed or overridden, without requiring a new "whole package optimization" build mode. However, this choice leaves no way to spell the two combinations marked in the table above with `?`. These are more complicated to design and implement and are discussed in Future Directions. @@ -240,7 +234,7 @@ However, this choice leaves no way to spell the two combinations marked in the t ## Future Directions ### Subclassing and Overrides -The entities marked with `?(1)` and `?(2)` from the matrix above both require accessing and subclassing cross-modules in a package (`open` within a package). The only difference is that (1) hides the symbol from outside of the package and (2) makes it visible outside. Use cases involving (2) should be rare but its underlying flow should be the same as (1) except its symbol visibility. +The entities marked with `?(a)` and `?(b)` from the matrix above both require accessing and subclassing cross-modules in a package (`open` within a package). The only difference is that (b) hides the symbol from outside of the package and (a) makes it visible outside. Use cases involving (a) should be rare but its underlying flow should be the same as (b) except its symbol visibility. We will need to expand on the existing `open` access modifier or introduce a new access modifier. The exact name is TBD, but so far suggestions include `packageopen`, `package open`, `open(package)`, and `open package(set)`. The `open package(set)` might be a good candidate since we can utilize the existing flow between `open` and `public` that allows subclasses to be `public` and expand on it to control the visibility of the base class. If we were to use a new access modifier, we might need more than one, e.g. `packageopen` that corresponds to (1) and `public packageopen` that corresponds to (2), and will also need to handle the inheritance access level hirearchy, i.e. whether subclasses can be `public` when their base class is `packageopen`. From 127612c3e7c6e1e6e98d572c5c844fcedf28f2bd Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 25 Jan 2023 11:15:34 -0800 Subject: [PATCH 2932/4563] updates --- proposals/NNNN-package-access-modifier.md | 54 +++++++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index f44fa4346d..60f7ab43be 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -145,6 +145,41 @@ When the Swift frontend builds a `.swiftmodule` file directly from source, the f `package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the function: they must be `open`, `public`, `package`, or `@usableFromInline`. Note that `@usableFromInline` allows the use of a symbol from `@inlinable` functions whether they're `package` or `public`. `@usableFromPackageInline` is introduced to export a symbol only for use by `@inlinable package` functions. +`package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the `@inlinable package` function: they must be `open`, `public`, `package`, `@usableFromInline`, or `usableFromPackageInline`. +`@usableFromPackageInline` is a new attribute which allows a symbol to be used from `@inlinable package` functions (that are defined in the same module) without having to make the symbol `package` or `public`. It can be used anywhere that `@usableFromInline` can be used, but the attributes cannot be combined. A `@usableFromPackageInline` symbol must be `internal`. For example: + +``` +@usableFromPackageInline func internalFuncA() {} + +@inlinable package func pkgUse() { + internalFuncA() // OK +} + +@inlinable public func publicUse() { + internalFuncA() // OK +} +``` + +The existing `@usableFromInline` attribute can be applied to `package` symbols as well as `internal` symbols. This allows those symbols to be used from `@inlinable public` functions (that are defined anywhere in the same package) without having to make them `public`. The Swift frontend will include `@usableFromInline package` symbols in the ordinary `.swiftinterface` for a module, not its `.package.swiftinterface`. + +``` +@usableFromPackageInline func internalFuncA() {} +@usableFromInline func internalFuncB() {} +@usableFromInline package func packageFunc() {} + +@inlinable package func pkgUse() { + internalFuncA() // OK + internalFuncB() // OK + packageFunc() // OK +} + +@inlinable public func publicUse() { + internalFuncA() // ERROR + internalFuncB() // OK + packageFunc() // OK +} +``` + ### Resiliency Library evolution makes modules resilient. We can incorporate a package name into a resiliency check and bypass it if modules are in the same package. This will remove the need for resilience overhead such as indirection and language requirements such as `@unknown default` for an unfrozen `enum`. @@ -234,19 +269,20 @@ However, this choice leaves no way to spell the two combinations marked in the t ## Future Directions ### Subclassing and Overrides -The entities marked with `?(a)` and `?(b)` from the matrix above both require accessing and subclassing cross-modules in a package (`open` within a package). The only difference is that (b) hides the symbol from outside of the package and (a) makes it visible outside. Use cases involving (a) should be rare but its underlying flow should be the same as (b) except its symbol visibility. -We will need to expand on the existing `open` access modifier or introduce a new access modifier. The exact name is TBD, but so far suggestions include `packageopen`, `package open`, `open(package)`, and `open package(set)`. The `open package(set)` might be a good candidate since we can utilize the existing flow between `open` and `public` that allows subclasses to be `public` and expand on it to control the visibility of the base class. If we were to use a new access modifier, we might need more than one, e.g. `packageopen` that corresponds to (1) and `public packageopen` that corresponds to (2), and will also need to handle the inheritance access level hirearchy, i.e. whether subclasses can be `public` when their base class is `packageopen`. +The entities marked with `?(a)` and `?(b)` from the matrix above both require accessing and subclassing cross-modules in a package (`open` within a package). The only difference is that (b) hides the symbol from outside of the package and (a) makes it visible outside. Use cases involving (a) should be rare but its underlying flow should be the same as (b) except its symbol visibility. -We will add a function that returns a two-dimensioned value for use and override. It will return the correct access level for use and indication on whether it's overridable. From the use aspect, the access level determined should just be `package` if not one of the existing access level. The overridable bit should return whether it's subclassable or overridable for all access levels. +Potential solutions include introducing new keywords for specific access combinations (e.g. `packageopen`), allowing `open` to be access-qualified (e.g. `open(package)`), and allowing access modifiers to be qualified with specific purposes (e.g. `package(override)`). ### Package-Private Modules + Sometimes entire modules are meant to be private to the package that provides them. Allowing this to be expressed directly would allow these utility modules to be completely hidden outside of the package, avoiding unwanted dependencies on the existence of the module. It would also allow the build system to automatically namespace the module within the package, reducing the need for [explicit module aliases](https://github.com/apple/swift-evolution/blob/main/proposals/0339-module-aliasing-for-disambiguation.md) when utility modules of different packages share a name (such as `Utility`) or when multiple versions of a package need to be built into the same program. ### Optimizations + * A package containing several modules can be treated as a resilience domain. If same-package clients need access to module binaries, they don't need to be independently rebuildable and could have an unstable ABI; they could avoid resilience overhead and unnecessary language rules. -* By default, `package` symbols are exported in the final libraries/executables. We plan to introduce a build setting that allows users to hide package symbols for statically linked libraries. Enabling package symbols to be hidden would help with code size optimizations. +* By default, `package` symbols are exported in the final libraries/executables. It would be useful to introduce a build setting that allows users to hide package symbols for statically linked libraries; this would help with code size and build time optimizations. ## Source compatibility @@ -254,22 +290,21 @@ A new keyword `package` is added as a new access modifier. It is a contextual k ## Effect on ABI stability -Boundaries between separately-built modules within a package are still potentially ABI boundaries. The ABI for package symbols is not different from the ABI for public symbols, although in the future we plan to add an option to not export package symbols that can be resolved within an image. +Boundaries between separately-built modules within a package are still potentially ABI boundaries. The ABI for package symbols is not different from the ABI for public symbols, although it might be considered in the future to add an option to not export package symbols that can be resolved within an image. ## Alternatives considered -### Use Current Workarounds ### `@_spi` One workaround for the scenario in the Motivation would be to use the `@_spi(groupName)` attribute, which allows part of the API of a module to be hidden unless it is imported in a special way that explicitly requests access to it. This is an unsatisfying alternative to package-level access control because it is designed around a very different situation. An SPI is a "hole" in the normal public interface, one meant for the use of a specific client. That client is typically outside of the module's normal code-distribution boundary, but the module authors still have a cooperative working relationship. This relationship is reflected in the design of `@_spi` in multiple ways: -- First, access to the SPI is granted to a specific client by name. This is a clear and unavoidable communication of intent about who is meant to be using the SPI. Other clients can still pose as this client and use the SPI, but that would be a clear breach of trust with predictable consequences. +* First, access to the SPI is granted to a specific client by name. This is a clear and unavoidable communication of intent about who is meant to be using the SPI. Other clients can still pose as this client and use the SPI, but that would be a clear breach of trust with predictable consequences. -- Second, clients must explicitly request the SPI by name. This means that clients must opt in to using the SPI in every file, which works to limit its accidental over-use even by the intended client. It also means that SPI use is obvious in the code, which code reviewers can see and raise questions about, and which SPI authors can easily find with a code search. +* Second, clients must explicitly request the SPI by name. This means that clients must opt in to using the SPI in every file, which works to limit its accidental over-use even by the intended client. It also means that SPI use is obvious in the code, which code reviewers can see and raise questions about, and which SPI authors can easily find with a code search. The level of care implied by these properties is appropriate for a carefully-targeted hole in an API that must cross a code-distribution boundary and will therefore require equal amounts of care to ever modify or close. That rarely applies to two modules within the same package, where a package-level interface can ideally be changed with just a quick edit to a few different parts of a repository. The `@_spi` attribute is intentionally designed to not be as lightweight as a package-local change should be. -`@_spi` would also not be easy to optimize. By design, clients of an SPI can be anywhere, making it effectively part of the public ABI of a module. To avoid exporting an SPI, the build system would have to know about that specific SPI group and promise the compiler that it was only used in the current built image. Recognizing that all of the modules in a package are being linked into the same image and can be optimized together is comparatively easy for a build system and so is a much more feasible future direction. +* `@_spi` would also not be easy to optimize. By design, clients of an SPI can be anywhere, making it effectively part of the public ABI of a module. To avoid exporting an SPI, the build system would have to know about that specific SPI group and promise the compiler that it was only used in the current built image. Recognizing that all of the modules in a package are being linked into the same image and can be optimized together is comparatively easy for a build system and so is a much more feasible future direction. ### `@_implementationOnly` @@ -280,6 +315,7 @@ Another workaround for the scenario in the Motivation is to use the `@_implement There are a few other workarounds to the absence of package-level access control, such as using `@testable` or the `-disable-access-control` flag. These are hacky subversions of Swift's language design, and they severely undermine the use of module boundaries for encapsulation. `-disable-access-control` is also an unstable and unsupported feature that can introduce build failures by causing symbol name collisions. ### Introduce Submodules + Instead of adding a new package access level above modules, we could allow modules to contain other modules as components. This is an idea often called "submodules". Packages would then define an "umbrella" module that contains the package's modules as components. However, there are several weaknesses in this approach: * It doesn't actually solve the problem by itself. Submodule APIs would still need to be able to declare whether they're usable outside of the umbrella or not, and that would require an access modifier. It might be written in a more general way, like `internal(MyPackage)`, but that generality would also make it more verbose. From d27b8c577d41c3f197a2864d44dbd9ddedcfe8cd Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 25 Jan 2023 13:53:20 -0800 Subject: [PATCH 2933/4563] formatting --- proposals/NNNN-package-access-modifier.md | 39 +++++++++++------------ 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 60f7ab43be..0853ba5d5d 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -25,9 +25,8 @@ For example, here’s a scenario where a client has access to a utility API from Here are source code examples. +Module `Engine` (in `gamePkg`): ``` -[Engine (a module in gamePkg)] - public struct MainEngine { public init() { ... } // Intended to be public @@ -35,17 +34,19 @@ public struct MainEngine { // A helper function made public only to be accessed by Game public func run() { ... } } +``` -[Game (a module in gamePkg)] - +Module `Game` (in `gamePkg`): +``` import Engine public func play() { MainEngine().run() // Can access `run` as intended since it's within the same package } +``` -[App (an executable in `appPkg`)] - +Client `App` (in `appPkg`): +``` import Game import Engine @@ -55,7 +56,7 @@ Game.play() print(engine.stats) // Can access `stats` as intended ``` -In the above scenario, App can import Engine (a utility module in 'gamePkg') and access its helper API directly, even though the API is not intended to be used outside of its package. +In the above scenario, `App` can import `Engine` (a utility module in 'gamePkg') and access its helper API directly, even though the API is not intended to be used outside of its package. Allowing this kind of unintended public access to package APIs is especially bad because packages are a unit of code distribution. Swift wants to encourage programs to be divided into modules with well-defined interfaces, so it enforces the boundaries between modules with access control. Despite being divided this way, it's not uncommon for closely-related modules to be written by closely-related (or even the same) people. Access control between such modules still serves a purpose — it promotes the separation of concerns — but if a module's interface needs to be fixed, that's usually easy to coordinate, maybe even as simple as a single commit. However, packages allow code to be shared much more broadly than a single small organization. The boundaries between packages often represent significant differences between programmers, making coordination around API changes much more difficult. For example, the developers of an open source package generally don't know most of their clients, and the standard recommendation is for such packages to only ever remove existing APIs in major-version releases. It's therefore particularly important to allow programmers to enforce these boundaries between packages. @@ -75,14 +76,12 @@ Our goal is to introduce a mechanism to Swift to recognize a package as a unit i package var package: String {...} ``` - ### Declaration Site The `package` keyword is added at the declaration site. Using the scenario above, the helper API `run` can be declared with the new access modifier like so: +Module `Engine`: ``` -[Engine] - public struct MainEngine { public init() { ... } public var stats: String { ... } @@ -97,11 +96,10 @@ Swift requires that the declarations used in certain places (such as the signatu ### Use Site -The Game module can access the helper API `run` since it is in the same package as Engine. +The `Game` module can access the helper API `run` since it is in the same package as `Engine`. +Module `Game`: ``` -[Game] - import Engine public func play() { @@ -111,16 +109,15 @@ public func play() { However, if a client outside of the package tries to access the helper API, it will not be allowed. +Client `App`: ``` -[App] - import Game import Engine let engine = MainEngine() engine.run() // Error: cannot find `run` in scope - ``` + ### Package Names Two modules belong to the same package if they were built with the same package name. A package name must be a unique string of US-ASCII alphanumeric characters, `_`, `.`, or `-`; that is, it must match the regular expression `\A[A-Za-z0-9_.-]+\z`. It is passed to the Swift frontend via a new flag `-package-name`. @@ -129,13 +126,13 @@ Here's an example of how a package name is passed to a commandline invocation. ``` swiftc -module-name Engine -package-name gamePkg ... -[Game] swiftc -module-name Game -package-name gamePkg ... -[App] swiftc App -package-name appPkg ... +swiftc -module-name Game -package-name gamePkg ... +swiftc App -package-name appPkg ... ``` -When building the Engine module, the package name 'gamePkg' is recorded in the built interface to the module. When building Game, its package name 'gamePkg' is compared with the package name recorded in Engine's built interface; since they match, Game is allowed to access Engine's `package` declarations. When building App, its package name 'appPkg' is different from `gamePkg`, so it is not allowed to access `package` symbols in either Engine or Game, which is what we want. +When building the `Engine` module, the package name 'gamePkg' is recorded in the built interface to the module. When building `Game`, its package name 'gamePkg' is compared with the package name recorded in `Engine`'s built interface; since they match, `Game` is allowed to access `Engine`'s `package` declarations. When building `App`, its package name 'appPkg' is different from `gamePkg`, so it is not allowed to access `package` symbols in either `Engine` or `Game`, which is what we want. -The Swift Package Manager already has a concept of a package identity string for every package, as specified by [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md). This string is verified to be unique via a registry, and it always works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. +The Swift Package Manager already has a concept of a package idpackage identity string for every package, as specified by [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md). This string is verified to be unique via a registry, and it always works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. @@ -310,7 +307,7 @@ The level of care implied by these properties is appropriate for a carefully-tar Another workaround for the scenario in the Motivation is to use the `@_implementationOnly` attribute on the import of a module. This attribute causes the module to be imported only for the use of the current module; clients of the current module don't implicitly transitively import the target module, and the symbols of the target module are restricted from appearing in the `public` API of the current module. This would prevent clients from accidentally using APIs from the target module. However, this is a very incomplete workaround for the lack of package-level access control. For one, it doesn't actually prevent access to the module, which can still be explicitly imported and used. For another, it only works on an entire module at a time, so a module cannot restrict some of its APIs to the package while making others available publicly. Taming transitive import would be a good future direction for Swift, but it does not solve the problems of package-level APIs. -### Other workarounds +### Other Workarounds There are a few other workarounds to the absence of package-level access control, such as using `@testable` or the `-disable-access-control` flag. These are hacky subversions of Swift's language design, and they severely undermine the use of module boundaries for encapsulation. `-disable-access-control` is also an unstable and unsupported feature that can introduce build failures by causing symbol name collisions. From cf2599fec8cd8c4a1d0040cd6f4fc12c6e64db37 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 25 Jan 2023 22:40:25 -0800 Subject: [PATCH 2934/4563] Minor tweaks for updated SE-0382 --- proposals/nnnn-freestanding-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index f0e0044f59..6439875b35 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -45,7 +45,7 @@ protocol FreestandingMacro: Macro { } ### Expression macros -As previously noted, expression macros are one form of freestanding macro. As such, we revise the definition of the `ExpressionMacro` protocol provided in [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) by making it refine `FreestandingMacro`: +As previously noted, expression macros are one form of freestanding macro. [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) already introduced the `FreestandingMacro` protocol and the `ExpressionMacro` protocol that inherits from it: ```swift protocol ExpressionMacro: FreestandingMacro { @@ -53,7 +53,7 @@ protocol ExpressionMacro: FreestandingMacro { } ``` -Additionally, we replace the `@expression` attribute introduced in SE-0382 with `@freestanding(expression)`. For example, the `stringify` macro would be declared as follows: +As well as the `@freestanding(expression)` syntax: ```swift @freestanding(expression) macro stringify(_: T) -> (T, String) From 3d70fc217aa939e5863e259113158f65a5b105eb Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Thu, 26 Jan 2023 14:03:49 -0800 Subject: [PATCH 2935/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 0853ba5d5d..80e1d56e3f 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -132,7 +132,7 @@ swiftc App -package-name appPkg ... When building the `Engine` module, the package name 'gamePkg' is recorded in the built interface to the module. When building `Game`, its package name 'gamePkg' is compared with the package name recorded in `Engine`'s built interface; since they match, `Game` is allowed to access `Engine`'s `package` declarations. When building `App`, its package name 'appPkg' is different from `gamePkg`, so it is not allowed to access `package` symbols in either `Engine` or `Game`, which is what we want. -The Swift Package Manager already has a concept of a package idpackage identity string for every package, as specified by [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md). This string is verified to be unique via a registry, and it always works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. +The Swift Package Manager already has a concept of a package identity string for every package, as specified by [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md). This string is verified to be unique via a registry, and it always works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. From a8cefcd06914d93c39c7b30b98fd75a5af2063f3 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Thu, 26 Jan 2023 14:04:14 -0800 Subject: [PATCH 2936/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 80e1d56e3f..0e7c7b8631 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -9,7 +9,7 @@ ## Introduction -This proposal introduces `package` as a new access modifier. Currently, to access a symbol in another module, that symbol needs to be declared `public`. However, a symbol being `public` allows it to be accessed from any module at all, both within a package and from outside of a package, which is sometimes undesirable. We need a new access modifier to enable a more fine control over the visibility scope of such symbols. +This proposal introduces `package` as a new access modifier. Currently, to access a symbol in another module, that symbol needs to be declared `public`. However, a symbol being `public` allows it to be accessed from any module at all, both within a package and from outside of a package, which is sometimes undesirable. We need a new access modifier to enable more control over the visibility scope of such symbols. ## Motivation From 78f480694b450c14be9ec7096a9dec1e09544576 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Thu, 26 Jan 2023 14:04:31 -0800 Subject: [PATCH 2937/4563] Update proposals/NNNN-package-access-modifier.md Co-authored-by: John McCall --- proposals/NNNN-package-access-modifier.md | 1 - 1 file changed, 1 deletion(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 0e7c7b8631..c4ff4bcb49 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -23,7 +23,6 @@ However, because Swift does not recognize organizations of code above the module For example, here’s a scenario where a client has access to a utility API from a package it depends on. The client `App` could be an executable or an Xcode project. It depends on a package called `gamePkg`, which contains two modules, `Game` and `Engine`. -Here are source code examples. Module `Engine` (in `gamePkg`): ``` From 28fd2fb9b7258117f912cec5e5f7eb178520fbf2 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Thu, 26 Jan 2023 14:17:04 -0800 Subject: [PATCH 2938/4563] updates --- proposals/NNNN-package-access-modifier.md | 50 ++++++++++------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index c4ff4bcb49..367772e714 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -24,7 +24,7 @@ However, because Swift does not recognize organizations of code above the module For example, here’s a scenario where a client has access to a utility API from a package it depends on. The client `App` could be an executable or an Xcode project. It depends on a package called `gamePkg`, which contains two modules, `Game` and `Engine`. -Module `Engine` (in `gamePkg`): +Module `Engine` in `gamePkg`: ``` public struct MainEngine { public init() { ... } @@ -35,7 +35,7 @@ public struct MainEngine { } ``` -Module `Game` (in `gamePkg`): +Module `Game` in `gamePkg`: ``` import Engine @@ -44,7 +44,7 @@ public func play() { } ``` -Client `App` (in `appPkg`): +Client `App` in `appPkg`: ``` import Game import Engine @@ -55,11 +55,10 @@ Game.play() print(engine.stats) // Can access `stats` as intended ``` -In the above scenario, `App` can import `Engine` (a utility module in 'gamePkg') and access its helper API directly, even though the API is not intended to be used outside of its package. +In the above scenario, `App` can import `Engine` (a utility module in `gamePkg`) and access its helper API directly, even though the API is not intended to be used outside of its package. Allowing this kind of unintended public access to package APIs is especially bad because packages are a unit of code distribution. Swift wants to encourage programs to be divided into modules with well-defined interfaces, so it enforces the boundaries between modules with access control. Despite being divided this way, it's not uncommon for closely-related modules to be written by closely-related (or even the same) people. Access control between such modules still serves a purpose — it promotes the separation of concerns — but if a module's interface needs to be fixed, that's usually easy to coordinate, maybe even as simple as a single commit. However, packages allow code to be shared much more broadly than a single small organization. The boundaries between packages often represent significant differences between programmers, making coordination around API changes much more difficult. For example, the developers of an open source package generally don't know most of their clients, and the standard recommendation is for such packages to only ever remove existing APIs in major-version releases. It's therefore particularly important to allow programmers to enforce these boundaries between packages. - ## Proposed solution Our goal is to introduce a mechanism to Swift to recognize a package as a unit in the aspect of access control. We propose to do so by introducing a new access modifier called `package`. The `package` access modifier allows symbols to be accessed from outside of their defining module, but only from other modules in the same package. This helps to set clear boundaries between packages. @@ -126,10 +125,10 @@ Here's an example of how a package name is passed to a commandline invocation. ``` swiftc -module-name Engine -package-name gamePkg ... swiftc -module-name Game -package-name gamePkg ... -swiftc App -package-name appPkg ... +swiftc -module-name App -package-name appPkg ... ``` -When building the `Engine` module, the package name 'gamePkg' is recorded in the built interface to the module. When building `Game`, its package name 'gamePkg' is compared with the package name recorded in `Engine`'s built interface; since they match, `Game` is allowed to access `Engine`'s `package` declarations. When building `App`, its package name 'appPkg' is different from `gamePkg`, so it is not allowed to access `package` symbols in either `Engine` or `Game`, which is what we want. +When building the `Engine` module, the package name `gamePkg` is recorded in the built interface to the module. When building `Game`, its package name `gamePkg` is compared with the package name recorded in `Engine`'s built interface; since they match, `Game` is allowed to access `Engine`'s `package` declarations. When building `App`, its package name `appPkg` is different from `gamePkg`, so it is not allowed to access `package` symbols in either `Engine` or `Game`, which is what we want. The Swift Package Manager already has a concept of a package identity string for every package, as specified by [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md). This string is verified to be unique via a registry, and it always works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. @@ -139,8 +138,6 @@ When the Swift frontend builds a `.swiftmodule` file directly from source, the f ### Package Symbols and `@inlinable` Functions -`package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the function: they must be `open`, `public`, `package`, or `@usableFromInline`. Note that `@usableFromInline` allows the use of a symbol from `@inlinable` functions whether they're `package` or `public`. `@usableFromPackageInline` is introduced to export a symbol only for use by `@inlinable package` functions. - `package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the `@inlinable package` function: they must be `open`, `public`, `package`, `@usableFromInline`, or `usableFromPackageInline`. `@usableFromPackageInline` is a new attribute which allows a symbol to be used from `@inlinable package` functions (that are defined in the same module) without having to make the symbol `package` or `public`. It can be used anywhere that `@usableFromInline` can be used, but the attributes cannot be combined. A `@usableFromPackageInline` symbol must be `internal`. For example: @@ -176,10 +173,6 @@ The existing `@usableFromInline` attribute can be applied to `package` symbols a } ``` -### Resiliency - -Library evolution makes modules resilient. We can incorporate a package name into a resiliency check and bypass it if modules are in the same package. This will remove the need for resilience overhead such as indirection and language requirements such as `@unknown default` for an unfrozen `enum`. - ### Subclassing and Overrides Access control in Swift usually doesn't distinguish between different kinds of use. If a program has access to a type, for example, that gives the programmer a broad set of privileges: the type name can be used in most places, values of the type can be borrowed, copied, and destroyed, members of the type can be accessed (up to the limits of their own access control), and so on. This is because access control is a tool for enforcing encapsulation and allowing the future evolution of code. Broad privileges are granted because restricting them more precisely usually doesn't serve that goal. @@ -193,24 +186,24 @@ Here is a matrix showing where symbols with each current access level can be use - - - + + + - + - + - + @@ -222,33 +215,33 @@ With `package` as a new access modifier, the matrix is modified like so:
    Subclassable in... \ Accessible in...anywheremoduleAccessible AnywhereAccessible in Module
    anywhereSubclassable Anywhere open (illegal)
    moduleSubclassable in Module public internal
    nowhereSubclassable Nowhere public final internal final
    - - - - + + + + - + - + - + - + @@ -261,7 +254,6 @@ This proposal takes the position that `package` alone should not allow subclassi However, this choice leaves no way to spell the two combinations marked in the table above with `?`. These are more complicated to design and implement and are discussed in Future Directions. - ## Future Directions ### Subclassing and Overrides @@ -276,7 +268,7 @@ Sometimes entire modules are meant to be private to the package that provides th ### Optimizations -* A package containing several modules can be treated as a resilience domain. If same-package clients need access to module binaries, they don't need to be independently rebuildable and could have an unstable ABI; they could avoid resilience overhead and unnecessary language rules. +* A package can be treated as a resilience domain, even with library evolution enabled which makes modules resilient. The Swift frontend will assume that modules defined in the same package will always be rebuilt together and do not require a resilient ABI boundary between them. This removes the need for performance and code size overhead introduced by ABI-resilient code generation, and it also eliminates language requirements such as `@unknown default` for a non-`frozen enum`. * By default, `package` symbols are exported in the final libraries/executables. It would be useful to introduce a build setting that allows users to hide package symbols for statically linked libraries; this would help with code size and build time optimizations. From 34086828c48a683f56aea05056381da20fa963dd Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 26 Jan 2023 18:04:39 -0500 Subject: [PATCH 2939/4563] Assign SE-0386 to the `package` access modifier proposal --- proposals/NNNN-package-access-modifier.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/NNNN-package-access-modifier.md index 367772e714..efc7acb461 100644 --- a/proposals/NNNN-package-access-modifier.md +++ b/proposals/NNNN-package-access-modifier.md @@ -1,9 +1,9 @@ # New access modifier: `package` -* Proposal: [SE-NNNN](NNNN-package-access-modifier.md) +* Proposal: [SE-0386](0386-package-access-modifier.md) * Authors: [Ellie Shin](https://github.com/elsh), [Alexis Laferriere](https://github.com/xymus) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Awaiting review** +* Status: **Active Review (January 26th...Feburary 8th, 2023** * Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/62700), [apple/swift#62704](https://github.com/apple/swift/pull/62704), [apple/swift#62652](https://github.com/apple/swift/pull/62652), [apple/swift#62652](https://github.com/apple/swift/pull/62652) * Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) @@ -91,7 +91,6 @@ The `package` access modifier can be used anywhere that the existing access modi Swift requires that the declarations used in certain places (such as the signature of a function) be at least as accessible as the containing declaration. For the purposes of this rule, `package` is less accessible than `open` and `public` and more accessible than `internal`, `fileprivate`, and `private`. For example, a `public` function cannot use a `package` type in its parameters or return type, and a `package` function cannot use an `internal` type in its parameters or return type. Similarly, an `@inlinable` `public` function cannot use a `package` declaration in its implementation, and an `@inlinable` `package` function cannot use an `internal` declaration in its implementation. - ### Use Site The `Game` module can access the helper API `run` since it is in the same package as `Engine`. @@ -139,6 +138,7 @@ When the Swift frontend builds a `.swiftmodule` file directly from source, the f ### Package Symbols and `@inlinable` Functions `package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the `@inlinable package` function: they must be `open`, `public`, `package`, `@usableFromInline`, or `usableFromPackageInline`. + `@usableFromPackageInline` is a new attribute which allows a symbol to be used from `@inlinable package` functions (that are defined in the same module) without having to make the symbol `package` or `public`. It can be used anywhere that `@usableFromInline` can be used, but the attributes cannot be combined. A `@usableFromPackageInline` symbol must be `internal`. For example: ``` @@ -307,10 +307,12 @@ There are a few other workarounds to the absence of package-level access control Instead of adding a new package access level above modules, we could allow modules to contain other modules as components. This is an idea often called "submodules". Packages would then define an "umbrella" module that contains the package's modules as components. However, there are several weaknesses in this approach: * It doesn't actually solve the problem by itself. Submodule APIs would still need to be able to declare whether they're usable outside of the umbrella or not, and that would require an access modifier. It might be written in a more general way, like `internal(MyPackage)`, but that generality would also make it more verbose. + * Submodule structure would be part of the source language, so it would naturally be source- and ABI-affecting. For example, programmers could use the parent module's name to qualify identifiers, and symbols exported by a submodule would include the parent module's name. This means that splitting a module into submodules or adding an umbrella parent module would be much more impactful than desired; ideally, those changes would be purely internal and not change a module's public interface. It also means that these changes would end up permanently encoding package structure. + * The "umbrella" submodule structure doesn't work for all packages. Some packages include multiple "top-level" modules which share common dependencies. Forcing these to share a common umbrella in order to use package-private dependencies is not desirable. -* In a few cases, the ABI and source impact above would be desirable. For example, many packages contain internal Utility modules; if these were declared as submodules, they would naturally be namespaced to the containing package, eliminating spurious collisions. However, such modules are generally not meant to be usable outside of the package at all. It is a reasonable future direction to allow whole modules to be made package-private, which would also make it reasonable to automatically namespace them. +* In a few cases, the ABI and source impact above would be desirable. For example, many packages contain internal Utility modules; if these were declared as submodules, they would naturally be namespaced to the containing package, eliminating spurious collisions. However, such modules are generally not meant to be usable outside of the package at all. It is a reasonable future direction to allow whole modules to be made package-private, which would also make it reasonable to automatically namespace them. ## Acknowledgments From abf71fde75d96eff124d56097253285f13faf09e Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 26 Jan 2023 18:05:51 -0500 Subject: [PATCH 2940/4563] Fix SE-0386's file name --- ...package-access-modifier.md => 0386-package-access-modifier.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{NNNN-package-access-modifier.md => 0386-package-access-modifier.md} (100%) diff --git a/proposals/NNNN-package-access-modifier.md b/proposals/0386-package-access-modifier.md similarity index 100% rename from proposals/NNNN-package-access-modifier.md rename to proposals/0386-package-access-modifier.md From 32e51946296f67be79a58a8c23eb9d7460a06232 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 26 Jan 2023 18:10:51 -0500 Subject: [PATCH 2941/4563] Link SE-0386 to its review thread --- proposals/0386-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index efc7acb461..259c51c445 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -5,7 +5,7 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active Review (January 26th...Feburary 8th, 2023** * Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/62700), [apple/swift#62704](https://github.com/apple/swift/pull/62704), [apple/swift#62652](https://github.com/apple/swift/pull/62652), [apple/swift#62652](https://github.com/apple/swift/pull/62652) -* Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) +* Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) ([review](https://forums.swift.org/t/se-0386-package-access-modifier/62808)) ## Introduction From fabe519fd4076422aab40fc54e1fb840664fcfab Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 26 Jan 2023 16:27:34 -0800 Subject: [PATCH 2942/4563] Note which kinds of declarations are permitted vs. not permitted --- proposals/nnnn-freestanding-macros.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index 6439875b35..446b885f08 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -187,6 +187,15 @@ Code item macros can only introduce new declarations that have unique names, cre ## Detailed design +### Permitted declaration kinds + +A macro can expand to any declaration that is syntatically and semantically well-formed within the context where the macro is expanded, with a few notable exceptions: + +* `import` declarations can never be produced by a macro. Swift tooling depends on the ability to resolve import declarations based on a simple scan of the original source files. Allowing a macro expansion to introduce an import declaration would complicate import resolution considerably. +* `extension` declarations can never be produced by a macro. The effect of an extension declaration is wide-ranging, with the ability to add conformances, members, and so on. These capabilities are meant to be introduced in a more fine-grained manner. +* `operator` and `precedencegroup` declarations can never be produced by a macro, because they could allow one to reshape the precedence graph for existing code causing subtle differences in the semantics of code that sees the macro expansion vs. code that does not. +* `macro` declarations can never be produced by a macro, because allowing this would allow a macro to trivially produce infinitely recursive macro expansion. + ### Up-front declarations of newly-introduced names Whenever a macro produces declarations that are visible to other Swift code, it is required to declare the names in advance. This enables the Swift compiler and related tools to better reason about the set of names that can be introduced by a given use of a macro without having to expand the macro (or type-check its arguments), which can reduce the compile-time cost of macros and improve incremental builds. All of the names need to be specified within the attribute declaring the macro role, using the following forms: @@ -263,7 +272,7 @@ f(1) { x in Therefore, a macro used within a closure or function body can only introduce declarations using names produced by `createUniqueName`. This maintains the [two-phase of checking macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion) where type checking and inference is performed without expanding the macro, then the macro is expanded and its result type-checked independently, with no ability to influence type inference further. -### Macros in he Standard Library +### Macros in the Standard Library #### SE-0196 `warning` and `error` From 4536b78f566efe37eca1eeb6afebc1d0f3046b4c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 26 Jan 2023 16:44:54 -0800 Subject: [PATCH 2943/4563] Cleanups for the attached macros proposal, and drop lots of Future Directions --- proposals/nnnn-attached-macros.md | 170 ++---------------------------- 1 file changed, 7 insertions(+), 163 deletions(-) diff --git a/proposals/nnnn-attached-macros.md b/proposals/nnnn-attached-macros.md index ed3f5fd32d..77bebf96ad 100644 --- a/proposals/nnnn-attached-macros.md +++ b/proposals/nnnn-attached-macros.md @@ -11,7 +11,7 @@ Attached macros provide a way to extend Swift by creating and extending declarations based on arbitrary syntactic transformations on their arguments. They make it possible to extend Swift in ways that were only previously possible by introducing new language features, helping developers build more expressive libraries and eliminate extraneous boilerplate. -Attached macros are one part of the [vision for macros in Swift](https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900), which lays out general motivation for introducing macros into the language. They build on the ideas and motivation of [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) and [freestanding macros](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md) to cover a large new set of use cases; we will refer to those proposals often for the basic model of how macros integrate into the language. While freestanding macros are designed as standalone entities introduced by `#`, attached macros are associated with a specific declaration in the program that they can augment and extend. This supports many new use cases, greatly expanding the expressiveness of the macro system: +Attached macros are one part of the [vision for macros in Swift](https://github.com/apple/swift-evolution/pull/1927), which lays out general motivation for introducing macros into the language. They build on the ideas and motivation of [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) and [freestanding macros](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md) to cover a large new set of use cases; we will refer to those proposals often for the basic model of how macros integrate into the language. While freestanding macros are designed as standalone entities introduced by `#`, attached macros are associated with a specific declaration in the program that they can augment and extend. This supports many new use cases, greatly expanding the expressiveness of the macro system: * Creating trampoline or wrapper functions, such as automatically creating a completion-handler version of an `async` function. * Creating members of a type based on its definition, such as forming an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset) from an enum containing flags. @@ -20,7 +20,7 @@ Attached macros are one part of the [vision for macros in Swift](https://forums. ## Proposed solution -The proposal adds *attached macros*, so-called because they are attached to a particular declaration. They are written using the custom attribute syntax (e.g., `@addCompletionHandler`) that was introduced for property wrappers, and is also used for result builders . Attached macros can reason about the declaration to which they are attached, and provide additions and changes based on one or more different macro *roles*. Each role has a specific purpose, such as adding members, creating accessors, or adding peers alongside the declaration. A given attached macro can inhabit several different roles, and as such will be expanded multiple times corresponding to do the different roles, which allows the various roles to be composed. For example, an attached macro emulating property wrappers might inhabit both the "peer" and "accessor" roles, allowing it to introduce a backing storage property and also synthesize a getter/setter that go through that backing storage property. Composition of macro roles will be discussed in more depth once the basic macro roles have been established. +The proposal adds *attached macros*, so-called because they are attached to a particular declaration. They are written using the custom attribute syntax (e.g., `@addCompletionHandler`) that was introduced for property wrappers, and is also used for result builders and global actors. Attached macros can reason about the declaration to which they are attached, and provide additions and changes based on one or more different macro *roles*. Each role has a specific purpose, such as adding members, creating accessors, or adding peers alongside the declaration. A given attached macro can inhabit several different roles, and as such will be expanded multiple times corresponding to the different roles, which allows the various roles to be composed. For example, an attached macro emulating property wrappers might inhabit both the "peer" and "accessor" roles, allowing it to introduce a backing storage property and also synthesize a getter/setter that go through that backing storage property. Composition of macro roles will be discussed in more depth once the basic macro roles have been established. As with freestanding macros, attached declaration macros are declared with `macro`, and have [type-checked macro arguments](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#type-checked-macro-arguments-and-results) that allow their behavior to be customized. Attached macros are identified with the `@attached` attribute, which also provides the specific role as well as [any names they introduce](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md#up-front-declarations-of-newly-introduced-names). For example, the aforemented macro emulating property wrappers could be declared as follows: @@ -43,7 +43,7 @@ Each attached macro role will have its own protocol that inherits `AttachedMacro Peer macros produce new declarations alongside the declaration to which they are attached. For example, here is a declaration of a macro that introduces a completion-handler version of a given asynchronous function: ```swift -@attached(peer, names: overloaded) macro addCompletionHandler +@attached(peer, names: overloaded) macro addCompletionHandler() ``` This macro uses the `@attached` attribute to indicate that it is an attached peer attribute. The `names` argument specifies that how the names of the peer declarations are created, using an extended form of the scheme introduced with [freestanding macros](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md#up-front-declarations-of-newly-introduced-names). In this case, `overloaded` means that this macro will produce a peer that is overloaded with the declaration to which it is attached, i.e., it has the same base name. @@ -90,7 +90,7 @@ The actual implementation of this macro involves a bit of syntax manipulation, s public struct AddCompletionHandler: PeerDeclarationMacro { public static func expansion( of node: CustomAttributeSyntax, - peersOf declaration: DeclSyntax, + providingPeersOf declaration: DeclSyntax, in context: inout MacroExpansionContext ) throws -> [DeclSyntax] { // make sure we have an async function to start with @@ -227,8 +227,8 @@ protocol AccessorMacro: AttachedMacro { /// the attribute is attached. static func expansion( of node: AttributeSyntax, - accessorsOf declaration: DeclSyntax, - in context: inout MacroExpansionContext + providingAccessorsOf declaration: DeclSyntax, + in context: any MacroExpansionContext ) throws -> [AccessorDeclSyntax] } ``` @@ -480,163 +480,7 @@ It might be possible to provide a macro implementation API that is expressed in ### Additional attached macro roles -There are numerous ways in which this proposal could be extended to provide new macro roles. Each new macro role would introduce a new role kind to the `@attached` attribute, along with a corresponding protocol. Here are several possibilities, omitted from this proposal primarily to reduce scope. - -#### Body macros - -A body macro would allow one to create or replace the body of a function, initializer, or closure through syntactic manipulation. Body macros are attached to one of these entities, e.g., - -```swift -@traced(logLevel: 2) -func myFunction(a: Int, b: Int) { ... } -``` - -where the `traced` macro is declared as something like: - -```swift -@attached(body) macro traced(logLevel: Int = 0) -``` - -and implemented in a conformance to the `BodyMacro` protocol: - -```swift -protocol BodyMacro: AttachedMacro { - /// Expand a macro described by the given custom attribute to - /// produce or modify a body for the given entity to which - /// the attribute is attached. - static func expansion( - of node: CustomAttributeSyntax, - providingBodyOf entity: some WithCodeBlockSyntax, - in context: inout MacroExpansionContext - ) throws -> CodeBlockSyntax -} -``` - -The `WithCodeBlockSyntax` protocol describes all entities that have an optional "body". The `traced` macro could inject a log-level check and a call to log the values of each of the parameters, e.g., - -```swift -if shouldLog(atLevel: 2) { - log("Entering myFunction((a: \(a), b: \(b)))") -} -``` - -Body macros will only be applied when the body is required, e.g., to generate code. A body macro will be applied whether the entity has an existing body or not; if the entity does have an existing body, it will be type-checked before the macro is invoked, as with other macro arguments. - -#### Conformance macros - -Conformance macros could introduce protocol conformances to the type or extension to which they are attached. For example, this could be useful when composed with macro roles that create other members, such as a macro that both adds a protocol conformance and also a stored property required by that conformance. For example, one could extend the `dictionaryStorage` macro from earlier in the proposal to introduce the dictionary when placed on the struct itself: - -```swift -protocol HasDictionaryStorage { - var storage: [AnyHashable: Any] { get set } -} - -@dictionaryStorage -struct MyStruct /* macro introduces conformance to HasDictionaryStorage */ { - // macro introduces this property, which satisfies the HasDictionarySrorage.storage requirement - // var storage: [AnyHashable: Any] = [:] - - // macro introduces @dictionaryStorage attribute on members that don't already have one - // @dictionaryStorage - var name: String - - @dictionaryStorage(key: "birth_date") - var birthDate: Date? -} -``` - -#### Default witness macros - -Swift provides default "witness" synthesis for a number of protocols in the standard library, including `Equatable`, `Hashable`, `Encodable`, and `Decodable`. This behavior is triggered when a type has a conformance to a known protocol, and there is no suitable implementation for one of the protocol's requirements, e.g., - -```swift -struct Point: Equatable { - var x: Int - var y: Int - - // no suitable function - // - // - // - // to satisfy the protocol requirement, so the compiler creates one like the following - // - -} -``` - -There is no suitable function to meet the protocol requirement - -```swift -static func ==(lhs: Self, rhs: Self) -> Bool -``` - -so the compiler as bespoke logic to synthesize the following: - -```swift - static func ==(lhs: Point, rhs: Point) -> Bool { - lhs.x == rhs.x && lhs.y == rhs.y - } -``` - -Default witness macros would bring this capability to the macro system, by allowing a macro to define a witness to satisfy a requirement when there is no suitable witness. Consider an `equatableSyntax` macro declared as follows: - -```swift -@declaration(witness) -macro equatableSynthesis -``` - -This default-witness macro would be written on the protocol requirement itself, i.e., - -```swift -protocol Equatable { - @equatableSynthesis - static func ==(lhs: Self, rhs: Self) -> Bool - - static func !=(lhs: Self, rhs: Self) -> Bool -} -``` - -The macro type would implement the following protocol: - -```swift -protocol DefaultWitnessMacro: AttachedMacro { - /// Expand a macro described by the given custom attribute to - /// produce a witness definition for the requirement to which - /// the attribute is attached. - static func expansion( - of node: CustomAttributeSyntax, - witness: DeclSyntax, - conformingType: TypeSyntax, - storedProperties: [StoredProperty], - in context: inout MacroExpansionContext - ) throws -> DeclSyntax -} -``` - -The contract with the compiler here is interesting. The compiler would use the `@equatableSynthesis` macro to define the witness only when there is no other potential witness. The compiler will then produce a declaration for the witness based on the requirement, performing substitutions as necessary to (e.g.) replace `Self` with the conforming type, and provide that declaration via the `witness` parameter. For `Point`, the `==` declaration would look like this: - -```swift -static func ==(lhs: Point, rhs: Point) -> Bool -``` - -The `expansion` operation then augments the provided `witness` with a body, and could perform other adjustments if necessary, before returning it. The resulting definition will be inserted as a member into the conforming type (or extension), wherever the protocol conformance is declared. This approach gives a good balance between making macro writing easier, because the compiler is providing a witness declaration that will match the requirement, while still allowing the macro implementation freedom to alter that witness as needed. Its design also follows how witnesses are currently synthesized in the compiler today, which has fairly well-understood implementation properties (at least, to compiler implementers). - -The `conformingType` parameter provides the syntax of the conforming type, which can be used to refer to the type anywhere in the macro. The `storedProperties` parameter provides the set of stored properties of the conforming type, which are needed for many (most?) kinds of witness synthesis. The `StoredProperty` struct is defined as follows: - -```swift -struct StoredProperty { - /// The stored property syntax node. - var property: VariableDeclSyntax - - /// The original declaration from which the stored property was created, if the stored property was - /// synthesized. - var original: Syntax? -} -``` - -The `property` field is the syntax node for the stored property. Typically, this is the syntax node as written in the source code. However, some stored properties are formed in other ways, e.g., as the backing property of a property wrapper (`_foo`) or due to some other macro expansion (member, peer, freestanding, etc.). In these cases, `property` refers to the syntax of the generated property, and `original` refers to the syntax node that caused the stored property to be generated. This `original` value can be used, for example, to find information from the original declaration that can affect the synthesis of the default witness. - -Providing stored properties to this expansion method does require us to introduce a limitation on default-witness macro implementations, which is that they cannot themselves introduce stored properties. This eliminates a potential circularity in the language model, where the list of stored properties could grow due to expansion of a macro, thereby potentially invalidating the results of already-expanded macros that saw a subset of the stored properties. Note that directly preventing default-witness macros from defining stored properties isn't a complete solution, because one could (for example) have a default-witness macro produce a witness function that itself involves a peer-declaration macro that introduces a stored property. Such problems will be detected as a dependency cycle in the compiler and reported as an error. +There are numerous ways in which this proposal could be extended to provide new macro roles. Each new macro role would introduce a new role kind to the `@attached` attribute, along with a corresponding protocol. The macro vision document has a number of such suggestions. ## Appendix From a7a19e7c55f15ffedc7d7cb93441334815add39a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 26 Jan 2023 16:48:44 -0800 Subject: [PATCH 2944/4563] Add missing 'async' --- proposals/nnnn-attached-macros.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/nnnn-attached-macros.md b/proposals/nnnn-attached-macros.md index 77bebf96ad..9c18a86d35 100644 --- a/proposals/nnnn-attached-macros.md +++ b/proposals/nnnn-attached-macros.md @@ -69,7 +69,7 @@ public PeerMacro: AttachedMacro { of node: AttributeSyntax, providingPeersOf declaration: DeclSyntax, in context: any MacroExpansionContext - ) throws -> [DeclSyntax] + ) async throws -> [DeclSyntax] } ``` @@ -160,7 +160,7 @@ protocol MemberMacro: AttachedMacro { of node: AttributeSyntax, providingMembersOf declaration: DeclSyntax, in context: inout MacroExpansionContext - ) throws -> [DeclSyntax] + ) async throws -> [DeclSyntax] } ``` @@ -229,7 +229,7 @@ protocol AccessorMacro: AttachedMacro { of node: AttributeSyntax, providingAccessorsOf declaration: DeclSyntax, in context: any MacroExpansionContext - ) throws -> [AccessorDeclSyntax] + ) async throws -> [AccessorDeclSyntax] } ``` @@ -289,7 +289,7 @@ protocol MemberAttributeMacro: AttachedMacro { attachedTo declaration: DeclSyntax, providingAttributesFor member: DeclSyntax, in context: any MacroExpansionContext - ) throws -> [AttributeSyntax] + ) async throws -> [AttributeSyntax] } ``` From ed0635a643ad9a5bf84bca8cf49805da0933e8cf Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 26 Jan 2023 16:49:50 -0800 Subject: [PATCH 2945/4563] Add missing 'async' --- proposals/nnnn-freestanding-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index 446b885f08..d5f19c912e 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -86,7 +86,7 @@ public protocol DeclarationMacro: FreestandingMacro { /// within the given context to produce a set of declarations. static func expansion( of node: MacroExpansionDeclSyntax, in context: any MacroExpansionContext - ) throws -> [DeclSyntax] + ) async throws -> [DeclSyntax] } ``` @@ -170,7 +170,7 @@ public protocol CodeItemMacro: FreestandingMacro { /// expressions, statements, and declarations. static func expansion( of node: MacroExpansionDeclSyntax, in context: any MacroExpansionContext - ) throws -> [CodeBlockItemSyntax] + ) async throws -> [CodeBlockItemSyntax] } ``` From 0bef50c1448073dc9119f4179e1b80d4ea203f49 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 26 Jan 2023 14:09:23 -0800 Subject: [PATCH 2946/4563] [SE-0385] Change semantics of interaction between attributes and extensions State that attributes could only be attached to an unavailable unconstrained extension in the same module, as a way to opt-out from use of listed attributes. --- proposals/0385-custom-reflection-metadata.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/proposals/0385-custom-reflection-metadata.md b/proposals/0385-custom-reflection-metadata.md index 40939afd1e..eec06d3ab1 100644 --- a/proposals/0385-custom-reflection-metadata.md +++ b/proposals/0385-custom-reflection-metadata.md @@ -151,14 +151,26 @@ A given declaration can have multiple reflection metadata attributes as long as } ``` -Reflection metadata attributes must be applied at either the primary declaration of a type or in an extension of the type within the same module as the type’s primary declaration. Applying the attribute to a type in an extension outside its module is prohibited to prevent the same type from having multiple reflection metadata annotations of the same type. +Reflection metadata attributes must be applied at either the primary declaration of a type or in an unavailable unconstrained extension of the type within the same module as the type’s primary declaration. Unavailable extensions are supported to allow API implementers a way to opt-out from an attribute. Applying the attribute to a type in an available/constrained extension or in extension outside its module is prohibited to prevent the same type from having multiple reflection metadata annotations of the same type. ```swift -@Flag extension MyType [where ...] { 🔴 +@available(*, unavailable) +@Flag extension MyType { 🟢 if extension is in the same module +} +``` + +```swift +@Flag extension MyType { 🔴 ^ error: cannot associate reflection metadata @Flag with MyType in extension } ``` +```swift +@Flag extension MyType where ... { 🔴 + ^ error: cannot associate reflection metadata @Flag with MyType in constrained extension +} +``` + Declarations with custom reflection metadata attributes must be fully concrete: ```swift @@ -334,3 +346,4 @@ A previous Swift Evolution discussion suggested [adding a built-in `@test` attri * Added discussion of some alternatives that were considered involving extending Reflection capabilities and other existing language features. * Added discussion of an alternative that was considered about using Reflection types as the parameters to `init(attachedTo:)`. * Added discussion of an alternative that was considered about using static methods instead of `init(attachedTo:)` overloads. +* Clarified interaction between extensions and custom reflection metadata attributes. From c17d4bcf65073591ad3d2083dbca6cdbe99bbff6 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 27 Jan 2023 09:35:27 -0800 Subject: [PATCH 2947/4563] [SE-0385] Add a section about magic literal behavior in `init(attachedTo:)` Discuss behavior of #function, #file, #line etc. associated with `init(attachedTo:)` when attributes are used through the Reflection API. --- proposals/0385-custom-reflection-metadata.md | 56 ++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/proposals/0385-custom-reflection-metadata.md b/proposals/0385-custom-reflection-metadata.md index 40939afd1e..1bb66bf703 100644 --- a/proposals/0385-custom-reflection-metadata.md +++ b/proposals/0385-custom-reflection-metadata.md @@ -176,6 +176,7 @@ extension GenericType where T == Int { Generic declarations cannot be discovered through the Reflection query that gathers all instances of reflection metadata, because generic values cannot be represented in a higher-kinded way in Swift; generic values must always have substitutions at runtime. Generic declarations could be supported in the future by adding reflection queries for the other direction, e.g. a query to return the custom reflection metadata for a given key-path `\Generic.value`. + ### Inference of reflection metadata attributes A reflection metadata attribute can be applied to a protocol: @@ -242,6 +243,61 @@ extension AttributeInstances: Sequence {} This API will retrieve all of the instances of your reflection attribute across all modules. Instances of metadata types are initialized in the Reflection query to gather the metadata. Attributes who are not available in the current running OS, i.e. because the `attachedTo` declaration is not available as described in the following section, will be excluded from the results. +### Magic literals in custom reflection metadata attributes + +When custom reflection metadata type is accessed through the Reflection APIs, magic literals - `#function`, `#file`, `#line`, and `#column` associated with `init(attachedTo:)` would behave in a special way. Even though in such cases `init(attachedTo:)` is called from a special generator function `#function` literal is still going to point to the declaration attribute is attached to, and `#file`, `#line`, and `#column` are going to point to the attribute itself at the point of use or to the declaration if the attribute has been inferred. + +**test.swift** + +```swift + 1: @reflectionMetadata + 2: struct Flag { + 3: init(attachedTo: T.Type, + 4: func: String = #function, + 5: file: String = #file, + 6: line: Int = #line, + 7: column: Int = #column) {} + 8: + 9: init(attachedTo: KeyPath, +10: func: String = #function, +11: file: String = #file, +12: line: Int = #line, +13: column: Int = #column) {} +14: } +15: +16: struct Test { +17: @Flag var value: Int = 42 +18: } +19: +20: @Flag +21: protocol Flagged {} +22: +23: struct InferredTest : Flagged {} +``` + +**other.swift** + +```swift +1: let flags = Attribute.allInstances(of: Flag.self) +``` + +`Flag.init(attachedTo:)` associated with `Test.value` in this case is going to receive the following information: + +* `#function` = `"value"` +* `#file` = `"test.swift"` +* `#line` = `17` +* `#column` = `4` + +`Flag.init(attachedTo:)` for implicitly inferred attribute on `InferredTest` in this case is going to receive the following information: + +* `#function` = `"InferredTest"` +* `#file` = `"test.swift"` +* `#line` = `23` +* `#column` = `1` + +We think that this behavior provides the most benefit to the users because it preserves all of the information about attribute locations. + + ### API Availability Custom metadata attributes can be attached to declarations with limited availability. The Reflection query for an individual instance of the metadata attribute type will be gated on a matching availability condition and will return `nil` for instances which are unavailable at runtime. For example: From 80ddc02831537418e23b7cefe8c7dcc5c318e20f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 27 Jan 2023 17:10:23 -0800 Subject: [PATCH 2948/4563] Replace "any" with "some" --- proposals/nnnn-freestanding-macros.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index d5f19c912e..803fd0aae5 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -85,7 +85,8 @@ public protocol DeclarationMacro: FreestandingMacro { /// Expand a macro described by the given freestanding macro expansion declaration /// within the given context to produce a set of declarations. static func expansion( - of node: MacroExpansionDeclSyntax, in context: any MacroExpansionContext + of node: MacroExpansionDeclSyntax, + in context: some MacroExpansionContext ) async throws -> [DeclSyntax] } ``` @@ -169,7 +170,8 @@ public protocol CodeItemMacro: FreestandingMacro { /// within the given context to produce a set of code items, which can be any mix of /// expressions, statements, and declarations. static func expansion( - of node: MacroExpansionDeclSyntax, in context: any MacroExpansionContext + of node: MacroExpansionDeclSyntax, + in context: some MacroExpansionContext ) async throws -> [CodeBlockItemSyntax] } ``` From d8978a6d10090b564dad69ad5b1eb9d25fe5369e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 27 Jan 2023 23:42:19 -0800 Subject: [PATCH 2949/4563] any -> some MacroExpansionContext --- proposals/nnnn-attached-macros.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/proposals/nnnn-attached-macros.md b/proposals/nnnn-attached-macros.md index 9c18a86d35..8c4f3674b7 100644 --- a/proposals/nnnn-attached-macros.md +++ b/proposals/nnnn-attached-macros.md @@ -68,7 +68,7 @@ public PeerMacro: AttachedMacro { static func expansion( of node: AttributeSyntax, providingPeersOf declaration: DeclSyntax, - in context: any MacroExpansionContext + in context: some MacroExpansionContext ) async throws -> [DeclSyntax] } ``` @@ -91,7 +91,7 @@ public struct AddCompletionHandler: PeerDeclarationMacro { public static func expansion( of node: CustomAttributeSyntax, providingPeersOf declaration: DeclSyntax, - in context: inout MacroExpansionContext + in context: some MacroExpansionContext ) throws -> [DeclSyntax] { // make sure we have an async function to start with // form a new function "completionHandlerFunc" by starting with that async function and @@ -158,8 +158,8 @@ protocol MemberMacro: AttachedMacro { /// the attribute is attached. static func expansion( of node: AttributeSyntax, - providingMembersOf declaration: DeclSyntax, - in context: inout MacroExpansionContext + providingMembersOf declaration: some DeclGroupSyntax, + in context: some MacroExpansionContext ) async throws -> [DeclSyntax] } ``` @@ -227,8 +227,8 @@ protocol AccessorMacro: AttachedMacro { /// the attribute is attached. static func expansion( of node: AttributeSyntax, - providingAccessorsOf declaration: DeclSyntax, - in context: any MacroExpansionContext + providingAccessorsOf declaration: some DeclSyntaxProtocol, + in context: some MacroExpansionContext ) async throws -> [AccessorDeclSyntax] } ``` @@ -286,9 +286,9 @@ protocol MemberAttributeMacro: AttachedMacro { /// produce additional attributes for the members of the type. static func expansion( of node: AttributeSyntax, - attachedTo declaration: DeclSyntax, + attachedTo declaration: some DeclGroupSyntax, providingAttributesFor member: DeclSyntax, - in context: any MacroExpansionContext + in context: some MacroExpansionContext ) async throws -> [AttributeSyntax] } ``` @@ -371,7 +371,7 @@ extension ClampingMacro: PeerDeclarationMacro { static func expansion( of node: CustomAttributeSyntax, providingPeersOf declaration: DeclSyntax, - in context: any MacroExpansionContext + in context: some MacroExpansionContext ) throws -> [DeclSyntax] { // create a new variable declaration that is the same as the original, but... // - prepend an underscore to the name @@ -405,7 +405,7 @@ extension ClampingMacro: AccessorMacro { static func expansion( of node: CustomAttributeSyntax, providingAccessorsOf declaration: DeclSyntax, - in context: inout MacroExpansionContext + in context: some MacroExpansionContext ) throws -> [AccessorDeclSyntax] { let originalName = /* get from declaration */, minValue = /* get from custom attribute node */, @@ -491,7 +491,7 @@ public struct AddCompletionHandler: PeerDeclarationMacro { public static func expansion( of node: CustomAttributeSyntax, providingPeersOf declaration: DeclSyntax, - in context: inout MacroExpansionContext + in context: some MacroExpansionContext ) throws -> [DeclSyntax] { // Only on functions at the moment. We could handle initializers as well // with a little bit of work. From 19272d243eb2d06415a26d570730644344d121be Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 13 Dec 2022 15:01:27 +0000 Subject: [PATCH 2950/4563] Fix proposal title, JSON syntax highlighting --- proposals/NNNN-cross-compilation-destinations.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/NNNN-cross-compilation-destinations.md b/proposals/NNNN-cross-compilation-destinations.md index 8d1123b87c..0ac8f9572f 100644 --- a/proposals/NNNN-cross-compilation-destinations.md +++ b/proposals/NNNN-cross-compilation-destinations.md @@ -1,4 +1,4 @@ -# Package Manager Feature name +# Cross-Compilation Destination Bundles * Proposal: [SE-NNNN](NNNN-cross-compilation-destinations.md) * Authors: [Max Desiatov](https://github.com/MaxDesiatov) @@ -137,7 +137,7 @@ Authors of this document intend to publish source code for a macOS → Linux CC As an example, destination publishers looking to add a library to an Ubuntu 22.04 destination environment would modify a `Dockerfile` similar to this one in CC destination generator source code: -```docker +```dockerfile FROM swift:5.7-jammy apt-get install -y \ @@ -194,7 +194,7 @@ Different formats of destination bundles can be considered, but we don't think t Platform triples are not specific enough in certain cases. For example, `aarch64-unknown-linux` host triple can’t prevent a user from installing a CC destination bundle on an unsupported Linux distribution. In the future we could deprecate `hostTriple` and `destinationTriple` JSON properties in favor of dictionaries with keys and values that describe aspects of platforms that are important for destinations. Such dictionaries could look like this: -```json +```json5 "destination": { "kernel": "Linux", "libcFlavor": "Glibc", From 37b6445aac39e40d46c576396f59b30a87cee5c7 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 24 Jan 2023 13:06:08 +0000 Subject: [PATCH 2951/4563] Clearly define "build platform" and scope of the proposal --- .../NNNN-cross-compilation-destinations.md | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/proposals/NNNN-cross-compilation-destinations.md b/proposals/NNNN-cross-compilation-destinations.md index 0ac8f9572f..097c9bd450 100644 --- a/proposals/NNNN-cross-compilation-destinations.md +++ b/proposals/NNNN-cross-compilation-destinations.md @@ -9,12 +9,17 @@ ## Introduction -Cross-compilation is a common development use case. When cross-compiling, we need to refer to these two main concepts: +Cross-compilation is a common development use case. When cross-compiling, we need to refer to these concepts: -* **host platform**, where developer's code is built; -* **target platform**, where developer's code is running. +* **toolchain**, which is a set of tools used to build an application or a library; +* **build platform**, where the toolchain is built; +* **host platform**, where application or library code is built; +* **target platform**, where application or library code is running natively. +* **SDK**, which is a set of dynamic and/or static libraries, headers, and other resources required to generate code for the target platform. -Another important term is **toolchain**, which is a set of executable binaries running on the host platform. Additionally, we define **SDK** as a set of dynamic and/or static libraries, headers, and other resources required to produce a binary for a target platform. Let’s call a toolchain and an SDK bundled together a **destination**. +Let’s call a toolchain and an SDK bundled together a **destination**. + +The proposal is designed for use cases where host and target platforms are different from each other. Cases where build platform is different from the host platform are out of scope for this proposal. ## Motivation @@ -86,18 +91,18 @@ Here each artifact directory is dedicated to a specific CC destination, while bi Note the presence of `destination.json` files in each `` subdirectory. These files should contain a JSON dictionary with an evolved version of the schema of [existing destination.json files that SwiftPM already supports](https://github.com/apple/swift-package-manager/pull/1098) (hence `"version": 2` ) -``` +```json { "version": 2, - "sdkRootDir": , - "toolchainBinDir": , - "runtimeDir": , - "hostTriples": [], - "targetTriples": [], - "extraSwiftCFlags": [], - "extraCCFlags": [], - "extraCXXFlags": [], - "extraLinkerFlags": [] + "sdkRootDir": "", + "toolchainBinDir": "", + "runtimeDir": "", + "hostTriples": [""], + "targetTriples": [""], + "extraSwiftCFlags": [""], + "extraCCFlags": [""], + "extraCXXFlags": [""], + "extraLinkerFlags": [""] } ``` From a10467dfbe6aa9bb489ad3cbf64ece0239bc3f67 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 26 Jan 2023 22:09:42 +0000 Subject: [PATCH 2952/4563] Expand proposal's authors list --- proposals/NNNN-cross-compilation-destinations.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-cross-compilation-destinations.md b/proposals/NNNN-cross-compilation-destinations.md index 097c9bd450..c56607be01 100644 --- a/proposals/NNNN-cross-compilation-destinations.md +++ b/proposals/NNNN-cross-compilation-destinations.md @@ -1,7 +1,8 @@ # Cross-Compilation Destination Bundles * Proposal: [SE-NNNN](NNNN-cross-compilation-destinations.md) -* Authors: [Max Desiatov](https://github.com/MaxDesiatov) +* Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd/), +[Evan Wilde](https://github.com/etcwilde) * Review Manager: TBD * Status: **Awaiting implementation** * Implementation: [apple/swift-package-manager#5911](https://github.com/apple/swift-package-manager/pull/5911), From be6b9a01c0609b33d67ebad4ec7370b609132c21 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 27 Jan 2023 18:34:28 +0000 Subject: [PATCH 2953/4563] Clean up formatting, update `destination.json` fields Clarify nomenclature and toolset files format Update proposal status --- .../NNNN-cross-compilation-destinations.md | 315 ++++++++++++++---- 1 file changed, 251 insertions(+), 64 deletions(-) diff --git a/proposals/NNNN-cross-compilation-destinations.md b/proposals/NNNN-cross-compilation-destinations.md index c56607be01..ec388e5fce 100644 --- a/proposals/NNNN-cross-compilation-destinations.md +++ b/proposals/NNNN-cross-compilation-destinations.md @@ -1,52 +1,77 @@ # Cross-Compilation Destination Bundles -* Proposal: [SE-NNNN](NNNN-cross-compilation-destinations.md) -* Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd/), -[Evan Wilde](https://github.com/etcwilde) -* Review Manager: TBD -* Status: **Awaiting implementation** -* Implementation: [apple/swift-package-manager#5911](https://github.com/apple/swift-package-manager/pull/5911), -[apple/swift-package-manager#5922](https://github.com/apple/swift-package-manager/pull/5922) +- Proposal: [SE-NNNN](NNNN-cross-compilation-destinations.md) +- Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd/), [Evan + Wilde](https://github.com/etcwilde) +- Review Manager: [Mishal Shah](https://github.com/shahmishal) +- Status: **Waiting for review** +- Implementation: [apple/swift-package-manager#5911](https://github.com/apple/swift-package-manager/pull/5911), + [apple/swift-package-manager#5922](https://github.com/apple/swift-package-manager/pull/5922), [apple/swift-package-manager#6023](https://github.com/apple/swift-package-manager/pull/6023) ## Introduction Cross-compilation is a common development use case. When cross-compiling, we need to refer to these concepts: -* **toolchain**, which is a set of tools used to build an application or a library; -* **build platform**, where the toolchain is built; -* **host platform**, where application or library code is built; -* **target platform**, where application or library code is running natively. -* **SDK**, which is a set of dynamic and/or static libraries, headers, and other resources required to generate code for the target platform. +- **toolchain** is a set of tools used to build an application or a library; +- **triple** describes features of a given machine such as CPU architecture, vendor, OS etc, corresponding to LLVM's + triple; +- **build-time triple** describes a machine where application or library code is built; +- **run-time triple** describes a machine where application or library code is running; +- **SDK** is a set of dynamic and/or static libraries, headers, and other resources required to generate code for the + run-time triple. Let’s call a toolchain and an SDK bundled together a **destination**. -The proposal is designed for use cases where host and target platforms are different from each other. Cases where build platform is different from the host platform are out of scope for this proposal. +Authors of the proposal are aware of the established "build/host/target platform" naming convention, but feel that +"target" already has a different meaning within the build systems nomenclature. In addition, "platform" +itself is quite loosely defined. For the avoidance of possible confusion, we're using "build-time triple" and "run-time +triple" terms in this proposal. ## Motivation -Swift cross-compilation (CC) destinations are currently produced on an ad-hoc basis for different combinations of host and target platforms. For example, scripts that produce macOS → Linux CC destinations were created by both[ the Swift team ](https://github.com/apple/swift-package-manager/blob/swift-5.7-RELEASE/Utilities/build_ubuntu_cross_compilation_toolchain)and [the Swift community](https://github.com/SPMDestinations/homebrew-tap). At the same time, the distribution process of CC destinations is cumbersome. After building a destination tree on the file system, required metadata files rely on hardcoded absolute paths. Adding support for relative paths in destination's metadata and providing a unified way to distribute and install destinations as archives would clearly be an improvement to the multi-platform Swift ecosystem. +Swift cross-compilation (CC) destinations are currently produced on an ad-hoc basis for different combinations of build-time +and run-time triples. For example, scripts that produce macOS → Linux CC destinations were created by both [the Swift +team](https://github.com/apple/swift-package-manager/blob/swift-5.7-RELEASE/Utilities/build_ubuntu_cross_compilation_toolchain) +and [the Swift community](https://github.com/SPMDestinations/homebrew-tap). At the same time, the distribution process +of CC destinations is cumbersome. After building a destination tree on the file system, required metadata files rely on +hardcoded absolute paths. Adding support for relative paths in destination's metadata and providing a unified way to +distribute and install destinations as archives would clearly be an improvement to the multi-platform Swift ecosystem. -The primary audience of this pitch are people who cross-compile from macOS to Linux. When deploying to single-board computers supporting Linux (e.g. Raspberry Pi), building on the target hardware may be too slow or run out of available memory. Quite naturally, users would prefer to cross-compile on their host machine when targeting these platforms. +The primary audience of this pitch are people who cross-compile from macOS to Linux. When deploying to single-board +computers supporting Linux (e.g. Raspberry Pi), building on such hardware may be too slow or run out of available +memory. Quite naturally, users would prefer to cross-compile on a different machine when developing for these platforms. -In other cases, building in a Docker container is not always the best solution for certain development workflows. For example, when working with Swift AWS Lambda Runtime, some developers may find that installing Docker just for building a project is a daunting step that shouldn’t be required. +In other cases, building in a Docker container is not always the best solution for certain development workflows. For +example, when working with Swift AWS Lambda Runtime, some developers may find that installing Docker just for building a +project is a daunting step that shouldn’t be required. -The solution described below is general enough to scale for any host/target platform combination. +The solution described below is general enough to scale for any build-time/run-time triple combination. ## Proposed solution -Since CC destination is a collection of binaries arranged in a certain directory hierarchy, it makes sense to distribute it as an archive. We'd like to build on top of [SE-0305](https://github.com/apple/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md) and extend the `.artifactbundle` format to support this. +Since CC destination is a collection of binaries arranged in a certain directory hierarchy, it makes sense to distribute +it as an archive. We'd like to build on top of +[SE-0305](https://github.com/apple/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md) and +extend the `.artifactbundle` format to support this. -Additionally, we propose introducing a new `swift destination` CLI command for installation and removal of CC destinations on the local filesystem. +Additionally, we propose introducing a new `swift destination` CLI command for installation and removal of CC +destinations on the local filesystem. -We introduce a notion of a top-level toolchain, which is the toolchain that handles user’s `swift destination` invocations. Parts of this top-level toolchain (linker, C/C++ compilers, and even the Swift compiler) can be overridden with tools supplied in `.artifactbundle` s installed by `swift destination` invocations. +We introduce a notion of a top-level toolchain, which is the toolchain that handles user’s `swift destination` +invocations. Parts of this top-level toolchain (linker, C/C++ compilers, and even the Swift compiler) can be overridden +with tools supplied in `.artifactbundle` s installed by `swift destination` invocations. -When the user runs `swift build` with the selected CC destination, the overriding tools from the corresponding bundle are invoked by `swift build` instead of tools from the top-level toolchain. +When the user runs `swift build` with the selected CC destination, the overriding tools from the corresponding bundle +are invoked by `swift build` instead of tools from the top-level toolchain. ## Detailed design ### CC Destination Artifact Bundles -As a quick reminder for a concept introduced in [SE-0305](https://github.com/apple/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md), an **artifact bundle** is a directory that has the filename suffix `.artifactbundle` and has a predefined structure with `.json` manifest files provided as metadata. +As a quick reminder for a concept introduced in +[SE-0305](https://github.com/apple/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md), an +**artifact bundle** is a directory that has the filename suffix `.artifactbundle` and has a predefined structure with +`.json` manifest files provided as metadata. The proposed structure of artifact bundles containing CC destinations looks like: @@ -68,7 +93,8 @@ The proposed structure of artifact bundles containing CC destinations looks like ┆ └┄ ``` -For example, a destination bundle allowing to cross-compile Swift 5.7 source code to recent versions of Ubuntu from macOS would look like this: +For example, a destination bundle allowing to cross-compile Swift 5.7 source code to recent versions of Ubuntu from +macOS would look like this: ``` swift-5.7_ubuntu.artifactbundle @@ -76,48 +102,149 @@ swift-5.7_ubuntu.artifactbundle ├ ubuntu_jammy │ ├ arm64-apple-darwin │ │ ├ destination.json +│ │ ├ toolset.json │ │ └ │ └ x86_64-apple-darwin │ ├ destination.json +│ ├ toolset.json │ └ ├ ubuntu_focal │ └ x86_64-apple-darwin │ ├ destination.json +│ ├ toolset.json │ └ ├ ubuntu_bionic ┆ └┄ ``` -Here each artifact directory is dedicated to a specific CC destination, while binaries for a specific host platform are placed in `arm64-apple-darwin` and `x86_64-apple-darwin` subdirectories. +Here each artifact directory is dedicated to a specific CC destination, while binaries for a specific host platform are +placed in `arm64-apple-darwin` and `x86_64-apple-darwin` subdirectories. -Note the presence of `destination.json` files in each `` subdirectory. These files should contain a JSON dictionary with an evolved version of the schema of [existing destination.json files that SwiftPM already supports](https://github.com/apple/swift-package-manager/pull/1098) (hence `"version": 2` ) +`info.json` bundle manifests at the root of artifact bundles should specify `"type": "crossCompilationDestination"` +for corresponding artifacts. Artifact identifiers in this manifest uniquely identify a CC destination. The rest of the +properties of bundle manifests introduced in SE-0305 are preserved. + +### `destination.json` files + +Note the presence of `destination.json` files in each `` subdirectory. These files should contain a JSON +dictionary with an evolved version of the schema of [existing `destination.json` files that SwiftPM already +supports](https://github.com/apple/swift-package-manager/pull/1098) and `destination.json` files presented in the pitch +version of this proposal, hence `"schemaVersion": "3.0"`: + +```json +{ + "schemaVersion": "3.0", + "buildTimeTriples": [""], + "runTimeTriples": [""], + "swiftResourcesPaths": [""], + "includeSearchPaths": [""], + "librarySearchPaths": [""], +} +``` + +We propose that all relative paths in `destination.json` files should be validated not to "escape" the destination +bundle for security reasons. That is, `../` components, if present in paths, will not be allowed to reference files and +directories outside of a corresponding destination bundle. Symlinks will also be validated to prevent them from escaping +out of the bundle. + +Since not all platforms can support self-contained destination bundles, users will be able to provide their own +additional paths on the filesystem outside of bundles after a destination is installed. The exact options for specifying +paths are proposed in a subsequent section for a newly introduced `swift destination configure` command. + +### `toolset.json` files + +We find that properties dedicated to tools configuration are useful outside of the cross-compilation context. Due to +that, a separate `toolset.json` file is introduced: + +```json5 +{ + "schemaVersion": "1.0", + "toolsetRootPath": "optional path to a root directory containing toolchain executables", + // If `toolsetRootPath` is specified, all of the relative paths below will be resolved relative to `toolsetRootPath`. + "swiftCompiler": { + "path": "", + "extraFlags": [""] + }, + "cCompiler": { + "path": "", + "extraFlags": [""] + }, + "cxxCompiler": { + "path": "", + "extraFlags": [""] + }, + "linker": { + "path": "", + "extraFlags": [""] + } +} +``` + +Users familiar with CMake can draw an analogy between toolset files and CMake toolchain files. Toolset files are +designed to supplant previous ad-hoc ways to specify paths and flags in SwiftPM, such as `SWIFT_EXEC` and `CC` +environment variables, which were applied in use cases unrelated to cross-compilation. We propose that +users should be able to pass `--toolset ` option to both `swift build` and `swift test`. + +We'd like to allow using multiple toolset files at once. With this users can "assemble" toolchains on the fly out of +tools that in certain scenarios may even come from different vendors. + +All of the properties related to names of the tools are optional, which also allows stacking multiple toolset files. +For example, consider `toolset1.json`: ```json { - "version": 2, - "sdkRootDir": "", - "toolchainBinDir": "", - "runtimeDir": "", - "hostTriples": [""], - "targetTriples": [""], - "extraSwiftCFlags": [""], - "extraCCFlags": [""], - "extraCXXFlags": [""], - "extraLinkerFlags": [""] + "schemaVersion": "1.0", + "swiftCompiler": { + "path": "/usr/bin/swiftc", + "extraFlags": ["-Xfrontend", "-enable-cxx-interop"] + }, + "cCompiler": { + "path": "/usr/bin/clang", + "extraFlags": ["-pedantic"] + } } ``` -We propose that all relative paths in `destination.json` files should be validated not to "escape" the destination bundle for security reasons. That is, `../` components, if present in paths, will not be allowed to reference files and directories outside of a corresponding destination bundle. Symlinks will also be validated to prevent them from escaping out of the bundle. +and `toolset2.json`: + +```json +{ + "schemaVersion": "1.0", + "swiftCompiler": { + "path": "/custom/swiftc", + "extraFlags": [] + } +} +``` + + +With multiple `--toolset` options, passing both of those files will merge them into a single configuration. Tools passed +in subsequent `--toolset` options will shadow tools from previous options with the same names. That is, +`swift build --toolset toolset1.json --toolset toolset2.json` will build with `/custom/swiftc` and no extra flags, as +specified in `toolset2.json`, but `/usr/bin/clang -pedantic` from `toolset1.json` will still be used. -Lastly, `info.json` bundle manifests at the root of artifact bundles should specify `"type": "crossCompilationDestination"` for corresponding artifacts. Artifact identifiers in this manifest uniquely identify a CC destination. The rest of the properties of bundle manifests introduced in SE-0305 are preserved. +When cross-compiling, paths in `toolset.json` files supplied in destination artifact bundles should be self-contained +with same rules applied as in `destination.json`: no absolute paths and no escaping symlinks are allowed. Users are +still able to provide their own `toolset.json` files outside of artifact bundles to specify additional developer tools +for which no relative path can be provided within the bundle. -### Destination Bundle Installation +### Destination Bundle Installation and Configuration To manage CC destinations, we'd like to introduce a new `swift destination` command with three subcommands: -* `swift destination install `, which downloads a given bundle if needed and installs it in a location discoverable by SwiftPM. For destinations installed from remote URLs an additional `--checksum` option is required, through which users of destinations can specify a checksum provided by publishers of destinations. The latter can produce a checksum by running `swift package compute-checksum` command (introduced in SE-0272) with the destination artifact bundle archive as an argument. -* `swift destination list`, which prints a list of already installed CC destinations with their identifiers. -* `swift destination delete ` will delete a given destination from the filesystem. +- `swift destination install `, which downloads a given bundle if needed and + installs it in a location discoverable by SwiftPM. For destinations installed from remote URLs an additional + `--checksum` option is required, through which users of destinations can specify a checksum provided by publishers of + destinations. The latter can produce a checksum by running `swift package compute-checksum` command (introduced in + SE-0272) with the destination artifact bundle archive as an argument. +- `swift destination list`, which prints a list of already installed CC destinations with their identifiers. +- `swift destination configure `, which allows users to provide additional search paths and toolsets to be +used subsequently when building with a given destination. Specifically, multiple `--swift-resources-path`, +`--include-search-path`, `--library-search-path`, and `--toolset` options with corresponding paths can be provided, +which then will be stored as configuration for this destination. +`swift destination configure --show-configuration` will print currently set paths, while +`swift destination configure --reset` will reset all of those at once. +- `swift destination delete ` will delete a given destination from the filesystem. ### Using a CC Destination @@ -127,21 +254,29 @@ After a destination is installed, users can refer to it via its identifier passe swift build --destination ubuntu-jammy ``` -We'd also like to make `--destination` flexible enough to recognize destination triples when there's only a single CC destination installed for such triple: +We'd also like to make `--destination` flexible enough to recognize destination triples when there's only a single CC +destination installed for such triple: ``` swift build --destination x86_64-unknown-linux-gnu ``` -When multiple destinations support the same triple, an error message will be printed listing these destinations and asking the user to select a single one via its identifier. +When multiple destinations support the same triple, an error message will be printed listing these destinations and +asking the user to select a single one via its identifier. ### CC Destination Bundle Generation -CC destinations can be generated quite differently, depending on host and target platform combinations and user's needs. We intentionally don't specify how destination artifact bundles should be generated. +CC destinations can be generated quite differently, depending on build-time and run-time triple combinations and user's +needs. We intentionally don't specify how destination artifact bundles should be generated. -Authors of this document intend to publish source code for a macOS → Linux CC destination generator, which community is welcome to fork and reuse for their specific needs. This generator will use Docker for setting up the build environment locally before copying it to the destination tree. Relying on Docker in this generator makes it easier to reuse and customize existing build environments. Important to clarify, that Docker is only used for bundle generation, and users of CC destinations do not need to have Docker installed on their machine to utilize it. +Authors of this document intend to publish source code for a macOS → Linux CC destination generator, which community is +welcome to fork and reuse for their specific needs. This generator will use Docker for setting up the build environment +locally before copying it to the destination tree. Relying on Docker in this generator makes it easier to reuse and +customize existing build environments. Important to clarify, that Docker is only used for bundle generation, and users +of CC destinations do not need to have Docker installed on their machine to utilize it. -As an example, destination publishers looking to add a library to an Ubuntu 22.04 destination environment would modify a `Dockerfile` similar to this one in CC destination generator source code: +As an example, destination publishers looking to add a library to an Ubuntu 22.04 destination environment would modify a +`Dockerfile` similar to this one in CC destination generator source code: ```dockerfile FROM swift:5.7-jammy @@ -152,11 +287,14 @@ apt-get install -y \ # Add more libraries as arguments to `apt-get install`. ``` -Then to generate a new CC destinations, a generator executable delegates to Docker for downloading and installing required tools and libraries, including the newly added ones. After a Docker image with destination environment is ready, the generator copies files from the image to a corresponding `.artifactbundle` destination tree. +Then to generate a new CC destinations, a generator executable delegates to Docker for downloading and installing +required tools and libraries, including the newly added ones. After a Docker image with destination environment is +ready, the generator copies files from the image to a corresponding `.artifactbundle` destination tree. ## Security -The proposed `--checksum` flag provides basic means of verifying destination bundle's validity. As a future direction, we'd like to consider sandboxing and codesigning toolchains running on macOS. +The proposed `--checksum` flag provides basic means of verifying destination bundle's validity. As a future direction, +we'd like to consider sandboxing and codesigning toolchains running on macOS. ## Impact on existing packages @@ -166,39 +304,70 @@ This is an additive change with no impact on existing packages. ### Rust -In the Rust ecosystem, its toolchain and standard library built for a target platform are managed by [the `rustup` tool](https://github.com/rust-lang/rustup). For example, artifacts required for cross-compilation to `aarch64-linux-unknown-gnu` are installed with [`rustup target add aarch64-linux-unknown-gnu`](https://rust-lang.github.io/rustup/cross-compilation.html). Then building for this target with Rust’s package manager looks like `cargo build --target=aarch64-linux-unknown-gnu` . +In the Rust ecosystem, its toolchain and standard library built for a run-time triple are managed by [the `rustup` +tool](https://github.com/rust-lang/rustup). For example, artifacts required for cross-compilation to +`aarch64-linux-unknown-gnu` are installed with +[`rustup target add aarch64-linux-unknown-gnu`](https://rust-lang.github.io/rustup/cross-compilation.html). Then +building for this target with Rust’s package manager looks like `cargo build --target=aarch64-linux-unknown-gnu` . -Mainstream Rust tools don’t provide an easy way to create your own destinations/targets. You’re only limited to the list of targets provided by Rust maintainers. This likely isn’t a big problem per se for Rust users, as Rust doesn’t provide C/C++ interop on the same level as Swift. It means that Rust packages much more rarely than Swift expect certain system-provided packages to be available in the same way that SwiftPM allows with `systemLibrary` . +Mainstream Rust tools don’t provide an easy way to create your own destinations/targets. You’re only limited to the list +of targets provided by Rust maintainers. This likely isn’t a big problem per se for Rust users, as Rust doesn’t provide +C/C++ interop on the same level as Swift. It means that Rust packages much more rarely than Swift expect certain +system-provided packages to be available in the same way that SwiftPM allows with `systemLibrary`. -Currently, Rust doesn’t supply all of the required tools when running `rustup target add`. It’s left to a user to specify paths to a linker that’s suitable for their host/target combination manually in a config file. We feel that this should be unnecessary, which is why destination bundles proposed for Swift can provide their own tools via `toolchainBinDir` property in `destination.json` . +Currently, Rust doesn’t supply all of the required tools when running `rustup target add`. It’s left to a user to +specify paths to a linker that’s suitable for their build-time/run-time triple combination manually in a config file. We +feel that this should be unnecessary, which is why destination bundles proposed for Swift can provide their own tools +via toolset configuration files. ### Go -Go’s standard library is famously self-contained and has no dependencies on C or C++ standard libraries. Because of this there’s no need to install additional targets and destinations. Cross-compiling in Go works out of the box by passing `GOARCH` and `GOOS` environment variables with chosen values, an example of this is `GOARCH=arm64 GOOS=linux go build` invocation. +Go’s standard library is famously self-contained and has no dependencies on C or C++ standard libraries. Because of this +there’s no need to install artifacts. Cross-compiling in Go works out of the box by passing `GOARCH` and `GOOS` +environment variables with chosen values, an example of this is `GOARCH=arm64 GOOS=linux go build` invocation. -This would be a great experience for Swift, but it isn’t easily achievable as long as Swift standard library depends on C and C++ standard libraries. Any code interoperating with C and/or C++ would have to link with those libraries as well. When compared to Go, our proposed solution allows both dynamic and, at least on Linux when Musl is supported, full static linking. We’d like Swift to allow as much customization as needed for users to prepare their own destination bundles. +This would be a great experience for Swift, but it isn’t easily achievable as long as Swift standard library depends on +C and C++ standard libraries. Any code interoperating with C and/or C++ would have to link with those libraries as well. +When compared to Go, our proposed solution allows both dynamic and, at least on Linux when Musl is supported, full +static linking. We’d like Swift to allow as much customization as needed for users to prepare their own destination +bundles. ## Alternatives Considered ### Extensions Other Than `.artifactbundle` -Some members of the community suggested that destination bundles should use a more specific extension. Since we're relying on the existing `.artifactbundle` format and extension, which is already used for binary targets, we think a specialized extension only for destinations would introduce an inconsistency. On the other hand, we think that specific extensions could make sense with a change applied at once. For example, we could consider `.binarytarget` and `.ccdestination` extensions for respective artifact types. But that would require a migration strategy for existing `.artifactbundle`s containing binary targets. +Some members of the community suggested that destination bundles should use a more specific extension. Since we're +relying on the existing `.artifactbundle` format and extension, which is already used for binary targets, we think a +specialized extension only for destinations would introduce an inconsistency. On the other hand, we think that specific +extensions could make sense with a change applied at once. For example, we could consider `.binarytarget` and +`.ccdestination` extensions for respective artifact types. But that would require a migration strategy for existing +`.artifactbundle`s containing binary targets. ### Building Applications in Docker Containers -Instead of coming up with a specialized bundle format for destinations, users of Swift on macOS targeting Linux could continue to use Docker. But, as discussed in the [Motivation](#motivation) section, building applications in Docker doesn’t cover all of the possible use cases and complicates onboarding for new users. It also only supports Linux as a target platform, while we’re looking for a solution that can be generalized for all possible platforms. +Instead of coming up with a specialized bundle format for destinations, users of Swift on macOS building for Linux could +continue to use Docker. But, as discussed in the [Motivation](#motivation) section, building applications in Docker +doesn’t cover all of the possible use cases and complicates onboarding for new users. It also only supports Linux, while +we’re looking for a solution that can be generalized for all possible platforms. ### Alternative Bundle Formats -One alternative is to allow only a single host → target platform combination per bundle, but this may complicate distribution of destinations bundles in some scenarios. The existing `.artifactbundle` format is flexible enough to support bundles with a single or multiple combinations. +One alternative is to allow only a single build-time/run-time combination per bundle, but this may complicate +distribution of destinations bundles in some scenarios. The existing `.artifactbundle` format is flexible enough to +support bundles with a single or multiple combinations. -Different formats of destination bundles can be considered, but we don't think those would be significantly different from the proposed one. If they were different, this would complicate bundle distribution scenarios for users who want to publish their own artifact bundles with executables, as defined in SE-0305. +Different formats of destination bundles can be considered, but we don't think those would be significantly different +from the proposed one. If they were different, this would complicate bundle distribution scenarios for users who want to +publish their own artifact bundles with executables, as defined in SE-0305. ## Future Directions ### Identifying Platforms with Dictionaries of Properties -Platform triples are not specific enough in certain cases. For example, `aarch64-unknown-linux` host triple can’t prevent a user from installing a CC destination bundle on an unsupported Linux distribution. In the future we could deprecate `hostTriple` and `destinationTriple` JSON properties in favor of dictionaries with keys and values that describe aspects of platforms that are important for destinations. Such dictionaries could look like this: +Platform triples are not specific enough in certain cases. For example, `aarch64-unknown-linux` host triple can’t +prevent a user from installing a CC destination bundle on an unsupported Linux distribution. In the future we could +deprecate `hostTriple` and `destinationTriple` JSON properties in favor of dictionaries with keys and values that +describe aspects of platforms that are important for destinations. Such dictionaries could look like this: ```json5 "destination": { @@ -210,22 +379,40 @@ Platform triples are not specific enough in certain cases. For example, `aarch64 } ``` -A toolchain providing this information could allow users to refer to these properties in their code for conditional compilation and potentially even runtime checks. +A toolchain providing this information could allow users to refer to these properties in their code for conditional +compilation and potentially even runtime checks. ### SwiftPM Plugins for Remote Running, Testing, Deployment, and Debugging -After an application is built with a CC destination, there are other development workflow steps to be improved. We could introduce new types of plugins invoked by `swift run` and `swift test` for purposes of remote running, debugging, and testing. For Linux as a target platform, these plugins could delegate to Docker for running produced executables. +After an application is built with a CC destination, there are other development workflow steps to be improved. We could +introduce new types of plugins invoked by `swift run` and `swift test` for purposes of remote running, debugging, and +testing. For Linux run-time triples, these plugins could delegate to Docker for running produced executables. ### `swift destination select` subcommand -While `swift destination select` subcommand or a similar one make sense for selecting a CC destination instead of passing `--destination` to `swift build` every time, users will expect `swift run` and `swift test` to also work for the target platform previously passed to `swift destination select`. That’s out of scope for this proposal on its own and depends on making plugins (from the previous subsection) or some other remote running and testing implementation to fully work. +While `swift destination select` subcommand or a similar one make sense for selecting a CC destination instead of +passing `--destination` to `swift build` every time, users will expect `swift run` and `swift test` to also work for any +destination previously passed to `swift destination select`. That’s out of scope for this proposal on its own and +depends on making plugins (from the previous subsection) or some other remote running and testing implementation to +fully work. ### SwiftPM and SourceKit-LSP improvements -It is a known issue that SwiftPM can’t run multiple concurrent builds for different target platforms. This may cause issues when SourceKit-LSP is building a project for indexing purposes (for a host platform by default), while a user may be trying to build for a target platform for example for testing. One of these build processes will fail due to the process locking the build database. A potential solution would be to maintain separate build databases per platform. +It is a known issue that SwiftPM can’t run multiple concurrent builds for different run-time triples. This may cause +issues when SourceKit-LSP is building a project for indexing purposes (for a host platform by default), while a user may +be trying to build for a run-time for example for testing. One of these build processes will fail due to the +process locking the build database. A potential solution would be to maintain separate build databases per platform. -Another issue related to SourceKit-LSP is that [it always build and indexes source code for the host platform](https://github.com/apple/sourcekit-lsp/issues/601). Ideally, we want it to maintain indices for multiple platforms at the same time. Users should be able to select to target platforms and corresponding indices to enable semantic syntax highlighting, auto-complete, and other features for areas of code that are conditionally compiled with `#if` directives. +Another issue related to SourceKit-LSP is that [it always build and indexes source code for the host +platform](https://github.com/apple/sourcekit-lsp/issues/601). Ideally, we want it to maintain indices for multiple +platforms at the same time. Users should be able to select run-time triples and corresponding indices to enable +semantic syntax highlighting, auto-complete, and other features for areas of code that are conditionally compiled with +`#if` directives. ### Source-Based CC Destinations -One interesting solution is distributing source code of a minimal base destination, as explored by [Zig programming language](https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html). In this scenario, a cross-compilation destination binaries are produced on the fly when needed. We don't consider this option to be mutually exclusive with solutions proposed in this document, and so it could be explored in the future for Swift as well. However, this requires reducing the number of dependencies that Swift runtime and core libraries have. \ No newline at end of file +One interesting solution is distributing source code of a minimal base destination, as explored by [Zig programming +language](https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html). In this scenario, a +cross-compilation destination binaries are produced on the fly when needed. We don't consider this option to be mutually +exclusive with solutions proposed in this document, and so it could be explored in the future for Swift as well. +However, this requires reducing the number of dependencies that Swift runtime and core libraries have. From 6c4ee708b576076f5121a9cf5516b118bae50a54 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 30 Jan 2023 00:27:16 +0000 Subject: [PATCH 2954/4563] Clarify use of Universal Binaries and multiarch layouts --- proposals/NNNN-cross-compilation-destinations.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/NNNN-cross-compilation-destinations.md b/proposals/NNNN-cross-compilation-destinations.md index ec388e5fce..48b410c6fe 100644 --- a/proposals/NNNN-cross-compilation-destinations.md +++ b/proposals/NNNN-cross-compilation-destinations.md @@ -142,6 +142,9 @@ version of this proposal, hence `"schemaVersion": "3.0"`: } ``` +Thanks to the availability of Universal Binaries on macOS and multiarch layouts on Linux, `buildTimeTriples` and +`runTimeTriples` properties use plural in their naming and use arrays as their values. + We propose that all relative paths in `destination.json` files should be validated not to "escape" the destination bundle for security reasons. That is, `../` components, if present in paths, will not be allowed to reference files and directories outside of a corresponding destination bundle. Symlinks will also be validated to prevent them from escaping From 9068570ae3061bf538e416a9b0a237ee265fac41 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 30 Jan 2023 00:28:07 +0000 Subject: [PATCH 2955/4563] Refine wording --- proposals/NNNN-cross-compilation-destinations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-cross-compilation-destinations.md b/proposals/NNNN-cross-compilation-destinations.md index 48b410c6fe..5f33154789 100644 --- a/proposals/NNNN-cross-compilation-destinations.md +++ b/proposals/NNNN-cross-compilation-destinations.md @@ -163,7 +163,7 @@ that, a separate `toolset.json` file is introduced: { "schemaVersion": "1.0", "toolsetRootPath": "optional path to a root directory containing toolchain executables", - // If `toolsetRootPath` is specified, all of the relative paths below will be resolved relative to `toolsetRootPath`. + // If `toolsetRootPath` is specified, all relative paths below will be resolved relative to `toolsetRootPath`. "swiftCompiler": { "path": "", "extraFlags": [""] From b60deb5ba2f3223af8b44d244f84b793df7b7ec4 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 30 Jan 2023 01:32:59 +0000 Subject: [PATCH 2956/4563] SE-0311: fix typo (#1935) `it should to so explicitly` -> ` it should do so explicitly` --- proposals/0311-task-locals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0311-task-locals.md b/proposals/0311-task-locals.md index d8aad76f83..79eee8dc89 100644 --- a/proposals/0311-task-locals.md +++ b/proposals/0311-task-locals.md @@ -424,7 +424,7 @@ detach(priority: Task.currentPriority) { // manually propagate priority } ``` -While this is quite labor intensive and boilerplate heavy, it is intentional that detached tasks never carry any of their legacy around with them. So if a detached task really has to carry some information, it should to so explicitly. +While this is quite labor intensive and boilerplate heavy, it is intentional that detached tasks never carry any of their legacy around with them. So if a detached task really has to carry some information, it should do so explicitly. At the same time, the new `async` (naming pending, perhaps `send` (?!)) operation _does_ inherit all of the following properties of the creating task: execution context, task priority, and task-local values. From e177c85453b7a2cb336b95fc23e6f1ee6edba3d6 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 30 Jan 2023 16:09:24 +0000 Subject: [PATCH 2957/4563] Clarify toolset search paths behavior --- proposals/NNNN-cross-compilation-destinations.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/proposals/NNNN-cross-compilation-destinations.md b/proposals/NNNN-cross-compilation-destinations.md index 5f33154789..8e3fa9cc5d 100644 --- a/proposals/NNNN-cross-compilation-destinations.md +++ b/proposals/NNNN-cross-compilation-destinations.md @@ -143,7 +143,7 @@ version of this proposal, hence `"schemaVersion": "3.0"`: ``` Thanks to the availability of Universal Binaries on macOS and multiarch layouts on Linux, `buildTimeTriples` and -`runTimeTriples` properties use plural in their naming and use arrays as their values. +`runTimeTriples` properties use plural in their naming and their values are arrays. We propose that all relative paths in `destination.json` files should be validated not to "escape" the destination bundle for security reasons. That is, `../` components, if present in paths, will not be allowed to reference files and @@ -226,6 +226,11 @@ in subsequent `--toolset` options will shadow tools from previous options with t `swift build --toolset toolset1.json --toolset toolset2.json` will build with `/custom/swiftc` and no extra flags, as specified in `toolset2.json`, but `/usr/bin/clang -pedantic` from `toolset1.json` will still be used. +Tools not specified in any of the supplied toolset files will be looked up in existing implied search paths that are +used without any presence of toolset files, even when `toolsetRootPath` is present in any toolset file. We'd like +toolsets to be explicit enough in this regard: if any tool would like to participate in toolsets path lookups, it must +have its path provided, either relative or absolute, in any of the provided toolset files. + When cross-compiling, paths in `toolset.json` files supplied in destination artifact bundles should be self-contained with same rules applied as in `destination.json`: no absolute paths and no escaping symlinks are allowed. Users are still able to provide their own `toolset.json` files outside of artifact bundles to specify additional developer tools @@ -257,7 +262,7 @@ After a destination is installed, users can refer to it via its identifier passe swift build --destination ubuntu-jammy ``` -We'd also like to make `--destination` flexible enough to recognize destination triples when there's only a single CC +We'd also like to make `--destination` flexible enough to recognize run-time triples when there's only a single CC destination installed for such triple: ``` @@ -265,7 +270,7 @@ swift build --destination x86_64-unknown-linux-gnu ``` When multiple destinations support the same triple, an error message will be printed listing these destinations and -asking the user to select a single one via its identifier. +asking the user to select a single one via its identifier instead. ### CC Destination Bundle Generation From 0720261039a56bfee3ac22adce5502d5bbcd6220 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 31 Jan 2023 10:24:27 +0000 Subject: [PATCH 2958/4563] Update 0385-custom-reflection-metadata.md (#1937) Fix broken link on dashboard. --- proposals/0385-custom-reflection-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0385-custom-reflection-metadata.md b/proposals/0385-custom-reflection-metadata.md index 40939afd1e..98db082177 100644 --- a/proposals/0385-custom-reflection-metadata.md +++ b/proposals/0385-custom-reflection-metadata.md @@ -1,6 +1,6 @@ # Custom Reflection Metadata -* Proposal: [SE-0385](385-custom-reflection-metadata.md) +* Proposal: [SE-0385](0385-custom-reflection-metadata.md) * Authors: [Pavel Yaskevich](https://github.com/xedin), [Holly Borla](https://github.com/hborla), [Alejandro Alonso](https://github.com/Azoy), [Stuart Montgomery](https://github.com/stmontgomery) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active review (January 24, 2023...February 7, 2023)** From 70956b714a5a02ce795c80000620e0db12c73206 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 31 Jan 2023 09:37:19 -0800 Subject: [PATCH 2959/4563] Use `FreestandingMacroExpansionSyntax` consistently for freestanding macros --- proposals/nnnn-freestanding-macros.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index 803fd0aae5..0ce4ff3068 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -85,7 +85,7 @@ public protocol DeclarationMacro: FreestandingMacro { /// Expand a macro described by the given freestanding macro expansion declaration /// within the given context to produce a set of declarations. static func expansion( - of node: MacroExpansionDeclSyntax, + of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext ) async throws -> [DeclSyntax] } @@ -103,7 +103,7 @@ The implementation of a `warning` declaration macro extracts the string literal ```swift public struct WarningMacro: DeclarationMacro { public static func expansion( - of node: MacroExpansionDeclSyntax, in context: inout MacroExpansionContext + of node: some FreestandingMacroExpansionSyntax, in context: inout MacroExpansionContext ) throws -> [DeclSyntax] { guard let messageExpr = node.argumentList.first?.expression?.as(SpecializeExprSyntax.self), messageExpr.segments.count == 1, @@ -170,7 +170,7 @@ public protocol CodeItemMacro: FreestandingMacro { /// within the given context to produce a set of code items, which can be any mix of /// expressions, statements, and declarations. static func expansion( - of node: MacroExpansionDeclSyntax, + of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext ) async throws -> [CodeBlockItemSyntax] } From 1433395993e69fa2b25bbdea6cb646623ed9ccc3 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Tue, 31 Jan 2023 13:33:33 -0800 Subject: [PATCH 2960/4563] Rename NNNN-cross-compilation-destinations.md to 0387-cross-compilation-destinations.md --- ...ion-destinations.md => 0387-cross-compilation-destinations.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{NNNN-cross-compilation-destinations.md => 0387-cross-compilation-destinations.md} (100%) diff --git a/proposals/NNNN-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md similarity index 100% rename from proposals/NNNN-cross-compilation-destinations.md rename to proposals/0387-cross-compilation-destinations.md From c22b0d88ecb446e21533d4d5b7d04f63f2971519 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Tue, 31 Jan 2023 13:43:09 -0800 Subject: [PATCH 2961/4563] Update the status, pitch and review information for SE-0387 --- proposals/0387-cross-compilation-destinations.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0387-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md index 8e3fa9cc5d..88be2da0f9 100644 --- a/proposals/0387-cross-compilation-destinations.md +++ b/proposals/0387-cross-compilation-destinations.md @@ -1,12 +1,13 @@ # Cross-Compilation Destination Bundles -- Proposal: [SE-NNNN](NNNN-cross-compilation-destinations.md) +- Proposal: [SE-0387](0387-cross-compilation-destinations.md) - Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd/), [Evan Wilde](https://github.com/etcwilde) - Review Manager: [Mishal Shah](https://github.com/shahmishal) -- Status: **Waiting for review** +- Status: **Active Review (January 31st...Feburary 14th, 2023**) - Implementation: [apple/swift-package-manager#5911](https://github.com/apple/swift-package-manager/pull/5911), [apple/swift-package-manager#5922](https://github.com/apple/swift-package-manager/pull/5922), [apple/swift-package-manager#6023](https://github.com/apple/swift-package-manager/pull/6023) +- Review: ([pitch](https://forums.swift.org/t/pitch-cross-compilation-destination-bundles/61777)) ([review](https://forums.swift.org/t/se-0387-cross-compilation-destination-bundles/62875)) ## Introduction From f2c22cd32bcebd4735f41a4089f98aaa0b7ad2f6 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Mon, 30 Jan 2023 10:21:57 -0800 Subject: [PATCH 2962/4563] Update SE-0376 based on the second round of review feedback: - Remove the requirement that `@available` be present simultaneously with `@backDeployed`. - Elaborate on alternatives considered for the `before:` label. - Various minor updates to the proposal text to improve word choice and clarify sentences. --- proposals/0376-function-back-deployment.md | 31 +++++++++++++++------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index 114246a2bd..15dad4ba31 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -2,7 +2,7 @@ * Proposal: [SE-0376](0376-function-back-deployment.md) * Author: [Allan Shortlidge](https://github.com/tshortli) -* Implementation: [apple/swift#41271](https://github.com/apple/swift/pull/41271), [apple/swift#41348](https://github.com/apple/swift/pull/41348), [apple/swift#41416](https://github.com/apple/swift/pull/41416), [apple/swift#41612](https://github.com/apple/swift/pull/41612) as the underscored attribute `@_backDeploy` +* Implementation: Available as `@_backDeploy(before:)` in Swift 5.8 * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) * Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0376-function-back-deployment/61507)) ([second review](https://forums.swift.org/t/se-0376-second-review-function-back-deployment/61671)) ([returned for revision (second review)](https://forums.swift.org/t/returned-for-revision-se-0376-second-review-function-back-deployment/62374)) * Status: **Returned for revision** @@ -62,12 +62,12 @@ While `@_alwaysEmitIntoClient` can be used to back deploy APIs, there are some d ## Proposed solution -Add a `@backDeployed(upTo: ...)` attribute to Swift that can be used to indicate that a copy of the function should be emitted into the client to be used at runtime when executing on an OS prior to a specific version. The attribute can be adopted by ToastKit's authors like this: +Add a `@backDeployed(before: ...)` attribute to Swift that can be used to indicate that a copy of the function should be emitted into the client to be used at runtime when executing on an OS prior to the version identified with the `before:` argument. The attribute can be adopted by ToastKit's authors like this: ```swift extension Toaster { @available(toasterOS 1.0, *) - @backDeployed(upTo: toasterOS 2.0) + @backDeployed(before: toasterOS 2.0) public func makeBatchOfToast(_ breadSlices: [BreadSlice]) -> [Toast] { ... } } ``` @@ -76,12 +76,12 @@ The API is now available on toasterOS 1.0 and later so clients may now reference ## Detailed design -The `@backDeployed` attribute may apply to functions, methods, and subscripts. Properties may also have the attribute as long as the they do not have storage. The attribute takes a comma separated list of one or more platform versions, so declarations that are available on more than one platform can be back deployed to multiple platforms with a single attribute. The following are examples of legal uses of the attribute: +The `@backDeployed` attribute may apply to functions, methods, and subscripts. Properties may also have the attribute as long as the they do not have storage. The attribute takes a comma separated list of one or more platform versions, so declarations that are available on more than one platform can be back deployed on multiple platforms with a single attribute. The following are examples of legal uses of the attribute: ```swift extension Temperature { @available(toasterOS 1.0, ovenOS 1.0, *) - @backDeployed(upTo: toasterOS 2.0, ovenOS 2.0) + @backDeployed(before: toasterOS 2.0, ovenOS 2.0) public var degreesFahrenheit: Double { return (degreesCelcius * 9 / 5) + 32 } @@ -90,7 +90,7 @@ extension Temperature { extension Toaster { /// Returns whether the slot at the given index can fit a bagel. @available(toasterOS 1.0, *) - @backDeployed(upTo: toasterOS 2.0) + @backDeployed(before: toasterOS 2.0) public subscript(fitsBagelsAt index: Int) -> Bool { get { return index < 2 } } @@ -128,7 +128,7 @@ extension Toaster { Developers familiar with JavaScript may recognize these generated compatibility functions as [polyfills](https://remysharp.com/2010/10/08/what-is-a-polyfill). -When the deployment target of the client app is at least toasterOS 2.0, the optimizer can eliminate the branch in `makeBatchOfToast_thunk(_:)` and therefore make `makeBatchOfToast_fallback(_:)` an unused function, which reduces the unnecessary bloat that could otherwise result from referencing a back deployed API. +When the deployment target of the client app is at least toasterOS 2.0, the compiler can eliminate the branch in `makeBatchOfToast_thunk(_:)` and therefore make `makeBatchOfToast_fallback(_:)` an unused function, which reduces the unnecessary bloat that could otherwise result from referencing a back deployed API. ### Restrictions on declarations that may be back deployed @@ -136,7 +136,6 @@ There are rules that limit which declarations may have a `@backDeployed` attribu * The declaration must be `public` or `@usableFromInline` since it only makes sense to offer back deployment for declarations that would be used by other modules. * Only functions that can be invoked with static dispatch are eligible to back deploy, so back deployed instance and class methods must be `final`. The `@objc` attribute also implies dynamic dispatch and therefore is incompatible with `@backDeployed`. -* Explicit availability must be specified with `@available` on the same declaration for each of the platforms that the declaration is back deployed on. * The declaration should be available earlier than the platform versions specified in `@backDeployed` (otherwise the fallback functions would never be called). * The `@_alwaysEmitIntoClient` and `@_transparent` attributes are incompatible with `@backDeployed` because they require the function body to always be emitted into the client, defeating the purpose of `@backDeployed`. * Declarations with `@inlinable` _may_ use `@backDeployed`. As usual with `@inlinable`, the bodies of these functions may be emitted into the client at the discretion of the optimizer. The copy of the function in the client may therefore be used even when a copy of the function is available in the library. @@ -155,13 +154,25 @@ The `@backDeployed` attribute has no effect on the ABI of Swift libraries. A Swi ## Effect on API resilience -By itself, adding a `@backDeployed` attribute to a declaration does not affect source compatibility for clients of a library, and neither does removing the attribute. However, adding a `@backDeployed` attribute would typically be done simultaneously with expanding the availability of the declaration. Expansion of the availability of an API is source compatible for clients, but reversing that expansion would not be. +By itself, adding a `@backDeployed` attribute to a declaration does not affect source compatibility for clients of a library, and neither does removing the attribute. However, adding a `@backDeployed` attribute would typically be done simultaneously with expanding the availability of the declaration by lowering the `introduced:` version in the `@available` attribute. Expansion of the availability of an API is source compatible for clients, but reversing that expansion would not be. ## Alternatives considered +### Use a different argument label name + +A few alternative spellings of the argument label `before:` were considered including `upTo:`, `until:`, and `implemented:`. The choice of label is significant because it influences the reader's intuitive understanding of the semantics of the attribute. The label should ideally make the directionality of the effect clear as well as the exclusivity of the OS version range. It also helps if the attribute as a whole reads fluently when expanded into an English sentence like this: + +> The function is back deployed for all minimum deployment targets _before_ iOS 13. + +Reviewers did not consistently agree that any of the labels that were considered successfully clarified the directionality of the effect or the exclusivity of the range but the label `before:` was ultimately deemed the clearest option. + +### Use a different attribute name + +One way to frame the proposed attribute is that it indicates which OS versions the function became ABI stable in. From that perspective, naming the attribute something like `@abi(introduced:)` could make sense. However, by default every public function in an SDK library is already implicitly ABI stable at the `introduced:` version of its availability so it would be reasonable to ask what distinction this attribute is making and why it is not present on every API that is ABI stable. This naming choice would obfuscate the essential effect of the attribute, requiring unfamiliar readers to read the documentation to learn that the purpose of the attribute is to extend the function's availability to earlier deployment targets. + ### Extend @available -Another possible design for this feature would be to augment the existing `@available` attribute. In the following example, a `backDeployBefore:` label is added to the `@available` attribute: +Another possible design for this feature would be to augment the existing `@available` attribute instead of introducing a new attribute. In the following example, a `backDeployBefore:` label is added to the `@available` attribute: ```swift extension Toaster { From 806ec32a644fb003417331013ed0ae3e09ad7cf8 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 1 Feb 2023 18:07:54 -0500 Subject: [PATCH 2963/4563] Update the "Swift" evolution proposal template. I've done three main things here: - updated the header fields for the process we're following now - added a lot of explanatory text and other guidance - recast the API Resilience section to have a somewhat broader purpose of talking about the general impact on adopters This template is meant for language and library proposals, so it's specific to the Language Workgroup, and I haven't shied from that in the text. --- proposal-templates/0000-swift-template.md | 263 ++++++++++++++-------- 1 file changed, 164 insertions(+), 99 deletions(-) diff --git a/proposal-templates/0000-swift-template.md b/proposal-templates/0000-swift-template.md index 1a2e8ed2a4..e677165ba3 100644 --- a/proposal-templates/0000-swift-template.md +++ b/proposal-templates/0000-swift-template.md @@ -3,15 +3,39 @@ * Proposal: [SE-NNNN](NNNN-filename.md) * Authors: [Author 1](https://github.com/swiftdev), [Author 2](https://github.com/swiftdev) * Review Manager: TBD -* Status: **Awaiting implementation** +* Status: **Awaiting implementation** or **Awaiting review** +* Vision: *if applicable* [Vision Name](https://github.com/apple/swift-evolution/visions/NNNNN.md) +* Roadmap: *if applicable* [Roadmap Name](https://forums.swift.org/...)) +* Bug: *if applicable* [apple/swift#NNNNN](https://github.com/apple/swift/issues/NNNNN) +* Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) or [apple/swift-evolution-staging#NNNNN](https://github.com/apple/swift-evolution-staging/pull/NNNNN) +* Feature Identifier: *if applicable* `MyFeatureName` +* Previous Proposal: *if applicable* [SE-XXXX](XXXX-filename.md) +* Previous Revision: *if applicable* [1](https://github.com/apple/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md) +* Review: ([pitch](https://forums.swift.org/...)) -*During the review process, add the following fields as needed:* +When filling out this template, you should delete or replace all of the text except for the section headers and the header fields above. For example, you should delete everything from this paragraph down to the Introduction section below. -* Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) or [apple/swift-evolution-staging#NNNNN](https://github.com/apple/swift-evolution-staging/pull/NNNNN) -* Decision Notes: [Rationale](https://forums.swift.org/), [Additional Commentary](https://forums.swift.org/) -* Bugs: [SR-NNNN](https://bugs.swift.org/browse/SR-NNNN), [SR-MMMM](https://bugs.swift.org/browse/SR-MMMM) -* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md) -* Previous Proposal: [SE-XXXX](XXXX-filename.md) +As a proposal author, you should fill out all of the header fields except `Review Manager`. The review manager will set that field and change several others as part of initiating the review. Delete any header fields that are not applicable to your proposal. + +When sharing a link to the proposal while it is still a PR, be sure to share a live link to the proposal, not an exact commit, so that readers will always see the latest version when you make changes. On GitHub, you can find this link by browsing the PR branch: from the PR page, click the "username wants to merge ... from username:my-branch-name" link and find the proposal file in that branch. + +`Status` should reflect the current implementation status while the proposal is still a PR. The proposal cannot be reviewed until an implementation is available, but early readers should see the correct status. + +`Vision` should link to the [vision document](https://forums.swift.org/t/the-role-of-vision-documents-in-swift-evolution/62101) for this proposal, if it is part of a vision. Most proposals are not part of a vision. If a vision has been written but not yet accepted, link to the discussion thread for the vision. + +`Roadmap` should link to the discussion thread for the roadmap for this proposal, if applicable. When a complex feature is broken down into several closely-related proposals to make evolution review easier and more focused, it's helpful to make a forum post concisely explaining what's going on and detailing how the proposals will be reviewed. That post is called a "roadmap", and this field should link to it. Most proposals don't need roadmaps. + +`Bug` should be used when this proposal is fixing a bug with significant discussion in the bug report. It is not necessary to link bugs that do not contain significant discussion or that merely duplicate discussion linked somewhere else. Do not link bugs from private bug trackers. + +`Implementation` should link to the PR(s) implementing the feature. If the proposal has not been implemented yet, or if it simply codifies existing behavior, just say that. If the implementation has already been committed to the main branch (as an experimental feature), say that and specify the experimental feature flag. If the implementation is spread across multiple PRs, just link to the most important ones. + +`Feature Identifier` should be the feature name used to identify this feature under [SE-0362](https://github.com/apple/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md#proposals-define-their-own-feature-identifier). Not all proposals need a feature identifier. You should think about whether one would be useful for your proposal as part of filling this field out. + +`Previous Proposal` should be used when there is a specific line of succession between the linked proposal and this one. For example, this proposal might have been removed from the previous proposal so that it can be reviewed separately, or this proposal might supersede the previous proposal in some way that was felt to exceed the scope of a "revision". + +`Previous Revision` should be added after a major substantive revision of a proposal that has undergone review. It links to the previously reviewed revision. It is not necessary to add or update this field after minor editorial changes. + +`Review` is a history of all discussion threads about this proposal, in chronological order. Use these standardized link names: `pitch` `review` `revision` `acceptance` `rejection`. If there are multiple such threads, spell the ordinal out: `first pitch` `second review` etc. ## Introduction @@ -36,6 +60,10 @@ Describe your solution to the problem. Provide examples and describe how they work. Show how your solution is better than current workarounds: is it cleaner, safer, or more efficient? +This section doesn't have to be comprehensive. Focus on the most +important parts of the proposal and make arguments about why the +proposal is better than the status quo. + ## Detailed design Describe the design of the solution in detail. If it involves new @@ -47,103 +75,137 @@ reasonably implement the feature. ## Source compatibility -Relative to the Swift 3 evolution process, the source compatibility -requirements for Swift 4 are *much* more stringent: we should only -break source compatibility if the Swift 3 constructs were actively -harmful in some way, the volume of affected Swift 3 code is relatively -small, and we can provide source compatibility (in Swift 3 -compatibility mode) and migration. - -Will existing correct Swift 3 or Swift 4 applications stop compiling -due to this change? Will applications still compile but produce -different behavior than they used to? If "yes" to either of these, is -it possible for the Swift 4 compiler to accept the old syntax in its -Swift 3 compatibility mode? Is it possible to automatically migrate -from the old syntax to the new syntax? Can Swift applications be -written in a common subset that works both with Swift 3 and Swift 4 to -aid in migration? - -## Effect on ABI stability - -The ABI comprises all aspects of how code is generated for the +Describe the impact of this proposal on source compatibility. As a +general rule, all else being equal, Swift code that worked in previous +releases of the tools should work in new releases. That means both that +it should continue to build and that it should continue to behave +dynamically the same as it did before. Changes that cannot satisfy +this must be opt-in, generally by requiring a new language mode. + +This is not an absolute guarantee, and the Language Workgroup will +consider intentional compatibility breaks if their negative impact +can be shown to be small and the current behavior is causing +substantial problems in practice. + +For proposals that affect parsing, consider whether existing valid +code might parse differently under the proposal. Does the proposal +reserve new keywords that can no longer be used as identifiers? + +For proposals that affect type checking, consider whether existing valid +code might type-check differently under the proposal. Does it add new +conversions that might make more overload candidates viable? Does it +change how names are looked up in existing code? Does it make +type-checking more expensive in ways that might run into implementation +limits more often? + +For proposals that affect the standard library, consider the impact on +existing clients. If clients provide a similar API, will type-checking +find the right one? If the feature overloads an existing API, is it +problematic that existing users of that API might start resolving to +the new API? + +## ABI compatibility + +Describe the impact on ABI compatibility. As a general rule, the ABI +of existing code must not change between tools releases or language +modes. This rule does not apply as often as source compatibility, but +it is much stricter, and the Language Workgroup generally cannot allow +exceptions. + +The ABI encompasses all aspects of how code is generated for the language, how that code interacts with other code that has been compiled separately, and how that code interacts with the Swift -runtime library. It includes the basic rules of the language ABI, -such as calling conventions, the layout of data types, and the -behavior of dynamic features in the language like reflection, -dynamic dispatch, and dynamic casting. It also includes applications -of those basic rules to ABI-exposed declarations, such as the `public` -functions and types of ABI-stable libraries like the Swift standard -library. - -Many language proposals have no direct impact on the ABI. For -example, a proposal to add the `typealias` declaration to Swift -would have no effect on the ABI because type aliases are not -represented dynamically and uses of them in code can be -straightforwardly translated into uses of the aliased type. -Proposals like this can simply state in this section that they -have no impact on the ABI. However, if *using* the feature in code -that must maintain a stable ABI can have a surprising ABI impact, -for example by changing a function signature to be different from -how it would be without using the feature, that should be discussed -in this section. - -Because Swift has a stable ABI on some platforms, proposals are -generally not acceptable if they would require changes to the ABI -of existing language features or declarations. Proposals must be -designed to avoid the need for this. - -For example, Swift could not accept a proposal for a feature which, -in order to work, would require parameters of certain (existing) -types to always be passed as owned values, because parameters are -not always passed as owned values in the ABI. This feature could -be fixed by only enabling it for parameters marked a special new way. -Adding that marking to an existing function parameter would change -the ABI of that specific function, which programmers can make good, -context-aware decisions about: adding the marking to an existing -function with a stable ABI would not be acceptable, but adding it -to a new function or to a function with no stable ABI restrictions -would be fine. - -Proposals that change the ABI may be acceptable if they can be thought -of as merely *adding* to the ABI, such as by adding new kinds of -declarations, adding new modifiers or attributes, or adding new types -or methods to the Swift standard library. The key principle is -that the ABI must not change for code that does not use the new -feature. On platforms with stable ABIs, uses of such features will -by default require a new release of the platform in order to work, -and so their use in code that may deploy to older releases will have -to be availability-guarded. If this limitation applies to any part -of this proposal, that should be discussed in this section. - -Adding a function to the standard library does not always require -an addition to the ABI if it can be implemented using existing -functions. Library maintainers may be able to help you with this -during the code review of your implementation. Adding a type or -protocol currently always requires an addition to the ABI. - -If a feature does require additions to the ABI, platforms with -stable ABIs may sometimes be able to back-deploy those additions -to existing releases of the platform. This is not always possible, -and in any case, it is outside the scope of the evolution process. -Proposals should usually discuss ABI stability concerns as if -it was not possible to back-deploy the necessary ABI additions. - -## Effect on API resilience - -API resilience describes the changes one can make to a public API -without breaking its ABI. Does this proposal introduce features that -would become part of a public API? If so, what kinds of changes can be -made without breaking ABI? Can this feature be added/removed without -breaking ABI? For more information about the resilience model, see the -[library evolution -document](https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst) -in the Swift repository. +runtime library. Most ABI changes center around interactions with +specific declarations. Proposals that do not affect how code is +generated to interact with an external declaration usually do not +have ABI impact. + +For proposals that affect general code generation rules, consider +the impact on code that's already been compiled. Does the proposal +affect declarations that haven't explicitly adopted it, and if so, +does it change ABI details such as symbol names or conventions +around their use? Will existing code change its dynamic behavior +when running against a new version of the language runtime or +standard library? Conversely, will code compiled in the new way +continue to run on old versions of the language runtime or standard +library? + +For proposals that affect the standard library, consider the impact +on any existing declarations. As above, does the proposal change symbol +names, conventions, or dynamic behavior? Will newly-compiled code work +on old library versions, and will new library versions work with +previously-compiled code? + +## API resilience and other adoption concerns + +The compatibility sections above are focused on the direct impact +of the proposal on existing code. In this section, describe issues +that intentional adopters of the proposal should be aware of. + +For proposals that add features to the language or standard library, +consider whether the features require ABI support. Will adopters need +a new version of the library or language runtime? Be conservative: if +you're hoping to support back-deployment, but you can't guarantee it +at the time of review, just say that the feature requires a new +version. + +Consider also the impact on library adopters of those features. Can +adopting this feature in a library break source or ABI compatibility +for users of the library? If a library adopts the feature, can it +be *un*-adopted later withour breaking source or ABI compatibility? +Will package authors be able to selectively adopt this feature depending +on the tools version available, or will it require bumping the minimum +tools version required by the package? + +If there are no concerns to raise in this section, it may be removed. + +## Future directions + +Describe any interesting proposals that could build on this proposal +in the future. This is especially important when these future +directions inform the design of the proposal, for example by making +sure an attribute encodes enough information to be used for other +purposes. + +The rest of the proposal should generally not talk about future +directions except by referring to this section. It is important +not to confuse reviewers about what is covered by this specific +proposal. If there's a larger vision that needs to be explained +in order to understand this proposal, consider starting a discussion +thread on the forums to capture your broader thoughts. + +Avoid making affirmative statements in this section, such as "we +will" or even "we should". Describe the proposals neutrally as +possibilities to be considered in the future. + +Consider whether any of these future directions should really just +be part of the current proposal. It's important to make focused, +self-contained proposals that can be incrementally implemented and +reviewed, but it's also good when proposals feel "complete" rather +than leaving significant gaps in their design. For example, when +[SE-0193](https://github.com/apple/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md) +introduced the `@inlinable` attribute, it also included the +`@usableFromInline` attribute so that declarations used in inlinable +functions didn't have to be `public`. This was a relatively small +addition to the proposal which avoided creating a serious usability +problem for many adopters of `@inlinable`. ## Alternatives considered -Describe alternative approaches to addressing the same problem, and -why you chose this approach instead. +Describe alternative approaches to addressing the same problem. +This is an important part of most proposal documents. Reviewers +are often familiar with other approaches prior to review and may +have reasons to prefer them. This section is your first opportunity +to try to convince them that your approach is the right one, and +even if you don't fully succeed, you can help set the terms of the +conversation and make the review a much more productive exchange +of ideas. + +You should be fair about other proposals, but you do not have to +be neutral; after all, you are specifically proposing something +else. Describe any advantages these alternatives might have, but +also be sure to explain the disadvantages that led you to prefer +the approach in this proposal. ## Acknowledgments @@ -151,3 +213,6 @@ If significant changes or improvements suggested by members of the community were incorporated into the proposal as it developed, take a moment here to thank them for their contributions. Swift evolution is a collaborative process, and everyone's input should receive recognition! + +Generally, you should not acknowledge anyone who is listed as a +co-author or as the review manager. From 096c881a1fb63a0e7a90f0c27ff2a9dddd2efaca Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 1 Feb 2023 18:38:58 -0500 Subject: [PATCH 2964/4563] Update 0382-expression-macros.md (#1939) --- proposals/0382-expression-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index d9cd127686..f3ce3219bd 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -3,9 +3,9 @@ * Proposal: [SE-0382](0382-expression-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (December 16, 2022...January 15, 2023)** +* Status: **Returned for revision** * Implementation: Partial implementation is available in `main` under the experimental feature flag `Macros`. An [example macro repository](https://github.com/DougGregor/swift-macro-examples) provides a way to experiment with this feature. -* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861/16)) ([review](https://forums.swift.org/t/se-0382-expression-macros/62090)) +* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861/16)) ([review #1](https://forums.swift.org/t/se-0382-expression-macros/62090)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0382-expression-macros/62898)) ([pitch #3](https://forums.swift.org/t/se-0382-expression-macros-mini-pitch-for-updates/62810)) ## Introduction From caa72defd7f939e88af90cdd5063c3b96e0f4d94 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 1 Feb 2023 20:21:34 -0500 Subject: [PATCH 2965/4563] Accept SE-0376 --- proposals/0376-function-back-deployment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index 114246a2bd..a066606819 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -4,8 +4,8 @@ * Author: [Allan Shortlidge](https://github.com/tshortli) * Implementation: [apple/swift#41271](https://github.com/apple/swift/pull/41271), [apple/swift#41348](https://github.com/apple/swift/pull/41348), [apple/swift#41416](https://github.com/apple/swift/pull/41416), [apple/swift#41612](https://github.com/apple/swift/pull/41612) as the underscored attribute `@_backDeploy` * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) -* Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0376-function-back-deployment/61507)) ([second review](https://forums.swift.org/t/se-0376-second-review-function-back-deployment/61671)) ([returned for revision (second review)](https://forums.swift.org/t/returned-for-revision-se-0376-second-review-function-back-deployment/62374)) -* Status: **Returned for revision** +* Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0376-function-back-deployment/61507)) ([second review](https://forums.swift.org/t/se-0376-second-review-function-back-deployment/61671)) ([returned for revision (second review)](https://forums.swift.org/t/returned-for-revision-se-0376-second-review-function-back-deployment/62374))([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0376-function-back-deployment/62905)) +* Status: **Accepted** ## Introduction From ca312404344dcc3b715a560ad0040bf222115be6 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 1 Feb 2023 21:15:41 -0500 Subject: [PATCH 2966/4563] Revert changes to implementation list --- proposals/0376-function-back-deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index 15dad4ba31..6ee987e5ab 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -2,7 +2,7 @@ * Proposal: [SE-0376](0376-function-back-deployment.md) * Author: [Allan Shortlidge](https://github.com/tshortli) -* Implementation: Available as `@_backDeploy(before:)` in Swift 5.8 +* Implementation: [apple/swift#41271](https://github.com/apple/swift/pull/41271), [apple/swift#41348](https://github.com/apple/swift/pull/41348), [apple/swift#41416](https://github.com/apple/swift/pull/41416), [apple/swift#41612](https://github.com/apple/swift/pull/41612) as the underscored attribute `@_backDeploy` * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) * Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0376-function-back-deployment/61507)) ([second review](https://forums.swift.org/t/se-0376-second-review-function-back-deployment/61671)) ([returned for revision (second review)](https://forums.swift.org/t/returned-for-revision-se-0376-second-review-function-back-deployment/62374)) * Status: **Returned for revision** From e74e11297c09b2fdd5f52343822c0c28d08a7168 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 3 Feb 2023 14:33:48 +0000 Subject: [PATCH 2967/4563] [SE-0368] StaticBigInt: use approved doc comments --- proposals/0368-staticbigint.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index 90c1fc0495..b91619e4aa 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -82,18 +82,18 @@ public struct StaticBigInt: /// Returns the given value unchanged. public static prefix func + (_ rhs: Self) -> Self - /// Returns `-1`, `0`, or `+1` to indicate whether this value is less than, - /// equal to, or greater than zero. + /// Indicates the value's sign. /// - /// The return type is `Int` rather than `Self`. + /// - Returns: `-1` if the value is less than zero, `0` if it is equal to + /// zero, or `+1` if it is greater than zero. public func signum() -> Int /// Returns the minimal number of bits in this value's binary representation, /// including the sign bit, and excluding the sign extension. /// /// The following examples show the least significant byte of each value's - /// binary representation, separated into excluded and included bits. Negative - /// values are in two's complement. + /// binary representation, separated (by an underscore) into excluded and + /// included bits. Negative values are in two's complement. /// /// * `-4` (`0b11111_100`) is 3 bits. /// * `-3` (`0b11111_101`) is 3 bits. From 3c89a1f9ccb1fd7438c91ea5fe969f8d9cf7e941 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 3 Feb 2023 14:39:33 +0000 Subject: [PATCH 2968/4563] [SE-0368] StaticBigInt: remove future directions --- proposals/0368-staticbigint.md | 39 ---------------------------------- 1 file changed, 39 deletions(-) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index b91619e4aa..6e233140cf 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -145,53 +145,14 @@ The integer literal type has to be selected statically as the associated type. T - Xiaodi Wu [suggested](https://forums.swift.org/t/staticbigint/54545/23) that a different naming scheme and API design be chosen to accommodate other similar types, such as IEEE 754 interchange formats. However, specific alternatives weren't put forward for consideration. Using non-arithmetic types for interchange formats would seem to be a deliberate choice; whereas for `StaticBigInt` it's because of an inherent limitation. -## Future directions - -- It may be possible to diagnose integer literal overflow at compile-time, if constant evaluation is added to the language. - -- A mutable `BigInt: SignedInteger` type (in the standard library) would either complement or [obsolete][] `StaticBigInt`: - - > … there is very little reason to use `StaticString` these days over a regular `String`, as the regular `String` is initialized with a pointer to a string in the const section and won't perform any reference counting. - -- `StaticBigInt` (or a similar type) might be useful for [auto-generated][] constant data, if we also had *multiline* integer literals: - - ```swift - let _swift_stdlib_graphemeBreakProperties: StaticBigInt = (((0x_ - 0x____________________________3DEE0100_0FEE0080_2BEE0020_03EE0000_B701F947_ - 0x_8121F93C_85C1F90C_8A21F8AE_80E1F888_80A1F85A_80E1F848_8061F80C_8541F7D5_ - /* [74 lines redacted] */ - 0x_2280064B_0000061C_21400610_40A00600_200005C7_202005C4_202005C1_200005BF_ - 0x_25800591_20C00483_2DE00300_800000AE_000000AD_800000A9_0400007F_03E00000_ - ))) - ``` - -- The unary `+` operator function isn't sufficient for all use cases: - - ```swift - let a: StaticBigInt = -42 // OK - let b: StaticBigInt = +42 // OK - - let c = -42 as StaticBigInt // OK - let d = +42 as StaticBigInt // OK - - let e = StaticBigInt(-42) // OK - let f = StaticBigInt(+42) // error - ``` - - Could the plus sign be added to the language grammar of integer (and floating-point) literals? - ## Acknowledgments John McCall made significant improvements to this proposal; and (in Swift 5.0) implemented arbitrary-precision integer literals. `StaticBigInt` is a thin wrapper around the existing [`Builtin.IntLiteral`][] type. -[auto-generated]: - [`Builtin.IntLiteral`]: [numeric protocols]: -[obsolete]: - [Swift Numerics]: From 21438f26dffcaecaa38e1fea09c2ff70d5ff9353 Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Mon, 6 Feb 2023 11:08:57 +0000 Subject: [PATCH 2969/4563] Update proposal with review feedback --- proposals/NNNN-async-stream-factory.md | 96 ++++++++++++++++++++------ 1 file changed, 76 insertions(+), 20 deletions(-) diff --git a/proposals/NNNN-async-stream-factory.md b/proposals/NNNN-async-stream-factory.md index e1247781ac..af87664378 100644 --- a/proposals/NNNN-async-stream-factory.md +++ b/proposals/NNNN-async-stream-factory.md @@ -7,37 +7,72 @@ * Pitch: [Convenience Async[Throwing]Stream.makeStream methods](https://forums.swift.org/t/pitch-convenience-async-throwing-stream-makestream-methods/61030) * Implementation: [apple/swift#62968](https://github.com/apple/swift/pull/62968) -
    -Revision history - -| | | -| ---------- | ------------------------------------------------- | -| 2022-10-26 | Initial pitch. | -| 2023-01-12 | Switch to concrete return type | +## Introduction -
    +We propose introducing helper methods for creating `AsyncStream` and `AsyncThrowingStream` +instances which make the stream's continuation easier to access. -## Introduction +## Motivation With [SE-0314](https://github.com/apple/swift-evolution/blob/main/proposals/0314-async-stream.md) -we introduced `AsyncStream` and `AsyncThrowingStream` which act as a source +we introduced `AsyncStream` and `AsyncThrowingStream` which act as a root `AsyncSequence` that the standard library offers. -## Motivation - After having used `Async[Throwing]Stream` for some time, a common usage is to pass the continuation and the `Async[Throwing]Stream` to different places. This requires escaping the `Async[Throwing]Stream.Continuation` out of the closure that is passed to the initialiser. Escaping the continuation is slightly inconvenient since it requires a dance around an implicitly unwrapped optional. Furthermore, the closure implies -that the continuation lifetime is scoped to the closure which it isn't. +that the continuation lifetime is scoped to the closure which it isn't. This is how +an example usage of the current `AsyncStream` API looks like. + +```swift +var cont: AsyncStream.Continuation! +let stream = AsyncStream { cont = $0 } +// We have to assing the continuation to a let to avoid sendability warnings +let continuation = cont + +await withTaskGroup(of: Void.self) { group in + group.addTask { + for i in 0...9 { + continuation.yield(i) + } + continuation.finish() + } + + group.addTask { + for await i in stream { + print(i) + } + } +} +``` ## Proposed solution In order to fill this gap, I propose to add a new static method `makeStream` on `AsyncStream` and `AsyncThrowingStream` that returns both the stream -and the continuation. +and the continuation. An example of using the new proposed convenience methods looks like this: + +```swift +let newStream = AsyncStream.makeStream(of: Int.self) + +await withTaskGroup(of: Void.self) { group in + group.addTask { + for i in 0...9 { + newStream.continuation.yield(i) + } + newStream.continuation.finish() + } + + group.addTask { + for await i in newStream.stream { + print(i) + } + } +} +``` ## Detailed design @@ -60,7 +95,7 @@ extension AsyncStream { /// The stream which should be passed to the consumer. public let stream: AsyncStream - public init(stream: AsyncStream, continuation: AsyncStream.Continuation) { + private init(stream: AsyncStream, continuation: AsyncStream.Continuation) { self.stream = stream self.continuation = continuation } @@ -98,7 +133,7 @@ extension AsyncThrowingStream { /// The stream which should be passed to the consumer. public let stream: AsyncThrowingStream - public init(stream: AsyncThrowingStream, continuation: AsyncThrowingStream.Continuation) { + private init(stream: AsyncThrowingStream, continuation: AsyncThrowingStream.Continuation) { self.stream = stream self.continuation = continuation } @@ -124,14 +159,19 @@ extension AsyncThrowingStream { } ``` -## Source compatibility, Effect on ABI stability, Effect on API resilience +## Source compatibility +This change is additive and does not affect source compatibility. -As this is an additive change, it should not have any compatibility, stability or resilience problems. The only potential problem would be if someone has already run into this shortcoming and decided to define their own `makeStream` methods. +## Effect on ABI stability +This change introduces new concurrency library ABI in the form of the two `makeStream` methods and `NewStream` structs, but it does not affect the ABI of existing declarations. + +## Effect on API resilience +None; adding nested types and static methods is permitted by the existing resilience model. ## Alternatives considered ### Return a tuple instead of a concrete type -My initial pitch was using a tuple as the return paramter of the factory; +My initial pitch was using a tuple as the result type of the factory; however, I walked back on it since I think we can provide better documentation on the concrete type. Furthermore, it makes it more discoverable as well. @@ -140,7 +180,7 @@ The upside of using a tuple based approach is that we can backdeploy it. An implementation returning a tuple would look like this; ```swift -extension AsyncStream +extension AsyncStream { /// Initializes a new ``AsyncStream`` and an ``AsyncStream/Continuation``. /// /// - Parameters: @@ -160,6 +200,22 @@ extension AsyncStream } ``` +### Expose an initilizer on the `NewStream` type +During the pitch it was brought up that we could expose an `init` on the `NewStream` types +that this proposal wants to add. I decided against that since one would have to spell out +`AsyncStream.NewStream()` to access the `init`. This is quite hard to discover in +my opinion. + +### Pass a continuation to the `AsyncStream.init()` +During the pitch it was brought up that we could let users pass a continuation to the +`AsyncStream.init()`; however, this opens up a few problems: +1. A continuation could be passed to multiple streams +2. A continuation which is not passed to a stream is useless + +In the end, the `AsyncStream.Continuation` is deeply coupled to one instance of an +`AsyncStream` hence we should create an API that conveys this coupling and prevents +users from misuse. + ### Do nothing alternative We could just leave the current creation of `Async[Throwing]Stream` as is; however, since it is part of the standard library we should provide From 9b32fcd89dd81650a444ceb248367f0607125315 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 6 Feb 2023 12:46:23 +0000 Subject: [PATCH 2970/4563] [SE-0368] StaticBigInt: remove prefix `+` operator Co-authored-by: Stephen Canon --- proposals/0368-staticbigint.md | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index 6e233140cf..182fd94a75 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -4,8 +4,8 @@ * Author: [Ben Rimmington](https://github.com/benrimmington) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 5.8)** -* Implementation: [apple/swift#40722](https://github.com/apple/swift/pull/40722) -* Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) ([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) ([acceptance](https://forums.swift.org/t/accepted-se-0368-staticbigint/59962)) +* Implementation: [apple/swift#40722](https://github.com/apple/swift/pull/40722), [apple/swift#62733](https://github.com/apple/swift/pull/62733) +* Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) ([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) ([acceptance](https://forums.swift.org/t/accepted-se-0368-staticbigint/59962)) ([amendment](https://forums.swift.org/t/pitch-amend-se-0368-to-remove-prefix-operator/62173))
    Revision history @@ -16,6 +16,7 @@ | 2022-02-01 | Updated with an "ABI-neutral" abstraction. | | 2022-04-23 | Updated with an "infinitely-sign-extended" model. | | 2022-08-18 | Updated with a "non-generic" subscript. | +| 2023-02-03 | Amended to remove the prefix `+` operator. |
    @@ -79,9 +80,6 @@ public struct StaticBigInt: ExpressibleByIntegerLiteral, Sendable { - /// Returns the given value unchanged. - public static prefix func + (_ rhs: Self) -> Self - /// Indicates the value's sign. /// /// - Returns: `-1` if the value is less than zero, `0` if it is equal to @@ -117,7 +115,7 @@ public struct StaticBigInt: /// negative[1] //-> 0xFFEEDDCCBBAA9988 /// negative[2] //-> 0xFFFFFFFFFFFFFFFF /// - /// let positive: StaticBigInt = +0x0011223344556677_8899AABBCCDDEEFF + /// let positive: StaticBigInt = 0x0011223344556677_8899AABBCCDDEEFF /// positive.signum() //-> +1 /// positive.bitWidth //-> 118 /// positive[0] //-> 0x8899AABBCCDDEEFF @@ -145,10 +143,33 @@ The integer literal type has to be selected statically as the associated type. T - Xiaodi Wu [suggested](https://forums.swift.org/t/staticbigint/54545/23) that a different naming scheme and API design be chosen to accommodate other similar types, such as IEEE 754 interchange formats. However, specific alternatives weren't put forward for consideration. Using non-arithmetic types for interchange formats would seem to be a deliberate choice; whereas for `StaticBigInt` it's because of an inherent limitation. +- A previously accepted version of this proposal included the following operator, for symmetry between negative and positive literals. + + ```swift + extension StaticBigInt { + /// Returns the given value unchanged. + public static prefix func + (_ rhs: Self) -> Self + } + ``` + + It was later discovered to be a source-breaking change. For example: + + ```swift + let a = -7 // inferred as `a: Int` + let b = +6 // inferred as `b: StaticBigInt` + let c = a * b + // ^ + // error: Cannot convert value of type 'StaticBigInt' to expected argument type 'Int' + ``` + + The prefix `+` operator on [`AdditiveArithmetic`][numeric protocols] was no longer chosen, because concrete overloads are preferred over generic overloads. + ## Acknowledgments John McCall made significant improvements to this proposal; and (in Swift 5.0) implemented arbitrary-precision integer literals. `StaticBigInt` is a thin wrapper around the existing [`Builtin.IntLiteral`][] type. +Stephen Canon proposed an amendment to remove the prefix `+` operator. + [`Builtin.IntLiteral`]: From 35292d27f678f645ac232c8024e710e622f705b3 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 6 Feb 2023 10:46:40 -0800 Subject: [PATCH 2971/4563] Schedule SE-0368 for active amendment review. --- proposals/0368-staticbigint.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index 182fd94a75..4ce5079d19 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -2,8 +2,8 @@ * Proposal: [SE-0368](0368-staticbigint.md) * Author: [Ben Rimmington](https://github.com/benrimmington) -* Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Implemented (Swift 5.8)** +* Review Manager: [Doug Gregor](https://github.com/DougGregor), [Holly Borla](https://github.com/hborla) +* Status: **Active Review (February 6, 2023...February 13, 2023)** * Implementation: [apple/swift#40722](https://github.com/apple/swift/pull/40722), [apple/swift#62733](https://github.com/apple/swift/pull/62733) * Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) ([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) ([acceptance](https://forums.swift.org/t/accepted-se-0368-staticbigint/59962)) ([amendment](https://forums.swift.org/t/pitch-amend-se-0368-to-remove-prefix-operator/62173)) From a2a4a77cf0ecf3fae7a4bf991a55cb26abff6c7d Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 6 Feb 2023 10:55:40 -0800 Subject: [PATCH 2972/4563] Link to amendment review for SE-0368. (#1941) --- proposals/0368-staticbigint.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index 4ce5079d19..a38b140110 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -5,7 +5,7 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor), [Holly Borla](https://github.com/hborla) * Status: **Active Review (February 6, 2023...February 13, 2023)** * Implementation: [apple/swift#40722](https://github.com/apple/swift/pull/40722), [apple/swift#62733](https://github.com/apple/swift/pull/62733) -* Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) ([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) ([acceptance](https://forums.swift.org/t/accepted-se-0368-staticbigint/59962)) ([amendment](https://forums.swift.org/t/pitch-amend-se-0368-to-remove-prefix-operator/62173)) +* Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) ([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) ([acceptance](https://forums.swift.org/t/accepted-se-0368-staticbigint/59962)) ([amendment](https://forums.swift.org/t/pitch-amend-se-0368-to-remove-prefix-operator/62173)), ([amendment review](https://forums.swift.org/t/amendment-se-0368-staticbigint/62992))
    Revision history From e4db2af906a4a3eded0d11e9fb2b575844dad49c Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 6 Feb 2023 14:20:41 -0500 Subject: [PATCH 2973/4563] Incorporate review feedback from Freddy and Alex --- proposal-templates/0000-swift-template.md | 127 +++++++++++++++++----- 1 file changed, 102 insertions(+), 25 deletions(-) diff --git a/proposal-templates/0000-swift-template.md b/proposal-templates/0000-swift-template.md index e677165ba3..28fd94c84c 100644 --- a/proposal-templates/0000-swift-template.md +++ b/proposal-templates/0000-swift-template.md @@ -13,29 +13,81 @@ * Previous Revision: *if applicable* [1](https://github.com/apple/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md) * Review: ([pitch](https://forums.swift.org/...)) -When filling out this template, you should delete or replace all of the text except for the section headers and the header fields above. For example, you should delete everything from this paragraph down to the Introduction section below. - -As a proposal author, you should fill out all of the header fields except `Review Manager`. The review manager will set that field and change several others as part of initiating the review. Delete any header fields that are not applicable to your proposal. - -When sharing a link to the proposal while it is still a PR, be sure to share a live link to the proposal, not an exact commit, so that readers will always see the latest version when you make changes. On GitHub, you can find this link by browsing the PR branch: from the PR page, click the "username wants to merge ... from username:my-branch-name" link and find the proposal file in that branch. - -`Status` should reflect the current implementation status while the proposal is still a PR. The proposal cannot be reviewed until an implementation is available, but early readers should see the correct status. - -`Vision` should link to the [vision document](https://forums.swift.org/t/the-role-of-vision-documents-in-swift-evolution/62101) for this proposal, if it is part of a vision. Most proposals are not part of a vision. If a vision has been written but not yet accepted, link to the discussion thread for the vision. - -`Roadmap` should link to the discussion thread for the roadmap for this proposal, if applicable. When a complex feature is broken down into several closely-related proposals to make evolution review easier and more focused, it's helpful to make a forum post concisely explaining what's going on and detailing how the proposals will be reviewed. That post is called a "roadmap", and this field should link to it. Most proposals don't need roadmaps. - -`Bug` should be used when this proposal is fixing a bug with significant discussion in the bug report. It is not necessary to link bugs that do not contain significant discussion or that merely duplicate discussion linked somewhere else. Do not link bugs from private bug trackers. - -`Implementation` should link to the PR(s) implementing the feature. If the proposal has not been implemented yet, or if it simply codifies existing behavior, just say that. If the implementation has already been committed to the main branch (as an experimental feature), say that and specify the experimental feature flag. If the implementation is spread across multiple PRs, just link to the most important ones. - -`Feature Identifier` should be the feature name used to identify this feature under [SE-0362](https://github.com/apple/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md#proposals-define-their-own-feature-identifier). Not all proposals need a feature identifier. You should think about whether one would be useful for your proposal as part of filling this field out. - -`Previous Proposal` should be used when there is a specific line of succession between the linked proposal and this one. For example, this proposal might have been removed from the previous proposal so that it can be reviewed separately, or this proposal might supersede the previous proposal in some way that was felt to exceed the scope of a "revision". - -`Previous Revision` should be added after a major substantive revision of a proposal that has undergone review. It links to the previously reviewed revision. It is not necessary to add or update this field after minor editorial changes. - -`Review` is a history of all discussion threads about this proposal, in chronological order. Use these standardized link names: `pitch` `review` `revision` `acceptance` `rejection`. If there are multiple such threads, spell the ordinal out: `first pitch` `second review` etc. +When filling out this template, you should delete or replace all of +the text except for the section headers and the header fields above. +For example, you should delete everything from this paragraph down to +the Introduction section below. + +As a proposal author, you should fill out all of the header fields +except `Review Manager`. The review manager will set that field and +change several others as part of initiating the review. Delete any +header fields marked *if applicable* that are not applicable to your +proposal. + +When sharing a link to the proposal while it is still a PR, be sure +to share a live link to the proposal, not an exact commit, so that +readers will always see the latest version when you make changes. +On GitHub, you can find this link by browsing the PR branch: from the +PR page, click the "username wants to merge ... from username:my-branch-name" +link and find the proposal file in that branch. + +`Status` should reflect the current implementation status while the +proposal is still a PR. The proposal cannot be reviewed until an +implementation is available, but early readers should see the correct +status. + +`Vision` should link to the [vision document](https://forums.swift.org/t/the-role-of-vision-documents-in-swift-evolution/62101) +for this proposal, if it is part of a vision. Most proposals are not +part of a vision. If a vision has been written but not yet accepted, +link to the discussion thread for the vision. + +`Roadmap` should link to the discussion thread for the roadmap for +this proposal, if applicable. When a complex feature is broken down +into several closely-related proposals to make evolution review easier +and more focused, it's helpful to make a forum post explaining what's +going on and detailing how the proposals are expected to be submitted +to review. That post is called a "roadmap". Most proposals don't need +roadmaps, but if this proposal was part of one, this field should link +to it. + +`Bug` should be used when this proposal is fixing a bug with significant +discussion in the bug report. It is not necessary to link bugs that do +not contain significant discussion or that merely duplicate discussion +linked somewhere else. Do not link bugs from private bug trackers. + +`Implementation` should link to the PR(s) implementing the feature. +If the proposal has not been implemented yet, or if it simply codifies +existing behavior, just say that. If the implementation has already +been committed to the main branch (as an experimental feature), say +that and specify the experimental feature flag. If the implementation +is spread across multiple PRs, just link to the most important ones. + +`Feature Identifier` should be the feature name used to identify this +feature under [SE-0362](https://github.com/apple/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md#proposals-define-their-own-feature-identifier). +Not all proposals need a feature identifier. You should think about +whether one would be useful for your proposal as part of filling this +field out. + +`Previous Proposal` should be used when there is a specific line of +succession between this proposal and another proposal. For example, +this proposal might have been removed from a previous proposal so +that it can be reviewed separately, or this proposal might supersede +a previous proposal in some way that was felt to exceed the scope of +a "revision". Include text briefly explaining the relationship, +such as "Supersedes SE-1234" or "Extracted from SE-01234". If possible, +link to a post explaining the relationship, such as a review decision +that asked for part of the proposal to be split off. Otherwise, you +can just link to the previous proposal. + +`Previous Revision` should be added after a major substantive revision +of a proposal that has undergone review. It links to the previously +reviewed revision. It is not necessary to add or update this field +after minor editorial changes. + +`Review` is a history of all discussion threads about this proposal, +in chronological order. Use these standardized link names: `pitch` +`review` `revision` `acceptance` `rejection`. If there are multiple +such threads, spell the ordinal out: `first pitch` `second review` etc. ## Introduction @@ -136,7 +188,20 @@ names, conventions, or dynamic behavior? Will newly-compiled code work on old library versions, and will new library versions work with previously-compiled code? -## API resilience and other adoption concerns +This section will often end up very short. A proposal that just +adds a new standard library feature, for example, will usually +say either "This proposal is purely an extension of the ABI of the +standard library and does not change any existing features" or +"This proposal is purely an extension of the standard library which +can be implemented without any ABI support" (whichever applies). +Nonetheless, it is important to demonstrate that you've considered +the ABI implications. + +If the design of the feature was significantly constrained by +the need to maintain ABI compatibility, this section is a reasonable +place to discuss that. + +## Implications on adoption The compatibility sections above are focused on the direct impact of the proposal on existing code. In this section, describe issues @@ -157,7 +222,10 @@ Will package authors be able to selectively adopt this feature depending on the tools version available, or will it require bumping the minimum tools version required by the package? -If there are no concerns to raise in this section, it may be removed. +If there are no concerns to raise in this section, leave it in with +text like "This feature can be freely adopted and un-adopted in source +code with no deployment constraints and without affecting source or ABI +compatibility." ## Future directions @@ -207,6 +275,15 @@ else. Describe any advantages these alternatives might have, but also be sure to explain the disadvantages that led you to prefer the approach in this proposal. +You should update this section during the pitch phase to discuss +any particularly interesting alternatives raised by the community. +You do not need to list every idea raised during the pitch, just +the ones you think raise points that are worth discussing. Of course, +if you decide the alternative is more compelling than what's in +the current proposal, you should change the main proposal; be sure +to then discuss your previous proposal in this section and explain +why the new idea is better. + ## Acknowledgments If significant changes or improvements suggested by members of the From a65540297416404a4124498eb90617d98338e694 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 7 Feb 2023 11:21:33 -0800 Subject: [PATCH 2974/4563] [SE-0385] Add another use case to the motivation section. (#1944) --- proposals/0385-custom-reflection-metadata.md | 46 ++++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/proposals/0385-custom-reflection-metadata.md b/proposals/0385-custom-reflection-metadata.md index 98db082177..1332d01ee7 100644 --- a/proposals/0385-custom-reflection-metadata.md +++ b/proposals/0385-custom-reflection-metadata.md @@ -19,7 +19,7 @@ Previous Swift Forum discussions There are some problem domains in which it can be beneficial for a library author to let a client annotate within their own code certain declarations that the library should be made aware of, since requiring the client call an explicit API instead would be too onerous, repetitive, or easy to forget. -One classic example is **testing**: there is a common pattern in unit testing libraries where users define a type that extends one of the library's types, they annotate some of its methods as tests, and the library locates and runs all tests automatically, after initializing a distinct instance of the containing type for each. There is no official mechanism in Swift to implement this test discovery pattern today, however XCTest—the language's current de-facto standard test library—has longstanding workarounds: +One classic example is **testing**: there is a common pattern in unit testing libraries where users define a type that extends one of the library's types, they annotate some of its methods as tests, and the library locates, initializes, and runs all tests automatically. There is no official mechanism in Swift to implement this test discovery pattern today, however XCTest—the language's current de-facto standard test library—has longstanding workarounds: * On Apple platforms, XCTest relies on the Objective-C runtime to enumerate all subclasses of a known base class and their methods, and it considers all instance methods with a supported signature and name prefixed with "test" a test method. * On other platforms, XCTest is typically used from a package, and the Swift Package Manager has special logic to introspect build-time indexer data, locate test methods, and explicitly pass the list of discovered tests to XCTest to run. @@ -30,16 +30,50 @@ XCTest's current approach has some drawbacks and limitations: * Since tests are declared implicitly, there is no way for a user to provide additional details about an individual test or group of tests. It would be useful to have a way to indicate whether a test is enabled, its requirements, or other metadata, for example, so that the testing library could use this information to inform how it executes tests and offer more powerful features. * The lack of a built-in runtime discovery mechanism means that related tools (such as Swift Package Manager) require specialized discovery logic for each test library they support. This makes adding support for alternate test libraries to those tools very difficult and increases their implementation complexity. -The general pattern of registering code to be discovered by a framework is common across Swift programs. For example, a program that uses a plugin architecture commonly uses a protocol for the interface of the plugin, which is then implemented on concrete types in clients. This pattern imposes error-prone registration boilerplate, where clients must explicitly supply a list of concrete plugin types or explicitly register individual plugin types to be used by the framework before the framework needs them. +Registering code to be discovered by a framework is a common pattern across Swift programs. For example, a program that uses a plugin architecture commonly uses a protocol for the interface of the plugin, which is then implemented on concrete types in clients. This pattern imposes error-prone registration boilerplate, where clients must explicitly supply a list of concrete plugin types or explicitly register individual plugin types to be used by the framework before the framework needs them. + +More generally, annotating parts of a program with metadata to be used by other parts of the program has use-cases beyond registration patterns. Consider the `Persisted` property wrapper from the [Realm](https://github.com/realm/realm-swift) package: + +```swift +@propertyWrapper +public struct Persisted { ... } + +class Dog: Object { + @Persisted var name: String + @Persisted var age: Int +} +``` + +To support [advanced schema customization](https://forums.swift.org/t/se-0385-custom-reflection-metadata/62777/19), the property wrapper could store a string that provides a custom name for the underlying database column, specified in the attribute arguments, e.g. `@Persisted(named: "CustomName")`. However, storing this metadata in the property wrapper requires additional storage for each _instance_ of the containing type, even though the metadata value is fixed for the declaration the property wrapper is attached to. In addition to higher memory overload, the metadata values are evaluated eagerly, and for each instantiation of the containing type, rendering property-wrapper instance metadata too expensive for this use case. -Java and C# programming languages both have a feature called “Java Annotations” and “C# attributes” respectively. It allows to add attributes on methods, variables, parameters, and, in case of Java, packages that are accessible to Java/C# compiler and at runtime via reflection APIs. We propose a similar addition to Swift called **custom reflection metadata**. ## Proposed solution * A new built-in attribute `@reflectionMetadata` that can be applied to structs, enums, classes, and actors. * Types annotated with this built-in attribute can be used as custom attributes on declarations that can be used as values. * The custom attribute can have additional arguments; the custom attribute application will turn into an initializer call on the attribute type, passing in the declaration value as the first argument. -* A reflection API that can gather all declarations with a given custom attribute attached. +* A reflection API that can gather all declarations with a given custom attribute attached, which lazily constructs the metadata values when invoked. + +Combined with [attached macros](https://forums.swift.org/t/pitch-attached-macros/62812), the `@Persisted` property wrapper in Realm can evolve into a macro attached to persistent types, combined with custom metadata attributes that provide schema customization for specific declarations: + +```swift +@reflectionMetadata +struct Named { + let name: String + + init(attachedTo: T.Type, _ name: String) { + self.name = name + } +} + +@Persisted +class Dog: Object { + var name: String + @Named("CustomName") var age: Int +} +``` + +This approach completely eliminates initialization overhead of using property wrappers, provides separate storage of custom metadata values, and enables lazy initialization of metadata values that is only invoked when the framework requests the metadata. ## Detailed design @@ -319,6 +353,10 @@ We considered several alternative spellings of the attribute used to declare a r A previous Swift Evolution discussion suggested [adding a built-in `@test` attribute](https://forums.swift.org/t/rfc-in-line-tests/12111) to the language. However, registration is a general code pattern that is also used outside of testing, so allowing libraries to declare their own domain-specific attributes is a more general approach that supports a wider set of use cases. +## Acknowledgments + +Thank you to [Thomas Goyne](https://forums.swift.org/u/tgoyne) for surfacing use cases in the Realm Swift project and insights into alternative design directions. + ## Revision history ### Changes after first pitch From 4e242fca826d0ed6aaba32c1585350b7a72c0d5e Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Thu, 9 Feb 2023 19:12:04 +0800 Subject: [PATCH 2975/4563] Ban macros generating default literal type overrides --- proposals/nnnn-freestanding-macros.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index 0ce4ff3068..8d992f4ba9 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -197,6 +197,10 @@ A macro can expand to any declaration that is syntatically and semantically well * `extension` declarations can never be produced by a macro. The effect of an extension declaration is wide-ranging, with the ability to add conformances, members, and so on. These capabilities are meant to be introduced in a more fine-grained manner. * `operator` and `precedencegroup` declarations can never be produced by a macro, because they could allow one to reshape the precedence graph for existing code causing subtle differences in the semantics of code that sees the macro expansion vs. code that does not. * `macro` declarations can never be produced by a macro, because allowing this would allow a macro to trivially produce infinitely recursive macro expansion. +* Top-level default literal type overrides, including `IntegerLiteralType`, + `FloatLiteralType`, `BooleanLiteralType`, + `ExtendedGraphemeClusterLiteralType`, `UnicodeScalarLiteralType`, and + `StringLiteralType`, can never be produced by a macro. ### Up-front declarations of newly-introduced names From 3d87a3960746cc8c3bf99432ab34b59c9d6eb9eb Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 10 Feb 2023 07:58:04 -0800 Subject: [PATCH 2976/4563] [SE-0382] Expression macros updates based on review discussion and further development (#1928) * [SE-0382] Review updates to simplify and protocol-ize MacroExpansionContext. * Switch `@expression` to `@freestanding(expression)` to align with the other macros proposals and vision document. * Make the `ExpressionMacro.expansion(of:in:)` requirement `async`. * Allow macro declarations to have opaque result types, and define the uniqueness rules. * Simplify the grammar of macro declarations to be more function-like: * Apply suggestions from code review Co-authored-by: Ben Rimmington * [SE-0382] Give `createUniqueName` a parameter, per Ben Rimmington * [SE-0382] Introduce a general `location` operation on `MacroExpansionContext` to get the source location of any syntax node from a macro input * Use abstract source locations instead of "real" ones * Apply suggestions from code review Co-authored-by: Ben Rimmington * Prohibit the use of non-builtin macros as default arguments of parameters --------- Co-authored-by: Ben Rimmington --- proposals/0382-expression-macros.md | 219 +++++++++++++++++++--------- 1 file changed, 147 insertions(+), 72 deletions(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index f3ce3219bd..94a4d02f65 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -13,7 +13,7 @@ Expression macros provide a way to extend Swift with new kinds of expressions, w ## Motivation -Expression macros are one part of the [vision for macros in Swift](https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900), which lays out general motivation for introducing macros into the language. Expressions in particular are an area where the language already provides decent abstractions for factoring out runtime behavior, because one can create a function that you call as an expression from anywhere. However, with a few hard-coded exceptions like `#file` and `#line`, an expression cannot reason about or modify the source code of the program being compiled. Such use cases will require external source-generating tools, which don't often integrate cleanly with other tooling. +Expression macros are one part of the [vision for macros in Swift](https://github.com/apple/swift-evolution/pull/1927), which lays out general motivation for introducing macros into the language. Expressions in particular are an area where the language already provides decent abstractions for factoring out runtime behavior, because one can create a function that you call as an expression from anywhere. However, with a few hard-coded exceptions like `#file` and `#line`, an expression cannot reason about or modify the source code of the program being compiled. Such use cases will require external source-generating tools, which don't often integrate cleanly with other tooling. ## Proposed solution @@ -36,7 +36,7 @@ and would be expanded into The type signature of a macro is part of its declaration, which looks a lot like a function: ```swift -@expression macro stringify(_: T) -> (T, String) +@freestanding(expression) macro stringify(_: T) -> (T, String) ``` ### Type-checked macro arguments and results @@ -86,12 +86,13 @@ Macro definitions operate on syntax trees. Broadly speaking, there are two diffe We propose the latter approach, where a macro definition is a separate program that operates on Swift syntax trees using the [swift-syntax](https://github.com/apple/swift-syntax/) package. Expression macros are defined as types that conform to the `ExpressionMacro ` protocol: ```swift -public protocol ExpressionMacro: Macro { - /// Expand a macro described by the given macro expansion expression +public protocol ExpressionMacro: FreestandingMacro { + /// Expand a macro described by the given freestanding macro expansion /// within the given context to produce a replacement expression. static func expansion( - of node: MacroExpansionExprSyntax, in context: MacroExpansionContext - ) throws -> ExprSyntax + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) async throws -> ExprSyntax } ``` @@ -110,7 +111,8 @@ import _SwiftSyntaxMacros public struct StringifyMacro: ExpressionMacro { public static func expansion( - of node: MacroExpansionExprSyntax, in context: MacroExpansionContext + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext ) -> ExprSyntax { guard let argument = node.argumentList.first?.expression else { fatalError("compiler bug: the macro does not have any arguments") @@ -126,7 +128,8 @@ The `expansion(of:in:)` function is fairly small, because the `stringify` macro The `StringifyMacro` struct is the implementation for the `stringify` macro declared earlier. We will need to tie these together in the source code via some mechanism. We propose to provide a builtin macro that names the module and the `ExpressionMacro` type name within the macro declaration following an `=`, e.g., ```swift -@expression macro stringify(_: T) -> (T, String) = +@freestanding(expression) +macro stringify(_: T) -> (T, String) = #externalMacro(module: "ExampleMacros", type: "StringifyMacro") ``` @@ -146,23 +149,31 @@ macro-declaration -> macro-head identifier generic-parameter-clause[opt] macro-s macro-head -> attributes[opt] declaration-modifiers[opt] 'macro' macro-signature -> parameter-clause macro-function-signature-result[opt] -macro-signature -> ':' type macro-function-signature-result -> '->' type macro-definition -> '=' expression ``` -The signature of a macro is either function-like (`(_ argument: T) -> (T, String)`) or value-like (`: Int`), depending on the form of the `macro-signature`. The `@expression` attribute applies only to macros. It indicates that the macro is an expression macro. +The `@freestanding(expression)` attribute applies only to macros. It indicates that the macro is an expression macro. The "freestanding" terminology comes from the [macros vision document](https://github.com/apple/swift-evolution/pull/1927), and is used to describe macros that are expanded with the leading `#` syntax. + +Macro signatures are function-like, with a parameter clause (that may be empty) and an optional result type. Macros can only be declared at file scope. They can be overloaded in the same way as functions, so long as the argument labels, parameter types, or result type differ. The `macro-definition` provides the implementation used to expand the macro. It is parsed as a general expression, but must always be a `macro-expansion-expression`, so all non-builtin macros are defined in terms of other macros, terminating in a builtin macro whose definition is provided by the compiler. The arguments provided within the `macro-expansion-expression` of the macro definition must either be direct references to the parameters of the enclosing macro or must be literals. The `macro-expansion-expression` is type-checked (to ensure that the argument and result types make sense), but no expansion is performed at the time of definition. Rather, expansion of the macro referenced by the `macro-definition` occurs when the macro being declared is expanded. See the following section on macro expansion for more information. -Macro result types cannot include opaque result types. - Macro parameters may have default arguments, but those default arguments can only consist of literal expressions and other macro expansions. +Macros can have opaque result types. The rules for uniqueness of opaque result types for macros are somewhat different from opaque result types of functions, because each macro expansion can easily produce a different type. Therefore, each macro expansion producing an opaque result type will be considered to have a distinct type, e.g., the following is ill-formed: + +```swift +@freestanding(expression) macro someMacroWithOpaqueResult() -> some Collection + +var a = #someMacroWithOpaqueResult +a = #someMacroWithOpaqueResult // cannot assign value with type of macro expansion here to opaque type from macro expansion above +``` + ### Macro expansion A macro expansion expression is described by the following grammar: @@ -172,18 +183,18 @@ primary-expression -> macro-expansion-expression macro-expansion-expression -> '#' identifier generic-argument-clause[opt] function-call-argument-clause[opt] trailing-closures[opt] ``` -When either a `function-call-argument-clause` or a `trailing-closures` term is present, the identifier must refer to a function-like macro. When neither is present, the identifier must refer to a value-like macro. There is no such thing as a value of macro type. +The `#` syntax for macro expansion expressions was specifically chosen because Swift already contains a number of a `#`-prefixed expressions that are macro-like in nature, some of which could be implemented directly as expression macros. The macro referenced by the `identifier` must be an an expression macro, as indicated by `@freestanding(expression)` on the corresponding macro declaration. -The `#` syntax for macro expansion expressions was specifically chosen because Swift already contains a number of a `#`-prefixed expressions that are macro-like in nature, some of which could be implemented directly as expression macros. The macro referenced by the `identifier` must be an an expression macro, as indicated by `@expression` on the corresponding macro declaration. +Both `function-call-argument-clause` and `trailing-closures` are optional. When both are omitted, the macro is expanded as-if the empty argument list `()` were provided. Macros are not first-class entities in the way functions are, so they cannot be passed around as values and do not need an "unapplied macro" syntax. This allows `#line` et al to be macros without requiring them to be written as `#line()`. There is some precedent for this with property wrappers, which will also be used for attached macros. When a macro expansion is encountered in the source code, it's expansion occurs in two phases. The first phase is the type-check phase, where the arguments to the macro are type-checked against the parameters of the named macro, and the result type of the named macro is checked against the context in which the macro expansion occurs. This type-checking is equivalent to that performed for a function call (for function-like macros) or a property reference (for value-like macros), and does not involve the macro definition. The second phase is the macro expansion phase, during which the syntax of the macro arguments is provided to the macro definition. For builtin-macro definitions, the behavior at this point depends on the semantics of the macro, e.g., the `externalMacro` macro invokes the external program and provides it with the source code of the macro expansion. For other macros, the arguments are substituted into the `macro-expansion-expression` of the definition. For example: ```swift -@expression macro prohibitBinaryOperators(_ value: T, operators: [String]) -> T = +@freestanding(expression) macro prohibitBinaryOperators(_ value: T, operators: [String]) -> T = #externalMacro(module: "ExampleMacros", type: "ProhibitBinaryOperators") -@expression macro addBlocker(_ value: T) -> T = #prohibitBinaryOperators(value, operators: ["+"]) +@freestanding(expression) macro addBlocker(_ value: T) -> T = #prohibitBinaryOperators(value, operators: ["+"]) #addBlocker(x + y * z) ``` @@ -210,6 +221,8 @@ From an implementation perspective, the compiler reserves the right to avoid per Macro expansion cannot be recursive: if the expansion of a given macro produces source code that expands that same macro, the program is ill-formed. This prevents unbounded macro expansion. +With the exception of the built-in macro declarations for source locations (e.g., `#fileID`, `#line`), a macro cannot be used as the default argument of a parameter. The existing features for source locations have special behavior when they appear as a default argument, wherein they are expanded by the caller using the source-location information at the call site rather than in the function declaration where they appear. This is useful, existing behavior that we cannot change, but it might not make sense for all macros, and could be surprising. Therefore, we prohibit such default argument that are (non-built-in) macros to avoid confusion, and are open to revisiting this restriction in the future. + ### Macro implementation library Macro definitions will make use of the [swift-syntax](https://github.com/apple/swift-syntax) package, which provides the Swift syntax tree manipulation and parsing capabilities for Swift tools. The `SwiftSyntaxMacros` module will provide the functionality required to define macros. @@ -222,57 +235,125 @@ The `Macro` protocol is the root protocol for all kinds of macro definitions. At public protocol Macro { } ``` -The `ExpressionMacro` protocol is used to describe expression macros: +All "freestanding" macros conform to the `FreestandingMacro` protocol: + +```swift +public protocol FreestandingMacro: Macro { } +``` + +The `ExpressionMacro` protocol is used to describe expression macros, and is a form of freestanding macro: ```swift -public protocol ExpressionMacro: Macro { - /// Expand a macro described by the given macro expansion expression +public protocol ExpressionMacro: FreestandingMacro { + /// Expand a macro described by the given freestanding macro expansion syntax node /// within the given context to produce a replacement expression. static func expansion( - of macro: MacroExpansionExprSyntax, in context: MacroExpansionContext - ) throws -> ExprSyntax + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) async throws -> ExprSyntax } ``` -The `MacroExpansionExprSyntax` type is the `swift-syntax` node describing the `macro-expansion-expression` grammar term from above, so it carries the complete syntax tree (including all whitespace and comments) of the macro expansion as it appears in the source code. +The `FreestandingMacroExpansionSyntax` protocol is the `swift-syntax` node describing the `macro-expansion-expression` grammar term from above, so it carries the complete syntax tree (including all whitespace and comments) of the macro expansion as it appears in the source code. Macro definitions should conform to the `ExpressionMacro` protocol and implement their syntactic transformation via `expansion(of:in:)`, returning the new expression as a syntax node. If the macro expansion cannot proceed for some reason, the `expansion(of:in:)` operation can throw an error rather than try to produce a new syntax node. The compiler will then report the error to the user. More detailed diagnostics can be provided via the macro expansion context. +The macro expansion operation is asynchronous, to account for potentially-asynchronous operations that will eventually be added to `MacroExpansionContext`. For example, operations that require additional communication with the compiler to get types of subexpressions, access files in the program, and so on. + #### `MacroExpansionContext` The macro expansion context provides additional information about the environment in which the macro is being expanded. This context can be queried as part of the macro expansion: ```swift -/// System-supplied class that provides information about the context in +/// Protocol whose conforming types provide information about the context in /// which a given macro is being expanded. -public class MacroExpansionContext { - /// The name of the module in which the macro is being expanded. - public let moduleName: String - - /// The name of the file in which the macro is being expanded, without - /// any additional path information. - public let fileName: String - - /// Create a new macro expansion context. - public init(moduleName: String, fileName: String) - +public protocol MacroExpansionContext: AnyObject { /// Generate a unique name for use in the macro. - public func createUniqueName() -> TokenSyntax + public func createUniqueName(_ name: String) -> TokenSyntax /// Emit a diagnostic (i.e., warning or error) that indicates a problem with the macro /// expansion. public func diagnose(_ diagnostic: Diagnostic) + + /// Retrieve a source location for the given syntax node. + /// + /// - Parameters: + /// - node: The syntax node whose source location to produce. + /// - position: The position within the syntax node for the resulting + /// location. + /// - filePathMode: How the file name contained in the source location is + /// formed. + /// + /// - Returns: the source location within the given node, or `nil` if the + /// given syntax node is not rooted in a source file that the macro + /// expansion context knows about. + func location( + of node: some SyntaxProtocol, + at position: PositionInSyntaxNode, + filePathMode: SourceLocationFilePathMode + ) -> AbstractSourceLocation? } ``` -The `createUniqueName()` function allows one to create new, unique names so that the macro expansion can produce new declarations that won't conflict with any other declarations in the same scope. It produces an identifier token containing the unique name. This allows macros to be more hygienic, by not introducing new names that could affect the way that the code provided via macro expansion arguments is type-checked. +The `createUniqueName()` function allows one to create new, unique names so that the macro expansion can produce new declarations that won't conflict with any other declarations in the same scope. It produces an identifier token containing the unique name, which will also incorporate the `name` identifier for better debuggability. This allows macros to be more hygienic, by not introducing new names that could affect the way that the code provided via macro expansion arguments is type-checked. It is intended that `MacroExpansionContext` will grow over time to include more information about the build environment in which the macro is being expanded. For example, information about the target platform (such as OS, architecture, and deployment version) and any compile-time definitions passed via `-D`, should be included as part of the context. The `diagnose` method allows a macro implementation to provide diagnostics that as part of macro expansion. The [`Diagnostic`](https://github.com/apple/swift-syntax/blob/main/Sources/SwiftDiagnostics/Diagnostic.swift) type used in the parameter is part of the swift-syntax library, and its form is likely to change over time, but it is able to express the different kinds of diagnostics a compiler or other tool might produce, such as warnings and errors, along with range highlights, Fix-Its, and attached notes to provide more clarity. A macro definition can introduce diagnostics if, for example, the macro argument successfully type-checked but used some Swift syntax that the macro implementation does not understand. The diagnostics will be presented by whatever tool is expanding the macro, such as the compiler. A macro that emits diagnostics is still expected to produce an expansion result unless it also throws an error, in which case both emitted diagnostics and the error will be reported. +The `location` operation allows one to determine source location information for a syntax node. The resulting source location contains the file, line, and column for the corresponding syntax node. The `position` and `filePathMode` can be used to customize the resulting output, e.g., which part of the syntax node to point at and how to render the file name. + +```swift +/// Describe the position within a syntax node that can be used to compute +/// source locations. +public enum PositionInSyntaxNode { + /// Refers to the start of the syntax node's leading trivia, which is + /// the first source location covered by the syntax node. + case beforeLeadingTrivia + + /// Refers to the start of the syntax node's first token, which + /// immediately follows the leading trivia. + case afterLeadingTrivia + + /// Refers to the end of the syntax node's last token, right before the + /// trailing trivia. + case beforeTrailingTrivia + + /// Refers just past the end of the source text that is covered by the + /// syntax node, after all trailing trivia. + case afterTrailingTrivia +} + +/// Describes how a source location file path will be formed. +public enum SourceLocationFilePathMode { + /// A file ID consisting of the module name and file name (without full path), + /// as would be generated by the macro expansion `#fileID`. + case fileID + + /// A full path name as would be generated by the macro expansion `#filePath`, + /// e.g., `/home/taylor/alison.swift`. + case filePath +} +``` + +Source locations are described in an abstract form that can be interpolated into source code (they are expressions) in places that expect a string literal (for the file name) or integer literal (for line and column). As with `createUniqueName` returning a `TokenSyntax` rather than a `String`, this abstraction allows the compiler to introduce a different kind of syntax node (that might not even be expressible in normal Swift) to represent these values. + +```swift +/// Abstractly represents a source location in the macro. +public struct AbstractSourceLocation { + /// A primary expression that represents the file and is `ExpressibleByStringLiteral`. + public let file: ExprSyntax + + /// A primary expression that represents the line and is `ExpressibleByIntegerLiteral`. + public let line: ExprSyntax + + /// A primary expression that represents the column and is `ExpressibleByIntegerLiteral`. + public let column: ExprSyntax +} +``` + ### Macros in the Standard Library #### `externalMacro` definition @@ -283,7 +364,7 @@ The builtin `externalMacro` macro is declared as follows: macro externalMacro(module: String, type: String) -> T ``` -The arguments identify the module name and type name of the type that provides an external macro definition. Note that the `externalMacro` macro is special in that it can only be expanded to define another macro. It is an error to use it anywhere else, which is why it does not include an `@expression` attribute. +The arguments identify the module name and type name of the type that provides an external macro definition. Note that the `externalMacro` macro is special in that it can only be expanded to define another macro. It is an error to use it anywhere else, which is why it does not include an `@freestanding(expression)` attribute. #### Builtin macro declarations @@ -295,22 +376,24 @@ We propose to introduce a number of macro declarations into the Swift standard l ```swift // File and path-related information -@expression macro fileID: T -@expression macro file: T -@expression macro filePath: T +@freestanding(expression) macro fileID() -> T +@freestanding(expression) macro file() -> T +@freestanding(expression) macro filePath() -> T // Current function -@expression macro function: T +@freestanding(expression) macro function() -> T // Source-location information -@expression macro line: T -@expression macro column: T +@freestanding(expression) macro line() -> T +@freestanding(expression) macro column() -> T // Current shared object handle. -@expression macro dsohandle: UnsafeRawPointer +@freestanding(expression) macro dsohandle() -> UnsafeRawPointer ``` -With the exception of `#fileID` (and `#file` when [SE-0274](https://github.com/apple/swift-evolution/blob/main/proposals/0274-magic-file.md) is enabled), the operations that provide information about the current location in source code are not implementable as `ExpressionMacro`-conforming types, because specific source-location information is intentionally unavailable to macro definitions. The type signatures of these macros capture most of the type system behavior of the existing `#file`, `#line`, etc., because they are treated like literals and therefore can pick up any contextual type that implements the proper `ExpressiblyBy*` protocol. However, the implementations above would fail to type-check code like this: +The operations that provide information about the current location in source code are mostly implementable as `ExpressionMacro`-conforming types, using the `location` operation on the `MacroExpansionContext`. The exceptions are `#file`, which would need an extension to `MacroExpansionContext` to determine whether we are in a compilation mode where `#file` behaves like `#fileID` vs. behaving like [`#filePath`](https://github.com/apple/swift-evolution/blob/main/proposals/0285-ease-pound-file-transition.md); `dsohandle`, which requires specific compiler support; and `#function`, which would require contextual information that is not available in the `MacroExpansionContext`. + +The type signatures of these macros capture most of the type system behavior of the existing `#file`, `#line`, etc., because they are treated like literals and therefore can pick up any contextual type that implements the proper `ExpressibleBy*` protocol. However, the implementations above would fail to type-check code like this: ```swift let x = #file @@ -329,10 +412,10 @@ To match the existing behavior of the built-in `#file`, `#line`, etc. would requ The Swift `#selector` and `#keyPath` expressions can have their syntax and type-checking behavior expressed in terms of macro declarations: ```swift -@expression macro selector(_ method: T) -> Selector -@expression macro selector(getter property: T) -> Selector -@expression macro selector(setter property: T) -> Selector -@expression macro keyPath(property: T) -> String +@freestanding(expression) macro selector(_ method: T) -> Selector +@freestanding(expression) macro selector(getter property: T) -> Selector +@freestanding(expression) macro selector(setter property: T) -> Selector +@freestanding(expression) macro keyPath(_ property: T) -> String ``` These macros cannot be implemented in terms of `ExpressionMacro` based on the facilities in this proposal, because one would need to determine which declarations are referenced within the argument of a macro expansion such as `#selector(getter: Person.name)`. However, providing them with macro declarations that have built-in implementations makes them less special, removing some special cases from more of the language. @@ -340,9 +423,9 @@ These macros cannot be implemented in terms of `ExpressionMacro` based on the fa ##### Object literals ```swift -@expression macro colorLiteral(red: Float, green: Float, blue: Float, alpha: Float) -> T -@expression macro imageLiteral(resourceName: String) -> T -@expression macro fileLiteral(resourceName: String) -> T +@freestanding(expression) macro colorLiteral(red: Float, green: Float, blue: Float, alpha: Float) -> T +@freestanding(expression) macro imageLiteral(resourceName: String) -> T +@freestanding(expression) macro fileLiteral(resourceName: String) -> T ``` The object literals allow one to reference a resource in a program of various kinds. The three kinds of object literals (color, image, and file) can be described as expression macros. The type signatures provided above are not exactly how type checking currently works for object literals, because they aren't necessarily generic. Rather, when they are used, the compiler currently looks for a specially-named type (e.g., `_ColorLiteralType`) in the current module and uses that as the type of the corresponding color literal. To maintain that behavior, we propose to type-check macro expansions for object literals by performing the same lookup that is done today (e.g., for `_ColorLiteralType`) and then using that type as the generic argument for the corresponding macro. That way, the type checking behavior is unchanged when moving from special object literal expressions in the language to macro declarations with built-in implementations. @@ -367,7 +450,7 @@ There are many uses for expression macros beyond what has presented here. This s ```swift // Declaration of #colorLiteral - @expression macro colorLiteral(red: Float, green: Float, blue: Float, alpha: Float) -> _ColorLiteralType + @freestanding(expression) macro colorLiteral(red: Float, green: Float, blue: Float, alpha: Float) -> _ColorLiteralType = SwiftBuiltinMacros.ColorLiteralMacro // Implementation of #colorLiteral @@ -386,8 +469,9 @@ There are many uses for expression macros beyond what has presented here. This s } static func expansion( - of node: MacroExpansionExprSyntax, in context: MacroExpansionContext - ) -> MacroResult { + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { let argList = replaceFirstLabel( of: node.argumentList, with: "_colorLiteralRed" ) @@ -427,20 +511,6 @@ Macros are a source-to-source transformation tool that have no ABI impact. Macros are a source-to-source transformation tool that have no effect on API resilience. -## Alternatives considered - -### Asynchronous macro expansion - -The `expansion(of:in:)` operation for an expression macro could be marked as `async`, e.g., - -```swift - static func expansion( - of macro: MacroExpansionExprSyntax, in context: MacroExpansionContext - ) async throws -> ExprSyntax -``` - -to allow it to perform asynchronous computations as part of macro expansion. This could be useful if certain macros were provided with access to asynchronous I/O or required some kind of non-blocking communication with a separate compiler process. - ## Future Directions There are a number of potential directions one could take macros, both by providing additional information to the macro implementations themselves and expanding the scope of macros. @@ -498,11 +568,16 @@ Expressions are just one place in the language where macros could be valuable. O ## Revision History * Revisions based on review feedback: - * Make `MacroExpansionContext` a class, because the state involving diagnostics and unique names needs to be shared. + * Switch `@expression` to `@freestanding(expression)` to align with the other macros proposals and vision document. + * Make the `ExpressionMacro.expansion(of:in:)` requirement `async`. + * Allow macro declarations to have opaque result types, and define the uniqueness rules. + * Simplify the grammar of macro declarations to be more function-like: they always require a parameter list, and if they have a return value, its type is specified following `->`. To account for macros that take no arguments, omitting both an argument list and trailing closures from a macro expansion expression will implicitly add `()`. + * Make `MacroExpansionContext` a class-bound protocol, because the state involving diagnostics and unique names needs to be shared, and the implementations could vary significantly between (e.g.) the compiler and a test harness. + * Introduce a general `location` operation on `MacroExpansionContext` to get the source location of any syntax node from a macro input. Remove the `moduleName` and `fileName`, which were always too limited to be useful. * Allow macro parameters to have default arguments, with restrictions on what can occur within a default argument. * Clarify that macro expansion cannot be recursive. - * Rename `createUniqueLocalName` to `createUniqueName`; the names might not always be local in scope. - + * Rename `createUniqueLocalName` to `createUniqueName`; the names might not always be local in scope. Also add a parameter to it so developers can provide a partial name that will show up in the unique name. + * Prohibit the use of non-builtin macros as default arguments of parameters. * Revisions from the second pitch: * Moved SwiftPM manifest changes to a separate proposal that can explore the building of macros in depth. This proposal will focus only on the language aspects. * Simplified the type signature of the `#externalMacro` built-in macro. From a29a74dbb053b75531c5d365af38dc853e5364f4 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Fri, 10 Feb 2023 11:27:51 -0500 Subject: [PATCH 2977/4563] Update SE-0382 for dates of and link to review 2 (#1946) --- proposals/0382-expression-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index 94a4d02f65..84484ea352 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -3,9 +3,9 @@ * Proposal: [SE-0382](0382-expression-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Returned for revision** +* Status: **Active review (February 10...20, 2023)** * Implementation: Partial implementation is available in `main` under the experimental feature flag `Macros`. An [example macro repository](https://github.com/DougGregor/swift-macro-examples) provides a way to experiment with this feature. -* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861/16)) ([review #1](https://forums.swift.org/t/se-0382-expression-macros/62090)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0382-expression-macros/62898)) ([pitch #3](https://forums.swift.org/t/se-0382-expression-macros-mini-pitch-for-updates/62810)) +* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861/16)) ([review #1](https://forums.swift.org/t/se-0382-expression-macros/62090)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0382-expression-macros/62898)) ([pitch #3](https://forums.swift.org/t/se-0382-expression-macros-mini-pitch-for-updates/62810)) ([review #2](https://forums.swift.org/t/se-0382-second-review-expression-macros/63064)) ## Introduction From fae668207d12d2fafde733284b84e4fde6cbd0e2 Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Fri, 10 Feb 2023 18:38:58 +0100 Subject: [PATCH 2978/4563] [SE-0382] Fix link (#1943) The link to the second pitch thread should point to the top of the thread, not to a specific post. Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- proposals/0382-expression-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index 84484ea352..492bf08d98 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -5,7 +5,7 @@ * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Active review (February 10...20, 2023)** * Implementation: Partial implementation is available in `main` under the experimental feature flag `Macros`. An [example macro repository](https://github.com/DougGregor/swift-macro-examples) provides a way to experiment with this feature. -* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861/16)) ([review #1](https://forums.swift.org/t/se-0382-expression-macros/62090)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0382-expression-macros/62898)) ([pitch #3](https://forums.swift.org/t/se-0382-expression-macros-mini-pitch-for-updates/62810)) ([review #2](https://forums.swift.org/t/se-0382-second-review-expression-macros/63064)) +* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861)) ([review #1](https://forums.swift.org/t/se-0382-expression-macros/62090)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0382-expression-macros/62898)) ([pitch #3](https://forums.swift.org/t/se-0382-expression-macros-mini-pitch-for-updates/62810)) ([review #2](https://forums.swift.org/t/se-0382-second-review-expression-macros/63064)) ## Introduction From 5dc0913b913aa4f5d708804ea5bf4a673a0b9632 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 14 Feb 2023 17:43:19 +0000 Subject: [PATCH 2979/4563] Update 0382-expression-macros.md (#1947) --- proposals/0382-expression-macros.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index 492bf08d98..94027f4352 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -80,10 +80,10 @@ The proposed macro design attempts to mitigate these problems, but they are some Macro definitions operate on syntax trees. Broadly speaking, there are two different ways in which a macro's expansion operation can be defined: -* *A declarative set of transformations*: this involves extending the language with a special syntax that allows macros to define how the macro is expanded given the macro inputs, and the compiler applies those rules for each macro expansion. The C preprocessor employs a simplistic form of this, but Racket's [pattern-based macros](https://docs.racket-lang.org/guide/pattern-macros.html) and Rust's [declarative macros](https://doc.rust-lang.org/book/ch19-06-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming) offer more advanced rules that match the macro arguments to a pattern and then perform a a rewrite to new syntax as described in the macro. For Swift to adopt this approach, we would likely need to invent a pattern language for matching and rewriting syntax trees. +* *A declarative set of transformations*: this involves extending the language with a special syntax that allows macros to define how the macro is expanded given the macro inputs, and the compiler applies those rules for each macro expansion. The C preprocessor employs a simplistic form of this, but Racket's [pattern-based macros](https://docs.racket-lang.org/guide/pattern-macros.html) and Rust's [declarative macros](https://doc.rust-lang.org/book/ch19-06-macros.html#declarative-macros-with-macro_rules-for-general-metaprogramming) offer more advanced rules that match the macro arguments to a pattern and then perform a rewrite to new syntax as described in the macro. For Swift to adopt this approach, we would likely need to invent a pattern language for matching and rewriting syntax trees. * *An executable program that transforms the source*: this involves running a program that manipulates the program syntax directly. How the program is executed depends a lot on the environment: [Scala 3 macros](https://docs.scala-lang.org/scala3/guides/macros/macros.html) benefit from the use of the JVM so they can intertwine target code (the program being generated) and host code (where the compiler is running), whereas Rust's [procedural macros](https://doc.rust-lang.org/reference/procedural-macros.html) are built into a separate crate that the compiler interacts with. For Swift to adopt this approach, we would either need to build a complete interpreter for Swift code in the compiler, or take an approach similar to Rust's and build macro definitions as separate programs that the compiler can interact with. -We propose the latter approach, where a macro definition is a separate program that operates on Swift syntax trees using the [swift-syntax](https://github.com/apple/swift-syntax/) package. Expression macros are defined as types that conform to the `ExpressionMacro ` protocol: +We propose the latter approach, where a macro definition is a separate program that operates on Swift syntax trees using the [swift-syntax](https://github.com/apple/swift-syntax/) package. Expression macros are defined as types that conform to the `ExpressionMacro` protocol: ```swift public protocol ExpressionMacro: FreestandingMacro { @@ -187,7 +187,7 @@ The `#` syntax for macro expansion expressions was specifically chosen because S Both `function-call-argument-clause` and `trailing-closures` are optional. When both are omitted, the macro is expanded as-if the empty argument list `()` were provided. Macros are not first-class entities in the way functions are, so they cannot be passed around as values and do not need an "unapplied macro" syntax. This allows `#line` et al to be macros without requiring them to be written as `#line()`. There is some precedent for this with property wrappers, which will also be used for attached macros. -When a macro expansion is encountered in the source code, it's expansion occurs in two phases. The first phase is the type-check phase, where the arguments to the macro are type-checked against the parameters of the named macro, and the result type of the named macro is checked against the context in which the macro expansion occurs. This type-checking is equivalent to that performed for a function call (for function-like macros) or a property reference (for value-like macros), and does not involve the macro definition. +When a macro expansion is encountered in the source code, it's expansion occurs in two phases. The first phase is the type-check phase, where the arguments to the macro are type-checked against the parameters of the named macro, and the result type of the named macro is checked against the context in which the macro expansion occurs. This type-checking is equivalent to that performed for a function call, and does not involve the macro definition. The second phase is the macro expansion phase, during which the syntax of the macro arguments is provided to the macro definition. For builtin-macro definitions, the behavior at this point depends on the semantics of the macro, e.g., the `externalMacro` macro invokes the external program and provides it with the source code of the macro expansion. For other macros, the arguments are substituted into the `macro-expansion-expression` of the definition. For example: @@ -210,7 +210,7 @@ let _: (Int, String) = Macro expansion expressions can occur within the arguments to a macro. For example, consider: ```swift -#addBlocker(#stringify(x + 1)) +#addBlocker(#stringify(1 + 2)) ``` The first phase of the macro type-check does not perform any macro expansion: the macro expansion expression `#stringify(1 + 2)` will infer that it's `T` is `Int`, and will produce a value of type `(Int, String)`. The `addBlocker` macro expansion expression will infer that it's `T` is `(Int, String)`, and the result is the same. @@ -301,7 +301,7 @@ The `createUniqueName()` function allows one to create new, unique names so that It is intended that `MacroExpansionContext` will grow over time to include more information about the build environment in which the macro is being expanded. For example, information about the target platform (such as OS, architecture, and deployment version) and any compile-time definitions passed via `-D`, should be included as part of the context. -The `diagnose` method allows a macro implementation to provide diagnostics that as part of macro expansion. The [`Diagnostic`](https://github.com/apple/swift-syntax/blob/main/Sources/SwiftDiagnostics/Diagnostic.swift) type used in the parameter is part of the swift-syntax library, and its form is likely to change over time, but it is able to express the different kinds of diagnostics a compiler or other tool might produce, such as warnings and errors, along with range highlights, Fix-Its, and attached notes to provide more clarity. A macro definition can introduce diagnostics if, for example, the macro argument successfully type-checked but used some Swift syntax that the macro implementation does not understand. The diagnostics will be presented by whatever tool is expanding the macro, such as the compiler. A macro that emits diagnostics is still expected to produce an expansion result unless it also throws an error, in which case both emitted diagnostics and the error will be reported. +The `diagnose` method allows a macro implementation to provide diagnostics as part of macro expansion. The [`Diagnostic`](https://github.com/apple/swift-syntax/blob/main/Sources/SwiftDiagnostics/Diagnostic.swift) type used in the parameter is part of the swift-syntax library, and its form is likely to change over time, but it is able to express the different kinds of diagnostics a compiler or other tool might produce, such as warnings and errors, along with range highlights, Fix-Its, and attached notes to provide more clarity. A macro definition can introduce diagnostics if, for example, the macro argument successfully type-checked but used some Swift syntax that the macro implementation does not understand. The diagnostics will be presented by whatever tool is expanding the macro, such as the compiler. A macro that emits diagnostics is still expected to produce an expansion result unless it also throws an error, in which case both emitted diagnostics and the error will be reported. The `location` operation allows one to determine source location information for a syntax node. The resulting source location contains the file, line, and column for the corresponding syntax node. The `position` and `filePathMode` can be used to customize the resulting output, e.g., which part of the syntax node to point at and how to render the file name. @@ -368,7 +368,7 @@ The arguments identify the module name and type name of the type that provides a #### Builtin macro declarations -As previously noted, expression macros use the same leading `#` syntax as number of built-in expressions like `#line`. With the introduction of expression macros, we propose to subsume those built-in expressions into macros that come as part of the Swift standard library. The actual macro implementations are provided by the compiler, and may even involve things that aren't necessarily implementable with the pure syntactic macro. However, by providing macro declarations we remove special cases from the language and benefit from all of the tooling affordances provided for macros. +As previously noted, expression macros use the same leading `#` syntax as a number of built-in expressions like `#line`. With the introduction of expression macros, we propose to subsume those built-in expressions into macros that come as part of the Swift standard library. The actual macro implementations are provided by the compiler, and may even involve things that aren't necessarily implementable with the pure syntactic macro. However, by providing macro declarations we remove special cases from the language and benefit from all of the tooling affordances provided for macros. We propose to introduce a number of macro declarations into the Swift standard library. There are several different kinds of such macros. @@ -444,7 +444,7 @@ The fact that macro implementations are separate programs actually makes it easi ## Example expression macros -There are many uses for expression macros beyond what has presented here. This section will collect several examples of macro implementations based on existing built-in `#` expressions as well as ones that come from the Swift forums and other sources of inspiration. Prototype implementations of a number of these macros are [available in the swift-syntax repository](https://github.com/apple/swift-syntax/blob/main/Sources/_SwiftSyntaxMacros/MacroSystem%2BBuiltin.swift) +There are many uses for expression macros beyond what has been presented here. This section will collect several examples of macro implementations based on existing built-in `#` expressions as well as ones that come from the Swift forums and other sources of inspiration. Prototype implementations of a number of these macros are [available in the swift-syntax repository](https://github.com/apple/swift-syntax/blob/main/Tests/SwiftSyntaxMacrosTest/MacroSystemTests.swift). * The `#colorLiteral` macro provides syntax for declaring a color with a given red, green, blue, and alpha values. It can be declared and implemented as follows @@ -523,7 +523,7 @@ The arguments to a macro are fully type-checked before the macro implementation #assert(Color(parsing: "red") == .red) ``` -The implementation would likely want to separate the two operands to `==` into local variables (with fresh names generated by `createUniqueLocalName`) to capture the values, so they can be printed later. For example, the assertion could be translated into code like the following: +The implementation would likely want to separate the two operands to `==` into local variables (with fresh names generated by `createUniqueName`) to capture the values, so they can be printed later. For example, the assertion could be translated into code like the following: ```swift { @@ -559,11 +559,11 @@ When given one of the expression syntax nodes that is part of the macro expansio Additional information could be provided about the actual resolved declarations. For example, the syntax node for `.red` could be queried to produce a full declaration name `Color.red`, and the syntax node for `==` could resolve to the full name of the declaration of the `==` operator that compares two `Color` values. A macro could then distinguish between different `==` operator implementations. -The main complexity of this future direction is in defining the APIs to be used by macro implementations to describe the Swift type system and related information. It would likely be a simplified form of a type checker's internal representation of types, but would need to maintain stable. Therefore, while we feel that the addition of type information is a highly valuable extension for expression macros, the scope of the addition means it would best be introduced as a follow-on proposal. +The main complexity of this future direction is in defining the APIs to be used by macro implementations to describe the Swift type system and related information. It would likely be a simplified form of a type checker's internal representation of types, but would need to remain stable. Therefore, while we feel that the addition of type information is a highly valuable extension for expression macros, the scope of the addition means it would best be introduced as a follow-on proposal. ### Additional kinds of macros -Expressions are just one place in the language where macros could be valuable. Other places could include function or closure bodies (e.g., to add tracing or logging), within type or extension definitions (e.g., to add new member), or on protocol conformances (e.g., to synthesize a protocol conformance). A number of potential ideas are presented in the [vision for macros in Swift](https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900). For each of them, we assume that the basic `macro` declaration will stay roughly the same, but the contexts in which the macro can be used would be different, as might the spelling of the expansion (e.g., `@` might be more appropriate if the macro expansion occurs on a declaration), there would be an attribute on the `macro` declaration that indicates what type of macro it is, and there would be a corresponding protocol that inherits from `Macro` in the `SwiftSyntaxMacros` module. +Expressions are just one place in the language where macros could be valuable. Other places could include function or closure bodies (e.g., to add tracing or logging), within type or extension definitions (e.g., to add new members), or on protocol conformances (e.g., to synthesize a protocol conformance). A number of potential ideas are presented in the [vision for macros in Swift](https://forums.swift.org/t/a-possible-vision-for-macros-in-swift/60900). For each of them, we assume that the basic `macro` declaration will stay roughly the same, but the contexts in which the macro can be used would be different, as might the spelling of the expansion (e.g., `@` might be more appropriate if the macro expansion occurs on a declaration), there would be an attribute on the `macro` declaration that indicates what type of macro it is, and there would be a corresponding protocol that inherits from `Macro` in the `SwiftSyntaxMacros` module. ## Revision History From 17074be5dc607749f66dcc96eeb146ecc27a91b9 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 14 Feb 2023 13:21:50 -0800 Subject: [PATCH 2980/4563] Add conformance macros --- proposals/nnnn-attached-macros.md | 61 ++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-attached-macros.md b/proposals/nnnn-attached-macros.md index 8c4f3674b7..2ea33fa917 100644 --- a/proposals/nnnn-attached-macros.md +++ b/proposals/nnnn-attached-macros.md @@ -6,6 +6,7 @@ * Status: **Pending review** * Implementation: Partially implemented on GitHub `main` behind the experimental flag `Macros`. * Review: +* Pitch threads: [Pitch #1 under the name "declaration macros"](https://forums.swift.org/t/pitch-declaration-macros/62373), [Pitch #2](https://forums.swift.org/t/pitch-attached-macros/62812) ## Introduction @@ -14,7 +15,7 @@ Attached macros provide a way to extend Swift by creating and extending declarat Attached macros are one part of the [vision for macros in Swift](https://github.com/apple/swift-evolution/pull/1927), which lays out general motivation for introducing macros into the language. They build on the ideas and motivation of [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) and [freestanding macros](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md) to cover a large new set of use cases; we will refer to those proposals often for the basic model of how macros integrate into the language. While freestanding macros are designed as standalone entities introduced by `#`, attached macros are associated with a specific declaration in the program that they can augment and extend. This supports many new use cases, greatly expanding the expressiveness of the macro system: * Creating trampoline or wrapper functions, such as automatically creating a completion-handler version of an `async` function. -* Creating members of a type based on its definition, such as forming an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset) from an enum containing flags. +* Creating members of a type based on its definition, such as forming an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset) from an enum containing flags and conforming it to the `OptionSet` type. * Creating accessors for a stored property or subscript, subsuming some of the behavior of [SE-0258 "Property Wrappers"](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md). * Augmenting members of a type with a new attribute, such as applying a property wrapper to all stored properties of a type. @@ -144,7 +145,7 @@ The macro itself will be declared as a member macro that defines an arbitrary se ```swift /// Create the necessary members to turn a struct into an option set. -@attached(member, names: names(rawValue), arbitrary]) macro optionSetMembers +@attached(member, names: names(rawValue), arbitrary]) macro optionSetMembers() ``` The `member` role specifies that this macro will be defining new members of the declaration to which it is attached. In this case, while the macro knows it will define a member named `rawValue`, there is no way for the macro to predict the names of the static properties it is defining, so it also specifies `arbitrary` to indicate that it will introduce members with arbitrarily-determined names. @@ -295,6 +296,56 @@ protocol MemberAttributeMacro: AttachedMacro { The `expansion` operation accepts the attribute syntax `node` for the spelling of the member attribute macro and the declaration to which that attribute is attached (i.e., the type or extension). The implementation of the macro can walk the members of the `declaration` to determine which members require additional attributes. The returned dictionary will map from those members to the additional attributes that should be added to each of the members. +#### Conformance macros + +Conformance macros allow one to introduce new protocol conformances to a type. This would often be paired with other macros whose purpose is to help satisfy the protocol conformance. For example, one could imagine an extended version of the `optionSetMembers` attributed shown earlier that also adds the `OptionSet` conformance. With it, the mimimal implementation of an option set could be: + +```swift +@OptionSet +struct MyOptions + enum Option: Int { + case a + case b + case c + } +} +``` + +where `OptionSet` is both a member and a conformance macro, providing members (as in `optionSetMembers`) and the conformance to `OptionSet`. The macro would be declared as, e.g., + +```swift +/// Create the necessary members to turn a struct into an option set. +@attached(member, names: names(rawValue), arbitrary]) +@attached(conformance) +macro OptionSet() +``` + +Conformance macro implementations should conform to the `ConformanceMacro` protocol: + +```swift +/// Describes a macro that can add conformances to the declaration it's +/// attached to. +public protocol ConformanceMacro: AttachedMacro { + /// Expand an attached conformance macro to produce a set of conformances. + /// + /// - Parameters: + /// - node: The custom attribute describing the attached macro. + /// - declaration: The declaration the macro attribute is attached to. + /// - context: The context in which to perform the macro expansion. + /// + /// - Returns: the set of `(type, where-clause?)` pairs that each provide the + /// protocol type to which the declared type conforms, along with + /// an optional where clause. + static func expansion( + of node: AttributeSyntax, + providingConformancesOf declaration: some DeclGroupSyntax, + in context: some MacroExpansionContext + ) throws -> [(TypeSyntax, WhereClauseSyntax?)] +} +``` + +The returned array contains the conformances. The `TypeSyntax` describes the protocol to which the enclosing type conforms, and the optional `where` clause provides any additional constraints that would make the conformance conditional. + ## Detailed design ### Composing macro roles @@ -476,6 +527,12 @@ An alternative approach would be to allow the macro implementation to directly a It might be possible to provide a macro implementation API that is expressed in terms of mutation on the original declaration, but restrict the permissible changes to those that can be handled by the implementation. For example, one could "diff" the syntax tree provided to the macro implementation and the syntax tree produced by the macro implementation to identify changes, and return those changes that are acceptable to the compiler. +## Revision History + +* After the first pitch: + * Added conformance macros, to produce conformances +* Originally pitched as "declaration macros"; attached macros were separated into their own proposal + ## Future directions ### Additional attached macro roles From 977681bd941ab5621c25a34eb899217dbe98671e Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Tue, 14 Feb 2023 15:45:18 -0800 Subject: [PATCH 2981/4563] Assign SE-0388 to AsyncStream.makeStream proposal --- ...async-stream-factory.md => 0388-async-stream-factory.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-async-stream-factory.md => 0388-async-stream-factory.md} (96%) diff --git a/proposals/NNNN-async-stream-factory.md b/proposals/0388-async-stream-factory.md similarity index 96% rename from proposals/NNNN-async-stream-factory.md rename to proposals/0388-async-stream-factory.md index af87664378..deed6e9402 100644 --- a/proposals/NNNN-async-stream-factory.md +++ b/proposals/0388-async-stream-factory.md @@ -1,11 +1,11 @@ # Convenience Async[Throwing]Stream.makeStream methods -* Proposal: [SE-NNNN](NNNN-async-stream-factory.md) +* Proposal: [SE-0388](0388-async-stream-factory.md) * Authors: [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) * Status: **Awaiting review** -* Pitch: [Convenience Async[Throwing]Stream.makeStream methods](https://forums.swift.org/t/pitch-convenience-async-throwing-stream-makestream-methods/61030) * Implementation: [apple/swift#62968](https://github.com/apple/swift/pull/62968) +* Review: ([pitch](https://forums.swift.org/t/pitch-convenience-async-throwing-stream-makestream-methods/61030)) ## Introduction @@ -219,4 +219,4 @@ users from misuse. ### Do nothing alternative We could just leave the current creation of `Async[Throwing]Stream` as is; however, since it is part of the standard library we should provide -a better method to create a stream and its continuation. \ No newline at end of file +a better method to create a stream and its continuation. From ac4a5d451ee4329bd7ae346984e997f8194c9f0a Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 15 Feb 2023 02:02:32 +0000 Subject: [PATCH 2982/4563] Update 0388-async-stream-factory.md (#1950) --- proposals/0388-async-stream-factory.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0388-async-stream-factory.md b/proposals/0388-async-stream-factory.md index deed6e9402..9a55c74a0b 100644 --- a/proposals/0388-async-stream-factory.md +++ b/proposals/0388-async-stream-factory.md @@ -30,7 +30,7 @@ an example usage of the current `AsyncStream` API looks like. ```swift var cont: AsyncStream.Continuation! let stream = AsyncStream { cont = $0 } -// We have to assing the continuation to a let to avoid sendability warnings +// We have to assign the continuation to a let to avoid sendability warnings let continuation = cont await withTaskGroup(of: Void.self) { group in @@ -200,7 +200,7 @@ extension AsyncStream { } ``` -### Expose an initilizer on the `NewStream` type +### Expose an initilizer on the `NewStream` type During the pitch it was brought up that we could expose an `init` on the `NewStream` types that this proposal wants to add. I decided against that since one would have to spell out `AsyncStream.NewStream()` to access the `init`. This is quite hard to discover in @@ -216,7 +216,7 @@ In the end, the `AsyncStream.Continuation` is deeply coupled to one instance of `AsyncStream` hence we should create an API that conveys this coupling and prevents users from misuse. -### Do nothing alternative +### Do nothing alternative We could just leave the current creation of `Async[Throwing]Stream` as is; however, since it is part of the standard library we should provide a better method to create a stream and its continuation. From cbb69526f7e4db1f8397e3034dc28484cba17994 Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Wed, 15 Feb 2023 08:59:20 +0000 Subject: [PATCH 2983/4563] Update stream factory proposal # Motivation There were a few small mistakes in the async stream proposal that @benrimmington pointed out --- proposals/0388-async-stream-factory.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/proposals/0388-async-stream-factory.md b/proposals/0388-async-stream-factory.md index 9a55c74a0b..a4debad21f 100644 --- a/proposals/0388-async-stream-factory.md +++ b/proposals/0388-async-stream-factory.md @@ -81,7 +81,7 @@ respectively. ```swift extension AsyncStream { - /// Struct for the return type of ``AsyncStream/makeStream(elementType:limit:)``. + /// Struct for the return type of ``AsyncStream/makeStream(of:bufferingPolicy:)``. /// /// This struct contains two properties: /// 1. The ``continuation`` which should be retained by the producer and is used @@ -119,7 +119,7 @@ extension AsyncStream { } extension AsyncThrowingStream { - /// Struct for the return type of ``AsyncThrowingStream/makeStream(elementType:limit:)``. + /// Struct for the return type of ``AsyncThrowingStream/makeStream(of:throwing:bufferingPolicy:)``. /// /// This struct contains two properties: /// 1. The ``continuation`` which should be retained by the producer and is used @@ -187,7 +187,6 @@ extension AsyncStream { /// - elementType: The element type of the stream. /// - limit: The buffering policy that the stream should use. /// - Returns: A tuple which contains the stream and its continuation. - @available(SwiftStdlib 5.8, *) public static func makeStream( of elementType: Element.Type = Element.self, bufferingPolicy limit: Continuation.BufferingPolicy = .unbounded From f76f42d57b066401cda23a9a8af6dcd1ec7eb1e9 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 10 Feb 2023 20:36:45 +0900 Subject: [PATCH 2984/4563] Initial draft of custom actor executors Co-authored-by: Yim Lee --- proposals/nnnn-custom-actor-executors.md | 570 +++++++++++++++++++++++ 1 file changed, 570 insertions(+) create mode 100644 proposals/nnnn-custom-actor-executors.md diff --git a/proposals/nnnn-custom-actor-executors.md b/proposals/nnnn-custom-actor-executors.md new file mode 100644 index 0000000000..b68b96f8c9 --- /dev/null +++ b/proposals/nnnn-custom-actor-executors.md @@ -0,0 +1,570 @@ +# Custom Actor Executors + +- Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/custom-actor-executors/proposals/NNNN-custom-actor-executors.md) +- Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [John McCall](https://github.com/rjmccall) +- Review Manager: TBD +- Status: **Partially implemented on `main`** +- Previous threads: + - Original pitch thread from around Swift 5.5: [Support custom executors in Swift Concurrency](https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425) + + +## Introduction + +As Swift Concurrency continues to mature it is becoming increasingly important to offer adopters tighter control over where exactly asynchronous work is actually executed. + +This proposal introduces a basic mechanism for customizing actor executors. By providing an instance of an executor, actors can influence "where" they will be executing any task they are running, while upholding the mutial excusion and actor isolation guaranteed by the actor model. + +> **Note:** This proposal defines only a set of APIs to customize actor executors, and other kinds of executor control is out of scope for this specific proposal. + +## Motivation + +Swift's concurrency design is intentionally vague about the details of how code is actually run. Most code does not rely on specific properties of the execution environment, such as being run to a specific operating system thread, and instead needs only high-level semantic properties, expressed in terms of actor isolation, such as that no other code will be accessing certain variables concurrently. Maintaining flexibility about how work is scheduled onto threads allows Swift to avoid certain performance pitfalls by default. + +Nonetheless, it is sometimes useful to more finely control how code is executed: + +- The code may need to cooperate with an existing system that expects to run code in a certain way. + + For example, the system might expect certain kinds of work to be scheduled in special ways, like how some platforms require UI code to be run on the main thread, or single-threaded event-loop based runtimes assume all calls will be made from the same thread that is owned and managed by the runtime itself. + + For another example, a project might have a large amount of existing code which protects some state with a shared queue. In principle, this is the actor pattern, and the code could be rewritten to use Swift's actor support. However, it may be impossible to do that, or at least impractical to do it immediately. Using the existing queue as the executor for an actor allows code to adopt actors more incrementally. + +- The code may depend on being run on a specific system thread. + + For example, some libraries maintain state in thread-local variables, and running code on the wrong thread will lead to broken assumptions in the library. + + For another example, not all execution environments are homogenous; some threads may be pinned to processors with extra capabilities. + +- The code's performance may benefit from the programmer being more explicit about where code should run. + + For example, if one actor frequently makes requests of another, and the actors rarely benefit from running concurrently, configuring them to use the same executor may decrease the runtime costs of switching between them. + + For another example, if an asynchronous function makes many calls to the same actor without any intervening suspensions, running the function explicitly on that actor's executor may eventually allow Swift to avoid a lot of switching overhead (or may even be necessary to perform those calls "atomically"). + +This is the first proposal discussing custom executors and customization points in the Swift Concurrency runtime, and while it introduces only the most basic customization points, we are certain that it already provides significant value to users seeking tighter control over their actor's execution semantics. + +## Proposed solution + +We propose to allow actors to declare the executor they are required to execute on by declaring a nonisolated `unownedExecutor` property with the executor of their choice. + +## Detailed design + +### A low-level design + +The API design of executors is intended to support high-performance implementations, with an expectation that custom executors will be primarily implemented by experts. Therefore, the following design heavily prioritizes the reliable elimination of abstraction costs over most other conceivable goals. In particular, the primitive operations specified by protocols are generally expressed in terms of opaque, unsafe types which implementations are required to use correctly. These operations are then used to implement more convenient APIs as well as the high-level language operations of Swift Concurrency. + +### Executors + +First, we introduce an `Executor` protocol, that serves as the parent protocol of all the specific kinds of executors we'll discuss next. It is the simplest kind of executor that does not provide any ordering guarantees about the submitted work. It could decide to run the submitted jobs in parallel, or sequentially. + +This protocol has existed in Swift ever since the introduction of Swift Concurrency, however, in this proposal we revise its API to make use of the newly introduced move-only capabilities in the language. The existing `UnownedJob` API will be deprecated in favor of one accepting a move-only `Job`. The `UnownedJob` type remains available (and equally unsafe), because today still some usage patterns are not supported by the initial revision of move-only types. + +The concurrency runtime uses the `enqueue(_:)` method of an executor to schedule some work onto given executor. + +```swift +/// A service that can execute jobs. +@available(SwiftStdlib 5.1, *) +public protocol Executor: AnyObject, Sendable { + + // This requirement is repeated here as a non-override so that we + // get a redundant witness-table entry for it. This allows us to + // avoid drilling down to the base conformance just for the basic + // work-scheduling operation. + @available(SwiftStdlib 5.9, *) + func enqueue(_ job: __owned Job) + + @available(SwiftStdlib 5.1, *) + @available(*, deprecated, message: "Implement the enqueue(Job) method instead") + func enqueue(_ job: UnownedJob) +} +``` + +In order to aid this transition, the compiler will offer assistance similar to how the transition from `Hashable.hashValue` to `Hashable.hash(into:)` was handled. Existing executor implementations which implemented `enqueue(UnownedJob)` will still work, but print a deprecation warning: + +```swift +final class MyOldExecutor: SerialExecutor { + // WARNING: 'Executor.enqueue(UnownedJob)' is deprecated as a protocol requirement; + // conform type 'MyOldExecutor' to 'Executor' by implementing 'enqueue(Job)' instead + func enqueue(_ job: UnownedJob) { + // ... + } +} +``` + +Executors are required to follow certain ordering rules when executing their jobs: + +- The call to `SerialExecutor.runJobSynchronously(_:)` must happen-after the call to `enqueue(_:)`. +- If the executor is a serial executor, then the execution of all jobs must be *totally ordered*: for any two different jobs *A* and *B* submitted to the same executor with `enqueue(_:)`, it must be true that either all events in *A* happen-before all events in *B* or all events in *B* happen-before all events in *A*. + - Do note that this allows the executor to reorder `A` and `B`–for example, if one job had a higher priority than the other–however they each independently must run to completion before the other one is allowed to run. + + +### Serial Executors + +We also define a `SerialExecutor` protocol, which is what actors use to guarantee their serial execution of tasks (jobs). + +```swift +/// A service that executes jobs one-by-one, and specifically, +/// guarantees mutual exclusion between job executions. +/// +/// A serial executor can be provided to an actor (or distributed actor), +/// to guarantee all work performed on that actor should be enqueued to this executor. +/// +/// Serial executors do not, in general, guarantee specific run-order of jobs, +/// and are free to re-order them e.g. using task priority, or any other mechanism. +@available(SwiftStdlib 5.1, *) +public protocol SerialExecutor: Executor { + /// Convert this executor value to the optimized form of borrowed + /// executor references. + @available(SwiftStdlib 5.1, *) + func asUnownedSerialExecutor() -> UnownedSerialExecutor +} + +@available(SwiftStdlib 5.9, *) +extension SerialExecutor { + // default implementation is sufficient for most implementations + func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(ordinary: self) + } +} +``` + +A `SerialExecutor` does not introduce new API, other than the wrapping itself in an `UnownedSerialExecutor` which is used by the Swift runtime to pass executors without incuring reference counting overhead. + +```swift +/// An unowned reference to a serial executor (a `SerialExecutor` +/// value). +/// +/// This is an optimized type used internally by the core scheduling +/// operations. It is an unowned reference to avoid unnecessary +/// reference-counting work even when working with actors abstractly. +/// Generally there are extra constraints imposed on core operations +/// in order to allow this. For example, keeping an actor alive must +/// also keep the actor's associated executor alive; if they are +/// different objects, the executor must be referenced strongly by the +/// actor. +@available(SwiftStdlib 5.1, *) +@frozen +public struct UnownedSerialExecutor: Sendable { + public init(ordinary executor: __shared E) +} +``` + +`SerialExecutors` will potentially be extended to support "switching" which can lessen the amount of thread switches incured when using custom executors. Please refer to the Future Directions for a discussion of this extension. + +### Jobs + +A `Job` is a representation of a chunk of of work that an executor should execute. For example, a `Task` effectively consists of a series of jobs that are enqueued onto executors, in order to run them. The name "job" was selected because we do not want to constrain this API to just "partial tasks", or tie them too closely to tasks, even though the most common type of job created by Swift concurrency are "partial tasks". + +Whenever the Swift concurrency needs to execute some piece of work, it enqueues an `UnownedJob`s on a specific executor the job should be executed on. The `UnownedJob` type is an opaque wrapper around Swift's low-level representation of such job. It cannot be meaningfully inspected, copied and must never be executed more than once. + +```swift +@available(SwiftStdlib 5.9, *) +@_moveOnly +public struct Job: Sendable { + /// Returns the priority sef on this Job. + /// + /// A Job priority is equal to TaskPriority if the job is a Task. + public var priority: Priority { get } +} + +@available(SwiftStdlib 5.9, *) +extension Job { + // TODO: A JobPriority technically is the same in value as a TaskPriority, + // but it feels wrong to expose "Task..." named APIs on Job which + // may be not only tasks. + // + // TODO: Alternatively, we could typealias `Priority = TaskPriority` here + public struct Priority { + public typealias RawValue = UInt8 + public var rawValue: RawValue + + /// Convert this ``UnownedJob/Priority`` to a ``TaskPriority``. + public var asTaskPriority: TaskPriority? { ... } + + public var description: String { ... } + } +} +``` + +Because move-only types in the first early iteration of this language feature still have a number of limitations, we also offer an `UnownedJob` type, that is an unsafe "unowned" version of a `Job`. One reason one might need to reach for an `UnownedJob` is whenever a `Job` were to be used in a generic context, because in the initial version of move-only types that is available today, such types cannot appear in a generic context. For example, a naive queue implementation using an `[Job]` would be rejected by the compiler, but it is possible to express using an UnownedJob (i.e.`[UnownedJob]`). + +```swift +@available(SwiftStdlib 5.1, *) +@frozen +public struct UnownedJob: Sendable, CustomStringConvertible { + + /// Create an unsafe, unowned, job by consuming a move-only Job. + /// + /// This may be necessary currently when intending to store a job in collections, + /// or otherwise intreracting with generics due to initial implementation + /// limitations of move-only types. + @available(SwiftStdlib 5.9, *) + @usableFromInline + internal init(_ job: __owned Job) { ... } + + @available(SwiftStdlib 5.9, *) + public var priority: Priority { ... } + + public var description: String { ... } +} +``` + +A job's description includes its job or task ID, that can be used to correlate it with task dumps as well as task lists in Instruments and other debugging tools (e.g. `swift-inspect`'s ). A task ID is an unique number assigned to a task, and can be useful when debugging scheduling issues, this is the same ID that is currently exposed in tools like Instruments when inspecting tasks, allowing to correlate debug logs with observations from profiling tools. + +Eventually, an executor will want to actually run a job. It may do so right away when it is enqueued, or on some different thread, this is entirely left up to the executor to decide. Running a job is done by calling the `runJobSynchronously` method which is provided on the SerialExecutor protocol. + +Running a `Job` _consumes_ it, and therefore it is not possible to accidentally run the same job twice, which would lead to undefined behavior if it were allowed. + +```swift +@available(SwiftStdlib 5.9, *) +extension SerialExecutor { + /// Run the job synchronously. + /// + /// This operation consumes the job. + @_alwaysEmitIntoClient + @inlinable + public func runJobSynchronously(_ job: __owned Job) { + _swiftJobRun(UnownedJob(job), self) + } + + /// Run the job synchronously. + /// + /// A job can only be run *once*. Accessing the job after it has been run is undefined behavior. + /// - Parameter job: + @_alwaysEmitIntoClient + @inlinable + public func runUnownedJobSynchronously(_ job: UnownedJob) { + _swiftJobRun(job, self) + } +} + +@available(SwiftStdlib 5.9, *) +extension UnownedSerialExecutor { + @_alwaysEmitIntoClient + @inlinable + public func runJobSynchronously(_ job: __owned Job) + + @_alwaysEmitIntoClient + @inlinable + public func runUnownedJobSynchronously(_ job: UnownedJob) +} +``` + +### Actors with custom SerialExecutors + +All actors implicitly conform to the `Actor` (or `DistributedActor`) protocols, and those protocols include the customization point for the executor they are required to run on in form of the the `unownedExecutor` property. + +An actor's executor must conform to the `SerialExecutor` protocol, which refines the Executor protocol, and provides enough guarantees to implement the actor's mutual exclusion guarantees. In the future, `SerialExecutors` may also be extended to support "switching", which is a technique to avoid thread-switching in calls between actors whose executors are compatible to "lending" each other the currently running thread. This proposal does not cover switching semantics. + +Actors select which serial executor they should use to run tasks is expressed by the `unownedExecutor` protocol requirement on the `Actor` and `DistributedActor` protocols: + +```swift +public protocol Actor: AnyActor { + /// Retrieve the executor for this actor as an optimized, unowned + /// reference. + /// + /// This property must always evaluate to the same executor for a + /// given actor instance, and holding on to the actor must keep the + /// executor alive. + /// + /// This property will be implicitly accessed when work needs to be + /// scheduled onto this actor. These accesses may be merged, + /// eliminated, and rearranged with other work, and they may even + /// be introduced when not strictly required. Visible side effects + /// are therefore strongly discouraged within this property. + nonisolated var unownedExecutor: UnownedSerialExecutor { get } +} + +public protocol DistributedActor: AnyActor { + /// ... + nonisolated var unownedExecutor: UnownedSerialExecutor { get } +} +``` + +> Note: It is not possible to express this protocol requirement on `AnyActor` directly because `AnyActor` is a "marker protocol" which are not present at runtime, and cannot have protocol requirements. + +The compiler synthesizes an implementation for this requirement for every `(distributed) actor` declaration, unless an explicit implementation is provided. The default implementation synthesized by the compiler uses the default `SerialExecutor`, that uses tha apropriate mechanism for the platform (e.g. Dispatch). Actors using this default synthesized implementation are referred to as "Default Actors", i.e. actors using the default serial executor implementation. + +Developers can customize the executor used by an actor on a declaration-by-declaration basis, by implementing this protocol requirement in an actor. For example, thanks to the `sharedUnownedExecutor` static property on `MainActor` it is possible to declare other actors which are also guaranteed to use the same serial executor (i.e. "the main thread"). + +```swift +(distributed) actor MainActorsBestFriend { + nonisolated var unownedExecutor: UnownedSerialExecutor { + MainActor.sharedUnownedExecutor + } + func greet() { + print("Main-friendly...") + try? await Task.sleep(for: .seconds(3)) + } +} + +@MainActor +func mainGreet() { + print("Main hello!") +} + +func test() { + Task { await mainGreet() } + Task { await MainActorsBestFriend().greet() } + // Legal executions: + // 1) + // - Main-friendly... hello! + // - Main hello! + // 2) + // - Main hello! + // - Main-friendly... hello! +} +``` + +The snippet above illustrates that while the `MainActor` and the `MainActorsBestFriend` are different actors, and thus are generally allowed to execute concurrently... because they *share* the same main actor (main thread) serial executor, they will never execute concurrently. + +It is also possible for libraries to offer protocols where a default, library specific, executor is already defined, like this: + +```swift +protocol WithSpecifiedExecutor: Actor { + nonisolated var executor: LibrarySecificExecutor { get } +} + +protocol LibrarySecificExecutor: SerialExecutor {} + +extension LibrarySpecificActor { + /// Establishes the WithSpecifiedExecutorExecutor as the serial + /// executor that will coordinate execution for the actor. + nonisolated var unownedExecutor: UnownedSerialExecutor { + executor.asUnownedSerialExecutor() + } +} + +/// A naive "run on calling thread" job executor. +/// Generally executors should enqueue and process the job on another thread instead. +/// Ways to efficiently avoid hops when not necessary, will be offered as part of the +/// "executor switching" feature, that is not part of this proposal. +final class InlineExecutor: SpecifiedExecutor, CustomStringConvertible { + public func enqueue(_ job: __owned Job) { + runJobSynchronously(job) + } +} +``` + +Which ensures that users of such library implementing such actors provide the library specific executor for their actors: + +```swift +actor MyActor: WithSpecifiedExecutor { + + nonisolated let executor: SpecifiedExecutor + + init(executor: SpecifiedExecutor) { + self.executor = executor + } +} +``` + +A library could also provide a default implementation of such executor as well. + +### Executor assertions + +Similar to the `unsafeAssumeOnMainActor` API pitched in [Pitch: Unsafe Assume on MainActor](https://forums.swift.org/t/pitch-unsafe-assume-on-mainactor/63074/), with the introduction of custom executors the same rationale of allowing people to migrate from other concurrency runtimes to swift concurrency _with confidence_ is something that applies to actors with custom executors as well. + +A common pattern in event-loop heavy code–not yet using Swift Concurrency–is to ensure/verify that a synchronous piece of code is executed on the exected event-loop. Since one of the goals of making executors customizable is to allow such libraries to adopt Swift Concurrency by making such event-loops conform to `SerialExecutor`, it is useful to allow the checking if code is indeed executing on the apropriate executor, for the library to gain confidence while it is moving towards fully embracing actors and Swift concurrency. + +For example, Swift NIO intentionally avoids synchronization checks in some synchronous methods, in order to avoid the overhead of doing so, however in DEBUG mode it performs assertions that given code is running on the expected event-loop: + +```swift +private var _channel: Channel +internal var channel: Channel { + self.eventLoop.assertInEventLoop() + assert(self._channel != nil || self.destroyed) + return self._channel ?? DeadChannel(pipeline: self) +} +``` + +Dispatch based systems also have similar functionality, with the `dispatchPrecondition` API: + +```swift +func checkIfMainQueue() { + dispatchPrecondition(condition: .onQueue(DispatchQueue.main)) +} +``` + +While, generally, in Swift Concurrency such preconditions are not necessary, because we can *statically* ensure to be on the right execution context by putting methods on specific actors, or using global actor annotations: + +```swift +@MainActor +func definitelyOnMainActor() {} + +actor Worker {} +extension Worker { + func definitelyOnWorker() {} +} +``` + +Sometimes, especially when porting existing codebases _to_ Swift Concurrency we recognize the ability to assert in synchronous code if it is running on the expected executor can bring developers more confidence during their migration to Swift Concurrency. In order to support these migrations, we propose the following method: + +```swift +/// Checks if the current task is running on the expected executor. +/// +/// Do note that if multiple actors share the same serial executor, +/// this assertion checks for the executor, not specific actor instance. +/// +/// Generally, Swift programs should be constructed such that it is statically +/// known that a specific executor is used, for example by using global actors or +/// custom executors. However, in some APIs it may be useful to provide an +/// additional runtime check for this, especially when moving towards Swift +/// concurrency from other runtimes which frequently use such assertions. +@available(SwiftStdlib 5.9, *) +public func preconditionTaskIsOnExecutor( + _ executor: some Executor, + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) + +// Same as ``preconditionTaskIsOnExecutor(_:_:file:line)`` however only in DEBUG mode. +@available(SwiftStdlib 5.9, *) +public func assertTaskIsOnExecutor( + _ executor: some Executor, + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) +``` + +It should be noted that this API will return true whenever two actors share an executor. Semantically sharing a serial executor means running in the same isolation domain, however this is only known dynamically and `await`s are stil necessary for calls between such actors: + +```swift +actor A { + nonisolated var unownedExecutor: UnownedSerialExecutor { MainActor.sharedUnownedExecutor } + + func test() {} +} + +actor B { + nonisolated var unownedExecutor: UnownedSerialExecutor { MainActor.sharedUnownedExecutor } + + func test(a: A) { + await a.test() // await is necessary, since we do not statically know about them being on the same executor + } +} +``` + +Potential future work could enable static checking where a relationship between actors is expressed statically (actor B declaring that it is on the same serial executor as a specific instance of `A`), and therefore awaits would not be necessary between such two specific actor instances. Such work is not within the scope of this initial proposal though, and only the dynamic aspect is proposed right now. Do note however that + +At this point, similar to Dispatch, these APIs only offer an "assert" / "precondition" version. And currently the way to dynamically get a boolean answer about being on a specific executor is not exposed. + +### Default Swift Runtime Executors + +Swift concurrency provides a number of default executors already, such as the main actor executor and the default global concurrent executor, which all (default) actors target by their own per-actor instantiated serial executor instances. + +The `MainActor`'s executor is available via the `sharedUnownedExecutor` static property on the `MainActor`: + +```swift +@available(SwiftStdlib 5.1, *) +@globalActor public final actor MainActor: GlobalActor { + public nonisolated var unownedExecutor: UnownedSerialExecutor + public static var sharedUnownedExecutor: UnownedSerialExecutor +} +``` + +So putting other actors onto the same executor as the MainActor is executing on, is possible using the following pattern: + +```swift +actor Friend { + nonisolated var unownedExecutor: UnownedSerialExecutor { + MainActor.sharedUnownedExecutor + } +} +``` + +The default global concurrent executor is currently not replacable. + +## Source compatibility + +Many of these APIs are existing public types since the first introduction of Swift Concurrency (and are included in back-deployment libraries). As all types and pieces of this proposal are designed in a way that allows to keep source and behavioral compatibility with already existing executor APIs. + +Special affordances are taken to introduce the move-only Job based enqueue API in an source compatible way, while deprecating the previously existing ("unowned") API. + +## Effect on ABI stability + +Swift's concurrency runtime has already been using executors, jobs and tasks since its first introduction, as such, this proposal remains ABI compatible with all existing runtime entry points and types. + +The design of `SerialExecutor` currently does not support non-reentrant actors, and it does not support executors for which dispatch is always synchronous (e.g. that just acquire a traditional mutex). + +To further explain the relationship of new and existing APIs in this proposal, we opted to keep the `@available` annotations on the discussed types, so that it is clearer which APIs exist today and cannot be entirely changed, and which APIs are new additions. Notably, there are no official APIs for operations like running a job before they were introduced in this prposal. + +## Effect on API resilience + +While some APIs may depend on being executed on particular executors, this proposal makes no effort to formalize that in interfaces, as opposed to being an implementation detail of implementations, and so has no API resilience implications. + +If this is extended in the future to automatic, declaration-driven executor switching, as actors do, that would have API resilience implications. + +## Alternatives considered + +The proposed ways for actors to opt in to custom executors are brittle, in the sense that a typo or some similar error could accidentally leave the actor using the default executor. This could be fully mitigated by requiring actors to explicitly opt in to using the default executor; however, that would be an unacceptable burden on the common case. Short of that, it would be possible to have a modifier that marks a declaration as having special significance, and then complain if the compiler doesn't recognize that significance. However, there are a number of existing features that use a name-sensitive design like this, such as dynamic member lookup ([SE-0195](https://github.com/rjmccall/swift-evolution/blob/custom-executors/proposals/0195-dynamic-member-lookup.md)). A "special significance" modifier should be designed and considered more holistically. + +## Future Directions + +### Overriding the MainActor executor + +Because of the special semantics of `MainActor` as well as its interaction with an asynchronous `main` function, customizing its serial executor is slightly more tricky than customizing any other executor. We must both guarantee that the main function of a program runs on the main thread, and that any `MainActor` code also gets to run on the main thread. This also introduces interesting complications with the main function actually returning an exit code. + +It should be possible to override the serial executor used by the the asynchronous `main` method, as well as the `MainActor`. While the exact semantics remain to be designed, we envision an API that allows replacing the main executor before any asynchronous work has happened, and this way uphold the serial execution guarantees expected from the main actor. + +```swift +// DRAFT; Names of protocols or exact shape of such replacement API are non-final. + +protocol MainActorSerialExecutor: [...]SerialExecutor { ... } +func setMainActorExecutor(_ executor: some MainActorSerialExecutor) { ... } + +@main struct Boot { + func main() async { + // + + // The following call must be made: + // - before any suspension point is encountered + // - before + setMainActorExecutor(...) + + // + await hello() // give control of the "raw" main thread to the RunLoopSerialExecutor + // still main thread, but executing on the selected MainActorExecutor + } +} + +@MainActor +func hello() { + // guaranteed to be MainActor (main thread), + // executed on the selected main actor executor + print("Hello") +} +``` + +### Executor Switching + +Executor switching is the capability to avoid un-necessary thread hops, when attempting to hop between actors/executors, where the target executor is compatible with "taking over" the calling thread. This allows Swift to optimize for less thread hops and scheduling calls. E.g. if actors are scheduled on the same executor identity and they are compatible with switching, it is possible to avoid thread-hops entirely and execution can "follow the Task" through multiple executors. + +The early sketch of switching focused around adding the following methods to the executor protocols: + +```swift +// DRAFT; Names and specific APIs mentioned in this snippet are non-final. + +protocol SerialExecutor: Executor { + // .... existing APIs ... + + /// Is it possible for this executor to give up the current thread + /// and allow it to start running a different actor? + var canGiveUpThread: Bool { get } + + /// Given that canGiveUpThread() previously returned true, give up + /// the current thread. + func giveUpThread() + + /// Attempt to start running a task on the current actor. Returns + /// true if this succeeds. + func tryClaimThread() -> Bool +} +``` + +We will consider adding these, or similar, APIs to enable custom executors to participate in efficient switching, when we are certain these API shapes are "enough" to support all potential use-cases for this feature. + +### Specifying Task executors + +Specifying executors to tasks has a suprising number of tricky questions it has to answer, so for the time being we are not introducing such capability. Specifically, passing an executor to `Task(startingOn: someExecutor) { ... }` would make the Task _start_ on the specified executor, but detailed semantics about if the _all_ of this Task's body is expected to execute on `someExecutor` (i.e. we have to hop-back to it every time after an `await`), or if it is enough to just start on it and then continue avoiding scheduling more jobs if possible (i.e. allow for aggressive switching). + +### DelegateActor property + +The previous pitch of custom executors included a concept of a `delegateActor` which allowed an actor to declare a `delegateActor: Actor` property which would allow given actor to execute on the same executor as another actor instance. At the same time, this would provide enough information to the compiler at compile time, that both actors can be assumed to be within the same isolation domain, and `await`s between those actors could be skipped (!). A property that with custom executors holds dynamically, would this way be reinforced statically by the compiler and type-system. From 9b47e0414d1948d777f7a61c7764a5fbc6714756 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 15 Feb 2023 21:30:30 +0900 Subject: [PATCH 2985/4563] add toc --- proposals/nnnn-custom-actor-executors.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/proposals/nnnn-custom-actor-executors.md b/proposals/nnnn-custom-actor-executors.md index b68b96f8c9..95c1f5345d 100644 --- a/proposals/nnnn-custom-actor-executors.md +++ b/proposals/nnnn-custom-actor-executors.md @@ -7,6 +7,29 @@ - Previous threads: - Original pitch thread from around Swift 5.5: [Support custom executors in Swift Concurrency](https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425) +## Table of Contents + +- [Custom Actor Executors](#custom-actor-executors) + * [Introduction](#introduction) + * [Motivation](#motivation) + * [Proposed solution](#proposed-solution) + * [Detailed design](#detailed-design) + + [A low-level design](#a-low-level-design) + + [Executors](#executors) + + [Serial Executors](#serial-executors) + + [Jobs](#jobs) + + [Actors with custom SerialExecutors](#actors-with-custom-serialexecutors) + + [Executor assertions](#executor-assertions) + + [Default Swift Runtime Executors](#default-swift-runtime-executors) + * [Source compatibility](#source-compatibility) + * [Effect on ABI stability](#effect-on-abi-stability) + * [Effect on API resilience](#effect-on-api-resilience) + * [Alternatives considered](#alternatives-considered) + * [Future Directions](#future-directions) + + [Overriding the MainActor executor](#overriding-the-mainactor-executor) + + [Executor Switching](#executor-switching) + + [Specifying Task executors](#specifying-task-executors) + + [DelegateActor property](#delegateactor-property) ## Introduction From 10d2a535cd16936aec58891cf69d1016bdbfd36c Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 15 Feb 2023 09:51:45 -0800 Subject: [PATCH 2986/4563] Start review of SE-0388 --- proposals/0388-async-stream-factory.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0388-async-stream-factory.md b/proposals/0388-async-stream-factory.md index a4debad21f..08f2aa9664 100644 --- a/proposals/0388-async-stream-factory.md +++ b/proposals/0388-async-stream-factory.md @@ -3,9 +3,9 @@ * Proposal: [SE-0388](0388-async-stream-factory.md) * Authors: [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) -* Status: **Awaiting review** +* Status: **Active review (February 15...26, 2023)** * Implementation: [apple/swift#62968](https://github.com/apple/swift/pull/62968) -* Review: ([pitch](https://forums.swift.org/t/pitch-convenience-async-throwing-stream-makestream-methods/61030)) +* Review: ([pitch](https://forums.swift.org/t/pitch-convenience-async-throwing-stream-makestream-methods/61030)) ([review](https://forums.swift.org/t/se-0388-convenience-async-throwing-stream-makestream-methods/63139)) ## Introduction @@ -199,7 +199,7 @@ extension AsyncStream { } ``` -### Expose an initilizer on the `NewStream` type +### Expose an initializer on the `NewStream` type During the pitch it was brought up that we could expose an `init` on the `NewStream` types that this proposal wants to add. I decided against that since one would have to spell out `AsyncStream.NewStream()` to access the `init`. This is quite hard to discover in From 35328533ffc064d89ccc240ff87a37ce53039aff Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Feb 2023 10:36:59 -0800 Subject: [PATCH 2987/4563] Move the naming discussion from the freestanding pitch here --- proposals/nnnn-attached-macros.md | 118 ++++++++++++++++-------------- 1 file changed, 63 insertions(+), 55 deletions(-) diff --git a/proposals/nnnn-attached-macros.md b/proposals/nnnn-attached-macros.md index 2ea33fa917..91c29b8c65 100644 --- a/proposals/nnnn-attached-macros.md +++ b/proposals/nnnn-attached-macros.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Holly Borla](https://github.com/hborla), [Richard Wei](https://github.com/rxwei) * Review Manager: Unassigned * Status: **Pending review** -* Implementation: Partially implemented on GitHub `main` behind the experimental flag `Macros`. +* Implementation: Implemented on GitHub `main` behind the experimental flag `Macros`. See the [example repository](https://github.com/DougGregor/swift-macro-examples) for more macros. * Review: * Pitch threads: [Pitch #1 under the name "declaration macros"](https://forums.swift.org/t/pitch-declaration-macros/62373), [Pitch #2](https://forums.swift.org/t/pitch-attached-macros/62812) @@ -12,51 +12,53 @@ Attached macros provide a way to extend Swift by creating and extending declarations based on arbitrary syntactic transformations on their arguments. They make it possible to extend Swift in ways that were only previously possible by introducing new language features, helping developers build more expressive libraries and eliminate extraneous boilerplate. -Attached macros are one part of the [vision for macros in Swift](https://github.com/apple/swift-evolution/pull/1927), which lays out general motivation for introducing macros into the language. They build on the ideas and motivation of [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) and [freestanding macros](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md) to cover a large new set of use cases; we will refer to those proposals often for the basic model of how macros integrate into the language. While freestanding macros are designed as standalone entities introduced by `#`, attached macros are associated with a specific declaration in the program that they can augment and extend. This supports many new use cases, greatly expanding the expressiveness of the macro system: +Attached macros are one part of the [vision for macros in Swift](https://github.com/apple/swift-evolution/pull/1927), which lays out general motivation for introducing macros into the language. They build on the ideas and motivation of [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) to cover a large new set of use cases; we will refer to that proposal for the basic model of how macros integrate into the language. While expression macros are designed as standalone entities introduced by `#`, attached macros are associated with a specific declaration in the program that they can augment and extend. This supports many new use cases, greatly expanding the expressiveness of the macro system: -* Creating trampoline or wrapper functions, such as automatically creating a completion-handler version of an `async` function. -* Creating members of a type based on its definition, such as forming an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset) from an enum containing flags and conforming it to the `OptionSet` type. +* Creating trampoline or wrapper functions, such as automatically creating a completion-handler version of an `async` function or vice-versa. +* Creating members of a type based on its definition, such as forming an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset) from an enum containing flags and conforming it to the `OptionSet` protocol or adding a memberwise initializer. * Creating accessors for a stored property or subscript, subsuming some of the behavior of [SE-0258 "Property Wrappers"](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md). * Augmenting members of a type with a new attribute, such as applying a property wrapper to all stored properties of a type. +There is an [example repository](https://github.com/DougGregor/swift-macro-examples) containing a number of macros that have been implemented using the prototype of this feature. + ## Proposed solution -The proposal adds *attached macros*, so-called because they are attached to a particular declaration. They are written using the custom attribute syntax (e.g., `@addCompletionHandler`) that was introduced for property wrappers, and is also used for result builders and global actors. Attached macros can reason about the declaration to which they are attached, and provide additions and changes based on one or more different macro *roles*. Each role has a specific purpose, such as adding members, creating accessors, or adding peers alongside the declaration. A given attached macro can inhabit several different roles, and as such will be expanded multiple times corresponding to the different roles, which allows the various roles to be composed. For example, an attached macro emulating property wrappers might inhabit both the "peer" and "accessor" roles, allowing it to introduce a backing storage property and also synthesize a getter/setter that go through that backing storage property. Composition of macro roles will be discussed in more depth once the basic macro roles have been established. +The proposal adds *attached macros*, so-called because they are attached to a particular declaration. They are written using the custom attribute syntax (e.g., `@AddCompletionHandler`) that already provides extensibility for declarations through property wrappers, result builders, and global actors. Attached macros can reason about the declaration to which they are attached, and provide additions and changes based on one or more different macro *roles*. Each role has a specific purpose, such as adding members, creating accessors, or adding peers alongside the declaration. A given attached macro can inhabit several different roles, and as such will be expanded multiple times corresponding to the different roles, which allows the various roles to be composed. For example, an attached macro emulating property wrappers might inhabit both the "peer" and "accessor" roles, allowing it to introduce a backing storage property and also synthesize a getter/setter that go through that backing storage property. Composition of macro roles will be discussed in more depth once the basic macro roles have been established. -As with freestanding macros, attached declaration macros are declared with `macro`, and have [type-checked macro arguments](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#type-checked-macro-arguments-and-results) that allow their behavior to be customized. Attached macros are identified with the `@attached` attribute, which also provides the specific role as well as [any names they introduce](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md#up-front-declarations-of-newly-introduced-names). For example, the aforemented macro emulating property wrappers could be declared as follows: +As with expression macros, attached declaration macros are declared with `macro`, and have [type-checked macro arguments](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#type-checked-macro-arguments-and-results) that allow their behavior to be customized. Attached macros are identified with the `@attached` attribute, which also provides the specific role as well as any names they introduce. For example, the aforemented macro to add a completion handler would be declared as follows: ```swift -@attached(peer, names: prefixed(_)) -@attached(accesor) -macro wrapProperty() +@attached(peer, names: overloaded) +macro AddCompletionHandler(parameterName: String = "completionHandler") ``` -All attached macros are implemented as types that conform one of the protocols that inherits from the `AttachedMacro` protocol. Like the [`Macro` protocol](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-protocols), the `AttachedMacro` protocol has no requirements, but is used to organize macro implementations. +The macro can be used as follows: ```swift -public protocol AttachedMacro: Macro { } +@AddCompletionHandler(parameterName: "onCompletion") +func fetchAvatar(_ username: String) async -> Image? { ... } ``` -Each attached macro role will have its own protocol that inherits `AttachedMacro`. - -#### Peer macros - -Peer macros produce new declarations alongside the declaration to which they are attached. For example, here is a declaration of a macro that introduces a completion-handler version of a given asynchronous function: +The use of the macro is attached to `fetchAvatar`, and generates a *peer* declaration alongside `fetchAvatar` whose name is "overloaded" with `fetchAvatar`. The generated declaration is: ```swift -@attached(peer, names: overloaded) macro addCompletionHandler() +/// Expansion of the macro produces the following. +func fetchAvatar(_ username: String, onCompletion: @escaping (Image?) -> Void) { + Task.detached { + completionHandler(await fetchAvatar(username)) + } +} ``` -This macro uses the `@attached` attribute to indicate that it is an attached peer attribute. The `names` argument specifies that how the names of the peer declarations are created, using an extended form of the scheme introduced with [freestanding macros](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md#up-front-declarations-of-newly-introduced-names). In this case, `overloaded` means that this macro will produce a peer that is overloaded with the declaration to which it is attached, i.e., it has the same base name. +### Implementing attached macros -The macro can be used like this: +All attached macros are implemented as types that conform to one of the protocols that inherits from the `AttachedMacro` protocol. Like the [`Macro` protocol](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-protocols), the `AttachedMacro` protocol has no requirements, but is used to organize macro implementations. Each attached macro role will have its own protocol that inherits `AttachedMacro`. ```swift -@addCompletionHandler -func fetchAvatar(_ username: String) async -> Image? { ... } +public protocol AttachedMacro: Macro { } ``` -Peer macros are implemented via types that conform to the `PeerMacro` protocol: +The biggest difference from expression macros is that there is more than one relevant piece of syntax for the macro to consider: each attached macro implementation receives the syntax node for both the attribute (e.g., `@AddCompletionHandler(parameterName: "onCompletion")`) and the declaration to which the macro is attached (`func fetchAvatar`...), and can return new code that's appropriate to the macro role: peer macros return new declarations, accessor macros return getters/getters, and so on. For example, `PeerMacro` is defined as follows: ```swift public PeerMacro: AttachedMacro { @@ -68,30 +70,29 @@ public PeerMacro: AttachedMacro { /// go alongside the given declaration. static func expansion( of node: AttributeSyntax, - providingPeersOf declaration: DeclSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext ) async throws -> [DeclSyntax] } ``` -The effect of `addCompletionHandler` is to produce a new "peer" declaration with the same signature as the declaration it is attached to, but with `async` and the result type removed in favor of a completion handler argument, e.g., +### Naming macro-produced declarations -```swift -/// Expansion of the macro produces the following. -func fetchAvatar(_ username: String, completionHandler: @escaping (Image?) -> Void) { - Task.detached { - completionHandler(await fetchAvatar(username)) - } -} -``` +Unlike expression macros, attached macros can introduce new declarations. These declarations can have an impact on code elsewhere in the program, for example if a macro provides a declaration of a function named `hello` and that function is called from another source file. Our design requires macros to document which names they can introduce: this provides more information up-front to developers and tools alike to understand the impact that a macro can have on the surrounding program. For developers, this can mean fewer surprises; for tools, this can be used to improve compilation times by avoiding unnecessary macro expansions. + +The `@AddCompletionHandler` macro notes that it introduces an *overloaded* name, meaning that it produces a declaration with the same base name as the declaration to which it is attached. A macro that emulated a property wrapper would specify the storage name via `prefixed(_)`, meaning that `_` will be added as a prefix to the name of the declaration to which that macro is attached. Other ways in which macro-generated names are communicated are discussed in the Detailed Design. -The actual implementation of this macro involves a bit of syntax manipulation, so we settle for a pseudo-code definition here and leave the complete implementation to the appendix: +### Kinds of attached macros + +#### Peer macros + +Peer macros produce new declarations alongside the declaration to which they are attached. The `AddCompletionHandler` macro from earlier was a peer macro. Peer macros are implemented via types that conform to the `PeerMacro` protocol shown earlier. The implementation of `AddCompletionHandlerMacro` looks like the following: ```swift -public struct AddCompletionHandler: PeerDeclarationMacro { +public struct AddCompletionHandlerMacro: PeerDeclarationMacro { public static func expansion( of node: CustomAttributeSyntax, - providingPeersOf declaration: DeclSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext ) throws -> [DeclSyntax] { // make sure we have an async function to start with @@ -106,12 +107,14 @@ public struct AddCompletionHandler: PeerDeclarationMacro { } ``` +The details of the implementation are left to an Appendix, with a complete version in the [example repository](https://github.com/DougGregor/swift-macro-examples). + #### Member macros Member macros allow one to introduce new members into the type or extension to which the macro is attached. For example, we can write a macro that defines static members to ease the definition of an [`OptionSet`](https://developer.apple.com/documentation/swift/optionset). Given: ```swift -@optionSetMembers +@OptionSetMembers struct MyOptions: OptionSet { enum Option: Int { case a @@ -145,7 +148,7 @@ The macro itself will be declared as a member macro that defines an arbitrary se ```swift /// Create the necessary members to turn a struct into an option set. -@attached(member, names: names(rawValue), arbitrary]) macro optionSetMembers() +@attached(member, names: names(rawValue), arbitrary) macro OptionSetMembers() ``` The `member` role specifies that this macro will be defining new members of the declaration to which it is attached. In this case, while the macro knows it will define a member named `rawValue`, there is no way for the macro to predict the names of the static properties it is defining, so it also specifies `arbitrary` to indicate that it will introduce members with arbitrarily-determined names. @@ -173,15 +176,15 @@ Accessor macros allow a macro to add accessors to a property or subscript, for e struct MyStruct { var storage: [AnyHashable: Any] = [:] - @dictionaryStorage + @DictionaryStorage var name: String - @dictionaryStorage(key: "birth_date") + @DictionaryStorage(key: "birth_date") var birthDate: Date? } ``` -The `dictionaryStorage` attached macro would alter the properties of `MyStruct` as follows, adding accessors to each: +The `DictionaryStorage` attached macro would alter the properties of `MyStruct` as follows, adding accessors to each: ```swift struct MyStruct { @@ -216,7 +219,7 @@ struct MyStruct { The macro can be declared as follows: ```swift -@attached(accessor) macro dictionaryStorage(key: String? = nil) +@attached(accessor) macro DictionaryStorage(key: String? = nil) ``` Implementations of accessor macros conform to the `AccessorMacro` protocol, which is defined as follows: @@ -234,7 +237,7 @@ protocol AccessorMacro: AttachedMacro { } ``` -The implementation of the `dictionaryStorage` macro would create the accessor declarations shown above, using either the `key` argument (if present) or deriving the key name from the property name. The effect of this macro isn't something that can be done with a property wrapper, because the property wrapper wouldn't have access to `self.storage`. +The implementation of the `DictionaryStorage` macro would create the accessor declarations shown above, using either the `key` argument (if present) or deriving the key name from the property name. The effect of this macro isn't something that can be done with a property wrapper, because the property wrapper wouldn't have access to `self.storage`. The presence of an accessor macro on a stored property removes the initializer. It's up to the implementation of the accessor macro to either diagnose the presence of the initializer (if it cannot be used) or incorporate it in the result. @@ -248,13 +251,13 @@ As an example, we'll define a macro that partially mimics the behavior of the `@ ```swift @attached(memberAttribute) -macro myObjCMembers +macro MyObjCMembers() ``` The implementation of this macro will add the `@objc` attribute to every member of the type or extension, unless that member either already has an `@objc` macro or has `@nonobjc` on it. For example, this: ```swift -@myObjCMembers extension MyClass { +@MyObjCMembers extension MyClass { func f() { } var answer: Int { 42 } @@ -288,7 +291,7 @@ protocol MemberAttributeMacro: AttachedMacro { static func expansion( of node: AttributeSyntax, attachedTo declaration: some DeclGroupSyntax, - providingAttributesFor member: DeclSyntax, + providingAttributesOf member: some DeclSyntaxProtocol, in context: some MacroExpansionContext ) async throws -> [AttributeSyntax] } @@ -298,7 +301,7 @@ The `expansion` operation accepts the attribute syntax `node` for the spelling o #### Conformance macros -Conformance macros allow one to introduce new protocol conformances to a type. This would often be paired with other macros whose purpose is to help satisfy the protocol conformance. For example, one could imagine an extended version of the `optionSetMembers` attributed shown earlier that also adds the `OptionSet` conformance. With it, the mimimal implementation of an option set could be: +Conformance macros allow one to introduce new protocol conformances to a type. This would often be paired with other macros whose purpose is to help satisfy the protocol conformance. For example, one could imagine an extended version of the `OptionSetMembers` attributed shown earlier that also adds the `OptionSet` conformance. With it, the mimimal implementation of an option set could be: ```swift @OptionSet @@ -311,11 +314,11 @@ struct MyOptions } ``` -where `OptionSet` is both a member and a conformance macro, providing members (as in `optionSetMembers`) and the conformance to `OptionSet`. The macro would be declared as, e.g., +where `OptionSet` is both a member and a conformance macro, providing members (as in `OptionSetMembers`) and the conformance to `OptionSet`. The macro would be declared as, e.g., ```swift /// Create the necessary members to turn a struct into an option set. -@attached(member, names: names(rawValue), arbitrary]) +@attached(member, names: names(rawValue), arbitrary) @attached(conformance) macro OptionSet() ``` @@ -455,7 +458,7 @@ by conforming to `AccessorMacro`: extension ClampingMacro: AccessorMacro { static func expansion( of node: CustomAttributeSyntax, - providingAccessorsOf declaration: DeclSyntax, + providingAccessorsOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext ) throws -> [AccessorDeclSyntax] { let originalName = /* get from declaration */, @@ -491,13 +494,16 @@ This formulation of `@Clamping` offers some benefits over the property-wrapper v ### Specifying newly-introduced names -The proposal for freestanding macros introduced the need to [specify macro-introduced names](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md#up-front-declarations-of-newly-introduced-names) as a way of isolating the effect that macros can have on the compilation process. Attached macros introduce a few more ways in which one can describe the names of declarations that are produced by a macro: +Whenever a macro produces declarations that are visible to other Swift code, it is required to declare the names in advance. All of the names need to be specified within the attribute declaring the macro role, using the following forms: + +- Declarations with a specific fixed name: `named()`. +- Declarations whose names cannot be described statically, for example because they are derived from other inputs: `arbitrary`. * Declarations that have the same base name as the declaration to which the macro is attached, and are therefore overloaded with it: `overloaded`. -* Declarations whose name is formed by adding a prefix to the name of the declaration to which the macro is attached: `prefixed("_")` +* Declarations whose name is formed by adding a prefix to the name of the declaration to which the macro is attached: `prefixed("_")`. As a special consideration, `$` is permissible as a prefix, allowing macros to produce names with a leading `$` that are derived from the name of the declaration to which the macro is attached. This carve-out enables macros that behavior similarly to property wrappers that introduce a projected value. * Declarations whose name is formed by adding a suffix to the name of the declaration to which the macro is attached: `suffixed("_docinfo")`. -A macro can only introduce new declarations whose names are covered by the kinds provided, or have their names generated via `MacroExpansionContext.createUniqueame`. This ensures that, in most cases (where `arbitrary` is not specified), the Swift compiler and related tools can reason about the set of names that will introduced by a given use of a macro without having to expand the macro, which can reduce the compile-time cost of macros and improve incremental builds. +A macro can only introduce new declarations whose names are covered by the kinds provided, or have their names generated via `MacroExpansionContext.createUniqueName`. This ensures that, in most cases (where `arbitrary` is not specified), the Swift compiler and related tools can reason about the set of names that will be introduced by a given use of a macro without having to expand the macro, which can reduce the compile-time cost of macros and improve incremental builds. The macro is not required to provide a declaration for every name it describes: for example, `OptionSetMembers` will state that it produces a declaration named `rawValue`, but the implementation may opt not to do so if it sees that such a property already exists. ### Ordering of macro expansions @@ -531,7 +537,9 @@ It might be possible to provide a macro implementation API that is expressed in * After the first pitch: * Added conformance macros, to produce conformances -* Originally pitched as "declaration macros"; attached macros were separated into their own proposal + * Moved the discussion of macro-introduced names from the freestanding macros proposal here. + * Added a carve-out to allow a `$` prefix on names generated from macros, allowing them to match the behavior of property wrappers. +* Originally pitched as "declaration macros"; attached macros were separated into their own proposal after the initial discussion. ## Future directions @@ -547,13 +555,13 @@ There are numerous ways in which this proposal could be extended to provide new public struct AddCompletionHandler: PeerDeclarationMacro { public static func expansion( of node: CustomAttributeSyntax, - providingPeersOf declaration: DeclSyntax, + providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext ) throws -> [DeclSyntax] { // Only on functions at the moment. We could handle initializers as well // with a little bit of work. guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else { - throw CustomError.message("@addCompletionHandler only works on functions") + throw CustomError.message("@AddCompletionHandler only works on functions") } // This only makes sense for async functions. From f5ffef40d3ccb22bb8a1872571422b5305a15948 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Feb 2023 13:31:35 -0800 Subject: [PATCH 2988/4563] Cover macro expansion independence/composability --- proposals/nnnn-attached-macros.md | 94 +++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/proposals/nnnn-attached-macros.md b/proposals/nnnn-attached-macros.md index 91c29b8c65..697f8e1e26 100644 --- a/proposals/nnnn-attached-macros.md +++ b/proposals/nnnn-attached-macros.md @@ -305,7 +305,7 @@ Conformance macros allow one to introduce new protocol conformances to a type. T ```swift @OptionSet -struct MyOptions +struct MyOptions { enum Option: Int { case a case b @@ -505,11 +505,96 @@ Whenever a macro produces declarations that are visible to other Swift code, it A macro can only introduce new declarations whose names are covered by the kinds provided, or have their names generated via `MacroExpansionContext.createUniqueName`. This ensures that, in most cases (where `arbitrary` is not specified), the Swift compiler and related tools can reason about the set of names that will be introduced by a given use of a macro without having to expand the macro, which can reduce the compile-time cost of macros and improve incremental builds. The macro is not required to provide a declaration for every name it describes: for example, `OptionSetMembers` will state that it produces a declaration named `rawValue`, but the implementation may opt not to do so if it sees that such a property already exists. +### Visibility of names used and introduced by macros + +Macros can introduce new names into the program. Whether and when those names are visible to other parts of the program, and in particular other macros, is a subtle design problem that has a number of interesting constraints. + +First, the arguments to a macro are type-checked before it can be determined which macro is being expanded, so they cannot depend on the expansion of that macro. For example, consider an attached macro use spelled `@myMacro(x)`: if it introduced a declaration named `x`, it would potentially invalidate the type-check of its own argument, or cause that macro expansion to choose a different `myMacro` that doesn't produce an `x`! + +Second, if the output of one macro expansion (say, `@myMacro1(x)`) introduces a name (say, `y`) that is then used in the argument to another macro expansion (say, `@myMacro2(y)`), then the order in which the macros are expanded can affect the semantics of the resulting program. It is not generally possible to introduce an ordering on macro expansions, nor would it be desirable, because Swift generally tries not to have order dependencies among declarations in a program. Incidental orderings based on the names introduced by the macro don't help, either, because names of declaration macros can be specified as `arbitrary` and therefore cannot be predicated. + +Third, macro expansion is a relatively expensive compile-time operation, involving serialization overhead to transfer parts of the program from the compiler/IDE to another program that expands the macro. Therefore, macros are [only expanded once per use site](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion), so their expansions cannot participate in the type checking of their arguments or of other surrounding statements or expressions. For example, consider a this (intentionally obtuse) Swift code: + +``` +@attached(peer) macro myMacro(_: Int) +@attached(peer, names: named("y")) macro myMacro(_: Double) + +func f(_: Int, body: (Int) -> Void) +func f(_: Double, body: (Double) -> Void) + +f(1) { x in + @myMacro(x) func g() { } + print(y) +} +``` + +The macro use `@myMacro(x)` provides different names depending on whether the argument is an `Int` or ` Double`. From the perspective of the call to `f`, both overloads of `f` are possible, and the only way to check beyond that macro use to the `print` expression that follows is to try to expand the macro... for the first `myMacro`, the `print(y)` expression will fail to type-check (there is no `y`) whereas the second would find the `y` generated by the second `myMacro` and will succeed. However, this approach does not scale, because it could involve expanding macros a large number of times during type checking. Moreover, the macro expansions might end up getting incomplete information if, for example, macros are someday provided with type information and that type information isn't known yet (because the expansion is happening during type inference). + +To address these issues, a name introduced by a macro expansion is not visible within macro arguments for macros the same scope as the macro expansion or any of its enclosing scopes. This is conceptually similar to [outside-in expansion of expression macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion), where one can imagine that all of the macros at a particular scope are type-checked and expanded simultaneously at the top-level scope. Then, macros within each type definition and its extensions are type-checked and expanded simultaneously: they can see the names introduced by top-level macro expansions, but not names introduced at other levels. The following annotated code example shows which names are visible: + +```swift +import OtherModule // declares names x, y, z + +@macro1(x) // uses OtherModule.x, introduces name y +func f() { } + +@macro2(y) // uses OtherModule.y, introduce name x +func g() { } + +struct S1 { + @macro3(x) // uses the name x introduced by #@acro2, introduces the name z + func h() +} + +extension S1 { + @macro4(z) // uses OtherModule.z + func g() { } + + func f() { + print(z) // uses z introduced by @macro3 + } +} +``` + +These name lookup rules, while important for providing a reasonable scheme for macros to be expanded without interfering with each other, do introduce a new kind of name shadowing to the language. If `@macro2` were manually expanded into source code, the declaration `x` it produces would then become visible to the macro argument in `@macro1(x)`, changing the behavior of the program. The compiler might be able to detect such shadowing in practice, when a macro-introduced name at a particular scope would change the meaning of a lookup from that particular scope. + +Within function and closure bodies, the fact that names introduced by the macro expansion are not visible within the current scope means that our earlier example will never find a declaration `y` introduced by `@myMacro`: + +```swift +f(1) { x in + @myMacro(x) func g() { } + print(y) // does not consider any names introduced by `@myMacro` +} +``` + +Therefore, a macro used within a closure or function body can only introduce declarations using names produced by `createUniqueName`. This maintains the [two-phase of checking macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion) where type checking and inference is performed without expanding the macro, then the macro is expanded and its result type-checked independently, with no ability to influence type inference further. + ### Ordering of macro expansions -The freestanding macros proposal describes the [visibility of macro-introduced names](https://github.com/DougGregor/swift-evolution/blob/freestanding-macros/proposals/nnnn-freestanding-macros.md#visibility-of-names-used-and-introduced-by-macros), which provides "outside-in" ordering rules where macros in outer scopes are expanded before those in inner scopes. The same general rules apply to attached macros, so macros attached to a type or extension will be expanded before macros on the members of that type or extension are. +When multiple macros are applied to a single declaration, the order in which macros are expanded could have a significant impact on the resulting program if the the outputs of one macro expansion are treated as inputs to the others. This form of macro composition could be fairly powerful, but it also can have a number of surprising side effects: + +1. The order in which attributes are written on a declaration could affect the result. +2. There is no obvious single source of truth for what the inputs to the macro would be. +3. Independently-developed macros could conflict in ways that break the program. + +The fine-grained nature of attached macros means that many attached macros have independent effects, e.g., macros can generate distinct members, conformances, and peers. We consider this independence of different macros applying to the same definition to be a feature, because it makes the application of macros more predictable as well as enabling more efficient implementations. + +To ensure the independence of macro invocations, each macro expansion is provided with syntax nodes as they were originally written, and not updated with the effects of other macros. For example, consider the `OptionSet` macro earlier: + +```swift +@OptionSet +struct MyOptions { + enum Option: Int { + case a + case b + case c + } +} +``` + +The `OptionSet` macro is both a member macro (which generates the instance property `rawValue` as well as static properties `a`, `b`, and `c`) and a conformance macro (to add the `OptionSet` conformance). Two different `expansion` functions will be called on the option set macro implementation: `expansion(of:providingMembersOf:in:)` and `expansion(of:providingConformancesOf:in:)`, respectively. In both cases, the syntax tree for `MyOptions` is passed as the middle argument, and both functions provide a result that augments the definition of `MyOptions`, either by adding members or by adding a conformance. -When there are multiple attached macros on a single declaration (e.g., `@macro1 @macro2 func f()`), and those macros have the same role, the macros will be expanded in left-to-right order. +In both cases, the expansion operation is provided with the original definition of `MyOptions`, without the new conformance or added members. That way, each expansion operation operates independently of the other---whether it's different roles for the same macro, or different macros entirely---and the order of expansion does not matter. ## Source compatibility @@ -539,6 +624,7 @@ It might be possible to provide a macro implementation API that is expressed in * Added conformance macros, to produce conformances * Moved the discussion of macro-introduced names from the freestanding macros proposal here. * Added a carve-out to allow a `$` prefix on names generated from macros, allowing them to match the behavior of property wrappers. + * Revisited the design around the ordering of macro expansions, forcing them to be independent. * Originally pitched as "declaration macros"; attached macros were separated into their own proposal after the initial discussion. ## Future directions @@ -549,7 +635,7 @@ There are numerous ways in which this proposal could be extended to provide new ## Appendix -### Implementation of `addCompletionHandler` +### Implementation of `AddCompletionHandler` ```swift public struct AddCompletionHandler: PeerDeclarationMacro { From 159da9f2d3d368f3a80cf0389b248ffc436b4129 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Feb 2023 14:24:40 -0800 Subject: [PATCH 2989/4563] Require accessor macros to return accessors that make stored properties into computed properties. --- proposals/nnnn-attached-macros.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/nnnn-attached-macros.md b/proposals/nnnn-attached-macros.md index 697f8e1e26..be7b7c81e8 100644 --- a/proposals/nnnn-attached-macros.md +++ b/proposals/nnnn-attached-macros.md @@ -170,7 +170,7 @@ protocol MemberMacro: AttachedMacro { #### Accessor macros -Accessor macros allow a macro to add accessors to a property or subscript, for example by turning a stored property into a computed property. For example, consider a macro that can be applied to a stored property to instead access a dictionary keyed by the property name. Such a macro could be used like this: +Accessor macros allow a macro to add accessors to a property, turning a stored property into a computed property. For example, consider a macro that can be applied to a stored property to instead access a dictionary keyed by the property name. Such a macro could be used like this: ```swift struct MyStruct { @@ -239,7 +239,7 @@ protocol AccessorMacro: AttachedMacro { The implementation of the `DictionaryStorage` macro would create the accessor declarations shown above, using either the `key` argument (if present) or deriving the key name from the property name. The effect of this macro isn't something that can be done with a property wrapper, because the property wrapper wouldn't have access to `self.storage`. -The presence of an accessor macro on a stored property removes the initializer. It's up to the implementation of the accessor macro to either diagnose the presence of the initializer (if it cannot be used) or incorporate it in the result. +The expansion of an accessor macro must result in a computed property. A side effect of the expansion is to remove any initializer from the stored property itself; it is up to the implementation of the accessor macro to either diagnose the presence of the initializer (if it cannot be used) or incorporate it in the result. #### Member attribute macros @@ -515,7 +515,7 @@ Second, if the output of one macro expansion (say, `@myMacro1(x)`) introduces a Third, macro expansion is a relatively expensive compile-time operation, involving serialization overhead to transfer parts of the program from the compiler/IDE to another program that expands the macro. Therefore, macros are [only expanded once per use site](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion), so their expansions cannot participate in the type checking of their arguments or of other surrounding statements or expressions. For example, consider a this (intentionally obtuse) Swift code: -``` +```swift @attached(peer) macro myMacro(_: Int) @attached(peer, names: named("y")) macro myMacro(_: Double) @@ -625,6 +625,7 @@ It might be possible to provide a macro implementation API that is expressed in * Moved the discussion of macro-introduced names from the freestanding macros proposal here. * Added a carve-out to allow a `$` prefix on names generated from macros, allowing them to match the behavior of property wrappers. * Revisited the design around the ordering of macro expansions, forcing them to be independent. + * Require accessor macros to return accessors that make stored properties into computed properties. * Originally pitched as "declaration macros"; attached macros were separated into their own proposal after the initial discussion. ## Future directions @@ -749,4 +750,3 @@ public struct AddCompletionHandler: PeerDeclarationMacro { } } ``` - From d996f6e6ae3bba6be8493bded7881959660c6d06 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Feb 2023 14:34:31 -0800 Subject: [PATCH 2990/4563] Explain a little more how "names" are used for optimization --- proposals/nnnn-attached-macros.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/proposals/nnnn-attached-macros.md b/proposals/nnnn-attached-macros.md index be7b7c81e8..b512e61a85 100644 --- a/proposals/nnnn-attached-macros.md +++ b/proposals/nnnn-attached-macros.md @@ -148,7 +148,7 @@ The macro itself will be declared as a member macro that defines an arbitrary se ```swift /// Create the necessary members to turn a struct into an option set. -@attached(member, names: names(rawValue), arbitrary) macro OptionSetMembers() +@attached(member, names: named(rawValue), arbitrary) macro OptionSetMembers() ``` The `member` role specifies that this macro will be defining new members of the declaration to which it is attached. In this case, while the macro knows it will define a member named `rawValue`, there is no way for the macro to predict the names of the static properties it is defining, so it also specifies `arbitrary` to indicate that it will introduce members with arbitrarily-determined names. @@ -318,7 +318,7 @@ where `OptionSet` is both a member and a conformance macro, providing members (a ```swift /// Create the necessary members to turn a struct into an option set. -@attached(member, names: names(rawValue), arbitrary) +@attached(member, names: named(rawValue), arbitrary) @attached(conformance) macro OptionSet() ``` @@ -500,10 +500,12 @@ Whenever a macro produces declarations that are visible to other Swift code, it - Declarations whose names cannot be described statically, for example because they are derived from other inputs: `arbitrary`. * Declarations that have the same base name as the declaration to which the macro is attached, and are therefore overloaded with it: `overloaded`. -* Declarations whose name is formed by adding a prefix to the name of the declaration to which the macro is attached: `prefixed("_")`. As a special consideration, `$` is permissible as a prefix, allowing macros to produce names with a leading `$` that are derived from the name of the declaration to which the macro is attached. This carve-out enables macros that behavior similarly to property wrappers that introduce a projected value. -* Declarations whose name is formed by adding a suffix to the name of the declaration to which the macro is attached: `suffixed("_docinfo")`. +* Declarations whose name is formed by adding a prefix to the name of the declaration to which the macro is attached: `prefixed(_)`. As a special consideration, `$` is permissible as a prefix, allowing macros to produce names with a leading `$` that are derived from the name of the declaration to which the macro is attached. This carve-out enables macros that behavior similarly to property wrappers that introduce a projected value. +* Declarations whose name is formed by adding a suffix to the name of the declaration to which the macro is attached: `suffixed(_docinfo)`. -A macro can only introduce new declarations whose names are covered by the kinds provided, or have their names generated via `MacroExpansionContext.createUniqueName`. This ensures that, in most cases (where `arbitrary` is not specified), the Swift compiler and related tools can reason about the set of names that will be introduced by a given use of a macro without having to expand the macro, which can reduce the compile-time cost of macros and improve incremental builds. The macro is not required to provide a declaration for every name it describes: for example, `OptionSetMembers` will state that it produces a declaration named `rawValue`, but the implementation may opt not to do so if it sees that such a property already exists. +A macro can only introduce new declarations whose names are covered by the kinds provided, or have their names generated via `MacroExpansionContext.createUniqueName`. This ensures that, in most cases (where `arbitrary` is not specified), the Swift compiler and related tools can reason about the set of names that will be introduced by a given use of a macro without having to expand the macro, which can reduce the compile-time cost of macros and improve incremental builds. For example, when the compiler performs name lookup for a name `_x`, it can avoid expanding any macros that are unable to produce a declaration named `_x`. Macros that can produce arbitrary names must always be expanded, as would a macro with `prefixed(_)` that is attached to a declaration named `x`. + +The macro is not required to provide a declaration for every name it describes: for example, `OptionSetMembers` will state that it produces a declaration named `rawValue`, but the implementation may opt not to do so if it sees that such a property already exists. ### Visibility of names used and introduced by macros @@ -517,7 +519,7 @@ Third, macro expansion is a relatively expensive compile-time operation, involvi ```swift @attached(peer) macro myMacro(_: Int) -@attached(peer, names: named("y")) macro myMacro(_: Double) +@attached(peer, names: named(y)) macro myMacro(_: Double) func f(_: Int, body: (Int) -> Void) func f(_: Double, body: (Double) -> Void) @@ -654,7 +656,7 @@ public struct AddCompletionHandler: PeerDeclarationMacro { // This only makes sense for async functions. if funcDecl.signature.asyncOrReasyncKeyword == nil { throw CustomError.message( - "@addCompletionHandler requires an async function" + "@AddCompletionHandler requires an async function" ) } @@ -687,7 +689,7 @@ public struct AddCompletionHandler: PeerDeclarationMacro { let callArguments: [String] = try parameterList.map { param in guard let argName = param.secondName ?? param.firstName else { throw CustomError.message( - "@addCompletionHandler argument must have a name" + "@AddCompletionHandler argument must have a name" ) } From 6e2211af1ab4d8ca393cde04895dbeeb5f30f534 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Feb 2023 14:48:56 -0800 Subject: [PATCH 2991/4563] Clarify when accessor macros need to produce observers for a stored property vs. turning it into a computed property. --- proposals/nnnn-attached-macros.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-attached-macros.md b/proposals/nnnn-attached-macros.md index b512e61a85..5fe2a4895b 100644 --- a/proposals/nnnn-attached-macros.md +++ b/proposals/nnnn-attached-macros.md @@ -239,7 +239,9 @@ protocol AccessorMacro: AttachedMacro { The implementation of the `DictionaryStorage` macro would create the accessor declarations shown above, using either the `key` argument (if present) or deriving the key name from the property name. The effect of this macro isn't something that can be done with a property wrapper, because the property wrapper wouldn't have access to `self.storage`. -The expansion of an accessor macro must result in a computed property. A side effect of the expansion is to remove any initializer from the stored property itself; it is up to the implementation of the accessor macro to either diagnose the presence of the initializer (if it cannot be used) or incorporate it in the result. +An accessor macro can specify that it produces observers by listing at least one of `willSet` or `didSet ` in the names, e.g., `@attached(accessors, names: named(willSet))`. Such a macro can only produce observers; it cannot change a stored property into a computed property. + +The expansion of an accessor macro that does not specify one of `willSet` or `didSet` in its list of names must result in a computed property. A side effect of the expansion is to remove any initializer from the stored property itself; it is up to the implementation of the accessor macro to either diagnose the presence of the initializer (if it cannot be used) or incorporate it in the result. #### Member attribute macros @@ -627,7 +629,7 @@ It might be possible to provide a macro implementation API that is expressed in * Moved the discussion of macro-introduced names from the freestanding macros proposal here. * Added a carve-out to allow a `$` prefix on names generated from macros, allowing them to match the behavior of property wrappers. * Revisited the design around the ordering of macro expansions, forcing them to be independent. - * Require accessor macros to return accessors that make stored properties into computed properties. + * Clarify when accessor macros need to produce observers for a stored property vs. turning it into a computed property. * Originally pitched as "declaration macros"; attached macros were separated into their own proposal after the initial discussion. ## Future directions From 7add6441c3d55643a38dc24ace02657eb3385b03 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Feb 2023 17:25:27 -0800 Subject: [PATCH 2992/4563] Add a paragraph eliding macro expansion for incompatible roles --- proposals/nnnn-attached-macros.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/nnnn-attached-macros.md b/proposals/nnnn-attached-macros.md index 5fe2a4895b..b4283b7a04 100644 --- a/proposals/nnnn-attached-macros.md +++ b/proposals/nnnn-attached-macros.md @@ -494,6 +494,8 @@ extension ClampingMacro: AccessorMacro { This formulation of `@Clamping` offers some benefits over the property-wrapper version: we don't need to store the min and max values as part of the backing storage (so the presence of `@Clamping` doesn't add any storage), nor do we need to define a new type. More importantly, it demonstrates how the composition of different macro roles together can produce interesting effects. +Not every macro role applies to every kind of declaration: an `accessor` macro doesn't make sense of a function, nor does a `member` member make sense on a property. If a macro is attached to a declaration, the macro will only be expanded for those roles that are applicable to a declaration. For example, if the `Clamping` macro is applied to a function, it will only be expanded as a peer macro; it's role as an accessor macro is ignored. If a macro is attached to a declaration and none of its roles apply to that kind of declaration, the program is ill-formed. + ### Specifying newly-introduced names Whenever a macro produces declarations that are visible to other Swift code, it is required to declare the names in advance. All of the names need to be specified within the attribute declaring the macro role, using the following forms: From d75549eaa8dcb5514973c87fdb2f19e07629d010 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Feb 2023 17:40:01 -0800 Subject: [PATCH 2993/4563] Moved the discussion of "permitted declaration kinds" from the freestanding macros proposal here. --- proposals/nnnn-attached-macros.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/proposals/nnnn-attached-macros.md b/proposals/nnnn-attached-macros.md index b4283b7a04..83191ddb37 100644 --- a/proposals/nnnn-attached-macros.md +++ b/proposals/nnnn-attached-macros.md @@ -602,6 +602,20 @@ The `OptionSet` macro is both a member macro (which generates the instance prope In both cases, the expansion operation is provided with the original definition of `MyOptions`, without the new conformance or added members. That way, each expansion operation operates independently of the other---whether it's different roles for the same macro, or different macros entirely---and the order of expansion does not matter. +### Permitted declaration kinds + +A macro can expand to any declaration that is syntactically and semantically well-formed within the context where the macro is expanded, with a few notable exceptions: + +* `import` declarations can never be produced by a macro. Swift tooling depends on the ability to resolve import declarations based on a simple scan of the original source files. Allowing a macro expansion to introduce an import declaration would complicate import resolution considerably. +* A type with the `@main` attribute cannot be produced by a macro. Swift tooling should be able to determine the presence of a main entry point in a source file based on a syntactic scan of the source file without expanding macros. +* `extension` declarations can never be produced by a macro. The effect of an extension declaration is wide-ranging, with the ability to add conformances, members, and so on. These capabilities are meant to be introduced in a more fine-grained manner. +* `operator` and `precedencegroup` declarations can never be produced by a macro, because they could allow one to reshape the precedence graph for existing code causing subtle differences in the semantics of code that sees the macro expansion vs. code that does not. +* `macro` declarations can never be produced by a macro, because allowing this would allow a macro to trivially produce infinitely recursive macro expansion. +* Top-level default literal type overrides, including `IntegerLiteralType`, + `FloatLiteralType`, `BooleanLiteralType`, + `ExtendedGraphemeClusterLiteralType`, `UnicodeScalarLiteralType`, and + `StringLiteralType`, can never be produced by a macro. + ## Source compatibility Attached declaration macros use the same syntax introduced for custom attributes (such as property wrappers), and therefore do not have an impact on source compatibility. @@ -632,6 +646,7 @@ It might be possible to provide a macro implementation API that is expressed in * Added a carve-out to allow a `$` prefix on names generated from macros, allowing them to match the behavior of property wrappers. * Revisited the design around the ordering of macro expansions, forcing them to be independent. * Clarify when accessor macros need to produce observers for a stored property vs. turning it into a computed property. + * Moved the discussion of "permitted declaration kinds" from the freestanding macros proposal here. * Originally pitched as "declaration macros"; attached macros were separated into their own proposal after the initial discussion. ## Future directions From 15e7dcaec84dcf4f51906f5317b3171226aa966a Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 16 Feb 2023 12:31:42 -0800 Subject: [PATCH 2994/4563] Mark as SE-0389 and assign review manager. --- .../{nnnn-attached-macros.md => 0389-attached-macros.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{nnnn-attached-macros.md => 0389-attached-macros.md} (99%) diff --git a/proposals/nnnn-attached-macros.md b/proposals/0389-attached-macros.md similarity index 99% rename from proposals/nnnn-attached-macros.md rename to proposals/0389-attached-macros.md index 83191ddb37..8c991ff946 100644 --- a/proposals/nnnn-attached-macros.md +++ b/proposals/0389-attached-macros.md @@ -1,9 +1,9 @@ # Attached Macros -* Proposal: [SE-nnnn](nnnn-attached-macros.md) +* Proposal: [SE-0389](0389-attached-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Holly Borla](https://github.com/hborla), [Richard Wei](https://github.com/rxwei) -* Review Manager: Unassigned -* Status: **Pending review** +* Review Manager: Tony Allevato (https://github.com/allevato) +* Status: **Active review (February 16...March 2, 2023)** * Implementation: Implemented on GitHub `main` behind the experimental flag `Macros`. See the [example repository](https://github.com/DougGregor/swift-macro-examples) for more macros. * Review: * Pitch threads: [Pitch #1 under the name "declaration macros"](https://forums.swift.org/t/pitch-declaration-macros/62373), [Pitch #2](https://forums.swift.org/t/pitch-attached-macros/62812) From 4e8e518bf2fa7eaa11352421603632e32a39a117 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 16 Feb 2023 12:37:19 -0800 Subject: [PATCH 2995/4563] Update 0389-attached-macros.md --- proposals/0389-attached-macros.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0389-attached-macros.md b/proposals/0389-attached-macros.md index 8c991ff946..67c199d236 100644 --- a/proposals/0389-attached-macros.md +++ b/proposals/0389-attached-macros.md @@ -5,8 +5,7 @@ * Review Manager: Tony Allevato (https://github.com/allevato) * Status: **Active review (February 16...March 2, 2023)** * Implementation: Implemented on GitHub `main` behind the experimental flag `Macros`. See the [example repository](https://github.com/DougGregor/swift-macro-examples) for more macros. -* Review: -* Pitch threads: [Pitch #1 under the name "declaration macros"](https://forums.swift.org/t/pitch-declaration-macros/62373), [Pitch #2](https://forums.swift.org/t/pitch-attached-macros/62812) +* Review: ([pitch #1, under the name "declaration macros"](https://forums.swift.org/t/pitch-declaration-macros/62373)) ([pitch #2](https://forums.swift.org/t/pitch-attached-macros/62812)) ([review](https://forums.swift.org/t/se-0389-attached-macros/63165)) ## Introduction From b938010754d47a6b285b74ecd11e7c079b5b81b5 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sun, 19 Feb 2023 16:56:43 -0800 Subject: [PATCH 2996/4563] [SE-0389] Fix typo in completion handler example. --- proposals/0389-attached-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0389-attached-macros.md b/proposals/0389-attached-macros.md index 67c199d236..c981e44ece 100644 --- a/proposals/0389-attached-macros.md +++ b/proposals/0389-attached-macros.md @@ -44,7 +44,7 @@ The use of the macro is attached to `fetchAvatar`, and generates a *peer* declar /// Expansion of the macro produces the following. func fetchAvatar(_ username: String, onCompletion: @escaping (Image?) -> Void) { Task.detached { - completionHandler(await fetchAvatar(username)) + onCompletion(await fetchAvatar(username)) } } ``` From 38857d3ce0c0cc2eb1cf57b15f3b0619da0165c5 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 20 Feb 2023 17:22:15 -0800 Subject: [PATCH 2997/4563] Accept the SE-0368 amendment. (#1957) --- proposals/0368-staticbigint.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0368-staticbigint.md b/proposals/0368-staticbigint.md index a38b140110..4c86fb5ff0 100644 --- a/proposals/0368-staticbigint.md +++ b/proposals/0368-staticbigint.md @@ -3,9 +3,9 @@ * Proposal: [SE-0368](0368-staticbigint.md) * Author: [Ben Rimmington](https://github.com/benrimmington) * Review Manager: [Doug Gregor](https://github.com/DougGregor), [Holly Borla](https://github.com/hborla) -* Status: **Active Review (February 6, 2023...February 13, 2023)** +* Status: **Implemented (Swift 5.8)** * Implementation: [apple/swift#40722](https://github.com/apple/swift/pull/40722), [apple/swift#62733](https://github.com/apple/swift/pull/62733) -* Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) ([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) ([acceptance](https://forums.swift.org/t/accepted-se-0368-staticbigint/59962)) ([amendment](https://forums.swift.org/t/pitch-amend-se-0368-to-remove-prefix-operator/62173)), ([amendment review](https://forums.swift.org/t/amendment-se-0368-staticbigint/62992)) +* Review: ([pitch](https://forums.swift.org/t/staticbigint/54545)) ([review](https://forums.swift.org/t/se-0368-staticbigint/59421)) ([acceptance](https://forums.swift.org/t/accepted-se-0368-staticbigint/59962)) ([amendment](https://forums.swift.org/t/pitch-amend-se-0368-to-remove-prefix-operator/62173)), ([amendment review](https://forums.swift.org/t/amendment-se-0368-staticbigint/62992)), ([amendment acceptance](https://forums.swift.org/t/accepted-amendment-se-0368-staticbigint/63246))
    Revision history From 9caa9076b1403a13391582f57cd14765cd311702 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 21 Feb 2023 07:18:16 -0800 Subject: [PATCH 2998/4563] Propose noncopyable structs and enums (#1865) * Propose noncopyable structs and enums also known as "move-only" structs and enums * Link to pitch forums thread * Some wording cleanup, and language tags on code blocks * Revisions from first round of feedback - Add some missing annotations - Cover some additional alternatives and future directions: - More discussion about interaction with generic constraints, along with an example of why `NonCopyable` is not a positive constraint but `Copyable` is - Discussion of conditionally-copyable types as a future direction - More discussion about trying to consume fields. If we want adding/removing deinit to be an API-compatible change, this has to be restricted from outside the type definition. * More revisions: - Wordsmithing - Spell out an example using `forget` conditionally, to show how to use `consume self` to explicitly invoke the destructor on non-forgetting paths - Frozen classes don't (officially) exist * Port over discussion of consuming/borrowing/mutating operations from the moribund @noImplicitCopy proposal * More revisions suggested by @atrick Clarify discussion of borrowing behavior of classes and tuples Remove remaining reference to `taking` spelling * ABI: Adding or removing copyability both affect ABI Add discussion about how the abstract property ABI has to be different between noncopyable and copyable properties because we can't generally `get` a noncopyable stored property. * expand on limitations of noncopyable types and generics also includes tips for working around the limitations and pointers to Future Directions. * allow noncopyable types to conform to `Sendable` * Add discussion of computed properties, and mention that `consuming get` cannot be paired with a setter * Add read/modify coroutines future direction * Update and rename NNNN-noncopyable-structs-and-enums.md to 0390-noncopyable-structs-and-enums.md --------- Co-authored-by: Kavon Farvardin Co-authored-by: Stephen Canon --- .../0390-noncopyable-structs-and-enums.md | 1476 +++++++++++++++++ 1 file changed, 1476 insertions(+) create mode 100644 proposals/0390-noncopyable-structs-and-enums.md diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md new file mode 100644 index 0000000000..68ad2dbb4e --- /dev/null +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -0,0 +1,1476 @@ +# `@noncopyable` structs and enums + +* Proposal: [SE-0390](0390-noncopyable-structs-and-enums.md) +* Authors: [Joe Groff](https://github.com/jckarter), [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Kavon Farvardin](https://github.com/kavon) +* Review Manager: [Stephen Canon](https://github.com/stephentyrone) +* Status: **Active review (February 21 ... March 7, 2023)** +* Implementation: on main as `@_moveOnly` behind the `-enable-experimental-move-only` option + +## Introduction + +This proposal introduces the concept of **noncopyable** types (also known +as "move-only" types). An instance of a noncopyable type always has unique +ownership, unlike normal Swift types which can be freely copied. + +Swift-evolution thread: [Noncopyable (or "move-only") structs and enums](https://forums.swift.org/t/pitch-noncopyable-or-move-only-structs-and-enums/61903) + +## Motivation + +All currently existing types in Swift are **copyable**, meaning it is possible +to create multiple identical, interchangeable representations of any value of +the type. However, copyable structs and enums are not a great model for +unique resources. Classes by contrast *can* represent a unique resource, +since an object has a unique identity once initialized, and only references to +that unique object get copied. However, because the references to the object are +still copyable, classes always demand *shared ownership* of the resource. This +imposes overhead in the form of heap allocation (since the overall lifetime of +the object is indefinite) and reference counting (to keep track of the number +of co-owners currently accessing the object), and shared access often +complicates or introduces unsafety or additional overhead into an object's +APIs. Swift does not yet have a mechanism for defining types that +represent unique resources with *unique ownership*. + +## Proposed solution + +We propose to allow for `struct` and `enum` types to be declared with the +`@noncopyable` attribute, which specifies that the declared type is +*noncopyable*. Values of noncopyable type always have unique ownership, and +can never be copied (at least, not using Swift's implicit copy mechanism). +Since values of noncopyable structs and enums have unique identities, they can +also have `deinit` declarations, like classes, which run automatically at the +end of the unique instance's lifetime. + +For example, a basic file descriptor type could be defined as: + +```swift +@noncopyable +struct FileDescriptor { + private var fd: Int32 + + init(fd: Int32) { self.fd = fd } + + func write(buffer: Data) { + buffer.withUnsafeBytes { + write(fd, $0.baseAddress!, $0.count) + } + } + + deinit { + close(fd) + } +} +``` + +Like a class, instances of this type can provide managed access to a file +handle, automatically closing the handle once the value's lifetime ends. Unlike +a class, no object needs to be allocated; only a simple struct containing the +file descriptor ID needs to be stored in the stack frame or aggregate value +that uniquely owns the instance. + +## Detailed design + +### Declaring noncopyable types + +A `struct` or `enum` type can be declared as noncopyable using the `@noncopyable` +attribute: + +```swift +@noncopyable +struct FileDescriptor { + private var fd: Int32 +} +``` + +If a `struct` has a stored property of noncopyable type, or an `enum` has +a case with an associated value of noncopyable type, then the containing type +must also be declared `@noncopyable`: + +```swift +@noncopyable +struct SocketPair { + var in, out: FileDescriptor +} + +@noncopyable +enum FileOrMemory { + // write to an OS file + case file(FileDescriptor) + // write to an array in memory + case memory([UInt8]) +} + +// ERROR: copyable value type cannot contain noncopyable members +struct FileWithPath { + var file: FileDescriptor + var path: String +} +``` + +Classes, on the other hand, may contain noncopyable stored properties without +themselves becoming noncopyable: + +```swift +class SharedFile { + var file: FileDescriptor +} +``` + +### Restrictions on use in generics + +Noncopyable types may have generic type parameters: + +```swift +// A type that reads from a file descriptor consisting of binary values of type T +// in sequence. +@noncopyable +struct TypedFile { + var rawFile: FileDescriptor + + func read() -> T { ... } +} + +let byteFile: TypedFile // OK +``` + +However, at this time, noncopyable types themselves are not allowed to be used +as a generic type. This means a noncopyable type _cannot_: + +- conform to any protocols, except for `Sendable`. +- serve as a type witness for an `associatedtype` requirement. +- be used as a type argument when instantiating generic types or calling generic functions. +- be cast to (or from) `Any` or any other existential. +- be accessed through reflection. +- appear in a tuple. + +The reasons for these restrictions and ways of lifting them are discussed under +Future Directions. The key implication of these restrictions is that a +noncopyable struct or enum is only a subtype of itself, because all other types +it might be compatible with for conversion would also permit copying. + +#### The `Sendable` exception + +The need for preventing noncopyable types from conforming to +protocols is rooted in the fact that all existing constrained generic types +(like `some P` types) and existentials (`any P` types) are assumed to be +copyable. Recording any conformances to these protocols would be invalid for +noncopyable types. + +But, an exception is made where noncopyable types can conform to `Sendable`. +Unlike other protocols, the `Sendable` marker protocol leaves no conformance +record in the output program. Thus, there will be no ABI impact if a future +noncopyable version of the `Sendable` protocol is created. + +The big benefit of allowing `Sendable` conformances is that noncopyable types +are compatible with concurrency. Keep in mind that despite their ability to +conform to `Sendable`, noncopyable structs and enums are still only a subtype +of themselves. That means when the noncopyable type conforms to `Sendable`, you +still cannot convert it to `any Sendable`, because copying that existential +would copy its underlying value: + +```swift +extension FileDescriptor: Sendable {} // OK + +@noncopyable struct RefHolder: Sendable { + var ref: Ref // ERROR: stored property 'ref' of 'Sendable'-conforming struct 'RefHolder' has non-sendable type 'Ref' +} + +func openAsync(_ path: String) async throws -> FileDescriptor {/* ... */} +func sendToSpace(_ s: some Sendable) {/* ... */} + +@MainActor func example() async throws { + // OK. FileDescriptor can cross actors because it is Sendable + var fd: FileDescriptor = try await openAsync("/dev/null") + + // ERROR: noncopyable types cannot be conditionally cast + // WARNING: cast from 'FileDescriptor' to unrelated type 'any Sendable' always fails + if let sendy: Sendable = fd as? Sendable { + + // ERROR: noncopyable types cannot be conditionally cast + // WARNING: cast from 'any Sendable' to unrelated type 'FileDescriptor' always fails + fd = sendy as! FileDescriptor + } + + // ERROR: noncopyable type 'FileDescriptor' cannot be used with generics + sendToSpace(fd) +} +``` + +#### Working around the generics restrictions + +Since a good portion of Swift's standard library rely on generics, there are a +a number of common types and functions that will not work with today's +noncopyable types: + +```swift +// ERROR: Cannot use noncopyable type FileDescriptor in generic type Optional +let x = Optional(FileDescriptor(open("/etc/passwd", O_RDONLY))) + +// ERROR: Cannot use noncopyable type FileDescriptor in generic type Array +let fds: [FileDescriptor] = [] + +// ERROR: Cannot use noncopyable type FileDescriptor in generic type Any +print(FileDescriptor(-1)) + +// ERROR: Noncopyable struct SocketEvent cannot conform to Error +@noncopyable enum SocketEvent: Error { + case requestedDisconnect(SocketPair) +} +``` + +For example, the `print` function expects to be able to convert its argument to +`Any`, which is a copyable value. Internally, it also relies on either +reflection or conformance to `CustomStringConvertible`. Since a noncopyable type +can't do any of those, a suggested workaround is to explicitly define a +conversion to `String`: + +```swift +extension FileDescriptor /*: CustomStringConvertible */ { + var description: String { + return "file descriptor #\(fd)" + } +} + +let fd = FileDescriptor(-1) +print(fd.description) +``` + +A more general kind of workaround to mix generics and noncopyable types +is to wrap the value in an ordinary class instance, which itself can participate +in generics. To transfer the noncopyable value in or out of the wrapper class +instance, using `Optional` for the class's field would be +ideal. But until that is supported, a concrete noncopyable enum can represent +the case where the value of interest was taken out of the instance: + +```swift +@noncopyable +enum MaybeFileDescriptor { + case some(FileDescriptor) + case none +} + +class WrappedFile { + var file: MaybeFileDescriptor + + enum Err: Error { case noFile } + + init(_ fd: consuming FileDescriptor) { + file = .some(fd) + } + + func consume() throws -> FileDescriptor { + if case let .some(fd) = file { // consume `self.file` + file = .none // must reinitialize `self.file` before returning + return fd + } + throw Err.noFile + } +} + +func example(_ fd1: consuming FileDescriptor, + _ fd2: consuming FileDescriptor) -> [WrappedFile] { + // create an array of descriptors + return [WrappedFile(fd1), WrappedFile(fd2)] +} +``` + +All of this boilerplate melts away once noncopyable types support generics. +Even before then, one major improvement would be to eliminate the need to define +types like `MaybeFileDescriptor` through a noncopyable `Optional` +(see Future Directions). + + +### Using values of `@noncopyable` type + +As the name suggests, values of noncopyable type cannot be copied, a major break +from most other types in Swift. Many operations are currently defined as +working as pass-by-value, and use copying as an implementation technique +to give that semantics, but they need to be defined more precisely in terms of +how they *borrow* or *consume* their operands in order to define their +effects on values that cannot be copied. + +We use the term **consume** to refer to an operation +that invalidates the value that it operates on. It may do this by directly +destroying the value, freeing its resources such as memory and file handles, +or forwarding ownership of the value to yet another owner who takes +responsibility for keeping it alive. Performing a consuming operation on +a noncopyable value generally requires having ownership of the value to begin +with, and invalidates the value the operation was performed on after it is +completed. + +We use the term **borrow** to refer to +a shared borrow of a single instance of a value; the operation that borrows +the value allows other operations to borrow the same value simultaneously, and +it does not take ownership of the value away from its current owner. This +generally means that borrowers are not allowed to mutate the value, since doing +so would invalidate the value as seen by the owner or other simultaneous +borrowers. Borrowers also cannot *consume* the value. They can, however, +initiate arbitrarily many additional borrowing operations on all or part of +the value they borrow. + +Both of these conventions stand in contrast to **mutating** (or **inout**) +operations, which take an *exclusive* borrow of their operands. The behavior +of mutating operations on noncopyable values is much the same as `inout` +parameters of copyable type today, which are already subject to the +"law of exclusivity". A mutating operation has exclusive access to its operand +for the duration of the operation, allowing it to freely mutate the value +without concern for aliasing or data races, since not even the owner may +access the value simultaneously. A mutating operation may pass its operand +to another mutating operation, but transfers exclusivity to that other operation +until it completes. A mutating operation may also pass its operand to +any number of borrowing operations, but cannot assume exclusivity while those +borrows are enacted; when the borrowing operations complete, the mutating +operation may assume exclusivity again. Unlike having true ownership of a +value, mutating operations give ownership back to the owner at the end of an +operation. A mutating operation therefore may consume the current value of its +operand, but if it does, it must replace it with a new value before completing. + +For copyable types, the distinction between borrowing and consuming operations +is largely hidden from the programmer, since Swift will implicitly insert +copies as needed to maintain the apparent value semantics of operations; a +consuming operation can be turned into a borrowing one by copying the value and +giving the operation the copy to consume, allowing the program to continue +using the original. This of course becomes impossible for values that cannot +be copied, forcing the distinction. + +Many code patterns that are allowed for copyable types also become errors for +noncopyable values because they would lead to conflicting uses of the same +value, without the ability to insert copies to avoid the conflict. For example, +a copyable value can normally be passed as an argument to the same function +multiple times, even to a `borrowing` and `consuming` parameter of the same +call, and the compiler will copy as necessary to make all of the function's +parameters valid according to their ownership specifiers: + +``` +func borrow(_: borrowing Value, and _: borrowing Value) {} +func consume(_: consuming Value, butBorrow _: borrowing Value) {} +let x = Value() +borrow(x, and: x) // this is fine, multiple borrows can share +consume(x, butBorrow: x) // also fine, we'll copy x to let a copy be consumed + // while the other is borrowed +``` + +By contrast, a noncopyable value *must* be passed by borrow or consumed, +without copying. This makes the second call above impossible for a noncopyable +`x`, since attempting to consume `x` would end the binding's lifetime while +it also needs to be borrowed: + +``` +func borrow(_: borrowing FileDescriptor, and _: borrowing FileDescriptor) {} +func consume(_: consuming FileDescriptor, butBorrow _: borrowing FileDescriptor) {} +let x = FileDescriptor() +borrow(x, and: x) // still OK to borrow multiple times +consume(x, butBorrow: x) // ERROR: consuming use of `x` would end its lifetime + // while being borrowed +``` + +A similar effect happens when `inout` parameters take noncopyable arguments. +Swift will copy the value of a variable if it is passed both by value and +`inout`, so that the by-value parameter receives a copy of the current value +while leaving the original binding available for the `inout` parameter to +exclusively access: + +``` +func update(_: inout Value, butBorrow _: borrow Value) {} +func update(_: inout Value, butConsume _: consume Value) {} +var x = Value() +update(&x, butBorrow: x) // this is fine, we'll copy `x` in the second parameter +update(&x, butConsume: x) // also fine, we'll also copy +``` + +But again, for a noncopyable value, this implicit copy is impossible, so +these sorts of calls become exclusivity errors: + +``` +func update(_: inout FileDescriptor, butBorrow _: borrow FileDescriptor) {} +func update(_: inout FileDescriptor, butConsume _: consume FileDescriptor) {} + +var y = FileDescriptor() +update(&y, butBorrow: y) // ERROR: cannot borrow `y` while exclusively accessed +update(&y, butConsume: y) // ERROR: cannot consume `y` while exclusively accessed +``` + +The following sections attempt to classify existing language operations +according to what ownership semantics they have when performed on noncopyable +values. + +### Consuming operations + +The following operations are consuming: + +- assigning a value to a new `let` or `var` binding, or setting an existing + variable or property to the binding: + + ```swift + let x = FileDescriptor() + let y = x + use(x) // ERROR: x consumed by assignment to `y` + ``` + + ```swift + var y = FileDescriptor() + let x = FileDescriptor() + y = x + use(x) // ERROR: x consumed by assignment to `y` + ``` + + ```swift + class C { + var property = FileDescriptor() + } + let c = C() + let x = FileDescriptor() + c.property = x + use(x) // ERROR: x consumed by assignment to `c.property` + ``` +- passing an argument to a `consuming` parameter of a function: + + ```swift + func consume(_: consuming FileDescriptor) {} + let x1 = FileDescriptor() + consume(x1) + use(x1) // ERROR: x1 consumed by call to `consume` + ``` + +- passing an argument to an `init` parameter that is not explicitly + `borrowing`: + + ```swift + @noncopyable + struct S { + var x: FileDescriptor, y: Int + } + let x = FileDescriptor() + let s = S(x: x, y: 219) + use(x) // ERROR: x consumed by `init` of struct `S` + ``` + +- invoking a `consuming` method on a value, or accessing a property of the + value through a `consuming get` or `consuming set` accessor: + + ```swift + extension FileDescriptor { + consuming func consume() {} + } + let x = FileDescriptor() + x.consume() + use(x) // ERROR: x consumed by method `consume` + ``` + +- explicitly consuming a value with the `consume` operator: + + ```swift + let x = FileDescriptor() + _ = consume x + use(x) // ERROR: x consumed by explicit `consume` + ``` + +- `return`-ing a value; + +- pattern-matching a value with `switch`, `if let`, or `if case`: + + ```swift + let x: Optional = getValue() + if let y = consume x { ... } + use(x) // ERROR: x consumed by `if let` + + @noncopyable + enum FileDescriptorOrBuffer { + case file(FileDescriptor) + case buffer(String) + } + + let x = FileDescriptorOrBuffer.file(FileDescriptor()) + + switch x { + case .file(let f): + break + case .buffer(let b): + break + } + + use(x) // ERROR: x consumed by `switch` + ``` + +- iterating a `Sequence` with a `for` loop: + + ```swift + let xs = [1, 2, 3] + for x in consume xs {} + use(xs) // ERROR: xs consumed by `for` loop + ``` + +(Although noncopyable types are not currently allowed to conform to +protocols, preventing them from implementing the `Sequence` protocol, +and cannot be used as generic parameters, preventing the formation of +`Optional` noncopyable types, these last two cases are listed for completeness, +since they would affect the behavior of other language features that +suppress implicit copying when applied to copyable types.) + +The `consume` operator can always transfer ownership of its operand when the +`consume` expression is itself the operand of a consuming operation. + +Consuming is flow-sensitive, so if one branch of an `if` or other control flow +consumes a noncopyable value, then other branches where the value +is not consumed may continue using it: + +```swift +let x = FileDescriptor() +guard let condition = getCondition() else { + consume(x) +} +// We can continue using x here, since only the exit branch of the guard +// consumed it +use(x) +``` + +### Borrowing operations + +The following operations are borrowing: + +- Passing an argument to a `func` or `subscript` parameter that does not + have an ownership modifier, or an argument to any `func`, `subscript`, or + `init` parameter which is explicitly marked `borrow`. The + argument is borrowed for the duration of the callee's execution. +- Borrowing a stored property of a struct or tuple borrows the struct or tuple + for the duration of the access to the stored property. This means that one + field of a struct cannot be borrowed while another is being mutated, as in + `call(struc.fieldA, &struc.fieldB)`. Allowing for fine-grained subelement + borrows in some circumstances is discussed as a Future Direction below. +- A stored property of a class may be borrowed using a dynamic exclusivity + check, to assert that there are no aliasing mutations attempted during the + borrow, as discussed under "Noncopyable stored properties in classes" below. +- Invoking a `borrowing` method on a value, or a method which is not annotated + as any of `borrowing`, `consuming` or `mutating`, borrows the `self` parameter + for the duration of the callee's execution. +- Accessing a computed property or subscript through `borrowing` or + `nonmutating` getter or setter borrows the `self` parameter for the duration + of the accessor's execution. +- Capturing an immutable local binding into a nonescaping closure borrows the + binding for the duration of the callee that receives the nonescaping closure. + +### Mutating operations + +The following operations are mutating uses: + +- Passing an argument to a `func` parameter that is `inout`. The argument is + exclusively accessed for the duration of the call. +- Projecting a stored property of a struct for mutation is a mutating use of + the entire struct. +- A stored property of a class may be mutated using a dynamic exclusivity + check, to assert that there are no aliasing mutations, as happens today. + For noncopyable properties, the assertion also enforces that no borrows + are attempted during the mutation, as discussed under "Noncopyable stored + properties in classes" below. +- Invoking a `mutating` method on a value is a mutating use of the `self` + parameter for the duration of the callee's execution. +- Accessing a computed property or subscript through a `mutating` getter and/or + setter is a mutating use of `self` for the duration of the accessor's + execution. +- Capturing a mutable local binding into a nonescaping closure is a mutating + use of the binding for the duration of the callee that receives the + nonescaping closure. + +### Declaring functions and methods with noncopyable parameters + +When noncopyable types are used as function parameters, the ownership +convention becomes a much more important part of the API contract. +As such, when a function parameter is declared with an noncopyable type, it +**must** declare whether the parameter uses the `borrowing`, `consuming`, or +`inout` convention: + +```swift +// Redirect a file descriptor +// Require exclusive access to the FileDescriptor to replace it +func redirect(_ file: inout FileDescriptor, to otherFile: borrowing FileDescriptor) { + dup2(otherFile.fd, file.fd) +} + +// Write to a file descriptor +// Only needs shared access +func write(_ data: [UInt8], to file: borrowing FileDescriptor) { + data.withUnsafeBytes { + write(file.fd, $0.baseAddress, $0.count) + } +} + +// Close a file descriptor +// Consumes the file descriptor +func close(file: consuming FileDescriptor) { + close(file.fd) +} +``` + +Methods of the noncopyable type are considered to be `borrowing` unless +declared `mutating` or `consuming`: + +```swift +extension FileDescriptor { + mutating func replace(with otherFile: borrowing FileDescriptor) { + dup2(otherFile.fd, self.fd) + } + + // borrowing by default + func write(_ data: [UInt8]) { + data.withUnsafeBytes { + write(file.fd, $0.baseAddress, $0.count) + } + } + + consuming func close() { + close(fd) + } +} +``` + +### Declaring properties of noncopyable type + +A class or noncopyable struct may declare stored `let` or `var` properties of +noncopyable type. A noncopyable `let` stored property may only be borrowed, +whereas a `var` stored property may be both borrowed and mutated. Stored +properties cannot generally be consumed because doing so would leave the +containing aggregate in an invalid state. + +Any type may also declare computed properties of noncopyable type. The `get` +accessor returns an owned value that the caller may consume, like a function +would. The `set` accessor receives its `newValue` as a `consuming` parameter, +so the setter may consume the parameter value to update the containing +aggregate. + +Accessors may use the `consuming` and `borrowing` declaration modifiers to +affect the ownership of `self` while the accessor executes. `consuming get` +is particularly useful as a way of forwarding ownership of part of an aggregate, +such as to take ownership away from a wrapper type: + +``` +@noncopyable +struct FileDescriptorWrapper { + private var _value: FileDescriptor + + var value: FileDescriptor { + consuming get { return _value } + } +} +``` + +However, a `consuming get` cannot be paired with a setter when the containing +type is `@noncopyable`, because invoking the getter consumes the aggregate, +leaving nothing to write a modified value back to. + +Because getters return owned values, non-`consuming` getters generally cannot +be used to wrap noncopyable stored properties, since doing so would require +copying the value out of the aggregate: + +``` +class File { + private var _descriptor: FileDescriptor + + var descriptor: FileDescriptor { + return _descriptor // ERROR: attempt to copy `_descriptor` + } +} +``` + +These limitations could be addressed in the future by exposing the ability for +computed properties to also provide "read" and "modify" coroutines, which would +have the ability to yield borrowing or mutating access to properties without +copying them. + +### Using stored properties and enum cases of `@noncopyable` type + +When classes or `@noncopyable` types contain members that are of `@noncopyable` +type, then the container is the unique owner of the member value. Outside of +the type's definition, client code cannot perform consuming operations on +the value, since it would need to take away the container's ownership to do +so: + +``` +@noncopyable +struct Inner {} + +@noncopyable +struct Outer { + var inner = Inner() +} + +let outer = Outer() +let i = outer.inner // ERROR: can't take `inner` away from `outer` +``` + +However, when code has the ability to mutate the member, it may freely modify, +reassign, or replace the value in the field: + +``` +var outer = Outer() +let newInner = Inner() +// OK, transfers ownership of `newInner` to `outer`, destroying its previous +// value +outer.inner = newInner +``` + +Note that, as currently defined, `switch` to pattern-match an `enum` is a +consuming operation, so it can only be performed inside `consuming` methods +on the type's original definition: + +``` +@noncopyable +enum OuterEnum { + case inner(Inner) + case file(FileDescriptor) +} + +// Error, can't partially consume a value outside of its definition +let enum = OuterEnum.inner(Inner()) +switch enum { +case .inner(let inner): + break +default: + break +} +``` + +Being able to borrow in pattern matches would address this shortcoming. + +### Noncopyable stored properties in classes + +Since objects may have any number of simultaneous references, Swift uses +dynamic exclusivity checking to prevent simultaneous writes of the same +stored property. This dynamic checking extends to borrows of noncopyable +stored properties; the compiler will attempt to diagnose obvious borrowing +failures, as it will for local variables and value types, but a runtime error +will occur if an uncaught exclusivity error occurs, such as an attempt to mutate +an object's stored property while it is being borrowed: + +``` +class Foo { + var fd: FileDescriptor + + init(fd: FileDescriptor) { self.fd = fd } +} + +func update(_: inout FileDescriptor, butBorrow _: borrow FileDescriptor) {} + +func updateFoo(_ a: Foo, butBorrowFoo b: Foo) { + update(&a.fd, butBorrow: b.fd) +} + +let foo = Foo(fd: FileDescriptor()) + +// Will trap at runtime when foo.fd is borrowed and mutated at the same time +updateFoo(foo, butBorrowFoo: foo) +``` + +`let` properties do not allow mutating accesses, and this continues to hold for +noncopyable types. The value of a `let` property in a class therefore does not +need dynamic checking, even if the value is noncopyable; the value behaves as +if it is always borrowed, since there may potentially be a borrow through +some reference to the object at any point in the program. Such values can +thus never be consumed or mutated. + +The dynamic borrow state of properties is tracked independently for every +stored property in the class, so it is safe to mutate one property while other +properties of the same object are also being mutated or borrowed: + +``` +class SocketTriple { + var in, middle, out: FileDescriptor +} + +func update(_: inout FileDescriptor, and _: inout FileDescriptor, + whileBorrowing _: borrowing FileDescriptor) {} + +// This is OK +let object = SocketTriple(...) +update(&object.in, and: &object.out, whileBorrowing: object.middle) +``` + +This dynamic tracking, however, cannot track accesses at finer resolution +than properties, so in circumstances where we might otherwise eventually be +able to support independent borrowing of fields in structs, tuples, and enums, +that support will not extend to fields within class properties, since the +entire property must be in the borrowing or mutating state. + +Dynamic borrowing or mutating accesses require that the enclosing object be +kept alive for the duration of the assertion of the access. Normally, this +is transparent to the developer, as the compiler will keep a copy of a +reference to the object retained while these accesses occur. However, if +we introduce noncopyable bindings to class references, such as [the `borrow` +and `inout` bindings](https://forums.swift.org/t/pitch-borrow-and-inout-declaration-keywords/62366) +currently being pitched, this would manifest as a borrow of the noncopyable +reference, preventing mutation or consumption of the reference during +dynamically-asserted accesses to its properties: + +``` +class SocketTriple { + var in, middle, out: FileDescriptor +} + +func borrow(_: borrowing FileDescriptor, + whileReplacingObject _: inout SocketTriple) {} + +var object = SocketTriple(...) + +// This is OK, since ARC will keep a copy of the `object` reference retained +// while `object.in` is borrowed +borrow(object.in, whileReplacingObject: &object) + +inout objectAlias = &object + +// This is an error, since we aren't allowed to implicitly copy through +// an `inout` binding, and replacing `objectAlias` without keeping a copy +// retained might invalidate the object while we're accessing it. +borrow(objectAlias.in, whileReplacingObject: &objectAlias) +``` + +### Noncopyable variables captured by escaping closures + +Nonescaping closures have scoped lifetimes, so they can borrow their captures, +as noted in the "borrowing operations" and "consuming operations" sections +above. Escaping closures, on the other hand, have indefinite lifetimes, since +they can be copied and passed around arbitrarily, and multiple escaping closures +can capture and access the same local variables alongside the local context +from which those captures were taken. Variables captured by escaping closures +thus behave like class properties; immutable captures are treated as always +borrowed both inside the closure body and in the capture's original context +after the closure was formed. + +``` +func escape(_: @escaping () -> ()) {...} + +func borrow(_: borrowing FileDescriptor) {} +func consume(_: consuming FileDescriptor) {} + +func foo() { + let x = FileDescriptor() + + escape { + borrow(x) // OK + consume(x) // ERROR: cannot consume captured variable + } + + // OK + borrow(x) + + // ERROR: cannot consume variable after it's been captured by an escaping + // closure + consume(x) +} +``` + +Mutable captures are subject to dynamic exclusivity checking like class +properties are. + +``` +var escapedClosure: (@escaping (inout FileDescriptor) -> ())? + +func foo() { + var x = FileDescriptor() + + escapedClosure = { _ in borrow(x) } + + // Runtime error when exclusive access to `x` dynamically conflicts + // with attempted borrow of `x` during `escapedClosure`'s execution + escapedClosure!(&x) +} +``` + +### Deinitializers + +A `@noncopyable` struct or enum may declare a `deinit`, which will run +implicitly when the lifetime of the value ends (unless explicitly suppressed +as noted below): + +```swift +@noncopyable +struct FileDescriptor { + private var fd: Int32 + + deinit { + close(fd) + } +} +``` + +Like a class `deinit`, a struct or enum `deinit` may not propagate any uncaught +errors. The body of `deinit` has exclusive access to `self` for the duration +of its execution, so `self` behaves as in a `mutating` method; it may be +modified by the body of `deinit`, but must remain valid until the end of the +deinit. (Allowing for partial invalidation inside a `deinit` is explored as +a future direction.) + +A value's lifetime ends, and its `deinit` runs if present, in the following +circumstances: + +- For a local `var` or `let` binding, or `consuming` function parameter, that + is not itself consumed, `deinit` runs after the last non-consuming use. + If, on the other hand, the binding is consumed, then responsibility for + deinitialization gets forwarded to the consumer (which may in turn forward + it somewhere else). + + ```swift + do { + var x = FileDescriptor(42) + + x.close() // consuming use + // x's deinit doesn't run here (but might run inside `close`) + } + + do { + var x = FileDescriptor(42) + x.write([1,2,3]) // borrowing use + // x's deinit runs here + + print("done writing") + } + ``` + +- When a `struct`, `enum`, or `class` contains a member of noncopyable type, + the member is destroyed, and its `deinit` is run, after the container's + `deinit` if any runs. + + ```swift + @noncopyable + struct Inner { + deinit { print("destroying inner") } + } + + @noncopyable + struct Outer { + var inner = Inner() + deinit { print("destroying outer") } + } + + do { + _ = Outer() + } + ``` + + will print: + + ```swift + destroying outer + destroying inner + ``` + +### Suppressing `deinit` in a `consuming` method + +It is often useful for noncopyable types to provide alternative ways to consume +the resource represented by the value besides the `deinit`. However, +under normal circumstances, a `consuming` method will still invoke the type's +`deinit` after the last use of `self`, which is undesirable when the method's +own logic already invalidates the value: + +```swift +@noncopyable +struct FileDescriptor { + private var fd: Int32 + + deinit { + close(fd) + } + + consuming func close() { + close(fd) + + // The lifetime of `self` ends here, triggering `deinit` (and another call to `close`)! + } +} +``` + +In the above example, the double-close could be avoided by having the +`close()` method do nothing on its own and just allow the `deinit` to +implicitly run. However, we may want the method to have different behavior +from the deinit, such as raising an error (which a normal `deinit` is unable to +do) if the `close` system call triggers an OS error : + +```swift +@noncopyable +struct FileDescriptor { + private var fd: Int32 + + consuming func close() throws { + // POSIX close may raise an error (which still invalidates the + // file descriptor, but may indicate a condition worth handling) + if close(fd) != 0 { + throw CloseError(errno) + } + + // We don't want to trigger another close here! + } +} +``` + +or it could be useful to take manual control of the file descriptor back from +the type, such as to pass to a C API that will take care of closing it: + +```swift +@noncopyable +struct FileDescriptor { + // Take ownership of the C file descriptor away from this type, + // returning the file descriptor without closing it + consuming func take() -> Int32 { + return fd + + // We don't want to trigger close here! + } +} +``` + +We propose to introduce a special operator, `forget self`, which ends the +lifetime of `self` without running its `deinit`: + +```swift +@noncopyable +struct FileDescriptor { + // Take ownership of the C file descriptor away from this type, + // returning the file descriptor without closing it + consuming func take() -> Int32 { + let fd = self.fd + forget self + return fd + } +} +``` + +`forget self` can only be applied to `self`, in a consuming method defined in +the type's defining module. (This is in contrast to Rust's similar special +function, [`mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html), +which is a standalone function which can be applied to any value, anywhere. +Although the Rust documentation notes that this operation is "safe" on the +principle that destructors may not run at all, due to reference cycles, +process termination, etc., in practice the ability to forget arbitrary values +creates semantic issues for many Rust APIs, particularly when there are +destructors on types with lifetime dependence on each other like `Mutex` +and `LockGuard`. As such, we +think it is safer to restrict the ability to forget a value to the +core API of its type. We can relax this restriction if experience shows a +need to.) + +Even with the ability to `forget self`, care would still need be taken when +writing destructive operations to avoid triggering the deinit on alternative +exit paths, such as early `return`s, `throw`s, or implicit propagation of +errors from `try` operations. For instance, if we write: + +```swift +@noncopyable +struct FileDescriptor { + private var fd: Int32 + + consuming func close() throws { + // POSIX close may raise an error (which still invalidates the + // file descriptor, but may indicate a condition worth handling) + if close(fd) != 0 { + throw CloseError(errno) + // !!! Oops, we didn't forget self on this path, so we'll deinit! + } + + // We don't need to deinit self anymore + forget self + } +} +``` + +then the `throw` path exits the method without `forget`-ing `self`, so +`deinit` will still execute if an error occurs. To avoid this mistake, we +propose that if any path through a method uses `forget self`, then **every** +path must choose either to `forget` or to explicitly `consume self` using +the standard `deinit`. This will make +the above code an error, alerting that the code should be rewritten to ensure +`forget self` always executes: + +```swift +@noncopyable +struct FileDescriptor { + private var fd: Int32 + + consuming func close() throws { + // Save the file descriptor and give up ownership of it + let fd = self.fd + forget self + + // We can now use `fd` below without worrying about `deinit`: + + // POSIX close may raise an error (which still invalidates the + // file descriptor, but may indicate a condition worth handling) + if close(fd) != 0 { + throw CloseError(errno) + } + } +} +``` + +The [consume operator](https://github.com/apple/swift-evolution/blob/main/proposals/0377-parameter-ownership-modifiers.md) +must be used to explicitly end the value's lifetime using its `deinit` if +`forget` is used to conditionally destroy the value on other paths through +the method. + +``` +@noncopyable +struct MemoryBuffer { + private var address: UnsafeRawPointer + + init(size: Int) throws { + guard let address = malloc(size) else { + throw MallocError() + } + self.address = address + } + + deinit { + free(address) + } + + consuming func takeOwnership(if condition: Bool) -> UnsafeRawPointer? { + if condition { + // Save the memory buffer and give it to the caller, who + // is promising to free it when they're done. + let address = self.address + forget self + return address + } else { + // We still want to free the memory if we aren't giving it away. + _ = consume self + return nil + } + } +} +``` + +## Source compatibility + +For existing Swift code, this proposal is additive. + +## Effect on ABI stability + +### Adding or removing `@noncopyable` breaks ABI + +An existing copyable struct or enum cannot be made `@noncopyable` without +breaking ABI, since existing clients may copy values of the type. + +Ideally, we would allow noncopyable types to become copyable without breaking +ABI; however, we cannot promise this, due to existing implementation choices we +have made in the ABI that cause the copyability of a type to have unavoidable +knock-on effects. In particular, when properties are declared in classes, +protocols, or public non-`@frozen` structs, we define the property's ABI to use +accessors even if the property is stored, with the idea that it should be +possible to change a property's implementation to change it from a stored to +computed property, or vice versa, without breaking ABI. + +The accessors used as ABI today are the traditional `get` and `set` +computed accessors, as well as a `_modify` coroutine which can optimize `inout` +operations and projections into stored properties. `_modify` and `set` are +not problematic for noncopyable types. However, `get` behaves like a +function, producing the property's value by returning it like a function would, +and returning requires *consuming* the return value to transfer it to the +caller. This is not possible for noncopyable stored properties, since the +value of the property cannot be copied in order to return a copy without +invalidating the entire containing struct or object. + +Therefore, properties of noncopyable type need a different ABI in order to +properly abstract them. In particular, instead of exposing a `get` accessor +through abstract interfaces, they must use a `_read` coroutine, which is the +read-only analog to `_modify`, allowing the implementation to yield a borrow of +the property value in-place instead of returning by value. This allows for +noncopyable stored properties to be exposed while still being abstracted enough +that they can be replaced by a computed implementation, since a `get`-based +implementation could still work underneath the `read` coroutine by evaluating +the getter, yielding a borrow of the returned value, then disposing of the +temporary value. + +As such, we cannot simply say that making a noncopyable type copyable is an +ABI-safe change, since doing so will have knock-on effects on the ABI of any +properties of the type. We could potentially provide a "born noncopyable" +attribute to indicate that a copyable type should use the noncopyable ABI +for any properties, as a way to enable the evolution into a copyable type +while preserving existing ABI. However, it also seems unlikely to us that many +types would need to evolve between being copyable or not frequently. + +### Adding, removing, or changing `deinit` in a struct or enum + +An noncopyable type that is not `@frozen` can add or remove its deinit without +affecting the type's ABI. If `@frozen`, a deinit cannot be added or removed, +but the deinit implementation may change (if the deinit is not additionally +`@inlinable`). + +### Adding noncopyable fields to classes + +A class may add fields of noncopyable type without changing ABI. + +## Effect on API resilience + +Introducing new APIs using noncopyable types is an additive change. APIs that +adopt noncopyable types have some notable restrictions on how they can further +evolve while maintaining source compatibility. + +A noncopyable type can be made copyable while generally maintaining source +compatibility. Values in client source would acquire normal ARC lifetime +semantics instead of eager-move semantics when those clients are recompiled +with the type as copyable, and that could affect the observable order of +destruction and cleanup. Since copyable value types cannot directly define +`deinit`s, being able to observe these order differences is unlikely, but not +impossible when references to classes are involved. + +A `consuming` parameter of noncopyable type can be changed into a `borrowing` +parameter without breaking source for clients (and likewise, a `consuming` +method can be made `borrowing`). Conversely, changing +a `borrowing` parameter to `consuming` may break client source. (Either direction +is an ABI breaking change.) This is because a consuming use is required to +be the final use of a noncopyable value, whereas a borrowing use may or may not +be. + +Adding or removing a `deinit` to a noncopyable type does not affect source +for clients. + +## Alternatives considered + +### Naming the attribute "move-only" + +We have frequently referred to these types as "move-only types" in various +vision documents. However, as we've evolved related proposals like the +`consume` operator and parameter modifiers, the community has drifted away +from exposing the term "move" in the language elsewhere. When explaining these +types to potential users, we've also found that the name "move-only" incorrectly +suggests that being noncopyable is a new capability of types, and that there +should be generic functions that only operate on "move-only" types, when really +the opposite is the case: all existing types in Swift today conform to +effectively an implicit "Copyable" requirement, and what this feature does is +allow types not to fulfill that requirement. When generics grow support for +move-only types, then generic functions and types that accept noncopyable +type parameters will also work with copyable types, since copyable types +are strictly more capable.This proposal prefers the term "noncopyable" to make +the relationship to an eventual `Copyable` constraint, and the fact that annotated +types lack the ability to satisfy this constraint, more explicit. + +### Spelling as a generic constraint + +It's a reasonable question why declaring a type as noncopyable isn't spelled +like a protocol constraint: + +``` +struct Foo: NonCopyable {} +``` + +As noted in the previous discussion, an issue with this notation is that it +implies that `NonCopyable` is a new capability or requirement, rather than +really being the lack of a `Copyable` capability. For an example of why +this might be misleading, consider what would happen if we expand +standard library collection types to support noncopyable elements. Value types +like `Array` and `Dictionary` would become copyable only when the elements they +contain are copyable. However, we cannot write this in terms of `NonCopyable` +conditional requirements, since if we write: + +``` +extension Dictionary: NonCopyable where Key: NonCopyable, Value: NonCopyable {} +``` + +this says that the dictionary is noncopyable only when both the key and value +are noncopyable, which is wrong because we also can't copy the dictionary if only +the keys or only the values are noncopyable. If we flip the constraint to +`Copyable`, the correct thing would fall out naturally: + +``` +extension Dictionary: Copyable where Key: Copyable, Value: Copyable {} +``` + +However, for progressive disclosure and source compatibility reasons, we still +want the majority of types to be `Copyable` by default, without making them +explicitly declare it; noncopyable types are likely to remain the exception +rather than the rule, with automatic lifetime management via ARC by the +compiler being sufficient for most code like it is today. + +We could conversely borrow another idea from Rust, which uses the syntax +`?Trait` to declare that a normally implicit trait is not required by +a generic declaration, or not satisfied by a concrete type. So in Swift we +might write: + +``` +struct Foo: ?Copyable { +} +``` + +`Copyable` is currently the only such implicit constraint we are considering, +so the `@noncopyable` attribute is appealing as a specific solution to address +this case. If we were to consider making other requirements implicit (perhaps +`Sendable` in some situations?) then a more general opt-out syntax would be +called for. + +### English language bikeshedding + +Some dictionaries specify that "copiable" is the standard spelling for "able to +copy", although the Oxford English Dictionary and Merriam-Webster both also +list "copyable" as an accepted alternative. We prefer the more regular "copyable" +spelling. + +The negation could just as well be spelled `@uncopyable` instead of `@noncopyable`. +Swift has precedent for favoring `non-` in modifiers, including `@nonescaping` +parameters and and `nonisolated` actor members, so we choose to follow that +precedent. + +## Future directions + +### Noncopyable tuples + +It should be possible for a tuple to contain noncopyable elements, rendering +the tuple noncopyable if any of its elements are. Since tuples' structure is +always known, it would be reasonable to allow for the elements within a tuple +to be independently borrowed, mutated, and consumed, as the language allows +today for the elements of a tuple to be independently mutated via `inout` +accesses. (Due to the limitations of dynamic exclusivity checking, this would +not be possible for class properties, globals, and escaping closure captures.) + +### Noncopyable `Optional` + +This proposal initiates support for noncopyable types without any support for +generics at all, which precludes their use in most standard library types, +including `Optional`. We expect the lack of `Optional` support in particular +to be extremely limiting, since `Optional` can be used to manage dynamic +consumption of noncopyable values in situations where the language's static +rules cannot soundly support consumption. For instance, the static rules above +state that a stored property of a class can never be consumed, because it is +not knowable if other references to an object exist that expect the property +to be inhabited. This could be avoided using `Optional` with `mutating` +operation that forwards ownership of the `Optional` value's payload, if any, +writing `nil` back. Eventually this could be written as an extension method +on `Optional`: + +``` +@noncopyable +extension Optional { + mutating func take() -> Wrapped { + switch self { + case .some(let wrapped): + self = nil + return wrapped + case .none: + fatalError("trying to take from an Optional that's already empty") + } + } +} + +class Foo { + var fd: FileDescriptor? + + func close() { + // We normally would not be able to close `fd` except via the + // object's `deinit` destroying the stored property. But using + // `Optional` assignment, we can dynamically end the value's lifetime + // here. + fd = nil + } + + func takeFD() -> FileDescriptor { + // We normally would not be able to forward `fd`'s ownership to + // anyone else. But using + // `Optional.take`, we can dynamically end the value's lifetime + // here. + return fd.take() + } +} +``` + +Without `Optional` support, the alternative would be for every noncopyable type +to provide its own ad-hoc `nil`-like state, which would be very unfortunate, +and go against Swift's general desire to encourage structural code correctness +by making invalid states unrepresentable. Therefore, `Optional` is likely to +be worth considering as a special case for noncopyable support, ahead of full +generics support for noncopyable types. + +### Generics support for noncopyable types + +This proposal comes with an admittedly severe restriction that noncopyable types +cannot conform to protocols or be used at all as type arguments to generic +functions or types, including common standard library types like `Optional` +and `Array`. All generic parameters in Swift today carry an implicit assumption +that the type is copyable, and it is another large language design project to +integrate the concept of noncopyable types into the generics system. Full +integration will very likely also involve changes to the Swift runtime and +standard library to accommodate noncopyable types in APIs that weren't +originally designed for them, and this integration might then have backward +deployment restrictions. We believe that, even with these restrictions, +noncopyable types are a useful self-contained addition to the language for +safely and efficiently modeling unique resources, and this subset of the feature +also has the benefit of being adoptable without additional runtime requirements, +so developers can begin making use of the feature without giving up backward +compatibility with existing Swift runtime deployments. + +### Conditionally copyable types + +This proposal states that a type, including one with generic parameters, is +currently always copyable or always noncopyable. However, some types may +eventually be generic over copyable and non-copyable types, with the ability +to be copyable for some generic arguments but not all. A simple case might be +a tuple-like `Pair` struct: + +``` +@noncopyable +// : ?Copyable is strawman syntax for declaring T and U don't require copying +struct Pair { + var first: T + var second: U +} +``` + +We will need a way to express this conditional copyability, perhaps using +conditional conformance style declarations: + +``` +extension Pair: Copyable where T: Copyable, U: Copyable {} +``` + +### Finer-grained destructuring in `consuming` methods and `deinit` + +As currently specified, noncopyable types are (outside of `init` implementations) +always either fully initialized or fully destroyed, without any support +for incremental destruction inside of `consuming` methods or deinits. A +`deinit` may modify, but not invalidate, `self`, and a `consuming` method may +`forget self`, forward ownership of all of `self`, or destroy `self`, but cannot +yet partially consume parts of `self`. This would be particularly useful for +types that contain other noncopyable types, which may want to relinquish +ownership of some or all of the resources owned by those members. In the current +proposal, this isn't possible without allowing for an intermediate invalid +state: + +```swift +@noncopyable +struct SocketPair { + let input, output: FileDescriptor + + // Gives up ownership of the output end, closing the input end + consuming func takeOutput() -> FileDescriptor { + // We would like to do something like this, taking ownership of + // `self.output` while leaving `self.input` to be destroyed. + // However, we can't do this without being able to either copy + // `self.output` or partially invalidate `self` + let output = self.output + forget self + return output + } +} +``` + +Analogously to how `init` implementations use a "definite initialization" +pass to allow the value to initialized field-by-field, we can implement the +inverse dataflow pass to allow `deinit` implementations, as well as `consuming` +methods that `forget self`, to partially invalidate `self`. + +### `read` and `modify` accessor coroutines for computed properties. + +The current computed property model allows for properties to provide a getter, +which returns the value of the property on read to the caller as an owned value, +and optionally a setter, which receives the `newValue` of the property as +a parameter with which to update the containing type's state. This is +sometimes inefficient for value types, since the get/set pattern requires +returning a copy, modifying the copy, then passing the copy back to the setter +in order to model an in-place update, but it also limits what computed +properties can express for noncopyable types. Because a getter has to return +by value, it cannot pass along the value of a stored noncopyable property +without also destroying the enclosing aggregate, so `get`/`set` cannot be used +to wrap logic around access to a stored noncopyable property. + +The Swift stable ABI for properties internally uses **accessor coroutines** +to allow for efficient access to stored properties, while still providing +abstraction that allows library evolution to change stored properties into +computed and back. These coroutines **yield** access to a value in-place for +borrowing or mutating, instead of passing copies of values back and forth. +We can expose the ability for code to implement these coroutines directly, +which is a good optimization for copyable value types, but also allows for +more expressivity with noncopyable properties. From 5d075b86d57e3436b223199bd314b2642e30045f Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 21 Feb 2023 10:29:44 -0500 Subject: [PATCH 2999/4563] Update 0390-noncopyable-structs-and-enums.md (#1958) Add review links --- proposals/0390-noncopyable-structs-and-enums.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index 68ad2dbb4e..36aa1eb4ff 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -5,6 +5,7 @@ * Review Manager: [Stephen Canon](https://github.com/stephentyrone) * Status: **Active review (February 21 ... March 7, 2023)** * Implementation: on main as `@_moveOnly` behind the `-enable-experimental-move-only` option +* Review: [(pitch)](https://forums.swift.org/t/pitch-noncopyable-or-move-only-structs-and-enums/61903)[(review)](https://forums.swift.org/t/se-0390-noncopyable-structs-and-enums/63258) ## Introduction @@ -12,8 +13,6 @@ This proposal introduces the concept of **noncopyable** types (also known as "move-only" types). An instance of a noncopyable type always has unique ownership, unlike normal Swift types which can be freely copied. -Swift-evolution thread: [Noncopyable (or "move-only") structs and enums](https://forums.swift.org/t/pitch-noncopyable-or-move-only-structs-and-enums/61903) - ## Motivation All currently existing types in Swift are **copyable**, meaning it is possible From 79138de1273acedf44fb1948284d70212efddcc5 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 22 Feb 2023 09:05:38 -0800 Subject: [PATCH 3000/4563] [SE-0373] Mark proposal as implemented in Swift 5.8 (#1959) --- proposals/0373-vars-without-limits-in-result-builders.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0373-vars-without-limits-in-result-builders.md b/proposals/0373-vars-without-limits-in-result-builders.md index 61594706b5..ec428914b2 100644 --- a/proposals/0373-vars-without-limits-in-result-builders.md +++ b/proposals/0373-vars-without-limits-in-result-builders.md @@ -3,7 +3,7 @@ * Proposal: [SE-0373](0373-vars-without-limits-in-result-builders.md) * Author: [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5.8)** * Implementation: [apple/swift#60839](https://github.com/apple/swift/pull/60839) * Review: ([pitch](https://forums.swift.org/t/pitch-lift-all-limitations-on-variables-in-result-builders/60460)) ([review](https://forums.swift.org/t/se-0373-lift-all-limitations-on-variables-in-result-builders/60592)) ([acceptance](https://forums.swift.org/t/accepted-se-0373-lift-all-limitations-on-variables-in-result-builders/61041)) From 5fe1578d4f748242030c7c1b6ac19885944c7c2a Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 22 Feb 2023 10:07:51 -0800 Subject: [PATCH 3001/4563] Mark SE-0376 as implemented in Swift 5.8 (#1960) --- proposals/0376-function-back-deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index 18efaae57b..8931429555 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -5,7 +5,7 @@ * Implementation: [apple/swift#41271](https://github.com/apple/swift/pull/41271), [apple/swift#41348](https://github.com/apple/swift/pull/41348), [apple/swift#41416](https://github.com/apple/swift/pull/41416), [apple/swift#41612](https://github.com/apple/swift/pull/41612) as the underscored attribute `@_backDeploy` * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) * Review: ([pitch](https://forums.swift.org/t/pitch-function-back-deployment/55769)) ([review](https://forums.swift.org/t/se-0376-function-back-deployment/61015)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0376-function-back-deployment/61507)) ([second review](https://forums.swift.org/t/se-0376-second-review-function-back-deployment/61671)) ([returned for revision (second review)](https://forums.swift.org/t/returned-for-revision-se-0376-second-review-function-back-deployment/62374))([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0376-function-back-deployment/62905)) -* Status: **Accepted** +* Status: **Implemented (Swift 5.8)** ## Introduction From 30e769fb765a5050203107ce7333db4f4d3e13c2 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 22 Feb 2023 10:42:01 -0800 Subject: [PATCH 3002/4563] Mark SE-0375 as implemented in Swift 5.8 (#1961) --- proposals/0375-opening-existential-optional.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0375-opening-existential-optional.md b/proposals/0375-opening-existential-optional.md index 97b6fc46ee..f49ac87416 100644 --- a/proposals/0375-opening-existential-optional.md +++ b/proposals/0375-opening-existential-optional.md @@ -3,7 +3,7 @@ * Proposal: [SE-0375](0375-opening-existential-optional.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Accepted** +* Status: **Implemented (Swift 5.8)** * Implementation: [apple/swift#61321](https://github.com/apple/swift/pull/61321) * Review: ([pitch](https://forums.swift.org/t/mini-pitch-for-se-0352-amendment-allow-opening-an-existential-argument-to-an-optional-parameter/60501)) ([review](https://forums.swift.org/t/se-0375-opening-existential-arguments-to-optional-parameters/60802)) ([acceptance](https://forums.swift.org/t/accepted-se-0375-opening-existential-arguments-to-optional-parameters/61045)) From 5f665af4e78faaa50ed03d01617d68355b469c1f Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 27 Feb 2023 22:59:56 +0900 Subject: [PATCH 3003/4563] Assertion, equality discussion and Updates --- Custom Executor updates.md | 236 +++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 Custom Executor updates.md diff --git a/Custom Executor updates.md b/Custom Executor updates.md new file mode 100644 index 0000000000..b5be70b298 --- /dev/null +++ b/Custom Executor updates.md @@ -0,0 +1,236 @@ +## Custom Executor updates + +### Assert, precondition and assume APIs + +All these APIs should be about executors and isolation domains. + +> Note: We don't actually have the "current actor" at all ABI-wise. We only have the current executor, which are unique for default actors, but when sharing executors this is no longer true. This might be problematic for tracing systems etc, but for the current assertions it should be enough... + +The custom actor executors pitch proposed we offer those two assertion APIs, and that we do **not** offer any way to "get current executor" publicly. Maybe we'll offer this someday, but for now we don't have a strong reason to expose this and we'd like to push people to model their code using actors, i.e. "an actor on a specific executor", rather than grab executors and use them directly just as something to throw work onto. + +The current executors proposal includes the following, and I propose we keep them as-is (or bikeshed names a bit): + +```swift +// Precondition + +public func preconditionTaskOnExecutor( + _ executor: some Executor, + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) + +public func preconditionTaskIsOnExecutor( + _ actor: some Actor, + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) + +// Assert (if we really want to make less API, we could skip the assert ones, but I think they're useful) + +public func assertTaskOnExecutor( + _ executor: some Executor, + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) + +public func assertOnActorExecutor( + _ actor: some Actor, + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) +``` + +I propose that we offer those assert/precondition because we can do a better job at error reporting than if we just gave people the `isTaskOnExecutor(some executor)` because we can offer a message like `"Was on executor ... but expected ..."`. + +And the `assume...` API, specifically for MainActor right now: + +```swift +// Assume + +func assumeOnMainActorExecutor( + _ operation: @MainActor () throws -> T, + file: StaticString = #fileID, line: UInt = #line +) rethrows -> T + +// TODO: We could offer a macro for all kinds of global actors, +// but we can't "just" implement it without a macro since the `@ACTOR () -> T` we can't abstract over +``` + +And we only offer the `assume` version that _does_ check (i.e. precondition-style) if we are on the main actor's executot. + +> Note: We do not offer any API to "get current executor" explicitly. I personally think steering people towards using actors as isolation domains, and expressing executors as "the ont that actor X is using" is useful enough, and we allow for this. + +Do note that users have no way of obtaining the specific "main actor executor" that conforms to SerialExecutor, we only ever expose it as the wrapped `UnownedExecutor`: + +```swift +@globalActor public final actor MainActor: GlobalActor { + public static let shared = MainActor() + + public nonisolated var unownedExecutor: UnownedSerialExecutor { + return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) + } + + public static var sharedUnownedExecutor: UnownedSerialExecutor { + return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) + } +} +``` + +This does mean however that developers can write these assertions: + +```swift +func sync() { + assertOnActorExecutor(MainActor.shared, "I sure hope I'm on the main actor") +} + +@MainActor +func test() { + sync() +} +``` + +All these APIs are expressed in terms of executors. + +> Naming note: I'd be open to ideas about naming this somehow around "the same mutually exclusive execution context" but the naming gets out of hand pretty quickly, so I stuck with ActorExecutor and Executor :thinking: + +Alternatively, we could have them speak about isolation context? Technically `assertSameActorIsolation(some Actor)` would be correct to compare the actors + +### "Deep" Executor equality + +By default, we assume pointer equality of executors is sufficient and correct. + +This exists today, and today is the only thing that users can do really. It is spelled as: + +```swift +extension MyCoolExecutor { + public func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(ordinary: self) + } +} +``` + +This is all good, but we should likely change the spelling a bit (this was also a point made in SE pitch review): + +```swift +extension MyCoolExecutor { + public func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(unique: self) // 5.9, same as the old ordinary: + } +} +``` + +The unique initializer keeps the current semantics of "*if the executor pointers are the same, it is the same executor and exclusive execution context*" fast path. + +If executors want to offer the "deep" equality they need to implement the `asUnownedExecutor` as: + +```swift +extension MyQueueThatTargetsAnotherQueueExecutor { + public func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(complexEquality: self) // TODO: unsure about the init name + } +} +``` + +The complex equality one must implement the following protocol requirement (that has a default implementation, because `SerialExecutor` is an existing protocol, so we must provide a default anyway): + +```swift + /// If this executor has complex equality semantics, and the runtime needs to compare + /// two executors, it will first attempt the usual pointer-based equality check, + /// and if it fails it will compare the types of both executors, if they are the same, + /// it will finally invoke this method, in an attempt to let the executor itself decide + /// if this and the `other` executor represent the same serial, exclusive, isolation context. + /// + /// This method must be implemented with great care, as wrongly returning `true` would allow + /// code from a different execution context (e.g. thread) to execute code which was intended + /// to be isolated by another actor. + /// + /// This check is not used when performing executor switching. + /// + /// This check is used when performing `assertOnSerialExecutor`, `preconditionOnSerialExecutor`, + /// `assumeMainActorExecutor` and similar APIs which assert about the same "exclusive execution context". + /// + /// - Parameter other: + /// - Returns: true, if `this` and the `other` executor actually are mutually exclusive, and it is safe from a concurrency perspective, to execute code assuming one on the other. + @available(SwiftStdlib 5.9, *) + func isSameExclusiveExecutionContext(other: Self) -> Bool +} + +@available(SwiftStdlib 5.9, *) +extension SerialExecutor { + func isSameExclusiveExecutionContext(other: Self) -> Bool { + self === other + } +} +``` + +For example, a dispatch queue based executor could implement this as follows: + +```swift +// how a dispatch queue based custom executor could use this: + + func isSameExclusiveExecutionContext(other: Self) -> Bool { + // pseudo-code + self.targetQueue == other.targetQueue + } +``` + +So even if two actors (`A` and `B`) are set up with different dispatch queue executors (`QA` and `QB`) but both target the same queue, dispatch could implement the `isSameExclusiveExecutionContext` method in such way that it returns true, based on the "target queue" information. + +For reference, an executor is stored like this in the Swift runtime, as an `ExecutorRef`: + +```swift +class ExecutorRef { + HeapObject *Identity; // Not necessarily Swift reference-countable + uintptr_t Implementation; // where we store our extra bit about "needs complexEquality" + + // We future-proof the ABI here by masking the low bits off the + // implementation pointer before using it as a witness table. + enum: uintptr_t { + WitnessTableMask = ~uintptr_t(alignof(void*) - 1) // 3 bits + }; +} +``` + + + +The following explains how equality for the `assert`/`precondition` and `assume...ActorExecutor` is implemented. + +We look at the type of the executor (the bit we store in the ExecutorRef, specifically in the Implementation / witness table field), and if both are: + +- **unique**, today's semantics ("**ordinary**"), may be thought of as "**root**" + - creation: + - using today's `UnownedSerialExecutor.init(ordinary:)` + - or a potentially new name for it `init(unique:)` + - comparison: + - compare the two executors pointers directly + - return the result + - notes: + - Default actors + - always have `Identity != 0` and `Implementation == 0` + - thus comparing their Identity gives us the expected result; as every default actor is its own isolation domain +- **complexEquality**, maybe a good name would be "`cooperativeEquality`"?), may be throught of as "**inner**" (i.e. it is not just a root) + - creation: + - `UnownedSerialExecutor` creation sets bit in the `ExecutorRef` + - comparison: + - If both executors are complexEquality, we **compare** the two executor **Identity** directly + - if true, return (fast-path, same as the unique ones) + - obtain and **compare** the **type** of both executors: + - `swift_getObjectType(executor1.Identity) == swift_getObjectType(executor2.Identity)` + - if false, return + - TODO: John mentioned a "compare the witness tables" but I didn't entirely follow about implementation, how we'd do that. + - invoke the **executor implemented comparison** the `executor1.isSameExclusiveExecutionContext(executor2)` + - TODO: We could make this `SerialExecutor.isSameExclusiveExecutionContext(executor1, executor2)` I think, but implementation wise not doing a static func was easier so for starters I did the simpler shape. + - return + + + +These checks are likely **NOT** enough to avoid switching, note from Rokhini: + +> “Mutual exclusion context equality” in `swift_task_switch()` is most likely **not sufficient** for determining that we can safely just run code inline instead of executor switching for dispatch queues cause dispatch isn’t holding all of the correct locks in place, other work might have built up behind the queue, etc. + +## Proposal + +1. We introduce the assert, precondition, assume APIs right now, along with the most basic existing custom actor executor API + 1. Aimed for Rainbow (SwiftData depends on it) +2. We follow up with the "**complexEquality**" additions + 1. if we make it for Rainbow, cool, if not, also okey + 2. it's just a caveat on these "queue targeting other queue" situations, which we'll improve later +3. Next, we continue towards the **switching support** in executors + 1. Unlikely to be in this release, but we started discussing the API shape which is good + 2. If we didn't do 2) by then, we can likely propose 2) and 3) together. \ No newline at end of file From 5276456a1b934a1da9588e5233661a26cb60f168 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Mon, 27 Feb 2023 13:39:24 -0800 Subject: [PATCH 3004/4563] Add proposal for package registry publish (#1925) Add proposal for package registry publish --- proposals/0391-package-registry-publish.md | 702 +++++++++++++++++++++ 1 file changed, 702 insertions(+) create mode 100644 proposals/0391-package-registry-publish.md diff --git a/proposals/0391-package-registry-publish.md b/proposals/0391-package-registry-publish.md new file mode 100644 index 0000000000..4a218d69da --- /dev/null +++ b/proposals/0391-package-registry-publish.md @@ -0,0 +1,702 @@ +# Package Registry Publish + +* Proposal: [SE-0391](0391-package-registry-publish.md) +* Author: [Yim Lee](https://github.com/yim-lee) +* Review Manager: [Tom Doron](https://github.com/tomerd) +* Status: **Active review (February 27 ... March 13, 2023)** +* Implementation: + * https://github.com/apple/swift-package-manager/pull/6101 + * https://github.com/apple/swift-package-manager/pull/6146 + * https://github.com/apple/swift-package-manager/pull/6159 + * https://github.com/apple/swift-package-manager/pull/6184 + * https://github.com/apple/swift-package-manager/pull/6189 +* Review: + * pitch: https://forums.swift.org/t/pitch-package-registry-publish/62828 + +## Introduction + +A package registry makes packages available to consumers. Starting with Swift 5.7, +SwiftPM supports dependency resolution and package download using any registry that +implements the [service specification](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md) proposed alongside with [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md). +SwiftPM does not yet provide any tooling for publishing packages, so package authors +must manually prepare the contents (e.g., source archive) and interact +with the registry on their own to publish a package release. This proposal +aims to standardize package publishing such that SwiftPM can offer a complete and +well-rounded experience for using package registries. + +## Motivation + +Publishing package release to a Swift package registry generally involves these steps: + 1. Gather package release metadata. + 1. Prepare package source archive by using the [`swift package archive-source` subcommand](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md#archive-source-subcommand). + 1. Sign the archive (if needed). + 1. [Authenticate](https://github.com/apple/swift-evolution/blob/main/proposals/0378-package-registry-auth.md) (if required by the registry). + 1. Send the archive (and signature if any) and metadata by calling the ["create a package release" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-6). + 1. Check registry server response to determine if publication has succeeded or failed (if the registry processes request synchronously), or is pending (if the registry processes request asynchronously). + +SwiftPM can streamline the workflow by combining all of these steps into a single +`publish` command. + +## Proposed solution + +We propose to introduce a new `swift package-registry publish` subcommand to SwiftPM +as well as standardization on package release metadata and package signing to ensure a +consistent user experience for publishing packages. + +## Detailed design + +### Package release metadata + +Typically a package release has metadata associated with it, such as URL of the source +code repository, license, etc. In general, metadata gets set when a package release is +being published, but a registry service may allow modifications of the metadata afterwards. + +The current [registry service specification](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md) states that: + - A client (e.g., package author, publishing tool) may provide metadata for a package release by including it in the ["create a package release" request](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#462-package-release-metadata). The registry server will store the metadata and include it in the ["fetch information about a package release" response](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-2). + - If a client does not include metadata, the registry server may populate it unless the client specifies otherwise (i.e., by sending an empty JSON object `{}` in the "create a package release" request). + +It does not, however, define any requirements or server-client API contract on the +metadata contents. We would like to change that by proposing the following: + - Package release metadata will continue to be sent as a JSON object. + - Package release metadata must be sent as part of the "create a package release" request and adhere to the [schema](#package-release-metadata-standards). + - Package release metadata may be included in the "create a package release" request in one of these ways, depending on registry server support: + + A multipart section named `metadata` in the request body. + + A file named `package-metadata.json` **inside** the source archive being published. + - Registry server may allow and/or populate additional metadata by expanding the schema, but it must not alter any of the predefined properties. + - Registry server will continue to include metadata in the "fetch information about a package release" response. + +#### Package release metadata standards + +Package release metadata submitted to a registry must be a JSON object of type +[`PackageRelease`](#packagerelease-type), the schema of which is defined below. + +
    + +Expand to view JSON schema + +```json +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md", + "title": "Package Release Metadata", + "description": "Metadata of a package release.", + "type": "object", + "properties": { + "author": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the author." + }, + "email": { + "type": "string", + "description": "Email address of the author." + }, + "description": { + "type": "string", + "description": "A description of the author." + }, + "organization": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Name of the organization." + }, + "email": { + "type": "string", + "description": "Email address of the organization." + }, + "description": { + "type": "string", + "description": "A description of the organization." + }, + "url": { + "type": "string", + "description": "URL of the organization." + }, + }, + "required": ["name"] + }, + "url": { + "type": "string", + "description": "URL of the author." + }, + }, + "required": ["name"] + }, + "description": { + "type": "string", + "description": "A description of the package release." + }, + "licenseURL": { + "type": "string", + "description": "URL of the package release's license document." + }, + "readmeURL": { + "type": "string", + "description": "URL of the README specifically for the package release or broadly for the package." + }, + "repositoryURLs": { + "type": "array", + "description": "Code repository URL(s) of the package release.", + "items": { + "type": "string", + "description": "Code repository URL." + } + } + } +} +``` + +
    + +##### `PackageRelease` type + +| Property | Type | Description | Required | +| ----------------- | :-----------------: | ------------------------------------------------ | :------: | +| `author` | [Author](#author-type) | Author of the package release. | | +| `description` | String | A description of the package release. | | +| `licenseURL` | String | URL of the package release's license document. | | +| `readmeURL` | String | URL of the README specifically for the package release or broadly for the package. | | +| `repositoryURLs` | Array | Code repository URL(s) of the package. It is recommended to include all URL variations (e.g., SSH, HTTPS) for the same repository. This can be an empty array if the package does not have source control representation. The registry must ensure that these URLs are searchable using the ["lookup package identifiers registered for a URL" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#45-lookup-package-identifiers-registered-for-a-url). | ✓ | + +##### `Author` type + +| Property | Type | Description | Required | +| ----------------- | :-----------------: | ------------------------------------------------ | :------: | +| `name` | String | Name of the author. | ✓ | +| `email` | String | Email address of the author. | | +| `description` | String | A description of the author. | | +| `organization` | [Organization](#organization-type) | Organization that the author belongs to. | | +| `url` | String | URL of the author. | | + +##### `Organization` type + +| Property | Type | Description | Required | +| ----------------- | :-----------------: | ------------------------------------------------ | :------: | +| `name` | String | Name of the organization. | ✓ | +| `email` | String | Email address of the organization. | | +| `description` | String | A description of the organization. | | +| `url` | String | URL of the organization. | | + +### Package signing + +A registry may require packages to be signed. In order for SwiftPM to be able to +download and handle signed packages from a registry, we propose to standardize +package signature format and establish server-client API contract on package +signing. + +#### Package signature + +Package signature format will be identified by the underlying standard/technology +(e.g., Cryptographic Message Syntax (CMS), JSON Web Signature (JWS), etc.) and +version number. In the initial release, all signatures will be in [CMS](https://www.rfc-editor.org/rfc/rfc5652.html). + +| Signature format ID | Description | +| ------------------- | --------------------------------------------------------- | +| `cms-1.0.0` | Version 1.0.0 of package signature in CMS | + +##### Package signature format `cms-1.0.0` + +Package signature format `cms-1.0.0` uses CMS. + +| CMS Attribute | Details | +| ------------------------------ | --------------------------------------------------------- | +| Content type | [Signed-Data](https://www.rfc-editor.org/rfc/rfc5652.html#section-5) | +| Encapsulated data | The content being signed ([`EncapsulatedContentInfo.eContent`](https://www.rfc-editor.org/rfc/rfc5652.html#section-5.2)) is omitted since we are constructing an external signature. | +| Message digest algorithm | SHA-256, computed on the package source archive. | +| Signature algorithm | ECDSA P-256 | +| Number of signatures | 1 | +| Certificate | Certificate that contains the signing key. It is up to the registry to define the certificate policy (e.g., trusted root(s)). | + +The signature, represented in CMS, will be included as part +of the "create a package release" API request. + +A registry receiving such signed package will: + - Check if the signature format (`cms-1.0.0`) is accepted. + - Validate the signature is well-formed according to the signature format. + - Validate the certificate chain meets registry policy. + - Extract public key from the certificate and use it to verify the signature. + +Then the registry will process the package and save it for client downloads if publishing is successful. + +The registry must include signature information in the "fetch information about a package release" API +response to indicate the package is signed and the signature format (`cms-1.0.0`). + +After downloading a signed package SwiftPM will: + - Check if the signature format (`cms-1.0.0`) is supported. + - Validate the signature is well-formed according to the signature format. + - Validate that the signed package complies with the locally-configured signing policy. + - Extract public key from the certificate and use it to verify the signature. + +#### New `package sign` subcommand + +There will be a new subcommand `package sign` dedicated to package signing. + +```manpage +> swift package sign --help +OVERVIEW: Sign a package archive + +USAGE: package sign + +ARGUMENTS: + The path to the package source archive to be signed. + The path the output signature file will be written to. + +OPTIONS: + --signing-identity The label of the signing identity to be retrieved from the system's secrets store if supported. + + --private-key-path The path to the certificate's PKCS#8 private key (DER-encoded). + --cert-path Path to the signing certificate (DER-encoded). +``` + +A signing identity encompasses a private key and a certificate. On +systems where it is supported, SwiftPM can look for a signing identity +using the query string given via the `--signing-identity` option. This +feature will be available on macOS through Keychain in the initial +release, so a certificate and its private key can be located by the +certificate label alone. + +Otherwise, both `--private-key-path` and `--cert-path` must be +provided to locate the signing key and certificate. + +All signatures in the initial release will be in the [`cms-1.0.0`](#package-signature-format-cms-100) format. + +#### Server-side requirements for package signing + +A registry that requires package signing should provide documentations +on the signing requirements (e.g., any requirements for certificates +used in signing). + +A registry must also modify the ["create package release" API](#create-package-release-api) to allow +signature in the request, as well as the response for the ["fetch package release metadata" API](#fetch-package-release-metadata-api) +to include signature information. + +#### SwiftPM's handling of registry packages + +##### SwiftPM configuration + +Users will be able to configure how SwiftPM handles packages downloaded from a +registry. In the user-level `registries.json` file, which by default is located at +`~/.swiftpm/configuration/registries.json`, we will introduce a new `security` key: + +```json +{ + "security": { + "default": { + "signing": { + "onUnsigned": "prompt", // One of: "error", "prompt", "warn", "silentAllow" + "onUntrustedCertificate": "prompt", // One of: "error", "prompt", "warn", "silentAllow" + "trustedRootCertificatesPath": "~/.swiftpm/security/trusted-root-certs/", + "includeDefaultTrustedRootCertificates": true, + "validationChecks": { + "certificateExpiration": "disabled", // One of: "enabled", "disabled" + "certificateRevocation": "disabled" // One of: "strict", "allowSoftFail", "disabled" + } + } + }, + "registryOverrides": { + // The example shows all configuration overridable at registry level + "packages.example.com": { + "signing": { + "onUnsigned": "warn", + "onUntrustedCertificate": "warn", + "trustedRootCertificatesPath": , + "includeDefaultTrustedRootCertificates": , + "validationChecks": { + "certificateExpiration": "enabled", + "certificateRevocation": "allowSoftFail" + } + } + } + }, + "scopeOverrides": { + // The example shows all configuration overridable at scope level + "mona": { + "signing": { + "trustedRootCertificatesPath": , + "includeDefaultTrustedRootCertificates": + } + } + }, + "packageOverrides": { + // The example shows all configuration overridable at package level + "mona.LinkedList": { + "signing": { + "trustedRootCertificatesPath": , + "includeDefaultTrustedRootCertificates": + } + } + } + }, + ... +} +``` + +Security configuration for a package is computed using values from +the following (in descending precedence): +1. `packageOverrides` (if any) +1. `scopeOverrides` (if any) +1. `registryOverrides` (if any) +1. `default` + +The `default` JSON object contains all configurable security options +and their default value when there is no override. + +- `signing.onUnsigned`: Indicates how SwiftPM will handle an unsigned package. + + | Option | Description | + | ------------- | --------------------------------------------------------- | + | `error` | SwiftPM will reject the package and fail the build. | + | `prompt` | SwiftPM will prompt user to see if the unsigned package should be allowed.
    • If no, SwiftPM will reject the package and fail the build.
    • If yes and the package has never been downloaded, its checksum will be stored for [local TOFU](#local-tofu). Otherwise, if the package has been downloaded before, its checksum must match the previous value or else SwiftPM will reject the package and fail the build.
    SwiftPM will record user's response to prevent repetitive prompting. | + | `warn` | SwiftPM will not prompt user but will emit a warning before proceeding. | + | `silentAllow` | SwiftPM will allow the unsigned package without prompting user or emitting warning. | + +- `signing.onUntrustedCertificate`: Indicates how SwiftPM will handle a package signed with an [untrusted certificate](#trusted-vs-untrusted-certificate). + + | Option | Description | + | ------------- | --------------------------------------------------------- | + | `error` | SwiftPM will reject the package and fail the build. | + | `prompt` | SwiftPM will prompt user to see if the package signed with an untrusted certificate should be allowed.
    • If no, SwiftPM will reject the package and fail the build.
    • If yes, SwiftPM will proceed with the package as if it were an unsigned package.
    SwiftPM will record user's response to prevent repetitive prompting. | + | `warn` | SwiftPM will not prompt user but will emit a warning before proceeding. | + | `silentAllow` | SwiftPM will allow the package signed with an untrusted certificate without prompting user or emitting warning. | + +- `signing.trustedRootCertificatesPath`: Absolute path to the directory containing custom trusted roots. SwiftPM will include these roots in its [trust store](#trusted-vs-untrusted-certificate), and certificates used for package signing must chain to roots found in this store. This configuration allows override at the package, scope, and registry levels. +- `signing.includeDefaultTrustedRootCertificates`: Indicates if SwiftPM should include default trusted roots in its [trust store](#trusted-vs-untrusted-certificate). This configuration allows override at the package, scope, and registry levels. +- `signing.validationChecks`: Validation check settings for the package signature. + + | Validation | Description | + | ------------------------ | --------------------------------------------------------------- | + | `certificateExpiration` |
    • `enabled`: SwiftPM will check that the current timestamp when downloading falls within the signing certificate's validity period. If it doesn't, SwiftPM will reject the package and fail the build.
    • `disabled`: SwiftPM will not perform this check.
    | + | `certificateRevocation` | With the exception of `disabled`, SwiftPM will check revocation status of the signing certificate. SwiftPM will only support revocation check done through [OCSP](https://www.rfc-editor.org/rfc/rfc6960) in the first feature release.
    • `strict`: Revocation check must complete successfully and the certificate must be in good status. SwiftPM will reject the package and fail the build if the revocation status is revoked or unknown (including revocation check not supported or failed).
    • `allowSoftFail`: SwiftPM will reject the package and fail the build iff the certificate has been revoked. SwiftPM will allow the certificate's revocation status to be unknown (including revocation check not supported or failed).
    • `disabled`: SwiftPM will not perform this check.
    | + +##### Trusted vs. untrusted certificate + +A certificate is **trusted** if it is chained to any roots in SwiftPM's +trust store, which is a combination of: + - SwiftPM's default trust store, if `signing.includeDefaultTrustedRootCertificates` is `true`. + - Custom root(s) in the configured trusted roots directory at `signing.trustedRootCertificatesPath`. + +Otherwise, a certificate is **untrusted** and handled according to the `signing.onUntrustedCertificate` setting. + +Both `signing.includeDefaultTrustedRootCertificates` and `signing.trustedRootCertificatesPath` +support multiple levels of overrides. SwiftPM will choose the configuration value that has the +highest specificity. + + +For example, when evaluating the value of `signing.includeDefaultTrustedRootCertificates` +or `signing.trustedRootCertificatesPath` for package `mona.LinkedList`: + 1. SwiftPM will use the package override from `packageOverrides` (i.e., `packageOverrides["mona.LinkedList"]`), if any. + 1. Otherwise, SwiftPM will use the scope override from `scopeOverrides` (i.e., `scopeOverrides["mona"]`), if any. + 1. Next, depending on the registry the package is downloaded from, SwiftPM will look for and use the registry override in `registryOverrides`, if any. + 1. Finally, if no override is found, SwiftPM will use the value from `default`. + +##### Local TOFU + +When SwiftPM downloads a package release from registry via the +["download source archive" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-4), it will: + 1. Search local fingerprints storage, which by default is located at `~/.swiftpm/security/fingerprints/`, to see if the package release has been downloaded before and its recorded checksum. The checksum of the downloaded source archive must match the previous value or else [trust on first use (TOFU)](https://en.wikipedia.org/wiki/Trust_on_first_use) check would fail. + 1. Fetch package release metadata from the registry to get: +
      +
    • Checksum for TOFU if the package release is downloaded for the first time.
    • +
    • Signature information if the package release is signed.
    • +
    + 1. Retrieve security settings from the user-level `registries.json`. + 1. Check if the package is allowed based on security settings. + 1. Validate the signature according to the signature format if package is signed. + 1. Some certificates allow SwiftPM to extract additional information that can drive additional security features. For packages signed with these certificates, SwiftPM will apply additional, publisher-level TOFU by extracting signing identity from the certificate and enforcing the same signing identity across all signed versions of a package. + +### New `package-registry publish` subcommand + +The new `package-registry publish` subcommand will create a package +source archive, sign it if needed, and publish it to a registry. + +```manpage +> swift package-registry publish --help +OVERVIEW: Publish a package release to registry + +USAGE: package-registry publish + +ARGUMENTS: + The package identifier. + The package release version being created. + +OPTIONS: + --url The registry URL. + --scratch-directory The path of the directory where working file(s) will be written. + + --metadata-path The path to the package metadata JSON file if it will not be part of the source archive. + + --signing-identity The label of the signing identity to be retrieved from the system's secrets store if supported. + + --private-key-path The path to the certificate's PKCS#8 private key (DER-encoded). + --cert-path Path to the signing certificate (DER-encoded). +``` + +- `id`: The package identifier in the `.` notation as defined in [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md#package-identity). It is the package author's responsibility to register the package identifier with the registry beforehand. +- `version`: The package release version in [SemVer 2.0](https://semver.org) notation. +- `url`: The URL of the registry to publish to. SwiftPM will try to determine the registry URL by searching for a scope-to-registry mapping or use the `[default]` URL in `registries.json`. The command will fail if this value is missing. +- `scratch-directory`: The path of the working directory. SwiftPM will write to the package directory by default. + +The following may be required depending on registry support and/or requirements: + - `metadata-path`: The path to the JSON file containing [package release metadata](#package-release-metadata). This parameter should be set if the registry expects metadata to be sent as part of the request. SwiftPM will always include the content of this file in the request body if given. Otherwise, if the registry expects `package-metadata.json` in the source archive, it is the package author's responsibility to make sure the file is present in the package directory so that it gets included in the source archive. If metadata is included in both the request body and the source archive, then it is at the registry's discretion to choose which to use, although having `package-metadata.json` in the archive is [recommended](#package-release-metadata-signing). + - `signing-identity`: The label that identifies the signing identity to use for package signing in the system's secrets store if supported. See also the [`package sign` subcommand](#new-package-sign-subcommand) for details. + - `private-key-path`: Required for package signing unless `signing-identity` is specified, this is the path to the private key used for signing. + - `cert-path`: Required for package signing unless `signing-identity` is specified, this is the signing certificate. + +SwiftPM will sign the package source archive if `signing-identity` +or both `private-key-path` and `cert-path` are set. + +All signatures in the initial release will be in the [`cms-1.0.0`](#package-signature-format-cms-100) format. + +Using these inputs, SwiftPM will: + - Generate source archive for the package release. + - Sign the source archive if the required parameters are provided. + - Make HTTP request to the "create a package release" API. + - Check server response for any errors. + +Prerequisites: +- Run [`swift package-registry login`](https://github.com/apple/swift-evolution/blob/main/proposals/0378-package-registry-auth.md#new-login-subcommand) to authenticate registry user if needed. +- The user has the necessary permissions to call the ["create a package release" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-6) for the package identifier. + +### Changes to the registry service specification + +#### Create package release API + +A registry must update [this existing endpoint](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-6) to handle package release +metadata as described in a [previous section](#package-release-metadata) of this document. In particular, + - Metadata is now required. + - Client must include metadata in the request. + - Empty metadata is not allowed. + - Metadata may be submitted in two ways: + - In the request body--this method should already be supported by the registry per the [current API specification](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#46-create-a-package-release). + - In the source archive--this is new and maybe optionally implemented by the registry. + - Values provided with the `repositoryURLs` JSON key must be searchable. + +If the package being published is signed, the client must identify the signature format +in the `X-Swift-Package-Signature-Format` HTTP request header so that the +server can process the signature accordingly. + +The signature is sent as part of the request body: + +``` +PUT /mona/LinkedList?version=1.1.1 HTTP/1.1 +Host: packages.example.com +Accept: application/vnd.swift.registry.v1+json +Content-Type: multipart/form-data;boundary="boundary" +Content-Length: 336 +Expect: 100-continue +X-Swift-Package-Signature-Format: cms-1.0.0 + +--boundary +Content-Disposition: form-data; name="source-archive" +Content-Type: application/zip +Content-Length: 32 +Content-Transfer-Encoding: base64 + +gHUFBgAAAAAAAAAAAAAAAAAAAAAAAA== + +--boundary +Content-Disposition: form-data; name="source-archive-signature" +Content-Type: application/octet-stream +Content-Length: 88 +Content-Transfer-Encoding: base64 + +l1TdTeIuGdNsO1FQ0ptD64F5nSSOsQ5WzhM6/7KsHRuLHfTsggnyIWr0DxMcBj5F40zfplwntXAgS0ynlqvlFw== +``` + +#### Fetch package release metadata API + +A registry may update [this existing endpoint](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-2) for the [metadata changes](#package-release-metadata) +described in this document. + +If the package release is signed, the registry must include a `signing` JSON +object in the response: + +```json +{ + "id": "mona.LinkedList", + "version": "1.1.1", + "resources": [ + { + "name": "source-archive", + "type": "application/zip", + "checksum": "a2ac54cf25fbc1ad0028f03f0aa4b96833b83bb05a14e510892bb27dea4dc812", + "signing": { + "signatureBase64Encoded": "l1TdTeIuGdNsO1FQ0ptD64F5nSSOsQ5WzhM6/7KsHRuLHfTsggnyIWr0DxMcBj5F40zfplwntXAgS0ynlqvlFw==", + "signatureFormat": "cms-1.0.0" + } + } + ], + "metadata": { ... } +} +``` + +A client can use the API response to determine if a package is signed and +handle it accordingly. + +## Security + +This proposal introduces the framework for package signing, allowing package +authors the ability to provide additional authenticity guarantees by signing their +source archives before publishing them to the registry. Package users will be +able to control the kind(s) of packages they trust by specifying a local validation +policy. This can include a trust on first use approach, or by validating against a +pre-configured set of trusted roots. + +While this proposal introduces the package signature format, it does not validate +that a package is published by a specific entity. Instead, it validates that a package +is published by an entity who can obtain a signing certificate that meets the +requirements defined by the registry, which could be anybody. As such, it does +not provide any protection against malware, and it would be wrong to assumed that +signed packages can be trusted unconditionally. + +In this proposal, package signing is primarily intended to provide additional security +controls for package registries. By requiring packages be signed, and by the +registry limiting what keys or identities are allowed to publish packages, a registry +can provide additional security in the event the package author's registry credentials +are compromised. + +Although this proposal introduces policy controls for package users, they are limited +in scope, and do not yet allow SwiftPM to validate that multiple package versions are +from the same entity--recording signing identities for [TOFU](#local-tofu) provides some +protection against a compromised registry, but it is not for all packages and +[more work needs to be done](#local-signing-identity-checks) before it can be so. As such, SwiftPM continues to trust +the registry to provide authentic packages and accurate information about the +signature status of the package. + +### Package release metadata signing + +Package release metadata submitted as `package-metadata.json` in a [signed package](#package-signing) +is considered signed and not modifiable. Otherwise, the registry server may override the +metadata and/or allow it to be edited afterwards. It is recommended that package authors +use `package-metadata.json` to submit metadata if this method and package signing are +supported by the registry. + +### Privacy implications of certificate revocation check + +Revocation checking via OCSP implicitly discloses to the certificate +authority and anyone on the network the packages that a user may be +downloading. If this is a concern, revocation check can be disabled +in [SwiftPM configuration](#swiftpm-configuration). + +## Impact on existing packages + +Current packages won't be affected by changes in this proposal. + +## Alternatives considered + +### Signing package source archive vs. manifest + +A package manifest is a reference list of files that are present in the +source archive. We considered an approach where SwiftPM would produce +such manifest, sign the manifest instead of the source archive, +then create a new archive containing the source archive, manifest, and +signature file. This way the archive and its signature can be distributed +by the registry as a single file. + +However, given the potential complications with extracting files from the +archive and verifying manifest contents, moreover there is no restriction +that would require single-file download (i.e., SwiftPM can download the +source archive and signature separately), we have decided to take the approach +covered in previous sections of this proposal. + +### Use key in certificate as signing identity for local publisher-level TOFU + +We considered using the key in a certificate as signing identity for +[local publisher-level TOFU](#local-tofu) (i.e., different versions of a package must +have the same signing identity). However, since key can change easily +(e.g., lost key, key rotation, etc.), all users of the package must reset data +used for local TOFU each time or else TOFU check would fail, which can introduce +significant overhead and confusion. + +## Future directions + +### Support encrypted private keys + +Private keys are encrypted typically. SwiftPM commands that have private key +as input, such as `package sign` and `package-registry publish`, should support +reading encrypted private key. This could mean modifying the command to prompt +user for the passphrase if needed, and adding a `--private-key-passphrase` +option to the command for non-interactive/automation use-cases. + +### Auto-populate package release metadata + +Parts of the [package release metadata](#package-release-metadata-standards) can be populated by SwiftPM using +information found in the package directory. The auto-generated metadata can +serve as a default or starting point which package authors may optionally edit, +and ensure every package release to have metadata. + +### Support additional certificate revocation checks + +SwiftPM may support alternative mechanisms to check revocation besides OCSP. + +### Local signing identity checks + +In the current proposal, signing identity is left for the package registry to define, implement, and +enforce at publication time. However, this requires SwiftPM to rely on the package +registry to correctly implement these checks, and a compromise of the registry, or +SwiftPM's connection to the registry, would potentially allow for unauthorized packages +to be published. Performing additional checks in SwiftPM can mitigate this risk, but +requires defining a consistent identity that can be extracted and relied upon, and +determining how those identities are provisioned and authorized. + +A future Swift evolution proposal can provide specification of a certificate +from which signing identity can be extracted, such that more certificates can +be used for [local publisher-level TOFU](#local-tofu), which provides an extra layer of +trust on top of checksum TOFU done at the package release level. + +### Timestamping and Countersignatures + +In the proposed implementation, the signing certificate associated with +the package may expire, and this can prevent SwiftPM from validating +the information and revocation status of the certificate. Using +approaches such as [Time Stamping Authority](https://www.rfc-editor.org/rfc/rfc3161) or having the registry +itself perform a [countersignature](https://www.rfc-editor.org/rfc/rfc5652#section-11.4), information about when a +package was first published can be provided, +even after the signing certificate has expired. This can avoid the need +for package authors to re-sign packages when the signing certificate +expires. + +### Transitive trust + +SwiftPM's TOFU mitigation could be further improved by +including checksum and signing identity in `Package.resolved` +(or another similar file), which then gets included in the package content. +Including such security metadata would allow distributing information about +direct and transitive dependencies across the ecosystem much faster than a +local-only TOFU without requiring a centralized database/service to vend +this information. + +```json +{ + "pins": [ + { + "identity": "mona.LinkedList", + "kind": "registry", + "location": "https://packages.example.com/mona/LinkedList", + "state": { + "checksum": "ed008d5af44c1d0ea0e3668033cae9b695235f18b1a99240b7cf0f3d9559a30d", + "version": "0.12.0" + }, + "signingBy": { + "identityType": , + "name": , + ... + } + }, + { + "identity": "Foo", + "kind": "remoteSourceControl", + "location": "https://github.com/something/Foo.git", + "state": { + "revision": "90a9574276f0fd17f02f58979423c3fd4d73b59e", + "version": "1.0.2", + } + } + ], + "version": 2 +} +``` From 9a05882e7979cb373b5c543d2dc73940a2ffe2b6 Mon Sep 17 00:00:00 2001 From: tomer doron Date: Mon, 27 Feb 2023 13:43:51 -0800 Subject: [PATCH 3005/4563] Update 0391-package-registry-publish.md add link to review thread --- proposals/0391-package-registry-publish.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0391-package-registry-publish.md b/proposals/0391-package-registry-publish.md index 4a218d69da..7649eabee1 100644 --- a/proposals/0391-package-registry-publish.md +++ b/proposals/0391-package-registry-publish.md @@ -12,6 +12,7 @@ * https://github.com/apple/swift-package-manager/pull/6189 * Review: * pitch: https://forums.swift.org/t/pitch-package-registry-publish/62828 + * review: https://forums.swift.org/t/se-0391-package-registry-publish/63405 ## Introduction From 2cf684cddde3d56f081a1412c6190d18b4cfb46c Mon Sep 17 00:00:00 2001 From: Boris Buegling Date: Wed, 1 Feb 2023 18:10:31 -0800 Subject: [PATCH 3006/4563] Add proposal for SwiftPM support of macros --- proposals/NNNN-swiftpm-expression-macros.md | 165 ++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 proposals/NNNN-swiftpm-expression-macros.md diff --git a/proposals/NNNN-swiftpm-expression-macros.md b/proposals/NNNN-swiftpm-expression-macros.md new file mode 100644 index 0000000000..e62f603a8d --- /dev/null +++ b/proposals/NNNN-swiftpm-expression-macros.md @@ -0,0 +1,165 @@ +# Package Manager Support for Custom Macros + +* Proposal: [SE-NNNN](NNNN-swiftpm-expression-macros.md) +* Authors: [Boris Buegling](https://github.com/neonichu), [Doug Gregor](https://github.com/DougGregor) +* Review Manager: TBD +* Status: **Implementation available behind pre-release tools-version https://github.com/apple/swift-package-manager/pull/6185 and https://github.com/apple/swift-package-manager/pull/6200** + +## Introduction + +Macros provide a way to extend Swift by performing arbitary syntactic transformations on input source code to produce new code. One example for this are expression macros which were previously proposed in [SE-0382](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md). This proposal covers how custom macros are defined, built and distributed as part of a Swift package. + +## Motivation + +[SE-0382](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) and [A Possible Vision for Macros in Swift](https://gist.github.com/DougGregor/4f3ba5f4eadac474ae62eae836328b71) covered the motivation for macros themselves, defining them as part of a package will offer a straightforward way to reuse and distribute macros as source code. + +## Proposed solution + +Macros implemented in an external program can be declared as part of a package via a new macro target type, defined in +the `CompilerPluginSupport` library: + +```swift +public extension Target { + /// Creates a macro target. + /// + /// - Parameters: + /// - name: The name of the macro. + /// - dependencies: The macro's dependencies. + /// - path: The path of the macro, relative to the package root. + /// - exclude: The paths to source and resource files you want to exclude from the macro. + /// - sources: The source files in the macro. + static func macro( + name: String, + dependencies: [Dependency] = [], + path: String? = nil, + exclude: [String] = [], + sources: [String]? = nil + ) -> Target { +} +``` + +Similar to package plugins ([SE-0303 "Package Manager Extensible Build Tools"](https://github.com/apple/swift-evolution/blob/main/proposals/0303-swiftpm-extensible-build-tools.md)), macro plugins are built as executables for the host (i.e, where the compiler is run). The compiler receives the paths to these executables from the build system and will run them on demand as part of the compilation process. Macro executables are automatically available for any target that transitively depends on them via the package manifest. + +A minimal package containing the implementation, definition and client of a macro would look like this: + +```swift +import PackageDescription +import CompilerPluginSupport + +let package = Package( + name: "MacroPackage", + targets: [ + .macro(name: "MacroImpl"), + .target(name: "MacroDef", dependencies: ["MacroImpl"]), + .executableTarget(name: "MacroClient", dependencies: ["MacroDef"]), + ] +) +``` + +Macro implementations will be executed in a sandbox [similar to package plugins](https://github.com/apple/swift-evolution/blob/main/proposals/0303-swiftpm-extensible-build-tools.md#security), preventing file system and network access. This is a practical way of encouraging macros to not depend on any state other than the specific macro expansion node they are given to expand and its child nodes (but not its parent nodes), and the information specifically provided by the macro expansion context. If in the future macros need access to other information, this will be accomplished by extending the macro expansion context, which also provides a mechanism for the compiler to track what information the macro actually queried. + +## Detailed Design + +SwiftPM builds each macro as an executable for the host platform, applying certain additional compiler flags such as search paths to the SwiftSyntax library supplied by the toolchain. Each target that transitively depends on a macro will have access to it, concretely this happens by SwiftPM passing `-load-plugin-executable` to the compiler to specify which executable contains the implementation of a certain macro module. The macro defintion refers to the module and concrete type via an `#externalMacro` declaration which allows any dependency of the defining target to have access to the concrete macro. If any target of a library product depends on a macro, clients of said library will also get access to any public macros. Macros can have dependencies like any other target, but product dependencies of macros need to be statically linked, so explicitly dynamic library products cannot be used by a macro target. + +Concretely, the code for the macro package shown earlier would contain a macro implementation looking like this: + +```swift +import SwiftSyntax +import SwiftCompilerPlugin +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +@main +struct MyPlugin: CompilerPlugin { + var providingMacros: [Macro.Type] = [FontLiteralMacro.self] +} + +/// Implementation of the `#fontLiteral` macro, which is similar in spirit +/// to the built-in expressions `#colorLiteral`, `#imageLiteral`, etc., but in +/// a small macro. +public struct FontLiteralMacro: ExpressionMacro { + public static func expansion( + of macro: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + let argList = replaceFirstLabel( + of: macro.argumentList, + with: "fontLiteralName" + ) + let initSyntax: ExprSyntax = ".init(\(argList))" + if let leadingTrivia = macro.leadingTrivia { + return initSyntax.with(\.leadingTrivia, leadingTrivia) + } + return initSyntax + } +} + +/// Replace the label of the first element in the tuple with the given +/// new label. +private func replaceFirstLabel( + of tuple: TupleExprElementListSyntax, + with newLabel: String +) -> TupleExprElementListSyntax { + guard let firstElement = tuple.first else { + return tuple + } + + return tuple.replacing( + childAt: 0, + with: firstElement.with(\.label, .identifier(newLabel)) + ) +} +``` + +The macro definition would look like this: + +```swift +public enum FontWeight { + case thin + case normal + case medium + case semiBold + case bold +} + +public protocol ExpressibleByFontLiteral { + init(fontLiteralName: String, size: Int, weight: FontWeight) +} + +/// Font literal similar to, e.g., #colorLiteral. +@freestanding(expression) public macro fontLiteral(name: String, size: Int, weight: FontWeight) -> T = #externalMacro(module: "MacroImpl", type: "FontLiteralMacro") + where T: ExpressibleByFontLiteral +``` + +And the client of the macro would look like this: + +```swift +import MacroDef + +struct Font: ExpressibleByFontLiteral { + init(fontLiteralName: String, size: Int, weight: MacroDef.FontWeight) { + } +} + +let _: Font = #fontLiteral(name: "Comic Sans", size: 14, weight: .thin) +``` + + +## Impact on existing packages + +Since macro plugins are entirely additive, there's no impact on existing packages. + +## Alternatives considered + +The original pitch of expression macros considered declaring macros by introducing a new capability to [package plugins](https://github.com/apple/swift-evolution/blob/main/proposals/0303-swiftpm-extensible-build-tools.md), but since the execution model is significantly different and the APIs used for macros are external to SwiftPM, this idea was discarded. + +## Future Directions + +### Evolution of `SwiftSyntaxMacros` + +Macro targets will get access to a version of `SwiftSyntaxMacros` and associated libraries which ships with the Swift toolchain. This means package authors aiming to support multiple Swift compiler versions may have to resort to conditional compilation based on the Swift compiler version to handle the evolving API of SwiftSyntax. It also means clients of packages which are utilizing macros may need to wait for their dependencies to adopt to new APIs in order to upgrade to a new version of the Swift tools. We expect these limitations to eventually be solved by stabilizing the relevant SwiftSyntax APIs at which point macros could depend on SwiftSyntax via a package dependency on the source repository. + +### Generalized support for additional manifest API + +The macro target type is provided by a new library `CompilerPluginSupport` as a starting point for making package manifests themselves more extensible. Support for product and target type plugins should eventually be generalized to allow other types of externally defined specialized target types, such as, for example, a Windows application. From 70f9605b9d8846e854c91ed4c46a55fb0649d534 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 1 Mar 2023 14:41:51 -0800 Subject: [PATCH 3007/4563] Document the upcoming feature flags implemented in Swift 5.8 (#1964) --- proposals/0274-magic-file.md | 6 ++++-- proposals/0286-forward-scan-trailing-closures.md | 5 ++++- proposals/0335-existential-any.md | 3 +++ proposals/0354-regex-literals.md | 3 ++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/proposals/0274-magic-file.md b/proposals/0274-magic-file.md index 636139fd9f..bc45070b91 100644 --- a/proposals/0274-magic-file.md +++ b/proposals/0274-magic-file.md @@ -3,8 +3,8 @@ * Proposal: [SE-0274](0274-magic-file.md) * Authors: [Becca Royal-Gordon](https://github.com/beccadax), [Dave DeLong](https://github.com/davedelong) * Review Manager: [Ben Cohen](https://github.com/airspeedswift/) -* Status: **Accepted (2020-02-26)** -* Implementation: Prototype in master behind `-Xfrontend -enable-experimental-concise-pound-file`; revisions in [apple/swift#29412](https://github.com/apple/swift/pull/29412) +* Status: **Implemented (Swift 5.8)** +* Upcoming feature flag: `ConciseMagicFile` * Decision Notes: [Review #1](https://forums.swift.org/t/se-0274-concise-magic-file-names/32373/50), [Review #2](https://forums.swift.org/t/re-review-se-0274-concise-magic-file-names/33171/11), [Additional Commentary](https://forums.swift.org/t/revisiting-the-source-compatibility-impact-of-se-0274-concise-magic-file-names/37720) * Next Proposal: [SE-0285](0285-ease-pound-file-transition.md) @@ -154,6 +154,8 @@ These are implemented in the unmerged [apple/swift#29412](https://github.com/app All existing source code will continue to compile with this change, and `#file`'s documentation never specified precisely what its contents were; in one of the pitch threads, [Ben Cohen](https://forums.swift.org/t/concise-magic-file-names/31297/19) said that this is sufficient to satisfy Swift's source compatibility requirements. However, the proposal *will* cause behavior to change in existing code, and in some cases it will change in ways that cause existing code to behave incorrectly when run. Code that is adversely affected by this change can access the previous behavior by using `#filePath` instead of `#file`. +The change to the behavior of `#file` was deferred to the next major language version (Swift 6). However, it can be enabled with the [upcoming feature flag](0362-piecemeal-future-features.md) `ConciseMagicFile`. + ## Effect on ABI stability None. `#file` is a compile-time feature; existing binaries will continue to work as they did before. diff --git a/proposals/0286-forward-scan-trailing-closures.md b/proposals/0286-forward-scan-trailing-closures.md index ac12acd6ee..467eafbf96 100644 --- a/proposals/0286-forward-scan-trailing-closures.md +++ b/proposals/0286-forward-scan-trailing-closures.md @@ -4,6 +4,7 @@ * Author: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Implemented (Swift 5.3)** +* Upcoming feature flag: `ForwardTrailingClosures` (implemented in Swift 5.8) * Implementation: [apple/swift#33092](https://github.com/apple/swift/pull/33092) * Toolchains: [Linux](https://ci.swift.org/job/swift-PR-toolchain-Linux/404//artifact/branch-master/swift-PR-33092-404-ubuntu16.04.tar.gz), [macOS](https://ci.swift.org/job/swift-PR-toolchain-osx/579//artifact/branch-master/swift-PR-33092-579-osx.tar.gz) * Discussion: ([Pitch #1](https://forums.swift.org/t/pitch-1-forward-scan-matching-for-trailing-closures-source-breaking/38162)), ([Pitch #2](https://forums.swift.org/t/pitch-2-forward-scan-matching-for-trailing-closures/38491)) @@ -19,7 +20,7 @@ However, the backward-scan matching rule makes it hard to write good API that us ## Motivation Several folks noted the downsides of the "backward" matching rule. The rule itself is described in the [detailed design](https://github.com/apple/swift-evolution/blob/master/proposals/0279-multiple-trailing-closures.md#detailed-design) section of SE-0279 (search for "backward"). To understand the problem with the backward rule, let's try to declare the UIView [`animate(withDuration:animations:completion:)`](https://developer.apple.com/documentation/uikit/uiview/1622515-animate) method in the obvious way to make use of SE-0279: - + ```swift class func animate( withDuration duration: TimeInterval, @@ -301,6 +302,8 @@ trailingClosureBothDirections(g: { $0 * $1 }) This suppresses the warning and eliminates the ambiguity, so the code behaves the same across all overload sets. +The Swift 6 and newer behavior can be enabled in existing language modes with the [upcoming feature flag](0362-piecemeal-future-features.md) `ForwardTrailingClosures`. + ### Workaround via overload sets APIs like the above that depend on the backward scan can be reworked to provide the same client API. The basic technique involves removing the default arguments, then adding additional overloads to create the same effect. For example, drop the default argument of `finishHandler` so that the heuristic will kick in to fix calls with a single unlabeled trailing closure: diff --git a/proposals/0335-existential-any.md b/proposals/0335-existential-any.md index 5f467aa08e..843077e937 100644 --- a/proposals/0335-existential-any.md +++ b/proposals/0335-existential-any.md @@ -4,6 +4,7 @@ * Authors: [Holly Borla](https://github.com/hborla) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 5.6)** +* Upcoming feature flag: `ExistentialAny` (implemented in Swift 5.8) * Implementation: [apple/swift#40282](https://github.com/apple/swift/pull/40282) * Decision Notes: [Acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0335-introduce-existential-any/54504) @@ -95,6 +96,8 @@ let pq1: P & Q = S() // error let pq2: any P & Q = S() // okay ``` +The Swift 6 behavior can be enabled in earlier language modes with the [upcoming feature flag](0362-piecemeal-future-features.md) `ExistentialAny`. + ## Detailed design ### Grammar of explicit existential types diff --git a/proposals/0354-regex-literals.md b/proposals/0354-regex-literals.md index 2390aea912..ddf417a95c 100644 --- a/proposals/0354-regex-literals.md +++ b/proposals/0354-regex-literals.md @@ -4,6 +4,7 @@ * Authors: [Hamish Knight](https://github.com/hamishknight), [Michael Ilseman](https://github.com/milseman), [David Ewing](https://github.com/DaveEwing) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Implemented (Swift 5.7)** +* Upcoming feature flag: `BareSlashRegexLiterals` (implemented in Swift 5.8) * Implementation: [apple/swift#42119](https://github.com/apple/swift/pull/42119), [apple/swift#58835](https://github.com/apple/swift/pull/58835) * Bare slash syntax `/.../` available with `-enable-bare-slash-regex` * Review: ([first pitch](https://forums.swift.org/t/pitch-regular-expression-literals/52820)) @@ -408,7 +409,7 @@ As explored above, the parsing of `/.../` does have potential to break source in However we expect these cases will be uncommon, and can be disambiguated with parentheses or closures if needed. -To accommodate the cases where source may be broken, `/.../` regex literals will be introduced in Swift 6 mode. However, projects may adopt the syntax earlier by passing the compiler flag `-enable-bare-slash-regex`. Note this does not affect the extended delimiter syntax `#/.../#`, which will be usable immediately. +To accommodate the cases where source may be broken, `/.../` regex literals will be introduced in Swift 6 mode. However, projects may adopt the syntax earlier by passing the compiler flag `-enable-bare-slash-regex` or the [upcoming feature flag](0362-piecemeal-future-features.md) `BareSlashRegexLiterals`. Note this does not affect the extended delimiter syntax `#/.../#`, which will be usable immediately. ## Future Directions From f5fa7f98fcecec92ed50054e12858176fff802f8 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 2 Mar 2023 22:14:56 +0900 Subject: [PATCH 3008/4563] Revision nr 2 of custom executors; add "assume" and assert APIs --- Custom Executor updates.md | 236 ------------- proposals/nnnn-custom-actor-executors.md | 427 +++++++++++++++++++---- 2 files changed, 355 insertions(+), 308 deletions(-) delete mode 100644 Custom Executor updates.md diff --git a/Custom Executor updates.md b/Custom Executor updates.md deleted file mode 100644 index b5be70b298..0000000000 --- a/Custom Executor updates.md +++ /dev/null @@ -1,236 +0,0 @@ -## Custom Executor updates - -### Assert, precondition and assume APIs - -All these APIs should be about executors and isolation domains. - -> Note: We don't actually have the "current actor" at all ABI-wise. We only have the current executor, which are unique for default actors, but when sharing executors this is no longer true. This might be problematic for tracing systems etc, but for the current assertions it should be enough... - -The custom actor executors pitch proposed we offer those two assertion APIs, and that we do **not** offer any way to "get current executor" publicly. Maybe we'll offer this someday, but for now we don't have a strong reason to expose this and we'd like to push people to model their code using actors, i.e. "an actor on a specific executor", rather than grab executors and use them directly just as something to throw work onto. - -The current executors proposal includes the following, and I propose we keep them as-is (or bikeshed names a bit): - -```swift -// Precondition - -public func preconditionTaskOnExecutor( - _ executor: some Executor, - _ message: @autoclosure () -> String = "", - file: String = #fileID, line: UInt = #line) - -public func preconditionTaskIsOnExecutor( - _ actor: some Actor, - _ message: @autoclosure () -> String = "", - file: String = #fileID, line: UInt = #line) - -// Assert (if we really want to make less API, we could skip the assert ones, but I think they're useful) - -public func assertTaskOnExecutor( - _ executor: some Executor, - _ message: @autoclosure () -> String = "", - file: String = #fileID, line: UInt = #line) - -public func assertOnActorExecutor( - _ actor: some Actor, - _ message: @autoclosure () -> String = "", - file: String = #fileID, line: UInt = #line) -``` - -I propose that we offer those assert/precondition because we can do a better job at error reporting than if we just gave people the `isTaskOnExecutor(some executor)` because we can offer a message like `"Was on executor ... but expected ..."`. - -And the `assume...` API, specifically for MainActor right now: - -```swift -// Assume - -func assumeOnMainActorExecutor( - _ operation: @MainActor () throws -> T, - file: StaticString = #fileID, line: UInt = #line -) rethrows -> T - -// TODO: We could offer a macro for all kinds of global actors, -// but we can't "just" implement it without a macro since the `@ACTOR () -> T` we can't abstract over -``` - -And we only offer the `assume` version that _does_ check (i.e. precondition-style) if we are on the main actor's executot. - -> Note: We do not offer any API to "get current executor" explicitly. I personally think steering people towards using actors as isolation domains, and expressing executors as "the ont that actor X is using" is useful enough, and we allow for this. - -Do note that users have no way of obtaining the specific "main actor executor" that conforms to SerialExecutor, we only ever expose it as the wrapped `UnownedExecutor`: - -```swift -@globalActor public final actor MainActor: GlobalActor { - public static let shared = MainActor() - - public nonisolated var unownedExecutor: UnownedSerialExecutor { - return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) - } - - public static var sharedUnownedExecutor: UnownedSerialExecutor { - return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) - } -} -``` - -This does mean however that developers can write these assertions: - -```swift -func sync() { - assertOnActorExecutor(MainActor.shared, "I sure hope I'm on the main actor") -} - -@MainActor -func test() { - sync() -} -``` - -All these APIs are expressed in terms of executors. - -> Naming note: I'd be open to ideas about naming this somehow around "the same mutually exclusive execution context" but the naming gets out of hand pretty quickly, so I stuck with ActorExecutor and Executor :thinking: - -Alternatively, we could have them speak about isolation context? Technically `assertSameActorIsolation(some Actor)` would be correct to compare the actors - -### "Deep" Executor equality - -By default, we assume pointer equality of executors is sufficient and correct. - -This exists today, and today is the only thing that users can do really. It is spelled as: - -```swift -extension MyCoolExecutor { - public func asUnownedSerialExecutor() -> UnownedSerialExecutor { - UnownedSerialExecutor(ordinary: self) - } -} -``` - -This is all good, but we should likely change the spelling a bit (this was also a point made in SE pitch review): - -```swift -extension MyCoolExecutor { - public func asUnownedSerialExecutor() -> UnownedSerialExecutor { - UnownedSerialExecutor(unique: self) // 5.9, same as the old ordinary: - } -} -``` - -The unique initializer keeps the current semantics of "*if the executor pointers are the same, it is the same executor and exclusive execution context*" fast path. - -If executors want to offer the "deep" equality they need to implement the `asUnownedExecutor` as: - -```swift -extension MyQueueThatTargetsAnotherQueueExecutor { - public func asUnownedSerialExecutor() -> UnownedSerialExecutor { - UnownedSerialExecutor(complexEquality: self) // TODO: unsure about the init name - } -} -``` - -The complex equality one must implement the following protocol requirement (that has a default implementation, because `SerialExecutor` is an existing protocol, so we must provide a default anyway): - -```swift - /// If this executor has complex equality semantics, and the runtime needs to compare - /// two executors, it will first attempt the usual pointer-based equality check, - /// and if it fails it will compare the types of both executors, if they are the same, - /// it will finally invoke this method, in an attempt to let the executor itself decide - /// if this and the `other` executor represent the same serial, exclusive, isolation context. - /// - /// This method must be implemented with great care, as wrongly returning `true` would allow - /// code from a different execution context (e.g. thread) to execute code which was intended - /// to be isolated by another actor. - /// - /// This check is not used when performing executor switching. - /// - /// This check is used when performing `assertOnSerialExecutor`, `preconditionOnSerialExecutor`, - /// `assumeMainActorExecutor` and similar APIs which assert about the same "exclusive execution context". - /// - /// - Parameter other: - /// - Returns: true, if `this` and the `other` executor actually are mutually exclusive, and it is safe from a concurrency perspective, to execute code assuming one on the other. - @available(SwiftStdlib 5.9, *) - func isSameExclusiveExecutionContext(other: Self) -> Bool -} - -@available(SwiftStdlib 5.9, *) -extension SerialExecutor { - func isSameExclusiveExecutionContext(other: Self) -> Bool { - self === other - } -} -``` - -For example, a dispatch queue based executor could implement this as follows: - -```swift -// how a dispatch queue based custom executor could use this: - - func isSameExclusiveExecutionContext(other: Self) -> Bool { - // pseudo-code - self.targetQueue == other.targetQueue - } -``` - -So even if two actors (`A` and `B`) are set up with different dispatch queue executors (`QA` and `QB`) but both target the same queue, dispatch could implement the `isSameExclusiveExecutionContext` method in such way that it returns true, based on the "target queue" information. - -For reference, an executor is stored like this in the Swift runtime, as an `ExecutorRef`: - -```swift -class ExecutorRef { - HeapObject *Identity; // Not necessarily Swift reference-countable - uintptr_t Implementation; // where we store our extra bit about "needs complexEquality" - - // We future-proof the ABI here by masking the low bits off the - // implementation pointer before using it as a witness table. - enum: uintptr_t { - WitnessTableMask = ~uintptr_t(alignof(void*) - 1) // 3 bits - }; -} -``` - - - -The following explains how equality for the `assert`/`precondition` and `assume...ActorExecutor` is implemented. - -We look at the type of the executor (the bit we store in the ExecutorRef, specifically in the Implementation / witness table field), and if both are: - -- **unique**, today's semantics ("**ordinary**"), may be thought of as "**root**" - - creation: - - using today's `UnownedSerialExecutor.init(ordinary:)` - - or a potentially new name for it `init(unique:)` - - comparison: - - compare the two executors pointers directly - - return the result - - notes: - - Default actors - - always have `Identity != 0` and `Implementation == 0` - - thus comparing their Identity gives us the expected result; as every default actor is its own isolation domain -- **complexEquality**, maybe a good name would be "`cooperativeEquality`"?), may be throught of as "**inner**" (i.e. it is not just a root) - - creation: - - `UnownedSerialExecutor` creation sets bit in the `ExecutorRef` - - comparison: - - If both executors are complexEquality, we **compare** the two executor **Identity** directly - - if true, return (fast-path, same as the unique ones) - - obtain and **compare** the **type** of both executors: - - `swift_getObjectType(executor1.Identity) == swift_getObjectType(executor2.Identity)` - - if false, return - - TODO: John mentioned a "compare the witness tables" but I didn't entirely follow about implementation, how we'd do that. - - invoke the **executor implemented comparison** the `executor1.isSameExclusiveExecutionContext(executor2)` - - TODO: We could make this `SerialExecutor.isSameExclusiveExecutionContext(executor1, executor2)` I think, but implementation wise not doing a static func was easier so for starters I did the simpler shape. - - return - - - -These checks are likely **NOT** enough to avoid switching, note from Rokhini: - -> “Mutual exclusion context equality” in `swift_task_switch()` is most likely **not sufficient** for determining that we can safely just run code inline instead of executor switching for dispatch queues cause dispatch isn’t holding all of the correct locks in place, other work might have built up behind the queue, etc. - -## Proposal - -1. We introduce the assert, precondition, assume APIs right now, along with the most basic existing custom actor executor API - 1. Aimed for Rainbow (SwiftData depends on it) -2. We follow up with the "**complexEquality**" additions - 1. if we make it for Rainbow, cool, if not, also okey - 2. it's just a caveat on these "queue targeting other queue" situations, which we'll improve later -3. Next, we continue towards the **switching support** in executors - 1. Unlikely to be in this release, but we started discussing the API shape which is good - 2. If we didn't do 2) by then, we can likely propose 2) and 3) together. \ No newline at end of file diff --git a/proposals/nnnn-custom-actor-executors.md b/proposals/nnnn-custom-actor-executors.md index 95c1f5345d..edd7217984 100644 --- a/proposals/nnnn-custom-actor-executors.md +++ b/proposals/nnnn-custom-actor-executors.md @@ -1,11 +1,12 @@ # Custom Actor Executors - Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/custom-actor-executors/proposals/NNNN-custom-actor-executors.md) -- Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [John McCall](https://github.com/rjmccall) -- Review Manager: TBD +- Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [John McCall](https://github.com/rjmccall), [Kavon Farvardin](https://github.com/kavon) +- Review Manager: [Holly Borla](https://github.com/hborla) - Status: **Partially implemented on `main`** - Previous threads: - Original pitch thread from around Swift 5.5: [Support custom executors in Swift Concurrency](https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425) + - Original "assume..." proposal which was subsumed into this proposal, as it relates closely to asserting on executors: [Pitch: Unsafe Assume on MainActor](https://forums.swift.org/t/pitch-unsafe-assume-on-mainactor/63074/) ## Table of Contents @@ -19,15 +20,16 @@ + [Serial Executors](#serial-executors) + [Jobs](#jobs) + [Actors with custom SerialExecutors](#actors-with-custom-serialexecutors) - + [Executor assertions](#executor-assertions) + + [Asserting on executors](#asserting-on-executors) + + [Assuming actor executors](#asserting-actor-executors) + [Default Swift Runtime Executors](#default-swift-runtime-executors) * [Source compatibility](#source-compatibility) * [Effect on ABI stability](#effect-on-abi-stability) * [Effect on API resilience](#effect-on-api-resilience) * [Alternatives considered](#alternatives-considered) * [Future Directions](#future-directions) - + [Overriding the MainActor executor](#overriding-the-mainactor-executor) - + [Executor Switching](#executor-switching) + + [Overriding the MainActor Executor](#overriding-the-mainactor-executor) + + [Executor Switching Optimizations](#executor-switching) + [Specifying Task executors](#specifying-task-executors) + [DelegateActor property](#delegateactor-property) @@ -65,9 +67,74 @@ Nonetheless, it is sometimes useful to more finely control how code is executed: This is the first proposal discussing custom executors and customization points in the Swift Concurrency runtime, and while it introduces only the most basic customization points, we are certain that it already provides significant value to users seeking tighter control over their actor's execution semantics. +Along with introducing ways to customize where code executes, this proposal also introduces ways to assert and assume the apropriate executor is used. This allows for more confidence when migrating away from other concurrency models to Swift Concurrency. + ## Proposed solution -We propose to allow actors to declare the executor they are required to execute on by declaring a nonisolated `unownedExecutor` property with the executor of their choice. +We propose to give developers the ability to implement simple serial executors, which then can be used with actors in order to ensure that any code executoring on such "actor with custom serial executor" runs on the apropriate thread or context. Implementing a naive executor takes the shape of: + +```swift +final class SpecificThreadExecutor: SerialExecutor { + let someThread: SomeThread // simplified handle to some specific thread + + func enqueue(_ job: consuming Job) { + someThread.run { + job.runSynchronously(on: self) + } + } + + func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(ordinary: self) + } +} + +extension SpecificThreadExecutor { + static var sharedUnownedExecutor: UnownedSerialExecutor { + // ... use some shared configured instance and return it ... + } +} +``` + +Such executor can then be used with an actor declaration by implementing its `unownedExecutor` property: + +```swift +actor Worker { + nonisolated var unownedExecutor: UnownedSerialExecutor { + // use the shared specific thread executor mentioned above. + // alternatively, we can pass specific executors to this actors init() and store and use them this way. + SpecificThreadExecutor.shared + } +} +``` + +And lastly, in order to increase the confidence during moves from other concurrency models to Swift Concurrency with custom executors, we also provide ways to assert that a piece of code is executing on the apropriate executor. These methods should be used only if there is not better way to express the requirement statically. For example by expressing the code as a method on a specific actor, or annotating it with a `@GlobalActor`, should be preferred to asserting when possible, however sometimes this is not possible due to the old code fulfilling synchronous protocol requirements that still have these threading requirements. + +Asserting the apropriate executor is used in a synchronous piece of code looks like this: + +````swift +func synchronousButNeedsMainActorContext() { + // check if we're executing on the maina actor context (or crash if we're not) + preconditionOnActorExecutor(MainActor.shared) + + // same as precondition, however only in DEBUG builds + assertOnActorExecutor(MainActor.shared) +} +```` + +Furthermore, we also offer a new API to safely "assume" an actor's execution context. For example, a synchronous function may know that it always will be invoked by the `MainActor` however for some reason it cannot be marked using `@MainActor`, this new API allows to assume (or crash if called from another execution context) the apropriate execution context, including the safety of synchronously accessing any state protected by the main actor executor: + +```swift +@MainActor func example() {} + +func alwaysOnMainActor() /* must be synchronous! */ { + assumeOnMainActorExecutor { // will crash if NOT invoked from the MainActor's executor + example() // ok to safely, synchronously, call + } +} + +// Always prefer annotating the method using a global actor, rather than assuming it though. +@MainActor func alwaysOnMainActor() /* must be synchronous! */ { } // better, but not always possible +``` ## Detailed design @@ -85,15 +152,13 @@ The concurrency runtime uses the `enqueue(_:)` method of an executor to schedule ```swift /// A service that can execute jobs. -@available(SwiftStdlib 5.1, *) public protocol Executor: AnyObject, Sendable { // This requirement is repeated here as a non-override so that we // get a redundant witness-table entry for it. This allows us to // avoid drilling down to the base conformance just for the basic // work-scheduling operation. - @available(SwiftStdlib 5.9, *) - func enqueue(_ job: __owned Job) + func enqueue(_ job: consuming Job) @available(SwiftStdlib 5.1, *) @available(*, deprecated, message: "Implement the enqueue(Job) method instead") @@ -115,7 +180,7 @@ final class MyOldExecutor: SerialExecutor { Executors are required to follow certain ordering rules when executing their jobs: -- The call to `SerialExecutor.runJobSynchronously(_:)` must happen-after the call to `enqueue(_:)`. +- The call to `Job.runSynchronously(on:)` must happen-after the call to `enqueue(_:)`. - If the executor is a serial executor, then the execution of all jobs must be *totally ordered*: for any two different jobs *A* and *B* submitted to the same executor with `enqueue(_:)`, it must be true that either all events in *A* happen-before all events in *B* or all events in *B* happen-before all events in *A*. - Do note that this allows the executor to reorder `A` and `B`–for example, if one job had a higher priority than the other–however they each independently must run to completion before the other one is allowed to run. @@ -133,12 +198,13 @@ We also define a `SerialExecutor` protocol, which is what actors use to guarante /// /// Serial executors do not, in general, guarantee specific run-order of jobs, /// and are free to re-order them e.g. using task priority, or any other mechanism. -@available(SwiftStdlib 5.1, *) public protocol SerialExecutor: Executor { /// Convert this executor value to the optimized form of borrowed /// executor references. - @available(SwiftStdlib 5.1, *) func asUnownedSerialExecutor() -> UnownedSerialExecutor + + // Discussed in depth in "Details of 'same executor' checking" of this proposal + func isSameExclusiveExecutionContext(other executor: Self) -> Bool } @available(SwiftStdlib 5.9, *) @@ -147,6 +213,10 @@ extension SerialExecutor { func asUnownedSerialExecutor() -> UnownedSerialExecutor { UnownedSerialExecutor(ordinary: self) } + + func isSameExclusiveExecutionContext(other: Self) -> Bool { + self === other + } } ``` @@ -164,10 +234,12 @@ A `SerialExecutor` does not introduce new API, other than the wrapping itself in /// also keep the actor's associated executor alive; if they are /// different objects, the executor must be referenced strongly by the /// actor. -@available(SwiftStdlib 5.1, *) -@frozen public struct UnownedSerialExecutor: Sendable { + /// The default and ordinary way to expose an unowned serial executor. public init(ordinary executor: __shared E) + + /// Discussed in depth in "Details of same-executor checking" of this proposal. + public init(complexEquality executor: __shared E) } ``` @@ -180,39 +252,45 @@ A `Job` is a representation of a chunk of of work that an executor should execut Whenever the Swift concurrency needs to execute some piece of work, it enqueues an `UnownedJob`s on a specific executor the job should be executed on. The `UnownedJob` type is an opaque wrapper around Swift's low-level representation of such job. It cannot be meaningfully inspected, copied and must never be executed more than once. ```swift -@available(SwiftStdlib 5.9, *) -@_moveOnly +@noncopyable public struct Job: Sendable { - /// Returns the priority sef on this Job. - /// - /// A Job priority is equal to TaskPriority if the job is a Task. - public var priority: Priority { get } + /// The priority of this job. + public var priority: JobPriority { get } } +``` -@available(SwiftStdlib 5.9, *) -extension Job { - // TODO: A JobPriority technically is the same in value as a TaskPriority, - // but it feels wrong to expose "Task..." named APIs on Job which - // may be not only tasks. - // - // TODO: Alternatively, we could typealias `Priority = TaskPriority` here - public struct Priority { - public typealias RawValue = UInt8 - public var rawValue: RawValue - - /// Convert this ``UnownedJob/Priority`` to a ``TaskPriority``. - public var asTaskPriority: TaskPriority? { ... } - - public var description: String { ... } - } +```swift +/// The priority of this job. +/// +/// The executor determines how priority information affects the way tasks are scheduled. +/// The behavior varies depending on the executor currently being used. +/// Typically, executors attempt to run tasks with a higher priority +/// before tasks with a lower priority. +/// However, the semantics of how priority is treated are left up to each +/// platform and `Executor` implementation. +/// +/// A Job's priority is roughly equivalent to a `TaskPriority`, +/// however, since not all jobs are tasks, represented as separate type. +/// +/// Conversions between the two priorities are available as initializers on the respective types. +public struct JobPriority { + public typealias RawValue = UInt8 + + /// The raw priority value. + public var rawValue: RawValue +} + +extension TaskPriority { + /// Convert a job priority to a task priority. + /// + /// Most values are directly interchangeable, but this initializer reserves the right to fail for certain values. + public init?(_ p: JobPriority) { ... } } ``` Because move-only types in the first early iteration of this language feature still have a number of limitations, we also offer an `UnownedJob` type, that is an unsafe "unowned" version of a `Job`. One reason one might need to reach for an `UnownedJob` is whenever a `Job` were to be used in a generic context, because in the initial version of move-only types that is available today, such types cannot appear in a generic context. For example, a naive queue implementation using an `[Job]` would be rejected by the compiler, but it is possible to express using an UnownedJob (i.e.`[UnownedJob]`). ```swift -@available(SwiftStdlib 5.1, *) -@frozen public struct UnownedJob: Sendable, CustomStringConvertible { /// Create an unsafe, unowned, job by consuming a move-only Job. @@ -220,12 +298,10 @@ public struct UnownedJob: Sendable, CustomStringConvertible { /// This may be necessary currently when intending to store a job in collections, /// or otherwise intreracting with generics due to initial implementation /// limitations of move-only types. - @available(SwiftStdlib 5.9, *) @usableFromInline - internal init(_ job: __owned Job) { ... } + internal init(_ job: consuming Job) { ... } - @available(SwiftStdlib 5.9, *) - public var priority: Priority { ... } + public var priority: JobPriority { ... } public var description: String { ... } } @@ -233,43 +309,28 @@ public struct UnownedJob: Sendable, CustomStringConvertible { A job's description includes its job or task ID, that can be used to correlate it with task dumps as well as task lists in Instruments and other debugging tools (e.g. `swift-inspect`'s ). A task ID is an unique number assigned to a task, and can be useful when debugging scheduling issues, this is the same ID that is currently exposed in tools like Instruments when inspecting tasks, allowing to correlate debug logs with observations from profiling tools. -Eventually, an executor will want to actually run a job. It may do so right away when it is enqueued, or on some different thread, this is entirely left up to the executor to decide. Running a job is done by calling the `runJobSynchronously` method which is provided on the SerialExecutor protocol. +Eventually, an executor will want to actually run a job. It may do so right away when it is enqueued, or on some different thread, this is entirely left up to the executor to decide. Running a job is done by calling the `runJobSynchronously` on a `Job` which consumes it. The same method is provided on the `UnownedJob` type, however that API is not as safe, since it cannot consume the job, and is open to running the same job multiple times accidentally which is undefined behavior. Generally, we urge developers to stick to using `Job` APIs whenever possible, and only move to the unowned API if the noncopyable `Job`s restrictions prove too strong to do the necessary operations on it. Running a `Job` _consumes_ it, and therefore it is not possible to accidentally run the same job twice, which would lead to undefined behavior if it were allowed. ```swift -@available(SwiftStdlib 5.9, *) -extension SerialExecutor { +extension Job { /// Run the job synchronously. /// /// This operation consumes the job. - @_alwaysEmitIntoClient - @inlinable - public func runJobSynchronously(_ job: __owned Job) { + public consuming func runSynchronously(_ job: __owned Job) { _swiftJobRun(UnownedJob(job), self) } +} +extension UnownedJob { /// Run the job synchronously. /// /// A job can only be run *once*. Accessing the job after it has been run is undefined behavior. - /// - Parameter job: - @_alwaysEmitIntoClient - @inlinable public func runUnownedJobSynchronously(_ job: UnownedJob) { _swiftJobRun(job, self) } } - -@available(SwiftStdlib 5.9, *) -extension UnownedSerialExecutor { - @_alwaysEmitIntoClient - @inlinable - public func runJobSynchronously(_ job: __owned Job) - - @_alwaysEmitIntoClient - @inlinable - public func runUnownedJobSynchronously(_ job: UnownedJob) -} ``` ### Actors with custom SerialExecutors @@ -383,15 +444,14 @@ actor MyActor: WithSpecifiedExecutor { A library could also provide a default implementation of such executor as well. -### Executor assertions - -Similar to the `unsafeAssumeOnMainActor` API pitched in [Pitch: Unsafe Assume on MainActor](https://forums.swift.org/t/pitch-unsafe-assume-on-mainactor/63074/), with the introduction of custom executors the same rationale of allowing people to migrate from other concurrency runtimes to swift concurrency _with confidence_ is something that applies to actors with custom executors as well. +### Asserting on executors A common pattern in event-loop heavy code–not yet using Swift Concurrency–is to ensure/verify that a synchronous piece of code is executed on the exected event-loop. Since one of the goals of making executors customizable is to allow such libraries to adopt Swift Concurrency by making such event-loops conform to `SerialExecutor`, it is useful to allow the checking if code is indeed executing on the apropriate executor, for the library to gain confidence while it is moving towards fully embracing actors and Swift concurrency. For example, Swift NIO intentionally avoids synchronization checks in some synchronous methods, in order to avoid the overhead of doing so, however in DEBUG mode it performs assertions that given code is running on the expected event-loop: ```swift +// Swift NIO private var _channel: Channel internal var channel: Channel { self.eventLoop.assertInEventLoop() @@ -403,6 +463,7 @@ internal var channel: Channel { Dispatch based systems also have similar functionality, with the `dispatchPrecondition` API: ```swift +// Dispatch func checkIfMainQueue() { dispatchPrecondition(condition: .onQueue(DispatchQueue.main)) } @@ -434,17 +495,32 @@ Sometimes, especially when porting existing codebases _to_ Swift Concurrency we /// additional runtime check for this, especially when moving towards Swift /// concurrency from other runtimes which frequently use such assertions. @available(SwiftStdlib 5.9, *) -public func preconditionTaskIsOnExecutor( +public func preconditionTaskOnExecutor( _ executor: some Executor, _ message: @autoclosure () -> String = "", file: String = #fileID, line: UInt = #line) -// Same as ``preconditionTaskIsOnExecutor(_:_:file:line)`` however only in DEBUG mode. @available(SwiftStdlib 5.9, *) -public func assertTaskIsOnExecutor( +public func preconditionTaskOnActorExecutor( + _ executor: some Actor, + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) +``` + +as well as an `assert...` version of this API, which triggers only in `debug` builds: + +```swift +// Same as ``preconditionTaskOnExecutor(_:_:file:line)`` however only in DEBUG mode. +@available(SwiftStdlib 5.9, *) +public func assertTaskOnExecutor( _ executor: some Executor, _ message: @autoclosure () -> String = "", file: String = #fileID, line: UInt = #line) + +public func assertTaskOnActorExecutor( + _ executor: some Actor, + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) ``` It should be noted that this API will return true whenever two actors share an executor. Semantically sharing a serial executor means running in the same isolation domain, however this is only known dynamically and `await`s are stil necessary for calls between such actors: @@ -469,14 +545,213 @@ Potential future work could enable static checking where a relationship between At this point, similar to Dispatch, these APIs only offer an "assert" / "precondition" version. And currently the way to dynamically get a boolean answer about being on a specific executor is not exposed. +### Assuming actor executors + +> Note: This API was initially pitched separately from custom executors, but as we worked on the feature we realized how closely it is related to custom executors and asserting on executors. The initial pitch thread is located here: [Pitch: Unsafe Assume on MainActor](https://forums.swift.org/t/pitch-unsafe-assume-on-mainactor/63074/). + +This revision of the proposal introduces the `assumeOnMainActorExecutor(_:)` method, which allows synchronous code to safely assume that they are called within the context of the main actor's executor. This is only available in synchronous functions, because the right way to spell this requirement in asynchronous code is to annotate the function using `@MainActor` which statically ensures this requirement. + +Synchronous code can assume that it is running on the main actor executor by using this assume method: + +```swift +/// Performs test at runtime check whether this function was called +/// while on the MainActor's executor. Then the operation is invoked +/// and its result is returned. +/// +/// - Attention: If this is called from a different execution context, +/// the method will crash, in order to prevent creating race conditions +/// with the MainActor. +@available(*, noasync) +func assumeOnMainActorExecutor( + _ operation: @MainActor () throws -> T, + file: StaticString = #fileID, line: UInt = #line +) rethrows -> T +``` + +Similarily to the assert and precondition APIs, this check is performed against the actor's executor, so if multiple actors are run on the same executor, this check will succeed in synchronous code invoked by such actors as well. In other words, the following code is also correct: + +```swift +func check(values: MainActorValues) /* synchronous! */ { + // values.get("any") // error: main actor isolated, cannot perform async call here + assumeOnMainActorExecutor { + values.get("any") // correct & safe + } +} + +actor Friend { + var unownedExecutor: UnownedSerialExecutor { + MainActor.sharedUnownedExecutor + } + + func callCheck(values: MainActorValues) { + check(values) // correct + } +} + +actor Unknown { + func callCheck(values: MainActorValues) { + check(values) // will crash, we're not on the MainActor executor + } +} + +@MainActor +final class MainActorValues { + func get(_: String) -> String { ... } +} +``` + +> Note: Because it is not possible to abstract over the `@SomeActor () -> T` function type's global actor isolation, we currently do not offer a version of this API for _any_ global actor, however it would be possible to implement such API today using macros, which could be expored in a follow-up proposal if seen as important enough. Such API would have to be spelled `#assumeOnGlobalActorExecutor(GlobalActor.self)`. + +In addition to the `MainActor` specialized API, the same shape of API is offered for instance actors and allows obtaining an `isolated` actor reference if we are guaranteed to be executing on the same serial executor as the given actor, and thus no concurrent access violations are possible. + +```swift +@available(*, noasync) +func assumeOnActorExecutor( + _ operation: (isolated Act) throws -> T, + file: StaticString = #fileID, line: UInt = #line +) rethrows -> T + +@available(*, noasync) +func assumeOnLocalDistributedActorExecutor( + _ operation: (isolated Act) throws -> T, + file: StaticString = #fileID, line: UInt = #line +) rethrows -> T +``` + +These assume methods have the same semantics as the just explained `assumeOnMainActorExecutor` in the sense that the check is performed about the actor's _executor_ and not specific instance. In other words, if many instance actors share the same serial executor, this check would pass for each of them, as long as the same executor is found to be the current one. + +### Details of "same executor" checking + +The previous two sections described the various `assert`, `precondition` and `assume` APIs all of which depend on the notion of "the same serial execution context". By default, every actor gets its own serial executor instance, and each such instance is unique. Therefore without sharing executors, each actor's serial executor is unique to itself, and thus the `precondition` APIs would efffectively check "are we on this _specific_ actor" even though the check is performed against the executor identity. + +#### Unique executors delegating to the same SerialExecutor + +There are two cases of checking "the same executor" that we'd like to discuss in this proposal. Firstly, even though some actors may want to share the a serial executor, sometimes developers may not want to receive this "different actors on same serial executor are in the same execution context" semantic for the various precondition checks. + +The solution here is in the way an executor may be implemented, and specifically, it is always possible to provide a _wrapper_ executor around another existing executor. This way we are able to assign unique executor identities, even if they would end up scheduling onto the same serial executor. As an example, this might look like this: + +```swift +final class SpecificThreadExecutor: SerialExecutor { ... } + +final class UniqueSpecificThreadExecutor: SerialExecutor { + let delegate SpecificThreadExecutor + init(delegate SpecificThreadExecutor) { + self.delegate = delegate + } + + func enqueue(_ job: consuming Job) { + delegate.enqueue(job) + } + + func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(ordinary: self) + } +} + +actor Worker { + let unownedExecutor: UnownedSerialExecutor + + init(executor: SpecificThreadExecutor) { + let uniqueExecutor = UniqueSpecificThreadExecutor(delegate: executor) + self.unownedExecutor = uniqueExecutor.asUnownedSerialExecutor() + } + + func test(other: Worker) { + assert(self !== other) + assertOnActorExecutor(other) // expected crash. + // `other` has different unique executor, + // even through they both eventually delegate to the same + } +} +``` + +#### Different executors offering the same execution context + +We also introduce an optional extension to serial executor identity compatibility checking, which allows an executor to _participate_ in the check. This is in order to handle the inverse situation to what we just discussed: when different executors _are_ in fact the same exclusive serial execution context and _want to_ inform Swift runtime about this for the purpose of these assertion APIs. + +One example of an executor which may have different unique instances of executors, however they should behave as the same exclusive serial execution context are dispatch queues which have the ability to "target" a different queue. In other words, it is possible to have a dispatch queue `Q1` and `Q2` target the same queue `Qx` (or even the "main" dispatch queue). + +In order to facilitate this capability, when exposing the `UnownedSerialExecutor` for itself, the executor must use the `init(complexEquality:)` initializer: + +```swift +extension MyQueueExecutor { + public func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(complexEquality: self) + } +} +``` + +The unique initializer keeps the current semantics of "*if the executor pointers are the same, it is the same executor and exclusive execution context*" fast path of executor equality checking, however it adds a "deep check" code-path if the equality has failed. + +When performing the "is this the same (or a compatible) serial execution context" checks, the Swift runtime first compares the raw pointers to the executor objects. If those are not equal and the executors in question have `complexEquality`, following some additional type-checks, the following `isSameExclusiveExecutionContext(other:)` method will be invoked: + +```swift +protocol SerialExecutor { + + // ... previously discussed protocol requirements ... + + /// If this executor has complex equality semantics, and the runtime needs to compare + /// two executors, it will first attempt the usual pointer-based equality check, + /// and if it fails it will compare the types of both executors, if they are the same, + /// it will finally invoke this method, in an attempt to let the executor itself decide + /// if this and the `other` executor represent the same serial, exclusive, isolation context. + /// + /// This method must be implemented with great care, as wrongly returning `true` would allow + /// code from a different execution context (e.g. thread) to execute code which was intended + /// to be isolated by another actor. + /// + /// This check is not used when performing executor switching. + /// + /// This check is used when performing `preconditionTaskOnActorExecutor`, `preconditionTaskOnActorExecutor`, + /// `assumeOnActorExecutor` and similar APIs which assert about the same "exclusive serial execution context". + /// + /// - Parameter other: + /// - Returns: true, if `self` and the `other` executor actually are mutually exclusive and it is safe–from a concurrency perspective–to execute code assuming one on the other. + func isSameExclusiveExecutionContext(other: Self) -> Bool +} + +extension SerialExecutor { + func isSameExclusiveExecutionContext(other: Self) -> Bool { + self === other + } +} +``` + +This API allows for executor, like for example dispatch queues in the future, to perform the "deep" check and e.g. return true if both executors are actually targeting the same thread or queue, and therefore guaranteeing a properly isolated mutually exclusive serial execution context. + +The API explicitly enforces that both executors must be of the same type, in order to avoid comparing completely unrelated executors using this rather expensive call into user code. The concrete logic for comparing executors for the purpose of the above described APIs is as follows: + +We inspect at the type of the executor (the bit we store in the ExecutorRef, specifically in the Implementation / witness table field), and if both are: + +- "**ordinary**" (or otherwise known as "unique"), which can be be thought of as definitely a "root" executor + - creation: + - using today's `UnownedSerialExecutor.init(ordinary:)` + - comparison: + - compare the two executors pointers directly + - return the result +- **complexEquality**, may be throught of as "**inner**" executor, i.e. one that's exact identity may need deeper introspection + - creation: + - `UnownedSerialExecutor(complexEquality:)` which sets specific bits that the runtime can recognize and enter the complex comparison code-path when necessary + - comparison: + - If both executors are `complexEquality`, we compare the two executor identity directly + - if true, return (fast-path, same as the ordinary executors ones) + - compare the witness tables of the executors, to see if they can potentially be compatible + - if false, return + - invoke the executor implemented comparison the `executor1.isSameExclusiveExecutionContext(executor2)` + - return the result + +These checks are likely *not* enough to to completely optimize task switching, and other mechanisms will be provided for optimized task switching in the future (see Future Directions). + ### Default Swift Runtime Executors -Swift concurrency provides a number of default executors already, such as the main actor executor and the default global concurrent executor, which all (default) actors target by their own per-actor instantiated serial executor instances. +Swift concurrency provides a number of default executors already, such as: + +- the main actor executor, which services any code annotated using @MainActor, and +- the default global concurrent executor, which all (default) actors target by their own per-actor instantiated serial executor instances. The `MainActor`'s executor is available via the `sharedUnownedExecutor` static property on the `MainActor`: ```swift -@available(SwiftStdlib 5.1, *) @globalActor public final actor MainActor: GlobalActor { public nonisolated var unownedExecutor: UnownedSerialExecutor public static var sharedUnownedExecutor: UnownedSerialExecutor @@ -493,7 +768,15 @@ actor Friend { } ``` -The default global concurrent executor is currently not replacable. +Note that the raw type of the MainActor executor is never exposed, but we merely get unowned wrappers for it. This allows the Swift runtime to pick various specific implementations depending on the runtime environment. + +Even though we do not have a concrete class type that we can use to pass to the `some Executor` based assertion APIs, we can use the `MainActor.shared` instance together with the `some Actor` based precondition, like this: + +```swift +preconditionTaskOnExecutor(MainActor.shared) +``` + +The default global concurrent executor is not accessible direcly from code, however it is the executor that handles all the tasks which do not have a specific executor requirement, or are explicitly required to run on that executor, e.g. like top-level async functions. ## Source compatibility @@ -507,7 +790,7 @@ Swift's concurrency runtime has already been using executors, jobs and tasks sin The design of `SerialExecutor` currently does not support non-reentrant actors, and it does not support executors for which dispatch is always synchronous (e.g. that just acquire a traditional mutex). -To further explain the relationship of new and existing APIs in this proposal, we opted to keep the `@available` annotations on the discussed types, so that it is clearer which APIs exist today and cannot be entirely changed, and which APIs are new additions. Notably, there are no official APIs for operations like running a job before they were introduced in this prposal. +Some of the APIs discussed in this proposal existed from the first introduction of Swift Concurrency, so making any breaking changes to them is not possible. Some APIs were carefully renamed and polished up though. We encourage discussion of all the types and methods present in this proposal, however changing some of them may prove to be challanging or impossible due to ABI impact. ## Effect on API resilience From b4ce750999f3b5aa4c769277fdf830e7c1e07e5e Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Thu, 2 Mar 2023 23:22:09 -0500 Subject: [PATCH 3009/4563] Accept SE-0382 (#1965) --- proposals/0382-expression-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index 94027f4352..2fc3fcf3e5 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -3,9 +3,9 @@ * Proposal: [SE-0382](0382-expression-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (February 10...20, 2023)** +* Status: **Accepted** * Implementation: Partial implementation is available in `main` under the experimental feature flag `Macros`. An [example macro repository](https://github.com/DougGregor/swift-macro-examples) provides a way to experiment with this feature. -* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861)) ([review #1](https://forums.swift.org/t/se-0382-expression-macros/62090)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0382-expression-macros/62898)) ([pitch #3](https://forums.swift.org/t/se-0382-expression-macros-mini-pitch-for-updates/62810)) ([review #2](https://forums.swift.org/t/se-0382-second-review-expression-macros/63064)) +* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861)) ([review #1](https://forums.swift.org/t/se-0382-expression-macros/62090)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0382-expression-macros/62898)) ([pitch #3](https://forums.swift.org/t/se-0382-expression-macros-mini-pitch-for-updates/62810)) ([review #2](https://forums.swift.org/t/se-0382-second-review-expression-macros/63064)) ([acceptance](https://forums.swift.org/t/accepted-se-0382-expression-macros/63495)) ## Introduction From ba1541232aa1108ad9d1433b9b63824cf093a774 Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Fri, 3 Mar 2023 10:10:00 +0000 Subject: [PATCH 3010/4563] Revise the async stream factory proposal to use a tuple instead --- proposals/0388-async-stream-factory.md | 124 +++++++------------------ 1 file changed, 33 insertions(+), 91 deletions(-) diff --git a/proposals/0388-async-stream-factory.md b/proposals/0388-async-stream-factory.md index 08f2aa9664..e45ae28e33 100644 --- a/proposals/0388-async-stream-factory.md +++ b/proposals/0388-async-stream-factory.md @@ -7,6 +7,9 @@ * Implementation: [apple/swift#62968](https://github.com/apple/swift/pull/62968) * Review: ([pitch](https://forums.swift.org/t/pitch-convenience-async-throwing-stream-makestream-methods/61030)) ([review](https://forums.swift.org/t/se-0388-convenience-async-throwing-stream-makestream-methods/63139)) +## Changelog +- 03/03/2023: Changed the return type from a concrete type to a tuple + ## Introduction We propose introducing helper methods for creating `AsyncStream` and `AsyncThrowingStream` @@ -56,18 +59,18 @@ In order to fill this gap, I propose to add a new static method `makeStream` on and the continuation. An example of using the new proposed convenience methods looks like this: ```swift -let newStream = AsyncStream.makeStream(of: Int.self) +let (continuation, stream) = AsyncStream.makeStream(of: Int.self) await withTaskGroup(of: Void.self) { group in group.addTask { for i in 0...9 { - newStream.continuation.yield(i) + continuation.yield(i) } - newStream.continuation.finish() + continuation.finish() } group.addTask { - for await i in newStream.stream { + for await i in stream { print(i) } } @@ -77,84 +80,48 @@ await withTaskGroup(of: Void.self) { group in ## Detailed design I propose to add the following code to `AsyncStream` and `AsyncThrowingStream` -respectively. +respectively. These methods are also marked as backdeployed to previous Swift versions. ```swift +@available(SwiftStdlib 5.1, *) extension AsyncStream { - /// Struct for the return type of ``AsyncStream/makeStream(of:bufferingPolicy:)``. - /// - /// This struct contains two properties: - /// 1. The ``continuation`` which should be retained by the producer and is used - /// to yield new elements to the stream and finish it. - /// 2. The ``stream`` which is the actual ``AsyncStream`` and - /// should be passed to the consumer. - public struct NewStream: Sendable { - /// The continuation of the ``AsyncStream`` used to yield and finish. - public let continuation: AsyncStream.Continuation - - /// The stream which should be passed to the consumer. - public let stream: AsyncStream - - private init(stream: AsyncStream, continuation: AsyncStream.Continuation) { - self.stream = stream - self.continuation = continuation - } - } - /// Initializes a new ``AsyncStream`` and an ``AsyncStream/Continuation``. /// /// - Parameters: /// - elementType: The element type of the stream. /// - limit: The buffering policy that the stream should use. - /// - Returns: A ``NewStream`` struct which contains the stream and its continuation. + /// - Returns: A tuple containing the stream and its continuation. The continuation should be passed to the + /// producer while the stream should be passed to the consumer. + @backDeployed(before: SwiftStdlib 5.9) public static func makeStream( of elementType: Element.Type = Element.self, bufferingPolicy limit: Continuation.BufferingPolicy = .unbounded - ) -> NewStream { - let storage: _Storage = .create(limit: limit) - let stream = AsyncStream(storage: storage) - let continuation = Continuation(storage: storage) - return .init(stream: stream, continuation: continuation) + ) -> (stream: AsyncStream, continuation: AsyncStream.Continuation) { + var continuation: AsyncStream.Continuation! + let stream = AsyncStream(bufferingPolicy: limit) { continuation = $0 } + return (stream: stream, continuation: continuation!) } } +@available(SwiftStdlib 5.1, *) extension AsyncThrowingStream { - /// Struct for the return type of ``AsyncThrowingStream/makeStream(of:throwing:bufferingPolicy:)``. - /// - /// This struct contains two properties: - /// 1. The ``continuation`` which should be retained by the producer and is used - /// to yield new elements to the stream and finish it. - /// 2. The ``stream`` which is the actual ``AsyncThrowingStream`` and - /// should be passed to the consumer. - public struct NewStream: Sendable { - /// The continuation of the ``AsyncThrowingStream`` used to yield and finish. - public let continuation: AsyncThrowingStream.Continuation - - /// The stream which should be passed to the consumer. - public let stream: AsyncThrowingStream - - private init(stream: AsyncThrowingStream, continuation: AsyncThrowingStream.Continuation) { - self.stream = stream - self.continuation = continuation - } - } - /// Initializes a new ``AsyncThrowingStream`` and an ``AsyncThrowingStream/Continuation``. /// /// - Parameters: /// - elementType: The element type of the stream. /// - failureType: The failure type of the stream. /// - limit: The buffering policy that the stream should use. - /// - Returns: A ``NewStream`` struct which contains the stream and its continuation. + /// - Returns: A tuple containing the stream and its continuation. The continuation should be passed to the + /// producer while the stream should be passed to the consumer. + @backDeployed(before: SwiftStdlib 5.9) public static func makeStream( of elementType: Element.Type = Element.self, throwing failureType: Failure.Type = Failure.self, bufferingPolicy limit: Continuation.BufferingPolicy = .unbounded - ) -> NewStream { - let storage: _Storage = .create(limit: limit) - let stream = AsyncThrowingStream(storage: storage) - let continuation = Continuation(storage: storage) - return .init(stream: stream, continuation: continuation) + ) -> (stream: AsyncThrowingStream, continuation: AsyncThrowingStream.Continuation) where Failure == Error { + var continuation: AsyncThrowingStream.Continuation! + let stream = AsyncThrowingStream(bufferingPolicy: limit) { continuation = $0 } + return (stream: stream, continuation: continuation!) } } ``` @@ -163,47 +130,22 @@ extension AsyncThrowingStream { This change is additive and does not affect source compatibility. ## Effect on ABI stability -This change introduces new concurrency library ABI in the form of the two `makeStream` methods and `NewStream` structs, but it does not affect the ABI of existing declarations. +This change introduces new concurrency library ABI in the form of the one `makeStream` methods, but it does not affect the ABI of existing declarations. ## Effect on API resilience -None; adding nested types and static methods is permitted by the existing resilience model. +None; adding static methods is permitted by the existing resilience model. ## Alternatives considered -### Return a tuple instead of a concrete type +### Return a concrete type instead of a tuple My initial pitch was using a tuple as the result type of the factory; -however, I walked back on it since I think we can provide better documentation on -the concrete type. Furthermore, it makes it more discoverable as well. - -The upside of using a tuple based approach is that we can backdeploy it. - -An implementation returning a tuple would look like this; - -```swift -extension AsyncStream { - /// Initializes a new ``AsyncStream`` and an ``AsyncStream/Continuation``. - /// - /// - Parameters: - /// - elementType: The element type of the stream. - /// - limit: The buffering policy that the stream should use. - /// - Returns: A tuple which contains the stream and its continuation. - public static func makeStream( - of elementType: Element.Type = Element.self, - bufferingPolicy limit: Continuation.BufferingPolicy = .unbounded - ) -> (stream: AsyncStream, continuation: AsyncStream.Continuation) { - let storage: _Storage = .create(limit: limit) - let stream = AsyncStream(storage: storage) - let continuation = Continuation(storage: storage) - return (stream: stream, continuation: continuation) - } -} -``` +however, I walked back on it before the review since I think we can provide better documentation on +the concrete type. However during the review the majority of the feedback was leaning towards the tuple based approach. +After comparing the two approaches, I agree with the review feedback. The tuple based approach has two major benefits: -### Expose an initializer on the `NewStream` type -During the pitch it was brought up that we could expose an `init` on the `NewStream` types -that this proposal wants to add. I decided against that since one would have to spell out -`AsyncStream.NewStream()` to access the `init`. This is quite hard to discover in -my opinion. +1. It nudges the user to destructure the returned typed which we want since the continuation and stream should be retained by the +producer and consumer respectively. +2. It allows us to back deploy the method. ### Pass a continuation to the `AsyncStream.init()` During the pitch it was brought up that we could let users pass a continuation to the From a0e49505e588d312575ce2f1e2464f6c60b6b7a3 Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Fri, 3 Mar 2023 14:50:05 +0000 Subject: [PATCH 3011/4563] Apply suggestions from code review Co-authored-by: Ben Rimmington --- proposals/0388-async-stream-factory.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0388-async-stream-factory.md b/proposals/0388-async-stream-factory.md index e45ae28e33..2bfa6a75c4 100644 --- a/proposals/0388-async-stream-factory.md +++ b/proposals/0388-async-stream-factory.md @@ -59,7 +59,7 @@ In order to fill this gap, I propose to add a new static method `makeStream` on and the continuation. An example of using the new proposed convenience methods looks like this: ```swift -let (continuation, stream) = AsyncStream.makeStream(of: Int.self) +let (stream, continuation) = AsyncStream.makeStream(of: Int.self) await withTaskGroup(of: Void.self) { group in group.addTask { @@ -130,7 +130,7 @@ extension AsyncThrowingStream { This change is additive and does not affect source compatibility. ## Effect on ABI stability -This change introduces new concurrency library ABI in the form of the one `makeStream` methods, but it does not affect the ABI of existing declarations. +This change introduces new concurrency library ABI in the form of the `makeStream` methods, but it does not affect the ABI of existing declarations. ## Effect on API resilience None; adding static methods is permitted by the existing resilience model. From beb0221ece170e3d4622b5386c95a5d6aacfa244 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Mon, 6 Mar 2023 13:38:45 -0500 Subject: [PATCH 3012/4563] Update SE-0382 to match feedback on API (#1966) We've decided that this is a Swift Syntax change that doesn't need to be re-reviewed, but that we should update the proposal text to match the adopted name. Leaving this as a PR for now to be merged when the implementation matches. --- proposals/0382-expression-macros.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index 2fc3fcf3e5..8d530a7b27 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -271,7 +271,7 @@ The macro expansion context provides additional information about the environmen /// which a given macro is being expanded. public protocol MacroExpansionContext: AnyObject { /// Generate a unique name for use in the macro. - public func createUniqueName(_ name: String) -> TokenSyntax + public func makeUniqueName(_ name: String) -> TokenSyntax /// Emit a diagnostic (i.e., warning or error) that indicates a problem with the macro /// expansion. @@ -297,7 +297,7 @@ public protocol MacroExpansionContext: AnyObject { } ``` -The `createUniqueName()` function allows one to create new, unique names so that the macro expansion can produce new declarations that won't conflict with any other declarations in the same scope. It produces an identifier token containing the unique name, which will also incorporate the `name` identifier for better debuggability. This allows macros to be more hygienic, by not introducing new names that could affect the way that the code provided via macro expansion arguments is type-checked. +The `makeUniqueName()` function allows one to create new, unique names so that the macro expansion can produce new declarations that won't conflict with any other declarations in the same scope. It produces an identifier token containing the unique name, which will also incorporate the `name` identifier for better debuggability. This allows macros to be more hygienic, by not introducing new names that could affect the way that the code provided via macro expansion arguments is type-checked. It is intended that `MacroExpansionContext` will grow over time to include more information about the build environment in which the macro is being expanded. For example, information about the target platform (such as OS, architecture, and deployment version) and any compile-time definitions passed via `-D`, should be included as part of the context. @@ -338,7 +338,7 @@ public enum SourceLocationFilePathMode { } ``` -Source locations are described in an abstract form that can be interpolated into source code (they are expressions) in places that expect a string literal (for the file name) or integer literal (for line and column). As with `createUniqueName` returning a `TokenSyntax` rather than a `String`, this abstraction allows the compiler to introduce a different kind of syntax node (that might not even be expressible in normal Swift) to represent these values. +Source locations are described in an abstract form that can be interpolated into source code (they are expressions) in places that expect a string literal (for the file name) or integer literal (for line and column). As with `makeUniqueName` returning a `TokenSyntax` rather than a `String`, this abstraction allows the compiler to introduce a different kind of syntax node (that might not even be expressible in normal Swift) to represent these values. ```swift /// Abstractly represents a source location in the macro. @@ -576,7 +576,7 @@ Expressions are just one place in the language where macros could be valuable. O * Introduce a general `location` operation on `MacroExpansionContext` to get the source location of any syntax node from a macro input. Remove the `moduleName` and `fileName`, which were always too limited to be useful. * Allow macro parameters to have default arguments, with restrictions on what can occur within a default argument. * Clarify that macro expansion cannot be recursive. - * Rename `createUniqueLocalName` to `createUniqueName`; the names might not always be local in scope. Also add a parameter to it so developers can provide a partial name that will show up in the unique name. + * Rename `createUniqueLocalName` to `makeUniqueName`; the names might not always be local in scope. Also add a parameter to it so developers can provide a partial name that will show up in the unique name. * Prohibit the use of non-builtin macros as default arguments of parameters. * Revisions from the second pitch: * Moved SwiftPM manifest changes to a separate proposal that can explore the building of macros in depth. This proposal will focus only on the language aspects. From ad5570e1a5e4c39dc6139e1713d933d4acde6ace Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 7 Mar 2023 17:34:45 +0900 Subject: [PATCH 3013/4563] Adjust run APIs to actual implemented names, and fixup text a bit --- proposals/nnnn-custom-actor-executors.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/proposals/nnnn-custom-actor-executors.md b/proposals/nnnn-custom-actor-executors.md index edd7217984..a411895d47 100644 --- a/proposals/nnnn-custom-actor-executors.md +++ b/proposals/nnnn-custom-actor-executors.md @@ -309,16 +309,14 @@ public struct UnownedJob: Sendable, CustomStringConvertible { A job's description includes its job or task ID, that can be used to correlate it with task dumps as well as task lists in Instruments and other debugging tools (e.g. `swift-inspect`'s ). A task ID is an unique number assigned to a task, and can be useful when debugging scheduling issues, this is the same ID that is currently exposed in tools like Instruments when inspecting tasks, allowing to correlate debug logs with observations from profiling tools. -Eventually, an executor will want to actually run a job. It may do so right away when it is enqueued, or on some different thread, this is entirely left up to the executor to decide. Running a job is done by calling the `runJobSynchronously` on a `Job` which consumes it. The same method is provided on the `UnownedJob` type, however that API is not as safe, since it cannot consume the job, and is open to running the same job multiple times accidentally which is undefined behavior. Generally, we urge developers to stick to using `Job` APIs whenever possible, and only move to the unowned API if the noncopyable `Job`s restrictions prove too strong to do the necessary operations on it. - -Running a `Job` _consumes_ it, and therefore it is not possible to accidentally run the same job twice, which would lead to undefined behavior if it were allowed. +Eventually, an executor will want to actually run a job. It may do so right away when it is enqueued, or on some different thread, this is entirely left up to the executor to decide. Running a job is done by calling the `runSynchronously` on a `Job` which consumes it. The same method is provided on the `UnownedJob` type, however that API is not as safe, since it cannot consume the job, and is open to running the same job multiple times accidentally which is undefined behavior. Generally, we urge developers to stick to using `Job` APIs whenever possible, and only move to the unowned API if the noncopyable `Job`s restrictions prove too strong to do the necessary operations on it. ```swift extension Job { /// Run the job synchronously. /// /// This operation consumes the job. - public consuming func runSynchronously(_ job: __owned Job) { + public consuming func runSynchronously(on executor: UnownedSerialExecutor) { _swiftJobRun(UnownedJob(job), self) } } @@ -327,7 +325,7 @@ extension UnownedJob { /// Run the job synchronously. /// /// A job can only be run *once*. Accessing the job after it has been run is undefined behavior. - public func runUnownedJobSynchronously(_ job: UnownedJob) { + public func runSynchronously(on executor: UnownedSerialExecutor) { _swiftJobRun(job, self) } } From 96a88976133455f4f5d8bab179bb7734d0f78048 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 7 Mar 2023 16:27:45 +0000 Subject: [PATCH 3014/4563] Update README.md (#1970) Swift 5.9 Release Process --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7424e1a88d..8b800f3b11 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ This repository tracks the ongoing evolution of the Swift programming language, | Version | Announced | Released | | :-------- | :----------------------------------------------------------------------- | :----------------------------------------------------------- | +| Swift 5.9 | [2023-03-06](https://forums.swift.org/t/swift-5-9-release-process/63557) | | Swift 5.8 | [2022-11-19](https://forums.swift.org/t/swift-5-8-release-process/61540) | | Swift 5.7 | [2022-03-29](https://forums.swift.org/t/swift-5-7-release-process/56316) | [2022-09-12](https://www.swift.org/blog/swift-5.7-released/) | | Swift 5.6 | [2021-11-10](https://forums.swift.org/t/swift-5-6-release-process/53412) | [2022-03-14](https://www.swift.org/blog/swift-5.6-released/) | From 732d42a9ff50871a69a99bc26ba58c4c768db26f Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 7 Mar 2023 16:40:06 +0000 Subject: [PATCH 3015/4563] Update 0000-swift-template.md * Remove "Swift-evolution thread". * Change "withour" to "without". --- proposal-templates/0000-swift-template.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/proposal-templates/0000-swift-template.md b/proposal-templates/0000-swift-template.md index 28fd94c84c..786b1befbc 100644 --- a/proposal-templates/0000-swift-template.md +++ b/proposal-templates/0000-swift-template.md @@ -95,8 +95,6 @@ A short description of what the feature is. Try to keep it to a single-paragraph "elevator pitch" so the reader understands what problem this proposal is addressing. -Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/) - ## Motivation Describe the problems that this proposal seeks to address. If the @@ -217,7 +215,7 @@ version. Consider also the impact on library adopters of those features. Can adopting this feature in a library break source or ABI compatibility for users of the library? If a library adopts the feature, can it -be *un*-adopted later withour breaking source or ABI compatibility? +be *un*-adopted later without breaking source or ABI compatibility? Will package authors be able to selectively adopt this feature depending on the tools version available, or will it require bumping the minimum tools version required by the package? From 8b1df0bda7179fc0c6fa276387fb5adcf6f1bdd8 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Tue, 7 Mar 2023 11:27:51 -0800 Subject: [PATCH 3016/4563] Tweak revision history format and accept proposal --- proposals/0388-async-stream-factory.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/proposals/0388-async-stream-factory.md b/proposals/0388-async-stream-factory.md index 2bfa6a75c4..ccc539495e 100644 --- a/proposals/0388-async-stream-factory.md +++ b/proposals/0388-async-stream-factory.md @@ -3,12 +3,9 @@ * Proposal: [SE-0388](0388-async-stream-factory.md) * Authors: [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) -* Status: **Active review (February 15...26, 2023)** +* Status: **Accepted** * Implementation: [apple/swift#62968](https://github.com/apple/swift/pull/62968) -* Review: ([pitch](https://forums.swift.org/t/pitch-convenience-async-throwing-stream-makestream-methods/61030)) ([review](https://forums.swift.org/t/se-0388-convenience-async-throwing-stream-makestream-methods/63139)) - -## Changelog -- 03/03/2023: Changed the return type from a concrete type to a tuple +* Review: ([pitch](https://forums.swift.org/t/pitch-convenience-async-throwing-stream-makestream-methods/61030)) ([review](https://forums.swift.org/t/se-0388-convenience-async-throwing-stream-makestream-methods/63139)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0388-convenience-async-throwing-stream-makestream-methods/63568)) ## Introduction @@ -161,3 +158,8 @@ users from misuse. We could just leave the current creation of `Async[Throwing]Stream` as is; however, since it is part of the standard library we should provide a better method to create a stream and its continuation. + +## Revision history + +- After review: Changed the return type from a concrete type to a tuple + From 2e940b4761bdd47155cb60a8cc640471eca206f1 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 8 Mar 2023 11:42:30 +0900 Subject: [PATCH 3017/4563] fix typos --- proposals/nnnn-custom-actor-executors.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/proposals/nnnn-custom-actor-executors.md b/proposals/nnnn-custom-actor-executors.md index a411895d47..9544be4fcd 100644 --- a/proposals/nnnn-custom-actor-executors.md +++ b/proposals/nnnn-custom-actor-executors.md @@ -78,6 +78,7 @@ final class SpecificThreadExecutor: SerialExecutor { let someThread: SomeThread // simplified handle to some specific thread func enqueue(_ job: consuming Job) { + let unownedJob = UnownedJob(job) // in order to escape it to the run{} closure someThread.run { job.runSynchronously(on: self) } @@ -492,13 +493,11 @@ Sometimes, especially when porting existing codebases _to_ Swift Concurrency we /// custom executors. However, in some APIs it may be useful to provide an /// additional runtime check for this, especially when moving towards Swift /// concurrency from other runtimes which frequently use such assertions. -@available(SwiftStdlib 5.9, *) public func preconditionTaskOnExecutor( _ executor: some Executor, _ message: @autoclosure () -> String = "", file: String = #fileID, line: UInt = #line) -@available(SwiftStdlib 5.9, *) public func preconditionTaskOnActorExecutor( _ executor: some Actor, _ message: @autoclosure () -> String = "", @@ -509,7 +508,6 @@ as well as an `assert...` version of this API, which triggers only in `debug` bu ```swift // Same as ``preconditionTaskOnExecutor(_:_:file:line)`` however only in DEBUG mode. -@available(SwiftStdlib 5.9, *) public func assertTaskOnExecutor( _ executor: some Executor, _ message: @autoclosure () -> String = "", @@ -632,8 +630,8 @@ The solution here is in the way an executor may be implemented, and specifically final class SpecificThreadExecutor: SerialExecutor { ... } final class UniqueSpecificThreadExecutor: SerialExecutor { - let delegate SpecificThreadExecutor - init(delegate SpecificThreadExecutor) { + let delegate: SpecificThreadExecutor + init(delegate: SpecificThreadExecutor) { self.delegate = delegate } From 1929fff8c9c6986a5912a9295ad26624e044532c Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 8 Mar 2023 11:42:32 +0900 Subject: [PATCH 3018/4563] include how error messages look like from preconditions --- proposals/nnnn-custom-actor-executors.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/proposals/nnnn-custom-actor-executors.md b/proposals/nnnn-custom-actor-executors.md index 9544be4fcd..0289e38942 100644 --- a/proposals/nnnn-custom-actor-executors.md +++ b/proposals/nnnn-custom-actor-executors.md @@ -519,6 +519,13 @@ public func assertTaskOnActorExecutor( file: String = #fileID, line: UInt = #line) ``` +These APIs offer better diagnostics than would be possible to implement using a plain `precondition()` implemented by developers using some `precondition(isOnExpectedExecutor(someExecutor))` because they offer a description of the actually active executor when mismatched: + +````swift +preconditionTaskOnActorExecutor(MainActor.shared) +// Precondition failed: Incorrect actor executor assumption; Expected 'MainActorExecutor' executor, but was executing on 'Sample.InlineExecutor'. +```` + It should be noted that this API will return true whenever two actors share an executor. Semantically sharing a serial executor means running in the same isolation domain, however this is only known dynamically and `await`s are stil necessary for calls between such actors: ```swift From 76eb8a91ce591aa7584918df7cf139f2d7a5686e Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 8 Mar 2023 12:08:25 +0900 Subject: [PATCH 3019/4563] remove incomplete sentence --- proposals/nnnn-custom-actor-executors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-custom-actor-executors.md b/proposals/nnnn-custom-actor-executors.md index 0289e38942..d41dd61cff 100644 --- a/proposals/nnnn-custom-actor-executors.md +++ b/proposals/nnnn-custom-actor-executors.md @@ -544,7 +544,7 @@ actor B { } ``` -Potential future work could enable static checking where a relationship between actors is expressed statically (actor B declaring that it is on the same serial executor as a specific instance of `A`), and therefore awaits would not be necessary between such two specific actor instances. Such work is not within the scope of this initial proposal though, and only the dynamic aspect is proposed right now. Do note however that +Potential future work could enable static checking where a relationship between actors is expressed statically (a specific instance of `B` declaring that it is on the same serial executor as a specific instance of `A`), and therefore awaits would not be necessary between such two specific actor instances. Such work is not within the scope of this initial proposal though, and only the dynamic aspect is proposed right now. At this point, similar to Dispatch, these APIs only offer an "assert" / "precondition" version. And currently the way to dynamically get a boolean answer about being on a specific executor is not exposed. From 9314bacb87024d4a3b83b54c960745eb8c5cb796 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 8 Mar 2023 13:17:16 -0800 Subject: [PATCH 3020/4563] Accept SE-0389. --- proposals/0389-attached-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0389-attached-macros.md b/proposals/0389-attached-macros.md index c981e44ece..fd5991bb22 100644 --- a/proposals/0389-attached-macros.md +++ b/proposals/0389-attached-macros.md @@ -3,9 +3,9 @@ * Proposal: [SE-0389](0389-attached-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Holly Borla](https://github.com/hborla), [Richard Wei](https://github.com/rxwei) * Review Manager: Tony Allevato (https://github.com/allevato) -* Status: **Active review (February 16...March 2, 2023)** +* Status: **Accepted** * Implementation: Implemented on GitHub `main` behind the experimental flag `Macros`. See the [example repository](https://github.com/DougGregor/swift-macro-examples) for more macros. -* Review: ([pitch #1, under the name "declaration macros"](https://forums.swift.org/t/pitch-declaration-macros/62373)) ([pitch #2](https://forums.swift.org/t/pitch-attached-macros/62812)) ([review](https://forums.swift.org/t/se-0389-attached-macros/63165)) +* Review: ([pitch #1, under the name "declaration macros"](https://forums.swift.org/t/pitch-declaration-macros/62373)) ([pitch #2](https://forums.swift.org/t/pitch-attached-macros/62812)) ([review](https://forums.swift.org/t/se-0389-attached-macros/63165)) ([acceptance](https://forums.swift.org/t/accepted-se-0389-attached-macros/63593)) ## Introduction From a538e32283f751de2e2f48ee7a1c6acd8a2f19e0 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 9 Mar 2023 09:41:43 +0900 Subject: [PATCH 3021/4563] final fixups so proposal has same APIs as impl --- proposals/nnnn-custom-actor-executors.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/nnnn-custom-actor-executors.md b/proposals/nnnn-custom-actor-executors.md index d41dd61cff..339abd99f8 100644 --- a/proposals/nnnn-custom-actor-executors.md +++ b/proposals/nnnn-custom-actor-executors.md @@ -494,7 +494,7 @@ Sometimes, especially when porting existing codebases _to_ Swift Concurrency we /// additional runtime check for this, especially when moving towards Swift /// concurrency from other runtimes which frequently use such assertions. public func preconditionTaskOnExecutor( - _ executor: some Executor, + _ executor: some SerialExecutor, _ message: @autoclosure () -> String = "", file: String = #fileID, line: UInt = #line) @@ -509,7 +509,7 @@ as well as an `assert...` version of this API, which triggers only in `debug` bu ```swift // Same as ``preconditionTaskOnExecutor(_:_:file:line)`` however only in DEBUG mode. public func assertTaskOnExecutor( - _ executor: some Executor, + _ executor: some SerialExecutor, _ message: @autoclosure () -> String = "", file: String = #fileID, line: UInt = #line) @@ -756,8 +756,8 @@ The `MainActor`'s executor is available via the `sharedUnownedExecutor` static p ```swift @globalActor public final actor MainActor: GlobalActor { - public nonisolated var unownedExecutor: UnownedSerialExecutor - public static var sharedUnownedExecutor: UnownedSerialExecutor + public nonisolated var unownedExecutor: UnownedSerialExecutor { get { ... } } + public static var sharedUnownedExecutor: UnownedSerialExecutor { get { ... } } } ``` @@ -776,7 +776,7 @@ Note that the raw type of the MainActor executor is never exposed, but we merely Even though we do not have a concrete class type that we can use to pass to the `some Executor` based assertion APIs, we can use the `MainActor.shared` instance together with the `some Actor` based precondition, like this: ```swift -preconditionTaskOnExecutor(MainActor.shared) +preconditionTaskOnActorExecutor(MainActor.shared) ``` The default global concurrent executor is not accessible direcly from code, however it is the executor that handles all the tasks which do not have a specific executor requirement, or are explicitly required to run on that executor, e.g. like top-level async functions. From beafb487d71b1847c5eb0b95a1ccb67d36d0892a Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 9 Mar 2023 11:07:54 +0900 Subject: [PATCH 3022/4563] Remove un-necessary @available --- proposals/nnnn-custom-actor-executors.md | 1 - 1 file changed, 1 deletion(-) diff --git a/proposals/nnnn-custom-actor-executors.md b/proposals/nnnn-custom-actor-executors.md index 339abd99f8..cef45780f5 100644 --- a/proposals/nnnn-custom-actor-executors.md +++ b/proposals/nnnn-custom-actor-executors.md @@ -161,7 +161,6 @@ public protocol Executor: AnyObject, Sendable { // work-scheduling operation. func enqueue(_ job: consuming Job) - @available(SwiftStdlib 5.1, *) @available(*, deprecated, message: "Implement the enqueue(Job) method instead") func enqueue(_ job: UnownedJob) } From d0f3b1be5d1737c786d4c7563c1de79847c629ba Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 8 Mar 2023 18:11:57 -0800 Subject: [PATCH 3023/4563] Kick off review of Custom Executors as SE-0392 --- ...m-actor-executors.md => 0392-custom-actor-executors.md} | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename proposals/{nnnn-custom-actor-executors.md => 0392-custom-actor-executors.md} (99%) diff --git a/proposals/nnnn-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md similarity index 99% rename from proposals/nnnn-custom-actor-executors.md rename to proposals/0392-custom-actor-executors.md index cef45780f5..b96edec2f4 100644 --- a/proposals/nnnn-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -1,9 +1,10 @@ # Custom Actor Executors -- Proposal: [SE-NNNN](https://github.com/apple/swift-evolution/blob/custom-actor-executors/proposals/NNNN-custom-actor-executors.md) +- Proposal: [SE-0392](https://github.com/apple/swift-evolution/blob/custom-actor-executors/proposals/0392-custom-actor-executors.md) - Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [John McCall](https://github.com/rjmccall), [Kavon Farvardin](https://github.com/kavon) -- Review Manager: [Holly Borla](https://github.com/hborla) -- Status: **Partially implemented on `main`** +- Review Manager: [Joe Groff](https://github.com/jckarter) +- Status: **Active review (March 8 ... March 21, 2023)** +- Implementation: Partially implemented on `main` - Previous threads: - Original pitch thread from around Swift 5.5: [Support custom executors in Swift Concurrency](https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425) - Original "assume..." proposal which was subsumed into this proposal, as it relates closely to asserting on executors: [Pitch: Unsafe Assume on MainActor](https://forums.swift.org/t/pitch-unsafe-assume-on-mainactor/63074/) From cfd784d27d98afefdf421533bff6eb3f049752ca Mon Sep 17 00:00:00 2001 From: Joseph Heck Date: Wed, 8 Mar 2023 20:01:20 -0800 Subject: [PATCH 3024/4563] Update 0392-custom-actor-executors.md minor typo fix in the introduction (mutial -> mutual) --- proposals/0392-custom-actor-executors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index b96edec2f4..cfab9d9483 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -38,7 +38,7 @@ As Swift Concurrency continues to mature it is becoming increasingly important to offer adopters tighter control over where exactly asynchronous work is actually executed. -This proposal introduces a basic mechanism for customizing actor executors. By providing an instance of an executor, actors can influence "where" they will be executing any task they are running, while upholding the mutial excusion and actor isolation guaranteed by the actor model. +This proposal introduces a basic mechanism for customizing actor executors. By providing an instance of an executor, actors can influence "where" they will be executing any task they are running, while upholding the mutual excusion and actor isolation guaranteed by the actor model. > **Note:** This proposal defines only a set of APIs to customize actor executors, and other kinds of executor control is out of scope for this specific proposal. From 36ba2a429637da13dd89e2bec2cd16004e4d373f Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 10 Mar 2023 11:00:54 +0900 Subject: [PATCH 3025/4563] Typo in executor reference --- proposals/0392-custom-actor-executors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index cfab9d9483..4bffbd9334 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -104,7 +104,7 @@ actor Worker { nonisolated var unownedExecutor: UnownedSerialExecutor { // use the shared specific thread executor mentioned above. // alternatively, we can pass specific executors to this actors init() and store and use them this way. - SpecificThreadExecutor.shared + SpecificThreadExecutor.sharedUnownedExecutor } } ``` From 700546f5aad3cb6a82269d27936591c19add1339 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 10 Mar 2023 02:23:46 +0000 Subject: [PATCH 3026/4563] Update 0392-custom-actor-executors.md (#1975) Fix dashboard error. --- proposals/0392-custom-actor-executors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index 4bffbd9334..e6cf904cad 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -1,6 +1,6 @@ # Custom Actor Executors -- Proposal: [SE-0392](https://github.com/apple/swift-evolution/blob/custom-actor-executors/proposals/0392-custom-actor-executors.md) +- Proposal: [SE-0392](0392-custom-actor-executors.md) - Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [John McCall](https://github.com/rjmccall), [Kavon Farvardin](https://github.com/kavon) - Review Manager: [Joe Groff](https://github.com/jckarter) - Status: **Active review (March 8 ... March 21, 2023)** From 5f00a9709d236abaf5a015d958fcfafb520b0585 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 15 Mar 2023 10:35:45 -0700 Subject: [PATCH 3027/4563] Update SE-0390 in response to LWG review (#1976) * Update SE-0390 in response to LWG review This version of the proposal makes the following changes from the [first reviewed revision](https://github.com/apple/swift-evolution/blob/5d075b86d57e3436b223199bd314b2642e30045f/proposals/0390-noncopyable-structs-and-enums.md) of this proposal: - The original revision did not provide a `Copyable` generic constraint, and declared types as noncopyable using a `@noncopyable` attribute. The language workgroup believes that it is a good idea to build toward a future where noncopyable types are integrated with the language's generics system, and that the syntax for suppressing generic constraints is a good general notation to have for suppressing implicit conformances or assumptions about generic capabilities we may take away in the future, so it makes sense to provide a syntax that allows for growth in those directions. - The original revision suppressed implicit `deinit` within methods using the spelling `forget self`. Although the term `forget` has a precedent in Rust, the behavior of `mem::forget` in Rust doesn't correspond to the semantics of the operation proposed here, and the language workgroup doesn't find the term clear enough on its own. This revision of the proposal chooses the more explicit `suppressdeinit`, which is long and awkward but at least explicit, as a starting point for further review discussion. - The original revision allowed for a `consuming` method declared anywhere in the type's original module to suppress `deinit`. This revision narrows the capability to only methods declared in the same file as the type, for consistency with other language features that depend on having visibility into a type's entire layout, such as implicit `Sendable` inference. * Expand discussion of `~Constraint` syntax Clarify that it is removing an assumed requirement, not negating the requirement. --- .../0390-noncopyable-structs-and-enums.md | 398 ++++++++++++------ 1 file changed, 264 insertions(+), 134 deletions(-) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index 36aa1eb4ff..2502a03c80 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -1,4 +1,4 @@ -# `@noncopyable` structs and enums +# Noncopyable structs and enums * Proposal: [SE-0390](0390-noncopyable-structs-and-enums.md) * Authors: [Joe Groff](https://github.com/jckarter), [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Kavon Farvardin](https://github.com/kavon) @@ -6,6 +6,7 @@ * Status: **Active review (February 21 ... March 7, 2023)** * Implementation: on main as `@_moveOnly` behind the `-enable-experimental-move-only` option * Review: [(pitch)](https://forums.swift.org/t/pitch-noncopyable-or-move-only-structs-and-enums/61903)[(review)](https://forums.swift.org/t/se-0390-noncopyable-structs-and-enums/63258) +* Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/5d075b86d57e3436b223199bd314b2642e30045f/proposals/0390-noncopyable-structs-and-enums.md) ## Introduction @@ -31,9 +32,9 @@ represent unique resources with *unique ownership*. ## Proposed solution -We propose to allow for `struct` and `enum` types to be declared with the -`@noncopyable` attribute, which specifies that the declared type is -*noncopyable*. Values of noncopyable type always have unique ownership, and +We propose to allow for `struct` and `enum` types to declare themselves as +*noncopyable*, using a new syntax for suppressing implied generic constraints, +`~Copyable`. Values of noncopyable type always have unique ownership, and can never be copied (at least, not using Swift's implicit copy mechanism). Since values of noncopyable structs and enums have unique identities, they can also have `deinit` declarations, like classes, which run automatically at the @@ -42,8 +43,7 @@ end of the unique instance's lifetime. For example, a basic file descriptor type could be defined as: ```swift -@noncopyable -struct FileDescriptor { +struct FileDescriptor: ~Copyable { private var fd: Int32 init(fd: Int32) { self.fd = fd } @@ -68,30 +68,138 @@ that uniquely owns the instance. ## Detailed design +### The `Copyable` generic constraint + +Before this proposal, almost every type in Swift was automatically copyable. +The standard library provides a new generic constraint `Copyable` to make +this capability explicit. All existing first-class types (excluding nonescaping +closures) implicitly satisfy this constraint, and all generic type parameters, +existential types, protocols, and associated type requirements implicitly +require it. Types may explicitly declare that they are `Copyable`, and generic +types may explicitly require `Copyable`, but this currently has no effect. + +``` +struct Foo: Copyable {} +``` + +### Suppressing assumed generic requirements with `~Constraint` + +There are situations where a type is implicitly assumed to satisfy a generic +requirement because of aspects of its declaration. For instance, enums that +don't have any associated values are implicitly made `Hashable` (and, +by refinement, `Equatable`): + +``` +enum Foo { + case a, b, c +} + +// OK to compare with `==` because `Foo` is automatically Equatable +print(Foo.a == Foo.b) +``` + +and internal structs and enums are implicitly `Sendable` if all of their +components are `Sendable`: + +``` +struct Bar { + var x: Int, y: Int +} + +func foo() async { + let x = Bar(x: 17, y: 38) + + // OK to use x in an async task because it's implicitly Sendable + async let y = x +} +``` + +However, this isn't always desirable; an enum may want to reserve the right to +add associated values in the future that aren't `Equatable`, or a type may be +made up of `Sendable` components that represent resources that are not safe +to share across threads. There is currently no direct way to suppress these +assumed generic requirements. We propose to introduce the `~Constraint` syntax +as a way to explicitly suppress a generic requirement that would be assumed on +a declaration: + +``` +enum Foo: ~Equatable { + case a, b, c +} + +// ERROR: `Foo` does not conform to `Equatable` +print(Foo.a == Foo.b) + +struct ThreadUnsafeHandle: ~Sendable { + // although this is an integer, it represents a system resource that + // can only be accessed from a specific thread, and should not be shared + // across threads + var handle: Int32 +} + +func foo(handle: ThreadUnsafeHandle) async { + // ERROR: `ThreadUnsafeHandle` is not `Sendable` + async let y = handle +} +``` + +It is important to note that `~Constraint` only removes the assumption of an +implicit requirement, but it does **not** mean that the type strictly does not +conform to the protocol. Extensions may add the conformance back separately, +possibly conditionally: + +``` +struct ResourceHandle: ~Sendable { + // although this is an integer, it represents a system resource that + // gives access to values of type `T`, which may not be thread safe + // across threads + var handle: Int32 +} + +// It is safe to share the handle when the resource type is thread safe +extension ResourceHandle: Sendable where T: Sendable {} + +// Suppress the default Equatable (and Hashable) implementation... +enum Foo: ~Equatable { + case a, b, c, a2 +} + +// ...but provide our own Equatable +extension Foo: Equatable { + static func ==(a: Foo, b: Foo) { + switch (a, b) { + // a2 is equal to a + case (.a, .a), (.b, .b), (.c, .c), (.a, .a2), (.a2, .a): + return true + default: + return false + } + } +} +``` + ### Declaring noncopyable types -A `struct` or `enum` type can be declared as noncopyable using the `@noncopyable` -attribute: +A `struct` or `enum` type can be declared as noncopyable by suppressing the +`Copyable` requirement on their declaration, by combining the new `Copyable` +constraint with the new requirement suppression syntax `~Copyable`: ```swift -@noncopyable -struct FileDescriptor { +struct FileDescriptor: ~Copyable { private var fd: Int32 } ``` If a `struct` has a stored property of noncopyable type, or an `enum` has a case with an associated value of noncopyable type, then the containing type -must also be declared `@noncopyable`: +must also suppress its `Copyable` capability: ```swift -@noncopyable -struct SocketPair { +struct SocketPair: ~Copyable { var in, out: FileDescriptor } -@noncopyable -enum FileOrMemory { +enum FileOrMemory: ~Copyable { // write to an OS file case file(FileDescriptor) // write to an array in memory @@ -114,6 +222,38 @@ class SharedFile { } ``` +A class type declaration may not use `~Copyable`; all class types remain copyable +by retaining and releasing references to the object. + +``` +// ERROR: classes must be `Copyable` +class SharedFile: ~Copyable { + var file: FileDescriptor +} +``` + +It is also not yet allowed to suppress the `Copyable` requirement on generic +parameters, associated type requirements in protocols, or the `Self` type +in a protocol declaration: + +``` +// ERROR: generic parameter types must be `Copyable` +func foo(x: T) {} + +// ERROR: types that conform to protocols must be `Copyable` +protocol Foo where Self: ~Copyable { + // ERROR: associated type requirements must be `Copyable` + associatedtype Bar: ~Copyable +} +``` + +`Copyable` also cannot be suppressed in existential type declarations: + +``` +// ERROR: `any` types must be `Copyable` +let foo: any ~Copyable = FileDescriptor() +``` + ### Restrictions on use in generics Noncopyable types may have generic type parameters: @@ -121,8 +261,7 @@ Noncopyable types may have generic type parameters: ```swift // A type that reads from a file descriptor consisting of binary values of type T // in sequence. -@noncopyable -struct TypedFile { +struct TypedFile: ~Copyable { var rawFile: FileDescriptor func read() -> T { ... } @@ -131,8 +270,9 @@ struct TypedFile { let byteFile: TypedFile // OK ``` -However, at this time, noncopyable types themselves are not allowed to be used -as a generic type. This means a noncopyable type _cannot_: +At this time, as noted above, generic types are still always required to be +`Copyable`, so noncopyable types themselves are not allowed to be used as a +generic type argument. This means a noncopyable type _cannot_: - conform to any protocols, except for `Sendable`. - serve as a type witness for an `associatedtype` requirement. @@ -169,7 +309,7 @@ would copy its underlying value: ```swift extension FileDescriptor: Sendable {} // OK -@noncopyable struct RefHolder: Sendable { +struct RefHolder: ~Copyable, Sendable { var ref: Ref // ERROR: stored property 'ref' of 'Sendable'-conforming struct 'RefHolder' has non-sendable type 'Ref' } @@ -211,7 +351,7 @@ let fds: [FileDescriptor] = [] print(FileDescriptor(-1)) // ERROR: Noncopyable struct SocketEvent cannot conform to Error -@noncopyable enum SocketEvent: Error { +enum SocketEvent: ~Copyable, Error { case requestedDisconnect(SocketPair) } ``` @@ -241,8 +381,7 @@ ideal. But until that is supported, a concrete noncopyable enum can represent the case where the value of interest was taken out of the instance: ```swift -@noncopyable -enum MaybeFileDescriptor { +enum MaybeFileDescriptor: ~Copyable { case some(FileDescriptor) case none } @@ -278,14 +417,14 @@ types like `MaybeFileDescriptor` through a noncopyable `Optional` (see Future Directions). -### Using values of `@noncopyable` type +### Using noncopyable values As the name suggests, values of noncopyable type cannot be copied, a major break from most other types in Swift. Many operations are currently defined as working as pass-by-value, and use copying as an implementation technique -to give that semantics, but they need to be defined more precisely in terms of -how they *borrow* or *consume* their operands in order to define their -effects on values that cannot be copied. +to give that semantics, but these operations now need to be defined more +precisely in terms of how they *borrow* or *consume* their operands in order to +define their effects on values that cannot be copied. We use the term **consume** to refer to an operation that invalidates the value that it operates on. It may do this by directly @@ -434,8 +573,7 @@ The following operations are consuming: `borrowing`: ```swift - @noncopyable - struct S { + struct S: ~Copyable { var x: FileDescriptor, y: Int } let x = FileDescriptor() @@ -472,8 +610,7 @@ The following operations are consuming: if let y = consume x { ... } use(x) // ERROR: x consumed by `if let` - @noncopyable - enum FileDescriptorOrBuffer { + enum FileDescriptorOrBuffer: ~Copyable { case file(FileDescriptor) case buffer(String) } @@ -641,8 +778,7 @@ is particularly useful as a way of forwarding ownership of part of an aggregate, such as to take ownership away from a wrapper type: ``` -@noncopyable -struct FileDescriptorWrapper { +struct FileDescriptorWrapper: ~Copyable { private var _value: FileDescriptor var value: FileDescriptor { @@ -652,7 +788,7 @@ struct FileDescriptorWrapper { ``` However, a `consuming get` cannot be paired with a setter when the containing -type is `@noncopyable`, because invoking the getter consumes the aggregate, +type is `~Copyable`, because invoking the getter consumes the aggregate, leaving nothing to write a modified value back to. Because getters return owned values, non-`consuming` getters generally cannot @@ -674,20 +810,18 @@ computed properties to also provide "read" and "modify" coroutines, which would have the ability to yield borrowing or mutating access to properties without copying them. -### Using stored properties and enum cases of `@noncopyable` type +### Using stored properties and enum cases of noncopyable type -When classes or `@noncopyable` types contain members that are of `@noncopyable` +When classes or noncopyable types contain members that are of noncopyable type, then the container is the unique owner of the member value. Outside of the type's definition, client code cannot perform consuming operations on the value, since it would need to take away the container's ownership to do so: ``` -@noncopyable -struct Inner {} +struct Inner: ~Copyable {} -@noncopyable -struct Outer { +struct Outer: ~Copyable { var inner = Inner() } @@ -711,8 +845,7 @@ consuming operation, so it can only be performed inside `consuming` methods on the type's original definition: ``` -@noncopyable -enum OuterEnum { +enum OuterEnum: ~Copyable { case inner(Inner) case file(FileDescriptor) } @@ -874,13 +1007,12 @@ func foo() { ### Deinitializers -A `@noncopyable` struct or enum may declare a `deinit`, which will run +A noncopyable struct or enum may declare a `deinit`, which will run implicitly when the lifetime of the value ends (unless explicitly suppressed as noted below): ```swift -@noncopyable -struct FileDescriptor { +struct FileDescriptor: ~Copyable { private var fd: Int32 deinit { @@ -927,13 +1059,11 @@ circumstances: `deinit` if any runs. ```swift - @noncopyable - struct Inner { + struct Inner: ~Copyable { deinit { print("destroying inner") } } - @noncopyable - struct Outer { + struct Outer: ~Copyable { var inner = Inner() deinit { print("destroying outer") } } @@ -959,8 +1089,7 @@ under normal circumstances, a `consuming` method will still invoke the type's own logic already invalidates the value: ```swift -@noncopyable -struct FileDescriptor { +struct FileDescriptor: ~Copyable { private var fd: Int32 deinit { @@ -982,13 +1111,13 @@ from the deinit, such as raising an error (which a normal `deinit` is unable to do) if the `close` system call triggers an OS error : ```swift -@noncopyable -struct FileDescriptor { +struct FileDescriptor: ~Copyable { private var fd: Int32 consuming func close() throws { - // POSIX close may raise an error (which still invalidates the - // file descriptor, but may indicate a condition worth handling) + // POSIX close may raise an error (which leaves the file descriptor in an + // unspecified state, so we can't really try to close it again, but the + // error may nonetheless indicate a condition worth handling) if close(fd) != 0 { throw CloseError(errno) } @@ -1002,8 +1131,7 @@ or it could be useful to take manual control of the file descriptor back from the type, such as to pass to a C API that will take care of closing it: ```swift -@noncopyable -struct FileDescriptor { +struct FileDescriptor: ~Copyable { // Take ownership of the C file descriptor away from this type, // returning the file descriptor without closing it consuming func take() -> Int32 { @@ -1014,44 +1142,42 @@ struct FileDescriptor { } ``` -We propose to introduce a special operator, `forget self`, which ends the +We propose to introduce a special operator, `suppressdeinit self`, which ends the lifetime of `self` without running its `deinit`: ```swift -@noncopyable -struct FileDescriptor { +struct FileDescriptor: ~Copyable { // Take ownership of the C file descriptor away from this type, // returning the file descriptor without closing it consuming func take() -> Int32 { let fd = self.fd - forget self + suppressdeinit self return fd } } ``` -`forget self` can only be applied to `self`, in a consuming method defined in -the type's defining module. (This is in contrast to Rust's similar special -function, [`mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html), -which is a standalone function which can be applied to any value, anywhere. -Although the Rust documentation notes that this operation is "safe" on the -principle that destructors may not run at all, due to reference cycles, -process termination, etc., in practice the ability to forget arbitrary values -creates semantic issues for many Rust APIs, particularly when there are -destructors on types with lifetime dependence on each other like `Mutex` -and `LockGuard`. As such, we -think it is safer to restrict the ability to forget a value to the -core API of its type. We can relax this restriction if experience shows a -need to.) - -Even with the ability to `forget self`, care would still need be taken when +`suppressdeinit self` can only be applied to `self`, in a consuming method +defined in the same file as the type's original definition. (This is in +contrast to Rust's similar special function, +[`mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html), which is a +standalone function that can be applied to any value, anywhere. Although the +Rust documentation notes that this operation is "safe" on the principle that +destructors may not run at all, due to reference cycles, process termination, +etc., in practice the ability to forget arbitrary values creates semantic +issues for many Rust APIs, particularly when there are destructors on types +with lifetime dependence on each other like `Mutex` and `LockGuard`. As such, +we think it is safer to restrict the ability to suppress the standard `deinit` +for a value to the core API of its type. We can relax this restriction if +experience shows a need to.) + +Even with the ability to `suppressdeinit self`, care would still need be taken when writing destructive operations to avoid triggering the deinit on alternative exit paths, such as early `return`s, `throw`s, or implicit propagation of errors from `try` operations. For instance, if we write: ```swift -@noncopyable -struct FileDescriptor { +struct FileDescriptor: ~Copyable { private var fd: Int32 consuming func close() throws { @@ -1059,32 +1185,31 @@ struct FileDescriptor { // file descriptor, but may indicate a condition worth handling) if close(fd) != 0 { throw CloseError(errno) - // !!! Oops, we didn't forget self on this path, so we'll deinit! + // !!! Oops, we didn't suppress deinit on this path, so we'll double close! } // We don't need to deinit self anymore - forget self + suppressdeinit self } } ``` -then the `throw` path exits the method without `forget`-ing `self`, so +then the `throw` path exits the method without `suppressdeinit`, and `deinit` will still execute if an error occurs. To avoid this mistake, we -propose that if any path through a method uses `forget self`, then **every** -path must choose either to `forget` or to explicitly `consume self` using -the standard `deinit`. This will make -the above code an error, alerting that the code should be rewritten to ensure -`forget self` always executes: +propose that if any path through a method uses `suppressdeinit self`, then +**every** path must choose either to `suppressdeinit` or to explicitly `consume self`, +which triggers the standard `deinit`. This will make the above code an error, +alerting that the code should be rewritten to ensure `suppressdeinit self` +always executes: ```swift -@noncopyable -struct FileDescriptor { +struct FileDescriptor: ~Copyable { private var fd: Int32 consuming func close() throws { // Save the file descriptor and give up ownership of it let fd = self.fd - forget self + suppressdeinit self // We can now use `fd` below without worrying about `deinit`: @@ -1099,12 +1224,11 @@ struct FileDescriptor { The [consume operator](https://github.com/apple/swift-evolution/blob/main/proposals/0377-parameter-ownership-modifiers.md) must be used to explicitly end the value's lifetime using its `deinit` if -`forget` is used to conditionally destroy the value on other paths through -the method. +`suppressdeinit` is used to conditionally destroy the value on other paths +through the method. ``` -@noncopyable -struct MemoryBuffer { +struct MemoryBuffer: ~Copyable { private var address: UnsafeRawPointer init(size: Int) throws { @@ -1123,7 +1247,7 @@ struct MemoryBuffer { // Save the memory buffer and give it to the caller, who // is promising to free it when they're done. let address = self.address - forget self + suppressdeinit self return address } else { // We still want to free the memory if we aren't giving it away. @@ -1140,12 +1264,13 @@ For existing Swift code, this proposal is additive. ## Effect on ABI stability -### Adding or removing `@noncopyable` breaks ABI +### Adding or removing `Copyable` breaks ABI -An existing copyable struct or enum cannot be made `@noncopyable` without -breaking ABI, since existing clients may copy values of the type. +An existing copyable struct or enum cannot have its `Copyable` capability +taken away without breaking ABI, since existing clients may copy values of the +type. -Ideally, we would allow noncopyable types to become copyable without breaking +Ideally, we would allow noncopyable types to become `Copyable` without breaking ABI; however, we cannot promise this, due to existing implementation choices we have made in the ABI that cause the copyability of a type to have unavoidable knock-on effects. In particular, when properties are declared in classes, @@ -1242,7 +1367,8 @@ types lack the ability to satisfy this constraint, more explicit. ### Spelling as a generic constraint It's a reasonable question why declaring a type as noncopyable isn't spelled -like a protocol constraint: +like a regular protocol constraint, instead of as the removal of an existing +constraint: ``` struct Foo: NonCopyable {} @@ -1262,8 +1388,8 @@ extension Dictionary: NonCopyable where Key: NonCopyable, Value: NonCopyable {} ``` this says that the dictionary is noncopyable only when both the key and value -are noncopyable, which is wrong because we also can't copy the dictionary if only -the keys or only the values are noncopyable. If we flip the constraint to +are noncopyable, which is wrong because we can't copy the dictionary even if +only the keys or only the values are noncopyable. If we flip the constraint to `Copyable`, the correct thing would fall out naturally: ``` @@ -1271,27 +1397,11 @@ extension Dictionary: Copyable where Key: Copyable, Value: Copyable {} ``` However, for progressive disclosure and source compatibility reasons, we still -want the majority of types to be `Copyable` by default, without making them +want the majority of types to be `Copyable` by default without making them explicitly declare it; noncopyable types are likely to remain the exception rather than the rule, with automatic lifetime management via ARC by the compiler being sufficient for most code like it is today. -We could conversely borrow another idea from Rust, which uses the syntax -`?Trait` to declare that a normally implicit trait is not required by -a generic declaration, or not satisfied by a concrete type. So in Swift we -might write: - -``` -struct Foo: ?Copyable { -} -``` - -`Copyable` is currently the only such implicit constraint we are considering, -so the `@noncopyable` attribute is appealing as a specific solution to address -this case. If we were to consider making other requirements implicit (perhaps -`Sendable` in some situations?) then a more general opt-out syntax would be -called for. - ### English language bikeshedding Some dictionaries specify that "copiable" is the standard spelling for "able to @@ -1299,11 +1409,6 @@ copy", although the Oxford English Dictionary and Merriam-Webster both also list "copyable" as an accepted alternative. We prefer the more regular "copyable" spelling. -The negation could just as well be spelled `@uncopyable` instead of `@noncopyable`. -Swift has precedent for favoring `non-` in modifiers, including `@nonescaping` -parameters and and `nonisolated` actor members, so we choose to follow that -precedent. - ## Future directions ### Noncopyable tuples @@ -1332,8 +1437,7 @@ writing `nil` back. Eventually this could be written as an extension method on `Optional`: ``` -@noncopyable -extension Optional { +extension Optional where Self: ~Copyable { mutating func take() -> Wrapped { switch self { case .some(let wrapped): @@ -1400,9 +1504,7 @@ to be copyable for some generic arguments but not all. A simple case might be a tuple-like `Pair` struct: ``` -@noncopyable -// : ?Copyable is strawman syntax for declaring T and U don't require copying -struct Pair { +struct Pair: ~Copyable { var first: T var second: U } @@ -1421,7 +1523,7 @@ As currently specified, noncopyable types are (outside of `init` implementations always either fully initialized or fully destroyed, without any support for incremental destruction inside of `consuming` methods or deinits. A `deinit` may modify, but not invalidate, `self`, and a `consuming` method may -`forget self`, forward ownership of all of `self`, or destroy `self`, but cannot +`suppressdeinit self`, forward ownership of all of `self`, or destroy `self`, but cannot yet partially consume parts of `self`. This would be particularly useful for types that contain other noncopyable types, which may want to relinquish ownership of some or all of the resources owned by those members. In the current @@ -1429,8 +1531,7 @@ proposal, this isn't possible without allowing for an intermediate invalid state: ```swift -@noncopyable -struct SocketPair { +struct SocketPair: ~Copyable { let input, output: FileDescriptor // Gives up ownership of the output end, closing the input end @@ -1440,7 +1541,7 @@ struct SocketPair { // However, we can't do this without being able to either copy // `self.output` or partially invalidate `self` let output = self.output - forget self + suppressdeinit self return output } } @@ -1449,9 +1550,9 @@ struct SocketPair { Analogously to how `init` implementations use a "definite initialization" pass to allow the value to initialized field-by-field, we can implement the inverse dataflow pass to allow `deinit` implementations, as well as `consuming` -methods that `forget self`, to partially invalidate `self`. +methods that `suppressdeinit self`, to partially invalidate `self`. -### `read` and `modify` accessor coroutines for computed properties. +### `read` and `modify` accessor coroutines for computed properties The current computed property model allows for properties to provide a getter, which returns the value of the property on read to the caller as an owned value, @@ -1473,3 +1574,32 @@ borrowing or mutating, instead of passing copies of values back and forth. We can expose the ability for code to implement these coroutines directly, which is a good optimization for copyable value types, but also allows for more expressivity with noncopyable properties. + +## Revision history + +This version of the proposal makes the following changes from the +[first reviewed revision](https://github.com/apple/swift-evolution/blob/5d075b86d57e3436b223199bd314b2642e30045f/proposals/0390-noncopyable-structs-and-enums.md) +of this proposal: + +- The original revision did not provide a `Copyable` generic constraint, and + declared types as noncopyable using a `@noncopyable` attribute. The + language workgroup believes that it is a good idea to build toward a future + where noncopyable types are integrated with the language's generics system, + and that the syntax for suppressing generic constraints is a good general + notation to have for suppressing implicit conformances or assumptions about + generic capabilities we may take away in the future, so it makes sense to + provide a syntax that allows for growth in those directions. + +- The original revision suppressed implicit `deinit` within methods using the + spelling `forget self`. Although the term `forget` has a precedent in Rust, + the behavior of `mem::forget` in Rust doesn't correspond to the semantics of + the operation proposed here, and the language workgroup doesn't find the + term clear enough on its own. This revision of the proposal chooses the + more explicit `suppressdeinit`, which is long and awkward but at least + explicit, as a starting point for further review discussion. + +- The original revision allowed for a `consuming` method declared anywhere in + the type's original module to suppress `deinit`. This revision narrows the + capability to only methods declared in the same file as the type, for + consistency with other language features that depend on having visibility into + a type's entire layout, such as implicit `Sendable` inference. From 2d873b813d2b328241682da28b7c709b35a93385 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 15 Mar 2023 20:30:17 -0700 Subject: [PATCH 3028/4563] Mark SE-0380 as implemented in Swift 5.9 (#1978) --- proposals/0380-if-switch-expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0380-if-switch-expressions.md b/proposals/0380-if-switch-expressions.md index 582dba0f63..5f8247dca0 100644 --- a/proposals/0380-if-switch-expressions.md +++ b/proposals/0380-if-switch-expressions.md @@ -3,7 +3,7 @@ * Proposal: [SE-0380](0380-if-switch-expressions.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Hamish Knight](https://github.com/hamishknight) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: [apple/swift#612178](https://github.com/apple/swift/pull/62178), including a downloadable toolchain. * Review: ([pitch](https://forums.swift.org/t/pitch-if-and-switch-expressions/61149)), ([review](https://forums.swift.org/t/se-0380-if-and-switch-expressions/61899)), ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0380-if-and-switch-expressions/62695)) From cf8419c15503dc2384b469c29b64c20bc905ea6c Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 15 Mar 2023 20:31:31 -0700 Subject: [PATCH 3029/4563] Mark SE-0362 as implemented in Swift 5.8 (#1979) --- proposals/0362-piecemeal-future-features.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0362-piecemeal-future-features.md b/proposals/0362-piecemeal-future-features.md index ec7ec95a9a..4fc8408506 100644 --- a/proposals/0362-piecemeal-future-features.md +++ b/proposals/0362-piecemeal-future-features.md @@ -3,7 +3,7 @@ * Proposal: [SE-0362](0362-piecemeal-future-features.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Accepted** +* Status: **Implemented (Swift 5.8)** * Implementation: [apple/swift#59055](https://github.com/apple/swift/pull/59055), [apple/swift-package-manager#5632](https://github.com/apple/swift-package-manager/pull/5632) * Review: ([pitch](https://forums.swift.org/t/piecemeal-adoption-of-swift-6-improvements-in-swift-5-x/57184)) ([review](https://forums.swift.org/t/se-0362-piecemeal-adoption-of-future-language-improvements/58384)) ([acceptance](https://forums.swift.org/t/accepted-se-0362-piecemeal-adoption-of-future-language-improvements/59076)) From 35ca88fa0e378350839478f7e803d715df3aefa8 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 16 Mar 2023 07:13:03 -0700 Subject: [PATCH 3030/4563] Mark SE-0384 as implemented in 5.9. https://github.com/apple/swift/pull/61606 was merged. --- ...-importing-forward-declared-objc-interfaces-and-protocols.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md index eb9506ad98..1f5d64fe4f 100644 --- a/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md +++ b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md @@ -3,7 +3,7 @@ * Proposal: [SE-0384](0384-importing-forward-declared-objc-interfaces-and-protocols.md) * Author: [Nuri Amari](https://github.com/NuriAmari) * Review Manager: Tony Allevato (https://github.com/allevato) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: https://github.com/apple/swift/pull/61606 * Review: ([pitch](https://forums.swift.org/t/pitch-importing-forward-declared-objective-c-classes-and-protocols/61926)) ([review](https://forums.swift.org/t/se-0384-importing-forward-declared-objective-c-interfaces-and-protocols/62392)) ([acceptance](https://forums.swift.org/t/accepted-se-0384-importing-forward-declared-objective-c-interfaces-and-protocols/62670)) From 4033adc36bce73e17d95711891e5aad9c7c59d9a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 16 Mar 2023 10:47:04 -0700 Subject: [PATCH 3031/4563] SE-0385 is returned for revision (#1983) --- proposals/0385-custom-reflection-metadata.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0385-custom-reflection-metadata.md b/proposals/0385-custom-reflection-metadata.md index a87e125fe7..8c7d571cf9 100644 --- a/proposals/0385-custom-reflection-metadata.md +++ b/proposals/0385-custom-reflection-metadata.md @@ -3,7 +3,7 @@ * Proposal: [SE-0385](0385-custom-reflection-metadata.md) * Authors: [Pavel Yaskevich](https://github.com/xedin), [Holly Borla](https://github.com/hborla), [Alejandro Alonso](https://github.com/Azoy), [Stuart Montgomery](https://github.com/stmontgomery) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (January 24, 2023...February 7, 2023)** +* Status: **Returned for revision** ([Rationale](https://forums.swift.org/t/returned-for-revision-se-0385-custom-reflection-metadata/63758)) * Implementation: [PR#1](https://github.com/apple/swift/pull/62426), [PR#2](https://github.com/apple/swift/pull/62738), [PR#3](https://github.com/apple/swift/pull/62818), [PR#4](https://github.com/apple/swift/pull/62850), [PR#5](https://github.com/apple/swift/pull/62920), [PR#6](https://github.com/apple/swift/pull/63057) ## Introduction From 9c53790a13a2a1616e59833230e235cdf767fcf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EC=9E=AC=ED=98=B8?= Date: Fri, 17 Mar 2023 04:39:40 +0900 Subject: [PATCH 3032/4563] [SE-0380] Fix a few typos (#1982) * [SE-0380] Fix a few typos * Update proposals/0380-if-switch-expressions.md Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --------- Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- proposals/0380-if-switch-expressions.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0380-if-switch-expressions.md b/proposals/0380-if-switch-expressions.md index 5f8247dca0..b2958c3d2f 100644 --- a/proposals/0380-if-switch-expressions.md +++ b/proposals/0380-if-switch-expressions.md @@ -110,11 +110,11 @@ For an `if` or `switch` to be used as an expression, it would need to meet these **Each branch of the `if`, or each `case` of the `switch`, must be a single expression.** -Each of these expressions become the value of the overall expression if the branch is chosen. +Each of these expressions becomes the value of the overall expression if the branch is chosen. This does have the downside of requiring fallback to the existing techniques when, for example, a single expression has a log line above it. This is in keeping with the current behavior of `return` omission. -An exception to this rule is if a branch either explicitly throws, or terminates the program (e.g. with `fatalError`), in which case no value for the overall expression need be produced. In these cases, multiple expressions could be executed on that branch prior to that point. +An exception to this rule is if a branch either explicitly throws, or terminates the program (e.g. with `fatalError`), in which case no value for the overall expression needs to be produced. In these cases, multiple expressions could be executed on that branch prior to that point. In the case where a branch throws, either because a call in the expression throws (which requires a `try`) or with an explicit `throw`, there is no requirement to mark the overall expression with an additional `try` (e.g. before the `if`). @@ -383,7 +383,7 @@ var response = switch (utterance) { A similar suggestion was made during [SE-0255: Implicit Returns from Single-Expression Functions](https://forums.swift.org/t/se-0255-implicit-returns-from-single-expression-functions/), where an alternate syntax for single-expression functions was discussed e.g. `func sum() -> Element = reduce(0, +)`. In that case, the core team did not consider introduction of a separate syntax for functions to be sufficiently motivated. -The main benefit to the alternate `->` syntax is to make it more explicit, but comes at the cost of needing to know about two different kinds of switch syntax. Note that this is orthoganal to, and does not solve, the separate goal of providing a way of explicitly "yielding" an expression value in the case of multi-statement branches (also shown here in this java example) versus taking the "last expression" approach. +The main benefit to the alternate `->` syntax is to make it more explicit, but comes at the cost of needing to know about two different kinds of switch syntax. Note that this is orthogonal to, and does not solve, the separate goal of providing a way of explicitly "yielding" an expression value in the case of multi-statement branches (also shown here in this java example) versus taking the "last expression" approach. Java did not introduce this syntax for `if` expressions. Since this is a goal for Swift, this implies: @@ -393,7 +393,7 @@ let x = else -> fatalError() ``` -However, this then poses an issue when evolving to multi-statement branches. Unlike with `switch`, these would require introducing braces, leaving a combination of both braces _and_ a "this is an expresion" sigil: +However, this then poses an issue when evolving to multi-statement branches. Unlike with `switch`, these would require introducing braces, leaving a combination of both braces _and_ a "this is an expression" sigil: ```swift let x = From f76261506d77e8de20d68a4fe3c97062fe5280b1 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Fri, 17 Mar 2023 00:22:45 -0700 Subject: [PATCH 3033/4563] change wording and clarify `~Constraint` --- .../0390-noncopyable-structs-and-enums.md | 68 +++++++++++++------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index 2502a03c80..3d8bb34300 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -82,10 +82,10 @@ types may explicitly require `Copyable`, but this currently has no effect. struct Foo: Copyable {} ``` -### Suppressing assumed generic requirements with `~Constraint` +### Suppressing implicitly derived conformances with `~Constraint` -There are situations where a type is implicitly assumed to satisfy a generic -requirement because of aspects of its declaration. For instance, enums that +There are situations where a type's conformance to a protocol is implicitly +derived because of aspects of its declaration or usage. For instance, enums that don't have any associated values are implicitly made `Hashable` (and, by refinement, `Equatable`): @@ -94,7 +94,8 @@ enum Foo { case a, b, c } -// OK to compare with `==` because `Foo` is automatically Equatable +// OK to compare with `==` because `Foo` is automatically Equatable, +// through an implementation of `==` synthesized by the compiler for you. print(Foo.a == Foo.b) ``` @@ -118,17 +119,17 @@ However, this isn't always desirable; an enum may want to reserve the right to add associated values in the future that aren't `Equatable`, or a type may be made up of `Sendable` components that represent resources that are not safe to share across threads. There is currently no direct way to suppress these -assumed generic requirements. We propose to introduce the `~Constraint` syntax -as a way to explicitly suppress a generic requirement that would be assumed on -a declaration: +automatically derived conformances. We propose to introduce the `~Constraint` +syntax as a way to explicitly suppress automatic derivation of a conformance +that would otherwise be performed for a declaration: ``` -enum Foo: ~Equatable { - case a, b, c +enum Candy: ~Equatable { + case redVimes, twisslers, smickers } -// ERROR: `Foo` does not conform to `Equatable` -print(Foo.a == Foo.b) +// ERROR: `Candy` does not conform to `Equatable` +print(Candy.redVimes == Candy.twisslers) struct ThreadUnsafeHandle: ~Sendable { // although this is an integer, it represents a system resource that @@ -143,9 +144,9 @@ func foo(handle: ThreadUnsafeHandle) async { } ``` -It is important to note that `~Constraint` only removes the assumption of an -implicit requirement, but it does **not** mean that the type strictly does not -conform to the protocol. Extensions may add the conformance back separately, +It is important to note that `~Constraint` only avoids the implicit, automatic +derivation of conformance. It does **not** mean that the type strictly does +not conform to the protocol. Extensions may add the conformance back separately, possibly conditionally: ``` @@ -159,17 +160,19 @@ struct ResourceHandle: ~Sendable { // It is safe to share the handle when the resource type is thread safe extension ResourceHandle: Sendable where T: Sendable {} -// Suppress the default Equatable (and Hashable) implementation... -enum Foo: ~Equatable { - case a, b, c, a2 +// Suppress the implicit Equatable (and Hashable) derivation... +enum Candy: ~Equatable { + case redVimes, twisslers, smickers } -// ...but provide our own Equatable -extension Foo: Equatable { - static func ==(a: Foo, b: Foo) { +// ... and still add an Equatable conformance. +extension Candy: Equatable { + static func ==(a: Candy, b: Candy) -> Bool { switch (a, b) { - // a2 is equal to a - case (.a, .a), (.b, .b), (.c, .c), (.a, .a2), (.a2, .a): + // RedVimes are considered equal to Twisslers + case (.redVimes, .redVimes), (.twisslers, .twisslers), + (.smickers, .smickers), (.twisslers, .redVimes) + (.redVimes, .twisslers): return true default: return false @@ -178,6 +181,27 @@ extension Foo: Equatable { } ``` +Keep in mind that `~Constraint` is not required to suppress Swift's synthesized implementations of protocol requirements. For example, if you only want to +provide your own implementation of `==` for an enum, but are fine with Equatable +(and Hashable, etc) being derived for you, then the derivation of `Equatable` +already will use your version of `==`. + +``` +enum Soda { + case mxPepper, drPibb, doogh + + // This is used instead of a synthesized `==` when + // implicitly deriving the Equatable conformance + static func ==(a: Soda, b: Soda) -> Bool { + switch (a, b) { + case (.doogh, .doogh): return true + case (_, .doogh), (.doogh, _): return false + default: return true + } + } +} +``` + ### Declaring noncopyable types A `struct` or `enum` type can be declared as noncopyable by suppressing the From f80fd3b682aed2c523c91d3a0c7580e35920b985 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Fri, 17 Mar 2023 10:23:25 -0700 Subject: [PATCH 3034/4563] move `~Constraint` to future directions --- .../0390-noncopyable-structs-and-enums.md | 240 +++++++++--------- 1 file changed, 120 insertions(+), 120 deletions(-) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index 3d8bb34300..70d8a0c67a 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -82,126 +82,6 @@ types may explicitly require `Copyable`, but this currently has no effect. struct Foo: Copyable {} ``` -### Suppressing implicitly derived conformances with `~Constraint` - -There are situations where a type's conformance to a protocol is implicitly -derived because of aspects of its declaration or usage. For instance, enums that -don't have any associated values are implicitly made `Hashable` (and, -by refinement, `Equatable`): - -``` -enum Foo { - case a, b, c -} - -// OK to compare with `==` because `Foo` is automatically Equatable, -// through an implementation of `==` synthesized by the compiler for you. -print(Foo.a == Foo.b) -``` - -and internal structs and enums are implicitly `Sendable` if all of their -components are `Sendable`: - -``` -struct Bar { - var x: Int, y: Int -} - -func foo() async { - let x = Bar(x: 17, y: 38) - - // OK to use x in an async task because it's implicitly Sendable - async let y = x -} -``` - -However, this isn't always desirable; an enum may want to reserve the right to -add associated values in the future that aren't `Equatable`, or a type may be -made up of `Sendable` components that represent resources that are not safe -to share across threads. There is currently no direct way to suppress these -automatically derived conformances. We propose to introduce the `~Constraint` -syntax as a way to explicitly suppress automatic derivation of a conformance -that would otherwise be performed for a declaration: - -``` -enum Candy: ~Equatable { - case redVimes, twisslers, smickers -} - -// ERROR: `Candy` does not conform to `Equatable` -print(Candy.redVimes == Candy.twisslers) - -struct ThreadUnsafeHandle: ~Sendable { - // although this is an integer, it represents a system resource that - // can only be accessed from a specific thread, and should not be shared - // across threads - var handle: Int32 -} - -func foo(handle: ThreadUnsafeHandle) async { - // ERROR: `ThreadUnsafeHandle` is not `Sendable` - async let y = handle -} -``` - -It is important to note that `~Constraint` only avoids the implicit, automatic -derivation of conformance. It does **not** mean that the type strictly does -not conform to the protocol. Extensions may add the conformance back separately, -possibly conditionally: - -``` -struct ResourceHandle: ~Sendable { - // although this is an integer, it represents a system resource that - // gives access to values of type `T`, which may not be thread safe - // across threads - var handle: Int32 -} - -// It is safe to share the handle when the resource type is thread safe -extension ResourceHandle: Sendable where T: Sendable {} - -// Suppress the implicit Equatable (and Hashable) derivation... -enum Candy: ~Equatable { - case redVimes, twisslers, smickers -} - -// ... and still add an Equatable conformance. -extension Candy: Equatable { - static func ==(a: Candy, b: Candy) -> Bool { - switch (a, b) { - // RedVimes are considered equal to Twisslers - case (.redVimes, .redVimes), (.twisslers, .twisslers), - (.smickers, .smickers), (.twisslers, .redVimes) - (.redVimes, .twisslers): - return true - default: - return false - } - } -} -``` - -Keep in mind that `~Constraint` is not required to suppress Swift's synthesized implementations of protocol requirements. For example, if you only want to -provide your own implementation of `==` for an enum, but are fine with Equatable -(and Hashable, etc) being derived for you, then the derivation of `Equatable` -already will use your version of `==`. - -``` -enum Soda { - case mxPepper, drPibb, doogh - - // This is used instead of a synthesized `==` when - // implicitly deriving the Equatable conformance - static func ==(a: Soda, b: Soda) -> Bool { - switch (a, b) { - case (.doogh, .doogh): return true - case (_, .doogh), (.doogh, _): return false - default: return true - } - } -} -``` - ### Declaring noncopyable types A `struct` or `enum` type can be declared as noncopyable by suppressing the @@ -1541,6 +1421,126 @@ conditional conformance style declarations: extension Pair: Copyable where T: Copyable, U: Copyable {} ``` +### Suppressing implicitly derived conformances with `~Constraint` + +There are situations where a type's conformance to a protocol is implicitly +derived because of aspects of its declaration or usage. For instance, enums that +don't have any associated values are implicitly made `Hashable` (and, +by refinement, `Equatable`): + +``` +enum Foo { + case a, b, c +} + +// OK to compare with `==` because `Foo` is automatically Equatable, +// through an implementation of `==` synthesized by the compiler for you. +print(Foo.a == Foo.b) +``` + +and internal structs and enums are implicitly `Sendable` if all of their +components are `Sendable`: + +``` +struct Bar { + var x: Int, y: Int +} + +func foo() async { + let x = Bar(x: 17, y: 38) + + // OK to use x in an async task because it's implicitly Sendable + async let y = x +} +``` + +However, this isn't always desirable; an enum may want to reserve the right to +add associated values in the future that aren't `Equatable`, or a type may be +made up of `Sendable` components that represent resources that are not safe +to share across threads. There is currently no direct way to suppress these +automatically derived conformances. We propose to introduce the `~Constraint` +syntax as a way to explicitly suppress automatic derivation of a conformance +that would otherwise be performed for a declaration: + +``` +enum Candy: ~Equatable { + case redVimes, twisslers, smickers +} + +// ERROR: `Candy` does not conform to `Equatable` +print(Candy.redVimes == Candy.twisslers) + +struct ThreadUnsafeHandle: ~Sendable { + // although this is an integer, it represents a system resource that + // can only be accessed from a specific thread, and should not be shared + // across threads + var handle: Int32 +} + +func foo(handle: ThreadUnsafeHandle) async { + // ERROR: `ThreadUnsafeHandle` is not `Sendable` + async let y = handle +} +``` + +It is important to note that `~Constraint` only avoids the implicit, automatic +derivation of conformance. It does **not** mean that the type strictly does +not conform to the protocol. Extensions may add the conformance back separately, +possibly conditionally: + +``` +struct ResourceHandle: ~Sendable { + // although this is an integer, it represents a system resource that + // gives access to values of type `T`, which may not be thread safe + // across threads + var handle: Int32 +} + +// It is safe to share the handle when the resource type is thread safe +extension ResourceHandle: Sendable where T: Sendable {} + +// Suppress the implicit Equatable (and Hashable) derivation... +enum Candy: ~Equatable { + case redVimes, twisslers, smickers +} + +// ... and still add an Equatable conformance. +extension Candy: Equatable { + static func ==(a: Candy, b: Candy) -> Bool { + switch (a, b) { + // RedVimes are considered equal to Twisslers + case (.redVimes, .redVimes), (.twisslers, .twisslers), + (.smickers, .smickers), (.twisslers, .redVimes) + (.redVimes, .twisslers): + return true + default: + return false + } + } +} +``` + +Keep in mind that `~Constraint` is not required to suppress Swift's synthesized implementations of protocol requirements. For example, if you only want to +provide your own implementation of `==` for an enum, but are fine with Equatable +(and Hashable, etc) being derived for you, then the derivation of `Equatable` +already will use your version of `==`. + +``` +enum Soda { + case mxPepper, drPibb, doogh + + // This is used instead of a synthesized `==` when + // implicitly deriving the Equatable conformance + static func ==(a: Soda, b: Soda) -> Bool { + switch (a, b) { + case (.doogh, .doogh): return true + case (_, .doogh), (.doogh, _): return false + default: return true + } + } +} +``` + ### Finer-grained destructuring in `consuming` methods and `deinit` As currently specified, noncopyable types are (outside of `init` implementations) From d15b4d2358763441eb0374168d74616a24752783 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Thu, 16 Mar 2023 22:04:30 -0700 Subject: [PATCH 3035/4563] discuss casting of parameters with ownership --- .../0390-noncopyable-structs-and-enums.md | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index 70d8a0c67a..f8dc425dc4 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -662,6 +662,11 @@ extension FileDescriptor { } ``` +Casts of function types that change the ownership modifier of a noncopyable +parameter are currently invalid. One reason is that it is impossible to cast a +function with a noncopyable `consuming` parameter, into one where that +parameter is `borrowed`. See Future Directions for details. + ### Declaring properties of noncopyable type A class or noncopyable struct may declare stored `let` or `var` properties of @@ -1599,6 +1604,35 @@ We can expose the ability for code to implement these coroutines directly, which is a good optimization for copyable value types, but also allows for more expressivity with noncopyable properties. +### Casting function types based on ownership modifiers +The current rules for casting functions with a noncopyable parameter is that +the ownership modifier must remain the same. + +One reason behind this limitation is a matter of scope. There is a broader need +to support such casts even for copyable types. For example, a function +containing an `inout` parameter cannot currently be cast to or from any other +function type, unless if the parameter remains `inout`. But it should be safe to +allow a cast to change a `borrowing` parameter into one that is `inout`, as it +only adds a capability (mutation) that is not actually used by the underlying +function: +```swift +// This could be possible, but currently is not. +{ (x: borrowing SomeType) in () } as (inout SomeType) -> () +``` +The second reason is that some casts are only valid for copyable types. +In particular, a cast that changes a `consuming` parameter into one that is +`borrowing` is only valid for copyable types, because a copy of the borrowed +value is required to provide a non-borrowed value to the underlying callee. +```swift +// String is copyable, so both are OK and currently permitted. +{ (x: borrowing String) in () } as (consuming String) -> () +{ (x: consuming String) in () } as (borrowing String) -> () +// FileDescriptor is noncopyable, so it cannot go from consuming to borrowing: +{ (x: consuming FileDescriptor) in () } as (borrowing String) -> () +// but the reverse could be permitted in the future: +{ (x: borrowing FileDescriptor) in () } as (consuming String) -> () +``` + ## Revision history This version of the proposal makes the following changes from the From a91b903ae8452a74035c766c2f0fb76133ae6eec Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Thu, 16 Mar 2023 22:39:07 -0700 Subject: [PATCH 3036/4563] fix the `MaybeFileDescriptor` example --- proposals/0390-noncopyable-structs-and-enums.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index f8dc425dc4..eb80eaa4fb 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -288,6 +288,14 @@ the case where the value of interest was taken out of the instance: enum MaybeFileDescriptor: ~Copyable { case some(FileDescriptor) case none + + // Returns this MaybeFileDescriptor by consuming it + // and leaving .none in its place. + mutating func take() -> MaybeFileDescriptor { + let old = self // consume self + self = .none // reinitialize self + return old + } } class WrappedFile { @@ -300,8 +308,7 @@ class WrappedFile { } func consume() throws -> FileDescriptor { - if case let .some(fd) = file { // consume `self.file` - file = .none // must reinitialize `self.file` before returning + if case let .some(fd) = file.take() { return fd } throw Err.noFile From 5297d9f74c493f56dcef6d6dbb55ffa1dcca4dac Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Thu, 16 Mar 2023 22:47:16 -0700 Subject: [PATCH 3037/4563] be specific that `~Copyable` is not ok in extensions --- proposals/0390-noncopyable-structs-and-enums.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index eb80eaa4fb..c7698999f8 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -138,7 +138,7 @@ class SharedFile: ~Copyable { It is also not yet allowed to suppress the `Copyable` requirement on generic parameters, associated type requirements in protocols, or the `Self` type -in a protocol declaration: +in a protocol declaration, or in extensions: ``` // ERROR: generic parameter types must be `Copyable` @@ -149,6 +149,9 @@ protocol Foo where Self: ~Copyable { // ERROR: associated type requirements must be `Copyable` associatedtype Bar: ~Copyable } + +// ERROR: cannot suppress `Copyable` in extension of `FileWithPath` +extension FileWithPath: ~Copyable {} ``` `Copyable` also cannot be suppressed in existential type declarations: From 99394c3cb7de62d4e0981ac1a21651c1b4faae85 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Fri, 17 Mar 2023 11:25:02 -0700 Subject: [PATCH 3038/4563] fix wording around conversion restriction --- .../0390-noncopyable-structs-and-enums.md | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index c7698999f8..c9745911ea 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -672,10 +672,11 @@ extension FileDescriptor { } ``` -Casts of function types that change the ownership modifier of a noncopyable -parameter are currently invalid. One reason is that it is impossible to cast a -function with a noncopyable `consuming` parameter, into one where that -parameter is `borrowed`. See Future Directions for details. +Static casts or coercions of function types that change the ownership modifier +of a noncopyable parameter are currently invalid. One reason is that it is +impossible to convert a function with a noncopyable `consuming` parameter, into +one where that parameter is `borrowed`, without inducing a copy of the borrowed +parameter. See Future Directions for details. ### Declaring properties of noncopyable type @@ -1614,25 +1615,26 @@ We can expose the ability for code to implement these coroutines directly, which is a good optimization for copyable value types, but also allows for more expressivity with noncopyable properties. -### Casting function types based on ownership modifiers -The current rules for casting functions with a noncopyable parameter is that -the ownership modifier must remain the same. - -One reason behind this limitation is a matter of scope. There is a broader need -to support such casts even for copyable types. For example, a function -containing an `inout` parameter cannot currently be cast to or from any other -function type, unless if the parameter remains `inout`. But it should be safe to -allow a cast to change a `borrowing` parameter into one that is `inout`, as it -only adds a capability (mutation) that is not actually used by the underlying -function: +### Static casts of functions with ownership modifiers +The rule for casting function values via `as` or some other static, implicit +coercion is that a noncopyable parameter's ownership modifier must remain the +same. But there are some cases where static conversions of functions +with noncopyable parameters are safe. It's not safe in general to do any dynamic +casts of function values, so `as?` and `as!` are excluded. + +One reason behind the currently restrictive rule for static casts is a matter of +scope for this proposal. There may be a broader demand to support such casts +even for copyable types. For example, it should be safe to allow a cast to +change a `borrowing` parameter into one that is `inout`, as it only adds a +capability (mutation) that is not actually used by the underlying function: ```swift // This could be possible, but currently is not. { (x: borrowing SomeType) in () } as (inout SomeType) -> () ``` -The second reason is that some casts are only valid for copyable types. +The second reason is that some casts are _only_ valid for copyable types. In particular, a cast that changes a `consuming` parameter into one that is `borrowing` is only valid for copyable types, because a copy of the borrowed -value is required to provide a non-borrowed value to the underlying callee. +value is required to provide a non-borrowed value to the underlying function. ```swift // String is copyable, so both are OK and currently permitted. { (x: borrowing String) in () } as (consuming String) -> () From be7cd61563dfc9a6f60ebde487b4d58bfd216268 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Mon, 20 Mar 2023 17:27:42 -0700 Subject: [PATCH 3039/4563] Amend SE-0391 (#1977) - Remove in-archive option for submitting package release metadata. The existing method, i.e. including metadata as part of the request body, will continue to be supported. - By default SwiftPM will look for a file named `package-metadata.json` inside the package directory being published for package release metadata. The file will be included in the archive, AND its contents will be included as a separate section in the publish request body. - If a source archive is being signed, then any submitted metadata will be signed also. Introduce a new part in the publish request body named `metadata-signature` to hold metadata signature. - Remove `sign` subcommand because with `--dry-run` the `publish` subcommand can do essentially the same. - Change `publish` subcommand to take cert chain (`cert-chain-paths`) rather than just the signing cert. In some cases the user needs to provide the cert chain such that the intermediate CA(s) get included in the signature and can be used for signature validation later. - Add section for changes to "download source archive" API--the response needs to include signature related reponse headers if source archive is signed. --- proposals/0391-package-registry-publish.md | 150 +++++++++++---------- 1 file changed, 77 insertions(+), 73 deletions(-) diff --git a/proposals/0391-package-registry-publish.md b/proposals/0391-package-registry-publish.md index 7649eabee1..914bc548fe 100644 --- a/proposals/0391-package-registry-publish.md +++ b/proposals/0391-package-registry-publish.md @@ -8,8 +8,14 @@ * https://github.com/apple/swift-package-manager/pull/6101 * https://github.com/apple/swift-package-manager/pull/6146 * https://github.com/apple/swift-package-manager/pull/6159 - * https://github.com/apple/swift-package-manager/pull/6184 + * https://github.com/apple/swift-package-manager/pull/6169 + * https://github.com/apple/swift-package-manager/pull/6188 * https://github.com/apple/swift-package-manager/pull/6189 + * https://github.com/apple/swift-package-manager/pull/6215 + * https://github.com/apple/swift-package-manager/pull/6217 + * https://github.com/apple/swift-package-manager/pull/6220 + * https://github.com/apple/swift-package-manager/pull/6229 + * https://github.com/apple/swift-package-manager/pull/6237 * Review: * pitch: https://forums.swift.org/t/pitch-package-registry-publish/62828 * review: https://forums.swift.org/t/se-0391-package-registry-publish/63405 @@ -30,9 +36,9 @@ well-rounded experience for using package registries. Publishing package release to a Swift package registry generally involves these steps: 1. Gather package release metadata. 1. Prepare package source archive by using the [`swift package archive-source` subcommand](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md#archive-source-subcommand). - 1. Sign the archive (if needed). + 1. Sign the metadata and archive (if needed). 1. [Authenticate](https://github.com/apple/swift-evolution/blob/main/proposals/0378-package-registry-auth.md) (if required by the registry). - 1. Send the archive (and signature if any) and metadata by calling the ["create a package release" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-6). + 1. Send the archive and metadata (and their signatures if any) by calling the ["create a package release" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-6). 1. Check registry server response to determine if publication has succeeded or failed (if the registry processes request synchronously), or is pending (if the registry processes request asynchronously). SwiftPM can streamline the workflow by combining all of these steps into a single @@ -59,11 +65,11 @@ The current [registry service specification](https://github.com/apple/swift-pack It does not, however, define any requirements or server-client API contract on the metadata contents. We would like to change that by proposing the following: - Package release metadata will continue to be sent as a JSON object. - - Package release metadata must be sent as part of the "create a package release" request and adhere to the [schema](#package-release-metadata-standards). - - Package release metadata may be included in the "create a package release" request in one of these ways, depending on registry server support: - + A multipart section named `metadata` in the request body. - + A file named `package-metadata.json` **inside** the source archive being published. - - Registry server may allow and/or populate additional metadata by expanding the schema, but it must not alter any of the predefined properties. + - Package release metadata must adhere to the [schema](#package-release-metadata-standards). + - Package release metadata will continue to be included in the "create a package release" request as a multipart section named `metadata` in the request body. + - Registry server may allow and/or populate additional metadata by expanding the schema, but it must not alter any of the predefined properties. + - Registry server may make any properties in the schema and additional metadata it defines required. Registry server may fail the "create a package release" request if any required metadata is missing. + - Client cannot change how registry server handles package release metadata. In other words, client will no longer be able to instruct registry server not to populate metadata by sending an empty JSON object `{}`. - Registry server will continue to include metadata in the "fetch information about a package release" response. #### Package release metadata standards @@ -161,7 +167,7 @@ Package release metadata submitted to a registry must be a JSON object of type | `description` | String | A description of the package release. | | | `licenseURL` | String | URL of the package release's license document. | | | `readmeURL` | String | URL of the README specifically for the package release or broadly for the package. | | -| `repositoryURLs` | Array | Code repository URL(s) of the package. It is recommended to include all URL variations (e.g., SSH, HTTPS) for the same repository. This can be an empty array if the package does not have source control representation. The registry must ensure that these URLs are searchable using the ["lookup package identifiers registered for a URL" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#45-lookup-package-identifiers-registered-for-a-url). | ✓ | +| `repositoryURLs` | Array | Code repository URL(s) of the package. It is recommended to include all URL variations (e.g., SSH, HTTPS) for the same repository. This can be an empty array if the package does not have source control representation.
    Setting this property is one way through which a registry can obtain repository URL to package identifier mappings for the ["lookup package identifiers registered for a URL" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#45-lookup-package-identifiers-registered-for-a-url). A registry may choose other mechanism(s) for package authors to specify such mappings. | | ##### `Author` type @@ -232,39 +238,6 @@ After downloading a signed package SwiftPM will: - Validate that the signed package complies with the locally-configured signing policy. - Extract public key from the certificate and use it to verify the signature. -#### New `package sign` subcommand - -There will be a new subcommand `package sign` dedicated to package signing. - -```manpage -> swift package sign --help -OVERVIEW: Sign a package archive - -USAGE: package sign - -ARGUMENTS: - The path to the package source archive to be signed. - The path the output signature file will be written to. - -OPTIONS: - --signing-identity The label of the signing identity to be retrieved from the system's secrets store if supported. - - --private-key-path The path to the certificate's PKCS#8 private key (DER-encoded). - --cert-path Path to the signing certificate (DER-encoded). -``` - -A signing identity encompasses a private key and a certificate. On -systems where it is supported, SwiftPM can look for a signing identity -using the query string given via the `--signing-identity` option. This -feature will be available on macOS through Keychain in the initial -release, so a certificate and its private key can be located by the -certificate label alone. - -Otherwise, both `--private-key-path` and `--cert-path` must be -provided to locate the signing key and certificate. - -All signatures in the initial release will be in the [`cms-1.0.0`](#package-signature-format-cms-100) format. - #### Server-side requirements for package signing A registry that requires package signing should provide documentations @@ -272,8 +245,8 @@ on the signing requirements (e.g., any requirements for certificates used in signing). A registry must also modify the ["create package release" API](#create-package-release-api) to allow -signature in the request, as well as the response for the ["fetch package release metadata" API](#fetch-package-release-metadata-api) -to include signature information. +signature in the request, as well as the response for the ["fetch package release metadata"](#fetch-package-release-metadata-api) +and ["download package source archive"](#download-package-source-archive-api) API to include signature information. #### SwiftPM's handling of registry packages @@ -421,19 +394,21 @@ OVERVIEW: Publish a package release to registry USAGE: package-registry publish ARGUMENTS: - The package identifier. - The package release version being created. + The package identifier. + The package release version being created. OPTIONS: --url The registry URL. --scratch-directory The path of the directory where working file(s) will be written. - --metadata-path The path to the package metadata JSON file if it will not be part of the source archive. + --metadata-path The path to the package metadata JSON file if it's not 'package-metadata.json' in the package directory. --signing-identity The label of the signing identity to be retrieved from the system's secrets store if supported. --private-key-path The path to the certificate's PKCS#8 private key (DER-encoded). - --cert-path Path to the signing certificate (DER-encoded). + --cert-chain-paths Path(s) to the signing certificate (DER-encoded) and optionally the rest of the certificate chain. The signing certificate must be listed first. + + --dry-run Dry run only; prepare the archive and sign it but do not publish to the registry. ``` - `id`: The package identifier in the `.` notation as defined in [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md#package-identity). It is the package author's responsibility to register the package identifier with the registry beforehand. @@ -442,19 +417,29 @@ OPTIONS: - `scratch-directory`: The path of the working directory. SwiftPM will write to the package directory by default. The following may be required depending on registry support and/or requirements: - - `metadata-path`: The path to the JSON file containing [package release metadata](#package-release-metadata). This parameter should be set if the registry expects metadata to be sent as part of the request. SwiftPM will always include the content of this file in the request body if given. Otherwise, if the registry expects `package-metadata.json` in the source archive, it is the package author's responsibility to make sure the file is present in the package directory so that it gets included in the source archive. If metadata is included in both the request body and the source archive, then it is at the registry's discretion to choose which to use, although having `package-metadata.json` in the archive is [recommended](#package-release-metadata-signing). - - `signing-identity`: The label that identifies the signing identity to use for package signing in the system's secrets store if supported. See also the [`package sign` subcommand](#new-package-sign-subcommand) for details. + - `metadata-path`: The path to the JSON file containing [package release metadata](#package-release-metadata). By default, SwiftPM will look for a file named `package-metadata.json` in the package directory if this is not specified. SwiftPM will include the content of the metadata file in the request body if present. If the package source archive is being signed, the metadata will be signed as well. + - `signing-identity`: The label that identifies the signing identity to use for package signing in the system's secrets store if supported. - `private-key-path`: Required for package signing unless `signing-identity` is specified, this is the path to the private key used for signing. - - `cert-path`: Required for package signing unless `signing-identity` is specified, this is the signing certificate. + - `cert-chain-paths`: Required for package signing unless `signing-identity` is specified, this is the signing certificate chain. -SwiftPM will sign the package source archive if `signing-identity` -or both `private-key-path` and `cert-path` are set. +A signing identity encompasses a private key and a certificate. On +systems where it is supported, SwiftPM can look for a signing identity +using the query string given via the `--signing-identity` option. This +feature will be available on macOS through Keychain in the initial +release, so a certificate and its private key can be located by the +certificate label alone. + +Otherwise, both `--private-key-path` and `--cert-chain-paths` must be +provided to locate the signing key and certificate chain. + +SwiftPM will sign the package source archive and package release metadata if `signing-identity` +or both `private-key-path` and `cert-chain-paths` are set. All signatures in the initial release will be in the [`cms-1.0.0`](#package-signature-format-cms-100) format. Using these inputs, SwiftPM will: - Generate source archive for the package release. - - Sign the source archive if the required parameters are provided. + - Sign the source archive and metadata if the required parameters are provided. - Make HTTP request to the "create a package release" API. - Check server response for any errors. @@ -467,20 +452,14 @@ Prerequisites: #### Create package release API A registry must update [this existing endpoint](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-6) to handle package release -metadata as described in a [previous section](#package-release-metadata) of this document. In particular, - - Metadata is now required. - - Client must include metadata in the request. - - Empty metadata is not allowed. - - Metadata may be submitted in two ways: - - In the request body--this method should already be supported by the registry per the [current API specification](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#46-create-a-package-release). - - In the source archive--this is new and maybe optionally implemented by the registry. - - Values provided with the `repositoryURLs` JSON key must be searchable. +metadata as described in a [previous section](#package-release-metadata) of this document. If the package being published is signed, the client must identify the signature format in the `X-Swift-Package-Signature-Format` HTTP request header so that the server can process the signature accordingly. -The signature is sent as part of the request body: +Signatures of the source-archive and metadata are sent as part of the request body +(`source-archive-signature` and `metadata-signature`, respectively): ``` PUT /mona/LinkedList?version=1.1.1 HTTP/1.1 @@ -506,6 +485,22 @@ Content-Length: 88 Content-Transfer-Encoding: base64 l1TdTeIuGdNsO1FQ0ptD64F5nSSOsQ5WzhM6/7KsHRuLHfTsggnyIWr0DxMcBj5F40zfplwntXAgS0ynlqvlFw== + +--boundary +Content-Disposition: form-data; name="metadata" +Content-Type: application/json +Content-Transfer-Encoding: quoted-printable +Content-Length: 25 + +{ "repositoryURLs": [] } + +--boundary +Content-Disposition: form-data; name="metadata-signature" +Content-Type: application/octet-stream +Content-Length: 88 +Content-Transfer-Encoding: base64 + +M6TdTeIuGdNsO1FQ0ptD64F5nSSOsQ5WzhM6/7KsHRuLHfTsggnyIWr0DxMcBj5F40zfplwntXAgS0ynlqvlFw== ``` #### Fetch package release metadata API @@ -535,8 +530,25 @@ object in the response: } ``` -A client can use the API response to determine if a package is signed and -handle it accordingly. +#### Download package source archive API + +If a registry supports signing, it must update [this existing endpoint](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-4) +to include the `X-Swift-Package-Signature-Format` and `X-Swift-Package-Signature` headers in +the HTTP response for a signed package source archive. + +``` +HTTP/1.1 200 OK +Accept-Ranges: bytes +Cache-Control: public, immutable +Content-Type: application/zip +Content-Disposition: attachment; filename="LinkedList-1.1.1.zip" +Content-Length: 2048 +Content-Version: 1 +Digest: sha-256=oqxUzyX7wa0AKPA/CqS5aDO4O7BaFOUQiSuyfepNyBI= +Link: ; rel=duplicate; geo=jp; pri=10; type="application/zip" +X-Swift-Package-Signature-Format: cms-1.0.0 +X-Swift-Package-Signature: l1TdTeIuGdNsO1FQ0ptD64F5nSSOsQ5WzhM6/7KsHRuLHfTsggnyIWr0DxMcBj5F40zfplwntXAgS0ynlqvlFw== +``` ## Security @@ -568,14 +580,6 @@ protection against a compromised registry, but it is not for all packages and the registry to provide authentic packages and accurate information about the signature status of the package. -### Package release metadata signing - -Package release metadata submitted as `package-metadata.json` in a [signed package](#package-signing) -is considered signed and not modifiable. Otherwise, the registry server may override the -metadata and/or allow it to be edited afterwards. It is recommended that package authors -use `package-metadata.json` to submit metadata if this method and package signing are -supported by the registry. - ### Privacy implications of certificate revocation check Revocation checking via OCSP implicitly discloses to the certificate From 3fc8bfc0acd5f1ee32b9a21741a59a2ca8eb7f66 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 21 Mar 2023 08:38:07 -0700 Subject: [PATCH 3040/4563] Narrow the capability of `discard self` for this proposal. (#1986) Only allow it for types with bitwise-copyable contents for now, and discuss the design questions for generalizing it as a future direction. --- .../0390-noncopyable-structs-and-enums.md | 134 ++++++++++++++---- 1 file changed, 106 insertions(+), 28 deletions(-) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index c9745911ea..65e9b7cd25 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -1003,7 +1003,7 @@ circumstances: ### Suppressing `deinit` in a `consuming` method It is often useful for noncopyable types to provide alternative ways to consume -the resource represented by the value besides the `deinit`. However, +the resource represented by the value besides `deinit`. However, under normal circumstances, a `consuming` method will still invoke the type's `deinit` after the last use of `self`, which is undesirable when the method's own logic already invalidates the value: @@ -1027,8 +1027,8 @@ struct FileDescriptor: ~Copyable { In the above example, the double-close could be avoided by having the `close()` method do nothing on its own and just allow the `deinit` to implicitly run. However, we may want the method to have different behavior -from the deinit, such as raising an error (which a normal `deinit` is unable to -do) if the `close` system call triggers an OS error : +from the deinit; for example, it could raise an error (which a normal `deinit` +is unable to do) if the `close` system call triggers an OS error : ```swift struct FileDescriptor: ~Copyable { @@ -1062,7 +1062,7 @@ struct FileDescriptor: ~Copyable { } ``` -We propose to introduce a special operator, `suppressdeinit self`, which ends the +We propose to introduce a special operator, `discard self`, which ends the lifetime of `self` without running its `deinit`: ```swift @@ -1071,13 +1071,13 @@ struct FileDescriptor: ~Copyable { // returning the file descriptor without closing it consuming func take() -> Int32 { let fd = self.fd - suppressdeinit self + discard self return fd } } ``` -`suppressdeinit self` can only be applied to `self`, in a consuming method +`discard self` can only be applied to `self`, in a consuming method defined in the same file as the type's original definition. (This is in contrast to Rust's similar special function, [`mem::forget`](https://doc.rust-lang.org/std/mem/fn.forget.html), which is a @@ -1091,7 +1091,15 @@ we think it is safer to restrict the ability to suppress the standard `deinit` for a value to the core API of its type. We can relax this restriction if experience shows a need to.) -Even with the ability to `suppressdeinit self`, care would still need be taken when +For the extent of this proposal, we also propose that `discard self` can only +be applied in types whose components include no reference-counted, generic, +or existential fields, nor do they include any types that transitively include +any fields of those types or that have `deinit`s defined of their own. (Such +a type might be called "POD" or "trivial" following C++ terminology). We explore +lifting this restriction as a future direction. + + +Even with the ability to `discard self`, care would still need be taken when writing destructive operations to avoid triggering the deinit on alternative exit paths, such as early `return`s, `throw`s, or implicit propagation of errors from `try` operations. For instance, if we write: @@ -1109,17 +1117,17 @@ struct FileDescriptor: ~Copyable { } // We don't need to deinit self anymore - suppressdeinit self + discard self } } ``` -then the `throw` path exits the method without `suppressdeinit`, and +then the `throw` path exits the method without `discard`, and `deinit` will still execute if an error occurs. To avoid this mistake, we -propose that if any path through a method uses `suppressdeinit self`, then -**every** path must choose either to `suppressdeinit` or to explicitly `consume self`, +propose that if any path through a method uses `discard self`, then +**every** path must choose either to `discard` or to explicitly `consume self`, which triggers the standard `deinit`. This will make the above code an error, -alerting that the code should be rewritten to ensure `suppressdeinit self` +alerting that the code should be rewritten to ensure `discard self` always executes: ```swift @@ -1129,7 +1137,7 @@ struct FileDescriptor: ~Copyable { consuming func close() throws { // Save the file descriptor and give up ownership of it let fd = self.fd - suppressdeinit self + discard self // We can now use `fd` below without worrying about `deinit`: @@ -1144,7 +1152,7 @@ struct FileDescriptor: ~Copyable { The [consume operator](https://github.com/apple/swift-evolution/blob/main/proposals/0377-parameter-ownership-modifiers.md) must be used to explicitly end the value's lifetime using its `deinit` if -`suppressdeinit` is used to conditionally destroy the value on other paths +`discard` is used to conditionally destroy the value on other paths through the method. ``` @@ -1167,7 +1175,7 @@ struct MemoryBuffer: ~Copyable { // Save the memory buffer and give it to the caller, who // is promising to free it when they're done. let address = self.address - suppressdeinit self + discard self return address } else { // We still want to free the memory if we aren't giving it away. @@ -1561,14 +1569,14 @@ enum Soda { As currently specified, noncopyable types are (outside of `init` implementations) always either fully initialized or fully destroyed, without any support -for incremental destruction inside of `consuming` methods or deinits. A +for incremental destruction even inside of `consuming` methods or deinits. A `deinit` may modify, but not invalidate, `self`, and a `consuming` method may -`suppressdeinit self`, forward ownership of all of `self`, or destroy `self`, but cannot -yet partially consume parts of `self`. This would be particularly useful for -types that contain other noncopyable types, which may want to relinquish -ownership of some or all of the resources owned by those members. In the current -proposal, this isn't possible without allowing for an intermediate invalid -state: +`discard self`, forward ownership of all of `self`, or destroy `self`, but +cannot yet partially consume parts of `self`. This would be particularly useful +for types that contain other noncopyable types, which may want to relinquish +ownership of some or all of the resources owned by those members. In the +current proposal, this isn't possible without allowing for an intermediate +invalid state: ```swift struct SocketPair: ~Copyable { @@ -1579,18 +1587,88 @@ struct SocketPair: ~Copyable { // We would like to do something like this, taking ownership of // `self.output` while leaving `self.input` to be destroyed. // However, we can't do this without being able to either copy - // `self.output` or partially invalidate `self` - let output = self.output - suppressdeinit self - return output + // `self.output` or partially invalidate `self`. + return self.output } } ``` Analogously to how `init` implementations use a "definite initialization" pass to allow the value to initialized field-by-field, we can implement the -inverse dataflow pass to allow `deinit` implementations, as well as `consuming` -methods that `suppressdeinit self`, to partially invalidate `self`. +inverse dataflow pass to allow `deinit` implementations to partially +invalidate `self`. This analysis would also enable `consuming` methods to +partially invalidate `self` in cases where either the type has no `deinit` or, +as discussed in the following section, `discard self` is used to disable the +`deinit` in cases when the value is partially invalidated. + +### Generalizing `discard self` for types with component cleanups + +The current proposal limits the use of `discard self` to types that don't have +any fields that require additional cleanup, meaning that it cannot be used in +a type that has class, generic, existential, or other noncopyable type fields. +Allowing this would be an obvious generalization; however, allowing it requires +answering some design questions: + +- When `self` is discarded, are its fields still destroyed? +- Is access to `self`'s fields still allowed after `discard self`? In other + words, does `discard self` immediately consume all of `self`, running + the cleanups for its elements at the point where the `discard` is executed, + or does it only disable the `deinit` on `self`, allowing the fields to + still be individually borrowed, mutated, and/or consumed, and leaving them + to be cleaned up when their individual lifetimes end? + +Although Rust's `mem::forget` completely leaks its operand, including its fields, +the authors of this proposal generally believe that is undesirable, so we expect +that `discard self` should only disable the type's own `deinit` while still +leaving the components of `self` to be cleaned up. + +The choice of what effect `discard` has on the lifetime of the fields affects +the observed order in which field deinits occurs, but also affects how code +would be expressed that performs destructuring or partial invalidation: + +``` +struct SocketPair: ~Copyable { + let input, output: FileDescriptor + + deinit { ... } + + enum End { case input, output } + + // Give up ownership of one end and closes the other end + consuming func takeOneEnd(which: End) -> FileDescriptor { + // If a consuming method could partially invalidate self, would it do it + // like this... +#if discard_immediately_consumes_whats_left_of_self + switch which { + case .input: + // Move out the field we want + let result = self.input + // Destroy the rest of self + discard self + return result + + case .output: + let result = self.output + discard self + return result + } + + // ...or like this +#elseif discard_only_disables_deinit + // Disable deinit on self, which subsequently allows individual consumption + // of its fields + discard self + + switch which { + case .input: + return self.input + case .output: + return self.output + } +#endif + } +} +``` ### `read` and `modify` accessor coroutines for computed properties From 998c2343ae6ae39dc410d2c553f44d4243195251 Mon Sep 17 00:00:00 2001 From: Boris Buegling Date: Tue, 21 Mar 2023 11:18:59 -0700 Subject: [PATCH 3041/4563] Update the proposal to reflect use of SwiftSyntax as a versioned dependency --- proposals/NNNN-swiftpm-expression-macros.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/proposals/NNNN-swiftpm-expression-macros.md b/proposals/NNNN-swiftpm-expression-macros.md index e62f603a8d..4e599ff44a 100644 --- a/proposals/NNNN-swiftpm-expression-macros.md +++ b/proposals/NNNN-swiftpm-expression-macros.md @@ -48,8 +48,15 @@ import CompilerPluginSupport let package = Package( name: "MacroPackage", + dependencies: [ + .package(url: "https://github.com/apple/swift-syntax", from: "509.0.0"), + ], targets: [ - .macro(name: "MacroImpl"), + .macro(name: "MacroImpl", + dependencies: [ + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + .product(name: "SwiftCompilerPlugin", package: "swift-syntax") + ]), .target(name: "MacroDef", dependencies: ["MacroImpl"]), .executableTarget(name: "MacroClient", dependencies: ["MacroDef"]), ] @@ -60,7 +67,7 @@ Macro implementations will be executed in a sandbox [similar to package plugins] ## Detailed Design -SwiftPM builds each macro as an executable for the host platform, applying certain additional compiler flags such as search paths to the SwiftSyntax library supplied by the toolchain. Each target that transitively depends on a macro will have access to it, concretely this happens by SwiftPM passing `-load-plugin-executable` to the compiler to specify which executable contains the implementation of a certain macro module. The macro defintion refers to the module and concrete type via an `#externalMacro` declaration which allows any dependency of the defining target to have access to the concrete macro. If any target of a library product depends on a macro, clients of said library will also get access to any public macros. Macros can have dependencies like any other target, but product dependencies of macros need to be statically linked, so explicitly dynamic library products cannot be used by a macro target. +SwiftPM builds each macro as an executable for the host platform, applying certain additional compiler flags. Macros are expected to depend on SwiftSyntax using a versioned dependency that corresponds to a particular major Swift release. Each target that transitively depends on a macro will have access to it, concretely this happens by SwiftPM passing `-load-plugin-executable` to the compiler to specify which executable contains the implementation of a certain macro module. The macro defintion refers to the module and concrete type via an `#externalMacro` declaration which allows any dependency of the defining target to have access to the concrete macro. If any target of a library product depends on a macro, clients of said library will also get access to any public macros. Macros can have dependencies like any other target, but product dependencies of macros need to be statically linked, so explicitly dynamic library products cannot be used by a macro target. Concretely, the code for the macro package shown earlier would contain a macro implementation looking like this: @@ -156,10 +163,6 @@ The original pitch of expression macros considered declaring macros by introduci ## Future Directions -### Evolution of `SwiftSyntaxMacros` - -Macro targets will get access to a version of `SwiftSyntaxMacros` and associated libraries which ships with the Swift toolchain. This means package authors aiming to support multiple Swift compiler versions may have to resort to conditional compilation based on the Swift compiler version to handle the evolving API of SwiftSyntax. It also means clients of packages which are utilizing macros may need to wait for their dependencies to adopt to new APIs in order to upgrade to a new version of the Swift tools. We expect these limitations to eventually be solved by stabilizing the relevant SwiftSyntax APIs at which point macros could depend on SwiftSyntax via a package dependency on the source repository. - ### Generalized support for additional manifest API The macro target type is provided by a new library `CompilerPluginSupport` as a starting point for making package manifests themselves more extensible. Support for product and target type plugins should eventually be generalized to allow other types of externally defined specialized target types, such as, for example, a Windows application. From b12d50410d295a82647b235e3735207e709c4a34 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 21 Mar 2023 18:28:00 +0000 Subject: [PATCH 3042/4563] Mark SE-0388 (AsyncStream.makeStream) implemented in 5.9 (#1988) The corresponding pull request was merged before 5.9 was branched off. --- proposals/0388-async-stream-factory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0388-async-stream-factory.md b/proposals/0388-async-stream-factory.md index ccc539495e..d249793ad4 100644 --- a/proposals/0388-async-stream-factory.md +++ b/proposals/0388-async-stream-factory.md @@ -3,7 +3,7 @@ * Proposal: [SE-0388](0388-async-stream-factory.md) * Authors: [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: [apple/swift#62968](https://github.com/apple/swift/pull/62968) * Review: ([pitch](https://forums.swift.org/t/pitch-convenience-async-throwing-stream-makestream-methods/61030)) ([review](https://forums.swift.org/t/se-0388-convenience-async-throwing-stream-makestream-methods/63139)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0388-convenience-async-throwing-stream-makestream-methods/63568)) From ef26a792e82d31c57944c086ce6e9bac59601da5 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 21 Mar 2023 14:10:08 -0700 Subject: [PATCH 3043/4563] Value and type parameter packs. (#1956) * Add a draft proposal for parameter packs. * Fix the type of postfix ... operator in the ambiguity example. * update pitch (#1) * Add missing SE proposal information. * Replace the '...' syntax with 'repeat each'. * Add more introductory explanation to the proposed solution. * Update proposals/NNNN-parameter-packs.md Co-authored-by: Remy Demarest * Address editorial feedback * Add pack iteration and pack element projection to the future directions. * Move local value packs and add explicit pack syntax to future directions. * Describe single-element tuple unwrapping under pack substitution. * Simplify the description of same-type requirements involving parameter packs. * Update proposals/NNNN-parameter-packs.md Co-authored-by: Becca Royal-Gordon * Move the introduction above the table of contents. * Specify trailing closure matching rules for parameter packs. * Replace the term "type sequence" with "type list" to avoid confusion with the Sequence protocol. * Add more commentary on the `repeat each` syntax design. * Minor editorial changes * Describe how variadic generic functions interact with overload resolution. * Update pitch links. * Update 0393-parameter-packs.md --------- Co-authored-by: Slava Pestov Co-authored-by: Remy Demarest Co-authored-by: Becca Royal-Gordon Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- proposals/0393-parameter-packs.md | 837 ++++++++++++++++++++++++++++++ 1 file changed, 837 insertions(+) create mode 100644 proposals/0393-parameter-packs.md diff --git a/proposals/0393-parameter-packs.md b/proposals/0393-parameter-packs.md new file mode 100644 index 0000000000..8e7f11d21d --- /dev/null +++ b/proposals/0393-parameter-packs.md @@ -0,0 +1,837 @@ +# SE-0393: Value and Type Parameter Packs + +* Proposal: [SE-0393](0393-parameter-packs.md) +* Authors: [Holly Borla](https://github.com/hborla), [John McCall](https://github.com/rjmccall), [Slava Pestov](https://github.com/slavapestov) +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Active review (March 21...April 3, 2023)** +* Implementation: On `main` gated behind the frontend flag `-enable-experimental-feature VariadicGenerics` +* Review: ([pitch 1](https://forums.swift.org/t/pitch-parameter-packs/60543)) ([pitch 2](https://forums.swift.org/t/pitch-2-value-and-type-parameter-packs/60830)) + +## Introduction + +Many modern Swift libraries include ad-hoc variadic APIs with an arbitrary upper bound, typically achieved with overloads that each have a different fixed number of type parameters and corresponding arguments. Without variadic generic programming support in the language, these ad-hoc variadic APIs have a significant cost on library maintenance and the developer experience of using these APIs. + +This proposal adds _type parameter packs_ and _value parameter packs_ to enable abstracting over the number of types and values with distinct type. This is the first step toward variadic generics in Swift. + +## Contents + +- [Value and Type Parameter Packs](#value-and-type-parameter-packs) + - [Introduction](#introduction) + - [Contents](#contents) + - [Motivation](#motivation) + - [Proposed solution](#proposed-solution) + - [Detailed design](#detailed-design) + - [Type parameter packs](#type-parameter-packs) + - [Pack expansion type](#pack-expansion-type) + - [Type substitution](#type-substitution) + - [Single-element pack substitution](#single-element-pack-substitution) + - [Type matching](#type-matching) + - [Label matching](#label-matching) + - [Trailing closure matching](#trailing-closure-matching) + - [Type list matching](#type-list-matching) + - [Member type parameter packs](#member-type-parameter-packs) + - [Generic requirements](#generic-requirements) + - [Same-shape requirements](#same-shape-requirements) + - [Restrictions on same-shape requirements](#restrictions-on-same-shape-requirements) + - [Value parameter packs](#value-parameter-packs) + - [Overload resolution](#overload-resolution) + - [Effect on ABI stability](#effect-on-abi-stability) + - [Alternatives considered](#alternatives-considered) + - [Modeling packs as tuples with abstract elements](#modeling-packs-as-tuples-with-abstract-elements) + - [Syntax alternatives to `repeat each`](#syntax-alternatives-to-repeat-each) + - [The `...` operator](#the--operator) + - [Another operator](#another-operator) + - [Magic builtin `map` method](#magic-builtin-map-method) + - [Future directions](#future-directions) + - [Variadic generic types](#variadic-generic-types) + - [Local value packs](#local-value-packs) + - [Explicit type pack syntax](#explicit-type-pack-syntax) + - [Pack iteration](#pack-iteration) + - [Pack element projection](#pack-element-projection) + - [Dynamic pack indexing with `Int`](#dynamic-pack-indexing-with-int) + - [Typed pack element projection using key-paths](#typed-pack-element-projection-using-key-paths) + - [Value expansion operator](#value-expansion-operator) + - [Pack destructuring operations](#pack-destructuring-operations) + - [Tuple conformances](#tuple-conformances) + - [Acknowledgments](#acknowledgments) + +## Motivation + +Generic functions currently require a fixed number of type parameters. It is not possible to write a generic function that accepts an arbitrary number of arguments with distinct types, instead requiring one of the following workarounds: + +* Erasing all of the types involved, e.g. using `Any...` +* Using a single tuple type argument instead of separate type arguments +* Overloading for each argument length with an artificial limit + +One example in the Swift Standard Library is the 6 overloads for each tuple comparison operator: + +```swift +func < (lhs: (), rhs: ()) -> Bool + +func < (lhs: (A, B), rhs: (A, B)) -> Bool where A: Comparable, B: Comparable + +func < (lhs: (A, B, C), rhs: (A, B, C)) -> Bool where A: Comparable, B: Comparable, C: Comparable + +// and so on, up to 6-element tuples +``` + +With language support for a variable number of type parameters, this API could be expressed more naturally and concisely as a single function declaration: + +```swift +func < (lhs: (repeat each Element), rhs: (repeat each Element)) -> Bool +``` + +## Proposed solution + +This proposal adds support for generic functions which abstract over a variable number of type parameters. While this proposal is useful on its own, there are many future directions that build upon this concept. This is the first step toward equipping Swift programmers with a set of tools that enable variadic generic programming. + +Parameter packs are the core concept that facilitates abstracting over a variable number of parameters. A pack is a new kind of type-level and value-level entity that represents a list of types or values, and it has an abstract length. A type parameter pack stores a list of zero or more type parameters, and a value parameter pack stores a list of zero or more value parameters. A type parameter pack is declared in angle brackets using the `each` contextual keyword: + +```swift +// 'S' is a type parameter pack where each pack element conforms to 'Sequence'. +func zip(...) +``` + +A parameter pack itself is not a first-class value or type, but the elements of a parameter pack can be used anywhere that naturally accepts a comma-separated list of values or types using _pack expansions_. A pack expansion unpacks the elements of a pack into a comma-separated list, and elements can be appended to either side of a pack expansion by writing more values in the comma-separated list. + +A pack expansion consists of the `repeat` keyword followed by a type or an expression. The type or expression that `repeat` is applied to is called the _repetition pattern_. The repetition pattern must contain pack references. Similarly, pack references can only appear inside repetition patterns and generic requirements: + +```swift +func zip(_ sequence: repeat each S) where each S: Sequence +``` + +Given a concrete pack substitution, the pattern is repeated for each element in the substituted pack. If `S` is substituted with `Array, Set`, then `repeat Optional` will repeat the pattern `Optional` for each element in the substitution to produce `Optional>, Optional>`. + +Here are the key concepts introduced by this proposal: + +- Under the new model, all existing types and values in the language become _scalar types_ and _scalar values_. +- A _type pack_ is a new kind of type which represents a list of scalar types. Type packs do not have syntax in the surface language, but we will write them as `{T1, ..., Tn}` where each `Ti` is a scalar type. Type packs cannot be nested; type substitution is defined to always flatten type packs. +- A _type parameter pack_ is a type parameter which can abstract over a type pack. These are declared in a generic parameter list using the syntax `each T`, and referenced with `each T`. +- A _pack expansion type_ is a new kind of scalar type which flattens a set of type packs in a context where a comma-separated list of types may appear. The syntax for a pack expansion type is `repeat each P`, where `P` is a type containing one or more type parameter packs. +- A _value pack_ is a list of scalar values. The type of a value pack is a type pack, where each element of the type pack is the scalar type of the corresponding scalar value. Value packs do not have syntax in the surface language, but we will write them as `{x1, ..., xn}` where each `xi` is a scalar value. Value packs cannot be nested; evaluation is always defined to flatten value packs. +- A _value parameter pack_ is a function parameter or local variable declared with a pack expansion type. +- A _pack expansion expression_ is a new kind of expression whose type is a pack expansion type. Written as `repeat each expr`, where `expr` is an expression referencing one or more value parameter packs. + +The following example demonstrates these concepts together: + +```swift +struct Pair { + init(_ first: First, _ second: Second) +} + +func makePairs( + firsts first: repeat each First, + seconds second: repeat each Second +) -> (repeat Pair) { + return (repeat Pair(each first, each second)) +} + +let pairs = makePairs(firsts: 1, "hello", seconds: true, 1.0) +// 'pairs' is '(Pair(1, true), Pair("hello", 2.0))' +``` + +The `makePairs` function declares two type parameter packs, `First` and `Second`. The value parameter packs `first` and `second` have the pack expansion types `repeat each First` and `repeat each Second`, respectively. The return type `(repeat Pair)` is a tuple type where each element is a `Pair` of elements from the `First` and `Second` parameter packs at the given tuple position. + +Inside the body of `makePairs()`, `repeat Pair(each first, each second)` is a pack expansion expression referencing the value parameter packs `first` and `second`. + +The call to `makePairs()` substitutes the type pack `{Int, Bool}` for `First`, and the type pack `{String, Double}` for `Second`. These substitutions are deduced by the _type matching rules_, described below. The function is called with four arguments; `first` is the value pack `{1, "hello"}`, and `second` is the value pack `{true, 2.0}`. + +The substituted return type is the tuple type with two elements `(Pair, Pair)`, and the returned value is the tuple value with two elements `(Pair(1, true), Pair("hello", 2.0))`. + +## Detailed design + +**Note:** While this proposal talks about "generic functions", everything also applies to initializers and subscripts nested inside types. With closure expressions, the situation is slightly more limited. Closure expressions support value parameter packs, however since closure expressions do not have polymorphic types in Swift, they're limited to referencing type parameter packs from outer scopes and cannot declare type parameter packs of their own. Also, the value parameter packs of closures cannot have argument labels, because as usual only named declarations have argument labels in Swift. + +### Type parameter packs + +The generic parameter list of a generic function can contain one or more _type parameter pack declarations_, written as an identifier preceeded by `each`: + +```swift +func variadic() {} +``` + +When referenced from type context, this identifier resolves to a _type parameter pack_. References to type parameter packs can only appear in the following positions: + +* The base type of a member type parameter pack, which is again subject to these rules +* The pattern type of a pack expansion type, where it stands for the corresponding scalar element type +* The pattern expression of a pack expansion expression, where it stands for the metatype of the corresponding scalar element type and can be used like any other scalar metatype, e.g. to call a static method, call an initializer, or reify the metatype value +* The subject type of a conformance, superclass, layout, or same-type requirement +* The constraint type of a same-type requirement + +### Pack expansion type + +A pack expansion type, written as `repeat P`, has a *pattern type* `P` and a non-empty set of _captured_ type parameter packs spelled with the `each` keyword. For example, the pack expansion type `repeat Array` has a pattern type `Array` that captures the type parameter pack `T`. + +**Syntactic validity:** Pack expansion types can appear in the following positions: + +* The type of a parameter in a function declaration, e.g. `func foo(values: repeat each T) -> Bool` +* The type of a parameter in a function type, e.g. `(repeat each T) -> Bool` +* The type of an unlabeled element in a tuple type, e.g. `(repeat each T)` + +Because pack expansions can only appear in positions that accept a comma-separated list, pack expansion patterns are naturally delimited by either a comma or the end-of-list delimiter, e.g. `)` for call argument lists or `>` for generic argument lists. + +The restriction where only unlabeled elements of a tuple type may have a pack expansion type is motivated by ergonomics. If you could write `(t: repeat each T)`, then after a substitution `T := {Int, String}`, the substituted type would be `(t: Int, String)`. This would be strange, because projecting the member `t` would only produce the first element. When an unlabeled element has a pack expansion type, like `(repeat each T)`, then after the above substitution you would get `(Int, String)`. You can still write `0` to project the first element, but this is less surprising to the Swift programmer. + +**Capture:** A type _captures_ a type parameter pack if the type parameter pack appears inside the pattern type, without any intervening pack expansion type. For example, if `T` and `U` are type parameter packs, then `repeat Array<(each T) -> each U>` captures both `T` and `U`. However, `repeat Array<(each T) -> (repeat each U)>` captures `T`, but *not* `U`. Only the inner pack expansion type `repeat each U` captures `U`. (Indeed, in a valid program, every reference to a type parameter pack is captured by exactly one pack expansion type.) + +The captures of the pattern type are a subset of the captures of the pack expansion type itself. In some situations (described in the next section), the pack expansion type might capture a type parameter pack that does not appear in the pattern type. + +**Typing rules:** A pack expansion type is _well-typed_ if replacing the captured type parameter packs in the pattern type with scalar type parameters of the same constraints produces a well-typed scalar type. + +For example, if `each T` is a type parameter pack subject to the conformance requirement `each T: Hashable`, then `repeat Set` is well-typed, because `Set` is well-typed given `T: Hashable`. + +However, if `each T` were not subject to this conformance requirement, then `repeat Set` would not be well-typed; the user might substitute `T` with a type pack containing types that do not conform to `Hashable`, like `T := {AnyObject, Int}`, and the expanded substitution `Set, Set` is not well-typed because `Set` is not well-typed. + +### Type substitution + +Recall that a reference to a generic function from expression context always provides an implicit list of *generic arguments* which map each of the function's type parameters to a *replacement type*. The type of the expression referencing a generic declaration is derived by substituting each type parameter in the declaration's type with the corresponding replacement type. + +The replacement type of a type parameter pack is always a type pack. Since type parameter packs always occur inside the pattern type of a pack expansion type, we need to define what it means to perform a substitution on a type that contains pack expansion types. + +Recall that pack expansion types appear in function parameter types and tuple types. Substitution replaces each pack expansion type with an expanded type list, which is flattened into the outer type list. + +**Intuition:** The substituted type list is formed by replacing the captured type parameter pack references with the corresponding elements of each replacement type pack. + +For example, consider the declaration: + +```swift +func variadic( + t: repeat each T, + u: repeat each U +) -> (Int, repeat ((each T) -> each U)) +``` + +Suppose we reference it with the following substitutions: + +```swift +T := {String, repeat each V, Float} +U := {NSObject, repeat Array, NSString} +``` + +The substituted return type of `variadic` becomes a tuple type with 4 elements: + +```swift +(Int, (String) -> NSObject, repeat ((each V) -> Array), (Float) -> NSString) +``` + +**Formal algorithm:** Suppose `repeat P` is a pack expansion type with pattern type `P`, that captures a list of type parameter packs `Ti`, and let `S[Ti]` be the replacement type pack for `Ti`. We require that each `S[Ti]` has the same length; call this length `N`. If the lengths do not match, the substitution is malformed. Let `S[Ti][j]` be the `j`th element of `S[Ti]`, where `0 ≤ j < N`. + +The `j`th element of the replacement type list is derived as follows: + +1. If each `S[Ti][j]` is a scalar type, the element type is obtained by substituting each `Ti` with `S[Ti][j]` in the pattern type `P`. +2. If each `S[Ti][j]` is a pack expansion type, then `S[Ti][j]` = `repeat Pij` for some pattern type `Pij`. The element type is the pack expansion type `repeat Qij`, where `Qij` is obtained by substituting each `Ti` with `Pij` in the pattern type `P`. +3. Any other combination means the substitution is malformed. + +When the lengths or structure of the replacement type packs do not match, the substitution is malformed. This situation is diagnosed with an error by checking generic requirements, as discussed below. + +For example, the following substitutions are malformed because the lengths do not match: + +```swift +T := {String, Float} +U := {NSObject} +``` + +The following substitutions are malformed because the replacement type packs have incompatible structure, hitting Case 3 above: + +```swift +T := {repeat each V, Float} +U := {NSObject, repeat each W} +``` + +To clarify what it means for a type to capture a type parameter pack, consider the following: + +```swift +func variadic(t: repeat each T, u: repeat each U) -> (repeat (each T) -> (repeat each U)) +``` + +The pack expansion type `repeat (each T) -> (repeat each U)` captures `T`, but not `U`. If we apply the following substitutions: + +```swift +T := {Int, String} +U := {Float, Double, Character} +``` + +Then the substituted return type becomes a pair of function types: + +```swift +((Int) -> (Float, Double, Character), (String) -> (Float, Double, Character)) +``` + +Note that the entire replacement type pack for `U` was flattened in each repetition of the pattern type; we did not expand "across" `U`. + +**Concrete pattern type:** It is possible to construct an expression with a pack expansion type whose pattern type does not capture any type parameter packs. This is called a pack expansion type with a _concrete_ pattern type. For example, consider this declaration: + +```swift +func counts(_ t: repeat each T) { + let x = (repeat (each t).count) +} +``` + +The `count` property on the `Collection` protocol returns `Int`, so the type of the expression `(repeat (each t).count)` is written as the one-element tuple type `(repeat Int)` whose element is the pack expansion type `repeat Int`. While the pattern type `Int` does not capture any type parameter packs, the pack expansion type must still capture `T` to represent the fact that after expansion, the resulting tuple type has the same length as `T`. This kind of pack expansion type can arise during type inference, but it cannot be written in source. + +#### Single-element pack substitution + +If a parameter pack `each T` is substituted with a single element, the parenthesis around `(repeat each T)` are unwrapped to produce the element type as a scalar instead of a one-element tuple type. + +For example, the following substitutions both produce the element type `Int`: +- Substituting `each T := {Int}` into `(repeat each T)`. +- Substituting `each T := {}` into `(Int, repeat each T)`. + +Though unwrapping single-element tuples complicates type matching, surfacing single-element tuples in the programming model would icnrease the surface area of the language. One-element tuples would need to be manually unrwapped with `.0` or pattern matching in order to make use of their contents. This unwrapping would clutter up code. + + +### Type matching + +Recall that the substitutions for a reference to a generic function are derived from the types of call argument expressions together with the contextual return type of the call, and are not explicitly written in source. This necessitates introducing new rules for _matching_ types containing pack expansions. + +There are two separate rules: + +- For call expressions where the callee is a named function declaration, _label matching_ is performed. +- For everything else, _type list matching_ is performed. + +#### Label matching + +Here, we use the same rule as the "legacy" variadic parameters that exist today. If a function declaration parameter has a pack expansion type, the parameter must either be the last parameter, or followed by a parameter with a label. A diagnostic is produced if the function declaration violates this rule. + +Given a function declaration that is well-formed under this rule, type matching then uses the labels to delimit type packs. For example, the following is valid: + + ```swift + func concat(t: repeat each T, u: repeat each U) -> (repeat each T, repeat each U) + + // T := {Int, Double} + // U := {String, Array} + concat(t: 1, 2.0, u: "hi", [3]) + + // substituted return type is (Int, Double, String, Array) + ``` + + while the following is not: + + ```swift + func bad(t: repeat each T, repeat each U) -> (repeat each T, repeat each U) + // error: 'repeat each T' followed by an unlabeled parameter + + bad(1, 2.0, "hi", [3]) // ambiguous; where does 'each T' end and 'each U' start? + ``` + +#### Trailing closure matching + +Argument-to-parameter matching for parameter pack always uses a forward-scan for trailing closures. For example, the following code is valid: + +```swift +func trailing(t: repeat each T, u: repeat each U) {} + +// T := {() -> Int} +// U := {} +trailing { 0 } +``` + +while the following produces an error: + +```swift +func trailing(t: repeat each T, u: repeat each U) {} + +// error: type '() -> Int' cannot conform to 'Sequence' +trailing { 0 } +``` + +#### Type list matching + +In all other cases, we're matching two comma-separated lists of types. If either list contains two or more pack expansion types, the match remains _unsolved_, and the type checker attempts to derive substitutions by matching other types before giving up. (This allows a call to `concat()` as defined above to succeed, for example; the match between the contextual return type and `(repeat each T, repeat each U)` remains unsolved, but we are able to derive the substitutions for `T` and `U` from the call argument expressions.) + +Otherwise, we match the common prefix and suffix as long as no pack expansion types appear on either side. After this has been done, there are three possibilities: + +1. Left hand side contains a single pack expansion type, right hand size contains zero or more types. +2. Left hand side contains zero or more types, right hand side contains a single pack expansion type. +3. Any other combination, in which case the match fails. + +For example: + +```swift +func variadic(_: repeat each T) -> (Int, repeat each T, String) {} + +let fn = { x, y in variadic(x, y) as (Int, Double, Float, String) } +``` + +Case 3 covers the case where one of the lists has a pack expansion, but the other one is too short; for example, matching `(Int, repeat each T, String, Float)` against `(Int, Float)` leaves you with `(repeat each T, String)` vs `()`, which is invalid. + +If neither side contains a pack expansion type, Case 3 also subsumes the current behavior as implemented without this proposal, where type list matching always requires the two type lists to have the same length. For example, when matching `(Int, String)` against `(Int, Float, String)`, we end up with `()` vs `(Float)`, which is invalid. + +The type checker derives the replacement type for `T` in the call to `variadic()` by matching the contextual return type `(Int, Double, Float, String)` against the declared return type `(Int, repeat each T, String)`. The common prefix `Int` and common suffix `String` successfully match. What remains is the pack expansion type `repeat each T` and the type list `Double, Float`. This successfully matches, deriving the substitution `T := {Double, Float}`. + +While type list matching is positional, the type lists may still contain labels if we're matching two tuple types. We require the labels to match exactly when dropping the common prefix and suffix, and then we only allow Case 1 and 2 to succeed if the remaining type lists do not contain any labels. + +For example, matching `(x: Int, repeat each T, z: String)` against `(x: Int, Double, y: Float, z: String)` drops the common prefix and suffix, and leaves you with the pack expansion type `repeat each T` vs the type list `Double, y: Float`, which fails because `Double: y: Float` contains a label. + +However, matching `(x: Int, repeat each T, z: String)` against `(x: Int, Double, Float, z: String)` leaves you with `repeat each T` vs `Double, Float`, which succeeds with `T := {Double, Float}`, because the labels match exactly in the common prefix and suffix, and no labels remain once we get to Case 1 above. + +### Member type parameter packs + +If a type parameter pack `each T` is subject to a protocol conformance requirement `P`, and `P` declares an associated type `A`, then `(each T).A` is a valid pattern type for a pack expansion type, called a _member type parameter pack_. + +Under substitution, a member type parameter pack projects the associated type from each element of the replacement type pack. + +For example: + +```swift +func variadic(_: repeat each T) -> (repeat (each T).Element) +``` + +After the substitution `T := {Array, Set}`, the substituted return type of this function becomes the tuple type `(Int, String)`. + +We will refer to `each T` as the _root type parameter pack_ of the member type parameter packs `(each T).A` and `(each T).A.B`. + +### Generic requirements + +All existing kinds of generic requirements generalize to type parameter packs. Same-type requirements generalize in multiple different ways, depending on whether one or both sides involve a type parameter pack. + +1. Conformance, superclass, and layout requirements where the subject type is a type parameter pack are interpreted as constraining each element of the replacement type pack: + + ```swift + func variadic(_: repeat each S) where each S: Sequence { ... } + ``` + + A valid substitution for the above might replace `S` with `{Array, Set}`. Expanding the substitution into the requirement `each S: Sequence` conceptually produces the following conformance requirements: `Array: Sequence, Set: Sequence`. + +2. A same-type requirement where one side is a type parameter pack and the other type is a scalar type that does not capture any type parameter packs is interpreted as constraining each element of the replacement type pack to _the same_ scalar type: + + ```swift + func variadic(_: repeat each S) where (each S).Element == T {} + ``` + + This is called a _same-element requirement_. + + A valid substitution for the above might replace `S` with `{Array, Set}`, and `T` with `Int`. + + +3. A same-type requirement where each side is a pattern type that captures at least one type parameter pack is interpreted as expanding the type packs on each side of the requirement, equating each element pair-wise. + + ```swift + func variadic(_: repeat each S) where (each S).Element == Array {} + ``` + + This is called a _same-type-pack requirement_. + + A valid substitution for the above might replace `S` with `{Array>, Set>}`, and `T` with `{Int, String}`. Expanding `(each S).Element == Array` will produce the following list of same-type requirements: `Array.Element == Array, Set>.Element == String`. + +There is an additional kind of requirement called a _same-shape requirement_. There is no surface syntax for spelling a same-shape requirement; they are always inferred, as described in the next section. + +**Symmetry:** Recall that same-type requirements are symmetrical, so `T == U` is equivalent to `U == T`. Therefore some of the possible cases above are not listed, but the behavior can be understood by first transposing the same-type requirement. + +**Constrained protocol types:** A conformance requirement where the right hand side is a constrained protocol type `P` may reference type parameter packs from the generic arguments `Ti` of the constrained protocol type. In this case, the semantics are defined in terms of the standard desugaring. Independent of the presence of type parameter packs, a conformance requirement to a constrained protocol type is equivalent to a conformance requirement to `P` together with one or more same-type requirements that constrain the primary associated types of `P` to the corresponding generic arguments `Ti`. After this desugaring step, the induced same-type requirements can then be understood by Case 2, 3, 4 or 5 above. + +#### Same-shape requirements + +A same-shape requirement states that two type parameter packs have the same number of elements, with pack expansion types occurring at identical positions. + +This proposal does not include a spelling for same-shape requirements in the surface language; same-shape requirements are always inferred, and an explicit same-shape requirement syntax is a future direction. However, we will use the notation `shape(T) == shape(U)` to denote same-shape requirements in this proposal. + +A same-shape requirement always relates two root type parameter packs. Member types always have the same shape as the root type parameter pack, so `shape(T.A) == shape(U.B)` reduces to `shape(T) == shape(U)`. + +**Inference:** Same-shape requirements are inferred in the following ways: + +1. A same-type-pack requirement implies a same-shape requirement between all type parameter packs captured by the pattern types on each side of the requirement. + + For example, given the parameter packs ``, the same-type-pack requirement `Pair == (each S).Element` implies `shape(First) == shape(Second), shape(First) == shape(S), shape(Second) == shape(S)`. + +2. A same-shape requirement is inferred between each pair of type parameter packs captured by a pack expansion type appearing in the following positions + * all types appearing in the requirements of a trailing `where` clause of a generic function + * the parameter types and the return type of a generic function + +Recall that if the pattern of a pack expansion type contains more than one type parameter pack, all type parameter packs must be known to have the same shape, as outlined in the [Type substitution](#type-substitution) section. Same-shape requirement inference ensures that these invariants are satisfied when the pack expansion type occurs in one of the two above positions. + +If a pack expansion type appears in any other position, all type parameter packs captured by the pattern type must already be known to have the same shape, otherwise an error is diagnosed. + +For example, `zip` is a generic function, and the return type `(repeat (each T, each U))` is a pack expansion type, therefore the same-shape requirement `shape(T) == shape(U)` is automatically inferred: + +```swift +// Return type infers 'where length(T) == length(U)' +func zip(firsts: repeat each T, seconds: repeat each U) -> (repeat (each T, each U)) { + return (repeat (each firsts, each seconds)) +} + +zip(firsts: 1, 2, seconds: "hi", "bye") // okay +zip(firsts: 1, 2, seconds: "hi") // error; length requirement is unsatisfied +``` + +Here is an example where the same-shape requirement is not inferred: + +```swift +func foo(t: repeat each T, u: repeat each U) { + let tup: (repeat (each T, each U)) = /* whatever */ +} +``` + +The type annotation of `tup` contains a pack expansion type `repeat (each T, each U)`, which is malformed because the requirement `shape(T) == shape(U)` is unsatisfied. This pack expansion type is not subject to requirement inference because it does not occur in one of the above positions. + +#### Restrictions on same-shape requirements + +While type packs cannot be written directly, a requirement where both sides are concrete types is desugared using the type matching algorithm, therefore it will be possible to write down a requirement that constrains a type parameter pack to a concrete type pack, unless some kind of restriction is imposed: + +```swift +func append(_: repeat each S, _: repeat each T) where (each S).Element == (Int, String) {} +``` + +Furthermore, since the same-type requirement implies a same-shape requirement, we've implicitly constrained `T` to having a length of 2 elements, without knowing what those elements are. + +This introduces theoretical complications. In the general case, same-type requirements on type parameter packs allows encoding arbitrary systems of integer linear equations: + +```swift +// shape(Q) = 2 * shape(R) + 1 +// shape(Q) = shape(S) + 2 +func solve(q: repeat each Q, r: repeat each R, s: repeat eachS) + where (repeat each Q) == (Int, repeat each R, repeat each R), + (repeat each Q) == (repeat each S, String, Bool) { } +``` + +While type-level linear algebra is interesting, we may not ever want to allow this in the language to avoid significant implementation complexity, and we definitely want to disallow this expressivity in this proposal. + +To impose restrictions on same-shape and same-type requirements, we will formalize the concept of the “shape” of a pack, where a shape is one of: + +* A single scalar type element; all scalar types have a singleton ``scalar shape'' +* An abstract shape that is specific to a pack parameter +* A concrete shape that is composed of the scalar shape and abstract shapes + +For example, the pack `{Int, repeat each T, U}` has a concrete shape that consists of two single elements and one abstract shape. + +This proposal only enables abstract shapes. Each type parameter pack has an abstract shape, and same-shape requirements merge equivalence classes of abstract shapes. Any same-type requirement that imposes a concrete shape on a type parameter pack will be diagnosed as a *conflict*, much like other conflicting requirements such as `where T == Int, T == String` today. + +This aspect of the language can evolve in a forward-compatible manner. Over time, some restrictions can be lifted, while others remain, as different use-cases for type parameter packs are revealed. + +### Value parameter packs + +A _value parameter pack_ represents zero or more function arguments, and it is declared with a function parameter that has a pack expansion type. In the following declaration, the function parameter `value` is a value parameter pack that receives a _value pack_ consisting of zero or more argument values from the call site: + +```swift +func tuplify(_ value: repeat each T) -> (repeat each T) + +_ = tuplify() // T := {}, value := {} +_ = tuplify(1) // T := {Int}, value := {1} +_ = tuplify(1, "hello", [Foo()]) // T := {Int, String, [Foo]}, value := {1, "hello", [Foo()]} +``` + +**Syntactic validity:** A value parameter pack can only be referenced from a pack expansion expression. A pack expansion expression is written as `repeat expr`, where `expr` is an expression containing one or more value parameter packs or type parameter packs spelled with the `each` keyword. Pack expansion expressions can appear in any position that naturally accepts a comma-separated list of expressions. This includes the following: + +* Call arguments, e.g. `generic(repeat each value)` +* Subscript arguments, e.g. `subscriptable[repeat each index]` +* The elements of a tuple value, e.g. `(repeat each value)` +* The elements of an array literal, e.g. `[repeat each value]` + +Pack expansion expressions can also appear in an expression statement at the top level of a brace statement. In this case, the semantics are the same as scalar expression statements; the expression is evaluated for its side effect and the results discarded. + +Note that pack expansion expressions can also reference _type_ pack parameters, as metatypes. + +**Capture:** A pack expansion expression _captures_ a value (or type) pack parameter the value (or type) pack parameter appears as a sub-expression without any intervening pack expansion expression. + +Furthermore, a pack expansion expression also captures all type parameter packs captured by the types of its captured value parameter packs. + +For example, say that `x` and `y` are both value parameter packs and `T` is a type parameter pack, and consider the pack expansion expression `repeat foo(each x, (each T).self, (repeat each y))`. This expression captures both the value parameter pack `x` and type parameter pack `T`, but it does not capture `y`, because `y` is captured by the inner pack expansion expression `repeat each y`. Additionally, if `x` has the type `Foo`, then our expression captures `U`, but not `V`, because again, `V` is captured by the inner pack expansion type `repeat each V`. + +**Typing rules:** For a pack expansion expression to be well-typed, two conditions must hold: + +1. The types of all value parameter packs captured by a pack expansion expression must be related via same-shape requirements. + +2. After replacing all value parameter packs with non-pack parameters that have equivalent types, the pattern expression must be well-typed. + +Assuming the above hold, the type of a pack expansion expression is defined to be the pack expansion type whose pattern type is the type of the pattern expression. + +**Evaluation semantics:** At runtime, each value (or type) parameter pack receives a value (or type) pack, which is a concrete list of values (or types). The same-shape requirements guarantee that all value (and type) packs have the same length, call it `N`. The evaluation semantics are that for each successive `i` such that `0 ≤ i < N`, the pattern expression is evaluated after substituting each occurrence of a value (or type) parameter pack with the `i`th element of the value (or type) pack. The evaluation proceeds from left to right according to the usual evaluation order, and the list of results from each evaluation forms the argument list for the parent expression. + +For example, pack expansion expressions can be used to forward value parameter packs to other functions: + +```swift +func tuplify(_ t: repeat each T) -> (repeat each T) { + return (repeat each t) +} + +func forward(u: repeat each U) { + let _ = tuplify(repeat each u) // T := {repeat each U} + let _ = tuplify(repeat each u, 10) // T := {repeat each U, Int} + let _ = tuplify(repeat each u, repeat each u) // T := {repeat each U, repeat each U} + let _ = tuplify(repeat [each u]) // T := {repeat Array} +} +``` + +### Overload resolution + +Generic functions can be overloaded by the "pack-ness" of their type parameters. For example, a function can have two overloads where one accepts a scalar type parameter and the other accepts a type parameter pack: + +```swift +func overload(_: T) {} +func overload(_: repeat each T) {} +``` + +If both overloads match a given call, e.g. `overload(1)`, the call is ambiguous. Similarly, two generic functions where one accepts a non-pack variadic parameter and the other accepts a type parameter pack: + +```swift +func overload(_: T...) {} +func overload(_: repeat each T) {} + +overload() // ambiguity error +``` + +In other words, variadic generic functions have the same ranking as other generic functions. + +## Effect on ABI stability + +This is still an area of open discussion, but we anticipate that generic functions with type parameter packs will not require runtime support, and thus will backward deploy. As work proceeds on the implementation, the above is subject to change. + +## Alternatives considered + +### Modeling packs as tuples with abstract elements + +Under this alternative design, packs are just tuples with abstract elements. This model is attractive because it adds expressivity to all tuple types, but there are some significant disadvantages that make packs hard to work with: + +* There is a fundamental ambiguity between forwarding a tuple with its elements flattened and passing a tuple as a single tuple value. This could be resolved by requiring a splat operator to forward the flattened elements, but it would still be valid in many cases to pass the tuple without flattening the elements. This may become a footgun, because you can easily forget to splat a tuple, which will become more problematic when tuples can conform to protocols. +* Because of the above issue, there is no clear way to zip tuples. This could be solved by having an explicit builtin to treat a tuple as a pack, which leads us back to needing a distinction between packs and tuples. + +The pack parameter design where packs are distinct from tuples also does not preclude adding flexibility to all tuple types. Converting tuples to packs and expanding tuple values are both useful features and are detailed in the future directions. + +### Syntax alternatives to `repeat each` + +The `repeat each` syntax produces fairly verbose variadic generic code. However, the `repeat` keyword is explicit signal that the pattern is repeated under substitution, and requiring the `each` keyword for pack references indicates which types or values will be subsituted in the expansion. This syntax design helps enforce the mental model that pack expansions result in iteration over each element in the parameter pack at runtime. + +The following syntax alternatives were also considered. + +#### The `...` operator + +A previous version of this proposal used `...` as the pack expansion operator with no explicit syntax for pack elements in pattern types. This syntax choice follows precedent from C++ variadic templates and non-pack variadic parameters in Swift. However, there are some serious downsides of this choice, because ... is already a postfix unary operator in the Swift standard library that is commonly used across existing Swift code bases, which lead to the following ambiguities: + +1. **Pack expansion vs non-pack variadic parameter.** Using `...` for pack expansions in parameter lists introduces an ambiguity with the use of `...` to indicate a non-pack variadic parameter. This ambiguity can arise when expanding a type parameter pack into the parameter list of a function type. For example: + +```swift +struct X { } + +struct Ambiguity { + struct Inner { + typealias A = X<((T...) -> U)...> + } +} +``` + +Here, the `...` within the function type `(T...) -> U` could mean one of two things: + +* The `...` defines a (non-pack) variadic parameter, so for each element `Ti` in the parameter pack, the function type has a single (non-pack) variadic parameter of type `Ti`, i.e., `(Ti...) -> Ui`. So, `Ambiguity.Inner.A` would be equivalent to `X<(String...) -> Float, (Character...) -> Double>`. +* The `...` expands the parameter pack `T` into individual parameters for the function type, and no pack parameters remain after expansion. Only `U` is expanded by the outer `...`. So, `Ambiguity.Inner.A` would be equivalent to `X<(String, Character) -> Float, (String, Character) -> Double>`. + +2. **Pack expansion vs postfix closed-range operator.** Using `...` as the value expansion operator introduces an ambiguity with the postfix closed-range operator. This ambiguity can arise when `...` is applied to a value pack in the pattern of a value pack expansion, and the values in the pack are known to have a postfix closed-range operator, such as in the following code which passes a list of tuple arguments to `acceptAnything`: + +```swift +func acceptAnything(_: T...) {} + +func ranges(values: T..., otherValues: U...) where T: Comparable, shape(T...) == shape(U...) { + acceptAnything((values..., otherValues)...) +} +``` + +In the above code, `values...` in the expansion pattern could mean either: + +* The postfix `...` operator is called on each element in `values`, and the result is expanded pairwise with `otherValues` such that each argument has type `(PartialRangeFrom, U)` +* `values` is expanded into each tuple passed to `acceptAnything`, with each element of `otherValues` appended onto the end of the tuple, and each argument has type `(T... U)` + + +3. **Pack expansion vs operator `...>`.** Another ambiguity arises when a pack expansion type `T...` appears as the final generic argument in the generic argument list of a generic type in expression context: + +```swift +let foo = Foo() +``` + +Here, the ambiguous parse is with the token `...>`, which would necessitate changing the grammar so that `...>` is no longer considered as a single token, and instead parses as the token `...` followed by the token `>`. + + +#### Another operator + +One alternative is to use a different operator, such as `*`, instead of `...` + +```swift +func zip(firsts: T*, seconds: U*) -> ((T, U)*) { + return ((firsts, seconds)*) +} +``` + +The downsides to postfix `*` include: + +* `*` is extremely subtle +* `*` evokes pointer types / a dereferencing operator to programmers familiar with other languages including C/C++, Go, Rust, etc. +* Choosing another operator does not alleviate the ambiguities in expressions, because values could also have a postfix `*` operator or any other operator symbol, leading to the same ambiguity. + +#### Magic builtin `map` method + +The prevlence of `map` and `zip` in Swift makes this syntax an attractive option for variadic generics: + +```swift +func wrap(_ values: repeat each T) -> (repeat Wrapped) { + return values.map { Wrapped($0) } +} +``` + +The downsides of a magic `map` method are: + +* `.map` isn't only used for mapping elements to a new element, it's also used for direct forwarding, which is very different to the way `.map` is used today. An old design exploration for variadic generics used a map-style builtin, but allowed exact forwarding to omit the `.map { $0 }`. Privileging exact forwarding would be pretty frustrating, because you would need to add `.map { $0 }` as soon as you want to append other elements to either side of the pack expansion, and it wouldn't work for other values that you might want to turn into packs such as tuples or arrays. +* There are two very different models for working with packs; the same conceptual expansion has very different spellings at the type and value level, `repeat Wrapped` vs `values.map { Wrapped($0) }`. +* Magic map can only be applied to one pack at a time, leaving no clear way to zip packs without adding other builtins. A `zip` builtin would also be misleading, because expanding two packs in parallel does not need to iterate over the packs twice, but using `zip(t, u).map { ... }` looks that way. +* The closure-like syntax is misleading because it’s not a normal closure that you can write in the language. This operation is also very complex over packs with any structure, including concrete types, because the compiler either needs to infer a common generic signature for the closure that works for all elements, or it needs to separately type check the closure once for each element type. +* `map` would still need to be resolved via overload resolution amongst the existing overloads, so this approach doesn't help much with the type checking issues that `...` has. + +## Future directions + +### Variadic generic types + +This proposal only supports type parameter packs on functions. A complementary proposal will describe type parameter packs on generic structs, enums and classes. + +### Local value packs + +This proposal only supports value packs for function parameters. The notion of a value parameter pack readily generalizes to a local variable of pack expansion type, for example: + +```swift +func variadic(t: repeat each T) { + let tt: repeat each T = repeat each t +} +``` + +References to `tt` have the same semantics as references to `t`, and must only appear inside other pack expansion expressions. + +### Explicit type pack syntax + +In this proposal, type packs do not have an explicit syntax, and a type pack is always inferred through the type matching rules. However, we could explore adding an explicit pack syntax in the future: + +```swift +struct Variadic {} + +extension Variadic where each T == {Int, String} {} // {Int, String} is a concrete pack +``` + +### Pack iteration + +All list operations can be expressed using pack expansion expressions by factoring code involving statements into a function or closure. However, this approach does not allow for short-circuiting, because the pattern expression will always be evaluated once for every element in the pack. Further, requiring a function or closure for code involving statements is unnatural. Allowing `for-in` loops to iterate over packs solves both of these problems. + +Value packs could be expanded into the source of a `for-in` loop, allowing you to iterate over each element in the pack and bind each value to a local variable: + +```swift +func allEmpty(_ array: repeat [each T]) -> Bool { + for a in repeat each array { + guard a.isEmpty else { return false } + } + + return true +} +``` + +The type of the local variable `a` in the above example is an `Array` of an opaque element type with the requirements that are written on `each T`. For the *i*th iteration, the element type is the *i*th type parameter in the type parameter pack `T`. + +### Pack element projection + +Use cases for variadic generics that break up pack iteration across function calls, require random access, or operate over concrete packs can be supported in the future by projecting individual elements out from a parameter pack. Because elements of the pack have different types, there are two approaches to pack element projection; using an `Int` index which will return the dynamic type of the element, and using a statically typed index which is parameterized over the requested pack element type. + +#### Dynamic pack indexing with `Int` + +Dynamic pack indexing is useful when the specific type of the element is not known, or when all indices must have the same type, such as for index manipulation or storing an index value. Packs could support subscript calls with an `Int` index, which would return the dynamic type of the pack element directly as the opened underlying type that can be assigned to a local variable with opaque type. Values of this type need to be erased or cast to another type to return an element value from the function: + +```swift +func element(at index: Int, in t: repeat each T) where each T: P -> any P { + // The subscript returns 'some P', which is erased to 'any P' + // based on the function return type. + let value: some P = t[index] + return value +} +``` + +#### Typed pack element projection using key-paths + +Some use cases for pack element projection know upfront which type within the pack will be projected, and can use a statically typed pack index. A statically typed pack index could be represented with `KeyPath` or a new `PackIndex` type, which is parameterized over the base type for access (i.e. the pack), and the resulting value type (i.e. the element within the pack to project). Pack element projection via key-paths falls out of 1) positional tuple key-paths, and 2) expanding packs into tuple values: + +```swift +struct Tuple { + var elements: (repeat each Elements) + + subscript(keyPath: KeyPath<(repeat each Elements), Value>) -> Value { + return elements[keyPath: keyPath] + } +} +``` + +The same positional key-path application could be supported directly on value packs. + +### Value expansion operator + +This proposal only supports the expansion operator on type parameter packs and value parameter packs, but there are other values that represent a list of zero or more values that the expansion operator would be useful for, including tuples and arrays. It would be desirable to introduce a new kind of expression that receives a scalar value and produces a value of pack expansion type. + +Here, we use the straw-man syntax `.element` for accessing tuple elements as a pack: + +```swift +func foo( + _ t: repeat each T, + _ u: repeat each U +) {} + +func bar1( + t: (repeat each T), + u: (repeat each U) +) { + repeat foo(each t.element, each u.element) +} + +func bar2( + t: (repeat each T), + u: (repeat each U) +) { + repeat foo(each t.element, repeat each u.element) +} +``` + +Here, `bar1(t: (1, 2), u: ("a", "b"))` will evaluate: + +```swift +foo(1, "a") +foo(2, "b") +``` + +While `bar2(t: (1, 2), u: ("a", "b"))` will evaluate: + +```swift +foo(1, "a", "b") +foo(2, "a", "b") +``` + +The distinction can be understood in terms of our notion of _captures_ in pack expansion expressions. + +### Pack destructuring operations + +The statically-known shape of a pack can enable destructing packs with concrete shape into the component elements: + +```swift +struct List { + let element: repeat each Element +} + +extension List { + func firstRemoved() -> List where (repeat each Element) == (First, repeat each Rest) { + let (first, rest) = (repeat each element) + return List(repeat each rest) + } +} + +let list = List(1, "Hello", true) +let firstRemoved = list.firstRemoved() // 'List("Hello", true)' +``` + +The body of `firstRemoved` decomposes `Element` into the components of its shape -- one value of type `First` and a value pack of type `repeat each Rest` -- effectively removing the first element from the list. + +### Tuple conformances + +Parameter packs, the above future directions, and a syntax for declaring tuple conformances based on [parameterized extensions](https://github.com/apple/swift/blob/main/docs/GenericsManifesto.md#parameterized-extensions) over non-nominal types enable implementing custom tuple conformances: + +```swift +extension (repeat each T): Equatable { + public static func ==(lhs: Self, rhs: Self) -> Bool { + for (l, r) in repeat (each lhs.element, each rhs.element) { + guard l == r else { return false } + } + return true + } +} +``` + +## Acknowledgments + +Thank you to Robert Widmann for exploring the design space of modeling packs as tuples, and to everyone who participated in earlier design discussions about variadic generics in Swift. From 2ccfc9865c39f59a64c300b4d0ddcc023010b9ab Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 21 Mar 2023 17:27:09 -0400 Subject: [PATCH 3044/4563] Update 0393-parameter-packs.md (#1989) Add review link and fix a typo identified by @xedin --- proposals/0393-parameter-packs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0393-parameter-packs.md b/proposals/0393-parameter-packs.md index 8e7f11d21d..31ea02da8c 100644 --- a/proposals/0393-parameter-packs.md +++ b/proposals/0393-parameter-packs.md @@ -5,7 +5,7 @@ * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Active review (March 21...April 3, 2023)** * Implementation: On `main` gated behind the frontend flag `-enable-experimental-feature VariadicGenerics` -* Review: ([pitch 1](https://forums.swift.org/t/pitch-parameter-packs/60543)) ([pitch 2](https://forums.swift.org/t/pitch-2-value-and-type-parameter-packs/60830)) +* Review: ([pitch 1](https://forums.swift.org/t/pitch-parameter-packs/60543)) ([pitch 2](https://forums.swift.org/t/pitch-2-value-and-type-parameter-packs/60830)) ([review](https://forums.swift.org/t/se-0393-value-and-type-parameter-packs/63859)) ## Introduction @@ -277,7 +277,7 @@ For example, the following substitutions both produce the element type `Int`: - Substituting `each T := {Int}` into `(repeat each T)`. - Substituting `each T := {}` into `(Int, repeat each T)`. -Though unwrapping single-element tuples complicates type matching, surfacing single-element tuples in the programming model would icnrease the surface area of the language. One-element tuples would need to be manually unrwapped with `.0` or pattern matching in order to make use of their contents. This unwrapping would clutter up code. +Though unwrapping single-element tuples complicates type matching, surfacing single-element tuples in the programming model would increase the surface area of the language. One-element tuples would need to be manually unrwapped with `.0` or pattern matching in order to make use of their contents. This unwrapping would clutter up code. ### Type matching From da22d9b16bc41a93cf7528263c51f6789a684499 Mon Sep 17 00:00:00 2001 From: Boris Buegling Date: Tue, 21 Mar 2023 16:43:38 -0700 Subject: [PATCH 3045/4563] Updated proposal - Added mention about SwiftPM dependency resolution - Added example for how `-load-plugin-executable` concretely looks like --- proposals/NNNN-swiftpm-expression-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-swiftpm-expression-macros.md b/proposals/NNNN-swiftpm-expression-macros.md index 4e599ff44a..279efce981 100644 --- a/proposals/NNNN-swiftpm-expression-macros.md +++ b/proposals/NNNN-swiftpm-expression-macros.md @@ -67,7 +67,7 @@ Macro implementations will be executed in a sandbox [similar to package plugins] ## Detailed Design -SwiftPM builds each macro as an executable for the host platform, applying certain additional compiler flags. Macros are expected to depend on SwiftSyntax using a versioned dependency that corresponds to a particular major Swift release. Each target that transitively depends on a macro will have access to it, concretely this happens by SwiftPM passing `-load-plugin-executable` to the compiler to specify which executable contains the implementation of a certain macro module. The macro defintion refers to the module and concrete type via an `#externalMacro` declaration which allows any dependency of the defining target to have access to the concrete macro. If any target of a library product depends on a macro, clients of said library will also get access to any public macros. Macros can have dependencies like any other target, but product dependencies of macros need to be statically linked, so explicitly dynamic library products cannot be used by a macro target. +SwiftPM builds each macro as an executable for the host platform, applying certain additional compiler flags. Macros are expected to depend on SwiftSyntax using a versioned dependency that corresponds to a particular major Swift release. Note that SwiftPM's dependency resolution is workspace-wide, so all macros (and potentially other clients) will end up consolidating on one particular version of SwiftSyntax. Each target that transitively depends on a macro will have access to it, concretely this happens by SwiftPM passing `-load-plugin-executable` to the compiler to specify which executable contains the implementation of a certain macro module (e.g. `-load-plugin-executable /path/to/package/.build/debug/MacroImpl#MacroImpl` where the argument after the hash symbol is a comma separated list of module names which can be referenced by the `module` parameter of external macro declarations). The macro defintion refers to the module and concrete type via an `#externalMacro` declaration which allows any dependency of the defining target to have access to the concrete macro. If any target of a library product depends on a macro, clients of said library will also get access to any public macros. Macros can have dependencies like any other target, but product dependencies of macros need to be statically linked, so explicitly dynamic library products cannot be used by a macro target. Concretely, the code for the macro package shown earlier would contain a macro implementation looking like this: From a9e21e3a4eb9526f998915c6554c7c72e5885a91 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 21 Mar 2023 20:38:55 -0400 Subject: [PATCH 3046/4563] Update 0390-noncopyable-structs-and-enums.md (#1990) --- proposals/0390-noncopyable-structs-and-enums.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index 65e9b7cd25..9ea1d21e2c 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -3,9 +3,9 @@ * Proposal: [SE-0390](0390-noncopyable-structs-and-enums.md) * Authors: [Joe Groff](https://github.com/jckarter), [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Kavon Farvardin](https://github.com/kavon) * Review Manager: [Stephen Canon](https://github.com/stephentyrone) -* Status: **Active review (February 21 ... March 7, 2023)** +* Status: **Active review (March 21 ... April 4, 2023)** * Implementation: on main as `@_moveOnly` behind the `-enable-experimental-move-only` option -* Review: [(pitch)](https://forums.swift.org/t/pitch-noncopyable-or-move-only-structs-and-enums/61903)[(review)](https://forums.swift.org/t/se-0390-noncopyable-structs-and-enums/63258) +* Review: [(pitch)](https://forums.swift.org/t/pitch-noncopyable-or-move-only-structs-and-enums/61903)[(first review)](https://forums.swift.org/t/se-0390-noncopyable-structs-and-enums/63258)[(second review)](https://forums.swift.org/t/second-review-se-0390-noncopyable-structs-and-enums/63866) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/5d075b86d57e3436b223199bd314b2642e30045f/proposals/0390-noncopyable-structs-and-enums.md) ## Introduction From ad2832265473bc803b1a50c7de81c6d0a947542b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 22 Mar 2023 10:55:48 -0700 Subject: [PATCH 3047/4563] Vision for macros in Swift (#1927) --- visions/macros.md | 515 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 515 insertions(+) create mode 100644 visions/macros.md diff --git a/visions/macros.md b/visions/macros.md new file mode 100644 index 0000000000..599e55aba9 --- /dev/null +++ b/visions/macros.md @@ -0,0 +1,515 @@ +# A Vision for Macros in Swift + +As Swift evolves, it gains new language features and capabilities. There are different categories of features: some fill in gaps, taking existing syntax that is not permitted and giving it a semantics that fit well with the existing language, for example conditional conformance or allowing existential values for protocols with `Self` or associated type requirements. Others introduce new capabilities or paradigms to the language, such as the addition of concurrency, ownership types, or comprehensive reflection. + +There is another large category of language features that provide syntactic sugar to eliminate common boilerplate, taking something that can be written out in long-form and making it more concise. Such features don't technically add any expressive power to the language, because you can always write the long-form version, but their effect can be transformational if it enables use cases that would otherwise have been unwieldy. The synthesis of `Codable` conformances, for example, is sheer boilerplate reduction, but it makes `Codable` support ubiquitous throughout the Swift ecosystem. Property wrappers allow one to factor out logic for property access, and have enabled a breadth of powerful libraries. New language features in this category are hard to evaluate, because there is a fundamental question of whether the feature is "worth it": does the set of use cases made better by this feature outweigh the cost of making the language larger and more complicated? + + + +## Democratizing syntactic sugar with macros + +Macros are a feature present in a number of languages that allow one to perform some kind of transformation on the program's input source code to produce a different program. The mechanism of transformation varies greatly, from lexical expansion in C macros, to custom rules that rewrite one syntax into other syntax, to programs that arbitrarily manipulate the abstract syntax tree (AST) of the program. Macro systems exist in C, LISP, Scheme, Scala, Racket, Rust, and a number of other languages, and each design has its own tradeoffs. + +In all of these languages, macros have the effect of democratizing syntactic sugar. Many tasks that would have required a new language feature or an external source-generating tool could, instead, be implemented as a macro. Doing so has trade-offs: many more people can implement a macro than can take a feature through the language's evolution process, but the macro implementation will likely have some compromises---non-ideal syntax, worse diagnostics, worse compile-time performance. Overall, the hope is that a macro system can keep the language smaller and more focused, yet remain expressive because it is extensible enough to support libraries for many different domains. As a project, a macro system should reduce the desire for new syntactic-sugar features, leaving more time for more transformative feature work. Even in the cases where a new language feature is warranted, a macro system can allow more experimentation with the feature to best understand how it should work, and then be "promoted" to a full language feature once we've gained experience from the macro version. + +### Use cases for macros + +There are many use cases for macros, but before we look forward to the new use cases that become possible with macros, let's take a look backward at existing Swift language features that might have been macros had this feature existed before: + +* **`Codable`**: What we think of as `Codable` is mostly a library feature, including the `Encodable` and `Decodable` protocols and the various encoding and decoding implementations. The language part of `Codable` is in the synthesis of `init(from:)`, `encode(to:)`, and `CodingKeys` definitions for structs, enums, and classes. A macro that is given information about the stored properties of a type, and the superclass of a class type, could generate the same implementations---and would be easier to implement, improve, and reason about than a bespoke implementation in the compiler. Synthesis for `Equatable`, `Comparable`, and `Hashable` conformances are similar. +* **String interpolation**: String interpolation is implemented as a series of calls into the string interpolation "builder." While the actual parsing of a string interpolation and matching of it to a type that is `ExpressibleByStringInterpolation` is outside the scope of most macro systems, the syntactic transformation into a set of `appendXXX` calls on the builder is something that could be implemented via a macro. +* **Property wrappers**: Property wrappers are integrated into the language syntax via a custom attribute approach (e.g., `@Clamped(0, 100) var percent: Double`), but the actual implementation of the feature is entirely a syntactic transformation that introduces new properties (e.g., the backing storage property `_percent`) and adds accessors to existing properties (e.g., `percent`'s getter becomes `_percent.wrappedValue`). Other built-in language features like `lazy`, `willSet`, and `didSet` use similar syntactic transformations. +* **Result builders**: Result builders are also integrated into the language syntax via a custom attribute, but the actual transformation applied to a closure is entirely syntactic: the compiler introduces calls into the builder's `buildExpression`, `buildBlock`, `buildOptional`, and so on. That syntactic transformation could be expressed via some form of macro. + +### When is a language feature better than a macro? + +As noted above, a macro system has the potential to replace large parts of existing Swift language features, and enable many new ones. But a macro system is not necessarily a good replacement for a special-built feature: + +* A special-built feature might benefit from custom ABI rules. +* A special-built feature might benefit from analyses that would be infeasible to apply in a macro, such as those dependent on data or control flow. +* A special-built feature might be able to offer substantially better diagnostics. +* A special-built feature might be substantially more efficient to apply because it can rely on information and data structures already in the compiler. +* A special-built feature might have capabilities that we need to deny to macros, lest the mere possibility of a macro applying incur massive compile-time costs. + +The goal of a macro system should be to be general enough to cover a breadth of potential language features, while still providing decent tooling support and discouraging abuse that makes Swift code hard to reason about. + + + +## Design questions for macros + +At a very high level, a macro takes part of the program's source code at compile time and translates it into other source code that is then compiled into the program. There are three fundamental questions about the use of macros: + +* What kind of translation can the macro perform? +* When is a macro expanded? +* How does the compiler expand the macro? + +### What kind of translation can the macro perform? + +A program's source code goes through several different representations as it is compiled, and a macro system can choose at what point in this translation it operates. We consider three different possibilities: + +* **Lexical**: a macro could operate directly on the program text (as a string) or a stream of tokens, and produce a new stream of tokens. The inputs to such a macro would not even have to be valid Swift syntax, which might allow for arbitrary sub-languages to be embedded within a macro. C macros are lexical in nature, and most lexical approaches would inherit the familiar problems of C macros: tooling (such as code completion and syntax highlighting) cannot reason about the inputs to lexical macros, and it's easy for such a macro to produce ill-formed output that results in poor diagnostics. +* **Syntactic**: a macro could operate on a syntax tree and produce a new syntax tree. The inputs to such a macro would be a parsed syntax tree, which is strictly less flexible than a lexical approach because it means the macros can only operate within the bounds of the existing Swift grammar. However, this restriction means that tooling based on the grammar (such as syntax highlighting) would apply to the inputs without having to expand the macro, and macro-using Swift code would follow the basic grammatical structure of Swift. The output of a macro should be a well-formed syntax tree, which will be type-checked by the compiler and integrated into the program. +* **Semantic**: a macro could operate on a type-checked representation of the program, such as an Abstract Syntax Tree (AST) with annotations providing types for expressions, information about which specific declarations are referenced in a function call, any implicit conversions applied to expressions, and so on. Semantic macros have a wealth of additional information that is not provided to lexical or syntactic information, but unlike lexical or syntactic macros, their inputs are restricted to well-typed Swift code. This limits the ability of macros to change the meaning of the code provided to them, which can be viewed both as a negative (less freedom to implement interesting macros) or as a positive (less chance of a macro doing something that confounds the expectations of a Swift programmer). A semantic macro could be required to itself produce a well-typed Abstract Syntax Tree that is incorporated into the program. + +Whichever kind of translation we choose, we will need some kind of language or library that is suitable for working with the program at that level. A lexical translation needs to be able to work with program text, whereas a syntactic translation also needs a representation of the program's syntax tree. Semantic translation requires a much larger part of the compiler, including a representation of the type system and the detailed results of fully type-checked code. + +### When is a macro expanded? + +The expansion of a macro could be initiated in a number of ways, including explicit macro-expansion syntax in the source code or implicit macro expansions that might depend on type checker behavior, e.g., as part of a conversion. The best way for macro expansion to be initiated may depend on the kind of translation that the macro performs: the expansion of a purely lexical or syntactic macro probably needs to be explicitly marked in the source code, because it can change the program structure in surprising ways, whereas a semantic macro might be implicitly expanded as part of type checking because it's working in concert with the type checker. + +Swift already has a syntactic pattern that could be used for explicit macro expansion in the form of the `#` prefix, e.g., as a generalization of language features like `#filePath`, `#line`, `#colorLiteral(red: 0.292, green: 0.081, blue: 0.6, alpha: 255)`, and `#warning("unknown platform")`. The general syntax of `#macroName(macro-arguments)` could be used to expand a macro with the given name and arguments. Doing so provides a clear indication of where macros are used, and would support lexical and/or syntactic macros that need to alter the fundamental syntactic structure of their arguments. We refer to macros written with the prefix `#` syntax as *freestanding macros*, because they act as an expression, declaration, or statement on their own, depending on context. For example, one could build a "stringify" macro that produces both its argument value and also a string representation of the argument's source code: + +```swift +let (value, code) = #stringify(x + y) // produces a tuple containing the result of x + y, and the string "x + y" +``` + +Similarly, Swift's attribute syntax provides an extension point that is already used for features such as property wrappers and result builders. This attribute syntax could be used to expand a macro whose expansion depends on the entity to which the attribute is attached. Therefore, we call these *attached macros*, and they can do things such as create a memberwise initializer for a struct: + +```swift +@MemberwiseInit +struct Point { + var x: Double + var y: Double +} +``` + +A `MemberwiseInit` attached macro would need access to the stored properties of the type it is applied to, as well as the ability to create a new declaration `init(x:y:)`. Such a macro would have to tie in to the compiler at an appropriate point where stored properties are known but the set of initializers has not been finalized. + +A similar approach could be used to synthesize parts of conformances to a protocol. For example, one could imagine that one could write a declaration whose body is implemented by a macro, e.g., + +```swift +protocol Equatable { + @SynthesizeEquatable + func ==(lhs: Self, rhs: Self) -> Bool +} +``` + +The `@SynthesizeEquatable` attribute could trigger a macro expansion when a particular type that conforms to `Equatable` is missing a suitable implementation of `==`. It could access the stored properties in the type used for `Self` so it can synthesize an `==` whose body is, e.g., `lhs.x == rhs.x && lhs.y == rhs.y`. + +There are likely many other places where a macro could be expanded, and the key points for any of them are: + +* What are the macro arguments and how are they evaluated (tokenized, parsed, type-checked, etc.)? +* What other information is available to macro expansion? +* What can the macro produce (statements, expressions, declarations, attributes, etc.) and how is that incorporated into the resulting program? + +### How does the compiler expand the macro? + +The prior design sections have focused on what the inputs and outputs of a macro are and where macros can be triggered, but not *how* the macro operates. Again, there are a number of possibilities: + +* Macros could use a limited textual expansion mechanism, like the C preprocessor. +* Macros could provide a set of pattern-matching rewrite rules, to identify specific syntax and rewrite it into other syntax, like Rust's [`macro_rules!`](https://doc.rust-lang.org/rust-by-example/macros.html). +* Macros could be arbitrary (Swift) code that manipulates a representation of the program (source code, syntax tree, typed AST, etc.) and produces a new program, like [Scala 3 macros](https://docs.scala-lang.org/scala3/guides/macros/macros.html) , [LISP macros](https://lisp-journey.gitlab.io/blog/common-lisp-macros-by-example-tutorial/), or [Rust procedural macros](https://doc.rust-lang.org/reference/procedural-macros.html). + +These options are ordered roughly in terms of increasing expressive power, where the last is the most flexible because one can write arbitrary Swift code to transform the program. The first two options have the benefit of being able to easily evaluate within the compiler, because they are fundamentally declarative in nature. This means that any tool built on the compiler can show the results of expanding a macro, e.g., within an IDE. + +The last option is more complicated, because it involves running arbitrary Swift code. The Swift compiler could conceivably include a complete interpreter for the Swift language, and so long as all of the code that is used in the macro definition is visible to that interpreter (e.g., it does not reference any code for which Swift source is not available), the Swift compiler could interpret the macro definition to produce the expanded result. LISP macros effectively work this way, because LISP is interpreted and can treat the executing program as data. + +Alternatively, the macro definition could be compiled separately from the program that uses the macro, for example into a standalone executable or a compiler plugin. The compiler would then invoke that executable or plugin to perform macro expansion each time it is necessary. This approach is taken both by Scala (which uses the JVM's JIT facilities to be able to compile the macro definition and load it into the compiler) and Rust procedural macros (which use a [`proc-macro`](https://doc.rust-lang.org/reference/linkage.html) crate type for specifically this purpose). A significant benefit of this approach is that the full source code of the macro need not be available as Swift code (so one can use system libraries), macro expansion can be faster (because it's compiled code), and it's easy to test macro definitions outside of the compiler. On the other hand, it means having the Swift compiler run arbitrary code, which opens up questions about security and sandboxing that need to be considered. + +### (Un)hygienic macros + +A [hygienic macro](https://en.wikipedia.org/wiki/Hygienic_macro#cite_note-hygiene-3) is a macro whose expansion cannot change the binding of names used in its macro arguments. For example, imagine the given use of `myMacro`: + +```swift +let x = 3.14159 +let y = 2.71828 +#myMacro(x + y) +``` + +The expression `x + y` is type-checked, and `x` and `y` are bound to local variables immediately above. With a hygienic macro, nothing the macro does can change the declarations to which `x` and `y` are bound. A non-hygienic macro could change these bindings. For example, imagine the macro use above expanded to the following: + +```swift +{ + let x = 42 + return x + y +}() +``` + +Here, the macro introduced a new local variable named `x`. With a hygienic macro, the newly-declared `x` is not found by the `x` in `x + y`: it is a different declaration (or it is not permitted to be introduced). With a non-hygienic macro, the `x` in `x + y` will now refer to the local variable introduced by the macro. In this case, the macro expansion for a non-hygienic macro will fail to type-check because one cannot add an `Int` and a `Double`. + +Hygienic macros do make some macros harder (or impossible) to write, if the macro intentionally wants to take over some of the names used in its arguments. For example, if one wanted to have a macro intercept access to local variables to (say) record the number of times `x` was dynamically accessed. As such, systems that provide hygienic macros often have a way to intentionally provide names from the environment in which the macro is used, such as Racket's [syntax parameters](https://docs.racket-lang.org/reference/stxparam.html). + +A standard approach to dealing with the problem of unintentional name collision in an unhygienic macro is to provide a way to generate unique names within the macro implementation. This approach has been used in LISP macros for decades via [`gensym`](http://clhs.lisp.se/Body/f_gensym.htm), and requires some discipline on the part of the macro implementer to create unique names whenever the macro creates a new declaration. + +## An approach to macros in Swift + +Based on the menu of design choices above, we propose a macro approach characterized by syntactic translation on already-type-checked Swift code that is implemented via a separate package. The intent here is to allow macros a lot of flexibility to implement interesting transformations, while not giving up the benefits of type-checking the code that the user wrote prior to translation. + +### Macro declarations + +A macro declaration indicates how the macro can be used in source code, much like a function declaration indicates the arguments and result type of a function. We declare macros as a new kind of entity, introduced with `macro`, that indicates that it's a macro definition and provides additional information about the interface to the macro. For example, consider the `stringify` macro described early, which could be defined as follows: + +```swift +@freestanding(expression) +macro stringify(_ value: T) -> (T, String) = #externalMacro(module: "MyMacros", type: "StringifyMacro") +``` + +The `macro` introducer indicates that this is a macro, named `stringify`. The `@freestanding` attribute notes that this is a freestanding macro (used with the `#` syntax) and that it is usable as an expression. The macro is defined (after the `=`) to have an externally-provided macro expansion operation that is the type named `MyMacros.StringifyMacro`. Because the definition is external, the `stringify` macro function doesn't need a function body. If Swift were to grow a way to implement macros directly here (rather than via a separate package), such macros could have a body but not an `#externalMacro` argument. + +A given macro can inhabit several different macro *roles*, each of which can expand in different ways. For example, consider a `Clamping` macro that implements behavior similar to the [property wrapper by the same name](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md#clamping-a-value-within-bounds): + +```swift +@attached(peer, prefixed(_)) +@attached(accessor) +macro Clamping(min: T, max: T) = #externalMacro(module: "MyMacros", type: "ClampingMacro") +``` + +The attribute specifies that this is an attached macro, so it can be used as an attribute as, e.g., + +```swift +@Clamping(min: 0, max: 255) var red: Int = 127 +``` + +The `Clamping` macro would be expanded in two different but complementary ways: + +* A *peer* declaration `_red` that provides the backing storage: + + ```swift + private var _red: Int = 127 + ``` + +* A set of *accessor*s that guard access to this storage, turning the `red` property into a computed property: + + ```swift + get { _red } + + set(__newValue) { + let __minValue = 0 + let __maxValue = 255 + if __newValue < __minValue { + _red = __minValue + } else if __newValue > __maxValue { + _red = __maxValue + } else { + _red = __newValue + } + } + ``` + + +### Macro definitions via a separate program + +Macro definitions would be provided in a separate program that performs a syntactic transformation. A macro definition would be implemented using [swift-syntax](https://github.com/apple/swift-syntax), by providing a type that conforms to one of the "macro" protocols in a new library, `SwiftSyntaxMacros`. For example, the `MyMacros` package we're using as an example might look like this: + +```swift +import SwiftDiagnostics +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +public struct StringifyMacro: ExpressionMacro { + static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + guard let argument = node.argumentList.first?.expression else { + fatalError("compiler bug: the macro does not have any arguments") + } + + return "(\(argument), \(literal: argument.description))" + } +} +``` + +Conformance to `ExpressionMacro` indicates a macro definition for an expression macro, and corresponds to `@freestanding(expression)`. There will be several protocols, corresponding to the various roles that macros inhabit. Each protocol has an `expansion` method that will be called with the syntax nodes that are involved in the macro expansion, along with a `context` instance that provides more information about how the macro is being invoked. + +The implementation of these functions makes extensive use of Swift syntax manipulation via the `swift-syntax` package. The inputs and outputs are in terms of syntax nodes: `ExprSyntax` describes the syntax for any kind of expression in Swift, whereas `MacroExpansionExprSyntax` is the syntax for an explicitly-written macro expansion. The `expansion` operation will return a new syntax node that will replace the ones it was given in the program. We use string interpolation as a form of quasi-quoting: the return of `StringifyMacro.expansion` forms a tuple `(\(argument), "\(literal: argument.description)")` where the first argument is the expression itself and the second is the source code translated into a string literal. The resulting string will be parsed into an expression that is returned to the compiler. + +Macro implementations are "host" programs that are completely separate from the program in which macros are used. This distinction is most apparent in cross-compilation scenarios, where the host platform (where the compiler is run) differs from the target platform (where the compiled program will run), and these could use different operating systems and processor architectures. Macro implementations are compiled for and executed on the host platform, whereas the results of expanding a macro will be compiled for and executed on the target platform. Therefore, macro implementations are defined as their own kind of target in the Swift package manifest. For example, a package that ties together the macro declaration for `#stringify` and its implementation as `StringifyMacro` follows: + +```swift +import PackageDescription +import CompilerPluginSupport + +let package = Package( + name: "MyMacros", + dependencies: [ + .package( + url: "https://github.com/apple/swift-syntax.git", + branch: "main" + ), + ], + targets: [ + // Macro implementation target contains the StringifyMacro type. + // Always built for the host platform. + .macro(name: "StringifyImpl", + dependencies: [.product(name: "SwiftSyntaxMacros", package: "swift-syntax")]), + + // Library target provides the macro declaration (public macro stringify) that is + // used by client code. + // Built for the target platform. + .target(name: "StringifyLib", dependencies: ["StringifyImpl"]), + + // Clients of the macro will depend on the library target. + .executableTarget(name: "StringifyClient", dependencies: ["StringifyLib"]), + ] +) +``` + +Conceptually, the separation of `macro` targets into separate programs (for the host platform) from other targets (for the target platform) means that the individual macros could be built completely separately from each other and from other targets, even if they happen to be for the same platform. In the extreme, this could mean that each `macro` would be allowed to build against a different version of `swift-syntax`, and other targets could choose to also use `swift-syntax` with a different version. Given that `swift-syntax` is modeling the Swift language (which evolves over time), it does not promise a stable API, so having the ability to have different macro implementations depend on different versions of `swift-syntax` is a feature: it would prevent conflicting version requirements in macros from causing problems. + +Note that this separation of dependencies for distinct targets is currently not possible in the Swift Package Manager. In the interim, macro implementations will need to adapt to be built with different versions of the `swift-syntax` package. + +#### Diagnostics + +A macro implementation can be used to produce diagnostics (e.g., warnings and errors) to indicate problems encountered during macro expansion. The `stringify` macro described above doesn't really have a failure case, but imagine an `#embed("somefile.txt")` macro that takes the contents of a file at build time and turns them into an array of bytes. The macro could have several different failure modes: + +* The macro argument isn't a string literal, so it doesn't know what the file name is. +* The file might not be available for reading because it is missing, inaccessible, etc. + +These failures would be reported as errors by providing [`Diagnostic`](https://github.com/apple/swift-syntax/blob/main/Sources/SwiftDiagnostics/Diagnostic.swift) instances to the context that specify the underlying problem. The diagnostics would refer to the syntax nodes provided to the macro definition, and the compiler would provide those diagnostics to the user. + +In its limit, a macro might perform no translation whatsoever on the syntax tree it is given, but instead be there only to provide diagnostics---for example, as a context-specific, custom lint-like rule that enforces additional constraints on the program. + +### Macro roles + +The `@freestanding` and `@attached` attributes for macro declarations specify the roles that the macro can inhabit, each of which corresponds to a different place in the source code where the macro can be expanded. Here is a potential set of roles where macro expansion could be warranted. The set of roles could certainly grow over time to enable new capabilities in the language: + +* **Expression**: A freestanding macro that can occur anywhere that an expression can occur, and must produce an expression. `#colorLiteral` could fall into this category: + + ```swift + // In library + @freestanding(expression) + macro colorLiteral(red: Double, green: Double, blue: Double, alpha: Double) -> _ColorLiteralType = + #externalMacro(module: "MyMacros", type: "ColorLiteral") + + // In macro definition package + public struct ColorLiteral: ExpressionMacro { + public static func expansion( + expansion: MacroExpansionExprSyntax, + in context: MacroExpansionContext + ) -> ExprSyntax { + return ".init(\(expansion.argumentList))" + } + } + ``` + + With this, an expression like `#colorLiteral(red: 0.5, green: 0.5, blue: 0.25, alpha: 1.0)` would produce a value of the `_ColorLiteralType` (presumably defined by a framework), and would be rewritten by the macro into `.init(red: 0.5, green: 0.5, blue: 0.25, alpha: 1.0)` and type-checked with the `_ColorLiteralType` as context so it would initialize a value of that type. + +* **Declaration**: A freestanding macro that can occur anywhere a declaration can occur, such as at the top level, in the definition of a type or extension thereof, or in a function or closure body. The macro can expand to zero or more declarations. These macros could be used to subsume the `#warning` and `#error` directives from [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md): + + ```swift + /// Emits the given message as a warning, as in SE-0196. + @freestanding(declaration) macro warning(_ message: String) + + /// Emits the given message as an error, as in SE-0196. + @freestanding(declaration) macro error(_ message: String) + ``` + +* **Code item**: A freestanding macro that can occur within a function or closure body and can produce a mix of zero or more statements, expressions, and declarations. + +* **Accessor**: An attached macro that adds accessors to a stored property or subscript, as shown by the `Clamping` macro example earlier. The inputs would be the arguments provided to the macro in the attribute, along with the property or subscript declaration to which the accessors will be attached. The output would be a set of accessor declarations, i.e., a getter and setter. A `Clamping` macro could be implemented as follows: + + ```swift + extension ClampingMacro: AccessorMacro { + static func expansion( + of node: CustomAttributeSyntax, + providingAccessorsOf declaration: DeclSyntax, + in context: MacroExpansionContext + ) throws -> [AccessorDeclSyntax] { + let originalName = /* get from declaration */, + minValue = /* get from custom attribute node */, + maxValue = /* get from custom attribute node */ + let storageName = "_\(originalName)", + newValueName = context.getUniqueName(), + maxValueName = context.getUniqueName(), + minValueName = context.getUniqueName() + return [ + """ + get { \(storageName) } + """, + """ + set(\(newValueName)) { + let \(minValueName) = \(minValue) + let \(maxValueName) = \(maxValue) + if \(newValueName) < \(minValueName) { + \(storageName) = \(minValueName) + } else if \(newValueName) > \(maxValueName) { + \(storageName) = \(maxValueName) + } else { + \(storageName) = \(newValueName) + } + } + """ + ] + } + } + ``` + +* **Witness**: An attached macro that can be expanded to provide a "witness" that satisfies a requirement of a protocol for a given concrete type's conformance to that protocol. Such a macro would take as input the conforming type, the protocol, and a declaration (without a body) that will be created in the conforming type. The output would be that declaration with a body added and (potentially) other modifications. For this to work well, we would almost certainly need to expose a lot of information about the conforming type, such as the set of stored properties. Assuming that exists, let's implement the `synthesizeEquatable` macro referenced earlier in this document: + + ```swift + // In the standard library + @attached(witness) + macro SynthesizeEquatable() = #externalMacro(module: "MyMacros", type: "EquatableSynthesis") + + protocol Equatable { + @SynthesizeEquatable + static func ==(lhs: Self, rhs: Self) -> Bool + } + + // In the macro definition library + struct EquatableSynthesis: AttachedMacro { + /// Expand a macro described by the given custom attribute to + /// produce a witness definition for the requirement to which + /// the attribute is attached. + static func expansion( + of node: CustomAttributeSyntax, + witness: DeclSyntax, + conformingType: TypeSyntax, + storedProperties: [StoredProperty], + in context: MacroExpansionContext + ) throws -> DeclSyntax { + let comparisons: [ExprSyntax] = storedProperties.map { property in + "lhs.\(property.name) == rhs.\(property.name)" + } + let comparisonExpr: ExprSyntax = comparisons.map { $0.description }.joined(separator: " && ") + return witness.withBody( + """ + { + return \(comparisonExpr) + } + """ + ) + } + } + ``` + +* **Member**: An attached macro that can be applied on a type or extension that expands to one or more declarations that will be inserted as members into that type or extension. As with a conformance macro, a member macro would probably want access to the stored properties of the enclosing type, and potentially other information. As an example, let's create a macro to synthesize a memberwise initializer: + + ```swift + // In the standard library + @attached(member) + macro memberwiseInit(access: Access = .public) = #externalMacro(module: "MyMacros", type: "MemberwiseInit") + + // In the macro definition library + struct MemberwiseInit: MemberMacro { + static func expansion( + of node: AttributeSyntax, + attachedTo declaration: DeclSyntax, + in context: inout MacroExpansionContext + ) throws -> [DeclSyntax] {} + let parameters: [FunctionParameterSyntax] = declaration.storedProperties.map { property in + let paramDecl: FunctionParameterSyntax = "\(property.name): \(property.type)" + guard let initializer = property.initializer else { + return paramDecl + } + return paramDecl.withDefaultArgument( + InitializerClauseSyntax( + equal: TokenSyntax(.equal, presence: .present), + value: "\(initializer)" + ) + ) + } + + let assignments: [ExprSyntax] = conformingType.storedProperties.map { property in + "self.\(property.name) = \(property.name)" + } + + return + #""" + public init(\(parameters.map { $0.description }.joined(separator: ", "))) { + \(assignments.map { $0.description }.joined(separator: "\n")) + } + """# + } + } + ``` + + Using this macro on a type, e.g., + + ```swift + @MemberwiseInit + class Point { + var x, y: Int + var z: Int = 0 + } + ``` + + would produce code like the following: + + ```swift + public init(x: Int, y: Int, z: Int = 0) { + self.x = x + self.y = y + self.z = z + } + ``` + +* **Body**: A body macro would allow one to create or replace the body of a function, initializer, or closure through syntactic manipulation. Body macros are attached to one of these entities, e.g., + + ```swift + @Traced(logLevel: 2) + func myFunction(a: Int, b: Int) { ... } + ``` + + where the `Traced` macro is declared as something like: + + ```swift + @attached(body) macro Traced(logLevel: Int = 0) + ``` + + and can introduce new code into the body to, e.g., perform logging. + +* **Conformance**: Conformance macros could introduce protocol conformances to the type or extension to which they are attached. For example, this could be useful when composed with macro roles that create other members, such as a macro that both adds a protocol conformance and also a stored property required by that conformance. + +## Tools for using and developing macros + +Macros introduce novel problems for tooling, because the macro expansion process replaces (or augments) code that is explicitly written with other source code that makes it into the final program. The design of a macro system has a large impact on the ability to build good tools, and a poor design can directly impact discoverability, predictability, debuggability, and compile-time efficiency. C macros demonstrate nearly all of these problems: + +* C macros are bare identifiers that can be used anywhere in source code, so it is hard to discover where macros are being applied in the source code. C programmers have adopted the `UPPERCASE_MACRO_NAME` convention to try to understand which names are macros and which aren't. +* C macros can expand to an arbitrary sequence of tokens in a manner that destroys program structure, for example, one can close a `struct` or function definition with a C macro, making it hard to predict the scope of effects a macro can have. +* C macros are expanded via logic within the compiler's preprocessor, and therefore offer no debugging capabilities. The only way to see the effect of a macro is to generate preprocessed output for an entire translate unit, then inspect the original code. +* C macros are rarely persisted after a program is built, so debugging a program that has made heavy use of macros requires one to manually map between the original source code (pre-macro) and the generated machine code, with no record of the expansion itself. + +The design proposed here for Swift makes it possible to build good tooling despite the challenges macros pose: + +* Uses of macros are indicated in the source (with `#` or `@`) to make the use of macros clear. +* Expansions of macros have their effects restricted to the scope in which the macro is used (e.g., augmenting or adding declarations locally), and any effects visible from other parts of the program are declared up front by the macro (e.g., the names it introduces), so one can reason about the effects of a macro expansion. +* Implementations of macros are normal Swift programs, so they can be developed and tested using the normal tools for Swift. Much of the development and testing of a macro can be done outside of the compiler, with unit tests that (for example) test the syntactic transformation on isolated examples that translate Swift code into different Swift code. +* The localized nature of macro effects, and the fact that all macro-expanded code is itself normal Swift code, make it possible to record the results of macro expansion in a way that can reconstitute the effects of macro expansion without rerunning the compiler, allowing useful debugging and diagnostics flows. + +Early implementations of Swift macros already provide macro-expansion information in a manner that is amenable to existing tooling. For example, the result of expanding a macro fits into several existing workflows: + +* If a warning or error message refers into code generated by macro expansion, the compiler writes the macro-expanded code into a separate Swift file and refers to that file within the diagnostic message. Users can open that file to see the results of that macro expansion to understand the problem. Each such diagnostic provides a stack of notes that refers back to the point where the macro expansion occurred, which may itself be within other macro-expanded code, all the way back to the original source code that triggered the outermost macro expansion. For example: + + ``` + /tmp/swift-generated-sources/@__swiftmacro_9MacroUser14testAddBlocker1a1b1c2oaySi_S2iAA8OnlyAddsVtF03addE0fMf1_.swift:1:4: error: binary operator '-' cannot be applied to two 'OnlyAdds' operands + oa - oa + ~~ ^ ~~ + macro_expand.swift:200:7: note: in expansion of macro 'addBlocker' here + _ = #addBlocker(oa + oa) + ^~~~~~~~~~~~~~~~~~~~ + ``` + +* Debug information for macro-expanded code uses a similar scheme to diagnostics, allowing one to see the macro-expanded code while debugging (e.g., in a backtrace), set breakpoints in it, step through it, and so on. Freestanding macros are treated like inline functions, so one can "step into" a macro expansion from the place it occurs in the source code. + +* SourceKit, which is used for IDE integration features, provides a new "Expand Macro" refactoring that can expand any single use of a macro right in the source code. This can be used both to see the effects of the macro, as well as to eliminate the use of the macro in favor of (say) a customized version where the macro is used as a starting point. + +* Macro implementations can be tested using `swift-syntax` and existing testing tooling. For example, the following test checks the expansion behavior of the `stringify` macro by performing syntactic expansion on the input source code (`sf`) and checking that against the expected result of expansion (in the `XCTAssertEqual` at the end): + + ```swift + final class MyMacroTests: XCTestCase { + func testStringify() { + let sf: SourceFileSyntax = + #""" + let a = #stringify(x + y) + let b = #stringify("Hello, \(name)") + """# + let context = BasicMacroExpansionContext.init( + sourceFiles: [sf: .init(moduleName: "MyModule", fullFilePath: "test.swift")] + ) + let transformedSF = sf.expand(macros: ["stringify": StringifyMacro.self], in: context) + XCTAssertEqual( + transformedSF.description, + #""" + let a = (x + y, "x + y") + let b = ("Hello, \(name)", #""Hello, \(name)""#) + """# + ) + } + } + ``` + +All of the above work with existing tooling, creating a baseline development experience that provides discoverability, predictability, and debuggability. Over time, more tooling can be made aware of macros to provide a more polished experience. From 0db4439e07ec098719f1f2edc9d1eef96ab67f10 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Wed, 22 Mar 2023 16:11:59 -0700 Subject: [PATCH 3048/4563] Revision to package acl proposal * Add to future directions: sub-packages/opt-out of package boundary * Updates to package name input string * Remove @usableFromPackageInline as it's unnecessary --- proposals/0386-package-access-modifier.md | 114 +++++++++++++++++----- 1 file changed, 89 insertions(+), 25 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index 259c51c445..5c01d20320 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -117,8 +117,7 @@ engine.run() // Error: cannot find `run` in scope ### Package Names -Two modules belong to the same package if they were built with the same package name. A package name must be a unique string of US-ASCII alphanumeric characters, `_`, `.`, or `-`; that is, it must match the regular expression `\A[A-Za-z0-9_.-]+\z`. It is passed to the Swift frontend via a new flag `-package-name`. - +Two modules belong to the same package if they were built with the same package name. A package name must be a unique string which starts with a letter or `_`, followed by characters allowed in [this c99 extended identifier definition](https://github.com/apple/swift-tools-support-core/blob/435a2708a6e486d69ea7d7aaa3f4ad243bc3b408/Sources/TSCUtility/StringMangling.swift#L43), including alphanumeric characters, a hypen, and a dot; any character not allowed in the definition is transcoded to a `_`. The string is case-insensitive, and it is passed to the Swift frontend via a new flag `-package-name`. Here's an example of how a package name is passed to a commandline invocation. ``` @@ -129,47 +128,45 @@ swiftc -module-name App -package-name appPkg ... When building the `Engine` module, the package name `gamePkg` is recorded in the built interface to the module. When building `Game`, its package name `gamePkg` is compared with the package name recorded in `Engine`'s built interface; since they match, `Game` is allowed to access `Engine`'s `package` declarations. When building `App`, its package name `appPkg` is different from `gamePkg`, so it is not allowed to access `package` symbols in either `Engine` or `Game`, which is what we want. -The Swift Package Manager already has a concept of a package identity string for every package, as specified by [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md). This string is verified to be unique via a registry, and it always works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. - If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. -When the Swift frontend builds a `.swiftmodule` file directly from source, the file will include the package name and all of the `package` declarations in the module. When the Swift frontend builds a `.swiftinterface` file from source, the file will include the package name, but it will put `package` declarations in a secondary `.package.swiftinterface` file. When the Swift frontend builds a `.swiftmodule` file from a `.swiftinterface` file that includes a package name, but it does not have the corresponding `.package.swiftinterface` file, it will record this in the `.swiftmodule`, and it will prevent this file from being used to build other modules in the same package. - -### Package Symbols and `@inlinable` Functions +The Swift Package Manager already has a concept of a package identity string for every package, as specified by [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md). This string is verified to be unique via a registry, and it always works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. -`package` functions can be made `@inlinable`. Just like with `@inlinable public`, not all symbols are usable within the `@inlinable package` function: they must be `open`, `public`, `package`, `@usableFromInline`, or `usableFromPackageInline`. +Scenarios where a target needs to be excluded from the package or project boundary or needes to be part of a subgroup within the boundary are discussed in the Future Directions. -`@usableFromPackageInline` is a new attribute which allows a symbol to be used from `@inlinable package` functions (that are defined in the same module) without having to make the symbol `package` or `public`. It can be used anywhere that `@usableFromInline` can be used, but the attributes cannot be combined. A `@usableFromPackageInline` symbol must be `internal`. For example: +### Package Symbols Distribution -``` -@usableFromPackageInline func internalFuncA() {} +When the Swift frontend builds a `.swiftmodule` file directly from source, the file will include the package name and all of the `package` declarations in the module. When the Swift frontend builds a `.swiftinterface` file from source, the file will include the package name, but it will put `package` declarations in a secondary `.package.swiftinterface` file. When the Swift frontend builds a `.swiftmodule` file from a `.swiftinterface` file that includes a package name, but it does not have the corresponding `.package.swiftinterface` file, it will record this in the `.swiftmodule`, and it will prevent this file from being used to build other modules in the same package. -@inlinable package func pkgUse() { - internalFuncA() // OK -} +### Package Symbols and `@inlinable` -@inlinable public func publicUse() { - internalFuncA() // OK -} -``` +`package` types can be made `@inlinable`. Just as with `@inlinable public`, not all symbols are usable within the body of `@inlinable package`: they must be `open`, `public`, or `@usableFromInline`. The `@usableFromInline` attribute can be applied to `package` besides `internal` declarations. These attributed symbols are allowed in the bodies of `@inlinable public` or `@inlinable package` declarations (that are defined anywhere in the same package). Just as with `internal` symbols, the `package` declarations with `@usableFromInline` or `@inlinable` are stored in the public `.swiftinterface` for a module. -The existing `@usableFromInline` attribute can be applied to `package` symbols as well as `internal` symbols. This allows those symbols to be used from `@inlinable public` functions (that are defined anywhere in the same package) without having to make them `public`. The Swift frontend will include `@usableFromInline package` symbols in the ordinary `.swiftinterface` for a module, not its `.package.swiftinterface`. +Here's an example. ``` -@usableFromPackageInline func internalFuncA() {} +func internalFuncA() {} @usableFromInline func internalFuncB() {} -@usableFromInline package func packageFunc() {} + +package func packageFuncA() {} +@usableFromInline package func packageFuncB() {} + +public func publicFunc() {} @inlinable package func pkgUse() { - internalFuncA() // OK + internalFuncA() // Error internalFuncB() // OK - packageFunc() // OK + packageFuncA() // Error + packageFuncB() // OK + publicFunc() // OK } @inlinable public func publicUse() { - internalFuncA() // ERROR + internalFuncA() // Error internalFuncB() // OK - packageFunc() // OK + packageFuncA() // Error + packageFuncB() // OK + publicFunc() // OK } ``` @@ -266,6 +263,73 @@ Potential solutions include introducing new keywords for specific access combina Sometimes entire modules are meant to be private to the package that provides them. Allowing this to be expressed directly would allow these utility modules to be completely hidden outside of the package, avoiding unwanted dependencies on the existence of the module. It would also allow the build system to automatically namespace the module within the package, reducing the need for [explicit module aliases](https://github.com/apple/swift-evolution/blob/main/proposals/0339-module-aliasing-for-disambiguation.md) when utility modules of different packages share a name (such as `Utility`) or when multiple versions of a package need to be built into the same program. +### Package Boundary Customization + +1. Opt-out + +It is not uncommon for a package to contain a target that does not need to be in the same package boundary setting. For example, an example app might be added to the package to demonstrate use case of the library it intends to distribute; the example app acts as a client outside of the package and should not have access to the package symbols within the library. However, it is still part of the same package, so it is not guaranteed that it will be prevented from having such access. A potential solution is to introduce a new per-target parameter that would disallow access to package symbols. + +Here's an example using a new parameter `accessPublicOnly`. + +``` +.executableTarget(name: "ExampleApp", + dependencies: ["MainTarget"]), +.target(name: "Game", + dependencies: ["Engine"], + accessPublicOnly: true) +``` + +By default, `accessPublicOnly` per target is set to `false`, so all targets within the package can access package symbols. When set to `true`, only public APIs of the target are visible to its client, i.e. `ExampleApp` in this case. + +The above applies to a test target as well. If a target contains package APIs that need to be tested, it would be recommended to split it into a "shell" that contains public APIs and a target that contains package APIs to be tested with the parameter `accessPublicOnly` set to the default value `false`. This naturally eliminates the need to import the module with `@testable`. + +2. Sub-packages + +As a package becomes larger, it is inevitable for some targets to belong to a certain group within the package. Even though it logically makes sense to move them out into a separate package, they often remain in the same package due to the maintenance and versioning overhead. A potential solution is to allow defining a sub-package within a package. + +For example, sub-packages could be defined directly in the dependencies of the top level package in its manifest, like so. + +``` +let package = Package( + name: "gamePkg", + products: [ + .library(name: "Game", targets: ["Game"]), + ], + dependencies: [ + Package( + name: "A", + products: [ + .library(name: "CoreA", targets: ["CoreA"]), + ], + targets: [ + .target(name: "CoreA", dependencies: ["EngineA"]), + .target(name: "EngineA"), + ] + ), + Package( + name: "B", + products: [ + .library(name: "CoreB", targets: ["CoreB"]), + ], + targets: [ + .target(name: "CoreB", dependencies: ["EngineB"]), + .target(name: "EngineB"), + ] + ), + ], + targets: [ + .target(name: "Game", + dependencies: [ + .product(name: "CoreA", package: "A"), + .product(name: "CoreB", package: "B") + ]), + ], +) +``` +When building target `CoreA`, SwiftPM will pass `gamePkg.A` to `-package-name`; similarly, target `CoreB` will be built with `-package-name gamePkg.B`. The top level package ID is required to be unique (in case of SwiftPM, uniqueness is guaranteed via a registry), and appending a sub-package ID to it will ensure the uniqueness of each sub-package; if another top level package has the same ID as one of the sub-packages in this example, such as `A`, the `-package-name` input for the top level package `A` will be differentiated from the input `gamePkg.A` for the sub-package. + +With the methods described above, users should be able to customize the package boundary settings for a specific group of targets if they choose to do so. + ### Optimizations * A package can be treated as a resilience domain, even with library evolution enabled which makes modules resilient. The Swift frontend will assume that modules defined in the same package will always be rebuilt together and do not require a resilient ABI boundary between them. This removes the need for performance and code size overhead introduced by ABI-resilient code generation, and it also eliminates language requirements such as `@unknown default` for a non-`frozen enum`. From 3cc39f4923777e5d1f4de14632c7e086b2d92062 Mon Sep 17 00:00:00 2001 From: Boris Buegling Date: Thu, 23 Mar 2023 10:01:31 -0700 Subject: [PATCH 3049/4563] Add explanation of SwiftSyntax versioning --- proposals/NNNN-swiftpm-expression-macros.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/proposals/NNNN-swiftpm-expression-macros.md b/proposals/NNNN-swiftpm-expression-macros.md index 279efce981..10805f8d42 100644 --- a/proposals/NNNN-swiftpm-expression-macros.md +++ b/proposals/NNNN-swiftpm-expression-macros.md @@ -152,6 +152,20 @@ struct Font: ExpressibleByFontLiteral { let _: Font = #fontLiteral(name: "Comic Sans", size: 14, weight: .thin) ``` +SwiftSyntax's versioning scheme is based on Swift major versions (e.g. 509.0.0 for Swift 5.9). + +If a package depends on two macros using the `from` version dependency and minor versions of a macro use different versions of SwiftSyntax, users should automatically get a version that's compatible with all macros. For example consider the following where a package depends on both Macro 1 and Macro 2 using `from: "1.0.0"` + +``` +Macro 1 SwiftSyntax Macro 2 + +1.0 --------------> 509.0.0 <-------------- 1.0 + 509.0.1 <-------------- 1.1 + 510.0.0 <-------------- 1.2 +``` + +In this case, SwiftPM would choose version 1.0 for Macro 1, version 1.1 for Macro 2 and end up with version 509.0.1 for SwiftSyntax. We're going to monitor how the versioning story plays out in practice and may take further action in SwiftSyntax or SwiftPM's dependency resolution if the concrete need arises. + ## Impact on existing packages From 88426ee063ac1fc512dd946feb6fbdf9631576e8 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Thu, 23 Mar 2023 11:56:13 -0700 Subject: [PATCH 3050/4563] Update sub-packages section --- proposals/0386-package-access-modifier.md | 45 +++++++++++++++-------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index 5c01d20320..251bd151c1 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -285,9 +285,11 @@ The above applies to a test target as well. If a target contains package APIs t 2. Sub-packages -As a package becomes larger, it is inevitable for some targets to belong to a certain group within the package. Even though it logically makes sense to move them out into a separate package, they often remain in the same package due to the maintenance and versioning overhead. A potential solution is to allow defining a sub-package within a package. +As a package becomes larger, it is inevitable for some targets to belong to a certain group within the package. Even though it logically makes sense to move them out into a separate package, they often remain in the same package due to the maintenance and versioning overhead. Currently each local package needs to be at the root directory regardless of the package dependencies, but we could allow a dependency package to reside in a sub-directory, which would make it easier for an existing package to convert into a structured top-level package with sub-packages. -For example, sub-packages could be defined directly in the dependencies of the top level package in its manifest, like so. +The top-level package and its 2 sub-packages would then be updated as follows. + +Top-level package in directory `gamePkg`: ``` let package = Package( @@ -296,7 +298,22 @@ let package = Package( .library(name: "Game", targets: ["Game"]), ], dependencies: [ - Package( + .package("A"), + .package("B") + ], + targets: [ + .target(name: "Game", + dependencies: [ + .product(name: "CoreA", package: "A"), + .product(name: "CoreB", package: "B") + ]), + ], +) +``` + +Sub-package `A` in directory 'gamePkg/A': +``` +let package = Package( name: "A", products: [ .library(name: "CoreA", targets: ["CoreA"]), @@ -305,8 +322,13 @@ let package = Package( .target(name: "CoreA", dependencies: ["EngineA"]), .target(name: "EngineA"), ] - ), - Package( + ) +``` + +Sub-package `B` in directory `gamePkg/B`: + +``` +let package = Package( name: "B", products: [ .library(name: "CoreB", targets: ["CoreB"]), @@ -316,17 +338,10 @@ let package = Package( .target(name: "EngineB"), ] ), - ], - targets: [ - .target(name: "Game", - dependencies: [ - .product(name: "CoreA", package: "A"), - .product(name: "CoreB", package: "B") - ]), - ], -) + ``` -When building target `CoreA`, SwiftPM will pass `gamePkg.A` to `-package-name`; similarly, target `CoreB` will be built with `-package-name gamePkg.B`. The top level package ID is required to be unique (in case of SwiftPM, uniqueness is guaranteed via a registry), and appending a sub-package ID to it will ensure the uniqueness of each sub-package; if another top level package has the same ID as one of the sub-packages in this example, such as `A`, the `-package-name` input for the top level package `A` will be differentiated from the input `gamePkg.A` for the sub-package. + +When building target `CoreA` in sub-package `A`, SwiftPM will pass `gamePkg.A` to `-package-name`; similarly, target `CoreB` will be built with `-package-name gamePkg.B`. The top level package ID is required to be unique (in case of SwiftPM, uniqueness is guaranteed via a registry), and appending a sub-package ID to it will ensure the uniqueness of each sub-package; if another top level package has the same ID as one of the sub-packages in this example, such as `A`, the `-package-name` input for the top level package `A` will be differentiated from the input `gamePkg.A` for the sub-package. With the methods described above, users should be able to customize the package boundary settings for a specific group of targets if they choose to do so. From 6ca8671d89fa9fe36c12abf0ccb5f5d28852382a Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Thu, 23 Mar 2023 15:34:10 -0700 Subject: [PATCH 3051/4563] Add prev revision Update package name string section Add group to allow opt-out / subpackages --- proposals/0386-package-access-modifier.md | 108 +++++----------------- 1 file changed, 22 insertions(+), 86 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index 251bd151c1..d45cf69dd4 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -6,6 +6,7 @@ * Status: **Active Review (January 26th...Feburary 8th, 2023** * Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/62700), [apple/swift#62704](https://github.com/apple/swift/pull/62704), [apple/swift#62652](https://github.com/apple/swift/pull/62652), [apple/swift#62652](https://github.com/apple/swift/pull/62652) * Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) ([review](https://forums.swift.org/t/se-0386-package-access-modifier/62808)) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/28fd2fb9b7258117f912cec5e5f7eb178520fbf2/proposals/NNNN-package-access-modifier.md) ## Introduction @@ -117,8 +118,9 @@ engine.run() // Error: cannot find `run` in scope ### Package Names -Two modules belong to the same package if they were built with the same package name. A package name must be a unique string which starts with a letter or `_`, followed by characters allowed in [this c99 extended identifier definition](https://github.com/apple/swift-tools-support-core/blob/435a2708a6e486d69ea7d7aaa3f4ad243bc3b408/Sources/TSCUtility/StringMangling.swift#L43), including alphanumeric characters, a hypen, and a dot; any character not allowed in the definition is transcoded to a `_`. The string is case-insensitive, and it is passed to the Swift frontend via a new flag `-package-name`. -Here's an example of how a package name is passed to a commandline invocation. +Two modules belong to the same package if they were built with the same package name. A package name must be (1) a unique identity string and (2) a valid UTF-8 string starting with a letter or `_`; for example, a string such as "Προϊόν" (translation "product") must be converted to "Προι_ο_ν" to be processed. A common package name includes alphanumeric characters (starting with a letter), `-`, `_`, and `.`. + +A new flag `-package-name` is passed down to a commandline invocation, as follows. ``` swiftc -module-name Engine -package-name gamePkg ... @@ -130,9 +132,25 @@ When building the `Engine` module, the package name `gamePkg` is recorded in the If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. -The Swift Package Manager already has a concept of a package identity string for every package, as specified by [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md). This string is verified to be unique via a registry, and it always works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. +The Swift Package Manager already has a concept of a package identity string for every package. This string is verified to be unique, and it already works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. + +If a target needs to be excluded from the package boundary or needes to be part of a subgroup within the package boundary, it can be set in a target setting, with a new parameter `group` in the manifest, like so: + +``` + .target(name: "Game", dependencies: ["Engine"], group: .excluded) +``` + +The `group` setting is mapped to the following type: + +``` +enum Group { + case package + case excluded + case named(String) +} +``` -Scenarios where a target needs to be excluded from the package or project boundary or needes to be part of a subgroup within the boundary are discussed in the Future Directions. +The default value is `.package`, and the target is built with `-package-name [package_id]`. If set to `.excluded`, no `-package-name` is passed when building the target, thus the target has no access to any package symbols; it essentially acts as if it's a client outside of the package. This would be useful for an example app or a black-box testing target in the same package. If set to `.named([group_id])`, `-package-name [package_id].[group_id]` is passed, and the target can only access package symbols from other targets within the same group in the package. This essentially allows "sub-packages" within the package. ### Package Symbols Distribution @@ -263,88 +281,6 @@ Potential solutions include introducing new keywords for specific access combina Sometimes entire modules are meant to be private to the package that provides them. Allowing this to be expressed directly would allow these utility modules to be completely hidden outside of the package, avoiding unwanted dependencies on the existence of the module. It would also allow the build system to automatically namespace the module within the package, reducing the need for [explicit module aliases](https://github.com/apple/swift-evolution/blob/main/proposals/0339-module-aliasing-for-disambiguation.md) when utility modules of different packages share a name (such as `Utility`) or when multiple versions of a package need to be built into the same program. -### Package Boundary Customization - -1. Opt-out - -It is not uncommon for a package to contain a target that does not need to be in the same package boundary setting. For example, an example app might be added to the package to demonstrate use case of the library it intends to distribute; the example app acts as a client outside of the package and should not have access to the package symbols within the library. However, it is still part of the same package, so it is not guaranteed that it will be prevented from having such access. A potential solution is to introduce a new per-target parameter that would disallow access to package symbols. - -Here's an example using a new parameter `accessPublicOnly`. - -``` -.executableTarget(name: "ExampleApp", - dependencies: ["MainTarget"]), -.target(name: "Game", - dependencies: ["Engine"], - accessPublicOnly: true) -``` - -By default, `accessPublicOnly` per target is set to `false`, so all targets within the package can access package symbols. When set to `true`, only public APIs of the target are visible to its client, i.e. `ExampleApp` in this case. - -The above applies to a test target as well. If a target contains package APIs that need to be tested, it would be recommended to split it into a "shell" that contains public APIs and a target that contains package APIs to be tested with the parameter `accessPublicOnly` set to the default value `false`. This naturally eliminates the need to import the module with `@testable`. - -2. Sub-packages - -As a package becomes larger, it is inevitable for some targets to belong to a certain group within the package. Even though it logically makes sense to move them out into a separate package, they often remain in the same package due to the maintenance and versioning overhead. Currently each local package needs to be at the root directory regardless of the package dependencies, but we could allow a dependency package to reside in a sub-directory, which would make it easier for an existing package to convert into a structured top-level package with sub-packages. - -The top-level package and its 2 sub-packages would then be updated as follows. - -Top-level package in directory `gamePkg`: - -``` -let package = Package( - name: "gamePkg", - products: [ - .library(name: "Game", targets: ["Game"]), - ], - dependencies: [ - .package("A"), - .package("B") - ], - targets: [ - .target(name: "Game", - dependencies: [ - .product(name: "CoreA", package: "A"), - .product(name: "CoreB", package: "B") - ]), - ], -) -``` - -Sub-package `A` in directory 'gamePkg/A': -``` -let package = Package( - name: "A", - products: [ - .library(name: "CoreA", targets: ["CoreA"]), - ], - targets: [ - .target(name: "CoreA", dependencies: ["EngineA"]), - .target(name: "EngineA"), - ] - ) -``` - -Sub-package `B` in directory `gamePkg/B`: - -``` -let package = Package( - name: "B", - products: [ - .library(name: "CoreB", targets: ["CoreB"]), - ], - targets: [ - .target(name: "CoreB", dependencies: ["EngineB"]), - .target(name: "EngineB"), - ] - ), - -``` - -When building target `CoreA` in sub-package `A`, SwiftPM will pass `gamePkg.A` to `-package-name`; similarly, target `CoreB` will be built with `-package-name gamePkg.B`. The top level package ID is required to be unique (in case of SwiftPM, uniqueness is guaranteed via a registry), and appending a sub-package ID to it will ensure the uniqueness of each sub-package; if another top level package has the same ID as one of the sub-packages in this example, such as `A`, the `-package-name` input for the top level package `A` will be differentiated from the input `gamePkg.A` for the sub-package. - -With the methods described above, users should be able to customize the package boundary settings for a specific group of targets if they choose to do so. - ### Optimizations * A package can be treated as a resilience domain, even with library evolution enabled which makes modules resilient. The Swift frontend will assume that modules defined in the same package will always be rebuilt together and do not require a resilient ABI boundary between them. This removes the need for performance and code size overhead introduced by ABI-resilient code generation, and it also eliminates language requirements such as `@unknown default` for a non-`frozen enum`. From 6c08c5f1ee48bd2d1f04d5876d7fe65426db7179 Mon Sep 17 00:00:00 2001 From: Jared Grubb Date: Wed, 15 Mar 2023 23:54:13 -0700 Subject: [PATCH 3052/4563] Update 0392-custom-actor-executor: fix code typo I believe the executor needs to be passed into these functions? --- proposals/0392-custom-actor-executors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index e6cf904cad..a68dc8634c 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -318,7 +318,7 @@ extension Job { /// /// This operation consumes the job. public consuming func runSynchronously(on executor: UnownedSerialExecutor) { - _swiftJobRun(UnownedJob(job), self) + _swiftJobRun(UnownedJob(job), executor) } } @@ -327,7 +327,7 @@ extension UnownedJob { /// /// A job can only be run *once*. Accessing the job after it has been run is undefined behavior. public func runSynchronously(on executor: UnownedSerialExecutor) { - _swiftJobRun(job, self) + _swiftJobRun(job, executor) } } ``` From 3acd7d99ace455a3656ac063885ff700860ddbab Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Fri, 24 Mar 2023 16:32:31 -0700 Subject: [PATCH 3053/4563] feedback --- proposals/0386-package-access-modifier.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index d45cf69dd4..df4c59ded2 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -118,7 +118,7 @@ engine.run() // Error: cannot find `run` in scope ### Package Names -Two modules belong to the same package if they were built with the same package name. A package name must be (1) a unique identity string and (2) a valid UTF-8 string starting with a letter or `_`; for example, a string such as "Προϊόν" (translation "product") must be converted to "Προι_ο_ν" to be processed. A common package name includes alphanumeric characters (starting with a letter), `-`, `_`, and `.`. +Two modules belong to the same package if they were built with the same package name. A package name must be (1) a unique identity string and (2) a valid UTF-8 string starting with a letter or `_`. A commonly accepted name includes alphanumeric characters (starting with a letter), `-`, `_`, and `.`. A build system may define its own rule to validate the input string; for example, SwiftPM checks the input against a rule defined [here](https://github.com/apple/swift-tools-support-core/blob/435a2708a6e486d69ea7d7aaa3f4ad243bc3b408/Sources/TSCUtility/StringMangling.swift#L43) and transcodes any undefined character to a `_`. A new flag `-package-name` is passed down to a commandline invocation, as follows. @@ -150,7 +150,7 @@ enum Group { } ``` -The default value is `.package`, and the target is built with `-package-name [package_id]`. If set to `.excluded`, no `-package-name` is passed when building the target, thus the target has no access to any package symbols; it essentially acts as if it's a client outside of the package. This would be useful for an example app or a black-box testing target in the same package. If set to `.named([group_id])`, `-package-name [package_id].[group_id]` is passed, and the target can only access package symbols from other targets within the same group in the package. This essentially allows "sub-packages" within the package. +The default value is `.package`, and the target is built with `-package-name PACKAGE_ID`. If set to `.excluded`, no `-package-name` is passed when building the target, thus the target has no access to any package symbols; it essentially acts as if it's a client outside of the package. This would be useful for an example app or a black-box testing target in the same package. If set to `.named(GROUP_ID)`, `-package-name PACKAGE_ID.GROUP_ID` is passed, and the target can only access package symbols from other targets within the same group in the package. This essentially allows "sub-packages" within the package. ### Package Symbols Distribution From b6ca38b9eee79650dce925e7aa8443a6a9e5e6ea Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sun, 26 Mar 2023 12:13:25 -0700 Subject: [PATCH 3054/4563] [SE-0393] Minor proposal clarifications. (#1994) --- proposals/0393-parameter-packs.md | 35 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/proposals/0393-parameter-packs.md b/proposals/0393-parameter-packs.md index 31ea02da8c..fcb937c904 100644 --- a/proposals/0393-parameter-packs.md +++ b/proposals/0393-parameter-packs.md @@ -1,4 +1,4 @@ -# SE-0393: Value and Type Parameter Packs +# Value and Type Parameter Packs * Proposal: [SE-0393](0393-parameter-packs.md) * Authors: [Holly Borla](https://github.com/hborla), [John McCall](https://github.com/rjmccall), [Slava Pestov](https://github.com/slavapestov) @@ -92,9 +92,11 @@ Parameter packs are the core concept that facilitates abstracting over a variabl func zip(...) ``` -A parameter pack itself is not a first-class value or type, but the elements of a parameter pack can be used anywhere that naturally accepts a comma-separated list of values or types using _pack expansions_. A pack expansion unpacks the elements of a pack into a comma-separated list, and elements can be appended to either side of a pack expansion by writing more values in the comma-separated list. +A parameter pack itself is not a first-class value or type, but the elements of a parameter pack can be used anywhere that naturally accepts a list of values or types using _pack expansions_, including top-level expressions. -A pack expansion consists of the `repeat` keyword followed by a type or an expression. The type or expression that `repeat` is applied to is called the _repetition pattern_. The repetition pattern must contain pack references. Similarly, pack references can only appear inside repetition patterns and generic requirements: +A pack expansion consists of the `repeat` keyword followed by a type or an expression. The type or expression that `repeat` is applied to is called the _repetition pattern_. The repetition pattern must contain at least one pack reference, spelled with the `each` keyword. At runtime, the pattern is repeated for each element in the substituted pack, and the resulting types or values are _expanded_ into the list provided by the surrounding context. + +Similarly, pack references can only appear inside repetition patterns and generic requirements: ```swift func zip(_ sequence: repeat each S) where each S: Sequence @@ -105,12 +107,11 @@ Given a concrete pack substitution, the pattern is repeated for each element in Here are the key concepts introduced by this proposal: - Under the new model, all existing types and values in the language become _scalar types_ and _scalar values_. -- A _type pack_ is a new kind of type which represents a list of scalar types. Type packs do not have syntax in the surface language, but we will write them as `{T1, ..., Tn}` where each `Ti` is a scalar type. Type packs cannot be nested; type substitution is defined to always flatten type packs. -- A _type parameter pack_ is a type parameter which can abstract over a type pack. These are declared in a generic parameter list using the syntax `each T`, and referenced with `each T`. -- A _pack expansion type_ is a new kind of scalar type which flattens a set of type packs in a context where a comma-separated list of types may appear. The syntax for a pack expansion type is `repeat each P`, where `P` is a type containing one or more type parameter packs. +- A _type pack_ is a new kind of type-level entity which represents a list of scalar types. Type packs do not have syntax in the surface language, but we will write them as `{T1, ..., Tn}` where each `Ti` is a scalar type. Type packs cannot be nested; type substitution is defined to always flatten type packs. +- A _type parameter pack_ is a list of zero or more scalar type parameters. These are declared in a generic parameter list using the syntax `each T`, and referenced with `each T`. - A _value pack_ is a list of scalar values. The type of a value pack is a type pack, where each element of the type pack is the scalar type of the corresponding scalar value. Value packs do not have syntax in the surface language, but we will write them as `{x1, ..., xn}` where each `xi` is a scalar value. Value packs cannot be nested; evaluation is always defined to flatten value packs. -- A _value parameter pack_ is a function parameter or local variable declared with a pack expansion type. -- A _pack expansion expression_ is a new kind of expression whose type is a pack expansion type. Written as `repeat each expr`, where `expr` is an expression referencing one or more value parameter packs. +- A _value parameter pack_ is a list of zero or more scalar function or macro parameters. +- A _pack expansion_ is a new kind of type-level and value-level construct that expands a type or value pack into a list of types or values, respectively. Written as `repeat P`, where `P` is the _repetition pattern_ that captures at least one type parameter pack (spelled with the `each` keyword). At runtime, the pattern is repeated for each element in the substituted pack. The following example demonstrates these concepts together: @@ -168,7 +169,7 @@ A pack expansion type, written as `repeat P`, has a *pattern type* `P` and a non * The type of a parameter in a function type, e.g. `(repeat each T) -> Bool` * The type of an unlabeled element in a tuple type, e.g. `(repeat each T)` -Because pack expansions can only appear in positions that accept a comma-separated list, pack expansion patterns are naturally delimited by either a comma or the end-of-list delimiter, e.g. `)` for call argument lists or `>` for generic argument lists. +Because pack expansions can only appear in positions that accept a list of types or values, pack expansion patterns are naturally delimited by a comma, the next statement in top-level code, or an end-of-list delimiter, e.g. `)` for call argument lists or `>` for generic argument lists. The restriction where only unlabeled elements of a tuple type may have a pack expansion type is motivated by ergonomics. If you could write `(t: repeat each T)`, then after a substitution `T := {Int, String}`, the substituted type would be `(t: Int, String)`. This would be strange, because projecting the member `t` would only produce the first element. When an unlabeled element has a pack expansion type, like `(repeat each T)`, then after the above substitution you would get `(Int, String)`. You can still write `0` to project the first element, but this is less surprising to the Swift programmer. @@ -466,20 +467,20 @@ The type annotation of `tup` contains a pack expansion type `repeat (each T, eac #### Restrictions on same-shape requirements -While type packs cannot be written directly, a requirement where both sides are concrete types is desugared using the type matching algorithm, therefore it will be possible to write down a requirement that constrains a type parameter pack to a concrete type pack, unless some kind of restriction is imposed: +Type packs cannot be written directly, but requirements involving pack expansions where both sides are concrete types are desugared using the type matching algorithm. This means it is possible to write down a requirement that constrains a type parameter pack to a concrete type pack, unless some kind of restriction is imposed: ```swift -func append(_: repeat each S, _: repeat each T) where (each S).Element == (Int, String) {} +func constrain(_: repeat each S) where (repeat (each S).Element) == (Int, String) {} ``` -Furthermore, since the same-type requirement implies a same-shape requirement, we've implicitly constrained `T` to having a length of 2 elements, without knowing what those elements are. +Furthermore, since the same-type requirement implies a same-shape requirement, we've implicitly constrained `S` to having a length of 2 elements, without knowing what those elements are. This introduces theoretical complications. In the general case, same-type requirements on type parameter packs allows encoding arbitrary systems of integer linear equations: ```swift // shape(Q) = 2 * shape(R) + 1 // shape(Q) = shape(S) + 2 -func solve(q: repeat each Q, r: repeat each R, s: repeat eachS) +func solve(q: repeat each Q, r: repeat each R, s: repeat each S) where (repeat each Q) == (Int, repeat each R, repeat each R), (repeat each Q) == (repeat each S, String, Bool) { } ``` @@ -500,7 +501,7 @@ This aspect of the language can evolve in a forward-compatible manner. Over time ### Value parameter packs -A _value parameter pack_ represents zero or more function arguments, and it is declared with a function parameter that has a pack expansion type. In the following declaration, the function parameter `value` is a value parameter pack that receives a _value pack_ consisting of zero or more argument values from the call site: +A _value parameter pack_ represents zero or more function or macro parameters, and it is declared with a function parameter that has a pack expansion type. In the following declaration, the function parameter `value` is a value parameter pack that receives a _value pack_ consisting of zero or more argument values from the call site: ```swift func tuplify(_ value: repeat each T) -> (repeat each T) @@ -510,7 +511,7 @@ _ = tuplify(1) // T := {Int}, value := {1} _ = tuplify(1, "hello", [Foo()]) // T := {Int, String, [Foo]}, value := {1, "hello", [Foo()]} ``` -**Syntactic validity:** A value parameter pack can only be referenced from a pack expansion expression. A pack expansion expression is written as `repeat expr`, where `expr` is an expression containing one or more value parameter packs or type parameter packs spelled with the `each` keyword. Pack expansion expressions can appear in any position that naturally accepts a comma-separated list of expressions. This includes the following: +**Syntactic validity:** A value parameter pack can only be referenced from a pack expansion expression. A pack expansion expression is written as `repeat expr`, where `expr` is an expression containing one or more value parameter packs or type parameter packs spelled with the `each` keyword. Pack expansion expressions can appear in any position that naturally accepts a list of expressions, including comma-separated lists and top-level expressions. This includes the following: * Call arguments, e.g. `generic(repeat each value)` * Subscript arguments, e.g. `subscriptable[repeat each index]` @@ -519,8 +520,6 @@ _ = tuplify(1, "hello", [Foo()]) // T := {Int, String, [Foo]}, value := {1, "hel Pack expansion expressions can also appear in an expression statement at the top level of a brace statement. In this case, the semantics are the same as scalar expression statements; the expression is evaluated for its side effect and the results discarded. -Note that pack expansion expressions can also reference _type_ pack parameters, as metatypes. - **Capture:** A pack expansion expression _captures_ a value (or type) pack parameter the value (or type) pack parameter appears as a sub-expression without any intervening pack expansion expression. Furthermore, a pack expansion expression also captures all type parameter packs captured by the types of its captured value parameter packs. @@ -834,4 +833,4 @@ extension (repeat each T): Equatable { ## Acknowledgments -Thank you to Robert Widmann for exploring the design space of modeling packs as tuples, and to everyone who participated in earlier design discussions about variadic generics in Swift. +Thank you to Robert Widmann for exploring the design space of modeling packs as tuples, and to everyone who participated in earlier design discussions about variadic generics in Swift. Thank you to the many engineers who contributed to the implementation, including Sophia Poirier, Pavel Yaskevich, Nate Chandler, Hamish Knight, and Adrian Prantl. From 5b8075fb926635eb692fd6aa96d142e6da79a4ae Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 28 Mar 2023 16:00:05 -0700 Subject: [PATCH 3055/4563] Update proposals/0386-package-access-modifier.md Co-authored-by: John McCall --- proposals/0386-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index df4c59ded2..feab21d154 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -118,7 +118,7 @@ engine.run() // Error: cannot find `run` in scope ### Package Names -Two modules belong to the same package if they were built with the same package name. A package name must be (1) a unique identity string and (2) a valid UTF-8 string starting with a letter or `_`. A commonly accepted name includes alphanumeric characters (starting with a letter), `-`, `_`, and `.`. A build system may define its own rule to validate the input string; for example, SwiftPM checks the input against a rule defined [here](https://github.com/apple/swift-tools-support-core/blob/435a2708a6e486d69ea7d7aaa3f4ad243bc3b408/Sources/TSCUtility/StringMangling.swift#L43) and transcodes any undefined character to a `_`. +Swift as a language leaves it up to the build system to define the boundaries of a package. The compiler considers two modules to belong to the same package if they were built with the same package name, which is just a Unicode string. The package name is not exposed in the source language, so its exact contents are not significant as long as it is unique to a "package". A new flag `-package-name` is passed down to a commandline invocation, as follows. From c25c40c1be295469f12e8c5f8ee9ecfaee6c9206 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 28 Mar 2023 16:00:13 -0700 Subject: [PATCH 3056/4563] Update proposals/0386-package-access-modifier.md Co-authored-by: John McCall --- proposals/0386-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index feab21d154..515cba212a 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -130,7 +130,7 @@ swiftc -module-name App -package-name appPkg ... When building the `Engine` module, the package name `gamePkg` is recorded in the built interface to the module. When building `Game`, its package name `gamePkg` is compared with the package name recorded in `Engine`'s built interface; since they match, `Game` is allowed to access `Engine`'s `package` declarations. When building `App`, its package name `appPkg` is different from `gamePkg`, so it is not allowed to access `package` symbols in either `Engine` or `Game`, which is what we want. -If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. +If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. Modules built without a package name are never considered to be in the same package as any other module. The Swift Package Manager already has a concept of a package identity string for every package. This string is verified to be unique, and it already works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. From b421bbf2e25efb135e2a6707e46789316bb0a4fc Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 28 Mar 2023 16:00:23 -0700 Subject: [PATCH 3057/4563] Update proposals/0386-package-access-modifier.md Co-authored-by: John McCall --- proposals/0386-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index 515cba212a..111bc3fc95 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -132,7 +132,7 @@ When building the `Engine` module, the package name `gamePkg` is recorded in the If `-package-name` is not given, the `package` access modifier is disallowed. Swift code that does not use `package` access will continue to build without needing to pass in `-package-name`. Modules built without a package name are never considered to be in the same package as any other module. -The Swift Package Manager already has a concept of a package identity string for every package. This string is verified to be unique, and it already works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. +The build system should make a best effort to ensure that package names are unique. The Swift Package Manager already has a concept of a package identity string for every package. This string is verified to be unique, and it already works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. If a target needs to be excluded from the package boundary or needes to be part of a subgroup within the package boundary, it can be set in a target setting, with a new parameter `group` in the manifest, like so: From 04ccec3710846907daad2e9548dd15867acc34ca Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 29 Mar 2023 08:02:39 +0900 Subject: [PATCH 3058/4563] Mark 0381 as implemented (#1995) --- proposals/0381-task-group-discard-results.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0381-task-group-discard-results.md b/proposals/0381-task-group-discard-results.md index 2b1c0f8fc3..7c4a2c0d3b 100644 --- a/proposals/0381-task-group-discard-results.md +++ b/proposals/0381-task-group-discard-results.md @@ -3,7 +3,7 @@ * Proposal: [SE-0381](0381-task-group-discard-results.md) * Authors: [Cory Benfield](https://github.com/Lukasa), [Konrad Malawski](https://github.com/ktoso) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: https://github.com/apple/swift/pull/62361 * Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0381-discardingtaskgroups/62615) From 7f2cf1067def28acf005f74809c4155868eb9314 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 28 Mar 2023 16:38:53 -0700 Subject: [PATCH 3059/4563] Move @usableFromPackageInline to Alternatives / Update .group setting --- proposals/0386-package-access-modifier.md | 24 +++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index 111bc3fc95..aadea88cce 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -146,11 +146,10 @@ The `group` setting is mapped to the following type: enum Group { case package case excluded - case named(String) } ``` -The default value is `.package`, and the target is built with `-package-name PACKAGE_ID`. If set to `.excluded`, no `-package-name` is passed when building the target, thus the target has no access to any package symbols; it essentially acts as if it's a client outside of the package. This would be useful for an example app or a black-box testing target in the same package. If set to `.named(GROUP_ID)`, `-package-name PACKAGE_ID.GROUP_ID` is passed, and the target can only access package symbols from other targets within the same group in the package. This essentially allows "sub-packages" within the package. +The default value is `.package`, and the target is built with `-package-name PACKAGE_ID`. If set to `.excluded`, no `-package-name` is passed when building the target, thus the target has no access to any package symbols; it essentially acts as if it's a client outside of the package. This would be useful for an example app or a black-box testing target in the same package. ### Package Symbols Distribution @@ -281,6 +280,23 @@ Potential solutions include introducing new keywords for specific access combina Sometimes entire modules are meant to be private to the package that provides them. Allowing this to be expressed directly would allow these utility modules to be completely hidden outside of the package, avoiding unwanted dependencies on the existence of the module. It would also allow the build system to automatically namespace the module within the package, reducing the need for [explicit module aliases](https://github.com/apple/swift-evolution/blob/main/proposals/0339-module-aliasing-for-disambiguation.md) when utility modules of different packages share a name (such as `Utility`) or when multiple versions of a package need to be built into the same program. +### Package Boundary Customization + +The `group` setting per target in Swift Package Manager allows a target to opt out of a package boundary, but if there is a need for creating multi-groups within a package, the setting could be expanded to allow that by introducing a `.named(GROUP_ID)` option, where `GROUP_ID` is a unique identifier for a specific group the target belongs to. + +``` +enum Group { + case package + case excluded + case named(String) +} +``` +For example, the new option could be specified in a target setting in a manifest like so: + +``` + .target(name: "Game", dependencies: ["Engine"], group: .named("Core")) +``` + ### Optimizations * A package can be treated as a resilience domain, even with library evolution enabled which makes modules resilient. The Swift frontend will assume that modules defined in the same package will always be rebuilt together and do not require a resilient ABI boundary between them. This removes the need for performance and code size overhead introduced by ABI-resilient code generation, and it also eliminates language requirements such as `@unknown default` for a non-`frozen enum`. @@ -329,6 +345,10 @@ Instead of adding a new package access level above modules, we could allow modul * In a few cases, the ABI and source impact above would be desirable. For example, many packages contain internal Utility modules; if these were declared as submodules, they would naturally be namespaced to the containing package, eliminating spurious collisions. However, such modules are generally not meant to be usable outside of the package at all. It is a reasonable future direction to allow whole modules to be made package-private, which would also make it reasonable to automatically namespace them. +### `@usableFromPackageInline` + +A new attribute `@usableFromPackageInline` was considered, which would allow an internal declaration to be used in the body of an `@inlinable package` declaration, but not in `@inlinable public`. It was however concluded unnecessary as inlining for package declarations could be enabled by default via optimization in the future (since they are in the same package boundary). + ## Acknowledgments Doug Gregor, Becca Royal-Gordon, Allan Shortlidge, Artem Chikin, and Xi Ge provided helpful feedback and analysis as well as code reviews on the implementation. From 4a3a11b18037526cf8d83a9d10b22b94890727e8 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 30 Mar 2023 15:29:24 -0400 Subject: [PATCH 3060/4563] Put SE-0386 back into review --- proposals/0386-package-access-modifier.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index aadea88cce..40a2656e6a 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -3,9 +3,9 @@ * Proposal: [SE-0386](0386-package-access-modifier.md) * Authors: [Ellie Shin](https://github.com/elsh), [Alexis Laferriere](https://github.com/xymus) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (January 26th...Feburary 8th, 2023** +* Status: **Active Review (March 31st...April 10th, 2023** * Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/62700), [apple/swift#62704](https://github.com/apple/swift/pull/62704), [apple/swift#62652](https://github.com/apple/swift/pull/62652), [apple/swift#62652](https://github.com/apple/swift/pull/62652) -* Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) ([review](https://forums.swift.org/t/se-0386-package-access-modifier/62808)) +* Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) ([first review](https://forums.swift.org/t/se-0386-package-access-modifier/62808)) ([second review](https://forums.swift.org/t/second-review-se-0386-package-access-modifier/64086)) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/28fd2fb9b7258117f912cec5e5f7eb178520fbf2/proposals/NNNN-package-access-modifier.md) ## Introduction From 419d0004a2237592de64211d2ef8b4c8a49e1dc4 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Thu, 30 Mar 2023 13:57:28 -0700 Subject: [PATCH 3061/4563] Update 0374-clock-sleep-for.md --- proposals/0374-clock-sleep-for.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0374-clock-sleep-for.md b/proposals/0374-clock-sleep-for.md index 9bd6d1fc79..f9653cc4e0 100644 --- a/proposals/0374-clock-sleep-for.md +++ b/proposals/0374-clock-sleep-for.md @@ -3,7 +3,7 @@ * Proposal: [SE-0374](0374-clock-sleep-for.md) * Authors: [Brandon Williams](https://github.com/mbrandonw), [Stephen Celis](https://github.com/stephencelis) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Implemented (Swift 5.8)** +* Status: **Implemented (Swift 5.9)** * Implementation: [apple/swift#61222](https://github.com/apple/swift/pull/61222) * Review: ([pitch](https://forums.swift.org/t/pitch-clock-sleep-for/60376)) ([review](https://forums.swift.org/t/se-0374-add-sleep-for-to-clock/60787)) ([acceptance](https://forums.swift.org/t/accepted-se-0374-add-sleep-for-to-clock/62148)) From a7942ebc63da3f0da6fc54eac25d5d4f49125a96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20=C5=A0r=C5=AFtek?= <35694712+michalsrutek@users.noreply.github.com> Date: Fri, 31 Mar 2023 12:21:11 +0200 Subject: [PATCH 3062/4563] Add a link for released Swift 5.8 (#1999) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b800f3b11..c61f61440d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This repository tracks the ongoing evolution of the Swift programming language, | Version | Announced | Released | | :-------- | :----------------------------------------------------------------------- | :----------------------------------------------------------- | | Swift 5.9 | [2023-03-06](https://forums.swift.org/t/swift-5-9-release-process/63557) | -| Swift 5.8 | [2022-11-19](https://forums.swift.org/t/swift-5-8-release-process/61540) | +| Swift 5.8 | [2022-11-19](https://forums.swift.org/t/swift-5-8-release-process/61540) | [2023-03-30](https://www.swift.org/blog/swift-5.8-released/) | | Swift 5.7 | [2022-03-29](https://forums.swift.org/t/swift-5-7-release-process/56316) | [2022-09-12](https://www.swift.org/blog/swift-5.7-released/) | | Swift 5.6 | [2021-11-10](https://forums.swift.org/t/swift-5-6-release-process/53412) | [2022-03-14](https://www.swift.org/blog/swift-5.6-released/) | | Swift 5.5 | [2021-03-12](https://forums.swift.org/t/swift-5-5-release-process/45644) | [2021-09-20](https://www.swift.org/blog/swift-5.5-released/) | From 9788a8d74a72e9f2062118dcdec32791b683005f Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 3 Apr 2023 14:02:39 +0900 Subject: [PATCH 3063/4563] update SE-0392 according to review feedback --- proposals/0392-custom-actor-executors.md | 256 +++++++++++++++-------- 1 file changed, 170 insertions(+), 86 deletions(-) diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index a68dc8634c..08153aff65 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -8,6 +8,10 @@ - Previous threads: - Original pitch thread from around Swift 5.5: [Support custom executors in Swift Concurrency](https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425) - Original "assume..." proposal which was subsumed into this proposal, as it relates closely to asserting on executors: [Pitch: Unsafe Assume on MainActor](https://forums.swift.org/t/pitch-unsafe-assume-on-mainactor/63074/) +- Revisions: + - move assert/precondition/assume APIs to extensions on actor types, e.g. `Actor/assertIsolated`, `DistributedActor/preconditionIsolated`, `MainActor/assumeIsolated { ... }` + - Distributed actor executor customization point is optional, and `nil` by default for remote actor references + ## Table of Contents @@ -81,7 +85,7 @@ final class SpecificThreadExecutor: SerialExecutor { func enqueue(_ job: consuming Job) { let unownedJob = UnownedJob(job) // in order to escape it to the run{} closure someThread.run { - job.runSynchronously(on: self) + unownedJob.runSynchronously(on: self) } } @@ -116,10 +120,10 @@ Asserting the apropriate executor is used in a synchronous piece of code looks l ````swift func synchronousButNeedsMainActorContext() { // check if we're executing on the maina actor context (or crash if we're not) - preconditionOnActorExecutor(MainActor.shared) + MainActor.preconditionIsolated() // same as precondition, however only in DEBUG builds - assertOnActorExecutor(MainActor.shared) + MainActor.assertIsolated() } ```` @@ -129,7 +133,7 @@ Furthermore, we also offer a new API to safely "assume" an actor's execution con @MainActor func example() {} func alwaysOnMainActor() /* must be synchronous! */ { - assumeOnMainActorExecutor { // will crash if NOT invoked from the MainActor's executor + MainActor.assumeIsolated { // will crash if NOT invoked from the MainActor's executor example() // ok to safely, synchronously, call } } @@ -162,7 +166,7 @@ public protocol Executor: AnyObject, Sendable { // work-scheduling operation. func enqueue(_ job: consuming Job) - @available(*, deprecated, message: "Implement the enqueue(Job) method instead") + @available(*, deprecated, message: "Implement the enqueue(_:Job) method instead") func enqueue(_ job: UnownedJob) } ``` @@ -204,7 +208,7 @@ public protocol SerialExecutor: Executor { /// executor references. func asUnownedSerialExecutor() -> UnownedSerialExecutor - // Discussed in depth in "Details of 'same executor' checking" of this proposal + // Discussed in depth in "Details of 'same executor' checking" of this proposal. func isSameExclusiveExecutionContext(other executor: Self) -> Bool } @@ -358,8 +362,25 @@ public protocol Actor: AnyActor { } public protocol DistributedActor: AnyActor { - /// ... - nonisolated var unownedExecutor: UnownedSerialExecutor { get } + /// Retrieve the executor for this distributed actor as an optimized, + /// unowned reference. This API is equivalent to ``Actor/unownedExecutor``, + /// however, by default, it intentionally returns `nil` if this actor is a reference + /// to a remote distributed actor, because the executor for remote references + /// is effectively never accessed nor can code be `isolated` to run on a remote + /// actor reference. + /// + /// ## Custom implementation requirements + /// + /// This property must always evaluate to the same executor for a + /// given actor instance, and holding on to the actor must keep the + /// executor alive. + /// + /// This property will be implicitly accessed when work needs to be + /// scheduled onto this actor. These accesses may be merged, + /// eliminated, and rearranged with other work, and they may even + /// be introduced when not strictly required. Visible side effects + /// are therefore strongly discouraged within this property. + nonisolated var localUnownedExecutor: UnownedSerialExecutor? { get } } ``` @@ -371,7 +392,7 @@ Developers can customize the executor used by an actor on a declaration-by-decla ```swift (distributed) actor MainActorsBestFriend { - nonisolated var unownedExecutor: UnownedSerialExecutor { + nonisolated var localUnownedExecutor: UnownedSerialExecutor { MainActor.sharedUnownedExecutor } func greet() { @@ -388,13 +409,6 @@ func mainGreet() { func test() { Task { await mainGreet() } Task { await MainActorsBestFriend().greet() } - // Legal executions: - // 1) - // - Main-friendly... hello! - // - Main hello! - // 2) - // - Main hello! - // - Main-friendly... hello! } ``` @@ -483,46 +497,67 @@ extension Worker { Sometimes, especially when porting existing codebases _to_ Swift Concurrency we recognize the ability to assert in synchronous code if it is running on the expected executor can bring developers more confidence during their migration to Swift Concurrency. In order to support these migrations, we propose the following method: ```swift -/// Checks if the current task is running on the expected executor. -/// -/// Do note that if multiple actors share the same serial executor, -/// this assertion checks for the executor, not specific actor instance. -/// -/// Generally, Swift programs should be constructed such that it is statically -/// known that a specific executor is used, for example by using global actors or -/// custom executors. However, in some APIs it may be useful to provide an -/// additional runtime check for this, especially when moving towards Swift -/// concurrency from other runtimes which frequently use such assertions. -public func preconditionTaskOnExecutor( - _ executor: some SerialExecutor, - _ message: @autoclosure () -> String = "", - file: String = #fileID, line: UInt = #line) - -public func preconditionTaskOnActorExecutor( - _ executor: some Actor, - _ message: @autoclosure () -> String = "", - file: String = #fileID, line: UInt = #line) +extension SerialExecutor { + /// Checks if the current task is running on the expected executor. + /// + /// Do note that if multiple actors share the same serial executor, + /// this assertion checks for the executor, not specific actor instance. + /// + /// Generally, Swift programs should be constructed such that it is statically + /// known that a specific executor is used, for example by using global actors or + /// custom executors. However, in some APIs it may be useful to provide an + /// additional runtime check for this, especially when moving towards Swift + /// concurrency from other runtimes which frequently use such assertions. + public func preconditionIsolated( + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) +} + +extension Actor { + public nonisolated func preconditionIsolated( + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) +} + +extension DistributedActor { + public nonisolated func preconditionIsolated( + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) +} ``` as well as an `assert...` version of this API, which triggers only in `debug` builds: ```swift -// Same as ``preconditionTaskOnExecutor(_:_:file:line)`` however only in DEBUG mode. -public func assertTaskOnExecutor( - _ executor: some SerialExecutor, - _ message: @autoclosure () -> String = "", - file: String = #fileID, line: UInt = #line) +extension SerialExecutor { + // Same as ``SerialExecutor/preconditionIsolated(_:file:line)`` however only in DEBUG mode. + public func assertOnExecutor( + _ executor: some SerialExecutor, + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) +} + +extension Actor { + // Same as ``Actor/preconditionIsolated(_:file:line)`` however only in DEBUG mode. + public nonisolated func assertOnExecutor( + _ executor: some SerialExecutor, + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) +} -public func assertTaskOnActorExecutor( - _ executor: some Actor, - _ message: @autoclosure () -> String = "", - file: String = #fileID, line: UInt = #line) +extension DistributedActor { + // Same as ``DistributedActor/preconditionIsolated(_:file:line)`` however only in DEBUG mode. + public nonisolated func assertOnExecutor( + _ executor: some SerialExecutor, + _ message: @autoclosure () -> String = "", + file: String = #fileID, line: UInt = #line) +} ``` -These APIs offer better diagnostics than would be possible to implement using a plain `precondition()` implemented by developers using some `precondition(isOnExpectedExecutor(someExecutor))` because they offer a description of the actually active executor when mismatched: +The versions of the APIs offered on `Actor` and `DistributedActor` offer better diagnostics than would be possible to implement using a plain `precondition()` implemented by developers using some `precondition(isOnExpectedExecutor"(someExecutor))` because they offer a description of the actually active executor when mismatched: ````swift -preconditionTaskOnActorExecutor(MainActor.shared) +MainActor.preconditionIsolated() // Precondition failed: Incorrect actor executor assumption; Expected 'MainActorExecutor' executor, but was executing on 'Sample.InlineExecutor'. ```` @@ -552,31 +587,42 @@ At this point, similar to Dispatch, these APIs only offer an "assert" / "precond > Note: This API was initially pitched separately from custom executors, but as we worked on the feature we realized how closely it is related to custom executors and asserting on executors. The initial pitch thread is located here: [Pitch: Unsafe Assume on MainActor](https://forums.swift.org/t/pitch-unsafe-assume-on-mainactor/63074/). -This revision of the proposal introduces the `assumeOnMainActorExecutor(_:)` method, which allows synchronous code to safely assume that they are called within the context of the main actor's executor. This is only available in synchronous functions, because the right way to spell this requirement in asynchronous code is to annotate the function using `@MainActor` which statically ensures this requirement. +This revision of the proposal introduces the `MainActor.assumeIsolated(_:)` method, which allows synchronous code to safely assume that they are called within the context of the main actor's executor. This is only available in synchronous functions, because the right way to spell this requirement in asynchronous code is to annotate the function using `@MainActor` which statically ensures this requirement. Synchronous code can assume that it is running on the main actor executor by using this assume method: ```swift -/// Performs test at runtime check whether this function was called -/// while on the MainActor's executor. Then the operation is invoked -/// and its result is returned. -/// -/// - Attention: If this is called from a different execution context, -/// the method will crash, in order to prevent creating race conditions -/// with the MainActor. -@available(*, noasync) -func assumeOnMainActorExecutor( - _ operation: @MainActor () throws -> T, - file: StaticString = #fileID, line: UInt = #line -) rethrows -> T +extension MainActor { + /// A safe way to synchronously assume that the current execution context belongs to the MainActor. + /// + /// This API should only be used as last resort, when it is not possible to express the current + /// execution context definitely belongs to the main actor in other ways. E.g. one may need to use + /// this in a delegate style API, where a synchronous method is guaranteed to be called by the + /// main actor, however it is not possible to annotate this legacy API with `@MainActor`. + /// + /// This method cannot be used in an asynchronous context. Instead, prefer implementing + /// a method annotated with `@MainActor` and calling it from your asynchronous context. + /// + /// - Warning: If the current executor is *not* the MainActor's serial executor, this function will crash. + /// + /// Note that this check is performed against the MainActor's serial executor, meaning that + /// if another actor uses the same serial executor--by using ``MainActor/sharedUnownedExecutor`` + /// as its own ``Actor/unownedExecutor``--this check will succeed, as from a concurrency safety + /// perspective, the serial executor guarantees mutual exclusion of those two actors. + @available(*, noasync) + func assumeIsolated( + _ operation: @MainActor () throws -> T, + file: StaticString = #fileID, line: UInt = #line + ) rethrows -> T +} ``` -Similarily to the assert and precondition APIs, this check is performed against the actor's executor, so if multiple actors are run on the same executor, this check will succeed in synchronous code invoked by such actors as well. In other words, the following code is also correct: +Similarily to the `preconditionIsolated` API, the executor check is performed against the target actor's executor, so if multiple actors are run on the same executor, this check will succeed in synchronous code invoked by such actors as well. In other words, the following code is also correct: ```swift func check(values: MainActorValues) /* synchronous! */ { // values.get("any") // error: main actor isolated, cannot perform async call here - assumeOnMainActorExecutor { + MainActor.assumeIsolated { values.get("any") // correct & safe } } @@ -603,25 +649,67 @@ final class MainActorValues { } ``` -> Note: Because it is not possible to abstract over the `@SomeActor () -> T` function type's global actor isolation, we currently do not offer a version of this API for _any_ global actor, however it would be possible to implement such API today using macros, which could be expored in a follow-up proposal if seen as important enough. Such API would have to be spelled `#assumeOnGlobalActorExecutor(GlobalActor.self)`. +> Note: Because it is not possible to abstract over the `@SomeGlobalActor () -> T` function type's global actor isolation, we currently do not offer a version of this API for _any_ global actor, however it would be possible to implement such API today using macros, which could be expored in a follow-up proposal if seen as important enough. Such API would have to be spelled `SomeGlobalActor.assumeIsolated() { @SomeGlobalActor in ... }`. In addition to the `MainActor` specialized API, the same shape of API is offered for instance actors and allows obtaining an `isolated` actor reference if we are guaranteed to be executing on the same serial executor as the given actor, and thus no concurrent access violations are possible. ```swift -@available(*, noasync) -func assumeOnActorExecutor( - _ operation: (isolated Act) throws -> T, - file: StaticString = #fileID, line: UInt = #line -) rethrows -> T - -@available(*, noasync) -func assumeOnLocalDistributedActorExecutor( - _ operation: (isolated Act) throws -> T, - file: StaticString = #fileID, line: UInt = #line -) rethrows -> T +extension Actor { + /// A safe way to synchronously assume that the current execution context belongs to the passed in `actor`. + /// + /// This method cannot be used in an asynchronous context. Instead, prefer implementing + /// a method on the distributed actor and calling it from your asynchronous context. + /// + /// This API should only be used as last resort, when it is not possible to express the current + /// execution context definitely belongs to the main actor in other ways. E.g. one may need to use + /// this in a delegate style API, where a synchronous method is guaranteed to be called by the + /// main actor, however it is not possible to move some function implementation onto the target + /// `distributed actor` for some reason. + /// + /// - Warning: If the current executor is *not* the actor's serial executor, and the actor is local, this function will crash. + /// + /// - Parameters: + /// - operation: the operation that will run if the actor was local, and the current executor is the same as of the passed in actors + /// - Returns: the result of the operation + /// - Throws: the error the operation has thrown + @available(*, noasync) + func assumeIsolated( + _ operation: (isolated Act) throws -> T, + file: StaticString = #fileID, line: UInt = #line + ) rethrows -> T +} ``` -These assume methods have the same semantics as the just explained `assumeOnMainActorExecutor` in the sense that the check is performed about the actor's _executor_ and not specific instance. In other words, if many instance actors share the same serial executor, this check would pass for each of them, as long as the same executor is found to be the current one. +These assume methods have the same semantics as the just explained `MainActor.assumeIsolated` in the sense that the check is performed about the actor's _executor_ and not specific instance. In other words, if many instance actors share the same serial executor, this check would pass for each of them, as long as the same executor is found to be the current one. + +The same method is offered for distributed actors, where code can only ever be isolated to an instance if the reference is to a _local_ distributed actor, as well as the same serial executor as the checked actor is running the current task: + +```swift +extension DistributedActor { + /// A safe way to synchronously assume that the current execution context belongs to the passed in `actor`. + /// + /// This method cannot be used in an asynchronous context. Instead, prefer implementing + /// a method on the distributed actor and calling it from your asynchronous context. + /// + /// This API should only be used as last resort, when it is not possible to express the current + /// execution context definitely belongs to the main actor in other ways. E.g. one may need to use + /// this in a delegate style API, where a synchronous method is guaranteed to be called by the + /// main actor, however it is not possible to move some function implementation onto the target + /// `distributed actor` for some reason. + /// + /// - Warning: If the current executor is *not* the actor's serial executor, and the actor is local, this function will crash. + /// + /// - Parameters: + /// - operation: the operation that will run if the actor was local, and the current executor is the same as of the passed in actors + /// - Returns: the result of the operation + /// - Throws: the error the operation has thrown + @available(*, noasync) + func assumeIsolated( + _ operation: (isolated Act) throws -> T, + file: StaticString = #fileID, line: UInt = #line + ) rethrows -> T +} +``` ### Details of "same executor" checking @@ -686,6 +774,8 @@ extension MyQueueExecutor { The unique initializer keeps the current semantics of "*if the executor pointers are the same, it is the same executor and exclusive execution context*" fast path of executor equality checking, however it adds a "deep check" code-path if the equality has failed. +> The word "complex" was selected due to its meaning "consisting of many different and connected parts", which describes this feature very well. The various executors are able to form a complex network that may be necessary to be inspected in order to answer the "*is this the same context?*" question. + When performing the "is this the same (or a compatible) serial execution context" checks, the Swift runtime first compares the raw pointers to the executor objects. If those are not equal and the executors in question have `complexEquality`, following some additional type-checks, the following `isSameExclusiveExecutionContext(other:)` method will be invoked: ```swift @@ -732,15 +822,15 @@ We inspect at the type of the executor (the bit we store in the ExecutorRef, spe - comparison: - compare the two executors pointers directly - return the result -- **complexEquality**, may be throught of as "**inner**" executor, i.e. one that's exact identity may need deeper introspection +- **complexEquality**, may be thought of as "**inner**" executor, i.e. one that's exact identity may need deeper introspection - creation: - `UnownedSerialExecutor(complexEquality:)` which sets specific bits that the runtime can recognize and enter the complex comparison code-path when necessary - comparison: - - If both executors are `complexEquality`, we compare the two executor identity directly - - if true, return (fast-path, same as the ordinary executors ones) - - compare the witness tables of the executors, to see if they can potentially be compatible - - if false, return - - invoke the executor implemented comparison the `executor1.isSameExclusiveExecutionContext(executor2)` + - compare the two executor pointers directly, + - if they are the same, return true (same as in the "ordinary" case) + - check if the *target* executor has `complexEquality`, we check if the current executors have compatible witness tables + - if not, we return false + - invoke the executor implemented comparison the `currentExecutor.isSameExclusiveExecutionContext(expectedExecutor)` - return the result These checks are likely *not* enough to to completely optimize task switching, and other mechanisms will be provided for optimized task switching in the future (see Future Directions). @@ -773,12 +863,6 @@ actor Friend { Note that the raw type of the MainActor executor is never exposed, but we merely get unowned wrappers for it. This allows the Swift runtime to pick various specific implementations depending on the runtime environment. -Even though we do not have a concrete class type that we can use to pass to the `some Executor` based assertion APIs, we can use the `MainActor.shared` instance together with the `some Actor` based precondition, like this: - -```swift -preconditionTaskOnActorExecutor(MainActor.shared) -``` - The default global concurrent executor is not accessible direcly from code, however it is the executor that handles all the tasks which do not have a specific executor requirement, or are explicitly required to run on that executor, e.g. like top-level async functions. ## Source compatibility From 5f966e1ab6fc0a2ba56d6ca7590aebf412bcc61b Mon Sep 17 00:00:00 2001 From: Boris Buegling Date: Mon, 3 Apr 2023 14:48:58 -0700 Subject: [PATCH 3064/4563] Mention testing of macros --- proposals/NNNN-swiftpm-expression-macros.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/NNNN-swiftpm-expression-macros.md b/proposals/NNNN-swiftpm-expression-macros.md index 10805f8d42..d62e296545 100644 --- a/proposals/NNNN-swiftpm-expression-macros.md +++ b/proposals/NNNN-swiftpm-expression-macros.md @@ -59,12 +59,15 @@ let package = Package( ]), .target(name: "MacroDef", dependencies: ["MacroImpl"]), .executableTarget(name: "MacroClient", dependencies: ["MacroDef"]), + .testTarget(name: "MacroTests", dependencies: ["MacroImpl"]), ] ) ``` Macro implementations will be executed in a sandbox [similar to package plugins](https://github.com/apple/swift-evolution/blob/main/proposals/0303-swiftpm-extensible-build-tools.md#security), preventing file system and network access. This is a practical way of encouraging macros to not depend on any state other than the specific macro expansion node they are given to expand and its child nodes (but not its parent nodes), and the information specifically provided by the macro expansion context. If in the future macros need access to other information, this will be accomplished by extending the macro expansion context, which also provides a mechanism for the compiler to track what information the macro actually queried. +Any code from macro implementations can be tested by declaring a dependency on the macro target from a test, this works similarly to the [testing of executable targets](https://github.com/apple/swift-package-manager/pull/3316). + ## Detailed Design SwiftPM builds each macro as an executable for the host platform, applying certain additional compiler flags. Macros are expected to depend on SwiftSyntax using a versioned dependency that corresponds to a particular major Swift release. Note that SwiftPM's dependency resolution is workspace-wide, so all macros (and potentially other clients) will end up consolidating on one particular version of SwiftSyntax. Each target that transitively depends on a macro will have access to it, concretely this happens by SwiftPM passing `-load-plugin-executable` to the compiler to specify which executable contains the implementation of a certain macro module (e.g. `-load-plugin-executable /path/to/package/.build/debug/MacroImpl#MacroImpl` where the argument after the hash symbol is a comma separated list of module names which can be referenced by the `module` parameter of external macro declarations). The macro defintion refers to the module and concrete type via an `#externalMacro` declaration which allows any dependency of the defining target to have access to the concrete macro. If any target of a library product depends on a macro, clients of said library will also get access to any public macros. Macros can have dependencies like any other target, but product dependencies of macros need to be statically linked, so explicitly dynamic library products cannot be used by a macro target. From 8ad9dee08ab8201734fc323b642082423fc17c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20B=C3=BCgling?= Date: Mon, 3 Apr 2023 14:49:35 -0700 Subject: [PATCH 3065/4563] Update proposals/NNNN-swiftpm-expression-macros.md Co-authored-by: Becca Royal-Gordon --- proposals/NNNN-swiftpm-expression-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-swiftpm-expression-macros.md b/proposals/NNNN-swiftpm-expression-macros.md index d62e296545..d5a0764418 100644 --- a/proposals/NNNN-swiftpm-expression-macros.md +++ b/proposals/NNNN-swiftpm-expression-macros.md @@ -34,7 +34,7 @@ public extension Target { path: String? = nil, exclude: [String] = [], sources: [String]? = nil - ) -> Target { + ) -> Target { ... } } ``` From 1d22c5320334bc080ccc32c6a91a30fcc33de0b6 Mon Sep 17 00:00:00 2001 From: Boris Buegling Date: Mon, 3 Apr 2023 14:53:22 -0700 Subject: [PATCH 3066/4563] Mention pitch threads --- proposals/NNNN-swiftpm-expression-macros.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/NNNN-swiftpm-expression-macros.md b/proposals/NNNN-swiftpm-expression-macros.md index d5a0764418..fbfaf4e774 100644 --- a/proposals/NNNN-swiftpm-expression-macros.md +++ b/proposals/NNNN-swiftpm-expression-macros.md @@ -4,6 +4,7 @@ * Authors: [Boris Buegling](https://github.com/neonichu), [Doug Gregor](https://github.com/DougGregor) * Review Manager: TBD * Status: **Implementation available behind pre-release tools-version https://github.com/apple/swift-package-manager/pull/6185 and https://github.com/apple/swift-package-manager/pull/6200** +* Review: ([pitch 1](https://forums.swift.org/t/pitch-package-manager-support-for-custom-macros/63482)) ([pitch 2](https://forums.swift.org/t/pitch-2-package-manager-support-for-custom-macros/63868)) ## Introduction From c6941bfb576086bb63600b01e52043150ae0a429 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Mon, 3 Apr 2023 15:34:13 -0700 Subject: [PATCH 3067/4563] Begin review of SE-0394 --- ...ression-macros.md => 0394-swiftpm-expression-macros.md} | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename proposals/{NNNN-swiftpm-expression-macros.md => 0394-swiftpm-expression-macros.md} (95%) diff --git a/proposals/NNNN-swiftpm-expression-macros.md b/proposals/0394-swiftpm-expression-macros.md similarity index 95% rename from proposals/NNNN-swiftpm-expression-macros.md rename to proposals/0394-swiftpm-expression-macros.md index fbfaf4e774..4111ead7d7 100644 --- a/proposals/NNNN-swiftpm-expression-macros.md +++ b/proposals/0394-swiftpm-expression-macros.md @@ -1,9 +1,10 @@ # Package Manager Support for Custom Macros -* Proposal: [SE-NNNN](NNNN-swiftpm-expression-macros.md) +* Proposal: [SE-0394](0394-swiftpm-expression-macros.md) * Authors: [Boris Buegling](https://github.com/neonichu), [Doug Gregor](https://github.com/DougGregor) -* Review Manager: TBD -* Status: **Implementation available behind pre-release tools-version https://github.com/apple/swift-package-manager/pull/6185 and https://github.com/apple/swift-package-manager/pull/6200** +* Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) +* Status: **Active Review (April 3...April 17, 2023)** +* Implementation: **Available behind pre-release tools-version** ([apple/swift-package-manager#6185](https://github.com/apple/swift-package-manager/pull/6185), [apple/swift-package-manager#6200](https://github.com/apple/swift-package-manager/pull/6200)) * Review: ([pitch 1](https://forums.swift.org/t/pitch-package-manager-support-for-custom-macros/63482)) ([pitch 2](https://forums.swift.org/t/pitch-2-package-manager-support-for-custom-macros/63868)) ## Introduction From 2c00ecf74deb74571e9989696b2df193a34179a0 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Mon, 3 Apr 2023 15:43:12 -0700 Subject: [PATCH 3068/4563] Add review link to SE-0394 --- proposals/0394-swiftpm-expression-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0394-swiftpm-expression-macros.md b/proposals/0394-swiftpm-expression-macros.md index 4111ead7d7..61f53186d5 100644 --- a/proposals/0394-swiftpm-expression-macros.md +++ b/proposals/0394-swiftpm-expression-macros.md @@ -5,7 +5,7 @@ * Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) * Status: **Active Review (April 3...April 17, 2023)** * Implementation: **Available behind pre-release tools-version** ([apple/swift-package-manager#6185](https://github.com/apple/swift-package-manager/pull/6185), [apple/swift-package-manager#6200](https://github.com/apple/swift-package-manager/pull/6200)) -* Review: ([pitch 1](https://forums.swift.org/t/pitch-package-manager-support-for-custom-macros/63482)) ([pitch 2](https://forums.swift.org/t/pitch-2-package-manager-support-for-custom-macros/63868)) +* Review: ([pitch 1](https://forums.swift.org/t/pitch-package-manager-support-for-custom-macros/63482)) ([pitch 2](https://forums.swift.org/t/pitch-2-package-manager-support-for-custom-macros/63868)) ([review](https://forums.swift.org/t/se-0394-package-manager-support-for-custom-macros/64170)) ## Introduction From c2d8f33589ada2df93b4a728c0307f098617b809 Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Wed, 29 Mar 2023 02:02:42 -0700 Subject: [PATCH 3069/4563] Update 'Upcoming feature flag' name Change the header field 'Feature Identifier' to 'Upcoming feature flag' to be consistent with the use of that name in the existing proposals with upcoming feature flags as added in 70f9605. --- proposal-templates/0000-swift-template.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposal-templates/0000-swift-template.md b/proposal-templates/0000-swift-template.md index 786b1befbc..f6423b69ed 100644 --- a/proposal-templates/0000-swift-template.md +++ b/proposal-templates/0000-swift-template.md @@ -8,7 +8,7 @@ * Roadmap: *if applicable* [Roadmap Name](https://forums.swift.org/...)) * Bug: *if applicable* [apple/swift#NNNNN](https://github.com/apple/swift/issues/NNNNN) * Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) or [apple/swift-evolution-staging#NNNNN](https://github.com/apple/swift-evolution-staging/pull/NNNNN) -* Feature Identifier: *if applicable* `MyFeatureName` +* Upcoming feature flag: *if applicable* `MyFeatureName` * Previous Proposal: *if applicable* [SE-XXXX](XXXX-filename.md) * Previous Revision: *if applicable* [1](https://github.com/apple/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md) * Review: ([pitch](https://forums.swift.org/...)) @@ -62,9 +62,9 @@ been committed to the main branch (as an experimental feature), say that and specify the experimental feature flag. If the implementation is spread across multiple PRs, just link to the most important ones. -`Feature Identifier` should be the feature name used to identify this +`Upcoming feature flag` should be the feature name used to identify this feature under [SE-0362](https://github.com/apple/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md#proposals-define-their-own-feature-identifier). -Not all proposals need a feature identifier. You should think about +Not all proposals need an upcoming feature flag. You should think about whether one would be useful for your proposal as part of filling this field out. From 944ed50f43fc7d281c91a0e76c9c5c41e5a3297f Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Tue, 4 Apr 2023 05:24:57 -0700 Subject: [PATCH 3070/4563] Make 'Upcoming Feature Flag' consistent with other proposal heading names - All other proposal heading fields use title case, update 'Upcoming feature flag' to match - Update proposal template and existing proposals using this field --- proposal-templates/0000-swift-template.md | 4 ++-- proposals/0274-magic-file.md | 2 +- proposals/0286-forward-scan-trailing-closures.md | 2 +- proposals/0335-existential-any.md | 2 +- proposals/0354-regex-literals.md | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/proposal-templates/0000-swift-template.md b/proposal-templates/0000-swift-template.md index f6423b69ed..f6851d5a3f 100644 --- a/proposal-templates/0000-swift-template.md +++ b/proposal-templates/0000-swift-template.md @@ -8,7 +8,7 @@ * Roadmap: *if applicable* [Roadmap Name](https://forums.swift.org/...)) * Bug: *if applicable* [apple/swift#NNNNN](https://github.com/apple/swift/issues/NNNNN) * Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) or [apple/swift-evolution-staging#NNNNN](https://github.com/apple/swift-evolution-staging/pull/NNNNN) -* Upcoming feature flag: *if applicable* `MyFeatureName` +* Upcoming Feature Flag: *if applicable* `MyFeatureName` * Previous Proposal: *if applicable* [SE-XXXX](XXXX-filename.md) * Previous Revision: *if applicable* [1](https://github.com/apple/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md) * Review: ([pitch](https://forums.swift.org/...)) @@ -62,7 +62,7 @@ been committed to the main branch (as an experimental feature), say that and specify the experimental feature flag. If the implementation is spread across multiple PRs, just link to the most important ones. -`Upcoming feature flag` should be the feature name used to identify this +`Upcoming Feature Flag` should be the feature name used to identify this feature under [SE-0362](https://github.com/apple/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md#proposals-define-their-own-feature-identifier). Not all proposals need an upcoming feature flag. You should think about whether one would be useful for your proposal as part of filling this diff --git a/proposals/0274-magic-file.md b/proposals/0274-magic-file.md index bc45070b91..68a7016951 100644 --- a/proposals/0274-magic-file.md +++ b/proposals/0274-magic-file.md @@ -4,7 +4,7 @@ * Authors: [Becca Royal-Gordon](https://github.com/beccadax), [Dave DeLong](https://github.com/davedelong) * Review Manager: [Ben Cohen](https://github.com/airspeedswift/) * Status: **Implemented (Swift 5.8)** -* Upcoming feature flag: `ConciseMagicFile` +* Upcoming Feature Flag: `ConciseMagicFile` * Decision Notes: [Review #1](https://forums.swift.org/t/se-0274-concise-magic-file-names/32373/50), [Review #2](https://forums.swift.org/t/re-review-se-0274-concise-magic-file-names/33171/11), [Additional Commentary](https://forums.swift.org/t/revisiting-the-source-compatibility-impact-of-se-0274-concise-magic-file-names/37720) * Next Proposal: [SE-0285](0285-ease-pound-file-transition.md) diff --git a/proposals/0286-forward-scan-trailing-closures.md b/proposals/0286-forward-scan-trailing-closures.md index 467eafbf96..4399a100cf 100644 --- a/proposals/0286-forward-scan-trailing-closures.md +++ b/proposals/0286-forward-scan-trailing-closures.md @@ -4,7 +4,7 @@ * Author: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Implemented (Swift 5.3)** -* Upcoming feature flag: `ForwardTrailingClosures` (implemented in Swift 5.8) +* Upcoming Feature Flag: `ForwardTrailingClosures` (implemented in Swift 5.8) * Implementation: [apple/swift#33092](https://github.com/apple/swift/pull/33092) * Toolchains: [Linux](https://ci.swift.org/job/swift-PR-toolchain-Linux/404//artifact/branch-master/swift-PR-33092-404-ubuntu16.04.tar.gz), [macOS](https://ci.swift.org/job/swift-PR-toolchain-osx/579//artifact/branch-master/swift-PR-33092-579-osx.tar.gz) * Discussion: ([Pitch #1](https://forums.swift.org/t/pitch-1-forward-scan-matching-for-trailing-closures-source-breaking/38162)), ([Pitch #2](https://forums.swift.org/t/pitch-2-forward-scan-matching-for-trailing-closures/38491)) diff --git a/proposals/0335-existential-any.md b/proposals/0335-existential-any.md index 843077e937..67f9d90e38 100644 --- a/proposals/0335-existential-any.md +++ b/proposals/0335-existential-any.md @@ -4,7 +4,7 @@ * Authors: [Holly Borla](https://github.com/hborla) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 5.6)** -* Upcoming feature flag: `ExistentialAny` (implemented in Swift 5.8) +* Upcoming Feature Flag: `ExistentialAny` (implemented in Swift 5.8) * Implementation: [apple/swift#40282](https://github.com/apple/swift/pull/40282) * Decision Notes: [Acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0335-introduce-existential-any/54504) diff --git a/proposals/0354-regex-literals.md b/proposals/0354-regex-literals.md index ddf417a95c..c3668431a0 100644 --- a/proposals/0354-regex-literals.md +++ b/proposals/0354-regex-literals.md @@ -4,7 +4,7 @@ * Authors: [Hamish Knight](https://github.com/hamishknight), [Michael Ilseman](https://github.com/milseman), [David Ewing](https://github.com/DaveEwing) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Implemented (Swift 5.7)** -* Upcoming feature flag: `BareSlashRegexLiterals` (implemented in Swift 5.8) +* Upcoming Feature Flag: `BareSlashRegexLiterals` (implemented in Swift 5.8) * Implementation: [apple/swift#42119](https://github.com/apple/swift/pull/42119), [apple/swift#58835](https://github.com/apple/swift/pull/58835) * Bare slash syntax `/.../` available with `-enable-bare-slash-regex` * Review: ([first pitch](https://forums.swift.org/t/pitch-regular-expression-literals/52820)) From aa4156a9243475b101c996864003d2d5f1e03661 Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Tue, 4 Apr 2023 11:02:53 -0700 Subject: [PATCH 3071/4563] Update freestanding macros proposal --- proposals/nnnn-freestanding-macros.md | 288 +++++++++++++++----------- 1 file changed, 172 insertions(+), 116 deletions(-) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index 8d992f4ba9..609bd1b3ee 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -4,7 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Richard Wei](https://github.com/rxwei), [Holly Borla](https://github.com/hborla) * Review Manager: Unassigned * Status: **Pending review** -* Implementation: On `main` behind the experimental flag `Macros` +* Implementation: On `main` behind the experimental flag `FreestandingMacros` * Review: ## Introduction @@ -18,17 +18,16 @@ This proposal generalizes the `#`-prefixed macro expansion syntax introduced for ## Proposed solution -The proposal introduces "freestanding" macros, which are expanded to create zero or more new declarations, statements, and expressions. The generated declarations can be referenced from other Swift code, making freestanding macros useful for many different kinds of code generation and manipulation. +The proposal introduces "freestanding" macros, which are expanded to create zero or more new declarations and expressions. The generated declarations can be referenced from other Swift code, making freestanding macros useful for many different kinds of code generation and manipulation. -All freestanding macros use the `#`-prefixed syntax introduced in [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) , building on and generalizing its design. Indeed, this proposal reclassifies expression macros as one form of freestanding macros, introducing two additional kinds of freestanding macro: +All freestanding macros use the `#`-prefixed syntax introduced in [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) , building on and generalizing its design. Indeed, this proposal reclassifies expression macros as one form of freestanding macros, introducing one additional kind of freestanding macro: * *Declaration macros* introduce zero or more declarations. These macros can be used anywhere where a declaration is permitted, including at the top level, in a function or closure body, or in a type definition or extension thereof. -* *Code item* macros introduce a mix of statements, expressions, and declarations. These macros can be used wherever one can write a statement, including at the top level (for Swift scripts or main files) and in the body of a function or closure. -Freestanding macros are declared with the `macro` introducer, and have one or more `@freestanding` attributes applied to them. The `@freestanding` attribute always contains a macro *role* (expression, declaration, or code item) and, optionally, a set of *introduced names*. For example, a freestanding code item macro would have an attribute like this: +Freestanding macros are declared with the `macro` introducer, and have one or more `@freestanding` attributes applied to them. The `@freestanding` attribute always contains a macro *role* (expression or declaration) and, optionally, a set of *introduced names* like attached macros. For example, a freestanding declaration macro would have an attribute like this: ```swift -@freestanding(codeItem) +@freestanding(declaration) ``` whereas a declaration macro that introduced an enum named `CodingKeys` would have an attribute like this: @@ -59,11 +58,11 @@ As well as the `@freestanding(expression)` syntax: @freestanding(expression) macro stringify(_: T) -> (T, String) ``` -Expression macros can be used anywhere that a declaration is permitted, e.g., within the body of a function or closure, or as a subexpression anywhere. Their implementations always produce another expression. +Expression macros can be used anywhere that an expression is permitted, e.g., within the body of a function or closure, or as a subexpression anywhere. Their implementations always produce another expression. ### Declaration macros -Declaration macros can be used anywhere that a declaration is permitted, e.g., in a function or closure body, at the top level, or within a type definition or extension thereof. Declaration macros produce zero or more declarations. The `warning` directive introduced by [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) can be described as a freestanding declaration macro as follows: +Declaration macros can be used anywhere that a declaration is permitted, e.g., in a function or closure body, at the top level, or within a type definition or extension thereof. Declaration macros produce zero or more declarations. The `warning` directive introduced by [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) can be described as a freestanding code item macro as follows: ```swift /// Emits the given message as a warning, as in SE-0196. @@ -98,203 +97,223 @@ declaration -> macro-expansion-declaration macro-expansion-declaration -> '#' identifier generic-argument-clause[opt] function-call-argument-clause[opt] trailing-closures[opt] ``` -The implementation of a `warning` declaration macro extracts the string literal argument (producing an error if there wasn't one) and emits a warning. It returns an empty list of declarations: +The implementation of a `warning` declaration macro extracts the string literal argument (producing an error if there wasn't one) and emits a warning. It returns an empty list of declarations: ```swift public struct WarningMacro: DeclarationMacro { public static func expansion( of node: some FreestandingMacroExpansionSyntax, in context: inout MacroExpansionContext ) throws -> [DeclSyntax] { - guard let messageExpr = node.argumentList.first?.expression?.as(SpecializeExprSyntax.self), - messageExpr.segments.count == 1, - let firstSegment = messageExpr.segments.first, - case let .stringSegment(message) = firstSegment else { - throw SimpleError(node, "warning macro requires a non-interpolated string literal") + guard let messageExpr = node.argumentList.first?.expression.as(StringLiteralExprSyntax.self), + messageExpr.segments.count == 1, + let firstSegment = messageExpr.segments.first, + case let .stringSegment(message) = firstSegment else { + throw SimpleError(node, "warning macro requires a non-interpolated string literal") } - context.diagnose(.warning(firstSegment.text)) + context.diagnose(Diagnostic(node: Syntax(node), message: SimpleDiagnosticMessage( + message: message.description, + diagnosticID: .init(domain: "test", id: "error"), + severity: .warning))) return [] } } ``` -A macro that does introduce declarations needs to document the names it introduces. The `@freestanding` attribute has a `names` argument that provides the names introduced by a macro. For example, consider a macro that declares a `main` function suitable for use with the [`@main` attribute](https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md) but that handles an exit code, e.g., +A macro that does introduce declarations needs to document the names it introduces. Just like `@attached`, the `@freestanding` attribute has a `names` argument that provides the names introduced by a macro. For example, consider a macro that declares a `main` function suitable for use with the [`@main` attribute](https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md) but that handles an exit code, e.g., ```swift -#main { - if hasBadArgument() { return -1 } - if noNetwork() { return -2 } - return 0 +@main +struct App { + #main { + if hasBadArgument() { return -1 } + if noNetwork() { return -2 } + return 0 + } } ``` -will generate code such as: +will be expanded to: ```swift -static func unique_name() -> Int { - if hasBadArgument() { return -1 } - if noNetwork() { return -2 } - return 0 -} +@main +struct App { + static func $_unique_name() -> Int { + if hasBadArgument() { return -1 } + if noNetwork() { return -2 } + return 0 + } -static func main() { - guard let exit_code = unique_name(), exit_code == 0 else { - exit(exit_code) + static func main() { + guard let exitCode = $_unique_name(), exitCode == 0 else { + exit(exitCode) + } } - - return 0 } ``` -The `main` attribute would be declared as follows: +The `main` macro would be declared as follows: ```swift @freestanding(declaration, names: named(main)) +macro main(_ body: () -> Int) ``` -This specifies that the macro will produce a declaration named `main`. It is also allowed to produce declarations with names produced by `MacroExpansionContext.createUniqueName` (implied by `unique_name` in the example above) without documenting them, because they are not visible to other parts of the program not generated by the macro. The reasons for documenting macro names are provided within the detailed design. +This specifies that the macro will produce a declaration named `main`. It is also allowed to produce declarations with names produced by `MacroExpansionContext.makeUniqueName(_:)` (implied by `$_unique_name` in the example above) without documenting them, because they are not visible to other parts of the program not generated by the macro. The reasons for documenting macro names are provided within the detailed design. -### Code item macros +## Detailed design -Code item macros can produce any mix of declarations, statements, and expressions, which are collectively called "code items" in the grammar. Code item macros can be used for top-level code and within the bodies of functions and closures. They are declaration with `@freestanding(codeItem)`. For example, we could declare a macro that logs when we are entering and exiting a function: +### Syntax -```swift -@freestanding(codeItem) macro logEntryExit(arguments: Any...) +The syntactic representation of a freestanding macro expansion site is a macro expansion declaration. A macro expansion declaration is described by the following grammar. It has the same production rule as a macro expansion expression. + +``` +declaration -> macro-expansion-declaration +macro-expansion-declaration -> '#' identifier generic-argument-clause[opt] function-call-argument-clause[opt] trailing-closures[opt] ``` -Code item macros are implemented as types conforming to the `CodeItemMacro` protocol: +At top level and function scope where both expressions and declarations are allowed, a freestanding macro expansion site is first parsed as a macro expansion expression. It will be replaced by a macro expansion declaration later during type checking, if the macro resolves to a declaration macro. This is to allow the following expressions to still be parsed correctly as an expression. ```swift -public protocol CodeItemMacro: FreestandingMacro { - /// Expand a macro described by the given freestanding macro expansion declaration - /// within the given context to produce a set of code items, which can be any mix of - /// expressions, statements, and declarations. - static func expansion( - of node: some FreestandingMacroExpansionSyntax, - in context: some MacroExpansionContext - ) async throws -> [CodeBlockItemSyntax] -} +#line + 1 +#line as Int? ``` -The `logEntryExit` macro could introduce code such as: +### Composing macro roles + +A freestanding macro can be declared as both an expression macro and a declaration macro. ```swift -print("- Entering \(#function)(\(arguments))") -defer { - print("- Exiting \(#function)(\(arguments))") -} +@freestanding(expression) +@freestanding(declaration) +macro dualRoleMacro() ``` -Code item macros can only introduce new declarations that have unique names, created with `createUniqueName`. They cannot introduce named declarations, because doing so affects the ability to type-check without repeatedly expanding the macro with potentially complete information. See the section on the visibility of names used and introduced by macros. - -## Detailed design +In this case, we expand it based on its expansion context. If it's being expanded where an declaration is allowed, it will always be expanded to a declaration. Otherwise, it's expanded as an expression. -### Permitted declaration kinds +```swift +// File scope +#dualRoleMacro // expanded as a declaration + +func foo() { + #dualRoleMacro // expanded as a declaration + + _ = #dualRoleMacro // expanded as an expression + + bar(#dualRoleMacro) // expanded as an expression + + takesClosure { + #dualRoleMacro // expanded as a declaration + } +} +``` -A macro can expand to any declaration that is syntatically and semantically well-formed within the context where the macro is expanded, with a few notable exceptions: +### Restrictions -* `import` declarations can never be produced by a macro. Swift tooling depends on the ability to resolve import declarations based on a simple scan of the original source files. Allowing a macro expansion to introduce an import declaration would complicate import resolution considerably. -* `extension` declarations can never be produced by a macro. The effect of an extension declaration is wide-ranging, with the ability to add conformances, members, and so on. These capabilities are meant to be introduced in a more fine-grained manner. -* `operator` and `precedencegroup` declarations can never be produced by a macro, because they could allow one to reshape the precedence graph for existing code causing subtle differences in the semantics of code that sees the macro expansion vs. code that does not. -* `macro` declarations can never be produced by a macro, because allowing this would allow a macro to trivially produce infinitely recursive macro expansion. -* Top-level default literal type overrides, including `IntegerLiteralType`, - `FloatLiteralType`, `BooleanLiteralType`, - `ExtendedGraphemeClusterLiteralType`, `UnicodeScalarLiteralType`, and - `StringLiteralType`, can never be produced by a macro. +Like attached peer macros, a freestanding macro can expand to any declaration that is syntatically and semantically well-formed within the context where the macro is expanded, but also share the same requirements and restrictions. -### Up-front declarations of newly-introduced names +- [Specifying newly-introduced names](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#specifying-newly-introduced-names) + - Note that only `named(...)` and `arbitrary` are allowed as macro-introduced names for a declaration macro. +- [Visibility of names used and introduced by macros](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#visibility-of-names-used-and-introduced-by-macros) +- [Permitted declaration kinds](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#permitted-declaration-kinds) -Whenever a macro produces declarations that are visible to other Swift code, it is required to declare the names in advance. This enables the Swift compiler and related tools to better reason about the set of names that can be introduced by a given use of a macro without having to expand the macro (or type-check its arguments), which can reduce the compile-time cost of macros and improve incremental builds. All of the names need to be specified within the attribute declaring the macro role, using the following forms: +### Examples -* Declarations with a specific fixed name: `named()`. -* Declarations whose names cannot be described statically, for example because they are derived from other inputs: `arbitrary`. +#### SE-0196 `warning` and `error` -Multiple names can be provided after the `names` label, e.g., +The `#warning` and `#error` directives introduced in [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md): can be implemented directly as declaration macros: ```swift -@freestanding(declarations, names: named(CodingKeys), named(init(coder:)), named(encode(with:))) -macro codable: Void -``` +/// Emit a warning containing the given message. +@freestanding(declaration) macro warning(_ message: String) -A macro can only introduce new declarations whose names are covered by the kinds provided, or have their names generated via `MacroExpansionContext.createUniqueName`. This ensures that, in most cases (where `.arbitrary` is not specified) the Swift compiler and related tools can reason about the set of names that will be introduced by a given use of a declaration macro without having to expand the macro, which can reduce the compile-time cost of macros and improve incremental builds. +/// Emit an error containing the given message +@freestanding(declaration) macro error(_ message: String) +``` -### Visibility of names used and introduced by macros +### Boilerplate generation -Declaration and code-item macros can introduce new names into the program. Whether and when those names are visible to other parts of the program, and in particular other macros, is a subtle design problem that has a number of interesting constraints. +Freestanding declaration macros can be used to generate boilerplace code. For example, the Standard Library could use such a macro to generate integer types. -First, the arguments to a macro are type-checked before it can be determined which macro is being expanded, so they cannot depend on the expansion of that macro. For example, consider a freestanding macro use spelled `#myMacro(x)`: if it introduced a declaration named `x`, it would potentially invalidate the type-check of its own argument, or cause that macro expansion to choose a different `myMacro` that doesn't produce an `x`! +```swift +@freestanding(declaration) +fileprivate macro IntegerTypes(_ bitWidth: Int...) -Second, if the output of one macro expansion (say, `#myMacro1(x)`) introduces a name (say, `y`) that is then used in the argument to another macro expansion (say, `#myMacro2(y)`), then the order in which the macros are expanded can affect the semantics of the resulting program. It is not generally possible to introduce an ordering on macro expansions, nor would it be desirable, because Swift generally tries not to have order dependencies among declarations in a program. Incidental orderings based on the names introduced by the macro don't help, either, because names of declaration macros can be specified as `arbitrary` and therefore cannot be predicated. +#IntegerTypes(bitWidths: 8, 16, 32, 64) +``` -Third, macro expansion is a relatively expensive compile-time operation, involving serialization overhead to transfer parts of the program from the compiler/IDE to another program that expands the macro. Therefore, freestanding macros are [only expanded once per use site](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion), so their expansions cannot participate in the type checking of their arguments or of other surrounding statements or expressions. For example, consider a this (intentionally obtuse) Swift code: +This expands to: ```swift -@freestanding(codeItem) macro myMacro(_: Int) -@freestanding(codeItem, names: named("y")) macro myMacro(_: Double) +public struct Int8 { ... } +public struct UInt8 { ... } -func f(_: Int, body: (Int) -> Void) -func f(_: Double, body: (Double) -> Void) +public struct Int16 { ... } +public struct UInt16 { ... } -f(1) { x in - #myMacro(x) - print(y) -} +public struct Int32 { ... } +public struct UInt32 { ... } + +public struct Int64 { ... } +public struct UInt64 { ... } ``` -The freestanding macro use `#myMacro(x)` provides different names depending on whether the argument is an `Int` or ` Double`. From the perspective of the call to `f`, both overloads of `f` are possible, and the only way to check beyond that macro use to the `print` expression that follows is to try to expand the macro... for the first `myMacro`, the `print(y)` expression will fail to type-check (there is no `y`) whereas the second would find the `y` generated by the second `myMacro` and will succeed. However, this approach does not scale, because it could involve expanding macros a large number of times during type checking. Moreover, the macro expansions might end up getting incomplete information if, for example, macros are someday provided with type information and that type information isn't known yet (because the expansion is happening during type inference). +### Short hand for `@main` -To address these issues, a name introduced by a macro expansion is not visible within macro arguments for macros the same scope as the macro expansion or any of its enclosing scopes. This is conceptually similar to [outside-in expansion of expression macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion), where one can imagine that all of the macros at a particular scope are type-checked and expanded simultaneously at the top-level scope. Then, macros within each type definition and its extensions are type-checked and expanded simultaneously: they can see the names introduced by top-level macro expansions, but not names introduced at other levels. The following annotated code example shows which names are visible: +A freestanding declaration macro `#main` could be defined as a short hand for a `@main` struct with a `main()` method. ```swift -import OtherModule // declares names x, y, z - -#macro1(x) // uses OtherModule.x, introduces name y +@freestanding(declaration) +macro main(_ body: () -> Void) -#macro2(y) // uses OtherModule.y, introduce name x - -struct S1 { - #macro3(x) // uses the name x introduced by #macro2, introduces the name z +#main { + print("Hello") } +``` -extension S1 { - #macro4(z) // uses OtherModule.z +This expands to: - func f() { - print(z) // uses z introduced by #macro3 +```swift +@main +struct $_unique_name { + static func main() { + print("Hello") } } ``` -These name lookup rules, while important for providing a reasonable scheme for macros to be expanded without interfering with each other, do introduce a new kind of name shadowing to the language. If `#macro2` were manually expanded into source code, the declaration `x` it produces would then become visible to the macro argument in `#macro1(x)`, changing the behavior of the program. The compiler might be able to detect such shadowing in practice, when a macro-introduced name at a particular scope would change the meaning of a lookup from that particular scope. +### Environment values -Within function and closure bodies, the fact that names introduced by the macro expansion are not visible within the current scope means that our earlier example will never find a declaration `y` introduced by `#myMacro`: +In apps built with SwiftUI, environment properties are declared with the `@Environment` property wrapper. In most cases, environment properties have the same identifier as the key path passed to `@Environment`, but are prone to typographical errors because the language won't enforce the same spelling. ```swift -f(1) { x in - #myMacro(x) - print(y) // does not consider any names introduced by `#myMacro` +struct ContentView: View { + @Environment(\.menuOrder) var menuOrder } ``` -Therefore, a macro used within a closure or function body can only introduce declarations using names produced by `createUniqueName`. This maintains the [two-phase of checking macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion) where type checking and inference is performed without expanding the macro, then the macro is expanded and its result type-checked independently, with no ability to influence type inference further. +One could define an `#EnvironmentProperty` macro such that the identifier only needs to be spelled once as part of the key path. -### Macros in the Standard Library +```swift +@freestanding(declaration) +macro EnvironmentProperty(_ keyPath: KeyPath) -#### SE-0196 `warning` and `error` +struct ContentView: View { + #EnvironmentProperty(\.menuOrder) +} +``` -The `#warning` and `#error` directives introduced in [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md): can be implemented directly as declaration macros: +This expands to: ```swift -/// Emit a warning containing the given message. -@freestanding(declaration) macro warning(_ message: String) - -/// Emit an error containing the given message -@freestanding(declaration) macro error(_ message: String) +struct ContentView: View { + @Environment(\.menuOrder) var menuOrder +} ``` ## Source compatibility -Freestanding declaration macros use the same syntax introduced for expression macros, which were themselves a pure extension without an impact on source compatibility. There is a syntactic ambiguity between expression and freestanding declaration macros, i.e., `#warning("watch out")` within a function body could be either an expression or a declaration. The distinction will need to be determined semantically, by determining whether the named macro is either an expression or a freestanding declaration macro. +Freestanding macros use the same syntax introduced for expression macros, which were themselves a pure extension without an impact on source compatibility. There is a syntactic ambiguity between expression and freestanding declaration macros, i.e., `#warning("watch out")` within a function body could be either an expression or a declaration. The distinction will need to be determined semantically, by determining whether the named macro is either an expression or a freestanding declaration macro. Attached declaration macros use the same syntax introduced for custom attributes (such as property wrappers), and therefore do not have an impact on source compatibility. @@ -308,6 +327,43 @@ Macros are a source-to-source transformation tool that have no effect on API res ## Alternatives considered +N/A + +## Revision History + +- Scoped code item macros out as a future direction. + ## Future directions -(nothing just yet) +### Code item macros + +A code item macro is another kind of freestanding macro that can produce any mix of declarations, statements, and expressions, which are collectively called "code items" in the grammar. Code item macros can be used for top-level code and within the bodies of functions and closures. They are declaration with `@freestanding(codeItem)`. For example, we could declare a macro that logs when we are entering and exiting a function: + +```swift +@freestanding(codeItem) macro logEntryExit(arguments: Any...) +``` + +Code item macros are implemented as types conforming to the `CodeItemMacro` protocol: + +```swift +public protocol CodeItemMacro: FreestandingMacro { + /// Expand a macro described by the given freestanding macro expansion declaration + /// within the given context to produce a set of code items, which can be any mix of + /// expressions, statements, and declarations. + static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) async throws -> [CodeBlockItemSyntax] +} +``` + +The `logEntryExit` macro could introduce code such as: + +```swift +print("- Entering \(#function)(\(arguments))") +defer { + print("- Exiting \(#function)(\(arguments))") +} +``` + +Code item macros can only introduce new declarations that have unique names, created with `makeUniqueName(_:)`. They cannot introduce named declarations, because doing so affects the ability to type-check without repeatedly expanding the macro with potentially complete information. See the section on the visibility of names used and introduced by macros. From af94210505258bde9458ae8e013a4648fc3f5600 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 31 Mar 2023 09:58:25 -0500 Subject: [PATCH 3072/4563] Add 'Make Never Codable' proposal --- proposals/NNNN-never-codable.md | 60 +++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 proposals/NNNN-never-codable.md diff --git a/proposals/NNNN-never-codable.md b/proposals/NNNN-never-codable.md new file mode 100644 index 0000000000..a68ce7a78f --- /dev/null +++ b/proposals/NNNN-never-codable.md @@ -0,0 +1,60 @@ +# Conform `Never` to `Codable` + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Author: [Nate Cook](https://github.com/natecook1000) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#64899](https://github.com/apple/swift/pull/64899) + +## Introduction + +Extend `Never` so that it conforms to the `Encodable` and `Decodable` protocols, together known as `Codable`. + +Forum discussion: [https://forums.swift.org/t/pitch-conform-never-to-codable/64056](https://forums.swift.org/t/pitch-conform-never-to-codable/64056) + +## Motivation + +Swift can synthesize `Codable` conformance for any type that has `Codable` members. Generic types often participate in this synthesized conformance by constraining their generic parameters, like this `Either` type: + +```swift +enum Either { + case left(A) + case right(B) +} + +extension Either: Codable where A: Codable, B: Codable {} +``` + +In this way, `Either` instances where both generic parameters are `Codable` are `Codable` themselves, such as an `Either`. However, since `Never` isn't `Codable`, using `Never` as one of the parameters blocks the conditional conformance, even though it would be perfectly fine to encode or decode a type like `Either`. + +## Proposed solution + +The standard library should add `Encodable` and `Decodable` conformance to the `Never` type. + +## Detailed design + +The `Encodable` conformance is simple — since it's impossible to have a `Never` instance, the `encode(to:)` method can simply be empty. + +The `Decodable` protocol requires the `init(from:)` initializer, which clearly can't create a `Never` instance. The implementation throws a `DecodingError` if decoding is attempted. + +## Source compatibility + +If existing code already declares `Codable` conformance, that code will begin to emit a warning: e.g. `Conformance of 'Never' to protocol 'Encodable' was already stated in the type's module 'Swift'`. + +The new conformance shouldn't differ from existing conformances, since it isn't possible to construct an instance of `Never`. + +## ABI compatibility + +The proposed change is additive and does not change any existing ABI. + +## Implications on adoption + +The new conformance will have availability annotations. + +## Future directions + +None. + +## Alternatives considered + +None. From 137aa8f6c3d5889c803176c06e78a4f4aa461bfb Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Tue, 4 Apr 2023 14:48:49 -0700 Subject: [PATCH 3073/4563] Freestanding macro updates - Reword the intro - Ban freestanding macro expansions with multiple roles - Typo fixes --- proposals/nnnn-freestanding-macros.md | 111 ++++++++++---------------- 1 file changed, 44 insertions(+), 67 deletions(-) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index 609bd1b3ee..73ca05a4c0 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -18,11 +18,11 @@ This proposal generalizes the `#`-prefixed macro expansion syntax introduced for ## Proposed solution -The proposal introduces "freestanding" macros, which are expanded to create zero or more new declarations and expressions. The generated declarations can be referenced from other Swift code, making freestanding macros useful for many different kinds of code generation and manipulation. +The proposal introduces "freestanding" macros, a category of macros that can be evaluated on their own and are not attached to any other entity. All freestanding macros use the `#`-prefixed syntax introduced in [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md), building on and generalizing its design. Indeed, this proposal reclassifies expression macros as one form of freestanding macros, introducing one additional kind of freestanding macro: -All freestanding macros use the `#`-prefixed syntax introduced in [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) , building on and generalizing its design. Indeed, this proposal reclassifies expression macros as one form of freestanding macros, introducing one additional kind of freestanding macro: +This proposal also introduces a new kind of freestanding macros, declaration macros, which are expanded to create zero or more new declarations. -* *Declaration macros* introduce zero or more declarations. These macros can be used anywhere where a declaration is permitted, including at the top level, in a function or closure body, or in a type definition or extension thereof. +* *Declaration macros* introduce zero or more declarations. These macros can be used anywhere where a declaration is permitted, including at the top level, in a function or closure body, or in a type definition or extension thereof. The generated declarations can be referenced from other Swift code, making freestanding macros useful for many different kinds of code generation and manipulation. Freestanding macros are declared with the `macro` introducer, and have one or more `@freestanding` attributes applied to them. The `@freestanding` attribute always contains a macro *role* (expression or declaration) and, optionally, a set of *introduced names* like attached macros. For example, a freestanding declaration macro would have an attribute like this: @@ -172,50 +172,29 @@ declaration -> macro-expansion-declaration macro-expansion-declaration -> '#' identifier generic-argument-clause[opt] function-call-argument-clause[opt] trailing-closures[opt] ``` -At top level and function scope where both expressions and declarations are allowed, a freestanding macro expansion site is first parsed as a macro expansion expression. It will be replaced by a macro expansion declaration later during type checking, if the macro resolves to a declaration macro. This is to allow the following expressions to still be parsed correctly as an expression. +At top level and function scope where both expressions and declarations are allowed, a freestanding macro expansion site is first parsed as a macro expansion expression. It will be replaced by a macro expansion declaration later during type checking, if the macro resolves to a declaration macro. This is to allow the following infix expressions to be parsed correctly as an expression. ```swift #line + 1 #line as Int? ``` -### Composing macro roles +### Restrictions -A freestanding macro can be declared as both an expression macro and a declaration macro. +Like attached peer macros, a freestanding macro can expand to any declaration that is syntatically and semantically well-formed within the context where the macro is expanded. It shares the same requirements and restrictions. -```swift -@freestanding(expression) -@freestanding(declaration) -macro dualRoleMacro() -``` +- [**Specifying newly-introduced names**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#specifying-newly-introduced-names) + - Note that only `named(...)` and `arbitrary` are allowed as macro-introduced names for a declaration macro. +- [**Visibility of names used and introduced by macros**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#visibility-of-names-used-and-introduced-by-macros) +- [**Permitted declaration kinds**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#permitted-declaration-kinds) -In this case, we expand it based on its expansion context. If it's being expanded where an declaration is allowed, it will always be expanded to a declaration. Otherwise, it's expanded as an expression. +One additional restriction is that a macro declaration can have at most one freestanding macro role. This is because top level and function scopes allow a combination of expressions, statements, and declarations, which would be ambiguous to a freestanding macro expansion with multiple roles. ```swift -// File scope -#dualRoleMacro // expanded as a declaration - -func foo() { - #dualRoleMacro // expanded as a declaration - - _ = #dualRoleMacro // expanded as an expression - - bar(#dualRoleMacro) // expanded as an expression - - takesClosure { - #dualRoleMacro // expanded as a declaration - } -} -``` - -### Restrictions - -Like attached peer macros, a freestanding macro can expand to any declaration that is syntatically and semantically well-formed within the context where the macro is expanded, but also share the same requirements and restrictions. - -- [Specifying newly-introduced names](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#specifying-newly-introduced-names) - - Note that only `named(...)` and `arbitrary` are allowed as macro-introduced names for a declaration macro. -- [Visibility of names used and introduced by macros](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#visibility-of-names-used-and-introduced-by-macros) -- [Permitted declaration kinds](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#permitted-declaration-kinds) +@freestanding(expression) +@freestanding(declaration) // error: a macro cannot have multiple freestanding macro roles +macro foo() +``` ### Examples @@ -282,38 +261,9 @@ struct $_unique_name { } ``` -### Environment values - -In apps built with SwiftUI, environment properties are declared with the `@Environment` property wrapper. In most cases, environment properties have the same identifier as the key path passed to `@Environment`, but are prone to typographical errors because the language won't enforce the same spelling. - -```swift -struct ContentView: View { - @Environment(\.menuOrder) var menuOrder -} -``` - -One could define an `#EnvironmentProperty` macro such that the identifier only needs to be spelled once as part of the key path. - -```swift -@freestanding(declaration) -macro EnvironmentProperty(_ keyPath: KeyPath) - -struct ContentView: View { - #EnvironmentProperty(\.menuOrder) -} -``` - -This expands to: - -```swift -struct ContentView: View { - @Environment(\.menuOrder) var menuOrder -} -``` - ## Source compatibility -Freestanding macros use the same syntax introduced for expression macros, which were themselves a pure extension without an impact on source compatibility. There is a syntactic ambiguity between expression and freestanding declaration macros, i.e., `#warning("watch out")` within a function body could be either an expression or a declaration. The distinction will need to be determined semantically, by determining whether the named macro is either an expression or a freestanding declaration macro. +Freestanding macros use the same syntax introduced for expression macros, which were themselves a pure extension without an impact on source compatibility. There is a syntactic ambiguity between expression and freestanding declaration macros, i.e., `#warn("watch out")` within a function body could refer to multiple freestanding macros of different roles. The distinction will need to be determined semantically with the same overload resolution rules as function calls. Attached declaration macros use the same syntax introduced for custom attributes (such as property wrappers), and therefore do not have an impact on source compatibility. @@ -327,7 +277,34 @@ Macros are a source-to-source transformation tool that have no effect on API res ## Alternatives considered -N/A +### Multiple freestanding macro roles on a single macro + +The proposed feature bans declaring a macro as having multiple freestanding macro roles such as being both `@freestanding(expression)` and `@freestanding(declaration)`. But such a scenario could be allowed with proper rules. + +One possible solution would be to expand such a macro based on its expansion context. If it's being expanded where a declaration is allowed, it will always be expanded as a declaration. Otherwise, it's expanded as an expression. + +```swift +@freestanding(expression) +@freestanding(declaration) +macro dualRoleMacro() + +// File scope +#dualRoleMacro // expanded as a declaration + +func foo() { + #dualRoleMacro // expanded as a declaration + + _ = #dualRoleMacro // expanded as an expression + + bar(#dualRoleMacro) // expanded as an expression + + takesClosure { + #dualRoleMacro // expanded as a declaration + } +} +``` + +If a future use case deems this feature necessary, this restriction can be lifted following its own proposal. ## Revision History From e99fc429fa5eb875dbfc5cd5fd28b320d8125827 Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Tue, 4 Apr 2023 18:30:29 -0700 Subject: [PATCH 3074/4563] Freestanding macros update - Fix typo - Specify feature flags for code item macros --- proposals/nnnn-freestanding-macros.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index 73ca05a4c0..d22a2a33f5 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -216,7 +216,7 @@ Freestanding declaration macros can be used to generate boilerplace code. For ex ```swift @freestanding(declaration) -fileprivate macro IntegerTypes(_ bitWidth: Int...) +fileprivate macro IntegerTypes(bitWidths: Int...) #IntegerTypes(bitWidths: 8, 16, 32, 64) ``` @@ -344,3 +344,5 @@ defer { ``` Code item macros can only introduce new declarations that have unique names, created with `makeUniqueName(_:)`. They cannot introduce named declarations, because doing so affects the ability to type-check without repeatedly expanding the macro with potentially complete information. See the section on the visibility of names used and introduced by macros. + +Code item macros are currently under both `FreestandingMacros` and `CodeItemMacros` experimental feature flags. From 2e515027f6937fd8dec54b45387bd74f77b24838 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 6 Apr 2023 17:24:30 +0900 Subject: [PATCH 3075/4563] update proposal with review 1 feedback --- proposals/0392-custom-actor-executors.md | 151 ++++++++++++++--------- 1 file changed, 92 insertions(+), 59 deletions(-) diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index 08153aff65..6ec51551c9 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -8,9 +8,12 @@ - Previous threads: - Original pitch thread from around Swift 5.5: [Support custom executors in Swift Concurrency](https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425) - Original "assume..." proposal which was subsumed into this proposal, as it relates closely to asserting on executors: [Pitch: Unsafe Assume on MainActor](https://forums.swift.org/t/pitch-unsafe-assume-on-mainactor/63074/) -- Revisions: - - move assert/precondition/assume APIs to extensions on actor types, e.g. `Actor/assertIsolated`, `DistributedActor/preconditionIsolated`, `MainActor/assumeIsolated { ... }` - - Distributed actor executor customization point is optional, and `nil` by default for remote actor references +- Reviews: + - First review thread: https://forums.swift.org/t/returned-for-revision-se-0392-custom-actor-executors/64172 + - Revisions: + - Rename `Job` to `ExecutorJob`, making it less likely to conflict with existing type names, and typealias `UnownedJob` with `UnownedExecutorJob` (however the old type remains for backwards compatibility). + - Move assert/precondition/assume APIs to extensions on actor types, e.g. `Actor/assertIsolated`, `DistributedActor/preconditionIsolated`, `MainActor/assumeIsolated { ... }` + - Distributed actor executor customization `unownedExecutor` invoked on a remote distributed actor, to return an executor that fatal errors only once attempts are made to enqueue work onto it, rather than crashing immediately upon attempting to obtain the executor. ## Table of Contents @@ -23,7 +26,7 @@ + [A low-level design](#a-low-level-design) + [Executors](#executors) + [Serial Executors](#serial-executors) - + [Jobs](#jobs) + + [ExecutorJobs](#executorjobs) + [Actors with custom SerialExecutors](#actors-with-custom-serialexecutors) + [Asserting on executors](#asserting-on-executors) + [Assuming actor executors](#asserting-actor-executors) @@ -42,7 +45,7 @@ As Swift Concurrency continues to mature it is becoming increasingly important to offer adopters tighter control over where exactly asynchronous work is actually executed. -This proposal introduces a basic mechanism for customizing actor executors. By providing an instance of an executor, actors can influence "where" they will be executing any task they are running, while upholding the mutual excusion and actor isolation guaranteed by the actor model. +This proposal introduces a basic mechanism for customizing actor executors. By providing an instance of an executor, actors can influence "where" they will be executing any task they are running, while upholding the mutual exclusion and actor isolation guaranteed by the actor model. > **Note:** This proposal defines only a set of APIs to customize actor executors, and other kinds of executor control is out of scope for this specific proposal. @@ -82,8 +85,8 @@ We propose to give developers the ability to implement simple serial executors, final class SpecificThreadExecutor: SerialExecutor { let someThread: SomeThread // simplified handle to some specific thread - func enqueue(_ job: consuming Job) { - let unownedJob = UnownedJob(job) // in order to escape it to the run{} closure + func enqueue(_ job: consuming ExecutorJob) { + let unownedJob = UnownedExecutorJob(job) // in order to escape it to the run{} closure someThread.run { unownedJob.runSynchronously(on: self) } @@ -152,7 +155,7 @@ The API design of executors is intended to support high-performance implementati First, we introduce an `Executor` protocol, that serves as the parent protocol of all the specific kinds of executors we'll discuss next. It is the simplest kind of executor that does not provide any ordering guarantees about the submitted work. It could decide to run the submitted jobs in parallel, or sequentially. -This protocol has existed in Swift ever since the introduction of Swift Concurrency, however, in this proposal we revise its API to make use of the newly introduced move-only capabilities in the language. The existing `UnownedJob` API will be deprecated in favor of one accepting a move-only `Job`. The `UnownedJob` type remains available (and equally unsafe), because today still some usage patterns are not supported by the initial revision of move-only types. +This protocol has existed in Swift ever since the introduction of Swift Concurrency, however, in this proposal we revise its API to make use of the newly introduced move-only capabilities in the language. The existing `UnownedExecutorJob` API will be deprecated in favor of one accepting a move-only `ExecutorJob`. The `UnownedExecutorJob` type remains available (and equally unsafe), because today still some usage patterns are not supported by the initial revision of move-only types. The concurrency runtime uses the `enqueue(_:)` method of an executor to schedule some work onto given executor. @@ -164,20 +167,20 @@ public protocol Executor: AnyObject, Sendable { // get a redundant witness-table entry for it. This allows us to // avoid drilling down to the base conformance just for the basic // work-scheduling operation. - func enqueue(_ job: consuming Job) + func enqueue(_ job: consuming ExecutorJob) - @available(*, deprecated, message: "Implement the enqueue(_:Job) method instead") - func enqueue(_ job: UnownedJob) + @available(*, deprecated, message: "Implement the enqueue(_:ExecutorJob) method instead") + func enqueue(_ job: UnownedExecutorJob) } ``` -In order to aid this transition, the compiler will offer assistance similar to how the transition from `Hashable.hashValue` to `Hashable.hash(into:)` was handled. Existing executor implementations which implemented `enqueue(UnownedJob)` will still work, but print a deprecation warning: +In order to aid this transition, the compiler will offer assistance similar to how the transition from `Hashable.hashValue` to `Hashable.hash(into:)` was handled. Existing executor implementations which implemented `enqueue(UnownedExecutorJob)` will still work, but print a deprecation warning: ```swift final class MyOldExecutor: SerialExecutor { - // WARNING: 'Executor.enqueue(UnownedJob)' is deprecated as a protocol requirement; - // conform type 'MyOldExecutor' to 'Executor' by implementing 'enqueue(Job)' instead - func enqueue(_ job: UnownedJob) { + // WARNING: 'Executor.enqueue(UnownedExecutorJob)' is deprecated as a protocol requirement; + // conform type 'MyOldExecutor' to 'Executor' by implementing 'enqueue(ExecutorJob)' instead + func enqueue(_ job: UnownedExecutorJob) { // ... } } @@ -185,7 +188,7 @@ final class MyOldExecutor: SerialExecutor { Executors are required to follow certain ordering rules when executing their jobs: -- The call to `Job.runSynchronously(on:)` must happen-after the call to `enqueue(_:)`. +- The call to `ExecutorJob.runSynchronously(on:)` must happen-after the call to `enqueue(_:)`. - If the executor is a serial executor, then the execution of all jobs must be *totally ordered*: for any two different jobs *A* and *B* submitted to the same executor with `enqueue(_:)`, it must be true that either all events in *A* happen-before all events in *B* or all events in *B* happen-before all events in *A*. - Do note that this allows the executor to reorder `A` and `B`–for example, if one job had a higher priority than the other–however they each independently must run to completion before the other one is allowed to run. @@ -212,7 +215,6 @@ public protocol SerialExecutor: Executor { func isSameExclusiveExecutionContext(other executor: Self) -> Bool } -@available(SwiftStdlib 5.9, *) extension SerialExecutor { // default implementation is sufficient for most implementations func asUnownedSerialExecutor() -> UnownedSerialExecutor { @@ -241,24 +243,24 @@ A `SerialExecutor` does not introduce new API, other than the wrapping itself in /// actor. public struct UnownedSerialExecutor: Sendable { /// The default and ordinary way to expose an unowned serial executor. - public init(ordinary executor: __shared E) + public init(ordinary executor: E) /// Discussed in depth in "Details of same-executor checking" of this proposal. - public init(complexEquality executor: __shared E) + public init(complexEquality executor: E) } ``` `SerialExecutors` will potentially be extended to support "switching" which can lessen the amount of thread switches incured when using custom executors. Please refer to the Future Directions for a discussion of this extension. -### Jobs +### ExecutorJobs -A `Job` is a representation of a chunk of of work that an executor should execute. For example, a `Task` effectively consists of a series of jobs that are enqueued onto executors, in order to run them. The name "job" was selected because we do not want to constrain this API to just "partial tasks", or tie them too closely to tasks, even though the most common type of job created by Swift concurrency are "partial tasks". +A `ExecutorJob` is a representation of a chunk of of work that an executor should execute. For example, a `Task` effectively consists of a series of jobs that are enqueued onto executors, in order to run them. The name "job" was selected because we do not want to constrain this API to just "partial tasks", or tie them too closely to tasks, even though the most common type of job created by Swift concurrency are "partial tasks". -Whenever the Swift concurrency needs to execute some piece of work, it enqueues an `UnownedJob`s on a specific executor the job should be executed on. The `UnownedJob` type is an opaque wrapper around Swift's low-level representation of such job. It cannot be meaningfully inspected, copied and must never be executed more than once. +Whenever the Swift concurrency needs to execute some piece of work, it enqueues an `UnownedExecutorJob`s on a specific executor the job should be executed on. The `UnownedExecutorJob` type is an opaque wrapper around Swift's low-level representation of such job. It cannot be meaningfully inspected, copied and must never be executed more than once. ```swift @noncopyable -public struct Job: Sendable { +public struct ExecutorJob: Sendable { /// The priority of this job. public var priority: JobPriority { get } } @@ -274,7 +276,7 @@ public struct Job: Sendable { /// However, the semantics of how priority is treated are left up to each /// platform and `Executor` implementation. /// -/// A Job's priority is roughly equivalent to a `TaskPriority`, +/// A ExecutorJob's priority is roughly equivalent to a `TaskPriority`, /// however, since not all jobs are tasks, represented as separate type. /// /// Conversions between the two priorities are available as initializers on the respective types. @@ -293,18 +295,18 @@ extension TaskPriority { } ``` -Because move-only types in the first early iteration of this language feature still have a number of limitations, we also offer an `UnownedJob` type, that is an unsafe "unowned" version of a `Job`. One reason one might need to reach for an `UnownedJob` is whenever a `Job` were to be used in a generic context, because in the initial version of move-only types that is available today, such types cannot appear in a generic context. For example, a naive queue implementation using an `[Job]` would be rejected by the compiler, but it is possible to express using an UnownedJob (i.e.`[UnownedJob]`). +Because move-only types in the first early iteration of this language feature still have a number of limitations, we also offer an `UnownedExecutorJob` type, that is an unsafe "unowned" version of a `ExecutorJob`. One reason one might need to reach for an `UnownedExecutorJob` is whenever a `ExecutorJob` were to be used in a generic context, because in the initial version of move-only types that is available today, such types cannot appear in a generic context. For example, a naive queue implementation using an `[ExecutorJob]` would be rejected by the compiler, but it is possible to express using an `UnownedExecutorJob` (i.e.`[UnownedExecutorJob]`). ```swift -public struct UnownedJob: Sendable, CustomStringConvertible { +public struct UnownedExecutorJob: Sendable, CustomStringConvertible { - /// Create an unsafe, unowned, job by consuming a move-only Job. + /// Create an unsafe, unowned, job by consuming a move-only ExecutorJob. /// /// This may be necessary currently when intending to store a job in collections, /// or otherwise intreracting with generics due to initial implementation /// limitations of move-only types. @usableFromInline - internal init(_ job: consuming Job) { ... } + internal init(_ job: consuming ExecutorJob) { ... } public var priority: JobPriority { ... } @@ -314,19 +316,19 @@ public struct UnownedJob: Sendable, CustomStringConvertible { A job's description includes its job or task ID, that can be used to correlate it with task dumps as well as task lists in Instruments and other debugging tools (e.g. `swift-inspect`'s ). A task ID is an unique number assigned to a task, and can be useful when debugging scheduling issues, this is the same ID that is currently exposed in tools like Instruments when inspecting tasks, allowing to correlate debug logs with observations from profiling tools. -Eventually, an executor will want to actually run a job. It may do so right away when it is enqueued, or on some different thread, this is entirely left up to the executor to decide. Running a job is done by calling the `runSynchronously` on a `Job` which consumes it. The same method is provided on the `UnownedJob` type, however that API is not as safe, since it cannot consume the job, and is open to running the same job multiple times accidentally which is undefined behavior. Generally, we urge developers to stick to using `Job` APIs whenever possible, and only move to the unowned API if the noncopyable `Job`s restrictions prove too strong to do the necessary operations on it. +Eventually, an executor will want to actually run a job. It may do so right away when it is enqueued, or on some different thread, this is entirely left up to the executor to decide. Running a job is done by calling the `runSynchronously` on a `ExecutorJob` which consumes it. The same method is provided on the `UnownedExecutorJob` type, however that API is not as safe, since it cannot consume the job, and is open to running the same job multiple times accidentally which is undefined behavior. Generally, we urge developers to stick to using `ExecutorJob` APIs whenever possible, and only move to the unowned API if the noncopyable `ExecutorJob`s restrictions prove too strong to do the necessary operations on it. ```swift -extension Job { +extension ExecutorJob { /// Run the job synchronously. /// /// This operation consumes the job. public consuming func runSynchronously(on executor: UnownedSerialExecutor) { - _swiftJobRun(UnownedJob(job), executor) + _swiftJobRun(UnownedExecutorJob(job), executor) } } -extension UnownedJob { +extension UnownedExecutorJob { /// Run the job synchronously. /// /// A job can only be run *once*. Accessing the job after it has been run is undefined behavior. @@ -363,11 +365,32 @@ public protocol Actor: AnyActor { public protocol DistributedActor: AnyActor { /// Retrieve the executor for this distributed actor as an optimized, - /// unowned reference. This API is equivalent to ``Actor/unownedExecutor``, - /// however, by default, it intentionally returns `nil` if this actor is a reference - /// to a remote distributed actor, because the executor for remote references - /// is effectively never accessed nor can code be `isolated` to run on a remote - /// actor reference. + /// unowned reference. This API is equivalent to ``Actor/unownedExecutor``. + /// + /// ## Executor of remote distributed actor reference + /// + /// The default implementation of the `unownedExecutor` uses a special "crash if enqueued on" + /// executor, that can be obtained using `buildDefaultDistributedRemoteActorExecutor(any DistributedActor)` + /// method. If implementing a custom executor of a distributed actor, the implementation may derive + /// its executor value from the `nonisolated var id` every actor possesses (e.g. by means of the `ID` + /// indicating some "executor preference"), however if the actor is remote, the implementation SHOULD + /// return the default remote distributed actor executor, same as the default implementation does. + /// + /// Even if a remote distributed actor reference were to return some shared executor, + /// the Swift runtime will never actively make use of it, because code in this process + /// never runs methods which can be called cross-actor isolated "on" such distributed actor, + /// but merely delegates to the ``DistributedActorSystem/remoteCall` to perform the remote call. + /// This call is performed on the actor system, and is not isolated to the actor. + /// + /// Returning a shared executor for a remote distributed actor reference will not "trick" the + /// swift runtime into wrongly allowing one to `assumeIsolated()` and run code isolated on a + /// remote actor, because a remote actor reference cannot ever be `isolated` with. + /// + /// ## Availability + /// + /// Distributed actors can only use custom executors if their availability requires + /// a platform with Swift 5.9 (or higher) present. On platforms without availability + /// annotations, a distributed actor may always /// /// ## Custom implementation requirements /// @@ -380,7 +403,7 @@ public protocol DistributedActor: AnyActor { /// eliminated, and rearranged with other work, and they may even /// be introduced when not strictly required. Visible side effects /// are therefore strongly discouraged within this property. - nonisolated var localUnownedExecutor: UnownedSerialExecutor? { get } + nonisolated var unownedExecutor: UnownedSerialExecutor { get } } ``` @@ -392,7 +415,7 @@ Developers can customize the executor used by an actor on a declaration-by-decla ```swift (distributed) actor MainActorsBestFriend { - nonisolated var localUnownedExecutor: UnownedSerialExecutor { + nonisolated var unownedExecutor: UnownedSerialExecutor { MainActor.sharedUnownedExecutor } func greet() { @@ -412,7 +435,7 @@ func test() { } ``` -The snippet above illustrates that while the `MainActor` and the `MainActorsBestFriend` are different actors, and thus are generally allowed to execute concurrently... because they *share* the same main actor (main thread) serial executor, they will never execute concurrently. +The snippet above illustrates that while the `MainActor` and the `MainActorsBestFriend` are different actors, and thus are generally allowed to execute concurrently, because they *share* the same main actor serial executor, they will never execute concurrently. A serial executor can only run one task at any given time, which enforces the mutual exclusive execution of those two actors. It is also possible for libraries to offer protocols where a default, library specific, executor is already defined, like this: @@ -436,13 +459,13 @@ extension LibrarySpecificActor { /// Ways to efficiently avoid hops when not necessary, will be offered as part of the /// "executor switching" feature, that is not part of this proposal. final class InlineExecutor: SpecifiedExecutor, CustomStringConvertible { - public func enqueue(_ job: __owned Job) { + public func enqueue(_ job: __owned ExecutorJob) { runJobSynchronously(job) } } ``` -Which ensures that users of such library implementing such actors provide the library specific executor for their actors: +Which ensures that users of such library implementing such actors provide the library specific `SpecificExecutor` for their actors: ```swift actor MyActor: WithSpecifiedExecutor { @@ -531,24 +554,21 @@ as well as an `assert...` version of this API, which triggers only in `debug` bu ```swift extension SerialExecutor { // Same as ``SerialExecutor/preconditionIsolated(_:file:line)`` however only in DEBUG mode. - public func assertOnExecutor( - _ executor: some SerialExecutor, + public func assertIsolated( _ message: @autoclosure () -> String = "", file: String = #fileID, line: UInt = #line) } extension Actor { // Same as ``Actor/preconditionIsolated(_:file:line)`` however only in DEBUG mode. - public nonisolated func assertOnExecutor( - _ executor: some SerialExecutor, + public nonisolated func assertIsolated( _ message: @autoclosure () -> String = "", file: String = #fileID, line: UInt = #line) } extension DistributedActor { // Same as ``DistributedActor/preconditionIsolated(_:file:line)`` however only in DEBUG mode. - public nonisolated func assertOnExecutor( - _ executor: some SerialExecutor, + public nonisolated func assertIsolated( _ message: @autoclosure () -> String = "", file: String = #fileID, line: UInt = #line) } @@ -657,24 +677,27 @@ In addition to the `MainActor` specialized API, the same shape of API is offered extension Actor { /// A safe way to synchronously assume that the current execution context belongs to the passed in `actor`. /// + /// If currently executing in the context of the actor's serial executor, safely execute the `operation` + /// isolated to the actor. Otherwise, crash reporting the difference in expected and actual executor. + /// /// This method cannot be used in an asynchronous context. Instead, prefer implementing - /// a method on the distributed actor and calling it from your asynchronous context. + /// a method on the actor and calling it from your asynchronous context. /// /// This API should only be used as last resort, when it is not possible to express the current /// execution context definitely belongs to the main actor in other ways. E.g. one may need to use /// this in a delegate style API, where a synchronous method is guaranteed to be called by the /// main actor, however it is not possible to move some function implementation onto the target - /// `distributed actor` for some reason. + /// `actor` for some reason. /// - /// - Warning: If the current executor is *not* the actor's serial executor, and the actor is local, this function will crash. + /// - Warning: If the current executor is *not* the actor's serial executor this function will crash. /// /// - Parameters: - /// - operation: the operation that will run if the actor was local, and the current executor is the same as of the passed in actors + /// - operation: the operation that will run if the executor checks pass /// - Returns: the result of the operation /// - Throws: the error the operation has thrown @available(*, noasync) func assumeIsolated( - _ operation: (isolated Act) throws -> T, + _ operation: (isolated Self) throws -> T, file: StaticString = #fileID, line: UInt = #line ) rethrows -> T } @@ -688,24 +711,34 @@ The same method is offered for distributed actors, where code can only ever be i extension DistributedActor { /// A safe way to synchronously assume that the current execution context belongs to the passed in `actor`. /// + /// If currently executing in the context of the actor's serial executor, safely execute the `operation` + /// isolated to the actor. If the actor is local, or the current and expected executors are not compatible, + /// crash reporting the difference in expected and actual executor. + /// /// This method cannot be used in an asynchronous context. Instead, prefer implementing /// a method on the distributed actor and calling it from your asynchronous context. /// + /// The actor must be a local distributed actor reference, as isolating execution to a remote reference + /// would not be memory safe, since a distributed remote actor reference is allowed to not allocate any + /// memory for its storage, and thus, any attempts to access it are illegal. If the actor is remote, + /// this method will terminate with a fatal error. + /// /// This API should only be used as last resort, when it is not possible to express the current /// execution context definitely belongs to the main actor in other ways. E.g. one may need to use /// this in a delegate style API, where a synchronous method is guaranteed to be called by the /// main actor, however it is not possible to move some function implementation onto the target /// `distributed actor` for some reason. /// - /// - Warning: If the current executor is *not* the actor's serial executor, and the actor is local, this function will crash. + /// - Warning: If the current executor is *not* compatible with the expected serial executor, + /// or the distributed actor is a remote reference, this function will crash. /// /// - Parameters: - /// - operation: the operation that will run if the actor was local, and the current executor is the same as of the passed in actors + /// - operation: the operation that will run if the executor checks pass /// - Returns: the result of the operation /// - Throws: the error the operation has thrown @available(*, noasync) func assumeIsolated( - _ operation: (isolated Act) throws -> T, + _ operation: (isolated Self) throws -> T, file: StaticString = #fileID, line: UInt = #line ) rethrows -> T } @@ -713,7 +746,7 @@ extension DistributedActor { ### Details of "same executor" checking -The previous two sections described the various `assert`, `precondition` and `assume` APIs all of which depend on the notion of "the same serial execution context". By default, every actor gets its own serial executor instance, and each such instance is unique. Therefore without sharing executors, each actor's serial executor is unique to itself, and thus the `precondition` APIs would efffectively check "are we on this _specific_ actor" even though the check is performed against the executor identity. +The previous two sections described the various `assert`, `precondition` and `assume` APIs all of which depend on the notion of "the same serial execution context". By default, every actor gets its own serial executor instance, and each such instance is unique. Therefore without sharing executors, each actor's serial executor is unique to itself, and thus the `precondition` APIs would effectively check "are we on this _specific_ actor" even though the check is performed against the executor identity. #### Unique executors delegating to the same SerialExecutor @@ -730,7 +763,7 @@ final class UniqueSpecificThreadExecutor: SerialExecutor { self.delegate = delegate } - func enqueue(_ job: consuming Job) { + func enqueue(_ job: consuming ExecutorJob) { delegate.enqueue(job) } @@ -869,7 +902,7 @@ The default global concurrent executor is not accessible direcly from code, howe Many of these APIs are existing public types since the first introduction of Swift Concurrency (and are included in back-deployment libraries). As all types and pieces of this proposal are designed in a way that allows to keep source and behavioral compatibility with already existing executor APIs. -Special affordances are taken to introduce the move-only Job based enqueue API in an source compatible way, while deprecating the previously existing ("unowned") API. +Special affordances are taken to introduce the move-only ExecutorJob based enqueue API in an source compatible way, while deprecating the previously existing ("unowned") API. ## Effect on ABI stability From abaf1ee2bd208b0b1bdc7388e3e6ec5e196d90e7 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Fri, 7 Apr 2023 11:36:00 -0400 Subject: [PATCH 3076/4563] Update status of SE-0392 for second review --- proposals/0392-custom-actor-executors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index 6ec51551c9..a39efa0f88 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -3,7 +3,7 @@ - Proposal: [SE-0392](0392-custom-actor-executors.md) - Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [John McCall](https://github.com/rjmccall), [Kavon Farvardin](https://github.com/kavon) - Review Manager: [Joe Groff](https://github.com/jckarter) -- Status: **Active review (March 8 ... March 21, 2023)** +- Status: **Active review (April 7 ... April 17, 2023)** - Implementation: Partially implemented on `main` - Previous threads: - Original pitch thread from around Swift 5.5: [Support custom executors in Swift Concurrency](https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425) From 570483039edb8d23cb792ee68c85e85453f88931 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Mon, 10 Apr 2023 13:10:14 -0700 Subject: [PATCH 3077/4563] Update SE-0384 to specify the Upcoming Feature Flag. This feature flag was merged in https://github.com/apple/swift/pull/64746, to align with other upcoming Swift 6 features. --- ...importing-forward-declared-objc-interfaces-and-protocols.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md index 1f5d64fe4f..e078d420a5 100644 --- a/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md +++ b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md @@ -5,6 +5,7 @@ * Review Manager: Tony Allevato (https://github.com/allevato) * Status: **Implemented (Swift 5.9)** * Implementation: https://github.com/apple/swift/pull/61606 +* Upcoming Feature Flag: `ImportObjcForwardDeclarations` * Review: ([pitch](https://forums.swift.org/t/pitch-importing-forward-declared-objective-c-classes-and-protocols/61926)) ([review](https://forums.swift.org/t/se-0384-importing-forward-declared-objective-c-interfaces-and-protocols/62392)) ([acceptance](https://forums.swift.org/t/accepted-se-0384-importing-forward-declared-objective-c-interfaces-and-protocols/62670)) ## Introduction @@ -149,7 +150,7 @@ foo-bar-consumer.h:3:1: note: interface 'Foo' forward declared here ^ ``` -The feature is gated behind a new frontend flag `-enable-import-objc-forward-declarations`. This flag is on by default for Swift version 6 and onwards. +In Swift 5.x, the feature is gated behind the upcoming feature flag `ImportObjcForwardDeclarations`. This flag is on by default for Swift version 6 and onwards. The flag is always disabled in the REPL, as allowing it currently leads to confusing behavior. In the REPL, the environment in terms of whether a complete definition of some type Foo is available can change during execution. These examples show some of the confusing behavior: From 4b70d0110cb97d08a37c798bf6f946d035b53fa8 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Tue, 11 Apr 2023 12:26:41 -0700 Subject: [PATCH 3078/4563] Observation (#1998) * Initial posted draft of observation proposal * Fixup a few typos and expand the macro section to a bit more detail and incorperate some of the feedback from the initial pitch phase * spelling fix Co-authored-by: Remy Demarest * Additional prose around general introduction, dependencies (and possible options of the proposal) tracking, and ABI/API impact * spelling fix Co-authored-by: Remy Demarest * Add type annotations and a note about same * Some grammar fixes * Add detail about ObservedChanges and ObservedValues * Revisions, remove dependencies(for:) default impl * More revisions re Ben's feedback * Update proposals/NNNN-observability.md * Update and rename NNNN-observability.md to 0396-observability.md --------- Co-authored-by: Remy Demarest Co-authored-by: Nate Cook Co-authored-by: Ben Cohen --- proposals/0396-observability.md | 760 ++++++++++++++++++++++++++++++++ 1 file changed, 760 insertions(+) create mode 100644 proposals/0396-observability.md diff --git a/proposals/0396-observability.md b/proposals/0396-observability.md new file mode 100644 index 0000000000..2fdc8a1d1f --- /dev/null +++ b/proposals/0396-observability.md @@ -0,0 +1,760 @@ +# Observation + +* Proposal: [SE-0396](0396-observation.md) +* Authors: [Philippe Hausler](https://github.com/phausler), [Nate Cook](https://github.com/natecook1000) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Active Review (April 11 – April 24, 2023)** + +#### Changes + +* Pitch 1: [Initial pitch](https://forums.swift.org/t/pitch-observation/62051) +* Pitch 2: Previously Observation registered observers directly to `Observable`, the new approach registers observers to an `Observable` via a `ObservationTransactionModel`. These models control the "edge" of where the change is emitted. They are the responsible component for notifying the observers of events. This allows the observers to focus on just the event and not worry about "leading" or "trailing" (will/did) "edges" of the signal. Additionally the pitch was shifted from the type wrapper feature over to the more appropriate macro features. +* Pitch 3: The `Observer` protocol and `addObserver(_:)` method are gone in favor of providing async sequences of changes and transactions. + +#### Suggested Reading + +* [Expression Macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) +* [Attached Macros](https://github.com/DougGregor/swift-evolution/blob/attached-macros/proposals/nnnn-attached-macros.md) + +## Introduction + +Making responsive apps often requires the ability to update the presentation when underlying data changes. The _observer pattern_ allows a subject to maintain a list of observers and notify them of specific or general state changes. This has the advantages of not directly coupling objects together and allowing implicit distribution of updates across potential multiple observers. An observable object needs no specific information about its observers. + +This design pattern is a well-traveled path by many languages, and Swift has an opportunity to provide a robust, type-safe, and performant implementation. This proposal defines what an observable reference is, what an observer needs to conform to, and the connection between a type and its observers. + +## Motivation + +There are already a few mechanisms for observation in Swift. These include key-value observing (KVO) and `ObservableObject`, but each of those have limitations. KVO can only be used with `NSObject` descendants, and `ObservableObject` requires using Combine, which is restricted to Darwin platforms and does not use current Swift concurrency features. By taking experience from those existing systems, we can build a more generally useful feature that applies to all Swift reference types, not just those that inherit from `NSObject`, and have it work cross-platform with the advantages from language features like `async`/`await`. + +The existing systems get a number of behaviors and characteristics right. However, there are a number of areas that can provide a better balance of safety, performance, and expressiveness. For example, grouping dependent changes into an independent transaction is a common task, but this is complex when using Combine and unsupported when using KVO. In practice, observers want access to transactions, with the ability to specify how transactions are interpreted. + +Annotations clarify what is observable, but can also be cumbersome. For example, Combine requires not just that a type conform to `ObservableObject`, but also requires each property that is being observed to be marked as `@Published`. Furthermore, computed properties cannot be directly observed. In reality, having non-observed fields in a type that is observable is uncommon. + +Throughout this document, references to both KVO and Combine will illustrate what capabilities are benefits and can be incorporated into the new approach, and what drawbacks are possible to solve in a more robust manner. + +### Prior Art + +#### KVO + +Key-value observing has served the Cocoa/Objective-C programming model well, but is limited to class hierarchies that inherit from `NSObject`. The APIs only offer the intercepting of events, meaning that the notification of changes is between the `willSet` and `didSet` events. KVO has great flexibility with granularity of events, but lacks in composability. KVO observers must also inherit from `NSObject`, and rely on the Objective-C runtime to track the changes that occur. Even though the interface for KVO has been updated to utilize the more modern Swift strongly-typed key paths, under the hood its events are still stringly typed. + +#### Combine + +Combine's `ObservableObject` produces changes at the beginning of a change event, so all values are delivered before the new value is set. While this serves SwiftUI well, it is restrictive for non-SwiftUI usage and can be surprising to developers first encountering that behavior. `ObservableObject` also requires all observed properties to be marked as `@Published` to interact with change events. In most cases, this requirement is applied to every single property and becomes redundant to the developer; folks writing an `ObservableObject` conforming type must repeatedly (with little to no true gained clarity) annotate each property. In the end, this results in meaning fatigue of what is or isn't a participating item. + +## Proposed solution + +A formalized observer pattern needs to support the following capabilities: + +* Marking a type as observable +* Tracking changes within an instance of an observable type +* Observing and utilizing those changes from somewhere else, e.g. another type + +In addition, the design and implementation should meet these criteria: + +* Observable types are easy to annotate (without fatigue of meaning) +* Access control should be respected +* Adopting the features for observability should require minimal effort to get started +* Using advanced features should progressively disclose to more complex systems +* Observation should be able to handle more than one observed member at once +* Observation should be able to work with computed properties that reference other properties +* Observation should be able to work with computed properties that process both get and set to external storage +* Integration of observation should work in transactions of graphs and not just singular objects + +We propose a new standard library module named `Observation` that includes the protocols, types, and macros to implement such a pattern. + +Primarily, a type can declare itself as observable simply by using the `@Observable` macro annotation: + +```swift +@Observable public final class MyObject { + public var someProperty: String = "" + public var someOtherProperty: Int = 0 + fileprivate var somePrivateProperty: Int = 1 +} +``` + +The `@Observable` macro declares and implements conformance to the `Observable` protocol, which includes a set of extension methods to handle observation. The `Observable` protocol supports three ways to interact with changed properties: tracking specific values changes coalesced around iteration, tracking changes coalesced around a specific actor isolation, and interoperating with UI. The first two systems are intended for non-UI uses, while user interface libraries such as SwiftUI can use the last system to allow data changes to flow through to views. + +In the simplest case, a client can use the `values(for:)` method to observe changes to a specific property for a given instance. + +```swift +func processChanges(_ object: MyObject) async { + for await value in object.values(for: \.someProperty) { + print(value) + } +} +``` + +This allows users of `Observable` types to observe changes to specific values or an instance as a whole as asynchronous sequences of change events. Because the `values(for:)` method only observes a single property, it provides a sequence of elements of that property's type. + +```swift +object.someProperty = "hello" +// prints "hello" in the awaiting loop +object.someOtherProperty += 1 +// nothing is printed +``` + +Observable objects can also provide changes grouped into transactions, which coalesce any changes that are made between suspension points. Transactions are accessed using the `changes(for:)` method, and are delivered isolated to an actor that you provide, or the main actor by default. Because transactions can cover changes to more than one property, the `changes(for:)` method provides a sequence of `TrackedProperties` instances, which can be queried to find the changed properties. + +```swift +func processTransactions(_ object: MyObject) async { + for await change in objects.changes(for: [\.someProperty, \.someOtherProperty]) { + print(myObject.someProperty, myObject.someOtherProperty) + } +} +``` + +Unlike `ObservableObject` and `@Published`, the properties of an `@Observable` type do not need to be individually marked as observable. Instead, all stored properties are implicitly observable. + +For read-only computed properties, the static `dependencies(of:)` method is used to indicate additional key paths from which the property is computed. This is similar to the mechanism that KVO uses to provide additional key paths that have effects to key paths. + +```swift +extension MyObject { + var someComputedProperty: Int { + somePrivateProperty + someOtherProperty + } + + nonisolated static func dependencies( + of keyPath: PartialKeyPath + ) -> TrackedProperties { + switch keyPath { + case \.someComputedProperty: + return [\.somePrivateProperty, \.someOtherProperty] + default: + return [keyPath] + } + } +} +``` + +Since all access to observing changes is by key path, visibility keywords like `public` and `private` determine what can and cannot be observed. Unlike KVO, this means that only members that are accessible in a particular scope can be observed. This fact is reflected in the design, where transactions are represented as `TrackedProperties` instances, which allow querying for the changed key paths, but not their iteration. + +```swift +// ✅ `someProperty` is publicly visible +object.changes(for: \.someProperty) +// ❌ `somePrivateProperty` is restricted to `private` access +object.changes(for: \.somePrivateProperty) +// ✅ `someComputedProperty` is visible; `somePrivateProperty` isn't accessible in returned `TrackedProperties` instances +object.changes(for: \.someComputedProperty) +``` + +## Detailed Design + +The `Observable` protocol, `@Observable` macro, and a handful of supporting types comprise the `Observation` module. As described below, this design allows adopters to use terse, straightforward syntax for simple cases, while allowing full control over the details the implementation when necessary. + +### `Observable` protocol + +The core protocol for observation is `Observable`. `Observable`-conforming types define what is observable by registering changes and provide asynchronous sequences of transactions and changes to individual properties, isolated to a specific actor. + +```swift +protocol Observable { + /// Returns an asynchronous sequence of change transactions for the specified + /// properties. + nonisolated func changes( + for properties: TrackedProperties, + isolatedTo: Isolation + ) -> ObservedChanges + + /// Returns an asynchronous sequence of changes for the specified key path. + nonisolated func values( + for keyPath: KeyPath + ) -> ObservedValues + + /// Returns a set of tracked properties that represent the given key path. + nonisolated static func dependencies( + of keyPath: PartialKeyPath + ) -> TrackedProperties +} +``` + +These protocol requirements need to be implemented by conforming types, either manually or by using the `@Observable` macro, described below. The first two methods make use of `ObservationRegistrar` (*also* described below) to track changes and provide async sequences of changes and transactions. + +In addition to these protocol requirements, `Observable` types must implement the _semantic_ requirement of tracking each access and mutation to observable properties. This tracking is also provided by using the `@Observable` macro, or can be implemented manually using the `access` and `withMutation` methods of a registrar. + +The `Observable` protocol also implements extension methods that provide convenient access to transactions isolated to the main actor or tracking just a single key path. + +```swift +extension Observable { + /// Returns an asynchronous sequence of change transactions for the specified + /// key path, isolated to the given actor. + nonisolated func changes( + for keyPath: KeyPath, + isolatedTo: Isolation + ) -> ObservedChanges + + /// Returns an asynchronous sequence of change transactions for the specified + /// properties, isolated to the main actor. + nonisolated func changes( + for properties: TrackedProperties + ) -> ObservedChanges + + /// Returns an asynchronous sequence of change transactions for the specified + /// key path, isolated to the main actor. + public nonisolated func changes( + for keyPath: KeyPath + ) -> ObservedChanges + + // Default implementation returns `[keyPath]`. + public nonisolated static func dependencies( + of keyPath: PartialKeyPath + ) -> TrackedProperties +} +``` + +All `Observable` types must correctly handle their own exclusivity, thread safety, and/or isolation. For example, if two properties must change together, with no observation of a halfway state where one has changed and the other hasn't, then it is the type's responsibility to provide that atomicity. For example, for an observable type named `ImageDescription`, with `width` and `height` properties that must be updated in lockstep, the type must assign both properties without an interrupting suspension point: + +``` +@Observable class ImageDescription { + // ... + + func BAD_updateDimensions() async { + self.width = await getUpdatedWidth() + self.height = away getUpdatedHeight() + } + + func GOOD_updateDimensions() async { + let (width, height) = await (getUpdatedWidth(), getUpdatedHeight()) + self.width = width + self.height = height + } +} +``` + +Likewise, a sequence observing a specific property for its values is only `Sendable` if `Observable` type is itself `Sendable`. + +### Macro Synthesis + +In order to make implementation as simple as possible, the `@Observable` macro automatically synthesizes conformance to the `Observable` protocol, transforming annotated types into a type that can be observed. The `@Observable` macro does the following: + +- declares conformance to the `Observable` protocol +- adds the required `Observable` method requirements +- adds a property for the registrar +- adds a storage abstraction for access tracking +- changes all stored properties into computed properties +- adds an initializer for the properties (with default values if they apply) + +Since all of the code generated by the macro could be manually written, developers can write their own implementation when they need more fine-grained control. For example, the following `Model` type is annotated with the `@Observable` macro: + +```swift +@Observable final class Model { + var order: Order? + var account: Account? + + var alternateIconsUnlocked: Bool = false + var allRecipesUnlocked: Bool = false + + func purchase(alternateIcons: Bool, allRecipes: Bool) { + alternateIconsUnlocked = alternateIcons + allRecipesUnlocked = allRecipes + } +} +``` + +Expanding the macro results in the following declaration: + +```swift +final class Model: Observable { + internal let _$observationRegistrar = ObservationRegistrar() + + nonisolated func changes( + for properties: TrackedProperties, + isolatedTo: Delivery + ) -> ObservedChanges { + _$observationRegistrar.changes(for: properties, isolation: isolation) + } + + nonisolated func values( + for keyPath: KeyPath + ) -> ObservedValues { + _$observationRegistrar.changes(for: keyPath) + } + + private struct _$ObservationStorage { + var order: Order? + var account: Account? + + var alternateIconsUnlocked: Bool + var allRecipesUnlocked: Bool + } + + private var _$observationStorage: _$ObservationStorage + + init(order: Order? = nil, account: Account? = nil, alternateIconsUnlocked: Bool = false, allRecipesUnlocked: Bool = false) { + _$observationStorage = _$ObservationStorage(order: order, account: account, alternateIconsUnlocked: alternateIconsUnlocked, allRecipesUnlocked: allRecipesUnlocked) + } + + var order: Order? { + get { + _$observationRegistrar.access(self, keyPath: \.order) + return _$observationStorage.order + } + set { + _$observationRegistrar.withMutation(self, keyPath: \.order) { + _$observationStorage.order = newValue + } + } + } + + var account: Account? { + get { + _$observationRegistrar.access(self, keyPath: \.account) + return _$observationStorage.account + } + set { + _$observationRegistrar.withMutation(self, keyPath: \.account) { + _$observationStorage.account = newValue + } + } + } + + var alternateIconsUnlocked: Bool { + get { + _$observationRegistrar.access(self, keyPath: \.alternateIconsUnlocked) + return _$observationStorage.alternateIconsUnlocked + } + set { + _$observationRegistrar.withMutation(self, keyPath: \.alternateIconsUnlocked) { + _$observationStorage.alternateIconsUnlocked = newValue + } + } + } + + var allRecipesUnlocked: Bool { + get { + _$observationRegistrar.access(self, keyPath: \.allRecipesUnlocked) + return _$observationStorage.allRecipesUnlocked + } + set { + _$observationRegistrar.withMutation(self, keyPath: \.allRecipesUnlocked) { + _$observationStorage.allRecipesUnlocked = newValue + } + } + } + + nonisolated static func dependencies( + of keyPath: PartialKeyPath + ) -> TrackedProperties { + [keyPath] + } + + func purchase(alternateIcons: Bool, allRecipes: Bool) { + alternateIconsUnlocked = alternateIcons + allRecipesUnlocked = allRecipes + } +} +``` + +When a property does not have a default value, that corresponding argument in the initializer does not have a default value. This means that the following example has a macro synthesized initializer of `init(a: Int, b: Int = 3)`. + +```swift +@Observable final class InitializationSample { + var a: Int + var b: Int = 3 +} +``` + +Because the memberwise initializer and backing storage are generated together, the initializer is able to initialize that storage. User-defined initializers should call the generated memberwise initializer instead of attempting to initialize the properties directly. + +Since macros are only syntactic transformations, all fields must have explicit type information. This restriction may be able to be lifted later when the type system grows a mechanism to detect inferred types. + +```swift +@Observable final class InitializationSample { + var a: Int + var b = 3 // this will emit an error: "@Observable requires properties to have type annotations. b is missing a non-inferred type" +} +``` + +Properties that have `willSet` and `didSet` property observations are supported. For example, the `@Observable` macro on the `PropertyExample` type here: + +```swift +@Observable final class PropertyExample { + var a: Int { + willSet { print("will set triggered") } + didSet { print("did set triggered") } + } + var b: Int = 0 + var c: String = "" +} +``` + +...transforms the property `a` as follows: + +```swift +var a: Int { + get { + _$observationRegistrar.access(self, keyPath: \.a) + return _$observationStorage.a + } + set { + print("will set triggered") + _$observationRegistrar.withMutation(of: self, keyPath: \.a) { + _$observationStorage.a = newValue + } + print("did set triggered") + } +} +``` + +The generated implementation for the `dependencies(of:)` requirement creates a list of all of the type's stored properties, and then returns that as the set of dependencies for any computed property. For the `PropertyExample` type above, that implementation looks like this: + +```swift +nonisolated static func dependencies( + of keyPath: PartialKeyPath +) -> TrackedProperties { + let storedProperties: [PartialKeyPath] = [\.b, \.c] + switch keyPath { + case \.a: + return TrackedProperties(storedProperties + [keyPath]) + default: + return [keyPath] + } +} +``` + +The `@Observable` macro omits the implementation if one already exists, so a type can provide a more selective implementation if necessary. + +### `TrackedProperties` + +When observing changes to a type, there may be associated side effects to members that are not publicly visible. The `TrackedProperties` type allows for internal key paths to be included in a transaction without being exposed beyond their visibility. + +```swift +public struct TrackedProperties: ExpressibleByArrayLiteral, @unchecked Sendable { + public typealias ArrayLiteralElement = PartialKeyPath + + public init() + + public init(_ sequence: some Sequence>) + + public init(arrayLiteral elements: PartialKeyPath...) + + public func contains(_ member: PartialKeyPath) -> Bool + + public mutating func insert(_ newMember: PartialKeyPath) -> Bool + + public mutating func remove(_ member: PartialKeyPath) +} + +extension TrackedProperties where Root: Observable { + public init(dependent: TrackedProperties) +} +``` + +### `ObservationRegistrar` + +`ObservationRegistrar` is the default storage for tracking and providing access to changes. The `@Observable` macro synthesizes a registrar to handle these mechanisms as a generalized feature. By default, the registrar is thread safe and must be as `Sendable` as containers could potentially be; therefore it must be designed to handle independent isolation for all actions. + +```swift +public struct ObservationRegistrar: Sendable { + public init() + + public func access( + _ subject: Subject, + keyPath: KeyPath + ) + + public func willSet( + _ subject: Subject, + keyPath: KeyPath + ) + + public func didSet( + _ subject: Subject, + keyPath: KeyPath + ) + + public func withMutation( + of subject: Subject, + keyPath: KeyPath, + _ mutation: () throws -> T + ) rethrows -> T + + public func changes( + for properties: TrackedProperties, + isolatedTo: Isolation + ) -> ObservedChanges + + public func values( + for keyPath: KeyPath + ) -> ObservedValues +} +``` + +The `access` and `withMutation` methods identify transactional accesses. These methods register access to the `ObservationTracking` system for access and identify mutations to the transactions registered for observers. + +### `ObservationTracking` + +In order to provide scoped observation, the `ObservationTracking` type provides a method to capture accesses to properties within a given scope and then call out upon the first change to any of those properties. This API is the primary mechanism in which UI interactions can be built. Specifically, this can be used by user interface libraries like SwiftUI to provide updates to properties that are accessed in a specific scope. For more detail, see the SDK Impact section below. + +```swift +public struct ObservationTracking { + public static func withTracking( + _ apply: () -> T, + onChange: @autoclosure () -> @Sendable () -> Void + ) -> T +} +``` + +The `withTracking` method takes two closures where any access to any property within the apply closure will indicate that property on that specific instance should participate in changes informed to the `onChange` closure. + +```swift +@Observable final class Car { + var name: String + var awards: [Award] +} + +let cars: [Car] = ... + +func render() { + ObservationTracking.withTracking { + for car in cars { + print(car.name) + } + } onChange { + scheduleRender() + } +} +``` + +In the example above, the `render` function accesses each car's `name` property. When any of the cars change `name`, the `onChange` closure is then called on the first change. However, if a car has an award added, the `onChange` call won't happen. This design supports uses that require implicit observation tracking, such as SwiftUI, ensuring that views are only updated in response to relevant changes. + +### `ObservedChanges` and `ObservedValues` + +The two included asynchronous sequences provide access to transactions based on a `TrackedProperties` instance or to changes based on a key path, respectively. The two sequences have slightly different semantics. Both of these asynchronous sequences are intended for primary use by non-UI systems. + +The `ObservedChanges` sequence is the result of calling `changes(for:isolatedTo:)` on an observable type and passing one or more key paths as a `TrackedProperties` instance. The isolating actor that you pass (or the main actor, by default) determines how changes are coalesced. Any changes between suspension points on the isolating actor, whether to one property or multiple properties, only provide a single transaction event. Sequence elements are `ObservedChange` instances, which you can query to see if a _specific_ property has changed. When the observed type is `Sendable`, an `ObservedChange` also includes the observed subject, in order to simplify accessing the updated properties. + +The `ObservedValues` sequence, on the other hand, is the result of calling `values(for:)`, passing a single key path to observe. Instead of coalescing changes in reference to an isolating actor, `ObservedValues` provides changed values that are coalesced at each suspension during iteration. Since the iterator isn't `Sendable`, that behavior implicitly isolates changes to the current actor. + +```swift +public struct ObservedChange: @unchecked Sendable { + public func contains(_ member: PartialKeyPath) -> Bool +} + +extension ObservedChange where Subject: Sendable { + public var subject: Subject { get } +} + +/// An asynchronous sequence of observed changes. +public struct ObservedChanges: AsyncSequence { + public typealias Element = ObservedChange + + public struct Iterator: AsyncIteratorProtocol { + public mutating func next() async -> Element? + } + + public func makeAsyncIterator() -> Iterator +} + +extension ObservedChanges: @unchecked Sendable where Subject: Sendable { } +@available(*, unavailable) +extension ObservedChanges.Iterator: Sendable { } + +/// An asynchronous sequence of observed changes. +public struct ObservedValues: AsyncSequence { + public struct Iterator: AsyncIteratorProtocol { + public mutating func next() async -> Element? + } + + public func makeAsyncIterator() -> Iterator +} + +extension ObservedValues: @unchecked Sendable where Subject: Sendable { } +@available(*, unavailable) +extension ObservedChanges.Iterator: Sendable { } +``` + + +`ObservedChanges` emits values when a change is made and an available suspension point has been met by the isolation. This means that changes can be grouped up transactionally to the boundary of when a given actor is available to run. Common use cases for this will iterate this `AsyncSequence` on the same actor as it is claiming for isolation. + +```swift +@Observable final class ChangesExample { + @MainActor var someField: Int + @MainActor var someOtherField: String +} + +@MainActor +func handleChanges(_ example: ChangesExample) { + Task { @MainActor in + for await change in example.changes(for: [\.someField, \.someOtherField]) { + switch (change.contains(\.someField), change.contains(\.someOtherField)) + case (true, true): + print("Changed both properties") + case (true, false): + print("changed integer field") + case (false, true): + print("changed string field") + default: + fatalError("this case will never happen") + } + } + } +} +``` + +The propagation of the `@MainActor` serves two purposes; to allow for access to the subject of observation safely and to coalesce changes around that same actor. If the subject of observation is not `Sendable` then the task creation will emit a concurrency warning that the item is being sent across an actor boundary. This also applies to the `ObservedChanges` `AsyncSequence` since the `Sendable` conformance is conditional upon the subject of observation's conformance to `Sendable`. This means that the potential of misuse of iterating changes on a different actor emits a warning (and in future Swift releases this will be an error). + +The mechanism to coalesce is based upon the suspension provided by the isolation. When changes are being iterated the tracked properties of the given `Observable` are gathered until a continuation is resumed from the suspension of the isolation. This means that the boundary of the collection starts at the leading edge of any given set of changes (willSet) with an ending of the transaction gated by the suspension of the isolating actor. If the scope of the iteration of `changes` is bound to a given actor like the example above it means that those changes are coalescing for that previous region. Provided the subject of observation is not violating actor isolation then the values should reflect the next available slot within the scheduling of that actor. In short; given the main actor, the changes that occur will be gathered up until the next tick of the run loop and usable for that object in that asynchronous context. + +The `ObservedValues` asynchronous sequence on the other hand will await a change to values end emit the latest value that is available between the last iteration (or starting point of iteration) and the suspension. Similar to the requirement of `Sendable` to `ObservedChanges` the `ObservedValues` async sequence is conditional; meaning that only cases where the observable subject is `Sendable` can the `AsyncSequence` of values be `Sendable`. However there is one extra requirement; that the type of the value being observed must also be `Sendable`. + +```swift +@Observable final class ValuesExample { + @MainActor var someField: Int +} + +@MainActor +func processValues(_ example: ValuesExample) { + Task { @MainActor in + for await value in example.values(for: \.someField) { + print(value) + } + } +} +``` + +In the example above if the `someField` property is changed in succession on the main actor without a suspend in between the awaiting continuation cannot resume in-between the values being set. This means that only the latest value is emitted. If the iteration is being done in a different actor the changes that might occur in-between calls to the iterator's next function will also be coalesced. + +It is worth noting that making a type observable does not provide any automatic atomicity of individual values or groups of values. Changes as such need to be managed by the implementor of the observed type. That management is one of the responsibilities that types need to account for when declaring conformance to `Sendable`. + +## SDK Impact (a preview of SwiftUI interaction) + +When using the existing `ObservableObject`-based observation, there are a number of edge cases that can be surprising unless developers have an in-depth understanding of SwiftUI. Formalizing observation can make these edge cases considerably more approachable by reducing the complexity of the different systems needed to be understood. + +The following is adapted from the [Fruta sample app](https://developer.apple.com/documentation/swiftui/fruta_building_a_feature-rich_app_with_swiftui), modified for clarity: + +```swift +final class Model: ObservableObject { + @Published var order: Order? + @Published var account: Account? + + var hasAccount: Bool { + return userCredential != nil && account != nil + } + + @Published var favoriteSmoothieIDs = Set() + @Published var selectedSmoothieID: Smoothie.ID? + + @Published var searchString = "" + + @Published var isApplePayEnabled = true + @Published var allRecipesUnlocked = false + @Published var unlockAllRecipesProduct: Product? +} + +struct SmoothieList: View { + var smoothies: [Smoothie] + @ObservedObject var model: Model + + var listedSmoothies: [Smoothie] { + smoothies + .filter { $0.matches(model.searchString) } + .sorted(by: { $0.title.localizedCompare($1.title) == .orderedAscending }) + } + + var body: some View { + List(listedSmoothies) { smoothie in + ... + } + } +} +``` + +The `@Published` attribute identifies each field that participates in changes in the object, but it does not provide any differentiation or distinction as to the source of changes. This unfortunately results in additional layouts, rendering, and updates. + +The proposed API not only reduces the `@Published` repetition, but also simplifies the SwiftUI view code too! With the proposed `@Observable` macro, the previous example can instead be written as the following: + +```swift +@Observable final class Model { + var order: Order? + var account: Account? + + var hasAccount: Bool { + return userCredential != nil && account != nil + } + + var favoriteSmoothieIDs: Set = [] + var selectedSmoothieID: Smoothie.ID? + + var searchString: String = "" + + var isApplePayEnabled: Bool = true + var allRecipesUnlocked: Bool = false + var unlockAllRecipesProduct: Product? +} + +struct SmoothieList: View { + var smoothies: [Smoothie] + var model: Model + + var listedSmoothies: [Smoothie] { + smoothies + .filter { $0.matches(model.searchString) } + .sorted(by: { $0.title.localizedCompare($1.title) == .orderedAscending }) + } + + var body: some View { + List(listedSmoothies) { smoothie in + ... + } + } +} +``` + +There are some other interesting differences that follow from using the proposed observation system. For example, tracking observation of access within a view can be applied to an array, an optional, or even a custom type. This opens up new and interesting ways that developers can utilize SwiftUI more easily. + +This is a potential future direction for SwiftUI, but is not part of this proposal. + +## Source compatibility + +This proposal is additive and provides no impact to existing source code. + +## Effect on ABI stability + +This proposal is additive and no impact is made upon existing ABI stability. This does have implication to the marking of inline to functions and back-porting of this feature. In the cases where it is determined to be performance critical to the distribution of change events the methods will be marked as inlineable. + +Changing a type from not observable to `@Observable` has the same ABI impact as changing a property from stored to computed (which is not ABI breaking). Removing `@Observable` not only transitions from computed to stored properties but also removes a conformance (which is ABI breaking). + +## Effect on API resilience + +This proposal is additive and no impact is made upon existing API resilience. The types that adopt `@Observable` cannot remove it without breaking API contract. + +## Location of API + +This API will be housed in a module that is part of the swift language but outside of the standard library. To use this module `import Observation` must be used (and provisionally using the preview `import _Observation`). + +## Future Directions + +The initial implementation will not track changes for key paths that have more than one layer of components. For example, key paths such as `\.account` would work, but `\.account.name` would not. This feature would be possible as soon as the standard library offers a mechanism to iterate components of a key path. Since there is no way to determine this yet, key paths that have more than one component will never observe any changes. + +Another area of focus for future enhancements is support for observable `actor` types. This would require specific handling for key paths that currently does not exist for actors. + +The current requirement that all stored properties declarations include a type could be lifted in the future, if macros are able to provide semantic type information for properties. This would allow property declarations like `var a = 3`. + +Future macro features could potentially permit refinements to the `dependencies(of:)` implementation generated by the `@Observable` macro, to more accurately select the stored properties that each computed property depends on. + +Finally, once variadic generics are available, an observation mechanism could be added to observe multiple key paths as an `AsyncSequence`. + +## Alternatives considered + +An earlier consideration instead of defining transactions used direct will/did events to the observer. This, albeit being more direct, promoted mechanisms that did not offer the correct granularity for supporting the required synchronization between dependent members. It was determined that building transactions are worth the extra complexity to encourage developers using the API to consider what models for transactionality they need, instead of thinking just in terms of will/did events. + +Another design included an `Observer` protocol that could be used to build callback-style observer types. This has been eliminated in favor of the `AsyncSequence` approach. + +The `ObservedChange` type could have the `Sendable` requirement relaxed by making the type only conditionally `Sendable` and then allowing access to the subject in all cases; however this poses some restriction to the internal implementations and may have a hole in the sendable nature of the type. Since it is viewed that accessing values is most commonly by one property the values `AsyncSequence` fills most of that role and for cases where more than one field is needed to be accessed on a given actor the iteration can be done with a weak reference to the observable subject. + +## Acknowledgments + +* [Holly Borla](https://github.com/hborla) - For providing fantastic ideas on how to implement supporting infrastructure to this pitch +* [Pavel Yaskevich](https://github.com/xedin) - For tirelessly iterating on prototypes for supporting compiler features +* Rishi Verma - For bouncing ideas and helping with the design of integrating this idea into other work +* [Kyle Macomber](https://github.com/kylemacomber) - For connecting resources and providing useful feedback +* Matt Ricketson - For helping highlight some of the inner guts of SwiftUI + +## Related systems + +* [Swift `Combine.ObservableObject`](https://developer.apple.com/documentation/combine/observableobject/) +* [Objective-C Key Value Observing](https://developer.apple.com/documentation/objectivec/nsobject/nskeyvalueobserving?language=objc) +* [C# `IObservable`](https://learn.microsoft.com/en-us/dotnet/api/system.iobservable-1?view=net-6.0) +* [Rust `Trait rx::Observable`](https://docs.rs/rx/latest/rx/trait.Observable.html) +* [Java `Observable`](https://docs.oracle.com/javase/7/docs/api/java/util/Observable.html) +* [Kotlin `observable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-delegates/observable.html) From ce2888ea01490e1879bcc79c6db183479ed8f0c1 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 11 Apr 2023 12:28:54 -0700 Subject: [PATCH 3079/4563] Update and rename 0396-observability.md to 0395-observability.md --- proposals/{0396-observability.md => 0395-observability.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{0396-observability.md => 0395-observability.md} (99%) diff --git a/proposals/0396-observability.md b/proposals/0395-observability.md similarity index 99% rename from proposals/0396-observability.md rename to proposals/0395-observability.md index 2fdc8a1d1f..6570395c82 100644 --- a/proposals/0396-observability.md +++ b/proposals/0395-observability.md @@ -1,6 +1,6 @@ # Observation -* Proposal: [SE-0396](0396-observation.md) +* Proposal: [SE-0395](0395-observation.md) * Authors: [Philippe Hausler](https://github.com/phausler), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Active Review (April 11 – April 24, 2023)** From f17ac7604911350ed32b9617502d3e3c5b257c98 Mon Sep 17 00:00:00 2001 From: dev-nordicio <106622056+dev-nordicio@users.noreply.github.com> Date: Tue, 11 Apr 2023 22:55:59 +0200 Subject: [PATCH 3080/4563] Capitalize Swift (#2009) --- proposals/0395-observability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0395-observability.md b/proposals/0395-observability.md index 6570395c82..0fb498d933 100644 --- a/proposals/0395-observability.md +++ b/proposals/0395-observability.md @@ -720,7 +720,7 @@ This proposal is additive and no impact is made upon existing API resilience. Th ## Location of API -This API will be housed in a module that is part of the swift language but outside of the standard library. To use this module `import Observation` must be used (and provisionally using the preview `import _Observation`). +This API will be housed in a module that is part of the Swift language but outside of the standard library. To use this module `import Observation` must be used (and provisionally using the preview `import _Observation`). ## Future Directions From 5e45bc797651d7ba08f7f5bbe809ec81205cfbf9 Mon Sep 17 00:00:00 2001 From: Zev Eisenberg Date: Tue, 11 Apr 2023 16:56:20 -0400 Subject: [PATCH 3081/4563] Add syntax highlighting and fix typo. (#2008) --- proposals/0395-observability.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0395-observability.md b/proposals/0395-observability.md index 0fb498d933..ee8b49266d 100644 --- a/proposals/0395-observability.md +++ b/proposals/0395-observability.md @@ -203,13 +203,13 @@ extension Observable { All `Observable` types must correctly handle their own exclusivity, thread safety, and/or isolation. For example, if two properties must change together, with no observation of a halfway state where one has changed and the other hasn't, then it is the type's responsibility to provide that atomicity. For example, for an observable type named `ImageDescription`, with `width` and `height` properties that must be updated in lockstep, the type must assign both properties without an interrupting suspension point: -``` +```swift @Observable class ImageDescription { // ... func BAD_updateDimensions() async { self.width = await getUpdatedWidth() - self.height = away getUpdatedHeight() + self.height = await getUpdatedHeight() } func GOOD_updateDimensions() async { From 2c2873c34391c044f9722e55b3b7858df4fc33df Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 12 Apr 2023 11:28:55 +0100 Subject: [PATCH 3082/4563] Update 0395-observability.md (#2011) Fix the proposal ID link. --- proposals/0395-observability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0395-observability.md b/proposals/0395-observability.md index ee8b49266d..3cb7080b81 100644 --- a/proposals/0395-observability.md +++ b/proposals/0395-observability.md @@ -1,6 +1,6 @@ # Observation -* Proposal: [SE-0395](0395-observation.md) +* Proposal: [SE-0395](0395-observability.md) * Authors: [Philippe Hausler](https://github.com/phausler), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Active Review (April 11 – April 24, 2023)** From 2e8a3453b83b5341d8f95f2236b593c673e968f4 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 13 Apr 2023 14:03:24 -0400 Subject: [PATCH 3083/4563] Pitch variadic generic types --- proposals/nnnn-variadic-types.md | 209 +++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 proposals/nnnn-variadic-types.md diff --git a/proposals/nnnn-variadic-types.md b/proposals/nnnn-variadic-types.md new file mode 100644 index 0000000000..62bf1d8aee --- /dev/null +++ b/proposals/nnnn-variadic-types.md @@ -0,0 +1,209 @@ +# Allow Generic Types to Abstract Over Packs + +* Proposal: [SE-NNNN](nnnn-variadic-types.md) +* Authors: [Slava Pestov](https://github.com/slavapestov) +* Review Manager: TBD +* Upcoming Feature Flag: `VariadicGenerics` +* Previous Proposal: [SE-0393](0393-parameter-packs.md) + +## Introduction + +Previously [SE-0393](0393-parameter-packs.md) introduced type parameter packs and several related concepts, allowing generic function declarations to abstract over a variable number of types. This proposal generalizes these ideas to generic type declarations. + +## Motivation + +Generic type declarations that abstract over a variable number of types arise naturally when attempting to generalize common algorithms on collections. For example, a lazy `ZipSequence` might be generic over two sequences. It would be possible to declare a `ZipSequence` type which presents the elements of a fixed list of sequences as a sequence of tuples: + +```swift +struct ZipSequence: Sequence { + typealias Element = (repeat each S.Element) + + let seq: (repeat each S) + + func makeIterator() -> Iterator { + return Iterator(iter: (repeat (each seq).makeIterator())) + } + + struct Iterator: IteratorProtocol { + typealias Element = (repeat each S.Element) + + var iter: (repeat each S.Iterator) + + mutating func next() -> Element? { + return ... + } + } +} +``` + +## Proposed solution + +In the generic parameter list of a generic type, the `each` keyword declares a generic parameter pack, just like it does in the generic parameter list of a generic function. The types of stored properties can contain pack expansion types, as in `let seq` and `var iter` above. + +## Detailed design + +Swift has the following kinds of generic type declarations: + +- Structs +- Enums +- Classes (and actors) +- Type aliases + +A generic type is _variadic_ if it directly declares a type parameter pack with `each`, or if it is nested inside of another variadic type. In this proposal, structs, classes, actors and type aliases can be variadic. Enums will be addressed in a follow-up proposal. + +### Single parameter + +A generic type is limited to declaring at most one type parameter pack. The following are allowed: + +```swift +struct S1 {} +struct S2 {} +struct S3 {} +``` + +But this is not: + +```swift +struct S4 {} +``` + +However, by virtue of nesting, a variadic type might can still abstract over multiple type parameter packs: + +```swift +struct Outer { + struct Inner { + var fn: (repeat each T) -> (repeat each U) + } +} +``` + +### Referencing a variadic type + +When used with a variadic type, the generic argument syntax `S<...>` allows a variable number of arguments to be specified. Since there can only be one generic parameter pack, the non-pack parameters are always specified with a fixed prefix and suffix of the generic argument list. + +```swift +struct S {} + +S.self // T := Int, U := Pack{}, V := Float +S.self // T := Int, U := Pack{Bool}, V := Float +S.self // T := Int, U := Pack{Bool, String}, V := Float +``` + +Note that `S` substitutes U with the empty pack type, which is allowed. The minimum number of generic arguments is equal to the number of non-pack generic parameters. In our above example, the minimum argument count is 2, because `T` and `V` must always be specified: + +```swift +S.self // error: expected at least 2 generic arguments +``` + +If the generic parameter list of a variadic type consists of a single generic parameter pack and nothing else, it is possible to reference it with an empty generic argument list: + +```swift +struct V {} + +V<>.self +``` +Note that `V<>` is not the same as `V`. The former substitutes the generic parameter pack `T` with the empty pack. The latter does not constrain the pack at all and is only permitted in contexts where the generic argument can be inferred. + +A placeholder type in the generic argument list of a variadic generic type is always understood as a single pack element. For example: + +```swift +struct V {} + +let x: V<_> = V() // okay +let x: V<_, _> = V() // okay +let x: V<_> = V() // error +``` + +### Stored properties + +In a variadic type, the type of a stored property can contain pack expansion types. The type of a stored property cannot _itself_ be a pack expansion type. Stored properties are limited to having pack expansions nested inside tuple types, function types and other named variadic types: + +```swift +struct S { + var a: (repeat each Array) + var b: (repeat each T) -> (Int) + var c: Other +} +``` +This is in contrast with the parameters of generic function declarations, which can have a pack expansion type. A future proposal might lift this restriction and introduce true "stored property packs": + +```swift +struct S { + var a: repeat each Array +} +``` + +### Requirements + +The behavior of generic requirements on type parameter packs is unchanged between generic functions and generic types. + +### Conformances + +Variadic structs, classes and actors can conform to protocols. The associated type requirements of the protocol may be fulfilled by a type alias whose underlying type contains pack expansions. + +### Type aliases + +As with the other variadic types, a variadic type alias either has a generic parameter pack of its own, or can be nested inside of another variadic generic type. + +The underlying type of a variadic type alias can reference pack expansion types in the same manner as the type of a stored property. That is, the pack expansions must appear in nested positions, but not at the top level. + +```swift +typealias Element = (repeat each S.Element) +typealias Callback = (repeat each S) -> () +typealias Factory = Other +``` + +Unlike structs and classes, but like other type aliases, variadic type aliases can also be nested inside of generic functions. + +### Classes + +While there are no restrictions on non-final classes adopting type parameter packs, for the time being the proposal restricts such classes from being the superclass of another class. + +An attempt to inherit from a variadic generic class outputs an error. The correct behavior of override checking and initializer inheritance in variadic generic classes will be dealt with in a follow-up proposal: + +```swift +class Base { + func foo(t: repeat each T) {} +} + +// error: cannot inherit from a class with a type parameter pack +class Derived: Base { + override func foo(t: U, _: V) {} +} +``` + +## Source compatibility + +Variadic generic types are a new language feature which does not impact source compatibility with existing code. + +## ABI compatibility + +Variadic type aliases are not part of the binary interface of a module and do not require runtime support. + +All other variadic types make use of new entry points and other behaviors being added to the Swift runtime. Since the runtime support requires extensive changes to the type metadata logic, backward deployment to older runtimes is not supported. + +Replacing a non-variadic generic type with a variadic generic type is **not** binary-compatible in either direction. When adopting variadic generic types, binary-stable frameworks must introduce them as wholly-new symbols. + +## Future directions + +A future proposal will address variadic generic enums, and complete support for variadic generic classes. + +Another possible future direction is stored property packs, which would eliminate the need to wrap a pack expansion in a tuple type in order to store a variable number of values inside of a variadic type. However, there is no expressivity lost in requiring the tuple today, since the contents of a tuple can be converted into a value pack. + +## Alternatives considered + +The one-parameter limitation could be lifted if we introduced labeled generic parameters at the same time. The choice to allow only a single (unlabeled) generic parameter pack does not preclude this possibility from being explored in the future. + +Another alternative is to not enforce the one-parameter limitation at all. There would then exist variadic generic types which cannot be spelled explicitly, but can still be constructed by type inference: + +```swift +struct S { + init(t: repeat each T, u: repeat each U) {} +} + +S(t: 1, "hi", u: false) +``` + +It was felt that in the end, the single-parameter model is the simplest. + +We could require that variadic classes are declared `final`, instead of rejecting subclassing at the point of use. However, since adding or removing `final` on a class is an ABI break, this would preclude the possibility of publishing APIs which work with the existing compiler but can allow subclassing in the future. \ No newline at end of file From 9e710d3976e0c1c9129fd8553c14b3d8115a6573 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 13 Apr 2023 18:29:23 -0700 Subject: [PATCH 3084/4563] [SE-0393] Incorporate revisions from review discussion. (#2004) --- proposals/0393-parameter-packs.md | 67 +++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/proposals/0393-parameter-packs.md b/proposals/0393-parameter-packs.md index fcb937c904..78bbaec858 100644 --- a/proposals/0393-parameter-packs.md +++ b/proposals/0393-parameter-packs.md @@ -53,6 +53,7 @@ This proposal adds _type parameter packs_ and _value parameter packs_ to enable - [Value expansion operator](#value-expansion-operator) - [Pack destructuring operations](#pack-destructuring-operations) - [Tuple conformances](#tuple-conformances) + - [Revision history](#revision-history) - [Acknowledgments](#acknowledgments) ## Motivation @@ -99,7 +100,7 @@ A pack expansion consists of the `repeat` keyword followed by a type or an expre Similarly, pack references can only appear inside repetition patterns and generic requirements: ```swift -func zip(_ sequence: repeat each S) where each S: Sequence +func zip(_ sequence: repeat each S) where repeat each S: Sequence ``` Given a concrete pack substitution, the pattern is repeated for each element in the substituted pack. If `S` is substituted with `Array, Set`, then `repeat Optional` will repeat the pattern `Optional` for each element in the substitution to produce `Optional>, Optional>`. @@ -384,20 +385,20 @@ We will refer to `each T` as the _root type parameter pack_ of the member type p ### Generic requirements -All existing kinds of generic requirements generalize to type parameter packs. Same-type requirements generalize in multiple different ways, depending on whether one or both sides involve a type parameter pack. +All existing kinds of generic requirements can be used inside _requirement expansions_, which represent a list of zero or more requirements. Requirement expansions are spelled with the `repeat` keyword followed by a generic requirement pattern that captures at least one type parameter pack reference spelled with the `each` keyword. Same-type requirements generalize in multiple different ways, depending on whether one or both sides involve a type parameter pack. 1. Conformance, superclass, and layout requirements where the subject type is a type parameter pack are interpreted as constraining each element of the replacement type pack: ```swift - func variadic(_: repeat each S) where each S: Sequence { ... } + func variadic(_: repeat each S) where repeat each S: Sequence { ... } ``` A valid substitution for the above might replace `S` with `{Array, Set}`. Expanding the substitution into the requirement `each S: Sequence` conceptually produces the following conformance requirements: `Array: Sequence, Set: Sequence`. -2. A same-type requirement where one side is a type parameter pack and the other type is a scalar type that does not capture any type parameter packs is interpreted as constraining each element of the replacement type pack to _the same_ scalar type: +1. A same-type requirement where one side is a type parameter pack and the other type is a scalar type that does not capture any type parameter packs is interpreted as constraining each element of the replacement type pack to _the same_ scalar type: ```swift - func variadic(_: repeat each S) where (each S).Element == T {} + func variadic(_: repeat each S) where repeat (each S).Element == T {} ``` This is called a _same-element requirement_. @@ -408,7 +409,7 @@ All existing kinds of generic requirements generalize to type parameter packs. S 3. A same-type requirement where each side is a pattern type that captures at least one type parameter pack is interpreted as expanding the type packs on each side of the requirement, equating each element pair-wise. ```swift - func variadic(_: repeat each S) where (each S).Element == Array {} + func variadic(_: repeat each S) where repeat (each S).Element == Array {} ``` This is called a _same-type-pack requirement_. @@ -560,16 +561,50 @@ func overload(_: T) {} func overload(_: repeat each T) {} ``` -If both overloads match a given call, e.g. `overload(1)`, the call is ambiguous. Similarly, two generic functions where one accepts a non-pack variadic parameter and the other accepts a type parameter pack: +If the parameters of the scalar overload have the same or refined requirements as the parameter pack overload, the scalar overload is considered a subtype of the parameter pack overload, because the parameters of the scalar overload can be forwarded to the parameter pack overload. Currently, if a function call successfully type checks with two different overloads, the subtype is preferred. This overload ranking rule generalizes to overloads with parameter packs, which effectively means that scalar overloads are preferred over parameter pack overloads when the scalar requirements meet the requirements of the parameter pack: ```swift -func overload(_: T...) {} +func overload() {} +func overload(_: T) {} func overload(_: repeat each T) {} -overload() // ambiguity error +overload() // calls the no-parameter overload + +overload(1) // calls the scalar overload + +overload(1, "") // calls the parameter pack overload ``` -In other words, variadic generic functions have the same ranking as other generic functions. +The general overload subtype ranking rule applies after localized ranking, such as implicit conversions and optional promotions. That remains unchanged with this proposal. For example: + +```swift +func overload(_: T, _: Any) {} +func overload(_: repeat each T) {} + +overload(1, "") // prefers the parameter pack overload because the scalar overload would require an existential conversion +``` + +More complex scenarios can still result in ambiguities. For example, if multiple overloads match a function call, but each parameter list can be forwarded to the other, the call is ambiguous: + +```swift +func overload(_: repeat each T) {} +func overload(vals: repeat each T) {} + +overload() // error: ambiguous +``` + +Similarly, if neither overload can forward their parameter lists to the other, the call is ambiguous: + +```swift +func overload(str: String, _: repeat each T) {} +func overload(str: repeat each U) {} + +func test(_ z: Z) { + overload(str: "Hello, world!", z, z) // error: ambiguous +} +``` + +Generalizing the existing overload resolution ranking rules to parameter packs enables library authors to introduce new function overloads using parameter packs that generalize existing fixed-arity overloads while preserving the overload resolution behavior of existing code. ## Effect on ABI stability @@ -697,7 +732,7 @@ In this proposal, type packs do not have an explicit syntax, and a type pack is ```swift struct Variadic {} -extension Variadic where each T == {Int, String} {} // {Int, String} is a concrete pack +extension Variadic where T == {Int, String} {} // {Int, String} is a concrete pack ``` ### Pack iteration @@ -727,7 +762,7 @@ Use cases for variadic generics that break up pack iteration across function cal Dynamic pack indexing is useful when the specific type of the element is not known, or when all indices must have the same type, such as for index manipulation or storing an index value. Packs could support subscript calls with an `Int` index, which would return the dynamic type of the pack element directly as the opened underlying type that can be assigned to a local variable with opaque type. Values of this type need to be erased or cast to another type to return an element value from the function: ```swift -func element(at index: Int, in t: repeat each T) where each T: P -> any P { +func element(at index: Int, in t: repeat each T) -> any P { // The subscript returns 'some P', which is erased to 'any P' // based on the function return type. let value: some P = t[index] @@ -831,6 +866,14 @@ extension (repeat each T): Equatable { } ``` +## Revision history + +Changes to the [first reviewed revision](https://github.com/apple/swift-evolution/blob/b6ca38b9eee79650dce925e7aa8443a6a9e5e6ea/proposals/0393-parameter-packs.md): + +* The `repeat` keyword is required for generic requirement expansions to distinguish requirement expansions from single requirements on an individual pack element nested inside of a pack expansion expression. +* Overload resolution prefers scalar overloads when the scalar overload is considered a subtype of a parmeter pack overload. + + ## Acknowledgments Thank you to Robert Widmann for exploring the design space of modeling packs as tuples, and to everyone who participated in earlier design discussions about variadic generics in Swift. Thank you to the many engineers who contributed to the implementation, including Sophia Poirier, Pavel Yaskevich, Nate Chandler, Hamish Knight, and Adrian Prantl. From f50c06645b1236ca6c587256f12865c347829436 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Thu, 13 Apr 2023 21:48:21 -0400 Subject: [PATCH 3085/4563] Accept SE-0393 with modifications (#2014) --- proposals/0393-parameter-packs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0393-parameter-packs.md b/proposals/0393-parameter-packs.md index 78bbaec858..22e2a92cf2 100644 --- a/proposals/0393-parameter-packs.md +++ b/proposals/0393-parameter-packs.md @@ -3,9 +3,9 @@ * Proposal: [SE-0393](0393-parameter-packs.md) * Authors: [Holly Borla](https://github.com/hborla), [John McCall](https://github.com/rjmccall), [Slava Pestov](https://github.com/slavapestov) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (March 21...April 3, 2023)** +* Status: **Accepted** * Implementation: On `main` gated behind the frontend flag `-enable-experimental-feature VariadicGenerics` -* Review: ([pitch 1](https://forums.swift.org/t/pitch-parameter-packs/60543)) ([pitch 2](https://forums.swift.org/t/pitch-2-value-and-type-parameter-packs/60830)) ([review](https://forums.swift.org/t/se-0393-value-and-type-parameter-packs/63859)) +* Review: ([pitch 1](https://forums.swift.org/t/pitch-parameter-packs/60543)) ([pitch 2](https://forums.swift.org/t/pitch-2-value-and-type-parameter-packs/60830)) ([review](https://forums.swift.org/t/se-0393-value-and-type-parameter-packs/63859)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0393-value-and-type-parameter-packs/64382)) ## Introduction From 339c0e72f93691d5a9665ed9ac6a7af670c81731 Mon Sep 17 00:00:00 2001 From: Roshan Kumar Sah Date: Sat, 15 Apr 2023 00:29:22 +0530 Subject: [PATCH 3086/4563] Update 0350-regex-type-overview.md Confirmation Type and conversion fix --- proposals/0350-regex-type-overview.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/proposals/0350-regex-type-overview.md b/proposals/0350-regex-type-overview.md index 4db19c5ec1..2eef59ffb0 100644 --- a/proposals/0350-regex-type-overview.md +++ b/proposals/0350-regex-type-overview.md @@ -43,7 +43,10 @@ Imagine processing a bank statement in order to extract transaction details for ```swift struct Transaction { - enum Kind { case credit; case debit } + enum Kind: String { + case credit = "CREDIT" + case debit = "DEBIT" + } let kind: Kind let date: Date @@ -104,7 +107,7 @@ func processEntry(_ line: String) -> Transaction? { let range = NSRange(line.startIndex.. Date: Tue, 18 Apr 2023 12:21:50 +0100 Subject: [PATCH 3087/4563] Fix review date typo for SE-0387 (#2017) --- proposals/0387-cross-compilation-destinations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0387-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md index 88be2da0f9..ebbf836148 100644 --- a/proposals/0387-cross-compilation-destinations.md +++ b/proposals/0387-cross-compilation-destinations.md @@ -4,7 +4,7 @@ - Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd/), [Evan Wilde](https://github.com/etcwilde) - Review Manager: [Mishal Shah](https://github.com/shahmishal) -- Status: **Active Review (January 31st...Feburary 14th, 2023**) +- Status: **Active Review (January 31st...February 14th, 2023**) - Implementation: [apple/swift-package-manager#5911](https://github.com/apple/swift-package-manager/pull/5911), [apple/swift-package-manager#5922](https://github.com/apple/swift-package-manager/pull/5922), [apple/swift-package-manager#6023](https://github.com/apple/swift-package-manager/pull/6023) - Review: ([pitch](https://forums.swift.org/t/pitch-cross-compilation-destination-bundles/61777)) ([review](https://forums.swift.org/t/se-0387-cross-compilation-destination-bundles/62875)) From 30f9d660d6d5769440f94fde41511b22a9046afa Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Tue, 18 Apr 2023 16:19:09 -0500 Subject: [PATCH 3088/4563] Add a bit more detail about `init(from:)` --- proposals/NNNN-never-codable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-never-codable.md b/proposals/NNNN-never-codable.md index a68ce7a78f..eac347534c 100644 --- a/proposals/NNNN-never-codable.md +++ b/proposals/NNNN-never-codable.md @@ -35,7 +35,7 @@ The standard library should add `Encodable` and `Decodable` conformance to the ` The `Encodable` conformance is simple — since it's impossible to have a `Never` instance, the `encode(to:)` method can simply be empty. -The `Decodable` protocol requires the `init(from:)` initializer, which clearly can't create a `Never` instance. The implementation throws a `DecodingError` if decoding is attempted. +The `Decodable` protocol requires the `init(from:)` initializer, which clearly can't create a `Never` instance. Because trying to decode invalid input isn't a programmer error, a fatal error would be inappropriate. Instead, the implementation throws a `DecodingError.dataCorrupted` error if decoding is attempted. ## Source compatibility From 2332d0a510cf8301cfb4852fd44041fdd154cbfb Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 18 Apr 2023 15:15:20 -0700 Subject: [PATCH 3089/4563] Assign "Conform Never to Codable" as SE-0396 and start review. --- .../{NNNN-never-codable.md => 0396-never-codable.md} | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) rename proposals/{NNNN-never-codable.md => 0396-never-codable.md} (89%) diff --git a/proposals/NNNN-never-codable.md b/proposals/0396-never-codable.md similarity index 89% rename from proposals/NNNN-never-codable.md rename to proposals/0396-never-codable.md index eac347534c..a037e3216e 100644 --- a/proposals/NNNN-never-codable.md +++ b/proposals/0396-never-codable.md @@ -1,17 +1,16 @@ # Conform `Never` to `Codable` -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0396](0396-never-codable.md) * Author: [Nate Cook](https://github.com/natecook1000) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Tony Allevato](https://github.com/allevato) +* Status: **Active Review (April 18...May 3, 2023)** * Implementation: [apple/swift#64899](https://github.com/apple/swift/pull/64899) +* Review: ([pitch](https://forums.swift.org/t/pitch-conform-never-to-codable/64056)) ## Introduction Extend `Never` so that it conforms to the `Encodable` and `Decodable` protocols, together known as `Codable`. -Forum discussion: [https://forums.swift.org/t/pitch-conform-never-to-codable/64056](https://forums.swift.org/t/pitch-conform-never-to-codable/64056) - ## Motivation Swift can synthesize `Codable` conformance for any type that has `Codable` members. Generic types often participate in this synthesized conformance by constraining their generic parameters, like this `Either` type: From 7b3af603c010a7ca83d98e26df15b0d36a2e90f3 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 18 Apr 2023 15:20:03 -0700 Subject: [PATCH 3090/4563] Add proposal review link to SE-0396. --- proposals/0396-never-codable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0396-never-codable.md b/proposals/0396-never-codable.md index a037e3216e..f064ed5126 100644 --- a/proposals/0396-never-codable.md +++ b/proposals/0396-never-codable.md @@ -5,7 +5,7 @@ * Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Active Review (April 18...May 3, 2023)** * Implementation: [apple/swift#64899](https://github.com/apple/swift/pull/64899) -* Review: ([pitch](https://forums.swift.org/t/pitch-conform-never-to-codable/64056)) +* Review: ([pitch](https://forums.swift.org/t/pitch-conform-never-to-codable/64056)) ([review](https://forums.swift.org/t/se-0396-conform-never-to-codable/64469)) ## Introduction From 5a0851f9de745648a3820bd5821f5ecbf35c7847 Mon Sep 17 00:00:00 2001 From: Boris Buegling Date: Wed, 19 Apr 2023 12:25:25 -0700 Subject: [PATCH 3091/4563] Amend SE-0394 - Add support for Swift/linker settings as well as plugin dependencies to the macro target API - Add reasoning for not using a `.macroTarget()` style API - Add reasoning for declaring dependencies on macros using the `dependencies` argument --- proposals/0394-swiftpm-expression-macros.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/proposals/0394-swiftpm-expression-macros.md b/proposals/0394-swiftpm-expression-macros.md index 61f53186d5..85b708573f 100644 --- a/proposals/0394-swiftpm-expression-macros.md +++ b/proposals/0394-swiftpm-expression-macros.md @@ -30,12 +30,18 @@ public extension Target { /// - path: The path of the macro, relative to the package root. /// - exclude: The paths to source and resource files you want to exclude from the macro. /// - sources: The source files in the macro. + /// - swiftSettings: The Swift settings for this macro. + /// - linkerSettings: The linker settings for this macro. + /// - plugins: The plugins used by this macro. static func macro( name: String, dependencies: [Dependency] = [], path: String? = nil, exclude: [String] = [], - sources: [String]? = nil + sources: [String]? = nil, + swiftSettings: [SwiftSetting]? = nil, + linkerSettings: [LinkerSetting]? = nil, + plugins: [PluginUsage]? = nil ) -> Target { ... } } ``` @@ -178,8 +184,18 @@ Since macro plugins are entirely additive, there's no impact on existing package ## Alternatives considered +### Package plugins + The original pitch of expression macros considered declaring macros by introducing a new capability to [package plugins](https://github.com/apple/swift-evolution/blob/main/proposals/0303-swiftpm-extensible-build-tools.md), but since the execution model is significantly different and the APIs used for macros are external to SwiftPM, this idea was discarded. +### `.macroTarget()` + +We're (slowly) trying to move away from having the target suffix since it is implied by the context. This is already the case for plugin targets and eventually we'd like to have e.g. `.test()` as well. This would also make the target APIs be more in line with the product ones, where e.g. we don't use `.libraryProduct()`. + +### Dependencies on macro targets + +In [SE-0303](https://github.com/apple/swift-evolution/blob/main/proposals/0303-swiftpm-extensible-build-tools.md), we introduced the `plugins` parameter for build order dependencies on plugins, so it could have make sense to use a `macros` parameter for dependencies on macros. However, introducing bespoke API for each type of host-side content used during the build does not seem scalable. We also already have precedence of executables being part of `dependencies` even though that dependency is strictly for build ordering (with the exception of tests, which also applies to macros). Because of this, dependencies on macros are declared via the `dependencies` parameter, however it could be interesting to revisit a separation of build order and linked dependencies in the future. + ## Future Directions ### Generalized support for additional manifest API From ee39d319cf9bcdc8447c44b3fcc0afde809246d3 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 20 Apr 2023 11:54:33 +0100 Subject: [PATCH 3092/4563] Update SE-0387: Cross-Compilation Destination Bundles (#1942) This addresses feedback provided by the community in [the proposal review thread](https://forums.swift.org/t/se-0387-cross-compilation-destination-bundles/62875/6). Some sections were expanded with examples, while properties of some of the configuration files were updated. General fixes for typos, formatting, and wording were also applied. --- .../0387-cross-compilation-destinations.md | 353 +++++++++++++----- 1 file changed, 260 insertions(+), 93 deletions(-) diff --git a/proposals/0387-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md index ebbf836148..3b9bd32ad5 100644 --- a/proposals/0387-cross-compilation-destinations.md +++ b/proposals/0387-cross-compilation-destinations.md @@ -1,13 +1,45 @@ # Cross-Compilation Destination Bundles - Proposal: [SE-0387](0387-cross-compilation-destinations.md) -- Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd/), [Evan - Wilde](https://github.com/etcwilde) +- Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd/), [Evan Wilde](https://github.com/etcwilde) - Review Manager: [Mishal Shah](https://github.com/shahmishal) - Status: **Active Review (January 31st...February 14th, 2023**) - Implementation: [apple/swift-package-manager#5911](https://github.com/apple/swift-package-manager/pull/5911), - [apple/swift-package-manager#5922](https://github.com/apple/swift-package-manager/pull/5922), [apple/swift-package-manager#6023](https://github.com/apple/swift-package-manager/pull/6023) -- Review: ([pitch](https://forums.swift.org/t/pitch-cross-compilation-destination-bundles/61777)) ([review](https://forums.swift.org/t/se-0387-cross-compilation-destination-bundles/62875)) + [apple/swift-package-manager#5922](https://github.com/apple/swift-package-manager/pull/5922), + [apple/swift-package-manager#6023](https://github.com/apple/swift-package-manager/pull/6023), + [apple/swift-package-manager#6186](https://github.com/apple/swift-package-manager/pull/6186) +- Review: ([pitch](https://forums.swift.org/t/pitch-cross-compilation-destination-bundles/61777)) + ([review](https://forums.swift.org/t/se-0387-cross-compilation-destination-bundles/62875)) + +## Table of Contents + +- [Introduction](#introduction) +- [Motivation](#motivation) +- [Proposed Solution](#proposed-solution) +- [Detailed Design](#detailed-design) + - [CC Destination Artifact Bundles](#cc-destination-artifact-bundles) + - [`toolset.json` Files](#toolsetjson-files) + - [`destination.json` Files](#destinationjson-files) + - [Destination Bundle Installation and Configuration](#destination-bundle-installation-and-configuration) + - [Using a CC Destination](#using-a-cc-destination) + - [CC Destination Bundle Generation](#cc-destination-bundle-generation) +- [Security](#security) +- [Impact on Existing Packages](#impact-on-existing-packages) +- [Prior Art](#prior-art) + - [Rust](#rust) + - [Go](#go) +- [Alternatives Considered](#alternatives-considered) + - [Extensions Other Than `.artifactbundle`](#extensions-other-than-artifactbundle) + - [Building Applications in Docker Containers](#building-applications-in-docker-containers) + - [Alternative Bundle Formats](#alternative-bundle-formats) +- [Making Destination Bundles Fully Self-Contained](#making-destination-bundles-fully-self-contained) +- [Future Directions](#future-directions) + - [Identifying Platforms with Dictionaries of Properties](#identifying-platforms-with-dictionaries-of-properties) + - [SwiftPM Plugins for Remote Running, Testing, Deployment, and Debugging](#swiftpm-plugins-for-remote-running-testing-deployment-and-debugging) + - [`swift destination select` Subcommand](#swift-destination-select-subcommand) + - [SwiftPM and SourceKit-LSP Improvements](#swiftpm-and-sourcekit-lsp-improvements) + - [Source-Based CC Destinations](#source-based-cc-destinations) + - [Destination Bundles and Package Registries](#destination-bundles-and-package-registries) ## Introduction @@ -30,8 +62,9 @@ triple" terms in this proposal. ## Motivation -Swift cross-compilation (CC) destinations are currently produced on an ad-hoc basis for different combinations of build-time -and run-time triples. For example, scripts that produce macOS → Linux CC destinations were created by both [the Swift +Swift cross-compilation (CC) destinations are currently produced on an ad-hoc basis for different combinations of +build-time and run-time triples. For example, scripts that produce macOS → Linux CC destinations were created by both +[the Swift team](https://github.com/apple/swift-package-manager/blob/swift-5.7-RELEASE/Utilities/build_ubuntu_cross_compilation_toolchain) and [the Swift community](https://github.com/SPMDestinations/homebrew-tap). At the same time, the distribution process of CC destinations is cumbersome. After building a destination tree on the file system, required metadata files rely on @@ -48,7 +81,7 @@ project is a daunting step that shouldn’t be required. The solution described below is general enough to scale for any build-time/run-time triple combination. -## Proposed solution +## Proposed Solution Since CC destination is a collection of binaries arranged in a certain directory hierarchy, it makes sense to distribute it as an archive. We'd like to build on top of @@ -65,7 +98,10 @@ with tools supplied in `.artifactbundle` s installed by `swift destination` invo When the user runs `swift build` with the selected CC destination, the overriding tools from the corresponding bundle are invoked by `swift build` instead of tools from the top-level toolchain. -## Detailed design +The proposal is intentionally limited in scope to build-time experience and specifies only configuration metadata, basic +directory layout for proposed artifact bundles, and some CLI helpers to operate on those. + +## Detailed Design ### CC Destination Artifact Bundles @@ -80,85 +116,92 @@ The proposed structure of artifact bundles containing CC destinations looks like .artifactbundle ├ info.json ├ -│ ├ -│ │ ├ destination.json -│ │ └ -│ └ -│ ├ destination.json -│ └ +│ ├ destination.json +│ ├ toolset.json +│ └ -│ └ -│ ├ destination.json -│ └ +│ ├ destination.json +│ ├ toolset.json +│ └ ┆ └┄ ``` -For example, a destination bundle allowing to cross-compile Swift 5.7 source code to recent versions of Ubuntu from +For example, a destination bundle allowing to cross-compile Swift 5.8 source code to recent versions of Ubuntu from macOS would look like this: ``` -swift-5.7_ubuntu.artifactbundle +swift-5.8_ubuntu.artifactbundle ├ info.json +├ toolset.json ├ ubuntu_jammy -│ ├ arm64-apple-darwin -│ │ ├ destination.json +│ ├ destination.json +│ ├ toolset.json +│ ├ +│ ├ aarch64-unknown-linux-gnu │ │ ├ toolset.json -│ │ └ -│ └ x86_64-apple-darwin -│ ├ destination.json +│ │ └ +│ └ x86_64-unknown-linux-gnu │ ├ toolset.json -│ └ +│ └ ├ ubuntu_focal -│ └ x86_64-apple-darwin -│ ├ destination.json +│ ├ destination.json +│ └ x86_64-unknown-linux-gnu │ ├ toolset.json -│ └ +│ └ ├ ubuntu_bionic ┆ └┄ ``` -Here each artifact directory is dedicated to a specific CC destination, while binaries for a specific host platform are -placed in `arm64-apple-darwin` and `x86_64-apple-darwin` subdirectories. - -`info.json` bundle manifests at the root of artifact bundles should specify `"type": "crossCompilationDestination"` -for corresponding artifacts. Artifact identifiers in this manifest uniquely identify a CC destination. The rest of the -properties of bundle manifests introduced in SE-0305 are preserved. +Here each artifact directory is dedicated to a specific CC destination, while files specific to each triple are placed +in `aarch64-unknown-linux-gnu` and `x86_64-unknown-linux-gnu` subdirectories. -### `destination.json` files +`info.json` bundle manifests at the root of artifact bundles should specify `"type": "crossCompilationDestination"` for +corresponding artifacts. Artifact identifiers in this manifest file uniquely identify a CC destination, and +`supportedTriples` property in `info.json` should contain build-time triples that a given destination supports. The rest +of the properties of bundle manifests introduced in SE-0305 are preserved. -Note the presence of `destination.json` files in each `` subdirectory. These files should contain a JSON -dictionary with an evolved version of the schema of [existing `destination.json` files that SwiftPM already -supports](https://github.com/apple/swift-package-manager/pull/1098) and `destination.json` files presented in the pitch -version of this proposal, hence `"schemaVersion": "3.0"`: +Here's how `info.json` file could look like for `swift-5.8_ubuntu.artifactbundle` introduced in the example +above: -```json +```json5 { - "schemaVersion": "3.0", - "buildTimeTriples": [""], - "runTimeTriples": [""], - "swiftResourcesPaths": [""], - "includeSearchPaths": [""], - "librarySearchPaths": [""], + "artifacts" : { + "swift-5.8_ubuntu22.04" : { + "type" : "crossCompilationDestination", + "version" : "0.0.1", + "variants" : [ + { + "path" : "ubuntu_jammy", + "supportedTriples" : [ + "arm64-apple-darwin", + "x86_64-apple-darwin" + ] + } + ] + }, + "swift-5.8_ubuntu20.04" : { + "type" : "crossCompilationDestination", + "version" : "0.0.1", + "variants" : [ + { + "path" : "ubuntu_focal", + "supportedTriples" : [ + "arm64-apple-darwin", + "x86_64-apple-darwin" + ] + } + ] + } + }, + "schemaVersion" : "1.0" } ``` -Thanks to the availability of Universal Binaries on macOS and multiarch layouts on Linux, `buildTimeTriples` and -`runTimeTriples` properties use plural in their naming and their values are arrays. - -We propose that all relative paths in `destination.json` files should be validated not to "escape" the destination -bundle for security reasons. That is, `../` components, if present in paths, will not be allowed to reference files and -directories outside of a corresponding destination bundle. Symlinks will also be validated to prevent them from escaping -out of the bundle. - -Since not all platforms can support self-contained destination bundles, users will be able to provide their own -additional paths on the filesystem outside of bundles after a destination is installed. The exact options for specifying -paths are proposed in a subsequent section for a newly introduced `swift destination configure` command. - -### `toolset.json` files +### `toolset.json` Files We find that properties dedicated to tools configuration are useful outside of the cross-compilation context. Due to -that, a separate `toolset.json` file is introduced: +that, separate toolset configuration files are introduced: ```json5 { @@ -167,75 +210,174 @@ that, a separate `toolset.json` file is introduced: // If `toolsetRootPath` is specified, all relative paths below will be resolved relative to `toolsetRootPath`. "swiftCompiler": { "path": "", - "extraFlags": [""] + "extraCLIOptions": [""] }, "cCompiler": { "path": "", - "extraFlags": [""] + "extraCLIOptions": [""] }, "cxxCompiler": { "path": "", - "extraFlags": [""] + "extraCLIOptions": [""] }, "linker": { "path": "", - "extraFlags": [""] - } + "extraCLIOptions": [""] + }, + "librarian": { + "path": "", + "extraCLIOptions": [""] + }, + "debugger": { + "path": "", + "extraCLIOptions": [""] + }, + "testRunner": { + "path": "", + "extraCLIOptions": [""] + }, } ``` +More types of tools may be enabled in toolset files in the future in addition to those listed above. + Users familiar with CMake can draw an analogy between toolset files and CMake toolchain files. Toolset files are -designed to supplant previous ad-hoc ways to specify paths and flags in SwiftPM, such as `SWIFT_EXEC` and `CC` +designed to supplant previous ad-hoc ways of specifying paths and flags in SwiftPM, such as `SWIFT_EXEC` and `CC` environment variables, which were applied in use cases unrelated to cross-compilation. We propose that -users should be able to pass `--toolset ` option to both `swift build` and `swift test`. +users also should be able to pass `--toolset ` option to `swift build`, `swift test`, and +`swift run`. We'd like to allow using multiple toolset files at once. With this users can "assemble" toolchains on the fly out of -tools that in certain scenarios may even come from different vendors. +tools that in certain scenarios may even come from different vendors. A toolset file can have an arbitrary name, and +each file should be passed with a separate `--toolset` option, i.e. `swift build --toolset t1.json --toolset t2.json`. -All of the properties related to names of the tools are optional, which also allows stacking multiple toolset files. -For example, consider `toolset1.json`: +All of the properties related to names of the tools are optional, which allows merging configuration from multiple +toolset files. For example, consider `toolset1.json`: -```json +```json5 { "schemaVersion": "1.0", "swiftCompiler": { "path": "/usr/bin/swiftc", - "extraFlags": ["-Xfrontend", "-enable-cxx-interop"] + "extraCLIOptions": ["-Xfrontend", "-enable-cxx-interop"] }, "cCompiler": { "path": "/usr/bin/clang", - "extraFlags": ["-pedantic"] + "extraCLIOptions": ["-pedantic"] } } ``` and `toolset2.json`: -```json +```json5 { "schemaVersion": "1.0", "swiftCompiler": { - "path": "/custom/swiftc", - "extraFlags": [] + "path": "/custom/swiftc" } } ``` - With multiple `--toolset` options, passing both of those files will merge them into a single configuration. Tools passed in subsequent `--toolset` options will shadow tools from previous options with the same names. That is, `swift build --toolset toolset1.json --toolset toolset2.json` will build with `/custom/swiftc` and no extra flags, as specified in `toolset2.json`, but `/usr/bin/clang -pedantic` from `toolset1.json` will still be used. Tools not specified in any of the supplied toolset files will be looked up in existing implied search paths that are -used without any presence of toolset files, even when `toolsetRootPath` is present in any toolset file. We'd like -toolsets to be explicit enough in this regard: if any tool would like to participate in toolsets path lookups, it must -have its path provided, either relative or absolute, in any of the provided toolset files. +used without toolsets, even when `toolsetRootPath` is present. We'd like toolsets to be explicit in this regard: if a +tool would like to participate in toolset path lookups, it must provide either a relative or an absolute path in a +toolset. + +Tools that don't have `path` property but have `extraCLIOptions` present will append options from that property to a +tool with the same name specified in a preceding toolset file. If no other toolset files were provided, these options +will be appended to the default tool invocation. For example `pedanticCCompiler.json` that looks like this + +```json5 +{ + "schemaVersion": "1.0", + "cCompiler": { + "extraCLIOptions": ["-pedantic"] + } +} +``` + +in `swift build --toolset pedanticCCompiler.json` will pass `-pedantic` to the C compiler located at a default path. + +When cross-compiling, paths in `toolset.json` files supplied in destination artifact bundles should be self-contained: +no absolute paths and no escaping symlinks are allowed. Users are still able to provide their own `toolset.json` files +outside of artifact bundles to specify additional developer tools for which no relative "non-escaping" path can be +provided within the bundle. + +### `destination.json` Files + +Note the presence of `destination.json` files in each `` subdirectory. These files should contain +a JSON dictionary with an evolved version of the schema of [existing `destination.json` files that SwiftPM already +supports](https://github.com/apple/swift-package-manager/pull/1098) and `destination.json` files presented in the pitch +version of this proposal, hence `"schemaVersion": "3.0"`. We'll keep parsing `"version": 1` and `"version": 2` for +backward compatibility, but for consistency with `info.json` this field is renamed to `"schemaVersion"`. Here's an +informally defined schema for these files: + +```json5 +{ + "schemaVersion": "3.0", + "runTimeTriples": [ + "": { + "sdkRootPath": "", + // all of the properties listed below are optional: + "swiftResourcesPath": "", + "swiftStaticResourcesPath": "", + "includeSearchPaths": [""], + "librarySearchPaths": [""], + "toolsetPaths": [""] + }, + // a destination can support more than one run-time triple: + "": { + "sdkRootPath": "", + // all of the properties listed below are optional: + "swiftResourcesPath": "", + "swiftStaticResourcesPath": "", + "includeSearchPaths": [""], + "librarySearchPaths": [""], + "toolsetPaths": [""] + } + // more triples can be supported by a single destination if needed, primarily for sharing files between them. + ] +} +``` -When cross-compiling, paths in `toolset.json` files supplied in destination artifact bundles should be self-contained -with same rules applied as in `destination.json`: no absolute paths and no escaping symlinks are allowed. Users are -still able to provide their own `toolset.json` files outside of artifact bundles to specify additional developer tools -for which no relative path can be provided within the bundle. +We propose that all relative paths in `destination.json` files should be validated not to "escape" the destination +bundle for security reasons, in the same way that `toolset.json` files are validated when contained in destination +bundles. That is, `../` components, if present in paths, will not be allowed to reference files and +directories outside of a corresponding destination bundle. Symlinks will also be validated to prevent them from escaping +out of the bundle. + +If `sdkRootPath` is specified and `swiftResourcesPath` is not, the latter is inferred to be +`"\(sdkRootPath)/usr/lib/swift"` when linking the Swift standard library dynamically, `"swiftStaticResourcesPath"` is +inferred to be `"\(sdkRootPath)/usr/lib/swift_static"` when linking it statically. Similarly, `includeSearchPaths` is +inferred as `["\(sdkRootPath)/usr/include"]`, `librarySearchPaths` as `["\(sdkRootPath)/usr/lib"]`. + +Here's `destination.json` file for the `ubuntu_jammy` artifact previously introduced as an example: + +```json5 +{ + "schemaVersion": "3.0", + "runTimeTriples": [ + "aarch64-unknown-linux-gnu": { + "sdkRootPath": "aarch64-unknown-linux-gnu/ubuntu-jammy.sdk", + "toolsetPaths": ["aarch64-unknown-linux-gnu/toolset.json"] + }, + "x86_64-unknown-linux-gnu": { + "sdkRootPath": "x86_64-unknown-linux-gnu/ubuntu-jammy.sdk", + "toolsetPaths": ["x86_64-unknown-linux-gnu/toolset.json"] + } + ], +} +``` + +Since not all platforms can support self-contained destination bundles, users will be able to provide their own +additional paths on the filesystem outside of bundles after a destination is installed. The exact options for specifying +paths are proposed in a subsequent section for a newly introduced `swift destination configure` command. ### Destination Bundle Installation and Configuration @@ -245,22 +387,28 @@ To manage CC destinations, we'd like to introduce a new `swift destination` comm installs it in a location discoverable by SwiftPM. For destinations installed from remote URLs an additional `--checksum` option is required, through which users of destinations can specify a checksum provided by publishers of destinations. The latter can produce a checksum by running `swift package compute-checksum` command (introduced in - SE-0272) with the destination artifact bundle archive as an argument. + [SE-0272](https://github.com/apple/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md)) with the + destination artifact bundle archive as an argument. + + If a destination with a given artifact ID has already been installed and its version is equal or higher to a version + of a new destination, an error message will be printed. If the new version is higher, users should invoke the + `install` subcommand with `--update` flag to allow updating an already installed destination artifact to a new + version. - `swift destination list`, which prints a list of already installed CC destinations with their identifiers. -- `swift destination configure `, which allows users to provide additional search paths and toolsets to be +- `swift destination configure `, which allows users to provide additional search paths and toolsets to be used subsequently when building with a given destination. Specifically, multiple `--swift-resources-path`, `--include-search-path`, `--library-search-path`, and `--toolset` options with corresponding paths can be provided, which then will be stored as configuration for this destination. `swift destination configure --show-configuration` will print currently set paths, while `swift destination configure --reset` will reset all of those at once. -- `swift destination delete ` will delete a given destination from the filesystem. +- `swift destination remove ` will remove a given destination from the filesystem. ### Using a CC Destination After a destination is installed, users can refer to it via its identifier passed to the `--destination` option, e.g. ``` -swift build --destination ubuntu-jammy +swift build --destination ubuntu_focal ``` We'd also like to make `--destination` flexible enough to recognize run-time triples when there's only a single CC @@ -288,7 +436,7 @@ As an example, destination publishers looking to add a library to an Ubuntu 22.0 `Dockerfile` similar to this one in CC destination generator source code: ```dockerfile -FROM swift:5.7-jammy +FROM swift:5.8-jammy apt-get install -y \ # PostgreSQL library provided as an example. @@ -305,7 +453,7 @@ ready, the generator copies files from the image to a corresponding `.artifactbu The proposed `--checksum` flag provides basic means of verifying destination bundle's validity. As a future direction, we'd like to consider sandboxing and codesigning toolchains running on macOS. -## Impact on existing packages +## Impact on Existing Packages This is an additive change with no impact on existing packages. @@ -369,13 +517,26 @@ Different formats of destination bundles can be considered, but we don't think t from the proposed one. If they were different, this would complicate bundle distribution scenarios for users who want to publish their own artifact bundles with executables, as defined in SE-0305. +## Making Destination Bundles Fully Self-Contained + +Some users expressed interest in self-contained destination bundles that ignore the value of `PATH` environment variable +and prevent launching any executables from outside of a bundle. So far in our practice we haven't seen any problems +caused by the use of executables from `PATH`. Quite the opposite, we think most destinations would want to reuse as many +tools from `PATH` as possible, which would allow making destination bundles much smaller. For example as of Swift 5.7, +on macOS `clang-13` binary takes ~360 MB, `clangd` ~150 MB, and `swift-frontend` ~420 MB. Keeping copies of these +binaries in every destination bundle seems quite redundant when existing binaries from `PATH` can be easily reused. +Additionally, we find that preventing tools from being launched from arbitrary paths can't be technically enforced +without sandboxing, and there's no cross-platform sandboxing solution available for SwiftPM. Until such sandboxing +solution is available, we'd like to keep the existing approach where setting `PATH` environment variable behaves in a +predictable way and is consistent with established CLI conventions. + ## Future Directions ### Identifying Platforms with Dictionaries of Properties Platform triples are not specific enough in certain cases. For example, `aarch64-unknown-linux` host triple can’t prevent a user from installing a CC destination bundle on an unsupported Linux distribution. In the future we could -deprecate `hostTriple` and `destinationTriple` JSON properties in favor of dictionaries with keys and values that +deprecate `supportedTriples` and `runTimeTriples` JSON properties in favor of dictionaries with keys and values that describe aspects of platforms that are important for destinations. Such dictionaries could look like this: ```json5 @@ -397,7 +558,7 @@ After an application is built with a CC destination, there are other development introduce new types of plugins invoked by `swift run` and `swift test` for purposes of remote running, debugging, and testing. For Linux run-time triples, these plugins could delegate to Docker for running produced executables. -### `swift destination select` subcommand +### `swift destination select` Subcommand While `swift destination select` subcommand or a similar one make sense for selecting a CC destination instead of passing `--destination` to `swift build` every time, users will expect `swift run` and `swift test` to also work for any @@ -405,7 +566,7 @@ destination previously passed to `swift destination select`. That’s out of sco depends on making plugins (from the previous subsection) or some other remote running and testing implementation to fully work. -### SwiftPM and SourceKit-LSP improvements +### SwiftPM and SourceKit-LSP Improvements It is a known issue that SwiftPM can’t run multiple concurrent builds for different run-time triples. This may cause issues when SourceKit-LSP is building a project for indexing purposes (for a host platform by default), while a user may @@ -425,3 +586,9 @@ language](https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-c cross-compilation destination binaries are produced on the fly when needed. We don't consider this option to be mutually exclusive with solutions proposed in this document, and so it could be explored in the future for Swift as well. However, this requires reducing the number of dependencies that Swift runtime and core libraries have. + +### Destination Bundles and Package Registries + +Since `info.json` manifest files contained within bundles contain versions, it would make sense to host destination +bundles at package registries. Although, it remains to be seen whether it makes sense for an arbitrary SwiftPM package +to specify a destination bundle within its list of dependencies. From 0fa6f2baaf383445812d331b948f852945b610b8 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Thu, 20 Apr 2023 16:03:45 -0500 Subject: [PATCH 3093/4563] Switch to using a `typeMismatch` error --- proposals/0396-never-codable.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0396-never-codable.md b/proposals/0396-never-codable.md index f064ed5126..d9ef096546 100644 --- a/proposals/0396-never-codable.md +++ b/proposals/0396-never-codable.md @@ -34,7 +34,7 @@ The standard library should add `Encodable` and `Decodable` conformance to the ` The `Encodable` conformance is simple — since it's impossible to have a `Never` instance, the `encode(to:)` method can simply be empty. -The `Decodable` protocol requires the `init(from:)` initializer, which clearly can't create a `Never` instance. Because trying to decode invalid input isn't a programmer error, a fatal error would be inappropriate. Instead, the implementation throws a `DecodingError.dataCorrupted` error if decoding is attempted. +The `Decodable` protocol requires the `init(from:)` initializer, which clearly can't create a `Never` instance. Because trying to decode invalid input isn't a programmer error, a fatal error would be inappropriate. Instead, the implementation throws a `DecodingError.typeMismatch` error if decoding is attempted. ## Source compatibility @@ -56,4 +56,4 @@ None. ## Alternatives considered -None. +A previous iteration of this proposal used `DecodingError.dataCorrupted` as the error thrown from the `Decodable` initializer. Since that error is typically used for syntactical errors, such as malformed JSON, the `typeMismatch` error is now used instead. A custom error type could be created for this purpose, but as `typeMismatch` already exists as documented API, and provides the necessary information for a developer to understand the error, its use is appropriate here. From ab6f583771edd8aa89e97cc8403576a8cbc915d2 Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Fri, 21 Apr 2023 12:51:28 -0700 Subject: [PATCH 3094/4563] Add template code generation examples --- proposals/nnnn-freestanding-macros.md | 75 ++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index d22a2a33f5..ac338984ee 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -1,4 +1,4 @@ -# Freestanding Macros +# Freestanding Declaration Macros * Proposal: [SE-nnnn](nnnn-freestanding-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Richard Wei](https://github.com/rxwei), [Holly Borla](https://github.com/hborla) @@ -215,7 +215,7 @@ The `#warning` and `#error` directives introduced in [SE-0196](https://github.co Freestanding declaration macros can be used to generate boilerplace code. For example, the Standard Library could use such a macro to generate integer types. ```swift -@freestanding(declaration) +@freestanding(declaration, names: arbitrary) fileprivate macro IntegerTypes(bitWidths: Int...) #IntegerTypes(bitWidths: 8, 16, 32, 64) @@ -237,26 +237,77 @@ public struct Int64 { ... } public struct UInt64 { ... } ``` -### Short hand for `@main` +### Template code generator -A freestanding declaration macro `#main` could be defined as a short hand for a `@main` struct with a `main()` method. +The Swift Standard Library makes extensive use of the [gyb](https://github.com/apple/swift/blob/main/utils/gyb.py) tool to generate boilerplate-y Swift code such as [tgmath.swift.gyb](https://github.com/apple/swift/blob/main/stdlib/public/Platform/tgmath.swift.gyb). The template code is written in `.gyb` files, which are processed by the gyb tool separately before Swift compilation. With freestanding declaration macros, one could write a macro to accept a string as a template and a list of replacement values, allowing templates to be defined inline and eliminating the need to set up a separate build phase. ```swift -@freestanding(declaration) -macro main(_ body: () -> Void) +@freestanding(declaration, names: arbitrary) +macro gyb(String, [Any]) + +#gyb({ + public struct Int${0} { ... } + public struct UInt${0} { ... } +}, [8, 16, 32, 64]) +``` + +This expands to: + +```swift + public struct Int8 { ... } + public struct UInt8 { ... } + + public struct Int16 { ... } + public struct UInt16 { ... } + + public struct Int32 { ... } + public struct UInt32 { ... } -#main { - print("Hello") + public struct Int64 { ... } + public struct UInt64 { ... } +``` + +### Data model generator + +Declaring a data model for an existing textual serialization may need some amount of eyeballing and is prone to errors. A freestanding declaration macro can be used to analyze a template textual serialization, e.g. JSON, and declare a model data structure against the template. + +```swift +@freestanding(declaration, names: arbitrary) +macro jsonModel(String) + +struct JSONValue: Codable { + #jsonModel(""" + "name": "Produce", + "shelves": [ + { + "name": "Discount Produce", + "product": { + "name": "Banana", + "points": 200, + "description": "A banana that's perfectly ripe." + } + } + ] + """) } ``` This expands to: ```swift -@main -struct $_unique_name { - static func main() { - print("Hello") +struct JSONValue: Codable { + var name: String + var shelves: [Shelves] + + struct Shelves: Codable { + var name: String + var product: Product + + struct Product: Codable { + var description: String + var name: String + var points: Double + } } } ``` From c9239ba9ee93fcbe4ece2d2e1762091baec0562a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 24 Apr 2023 12:12:06 -0700 Subject: [PATCH 3095/4563] Editorial/cleanup pass over the freestanding declaration macros proposal --- proposals/nnnn-freestanding-macros.md | 137 ++++++-------------------- 1 file changed, 30 insertions(+), 107 deletions(-) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index ac338984ee..76219ac57b 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -9,64 +9,46 @@ ## Introduction - [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) introduces macros into Swift. The approach involves an explicit syntax for uses of macros (prefixed by `#`), type checking for macro arguments prior to macro expansion, and macro expansion implemented via separate programs that operate on the syntax tree of the arguments. + [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) introduced macros into Swift. The approach involves an explicit syntax for uses of macros (prefixed by `#`), type checking for macro arguments prior to macro expansion, and macro expansion implemented via separate programs that operate on the syntax tree of the arguments. -This proposal generalizes the `#`-prefixed macro expansion syntax introduced for expression macros to also allow macros to generate declarations and statements, enabling a number of other use cases, including: +This proposal generalizes the `#`-prefixed macro expansion syntax introduced for expression macros to also allow macros to generate declarations, enabling a number of other use cases, including: -* Subsuming the `#warning` and `#error` directives introduced in [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) into macros. -* Logging entry/exit of a function. +* Generating data structures from a template or other data format (e.g., JSON). +* Subsuming the `#warning` and `#error` directives introduced in [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) as macros. ## Proposed solution -The proposal introduces "freestanding" macros, a category of macros that can be evaluated on their own and are not attached to any other entity. All freestanding macros use the `#`-prefixed syntax introduced in [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md), building on and generalizing its design. Indeed, this proposal reclassifies expression macros as one form of freestanding macros, introducing one additional kind of freestanding macro: +The proposal extends the notion of "freestanding" macros introduced in [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) to also allow macros to introduce new declarations. Like expression macros, freestanding declaration macros are expanded using the `#` syntax, and have type-checked macro arguments. However, freestanding declaration macros can be used any place that a declaration is provided, and never produce a value. -This proposal also introduces a new kind of freestanding macros, declaration macros, which are expanded to create zero or more new declarations. - -* *Declaration macros* introduce zero or more declarations. These macros can be used anywhere where a declaration is permitted, including at the top level, in a function or closure body, or in a type definition or extension thereof. The generated declarations can be referenced from other Swift code, making freestanding macros useful for many different kinds of code generation and manipulation. - -Freestanding macros are declared with the `macro` introducer, and have one or more `@freestanding` attributes applied to them. The `@freestanding` attribute always contains a macro *role* (expression or declaration) and, optionally, a set of *introduced names* like attached macros. For example, a freestanding declaration macro would have an attribute like this: +As with other macros, freestanding declaration macros are declared with the `macro` introducer. They will use the `@freestanding` attribute with the new `declaration` role and, optionally, a set of *introduced names* as described in [SE-0389 "Attached macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#specifying-newly-introduced-names). For example, a freestanding declaration macro would have an attribute like this: ```swift @freestanding(declaration) ``` -whereas a declaration macro that introduced an enum named `CodingKeys` would have an attribute like this: +whereas a freestanding declaration macro that introduced an enum named `CodingKeys` would have an attribute like this: ```swift @freestanding(declaration, names: named(CodingKeys)) ``` -Implementations of freestanding macros are types that conform to the `FreestandingMacro` protocol, which is defined as follows: - -```swift -protocol FreestandingMacro: Macro { } -``` - -### Expression macros - -As previously noted, expression macros are one form of freestanding macro. [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) already introduced the `FreestandingMacro` protocol and the `ExpressionMacro` protocol that inherits from it: +Implementations of freestanding declaration macros are types that conform to the `DeclarationMacro` protocol, which is defined as follows: ```swift -protocol ExpressionMacro: FreestandingMacro { - // ... +public protocol DeclarationMacro: FreestandingMacro { + static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) async throws -> [DeclSyntax] } ``` -As well as the `@freestanding(expression)` syntax: - -```swift -@freestanding(expression) macro stringify(_: T) -> (T, String) -``` - -Expression macros can be used anywhere that an expression is permitted, e.g., within the body of a function or closure, or as a subexpression anywhere. Their implementations always produce another expression. - -### Declaration macros - -Declaration macros can be used anywhere that a declaration is permitted, e.g., in a function or closure body, at the top level, or within a type definition or extension thereof. Declaration macros produce zero or more declarations. The `warning` directive introduced by [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) can be described as a freestanding code item macro as follows: +Declaration macros can be used anywhere that a declaration is permitted, e.g., in a function or closure body, at the top level, or within a type definition or extension thereof. Declaration macros produce zero or more declarations. The `warning` directive introduced by [SE-0196](https://github.com/apple/swift-evolution/blob/main/proposals/0196-diagnostic-directives.md) can be described as a freestanding declaration macro as follows: ```swift /// Emits the given message as a warning, as in SE-0196. -@freestanding(declaration) macro warning(_ message: String) +@freestanding(declaration) +macro warning(_ message: String) = #externalMacro(module: "MyMacros", type: "WarningMacro") ``` Given this macro declaration, the syntax @@ -77,32 +59,13 @@ Given this macro declaration, the syntax can be used anywhere a declaration can occur. -Freestanding macros are implemented as types that conform to the `DeclarationMacro` protocol : - -```swift -public protocol DeclarationMacro: FreestandingMacro { - /// Expand a macro described by the given freestanding macro expansion declaration - /// within the given context to produce a set of declarations. - static func expansion( - of node: some FreestandingMacroExpansionSyntax, - in context: some MacroExpansionContext - ) async throws -> [DeclSyntax] -} -``` - -The `MacroExpansionDeclSyntax` node provides the syntax tree for the use site (e.g., `#warning("unsupported configuration")`), and has the same grammar and members as the `MacroExpansionExprSyntax` node introduced in [SE-0382](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion). The grammar parallels `macro-expansion-expression`: - -``` -declaration -> macro-expansion-declaration -macro-expansion-declaration -> '#' identifier generic-argument-clause[opt] function-call-argument-clause[opt] trailing-closures[opt] -``` - The implementation of a `warning` declaration macro extracts the string literal argument (producing an error if there wasn't one) and emits a warning. It returns an empty list of declarations: ```swift public struct WarningMacro: DeclarationMacro { public static func expansion( - of node: some FreestandingMacroExpansionSyntax, in context: inout MacroExpansionContext + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext ) throws -> [DeclSyntax] { guard let messageExpr = node.argumentList.first?.expression.as(StringLiteralExprSyntax.self), messageExpr.segments.count == 1, @@ -120,52 +83,11 @@ public struct WarningMacro: DeclarationMacro { } ``` -A macro that does introduce declarations needs to document the names it introduces. Just like `@attached`, the `@freestanding` attribute has a `names` argument that provides the names introduced by a macro. For example, consider a macro that declares a `main` function suitable for use with the [`@main` attribute](https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md) but that handles an exit code, e.g., - -```swift -@main -struct App { - #main { - if hasBadArgument() { return -1 } - if noNetwork() { return -2 } - return 0 - } -} -``` - -will be expanded to: - -```swift -@main -struct App { - static func $_unique_name() -> Int { - if hasBadArgument() { return -1 } - if noNetwork() { return -2 } - return 0 - } - - static func main() { - guard let exitCode = $_unique_name(), exitCode == 0 else { - exit(exitCode) - } - } -} -``` - -The `main` macro would be declared as follows: - -```swift -@freestanding(declaration, names: named(main)) -macro main(_ body: () -> Int) -``` - -This specifies that the macro will produce a declaration named `main`. It is also allowed to produce declarations with names produced by `MacroExpansionContext.makeUniqueName(_:)` (implied by `$_unique_name` in the example above) without documenting them, because they are not visible to other parts of the program not generated by the macro. The reasons for documenting macro names are provided within the detailed design. - ## Detailed design ### Syntax -The syntactic representation of a freestanding macro expansion site is a macro expansion declaration. A macro expansion declaration is described by the following grammar. It has the same production rule as a macro expansion expression. +The syntactic representation of a freestanding macro expansion site is a macro expansion declaration. A macro expansion declaration is described by the following grammar. It has the same production rule as a [macro expansion expression](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion): ``` declaration -> macro-expansion-declaration @@ -181,10 +103,10 @@ At top level and function scope where both expressions and declarations are allo ### Restrictions -Like attached peer macros, a freestanding macro can expand to any declaration that is syntatically and semantically well-formed within the context where the macro is expanded. It shares the same requirements and restrictions. +Like attached peer macros, a freestanding declaration macro can expand to any declaration that is syntatically and semantically well-formed within the context where the macro is expanded. It shares the same requirements and restrictions: - [**Specifying newly-introduced names**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#specifying-newly-introduced-names) - - Note that only `named(...)` and `arbitrary` are allowed as macro-introduced names for a declaration macro. + - Note that only `named(...)` and `arbitrary` are allowed as macro-introduced names for a declaration macro. `overloaded`, `prefixed`, and `suffixed` do not make sense when there is no declaration from which to derive names. - [**Visibility of names used and introduced by macros**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#visibility-of-names-used-and-introduced-by-macros) - [**Permitted declaration kinds**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#permitted-declaration-kinds) @@ -194,7 +116,7 @@ One additional restriction is that a macro declaration can have at most one free @freestanding(expression) @freestanding(declaration) // error: a macro cannot have multiple freestanding macro roles macro foo() -``` +``` ### Examples @@ -243,12 +165,15 @@ The Swift Standard Library makes extensive use of the [gyb](https://github.com/a ```swift @freestanding(declaration, names: arbitrary) -macro gyb(String, [Any]) +macro gyb(String, [Any]) = #externalMacro(module: "MyMacros", type: "GYBMacro") -#gyb({ +#gyb( + """ public struct Int${0} { ... } public struct UInt${0} { ... } -}, [8, 16, 32, 64]) + """, + [8, 16, 32, 64] +) ``` This expands to: @@ -273,7 +198,7 @@ Declaring a data model for an existing textual serialization may need some amoun ```swift @freestanding(declaration, names: arbitrary) -macro jsonModel(String) +macro jsonModel(String) = #externalMacro(module: "MyMacros", type: "JSONModelMacro") struct JSONValue: Codable { #jsonModel(""" @@ -314,9 +239,7 @@ struct JSONValue: Codable { ## Source compatibility -Freestanding macros use the same syntax introduced for expression macros, which were themselves a pure extension without an impact on source compatibility. There is a syntactic ambiguity between expression and freestanding declaration macros, i.e., `#warn("watch out")` within a function body could refer to multiple freestanding macros of different roles. The distinction will need to be determined semantically with the same overload resolution rules as function calls. - -Attached declaration macros use the same syntax introduced for custom attributes (such as property wrappers), and therefore do not have an impact on source compatibility. +Freestanding macros use the same syntax introduced for expression macros, which were themselves a pure extension without an impact on source compatibility. Because a given macro can only have a single freestanding role, and we retain the parsing rules for macro expansion expressions, this proposal introduces no new ambiguities with SE-0392 "Expression Macros". ## Effect on ABI stability From 750262264f3c7c07c71330a7270a8f9785e9b254 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 24 Apr 2023 12:20:36 -0700 Subject: [PATCH 3096/4563] Apply suggestions from code review Co-authored-by: Richard Wei --- proposals/nnnn-freestanding-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-macros.md index 76219ac57b..a5cfdd8a43 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-macros.md @@ -159,7 +159,7 @@ public struct Int64 { ... } public struct UInt64 { ... } ``` -### Template code generator +### Template code generation The Swift Standard Library makes extensive use of the [gyb](https://github.com/apple/swift/blob/main/utils/gyb.py) tool to generate boilerplate-y Swift code such as [tgmath.swift.gyb](https://github.com/apple/swift/blob/main/stdlib/public/Platform/tgmath.swift.gyb). The template code is written in `.gyb` files, which are processed by the gyb tool separately before Swift compilation. With freestanding declaration macros, one could write a macro to accept a string as a template and a list of replacement values, allowing templates to be defined inline and eliminating the need to set up a separate build phase. @@ -192,7 +192,7 @@ This expands to: public struct UInt64 { ... } ``` -### Data model generator +### Data model generation Declaring a data model for an existing textual serialization may need some amount of eyeballing and is prone to errors. A freestanding declaration macro can be used to analyze a template textual serialization, e.g. JSON, and declare a model data structure against the template. From 82afa2e79e451e19b842b4f4c7c7a58a05440e9c Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Mon, 24 Apr 2023 14:04:52 -0700 Subject: [PATCH 3097/4563] Revision: replace target group param with packageAccess in spm --- proposals/0386-package-access-modifier.md | 39 ++++++----------------- 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index aadea88cce..f94aeffdf0 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -6,7 +6,8 @@ * Status: **Active Review (January 26th...Feburary 8th, 2023** * Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/62700), [apple/swift#62704](https://github.com/apple/swift/pull/62704), [apple/swift#62652](https://github.com/apple/swift/pull/62652), [apple/swift#62652](https://github.com/apple/swift/pull/62652) * Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) ([review](https://forums.swift.org/t/se-0386-package-access-modifier/62808)) -* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/28fd2fb9b7258117f912cec5e5f7eb178520fbf2/proposals/NNNN-package-access-modifier.md) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/28fd2fb9b7258117f912cec5e5f7eb178520fbf2/proposals/NNNN-package-access-modifier.md), +[2](https://github.com/apple/swift-evolution/blob/main/proposals/0386-package-access-modifier.md) ## Introduction @@ -18,7 +19,7 @@ At the most basic level, every Swift program is just a collection of declaration As a language, Swift recognizes some of these levels. Modules are the smallest unit of library structure, with an independent interface and non-cyclic dependencies, and it makes sense for Swift to recognize that in both namespacing and access control. Files are the smallest grouping beneath that and are often used to collect tightly-related declarations, so they also make sense to respect in access control. -Packages, as expressed by the Swift Package Manager, are a unit of code distribution. Some packages contain just a single module, but it's frequently useful to split a package's code into multiple modules. For example, when a module contains some internal helper APIs, those APIs can be split out into a utility module and maybe reused by other modules or packages. +Packages, as expressed by the Swift Package Manager, are a unit of code distribution. Some packages contain just a single module, but it's frequently useful to split a package's code into multiple modules. For example, when a module contains some `internal` helper APIs, those APIs can be split out into a utility module and maybe reused by other modules or packages. However, because Swift does not recognize organizations of code above the module level, it is not possible to create APIs like this that are purely internal to the package. To be usable from other modules within the package, the API must be public, but this means it can also be used outside of the package. This allows clients to form unwanted source dependencies on the API. It also means the built module has to export the API, which has negative implications for code size and performance. @@ -134,22 +135,13 @@ If `-package-name` is not given, the `package` access modifier is disallowed. S The build system should make a best effort to ensure that package names are unique. The Swift Package Manager already has a concept of a package identity string for every package. This string is verified to be unique, and it already works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. -If a target needs to be excluded from the package boundary or needes to be part of a subgroup within the package boundary, it can be set in a target setting, with a new parameter `group` in the manifest, like so: +If a target needs to be excluded from the package boundary, it can be set in a new setting per target `packageAccess` in the manifest, like so: ``` - .target(name: "Game", dependencies: ["Engine"], group: .excluded) + .target(name: "Game", dependencies: ["Engine"], packageAccess: false) ``` -The `group` setting is mapped to the following type: - -``` -enum Group { - case package - case excluded -} -``` - -The default value is `.package`, and the target is built with `-package-name PACKAGE_ID`. If set to `.excluded`, no `-package-name` is passed when building the target, thus the target has no access to any package symbols; it essentially acts as if it's a client outside of the package. This would be useful for an example app or a black-box testing target in the same package. +The `packageAccess` setting per target is set to `true` by default. The target is built with `-package-name PACKAGE_ID` where `PACKAGE_ID` is the manifest's package identifier. If set to `false`, `-package-name` is not passed when building the target, thus the target has no access to any package symbols; it essentially acts as if it's a client outside of the package. This would be useful for an example app or a black-box test target in the package. ### Package Symbols Distribution @@ -280,22 +272,9 @@ Potential solutions include introducing new keywords for specific access combina Sometimes entire modules are meant to be private to the package that provides them. Allowing this to be expressed directly would allow these utility modules to be completely hidden outside of the package, avoiding unwanted dependencies on the existence of the module. It would also allow the build system to automatically namespace the module within the package, reducing the need for [explicit module aliases](https://github.com/apple/swift-evolution/blob/main/proposals/0339-module-aliasing-for-disambiguation.md) when utility modules of different packages share a name (such as `Utility`) or when multiple versions of a package need to be built into the same program. -### Package Boundary Customization - -The `group` setting per target in Swift Package Manager allows a target to opt out of a package boundary, but if there is a need for creating multi-groups within a package, the setting could be expanded to allow that by introducing a `.named(GROUP_ID)` option, where `GROUP_ID` is a unique identifier for a specific group the target belongs to. +### Sub-grouping in Package -``` -enum Group { - case package - case excluded - case named(String) -} -``` -For example, the new option could be specified in a target setting in a manifest like so: - -``` - .target(name: "Game", dependencies: ["Engine"], group: .named("Core")) -``` +If there is a need to split a package into sub-packages, it is currently recommended to create new repositories and manifests and create a dependency among them. However, if there is a demand for creating a more lightweight sub-pacaking mechanism, it might be useful to allow sub-grouping within a package. A `group` setting could be created per target, which allows multiple targets to belong to a specific group within the package. ### Optimizations @@ -347,7 +326,7 @@ Instead of adding a new package access level above modules, we could allow modul ### `@usableFromPackageInline` -A new attribute `@usableFromPackageInline` was considered, which would allow an internal declaration to be used in the body of an `@inlinable package` declaration, but not in `@inlinable public`. It was however concluded unnecessary as inlining for package declarations could be enabled by default via optimization in the future (since they are in the same package boundary). +A new attribute `@usableFromPackageInline` was considered, which would allow an `internal` declaration to be used in the body of an `@inlinable package` declaration, but not in `@inlinable public`. It was however concluded unnecessary as inlining for `package` declarations could be enabled by default via optimization in the future (since they are in the same package boundary). ## Acknowledgments From f75ed15533e8a9e7872cd54c35ca8e6619733cf2 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Mon, 24 Apr 2023 14:58:37 -0700 Subject: [PATCH 3098/4563] Accept the modified SE-0394 --- proposals/0394-swiftpm-expression-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0394-swiftpm-expression-macros.md b/proposals/0394-swiftpm-expression-macros.md index 85b708573f..5433b20ae2 100644 --- a/proposals/0394-swiftpm-expression-macros.md +++ b/proposals/0394-swiftpm-expression-macros.md @@ -3,9 +3,9 @@ * Proposal: [SE-0394](0394-swiftpm-expression-macros.md) * Authors: [Boris Buegling](https://github.com/neonichu), [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) -* Status: **Active Review (April 3...April 17, 2023)** +* Status: **Accepted** * Implementation: **Available behind pre-release tools-version** ([apple/swift-package-manager#6185](https://github.com/apple/swift-package-manager/pull/6185), [apple/swift-package-manager#6200](https://github.com/apple/swift-package-manager/pull/6200)) -* Review: ([pitch 1](https://forums.swift.org/t/pitch-package-manager-support-for-custom-macros/63482)) ([pitch 2](https://forums.swift.org/t/pitch-2-package-manager-support-for-custom-macros/63868)) ([review](https://forums.swift.org/t/se-0394-package-manager-support-for-custom-macros/64170)) +* Review: ([pitch 1](https://forums.swift.org/t/pitch-package-manager-support-for-custom-macros/63482)) ([pitch 2](https://forums.swift.org/t/pitch-2-package-manager-support-for-custom-macros/63868)) ([review](https://forums.swift.org/t/se-0394-package-manager-support-for-custom-macros/64170)) ([acceptance](https://forums.swift.org/t/accepted-se-0394-package-manager-support-for-custom-macros/64589)) ## Introduction From 71d0de4451f88c7819cc9939962352cdee692283 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 25 Apr 2023 02:51:23 -0400 Subject: [PATCH 3099/4563] Make a few wording suggestions on the latest SE-0386 revision --- proposals/0386-package-access-modifier.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index f94aeffdf0..4beaea4118 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -135,13 +135,13 @@ If `-package-name` is not given, the `package` access modifier is disallowed. S The build system should make a best effort to ensure that package names are unique. The Swift Package Manager already has a concept of a package identity string for every package. This string is verified to be unique, and it already works as a package name, so SwiftPM will pass it down automatically. Other build systems such as Bazel may need to introduce a new build setting for a package name. Since it needs to be unique, a reverse-DNS name may be used to avoid clashing. -If a target needs to be excluded from the package boundary, it can be set in a new setting per target `packageAccess` in the manifest, like so: +If a target needs to be excluded from the package boundary, that can be done with a new `packageAccess` setting in the manifest, like so: ``` .target(name: "Game", dependencies: ["Engine"], packageAccess: false) ``` -The `packageAccess` setting per target is set to `true` by default. The target is built with `-package-name PACKAGE_ID` where `PACKAGE_ID` is the manifest's package identifier. If set to `false`, `-package-name` is not passed when building the target, thus the target has no access to any package symbols; it essentially acts as if it's a client outside of the package. This would be useful for an example app or a black-box test target in the package. +The `packageAccess` setting is set to `true` by default, and the target is built with `-package-name PACKAGE_ID` where `PACKAGE_ID` is the manifest's package identifier. If `packageAccess` is set to `false`, `-package-name` is not passed when building the target, thus the target has no access to any package symbols; it essentially acts as if it's a client outside of the package. This would be useful for an example app or a black-box test target in the package. ### Package Symbols Distribution @@ -272,9 +272,11 @@ Potential solutions include introducing new keywords for specific access combina Sometimes entire modules are meant to be private to the package that provides them. Allowing this to be expressed directly would allow these utility modules to be completely hidden outside of the package, avoiding unwanted dependencies on the existence of the module. It would also allow the build system to automatically namespace the module within the package, reducing the need for [explicit module aliases](https://github.com/apple/swift-evolution/blob/main/proposals/0339-module-aliasing-for-disambiguation.md) when utility modules of different packages share a name (such as `Utility`) or when multiple versions of a package need to be built into the same program. -### Sub-grouping in Package +### Grouping within a package -If there is a need to split a package into sub-packages, it is currently recommended to create new repositories and manifests and create a dependency among them. However, if there is a demand for creating a more lightweight sub-pacaking mechanism, it might be useful to allow sub-grouping within a package. A `group` setting could be created per target, which allows multiple targets to belong to a specific group within the package. +The basic language design of this proposal can work for any group of related modules, but the application of that design in SPM allows only a single such group per SPM package. Developers with complex SPM packages sometimes find that they have multiple architectural "layers" within a single package and may wish to make `package` apply only within a layer. Logically, it makes some sense to put each layer in its own package. Pragmatically, because different SPM packages must currently live in separate repositories and be independently versioned, splitting a package that way introduces a huge amount of extra complexity to the development process, and it is not something that should be done casually. + +There are several reasonable ways that SPM could evolve to support multiple layers within a single package repository. One would be to allow targets to be grouped within a manifest, such as by adding a `group` parameter to `.target`. An earlier version of this proposal suggested this and even designed the `packageAccess:` exclusion feature around it. However, this would tend to lead to large, complex manifests that mingled the details of all the layers together. A very different approach would be to allow the creation of sub-packages within a repository, each with its own manifest. SPM would treat these sub-packages as logically separate units that happen to share a single repository and version. Because they would be described in independent manifests, they would feel like different packages, and it would make sense for `package` access to be scoped within them. ### Optimizations @@ -284,7 +286,7 @@ If there is a need to split a package into sub-packages, it is currently recomme ## Source compatibility -A new keyword `package` is added as a new access modifier. It is a contextual keyword and is an additive change. Symbols that are named `package` should not require renaming, and the source code as is should continue to work. +The new `package` access modifier is a contextual keyword. Existing symbols that are named `package` should not require renaming, and existing source code should continue to work. ## Effect on ABI stability @@ -326,7 +328,7 @@ Instead of adding a new package access level above modules, we could allow modul ### `@usableFromPackageInline` -A new attribute `@usableFromPackageInline` was considered, which would allow an `internal` declaration to be used in the body of an `@inlinable package` declaration, but not in `@inlinable public`. It was however concluded unnecessary as inlining for `package` declarations could be enabled by default via optimization in the future (since they are in the same package boundary). +An earlier version of this proposal included a new attribute `@usableFromPackageInline`, which would have allowed an `internal` declaration to be used in the body of an `@inlinable package` declaration, but not in an `@inlinable public` declaration. Under the logic of this proposal, there is no good reason to make a declaration `@usableFromPackageInline internal` instead of simply `package`: the uses of the latter will be restricted to the package and therefore by assumption can still be easily found and reviewed. Furthermore, it is a goal of the Swift project to not require extensive `@inlinable` annotations just to enable basic optimizations between modules: there should be little reason in the long run to have an `@inlinable package` declaration at all. Therefore this attribute has been removed from the proposal. ## Acknowledgments From 2668aae7c445c0aa6f4f7726fdfe728a973d2108 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 25 Apr 2023 12:17:23 -0400 Subject: [PATCH 3100/4563] Add Holly's requirement inference section --- proposals/nnnn-variadic-types.md | 33 ++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-variadic-types.md b/proposals/nnnn-variadic-types.md index 62bf1d8aee..aa2a64ad69 100644 --- a/proposals/nnnn-variadic-types.md +++ b/proposals/nnnn-variadic-types.md @@ -1,7 +1,7 @@ # Allow Generic Types to Abstract Over Packs * Proposal: [SE-NNNN](nnnn-variadic-types.md) -* Authors: [Slava Pestov](https://github.com/slavapestov) +* Authors: [Slava Pestov](https://github.com/slavapestov), [Holly Borla](https://github.com/hborla) * Review Manager: TBD * Upcoming Feature Flag: `VariadicGenerics` * Previous Proposal: [SE-0393](0393-parameter-packs.md) @@ -135,7 +135,36 @@ struct S { ### Requirements -The behavior of generic requirements on type parameter packs is unchanged between generic functions and generic types. +The behavior of generic requirements on type parameter packs is mostly unchanged between generic functions and generic types. However, allowing types to abstract over parameter packs introduces _requirement inference_ of [generic requirement expansions](https://github.com/apple/swift-evolution/blob/main/proposals/0393-parameter-packs.md#generic-requirements). Requirement expansion inference follows these rules: + +1. If a generic type that imposes an inferred scalar requirement is applied to a pack element inside a pack expansion, the inferred requirement is a requirement expansion. +2. If a generic type imposes an inferred requirement expansion, the requirement is expanded for each of the concrete generic arguments. +3. If a generic type that imposes an inferred requirement expansion is applied to a pack element inside a pack expansion: + a. The inferred requirement is invalid if it contains multiple pack elements captured by expansions at different depths. + b. Otherwise, the nested requirement expansion is semantically equivalent to the innermost requirement expansion. + + The below code demonstrates each of the above rules: + + ```swift + protocol P { + associatedtype A + } + struct ImposeRequirement where T: P {} + struct ImposeRepeatedRequirement where repeat each T: P {} + struct ImposeRepeatedSameType where repeat T1.A == each T2 {} + + // Infers 'repeat each U: P' + func demostrate1(_: repeat ImposeRequirement) + + // Infers 'Int: P, V: P, repeat each U: P' + func demostrate2(_: ImposeRepeatedRequirement) + + // Error. Would attempt to infer 'repeat repeat U'.A == V' which is not a supported requirement in the language + func demonstrate3a(_: repeat ImposeRepeatedSameType)) + + // Infers 'Int: P, repeat each V: P' + func demostrate3b(_: repeat (each U, ImposeRequirementExpansion)) +``` ### Conformances From 70b132dd3f459af23876f94f493ffb567f12cb89 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 25 Apr 2023 12:18:38 -0400 Subject: [PATCH 3101/4563] Fix nested list --- proposals/nnnn-variadic-types.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-variadic-types.md b/proposals/nnnn-variadic-types.md index aa2a64ad69..7cc4dfcbaf 100644 --- a/proposals/nnnn-variadic-types.md +++ b/proposals/nnnn-variadic-types.md @@ -140,8 +140,9 @@ The behavior of generic requirements on type parameter packs is mostly unchanged 1. If a generic type that imposes an inferred scalar requirement is applied to a pack element inside a pack expansion, the inferred requirement is a requirement expansion. 2. If a generic type imposes an inferred requirement expansion, the requirement is expanded for each of the concrete generic arguments. 3. If a generic type that imposes an inferred requirement expansion is applied to a pack element inside a pack expansion: - a. The inferred requirement is invalid if it contains multiple pack elements captured by expansions at different depths. - b. Otherwise, the nested requirement expansion is semantically equivalent to the innermost requirement expansion. + + 1. The inferred requirement is invalid if it contains multiple pack elements captured by expansions at different depths. + 2. Otherwise, the nested requirement expansion is semantically equivalent to the innermost requirement expansion. The below code demonstrates each of the above rules: From b3ffb0b02dabbb679b3f1478e0bc557db669c710 Mon Sep 17 00:00:00 2001 From: Harlan Haskins Date: Tue, 25 Apr 2023 12:41:17 -0600 Subject: [PATCH 3102/4563] Update SE-0364 to new @retroactive syntax (#1969) * Update SE-0364 to new @retroactive syntax * Update proposal per review feedback --- .../0364-retroactive-conformance-warning.md | 50 ++++++++++++++----- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/proposals/0364-retroactive-conformance-warning.md b/proposals/0364-retroactive-conformance-warning.md index b35a29e06c..53d6cf26bb 100644 --- a/proposals/0364-retroactive-conformance-warning.md +++ b/proposals/0364-retroactive-conformance-warning.md @@ -55,17 +55,22 @@ This proposal adds a warning that explicitly calls out this pattern as problematic and unsupported. ```swift -/tmp/retro.swift:3:1: warning: extension declares a conformance of imported type 'Date' to imported protocol 'Identifiable'; this will not behave correctly if the owners of 'Foundation' introduce this conformance in the future +/tmp/retro.swift:3:1: warning: extension declares a conformance of imported type +'Date' to imported protocol 'Identifiable'; this will not behave correctly if +the owners of 'Foundation' introduce this conformance in the future extension Date: Identifiable { ^ ``` -If absolutely necessary, clients can silence this warning by explicitly -module-qualifying both of the types in question, to explicitly state that they -are intentionally declaring this conformance: +If absolutely necessary, clients can silence this warning by adding a new attribute, +`@retroactive`, to the protocol in question. -``` -extension Foundation.Date: Swift.Identifiable { +The compiler will enforce that there is an explicit `@retroactive` conformance +for each protocol included up the hierarchy. If needed, it will emit a fix-it to +generate extensions for each retroactive conformance in the hierarchy. + +```swift +extension Date: @retroactive Identifiable { // ... } ``` @@ -78,24 +83,43 @@ This warning will appear only if all of the following conditions are met, with a - The protocol for which the extension introduces the conformance is declared in a different module from the extension. -The following exceptions apply: +The following exceptions apply to either the conforming type or the protocol: -- If the type is declared in a Clang module, and the extension in question is declared in a Swift - overlay, this is not considered a retroactive conformance. -- If the type is declared or transitively imported in a bridging header or through the +- If it is declared in a Clang module, and the extension in question is declared + in a Swift overlay of that module, this is not considered a retroactive conformance. +- If it is declared or transitively imported in a bridging header or through the `-import-objc-header` flag, and the type does not belong to any other module, the warning is not emitted. This could be a retroactive conformance, but since these are added to an implicit module called `__ObjC`, we have to assume the client takes responsibility for these declaration. +- If it is declared in one module, but uses the `@_originallyDefined(in:)` attribute to + signify that it has moved from a different module, then this will not warn. For clarification, the following are still valid, safe, and allowed: - Conformances of external types to protocols defined within the current module. - Extensions of external types that do not introduce a conformance. These do not introduce runtime conflicts, since the module name is mangled into the symbol. +The `@retroactive` attribute may only be used in extensions, and only when used +to introduce a conformance that requires its existence. It will be an error to +use `@retroactive` outside of the declaration of a retroactive conformance. + ## Source compatibility -This proposal is just a warning addition, and doesn't affect source -compatibility. +`@retroactive` is a new attribute, but it is purely additive; it can be accepted +by all language versions. It does mean projects building with an older Swift +will not have access to this syntax, so as a source compatible fallback, +a client can silence this warning by fully-qualifying all types in the extension. +As an example, the above conformance can also be written as + +```swift +extension Foundation.Date: Swift.Identifiable { + // ... +} +``` + +This will allow projects that need to build with multiple versions of Swift, and +which have valid reason to declare such conformances, to declare them without +tying their project to a newer compiler build. ## Effect on ABI stability @@ -103,7 +127,7 @@ This proposal has no effect on ABI stability. ## Effect on API resilience -This proposal has direct effect on API resilience, but has the indirect effect of reducing +This proposal has no direct effect on API resilience, but has the indirect effect of reducing the possible surface of client changes introduced by the standard library adding new conformances. ## Alternatives considered From 565cd9bbb06ff2a458d76dbd2c113cebb446a155 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 25 Apr 2023 14:50:04 -0400 Subject: [PATCH 3103/4563] Update 0364-retroactive-conformance-warning.md (#2024) --- proposals/0364-retroactive-conformance-warning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0364-retroactive-conformance-warning.md b/proposals/0364-retroactive-conformance-warning.md index 53d6cf26bb..22827bae00 100644 --- a/proposals/0364-retroactive-conformance-warning.md +++ b/proposals/0364-retroactive-conformance-warning.md @@ -8,7 +8,7 @@ * Review: ([first pitch](https://forums.swift.org/t/warning-for-retroactive-conformances-if-library-evolution-is-enabled/45321)) ([second pitch](https://forums.swift.org/t/pitch-warning-for-retroactive-conformances-of-external-types-in-resilient-libraries/56243)) ([first review](https://forums.swift.org/t/se-0364-warning-for-retroactive-conformances-of-external-types/58922)) - ([revision](https://forums.swift.org/t/returned-for-revision-se-0364-warning-for-retroactive-conformance-of-external-types/59729)) + ([second review](https://forums.swift.org/t/second-review-se-0364-warning-for-retroactive-conformances-of-external-types/64615)) ## Introduction From 9412bdd3e8086791efdab281c52862a7ff4b41cb Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 25 Apr 2023 13:15:47 -0700 Subject: [PATCH 3104/4563] Updates to the grouping section --- proposals/0386-package-access-modifier.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index a7c1f24c80..f28d4a7e41 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -274,9 +274,9 @@ Sometimes entire modules are meant to be private to the package that provides th ### Grouping within a package -The basic language design of this proposal can work for any group of related modules, but the application of that design in SPM allows only a single such group per SPM package. Developers with complex SPM packages sometimes find that they have multiple architectural "layers" within a single package and may wish to make `package` apply only within a layer. Logically, it makes some sense to put each layer in its own package. Pragmatically, because different SPM packages must currently live in separate repositories and be independently versioned, splitting a package that way introduces a huge amount of extra complexity to the development process, and it is not something that should be done casually. +The basic language design of this proposal can work for any group of related modules, but the application of that design in SPM allows only a single such group per SPM package. Developers with complex SPM packages sometimes find that they have multiple architectural "layers" within a single package and may wish to make `package` apply only within a layer. Logically, it makes some sense to put each layer in its own package. Pragmatically, because different SPM packages currently require separate manifests and be independently versioned, splitting a package that way introduces extra complexity to the development process, and it is not something that should be done casually. -There are several reasonable ways that SPM could evolve to support multiple layers within a single package repository. One would be to allow targets to be grouped within a manifest, such as by adding a `group` parameter to `.target`. An earlier version of this proposal suggested this and even designed the `packageAccess:` exclusion feature around it. However, this would tend to lead to large, complex manifests that mingled the details of all the layers together. A very different approach would be to allow the creation of sub-packages within a repository, each with its own manifest. SPM would treat these sub-packages as logically separate units that happen to share a single repository and version. Because they would be described in independent manifests, they would feel like different packages, and it would make sense for `package` access to be scoped within them. +There are several reasonable ways that SPM could evolve to support multiple layers within a single package. One would be to allow targets to be grouped within a manifest, such as by adding a `group` parameter to `.target`. An earlier version of this proposal suggested this and even designed the `packageAccess:` exclusion feature around it. However, this would tend to lead to large, complex manifests that mingled the details of all the layers together. A very different approach would be to make it easier to create sub-packages by mapping the location of each manifest to where a group of similar targets reside in the file system and optimizing dependency graph resolution. These sub-packgaes would logically be separate units but share a single repository and version. Because they would be described in independent manifests, they would feel like different packages, and it would make sense for `package` access to be scoped within them. ### Optimizations From 783baff33baad1faca78b580b2a17b7eafacf321 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 25 Apr 2023 13:45:44 -0700 Subject: [PATCH 3105/4563] Revert "Updates to the grouping section" This reverts commit 9412bdd3e8086791efdab281c52862a7ff4b41cb. --- proposals/0386-package-access-modifier.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index f28d4a7e41..a7c1f24c80 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -274,9 +274,9 @@ Sometimes entire modules are meant to be private to the package that provides th ### Grouping within a package -The basic language design of this proposal can work for any group of related modules, but the application of that design in SPM allows only a single such group per SPM package. Developers with complex SPM packages sometimes find that they have multiple architectural "layers" within a single package and may wish to make `package` apply only within a layer. Logically, it makes some sense to put each layer in its own package. Pragmatically, because different SPM packages currently require separate manifests and be independently versioned, splitting a package that way introduces extra complexity to the development process, and it is not something that should be done casually. +The basic language design of this proposal can work for any group of related modules, but the application of that design in SPM allows only a single such group per SPM package. Developers with complex SPM packages sometimes find that they have multiple architectural "layers" within a single package and may wish to make `package` apply only within a layer. Logically, it makes some sense to put each layer in its own package. Pragmatically, because different SPM packages must currently live in separate repositories and be independently versioned, splitting a package that way introduces a huge amount of extra complexity to the development process, and it is not something that should be done casually. -There are several reasonable ways that SPM could evolve to support multiple layers within a single package. One would be to allow targets to be grouped within a manifest, such as by adding a `group` parameter to `.target`. An earlier version of this proposal suggested this and even designed the `packageAccess:` exclusion feature around it. However, this would tend to lead to large, complex manifests that mingled the details of all the layers together. A very different approach would be to make it easier to create sub-packages by mapping the location of each manifest to where a group of similar targets reside in the file system and optimizing dependency graph resolution. These sub-packgaes would logically be separate units but share a single repository and version. Because they would be described in independent manifests, they would feel like different packages, and it would make sense for `package` access to be scoped within them. +There are several reasonable ways that SPM could evolve to support multiple layers within a single package repository. One would be to allow targets to be grouped within a manifest, such as by adding a `group` parameter to `.target`. An earlier version of this proposal suggested this and even designed the `packageAccess:` exclusion feature around it. However, this would tend to lead to large, complex manifests that mingled the details of all the layers together. A very different approach would be to allow the creation of sub-packages within a repository, each with its own manifest. SPM would treat these sub-packages as logically separate units that happen to share a single repository and version. Because they would be described in independent manifests, they would feel like different packages, and it would make sense for `package` access to be scoped within them. ### Optimizations From dbadd4636f1d1deba49df5872eeda2611fd4f023 Mon Sep 17 00:00:00 2001 From: Ellie Shin Date: Tue, 25 Apr 2023 13:50:03 -0700 Subject: [PATCH 3106/4563] Update formatting --- proposals/0386-package-access-modifier.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index a7c1f24c80..5eeeee79d9 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -272,7 +272,7 @@ Potential solutions include introducing new keywords for specific access combina Sometimes entire modules are meant to be private to the package that provides them. Allowing this to be expressed directly would allow these utility modules to be completely hidden outside of the package, avoiding unwanted dependencies on the existence of the module. It would also allow the build system to automatically namespace the module within the package, reducing the need for [explicit module aliases](https://github.com/apple/swift-evolution/blob/main/proposals/0339-module-aliasing-for-disambiguation.md) when utility modules of different packages share a name (such as `Utility`) or when multiple versions of a package need to be built into the same program. -### Grouping within a package +### Grouping Within A Package The basic language design of this proposal can work for any group of related modules, but the application of that design in SPM allows only a single such group per SPM package. Developers with complex SPM packages sometimes find that they have multiple architectural "layers" within a single package and may wish to make `package` apply only within a layer. Logically, it makes some sense to put each layer in its own package. Pragmatically, because different SPM packages must currently live in separate repositories and be independently versioned, splitting a package that way introduces a huge amount of extra complexity to the development process, and it is not something that should be done casually. @@ -284,7 +284,7 @@ There are several reasonable ways that SPM could evolve to support multiple laye * By default, `package` symbols are exported in the final libraries/executables. It would be useful to introduce a build setting that allows users to hide package symbols for statically linked libraries; this would help with code size and build time optimizations. -## Source compatibility +## Source Compatibility The new `package` access modifier is a contextual keyword. Existing symbols that are named `package` should not require renaming, and existing source code should continue to work. From ba6e6ad839c2c591999bd3d51190c0fe91b029a3 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 25 Apr 2023 19:25:07 -0400 Subject: [PATCH 3107/4563] Formally extend the SE-0386 review period to consider the new revision --- proposals/0386-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index 5eeeee79d9..088ea46405 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -3,7 +3,7 @@ * Proposal: [SE-0386](0386-package-access-modifier.md) * Authors: [Ellie Shin](https://github.com/elsh), [Alexis Laferriere](https://github.com/xymus) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (March 31st...April 10th, 2023** +* Status: **Active Review (March 31st...May 1st, 2023** * Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/62700), [apple/swift#62704](https://github.com/apple/swift/pull/62704), [apple/swift#62652](https://github.com/apple/swift/pull/62652), [apple/swift#62652](https://github.com/apple/swift/pull/62652) * Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) ([first review](https://forums.swift.org/t/se-0386-package-access-modifier/62808)) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/28fd2fb9b7258117f912cec5e5f7eb178520fbf2/proposals/NNNN-package-access-modifier.md), From 4160ffdb27ead98743cd0d6914a69f1e17ecbd0c Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 26 Apr 2023 08:28:13 +0100 Subject: [PATCH 3108/4563] Update 0364-retroactive-conformance-warning.md (#2025) Update status of second review. --- proposals/0364-retroactive-conformance-warning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0364-retroactive-conformance-warning.md b/proposals/0364-retroactive-conformance-warning.md index 22827bae00..145217e955 100644 --- a/proposals/0364-retroactive-conformance-warning.md +++ b/proposals/0364-retroactive-conformance-warning.md @@ -3,7 +3,7 @@ * Proposal: [SE-0364](0364-retroactive-conformance-warning.md) * Author: [Harlan Haskins](https://github.com/harlanhaskins) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Returned for Revision** +* Status: **Active review (April 25...May 9)** * Implementation: [apple/swift#36068](https://github.com/apple/swift/pull/36068) * Review: ([first pitch](https://forums.swift.org/t/warning-for-retroactive-conformances-if-library-evolution-is-enabled/45321)) ([second pitch](https://forums.swift.org/t/pitch-warning-for-retroactive-conformances-of-external-types-in-resilient-libraries/56243)) From 2e733a9c3ef3d5f5674dc0d8ba06f936d7491159 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 26 Apr 2023 09:00:50 +0100 Subject: [PATCH 3109/4563] Update 0391-package-registry-publish.md (#2026) Update status to Accepted. --- proposals/0391-package-registry-publish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0391-package-registry-publish.md b/proposals/0391-package-registry-publish.md index 914bc548fe..d940445a3f 100644 --- a/proposals/0391-package-registry-publish.md +++ b/proposals/0391-package-registry-publish.md @@ -3,7 +3,7 @@ * Proposal: [SE-0391](0391-package-registry-publish.md) * Author: [Yim Lee](https://github.com/yim-lee) * Review Manager: [Tom Doron](https://github.com/tomerd) -* Status: **Active review (February 27 ... March 13, 2023)** +* Status: **Accepted** * Implementation: * https://github.com/apple/swift-package-manager/pull/6101 * https://github.com/apple/swift-package-manager/pull/6146 From 926909d5c75aefd5225c013d58de0b87b58317b9 Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Wed, 26 Apr 2023 21:13:56 -0700 Subject: [PATCH 3110/4563] Fix review manager link in SE-0384 (#2027) Ensure review manager name is a markdown link to their GitHub profile URL. This also ensures correct link behavior on the Swift Evolution dashboard. --- ...-importing-forward-declared-objc-interfaces-and-protocols.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md index e078d420a5..9669e7d019 100644 --- a/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md +++ b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md @@ -2,7 +2,7 @@ * Proposal: [SE-0384](0384-importing-forward-declared-objc-interfaces-and-protocols.md) * Author: [Nuri Amari](https://github.com/NuriAmari) -* Review Manager: Tony Allevato (https://github.com/allevato) +* Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Implemented (Swift 5.9)** * Implementation: https://github.com/apple/swift/pull/61606 * Upcoming Feature Flag: `ImportObjcForwardDeclarations` From e44334cf967434c744e8d1467c84b03d205510b7 Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Thu, 27 Apr 2023 01:09:01 -0700 Subject: [PATCH 3111/4563] Fix review manager link in SE-0389 Ensure review manager name is a markdown link to their GitHub profile URL. This also ensures correct link behavior on the Swift Evolution dashboard. --- proposals/0389-attached-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0389-attached-macros.md b/proposals/0389-attached-macros.md index fd5991bb22..1061a929a6 100644 --- a/proposals/0389-attached-macros.md +++ b/proposals/0389-attached-macros.md @@ -2,7 +2,7 @@ * Proposal: [SE-0389](0389-attached-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Holly Borla](https://github.com/hborla), [Richard Wei](https://github.com/rxwei) -* Review Manager: Tony Allevato (https://github.com/allevato) +* Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Accepted** * Implementation: Implemented on GitHub `main` behind the experimental flag `Macros`. See the [example repository](https://github.com/DougGregor/swift-macro-examples) for more macros. * Review: ([pitch #1, under the name "declaration macros"](https://forums.swift.org/t/pitch-declaration-macros/62373)) ([pitch #2](https://forums.swift.org/t/pitch-attached-macros/62812)) ([review](https://forums.swift.org/t/se-0389-attached-macros/63165)) ([acceptance](https://forums.swift.org/t/accepted-se-0389-attached-macros/63593)) From d90bc1d1ea576a73a28f3ab3f7965382bc41592f Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 27 Apr 2023 07:51:39 -0700 Subject: [PATCH 3112/4563] Update proposals/nnnn-variadic-types.md Co-authored-by: Remy Demarest --- proposals/nnnn-variadic-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-variadic-types.md b/proposals/nnnn-variadic-types.md index 7cc4dfcbaf..8b0f264865 100644 --- a/proposals/nnnn-variadic-types.md +++ b/proposals/nnnn-variadic-types.md @@ -67,7 +67,7 @@ But this is not: struct S4 {} ``` -However, by virtue of nesting, a variadic type might can still abstract over multiple type parameter packs: +However, by virtue of nesting, a variadic type can still abstract over multiple type parameter packs: ```swift struct Outer { From 1cbf66e7163e918095d2cdb99befd38b3c67ca2b Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 27 Apr 2023 15:04:29 -0400 Subject: [PATCH 3113/4563] Assign SE-0397 to the freestanding declaration macros proposal and put it in review --- ...os.md => nnnn-freestanding-declaration-macros.md} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename proposals/{nnnn-freestanding-macros.md => nnnn-freestanding-declaration-macros.md} (90%) diff --git a/proposals/nnnn-freestanding-macros.md b/proposals/nnnn-freestanding-declaration-macros.md similarity index 90% rename from proposals/nnnn-freestanding-macros.md rename to proposals/nnnn-freestanding-declaration-macros.md index a5cfdd8a43..7f3238f10f 100644 --- a/proposals/nnnn-freestanding-macros.md +++ b/proposals/nnnn-freestanding-declaration-macros.md @@ -1,15 +1,15 @@ # Freestanding Declaration Macros -* Proposal: [SE-nnnn](nnnn-freestanding-macros.md) +* Proposal: [SE-0397](0397-freestanding-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Richard Wei](https://github.com/rxwei), [Holly Borla](https://github.com/hborla) -* Review Manager: Unassigned -* Status: **Pending review** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (April 27th...May 8th, 2023)** * Implementation: On `main` behind the experimental flag `FreestandingMacros` * Review: ## Introduction - [SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) introduced macros into Swift. The approach involves an explicit syntax for uses of macros (prefixed by `#`), type checking for macro arguments prior to macro expansion, and macro expansion implemented via separate programs that operate on the syntax tree of the arguments. +[SE-0382 "Expression macros"](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) introduced macros into Swift. The approach involves an explicit syntax for uses of macros (prefixed by `#`), type checking for macro arguments prior to macro expansion, and macro expansion implemented via separate programs that operate on the syntax tree of the arguments. This proposal generalizes the `#`-prefixed macro expansion syntax introduced for expression macros to also allow macros to generate declarations, enabling a number of other use cases, including: @@ -94,7 +94,7 @@ declaration -> macro-expansion-declaration macro-expansion-declaration -> '#' identifier generic-argument-clause[opt] function-call-argument-clause[opt] trailing-closures[opt] ``` -At top level and function scope where both expressions and declarations are allowed, a freestanding macro expansion site is first parsed as a macro expansion expression. It will be replaced by a macro expansion declaration later during type checking, if the macro resolves to a declaration macro. This is to allow the following infix expressions to be parsed correctly as an expression. +At top level and function scope where both expressions and declarations are allowed, a freestanding macro expansion site is first parsed as a macro expansion expression. It will be replaced by a macro expansion declaration later during type checking, if the macro resolves to a declaration macro. It is ill-formed if a macro expansion expression resolves to a declaration macro but isn't the outermost expression. This parsing rule is required in case an expression starts with a macro expansion expression, such as in the following infix expression: ```swift #line + 1 @@ -239,7 +239,7 @@ struct JSONValue: Codable { ## Source compatibility -Freestanding macros use the same syntax introduced for expression macros, which were themselves a pure extension without an impact on source compatibility. Because a given macro can only have a single freestanding role, and we retain the parsing rules for macro expansion expressions, this proposal introduces no new ambiguities with SE-0392 "Expression Macros". +Freestanding macros use the same syntax introduced for [expression macros](https://github.com/apple/swift-evolution/blob/main/proposals/0392-custom-actor-executors.md), which were themselves a pure extension without an impact on source compatibility. Because a given macro can only have a single freestanding role, and we retain the parsing rules for macro expansion expressions, this proposal introduces no new ambiguities with SE-0392. ## Effect on ABI stability From 56162cd0a65110f28ce6b7ebc2e27876232b5e15 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 27 Apr 2023 12:19:10 -0700 Subject: [PATCH 3114/4563] Drop the redundant "boilerplate generation" section It's subsumed by "Template code generation" --- .../nnnn-freestanding-declaration-macros.md | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/proposals/nnnn-freestanding-declaration-macros.md b/proposals/nnnn-freestanding-declaration-macros.md index 7f3238f10f..3389fde344 100644 --- a/proposals/nnnn-freestanding-declaration-macros.md +++ b/proposals/nnnn-freestanding-declaration-macros.md @@ -132,33 +132,6 @@ The `#warning` and `#error` directives introduced in [SE-0196](https://github.co @freestanding(declaration) macro error(_ message: String) ``` -### Boilerplate generation - -Freestanding declaration macros can be used to generate boilerplace code. For example, the Standard Library could use such a macro to generate integer types. - -```swift -@freestanding(declaration, names: arbitrary) -fileprivate macro IntegerTypes(bitWidths: Int...) - -#IntegerTypes(bitWidths: 8, 16, 32, 64) -``` - -This expands to: - -```swift -public struct Int8 { ... } -public struct UInt8 { ... } - -public struct Int16 { ... } -public struct UInt16 { ... } - -public struct Int32 { ... } -public struct UInt32 { ... } - -public struct Int64 { ... } -public struct UInt64 { ... } -``` - ### Template code generation The Swift Standard Library makes extensive use of the [gyb](https://github.com/apple/swift/blob/main/utils/gyb.py) tool to generate boilerplate-y Swift code such as [tgmath.swift.gyb](https://github.com/apple/swift/blob/main/stdlib/public/Platform/tgmath.swift.gyb). The template code is written in `.gyb` files, which are processed by the gyb tool separately before Swift compilation. With freestanding declaration macros, one could write a macro to accept a string as a template and a list of replacement values, allowing templates to be defined inline and eliminating the need to set up a separate build phase. From 7a61a9baafa626de80901795777a44dba1564e7f Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 27 Apr 2023 15:55:51 -0400 Subject: [PATCH 3115/4563] Rename SE-0397 file correctly and link to the vision document --- ...aration-macros.md => 0397-freestanding-declaration-macros.md} | 1 + 1 file changed, 1 insertion(+) rename proposals/{nnnn-freestanding-declaration-macros.md => 0397-freestanding-declaration-macros.md} (99%) diff --git a/proposals/nnnn-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md similarity index 99% rename from proposals/nnnn-freestanding-declaration-macros.md rename to proposals/0397-freestanding-declaration-macros.md index 3389fde344..8454d15e11 100644 --- a/proposals/nnnn-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -4,6 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Richard Wei](https://github.com/rxwei), [Holly Borla](https://github.com/hborla) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (April 27th...May 8th, 2023)** +* Vision: [Macros](https://github.com/apple/swift-evolution/blob/main/visions/macros.md) * Implementation: On `main` behind the experimental flag `FreestandingMacros` * Review: From b2d8ae3f13f20d3ae4c8cb5258fbae2bfd2cfd3a Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 27 Apr 2023 15:57:26 -0400 Subject: [PATCH 3116/4563] Link SE-0397 to its review thread --- proposals/0397-freestanding-declaration-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index 8454d15e11..8271f8b720 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -6,7 +6,7 @@ * Status: **Active review (April 27th...May 8th, 2023)** * Vision: [Macros](https://github.com/apple/swift-evolution/blob/main/visions/macros.md) * Implementation: On `main` behind the experimental flag `FreestandingMacros` -* Review: +* Review: ([review](https://forums.swift.org/t/se-0397-freestanding-declaration-macros/64655)) ## Introduction From 28674d672a1bb2eb70674dc8034de622911c3f1e Mon Sep 17 00:00:00 2001 From: Freddy Kellison-Linn Date: Thu, 27 Apr 2023 20:17:15 -0400 Subject: [PATCH 3117/4563] Update header fields, assign SE number --- .../{nnnn-variadic-types.md => 0398-variadic-types.md} | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) rename proposals/{nnnn-variadic-types.md => 0398-variadic-types.md} (95%) diff --git a/proposals/nnnn-variadic-types.md b/proposals/0398-variadic-types.md similarity index 95% rename from proposals/nnnn-variadic-types.md rename to proposals/0398-variadic-types.md index 8b0f264865..8b839d7d58 100644 --- a/proposals/nnnn-variadic-types.md +++ b/proposals/0398-variadic-types.md @@ -1,10 +1,12 @@ # Allow Generic Types to Abstract Over Packs -* Proposal: [SE-NNNN](nnnn-variadic-types.md) +* Proposal: [SE-0398](0398-variadic-types.md) * Authors: [Slava Pestov](https://github.com/slavapestov), [Holly Borla](https://github.com/hborla) -* Review Manager: TBD -* Upcoming Feature Flag: `VariadicGenerics` +* Review Manager: [Frederick Kellison-Linn](https://github.com/Jumhyn) +* Status: **Review scheduled (April 27th...May 8th, 2023)** +* Implementation: On main and release/5.9 gated behind the frontend flag -enable-experimental-feature VariadicGenerics * Previous Proposal: [SE-0393](0393-parameter-packs.md) +* Review: ([pitch](https://forums.swift.org/t/pitch-variadic-generic-types-abstracting-over-packs/64377)) ## Introduction @@ -236,4 +238,4 @@ S(t: 1, "hi", u: false) It was felt that in the end, the single-parameter model is the simplest. -We could require that variadic classes are declared `final`, instead of rejecting subclassing at the point of use. However, since adding or removing `final` on a class is an ABI break, this would preclude the possibility of publishing APIs which work with the existing compiler but can allow subclassing in the future. \ No newline at end of file +We could require that variadic classes are declared `final`, instead of rejecting subclassing at the point of use. However, since adding or removing `final` on a class is an ABI break, this would preclude the possibility of publishing APIs which work with the existing compiler but can allow subclassing in the future. From 69d93ba17898e2cad0412849a9a7f1331be19cd8 Mon Sep 17 00:00:00 2001 From: Freddy Kellison-Linn Date: Thu, 27 Apr 2023 20:18:13 -0400 Subject: [PATCH 3118/4563] Fix typo --- proposals/0398-variadic-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0398-variadic-types.md b/proposals/0398-variadic-types.md index 8b839d7d58..ce989a1821 100644 --- a/proposals/0398-variadic-types.md +++ b/proposals/0398-variadic-types.md @@ -166,7 +166,7 @@ The behavior of generic requirements on type parameter packs is mostly unchanged func demonstrate3a(_: repeat ImposeRepeatedSameType)) // Infers 'Int: P, repeat each V: P' - func demostrate3b(_: repeat (each U, ImposeRequirementExpansion)) + func demostrate3b(_: repeat (each U, ImposeRepeatedRequirement)) ``` ### Conformances From 3db77b699e3654e5e63d3f07147431979ff00157 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 27 Apr 2023 20:29:41 -0400 Subject: [PATCH 3119/4563] Update SE-0398 header for active review (#2031) --- proposals/0398-variadic-types.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0398-variadic-types.md b/proposals/0398-variadic-types.md index ce989a1821..e2e67df2ab 100644 --- a/proposals/0398-variadic-types.md +++ b/proposals/0398-variadic-types.md @@ -3,10 +3,10 @@ * Proposal: [SE-0398](0398-variadic-types.md) * Authors: [Slava Pestov](https://github.com/slavapestov), [Holly Borla](https://github.com/hborla) * Review Manager: [Frederick Kellison-Linn](https://github.com/Jumhyn) -* Status: **Review scheduled (April 27th...May 8th, 2023)** +* Status: **Active review (April 27th...May 8th, 2023)** * Implementation: On main and release/5.9 gated behind the frontend flag -enable-experimental-feature VariadicGenerics * Previous Proposal: [SE-0393](0393-parameter-packs.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-variadic-generic-types-abstracting-over-packs/64377)) +* Review: ([pitch](https://forums.swift.org/t/pitch-variadic-generic-types-abstracting-over-packs/64377)) ([review](https://forums.swift.org/t/se-0398-allow-generic-types-to-abstract-over-packs/64661)) ## Introduction From 08bf19c3315824b632c0e2ef7591b34ac74ce8fb Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 28 Apr 2023 17:35:18 +0100 Subject: [PATCH 3120/4563] Update 0387-cross-compilation-destinations.md (#2033) Update status of second review. --- proposals/0387-cross-compilation-destinations.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/0387-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md index 3b9bd32ad5..a395b6f4e0 100644 --- a/proposals/0387-cross-compilation-destinations.md +++ b/proposals/0387-cross-compilation-destinations.md @@ -1,15 +1,16 @@ # Cross-Compilation Destination Bundles - Proposal: [SE-0387](0387-cross-compilation-destinations.md) -- Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd/), [Evan Wilde](https://github.com/etcwilde) +- Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd), [Evan Wilde](https://github.com/etcwilde) - Review Manager: [Mishal Shah](https://github.com/shahmishal) -- Status: **Active Review (January 31st...February 14th, 2023**) +- Status: **Active Review (April 27...May 5, 2023)** - Implementation: [apple/swift-package-manager#5911](https://github.com/apple/swift-package-manager/pull/5911), [apple/swift-package-manager#5922](https://github.com/apple/swift-package-manager/pull/5922), [apple/swift-package-manager#6023](https://github.com/apple/swift-package-manager/pull/6023), [apple/swift-package-manager#6186](https://github.com/apple/swift-package-manager/pull/6186) - Review: ([pitch](https://forums.swift.org/t/pitch-cross-compilation-destination-bundles/61777)) - ([review](https://forums.swift.org/t/se-0387-cross-compilation-destination-bundles/62875)) + ([first review](https://forums.swift.org/t/se-0387-cross-compilation-destination-bundles/62875)) + ([second review](https://forums.swift.org/t/second-review-se-0387-cross-compilation-destination-bundles/64660)) ## Table of Contents From 10518107799f5f59e57ef3853c0be7bb800afb9c Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 28 Apr 2023 18:11:09 +0100 Subject: [PATCH 3121/4563] SE-0387: rename "destination" to "Swift SDK" --- .../0387-cross-compilation-destinations.md | 194 +++++++++--------- 1 file changed, 99 insertions(+), 95 deletions(-) diff --git a/proposals/0387-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md index a395b6f4e0..ea93b4f466 100644 --- a/proposals/0387-cross-compilation-destinations.md +++ b/proposals/0387-cross-compilation-destinations.md @@ -1,4 +1,4 @@ -# Cross-Compilation Destination Bundles +# Swift SDKs for Cross-Compilation - Proposal: [SE-0387](0387-cross-compilation-destinations.md) - Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd), [Evan Wilde](https://github.com/etcwilde) @@ -14,33 +14,35 @@ ## Table of Contents -- [Introduction](#introduction) -- [Motivation](#motivation) -- [Proposed Solution](#proposed-solution) -- [Detailed Design](#detailed-design) - - [CC Destination Artifact Bundles](#cc-destination-artifact-bundles) - - [`toolset.json` Files](#toolsetjson-files) - - [`destination.json` Files](#destinationjson-files) - - [Destination Bundle Installation and Configuration](#destination-bundle-installation-and-configuration) - - [Using a CC Destination](#using-a-cc-destination) - - [CC Destination Bundle Generation](#cc-destination-bundle-generation) -- [Security](#security) -- [Impact on Existing Packages](#impact-on-existing-packages) -- [Prior Art](#prior-art) - - [Rust](#rust) - - [Go](#go) -- [Alternatives Considered](#alternatives-considered) - - [Extensions Other Than `.artifactbundle`](#extensions-other-than-artifactbundle) - - [Building Applications in Docker Containers](#building-applications-in-docker-containers) - - [Alternative Bundle Formats](#alternative-bundle-formats) -- [Making Destination Bundles Fully Self-Contained](#making-destination-bundles-fully-self-contained) -- [Future Directions](#future-directions) - - [Identifying Platforms with Dictionaries of Properties](#identifying-platforms-with-dictionaries-of-properties) - - [SwiftPM Plugins for Remote Running, Testing, Deployment, and Debugging](#swiftpm-plugins-for-remote-running-testing-deployment-and-debugging) - - [`swift destination select` Subcommand](#swift-destination-select-subcommand) - - [SwiftPM and SourceKit-LSP Improvements](#swiftpm-and-sourcekit-lsp-improvements) - - [Source-Based CC Destinations](#source-based-cc-destinations) - - [Destination Bundles and Package Registries](#destination-bundles-and-package-registries) +- [Swift SDKs for Cross-Compilation](#swift-sdks-for-cross-compilation) + - [Table of Contents](#table-of-contents) + - [Introduction](#introduction) + - [Motivation](#motivation) + - [Proposed Solution](#proposed-solution) + - [Detailed Design](#detailed-design) + - [Swift SDK Bundles](#swift-sdk-bundles) + - [`toolset.json` Files](#toolsetjson-files) + - [`swift-sdk.json` Files](#swift-sdkjson-files) + - [Swift SDK Installation and Configuration](#swift-sdk-installation-and-configuration) + - [Using a CC Destination](#using-a-cc-destination) + - [CC Destination Bundle Generation](#cc-destination-bundle-generation) + - [Security](#security) + - [Impact on Existing Packages](#impact-on-existing-packages) + - [Prior Art](#prior-art) + - [Rust](#rust) + - [Go](#go) + - [Alternatives Considered](#alternatives-considered) + - [Extensions Other Than `.artifactbundle`](#extensions-other-than-artifactbundle) + - [Building Applications in Docker Containers](#building-applications-in-docker-containers) + - [Alternative Bundle Formats](#alternative-bundle-formats) + - [Making Destination Bundles Fully Self-Contained](#making-destination-bundles-fully-self-contained) + - [Future Directions](#future-directions) + - [Identifying Platforms with Dictionaries of Properties](#identifying-platforms-with-dictionaries-of-properties) + - [SwiftPM Plugins for Remote Running, Testing, Deployment, and Debugging](#swiftpm-plugins-for-remote-running-testing-deployment-and-debugging) + - [`swift destination select` Subcommand](#swift-destination-select-subcommand) + - [SwiftPM and SourceKit-LSP Improvements](#swiftpm-and-sourcekit-lsp-improvements) + - [Source-Based CC Destinations](#source-based-cc-destinations) + - [Destination Bundles and Package Registries](#destination-bundles-and-package-registries) ## Introduction @@ -54,7 +56,7 @@ Cross-compilation is a common development use case. When cross-compiling, we nee - **SDK** is a set of dynamic and/or static libraries, headers, and other resources required to generate code for the run-time triple. -Let’s call a toolchain and an SDK bundled together a **destination**. +Let’s call a Swift toolchain and an SDK bundled together a **Swift SDK**. Authors of the proposal are aware of the established "build/host/target platform" naming convention, but feel that "target" already has a different meaning within the build systems nomenclature. In addition, "platform" @@ -63,14 +65,16 @@ triple" terms in this proposal. ## Motivation -Swift cross-compilation (CC) destinations are currently produced on an ad-hoc basis for different combinations of -build-time and run-time triples. For example, scripts that produce macOS → Linux CC destinations were created by both +In Swift 5.8 and earlier versions users can cross-compile their code with so called "destination files" passed to +SwiftPM invocations. These destination files are produced on an ad-hoc basis for different combinations of +build-time and run-time triples. For example, scripts that produce macOS → Linux destinations were created by both [the Swift team](https://github.com/apple/swift-package-manager/blob/swift-5.7-RELEASE/Utilities/build_ubuntu_cross_compilation_toolchain) and [the Swift community](https://github.com/SPMDestinations/homebrew-tap). At the same time, the distribution process -of CC destinations is cumbersome. After building a destination tree on the file system, required metadata files rely on -hardcoded absolute paths. Adding support for relative paths in destination's metadata and providing a unified way to -distribute and install destinations as archives would clearly be an improvement to the multi-platform Swift ecosystem. +of assets required for cross-compiling is cumbersome. After building a destination tree on the file system, required +metadata files rely on hardcoded absolute paths. Adding support for relative paths in destination's metadata and +providing a unified way to distribute and install required assets as archives would clearly be an improvement for the +multi-platform Swift ecosystem. The primary audience of this pitch are people who cross-compile from macOS to Linux. When deploying to single-board computers supporting Linux (e.g. Raspberry Pi), building on such hardware may be too slow or run out of available @@ -84,19 +88,19 @@ The solution described below is general enough to scale for any build-time/run-t ## Proposed Solution -Since CC destination is a collection of binaries arranged in a certain directory hierarchy, it makes sense to distribute +Since a Swift SDK is a collection of binaries arranged in a certain directory hierarchy, it makes sense to distribute it as an archive. We'd like to build on top of [SE-0305](https://github.com/apple/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md) and extend the `.artifactbundle` format to support this. -Additionally, we propose introducing a new `swift destination` CLI command for installation and removal of CC -destinations on the local filesystem. +Additionally, we propose introducing a new `swift sdk` CLI command for installation and removal of Swift SDKs on the +local filesystem. -We introduce a notion of a top-level toolchain, which is the toolchain that handles user’s `swift destination` +We introduce a notion of a top-level toolchain, which is the toolchain that handles user’s `swift sdk` invocations. Parts of this top-level toolchain (linker, C/C++ compilers, and even the Swift compiler) can be overridden -with tools supplied in `.artifactbundle` s installed by `swift destination` invocations. +with tools supplied in `.artifactbundle` s installed by `swift sdk` invocations. -When the user runs `swift build` with the selected CC destination, the overriding tools from the corresponding bundle +When the user runs `swift build` with the selected Swift SDK, the overriding tools from the corresponding bundle are invoked by `swift build` instead of tools from the top-level toolchain. The proposal is intentionally limited in scope to build-time experience and specifies only configuration metadata, basic @@ -104,31 +108,31 @@ directory layout for proposed artifact bundles, and some CLI helpers to operate ## Detailed Design -### CC Destination Artifact Bundles +### Swift SDK Bundles As a quick reminder for a concept introduced in [SE-0305](https://github.com/apple/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md), an **artifact bundle** is a directory that has the filename suffix `.artifactbundle` and has a predefined structure with `.json` manifest files provided as metadata. -The proposed structure of artifact bundles containing CC destinations looks like: +The proposed structure of artifact bundles containing Swift SDKs looks like: ``` .artifactbundle ├ info.json -├ -│ ├ destination.json +├ +│ ├ swift-sdk.json │ ├ toolset.json -│ └ -├ -│ ├ destination.json +│ └ +├ +│ ├ swift-sdk.json │ ├ toolset.json -│ └ -├ +│ └ +├ ┆ └┄ ``` -For example, a destination bundle allowing to cross-compile Swift 5.8 source code to recent versions of Ubuntu from +For example, a Swift SDK bundle allowing to cross-compile Swift 5.8 source code to recent versions of Ubuntu from macOS would look like this: ``` @@ -136,29 +140,29 @@ swift-5.8_ubuntu.artifactbundle ├ info.json ├ toolset.json ├ ubuntu_jammy -│ ├ destination.json +│ ├ swift-sdk.json │ ├ toolset.json -│ ├ +│ ├ │ ├ aarch64-unknown-linux-gnu │ │ ├ toolset.json -│ │ └ +│ │ └ │ └ x86_64-unknown-linux-gnu │ ├ toolset.json -│ └ +│ └ ├ ubuntu_focal -│ ├ destination.json +│ ├ swift-sdk.json │ └ x86_64-unknown-linux-gnu │ ├ toolset.json -│ └ +│ └ ├ ubuntu_bionic ┆ └┄ ``` -Here each artifact directory is dedicated to a specific CC destination, while files specific to each triple are placed +Here each artifact directory is dedicated to a specific Swift SDK, while files specific to each triple are placed in `aarch64-unknown-linux-gnu` and `x86_64-unknown-linux-gnu` subdirectories. -`info.json` bundle manifests at the root of artifact bundles should specify `"type": "crossCompilationDestination"` for -corresponding artifacts. Artifact identifiers in this manifest file uniquely identify a CC destination, and +`info.json` bundle manifests at the root of artifact bundles should specify `"type": "swiftSDK"` for +corresponding artifacts. Artifact identifiers in this manifest file uniquely identify a Swift SDK, and `supportedTriples` property in `info.json` should contain build-time triples that a given destination supports. The rest of the properties of bundle manifests introduced in SE-0305 are preserved. @@ -169,7 +173,7 @@ above: { "artifacts" : { "swift-5.8_ubuntu22.04" : { - "type" : "crossCompilationDestination", + "type" : "swiftSDK", "version" : "0.0.1", "variants" : [ { @@ -182,7 +186,7 @@ above: ] }, "swift-5.8_ubuntu20.04" : { - "type" : "crossCompilationDestination", + "type" : "swiftSDK", "version" : "0.0.1", "variants" : [ { @@ -305,14 +309,14 @@ will be appended to the default tool invocation. For example `pedanticCCompiler. in `swift build --toolset pedanticCCompiler.json` will pass `-pedantic` to the C compiler located at a default path. -When cross-compiling, paths in `toolset.json` files supplied in destination artifact bundles should be self-contained: +When cross-compiling, paths in `toolset.json` files supplied in Swift SDK bundles should be self-contained: no absolute paths and no escaping symlinks are allowed. Users are still able to provide their own `toolset.json` files outside of artifact bundles to specify additional developer tools for which no relative "non-escaping" path can be provided within the bundle. -### `destination.json` Files +### `swift-sdk.json` Files -Note the presence of `destination.json` files in each `` subdirectory. These files should contain +Note the presence of `swift-sdk.json` files in each `` subdirectory. These files should contain a JSON dictionary with an evolved version of the schema of [existing `destination.json` files that SwiftPM already supports](https://github.com/apple/swift-package-manager/pull/1098) and `destination.json` files presented in the pitch version of this proposal, hence `"schemaVersion": "3.0"`. We'll keep parsing `"version": 1` and `"version": 2` for @@ -324,33 +328,33 @@ informally defined schema for these files: "schemaVersion": "3.0", "runTimeTriples": [ "": { - "sdkRootPath": "", + "sdkRootPath": "", // all of the properties listed below are optional: - "swiftResourcesPath": "", - "swiftStaticResourcesPath": "", - "includeSearchPaths": [""], - "librarySearchPaths": [""], - "toolsetPaths": [""] + "swiftResourcesPath": "", + "swiftStaticResourcesPath": "", + "includeSearchPaths": [""], + "librarySearchPaths": [""], + "toolsetPaths": [""] }, - // a destination can support more than one run-time triple: + // a Swift SDK can support more than one run-time triple: "": { - "sdkRootPath": "", + "sdkRootPath": "", // all of the properties listed below are optional: - "swiftResourcesPath": "", - "swiftStaticResourcesPath": "", - "includeSearchPaths": [""], - "librarySearchPaths": [""], - "toolsetPaths": [""] + "swiftResourcesPath": "", + "swiftStaticResourcesPath": "", + "includeSearchPaths": [""], + "librarySearchPaths": [""], + "toolsetPaths": [""] } - // more triples can be supported by a single destination if needed, primarily for sharing files between them. + // more triples can be supported by a single Swift SDK if needed, primarily for sharing files between them. ] } ``` -We propose that all relative paths in `destination.json` files should be validated not to "escape" the destination -bundle for security reasons, in the same way that `toolset.json` files are validated when contained in destination +We propose that all relative paths in `swift-sdk.json` files should be validated not to "escape" the Swift SDK +bundle for security reasons, in the same way that `toolset.json` files are validated when contained in Swift SDK bundles. That is, `../` components, if present in paths, will not be allowed to reference files and -directories outside of a corresponding destination bundle. Symlinks will also be validated to prevent them from escaping +directories outside of a corresponding Swift SDK bundle. Symlinks will also be validated to prevent them from escaping out of the bundle. If `sdkRootPath` is specified and `swiftResourcesPath` is not, the latter is inferred to be @@ -358,7 +362,7 @@ If `sdkRootPath` is specified and `swiftResourcesPath` is not, the latter is inf inferred to be `"\(sdkRootPath)/usr/lib/swift_static"` when linking it statically. Similarly, `includeSearchPaths` is inferred as `["\(sdkRootPath)/usr/include"]`, `librarySearchPaths` as `["\(sdkRootPath)/usr/lib"]`. -Here's `destination.json` file for the `ubuntu_jammy` artifact previously introduced as an example: +Here's `swift-sdk.json` file for the `ubuntu_jammy` artifact previously introduced as an example: ```json5 { @@ -376,24 +380,24 @@ Here's `destination.json` file for the `ubuntu_jammy` artifact previously introd } ``` -Since not all platforms can support self-contained destination bundles, users will be able to provide their own -additional paths on the filesystem outside of bundles after a destination is installed. The exact options for specifying -paths are proposed in a subsequent section for a newly introduced `swift destination configure` command. +Since not all platforms can support self-contained Swift SDK bundles, users will be able to provide their own +additional paths on the filesystem outside of bundles after a Swift SDK is installed. The exact options for specifying +paths are proposed in a subsequent section for a newly introduced `swift sdk configure` command. -### Destination Bundle Installation and Configuration +### Swift SDK Installation and Configuration -To manage CC destinations, we'd like to introduce a new `swift destination` command with three subcommands: +To manage Swift SDKs, we'd like to introduce a new `swift sdk` command with three subcommands: -- `swift destination install `, which downloads a given bundle if needed and - installs it in a location discoverable by SwiftPM. For destinations installed from remote URLs an additional - `--checksum` option is required, through which users of destinations can specify a checksum provided by publishers of - destinations. The latter can produce a checksum by running `swift package compute-checksum` command (introduced in +- `swift sdk install `, which downloads a given bundle if needed and + installs it in a location discoverable by SwiftPM. For Swift SDKs installed from remote URLs an additional + `--checksum` option is required, through which users of a Swift SDK can specify a checksum provided by a publisher of + the SDK. The latter can produce a checksum by running `swift package compute-checksum` command (introduced in [SE-0272](https://github.com/apple/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md)) with the - destination artifact bundle archive as an argument. + Swift SDK bundle archive as an argument. - If a destination with a given artifact ID has already been installed and its version is equal or higher to a version - of a new destination, an error message will be printed. If the new version is higher, users should invoke the - `install` subcommand with `--update` flag to allow updating an already installed destination artifact to a new + If a Swift SDK with a given artifact ID has already been installed and its version is equal or higher to a version + of a new Swift SDK, an error message will be printed. If the new version is higher, users should invoke the + `install` subcommand with `--update` flag to allow updating an already installed Swift SDK artifact to a new version. - `swift destination list`, which prints a list of already installed CC destinations with their identifiers. - `swift destination configure `, which allows users to provide additional search paths and toolsets to be From cefb6faec1c3258e64a7eefa25e3c3a16c636bbf Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 28 Apr 2023 15:43:16 -0400 Subject: [PATCH 3122/4563] Minor editorial updates to variadic generic types (#2032) --- proposals/0398-variadic-types.md | 42 ++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/proposals/0398-variadic-types.md b/proposals/0398-variadic-types.md index e2e67df2ab..b77490aa29 100644 --- a/proposals/0398-variadic-types.md +++ b/proposals/0398-variadic-types.md @@ -14,7 +14,18 @@ Previously [SE-0393](0393-parameter-packs.md) introduced type parameter packs an ## Motivation -Generic type declarations that abstract over a variable number of types arise naturally when attempting to generalize common algorithms on collections. For example, a lazy `ZipSequence` might be generic over two sequences. It would be possible to declare a `ZipSequence` type which presents the elements of a fixed list of sequences as a sequence of tuples: +Generic type declarations that abstract over a variable number of types arise naturally when attempting to generalize common algorithms on collections. For example, the current `zip` function returns a `Zip2Sequence`, but it's not possible from SE-0393 alone to define an equivalent variadic `zip` function because the return type would need an arbitrary number of type parameters—one for each input sequence: + +```swift +func zip(_ seq: repeat each S) -> ??? + where repeat each S: Sequence +``` + +## Proposed solution + +In the generic parameter list of a generic type, the `each` keyword declares a generic parameter pack, just like it does in the generic parameter list of a generic function. The types of stored properties can contain pack expansion types, as in `let seq` and `var iter` above. + +This lets us define the return type of the variadic `zip` function as follows: ```swift struct ZipSequence: Sequence { @@ -36,11 +47,10 @@ struct ZipSequence: Sequence { } } } -``` - -## Proposed solution -In the generic parameter list of a generic type, the `each` keyword declares a generic parameter pack, just like it does in the generic parameter list of a generic function. The types of stored properties can contain pack expansion types, as in `let seq` and `var iter` above. +func zip(_ seq: repeat each S) -> ZipSequence + where repeat each S: Sequence +``` ## Detailed design @@ -104,7 +114,7 @@ struct V {} V<>.self ``` -Note that `V<>` is not the same as `V`. The former substitutes the generic parameter pack `T` with the empty pack. The latter does not constrain the pack at all and is only permitted in contexts where the generic argument can be inferred. +Note that `V<>` is not the same as `V`. The former substitutes the generic parameter pack `T` with the empty pack. The latter does not constrain the pack at all and is only permitted in contexts where the generic argument can be inferred (or within the body of `V` or an extension thereof, where it is considered identical to `Self`). A placeholder type in the generic argument list of a variadic generic type is always understood as a single pack element. For example: @@ -127,13 +137,7 @@ struct S { var c: Other } ``` -This is in contrast with the parameters of generic function declarations, which can have a pack expansion type. A future proposal might lift this restriction and introduce true "stored property packs": - -```swift -struct S { - var a: repeat each Array -} -``` +This is in contrast with the parameters of generic function declarations, which can have a pack expansion type. A [future proposal](#future-directions) might lift this restriction and introduce true "stored property packs." ### Requirements @@ -185,7 +189,7 @@ typealias Callback = (repeat each S) -> () typealias Factory = Other ``` -Unlike structs and classes, but like other type aliases, variadic type aliases can also be nested inside of generic functions. +Like other type aliases, variadic type aliases can be nested inside of generic functions (and like other structs and classes, variadic structs and classes cannot). ### Classes @@ -220,7 +224,15 @@ Replacing a non-variadic generic type with a variadic generic type is **not** bi A future proposal will address variadic generic enums, and complete support for variadic generic classes. -Another possible future direction is stored property packs, which would eliminate the need to wrap a pack expansion in a tuple type in order to store a variable number of values inside of a variadic type. However, there is no expressivity lost in requiring the tuple today, since the contents of a tuple can be converted into a value pack. +Another possible future direction is stored property packs, which would eliminate the need to wrap a pack expansion in a tuple type in order to store a variable number of values inside of a variadic type: + +```swift +struct S { + var a: repeat each Array +} +``` + +However, there is no expressivity lost in requiring the tuple today, since the contents of a tuple can be converted into a value pack. ## Alternatives considered From 09605473c506ab161695cc194a26868a36809396 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sat, 29 Apr 2023 15:45:28 +0100 Subject: [PATCH 3123/4563] SE-0387: replace all uses of "destination" with "Swift SDK" --- .../0387-cross-compilation-destinations.md | 182 +++++++++--------- 1 file changed, 90 insertions(+), 92 deletions(-) diff --git a/proposals/0387-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md index ea93b4f466..6332cc479e 100644 --- a/proposals/0387-cross-compilation-destinations.md +++ b/proposals/0387-cross-compilation-destinations.md @@ -14,35 +14,33 @@ ## Table of Contents -- [Swift SDKs for Cross-Compilation](#swift-sdks-for-cross-compilation) - - [Table of Contents](#table-of-contents) - - [Introduction](#introduction) - - [Motivation](#motivation) - - [Proposed Solution](#proposed-solution) - - [Detailed Design](#detailed-design) - - [Swift SDK Bundles](#swift-sdk-bundles) - - [`toolset.json` Files](#toolsetjson-files) - - [`swift-sdk.json` Files](#swift-sdkjson-files) - - [Swift SDK Installation and Configuration](#swift-sdk-installation-and-configuration) - - [Using a CC Destination](#using-a-cc-destination) - - [CC Destination Bundle Generation](#cc-destination-bundle-generation) - - [Security](#security) - - [Impact on Existing Packages](#impact-on-existing-packages) - - [Prior Art](#prior-art) - - [Rust](#rust) - - [Go](#go) - - [Alternatives Considered](#alternatives-considered) - - [Extensions Other Than `.artifactbundle`](#extensions-other-than-artifactbundle) - - [Building Applications in Docker Containers](#building-applications-in-docker-containers) - - [Alternative Bundle Formats](#alternative-bundle-formats) - - [Making Destination Bundles Fully Self-Contained](#making-destination-bundles-fully-self-contained) - - [Future Directions](#future-directions) - - [Identifying Platforms with Dictionaries of Properties](#identifying-platforms-with-dictionaries-of-properties) - - [SwiftPM Plugins for Remote Running, Testing, Deployment, and Debugging](#swiftpm-plugins-for-remote-running-testing-deployment-and-debugging) - - [`swift destination select` Subcommand](#swift-destination-select-subcommand) - - [SwiftPM and SourceKit-LSP Improvements](#swiftpm-and-sourcekit-lsp-improvements) - - [Source-Based CC Destinations](#source-based-cc-destinations) - - [Destination Bundles and Package Registries](#destination-bundles-and-package-registries) +- [Introduction](#introduction) +- [Motivation](#motivation) +- [Proposed Solution](#proposed-solution) +- [Detailed Design](#detailed-design) + - [Swift SDK Bundles](#swift-sdk-bundles) + - [`toolset.json` Files](#toolsetjson-files) + - [`swift-sdk.json` Files](#swift-sdkjson-files) + - [Swift SDK Installation and Configuration](#swift-sdk-installation-and-configuration) + - [Using a Swift SDK](#using-a-swift-sdk) + - [Swift SDK Bundle Generation](#swift-sdk-bundle-generation) +- [Security](#security) +- [Impact on Existing Packages](#impact-on-existing-packages) +- [Prior Art](#prior-art) + - [Rust](#rust) + - [Go](#go) +- [Alternatives Considered](#alternatives-considered) + - [Extensions Other Than `.artifactbundle`](#extensions-other-than-artifactbundle) + - [Building Applications in Docker Containers](#building-applications-in-docker-containers) + - [Alternative Bundle Formats](#alternative-bundle-formats) +- [Making Swift SDK Bundles Fully Self-Contained](#making-swift-sdk-bundles-fully-self-contained) +- [Future Directions](#future-directions) + - [Identifying Platforms with Dictionaries of Properties](#identifying-platforms-with-dictionaries-of-properties) + - [SwiftPM Plugins for Remote Running, Testing, Deployment, and Debugging](#swiftpm-plugins-for-remote-running-testing-deployment-and-debugging) + - [`swift sdk select` Subcommand](#swift-sdk-select-subcommand) + - [SwiftPM and SourceKit-LSP Improvements](#swiftpm-and-sourcekit-lsp-improvements) + - [Source-Based Swift SDKs](#source-based-swift-sdks) + - [Swift SDK Bundles and Package Registries](#swift-sdk-bundles-and-package-registries) ## Introduction @@ -163,7 +161,7 @@ in `aarch64-unknown-linux-gnu` and `x86_64-unknown-linux-gnu` subdirectories. `info.json` bundle manifests at the root of artifact bundles should specify `"type": "swiftSDK"` for corresponding artifacts. Artifact identifiers in this manifest file uniquely identify a Swift SDK, and -`supportedTriples` property in `info.json` should contain build-time triples that a given destination supports. The rest +`supportedTriples` property in `info.json` should contain build-time triples that a given Swift SDK supports. The rest of the properties of bundle manifests introduced in SE-0305 are preserved. Here's how `info.json` file could look like for `swift-5.8_ubuntu.artifactbundle` introduced in the example @@ -399,46 +397,46 @@ To manage Swift SDKs, we'd like to introduce a new `swift sdk` command with thre of a new Swift SDK, an error message will be printed. If the new version is higher, users should invoke the `install` subcommand with `--update` flag to allow updating an already installed Swift SDK artifact to a new version. -- `swift destination list`, which prints a list of already installed CC destinations with their identifiers. -- `swift destination configure `, which allows users to provide additional search paths and toolsets to be -used subsequently when building with a given destination. Specifically, multiple `--swift-resources-path`, +- `swift sdk list`, which prints a list of already installed Swift SDKs with their identifiers. +- `swift sdk configure `, which allows users to provide additional search paths and toolsets to be +used subsequently when building with a given Swift SDK. Specifically, multiple `--swift-resources-path`, `--include-search-path`, `--library-search-path`, and `--toolset` options with corresponding paths can be provided, -which then will be stored as configuration for this destination. -`swift destination configure --show-configuration` will print currently set paths, while -`swift destination configure --reset` will reset all of those at once. -- `swift destination remove ` will remove a given destination from the filesystem. +which then will be stored as configuration for this Swift SDK. +`swift sdk configure --show-configuration` will print currently set paths, while +`swift sdk configure --reset` will reset all of those at once. +- `swift sdk remove ` will remove a given Swift SDK from the filesystem. -### Using a CC Destination +### Using a Swift SDK -After a destination is installed, users can refer to it via its identifier passed to the `--destination` option, e.g. +After a Swift SDK is installed, users can refer to it via its identifier passed to the `--swift-sdk` option, e.g. ``` -swift build --destination ubuntu_focal +swift build --swift-sdk ubuntu_focal ``` -We'd also like to make `--destination` flexible enough to recognize run-time triples when there's only a single CC -destination installed for such triple: +We'd also like to make `--swift-sdk` option flexible enough to recognize run-time triples when there's only a single +Swift SDK installed for such triple: ``` -swift build --destination x86_64-unknown-linux-gnu +swift build --swift-sdk x86_64-unknown-linux-gnu ``` -When multiple destinations support the same triple, an error message will be printed listing these destinations and +When multiple Swift SDKs support the same triple, an error message will be printed listing these Swift SDKs and asking the user to select a single one via its identifier instead. -### CC Destination Bundle Generation +### Swift SDK Bundle Generation -CC destinations can be generated quite differently, depending on build-time and run-time triple combinations and user's -needs. We intentionally don't specify how destination artifact bundles should be generated. +Swift SDKs can be generated quite differently, depending on build-time and run-time triple combinations and user's +needs. We intentionally don't specify in this proposal how exactly Swift SDK bundles should be generated. -Authors of this document intend to publish source code for a macOS → Linux CC destination generator, which community is -welcome to fork and reuse for their specific needs. This generator will use Docker for setting up the build environment -locally before copying it to the destination tree. Relying on Docker in this generator makes it easier to reuse and -customize existing build environments. Important to clarify, that Docker is only used for bundle generation, and users -of CC destinations do not need to have Docker installed on their machine to utilize it. +Authors of this document intend to publish source code for a macOS → Linux Swift SDK generator, which community is +welcome to fork and reuse for their specific needs. As a configurable option, this generator will use Docker for setting +up the build environment locally before copying it to a Swift SDK tree. Relying on Docker in this generator makes it +easier to reuse and customize existing build environments. Important to clarify, that Docker is only used for bundle +generation, and users of Swift SDK bundles do not need to have Docker installed on their machine to use these bundles. -As an example, destination publishers looking to add a library to an Ubuntu 22.04 destination environment would modify a -`Dockerfile` similar to this one in CC destination generator source code: +As an example, Swift SDK publishers looking to add a library to an Ubuntu 22.04 run-time environment would modify a +`Dockerfile` similar to this one in their Swift SDK generator source code: ```dockerfile FROM swift:5.8-jammy @@ -449,14 +447,14 @@ apt-get install -y \ # Add more libraries as arguments to `apt-get install`. ``` -Then to generate a new CC destinations, a generator executable delegates to Docker for downloading and installing -required tools and libraries, including the newly added ones. After a Docker image with destination environment is -ready, the generator copies files from the image to a corresponding `.artifactbundle` destination tree. +Then to generate a new Swift SDK, a generator executable delegates to Docker for downloading and installing +required tools and libraries, including the newly added ones. After a Docker image with Swift SDK environment is +ready, the generator copies files from the image to a corresponding `.artifactbundle` Swift SDK tree. ## Security -The proposed `--checksum` flag provides basic means of verifying destination bundle's validity. As a future direction, -we'd like to consider sandboxing and codesigning toolchains running on macOS. +The proposed `--checksum` flag provides basic means of verifying Swift SDK bundle's validity. As a future direction, +we'd like to consider sandboxing and codesigning toolchains included in Swift SDKs running on macOS. ## Impact on Existing Packages @@ -472,14 +470,14 @@ tool](https://github.com/rust-lang/rustup). For example, artifacts required for [`rustup target add aarch64-linux-unknown-gnu`](https://rust-lang.github.io/rustup/cross-compilation.html). Then building for this target with Rust’s package manager looks like `cargo build --target=aarch64-linux-unknown-gnu` . -Mainstream Rust tools don’t provide an easy way to create your own destinations/targets. You’re only limited to the list +Mainstream Rust tools don’t provide an easy way to create your own targets. You’re only limited to the list of targets provided by Rust maintainers. This likely isn’t a big problem per se for Rust users, as Rust doesn’t provide C/C++ interop on the same level as Swift. It means that Rust packages much more rarely than Swift expect certain system-provided packages to be available in the same way that SwiftPM allows with `systemLibrary`. Currently, Rust doesn’t supply all of the required tools when running `rustup target add`. It’s left to a user to specify paths to a linker that’s suitable for their build-time/run-time triple combination manually in a config file. We -feel that this should be unnecessary, which is why destination bundles proposed for Swift can provide their own tools +feel that this should be unnecessary, which is why Swift SDK bundles proposed for Swift can provide their own tools via toolset configuration files. ### Go @@ -491,23 +489,23 @@ environment variables with chosen values, an example of this is `GOARCH=arm64 GO This would be a great experience for Swift, but it isn’t easily achievable as long as Swift standard library depends on C and C++ standard libraries. Any code interoperating with C and/or C++ would have to link with those libraries as well. When compared to Go, our proposed solution allows both dynamic and, at least on Linux when Musl is supported, full -static linking. We’d like Swift to allow as much customization as needed for users to prepare their own destination +static linking. We’d like Swift to allow as much customization as needed for users to prepare their own Swift SDK bundles. ## Alternatives Considered ### Extensions Other Than `.artifactbundle` -Some members of the community suggested that destination bundles should use a more specific extension. Since we're -relying on the existing `.artifactbundle` format and extension, which is already used for binary targets, we think a -specialized extension only for destinations would introduce an inconsistency. On the other hand, we think that specific -extensions could make sense with a change applied at once. For example, we could consider `.binarytarget` and -`.ccdestination` extensions for respective artifact types. But that would require a migration strategy for existing +Some members of the community suggested that Swift SDK bundles should use a more specific filepath extension. Since +we're relying on the existing `.artifactbundle` format and extension, which is already used for binary targets, we think +a specialized extension only for Swift SDKs would introduce an inconsistency. On the other hand, we think that +specific extensions could make sense with a change applied at once. For example, we could consider `.binarytarget` and +`.swiftsdk` extensions for respective artifact types. But that would require a migration strategy for existing `.artifactbundle`s containing binary targets. ### Building Applications in Docker Containers -Instead of coming up with a specialized bundle format for destinations, users of Swift on macOS building for Linux could +Instead of coming up with a specialized bundle format for Swift SDKs, users of Swift on macOS building for Linux could continue to use Docker. But, as discussed in the [Motivation](#motivation) section, building applications in Docker doesn’t cover all of the possible use cases and complicates onboarding for new users. It also only supports Linux, while we’re looking for a solution that can be generalized for all possible platforms. @@ -515,21 +513,21 @@ we’re looking for a solution that can be generalized for all possible platform ### Alternative Bundle Formats One alternative is to allow only a single build-time/run-time combination per bundle, but this may complicate -distribution of destinations bundles in some scenarios. The existing `.artifactbundle` format is flexible enough to +distribution of Swift SDK bundles in some scenarios. The existing `.artifactbundle` format is flexible enough to support bundles with a single or multiple combinations. -Different formats of destination bundles can be considered, but we don't think those would be significantly different +Different formats of Swift SDK bundles can be considered, but we don't think those would be significantly different from the proposed one. If they were different, this would complicate bundle distribution scenarios for users who want to publish their own artifact bundles with executables, as defined in SE-0305. -## Making Destination Bundles Fully Self-Contained +## Making Swift SDK Bundles Fully Self-Contained -Some users expressed interest in self-contained destination bundles that ignore the value of `PATH` environment variable +Some users expressed interest in self-contained Swift SDK bundles that ignore the value of `PATH` environment variable and prevent launching any executables from outside of a bundle. So far in our practice we haven't seen any problems -caused by the use of executables from `PATH`. Quite the opposite, we think most destinations would want to reuse as many -tools from `PATH` as possible, which would allow making destination bundles much smaller. For example as of Swift 5.7, +caused by the use of executables from `PATH`. Quite the opposite, we think most Swift SDKs would want to reuse as many +tools from `PATH` as possible, which would allow making Swift SDK bundles much smaller. For example as of Swift 5.7, on macOS `clang-13` binary takes ~360 MB, `clangd` ~150 MB, and `swift-frontend` ~420 MB. Keeping copies of these -binaries in every destination bundle seems quite redundant when existing binaries from `PATH` can be easily reused. +binaries in every Swift SDK bundle seems quite redundant when existing binaries from `PATH` can be easily reused. Additionally, we find that preventing tools from being launched from arbitrary paths can't be technically enforced without sandboxing, and there's no cross-platform sandboxing solution available for SwiftPM. Until such sandboxing solution is available, we'd like to keep the existing approach where setting `PATH` environment variable behaves in a @@ -540,12 +538,12 @@ predictable way and is consistent with established CLI conventions. ### Identifying Platforms with Dictionaries of Properties Platform triples are not specific enough in certain cases. For example, `aarch64-unknown-linux` host triple can’t -prevent a user from installing a CC destination bundle on an unsupported Linux distribution. In the future we could +prevent a user from installing a Swift SDK bundle on an unsupported Linux distribution. In the future we could deprecate `supportedTriples` and `runTimeTriples` JSON properties in favor of dictionaries with keys and values that -describe aspects of platforms that are important for destinations. Such dictionaries could look like this: +describe aspects of platforms that are important for Swift SDKs. Such dictionaries could look like this: ```json5 -"destination": { +"platform": { "kernel": "Linux", "libcFlavor": "Glibc", "libcMinVersion": "2.36", @@ -559,15 +557,15 @@ compilation and potentially even runtime checks. ### SwiftPM Plugins for Remote Running, Testing, Deployment, and Debugging -After an application is built with a CC destination, there are other development workflow steps to be improved. We could +After an application is built with a Swift SDK, there are other development workflow steps to be improved. We could introduce new types of plugins invoked by `swift run` and `swift test` for purposes of remote running, debugging, and testing. For Linux run-time triples, these plugins could delegate to Docker for running produced executables. -### `swift destination select` Subcommand +### `swift sdk select` Subcommand -While `swift destination select` subcommand or a similar one make sense for selecting a CC destination instead of -passing `--destination` to `swift build` every time, users will expect `swift run` and `swift test` to also work for any -destination previously passed to `swift destination select`. That’s out of scope for this proposal on its own and +While `swift sdk select` subcommand or a similar one make sense for selecting a Swift SDK instead of +passing `--swift-sdk` to `swift build` every time, users will expect `swift run` and `swift test` to also work for any +Swift SDK previously passed to `swift sdk select`. That’s out of scope for this proposal on its own and depends on making plugins (from the previous subsection) or some other remote running and testing implementation to fully work. @@ -584,16 +582,16 @@ platforms at the same time. Users should be able to select run-time triples and semantic syntax highlighting, auto-complete, and other features for areas of code that are conditionally compiled with `#if` directives. -### Source-Based CC Destinations +### Source-Based Swift SDKs -One interesting solution is distributing source code of a minimal base destination, as explored by [Zig programming -language](https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html). In this scenario, a -cross-compilation destination binaries are produced on the fly when needed. We don't consider this option to be mutually -exclusive with solutions proposed in this document, and so it could be explored in the future for Swift as well. -However, this requires reducing the number of dependencies that Swift runtime and core libraries have. +One interesting solution is distributing source code of a minimal base SDK, as explored by [Zig programming +language](https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html). In this scenario, Swift SDK +binaries are produced on the fly when needed. We don't consider this option to be mutually exclusive with solutions +proposed in this document, and so it could be explored in the future for Swift as well. However, this requires reducing +the number of dependencies that Swift runtime and core libraries have. -### Destination Bundles and Package Registries +### Swift SDK Bundles and Package Registries -Since `info.json` manifest files contained within bundles contain versions, it would make sense to host destination +Since `info.json` manifest files contained within bundles contain versions, it would make sense to host Swift SDK bundles at package registries. Although, it remains to be seen whether it makes sense for an arbitrary SwiftPM package -to specify a destination bundle within its list of dependencies. +to specify a Swift SDK bundle within its list of dependencies. From e320c98170d3e85b7741a71267f624a82397bc2e Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sat, 29 Apr 2023 16:03:59 +0100 Subject: [PATCH 3124/4563] SE-0387: update mentions of Swift 5.8 to Swift 5.9 --- proposals/0387-cross-compilation-destinations.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/0387-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md index 6332cc479e..e5601adacf 100644 --- a/proposals/0387-cross-compilation-destinations.md +++ b/proposals/0387-cross-compilation-destinations.md @@ -130,11 +130,11 @@ The proposed structure of artifact bundles containing Swift SDKs looks like: ┆ └┄ ``` -For example, a Swift SDK bundle allowing to cross-compile Swift 5.8 source code to recent versions of Ubuntu from +For example, a Swift SDK bundle allowing to cross-compile Swift 5.9 source code to recent versions of Ubuntu from macOS would look like this: ``` -swift-5.8_ubuntu.artifactbundle +swift-5.9_ubuntu.artifactbundle ├ info.json ├ toolset.json ├ ubuntu_jammy @@ -164,13 +164,13 @@ corresponding artifacts. Artifact identifiers in this manifest file uniquely ide `supportedTriples` property in `info.json` should contain build-time triples that a given Swift SDK supports. The rest of the properties of bundle manifests introduced in SE-0305 are preserved. -Here's how `info.json` file could look like for `swift-5.8_ubuntu.artifactbundle` introduced in the example +Here's how `info.json` file could look like for `swift-5.9_ubuntu.artifactbundle` introduced in the example above: ```json5 { "artifacts" : { - "swift-5.8_ubuntu22.04" : { + "swift-5.9_ubuntu22.04" : { "type" : "swiftSDK", "version" : "0.0.1", "variants" : [ @@ -183,7 +183,7 @@ above: } ] }, - "swift-5.8_ubuntu20.04" : { + "swift-5.9_ubuntu20.04" : { "type" : "swiftSDK", "version" : "0.0.1", "variants" : [ @@ -439,7 +439,7 @@ As an example, Swift SDK publishers looking to add a library to an Ubuntu 22.04 `Dockerfile` similar to this one in their Swift SDK generator source code: ```dockerfile -FROM swift:5.8-jammy +FROM swift:5.9-jammy apt-get install -y \ # PostgreSQL library provided as an example. From c0f1e6729b6ca1a4fc2367efe68612fde175afe4 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 2 May 2023 16:03:09 -0700 Subject: [PATCH 3125/4563] Fix SE-0397 proposal link. (#2037) --- proposals/0397-freestanding-declaration-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index 8271f8b720..87618abb7c 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -1,6 +1,6 @@ # Freestanding Declaration Macros -* Proposal: [SE-0397](0397-freestanding-macros.md) +* Proposal: [SE-0397](0397-freestanding-declaration-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Richard Wei](https://github.com/rxwei), [Holly Borla](https://github.com/hborla) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (April 27th...May 8th, 2023)** From 45e305473ac94d6e32ee78b42b8c8e34fd498464 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Fri, 5 May 2023 12:33:30 -0700 Subject: [PATCH 3126/4563] SE-0392 is accepted --- proposals/0392-custom-actor-executors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index a39efa0f88..14360ab85c 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -3,7 +3,7 @@ - Proposal: [SE-0392](0392-custom-actor-executors.md) - Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [John McCall](https://github.com/rjmccall), [Kavon Farvardin](https://github.com/kavon) - Review Manager: [Joe Groff](https://github.com/jckarter) -- Status: **Active review (April 7 ... April 17, 2023)** +- Status: **Accepted** - Implementation: Partially implemented on `main` - Previous threads: - Original pitch thread from around Swift 5.5: [Support custom executors in Swift Concurrency](https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425) From 5d8b488ea833428290fe2f690895c9f73bb77380 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Fri, 5 May 2023 17:13:16 -0700 Subject: [PATCH 3127/4563] Accept SE-0396. --- proposals/0396-never-codable.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0396-never-codable.md b/proposals/0396-never-codable.md index d9ef096546..176358b0fa 100644 --- a/proposals/0396-never-codable.md +++ b/proposals/0396-never-codable.md @@ -3,9 +3,9 @@ * Proposal: [SE-0396](0396-never-codable.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Active Review (April 18...May 3, 2023)** +* Status: **Accepted** * Implementation: [apple/swift#64899](https://github.com/apple/swift/pull/64899) -* Review: ([pitch](https://forums.swift.org/t/pitch-conform-never-to-codable/64056)) ([review](https://forums.swift.org/t/se-0396-conform-never-to-codable/64469)) +* Review: ([pitch](https://forums.swift.org/t/pitch-conform-never-to-codable/64056)) ([review](https://forums.swift.org/t/se-0396-conform-never-to-codable/64469)) ([acceptance](https://forums.swift.org/t/accepted-se-0396-conform-never-to-codable/64848)) ## Introduction From 23c770dd0dd8863169b0d8b9c2ecda24c5770939 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 8 May 2023 15:15:17 -0700 Subject: [PATCH 3128/4563] New proposal: Make `borrowing` and `consuming` parameters require explicit copying with the `copy` operator --- ...CD-borrowing-consuming-no-implicit-copy.md | 253 ++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 proposals/ABCD-borrowing-consuming-no-implicit-copy.md diff --git a/proposals/ABCD-borrowing-consuming-no-implicit-copy.md b/proposals/ABCD-borrowing-consuming-no-implicit-copy.md new file mode 100644 index 0000000000..a5464d8ad9 --- /dev/null +++ b/proposals/ABCD-borrowing-consuming-no-implicit-copy.md @@ -0,0 +1,253 @@ +# Make `borrowing` and `consuming` parameters require explicit copying with the `copy` operator + +* Proposal: [SE-ABCD](ABCD-borrowing-consuming-no-implicit-copy.md) +* Authors: [Joe Groff](https://github.com/jckarter), [Andrew Trick](https://github.com/atrick/), [Michael Gottesman](https://github.com/gottesmm), [Kavon Favardin](https://github.com/kavon) +* Review Manager: TBD +* Status: **Awaiting implementation** +* Previous Proposal: [SE-0377](0377-parameter-ownership-modifiers.md) +* Review: ([pitch](https://forums.swift.org/t/pitch-making-borrowing-and-consuming-parameters-require-manual-copying-with-a-copy-operator/64843/11)) + +## Introduction + +This proposal changes the `borrowing` and `consuming` parameter modifiers to +make it so that the parameter binding is **not implicitly copyable**. Similarly, +the `borrowing` and `consuming` method modifiers make the `self` binding +within the method not implicitly copyable. We also introduce a new operator, +`copy x`, as a way to explicitly allow a copy to occur. + +## Motivation + +The `borrowing` and `consuming` modifiers were introduced by +[SE-0377](0377-parameter-ownership-modifiers.md) as a way to allow for +developers to optimize the calling convention of their functions while +working with copyable types, and also to specify the ownership convention +for working with noncopyable types introduced by +[SE-0390](0390-noncopyable-structs-and-enums.md). Even when working with +values of copyable type, the use of these ownership modifiers is a strong +signal that the developer is optimizing the copying behavior of the function, +but with Swift's normal implicit copying behavior, it is difficult to validate +the effect, if any, of manipulating the ownership convention on the efficiency +of the code. + +Furthermore, as we develop the ownership model, we also plan to +introduce [borrow bindings](https://forums.swift.org/t/pitch-borrow-and-inout-declaration-keywords/62366) +as a way to bind local variables to the internal storage of data structures +without copying. We also believe these bindings should not be implicitly +copying, since it would likely subvert developers' expectations to bind +a variable to a borrow of a value, and then have the value of that binding +get implicitly copied. Making it so that `borrowing` parameters are also +not implicitly copyable provides a consistent model between the parameter +modifiers and corresponding binding kinds. + +## Proposed solution + +We propose to make it so that parameters modified by the `borrowing` or +`consuming` ownership modifiers, as well as the `self` parameter to methods +modified by the `borrowing` or `consuming` method modifiers, is not implicitly +copyable. In cases where copying is acceptable, the new `copy x` operator allows +a copy to occur. + +## Detailed design + +Bindings to parameters modified by the `borrowing` or `consuming` modifier +become not implicitly copyable: + +``` +func foo(x: borrowing String) -> (String, String) { + return (x, x) // ERROR: needs to copy `x` +} +func bar(x: consuming String) -> (String, String) { + return (x, x) // ERROR: needs to copy `x` +} +``` + +So does the `self` parameter to methods with the method-level `borrowing` +or `consuming` modifier: + +``` +extension String { + borrowing func foo() -> (String, String) { + return (self, self) // ERROR: needs to copy `self` + } + consuming func bar() -> (String, String) { + return (self, self) // ERROR: needs to copy `x` + } +} +``` + +A value would need to be implicitly copied if: + +- a *consuming operation* is applied to a `borrowing` binding, or +- a *consuming operation* is applied to a `consuming` binding after it has + already been consumed, or while a *borrowing* or *mutating operation* is simultaneously + being performed on the same binding + +where *consuming*, *borrowing*, and *mutating operations* are as described for +values of noncopyable type in +[SE-0390](https://github.com/apple/swift-evolution/blob/main/proposals/0390-noncopyable-structs-and-enums.md#using-noncopyable-values). +In essence, disabling implicit copying for a value makes it behave as if it +were a value of noncopyable type. + +To allow a copy to occur, the `copy x` operator may be used: + +``` +func dup(_ x: borrowing String) -> (String, String) { + return (copy x, copy x) // OK, copies explicitly allowed here +} +``` + +`copy x` is a *borrowing operation* on `x` that returns an independently +owned copy of the current value of `x`. The copy may then be independently +consumed or modified without affecting the original `x`. Note that, while +`copy` allows for a copy to occur, it is not a strict +obligation for the compiler to do so; the copy may still be optimized away +if it is deemed semantically unnecessary. + +`copy` is a contextual keyword, parsed as an operator if it is immediately +followed by an identifier on the same line, like the `consume x` operator before +it. In all other cases, `copy` is still treated as a reference to a +declaration named `copy`, as it would have been prior to this proposal. + +The constraint on implicit copies only affects the parameter binding itself. +The value of the parameter may be passed to other functions, or assigned to +other variables (if the convention allows), at which point the value may +be implicitly copied through those other parameter or variable bindings. + +``` +func foo(x: borrowing String) { + let y = x // ERROR: attempt to copy `x` + bar(z: x) // OK, invoking `bar(z:)` does not require copying `x` +} + +func bar(z: String) { + let w = z // OK, z is implicitly copyable here +} + +func baz(a: consuming String) { + // let aa = (a, a) // ERROR: attempt to copy `a` + + let b = a + let bb = (b, b) // OK, b is implicitly copyable +} +``` + +## Source compatibility + +SE-0377 has not yet shipped with any released version of Swift, so although +this is a breaking change to the behavior specified by SE-0377, it should not +affect any production code in released versions of Swift. + +## ABI compatibility + +This change has no ABI impact. + +## Implications on adoption + +This constraint somewhat increases the risk of source breakage when adopting +the `borrowing` and `consuming` modifiers, since there will be more +bindings that don't allow for implicit copying to match convention changes. +However, we believe this sort of source break is desirable for code using +the ownership modifiers, because the breaks will indicate to developers +where copies become necessary or unnecessary as they manipulate +the conventions of their performance-sensitive APIs. + +## Future directions + +### `borrowing`, `mutating`, and `consuming` local variables + +Swift currently lacks the ability to form local bindings to part of an +aggregate without copying that part, other than by passing the part as +an argument to a function call. We plan to introduce [`borrow` and `inout` +bindings](https://forums.swift.org/t/pitch-borrow-and-inout-declaration-keywords/62366) +that will provide this functionality, with the same no-implicit-copy constraint +described by this proposal applied to these bindings. + +### Consistency for `inout` parameters and the `self` parameter of `mutating` methods + +`inout` parameters and `mutating` methods have been part of Swift since before +version 1.0, and their existing behavior allows for implicit copying of the +current value of the binding. We can't change the existing language +behavior in Swift 5, but accepting this proposal would leave `inout` parameters +and `mutating self` inconsistent with the new modifiers. There are a few things +we could potentially do about that: + +- We could change the behavior of `inout` and `mutating self` parameters to + make them not implicitly copyable in Swift 6 language mode. +- `inout` is also conspicuous now in not following the `-ing` convention we've + settled on for `consuming`/`borrowing`/`mutating` modifiers. We could introduce + `mutating` as a new parameter modifier spelling, with no-implicit-copy + behavior. + +One consideration is that, whereas `borrowing` and `consuming` are strictly +optional for code that works only with copyable types, and is OK with letting +the compiler manage copies automatically, there is no way to get in-place +mutation through function parameters except via `inout`. Tying +no-implicit-copy behavior to mutating parameters could be seen as a violation +of the "progressive disclosure" goal of these ownership features, since +developers would not be able to avoid interacting with the ownership model when +using `inout` parameters anymore. + +## Alternatives considered + +### `@noImplicitCopy` attribute + +Instead of having no-implicit-copy behavior be tied to the ownership-related +binding forms and parameter modifiers, we could have an attribute that can +be applied to any binding to say that it should not be implicitly copyable: + +``` +@noImplicitCopy(self) +func foo(x: @noImplicitCopy String) { + @noImplicitCopy let y = copy x +} +``` + +We had [pitched this possibility](https://forums.swift.org/t/pitch-noimplicitcopy-attribute-for-local-variables-and-function-parameters/61506), +but community feedback rightly pointed out the syntactic weight and noise +of this approach, as well as the fact that, as an attribute, it makes the +ability to control copies feel like an afterthought not well integrated +with the rest of the language. We've decided not to continue in this direction, +since we think that attaching no-implicit-copy behavior to the ownership +modifiers themselves leads to a more coherent design. + +### `copy` as a regular function + +Unlike the `consume x` or `borrow x` operator, copying doesn't have any specific +semantic needs that couldn't be done by a regular function. Instead of an +operator, `copy` could be defined as a regular standard library function: + +``` +func copy(_ value: T) -> T { + return value +} +``` + +We propose `copy x` as an operator, because it makes the relation to +`consume x` and `borrow x`, and it avoids the issues of polluting the +global identifier namespace and occasionally needing to be qualified as +`Swift.copy` if it was a standard library function. + +### Transitive no-implicit-copy constraint + +The no-implicit-copy constraint for a `borrowing` and `consuming` parameter +only applies to that binding, and is not carried over to other variables +or function call arguments receiving the binding's value. We could also +say that the parameter can only be passed as an argument to another function +if that function's parameter uses the `borrowing` or `consuming` modifier to +keep implicit copies suppressed, or that it cannot be bound to `let` or `var` +bindings and must be bound using one of the borrowing bindings once we have +those. However, we think those additional restrictions would only make the +`borrowing` and `consuming` modifiers harder to adopt, since developers would +only be able to use them in cases where they can introduce them bottom-up from +leaf functions. + +The transitivity restriction also would not really improve +local reasoning; since the restriction is only on *implicit* copies, but +explicit copies are still possible, calling into another function may lead +to that other function performing copies, whether they're implicit or not. +The only way to be sure would be to inspect the callee's implementation. +One of the goals of SE-0377 is to introduce the parameter ownership modifiers +in a way that minimizes disruption to the the rest of a codebase, allowing +for the modifiers to be easily adopted in spots where the added control is +necessary, and a transitivity requirement would interfere with that goal for +little benefit. From af28b0131fd18319657618bf7a89650db5c3df2c Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 9 May 2023 21:09:31 -0600 Subject: [PATCH 3129/4563] Accept SE-0386 Also fix some of the metadata --- proposals/0386-package-access-modifier.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index 088ea46405..3cf025f072 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -3,11 +3,10 @@ * Proposal: [SE-0386](0386-package-access-modifier.md) * Authors: [Ellie Shin](https://github.com/elsh), [Alexis Laferriere](https://github.com/xymus) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (March 31st...May 1st, 2023** +* Status: **Accepted** * Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/62700), [apple/swift#62704](https://github.com/apple/swift/pull/62704), [apple/swift#62652](https://github.com/apple/swift/pull/62652), [apple/swift#62652](https://github.com/apple/swift/pull/62652) -* Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) ([first review](https://forums.swift.org/t/se-0386-package-access-modifier/62808)) -* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/28fd2fb9b7258117f912cec5e5f7eb178520fbf2/proposals/NNNN-package-access-modifier.md), -[2](https://github.com/apple/swift-evolution/blob/main/proposals/0386-package-access-modifier.md) +* Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) ([first review](https://forums.swift.org/t/se-0386-package-access-modifier/62808)) ([second review](https://forums.swift.org/t/second-review-se-0386-package-access-modifier/64086)) ([acceptance](https://forums.swift.org/t/accepted-se-0386-package-access-modifier/64904)) +* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/28fd2fb9b7258117f912cec5e5f7eb178520fbf2/proposals/NNNN-package-access-modifier.md), [2](https://github.com/apple/swift-evolution/blob/32e51946296f67be79a58a8c23eb9d7460a06232/proposals/0386-package-access-modifier.md), [3](https://github.com/apple/swift-evolution/blob/4a3a11b18037526cf8d83a9d10b22b94890727e8/proposals/0386-package-access-modifier.md) ## Introduction From fdaeed3f4f77ba1c8be8599bdfc275b5aae8e5ce Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 10 May 2023 15:22:59 -0700 Subject: [PATCH 3130/4563] Recast as an amendment to SE-0377 --- .../0377-parameter-ownership-modifiers.md | 359 ++++++++++++------ ...CD-borrowing-consuming-no-implicit-copy.md | 253 ------------ 2 files changed, 253 insertions(+), 359 deletions(-) delete mode 100644 proposals/ABCD-borrowing-consuming-no-implicit-copy.md diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index 19ccf627fc..09b154354c 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -3,19 +3,27 @@ * Proposal: [SE-0377](0377-parameter-ownership-modifiers.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** -* Implementation: available using the internal names `__shared` and `__owned` -* Review: ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759)) -* Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) +* Status: **Implemented** +* Implementation: Swift 5.9, without no-implicit-copy constraint +* Review: + * [first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759) + * [first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020) + * [first acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759) +* Previous Revisions: + * [1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) + * [2](https://github.com/apple/swift-evolution/blob/7e1d16316e5f68eb94546df9241aa6b4cacb9411/proposals/0377-parameter-ownership-modifiers.md), originally accepted revision ## Introduction We propose new `borrowing` and `consuming` parameter modifiers to allow developers to explicitly choose the ownership convention that a function uses to receive -immutable parameters. This allows for fine-tuning of performance by reducing -the number of ARC calls or copies needed to call a function, and provides a -necessary prerequisite feature for move-only types to specify whether a function -consumes a move-only value or not. +immutable parameters. Applying one of these modifiers to a parameter causes +that parameter binding to no longer be implicitly copyable, and potential +copies need to be marked with the new `copy x` operator. This allows for +fine-tuning of performance by reducing the number of ARC calls or copies needed +to call a function, and provides a necessary prerequisite feature for +noncopyable types to specify whether a function consumes a noncopyable value or +not. ## Motivation @@ -54,23 +62,26 @@ also does not try to optimize polymorphic interfaces, such as non-final class methods or protocol requirements. If a programmer wants behavior different from the default in these circumstances, there is currently no way to do so. -Looking to the future, as part of our [ongoing project to add ownership to Swift](https://forums.swift.org/t/manifesto-ownership/5212), we will eventually have -move-only values and types. Since move-only types do not have the ability to -be copied, the distinction between the two conventions becomes an important -part of the API contract: functions that *borrow* move-only values make -temporary use of the value and leave it valid for further use, like reading -from a file handle, whereas functions that *consume* a move-only value consume -it and prevent its further use, like closing a file handle. Relying on -implicit selection of the parameter convention will not suffice for these -types. +[SE-0390](https://github.com/apple/swift-evolution/blob/main/proposals/0390-noncopyable-structs-and-enums.md) +introduces noncopyable types into Swift. Since noncopyable types do not have +the ability to be copied, the distinction between these two conventions becomes +an important part of the API contract: functions that *borrow* noncopyable +values make temporary use of the value and leave it valid for further use, like +reading from a file handle, whereas functions that *consume* a noncopyable +value consume it and prevent its further use, like closing a file handle. +Relying on implicit selection of the parameter convention will not suffice for +these types. ## Proposed solution We give developers direct control over the ownership convention of -parameters by introducing two new parameter modifiers `borrowing` and `consuming`. +parameters by introducing two new parameter modifiers, `borrowing` and +`consuming`. ## Detailed design +### Syntax of parameter ownership modifiers + `borrowing` and `consuming` become contextual keywords inside parameter type declarations. They can appear in the same places as the `inout` modifier, and are mutually exclusive with each other and with `inout`. In a `func`, @@ -128,6 +139,8 @@ source-breaking effects. (See "related directions" below for interactions with other language features being considered currently or in the near future which might interact with these modifiers in ways that cause them to break source.) +### Ownership convention conversions in protocols and function types + Protocol requirements can also use `consuming` and `borrowing`, and the modifiers will affect the convention used by the generic interface to call the requirement. The requirement may still be satisfied by an implementation that uses different @@ -166,6 +179,12 @@ let h: (consuming Foo) -> Void = f let f2: (Foo) -> Void = h ``` +These implicit conversions for protocol conformances and function values +are not available for parameter types that are noncopyable, in which case +the convention must match exactly. + +### Using parameter bindings with ownership modifiers + Inside of a function or closure body, `consuming` parameters may be mutated, as can the `self` parameter of a `consuming func` method. These mutations are performed on the value that the function itself took ownership of, @@ -189,26 +208,111 @@ extension String { let helloWorld = "hello ".plus("cruel ").plus("world") ``` +`borrowing` and `consuming` parameter values are also **not implicitly copyable** +inside of the function or closure body: + +``` +func foo(x: borrowing String) -> (String, String) { + return (x, x) // ERROR: needs to copy `x` +} +func bar(x: consuming String) -> (String, String) { + return (x, x) // ERROR: needs to copy `x` +} +``` + +And so is the `self` parameter within a method that has the method-level +`borrowing` or `consuming` modifier: + +``` +extension String { + borrowing func foo() -> (String, String) { + return (self, self) // ERROR: needs to copy `self` + } + consuming func bar() -> (String, String) { + return (self, self) // ERROR: needs to copy `x` + } +} +``` + + +A value would need to be implicitly copied if: + +- a *consuming operation* is applied to a `borrowing` binding, or +- a *consuming operation* is applied to a `consuming` binding after it has + already been consumed, or while a *borrowing* or *mutating operation* is simultaneously + being performed on the same binding + +where *consuming*, *borrowing*, and *mutating operations* are as described for +values of noncopyable type in +[SE-0390](https://github.com/apple/swift-evolution/blob/main/proposals/0390-noncopyable-structs-and-enums.md#using-noncopyable-values). +In essence, disabling implicit copying for a value makes it behave as if it +were a value of noncopyable type. + +To allow a copy to occur, the `copy x` operator may be used: + +``` +func dup(_ x: borrowing String) -> (String, String) { + return (copy x, copy x) // OK, copies explicitly allowed here +} +``` + +`copy x` is a *borrowing operation* on `x` that returns an independently +owned copy of the current value of `x`. The copy may then be independently +consumed or modified without affecting the original `x`. Note that, while +`copy` allows for a copy to occur, it is not a strict +obligation for the compiler to do so; the copy may still be optimized away +if it is deemed semantically unnecessary. + +`copy` is a contextual keyword, parsed as an operator if it is immediately +followed by an identifier on the same line, like the `consume x` operator before +it. In all other cases, `copy` is still treated as a reference to a +declaration named `copy`, as it would have been prior to this proposal. + +The constraint on implicit copies only affects the parameter binding itself. +The value of the parameter may be passed to other functions, or assigned to +other variables (if the convention allows), at which point the value may +be implicitly copied through those other parameter or variable bindings. + +``` +func foo(x: borrowing String) { + let y = x // ERROR: attempt to copy `x` + bar(z: x) // OK, invoking `bar(z:)` does not require copying `x` +} + +func bar(z: String) { + let w = z // OK, z is implicitly copyable here +} + +func baz(a: consuming String) { + // let aa = (a, a) // ERROR: attempt to copy `a` + + let b = a + let bb = (b, b) // OK, b is implicitly copyable +} +``` + ## Source compatibility Adding `consuming` or `borrowing` to a parameter in the language today does not -otherwise affect source compatibility. Callers can continue to call the -function as normal, and the function body can use the parameter as it already -does. A method with `consuming` or `borrowing` modifiers on its parameters can still -be used to satisfy a protocol requirement with different modifiers. The -compiler will introduce implicit copies as needed to maintain the expected -conventions. This allows for API authors to use `consuming` and `borrowing` annotations -to fine-tune the copying behavior of their implementations, without forcing -clients to be aware of ownership to use the annotated APIs. Source-only -packages can add, remove, or adjust these annotations on copyable types -over time without breaking their clients. - -This will change if we introduce features that limit the compiler's ability -to implicitly copy values, such as move-only types, "no implicit copy" values -or scopes, and `consume` or `borrow` operators in expressions. Changing the -parameter convention changes where copies may be necessary to perform the call. -Passing an uncopyable value as an argument to a `consuming` parameter ends its -lifetime, and that value cannot be used again after it's consumed. +affect source compatibility with existing code outside of that function. +Callers can continue to call the function as normal, and the function body can +use the parameter as it already does. A method with `consuming` or `borrowing` +modifiers on its parameters can still be used to satisfy a protocol requirement +with different modifiers. Although `consuming` parameter bindings become +mutable, and parameters with either of the `borrowing` or `consuming` modifiers +are not implicitly copyable, the effects are localized to the function +adopting the modifiers. This allows for API authors to use +`consuming` and `borrowing` annotations to fine-tune the copying behavior of +their implementations, without forcing clients to be aware of ownership to use +the annotated APIs. Source-only packages can add, remove, or adjust these +annotations on copyable types over time without breaking their clients. + +Changing parameter modifiers from `borrowing` to `consuming` may however break +source of any client code that also adopts those parameter modifiers, since the +change may affect where copies need to occur in the caller. Going from +`consuming` to `borrowing` however should generally not be source-breaking +for a copyable type. A change in either direction is source-breaking if the +parameter type is noncopyable. ## Effect on ABI stability @@ -238,7 +342,7 @@ because of this potential for confusion. However, whereas `var` and `inout` both suggest mutability, and `var` does not provide explicit directionality as to where mutations become visible, `consuming` on the other hand does not suggest any kind of mutability to the caller, and it explicitly states the -directionality of ownership transfer. Furthermore, with move-only types, the +directionality of ownership transfer. Furthermore, with noncopyable types, the chance for confusion is moot, because the transfer of ownership means the caller cannot even use the value after the callee takes ownership anyway. @@ -290,43 +394,75 @@ function declaration. Similarly, to explicitly pass an argument by borrow without copying, use `foo(borrow x)` at the call site, and `func foo(_: borrowing T)` in the function declaration. -### Effect on call sites and uses of the parameter - -This proposal designs the `consuming` and `borrowing` modifiers to have minimal source -impact when applied to parameters, on the expectation that, in typical Swift -code that isn't using move-only types or other copy-controlling features, -adjusting the convention is a useful optimization on its own without otherwise -changing the programming model, letting the optimizer automatically minimize -copies once the convention is manually optimized. - -It could alternatively be argued that explicitly stating the convention for -a value argument indicates that the developer is interested in guaranteeing -that the optimization occurs, and having the annotation imply changed behavior -at call sites or inside the function definition, such as disabling implicit -copies of the parameter inside the function, or implicitly taking an argument -to a `consuming` parameter and ending its lifetime inside the caller after the -call site. We believe that it is better to keep the behavior of the call in -expressions independent of the declaration (to the degree possible with -implicitly copyable values), and that explicit operators on the call site -can be used in the important, but relatively rare, cases where the default -optimizer behavior is insufficient to get optimal code. +### `@noImplicitCopy` attribute -## Related directions +Instead of having no-implicit-copy behavior be tied to the ownership-related +binding forms and parameter modifiers, we could have an attribute that can +be applied to any binding to say that it should not be implicitly copyable: + +``` +@noImplicitCopy(self) +func foo(x: @noImplicitCopy String) { + @noImplicitCopy let y = copy x +} +``` + +We had [pitched this possibility](https://forums.swift.org/t/pitch-noimplicitcopy-attribute-for-local-variables-and-function-parameters/61506), +but community feedback rightly pointed out the syntactic weight and noise +of this approach, as well as the fact that, as an attribute, it makes the +ability to control copies feel like an afterthought not well integrated +with the rest of the language. We've decided not to continue in this direction, +since we think that attaching no-implicit-copy behavior to the ownership +modifiers themselves leads to a more coherent design. + +### `copy` as a regular function + +Unlike the `consume x` or `borrow x` operator, copying doesn't have any specific +semantic needs that couldn't be done by a regular function. Instead of an +operator, `copy` could be defined as a regular standard library function: + +``` +func copy(_ value: T) -> T { + return value +} +``` -### Caller-side controls on implicit copying +We propose `copy x` as an operator, because it makes the relation to +`consume x` and `borrow x`, and it avoids the issues of polluting the +global identifier namespace and occasionally needing to be qualified as +`Swift.copy` if it was a standard library function. + +### Transitive no-implicit-copy constraint + +The no-implicit-copy constraint for a `borrowing` and `consuming` parameter +only applies to that binding, and is not carried over to other variables +or function call arguments receiving the binding's value. We could also +say that the parameter can only be passed as an argument to another function +if that function's parameter uses the `borrowing` or `consuming` modifier to +keep implicit copies suppressed, or that it cannot be bound to `let` or `var` +bindings and must be bound using one of the borrowing bindings once we have +those. However, we think those additional restrictions would only make the +`borrowing` and `consuming` modifiers harder to adopt, since developers would +only be able to use them in cases where they can introduce them bottom-up from +leaf functions. + +The transitivity restriction also would not really improve +local reasoning; since the restriction is only on *implicit* copies, but +explicit copies are still possible, calling into another function may lead +to that other function performing copies, whether they're implicit or not. +The only way to be sure would be to inspect the callee's implementation. +One of the goals of SE-0377 is to introduce the parameter ownership modifiers +in a way that minimizes disruption to the the rest of a codebase, allowing +for the modifiers to be easily adopted in spots where the added control is +necessary, and a transitivity requirement would interfere with that goal for +little benefit. -There are a number of caller-side operators we are considering to allow for -performance-sensitive code to make assertions about call behavior. These -are closely related to the `consuming` and `borrowing` parameter modifiers and so -share their names. See also the -[Selective control of implicit copying behavior](https://forums.swift.org/t/selective-control-of-implicit-copying-behavior-take-borrow-and-copy-operators-noimplicitcopy/60168) -thread on the Swift forums for deeper discussion of this suite of features +## Related directions #### `consume` operator -Currently under review as -[SE-0366](https://github.com/apple/swift-evolution/blob/main/proposals/0366-move-function.md), -it is useful to have an operator that explicitly ends the lifetime of a +[SE-0366](https://github.com/apple/swift-evolution/blob/main/proposals/0366-move-function.md) +introduced an operator that explicitly ends the lifetime of a variable before the end of its scope. This allows the compiler to reliably destroy the value of the variable, or transfer ownership, at the point of its last use, without depending on optimization and vague ARC optimizer rules. @@ -404,21 +540,22 @@ func callUseFoo() { If `useFooWithoutTouchingGlobal` did in fact attempt to mutate `global` while the caller is borrowing it, an exclusivity failure would be raised. -#### Move-only types, uncopyable values, and related features +#### Noncopyable types The `consuming` versus `borrowing` distinction becomes much more important and -prominent for values that cannot be implicitly copied. We have plans to -introduce move-only types, whose values are never copyable, as well as +prominent for values that cannot be implicitly copied. +[SE-0390](https://github.com/apple/swift-evolution/blob/main/proposals/0390-noncopyable-structs-and-enums.md) +introduces noncopyable types, whose values are never copyable, as well as attributes that suppress the compiler's implicit copying behavior selectively for particular variables or scopes. Operations that borrow a value allow the same value to continue being used, whereas operations that consume a value destroy it and prevent its continued use. This makes the -convention used for move-only parameters a much more important part of their +convention used for noncopyable parameters a much more important part of their API contract, since it directly affects whether the value is still available after the operation: ```swift -moveonly struct FileHandle { ... } +struct FileHandle: ~Copyable { ... } // Operations that open a file handle return new FileHandle values func open(path: FilePath) throws -> FileHandle @@ -444,39 +581,9 @@ func hackPasswords() throws -> HackedPasswords { } ``` -When protocol requirements have parameters of move-only type, the ownership -convention of the corresponding parameters in an implementing method will need to -match exactly, since they cannot be implicitly copied in a thunk: - -```swift -protocol WritableToFileHandle { - func write(to fh: borrowing FileHandle) -} - -extension String: WritableToFileHandle { - // error: does not satisfy protocol requirement, ownership modifier for - // parameter of move-only type `FileHandle` does not match - /* - func write(to fh: consuming FileHandle) { - ... - } - */ - - // This is OK: - func write(to fh: borrowing FileHandle) { - ... - } -} -``` - -All generic and existential types in the language today are assumed to be -copyable, but copyability will need to become an optional constraint in order -to allow move-only types to conform to protocols and be used in generic -functions. When that happens, the need for strict matching of ownership -modifiers in protocol requirements would extend also to any generic parameter -types, associated types, and existential types that are not required to be -copyable, as well as the `Self` type in a protocol that does not require -conforming types to be copyable. +As such, SE-0390 requires parameters of noncopyable type to explicitly state +whether they are `borrowing` or `consuming`, since there isn't a clear +default that is always safe to assume. ### `set`/`out` parameter convention @@ -497,6 +604,40 @@ In Swift up to this point, return values have been the preferred mechanism for functions to pass values back to their callers. This proposal does not propose to add some kind of `out` parameter, but a future proposal could. +### `borrowing`, `mutating`, and `consuming` local variables + +Swift currently lacks the ability to form local bindings to part of an +aggregate without copying that part, other than by passing the part as +an argument to a function call. We plan to introduce [`borrow` and `inout` +bindings](https://forums.swift.org/t/pitch-borrow-and-inout-declaration-keywords/62366) +that will provide this functionality, with the same no-implicit-copy constraint +described by this proposal applied to these bindings. + +### Consistency for `inout` parameters and the `self` parameter of `mutating` methods + +`inout` parameters and `mutating` methods have been part of Swift since before +version 1.0, and their existing behavior allows for implicit copying of the +current value of the binding. We can't change the existing language +behavior in Swift 5, but accepting this proposal would leave `inout` parameters +and `mutating self` inconsistent with the new modifiers. There are a few things +we could potentially do about that: + +- We could change the behavior of `inout` and `mutating self` parameters to + make them not implicitly copyable in Swift 6 language mode. +- `inout` is also conspicuous now in not following the `-ing` convention we've + settled on for `consuming`/`borrowing`/`mutating` modifiers. We could introduce + `mutating` as a new parameter modifier spelling, with no-implicit-copy + behavior. + +One consideration is that, whereas `borrowing` and `consuming` are strictly +optional for code that works only with copyable types, and is OK with letting +the compiler manage copies automatically, there is no way to get in-place +mutation through function parameters except via `inout`. Tying +no-implicit-copy behavior to mutating parameters could be seen as a violation +of the "progressive disclosure" goal of these ownership features, since +developers would not be able to avoid interacting with the ownership model when +using `inout` parameters anymore. + ## Acknowledgments Thanks to Robert Widmann for the original underscored implementation of @@ -509,4 +650,10 @@ of this proposal used `take` and `taking` as the name of the callee-destroy conv The [second reviewed revision](https://github.com/apple/swift-evolution/blob/e3966645cf07d6103561454574ab3e2cc2b48ee9/proposals/0377-parameter-ownership-modifiers.md) used the imperative forms, `consume` and `borrow`, as parameter modifiers, -which were changed to the gerunds `consuming` and `borrowing` in review. +which were changed to the gerunds `consuming` and `borrowing` in review. The +proposal was originally accepted after these revisions. + +The current revision alters the originally-accepted proposal to make it so that +`borrowing` and `consuming` parameter bindings are not implicitly copyable, +and introduces a `copy x` operator that can be used to explicitly allow copies +where needed. diff --git a/proposals/ABCD-borrowing-consuming-no-implicit-copy.md b/proposals/ABCD-borrowing-consuming-no-implicit-copy.md deleted file mode 100644 index a5464d8ad9..0000000000 --- a/proposals/ABCD-borrowing-consuming-no-implicit-copy.md +++ /dev/null @@ -1,253 +0,0 @@ -# Make `borrowing` and `consuming` parameters require explicit copying with the `copy` operator - -* Proposal: [SE-ABCD](ABCD-borrowing-consuming-no-implicit-copy.md) -* Authors: [Joe Groff](https://github.com/jckarter), [Andrew Trick](https://github.com/atrick/), [Michael Gottesman](https://github.com/gottesmm), [Kavon Favardin](https://github.com/kavon) -* Review Manager: TBD -* Status: **Awaiting implementation** -* Previous Proposal: [SE-0377](0377-parameter-ownership-modifiers.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-making-borrowing-and-consuming-parameters-require-manual-copying-with-a-copy-operator/64843/11)) - -## Introduction - -This proposal changes the `borrowing` and `consuming` parameter modifiers to -make it so that the parameter binding is **not implicitly copyable**. Similarly, -the `borrowing` and `consuming` method modifiers make the `self` binding -within the method not implicitly copyable. We also introduce a new operator, -`copy x`, as a way to explicitly allow a copy to occur. - -## Motivation - -The `borrowing` and `consuming` modifiers were introduced by -[SE-0377](0377-parameter-ownership-modifiers.md) as a way to allow for -developers to optimize the calling convention of their functions while -working with copyable types, and also to specify the ownership convention -for working with noncopyable types introduced by -[SE-0390](0390-noncopyable-structs-and-enums.md). Even when working with -values of copyable type, the use of these ownership modifiers is a strong -signal that the developer is optimizing the copying behavior of the function, -but with Swift's normal implicit copying behavior, it is difficult to validate -the effect, if any, of manipulating the ownership convention on the efficiency -of the code. - -Furthermore, as we develop the ownership model, we also plan to -introduce [borrow bindings](https://forums.swift.org/t/pitch-borrow-and-inout-declaration-keywords/62366) -as a way to bind local variables to the internal storage of data structures -without copying. We also believe these bindings should not be implicitly -copying, since it would likely subvert developers' expectations to bind -a variable to a borrow of a value, and then have the value of that binding -get implicitly copied. Making it so that `borrowing` parameters are also -not implicitly copyable provides a consistent model between the parameter -modifiers and corresponding binding kinds. - -## Proposed solution - -We propose to make it so that parameters modified by the `borrowing` or -`consuming` ownership modifiers, as well as the `self` parameter to methods -modified by the `borrowing` or `consuming` method modifiers, is not implicitly -copyable. In cases where copying is acceptable, the new `copy x` operator allows -a copy to occur. - -## Detailed design - -Bindings to parameters modified by the `borrowing` or `consuming` modifier -become not implicitly copyable: - -``` -func foo(x: borrowing String) -> (String, String) { - return (x, x) // ERROR: needs to copy `x` -} -func bar(x: consuming String) -> (String, String) { - return (x, x) // ERROR: needs to copy `x` -} -``` - -So does the `self` parameter to methods with the method-level `borrowing` -or `consuming` modifier: - -``` -extension String { - borrowing func foo() -> (String, String) { - return (self, self) // ERROR: needs to copy `self` - } - consuming func bar() -> (String, String) { - return (self, self) // ERROR: needs to copy `x` - } -} -``` - -A value would need to be implicitly copied if: - -- a *consuming operation* is applied to a `borrowing` binding, or -- a *consuming operation* is applied to a `consuming` binding after it has - already been consumed, or while a *borrowing* or *mutating operation* is simultaneously - being performed on the same binding - -where *consuming*, *borrowing*, and *mutating operations* are as described for -values of noncopyable type in -[SE-0390](https://github.com/apple/swift-evolution/blob/main/proposals/0390-noncopyable-structs-and-enums.md#using-noncopyable-values). -In essence, disabling implicit copying for a value makes it behave as if it -were a value of noncopyable type. - -To allow a copy to occur, the `copy x` operator may be used: - -``` -func dup(_ x: borrowing String) -> (String, String) { - return (copy x, copy x) // OK, copies explicitly allowed here -} -``` - -`copy x` is a *borrowing operation* on `x` that returns an independently -owned copy of the current value of `x`. The copy may then be independently -consumed or modified without affecting the original `x`. Note that, while -`copy` allows for a copy to occur, it is not a strict -obligation for the compiler to do so; the copy may still be optimized away -if it is deemed semantically unnecessary. - -`copy` is a contextual keyword, parsed as an operator if it is immediately -followed by an identifier on the same line, like the `consume x` operator before -it. In all other cases, `copy` is still treated as a reference to a -declaration named `copy`, as it would have been prior to this proposal. - -The constraint on implicit copies only affects the parameter binding itself. -The value of the parameter may be passed to other functions, or assigned to -other variables (if the convention allows), at which point the value may -be implicitly copied through those other parameter or variable bindings. - -``` -func foo(x: borrowing String) { - let y = x // ERROR: attempt to copy `x` - bar(z: x) // OK, invoking `bar(z:)` does not require copying `x` -} - -func bar(z: String) { - let w = z // OK, z is implicitly copyable here -} - -func baz(a: consuming String) { - // let aa = (a, a) // ERROR: attempt to copy `a` - - let b = a - let bb = (b, b) // OK, b is implicitly copyable -} -``` - -## Source compatibility - -SE-0377 has not yet shipped with any released version of Swift, so although -this is a breaking change to the behavior specified by SE-0377, it should not -affect any production code in released versions of Swift. - -## ABI compatibility - -This change has no ABI impact. - -## Implications on adoption - -This constraint somewhat increases the risk of source breakage when adopting -the `borrowing` and `consuming` modifiers, since there will be more -bindings that don't allow for implicit copying to match convention changes. -However, we believe this sort of source break is desirable for code using -the ownership modifiers, because the breaks will indicate to developers -where copies become necessary or unnecessary as they manipulate -the conventions of their performance-sensitive APIs. - -## Future directions - -### `borrowing`, `mutating`, and `consuming` local variables - -Swift currently lacks the ability to form local bindings to part of an -aggregate without copying that part, other than by passing the part as -an argument to a function call. We plan to introduce [`borrow` and `inout` -bindings](https://forums.swift.org/t/pitch-borrow-and-inout-declaration-keywords/62366) -that will provide this functionality, with the same no-implicit-copy constraint -described by this proposal applied to these bindings. - -### Consistency for `inout` parameters and the `self` parameter of `mutating` methods - -`inout` parameters and `mutating` methods have been part of Swift since before -version 1.0, and their existing behavior allows for implicit copying of the -current value of the binding. We can't change the existing language -behavior in Swift 5, but accepting this proposal would leave `inout` parameters -and `mutating self` inconsistent with the new modifiers. There are a few things -we could potentially do about that: - -- We could change the behavior of `inout` and `mutating self` parameters to - make them not implicitly copyable in Swift 6 language mode. -- `inout` is also conspicuous now in not following the `-ing` convention we've - settled on for `consuming`/`borrowing`/`mutating` modifiers. We could introduce - `mutating` as a new parameter modifier spelling, with no-implicit-copy - behavior. - -One consideration is that, whereas `borrowing` and `consuming` are strictly -optional for code that works only with copyable types, and is OK with letting -the compiler manage copies automatically, there is no way to get in-place -mutation through function parameters except via `inout`. Tying -no-implicit-copy behavior to mutating parameters could be seen as a violation -of the "progressive disclosure" goal of these ownership features, since -developers would not be able to avoid interacting with the ownership model when -using `inout` parameters anymore. - -## Alternatives considered - -### `@noImplicitCopy` attribute - -Instead of having no-implicit-copy behavior be tied to the ownership-related -binding forms and parameter modifiers, we could have an attribute that can -be applied to any binding to say that it should not be implicitly copyable: - -``` -@noImplicitCopy(self) -func foo(x: @noImplicitCopy String) { - @noImplicitCopy let y = copy x -} -``` - -We had [pitched this possibility](https://forums.swift.org/t/pitch-noimplicitcopy-attribute-for-local-variables-and-function-parameters/61506), -but community feedback rightly pointed out the syntactic weight and noise -of this approach, as well as the fact that, as an attribute, it makes the -ability to control copies feel like an afterthought not well integrated -with the rest of the language. We've decided not to continue in this direction, -since we think that attaching no-implicit-copy behavior to the ownership -modifiers themselves leads to a more coherent design. - -### `copy` as a regular function - -Unlike the `consume x` or `borrow x` operator, copying doesn't have any specific -semantic needs that couldn't be done by a regular function. Instead of an -operator, `copy` could be defined as a regular standard library function: - -``` -func copy(_ value: T) -> T { - return value -} -``` - -We propose `copy x` as an operator, because it makes the relation to -`consume x` and `borrow x`, and it avoids the issues of polluting the -global identifier namespace and occasionally needing to be qualified as -`Swift.copy` if it was a standard library function. - -### Transitive no-implicit-copy constraint - -The no-implicit-copy constraint for a `borrowing` and `consuming` parameter -only applies to that binding, and is not carried over to other variables -or function call arguments receiving the binding's value. We could also -say that the parameter can only be passed as an argument to another function -if that function's parameter uses the `borrowing` or `consuming` modifier to -keep implicit copies suppressed, or that it cannot be bound to `let` or `var` -bindings and must be bound using one of the borrowing bindings once we have -those. However, we think those additional restrictions would only make the -`borrowing` and `consuming` modifiers harder to adopt, since developers would -only be able to use them in cases where they can introduce them bottom-up from -leaf functions. - -The transitivity restriction also would not really improve -local reasoning; since the restriction is only on *implicit* copies, but -explicit copies are still possible, calling into another function may lead -to that other function performing copies, whether they're implicit or not. -The only way to be sure would be to inspect the callee's implementation. -One of the goals of SE-0377 is to introduce the parameter ownership modifiers -in a way that minimizes disruption to the the rest of a codebase, allowing -for the modifiers to be easily adopted in spots where the added control is -necessary, and a transitivity requirement would interfere with that goal for -little benefit. From 76a80000ce3122ca2a648ab28569b8b92827126e Mon Sep 17 00:00:00 2001 From: Zev Eisenberg Date: Thu, 11 May 2023 10:38:07 -0400 Subject: [PATCH 3131/4563] SE-0386: Add syntax highlighting (#2044) --- proposals/0386-package-access-modifier.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index 3cf025f072..bac6483e0a 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -26,7 +26,7 @@ For example, here’s a scenario where a client has access to a utility API from Module `Engine` in `gamePkg`: -``` +```swift public struct MainEngine { public init() { ... } // Intended to be public @@ -37,7 +37,7 @@ public struct MainEngine { ``` Module `Game` in `gamePkg`: -``` +```swift import Engine public func play() { @@ -46,7 +46,7 @@ public func play() { ``` Client `App` in `appPkg`: -``` +```swift import Game import Engine @@ -71,7 +71,7 @@ Our goal is to introduce a mechanism to Swift to recognize a package as a unit i `package` is introduced as an access modifier. It cannot be combined with other access modifiers. `package` is a contextual keyword, so existing declarations named `package` will continue to work. This follows the precedent of `open`, which was also added as a contextual keyword. For example, the following is allowed: -``` +```swift package var package: String {...} ``` @@ -80,7 +80,7 @@ package var package: String {...} The `package` keyword is added at the declaration site. Using the scenario above, the helper API `run` can be declared with the new access modifier like so: Module `Engine`: -``` +```swift public struct MainEngine { public init() { ... } public var stats: String { ... } @@ -97,7 +97,7 @@ Swift requires that the declarations used in certain places (such as the signatu The `Game` module can access the helper API `run` since it is in the same package as `Engine`. Module `Game`: -``` +```swift import Engine public func play() { @@ -108,7 +108,7 @@ public func play() { However, if a client outside of the package tries to access the helper API, it will not be allowed. Client `App`: -``` +```swift import Game import Engine @@ -122,7 +122,7 @@ Swift as a language leaves it up to the build system to define the boundaries of A new flag `-package-name` is passed down to a commandline invocation, as follows. -``` +```sh swiftc -module-name Engine -package-name gamePkg ... swiftc -module-name Game -package-name gamePkg ... swiftc -module-name App -package-name appPkg ... @@ -136,7 +136,7 @@ The build system should make a best effort to ensure that package names are uniq If a target needs to be excluded from the package boundary, that can be done with a new `packageAccess` setting in the manifest, like so: -``` +```swift .target(name: "Game", dependencies: ["Engine"], packageAccess: false) ``` @@ -152,7 +152,7 @@ When the Swift frontend builds a `.swiftmodule` file directly from source, the f Here's an example. -``` +```swift func internalFuncA() {} @usableFromInline func internalFuncB() {} From 1cb5e6df042136b6cf36aa8dd1e61831989f3716 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 11 May 2023 11:32:32 -0700 Subject: [PATCH 3132/4563] [SE-0397] Allow freestanding macro declarations to have attributes & modifiers Specify that those attributes and modifiers are propagated to the declarations created by the macro expansion. --- .../0397-freestanding-declaration-macros.md | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index 87618abb7c..d3545a5aa5 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -88,11 +88,11 @@ public struct WarningMacro: DeclarationMacro { ### Syntax -The syntactic representation of a freestanding macro expansion site is a macro expansion declaration. A macro expansion declaration is described by the following grammar. It has the same production rule as a [macro expansion expression](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion): +The syntactic representation of a freestanding macro expansion site is a macro expansion declaration. A macro expansion declaration is described by the following grammar. It is based on the production rule as a [macro expansion expression](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion), but with the addition of attributes and modifiers: ``` declaration -> macro-expansion-declaration -macro-expansion-declaration -> '#' identifier generic-argument-clause[opt] function-call-argument-clause[opt] trailing-closures[opt] +macro-expansion-declaration -> attributes? declaration-modifiers? '#' identifier generic-argument-clause[opt] function-call-argument-clause[opt] trailing-closures[opt] ``` At top level and function scope where both expressions and declarations are allowed, a freestanding macro expansion site is first parsed as a macro expansion expression. It will be replaced by a macro expansion declaration later during type checking, if the macro resolves to a declaration macro. It is ill-formed if a macro expansion expression resolves to a declaration macro but isn't the outermost expression. This parsing rule is required in case an expression starts with a macro expansion expression, such as in the following infix expression: @@ -102,6 +102,49 @@ At top level and function scope where both expressions and declarations are allo #line as Int? ``` +#### Attributes and modifiers + +Any attributes and modifiers written on a freestanding macro declaration are implicitly applied to each declaration produced by the macro expansion. For example: + +```swift +@available(toasterOS 2.0, *) +public #gyb( + """ + struct Int${0} { ... } + struct UInt${0} { ... } + """, + [8, 16, 32, 64] +) +``` + +would expand to: + +```swift +@available(toasterOS 2.0, *) +public struct Int8 { ... } + +@available(toasterOS 2.0, *) +public struct UInt8 { ... } + +@available(toasterOS 2.0, *) +public struct Int16 { ... } + +@available(toasterOS 2.0, *) +public struct UInt16 { ... } + +@available(toasterOS 2.0, *) +public struct Int32 { ... } + +@available(toasterOS 2.0, *) +public struct UInt32 { ... } + +@available(toasterOS 2.0, *) +public struct Int64 { ... } + +@available(toasterOS 2.0, *) +public struct UInt64 { ... } +``` + ### Restrictions Like attached peer macros, a freestanding declaration macro can expand to any declaration that is syntatically and semantically well-formed within the context where the macro is expanded. It shares the same requirements and restrictions: From eef4ff9a153cb0f35357d65c4f1c8f5b087ec2e0 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 15 May 2023 12:43:43 +0200 Subject: [PATCH 3133/4563] Update 0392-custom-actor-executors.md This is implemented on 5.9, so mark it as such here --- proposals/0392-custom-actor-executors.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index 14360ab85c..69334c6cfe 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -3,8 +3,7 @@ - Proposal: [SE-0392](0392-custom-actor-executors.md) - Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [John McCall](https://github.com/rjmccall), [Kavon Farvardin](https://github.com/kavon) - Review Manager: [Joe Groff](https://github.com/jckarter) -- Status: **Accepted** -- Implementation: Partially implemented on `main` +- Status: **Implemented (Swift 5.9)** - Previous threads: - Original pitch thread from around Swift 5.5: [Support custom executors in Swift Concurrency](https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425) - Original "assume..." proposal which was subsumed into this proposal, as it relates closely to asserting on executors: [Pitch: Unsafe Assume on MainActor](https://forums.swift.org/t/pitch-unsafe-assume-on-mainactor/63074/) From 77d250e93b3651db7d01d0a7ab69b18e3a3c5689 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 15 May 2023 10:34:28 -0700 Subject: [PATCH 3134/4563] Feedback from @glessard --- proposals/0377-parameter-ownership-modifiers.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index 09b154354c..e53d23d844 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -245,8 +245,8 @@ A value would need to be implicitly copied if: where *consuming*, *borrowing*, and *mutating operations* are as described for values of noncopyable type in [SE-0390](https://github.com/apple/swift-evolution/blob/main/proposals/0390-noncopyable-structs-and-enums.md#using-noncopyable-values). -In essence, disabling implicit copying for a value makes it behave as if it -were a value of noncopyable type. +In essence, disabling implicit copying for a binding makes the binding behave +as if it were of some noncopyable type. To allow a copy to occur, the `copy x` operator may be used: @@ -325,7 +325,9 @@ for which copying is equivalent to `memcpy` and destroying is a no-op; however, `consuming` or `borrowing` break ABI for ABI-stable libraries, but are intended to have minimal impact on source-level API. When using copyable types, adding or -changing these annotations to an API should not affect its existing clients. +changing these annotations to an API should not affect its existing clients, +except where those clients have also adopted the not-implicitly-copyable +conventions. ## Alternatives considered From 0e0dc45347c9119a361331b5c551ca9fd555a36d Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 15 May 2023 16:01:16 -0400 Subject: [PATCH 3135/4563] Take over SE-0377 and put it back into review --- proposals/0377-parameter-ownership-modifiers.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index e53d23d844..1f7a3118d0 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -2,13 +2,11 @@ * Proposal: [SE-0377](0377-parameter-ownership-modifiers.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Joe Groff](https://github.com/jckarter) -* Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Implemented** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (May 15th...29th, 2023)** * Implementation: Swift 5.9, without no-implicit-copy constraint * Review: * [first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759) - * [first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020) - * [first acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759) * Previous Revisions: * [1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) * [2](https://github.com/apple/swift-evolution/blob/7e1d16316e5f68eb94546df9241aa6b4cacb9411/proposals/0377-parameter-ownership-modifiers.md), originally accepted revision From 93ffea0bac2f8eadee1a8c00ea4aff3298e510f5 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 15 May 2023 16:36:17 -0400 Subject: [PATCH 3136/4563] Link SE-0377 to its new review thread Also prettify some links --- proposals/0377-parameter-ownership-modifiers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index 1f7a3118d0..e43e03d005 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -6,7 +6,7 @@ * Status: **Active review (May 15th...29th, 2023)** * Implementation: Swift 5.9, without no-implicit-copy constraint * Review: - * [first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759) + * ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759)) ([revision and third review](https://forums.swift.org/t/se-0377-revision-make-borrowing-and-consuming-parameters-require-explicit-copying-with-the-copy-operator/64996)) * Previous Revisions: * [1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) * [2](https://github.com/apple/swift-evolution/blob/7e1d16316e5f68eb94546df9241aa6b4cacb9411/proposals/0377-parameter-ownership-modifiers.md), originally accepted revision From 4111639f1915528ce709bd011ef3bd017d25550f Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 15 May 2023 16:59:56 -0400 Subject: [PATCH 3137/4563] Extend the SE-0397 review --- proposals/0397-freestanding-declaration-macros.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index d3545a5aa5..1b13b0cc92 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -3,10 +3,11 @@ * Proposal: [SE-0397](0397-freestanding-declaration-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Richard Wei](https://github.com/rxwei), [Holly Borla](https://github.com/hborla) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (April 27th...May 8th, 2023)** +* Status: **Active review (May 15th...22nd, 2023)** * Vision: [Macros](https://github.com/apple/swift-evolution/blob/main/visions/macros.md) * Implementation: On `main` behind the experimental flag `FreestandingMacros` * Review: ([review](https://forums.swift.org/t/se-0397-freestanding-declaration-macros/64655)) +* Previous revisions: ([1](https://github.com/apple/swift-evolution/blob/c0f1e6729b6ca1a4fc2367efe68612fde175afe4/proposals/0397-freestanding-declaration-macros.md)) ## Introduction From 64d9ec11af83923ffb380c7c614e49945763f214 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 15 May 2023 17:30:12 -0400 Subject: [PATCH 3138/4563] Link SE-0397 to its second review thread --- proposals/0397-freestanding-declaration-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index 1b13b0cc92..de3ae2e0b0 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -6,7 +6,7 @@ * Status: **Active review (May 15th...22nd, 2023)** * Vision: [Macros](https://github.com/apple/swift-evolution/blob/main/visions/macros.md) * Implementation: On `main` behind the experimental flag `FreestandingMacros` -* Review: ([review](https://forums.swift.org/t/se-0397-freestanding-declaration-macros/64655)) +* Review: ([review](https://forums.swift.org/t/se-0397-freestanding-declaration-macros/64655)) ([partial acceptance and second review](https://forums.swift.org/t/se-0397-second-review-freestanding-declaration-macros/64997)) * Previous revisions: ([1](https://github.com/apple/swift-evolution/blob/c0f1e6729b6ca1a4fc2367efe68612fde175afe4/proposals/0397-freestanding-declaration-macros.md)) ## Introduction From 0733be9888e29df7a6b502fbc5ae2673ed27ec42 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Mon, 15 May 2023 17:38:35 -0400 Subject: [PATCH 3139/4563] Accept SE-0398 (#2049) --- proposals/0398-variadic-types.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0398-variadic-types.md b/proposals/0398-variadic-types.md index b77490aa29..395fb39f32 100644 --- a/proposals/0398-variadic-types.md +++ b/proposals/0398-variadic-types.md @@ -3,10 +3,10 @@ * Proposal: [SE-0398](0398-variadic-types.md) * Authors: [Slava Pestov](https://github.com/slavapestov), [Holly Borla](https://github.com/hborla) * Review Manager: [Frederick Kellison-Linn](https://github.com/Jumhyn) -* Status: **Active review (April 27th...May 8th, 2023)** +* Status: **Accepted** * Implementation: On main and release/5.9 gated behind the frontend flag -enable-experimental-feature VariadicGenerics * Previous Proposal: [SE-0393](0393-parameter-packs.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-variadic-generic-types-abstracting-over-packs/64377)) ([review](https://forums.swift.org/t/se-0398-allow-generic-types-to-abstract-over-packs/64661)) +* Review: ([pitch](https://forums.swift.org/t/pitch-variadic-generic-types-abstracting-over-packs/64377)) ([review](https://forums.swift.org/t/se-0398-allow-generic-types-to-abstract-over-packs/64661)) ([acceptance](https://forums.swift.org/t/accepted-se-0398-allow-generic-types-to-abstract-over-packs/64998)) ## Introduction From 237d213c9c278b26cacecb66620f88fcab411121 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Tue, 16 May 2023 00:15:23 +0100 Subject: [PATCH 3140/4563] Fix incorrect proposal link (#2050) --- proposals/0397-freestanding-declaration-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index de3ae2e0b0..a3555e70db 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -257,7 +257,7 @@ struct JSONValue: Codable { ## Source compatibility -Freestanding macros use the same syntax introduced for [expression macros](https://github.com/apple/swift-evolution/blob/main/proposals/0392-custom-actor-executors.md), which were themselves a pure extension without an impact on source compatibility. Because a given macro can only have a single freestanding role, and we retain the parsing rules for macro expansion expressions, this proposal introduces no new ambiguities with SE-0392. +Freestanding macros use the same syntax introduced for [expression macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md), which were themselves a pure extension without an impact on source compatibility. Because a given macro can only have a single freestanding role, and we retain the parsing rules for macro expansion expressions, this proposal introduces no new ambiguities with SE-0382. ## Effect on ABI stability From 0ba97f21d17e956eaef7fc7e65d263cc62ce98d2 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 15 May 2023 16:39:38 -0700 Subject: [PATCH 3141/4563] Updates to SE-0390 to correspond to current implementation - Doing flow sensitive escape analysis to allow for locals to be consumed before they get escapingly captured is too hard and probably not really worth it. Just say that they always behave as escaped if they ever are escaped. - Local `let` bindings to closure literals are considered nonescaping if the `let` doesn't escape. This falls out of the existing analysis built into the mandatory SIL passes. - Deinit `self` is immutable for now, while we debate the proper way to handle mutation and consumption while controlling the potential for accidental recursion. --- SE-0390-revs.diff | 46 ++++++++++ .../0390-noncopyable-structs-and-enums.md | 90 +++++++++++++++++-- 2 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 SE-0390-revs.diff diff --git a/SE-0390-revs.diff b/SE-0390-revs.diff new file mode 100644 index 0000000000..fdd3efee46 --- /dev/null +++ b/SE-0390-revs.diff @@ -0,0 +1,46 @@ +diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md +index 9ea1d21e..1dd43893 100644 +--- a/proposals/0390-noncopyable-structs-and-enums.md ++++ b/proposals/0390-noncopyable-structs-and-enums.md +@@ -882,8 +882,7 @@ they can be copied and passed around arbitrarily, and multiple escaping closures + can capture and access the same local variables alongside the local context + from which those captures were taken. Variables captured by escaping closures + thus behave like class properties; immutable captures are treated as always +-borrowed both inside the closure body and in the capture's original context +-after the closure was formed. ++borrowed both inside the closure body and in the capture's original context. + + ``` + func escape(_: @escaping () -> ()) {...} +@@ -894,6 +893,9 @@ func consume(_: consuming FileDescriptor) {} + func foo() { + let x = FileDescriptor() + ++ // ERROR: cannot consume variable before it's been captured ++ consume(x) ++ + escape { + borrow(x) // OK + consume(x) // ERROR: cannot consume captured variable +@@ -909,7 +911,7 @@ func foo() { + ``` + + Mutable captures are subject to dynamic exclusivity checking like class +-properties are. ++properties are, and similarly cannot be consumed and reinitialized. + + ``` + var escapedClosure: (@escaping (inout FileDescriptor) -> ())? +@@ -917,6 +919,12 @@ var escapedClosure: (@escaping (inout FileDescriptor) -> ())? + func foo() { + var x = FileDescriptor() + ++ // ERROR: cannot consume variable before it's been captured. ++ // (We could potentially support local consumption before the variable ++ // capture occurs as a future direction.) ++ consume(x) ++ x = FileDescriptor() ++ + escapedClosure = { _ in borrow(x) } + + // Runtime error when exclusive access to `x` dynamically conflicts diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index 9ea1d21e2c..bb7eb38ac8 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -573,6 +573,14 @@ guard let condition = getCondition() else { use(x) ``` +For the purposes of the following discussion, a closure is considered nonescaping +in the following cases: + +- if the closure literal appears as an argument to a function parameter of + non-`@escaping` function type, or +- if the closure literal is assigned to a local `let` variable, that does not + itself get captured by an escaping closure. + ### Borrowing operations The following operations are borrowing: @@ -882,8 +890,7 @@ they can be copied and passed around arbitrarily, and multiple escaping closures can capture and access the same local variables alongside the local context from which those captures were taken. Variables captured by escaping closures thus behave like class properties; immutable captures are treated as always -borrowed both inside the closure body and in the capture's original context -after the closure was formed. +borrowed both inside the closure body and in the capture's original context. ``` func escape(_: @escaping () -> ()) {...} @@ -894,6 +901,9 @@ func consume(_: consuming FileDescriptor) {} func foo() { let x = FileDescriptor() + // ERROR: cannot consume variable before it's been captured + consume(x) + escape { borrow(x) // OK consume(x) // ERROR: cannot consume captured variable @@ -909,7 +919,7 @@ func foo() { ``` Mutable captures are subject to dynamic exclusivity checking like class -properties are. +properties are, and similarly cannot be consumed and reinitialized. ``` var escapedClosure: (@escaping (inout FileDescriptor) -> ())? @@ -917,6 +927,12 @@ var escapedClosure: (@escaping (inout FileDescriptor) -> ())? func foo() { var x = FileDescriptor() + // ERROR: cannot consume variable before it's been captured. + // (We could potentially support local consumption before the variable + // capture occurs as a future direction.) + consume(x) + x = FileDescriptor() + escapedClosure = { _ in borrow(x) } // Runtime error when exclusive access to `x` dynamically conflicts @@ -942,11 +958,9 @@ struct FileDescriptor: ~Copyable { ``` Like a class `deinit`, a struct or enum `deinit` may not propagate any uncaught -errors. The body of `deinit` has exclusive access to `self` for the duration -of its execution, so `self` behaves as in a `mutating` method; it may be -modified by the body of `deinit`, but must remain valid until the end of the -deinit. (Allowing for partial invalidation inside a `deinit` is explored as -a future direction.) +errors. `self` behaves as in a `borrowing` method; it may not be +modified or consumed by the body of `deinit`. (Allowing for mutation and +partial invalidation inside a `deinit` is explored as a future direction.) A value's lifetime ends, and its `deinit` runs if present, in the following circumstances: @@ -1565,6 +1579,65 @@ enum Soda { } ``` +### Allowing `deinit` to mutate or consume `self`, while avoiding accidental recursion + +During destruction, `deinit` formally has sole ownership of `self`, so it +is possible to allow `deinit` to mutate or consume `self` as part of +deinitialization. However, inside of other `mutating` or `consuming` methods, +it's easy to inadvertently trigger implicit destruction of the value and +reenter `deinit` again: + +``` +struct Foo: ~Copyable { + init() { ... } + + consuming func consumingHelper() { + // If a consuming method does nothing else, it will run `deinit` + } + + mutating func mutatingHelper() { + // A mutating method may consume and reassign self, indirectly triggering + // an implicit deinit + consumingHelper() + self = .init() + } + + deinit { + // mutatingHelper calls consumingHelper, which calls deinit again, leading to an infinite loop + mutatingHelper() + } +} +``` + +Since this is an easy trap to fall into, before we allow `deinit` to mutate +or consume `self`, it's worth considering whether there are any constraints we +could impose to make it less likely to get into an infinite +`deinit` loop situation when doing so. Some possibilities include: + +* We could say that the value remains immutable during `deinit`. Many types + don't need to modify their internal state for cleanup, especially if they + only store a pointer or handle to some resource. This seems overly + restrictive for other kinds of types that have direct ownership of resources, + though. +* We could say that individual *fields* of the value inside of `deinit` are + mutable and consumable, but that the value as a whole is not. This would + allow for `deinit` to individually mutate and/or forward ownership of + elements of the value, but not pass off the entire value to be mutated or + consumed (and potentially re-deinited). This would allow for `deinit`s to + implement logic that modifies or consumes part of the value, but they + wouldn't be allowed to use any methods of the type, other than maybe + `borrowing` methods, to share implementation logic with other members of the + type. +* Since `deinit` must be declared as part of the original type declaration, any + nongeneric methods that it can possibly call on the type must be defined in + the same module as the `deinit`, so we could potentially do some local + analysis of those methods. We could raise a warning or error if a method + called from the deinit either visibly contains any implicit deinit calls + itself, or cannot be analyzed because it's generic, from a protocol + extension, etc. +* We could do nothing and leave it in developers' hands to understand why + deinit loops happen when they do. + ### Finer-grained destructuring in `consuming` methods and `deinit` As currently specified, noncopyable types are (outside of `init` implementations) @@ -1694,6 +1767,7 @@ which is a good optimization for copyable value types, but also allows for more expressivity with noncopyable properties. ### Static casts of functions with ownership modifiers + The rule for casting function values via `as` or some other static, implicit coercion is that a noncopyable parameter's ownership modifier must remain the same. But there are some cases where static conversions of functions From 800569194d40f897d685149eb79ec18cdf92844f Mon Sep 17 00:00:00 2001 From: Sophia Poirier <2997196+sophiapoirier@users.noreply.github.com> Date: Tue, 16 May 2023 13:17:05 -0700 Subject: [PATCH 3142/4563] Tuple of value pack expansion proposal (#2016) * Tuple of value pack expansion proposal * address xwu's review feedback * Update header fields in preparation for review --------- Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- .../0399-tuple-of-value-pack-expansion.md | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 proposals/0399-tuple-of-value-pack-expansion.md diff --git a/proposals/0399-tuple-of-value-pack-expansion.md b/proposals/0399-tuple-of-value-pack-expansion.md new file mode 100644 index 0000000000..ced9047740 --- /dev/null +++ b/proposals/0399-tuple-of-value-pack-expansion.md @@ -0,0 +1,127 @@ +# Tuple of value pack expansion + +* Proposal: [SE-0399](0399-tuple-of-value-pack-expansion.md) +* Authors: [Sophia Poirier](https://github.com/sophiapoirier), [Holly Borla](https://github.com/hborla) +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Active review (May 16...29, 2023)** +* Upcoming Feature Flag: `VariadicGenerics` +* Previous Proposals: [SE-0393](0393-parameter-packs.md), [SE-0398](0398-variadic-types.md) +* Review: ([pitch](https://forums.swift.org/t/tuple-of-value-pack-expansion/64269)) + +## Introduction + +Building upon the **Value and Type Parameter Packs** proposal [SE-0393](https://forums.swift.org/t/se-0393-value-and-type-parameter-packs/63859), this proposal enables referencing a tuple value that contains a value pack inside a pack repetition pattern. + +## Motivation + +When a tuple value contains a value pack, there is no way to reference those pack elements or pass them as a value pack function argument. + +Additionally, type parameter packs are only permitted within a function parameter list, tuple element, or generic argument list. This precludes declaring a type parameter pack as a function return type, type alias, or as a local variable type to permit storage. The available solution to these restrictions is to contain the value pack in a tuple, which makes it important to provide full functional parity between value packs and tuple values containing them. This proposal fills that functionality gap by providing a method to reference individual value pack elements contained within a tuple. + +## Proposed solution + +This proposal extends the functionality of pack repetition patterns to values of _abstract tuple type_, which enables an implicit conversion of an _abstract tuple value_ to its contained value pack. An _abstract tuple type_ is a tuple that has an unknown length and elements of unknown types. Its elements are that of a single type parameter pack and no additional elements, and no label. In other words, the elements of the type parameter pack are the elements of the tuple. An _abstract tuple value_ is a value of _abstract tuple type_. This proposal provides methods to individually access the dynamic pack elements of an abstract tuple value inside of a repetition pattern. + +The following example demonstrates how, with this proposal, we can individually reference and make use of the elements in an abstract tuple value that was returned from another function. The example also highlights some constructs that are not permitted under this proposal: + +```swift +func tuplify(_ value: repeat each T) -> (repeat each T) { + return (repeat each value) +} + +func example(_ value: repeat each T) { + let abstractTuple = tuplify(repeat each value) + repeat print(each abstractTuple) // okay as of this proposal + + let concreteTuple = (true, "two", 3) + repeat print(each concreteTuple) // invalid + + let mixedConcreteAndAbstractTuple = (1, repeat each value) + repeat print(each mixedConcreteAndAbstractTuple) // invalid + + let labeledAbstractTuple = (label: repeat each value) + repeat print(each labeledAbstractTuple) // invalid +} +``` + +### Distinction between tuple values and value packs + +The following example demonstrates a pack repetition pattern on a value pack and an abstract tuple value separately first, then together but with the repetition pattern operating only on the value pack, and finally with the repetition pattern operating on both the value pack and the tuple value's contained value pack together interleaved. Note that, because the standard library function `print` does not currently accept parameter packs but instead only a single value parameter, all of the calls to it wrap the value argument pack in a tuple (hence all of those parentheses). + +```swift +func example(packElements value: repeat each T, tuple: (repeat each T)) { + print((repeat each value)) + print((repeat each tuple)) + + print((repeat (each value, tuple))) + + print((repeat (each value, each tuple))) +} + +example(packElements: 1, 2, 3, tuple: (4, 5, 6)) + +// Prints the following output: +// (1, 2, 3) +// (4, 5, 6) +// ((1, (4, 5, 6)), (2, (4, 5, 6)), (3, (4, 5, 6))) +// ((1, 4), (2, 5), (3, 6)) +``` + +## Detailed design + +Pack reference expressions inside a repetition pattern can have abstract tuple type. The outer structure of the tuple is removed, leaving the elements of a value pack: + +```swift +func expand(value: (repeat each T)) -> (repeat (each T)?) { + return (repeat each value) +} +``` + +Applying the pack repetition pattern effectively removes the outer structure of the tuple leaving just the value pack. In the repetition expression, the base tuple is evaluated once before iterating over its elements. + +```swift +repeat each + +// the above is evaluated like this +let tempTuple = +repeat each tempTuple +``` + +## Source compatibility + +There is no source compatibility impact given that this is an additive change. It enables compiling code that previously would not compile. + +## ABI compatibility + +This proposal does not add or affect ABI as its impact is only on expressions. It does not change external declarations or types. It rests atop the ABI introduced in the **Value and Type Parameter Packs** proposal. + +## Implications on adoption + +Given that this change rests atop the ABI introduced in the **Value and Type Parameter Packs** proposal, this shares with it the same runtime back-deployment story. + +## Alternatives considered + +An earlier design required the use of an abstract tuple value expansion operator, in the form of `.element` (effectively a synthesized label for the value pack contained within the abstract tuple value). This proposal already requires a tuple with a single element that is a value pack, so it is unnecessary to explicitly call out that the expansion is occurring on that element. Requiring `.element` could also introduce potential source breakage in the case of existing code that contains a tuple using the label "element". Dropping the `.element` requirement averts the language inconsistency of designating a reserved tuple label that functions differently than any other tuple label. + +## Future directions + +### Repetition patterns for concrete tuples + +It could help unify language features to extend the repetition pattern syntax to tuples of concrete type. + +```swift +func example(_ value: repeat each T) { + let abstractTuple = (repeat each value) + let concreteTuple = (true, "two", 3) + repeat print(each abstractTuple) + repeat print(each concreteTuple) // currently invalid +} +``` + +### Pack repetition patterns for arrays + +If all elements in a type argument pack are the same or share a conformance, then it should be possible to declare an Array value using a value pack. + +### Lift the single value pack with no label restriction + +This would be required to enable pack repetition patterns for a contained value pack amongst arbitrary other tuple elements that could be addressable via their labels. From 3e4ed1cb846481ff20f5fcc27a980a5c18c805f5 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 16 May 2023 16:25:35 -0400 Subject: [PATCH 3143/4563] Update SE-0399 with review link (#2052) --- proposals/0399-tuple-of-value-pack-expansion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0399-tuple-of-value-pack-expansion.md b/proposals/0399-tuple-of-value-pack-expansion.md index ced9047740..77937856d0 100644 --- a/proposals/0399-tuple-of-value-pack-expansion.md +++ b/proposals/0399-tuple-of-value-pack-expansion.md @@ -6,7 +6,7 @@ * Status: **Active review (May 16...29, 2023)** * Upcoming Feature Flag: `VariadicGenerics` * Previous Proposals: [SE-0393](0393-parameter-packs.md), [SE-0398](0398-variadic-types.md) -* Review: ([pitch](https://forums.swift.org/t/tuple-of-value-pack-expansion/64269)) +* Review: ([pitch](https://forums.swift.org/t/tuple-of-value-pack-expansion/64269)) ([review](https://forums.swift.org/t/se-0399-tuple-of-value-pack-expansion/65017)) ## Introduction From 7b8cbaead3638a474b87972cb1a49fc99defe365 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 16 May 2023 14:42:11 -0700 Subject: [PATCH 3144/4563] Clarify that passing a non-implicitly-copyable binding as an argument is an error if it copies. --- .../0377-parameter-ownership-modifiers.md | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index e43e03d005..e00812c21f 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -232,7 +232,6 @@ extension String { } ``` - A value would need to be implicitly copied if: - a *consuming operation* is applied to a `borrowing` binding, or @@ -289,6 +288,28 @@ func baz(a: consuming String) { } ``` +To clarify the boundary within which the no-implicit-copy constraint applies, a +parameter binding's value *is* noncopyable as part of the *call expression* in +the caller, so if forming the call requires copying, that will raise an error, +even if the parameter would be implicitly copyable in the callee. The function +body serves as the boundary for the no-implicit-copy constraint: + +``` +struct Bar { + var a: String + var b: String + init(ab: String) { + // OK, ab is implicitly copyable here + a = ab + b = ab + } +} + +func foo(x: borrowing String) { + _ = Bar(ab: x) // ERROR: would need to copy `x` to let `Bar.init` consume it +} +``` + ## Source compatibility Adding `consuming` or `borrowing` to a parameter in the language today does not From 6bb642da8bff0ad7624222878c780421c50755e5 Mon Sep 17 00:00:00 2001 From: Ashley Garland Date: Thu, 18 May 2023 11:52:57 -0700 Subject: [PATCH 3145/4563] Update snippets implementation status: Swift 5.7 --- proposals/0356-swift-snippets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0356-swift-snippets.md b/proposals/0356-swift-snippets.md index 5408837f9d..c0090e3bde 100644 --- a/proposals/0356-swift-snippets.md +++ b/proposals/0356-swift-snippets.md @@ -3,7 +3,7 @@ * Proposal: [SE-0356](0356-swift-snippets.md) * Authors: [Ashley Garland](http://github.com/bitjammer) * Review Manager: [Tom Doron](https://github.com/tomerd) -* Status: **Accepted** +* Status: **Implemented (Swift 5.7)** * Implementation: Available in [recent nightly](https://swift.org/download/#snapshots) snapshots. Requires `--enable-experimental-snippet-support` feature flag when using the [Swift DocC Plugin](https://github.com/apple/swift-docc-plugin). Related pull requests: * Swift DocC From 2c996ccdb436820b2822ed4508984eb93bb295ae Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 22 May 2023 09:24:01 -0700 Subject: [PATCH 3146/4563] [SE-0399] `VariadicGenerics` is not an upcoming feature flag. (#2056) Fix the mention of the `VariadicGenerics` flag to clarify that it's an experimental feature flag and not an upcoming feature flag. --- proposals/0399-tuple-of-value-pack-expansion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0399-tuple-of-value-pack-expansion.md b/proposals/0399-tuple-of-value-pack-expansion.md index 77937856d0..fcb8878a68 100644 --- a/proposals/0399-tuple-of-value-pack-expansion.md +++ b/proposals/0399-tuple-of-value-pack-expansion.md @@ -4,7 +4,7 @@ * Authors: [Sophia Poirier](https://github.com/sophiapoirier), [Holly Borla](https://github.com/hborla) * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Active review (May 16...29, 2023)** -* Upcoming Feature Flag: `VariadicGenerics` +* Implementation: On `main` gated behind `-enable-experimental-feature VariadicGenerics` * Previous Proposals: [SE-0393](0393-parameter-packs.md), [SE-0398](0398-variadic-types.md) * Review: ([pitch](https://forums.swift.org/t/tuple-of-value-pack-expansion/64269)) ([review](https://forums.swift.org/t/se-0399-tuple-of-value-pack-expansion/65017)) From 9c8da8f6bd218316187fc2cc93dbfad702183a6e Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 22 May 2023 09:57:46 -0700 Subject: [PATCH 3147/4563] Add a 'Forward Vision' document: using C++ from APIs from Swift. This is a vision document outlining the 'forward' half of C++ and Swift interoperability as requested by the LWG. --- visions/using-c++-from-swift.md | 370 ++++++++++++++++++++++++++++++++ 1 file changed, 370 insertions(+) create mode 100644 visions/using-c++-from-swift.md diff --git a/visions/using-c++-from-swift.md b/visions/using-c++-from-swift.md new file mode 100644 index 0000000000..0c1e14fa00 --- /dev/null +++ b/visions/using-c++-from-swift.md @@ -0,0 +1,370 @@ +# Using C++ from Swift + +## Introduction + +This document lays out a vision for the development of the "forward" half of C++ and Swift interoperability: using C++ APIs from Swift. It sets overarching goals that drive the project’s design decisions, outlines some high-level topics related to C++ interoperability, and, finally, investigates a collection of specific API patterns and potential ways for the compiler to import them. This vision is a sketch, rather than a final design for C++ and Swift interoperability. Towards the end, this document suggests a process for evolving C++ interoperability over time, and it lays out the path for finalizing the designs discussed here. + +“Reverse” interoperability (using Swift APIs from C++) is another extremely important part of the interoperability story. However, reverse interoperability has largely different goals and constraints, which necessarily mean a different design and therefore a different vision document. The [vision for reverse interoperability](https://github.com/apple/swift-evolution/blob/main/visions/using-swift-from-c%2B%2B.md) has already been [accepted](https://forums.swift.org/t/accepted-a-vision-for-using-swift-from-c/62102) by the Language Workgroup. + +This document is a prospective feature vision document, as described in the [draft review management guidelines](https://github.com/rjmccall/swift-evolution/blob/057b2383102f34c3d0f5b257f82bba0f5b94683d/review_management.md#future-directions-and-roadmaps) of the Swift evolution process. It has not yet been approved by the Language Workgroup. + +## Goals + +Swift should strive to import C++ APIs safely and idiomatically. The lack of safety and strong idioms (i.e., complexity) are two common reasons to move away from C++. If using C++ APIs in Swift continued to incur both issues, then interoperability would make Swift a weaker language, and the reason for moving to Swift (especially in mixed language projects) would be less clear. So, Swift must do its best to import C++ APIs in a manner that respects Swift’s safety guarantees and Swift’s strong idioms. If APIs cannot be imported safely or idiomatically, the compiler should request more information from the users (likely through the use of annotations) so that the APIs can be imported in a way that meets Swift’s standards. + +For example, Swift should recognize many different kinds of C++ iterators and ranges and map these into Swift `Collection`s. In C++, iterators travel in pairs forming a range. These iterators are highly unsafe and the inconsistent in their semantics: if the contents of the range change, rendering the iterators invalid; the iterators may escape the lifetime of the range, causing a use-after-free, and so on. The semantics are also often unclear: does a range own its storage, or is merely a view? Is the storage mutable? Can elements of a range be accessed via indexing? When these C++ iterators and ranges are imported in Swift, the compiler can often map them to correct Swift idioms, such as `Collection`s. Swift collections largely (or entirely) side step both the unsafely and unclear semantics of C++ iterators and ranges. When using this Swift interface, even for C++ ranges, it is impossible mismatch iterators, outlive the lifetime of a backing collection, and so on. Taking `std::vector` for example, Swift can idiomatically and safely filter and map its contents using Swift’s `Collection` interface: + +```Swift +images // "images" is of type std::vector + .filter { $0.size > 256 } + .map(UIImage.init) +``` + +So Swift users can immediately see the benefits of adopting Swift, even when using C++ APIs, making Swift a strong, viable successor language to C++. The goal of being a good C++ successor language is to remove a barrier to writing more code in Swift rather than C++. This overarching goal addresses two primary use cases: 1) programmers who are already using Swift and C++ together through a bridging layer and 2) programmers who are considering Swift as a successor language for their C++ projects (especially those looking to move to a memory safe language). + +Many Swift programmers work on mixed-language codebases that directly link Swift code with code written in other languages, most commonly C, Objective-C, and C++. To use C++ APIs from Swift today, the C++ APIs must be wrapped in a C or Objecive-C bridging layer that can be imported into Swift. Allowing Swift to directly use C++ APIs will allow programmers to *incrementally* remove these (potentially huge) bridging layers that are often the source of bugs, performance problems, and expressivity restrictions. + +Additionally, C++ projects are increasingly looking for successor languages that address some of the innate flaws of the language, especially its memory unsafety. Programmers often decline to adopt Swift (or any other new language) in their C++-dominant projects because they fear it would require large rewrites or the creation of massive new bridging layers. C++ interoperability aims to address this case and make Swift a viable option for these projects by allowing incremental adoption of Swift. + +Swift has had great success as a successor language to Objective-C. This success is due to Swifts ability to import most Objective-C APIs as idiomatic APIs that are safe and make sense in Swift as well as expose Swift APIs back to Objective-C, promoting incremental adoption of Swift. While the specifics, constraints, and trade offs of interoperating with C++ are vastly different than Objective-C, the overall philosophy can largely be applied to make Swift an excellent C++ successor. One specific aspect of Objective-C interoperability that lead to Swift being such a successful successor is bidirectional interoperability which allows for incremental adoption of Swift in large codebases. C++ interoperability should strive to acheive a simliar kind of bidirectional ability (if not better) so that when adopting Swift into an existing, large, C++ codebase, a small feature can be written in Swift. The cost of writing this new feature in Swift can be low, because most or all of the C++ APIs should be available automatically as safe and idiomatic Swift APIs. And it should be easy to incorperate the feature into the existing C++ code through the use of ["reverse" interoperability](https://github.com/apple/swift-evolution/blob/main/visions/using-swift-from-c%2B%2B.md). Bidirectional interoperability means that the stakes are low, and projects can adopt Swift at their own pace. + +Importing C++ APIs into Swift is a difficult task that must be handled with care. Almost every goal of C++ interoperability will be in tension with Swift's safety requirements. Swift must strike a careful balance in order to maintain Swift's safety without reintroducing the development, performance, or expressivity costs of an intermediate wrapper API. + +Safety is a top priority for the Swift programming language, which creates a tension with C++. While Swift enforces strong rules around things like memory safety, mutability, and nullability, C++ largely makes the programmer responsible for handling them correctly, on the pain of undefined behavior. Simply using C++ APIs should not completely undermine Swift's lanaguage guarantees, especially guarantees around safety. At a minimum, imported C++ APIs should generally not be less safe to use from Swift than they would be in C++, and C++ interoperability should strive to make imported APIs *safer* in Swift than they are in C++ by providing safe API interfaces for common, unsafe C++ API patterns (such as iterators). When it is possible for the Swift compiler to statically derive safety properties and API semantics (i.e., how to safely use an API in Swift) from the C++ API interface, C++ interoperability should take advantage of this information. When that is not possible, C++ interoperability should provide annotations to communicate the necessary information to use these APIs safely in Swift. When APIs cannot be used safely or need careful management, Swift should make that clear to the programmer. As a last resort, Swift should make an API unavailable if there's no reasonable path to a sufficiently safe Swift interface for it. + +C++ interoperability should strive to have good diagnostics. Diagnostics that report source locations for a C++ API should refer to the API's original declaration in a C++ header, not to a location in a synthesized interface file. When a C++ API can be imported into Swift, diagnostics from misusing it (e.g. type errors when passing it an argument of the wrong type) should be similar to the diagnostics for analogous misuses of a Swift API. When a C++ API cannot be imported, attempts to use it should result in a clear error indicating why the API could not be imported, and the diagnostics should suggest specific ways that the programmer could make it importable (for example, by adding annotations). + +C++ provides tools to create high-performance APIs. The Swift compiler should embrace this. Interop should not be a significant source of overhead, and performance concerns should not be a reason to continue using C++ to call C++ APIs rather than Swift. + +C++ is an "unopinionated" multi-paradigm language, designed to fit many use cases and allow many different programming styles. Different codebases often express the same concept in different ways. There is no prevailing consensus among C++ programmers about the right way to express specific concepts: how to name types and methods, how much to use templates, when to use heap allocation, how to propagate and handle errors, and so on. This creates problems for importing C++ APIs into Swift, which tends to have stronger conventions, some of which are backed by language rules. For instance, it is a common pattern in some C++ codebases to have classes that are only (or at least mostly) intended to be heap-allocated and passed around by pointer; consider this example: + +``` +// StatefulObject has object identity and reference semantcs: +// it should be constructed with "create" and used via a pointer. +struct StatefulObject { + StatefulObject(const StatefulObject&) = delete; + StatefulObject() = delete; + + StatefulObject *create() { return new StatefulObject(); } +}; +``` + +This type is not intended to be used directly as the type of a local variable or a `std::vector` element. Values of the type are allocated on the heap by the `create` method and passed around as a pointer. This is weakly enforced by the way the type hides its constructors, but mostly it's communicated in the documentation and by the overall shape of the API. There is no C++ language feature or programming pattern that directly expresses these semantics. + +If `StatefulObject` were written idiomatically in Swift, it would be defined as a `class` to make it a reference type. This is an example of how Swift defines clear patterns for naming, generic programming, value categories, error handling, and so on, which codebases are encouraged to use as standard practices. These well-defined programming patterns make using Swift APIs a cohesive experience, and C++ interoperability should stive to maintain this experience for Swift programmers using C++ APIs. + +To achieve that, the compiler should map C++ APIs to one of these specific Swift programming patterns. In cases where the most appropriate Swift pattern can be inferred by the Swift compiler, it should map the API automatically. Otherwise, Swift should ask programmers to annotate their C++ APIs to guide how they are imported. For example, Swift imports C++ types as structs with value semantics by default. Because `StatefulObject` cannot be copied, Swift cannot import it via the default approach. To be able to use `StatefulObject`, the user should annotate it as a reference type so that the compiler can import it as a Swift `class`. Information on how to import APIs, such as `StatefulObject`, cannot always be statically determined (for example, `StatefulObject` might have been a move-only type, a singleton, or RAII-style API). The Swift compiler should not import APIs like `StatefulObject` for which it does not have sufficent semantic information. It is not a goal to import every C++ API into Swift, especially without additional, required information to present the API in an idiomatic way that promotes a cohesive Swift expirence. + +Because of the difference in idioms between the two languages, and because of the safety concerns when exposing certain APIs to Swift, a C++ API might look quite different in Swift than it does in C++. It is a goal of C++ interoperability to provide a clear, well-defined mapping for whether and how APIs are imported into Swift. Users should be able to read the C++ interoperability documentation to have a good idea of how much of their API will be able to imported and what it will look like. Swift should also provide tools for inspecting what a C++ API will look like in Swift, and these tools should call out notable parts of the API that were not imported. + +## The approach + +Many C++ constructs have a clear, analogous mapping in Swift. These constructs can be easily and automatically imported to their corresponding Swift constructs. For example: C++ operators can usually be mapped to similar Swift operators. Sometimes, to promote Swift’s idioms, operators should be imported semantically rather than directly. For example, `operator++` should map to a `successor` method in Swift and `operator*` should map to a `pointee` property to promote clarity and uphold Swift’s strong idioms. Another example is namespaces which can be mapped to empty Swift enums, a common pattern in Swift. And enums, even class enums, can be mapped trivially to Swift enums. + +Swift and C++ both support object-oriented programming, allowing objects and members to be expressed easily. For the most part, these objects and their members can be mapped trivially. A member function in C++ translates to a method in Swift, and so on. Swift and C++ not only support object-oriented programmer, but also promote value semantics, providing a clear default for importing C++ classes and structs. Swift can express C++ objects as `struct`s or “value types” where copying, moving, and destroying the value invokes one of the C++ type’s special members: copy constructor, move constructor, or destructor. This default for importing C++ types fits nicely into Swift’s existing object model, and allows most types to automatically work in Swift in an idiomatic way that feels native. + +Unfortunately, not all C++ types are value types, and while Swift expresses value categories explicitly, C++ does not. The example above (in the goals section) shows how the basic tools provided by C++ are often used in idiomatically different ways. This poses a conundrum for importing C++ APIs automatically into Swift which extends far past value categories. A more fundamental place to see this is with memory management. + +In Objective-C, it's fairly straightforward for ARC to ensure that data is valid when it's used. Almost all data in Objective-C is represented with either a fundamental type (such as `double` or `BOOL`) or a reference-counted object type (such as `NSString *`). Values of fundamental types can be safely copied around, and reference-counted objects can be "managed" by retaining them, extending their lifetime without changing the semantics of the program. It's rare to work with an unmanaged pointer in Objective-C, and even rarer to work with an unmanaged pointer that has a dependency on a managed object (although exceptions do exist, such as [`NSData`'s `-bytes` method](https://developer.apple.com/documentation/foundation/nsdata/1410616-bytes?language=objc)). This kind of dependency is problematic for safe memory management because the language often does not know about it and cannot ensure that the backing object stays valid while the pointer is being used. Swift's Objective-C interop has treated these as special cases (with an attribute) and dealt with them individually; while this solution is imperfect in several ways, its rarity has made it a low priority to fix. + +In contrast, it's very common for C++ APIs to work with unmanaged pointers, references, and views into other objects. The lifetime rules for using these correctly are inconsistent and sometimes unique to an API. As an example, consider three values: a value of type `std::vector`, a reference returned from that vector's subscript operator, and an iterator returned from that vector's `begin()` method. At first glance, these values look similar to the compiler: they are all either pointers or class objects containing pointers. But each has its own semantics and expected use (especially concerning lifetime), and these differences are not conveyed explicitly in the source. The vector is a value type that can be copied, but copies can be expensive, and iterators and references into the vector are only valid for a specific copy. The result of the subscript operator is a mutable projection of a specific element, dependent on the vector for validity; but the value of that element can be copied out of the reference to get an independent value, and that is often how the subscript operator is used. The iterator is also a projection, dependent on the vector for validity, but it must be used in conjunction with other iterators or with the vector itself in certain careful ways, and some operations will invalidate it completely. + +So there is a conundrum where superficially similar language constructs in C++ are used to express idiomatic patterns that are vastly different in their impact. The only viable approach for addressing this conundrum is to pick off these patterns one at a time. The Swift compiler will know about many possible C++ API patterns. If a C++ API has semantic annotations telling Swift that it follows a certain pattern, Swift will try to create a Swift interface for it following the rules of that pattern. In the absence of those annotations, Swift will try to use huristics to recognize an appropriate pattern. If this fails, Swift will make the API unavailable. + +Consider how this applies to the `std::vector` example. `std::vector` maps over well as a Swift value type. Its subscript operator can be imported as a Swift `subscript`, and the importer can take advantage of the fact that it returns a reference to allow elements to be efficiently borrowed. And while C++ iterators in general pose serious lifetime safety problems in Swift, Swift can recognize the common `begin()`/`end()` pattern and import it as a safe Swift iterator that encapsulates the unsafety internally. The following sections will go into detail explaining how each of these specific API patterns can be picked off of a C++ codebase. + +### Importing types + +One of the most common uses of this "API patterns" concept concerns the import of types. Swift types fall into two categories: value types and reference types. Copying a value of a reference type produces a new reference to the same underlying object, similar to an intrusive `std::shared_ptr` in C++ or a class type in Java. Copying a value of a value type recursively copies the components of the type to produce an independent value, similar to the behavior of a struct in C or the default behavior of a class type in C++. Furthermore, both kinds of types must always be copyable, although there are plans in the works to allow types to restrict this. + +Types in C++ do not always fit cleanly into this model, and they cannot always be automatically mapped to it even when they do. Many C++ classes are meant to be used as value types, but there are also quite a few C++ classes that Swift programmers would think of as reference types. The difference is not necessarily obvious in source. The closest bit of information that C++ provides directly is whether and how a class has changed its value operations (its copy and move constructors, its assignment operators, and its destructor). A reference type is more likely to delete its copy operations, while a value type is likely to still provide them. But this is not a reliable signal, because some value types are meant to be uncopyable (or even unmovable), while some reference types leave their copy operations intact, either by neglect or to enable objects to be easily cloned when necessary. Furthermore, C++ types sometimes have a more hybrid semantics: iterators, for example, can be used like values, but they're not independent from their underlying collection and in some ways act like references. And some C++ types aren't meant to be used as normal values at all; instead they fill specific idiomatic purposes, like the proxy element references used by `std::vector`, or scoped-destructor types like `std::lock_guard`. + +### Reference types + +Reference types generally fit well into the existing Swift model, and there is little need to restrict them. The safety properties of managed reference types imported from C++ are generally similar to Swift's own class types and imported Objective-C class types. The design below also includes unmanaged reference types, which are less safe than managed types, but not more unsafe than writing the code in C++. Overall, this allows C++ interoperability to offer a clear, native-feeling mapping for several common C++ API patterns. + +#### Criteria for importing as a reference type + +Whether a C++ class type is appropriate to import as a reference type is a complex question, and there are several criteria that go into answering it. + +The first criterion is whether object identity is part of the "value" of the type. Is comparing the address of two objects just asking whether they're stored at the same location, or it is deciding whether they represent the "same object" in a more significant sense? For example, consider a computer game that uses a world model where each object of the `GameObject` class represents a different game object. Copying an object actually means making a second object in the game world, one which initially shares the same internal data as another. This is a classic use of reference semantics, and `GameObject` is clearly a reference type. In contrast, a different game might use a world model where the `GameObjectState` class holds a snapshot of the current state of a game object. The actual game object is identified as part of that snapshot, but it's not synonymous with the snapshot, and copying the snapshot just produces an equivalent snapshot of the same object. This design does not rely on object identity; if `GameObjectState` is a reference type, it is because of some other factor. + +The second criterion is whether the C++ class is polymorphic. Does the class have subclasses whose objects contain additional data or behave differently from objects of the parent class? Swift value types cannot be directly polymorphic, so if polymorphism is an important part of a C++ class, it must be imported as a reference type. The most common indicator of a polymorphic C++ class is having `virtual` methods. More rarely, some C++ classes behave polymorphically but intentionally avoid having `virtual` methods to eliminate the memory overhead of a v-table pointer in every object. + +The third and final criterion whether objects of the C++ class are always passed around by reference. Are objects predominantly passed around using a pointer or reference type, such as a raw pointer (`*`), raw reference (`&` or `&&`), or smart pointer (like `std::unique_ptr` or `std::shared_ptr`)? When passed by raw pointer or reference, is there an expectation that that memory is stable and will continue to stay valid, or are receivers expected to copy the object if they need to keep the value alive independently? If objects are generally allocated and remain at a stable address, even if that address is not semantically part of the "value" of an object, the class may be idiomatically a reference type. This will sometimes be a judgment call for the programmer. + +Most of these criteria are not possible for a compiler to answer automatically by just looking at the code. A compiler cannot know the semantic meaning of object identity for a class type. Nor can can it know whether it is looking at a representative sample of how a type is passed around in a project. Classes satisfying these criteria will have to be annotated somehow to tell the compiler to import them as Swift classes. The one exception is that it might be reasonable to assume that a C++ class with `virtual` functions should be imported as a reference type. + +#### Object management + +Swift generally promises to make sure that objects are valid when used. This is an important part of Swift's core language goal of memory safety. Ideally, when Swift imports a C++ class as a reference type, it will import it as an appropriately managed type that receives the same guarantees as native Swift and imported Objective-C classes. + +It's useful to split the object-management problem into two questions: how objects are managed and whether they can be managed automatically. + +There are three common patterns for managing reference object lifetimes in C++. Swift should endeavor to support all three of them: + + - **Immortal** reference types are not designed to be managed individually by the program. Objects of these types are allocated and then intentionally "leaked" without tracking their uses. Sometimes these objects are not truly immortal: for example, they may be arena-allocated, with an expectation that they will only be referenced from other objects within the arena. Nonetheless, they aren't expected to be individually managed. + + The only reasonable thing Swift can do with immortal reference types is import them as unmanaged classes. This is perfectly fine when objects are truly immortal. If the object is arena-allocated, this is unsafe, but it's essentially an unavoidable level of unsafety given the choices of the C++ API. + + - **Unique** reference types are owned by a single context at once, which must ultimately either destroy it or pass ownership of it to a different context. There are two common idioms for unique ownership in C++. The first is that the object is passed around using a raw pointer (or sometimes a reference) and eventually destroyed using the `delete` operator. The second is that this is automated using a move-only smart pointer such as `std::unique_ptr`. This kind of use of `std::unique_ptr` is often paired with "borrowed" uses that traffic in raw pointers temporarily extracted from the smart pointer; in particular, method calls on the class via `operator->` implicitly receive a raw pointer as `this`. + + The introduction of [non-copyable types](https://forums.swift.org/t/pitch-noncopyable-or-move-only-structs-and-enums/61903) will allow Swift to directly support unique reference types as managed types. The main challenge in doing this will be understanding the ownership conventions for different C++ APIs. If ownership of a class is known to be passed around with a smart pointer like `std::unique_ptr`, then APIs trafficking in raw pointers can be assumed to be working with a borrow; that would support importing as a managed type. Otherwise, Swift will have to put the programmer in charge and either import as an unmanaged type or use a wrapper type like `Unmanaged` to mediate APIs with unknown conventions. + + - **Shared** reference types are reference-counted with custom retain and release operations. In C++, this is nearly always done with a smart pointer like `std::shared_ptr` rather than expecting programmers to manually use retain and release. This is generally compatible with being imported as a managed type. Shared pointer types are either "intrusive" or "non-intrusive", which unfortunately ends up being relevant to semantics. `std::shared_ptr` is a non-intrusive shared pointer, which supports pointers of any type without needing any cooperation. Intrusive shared pointers require cooperation but support some additional operations. Swift should endeavor to support both. + + As with unique reference types, shared reference types in C++ often have APIs that take raw pointers, such as methods on the class type. Unlike unique references, these cannot necessarily be thought of as borrows. Shared reference types are copyable types, and as a general rule, borrowed copyable values can be copied to produce owned values. However, in C++ terms, this would require constructing a shared pointer value from a raw pointer, which in general is not possible to do correctly for non-intrusive shared pointers. It's fine for Swift to *call* APIs that take raw pointers for shared reference types, but it cannot *implement* them without having a way to prevent copying the reference. + + `std::shared_ptr` uses atomic reference-counting in both its intrusive and non-intrusive modes. Non-atomic smart pointer types are supportable, but the imported class type must not be `Sendable`. + +[Examples of each of these are given below.](## Examples and Definitions) + +If a type is annotated as using one of these reference-type patterns, uses of the type in C++ that do not have a consistent interpretation under the pattern will be impossible to import. For example, suppose that `GameObject` is annotated as a shared reference type that uses `std::shared_ptr`. A C++ API that takes a parameter of type `std::unique_ptr`, or a different shared pointer class from the annotated one, must be made unavailable. (This would also include differences in secondary template arguments, such as the `Deleter` template argument of `std::unique_ptr`.) Similarly, a C++ API that takes a `GameObject` as an r-value must be made unavailable. + +Swift doesn't have to force programmers to pick one of these patterns specifically. Without further information, foreign reference types can be imported as an unmanaged class type. There would be an operation on an object to delete it, but if that's not safe to use, the programmer could simply not use it. This behavior would allow types with reference semantics to be expressed with little effort at the cost of complete safety. For some types this may be acceptable or even necessary. However, Swift should strive to make it easy to import types as managed class types, especially for APIs that already make extensive use of smart pointers. + +### Value types + +Value types have value semantics, that is, they can be copied and destroyed. Each instance of the type is a separate copy of the object, rather than a reference to the underlying storage. Swift expresses value types using structs which have the same behavior as C struct. C++ structs also have this behavior by default, but can be custimized to have more complicated lifetime operations, often via custom copy constructors. Custom copy operations are often used to manage storage in C++. While value types with custom copy operations fit into the Swift value type model at a high level, these types are actually novel to Swift. In Swift there is no way to define a custom copy operation and managed storage ususually has long, stable lifetimes. This is in contrast to C++ value types which may have a short lifetime, where storage is associated with an individual copy of the object. To accomidate this novelty, value types must be broken down into three categories. These categories are largely opaque to users of interop, but are essential to describing the interop story, safety and performance properties, potential API restrictions, and the user model more generally. + +#### Simple data types + +This document will refer to C++'s trivially-copyable value types that do not hold pointers as “simple data types.” These types include primitive types such as integers and types which are composed of other simple data types. Simple data types are “owned” types that provide trivial lifetime operations: a copy is a copy of their bits and a destroy is a no-op. Simple data types have roughly the same mapping throughout Swift, C, Objective-C, and C++ making them trivial to import. Simple data types, their instances, methods on simple data types, and other APIs that use simple data types are generally considered to be safe and usable. + +**View types** + +This document will refer to trivially-copyable value types that hold pointers as “view types.” These types include pointers themselves and types which are composed of any other view types (potentially including other types as well). The pointers held by view types refer to memory that is *not owned* by the pointer type (making view types a “view” or “projection” into memory). While view types are very similar to simple data types with respect to their trivial lifetime operations and the fact that they map similarly in these four language, they differ in the fact that while they themselves are not inherently unsafe, they may be used in unsafe APIs (discussed later). + +#### Self-contained types + +This category of types subsumes trivial types to include types with non-trivial members and custom lifetime operations. These types might be "view types" except for the fact that self-contained types *do own* the memory that their members point to. C++ often uses copy constructors and destructors to manage the lifetime of self-contained types. Therefore, the Swift compiler should assume that view types with custom copy constructor and destructors own their memory. Unfortuantly, this is not always the case. Types like `std::vector` have these custom lifetime operations, but do not own their storage. For these cases, Swift must provide annotations that allow the default to be corrected. + +### Other projections + +Value types that own memory through custom lifetime operations do not natively exist in Swift today. Any value types that own memory in Swift do so transatively through reference types that have long, stable lifetimes. Because Swift was not built around this kind of value type with short lifetimes and deep copies, dealing with projections of these owned types can be dangerous. This pattern is, up until now, foreign to Swift, so there are no existing tools that allow users to control this behavior or improve saftey. The best model for handling these potentially unsafe APIs is unclear; maybe most projects can be represented using generalized accessors, maybe most projects can be represented as iterators, maybe some projections should not be projections at all (and rather imported as values that are copied), most likely the answer is some combination of these. The best approach for handling projections will be revealed over time as evolution posts propose potential solutions, such as the iterator bridging described below, and as users of interop provide feedback. + +Besides the dissonant semantic models for representing projections, the bredth of ways to define projections in C++ will prove a challenege for importing this API pattern. + +Consider the following API which returns a vector of pointers: +``` +std::vector OwnedType::projectsInternalStorage(); +``` + +Or this API which fills in a pointer that has two levels of indirection: +``` +void VectorLike::begin(int **out) { *out = data(); } +``` + +Or even this global function that projects one of it's parameters: +``` +int *begin(std::vector *v) { return v->data(); } +``` + +It may be convient for Swift to assume that all projects follow one, unique pattern: a method of an owned type that returns a pointer. However, that is certainly not the only way in which a projection can be created. This is but one of the many places where Swift will need to decide between expressiveness and safety. Allowing the above APIs to be imported would allow interop to be more usable by default. Taking the first example, most of the time, when a vector holds pointers, those pointers do not point to storage with a short lifetime. Making this API unavailable would ensure 100% safety on the pain usability in the 99% case, when this API is safe. The tradeoffs here are an open question for the Swift evolution process to eventually determine. In any case, it is essential that C++ interoperability makes certain assumptions about the APIs Swift imports. There will always be an edge case that cannot be covered or does not make sense to accomidate. Interop as a whole should not become unusable so that these edge cases can be accomidated, or worse yet, so that that neither safe nor unsafe APIs are available in Swift. So, C++ interoperability should make reasonable (not conservative) assumptions when importing APIs. + + ### Iterators + +Both Swift and C++ have powerful libraries for algorithms and iterators. The standard C++ iterator API interface lends itself to the Swift model, allowing C++ iterators and ranges to be mapped to Swift iterators and sequences with relative ease. These mapped APIs are idomatic, native Swift iterators and sequences; their semantics match the rest of the Swift language and Swift APIs compose around them nicely. By taking on Swift iterator semantics, iterators that are imported in this way are able to side-step most or all of the issues that other projects have (described above). + +Swift's powerful suite of algorithms match and go beyond the standard library algorithms provided by C++. These algorithms compose on top of protocols such as Sequence, which C++ ranges should automatically conform to. These Swift APIs and algorithms that operate on Swift iterators and sequences should be prefered to their C++ analogous, as they fit into the rest of the language natrually. However, algorithms are not the only API which operate on iterators and sequences and other C++ APIs must still be useable from Swift. The best way to represent C++ APIs that take one or many iterators (potentially pointing at the same range) is not clear and will need to be explored during the evolution processes. + +### Mutability + +Mutability is a concept where Swift and C++ diverge: by default C++ values are mutable and even when a value is const in C++, it is easily and often ignored (cast away). C++ often overloads functionality on a value’s constness, harming code-clarity. The generally weak notion of mutability in C++ leads to few codebases fully and strictly utilizing the `const` keyword. Swift, on the other hand, enforces mutability strictly and by default. In Swift, mutability conveys semantics, so importing largely mutable C++ codebases can lead to confusion. Swift should encourage C++ codebases to adopt const on methods and values that are used in Swift and expect that C++ does not mutate values that are marked as `const`. And overloaded functions should be clearly be disambiguated in Swift through naming. Mutability is a place where programmers may need to intervene and provide Swift with more information to help promote idiomatic APIs that are expressive and feel natural in Swift. As discussed in the "Views" section, the Swift compiler must make assumptions about the C++ APIs that it is importing, and mutability is another place where Swift will need to make reasonable (not conservative) assumptions about the APIs that it is importing, promoting C++‘s weak notion of `const` to Swift’s much stricter ideal. + +By using Swift's strong notion of mutability, programmers will see the benfitis (especially improved safety) immediatly. For example, +``` +void alias(const int& c, int& m) { + std::jthread t1([&m]() { + m = 0; + }); + std::jthread t2([&c]() { + assert(c == 42); + }); +} +``` +The obviously contrived function above will often assert in C++ if "c" and "m" alias. However in Swift, "c" is guaranteed to be a distinct, truely immutable reference (to uphold Swift's strict safety model). So calling this function in Swift will never assert: +``` +var local: CInt = 42 +test(local, &local) +``` + +### Computed properties + +Value categories and mutability may require user input to map correctly in Swift, but constructs like iterators and getters and setters, can largely be imported automatically. Getters, setters, and subscripts can all be imported into Swift as computed properties. While many C++ codebases define a getter and setter pair, computed properties are the idiomatic way to handle this API pattern in Swift. And computed properties are not just about syntax, they also help promote safety and performance. For example, a C++ getter that returns a reference can be mapped into a generalized accessor in Swift that leverages coroutines to safely yield out its storage to the caller. This generalized accessor pattern allows safe and efficient access to C++ references in Swift and is another example of the more general philosophy for importing APIs: when Swift understands the semantics of an API it can map that API pattern to a strict Swift idiom that is safe, performant, and feels native, so that users get most of the benefits of Swift, even when calling C++ APIs. + +### Templates and generic APIs + +C++ and Swift completely different models for generic programming. C++ templates provide textual specializations of functions and classes that are type checked after being specialized while Swift generics are type checked ahead of time, or separately, and based around APIs rather than textual substituion. The difference makes using generic C++ APIs in Swift difficult. Bridging C++ templates to the Swift model would require substantial engineering effort and the user's guidance. Even if this work was done and users were willing to annotate their C++ libraries sufficently, the Swift model may cause tention with C++ APIs that were designed with specific performance semantics in mind (for example, unboxing every element of a `vector` when performing a copy). + +Swift protocols allow C++ types to be used in a generic context in Swift. Users can extend concrete C++ types, or concrete specializations of C++ class templates to conform to a protocol. Swift could provide a tool (likely in the form of an annotation) that allows users to even conform un-specialized templates to Swift protocols. This level of generic programming may be sufficent for most C++ interop users and would allow the Swift compiler to side-step one of the most difficult and complicated parts of C++ interoperability. + +[This forum post](https://forums.swift.org/t/bridging-c-templates-with-interop/55003) (Bridging C++ Templates with Interop) goes into depth on the issue of importing C++ templates into Swift. + +## The standard library + +Swift should provide an overlay for the C++ standard library to assist in the import of commonly used APIs (such as containers). This overlay should also provide helpful bridging utilites such as protocols that map imported ranges and iterators and explicit conversions from C++ types to standard Swift types. + +C++ aims to provide sufficent tools to implement many features in The Standard Library rather than the compiler. While the Swift compiler also attempts to do this, it is not a goal in and of itself, resulting in many of C++'s analogus features being implemented in the compiler: tuples, pairs, reference counting, ownership, casting support, optionals, and so on. In these cases the Swift compiler will need to work with both the C++ standard library and the Swift overlay for the C++ standard library to import these APIs correctly. + +The reverse is also true; C++ interop may require library-level Swift utilties to assist in the import of various C++ language concepts, such as iterators. To support this case, a set of C++ interop specific Swift APIs will be imported implicitly whenever a C++ module is imported. These APIs do not have a depedency on the distinct C++ standard library or its overlay. + +## Evolution + +C++ interoperability is a huge feature that derives most of its benefit from the combination of its component features (for example, methods can't be used without types). C++ interop should be made useful to programmers before all component pieces have necessarily gone through evolution, both for the benefit of programmers wanting to use this feature, and for compiler developers designing and implementing the feature. + +C++ interoperability should bring in as many APIs as possible, even if they haven't gone through evolution. Swift evolution will progressively work through these APIs, formalizing them, and eventually interop will become a stable feature. Until a critical mass of APIs have been brought Swift's evolution process, a versioning scheme will allow C++ interoperability to be adopted and remain source stable while being evolved. Versions may be rapidly deprecated, but will be independent of Swift compiler versions, allowing source breaks even in minor compiler updates without disturbing adopters. + +This document allows specific, focused, and self contained evolution proposals to be created for individual pieces of the language and specific programming patterns by providing goals that lend themself to this kind of incremental design and evolution (by not importing everything and requiring specific mappings for specific API patterns) and by framing interop in a larger context that these individual evolution proposals can fit into. + +## Tooling and build process + +It goes without saying (yet will be said anyway) that as a supported language feature, C++ and Swift interoperability must work well on every platform supported by Swift. In a similar vein, tools in the Swift ecosystem should be updated to support interoperability features. For example, SourceKit should provide autocompletion, jump-to-definition, etc. for C++ functions, methods, and types and lldb should be able to print C++ types (even in Swift frames). Finally, the Swift package manager should be updated with the necessary features to support building C++ dependencies. + +This document outlines a strategy for importing APIs that rely on semantic information from the user. In order to make this painless for users across a variety of projects, Swift will need to provide both inline annotation support for C++ APIs and side-file support for APIs that cannot be updated. For Objective-C, this side-file is an APINotes file. As part of Swift and C++ interoperability, APINotes will either need to be updated to support C++ APIs, or another kind side-file will need to be created. + +## Appendix 1: Examples and Definitions + +**Reference Types** have reference semantics and object identity. A reference type is a pointer (or “reference”) to some object which means there is a layer of indirection. When a reference type is copied, the pointer’s value is copied rather than the object’s storage. This means reference types can be used to represent non-copyable types in C++. For real-world examples of C++ reference types, consider LLVM's [`Instruction` class](https://llvm.org/doxygen/IR_2Instruction_8h_source.html) or Qt's [`QWidget` class](https://github.com/qt/qtbase/blob/dev/src/widgets/kernel/qwidget.h). + +**Manually Managed Reference Types** + +Here a programmer has written a very large `StatefulObject` which contains many fields: + +``` +struct StatefulObject { + std::array names; + std::array places; + // ... + + StatefulObject(const StatefulObject&) = delete; + StatefulObject() = delete; + + StatefulObject *create() { return new StatefulObject(); } +}; +``` + + +Because this object is so expensive to copy, the programmer decided to delete the copy constructor. The programmer also decided that this object should be allocated on the heap, so they decided to delete the default constructor, and provide a create method in its place. + +In Swift, this `StatefulObject` should be imported as a reference type, as it has reference semantics. + +**API Incorrectly Using Reference Types** + +Here someone has written an API that uses `StatefulObject` as a value type. + +``` +StatefulObject makeAppState(); +``` + +This will invoke a copy of `StatefulObject` which violates the semantics that the API was written with. To be useable from Swift, this API needs to be updated to pass the object indirectly (by reference): + +``` +StatefulObject *makeAppState(); // OK +const StatefulObject *makeAppState(); // OK +StatefulObject &makeAppState(); // OK +const StatefulObject &makeAppState(); // OK +``` + +**Immortal Reference Types** + +Instances of `StatefulObject` above are manually managed by the programmer, they create it with the create method and are responsible for destroying it once it is no longer needed. However, some reference types need to exist for the duration of the program, these references types are known as “immortal.” Examples of these immortal reference types might be pool allocators or app contexts. Let’s look at a `GameContext` object which allocates (and owns) various game elements: + +``` +struct GameContext { + // ... + + GameContext(const GameContext&) = delete; + + Player *createPlayer(); + Scene *createScene(); + Camera *createCamera(); +}; +``` + +Here the `GameContext` is meant to last for the entire game as a global allocator/state. Because the context will never be deallocated, it is known as an “immortal reference type” and the Swift compiler can make certain assumptions about it. + +**Automatically Managed Reference Types** + +While the `GameContext` will live for the duration of the program, individual `GameObject` should be released once they’re done being used. One such object is Player: + +``` +struct GameObject { + int referenceCount; + + GameObject(const GameObject&) = delete; +}; + +void gameObjectRetain(GameObject *obj); +void gameObjectRelease(GameObject *obj); + +struct Player : GameObject { + // ... +}; +``` + +Here Player uses the `gameObjectRetain` and `gameObjectRelease` function to manually manage its reference count in C++. Once the `referenceCount` hits `0`, the Player will be destroyed. Manually managing the reference count is prone to errors, as programmers may forget to retain or release the object. Fortunately, this kind of reference counting is something that Swift is very good at. To enable automatic reference counting, the user can specify the retain and release operations via attributes directly on the `GameObject`. This means the programmer no longer needs to manually call `gameObjectRetain` and `gameObjectRelease`; Swift will do this for them. They will also benefit from the suite of ARC optimizations that Swift has built up over the years. + +**Owned types** “own” some storage which can be copied and destroyed. An owned type must be copyable and destructible. The copy constructor must copy any storage that is owned by the type and the destructor must destroy that storage. Copies and destroys must balance out and these operations must not have side effects. Examples of owned types include `std::vector` and `std::string`. + +**Trivial types** are a subset of owned types. They can be copied by copying the bits of a value of the trivial type and do not need any special destruction logic. Examples of trivial types are `std::array` and `std::pair`. + +**Pointer Types** are trivial types that hold pointers or references to some un-owned storage (storage that is not destroyed when the object is destroyed). Pointer types are *not* a subset of trivial types or owned types. Examples of pointer types include `std::string_view` and `std::span` and raw pointer types such as `int *` or `void *`. + +**Projections** are values rather than types. An example of a method which yields a projection is the `c_str` method on `std::string`. + +``` +struct string { // String is an owned type. + char *storage; + size_t size; + + char *c_str() { return storage; } // Projects internal storage +``` + +Iterators are also projections: + +``` + char *begin() { return storage; } // Projects internal storage + char *end() { return storage + size; } // Projects internal storage +``` + +Because `string` is an owned type, the Swift compiler cannot represent a projection of its storage, so the `begin`, `end`, and `c_str` APIs are not imported. A projection is only valid as long as the storage it points to is valid. Projections of reference types are usually safe because reference types have storage with long, stable lifetimes, but projections of owned types are more dangerous because the storage associated with a specific copy usually has a much shorter lifetime (therefore most of these projections of owned storage cannot yet be imported). + + +## Appendix 2: Lifetime and safety of self-contained types and projections + +The following section will go further into depth on the issues with using projections of self contained types in Swift, rather than proposing a solution on how to import them. Let’s start with an example Swift program that naively imports some self-contained type and returns a projections of it: + +``` +var v = vector(1) +let start = v.begin() +doSomething(start) +fixLifetime(v) +``` + +To understand the problem with this code, the following snippet highlights where an implicit copy is created and destroyed: + +``` +var v = vector(1) +let copy = copy(v) +let start = copy.begin() +destroy(copy) +doSomething(start) +fixLifetime(v) +``` + +Here, because Swift copies `v` into a temporary with a tight lifetime before the call to `begin`, `v` projects a dangling reference. This is an example of how subtly different lifetime models make using C++ types from Swift hard, if their semantics aren’t understood by the compiler. + +To make these APIs safe and usable, Swift cannot import unsafe projections of types that own memory, because they don’t fit the Swift model. Instead, the Swift compiler can try to infer what, semantically, the API is trying to do, or the library author can provide this information via annotations. In this case, the Swift compiler can infer that begin returns an iterator, which Swift can represent through the existing, safe Swift iterator interface. In the example above, “start” is a pointer type. Using this pointer returned by the “begin” method is unsafe, but the type of start itself is not unsafe. In other words, safety restrictions need not be applied to pointer types themselves but rather their unsafe uses. + +C++ often projects the storage of owned types. C++ is able to tie the lifetime of the projection to the source using lexcal scopes. Because there is a well-defined, lexical point in which objects are destroyed, C++ users can reason about projection’s lifetimes. While these safety properties are less formal than Swift, they are safety properties none-the-less, and form a model that works in C++. + +This model cannot be adopted in Swift, however, because the the same lexical lifetime model does not exist. Further, projections of self-contained types are completely foreign concept in Swift, meaning users aren’t familiar with programming in terms of this lexical model, and may not be aware of the added (implicit) constraints (that is, when objects are destroyed). Swift’s language model is such that returning projections from a copied value, even in smaller lexical scope, should be safe. In order to allow projections of self-contained types, this assumption must be broken, or C++ interoperability must take advantage of Swift ownership features to associate the lifetime of the projection to the source. + +The following example highlights the case described above: + +``` +func getCString(str: std.string) -> UnsafePointer { str.c_str() } +``` + +The above function returns a dangling reference to `str`‘s inner storage. In C++, it is assumed that the programmer understands this is a bug, and generally would be expected to take `str` by reference. This is not the case in Swift. To represent this idiomatically in Swift, the lifetimes must be associated through a projection. Using the tools provided in the ownership manifesto this would mean yielding the value returned by `c_str` out of a [generalized accessor](https://github.com/apple/swift/blob/main/docs/OwnershipManifesto.md#generalized-accessors)(resulting in an error when the pointer is returned). From 9f15cfa940dc56214c3b52bdbed112408094aecf Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 22 May 2023 20:36:49 -0400 Subject: [PATCH 3148/4563] Suggest a new start to the Goals section --- visions/using-c++-from-swift.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/visions/using-c++-from-swift.md b/visions/using-c++-from-swift.md index 0c1e14fa00..106623194c 100644 --- a/visions/using-c++-from-swift.md +++ b/visions/using-c++-from-swift.md @@ -10,9 +10,11 @@ This document is a prospective feature vision document, as described in the [dra ## Goals -Swift should strive to import C++ APIs safely and idiomatically. The lack of safety and strong idioms (i.e., complexity) are two common reasons to move away from C++. If using C++ APIs in Swift continued to incur both issues, then interoperability would make Swift a weaker language, and the reason for moving to Swift (especially in mixed language projects) would be less clear. So, Swift must do its best to import C++ APIs in a manner that respects Swift’s safety guarantees and Swift’s strong idioms. If APIs cannot be imported safely or idiomatically, the compiler should request more information from the users (likely through the use of annotations) so that the APIs can be imported in a way that meets Swift’s standards. +There are many reasons for programmers to use C++ from Swift. They might work mostly in Swift but need to take advantage of some code written in C++, anything from a small snippet to a large library. On the other end of the spectrum, they might be C++ programmers looking to adopt Swift as a memory-safe successor language, with a goal of gradually rewriting their codebases into Swift. The foremost goal of Swift's C++ interoperation is to work well for all of these use cases, removing barriers to writing Swift instead of C++, without compromising Swift as a language. -For example, Swift should recognize many different kinds of C++ iterators and ranges and map these into Swift `Collection`s. In C++, iterators travel in pairs forming a range. These iterators are highly unsafe and the inconsistent in their semantics: if the contents of the range change, rendering the iterators invalid; the iterators may escape the lifetime of the range, causing a use-after-free, and so on. The semantics are also often unclear: does a range own its storage, or is merely a view? Is the storage mutable? Can elements of a range be accessed via indexing? When these C++ iterators and ranges are imported in Swift, the compiler can often map them to correct Swift idioms, such as `Collection`s. Swift collections largely (or entirely) side step both the unsafely and unclear semantics of C++ iterators and ranges. When using this Swift interface, even for C++ ranges, it is impossible mismatch iterators, outlive the lifetime of a backing collection, and so on. Taking `std::vector` for example, Swift can idiomatically and safely filter and map its contents using Swift’s `Collection` interface: +To do this, Swift must import C++ APIs safely and idiomatically. Swift's memory safety is a major feature of its design, and C++'s lack of safety is a major defect. If C++'s unsafety is fully inherited when using C++ APIs from Swift, interoperability will have made Swift a worse language, and it will have undermined one of the reasons to migrate to Swift in the first place. But Swift must also make C++ APIs feel natural to use and fit into Swift's strong language idioms. Often these goals coincide, because the better Swift understands how a C++ API is meant to be used, the more unsafety and boilerplate it can eliminate from use sites. If the Swift compiler does not understand how to import an API safely or idiomatically, it should decline to import it, requesting more information from the user (likely through the use of annotations) so that the API can be imported in a way that meets Swift’s standards. + +For example, many C++ APIs traffic in iterators. Direct uses of C++ iterators are difficult to make safe: iterators are unsafe unless used correctly, and that correctness relies on complex properties (such as that the iterator has not been invalidated) that are hard to statically enforce given how iterators are expected to be used. Iterators are also not very idiomatic in Swift because iterator values can only be meaningfully interpreted in pairs. Swift should recognize common C++ patterns like ranges (pairs of iterators) and containers and map them into Swift `Collection`s, making them automatically work with Swift's library of safe and idiomatic collections algorithms. For example, Swift code should be able to filter and map the contents of a `std::vector`: ```Swift images // "images" is of type std::vector @@ -20,15 +22,11 @@ images // "images" is of type std::vector .map(UIImage.init) ``` -So Swift users can immediately see the benefits of adopting Swift, even when using C++ APIs, making Swift a strong, viable successor language to C++. The goal of being a good C++ successor language is to remove a barrier to writing more code in Swift rather than C++. This overarching goal addresses two primary use cases: 1) programmers who are already using Swift and C++ together through a bridging layer and 2) programmers who are considering Swift as a successor language for their C++ projects (especially those looking to move to a memory safe language). - -Many Swift programmers work on mixed-language codebases that directly link Swift code with code written in other languages, most commonly C, Objective-C, and C++. To use C++ APIs from Swift today, the C++ APIs must be wrapped in a C or Objecive-C bridging layer that can be imported into Swift. Allowing Swift to directly use C++ APIs will allow programmers to *incrementally* remove these (potentially huge) bridging layers that are often the source of bugs, performance problems, and expressivity restrictions. - -Additionally, C++ projects are increasingly looking for successor languages that address some of the innate flaws of the language, especially its memory unsafety. Programmers often decline to adopt Swift (or any other new language) in their C++-dominant projects because they fear it would require large rewrites or the creation of massive new bridging layers. C++ interoperability aims to address this case and make Swift a viable option for these projects by allowing incremental adoption of Swift. +This level of idiomatic interoperation allows programmers to immediately see the benefits of adopting Swift, even when using C++ APIs. It makes the two languages work cleanly together. It removes the need for extensive C or Objective-C bridging layers between C++ libraries and their Swift clients, which are often the source of bugs, performance problems, and expressivity restrictions. And when combined with "reverse" interop that exposes Swift APIs to C++, it allows Swift to be added incrementally to an existing C++ codebase and interoperate on a file-by-file basis, enabling it to function as a viable successor language for programmers looking to move past C++. -Swift has had great success as a successor language to Objective-C. This success is due to Swifts ability to import most Objective-C APIs as idiomatic APIs that are safe and make sense in Swift as well as expose Swift APIs back to Objective-C, promoting incremental adoption of Swift. While the specifics, constraints, and trade offs of interoperating with C++ are vastly different than Objective-C, the overall philosophy can largely be applied to make Swift an excellent C++ successor. One specific aspect of Objective-C interoperability that lead to Swift being such a successful successor is bidirectional interoperability which allows for incremental adoption of Swift in large codebases. C++ interoperability should strive to acheive a simliar kind of bidirectional ability (if not better) so that when adopting Swift into an existing, large, C++ codebase, a small feature can be written in Swift. The cost of writing this new feature in Swift can be low, because most or all of the C++ APIs should be available automatically as safe and idiomatic Swift APIs. And it should be easy to incorperate the feature into the existing C++ code through the use of ["reverse" interoperability](https://github.com/apple/swift-evolution/blob/main/visions/using-swift-from-c%2B%2B.md). Bidirectional interoperability means that the stakes are low, and projects can adopt Swift at their own pace. +Swift has had great success as a successor language to Objective-C with this approach of bidirectional, file-by-file interoperation. While the constraints and trade-offs of interoperating with C++ are vastly different from Objective-C, the same overall philosophy can largely be applied to make Swift an excellent C++ successor, because it permits the incremental adoption of Swift in a codebase rather than relying on all-at-once rewrites. To make this viable, interoperation must not rely on radically changing interfaces on either side of the language barrier. For Objective-C, Swift takes the approach of largely incorporating Objective-C by inclusion: most major Objective-C features have corresponding Swift features that are at least as expressive. This is not desirable for C++, most importantly because most of the unsafety of C++ arises from its widespread and idiomatic use of unmanaged pointer and reference types; naively translating these all to `UnsafePointer`s would create an unidiomatic and unsafe mess. So successful import of C++ to Swift must rely on recognizing patterns of how these features are used, perhaps with user guidance, and mapping them to more idiomatic Swift constructs. This idea has proven successful with Objective-C, such as methods with `NSError**` parameters being translated to `throws`; C++ will just need to use it more pervasively. -Importing C++ APIs into Swift is a difficult task that must be handled with care. Almost every goal of C++ interoperability will be in tension with Swift's safety requirements. Swift must strike a careful balance in order to maintain Swift's safety without reintroducing the development, performance, or expressivity costs of an intermediate wrapper API. +Because of this, importing C++ APIs into Swift is a difficult task that must be handled with care. Almost every goal of C++ interoperability will be in tension with Swift's safety requirements. Swift must strike a careful balance in order to maintain Swift's safety without reintroducing the development, performance, or expressivity costs of an intermediate wrapper API. Safety is a top priority for the Swift programming language, which creates a tension with C++. While Swift enforces strong rules around things like memory safety, mutability, and nullability, C++ largely makes the programmer responsible for handling them correctly, on the pain of undefined behavior. Simply using C++ APIs should not completely undermine Swift's lanaguage guarantees, especially guarantees around safety. At a minimum, imported C++ APIs should generally not be less safe to use from Swift than they would be in C++, and C++ interoperability should strive to make imported APIs *safer* in Swift than they are in C++ by providing safe API interfaces for common, unsafe C++ API patterns (such as iterators). When it is possible for the Swift compiler to statically derive safety properties and API semantics (i.e., how to safely use an API in Swift) from the C++ API interface, C++ interoperability should take advantage of this information. When that is not possible, C++ interoperability should provide annotations to communicate the necessary information to use these APIs safely in Swift. When APIs cannot be used safely or need careful management, Swift should make that clear to the programmer. As a last resort, Swift should make an API unavailable if there's no reasonable path to a sufficiently safe Swift interface for it. From 778d1b9c3ac201464382b94a3864f751012bf548 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Mon, 22 May 2023 22:11:00 -0700 Subject: [PATCH 3149/4563] A few tiny tweaks to the iterators section. --- visions/using-c++-from-swift.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visions/using-c++-from-swift.md b/visions/using-c++-from-swift.md index 106623194c..af34423a46 100644 --- a/visions/using-c++-from-swift.md +++ b/visions/using-c++-from-swift.md @@ -14,7 +14,7 @@ There are many reasons for programmers to use C++ from Swift. They might work mo To do this, Swift must import C++ APIs safely and idiomatically. Swift's memory safety is a major feature of its design, and C++'s lack of safety is a major defect. If C++'s unsafety is fully inherited when using C++ APIs from Swift, interoperability will have made Swift a worse language, and it will have undermined one of the reasons to migrate to Swift in the first place. But Swift must also make C++ APIs feel natural to use and fit into Swift's strong language idioms. Often these goals coincide, because the better Swift understands how a C++ API is meant to be used, the more unsafety and boilerplate it can eliminate from use sites. If the Swift compiler does not understand how to import an API safely or idiomatically, it should decline to import it, requesting more information from the user (likely through the use of annotations) so that the API can be imported in a way that meets Swift’s standards. -For example, many C++ APIs traffic in iterators. Direct uses of C++ iterators are difficult to make safe: iterators are unsafe unless used correctly, and that correctness relies on complex properties (such as that the iterator has not been invalidated) that are hard to statically enforce given how iterators are expected to be used. Iterators are also not very idiomatic in Swift because iterator values can only be meaningfully interpreted in pairs. Swift should recognize common C++ patterns like ranges (pairs of iterators) and containers and map them into Swift `Collection`s, making them automatically work with Swift's library of safe and idiomatic collections algorithms. For example, Swift code should be able to filter and map the contents of a `std::vector`: +For example, many C++ APIs traffic in iterators. Direct uses of C++ iterators are difficult to make safe: iterators are unsafe unless used correctly, and that correctness relies on complex properties (such as the lifetime or consistancy of the underlying data) that are impossible to statically enforce. Iterators are also not very idiomatic in Swift because iterator values can only be meaningfully interpreted in pairs (that violate Swift's exclusivity by defintion). And iterator properties are often inconsistantly defined, making them hard to use. So, Swift should recognize common C++ patterns like ranges (pairs of iterators) and containers and map them into Swift `Collection`s, making them automatically work with Swift's library of safe and idiomatic collections algorithms. For example, Swift code should be able to filter and map the contents of a `std::vector`: ```Swift images // "images" is of type std::vector From fef75f822af140da04cce49ebee4b9fcb9a7d064 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 23 May 2023 18:37:47 -0400 Subject: [PATCH 3150/4563] Accept SE-0397 --- proposals/0397-freestanding-declaration-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index a3555e70db..2b3cd1e71f 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -3,10 +3,10 @@ * Proposal: [SE-0397](0397-freestanding-declaration-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Richard Wei](https://github.com/rxwei), [Holly Borla](https://github.com/hborla) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (May 15th...22nd, 2023)** +* Status: **Accepted** * Vision: [Macros](https://github.com/apple/swift-evolution/blob/main/visions/macros.md) * Implementation: On `main` behind the experimental flag `FreestandingMacros` -* Review: ([review](https://forums.swift.org/t/se-0397-freestanding-declaration-macros/64655)) ([partial acceptance and second review](https://forums.swift.org/t/se-0397-second-review-freestanding-declaration-macros/64997)) +* Review: ([review](https://forums.swift.org/t/se-0397-freestanding-declaration-macros/64655)) ([partial acceptance and second review](https://forums.swift.org/t/se-0397-second-review-freestanding-declaration-macros/64997)) ([acceptance](https://forums.swift.org/t/accepted-se-0397-freestanding-declaration-macros/65167)) * Previous revisions: ([1](https://github.com/apple/swift-evolution/blob/c0f1e6729b6ca1a4fc2367efe68612fde175afe4/proposals/0397-freestanding-declaration-macros.md)) ## Introduction From 3256099746fb70be74223a0136f7bcbedc85a88d Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 23 May 2023 16:12:28 -0700 Subject: [PATCH 3151/4563] A few editorial fixes and a better example for mutability. --- visions/using-c++-from-swift.md | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/visions/using-c++-from-swift.md b/visions/using-c++-from-swift.md index af34423a46..ac6561a3d7 100644 --- a/visions/using-c++-from-swift.md +++ b/visions/using-c++-from-swift.md @@ -12,7 +12,7 @@ This document is a prospective feature vision document, as described in the [dra There are many reasons for programmers to use C++ from Swift. They might work mostly in Swift but need to take advantage of some code written in C++, anything from a small snippet to a large library. On the other end of the spectrum, they might be C++ programmers looking to adopt Swift as a memory-safe successor language, with a goal of gradually rewriting their codebases into Swift. The foremost goal of Swift's C++ interoperation is to work well for all of these use cases, removing barriers to writing Swift instead of C++, without compromising Swift as a language. -To do this, Swift must import C++ APIs safely and idiomatically. Swift's memory safety is a major feature of its design, and C++'s lack of safety is a major defect. If C++'s unsafety is fully inherited when using C++ APIs from Swift, interoperability will have made Swift a worse language, and it will have undermined one of the reasons to migrate to Swift in the first place. But Swift must also make C++ APIs feel natural to use and fit into Swift's strong language idioms. Often these goals coincide, because the better Swift understands how a C++ API is meant to be used, the more unsafety and boilerplate it can eliminate from use sites. If the Swift compiler does not understand how to import an API safely or idiomatically, it should decline to import it, requesting more information from the user (likely through the use of annotations) so that the API can be imported in a way that meets Swift’s standards. +To do this, **Swift must import C++ APIs safely and idiomatically**. Swift's memory safety is a major feature of its design, and C++'s lack of safety is a major defect. If C++'s unsafety is fully inherited when using C++ APIs from Swift, interoperability will have made Swift a worse language, and it will have undermined one of the reasons to migrate to Swift in the first place. But Swift must also make C++ APIs feel natural to use and fit into Swift's strong language idioms. Often these goals coincide, because the better Swift understands how a C++ API is meant to be used, the more unsafety and boilerplate it can eliminate from use sites. If the Swift compiler does not understand how to import an API safely or idiomatically, it should decline to import it, requesting more information from the user (likely through the use of annotations) so that the API can be imported in a way that meets Swift’s standards. For example, many C++ APIs traffic in iterators. Direct uses of C++ iterators are difficult to make safe: iterators are unsafe unless used correctly, and that correctness relies on complex properties (such as the lifetime or consistancy of the underlying data) that are impossible to statically enforce. Iterators are also not very idiomatic in Swift because iterator values can only be meaningfully interpreted in pairs (that violate Swift's exclusivity by defintion). And iterator properties are often inconsistantly defined, making them hard to use. So, Swift should recognize common C++ patterns like ranges (pairs of iterators) and containers and map them into Swift `Collection`s, making them automatically work with Swift's library of safe and idiomatic collections algorithms. For example, Swift code should be able to filter and map the contents of a `std::vector`: @@ -34,7 +34,7 @@ C++ interoperability should strive to have good diagnostics. Diagnostics that re C++ provides tools to create high-performance APIs. The Swift compiler should embrace this. Interop should not be a significant source of overhead, and performance concerns should not be a reason to continue using C++ to call C++ APIs rather than Swift. -C++ is an "unopinionated" multi-paradigm language, designed to fit many use cases and allow many different programming styles. Different codebases often express the same concept in different ways. There is no prevailing consensus among C++ programmers about the right way to express specific concepts: how to name types and methods, how much to use templates, when to use heap allocation, how to propagate and handle errors, and so on. This creates problems for importing C++ APIs into Swift, which tends to have stronger conventions, some of which are backed by language rules. For instance, it is a common pattern in some C++ codebases to have classes that are only (or at least mostly) intended to be heap-allocated and passed around by pointer; consider this example: +C++ is a multi-paradigm language, designed to fit many use cases and allow many different programming styles. Different codebases often express the same concept in different ways. There is no prevailing consensus among C++ programmers about the right way to express specific concepts: how to name types and methods, how much to use templates, when to use heap allocation, how to propagate and handle errors, and so on. This creates problems for importing C++ APIs into Swift, which tends to have stronger conventions, some of which are backed by language rules. For instance, it is a common pattern in some C++ codebases to have classes that are only (or at least mostly) intended to be heap-allocated and passed around by pointer; consider this example: ``` // StatefulObject has object identity and reference semantcs: @@ -170,21 +170,17 @@ Swift's powerful suite of algorithms match and go beyond the standard library al Mutability is a concept where Swift and C++ diverge: by default C++ values are mutable and even when a value is const in C++, it is easily and often ignored (cast away). C++ often overloads functionality on a value’s constness, harming code-clarity. The generally weak notion of mutability in C++ leads to few codebases fully and strictly utilizing the `const` keyword. Swift, on the other hand, enforces mutability strictly and by default. In Swift, mutability conveys semantics, so importing largely mutable C++ codebases can lead to confusion. Swift should encourage C++ codebases to adopt const on methods and values that are used in Swift and expect that C++ does not mutate values that are marked as `const`. And overloaded functions should be clearly be disambiguated in Swift through naming. Mutability is a place where programmers may need to intervene and provide Swift with more information to help promote idiomatic APIs that are expressive and feel natural in Swift. As discussed in the "Views" section, the Swift compiler must make assumptions about the C++ APIs that it is importing, and mutability is another place where Swift will need to make reasonable (not conservative) assumptions about the APIs that it is importing, promoting C++‘s weak notion of `const` to Swift’s much stricter ideal. -By using Swift's strong notion of mutability, programmers will see the benfitis (especially improved safety) immediatly. For example, +By using Swift's strong notion of mutability, programmers will see the benefits (especially improved safety) immediately. For example, ``` -void alias(const int& c, int& m) { - std::jthread t1([&m]() { - m = 0; - }); - std::jthread t2([&c]() { - assert(c == 42); - }); +void alias(const std::string& c, std::string& m) { + auto &noConst = const_cast(c) + m = std::move(noConst); } ``` -The obviously contrived function above will often assert in C++ if "c" and "m" alias. However in Swift, "c" is guaranteed to be a distinct, truely immutable reference (to uphold Swift's strict safety model). So calling this function in Swift will never assert: +`m` will likely be empty if `c` and `m` alias in C++ (because the empty, moved-from string is used to initialize `m`). However in Swift, `c` is guaranteed to be a distinct, truely immutable reference (to uphold Swift's strict safety model). So calling this function in Swift will never assert: ``` -var local: CInt = 42 -test(local, &local) +var local: std.string = "Hello, World" +alias(local, &local) ``` ### Computed properties From ca906cef05d21542c03b3fb569d32d0e061b03f7 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 24 May 2023 20:35:52 +0100 Subject: [PATCH 3152/4563] Update 0364-retroactive-conformance-warning.md (#2061) --- proposals/0364-retroactive-conformance-warning.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0364-retroactive-conformance-warning.md b/proposals/0364-retroactive-conformance-warning.md index 145217e955..76c417acaa 100644 --- a/proposals/0364-retroactive-conformance-warning.md +++ b/proposals/0364-retroactive-conformance-warning.md @@ -3,12 +3,13 @@ * Proposal: [SE-0364](0364-retroactive-conformance-warning.md) * Author: [Harlan Haskins](https://github.com/harlanhaskins) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active review (April 25...May 9)** +* Status: **Accepted** * Implementation: [apple/swift#36068](https://github.com/apple/swift/pull/36068) * Review: ([first pitch](https://forums.swift.org/t/warning-for-retroactive-conformances-if-library-evolution-is-enabled/45321)) ([second pitch](https://forums.swift.org/t/pitch-warning-for-retroactive-conformances-of-external-types-in-resilient-libraries/56243)) ([first review](https://forums.swift.org/t/se-0364-warning-for-retroactive-conformances-of-external-types/58922)) ([second review](https://forums.swift.org/t/second-review-se-0364-warning-for-retroactive-conformances-of-external-types/64615)) + ([acceptance](https://forums.swift.org/t/accepted-se-0364-warning-for-retroactive-conformances-of-external-types/65015)) ## Introduction From 3ab911e042314393318fb5ddc4f883a43786a20a Mon Sep 17 00:00:00 2001 From: Joshua Turcotti Date: Wed, 24 May 2023 12:54:31 -0700 Subject: [PATCH 3153/4563] Fix typo in 0302-concurrent-value-and-concurrent-closures.md (#2060) fix typo - s/classes/enums --- proposals/0302-concurrent-value-and-concurrent-closures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0302-concurrent-value-and-concurrent-closures.md b/proposals/0302-concurrent-value-and-concurrent-closures.md index 17cd2009ac..a0bb795637 100644 --- a/proposals/0302-concurrent-value-and-concurrent-closures.md +++ b/proposals/0302-concurrent-value-and-concurrent-closures.md @@ -197,7 +197,7 @@ Metatypes (such as `Int.Type`, the type produced by the expression `Int.self`) a #### `Sendable` conformance checking for structs and enums -`Sendable` types are extremely common in Swift and aggregates of them are also safe to transfer across concurrency domains. As such, the Swift compiler allows direct conformance to `Sendable` for structs and classes that are compositions of other `Sendable` types: +`Sendable` types are extremely common in Swift and aggregates of them are also safe to transfer across concurrency domains. As such, the Swift compiler allows direct conformance to `Sendable` for structs and enums that are compositions of other `Sendable` types: ```swift struct MyPerson : Sendable { var name: String, age: Int } From 2ddca4f632c687d67d30fded4dec5174c9327aa1 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 16 May 2023 11:49:42 -0700 Subject: [PATCH 3154/4563] Address @gottesmm's feedback --- .../0390-noncopyable-structs-and-enums.md | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index bb7eb38ac8..a5b1683065 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -474,6 +474,12 @@ The following operations are consuming: c.property = x use(x) // ERROR: x consumed by assignment to `c.property` ``` + + The one exception is assigning to the "black hole" `_ = x`, which is + a borrowing operation, as noted below. This allows for the + `_ = x` idiom to still be used to prevent warnings about a borrowed + binding that is otherwise unused. + - passing an argument to a `consuming` parameter of a function: ```swift @@ -531,7 +537,7 @@ The following operations are consuming: let x = FileDescriptorOrBuffer.file(FileDescriptor()) - switch x { + switch consume x { case .file(let f): break case .buffer(let b): @@ -541,6 +547,12 @@ The following operations are consuming: use(x) // ERROR: x consumed by `switch` ``` + In order to allow for borrowing pattern matching to potentially become + the default later, when it's supported, the operand to `switch` or + the right-hand side of a `case` condition in an `if` or `while` must + use the `consume` operator in order to indicate that it is consumed. + We may want `switch x` to borrow by default in the future. + - iterating a `Sequence` with a `for` loop: ```swift @@ -581,6 +593,9 @@ in the following cases: - if the closure literal is assigned to a local `let` variable, that does not itself get captured by an escaping closure. +These cases correspond to the cases where a closure is allowed to capture an +`inout` parameter from its surrounding scope, before this proposal. + ### Borrowing operations The following operations are borrowing: @@ -605,6 +620,8 @@ The following operations are borrowing: of the accessor's execution. - Capturing an immutable local binding into a nonescaping closure borrows the binding for the duration of the callee that receives the nonescaping closure. +- Assigning into the "black hole" `_ = x` borrows the right-hand side of the + assignment. ### Mutating operations @@ -919,7 +936,12 @@ func foo() { ``` Mutable captures are subject to dynamic exclusivity checking like class -properties are, and similarly cannot be consumed and reinitialized. +properties are, and similarly cannot be consumed and reinitialized. When +a closure escapes, the compiler isn't able to statically know when the closure +is invoked, and it may even be invoked multiple overlapping times, or +simultaneously on different threads if the closure is `@Sendable`, so the +captures must always remain in a valid state for memory safety, and exclusivity +of mutations can only be enforced dynamically. ``` var escapedClosure: (@escaping (inout FileDescriptor) -> ())? @@ -1799,9 +1821,28 @@ value is required to provide a non-borrowed value to the underlying function. ## Revision history -This version of the proposal makes the following changes from the -[first reviewed revision](https://github.com/apple/swift-evolution/blob/5d075b86d57e3436b223199bd314b2642e30045f/proposals/0390-noncopyable-structs-and-enums.md) -of this proposal: +This revision makes the following changes from the [second reviewed revision](https://github.com/apple/swift-evolution/blob/a9e21e3a4eb9526f998915c6554c7c72e5885a91/proposals/0390-noncopyable-structs-and-enums.md) +in response to Language Steering Group review and implementation experience: + +- `_ = x` is now a borrowing operation. +- `switch` and `if/while case` require the subject of a pattern match to use + the `consume x` operator. The fact that they are consuming operations now + is an artifact of the implementation, and with further development, we may + want to make the default semantics of `switch x` without explicit consumption + to be borrowing. +- Escaped closure captures are constrained from being consumed for their + entire lifetime, even before the closure that escapes it is formed. This + analysis was not practical to implement using our current analysis, and the + added expressivity is unlikely to be worth the implementation complexity. +- `self` in a deinit is currently constrained to be immutable, since there + is [ongoing discussion](https://forums.swift.org/t/se-0390-noncopyable-type-deinit-s-mutation-and-accidental-recursion/64767) + about how best to manage mutation or consumption during deinits while + managing the possibility to accidentally cause recursion into `deinit` + by implicit destruction. + +The [second reviewed revision](https://github.com/apple/swift-evolution/blob/a9e21e3a4eb9526f998915c6554c7c72e5885a91/proposals/0390-noncopyable-structs-and-enums.md) +of the proposal made the following changes from the +[first reviewed revision](https://github.com/apple/swift-evolution/blob/5d075b86d57e3436b223199bd314b2642e30045f/proposals/0390-noncopyable-structs-and-enums.md): - The original revision did not provide a `Copyable` generic constraint, and declared types as noncopyable using a `@noncopyable` attribute. The @@ -1817,8 +1858,10 @@ of this proposal: the behavior of `mem::forget` in Rust doesn't correspond to the semantics of the operation proposed here, and the language workgroup doesn't find the term clear enough on its own. This revision of the proposal chooses the - more explicit `suppressdeinit`, which is long and awkward but at least - explicit, as a starting point for further review discussion. + `discard` as a starting point for further review discussion. Furthermore, + we limit its use to types whose contents are otherwise trivial, in order to + avoid committing to interactions with elementwise consumption of fields + that we may want to refine later. - The original revision allowed for a `consuming` method declared anywhere in the type's original module to suppress `deinit`. This revision narrows the From bc34486b7a2484e1f80f2037a375f6b12e7500ed Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 26 May 2023 01:15:29 +0100 Subject: [PATCH 3155/4563] Delete SE-0390-revs.diff (#2062) --- SE-0390-revs.diff | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) delete mode 100644 SE-0390-revs.diff diff --git a/SE-0390-revs.diff b/SE-0390-revs.diff deleted file mode 100644 index fdd3efee46..0000000000 --- a/SE-0390-revs.diff +++ /dev/null @@ -1,46 +0,0 @@ -diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md -index 9ea1d21e..1dd43893 100644 ---- a/proposals/0390-noncopyable-structs-and-enums.md -+++ b/proposals/0390-noncopyable-structs-and-enums.md -@@ -882,8 +882,7 @@ they can be copied and passed around arbitrarily, and multiple escaping closures - can capture and access the same local variables alongside the local context - from which those captures were taken. Variables captured by escaping closures - thus behave like class properties; immutable captures are treated as always --borrowed both inside the closure body and in the capture's original context --after the closure was formed. -+borrowed both inside the closure body and in the capture's original context. - - ``` - func escape(_: @escaping () -> ()) {...} -@@ -894,6 +893,9 @@ func consume(_: consuming FileDescriptor) {} - func foo() { - let x = FileDescriptor() - -+ // ERROR: cannot consume variable before it's been captured -+ consume(x) -+ - escape { - borrow(x) // OK - consume(x) // ERROR: cannot consume captured variable -@@ -909,7 +911,7 @@ func foo() { - ``` - - Mutable captures are subject to dynamic exclusivity checking like class --properties are. -+properties are, and similarly cannot be consumed and reinitialized. - - ``` - var escapedClosure: (@escaping (inout FileDescriptor) -> ())? -@@ -917,6 +919,12 @@ var escapedClosure: (@escaping (inout FileDescriptor) -> ())? - func foo() { - var x = FileDescriptor() - -+ // ERROR: cannot consume variable before it's been captured. -+ // (We could potentially support local consumption before the variable -+ // capture occurs as a future direction.) -+ consume(x) -+ x = FileDescriptor() -+ - escapedClosure = { _ in borrow(x) } - - // Runtime error when exclusive access to `x` dynamically conflicts From db1ea8cd465cf778f2c6a0163e8039ae4c219b70 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 26 May 2023 01:43:51 +0100 Subject: [PATCH 3156/4563] Update 0390-noncopyable-structs-and-enums.md (#2063) --- proposals/0390-noncopyable-structs-and-enums.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index a5b1683065..e95503fac9 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -3,9 +3,9 @@ * Proposal: [SE-0390](0390-noncopyable-structs-and-enums.md) * Authors: [Joe Groff](https://github.com/jckarter), [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Kavon Farvardin](https://github.com/kavon) * Review Manager: [Stephen Canon](https://github.com/stephentyrone) -* Status: **Active review (March 21 ... April 4, 2023)** +* Status: **Accepted** * Implementation: on main as `@_moveOnly` behind the `-enable-experimental-move-only` option -* Review: [(pitch)](https://forums.swift.org/t/pitch-noncopyable-or-move-only-structs-and-enums/61903)[(first review)](https://forums.swift.org/t/se-0390-noncopyable-structs-and-enums/63258)[(second review)](https://forums.swift.org/t/second-review-se-0390-noncopyable-structs-and-enums/63866) +* Review: ([pitch](https://forums.swift.org/t/pitch-noncopyable-or-move-only-structs-and-enums/61903)) ([first review](https://forums.swift.org/t/se-0390-noncopyable-structs-and-enums/63258)) ([second review](https://forums.swift.org/t/second-review-se-0390-noncopyable-structs-and-enums/63866)) ([acceptance](https://forums.swift.org/t/accepted-se-0390-noncopyable-structs-and-enums/65157)) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/5d075b86d57e3436b223199bd314b2642e30045f/proposals/0390-noncopyable-structs-and-enums.md) ## Introduction From 9516072bff9b8cda506f68d7a8a06d60d51f2b16 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Tue, 30 May 2023 10:32:26 -0500 Subject: [PATCH 3157/4563] Observability proposal revision (#2040) * Update Observation proposal for latest changes * Incorporate Philippe's feedback, finish revision * Expand on intention for latest proposal revision * Set second review timeframe --------- Co-authored-by: Ben Cohen --- proposals/0395-observability.md | 600 ++++++++++---------------------- 1 file changed, 181 insertions(+), 419 deletions(-) diff --git a/proposals/0395-observability.md b/proposals/0395-observability.md index 3cb7080b81..78f6ff504b 100644 --- a/proposals/0395-observability.md +++ b/proposals/0395-observability.md @@ -3,13 +3,14 @@ * Proposal: [SE-0395](0395-observability.md) * Authors: [Philippe Hausler](https://github.com/phausler), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (April 11 – April 24, 2023)** +* Status: **Active Review (May 30 – June 12, 2023)** #### Changes -* Pitch 1: [Initial pitch](https://forums.swift.org/t/pitch-observation/62051) -* Pitch 2: Previously Observation registered observers directly to `Observable`, the new approach registers observers to an `Observable` via a `ObservationTransactionModel`. These models control the "edge" of where the change is emitted. They are the responsible component for notifying the observers of events. This allows the observers to focus on just the event and not worry about "leading" or "trailing" (will/did) "edges" of the signal. Additionally the pitch was shifted from the type wrapper feature over to the more appropriate macro features. -* Pitch 3: The `Observer` protocol and `addObserver(_:)` method are gone in favor of providing async sequences of changes and transactions. +* Version 1: [Initial pitch](https://forums.swift.org/t/pitch-observation/62051) +* Version 2: Previously Observation registered observers directly to `Observable`, the new approach registers observers to an `Observable` via a `ObservationTransactionModel`. These models control the "edge" of where the change is emitted. They are the responsible component for notifying the observers of events. This allows the observers to focus on just the event and not worry about "leading" or "trailing" (will/did) "edges" of the signal. Additionally the pitch was shifted from the type wrapper feature over to the more appropriate macro features. +* Version 3: The `Observer` protocol and `addObserver(_:)` method are gone in favor of providing async sequences of changes and transactions. +* Version 4: In order to support observation for subclasses and to provide space to address design question around the asynchronous `values(for:)` and `changes(for:)` methods, the proposal now focuses on an `Observable` marker protocol and the `withTracking(_:changes:)` function. #### Suggested Reading @@ -48,7 +49,7 @@ A formalized observer pattern needs to support the following capabilities: * Marking a type as observable * Tracking changes within an instance of an observable type -* Observing and utilizing those changes from somewhere else, e.g. another type +* Observing and utilizing those changes from somewhere else In addition, the design and implementation should meet these criteria: @@ -58,185 +59,79 @@ In addition, the design and implementation should meet these criteria: * Using advanced features should progressively disclose to more complex systems * Observation should be able to handle more than one observed member at once * Observation should be able to work with computed properties that reference other properties -* Observation should be able to work with computed properties that process both get and set to external storage +* Observation should be able to work with computed properties that store their values in external storage * Integration of observation should work in transactions of graphs and not just singular objects -We propose a new standard library module named `Observation` that includes the protocols, types, and macros to implement such a pattern. +We propose a new standard library module named `Observation` that includes the required functionality to implement such a pattern. Primarily, a type can declare itself as observable simply by using the `@Observable` macro annotation: ```swift -@Observable public final class MyObject { - public var someProperty: String = "" - public var someOtherProperty: Int = 0 - fileprivate var somePrivateProperty: Int = 1 -} -``` - -The `@Observable` macro declares and implements conformance to the `Observable` protocol, which includes a set of extension methods to handle observation. The `Observable` protocol supports three ways to interact with changed properties: tracking specific values changes coalesced around iteration, tracking changes coalesced around a specific actor isolation, and interoperating with UI. The first two systems are intended for non-UI uses, while user interface libraries such as SwiftUI can use the last system to allow data changes to flow through to views. - -In the simplest case, a client can use the `values(for:)` method to observe changes to a specific property for a given instance. - -```swift -func processChanges(_ object: MyObject) async { - for await value in object.values(for: \.someProperty) { - print(value) - } -} -``` - -This allows users of `Observable` types to observe changes to specific values or an instance as a whole as asynchronous sequences of change events. Because the `values(for:)` method only observes a single property, it provides a sequence of elements of that property's type. - -```swift -object.someProperty = "hello" -// prints "hello" in the awaiting loop -object.someOtherProperty += 1 -// nothing is printed -``` - -Observable objects can also provide changes grouped into transactions, which coalesce any changes that are made between suspension points. Transactions are accessed using the `changes(for:)` method, and are delivered isolated to an actor that you provide, or the main actor by default. Because transactions can cover changes to more than one property, the `changes(for:)` method provides a sequence of `TrackedProperties` instances, which can be queried to find the changed properties. - -```swift -func processTransactions(_ object: MyObject) async { - for await change in objects.changes(for: [\.someProperty, \.someOtherProperty]) { - print(myObject.someProperty, myObject.someOtherProperty) - } +@Observable class Car { + var name: String + var awards: [Award] } ``` -Unlike `ObservableObject` and `@Published`, the properties of an `@Observable` type do not need to be individually marked as observable. Instead, all stored properties are implicitly observable. +The `@Observable` macro implements conformance to the `Observable` marker protocol and tracking for each stored property. Unlike `ObservableObject` and `@Published`, the properties of an `@Observable` type do not need to be individually marked as observable. Instead, all stored properties are implicitly observable. -For read-only computed properties, the static `dependencies(of:)` method is used to indicate additional key paths from which the property is computed. This is similar to the mechanism that KVO uses to provide additional key paths that have effects to key paths. +The `Observation` module also provides the top-level function `withObservationTracking`, which detects accesses to tracked properties within a specific scope. Once those properties are identified, any changes to the tracked properties triggers a call to the provided `onChange` closure. ```swift -extension MyObject { - var someComputedProperty: Int { - somePrivateProperty + someOtherProperty - } +let cars: [Car] = ... - nonisolated static func dependencies( - of keyPath: PartialKeyPath - ) -> TrackedProperties { - switch keyPath { - case \.someComputedProperty: - return [\.somePrivateProperty, \.someOtherProperty] - default: - return [keyPath] +@MainActor +func renderCars() { + withObservationTracking { + for car in cars { + print(car.name) + } + } onChange: { + Task { @MainActor in + renderCars() } } } ``` -Since all access to observing changes is by key path, visibility keywords like `public` and `private` determine what can and cannot be observed. Unlike KVO, this means that only members that are accessible in a particular scope can be observed. This fact is reflected in the design, where transactions are represented as `TrackedProperties` instances, which allow querying for the changed key paths, but not their iteration. - -```swift -// ✅ `someProperty` is publicly visible -object.changes(for: \.someProperty) -// ❌ `somePrivateProperty` is restricted to `private` access -object.changes(for: \.somePrivateProperty) -// ✅ `someComputedProperty` is visible; `somePrivateProperty` isn't accessible in returned `TrackedProperties` instances -object.changes(for: \.someComputedProperty) -``` +In the example above, the `render` function accesses each car's `name` property. When any of the cars change `name`, the `onChange` closure is then called on the first change. However, if a car has an award added, the `onChange` call won't happen. This design supports uses that require implicit observation tracking, ensuring that updates are only performed in response to relevant changes. ## Detailed Design -The `Observable` protocol, `@Observable` macro, and a handful of supporting types comprise the `Observation` module. As described below, this design allows adopters to use terse, straightforward syntax for simple cases, while allowing full control over the details the implementation when necessary. +The `Observable` protocol, `@Observable` macro, and a handful of supporting types comprise the `Observation` module. As described below, this design allows adopters to use a straightforward syntax for simple cases, while allowing full control over the details the implementation when necessary. -### `Observable` protocol +### `Observable` protocol -The core protocol for observation is `Observable`. `Observable`-conforming types define what is observable by registering changes and provide asynchronous sequences of transactions and changes to individual properties, isolated to a specific actor. +Observable types conform to the `Observable` marker protocol. While the `Observable` protocol doesn't have formal requirements, it includes a semantic requirement that conforming types must implement tracking for each stored property using an `ObservationRegistrar`. Most types can meet that requirement simply by using the `@Observable` macro: ```swift -protocol Observable { - /// Returns an asynchronous sequence of change transactions for the specified - /// properties. - nonisolated func changes( - for properties: TrackedProperties, - isolatedTo: Isolation - ) -> ObservedChanges - - /// Returns an asynchronous sequence of changes for the specified key path. - nonisolated func values( - for keyPath: KeyPath - ) -> ObservedValues - - /// Returns a set of tracked properties that represent the given key path. - nonisolated static func dependencies( - of keyPath: PartialKeyPath - ) -> TrackedProperties +@Observable public final class MyObject { + public var someProperty = "" + public var someOtherProperty = 0 + fileprivate var somePrivateProperty = 1 } ``` -These protocol requirements need to be implemented by conforming types, either manually or by using the `@Observable` macro, described below. The first two methods make use of `ObservationRegistrar` (*also* described below) to track changes and provide async sequences of changes and transactions. +### `@Observable` Macro -In addition to these protocol requirements, `Observable` types must implement the _semantic_ requirement of tracking each access and mutation to observable properties. This tracking is also provided by using the `@Observable` macro, or can be implemented manually using the `access` and `withMutation` methods of a registrar. +In order to make implementation as simple as possible, the `@Observable` macro automatically synthesizes conformance to the `Observable` protocol, transforming annotated types into a type that can be observed. When fully expanded, the `@Observable` macro does the following: -The `Observable` protocol also implements extension methods that provide convenient access to transactions isolated to the main actor or tracking just a single key path. +- declares conformance to the `Observable` protocol, +- adds a property for the registrar, +- and adds internal helper methods for tracking accesses and mutations. -```swift -extension Observable { - /// Returns an asynchronous sequence of change transactions for the specified - /// key path, isolated to the given actor. - nonisolated func changes( - for keyPath: KeyPath, - isolatedTo: Isolation - ) -> ObservedChanges - - /// Returns an asynchronous sequence of change transactions for the specified - /// properties, isolated to the main actor. - nonisolated func changes( - for properties: TrackedProperties - ) -> ObservedChanges - - /// Returns an asynchronous sequence of change transactions for the specified - /// key path, isolated to the main actor. - public nonisolated func changes( - for keyPath: KeyPath - ) -> ObservedChanges - - // Default implementation returns `[keyPath]`. - public nonisolated static func dependencies( - of keyPath: PartialKeyPath - ) -> TrackedProperties -} -``` +Additionally, for each stored property, the macro: -All `Observable` types must correctly handle their own exclusivity, thread safety, and/or isolation. For example, if two properties must change together, with no observation of a halfway state where one has changed and the other hasn't, then it is the type's responsibility to provide that atomicity. For example, for an observable type named `ImageDescription`, with `width` and `height` properties that must be updated in lockstep, the type must assign both properties without an interrupting suspension point: +- annotates each stored property with the `@ObservationTracked` macro, +- converts each stored property to a computed property, +- and adds an underscored, `@ObservationIgnored` version of each stored property. -```swift -@Observable class ImageDescription { - // ... - - func BAD_updateDimensions() async { - self.width = await getUpdatedWidth() - self.height = await getUpdatedHeight() - } +Since all of the code generated by the macro could be manually written, developers can write or customize their own implementation when they need more fine-grained control. - func GOOD_updateDimensions() async { - let (width, height) = await (getUpdatedWidth(), getUpdatedHeight()) - self.width = width - self.height = height - } -} -``` - -Likewise, a sequence observing a specific property for its values is only `Sendable` if `Observable` type is itself `Sendable`. - -### Macro Synthesis - -In order to make implementation as simple as possible, the `@Observable` macro automatically synthesizes conformance to the `Observable` protocol, transforming annotated types into a type that can be observed. The `@Observable` macro does the following: - -- declares conformance to the `Observable` protocol -- adds the required `Observable` method requirements -- adds a property for the registrar -- adds a storage abstraction for access tracking -- changes all stored properties into computed properties -- adds an initializer for the properties (with default values if they apply) - -Since all of the code generated by the macro could be manually written, developers can write their own implementation when they need more fine-grained control. For example, the following `Model` type is annotated with the `@Observable` macro: +As an example of the `@Observable` macro expansion, consider the following `Model` type: ```swift -@Observable final class Model { +@Observable class Model { var order: Order? var account: Account? @@ -250,371 +145,242 @@ Since all of the code generated by the macro could be manually written, develope } ``` -Expanding the macro results in the following declaration: +Expanding the `@Observable` macro, as well as the generated macros, results in the following declaration: ```swift -final class Model: Observable { +class Model: Observable { internal let _$observationRegistrar = ObservationRegistrar() - - nonisolated func changes( - for properties: TrackedProperties, - isolatedTo: Delivery - ) -> ObservedChanges { - _$observationRegistrar.changes(for: properties, isolation: isolation) - } - - nonisolated func values( - for keyPath: KeyPath - ) -> ObservedValues { - _$observationRegistrar.changes(for: keyPath) - } - - private struct _$ObservationStorage { - var order: Order? - var account: Account? - var alternateIconsUnlocked: Bool - var allRecipesUnlocked: Bool + internal func access( + keyPath: KeyPath + ) { + _$observationRegistrar.access(self, keyPath: keyPath) } - - private var _$observationStorage: _$ObservationStorage - init(order: Order? = nil, account: Account? = nil, alternateIconsUnlocked: Bool = false, allRecipesUnlocked: Bool = false) { - _$observationStorage = _$ObservationStorage(order: order, account: account, alternateIconsUnlocked: alternateIconsUnlocked, allRecipesUnlocked: allRecipesUnlocked) + internal func withMutation( + keyPath: KeyPath, + _ mutation: () throws -> T + ) rethrows -> T { + try _$observationRegistrar.withMutation(of: self, keyPath: keyPath, mutation) } - + var order: Order? { get { - _$observationRegistrar.access(self, keyPath: \.order) - return _$observationStorage.order + self.access(keyPath: \.order) + return _order } set { - _$observationRegistrar.withMutation(self, keyPath: \.order) { - _$observationStorage.order = newValue + self.withMutation(keyPath: \.order) { + _order = newValue } } } var account: Account? { get { - _$observationRegistrar.access(self, keyPath: \.account) - return _$observationStorage.account + self.access(keyPath: \.account) + return _account } set { - _$observationRegistrar.withMutation(self, keyPath: \.account) { - _$observationStorage.account = newValue + self.withMutation(keyPath: \.account) { + _account = newValue } } } var alternateIconsUnlocked: Bool { get { - _$observationRegistrar.access(self, keyPath: \.alternateIconsUnlocked) - return _$observationStorage.alternateIconsUnlocked + self.access(keyPath: \.alternateIconsUnlocked) + return _alternateIconsUnlocked } set { - _$observationRegistrar.withMutation(self, keyPath: \.alternateIconsUnlocked) { - _$observationStorage.alternateIconsUnlocked = newValue + self.withMutation(keyPath: \.alternateIconsUnlocked) { + _alternateIconsUnlocked = newValue } } } var allRecipesUnlocked: Bool { get { - _$observationRegistrar.access(self, keyPath: \.allRecipesUnlocked) - return _$observationStorage.allRecipesUnlocked + self.access(keyPath: \.allRecipesUnlocked) + return _allRecipesUnlocked } set { - _$observationRegistrar.withMutation(self, keyPath: \.allRecipesUnlocked) { - _$observationStorage.allRecipesUnlocked = newValue + self.withMutation(keyPath: \.allRecipesUnlocked) { + _allRecipesUnlocked = newValue } } } + + var _order: Order? + var _account: Account? - nonisolated static func dependencies( - of keyPath: PartialKeyPath - ) -> TrackedProperties { - [keyPath] - } - - func purchase(alternateIcons: Bool, allRecipes: Bool) { - alternateIconsUnlocked = alternateIcons - allRecipesUnlocked = allRecipes - } + var _alternateIconsUnlocked: Bool = false + var _allRecipesUnlocked: Bool = false } ``` -When a property does not have a default value, that corresponding argument in the initializer does not have a default value. This means that the following example has a macro synthesized initializer of `init(a: Int, b: Int = 3)`. +### `@ObservationTracked` and `@ObservationIgnored` macros -```swift -@Observable final class InitializationSample { - var a: Int - var b: Int = 3 -} -``` +The `Observation` module includes two additional macros that can annotate properties of observable types. The `@ObservationTracked` macro is added to stored properties by the `@Observable` macro expansion, and, when expanded, converts a stored property to a computed one with access and mutation tracking. Developers generally won't use `@ObservationTracked` themselves. + +The `@ObservationIgnored` macro, on the other hand, doesn't add anything to a source file when expanded. Instead, it acts as a marker for properties that shouldn't be tracked. The `@Observable` macro expansion adds `@ObservationIgnored` to the underscored stored properties it creates. Developers can also apply `@ObservationIgnored` to stored properties that shouldn't be included in observation tracking. -Because the memberwise initializer and backing storage are generated together, the initializer is able to initialize that storage. User-defined initializers should call the generated memberwise initializer instead of attempting to initialize the properties directly. +### Computed properties -Since macros are only syntactic transformations, all fields must have explicit type information. This restriction may be able to be lifted later when the type system grows a mechanism to detect inferred types. +Computed properties that derive their values from stored properties are automatically tracked due to their reliance on tracked properties. Computed properties that source their value from remote storage or via indirection, however, must manually add tracking using the generated `access(keyPath:)` and `withMutation(keyPath:)` methods. + +For example, consider the `AtomicModel` in the following code sample. `AtomicModel` stores a score in an `AtomicInt`, with a computed property providing an `Int` interface. The atomic property is annotated with the `@ObservationIgnored` macro because it isn't useful to track the constant value for observation. For the computed `score` property, which is the public interface of the type, the getter and setter include manually-written calls to track accesses and mutations. ```swift -@Observable final class InitializationSample { - var a: Int - var b = 3 // this will emit an error: "@Observable requires properties to have type annotations. b is missing a non-inferred type" +@Observable +public class AtomicModel { + @ObservationIgnored + fileprivate let _scoreStorage = AtomicInt(initialValue: 0) + + public var score: Int { + get { + self.access(keyPath: \.score) + return _scoreStorage.value + } + set { + self.withMutation(keyPath: \.score) { + _scoreStorage.value = newValue + } + } + } } ``` -Properties that have `willSet` and `didSet` property observations are supported. For example, the `@Observable` macro on the `PropertyExample` type here: +### `willSet`/`didSet` + +Observation is supported for properties with `willSet` and `didSet` property observers. For example, the `@Observable` macro on the `PropertyExample` type here: ```swift -@Observable final class PropertyExample { - var a: Int { +@Observable class PropertyExample { + var a = 0 { willSet { print("will set triggered") } didSet { print("did set triggered") } } - var b: Int = 0 - var c: String = "" + var b = 0 + var c = "" } ``` -...transforms the property `a` as follows: +...transforms the `a` property as follows, preserving the `willSet` and `didSet` behavior: ```swift var a: Int { get { - _$observationRegistrar.access(self, keyPath: \.a) - return _$observationStorage.a + self.access(keyPath: \.a) + return _a } set { - print("will set triggered") - _$observationRegistrar.withMutation(of: self, keyPath: \.a) { - _$observationStorage.a = newValue + self.withMutation(keyPath: \.a) { + _a = newValue } - print("did set triggered") } } -``` - -The generated implementation for the `dependencies(of:)` requirement creates a list of all of the type's stored properties, and then returns that as the set of dependencies for any computed property. For the `PropertyExample` type above, that implementation looks like this: - -```swift -nonisolated static func dependencies( - of keyPath: PartialKeyPath -) -> TrackedProperties { - let storedProperties: [PartialKeyPath] = [\.b, \.c] - switch keyPath { - case \.a: - return TrackedProperties(storedProperties + [keyPath]) - default: - return [keyPath] - } -} -``` - -The `@Observable` macro omits the implementation if one already exists, so a type can provide a more selective implementation if necessary. - -### `TrackedProperties` - -When observing changes to a type, there may be associated side effects to members that are not publicly visible. The `TrackedProperties` type allows for internal key paths to be included in a transaction without being exposed beyond their visibility. - -```swift -public struct TrackedProperties: ExpressibleByArrayLiteral, @unchecked Sendable { - public typealias ArrayLiteralElement = PartialKeyPath - - public init() - - public init(_ sequence: some Sequence>) - - public init(arrayLiteral elements: PartialKeyPath...) - - public func contains(_ member: PartialKeyPath) -> Bool - - public mutating func insert(_ newMember: PartialKeyPath) -> Bool - - public mutating func remove(_ member: PartialKeyPath) -} -extension TrackedProperties where Root: Observable { - public init(dependent: TrackedProperties) +var _a = 0 { + willSet { print("will set triggered") } + didSet { print("did set triggered") } } ``` -### `ObservationRegistrar` +### Initializers -`ObservationRegistrar` is the default storage for tracking and providing access to changes. The `@Observable` macro synthesizes a registrar to handle these mechanisms as a generalized feature. By default, the registrar is thread safe and must be as `Sendable` as containers could potentially be; therefore it must be designed to handle independent isolation for all actions. +Because observable types generally use the implicitly generated initializers, the `@Observable` macro requires that all stored properties have a default value. This guarantees definitive initialization, so that additional initializers can be added to observable types in an extension. -```swift -public struct ObservationRegistrar: Sendable { - public init() - - public func access( - _ subject: Subject, - keyPath: KeyPath - ) - - public func willSet( - _ subject: Subject, - keyPath: KeyPath - ) - - public func didSet( - _ subject: Subject, - keyPath: KeyPath - ) - - public func withMutation( - of subject: Subject, - keyPath: KeyPath, - _ mutation: () throws -> T - ) rethrows -> T - - public func changes( - for properties: TrackedProperties, - isolatedTo: Isolation - ) -> ObservedChanges - - public func values( - for keyPath: KeyPath - ) -> ObservedValues -} -``` - -The `access` and `withMutation` methods identify transactional accesses. These methods register access to the `ObservationTracking` system for access and identify mutations to the transactions registered for observers. +The default value requirement could be relaxed in a future version; see the Future Directions section for more. -### `ObservationTracking` +### Subclasses -In order to provide scoped observation, the `ObservationTracking` type provides a method to capture accesses to properties within a given scope and then call out upon the first change to any of those properties. This API is the primary mechanism in which UI interactions can be built. Specifically, this can be used by user interface libraries like SwiftUI to provide updates to properties that are accessed in a specific scope. For more detail, see the SDK Impact section below. +Developers can create `Observable` subclasses of either observable or non-observable types. Only the properties of a type that implements the `Observable` tracking requirements will be observed. That is, when working with an observable subclass of a non-observable type, the superclass's stored properties will not be tracked under observation. -```swift -public struct ObservationTracking { - public static func withTracking( - _ apply: () -> T, - onChange: @autoclosure () -> @Sendable () -> Void - ) -> T -} -``` +### `withObservationTracking(_:onChange:)` -The `withTracking` method takes two closures where any access to any property within the apply closure will indicate that property on that specific instance should participate in changes informed to the `onChange` closure. +In order to provide automatically scoped observation, the `ObservationModule` provides a function to capture accesses to properties within a given scope, and then call out upon the first change to any of those properties. This can be used by user interface libraries, such as SwiftUI, to provide updates to the specific properties which are accessed within a particular scope, limiting interface updates or renders to only the relevant changes. For more detail, see the SDK Impact section below. ```swift -@Observable final class Car { - var name: String - var awards: [Award] -} - -let cars: [Car] = ... - -func render() { - ObservationTracking.withTracking { - for car in cars { - print(car.name) - } - } onChange { - scheduleRender() - } -} +public func withObservationTracking( + _ apply: () -> T, + onChange: @autoclosure () -> @Sendable () -> Void +) -> T ``` -In the example above, the `render` function accesses each car's `name` property. When any of the cars change `name`, the `onChange` closure is then called on the first change. However, if a car has an award added, the `onChange` call won't happen. This design supports uses that require implicit observation tracking, such as SwiftUI, ensuring that views are only updated in response to relevant changes. - -### `ObservedChanges` and `ObservedValues` - -The two included asynchronous sequences provide access to transactions based on a `TrackedProperties` instance or to changes based on a key path, respectively. The two sequences have slightly different semantics. Both of these asynchronous sequences are intended for primary use by non-UI systems. +The `withObservationTracking` function takes two closures. Any access to a tracked property within the `apply` closure will flag the property; any change to a flagged property will trigger a call to the `onChange` closure. -The `ObservedChanges` sequence is the result of calling `changes(for:isolatedTo:)` on an observable type and passing one or more key paths as a `TrackedProperties` instance. The isolating actor that you pass (or the main actor, by default) determines how changes are coalesced. Any changes between suspension points on the isolating actor, whether to one property or multiple properties, only provide a single transaction event. Sequence elements are `ObservedChange` instances, which you can query to see if a _specific_ property has changed. When the observed type is `Sendable`, an `ObservedChange` also includes the observed subject, in order to simplify accessing the updated properties. +Accesses are recognized for: +- tracked properties on observable objects +- tracked properties of properties that have observable type +- properties that are accessed via computed property accesses -The `ObservedValues` sequence, on the other hand, is the result of calling `values(for:)`, passing a single key path to observe. Instead of coalescing changes in reference to an isolating actor, `ObservedValues` provides changed values that are coalesced at each suspension during iteration. Since the iterator isn't `Sendable`, that behavior implicitly isolates changes to the current actor. +For example, this `Person` class has multiple tracked properties, some of which are internal: ```swift -public struct ObservedChange: @unchecked Sendable { - public func contains(_ member: PartialKeyPath) -> Bool -} - -extension ObservedChange where Subject: Sendable { - public var subject: Subject { get } -} - -/// An asynchronous sequence of observed changes. -public struct ObservedChanges: AsyncSequence { - public typealias Element = ObservedChange - - public struct Iterator: AsyncIteratorProtocol { - public mutating func next() async -> Element? - } +@Observable public class Person: Sendable { + internal var firstName = "" + internal var lastName = "" + public var age: Int? - public func makeAsyncIterator() -> Iterator -} - -extension ObservedChanges: @unchecked Sendable where Subject: Sendable { } -@available(*, unavailable) -extension ObservedChanges.Iterator: Sendable { } - -/// An asynchronous sequence of observed changes. -public struct ObservedValues: AsyncSequence { - public struct Iterator: AsyncIteratorProtocol { - public mutating func next() async -> Element? + public var fullName: String { + "\(firstName) \(lastName)" } - public func makeAsyncIterator() -> Iterator + public var friends: [Person] = [] } - -extension ObservedValues: @unchecked Sendable where Subject: Sendable { } -@available(*, unavailable) -extension ObservedChanges.Iterator: Sendable { } ``` - -`ObservedChanges` emits values when a change is made and an available suspension point has been met by the isolation. This means that changes can be grouped up transactionally to the boundary of when a given actor is available to run. Common use cases for this will iterate this `AsyncSequence` on the same actor as it is claiming for isolation. +Accessing the `fullName` and `friends` properties will result in the `firstName`, `lastName`, and `friends` properties being tracked for changes: ```swift -@Observable final class ChangesExample { - @MainActor var someField: Int - @MainActor var someOtherField: String -} - @MainActor -func handleChanges(_ example: ChangesExample) { - Task { @MainActor in - for await change in example.changes(for: [\.someField, \.someOtherField]) { - switch (change.contains(\.someField), change.contains(\.someOtherField)) - case (true, true): - print("Changed both properties") - case (true, false): - print("changed integer field") - case (false, true): - print("changed string field") - default: - fatalError("this case will never happen") - } +func renderPerson(_ person: Person) { + withObservationTracking { + print("\(person.fullName) has \(person.friends.count) friends.") + } onChange: { + Task { @MainActor in + renderPerson(person) } } } ``` -The propagation of the `@MainActor` serves two purposes; to allow for access to the subject of observation safely and to coalesce changes around that same actor. If the subject of observation is not `Sendable` then the task creation will emit a concurrency warning that the item is being sent across an actor boundary. This also applies to the `ObservedChanges` `AsyncSequence` since the `Sendable` conformance is conditional upon the subject of observation's conformance to `Sendable`. This means that the potential of misuse of iterating changes on a different actor emits a warning (and in future Swift releases this will be an error). +Whenever the person's `firstName` or `lastName` properties are updated, the `onChange` closure will be called, even though those properties are internal, since their accesses are linked to a public computed property. Mutations to the `friends` array will also cause a call to `onChange`, though changes to individual members of the array are not tracked. -The mechanism to coalesce is based upon the suspension provided by the isolation. When changes are being iterated the tracked properties of the given `Observable` are gathered until a continuation is resumed from the suspension of the isolation. This means that the boundary of the collection starts at the leading edge of any given set of changes (willSet) with an ending of the transaction gated by the suspension of the isolating actor. If the scope of the iteration of `changes` is bound to a given actor like the example above it means that those changes are coalescing for that previous region. Provided the subject of observation is not violating actor isolation then the values should reflect the next available slot within the scheduling of that actor. In short; given the main actor, the changes that occur will be gathered up until the next tick of the run loop and usable for that object in that asynchronous context. +### `ObservationRegistrar` -The `ObservedValues` asynchronous sequence on the other hand will await a change to values end emit the latest value that is available between the last iteration (or starting point of iteration) and the suspension. Similar to the requirement of `Sendable` to `ObservedChanges` the `ObservedValues` async sequence is conditional; meaning that only cases where the observable subject is `Sendable` can the `AsyncSequence` of values be `Sendable`. However there is one extra requirement; that the type of the value being observed must also be `Sendable`. +`ObservationRegistrar` is the required storage for tracking accesses and mutations. The `@Observable` macro synthesizes a registrar to handle these mechanisms as a generalized feature. By default, the registrar is thread safe and must be as `Sendable` as containers could potentially be; therefore it must be designed to handle independent isolation for all actions. ```swift -@Observable final class ValuesExample { - @MainActor var someField: Int -} - -@MainActor -func processValues(_ example: ValuesExample) { - Task { @MainActor in - for await value in example.values(for: \.someField) { - print(value) - } - } +public struct ObservationRegistrar: Sendable { + public init() + + public func access( + _ subject: Subject, + keyPath: KeyPath + ) + + public func willSet( + _ subject: Subject, + keyPath: KeyPath + ) + + public func didSet( + _ subject: Subject, + keyPath: KeyPath + ) + + public func withMutation( + of subject: Subject, + keyPath: KeyPath, + _ mutation: () throws -> T + ) rethrows -> T } ``` -In the example above if the `someField` property is changed in succession on the main actor without a suspend in between the awaiting continuation cannot resume in-between the values being set. This means that only the latest value is emitted. If the iteration is being done in a different actor the changes that might occur in-between calls to the iterator's next function will also be coalesced. - -It is worth noting that making a type observable does not provide any automatic atomicity of individual values or groups of values. Changes as such need to be managed by the implementor of the observed type. That management is one of the responsibilities that types need to account for when declaring conformance to `Sendable`. +The `access` and `withMutation` methods identify transactional accesses. These methods register access to the underlying tracking system for access and identify mutations to the transactions registered for observers. ## SDK Impact (a preview of SwiftUI interaction) @@ -623,7 +389,7 @@ When using the existing `ObservableObject`-based observation, there are a number The following is adapted from the [Fruta sample app](https://developer.apple.com/documentation/swiftui/fruta_building_a_feature-rich_app_with_swiftui), modified for clarity: ```swift -final class Model: ObservableObject { +class Model: ObservableObject { @Published var order: Order? @Published var account: Account? @@ -664,21 +430,21 @@ The `@Published` attribute identifies each field that participates in changes in The proposed API not only reduces the `@Published` repetition, but also simplifies the SwiftUI view code too! With the proposed `@Observable` macro, the previous example can instead be written as the following: ```swift -@Observable final class Model { +@Observable class Model { var order: Order? var account: Account? var hasAccount: Bool { - return userCredential != nil && account != nil + userCredential != nil && account != nil } var favoriteSmoothieIDs: Set = [] var selectedSmoothieID: Smoothie.ID? - var searchString: String = "" + var searchString = "" - var isApplePayEnabled: Bool = true - var allRecipesUnlocked: Bool = false + var isApplePayEnabled = true + var allRecipesUnlocked = false var unlockAllRecipesProduct: Product? } @@ -724,15 +490,11 @@ This API will be housed in a module that is part of the Swift language but outsi ## Future Directions -The initial implementation will not track changes for key paths that have more than one layer of components. For example, key paths such as `\.account` would work, but `\.account.name` would not. This feature would be possible as soon as the standard library offers a mechanism to iterate components of a key path. Since there is no way to determine this yet, key paths that have more than one component will never observe any changes. +The requirement that all stored properties of an observable type have initial values could be relaxed in the future, if language features are added that would support that. For example, property wrappers have a feature that allows their underlying wrapped value to be provided in an initializer rather than as a default value. Generalizing that feature to all properties could allow the `@Observable` macro to enable a more typical initialization implementation. Another area of focus for future enhancements is support for observable `actor` types. This would require specific handling for key paths that currently does not exist for actors. -The current requirement that all stored properties declarations include a type could be lifted in the future, if macros are able to provide semantic type information for properties. This would allow property declarations like `var a = 3`. - -Future macro features could potentially permit refinements to the `dependencies(of:)` implementation generated by the `@Observable` macro, to more accurately select the stored properties that each computed property depends on. - -Finally, once variadic generics are available, an observation mechanism could be added to observe multiple key paths as an `AsyncSequence`. +An earlier version of this proposal included asynchronous sequences of coalesced transactions and individual property changes, named `values(for:)` and `changes(for:)`. Similar invariant-preserving asynchronous sequences could be added in a future proposal. ## Alternatives considered From c1ac29ae20e8ac76222bc109b46b2354af1cdfb9 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 30 May 2023 17:20:57 -0400 Subject: [PATCH 3158/4563] Accept SE-0399 (#2065) --- proposals/0399-tuple-of-value-pack-expansion.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0399-tuple-of-value-pack-expansion.md b/proposals/0399-tuple-of-value-pack-expansion.md index fcb8878a68..96e9b2c922 100644 --- a/proposals/0399-tuple-of-value-pack-expansion.md +++ b/proposals/0399-tuple-of-value-pack-expansion.md @@ -3,10 +3,10 @@ * Proposal: [SE-0399](0399-tuple-of-value-pack-expansion.md) * Authors: [Sophia Poirier](https://github.com/sophiapoirier), [Holly Borla](https://github.com/hborla) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (May 16...29, 2023)** +* Status: **Accepted** * Implementation: On `main` gated behind `-enable-experimental-feature VariadicGenerics` * Previous Proposals: [SE-0393](0393-parameter-packs.md), [SE-0398](0398-variadic-types.md) -* Review: ([pitch](https://forums.swift.org/t/tuple-of-value-pack-expansion/64269)) ([review](https://forums.swift.org/t/se-0399-tuple-of-value-pack-expansion/65017)) +* Review: ([pitch](https://forums.swift.org/t/tuple-of-value-pack-expansion/64269)) ([review](https://forums.swift.org/t/se-0399-tuple-of-value-pack-expansion/65017)) ([acceptance](https://forums.swift.org/t/accepted-se-0399-tuple-of-value-pack-expansion/65271)) ## Introduction From b5e796cb36a9a8e3ed7bc385b0891770c8b0069a Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 25 May 2023 22:12:13 -0400 Subject: [PATCH 3159/4563] Another editorial / clarification pass --- visions/using-c++-from-swift.md | 111 ++++++++++++++++++++------------ 1 file changed, 70 insertions(+), 41 deletions(-) diff --git a/visions/using-c++-from-swift.md b/visions/using-c++-from-swift.md index ac6561a3d7..e13b8f0ef5 100644 --- a/visions/using-c++-from-swift.md +++ b/visions/using-c++-from-swift.md @@ -57,19 +57,19 @@ Because of the difference in idioms between the two languages, and because of th ## The approach -Many C++ constructs have a clear, analogous mapping in Swift. These constructs can be easily and automatically imported to their corresponding Swift constructs. For example: C++ operators can usually be mapped to similar Swift operators. Sometimes, to promote Swift’s idioms, operators should be imported semantically rather than directly. For example, `operator++` should map to a `successor` method in Swift and `operator*` should map to a `pointee` property to promote clarity and uphold Swift’s strong idioms. Another example is namespaces which can be mapped to empty Swift enums, a common pattern in Swift. And enums, even class enums, can be mapped trivially to Swift enums. +Many C++ constructs have a clear, analogous mapping in Swift. These constructs can be easily and automatically imported to their corresponding Swift constructs. For example, C++ `enum`s and `enum class`es can be mapped to Swift `enum`s, and C++ operators can usually be mapped to similar Swift operators. Sometimes, to promote Swift’s idioms, operators should be imported "semantically" rather than directly. For example, `operator++` should map to a `successor` method in Swift, and `operator*` should map to a `pointee` property. Another example is a C++ `namespace`, which can be mapped to an empty `enum`, a common pattern in Swift. -Swift and C++ both support object-oriented programming, allowing objects and members to be expressed easily. For the most part, these objects and their members can be mapped trivially. A member function in C++ translates to a method in Swift, and so on. Swift and C++ not only support object-oriented programmer, but also promote value semantics, providing a clear default for importing C++ classes and structs. Swift can express C++ objects as `struct`s or “value types” where copying, moving, and destroying the value invokes one of the C++ type’s special members: copy constructor, move constructor, or destructor. This default for importing C++ types fits nicely into Swift’s existing object model, and allows most types to automatically work in Swift in an idiomatic way that feels native. +Swift and C++ both support object-oriented programming, and most C++ object-oriented features can be mapped trivially onto Swift counterparts: a member function in C++ translates to a method in Swift, and so on. Like Swift, C++ programming is often focused around types with value semantics, and the natural default when importing a C++ `class` or `struct` is to map it to a Swift `struct`. Copying and destroying this `struct` can simply invoke the corresponding C++ special members as appropriate. This fits nicely into Swift’s existing object model and allows most types to automatically work in Swift in an idiomatic, natural way. -Unfortunately, not all C++ types are value types, and while Swift expresses value categories explicitly, C++ does not. The example above (in the goals section) shows how the basic tools provided by C++ are often used in idiomatically different ways. This poses a conundrum for importing C++ APIs automatically into Swift which extends far past value categories. A more fundamental place to see this is with memory management. +However, not all C++ `struct`s and `class`es are intended to be used as value types. The `StatefulObject` example in the Goals section shows how the basic tools provided by C++ are often used in idiomatically different ways, and this can be hard to automatically detect when importing the type. This is one example of a deeper conundrum when importing C++ APIs. A more fundamental place to see this is with memory management. -In Objective-C, it's fairly straightforward for ARC to ensure that data is valid when it's used. Almost all data in Objective-C is represented with either a fundamental type (such as `double` or `BOOL`) or a reference-counted object type (such as `NSString *`). Values of fundamental types can be safely copied around, and reference-counted objects can be "managed" by retaining them, extending their lifetime without changing the semantics of the program. It's rare to work with an unmanaged pointer in Objective-C, and even rarer to work with an unmanaged pointer that has a dependency on a managed object (although exceptions do exist, such as [`NSData`'s `-bytes` method](https://developer.apple.com/documentation/foundation/nsdata/1410616-bytes?language=objc)). This kind of dependency is problematic for safe memory management because the language often does not know about it and cannot ensure that the backing object stays valid while the pointer is being used. Swift's Objective-C interop has treated these as special cases (with an attribute) and dealt with them individually; while this solution is imperfect in several ways, its rarity has made it a low priority to fix. +In Objective-C, it's fairly straightforward for ARC to ensure that data is valid when it's used. Almost all data in Objective-C is represented with either a fundamental type (such as `double` or `BOOL`) or a reference-counted object type (such as `NSString *`). Values of fundamental types can be safely used without any concern about memory management, and reference-counted objects can be safely managed by ensuring that they're retained while they're still potentially used. The compiler will sometimes be more conservative about reference counts than a human would be, extending the lifetime of an object longer than is strictly necessary, but this usually doesn't change the semantics of the program. ARC doesn't manage C pointers, but it's relatively rare to work with C pointers in Objective-C, and it's rarer still to work with a C pointer that has a dependency on a managed object (although exceptions do exist, such as [`NSData`'s `-bytes` method](https://developer.apple.com/documentation/foundation/nsdata/1410616-bytes?language=objc)). This kind of dependency is problematic for safe memory management because the language often does not know about it and cannot ensure that the backing object stays valid while the pointer is being used. Swift's Objective-C interop has treated these as special cases (with an attribute) and dealt with them individually; while this solution is imperfect in several ways, its rarity has made it a low priority to fix. -In contrast, it's very common for C++ APIs to work with unmanaged pointers, references, and views into other objects. The lifetime rules for using these correctly are inconsistent and sometimes unique to an API. As an example, consider three values: a value of type `std::vector`, a reference returned from that vector's subscript operator, and an iterator returned from that vector's `begin()` method. At first glance, these values look similar to the compiler: they are all either pointers or class objects containing pointers. But each has its own semantics and expected use (especially concerning lifetime), and these differences are not conveyed explicitly in the source. The vector is a value type that can be copied, but copies can be expensive, and iterators and references into the vector are only valid for a specific copy. The result of the subscript operator is a mutable projection of a specific element, dependent on the vector for validity; but the value of that element can be copied out of the reference to get an independent value, and that is often how the subscript operator is used. The iterator is also a projection, dependent on the vector for validity, but it must be used in conjunction with other iterators or with the vector itself in certain careful ways, and some operations will invalidate it completely. +In contrast, it's very common for C++ APIs to work with unmanaged pointers, references, and views into other objects. The lifetime rules for using these correctly are inconsistent and sometimes unique to an API. As an example, consider three values: a value of type `std::vector`, a reference returned from that vector's `operator[]`, and an iterator returned from that vector's `begin()` method. At first glance, these values look similar to the compiler: they are all either pointers or class objects containing pointers. But each has its own semantics and expected use (especially concerning lifetime), and these differences are not conveyed explicitly in the source. The vector is a value type that can be copied, but copies can be expensive, and iterators and references into the vector are only valid for a specific copy. The result of `operator[]` is a mutable projection of a specific element, dependent on the vector for validity; but the value of that element can be copied out of the reference to get an independent value, and that is often how the operator is used. The iterator is also a projection, dependent on the vector for validity, but it must be used in conjunction with other iterators or with the vector itself in certain careful ways, and some operations will invalidate it completely. -So there is a conundrum where superficially similar language constructs in C++ are used to express idiomatic patterns that are vastly different in their impact. The only viable approach for addressing this conundrum is to pick off these patterns one at a time. The Swift compiler will know about many possible C++ API patterns. If a C++ API has semantic annotations telling Swift that it follows a certain pattern, Swift will try to create a Swift interface for it following the rules of that pattern. In the absence of those annotations, Swift will try to use huristics to recognize an appropriate pattern. If this fails, Swift will make the API unavailable. +So there is a conundrum where superficially similar language constructs in C++ are used to express idiomatic patterns that are vastly different in their impact. The only viable approach for addressing this problem is to pick off these patterns one at a time. The Swift compiler will know about many possible C++ API patterns. If a C++ API has semantic annotations telling Swift that it follows a certain pattern, Swift will try to create a Swift interface for it following the rules of that pattern. In the absence of those annotations, Swift will try to use huristics to recognize an appropriate pattern. If this fails, Swift will make the API unavailable. -Consider how this applies to the `std::vector` example. `std::vector` maps over well as a Swift value type. Its subscript operator can be imported as a Swift `subscript`, and the importer can take advantage of the fact that it returns a reference to allow elements to be efficiently borrowed. And while C++ iterators in general pose serious lifetime safety problems in Swift, Swift can recognize the common `begin()`/`end()` pattern and import it as a safe Swift iterator that encapsulates the unsafety internally. The following sections will go into detail explaining how each of these specific API patterns can be picked off of a C++ codebase. +Consider how this applies to the `std::vector` example. `std::vector` maps over well as a Swift value type. Its `operator[]` can be imported as a Swift `subscript`, and the importer can take advantage of the fact that it returns a reference to allow elements to be efficiently borrowed. And while C++ iterators in general pose serious lifetime safety problems in Swift, Swift can recognize the common `begin()`/`end()` pattern and import it as a safe Swift iterator that encapsulates the unsafety internally. The following sections will go into detail explaining how each of these specific API patterns can be recognized in a C++ codebase. ### Importing types @@ -79,7 +79,7 @@ Types in C++ do not always fit cleanly into this model, and they cannot always b ### Reference types -Reference types generally fit well into the existing Swift model, and there is little need to restrict them. The safety properties of managed reference types imported from C++ are generally similar to Swift's own class types and imported Objective-C class types. The design below also includes unmanaged reference types, which are less safe than managed types, but not more unsafe than writing the code in C++. Overall, this allows C++ interoperability to offer a clear, native-feeling mapping for several common C++ API patterns. +Reference types generally fit well into the existing Swift model, and there is little need to restrict them. The safety properties of managed reference types imported from C++ are generally similar to both Swift's own classes and classes imported from Objective-C. The design below also includes unmanaged reference types, which are less safe than managed types, but not more unsafe than writing the code in C++. Overall, this allows C++ interoperability to offer a clear, native-feeling mapping for several common C++ API patterns. #### Criteria for importing as a reference type @@ -123,27 +123,43 @@ Swift doesn't have to force programmers to pick one of these patterns specifical ### Value types -Value types have value semantics, that is, they can be copied and destroyed. Each instance of the type is a separate copy of the object, rather than a reference to the underlying storage. Swift expresses value types using structs which have the same behavior as C struct. C++ structs also have this behavior by default, but can be custimized to have more complicated lifetime operations, often via custom copy constructors. Custom copy operations are often used to manage storage in C++. While value types with custom copy operations fit into the Swift value type model at a high level, these types are actually novel to Swift. In Swift there is no way to define a custom copy operation and managed storage ususually has long, stable lifetimes. This is in contrast to C++ value types which may have a short lifetime, where storage is associated with an individual copy of the object. To accomidate this novelty, value types must be broken down into three categories. These categories are largely opaque to users of interop, but are essential to describing the interop story, safety and performance properties, potential API restrictions, and the user model more generally. +For the purposes of this document, a value type is any type that doesn't make sense to import as a reference type. Value types can be copied and destroyed, and the copies will be independent from each other, at least at the direct level. That is, part of the value of a value type might be a reference to an object (e.g. if it has a stored property of class type), and different copies of the value will share the same reference, but this reference can still be replaced in one copy without affecting other copies. -#### Simple data types +Swift expresses value types using `struct`s and `enum`s. Copying a Swift `struct` normally does the same thing that copying a C or C++ `struct` does by default: it recursively copies all of the stored properties of the type. In C++, of course, that behavior can be customized with user-defined copy/move constructors and destructors; while Swift doesn't have an equivalent feature, it does still honor the operations specified in C++, so that destroying a value of the imported type in Swift calls the C++ destructor and so on. + +It's useful to call out three categories of value types. These categories don't necessarily change how the type is actually used in Swift, but they are essential to describing the interop story, safety and performance properties, potential API restrictions, and the user model more generally. -This document will refer to C++'s trivially-copyable value types that do not hold pointers as “simple data types.” These types include primitive types such as integers and types which are composed of other simple data types. Simple data types are “owned” types that provide trivial lifetime operations: a copy is a copy of their bits and a destroy is a no-op. Simple data types have roughly the same mapping throughout Swift, C, Objective-C, and C++ making them trivial to import. Simple data types, their instances, methods on simple data types, and other APIs that use simple data types are generally considered to be safe and usable. +#### Simple data types -**View types** +This document will refer to C++'s trivially-copyable value types that do not contain pointers as “simple data types.” This category includes fundamental types, such as integers and floating-point types, as well as aggregate types composed only of other simple data types. Simple data types have trivial value operations and never carry lifetime dependencies on other values. Simple data types and operations on them generally don't need any special restrictions in Swift. -This document will refer to trivially-copyable value types that hold pointers as “view types.” These types include pointers themselves and types which are composed of any other view types (potentially including other types as well). The pointers held by view types refer to memory that is *not owned* by the pointer type (making view types a “view” or “projection” into memory). While view types are very similar to simple data types with respect to their trivial lifetime operations and the fact that they map similarly in these four language, they differ in the fact that while they themselves are not inherently unsafe, they may be used in unsafe APIs (discussed later). +Swift will assume by default that lifetime dependencies aren't carried in integer types even though technically pointers can be reinterpreted into integer types. It's very uncommon for C++ APIs to violate this assumption: reinterpeting pointers as integers is important to a fair amount of code, but usually it's localized and transient and the pointer doesn't get passed around as an integer long-term. #### Self-contained types -This category of types subsumes trivial types to include types with non-trivial members and custom lifetime operations. These types might be "view types" except for the fact that self-contained types *do own* the memory that their members point to. C++ often uses copy constructors and destructors to manage the lifetime of self-contained types. Therefore, the Swift compiler should assume that view types with custom copy constructor and destructors own their memory. Unfortuantly, this is not always the case. Types like `std::vector` have these custom lifetime operations, but do not own their storage. For these cases, Swift must provide annotations that allow the default to be corrected. +This category is a superset of the simple data types which also includes types with internally-managed pointers. Like simple data types, these types and their operations usually don't need any special restrictions in Swift. However, the fact that they can be non-trivial types can complicate some things. + +Swift will assume by default that a C++ `class` type which contains pointers but also provides user-defined special members is self-contained. This is a fairly reliable heuristic, but more consideration may be required in order to handle cases such as `std::vector`, which can carry lifetime dependencies indirectly even though it does manage the pointers it stores directly. In any case, Swift must provide annotations that allow the default to be corrected. + +#### View types -### Other projections +This document will refer to value types that are not self-contained types as "view types". These types include pointers themselves as well as types which are recursively composed of other view types. The pointers held by view types refer to memory that is *not owned* by the pointer type (making view types a “view” into that memory rather than a value that encapsulates it). View types usually carry a dependency on some other value and must be used carefully to be safe. -Value types that own memory through custom lifetime operations do not natively exist in Swift today. Any value types that own memory in Swift do so transatively through reference types that have long, stable lifetimes. Because Swift was not built around this kind of value type with short lifetimes and deep copies, dealing with projections of these owned types can be dangerous. This pattern is, up until now, foreign to Swift, so there are no existing tools that allow users to control this behavior or improve saftey. The best model for handling these potentially unsafe APIs is unclear; maybe most projects can be represented using generalized accessors, maybe most projects can be represented as iterators, maybe some projections should not be projections at all (and rather imported as values that are copied), most likely the answer is some combination of these. The best approach for handling projections will be revealed over time as evolution posts propose potential solutions, such as the iterator bridging described below, and as users of interop provide feedback. +While trivially-copyable view types are very similar to simple data types with respect to their trivial value operations, they differ in the fact that, while they themselves are not inherently unsafe, they may be used in unsafe APIs (discussed later). -Besides the dissonant semantic models for representing projections, the bredth of ways to define projections in C++ will prove a challenege for importing this API pattern. +### Projections -Consider the following API which returns a vector of pointers: +The safety problem posed by view types is very broad. If we apply this categorization to the types offered natively by Swift, most types are self-contained; only the unsafe pointer types are view types. Swift also encourages view types to be encapsulated within types and exposed in only carefully-scoped ways, like with a `with...` API. These properties are not true for many C++ APIs, which offer a broad spectrum of novel view types and ways to project them out of managed types. Swift does not currently offer strong language tools for dealing with these projections. + +Probably the most common pattern of projection in C++ APIs is a method that returns a reference or pointer to memory that depends on `this`. Consider this example: + +``` +const std::string &getName() const { return this->_name; } +``` + +There are several possibilities for dealing with this pattern. For example, Swift could wrap it in a `_read` accessor, implicitly encoding that the reference is only available during an access to the containing object. Swift could also add explicit lifetime-dependency features, allowing this to be treated as a return of a borrowed value. Alternatively, Swift could simply force the return value to be immediately copied after return, as if the call actually returned an owned value. It's unclear which of these would be the best approach; perhaps a combination would. This is something that will need to be investigated over time, incorporating the experience of the community with using this feature. + +But there are also many projections in C++ that don't match the above pattern. Consider the following API which returns a vector of internal pointers: ``` std::vector OwnedType::projectsInternalStorage(); ``` @@ -153,14 +169,16 @@ Or this API which fills in a pointer that has two levels of indirection: void VectorLike::begin(int **out) { *out = data(); } ``` -Or even this global function that projects one of it's parameters: +Or even this global function that projects one of its parameters: ``` int *begin(std::vector *v) { return v->data(); } ``` -It may be convient for Swift to assume that all projects follow one, unique pattern: a method of an owned type that returns a pointer. However, that is certainly not the only way in which a projection can be created. This is but one of the many places where Swift will need to decide between expressiveness and safety. Allowing the above APIs to be imported would allow interop to be more usable by default. Taking the first example, most of the time, when a vector holds pointers, those pointers do not point to storage with a short lifetime. Making this API unavailable would ensure 100% safety on the pain usability in the 99% case, when this API is safe. The tradeoffs here are an open question for the Swift evolution process to eventually determine. In any case, it is essential that C++ interoperability makes certain assumptions about the APIs Swift imports. There will always be an edge case that cannot be covered or does not make sense to accomidate. Interop as a whole should not become unusable so that these edge cases can be accomidated, or worse yet, so that that neither safe nor unsafe APIs are available in Swift. So, C++ interoperability should make reasonable (not conservative) assumptions when importing APIs. +Swift will need to decide how to handle projections, and more generally the use of view types, that it doesn't recognize how to make safe. This may come with difficult trade-offs between usefulness and safety. Consider the `projectsInternalStorage` API above, and pretend that we aren't able to recognize a pattern here --- we don't know that the pointers just depend on the `OwnedType` object. Often, uses of this kind of API can be made safe in practice: when there's a `std::vector` of pointers, there's probably some straightforward thing making those pointers valid. If Swift decides not to import this API just because it might be used unsafely, that could do serious damage to the usability of C++ interop. At the very least, it should be possible to wrap such an API in a safer Swift abstraction, which will be impossible if it isn't imported at all. + +The trade-offs here are an open question for the Swift evolution process to eventually determine. - ### Iterators +### Iterators Both Swift and C++ have powerful libraries for algorithms and iterators. The standard C++ iterator API interface lends itself to the Swift model, allowing C++ iterators and ranges to be mapped to Swift iterators and sequences with relative ease. These mapped APIs are idomatic, native Swift iterators and sequences; their semantics match the rest of the Swift language and Swift APIs compose around them nicely. By taking on Swift iterator semantics, iterators that are imported in this way are able to side-step most or all of the issues that other projects have (described above). @@ -168,44 +186,55 @@ Swift's powerful suite of algorithms match and go beyond the standard library al ### Mutability -Mutability is a concept where Swift and C++ diverge: by default C++ values are mutable and even when a value is const in C++, it is easily and often ignored (cast away). C++ often overloads functionality on a value’s constness, harming code-clarity. The generally weak notion of mutability in C++ leads to few codebases fully and strictly utilizing the `const` keyword. Swift, on the other hand, enforces mutability strictly and by default. In Swift, mutability conveys semantics, so importing largely mutable C++ codebases can lead to confusion. Swift should encourage C++ codebases to adopt const on methods and values that are used in Swift and expect that C++ does not mutate values that are marked as `const`. And overloaded functions should be clearly be disambiguated in Swift through naming. Mutability is a place where programmers may need to intervene and provide Swift with more information to help promote idiomatic APIs that are expressive and feel natural in Swift. As discussed in the "Views" section, the Swift compiler must make assumptions about the C++ APIs that it is importing, and mutability is another place where Swift will need to make reasonable (not conservative) assumptions about the APIs that it is importing, promoting C++‘s weak notion of `const` to Swift’s much stricter ideal. +Swift and C++ use very different models for controlling mutability. C++ defaults to treating methods, pointers, and references as non-`const`, and any `const`-ness can be easily cast away, which together mean that `const`-ness is not always a very reliable signal. C++ also encodes mutability into the type system in a first-class way, allowing functions to be overloaded bsaed on whether an argument is `const` or not. These decisions don't always align well with Swift, which defaults to immutability and relies on local information to strictly enforce it. + +One consequence is that C++ codebases which haven't adopted `const` correctness can be confusing to awkward to use from Swift because many operations which are not actually mutating appear to require mutability. Swift should encourage C++ codebases to adopt `const` on methods and values that are used in Swift. + +Overloaded functions should be clearly be disambiguated in Swift through naming. Mutability is a place where programmers may need to intervene and provide Swift with more information to help promote idiomatic APIs that are expressive and feel natural in Swift. + +Swift should also assume that C++ will not mutate values through `const` pointers and references, even though technically `const`-ness can cast away. It is reasonable for Swift to assume that C++ APIs will obey their type signatures, and the alternative would be very onerous for interop users. -By using Swift's strong notion of mutability, programmers will see the benefits (especially improved safety) immediately. For example, +As discussed in the "View types" section, the Swift compiler must make assumptions about the C++ APIs that it is importing, and mutability is another place where Swift will need to make reasonable (not conservative) assumptions about the APIs that it is importing, promoting C++‘s weak notion of `const` to Swift’s much stricter ideal. + +Programmers will see some benefits from Swift's stronger mutability model immediately. Consider this example: ``` -void alias(const std::string& c, std::string& m) { - auto &noConst = const_cast(c) - m = std::move(noConst); +// C++ +void append_n_times(std::string& s, const std::string& m, size_t n) { + for (size_t i = 0; i < n; ++i) + s += m; } -``` -`m` will likely be empty if `c` and `m` alias in C++ (because the empty, moved-from string is used to initialize `m`). However in Swift, `c` is guaranteed to be a distinct, truely immutable reference (to uphold Swift's strict safety model). So calling this function in Swift will never assert: -``` -var local: std.string = "Hello, World" -alias(local, &local) + +// Swift +var local: std.string = "a" +append_n_times(&local, local, 5) ``` +`append_n_times` misbehaves if `s` and `m` alias because `m` will be modified by the append: `s` will contain `2^n` copies of the original string instead of `n + 1`. This is not possible when called from Swift because Swift does not permit mutable arguments to be aliased, so the argument for `m` will be copied. (And if the programmer requests that this copy not happen, e.g. by explicitly borrowing that argument, the call will be statically diagnosed as ill-formed.) ### Computed properties -Value categories and mutability may require user input to map correctly in Swift, but constructs like iterators and getters and setters, can largely be imported automatically. Getters, setters, and subscripts can all be imported into Swift as computed properties. While many C++ codebases define a getter and setter pair, computed properties are the idiomatic way to handle this API pattern in Swift. And computed properties are not just about syntax, they also help promote safety and performance. For example, a C++ getter that returns a reference can be mapped into a generalized accessor in Swift that leverages coroutines to safely yield out its storage to the caller. This generalized accessor pattern allows safe and efficient access to C++ references in Swift and is another example of the more general philosophy for importing APIs: when Swift understands the semantics of an API it can map that API pattern to a strict Swift idiom that is safe, performant, and feels native, so that users get most of the benefits of Swift, even when calling C++ APIs. +Value vs. reference types and mutability may require user input to map correctly in Swift, but constructs like iterators, getters, and setters can largely be imported automatically. Getters, setters, and subscripts can all be imported into Swift as computed properties. While many C++ codebases define a getter and setter pair, computed properties are the idiomatic way to handle this API pattern in Swift. And computed properties are not just about syntax, they also help promote safety and performance. For example, a C++ getter that returns a reference can be mapped into a generalized accessor in Swift that leverages coroutines to safely yield out its storage to the caller. This generalized accessor pattern allows safe and efficient access to C++ references in Swift and is another example of the more general philosophy for importing APIs: when Swift understands the semantics of an API it can map that API pattern to a strict Swift idiom that is safe, performant, and feels native, so that users get most of the benefits of Swift, even when calling C++ APIs. ### Templates and generic APIs -C++ and Swift completely different models for generic programming. C++ templates provide textual specializations of functions and classes that are type checked after being specialized while Swift generics are type checked ahead of time, or separately, and based around APIs rather than textual substituion. The difference makes using generic C++ APIs in Swift difficult. Bridging C++ templates to the Swift model would require substantial engineering effort and the user's guidance. Even if this work was done and users were willing to annotate their C++ libraries sufficently, the Swift model may cause tention with C++ APIs that were designed with specific performance semantics in mind (for example, unboxing every element of a `vector` when performing a copy). +C++ and Swift use very different models for generic programming. C++ templates are eagerly instantiated for each set of template arguments they're used with, with type checking done separately for each instantiation based on the exact types in use. In contrast, Swift generics are type-checked once based on the requirements they impose on their type parameters, and while they can be specialized for a particular set of type arguments, that is not required or even always possible. + +This difference makes using generic C++ APIs in Swift difficult. Generic code in Swift will not be able to use C++ templates generically without substantial new language features and a lot of implementation work. Allowing C++ templates to be used on concrete Swift types is theoretically more feasible but still a major project because of the *ad hoc* nature of type constraints in templates. If this feature is ever pursued, it may require substantial user guidance, such as the thorough adoption of [C++20 concepts constraints](https://en.cppreference.com/w/cpp/language/constraints) on any constrained template used from Swift. -Swift protocols allow C++ types to be used in a generic context in Swift. Users can extend concrete C++ types, or concrete specializations of C++ class templates to conform to a protocol. Swift could provide a tool (likely in the form of an annotation) that allows users to even conform un-specialized templates to Swift protocols. This level of generic programming may be sufficent for most C++ interop users and would allow the Swift compiler to side-step one of the most difficult and complicated parts of C++ interoperability. +Fortunately, these limitations do not apply when using C++ types with Swift generics. Unconstrained generics can be used with C++ types without any further work, and programmers can simply add protocol conformances to concrete C++ types in order to use them with constrained generics. Swift could even provide a tool (likely in the form of an annotation) that allows users to give conformances for all specializations of a template. This level of generic programming may be sufficient for most C++ interop users, and it would allow the Swift compiler to side-step one of the most difficult and complicated parts of C++ interoperability. [This forum post](https://forums.swift.org/t/bridging-c-templates-with-interop/55003) (Bridging C++ Templates with Interop) goes into depth on the issue of importing C++ templates into Swift. ## The standard library -Swift should provide an overlay for the C++ standard library to assist in the import of commonly used APIs (such as containers). This overlay should also provide helpful bridging utilites such as protocols that map imported ranges and iterators and explicit conversions from C++ types to standard Swift types. +Swift should provide an overlay for the C++ standard library to assist in the import of commonly used APIs, such as containers. This overlay should also provide helpful bridging utilites, such as protocols for handling imported ranges and iterators, or explicit conversions from C++ types to standard Swift types. -C++ aims to provide sufficent tools to implement many features in The Standard Library rather than the compiler. While the Swift compiler also attempts to do this, it is not a goal in and of itself, resulting in many of C++'s analogus features being implemented in the compiler: tuples, pairs, reference counting, ownership, casting support, optionals, and so on. In these cases the Swift compiler will need to work with both the C++ standard library and the Swift overlay for the C++ standard library to import these APIs correctly. +C++ aims to provide sufficent tools to implement many features in its standard library rather than the compiler. While the Swift compiler also attempts to do this, it is not a goal in and of itself, resulting in many of C++'s analogous features being implemented in the compiler: tuples, pairs, reference counting, ownership, casting support, optionals, and so on. In these cases the Swift compiler will need to work with both the C++ standard library and the Swift overlay for the C++ standard library to import these APIs correctly. -The reverse is also true; C++ interop may require library-level Swift utilties to assist in the import of various C++ language concepts, such as iterators. To support this case, a set of C++ interop specific Swift APIs will be imported implicitly whenever a C++ module is imported. These APIs do not have a depedency on the distinct C++ standard library or its overlay. +The reverse is also true: C++ interop may require library-level Swift utilities to assist in the import of various C++ language concepts, such as iterators. To support this case, a set of Swift APIs specific to C++ interop will be imported implicitly whenever a C++ module is imported. These APIs should not have a dependency on the distinct C++ standard library or its overlay. ## Evolution -C++ interoperability is a huge feature that derives most of its benefit from the combination of its component features (for example, methods can't be used without types). C++ interop should be made useful to programmers before all component pieces have necessarily gone through evolution, both for the benefit of programmers wanting to use this feature, and for compiler developers designing and implementing the feature. +C++ interoperability is a huge feature that derives most of its benefit from the combination of its component features; for example, methods can't be used without types. C++ interop should be made useful to programmers before all component pieces have necessarily gone through evolution, both for the benefit of programmers wanting to use this feature, and for compiler developers designing and implementing the feature. C++ interoperability should bring in as many APIs as possible, even if they haven't gone through evolution. Swift evolution will progressively work through these APIs, formalizing them, and eventually interop will become a stable feature. Until a critical mass of APIs have been brought Swift's evolution process, a versioning scheme will allow C++ interoperability to be adopted and remain source stable while being evolved. Versions may be rapidly deprecated, but will be independent of Swift compiler versions, allowing source breaks even in minor compiler updates without disturbing adopters. @@ -213,9 +242,9 @@ This document allows specific, focused, and self contained evolution proposals t ## Tooling and build process -It goes without saying (yet will be said anyway) that as a supported language feature, C++ and Swift interoperability must work well on every platform supported by Swift. In a similar vein, tools in the Swift ecosystem should be updated to support interoperability features. For example, SourceKit should provide autocompletion, jump-to-definition, etc. for C++ functions, methods, and types and lldb should be able to print C++ types (even in Swift frames). Finally, the Swift package manager should be updated with the necessary features to support building C++ dependencies. +As a supported language feature, C++ and Swift interoperability must work well on every platform supported by Swift. In a similar vein, tools in the Swift ecosystem should be updated to support interoperability features. For example, SourceKit should provide autocompletion, jump-to-definition, etc. for C++ functions, methods, and types, and lldb should be able to print C++ types even in Swift frames. Finally, the Swift package manager should be updated with the necessary features to support building C++ dependencies. -This document outlines a strategy for importing APIs that rely on semantic information from the user. In order to make this painless for users across a variety of projects, Swift will need to provide both inline annotation support for C++ APIs and side-file support for APIs that cannot be updated. For Objective-C, this side-file is an APINotes file. As part of Swift and C++ interoperability, APINotes will either need to be updated to support C++ APIs, or another kind side-file will need to be created. +This document outlines a strategy for importing APIs that rely on semantic information from the user. In order to make this painless for users across a variety of projects, Swift will need to provide both inline annotation support for C++ APIs and side-file support for APIs that cannot be updated. For Objective-C, this side-file is an APINotes file. As part of Swift and C++ interoperability, APINotes will either need to be updated to support C++ APIs, or another kind of side-file will need to be created. ## Appendix 1: Examples and Definitions @@ -303,7 +332,7 @@ Here Player uses the `gameObjectRetain` and `gameObjectRelease` function to manu **Trivial types** are a subset of owned types. They can be copied by copying the bits of a value of the trivial type and do not need any special destruction logic. Examples of trivial types are `std::array` and `std::pair`. -**Pointer Types** are trivial types that hold pointers or references to some un-owned storage (storage that is not destroyed when the object is destroyed). Pointer types are *not* a subset of trivial types or owned types. Examples of pointer types include `std::string_view` and `std::span` and raw pointer types such as `int *` or `void *`. +**Pointer types** are trivial types that hold pointers or references to some un-owned storage (storage that is not destroyed when the object is destroyed). Pointer types are *not* a subset of trivial types or owned types. Examples of pointer types include `std::string_view` and `std::span` and raw pointer types such as `int *` or `void *`. **Projections** are values rather than types. An example of a method which yields a projection is the `c_str` method on `std::string`. From 5d10bfbf20849f228c048a2fb87fea5b68818ed1 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 30 May 2023 15:50:28 -0700 Subject: [PATCH 3160/4563] Pull back my wording in a few places (after John's commit). --- visions/using-c++-from-swift.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/visions/using-c++-from-swift.md b/visions/using-c++-from-swift.md index e13b8f0ef5..4778b0b113 100644 --- a/visions/using-c++-from-swift.md +++ b/visions/using-c++-from-swift.md @@ -28,7 +28,7 @@ Swift has had great success as a successor language to Objective-C with this app Because of this, importing C++ APIs into Swift is a difficult task that must be handled with care. Almost every goal of C++ interoperability will be in tension with Swift's safety requirements. Swift must strike a careful balance in order to maintain Swift's safety without reintroducing the development, performance, or expressivity costs of an intermediate wrapper API. -Safety is a top priority for the Swift programming language, which creates a tension with C++. While Swift enforces strong rules around things like memory safety, mutability, and nullability, C++ largely makes the programmer responsible for handling them correctly, on the pain of undefined behavior. Simply using C++ APIs should not completely undermine Swift's lanaguage guarantees, especially guarantees around safety. At a minimum, imported C++ APIs should generally not be less safe to use from Swift than they would be in C++, and C++ interoperability should strive to make imported APIs *safer* in Swift than they are in C++ by providing safe API interfaces for common, unsafe C++ API patterns (such as iterators). When it is possible for the Swift compiler to statically derive safety properties and API semantics (i.e., how to safely use an API in Swift) from the C++ API interface, C++ interoperability should take advantage of this information. When that is not possible, C++ interoperability should provide annotations to communicate the necessary information to use these APIs safely in Swift. When APIs cannot be used safely or need careful management, Swift should make that clear to the programmer. As a last resort, Swift should make an API unavailable if there's no reasonable path to a sufficiently safe Swift interface for it. +Safety is a top priority for the Swift programming language, which creates a tension with C++. While Swift enforces strong rules around things like memory safety, mutability, and nullability, C++ largely makes the programmer responsible for handling them correctly, on pain of undefined behavior. Simply using C++ APIs should not completely undermine Swift's language guarantees, especially guarantees around safety. At a minimum, imported C++ APIs should generally not be less safe to use from Swift than they would be in C++, and C++ interoperability should strive to make imported APIs *safer* in Swift than they are in C++ by providing safe API interfaces for common, unsafe C++ API patterns (such as iterators). When it is possible for the Swift compiler to statically derive safety properties and API semantics (i.e., how to safely use an API in Swift) from the C++ API interface, C++ interoperability should take advantage of this information. When that is not possible, C++ interoperability should provide annotations to communicate the necessary information to use these APIs safely in Swift. When APIs cannot be used safely or need careful management, Swift should make that clear to the programmer. As a last resort, Swift should make an API unavailable if there's no reasonable path to a sufficiently safe Swift interface for it. C++ interoperability should strive to have good diagnostics. Diagnostics that report source locations for a C++ API should refer to the API's original declaration in a C++ header, not to a location in a synthesized interface file. When a C++ API can be imported into Swift, diagnostics from misusing it (e.g. type errors when passing it an argument of the wrong type) should be similar to the diagnostics for analogous misuses of a Swift API. When a C++ API cannot be imported, attempts to use it should result in a clear error indicating why the API could not be imported, and the diagnostics should suggest specific ways that the programmer could make it importable (for example, by adding annotations). @@ -174,9 +174,7 @@ Or even this global function that projects one of its parameters: int *begin(std::vector *v) { return v->data(); } ``` -Swift will need to decide how to handle projections, and more generally the use of view types, that it doesn't recognize how to make safe. This may come with difficult trade-offs between usefulness and safety. Consider the `projectsInternalStorage` API above, and pretend that we aren't able to recognize a pattern here --- we don't know that the pointers just depend on the `OwnedType` object. Often, uses of this kind of API can be made safe in practice: when there's a `std::vector` of pointers, there's probably some straightforward thing making those pointers valid. If Swift decides not to import this API just because it might be used unsafely, that could do serious damage to the usability of C++ interop. At the very least, it should be possible to wrap such an API in a safer Swift abstraction, which will be impossible if it isn't imported at all. - -The trade-offs here are an open question for the Swift evolution process to eventually determine. +Swift will need to decide how to handle projections, and more generally the use of view types, that it doesn't recognize how to make safe. This may come with difficult trade-offs between usefulness and safety. Consider the `projectsInternalStorage` API above, and pretend that we aren't able to recognize a pattern here --- we don't know that the pointers just depend on the `OwnedType` object. Often, uses of this kind of API can be made safe in practice: when there's a `std::vector` of pointers, there's probably some straightforward thing making those pointers valid. Making this API unavailable would ensure 100% safety on the pain usability in the 99% case, when this API is safe. The tradeoffs here are an open question for the Swift evolution process to eventually determine (taking into account the feedback from early adopers on the impact of these tradeoffs). In any case, it is essential that C++ interoperability makes certain assumptions about the APIs Swift imports. There will always be an edge case that cannot be covered or does not make sense to accomidate. Interop as a whole should not become unusable so that these edge cases can be accomidated, or worse yet, so that that neither safe nor unsafe APIs are available in Swift. So, C++ interoperability should make reasonable (not conservative) assumptions when importing APIs and it should be possible to wrap such APIs in a safer Swift abstraction (which will be impossible if it isn't imported at all). ### Iterators @@ -218,9 +216,9 @@ Value vs. reference types and mutability may require user input to map correctly C++ and Swift use very different models for generic programming. C++ templates are eagerly instantiated for each set of template arguments they're used with, with type checking done separately for each instantiation based on the exact types in use. In contrast, Swift generics are type-checked once based on the requirements they impose on their type parameters, and while they can be specialized for a particular set of type arguments, that is not required or even always possible. -This difference makes using generic C++ APIs in Swift difficult. Generic code in Swift will not be able to use C++ templates generically without substantial new language features and a lot of implementation work. Allowing C++ templates to be used on concrete Swift types is theoretically more feasible but still a major project because of the *ad hoc* nature of type constraints in templates. If this feature is ever pursued, it may require substantial user guidance, such as the thorough adoption of [C++20 concepts constraints](https://en.cppreference.com/w/cpp/language/constraints) on any constrained template used from Swift. +This difference makes using generic C++ APIs in Swift difficult. Generic code in Swift will not be able to use C++ templates generically without substantial new language features and a lot of implementation work. Allowing C++ templates to be used on concrete Swift types is theoretically more feasible but still a major project because of the *ad hoc* nature of type constraints in templates. If this feature is ever pursued, it will likely require substantial user guidance through some kind of annotations or wrappers around imported APIs. -Fortunately, these limitations do not apply when using C++ types with Swift generics. Unconstrained generics can be used with C++ types without any further work, and programmers can simply add protocol conformances to concrete C++ types in order to use them with constrained generics. Swift could even provide a tool (likely in the form of an annotation) that allows users to give conformances for all specializations of a template. This level of generic programming may be sufficient for most C++ interop users, and it would allow the Swift compiler to side-step one of the most difficult and complicated parts of C++ interoperability. +Fortunately, these limitations do not apply when using C++ types with Swift generics. Unconstrained generics can be used with C++ types without any further work, and programmers can simply add protocol conformances to concrete C++ types in order to use them with constrained generics. Swift could provide a tool (likely in the form of an annotation) that allows users to even conform un-specialized templates to Swift protocols. This level of generic programming may be sufficient for most C++ interop users, and it would allow the Swift compiler to side-step one of the most difficult and complicated parts of C++ interoperability. [This forum post](https://forums.swift.org/t/bridging-c-templates-with-interop/55003) (Bridging C++ Templates with Interop) goes into depth on the issue of importing C++ templates into Swift. From 2f027211dc330fa9b79d1eb688289c57d766e9b1 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Tue, 30 May 2023 18:58:56 -0700 Subject: [PATCH 3161/4563] A few minor tweaks (bring back John's paragraph). --- visions/using-c++-from-swift.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/visions/using-c++-from-swift.md b/visions/using-c++-from-swift.md index 4778b0b113..4fe9154cb4 100644 --- a/visions/using-c++-from-swift.md +++ b/visions/using-c++-from-swift.md @@ -174,7 +174,9 @@ Or even this global function that projects one of its parameters: int *begin(std::vector *v) { return v->data(); } ``` -Swift will need to decide how to handle projections, and more generally the use of view types, that it doesn't recognize how to make safe. This may come with difficult trade-offs between usefulness and safety. Consider the `projectsInternalStorage` API above, and pretend that we aren't able to recognize a pattern here --- we don't know that the pointers just depend on the `OwnedType` object. Often, uses of this kind of API can be made safe in practice: when there's a `std::vector` of pointers, there's probably some straightforward thing making those pointers valid. Making this API unavailable would ensure 100% safety on the pain usability in the 99% case, when this API is safe. The tradeoffs here are an open question for the Swift evolution process to eventually determine (taking into account the feedback from early adopers on the impact of these tradeoffs). In any case, it is essential that C++ interoperability makes certain assumptions about the APIs Swift imports. There will always be an edge case that cannot be covered or does not make sense to accomidate. Interop as a whole should not become unusable so that these edge cases can be accomidated, or worse yet, so that that neither safe nor unsafe APIs are available in Swift. So, C++ interoperability should make reasonable (not conservative) assumptions when importing APIs and it should be possible to wrap such APIs in a safer Swift abstraction (which will be impossible if it isn't imported at all). +Swift will need to decide how to handle projections, and more generally the use of view types, that it doesn't recognize how to make safe. This may come with difficult trade-offs between usefulness and safety. Consider the `projectsInternalStorage` API above, and pretend that we aren't able to recognize a pattern here --- we don't know that the pointers just depend on the `OwnedType` object. Often, uses of this kind of API can be made safe in practice: when there's a `std::vector` of pointers, there's probably some straightforward thing making those pointers valid. If Swift decides not to import this API just because it might be used unsafely, that could do serious damage to the usability of C++ interop. At the very least, it should be possible to wrap such an API in a safer Swift abstraction, which will be impossible if it isn't imported at all. + +The trade-offs here are an open question for the Swift evolution process to eventually determine. ### Iterators @@ -216,9 +218,9 @@ Value vs. reference types and mutability may require user input to map correctly C++ and Swift use very different models for generic programming. C++ templates are eagerly instantiated for each set of template arguments they're used with, with type checking done separately for each instantiation based on the exact types in use. In contrast, Swift generics are type-checked once based on the requirements they impose on their type parameters, and while they can be specialized for a particular set of type arguments, that is not required or even always possible. -This difference makes using generic C++ APIs in Swift difficult. Generic code in Swift will not be able to use C++ templates generically without substantial new language features and a lot of implementation work. Allowing C++ templates to be used on concrete Swift types is theoretically more feasible but still a major project because of the *ad hoc* nature of type constraints in templates. If this feature is ever pursued, it will likely require substantial user guidance through some kind of annotations or wrappers around imported APIs. +This difference makes using generic C++ APIs in Swift difficult. Generic code in Swift will not be able to use C++ templates generically without substantial new language features and a lot of implementation work. Allowing C++ templates to be used on concrete Swift types is theoretically more feasible but still a major project because of the *ad hoc* nature of type constraints in templates. If this feature is ever pursued, it will likely require substantial user guidance through annotations or wrappers around imported APIs. -Fortunately, these limitations do not apply when using C++ types with Swift generics. Unconstrained generics can be used with C++ types without any further work, and programmers can simply add protocol conformances to concrete C++ types in order to use them with constrained generics. Swift could provide a tool (likely in the form of an annotation) that allows users to even conform un-specialized templates to Swift protocols. This level of generic programming may be sufficient for most C++ interop users, and it would allow the Swift compiler to side-step one of the most difficult and complicated parts of C++ interoperability. +Fortunately, these limitations do not apply when using C++ types with Swift generics. Unconstrained generics can be used with C++ types without any further work, and programmers can simply add protocol conformances to concrete C++ types in order to use them with constrained generics. [This forum post](https://forums.swift.org/t/bridging-c-templates-with-interop/55003) (Bridging C++ Templates with Interop) goes into depth on the issue of importing C++ templates into Swift. From dbbff5a62720076764618d85e21d9b6dc4d5d215 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 30 May 2023 22:16:02 -0400 Subject: [PATCH 3162/4563] Accept the "Using C++ from Swift" vision --- visions/using-c++-from-swift.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visions/using-c++-from-swift.md b/visions/using-c++-from-swift.md index 4fe9154cb4..4562c29d64 100644 --- a/visions/using-c++-from-swift.md +++ b/visions/using-c++-from-swift.md @@ -6,7 +6,7 @@ This document lays out a vision for the development of the "forward" half of C++ “Reverse” interoperability (using Swift APIs from C++) is another extremely important part of the interoperability story. However, reverse interoperability has largely different goals and constraints, which necessarily mean a different design and therefore a different vision document. The [vision for reverse interoperability](https://github.com/apple/swift-evolution/blob/main/visions/using-swift-from-c%2B%2B.md) has already been [accepted](https://forums.swift.org/t/accepted-a-vision-for-using-swift-from-c/62102) by the Language Workgroup. -This document is a prospective feature vision document, as described in the [draft review management guidelines](https://github.com/rjmccall/swift-evolution/blob/057b2383102f34c3d0f5b257f82bba0f5b94683d/review_management.md#future-directions-and-roadmaps) of the Swift evolution process. It has not yet been approved by the Language Workgroup. +This document is an official feature vision document, as described in the [draft review management guidelines](https://github.com/rjmccall/swift-evolution/blob/057b2383102f34c3d0f5b257f82bba0f5b94683d/review_management.md#future-directions-and-roadmaps) of the Swift evolution process. The Language Workgroup has endorsed the goals and basic approach laid out in this document. This endorsement is not a pre-approval of any of the concrete proposals that may come out of this document. All proposals will undergo normal evolution review, which may result in rejection or revision from how they appear in this document. ## Goals From 18ecde4f24afc835fd781c222fb1fd7467b5b6c5 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 31 May 2023 12:26:40 +0100 Subject: [PATCH 3163/4563] SE-0390: fix inconsistent syntax highlighting (#2067) Syntax highlighting for Swift was enabled inconsistently in snippets used in this proposal. --- .../0390-noncopyable-structs-and-enums.md | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index e95503fac9..7c23718e4c 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -78,7 +78,7 @@ existential types, protocols, and associated type requirements implicitly require it. Types may explicitly declare that they are `Copyable`, and generic types may explicitly require `Copyable`, but this currently has no effect. -``` +```swift struct Foo: Copyable {} ``` @@ -129,7 +129,7 @@ class SharedFile { A class type declaration may not use `~Copyable`; all class types remain copyable by retaining and releasing references to the object. -``` +```swift // ERROR: classes must be `Copyable` class SharedFile: ~Copyable { var file: FileDescriptor @@ -140,7 +140,7 @@ It is also not yet allowed to suppress the `Copyable` requirement on generic parameters, associated type requirements in protocols, or the `Self` type in a protocol declaration, or in extensions: -``` +```swift // ERROR: generic parameter types must be `Copyable` func foo(x: T) {} @@ -156,7 +156,7 @@ extension FileWithPath: ~Copyable {} `Copyable` also cannot be suppressed in existential type declarations: -``` +```swift // ERROR: `any` types must be `Copyable` let foo: any ~Copyable = FileDescriptor() ``` @@ -392,7 +392,7 @@ multiple times, even to a `borrowing` and `consuming` parameter of the same call, and the compiler will copy as necessary to make all of the function's parameters valid according to their ownership specifiers: -``` +```swift func borrow(_: borrowing Value, and _: borrowing Value) {} func consume(_: consuming Value, butBorrow _: borrowing Value) {} let x = Value() @@ -406,7 +406,7 @@ without copying. This makes the second call above impossible for a noncopyable `x`, since attempting to consume `x` would end the binding's lifetime while it also needs to be borrowed: -``` +```swift func borrow(_: borrowing FileDescriptor, and _: borrowing FileDescriptor) {} func consume(_: consuming FileDescriptor, butBorrow _: borrowing FileDescriptor) {} let x = FileDescriptor() @@ -421,7 +421,7 @@ Swift will copy the value of a variable if it is passed both by value and while leaving the original binding available for the `inout` parameter to exclusively access: -``` +```swift func update(_: inout Value, butBorrow _: borrow Value) {} func update(_: inout Value, butConsume _: consume Value) {} var x = Value() @@ -432,7 +432,7 @@ update(&x, butConsume: x) // also fine, we'll also copy But again, for a noncopyable value, this implicit copy is impossible, so these sorts of calls become exclusivity errors: -``` +```swift func update(_: inout FileDescriptor, butBorrow _: borrow FileDescriptor) {} func update(_: inout FileDescriptor, butConsume _: consume FileDescriptor) {} @@ -722,7 +722,7 @@ affect the ownership of `self` while the accessor executes. `consuming get` is particularly useful as a way of forwarding ownership of part of an aggregate, such as to take ownership away from a wrapper type: -``` +```swift struct FileDescriptorWrapper: ~Copyable { private var _value: FileDescriptor @@ -740,7 +740,7 @@ Because getters return owned values, non-`consuming` getters generally cannot be used to wrap noncopyable stored properties, since doing so would require copying the value out of the aggregate: -``` +```swift class File { private var _descriptor: FileDescriptor @@ -763,7 +763,7 @@ the type's definition, client code cannot perform consuming operations on the value, since it would need to take away the container's ownership to do so: -``` +```swift struct Inner: ~Copyable {} struct Outer: ~Copyable { @@ -777,7 +777,7 @@ let i = outer.inner // ERROR: can't take `inner` away from `outer` However, when code has the ability to mutate the member, it may freely modify, reassign, or replace the value in the field: -``` +```swift var outer = Outer() let newInner = Inner() // OK, transfers ownership of `newInner` to `outer`, destroying its previous @@ -789,7 +789,7 @@ Note that, as currently defined, `switch` to pattern-match an `enum` is a consuming operation, so it can only be performed inside `consuming` methods on the type's original definition: -``` +```swift enum OuterEnum: ~Copyable { case inner(Inner) case file(FileDescriptor) @@ -817,7 +817,7 @@ failures, as it will for local variables and value types, but a runtime error will occur if an uncaught exclusivity error occurs, such as an attempt to mutate an object's stored property while it is being borrowed: -``` +```swift class Foo { var fd: FileDescriptor @@ -847,7 +847,7 @@ The dynamic borrow state of properties is tracked independently for every stored property in the class, so it is safe to mutate one property while other properties of the same object are also being mutated or borrowed: -``` +```swift class SocketTriple { var in, middle, out: FileDescriptor } @@ -876,7 +876,7 @@ currently being pitched, this would manifest as a borrow of the noncopyable reference, preventing mutation or consumption of the reference during dynamically-asserted accesses to its properties: -``` +```swift class SocketTriple { var in, middle, out: FileDescriptor } @@ -909,7 +909,7 @@ from which those captures were taken. Variables captured by escaping closures thus behave like class properties; immutable captures are treated as always borrowed both inside the closure body and in the capture's original context. -``` +```swift func escape(_: @escaping () -> ()) {...} func borrow(_: borrowing FileDescriptor) {} @@ -943,7 +943,7 @@ simultaneously on different threads if the closure is `@Sendable`, so the captures must always remain in a valid state for memory safety, and exclusivity of mutations can only be enforced dynamically. -``` +```swift var escapedClosure: (@escaping (inout FileDescriptor) -> ())? func foo() { @@ -1191,7 +1191,7 @@ must be used to explicitly end the value's lifetime using its `deinit` if `discard` is used to conditionally destroy the value on other paths through the method. -``` +```swift struct MemoryBuffer: ~Copyable { private var address: UnsafeRawPointer @@ -1334,7 +1334,7 @@ It's a reasonable question why declaring a type as noncopyable isn't spelled like a regular protocol constraint, instead of as the removal of an existing constraint: -``` +```swift struct Foo: NonCopyable {} ``` @@ -1347,7 +1347,7 @@ like `Array` and `Dictionary` would become copyable only when the elements they contain are copyable. However, we cannot write this in terms of `NonCopyable` conditional requirements, since if we write: -``` +```swift extension Dictionary: NonCopyable where Key: NonCopyable, Value: NonCopyable {} ``` @@ -1356,7 +1356,7 @@ are noncopyable, which is wrong because we can't copy the dictionary even if only the keys or only the values are noncopyable. If we flip the constraint to `Copyable`, the correct thing would fall out naturally: -``` +```swift extension Dictionary: Copyable where Key: Copyable, Value: Copyable {} ``` @@ -1400,7 +1400,7 @@ operation that forwards ownership of the `Optional` value's payload, if any, writing `nil` back. Eventually this could be written as an extension method on `Optional`: -``` +```swift extension Optional where Self: ~Copyable { mutating func take() -> Wrapped { switch self { @@ -1467,7 +1467,7 @@ eventually be generic over copyable and non-copyable types, with the ability to be copyable for some generic arguments but not all. A simple case might be a tuple-like `Pair` struct: -``` +```swift struct Pair: ~Copyable { var first: T var second: U @@ -1477,7 +1477,7 @@ struct Pair: ~Copyable { We will need a way to express this conditional copyability, perhaps using conditional conformance style declarations: -``` +```swift extension Pair: Copyable where T: Copyable, U: Copyable {} ``` @@ -1488,7 +1488,7 @@ derived because of aspects of its declaration or usage. For instance, enums that don't have any associated values are implicitly made `Hashable` (and, by refinement, `Equatable`): -``` +```swift enum Foo { case a, b, c } @@ -1501,7 +1501,7 @@ print(Foo.a == Foo.b) and internal structs and enums are implicitly `Sendable` if all of their components are `Sendable`: -``` +```swift struct Bar { var x: Int, y: Int } @@ -1522,7 +1522,7 @@ automatically derived conformances. We propose to introduce the `~Constraint` syntax as a way to explicitly suppress automatic derivation of a conformance that would otherwise be performed for a declaration: -``` +```swift enum Candy: ~Equatable { case redVimes, twisslers, smickers } @@ -1548,7 +1548,7 @@ derivation of conformance. It does **not** mean that the type strictly does not conform to the protocol. Extensions may add the conformance back separately, possibly conditionally: -``` +```swift struct ResourceHandle: ~Sendable { // although this is an integer, it represents a system resource that // gives access to values of type `T`, which may not be thread safe @@ -1585,7 +1585,7 @@ provide your own implementation of `==` for an enum, but are fine with Equatable (and Hashable, etc) being derived for you, then the derivation of `Equatable` already will use your version of `==`. -``` +```swift enum Soda { case mxPepper, drPibb, doogh @@ -1609,7 +1609,7 @@ deinitialization. However, inside of other `mutating` or `consuming` methods, it's easy to inadvertently trigger implicit destruction of the value and reenter `deinit` again: -``` +```swift struct Foo: ~Copyable { init() { ... } @@ -1721,7 +1721,7 @@ The choice of what effect `discard` has on the lifetime of the fields affects the observed order in which field deinits occurs, but also affects how code would be expressed that performs destructuring or partial invalidation: -``` +```swift struct SocketPair: ~Copyable { let input, output: FileDescriptor From dda8466621d5a3f2f7ce69d62c47efdc87adf170 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 31 May 2023 16:32:54 +0100 Subject: [PATCH 3164/4563] using-c++-from-swift.md: fix syntax highlighting and a typo Most of the code snippets in the document weren't highlighted. --- visions/using-c++-from-swift.md | 37 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/visions/using-c++-from-swift.md b/visions/using-c++-from-swift.md index 4562c29d64..dfb56d4a2c 100644 --- a/visions/using-c++-from-swift.md +++ b/visions/using-c++-from-swift.md @@ -36,8 +36,8 @@ C++ provides tools to create high-performance APIs. The Swift compiler should em C++ is a multi-paradigm language, designed to fit many use cases and allow many different programming styles. Different codebases often express the same concept in different ways. There is no prevailing consensus among C++ programmers about the right way to express specific concepts: how to name types and methods, how much to use templates, when to use heap allocation, how to propagate and handle errors, and so on. This creates problems for importing C++ APIs into Swift, which tends to have stronger conventions, some of which are backed by language rules. For instance, it is a common pattern in some C++ codebases to have classes that are only (or at least mostly) intended to be heap-allocated and passed around by pointer; consider this example: -``` -// StatefulObject has object identity and reference semantcs: +```cpp +// StatefulObject has object identity and reference semantics: // it should be constructed with "create" and used via a pointer. struct StatefulObject { StatefulObject(const StatefulObject&) = delete; @@ -153,24 +153,24 @@ The safety problem posed by view types is very broad. If we apply this categoriz Probably the most common pattern of projection in C++ APIs is a method that returns a reference or pointer to memory that depends on `this`. Consider this example: -``` +```cpp const std::string &getName() const { return this->_name; } ``` There are several possibilities for dealing with this pattern. For example, Swift could wrap it in a `_read` accessor, implicitly encoding that the reference is only available during an access to the containing object. Swift could also add explicit lifetime-dependency features, allowing this to be treated as a return of a borrowed value. Alternatively, Swift could simply force the return value to be immediately copied after return, as if the call actually returned an owned value. It's unclear which of these would be the best approach; perhaps a combination would. This is something that will need to be investigated over time, incorporating the experience of the community with using this feature. But there are also many projections in C++ that don't match the above pattern. Consider the following API which returns a vector of internal pointers: -``` +```cpp std::vector OwnedType::projectsInternalStorage(); ``` Or this API which fills in a pointer that has two levels of indirection: -``` +```cpp void VectorLike::begin(int **out) { *out = data(); } ``` Or even this global function that projects one of its parameters: -``` +```cpp int *begin(std::vector *v) { return v->data(); } ``` @@ -197,13 +197,14 @@ Swift should also assume that C++ will not mutate values through `const` pointer As discussed in the "View types" section, the Swift compiler must make assumptions about the C++ APIs that it is importing, and mutability is another place where Swift will need to make reasonable (not conservative) assumptions about the APIs that it is importing, promoting C++‘s weak notion of `const` to Swift’s much stricter ideal. Programmers will see some benefits from Swift's stronger mutability model immediately. Consider this example: -``` +```cpp // C++ void append_n_times(std::string& s, const std::string& m, size_t n) { for (size_t i = 0; i < n; ++i) s += m; } - +``` +```swift // Swift var local: std.string = "a" append_n_times(&local, local, 5) @@ -254,7 +255,7 @@ This document outlines a strategy for importing APIs that rely on semantic infor Here a programmer has written a very large `StatefulObject` which contains many fields: -``` +```cpp struct StatefulObject { std::array names; std::array places; @@ -276,13 +277,13 @@ In Swift, this `StatefulObject` should be imported as a reference type, as it ha Here someone has written an API that uses `StatefulObject` as a value type. -``` +```cpp StatefulObject makeAppState(); ``` This will invoke a copy of `StatefulObject` which violates the semantics that the API was written with. To be useable from Swift, this API needs to be updated to pass the object indirectly (by reference): -``` +```cpp StatefulObject *makeAppState(); // OK const StatefulObject *makeAppState(); // OK StatefulObject &makeAppState(); // OK @@ -293,7 +294,7 @@ const StatefulObject &makeAppState(); // OK Instances of `StatefulObject` above are manually managed by the programmer, they create it with the create method and are responsible for destroying it once it is no longer needed. However, some reference types need to exist for the duration of the program, these references types are known as “immortal.” Examples of these immortal reference types might be pool allocators or app contexts. Let’s look at a `GameContext` object which allocates (and owns) various game elements: -``` +```cpp struct GameContext { // ... @@ -311,7 +312,7 @@ Here the `GameContext` is meant to last for the entire game as a global allocato While the `GameContext` will live for the duration of the program, individual `GameObject` should be released once they’re done being used. One such object is Player: -``` +```cpp struct GameObject { int referenceCount; @@ -336,7 +337,7 @@ Here Player uses the `gameObjectRetain` and `gameObjectRelease` function to manu **Projections** are values rather than types. An example of a method which yields a projection is the `c_str` method on `std::string`. -``` +```cpp struct string { // String is an owned type. char *storage; size_t size; @@ -346,7 +347,7 @@ struct string { // String is an owned type. Iterators are also projections: -``` +```cpp char *begin() { return storage; } // Projects internal storage char *end() { return storage + size; } // Projects internal storage ``` @@ -358,7 +359,7 @@ Because `string` is an owned type, the Swift compiler cannot represent a project The following section will go further into depth on the issues with using projections of self contained types in Swift, rather than proposing a solution on how to import them. Let’s start with an example Swift program that naively imports some self-contained type and returns a projections of it: -``` +```swift var v = vector(1) let start = v.begin() doSomething(start) @@ -367,7 +368,7 @@ fixLifetime(v) To understand the problem with this code, the following snippet highlights where an implicit copy is created and destroyed: -``` +```swift var v = vector(1) let copy = copy(v) let start = copy.begin() @@ -386,7 +387,7 @@ This model cannot be adopted in Swift, however, because the the same lexical lif The following example highlights the case described above: -``` +```swift func getCString(str: std.string) -> UnsafePointer { str.c_str() } ``` From 13273deab635f09a18cadb8b497e2e1705bbd83a Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 31 May 2023 18:48:11 -0400 Subject: [PATCH 3165/4563] Accept SE-0377 --- proposals/0377-parameter-ownership-modifiers.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index e00812c21f..a542aa4701 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -3,13 +3,11 @@ * Proposal: [SE-0377](0377-parameter-ownership-modifiers.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (May 15th...29th, 2023)** +* Status: **Accept** * Implementation: Swift 5.9, without no-implicit-copy constraint * Review: - * ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759)) ([revision and third review](https://forums.swift.org/t/se-0377-revision-make-borrowing-and-consuming-parameters-require-explicit-copying-with-the-copy-operator/64996)) -* Previous Revisions: - * [1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md) - * [2](https://github.com/apple/swift-evolution/blob/7e1d16316e5f68eb94546df9241aa6b4cacb9411/proposals/0377-parameter-ownership-modifiers.md), originally accepted revision + * ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759)) ([revision and third review](https://forums.swift.org/t/se-0377-revision-make-borrowing-and-consuming-parameters-require-explicit-copying-with-the-copy-operator/64996)) ([revision acceptance](https://forums.swift.org/t/accepted-se-0377-revision-make-borrowing-and-consuming-parameters-require-explicit-copying-with-the-copy-operator/65293)) +* Previous Revisions: ([1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md)), ([2](https://github.com/apple/swift-evolution/blob/7e1d16316e5f68eb94546df9241aa6b4cacb9411/proposals/0377-parameter-ownership-modifiers.md) (originally accepted revision)) ## Introduction From 1842bab96a804a93510960691f0c08ae9e108fbf Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 31 May 2023 18:49:36 -0400 Subject: [PATCH 3166/4563] Clean up a little --- proposals/0377-parameter-ownership-modifiers.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index a542aa4701..065bcfc948 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -3,11 +3,10 @@ * Proposal: [SE-0377](0377-parameter-ownership-modifiers.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accept** +* Status: **Accepted** * Implementation: Swift 5.9, without no-implicit-copy constraint -* Review: - * ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759)) ([revision and third review](https://forums.swift.org/t/se-0377-revision-make-borrowing-and-consuming-parameters-require-explicit-copying-with-the-copy-operator/64996)) ([revision acceptance](https://forums.swift.org/t/accepted-se-0377-revision-make-borrowing-and-consuming-parameters-require-explicit-copying-with-the-copy-operator/65293)) -* Previous Revisions: ([1](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md)), ([2](https://github.com/apple/swift-evolution/blob/7e1d16316e5f68eb94546df9241aa6b4cacb9411/proposals/0377-parameter-ownership-modifiers.md) (originally accepted revision)) +* Review: ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759)) ([revision and third review](https://forums.swift.org/t/se-0377-revision-make-borrowing-and-consuming-parameters-require-explicit-copying-with-the-copy-operator/64996)) ([revision acceptance](https://forums.swift.org/t/accepted-se-0377-revision-make-borrowing-and-consuming-parameters-require-explicit-copying-with-the-copy-operator/65293)) +* Previous Revisions: ([first review](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md)), ([originally accepted revision](https://github.com/apple/swift-evolution/blob/7e1d16316e5f68eb94546df9241aa6b4cacb9411/proposals/0377-parameter-ownership-modifiers.md)) ## Introduction From d606b75e90b91c4cee2a89ef2933eb06a14aae7a Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 31 May 2023 18:51:52 -0400 Subject: [PATCH 3167/4563] Clean up some more --- proposals/0377-parameter-ownership-modifiers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index 065bcfc948..8028de627a 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -6,7 +6,7 @@ * Status: **Accepted** * Implementation: Swift 5.9, without no-implicit-copy constraint * Review: ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759)) ([revision and third review](https://forums.swift.org/t/se-0377-revision-make-borrowing-and-consuming-parameters-require-explicit-copying-with-the-copy-operator/64996)) ([revision acceptance](https://forums.swift.org/t/accepted-se-0377-revision-make-borrowing-and-consuming-parameters-require-explicit-copying-with-the-copy-operator/65293)) -* Previous Revisions: ([first review](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md)), ([originally accepted revision](https://github.com/apple/swift-evolution/blob/7e1d16316e5f68eb94546df9241aa6b4cacb9411/proposals/0377-parameter-ownership-modifiers.md)) +* Previous Revisions: ([as of first review](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md)) ([as of second review](https://github.com/apple/swift-evolution/blob/7e1d16316e5f68eb94546df9241aa6b4cacb9411/proposals/0377-parameter-ownership-modifiers.md)) ## Introduction From 8ffd6fc6d6e8fef9d9ed3db094d365af768fa651 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sat, 3 Jun 2023 10:43:00 -0700 Subject: [PATCH 3168/4563] Mark SE-0393 as implemented in Swift 5.9. (#2072) --- proposals/0393-parameter-packs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0393-parameter-packs.md b/proposals/0393-parameter-packs.md index 22e2a92cf2..89e5843d9d 100644 --- a/proposals/0393-parameter-packs.md +++ b/proposals/0393-parameter-packs.md @@ -3,7 +3,7 @@ * Proposal: [SE-0393](0393-parameter-packs.md) * Authors: [Holly Borla](https://github.com/hborla), [John McCall](https://github.com/rjmccall), [Slava Pestov](https://github.com/slavapestov) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: On `main` gated behind the frontend flag `-enable-experimental-feature VariadicGenerics` * Review: ([pitch 1](https://forums.swift.org/t/pitch-parameter-packs/60543)) ([pitch 2](https://forums.swift.org/t/pitch-2-value-and-type-parameter-packs/60830)) ([review](https://forums.swift.org/t/se-0393-value-and-type-parameter-packs/63859)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0393-value-and-type-parameter-packs/64382)) From c9b3abb3062dd5b7a86e2ea6e5efab185660c90f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EC=9E=AC=ED=98=B8?= Date: Mon, 5 Jun 2023 01:42:06 +0900 Subject: [PATCH 3169/4563] [SE-0161] Fix minor typo (#2073) --- proposals/0161-key-paths.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/proposals/0161-key-paths.md b/proposals/0161-key-paths.md index 608127f527..1a6299ee0f 100644 --- a/proposals/0161-key-paths.md +++ b/proposals/0161-key-paths.md @@ -182,9 +182,9 @@ We also explored many different spellings, each with different strengths. We hav | Case | `#keyPath` | Function Type Reference | Escape | | --- | --- | --- | --- | | Fully qualified | `#keyPath(Person, .friends[0].name)` | `Person.friends[0].name` | `\Person.friends[0].name` | -| Type Inferred| `#keyPath(.friends[0].name)` |`Person.friends[0].name` | `\.friends[0].name` | +| Type Inferred | `#keyPath(.friends[0].name)` |`Person.friends[0].name` | `\.friends[0].name` | -While the crispness of the function-type-reference is appealing, it becomes ambigious when working with type properties. The escape-sigil variant avoids this, and remains quite readable. +While the crispness of the function-type-reference is appealing, it becomes ambiguous when working with type properties. The escape-sigil variant avoids this, and remains quite readable. #### Why `\`? During review many different sigils were considered: @@ -199,4 +199,3 @@ During review many different sigils were considered: #### Function Type References We think the disambiguating benefits of the escape-sigil would greatly benefit function type references, but such considerations are outside the scope of this proposal. - From e58823fc540f007805658cfe536264113e783282 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 4 Jun 2023 21:40:14 -0700 Subject: [PATCH 3170/4563] Mark the various macros proposals as implemented in Swift 5.9 (#2074) --- proposals/0382-expression-macros.md | 2 +- proposals/0389-attached-macros.md | 2 +- proposals/0394-swiftpm-expression-macros.md | 2 +- proposals/0397-freestanding-declaration-macros.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index 8d530a7b27..de7ed0a38e 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -3,7 +3,7 @@ * Proposal: [SE-0382](0382-expression-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: Partial implementation is available in `main` under the experimental feature flag `Macros`. An [example macro repository](https://github.com/DougGregor/swift-macro-examples) provides a way to experiment with this feature. * Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861)) ([review #1](https://forums.swift.org/t/se-0382-expression-macros/62090)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0382-expression-macros/62898)) ([pitch #3](https://forums.swift.org/t/se-0382-expression-macros-mini-pitch-for-updates/62810)) ([review #2](https://forums.swift.org/t/se-0382-second-review-expression-macros/63064)) ([acceptance](https://forums.swift.org/t/accepted-se-0382-expression-macros/63495)) diff --git a/proposals/0389-attached-macros.md b/proposals/0389-attached-macros.md index 1061a929a6..59bc836f51 100644 --- a/proposals/0389-attached-macros.md +++ b/proposals/0389-attached-macros.md @@ -3,7 +3,7 @@ * Proposal: [SE-0389](0389-attached-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Holly Borla](https://github.com/hborla), [Richard Wei](https://github.com/rxwei) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: Implemented on GitHub `main` behind the experimental flag `Macros`. See the [example repository](https://github.com/DougGregor/swift-macro-examples) for more macros. * Review: ([pitch #1, under the name "declaration macros"](https://forums.swift.org/t/pitch-declaration-macros/62373)) ([pitch #2](https://forums.swift.org/t/pitch-attached-macros/62812)) ([review](https://forums.swift.org/t/se-0389-attached-macros/63165)) ([acceptance](https://forums.swift.org/t/accepted-se-0389-attached-macros/63593)) diff --git a/proposals/0394-swiftpm-expression-macros.md b/proposals/0394-swiftpm-expression-macros.md index 5433b20ae2..a3a48e3779 100644 --- a/proposals/0394-swiftpm-expression-macros.md +++ b/proposals/0394-swiftpm-expression-macros.md @@ -3,7 +3,7 @@ * Proposal: [SE-0394](0394-swiftpm-expression-macros.md) * Authors: [Boris Buegling](https://github.com/neonichu), [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: **Available behind pre-release tools-version** ([apple/swift-package-manager#6185](https://github.com/apple/swift-package-manager/pull/6185), [apple/swift-package-manager#6200](https://github.com/apple/swift-package-manager/pull/6200)) * Review: ([pitch 1](https://forums.swift.org/t/pitch-package-manager-support-for-custom-macros/63482)) ([pitch 2](https://forums.swift.org/t/pitch-2-package-manager-support-for-custom-macros/63868)) ([review](https://forums.swift.org/t/se-0394-package-manager-support-for-custom-macros/64170)) ([acceptance](https://forums.swift.org/t/accepted-se-0394-package-manager-support-for-custom-macros/64589)) diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index 2b3cd1e71f..52590e3a08 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -3,7 +3,7 @@ * Proposal: [SE-0397](0397-freestanding-declaration-macros.md) * Authors: [Doug Gregor](https://github.com/DougGregor), [Richard Wei](https://github.com/rxwei), [Holly Borla](https://github.com/hborla) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Vision: [Macros](https://github.com/apple/swift-evolution/blob/main/visions/macros.md) * Implementation: On `main` behind the experimental flag `FreestandingMacros` * Review: ([review](https://forums.swift.org/t/se-0397-freestanding-declaration-macros/64655)) ([partial acceptance and second review](https://forums.swift.org/t/se-0397-second-review-freestanding-declaration-macros/64997)) ([acceptance](https://forums.swift.org/t/accepted-se-0397-freestanding-declaration-macros/65167)) From cff79d9c5481692a49a2d75609e2206748200e15 Mon Sep 17 00:00:00 2001 From: Gwynne Raskind Date: Thu, 8 Jun 2023 01:36:53 -0500 Subject: [PATCH 3171/4563] SE-0392 trivial typo fix --- proposals/0392-custom-actor-executors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index 69334c6cfe..afc8fbe640 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -121,7 +121,7 @@ Asserting the apropriate executor is used in a synchronous piece of code looks l ````swift func synchronousButNeedsMainActorContext() { - // check if we're executing on the maina actor context (or crash if we're not) + // check if we're executing on the main actor context (or crash if we're not) MainActor.preconditionIsolated() // same as precondition, however only in DEBUG builds From 97197b94b7f64147080f491bb9f81fcd912a1575 Mon Sep 17 00:00:00 2001 From: Tim Wang Date: Mon, 12 Jun 2023 12:58:29 +1000 Subject: [PATCH 3172/4563] SE-0382:Fix typo (#2079) --- proposals/0382-expression-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index de7ed0a38e..f88aa56645 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -9,7 +9,7 @@ ## Introduction -Expression macros provide a way to extend Swift with new kinds of expressions, which can perform arbitary syntactic transformations on their arguments to produce new code. Expression macros make it possible to extend Swift in ways that were only previously possible by introducing new language features, helping developers build more expressive libraries and eliminate extraneous boilerplate. +Expression macros provide a way to extend Swift with new kinds of expressions, which can perform arbitrary syntactic transformations on their arguments to produce new code. Expression macros make it possible to extend Swift in ways that were only previously possible by introducing new language features, helping developers build more expressive libraries and eliminate extraneous boilerplate. ## Motivation @@ -187,7 +187,7 @@ The `#` syntax for macro expansion expressions was specifically chosen because S Both `function-call-argument-clause` and `trailing-closures` are optional. When both are omitted, the macro is expanded as-if the empty argument list `()` were provided. Macros are not first-class entities in the way functions are, so they cannot be passed around as values and do not need an "unapplied macro" syntax. This allows `#line` et al to be macros without requiring them to be written as `#line()`. There is some precedent for this with property wrappers, which will also be used for attached macros. -When a macro expansion is encountered in the source code, it's expansion occurs in two phases. The first phase is the type-check phase, where the arguments to the macro are type-checked against the parameters of the named macro, and the result type of the named macro is checked against the context in which the macro expansion occurs. This type-checking is equivalent to that performed for a function call, and does not involve the macro definition. +When a macro expansion is encountered in the source code, its expansion occurs in two phases. The first phase is the type-check phase, where the arguments to the macro are type-checked against the parameters of the named macro, and the result type of the named macro is checked against the context in which the macro expansion occurs. This type-checking is equivalent to that performed for a function call, and does not involve the macro definition. The second phase is the macro expansion phase, during which the syntax of the macro arguments is provided to the macro definition. For builtin-macro definitions, the behavior at this point depends on the semantics of the macro, e.g., the `externalMacro` macro invokes the external program and provides it with the source code of the macro expansion. For other macros, the arguments are substituted into the `macro-expansion-expression` of the definition. For example: From 95612af81ba5fd8696d5812612004abe033258b7 Mon Sep 17 00:00:00 2001 From: Ryu <87907656+Ryu0118@users.noreply.github.com> Date: Thu, 15 Jun 2023 06:34:11 +0900 Subject: [PATCH 3173/4563] Update 0395-observability.md (#2075) --- proposals/0395-observability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0395-observability.md b/proposals/0395-observability.md index 78f6ff504b..35249ae2f5 100644 --- a/proposals/0395-observability.md +++ b/proposals/0395-observability.md @@ -15,7 +15,7 @@ #### Suggested Reading * [Expression Macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md) -* [Attached Macros](https://github.com/DougGregor/swift-evolution/blob/attached-macros/proposals/nnnn-attached-macros.md) +* [Attached Macros](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md) ## Introduction From 46fa1ee4aa05fac9adb010bf2139465f63f6bd1e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 14 Jun 2023 14:39:01 -0700 Subject: [PATCH 3174/4563] `init` accessors. (#2042) * Add proposal for init accessors. * Init accessors suggestions (#2) * Expand description of accesses and give a "dictionary storage" example * Make the name of the `newValue` parameter configurable Be consistent with set/willSet/didSet in making the parameter name optional and overridable. * A few clarifications regarding init accessor signatures * Clarify the rules for definite initialization Expand the examples and show the case where virtual and stored-property initialization collide. --------- Co-authored-by: Holly Borla * Address outstanding proposal comments. * Update the syntax for init accessor effects based on pitch feedback. * Update the first init accessor example to use a custom parameter name. * Add an acknowledgments section for the init accessors proposal. * Update proposals/NNNN-init-accessors.md Co-authored-by: Frederick Kellison-Linn * Remove the phrase "virtual initialization". * Editorial changes. * Update the Alternatives Considered section with more syntax suggestions from the pitch thread. * Add more commentary on member-wise initializers. * Add a DI example where a property is not initialized on all paths. * Init accessors is SE-0400 --------- Co-authored-by: Doug Gregor Co-authored-by: Frederick Kellison-Linn Co-authored-by: Freddy Kellison-Linn --- proposals/0400-init-accessors.md | 414 +++++++++++++++++++++++++++++++ 1 file changed, 414 insertions(+) create mode 100644 proposals/0400-init-accessors.md diff --git a/proposals/0400-init-accessors.md b/proposals/0400-init-accessors.md new file mode 100644 index 0000000000..a5631df7dc --- /dev/null +++ b/proposals/0400-init-accessors.md @@ -0,0 +1,414 @@ +# Init Accessors + +* Proposal: [SE-0400](0400-init-accessors.md) +* Authors: [Holly Borla](https://github.com/hborla), [Doug Gregor](https://github.com/douggregor) +* Review Manager: [Frederick Kellison-Linn](https://github.com/Jumhyn) +* Status: **Active review (June 14th...June 26th, 2023)** +* Implementation: On `main` behind experimental feature flag `InitAccessors` +* Review: ([pitch](https://forums.swift.org/t/pitch-init-accessors/64881)) + +## Introduction + +Init accessors generalize the out-of-line initialization feature of property wrappers to allow any computed property on types to opt into definite initialization analysis, and subsume initialization of a set of stored properties with custom initialization code. + +## Motivation + +Swift applies [definite initialization analysis](https://en.wikipedia.org/wiki/Definite_assignment_analysis) to stored properties, stored local variables, and variables with property wrappers. Definite initialization ensures that memory is initialized on all paths before it is accessed. A common pattern in Swift code is to use one property as backing storage for one or more computed properties, and abstractions like [property wrappers](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md) and [attached macros](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md) help facilitate this pattern. Under this pattern, the backing storage is an implementation detail, and most code works with the computed property, including initializers. + +Property wrappers support bespoke definite initialization that allows initializing the backing property wrapper storage via the computed property, always re-writing initialization-via-wrapped-property in the form `self.value = value` to initialization of the backing storage in the form of `_value = Wrapper(wrappedValue: value)`: + +```swift +@propertyWrapper +struct Wrapper { + var wrappedValue: T +} + +struct S { + @Wrapper var value: Int + + init(value: Int) { + self.value = value // Re-written to self._x = Wrapper(wrappedValue: value) + } + + init(other: Int) { + self._value = Wrapper(wrappedValue: other) // Okay, initializes storage '_x' directly + } +} +``` + +The ad-hoc nature of property wrapper initializers mixed with an exact definite initialization pattern prevent property wrappers with additional arguments from being initialized out-of-line. Furthermore, property-wrapper-like macros cannot achieve the same initializer usability, because any backing storage variables added must be initialized directly instead of supporting initialization through computed properties. For example, the [`@Observable` macro](https://github.com/apple/swift-evolution/blob/main/proposals/0395-observability.md) applies a property-wrapper-like transform that turns stored properties into computed properties backed by the observation APIs, but it provides no way to write an initializer using the original property names like the programmer expects: + +```swift +@Observable +struct Proposal { + var title: String + var text: String + + init(title: String, text: String) { + self.title = title // error: 'self' used before all stored properties are initialized + self.text = text // error: 'self' used before all stored properties are initialized + } // error: Return from initializer without initializing all stored properties +} +``` + +## Proposed solution + +This proposal adds _`init` accessors_ to opt computed properties on types into definite initialization that subsumes initialization of a set of zero or more specified stored properties, which allows assigning to computed properties in the body of a type's initializer: + +```swift +struct Angle { + var degrees: Double + var radians: Double { + init(initialValue) initializes(degrees) { + degrees = initialValue * 180 / .pi + } + + get { degrees * .pi / 180 } + set { degrees = newValue * 180 / .pi } + } + + init(degrees: Double) { + self.degrees = degrees // initializes 'self.degrees' directly + } + + init(radiansParam: Double) { + self.radians = radiansParam // calls init accessor for 'self.radians', passing 'radiansParam' as the argument + } +} +``` + +The signature of an `init` accessor specifies up to two sets of stored properties: the properties that are accessed (via `accesses`) and the properties that are initialized (via `initializes`) by the accessor. `initializes` and `accesses` are side-effects of the `init` accessor. Access effects specify the other stored properties that can be accessed from within the `init` accessor (no other uses of `self` are allowed), and therefore must be initialized before the computed property's `init` accessor is invoked. The `init` accessor must initialize each of the initialized stored properties on all control flow paths. The `radians` property in the example above specifies no access effect, but initializes the `degrees` property, so it specifies only `initializes(degrees)`. + +Access effects allow a computed property to be initialized by placing its contents into another stored property: + +```swift +struct ProposalViaDictionary { + private var dictionary: [String: String] + + var title: String { + init(newValue) accesses(dictionary) { + dictionary["title"] = newValue + } + + get { dictionary["title"]! } + set { dictionary["title"] = newValue } + } + + var text: String { + init(newValue) accesses(dictionary) { + dictionary["text"] = newValue + } + + get { dictionary["text"]! } + set { dictionary["text"] = newValue } + } + + init(title: String, text: String) { + self.dictionary = [:] // 'dictionary' must be initialized before init accessors access it + self.title = title // calls init accessor to insert title into the dictionary + self.text = text // calls init accessor to insert text into the dictionary + + // it is an error to omit either initialization above + } +} +``` + +Both `init` accessors document that they access `dictionary`, which allows them to insert the new values into the dictionary with the appropriate key as part of initialization. This allows one to fully abstract away the storage mechanism used in the type. + +Finally, computed properties with `init` accessors are privileged in the synthesized member-wise initializer. With this proposal, property wrappers have no bespoke definite and member-wise initialization support. Instead, the desugaring for property wrappers with an `init(wrappedValue:)` includes an `init` accessor for wrapped properties and a member-wise initializer including wrapped values instead of the respective backing storage. The property wrapper code in the Motivation section will desugar to the following code: + +```swift +@propertyWrapper +struct Wrapper { + var wrappedValue: T +} + +struct S { + private var _value: Wrapper + var value: Int { + init(newValue) initializes(_value) { + self._value = Wrapper(wrappedValue: newValue) + } + + get { _value.wrappedValue } + set { _value.wrappedValue = newValue } + } + + // This initializer is the same as the generated member-wise initializer. + init(value: Int) { + self.value = value // Calls 'init' accessor on 'self.value' + } +} + +S(value: 10) +``` + +This proposal allows macros to model the following property-wrapper-like patterns including out-of-line initialization of the computed property: +* A wrapped property with attribute arguments +* A wrapped property that is backed by an explicit stored property +* A set of wrapped properties that are backed by a single stored property + +## Detailed design + +### Syntax + +This proposal adds new syntax for `init` accessor blocks, which can be written in the accessor list of a computed property. Init accessors add the following production rules to the grammar: + +``` +init-accessor -> 'init' init-accessor-parameter[opt] init-effect[opt] access-effect[opt] function-body + +init-accessor-parameter -> '(' identifier ')' + +init-effect -> 'initializes' '(' identifier-list ')' + +access-effect -> 'accesses' '(' identifier-list ')' + +accessor-block -> init-accessor +``` + +The `identifier` in an `init-accessor-parameter`, if provided, is the name of the parameter that contains the initial value. If not provided, a parameter with the name `newValue` is automatically created. The minimal init accessor has no parameter list and no initialization effects: + +```swift +struct Minimal { + var value: Int { + init { + print("init accessor called with \(newValue)") + } + + get { 0 } + } +} +``` + +### `init` accessor signatures + +`init` accessor declarations can optionally specify a signature. An `init` accessor signature is composed of a parameter list, followed by an initialization effects specifier clause. The initialization effects can include a list of stored properties that are initialized by this accessor specified in the argument list of the contextual `initializes` keyword, and a list of stored properties that are accessed by this accessor specified in the argument list of the contextual `accesses` keyword, each of which are optional: + +```swift +struct S { + var readMe: String + + var _x: Int + + var x: Int { + init(newValue) initializes(_x) accesses(readMe) { + print(readMe) + _x = newValue + } + + get { _x } + set { _x = newValue } + } +} +``` + +If the accessor uses the default parameter name `newValue` and neither initializes nor accesses any stored property, the signature is not required. + +Init accessors can subsume the initialization of a set of stored properties. Subsumed stored properties are specified through the `initializes` effect. The body of an `init` accessor is required to initialize the subsumed stored properties on all control flow paths. + +Init accessors can also require a set of stored properties to already be initialized when the body is evaluated, which are specified through the `accesses` effect. These stored properties can be accessed in the accessor body; no other properties or methods on `self` are available inside the accessor body, nor is `self` available as a whole object (i.e., to call methods on it). + +### Definite initialization of properties on `self` + +The semantics of an assignment inside of a type's initializer depend on whether or not all of `self` is initialized on all paths at the point of assignment. Before all of `self` is initialized, assignment to a computed property with an `init` accessor is re-written to an `init` accessor call; after `self` has been initialized, assignment to a computed property is re-written to a setter call. + +With this proposal, all of `self` is initialized if: +* All stored properties are initialized on all paths, and +* All computed properties with `init` accessors are initialized on all paths. + +An assignment to a computed property with an `init` accessor before all of `self` is initialized will call the computed property's `init` accessor and initialize all of the stored properties specified in its `initializes` clause: + +```swift +struct S { + var x1: Int + var x2: Int + var computed: Int { + init(newValue) initializes(x1, x2) { ... } + } + + init() { + self.computed = 1 // initializes 'computed', 'x1', and 'x2'; 'self' is now fully initialized + } +} +``` + +An assignment to a computed property that has not been initialized on all paths will be re-written to an `init` accessor call: + +```swift +struct S { + var x: Int + var y: Int + var point: (Int, Int) { + init(newValue) initializes(x, y) { + (self.x, self.y) = newValue + } + get { (x, y) } + set { (x, y) = newValue } + } + + init(x: Int, y: Int) { + if (x == y) { + self.point = (x, x) // calls 'init' accessor + } + + // 'self.point' is not initialized on all paths here + + self.point = (x, y) // calls 'init' accessor + + // 'self.point' is initialized on all paths here + } +} +``` + +An assignment to a stored property before all of `self` is initialized will initialize that stored property. When all of the stored properties listed in the `initializes` clause of a computed property with an `init` accessor have been initialized, that computed property is considered initialized: + +```swift +struct S { + var x1: Int + var x2: Int + var x3: Int + var computed: Int { + init(newValue) initializes(x1, x2) { ... } + } + + init() { + self.x1 = 1 // initializes 'x1'; neither 'x2' or 'computed' is initialized + self.x2 = 1 // initializes 'x2' and 'computed' + self.x3 = 1 // initializes 'x3'; 'self' is now fully initialized + } +} +``` + +An assignment to a computed property where at least one of the stored properties listed in `initializes` is initialized, but `self` is not initialized, is an error. This prevents double-initialization of the underlying stored properties: + +```swift +struct S { + var x: Int + var y: Int + var point: (Int, Int) { + init(newValue) initializes(x, y) { + (self.x, self.y) = newValue + } + get { (x, y) } + set { (x, y) = newValue } + } + + init(x: Int, y: Int) { + self.x = x // Only initializes 'x' + self.point = (x, y) // error: neither the `init` accessor nor the setter can be called here + } +} +``` + +### Memberwise initializers + +If a struct does not declare its own initializers, it receives an implicit memberwise initializer based on the stored properties of the struct, because the storage is what needs to be initialized. Because many use-cases for `init` accessors are fully abstracting a single computed property to be backed by a single stored property, such as in the property-wrapper use case, an `init` accessor provides a preferred mechansim for initializing storage because the programmer will primarily interact with that storage through the computed property. As such, the memberwise initializer parameter list will include any computed properties that subsume the initialization of stored properties instead of parameters for those stored properties. + +```swift +struct S { + var _x: Int + var x: Int { + init(newValue) initializes(_x) { + _x = newValue + } + + get { _x } + set { _x = newValue } + } + + var y: Int +} + +S(x: 10, y: 100) +``` + +The above struct `S` receives a synthesized initializer: + +```swift +init(x: Int, y: Int) { + self.x = x + self.y = y +} +``` + +A memberwise initializer will not be synthesized if a stored property that is an `accesses` effect of a computed property is ordered after that computed property in the source code: + +```swift +struct S { + var _x: Int + var x: Int { + init(newValue) initializes(_x) accesses(y) { + _x = newValue + } + + get { _x } + set { _x = newValue } + } + + var y: Int +} +``` + +The above struct would receive the following memberwise initializer, which is invalid so an error is emitted: + +```swift +init(x: Int, y: Int) { + self.x = x // error + self.y = y +} +``` + +Use cases for `init` accessors that provide a projection of a stored property as various units through several computed properties don't have a single preferred unit from which to initialize. Most likely, these use cases want a different member-wise initializer for each unit that you can initialize from. If a type contains several computed properties with `init` accessors that initialize the same stored property, a member-wise initializer will not be synthesized. + +## Source compatibility + +`init` accessors are an additive capability with new syntax; there is no impact on existing source code. + +## ABI compatibility + +`init` accessors are only called from within a module, so they are not part of the module's ABI. In cases where a type's initializer is `@inlinable`, the body of an `init` accessor must also be inlinable. + +## Implications on adoption + +Because `init` accessors are always called from within the defining module, adopting `init` accessors is an ABI-compatible change. Adding an `init` accessor to an existing property also cannot have any source compatibility impact outside of the defining module; the only possible source incompatibilities are on the generated memberwise initializer (if new entries are added), or on the type's `init` implementation (if new initialization effects are added). + +## Alternatives considered + +A previous version of this proposal specified init accessor effects in the parameter list using special labels: + +```swift +struct S { + var _x: Int + var x: Int { + init(newValue, initializes: _x, accesses: y) { + _x = newValue + } + + get { _x } + set { _x = newValue } + } + + var y: Int +} +``` + +This syntax choice is misleading because the effects look like function parameters, while `initializes` behaves more like the output of an init accessor, and `accesses` are not explicitly provided at the call-site. Conceptually, `initializes` and `accesses` are side effects of an `init` accessor, so the proposal was revised to place these modifiers in the effects clause. + +Other syntax suggestions from pitch reviewers included: + +* Using a capture-list-style clause, e.g. `init { [&x, y] in ... }` +* Attributes on the computed property itself, e.g. `@initializes(_x) var x: Int { ... }` +* Using more concise effect names, e.g. `writes` and `reads` instead of `initializes` and `accesses` +* And more! + +However, the current synatx in this proposal most accurately models the semantics of initialization effects. An `init` accessor is a function -- not a closure -- that has side-effects related to initialization. _Only_ the `init` accessor has these effects; though the `set` accessor often contains code that looks the same as the code in the `init` accessor, the effects of these accessors are different. Because `init` accessors are called before all of `self` is initialized, they do not recieve a fully-initialized `self` as a parameter like `set` accessors do, and assignments to `initializes` stored properties in `init` accessors have the same semantics as that of a standard initializer, such as suppressing `willSet` and `didSet` observers. These reasons reinforce the decision to specify `initializes` and `accesses` in the effects clause of an `init` accessor. + +## Future directions + +### `init` accessors for local variables + +`init` accessors for local variables have different implications on definite initialization, because re-writing assignment to `init` or `set` is not based on the initialization state of `self`. Local variable getters and setters can also capture any other local variables in scope, which raises more challenges for diagnosing escaping uses before initialization during the same pass where assignments may be re-written to `init` or `set`. As such, local variables with `init` accessors are a future direction. + +## Acknowledgments + +Thank you to TJ Usiyan, Michel Fortin, and others for suggesting alternative syntax ideas for `init` accessor effects; thank you to Pavel Yaskevich for helping with the implementation. From f24b5dce42f4a8593dbe8ad0692f11f10c8fe10f Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 14 Jun 2023 17:54:46 -0400 Subject: [PATCH 3175/4563] Link review thread for SE-0400 (#2080) --- proposals/0400-init-accessors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0400-init-accessors.md b/proposals/0400-init-accessors.md index a5631df7dc..b5ac95c5c0 100644 --- a/proposals/0400-init-accessors.md +++ b/proposals/0400-init-accessors.md @@ -5,7 +5,7 @@ * Review Manager: [Frederick Kellison-Linn](https://github.com/Jumhyn) * Status: **Active review (June 14th...June 26th, 2023)** * Implementation: On `main` behind experimental feature flag `InitAccessors` -* Review: ([pitch](https://forums.swift.org/t/pitch-init-accessors/64881)) +* Review: ([pitch](https://forums.swift.org/t/pitch-init-accessors/64881)) ([review](https://forums.swift.org/t/se-0400-init-accessors/65583)) ## Introduction From 676d821384a941aca0ecb5a55ce020e196e2acf5 Mon Sep 17 00:00:00 2001 From: R Olson Date: Wed, 14 Jun 2023 17:39:06 -0600 Subject: [PATCH 3176/4563] Update 0400-init-accessors.md (#2081) Fix typo in code comments --- proposals/0400-init-accessors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0400-init-accessors.md b/proposals/0400-init-accessors.md index b5ac95c5c0..88d6e8335c 100644 --- a/proposals/0400-init-accessors.md +++ b/proposals/0400-init-accessors.md @@ -27,11 +27,11 @@ struct S { @Wrapper var value: Int init(value: Int) { - self.value = value // Re-written to self._x = Wrapper(wrappedValue: value) + self.value = value // Re-written to self._value = Wrapper(wrappedValue: value) } init(other: Int) { - self._value = Wrapper(wrappedValue: other) // Okay, initializes storage '_x' directly + self._value = Wrapper(wrappedValue: other) // Okay, initializes storage '_value' directly } } ``` From a7ee9faf1ed3765d7e9430aa6a421c359a5518ce Mon Sep 17 00:00:00 2001 From: Scott Perry Date: Wed, 14 Jun 2023 20:13:41 -0700 Subject: [PATCH 3177/4563] Update minor typo in 0400-init-accessors.md (#2082) --- proposals/0400-init-accessors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0400-init-accessors.md b/proposals/0400-init-accessors.md index 88d6e8335c..8e5179238e 100644 --- a/proposals/0400-init-accessors.md +++ b/proposals/0400-init-accessors.md @@ -401,7 +401,7 @@ Other syntax suggestions from pitch reviewers included: * Using more concise effect names, e.g. `writes` and `reads` instead of `initializes` and `accesses` * And more! -However, the current synatx in this proposal most accurately models the semantics of initialization effects. An `init` accessor is a function -- not a closure -- that has side-effects related to initialization. _Only_ the `init` accessor has these effects; though the `set` accessor often contains code that looks the same as the code in the `init` accessor, the effects of these accessors are different. Because `init` accessors are called before all of `self` is initialized, they do not recieve a fully-initialized `self` as a parameter like `set` accessors do, and assignments to `initializes` stored properties in `init` accessors have the same semantics as that of a standard initializer, such as suppressing `willSet` and `didSet` observers. These reasons reinforce the decision to specify `initializes` and `accesses` in the effects clause of an `init` accessor. +However, the current syntax in this proposal most accurately models the semantics of initialization effects. An `init` accessor is a function -- not a closure -- that has side-effects related to initialization. _Only_ the `init` accessor has these effects; though the `set` accessor often contains code that looks the same as the code in the `init` accessor, the effects of these accessors are different. Because `init` accessors are called before all of `self` is initialized, they do not recieve a fully-initialized `self` as a parameter like `set` accessors do, and assignments to `initializes` stored properties in `init` accessors have the same semantics as that of a standard initializer, such as suppressing `willSet` and `didSet` observers. These reasons reinforce the decision to specify `initializes` and `accesses` in the effects clause of an `init` accessor. ## Future directions From 600af7ca8dcc46f88dcb64a261987c1fbf7d1a51 Mon Sep 17 00:00:00 2001 From: BJ Homer Date: Fri, 16 Jun 2023 00:53:47 -0600 Subject: [PATCH 3178/4563] Disable actor isolation inference from property wrapper usage (#1962) * Create nnnn-remove-property-wrapper-isolation.md * Add link to implementation * Add source compatibility evaluation to proposal * Update source compatibility report * Update nnnn-remove-property-wrapper-isolation.md * Added Day One compatibility * Added a note about concurrency checking levels * Update proposals/nnnn-remove-property-wrapper-isolation.md Co-authored-by: Remy Demarest * Update nnnn-remove-property-wrapper-isolation.md Remove references to "recent", as a fair bit of time has passed. * Fixed a link to reference a specific commit This way it always points to the right line * Update nnnn-remove-property-wrapper-isolation.md A couple minor tweaks to the text. Fixed typos, clarified some things. * Assign "Remove property wrapper isolation" to SE-0401. --------- Co-authored-by: Remy Demarest Co-authored-by: Holly Borla --- .../0401-remove-property-wrapper-isolation.md | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 proposals/0401-remove-property-wrapper-isolation.md diff --git a/proposals/0401-remove-property-wrapper-isolation.md b/proposals/0401-remove-property-wrapper-isolation.md new file mode 100644 index 0000000000..4fa01c9a74 --- /dev/null +++ b/proposals/0401-remove-property-wrapper-isolation.md @@ -0,0 +1,220 @@ +# Remove Actor Isolation Inference caused by Property Wrappers + +* Proposal: [SE-0401](0401-remove-property-wrapper-isolation.md) +* Authors: [BJ Homer](https://github.com/bjhomer) +* Review Manager: [Holly Borla](https://github.com/hborla) +* Status: **Active review (June 16th...June 30th, 2023)** +* Implementation: [apple/swift#63884](https://github.com/apple/swift/pull/63884) +* Review: ([pitch]([https://forums.swift.org/t/pitch-init-accessors/64881](https://forums.swift.org/t/pitch-stop-inferring-actor-isolation-based-on-property-wrapper-usage/63262))) + + + +## Introduction + +[SE-0316: Global Actors](https://github.com/apple/swift-evolution/blob/main/proposals/0316-global-actors.md) introduced annotations like `@MainActor` to isolate a type, function, or property to a particular global actor. It also introduced various rules for how that global actor isolation could be inferred. One of those rules was: + +> Declarations that are not explicitly annotated with either a global actor or `nonisolated` can infer global actor isolation from several different places: +> +> [...] +> +> - A struct or class containing a wrapped instance property with a global actor-qualified wrappedValue infers actor isolation from that property wrapper: +> +> ```swift +> @propertyWrapper +> struct UIUpdating { +> @MainActor var wrappedValue: Wrapped +> } +> +> struct CounterView { // infers @MainActor from use of @UIUpdating +> @UIUpdating var intValue: Int = 0 +> } +> ``` + + +This proposal advocates for **removing this inference rule** when compiling in the Swift 6 language mode. Given the example above, CounterView would no longer infer `@MainActor` isolation in Swift 6. + +## Motivation + +This particular inference rule is surprising and nonobvious to many users. Some developers have trouble understanding the Swift Concurrency model because it's not obvious to them when actor isolation applies. When something is inferred, it is not visible to the user, and that makes it harder to understand. This frequently arises when using the property wrappers introduced by Apple's SwiftUI framework, although it is not limited to that framework. For example: + +### An example using SwiftUI + +```swift +struct MyView: View { + // Note that `StateObject` has a MainActor-isolated `wrappedValue` + @StateObject private var model = Model() + + var body: some View { + Text("Hello, \(model.name)") + .onAppear { viewAppeared() } + } + + // This function is inferred to be `@MainActor` + func viewAppeared() { + updateUI() + } +} + +@MainActor func updateUI() { /* do stuff here */ } +``` + +The above code compiles just fine. But if we change `@StateObject` to `@State`, we get an error: + +```diff +- @StateObject private var model = Model() ++ @State private var model = Model() +``` + +```swift + func viewAppeared() { + // error: Call to main actor-isolated global function + // 'updateUI()' in a synchronous nonisolated context + updateUI() + } +``` + +Changing `@StateObject var model` to `@State var model` caused `viewAppeared()` to stop compiling, even though that function didn't use `model` at all. It feels non-obvious that changing the declaration of one property should cause a _sibling_ function to stop compiling. In fact, we also changed the isolation of the entire `MyView` type by changing one property wrapper. + +### An example not using SwiftUI + +This problem is not isolated to SwiftUI. For example: + + +```swift +// A property wrapper for use with our database library +@propertyWrapper +struct DBParameter { + @DatabaseActor public var wrappedValue: T +} + +// Inferred `@DatabaseActor` isolation because of use of `@DBParameter` +struct DBConnection { + @DBParameter private var connectionID: Int + + func executeQuery(_ query: String) -> [DBRow] { /* implementation here */ } +} + + +// In some other file... + +@DatabaseActor +func fetchOrdersFromDatabase() async -> [Order] { + let connection = DBConnection() + + // No 'await' needed here, because 'connection' is also isolated to `DatabaseActor`. + connection.executeQuery("...") +} +``` + +Removing the property wrapper on `DBConnection.connectionID` would remove the inferred actor isolation of `DBConnection`, which would in turn cause `fetchOrdersFromDatabase` to fail to compile. **It's unprecedented in Swift that changes to a _private_ property should cause compilation errors in some entirely separate file**. Upward inference of actor isolation (from property wrappers to their containing type) means that we can no longer locally reason about the effects of even *private* properties within a type. Instead, we get "spooky action at a distance". + +### Does this cause actual problems? + +This behavior has caused quite a bit of confusion in the community. For example, see [this tweet](https://twitter.com/teilweise/status/1580105376913297409?s=61&t=hwuO4NDJK1aIxSntRwDuZw), [this blog post](https://oleb.net/2022/swiftui-task-mainactor/), and [this entire Swift Forums thread](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821). One particular callout comes from [this post](https://forums.swift.org/t/reconsider-inference-of-global-actor-based-on-property-wrappers/60821/6/), where this inference made it hard to adopt Swift Concurrency in some cases, because the actor isolation goes "viral" beyond the intended scope: + +```swift +class MyContainer { + let contained = Contained() // error: Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context +} + +class Contained { + @OnMainThread var i = 1 +} +``` + +The author created an `@OnMainThread` property wrapper, intended to declare that a particular property was isolated to the main thread. However, they cannot enforce that by using `@MainActor` within the property wrapper, because doing so causes the entire contained type to become unexpectedly isolated. + +It's not clear why this upward inference based on property wrappers was initially proposed. No discussion of that aspect was found during the Global Actors review. We can speculate that it may have been intended to make it easier to interact with SwiftUI's `@ObservedObject` when that first rolled out. But it's not clear it actually makes anything significantly easier; it only saves us from writing a single annotation on the type, and the loss of that annotation introduces violations of the [principle of least surprise](https://en.wikipedia.org/wiki/Principle_of_least_astonishment). + + +## Proposed solution + +The proposal is simple: In the Swift 6 language mode, property wrappers used within a type will not affect the type's actor isolation. We simply disable this inference step entirely. + +In the Swift 5 language mode, isolation will continue to be inferred as it currently is. The new behavior can be requested using the **`-enable-upcoming-feature DisableActorIsolationFromPropertyWrapperUsage`** compiler flag. + +## Detailed design + +[`ActorIsolationRequest.getIsolationFromWrappers()`](https://github.com/apple/swift/blob/85d59d2e55e5e063c552c15f12a8abe933d8438a/lib/Sema/TypeCheckConcurrency.cpp#L3618) implements the actor isolation inference described in this proposal. That function will be adjusted to avoid producing any inference when running in the Swift 6 language mode or when the compiler flag described above is passed. + +## Source compatibility + +This change _does_ introduce potential for source incompatibility, because there may be code which was relying on the inferred actor isolation. That code can be explicitly annotated with the desired global actor in a source-compatible way right now. For example, if a type is currently inferred to have `@MainActor` isolation, you could explicitly declare that isolation on the type right now to avoid source compatibility. (See note about warnings in Alternatives Considered.) + +There may be cases where the source incompatibility could be mitigated by library authors in a source-compatible way. For example, if Apple chose to make SwiftUI's `View` protocol `@MainActor`-isolated, then all conforming types would consistently be isolated to the Main Actor, rather than being inconsistently isolated based on the usage of certain property wrappers. This proposal only notes that this mitigation may be _possible_, but does not make any recommendation as to whether that is necessary. + +### Source compatibility evaluation + +In an effort to determine the practical impact of this change, I used a macOS toolchain containing these changes evaluated various open-source Swift projects (from the Swift Source Compatibility Library and elsewhere). I found no instances of actual source incompatibility as a result of the proposed changes. Most open-source projects are libraries that use no property wrappers at all, but I tried to specifically seek out a few projects that *do* use property wrappers and may be affected by this change. The results are as follows: + +Project | Outcome | Notes +---|---|--- +[ACNHBrowserUI](https://github.com/Dimillian/ACHNBrowserUI) | Fully Compatible | Uses SwiftUI property wrappers +[AlamoFire](https://github.com/Alamofire/Alamofire) | Fully Compatible | Uses custom property wrappers, but none are actor isolated +[Day One (Mac)](https://dayoneapp.com) | Fully Compatible | Uses SwiftUI property wrappers. (Not open source) +[Eureka](https://github.com/xmartlabs/Eureka) | Fully Compatible | Does not use property wrappers at all +[NetNewsWire](https://github.com/Alamofire/Alamofire) | Fully Compatible | Uses SwiftUI property wrappers +[swift-nio](https://github.com/apple/swift-nio) | Fully Compatible | Does not use property wrappers at all +[SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) | Fully Compatible | Does not use property wrappers at all +[XcodesApp](https://github.com/RobotsAndPencils/XcodesApp) | Fully Compatible | Uses SwiftUI property wrappers + +All of the above had a `Swift Concurrency Checking` setting of **Minimal** by default. When I changed the concurrency checking level to **Targeted**, all of the above continued to compile with no errors, both with and without the proposed changes. + +When I changed the concurrency checking level to **Complete**, most of the above projects had compilation errors, _even without the changes proposed here_. The changes proposed here likely contributed a few _additional_ errors under "Complete" checking, but they did not break source compatibility in projects that would have otherwise been source compatible. + +## Effect on ABI stability + +This change is ABI stable, as the actor isolation of a type is not reflected in its runtime calling convention in any way. + +## Effect on API resilience + +This proposal has no effect on API resilience. + +## Alternatives considered + +#### Warn about Property Wrapper-based inference in Swift 5 + +In certain cases, we produce a warning that code will become invalid in a future Swift release. (For example, this has been done with the planned changes to Swift Concurrency in Swift 6.) I considered adding a warning to the Swift 5 language mode along these lines: + +```swift + +// ⚠️ Warning: `MyView` is inferred to use '@MainActor' isolation because +// it uses `@StateObject`. This inference will go away in Swift 6. +// +// Add `@MainActor` to the type to silence this warning. + +struct MyView: View { + @StateObject private var model = Model() + + var body: some View { + Text("Hello") + } +} +``` + +However, I found two problems: + +1. This would produce a _lot_ of warnings, even in code that will not break under the Swift 6 language mode. + +2. There's no way to silence this warning _without_ isolating the type. If I actually _didn't_ want the type to be isolated, there's no way to express that. You can't declare a non-isolated type: + +```swift +nonisolated // 🛑 Error: 'nonisolated' modifier cannot be applied to this declaration +struct MyView: View { + /* ... */ +} +``` + +Given that users cannot silence the warning in a way that matches the new Swift 6 behavior, it seems inappropriate to produce a warning here. + + +## Acknowledgments + +Thanks to Dave DeLong for reviewing this proposal, and to the many members of the Swift community who have engaged in discussion on this topic. From 14c5fc239d3cfc0142422f1ff77982f0175db97b Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 16 Jun 2023 00:00:10 -0700 Subject: [PATCH 3179/4563] SE-0401: fix pitch link and add review link. (#2083) --- proposals/0401-remove-property-wrapper-isolation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0401-remove-property-wrapper-isolation.md b/proposals/0401-remove-property-wrapper-isolation.md index 4fa01c9a74..181dd13f61 100644 --- a/proposals/0401-remove-property-wrapper-isolation.md +++ b/proposals/0401-remove-property-wrapper-isolation.md @@ -5,7 +5,7 @@ * Review Manager: [Holly Borla](https://github.com/hborla) * Status: **Active review (June 16th...June 30th, 2023)** * Implementation: [apple/swift#63884](https://github.com/apple/swift/pull/63884) -* Review: ([pitch]([https://forums.swift.org/t/pitch-init-accessors/64881](https://forums.swift.org/t/pitch-stop-inferring-actor-isolation-based-on-property-wrapper-usage/63262))) +* Review: ([pitch](https://forums.swift.org/t/pitch-stop-inferring-actor-isolation-based-on-property-wrapper-usage/63262)) ([review](https://forums.swift.org/t/se-0401-remove-actor-isolation-inference-caused-by-property-wrappers/65618)) - ## Introduction [SE-0316: Global Actors](https://github.com/apple/swift-evolution/blob/main/proposals/0316-global-actors.md) introduced annotations like `@MainActor` to isolate a type, function, or property to a particular global actor. It also introduced various rules for how that global actor isolation could be inferred. One of those rules was: @@ -138,7 +130,7 @@ The [original motivation](https://forums.swift.org/t/se-0401-remove-actor-isolat The proposal is simple: In the Swift 6 language mode, property wrappers used within a type will not affect the type's actor isolation. We simply disable this inference step entirely. -In the Swift 5 language mode, isolation will continue to be inferred as it currently is. The new behavior can be requested using the **`-enable-upcoming-feature DisableActorIsolationFromPropertyWrapperUsage`** compiler flag. +In the Swift 5 language mode, isolation will continue to be inferred as it currently is. The new behavior can be requested using the **`-enable-upcoming-feature DisableActorInferenceFromPropertyWrapperUsage`** compiler flag. ## Detailed design From f0cc8a4f621e43a50220967c765ef73b1eba9722 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 29 Jun 2023 07:25:40 -0700 Subject: [PATCH 3192/4563] Mark SE-0399 as implemented in Swift 5.9 (#2093) --- proposals/0399-tuple-of-value-pack-expansion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0399-tuple-of-value-pack-expansion.md b/proposals/0399-tuple-of-value-pack-expansion.md index 96e9b2c922..8918268825 100644 --- a/proposals/0399-tuple-of-value-pack-expansion.md +++ b/proposals/0399-tuple-of-value-pack-expansion.md @@ -3,7 +3,7 @@ * Proposal: [SE-0399](0399-tuple-of-value-pack-expansion.md) * Authors: [Sophia Poirier](https://github.com/sophiapoirier), [Holly Borla](https://github.com/hborla) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: On `main` gated behind `-enable-experimental-feature VariadicGenerics` * Previous Proposals: [SE-0393](0393-parameter-packs.md), [SE-0398](0398-variadic-types.md) * Review: ([pitch](https://forums.swift.org/t/tuple-of-value-pack-expansion/64269)) ([review](https://forums.swift.org/t/se-0399-tuple-of-value-pack-expansion/65017)) ([acceptance](https://forums.swift.org/t/accepted-se-0399-tuple-of-value-pack-expansion/65271)) From b9a7a55ff4d75afe9677383fc0eaadac4998b450 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Thu, 29 Jun 2023 10:27:41 -0400 Subject: [PATCH 3193/4563] Update 0396-never-codable.md (#2094) --- proposals/0396-never-codable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0396-never-codable.md b/proposals/0396-never-codable.md index 176358b0fa..be3ecd86ea 100644 --- a/proposals/0396-never-codable.md +++ b/proposals/0396-never-codable.md @@ -3,7 +3,7 @@ * Proposal: [SE-0396](0396-never-codable.md) * Author: [Nate Cook](https://github.com/natecook1000) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: [apple/swift#64899](https://github.com/apple/swift/pull/64899) * Review: ([pitch](https://forums.swift.org/t/pitch-conform-never-to-codable/64056)) ([review](https://forums.swift.org/t/se-0396-conform-never-to-codable/64469)) ([acceptance](https://forums.swift.org/t/accepted-se-0396-conform-never-to-codable/64848)) From f28452d655fcdfab67c43befbb62bc6c34f6ffb4 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 29 Jun 2023 15:06:44 -0700 Subject: [PATCH 3194/4563] SE-0366, 0377, and 0390 are implemented --- proposals/0366-move-function.md | 4 ++-- proposals/0377-parameter-ownership-modifiers.md | 4 ++-- proposals/0390-noncopyable-structs-and-enums.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/0366-move-function.md b/proposals/0366-move-function.md index 194be7e2f7..cf0322911a 100644 --- a/proposals/0366-move-function.md +++ b/proposals/0366-move-function.md @@ -3,8 +3,8 @@ * Proposal: [SE-0366](0366-move-function.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Joe Groff](https://github.com/jckarter) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Accepted** -* Implementation: Implemented on main as stdlib SPI (`_move` instead of `consume` keyword) +* Status: **Implemented (Swift 5.9)** +* Implementation: in main branch of compiler * Review: ([pitch](https://forums.swift.org/t/pitch-move-function-use-after-move-diagnostic/53983)) ([first review](https://forums.swift.org/t/se-0366-move-function-use-after-move-diagnostic/59202)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0366-move-operation-use-after-move-diagnostic/59687)) ([second review](https://forums.swift.org/t/se-0366-second-review-take-operator-to-end-the-lifetime-of-a-variable-binding/61021)) ([third review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)), ([acceptance](https://forums.swift.org/t/accepted-se-0366-consume-operator-to-end-the-lifetime-of-a-variable-binding/62758)) * Previous Revisions: * [1](https://github.com/apple/swift-evolution/blob/567fb1a66c784bcc5394491d24f72a3cb393674f/proposals/0366-move-function.md) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index 8028de627a..6b962fa201 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -3,8 +3,8 @@ * Proposal: [SE-0377](0377-parameter-ownership-modifiers.md) * Authors: [Michael Gottesman](https://github.com/gottesmm), [Joe Groff](https://github.com/jckarter) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** -* Implementation: Swift 5.9, without no-implicit-copy constraint +* Status: **Implemented (Swift 5.9)** +* Implementation: in main branch of compiler * Review: ([first pitch](https://forums.swift.org/t/pitch-formally-defining-consuming-and-nonconsuming-argument-type-modifiers/54313)) ([second pitch](https://forums.swift.org/t/borrow-and-take-parameter-ownership-modifiers/59581)) ([first review](https://forums.swift.org/t/se-0377-borrow-and-take-parameter-ownership-modifiers/61020)) ([second review](https://forums.swift.org/t/combined-se-0366-third-review-and-se-0377-second-review-rename-take-taking-to-consume-consuming/61904)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0377-borrowing-and-consuming-parameter-ownership-modifiers/62759)) ([revision and third review](https://forums.swift.org/t/se-0377-revision-make-borrowing-and-consuming-parameters-require-explicit-copying-with-the-copy-operator/64996)) ([revision acceptance](https://forums.swift.org/t/accepted-se-0377-revision-make-borrowing-and-consuming-parameters-require-explicit-copying-with-the-copy-operator/65293)) * Previous Revisions: ([as of first review](https://github.com/apple/swift-evolution/blob/3f984e6183ce832307bb73ec72c842f6cb0aab86/proposals/0377-parameter-ownership-modifiers.md)) ([as of second review](https://github.com/apple/swift-evolution/blob/7e1d16316e5f68eb94546df9241aa6b4cacb9411/proposals/0377-parameter-ownership-modifiers.md)) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index 7c23718e4c..b6e5455912 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -3,8 +3,8 @@ * Proposal: [SE-0390](0390-noncopyable-structs-and-enums.md) * Authors: [Joe Groff](https://github.com/jckarter), [Michael Gottesman](https://github.com/gottesmm), [Andrew Trick](https://github.com/atrick), [Kavon Farvardin](https://github.com/kavon) * Review Manager: [Stephen Canon](https://github.com/stephentyrone) -* Status: **Accepted** -* Implementation: on main as `@_moveOnly` behind the `-enable-experimental-move-only` option +* Status: **Implemented (Swift 5.9)** +* Implementation: in main branch of compiler * Review: ([pitch](https://forums.swift.org/t/pitch-noncopyable-or-move-only-structs-and-enums/61903)) ([first review](https://forums.swift.org/t/se-0390-noncopyable-structs-and-enums/63258)) ([second review](https://forums.swift.org/t/second-review-se-0390-noncopyable-structs-and-enums/63866)) ([acceptance](https://forums.swift.org/t/accepted-se-0390-noncopyable-structs-and-enums/65157)) * Previous Revisions: [1](https://github.com/apple/swift-evolution/blob/5d075b86d57e3436b223199bd314b2642e30045f/proposals/0390-noncopyable-structs-and-enums.md) From d2e2ae85a50d3e669ef7a7fecbbd7dec9e19bf29 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 3 Jul 2023 11:09:58 -0700 Subject: [PATCH 3195/4563] Update proposals/NNNN-extension-macros.md Co-authored-by: John McCall --- proposals/NNNN-extension-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-extension-macros.md b/proposals/NNNN-extension-macros.md index 9eadb11136..453560196b 100644 --- a/proposals/NNNN-extension-macros.md +++ b/proposals/NNNN-extension-macros.md @@ -27,7 +27,7 @@ struct S {} extension S: Equatable {} ``` -However, the `conformance` macro role is extremely limited on its own. They _only_ have the ability to return a protocol name and the syntax for a `where` clause. If the protocol conformance requires members -- as most protocol conformances do -- those must be added through a separate `member` macro role. +However, the `conformance` macro role is extremely limited on its own. A conformance macro _only_ has the ability to return a protocol name and the syntax for a `where` clause. If the protocol conformance requires members --- as most protocol conformances do --- those must be added through a separate `member` macro role. More importantly, conformance macros are the only way for a macro to expand to an extension on the annotated type. The inability to add members in an extension of a type rather than the primary declaration is a serious limitation of the macro system, because extensions have several important semantic implications, including (but not limited to): From 17f9c5f6e9ad29f5b18bd43f27bbe7efe62c9bbd Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 3 Jul 2023 11:10:09 -0700 Subject: [PATCH 3196/4563] Update proposals/NNNN-extension-macros.md Co-authored-by: John McCall --- proposals/NNNN-extension-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-extension-macros.md b/proposals/NNNN-extension-macros.md index 453560196b..47e91544ef 100644 --- a/proposals/NNNN-extension-macros.md +++ b/proposals/NNNN-extension-macros.md @@ -39,7 +39,7 @@ Extensions also have stylistic benefits. Code inside an extension will share the ## Proposed solution -This proposal removes the `conformance` macro role in favor or an `extension` macro role. An `extension` macro role can be used with the `@attached` macro attribute, and it can add a conformance, a where clause, and a member list in an extension on the type the macro is attached to: +This proposal removes the `conformance` macro role in favor of an `extension` macro role. An `extension` macro role can be used with the `@attached` macro attribute, and it can add a conformance, a `where` clause, and a member list in an extension on the type the macro is attached to: ```swift protocol MyProtocol { From d2b22bfab901871db27dc499910bc90372145d0e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 3 Jul 2023 11:30:41 -0700 Subject: [PATCH 3197/4563] Clarify the restrictions on extension macros. --- proposals/NNNN-extension-macros.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/proposals/NNNN-extension-macros.md b/proposals/NNNN-extension-macros.md index 47e91544ef..8a10fe55d9 100644 --- a/proposals/NNNN-extension-macros.md +++ b/proposals/NNNN-extension-macros.md @@ -63,16 +63,26 @@ The generated extensions of the macro must only extend the type the macro is att ## Detailed design -### Specifying macro-introduced protocol conformances +### Specifying macro-introduced protocol conformances and member names SE-0389 states that whenever a macro produces declarations that are visible to other Swift code, it is required to declare the names in advance. This rule also applies to extension macros, which must specify: * Declarations inside the extension, which can be specified using `named`, `prefixed`, `suffixed`, and `arbitrary`. -* The names of protocols that are listed in the extension's conformance clause. +* The names of protocols that are listed in the extension's conformance clause. These protocols are specified in the `conformances:` list of the `@attached(conformances:)` attribute. Each name that appears in this list must be a conformance constraint, where a conformance constraint is one of: + * A protocol name + * A typealias whose underlying type is a conformance constraint + * A protocol composition whose entires are each a conformance constraint -It is an error for a macro to add a conformance or an extension member that is not covered by the `@attached(extension)` attribute. +The following restrictions apply to generated conformances and names listed in `@attached(extension)`: + +* An extension macro cannot add a conformance to a protocol that is not covered by the `conformances:` list in `@attached(extension, conformnaces:)`. +* An extension macro cannot add a member that is not covered by the `names:` list in `@attached(extension, names:)`. +* An extension macro cannot introduce an extension with an attached `peer` macro, because the peer-macro-generated names are not covered by the original `@attached(extension)` attribute. + +### Extension macro application + +Extension macros can only be attached to the primary declaration of a nominal type; they cannot be attached to typealias or extension declarations. -### Extension macros applied to nested types Extensions are only valid at the top-level. When an extension macro is applied to a nested type: From d5f50e998d00230b8aec65d394e394d87c319c01 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 3 Jul 2023 11:53:20 -0700 Subject: [PATCH 3198/4563] Add a section about extension macro implementations suppressing conformances that are already stated in the original source. --- proposals/NNNN-extension-macros.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-extension-macros.md b/proposals/NNNN-extension-macros.md index 8a10fe55d9..fa94cb28ae 100644 --- a/proposals/NNNN-extension-macros.md +++ b/proposals/NNNN-extension-macros.md @@ -132,6 +132,9 @@ public protocol ExtensionMacro: AttachedMacro { /// - node: The custom attribute describing the attached macro. /// - declaration: The declaration the macro attribute is attached to. /// - type: The type to provide extensions of. + /// - protocols: The list of protocols to add conformances to. These will + /// always be protocols that `type` does not already state a conformance + /// to. /// - context: The context in which to perform the macro expansion. /// /// - Returns: the set of extensions declarations introduced by the macro, @@ -140,7 +143,8 @@ public protocol ExtensionMacro: AttachedMacro { static func expansion( of node: AttributeSyntax, attachedTo declaration: some DeclGroupSyntax, - providingExtensionsOf type: some TypeSyntaxProtocol + providingExtensionsOf type: some TypeSyntaxProtocol, + conformingTo protocols: [TypeSyntax], in context: some MacroExpansionContext ) throws -> [ExtensionDeclSyntax] } @@ -157,6 +161,29 @@ struct Outer { The type syntax passed to `ExtensionMacro.expansion` for `providingExtensionsOf` is `Outer.Inner`. +#### Suppressing redundant conformances + +The `conformingTo:` parameter of `ExtensionMacro.expansion` allows extension macros to suppress generating conformances that are already stated in the original source code. The `conformingTo:` argument array will contain only the protocols from the `conformances:` list in `@attached(extension conformances:)` that the type does not already conform to in the original source code, including through implied conformances or class inheritance. + +For example, consider the following code which contains an attached extension macro: + +```swift +protocol Encodable {} +protocol Decodable {} + +typelias Codable = Encodable & Decodable + +@attached(extension, conformances: Codable) +macro MyMacro() = #externalMacro(...) + +@MyMacro +struct S { ... } + +extension S: Encodable { ... } +``` + +The extension macro can add conformances to `Codable`, aka `Encodable & Decodable`. Because the struct `S` already conforms to `Encodable` in the original source, the `ExtensionMacro.expansion` method will recieve the argument `[TypeSyntax(Encodable)]` for the `conformingTo:` parameter. Using this information, the macro implementation can decide to only add an extension with a conformance to `Decodable`. + ## Source compatibility This propsoal removes the `conformance` macro role from SE-0389, which is accepted and implemented in Swift 5.9. If this proposal is accepted after 5.9, the `conformance` macro role will remain in the language as sugar for an `extension` macro that adds only a conformance. From 8160a0c57db69189472db29dc6b04ad17f1cfb24 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 3 Jul 2023 12:33:36 -0700 Subject: [PATCH 3199/4563] Update proposals/NNNN-extension-macros.md Co-authored-by: John McCall --- proposals/NNNN-extension-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-extension-macros.md b/proposals/NNNN-extension-macros.md index fa94cb28ae..11438b2e21 100644 --- a/proposals/NNNN-extension-macros.md +++ b/proposals/NNNN-extension-macros.md @@ -84,7 +84,7 @@ The following restrictions apply to generated conformances and names listed in ` Extension macros can only be attached to the primary declaration of a nominal type; they cannot be attached to typealias or extension declarations. -Extensions are only valid at the top-level. When an extension macro is applied to a nested type: +Swift only allows `extension` declarations at the top level in a file. Despite this, extension macros can be applied to a nested type: ```swift @attached(extension, conformances: MyProtocol, names: named(requirement)) From cb71244fb4bb0dcb1bc6a7fef6da9dc8449a249d Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 3 Jul 2023 12:33:43 -0700 Subject: [PATCH 3200/4563] Update proposals/NNNN-extension-macros.md Co-authored-by: John McCall --- proposals/NNNN-extension-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-extension-macros.md b/proposals/NNNN-extension-macros.md index 11438b2e21..d56f95103d 100644 --- a/proposals/NNNN-extension-macros.md +++ b/proposals/NNNN-extension-macros.md @@ -96,7 +96,7 @@ struct Outer { } ``` -The macro expansion containing the extension is inserted at the top-level. The above code expands to: +In this situation, the macro expansion containing the `extension` is inserted at the top level of the file, instead of immediately where the macro is invoked, where the `extension` would be invalid. The above code expands to: ```swift struct Outer { From cca23d643fee9a6453c0bba3cef6ece9be3306db Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 3 Jul 2023 15:41:30 -0400 Subject: [PATCH 3201/4563] Assign SE-0402 to the extension macros proposal and put it in review --- .../{NNNN-extension-macros.md => 0402-extension-macros.md} | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename proposals/{NNNN-extension-macros.md => 0402-extension-macros.md} (98%) diff --git a/proposals/NNNN-extension-macros.md b/proposals/0402-extension-macros.md similarity index 98% rename from proposals/NNNN-extension-macros.md rename to proposals/0402-extension-macros.md index d56f95103d..0d85d89791 100644 --- a/proposals/NNNN-extension-macros.md +++ b/proposals/0402-extension-macros.md @@ -1,13 +1,12 @@ # Generalize `conformance` macros as `extension` macros -* Proposal: [SE-NNNN](NNNN-extension-macros.md) +* Proposal: [SE-0402](0402-extension-macros.md) * Authors: [Holly Borla](https://github.com/hborla) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Awaiting implementation** +* Status: **Active Review (July 3rd...17th, 2023)** * Implementation: [apple/swift#66967](https://github.com/apple/swift/pull/66967), [apple/swift-syntax#1859](https://github.com/apple/swift-syntax/pull/1859) * Review: ([pitch](https://forums.swift.org/t/pitch-generalize-conformance-macros-as-extension-macros/65653)) - ## Introduction This proposal generalizes the `conformance` macro role as an `extension` macro role that can add a member list to an extension in addition to a protocol and `where` clause. @@ -198,4 +197,4 @@ The adoption implications for using extensions macros are the same as writing th ## Acknowledgments -Thank you to Gwendal Roué for inspiring the idea of `extension` macros by suggesting combining `member` macros and `conformance` macros. \ No newline at end of file +Thank you to Gwendal Roué for inspiring the idea of `extension` macros by suggesting combining `member` macros and `conformance` macros. From 4c6df6518ffefa61456044c1a9baf3f587fcd325 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 3 Jul 2023 15:50:28 -0400 Subject: [PATCH 3202/4563] Link SE-0402 to its review thread --- proposals/0402-extension-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0402-extension-macros.md b/proposals/0402-extension-macros.md index 0d85d89791..4c41ecbe2a 100644 --- a/proposals/0402-extension-macros.md +++ b/proposals/0402-extension-macros.md @@ -5,7 +5,7 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active Review (July 3rd...17th, 2023)** * Implementation: [apple/swift#66967](https://github.com/apple/swift/pull/66967), [apple/swift-syntax#1859](https://github.com/apple/swift-syntax/pull/1859) -* Review: ([pitch](https://forums.swift.org/t/pitch-generalize-conformance-macros-as-extension-macros/65653)) +* Review: ([pitch](https://forums.swift.org/t/pitch-generalize-conformance-macros-as-extension-macros/65653)) ([review](https://forums.swift.org/t/se-0402-generalize-conformance-macros-as-extension-macros/65965)) ## Introduction From 4dfb497d263e01f8249a6e0f94de77b4f541d52f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 3 Jul 2023 19:20:14 -0700 Subject: [PATCH 3203/4563] [SE-0400] Updates to the init accessors proposal (#2096) * [SE-0400] Use `@storageRestrictions` attribute instead of effects. The initializes and accesses effects didn't match well with other effects in the language (e.g., `throws` and `async`). Instead, use an attribute with "initializes" and "accesses" arguments. * [SE-0400] Add section on init accessors for computed properties. * [SE-400] Add section on init accessors for read-only properties. * [SE-0400] Allow reordering of the initializations in the synthesized memberwise initializer to respect `accesses` restrictions. * Add a potential future direction for the generalization of storage restrictions to other functions. * [SE-0400] Clarify the behavior of properties that have init accessors and initial values. * [SE-0400] Fix getter/setter typo --------- Co-authored-by: Frederick Kellison-Linn --- proposals/0400-init-accessors.md | 285 +++++++++++++++++++++++++++---- 1 file changed, 256 insertions(+), 29 deletions(-) diff --git a/proposals/0400-init-accessors.md b/proposals/0400-init-accessors.md index 8e5179238e..8a5d45c22d 100644 --- a/proposals/0400-init-accessors.md +++ b/proposals/0400-init-accessors.md @@ -59,7 +59,8 @@ This proposal adds _`init` accessors_ to opt computed properties on types into d struct Angle { var degrees: Double var radians: Double { - init(initialValue) initializes(degrees) { + @storageRestrictions(initializes: degrees) + init(initialValue) { degrees = initialValue * 180 / .pi } @@ -77,7 +78,7 @@ struct Angle { } ``` -The signature of an `init` accessor specifies up to two sets of stored properties: the properties that are accessed (via `accesses`) and the properties that are initialized (via `initializes`) by the accessor. `initializes` and `accesses` are side-effects of the `init` accessor. Access effects specify the other stored properties that can be accessed from within the `init` accessor (no other uses of `self` are allowed), and therefore must be initialized before the computed property's `init` accessor is invoked. The `init` accessor must initialize each of the initialized stored properties on all control flow paths. The `radians` property in the example above specifies no access effect, but initializes the `degrees` property, so it specifies only `initializes(degrees)`. +The signature of an `init` accessor specifies up to two sets of stored properties: the properties that are accessed (via `accesses`) and the properties that are initialized (via `initializes`) by the accessor. `initializes` and `accesses` are side-effects of the `init` accessor. Access effects specify the other stored properties that can be accessed from within the `init` accessor (no other uses of `self` are allowed), and therefore must be initialized before the computed property's `init` accessor is invoked. The `init` accessor must initialize each of the initialized stored properties on all control flow paths. The `radians` property in the example above specifies no access effect, but initializes the `degrees` property, so it specifies only `initializes: degrees`. Access effects allow a computed property to be initialized by placing its contents into another stored property: @@ -86,7 +87,8 @@ struct ProposalViaDictionary { private var dictionary: [String: String] var title: String { - init(newValue) accesses(dictionary) { + @storageRestrictions(accesses: dictionary) + init(newValue) { dictionary["title"] = newValue } @@ -95,7 +97,8 @@ struct ProposalViaDictionary { } var text: String { - init(newValue) accesses(dictionary) { + @storageRestrictions(accesses: dictionary) + init(newValue) { dictionary["text"] = newValue } @@ -126,7 +129,8 @@ struct Wrapper { struct S { private var _value: Wrapper var value: Int { - init(newValue) initializes(_value) { + @storageRestrictions(initializes: _value) + init(newValue) { self._value = Wrapper(wrappedValue: newValue) } @@ -152,17 +156,13 @@ This proposal allows macros to model the following property-wrapper-like pattern ### Syntax -This proposal adds new syntax for `init` accessor blocks, which can be written in the accessor list of a computed property. Init accessors add the following production rules to the grammar: +The proposal adds a new kind of accessor, an `init` accessor, which can be written in the accessor list of a computed property. Init accessors add the following production rules to the grammar: ``` -init-accessor -> 'init' init-accessor-parameter[opt] init-effect[opt] access-effect[opt] function-body +init-accessor -> 'init' init-accessor-parameter[opt] function-body init-accessor-parameter -> '(' identifier ')' -init-effect -> 'initializes' '(' identifier-list ')' - -access-effect -> 'accesses' '(' identifier-list ')' - accessor-block -> init-accessor ``` @@ -180,9 +180,22 @@ struct Minimal { } ``` -### `init` accessor signatures +This proposal also adds a new `storageRestrictions` attribute to describe the storage restrictions for `init` accessor blocks. The attribute can only be used on `init` accessors. The attribute is described by the following production rules in the grammar: + +``` +attribute ::= storage-restrictions-attribute + +storage-restrictions-attribute ::= '@' storageRestrictions '(' storage-restrictions[opt] ')' -`init` accessor declarations can optionally specify a signature. An `init` accessor signature is composed of a parameter list, followed by an initialization effects specifier clause. The initialization effects can include a list of stored properties that are initialized by this accessor specified in the argument list of the contextual `initializes` keyword, and a list of stored properties that are accessed by this accessor specified in the argument list of the contextual `accesses` keyword, each of which are optional: +storage-restrictions-initializes ::= 'initializes' ':' identifier-list +storage-restrictions-accesses ::= 'accesses' ':' identifier-list + +storage-restrictions ::= storage-restrictions-accesses +storage-restrictions ::= storage-restrictions-initializes +storage-restrictions ::= storage-restrictions-initializes ',' storage-restrictions-accesses +``` + +The storage restriction attribute can include a list of stored properties that are initialized by this accessor (the identifier list in `storage-restrictions-initializes`), and a list of stored properties that are accessed by this accessor (the identifier list in `storage-restrictions-accesses`), each of which are optional: ```swift struct S { @@ -191,7 +204,8 @@ struct S { var _x: Int var x: Int { - init(newValue) initializes(_x) accesses(readMe) { + @storageRestrictions(initializes: _x, accesses: readMe) + init(newValue) { print(readMe) _x = newValue } @@ -204,9 +218,9 @@ struct S { If the accessor uses the default parameter name `newValue` and neither initializes nor accesses any stored property, the signature is not required. -Init accessors can subsume the initialization of a set of stored properties. Subsumed stored properties are specified through the `initializes` effect. The body of an `init` accessor is required to initialize the subsumed stored properties on all control flow paths. +Init accessors can subsume the initialization of a set of stored properties. Subsumed stored properties are specified through the `initializes` argument to the attribute. The body of an `init` accessor is required to initialize the subsumed stored properties on all control flow paths. -Init accessors can also require a set of stored properties to already be initialized when the body is evaluated, which are specified through the `accesses` effect. These stored properties can be accessed in the accessor body; no other properties or methods on `self` are available inside the accessor body, nor is `self` available as a whole object (i.e., to call methods on it). +Init accessors can also require a set of stored properties to already be initialized when the body is evaluated, which are specified through the `accesses` argument to the attribute. These stored properties can be accessed in the accessor body; no other properties or methods on `self` are available inside the accessor body, nor is `self` available as a whole object (i.e., to call methods on it). ### Definite initialization of properties on `self` @@ -222,8 +236,10 @@ An assignment to a computed property with an `init` accessor before all of `self struct S { var x1: Int var x2: Int + + @storageRestrictions(initializes: x1, x2) var computed: Int { - init(newValue) initializes(x1, x2) { ... } + init(newValue) { ... } } init() { @@ -238,8 +254,10 @@ An assignment to a computed property that has not been initialized on all paths struct S { var x: Int var y: Int + + @storageRestrictions(initializes: x, y) var point: (Int, Int) { - init(newValue) initializes(x, y) { + init(newValue) { (self.x, self.y) = newValue } get { (x, y) } @@ -267,8 +285,10 @@ struct S { var x1: Int var x2: Int var x3: Int + + @storageRestrictions(initializes: x1, x2) var computed: Int { - init(newValue) initializes(x1, x2) { ... } + init(newValue) { ... } } init() { @@ -285,8 +305,10 @@ An assignment to a computed property where at least one of the stored properties struct S { var x: Int var y: Int + + @storageRestrictions(initializes: x, y) var point: (Int, Int) { - init(newValue) initializes(x, y) { + init(newValue) { (self.x, self.y) = newValue } get { (x, y) } @@ -302,13 +324,15 @@ struct S { ### Memberwise initializers -If a struct does not declare its own initializers, it receives an implicit memberwise initializer based on the stored properties of the struct, because the storage is what needs to be initialized. Because many use-cases for `init` accessors are fully abstracting a single computed property to be backed by a single stored property, such as in the property-wrapper use case, an `init` accessor provides a preferred mechansim for initializing storage because the programmer will primarily interact with that storage through the computed property. As such, the memberwise initializer parameter list will include any computed properties that subsume the initialization of stored properties instead of parameters for those stored properties. +If a struct does not declare its own initializers, it receives an implicit memberwise initializer based on the stored properties of the struct, because the storage is what needs to be initialized. Because many use-cases for `init` accessors are fully abstracting a single computed property to be backed by a single stored property, such as in the property-wrapper use case, an `init` accessor provides a preferred mechanism for initializing storage because the programmer will primarily interact with that storage through the computed property. As such, the memberwise initializer parameter list will include computed properties that have init accessors along with only those stored properties that have not been subsumed by an init accessor. ```swift struct S { var _x: Int + + @storageRestrictions(initializes: _x) var x: Int { - init(newValue) initializes(_x) { + init(newValue) { _x = newValue } @@ -331,13 +355,15 @@ init(x: Int, y: Int) { } ``` -A memberwise initializer will not be synthesized if a stored property that is an `accesses` effect of a computed property is ordered after that computed property in the source code: +The parameters of the memberwise initializer follow source order. However, if an init accessor `accesses` a stored property that precedes it in the memberwise initializer, then the properties cannot be initialized in the same order as the parameters occur in the memberwise initializer. For example: ```swift struct S { var _x: Int + + @storageRestrictions(initializes: _x, accesses: y) var x: Int { - init(newValue) initializes(_x) accesses(y) { + init(newValue) { _x = newValue } @@ -349,7 +375,7 @@ struct S { } ``` -The above struct would receive the following memberwise initializer, which is invalid so an error is emitted: +If the memberwise initializer of the above struct were written to initialize the properties in the same order as the parameters, it would produce an error: ```swift init(x: Int, y: Int) { @@ -358,7 +384,104 @@ init(x: Int, y: Int) { } ``` -Use cases for `init` accessors that provide a projection of a stored property as various units through several computed properties don't have a single preferred unit from which to initialize. Most likely, these use cases want a different member-wise initializer for each unit that you can initialize from. If a type contains several computed properties with `init` accessors that initialize the same stored property, a member-wise initializer will not be synthesized. +Therefore, the compiler will order the initializations in the synthesized memberwise initializer to respect the `accesses` clauses: + +```swift +init(x: Int, y: Int) { + self.y = y + self.x = x +} +``` + +The initial review of this proposal suppressed the memberwise initializer in such cases, based on a concern that out-of-order initialization would cause surprises. However, given the fact that the fields are initialized independently (or have `accessses` relationships that define their relative ordering), and that side effects here are limited to those of the `init` accessors themselves, one has to introduce global side effects during initialization to observe any difference. + +There remain cases where a memberwise initializer cannot be synthesized. For example, if a type contains several computed properties with `init` accessors that initialize the same stored property, it is not clear which computed property should be used within the member-wise initializer. In such cases, a member-wise initializer will not be synthesized. + +### Init accessors on computed properties + +An init accessor can be provided on a computed property, in which case it is used for initialization and as a default argument in the memberwise initializer. For example, given the following: + +```swift +struct Angle { + var degrees: Double + + var radians: Double { + @storageRestrictions(initializes: degrees) + init(initialValue) { + degrees = initialValue * 180 / .pi + } + + get { degrees * .pi / 180 } + set { degrees = newValue * 180 / .pi } + } +} +``` + +### Init accessors for read-only properties + +Init accessors can be provided for properties that lack a setter. Such properties act much like a `let` property, able to be initialized (exactly) once and not set thereafter: + +```swift +struct S { + var _x: Int + + @storageRestrictions(initializes: _x) + var x: Int { + init(initialValue) { + self._x = x + } + + get { _x } + } + + init(halfOf y: Int) { + self.x = y / 2 // okay, calls init accessor for x + self.x = y / 2 // error, 'x' cannot be set + } +} + +``` + +### Initial values on properties with an init accessor + +A property with an init accessor can have an initial value, e.g., + +```swift +struct WithInitialValues { + var _x: Int + + var x: Int = 0 { + @storageRestrictions(initializes: _x) + init(initialValue) { + _x = initialValue + } + + get { ... } + set { ... } + } + + var y: Int +} +``` + +The synthesized memberwise initializer will use the initial value as a default argument, so it will look like the following: + +```swift +init(x: Int = 0, y: Int) { + self.x = x // calls init accessor, which initializes _x + self.y = y +} +``` + +In a manually written initializer, the initial value will be used to initialize the property with the init accessor prior to any user-written code: + +```swift +init() { + // implicitly initializes self.x = 0 + self.y = 10 + self.x = 20 // calls setter +} +``` ## Source compatibility @@ -374,7 +497,9 @@ Because `init` accessors are always called from within the defining module, adop ## Alternatives considered -A previous version of this proposal specified init accessor effects in the parameter list using special labels: +### Syntax for "initializes" and "accesses" + +A number of different syntaxes have been considered for specifying the set of stored properties that are initialized or accessed by a property that has an `init` accessor. The original pitch specified them in the parameter list using special labels: ```swift struct S { @@ -394,14 +519,69 @@ struct S { This syntax choice is misleading because the effects look like function parameters, while `initializes` behaves more like the output of an init accessor, and `accesses` are not explicitly provided at the call-site. Conceptually, `initializes` and `accesses` are side effects of an `init` accessor, so the proposal was revised to place these modifiers in the effects clause. +The first reviewed version of this proposal placed `initializes` and `accesses` along with other *effects*, e.g., + +```swift +struct S { + var _x: Int + var x: Int { + init(newValue) initializes(_x), accesses(y) { + _x = newValue + } + + get { _x } + set { _x = newValue } + } + + var y: Int +} +``` + +However, `initializes` and `effects` don't behave in the same manner as other effects in Swift, such as `throws` and `async`, for several reasons. First, there's no annotation like `try` or `await` at the call site. Second, these aren't part of the type of the entity (e.g., there is not function type that has an `initializes` clause). Therefore, using the effects clause is not a good match for Swift's semantic model. + +The current proposal uses an attribute. With attributes, there is question of whether we can remove the `@` to turn it into a declaration modifier: + +```swift +struct S { + var _x: Int + var x: Int { + storageRestrictions(initializes: _x, accesses: y) + init(newValue) { + _x = newValue + } + + get { _x } + set { _x = newValue } + } + + var y: Int +} +``` + +This is doable within the confines of this proposal's init accessors, but would prevent further extensions of this proposal that would allow the use of `initializes` or `accesses` on arbitrary functions. For example, such an extension might allow the following + +```swift +var _x, _y: Double + +storageRestrictions(initializes: _x, _y) +func initCoordinates(radius: Double, angle: Double) { ... } + +if let (r, theta) = decodeAsPolar() { + initCoordinates(radius: r, angle: theta) +} else { + // ... +} +``` + +However, there is a parsing ambiguity in the above because `storageRestrictions(initializes: _x, _y)` could be a call to a function names `storageRestrictions(initializes:)` or it could be a declaration modifier specifying that `initCoordinates` initializes `_x` and `_y`. + Other syntax suggestions from pitch reviewers included: * Using a capture-list-style clause, e.g. `init { [&x, y] in ... }` -* Attributes on the computed property itself, e.g. `@initializes(_x) var x: Int { ... }` * Using more concise effect names, e.g. `writes` and `reads` instead of `initializes` and `accesses` * And more! -However, the current syntax in this proposal most accurately models the semantics of initialization effects. An `init` accessor is a function -- not a closure -- that has side-effects related to initialization. _Only_ the `init` accessor has these effects; though the `set` accessor often contains code that looks the same as the code in the `init` accessor, the effects of these accessors are different. Because `init` accessors are called before all of `self` is initialized, they do not recieve a fully-initialized `self` as a parameter like `set` accessors do, and assignments to `initializes` stored properties in `init` accessors have the same semantics as that of a standard initializer, such as suppressing `willSet` and `didSet` observers. These reasons reinforce the decision to specify `initializes` and `accesses` in the effects clause of an `init` accessor. +However, the current syntax in this proposal, which uses an attribute, most accurately models the semantics of initialization effects. An `init` accessor is a function -- not a closure -- that has side-effects related to initialization. _Only_ the `init` accessor has these effects; though the `set` accessor often contains code that looks the same as the code in the `init` accessor, the effects of these accessors are different. Because `init` accessors are called before all of `self` is initialized, they do not recieve a fully-initialized `self` as a parameter like `set` accessors do, and assignments to `initializes` stored properties in `init` accessors have the same semantics as that of a standard initializer, such as suppressing `willSet` and `didSet` observers. ## Future directions @@ -409,6 +589,53 @@ However, the current syntax in this proposal most accurately models the semantic `init` accessors for local variables have different implications on definite initialization, because re-writing assignment to `init` or `set` is not based on the initialization state of `self`. Local variable getters and setters can also capture any other local variables in scope, which raises more challenges for diagnosing escaping uses before initialization during the same pass where assignments may be re-written to `init` or `set`. As such, local variables with `init` accessors are a future direction. +### Generalization of storage restrictions to other functions + +In the future, the `storageRestrictions` attribute could be be generalized to apply to other functions. For example, this could allow one to implement a common initialization function within a class: + +```swift +class C { + var id: String + var state: State + + @storageRestrictions(initializes: state, accesses: id) + func initState() { + self.state = /* initialization code here */ + } + + init(id: String) { + self.id = id + initState() // okay, accesses id and initializes state + } +} +``` + +The principles are the same as with `init` accessors: a function's implementation can be restricted to only access certain stored properties, and to initialize others along all paths. A call to the function then participates in definite initialization. + +This generalization comes with limitations that were not relevant to `init` accessors, because the functions are more akin to fragments of an initializer. For example, the `initState` function cannot be called after `state` is initialized (because it would re-initialize `state`), nor can it be used as a "first-class" function: + +```swift + init(id: String) { + self.id = id + initState() // okay, accesses id and initializes state + + initState() // error, 'state' is already initialized + let fn = self.initState // error: can't treat it like a function value + } +``` + +These limitations are severe enough that this future direction would require a significant amount of justification on its own to pursue, and therefore is not part of the `init` accessors proposal. + +## Revision history + +* Following the initial review: + * Replaced the "effects" syntax with the `@storageRestrictions` attribute. + * Add section on init accessors for computed properties. + * Add section on init accessors for read-only properties. + * Allow reordering of the initializations in the synthesized memberwise initializer to respect `accesses` restrictions. + * Add a potential future direction for the generalization of storage restrictions to other functions. + * Clarify the behavior of properties that have init accessors and initial values. + ## Acknowledgments Thank you to TJ Usiyan, Michel Fortin, and others for suggesting alternative syntax ideas for `init` accessor effects; thank you to Pavel Yaskevich for helping with the implementation. From 51f3fbe08d7c416c7b3beb67d8db66fd73117748 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 5 Jul 2023 15:30:28 -0700 Subject: [PATCH 3204/4563] [SE-0400] Finish the exampel for "init accessors on computed properties" (#2098) --- proposals/0400-init-accessors.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/proposals/0400-init-accessors.md b/proposals/0400-init-accessors.md index 8a5d45c22d..bad722e604 100644 --- a/proposals/0400-init-accessors.md +++ b/proposals/0400-init-accessors.md @@ -417,6 +417,14 @@ struct Angle { } ``` +The implicit memberwise initializer will contain `radians`, but not the `degrees` stored property that it subsumes: + +```swift +init(radians: Double) { + self.radians = radians // calls init accessor, subsumes initialization of 'degrees' +} +``` + ### Init accessors for read-only properties Init accessors can be provided for properties that lack a setter. Such properties act much like a `let` property, able to be initialized (exactly) once and not set thereafter: From 1f8e1017a23aabb7157e60c3ef65cc3c78fccdd7 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 7 Jul 2023 12:21:51 -0500 Subject: [PATCH 3205/4563] Apply `@storageRestrictions` only to init accessors (#2099) --- proposals/0400-init-accessors.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/proposals/0400-init-accessors.md b/proposals/0400-init-accessors.md index bad722e604..e88edea1a2 100644 --- a/proposals/0400-init-accessors.md +++ b/proposals/0400-init-accessors.md @@ -237,8 +237,8 @@ struct S { var x1: Int var x2: Int - @storageRestrictions(initializes: x1, x2) var computed: Int { + @storageRestrictions(initializes: x1, x2) init(newValue) { ... } } @@ -255,8 +255,8 @@ struct S { var x: Int var y: Int - @storageRestrictions(initializes: x, y) var point: (Int, Int) { + @storageRestrictions(initializes: x, y) init(newValue) { (self.x, self.y) = newValue } @@ -286,8 +286,8 @@ struct S { var x2: Int var x3: Int - @storageRestrictions(initializes: x1, x2) var computed: Int { + @storageRestrictions(initializes: x1, x2) init(newValue) { ... } } @@ -306,10 +306,10 @@ struct S { var x: Int var y: Int - @storageRestrictions(initializes: x, y) var point: (Int, Int) { + @storageRestrictions(initializes: x, y) init(newValue) { - (self.x, self.y) = newValue + (self.x, self.y) = newValue } get { (x, y) } set { (x, y) = newValue } @@ -330,8 +330,8 @@ If a struct does not declare its own initializers, it receives an implicit membe struct S { var _x: Int - @storageRestrictions(initializes: _x) var x: Int { + @storageRestrictions(initializes: _x) init(newValue) { _x = newValue } @@ -361,8 +361,8 @@ The parameters of the memberwise initializer follow source order. However, if an struct S { var _x: Int - @storageRestrictions(initializes: _x, accesses: y) var x: Int { + @storageRestrictions(initializes: _x, accesses: y) init(newValue) { _x = newValue } @@ -433,8 +433,8 @@ Init accessors can be provided for properties that lack a setter. Such propertie struct S { var _x: Int - @storageRestrictions(initializes: _x) var x: Int { + @storageRestrictions(initializes: _x) init(initialValue) { self._x = x } From 8d32c167d947c57a8eb77eb30b9005a17df01f5f Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Fri, 7 Jul 2023 20:38:24 +0200 Subject: [PATCH 3206/4563] [SE-0346] Fix error in code snippet in Alternatives considered The return type of a convert-to-`Double` function should be `Double`, not `Int`. This looks like a copy-paste oversight. --- proposals/0346-light-weight-same-type-syntax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0346-light-weight-same-type-syntax.md b/proposals/0346-light-weight-same-type-syntax.md index a4c47112f5..d1e4d2e46a 100644 --- a/proposals/0346-light-weight-same-type-syntax.md +++ b/proposals/0346-light-weight-same-type-syntax.md @@ -451,7 +451,7 @@ extension Convertible(from: String, to: Int) { } extension Convertible(from: String, to: Double) { - static func convert(_: String) -> Int + static func convert(_: String) -> Double } ``` From 25d15b7fc845dcf635e6718a72ddd81f554898f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EC=9E=AC=ED=98=B8?= Date: Thu, 13 Jul 2023 04:44:43 +0900 Subject: [PATCH 3207/4563] [SE-0402] Fix some typos (#2101) --- proposals/0402-extension-macros.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0402-extension-macros.md b/proposals/0402-extension-macros.md index 4c41ecbe2a..f37046b566 100644 --- a/proposals/0402-extension-macros.md +++ b/proposals/0402-extension-macros.md @@ -74,7 +74,7 @@ SE-0389 states that whenever a macro produces declarations that are visible to o The following restrictions apply to generated conformances and names listed in `@attached(extension)`: -* An extension macro cannot add a conformance to a protocol that is not covered by the `conformances:` list in `@attached(extension, conformnaces:)`. +* An extension macro cannot add a conformance to a protocol that is not covered by the `conformances:` list in `@attached(extension, conformances:)`. * An extension macro cannot add a member that is not covered by the `names:` list in `@attached(extension, names:)`. * An extension macro cannot introduce an extension with an attached `peer` macro, because the peer-macro-generated names are not covered by the original `@attached(extension)` attribute. @@ -136,7 +136,7 @@ public protocol ExtensionMacro: AttachedMacro { /// to. /// - context: The context in which to perform the macro expansion. /// - /// - Returns: the set of extensions declarations introduced by the macro, + /// - Returns: the set of extension declarations introduced by the macro, /// which are always inserted at top-level scope. Each extension must extend /// the `type` parameter. static func expansion( @@ -170,7 +170,7 @@ For example, consider the following code which contains an attached extension ma protocol Encodable {} protocol Decodable {} -typelias Codable = Encodable & Decodable +typealias Codable = Encodable & Decodable @attached(extension, conformances: Codable) macro MyMacro() = #externalMacro(...) @@ -181,11 +181,11 @@ struct S { ... } extension S: Encodable { ... } ``` -The extension macro can add conformances to `Codable`, aka `Encodable & Decodable`. Because the struct `S` already conforms to `Encodable` in the original source, the `ExtensionMacro.expansion` method will recieve the argument `[TypeSyntax(Encodable)]` for the `conformingTo:` parameter. Using this information, the macro implementation can decide to only add an extension with a conformance to `Decodable`. +The extension macro can add conformances to `Codable`, aka `Encodable & Decodable`. Because the struct `S` already conforms to `Encodable` in the original source, the `ExtensionMacro.expansion` method will receive the argument `[TypeSyntax(Encodable)]` for the `conformingTo:` parameter. Using this information, the macro implementation can decide to only add an extension with a conformance to `Decodable`. ## Source compatibility -This propsoal removes the `conformance` macro role from SE-0389, which is accepted and implemented in Swift 5.9. If this proposal is accepted after 5.9, the `conformance` macro role will remain in the language as sugar for an `extension` macro that adds only a conformance. +This proposal removes the `conformance` macro role from SE-0389, which is accepted and implemented in Swift 5.9. If this proposal is accepted after 5.9, the `conformance` macro role will remain in the language as sugar for an `extension` macro that adds only a conformance. ## ABI compatibility From 1ee20208eb0e7ab35004e678f7a6b6b1b476ac3a Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 13 Jul 2023 22:15:14 -0700 Subject: [PATCH 3208/4563] [SE-0389] Specify that peer macros attached to top-level declarations cannot introduce `arbitrary` names. --- proposals/0389-attached-macros.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/proposals/0389-attached-macros.md b/proposals/0389-attached-macros.md index 59bc836f51..4a619a43b6 100644 --- a/proposals/0389-attached-macros.md +++ b/proposals/0389-attached-macros.md @@ -574,6 +574,27 @@ f(1) { x in Therefore, a macro used within a closure or function body can only introduce declarations using names produced by `createUniqueName`. This maintains the [two-phase of checking macros](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md#macro-expansion) where type checking and inference is performed without expanding the macro, then the macro is expanded and its result type-checked independently, with no ability to influence type inference further. +### Restrictions on `arbitrary` names + +Attached macros that specify `arbitrary` names require expanding the macro in order to determine the exact set of names that the macro generates. This means that name lookup must expand all macros that can produce `arbitrary` names in the given scope in order to determine name lookup results. This is a problem for macros that introduce names at global scope, because any unqualified or module qualified lookup must expand those macros. However, because macro attributes can have arguments that are type-checked prior to macro expansion, type checking those arguments may require unqualified or module qualified name lookup that requires expanding that same macro, which is fundamentally circular. Though macro arguments do not have visibility of macro-generated names in the same scope, macro argument type checking can invoke type checking of other declarations that can use macro-generated names. For example: + +```swift +@attached(peer, names: arbitrary) +macro IntroduceArbitraryPeers(_: T) = #externalMacro(...) + +@IntroduceArbitraryPeers(MyType().x) +struct S {} + +struct MyType { + var x: AnotherType +} +``` + +Resolving the macro attribute `@IntroduceArbitraryPeers(MyType().x)` can invoke type-checking of the `var x: AnotherType` property of the `MyType` struct. Unqualified lookup of `AnotherType` must expand `@IntroduceArbitraryPeers` because the macro can introduce arbitrary names; it might introduce a top-level type called `AnotherType`. Resolving the `@IntroduceArbitraryPeers` attribute depends on type checking the `var x: AnotherType` property, and type checking the property depends on expanding the `@IntroduceArbitraryPeers` macro, which results in a circular reference error. + +Because `arbitrary` names introduced at global scope are extremely prone to circularity, peer macros attached to top-level declarations cannot introduce `arbitrary` names. + + ### Ordering of macro expansions When multiple macros are applied to a single declaration, the order in which macros are expanded could have a significant impact on the resulting program if the the outputs of one macro expansion are treated as inputs to the others. This form of macro composition could be fairly powerful, but it also can have a number of surprising side effects: From ff500e4511dbfe6d8cbbc193cabbca36e9e52cae Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 14 Jul 2023 10:34:16 -0400 Subject: [PATCH 3209/4563] [SE-NNNN] Package Manager Mixed Language Target Support (#1895) Co-authored-by: Max Desiatov --- .../NNNN-swiftpm-mixed-language-targets.md | 804 ++++++++++++++++++ 1 file changed, 804 insertions(+) create mode 100644 proposals/NNNN-swiftpm-mixed-language-targets.md diff --git a/proposals/NNNN-swiftpm-mixed-language-targets.md b/proposals/NNNN-swiftpm-mixed-language-targets.md new file mode 100644 index 0000000000..63eceda764 --- /dev/null +++ b/proposals/NNNN-swiftpm-mixed-language-targets.md @@ -0,0 +1,804 @@ +# Package Manager Mixed Language Target Support + +* Proposal: [SE-NNNN](NNNN-swiftpm-mixed-language-targets.md) +* Authors: [Nick Cooke](https://github.com/ncooke3) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift-package-manager#5919](https://github.com/apple/swift-package-manager/pull/5919) +* Decision Notes: [Pitch](https://forums.swift.org/t/61564) + +## Introduction + +This is a proposal for adding package manager support for targets containing +both Swift and [C based language sources][SE-0038] (henceforth, referred to as +mixed language sources). Currently, a target’s source can be either Swift or a +C based language ([SE-0038]), but not both. + +Swift-evolution thread: [Discussion thread topic for that +proposal](https://forums.swift.org/) + +## Motivation + +This proposal enables Swift Package Manager support for multi-language targets. + +Packages may need to contain mixed language sources for both legacy or +technical reasons. For developers building or maintaining packages with mixed +languages (e.g. Swift and Objective-C), there are two workarounds for doing so +with Swift Package Manager, but they have drawbacks that degrade the developer +experience, and sometimes are not even an option: +- Distribute binary frameworks via binary targets. Drawbacks include that the + package will be less portable as it can only support platforms that the + binaries support, binary dependencies are only available on Apple platforms, + customers cannot view or easily debug the source in their project workspace, + and tooling is required to generate the binaries for release. +- Separate a target’s implementation into sub-targets based on language type, + adding dependencies where necessary. For example, a target `Foo` may have + Swift-only sources that can call into an underlying target `FooObjc` that + contains Clang-only sources. Drawbacks include needing to depend on the + public API surfaces between the targets, increasing the complexity of the + package’s manifest and organization for both maintainers and clients, and + preventing package developers from incrementally migrating internal + implementation from one language to another (e.g. Objective-C to Swift) since + there is still a separation across targets based on language. + +Package manager support for mixed language targets addresses both of the above +drawbacks by enabling developers to mix sources of supported languages within a +single target without complicating their package’s structure or developer +experience. + +## Proposed solution + +This solution enables the package manager to determine if a target contains +mixed language sources and build it as a single module. This happens as an +implementation detail and doesn't require changes to the package manager's +public API. This is cleaner and easier because developers can organize their +packages without exposing language barrier complexity to clients. + +At a high level, the package creation process is split into two parts based on +the language of the sources. The Swift sources are built by the Swift compiler +and the C Language sources are built by the Clang compiler. To achieve +interoperability between the two halves of the package, a few things have to +happen: +1. The Swift compiler is made aware of the Clang part of the package when + building the Swift sources into a `swiftmodule`. +1. The generated interoperability header emitted by the Swift compiler is added + as a submodule to the Clang part of the package’s generated module map. +1. The Clang part of the package is built with knowledge of the generated + interoperability header. + +The [following example][mixed-package] defines a package containing mixed +language sources. + +``` +MixedPackage +├── Package.swift +├── Sources +│   └── MixedPackage +│ ├── Jedi.swift ⎤-- Swift sources +│ ├── Lightsaber.swift ⎦ +│ ├── Sith.m ⎤-- Implementations & internal headers +│ ├── SithRegistry.h ⎟ +│ ├── SithRegistry.m ⎟ +│   ├── droid_debug.c ⎦ +│   ├── hello_there.txt ]-- Resources +│   └── include ⎤-- Public headers +│   ├── MixedPackage.h ⎟ +│   ├── Sith.h ⎟ +│   └── droid_debug.h ⎦ +└── Tests + └── MixedPackageTests + ├── JediTests.swift ]-- Swift tests + ├── SithTests.m        ]-- Objective-C tests + ├── ObjcTestConstants.h ⎤-- Mixed language test utils + ├── ObjcTestConstants.m ⎟ + └── TestConstants.swift ⎦ +``` + +The proposed solution would enable the above package to have the following +capabilities: +1. Export public API of the mixed language sources as a single module for + clients of the package. +1. Use Obj-C compatible Swift API from target’s Swift sources within the + target’s Obj-C sources. +1. Use all API exposed by target’s Objective-C or C sources within the target’s + Swift sources. +1. Use internal C based language types within the Clang part of the module. +1. Use internal Swift types within the Swift part of the module. +1. Access target resources from a Swift and Objective-C context. +1. Define test utility types in either Obj-C or Swift and use them in both + Swift and Obj-C test files. + +### Requirements + +Initial support for targets containing mixed language sources will have the +following requirements: +1. The target must be either a library or test target. Support for other types + of targets is deferred until the use cases become clear. +1. The target must be built on a Mac. This is because the Swift + compiler-generated Objective-C compatibility header is only generated on + macOS. +1. If the target contains a custom module map, it cannot contain a submodule of + the form `$(ModuleName).Swift`. + +### Importing a mixed target + +Mixed targets can be imported into a client target in several ways. The +following examples will reference `MixedPackage`, a package containing mixed +language target(s). + +#### Importing within a **Swift** context + +A mixed target, `MixedPackage`, can be imported into a **Swift** file via an +`import` statement: + +```swift +// MyClientTarget.swift + +import MixedPackage +``` + +This imports the module's `public`/`open` Swift types as well as public +Objective-C/C headers. + +_Testing targets_ can import the mixed target via +`@testable import MixedPackage`. As expected, this will expose internal Swift +types within the module. It will not expose any non-public C language types. + +#### Importing within an **Objective-C** context + +A mixed target, `MixedPackage`, can be imported into an **Objective-C** file +via _importing the module_ or _importing the mixed target's public headers_. + +- **Importing the module** + ```objc + // MyClientTarget.m + + @import MixedPackage; + ``` + + This imports the module's `public`/`open` Swift types as well as public + Objective-C/C headers. + +- **Importing the mixed target's public headers** + For this example, consider `MixedPackage` being organized as such: + ``` + MixedPackage + ├── Package.swift + └── Sources +    ├── NewCar.swift +   └── include ]-- Public headers directory +     ├── MixedPackage + │ ├── OldCar.h +    │ └── MixedPackage.h ⎤-- These headers are generated by the + └── MixedPackage-Swift.h ⎦ package manager and virtually overlayed + within the package's public headers + directory. More details in the Detailed + Design section.* + ``` + + > * NOTE: The generated interop header (`$(TARGET_NAME)-Swift.h`) is always + > generated for a mixed target (even when there is no Objective-C compatible + > Swift API). + > + > On the other hand, the umbrella header located at + > `$(PUBLIC_HDRS_DIR)/$(TARGET_NAME)/$(TARGET_NAME).h` is only generated when + > the mixed target does not contain an umbrella header at that path. It + > imports all Objective-C/C headers and acts as the bridging header used in + > the generated interop header. + + `MixedPackage`'s public headers directory (`include`) is added a header + search path to client targets. The following example demonstrates all the + possible public headers that can be imported from `MixedPackage`. + + ```objc + // MyClientTarget.m + + // Imports types defined in `OldCar.h`. + #import "MixedPackage/OldCar.h" + // Imports a generated umbrella header that includes all public Objective-C/C + // headers within `MixedPackage`. ** + #import "MixedPackage/MixedPackage.h" + // Imports Objective-C compatible Swift types defined in `MixedPackage`. + #import "MixedPackage-Swift.h" + ``` + + > ** NOTE: If imported, the generated umbrella/bridging header will need to + > appear _before_ importing the generated interop header. This is due to the + > intrinsic relationship between the generated interop header and the + > generated umbrella/bridging header. + +## Detailed design + +### Modeling a mixed language target and its build process + +Up until this proposal, when a package was loading, each target was represented +programmatically as either a [`SwiftTarget`] or [`ClangTarget`]. Which of these +types to use was informed by the sources found in the target. For targets with +mixed language sources, an error was thrown and surfaced to the client. During +the build process, each of those types mapped to another type +([`SwiftTargetBuildDescription`] or [`ClangTargetBuildDescription`]) that +described how the target should be built. + +This proposal adds two new types, `MixedTarget` and `MixedTargetDescription`, +that represent targets with mixed language sources during the package loading +and building phases, respectively. + +While an implementation detail, it’s worth noting that in this approach, a +`MixedTarget` is a wrapper type around an underlying `SwiftTarget` and +`ClangTarget`. Initializing a `MixedTarget` will internally initialize a +`SwiftTarget` from the given Swift sources and a `ClangTarget` from the given +Clang sources. This extends to the `MixedTargetDescription` type in that it +wraps a `SwiftTargetDescription` and `ClangTargetDescription`. + +The role of the `MixedTargetBuildDescription` is to generate auxiliary +artifacts needed for the build and pass specific build flags to the underlying +`SwiftTargetBuildDescription` and `ClangTargetBuildDescription`. + +The following diagram shows the relationship between the various types. +```mermaid +flowchart LR + A>Swift sources] --> B[SwiftTarget] --> C[SwiftTargetBuildDescription] + D>Clang sources] --> E[ClangTarget] --> F[ClangTargetBuildDescription] + + subgraph MixedTarget + SwiftTarget + ClangTarget + end + + subgraph MixedTargetBuildDescription + SwiftTargetBuildDescription + ClangTargetBuildDescription + end + + G>Mixed sources] --> MixedTarget --> MixedTargetBuildDescription +``` + +### Building a mixed language target + +Building a mixed target involves building an underlying Clang target and an +underlying Swift target in a way where each part of the resulting module knows +about the other. To achieve this, the package manager creates two module maps +and two VFS (virtual file system) overlay files where each of the module maps +correspond to a VFS overlay file. The VFS overlay files are used to virtually +alter the filesystem so that all relevant files are in the correct place for +the build. The reason that the filesystem is modified virtually is because +doing otherwise would involve adding and modifying files in the package’s +source itself– which would be unexpected for the package author. + +The four files are considered intermediary build artifacts and are stored in an +`Intermediates` subdirectory within the target’s build folder. The +`Intermediates` subdirectory is something specific to building mixed targets +and is therefore part of this proposal’s design. + +For example, for a target named `MixedTarget`, the `Intermediates` subdirectory +would look like such: +``` +/Users/crusty/Developer/MixedTarget/.build/x86_64-apple-macosx/debug/ +├── ... +└── MixedTarget.build + ├── ... + └── Intermediates + ├── module.modulemap + ├── all-product-headers.yaml + ├── unextended-module.modulemap + └── unextended-module-overlay.yaml +``` + +#### module.modulemap + +A mixed target will either have a custom module map defined by the package +author or it won’t. In both cases, the package manager creates an intermediary +module map in the `Intermediates` subdirectory that exposes the generated +interop header via a submodule. This is needed when compiling the target’s +Objective-C sources so that the sources can import the generated interop +header–– making Objective-C compatible types defined in the target’s Swift API +visible within an Objective-C context. + +> Note: As opposed to the unextended module map, the contents of the +> `module MixedTarget { … }` is not important here because the module map’s +> sole purpose is to assist in the compilation of the target’s Objective-C +> sources, which access other C Language types defined in the target via +> importing headers found in the target’s header search paths. Nonetheless, the +> `module MixedTarget { … }` declaration is the same between the intermediary +> `module.modulemap` and `unextended-module.modulemap` for consistency. + +``` +// module.modulemap + +// See above note for context regarding this module declaration. +module MixedTarget { + umbrella "/Users/crusty/Developer/MixedTarget/Sources" + export * +} + +// A submodule exposes the generated interop header for Objective-C clients. +module MixedTarget.Swift { + header "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/MixedTarget-Swift.h" + requires objc +} +``` + +#### all-product-headers.yaml + +This overlay file’s purpose is to facilitate the building of the Clang sources +by positioning the above module map and the generated interop header +accordingly so that the build can be successful. The positioning is determined +based on whether or not the package author has defined a custom module map in +the mixed target: +- If there is a custom module map, then the overlay is created over the + directory that contains the custom module map. The overlay has two + responsibilities: + 1. Redirect the path to the custom module map to the intermediary module + map. This means that when the custom module map is read from, the + contents will instead come from the intermediary module map. This is + done to avoid a module redeclaration error from there being two + discoverable, distinct module maps. + 2. Add the generated interop header to the directory. This will enable the + header to be imported within this target via + `#import MixedTarget-Swift.h`. +- If there is no custom module map, then the overlay is created over the + `Intermediates` subdirectory. The overlay has one responsibility: + 1. Add the generated interop header to the directory. This will enabled the + header to be imported within this target via + `#import MixedTarget-Swift.h`. + +The below sample shows what the overlay file would look like for a target +without a custom module map. +```yaml +// all-product-headers.yaml +{ + "version": 0, + "case-sensitive": false, + "roots": + [ + { + // Note #1: If a custom module map exists, the below directory will + // instead be the custom module map's parent directory. + "name": "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/Intermediates", + "type": "directory", + "contents": + [ + // Note #2: Including the below file has no effect when there is + // no custom module map because the module.modulemap already + // exists in the Intermediates subdirectory. It is included to + // simplify the VFS templating logic between the two cases. + { + "name": "module.modulemap", + "type": "file", + "external-contents": "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/Intermediates/module.modulemap", + }, + { + "name": "MixedTarget-Swift.h", + "type": "file", + "external-contents": "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/MixedTarget-Swift.h", + }, + ], + }, + ], + "use-external-names": false, +} +``` + +#### unextended-module.modulemap + +The unextended module map is used when compiling the target’s Swift sources. +The *unextended* part of its name comes from the fact that it does not include +a submodule to expose the generated interop header, as is done in the +intermediary `module.modulemap`. This is intentionally excluded for two +reasons: +- This module map is only used to build the target’s Swift sources; therefore, + the Swift types defined in the generated interop header can instead be + accessed from their native Swift declarations. +- Because the generated interop header is a build artifact from building the + target’s Swift sources (it is emitted alongside the target’s `.swiftmodule`), + including it in the module map will cause an error since the header won’t + exist when the module map is evaluated. + +This module map defines an umbrella directory at the target’s [path], exposing +all headers for the target. When used to build the target’s Swift sources, the +target’s Swift sources can use all types defined in the target’s headers, as +opposed to only types declared in the target’s public headers. + +Additionally, when present, each non-Objective-C/C header (e.g. a C++ header) +is excluded from the module. This avoids build errors that arise when exposing, +for example, a C++ header to Swift. + +``` +// unextended-module.modulemap + +module MixedTarget { + umbrella "/Users/crusty/Developer/MixedTarget/Sources" + exclude header "/Users/crusty/Developer/MixedTarget/Sources/Foo.hpp" + export * +} +``` + +#### unextended-module-overlay.yaml + +This overlay file is similar in purpose to the previously discussed +`all-product-headers.yaml` overlay file except that it instead facilitates the +building of the target’s Swift sources. Like with the +`all-product-headers.yaml` overlay file, the positioning of the overlay is +determined based on the presence of a custom module map in the mixed target: +- If there is a custom module map, then the overlay is created over the + directory that contains the custom module map. +- If there is no custom module map, then the overlay is created over the + `Intermediates` subdirectory. + +In both cases, the overlay’s sole responsibility is to redirect the path to the +custom module map to the intermediary unextended module map. This redirection +avoids the situation where a module map that exposes the generated interop +header is used to compile the target’s Swift sources. As discussed previously, +this would cause an error because the generated interop header would not yet +exist at the time of the module map’s evaluation. + +The below sample shows what the overlay file would look like for a target +without a custom module map. + +```yaml +// unextended-module-overlay.yaml + +{ + "version": 0, + "case-sensitive": false, + "roots": + [ + { + // Note #1: If a custom module map exists, the below directory will + // instead be the custom module map's parent directory. + "name": "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/Intermediates", + "type": "directory", + "contents": + [ + { + "name": "module.modulemap", + "type": "file", + "external-contents": "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/Intermediates/unextended-module.modulemap", + }, + ], + }, + ], + "use-external-names": false, +} +``` + +#### Bringing everything together with build flags + +The Swift part of the target is built before the Clang part. This is because +the C language sources may require resolving an import of the generated interop +header, and that header is emitted alongside the Swift module when the Swift +part of the target is built. This relationship is enforced in that the +generated interop header is listed as an input to the compilation commands for +the target’s C language sources. This is specified in the llbuild manifest +(`debug.yaml` in the packag's `.build` directory). + +##### Build flags for the Swift part of the target + +The following flags are used when compiling the **Swift** part of the target: +1. `-import-underlying-module` This flag triggers a partial build of the + underlying C language sources when building the Swift module. This critical + flag enables the Swift sources to use C language types defined in the Clang + part of the target. +1. `-I /path/to/overlay_directory` The above `-import-underlying-module` flag + will look for a module map in the given header search path. The overlay + directory chosen when creating the above VFS overlay files is used here. +1. `-ivfsoverlay /path/to/Intermediates/all-product-headers.yaml` This enables + the overlay to take effect during the compilation. Specifically, it will be + used during the partial build of the C language sources that is triggered by + the `-import-underlying-module` flag. +1. `-ivfsoverlay /path/to/Intermediates/unextended-module-overlay.yaml` This + enables the overlay to take effect during the compilation. Specifically, it + will be used when compiling the Swift sources. +1. `-I $(target’s path)` Adding the target's [path] allows for importing + headers using paths relative to the root of the target. Because passing + `-import-underlying-module` triggers a partial build of the C language + sources, this is needed for resolving possible header imports. + +##### Build flags for the Clang part of the target +The following flags are used when compiling the **Clang** part of the target: +1. `-I $(target’s path)` Adding the target's [path] allows for importing + headers using paths relative to the root of the target. +1. `-ivfsoverlay /path/to/Intermediates/all-product-headers.yaml` This enables + the overlay to take effect during the compilation. +1. `-I /path/to/Intermediates/` The above overlay virtually adds the generated + Swift header to the overlay directory. Adding it as a search path enables it + to then be imported with `#import “$(TargetName)-Swift.h”`. + +#### Performing the build + +To actually build a package, the package manager creates a llbuild manifest and +passes it to the llbuild system. Adding support for mixed targets involved +modifying [LLBuildManifestBuilder.swift] to convert a +`MixedTargetBuildDescription` into llbuild build nodes. +`MixedTargetBuildDescription` intentionally wraps and configures an underlying +`SwiftTargetBuildDescription` and `ClangTargetBuildDescription`. This means +that creating a llbuild build node for a mixed target is really just creating +build nodes for the its `SwiftTargetBuildDescription` and +`ClangTargetBuildDescription`, respectively. + +#### Build artifacts for client targets + +As explained above, intermediary artifacts support the mixed target’s +build process. For example, the intermediary module maps intentionally expose +all headers so all types defined in the target’s headers can be used in the +Swift sources. While this module map setup was ideal for building the mixed +target, it is not ideal for clients depending on a mixed target because the +client would have access to all headers. Therefore, building a mixed target +will create an additional module map and, conditionally, a corresponding VFS +overlay for use by clients depending on the mixed target. + +The two files are considered product build artifacts and are stored in an +`Product` subdirectory within the target’s build folder. The `Product` +subdirectory is something specific to building mixed targets and is therefore +part of this proposal’s design. + +For example, for a target named `MixedTarget`, the `Product` subdirectory would +look like such: + +``` +/Users/crusty/Developer/MixedTarget/.build/x86_64-apple-macosx/debug/ +├── ... +└── MixedTarget.build + ├── ... + └── Product + ├── module.modulemap + └── all-product-headers.yaml +``` + +##### module.modulemap + +The product module map’s purpose is to define the public API of the mixed +language module. It has two parts, a primary module declaration and a secondary +submodule declaration. The former of which exposes the public C language +headers and the latter of which exposes the generated interop header. + +There are two cases when creating the product module map: +- If a custom module map exists in the target, its contents are copied to the + product module map and a submodule is added to expose the generated interop + header. +- Else, the product module map’s contents will be generated via the same + generation rules established in [SE-0038] with an added step to generate the + `.Swift` submodule. + +> Note: It’s possible that the Clang part of the module exports no public API. +> This could be the case for a target whose public API surface is written in +> Swift but whose implementation is written in Objective-C. In this case, the +> primary module declaration will not specify any headers or umbrella +> directories. + +Below is an example of a module map for a target that has an umbrella +header in its public headers directory (`include`). + +``` +// module.modulemap + +// This declaration is either copied from the custom module map or generated +// via the rules from SE-0038. +module MixedTarget { + umbrella header "/Users/crusty/Developer/MixedTarget/Sources/MixedTarget/include/MixedTarget.h" + export * +} +// This is added on by the package manager. +module MixedTarget.Swift { + header "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/MixedTarget-Swift.h" + requires objc +} +``` + +##### all-product-headers.yaml + +The product `all-product-headers.yaml` overlay file is only created when there +exists a custom module map that needs to be swapped out for the product module +map. + +In the case that this overlay file exists, it will be passed alongside the +product module map as a compilation argument to clients: +``` +-fmodule-map-file=/Users/crusty/Developer/MixedTarget/Sources/MixedTarget/include/module.modulemap +-ivfsoverlay /Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/Product/all-product-headers.yaml +``` + +The `-fmodule-map-file` argument tells the client to find the mixed target’s +module map in its public `include` directory. However, the product module map +should be used instead of the custom module map, so passing the overlay via +`-vfsoverlay` will ensure that resolving the module map path redirects to the +product module map in the build directory. The reason this is necessary is +because a custom module map will likely have relative paths within it (e.g. +`umbrella header “MyHeader.h”`) that are preserved when its contents are copied +to the product module map. In order for these relative paths to be resolved, +the product module map needs to appear as if it is in the original directory +that the custom module map resides in. + +The below sample shows what this overlay file may look like: +```yaml +// all-product-headers.yaml + +{ + "version": 0, + "case-sensitive": false, + "roots": + [ + { + "name": "/Users/crusty/Developer/MixedTarget/Sources/MixedTarget/include", + "type": "directory", + "contents": + [ + { + "name": "module.modulemap", + "type": "file", + "external-contents": "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/Product/module.modulemap", + }, + ], + }, + ], + "use-external-names": false, +} +``` + +### Mixed language Test Targets + +To complement library targets with mixed languages, mixed test targets are +also supported as part of this proposal. Using the same strategy, mixed test +targets are built with intermediary module maps that enable the sharing of +mixed language testing utilities. + +Using the [example package][mixed-package] from before, consider the following +layout of the package's `Tests` directory. + +``` +MixedPackage +├── ... +└── Tests + └── MixedPackageTests + ├── JediTests.swift ]-- Swift tests + ├── SithTests.m        ]-- Objective-C tests + ├── ObjcTestConstants.h ⎤-- Mixed language test utils + ├── ObjcTestConstants.m ⎟ + └── TestConstants.swift ⎦ +``` + +The types defined in `ObjcTestConstants.h` are visible in `SithTests.m` (via +importing the header) and implicitly visible in `JediTests.swift`. + +The Objective-C compatible types defined in `TestConstants.swift` are visible +in `SithTests.m` (via importing the generated `MixedPackageTests-Swift.h` +header) and implicitly visible in `JediTests.swift`. + +This design should give package authors flexibility in designing test suites +for their mixed targets. + +#### Testing Visibility + +Documented below are several strategies for testing types defined in mixed +targets. + +- **Expose non-public C-Language types to Objective-C test files**: This is + done by configuring the test target's [C settings][CSetting] to search for + the mixed target's headers via the + [`.headerSearchPaths(_:_:)`][headerSearchPath] setting. + +- **Expose non-public C-Language types to Swift test files**: This can be done + by adding a header to the test target that imports the desired non-public + header(s). Note that the test target will need to be configured to include + the mixed test target's header in its header search paths (see above + strategy). Additionally, the test target will need at least one `.m` file + (e.g. a blank `Dummy.m` file). + +- **Expose `internal` Swift types to Swift test files**: As expected, Swift + types with `internal` access be tested within a Swift test file by importing + with `@testable`. + +Note: Objective-C test files cannot import non-public Swift types. + +### Failure cases + +There are several failure cases that may surface to end users: +- Attempting to build a mixed target using a tools version that does not + include this proposal’s implementation. + ``` + target at '\(path)' contains mixed language source files; feature not supported + ``` +- Attempting to build a mixed target anywhere other than macOS. + ``` + Targets with mixed language sources are only supported on Apple platforms. + ``` +- Attempting to build a mixed target that is neither a library target + or test target. + ``` + Target with mixed sources at '\(path)' is a \(type) target; targets + with mixed language sources are only supported for library and test + targets. + ``` +- Attempting to build a mixed target containing a custom module map + that contains a `$(MixedTargetName).Swift` submodule. + ``` + The target's module map may not contain a Swift submodule for the + module \(target name). + ``` + +### Testing + +This feature was tested with a mix of unit and integration tests. +- `Tests/BuildTests/BuildPlanTests.swift`: Added several unit tests to assert + behavior for fundamental mixed target use cases. +- `Tests/PackageLoadingTests/ModuleMapGenerationTests.swift`: Added a unit test + to cover changes at the module map generation level. +- `Tests/PackageLoadingTests/PackageBuilderTests.swift`: Added several unit + tests to assert behavior when loading a mixed target. +- `Tests/FunctionalTests/MixedTargetTests.swift`: Added a thorough suite of + integration tests that use targets defined in the `Fixtures/MixedTargets` + directory. These fixture targets double as documented examples for different + mixed target configurations. + +## Security + +This has no impact on security, safety, or privacy. + +## Impact on existing packages + +This proposal will not affect the behavior of existing packages. In the +proposed solution, the code path to build a mixed language package is separate +from the existing code paths to build packages with Swift sources and C +Language sources, respectively. + +Additionally, this feature will be gated on a tools minor version update, so +mixed language targets building on older toolchains that do not support this +feature will continue to [throw an error][mixed-target-error]. + +## Alternatives considered + +### Provide custom implementations for `MixedTarget` and `MixedTargetBuildDescription` + +As explained in the Detailed Design section, these two types effectively wrap +the Swift and Clang parts necessary to define or build the target. One +alternative approach was to provide custom implementations that did not heavily +rely on code reuse of existing types. The deciding drawback of this approach +was that it would have resulted in a lot of duplicated code. + +### Consolidate target modeling logic so that all targets are `MixedTarget`s + +The deciding drawback here was the risk of introducing a regression in how +Swift or Clang targets are built. A benefit of the chosen design over this +alternative is that the code paths introduced in this proposal have little +crossover with the existing code paths that build Swift or Clang targets– +further reducing the chance of introducing an untested regression. However, +this alternative should be considered as a future direction for the package +manager. The chosen design offers a natural path to making all targets mixed +source targets by default. The implementations from `ClangTarget`, +`SwiftTarget`, `ClangTargetBuildDescription` and `SwiftTargetBuildDescription` +can be bubbled up to the mixed target types accordingly. This alternative is +listed in the Future Directions section as an area of future work. + +## Future Directions + +- Investigate uses cases for extending mixed language target support to + currently unsupported types of targets (e.g. executables). +- Investigate uses cases for expanding the level of mixed target support when + building on non-macOS machines. +- Extend this solution so that all targets are mixed language targets by + default. This refactor would simplify the current implementation of the + package manager. + + + +[SE-0038]: https://github.com/apple/swift-evolution/blob/main/proposals/0038-swiftpm-c-language-targets.md + +[mixed-package]: https://github.com/ncooke3/MixedPackage + +[`SwiftTarget`]: https://github.com/apple/swift-package-manager/blob/ce099264a187759c2f587393bd209d317a0352b4/Sources/PackageModel/Target.swift#L313 + +[`ClangTarget`]: https://github.com/apple/swift-package-manager/blob/ce099264a187759c2f587393bd209d317a0352b4/Sources/PackageModel/Target.swift#L470 + +[`SwiftTargetBuildDescription`]: https://github.com/apple/swift-package-manager/blob/main/Sources/Build/BuildPlan.swift#L549 + +[`ClangTargetBuildDescription`]: https://github.com/apple/swift-package-manager/blob/ce099264a187759c2f587393bd209d317a0352b4/Sources/Build/BuildPlan.swift#L232 + +[path]: https://developer.apple.com/documentation/packagedescription/target/path + +[LLBuildManifestBuilder.swift]: https://github.com/apple/swift-package-manager/blob/14d05ccaa13b768449cd405fff81d630a520e04a/Sources/Build/LLBuildManifestBuilder.swift + +[mixed-target-error]: https://github.com/apple/swift-package-manager/blob/ce099264a187759c2f587393bd209d317a0352b4/Sources/PackageLoading/TargetSourcesBuilder.swift#L183-L189 + +[CSetting]: https://developer.apple.com/documentation/packagedescription/target/csettings + +[headerSearchPath]: https://developer.apple.com/documentation/packagedescription/csetting/headersearchpath(_:_:) From bd0de49d0330c261251989af5b1f61760313f9ae Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 14 Jul 2023 07:40:13 -0700 Subject: [PATCH 3210/4563] SE-0403: christen SE-0403 and schedule for review (#2105) --- ...ge-targets.md => 0403-swiftpm-mixed-language-targets.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-swiftpm-mixed-language-targets.md => 0403-swiftpm-mixed-language-targets.md} (99%) diff --git a/proposals/NNNN-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md similarity index 99% rename from proposals/NNNN-swiftpm-mixed-language-targets.md rename to proposals/0403-swiftpm-mixed-language-targets.md index 63eceda764..d6ce31032c 100644 --- a/proposals/NNNN-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -1,9 +1,9 @@ # Package Manager Mixed Language Target Support -* Proposal: [SE-NNNN](NNNN-swiftpm-mixed-language-targets.md) +* Proposal: [SE-0403](0403-swiftpm-mixed-language-targets.md) * Authors: [Nick Cooke](https://github.com/ncooke3) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Saleem Abdulrasool](https://github.com/compnerd) +* Status: **Scheduled for review (July 17, 2023...July 28, 2023)** * Implementation: [apple/swift-package-manager#5919](https://github.com/apple/swift-package-manager/pull/5919) * Decision Notes: [Pitch](https://forums.swift.org/t/61564) From 7003da1439ad60896ec14657dfce829f04b0632c Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 14 Jul 2023 13:10:58 -0500 Subject: [PATCH 3211/4563] Add missing `return` in noncopyable types proposal The `guard`-`else` block in one of the examples is missing an exit. --- proposals/0390-noncopyable-structs-and-enums.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0390-noncopyable-structs-and-enums.md b/proposals/0390-noncopyable-structs-and-enums.md index b6e5455912..c6d946132d 100644 --- a/proposals/0390-noncopyable-structs-and-enums.md +++ b/proposals/0390-noncopyable-structs-and-enums.md @@ -579,6 +579,7 @@ is not consumed may continue using it: let x = FileDescriptor() guard let condition = getCondition() else { consume(x) + return } // We can continue using x here, since only the exit branch of the guard // consumed it From eb1e616281d34c4bd36ea495b9a86efe4edcf387 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 14 Jul 2023 15:25:38 -0700 Subject: [PATCH 3212/4563] [SE-0397] Specify the same restrictions on arbitrary names as peer macros. --- proposals/0397-freestanding-declaration-macros.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index 52590e3a08..72fdfad1c5 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -153,6 +153,7 @@ Like attached peer macros, a freestanding declaration macro can expand to any de - [**Specifying newly-introduced names**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#specifying-newly-introduced-names) - Note that only `named(...)` and `arbitrary` are allowed as macro-introduced names for a declaration macro. `overloaded`, `prefixed`, and `suffixed` do not make sense when there is no declaration from which to derive names. - [**Visibility of names used and introduced by macros**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#visibility-of-names-used-and-introduced-by-macros) +- [**Restrictions on `arbitrary` names**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md##restrictions-on-arbitrary-names) - [**Permitted declaration kinds**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#permitted-declaration-kinds) One additional restriction is that a macro declaration can have at most one freestanding macro role. This is because top level and function scopes allow a combination of expressions, statements, and declarations, which would be ambiguous to a freestanding macro expansion with multiple roles. From 1d541e80d02a2debc25492af2d0c444398459f19 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Mon, 17 Jul 2023 18:13:19 -0400 Subject: [PATCH 3213/4563] Accept SE-0400 (#2109) --- proposals/0400-init-accessors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0400-init-accessors.md b/proposals/0400-init-accessors.md index e88edea1a2..36b20f0346 100644 --- a/proposals/0400-init-accessors.md +++ b/proposals/0400-init-accessors.md @@ -3,9 +3,9 @@ * Proposal: [SE-0400](0400-init-accessors.md) * Authors: [Holly Borla](https://github.com/hborla), [Doug Gregor](https://github.com/douggregor) * Review Manager: [Frederick Kellison-Linn](https://github.com/Jumhyn) -* Status: **Active review (June 14th...June 26th, 2023)** +* Status: **Accepted** * Implementation: On `main` behind experimental feature flag `InitAccessors` -* Review: ([pitch](https://forums.swift.org/t/pitch-init-accessors/64881)) ([review](https://forums.swift.org/t/se-0400-init-accessors/65583)) +* Review: ([pitch](https://forums.swift.org/t/pitch-init-accessors/64881)) ([review](https://forums.swift.org/t/se-0400-init-accessors/65583)) ([acceptance](https://forums.swift.org/t/accepted-se-0400-init-accessors/66212)) ## Introduction From 66f4467e8fb01b10dcbe1154b8a635be74a72b95 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 17 Jul 2023 17:38:49 -0700 Subject: [PATCH 3214/4563] Update proposals/0397-freestanding-declaration-macros.md Co-authored-by: John McCall --- proposals/0397-freestanding-declaration-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index 72fdfad1c5..159769efc9 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -153,7 +153,7 @@ Like attached peer macros, a freestanding declaration macro can expand to any de - [**Specifying newly-introduced names**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#specifying-newly-introduced-names) - Note that only `named(...)` and `arbitrary` are allowed as macro-introduced names for a declaration macro. `overloaded`, `prefixed`, and `suffixed` do not make sense when there is no declaration from which to derive names. - [**Visibility of names used and introduced by macros**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#visibility-of-names-used-and-introduced-by-macros) -- [**Restrictions on `arbitrary` names**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md##restrictions-on-arbitrary-names) +- [**Restrictions on `arbitrary` names**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#restrictions-on-arbitrary-names) - [**Permitted declaration kinds**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#permitted-declaration-kinds) One additional restriction is that a macro declaration can have at most one freestanding macro role. This is because top level and function scopes allow a combination of expressions, statements, and declarations, which would be ambiguous to a freestanding macro expansion with multiple roles. From f92a3d2f59abbc0737127d59d46fae65959c123a Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 17 Jul 2023 10:52:41 -0700 Subject: [PATCH 3215/4563] Proposal for String Validating Initializers --- .../nnnn-string-validating-initializers.md | 283 ++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 proposals/nnnn-string-validating-initializers.md diff --git a/proposals/nnnn-string-validating-initializers.md b/proposals/nnnn-string-validating-initializers.md new file mode 100644 index 0000000000..bdd499ce1d --- /dev/null +++ b/proposals/nnnn-string-validating-initializers.md @@ -0,0 +1,283 @@ +# String initializers with encoding validation + +* Proposal: [SE-NNNN String initializers with encoding validation](https://gist.github.com/glessard/d1ed79b7968b4ad2115462b3d1eba805) +* Author: [Guillaume Lessard](https://github.com/glessard) +* Review Manager: TBD +* Status: Pitch +* Bugs: rdar://99276048, rdar://99832858 +* Implementation: (pending) +* Review: ([pitch](https://forums.swift.org/t/66206)) +* Previous Revision: ([0](https://gist.github.com/glessard/d1ed79b7968b4ad2115462b3d1eba805)) + +## Introduction + +We propose adding new `String` failable initializers that validate encoded input, and return `nil` when the input contains any invalid elements. + +## Motivation + +The `String` type guarantees that it represents well-formed Unicode text. When data representing text is received from a file, the network, or some other source, it may be relevant to store it in a `String`, but that data must be validated first. `String` already provides a way to transform data to valid Unicode by repairing invalid elements, but such a transformation is often not desirable, especially when dealing with untrusted sources. For example a JSON decoder cannot transform its input; it must fail if a span representing text contains any invalid UTF-8. + +This functionality has not been available directly from the standard library. It is possible to compose it using existing public API, but only at the cost of extra memory copies and allocations. The standard library is uniquely positioned to implement this functionality in a performant way. + +## Proposed Solution + +We will add a new `String` initializer that can fail, returning `nil`, when its input is found to be invalid according the encoding represented by a type parameter that conforms to `Unicode.Encoding`. + +```swift +extension String { + public init?( + validating codeUnits: some Sequence, as: Encoding.Type + ) +} +``` + +For convenience and discoverability, we will also provide initializers that specify the input encoding as part as an argument label: + +```swift +extension String { + public init?(validatingAsUTF8 codeUnits: some Sequence) + + public init?(validatingAsUTF16 codeUnits: some Sequence) + + public init?(validatingAsUTF32 codeUnits: some Sequence) +} +``` + +These will construct a new `String`, returning `nil` when their input is found invalid according to the encoding specified by the label. + +When handling with data obtained from C, it is frequently the case that UTF-8 data is represented by `CChar` rather than `UInt8`. We will provide a convenience initializer for this use case, noting that it typically involves contiguous memory, and as such is well-served by explicitly using an abstraction for contiguous memory (`UnsafeBufferPointer`): + +```swift +extension String { + public init?(validatingAsUTF8 codeUnits: UnsafeBufferPointer) +} +``` + +`String` already features a validating initializer for UTF-8 input. Is is intended for C interoperability, but its argument label does not convey the expectation that its input is a null-terminated C string. We propose to rename it in order to clarify this: + +```swift +extension String { + public init?(validatingCString nullTerminatedUTF8: UnsafePointer) + + @available(Swift 5.XLIX, deprecated, renamed:"String.init(validatingCString:)") + public init?(validatingUTF8 cString: UnsafePointer) +} +``` + +## Detailed Design + +We want these new initializers to be performant. As such, their implementation should minimize the number of memory allocations and copies required. We achieve this performance with `@inlinable` implementations that leverage `withContiguousStorageIfAvailable` to provide a concrete (`internal`) code path for the validation cases. The concrete `internal` initializer itself calls a number of functions internal to the standard library. + +```swift +extension String { + /// Create a new `String` by copying and validating the sequence of + /// code units passed in, according to the specified encoding. + /// + /// This initializer does not try to repair ill-formed code unit sequences. + /// If any are found, the result of the initializer is `nil`. + /// + /// The following example calls this initializer with the contents of two + /// different arrays---first with a well-formed UTF-8 code unit sequence and + /// then with an ill-formed UTF-16 code unit sequence. + /// + /// let validUTF8: [UInt8] = [67, 97, 102, 195, 169] + /// let valid = String(validating: validUTF8, as: UTF8.self) + /// print(valid) + /// // Prints "Optional("Café")" + /// + /// let invalidUTF16: [UInt16] = [0x41, 0x42, 0xd801] + /// let invalid = String(validating: invalidUTF16, as: UTF16.self) + /// print(invalid) + /// // Prints "nil" + /// + /// - Parameters + /// - codeUnits: A sequence of code units that encode a `String` + /// - encoding: An implementation of `Unicode.Encoding` that should be used + /// to decode `codeUnits`. + @inlinable + public init?( + validating codeUnits: some Sequence, as: Encoding.Type + ) where Encoding: Unicode.Encoding +} +``` + +```swift +extension String { + /// Create a new `String` by copying and validating the sequence of + /// UTF-8 code units passed in. + /// + /// This initializer does not try to repair ill-formed code unit sequences. + /// If any are found, the result of the initializer is `nil`. + /// + /// The following example calls this initializer with the contents of two + /// different arrays---first with a well-formed UTF-8 code unit sequence and + /// then with an ill-formed code unit sequence. + /// + /// let validUTF8: [UInt8] = [67, 97, 102, 195, 169] + /// let valid = String.init(validatingAsUTF8: validUTF8) + /// print(valid) + /// // Prints "Optional("Café")" + /// + /// let invalidUTF8: [UInt8] = [67, 195, 0] + /// let invalid = String.init(validatingAsUTF8: invalidUTF8) + /// print(invalid) + /// // Prints "nil" + /// + /// - Parameters + /// - codeUnits: A sequence of code units that encode a `String` + public init?(validatingAsUTF8 codeUnits: some Sequence) + + /// Create a new `String` by copying and validating the sequence of + /// UTF-16 code units passed in. + /// + /// This initializer does not try to repair ill-formed code unit sequences. + /// If any are found, the result of the initializer is `nil`. + /// + /// The following example calls this initializer with the contents of two + /// different arrays---first with a well-formed UTF-16 code unit sequence and + /// then with an ill-formed code unit sequence. + /// + /// let validUTF16: [UInt16] = [67, 97, 102, 233] + /// let valid = String(validatingAsUTF16: validUTF16) + /// print(valid) + /// // Prints "Optional("Café")" + /// + /// let invalidUTF16: [UInt16] = [0x41, 0x42, 0xd801] + /// let invalid = String(validatingAsUTF16: invalidUTF16) + /// print(invalid) + /// // Prints "nil" + /// + /// - Parameters + /// - codeUnits: A sequence of code units that encode a `String` + public init?(validatingAsUTF16 codeUnits: some Sequence) + + /// Create a new `String` by copying and validating the sequence of + /// UTF-32 code units passed in. + /// + /// This initializer does not try to repair ill-formed code unit sequences. + /// If any are found, the result of the initializer is `nil`. + /// + /// The following example calls this initializer with the contents of two + /// different arrays---first with correct UTF-32 code units and then with + /// a sequence containing an ill-formed code unit. + /// + /// let validUTF32: [UInt32] = [67, 97, 102, 233] + /// let valid = String(validatingAsUTF32: validUTF32) + /// print(valid) + /// // Prints "Optional("Café")" + /// + /// let invalidUTF32: [UInt32] = [0x41, 0x42, 0xd801] + /// let invalid = String(validatingAsUTF32: invalidUTF32) + /// print(invalid) + /// // Prints "nil" + /// + /// - Parameters + /// - codeUnits: A sequence of code units that encode a `String` + public init?(validatingAsUTF32 codeUnits: some Sequence) +} +``` + +```swift +extension String { + /// Create a new `String` by copying and validating the sequence of `CChar` + /// passed in, by interpreting them as UTF-8 code units. + /// + /// This initializer does not try to repair ill-formed code unit sequences. + /// If any are found, the result of the initializer is `nil`. + /// + /// The following example calls this initializer with pointers to the + /// contents of two different `CChar` arrays---first with a well-formed UTF-8 + /// code unit sequence and then with an ill-formed code unit sequence. + /// + /// let validUTF8: [CChar] = [67, 97, 102, -61, -87] + /// validUTF8.withUnsafeBufferPointer { + /// let s = String.init(validatingAsUTF8: $0) + /// print(s) + /// } + /// // Prints "Optional("Café")" + /// + /// let invalidUTF8: [CChar] = [67, -61, 0] + /// invalidUTF8.withUnsafeBufferPointer { + /// let s = String.init(validatingAsUTF8: $0) + /// print(s) + /// } + /// // Prints "nil" + /// + /// - Parameters + /// - codeUnits: A sequence of code units that encode a `String` + public init?(validatingAsUTF8 codeUnits: UnsafeBufferPointer) +} +``` + +```swift +extension String { + /// Create a new string by copying and validating the null-terminated UTF-8 + /// data referenced by the given pointer. + /// + /// This initializer does not try to repair ill-formed UTF-8 code unit + /// sequences. If any are found, the result of the initializer is `nil`. + /// + /// The following example calls this initializer with pointers to the + /// contents of two different `CChar` arrays---first with well-formed + /// UTF-8 code unit sequences and the second with an ill-formed sequence at + /// the end. + /// + /// let validUTF8: [CChar] = [67, 97, 102, -61, -87, 0] + /// validUTF8.withUnsafeBufferPointer { ptr in + /// let s = String(validatingUTF8: ptr.baseAddress!) + /// print(s) + /// } + /// // Prints "Optional("Café")" + /// + /// let invalidUTF8: [CChar] = [67, 97, 102, -61, 0] + /// invalidUTF8.withUnsafeBufferPointer { ptr in + /// let s = String(validatingUTF8: ptr.baseAddress!) + /// print(s) + /// } + /// // Prints "nil" + /// + /// - Parameter cString: A pointer to a null-terminated UTF-8 code sequence. + @_silgen_name("sSS14validatingUTF8SSSgSPys4Int8VG_tcfC") + public init?(validatingCString nullTerminatedCodeUnits: UnsafePointer) + + @available(Swift 5.XLIX, deprecated, renamed:"String.init(validatingCString:)") + @_silgen_name("_swift_stdlib_legacy_String_validatingUTF8") + @_alwaysEmitIntoClient + public init?(validatingUTF8 cString: UnsafePointer) +} +``` + +## Source Compatibility + +This proposal is strictly additive. + +## ABI Compatibility + +This proposal adds new functions to the ABI. + +## Implications on adoption + +This feature requires a new version of the standard library. + +## Alternatives considered + +#### The `validatingAsUTF8` argument label + +The argument label `validatingUTF8` seems like it may have been preferable to `validatingAsUTF8`, but using the former would have been source-breaking. The C string validation initializer takes an `UnsafePointer`, but it can also accept `[UInt8]` via implicit pointer conversion. Any use site that passes an `[UInt8]` to the C string validation initializer would have changed behaviour upon recompilation, from considering a null character (`\0`) as the termination of the C string to considering it as a valid, non-terminating character. + +#### Have the `CChar`-validating function take a parameter of type `some Sequence` + +This would produce a compile-time ambiguity on platforms where `CChar` is typealiased to `UInt8` rather than `Int8`. Using `UnsafeBufferPointer` as the parameter type will avoid such a compile-time ambiguity. + +## Future directions + +#### Throw an error containing information about a validation failure + +When decoding a byte stream, obtaining the details of a validation failure would be useful in order to diagnose issues. We would like to provide this functionality, but the current input validation functionality is not well-suited for it. This is left as a future improvement. + +## Acknowledgements + +Thanks to Michael Ilseman, Tina Liu and Quinn Quinn for discussions about input validation issues. + +[SE-0027](https://github.com/apple/swift-evolution/blob/main/proposals/0027-string-from-code-units.md) by [Zachary Waldowski](https://github.com/zwaldowski) was reviewed in February 2016, covering similar ground. It was rejected at the time because the design of `String` had not been finalized. The name `String.init(validatingCString:)` was suggested as part of SE-0027. Lily Ballard later [pitched](https://forums.swift.org/t/21538) a renaming of `String.init(validatingUTF8:)`, citing consistency with other `String` API involving C strings. + From 251fd0ffed75f10c0f8a64a82bc6a5144a04b347 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 18 Jul 2023 20:42:43 +0100 Subject: [PATCH 3216/4563] Update 0220-count-where.md (#2111) --- proposals/0220-count-where.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/0220-count-where.md b/proposals/0220-count-where.md index 777905ab1a..ae528b3b32 100644 --- a/proposals/0220-count-where.md +++ b/proposals/0220-count-where.md @@ -2,10 +2,11 @@ * Proposal: [SE-0220](0220-count-where.md) * Author: [Soroush Khanlou](https://github.com/khanlou) -* Review Manager: [Chris Lattner](https://github.com/lattner) -* Status: **Accepted (2018-08-15)** +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Active Review (July 18 – July 28, 2023)** +* Previous status: **Accepted (2018-08-15) then Expired** * Implementation: [apple/swift#16099](https://github.com/apple/swift/pull/16099), [apple/swift#22289](https://github.com/apple/swift/pull/22289) (revert PR) -* Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0220-count-where/15280), [Additional Commentary](https://forums.swift.org/t/require-parameter-names-when-referencing-to-functions/27048) +* Previous Decision Notes: [Rationale](https://forums.swift.org/t/accepted-se-0220-count-where/15280), [Additional Commentary](https://forums.swift.org/t/require-parameter-names-when-referencing-to-functions/27048) ## Introduction From 4209853a2fbb66a49d883b8d263467bf50c31ddb Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 18 Jul 2023 15:35:14 -0700 Subject: [PATCH 3217/4563] Accept SE-0401 with modifications. (#2112) Accept SE-0401 and change the upcoming feature identifier to `DisableOutwardActorInference`. --- proposals/0401-remove-property-wrapper-isolation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0401-remove-property-wrapper-isolation.md b/proposals/0401-remove-property-wrapper-isolation.md index b86578455b..5a32bd859c 100644 --- a/proposals/0401-remove-property-wrapper-isolation.md +++ b/proposals/0401-remove-property-wrapper-isolation.md @@ -3,10 +3,10 @@ * Proposal: [SE-0401](0401-remove-property-wrapper-isolation.md) * Authors: [BJ Homer](https://github.com/bjhomer) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active review (June 16th...June 30th, 2023)** +* Status: **Accepted** * Implementation: [apple/swift#63884](https://github.com/apple/swift/pull/63884) -* Upcoming Feature Flag: `DisableActorInferenceFromPropertyWrapperUsage` -* Review: ([pitch](https://forums.swift.org/t/pitch-stop-inferring-actor-isolation-based-on-property-wrapper-usage/63262)) ([review](https://forums.swift.org/t/se-0401-remove-actor-isolation-inference-caused-by-property-wrappers/65618)) +* Upcoming Feature Flag: `DisableOutwardActorInference` +* Review: ([pitch](https://forums.swift.org/t/pitch-stop-inferring-actor-isolation-based-on-property-wrapper-usage/63262)) ([review](https://forums.swift.org/t/se-0401-remove-actor-isolation-inference-caused-by-property-wrappers/65618)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0401-remove-actor-isolation-inference-caused-by-property-wrappers/66241)) ## Introduction @@ -130,7 +130,7 @@ The [original motivation](https://forums.swift.org/t/se-0401-remove-actor-isolat The proposal is simple: In the Swift 6 language mode, property wrappers used within a type will not affect the type's actor isolation. We simply disable this inference step entirely. -In the Swift 5 language mode, isolation will continue to be inferred as it currently is. The new behavior can be requested using the **`-enable-upcoming-feature DisableActorInferenceFromPropertyWrapperUsage`** compiler flag. +In the Swift 5 language mode, isolation will continue to be inferred as it currently is. The new behavior can be requested using the **`-enable-upcoming-feature DisableOutwardActorInference`** compiler flag. ## Detailed design From 3f66d8d7d097c4330e7edfcde3ad701e0e7bb29c Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 19 Jul 2023 09:46:40 -0700 Subject: [PATCH 3218/4563] additional future directions --- proposals/nnnn-string-validating-initializers.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/nnnn-string-validating-initializers.md b/proposals/nnnn-string-validating-initializers.md index bdd499ce1d..f026323a66 100644 --- a/proposals/nnnn-string-validating-initializers.md +++ b/proposals/nnnn-string-validating-initializers.md @@ -275,6 +275,10 @@ This would produce a compile-time ambiguity on platforms where `CChar` is typeal When decoding a byte stream, obtaining the details of a validation failure would be useful in order to diagnose issues. We would like to provide this functionality, but the current input validation functionality is not well-suited for it. This is left as a future improvement. +#### Add normalizing initializers, improve repairing initializers + +It is often desirable to normalize strings, but the standard library does not expose public API for doing so. Hypothetical normalizing initializers should have convenience initializers similar to the validating convenience initializers. Input-repairing initializers should also have convenience initializers. + ## Acknowledgements Thanks to Michael Ilseman, Tina Liu and Quinn Quinn for discussions about input validation issues. From e988b2a81ffc0721a34f77fd16902bcd88eab38a Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 19 Jul 2023 10:13:48 -0700 Subject: [PATCH 3219/4563] additional future directions --- proposals/nnnn-string-validating-initializers.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-string-validating-initializers.md b/proposals/nnnn-string-validating-initializers.md index f026323a66..6f04d837d0 100644 --- a/proposals/nnnn-string-validating-initializers.md +++ b/proposals/nnnn-string-validating-initializers.md @@ -275,9 +275,17 @@ This would produce a compile-time ambiguity on platforms where `CChar` is typeal When decoding a byte stream, obtaining the details of a validation failure would be useful in order to diagnose issues. We would like to provide this functionality, but the current input validation functionality is not well-suited for it. This is left as a future improvement. -#### Add normalizing initializers, improve repairing initializers +#### Add normalizing initializers -It is often desirable to normalize strings, but the standard library does not expose public API for doing so. Hypothetical normalizing initializers should have convenience initializers similar to the validating convenience initializers. Input-repairing initializers should also have convenience initializers. +It is often desirable to normalize strings, but the standard library does not expose public API for doing so. Hypothetical normalizing initializers should have convenience initializers similar to the validating convenience initializers. + +#### Other + +Add initializer from `some Sequence`. + +Add more discoverable convenience initializers for the input-repairing initialization case. + +Add API devoted to input validation specifically. ## Acknowledgements From 10ad5eba40fae24571c06eeaa3dc790501c21469 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 19 Jul 2023 10:27:30 -0700 Subject: [PATCH 3220/4563] Add link to staged package --- proposals/nnnn-string-validating-initializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-string-validating-initializers.md b/proposals/nnnn-string-validating-initializers.md index 6f04d837d0..183cf9d2f8 100644 --- a/proposals/nnnn-string-validating-initializers.md +++ b/proposals/nnnn-string-validating-initializers.md @@ -5,7 +5,7 @@ * Review Manager: TBD * Status: Pitch * Bugs: rdar://99276048, rdar://99832858 -* Implementation: (pending) +* Implementation: [Staged package](https://github.com/apple/swift-evolution-staging/tree/input-validating-string-initializers) * Review: ([pitch](https://forums.swift.org/t/66206)) * Previous Revision: ([0](https://gist.github.com/glessard/d1ed79b7968b4ad2115462b3d1eba805)) From 3c500810ff86f1a9fb8c6ae131146142f9441aeb Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 20 Jul 2023 15:38:31 -0400 Subject: [PATCH 3221/4563] Accept SE-0402 --- proposals/0402-extension-macros.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0402-extension-macros.md b/proposals/0402-extension-macros.md index f37046b566..6e916d20d2 100644 --- a/proposals/0402-extension-macros.md +++ b/proposals/0402-extension-macros.md @@ -3,9 +3,9 @@ * Proposal: [SE-0402](0402-extension-macros.md) * Authors: [Holly Borla](https://github.com/hborla) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (July 3rd...17th, 2023)** +* Status: **Accepted** * Implementation: [apple/swift#66967](https://github.com/apple/swift/pull/66967), [apple/swift-syntax#1859](https://github.com/apple/swift-syntax/pull/1859) -* Review: ([pitch](https://forums.swift.org/t/pitch-generalize-conformance-macros-as-extension-macros/65653)) ([review](https://forums.swift.org/t/se-0402-generalize-conformance-macros-as-extension-macros/65965)) +* Review: ([pitch](https://forums.swift.org/t/pitch-generalize-conformance-macros-as-extension-macros/65653)) ([review](https://forums.swift.org/t/se-0402-generalize-conformance-macros-as-extension-macros/65965)) ([acceptance](https://forums.swift.org/t/accepted-se-0402-generalize-conformance-macros-as-extension-macros/66276)) ## Introduction From 5c2a48755ac8a2fa3c700d5647c9910003bec5c4 Mon Sep 17 00:00:00 2001 From: Karl <5254025+karwa@users.noreply.github.com> Date: Sun, 23 Jul 2023 21:35:18 +0200 Subject: [PATCH 3222/4563] Nested Protocols (#2070) * Nested Protocols * Address review comments * Assign SE-0404 to nested protocols and schedule for review. --------- Co-authored-by: Holly Borla --- proposals/0404-nested-protocols.md | 168 +++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 proposals/0404-nested-protocols.md diff --git a/proposals/0404-nested-protocols.md b/proposals/0404-nested-protocols.md new file mode 100644 index 0000000000..0a8ce1abef --- /dev/null +++ b/proposals/0404-nested-protocols.md @@ -0,0 +1,168 @@ +# Allow Protocols to be Nested in Non-Generic Contexts + +* Proposal: [SE-0404](0404-nested-protocols.md) +* Authors: [Karl Wagner](https://github.com/karwa) +* Review Manager: [Holly Borla](https://github.com/hbborla) +* Status: **Scheduled for review (July 24 - August 7, 2023)** +* Implementation: [apple/swift#66247](https://github.com/apple/swift/pull/66247) (gated behind flag `-enable-experimental-feature NestedProtocols`) +* Review: ([pitch](https://forums.swift.org/t/pitch-allow-protocols-to-be-nested-in-non-generic-contexts/65285)) + +## Introduction + +Allows protocols to be nested in non-generic `struct/class/enum/actor`s, and functions. + +## Motivation + +Nesting nominal types inside other nominal types allows developers to express a natural scope for the inner type -- for example, `String.UTF8View` is `struct UTF8View` nested within `struct String`, and its name clearly communicates its purpose as an interface to the UTF-8 code-units of a String value. + +However, nesting is currently restricted to struct/class/enum/actors within other struct/class/enum/actors; protocols cannot be nested at all, and so must always be top-level types within a module. This is unfortunate, and we should relax this restriction so that developers can express protocols which are naturally scoped to some outer type. + +## Proposed solution + +We would allow nesting protocols within non-generic struct/class/enum/actors, and also within functions that do not belong to a generic context. + +For example, `TableView.Delegate` is naturally a delegate protocol pertaining to table-views. Developers should be declare it as such - nested within their `TableView` class: + +```swift +class TableView { + protocol Delegate: AnyObject { + func tableView(_: TableView, didSelectRowAtIndex: Int) + } +} + +class DelegateConformer: TableView.Delegate { + func tableView(_: TableView, didSelectRowAtIndex: Int) { + // ... + } +} +``` + +Currently, developers resort to giving things compound names, such as `TableViewDelegate`, to express the same natural scoping that could otherwise be expressed via nesting. + +As an additional benefit, within the context of `TableView`, the nested protocol `Delegate` can be referred to by a shorter name (as is the case with all other nested types): + +```swift +class TableView { + weak var delegate: Delegate? + + protocol Delegate { /* ... */ } +} +``` + +Protocols can also be nested within non-generic functions and closures. Admittedly, this is of somewhat limited utility, as all conformances to such protocols must also be within the same function. However, there is also no reason to artificially limit the complexity of the models which developers create within a function. Some codebases (of note, the Swift compiler itself) make use of large closures with nested types, and they beneift from abstractions using protocols. + +```swift +func doSomething() { + + protocol Abstraction { + associatedtype ResultType + func requirement() -> ResultType + } + struct SomeConformance: Abstraction { + func requirement() -> Int { ... } + } + struct AnotherConformance: Abstraction { + func requirement() -> String { ... } + } + + func impl(_ input: T) -> T.ResultType { + // ... + } + + let _: Int = impl(SomeConformance()) + let _: String = impl(AnotherConformance()) +} +``` + +## Detailed design + +Protocols may be nested anywhere that a struct/class/enum/actor may be nested, with the exception of generic contexts. For example, the following remains forbidden: + +```swift +class TableView { + + protocol Delegate { // Error: protocol 'Delegate' cannot be nested within a generic context. + func didSelect(_: Element) + } +} +``` + +The same applies to generic functions: + +```swift +func genericFunc(_: T) { + protocol Abstraction { // Error: protocol 'Abstraction' cannot be nested within a generic context. + } +} +``` + +And to other functions within generic contexts: + +```swift +class TableView { + func doSomething() { + protocol MyProtocol { // Error: protocol 'Abstraction' cannot be nested within a generic context. + } + } +} +``` + +Supporting this would require either: + +- Introducing generic protocols, or +- Mapping generic parameters to associated types. + +Neither is in in-scope for this proposal, but this author feels there is enough benefit here even without supporting generic contexts. Either of these would certainly make for interesting future directions. + +### Associated Type matching + +When nested in a concrete type, protocols do not witness associated type requirements. + +```swift +protocol Widget { + associatedtype Delegate +} + +struct TableWidget: Widget { + // Does NOT witness Widget.Delegate + protocol Delegate { ... } +} +``` + +Associated types associate one concrete type with one conforming type. Protocols are constraint types which many concrete types may conform to, so there is no obvious meaning to having a protocol witness an associated type requirement. + +There have been discussions in the past about whether protocols could gain an "associated protocol" feature, which would allow these networks of constraint types to be expressed. If such a feature were ever introduced, it may be reasonable for associated protocol requirements to be witnessed by nested protocols, the same way associated type requirements can be witnessed by nested concrete types today. + +## Source compatibility + +This feature is additive. + +## ABI compatibility + +This proposal is purely an extension of the language's ABI and does not change any existing features. + +## Implications on adoption + +This feature can be freely adopted and un-adopted in source code with no deployment constraints. + +In general, moving a protocol in/out of a parent context is a source-breaking change. However, this breakage can mitigated by providing a `typealias` to the new name. + +As with other nested types, the parent context forms part of the mangled name of a nested protocol. Therefore, moving a protocol in/out of a parent context is an ABI-incompatible change. + +## Future directions + +- Allow nesting other (non-protocol) types in protocols + + Protocols themselves sometimes wish to define types which are naturally scoped to that protocol. For example, the standard library's [`FloatingPointRoundingRule`](https://developer.apple.com/documentation/swift/FloatingPointRoundingRule) enum is used by a [requirement of the `FloatingPoint` protocol](https://developer.apple.com/documentation/swift/floatingpoint/round(_:)) and defined for that purpose. + +- Allow nesting protocols in generic types + + As mentioned in the Detailed Design section, there are potentially strategies that would allow nesting protocols within generic types, and one could certainly imagine ways to use that expressive capability. The community is invited to discuss potential approaches in a separate topic. + +## Alternatives considered + +None. This is a straightforward extension of the language's existing nesting functionality. + +## Acknowledgments + +Thanks to [`@jumhyn`](https://forums.swift.org/u/jumhyn/) for reminding me about this, and to [`@suyashsrijan`](https://forums.swift.org/u/suyashsrijan/). From d419d6beefc9528ea7daaf719ce4e1db2b91a434 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 24 Jul 2023 09:10:50 -0700 Subject: [PATCH 3223/4563] Update SE-0404 status to active review. (#2114) * Update SE-0404 status to active review. * Link SE-0404 review thread. --- proposals/0404-nested-protocols.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0404-nested-protocols.md b/proposals/0404-nested-protocols.md index 0a8ce1abef..1c31716859 100644 --- a/proposals/0404-nested-protocols.md +++ b/proposals/0404-nested-protocols.md @@ -3,9 +3,9 @@ * Proposal: [SE-0404](0404-nested-protocols.md) * Authors: [Karl Wagner](https://github.com/karwa) * Review Manager: [Holly Borla](https://github.com/hbborla) -* Status: **Scheduled for review (July 24 - August 7, 2023)** +* Status: **Active review (July 24 - August 7, 2023)** * Implementation: [apple/swift#66247](https://github.com/apple/swift/pull/66247) (gated behind flag `-enable-experimental-feature NestedProtocols`) -* Review: ([pitch](https://forums.swift.org/t/pitch-allow-protocols-to-be-nested-in-non-generic-contexts/65285)) +* Review: ([pitch](https://forums.swift.org/t/pitch-allow-protocols-to-be-nested-in-non-generic-contexts/65285)), ([review](https://forums.swift.org/t/se-0404-allow-protocols-to-be-nested-in-non-generic-contexts/66332)) ## Introduction From c283fff84b2171d8c6b5bce88614364d37db9600 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 24 Jul 2023 11:59:27 -0700 Subject: [PATCH 3224/4563] SE-0401 is implemented in Swift 5.9 (#2115) --- proposals/0401-remove-property-wrapper-isolation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0401-remove-property-wrapper-isolation.md b/proposals/0401-remove-property-wrapper-isolation.md index 5a32bd859c..b5c0e1dbbf 100644 --- a/proposals/0401-remove-property-wrapper-isolation.md +++ b/proposals/0401-remove-property-wrapper-isolation.md @@ -3,7 +3,7 @@ * Proposal: [SE-0401](0401-remove-property-wrapper-isolation.md) * Authors: [BJ Homer](https://github.com/bjhomer) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: [apple/swift#63884](https://github.com/apple/swift/pull/63884) * Upcoming Feature Flag: `DisableOutwardActorInference` * Review: ([pitch](https://forums.swift.org/t/pitch-stop-inferring-actor-isolation-based-on-property-wrapper-usage/63262)) ([review](https://forums.swift.org/t/se-0401-remove-actor-isolation-inference-caused-by-property-wrappers/65618)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0401-remove-actor-isolation-inference-caused-by-property-wrappers/66241)) From 488ebdb9f3ba2bd3c0a0682935d112f63396e74b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 24 Jul 2023 12:03:34 -0700 Subject: [PATCH 3225/4563] SE-0400 is implemented in Swift 5.9 (#2116) --- proposals/0400-init-accessors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0400-init-accessors.md b/proposals/0400-init-accessors.md index 36b20f0346..3736f14eb5 100644 --- a/proposals/0400-init-accessors.md +++ b/proposals/0400-init-accessors.md @@ -3,7 +3,7 @@ * Proposal: [SE-0400](0400-init-accessors.md) * Authors: [Holly Borla](https://github.com/hborla), [Doug Gregor](https://github.com/douggregor) * Review Manager: [Frederick Kellison-Linn](https://github.com/Jumhyn) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: On `main` behind experimental feature flag `InitAccessors` * Review: ([pitch](https://forums.swift.org/t/pitch-init-accessors/64881)) ([review](https://forums.swift.org/t/se-0400-init-accessors/65583)) ([acceptance](https://forums.swift.org/t/accepted-se-0400-init-accessors/66212)) From 687714d3564febcdfbd00d8cc8cd3d902d9eb1ed Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 24 Jul 2023 12:04:33 -0700 Subject: [PATCH 3226/4563] SE-0402 is implemented in Swift 5.9 (#2117) --- proposals/0402-extension-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0402-extension-macros.md b/proposals/0402-extension-macros.md index 6e916d20d2..b47ef4ee4a 100644 --- a/proposals/0402-extension-macros.md +++ b/proposals/0402-extension-macros.md @@ -3,7 +3,7 @@ * Proposal: [SE-0402](0402-extension-macros.md) * Authors: [Holly Borla](https://github.com/hborla) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: [apple/swift#66967](https://github.com/apple/swift/pull/66967), [apple/swift-syntax#1859](https://github.com/apple/swift-syntax/pull/1859) * Review: ([pitch](https://forums.swift.org/t/pitch-generalize-conformance-macros-as-extension-macros/65653)) ([review](https://forums.swift.org/t/se-0402-generalize-conformance-macros-as-extension-macros/65965)) ([acceptance](https://forums.swift.org/t/accepted-se-0402-generalize-conformance-macros-as-extension-macros/66276)) From 5ba6d42cfaefcd897a0fa886bf75e986234cd61d Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Wed, 26 Jul 2023 17:12:15 -0400 Subject: [PATCH 3227/4563] Scope down capabilities --- .../0403-swiftpm-mixed-language-targets.md | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index d6ce31032c..a116a88a21 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -94,19 +94,13 @@ MixedPackage └── TestConstants.swift ⎦ ``` -The proposed solution would enable the above package to have the following -capabilities: -1. Export public API of the mixed language sources as a single module for - clients of the package. -1. Use Obj-C compatible Swift API from target’s Swift sources within the - target’s Obj-C sources. -1. Use all API exposed by target’s Objective-C or C sources within the target’s - Swift sources. -1. Use internal C based language types within the Clang part of the module. -1. Use internal Swift types within the Swift part of the module. -1. Access target resources from a Swift and Objective-C context. -1. Define test utility types in either Obj-C or Swift and use them in both - Swift and Obj-C test files. +The proposed solution would enable the above package to do the following: +1. Export the public API from across the mixed language sources. +1. Use C/Objective-C/C++ compatible Swift API from target’s Swift sources + within the target’s C/Objective-C/C++ sources. +1. Use Swift compatible C/Objective-C/C++ API from target’s C/Objective-C/C++ + sources within the target’s Swift sources. +1. Access target resources from Swift and Objective-C contexts. ### Requirements From cab9dd27413eff799f914b0c24eb384e7b11198e Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Wed, 26 Jul 2023 17:15:12 -0400 Subject: [PATCH 3228/4563] Update limitations --- proposals/0403-swiftpm-mixed-language-targets.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index a116a88a21..e8aba3a0f4 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -102,7 +102,7 @@ The proposed solution would enable the above package to do the following: sources within the target’s Swift sources. 1. Access target resources from Swift and Objective-C contexts. -### Requirements +### Limitations Initial support for targets containing mixed language sources will have the following requirements: @@ -111,8 +111,6 @@ following requirements: 1. The target must be built on a Mac. This is because the Swift compiler-generated Objective-C compatibility header is only generated on macOS. -1. If the target contains a custom module map, it cannot contain a submodule of - the form `$(ModuleName).Swift`. ### Importing a mixed target From fe4dc24b6f8c0a87b5225ceed303d7b057b11f86 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Wed, 26 Jul 2023 17:18:41 -0400 Subject: [PATCH 3229/4563] Update 'Importing within a Swift context section' --- proposals/0403-swiftpm-mixed-language-targets.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index e8aba3a0f4..0372e60a4a 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -120,8 +120,8 @@ language target(s). #### Importing within a **Swift** context -A mixed target, `MixedPackage`, can be imported into a **Swift** file via an -`import` statement: +The public API of a mixed target, `MixedPackage`, can be imported into a +**Swift** file via an `import` statement: ```swift // MyClientTarget.swift @@ -129,10 +129,7 @@ A mixed target, `MixedPackage`, can be imported into a **Swift** file via an import MixedPackage ``` -This imports the module's `public`/`open` Swift types as well as public -Objective-C/C headers. - -_Testing targets_ can import the mixed target via +Testing targets can import the mixed target via `@testable import MixedPackage`. As expected, this will expose internal Swift types within the module. It will not expose any non-public C language types. From 71ddf3977d655ae6382ce11e17157fa50984aa1e Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Wed, 26 Jul 2023 17:46:18 -0400 Subject: [PATCH 3230/4563] Rework importing mixed lang. target into C sources --- .../0403-swiftpm-mixed-language-targets.md | 108 +++++++++--------- 1 file changed, 53 insertions(+), 55 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 0372e60a4a..4627b69ca4 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -133,68 +133,66 @@ Testing targets can import the mixed target via `@testable import MixedPackage`. As expected, this will expose internal Swift types within the module. It will not expose any non-public C language types. -#### Importing within an **Objective-C** context +#### Importing within an **C/Objective-C/C++** context -A mixed target, `MixedPackage`, can be imported into an **Objective-C** file -via _importing the module_ or _importing the mixed target's public headers_. +How a mixed target, `MixedPackage`, is imported into an **C/Objective-C/C++** +file will vary dependong on the language it is being imported in. -- **Importing the module** - ```objc - // MyClientTarget.m +When Clang modules are supported, clients can import the module. Textual +imports are also an option. - @import MixedPackage; - ``` +**Note that the C/Objective-C/C++ compatible Swift API is only available via +textually importing the generated Swift header.** - This imports the module's `public`/`open` Swift types as well as public - Objective-C/C headers. +For this example, consider `MixedPackage` being organized as such: -- **Importing the mixed target's public headers** - For this example, consider `MixedPackage` being organized as such: - ``` - MixedPackage - ├── Package.swift - └── Sources -    ├── NewCar.swift -   └── include ]-- Public headers directory -     ├── MixedPackage - │ ├── OldCar.h -    │ └── MixedPackage.h ⎤-- These headers are generated by the - └── MixedPackage-Swift.h ⎦ package manager and virtually overlayed - within the package's public headers - directory. More details in the Detailed - Design section.* - ``` +``` +MixedPackage +├── Package.swift +└── Sources +    ├── NewCar.swift +   └── include ]-- Public headers directory +    ├── MixedPackage + │ ├── OldCar.h +   │ └── MixedPackage.h ⎤-- These headers are generated + └── MixedPackage-Swift.h ⎦ by the package manager and + virtually overlayed + within the package's public + headers directory. More + details in the Detailed + Design section. * +``` - > * NOTE: The generated interop header (`$(TARGET_NAME)-Swift.h`) is always - > generated for a mixed target (even when there is no Objective-C compatible - > Swift API). - > - > On the other hand, the umbrella header located at - > `$(PUBLIC_HDRS_DIR)/$(TARGET_NAME)/$(TARGET_NAME).h` is only generated when - > the mixed target does not contain an umbrella header at that path. It - > imports all Objective-C/C headers and acts as the bridging header used in - > the generated interop header. - - `MixedPackage`'s public headers directory (`include`) is added a header - search path to client targets. The following example demonstrates all the - possible public headers that can be imported from `MixedPackage`. - - ```objc - // MyClientTarget.m - - // Imports types defined in `OldCar.h`. - #import "MixedPackage/OldCar.h" - // Imports a generated umbrella header that includes all public Objective-C/C - // headers within `MixedPackage`. ** - #import "MixedPackage/MixedPackage.h" - // Imports Objective-C compatible Swift types defined in `MixedPackage`. - #import "MixedPackage-Swift.h" - ``` +\* The generated interop header (`$(TARGET_NAME)-Swift.h`) is always +generated for a mixed target (even when there is no Objective-C compatible +Swift API). On the other hand, the umbrella header located at +`$(PUBLIC_HDRS_DIR)/$(TARGET_NAME)/$(TARGET_NAME).h` is only generated when +the mixed target does not contain an umbrella header at that path. It imports +all Objective-C/C headers and acts as the bridging header used in the generated +interop header. + +`MixedPackage`'s public headers directory (`include`) is added a header +search path to client targets. The following example demonstrates all the +possible public headers that can be imported from `MixedPackage`. + +```objc +// MyClientTarget.m + +// Import the public non-Swift API if module imports are supported. +@import MixedPackage; +// Imports types defined in `OldCar.h`. +#import "MixedPackage/OldCar.h" +// Imports a generated umbrella header that includes all public Objective-C/C +// headers within `MixedPackage`. ** +#import "MixedPackage/MixedPackage.h" +// Imports Objective-C compatible Swift types defined in `MixedPackage`. +#import "MixedPackage-Swift.h" +``` - > ** NOTE: If imported, the generated umbrella/bridging header will need to - > appear _before_ importing the generated interop header. This is due to the - > intrinsic relationship between the generated interop header and the - > generated umbrella/bridging header. +> ** NOTE: If imported, the generated umbrella/bridging header will need to +> appear _before_ importing the generated interop header. This is due to the +> intrinsic relationship between the generated interop header and the +> generated umbrella/bridging header. ## Detailed design From 472c2f4e6f5d7c8c646849c4de8c01d4f379f7d3 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Wed, 26 Jul 2023 17:56:46 -0400 Subject: [PATCH 3231/4563] Update test targets sections --- .../0403-swiftpm-mixed-language-targets.md | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 4627b69ca4..302cd17cbd 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -625,9 +625,7 @@ The below sample shows what this overlay file may look like: ### Mixed language Test Targets To complement library targets with mixed languages, mixed test targets are -also supported as part of this proposal. Using the same strategy, mixed test -targets are built with intermediary module maps that enable the sharing of -mixed language testing utilities. +also supported as part of this proposal. Using the [example package][mixed-package] from before, consider the following layout of the package's `Tests` directory. @@ -645,11 +643,10 @@ MixedPackage ``` The types defined in `ObjcTestConstants.h` are visible in `SithTests.m` (via -importing the header) and implicitly visible in `JediTests.swift`. +importing the header). The Objective-C compatible types defined in `TestConstants.swift` are visible -in `SithTests.m` (via importing the generated `MixedPackageTests-Swift.h` -header) and implicitly visible in `JediTests.swift`. +in `JediTests.swift` (via importing the header). This design should give package authors flexibility in designing test suites for their mixed targets. @@ -664,13 +661,6 @@ targets. the mixed target's headers via the [`.headerSearchPaths(_:_:)`][headerSearchPath] setting. -- **Expose non-public C-Language types to Swift test files**: This can be done - by adding a header to the test target that imports the desired non-public - header(s). Note that the test target will need to be configured to include - the mixed test target's header in its header search paths (see above - strategy). Additionally, the test target will need at least one `.m` file - (e.g. a blank `Dummy.m` file). - - **Expose `internal` Swift types to Swift test files**: As expected, Swift types with `internal` access be tested within a Swift test file by importing with `@testable`. @@ -696,12 +686,6 @@ There are several failure cases that may surface to end users: with mixed language sources are only supported for library and test targets. ``` -- Attempting to build a mixed target containing a custom module map - that contains a `$(MixedTargetName).Swift` submodule. - ``` - The target's module map may not contain a Swift submodule for the - module \(target name). - ``` ### Testing From e6fac62faa19f82449cf41d77832d2127a338f75 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Wed, 26 Jul 2023 18:00:02 -0400 Subject: [PATCH 3232/4563] Remove intermediates module map section --- .../0403-swiftpm-mixed-language-targets.md | 53 +------------------ 1 file changed, 2 insertions(+), 51 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 302cd17cbd..7408f15188 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -265,46 +265,11 @@ would look like such: └── MixedTarget.build ├── ... └── Intermediates - ├── module.modulemap ├── all-product-headers.yaml ├── unextended-module.modulemap └── unextended-module-overlay.yaml ``` -#### module.modulemap - -A mixed target will either have a custom module map defined by the package -author or it won’t. In both cases, the package manager creates an intermediary -module map in the `Intermediates` subdirectory that exposes the generated -interop header via a submodule. This is needed when compiling the target’s -Objective-C sources so that the sources can import the generated interop -header–– making Objective-C compatible types defined in the target’s Swift API -visible within an Objective-C context. - -> Note: As opposed to the unextended module map, the contents of the -> `module MixedTarget { … }` is not important here because the module map’s -> sole purpose is to assist in the compilation of the target’s Objective-C -> sources, which access other C Language types defined in the target via -> importing headers found in the target’s header search paths. Nonetheless, the -> `module MixedTarget { … }` declaration is the same between the intermediary -> `module.modulemap` and `unextended-module.modulemap` for consistency. - -``` -// module.modulemap - -// See above note for context regarding this module declaration. -module MixedTarget { - umbrella "/Users/crusty/Developer/MixedTarget/Sources" - export * -} - -// A submodule exposes the generated interop header for Objective-C clients. -module MixedTarget.Swift { - header "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/MixedTarget-Swift.h" - requires objc -} -``` - #### all-product-headers.yaml This overlay file’s purpose is to facilitate the building of the Clang sources @@ -313,14 +278,9 @@ accordingly so that the build can be successful. The positioning is determined based on whether or not the package author has defined a custom module map in the mixed target: - If there is a custom module map, then the overlay is created over the - directory that contains the custom module map. The overlay has two + directory that contains the custom module map. The overlay has the following responsibilities: - 1. Redirect the path to the custom module map to the intermediary module - map. This means that when the custom module map is read from, the - contents will instead come from the intermediary module map. This is - done to avoid a module redeclaration error from there being two - discoverable, distinct module maps. - 2. Add the generated interop header to the directory. This will enable the + 1. Add the generated interop header to the directory. This will enable the header to be imported within this target via `#import MixedTarget-Swift.h`. - If there is no custom module map, then the overlay is created over the @@ -345,15 +305,6 @@ without a custom module map. "type": "directory", "contents": [ - // Note #2: Including the below file has no effect when there is - // no custom module map because the module.modulemap already - // exists in the Intermediates subdirectory. It is included to - // simplify the VFS templating logic between the two cases. - { - "name": "module.modulemap", - "type": "file", - "external-contents": "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/Intermediates/module.modulemap", - }, { "name": "MixedTarget-Swift.h", "type": "file", From 827ae0a09d00270f7275f932b832d0b164abf4a8 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Wed, 26 Jul 2023 22:36:18 -0700 Subject: [PATCH 3233/4563] Accept SE-0387 Swift SDKs for Cross-Compilation --- proposals/0387-cross-compilation-destinations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0387-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md index e5601adacf..5022d7cd72 100644 --- a/proposals/0387-cross-compilation-destinations.md +++ b/proposals/0387-cross-compilation-destinations.md @@ -3,7 +3,7 @@ - Proposal: [SE-0387](0387-cross-compilation-destinations.md) - Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd), [Evan Wilde](https://github.com/etcwilde) - Review Manager: [Mishal Shah](https://github.com/shahmishal) -- Status: **Active Review (April 27...May 5, 2023)** +- Status: **Accepted** - Implementation: [apple/swift-package-manager#5911](https://github.com/apple/swift-package-manager/pull/5911), [apple/swift-package-manager#5922](https://github.com/apple/swift-package-manager/pull/5922), [apple/swift-package-manager#6023](https://github.com/apple/swift-package-manager/pull/6023), From b82c2d52baba8fdc58237ce25f6df36e853f508f Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Thu, 27 Jul 2023 15:31:08 -0400 Subject: [PATCH 3234/4563] Update solution high-level text --- .../0403-swiftpm-mixed-language-targets.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 7408f15188..c60d21fec4 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -56,15 +56,16 @@ packages without exposing language barrier complexity to clients. At a high level, the package creation process is split into two parts based on the language of the sources. The Swift sources are built by the Swift compiler -and the C Language sources are built by the Clang compiler. To achieve -interoperability between the two halves of the package, a few things have to -happen: +and the C Language sources are built by the Clang compiler. Achieving +interoperability between the two halves of the package depends on which +language(s) is/are being mixed with Swift. Package authors will need to +explicitly opt in to language interoperability via the `PackageDescription` +module's [`SwiftSetting.InteroperabilityMode`] API. + 1. The Swift compiler is made aware of the Clang part of the package when building the Swift sources into a `swiftmodule`. -1. The generated interoperability header emitted by the Swift compiler is added - as a submodule to the Clang part of the package’s generated module map. -1. The Clang part of the package is built with knowledge of the generated - interoperability header. +1. The Clang part of the package is built with knowledge of any generated + interoperability headers. The [following example][mixed-package] defines a package containing mixed language sources. @@ -724,3 +725,5 @@ listed in the Future Directions section as an area of future work. [CSetting]: https://developer.apple.com/documentation/packagedescription/target/csettings [headerSearchPath]: https://developer.apple.com/documentation/packagedescription/csetting/headersearchpath(_:_:) + +[`SwiftSetting.InteroperabilityMode`]: https://developer.apple.com/documentation/packagedescription/swiftsetting/interoperabilitymode \ No newline at end of file From 3d34d9e5105a02e32d87b41582fdac28b6eb9ced Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 28 Jul 2023 00:16:18 +0100 Subject: [PATCH 3235/4563] Fix typo in SE-0387: Swift SDKs (#2120) `sandboxing and codesigning toolchains` -> `sandboxed and codesigned toolchains` --- proposals/0387-cross-compilation-destinations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0387-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md index 5022d7cd72..90d426284d 100644 --- a/proposals/0387-cross-compilation-destinations.md +++ b/proposals/0387-cross-compilation-destinations.md @@ -454,7 +454,7 @@ ready, the generator copies files from the image to a corresponding `.artifactbu ## Security The proposed `--checksum` flag provides basic means of verifying Swift SDK bundle's validity. As a future direction, -we'd like to consider sandboxing and codesigning toolchains included in Swift SDKs running on macOS. +we'd like to consider sandboxed and codesigned toolchains included in Swift SDKs running on macOS. ## Impact on Existing Packages From 604c0d326249cd35d775c2d7163f9be695ef6de0 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:14:13 -0400 Subject: [PATCH 3236/4563] Wordsmith propose solution --- .../0403-swiftpm-mixed-language-targets.md | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index c60d21fec4..568b043e1a 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -48,24 +48,24 @@ experience. ## Proposed solution -This solution enables the package manager to determine if a target contains -mixed language sources and build it as a single module. This happens as an -implementation detail and doesn't require changes to the package manager's -public API. This is cleaner and easier because developers can organize their -packages without exposing language barrier complexity to clients. +Package authors can create a mixed target by mixing language sources in their +target's source directory. When mixing some languages, like C++, authors have +the option of opting in to advanced interoperability features by configuring +the target with an interoperability mode [`SwiftSetting.InteroperabilityMode`]. -At a high level, the package creation process is split into two parts based on +When building a mixed language target, the package manager will build the +public API into a single module for use by clients. + +At a high level, the build process is split into two parts based on the language of the sources. The Swift sources are built by the Swift compiler -and the C Language sources are built by the Clang compiler. Achieving -interoperability between the two halves of the package depends on which -language(s) is/are being mixed with Swift. Package authors will need to -explicitly opt in to language interoperability via the `PackageDescription` -module's [`SwiftSetting.InteroperabilityMode`] API. +and the C/Objective-C/C++ sources are built by the Clang compiler. 1. The Swift compiler is made aware of the Clang part of the package when building the Swift sources into a `swiftmodule`. -1. The Clang part of the package is built with knowledge of any generated - interoperability headers. +1. The Clang part of the package is built with knowledge of the + interoperability Swift header. The contents of this header will vary + depending on if/what language-specific interoperability mode is configured + on the target. The [following example][mixed-package] defines a package containing mixed language sources. @@ -140,7 +140,7 @@ How a mixed target, `MixedPackage`, is imported into an **C/Objective-C/C++** file will vary dependong on the language it is being imported in. When Clang modules are supported, clients can import the module. Textual -imports are also an option. +imports are also an option. **Note that the C/Objective-C/C++ compatible Swift API is only available via textually importing the generated Swift header.** From d91eebd81ad1a16dbc50e6c14f1bc65c6f1e8914 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:18:23 -0400 Subject: [PATCH 3237/4563] Clarify that interop hdr is modularized following thread discussion --- proposals/0403-swiftpm-mixed-language-targets.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 568b043e1a..6fed716a5e 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -65,7 +65,9 @@ and the C/Objective-C/C++ sources are built by the Clang compiler. 1. The Clang part of the package is built with knowledge of the interoperability Swift header. The contents of this header will vary depending on if/what language-specific interoperability mode is configured - on the target. + on the target. The interoperability header is modularized as part of the + mixed target's public interface. + The [following example][mixed-package] defines a package containing mixed language sources. From 9ca73cce97ed60f02e56b547ff1a30be4da22158 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:19:57 -0400 Subject: [PATCH 3238/4563] Make diagram more precise --- proposals/0403-swiftpm-mixed-language-targets.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 6fed716a5e..99cb047695 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -90,11 +90,11 @@ MixedPackage │   └── droid_debug.h ⎦ └── Tests └── MixedPackageTests - ├── JediTests.swift ]-- Swift tests - ├── SithTests.m        ]-- Objective-C tests - ├── ObjcTestConstants.h ⎤-- Mixed language test utils - ├── ObjcTestConstants.m ⎟ - └── TestConstants.swift ⎦ + ├── JediTests.swift ]-- Swift tests + ├── SithTests.m        ]-- Objective-C tests + ├── ObjcTestConstants.h ⎤-- Mixed language test utils + ├── ObjcTestConstants.m ⎟ + └── SwiftTestConstants.swift ⎦ ``` The proposed solution would enable the above package to do the following: From e2594acb56baf9ca613759e21ef986be86798c85 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:24:39 -0400 Subject: [PATCH 3239/4563] Wordsmithing and remove outdated limitations --- proposals/0403-swiftpm-mixed-language-targets.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 99cb047695..62160e2098 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -97,8 +97,8 @@ MixedPackage └── SwiftTestConstants.swift ⎦ ``` -The proposed solution would enable the above package to do the following: -1. Export the public API from across the mixed language sources. +The proposed solution would enable the above targets to do the following: +1. Export their public API, if any, from across the mixed language sources. 1. Use C/Objective-C/C++ compatible Swift API from target’s Swift sources within the target’s C/Objective-C/C++ sources. 1. Use Swift compatible C/Objective-C/C++ API from target’s C/Objective-C/C++ @@ -108,12 +108,10 @@ The proposed solution would enable the above package to do the following: ### Limitations Initial support for targets containing mixed language sources will have the -following requirements: +following limitations: 1. The target must be either a library or test target. Support for other types of targets is deferred until the use cases become clear. -1. The target must be built on a Mac. This is because the Swift - compiler-generated Objective-C compatibility header is only generated on - macOS. + ### Importing a mixed target From 22fd0f90f0a2e60508fc68cc0c214fc75273b788 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:28:01 -0400 Subject: [PATCH 3240/4563] Add future directions per review thread discussion --- proposals/0403-swiftpm-mixed-language-targets.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 62160e2098..0343bba1a5 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -694,6 +694,9 @@ listed in the Future Directions section as an area of future work. ## Future Directions +- Enable package manager plugin tools to process mixed language targets. +- Enable package authors to expose non-public headers to their mixed + target's Swift implemention. - Investigate uses cases for extending mixed language target support to currently unsupported types of targets (e.g. executables). - Investigate uses cases for expanding the level of mixed target support when From dc86dac00a9251dcd27f6ecd600d6b0bf89f3e89 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:28:24 -0400 Subject: [PATCH 3241/4563] Remove outdated limitation --- proposals/0403-swiftpm-mixed-language-targets.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 0343bba1a5..520df4422f 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -699,8 +699,6 @@ listed in the Future Directions section as an area of future work. target's Swift implemention. - Investigate uses cases for extending mixed language target support to currently unsupported types of targets (e.g. executables). -- Investigate uses cases for expanding the level of mixed target support when - building on non-macOS machines. - Extend this solution so that all targets are mixed language targets by default. This refactor would simplify the current implementation of the package manager. From 3f7c7b06835de8c23ee8a282e3266dfb7687ebb5 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:33:05 -0400 Subject: [PATCH 3242/4563] Removing section on testing visibility that no longer has mixed lang specific info --- proposals/0403-swiftpm-mixed-language-targets.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 520df4422f..1e47b928df 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -603,21 +603,6 @@ in `JediTests.swift` (via importing the header). This design should give package authors flexibility in designing test suites for their mixed targets. -#### Testing Visibility - -Documented below are several strategies for testing types defined in mixed -targets. - -- **Expose non-public C-Language types to Objective-C test files**: This is - done by configuring the test target's [C settings][CSetting] to search for - the mixed target's headers via the - [`.headerSearchPaths(_:_:)`][headerSearchPath] setting. - -- **Expose `internal` Swift types to Swift test files**: As expected, Swift - types with `internal` access be tested within a Swift test file by importing - with `@testable`. - -Note: Objective-C test files cannot import non-public Swift types. ### Failure cases From 197a61b12440e1539507cbb5e2ba6f84861b8ec7 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:41:21 -0400 Subject: [PATCH 3243/4563] Update diagram following review discussion --- .../0403-swiftpm-mixed-language-targets.md | 38 +++++-------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 1e47b928df..d0a828d154 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -153,47 +153,29 @@ MixedPackage └── Sources    ├── NewCar.swift   └── include ]-- Public headers directory -    ├── MixedPackage - │ ├── OldCar.h -   │ └── MixedPackage.h ⎤-- These headers are generated - └── MixedPackage-Swift.h ⎦ by the package manager and - virtually overlayed - within the package's public - headers directory. More - details in the Detailed - Design section. * +    ├── OldCar.h + └── MixedPackage-Swift.h ]-- This header is generated + during the build. ``` -\* The generated interop header (`$(TARGET_NAME)-Swift.h`) is always -generated for a mixed target (even when there is no Objective-C compatible -Swift API). On the other hand, the umbrella header located at -`$(PUBLIC_HDRS_DIR)/$(TARGET_NAME)/$(TARGET_NAME).h` is only generated when -the mixed target does not contain an umbrella header at that path. It imports -all Objective-C/C headers and acts as the bridging header used in the generated -interop header. -`MixedPackage`'s public headers directory (`include`) is added a header -search path to client targets. The following example demonstrates all the -possible public headers that can be imported from `MixedPackage`. +Like Clang targets, `MixedPackage`'s public headers directory (`include` in the +above example) is added a header search path to client targets. The following +example demonstrates all the possible public headers that can be imported from +`MixedPackage`. ```objc // MyClientTarget.m -// Import the public non-Swift API if module imports are supported. +// If module imports are supported, the public API (including API in the +// generated Swift header) can be imported via a module import. @import MixedPackage; // Imports types defined in `OldCar.h`. -#import "MixedPackage/OldCar.h" -// Imports a generated umbrella header that includes all public Objective-C/C -// headers within `MixedPackage`. ** -#import "MixedPackage/MixedPackage.h" +#import "OldCar.h" // Imports Objective-C compatible Swift types defined in `MixedPackage`. #import "MixedPackage-Swift.h" ``` -> ** NOTE: If imported, the generated umbrella/bridging header will need to -> appear _before_ importing the generated interop header. This is due to the -> intrinsic relationship between the generated interop header and the -> generated umbrella/bridging header. ## Detailed design From 5ba18cf9e2bfd4cf1817521867d4f6a200ccf1cc Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:43:24 -0400 Subject: [PATCH 3244/4563] Wordsmith header section --- proposals/0403-swiftpm-mixed-language-targets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index d0a828d154..b5e9baca46 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -179,7 +179,7 @@ example demonstrates all the possible public headers that can be imported from ## Detailed design -### Modeling a mixed language target and its build process +### Modeling a mixed language target Up until this proposal, when a package was loading, each target was represented programmatically as either a [`SwiftTarget`] or [`ClangTarget`]. Which of these From 5d015ae1e4a809a5193319330585e34e9d24b028 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:57:12 -0400 Subject: [PATCH 3245/4563] Remove intermediate build artifacts section --- .../0403-swiftpm-mixed-language-targets.md | 153 ------------------ 1 file changed, 153 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index b5e9baca46..bc289eab59 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -223,165 +223,12 @@ flowchart LR G>Mixed sources] --> MixedTarget --> MixedTargetBuildDescription ``` -### Building a mixed language target - -Building a mixed target involves building an underlying Clang target and an -underlying Swift target in a way where each part of the resulting module knows -about the other. To achieve this, the package manager creates two module maps -and two VFS (virtual file system) overlay files where each of the module maps -correspond to a VFS overlay file. The VFS overlay files are used to virtually -alter the filesystem so that all relevant files are in the correct place for -the build. The reason that the filesystem is modified virtually is because -doing otherwise would involve adding and modifying files in the package’s -source itself– which would be unexpected for the package author. - -The four files are considered intermediary build artifacts and are stored in an -`Intermediates` subdirectory within the target’s build folder. The -`Intermediates` subdirectory is something specific to building mixed targets -and is therefore part of this proposal’s design. - -For example, for a target named `MixedTarget`, the `Intermediates` subdirectory -would look like such: -``` -/Users/crusty/Developer/MixedTarget/.build/x86_64-apple-macosx/debug/ -├── ... -└── MixedTarget.build - ├── ... - └── Intermediates - ├── all-product-headers.yaml - ├── unextended-module.modulemap - └── unextended-module-overlay.yaml -``` - -#### all-product-headers.yaml - -This overlay file’s purpose is to facilitate the building of the Clang sources -by positioning the above module map and the generated interop header -accordingly so that the build can be successful. The positioning is determined -based on whether or not the package author has defined a custom module map in -the mixed target: -- If there is a custom module map, then the overlay is created over the - directory that contains the custom module map. The overlay has the following - responsibilities: - 1. Add the generated interop header to the directory. This will enable the - header to be imported within this target via - `#import MixedTarget-Swift.h`. -- If there is no custom module map, then the overlay is created over the - `Intermediates` subdirectory. The overlay has one responsibility: - 1. Add the generated interop header to the directory. This will enabled the - header to be imported within this target via - `#import MixedTarget-Swift.h`. - -The below sample shows what the overlay file would look like for a target -without a custom module map. -```yaml -// all-product-headers.yaml -{ - "version": 0, - "case-sensitive": false, - "roots": - [ - { - // Note #1: If a custom module map exists, the below directory will - // instead be the custom module map's parent directory. - "name": "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/Intermediates", - "type": "directory", - "contents": - [ - { - "name": "MixedTarget-Swift.h", - "type": "file", - "external-contents": "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/MixedTarget-Swift.h", - }, - ], - }, - ], - "use-external-names": false, -} -``` -#### unextended-module.modulemap - -The unextended module map is used when compiling the target’s Swift sources. -The *unextended* part of its name comes from the fact that it does not include -a submodule to expose the generated interop header, as is done in the -intermediary `module.modulemap`. This is intentionally excluded for two -reasons: -- This module map is only used to build the target’s Swift sources; therefore, - the Swift types defined in the generated interop header can instead be - accessed from their native Swift declarations. -- Because the generated interop header is a build artifact from building the - target’s Swift sources (it is emitted alongside the target’s `.swiftmodule`), - including it in the module map will cause an error since the header won’t - exist when the module map is evaluated. - -This module map defines an umbrella directory at the target’s [path], exposing -all headers for the target. When used to build the target’s Swift sources, the -target’s Swift sources can use all types defined in the target’s headers, as -opposed to only types declared in the target’s public headers. - -Additionally, when present, each non-Objective-C/C header (e.g. a C++ header) -is excluded from the module. This avoids build errors that arise when exposing, -for example, a C++ header to Swift. -``` -// unextended-module.modulemap - -module MixedTarget { - umbrella "/Users/crusty/Developer/MixedTarget/Sources" - exclude header "/Users/crusty/Developer/MixedTarget/Sources/Foo.hpp" - export * -} -``` - -#### unextended-module-overlay.yaml - -This overlay file is similar in purpose to the previously discussed -`all-product-headers.yaml` overlay file except that it instead facilitates the -building of the target’s Swift sources. Like with the -`all-product-headers.yaml` overlay file, the positioning of the overlay is -determined based on the presence of a custom module map in the mixed target: -- If there is a custom module map, then the overlay is created over the - directory that contains the custom module map. -- If there is no custom module map, then the overlay is created over the - `Intermediates` subdirectory. -In both cases, the overlay’s sole responsibility is to redirect the path to the -custom module map to the intermediary unextended module map. This redirection -avoids the situation where a module map that exposes the generated interop -header is used to compile the target’s Swift sources. As discussed previously, -this would cause an error because the generated interop header would not yet -exist at the time of the module map’s evaluation. -The below sample shows what the overlay file would look like for a target -without a custom module map. -```yaml -// unextended-module-overlay.yaml -{ - "version": 0, - "case-sensitive": false, - "roots": - [ - { - // Note #1: If a custom module map exists, the below directory will - // instead be the custom module map's parent directory. - "name": "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/Intermediates", - "type": "directory", - "contents": - [ - { - "name": "module.modulemap", - "type": "file", - "external-contents": "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/Intermediates/unextended-module.modulemap", - }, - ], - }, - ], - "use-external-names": false, -} -``` #### Bringing everything together with build flags From 259ae34148899023912d0acb50d2b9b160d25ab8 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 17:47:51 -0400 Subject: [PATCH 3246/4563] Update possible error messages --- proposals/0403-swiftpm-mixed-language-targets.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index bc289eab59..4f35ff930f 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -441,10 +441,6 @@ There are several failure cases that may surface to end users: ``` target at '\(path)' contains mixed language source files; feature not supported ``` -- Attempting to build a mixed target anywhere other than macOS. - ``` - Targets with mixed language sources are only supported on Apple platforms. - ``` - Attempting to build a mixed target that is neither a library target or test target. ``` From ecfc26a92230930d7be0759e9ed25b3d8d371de7 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 18:00:46 -0400 Subject: [PATCH 3247/4563] Refactor detailed design section (1) --- proposals/0403-swiftpm-mixed-language-targets.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 4f35ff930f..82b17c7bed 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -223,6 +223,7 @@ flowchart LR G>Mixed sources] --> MixedTarget --> MixedTargetBuildDescription ``` +### Building a mixed language target @@ -230,12 +231,10 @@ flowchart LR -#### Bringing everything together with build flags - The Swift part of the target is built before the Clang part. This is because -the C language sources may require resolving an import of the generated interop -header, and that header is emitted alongside the Swift module when the Swift -part of the target is built. This relationship is enforced in that the +the C language sources may require resolving a textual import of the generated +interop header, and that header is emitted alongside the Swift module when the +Swift part of the target is built. This relationship is enforced in that the generated interop header is listed as an input to the compilation commands for the target’s C language sources. This is specified in the llbuild manifest (`debug.yaml` in the packag's `.build` directory). From 5603b15777c7a23ae3da99536cf148534157b5c5 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 18:12:24 -0400 Subject: [PATCH 3248/4563] Update build flags --- proposals/0403-swiftpm-mixed-language-targets.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 82b17c7bed..813b1ab1b3 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -241,21 +241,14 @@ the target’s C language sources. This is specified in the llbuild manifest ##### Build flags for the Swift part of the target -The following flags are used when compiling the **Swift** part of the target: 1. `-import-underlying-module` This flag triggers a partial build of the underlying C language sources when building the Swift module. This critical flag enables the Swift sources to use C language types defined in the Clang part of the target. -1. `-I /path/to/overlay_directory` The above `-import-underlying-module` flag - will look for a module map in the given header search path. The overlay - directory chosen when creating the above VFS overlay files is used here. -1. `-ivfsoverlay /path/to/Intermediates/all-product-headers.yaml` This enables - the overlay to take effect during the compilation. Specifically, it will be - used during the partial build of the C language sources that is triggered by - the `-import-underlying-module` flag. -1. `-ivfsoverlay /path/to/Intermediates/unextended-module-overlay.yaml` This - enables the overlay to take effect during the compilation. Specifically, it - will be used when compiling the Swift sources. +1. `-I /path/to/modulemap_dir` The above `-import-underlying-module` flag + will look for a module map in the given header search path. This may be + either the public headers directory or the build directory, depending on + whether a custom module map is provided. 1. `-I $(target’s path)` Adding the target's [path] allows for importing headers using paths relative to the root of the target. Because passing `-import-underlying-module` triggers a partial build of the C language From ab7542d5a527c219e1a4cd89621628d61018c5b8 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 18:15:23 -0400 Subject: [PATCH 3249/4563] Update build flags (2) --- proposals/0403-swiftpm-mixed-language-targets.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 813b1ab1b3..cb49935775 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -239,8 +239,8 @@ generated interop header is listed as an input to the compilation commands for the target’s C language sources. This is specified in the llbuild manifest (`debug.yaml` in the packag's `.build` directory). -##### Build flags for the Swift part of the target - +##### Additional Swift build flags +The following flags are used when compiling the **Swift** part of the target: 1. `-import-underlying-module` This flag triggers a partial build of the underlying C language sources when building the Swift module. This critical flag enables the Swift sources to use C language types defined in the Clang @@ -254,15 +254,12 @@ the target’s C language sources. This is specified in the llbuild manifest `-import-underlying-module` triggers a partial build of the C language sources, this is needed for resolving possible header imports. -##### Build flags for the Clang part of the target +##### Additional Clang build flags The following flags are used when compiling the **Clang** part of the target: 1. `-I $(target’s path)` Adding the target's [path] allows for importing headers using paths relative to the root of the target. -1. `-ivfsoverlay /path/to/Intermediates/all-product-headers.yaml` This enables - the overlay to take effect during the compilation. -1. `-I /path/to/Intermediates/` The above overlay virtually adds the generated - Swift header to the overlay directory. Adding it as a search path enables it - to then be imported with `#import “$(TargetName)-Swift.h”`. +1. `-I /path/to/generated_swift_header/` The generated Swift header may be + needed when compiling the Clang sources. #### Performing the build From 70ffd20f852a9acbf596f36728288b4e96a516dd Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 18:29:25 -0400 Subject: [PATCH 3250/4563] Further refining --- .../0403-swiftpm-mixed-language-targets.md | 36 ++++--------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index cb49935775..b9dcf26f88 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -275,32 +275,11 @@ build nodes for the its `SwiftTargetBuildDescription` and #### Build artifacts for client targets -As explained above, intermediary artifacts support the mixed target’s -build process. For example, the intermediary module maps intentionally expose -all headers so all types defined in the target’s headers can be used in the -Swift sources. While this module map setup was ideal for building the mixed -target, it is not ideal for clients depending on a mixed target because the -client would have access to all headers. Therefore, building a mixed target -will create an additional module map and, conditionally, a corresponding VFS -overlay for use by clients depending on the mixed target. - -The two files are considered product build artifacts and are stored in an -`Product` subdirectory within the target’s build folder. The `Product` -subdirectory is something specific to building mixed targets and is therefore -part of this proposal’s design. - -For example, for a target named `MixedTarget`, the `Product` subdirectory would -look like such: -``` -/Users/crusty/Developer/MixedTarget/.build/x86_64-apple-macosx/debug/ -├── ... -└── MixedTarget.build - ├── ... - └── Product - ├── module.modulemap - └── all-product-headers.yaml -``` + + + + ##### module.modulemap @@ -320,8 +299,7 @@ There are two cases when creating the product module map: > Note: It’s possible that the Clang part of the module exports no public API. > This could be the case for a target whose public API surface is written in > Swift but whose implementation is written in Objective-C. In this case, the -> primary module declaration will not specify any headers or umbrella -> directories. +> primary module declaration will expose no headers. Below is an example of a module map for a target that has an umbrella header in its public headers directory (`include`). @@ -332,12 +310,12 @@ header in its public headers directory (`include`). // This declaration is either copied from the custom module map or generated // via the rules from SE-0038. module MixedTarget { - umbrella header "/Users/crusty/Developer/MixedTarget/Sources/MixedTarget/include/MixedTarget.h" + umbrella header "MixedTarget.h" export * } // This is added on by the package manager. module MixedTarget.Swift { - header "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/MixedTarget-Swift.h" + header "MixedTarget-Swift.h" requires objc } ``` From 40ce7722f3cc368710d589f03a0f54caf973545f Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Fri, 28 Jul 2023 18:30:55 -0400 Subject: [PATCH 3251/4563] Removed unneeded hyperlink --- proposals/0403-swiftpm-mixed-language-targets.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index b9dcf26f88..a6240e7475 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -500,8 +500,4 @@ listed in the Future Directions section as an area of future work. [mixed-target-error]: https://github.com/apple/swift-package-manager/blob/ce099264a187759c2f587393bd209d317a0352b4/Sources/PackageLoading/TargetSourcesBuilder.swift#L183-L189 -[CSetting]: https://developer.apple.com/documentation/packagedescription/target/csettings - -[headerSearchPath]: https://developer.apple.com/documentation/packagedescription/csetting/headersearchpath(_:_:) - [`SwiftSetting.InteroperabilityMode`]: https://developer.apple.com/documentation/packagedescription/swiftsetting/interoperabilitymode \ No newline at end of file From ea1cb6561855ba446acf2ee23f492a85aa91ecae Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Sat, 29 Jul 2023 15:37:18 -0400 Subject: [PATCH 3252/4563] Newline at end --- proposals/0403-swiftpm-mixed-language-targets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index a6240e7475..3c6e92ccc4 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -500,4 +500,4 @@ listed in the Future Directions section as an area of future work. [mixed-target-error]: https://github.com/apple/swift-package-manager/blob/ce099264a187759c2f587393bd209d317a0352b4/Sources/PackageLoading/TargetSourcesBuilder.swift#L183-L189 -[`SwiftSetting.InteroperabilityMode`]: https://developer.apple.com/documentation/packagedescription/swiftsetting/interoperabilitymode \ No newline at end of file +[`SwiftSetting.InteroperabilityMode`]: https://developer.apple.com/documentation/packagedescription/swiftsetting/interoperabilitymode From b6e2f8e5f18829ad789395c1830d4191e06ab627 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Sat, 29 Jul 2023 16:19:44 -0400 Subject: [PATCH 3253/4563] Per review thread, add section on related Swift compiler changes --- .../0403-swiftpm-mixed-language-targets.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 3c6e92ccc4..ddac6ce12c 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -370,6 +370,32 @@ The below sample shows what this overlay file may look like: } ``` +### Related change to the Swift compiler + +When the Swift compiler creates the generated interop header (via +`-emit-objc-header`), any Objective-C symbol referenced in the Swift API that +cannot be forward declared (e.g. superclass, protocol, etc.) will attempt to +be imported via an umbrella header. Since the compiler evaluates +the target as a framework (as opposed to an app), the compiler assumes an +umbrella header exists in a subdirectory (named after the module) within +the public headers directory: + + #import <$(ModuleName)/$(ModuleName).h> + +The compiler assumes that the above path can be resolved relative to the public +header directory. Instead of forcing package authors to structure their +packages around that constraint, the Swift compiler's interop header generation +logic will be ammended to do the following in such cases where the target +does not have the public headers directory structure of an xcframework: + +- If an umbrella header that is modularized by the Clang module exists, the + interop header emit a reference directly to that umbrella header instead. +- Else, the interop header will import all textual includes from the Clang + module map. + +See the related discussion [thread][swift-compiler-thread-fr] from the initial +formal review. + ### Mixed language Test Targets To complement library targets with mixed languages, mixed test targets are @@ -501,3 +527,5 @@ listed in the Future Directions section as an area of future work. [mixed-target-error]: https://github.com/apple/swift-package-manager/blob/ce099264a187759c2f587393bd209d317a0352b4/Sources/PackageLoading/TargetSourcesBuilder.swift#L183-L189 [`SwiftSetting.InteroperabilityMode`]: https://developer.apple.com/documentation/packagedescription/swiftsetting/interoperabilitymode + +[swift-compiler-thread-fr]: https://forums.swift.org/t/se-0403-package-manager-mixed-language-target-support/66202/32 From 9cc5cb9bb58a8425fb62e521aa19075f429bb944 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Sat, 29 Jul 2023 16:47:24 -0400 Subject: [PATCH 3254/4563] Per review thread, add section about add. change to SPM --- proposals/0403-swiftpm-mixed-language-targets.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index ddac6ce12c..df74fda245 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -370,6 +370,18 @@ The below sample shows what this overlay file may look like: } ``` +### Additional changes to the package manager + +It is the goal for mixed language targets to work on all platforms supported +by the package manager. One obstacle to that is that the package manager, +at the time of this proposal, does not invoke the build system with the +flag needed to emit the interoperability header +([code][should-emit-header]). This limitation is outdated and will be +removed as part of this proposal. + +See the related discussion [thread][swift-emit-header-fr] from the initial +formal review. + ### Related change to the Swift compiler When the Swift compiler creates the generated interop header (via @@ -529,3 +541,7 @@ listed in the Future Directions section as an area of future work. [`SwiftSetting.InteroperabilityMode`]: https://developer.apple.com/documentation/packagedescription/swiftsetting/interoperabilitymode [swift-compiler-thread-fr]: https://forums.swift.org/t/se-0403-package-manager-mixed-language-target-support/66202/32 + +[should-emit-header]: https://github.com/apple/swift-package-manager/blob/6478e2724b8bf77856ff358cba5f59a4a62978bf/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift#L732-L735 + +[swift-emit-header-fr]: https://forums.swift.org/t/se-0403-package-manager-mixed-language-target-support/66202/31 From 0cd3ff8ccd8ffc89483b2d53e501cd9de50e50da Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Mon, 31 Jul 2023 10:18:47 -0400 Subject: [PATCH 3255/4563] Add back limitation discussed in pitch --- proposals/0403-swiftpm-mixed-language-targets.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index df74fda245..142ff0c0d8 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -111,7 +111,10 @@ Initial support for targets containing mixed language sources will have the following limitations: 1. The target must be either a library or test target. Support for other types of targets is deferred until the use cases become clear. - +1. If the target contains a custom module map, it cannot contain a submodule of + the form `$(ModuleName).Swift`. This is because the package manager will + synthesize an _extended_ module map that includes a submodule that + modularizes the generated Swift interop header. ### Importing a mixed target @@ -453,6 +456,12 @@ There are several failure cases that may surface to end users: with mixed language sources are only supported for library and test targets. ``` +- Attempting to build a mixed target containing a custom module map + that contains a `$(MixedTargetName).Swift` submodule. + ``` + The target's module map may not contain a Swift submodule for the + module \(target name). + ``` ### Testing From 33646d57dfaf52c746da99cc2bd437de5cf5c7f3 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Mon, 31 Jul 2023 10:44:47 -0400 Subject: [PATCH 3256/4563] Refine swift build flags --- .../0403-swiftpm-mixed-language-targets.md | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 142ff0c0d8..493b34883f 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -243,22 +243,40 @@ the target’s C language sources. This is specified in the llbuild manifest (`debug.yaml` in the packag's `.build` directory). ##### Additional Swift build flags -The following flags are used when compiling the **Swift** part of the target: +The following flags are additionally used when compiling the Swift sub-target: 1. `-import-underlying-module` This flag triggers a partial build of the underlying C language sources when building the Swift module. This critical flag enables the Swift sources to use C language types defined in the Clang part of the target. 1. `-I /path/to/modulemap_dir` The above `-import-underlying-module` flag - will look for a module map in the given header search path. This may be - either the public headers directory or the build directory, depending on - whether a custom module map is provided. -1. `-I $(target’s path)` Adding the target's [path] allows for importing - headers using paths relative to the root of the target. Because passing - `-import-underlying-module` triggers a partial build of the C language + will look for a module map in the given header search path. The module + map used here cannot modularize the generated interop header as will be + created from building the Swift sub-target and therefore does not exist + yet. If a custom module map is provided, the public headers directory + will be used as that is where the custom module map is enforced to be + located. It's also enforced that this module map does not expose an + interop header. If a custom module map is _not_ provided, the package + manager will pass the target's build directory as that is where a module + map will be synthesized. This module map will be _un-extended_, in that + it does not modularize the generated interop header. +1. _If a custom module is NOT provided,_ the package manager will synthesize + two module maps. One is _extended_ in that it modualrizes the generated + interop header. The other is _un-extended_ in that it does not modularize + the generated interop header. A VFS Overlay file is created to swap the + extended one (named `module.modulemap`) for the unextended one + (`unextended-module.modulemap`) for the build. +1. `-Xcc -I -Xcc $(TARGET_SRC_PATH)` Adding the target's [path] allows for + importing headers using paths relative to the root of the target. Because + passing `-import-underlying-module` triggers a partial build of the Clang sources, this is needed for resolving possible header imports. +1. `-Xcc -I -Xcc $(TARGET_PUBLIC_HDRS)` Adding the target's public header's + path allows for importing headers using paths relative to the public + header's directory. Because passing `-import-underlying-module` triggers + a partial build of the Clang sources, this is needed for resolving + possible header imports. ##### Additional Clang build flags -The following flags are used when compiling the **Clang** part of the target: +The following flags are additionally used when compiling the Clang sub-target: 1. `-I $(target’s path)` Adding the target's [path] allows for importing headers using paths relative to the root of the target. 1. `-I /path/to/generated_swift_header/` The generated Swift header may be From 73f4256af1ac2a88581eb6375251a710e4a08b36 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Mon, 31 Jul 2023 10:46:00 -0400 Subject: [PATCH 3257/4563] Refine clang build flags section --- proposals/0403-swiftpm-mixed-language-targets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 493b34883f..06a40f2800 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -279,7 +279,7 @@ The following flags are additionally used when compiling the Swift sub-target: The following flags are additionally used when compiling the Clang sub-target: 1. `-I $(target’s path)` Adding the target's [path] allows for importing headers using paths relative to the root of the target. -1. `-I /path/to/generated_swift_header/` The generated Swift header may be +1. `-I /path/to/generated_swift_header_dir/` The generated Swift header may be needed when compiling the Clang sources. #### Performing the build From 73c13ef4d85d8587cea7769b67ea9329d5416ee5 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Mon, 31 Jul 2023 12:39:18 -0400 Subject: [PATCH 3258/4563] Add clarification in module map generation --- proposals/0403-swiftpm-mixed-language-targets.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 06a40f2800..b075ae4707 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -331,10 +331,10 @@ header in its public headers directory (`include`). // This declaration is either copied from the custom module map or generated // via the rules from SE-0038. module MixedTarget { - umbrella header "MixedTarget.h" + umbrella header "/Users/crusty/Developer/MixedTarget/Sources/MixedTarget/include/MixedTarget.h" export * } -// This is added on by the package manager. +// This is added on by the package manager as part of this proposal. module MixedTarget.Swift { header "MixedTarget-Swift.h" requires objc From 73420fbc702c6a66ffa0ba6718bed214c1e34813 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Mon, 31 Jul 2023 14:10:46 -0400 Subject: [PATCH 3259/4563] Final touch ups to sync with review discussion --- .../0403-swiftpm-mixed-language-targets.md | 68 ++++++------------- 1 file changed, 20 insertions(+), 48 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index b075ae4707..427c3ecb2d 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -302,7 +302,7 @@ build nodes for the its `SwiftTargetBuildDescription` and -##### module.modulemap +##### Module Maps The product module map’s purpose is to define the public API of the mixed language module. It has two parts, a primary module declaration and a secondary @@ -310,12 +310,20 @@ submodule declaration. The former of which exposes the public C language headers and the latter of which exposes the generated interop header. There are two cases when creating the product module map: -- If a custom module map exists in the target, its contents are copied to the - product module map and a submodule is added to expose the generated interop - header. +- If a custom module map exists in the target, its contents are copied and + extended to modularize the generated interop header. These contents are + written to the build directory as `extended-custom-module.modulemap`. + Since the public header directory and build directory are passed as import + paths to the build invocations, a different name is needed for this module + map as the `-import-underlying-module` should only be able to find one + `module.modulemap` file from the given import paths. - Else, the product module map’s contents will be generated via the same generation rules established in [SE-0038] with an added step to generate the - `.Swift` submodule. + `.Swift` submodule. This file is called `module.modulemap` and lives in the + build directory. + +Clients will use an _extended_ module map that includes the modularized interop +header. Building the target will use _unextended_ module map. > Note: It’s possible that the Clang part of the module exports no public API. > This could be the case for a target whose public API surface is written in @@ -326,7 +334,7 @@ Below is an example of a module map for a target that has an umbrella header in its public headers directory (`include`). ``` -// module.modulemap +// extended-custom-module.modulemap // This declaration is either copied from the custom module map or generated // via the rules from SE-0038. @@ -343,54 +351,18 @@ module MixedTarget.Swift { ##### all-product-headers.yaml -The product `all-product-headers.yaml` overlay file is only created when there -exists a custom module map that needs to be swapped out for the product module -map. +An `all-product-headers.yaml` VFS overlay file will adjust the public headers +directory to expose the interop header as a relative path, and, if a custom +module map exists, swap it out for the extended one that modualrizes the interop +header. -In the case that this overlay file exists, it will be passed alongside the -product module map as a compilation argument to clients: +In either case, it will be passed alongside the module map as a compilation +argument to clients: ``` -fmodule-map-file=/Users/crusty/Developer/MixedTarget/Sources/MixedTarget/include/module.modulemap -ivfsoverlay /Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/Product/all-product-headers.yaml ``` -The `-fmodule-map-file` argument tells the client to find the mixed target’s -module map in its public `include` directory. However, the product module map -should be used instead of the custom module map, so passing the overlay via -`-vfsoverlay` will ensure that resolving the module map path redirects to the -product module map in the build directory. The reason this is necessary is -because a custom module map will likely have relative paths within it (e.g. -`umbrella header “MyHeader.h”`) that are preserved when its contents are copied -to the product module map. In order for these relative paths to be resolved, -the product module map needs to appear as if it is in the original directory -that the custom module map resides in. - -The below sample shows what this overlay file may look like: -```yaml -// all-product-headers.yaml - -{ - "version": 0, - "case-sensitive": false, - "roots": - [ - { - "name": "/Users/crusty/Developer/MixedTarget/Sources/MixedTarget/include", - "type": "directory", - "contents": - [ - { - "name": "module.modulemap", - "type": "file", - "external-contents": "/Users/crusty/Developer/MixedTarget/.build/.../MixedTarget.build/Product/module.modulemap", - }, - ], - }, - ], - "use-external-names": false, -} -``` - ### Additional changes to the package manager It is the goal for mixed language targets to work on all platforms supported From 9daca000bd1b09db85d09d588ef573356c203f2b Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Mon, 31 Jul 2023 14:13:11 -0400 Subject: [PATCH 3260/4563] Fix example code block --- proposals/0403-swiftpm-mixed-language-targets.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 427c3ecb2d..f8239862f4 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -145,8 +145,6 @@ file will vary dependong on the language it is being imported in. When Clang modules are supported, clients can import the module. Textual imports are also an option. -**Note that the C/Objective-C/C++ compatible Swift API is only available via -textually importing the generated Swift header.** For this example, consider `MixedPackage` being organized as such: From 264b7e05b71485bc51d766d87a3c684ee13198e4 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Mon, 31 Jul 2023 14:15:05 -0400 Subject: [PATCH 3261/4563] Remove 'product' references --- proposals/0403-swiftpm-mixed-language-targets.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index f8239862f4..23da94326e 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -302,12 +302,12 @@ build nodes for the its `SwiftTargetBuildDescription` and ##### Module Maps -The product module map’s purpose is to define the public API of the mixed +The client-facing module map’s purpose is to define the public API of the mixed language module. It has two parts, a primary module declaration and a secondary submodule declaration. The former of which exposes the public C language headers and the latter of which exposes the generated interop header. -There are two cases when creating the product module map: +There are two cases when creating the client-facing module map: - If a custom module map exists in the target, its contents are copied and extended to modularize the generated interop header. These contents are written to the build directory as `extended-custom-module.modulemap`. @@ -315,7 +315,7 @@ There are two cases when creating the product module map: paths to the build invocations, a different name is needed for this module map as the `-import-underlying-module` should only be able to find one `module.modulemap` file from the given import paths. -- Else, the product module map’s contents will be generated via the same +- Else, the module map’s contents will be generated via the same generation rules established in [SE-0038] with an added step to generate the `.Swift` submodule. This file is called `module.modulemap` and lives in the build directory. From 0e4278a7c7d6e7814973a2fac87f1baf61605914 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 1 Aug 2023 11:46:53 -0700 Subject: [PATCH 3262/4563] update with additional feedback from pitch thread --- .../nnnn-string-validating-initializers.md | 82 ++++--------------- 1 file changed, 17 insertions(+), 65 deletions(-) diff --git a/proposals/nnnn-string-validating-initializers.md b/proposals/nnnn-string-validating-initializers.md index 183cf9d2f8..9f191dea38 100644 --- a/proposals/nnnn-string-validating-initializers.md +++ b/proposals/nnnn-string-validating-initializers.md @@ -31,19 +31,15 @@ extension String { } ``` -For convenience and discoverability, we will also provide initializers that specify the input encoding as part as an argument label: +For convenience and discoverability for the most common case, we will also provide an initializer that specifies the UTF-8 input encoding as part of its argument label: ```swift extension String { public init?(validatingAsUTF8 codeUnits: some Sequence) - - public init?(validatingAsUTF16 codeUnits: some Sequence) - - public init?(validatingAsUTF32 codeUnits: some Sequence) } ``` -These will construct a new `String`, returning `nil` when their input is found invalid according to the encoding specified by the label. +This will construct a new `String`, returning `nil` when the input is found invalid according to the UTF-8 encoding. When handling with data obtained from C, it is frequently the case that UTF-8 data is represented by `CChar` rather than `UInt8`. We will provide a convenience initializer for this use case, noting that it typically involves contiguous memory, and as such is well-served by explicitly using an abstraction for contiguous memory (`UnsafeBufferPointer`): @@ -53,7 +49,7 @@ extension String { } ``` -`String` already features a validating initializer for UTF-8 input. Is is intended for C interoperability, but its argument label does not convey the expectation that its input is a null-terminated C string. We propose to rename it in order to clarify this: +`String` already features a validating initializer for UTF-8 input. It is intended for C interoperability, but its argument label does not convey the expectation that its input is a null-terminated C string. We propose to rename it in order to clarify this: ```swift extension String { @@ -114,66 +110,21 @@ extension String { /// then with an ill-formed code unit sequence. /// /// let validUTF8: [UInt8] = [67, 97, 102, 195, 169] - /// let valid = String.init(validatingAsUTF8: validUTF8) + /// let valid = String(validatingAsUTF8: validUTF8) /// print(valid) /// // Prints "Optional("Café")" /// /// let invalidUTF8: [UInt8] = [67, 195, 0] - /// let invalid = String.init(validatingAsUTF8: invalidUTF8) + /// let invalid = String(validatingAsUTF8: invalidUTF8) /// print(invalid) /// // Prints "nil" /// - /// - Parameters - /// - codeUnits: A sequence of code units that encode a `String` - public init?(validatingAsUTF8 codeUnits: some Sequence) - - /// Create a new `String` by copying and validating the sequence of - /// UTF-16 code units passed in. - /// - /// This initializer does not try to repair ill-formed code unit sequences. - /// If any are found, the result of the initializer is `nil`. - /// - /// The following example calls this initializer with the contents of two - /// different arrays---first with a well-formed UTF-16 code unit sequence and - /// then with an ill-formed code unit sequence. - /// - /// let validUTF16: [UInt16] = [67, 97, 102, 233] - /// let valid = String(validatingAsUTF16: validUTF16) - /// print(valid) - /// // Prints "Optional("Café")" - /// - /// let invalidUTF16: [UInt16] = [0x41, 0x42, 0xd801] - /// let invalid = String(validatingAsUTF16: invalidUTF16) - /// print(invalid) - /// // Prints "nil" + /// Note: This initializer is functionally equivalent to using + /// `String(validating: some Sequence, as: UTF8.self)`. /// /// - Parameters /// - codeUnits: A sequence of code units that encode a `String` - public init?(validatingAsUTF16 codeUnits: some Sequence) - - /// Create a new `String` by copying and validating the sequence of - /// UTF-32 code units passed in. - /// - /// This initializer does not try to repair ill-formed code unit sequences. - /// If any are found, the result of the initializer is `nil`. - /// - /// The following example calls this initializer with the contents of two - /// different arrays---first with correct UTF-32 code units and then with - /// a sequence containing an ill-formed code unit. - /// - /// let validUTF32: [UInt32] = [67, 97, 102, 233] - /// let valid = String(validatingAsUTF32: validUTF32) - /// print(valid) - /// // Prints "Optional("Café")" - /// - /// let invalidUTF32: [UInt32] = [0x41, 0x42, 0xd801] - /// let invalid = String(validatingAsUTF32: invalidUTF32) - /// print(invalid) - /// // Prints "nil" - /// - /// - Parameters - /// - codeUnits: A sequence of code units that encode a `String` - public init?(validatingAsUTF32 codeUnits: some Sequence) + public init?(validatingAsUTF8 codeUnits: some Sequence) } ``` @@ -191,14 +142,14 @@ extension String { /// /// let validUTF8: [CChar] = [67, 97, 102, -61, -87] /// validUTF8.withUnsafeBufferPointer { - /// let s = String.init(validatingAsUTF8: $0) + /// let s = String(validatingAsUTF8: $0) /// print(s) /// } /// // Prints "Optional("Café")" /// /// let invalidUTF8: [CChar] = [67, -61, 0] /// invalidUTF8.withUnsafeBufferPointer { - /// let s = String.init(validatingAsUTF8: $0) + /// let s = String(validatingAsUTF8: $0) /// print(s) /// } /// // Prints "nil" @@ -275,17 +226,18 @@ This would produce a compile-time ambiguity on platforms where `CChar` is typeal When decoding a byte stream, obtaining the details of a validation failure would be useful in order to diagnose issues. We would like to provide this functionality, but the current input validation functionality is not well-suited for it. This is left as a future improvement. -#### Add normalizing initializers +#### Improve input-repairing initialization -It is often desirable to normalize strings, but the standard library does not expose public API for doing so. Hypothetical normalizing initializers should have convenience initializers similar to the validating convenience initializers. +There is only one initializer in the standard library for input-repairing initilization, and it suffers from a discoverability issue. We can add a more discoverable version specifically for the UTF-8 encoding, similarly to one of the additions proposed here. -#### Other +#### Add normalization options -Add initializer from `some Sequence`. +It is often desirable to normalize strings, but the standard library does not expose public API for doing so. We could add initializers that perform normalization, as well as mutating functions that perform normalization. -Add more discoverable convenience initializers for the input-repairing initialization case. +#### Other -Add API devoted to input validation specifically. +- Add a (non-failable) initializer to create a `String` from `some Sequence`. +- Add API devoted to input validation specifically. ## Acknowledgements From 55a3471301133d92a900ec1fcdf3e17498a5bea8 Mon Sep 17 00:00:00 2001 From: jamieQ <2119834+jamieQ@users.noreply.github.com> Date: Tue, 1 Aug 2023 15:38:42 -0500 Subject: [PATCH 3263/4563] [fix]: fix typos in implicit-open-existentials.md (#2122) --- proposals/0352-implicit-open-existentials.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0352-implicit-open-existentials.md b/proposals/0352-implicit-open-existentials.md index c69bbf86fe..3528f07909 100644 --- a/proposals/0352-implicit-open-existentials.md +++ b/proposals/0352-implicit-open-existentials.md @@ -365,9 +365,9 @@ func eraseQAssocWithSE0309(q: any Q) { ### Contravariant erasure for parameters of function type -While covariant erasure applies to the result type of a generic function, the opposite applies to other parameters of the generic function. This affects parameters of function type that reference the generic parameter binding to the opened existential, which will be type-erased to their upper bounds . For example: +While covariant erasure applies to the result type of a generic function, the opposite applies to other parameters of the generic function. This affects parameters of function type that reference the generic parameter binding to the opened existential, which will be type-erased to their upper bounds. For example: -```swi +```swift func acceptValueAndFunction(_ value: T, body: (T) -> Void) { ... } func testContravariantErasure(p: any P) { From 8c5c87559c206e9584291c9dbc3317eb130ff8ec Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 2 Aug 2023 14:50:16 -0700 Subject: [PATCH 3264/4563] clarifications around embedded nulls --- proposals/nnnn-string-validating-initializers.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/proposals/nnnn-string-validating-initializers.md b/proposals/nnnn-string-validating-initializers.md index 9f191dea38..beb735284a 100644 --- a/proposals/nnnn-string-validating-initializers.md +++ b/proposals/nnnn-string-validating-initializers.md @@ -41,7 +41,7 @@ extension String { This will construct a new `String`, returning `nil` when the input is found invalid according to the UTF-8 encoding. -When handling with data obtained from C, it is frequently the case that UTF-8 data is represented by `CChar` rather than `UInt8`. We will provide a convenience initializer for this use case, noting that it typically involves contiguous memory, and as such is well-served by explicitly using an abstraction for contiguous memory (`UnsafeBufferPointer`): +When processing data obtained from C, it is frequently the case that UTF-8 data is represented by `CChar` rather than `UInt8`. We will provide a convenience initializer for this use case. Noting that this situation typically involves contiguous memory, we believe it will be well-served by explicitly using an abstraction for contiguous memory (`UnsafeBufferPointer`): ```swift extension String { @@ -49,7 +49,9 @@ extension String { } ``` -`String` already features a validating initializer for UTF-8 input. It is intended for C interoperability, but its argument label does not convey the expectation that its input is a null-terminated C string. We propose to rename it in order to clarify this: +The `String.init(validatingAsUTF8:)` functions convert their whole input, including any embedded `\0` code units. + +`String` already features a validating initializer for UTF-8 input, though it is intended for C interoperability. Its argument label does not convey the expectation that its input is a null-terminated C string, and this has caused errors. We propose to change the labels in order to clarify the preconditions: ```swift extension String { @@ -136,11 +138,11 @@ extension String { /// This initializer does not try to repair ill-formed code unit sequences. /// If any are found, the result of the initializer is `nil`. /// - /// The following example calls this initializer with pointers to the - /// contents of two different `CChar` arrays---first with a well-formed UTF-8 + /// The following example calls this initializer with the contents of two + /// different `CChar` arrays---first with a well-formed UTF-8 /// code unit sequence and then with an ill-formed code unit sequence. /// - /// let validUTF8: [CChar] = [67, 97, 102, -61, -87] + /// let validUTF8: [CChar] = [67, 97, 0, 102, -61, -87] /// validUTF8.withUnsafeBufferPointer { /// let s = String(validatingAsUTF8: $0) /// print(s) From 5932371f084a04da293c4709ec303cf0f88bc5f5 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sat, 5 Aug 2023 00:55:27 +0100 Subject: [PATCH 3265/4563] Update SE-0387 with review feedback (#2121) Based on the review announcement, in this proposal "build-time" and "run-time" triples are renamed to "host" and "target" respectively. --- .../0387-cross-compilation-destinations.md | 80 ++++++++++--------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/proposals/0387-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md index 90d426284d..01f8374d22 100644 --- a/proposals/0387-cross-compilation-destinations.md +++ b/proposals/0387-cross-compilation-destinations.md @@ -46,28 +46,27 @@ Cross-compilation is a common development use case. When cross-compiling, we need to refer to these concepts: -- **toolchain** is a set of tools used to build an application or a library; -- **triple** describes features of a given machine such as CPU architecture, vendor, OS etc, corresponding to LLVM's +- a **toolchain** is a set of tools used to build an application or a library; +- a **triple** describes features of a given machine such as CPU architecture, vendor, OS etc, corresponding to LLVM's triple; -- **build-time triple** describes a machine where application or library code is built; -- **run-time triple** describes a machine where application or library code is running; -- **SDK** is a set of dynamic and/or static libraries, headers, and other resources required to generate code for the - run-time triple. +- a **host triple** describes a machine where application or library code is built; +- a **target triple** describes a machine where application or library code is running; +- an **SDK** is a set of dynamic and/or static libraries, headers, and other resources required to generate code for the + target triple. -Let’s call a Swift toolchain and an SDK bundled together a **Swift SDK**. +When a triple of a machine on which the toolchain is built is different from the host triple, we'll call it a **build triple**. +The cross-compilation configuration itself that involves three different triples is called +[the Canadian Cross](https://en.wikipedia.org/wiki/Cross_compiler#Canadian_Cross). -Authors of the proposal are aware of the established "build/host/target platform" naming convention, but feel that -"target" already has a different meaning within the build systems nomenclature. In addition, "platform" -itself is quite loosely defined. For the avoidance of possible confusion, we're using "build-time triple" and "run-time -triple" terms in this proposal. +Let’s call a Swift toolchain and an SDK bundled together a **Swift SDK**. ## Motivation In Swift 5.8 and earlier versions users can cross-compile their code with so called "destination files" passed to SwiftPM invocations. These destination files are produced on an ad-hoc basis for different combinations of -build-time and run-time triples. For example, scripts that produce macOS → Linux destinations were created by both +host and target triples. For example, scripts that produce macOS → Linux destinations were created by both [the Swift -team](https://github.com/apple/swift-package-manager/blob/swift-5.7-RELEASE/Utilities/build_ubuntu_cross_compilation_toolchain) +team](https://github.com/apple/swift-package-manager/blob/swift-5.8-RELEASE/Utilities/build_ubuntu_cross_compilation_toolchain) and [the Swift community](https://github.com/SPMDestinations/homebrew-tap). At the same time, the distribution process of assets required for cross-compiling is cumbersome. After building a destination tree on the file system, required metadata files rely on hardcoded absolute paths. Adding support for relative paths in destination's metadata and @@ -82,7 +81,7 @@ In other cases, building in a Docker container is not always the best solution f example, when working with Swift AWS Lambda Runtime, some developers may find that installing Docker just for building a project is a daunting step that shouldn’t be required. -The solution described below is general enough to scale for any build-time/run-time triple combination. +The solution described below is general enough to scale for any host/target triple combination. ## Proposed Solution @@ -161,7 +160,7 @@ in `aarch64-unknown-linux-gnu` and `x86_64-unknown-linux-gnu` subdirectories. `info.json` bundle manifests at the root of artifact bundles should specify `"type": "swiftSDK"` for corresponding artifacts. Artifact identifiers in this manifest file uniquely identify a Swift SDK, and -`supportedTriples` property in `info.json` should contain build-time triples that a given Swift SDK supports. The rest +`supportedTriples` property in `info.json` should contain host triples that a given Swift SDK supports. The rest of the properties of bundle manifests introduced in SE-0305 are preserved. Here's how `info.json` file could look like for `swift-5.9_ubuntu.artifactbundle` introduced in the example @@ -317,14 +316,14 @@ provided within the bundle. Note the presence of `swift-sdk.json` files in each `` subdirectory. These files should contain a JSON dictionary with an evolved version of the schema of [existing `destination.json` files that SwiftPM already supports](https://github.com/apple/swift-package-manager/pull/1098) and `destination.json` files presented in the pitch -version of this proposal, hence `"schemaVersion": "3.0"`. We'll keep parsing `"version": 1` and `"version": 2` for -backward compatibility, but for consistency with `info.json` this field is renamed to `"schemaVersion"`. Here's an -informally defined schema for these files: +version of this proposal, hence `"schemaVersion": "4.0"`. We'll keep parsing `"version": 1`, `"version": 2`, +and `"version": "3.0"` for backward compatibility, but for consistency with `info.json` this field is renamed to +`"schemaVersion"`. Here's an informally defined schema for these files: ```json5 { - "schemaVersion": "3.0", - "runTimeTriples": [ + "schemaVersion": "4.0", + "targetTriples": [ "": { "sdkRootPath": "", // all of the properties listed below are optional: @@ -334,7 +333,7 @@ informally defined schema for these files: "librarySearchPaths": [""], "toolsetPaths": [""] }, - // a Swift SDK can support more than one run-time triple: + // a Swift SDK can support more than one target triple: "": { "sdkRootPath": "", // all of the properties listed below are optional: @@ -364,8 +363,8 @@ Here's `swift-sdk.json` file for the `ubuntu_jammy` artifact previously introduc ```json5 { - "schemaVersion": "3.0", - "runTimeTriples": [ + "schemaVersion": "4.0", + "targetTriples": [ "aarch64-unknown-linux-gnu": { "sdkRootPath": "aarch64-unknown-linux-gnu/ubuntu-jammy.sdk", "toolsetPaths": ["aarch64-unknown-linux-gnu/toolset.json"] @@ -398,7 +397,7 @@ To manage Swift SDKs, we'd like to introduce a new `swift sdk` command with thre `install` subcommand with `--update` flag to allow updating an already installed Swift SDK artifact to a new version. - `swift sdk list`, which prints a list of already installed Swift SDKs with their identifiers. -- `swift sdk configure `, which allows users to provide additional search paths and toolsets to be +- `swift sdk configure `, which allows users to provide additional search paths and toolsets to be used subsequently when building with a given Swift SDK. Specifically, multiple `--swift-resources-path`, `--include-search-path`, `--library-search-path`, and `--toolset` options with corresponding paths can be provided, which then will be stored as configuration for this Swift SDK. @@ -414,7 +413,7 @@ After a Swift SDK is installed, users can refer to it via its identifier passed swift build --swift-sdk ubuntu_focal ``` -We'd also like to make `--swift-sdk` option flexible enough to recognize run-time triples when there's only a single +We'd also like to make `--swift-sdk` option flexible enough to recognize target triples when there's only a single Swift SDK installed for such triple: ``` @@ -426,7 +425,7 @@ asking the user to select a single one via its identifier instead. ### Swift SDK Bundle Generation -Swift SDKs can be generated quite differently, depending on build-time and run-time triple combinations and user's +Swift SDKs can be generated quite differently, depending on host and target triple combinations and user's needs. We intentionally don't specify in this proposal how exactly Swift SDK bundles should be generated. Authors of this document intend to publish source code for a macOS → Linux Swift SDK generator, which community is @@ -435,7 +434,7 @@ up the build environment locally before copying it to a Swift SDK tree. Relying easier to reuse and customize existing build environments. Important to clarify, that Docker is only used for bundle generation, and users of Swift SDK bundles do not need to have Docker installed on their machine to use these bundles. -As an example, Swift SDK publishers looking to add a library to an Ubuntu 22.04 run-time environment would modify a +As an example, Swift SDK publishers looking to add a library to an Ubuntu 22.04 target environment would modify a `Dockerfile` similar to this one in their Swift SDK generator source code: ```dockerfile @@ -464,7 +463,7 @@ This is an additive change with no impact on existing packages. ### Rust -In the Rust ecosystem, its toolchain and standard library built for a run-time triple are managed by [the `rustup` +In the Rust ecosystem, its toolchain and standard library built for a target triple are managed by [the `rustup` tool](https://github.com/rust-lang/rustup). For example, artifacts required for cross-compilation to `aarch64-linux-unknown-gnu` are installed with [`rustup target add aarch64-linux-unknown-gnu`](https://rust-lang.github.io/rustup/cross-compilation.html). Then @@ -476,7 +475,7 @@ C/C++ interop on the same level as Swift. It means that Rust packages much more system-provided packages to be available in the same way that SwiftPM allows with `systemLibrary`. Currently, Rust doesn’t supply all of the required tools when running `rustup target add`. It’s left to a user to -specify paths to a linker that’s suitable for their build-time/run-time triple combination manually in a config file. We +specify paths to a linker that’s suitable for their host/target triple combination manually in a config file. We feel that this should be unnecessary, which is why Swift SDK bundles proposed for Swift can provide their own tools via toolset configuration files. @@ -512,7 +511,7 @@ we’re looking for a solution that can be generalized for all possible platform ### Alternative Bundle Formats -One alternative is to allow only a single build-time/run-time combination per bundle, but this may complicate +One alternative is to allow only a single host/target combination per bundle, but this may complicate distribution of Swift SDK bundles in some scenarios. The existing `.artifactbundle` format is flexible enough to support bundles with a single or multiple combinations. @@ -520,7 +519,16 @@ Different formats of Swift SDK bundles can be considered, but we don't think tho from the proposed one. If they were different, this would complicate bundle distribution scenarios for users who want to publish their own artifact bundles with executables, as defined in SE-0305. -## Making Swift SDK Bundles Fully Self-Contained +### Triples nomenclature + +Authors of the proposal considered alternative nomenclature to the established "build/host/target platform" naming convention, +but felt that preserving consistency with other ecosystems is more important. + +While "target" already has a different meaning within the build systems nomenclature, users are most likely to stumble upon +targets when working with SwiftPM package manifests. To avoid this ambiguity, as a future direction SwiftPM can consider renaming +`target` declarations used in `Package.swift` to a different unambiguous term. + +### Making Swift SDK Bundles Fully Self-Contained Some users expressed interest in self-contained Swift SDK bundles that ignore the value of `PATH` environment variable and prevent launching any executables from outside of a bundle. So far in our practice we haven't seen any problems @@ -539,7 +547,7 @@ predictable way and is consistent with established CLI conventions. Platform triples are not specific enough in certain cases. For example, `aarch64-unknown-linux` host triple can’t prevent a user from installing a Swift SDK bundle on an unsupported Linux distribution. In the future we could -deprecate `supportedTriples` and `runTimeTriples` JSON properties in favor of dictionaries with keys and values that +deprecate `supportedTriples` and `targetTriples` JSON properties in favor of dictionaries with keys and values that describe aspects of platforms that are important for Swift SDKs. Such dictionaries could look like this: ```json5 @@ -559,7 +567,7 @@ compilation and potentially even runtime checks. After an application is built with a Swift SDK, there are other development workflow steps to be improved. We could introduce new types of plugins invoked by `swift run` and `swift test` for purposes of remote running, debugging, and -testing. For Linux run-time triples, these plugins could delegate to Docker for running produced executables. +testing. For Linux target triples, these plugins could delegate to Docker for running produced executables. ### `swift sdk select` Subcommand @@ -571,14 +579,14 @@ fully work. ### SwiftPM and SourceKit-LSP Improvements -It is a known issue that SwiftPM can’t run multiple concurrent builds for different run-time triples. This may cause +It is a known issue that SwiftPM can’t run multiple concurrent builds for different target triples. This may cause issues when SourceKit-LSP is building a project for indexing purposes (for a host platform by default), while a user may -be trying to build for a run-time for example for testing. One of these build processes will fail due to the +be trying to build for a target for testing, for example. One of these build processes will fail due to the process locking the build database. A potential solution would be to maintain separate build databases per platform. Another issue related to SourceKit-LSP is that [it always build and indexes source code for the host platform](https://github.com/apple/sourcekit-lsp/issues/601). Ideally, we want it to maintain indices for multiple -platforms at the same time. Users should be able to select run-time triples and corresponding indices to enable +platforms at the same time. Users should be able to select target triples and corresponding indices to enable semantic syntax highlighting, auto-complete, and other features for areas of code that are conditionally compiled with `#if` directives. From 70fc6ccdc4dbea5ef93f7fefc946ce18c2df19e9 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 8 Aug 2023 08:55:06 -0400 Subject: [PATCH 3266/4563] Assign String Initializers with Encoding Validation as SE-0405. --- ...ializers.md => 0405-string-validating-initializers.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{nnnn-string-validating-initializers.md => 0405-string-validating-initializers.md} (97%) diff --git a/proposals/nnnn-string-validating-initializers.md b/proposals/0405-string-validating-initializers.md similarity index 97% rename from proposals/nnnn-string-validating-initializers.md rename to proposals/0405-string-validating-initializers.md index beb735284a..bbe5b7fafa 100644 --- a/proposals/nnnn-string-validating-initializers.md +++ b/proposals/0405-string-validating-initializers.md @@ -1,9 +1,9 @@ -# String initializers with encoding validation +# String Initializers with Encoding Validation -* Proposal: [SE-NNNN String initializers with encoding validation](https://gist.github.com/glessard/d1ed79b7968b4ad2115462b3d1eba805) +* Proposal: [SE-0405](https://github.com/apple/swift-evolution/blob/main/proposals/0405-string-validating-initializers.md) * Author: [Guillaume Lessard](https://github.com/glessard) -* Review Manager: TBD -* Status: Pitch +* Review Manager: [Tony Allevato](https://github.com/allevato) +* Status: **Active Review (August 8–22, 2023)** * Bugs: rdar://99276048, rdar://99832858 * Implementation: [Staged package](https://github.com/apple/swift-evolution-staging/tree/input-validating-string-initializers) * Review: ([pitch](https://forums.swift.org/t/66206)) From 6328db33c1d29f3bd484b76f308841fdf207875a Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 8 Aug 2023 09:25:37 -0400 Subject: [PATCH 3267/4563] Add link to SE-0405 review thread. --- proposals/0405-string-validating-initializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0405-string-validating-initializers.md b/proposals/0405-string-validating-initializers.md index bbe5b7fafa..3962f1a4c4 100644 --- a/proposals/0405-string-validating-initializers.md +++ b/proposals/0405-string-validating-initializers.md @@ -6,7 +6,7 @@ * Status: **Active Review (August 8–22, 2023)** * Bugs: rdar://99276048, rdar://99832858 * Implementation: [Staged package](https://github.com/apple/swift-evolution-staging/tree/input-validating-string-initializers) -* Review: ([pitch](https://forums.swift.org/t/66206)) +* Review: ([pitch](https://forums.swift.org/t/66206)), ([review](https://forums.swift.org/t/se-0405-string-initializers-with-encoding-validation/66655)) * Previous Revision: ([0](https://gist.github.com/glessard/d1ed79b7968b4ad2115462b3d1eba805)) ## Introduction From b982cd7a0324c858e459634db5b5f20a416ce414 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 8 Aug 2023 21:59:53 -0700 Subject: [PATCH 3268/4563] Accept SE-0404. (#2125) --- proposals/0404-nested-protocols.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0404-nested-protocols.md b/proposals/0404-nested-protocols.md index 1c31716859..ed01cb41d6 100644 --- a/proposals/0404-nested-protocols.md +++ b/proposals/0404-nested-protocols.md @@ -2,10 +2,10 @@ * Proposal: [SE-0404](0404-nested-protocols.md) * Authors: [Karl Wagner](https://github.com/karwa) -* Review Manager: [Holly Borla](https://github.com/hbborla) -* Status: **Active review (July 24 - August 7, 2023)** +* Review Manager: [Holly Borla](https://github.com/hborla) +* Status: **Accepted** * Implementation: [apple/swift#66247](https://github.com/apple/swift/pull/66247) (gated behind flag `-enable-experimental-feature NestedProtocols`) -* Review: ([pitch](https://forums.swift.org/t/pitch-allow-protocols-to-be-nested-in-non-generic-contexts/65285)), ([review](https://forums.swift.org/t/se-0404-allow-protocols-to-be-nested-in-non-generic-contexts/66332)) +* Review: ([pitch](https://forums.swift.org/t/pitch-allow-protocols-to-be-nested-in-non-generic-contexts/65285)), ([review](https://forums.swift.org/t/se-0404-allow-protocols-to-be-nested-in-non-generic-contexts/66332)), ([acceptance](https://forums.swift.org/t/accepted-se-0404-allow-protocols-to-be-nested-in-non-generic-contexts/66668)) ## Introduction From 65ff1b2220d7852384e1a2425f5a07ccd1ccd0f8 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 14 Aug 2023 16:56:52 -0700 Subject: [PATCH 3269/4563] change whitespace to only use spaces --- proposals/0405-string-validating-initializers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0405-string-validating-initializers.md b/proposals/0405-string-validating-initializers.md index 3962f1a4c4..13d2caad4e 100644 --- a/proposals/0405-string-validating-initializers.md +++ b/proposals/0405-string-validating-initializers.md @@ -26,7 +26,7 @@ We will add a new `String` initializer that can fail, returning `nil`, when its ```swift extension String { public init?( - validating codeUnits: some Sequence, as: Encoding.Type + validating codeUnits: some Sequence, as: Encoding.Type ) } ``` @@ -49,7 +49,7 @@ extension String { } ``` -The `String.init(validatingAsUTF8:)` functions convert their whole input, including any embedded `\0` code units. +The `String.init(validatingAsUTF8:)` functions convert their whole input, including any embedded `\0` code units. `String` already features a validating initializer for UTF-8 input, though it is intended for C interoperability. Its argument label does not convey the expectation that its input is a null-terminated C string, and this has caused errors. We propose to change the labels in order to clarify the preconditions: From 9e10aab94bcc5c8e8338c83b993782fab65b1e12 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 14 Aug 2023 16:57:18 -0700 Subject: [PATCH 3270/4563] improve Source- and ABI-compatibility sections --- proposals/0405-string-validating-initializers.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/proposals/0405-string-validating-initializers.md b/proposals/0405-string-validating-initializers.md index 13d2caad4e..2747f38fb0 100644 --- a/proposals/0405-string-validating-initializers.md +++ b/proposals/0405-string-validating-initializers.md @@ -202,12 +202,16 @@ extension String { ## Source Compatibility -This proposal is strictly additive. +This proposal consists mostly of additions, which are by definition source compatible. + +The proposal includes the renaming of one function from `String.init?(validatingUTF8:)` to `String.init?(validatingCString:)`. The existing function name will be deprecated, producing a warning. A fixit will support an easy transition to the renamed version of the function. ## ABI Compatibility This proposal adds new functions to the ABI. +The renamed function reuses the existing ABI entry point, making the change ABI-compatible. + ## Implications on adoption This feature requires a new version of the standard library. From da43bb5532e970d79f1b3f69f94a9cc403c0b468 Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Tue, 15 Aug 2023 21:21:03 +0100 Subject: [PATCH 3271/4563] SE-0406: New APIs for Async[Throwing]Stream with backpressure support (#2077) * New APIs for Async[Throwing]Stream with backpressure support * Add termination examples, comparison to other sequences and acknowledgements * Fix termination example * Adapt to latest changes * Update NNNN-async-stream-backpressure.md to add review manager * Adapt to latest changes * Prepare SE-0406 for review --------- Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- proposals/0406-async-stream-backpressure.md | 806 ++++++++++++++++++++ 1 file changed, 806 insertions(+) create mode 100644 proposals/0406-async-stream-backpressure.md diff --git a/proposals/0406-async-stream-backpressure.md b/proposals/0406-async-stream-backpressure.md new file mode 100644 index 0000000000..36dd86d758 --- /dev/null +++ b/proposals/0406-async-stream-backpressure.md @@ -0,0 +1,806 @@ +# Backpressure support for AsyncStream + +* Proposal: [SE-0406](0406-async-stream-backpressure.md) +* Author: [Franz Busch](https://github.com/FranzBusch) +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Active review (August 15...28, 2023)** +* Implementation: [apple/swift#66488](https://github.com/apple/swift/pull/66488) +* Review: ([pitch](https://forums.swift.org/t/pitch-new-apis-for-async-throwing-stream-with-backpressure-support/65449)) + +## Introduction + +[SE-0314](https://github.com/apple/swift-evolution/blob/main/proposals/0314-async-stream.md) +introduced new `Async[Throwing]Stream` types which act as root asynchronous +sequences. These two types allow bridging from synchronous callbacks such as +delegates to an asynchronous sequence. This proposal adds a new way of +constructing asynchronous streams with the goal to bridge backpressured systems +into an asynchronous sequence. Furthermore, this proposal aims to clarify the +cancellation behaviour both when the consuming task is cancelled and when +the production side indicates termination. + +## Motivation + +After using the `AsyncSequence` protocol and the `Async[Throwing]Stream` types +extensively over the past years, we learned that there are a few important +behavioral details that any `AsyncSequence` implementation needs to support. +These behaviors are: + +1. Backpressure +2. Multi/single consumer support +3. Downstream consumer termination +4. Upstream producer termination + +In general, `AsyncSequence` implementations can be divided into two kinds: Root +asynchronous sequences that are the source of values such as +`Async[Throwing]Stream` and transformational asynchronous sequences such as +`AsyncMapSequence`. Most transformational asynchronous sequences implicitly +fulfill the above behaviors since they forward any demand to a base asynchronous +sequence that should implement the behaviors. On the other hand, root +asynchronous sequences need to make sure that all of the above behaviors are +correctly implemented. Let's look at the current behavior of +`Async[Throwing]Stream` to see if and how it achieves these behaviors. + +### Backpressure + +Root asynchronous sequences need to relay the backpressure to the producing +system. `Async[Throwing]Stream` aims to support backpressure by providing a +configurable buffer and returning +`Async[Throwing]Stream.Continuation.YieldResult` which contains the current +buffer depth from the `yield()` method. However, only providing the current +buffer depth on `yield()` is not enough to bridge a backpressured system into +an asynchronous sequence since this can only be used as a "stop" signal but we +are missing a signal to indicate resuming the production. The only viable +backpressure strategy that can be implemented with the current API is a timed +backoff where we stop producing for some period of time and then speculatively +produce again. This is a very inefficient pattern that produces high latencies +and inefficient use of resources. + +### Multi/single consumer support + +The `AsyncSequence` protocol itself makes no assumptions about whether the +implementation supports multiple consumers or not. This allows the creation of +unicast and multicast asynchronous sequences. The difference between a unicast +and multicast asynchronous sequence is if they allow multiple iterators to be +created. `AsyncStream` does support the creation of multiple iterators and it +does handle multiple consumers correctly. On the other hand, +`AsyncThrowingStream` also supports multiple iterators but does `fatalError` +when more than one iterator has to suspend. The original proposal states: + +> As with any sequence, iterating over an AsyncStream multiple times, or +creating multiple iterators and iterating over them separately, may produce an +unexpected series of values. + +While that statement leaves room for any behavior we learned that a clear distinction +of behavior for root asynchronous sequences is benficial; especially, when it comes to +how transformation algorithms are applied on top. + +### Downstream consumer termination + +Downstream consumer termination allows the producer to notify the consumer that +no more values are going to be produced. `Async[Throwing]Stream` does support +this by calling the `finish()` or `finish(throwing:)` methods of the +`Async[Throwing]Stream.Continuation`. However, `Async[Throwing]Stream` does not +handle the case that the `Continuation` may be `deinit`ed before one of the +finish methods is called. This currently leads to async streams that never +terminate. The behavior could be changed but it could result in semantically +breaking code. + +### Upstream producer termination + +Upstream producer termination is the inverse of downstream consumer termination +where the producer is notified once the consumption has terminated. Currently, +`Async[Throwing]Stream` does expose the `onTermination` property on the +`Continuation`. The `onTermination` closure is invoked once the consumer has +terminated. The consumer can terminate in four separate cases: + +1. The asynchronous sequence was `deinit`ed and no iterator was created +2. The iterator was `deinit`ed and the asynchronous sequence is unicast +3. The consuming task is canceled +4. The asynchronous sequence returned `nil` or threw + +`Async[Throwing]Stream` currently invokes `onTermination` in all cases; however, +since `Async[Throwing]Stream` supports multiple consumers (as discussed in the +`Multi/single consumer support` section), a single consumer task being canceled +leads to the termination of all consumers. This is not expected from multicast +asynchronous sequences in general. + +## Proposed solution + +The above motivation lays out the expected behaviors from a root asynchronous +sequence and compares them to the behaviors of `Async[Throwing]Stream`. These +are the behaviors where `Async[Throwing]Stream` diverges from the expectations. + +- Backpressure: Doesn't expose a "resumption" signal to the producer +- Multi/single consumer: + - Divergent implementation between throwing and non-throwing variant + - Supports multiple consumers even though proposal positions it as a unicast + asynchronous sequence +- Consumer termination: Doesn't handle the `Continuation` being `deinit`ed +- Producer termination: Happens on first consumer termination + +This section proposes new APIs for `Async[Throwing]Stream` that implement all of +the above-mentioned behaviors. + +### Creating an AsyncStream with backpressure support + +You can create an `Async[Throwing]Stream` instance using the new `makeStream(of: +backpressureStrategy:)` method. This method returns you the stream and the +source. The source can be used to write new values to the asynchronous stream. +The new API specifically provides a multi-producer/single-consumer pattern. + +```swift +let (stream, source) = AsyncStream.makeStream( + of: Int.self, + backpressureStrategy: .watermark(low: 2, high: 4) +) +``` + +The new proposed APIs offer three different ways to bridge a backpressured +system. The foundation is the multi-step synchronous interface. Below is an +example of how it can be used: + +```swift +do { + let writeResult = try source.write(contentsOf: sequence) + + switch writeResult { + case .produceMore: + // Trigger more production + + case .enqueueCallback(let callbackToken): + source.enqueueCallback(token: callbackToken, onProduceMore: { result in + switch result { + case .success: + // Trigger more production + case .failure(let error): + // Terminate the underlying producer + } + }) + } +} catch { + // `write(contentsOf:)` throws if the asynchronous stream already terminated +} +``` + +The above API offers the most control and highest performance when bridging a +synchronous producer to an asynchronous sequence. First, you have to write +values using the `write(contentsOf:)` which returns a `WriteResult`. The result +either indicates that more values should be produced or that a callback should +be enqueued by calling the `enqueueCallback(callbackToken: onProduceMore:)` +method. This callback is invoked once the backpressure strategy decided that +more values should be produced. This API aims to offer the most flexibility with +the greatest performance. The callback only has to be allocated in the case +where the producer needs to be suspended. + +Additionally, the above API is the building block for some higher-level and +easier-to-use APIs to write values to the asynchronous stream. Below is an +example of the two higher-level APIs. + +```swift +// Writing new values and providing a callback when to produce more +try source.write(contentsOf: sequence, onProduceMore: { result in + switch result { + case .success: + // Trigger more production + case .failure(let error): + // Terminate the underlying producer + } +}) + +// This method suspends until more values should be produced +try await source.write(contentsOf: sequence) +``` + +With the above APIs, we should be able to effectively bridge any system into an +asynchronous stream regardless if the system is callback-based, blocking or +asynchronous. + +### Downstream consumer termination + +> When reading the next two examples around termination behaviour keep in mind +that the newly proposed APIs are providing a strict unicast asynchronous sequence. + +Calling `finish()` terminates the downstream consumer. Below is an example of +this: + +```swift +// Termination through calling finish +let (stream, source) = AsyncStream.makeStream( + of: Int.self, + backpressureStrategy: .watermark(low: 2, high: 4) +) + +_ = try await source.write(1) +source.finish() + +for await element in stream { + print(element) +} +print("Finished") + +// Prints +// 1 +// Finished +``` + +The other way to terminate the consumer is by deiniting the source. This has the +same effect as calling `finish()` and makes sure that no consumer is stuck +indefinitely. + +```swift +// Termination through deiniting the source +let (stream, _) = AsyncStream.makeStream( + of: Int.self, + backpressureStrategy: .watermark(low: 2, high: 4) +) + +for await element in stream { + print(element) +} +print("Finished") + +// Prints +// Finished +``` + +Trying to write more elements after the source has been finish will result in an +error thrown from the write methods. + +### Upstream producer termination + +The producer will get notified about termination through the `onTerminate` +callback. Termination of the producer happens in the following scenarios: + +```swift +// Termination through task cancellation +let (stream, source) = AsyncStream.makeStream( + of: Int.self, + backpressureStrategy: .watermark(low: 2, high: 4) +) + +let task = Task { + for await element in stream { + + } +} +task.cancel() +``` + +```swift +// Termination through deiniting the sequence +let (_, source) = AsyncStream.makeStream( + of: Int.self, + backpressureStrategy: .watermark(low: 2, high: 4) +) +``` + +```swift +// Termination through deiniting the iterator +let (stream, source) = AsyncStream.makeStream( + of: Int.self, + backpressureStrategy: .watermark(low: 2, high: 4) +) +_ = stream.makeAsyncIterator() +``` + +```swift +// Termination through calling finish +let (stream, source) = AsyncStream.makeStream( + of: Int.self, + backpressureStrategy: .watermark(low: 2, high: 4) +) + +_ = try source.write(1) +source.finish() + +for await element in stream {} + +// onTerminate will be called after all elements have been consumed +``` + +Similar to the downstream consumer termination, trying to write more elements after the +producer has been terminated will result in an error thrown from the write methods. + +## Detailed design + +All new APIs on `AsyncStream` and `AsyncThrowingStream` are as follows: + +```swift +/// Error that is thrown from the various `write` methods of the +/// ``AsyncStream.Source`` and ``AsyncThrowingStream.Source``. +/// +/// This error is thrown when the asynchronous stream is already finished when +/// trying to write new elements. +public struct AsyncStreamAlreadyFinishedError: Error {} + +extension AsyncStream { + /// A mechanism to interface between producer code and an asynchronous stream. + /// + /// Use this source to provide elements to the stream by calling one of the `write` methods, then terminate the stream normally + /// by calling the `finish()` method. + public struct Source: Sendable { + /// A strategy that handles the backpressure of the asynchronous stream. + public struct BackpressureStrategy: Sendable { + /// When the high watermark is reached producers will be suspended. All producers will be resumed again once + /// the low watermark is reached. + public static func watermark(low: Int, high: Int) -> BackpressureStrategy {} + } + + /// A type that indicates the result of writing elements to the source. + @frozen + public enum WriteResult: Sendable { + /// A token that is returned when the asynchronous stream's backpressure strategy indicated that production should + /// be suspended. Use this token to enqueue a callback by calling the ``enqueueCallback(_:)`` method. + public struct CallbackToken: Sendable {} + + /// Indicates that more elements should be produced and written to the source. + case produceMore + + /// Indicates that a callback should be enqueued. + /// + /// The associated token should be passed to the ``enqueueCallback(_:)`` method. + case enqueueCallback(CallbackToken) + } + + /// A callback to invoke when the stream finished. + /// + /// The stream finishes and calls this closure in the following cases: + /// - No iterator was created and the sequence was deinited + /// - An iterator was created and deinited + /// - After ``finish(throwing:)`` was called and all elements have been consumed + /// - The consuming task got cancelled + public var onTermination: (@Sendable () -> Void)? + + /// Writes new elements to the asynchronous stream. + /// + /// If there is a task consuming the stream and awaiting the next element then the task will get resumed with the + /// first element of the provided sequence. If the asynchronous stream already terminated then this method will throw an error + /// indicating the failure. + /// + /// - Parameter sequence: The elements to write to the asynchronous stream. + /// - Returns: The result that indicates if more elements should be produced at this time. + public func write(contentsOf sequence: S) throws -> WriteResult where Element == S.Element, S: Sequence {} + + /// Write the element to the asynchronous stream. + /// + /// If there is a task consuming the stream and awaiting the next element then the task will get resumed with the + /// provided element. If the asynchronous stream already terminated then this method will throw an error + /// indicating the failure. + /// + /// - Parameter element: The element to write to the asynchronous stream. + /// - Returns: The result that indicates if more elements should be produced at this time. + public func write(_ element: Element) throws -> WriteResult {} + + /// Enqueues a callback that will be invoked once more elements should be produced. + /// + /// Call this method after ``write(contentsOf:)`` or ``write(_:)`` returned ``WriteResult/enqueueCallback(_:)``. + /// + /// - Important: Enqueueing the same token multiple times is not allowed. + /// + /// - Parameters: + /// - token: The callback token. + /// - onProduceMore: The callback which gets invoked once more elements should be produced. + public func enqueueCallback(token: WriteResult.CallbackToken, onProduceMore: @escaping @Sendable (Result) -> Void) {} + + /// Cancel an enqueued callback. + /// + /// Call this method to cancel a callback enqueued by the ``enqueueCallback(callbackToken:onProduceMore:)`` method. + /// + /// - Note: This methods supports being called before ``enqueueCallback(callbackToken:onProduceMore:)`` is called and + /// will mark the passed `token` as cancelled. + /// + /// - Parameter token: The callback token. + public func cancelCallback(token: WriteResult.CallbackToken) {} + + /// Write new elements to the asynchronous stream and provide a callback which will be invoked once more elements should be produced. + /// + /// If there is a task consuming the stream and awaiting the next element then the task will get resumed with the + /// first element of the provided sequence. If the asynchronous stream already terminated then `onProduceMore` will be invoked with + /// a `Result.failure`. + /// + /// - Parameters: + /// - sequence: The elements to write to the asynchronous stream. + /// - onProduceMore: The callback which gets invoked once more elements should be produced. This callback might be + /// invoked during the call to ``write(contentsOf:onProduceMore:)``. + public func write(contentsOf sequence: S, onProduceMore: @escaping @Sendable (Result) -> Void) where Element == S.Element, S: Sequence {} + + /// Writes the element to the asynchronous stream. + /// + /// If there is a task consuming the stream and awaiting the next element then the task will get resumed with the + /// provided element. If the asynchronous stream already terminated then `onProduceMore` will be invoked with + /// a `Result.failure`. + /// + /// - Parameters: + /// - sequence: The element to write to the asynchronous stream. + /// - onProduceMore: The callback which gets invoked once more elements should be produced. This callback might be + /// invoked during the call to ``write(_:onProduceMore:)``. + public func write(_ element: Element, onProduceMore: @escaping @Sendable (Result) -> Void) {} + + /// Write new elements to the asynchronous stream. + /// + /// If there is a task consuming the stream and awaiting the next element then the task will get resumed with the + /// first element of the provided sequence. If the asynchronous stream already terminated then this method will throw an error + /// indicating the failure. + /// + /// This method returns once more elements should be produced. + /// + /// - Parameters: + /// - sequence: The elements to write to the asynchronous stream. + public func write(contentsOf sequence: S) async throws where Element == S.Element, S: Sequence {} + + /// Write new element to the asynchronous stream. + /// + /// If there is a task consuming the stream and awaiting the next element then the task will get resumed with the + /// provided element. If the asynchronous stream already terminated then this method will throw an error + /// indicating the failure. + /// + /// This method returns once more elements should be produced. + /// + /// - Parameters: + /// - sequence: The element to write to the asynchronous stream. + public func write(_ element: Element) async throws {} + + /// Write the elements of the asynchronous sequence to the asynchronous stream. + /// + /// This method returns once the provided asynchronous sequence or the the asynchronous stream finished. + /// + /// - Important: This method does not finish the source if consuming the upstream sequence terminated. + /// + /// - Parameters: + /// - sequence: The elements to write to the asynchronous stream. + public func write(contentsOf sequence: S) async throws where Element == S.Element, S: AsyncSequence {} + + /// Indicates that the production terminated. + /// + /// After all buffered elements are consumed the next iteration point will return `nil`. + /// + /// Calling this function more than once has no effect. After calling finish, the stream enters a terminal state and doesn't accept + /// new elements. + public func finish() {} + } + + /// Initializes a new ``AsyncStream`` and an ``AsyncStream/Source``. + /// + /// - Parameters: + /// - elementType: The element type of the stream. + /// - backpressureStrategy: The backpressure strategy that the stream should use. + /// - Returns: A tuple containing the stream and its source. The source should be passed to the + /// producer while the stream should be passed to the consumer. + public static func makeStream( + of elementType: Element.Type = Element.self, + backpressureStrategy: Source.BackpressureStrategy + ) -> (`Self`, Source) {} +} + +extension AsyncThrowingStream { + /// A mechanism to interface between producer code and an asynchronous stream. + /// + /// Use this source to provide elements to the stream by calling one of the `write` methods, then terminate the stream normally + /// by calling the `finish()` method. You can also use the source's `finish(throwing:)` method to terminate the stream by + /// throwing an error + public struct Source: Sendable { + /// A strategy that handles the backpressure of the asynchronous stream. + public struct BackpressureStrategy: Sendable { + /// When the high watermark is reached producers will be suspended. All producers will be resumed again once + /// the low watermark is reached. + public static func watermark(low: Int, high: Int) -> BackpressureStrategy {} + } + + /// A type that indicates the result of writing elements to the source. + @frozen + public enum WriteResult: Sendable { + /// A token that is returned when the asynchronous stream's backpressure strategy indicated that production should + /// be suspended. Use this token to enqueue a callback by calling the ``enqueueCallback(_:)`` method. + public struct CallbackToken: Sendable {} + + /// Indicates that more elements should be produced and written to the source. + case produceMore + + /// Indicates that a callback should be enqueued. + /// + /// The associated token should be passed to the ``enqueueCallback(_:)`` method. + case enqueueCallback(CallbackToken) + } + + /// A callback to invoke when the stream finished. + /// + /// The stream finishes and calls this closure in the following cases: + /// - No iterator was created and the sequence was deinited + /// - An iterator was created and deinited + /// - After ``finish(throwing:)`` was called and all elements have been consumed + /// - The consuming task got cancelled + public var onTermination: (@Sendable () -> Void)? {} + + /// Writes new elements to the asynchronous stream. + /// + /// If there is a task consuming the stream and awaiting the next element then the task will get resumed with the + /// first element of the provided sequence. If the asynchronous stream already terminated then this method will throw an error + /// indicating the failure. + /// + /// - Parameter sequence: The elements to write to the asynchronous stream. + /// - Returns: The result that indicates if more elements should be produced at this time. + public func write(contentsOf sequence: S) throws -> WriteResult where Element == S.Element, S: Sequence {} + + /// Write the element to the asynchronous stream. + /// + /// If there is a task consuming the stream and awaiting the next element then the task will get resumed with the + /// provided element. If the asynchronous stream already terminated then this method will throw an error + /// indicating the failure. + /// + /// - Parameter element: The element to write to the asynchronous stream. + /// - Returns: The result that indicates if more elements should be produced at this time. + public func write(_ element: Element) throws -> WriteResult {} + + /// Enqueues a callback that will be invoked once more elements should be produced. + /// + /// Call this method after ``write(contentsOf:)`` or ``write(_:)`` returned ``WriteResult/enqueueCallback(_:)``. + /// + /// - Important: Enqueueing the same token multiple times is not allowed. + /// + /// - Parameters: + /// - token: The callback token. + /// - onProduceMore: The callback which gets invoked once more elements should be produced. + public func enqueueCallback(token: WriteResult.CallbackToken, onProduceMore: @escaping @Sendable (Result) -> Void) {} + + /// Cancel an enqueued callback. + /// + /// Call this method to cancel a callback enqueued by the ``enqueueCallback(callbackToken:onProduceMore:)`` method. + /// + /// - Note: This methods supports being called before ``enqueueCallback(callbackToken:onProduceMore:)`` is called and + /// will mark the passed `token` as cancelled. + /// + /// - Parameter token: The callback token. + public func cancelCallback(token: WriteResult.CallbackToken) {} + + /// Write new elements to the asynchronous stream and provide a callback which will be invoked once more elements should be produced. + /// + /// If there is a task consuming the stream and awaiting the next element then the task will get resumed with the + /// first element of the provided sequence. If the asynchronous stream already terminated then `onProduceMore` will be invoked with + /// a `Result.failure`. + /// + /// - Parameters: + /// - sequence: The elements to write to the asynchronous stream. + /// - onProduceMore: The callback which gets invoked once more elements should be produced. This callback might be + /// invoked during the call to ``write(contentsOf:onProduceMore:)``. + public func write(contentsOf sequence: S, onProduceMore: @escaping @Sendable (Result) -> Void) where Element == S.Element, S: Sequence {} + + /// Writes the element to the asynchronous stream. + /// + /// If there is a task consuming the stream and awaiting the next element then the task will get resumed with the + /// provided element. If the asynchronous stream already terminated then `onProduceMore` will be invoked with + /// a `Result.failure`. + /// + /// - Parameters: + /// - sequence: The element to write to the asynchronous stream. + /// - onProduceMore: The callback which gets invoked once more elements should be produced. This callback might be + /// invoked during the call to ``write(_:onProduceMore:)``. + public func write(_ element: Element, onProduceMore: @escaping @Sendable (Result) -> Void) {} + + /// Write new elements to the asynchronous stream. + /// + /// If there is a task consuming the stream and awaiting the next element then the task will get resumed with the + /// first element of the provided sequence. If the asynchronous stream already terminated then this method will throw an error + /// indicating the failure. + /// + /// This method returns once more elements should be produced. + /// + /// - Parameters: + /// - sequence: The elements to write to the asynchronous stream. + public func write(contentsOf sequence: S) async throws where Element == S.Element, S: Sequence {} + + /// Write new element to the asynchronous stream. + /// + /// If there is a task consuming the stream and awaiting the next element then the task will get resumed with the + /// provided element. If the asynchronous stream already terminated then this method will throw an error + /// indicating the failure. + /// + /// This method returns once more elements should be produced. + /// + /// - Parameters: + /// - sequence: The element to write to the asynchronous stream. + public func write(_ element: Element) async throws {} + + /// Write the elements of the asynchronous sequence to the asynchronous stream. + /// + /// This method returns once the provided asynchronous sequence or the the asynchronous stream finished. + /// + /// - Important: This method does not finish the source if consuming the upstream sequence terminated. + /// + /// - Parameters: + /// - sequence: The elements to write to the asynchronous stream. + public func write(contentsOf sequence: S) async throws where Element == S.Element, S: AsyncSequence {} + + /// Indicates that the production terminated. + /// + /// After all buffered elements are consumed the next iteration point will return `nil` or throw an error. + /// + /// Calling this function more than once has no effect. After calling finish, the stream enters a terminal state and doesn't accept + /// new elements. + /// + /// - Parameters: + /// - error: The error to throw, or `nil`, to finish normally. + public func finish(throwing error: Failure?) {} + } + + /// Initializes a new ``AsyncThrowingStream`` and an ``AsyncThrowingStream/Source``. + /// + /// - Parameters: + /// - elementType: The element type of the stream. + /// - failureType: The failure type of the stream. + /// - backpressureStrategy: The backpressure strategy that the stream should use. + /// - Returns: A tuple containing the stream and its source. The source should be passed to the + /// producer while the stream should be passed to the consumer. + public static func makeStream( + of elementType: Element.Type = Element.self, + throwing failureType: Failure.Type = Failure.self, + backpressureStrategy: Source.BackpressureStrategy + ) -> (`Self`, Source) where Failure == Error {} +} +``` + +## Comparison to other root asynchronous sequences + +### swift-async-algorithm: AsyncChannel + +The `AsyncChannel` is a multi-consumer/multi-producer root asynchronous sequence +which can be used to communicate between two tasks. It only offers asynchronous +production APIs and has no internal buffer. This means that any producer will be +suspended until its value has been consumed. `AsyncChannel` can handle multiple +consumers and resumes them in FIFO order. + +### swift-nio: NIOAsyncSequenceProducer + +The NIO team have created their own root asynchronous sequence with the goal to +provide a high performance sequence that can be used to bridge a NIO `Channel` +inbound stream into Concurrency. The `NIOAsyncSequenceProducer` is a highly +generic and fully inlinable type and quite unwiedly to use. This proposal is +heavily inspired by the learnings from this type but tries to create a more +flexible and easier to use API that fits into the standard library. + +## Source compatibility + +This change is additive and does not affect source compatibility. + +## ABI compatibility + +This change is additive and does not affect ABI compatibility. All new methods +are non-inlineable leaving us flexiblity to change the implementation in the +future. + +## Future directions + +### Adaptive backpressure strategy + +The high/low watermark strategy is common in networking code; however, there are +other strategies such as an adaptive strategy that we could offer in the future. +An adaptive strategy regulates the backpressure based on the rate of +consumption and production. With the proposed new APIs we can easily add further +strategies. + +### Element size dependent strategy + +When the stream's element is a collection type then the proposed high/low +watermark backpressure strategy might lead to unexpected results since each +element can vary in actual memory size. In the future, we could provide a new +backpressure strategy that supports inspecting the size of the collection. + +### Deprecate `Async[Throwing]Stream.Continuation` + +In the future, we could deprecate the current continuation based APIs since the +new proposed APIs are also capable of bridging non-backpressured producers by +just discarding the `WriteResult`. The only use-case that the new APIs do not +cover is the _anycast_ behaviour of the current `AsyncStream` where one can +create multiple iterators to the stream as long as no two iterators are +consuming the stream at the same time. This can be solved via additional +algorithms such as `broadcast` in the `swift-async-algorithms` package. + +To give developers more time to adopt the new APIs the deprecation of the +current APIs should deferred to a future version. Especially since those new +APIs are not backdeployed like the current Concurrency runtime. + +### Introduce a `Writer` and an `AsyncWriter` protocol + +The newly introduced `Source` type offers a bunch of different write methods. We +have seen similar types used in other places such as file abstraction or +networking APIs. We could introduce a new `Writer` and `AsyncWriter` protocol in +the future to enable writing generic algorithms on top of writers. The `Source` +type could then conform to these new protocols. + +## Alternatives considered + +### Providing an `Async[Throwing]Stream.Continuation.onConsume` + +We could add a new closure property to the `Async[Throwing]Stream.Continuation` +which is invoked once an element has been consumed to implement a backpressure +strategy; however, this requires the usage of a synchronization mechanism since +the consumption and production often happen on separate threads. The +added complexity and performance impact led to avoiding this approach. + +### Provide a getter for the current buffer depth + +We could provide a getter for the current buffer depth on the +`Async[Throwing]Stream.Continuation`. This could be used to query the buffer +depth at an arbitrary time; however, it wouldn't allow us to implement +backpressure strategies such as high/low watermarks without continuously asking +what the buffer depth is. That would result in a very inefficient +implementation. + +### Extending `Async[Throwing]Stream.Continuation` + +Extending the current APIs to support all expected behaviors is problematic +since it would change the semantics and might lead to currently working code +misbehaving. Furthermore, extending the current APIs to support backpressure +turns out to be problematic without compromising performance or usability. + +### Introducing a new type + +We could introduce a new type such as `AsyncBackpressured[Throwing]Stream`; +however, one of the original intentions of `Async[Throwing]Stream` was to be +able to bridge backpressured systems. Furthermore, `Async[Throwing]Stream` is +the best name. Therefore, this proposal decided to provide new interfaces to +`Async[Throwing]Stream`. + +### Stick with the current `Continuation` and `yield` naming + +The proposal decided against sticking to the current names since the existing +names caused confusion to them being used in multiple places. Continuation was +both used by the `AsyncStream` but also by Swift Concurrency via +`CheckedContinuation` and `UnsafeContinuation`. Similarly, yield was used by +both `AsyncStream.Continuation.yield()`, `Task.yield()` and the `yield` keyword. +Having different names for these different concepts makes it easier to explain +their usage. The currently proposed `write` names were choosen to align with the +future direction of adding an `AsyncWriter` protocol. `Source` is a common name +in flow based systems such as Akka. Other names that were considered: + +- `enqueue` +- `send` + +### Provide the `onTermination` callback to the factory method + +During development of the new APIs, I first tried to provide the `onTermination` +callback in the `makeStream` method. However, that showed significant usability +problems in scenarios where one wants to store the source in a type and +reference `self` in the `onTermination` closure at the same time; hence, I kept +the current pattern of setting the `onTermination` closure on the source. + +### Provide a `onConsumerCancellation` callback + +During the pitch phase it was raised that we should provide a +`onConsumerCancellation` callback which gets invoked once the asynchronous +stream notices that the consuming task got cancelled. This callback could be +used to customize how cancellation is handled by the stream e.g. one could +imagine writing a few more elements to the stream before finishing it. Right now +the stream immediately returns `nil` or throws a `CancellationError` when it +notices cancellation. This proposal decided to not provide this customization +because it opens up the possiblity that asynchronous streams are not terminating +when implemented incorrectly. Additionally, asynchronous sequences are not the +only place where task cancellation leads to an immediate error being thrown i.e. +`Task.sleep()` does the same. Hence, the value of the asynchronous not +terminating immediately brings little value when the next call in the iterating +task might throw. However, the implementation is flexible enough to add this in +the future and we can just default it to the current behaviour. + +### Create a custom type for the `Result` of the `onProduceMore` callback + +The `onProducerMore` callback takes a `Result` which is used to +indicate if the producer should produce more or if the asynchronous stream +finished. We could introduce a new type for this but the proposal decided +against it since it effectively is a result type. + +### Use an initializer instead of factory methods + +Instead of providing a `makeStream` factory method we could use an initializer +approach that takes a closure which gets the `Source` passed into. A similar API +has been offered with the `Continuation` based approach and +[SE-0388](https://github.com/apple/swift-evolution/blob/main/proposals/0388-async-stream-factory.md) +introduced new factory methods to solve some of the usability ergonomics with +the initializer based APIs. + +## Acknowledgements + +- [Johannes Weiss](https://github.com/weissi) - For making me aware how +important this problem is and providing great ideas on how to shape the API. +- [Philippe Hausler](https://github.com/phausler) - For helping me designing the +APIs and continuously providing feedback +- [George Barnett](https://github.com/glbrntt) - For providing extensive code +reviews and testing the implementation. From 7f0f1913f732482e1bf951a67976ebf89ba7aa9c Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 15 Aug 2023 14:30:10 -0600 Subject: [PATCH 3272/4563] Add review thread for SE-0406 (#2128) --- proposals/0406-async-stream-backpressure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0406-async-stream-backpressure.md b/proposals/0406-async-stream-backpressure.md index 36dd86d758..a761080ce7 100644 --- a/proposals/0406-async-stream-backpressure.md +++ b/proposals/0406-async-stream-backpressure.md @@ -5,7 +5,7 @@ * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Active review (August 15...28, 2023)** * Implementation: [apple/swift#66488](https://github.com/apple/swift/pull/66488) -* Review: ([pitch](https://forums.swift.org/t/pitch-new-apis-for-async-throwing-stream-with-backpressure-support/65449)) +* Review: ([pitch](https://forums.swift.org/t/pitch-new-apis-for-async-throwing-stream-with-backpressure-support/65449)) ([review](https://forums.swift.org/t/se-0406-backpressure-support-for-asyncstream/66771)) ## Introduction From 304e893e900bf7e2dd9aa5be003e92aee93af921 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Tue, 15 Aug 2023 17:54:02 -0400 Subject: [PATCH 3273/4563] Remove plugin support from the future work section --- proposals/0403-swiftpm-mixed-language-targets.md | 1 - 1 file changed, 1 deletion(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 23da94326e..5cbe4aa89b 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -506,7 +506,6 @@ listed in the Future Directions section as an area of future work. ## Future Directions -- Enable package manager plugin tools to process mixed language targets. - Enable package authors to expose non-public headers to their mixed target's Swift implemention. - Investigate uses cases for extending mixed language target support to From dc329b64751976380faa0e40962de3a7042ae885 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Tue, 15 Aug 2023 18:12:23 -0400 Subject: [PATCH 3274/4563] Remove alt. considered section --- .../0403-swiftpm-mixed-language-targets.md | 34 +++++-------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 5cbe4aa89b..a7283fa78c 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -201,6 +201,10 @@ While an implementation detail, it’s worth noting that in this approach, a Clang sources. This extends to the `MixedTargetDescription` type in that it wraps a `SwiftTargetDescription` and `ClangTargetDescription`. +Using this approach allows for greater code-reuse, and reduces the chance of +introducing a regression from changing existing sub-target types like +`SwiftTarget` and `ClangTarget`. + The role of the `MixedTargetBuildDescription` is to generate auxiliary artifacts needed for the build and pass specific build flags to the underlying `SwiftTargetBuildDescription` and `ClangTargetBuildDescription`. @@ -480,30 +484,6 @@ Additionally, this feature will be gated on a tools minor version update, so mixed language targets building on older toolchains that do not support this feature will continue to [throw an error][mixed-target-error]. -## Alternatives considered - -### Provide custom implementations for `MixedTarget` and `MixedTargetBuildDescription` - -As explained in the Detailed Design section, these two types effectively wrap -the Swift and Clang parts necessary to define or build the target. One -alternative approach was to provide custom implementations that did not heavily -rely on code reuse of existing types. The deciding drawback of this approach -was that it would have resulted in a lot of duplicated code. - -### Consolidate target modeling logic so that all targets are `MixedTarget`s - -The deciding drawback here was the risk of introducing a regression in how -Swift or Clang targets are built. A benefit of the chosen design over this -alternative is that the code paths introduced in this proposal have little -crossover with the existing code paths that build Swift or Clang targets– -further reducing the chance of introducing an untested regression. However, -this alternative should be considered as a future direction for the package -manager. The chosen design offers a natural path to making all targets mixed -source targets by default. The implementations from `ClangTarget`, -`SwiftTarget`, `ClangTargetBuildDescription` and `SwiftTargetBuildDescription` -can be bubbled up to the mixed target types accordingly. This alternative is -listed in the Future Directions section as an area of future work. - ## Future Directions - Enable package authors to expose non-public headers to their mixed @@ -511,8 +491,10 @@ listed in the Future Directions section as an area of future work. - Investigate uses cases for extending mixed language target support to currently unsupported types of targets (e.g. executables). - Extend this solution so that all targets are mixed language targets by - default. This refactor would simplify the current implementation of the - package manager. + default. This could simplify the implemention as language-specific types + like `ClangTarget`, `SwiftTarget`, and `MixedTarget` could be consolidated + into a single type. This approach was avoided in the initial implementation + of this feature to reduce the risk of introducing a regression. From c19b689c1e0a7e0bb0bbe8271e7e0d2d4d258ec5 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Tue, 15 Aug 2023 18:13:20 -0400 Subject: [PATCH 3275/4563] Make sentence more concise --- proposals/0403-swiftpm-mixed-language-targets.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index a7283fa78c..735300ef50 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -488,8 +488,8 @@ feature will continue to [throw an error][mixed-target-error]. - Enable package authors to expose non-public headers to their mixed target's Swift implemention. -- Investigate uses cases for extending mixed language target support to - currently unsupported types of targets (e.g. executables). +- Extend mixed language target support to currently unsupported types of + targets (e.g. executables). - Extend this solution so that all targets are mixed language targets by default. This could simplify the implemention as language-specific types like `ClangTarget`, `SwiftTarget`, and `MixedTarget` could be consolidated From be40a22768815e8ce6c44a95de099cd5ee4aa5d3 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Tue, 15 Aug 2023 18:14:31 -0400 Subject: [PATCH 3276/4563] Remove testing section --- proposals/0403-swiftpm-mixed-language-targets.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 735300ef50..f7e3ef27c2 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -455,20 +455,6 @@ There are several failure cases that may surface to end users: module \(target name). ``` -### Testing - -This feature was tested with a mix of unit and integration tests. -- `Tests/BuildTests/BuildPlanTests.swift`: Added several unit tests to assert - behavior for fundamental mixed target use cases. -- `Tests/PackageLoadingTests/ModuleMapGenerationTests.swift`: Added a unit test - to cover changes at the module map generation level. -- `Tests/PackageLoadingTests/PackageBuilderTests.swift`: Added several unit - tests to assert behavior when loading a mixed target. -- `Tests/FunctionalTests/MixedTargetTests.swift`: Added a thorough suite of - integration tests that use targets defined in the `Fixtures/MixedTargets` - directory. These fixture targets double as documented examples for different - mixed target configurations. - ## Security This has no impact on security, safety, or privacy. From 4ce94bf26af6c5a9bd656182949bb5c2a40efdc6 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Tue, 15 Aug 2023 18:29:40 -0400 Subject: [PATCH 3277/4563] Add plugin support section --- .../0403-swiftpm-mixed-language-targets.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index f7e3ef27c2..3fff5905aa 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -177,6 +177,69 @@ example demonstrates all the possible public headers that can be imported from #import "MixedPackage-Swift.h" ``` +## Plugin Support + +Package manager plugins should be able to process mixed language source +targets. The following type will be added to the `PackagePlugin` module +to represent a mixed language target in a plugin's context. + +This API was created by joining together the properties of the existing +`SwiftSourceModuleTarget` and `ClangSourceModuleTarget` types +([source][Swift-Clang-SourceModuleTarget]). + +```swift +/// Represents a target consisting of a source code module compiled using both the Clang and Swift compiler. +public struct MixedSourceModuleTarget: SourceModuleTarget { + /// Unique identifier for the target. + public let id: ID + + /// The name of the target, as defined in the package manifest. This name + /// is unique among the targets of the package in which it is defined. + public let name: String + + /// The kind of module, describing whether it contains unit tests, contains + /// the main entry point of an executable, or neither. + public let kind: ModuleKind + + /// The absolute path of the target directory in the local file system. + public let directory: Path + + /// Any other targets on which this target depends, in the same order as + /// they are specified in the package manifest. Conditional dependencies + /// that do not apply have already been filtered out. + public let dependencies: [TargetDependency] + + /// The name of the module produced by the target (derived from the target + /// name, though future SwiftPM versions may allow this to be customized). + public let moduleName: String + + /// The source files that are associated with this target (any files that + /// have been excluded in the manifest have already been filtered out). + public let sourceFiles: FileList + + /// Any custom compilation conditions specified for the target's Swift sources. + public let swiftCompilationConditions: [String] + + /// Any preprocessor definitions specified for the target's Clang sources. + public let clangPreprocessorDefinitions: [String] + + /// Any custom header search paths specified for the Clang target. + public let headerSearchPaths: [String] + + /// The directory containing public C headers, if applicable. This will + /// only be set for targets that have a directory of a public headers. + public let publicHeadersDirectory: Path? + + /// Any custom linked libraries required by the module, as specified in the + /// package manifest. + public let linkedLibraries: [String] + + /// Any custom linked frameworks required by the module, as specified in the + /// package manifest. + public let linkedFrameworks: [String] +} +``` + ## Detailed design @@ -509,3 +572,5 @@ feature will continue to [throw an error][mixed-target-error]. [should-emit-header]: https://github.com/apple/swift-package-manager/blob/6478e2724b8bf77856ff358cba5f59a4a62978bf/Sources/Build/BuildDescription/SwiftTargetBuildDescription.swift#L732-L735 [swift-emit-header-fr]: https://forums.swift.org/t/se-0403-package-manager-mixed-language-target-support/66202/31 + +[Swift-Clang-SourceModuleTarget]: https://github.com/apple/swift-package-manager/blob/8e512308530f808e9ef0cd149f4f632339c65bc4/Sources/PackagePlugin/PackageModel.swift#L231-L319 From 36e27aeec47195d2eade885491af2983f42fc142 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 18 Aug 2023 16:41:26 +0900 Subject: [PATCH 3278/4563] Correct example in 0309 so that it compiles The example in Proposed solution was missing an `extension` necessary to compile, and the `let array` must be a `var` because the append operation is mutating. The `any` could be debated if we should add it here -- in 5.9 that's required but at time of writing this proposal it wasn't. --- proposals/0309-unlock-existential-types-for-all-protocols.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0309-unlock-existential-types-for-all-protocols.md b/proposals/0309-unlock-existential-types-for-all-protocols.md index 35d1974787..39d7025c20 100644 --- a/proposals/0309-unlock-existential-types-for-all-protocols.md +++ b/proposals/0309-unlock-existential-types-for-all-protocols.md @@ -119,8 +119,9 @@ We suggest allowing any protocol to be used as a type and exercise the restricti ```swift protocol IntCollection: RangeReplaceableCollection where Self.Element == Int {} +extension Array : IntCollection where Element == Int {} -let array: IntCollection = [3, 1, 4, 1, 5] +var array: any IntCollection = [3, 1, 4, 1, 5] array.append(9) // OK, 'Self.Element' is known to be 'Int'. ``` From d1164416e8b955d5fede8bb177af81ad0cf6ff9e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 19 Aug 2023 00:01:01 -0700 Subject: [PATCH 3279/4563] Initial proposal for member macro conformances --- proposals/nnnn-member-macro-conformances.md | 116 ++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 proposals/nnnn-member-macro-conformances.md diff --git a/proposals/nnnn-member-macro-conformances.md b/proposals/nnnn-member-macro-conformances.md new file mode 100644 index 0000000000..2c4ad7a766 --- /dev/null +++ b/proposals/nnnn-member-macro-conformances.md @@ -0,0 +1,116 @@ +# Member Macro Conformances + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Doug Gregor](https://github.com/DougGregor) +* Review Manager: TBD +* Status: **Awaiting review** +* Vision: [Macros](https://github.com/apple/swift-evolution/blob/main/visions/macros.md) +* Implementation: [apple/swift#67758](https://github.com/apple/swift/pull/67758) +* Review: ([pitch](https://forums.swift.org/t/pitch-member-macros-that-know-what-conformances-are-missing/66590)) + +## Introduction + +The move from conformance macros to extension macros in [SE-0402](https://github.com/apple/swift-evolution/blob/main/proposals/0402-extension-macros.md) included the ability for extension macros to learn about which protocols the type already conformed to (e.g., because a superclass conformed or an explicit conformance was stated somewhere), so that the macro could avoid adding declarations and conformances that aren't needed. It also meant that any new declarations added are part of an extension---not the original type definition---which is generally beneficial, because it means that (e.g.) a new initializer doesn't suppress the memberwise initializer. It's also usually considered good form to split protocol conformances out into their own extensions. + +However, there are some times when the member used for the conformance really needs to be part of the original type definition. For example: + +- An initializer in a non-final class needs to be a `required init` to satisfy a protocol requirement. +- An overridable member of a non-final class. +- A stored property or case can only be in the primary type definition. + +For these cases, a member macro can produce the declarations. However, member macros aren't provided with any information about which protocol conformances they should provide members for, so a macro might erroneously try to add conforming members to a type that already conforms to the protocol (e.g., through a superclass). This can make certain macros---such as macros that implement the `Encodable` or `Decodable` protocols---unimplemented. + +## Proposed solution + +To make it possible for a member macro to provide the right members for a conformance, we propose to extend member macros with the same ability that extension macros have to reason about conformances. Specifically: + +* The `attached` attribute specifying a `member` role gains the ability to specify the set of protocol conformances it is interest in, the same way an `extension` macro specifies the conformances it can provide. +* The `expansion` operation for a `MemberMacro` -conforming implementation receives the set of protocols that were stated (as above) and which the type does not already conform to. + +This information allows a macro to reason about which members it should produce to satisfy conformances. Member macros that are interested in conformances are often going to also be extension macros, which work along with the member macro to provide complete conformance information. + +As an example, consider a `Codable` macro that provides the `init(from:)` and `encode(to:)` operations required by the `Decodable` and `Encodable` protocols, respectively. Such a macro could be defined as follows: + +```swift +@attached(member, conformances: Decodable, Encodable, names: named(init(from:), encode(to:))) +@attached(extension, conformances: Decodable, Encodable, names: named(init(from:), encode(to:))) +macro Codable() = #externalMacro(module: "MyMacros", type: "CodableMacro") +``` + +This macro has several important decisions to make about where and how to generate `init(from:)` and `encode(to:)`: + +* For a struct, enum, actor, or final class, `init(from:)` and `encode(to:)`should be emitted into an extension (via the member role) along with the conformance. This is both good style and, for structs, ensures that the initializer doesn't inhibit the memberwise initializer. +* For a non-final class, `init(from:)` and `encode(to:)`should be emitted into the main class definition (via the member role) so that can be overridden by subclasses. +* For a class that inherits `Encodable` or `Decodable` conformances from a superclass, the implementations of `init(from:)` and `encode(to:)` need to call the superclass's initializer and method, respectively, to decode/encode the entire class hierarchy. + +Given existing syntactic information about the type (including the presence or absence of `final`), and providing both the member and extension roles with information about which conformances the type needs (as proposed here), all of the above decisions can be made in the macro implementation, allowing a flexible implementation of a `Codable` macro that accounts for all manner of types. + +## Detailed design + +The specification of the `conformances` argument for the `@attached(member, ...)` attribute matches that of the corresponding argument for extension macros documented in [SE-0402](https://github.com/apple/swift-evolution/blob/main/proposals/0402-extension-macros.md). + +For macro implementations, the `expansion` requirement in the `MemberMacro` protocol is augmented with a `conformingTo:` argument that receives the same set of protocols as for extension macros. The `MemberMacro` protocol is now defined as follows: + +```swift +protocol MemberMacro: AttachedMacro { + /// Expand an attached declaration macro to produce a set of members. + /// + /// - Parameters: + /// - node: The custom attribute describing the attached macro. + /// - declaration: The declaration the macro attribute is attached to. + /// - missingConformancesTo: The set of protocols that were declared + /// in the set of conformances for the macro and to which the declaration + /// does not explicitly conform. The member macro itself cannot declare + /// conformances to these protocols (only an extension macro can do that), + /// but can provide supporting declarations, such as a required + /// initializer or stored property, that cannot be written in an + /// extension. + /// - context: The context in which to perform the macro expansion. + /// + /// - Returns: the set of member declarations introduced by this macro, which + /// are nested inside the `attachedTo` declaration. + static func expansion( + of node: AttributeSyntax, + providingMembersOf declaration: some DeclGroupSyntax, + conformingTo protocols: [TypeSyntax], + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] +} +``` + +Note that member macro definitions don't provide the conformances themselves; that is still part of the extension macro role. + +## Source compatibility + +This proposal uses the existing syntactic space for the `@attached` attribute and is a pure extension; it does not have any source compatibility impact. + +## ABI compatibility + +As a macro feature, this proposal does not affect ABI in any way. + +## Implications on adoption + +This feature can be freely adopted in source code with no deployment constraints or affecting source or ABI compatibility. Uses of any macro that employs this feature can also be removed from the source code by expanding the macro in place. + +## Alternatives considered + +### Extensions that affect the primary type definition + +A completely different approach to the stated problem would be to introduce a form of extension that adds members to the type as-if they were written directly in the type definition. For example: + +```swift +class MyClass { ... } + +@implementation extension MyClass: Codable { + required init(from decoder: Decoder) throws { ... } + func encode(to coder: Coder) throws { ... } +} +``` + +The members of `@implementation` extensions would follow the same rules as members in the main type definition. For example, stored properties could be defined in the `@implementation` extension, as could `required` initializers, and any overridable methods, properties, or subscripts. The deinitializer and enum cases could also be defined in `@implementation` extensions if that were deemed useful. + +There would be some limitations on `@implementation` extensions: they could only be defined in the same source file as the original type, and these extensions might not be permitted to have any additional generic constraints. Protocols don't have implementations per se, and therefore might not support implementation extensions. + +Given the presence of `@implementation` extensions, the extension to member macros in this proposal would no longer be needed, because one could achieve the desired effect using an extension macro that produces an `@implementation` extension for cases where it needs to extend the implementation itself. + +The primary drawback to this notion of implementation extensions is that it will no longer be possible to look at the primary definition of a type to find it's full "shape": it's stored properties, designated/required initializers, overridable methods, and so on. Instead, that information can be scattered amongst the original type definition and any implementation extensions, requiring one to stitch together a view of the whole type. From 4c4744cdb807bd47e1dfb5dd8cf0fe2d6e31f0e5 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 19 Aug 2023 21:43:12 -0700 Subject: [PATCH 3280/4563] Strengthen argument qagainst implementation extensions --- proposals/nnnn-member-macro-conformances.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-member-macro-conformances.md b/proposals/nnnn-member-macro-conformances.md index 2c4ad7a766..4852a1b4d5 100644 --- a/proposals/nnnn-member-macro-conformances.md +++ b/proposals/nnnn-member-macro-conformances.md @@ -113,4 +113,4 @@ There would be some limitations on `@implementation` extensions: they could only Given the presence of `@implementation` extensions, the extension to member macros in this proposal would no longer be needed, because one could achieve the desired effect using an extension macro that produces an `@implementation` extension for cases where it needs to extend the implementation itself. -The primary drawback to this notion of implementation extensions is that it will no longer be possible to look at the primary definition of a type to find it's full "shape": it's stored properties, designated/required initializers, overridable methods, and so on. Instead, that information can be scattered amongst the original type definition and any implementation extensions, requiring one to stitch together a view of the whole type. +The primary drawback to this notion of implementation extensions is that it will no longer be possible to look at the primary definition of a type to find it's full "shape": it's stored properties, designated/required initializers, overridable methods, and so on. Instead, that information can be scattered amongst the original type definition and any implementation extensions, requiring one to stitch together a view of the whole type. This would have a particularly negative effect on macros that want to reason about the shape of the type, because macros only see a single entity (such as a class or struct definition), and not across extensions to that entity. The `Codable` macro discussed in this proposal, for example, would not be able to encode or decode stored properties that are written in an implementation extension, and the [`Observable`](https://github.com/apple/swift-evolution/blob/main/proposals/0395-observability.md) macro would silently fail to observe any properties written in an implementation extension. By trying to use implementation extensions to address the shortcoming of macros described in this proposal, we would end up creating a larger problem for those same macros. From 3d9cae4a54bd74000daa092400e39a3bc678b47c Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 24 Aug 2023 06:09:30 +0100 Subject: [PATCH 3281/4563] Update README.md (#2132) Swift 5.10 Release Process --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c61f61440d..e443d6eccb 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ This repository tracks the ongoing evolution of the Swift programming language, | Version | Announced | Released | | :-------- | :----------------------------------------------------------------------- | :----------------------------------------------------------- | +| Swift 5.10 | [2023-08-23](https://forums.swift.org/t/swift-5-10-release-process/66911) | | Swift 5.9 | [2023-03-06](https://forums.swift.org/t/swift-5-9-release-process/63557) | | Swift 5.8 | [2022-11-19](https://forums.swift.org/t/swift-5-8-release-process/61540) | [2023-03-30](https://www.swift.org/blog/swift-5.8-released/) | | Swift 5.7 | [2022-03-29](https://forums.swift.org/t/swift-5-7-release-process/56316) | [2022-09-12](https://www.swift.org/blog/swift-5.7-released/) | From 7bd9b84e72f2ba1df1f1c9f38165f766d708ed6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EC=9E=AC=ED=98=B8?= Date: Sat, 26 Aug 2023 03:24:09 +0900 Subject: [PATCH 3282/4563] [SE-0404] Fix a typo (#2136) --- proposals/0404-nested-protocols.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0404-nested-protocols.md b/proposals/0404-nested-protocols.md index ed01cb41d6..44ce6739f2 100644 --- a/proposals/0404-nested-protocols.md +++ b/proposals/0404-nested-protocols.md @@ -49,7 +49,7 @@ class TableView { } ``` -Protocols can also be nested within non-generic functions and closures. Admittedly, this is of somewhat limited utility, as all conformances to such protocols must also be within the same function. However, there is also no reason to artificially limit the complexity of the models which developers create within a function. Some codebases (of note, the Swift compiler itself) make use of large closures with nested types, and they beneift from abstractions using protocols. +Protocols can also be nested within non-generic functions and closures. Admittedly, this is of somewhat limited utility, as all conformances to such protocols must also be within the same function. However, there is also no reason to artificially limit the complexity of the models which developers create within a function. Some codebases (of note, the Swift compiler itself) make use of large closures with nested types, and they benefit from abstractions using protocols. ```swift func doSomething() { From 1ad3d99e21b54da975b26daa69ad10413fd48744 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 25 Aug 2023 11:25:28 -0700 Subject: [PATCH 3283/4563] Apply suggestions from code review Co-authored-by: John McCall --- proposals/nnnn-member-macro-conformances.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/nnnn-member-macro-conformances.md b/proposals/nnnn-member-macro-conformances.md index 4852a1b4d5..b5fc658055 100644 --- a/proposals/nnnn-member-macro-conformances.md +++ b/proposals/nnnn-member-macro-conformances.md @@ -24,7 +24,7 @@ For these cases, a member macro can produce the declarations. However, member ma To make it possible for a member macro to provide the right members for a conformance, we propose to extend member macros with the same ability that extension macros have to reason about conformances. Specifically: -* The `attached` attribute specifying a `member` role gains the ability to specify the set of protocol conformances it is interest in, the same way an `extension` macro specifies the conformances it can provide. +* The `attached` attribute specifying a `member` role gains the ability to specify the set of protocol conformances it is interested in, the same way an `extension` macro specifies the conformances it can provide. * The `expansion` operation for a `MemberMacro` -conforming implementation receives the set of protocols that were stated (as above) and which the type does not already conform to. This information allows a macro to reason about which members it should produce to satisfy conformances. Member macros that are interested in conformances are often going to also be extension macros, which work along with the member macro to provide complete conformance information. @@ -39,8 +39,8 @@ macro Codable() = #externalMacro(module: "MyMacros", type: "CodableMacro") This macro has several important decisions to make about where and how to generate `init(from:)` and `encode(to:)`: -* For a struct, enum, actor, or final class, `init(from:)` and `encode(to:)`should be emitted into an extension (via the member role) along with the conformance. This is both good style and, for structs, ensures that the initializer doesn't inhibit the memberwise initializer. -* For a non-final class, `init(from:)` and `encode(to:)`should be emitted into the main class definition (via the member role) so that can be overridden by subclasses. +* For a struct, enum, actor, or final class, `init(from:)` and `encode(to:)` should be emitted into an extension (via the member role) along with the conformance. This is both good style and, for structs, ensures that the initializer doesn't inhibit the memberwise initializer. +* For a non-final class, `init(from:)` and `encode(to:)` should be emitted into the main class definition (via the member role) so that they can be overridden by subclasses. * For a class that inherits `Encodable` or `Decodable` conformances from a superclass, the implementations of `init(from:)` and `encode(to:)` need to call the superclass's initializer and method, respectively, to decode/encode the entire class hierarchy. Given existing syntactic information about the type (including the presence or absence of `final`), and providing both the member and extension roles with information about which conformances the type needs (as proposed here), all of the above decisions can be made in the macro implementation, allowing a flexible implementation of a `Codable` macro that accounts for all manner of types. @@ -113,4 +113,4 @@ There would be some limitations on `@implementation` extensions: they could only Given the presence of `@implementation` extensions, the extension to member macros in this proposal would no longer be needed, because one could achieve the desired effect using an extension macro that produces an `@implementation` extension for cases where it needs to extend the implementation itself. -The primary drawback to this notion of implementation extensions is that it will no longer be possible to look at the primary definition of a type to find it's full "shape": it's stored properties, designated/required initializers, overridable methods, and so on. Instead, that information can be scattered amongst the original type definition and any implementation extensions, requiring one to stitch together a view of the whole type. This would have a particularly negative effect on macros that want to reason about the shape of the type, because macros only see a single entity (such as a class or struct definition), and not across extensions to that entity. The `Codable` macro discussed in this proposal, for example, would not be able to encode or decode stored properties that are written in an implementation extension, and the [`Observable`](https://github.com/apple/swift-evolution/blob/main/proposals/0395-observability.md) macro would silently fail to observe any properties written in an implementation extension. By trying to use implementation extensions to address the shortcoming of macros described in this proposal, we would end up creating a larger problem for those same macros. +The primary drawback to this notion of implementation extensions is that it would no longer be possible to look at the primary definition of a type to find its full "shape": its stored properties, designated/required initializers, overridable methods, and so on. Instead, that information could be scattered amongst the original type definition and any implementation extensions, requiring readers to stitch together a view of the whole type. This would have a particularly negative effect on macros that want to reason about the shape of the type, because macros only see a single entity (such as a class or struct definition) and not extensions to that entity. The `Codable` macro discussed in this proposal, for example, would not be able to encode or decode stored properties that are written in an implementation extension, and the [`Observable`](https://github.com/apple/swift-evolution/blob/main/proposals/0395-observability.md) macro would silently fail to observe any properties written in an implementation extension. By trying to use implementation extensions to address the shortcoming of macros described in this proposal, we would end up creating a larger problem for those same macros. From f81d6596553a7c17e2e0e54dd980e4cd681c4344 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 25 Aug 2023 15:49:30 -0400 Subject: [PATCH 3284/4563] Assign SE-0407 to the member macros conformances proposal and put it in review --- ...ro-conformances.md => 0407-member-macro-conformances.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{nnnn-member-macro-conformances.md => 0407-member-macro-conformances.md} (98%) diff --git a/proposals/nnnn-member-macro-conformances.md b/proposals/0407-member-macro-conformances.md similarity index 98% rename from proposals/nnnn-member-macro-conformances.md rename to proposals/0407-member-macro-conformances.md index b5fc658055..72a593fa08 100644 --- a/proposals/nnnn-member-macro-conformances.md +++ b/proposals/0407-member-macro-conformances.md @@ -1,9 +1,9 @@ # Member Macro Conformances -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0407](0407-member-macro-conformances.md) * Authors: [Doug Gregor](https://github.com/DougGregor) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (August 25th...September 4th, 2023)** * Vision: [Macros](https://github.com/apple/swift-evolution/blob/main/visions/macros.md) * Implementation: [apple/swift#67758](https://github.com/apple/swift/pull/67758) * Review: ([pitch](https://forums.swift.org/t/pitch-member-macros-that-know-what-conformances-are-missing/66590)) From 063b9342e35c545a6a1f6fdb03ae012d5a4cdad8 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 25 Aug 2023 16:14:30 -0400 Subject: [PATCH 3285/4563] Link SE-0407 to its review thread --- proposals/0407-member-macro-conformances.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0407-member-macro-conformances.md b/proposals/0407-member-macro-conformances.md index 72a593fa08..1b50396a40 100644 --- a/proposals/0407-member-macro-conformances.md +++ b/proposals/0407-member-macro-conformances.md @@ -6,7 +6,7 @@ * Status: **Active review (August 25th...September 4th, 2023)** * Vision: [Macros](https://github.com/apple/swift-evolution/blob/main/visions/macros.md) * Implementation: [apple/swift#67758](https://github.com/apple/swift/pull/67758) -* Review: ([pitch](https://forums.swift.org/t/pitch-member-macros-that-know-what-conformances-are-missing/66590)) +* Review: ([pitch](https://forums.swift.org/t/pitch-member-macros-that-know-what-conformances-are-missing/66590)) ([review](https://forums.swift.org/t/se-0407-member-macro-conformances/66951)) ## Introduction From 7d348155e161907398e52b56601f62198be908a6 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sun, 27 Aug 2023 09:42:30 -0700 Subject: [PATCH 3286/4563] SE-0403: Update status --- proposals/0403-swiftpm-mixed-language-targets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index d6ce31032c..57dde11ac5 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -3,7 +3,7 @@ * Proposal: [SE-0403](0403-swiftpm-mixed-language-targets.md) * Authors: [Nick Cooke](https://github.com/ncooke3) * Review Manager: [Saleem Abdulrasool](https://github.com/compnerd) -* Status: **Scheduled for review (July 17, 2023...July 28, 2023)** +* Status: **Returned for Revision** * Implementation: [apple/swift-package-manager#5919](https://github.com/apple/swift-package-manager/pull/5919) * Decision Notes: [Pitch](https://forums.swift.org/t/61564) From 882da51e9024c9d012022b4bed0c91a8f42f797d Mon Sep 17 00:00:00 2001 From: uhooi Date: Thu, 31 Aug 2023 11:13:57 +0900 Subject: [PATCH 3287/4563] Update 0377-parameter-ownership-modifiers.md --- proposals/0377-parameter-ownership-modifiers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index 6b962fa201..b492a00606 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -224,7 +224,7 @@ extension String { return (self, self) // ERROR: needs to copy `self` } consuming func bar() -> (String, String) { - return (self, self) // ERROR: needs to copy `x` + return (self, self) // ERROR: needs to copy `self` } } ``` From ff118fdf3faff8437f5563206b4aa0d7e377c978 Mon Sep 17 00:00:00 2001 From: uhooi Date: Thu, 31 Aug 2023 11:16:24 +0900 Subject: [PATCH 3288/4563] Update 0377-parameter-ownership-modifiers.md --- proposals/0377-parameter-ownership-modifiers.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/0377-parameter-ownership-modifiers.md b/proposals/0377-parameter-ownership-modifiers.md index 6b962fa201..6cb40f9951 100644 --- a/proposals/0377-parameter-ownership-modifiers.md +++ b/proposals/0377-parameter-ownership-modifiers.md @@ -206,7 +206,7 @@ let helloWorld = "hello ".plus("cruel ").plus("world") `borrowing` and `consuming` parameter values are also **not implicitly copyable** inside of the function or closure body: -``` +```swift func foo(x: borrowing String) -> (String, String) { return (x, x) // ERROR: needs to copy `x` } @@ -218,7 +218,7 @@ func bar(x: consuming String) -> (String, String) { And so is the `self` parameter within a method that has the method-level `borrowing` or `consuming` modifier: -``` +```swift extension String { borrowing func foo() -> (String, String) { return (self, self) // ERROR: needs to copy `self` @@ -244,7 +244,7 @@ as if it were of some noncopyable type. To allow a copy to occur, the `copy x` operator may be used: -``` +```swift func dup(_ x: borrowing String) -> (String, String) { return (copy x, copy x) // OK, copies explicitly allowed here } @@ -267,7 +267,7 @@ The value of the parameter may be passed to other functions, or assigned to other variables (if the convention allows), at which point the value may be implicitly copied through those other parameter or variable bindings. -``` +```swift func foo(x: borrowing String) { let y = x // ERROR: attempt to copy `x` bar(z: x) // OK, invoking `bar(z:)` does not require copying `x` @@ -291,7 +291,7 @@ the caller, so if forming the call requires copying, that will raise an error, even if the parameter would be implicitly copyable in the callee. The function body serves as the boundary for the no-implicit-copy constraint: -``` +```swift struct Bar { var a: String var b: String @@ -418,7 +418,7 @@ Instead of having no-implicit-copy behavior be tied to the ownership-related binding forms and parameter modifiers, we could have an attribute that can be applied to any binding to say that it should not be implicitly copyable: -``` +```swift @noImplicitCopy(self) func foo(x: @noImplicitCopy String) { @noImplicitCopy let y = copy x @@ -439,7 +439,7 @@ Unlike the `consume x` or `borrow x` operator, copying doesn't have any specific semantic needs that couldn't be done by a regular function. Instead of an operator, `copy` could be defined as a regular standard library function: -``` +```swift func copy(_ value: T) -> T { return value } From e606c0d866d5c68bb58fa660628492af9431e598 Mon Sep 17 00:00:00 2001 From: Lawrence Gimenez Date: Mon, 4 Sep 2023 02:50:11 +0800 Subject: [PATCH 3289/4563] [SE-0403] Fixed typo (#2146) --- proposals/0403-swiftpm-mixed-language-targets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 57dde11ac5..34d06a80c9 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -470,7 +470,7 @@ header, and that header is emitted alongside the Swift module when the Swift part of the target is built. This relationship is enforced in that the generated interop header is listed as an input to the compilation commands for the target’s C language sources. This is specified in the llbuild manifest -(`debug.yaml` in the packag's `.build` directory). +(`debug.yaml` in the package's `.build` directory). ##### Build flags for the Swift part of the target From 37531427931a57ff2a76225741c99de8fa8b8c59 Mon Sep 17 00:00:00 2001 From: Lawrence Gimenez Date: Mon, 4 Sep 2023 02:50:35 +0800 Subject: [PATCH 3290/4563] [SE-0405] Fixed typo initialization (#2145) --- proposals/0405-string-validating-initializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0405-string-validating-initializers.md b/proposals/0405-string-validating-initializers.md index 2747f38fb0..8d57f61d40 100644 --- a/proposals/0405-string-validating-initializers.md +++ b/proposals/0405-string-validating-initializers.md @@ -234,7 +234,7 @@ When decoding a byte stream, obtaining the details of a validation failure would #### Improve input-repairing initialization -There is only one initializer in the standard library for input-repairing initilization, and it suffers from a discoverability issue. We can add a more discoverable version specifically for the UTF-8 encoding, similarly to one of the additions proposed here. +There is only one initializer in the standard library for input-repairing initialization, and it suffers from a discoverability issue. We can add a more discoverable version specifically for the UTF-8 encoding, similarly to one of the additions proposed here. #### Add normalization options From 1de9cf507cc5a6eed0c6f8e63e24b75ed8ca9006 Mon Sep 17 00:00:00 2001 From: Lawrence Gimenez Date: Mon, 4 Sep 2023 07:59:08 +0800 Subject: [PATCH 3291/4563] [SE-0397] Fixed typo syntactically --- proposals/0397-freestanding-declaration-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index 159769efc9..b2cb5b4712 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -148,7 +148,7 @@ public struct UInt64 { ... } ### Restrictions -Like attached peer macros, a freestanding declaration macro can expand to any declaration that is syntatically and semantically well-formed within the context where the macro is expanded. It shares the same requirements and restrictions: +Like attached peer macros, a freestanding declaration macro can expand to any declaration that is syntactically and semantically well-formed within the context where the macro is expanded. It shares the same requirements and restrictions: - [**Specifying newly-introduced names**](https://github.com/apple/swift-evolution/blob/main/proposals/0389-attached-macros.md#specifying-newly-introduced-names) - Note that only `named(...)` and `arbitrary` are allowed as macro-introduced names for a declaration macro. `overloaded`, `prefixed`, and `suffixed` do not make sense when there is no declaration from which to derive names. From 09a537d873efd3aa7acc5590a614d86e50cc0b66 Mon Sep 17 00:00:00 2001 From: Lawrence Gimenez Date: Mon, 4 Sep 2023 08:46:52 +0800 Subject: [PATCH 3292/4563] [SE-0394] Fixed typo --- proposals/0394-swiftpm-expression-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0394-swiftpm-expression-macros.md b/proposals/0394-swiftpm-expression-macros.md index a3a48e3779..6eeb5e8fd5 100644 --- a/proposals/0394-swiftpm-expression-macros.md +++ b/proposals/0394-swiftpm-expression-macros.md @@ -9,7 +9,7 @@ ## Introduction -Macros provide a way to extend Swift by performing arbitary syntactic transformations on input source code to produce new code. One example for this are expression macros which were previously proposed in [SE-0382](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md). This proposal covers how custom macros are defined, built and distributed as part of a Swift package. +Macros provide a way to extend Swift by performing arbitrary syntactic transformations on input source code to produce new code. One example for this are expression macros which were previously proposed in [SE-0382](https://github.com/apple/swift-evolution/blob/main/proposals/0382-expression-macros.md). This proposal covers how custom macros are defined, built and distributed as part of a Swift package. ## Motivation From 0ef123aeae2d26411d24a7bb947b9dd0ebf096a2 Mon Sep 17 00:00:00 2001 From: Lawrence Gimenez Date: Mon, 4 Sep 2023 09:01:50 +0800 Subject: [PATCH 3293/4563] Fixed typo --- proposals/0394-swiftpm-expression-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0394-swiftpm-expression-macros.md b/proposals/0394-swiftpm-expression-macros.md index 6eeb5e8fd5..f018470cf8 100644 --- a/proposals/0394-swiftpm-expression-macros.md +++ b/proposals/0394-swiftpm-expression-macros.md @@ -78,7 +78,7 @@ Any code from macro implementations can be tested by declaring a dependency on t ## Detailed Design -SwiftPM builds each macro as an executable for the host platform, applying certain additional compiler flags. Macros are expected to depend on SwiftSyntax using a versioned dependency that corresponds to a particular major Swift release. Note that SwiftPM's dependency resolution is workspace-wide, so all macros (and potentially other clients) will end up consolidating on one particular version of SwiftSyntax. Each target that transitively depends on a macro will have access to it, concretely this happens by SwiftPM passing `-load-plugin-executable` to the compiler to specify which executable contains the implementation of a certain macro module (e.g. `-load-plugin-executable /path/to/package/.build/debug/MacroImpl#MacroImpl` where the argument after the hash symbol is a comma separated list of module names which can be referenced by the `module` parameter of external macro declarations). The macro defintion refers to the module and concrete type via an `#externalMacro` declaration which allows any dependency of the defining target to have access to the concrete macro. If any target of a library product depends on a macro, clients of said library will also get access to any public macros. Macros can have dependencies like any other target, but product dependencies of macros need to be statically linked, so explicitly dynamic library products cannot be used by a macro target. +SwiftPM builds each macro as an executable for the host platform, applying certain additional compiler flags. Macros are expected to depend on SwiftSyntax using a versioned dependency that corresponds to a particular major Swift release. Note that SwiftPM's dependency resolution is workspace-wide, so all macros (and potentially other clients) will end up consolidating on one particular version of SwiftSyntax. Each target that transitively depends on a macro will have access to it, concretely this happens by SwiftPM passing `-load-plugin-executable` to the compiler to specify which executable contains the implementation of a certain macro module (e.g. `-load-plugin-executable /path/to/package/.build/debug/MacroImpl#MacroImpl` where the argument after the hash symbol is a comma separated list of module names which can be referenced by the `module` parameter of external macro declarations). The macro definition refers to the module and concrete type via an `#externalMacro` declaration which allows any dependency of the defining target to have access to the concrete macro. If any target of a library product depends on a macro, clients of said library will also get access to any public macros. Macros can have dependencies like any other target, but product dependencies of macros need to be statically linked, so explicitly dynamic library products cannot be used by a macro target. Concretely, the code for the macro package shown earlier would contain a macro implementation looking like this: From 729fadae4f823def333c12502fde6fb110aff028 Mon Sep 17 00:00:00 2001 From: Lawrence Gimenez Date: Tue, 5 Sep 2023 12:45:44 +0800 Subject: [PATCH 3294/4563] [SE-0406] Fixed typo (#2144) --- proposals/0406-async-stream-backpressure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0406-async-stream-backpressure.md b/proposals/0406-async-stream-backpressure.md index a761080ce7..4970ce2cdf 100644 --- a/proposals/0406-async-stream-backpressure.md +++ b/proposals/0406-async-stream-backpressure.md @@ -71,7 +71,7 @@ creating multiple iterators and iterating over them separately, may produce an unexpected series of values. While that statement leaves room for any behavior we learned that a clear distinction -of behavior for root asynchronous sequences is benficial; especially, when it comes to +of behavior for root asynchronous sequences is benificial; especially, when it comes to how transformation algorithms are applied on top. ### Downstream consumer termination From 12c38e8955b61d59da106c1c21fb57a6155b8848 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 5 Sep 2023 15:07:37 -0400 Subject: [PATCH 3295/4563] Accept SE-0405 with modifications. --- proposals/0405-string-validating-initializers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0405-string-validating-initializers.md b/proposals/0405-string-validating-initializers.md index 8d57f61d40..a09adbbdf7 100644 --- a/proposals/0405-string-validating-initializers.md +++ b/proposals/0405-string-validating-initializers.md @@ -3,10 +3,10 @@ * Proposal: [SE-0405](https://github.com/apple/swift-evolution/blob/main/proposals/0405-string-validating-initializers.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Active Review (August 8–22, 2023)** +* Status: **Accepted** * Bugs: rdar://99276048, rdar://99832858 * Implementation: [Staged package](https://github.com/apple/swift-evolution-staging/tree/input-validating-string-initializers) -* Review: ([pitch](https://forums.swift.org/t/66206)), ([review](https://forums.swift.org/t/se-0405-string-initializers-with-encoding-validation/66655)) +* Review: ([pitch](https://forums.swift.org/t/66206)), ([review](https://forums.swift.org/t/se-0405-string-initializers-with-encoding-validation/66655)), ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0405-string-initializers-with-encoding-validation/67134)) * Previous Revision: ([0](https://gist.github.com/glessard/d1ed79b7968b4ad2115462b3d1eba805)) ## Introduction From 06468904b9e247a568eb403de22d21484a30511b Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 6 Sep 2023 09:30:52 -0700 Subject: [PATCH 3296/4563] Update 0395 as implemented (#2149) * Update 0395-observability.md * Add review threads to 0395-observability.md * Fix links --- proposals/0395-observability.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0395-observability.md b/proposals/0395-observability.md index 35249ae2f5..9a03397eee 100644 --- a/proposals/0395-observability.md +++ b/proposals/0395-observability.md @@ -3,7 +3,8 @@ * Proposal: [SE-0395](0395-observability.md) * Authors: [Philippe Hausler](https://github.com/phausler), [Nate Cook](https://github.com/natecook1000) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (May 30 – June 12, 2023)** +* Status: **Implemented (Swift 5.9)** +* Review: [(pitch)](https://forums.swift.org/t/pitch-observation-revised/63757) [(first review)](https://forums.swift.org/t/se-0395-observability/64342/) [(second review)](https://forums.swift.org/t/second-review-se-0395-observability/65261/) [(acceptance)](https://forums.swift.org/t/accepted-with-revision-se-0395-observability/66760) #### Changes From ed0d31ffec7a91d6c709f983c313820639296cae Mon Sep 17 00:00:00 2001 From: Sima Nerush <51447912+simanerush@users.noreply.github.com> Date: Wed, 6 Sep 2023 09:42:15 -0700 Subject: [PATCH 3297/4563] [Proposal] Enable pack iteration. (#2108) * [Proposal] Enable pack iteration. * Update 0404-pack-iteration.md * Update 0404-pack-iteration.md * Update proposals/0404-pack-iteration.md Co-authored-by: Holly Borla * Adjust the wording --------- Co-authored-by: Holly Borla --- proposals/0404-pack-iteration.md | 202 +++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 proposals/0404-pack-iteration.md diff --git a/proposals/0404-pack-iteration.md b/proposals/0404-pack-iteration.md new file mode 100644 index 0000000000..27c0bdcf7a --- /dev/null +++ b/proposals/0404-pack-iteration.md @@ -0,0 +1,202 @@ +# Pack Iteration + +* Proposal: [SE-0408](0408-pack-iteration.md) +* Authors: [Sima Nerush](https://github.com/simanerush), [Holly Borla](https://github.com/hborla) +* Review Manager: TBD +* Status: **Implementation In Progress** +* Implementation: [apple/swift#67594](https://github.com/apple/swift/pull/67594) (gated behind flag `-enable-experimental-feature PackIteration`) +* Review: ([pitch](https://forums.swift.org/t/pitch-enable-pack-iteration/66168)) + +## Introduction + +Building upon the Value and Type Parameter Packs proposal [SE-0393](https://forums.swift.org/t/se-0393-value-and-type-parameter-packs/63859), this proposal enables iterating over each element in a value pack and bind each value to a local variable using a `for-in` syntax. + + +## Motivation + +Currently, it is possible to express list operations on value packs using pack expansion expressions. This approach requires putting code involving statements into a function or closure. For example, limiting repetition patterns to expressions does not allow for short-circuiting with `break` or `continue` statements, so the pattern expression will always be evaluated once for every element in the pack. The only way to stop evaluation would be to mark the function/closure containing the pattern expression throwing, and catch the error in a do/catch block to return, which is unnatural for Swift users. + +The following implementation of `==` over tuples of arbitrary length demonstrates these workarounds: + +```swift +struct NotEqual: Error {} + +func == (lhs: (repeat each Element), rhs: (repeat each Element)) -> Bool { + // Local throwing function for operating over each element of a pack expansion. + func isEqual(_ left: T, _ right: T) throws { + if (left == right) + return + + throw NotEqual() + } + + // Do-catch statement for short-circuiting as soon as two tuple elements are not equal. + do { + repeat isEqual(each lhs, each rhs) + } catch { + return false + } + + return true +} +``` + +Here, the programmer can only return `false` when the `NotEqual` error was thrown. The `isEqual` function performs the comparison and throws if the sides are not equal. + +## Proposed Solution + +We propose allowing iteration over value packs using `for-in` loops. With the adoption of pack iteration, the implementation of the standard library methods like `==` operator for tuples of any number of elements will become straightforward. Instead of throwing a `NotEqual` error, the function can simply iterate over each respective element of the tuple and `return false` in the body of the loop if the elements are not equal: + +```swift +func == (lhs: (repeat each Element), rhs: (repeat each Element)) -> Bool { + + for (left, right) in repeat (each lhs, each rhs) { + guard left == right else { return false } + } + return true +} +``` + +The above code iterates pairwise over two tuples `lhs` and `rhs` using a `for-in` loop syntax. At each iteration, the pack elements of `lhs` and `rhs` are bound to the local variables `left` and `right`, respectively. Because `lhs` and `rhs` have the same type, so do `left` and `right`, and the `Equatable` requirement allows comparing `left` and `right` with `==`. + +## Detailed Design + +In addition to expressions that conform to `Sequence`, the source of a `for-in` loop can be a pack expansion expression. + +```swift +func iterate(over element: repeat each Element) { + for element in repeat each element { + + } +} +``` + +On the *i*th iteration, the type of `element` is the *i*th type parameter in the `Element` type parameter pack, and the value of `element` is the *i*th value parameter in the shadowed `element` value parameter pack. Conceptually, the type of `element` is the pattern type of `repeat each element` with each captured type parameter pack replaced with an implicit scalar type parameter with matching requirements. In this case, the pattern type is `each Element`, so the type of `element` is a scalar type parameter with no requirements. Let’s call the scalar type parameter `Element'`. For example, if `iterate` is called with `each Element` bound to the type pack `{Int, String, Bool}` the body of the `for-in` loop will first substitute `Element'` for `Int`, then `String`, then `Bool`. + +If the type parameter packs captured by the pack expansion pattern contain requirements, the scalar type parameter in the loop body will have the same requirements: + +```swift +struct Generic {} + +protocol P {} + +func iterate() { + for x in repeat Generic() { + // the type of 'x' is Generic + } +} +``` + +In the above code, the pattern type of the pack expansion is `Generic` where `each Element: P`, so the type of `x` is a scalar type parameter ` where Element': P`. + +Like regular `for-in` loops, `for-in` loops over pack expansions can pattern match over each element of the value pack: + +```swift +enum E { + case one(T) + case two +} + +func iterate(over element: repeat E) { + for case .one(let value) in repeat each element { + // 'value' has type Element' + } +} +``` +The pattern expression in the source of a `for-in repeat` loop is evaluated once at each iteration, instead of `n` times eagerly where `n` is the length of the packs captured by the pattern. If `p_i` is the pattern expression at the `i`th iteration and control flow exits the loop at iteration `i`, then `p_j` is not evaluated for `i < j < n`. For example: + +```swift +func printAndReturn(_ value: Value) -> Value { + print("Evaluated pack element value \(value)") + return value +} + +func iterate(_ t: repeat each T) { + var i = 0 + for value in repeat printAndReturn(each t) { + print("Evaluating loop iteration \(i)") + if i == 1 { + break + } else { + i += 1 + } + } + + print("Done iterating") +} + +iterate(1, "hello", true) +``` + +The above code has the following output + +``` +Evaluated pack element value 1 +Evaluating loop iteration 0 +Evaluated pack element value "hello" +Evaluating loop iteration 1 +Done iterating +``` + +## Source Compatibility + +There is no source compatibility impact, since this is an additive change. + + +## ABI Compatibility + +This proposal does not affect ABI, since its impact is only in expressions. + + +## Implications on adoption + +This feature can be freely adopted and un-adopted in source code with no deployment constraints and without affecting source or ABI compatibility. + +## Alternatives Considered + +The only alternative to allowing pack iteration would be placing code with statements into functions/closures, however, that approach is unnatural and over-complicated. For example, this is a version of the `==` operator for tuples implementation mentioned earlier. + +## Future directions + +### Enabling `guard let` + +Another familiar pattern to Swift programmers is `guard let`. + +For example, consider the `zip` function from the standard library. `guard let` can be used for enabling this function to support any number of sequences, instead of just 2: + +```swift +public func zip(_ sequences: repeat each S) -> ZipSequence { + .init(sequences: repeat each sequences) +} + +public struct ZipSequence { + let sequences: (repeat each S) +} + +extension ZipSequence: Sequence { + public typealias Element = (repeat (each S).Element) + + public struct Iterator: IteratorProtocol { + var iterators: (repeat (each S).Iterator) + var reachedEnd = false + + public mutating func next() -> Element? { + if reachedEnd { + return nil + } + + // Using guard let for checking that the next element is not nil. + guard let element = repeat (each iterators).next() else { + return nil + } + + return (repeat each element) + } + } + + public func makeIterator() -> Iterator { + return Iterator(iter: (repeat (each sequences).makeIterator())) + } +} +``` + From 466aa37859aaa9f358f6ec8d0a63c1c9049d3fbd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 6 Sep 2023 09:53:11 -0700 Subject: [PATCH 3298/4563] Initiate review of SE-0408 "Pack Iteration" (#2151) --- proposals/{0404-pack-iteration.md => 0408-pack-iteration.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{0404-pack-iteration.md => 0408-pack-iteration.md} (98%) diff --git a/proposals/0404-pack-iteration.md b/proposals/0408-pack-iteration.md similarity index 98% rename from proposals/0404-pack-iteration.md rename to proposals/0408-pack-iteration.md index 27c0bdcf7a..42b976d0b0 100644 --- a/proposals/0404-pack-iteration.md +++ b/proposals/0408-pack-iteration.md @@ -2,8 +2,8 @@ * Proposal: [SE-0408](0408-pack-iteration.md) * Authors: [Sima Nerush](https://github.com/simanerush), [Holly Borla](https://github.com/hborla) -* Review Manager: TBD -* Status: **Implementation In Progress** +* Review Manager: [Doug Gregor](https://github.com/DougGregor/) +* Status: **Active Review (September 6...19, 2023)** * Implementation: [apple/swift#67594](https://github.com/apple/swift/pull/67594) (gated behind flag `-enable-experimental-feature PackIteration`) * Review: ([pitch](https://forums.swift.org/t/pitch-enable-pack-iteration/66168)) From 6fea4962167a6141cf1c233ebad466ad701b9b65 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 6 Sep 2023 11:09:25 -0700 Subject: [PATCH 3299/4563] [SE-0408] Add link to review thread (#2152) --- proposals/0408-pack-iteration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0408-pack-iteration.md b/proposals/0408-pack-iteration.md index 42b976d0b0..47f952db28 100644 --- a/proposals/0408-pack-iteration.md +++ b/proposals/0408-pack-iteration.md @@ -5,7 +5,7 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor/) * Status: **Active Review (September 6...19, 2023)** * Implementation: [apple/swift#67594](https://github.com/apple/swift/pull/67594) (gated behind flag `-enable-experimental-feature PackIteration`) -* Review: ([pitch](https://forums.swift.org/t/pitch-enable-pack-iteration/66168)) +* Review: ([pitch](https://forums.swift.org/t/pitch-enable-pack-iteration/66168), [review](https://forums.swift.org/t/review-se-0408-pack-iteration/67152)) ## Introduction From 2c0657bdf17e77acd9a5568abd4286e4c3babd34 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 5 Sep 2023 17:03:25 -0700 Subject: [PATCH 3300/4563] [SE-0405] update proposal to match LSG acceptance --- .../0405-string-validating-initializers.md | 112 +++++++----------- 1 file changed, 43 insertions(+), 69 deletions(-) diff --git a/proposals/0405-string-validating-initializers.md b/proposals/0405-string-validating-initializers.md index a09adbbdf7..c2a101060a 100644 --- a/proposals/0405-string-validating-initializers.md +++ b/proposals/0405-string-validating-initializers.md @@ -7,7 +7,7 @@ * Bugs: rdar://99276048, rdar://99832858 * Implementation: [Staged package](https://github.com/apple/swift-evolution-staging/tree/input-validating-string-initializers) * Review: ([pitch](https://forums.swift.org/t/66206)), ([review](https://forums.swift.org/t/se-0405-string-initializers-with-encoding-validation/66655)), ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0405-string-initializers-with-encoding-validation/67134)) -* Previous Revision: ([0](https://gist.github.com/glessard/d1ed79b7968b4ad2115462b3d1eba805)) +* Previous Revisions: [0](https://gist.github.com/glessard/d1ed79b7968b4ad2115462b3d1eba805), [1](https://github.com/apple/swift-evolution/blob/37531427931a57ff2a76225741c99de8fa8b8c59/proposals/0405-string-validating-initializers.md) ## Introduction @@ -26,42 +26,36 @@ We will add a new `String` initializer that can fail, returning `nil`, when its ```swift extension String { public init?( - validating codeUnits: some Sequence, as: Encoding.Type + validating codeUnits: some Sequence, + as: Encoding.Type ) } ``` -For convenience and discoverability for the most common case, we will also provide an initializer that specifies the UTF-8 input encoding as part of its argument label: +When processing data obtained from C, it is frequently the case that UTF-8 data is represented by `Int8` (typically as `CChar`) rather than `UInt8`. We will provide a convenience initializer for this use case: ```swift extension String { - public init?(validatingAsUTF8 codeUnits: some Sequence) -} -``` - -This will construct a new `String`, returning `nil` when the input is found invalid according to the UTF-8 encoding. - -When processing data obtained from C, it is frequently the case that UTF-8 data is represented by `CChar` rather than `UInt8`. We will provide a convenience initializer for this use case. Noting that this situation typically involves contiguous memory, we believe it will be well-served by explicitly using an abstraction for contiguous memory (`UnsafeBufferPointer`): - -```swift -extension String { - public init?(validatingAsUTF8 codeUnits: UnsafeBufferPointer) + public init?( + validating codeUnits: some Sequence, + as: Encoding.Type + ) where Encoding.CodeUnit == UInt8 } ``` -The `String.init(validatingAsUTF8:)` functions convert their whole input, including any embedded `\0` code units. - -`String` already features a validating initializer for UTF-8 input, though it is intended for C interoperability. Its argument label does not convey the expectation that its input is a null-terminated C string, and this has caused errors. We propose to change the labels in order to clarify the preconditions: +`String` already features a validating initializer for UTF-8 input, intended for C interoperability. Its argument label does not convey the expectation that its input is a null-terminated C string, and this has caused errors. We propose to change the labels in order to clarify the preconditions: ```swift extension String { public init?(validatingCString nullTerminatedUTF8: UnsafePointer) - @available(Swift 5.XLIX, deprecated, renamed:"String.init(validatingCString:)") + @available(Swift 5.XLIX, deprecated, renamed: "String.init(validatingCString:)") public init?(validatingUTF8 cString: UnsafePointer) } ``` +Note that unlike `String.init?(validatingCString:)`, the `String.init?(validating:as:)` initializers convert their whole input, including any embedded `\0` code units. + ## Detailed Design We want these new initializers to be performant. As such, their implementation should minimize the number of memory allocations and copies required. We achieve this performance with `@inlinable` implementations that leverage `withContiguousStorageIfAvailable` to provide a concrete (`internal`) code path for the validation cases. The concrete `internal` initializer itself calls a number of functions internal to the standard library. @@ -78,7 +72,7 @@ extension String { /// different arrays---first with a well-formed UTF-8 code unit sequence and /// then with an ill-formed UTF-16 code unit sequence. /// - /// let validUTF8: [UInt8] = [67, 97, 102, 195, 169] + /// let validUTF8: [UInt8] = [67, 97, 0, 102, 195, 169] /// let valid = String(validating: validUTF8, as: UTF8.self) /// print(valid) /// // Prints "Optional("Café")" @@ -90,75 +84,47 @@ extension String { /// /// - Parameters /// - codeUnits: A sequence of code units that encode a `String` - /// - encoding: An implementation of `Unicode.Encoding` that should be used + /// - encoding: A conformer to `Unicode.Encoding` to be used /// to decode `codeUnits`. @inlinable public init?( - validating codeUnits: some Sequence, as: Encoding.Type + validating codeUnits: some Sequence, + as: Encoding.Type ) where Encoding: Unicode.Encoding -} -``` -```swift -extension String { /// Create a new `String` by copying and validating the sequence of - /// UTF-8 code units passed in. + /// `Int8` passed in, according to the specified encoding. /// /// This initializer does not try to repair ill-formed code unit sequences. /// If any are found, the result of the initializer is `nil`. /// /// The following example calls this initializer with the contents of two /// different arrays---first with a well-formed UTF-8 code unit sequence and - /// then with an ill-formed code unit sequence. - /// - /// let validUTF8: [UInt8] = [67, 97, 102, 195, 169] - /// let valid = String(validatingAsUTF8: validUTF8) - /// print(valid) - /// // Prints "Optional("Café")" + /// then with an ill-formed ASCII code unit sequence. /// - /// let invalidUTF8: [UInt8] = [67, 195, 0] - /// let invalid = String(validatingAsUTF8: invalidUTF8) - /// print(invalid) - /// // Prints "nil" - /// - /// Note: This initializer is functionally equivalent to using - /// `String(validating: some Sequence, as: UTF8.self)`. - /// - /// - Parameters - /// - codeUnits: A sequence of code units that encode a `String` - public init?(validatingAsUTF8 codeUnits: some Sequence) -} -``` - -```swift -extension String { - /// Create a new `String` by copying and validating the sequence of `CChar` - /// passed in, by interpreting them as UTF-8 code units. - /// - /// This initializer does not try to repair ill-formed code unit sequences. - /// If any are found, the result of the initializer is `nil`. - /// - /// The following example calls this initializer with the contents of two - /// different `CChar` arrays---first with a well-formed UTF-8 - /// code unit sequence and then with an ill-formed code unit sequence. - /// - /// let validUTF8: [CChar] = [67, 97, 0, 102, -61, -87] + /// let validUTF8: [Int8] = [67, 97, 0, 102, -61, -87] /// validUTF8.withUnsafeBufferPointer { - /// let s = String(validatingAsUTF8: $0) + /// let s = String(validating: $0, as: UTF8.self) /// print(s) /// } /// // Prints "Optional("Café")" /// - /// let invalidUTF8: [CChar] = [67, -61, 0] + /// let invalidASCII: [Int8] = [67, 97, -5] /// invalidUTF8.withUnsafeBufferPointer { - /// let s = String(validatingAsUTF8: $0) + /// let s = String(validating: $0, as) /// print(s) /// } /// // Prints "nil" /// /// - Parameters /// - codeUnits: A sequence of code units that encode a `String` - public init?(validatingAsUTF8 codeUnits: UnsafeBufferPointer) + /// - encoding: A conformer to `Unicode.Encoding` that can decode + /// `codeUnits` as `UInt8` + @inlinable + public init?( + validating codeUnits: some Sequence, + as: Encoding.Type + ) where Encoding: Unicode.Encoding, Encoding.CodeUnit == UInt8 } ``` @@ -193,7 +159,7 @@ extension String { @_silgen_name("sSS14validatingUTF8SSSgSPys4Int8VG_tcfC") public init?(validatingCString nullTerminatedCodeUnits: UnsafePointer) - @available(Swift 5.XLIX, deprecated, renamed:"String.init(validatingCString:)") + @available(Swift 5.XLIX, deprecated, renamed: "String.init(validatingCString:)") @_silgen_name("_swift_stdlib_legacy_String_validatingUTF8") @_alwaysEmitIntoClient public init?(validatingUTF8 cString: UnsafePointer) @@ -204,7 +170,7 @@ extension String { This proposal consists mostly of additions, which are by definition source compatible. -The proposal includes the renaming of one function from `String.init?(validatingUTF8:)` to `String.init?(validatingCString:)`. The existing function name will be deprecated, producing a warning. A fixit will support an easy transition to the renamed version of the function. +The proposal includes the renaming of one function from `String.init?(validatingUTF8:)` to `String.init?(validatingCString:)`. The existing function name will be deprecated, producing a warning. A fixit will support an easy transition to the renamed version of the function. ## ABI Compatibility @@ -218,13 +184,21 @@ This feature requires a new version of the standard library. ## Alternatives considered -#### The `validatingAsUTF8` argument label +#### Initializers specifying the encoding by their argument label + +For convenience and discoverability for the most common case, we originally proposed an initializer that specifies the UTF-8 input encoding as part of its argument label: + +```swift +extension String { + public init?(validatingAsUTF8 codeUnits: some Sequence) +} +``` -The argument label `validatingUTF8` seems like it may have been preferable to `validatingAsUTF8`, but using the former would have been source-breaking. The C string validation initializer takes an `UnsafePointer`, but it can also accept `[UInt8]` via implicit pointer conversion. Any use site that passes an `[UInt8]` to the C string validation initializer would have changed behaviour upon recompilation, from considering a null character (`\0`) as the termination of the C string to considering it as a valid, non-terminating character. +Reviewers and the Language Steering Group believed that this initializer does not carry its weight, and that the discoverability issues it sought to alleviate would best be solved by improved tooling. -#### Have the `CChar`-validating function take a parameter of type `some Sequence` +#### Have `String.init?(validating: some Sequence)` take a parameter typed as `some Sequence`, or as a specific `Collection` of `CChar` -This would produce a compile-time ambiguity on platforms where `CChar` is typealiased to `UInt8` rather than `Int8`. Using `UnsafeBufferPointer` as the parameter type will avoid such a compile-time ambiguity. +Defining this validating initializer in terms of `some Sequence` would produce a compile-time ambiguity on platforms where `CChar` is typealiased to `UInt8` rather than `Int8`. The reviewed proposal suggested defining it in terms of `UnsafeBufferPointer`, since this parameter type would avoid such a compile-time ambiguity. The actual root of the problem is that `CChar` is a typealias instead of a separate type. Given this, discussions during the review period and by the Language Steering Group led to this initializer to be re-defined using `some Sequence`. This solves the `CChar`-vs-`UInt8` interoperability issue at source-code level, and preserves as much flexibility as possible without ambiguities. ## Future directions From c9add125999e28bb4ba0fd6625ffdaab25495368 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 6 Sep 2023 17:51:19 -0700 Subject: [PATCH 3301/4563] [SE-0405] fix code example --- proposals/0405-string-validating-initializers.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/proposals/0405-string-validating-initializers.md b/proposals/0405-string-validating-initializers.md index c2a101060a..31d683c1c5 100644 --- a/proposals/0405-string-validating-initializers.md +++ b/proposals/0405-string-validating-initializers.md @@ -103,17 +103,13 @@ extension String { /// then with an ill-formed ASCII code unit sequence. /// /// let validUTF8: [Int8] = [67, 97, 0, 102, -61, -87] - /// validUTF8.withUnsafeBufferPointer { - /// let s = String(validating: $0, as: UTF8.self) - /// print(s) - /// } + /// let valid = String(validating: $0, as: UTF8.self) + /// print(valid) /// // Prints "Optional("Café")" /// /// let invalidASCII: [Int8] = [67, 97, -5] - /// invalidUTF8.withUnsafeBufferPointer { - /// let s = String(validating: $0, as) - /// print(s) - /// } + /// let invalid = String(validating: $0, as: Unicode.ASCII.self) + /// print(invalid) /// // Prints "nil" /// /// - Parameters From c6931ca5b7c49add88f27a278d77776531af5acf Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Wed, 6 Sep 2023 22:58:46 -0400 Subject: [PATCH 3302/4563] Review --- proposals/0403-swiftpm-mixed-language-targets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 3fff5905aa..76e195c572 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -140,7 +140,7 @@ types within the module. It will not expose any non-public C language types. #### Importing within an **C/Objective-C/C++** context How a mixed target, `MixedPackage`, is imported into an **C/Objective-C/C++** -file will vary dependong on the language it is being imported in. +file will vary depending on the language it is being imported in. When Clang modules are supported, clients can import the module. Textual imports are also an option. From d6f59a4c8c7f55662e03f5bd48d091f3ca1eabee Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 8 Sep 2023 04:47:42 +0100 Subject: [PATCH 3303/4563] Update 0405-string-validating-initializers.md --- .../0405-string-validating-initializers.md | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/proposals/0405-string-validating-initializers.md b/proposals/0405-string-validating-initializers.md index 31d683c1c5..86b4ddda0e 100644 --- a/proposals/0405-string-validating-initializers.md +++ b/proposals/0405-string-validating-initializers.md @@ -1,6 +1,6 @@ # String Initializers with Encoding Validation -* Proposal: [SE-0405](https://github.com/apple/swift-evolution/blob/main/proposals/0405-string-validating-initializers.md) +* Proposal: [SE-0405](0405-string-validating-initializers.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Accepted** @@ -62,7 +62,7 @@ We want these new initializers to be performant. As such, their implementation s ```swift extension String { - /// Create a new `String` by copying and validating the sequence of + /// Creates a new `String` by copying and validating the sequence of /// code units passed in, according to the specified encoding. /// /// This initializer does not try to repair ill-formed code unit sequences. @@ -82,17 +82,17 @@ extension String { /// print(invalid) /// // Prints "nil" /// - /// - Parameters + /// - Parameters: /// - codeUnits: A sequence of code units that encode a `String` /// - encoding: A conformer to `Unicode.Encoding` to be used /// to decode `codeUnits`. @inlinable public init?( validating codeUnits: some Sequence, - as: Encoding.Type + as encoding: Encoding.Type ) where Encoding: Unicode.Encoding - /// Create a new `String` by copying and validating the sequence of + /// Creates a new `String` by copying and validating the sequence of /// `Int8` passed in, according to the specified encoding. /// /// This initializer does not try to repair ill-formed code unit sequences. @@ -103,30 +103,30 @@ extension String { /// then with an ill-formed ASCII code unit sequence. /// /// let validUTF8: [Int8] = [67, 97, 0, 102, -61, -87] - /// let valid = String(validating: $0, as: UTF8.self) + /// let valid = String(validating: validUTF8, as: UTF8.self) /// print(valid) /// // Prints "Optional("Café")" /// /// let invalidASCII: [Int8] = [67, 97, -5] - /// let invalid = String(validating: $0, as: Unicode.ASCII.self) + /// let invalid = String(validating: invalidASCII, as: Unicode.ASCII.self) /// print(invalid) /// // Prints "nil" /// - /// - Parameters + /// - Parameters: /// - codeUnits: A sequence of code units that encode a `String` /// - encoding: A conformer to `Unicode.Encoding` that can decode /// `codeUnits` as `UInt8` @inlinable public init?( validating codeUnits: some Sequence, - as: Encoding.Type + as encoding: Encoding.Type ) where Encoding: Unicode.Encoding, Encoding.CodeUnit == UInt8 } ``` ```swift extension String { - /// Create a new string by copying and validating the null-terminated UTF-8 + /// Creates a new string by copying and validating the null-terminated UTF-8 /// data referenced by the given pointer. /// /// This initializer does not try to repair ill-formed UTF-8 code unit @@ -139,23 +139,23 @@ extension String { /// /// let validUTF8: [CChar] = [67, 97, 102, -61, -87, 0] /// validUTF8.withUnsafeBufferPointer { ptr in - /// let s = String(validatingUTF8: ptr.baseAddress!) + /// let s = String(validatingCString: ptr.baseAddress!) /// print(s) /// } /// // Prints "Optional("Café")" /// /// let invalidUTF8: [CChar] = [67, 97, 102, -61, 0] /// invalidUTF8.withUnsafeBufferPointer { ptr in - /// let s = String(validatingUTF8: ptr.baseAddress!) + /// let s = String(validatingCString: ptr.baseAddress!) /// print(s) /// } /// // Prints "nil" /// - /// - Parameter cString: A pointer to a null-terminated UTF-8 code sequence. + /// - Parameter nullTerminatedUTF8: A pointer to a null-terminated UTF-8 code sequence. @_silgen_name("sSS14validatingUTF8SSSgSPys4Int8VG_tcfC") - public init?(validatingCString nullTerminatedCodeUnits: UnsafePointer) + public init?(validatingCString nullTerminatedUTF8: UnsafePointer) - @available(Swift 5.XLIX, deprecated, renamed: "String.init(validatingCString:)") + @available(*, deprecated, renamed: "String.init(validatingCString:)") @_silgen_name("_swift_stdlib_legacy_String_validatingUTF8") @_alwaysEmitIntoClient public init?(validatingUTF8 cString: UnsafePointer) From 0e5bcf01b64b9abd4cfeaab6a98c54ff9189acce Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Sun, 10 Sep 2023 11:18:39 -0700 Subject: [PATCH 3304/4563] [SE-0408] Fix syntax error --- proposals/0408-pack-iteration.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0408-pack-iteration.md b/proposals/0408-pack-iteration.md index 47f952db28..55635a73eb 100644 --- a/proposals/0408-pack-iteration.md +++ b/proposals/0408-pack-iteration.md @@ -24,9 +24,10 @@ struct NotEqual: Error {} func == (lhs: (repeat each Element), rhs: (repeat each Element)) -> Bool { // Local throwing function for operating over each element of a pack expansion. func isEqual(_ left: T, _ right: T) throws { - if (left == right) + if left == right { return - + } + throw NotEqual() } From 79a17c9a00cfc5a02bb99ac51e02198a41a7e7cb Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 11 Sep 2023 17:26:47 -0400 Subject: [PATCH 3305/4563] Mark SE-0386 as implemented in Swift 5.9 --- proposals/0386-package-access-modifier.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0386-package-access-modifier.md b/proposals/0386-package-access-modifier.md index bac6483e0a..3376ac883d 100644 --- a/proposals/0386-package-access-modifier.md +++ b/proposals/0386-package-access-modifier.md @@ -3,7 +3,7 @@ * Proposal: [SE-0386](0386-package-access-modifier.md) * Authors: [Ellie Shin](https://github.com/elsh), [Alexis Laferriere](https://github.com/xymus) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: [apple/swift#61546](https://github.com/apple/swift/pull/62700), [apple/swift#62704](https://github.com/apple/swift/pull/62704), [apple/swift#62652](https://github.com/apple/swift/pull/62652), [apple/swift#62652](https://github.com/apple/swift/pull/62652) * Review: ([pitch](https://forums.swift.org/t/new-access-modifier-package/61459)) ([first review](https://forums.swift.org/t/se-0386-package-access-modifier/62808)) ([second review](https://forums.swift.org/t/second-review-se-0386-package-access-modifier/64086)) ([acceptance](https://forums.swift.org/t/accepted-se-0386-package-access-modifier/64904)) * Previous Revision: [1](https://github.com/apple/swift-evolution/blob/28fd2fb9b7258117f912cec5e5f7eb178520fbf2/proposals/NNNN-package-access-modifier.md), [2](https://github.com/apple/swift-evolution/blob/32e51946296f67be79a58a8c23eb9d7460a06232/proposals/0386-package-access-modifier.md), [3](https://github.com/apple/swift-evolution/blob/4a3a11b18037526cf8d83a9d10b22b94890727e8/proposals/0386-package-access-modifier.md) From 8a2975cfbdc9c822334040d2a86c150c9698a50a Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Tue, 12 Sep 2023 14:00:28 -0500 Subject: [PATCH 3306/4563] Update 0397-freestanding-declaration-macros.md The required method for `DeclarationMacro` conformance isn't actually `async`. --- proposals/0397-freestanding-declaration-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index b2cb5b4712..c0d3352011 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -41,7 +41,7 @@ public protocol DeclarationMacro: FreestandingMacro { static func expansion( of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext - ) async throws -> [DeclSyntax] + ) throws -> [DeclSyntax] } ``` From e5b6b49c31f8e7a5eee6e261bdc13fdc37ad56f8 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:19:03 -0400 Subject: [PATCH 3307/4563] Return SE-0406 for revision (#2161) ...and correct an incorrectly corrected typo. --- proposals/0406-async-stream-backpressure.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0406-async-stream-backpressure.md b/proposals/0406-async-stream-backpressure.md index 4970ce2cdf..2df9bb7748 100644 --- a/proposals/0406-async-stream-backpressure.md +++ b/proposals/0406-async-stream-backpressure.md @@ -3,9 +3,9 @@ * Proposal: [SE-0406](0406-async-stream-backpressure.md) * Author: [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (August 15...28, 2023)** +* Status: **Returned for revision** * Implementation: [apple/swift#66488](https://github.com/apple/swift/pull/66488) -* Review: ([pitch](https://forums.swift.org/t/pitch-new-apis-for-async-throwing-stream-with-backpressure-support/65449)) ([review](https://forums.swift.org/t/se-0406-backpressure-support-for-asyncstream/66771)) +* Review: ([pitch](https://forums.swift.org/t/pitch-new-apis-for-async-throwing-stream-with-backpressure-support/65449)) ([review](https://forums.swift.org/t/se-0406-backpressure-support-for-asyncstream/66771)) ([return for revision](https://forums.swift.org/t/returned-for-revision-se-0406-backpressure-support-for-asyncstream/67248)) ## Introduction @@ -71,7 +71,7 @@ creating multiple iterators and iterating over them separately, may produce an unexpected series of values. While that statement leaves room for any behavior we learned that a clear distinction -of behavior for root asynchronous sequences is benificial; especially, when it comes to +of behavior for root asynchronous sequences is beneficial; especially, when it comes to how transformation algorithms are applied on top. ### Downstream consumer termination From 23647251b1b957f0e5e2a8268ace2f5f4b877659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 14 Sep 2023 18:14:35 -0700 Subject: [PATCH 3308/4563] Access-level on import statements (#2135) * Proposal for declaring an access-level on import statements * Assign review manager * Apply simple language changes Co-authored-by: Frederick Kellison-Linn * Update different spellings for "access level" vs "access-level" * Replace uses of keywords with modifiers * Improve the motivation section * Move the default import access-level code sample to the section about it * Clarify when we talk about a declaration signature * List only the basic implementation PR and the experimental flag * Clarify "how module dependencies behave" * Update more "declaration signatures" * Move "replacement to @_implementationOnly" with the other import attributes * Rephrase 'import statement' to 'import declaration' * Christen SE-0409, schedule review --------- Co-authored-by: Frederick Kellison-Linn --- proposals/0409-access-level-on-imports.md | 314 ++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 proposals/0409-access-level-on-imports.md diff --git a/proposals/0409-access-level-on-imports.md b/proposals/0409-access-level-on-imports.md new file mode 100644 index 0000000000..016c6e86ea --- /dev/null +++ b/proposals/0409-access-level-on-imports.md @@ -0,0 +1,314 @@ +# Access-level modifiers on import declarations + +* Proposal: [SE-0409](0409-access-level-on-imports.md) +* Author: [Alexis Laferrière](https://github.com/xymus) +* Review Manager: [Frederick Kellison-Linn](https://github.com/Jumhyn) +* Status: Status: **Active Review (September 14...26, 2023)** +* Implementation: On main and release/5.9 gated behind the frontend flag `-enable-experimental-feature AccessLevelOnImport` +* Upcoming Feature Flag: `InternalImports` (Enables Swift 6 behavior with imports defaulting to internal. Soon on main only.) +* Review: ([pitch](https://forums.swift.org/t/pitch-access-level-on-import-statements/66657)) + +## Introduction + +Declaring the visibility of a dependency with an access-level modifier on import declarations enables enforcing which declarations can reference the imported module. +A dependency can be marked as being visible only to the source file, module, package, or to all clients. +This brings the familiar behavior of the access level of declarations to dependencies and imported declarations. +This feature can hide implementation details from clients and helps to manage dependency creep. + +## Motivation + +Good practices guide us to separate public and internal services to avoid having external clients rely on internal details. +Swift already offers access levels with their respective modifiers to declarations and enforcement during type-checking, +but there is currently no equivalent official feature for dependencies. + +The author of a library may have a different intent for each of the library dependencies; +some are expected to be known to the library clients while other are for implementation details internal to the package, module, or source file. +Without a way to enforce the intended access level of dependencies +it is easy to make a mistake and expose a dependency of the library to the library clients by referencing it from a public declaration even if it's intended to remain an implementation detail. + +All the library dependencies being visible to the library clients also requires the compiler to do more work than necessary. +The compiler must load all of the library dependencies when building a client of the library, +even the dependencies that are not actually required to build the client. + +## Proposed solution + +The core of this proposal consists of extending the current access level logic to support declaring the existing modifiers (excluding `open`) on import declarations and +applying the access level to the imported declarations. + +Here's an example case where a module `DatabaseAdapter` is an implementation detail of the local module. +We don't want to expose it to clients so we mark the import as `internal`. +The compiler then allows references to it from internal functions but diagnoses references from the signature of public functions. +```swift +internal import DatabaseAdapter + +internal func internalFunc() -> DatabaseAdapter.Entry {...} // Ok +public func publicFunc() -> DatabaseAdapter.Entry {...} // error: function cannot be declared public because its result uses an internal type +``` + +Additionally, this proposal uses the access level declared on each import declaration in all source files composing a module to determine when clients of a library need to load the library's dependencies or when they can be skipped. +To balance source compatibility and best practices, the proposed default import has an implicit access level of public in Swift 5 and of internal in Swift 6 mode. + +## Detailed design + +In this section we discuss the three main language changes of this proposal: +accept access-level modifiers on import declarations to declare the visibility of the imported module, +apply that information when type-checking the source file, +and determine when indirect clients can skip loading transitive dependencies. +We then cover other concerns addressed by this proposal: +the different default access levels of imports in Swift 5 and Swift 6, +and the relationship with other attributes on imports. + +### Declaring the access level of an imported module + +The access level is declared in front of the import declaration using some of the +modifiers used for a declaration: `public`, `package`, `internal`, `fileprivate`, and `private`. + +A public dependency can be referenced from any declaration and will be visible to all clients. +It is declared with the `public` modifier. + +```swift +public import PublicDependency +``` + +A dependency visible only to the modules of the same package is declared with the `package` modifier. +Only the signature of `package`, `internal`, `fileprivate` and `private` declarations can reference the imported module. + +```swift +package import PackageDependency +``` + +A dependency internal to the module is declared with the `internal` modifier. +Only the signature of `internal`, `fileprivate` and `private` declarations can reference the imported module. + +```swift +internal import InternalDependency +``` + +A dependency private to this source file is declared with either the `fileprivate` or the `private` modifier. +In both cases the access is scoped to the source file declaring the import. +Only the signature of `fileprivate` and `private` declarations can reference the imported module. + +```swift +fileprivate import DependencyPrivateToThisFile +private import OtherDependencyPrivateToThisFile +``` + +The `open` access-level modifier is rejected on import declarations. + +### Type-checking references to imported modules + +Current type-checking enforces that declaration respect their respective access levels. +It reports as errors when a more visible declaration refers to a less visible declaration. +For example, it raises an error if a public function signature uses an internal type. + +This proposal extends the existing logic by using the access level on the import declaration as an upper bound to the visibility of imported declarations within the source file with the import. +For example, when type-checking a source file with an `internal import SomeModule`, +we consider all declarations imported from `SomeModule` to have an access level of `internal` in the context of the file. +In this case, type-checking will enforce that declarations imported as `internal` are only referenced from `internal` or lower declaration signatures and in regular function bodies. +They cannot appear in public declaration signatures, `@usableFromInline` declaration signatures, or inlinable code. +This will be reported by the familiar diagnostics currently applied to access-level modifiers on declarations and to inlinable code. + +We apply the same logic for `package`, `fileprivate` and `private` import declarations. +In the case of a `public` import, there is no restriction on how the imported declarations can be referenced +beyond the existing restrictions on imported `package` declarations which cannot be referenced from public declaration signatures. + +Here is an example of the approximate diagnostics produced from type-checking in a typical case with a `fileprivate` import. +```swift +fileprivate import DatabaseAdapter + +fileprivate func fileprivateFunc() -> DatabaseAdapter.Entry { ... } // Ok + +internal func internalFunc() -> DatabaseAdapter.Entry { ... } // error: function cannot be declared internal because its return uses a fileprivate type + +public func publicFunc(entry: DatabaseAdapter.Entry) { ... } // error: function cannot be declared public because its parameter uses a fileprivate type + +public func useInBody() { + DatabaseAdapter.create() // Ok +} + +@inlinable +public func useInInlinableBody() { + DatabaseAdapter.create() // error: global function 'create()' is fileprivate and cannot be referenced from an '@inlinable' function +} +``` + +### Transitive dependency loading + +When using this access level information at the module level, +if a dependency is never imported publicly and other requirements are met, +it becomes possible to hide the dependency from clients. +The clients can then be built without loading the transitive dependency. +This can speed up build times and +lift the need to distribute modules that are implementation details. + +The same dependency can be imported with different access levels by different files of a same module. +At the module level we only take into account the most permissive access level. +For example, if a dependency is imported both as `package` and `internal` from two different files, +we consider the dependency to be of a `package` visibility at the module level. + +The module level information implies different behaviors for transitive clients of the dependency. +Transitive clients are modules that indirectly import that dependency. +For example, in the following scenario, `TransitiveClient` is a transitive client +of the `IndirectDependency` via the import of `ImporterModule`. + +``` +module IndirectDependency + ↑ +module MiddleModule + ↑ +module TransitiveClient +``` + +Depending on how the indirect dependency is imported from the middle module, +the transitive client may need to load it at compile time or not. +There are four factors requiring a transitive dependency to be loaded, +if none of these apply the dependency can be hidden. + +1. Public dependencies must always be loaded by transitive clients. + +2. All dependencies of a non-resilient module must be loaded by transitive clients. + +3. Package dependencies must be loaded by transitive clients if the middle module and the transitive client are part of the same package. + This allows for the signature of package declarations to reference that dependency. + We consider two modules to be in the same package when their package name matches, + applying the same logic used for package declarations. + +4. All the dependencies must be loaded when the transitive client has a `@testable` import of the middle module. + Testable clients can use internal declarations which may rely on all levels of import visibility. + Even `private` and `fileprivate` dependencies must be loaded as they can contribute to the memory layout of the non-resilient internal types. + +In all other cases not covered by these four factors, +we consider the dependency to be hidden and it doesn't have to be loaded by transitive clients. +Note that the same dependency may still be loaded for a different import path. + +The module associated with a hidden dependency doesn't need to be distributed to clients. +However, the binary associated to the module still needs to be distributed to execute the resulting program. + +### Default import access level in Swift 5 and Swift 6 + +The access level of a default import declaration without an explicit access-level modifier depends on the language version. +We list here the implicit access levels and reasoning behind this choice. + +In Swift 5 an import is public by default. +This choice preserves source compatibility. +The only official import previously available in Swift 5 behaves like the public import proposed in this document. + +In Swift 6 mode an import is internal by default. +This will align the behavior of imports with declarations where the implicit access level is internal. +It should help limit unintentional dependency creep as marking a dependency public will require an explicit modifier. + +As a result, the following import is public in Swift 5, and internal in Swift 6 mode. +``` +import ADependency +``` + +The Swift 6 change will likely break source compatibility for libraries. +A migration tool could automatically insert the `public` modifier where required. +Where the tool is unavailable, a simple script can insert a `public` modifier in front of all imports to preserve the Swift 5 behavior. + +The upcoming feature flag `InternalImports` will enable the Swift 6 behavior even when using Swift 5. + +### Relation with other attributes on imports + +The `@_exported` attribute is a step above a `public` import +as clients see the imported module declarations is if they were part of the local module. +With this proposal, `@_exported` is accepted only on public import declarations, +both with the modifier or the default public visibility in Swift 6 mode. + +The `@testable` attribute allows the local module to reference the internal declarations of the imported module. +The current design even allows to use an imported internal or package type in a public declaration. +The access level behavior applies in the same way as a normal import, +all imported declarations have as upper-bound the access level on the import declaration. +In the case of a `@testable` import, even the imported internal declarations are affected by the bound. + +Current uses of `@_implementationOnly import` should be replaced with an internal import or lower. +In comparison, this new feature enables stricter type-checking and shows fewer superfluous warnings. +After replacing with an internal import, the transitive dependency loading requirements will remain the same for resilient modules, +but will change for non-resilient modules where transitive dependencies must always be loaded. +In all cases, updating modules relying on `@_implementationOnly` to instead use internal imports is strongly encouraged. + +## Source compatibility + +To preserve source compatibility, imports are public by default in Swift 5. +This will preserve the current behavior of imports in Swift 5. +As discussed previously, the Swift 6 behavior changes the default value and will require code changes. + +## ABI compatibility + +This proposal doesn't affect ABI compatibility, +it is a compile time change enforced by type-checking. + +## Implications on adoption + +Adopting or reverting the adoption of this feature should not affect clients if used with care. + +In the case of adoption in a non-resilient module, the change is in type-checking of the module source files only. +In this case changing the access level of different dependencies won't affect clients. + +For adoption in a resilient module, +marking an existing import as less than public will affect how clients build. +The compiler can build the clients by loading fewer transitive dependencies. +In theory, this shouldn't affect the clients but it may still lead to different compilation behaviors. + +In theory, these transitive dependencies couldn't be used by the clients, +so hiding them doesn't affect the clients. +In practice, there are leaks allowing use of extension members from transitive dependencies. +Adopting this feature may skip loading transitive dependencies and prevent those leaks, +it can break source compatibility in code relying of those behaviors. + +## Future directions + +### Hiding dependency for non-resilient modules + +Hiding dependencies on non-resilient modules would be possible in theory but requires rethinking a few restrictions in the compilation process. +The main restriction is the need of the compiler to know the memory layout of imported types, which can depend on a transitive dependencies. +Resilient modules can provide this information at run time so the transitive module isn't required at build time. +Non-resilient modules do not provide this information at run time, so the compiler must load the transitive dependencies at build time to access it. +Solutions could involve copying the required information in each modules, +or restricting further how a dependency can be referenced. +In all cases, it's a feature in itself and distinct from this proposal. + +## Alternatives considered + +### `@_implementationOnly import` + +The unofficial `@_implementationOnly` attribute offers a similar feature with both type-checking and hiding transitive dependencies. +This attribute has lead to instability and run time crashes when used from a non-resilient module or combined with an `@testable` import. +It applies a slightly different semantic than this proposal and its type-checking isn't as strict as it could be. +It relied on its own type-checking logic to report references to the implementation-only imported module from public declarations. +In contrast, this proposal uses the existing access level checking logic and semantics, +this should make it easier to learn. +Plus this proposal introduces whole new features with `package` imports and file-scoped imports with `private` and `fileprivate`. + +### Use `open import` as an official `@_exported import` + +The access-level modifier `open` remains available for use on imports as this proposal doesn't assign it a specific meaning. +It has been suggested to use it as an official `@_exported`. +That is, mark an import that is visible from all source files of the module and shown to clients as if it was part of the same module. +We usually use `@_exported` for Swift overlays to clang module +where two modules share the same name and the intention is to show them as unified to clients. + +Two main reasons keep me from incorporating this change to this proposal: + +1. A declaration marked as `open` can be overridden from outside the module. + This meaning has no relation with the behavior of `@_exported`. + The other access levels have a corresponding meaning between their use on a declaration and on an import declaration. +2. A motivation for this proposal is to hide implementation details and limit dependency creep. + Encouraging the use of `open import` or `@_exported` goes against this motivation and addresses a different set of problems. + It should be discussed in a distinct proposal with related motivations. + +### Infer the visibility of a dependency from its use in API + +By analyzing a module the compiler could determine which dependencies are used by public declarations and need to be visible to clients. +We could then automatically consider all other dependencies as internal and hide them from indirect clients if the other criteria are met. + +This approach lacks the duplication of information offered by the access-level modifier on the import declaration and the references from declaration signatures. +This duplication enables the type-checking behavior described in this proposal by +allowing the compiler to compare the intent marked on the import with the use in declaration signatures. +This check is important when the dependency is not distributed, +a change from a hidden dependency to a public dependency may break the distributed module on a dependency that is not available to third parties. + +## Acknowledgments + +Becca Royal-Gordon contributed to the design and wrote the pre-pitch of this proposal. + From 655f181fa68219674eb2e2f6096889263de107b0 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 14 Sep 2023 21:19:36 -0400 Subject: [PATCH 3309/4563] Add link to review thread (#2162) --- proposals/0409-access-level-on-imports.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0409-access-level-on-imports.md b/proposals/0409-access-level-on-imports.md index 016c6e86ea..735821c76e 100644 --- a/proposals/0409-access-level-on-imports.md +++ b/proposals/0409-access-level-on-imports.md @@ -6,7 +6,7 @@ * Status: Status: **Active Review (September 14...26, 2023)** * Implementation: On main and release/5.9 gated behind the frontend flag `-enable-experimental-feature AccessLevelOnImport` * Upcoming Feature Flag: `InternalImports` (Enables Swift 6 behavior with imports defaulting to internal. Soon on main only.) -* Review: ([pitch](https://forums.swift.org/t/pitch-access-level-on-import-statements/66657)) +* Review: ([pitch](https://forums.swift.org/t/pitch-access-level-on-import-statements/66657)) ([review](https://forums.swift.org/t/se-0409-access-level-modifiers-on-import-declarations/67290)) ## Introduction From 51525053e0379ad89af69410be9c210c42f3aa3d Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 15 Sep 2023 11:54:35 -0400 Subject: [PATCH 3310/4563] Fix Swift 6/5 typo (#2163) --- proposals/0409-access-level-on-imports.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0409-access-level-on-imports.md b/proposals/0409-access-level-on-imports.md index 735821c76e..361d2419c0 100644 --- a/proposals/0409-access-level-on-imports.md +++ b/proposals/0409-access-level-on-imports.md @@ -213,7 +213,7 @@ The upcoming feature flag `InternalImports` will enable the Swift 6 behavior eve The `@_exported` attribute is a step above a `public` import as clients see the imported module declarations is if they were part of the local module. With this proposal, `@_exported` is accepted only on public import declarations, -both with the modifier or the default public visibility in Swift 6 mode. +both with the modifier or the default public visibility in Swift 5 mode. The `@testable` attribute allows the local module to reference the internal declarations of the imported module. The current design even allows to use an imported internal or package type in a public declaration. From 630a47096d16672cdd8b77a0e6b460ed41118b3a Mon Sep 17 00:00:00 2001 From: Lawrence Gimenez Date: Sat, 16 Sep 2023 00:04:41 +0800 Subject: [PATCH 3311/4563] [SE-0393] - Fixed several typos (#2159) * [se-0393] Fixed typo preceeded * [se-0393] Fixed typo unwrapped * [se-0393] Fixed typo subsituted * [se-0393] Fixed typo prevlence * [se-0393] Fixed typo parmeter --- proposals/0393-parameter-packs.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0393-parameter-packs.md b/proposals/0393-parameter-packs.md index 89e5843d9d..038d3471c5 100644 --- a/proposals/0393-parameter-packs.md +++ b/proposals/0393-parameter-packs.md @@ -146,7 +146,7 @@ The substituted return type is the tuple type with two elements `(Pair() {} @@ -279,7 +279,7 @@ For example, the following substitutions both produce the element type `Int`: - Substituting `each T := {Int}` into `(repeat each T)`. - Substituting `each T := {}` into `(Int, repeat each T)`. -Though unwrapping single-element tuples complicates type matching, surfacing single-element tuples in the programming model would increase the surface area of the language. One-element tuples would need to be manually unrwapped with `.0` or pattern matching in order to make use of their contents. This unwrapping would clutter up code. +Though unwrapping single-element tuples complicates type matching, surfacing single-element tuples in the programming model would increase the surface area of the language. One-element tuples would need to be manually unwrapped with `.0` or pattern matching in order to make use of their contents. This unwrapping would clutter up code. ### Type matching @@ -623,7 +623,7 @@ The pack parameter design where packs are distinct from tuples also does not pre ### Syntax alternatives to `repeat each` -The `repeat each` syntax produces fairly verbose variadic generic code. However, the `repeat` keyword is explicit signal that the pattern is repeated under substitution, and requiring the `each` keyword for pack references indicates which types or values will be subsituted in the expansion. This syntax design helps enforce the mental model that pack expansions result in iteration over each element in the parameter pack at runtime. +The `repeat each` syntax produces fairly verbose variadic generic code. However, the `repeat` keyword is explicit signal that the pattern is repeated under substitution, and requiring the `each` keyword for pack references indicates which types or values will be substituted in the expansion. This syntax design helps enforce the mental model that pack expansions result in iteration over each element in the parameter pack at runtime. The following syntax alternatives were also considered. @@ -691,7 +691,7 @@ The downsides to postfix `*` include: #### Magic builtin `map` method -The prevlence of `map` and `zip` in Swift makes this syntax an attractive option for variadic generics: +The prevalence of `map` and `zip` in Swift makes this syntax an attractive option for variadic generics: ```swift func wrap(_ values: repeat each T) -> (repeat Wrapped) { @@ -871,7 +871,7 @@ extension (repeat each T): Equatable { Changes to the [first reviewed revision](https://github.com/apple/swift-evolution/blob/b6ca38b9eee79650dce925e7aa8443a6a9e5e6ea/proposals/0393-parameter-packs.md): * The `repeat` keyword is required for generic requirement expansions to distinguish requirement expansions from single requirements on an individual pack element nested inside of a pack expansion expression. -* Overload resolution prefers scalar overloads when the scalar overload is considered a subtype of a parmeter pack overload. +* Overload resolution prefers scalar overloads when the scalar overload is considered a subtype of a parameter pack overload. ## Acknowledgments From 12e6cb0483357530efb5f9dbd033add7afbfd8b7 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 16 Sep 2023 20:47:29 -0700 Subject: [PATCH 3312/4563] SE-0409: Fix typo in code example (#2164) --- proposals/0409-access-level-on-imports.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0409-access-level-on-imports.md b/proposals/0409-access-level-on-imports.md index 361d2419c0..4a47da59c0 100644 --- a/proposals/0409-access-level-on-imports.md +++ b/proposals/0409-access-level-on-imports.md @@ -149,7 +149,7 @@ we consider the dependency to be of a `package` visibility at the module level. The module level information implies different behaviors for transitive clients of the dependency. Transitive clients are modules that indirectly import that dependency. For example, in the following scenario, `TransitiveClient` is a transitive client -of the `IndirectDependency` via the import of `ImporterModule`. +of the `IndirectDependency` via the import of `MiddleModule`. ``` module IndirectDependency From e7dee2af80012281645f19d483034342801a186f Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 18 Sep 2023 14:40:36 -0400 Subject: [PATCH 3313/4563] Accept SE-0407 --- proposals/0407-member-macro-conformances.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0407-member-macro-conformances.md b/proposals/0407-member-macro-conformances.md index 1b50396a40..43bbb07e6d 100644 --- a/proposals/0407-member-macro-conformances.md +++ b/proposals/0407-member-macro-conformances.md @@ -3,10 +3,10 @@ * Proposal: [SE-0407](0407-member-macro-conformances.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (August 25th...September 4th, 2023)** +* Status: **Accepted** * Vision: [Macros](https://github.com/apple/swift-evolution/blob/main/visions/macros.md) * Implementation: [apple/swift#67758](https://github.com/apple/swift/pull/67758) -* Review: ([pitch](https://forums.swift.org/t/pitch-member-macros-that-know-what-conformances-are-missing/66590)) ([review](https://forums.swift.org/t/se-0407-member-macro-conformances/66951)) +* Review: ([pitch](https://forums.swift.org/t/pitch-member-macros-that-know-what-conformances-are-missing/66590)) ([review](https://forums.swift.org/t/se-0407-member-macro-conformances/66951)) ([acceptance](https://forums.swift.org/t/accepted-se-0407-member-macro-conformances/67345)) ## Introduction From d6ff403aa20546fac03d1c200f7ad7d2b6b31be9 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 18 Sep 2023 13:19:25 -0700 Subject: [PATCH 3314/4563] Pitch atomics Update NNNN-atomics.md Update NNNN-atomics.md --- proposals/NNNN-atomics.md | 1645 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1645 insertions(+) create mode 100644 proposals/NNNN-atomics.md diff --git a/proposals/NNNN-atomics.md b/proposals/NNNN-atomics.md new file mode 100644 index 0000000000..b47d801ccc --- /dev/null +++ b/proposals/NNNN-atomics.md @@ -0,0 +1,1645 @@ +# Low-Level Atomic Operations ⚛︎ + +* Proposal: [SE-NNNN](NNNN-atomics.md) +* Author: [Karoy Lorentey](https://github.com/lorentey), [Alejandro Alonso](https://github.com/Azoy) +* Review Manager: TBD +* Bug: [SR-9144](https://github.com/apple/swift/issues/51640) +* Implementation: N/A +* Version: 2023-09-18 +* Status: **Awaiting review** + + + +## Introduction + +This proposal adds a limited set of low-level atomic operations to the Standard Library, including native spellings for C++-style memory orderings. Our goal is to enable intrepid library authors and developers writing system level code to start building synchronization constructs directly in Swift. + +Previous Swift-evolution thread: [Low-Level Atomic Operations](https://forums.swift.org/t/low-level-atomic-operations/34683) + +New Swift-evolution thread: [Atomics]() + +## Revision History + +- 2020-04-13: Initial proposal version. +- 2020-06-05: Second revision. + - Removed all new APIs; the proposal is now focused solely on C interoperability. +- 2023-09-18: Third revision. + - Introduced new APIs to the standard library. + +## Table of Contents + + * [Motivation](#motivation) + * [Proposed Solution](#proposed-solution) + * [The Synchronization Module](#the-synchronization-module) + * [Atomic Memory Orderings](#atomic-memory-orderings) + * [The Atomic Protocol Hierarchy](#the-atomic-protocol-hierarchy) + * [Optional Atomics](#optional-atomics) + * [Custom Atomic Types](#custom-atomic-types) + * [DoubleWord](#doubleword) + * [The Atomic type](#the-atomic-type) + * [Basic Atomic Operations](#basic-atomic-operations) + * [Specialized Integer Operations](#specialized-integer-operations) + * [Specialized Boolean Operations](#specialized-boolean-operations) + * [Atomic Lazy References](#atomic-lazy-references) + * [Restricting Ordering Arguments to Compile\-Time Constants](#restricting-ordering-arguments-to-compile-time-constants) + * [Interaction with Existing Language Features](#interaction-with-existing-language-features) + * [Interaction with Swift Concurrency](#interaction-with-swift-concurrency) + * [Detailed Design](#detailed-design) + * [Atomic Memory Orderings](#atomic-memory-orderings-1) + * [Atomic Protocols](#atomic-protocols) + * [AtomicStorage](#atomicstorage) + * [AtomicValue](#atomicvalue) + * [DoubleWord](#doubleword-1) + * [Atomic Types](#atomic-types) + * [Atomic<Value>](#atomicvalue-1) + * [AtomicLazyReference<Instance>](#atomiclazyreferenceinstance) + * [Source Compatibility](#source-compatibility) + * [Effect on ABI Stability](#effect-on-abi-stability) + * [Effect on API Resilience](#effect-on-api-resilience) + * [Potential Future Directions](#potential-future-directions) + * [Atomic Strong References and The Problem of Memory Reclamation](#atomic-strong-references-and-the-problem-of-memory-reclamation) + * [Additional Low\-Level Atomic Features](#additional-low-level-atomic-features) + * [Alternatives Considered](#alternatives-considered) + * [Default Orderings](#default-orderings) + * [A Truly Universal Generic Atomic Type](#a-truly-universal-generic-atomic-type) + * [Providing a value Property](#providing-a-value-property) + * [Alternative Designs for Memory Orderings](#alternative-designs-for-memory-orderings) + * [Encode Orderings in Method Names](#encode-orderings-in-method-names) + * [Orderings As Generic Type Parameters](#orderings-as-generic-type-parameters) + * [Ordering Views](#ordering-views) + * [Directly bring over `swift-atomics`'s API](#directly-bring-over-swift-atomicss-api) + * [References](#references) + +## Motivation + +In Swift today, application developers use Swift's recently accepted concurrency features including async/await, structured concurrency with Task and TaskGroup, AsyncSequence/AsyncStream, etc. as well as dispatch queues and Foundation's NSLocking protocol to synchronize access to mutable state across concurrent threads of execution. + +However, for Swift to be successful as a systems programming language, it needs to also provide low-level primitives that can be used to implement such synchronization constructs (and many more!) directly within Swift. Such low-level synchronization primitives allow developers more flexible ways to synchronize access to specific properties or storage allowing them to opt their types into Swift conconcurrency by declaring their types `@unchecked Sendable`. Of course these low-level primitives also allow library authors to build more high level synchronization structures that are both easier and safer to use that developers can also utilize to synchronize memory access. + +One such low-level primitive is the concept of an atomic value, which (in the form we propose here) has two equally important roles: + +- First, atomics introduce a limited set of types whose values provide well-defined semantics for certain kinds of concurrent access. This includes explicit support for concurrent mutations -- a concept that Swift never supported before. + +- Second, atomic operations come with explicit memory ordering arguments, which provide guarantees on how/when the effects of earlier or later memory accesses become visible to other threads. Such guarantees are crucial for building higher-level synchronization abstractions. + +These new primitives are intended for people who wish to implement synchronization constructs or concurrent data structures in pure Swift code. Note that this is a hazardous area that is full of pitfalls. While a well-designed atomics facility can help simplify building such tools, the goal here is merely to make it *possible* to build them, not necessarily to make it *easy* to do so. We expect that the higher-level synchronization tools that can be built on top of these atomic primitives will provide a nicer abstraction layer. + +We want to limit this proposal to constructs that satisfy the following requirements: + +1. All atomic operations need to be explicit in Swift source, and it must be possible to easily distinguish them from regular non-atomic operations on the underlying values. + +2. The atomic type we provide must come with a lock-free implementation on every platform that implements them. (Platforms that are unable to provide lock-free implementations must not provide the affected constructs at all.) + +3. Every atomic operation must compile down to the corresponding CPU instruction (when one is available), with minimal overhead. (Ideally even if the code is compiled without optimizations.) Wait-freedom isn't a requirement -- if no direct instruction is available for an operation, then it must still be implemented, e.g. by mapping it to a compare-exchange loop. + +Following the acceptance of [Clarify the Swift memory consistency model (SE-0282)](https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md), the [swift-atomics package](https://github.com/apple/swift-atomics) was shortly created to experiment and design what a standard atomic API would look like. This proposal is relying heavily on some of the ideas that package has spent years developing and designing. + +## Proposed Solution + +We propose to introduce new low-level atomic APIs to the standard library via a new module. These atomic APIs will serve as the foundation for building higher-level concurrent code directly in Swift. + +As a quick taste, this is how atomics will work: + +```swift +import Synchronization +import Dispatch + +let counter = Atomic(0) + +DispatchQueue.concurrentPerform(iterations: 10) { _ in + for _ in 0 ..< 1_000_000 { + counter.wrappingIncrement(ordering: .relaxed) + } +} + +print(counter.load(ordering: .relaxed)) +``` + +### The Synchronization Module + +While most Swift programs won't directly use the new atomic primitives, we still consider the new constructs to be an integral part of the core Standard Library. + + * The implementation of atomic operations needs access to compiler intrinsics that are only exposed to the Standard Library. + * The memory orderings introduced here define a concurrency memory model for Swift code that has implications on the language as a whole. (Fortunately, Swift is already designed to interoperate with the C/C++ memory model, so introducing a subset of C++ memory orderings in the Standard Library doesn't by itself require language-level changes.) + +That said, it seems highly undesirable to add low-level atomics to the default namespace of every Swift program, so we propose to place the atomic constructs in a new Standard Library module called `Synchronization`. Code that needs to use low-level atomics will need to explicitly import the new module: + +```swift +import Synchronization +``` + +We expect that most Swift projects will use atomic operations only indirectly, through higher-level synchronization constructs. Therefore, importing the Synchronization module will be a relatively rare occurrence, mostly limited to projects that implement such tools. + +### Atomic Memory Orderings + +The atomic constructs later in this proposal implement concurrent read/write access by mapping to atomic instructions in the underlying architecture. All accesses of a particular atomic value get serialized into some global sequential timeline, no matter what thread executed them. + +However, this alone does not give us a way to synchronize accesses to regular variables, or between atomic accesses to different memory locations. To support such synchronization, each atomic operation can be configured to also act as a synchronization point for other variable accesses within the same thread, preventing previous accesses from getting executed after the atomic operation, and/or vice versa. Atomic operations on another thread can then synchronize with the same point, establishing a strict (although partial) timeline between accesses performed by both threads. This way, we can reason about the possible ordering of operations across threads, even if we know nothing about how those operations are implemented. (This is how locks or dispatch queues can be used to serialize the execution of arbitrary blocks containing regular accesses to shared variables.) For more details, see \[[C++17], [N2153], [Boehm 2008]]. + +In order to enable atomic synchronization within Swift, we must first introduce memory orderings who will give us control of the timeline of these operations across threads. Luckily, with the acceptance of [Clarify the Swift memory consistency model (SE-0282)](https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md), Swift already adopts the C/C++ concurrency memory model. In this model, concurrent access to shared state remains undefined behavior unless all such access is forced into a conflict-free timeline through explicit synchronization operations. + +This proposal introduces five distinct memory orderings, organized into three logical groups, from loosest to strictest: + +* `.relaxed` +* `.acquiring`, `.releasing`, `.acquiringAndReleasing` +* `.sequentiallyConsistent` + +These align with select members of the standard `std::memory_order` enumeration in C++, and are intended to carry the same semantic meaning: + +| C++ | Swift | +| :---: | :---: | +| `std::memory_order_relaxed` | `.relaxed` | +| `std::memory_order_consume` | *not yet adopted [[P0735]]* | +| `std::memory_order_acquire` | `.acquiring` | +| `std::memory_order_release` | `.releasing` | +| `std::memory_order_acq_rel` | `.acquiringAndReleasing` | +| `std::memory_order_seq_cst` | `.sequentiallyConsistent` | + +Atomic orderings are grouped into three frozen structs based on the kind of operation to which they are attached, as listed below. By modeling these as separate types, we can ensure that unsupported operation/ordering combinations (such as an atomic "releasing load") will lead to clear compile-time errors: + +```swift +/// Specifies the memory ordering semantics of an atomic load operation. +public struct AtomicLoadOrdering { + public static var relaxed: Self { get } + public static var acquiring: Self { get } + public static var sequentiallyConsistent: Self { get } +} + +/// Specifies the memory ordering semantics of an atomic store operation. +public struct AtomicStoreOrdering { + public static var relaxed: Self { get } + public static var releasing: Self { get } + public static var sequentiallyConsistent: Self { get } +} + +/// Specifies the memory ordering semantics of an atomic read-modify-write +/// operation. +public struct AtomicUpdateOrdering { + public static var relaxed: Self { get } + public static var acquiring: Self { get } + public static var releasing: Self { get } + public static var acquiringAndReleasing: Self { get } + public static var sequentiallyConsistent: Self { get } +} +``` + +These structs behave like non-frozen enums with a known (non-public) raw representation. This allows us to define additional memory orderings in the future (if and when they become necessary, specifically `std::memory_order_consume`) while making use of the known representation to optimize existing cases. (These cannot be frozen enums because that would prevent us from adding more orderings, but regular resilient enums can't freeze their representation, and the layout indirection interferes with guaranteed optimizations, especially in -Onone.) + +Every atomic operation introduced later in this proposal requires an ordering argument. We consider these ordering arguments to be an essential part of these low-level atomic APIs, and we require an explicit `ordering` argument on all atomic operations. The intention here is to force developers to carefully think about what ordering they need to use, each time they use one of these primitives. (Perhaps more importantly, this also makes it obvious to readers of the code what ordering is used -- making it far less likely that an unintended default `.sequentiallyConsistent` ordering slips through code review.) + +Projects that prefer to default to sequentially consistent ordering are welcome to add non-public `Atomic` extensions that implement that. However, we expect that providing an implicit default ordering would be highly undesirable in most production uses of atomics. + +We also provide a top-level function called `atomicMemoryFence` that allows issuing a memory ordering constraint without directly associating it with a particular atomic operation. This corresponds to `std::memory_thread_fence` in C++ [[C++17]]. + +```swift +/// Establishes a memory ordering without associating it with a +/// particular atomic operation. +/// +/// - A relaxed fence has no effect. +/// - An acquiring fence ties to any preceding atomic operation that +/// reads a value, and synchronizes with any releasing operation whose +/// value was read. +/// - A releasing fence ties to any subsequent atomic operation that +/// modifies a value, and synchronizes with any acquiring operation +/// that reads the result. +/// - An acquiring and releasing fence is a combination of an +/// acquiring and a releasing fence. +/// - A sequentially consistent fence behaves like an acquiring and +/// releasing fence, and ensures that the fence itself is part of +/// the single, total ordering for all sequentially consistent +/// operations. +/// +/// This operation corresponds to `std::atomic_thread_fence` in C++. +/// +/// Be aware that Thread Sanitizer does not support fences and may report +/// false-positive races for data protected by a fence. +public func atomicMemoryFence(ordering: AtomicUpdateOrdering) +``` + +Fences are slightly more powerful (but even more difficult to use) than orderings tied to specific atomic operations [[N2153]]; we expect their use will be limited to the most performance-sensitive synchronization constructs. + +### The Atomic Protocol Hierarchy + +The notion of an atomic type is captured by the `AtomicValue` protocol. + +```swift +/// A type that supports atomic operations through a separate atomic storage +/// representation. +public protocol AtomicValue { + associatedtype AtomicRepresentation: AtomicStorage + + static func encodeAtomicRepresentation( + _ value: consuming Self + ) -> AtomicRepresentation + + static func decodeAtomicRepresentation( + _ representation: consuming AtomicRepresentation + ) -> Self +} +``` + +Backing the atomic representation is an `AtomicStorage` protocol that defines all of the atomic operations one must implement in order for the type itself to be used when lowering atomic operations. + +```swift +/// The storage representation for an atomic value, providing pointer-based +/// atomic operations. This is a low-level implementation detail of atomic +/// types. +public protocol AtomicStorage { + ... +} +``` + +While `AtomicStorage` is a public protocol, its requirements are considered an implementation detail of the Standard Library. (They are replaced by ellipses above.) + +The requirements in `AtomicValue` set up a bidirectional mapping between values of the atomic type and an associated storage representation that implements the actual primitive atomic operations. + +Following existing Standard Library conventions for such interfaces, the names of all associated types and member requirements of the `AtomicStorage` protocol start with a leading underscore character. As with any other underscored interface exposed by the Standard Library, code that manually implements or directly uses these underscored requirements may fail to compile (or correctly run) when built using any Swift release other than the one for which it was initially written. + +The full set of standard types implementing `AtomicStorage` is listed below: + +```swift +extension Int { + public struct AtomicRepresentation: AtomicStorage {...} +} +extension Int8 { + public struct AtomicRepresentation: AtomicStorage {...} +} +extension Int16 { + public struct AtomicRepresentation: AtomicStorage {...} +} +extension Int32 { + public struct AtomicRepresentation: AtomicStorage {...} +} +extension Int64 { + public struct AtomicRepresentation: AtomicStorage {...} +} +extension UInt { + public struct AtomicRepresentation: AtomicStorage {...} +} +extension UInt8 { + public struct AtomicRepresentation: AtomicStorage {...} +} +extension UInt16 { + public struct AtomicRepresentation: AtomicStorage {...} +} +extension UInt32 { + public struct AtomicRepresentation: AtomicStorage {...} +} +extension UInt64 { + public struct AtomicRepresentation: AtomicStorage {...} +} +``` + +The full set of standard types implementing `AtomicValue` is listed below: + +```swift +extension Int: AtomicValue {...} +extension Int64: AtomicValue {...} +extension Int32: AtomicValue {...} +extension Int16: AtomicValue {...} +extension Int8: AtomicValue {...} +extension UInt: AtomicValue {...} +extension UInt64: AtomicValue {...} +extension UInt32: AtomicValue {...} +extension UInt16: AtomicValue {...} +extension UInt8: AtomicValue {...} + +extension Bool: AtomicValue {...} + +extension UnsafeRawPointer: AtomicValue {...} +extension UnsafeMutableRawPointer: AtomicValue {...} +extension UnsafePointer: AtomicValue {...} +extension UnsafeMutablePointer: AtomicValue {...} +extension Unmanaged: AtomicValue {...} + +extension Optional: AtomicValue where Wrapped: AtomicValue, ... {...} +``` + +#### Optional Atomics + +The standard atomic pointer types and unmanaged references also support atomic operations on their optional-wrapped form. `Optional` implements this through a conditional conformance to `AtomicValue`; the exact constraint is an implementation detail. (It works by requiring the wrapped type's internal atomic storage representation to support a special nil value.) + +```swift +extension Optional: AtomicValue where Wrapped: AtomicValue, ... { + ... +} +``` + +This proposal enables optional-atomics support for the following types: + +```swift +UnsafeRawPointer +UnsafeMutableRawPointer +UnsafePointer +UnsafeMutablePointer +Unmanaged +``` + +User code is not allowed to extend this list with additional types; this capability is reserved for potential future proposals. + +Atomic optional pointers and references are helpful when building lock-free data structures. (Although this initial set of reference types considerably limits the scope of what can be built; for more details, see the discussion on the [ABA problem](#doubleword) and [memory reclamation](#atomic-strong-references-and-the-problem-of-memory-reclamation).) + +For example, consider the lock-free, single-consumer stack implementation below. (It supports an arbitrary number of concurrently pushing threads, but it only allows a single pop at a time.) + +```swift +class LockFreeSingleConsumerStack { + struct Node { + let value: Element + var next: UnsafeMutablePointer? + } + typealias NodePtr = UnsafeMutablePointer + + private var _last = Atomic(nil) + private var _consumerCount = Atomic(0) + + deinit { + // Discard remaining nodes + while let _ = pop() {} + } + + // Push the given element to the top of the stack. + // It is okay to concurrently call this in an arbitrary number of threads. + func push(_ value: Element) { + let new = NodePtr.allocate(capacity: 1) + new.initialize(to: Node(value: value, next: nil)) + + var done = false + var current = _last.load(ordering: .relaxed) + while !done { + new.pointee.next = current + (done, current) = _last.compareExchange( + expected: current, + desired: new, + ordering: .releasing + ) + } + } + + // Pop and return the topmost element from the stack. + // This method does not support multiple overlapping concurrent calls. + func pop() -> Element? { + precondition( + _consumerCount.loadThenWrappingIncrement(ordering: .acquiring) == 0, + "Multiple consumers detected") + defer { _consumerCount.wrappingDecrement(ordering: .releasing) } + var done = false + var current = _last.load(ordering: .acquiring) + while let c = current { + (done, current) = _last.compareExchange( + expected: c, + desired: c.pointee.next, + ordering: .acquiring + ) + + if done { + let result = c.move() + c.deallocate() + return result.value + } + } + return nil + } +} +``` + +#### Custom Atomic Types + +To enable a limited set of user-defined atomic types, `AtomicValue` also provides a full set of default implementations for `RawRepresentable` types whose raw value is itself atomic: + +```swift +extension RawRepresentable where Self: AtomicValue, RawValue: AtomicValue { + ... +} +``` + +The default implementations work by forwarding all atomic operations to the raw value's implementation, converting to/from as needed. + +This enables code outside of the Standard Library to add new `AtomicValue` conformances without manually implementing any of the requirements. This is especially handy for trivial raw-representable enumerations, such as in simple atomic state machines: + +```swift +enum MyState: Int, AtomicValue { + case starting + case running + case stopped +} + +let currentState = Atomic(.starting) +... +if currentState.compareExchange( + expected: .starting, + desired: .running, + ordering: .sequentiallyConsistent +).exchanged { + ... +} +... +currentState.store(.stopped, ordering: .sequentiallyConsistent) +``` + +### `DoubleWord` + +In their current single-word form, atomic pointer and reference types are susceptible to a class of race condition called the *ABA problem*. A freshly allocated object often happens to be placed at the same memory location as a recently deallocated one. Therefore, two successive `load`s of a simple atomic pointer may return the exact same value, even though the pointer may have received an arbitrary number of updates between the two loads, and the pointee may have been completely replaced. This can be a subtle, but deadly source of race conditions in naive implementations of many concurrent data structures. + +While the single-word atomic primitives introduced in this document are already useful for some applications, it would be helpful to also provide a set of additional atomic operations that operate on two consecutive `Int`-sized values in the same transaction. All supported architectures provide direct hardware support for such "double-wide" atomic operations. + +We propose a new separate type that provides an abstraction over the layout of what a double word is for a platform. + +```swift +public struct DoubleWord { + public var first: UInt { get } + public var second: UInt { get } + + public init(first: UInt, second: UInt) +} + +extension DoubleWord: AtomicValue { + public struct AtomicRepresentation: AtomicStorage { + ... + } + + ... +} +``` + +For example, the second word can be used to augment atomic values with a version counter (sometimes called a "stamp" or a "tag"), which can help resolve the ABA problem by allowing code to reliably verify if a value remained unchanged between two successive loads. + +Note that not all CPUs support double-wide atomic operations and for that reason this type is not always available. Platforms that do not have this support must not make this type available for use. Perhaps a future direction for this is something akin to `#if canImport(struct DoubleWord)` to conditionally compile against this type if it's available. + +### The Atomic type + +So far, we've introduced memory orderings, giving us control of memory access around atomic operations; the atomic protocol hierarchy, which give us the initial list of standard types that can be as atomic values; and the `DoubleWord` type, providing an abstraction over a platform's double word type. However, we haven't yet introduced a way to actually _use_ atomics. Here we introduce the single Atomic type that exposes atomic operations for us: + +```swift +/// An atomic value. +public struct Atomic: ~Copyable { + public init(_ initialValue: consuming Value) +} +``` + +A value of `Atomic` shares the same layout as `Value.AtomicRepresentation`. + +Now that we know how to create an atomic value, it's time to introduce some actual atomic operations. + +### Basic Atomic Operations + +`Atomic` provides seven basic atomic operations for all supported types: + +```swift +extension Atomic { + /// Atomically loads and returns the current value, applying the specified + /// memory ordering. + /// + /// - Parameter ordering: The memory ordering to apply on this operation. + /// - Returns: The current value. + public borrowing func load(ordering: AtomicLoadOrdering) -> Value + + /// Atomically sets the current value to `desired`, applying the specified + /// memory ordering. + /// + /// - Parameter desired: The desired new value. + /// - Parameter ordering: The memory ordering to apply on this operation. + public borrowing func store( + _ desired: consuming Value, + ordering: AtomicStoreOrdering + ) + + /// Atomically sets the current value to `desired` and returns the original + /// value, applying the specified memory ordering. + /// + /// - Parameter desired: The desired new value. + /// - Parameter ordering: The memory ordering to apply on this operation. + /// - Returns: The original value. + public borrowing func exchange( + _ desired: consuming Value, + ordering: AtomicUpdateOrdering + ) -> Value + + /// Perform an atomic compare and exchange operation on the current value, + /// applying the specified memory ordering. + /// + /// This operation performs the following algorithm as a single atomic + /// transaction: + /// + /// ``` + /// atomic(self) { currentValue in + /// let original = currentValue + /// guard original == expected else { return (false, original) } + /// currentValue = desired + /// return (true, original) + /// } + /// ``` + /// + /// This method implements a "strong" compare and exchange operation + /// that does not permit spurious failures. + /// + /// - Parameter expected: The expected current value. + /// - Parameter desired: The desired new value. + /// - Parameter ordering: The memory ordering to apply on this operation. + /// - Returns: A tuple `(exchanged, original)`, where `exchanged` is true if + /// the exchange was successful, and `original` is the original value. + public borrowing func compareExchange( + expected: borrowing Value, + desired: consuming Value, + ordering: AtomicUpdateOrdering + ) -> (exchanged: Bool, original: Value) + + /// Perform an atomic compare and exchange operation on the current value, + /// applying the specified success/failure memory orderings. + /// + /// This operation performs the following algorithm as a single atomic + /// transaction: + /// + /// ``` + /// atomic(self) { currentValue in + /// let original = currentValue + /// guard original == expected else { return (false, original) } + /// currentValue = desired + /// return (true, original) + /// } + /// ``` + /// + /// The `successOrdering` argument specifies the memory ordering to use when + /// the operation manages to update the current value, while `failureOrdering` + /// will be used when the operation leaves the value intact. + /// + /// This method implements a "strong" compare and exchange operation + /// that does not permit spurious failures. + /// + /// - Parameter expected: The expected current value. + /// - Parameter desired: The desired new value. + /// - Parameter successOrdering: The memory ordering to apply if this + /// operation performs the exchange. + /// - Parameter failureOrdering: The memory ordering to apply on this + /// operation if it does not perform the exchange. + /// - Returns: A tuple `(exchanged, original)`, where `exchanged` is true if + /// the exchange was successful, and `original` is the original value. + public borrowing func compareExchange( + expected: borrowing Value, + desired: consuming Value, + successOrdering: AtomicUpdateOrdering, + failureOrdering: AtomicLoadOrdering + ) -> (exchanged: Bool, original: Value) + + /// Perform an atomic weak compare and exchange operation on the current + /// value, applying the memory ordering. This compare-exchange variant is + /// allowed to spuriously fail; it is designed to be called in a loop until + /// it indicates a successful exchange has happened. + /// + /// This operation performs the following algorithm as a single atomic + /// transaction: + /// + /// ``` + /// atomic(self) { currentValue in + /// let original = currentValue + /// guard original == expected else { return (false, original) } + /// currentValue = desired + /// return (true, original) + /// } + /// ``` + /// + /// (In this weak form, transient conditions may cause the `original == + /// expected` check to sometimes return false when the two values are in fact + /// the same.) + /// + /// - Parameter expected: The expected current value. + /// - Parameter desired: The desired new value. + /// - Parameter ordering: The memory ordering to apply on this operation. + /// - Returns: A tuple `(exchanged, original)`, where `exchanged` is true if + /// the exchange was successful, and `original` is the original value. + public borrowing func weakCompareExchange( + expected: borrowing Value, + desired: consuming Value, + ordering: AtomicUpdateOrdering + ) -> (exchanged: Bool, original: Value) + + /// Perform an atomic weak compare and exchange operation on the current + /// value, applying the specified success/failure memory orderings. This + /// compare-exchange variant is allowed to spuriously fail; it is designed to + /// be called in a loop until it indicates a successful exchange has happened. + /// + /// This operation performs the following algorithm as a single atomic + /// transaction: + /// + /// ``` + /// atomic(self) { currentValue in + /// let original = currentValue + /// guard original == expected else { return (false, original) } + /// currentValue = desired + /// return (true, original) + /// } + /// ``` + /// + /// (In this weak form, transient conditions may cause the `original == + /// expected` check to sometimes return false when the two values are in fact + /// the same.) + /// + /// The `ordering` argument specifies the memory ordering to use when the + /// operation manages to update the current value, while `failureOrdering` + /// will be used when the operation leaves the value intact. + /// + /// - Parameter expected: The expected current value. + /// - Parameter desired: The desired new value. + /// - Parameter successOrdering: The memory ordering to apply if this + /// operation performs the exchange. + /// - Parameter failureOrdering: The memory ordering to apply on this + /// operation does not perform the exchange. + /// - Returns: A tuple `(exchanged, original)`, where `exchanged` is true if + /// the exchange was successful, and `original` is the original value. + public borrowing func weakCompareExchange( + expected: borrowing Value, + desired: consuming Value, + successOrdering: AtomicUpdateOrdering, + failureOrdering: AtomicLoadOrdering + ) -> (exchanged: Bool, original: Value) +} +``` + +The first three operations are relatively simple: + +- `load` returns the current value. +- `store` updates it. +- `exchange` is a combination of `load` and `store`; it updates the + current value and returns the previous one as a single atomic + operation. + +The three `compareExchange` variants are somewhat more complicated: they implement a version of `exchange` that only performs the update if the original value is the same as a supplied expected value. To be specific, they execute the following algorithm as a single atomic transaction: + +```swift + guard currentValue == expected else { + return (exchanged: false, original: currentValue) + } + currentValue = desired + return (exchanged: true, original: expected) +``` + +All four variants implement the same algorithm. The single ordering variants use the same memory ordering whether or not the exchange succeeds, while the others allow callers to specify two distinct memory orderings for the success and failure cases. The two orderings are independent from each other -- all combinations of update/load orderings are supported [[P0418]]. (Of course, the implementation may need to "round up" to the nearest ordering combination that is supported by the underlying code generation layer and the targeted CPU architecture.) + +The `weakCompareExchange` form may sometimes return false even when the original and expected values are equal. (Such failures may happen when some transient condition prevents the underlying operation from succeeding -- such as an incoming interrupt during a load-link/store-conditional instruction sequence.) This variant is designed to be called in a loop that only exits when the exchange is successful; in such loops, using `weakCompareExchange` may lead to a performance improvement by eliminating a nested loop in the regular, "strong", `compareExchange` variants. + +The compare-exchange primitive is special: it is a universal operation that can be used to implement all other atomic operations, and more. For example, here is how we could use `compareExchange` to implement a wrapping increment operation over `Atomic` values: + +```swift +extension Atomic where Value == Int { + func wrappingIncrement( + by operand: Int, + ordering: AtomicUpdateOrdering + ) { + var done = false + var current = load(ordering: .relaxed) + while !done { + (done, current) = compareExchange( + expected: current, + desired: current &+ operand, + ordering: ordering + ) + } + } +} +``` + +### Specialized Integer Operations + +Most CPU architectures provide dedicated atomic instructions for certain integer operations, and these are generally more efficient than implementations using `compareExchange`. Therefore, it makes sense to expose a set of dedicated methods for common integer operations so that these will always get compiled into the most efficient implementation available. + +These specialized integer operations generally come in two variants, based on whether they're returning the value before or after the operation: + +| Method Name | Returns | Implements | +| --- | --- | --- | +| `loadThenWrappingIncrement(by:ordering:)` | original value | `a &+= b` | +| `loadThenWrappingDecrement(by:ordering:)` | original value | `a &-= b` | +| `loadThenBitwiseAnd(with:ordering)` | original value | `a &= b` | +| `loadThenBitwiseOr(with:ordering)` | original value | `a \|= b` | +| `loadThenBitwiseXor(with:ordering)` | original value | `a ^= b` | +| `wrappingIncrementThenLoad(by:ordering:)` | new value | `a &+= b` | +| `wrappingDecrementThenLoad(by:ordering:)` | new value |`a &-= b` | +| `bitwiseAndThenLoad(with:ordering)` | new value |`a &= b` | +| `bitwiseOrThenLoad(with:ordering)` | new value |`a \|= b` | +| `bitwiseXorThenLoad(with:ordering)` | new value |`a ^= b` | +| `wrappingIncrement(by:ordering:)` | none | `a &+= b` | +| `wrappingDecrement(by:ordering:)` | none | `a &-= b` | + +The `wrappingIncrement` and `wrappingDecrement` operations are provided as a convenience for incrementing/decrementing values in the common case when a return value is not required. + +While we require all atomic operations to be free of locks, we don't require wait-freedom. Therefore, on architectures that don't provide direct hardware support for some or all of these operations, we still require them to be implemented using `compareExchange` loops like the one for `wrappingIncrement` above. + +`Atomic` exposes these operations when `Value` is one of the standard fixed-width integer types. + +```swift +extension Atomic where Value == Int {...} +extension Atomic where Value == UInt8 {...} +... + +let counter = Atomic(0) +counter.wrappingIncrement(by: 42, ordering: .relaxed) +``` + +### Specialized Boolean Operations + +Similar to the specialized integer operations, we can provide similar ones for booleans with the same two variants: + +| Method Name | Returns | Implements | +|--------------------------------------|----------------|----------------| +| `loadThenLogicalAnd(with:ordering:)` | original value | `a = a && b` | +| `loadThenLogicalOr(with:ordering:)` | original value | `a = a \|\| b` | +| `loadThenLogicalXor(with:ordering:)` | original value | `a = a ^ b` | +| `logicalAndThenLoad(with:ordering:)` | new value | `a = a && b` | +| `logicalOrThenLoad(with:ordering:)` | new value | `a = a \|\| b` | +| `logicalXorThenLoad(with:ordering:)` | new value | `a = a ^ b` | + +`Atomic` exposes these operations when `Value` is `Bool`. + +```swift +extension Atomic where Value == Bool {...} + +let tracker = Atomic(false) +let new = tracker.logicalOrThenLoad(with: true, ordering: .relaxed) +``` + +### Atomic Lazy References + +The operations provided by `Atomic>` only operate on the unmanaged reference itself. They don't allow us to directly access the referenced object -- we need to manually invoke the methods `Unmanaged` provides for this purpose (usually, `takeUnretainedValue`). + +Note that loading the atomic unmanaged reference and converting it to a strong reference are two distinct operations that won't execute as a single atomic transaction. This can easily lead to race conditions when a thread releases an object while another is busy loading it: + +```swift +// BROKEN CODE. DO NOT EMULATE IN PRODUCTION. +let myAtomicRef = Atomic>(...) + +// Thread A: Load the unmanaged value and then convert it to a regular +// strong reference. +let ref = myAtomicRef.load(ordering: .acquiring).takeUnretainedValue() +... + +// Thread B: Store a new reference in the atomic unmanaged value and +// release the previous reference. +let new = Unmanaged.passRetained(...) +let old = myAtomicRef.exchange(new, ordering: .acquiringAndReleasing) +old.release() // RACE CONDITION +``` + +If thread B happens to release the same object that thread A is in the process of loading, then thread A's `takeUnretainedValue` may attempt to retain a deallocated object. + +Such problems make `Atomic>` exceedingly difficult to use in all but the simplest situations. The section on [*Atomic Strong References*](#atomic-strong-references-and-the-problem-of-memory-reclamation) below describes some new constructs we may introduce in future proposals to assist with this issue. + +For now, we provide the standalone type `AtomicLazyReference`; this is an example of a useful construct that could be built on top of `Atomic>` operations. (Of the atomic constructs introduced in this proposal, only `AtomicLazyReference` represents a regular strong reference to a class instance -- the other pointer/reference types leave memory management entirely up to the user.) + +An `AtomicLazyReference` holds an optional reference that is initially set to `nil`. The value can be set exactly once, but it can be read an arbitrary number of times. Attempts to change the value after the first `storeIfNilThenLoad` call are ignored, and return the current value instead. + +```swift +/// A lazily initializable atomic strong reference. +/// +/// These values can be set (initialized) exactly once, but read many +/// times. +public struct AtomicLazyReference: ~Copyable { + /// The value logically stored in an atomic lazy reference value. + public typealias Value = Instance? + + /// Initializes a new managed atomic lazy reference with a nil value. + public init() +} + +extension AtomicLazyReference { + /// Atomically initializes this reference if its current value is nil, then + /// returns the initialized value. If this reference is already initialized, + /// then `storeIfNilThenLoad(_:)` discards its supplied argument and returns + /// the current value without updating it. + /// + /// The following example demonstrates how this can be used to implement a + /// thread-safe lazily initialized reference: + /// + /// ``` + /// class Image { + /// var _histogram: AtomicLazyReference = .init() + /// + /// // This is safe to call concurrently from multiple threads. + /// var atomicLazyHistogram: Histogram { + /// if let histogram = _histogram.load() { return histogram } + /// // Note that code here may run concurrently on + /// // multiple threads, but only one of them will get to + /// // succeed setting the reference. + /// let histogram = ... + /// return _histogram.storeIfNilThenLoad(histogram) + /// } + /// ``` + /// + /// This operation uses acquiring-and-releasing memory ordering. + public borrowing func storeIfNilThenLoad( + _ desired: consuming Instance + ) -> Instance + + /// Atomically loads and returns the current value of this reference. + /// + /// The load operation is performed with the memory ordering + /// `AtomicLoadOrdering.acquiring`. + public borrowing func load() -> Instance? +} +``` + +This is the only atomic type in this proposal that doesn't provide the usual `load`/`store`/`exchange`/`compareExchange` operations. + +This construct allows library authors to implement a thread-safe lazy initialization pattern: + +```swift +var _foo: AtomicLazyReference = ... + +// This is safe to call concurrently from multiple threads. +var atomicLazyFoo: Foo { + if let foo = _foo.load() { return foo } + // Note: the code here may run concurrently on multiple threads. + // All but one of the resulting values will be discarded. + let foo = Foo() + return _foo.storeIfNilThenLoad(foo) +} +``` + +The Standard Library has been internally using such a pattern to implement deferred bridging for `Array`, `Dictionary` and `Set`. + +Note that unlike the rest of the atomic types, `load` and `storeIfNilThenLoad(_:)` do not expose `ordering` parameters. (Internally, they map to acquiring/releasing operations to guarantee correct synchronization.) + +### Restricting Ordering Arguments to Compile-Time Constants + +Modeling orderings as regular function parameters allows us to specify them using syntax that's familiar to all Swift programmers. Unfortunately, it means that in the implementation of atomic operations we're forced to switch over the ordering argument: + +```swift +extension Int: AtomicStorage { + public func atomicCompareExchange( + expected: Int, + desired: Int, + at address: UnsafeMutablePointer, + ordering: AtomicUpdateOrdering + ) -> (exchanged: Bool, original: Int) { + // Note: This is a simplified version of the actual implementation + let won: Bool + let oldValue: Int + + switch ordering { + case .relaxed: + (oldValue, won) = Builtin.cmpxchg_monotonic_monotonic_Word( + address, expected, desired + ) + + case .acquiring: + (oldValue, won) = Builtin.cmpxchg_acquire_acquire_Word( + address, expected, desired + ) + + case .releasing: + (oldValue, won) = Builtin.cmpxchg_release_monotonic_Word( + address, expected, desired + ) + + case .acquiringAndReleasing: + (oldValue, won) = Builtin.cmpxchg_acqrel_acquire_Word( + address, expected, desired + ) + + case .sequentiallyConsistent: + (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_Word( + address, expected, desired + ) + + default: + fatalError("Unknown atomic memory ordering") + } + + return (exchanged: won, original: oldValue) + } +} +``` + +Given our requirement that primitive atomics must always compile down to the actual atomic instructions with minimal additional overhead, we must guarantee that these switch statements always get optimized away into the single case we need; they must never actually be evaluated at runtime. + +Luckily, configuring these special functions to always get force-inlined into all callers guarantees that constant folding will get rid of the switch statement *as long as the supplied ordering is a compile-time constant*. Unfortunately, it's all too easy to accidentally violate this latter requirement, with dire consequences to the expected performance of the atomic operation. + +Consider the following well-meaning attempt at using `compareExchange` to define an atomic integer addition operation that traps on overflow rather than allowing the result to wrap around: + +```swift +extension Atomic where Value == Int { + // Non-inlinable + public func checkedIncrement(by delta: Int, ordering: AtomicUpdateOrdering) { + var done = false + var current = load(ordering: .relaxed) + + while !done { + (done, current) = compareExchange( + expected: current, + desired: current + operand, // Traps on overflow + ordering: ordering + ) + } + } +} + +// Elsewhere: +counter.checkedIncrement(by: 1, ordering: .relaxed) +``` + +If for whatever reason the Swift compiler isn't able (or willing) to inline the `checkedIncrement` call, then the value of `ordering` won't be known at compile time to the body of the function, so even though `compareExchange` will still get inlined, its switch statement won't be eliminated. This leads to a potentially significant performance regression that could interfere with the scalability of the operation. + +To prevent these issues, we are constraining the memory ordering arguments of all atomic operations to be compile-time constants. Any attempt to pass a dynamic ordering value (such as in the `compareExchange` call above) will result in a compile-time error. + +An ordering expression will be considered constant-evaluable if it's either (1) a direct call to one of the `Atomic*Ordering` factory methods (`.relaxed`, `.acquiring`, etc.), or (2) it is a direct reference to a variable that is in turn constrained to be constant-evaluable. + +## Interaction with Existing Language Features + +Please refer to the [Clarify the Swift memory consistency model (SE-0282)](https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md#interaction-with-non-instantaneous-accesses) proposal which goes over how atomics interact with the Law of Exclusivity, Non-Instantaneous Accesses, and Implicit Pointer Conversions. + +An additional note with regards to the Law of Exclusivity, atomic values should never be declared with a `var` binding, always prefer a `let` one. Consider the following: + +```swift +class Counter { + var value: Atomic +} +``` + +By declaring this atomic value as a `var`, we've opted into Swift's dynamic exclusivity checking for this property, so all reads are checking whether or not it's currently being written to. This inherently means the atomic value is no longer, atomic. In general, if one is unsure if a `var` atomic value will incur the dynamic exclusivity checking, use a `let`. We can achieve store operations with these atomic types while only needing a borrow of the value, so there's no need to have a mutable reference to the value. + +It is very important, however, that one must never pass an `Atomic` as `inout` or `consuming` because then you declare the parameter to have exclusive access of the atomic value which as we said, makes the value no longer atomic. + +### Interaction with Swift Concurrency + +The `Atomic` type is `Sendable` where `Value: Sendable`. One can pass a value of an `Atomic` to an actor or any other async context by using a borrow reference. + +```swift +actor Updater { + ... + + func update(_ counter: borrowing Atomic) { + ... + } + + func doOtherWork() {} +} + +func version1() async { + let counter = Atomic(0) + + // |--- There are no suspension points in this function, so + // | this atomic value will be allocated on whatever + // | thread's stack that decides to run this async function. + // | + // v + let updatedCount = counter.load(ordering: .relaxed) +} + +func version2() async { + let counter = Atomic(0) + + let updater = Updater() + await updater.update(counter) // <--------- Potential suspension point that + // uses the atomic value directly. + // The atomic value will get + // promoted to the async stack frame + // meaning it will be available until + // this async function has ended. + + // |----- Atomic value used after suspension point. + // | Because of the fact that we're passing it to a + // | suspension point, it's already been allocated on + // | the async stack frame, and accessing it later will + // | access that same resource, preserving atomicity. + // | + // v + let updatedCount = counter.load(ordering: .relaxed) +} + +func version3() async { + let counter = Atomic(0) + + let updater = Updater() + await updater.doOtherWork() // <--------- Potential suspension point that + // doesn't use the atomic value directly. + + + // |----- Atomic value used after suspension point, so it is + // | promoted to the async stack frame which makes this + // | value's lifetime persist even after the await. + // | The compiler could in theory also reorder the + // | atomic value's initialization after the suspension + // | because it isn't used before nor during meaning it + // | could be allocated on whatever thread's stack frame. + // | + // v + let updatedCount = counter.load(ordering: .relaxed) +} +``` + +Considering these factors, we can safely say that `extension Atomic: Sendable where Value: Sendable {}`. All of the places where one can store a value an `Atomic`, is either as a global, as a class ivar, or as a local. For globals and class ivars, everyone agrees that they exist at a single location and will never try to perform atomic operations by moving the value to some local. As explained above, we can safely reason about local atomic values in async contexts and all of the places where we care about preserving atomicity will do the right thing for us, either by just using the thread's stack frame for allocation or by promoting it to some async coroutine's stack frame on the heap. + +## Detailed Design + +In the interest of keeping this document (relatively) short, the following API synopsis does not include API documentation, inlinable method bodies, or `@usableFromInline` declarations, and omits most attributes (`@available`, `@inlinable`, etc.). + +To allow atomic operations to compile down to their corresponding CPU instructions, most entry points listed here will be defined `@inlinable`. + +For the full API definition, please refer to the [implementation][implementation]. + +### Atomic Memory Orderings + +```swift +public struct AtomicLoadOrdering: Equatable, Hashable, CustomStringConvertible { + public static var relaxed: Self { get } + public static var acquiring: Self { get } + public static var sequentiallyConsistent: Self { get } + + public static func ==(left: Self, right: Self) -> Bool + public func hash(into hasher: inout Hasher) + public var description: String { get } +} + +public struct AtomicStoreOrdering: Equatable, Hashable, CustomStringConvertible { + public static var relaxed: Self { get } + public static var releasing: Self { get } + public static var sequentiallyConsistent: Self { get } + + public static func ==(left: Self, right: Self) -> Bool + public func hash(into hasher: inout Hasher) + public var description: String { get } +} + +public struct AtomicUpdateOrdering: Equatable, Hashable, CustomStringConvertible { + public static var relaxed: Self { get } + public static var acquiring: Self { get } + public static var releasing: Self { get } + public static var acquiringAndReleasing: Self { get } + public static var sequentiallyConsistent: Self { get } + + public static func ==(left: Self, right: Self) -> Bool + public func hash(into hasher: inout Hasher) + public var description: String { get } +} + +public func atomicMemoryFence(ordering: AtomicUpdateOrdering) +``` + +### Atomic Protocols + +#### `AtomicStorage` + +```swift +public protocol AtomicStorage { + // Requirements aren't public API +} +``` + +This protocol supplies the actual primitive atomic operations for conformers. The exact requirements are a private implementation detail of the Standard Library. They are outside the scope of the Swift Evolution process and they may arbitrarily change between library releases. User code must not directly use them or manually implement them. + +Conforming types: + +```swift +extension Int.AtomicRepresentation: AtomicStorage {...} +extension Int64.AtomicRepresentation: AtomicStorage {...} +extension Int32.AtomicRepresentation: AtomicStorage {...} +extension Int16.AtomicRepresentation: AtomicStorage {...} +extension Int8.AtomicRepresentation: AtomicStorage {...} +extension UInt.AtomicRepresentation: AtomicStorage {...} +extension UInt64.AtomicRepresentation: AtomicStorage {...} +extension UInt32.AtomicRepresentation: AtomicStorage {...} +extension UInt16.AtomicRepresentation: AtomicStorage {...} +extension UInt8.AtomicRepresentation: AtomicStorage {...} + +extension DoubleWord.AtomicRepresentation: AtomicStorage {...} +``` + +#### `AtomicValue` + +```swift +public protocol AtomicValue { + associatedtype AtomicRepresentation: AtomicStorage + + static func encodeAtomicRepresentation( + _ value: consuming Self + ) -> AtomicRepresentation + + static func decodeAtomicRepresentation( + _ representation: consuming AtomicRepresentation + ) -> Self +} +``` + +The requirements set up a bidirectional mapping between values of the atomic type and an associated storage representation that supplies the actual primitive atomic operations. + +Conforming types: + +```swift +extension Int: AtomicValue {...} +extension Int64: AtomicValue {...} +extension Int32: AtomicValue {...} +extension Int16: AtomicValue {...} +extension Int8: AtomicValue {...} +extension UInt: AtomicValue {...} +extension UInt64: AtomicValue {...} +extension UInt32: AtomicValue {...} +extension UInt16: AtomicValue {...} +extension UInt8: AtomicValue {...} + +extension DoubleWord: AtomicValue {...} + +extension Bool: AtomicValue {...} + +extension UnsafeRawPointer: AtomicValue {...} +extension UnsafeMutableRawPointer: AtomicValue {...} +extension UnsafePointer: AtomicValue {...} +extension UnsafeMutablePointer: AtomicValue {...} +extension Unmanaged: AtomicValue {...} + +extension Optional: AtomicValue where Wrapped: AtomicValue, ... {...} +``` + +The exact constraints on `Optional`'s conditional conformance are a private implementation detail. (They specify that the underlying (private) storage representation must be able to represent an extra `nil` value.) + +Atomic `Optional` operations are currently enabled for the following `Wrapped` types: + +```swift +UnsafeRawPointer +UnsafeMutableRawPointer +UnsafePointer +UnsafeMutablePointer +Unmanaged +``` + +User code is not allowed to extend this list with additional types; this capability is reserved for potential future proposals. + +To support custom "atomic-representable" types, `AtomicValue` also comes with default implementations for all its requirements for `RawRepresentable` types whose `RawValue` is also atomic: + +```swift +extension RawRepresentable where Self: AtomicValue, RawValue: AtomicValue, ... { + // Implementations for all requirements. +} +``` + +The omitted constraint sets up the (private) atomic storage type to match that of the `RawValue`. The default implementations work by converting values to their `rawValue` form, and forwarding all atomic operations to it. + +### `DoubleWord` + +```swift +public struct DoubleWord { + public var first: UInt { get } + public var second: UInt { get } + + public init(first: UInt, second: UInt) +} +``` + +### Atomic Types + +#### `Atomic` + +```swift +public struct Atomic: ~Copyable { + public init(_ initialValue: consuming Value) + + // Atomic operations: + + public borrowing func load(ordering: AtomicLoadOrdering) -> Value + + public borrowing func store( + _ desired: consuming Value, + ordering: AtomicStoreOrdering + ) + + public borrowing func exchange( + _ desired: consuming Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func compareExchange( + expected: borrowing Value, + desired: consuming Value, + ordering: AtomicUpdateOrdering + ) -> (exchanged: Bool, original: Value) + + public borrowing func compareExchange( + expected: borrowing Value, + desired: consuming Value, + successOrdering: AtomicUpdateOrdering, + failureOrdering: AtomicLoadOrdering + ) -> (exchanged: Bool, original: Value) + + public borrowing func weakCompareExchange( + expected: borrowing Value, + desired: consuming Value, + ordering: AtomicUpdateOrdering + ) -> (exchanged: Bool, original: Value) + + public borrowing func weakCompareExchange( + expected: borrowing Value, + desired: consuming Value, + successOrdering: AtomicUpdateOrdering, + failureOrdering: AtomicLoadOrdering + ) -> (exchanged: Bool, original: Value) +} +``` + +`Atomic` also provides a handful of integer operations for the standard fixed-width integer types. This is implemented via same type requirements: + +```swift +extension Atomic where Value == Int { + public borrowing func loadThenWrappingIncrement( + by operand: Value = 1, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func wrappingIncrementThenLoad( + by operand: Value = 1, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func wrappingIncrement( + by operand: Value = 1, + ordering: AtomicUpdateOrdering + ) + + public borrowing func loadThenWrappingDecrement( + by operand: Value = 1, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func wrappingDecrementThenLoad( + by operand: Value = 1, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func wrappingDecrement( + by operand: Value = 1, + ordering: AtomicUpdateOrdering + ) + + public borrowing func loadThenBitwiseAnd( + _ operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func bitwiseAndThenLoad( + _ operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func loadThenBitwiseOr( + _ operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func bitwiseOrThenLoad( + _ operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func loadThenBitwiseXor( + _ operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func bitwiseXorThenLoad( + _ operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value +} + +extension Atomic where Value == Int8 {...} +... +``` + +as well as providing convenience functions for boolean operations: + +```swift +extension Atomic where Value == Bool { + public borrowing func loadThenLogicalAnd( + with operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func loadThenLogicalOr( + with operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func loadThenLogicalXor( + with operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func logicalAndThenLoad( + with operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func logicalOrThenLoad( + with operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func logicalXorThenLoad( + with operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value +} +``` + +#### `AtomicLazyReference` + +```swift +public struct AtomicLazyReference: ~Copyable { + public typealias Value = Instance? + + public init(_ initialValue: consuming Instance) + + // Atomic operations: + + public borrowing func storeIfNilThenLoad( + _ desired: consuming Instance + ) -> Instance + + public borrowing func load() -> Instance? +} +``` + +## Source Compatibility + +This is a purely additive change with no source compatibility impact. + +## Effect on ABI Stability + +This proposal introduces new entry points to the Standard Library ABI in a standalone `Synchronization` module, but otherwise it has no effect on ABI stability. + +On ABI-stable platforms, the struct types and protocols introduced here will become part of the stdlib's ABI with availability matching the first OS releases that include them. + +Most of the atomic methods introduced in this document will be force-inlined directly into client code at every call site. As such, there is no reason to bake them into the stdlib's ABI -- the stdlib binary will not export symbols for them. + +## Effect on API Resilience + +This is an additive change; it has no effect on the API of existing code. + +For the new constructs introduced here, the proposed design allows us to make the following changes in future versions of the Swift Standard Library: + +- Addition of new atomic types (and higher-level constructs built around them). (These new types would not directly back-deploy to OS versions that predate their introduction.) + +- Addition of new memory orderings. Because all atomic operations compile directly into user code, new memory orderings that we decide to introduce later could potentially back-deploy to any OS release that includes this proposal. + +- Addition of new atomic operations on the types introduced here. These would be reflected in internal protocol requirements, so they would not be directly back-deployable to previous ABI-stable OS releases. + +- Introducing a default memory ordering for atomic operations (either by adding a default value to `ordering`, or by adding new overloads that lack that parameter). This too would be a back-deployable change. + +(We don't necessarily plan to actually perform any of these changes; we merely leave the door open to doing them.) + +## Potential Future Directions + +### Atomic Strong References and The Problem of Memory Reclamation + +Perhaps counter-intuitively, implementing a high-performance, *lock-free* atomic version of regular everyday strong references is not a trivial task. This proposal doesn't attempt to provide such a construct beyond the limited use-case of `AtomicLazyReference`. + +Under the hood, Swift's strong references have always been using atomic operations to implement reference counting. This allows references to be read (but not mutated) from multiple, concurrent threads of execution, while also ensuring that each object still gets deallocated as soon as its last outstanding reference disappears. However, atomic reference counts on their own do not allow threads to safely share a single *mutable* reference without additional synchronization. + +The difficulty is in the implementation of the atomic load operation, which boils down to two separate sub-operations, both of which need to be part of the *same atomic transaction*: + +1. Load the value of the reference. +2. Increment the reference count of the corresponding object. + +If an intervening store operation were allowed to release the reference between steps 1 and 2, then the loaded reference could already be deallocated by the time `load` tries to increment its refcount. + +Without an efficient way to implement these two steps as a single atomic transaction, the implementation of `store` needs to delay releasing the overwritten value until it can guarantee that every outstanding load operation is completed. Exactly how to implement this is the problem of *memory reclamation* in concurrent data structures. + +There are a variety of approaches to tackle this problem, some of which may be general enough to consider in future proposals. (One potential solution can be built on top of double-wide atomic operations, by offloading some of the reference counting operations into the second word of a double-wide atomic reference.) + +(It'd be straightforward to use locks to build an atomic strong reference; while such a construct obviously wouldn't be lock-free, it is still a useful abstraction, so it may be a worthy addition to the Standard Library. However, locking constructs are outside the scope of this proposal.) + +### Additional Low-Level Atomic Features + +To enable use cases that require even more fine-grained control over atomic operations, it may be useful to introduce additional low-level atomics features: + +* support for additional kinds of atomic values (such as floating-point atomics [[P0020]]), +* new memory orderings, such as a consuming load ordering [[P0750]] or tearable atomics [[P0690]], +* "volatile" atomics that prevent certain compiler optimizations +* and more + +We defer these for future proposals. + +## Alternatives Considered + +### Default Orderings + +We considered defaulting all atomic operations throughout the `Synchronization` module to sequentially consistent ordering. While we concede that doing so would make atomics slightly more approachable, implicit ordering values tend to interfere with highly performance-sensitive use cases of atomics (which is *most* use cases of atomics). Sequential consistency tends to be relatively rarely used in these contexts, and implicitly defaulting to it would allow accidental use to easily slip through code review. + +Users who wish for default orderings are welcome to define their own overloads for atomic operations: + +```swift +extension Atomic { + func load() -> Value { + load(ordering: .sequentiallyConsistent) + } + + func store(_ desired: consuming Value) { + store(desired, ordering: .sequentiallyConsistent) + } + + func exchange(_ desired: consuming Value) -> Value { + exchange(desired, ordering: .sequentiallyConsistent) + } + + func compareExchange( + expected: borrowing Value, + desired: consuming Value + ) -> (exchanged: Bool, original: Value) { + compareExchange( + expected: expected, + desired: desired, + ordering: .sequentiallyConsistent + ) + } + + func weakCompareExchange( + expected: borrowing Value, + desired: consuming Value + ) -> (exchanged: Bool, original: Value) { + weakCompareExchange( + expected: expected, + desired: desired, + successOrdering: .sequentiallyConsistent, + failureOrdering: .sequentiallyConsistent + ) + } +} + +extension Atomic where Value == Int { + func wrappingIncrement(by delta: Value = 1) { + wrappingIncrement(by: delta, ordering: .sequentiallyConsistent) + } + + etc. +} +``` + +### A Truly Universal Generic Atomic Type + +While future proposals may add a variety of other atomic types, we do not expect to ever provide a truly universal generic `Atomic` construct. The Synchronization module is designed to provide high-performance wait-free primitives, and these are heavily constrained by the atomic instruction sets of the CPU architectures Swift targets. + +A universal `Atomic` type that can hold *any* value is unlikely to be implementable without locks, so it is outside the scope of this proposal. We may eventually consider adding such a construct in a future concurrency proposal: + +```swift +struct Serialized: ~Copyable { + private let _lock = UnfairLock() + private var _value: Value + + func withLock(_ body: (inout Value) throws -> T) rethrows -> T { + _lock.lock() + defer { _lock.unlock() } + + return try body(&_value) + } +} +``` + +### Providing a `value` Property + +Our atomic constructs are unusual because even though semantically they behave like containers holding a value, they do not provide direct access to it. Instead of exposing a getter and a setter on a handy `value` property, they expose cumbersome `load` and `store` methods. There are two reasons for this curious inconsistency: + +First, there is the obvious issue that property getter/setters have no room for an ordering parameter. + +Second, there is a deep underlying problem with the property syntax: it encourages silent race conditions. For example, consider the code below: + +```swift +let counter = Atomic(0) +... +counter.value += 1 +``` + +Even though this increment looks like it may be a single atomic operation, it gets executed as two separate atomic transactions: + +```swift +var temp = counter.value // atomic load +temp += 1 +counter.value = temp // atomic store +``` + +If some other thread happens to update the value after the atomic load, then that update gets overwritten by the subsequent store, resulting in data loss. + +To prevent this gotcha, none of the proposed atomic types provide a property for accessing their value, and we don't foresee adding such a property in the future, either. + +(Note that this problem cannot be mitigated by implementing [modify accessors]. Lock-free updates cannot be implemented without the ability to retry the update multiple times, and modify accessors can only yield once.) + +[modify accessors]: https://forums.swift.org/t/modify-accessors/31872 + +### Alternative Designs for Memory Orderings + +Modeling memory orderings with enumeration(-like) values fits well into the Standard Library's existing API design practice, but `ordering` arguments aren't without problems. Most importantly, the quality of code generation depends greatly on the compiler's ability to constant-fold switch statements over these ordering values into a single instruction. This can be fragile -- especially in unoptimized builds. We think [constraining these arguments to compile-time constants](#restricting-ordering-arguments-to-compile-time-constants) strikes a good balance between readability and performance, but it's instructive to look at some of the approaches we considered before settling on this choice. + +#### Encode Orderings in Method Names + +One obvious idea is to put the ordering values directly in the method name for every atomic operation. This would be easy to implement but it leads to practically unusable API names. Consider the two-ordering compare/exchange variant below: + +```swift +flag.sequentiallyConsistentButAcquiringAndReleasingOnFailureCompareExchange( + expected: 0, + desired: 1 +) +``` + +We could find shorter names for the orderings (`Serialized`, `Barrier` etc.), but ultimately the problem is that this approach tries to cram too much information into the method name, and the resulting multitude of similar-but-not-exactly-the-same methods become an ill-structured mess. + +#### Orderings As Generic Type Parameters + +A second idea is model the orderings as generic type parameters on the atomic types themselves. + +```swift +struct Atomic { + ... +} + +let counter = Atomic(0) +counter.wrappingIncrement() +``` + +This simplifies the typical case where all operations on a certain atomic value use the same "level" of ordering (relaxed, acquire/release, or sequentially consistent). However, there are considerable drawbacks: + +* This design puts the ordering specification far away from the actual operations -- obfuscating their meaning. +* It makes it a lot more difficult to use custom orderings for specific operations (like the speculative relaxed load in the `wrappingIncrement` example in the section on [Atomic Operations](#atomic-operations) above). +* We wouldn't be able to provide a default value for a generic type parameter. +* Finally, there is also the risk of unspecialized generics interfering with runtime performance. + +#### Ordering Views + +The most promising alternative idea to represent memory orderings was to model them like `String`'s encoding views: + +```swift +let counter = Atomic(0) + +counter.relaxed.increment() + +let current = counter.acquiring.load() +``` + +There are some things that we really like about this "ordering view" approach: + +- It eliminates the need to ever switch over orderings, preventing any and all constant folding issues. +- It makes it obvious that memory orderings are supposed to be compile-time parameters. +- The syntax is arguably more attractive. + +However, we ultimately decided against going down this route, for the following reasons: + + - **Composability.** Such ordering views are unwieldy for the variant of `compareExchange` that takes separate success/failure orderings. Ordering views don't nest very well at all: + + ```swift + counter.acquiringAndReleasing.butAcquiringOnFailure.compareExchange(...) + ``` + + - **API surface area and complexity.** Ordering views act like a multiplier for API entry points. In our prototype implementation, introducing ordering views increased the API surface area of atomics by 3×: we went from 6 public structs with 53 public methods to 27 structs with 175 methods. While clever use of protocols and generics could reduce this factor, the increased complexity seems undesirable. (E.g., generic ordering views would reintroduce potential performance problems in the form of unspecialized generics.) + + API surface area is not necessarily the most important statistic, but public methods do have some cost. (In e.g. the size of the stdlib module, API documentation etc.) + + - **Unintuitive syntax.** While the syntax is indeed superficially attractive, it feels backward to put the memory ordering *before* the actual operation. While memory orderings are important, I suspect most people would consider them secondary to the operations themselves. + + - **Limited Reuse.** Implementing ordering views takes a rather large amount of (error-prone) boilerplate-heavy code that is not directly reusable. Every new atomic type would need to implement a new set of ordering views, tailor-fit to its own use-case. + +### Directly bring over `swift-atomics`'s API + +The `swift-atomics` package has many years of experience using their APIs to interface with atomic values and it would be beneficial to simply bring over the same API. However, once we designed the general purpose `Atomic` type, we noticied a few deficiencies with the atomic protocol hierarchy that made using this type awkward for users. We've redesigned these protocols to make using the atomic type easier to use and easier to reason about. + +While there are some API differences between this proposal and the package, most of the atomic operations are the same and should feel very familiar to those who have used the package before. We don't plan on drastically renaming any core atomic operation because we believe `swift-atomics` already got those names correct. + +## References + +[Clarify the Swift memory consistency model (SE-0282)]: https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md +**\[Clarify the Swift memory consistency model (SE-0282)]** Karoy Lorenty. "Clarify the Swift memory consistency model."*Swift Evolution Proposal*, 2020. https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md + +[C++17]: https://isocpp.org/std/the-standard +**\[C++17]** ISO/IEC. *ISO International Standard ISO/IEC 14882:2017(E) – Programming Language C++.* 2017. + https://isocpp.org/std/the-standard + +[Boehm 2008]: https://doi.org/10.1145/1375581.1375591 +**\[Boehm 2008]** Hans-J. Boehm, Sarita V. Adve. "Foundations of the C++ Concurrency Memory Model." In *PLDI '08: Proc. of the 29th ACM SIGPLAN Conf. on Programming Language Design and Implementation*, pages 68–78, June 2008. + https://doi.org/10.1145/1375581.1375591 + +[N2153]: http://wg21.link/N2153 +**\[N2153]** Raúl Silvera, Michael Wong, Paul McKenney, Bob Blainey. *A simple and efficient memory model for weakly-ordered architectures.* WG21/N2153, January 12, 2007. http://wg21.link/N2153 + +[P0020]: http://wg21.link/P0020 +**\[P0020]** H. Carter Edwards, Hans Boehm, Olivier Giroux, JF Bastien, James Reus. *Floating Point Atomic.* WG21/P0020r6, November 10, 2017. http://wg21.link/P0020 + +[P0418]: http://wg21.link/P0418 +**\[P0418]** JF Bastien, Hans-J. Boehm. *Fail or succeed: there is no atomic lattice.* WG21/P0417r2, November 9, 2016. http://wg21.link/P0418 + +[P0690]: http://wg21.link/P0690 +**\[P0690]** JF Bastien, Billy Robert O'Neal III, Andrew Hunter. *Tearable Atomics.* WG21/P0690, February 10, 2018. http://wg21.link/P0690 + +[P0735]: http://wg21.link/P0735 +**\[P0735]**: Will Deacon, Jade Alglave. *Interaction of `memory_order_consume` with release sequences.* WG21/P0735r1, June 17, 2019. http://wg21.link/P0735 + +[P0750]: http://wg21.link/P0750 +**\[P0750]** JF Bastien, Paul E. McKinney. *Consume*. WG21/P0750, February 11, 2018. http://wg21.link/P0750 + +⚛︎︎ + + + + + + + + From 4cf27e1ef20dc20a5707e732d84a3edb0e20521f Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Tue, 19 Sep 2023 05:50:05 +0100 Subject: [PATCH 3315/4563] Update README.md (#2168) Swift 5.9 Released --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e443d6eccb..f719030f54 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This repository tracks the ongoing evolution of the Swift programming language, | Version | Announced | Released | | :-------- | :----------------------------------------------------------------------- | :----------------------------------------------------------- | | Swift 5.10 | [2023-08-23](https://forums.swift.org/t/swift-5-10-release-process/66911) | -| Swift 5.9 | [2023-03-06](https://forums.swift.org/t/swift-5-9-release-process/63557) | +| Swift 5.9 | [2023-03-06](https://forums.swift.org/t/swift-5-9-release-process/63557) | [2023-09-18](https://www.swift.org/blog/swift-5.9-released/) | | Swift 5.8 | [2022-11-19](https://forums.swift.org/t/swift-5-8-release-process/61540) | [2023-03-30](https://www.swift.org/blog/swift-5.8-released/) | | Swift 5.7 | [2022-03-29](https://forums.swift.org/t/swift-5-7-release-process/56316) | [2022-09-12](https://www.swift.org/blog/swift-5.7-released/) | | Swift 5.6 | [2021-11-10](https://forums.swift.org/t/swift-5-6-release-process/53412) | [2022-03-14](https://www.swift.org/blog/swift-5.6-released/) | From 69a7e3c6795356e177c4c04d43bb98c4c3ab6f1c Mon Sep 17 00:00:00 2001 From: Lee Seungjun <82270058+ValseLee@users.noreply.github.com> Date: Mon, 25 Sep 2023 00:54:22 +0900 Subject: [PATCH 3316/4563] Update 0406-async-stream-backpressure.md 1. fixed several typos --- proposals/0406-async-stream-backpressure.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/0406-async-stream-backpressure.md b/proposals/0406-async-stream-backpressure.md index 2df9bb7748..74e88562bf 100644 --- a/proposals/0406-async-stream-backpressure.md +++ b/proposals/0406-async-stream-backpressure.md @@ -386,7 +386,7 @@ extension AsyncStream { /// /// Call this method to cancel a callback enqueued by the ``enqueueCallback(callbackToken:onProduceMore:)`` method. /// - /// - Note: This methods supports being called before ``enqueueCallback(callbackToken:onProduceMore:)`` is called and + /// - Note: This method supports being called before ``enqueueCallback(callbackToken:onProduceMore:)`` is called and /// will mark the passed `token` as cancelled. /// /// - Parameter token: The callback token. @@ -442,7 +442,7 @@ extension AsyncStream { /// Write the elements of the asynchronous sequence to the asynchronous stream. /// - /// This method returns once the provided asynchronous sequence or the the asynchronous stream finished. + /// This method returns once the provided asynchronous sequence or the asynchronous stream finished. /// /// - Important: This method does not finish the source if consuming the upstream sequence terminated. /// @@ -477,11 +477,11 @@ extension AsyncThrowingStream { /// /// Use this source to provide elements to the stream by calling one of the `write` methods, then terminate the stream normally /// by calling the `finish()` method. You can also use the source's `finish(throwing:)` method to terminate the stream by - /// throwing an error + /// throwing an error. public struct Source: Sendable { /// A strategy that handles the backpressure of the asynchronous stream. public struct BackpressureStrategy: Sendable { - /// When the high watermark is reached producers will be suspended. All producers will be resumed again once + /// When the high watermark is reached, producers will be suspended. All producers will be resumed again once /// the low watermark is reached. public static func watermark(low: Int, high: Int) -> BackpressureStrategy {} } @@ -546,7 +546,7 @@ extension AsyncThrowingStream { /// /// Call this method to cancel a callback enqueued by the ``enqueueCallback(callbackToken:onProduceMore:)`` method. /// - /// - Note: This methods supports being called before ``enqueueCallback(callbackToken:onProduceMore:)`` is called and + /// - Note: This method supports being called before ``enqueueCallback(callbackToken:onProduceMore:)`` is called and /// will mark the passed `token` as cancelled. /// /// - Parameter token: The callback token. @@ -695,7 +695,7 @@ consuming the stream at the same time. This can be solved via additional algorithms such as `broadcast` in the `swift-async-algorithms` package. To give developers more time to adopt the new APIs the deprecation of the -current APIs should deferred to a future version. Especially since those new +current APIs should be deferred to a future version. Especially since those new APIs are not backdeployed like the current Concurrency runtime. ### Introduce a `Writer` and an `AsyncWriter` protocol @@ -765,7 +765,7 @@ the current pattern of setting the `onTermination` closure on the source. ### Provide a `onConsumerCancellation` callback -During the pitch phase it was raised that we should provide a +During the pitch phase, it was raised that we should provide a `onConsumerCancellation` callback which gets invoked once the asynchronous stream notices that the consuming task got cancelled. This callback could be used to customize how cancellation is handled by the stream e.g. one could From a82e32b086ecb745d202bc66aca02645dcc0c7d6 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Mon, 25 Sep 2023 09:35:51 -0700 Subject: [PATCH 3317/4563] Mark proposals 0378 and 0391 as implemented --- proposals/0378-package-registry-auth.md | 2 +- proposals/0391-package-registry-publish.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0378-package-registry-auth.md b/proposals/0378-package-registry-auth.md index 6202f1b903..5b20615c83 100644 --- a/proposals/0378-package-registry-auth.md +++ b/proposals/0378-package-registry-auth.md @@ -3,7 +3,7 @@ * Proposal: [SE-0378](0378-package-registry-auth.md) * Author: [Yim Lee](https://github.com/yim-lee) * Review Manager: [Tom Doron](https://github.com/tomerd) -* Status: **Accepted** +* Status: **Implemented (Swift 5.8)** * Implementation: [apple/swift-package-manager#5838](https://github.com/apple/swift-package-manager/pull/5838) * Review: * Pitch: https://forums.swift.org/t/pitch-package-registry-authentication/61047 diff --git a/proposals/0391-package-registry-publish.md b/proposals/0391-package-registry-publish.md index d940445a3f..c510471d81 100644 --- a/proposals/0391-package-registry-publish.md +++ b/proposals/0391-package-registry-publish.md @@ -3,7 +3,7 @@ * Proposal: [SE-0391](0391-package-registry-publish.md) * Author: [Yim Lee](https://github.com/yim-lee) * Review Manager: [Tom Doron](https://github.com/tomerd) -* Status: **Accepted** +* Status: **Implemented (Swift 5.9)** * Implementation: * https://github.com/apple/swift-package-manager/pull/6101 * https://github.com/apple/swift-package-manager/pull/6146 From 8ac34f91164e06a4fda373e8f237abcd65bfb261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 28 Sep 2023 13:58:20 -0700 Subject: [PATCH 3318/4563] [SE-0409] Document the relationship between access level and scoped imports --- proposals/0409-access-level-on-imports.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/proposals/0409-access-level-on-imports.md b/proposals/0409-access-level-on-imports.md index 4a47da59c0..6db73ec14d 100644 --- a/proposals/0409-access-level-on-imports.md +++ b/proposals/0409-access-level-on-imports.md @@ -208,7 +208,7 @@ Where the tool is unavailable, a simple script can insert a `public` modifier in The upcoming feature flag `InternalImports` will enable the Swift 6 behavior even when using Swift 5. -### Relation with other attributes on imports +### Relation with other imports modifiers The `@_exported` attribute is a step above a `public` import as clients see the imported module declarations is if they were part of the local module. @@ -227,6 +227,15 @@ After replacing with an internal import, the transitive dependency loading requi but will change for non-resilient modules where transitive dependencies must always be loaded. In all cases, updating modules relying on `@_implementationOnly` to instead use internal imports is strongly encouraged. +The scoped imports feature remains independent from the access level declared on the same import. +Given the example below, the module `Foo` is a public dependency at the module-level and can be referenced from public declaration signatures in the local source file. +The scoped part, `struct Foo.Bar`, is a hint to the name lookup logic to prioritize resolving references to `Bar` as this one compared to other `Bar` from other imports. +Since there's no interactions between the two features, +scoped imports cannot be used to restrict the access level of a single declaration. +``` +public import struct Foo.Bar +``` + ## Source compatibility To preserve source compatibility, imports are public by default in Swift 5. From a2501fd4b2f7495bfb59c8d80b50e0bbebbc0f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 28 Sep 2023 13:59:31 -0700 Subject: [PATCH 3319/4563] [SE-0409] Add some context on why we load dependencies in non-resilient cases --- proposals/0409-access-level-on-imports.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/0409-access-level-on-imports.md b/proposals/0409-access-level-on-imports.md index 6db73ec14d..fa17df9a19 100644 --- a/proposals/0409-access-level-on-imports.md +++ b/proposals/0409-access-level-on-imports.md @@ -167,6 +167,9 @@ if none of these apply the dependency can be hidden. 1. Public dependencies must always be loaded by transitive clients. 2. All dependencies of a non-resilient module must be loaded by transitive clients. + The compiler need the dependencies to access information required at build time for non-resilient modules, + the same information is provided at run time by resilient modules. + This restriction is discussed further in the future directions section. 3. Package dependencies must be loaded by transitive clients if the middle module and the transitive client are part of the same package. This allows for the signature of package declarations to reference that dependency. From accbd05fce7805a8523d9b6d23ae7e9f953ee3ef Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 29 Sep 2023 11:19:31 -0700 Subject: [PATCH 3320/4563] Accept SE-0408: Pack Iteration (#2176) --- proposals/0408-pack-iteration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0408-pack-iteration.md b/proposals/0408-pack-iteration.md index 55635a73eb..143883e5e2 100644 --- a/proposals/0408-pack-iteration.md +++ b/proposals/0408-pack-iteration.md @@ -3,9 +3,9 @@ * Proposal: [SE-0408](0408-pack-iteration.md) * Authors: [Sima Nerush](https://github.com/simanerush), [Holly Borla](https://github.com/hborla) * Review Manager: [Doug Gregor](https://github.com/DougGregor/) -* Status: **Active Review (September 6...19, 2023)** +* Status: **Accepted** * Implementation: [apple/swift#67594](https://github.com/apple/swift/pull/67594) (gated behind flag `-enable-experimental-feature PackIteration`) -* Review: ([pitch](https://forums.swift.org/t/pitch-enable-pack-iteration/66168), [review](https://forums.swift.org/t/review-se-0408-pack-iteration/67152)) +* Review: ([pitch](https://forums.swift.org/t/pitch-enable-pack-iteration/66168), [review](https://forums.swift.org/t/review-se-0408-pack-iteration/67152), [acceptance](https://forums.swift.org/t/accepted-se-0408-pack-iteration/67598)) ## Introduction From b37772568b4735a0cc8677ad214d3a3d2375ad0c Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 29 Sep 2023 16:20:49 -0700 Subject: [PATCH 3321/4563] Feedback from pitch --- proposals/NNNN-atomics.md | 315 +++++++++++++++++++++----------------- 1 file changed, 173 insertions(+), 142 deletions(-) diff --git a/proposals/NNNN-atomics.md b/proposals/NNNN-atomics.md index b47d801ccc..d075e4f990 100644 --- a/proposals/NNNN-atomics.md +++ b/proposals/NNNN-atomics.md @@ -4,7 +4,7 @@ * Author: [Karoy Lorentey](https://github.com/lorentey), [Alejandro Alonso](https://github.com/Azoy) * Review Manager: TBD * Bug: [SR-9144](https://github.com/apple/swift/issues/51640) -* Implementation: N/A +* Implementation: https://github.com/apple/swift/pull/68857 * Version: 2023-09-18 * Status: **Awaiting review** @@ -23,7 +23,7 @@ This proposal adds a limited set of low-level atomic operations to the Standard Previous Swift-evolution thread: [Low-Level Atomic Operations](https://forums.swift.org/t/low-level-atomic-operations/34683) -New Swift-evolution thread: [Atomics]() +New Swift-evolution thread: [Atomics](https://forums.swift.org/t/atomics/67350) ## Revision History @@ -42,6 +42,7 @@ New Swift-evolution thread: [Atomics]() * [The Atomic Protocol Hierarchy](#the-atomic-protocol-hierarchy) * [Optional Atomics](#optional-atomics) * [Custom Atomic Types](#custom-atomic-types) + * [Atomic Storage Types](#atomic-storage-types) * [DoubleWord](#doubleword) * [The Atomic type](#the-atomic-type) * [Basic Atomic Operations](#basic-atomic-operations) @@ -54,8 +55,9 @@ New Swift-evolution thread: [Atomics]() * [Detailed Design](#detailed-design) * [Atomic Memory Orderings](#atomic-memory-orderings-1) * [Atomic Protocols](#atomic-protocols) - * [AtomicStorage](#atomicstorage) * [AtomicValue](#atomicvalue) + * [AtomicOptionalWrappable](#atomicoptionalwrappable) + * [Atomic Storage Types](#atomic-storage-types-1) * [DoubleWord](#doubleword-1) * [Atomic Types](#atomic-types) * [Atomic<Value>](#atomicvalue-1) @@ -143,7 +145,7 @@ The atomic constructs later in this proposal implement concurrent read/write acc However, this alone does not give us a way to synchronize accesses to regular variables, or between atomic accesses to different memory locations. To support such synchronization, each atomic operation can be configured to also act as a synchronization point for other variable accesses within the same thread, preventing previous accesses from getting executed after the atomic operation, and/or vice versa. Atomic operations on another thread can then synchronize with the same point, establishing a strict (although partial) timeline between accesses performed by both threads. This way, we can reason about the possible ordering of operations across threads, even if we know nothing about how those operations are implemented. (This is how locks or dispatch queues can be used to serialize the execution of arbitrary blocks containing regular accesses to shared variables.) For more details, see \[[C++17], [N2153], [Boehm 2008]]. -In order to enable atomic synchronization within Swift, we must first introduce memory orderings who will give us control of the timeline of these operations across threads. Luckily, with the acceptance of [Clarify the Swift memory consistency model (SE-0282)](https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md), Swift already adopts the C/C++ concurrency memory model. In this model, concurrent access to shared state remains undefined behavior unless all such access is forced into a conflict-free timeline through explicit synchronization operations. +In order to enable atomic synchronization within Swift, we must first introduce memory orderings that will give us control of the timeline of these operations across threads. Luckily, with the acceptance of [Clarify the Swift memory consistency model (SE-0282)](https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md), Swift already adopts the C/C++ concurrency memory model. In this model, concurrent access to shared state remains undefined behavior unless all such access is forced into a conflict-free timeline through explicit synchronization operations. This proposal introduces five distinct memory orderings, organized into three logical groups, from loosest to strictest: @@ -196,7 +198,7 @@ Every atomic operation introduced later in this proposal requires an ordering ar Projects that prefer to default to sequentially consistent ordering are welcome to add non-public `Atomic` extensions that implement that. However, we expect that providing an implicit default ordering would be highly undesirable in most production uses of atomics. -We also provide a top-level function called `atomicMemoryFence` that allows issuing a memory ordering constraint without directly associating it with a particular atomic operation. This corresponds to `std::memory_thread_fence` in C++ [[C++17]]. +We also provide a top-level function called `atomicMemoryFence` that allows issuing a memory ordering constraint without directly associating it with a particular atomic operation. This corresponds to `std::atomic_thread_fence` in C++ [[C++17]]. ```swift /// Establishes a memory ordering without associating it with a @@ -233,7 +235,7 @@ The notion of an atomic type is captured by the `AtomicValue` protocol. /// A type that supports atomic operations through a separate atomic storage /// representation. public protocol AtomicValue { - associatedtype AtomicRepresentation: AtomicStorage + associatedtype AtomicRepresentation static func encodeAtomicRepresentation( _ value: consuming Self @@ -245,57 +247,9 @@ public protocol AtomicValue { } ``` -Backing the atomic representation is an `AtomicStorage` protocol that defines all of the atomic operations one must implement in order for the type itself to be used when lowering atomic operations. - -```swift -/// The storage representation for an atomic value, providing pointer-based -/// atomic operations. This is a low-level implementation detail of atomic -/// types. -public protocol AtomicStorage { - ... -} -``` - -While `AtomicStorage` is a public protocol, its requirements are considered an implementation detail of the Standard Library. (They are replaced by ellipses above.) - The requirements in `AtomicValue` set up a bidirectional mapping between values of the atomic type and an associated storage representation that implements the actual primitive atomic operations. -Following existing Standard Library conventions for such interfaces, the names of all associated types and member requirements of the `AtomicStorage` protocol start with a leading underscore character. As with any other underscored interface exposed by the Standard Library, code that manually implements or directly uses these underscored requirements may fail to compile (or correctly run) when built using any Swift release other than the one for which it was initially written. - -The full set of standard types implementing `AtomicStorage` is listed below: - -```swift -extension Int { - public struct AtomicRepresentation: AtomicStorage {...} -} -extension Int8 { - public struct AtomicRepresentation: AtomicStorage {...} -} -extension Int16 { - public struct AtomicRepresentation: AtomicStorage {...} -} -extension Int32 { - public struct AtomicRepresentation: AtomicStorage {...} -} -extension Int64 { - public struct AtomicRepresentation: AtomicStorage {...} -} -extension UInt { - public struct AtomicRepresentation: AtomicStorage {...} -} -extension UInt8 { - public struct AtomicRepresentation: AtomicStorage {...} -} -extension UInt16 { - public struct AtomicRepresentation: AtomicStorage {...} -} -extension UInt32 { - public struct AtomicRepresentation: AtomicStorage {...} -} -extension UInt64 { - public struct AtomicRepresentation: AtomicStorage {...} -} -``` +`AtomicRepresentation` is intentionally left unconstrained because as you'll see later in the proposal, atomic operations are only available when `AtomicRepresentation` is one of the core atomic storage types found here: [Atomic Storage Types](#atomic-storage-types). The full set of standard types implementing `AtomicValue` is listed below: @@ -324,10 +278,28 @@ extension Optional: AtomicValue where Wrapped: AtomicValue, ... {...} #### Optional Atomics -The standard atomic pointer types and unmanaged references also support atomic operations on their optional-wrapped form. `Optional` implements this through a conditional conformance to `AtomicValue`; the exact constraint is an implementation detail. (It works by requiring the wrapped type's internal atomic storage representation to support a special nil value.) +The standard atomic pointer types and unmanaged references also support atomic operations on their optional-wrapped form. To spell out this optional wrapped, we introduce a new protocol: + +```swift +public protocol AtomicOptionalWrappable: AtomicValue { + associatedtype AtomicOptionalRepresentation + + static func encodeAtomicOptionalRepresentation( + _ value: consuming Self? + ) -> AtomicOptionalRepresentation + + static func decodeAtomicOptionalRepresentation( + _ representation: consuming AtomicOptionalRepresentation + ) -> Self? +} +``` + +Similar to `AtomicValue`, `AtomicOptionalWrappable`'s requirements create a bidirectional mapping between an optional value of `Self` to some atomic optional storage representation and vice versa. + +`Optional` implements `AtomicValue` through a conditional conformance to this new `AtomicOptionalWrappable` protocol. ```swift -extension Optional: AtomicValue where Wrapped: AtomicValue, ... { +extension Optional: AtomicValue where Wrapped: AtomicOptionalWrappable { ... } ``` @@ -335,11 +307,11 @@ extension Optional: AtomicValue where Wrapped: AtomicValue, ... { This proposal enables optional-atomics support for the following types: ```swift -UnsafeRawPointer -UnsafeMutableRawPointer -UnsafePointer -UnsafeMutablePointer -Unmanaged +extension UnsafeRawPointer: AtomicOptionalWrappable {} +extension UnsafeMutableRawPointer: AtomicOptionalWrappable {} +extension UnsafePointer: AtomicOptionalWrappable {} +extension UnsafeMutablePointer: AtomicOptionalWrappable {} +extension Unmanaged: AtomicOptionalWrappable {} ``` User code is not allowed to extend this list with additional types; this capability is reserved for potential future proposals. @@ -356,8 +328,8 @@ class LockFreeSingleConsumerStack { } typealias NodePtr = UnsafeMutablePointer - private var _last = Atomic(nil) - private var _consumerCount = Atomic(0) + private let _last = Atomic(nil) + private let _consumerCount = Atomic(0) deinit { // Discard remaining nodes @@ -443,6 +415,34 @@ if currentState.compareExchange( currentState.store(.stopped, ordering: .sequentiallyConsistent) ``` +### Atomic Storage Types + +Fundamental to working with atomics is knowing that CPUs can only do atomic operations on integers. While we could theoretically do atomic operations with our current list of standard library integer types (`Int8`, `Int16`, ...), some platforms don't ensure that these types have the same alignment as their size. For example, `Int64` and `UInt64` have 4 byte alignment on i386. Atomic operations must occur on correctly aligned types. To ensure this, we need to introduce helper types that all atomic operations will be traffiked through: + +```swift +public struct AtomicInt8Storage {} +public struct AtomicInt16Storage {} +public struct AtomicInt32Storage {} +public struct AtomicInt64Storage {} +public struct AtomicInt128Storage {} +``` + +Because these are the fundamental atomic storage types, they will serve as the `AtomicRepresentation` for all of the standard integer types: + +```swift +extension Int8: AtomicValue { + public typealias AtomicRepresentation = AtomicInt8Storage +} + +... + +extension UInt64: AtomicValue { + public typealias AtomicRepresentation = AtomicInt64Storage +} + +... +``` + ### `DoubleWord` In their current single-word form, atomic pointer and reference types are susceptible to a class of race condition called the *ABA problem*. A freshly allocated object often happens to be placed at the same memory location as a recently deallocated one. Therefore, two successive `load`s of a simple atomic pointer may return the exact same value, even though the pointer may have received an arbitrary number of updates between the two loads, and the pointee may have been completely replaced. This can be a subtle, but deadly source of race conditions in naive implementations of many concurrent data structures. @@ -453,16 +453,21 @@ We propose a new separate type that provides an abstraction over the layout of w ```swift public struct DoubleWord { - public var first: UInt { get } - public var second: UInt { get } + public var highWord: UInt { get } + public var lowWord: UInt { get } - public init(first: UInt, second: UInt) + public init(highWord: UInt, lowWord: UInt) } extension DoubleWord: AtomicValue { - public struct AtomicRepresentation: AtomicStorage { - ... - } +// Not a real compilation conditional +#if 64 bit + public typealias AtomicRepresentaton = AtomicInt128Storage +#elseif 32 bit + public typealias AtomicRepresentation = AtomicInt64Storage +#else +#error("Not a supported platform") +#endif ... } @@ -489,10 +494,10 @@ Now that we know how to create an atomic value, it's time to introduce some actu ### Basic Atomic Operations -`Atomic` provides seven basic atomic operations for all supported types: +`Atomic` provides seven basic atomic operations when `Value.AtomicRepresenation` is one of the fundamental atomic storage types: ```swift -extension Atomic { +extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { /// Atomically loads and returns the current value, applying the specified /// memory ordering. /// @@ -661,6 +666,8 @@ extension Atomic { } ``` +Because these are only available when `Value.AtomicRepresentation == AtomicIntNNStorage`, some atomic specializations may not support atomic operations at all. + The first three operations are relatively simple: - `load` returns the current value. @@ -712,18 +719,22 @@ These specialized integer operations generally come in two variants, based on wh | Method Name | Returns | Implements | | --- | --- | --- | -| `loadThenWrappingIncrement(by:ordering:)` | original value | `a &+= b` | -| `loadThenWrappingDecrement(by:ordering:)` | original value | `a &-= b` | -| `loadThenBitwiseAnd(with:ordering)` | original value | `a &= b` | -| `loadThenBitwiseOr(with:ordering)` | original value | `a \|= b` | -| `loadThenBitwiseXor(with:ordering)` | original value | `a ^= b` | -| `wrappingIncrementThenLoad(by:ordering:)` | new value | `a &+= b` | -| `wrappingDecrementThenLoad(by:ordering:)` | new value |`a &-= b` | -| `bitwiseAndThenLoad(with:ordering)` | new value |`a &= b` | -| `bitwiseOrThenLoad(with:ordering)` | new value |`a \|= b` | -| `bitwiseXorThenLoad(with:ordering)` | new value |`a ^= b` | -| `wrappingIncrement(by:ordering:)` | none | `a &+= b` | -| `wrappingDecrement(by:ordering:)` | none | `a &-= b` | +| `loadThenWrappingIncrement(by:ordering:)` | original value | `a &+= b` | +| `loadThenWrappingDecrement(by:ordering:)` | original value | `a &-= b` | +| `loadThenBitwiseAnd(with:ordering:)` | original value | `a &= b` | +| `loadThenBitwiseOr(with:ordering:)` | original value | `a \|= b` | +| `loadThenBitwiseXor(with:ordering:)` | original value | `a ^= b` | +| `loadThenMin(with:ordering:)` | original value | `a = Swift.min(a, b)` | +| `loadThenMax(with:ordering:` | original value | `a = Swift.max(a, b)` | +| `wrappingIncrementThenLoad(by:ordering:)` | new value | `a &+= b` | +| `wrappingDecrementThenLoad(by:ordering:)` | new value |`a &-= b` | +| `bitwiseAndThenLoad(with:ordering:)` | new value |`a &= b` | +| `bitwiseOrThenLoad(with:ordering:)` | new value |`a \|= b` | +| `bitwiseXorThenLoad(with:ordering:)` | new value |`a ^= b` | +| `minThenLoad(with:ordering:)` | new value | `a = Swift.min(a, b)` | +| `maxThenLoad(with:ordering:` | new value | `a = Swift.max(a, b)` | +| `wrappingIncrement(by:ordering:)` | none | `a &+= b` | +| `wrappingDecrement(by:ordering:)` | none | `a &-= b` | The `wrappingIncrement` and `wrappingDecrement` operations are provided as a convenience for incrementing/decrementing values in the common case when a return value is not required. @@ -868,40 +879,39 @@ Note that unlike the rest of the atomic types, `load` and `storeIfNilThenLoad(_: Modeling orderings as regular function parameters allows us to specify them using syntax that's familiar to all Swift programmers. Unfortunately, it means that in the implementation of atomic operations we're forced to switch over the ordering argument: ```swift -extension Int: AtomicStorage { - public func atomicCompareExchange( - expected: Int, - desired: Int, - at address: UnsafeMutablePointer, +extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { + public borrowing func compareExchange( + expected: consuming Value, + desired: consuming Value, ordering: AtomicUpdateOrdering ) -> (exchanged: Bool, original: Int) { // Note: This is a simplified version of the actual implementation let won: Bool - let oldValue: Int + let oldValue: Value switch ordering { case .relaxed: - (oldValue, won) = Builtin.cmpxchg_monotonic_monotonic_Word( + (oldValue, won) = Builtin.cmpxchg_monotonic_monotonic_IntNN( address, expected, desired ) case .acquiring: - (oldValue, won) = Builtin.cmpxchg_acquire_acquire_Word( + (oldValue, won) = Builtin.cmpxchg_acquire_acquire_IntNN( address, expected, desired ) case .releasing: - (oldValue, won) = Builtin.cmpxchg_release_monotonic_Word( + (oldValue, won) = Builtin.cmpxchg_release_monotonic_IntNN( address, expected, desired ) case .acquiringAndReleasing: - (oldValue, won) = Builtin.cmpxchg_acqrel_acquire_Word( + (oldValue, won) = Builtin.cmpxchg_acqrel_acquire_IntNN( address, expected, desired ) case .sequentiallyConsistent: - (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_Word( + (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_IntNN( address, expected, desired ) @@ -1081,38 +1091,11 @@ public func atomicMemoryFence(ordering: AtomicUpdateOrdering) ### Atomic Protocols -#### `AtomicStorage` - -```swift -public protocol AtomicStorage { - // Requirements aren't public API -} -``` - -This protocol supplies the actual primitive atomic operations for conformers. The exact requirements are a private implementation detail of the Standard Library. They are outside the scope of the Swift Evolution process and they may arbitrarily change between library releases. User code must not directly use them or manually implement them. - -Conforming types: - -```swift -extension Int.AtomicRepresentation: AtomicStorage {...} -extension Int64.AtomicRepresentation: AtomicStorage {...} -extension Int32.AtomicRepresentation: AtomicStorage {...} -extension Int16.AtomicRepresentation: AtomicStorage {...} -extension Int8.AtomicRepresentation: AtomicStorage {...} -extension UInt.AtomicRepresentation: AtomicStorage {...} -extension UInt64.AtomicRepresentation: AtomicStorage {...} -extension UInt32.AtomicRepresentation: AtomicStorage {...} -extension UInt16.AtomicRepresentation: AtomicStorage {...} -extension UInt8.AtomicRepresentation: AtomicStorage {...} - -extension DoubleWord.AtomicRepresentation: AtomicStorage {...} -``` - #### `AtomicValue` ```swift public protocol AtomicValue { - associatedtype AtomicRepresentation: AtomicStorage + associatedtype AtomicRepresentation static func encodeAtomicRepresentation( _ value: consuming Self @@ -1150,33 +1133,57 @@ extension UnsafePointer: AtomicValue {...} extension UnsafeMutablePointer: AtomicValue {...} extension Unmanaged: AtomicValue {...} -extension Optional: AtomicValue where Wrapped: AtomicValue, ... {...} +extension Optional: AtomicValue where Wrapped: AtomicOptionalWrappable {...} +``` + +To support custom "atomic-representable" types, `AtomicValue` also comes with default implementations for all its requirements for `RawRepresentable` types whose `RawValue` is also atomic: + +```swift +extension RawRepresentable where Self: AtomicValue, RawValue: AtomicValue, ... { + // Implementations for all requirements. +} ``` -The exact constraints on `Optional`'s conditional conformance are a private implementation detail. (They specify that the underlying (private) storage representation must be able to represent an extra `nil` value.) +The omitted constraint sets up the (private) atomic storage type to match that of the `RawValue`. The default implementations work by converting values to their `rawValue` form, and forwarding all atomic operations to it. + +#### `AtomicOptionalWrappable` + +```swift +public protocol AtomicOptionalWrappable: AtomicValue { + associatedtype AtomicOptionalRepresentation + + static func encodeAtomicOptionalRepresentation( + _ value: consuming Self? + ) -> AtomicOptionalRepresentation + + static func decodeAtomicOptionalRepresentation( + _ representation: consuming AtomicOptionalRepresentation + ) -> Self? +} +``` Atomic `Optional` operations are currently enabled for the following `Wrapped` types: ```swift -UnsafeRawPointer -UnsafeMutableRawPointer -UnsafePointer -UnsafeMutablePointer -Unmanaged +extension UnsafeRawPointer: AtomicOptionalWrappable {} +extension UnsafeMutableRawPointer: AtomicOptionalWrappable {} +extension UnsafePointer: AtomicOptionalWrappable {} +extension UnsafeMutablePointer: AtomicOptionalWrappable {} +extension Unmanaged: AtomicOptionalWrappable {} ``` User code is not allowed to extend this list with additional types; this capability is reserved for potential future proposals. -To support custom "atomic-representable" types, `AtomicValue` also comes with default implementations for all its requirements for `RawRepresentable` types whose `RawValue` is also atomic: +### Atomic Storage Types ```swift -extension RawRepresentable where Self: AtomicValue, RawValue: AtomicValue, ... { - // Implementations for all requirements. -} +public struct AtomicInt8Storage {...} +public struct AtomicInt16Storage {...} +public struct AtomicInt32Storage {...} +public struct AtomicInt64Storage {...} +public struct AtomicInt128Storage {...} ``` -The omitted constraint sets up the (private) atomic storage type to match that of the `RawValue`. The default implementations work by converting values to their `rawValue` form, and forwarding all atomic operations to it. - ### `DoubleWord` ```swift @@ -1186,6 +1193,8 @@ public struct DoubleWord { public init(first: UInt, second: UInt) } + +extension DoubleWord: AtomicValue {...} ``` ### Atomic Types @@ -1195,7 +1204,9 @@ public struct DoubleWord { ```swift public struct Atomic: ~Copyable { public init(_ initialValue: consuming Value) +} +extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { // Atomic operations: public borrowing func load(ordering: AtomicLoadOrdering) -> Value @@ -1273,32 +1284,52 @@ extension Atomic where Value == Int { ) public borrowing func loadThenBitwiseAnd( - _ operand: Value, + with operand: Value, ordering: AtomicUpdateOrdering ) -> Value public borrowing func bitwiseAndThenLoad( - _ operand: Value, + with operand: Value, ordering: AtomicUpdateOrdering ) -> Value public borrowing func loadThenBitwiseOr( - _ operand: Value, + with operand: Value, ordering: AtomicUpdateOrdering ) -> Value public borrowing func bitwiseOrThenLoad( - _ operand: Value, + with operand: Value, ordering: AtomicUpdateOrdering ) -> Value public borrowing func loadThenBitwiseXor( - _ operand: Value, + with operand: Value, ordering: AtomicUpdateOrdering ) -> Value public borrowing func bitwiseXorThenLoad( - _ operand: Value, + with operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func loadThenMin( + with operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func minThenLoad( + with operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func loadThenMax( + with operand: Value, + ordering: AtomicUpdateOrdering + ) -> Value + + public borrowing func maxThenLoad( + with operand: Value, ordering: AtomicUpdateOrdering ) -> Value } From ba1ba28f74498b1338dc467ba93fcbde4703d293 Mon Sep 17 00:00:00 2001 From: Romain Pouclet Date: Sun, 1 Oct 2023 14:56:41 -0700 Subject: [PATCH 3322/4563] Update url to swift package registry spec (#2177) --- proposals/0292-package-registry-service.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0292-package-registry-service.md b/proposals/0292-package-registry-service.md index 0dc81724bb..9cfafb0832 100644 --- a/proposals/0292-package-registry-service.md +++ b/proposals/0292-package-registry-service.md @@ -125,7 +125,7 @@ and downloading the source archive for a release: | `GET` | `/identifiers{?url}` | Lookup package identifiers registered for a URL | A formal specification for the package registry interface is provided -[alongside this proposal](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md). +[alongside this proposal](https://github.com/apple/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md). In addition, an OpenAPI (v3) document and a reference implementation written in Swift From f2d9868ab0826c1b66e7da27633e07dd146c53e6 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 2 Oct 2023 18:14:34 -0700 Subject: [PATCH 3323/4563] More feedback --- proposals/NNNN-atomics.md | 447 ++++++++++++++++++++------------------ 1 file changed, 230 insertions(+), 217 deletions(-) diff --git a/proposals/NNNN-atomics.md b/proposals/NNNN-atomics.md index d075e4f990..9d1f3900c4 100644 --- a/proposals/NNNN-atomics.md +++ b/proposals/NNNN-atomics.md @@ -37,7 +37,6 @@ New Swift-evolution thread: [Atomics](https://forums.swift.org/t/atomics/67350) * [Motivation](#motivation) * [Proposed Solution](#proposed-solution) - * [The Synchronization Module](#the-synchronization-module) * [Atomic Memory Orderings](#atomic-memory-orderings) * [The Atomic Protocol Hierarchy](#the-atomic-protocol-hierarchy) * [Optional Atomics](#optional-atomics) @@ -49,7 +48,6 @@ New Swift-evolution thread: [Atomics](https://forums.swift.org/t/atomics/67350) * [Specialized Integer Operations](#specialized-integer-operations) * [Specialized Boolean Operations](#specialized-boolean-operations) * [Atomic Lazy References](#atomic-lazy-references) - * [Restricting Ordering Arguments to Compile\-Time Constants](#restricting-ordering-arguments-to-compile-time-constants) * [Interaction with Existing Language Features](#interaction-with-existing-language-features) * [Interaction with Swift Concurrency](#interaction-with-swift-concurrency) * [Detailed Design](#detailed-design) @@ -105,12 +103,11 @@ Following the acceptance of [Clarify the Swift memory consistency model (SE-0282 ## Proposed Solution -We propose to introduce new low-level atomic APIs to the standard library via a new module. These atomic APIs will serve as the foundation for building higher-level concurrent code directly in Swift. +We propose to introduce new low-level atomic APIs to the standard library. These atomic APIs will serve as the foundation for building higher-level concurrent code directly in Swift. As a quick taste, this is how atomics will work: ```swift -import Synchronization import Dispatch let counter = Atomic(0) @@ -124,21 +121,6 @@ DispatchQueue.concurrentPerform(iterations: 10) { _ in print(counter.load(ordering: .relaxed)) ``` -### The Synchronization Module - -While most Swift programs won't directly use the new atomic primitives, we still consider the new constructs to be an integral part of the core Standard Library. - - * The implementation of atomic operations needs access to compiler intrinsics that are only exposed to the Standard Library. - * The memory orderings introduced here define a concurrency memory model for Swift code that has implications on the language as a whole. (Fortunately, Swift is already designed to interoperate with the C/C++ memory model, so introducing a subset of C++ memory orderings in the Standard Library doesn't by itself require language-level changes.) - -That said, it seems highly undesirable to add low-level atomics to the default namespace of every Swift program, so we propose to place the atomic constructs in a new Standard Library module called `Synchronization`. Code that needs to use low-level atomics will need to explicitly import the new module: - -```swift -import Synchronization -``` - -We expect that most Swift projects will use atomic operations only indirectly, through higher-level synchronization constructs. Therefore, importing the Synchronization module will be a relatively rare occurrence, mostly limited to projects that implement such tools. - ### Atomic Memory Orderings The atomic constructs later in this proposal implement concurrent read/write access by mapping to atomic instructions in the underlying architecture. All accesses of a particular atomic value get serialized into some global sequential timeline, no matter what thread executed them. @@ -164,37 +146,46 @@ These align with select members of the standard `std::memory_order` enumeration | `std::memory_order_acq_rel` | `.acquiringAndReleasing` | | `std::memory_order_seq_cst` | `.sequentiallyConsistent` | -Atomic orderings are grouped into three frozen structs based on the kind of operation to which they are attached, as listed below. By modeling these as separate types, we can ensure that unsupported operation/ordering combinations (such as an atomic "releasing load") will lead to clear compile-time errors: +Atomic orderings are nested in a memory ordering "namespace". Each ordering has its own type and a static getter to retrieve a value of the ordering type. We've modeled it this way to present ordering overloads for atomic operations as you'll see later in the proposal. ```swift -/// Specifies the memory ordering semantics of an atomic load operation. -public struct AtomicLoadOrdering { - public static var relaxed: Self { get } - public static var acquiring: Self { get } - public static var sequentiallyConsistent: Self { get } +// Specifies the memory ordering to be applied to any atomic operation. +public enum AtomicMemoryOrdering { + public struct Relaxed { + public static var relaxed: Self { get } + } + + public struct Acquiring { + public static var acquiring: Self { get } + } + + public struct Releasing { + public static var releasing: Self { get } + } + + public struct AcquiringAndReleasing { + public static var acquiringAndReleasing: Self { get } + } + + public struct SequentiallyConsistent { + public static var sequentiallyConsistent: Self { get } + } } +``` -/// Specifies the memory ordering semantics of an atomic store operation. -public struct AtomicStoreOrdering { - public static var relaxed: Self { get } - public static var releasing: Self { get } - public static var sequentiallyConsistent: Self { get } -} +Every atomic operation introduced later in this proposal requires an ordering argument. We consider these ordering arguments to be an essential part of these low-level atomic APIs, and we require an explicit `ordering` argument on all atomic operations. The intention here is to force developers to carefully think about what ordering they need to use, each time they use one of these primitives. (Perhaps more importantly, this also makes it obvious to readers of the code what ordering is used -- making it far less likely that an unintended default `.sequentiallyConsistent` ordering slips through code review.) -/// Specifies the memory ordering semantics of an atomic read-modify-write -/// operation. -public struct AtomicUpdateOrdering { - public static var relaxed: Self { get } - public static var acquiring: Self { get } - public static var releasing: Self { get } - public static var acquiringAndReleasing: Self { get } - public static var sequentiallyConsistent: Self { get } -} -``` +In addition to every atomic operation requiring an explicit ordering, each operation will have overloads for each accepted memory ordering. This has the benefit of every application of an atomic operation to have a single representation that will always produce the best result as opposed to switching over the ordering in the implementation at runtime potentially. We go more into detail about the though process for this design in [Alternative Designs for Memory Orderings](#alternative-designs-for-memory-orderings). -These structs behave like non-frozen enums with a known (non-public) raw representation. This allows us to define additional memory orderings in the future (if and when they become necessary, specifically `std::memory_order_consume`) while making use of the known representation to optimize existing cases. (These cannot be frozen enums because that would prevent us from adding more orderings, but regular resilient enums can't freeze their representation, and the layout indirection interferes with guaranteed optimizations, especially in -Onone.) +All atomic operations later proposed in this document will be simplified because there are lots of overloads for each memory ordering argument. To make this simpler, we're going to define the following: -Every atomic operation introduced later in this proposal requires an ordering argument. We consider these ordering arguments to be an essential part of these low-level atomic APIs, and we require an explicit `ordering` argument on all atomic operations. The intention here is to force developers to carefully think about what ordering they need to use, each time they use one of these primitives. (Perhaps more importantly, this also makes it obvious to readers of the code what ordering is used -- making it far less likely that an unintended default `.sequentiallyConsistent` ordering slips through code review.) +```swift +typealias LoadOrdering = Relaxed | Acquiring | SequentiallyConsistent +typealias StoreOrdering = Relaxed | Releasing | SequentiallyConsistent +typealias UpdateOrdering = Relaxed | Acquiring | Releasing | AcquiringAndReleasing | SequentiallyConsistent +``` + +NOTE: This syntax is not real and is not something being proposed. This is just for the simplification of proposing these API overloads. Projects that prefer to default to sequentially consistent ordering are welcome to add non-public `Atomic` extensions that implement that. However, we expect that providing an implicit default ordering would be highly undesirable in most production uses of atomics. @@ -204,7 +195,6 @@ We also provide a top-level function called `atomicMemoryFence` that allows issu /// Establishes a memory ordering without associating it with a /// particular atomic operation. /// -/// - A relaxed fence has no effect. /// - An acquiring fence ties to any preceding atomic operation that /// reads a value, and synchronizes with any releasing operation whose /// value was read. @@ -222,9 +212,11 @@ We also provide a top-level function called `atomicMemoryFence` that allows issu /// /// Be aware that Thread Sanitizer does not support fences and may report /// false-positive races for data protected by a fence. -public func atomicMemoryFence(ordering: AtomicUpdateOrdering) +public func atomicMemoryFence(ordering: UpdateOrdering) ``` +Where there are 4 overloads of this function taking either an `.acquiring`, `.releasing`, `.acquiringAndReleasing`, or `.sequentiallyConsistent` ordering. + Fences are slightly more powerful (but even more difficult to use) than orderings tied to specific atomic operations [[N2153]]; we expect their use will be limited to the most performance-sensitive synchronization constructs. ### The Atomic Protocol Hierarchy @@ -503,7 +495,7 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { /// /// - Parameter ordering: The memory ordering to apply on this operation. /// - Returns: The current value. - public borrowing func load(ordering: AtomicLoadOrdering) -> Value + public borrowing func load(ordering: LoadOrdering) -> Value /// Atomically sets the current value to `desired`, applying the specified /// memory ordering. @@ -512,7 +504,7 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { /// - Parameter ordering: The memory ordering to apply on this operation. public borrowing func store( _ desired: consuming Value, - ordering: AtomicStoreOrdering + ordering: StoreOrdering ) /// Atomically sets the current value to `desired` and returns the original @@ -523,7 +515,7 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { /// - Returns: The original value. public borrowing func exchange( _ desired: consuming Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value /// Perform an atomic compare and exchange operation on the current value, @@ -550,9 +542,9 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { /// - Returns: A tuple `(exchanged, original)`, where `exchanged` is true if /// the exchange was successful, and `original` is the original value. public borrowing func compareExchange( - expected: borrowing Value, + expected: consuming Value, desired: consuming Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> (exchanged: Bool, original: Value) /// Perform an atomic compare and exchange operation on the current value, @@ -586,10 +578,10 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { /// - Returns: A tuple `(exchanged, original)`, where `exchanged` is true if /// the exchange was successful, and `original` is the original value. public borrowing func compareExchange( - expected: borrowing Value, + expected: consuming Value, desired: consuming Value, - successOrdering: AtomicUpdateOrdering, - failureOrdering: AtomicLoadOrdering + successOrdering: UpdateOrdering, + failureOrdering: LoadOrdering ) -> (exchanged: Bool, original: Value) /// Perform an atomic weak compare and exchange operation on the current @@ -619,9 +611,9 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { /// - Returns: A tuple `(exchanged, original)`, where `exchanged` is true if /// the exchange was successful, and `original` is the original value. public borrowing func weakCompareExchange( - expected: borrowing Value, + expected: consuming Value, desired: consuming Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> (exchanged: Bool, original: Value) /// Perform an atomic weak compare and exchange operation on the current @@ -658,10 +650,10 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { /// - Returns: A tuple `(exchanged, original)`, where `exchanged` is true if /// the exchange was successful, and `original` is the original value. public borrowing func weakCompareExchange( - expected: borrowing Value, + expected: consuming Value, desired: consuming Value, - successOrdering: AtomicUpdateOrdering, - failureOrdering: AtomicLoadOrdering + successOrdering: UpdateOrdering, + failureOrdering: LoadOrdering ) -> (exchanged: Bool, original: Value) } ``` @@ -696,7 +688,7 @@ The compare-exchange primitive is special: it is a universal operation that can extension Atomic where Value == Int { func wrappingIncrement( by operand: Int, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) { var done = false var current = load(ordering: .relaxed) @@ -848,7 +840,7 @@ extension AtomicLazyReference { /// Atomically loads and returns the current value of this reference. /// /// The load operation is performed with the memory ordering - /// `AtomicLoadOrdering.acquiring`. + /// `AtomicMemoryOrdering.Acquiring`. public borrowing func load() -> Instance? } ``` @@ -874,89 +866,6 @@ The Standard Library has been internally using such a pattern to implement defer Note that unlike the rest of the atomic types, `load` and `storeIfNilThenLoad(_:)` do not expose `ordering` parameters. (Internally, they map to acquiring/releasing operations to guarantee correct synchronization.) -### Restricting Ordering Arguments to Compile-Time Constants - -Modeling orderings as regular function parameters allows us to specify them using syntax that's familiar to all Swift programmers. Unfortunately, it means that in the implementation of atomic operations we're forced to switch over the ordering argument: - -```swift -extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { - public borrowing func compareExchange( - expected: consuming Value, - desired: consuming Value, - ordering: AtomicUpdateOrdering - ) -> (exchanged: Bool, original: Int) { - // Note: This is a simplified version of the actual implementation - let won: Bool - let oldValue: Value - - switch ordering { - case .relaxed: - (oldValue, won) = Builtin.cmpxchg_monotonic_monotonic_IntNN( - address, expected, desired - ) - - case .acquiring: - (oldValue, won) = Builtin.cmpxchg_acquire_acquire_IntNN( - address, expected, desired - ) - - case .releasing: - (oldValue, won) = Builtin.cmpxchg_release_monotonic_IntNN( - address, expected, desired - ) - - case .acquiringAndReleasing: - (oldValue, won) = Builtin.cmpxchg_acqrel_acquire_IntNN( - address, expected, desired - ) - - case .sequentiallyConsistent: - (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_IntNN( - address, expected, desired - ) - - default: - fatalError("Unknown atomic memory ordering") - } - - return (exchanged: won, original: oldValue) - } -} -``` - -Given our requirement that primitive atomics must always compile down to the actual atomic instructions with minimal additional overhead, we must guarantee that these switch statements always get optimized away into the single case we need; they must never actually be evaluated at runtime. - -Luckily, configuring these special functions to always get force-inlined into all callers guarantees that constant folding will get rid of the switch statement *as long as the supplied ordering is a compile-time constant*. Unfortunately, it's all too easy to accidentally violate this latter requirement, with dire consequences to the expected performance of the atomic operation. - -Consider the following well-meaning attempt at using `compareExchange` to define an atomic integer addition operation that traps on overflow rather than allowing the result to wrap around: - -```swift -extension Atomic where Value == Int { - // Non-inlinable - public func checkedIncrement(by delta: Int, ordering: AtomicUpdateOrdering) { - var done = false - var current = load(ordering: .relaxed) - - while !done { - (done, current) = compareExchange( - expected: current, - desired: current + operand, // Traps on overflow - ordering: ordering - ) - } - } -} - -// Elsewhere: -counter.checkedIncrement(by: 1, ordering: .relaxed) -``` - -If for whatever reason the Swift compiler isn't able (or willing) to inline the `checkedIncrement` call, then the value of `ordering` won't be known at compile time to the body of the function, so even though `compareExchange` will still get inlined, its switch statement won't be eliminated. This leads to a potentially significant performance regression that could interfere with the scalability of the operation. - -To prevent these issues, we are constraining the memory ordering arguments of all atomic operations to be compile-time constants. Any attempt to pass a dynamic ordering value (such as in the `compareExchange` call above) will result in a compile-time error. - -An ordering expression will be considered constant-evaluable if it's either (1) a direct call to one of the `Atomic*Ordering` factory methods (`.relaxed`, `.acquiring`, etc.), or (2) it is a direct reference to a variable that is in turn constrained to be constant-evaluable. - ## Interaction with Existing Language Features Please refer to the [Clarify the Swift memory consistency model (SE-0282)](https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md#interaction-with-non-instantaneous-accesses) proposal which goes over how atomics interact with the Law of Exclusivity, Non-Instantaneous Accesses, and Implicit Pointer Conversions. @@ -1054,39 +963,30 @@ For the full API definition, please refer to the [implementation][implementation ### Atomic Memory Orderings ```swift -public struct AtomicLoadOrdering: Equatable, Hashable, CustomStringConvertible { - public static var relaxed: Self { get } - public static var acquiring: Self { get } - public static var sequentiallyConsistent: Self { get } - - public static func ==(left: Self, right: Self) -> Bool - public func hash(into hasher: inout Hasher) - public var description: String { get } -} - -public struct AtomicStoreOrdering: Equatable, Hashable, CustomStringConvertible { - public static var relaxed: Self { get } - public static var releasing: Self { get } - public static var sequentiallyConsistent: Self { get } - - public static func ==(left: Self, right: Self) -> Bool - public func hash(into hasher: inout Hasher) - public var description: String { get } -} - -public struct AtomicUpdateOrdering: Equatable, Hashable, CustomStringConvertible { - public static var relaxed: Self { get } - public static var acquiring: Self { get } - public static var releasing: Self { get } - public static var acquiringAndReleasing: Self { get } - public static var sequentiallyConsistent: Self { get } - - public static func ==(left: Self, right: Self) -> Bool - public func hash(into hasher: inout Hasher) - public var description: String { get } +// Specifies the memory ordering to be applied to any atomic operation. +public enum AtomicMemoryOrdering { + public struct Relaxed { + public static var relaxed: Self { get } + } + + public struct Acquiring { + public static var acquiring: Self { get } + } + + public struct Releasing { + public static var releasing: Self { get } + } + + public struct AcquiringAndReleasing { + public static var acquiringAndReleasing: Self { get } + } + + public struct SequentiallyConsistent { + public static var sequentiallyConsistent: Self { get } + } } -public func atomicMemoryFence(ordering: AtomicUpdateOrdering) +public func atomicMemoryFence(ordering: UpdateOrdering) ``` ### Atomic Protocols @@ -1209,42 +1109,44 @@ public struct Atomic: ~Copyable { extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { // Atomic operations: - public borrowing func load(ordering: AtomicLoadOrdering) -> Value + public borrowing func load( + ordering: LoadOrdering + ) -> Value public borrowing func store( _ desired: consuming Value, - ordering: AtomicStoreOrdering + ordering: StoreOrdering ) public borrowing func exchange( _ desired: consuming Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func compareExchange( - expected: borrowing Value, + expected: consuming Value, desired: consuming Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> (exchanged: Bool, original: Value) public borrowing func compareExchange( - expected: borrowing Value, + expected: consuming Value, desired: consuming Value, - successOrdering: AtomicUpdateOrdering, - failureOrdering: AtomicLoadOrdering + successOrdering: UpdateOrdering, + failureOrdering: LoadOrdering ) -> (exchanged: Bool, original: Value) public borrowing func weakCompareExchange( - expected: borrowing Value, + expected: consuming Value, desired: consuming Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> (exchanged: Bool, original: Value) public borrowing func weakCompareExchange( - expected: borrowing Value, + expected: consuming Value, desired: consuming Value, - successOrdering: AtomicUpdateOrdering, - failureOrdering: AtomicLoadOrdering + successOrdering: UpdateOrdering, + failureOrdering: LoadOrdering ) -> (exchanged: Bool, original: Value) } ``` @@ -1255,82 +1157,82 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { extension Atomic where Value == Int { public borrowing func loadThenWrappingIncrement( by operand: Value = 1, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func wrappingIncrementThenLoad( by operand: Value = 1, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func wrappingIncrement( by operand: Value = 1, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) public borrowing func loadThenWrappingDecrement( by operand: Value = 1, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func wrappingDecrementThenLoad( by operand: Value = 1, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func wrappingDecrement( by operand: Value = 1, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) public borrowing func loadThenBitwiseAnd( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func bitwiseAndThenLoad( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func loadThenBitwiseOr( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func bitwiseOrThenLoad( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func loadThenBitwiseXor( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func bitwiseXorThenLoad( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func loadThenMin( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func minThenLoad( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func loadThenMax( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func maxThenLoad( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value } @@ -1344,32 +1246,32 @@ as well as providing convenience functions for boolean operations: extension Atomic where Value == Bool { public borrowing func loadThenLogicalAnd( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func loadThenLogicalOr( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func loadThenLogicalXor( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func logicalAndThenLoad( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func logicalOrThenLoad( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value public borrowing func logicalXorThenLoad( with operand: Value, - ordering: AtomicUpdateOrdering + ordering: UpdateOrdering ) -> Value } ``` @@ -1398,8 +1300,6 @@ This is a purely additive change with no source compatibility impact. ## Effect on ABI Stability -This proposal introduces new entry points to the Standard Library ABI in a standalone `Synchronization` module, but otherwise it has no effect on ABI stability. - On ABI-stable platforms, the struct types and protocols introduced here will become part of the stdlib's ABI with availability matching the first OS releases that include them. Most of the atomic methods introduced in this document will be force-inlined directly into client code at every call site. As such, there is no reason to bake them into the stdlib's ABI -- the stdlib binary will not export symbols for them. @@ -1414,10 +1314,12 @@ For the new constructs introduced here, the proposed design allows us to make th - Addition of new memory orderings. Because all atomic operations compile directly into user code, new memory orderings that we decide to introduce later could potentially back-deploy to any OS release that includes this proposal. -- Addition of new atomic operations on the types introduced here. These would be reflected in internal protocol requirements, so they would not be directly back-deployable to previous ABI-stable OS releases. +- Addition of new atomic operations on the types introduced here. Back deployable. - Introducing a default memory ordering for atomic operations (either by adding a default value to `ordering`, or by adding new overloads that lack that parameter). This too would be a back-deployable change. +- Change the memory ordering model to not require tons of overloads as long as it's not source breaking. This would also be back-deployable. + (We don't necessarily plan to actually perform any of these changes; we merely leave the door open to doing them.) ## Potential Future Directions @@ -1456,7 +1358,7 @@ We defer these for future proposals. ### Default Orderings -We considered defaulting all atomic operations throughout the `Synchronization` module to sequentially consistent ordering. While we concede that doing so would make atomics slightly more approachable, implicit ordering values tend to interfere with highly performance-sensitive use cases of atomics (which is *most* use cases of atomics). Sequential consistency tends to be relatively rarely used in these contexts, and implicitly defaulting to it would allow accidental use to easily slip through code review. +We considered defaulting all atomic operations to sequentially consistent ordering. While we concede that doing so would make atomics slightly more approachable, implicit ordering values tend to interfere with highly performance-sensitive use cases of atomics (which is *most* use cases of atomics). Sequential consistency tends to be relatively rarely used in these contexts, and implicitly defaulting to it would allow accidental use to easily slip through code review. Users who wish for default orderings are welcome to define their own overloads for atomic operations: @@ -1475,7 +1377,7 @@ extension Atomic { } func compareExchange( - expected: borrowing Value, + expected: consuming Value, desired: consuming Value ) -> (exchanged: Bool, original: Value) { compareExchange( @@ -1486,7 +1388,7 @@ extension Atomic { } func weakCompareExchange( - expected: borrowing Value, + expected: consuming Value, desired: consuming Value ) -> (exchanged: Bool, original: Value) { weakCompareExchange( @@ -1509,7 +1411,7 @@ extension Atomic where Value == Int { ### A Truly Universal Generic Atomic Type -While future proposals may add a variety of other atomic types, we do not expect to ever provide a truly universal generic `Atomic` construct. The Synchronization module is designed to provide high-performance wait-free primitives, and these are heavily constrained by the atomic instruction sets of the CPU architectures Swift targets. +While future proposals may add a variety of other atomic types, we do not expect to ever provide a truly universal generic `Atomic` construct. The `Atomic` type is designed to provide high-performance lock-free primitives, and these are heavily constrained by the atomic instruction sets of the CPU architectures Swift targets. A universal `Atomic` type that can hold *any* value is unlikely to be implementable without locks, so it is outside the scope of this proposal. We may eventually consider adding such a construct in a future concurrency proposal: @@ -1559,7 +1461,118 @@ To prevent this gotcha, none of the proposed atomic types provide a property for ### Alternative Designs for Memory Orderings -Modeling memory orderings with enumeration(-like) values fits well into the Standard Library's existing API design practice, but `ordering` arguments aren't without problems. Most importantly, the quality of code generation depends greatly on the compiler's ability to constant-fold switch statements over these ordering values into a single instruction. This can be fragile -- especially in unoptimized builds. We think [constraining these arguments to compile-time constants](#restricting-ordering-arguments-to-compile-time-constants) strikes a good balance between readability and performance, but it's instructive to look at some of the approaches we considered before settling on this choice. +Modeling memory orderings with enumeration(-like) values fits well into the Standard Library's existing API design practice, but `ordering` arguments aren't without problems. Most importantly, the quality of code generation will always be its best because each application of an atomic operation gets resolved to a single canonical atomic intrinsic. We think memory ordering argument overloads side steps a bunch of issues that occur by modeling memory orderings by anything else, but it's instructive to look at some of the approaches we considered before settling on this choice. + +#### Group Orderings in Memory Ordering Structs + +An earlier revision of this pitch proposed something like the following: + +```swift +/// Specifies the memory ordering semantics of an atomic load operation. +public struct AtomicLoadOrdering { + public static var relaxed: Self { get } + public static var acquiring: Self { get } + public static var sequentiallyConsistent: Self { get } +} + +/// Specifies the memory ordering semantics of an atomic store operation. +public struct AtomicStoreOrdering { + public static var relaxed: Self { get } + public static var releasing: Self { get } + public static var sequentiallyConsistent: Self { get } +} + +/// Specifies the memory ordering semantics of an atomic read-modify-write +/// operation. +public struct AtomicUpdateOrdering { + public static var relaxed: Self { get } + public static var acquiring: Self { get } + public static var releasing: Self { get } + public static var acquiringAndReleasing: Self { get } + public static var sequentiallyConsistent: Self { get } +} +``` + +While a perfectly fine solution, it has the drawback that the implementation needs to switch over the orderings which can lead to the optimizer not eliminating the switch statement in unoptimized builds still: + +```swift +extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { + public borrowing func compareExchange( + expected: consuming Value, + desired: consuming Value, + ordering: AtomicUpdateOrdering + ) -> (exchanged: Bool, original: Int) { + // Note: This is a simplified version of the actual implementation + let won: Bool + let oldValue: Value + + switch ordering { + case .relaxed: + (oldValue, won) = Builtin.cmpxchg_monotonic_monotonic_IntNN( + address, expected, desired + ) + + case .acquiring: + (oldValue, won) = Builtin.cmpxchg_acquire_acquire_IntNN( + address, expected, desired + ) + + case .releasing: + (oldValue, won) = Builtin.cmpxchg_release_monotonic_IntNN( + address, expected, desired + ) + + case .acquiringAndReleasing: + (oldValue, won) = Builtin.cmpxchg_acqrel_acquire_IntNN( + address, expected, desired + ) + + case .sequentiallyConsistent: + (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_IntNN( + address, expected, desired + ) + + default: + fatalError("Unknown atomic memory ordering") + } + + return (exchanged: won, original: oldValue) + } +} +``` + +Given our requirement that primitive atomics must always compile down to the actual atomic instructions with minimal additional overhead, we must guarantee that these switch statements always get optimized away into the single case we need; they must never actually be evaluated at runtime. + +Luckily, configuring these special functions to always get force-inlined into all callers guarantees that constant folding will get rid of the switch statement *as long as the supplied ordering is a compile-time constant*. Unfortunately, it's all too easy to accidentally violate this latter requirement, with dire consequences to the expected performance of the atomic operation. + +Consider the following well-meaning attempt at using `compareExchange` to define an atomic integer addition operation that traps on overflow rather than allowing the result to wrap around: + +```swift +extension Atomic where Value == Int { + // Non-inlinable + public func checkedIncrement(by delta: Int, ordering: AtomicUpdateOrdering) { + var done = false + var current = load(ordering: .relaxed) + + while !done { + (done, current) = compareExchange( + expected: current, + desired: current + operand, // Traps on overflow + ordering: ordering + ) + } + } +} + +// Elsewhere: +counter.checkedIncrement(by: 1, ordering: .relaxed) +``` + +If for whatever reason the Swift compiler isn't able (or willing) to inline the `checkedIncrement` call, then the value of `ordering` won't be known at compile time to the body of the function, so even though `compareExchange` will still get inlined, its switch statement won't be eliminated. This leads to a potentially significant performance regression that could interfere with the scalability of the operation. + +Our solution to this problem was to use a special compiler attribute that forced the ordering argument for all of the atomic operations we proposed to be constant evaluable. This solves the immediate issue at hand, but it would make adding new external operations awkward because they too would need to copy the special compiler attribute to get the same behavior that the stdlib had. However, the ultimate reason why we dropped this approach was due to the fact that we were relying on the optimizer (which we can usually do!) to eliminate the switch in unoptimized builds vs. not having a switch at all in the first place. It also feels good to not rely on special compiler support and instead use language features to get us the API design, code gen, and performance that we desire. + +While the overload approach we've gone with does put slightly more pressure on the type checker to resolve, it would still resolve to either the correct answer or no answer at all. Compared to the type checker, the optimizer has the freedom to decide not to optimize the switch statement out in debug builds which would still technically be a correct answer, but would severely reduce performance for these atomic operations. #### Encode Orderings in Method Names From ee4dbf1562e63e331e16270f3aeb4eaa824e002e Mon Sep 17 00:00:00 2001 From: Taeyoung Won <45925685+wontaeyoung@users.noreply.github.com> Date: Tue, 3 Oct 2023 22:19:20 +0900 Subject: [PATCH 3324/4563] [SE-0409] Fix some typos (#2179) --- proposals/0409-access-level-on-imports.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0409-access-level-on-imports.md b/proposals/0409-access-level-on-imports.md index 4a47da59c0..ba3bc7f626 100644 --- a/proposals/0409-access-level-on-imports.md +++ b/proposals/0409-access-level-on-imports.md @@ -3,7 +3,7 @@ * Proposal: [SE-0409](0409-access-level-on-imports.md) * Author: [Alexis Laferrière](https://github.com/xymus) * Review Manager: [Frederick Kellison-Linn](https://github.com/Jumhyn) -* Status: Status: **Active Review (September 14...26, 2023)** +* Status: **Active Review (September 14...26, 2023)** * Implementation: On main and release/5.9 gated behind the frontend flag `-enable-experimental-feature AccessLevelOnImport` * Upcoming Feature Flag: `InternalImports` (Enables Swift 6 behavior with imports defaulting to internal. Soon on main only.) * Review: ([pitch](https://forums.swift.org/t/pitch-access-level-on-import-statements/66657)) ([review](https://forums.swift.org/t/se-0409-access-level-modifiers-on-import-declarations/67290)) @@ -22,7 +22,7 @@ Swift already offers access levels with their respective modifiers to declaratio but there is currently no equivalent official feature for dependencies. The author of a library may have a different intent for each of the library dependencies; -some are expected to be known to the library clients while other are for implementation details internal to the package, module, or source file. +some are expected to be known to the library clients while others are for implementation details internal to the package, module, or source file. Without a way to enforce the intended access level of dependencies it is easy to make a mistake and expose a dependency of the library to the library clients by referencing it from a public declaration even if it's intended to remain an implementation detail. @@ -261,7 +261,7 @@ it can break source compatibility in code relying of those behaviors. ### Hiding dependency for non-resilient modules Hiding dependencies on non-resilient modules would be possible in theory but requires rethinking a few restrictions in the compilation process. -The main restriction is the need of the compiler to know the memory layout of imported types, which can depend on a transitive dependencies. +The main restriction is the need of the compiler to know the memory layout of imported types, which can depend on transitive dependencies. Resilient modules can provide this information at run time so the transitive module isn't required at build time. Non-resilient modules do not provide this information at run time, so the compiler must load the transitive dependencies at build time to access it. Solutions could involve copying the required information in each modules, From 128c549ffe8d76eb7eee194d9db9d8f4f2a51036 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 4 Oct 2023 12:41:33 -0400 Subject: [PATCH 3325/4563] Accept SE-0409 (#2180) * Accept SE-0409 * Fix link --- proposals/0409-access-level-on-imports.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0409-access-level-on-imports.md b/proposals/0409-access-level-on-imports.md index ba3bc7f626..ba14798c9f 100644 --- a/proposals/0409-access-level-on-imports.md +++ b/proposals/0409-access-level-on-imports.md @@ -3,10 +3,10 @@ * Proposal: [SE-0409](0409-access-level-on-imports.md) * Author: [Alexis Laferrière](https://github.com/xymus) * Review Manager: [Frederick Kellison-Linn](https://github.com/Jumhyn) -* Status: **Active Review (September 14...26, 2023)** +* Status: **Accepted with modifications** * Implementation: On main and release/5.9 gated behind the frontend flag `-enable-experimental-feature AccessLevelOnImport` * Upcoming Feature Flag: `InternalImports` (Enables Swift 6 behavior with imports defaulting to internal. Soon on main only.) -* Review: ([pitch](https://forums.swift.org/t/pitch-access-level-on-import-statements/66657)) ([review](https://forums.swift.org/t/se-0409-access-level-modifiers-on-import-declarations/67290)) +* Review: ([pitch](https://forums.swift.org/t/pitch-access-level-on-import-statements/66657)) ([review](https://forums.swift.org/t/se-0409-access-level-modifiers-on-import-declarations/67290)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0409-access-level-modifiers-on-import-declarations/67666)) ## Introduction From 761ce8024b7fa6b4d6ca7a367f5b3ab621af9eae Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 5 Oct 2023 00:07:57 -0400 Subject: [PATCH 3326/4563] Improve source compatibility example --- ...generalize-keypath-function-conversions.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/proposals/NNNN-generalize-keypath-function-conversions.md b/proposals/NNNN-generalize-keypath-function-conversions.md index 10f1ca12cc..dea1e73da6 100644 --- a/proposals/NNNN-generalize-keypath-function-conversions.md +++ b/proposals/NNNN-generalize-keypath-function-conversions.md @@ -73,34 +73,34 @@ let f: (User) -> String? = { kp in { root in root[keyPath: kp] } }(\User.email) While this proposal _mostly_ only makes previously invalid code valid, [@jrose](https://forums.swift.org/u/jrose) pointed out some corner cases where this proposal could potentially change the meaning of existing code: ``` -func evil3(_: (T) -> U) {} -func evil3(_ x: (String) -> Bool?) { print("aha") } +func evil(_: (T) -> U) { print("generic") } +func evil(_ x: (String) -> Bool?) { print("concrete") } -evil3(\String.isEmpty) +evil(\String.isEmpty) ``` -Previously, the `evil3` call would select the first overload of `evil3` (since the `KeyPath` to `(String) -> Bool?` conversion was considered invalid), but this proposal would select the second overload of `evil3` (printing "aha"). +Previously, the `evil` call would select the first overload of `evil` (printing "generic", since the `KeyPath` to `(String) -> Bool?` conversion was considered invalid), but this proposal would select the second overload of `evil` (printing "concrete"). This proposal opts to treat such differences as a bug in the implementation of SE-0249, which promises that the keypath-to-function conversion will have "semantics equivalent to capturing the key path and applying it to the Root argument": ```swift -evil3({ kp in { $0[keyPath: kp] } }(\String.isEmpty)) // Prints 'aha' +evil({ kp in { $0[keyPath: kp] } }(\String.isEmpty)) // Prints 'concrete' ``` The author expects such situations to be quite rare, and, moreover, already plagued by unreliable overload resolution in the face of calls that _should_ be semantically equivalent: ```swift -// Doesn't print 'aha' -evil3({ kp in { (string: String) -> Bool in string[keyPath: kp] } }(\String.isEmpty)) +// 'generic' +evil({ kp in { (string: String) -> Bool in string[keyPath: kp] } }(\String.isEmpty)) -// Prints 'aha' -evil3({ kp in { string -> Bool in string[keyPath: kp] } }(\String.isEmpty)) +// 'concrete' +evil({ kp in { string -> Bool in string[keyPath: kp] } }(\String.isEmpty)) -// Doesn't print 'aha' -evil3({ kp in { (string: String) in string[keyPath: kp] } }(\String.isEmpty)) +// 'generic' +evil({ kp in { (string: String) in string[keyPath: kp] } }(\String.isEmpty)) -// Prints 'aha' -evil3({ kp in { $0[keyPath: kp] } }(\String.isEmpty)) +// 'concrete' +evil({ kp in { $0[keyPath: kp] } }(\String.isEmpty)) ``` Thus, the author opts for a model which maintains the simple "keypath version has the same semantics as the explicit closure version" rule. From 83f002523439dc3cb4b1af3461a8d92e081c69d4 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 5 Oct 2023 11:06:55 -0700 Subject: [PATCH 3327/4563] Go back to constant expression orderings --- proposals/NNNN-atomics.md | 439 +++++++++++++++++++------------------- 1 file changed, 225 insertions(+), 214 deletions(-) diff --git a/proposals/NNNN-atomics.md b/proposals/NNNN-atomics.md index 9d1f3900c4..94c4295247 100644 --- a/proposals/NNNN-atomics.md +++ b/proposals/NNNN-atomics.md @@ -48,6 +48,7 @@ New Swift-evolution thread: [Atomics](https://forums.swift.org/t/atomics/67350) * [Specialized Integer Operations](#specialized-integer-operations) * [Specialized Boolean Operations](#specialized-boolean-operations) * [Atomic Lazy References](#atomic-lazy-references) + * [Restricting Ordering Arguments to Compile\-Time Constants](#restricting-ordering-arguments-to-compile-time-constants) * [Interaction with Existing Language Features](#interaction-with-existing-language-features) * [Interaction with Swift Concurrency](#interaction-with-swift-concurrency) * [Detailed Design](#detailed-design) @@ -146,46 +147,37 @@ These align with select members of the standard `std::memory_order` enumeration | `std::memory_order_acq_rel` | `.acquiringAndReleasing` | | `std::memory_order_seq_cst` | `.sequentiallyConsistent` | -Atomic orderings are nested in a memory ordering "namespace". Each ordering has its own type and a static getter to retrieve a value of the ordering type. We've modeled it this way to present ordering overloads for atomic operations as you'll see later in the proposal. +Atomic orderings are grouped into three frozen structs based on the kind of operation to which they are attached, as listed below. By modeling these as separate types, we can ensure that unsupported operation/ordering combinations (such as an atomic "releasing load") will lead to clear compile-time errors: ```swift -// Specifies the memory ordering to be applied to any atomic operation. -public enum AtomicMemoryOrdering { - public struct Relaxed { - public static var relaxed: Self { get } - } - - public struct Acquiring { - public static var acquiring: Self { get } - } - - public struct Releasing { - public static var releasing: Self { get } - } - - public struct AcquiringAndReleasing { - public static var acquiringAndReleasing: Self { get } - } - - public struct SequentiallyConsistent { - public static var sequentiallyConsistent: Self { get } - } +/// Specifies the memory ordering semantics of an atomic load operation. +public struct AtomicLoadOrdering { + public static var relaxed: Self { get } + public static var acquiring: Self { get } + public static var sequentiallyConsistent: Self { get } } -``` -Every atomic operation introduced later in this proposal requires an ordering argument. We consider these ordering arguments to be an essential part of these low-level atomic APIs, and we require an explicit `ordering` argument on all atomic operations. The intention here is to force developers to carefully think about what ordering they need to use, each time they use one of these primitives. (Perhaps more importantly, this also makes it obvious to readers of the code what ordering is used -- making it far less likely that an unintended default `.sequentiallyConsistent` ordering slips through code review.) - -In addition to every atomic operation requiring an explicit ordering, each operation will have overloads for each accepted memory ordering. This has the benefit of every application of an atomic operation to have a single representation that will always produce the best result as opposed to switching over the ordering in the implementation at runtime potentially. We go more into detail about the though process for this design in [Alternative Designs for Memory Orderings](#alternative-designs-for-memory-orderings). - -All atomic operations later proposed in this document will be simplified because there are lots of overloads for each memory ordering argument. To make this simpler, we're going to define the following: +/// Specifies the memory ordering semantics of an atomic store operation. +public struct AtomicStoreOrdering { + public static var relaxed: Self { get } + public static var releasing: Self { get } + public static var sequentiallyConsistent: Self { get } +} -```swift -typealias LoadOrdering = Relaxed | Acquiring | SequentiallyConsistent -typealias StoreOrdering = Relaxed | Releasing | SequentiallyConsistent -typealias UpdateOrdering = Relaxed | Acquiring | Releasing | AcquiringAndReleasing | SequentiallyConsistent +/// Specifies the memory ordering semantics of an atomic read-modify-write +/// operation. +public struct AtomicUpdateOrdering { + public static var relaxed: Self { get } + public static var acquiring: Self { get } + public static var releasing: Self { get } + public static var acquiringAndReleasing: Self { get } + public static var sequentiallyConsistent: Self { get } +} ``` -NOTE: This syntax is not real and is not something being proposed. This is just for the simplification of proposing these API overloads. +These structs behave like non-frozen enums with a known (non-public) raw representation. This allows us to define additional memory orderings in the future (if and when they become necessary, specifically `std::memory_order_consume`) while making use of the known representation to optimize existing cases. (These cannot be frozen enums because that would prevent us from adding more orderings, but regular resilient enums can't freeze their representation, and the layout indirection interferes with guaranteed optimizations, especially in -Onone.) + +Every atomic operation introduced later in this proposal requires an ordering argument. We consider these ordering arguments to be an essential part of these low-level atomic APIs, and we require an explicit `ordering` argument on all atomic operations. The intention here is to force developers to carefully think about what ordering they need to use, each time they use one of these primitives. (Perhaps more importantly, this also makes it obvious to readers of the code what ordering is used -- making it far less likely that an unintended default `.sequentiallyConsistent` ordering slips through code review.) Projects that prefer to default to sequentially consistent ordering are welcome to add non-public `Atomic` extensions that implement that. However, we expect that providing an implicit default ordering would be highly undesirable in most production uses of atomics. @@ -195,6 +187,7 @@ We also provide a top-level function called `atomicMemoryFence` that allows issu /// Establishes a memory ordering without associating it with a /// particular atomic operation. /// +/// - A relaxed fence has no effect. /// - An acquiring fence ties to any preceding atomic operation that /// reads a value, and synchronizes with any releasing operation whose /// value was read. @@ -212,11 +205,9 @@ We also provide a top-level function called `atomicMemoryFence` that allows issu /// /// Be aware that Thread Sanitizer does not support fences and may report /// false-positive races for data protected by a fence. -public func atomicMemoryFence(ordering: UpdateOrdering) +public func atomicMemoryFence(ordering: AtomicUpdateOrdering) ``` -Where there are 4 overloads of this function taking either an `.acquiring`, `.releasing`, `.acquiringAndReleasing`, or `.sequentiallyConsistent` ordering. - Fences are slightly more powerful (but even more difficult to use) than orderings tied to specific atomic operations [[N2153]]; we expect their use will be limited to the most performance-sensitive synchronization constructs. ### The Atomic Protocol Hierarchy @@ -495,7 +486,7 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { /// /// - Parameter ordering: The memory ordering to apply on this operation. /// - Returns: The current value. - public borrowing func load(ordering: LoadOrdering) -> Value + public borrowing func load(ordering: AtomicLoadOrdering) -> Value /// Atomically sets the current value to `desired`, applying the specified /// memory ordering. @@ -504,7 +495,7 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { /// - Parameter ordering: The memory ordering to apply on this operation. public borrowing func store( _ desired: consuming Value, - ordering: StoreOrdering + ordering: AtomicStoreOrdering ) /// Atomically sets the current value to `desired` and returns the original @@ -515,7 +506,7 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { /// - Returns: The original value. public borrowing func exchange( _ desired: consuming Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value /// Perform an atomic compare and exchange operation on the current value, @@ -544,7 +535,7 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { public borrowing func compareExchange( expected: consuming Value, desired: consuming Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> (exchanged: Bool, original: Value) /// Perform an atomic compare and exchange operation on the current value, @@ -580,8 +571,8 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { public borrowing func compareExchange( expected: consuming Value, desired: consuming Value, - successOrdering: UpdateOrdering, - failureOrdering: LoadOrdering + successOrdering: AtomicUpdateOrdering, + failureOrdering: AtomicLoadOrdering ) -> (exchanged: Bool, original: Value) /// Perform an atomic weak compare and exchange operation on the current @@ -613,7 +604,7 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { public borrowing func weakCompareExchange( expected: consuming Value, desired: consuming Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> (exchanged: Bool, original: Value) /// Perform an atomic weak compare and exchange operation on the current @@ -652,8 +643,8 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { public borrowing func weakCompareExchange( expected: consuming Value, desired: consuming Value, - successOrdering: UpdateOrdering, - failureOrdering: LoadOrdering + successOrdering: AtomicUpdateOrdering, + failureOrdering: AtomicLoadOrdering ) -> (exchanged: Bool, original: Value) } ``` @@ -688,7 +679,7 @@ The compare-exchange primitive is special: it is a universal operation that can extension Atomic where Value == Int { func wrappingIncrement( by operand: Int, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) { var done = false var current = load(ordering: .relaxed) @@ -840,7 +831,7 @@ extension AtomicLazyReference { /// Atomically loads and returns the current value of this reference. /// /// The load operation is performed with the memory ordering - /// `AtomicMemoryOrdering.Acquiring`. + /// `AtomicLoadOrdering.acquiring`. public borrowing func load() -> Instance? } ``` @@ -866,6 +857,91 @@ The Standard Library has been internally using such a pattern to implement defer Note that unlike the rest of the atomic types, `load` and `storeIfNilThenLoad(_:)` do not expose `ordering` parameters. (Internally, they map to acquiring/releasing operations to guarantee correct synchronization.) +### Restricting Ordering Arguments to Compile-Time Constants + +Modeling orderings as regular function parameters allows us to specify them using syntax that's familiar to all Swift programmers. Unfortunately, it means that in the implementation of atomic operations we're forced to switch over the ordering argument: + +```swift +extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { + public borrowing func compareExchange( + expected: consuming Value, + desired: consuming Value, + ordering: AtomicUpdateOrdering + ) -> (exchanged: Bool, original: Int) { + // Note: This is a simplified version of the actual implementation + let won: Bool + let oldValue: Value + + switch ordering { + case .relaxed: + (oldValue, won) = Builtin.cmpxchg_monotonic_monotonic_IntNN( + address, expected, desired + ) + + case .acquiring: + (oldValue, won) = Builtin.cmpxchg_acquire_acquire_IntNN( + address, expected, desired + ) + + case .releasing: + (oldValue, won) = Builtin.cmpxchg_release_monotonic_IntNN( + address, expected, desired + ) + + case .acquiringAndReleasing: + (oldValue, won) = Builtin.cmpxchg_acqrel_acquire_IntNN( + address, expected, desired + ) + + case .sequentiallyConsistent: + (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_IntNN( + address, expected, desired + ) + + default: + fatalError("Unknown atomic memory ordering") + } + + return (exchanged: won, original: oldValue) + } +} +``` + +Given our requirement that primitive atomics must always compile down to the actual atomic instructions with minimal additional overhead, we must guarantee that these switch statements always get optimized away into the single case we need; they must never actually be evaluated at runtime. + +Luckily, configuring these special functions to always get force-inlined into all callers guarantees that constant folding will get rid of the switch statement *as long as the supplied ordering is a compile-time constant*. Unfortunately, it's all too easy to accidentally violate this latter requirement, with dire consequences to the expected performance of the atomic operation. + +Consider the following well-meaning attempt at using `compareExchange` to define an atomic integer addition operation that traps on overflow rather than allowing the result to wrap around: + +```swift +extension Atomic where Value == Int { + // Non-inlinable + public func checkedIncrement(by delta: Int, ordering: AtomicUpdateOrdering) { + var done = false + var current = load(ordering: .relaxed) + + while !done { + (done, current) = compareExchange( + expected: current, + desired: current + operand, // Traps on overflow + ordering: ordering + ) + } + } +} + +// Elsewhere: +counter.checkedIncrement(by: 1, ordering: .relaxed) +``` + +If for whatever reason the Swift compiler isn't able (or willing) to inline the `checkedIncrement` call, then the value of `ordering` won't be known at compile time to the body of the function, so even though `compareExchange` will still get inlined, its switch statement won't be eliminated. This leads to a potentially significant performance regression that could interfere with the scalability of the operation. + +The big issue here is that if `checkedIncrement` is in another module, then callers of this function have no visibility inside this function's body. If callers can't see this function's implementation, then the switch statement will be executed at runtime regardless of the compiler optimization mode. However, another issue is that the ordering argument may still be dynamic in which case the compiler still can't eliminate the switch statement even though the caller may be able to see the entire implementation. + +To help prevent the last issue, we are constraining the memory ordering arguments of all atomic operations to be compile-time constants. Any attempt to pass a dynamic ordering value (such as in the `compareExchange` call above) will result in a compile-time error. + +An ordering expression will be considered constant-evaluable if it's either (1) a direct call to one of the `Atomic*Ordering` factory methods (`.relaxed`, `.acquiring`, etc.), or (2) it is a direct reference to a variable that is in turn constrained to be constant-evaluable. + ## Interaction with Existing Language Features Please refer to the [Clarify the Swift memory consistency model (SE-0282)](https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md#interaction-with-non-instantaneous-accesses) proposal which goes over how atomics interact with the Law of Exclusivity, Non-Instantaneous Accesses, and Implicit Pointer Conversions. @@ -963,30 +1039,39 @@ For the full API definition, please refer to the [implementation][implementation ### Atomic Memory Orderings ```swift -// Specifies the memory ordering to be applied to any atomic operation. -public enum AtomicMemoryOrdering { - public struct Relaxed { - public static var relaxed: Self { get } - } - - public struct Acquiring { - public static var acquiring: Self { get } - } - - public struct Releasing { - public static var releasing: Self { get } - } - - public struct AcquiringAndReleasing { - public static var acquiringAndReleasing: Self { get } - } - - public struct SequentiallyConsistent { - public static var sequentiallyConsistent: Self { get } - } +public struct AtomicLoadOrdering: Equatable, Hashable, CustomStringConvertible { + public static var relaxed: Self { get } + public static var acquiring: Self { get } + public static var sequentiallyConsistent: Self { get } + + public static func ==(left: Self, right: Self) -> Bool + public func hash(into hasher: inout Hasher) + public var description: String { get } +} + +public struct AtomicStoreOrdering: Equatable, Hashable, CustomStringConvertible { + public static var relaxed: Self { get } + public static var releasing: Self { get } + public static var sequentiallyConsistent: Self { get } + + public static func ==(left: Self, right: Self) -> Bool + public func hash(into hasher: inout Hasher) + public var description: String { get } +} + +public struct AtomicUpdateOrdering: Equatable, Hashable, CustomStringConvertible { + public static var relaxed: Self { get } + public static var acquiring: Self { get } + public static var releasing: Self { get } + public static var acquiringAndReleasing: Self { get } + public static var sequentiallyConsistent: Self { get } + + public static func ==(left: Self, right: Self) -> Bool + public func hash(into hasher: inout Hasher) + public var description: String { get } } -public func atomicMemoryFence(ordering: UpdateOrdering) +public func atomicMemoryFence(ordering: AtomicUpdateOrdering) ``` ### Atomic Protocols @@ -1110,43 +1195,43 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { // Atomic operations: public borrowing func load( - ordering: LoadOrdering + ordering: AtomicLoadOrdering ) -> Value public borrowing func store( _ desired: consuming Value, - ordering: StoreOrdering + ordering: AtomicStoreOrdering ) public borrowing func exchange( _ desired: consuming Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func compareExchange( expected: consuming Value, desired: consuming Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> (exchanged: Bool, original: Value) public borrowing func compareExchange( expected: consuming Value, desired: consuming Value, - successOrdering: UpdateOrdering, - failureOrdering: LoadOrdering + successOrdering: AtomicUpdateOrdering, + failureOrdering: AtomicLoadOrdering ) -> (exchanged: Bool, original: Value) public borrowing func weakCompareExchange( expected: consuming Value, desired: consuming Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> (exchanged: Bool, original: Value) public borrowing func weakCompareExchange( expected: consuming Value, desired: consuming Value, - successOrdering: UpdateOrdering, - failureOrdering: LoadOrdering + successOrdering: AtomicUpdateOrdering, + failureOrdering: AtomicLoadOrdering ) -> (exchanged: Bool, original: Value) } ``` @@ -1157,82 +1242,82 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { extension Atomic where Value == Int { public borrowing func loadThenWrappingIncrement( by operand: Value = 1, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func wrappingIncrementThenLoad( by operand: Value = 1, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func wrappingIncrement( by operand: Value = 1, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) public borrowing func loadThenWrappingDecrement( by operand: Value = 1, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func wrappingDecrementThenLoad( by operand: Value = 1, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func wrappingDecrement( by operand: Value = 1, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) public borrowing func loadThenBitwiseAnd( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func bitwiseAndThenLoad( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func loadThenBitwiseOr( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func bitwiseOrThenLoad( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func loadThenBitwiseXor( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func bitwiseXorThenLoad( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func loadThenMin( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func minThenLoad( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func loadThenMax( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func maxThenLoad( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value } @@ -1246,32 +1331,32 @@ as well as providing convenience functions for boolean operations: extension Atomic where Value == Bool { public borrowing func loadThenLogicalAnd( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func loadThenLogicalOr( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func loadThenLogicalXor( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func logicalAndThenLoad( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func logicalOrThenLoad( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value public borrowing func logicalXorThenLoad( with operand: Value, - ordering: UpdateOrdering + ordering: AtomicUpdateOrdering ) -> Value } ``` @@ -1314,11 +1399,11 @@ For the new constructs introduced here, the proposed design allows us to make th - Addition of new memory orderings. Because all atomic operations compile directly into user code, new memory orderings that we decide to introduce later could potentially back-deploy to any OS release that includes this proposal. -- Addition of new atomic operations on the types introduced here. Back deployable. +- Addition of new atomic operations on the types introduced here. These would be also be back deployable. - Introducing a default memory ordering for atomic operations (either by adding a default value to `ordering`, or by adding new overloads that lack that parameter). This too would be a back-deployable change. -- Change the memory ordering model to not require tons of overloads as long as it's not source breaking. This would also be back-deployable. +- Change the memory ordering model as long as the changes preserve source compatibility. (We don't necessarily plan to actually perform any of these changes; we merely leave the door open to doing them.) @@ -1461,118 +1546,7 @@ To prevent this gotcha, none of the proposed atomic types provide a property for ### Alternative Designs for Memory Orderings -Modeling memory orderings with enumeration(-like) values fits well into the Standard Library's existing API design practice, but `ordering` arguments aren't without problems. Most importantly, the quality of code generation will always be its best because each application of an atomic operation gets resolved to a single canonical atomic intrinsic. We think memory ordering argument overloads side steps a bunch of issues that occur by modeling memory orderings by anything else, but it's instructive to look at some of the approaches we considered before settling on this choice. - -#### Group Orderings in Memory Ordering Structs - -An earlier revision of this pitch proposed something like the following: - -```swift -/// Specifies the memory ordering semantics of an atomic load operation. -public struct AtomicLoadOrdering { - public static var relaxed: Self { get } - public static var acquiring: Self { get } - public static var sequentiallyConsistent: Self { get } -} - -/// Specifies the memory ordering semantics of an atomic store operation. -public struct AtomicStoreOrdering { - public static var relaxed: Self { get } - public static var releasing: Self { get } - public static var sequentiallyConsistent: Self { get } -} - -/// Specifies the memory ordering semantics of an atomic read-modify-write -/// operation. -public struct AtomicUpdateOrdering { - public static var relaxed: Self { get } - public static var acquiring: Self { get } - public static var releasing: Self { get } - public static var acquiringAndReleasing: Self { get } - public static var sequentiallyConsistent: Self { get } -} -``` - -While a perfectly fine solution, it has the drawback that the implementation needs to switch over the orderings which can lead to the optimizer not eliminating the switch statement in unoptimized builds still: - -```swift -extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { - public borrowing func compareExchange( - expected: consuming Value, - desired: consuming Value, - ordering: AtomicUpdateOrdering - ) -> (exchanged: Bool, original: Int) { - // Note: This is a simplified version of the actual implementation - let won: Bool - let oldValue: Value - - switch ordering { - case .relaxed: - (oldValue, won) = Builtin.cmpxchg_monotonic_monotonic_IntNN( - address, expected, desired - ) - - case .acquiring: - (oldValue, won) = Builtin.cmpxchg_acquire_acquire_IntNN( - address, expected, desired - ) - - case .releasing: - (oldValue, won) = Builtin.cmpxchg_release_monotonic_IntNN( - address, expected, desired - ) - - case .acquiringAndReleasing: - (oldValue, won) = Builtin.cmpxchg_acqrel_acquire_IntNN( - address, expected, desired - ) - - case .sequentiallyConsistent: - (oldValue, won) = Builtin.cmpxchg_seqcst_seqcst_IntNN( - address, expected, desired - ) - - default: - fatalError("Unknown atomic memory ordering") - } - - return (exchanged: won, original: oldValue) - } -} -``` - -Given our requirement that primitive atomics must always compile down to the actual atomic instructions with minimal additional overhead, we must guarantee that these switch statements always get optimized away into the single case we need; they must never actually be evaluated at runtime. - -Luckily, configuring these special functions to always get force-inlined into all callers guarantees that constant folding will get rid of the switch statement *as long as the supplied ordering is a compile-time constant*. Unfortunately, it's all too easy to accidentally violate this latter requirement, with dire consequences to the expected performance of the atomic operation. - -Consider the following well-meaning attempt at using `compareExchange` to define an atomic integer addition operation that traps on overflow rather than allowing the result to wrap around: - -```swift -extension Atomic where Value == Int { - // Non-inlinable - public func checkedIncrement(by delta: Int, ordering: AtomicUpdateOrdering) { - var done = false - var current = load(ordering: .relaxed) - - while !done { - (done, current) = compareExchange( - expected: current, - desired: current + operand, // Traps on overflow - ordering: ordering - ) - } - } -} - -// Elsewhere: -counter.checkedIncrement(by: 1, ordering: .relaxed) -``` - -If for whatever reason the Swift compiler isn't able (or willing) to inline the `checkedIncrement` call, then the value of `ordering` won't be known at compile time to the body of the function, so even though `compareExchange` will still get inlined, its switch statement won't be eliminated. This leads to a potentially significant performance regression that could interfere with the scalability of the operation. - -Our solution to this problem was to use a special compiler attribute that forced the ordering argument for all of the atomic operations we proposed to be constant evaluable. This solves the immediate issue at hand, but it would make adding new external operations awkward because they too would need to copy the special compiler attribute to get the same behavior that the stdlib had. However, the ultimate reason why we dropped this approach was due to the fact that we were relying on the optimizer (which we can usually do!) to eliminate the switch in unoptimized builds vs. not having a switch at all in the first place. It also feels good to not rely on special compiler support and instead use language features to get us the API design, code gen, and performance that we desire. - -While the overload approach we've gone with does put slightly more pressure on the type checker to resolve, it would still resolve to either the correct answer or no answer at all. Compared to the type checker, the optimizer has the freedom to decide not to optimize the switch statement out in debug builds which would still technically be a correct answer, but would severely reduce performance for these atomic operations. +Modeling memory orderings with enumeration(-like) values fits well into the Standard Library's existing API design practice, but `ordering` arguments aren't without problems. Most importantly, the quality of code generation depends greatly on the compiler's ability to constant-fold switch statements over these ordering values into a single instruction. This can be fragile -- especially in unoptimized builds. We think [constraining these arguments to compile-time constants](#restricting-ordering-arguments-to-compile-time-constants) strikes a good balance between readability and performance, but it's instructive to look at some of the approaches we considered before settling on this choice. #### Encode Orderings in Method Names @@ -1641,6 +1615,43 @@ However, we ultimately decided against going down this route, for the following - **Limited Reuse.** Implementing ordering views takes a rather large amount of (error-prone) boilerplate-heavy code that is not directly reusable. Every new atomic type would need to implement a new set of ordering views, tailor-fit to its own use-case. +#### Memory Orderings as Overloads + +Another promising alternative was the idea to model each ordering as a separate type and have overloads for the various atomic operations. + +```swift +struct AtomicMemoryOrdering { + struct Relaxed { + static var relaxed: Self { get } + } + + struct Acquiring { + static var acquiring: Self { get } + } + + ... +} + +extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { + func load(ordering: AtomicMemoryOrdering.Relaxed) -> Value {...} + func load(ordering: AtomicMemoryOrdering.Acquiring) -> Value {...} + ... +} +``` + +This approach shares a lot of the same benefits of views, but the biggest reason for this alternative was the fact that the switch statement problem we described earlier just doesn't exist anymore. There is no switch statement! The overload always gets resolved to a single atomic operation + ordering + storage meaning there's no question about what to compile the operation down to. However, this is just another type of flavor of views in that the API surface explodes especially with double ordering operations. + +There are 5 storage types and we define the primitive atomic operations on extensions of all of these. For the constant expression case for single ordering operations that's `5 (storage) * 1 (ordering) = 5` number of overloads and `5 (storage) * 1 (update ordering) * 1 (load ordering) = 5` for the double ordering case. The overload solution is now dependent on the number of orderings supported for a specific operation. So for single ordering loads it's `5 (storage) * 3 (orderings) = 15` different load orderings and for the double ordering compare and exchange it's `5 (storage) * 5 (update orderings) * 3 (load orderings) = 75` overloads. + +| | Overloads | Constant Expressions | +| ----------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| Overload Resolution | Very bad | Not so bad | +| API Documentation | Very bad (but can be fixed!) | Not so bad (but can be fixed!) | +| Custom Atomic Operations | Requires users to define multiple overloads for their operations. | Allows users to define a single entrypoint that takes a constant ordering and passes that to the primitive atomic operations. | +| Back Deployable New Orderings | Almost impossible unless we defined the ordering types in C because types in Swift must come with availability. | Can easily be done because the orderings are static property getters that we can back deploy. | + +The same argument for views creating a very vast API surface can be said about the overloads which helped us determine that the constant expression approach is still superior. + ### Directly bring over `swift-atomics`'s API The `swift-atomics` package has many years of experience using their APIs to interface with atomic values and it would be beneficial to simply bring over the same API. However, once we designed the general purpose `Atomic` type, we noticied a few deficiencies with the atomic protocol hierarchy that made using this type awkward for users. We've redesigned these protocols to make using the atomic type easier to use and easier to reason about. From 134547e065eb3e7a78f2a9c3128d64b57ed133a8 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 5 Oct 2023 17:11:36 -0400 Subject: [PATCH 3328/4563] Clarify source compatibility change --- ...generalize-keypath-function-conversions.md | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/proposals/NNNN-generalize-keypath-function-conversions.md b/proposals/NNNN-generalize-keypath-function-conversions.md index dea1e73da6..ddc05f11a6 100644 --- a/proposals/NNNN-generalize-keypath-function-conversions.md +++ b/proposals/NNNN-generalize-keypath-function-conversions.md @@ -70,7 +70,7 @@ let f: (User) -> String? = { kp in { root in root[keyPath: kp] } }(\User.email) ## Source compatibility -While this proposal _mostly_ only makes previously invalid code valid, [@jrose](https://forums.swift.org/u/jrose) pointed out some corner cases where this proposal could potentially change the meaning of existing code: +While this proposal _mostly_ only makes previously invalid code valid, [@jrose](https://forums.swift.org/u/jrose) pointed out a case where this proposal could potentially change the meaning of existing code: ``` func evil(_: (T) -> U) { print("generic") } @@ -87,23 +87,36 @@ This proposal opts to treat such differences as a bug in the implementation of S evil({ kp in { $0[keyPath: kp] } }(\String.isEmpty)) // Prints 'concrete' ``` -The author expects such situations to be quite rare, and, moreover, already plagued by unreliable overload resolution in the face of calls that _should_ be semantically equivalent: +The circumstances necessary are exceedingly narrow to reproduce the above behavior. It is not enough merely to declare the `evil` overloads. The following naive reproduction attempt fails to compile, thus posing no source compatibility error: -```swift -// 'generic' -evil({ kp in { (string: String) -> Bool in string[keyPath: kp] } }(\String.isEmpty)) +``` +struct S { + let x: Bool +} -// 'concrete' -evil({ kp in { string -> Bool in string[keyPath: kp] } }(\String.isEmpty)) +func evil(_: (T) -> U) { } +func evil(_: (S) -> Bool?) { } +evil(\S.x) // +``` -// 'generic' -evil({ kp in { (string: String) in string[keyPath: kp] } }(\String.isEmpty)) +That this compilation fails seems to be a bug of its own. The additional ingredient appears to be overloading `x`—with the following additions, the snipped above compiles without error: -// 'concrete' -evil({ kp in { $0[keyPath: kp] } }(\String.isEmpty)) ``` +protocol P {} +extension P { + var x: Bool { true } +} +extension S: P {} +``` + +So, to summarize, in order to cause a source compatibility issue, one must have: +1. Function overloads A and B accepting function arguments +2. A call to the overloaded function name, passed a keypath literal using an overloaded property +3. The keypath is viable for conversion to exactly one of A or B's parameter types under currently-implemented rules, but viable for conversion to _both_ A and B's parameter types under the rules in this proposal +4. The overload which is *not* currently viable would outrank the currently-viable overload once both are made viable under this proposal +5. A and B are not semantically interchangable such that calling one instead of the other results in a substantive change in behavior -Thus, the author opts for a model which maintains the simple "keypath version has the same semantics as the explicit closure version" rule. +In the author's judgement, the liklihood of any significant compatibility issue is negligible. ## Effect on ABI stability From 28766a85b94c99cc29cabf66dfe80cd5e5a30e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 5 Oct 2023 15:47:53 -0700 Subject: [PATCH 3329/4563] [SE-0409] Refine the description of the scoped imports effects --- proposals/0409-access-level-on-imports.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0409-access-level-on-imports.md b/proposals/0409-access-level-on-imports.md index fa17df9a19..9ec3eb21ff 100644 --- a/proposals/0409-access-level-on-imports.md +++ b/proposals/0409-access-level-on-imports.md @@ -232,8 +232,8 @@ In all cases, updating modules relying on `@_implementationOnly` to instead use The scoped imports feature remains independent from the access level declared on the same import. Given the example below, the module `Foo` is a public dependency at the module-level and can be referenced from public declaration signatures in the local source file. -The scoped part, `struct Foo.Bar`, is a hint to the name lookup logic to prioritize resolving references to `Bar` as this one compared to other `Bar` from other imports. -Since there's no interactions between the two features, +The scoped part, `struct Foo.Bar`, limits lookup so only `Bar` can be referenced from this file, it also prioritizes resolving references to this `Bar` if there are other `Bar` declarations in other imports. +Since there's no direct interaction between the two features, scoped imports cannot be used to restrict the access level of a single declaration. ``` public import struct Foo.Bar From e6c7235ae568b2fde44625203d6425b9c1881930 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Fri, 6 Oct 2023 11:48:30 -0400 Subject: [PATCH 3330/4563] Add language annotations --- .../NNNN-generalize-keypath-function-conversions.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/NNNN-generalize-keypath-function-conversions.md b/proposals/NNNN-generalize-keypath-function-conversions.md index ddc05f11a6..85033749c2 100644 --- a/proposals/NNNN-generalize-keypath-function-conversions.md +++ b/proposals/NNNN-generalize-keypath-function-conversions.md @@ -72,7 +72,7 @@ let f: (User) -> String? = { kp in { root in root[keyPath: kp] } }(\User.email) While this proposal _mostly_ only makes previously invalid code valid, [@jrose](https://forums.swift.org/u/jrose) pointed out a case where this proposal could potentially change the meaning of existing code: -``` +```swift func evil(_: (T) -> U) { print("generic") } func evil(_ x: (String) -> Bool?) { print("concrete") } @@ -89,19 +89,19 @@ evil({ kp in { $0[keyPath: kp] } }(\String.isEmpty)) // Prints 'concrete' The circumstances necessary are exceedingly narrow to reproduce the above behavior. It is not enough merely to declare the `evil` overloads. The following naive reproduction attempt fails to compile, thus posing no source compatibility error: -``` +```swift struct S { let x: Bool } func evil(_: (T) -> U) { } func evil(_: (S) -> Bool?) { } -evil(\S.x) // +evil(\S.x) ``` -That this compilation fails seems to be a bug of its own. The additional ingredient appears to be overloading `x`—with the following additions, the snipped above compiles without error: +That this compilation fails seems to be a bug of its own. The additional necessary ingredient appears to be overloading `x`—with the following additions, the snipped above compiles without error: -``` +```swift protocol P {} extension P { var x: Bool { true } From 58e1cd55c97931e8920b4d0ce62a60eca3577250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Fri, 6 Oct 2023 11:25:11 -0700 Subject: [PATCH 3331/4563] SE-0409: Document the behavior of `@usableFromInline` on imports (#2181) * [SE-0409] Add @usableFromInline behavior description * [SE-0409] Rename upcoming feature flag --- proposals/0409-access-level-on-imports.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/proposals/0409-access-level-on-imports.md b/proposals/0409-access-level-on-imports.md index ba14798c9f..3d8fea05c5 100644 --- a/proposals/0409-access-level-on-imports.md +++ b/proposals/0409-access-level-on-imports.md @@ -5,7 +5,7 @@ * Review Manager: [Frederick Kellison-Linn](https://github.com/Jumhyn) * Status: **Accepted with modifications** * Implementation: On main and release/5.9 gated behind the frontend flag `-enable-experimental-feature AccessLevelOnImport` -* Upcoming Feature Flag: `InternalImports` (Enables Swift 6 behavior with imports defaulting to internal. Soon on main only.) +* Upcoming Feature Flag: `InternalImportsByDefault` (Enables Swift 6 behavior with imports defaulting to internal. Soon on main only.) * Review: ([pitch](https://forums.swift.org/t/pitch-access-level-on-import-statements/66657)) ([review](https://forums.swift.org/t/se-0409-access-level-modifiers-on-import-declarations/67290)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0409-access-level-modifiers-on-import-declarations/67666)) ## Introduction @@ -47,6 +47,7 @@ public func publicFunc() -> DatabaseAdapter.Entry {...} // error: function canno Additionally, this proposal uses the access level declared on each import declaration in all source files composing a module to determine when clients of a library need to load the library's dependencies or when they can be skipped. To balance source compatibility and best practices, the proposed default import has an implicit access level of public in Swift 5 and of internal in Swift 6 mode. +The attribute `@usableFromInline` on an import allows references from inlinable code. ## Detailed design @@ -95,6 +96,15 @@ private import OtherDependencyPrivateToThisFile The `open` access-level modifier is rejected on import declarations. +The `@usableFromInline` attribute can be applied to an import declaration to allow referencing a dependency from inlinable code +while limiting which declarations signatures can reference it. +The attribute `@usableFromInline` can be used only on `package` and `internal` imports. +It marks the dependency as visible to clients. +```swift +@usableFromInline package import UsableFromInlinePackageDependency +@usableFromInline internal import UsableFromInlineInternalDependency +``` + ### Type-checking references to imported modules Current type-checking enforces that declaration respect their respective access levels. @@ -112,6 +122,11 @@ We apply the same logic for `package`, `fileprivate` and `private` import declar In the case of a `public` import, there is no restriction on how the imported declarations can be referenced beyond the existing restrictions on imported `package` declarations which cannot be referenced from public declaration signatures. +The attribute `@usableFromInline` on an import takes effect for inlinable code: +`@inlinable` and `@backDeployed` function bodies, default initializers of arguments, and properties of `@frozen` structs. +The `@usableFromInline` imported dependency can be referenced from inliable code +but doesn't affect type-checking of declaration signatures where only the access level is taken into account. + Here is an example of the approximate diagnostics produced from type-checking in a typical case with a `fileprivate` import. ```swift fileprivate import DatabaseAdapter @@ -164,7 +179,7 @@ the transitive client may need to load it at compile time or not. There are four factors requiring a transitive dependency to be loaded, if none of these apply the dependency can be hidden. -1. Public dependencies must always be loaded by transitive clients. +1. Public or `@usableFromInline` dependencies must always be loaded by transitive clients. 2. All dependencies of a non-resilient module must be loaded by transitive clients. @@ -206,7 +221,7 @@ The Swift 6 change will likely break source compatibility for libraries. A migration tool could automatically insert the `public` modifier where required. Where the tool is unavailable, a simple script can insert a `public` modifier in front of all imports to preserve the Swift 5 behavior. -The upcoming feature flag `InternalImports` will enable the Swift 6 behavior even when using Swift 5. +The upcoming feature flag `InternalImportsByDefault` will enable the Swift 6 behavior even when using Swift 5. ### Relation with other attributes on imports From 34eb669b9d2f2582327ee5f8e6bebb783a5798ab Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 2 Oct 2023 13:32:21 -0400 Subject: [PATCH 3332/4563] Remove mention of obsolete feature flags --- proposals/0346-light-weight-same-type-syntax.md | 1 - proposals/0393-parameter-packs.md | 1 - proposals/0398-variadic-types.md | 1 - 3 files changed, 3 deletions(-) diff --git a/proposals/0346-light-weight-same-type-syntax.md b/proposals/0346-light-weight-same-type-syntax.md index d1e4d2e46a..5bbf8aabc3 100644 --- a/proposals/0346-light-weight-same-type-syntax.md +++ b/proposals/0346-light-weight-same-type-syntax.md @@ -4,7 +4,6 @@ * Authors: [Pavel Yaskevich](https://github.com/xedin), [Holly Borla](https://github.com/hborla), [Slava Pestov](https://github.com/slavapestov) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Implemented (Swift 5.7)** -* Implementation: Principally [#40714](https://github.com/apple/swift/pull/40714), [#41640](https://github.com/apple/swift/pull/41640); in `main`, enabled by the experimental `-Xfrontend -enable-parameterized-protocol-types` flag * Previous Revisions: [1st](https://github.com/apple/swift-evolution/blob/5d86d57cfd6d803df4da90b196682d495e5de9b9/proposals/0346-light-weight-same-type-syntax.md) * Review: ([first pitch](https://forums.swift.org/t/pitch-light-weight-same-type-constraint-syntax/52889)) ([second pitch](https://forums.swift.org/t/pitch-2-light-weight-same-type-requirement-syntax/55081)) ([first review](https://forums.swift.org/t/se-0346-lightweight-same-type-requirements-for-primary-associated-types/55869)) ([second review](https://forums.swift.org/t/se-0346-second-review-lightweight-same-type-requirements-for-primary-associated-types/56414)) ([acceptance](https://forums.swift.org/t/accepted-se-0346-lightweight-same-type-requirements-for-primary-associated-types/56747)) diff --git a/proposals/0393-parameter-packs.md b/proposals/0393-parameter-packs.md index 038d3471c5..97cb16133f 100644 --- a/proposals/0393-parameter-packs.md +++ b/proposals/0393-parameter-packs.md @@ -4,7 +4,6 @@ * Authors: [Holly Borla](https://github.com/hborla), [John McCall](https://github.com/rjmccall), [Slava Pestov](https://github.com/slavapestov) * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Implemented (Swift 5.9)** -* Implementation: On `main` gated behind the frontend flag `-enable-experimental-feature VariadicGenerics` * Review: ([pitch 1](https://forums.swift.org/t/pitch-parameter-packs/60543)) ([pitch 2](https://forums.swift.org/t/pitch-2-value-and-type-parameter-packs/60830)) ([review](https://forums.swift.org/t/se-0393-value-and-type-parameter-packs/63859)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0393-value-and-type-parameter-packs/64382)) ## Introduction diff --git a/proposals/0398-variadic-types.md b/proposals/0398-variadic-types.md index 9a9cf75810..cbd820bb72 100644 --- a/proposals/0398-variadic-types.md +++ b/proposals/0398-variadic-types.md @@ -4,7 +4,6 @@ * Authors: [Slava Pestov](https://github.com/slavapestov), [Holly Borla](https://github.com/hborla) * Review Manager: [Frederick Kellison-Linn](https://github.com/Jumhyn) * Status: **Implemented (Swift 5.9)** -* Implementation: On main and release/5.9 gated behind the frontend flag -enable-experimental-feature VariadicGenerics * Previous Proposal: [SE-0393](0393-parameter-packs.md) * Review: ([pitch](https://forums.swift.org/t/pitch-variadic-generic-types-abstracting-over-packs/64377)) ([review](https://forums.swift.org/t/se-0398-allow-generic-types-to-abstract-over-packs/64661)) ([acceptance](https://forums.swift.org/t/accepted-se-0398-allow-generic-types-to-abstract-over-packs/64998)) From 9754d51f5882d2b8c6304ce8634ccad052316ae8 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Mon, 9 Oct 2023 17:54:23 -0400 Subject: [PATCH 3333/4563] Further changes to source compatibility --- ...generalize-keypath-function-conversions.md | 41 +------------------ 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/proposals/NNNN-generalize-keypath-function-conversions.md b/proposals/NNNN-generalize-keypath-function-conversions.md index 85033749c2..dcdec99815 100644 --- a/proposals/NNNN-generalize-keypath-function-conversions.md +++ b/proposals/NNNN-generalize-keypath-function-conversions.md @@ -70,7 +70,7 @@ let f: (User) -> String? = { kp in { root in root[keyPath: kp] } }(\User.email) ## Source compatibility -While this proposal _mostly_ only makes previously invalid code valid, [@jrose](https://forums.swift.org/u/jrose) pointed out a case where this proposal could potentially change the meaning of existing code: +This proposal makes previously invalid conversions valid, and should not affect source compatibility. In situations such as: ```swift func evil(_: (T) -> U) { print("generic") } @@ -79,44 +79,7 @@ func evil(_ x: (String) -> Bool?) { print("concrete") } evil(\String.isEmpty) ``` -Previously, the `evil` call would select the first overload of `evil` (printing "generic", since the `KeyPath` to `(String) -> Bool?` conversion was considered invalid), but this proposal would select the second overload of `evil` (printing "concrete"). - -This proposal opts to treat such differences as a bug in the implementation of SE-0249, which promises that the keypath-to-function conversion will have "semantics equivalent to capturing the key path and applying it to the Root argument": - -```swift -evil({ kp in { $0[keyPath: kp] } }(\String.isEmpty)) // Prints 'concrete' -``` - -The circumstances necessary are exceedingly narrow to reproduce the above behavior. It is not enough merely to declare the `evil` overloads. The following naive reproduction attempt fails to compile, thus posing no source compatibility error: - -```swift -struct S { - let x: Bool -} - -func evil(_: (T) -> U) { } -func evil(_: (S) -> Bool?) { } -evil(\S.x) -``` - -That this compilation fails seems to be a bug of its own. The additional necessary ingredient appears to be overloading `x`—with the following additions, the snipped above compiles without error: - -```swift -protocol P {} -extension P { - var x: Bool { true } -} -extension S: P {} -``` - -So, to summarize, in order to cause a source compatibility issue, one must have: -1. Function overloads A and B accepting function arguments -2. A call to the overloaded function name, passed a keypath literal using an overloaded property -3. The keypath is viable for conversion to exactly one of A or B's parameter types under currently-implemented rules, but viable for conversion to _both_ A and B's parameter types under the rules in this proposal -4. The overload which is *not* currently viable would outrank the currently-viable overload once both are made viable under this proposal -5. A and B are not semantically interchangable such that calling one instead of the other results in a substantive change in behavior - -In the author's judgement, the liklihood of any significant compatibility issue is negligible. +we today resolve the `evil` call as referring to the generic function, since the conversion necessary for the concrete function is invalid. This proposal would make both overloads viable, but the concrete version is still considered a worse solution since it requires more conversions to reach a solution (not only does the keypath need to be converted to a function, but the 'natural' type of the keypath function is `(String) -> Bool`, which requires an additional conversion from the type system's perspective). ## Effect on ABI stability From 275a19ce18b03ebf62606e0eb3fdc69b45999bc3 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 10 Oct 2023 09:18:03 -0700 Subject: [PATCH 3334/4563] Create more fluent specialized operations Update detailed design as well --- proposals/NNNN-atomics.md | 152 ++++++++++++-------------------------- 1 file changed, 47 insertions(+), 105 deletions(-) diff --git a/proposals/NNNN-atomics.md b/proposals/NNNN-atomics.md index 94c4295247..cd1adee764 100644 --- a/proposals/NNNN-atomics.md +++ b/proposals/NNNN-atomics.md @@ -702,24 +702,15 @@ These specialized integer operations generally come in two variants, based on wh | Method Name | Returns | Implements | | --- | --- | --- | -| `loadThenWrappingIncrement(by:ordering:)` | original value | `a &+= b` | -| `loadThenWrappingDecrement(by:ordering:)` | original value | `a &-= b` | -| `loadThenBitwiseAnd(with:ordering:)` | original value | `a &= b` | -| `loadThenBitwiseOr(with:ordering:)` | original value | `a \|= b` | -| `loadThenBitwiseXor(with:ordering:)` | original value | `a ^= b` | -| `loadThenMin(with:ordering:)` | original value | `a = Swift.min(a, b)` | -| `loadThenMax(with:ordering:` | original value | `a = Swift.max(a, b)` | -| `wrappingIncrementThenLoad(by:ordering:)` | new value | `a &+= b` | -| `wrappingDecrementThenLoad(by:ordering:)` | new value |`a &-= b` | -| `bitwiseAndThenLoad(with:ordering:)` | new value |`a &= b` | -| `bitwiseOrThenLoad(with:ordering:)` | new value |`a \|= b` | -| `bitwiseXorThenLoad(with:ordering:)` | new value |`a ^= b` | -| `minThenLoad(with:ordering:)` | new value | `a = Swift.min(a, b)` | -| `maxThenLoad(with:ordering:` | new value | `a = Swift.max(a, b)` | -| `wrappingIncrement(by:ordering:)` | none | `a &+= b` | -| `wrappingDecrement(by:ordering:)` | none | `a &-= b` | - -The `wrappingIncrement` and `wrappingDecrement` operations are provided as a convenience for incrementing/decrementing values in the common case when a return value is not required. +| `wrappingIncrement(by:ordering:)` | `(oldValue:newValue:)` | `a &+= b` | +| `wrappingDecrement(by:ordering:)` | `(oldValue:newValue)` | `a &-= b` | +| `bitwiseAnd(with:ordering:)` | `(oldValue:newValue)` | `a &= b` | +| `bitwiseOr(with:ordering:)` | `(oldValue:newValue)` | `a |= b` | +| `bitwiseXor(with:ordering:)` | `(oldValue:newValue)` | `a ^= b` | +| `min(with:ordering:)` | `(oldValue:newValue)` | `a = Swift.min(a, b)` | +| `max(with:ordering:` | `(oldValue:newValue)` | `a = Swift.max(a, b)` | + +All operations are also marked as `@discardableResult` in the case where one doesn't care about the old value or new value. The compiler can't optimize the atomic operation away if the return value isn't used. While we require all atomic operations to be free of locks, we don't require wait-freedom. Therefore, on architectures that don't provide direct hardware support for some or all of these operations, we still require them to be implemented using `compareExchange` loops like the one for `wrappingIncrement` above. @@ -732,20 +723,21 @@ extension Atomic where Value == UInt8 {...} let counter = Atomic(0) counter.wrappingIncrement(by: 42, ordering: .relaxed) + +let oldMax = counter.max(with: 82, ordering: .relaxed).oldValue ``` ### Specialized Boolean Operations Similar to the specialized integer operations, we can provide similar ones for booleans with the same two variants: -| Method Name | Returns | Implements | -|--------------------------------------|----------------|----------------| -| `loadThenLogicalAnd(with:ordering:)` | original value | `a = a && b` | -| `loadThenLogicalOr(with:ordering:)` | original value | `a = a \|\| b` | -| `loadThenLogicalXor(with:ordering:)` | original value | `a = a ^ b` | -| `logicalAndThenLoad(with:ordering:)` | new value | `a = a && b` | -| `logicalOrThenLoad(with:ordering:)` | new value | `a = a \|\| b` | -| `logicalXorThenLoad(with:ordering:)` | new value | `a = a ^ b` | +| Method Name | Returns | Implements | +| ---------------------------- | --------------------- | ------------ | +| `logicalAnd(with:ordering:)` | `(oldValue:newValue)` | `a = a && b` | +| `logicalOr(with:ordering:)` | `(oldValue:newValue)` | `a = a || b` | +| `logicalXor(with:ordering:)` | `(oldValue:newValue)` | `a = a != b` | + +Like the integer operations, all of these boolean operations are marked as `@discardableResult`. `Atomic` exposes these operations when `Value` is `Bool`. @@ -753,7 +745,7 @@ Similar to the specialized integer operations, we can provide similar ones for b extension Atomic where Value == Bool {...} let tracker = Atomic(false) -let new = tracker.logicalOrThenLoad(with: true, ordering: .relaxed) +let newOr = tracker.logicalOr(with: true, ordering: .relaxed).newValue ``` ### Atomic Lazy References @@ -1240,85 +1232,47 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { ```swift extension Atomic where Value == Int { - public borrowing func loadThenWrappingIncrement( - by operand: Value = 1, - ordering: AtomicUpdateOrdering - ) -> Value - - public borrowing func wrappingIncrementThenLoad( - by operand: Value = 1, - ordering: AtomicUpdateOrdering - ) -> Value - + @discardableResult public borrowing func wrappingIncrement( by operand: Value = 1, ordering: AtomicUpdateOrdering - ) - - public borrowing func loadThenWrappingDecrement( - by operand: Value = 1, - ordering: AtomicUpdateOrdering - ) -> Value - - public borrowing func wrappingDecrementThenLoad( - by operand: Value = 1, - ordering: AtomicUpdateOrdering - ) -> Value + ) -> (oldValue: Value, newValue: Value) + @discardableResult public borrowing func wrappingDecrement( by operand: Value = 1, ordering: AtomicUpdateOrdering - ) + ) -> (oldValue: Value, newValue: Value) - public borrowing func loadThenBitwiseAnd( + @discardableResult + public borrowing func bitwiseAnd( with operand: Value, ordering: AtomicUpdateOrdering - ) -> Value + ) -> (oldValue: Value, newValue: Value) - public borrowing func bitwiseAndThenLoad( + @discardableResult + public borrowing func bitwiseOr( with operand: Value, ordering: AtomicUpdateOrdering - ) -> Value + ) -> (oldValue: Value, newValue: Value) - public borrowing func loadThenBitwiseOr( + @discardableResult + public borrowing func bitwiseXor( with operand: Value, ordering: AtomicUpdateOrdering - ) -> Value + ) -> (oldValue: Value, newValue: Value) - public borrowing func bitwiseOrThenLoad( + @discardableResult + public borrowing func min( with operand: Value, ordering: AtomicUpdateOrdering - ) -> Value + ) -> (oldValue: Value, newValue: Value) - public borrowing func loadThenBitwiseXor( + @discardableResult + public borrowing func max( with operand: Value, ordering: AtomicUpdateOrdering - ) -> Value - - public borrowing func bitwiseXorThenLoad( - with operand: Value, - ordering: AtomicUpdateOrdering - ) -> Value - - public borrowing func loadThenMin( - with operand: Value, - ordering: AtomicUpdateOrdering - ) -> Value - - public borrowing func minThenLoad( - with operand: Value, - ordering: AtomicUpdateOrdering - ) -> Value - - public borrowing func loadThenMax( - with operand: Value, - ordering: AtomicUpdateOrdering - ) -> Value - - public borrowing func maxThenLoad( - with operand: Value, - ordering: AtomicUpdateOrdering - ) -> Value + ) -> (oldValue: Value, newValue: Value) } extension Atomic where Value == Int8 {...} @@ -1329,35 +1283,23 @@ as well as providing convenience functions for boolean operations: ```swift extension Atomic where Value == Bool { - public borrowing func loadThenLogicalAnd( + @discardableResult + public borrowing func logicalAnd( with operand: Value, ordering: AtomicUpdateOrdering - ) -> Value - - public borrowing func loadThenLogicalOr( - with operand: Value, - ordering: AtomicUpdateOrdering - ) -> Value + ) -> (oldValue: Value, newValue: Value) - public borrowing func loadThenLogicalXor( + @discardableResult + public borrowing func logicalOr( with operand: Value, ordering: AtomicUpdateOrdering - ) -> Value + ) -> (oldValue: Value, newValue: Value) - public borrowing func logicalAndThenLoad( + @discardableResult + public borrowing func logicalXor( with operand: Value, ordering: AtomicUpdateOrdering - ) -> Value - - public borrowing func logicalOrThenLoad( - with operand: Value, - ordering: AtomicUpdateOrdering - ) -> Value - - public borrowing func logicalXorThenLoad( - with operand: Value, - ordering: AtomicUpdateOrdering - ) -> Value + ) -> (oldValue: Value, newValue: Value) } ``` From b805dcf99bacb0a8014d43b3b4d8ddc741b5842e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 11 Oct 2023 07:08:35 -0700 Subject: [PATCH 3335/4563] Mark SE-0404 as implemented in 5.10. (#2185) --- proposals/0404-nested-protocols.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0404-nested-protocols.md b/proposals/0404-nested-protocols.md index 44ce6739f2..9722c8b280 100644 --- a/proposals/0404-nested-protocols.md +++ b/proposals/0404-nested-protocols.md @@ -3,7 +3,7 @@ * Proposal: [SE-0404](0404-nested-protocols.md) * Authors: [Karl Wagner](https://github.com/karwa) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Accepted** +* Status: **Implemented (Swift 5.10)** * Implementation: [apple/swift#66247](https://github.com/apple/swift/pull/66247) (gated behind flag `-enable-experimental-feature NestedProtocols`) * Review: ([pitch](https://forums.swift.org/t/pitch-allow-protocols-to-be-nested-in-non-generic-contexts/65285)), ([review](https://forums.swift.org/t/se-0404-allow-protocols-to-be-nested-in-non-generic-contexts/66332)), ([acceptance](https://forums.swift.org/t/accepted-se-0404-allow-protocols-to-be-nested-in-non-generic-contexts/66668)) From f447a106427b76a71e75f6b5b9fb8e3474e9908f Mon Sep 17 00:00:00 2001 From: Angela Laar Date: Mon, 28 Aug 2023 16:15:18 -0700 Subject: [PATCH 3336/4563] Proposal - Inferring @Sendable for methods --- proposals/nnnn-inferring-senable-for-methods | 212 +++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 proposals/nnnn-inferring-senable-for-methods diff --git a/proposals/nnnn-inferring-senable-for-methods b/proposals/nnnn-inferring-senable-for-methods new file mode 100644 index 0000000000..3a8ae62fac --- /dev/null +++ b/proposals/nnnn-inferring-senable-for-methods @@ -0,0 +1,212 @@ +# [Pitch] Inferring `@Sendable` for methods + +* Proposal: [SE-NNNN](https://github.com/kavon/swift-evolution/blob/sendable-functions/proposals/NNNN-filename.md) +* Authors: [Angela Laar](https://github.com/angela-laar), [Kavon Farvardin](https://github.com/kavon) +* Review Manager: TBD +* Status: Awaiting Implementation + +## Introduction + +This proposal is focused on a few corner cases in the language surrounding functions as values when using concurrency. The goal is to improve flexibility, simplicity, and ergonomics without significant changes to Swift. + +## Motivation + +The partial application of methods and other first-class uses of functions have a few rough edges when combined with concurrency. + +Let’s look at partial application on its own before we combine it with concurrency. In Swift, you can create a function-value representing a method by writing an expression that only accesses (but does not call) a method using one of its instances. This access is referred to as a "partial application" of a method to one of its (curried) arguments - the object instance. + +``` +struct S { + func f() { ... } +} + +let partial: (() -> Void) = S().f +``` + + +When referencing a method *without* partially applying it to the object instance, using the expression NominalType.method, we call it "unapplied." + + +``` +let unapplied:(T) -> (() -> Void) = S.f +``` + + +Suppose we want to create a generic method that expects an unapplied function method conforming to Senable as a parameter. We can create a protocol ``P`` that conforms to the `Sendable` protocol and tell our generic function to expect some generic type that conforms to ``P``. We can also use the `@Sendable` attribute, introduced for closures and functions in [SE-302](https://github.com/kavon/swift-evolution/blob/sendable-functions/proposals/0302-concurrent-value-and-concurrent-closures.md), to annotate the closure parameter. + + +``` +protocol P: Sendable { + init() +} + +func g(_ f: @escaping @Sendable (T) -> (() -> Void)) where T: P { + Task { + let instance = T() + f(instance)() + } +} +``` + +Now let’s call our method and pass our struct type `S` . First we should make `S` conform to Sendable, which we can do by making `S` conform to our new Sendable type `P` . + +This should make `S` and its methods Sendable as well. However, when we pass our unapplied function `S.f` to our generic function `g`, we get a warning that `S.f` is not Sendable as `g()` is expecting. + + +``` +struct S: P { + func f() { ... } +} + +g(S.f) // Converting non-sendable function value to '@Sendable (S) -> (() -> Void)' may introduce data races +``` + + +We can work around this by wrapping our unapplied function in a Sendable closure. + +``` +// S.f($0) == S.f() +g({ @Sendable **in** S.f($0) }) +``` + + +This is a lot of churn to get the expected behavior. The compiler should preserve `@Sendable` in the type signature instead. + +## Proposed solution + +For a function, the `@Sendable` attribute primarily influences the kinds of values that can be captured by the function. But methods of a nominal type do not capture anything but the object instance itself. Semantically, a method can be thought of as being represented by the following functions: + + +``` +// Pseudo-code declaration of a Nominal Type: +type NominalType { + func method(ArgType) -> ReturnType { /* body of method */ } +} + +// Can desugar to these two global functions: +func NominalType_method_partiallyAppliedTo(_ obj: NominalType) -> ((ArgType) -> ReturnType) { + let inner = { [obj] (_ arg1: ArgType) -> ReturnType in + return NominalType_method(obj, arg1) + } + return inner +} +func NominalType_method(_ self: NominalType, _ arg1: ArgType) -> ReturnType { + return self.method(arg1) +} +``` + +Thus, the only way a partially-applied method can be `@Sendable` is if the `inner` closure were `@Sendable`, which is true if and only if the nominal type conforms to `Sendable`. + + +``` +type NominalType : Sendable { + func method(ArgType) -> ReturnType { /* body of method */ } +} +``` + +For example, by declaring the following type `Sendable`, the partial and unapplied function values of the type would have implied Sendabilty and the following code would compile with no errors. + +``` +struct User : Sendable { + func updatePassword (new: String, old:String) -> Bool { /* update password*/ return true} +} + +**let** unapplied: **@Sendable** (User) → ((String, String) → Bool) = User.updatePassword // no error + +**let** partial: **@Sendable** (String, String) → Bool = User().updatePassword // no error +``` + + + +## Detailed design + +This proposal includes five changes to `Sendable` behavior. + +The first two are what we just discussed regarding partial and unapplied function values. + +``` +struct User : Sendable { + var address + var password + + func changeAddress () {/*do work*/ } +} +``` + +1. The inference of `@Sendable` for unapplied references to methods of a Sendable type. + +``` +**let** unapplied: **@Sendable** (User) → ((String, String) → Void) = User.changeAddress // no error +``` + +1. The inference of `@Sendable` for partially-applied methods of a Sendable type. + +``` +**let** partial: **@Sendable** (String, String) → Void = User().changeAddress // no error +``` + +The next few are: + +1. The inference of `@Sendable` when referencing non-local functions. + +Unlike closures, which retain the captured value, global functions can't capture any variables - because global variables are just referenced by the function without any ownership. With this in mind there is no reason not to make these, Sendable by default. + +``` +func doWork() -> Int { + `Int.random(in: 1..<42)` +} + +Task.detached(priority: **nil**, operation: doWork) // Converting non-sendable function value to '@Sendable () async -> Void' may introduce data races +``` + +Currently, trying to start a `Task` with the global function `doWork` will cause an error complaining that the function is not `Sendable`. This should compile with no issue. + +1. Prohibition of marking methods `@Sendable` when the type they belong to is not `@Sendable`. + 1. class C { + var random: Int = 0 // random is mutable so `C` can't be checked sendable + + @Sendable func generateN() async -> Int { //error: adding @Sendable to function of non-Senable type prohibited + random = Int.random(in: 1..<100) + return random + } + } + + Task.detached { + let num = C() + let n = await num.generateN() + num.random = 42 // accessing the `random` var while generateN is mutating it + } + 2. If we move the previous work we wanted to do into a class that stores the random number we generate as a mutable value, we could be introducing a data race by marking the function responsible for this work `@Sendable` . Doing this should be prohibited by the compiler. + +Since `@Sendable` attribute will be automatically determined with this proposal, you don’t have to explicitly write it on function declarations. + +## Source compatibility + +No impact. + +## Effect on ABI stability + +This would impact the mangling of function names. + +## Effect on API resilience + +No effect on ABI stability. + +## Future Directions + +Accessors are not currently allowed to participate with the `@Sendable` system in this proposal. It would be straight-forward to allow getters to do so in a future proposal if there was demand for this. + +## Alternatives Considered + +Swift could forbid explicitly marking function declarations with the` @Sendable` attribute, since under this proposal there’s no longer any reason to do this. + +``` +/***@Sendable*/** func alwaysSendable() {} +``` + +However, since these attributes are allowed today, this would be a source breaking change. Swift 6 could potentially include fix-its to remove `@Sendable` attributes to ease migration, but it’d still be disruptive. The attributes are harmless under this proposal, and they’re still sometimes useful for code that needs to compile with older tools, so we have chosen not to make this change in this proposal. We can consider deprecation at a later time if we find a good reason to do so. + + + + + From 45a89bafbea66527c89f1962eb4eafc076413c85 Mon Sep 17 00:00:00 2001 From: Angela Laar Date: Wed, 11 Oct 2023 10:13:55 -0700 Subject: [PATCH 3337/4563] Update proposal --- ... => nnnn-inferring-senable-for-methods.md} | 87 ++++++++++--------- 1 file changed, 48 insertions(+), 39 deletions(-) rename proposals/{nnnn-inferring-senable-for-methods => nnnn-inferring-senable-for-methods.md} (61%) diff --git a/proposals/nnnn-inferring-senable-for-methods b/proposals/nnnn-inferring-senable-for-methods.md similarity index 61% rename from proposals/nnnn-inferring-senable-for-methods rename to proposals/nnnn-inferring-senable-for-methods.md index 3a8ae62fac..bc6f4e4e3e 100644 --- a/proposals/nnnn-inferring-senable-for-methods +++ b/proposals/nnnn-inferring-senable-for-methods.md @@ -1,9 +1,10 @@ -# [Pitch] Inferring `@Sendable` for methods +# Inferring `@Sendable` for methods * Proposal: [SE-NNNN](https://github.com/kavon/swift-evolution/blob/sendable-functions/proposals/NNNN-filename.md) * Authors: [Angela Laar](https://github.com/angela-laar), [Kavon Farvardin](https://github.com/kavon) * Review Manager: TBD * Status: Awaiting Implementation +* Review: ([pitch](https://forums.swift.org/t/pitch-inferring-sendable-for-methods/66565)) ## Introduction @@ -28,11 +29,11 @@ When referencing a method *without* partially applying it to the object instanc ``` -let unapplied:(T) -> (() -> Void) = S.f +let unapplied: (T) -> (() -> Void) = S.f ``` -Suppose we want to create a generic method that expects an unapplied function method conforming to Senable as a parameter. We can create a protocol ``P`` that conforms to the `Sendable` protocol and tell our generic function to expect some generic type that conforms to ``P``. We can also use the `@Sendable` attribute, introduced for closures and functions in [SE-302](https://github.com/kavon/swift-evolution/blob/sendable-functions/proposals/0302-concurrent-value-and-concurrent-closures.md), to annotate the closure parameter. +Suppose we want to create a generic method that expects an unapplied function method conforming to Sendable as a parameter. We can create a protocol ``P`` that conforms to the `Sendable` protocol and tell our generic function to expect some generic type that conforms to ``P``. We can also use the `@Sendable` attribute, introduced for closures and functions in [SE-302](https://github.com/kavon/swift-evolution/blob/sendable-functions/proposals/0302-concurrent-value-and-concurrent-closures.md), to annotate the closure parameter. ``` @@ -74,6 +75,8 @@ This is a lot of churn to get the expected behavior. The compiler should preserv ## Proposed solution +We propose the compiler should automatically employ `@Sendable` to functions that cannot capture non-Sendable states. This includes partially-applied and unapplied instance methods of `Sendable` types, as well as non-local functions. Additionally, it should be disallowed to utilize `@Sendable` on instance methods of non-`Sendable` types. + For a function, the `@Sendable` attribute primarily influences the kinds of values that can be captured by the function. But methods of a nominal type do not capture anything but the object instance itself. Semantically, a method can be thought of as being represented by the following functions: @@ -90,8 +93,9 @@ func NominalType_method_partiallyAppliedTo(_ obj: NominalType) -> ((ArgType) -> } return inner } +// The actual method call func NominalType_method(_ self: NominalType, _ arg1: ArgType) -> ReturnType { - return self.method(arg1) + /* body of method */ } ``` @@ -104,25 +108,27 @@ type NominalType : Sendable { } ``` -For example, by declaring the following type `Sendable`, the partial and unapplied function values of the type would have implied Sendabilty and the following code would compile with no errors. - +For example, by declaring the following type `Sendable`, the partial and unapplied function values of the type would have implied Sendability and the following code would compile with no errors. ``` struct User : Sendable { - func updatePassword (new: String, old:String) -> Bool { /* update password*/ return true} + func updatePassword (new: String, old:String) -> Bool { + /* update password*/ + return true + } } -**let** unapplied: **@Sendable** (User) → ((String, String) → Bool) = User.updatePassword // no error +let unapplied: @Sendable (User) -> ((String, String) → Bool) = User.updatePassword // no error -**let** partial: **@Sendable** (String, String) → Bool = User().updatePassword // no error +let partial: @Sendable (String, String) -> Bool = User().updatePassword // no error ``` ## Detailed design -This proposal includes five changes to `Sendable` behavior. +This proposal includes four changes to `Sendable` behavior. -The first two are what we just discussed regarding partial and unapplied function values. +The first two are what we just discussed regarding partial and unapplied methods. ``` struct User : Sendable { @@ -136,24 +142,26 @@ struct User : Sendable { 1. The inference of `@Sendable` for unapplied references to methods of a Sendable type. ``` -**let** unapplied: **@Sendable** (User) → ((String, String) → Void) = User.changeAddress // no error +let unapplied : @Sendable (User) → ((String, String) → Void) = User.changeAddress // no error ``` -1. The inference of `@Sendable` for partially-applied methods of a Sendable type. +2. The inference of `@Sendable` for partially-applied methods of a Sendable type. ``` -**let** partial: **@Sendable** (String, String) → Void = User().changeAddress // no error +let partial : @Sendable (String, String) → Void = User().changeAddress // no error ``` -The next few are: +These two rules include partially applied and unapplied static methods but do not include partially applied or unapplied mutable methods. Unapplied references to mutable methods are not allowed in the language because they can lead to undefined behavior. More details about this can be found in [SE-0042](https://github.com/apple/swift-evolution/blob/main/proposals/0042-flatten-method-types.md). + +Next is: -1. The inference of `@Sendable` when referencing non-local functions. +3. The inference of `@Sendable` when referencing non-local functions. -Unlike closures, which retain the captured value, global functions can't capture any variables - because global variables are just referenced by the function without any ownership. With this in mind there is no reason not to make these, Sendable by default. +Unlike closures, which retain the captured value, global functions can't capture any variables - because global variables are just referenced by the function without any ownership. With this in mind there is no reason not to make these `Sendable` by default. This change will also include static global functions. ``` func doWork() -> Int { - `Int.random(in: 1..<42)` +` Int.random(in: 1..<42)` } Task.detached(priority: **nil**, operation: doWork) // Converting non-sendable function value to '@Sendable () async -> Void' may introduce data races @@ -161,24 +169,29 @@ Task.detached(priority: **nil**, operation: doWork) // Converting no Currently, trying to start a `Task` with the global function `doWork` will cause an error complaining that the function is not `Sendable`. This should compile with no issue. -1. Prohibition of marking methods `@Sendable` when the type they belong to is not `@Sendable`. - 1. class C { - var random: Int = 0 // random is mutable so `C` can't be checked sendable - - @Sendable func generateN() async -> Int { //error: adding @Sendable to function of non-Senable type prohibited - random = Int.random(in: 1..<100) - return random - } - } +4. Prohibition of marking methods `@Sendable` when the type they belong to is not `@Sendable`. +``` + class C { + var random: Int = 0 // random is mutable so `C` can't be checked sendable - Task.detached { - let num = C() - let n = await num.generateN() - num.random = 42 // accessing the `random` var while generateN is mutating it + @Sendable func generateN() async -> Int { //error: adding @Sendable to function of non-Senable type prohibited + random = Int.random(in: 1..<100) + return random } - 2. If we move the previous work we wanted to do into a class that stores the random number we generate as a mutable value, we could be introducing a data race by marking the function responsible for this work `@Sendable` . Doing this should be prohibited by the compiler. + } + + func test(c: C) { c.generateN() } -Since `@Sendable` attribute will be automatically determined with this proposal, you don’t have to explicitly write it on function declarations. + let num = C() + Task.detached { + test(num) + } + test(num) // data-race +``` + +If we move the previous work we wanted to do into a class that stores the random number we generate as a mutable value, we could be introducing a data race by marking the function responsible for this work `@Sendable` . Doing this should be prohibited by the compiler. + +Since `@Sendable` attribute will be automatically determined with this proposal, you will no longer have to explicitly write it on function and method declarations. ## Source compatibility @@ -186,7 +199,7 @@ No impact. ## Effect on ABI stability -This would impact the mangling of function names. +When you remove an explicit `@Sendable` from a method, the mangling of that method will change. Since `@Sendable` will now be inferred, if you choose to remove the explicit annotation to "adopt" the inference, you may need to consider the mangling change. ## Effect on API resilience @@ -201,12 +214,8 @@ Accessors are not currently allowed to participate with the `@Sendable` system i Swift could forbid explicitly marking function declarations with the` @Sendable` attribute, since under this proposal there’s no longer any reason to do this. ``` -/***@Sendable*/** func alwaysSendable() {} +/*@Sendable*/ func alwaysSendable() {} ``` However, since these attributes are allowed today, this would be a source breaking change. Swift 6 could potentially include fix-its to remove `@Sendable` attributes to ease migration, but it’d still be disruptive. The attributes are harmless under this proposal, and they’re still sometimes useful for code that needs to compile with older tools, so we have chosen not to make this change in this proposal. We can consider deprecation at a later time if we find a good reason to do so. - - - - From b36c56879267688ac8a81185dee2599e3c3a1fda Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 12 Oct 2023 12:09:54 -0700 Subject: [PATCH 3338/4563] Use * for bullet points in the metadata list. The existing markup is valid, but this change makes these proposals match everything else. --- proposals/0165-dict.md | 10 +++++----- proposals/0379-opt-in-reflection-metadata.md | 13 ++++++------- proposals/0387-cross-compilation-destinations.md | 12 ++++++------ proposals/0392-custom-actor-executors.md | 13 ++++++------- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/proposals/0165-dict.md b/proposals/0165-dict.md index 0c96c4a61f..d33a7b192c 100644 --- a/proposals/0165-dict.md +++ b/proposals/0165-dict.md @@ -1,10 +1,10 @@ # Dictionary & Set Enhancements -- Proposal: [SE-0165](0165-dict.md) -- Author: [Nate Cook](https://github.com/natecook1000) -- Review Manager: [Ben Cohen](https://github.com/airspeedswift) -- Status: **Implemented (Swift 4)** -- Decision Notes: [Rationale][rationale] +* Proposal: [SE-0165](0165-dict.md) +* Author: [Nate Cook](https://github.com/natecook1000) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Implemented (Swift 4)** +* Decision Notes: [Rationale][rationale] ## Introduction diff --git a/proposals/0379-opt-in-reflection-metadata.md b/proposals/0379-opt-in-reflection-metadata.md index c5085ce9df..ad2ecc69a8 100644 --- a/proposals/0379-opt-in-reflection-metadata.md +++ b/proposals/0379-opt-in-reflection-metadata.md @@ -1,12 +1,11 @@ # Swift Opt-In Reflection Metadata -* Proposal: [SE-0379](0379-opt-in-reflection-metadata.md) -* Authors: [Max Ovtsin](https://github.com/maxovtsin) -* Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Returned for revision** -* Implementation: [apple/swift#34199](https://github.com/apple/swift/pull/34199) -* Review: ([first pitch](https://forums.swift.org/t/proposal-opt-in-reflection-metadata/40981)) ([second pitch](https://forums.swift.org/t/pitch-2-opt-in-reflection-metadata/41696)) ([third pitch](https://forums.swift.org/t/pitch-3-opt-in-reflection-metadata/58852)) ([review](https://forums.swift.org/t/se-0379-opt-in-reflection-metadata/61714)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0379-opt-in-reflection-metadata/62390)) - +* Proposal: [SE-0379](0379-opt-in-reflection-metadata.md) +* Authors: [Max Ovtsin](https://github.com/maxovtsin) +* Review Manager: [Joe Groff](https://github.com/jckarter) +* Status: **Returned for revision** +* Implementation: [apple/swift#34199](https://github.com/apple/swift/pull/34199) +* Review: ([first pitch](https://forums.swift.org/t/proposal-opt-in-reflection-metadata/40981)) ([second pitch](https://forums.swift.org/t/pitch-2-opt-in-reflection-metadata/41696)) ([third pitch](https://forums.swift.org/t/pitch-3-opt-in-reflection-metadata/58852)) ([review](https://forums.swift.org/t/se-0379-opt-in-reflection-metadata/61714)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0379-opt-in-reflection-metadata/62390)) ## Introduction diff --git a/proposals/0387-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md index 01f8374d22..abaf7c31b6 100644 --- a/proposals/0387-cross-compilation-destinations.md +++ b/proposals/0387-cross-compilation-destinations.md @@ -1,14 +1,14 @@ # Swift SDKs for Cross-Compilation -- Proposal: [SE-0387](0387-cross-compilation-destinations.md) -- Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd), [Evan Wilde](https://github.com/etcwilde) -- Review Manager: [Mishal Shah](https://github.com/shahmishal) -- Status: **Accepted** -- Implementation: [apple/swift-package-manager#5911](https://github.com/apple/swift-package-manager/pull/5911), +* Proposal: [SE-0387](0387-cross-compilation-destinations.md) +* Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd), [Evan Wilde](https://github.com/etcwilde) +* Review Manager: [Mishal Shah](https://github.com/shahmishal) +* Status: **Accepted** +* Implementation: [apple/swift-package-manager#5911](https://github.com/apple/swift-package-manager/pull/5911), [apple/swift-package-manager#5922](https://github.com/apple/swift-package-manager/pull/5922), [apple/swift-package-manager#6023](https://github.com/apple/swift-package-manager/pull/6023), [apple/swift-package-manager#6186](https://github.com/apple/swift-package-manager/pull/6186) -- Review: ([pitch](https://forums.swift.org/t/pitch-cross-compilation-destination-bundles/61777)) +* Review: ([pitch](https://forums.swift.org/t/pitch-cross-compilation-destination-bundles/61777)) ([first review](https://forums.swift.org/t/se-0387-cross-compilation-destination-bundles/62875)) ([second review](https://forums.swift.org/t/second-review-se-0387-cross-compilation-destination-bundles/64660)) diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index afc8fbe640..ab0b5ffe0f 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -1,20 +1,19 @@ # Custom Actor Executors -- Proposal: [SE-0392](0392-custom-actor-executors.md) -- Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [John McCall](https://github.com/rjmccall), [Kavon Farvardin](https://github.com/kavon) -- Review Manager: [Joe Groff](https://github.com/jckarter) -- Status: **Implemented (Swift 5.9)** -- Previous threads: +* Proposal: [SE-0392](0392-custom-actor-executors.md) +* Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso), [John McCall](https://github.com/rjmccall), [Kavon Farvardin](https://github.com/kavon) +* Review Manager: [Joe Groff](https://github.com/jckarter) +* Status: **Implemented (Swift 5.9)** +* Previous threads: - Original pitch thread from around Swift 5.5: [Support custom executors in Swift Concurrency](https://forums.swift.org/t/support-custom-executors-in-swift-concurrency/44425) - Original "assume..." proposal which was subsumed into this proposal, as it relates closely to asserting on executors: [Pitch: Unsafe Assume on MainActor](https://forums.swift.org/t/pitch-unsafe-assume-on-mainactor/63074/) -- Reviews: +* Reviews: - First review thread: https://forums.swift.org/t/returned-for-revision-se-0392-custom-actor-executors/64172 - Revisions: - Rename `Job` to `ExecutorJob`, making it less likely to conflict with existing type names, and typealias `UnownedJob` with `UnownedExecutorJob` (however the old type remains for backwards compatibility). - Move assert/precondition/assume APIs to extensions on actor types, e.g. `Actor/assertIsolated`, `DistributedActor/preconditionIsolated`, `MainActor/assumeIsolated { ... }` - Distributed actor executor customization `unownedExecutor` invoked on a remote distributed actor, to return an executor that fatal errors only once attempts are made to enqueue work onto it, rather than crashing immediately upon attempting to obtain the executor. - ## Table of Contents - [Custom Actor Executors](#custom-actor-executors) From a576c1f101307e6fe303ca437d0411a44ea3d0ae Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Sun, 15 Oct 2023 22:05:56 -0700 Subject: [PATCH 3339/4563] Apply more feedback from pitch --- proposals/NNNN-atomics.md | 217 ++++++++++++++++++++++++++------------ 1 file changed, 147 insertions(+), 70 deletions(-) diff --git a/proposals/NNNN-atomics.md b/proposals/NNNN-atomics.md index cd1adee764..5f68cefee1 100644 --- a/proposals/NNNN-atomics.md +++ b/proposals/NNNN-atomics.md @@ -42,7 +42,7 @@ New Swift-evolution thread: [Atomics](https://forums.swift.org/t/atomics/67350) * [Optional Atomics](#optional-atomics) * [Custom Atomic Types](#custom-atomic-types) * [Atomic Storage Types](#atomic-storage-types) - * [DoubleWord](#doubleword) + * [WordPair](#wordpair) * [The Atomic type](#the-atomic-type) * [Basic Atomic Operations](#basic-atomic-operations) * [Specialized Integer Operations](#specialized-integer-operations) @@ -57,7 +57,7 @@ New Swift-evolution thread: [Atomics](https://forums.swift.org/t/atomics/67350) * [AtomicValue](#atomicvalue) * [AtomicOptionalWrappable](#atomicoptionalwrappable) * [Atomic Storage Types](#atomic-storage-types-1) - * [DoubleWord](#doubleword-1) + * [WordPair](#wordpair-1) * [Atomic Types](#atomic-types) * [Atomic<Value>](#atomicvalue-1) * [AtomicLazyReference<Instance>](#atomiclazyreferenceinstance) @@ -115,7 +115,7 @@ let counter = Atomic(0) DispatchQueue.concurrentPerform(iterations: 10) { _ in for _ in 0 ..< 1_000_000 { - counter.wrappingIncrement(ordering: .relaxed) + counter.wrappingAdd(1, ordering: .relaxed) } } @@ -250,15 +250,36 @@ extension UInt8: AtomicValue {...} extension Bool: AtomicValue {...} +extension Float16: AtomicValue {...} +extension Float: AtomicValue {...} +extension Double: AtomicValue {...} + +/// New type in the standard library discussed +/// shortly after this. +extension WordPair: AtomicValue {...} + +extension Duration: AtomicValue {...} + +extension Never: AtomicValue {...} + extension UnsafeRawPointer: AtomicValue {...} extension UnsafeMutableRawPointer: AtomicValue {...} extension UnsafePointer: AtomicValue {...} extension UnsafeMutablePointer: AtomicValue {...} extension Unmanaged: AtomicValue {...} +extension OpaquePointer: AtomicValue {...} +extension ObjectIdentifier: AtomicValue {...} -extension Optional: AtomicValue where Wrapped: AtomicValue, ... {...} +extension UnsafeBufferPointer: AtomicValue {...} +extension UnsafeMutableBufferPointer: AtomicValue {...} +extension UnsafeRawBufferPointer: AtomicValue {...} +extension UnsafeMutableRawBufferPointer: AtomicValue {...} + +extension Optional: AtomicValue where Wrapped: AtomicOptionalWrappable {...} ``` +Note that on 32 bit platforms that do not support double wide atomics, the `UInt64` , `Int64`, and `Double` conformances are not available. On all platforms that do not support double wide atomics, the `UnsafeBufferPointer`, `UnsafeMutableBufferPointer`, `UnsafeRawBufferPointer`, and `UnsafeMutableRawBufferPointer` conformances are not available. `Duration` only conforms to `AtomicValue` on 64 bit platforms that support double wide atomics. + #### Optional Atomics The standard atomic pointer types and unmanaged references also support atomic operations on their optional-wrapped form. To spell out this optional wrapped, we introduce a new protocol: @@ -295,11 +316,11 @@ extension UnsafeMutableRawPointer: AtomicOptionalWrappable {} extension UnsafePointer: AtomicOptionalWrappable {} extension UnsafeMutablePointer: AtomicOptionalWrappable {} extension Unmanaged: AtomicOptionalWrappable {} +extension OpaquePointer: AtomicOptionalWrappable {} +extension ObjectIdentifier: AtomicOptionalWrappable {} ``` -User code is not allowed to extend this list with additional types; this capability is reserved for potential future proposals. - -Atomic optional pointers and references are helpful when building lock-free data structures. (Although this initial set of reference types considerably limits the scope of what can be built; for more details, see the discussion on the [ABA problem](#doubleword) and [memory reclamation](#atomic-strong-references-and-the-problem-of-memory-reclamation).) +Atomic optional pointers and references are helpful when building lock-free data structures. (Although this initial set of reference types considerably limits the scope of what can be built; for more details, see the discussion on the [ABA problem](#wordpair) and [memory reclamation](#atomic-strong-references-and-the-problem-of-memory-reclamation).) For example, consider the lock-free, single-consumer stack implementation below. (It supports an arbitrary number of concurrently pushing threads, but it only allows a single pop at a time.) @@ -341,9 +362,9 @@ class LockFreeSingleConsumerStack { // This method does not support multiple overlapping concurrent calls. func pop() -> Element? { precondition( - _consumerCount.loadThenWrappingIncrement(ordering: .acquiring) == 0, + _consumerCount.wrappingAdd(1, ordering: .acquiring).oldValue == 0, "Multiple consumers detected") - defer { _consumerCount.wrappingDecrement(ordering: .releasing) } + defer { _consumerCount.wrappingSubtract(1, ordering: .releasing) } var done = false var current = _last.load(ordering: .acquiring) while let c = current { @@ -400,33 +421,51 @@ currentState.store(.stopped, ordering: .sequentiallyConsistent) ### Atomic Storage Types -Fundamental to working with atomics is knowing that CPUs can only do atomic operations on integers. While we could theoretically do atomic operations with our current list of standard library integer types (`Int8`, `Int16`, ...), some platforms don't ensure that these types have the same alignment as their size. For example, `Int64` and `UInt64` have 4 byte alignment on i386. Atomic operations must occur on correctly aligned types. To ensure this, we need to introduce helper types that all atomic operations will be traffiked through: - -```swift -public struct AtomicInt8Storage {} -public struct AtomicInt16Storage {} -public struct AtomicInt32Storage {} -public struct AtomicInt64Storage {} -public struct AtomicInt128Storage {} -``` - -Because these are the fundamental atomic storage types, they will serve as the `AtomicRepresentation` for all of the standard integer types: +Fundamental to working with atomics is knowing that CPUs can only do atomic operations on integers. While we could theoretically do atomic operations with our current list of standard library integer types (`Int8`, `Int16`, ...), some platforms don't ensure that these types have the same alignment as their size. For example, `Int64` and `UInt64` have 4 byte alignment on i386. Atomic operations must occur on correctly aligned types. To ensure this, we need to introduce helper types that all atomic operations will be traffiked through. These types will serve as the `AtomicRepresentation` for all of the standard integer types: ```swift extension Int8: AtomicValue { - public typealias AtomicRepresentation = AtomicInt8Storage + public typealias AtomicRepresentation = ... } ... extension UInt64: AtomicValue { - public typealias AtomicRepresentation = AtomicInt64Storage + public typealias AtomicRepresentation = ... } ... ``` -### `DoubleWord` +The actual underlying type is an implementation detail of the standard library. While we generally don't prefer to propose such API, the underlying types themselves are quite useless and only useful for the primitive integers. One can still access the underlying type by using the public name, `Int8.AtomicRepresentation`, for example. An example conformance to `AtomicValue` may look something like the following: + +```swift +struct MyCoolInt { + var x: Int +} + +extension MyCoolInt: AtomicValue { + typealias AtomicRepresentation = Int.AtomicRepresentation + + static func encodeAtomicRepresentation( + _ value: consuming MyCoolInt + ) -> AtomicRepresentation { + Int.encodeAtomicRepresentation(value.x) + } + + static func decodeAtomicRepresentation( + _ representation: consuming AtomicRepresentation + ) -> MyCoolInt { + MyCoolInt( + x:Int.decodeAtomicRepresentation(representation) + ) + } +} +``` + +This works by going `Int`'s `AtomicValue` conformance and converting our `MyCoolInt` -> `Int` -> `Int.AtomicRepresentation` . + +### `WordPair` In their current single-word form, atomic pointer and reference types are susceptible to a class of race condition called the *ABA problem*. A freshly allocated object often happens to be placed at the same memory location as a recently deallocated one. Therefore, two successive `load`s of a simple atomic pointer may return the exact same value, even though the pointer may have received an arbitrary number of updates between the two loads, and the pointee may have been completely replaced. This can be a subtle, but deadly source of race conditions in naive implementations of many concurrent data structures. @@ -435,14 +474,16 @@ While the single-word atomic primitives introduced in this document are already We propose a new separate type that provides an abstraction over the layout of what a double word is for a platform. ```swift -public struct DoubleWord { +public struct WordPair { public var highWord: UInt { get } public var lowWord: UInt { get } public init(highWord: UInt, lowWord: UInt) } -extension DoubleWord: AtomicValue { +// Not a real compilation conditional +#if hasDoubleWideAtomics +extension WordPair: AtomicValue { // Not a real compilation conditional #if 64 bit public typealias AtomicRepresentaton = AtomicInt128Storage @@ -454,15 +495,16 @@ extension DoubleWord: AtomicValue { ... } +#endif ``` For example, the second word can be used to augment atomic values with a version counter (sometimes called a "stamp" or a "tag"), which can help resolve the ABA problem by allowing code to reliably verify if a value remained unchanged between two successive loads. -Note that not all CPUs support double-wide atomic operations and for that reason this type is not always available. Platforms that do not have this support must not make this type available for use. Perhaps a future direction for this is something akin to `#if canImport(struct DoubleWord)` to conditionally compile against this type if it's available. +Note that not all CPUs support double-wide atomic operations and for that reason this type is not always available. Platforms that do not have this support must not make this type available for use. Perhaps a future direction for this is something akin to `#if canImport(struct WordPair)` to conditionally compile against this type if it's available. ### The Atomic type -So far, we've introduced memory orderings, giving us control of memory access around atomic operations; the atomic protocol hierarchy, which give us the initial list of standard types that can be as atomic values; and the `DoubleWord` type, providing an abstraction over a platform's double word type. However, we haven't yet introduced a way to actually _use_ atomics. Here we introduce the single Atomic type that exposes atomic operations for us: +So far, we've introduced memory orderings, giving us control of memory access around atomic operations; the atomic protocol hierarchy, which give us the initial list of standard types that can be as atomic values; and the `WordPair` type, providing an abstraction over a platform's double word type. However, we haven't yet introduced a way to actually _use_ atomics. Here we introduce the single Atomic type that exposes atomic operations for us: ```swift /// An atomic value. @@ -673,12 +715,12 @@ All four variants implement the same algorithm. The single ordering variants use The `weakCompareExchange` form may sometimes return false even when the original and expected values are equal. (Such failures may happen when some transient condition prevents the underlying operation from succeeding -- such as an incoming interrupt during a load-link/store-conditional instruction sequence.) This variant is designed to be called in a loop that only exits when the exchange is successful; in such loops, using `weakCompareExchange` may lead to a performance improvement by eliminating a nested loop in the regular, "strong", `compareExchange` variants. -The compare-exchange primitive is special: it is a universal operation that can be used to implement all other atomic operations, and more. For example, here is how we could use `compareExchange` to implement a wrapping increment operation over `Atomic` values: +The compare-exchange primitive is special: it is a universal operation that can be used to implement all other atomic operations, and more. For example, here is how we could use `compareExchange` to implement a wrapping add operation over `Atomic` values: ```swift extension Atomic where Value == Int { - func wrappingIncrement( - by operand: Int, + func wrappingAdd( + _ operand: Int, ordering: AtomicUpdateOrdering ) { var done = false @@ -702,17 +744,19 @@ These specialized integer operations generally come in two variants, based on wh | Method Name | Returns | Implements | | --- | --- | --- | -| `wrappingIncrement(by:ordering:)` | `(oldValue:newValue:)` | `a &+= b` | -| `wrappingDecrement(by:ordering:)` | `(oldValue:newValue)` | `a &-= b` | -| `bitwiseAnd(with:ordering:)` | `(oldValue:newValue)` | `a &= b` | -| `bitwiseOr(with:ordering:)` | `(oldValue:newValue)` | `a |= b` | -| `bitwiseXor(with:ordering:)` | `(oldValue:newValue)` | `a ^= b` | -| `min(with:ordering:)` | `(oldValue:newValue)` | `a = Swift.min(a, b)` | -| `max(with:ordering:` | `(oldValue:newValue)` | `a = Swift.max(a, b)` | +| `wrappingAdd(_:ordering:)` | `(oldValue:newValue:)` | `a &+= b` | +| `wrappingSubtract(_:ordering:)` | `(oldValue:newValue)` | `a &-= b` | +| `add(_:ordering:)` | `(oldValue:newValue)` | `a += b` (checks for overflow) | +| `subtract(_:ordering:)` | `(oldValue:newValue)` | `a -= b` (checks for overflow) | +| `bitwiseAnd(with:ordering:)` | `(oldValue:newValue)` | `a &= b` | +| `bitwiseOr(with:ordering:)` | `(oldValue:newValue)` | `a \|= b` | +| `bitwiseXor(with:ordering:)` | `(oldValue:newValue)` | `a ^= b` | +| `min(with:ordering:)` | `(oldValue:newValue)` | `a = Swift.min(a, b)` | +| `max(with:ordering:)` | `(oldValue:newValue)` | `a = Swift.max(a, b)` | -All operations are also marked as `@discardableResult` in the case where one doesn't care about the old value or new value. The compiler can't optimize the atomic operation away if the return value isn't used. +All operations are also marked as `@discardableResult` in the case where one doesn't care about the old value or new value. The compiler can't optimize the atomic operation away if the return value isn't used. The `add` and `subtract` operations explicitly check for overflow and will trap at runtime if one occurs. This is unchecked in `-Ounchecked` builds. -While we require all atomic operations to be free of locks, we don't require wait-freedom. Therefore, on architectures that don't provide direct hardware support for some or all of these operations, we still require them to be implemented using `compareExchange` loops like the one for `wrappingIncrement` above. +While we require all atomic operations to be free of locks, we don't require wait-freedom. Therefore, on architectures that don't provide direct hardware support for some or all of these operations, we still require them to be implemented using `compareExchange` loops like the one for `wrappingAdd` above. `Atomic` exposes these operations when `Value` is one of the standard fixed-width integer types. @@ -722,7 +766,7 @@ extension Atomic where Value == UInt8 {...} ... let counter = Atomic(0) -counter.wrappingIncrement(by: 42, ordering: .relaxed) +counter.wrappingAdd(42, ordering: .relaxed) let oldMax = counter.max(with: 82, ordering: .relaxed).oldValue ``` @@ -734,7 +778,7 @@ Similar to the specialized integer operations, we can provide similar ones for b | Method Name | Returns | Implements | | ---------------------------- | --------------------- | ------------ | | `logicalAnd(with:ordering:)` | `(oldValue:newValue)` | `a = a && b` | -| `logicalOr(with:ordering:)` | `(oldValue:newValue)` | `a = a || b` | +| `logicalOr(with:ordering:)` | `(oldValue:newValue)` | `a = a \|\| b` | | `logicalXor(with:ordering:)` | `(oldValue:newValue)` | `a = a != b` | Like the integer operations, all of these boolean operations are marked as `@discardableResult`. @@ -776,7 +820,7 @@ Such problems make `Atomic>` exceedingly difficult to use in all bu For now, we provide the standalone type `AtomicLazyReference`; this is an example of a useful construct that could be built on top of `Atomic>` operations. (Of the atomic constructs introduced in this proposal, only `AtomicLazyReference` represents a regular strong reference to a class instance -- the other pointer/reference types leave memory management entirely up to the user.) -An `AtomicLazyReference` holds an optional reference that is initially set to `nil`. The value can be set exactly once, but it can be read an arbitrary number of times. Attempts to change the value after the first `storeIfNilThenLoad` call are ignored, and return the current value instead. +An `AtomicLazyReference` holds an optional reference that is initially set to `nil`. The value can be set exactly once, but it can be read an arbitrary number of times. Attempts to change the value after the first `storeIfNil` call are ignored, and return the current value instead. ```swift /// A lazily initializable atomic strong reference. @@ -794,7 +838,7 @@ public struct AtomicLazyReference: ~Copyable { extension AtomicLazyReference { /// Atomically initializes this reference if its current value is nil, then /// returns the initialized value. If this reference is already initialized, - /// then `storeIfNilThenLoad(_:)` discards its supplied argument and returns + /// then `storeIfNil(_:)` discards its supplied argument and returns /// the current value without updating it. /// /// The following example demonstrates how this can be used to implement a @@ -811,12 +855,12 @@ extension AtomicLazyReference { /// // multiple threads, but only one of them will get to /// // succeed setting the reference. /// let histogram = ... - /// return _histogram.storeIfNilThenLoad(histogram) + /// return _histogram.storeIfNil(histogram) /// } /// ``` /// /// This operation uses acquiring-and-releasing memory ordering. - public borrowing func storeIfNilThenLoad( + public borrowing func storeIfNil( _ desired: consuming Instance ) -> Instance @@ -841,13 +885,13 @@ var atomicLazyFoo: Foo { // Note: the code here may run concurrently on multiple threads. // All but one of the resulting values will be discarded. let foo = Foo() - return _foo.storeIfNilThenLoad(foo) + return _foo.storeIfNil(foo) } ``` The Standard Library has been internally using such a pattern to implement deferred bridging for `Array`, `Dictionary` and `Set`. -Note that unlike the rest of the atomic types, `load` and `storeIfNilThenLoad(_:)` do not expose `ordering` parameters. (Internally, they map to acquiring/releasing operations to guarantee correct synchronization.) +Note that unlike the rest of the atomic types, `load` and `storeIfNil(_:)` do not expose `ordering` parameters. (Internally, they map to acquiring/releasing operations to guarantee correct synchronization.) ### Restricting Ordering Arguments to Compile-Time Constants @@ -908,7 +952,7 @@ Consider the following well-meaning attempt at using `compareExchange` to define ```swift extension Atomic where Value == Int { // Non-inlinable - public func checkedIncrement(by delta: Int, ordering: AtomicUpdateOrdering) { + public func add(_ operand: Int, ordering: AtomicUpdateOrdering) { var done = false var current = load(ordering: .relaxed) @@ -923,12 +967,12 @@ extension Atomic where Value == Int { } // Elsewhere: -counter.checkedIncrement(by: 1, ordering: .relaxed) +counter.add(1, ordering: .relaxed) ``` -If for whatever reason the Swift compiler isn't able (or willing) to inline the `checkedIncrement` call, then the value of `ordering` won't be known at compile time to the body of the function, so even though `compareExchange` will still get inlined, its switch statement won't be eliminated. This leads to a potentially significant performance regression that could interfere with the scalability of the operation. +If for whatever reason the Swift compiler isn't able (or willing) to inline the `add` call, then the value of `ordering` won't be known at compile time to the body of the function, so even though `compareExchange` will still get inlined, its switch statement won't be eliminated. This leads to a potentially significant performance regression that could interfere with the scalability of the operation. -The big issue here is that if `checkedIncrement` is in another module, then callers of this function have no visibility inside this function's body. If callers can't see this function's implementation, then the switch statement will be executed at runtime regardless of the compiler optimization mode. However, another issue is that the ordering argument may still be dynamic in which case the compiler still can't eliminate the switch statement even though the caller may be able to see the entire implementation. +The big issue here is that if `add` is in another module, then callers of this function have no visibility inside this function's body. If callers can't see this function's implementation, then the switch statement will be executed at runtime regardless of the compiler optimization mode. However, another issue is that the ordering argument may still be dynamic in which case the compiler still can't eliminate the switch statement even though the caller may be able to see the entire implementation. To help prevent the last issue, we are constraining the memory ordering arguments of all atomic operations to be compile-time constants. Any attempt to pass a dynamic ordering value (such as in the `compareExchange` call above) will result in a compile-time error. @@ -1100,15 +1144,29 @@ extension UInt32: AtomicValue {...} extension UInt16: AtomicValue {...} extension UInt8: AtomicValue {...} -extension DoubleWord: AtomicValue {...} - extension Bool: AtomicValue {...} +extension Float16: AtomicValue {...} +extension Float: AtomicValue {...} +extension Double: AtomicValue {...} + +extension WordPair: AtomicValue {...} +extension Duration: AtomicValue {...} + +extension Never: AtomicValue {...} + extension UnsafeRawPointer: AtomicValue {...} extension UnsafeMutableRawPointer: AtomicValue {...} extension UnsafePointer: AtomicValue {...} extension UnsafeMutablePointer: AtomicValue {...} extension Unmanaged: AtomicValue {...} +extension OpaquePointer: AtomicValue {...} +extension ObjectIdentifier: AtomicValue {...} + +extension UnsafeBufferPointer: AtomicValue {...} +extension UnsafeMutableBufferPointer: AtomicValue {...} +extension UnsafeRawBufferPointer: AtomicValue {...} +extension UnsafeMutableRawBufferPointer: AtomicValue {...} extension Optional: AtomicValue where Wrapped: AtomicOptionalWrappable {...} ``` @@ -1116,12 +1174,12 @@ extension Optional: AtomicValue where Wrapped: AtomicOptionalWrappable {...} To support custom "atomic-representable" types, `AtomicValue` also comes with default implementations for all its requirements for `RawRepresentable` types whose `RawValue` is also atomic: ```swift -extension RawRepresentable where Self: AtomicValue, RawValue: AtomicValue, ... { +extension RawRepresentable where Self: AtomicValue, RawValue: AtomicValue { // Implementations for all requirements. } ``` -The omitted constraint sets up the (private) atomic storage type to match that of the `RawValue`. The default implementations work by converting values to their `rawValue` form, and forwarding all atomic operations to it. +The default implementations work by converting values to their `rawValue` form, and forwarding all atomic operations to it. #### `AtomicOptionalWrappable` @@ -1147,10 +1205,10 @@ extension UnsafeMutableRawPointer: AtomicOptionalWrappable {} extension UnsafePointer: AtomicOptionalWrappable {} extension UnsafeMutablePointer: AtomicOptionalWrappable {} extension Unmanaged: AtomicOptionalWrappable {} +extension OpaquePointer: AtomicOptionalWrappable {} +extension ObjectIdentifier: AtomicOptionalWrappable {} ``` -User code is not allowed to extend this list with additional types; this capability is reserved for potential future proposals. - ### Atomic Storage Types ```swift @@ -1161,17 +1219,17 @@ public struct AtomicInt64Storage {...} public struct AtomicInt128Storage {...} ``` -### `DoubleWord` +### `WordPair` ```swift -public struct DoubleWord { +public struct WordPair { public var first: UInt { get } public var second: UInt { get } public init(first: UInt, second: UInt) } -extension DoubleWord: AtomicValue {...} +extension WordPair: AtomicValue {...} ``` ### Atomic Types @@ -1233,17 +1291,29 @@ extension Atomic where Value.AtomicRepresentation == AtomicIntNNStorage { ```swift extension Atomic where Value == Int { @discardableResult - public borrowing func wrappingIncrement( - by operand: Value = 1, + public borrowing func wrappingAdd( + _ operand: Value, ordering: AtomicUpdateOrdering ) -> (oldValue: Value, newValue: Value) @discardableResult - public borrowing func wrappingDecrement( - by operand: Value = 1, + public borrowing func wrappingSubtract( + _ operand: Value, ordering: AtomicUpdateOrdering ) -> (oldValue: Value, newValue: Value) + @discardableResult + public borrowing func add( + _ operand: Value, + ordering: AtomicUpdateOrdering + ) -> (oldValue: Value, newValue: Value) + + @discardableResult + public borrowing func subtract( + _ operand: Value, + ordering: AtomicUpdateOrdering + ) -> (oldValue: Value, newValue: Value) + @discardableResult public borrowing func bitwiseAnd( with operand: Value, @@ -1313,7 +1383,7 @@ public struct AtomicLazyReference: ~Copyable { // Atomic operations: - public borrowing func storeIfNilThenLoad( + public borrowing func storeIfNil( _ desired: consuming Instance ) -> Instance @@ -1428,8 +1498,8 @@ extension Atomic { } extension Atomic where Value == Int { - func wrappingIncrement(by delta: Value = 1) { - wrappingIncrement(by: delta, ordering: .sequentiallyConsistent) + func wrappingAdd(_ operand: Value) { + wrappingAdd(operand, ordering: .sequentiallyConsistent) } etc. @@ -1513,13 +1583,13 @@ struct Atomic { } let counter = Atomic(0) -counter.wrappingIncrement() +counter.wrappingAdd(1) ``` This simplifies the typical case where all operations on a certain atomic value use the same "level" of ordering (relaxed, acquire/release, or sequentially consistent). However, there are considerable drawbacks: * This design puts the ordering specification far away from the actual operations -- obfuscating their meaning. -* It makes it a lot more difficult to use custom orderings for specific operations (like the speculative relaxed load in the `wrappingIncrement` example in the section on [Atomic Operations](#atomic-operations) above). +* It makes it a lot more difficult to use custom orderings for specific operations (like the speculative relaxed load in the `wrappingAdd` example in the section on [Atomic Operations](#atomic-operations) above). * We wouldn't be able to provide a default value for a generic type parameter. * Finally, there is also the risk of unspecialized generics interfering with runtime performance. @@ -1530,7 +1600,7 @@ The most promising alternative idea to represent memory orderings was to model t ```swift let counter = Atomic(0) -counter.relaxed.increment() +counter.relaxed.wrappingAdd(1) let current = counter.acquiring.load() ``` @@ -1600,6 +1670,13 @@ The `swift-atomics` package has many years of experience using their APIs to int While there are some API differences between this proposal and the package, most of the atomic operations are the same and should feel very familiar to those who have used the package before. We don't plan on drastically renaming any core atomic operation because we believe `swift-atomics` already got those names correct. +### A different name for `WordPair` + +Previous revisions of this proposal named this type `DoubleWord`. This is a good name and is in fact the name used in the `swift-atomics` package. We felt the prefix `Double*` could cause confusion with the pre-existing type in the standard library `Double`. The name `WordPair` has a couple of advantages: + +1. Code completion. Because this name starts with an less common letter in the English alphabet, the likelyhood of seeing this type at the top level in code completion is very unlikely and not generally a type used for newer programmers of Swift. +2. Directly conveys the semantic meaning of the type. This type is not semantically equivalent to something like `{U}Int128` (on 64 bit platforms). While although its layout shares the same size, the meaning we want to drive home with this type is quite simply that it's a pair of `UInt` words. If and when the standard library proposes a `{U}Int128` type, that will add a conformance to `AtomicValue` on 64 bit platforms who support double words as well. That itself wouldn't deprecate uses of `WordPair` however, because it's much easier to grab both words independently with `WordPair` as well as being a portable name for such semantics on both 32 bit and 64 bit platforms. + ## References [Clarify the Swift memory consistency model (SE-0282)]: https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md From ea50b195e0fb8e034b4a6c40db7c9570fc460544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20G?= <101190846+spqw@users.noreply.github.com> Date: Wed, 18 Oct 2023 00:42:27 +0200 Subject: [PATCH 3340/4563] Fix broken links to Registry.md (#2182) --- proposals/0321-package-registry-publish.md | 2 +- proposals/0391-package-registry-publish.md | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/proposals/0321-package-registry-publish.md b/proposals/0321-package-registry-publish.md index fdf676d47a..68d324463a 100644 --- a/proposals/0321-package-registry-publish.md +++ b/proposals/0321-package-registry-publish.md @@ -313,7 +313,7 @@ like [Trillian] or [sigstore]. [autobuilds]: https://docs.docker.com/docker-hub/builds/ "Docker Hub: Set up Automated Builds" [Damerau–Levenshtein distance]: https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance "Damerau–Levenshtein distance" [Docker Hub]: https://hub.docker.com -[Registry.md]: https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md "Swift Package Registry Service Specification" +[Registry.md]: https://github.com/apple/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md "Swift Package Registry Service Specification" [rss]: https://validator.w3.org/feed/docs/rss2.html "RSS 2.0 specification" [RubyGems.org]: https://rubygems.org/ [SE-0292]: https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md "Package Registry Service" diff --git a/proposals/0391-package-registry-publish.md b/proposals/0391-package-registry-publish.md index c510471d81..32212157e2 100644 --- a/proposals/0391-package-registry-publish.md +++ b/proposals/0391-package-registry-publish.md @@ -24,7 +24,7 @@ A package registry makes packages available to consumers. Starting with Swift 5.7, SwiftPM supports dependency resolution and package download using any registry that -implements the [service specification](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md) proposed alongside with [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md). +implements the [service specification](https://github.com/apple/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md) proposed alongside with [SE-0292](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md). SwiftPM does not yet provide any tooling for publishing packages, so package authors must manually prepare the contents (e.g., source archive) and interact with the registry on their own to publish a package release. This proposal @@ -38,7 +38,7 @@ Publishing package release to a Swift package registry generally involves these 1. Prepare package source archive by using the [`swift package archive-source` subcommand](https://github.com/apple/swift-evolution/blob/main/proposals/0292-package-registry-service.md#archive-source-subcommand). 1. Sign the metadata and archive (if needed). 1. [Authenticate](https://github.com/apple/swift-evolution/blob/main/proposals/0378-package-registry-auth.md) (if required by the registry). - 1. Send the archive and metadata (and their signatures if any) by calling the ["create a package release" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-6). + 1. Send the archive and metadata (and their signatures if any) by calling the ["create a package release" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-6). 1. Check registry server response to determine if publication has succeeded or failed (if the registry processes request synchronously), or is pending (if the registry processes request asynchronously). SwiftPM can streamline the workflow by combining all of these steps into a single @@ -58,8 +58,8 @@ Typically a package release has metadata associated with it, such as URL of the code repository, license, etc. In general, metadata gets set when a package release is being published, but a registry service may allow modifications of the metadata afterwards. -The current [registry service specification](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md) states that: - - A client (e.g., package author, publishing tool) may provide metadata for a package release by including it in the ["create a package release" request](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#462-package-release-metadata). The registry server will store the metadata and include it in the ["fetch information about a package release" response](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-2). +The current [registry service specification](https://github.com/apple/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md) states that: + - A client (e.g., package author, publishing tool) may provide metadata for a package release by including it in the ["create a package release" request](https://github.com/apple/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#462-package-release-metadata). The registry server will store the metadata and include it in the ["fetch information about a package release" response](https://github.com/apple/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-2). - If a client does not include metadata, the registry server may populate it unless the client specifies otherwise (i.e., by sending an empty JSON object `{}` in the "create a package release" request). It does not, however, define any requirements or server-client API contract on the @@ -84,7 +84,7 @@ Package release metadata submitted to a registry must be a JSON object of type ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md", + "$id": "https://github.com/apple/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md", "title": "Package Release Metadata", "description": "Metadata of a package release.", "type": "object", @@ -167,7 +167,7 @@ Package release metadata submitted to a registry must be a JSON object of type | `description` | String | A description of the package release. | | | `licenseURL` | String | URL of the package release's license document. | | | `readmeURL` | String | URL of the README specifically for the package release or broadly for the package. | | -| `repositoryURLs` | Array | Code repository URL(s) of the package. It is recommended to include all URL variations (e.g., SSH, HTTPS) for the same repository. This can be an empty array if the package does not have source control representation.
    Setting this property is one way through which a registry can obtain repository URL to package identifier mappings for the ["lookup package identifiers registered for a URL" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#45-lookup-package-identifiers-registered-for-a-url). A registry may choose other mechanism(s) for package authors to specify such mappings. | | +| `repositoryURLs` | Array | Code repository URL(s) of the package. It is recommended to include all URL variations (e.g., SSH, HTTPS) for the same repository. This can be an empty array if the package does not have source control representation.
    Setting this property is one way through which a registry can obtain repository URL to package identifier mappings for the ["lookup package identifiers registered for a URL" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#45-lookup-package-identifiers-registered-for-a-url). A registry may choose other mechanism(s) for package authors to specify such mappings. | | ##### `Author` type @@ -370,7 +370,7 @@ or `signing.trustedRootCertificatesPath` for package `mona.LinkedList`: ##### Local TOFU When SwiftPM downloads a package release from registry via the -["download source archive" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#endpoint-4), it will: +["download source archive" API](https://github.com/apple/swift-package-manager/blob/main/Documentation/PackageRegistry/Registry.md#endpoint-4), it will: 1. Search local fingerprints storage, which by default is located at `~/.swiftpm/security/fingerprints/`, to see if the package release has been downloaded before and its recorded checksum. The checksum of the downloaded source archive must match the previous value or else [trust on first use (TOFU)](https://en.wikipedia.org/wiki/Trust_on_first_use) check would fail. 1. Fetch package release metadata from the registry to get:
    Subclassable in... \ Accessible in...anywherepackagemoduleAccessible AnywhereAccessible in PackageAccessible in Module
    anywhereSubclassable Anywhere open (illegal) (illegal)
    packageSubclassable in Package ?(a) ?(b) (illegal)
    moduleSubclassable in Module public package internal
    nowhereSubclassable Nowhere public final package final internal final
    + + + + + + + + + + + + + + + + + + + + +
    Change|opennon-open
    | + @objc + + non-@objc +
    remove isolation|breakingokok
    add isolation|breakingbreakingok
    change actor|breakingbreakingok
    + +Adding an isolation annotation to a `dealloc` method of an imported Objective-C class is a breaking change. Existing Swift subclasses may have `deinit` isolated on different actor, and without recompilation will be calling `[super deinit]` on that actor. When recompiled, subclasses isolating on a different actor will produce a compilation error. Subclasses that had a non-isolated `deinit` (or a `deinit` isolated on the same actor) remain ABI compatible. It is possible to add isolation annotation to UIKit classes now, because currently all their subclasses have nonisolated `deinit`s. Removing an isolation annotation from the `dealloc` method (together with `retain`/`release` overrides) is a breaking change. Any existing subclasses would be type-checked as isolated but compiled without isolation thunks. After changes in the base class, subclass `deinit`s could be called on the wrong executor. +If isolated deinit need to be suppressed in `.swiftinterface` for compatibility with older compilers, then `open` classes are emitted as `public` to prevent subclassing. + ## Future Directions -### Asynchronous `deinit` +### Implicit asynchronous `deinit` -Currently, if users need to initiate an asynchronous operation from `deinit`, they need to manually start a task. It is easy to accidentally capture `self` inside task's closure. Such `self` would become a dangling reference which may cause a crash, but is not guaranteed to reproduce during debugging. +Currently, if users need to initiate an asynchronous operation from `deinit`, they need to manually start a task. This requires copying all the needed data from the object, which can be tedious and error-prone. If some data is not copied explicitly, `self` will be captured implicitly, leading to a fatal error in runtime. ```swift actor Service { @@ -295,7 +408,7 @@ class ViewModel { } ``` -A more developer-friendly approach would be to allow asynchronous deinit: +If almost every instance property is copied, then it would be more efficient to reuse original object as a task closure context and make `deinit` asynchronous: ```swift ... @@ -308,11 +421,26 @@ A more developer-friendly approach would be to allow asynchronous deinit: } ``` -Similarly to this proposal, `__deallocating_deinit` can be used as a thunk that starts an unstructured task for executing async deinit. But such naïve approach can flood the task scheduler when deallocating large data structure with many async `deinit`s. More research is needed to understand typical usage of asynchronous operations in `deinit`, and applicable optimization methods. This is out of scope of this proposal. +Similarly to this proposal, `__deallocating_deinit` can be used as a thunk that starts an unstructured task for executing async deinit. But this is out of scope of this proposal. -### Asserting that `self` does not escape from `deinit`. +### Linear types -It is not legal for `self` to escape from `deinit`. Any strong references remaining after `swift_deallocObject()` is executed cannot be even released safely. Dereferencing dangling references can manifest itself as a crash, reading garbage data or corruption of unrelated memory. It can be hard to connect symptoms with the origin of the problem. A better solution would be to deterministically produce `fatalError()` in `swift_deallocObject()` if there are additional strong references. The ideal solution would be to detect escaping `self` using compile-time analysis. +Invoking sequential async cleanup is a suspension point, and needs to be marked with `await`. Explicit method calls fit this role better than implicitly invoked `deinit`. But using such methods can be error-prone without compiler checks that cleanup method is called exactly once on all code paths. Move-only types help to ensure that cleanup method is called **at most once**. Linear types help to ensure that cleanup method is called **exactly once**. + +```swift +@linear // like @moveonly, but consumption is mandatory +struct Connection { + // acts as a named explicit async deinit + consuming func close() async { + ... + } +} + +func communicate() async { + let c = Connection(...) + // error: value of linear type is not consumed +} +``` ### Improving de-virtualization and inlining of the executor access. @@ -334,8 +462,7 @@ public class Foo { } ``` -Currently both the `foo()` and `deinit` entry points produce two calls to access the `MainActor.shared.unownedExecutor`, with the second one even using dynamic dispatch. -These two calls could be replaced with a single call to the statically referenced `swift_task_getMainExecutor()`. +Currently both the `foo()` and `deinit` entry points produce two calls to access the `MainActor.shared.unownedExecutor`, with the second one even using dynamic dispatch. These two calls could be replaced with a single call to the statically referenced `swift_task_getMainExecutor()`. ```llvm %1 = tail call swiftcc %swift.metadata_response @"type metadata accessor for Swift.MainActor"(i64 0) #6 @@ -346,17 +473,13 @@ These two calls could be replaced with a single call to the statically reference %6 = tail call swiftcc { i64, i64 } @"dispatch thunk of Swift.Actor.unownedExecutor.getter : Swift.UnownedSerialExecutor"(%objc_object* swiftself %5, %swift.type* %2, i8** %4) ``` -### Making fast path inlinable - -For this to be useful the compiler first needs to be able to reason about the value of the current executor based on the isolation of the surrounding function. Then, an inlinable pre-check for `swift_task_isCurrentExecutor()` can be inserted allowing the isolated deallocating `deinit` to be inlined into the non-isolated one. - ### Improving extended stack trace support -Developers who put breakpoints in the isolated deinit might want to see the call stack that lead to the last release of the object. Currently, if switching of executors was involved, the release call stack won't be shown in the debugger. +Developers who put breakpoints in the isolated deinit might want to see the call stack that led to the last release of the object. Currently, if switching of executors was involved, the release call stack won't be shown in the debugger. ### Implementing API for synchronously scheduling arbitrary work on the actor -The introduced runtime function has calling convention optimized for the `deinit` use case, but using a similar runtime function with a slightly different signature, one could implement an API for synchronously scheduling arbitrary work on the actor: +Added runtime function has calling convention optimized for the `deinit` use case, but using a similar runtime function with a slightly different signature, one could implement an API for synchronously scheduling arbitrary work on the actor: ```swift extension Actor { @@ -379,71 +502,19 @@ a.enqueue { aIsolated in } ``` -### Isolated deinit for move-only types without isolation thunk - -> [Joe Groff](https://forums.swift.org/t/isolated-synchronous-deinit/58177/17): -> -> Classes with shared ownership are ultimately the wrong tool for the job—they may be the least bad tool today, though. Ultimately, when we have move-only types, then since those have unique ownership, we'd be able to reason more strongly about what context their deinit executes in, since it would either happen at the end of the value's original lifetime, or if it's moved to a different owner, when that new owner consumes it. Within move-only types, we could also have a unique modifier for class types, to indicate statically that an object reference is the only one to the object, and that its release definitely executes deinit. - -For move-only types as a tool for explicit resource management, it may be desired to have switching or not switching actor to execute isolating `deinit` to be explicit as well. Such types could have isolated `deinit` without isolating thunk, and instead compiler would check that value is dropped on the correct actor. - -```swift -@moveonly -struct Resource { - @MainActor - deinit { - ... - } -} - -@MainActor func foo() { - let r: Resource = ... - Task.detached { [move r] in - // error: expression is 'async' but is not marked with 'await' - // note: dropping move-only value with isolated deinit from outside of its actor context is implicitly asynchronous - drop(r) - } -} -``` - ## Alternatives considered ### Placing hopping logic in `swift_release()` instead. `UIView` and `UIViewController` implement hopping to the main thread by overriding the `release` method. But in Swift there are no vtable/wvtable slots for releasing, and adding them would also affect a lot of code that does not need isolated deinit. -### Deterministic task local values - -When switching executors, the current implementation copies priority and task-local values from the task/thread where the last release happened. This minimizes differences between isolated and nonisolated `deinit`s. - -If there are references from different tasks/threads, the values of the priority and task-local values observed by the `deinit` are racy, both for isolated and nonsiolated `deinit`s. - -One way of making task-local values predictable would be to clear them for the duration of the `deinit` execution. -This can be implemented efficiently, but would be too restrictive. -If the object is referenced in several tasks which all have a common parent, then `deinit` can reliably use task-local values which are known to be set in the parent task and not overridden in the child tasks. - -If there is a demand for resetting task-local values, it can be implemented separately as an API: - -```swift -// Temporary resets all task-local values to their defaults -// by appending a stop-node to the linked-list of task-locals -func withoutTaskLocalValues(operation: () throws -> Void) rethrows -``` - -### Don't isolate empty explicit `deinit` - -Empty explicit `deinit`s are also eligible to be nonisolated as an optimization. But that would mean that the interface of the declaration depends on its implementation. Currently, Swift infers function signature only for closure literals, but never for named functions. - -### Explicit opt-in into `deinit` isolation - -This would eliminate any possibility of unexpected changes in behavior of the existing code, but would penalize future users, creating inconsistency between isolation inference rules for `deinit` and regular methods. Currently there is syntax for opt-out from isolation for actor instance members, but there is no syntax for opt-in. Having opt-in for `deinit`s would require introducing such syntax, and it would be used only for actor `deinit`s. There is already the `isolated` keyword, but it is applied to function arguments, not to function declarations. - -Classes whose `deinit`s have nonisolated synchronous externally-visible side effects, like `AnyCancellable`, are unlikely to be isolated on global actors or be implemented as an actor. +### Copy task-local values when hopping by default -This proposal preserves behavior of `deinit`s that have synchronous externally-visible side effects only under assumption that they are always released on the isolating actor. +This comes with a performance cost, which is unlikely to be beneficial to most of the users. +Leaving behavior of the task-locals undefined allows to potentially change it in the future, after getting more feedback from the users. -`deinit`s that explicitly notify about completion of their side effect continue to satisfy their contract even if proposal changes their behavior. +### Implicitly propagate isolation to synchronous `deinit`. -### Use asynchronous deinit as the only tool for `deinit` isolation +This would be a source-breaking change. -Synchronous `deinit` has an efficient fast path that jumps right into the `deinit` implementation without context switching, or any memory allocations. But asynchronous `deinit` would require creation of task in all cases. +Majority of the `deinit`'s are implicitly synthesized by the compiler and only release stored properties. Global open source search in Sourcegraph, gives is 77.5k deinit declarations for 2.2m classes - 3.5%. Release can happen from any executor/thread and does not need isolation. Isolating implicit `deinit`s would come with a major performance cost. Providing special rules for propagating isolation to synchronous `deinit` unless it is implicit, would complicate propagation rules. From 568e3469987f9afb30b9962d4fa8f5b39b9c0d72 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Tue, 23 Jul 2024 22:14:12 -0400 Subject: [PATCH 3798/4563] Second review for SE-0371 (#2528) --- proposals/0371-isolated-synchronous-deinit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0371-isolated-synchronous-deinit.md b/proposals/0371-isolated-synchronous-deinit.md index 19f60a86bd..ed38e090d9 100644 --- a/proposals/0371-isolated-synchronous-deinit.md +++ b/proposals/0371-isolated-synchronous-deinit.md @@ -5,7 +5,7 @@ * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) * Status: **Returned for revision** * Implementation: [apple/swift#60057](https://github.com/apple/swift/pull/60057) -* Review: ([pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) ([review](https://forums.swift.org/t/se-0371-isolated-synchronous-deinit/59754)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0371-isolated-synchronous-deinit/60060)) +* Review: ([pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) ([review](https://forums.swift.org/t/se-0371-isolated-synchronous-deinit/59754)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0371-isolated-synchronous-deinit/60060)) ([second review](https://forums.swift.org/t/second-review-se-0371-isolated-synchronous-deinit/73406)) ## Introduction From f2c654941e316315a6c918cc5c04a1c5e9097baf Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Wed, 24 Jul 2024 08:44:09 -0300 Subject: [PATCH 3799/4563] Update SE-0371 status to reflect active second review --- proposals/0371-isolated-synchronous-deinit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0371-isolated-synchronous-deinit.md b/proposals/0371-isolated-synchronous-deinit.md index ed38e090d9..2cf4453658 100644 --- a/proposals/0371-isolated-synchronous-deinit.md +++ b/proposals/0371-isolated-synchronous-deinit.md @@ -3,7 +3,7 @@ * Proposal: [SE-0371](0371-isolated-synchronous-deinit.md) * Author: [Mykola Pokhylets](https://github.com/nickolas-pohilets) * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) -* Status: **Returned for revision** +* Status: **Active review (July 23...August 6, 2024)** * Implementation: [apple/swift#60057](https://github.com/apple/swift/pull/60057) * Review: ([pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) ([review](https://forums.swift.org/t/se-0371-isolated-synchronous-deinit/59754)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0371-isolated-synchronous-deinit/60060)) ([second review](https://forums.swift.org/t/second-review-se-0371-isolated-synchronous-deinit/73406)) From bdf7fcf91812b7712b4c9c9c0169d31808287fca Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Wed, 24 Jul 2024 08:52:22 -0300 Subject: [PATCH 3800/4563] Add link to second review thread for SE-0440 --- proposals/0440-debug-description-macro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0440-debug-description-macro.md b/proposals/0440-debug-description-macro.md index 9b4feacd06..47cb953557 100644 --- a/proposals/0440-debug-description-macro.md +++ b/proposals/0440-debug-description-macro.md @@ -6,7 +6,7 @@ * Status: **Active Review (July 20-July 30, 2024)** * Implementation: Present in `main` under experimental feature `DebugDescriptionMacro` [apple/swift#69626](https://github.com/apple/swift/pull/69626) * Previous Revision: [1](https://github.com/swiftlang/swift-evolution/blob/fda6746506368c8c6d2933ee6d71c87e6ed92f94/proposals/0440-debug-description-macro.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-debug-description-macro/67711)) ([review](https://forums.swift.org/t/se-0440-debugdescription-macro/72958)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0440-debugdescription-macro/73270)) +* Review: ([pitch](https://forums.swift.org/t/pitch-debug-description-macro/67711)) ([review](https://forums.swift.org/t/se-0440-debugdescription-macro/72958)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0440-debugdescription-macro/73270)) ([second review](https://forums.swift.org/t/second-review-se-0440-debugdescription-macro/73325)) ## Introduction From 04ccd9fe4d30ade799b0a3b6297333171ce9de36 Mon Sep 17 00:00:00 2001 From: Matt Massicotte <85322+mattmassicotte@users.noreply.github.com> Date: Thu, 25 Jul 2024 14:52:35 -0400 Subject: [PATCH 3801/4563] Typo in 0423-dynamic-actor-isolation.md (#2532) Minor spelling mistake. --- proposals/0423-dynamic-actor-isolation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0423-dynamic-actor-isolation.md b/proposals/0423-dynamic-actor-isolation.md index ca43a33240..1d4b1b08b3 100644 --- a/proposals/0423-dynamic-actor-isolation.md +++ b/proposals/0423-dynamic-actor-isolation.md @@ -145,7 +145,7 @@ A `@preconcurrency` protocol conformance is scoped to the implementation of the ### Disabling dynamic actor isolation checking -The dynamic actor isolation checks can be disabled using the flag `-disable-dynamic-actor-isolation`. Disabling dynamic actor isolation is discouraged, but it may be necessary if code that you don't control violates actor isolation in a way that causes the program to crash, such as by passing a non-`Sendable` function argument outside of a main actor context. `-disable-dynamic-actor-isolation` is similar to the `-enforce-exclusivity=unchecked` flag, which was a tool provided when staging in dynamic memory exclusivity enforcement under the Swift 5 lanugage mode. +The dynamic actor isolation checks can be disabled using the flag `-disable-dynamic-actor-isolation`. Disabling dynamic actor isolation is discouraged, but it may be necessary if code that you don't control violates actor isolation in a way that causes the program to crash, such as by passing a non-`Sendable` function argument outside of a main actor context. `-disable-dynamic-actor-isolation` is similar to the `-enforce-exclusivity=unchecked` flag, which was a tool provided when staging in dynamic memory exclusivity enforcement under the Swift 5 language mode. ## Source compatibility From 14495e8d625ea21c67c8215b54d0a0946981a536 Mon Sep 17 00:00:00 2001 From: Lawrence Gimenez Date: Tue, 30 Jul 2024 03:35:42 +0800 Subject: [PATCH 3802/4563] Fixed typo most (#2519) From 686b10e10ff7a8fd4793cf5a9024baedf851759b Mon Sep 17 00:00:00 2001 From: Minseong Kim <121244634+kim-minseong@users.noreply.github.com> Date: Fri, 2 Aug 2024 04:17:24 +0900 Subject: [PATCH 3803/4563] [SE-0306] Update `detach` to `Task.detached` (#2533) --- proposals/0306-actors.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/proposals/0306-actors.md b/proposals/0306-actors.md index 381f50a226..8dda3655eb 100644 --- a/proposals/0306-actors.md +++ b/proposals/0306-actors.md @@ -262,7 +262,7 @@ The restrictions on cross-actor references only work so long as we can ensure th extension BankAccount { func endOfMonth(month: Int, year: Int) { // Schedule a task to prepare an end-of-month report. - detach { + Task.detached { let transactions = await self.transactions(month: month, year: year) let report = Report(accountNumber: self.accountNumber, transactions: transactions) await report.email(to: self.accountOwnerEmailAddress) @@ -271,7 +271,7 @@ extension BankAccount { } ``` -A task created with `detach` runs concurrently with all other code. If the closure passed to `detach` were to be actor-isolated, we would introduce a data race on access to the mutable state on `BankAccount`. Actors prevent this data race by specifying that a `@Sendable` closure (described in [`Sendable` and `@Sendable` closures][se302], and used in the definition of `detach` in the [Structured Concurrency][sc] proposal) is always non-isolated. Therefore, it is required to asynchronously access any actor-isolated declarations. +A task created with `Task.detached` runs concurrently with all other code. If the closure passed to `Task.detached` were to be actor-isolated, we would introduce a data race on access to the mutable state on `BankAccount`. Actors prevent this data race by specifying that a `@Sendable` closure (described in [`Sendable` and `@Sendable` closures][se302], and used in the definition of `Task.detached` in the [Structured Concurrency][sc] proposal) is always non-isolated. Therefore, it is required to asynchronously access any actor-isolated declarations. A closure that is not `@Sendable` cannot escape the concurrency domain in which it was formed. Therefore, such a closure will be actor-isolated if it is formed within an actor-isolated context. This is useful, for example, when applying sequence algorithms like `forEach` where the provided closure will be called serially: @@ -292,7 +292,7 @@ extension BankAccount { A closure formed within an actor-isolated context is actor-isolated if it is non-`@Sendable`, and non-isolated if it is `@Sendable`. For the examples above: -* The closure passed to `detach` is non-isolated because that function requires a `@Sendable` function to be passed to it. +* The closure passed to `Task.detached` is non-isolated because that function requires a `@Sendable` function to be passed to it. * The closure passed to `forEach` is actor-isolated to `self` because it takes a non-`@Sendable` function. ### Actor reentrancy @@ -333,8 +333,8 @@ In the example above the `DecisionMaker` can think of a good or bad idea, shares This is exemplified by the following piece of code, exercising the `decisionMaker` actor: ```swift -let goodThink = detach { await decisionMaker.thinkOfGoodIdea() } // runs async -let badThink = detach { await decisionMaker.thinkOfBadIdea() } // runs async +let goodThink = Task.detached { await decisionMaker.thinkOfGoodIdea() } // runs async +let badThink = Task.detached { await decisionMaker.thinkOfBadIdea() } // runs async let shouldBeGood = await goodThink.get() let shouldBeBad = await badThink.get() @@ -599,7 +599,7 @@ actor A { func useAF(array: [Int]) { array.map(self.f) // okay - detach(operation: self.g) // error: self.g has non-sendable type () -> Double that cannot be converted to a @Sendable function type + Task.detached(operation: self.g) // error: self.g has non-sendable type () -> Double that cannot be converted to a @Sendable function type runLater(self.g) // error: cannot convert value of non-sendable function type () -> Double to sendable function type } } @@ -612,7 +612,7 @@ These restrictions follow from the actor isolation rules for the "desugaring" of extension A { func useAFDesugared(a: A, array: [Int]) { array.map { f($0) } ) // okay - detach { g() } // error: self is non-isolated, so call to `g` cannot be synchronous + Task.detached { g() } // error: self is non-isolated, so call to `g` cannot be synchronous runLater { g() } // error: self is non-isolated, so the call to `g` cannot be synchronous } } @@ -879,7 +879,7 @@ The original accepted version of this proposal required *all* access to immutabl let total = 100 var counter = 0 - asyncDetached { + Task.detached { print(total) // okay to reference immutable state print(counter) // error, cannot reference a `var` from a @Sendable closure } @@ -900,7 +900,7 @@ By allowing synchronous access to actor `let`s within a module, we provide a smo * Escaping closures can now be actor-isolated; only `@Sendable` prevents isolation. * Removed actor inheritance. It can be considered at some future point. * Added "cross-actor lets" to Alternatives Considered. While there is no change to the proposed direction, the issue is explained here for further discussion. - * Replaced `Task.runDetached` with `detach` to match updates to the [Structured Concurrency proposal][sc]. + * Replaced `detach` with `Task.detached` to match updates to the [Structured Concurrency proposal][sc]. * Changes in the seventh pitch: * Removed isolated parameters and `nonisolated` from this proposal. They'll come in a follow-up proposal on [controlling actor isolation][isolationcontrol]. * Changes in the sixth pitch: From 802d66245ae42afb32f3259259a415e7a228fda2 Mon Sep 17 00:00:00 2001 From: Minseong Kim <121244634+kim-minseong@users.noreply.github.com> Date: Sat, 3 Aug 2024 03:34:09 +0900 Subject: [PATCH 3804/4563] [SE-0317] Update `TaskGroup.async` to `TaskGroup.addTask` (#2534) --- proposals/0317-async-let.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/proposals/0317-async-let.md b/proposals/0317-async-let.md index 3b313a221f..d85965d91c 100644 --- a/proposals/0317-async-let.md +++ b/proposals/0317-async-let.md @@ -42,13 +42,13 @@ func makeDinner() async -> Meal { // Create a task group to scope the lifetime of our three child tasks return try await withThrowingTaskGroup(of: CookingTask.self) { group in // spawn three cooking tasks and execute them in parallel: - group.async { + group.addTask { CookingTask.veggies(try await chopVegetables()) } - group.async { + group.addTask { CookingTask.meat(await marinateMeat()) } - group.async { + group.addTask { CookingTask.oven(await preheatOven(temperature: 350)) } @@ -180,9 +180,9 @@ The child-task created to initialize the `async let` by default runs on the glob > Customizing the execution context of async lets is a future direction we are likely to explore with the introduction of Custom Executors. -The initializer of the `async let` can be thought of as a closure that runs the code contained within it in a separate task, very much like the explicit `group.async { }` API of task groups. +The initializer of the `async let` can be thought of as a closure that runs the code contained within it in a separate task, very much like the explicit `group.addTask { }` API of task groups. -Similarly to the `group.async()` function, the closure is `@Sendable` and `nonisolated`, meaning that it cannot access non-sendable state of the enclosing context. For example, it will result in a compile-time error, preventing a potential race condition, for a `async let` initializer to attempt mutating a closed-over variable: +Similarly to the `group.addTask()` function, the closure is `@Sendable` and `nonisolated`, meaning that it cannot access non-sendable state of the enclosing context. For example, it will result in a compile-time error, preventing a potential race condition, for a `async let` initializer to attempt mutating a closed-over variable: ```swift var localText: [String] = ... @@ -372,7 +372,7 @@ This is the same as spawning a number of child-tasks in a task group, and not co ```swift try await withThrowingTaskGroup(of: Int.self) { group in - group.async { throw Boom() } + group.addTask { throw Boom() } return 0 // we didn't care about the child-task at all(!) } // returns 0 @@ -414,7 +414,7 @@ Cancellation propagates recursively through the task hierarchy from parent to ch Because tasks spawned by `async let` are child tasks, they naturally participate in their parent's cancellation. -Cancellation of the parent task means that the context in which the `async let` declarations exist is cancelled, and any tasks created by those declarations will be cancelled as well. Because cancellation in Swift is co-operative, it does not prevent the spawning of tasks, however tasks spawned from a cancelled context are *immediately* marked as cancelled. This exhibits the same semantics as `TaskGroup.async` which, when used from an already cancelled task, _will_ spawn more child-tasks, however they will be immediately created as cancelled tasks — which they can inspect by calling `Task.isCancelled`. +Cancellation of the parent task means that the context in which the `async let` declarations exist is cancelled, and any tasks created by those declarations will be cancelled as well. Because cancellation in Swift is co-operative, it does not prevent the spawning of tasks, however tasks spawned from a cancelled context are *immediately* marked as cancelled. This exhibits the same semantics as `TaskGroup.addTask` which, when used from an already cancelled task, _will_ spawn more child-tasks, however they will be immediately created as cancelled tasks — which they can inspect by calling `Task.isCancelled`. We can observe this in the following example: @@ -459,7 +459,7 @@ func toyParallelMap(_ items: [A], f: (A) async -> B) async -> [B] { // spawn off processing all `f` mapping functions in parallel // in reality, one might want to limit the "width" of these for i in items.indices { - group.async { (i, await f(items[i])) } + group.addTask { (i, await f(items[i])) } } // collect all results @@ -494,8 +494,8 @@ For example, the `race(left:right:)` function shown below, runs two child tasks ```swift func race(left: () async -> Int, right: () async -> Int) async -> Int { await withTaskGroup(of: Int.self) { group in - group.async { left() } - group.async { right() } + group.addTask { left() } + group.addTask { right() } let first = await group.next()! // !-safe, there is at-least one result to collect group.cancelAll() // cancel the other task From 26eb88d0d0075ebf6bcdaff00a450ba751a8eb80 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 3 Aug 2024 00:02:04 +0100 Subject: [PATCH 3805/4563] [SE-0427] Noncopyable Generics is accepted (#2536) Update status field. Add review links. --- proposals/0427-noncopyable-generics.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0427-noncopyable-generics.md b/proposals/0427-noncopyable-generics.md index 9e6362fca7..278cfb580d 100644 --- a/proposals/0427-noncopyable-generics.md +++ b/proposals/0427-noncopyable-generics.md @@ -3,10 +3,10 @@ * Proposal: [SE-0427](0427-noncopyable-generics.md) * Authors: [Kavon Farvardin](https://github.com/kavon), [Tim Kientzle](https://github.com/tbkka), [Slava Pestov](https://github.com/slavapestov) * Review Manager: [Holly Borla](https://github.com/hborla), [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (July 1 - July 10, 2024)** +* Status: **Accepted** * Implementation: On `main` gated behind `-enable-experimental-feature NoncopyableGenerics` * Previous Proposal: [SE-0390: Noncopyable structs and enums](0390-noncopyable-structs-and-enums.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-noncopyable-generics/68180)) ([first review](https://forums.swift.org/t/se-0427-noncopyable-generics/70525)) +* Review: ([pitch](https://forums.swift.org/t/pitch-noncopyable-generics/68180)) ([first review](https://forums.swift.org/t/se-0427-noncopyable-generics/70525)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0427-noncopyable-generics/72039)) ([second review](https://forums.swift.org/t/second-review-se-0427-noncopyable-generics/72881)) ([acceptance](https://forums.swift.org/t/accepted-se-0427-noncopyable-generics/73560)) **Table of Contents** From eaa08a0f68c71674fe57f1138ccf5ae48a7ca79a Mon Sep 17 00:00:00 2001 From: mocolaa <125405094+mocolaa@users.noreply.github.com> Date: Sat, 3 Aug 2024 07:22:37 +0800 Subject: [PATCH 3806/4563] [SE-0304] Delete a misrepresentation about cancellation in withThrowingTaskGroup In fact, when group await any not yet complete tasks: If any of those tasks throws, the remaining tasks will not be cancelled automatically. --- proposals/0304-structured-concurrency.md | 1 - 1 file changed, 1 deletion(-) diff --git a/proposals/0304-structured-concurrency.md b/proposals/0304-structured-concurrency.md index 26d8d9b9e7..5bb381c6a3 100644 --- a/proposals/0304-structured-concurrency.md +++ b/proposals/0304-structured-concurrency.md @@ -1051,7 +1051,6 @@ func withTaskGroup( /// This is achieved in the following way: /// - if the body returns normally: /// - the group will await any not yet complete tasks, -/// - if any of those tasks throws, the remaining tasks will be cancelled, /// - once the `withTaskGroup` returns the group is guaranteed to be empty. /// - if the body throws: /// - all tasks remaining in the group will be automatically cancelled. From dbf4a7de6d30fd836a4a8f5887e0965213ffb9f8 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 5 Aug 2024 22:39:25 +0900 Subject: [PATCH 3807/4563] Avoid confusing acronym in SE-0371 (#2538) --- proposals/0371-isolated-synchronous-deinit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0371-isolated-synchronous-deinit.md b/proposals/0371-isolated-synchronous-deinit.md index 2cf4453658..d91e7fd4ae 100644 --- a/proposals/0371-isolated-synchronous-deinit.md +++ b/proposals/0371-isolated-synchronous-deinit.md @@ -13,7 +13,7 @@ This feature allows `deinit`'s of actors and global-actor isolated classes to ac ## Motivation -Restrictions imposed by [SE-0327](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0327-actor-initializers.md) reduce the usefulness of explicit `deinit`s in actors and GAITs. Workarounds for these limitations may involve creation of `close()`-like methods, or even manual reference counting if the API should be able to serve several clients. +Restrictions imposed by [SE-0327](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0327-actor-initializers.md) reduce the usefulness of explicit `deinit`s in actors and global actor isolated types. Workarounds for these limitations may involve creation of `close()`-like methods, or even manual reference counting if the API should be able to serve several clients. In cases when `deinit` belongs to a subclass of `UIView` or `UIViewController` which are known to call `dealloc` on the main thread, developers may be tempted to silence the diagnostic by adopting `@unchecked Sendable` in types that are not actually sendable. This undermines concurrency checking by the compiler, and may lead to data races when using incorrectly marked types in other places. From 048f0b651a828938e4631d601950cdc8a7a67e1e Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 6 Aug 2024 13:58:00 -0600 Subject: [PATCH 3808/4563] Update 0440-debug-description-macro.md (#2540) --- proposals/0440-debug-description-macro.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0440-debug-description-macro.md b/proposals/0440-debug-description-macro.md index 47cb953557..bfda4c3771 100644 --- a/proposals/0440-debug-description-macro.md +++ b/proposals/0440-debug-description-macro.md @@ -3,10 +3,10 @@ * Proposal: [SE-0440](0440-debug-description-macro.md) * Authors: [Dave Lee](https://github.com/kastiglione) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active Review (July 20-July 30, 2024)** +* Status: **Implemented (Swift 6.0)** * Implementation: Present in `main` under experimental feature `DebugDescriptionMacro` [apple/swift#69626](https://github.com/apple/swift/pull/69626) * Previous Revision: [1](https://github.com/swiftlang/swift-evolution/blob/fda6746506368c8c6d2933ee6d71c87e6ed92f94/proposals/0440-debug-description-macro.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-debug-description-macro/67711)) ([review](https://forums.swift.org/t/se-0440-debugdescription-macro/72958)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0440-debugdescription-macro/73270)) ([second review](https://forums.swift.org/t/second-review-se-0440-debugdescription-macro/73325)) +* Review: ([pitch](https://forums.swift.org/t/pitch-debug-description-macro/67711)) ([review](https://forums.swift.org/t/se-0440-debugdescription-macro/72958)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0440-debugdescription-macro/73270)) ([second review](https://forums.swift.org/t/second-review-se-0440-debugdescription-macro/73325))([acceptance](https://forums.swift.org/t/accepted-se-0440-debugdescription-macro/73741)) ## Introduction From 9b7f11ccb3bda1a71d6b9db1686bfd1a711f4563 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 7 Aug 2024 07:33:51 +0900 Subject: [PATCH 3809/4563] [SE-0417] Revise Task(executorPreference:) ownership semantics (#2504) Co-authored-by: Doug Gregor --- proposals/0417-task-executor-preference.md | 57 ++++++++++------------ 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/proposals/0417-task-executor-preference.md b/proposals/0417-task-executor-preference.md index cd668b3a6f..3c1d6b6634 100644 --- a/proposals/0417-task-executor-preference.md +++ b/proposals/0417-task-executor-preference.md @@ -422,37 +422,23 @@ and wrapping the code that is required to run on a specific executor in an `with Nevertheless, because we understand there may be situations where synchronous code may want to compare task executors, this capability is exposed for advanced use cases. -Another use case may be carrying the same task executor into an un-structured Task -- although this should only be done with **extreme caution**, -because it breaks structured concurrency lifetime expectations of executors. For example, the following code is correct under structured concurrency's -default and automatic behavior surrounding task executors: +### TaskExecutor ownership -```swift -func computeThings() async { - let eventLoop = MyCoolEventLoop() - defer { eventLoop.shutdown() } +Task executors, unlike serial executors, are explicitly owned by tasks as long as they are running on the given task executor. - let computed = await withTaskExecutorPreference(eventLoop) { - async let first = computation(1) - async let second = computation(2) - return await first + second - } +This is achieved in two ways. The `withTaskExecutorPreference` APIs by their construction as `with...`-style APIs, +naturally retain and keep alive the task executor for as long as the `with... { ... }` body is executing. +This also naturally extends to other structured concurrency constructs like `async let` and task groups, which can +rely on the task executor to remain alive while these constructs are running within such `withTaskExecutorPreference(...) { ... }` closure body. - return computed // event loop will be shutdown and the executor destroyed(!) -} +Unstructured tasks which are started with a task executor preference (e.g. `Task(executorPreference: someTaskExecutor)`), +take ownership of the executor for as long as the task is running. -func computation(_ int: Int) -> Int { return int * 2 } -``` - -The above code is structurally correct and we guarantee the lifetime of `MyCoolEventLoop` throughout all of its uses -by structured concurrency tasks in this snippet. - -The following snippet is **not safe**, which is why task executors are not inherited to un-structured tasks: +In other words, it is safe to rely on a task, structured or not, to keep alive the task executor it may be running on. +This makes it possible to write code like the following snippet, without having to worry about manually keeping the +executor alive until "all tasks which may be executing on it have finished": ```swift -// !!! POTENTIALLY UNSAFE !!! -// Do not do this, unless you can guarantee the lifetime of TaskExecutor -// exceeds all potential for any task to be running on it (!) - func computeThings() async { let eventLoop: any TaskExecutor = MyCoolEventLoop() defer { eventLoop.shutdown() } @@ -466,19 +452,27 @@ func computeThings() async { return computed // event loop will be shutdown and the executor destroyed(!) } -// DANGEROUS; MUST ENSURE THE EXECUTOR REMAINS ALIVE FOR AS LONG AS ANY TASK MAY BE RUNNING ON IT func computation(_ int: Int) -> Int { withUnsafeCurrentTask { task in let unownedExecutor: UnownedTaskExecutor? = task?.unownedTaskExecutor - let eventLoop: MyCoolEventLoop? = EventLoops.find(unownedExecutor) - - // Dangerous because there is no structured guarantee that eventLoop will be kept alive - // for as long as there are any of its child tasks and functions running on it - Task(executorPreference: eventLoop) { ... } + let eventLoop: MyCoolEventLoop = EventLoops.find(unownedExecutor) + // we need to start an unstructured task for some reason (try to avoid this if possible) + // and we have located the `MyCoolEventLoop` in our "cache". + // + // Since we have a real MyCoolEventLoop reference, this is safe to forward + // to the unstructured task which will retain it. + Task(executorPreference: eventLoop) { + async let something = ... // inherits the executor preference + } } } ``` +Same as with `SerialExecutor`'s `UnownedSerialExecutor` type, the `UnownedTaskExecutor` does _not_ retain the executor, +so you have to be extra careful when relying on unowned task executor references for any kind of operations. If you +were to write some form of "lookup" function, which takes an unowned executor and returns an `any TaskExecutor`, +please make sure that the returned references are alive (i.e. by keeping them alive in the "cache" using strong references). + ## Combining `SerialExecutor` and `TaskExecutor` It is possible to declare a single executor type and have it conform to *both* the `SerialExecutor` (introduced in the custom actor executors proposal), @@ -786,6 +780,7 @@ We considered if not introducing this feature could be beneficial and forcing de ## Revisions + - 1.6 - introduce the global `var defaultConcurrentExecutor: any TaskExecutor` we we can express a task specifically wanting to run on the default global concurrency pool. - 1.5 From f358d792077e42da89dfc0c8ab568039e29368ef Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 6 Aug 2024 15:48:40 -0700 Subject: [PATCH 3810/4563] Accept SE-0422: Allow TaskGroup's ChildTaskResult Type To Be Inferred (#2541) --- ...442-allow-taskgroup-childtaskresult-type-to-be-inferred.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md b/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md index 6023718ee3..d4f28f5575 100644 --- a/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md +++ b/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md @@ -3,9 +3,9 @@ * Proposal: [SE-0442](0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md) * Author: [Richard L Zarth III](https://github.com/rlziii) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (July 23...August 6, 2024)** +* Status: **Accepted** * Implementation: [apple/swift#74517](https://github.com/apple/swift/pull/74517) -* Review: ([pitch](https://forums.swift.org/t/allow-taskgroups-childtaskresult-type-to-be-inferred/72175))([review](https://forums.swift.org/t/se-0442-allow-taskgroups-childtaskresult-type-to-be-inferred/73397)) +* Review: ([pitch](https://forums.swift.org/t/allow-taskgroups-childtaskresult-type-to-be-inferred/72175))([review](https://forums.swift.org/t/se-0442-allow-taskgroups-childtaskresult-type-to-be-inferred/73397))([acceptance](https://forums.swift.org/t/accepted-se-0422-allow-taskgroups-childtaskresult-type-to-be-inferred/73747)) ## Introduction From d9097a3232be5ea3db011d48c7d4f6f8acebad6b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 7 Aug 2024 11:01:21 -0700 Subject: [PATCH 3811/4563] [SE-0413] Shrink proposal to what was implemented in Swift 6.0 (#2535) * [SE-0413] Shrink proposal to what was implemented in Swift 6.0 Error type inference for closures did not make it into Swift 6.0, although the rest of this proposal did (as well as some of the future directions, e.g., for `AsyncSequence`). Move this inference, and the other changes behind the upcoming flag `FullTypedThrows`, out to "Future Directions" and mark this proposal as (otherwise) implemented. * Fix mangled sentence --- proposals/0413-typed-throws.md | 121 +++++++++++++++++---------------- 1 file changed, 61 insertions(+), 60 deletions(-) diff --git a/proposals/0413-typed-throws.md b/proposals/0413-typed-throws.md index 44bfacd731..31e695be98 100644 --- a/proposals/0413-typed-throws.md +++ b/proposals/0413-typed-throws.md @@ -3,8 +3,7 @@ * Proposal: [SE-0413](0413-typed-throws.md) * Authors: [Jorge Revuelta (@minuscorp)](https://github.com/minuscorp), [Torsten Lehmann](https://github.com/torstenlehmann), [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Accepted** -* Upcoming Feature Flag: `FullTypedThrows` +* Status: **Implemented (Swift 6)** * Review: [latest pitch](https://forums.swift.org/t/pitch-n-1-typed-throws/67496), [review](https://forums.swift.org/t/se-0413-typed-throws/68507), [acceptance](https://forums.swift.org/t/accepted-se-0413-typed-throws/69099) ## Introduction @@ -13,6 +12,8 @@ Swift's error handling model allows functions and closures marked `throws` to no This proposal introduces the ability to specify that functions and closures only throw errors of a particular concrete type. +> Note: the [originally accepted version](https://github.com/swiftlang/swift-evolution/blob/821970ae986219f88eb3f950ed787a55ce31d512/proposals/0413-typed-throws.md) of this proposal included type inference changes intended for Swift 6.0 that were behind the upcoming feature flag `FullTypedThrows`. These type inference changes did not get implemented in Swift 6.0, and have therefore been removed from this proposal and placed into "Future Directions" so they can be revisited once implemented. + ## Table of Contents [Typed throws](#typed-throws) @@ -46,7 +47,6 @@ This proposal introduces the ability to specify that functions and closures only * [Protocol conformance](#protocol-conformance) * [Override checking](#override-checking) * [Type inference](#type-inference) - * [Closure thrown type inference](#closure-thrown-type-inference) * [Associated type inference](#associated-type-inference) * [Standard library adoption](#standard-library-adoption) * [Converting between throws and Result](#converting-between-throws-and-result) @@ -55,6 +55,7 @@ This proposal introduces the ability to specify that functions and closures only * [Effect on API resilience](#effect-on-api-resilience) * [Effect on ABI stability](#effect-on-abi-stability) * [Future directions](#future-directions) + * [Closure thrown type inference](#closure-thrown-type-inference) * [Standard library operations that rethrow](#standard-library-operations-that-rethrow) * [Concurrency library adoption](#concurrency-library-adoption) * [Specific thrown error types for distributed actors](#specific-thrown-error-types-for-distributed-actors) @@ -687,7 +688,7 @@ do /*infers throws(CatError) in Swift 6 */ { } ``` -> **Swift 6**: To prevent this source compatibility issue, we can refine the rule slightly for Swift 5 code bases to specify that any `throw` statement always throws a value of type `any Error`. That way, one can only get a caught error type more specific than `any Error` when the both of the `do..catch` contains no `throw` statements and all of the `try` operations are using functions that make use of typed throws. +To prevent this source compatibility issue, we refine the rule slightly to specify that any `throw` statement always throws a value of type `any Error`. That way, one can only get a caught error type more specific than `any Error` when the both of the `do..catch` contains no `throw` statements and all of the `try` operations are using functions that make use of typed throws. Note that the only way to write an exhaustive `do...catch` statement is to have an unconditional `catch` block. The dynamic checking provided by `is` or `as` patterns in the `catch` block cannot be used to make a catch exhaustive, even if the type specified is the same as the type thrown from the body of the `do`: @@ -787,7 +788,7 @@ The standard `rethrows` checking rejects the call to `filter` because, technical 2. Has no protocol requirements on `E` other than that it conform to the `Error` protocol, and 3. Any parameters of throwing function type throw the specific error type `E`. -to be a rethrowing function for the purposes of `rethrows` checking in its caller. This compatibility feature introduces a small soundness hole in `rethrows` functions, so it is temporary: it is only available in Swift 5, and is removed when the `FullTypedThrows` upcoming feature is enabled. +to be a rethrowing function for the purposes of `rethrows` checking in its caller. This compatibility feature introduces a small soundness hole in `rethrows` functions that can only be removed with improvements to type inference behavior. #### Opaque thrown error types @@ -924,56 +925,6 @@ class Subsubclass: Subclass { The type checker can infer thrown error types in a number of different places, making it easier to carry specific thrown type information through a program without additional annotation. This section covers the various ways in which thrown errors interact with type inference. -#### Closure thrown type inference - -Function declarations must always explicitly specify whether they throw, optionally providing a specific thrown error type. For closures, whether they throw or not is inferred by the Swift compiler. Specifically, the Swift compiler looks at the structure of body of the closure. If the body of the closure contains a throwing site (either a `throw` statement or a `try` expression) that is not within an exhaustive `do...catch` (i.e., one that has an unconditional `catch` clause), then the closure is inferred to be `throws`. Otherwise, it is non-throwing. Here are some examples: - -```swift -{ throw E() } // throws - -{ try call() } // throws - -{ - do { - try call() - } catch let e as CatError { - // ... - } -} // throws, the do...catch is not exhaustive - -{ - do { - try call() - } catch e {} - // ... - } -} // does not throw, the do...catch is exhaustive -``` - -With typed throws, the closure type could be inferred to have a typed error by considering all of the throwing sites that aren't caught (let each have a thrown type `Ei`) and then inferring the closure's thrown error type to be `errorUnion(E1, E2, ... EN)`. - -> **Swift 6**: This inference rule will change the thrown error types of existing closures that throw concrete types. For example, the following closure: -> -> ```swift -> { -> if Int.random(in: 0..<24) < 20 { -> throw CatError.asleep -> } -> } -> ``` -> -> will currently be inferred as `throws`. With the rule specified here, it will be inferred as `throws(CatError)`. This could break some code that depends on the precisely inferred type. To prevent this from becoming a source compatibility problem, we apply the same rule as for `do...catch` statements to limit inference: `throw` statements within the closure body are treated as having the type `any Error` in Swift 5. This way, one can only infer a more specific thrown error type in a closure when the `try` operations are calling functions that make use of typed errors. -> -> Note that one can explicitly specify the thrown error type of a closure to disable this type inference, which has the nice effect of also providing a contextual type for throw statements: -> -> ```swift -> { () throws(CatError) in -> if Int.random(in: 0..<24) < 20 { -> throw .asleep -> } -> } -> ``` - #### Associated type inference An associated type can be used as the thrown error type in other protocol requirements. For example: @@ -1064,11 +1015,7 @@ This is a mechanical transformation that is applied throughout the standard libr ## Source compatibility -This proposal has called out two specific places where the introduction of typed throws into the language will affect source compatibility. In both cases, the type inference behavior of the language will differ when there are `throw` statements that throw a specific concrete type. - -To mitigate this source compatibility problem in Swift 5, `throw` statements will be treated as always throwing `any Error`. In Swift 6, they will be treated as throwing the type of their thrown expression. One can enable the Swift 6 behavior with the [upcoming feature flag](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md) named `FullTypedThrows`. - -Note that the source compatibility arguments in this proposal are there to ensure that Swift code that does not use typed throws will continue to work in the same way it always has. Once a function adopts typed throws, the effect of typed throws can then ripple to its callers. +This proposal has called out a few specific places where the introduction of typed throws into the language could affect source compatibility. However, in those places, we have opted for semantics that ensure that existing Swift code that does not change behavior, to make this proposal act as a purely additive change to the language. Once a function adopts typed throws, the effect of typed throws can then ripple to its callers. ## Effect on API resilience @@ -1184,6 +1131,58 @@ This way, clients compiled against the updated standard library will always use ## Future directions +### Closure thrown type inference + +Function declarations must always explicitly specify whether they throw, optionally providing a specific thrown error type. For closures, whether they throw or not is inferred by the Swift compiler. Specifically, the Swift compiler looks at the structure of body of the closure. If the body of the closure contains a throwing site (either a `throw` statement or a `try` expression) that is not within an exhaustive `do...catch` (i.e., one that has an unconditional `catch` clause), then the closure is inferred to be `throws`. Otherwise, it is non-throwing. Here are some examples: + +```swift +{ throw E() } // throws + +{ try call() } // throws + +{ + do { + try call() + } catch let e as CatError { + // ... + } +} // throws, the do...catch is not exhaustive + +{ + do { + try call() + } catch e {} + // ... + } +} // does not throw, the do...catch is exhaustive +``` + +With typed throws, the closure type could be inferred to have a typed error by considering all of the throwing sites that aren't caught (let each have a thrown type `Ei`) and then inferring the closure's thrown error type to be `errorUnion(E1, E2, ... EN)`. + +This inference rule will change the thrown error types of existing closures that throw concrete types. For example, the following closure: + +```swift +{ + if Int.random(in: 0..<24) < 20 { + throw CatError.asleep + } +} +``` + +will currently be inferred as `throws`. With the rule specified here, it will be inferred as `throws(CatError)`. This could break some code that depends on the precisely inferred type. To prevent this from becoming a source compatibility problem, we apply the same rule as for `do...catch` statements to limit inference: `throw` statements within the closure body are treated as having the type `any Error` in Swift 5. This way, one can only infer a more specific thrown error type in a closure when the `try` operations are calling functions that make use of typed errors. + +Note that one can explicitly specify the thrown error type of a closure to disable this type inference, which has the nice effect of also providing a contextual type for throw statements: + +```swift +{ () throws(CatError) in + if Int.random(in: 0..<24) < 20 { + throw .asleep + } +} +``` + +Such a change would need to be under an upcoming feature flag (e.g., `FullTypedThrows`) and should also involve inference from the actual thrown error type of `throw` statements as well as closing the minor semantic hole introduced for compatibility with `rethrows` functions. + ### Concurrency library adoption The concurrency library has a number of places that could benefit from the adoption of typed throws, including `Task` creation and completion, continuations, task cancellation, task groups, and async sequences and streams. @@ -1374,6 +1373,8 @@ Removing or changing the semantics of `rethrows` would be a source-incompatible ## Revision history +* Revision 6 (post-review): + * Closure type inference did not get implemented in Swift 6.0, so this proposal has been "shrunk" down to what actually got implemented in Swift 6.0. * Revision 5 (first review): * Add `do throws(MyError)` { ... } syntax to allow explicit specification of the thrown error type within the body of a `do..catch` block, suppressing type inference of the thrown error type. Thank you to Becca Royal-Gordon for the idea! * Revision 4: From 109f528218922226085c0576cdf13702ad5a36a8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 7 Aug 2024 12:44:58 -0700 Subject: [PATCH 3812/4563] SE-0442 has been implemented on main (#2542) --- .../0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md b/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md index d4f28f5575..1abe4d92f3 100644 --- a/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md +++ b/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md @@ -3,7 +3,7 @@ * Proposal: [SE-0442](0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md) * Author: [Richard L Zarth III](https://github.com/rlziii) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift Next)** * Implementation: [apple/swift#74517](https://github.com/apple/swift/pull/74517) * Review: ([pitch](https://forums.swift.org/t/allow-taskgroups-childtaskresult-type-to-be-inferred/72175))([review](https://forums.swift.org/t/se-0442-allow-taskgroups-childtaskresult-type-to-be-inferred/73397))([acceptance](https://forums.swift.org/t/accepted-se-0422-allow-taskgroups-childtaskresult-type-to-be-inferred/73747)) From 6ad26eb9006bb690ab5d60f2851689ef045decb9 Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Wed, 7 Aug 2024 13:02:55 -0700 Subject: [PATCH 3813/4563] Add upcoming feature flag to SE-0337 to match inclusion in compiler (#2384) - Add 'Implemented in 6.0' to match guidance the flag should not be used as an upcoming feature flag until Swift 6.0 - Annotate that flag is always enabled in Swift 6 language mode --- ...0337-support-incremental-migration-to-concurrency-checking.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0337-support-incremental-migration-to-concurrency-checking.md b/proposals/0337-support-incremental-migration-to-concurrency-checking.md index efe384df91..2bfd135f3c 100644 --- a/proposals/0337-support-incremental-migration-to-concurrency-checking.md +++ b/proposals/0337-support-incremental-migration-to-concurrency-checking.md @@ -4,6 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor), [Becca Royal-Gordon](https://github.com/beccadax) * Review Manager: [Ben Cohen](https://github.com/AirspeedSwift) * Status: **Implemented (Swift 5.6)** +* Upcoming Feature Flag: `StrictConcurrency` (Implemented in Swift 6.0) (Enabled in Swift 6 language mode) * Implementation: [Pull request](https://github.com/apple/swift/pull/40680), [Linux toolchain](https://ci.swift.org/job/swift-PR-toolchain-Linux/761//artifact/branch-main/swift-PR-40680-761-ubuntu16.04.tar.gz), [macOS toolchain](https://ci.swift.org/job/swift-PR-toolchain-osx/1256//artifact/branch-main/swift-PR-40680-1256-osx.tar.gz) ## Introduction From b3d79afbe083a770c79f328bb40d4834e941f163 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 7 Aug 2024 23:53:47 +0100 Subject: [PATCH 3814/4563] [SE-0413] Normalize version number to "6.0" (#2543) --- proposals/0413-typed-throws.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0413-typed-throws.md b/proposals/0413-typed-throws.md index 31e695be98..b5184677cf 100644 --- a/proposals/0413-typed-throws.md +++ b/proposals/0413-typed-throws.md @@ -3,7 +3,7 @@ * Proposal: [SE-0413](0413-typed-throws.md) * Authors: [Jorge Revuelta (@minuscorp)](https://github.com/minuscorp), [Torsten Lehmann](https://github.com/torstenlehmann), [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Implemented (Swift 6)** +* Status: **Implemented (Swift 6.0)** * Review: [latest pitch](https://forums.swift.org/t/pitch-n-1-typed-throws/67496), [review](https://forums.swift.org/t/se-0413-typed-throws/68507), [acceptance](https://forums.swift.org/t/accepted-se-0413-typed-throws/69099) ## Introduction From f65f16282c6b69b8a607a93f91a437d5ff2ebecb Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Mon, 5 Aug 2024 15:20:06 -0700 Subject: [PATCH 3815/4563] Revise SE-0441 to reflect changes in accepted proposal --- ...441-formalize-language-mode-terminology.md | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/proposals/0441-formalize-language-mode-terminology.md b/proposals/0441-formalize-language-mode-terminology.md index 9cb32868ab..4de3475990 100644 --- a/proposals/0441-formalize-language-mode-terminology.md +++ b/proposals/0441-formalize-language-mode-terminology.md @@ -11,7 +11,7 @@ The term "Swift version” can refer to either the toolchain/compiler version or the language mode. This ambiguity is a consistent source of confusion. This proposal formalizes the term _language mode_ in tool options and APIs. ## Proposed Solution -The proposed solution is to use the term _language mode_ for the appropriate Swift compiler option and Swift Package Manager APIs. Use of "Swift version" to refer to language mode will be deprecated or obsoleted as needed. +The proposed solution is to use the term _language mode_ for the appropriate Swift compiler option and Swift Package Manager APIs. Use of "Swift version" to refer to language mode will be deprecated. ### Terminology The term _language mode_ has been consistently used to describe this compiler feature since it was introduced with Swift 4.0 and is an established term of art in the Swift community. @@ -49,7 +49,7 @@ Add a new `init` method to `Package` with the following changes from the current - The parameter `swiftLanguageVersions` is renamed to `swiftLanguageModes` - The parameter type is now an optional array of `SwiftLanguageMode` values instead of `SwiftVersion` values -The existing init method will be marked as `obsoleted` and `renamed` allowing the compiler to provide a fix-it. +The existing init method will be marked as `deprecated` and `renamed` allowing the compiler to provide a fix-it. It will also have the @_disfavoredOverload attribute added to allow the compiler to disambiguate. #### 2. Rename `swiftLanguageVersions` property to `swiftLanguageModes` Rename the public `Package` property `swiftLanguageVersions` to `swiftLanguageModes`. Add a `swiftLanguageVersions` computed property that accesses `swiftLanguageModes` for backwards compatibility. @@ -104,7 +104,7 @@ The `-swift-version` option will be suppressed from the top-level help of the co ### Swift Package Manager Proposed Swift Package Manager API changes are limited to manifests \>= 6.0: -### New Package init method and obsoleted init method +### New Package init method and deprecated init method A new `init` method will be added to `Package` that renames the `swiftLanguageVersions` parameter to `swiftLanguageModes` with the type of the parameter being an optional array of `SwiftLanguageMode` values instead of `SwiftVersion` values: ```swift @@ -123,10 +123,11 @@ Package( ``` -The existing init method will be marked as `obsoleted` and `renamed`, allowing the compiler to provide a fix-it: +The existing init method will be marked as `deprecated` and `renamed`, allowing the compiler to provide a fix-it. It will also be annotated with `@_disfavoredOverload` to disambiguate the new and existing init methods: ``` -@available(_PackageDescription, introduced: 5.3, obsoleted: 6, renamed: +@_disfavoredOverload +@available(_PackageDescription, introduced: 5.3, deprecated: 6, renamed: "init(name:defaultLocalization:platforms:pkgConfig:providers:products: dependencies:targets:swiftLanguageModes:cLanguageStandard: cxxLanguageStandard:)") @@ -139,8 +140,8 @@ cxxLanguageStandard:)") ) { ``` -#### Obsoleting existing init method -The existing method must be obsoleted because the two methods are ambiguous when the default value for `swiftLanguageVersions` / `swiftLanguageModes` is used: +#### Deprecating the existing init method by making it a disfavored overload +In order to deprecate the existing method instead of marking it obsoleted, the existing method needs to be annotated with the `@_disfavoredOverlaod` attribute. This is because the two methods are ambiguous when the default value for `swiftLanguageVersions` / `swiftLanguageModes` is used: ``` Package ( // Error: Ambiguous use of 'init' @@ -150,7 +151,7 @@ Package ( // Error: Ambiguous use of 'init' ) ``` -This follows the same approach used by all past revisions of the Package `init` method. +Adding the `@_disfavoredOverlaod` attribute allows the compiler to disambiguate between the two methods. See the **Source compatibility** section for more details about this change. @@ -212,11 +213,11 @@ public struct SwiftSetting { ``` ## Source compatibility -The new Package `init` method and obsoleting of the existing `init` method will cause source breakage for package manifests that specify the existing `swiftLanguageVersions` parameter when updating to swift tools version 6.0 +The new Package `init` method and deprecating the existing `init` method will cause a deprecation warning for package manifests that specify the existing `swiftLanguageVersions` parameter when updating to swift tools version 6.0 A search of manifest files in public repositories suggests that about 10% of manifest files will encounter this breakage. -Because the obsoleted `init` method is annotated as `renamed` the compiler will automatically provide a fix-it to update to the new `init` method. +Because the deprecated `init` method is annotated as `renamed` the compiler will automatically provide a fix-it to update to the new `init` method. Renaming the public `swiftLanguageVersions` property of `Package` preserves backwards compatibility by introducing a computed property with that name. The computed property will be marked as `deprecated` in 6.0 and annotated as `renamed` to provide a fix-it. From 53c7bfc8f4a7a6ec23ed80ef8c808e9157e65314 Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Tue, 6 Aug 2024 18:03:27 -0700 Subject: [PATCH 3816/4563] Update status to Accepted and add acceptance forum thread --- proposals/0441-formalize-language-mode-terminology.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0441-formalize-language-mode-terminology.md b/proposals/0441-formalize-language-mode-terminology.md index 4de3475990..f73160b596 100644 --- a/proposals/0441-formalize-language-mode-terminology.md +++ b/proposals/0441-formalize-language-mode-terminology.md @@ -3,9 +3,9 @@ * Proposal: [SE-0441](0441-formalize-language-mode-terminology.md) * Author: [James Dempsey](https://github.com/dempseyatgithub) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (July 15th...29th, 2024)** +* Status: **Accepted** * Implementation: [apple/swift-package-manager#7620](https://github.com/apple/swift-package-manager/pull/7620) -* Review: ([first pitch](https://forums.swift.org/t/pitch-formalize-swift-language-mode-naming-in-tools-and-api/71733)) ([second pitch](https://forums.swift.org/t/pitch-2-formalize-language-mode-naming-in-tools-and-api/72136)) ([review](https://forums.swift.org/t/se-0441-formalize-language-mode-terminology/73182)) +* Review: ([first pitch](https://forums.swift.org/t/pitch-formalize-swift-language-mode-naming-in-tools-and-api/71733)) ([second pitch](https://forums.swift.org/t/pitch-2-formalize-language-mode-naming-in-tools-and-api/72136)) ([review](https://forums.swift.org/t/se-0441-formalize-language-mode-terminology/73182)) ([acceptance](https://forums.swift.org/t/accepted-se-0441-formalize-language-mode-terminology/73716)) ## Introduction The term "Swift version” can refer to either the toolchain/compiler version or the language mode. This ambiguity is a consistent source of confusion. This proposal formalizes the term _language mode_ in tool options and APIs. From 540b9c6c77a48fefd68ca248daba749b6ed22875 Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Thu, 8 Aug 2024 06:48:09 -0700 Subject: [PATCH 3817/4563] Incorporate changes from PR feedback --- ...441-formalize-language-mode-terminology.md | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/proposals/0441-formalize-language-mode-terminology.md b/proposals/0441-formalize-language-mode-terminology.md index f73160b596..dcfbc9d2d6 100644 --- a/proposals/0441-formalize-language-mode-terminology.md +++ b/proposals/0441-formalize-language-mode-terminology.md @@ -4,7 +4,7 @@ * Author: [James Dempsey](https://github.com/dempseyatgithub) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Accepted** -* Implementation: [apple/swift-package-manager#7620](https://github.com/apple/swift-package-manager/pull/7620) +* Implementation: [swiftlang/swift-package-manager#7620](https://github.com/swiftlang/swift-package-manager/pull/7620), [swiftlang/swift#75564](https://github.com/swiftlang/swift/pull/75564) * Review: ([first pitch](https://forums.swift.org/t/pitch-formalize-swift-language-mode-naming-in-tools-and-api/71733)) ([second pitch](https://forums.swift.org/t/pitch-2-formalize-language-mode-naming-in-tools-and-api/72136)) ([review](https://forums.swift.org/t/se-0441-formalize-language-mode-terminology/73182)) ([acceptance](https://forums.swift.org/t/accepted-se-0441-formalize-language-mode-terminology/73716)) ## Introduction @@ -49,7 +49,7 @@ Add a new `init` method to `Package` with the following changes from the current - The parameter `swiftLanguageVersions` is renamed to `swiftLanguageModes` - The parameter type is now an optional array of `SwiftLanguageMode` values instead of `SwiftVersion` values -The existing init method will be marked as `deprecated` and `renamed` allowing the compiler to provide a fix-it. It will also have the @_disfavoredOverload attribute added to allow the compiler to disambiguate. +The existing init method will be marked as `deprecated` and `renamed` allowing the compiler to provide a fix-it. #### 2. Rename `swiftLanguageVersions` property to `swiftLanguageModes` Rename the public `Package` property `swiftLanguageVersions` to `swiftLanguageModes`. Add a `swiftLanguageVersions` computed property that accesses `swiftLanguageModes` for backwards compatibility. @@ -123,7 +123,7 @@ Package( ``` -The existing init method will be marked as `deprecated` and `renamed`, allowing the compiler to provide a fix-it. It will also be annotated with `@_disfavoredOverload` to disambiguate the new and existing init methods: +The existing init method will be marked as `deprecated` and `renamed`, allowing the compiler to provide a fix-it: ``` @_disfavoredOverload @@ -140,19 +140,6 @@ cxxLanguageStandard:)") ) { ``` -#### Deprecating the existing init method by making it a disfavored overload -In order to deprecate the existing method instead of marking it obsoleted, the existing method needs to be annotated with the `@_disfavoredOverlaod` attribute. This is because the two methods are ambiguous when the default value for `swiftLanguageVersions` / `swiftLanguageModes` is used: - -``` -Package ( // Error: Ambiguous use of 'init' - name: "MyPackage", - products: ..., - targets: ... -) -``` - -Adding the `@_disfavoredOverlaod` attribute allows the compiler to disambiguate between the two methods. - See the **Source compatibility** section for more details about this change. ### Rename `swiftLanguageVersions` property to `swiftLanguageModes` From e1069b760c70638a4291f7e053d4be663129f485 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Thu, 11 Apr 2024 12:45:41 -0700 Subject: [PATCH 3818/4563] Initial draft of 'Member import visibility' proposal. --- proposals/NNNN-member-import-visibility.md | 139 +++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 proposals/NNNN-member-import-visibility.md diff --git a/proposals/NNNN-member-import-visibility.md b/proposals/NNNN-member-import-visibility.md new file mode 100644 index 0000000000..777fe4af08 --- /dev/null +++ b/proposals/NNNN-member-import-visibility.md @@ -0,0 +1,139 @@ +# Member import visibility + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Allan Shortlidge](https://github.com/tshortli) +* Review Manager: TBD +* Status: **Awaiting review** +* Bug: [apple/swift#46493](https://github.com/apple/swift/issues/46493) +* Implementation: [apple/swift#72974](https://github.com/apple/swift/pull/72974), [apple/swift#73063](https://github.com/apple/swift/pull/73063) +* Upcoming Feature Flag: `MemberImportVisibility` +* Review: TBD + +## Introduction + +In Swift, there are rules dictating whether the name of a declaration in another module is considered in scope. For example, if you have a program that uses the `swift-algorithms` package and you want to use the global function [chain()](https://github.com/apple/swift-algorithms/blob/33abb694280321a84aa7dc9806de284afb8ca226/Sources/Algorithms/Chain.swift#L287) then you must write `import Algorithms` in the file that references that function or the compiler will consider it out of scope: + +``` swift +// Missing 'import Algorithms' +let chained = chain([1], [2]) // error: Cannot find 'chain' in scope +``` + +The visibility rules for a member declaration, such as a method declared inside of a struct, are different though. When resolving a name to a member declaration, the member is in scope even if the module introducing the member is only *transitively* imported. A transitively imported module could be imported directly in another source file, or it could be a dependency of some direct dependency of your program. This inconsistency may be best understood as a subtle bug rather than an intentional design decision, and in a lot of Swift code it goes unnoticed. However, the import rules for members become more surprising when you consider the members of extensions, since an extension and its nominal type can be declared in different modules. + +This proposal unifies the behavior of name lookup by changing the rules to consistently require direct module imports in order to bring both top-level declarations and members into scope. + +## Motivation + +Suppose you have a program depends on a library named `RecipeKit`. The library interface looks like this: + +```swift +// RecipeKit interface + +public struct Recipe { /*...*/ } + +extension String { + /// Returns the recipe represented by this string. + public func parse() -> Recipe? +} +``` + +To start, your program contains a single source file `main.swift` that imports `RecipeKit`: + +```swift +// main.swift +import RecipeKit + +let recipe = "2 slices of bread, 1.5 tbs peanut butter".parse() +``` + +Later, you decide to integrate with a new library named `GroceryKit` which happens to also declares its own `parse()` method in an extension on `String`: + +```swift +// GroceryKit interface +public struct GroceryList { /*...*/ } + +extension String { + /// Returns the grocery list represented by this string. + public func parse() -> GroceryList? +} + +``` + +You add a second file that imports `GroceryKit`: + +```swift +// Groceries.swift +import GroceryKit + +var groceries = GroceryList() +// ... +``` + +Surprisingly, now that `GroceryKit` is a transitive dependency of `main.swift`, there's a new compilation error: + +```swift +// main.swift +import RecipeKit + +let recipe = "2 slices of bread, 1.5 tbs peanut butter".parse() +// error: Ambiguous use of 'parse()' +``` + +Before the new file was added, `parse()` could only refer to the extension member from `RecipeKit`. Now that it might also reference the extension member in `GroceryKit` the compiler considers the use of `parse()` to be ambiguous. To resolve the ambiguity, the developer must add a type annotation to the declaration of the variable `recipe` to give the compiler the additional context it needs to disambiguate: +```swift +let recipe: Recipe = "2 slices of bread, 1.5 tbs peanut butter".parse() // OK +``` + +This example demonstrates why "leaky" member visibility is undesirable. Although the fix for the new error is relatively simple in this code, providing disambiguation context to the compiler is not always so straightforward. Additionally, the fact that some declarations from `GroceryKit` are now visible in `main.swift` contradicts developer expectations, since visibility rules for top level declarations do not behave this way. This idiosyncrasy in Swift's import visibility rules harms local reasoning and results in confusing errors. + +## Proposed solution + +In a future language version, or whenever the `MemberImportVisibility` feature is enabled, the Swift compiler should only consider members from directly imported modules to be in scope. + +## Detailed design + +Excluding references to members from transitively imported modules in the local scope will prevent surprising ambiguities like the one detailed earlier. However, this change of behavior will also break some existing code that used to be accepted. The Swift compiler will now emit an error for member references that used to successfully resolve to a declaration in a transitively imported module: + +```swift +// In this example, RecipeKit is imported in another file + +// note: add import of module 'RecipeKit' + +let recipe = "1 scoop ice cream, 1 tbs chocolate syrup".parse() +// error: instance method 'parse()' is inaccessible due to missing import of defining module 'RecipeKit' +``` + + +Rather than simply indicating that the member's name is not in-scope, though, the compiler can identify the declaration you likely meant to use and suggest importing the module that defines it. An IDE may also offer a fix-it to add the missing module import to the file. + +## Source compatibility + +The suggested change in behavior is source breaking because it adds stricter requirements to name lookup. There is much existing Swift code that will need to be updated to adhere to these new requirements, either by introducing additional import statements in some source files or by reorganizing code among files. This change in behavior therefore must be opt-in, which is why it should be limited to a future language mode with an upcoming feature identifier that allows opt-in with previous language modes. + +## ABI compatibility + +This change does not affect ABI. + +## Implications on adoption + +Adopting this feature ought to be straightforward because the compiler can identify module imports that are missing under the new rules and guide the developer to add an explicit import to resolve the error. Furthermore, although enabling this feature would be source breaking for many packages, it's important to note that source code that conforms to the new import requirements will be backwards compatible with older compilers and language modes that don't enforce this requirement. This feature does not require any new syntax or introduce rules that contradict rules of previous language modes. + +## Future directions + +#### Add module qualification syntax for extension members + +This proposal seeks to give developers explicit control over which members are visible in a source file because this control can be used to prevent and resolve ambiguities that arise when different modules declare conflicting members in extensions. With this proposal implemented, if an extension member ambiguity still arises in a source file then the developer has the option of curating the imports in that file to resolve the ambiguity. This may work in some situations, but in others it may be awkward to refactor code in order to avoid importing a module that introduces a conflict. For these cases it would be useful to have a syntax that unambiguously identifies the desired extension member at the use site. For example, here's a hypothetical syntax for explicitly calling the `parse()` method declared in the module `RecipeKit`: + +```swift +let recipe = "...".RecipeKit::parse() +``` + +## Alternatives considered + +#### Introduce module qualification syntax for extension members instead + +One alternative approach to the problem would be to rely exclusively on a new syntax for disambiguation of extension members (as discussed in Future directions). The limitation of that approach is that alone it only empowers the developer to solve conflicts *reactively*. On the other hand, the solution provided by this proposal is preventative because it stops unnecessary conflicts from arising in the first place. In the fullness of time, it would be best for both solutions to be available simultaneously. + +## Acknowledgments + +I would like to thank Doug Gregor for providing a proof-of-concept implementation of this pitch. \ No newline at end of file From 54522366d4e078e4f21a7e38564411d5ca801f0a Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Wed, 14 Aug 2024 12:55:14 -0700 Subject: [PATCH 3819/4563] Update the text of the 'Member import visibility' proposal to address feedback from the Swift Evolution forums. --- proposals/NNNN-member-import-visibility.md | 38 +++++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/proposals/NNNN-member-import-visibility.md b/proposals/NNNN-member-import-visibility.md index 777fe4af08..06173d85a4 100644 --- a/proposals/NNNN-member-import-visibility.md +++ b/proposals/NNNN-member-import-visibility.md @@ -20,7 +20,7 @@ let chained = chain([1], [2]) // error: Cannot find 'chain' in scope The visibility rules for a member declaration, such as a method declared inside of a struct, are different though. When resolving a name to a member declaration, the member is in scope even if the module introducing the member is only *transitively* imported. A transitively imported module could be imported directly in another source file, or it could be a dependency of some direct dependency of your program. This inconsistency may be best understood as a subtle bug rather than an intentional design decision, and in a lot of Swift code it goes unnoticed. However, the import rules for members become more surprising when you consider the members of extensions, since an extension and its nominal type can be declared in different modules. -This proposal unifies the behavior of name lookup by changing the rules to consistently require direct module imports in order to bring both top-level declarations and members into scope. +This proposal unifies the behavior of name lookup by changing the rules to bring both top-level declarations and members into scope using the same criteria. ## Motivation @@ -88,27 +88,28 @@ This example demonstrates why "leaky" member visibility is undesirable. Although ## Proposed solution -In a future language version, or whenever the `MemberImportVisibility` feature is enabled, the Swift compiler should only consider members from directly imported modules to be in scope. +In a future language version, or whenever the `MemberImportVisibility` feature is enabled, both member declarations and top level declarations should be resolved from the same set of visible modules in a given source file. ## Detailed design -Excluding references to members from transitively imported modules in the local scope will prevent surprising ambiguities like the one detailed earlier. However, this change of behavior will also break some existing code that used to be accepted. The Swift compiler will now emit an error for member references that used to successfully resolve to a declaration in a transitively imported module: +A reference to a member in a source file will only be accepted if that member is declared in a module that is contained in the set of visible modules for that source file. A module is in the set of visible modules if any of the following statements are true: -```swift -// In this example, RecipeKit is imported in another file +- The module is directly imported. In other words, some import statement in the source file names the module explicitly. +- The module is directly imported from the bridging header. +- The module is in the set of modules that is re-exported by any module that is either directly imported in the file or directly imported in the bridging header. -// note: add import of module 'RecipeKit' +A module is considered to be re-exported by the module that imports it when any of the following statements are true: -let recipe = "1 scoop ice cream, 1 tbs chocolate syrup".parse() -// error: instance method 'parse()' is inaccessible due to missing import of defining module 'RecipeKit' -``` +- The associated import statement has the `@_exported` attribute. +- The exporting module is a clang module. +Re-exports are transitive, so if module `A` re-exports module `B`, and module `B` re-exports module `C`, then declarations from `A`, `B`, and `C` are all in scope in a file that directly imports `A`. -Rather than simply indicating that the member's name is not in-scope, though, the compiler can identify the declaration you likely meant to use and suggest importing the module that defines it. An IDE may also offer a fix-it to add the missing module import to the file. +Note that there are some imports that are added to every source file implicitly by the compiler for normal programs. The implicitly imported modules include the standard library and the module being compiled. As a subtle consequence of the implicit import of the current module, any module that is `@_exported` in any source file of the module is also part of the set of re-exported modules that are visible in the file. ## Source compatibility -The suggested change in behavior is source breaking because it adds stricter requirements to name lookup. There is much existing Swift code that will need to be updated to adhere to these new requirements, either by introducing additional import statements in some source files or by reorganizing code among files. This change in behavior therefore must be opt-in, which is why it should be limited to a future language mode with an upcoming feature identifier that allows opt-in with previous language modes. +The proposed change in behavior is source breaking because it adds stricter requirements to name lookup. There is much existing Swift code that will need to be updated to adhere to these new requirements, either by introducing additional import statements in some source files or by reorganizing code among files. This change in behavior therefore must be opt-in, which is why it should be limited to a future language mode with an upcoming feature identifier that allows opt-in with previous language modes. ## ABI compatibility @@ -116,7 +117,20 @@ This change does not affect ABI. ## Implications on adoption -Adopting this feature ought to be straightforward because the compiler can identify module imports that are missing under the new rules and guide the developer to add an explicit import to resolve the error. Furthermore, although enabling this feature would be source breaking for many packages, it's important to note that source code that conforms to the new import requirements will be backwards compatible with older compilers and language modes that don't enforce this requirement. This feature does not require any new syntax or introduce rules that contradict rules of previous language modes. +To make it easier to migrate to the new language mode, the compiler can attempt to identify whether a member reference would resolve to a member declared in a transitively imported module and emit a fix-it to suggest adding a direct import to resolve the errors caused by the stricter look up rules: + +```swift +// In this example, RecipeKit is imported in another file + +// note: add import of module 'RecipeKit' + +let recipe = "1 scoop ice cream, 1 tbs chocolate syrup".parse() +// error: instance method 'parse()' is inaccessible due to missing import of defining module 'RecipeKit' +``` + +With these fix-its, the burden of updating source code to be compatible with the new language mode should be significantly reduced. + +This feature will have some impact on source compatibility with older compilers and previous language modes. Adding new direct imports of modules that were previously only transitively imported is a backward compatible change syntactically. However, if the new language mode is necessary in order to make some source code unambiguous, then the ambiguity will become an issue when compiling the same code using an older language mode so maintaining backward compatibility would require additional measures to be taken. ## Future directions From a1752a3940d114d245095552a549a156a9fe9632 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Wed, 14 Aug 2024 16:16:06 -0700 Subject: [PATCH 3820/4563] Update 'Member import visibility' proposal: - Re-organize the Motivation section for clarity - Correct the explanation of Clang module re-exports --- proposals/NNNN-member-import-visibility.md | 58 ++++++++++------------ 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/proposals/NNNN-member-import-visibility.md b/proposals/NNNN-member-import-visibility.md index 06173d85a4..ebeb6dbae6 100644 --- a/proposals/NNNN-member-import-visibility.md +++ b/proposals/NNNN-member-import-visibility.md @@ -24,20 +24,7 @@ This proposal unifies the behavior of name lookup by changing the rules to bring ## Motivation -Suppose you have a program depends on a library named `RecipeKit`. The library interface looks like this: - -```swift -// RecipeKit interface - -public struct Recipe { /*...*/ } - -extension String { - /// Returns the recipe represented by this string. - public func parse() -> Recipe? -} -``` - -To start, your program contains a single source file `main.swift` that imports `RecipeKit`: +Suppose you have an app that depends on an external library named `RecipeKit`. To start, your app contains a single source file `main.swift` that imports `RecipeKit`: ```swift // main.swift @@ -46,20 +33,20 @@ import RecipeKit let recipe = "2 slices of bread, 1.5 tbs peanut butter".parse() ``` -Later, you decide to integrate with a new library named `GroceryKit` which happens to also declares its own `parse()` method in an extension on `String`: +The interface of `RecipeKit` looks like this: ```swift -// GroceryKit interface -public struct GroceryList { /*...*/ } +// RecipeKit interface + +public struct Recipe { /*...*/ } extension String { - /// Returns the grocery list represented by this string. - public func parse() -> GroceryList? + /// Returns the recipe represented by this string. + public func parse() -> Recipe? } - ``` -You add a second file that imports `GroceryKit`: +Later, you decide to integrate with a new library named `GroceryKit`. You add a second file to your app that imports `GroceryKit`: ```swift // Groceries.swift @@ -69,7 +56,7 @@ var groceries = GroceryList() // ... ``` -Surprisingly, now that `GroceryKit` is a transitive dependency of `main.swift`, there's a new compilation error: +Surprisingly, after adding the second file there's now a compilation error in `main.swift`: ```swift // main.swift @@ -79,12 +66,26 @@ let recipe = "2 slices of bread, 1.5 tbs peanut butter".parse() // error: Ambiguous use of 'parse()' ``` -Before the new file was added, `parse()` could only refer to the extension member from `RecipeKit`. Now that it might also reference the extension member in `GroceryKit` the compiler considers the use of `parse()` to be ambiguous. To resolve the ambiguity, the developer must add a type annotation to the declaration of the variable `recipe` to give the compiler the additional context it needs to disambiguate: +The call to `parse()` is now ambiguous because `GroceryKit` happens to also declares its own `parse()` method in an extension on `String`: + +```swift +// GroceryKit interface +public struct GroceryList { /*...*/ } + +extension String { + /// Returns the grocery list represented by this string. + public func parse() -> GroceryList? +} + +``` + +Even though `GroceryKit` was not imported in `main.swift`, its `parse()` method is now a candidate in that file. To resolve the ambiguity, you can add a type annotation to the declaration of the variable `recipe` to give the compiler the additional context it needs to disambiguate the call: + ```swift let recipe: Recipe = "2 slices of bread, 1.5 tbs peanut butter".parse() // OK ``` -This example demonstrates why "leaky" member visibility is undesirable. Although the fix for the new error is relatively simple in this code, providing disambiguation context to the compiler is not always so straightforward. Additionally, the fact that some declarations from `GroceryKit` are now visible in `main.swift` contradicts developer expectations, since visibility rules for top level declarations do not behave this way. This idiosyncrasy in Swift's import visibility rules harms local reasoning and results in confusing errors. +This example demonstrates why Swift's existing "leaky" member visibility is undesirable. Although the fix for the new error is relatively simple in this code, providing disambiguation context to the compiler is not always so straightforward. Additionally, the fact that some declarations from `GroceryKit` are now visible in `main.swift` contradicts developer expectations, since visibility rules for top level declarations do not behave this way. This idiosyncrasy in Swift's import visibility rules harms local reasoning and results in confusing errors. ## Proposed solution @@ -98,14 +99,9 @@ A reference to a member in a source file will only be accepted if that member is - The module is directly imported from the bridging header. - The module is in the set of modules that is re-exported by any module that is either directly imported in the file or directly imported in the bridging header. -A module is considered to be re-exported by the module that imports it when any of the following statements are true: - -- The associated import statement has the `@_exported` attribute. -- The exporting module is a clang module. - -Re-exports are transitive, so if module `A` re-exports module `B`, and module `B` re-exports module `C`, then declarations from `A`, `B`, and `C` are all in scope in a file that directly imports `A`. +A Swift module re-exports any modules that have been imported using the `@_exported` attribute. Clang modules list the modules that they re-export in their modulemap files, and it is common for a Clang module to re-export every module it imports using `export *`. Re-exports are also transitive, so if module `A` re-exports module `B`, and module `B` re-exports module `C`, then declarations from `A`, `B`, and `C` are all in scope in a file that only imports `A` directly. -Note that there are some imports that are added to every source file implicitly by the compiler for normal programs. The implicitly imported modules include the standard library and the module being compiled. As a subtle consequence of the implicit import of the current module, any module that is `@_exported` in any source file of the module is also part of the set of re-exported modules that are visible in the file. +Note that there are some imports that are added to every source file implicitly by the compiler for normal programs. The implicitly imported modules include the standard library and the module being compiled. As a subtle consequence implicitly importing the current module, any module that is `@_exported` in any source file is also considered visible in every other source file because it is a re-export of a direct import. ## Source compatibility From aca515e64a1b045dc5e1650961e496d0b9366b2e Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Thu, 15 Aug 2024 08:34:18 -0700 Subject: [PATCH 3821/4563] Fix a typo in the Motivation section of the 'Member import visibility' proposal. --- proposals/NNNN-member-import-visibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-member-import-visibility.md b/proposals/NNNN-member-import-visibility.md index ebeb6dbae6..d612e7d05a 100644 --- a/proposals/NNNN-member-import-visibility.md +++ b/proposals/NNNN-member-import-visibility.md @@ -82,7 +82,7 @@ extension String { Even though `GroceryKit` was not imported in `main.swift`, its `parse()` method is now a candidate in that file. To resolve the ambiguity, you can add a type annotation to the declaration of the variable `recipe` to give the compiler the additional context it needs to disambiguate the call: ```swift -let recipe: Recipe = "2 slices of bread, 1.5 tbs peanut butter".parse() // OK +let recipe: Recipe? = "2 slices of bread, 1.5 tbs peanut butter".parse() // OK ``` This example demonstrates why Swift's existing "leaky" member visibility is undesirable. Although the fix for the new error is relatively simple in this code, providing disambiguation context to the compiler is not always so straightforward. Additionally, the fact that some declarations from `GroceryKit` are now visible in `main.swift` contradicts developer expectations, since visibility rules for top level declarations do not behave this way. This idiosyncrasy in Swift's import visibility rules harms local reasoning and results in confusing errors. From 046cca829f7810ee5a8e9d05a29b6ddcf36724a9 Mon Sep 17 00:00:00 2001 From: Dmitrii Galimzianov Date: Sun, 11 Aug 2024 19:22:41 +0200 Subject: [PATCH 3822/4563] Warning control flags --- proposals/NNNN-warning-control-flags.md | 235 ++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 proposals/NNNN-warning-control-flags.md diff --git a/proposals/NNNN-warning-control-flags.md b/proposals/NNNN-warning-control-flags.md new file mode 100644 index 0000000000..bdd5bc9176 --- /dev/null +++ b/proposals/NNNN-warning-control-flags.md @@ -0,0 +1,235 @@ + +# Precise Control Flags over Compiler Warnings + +* Proposal: [SE-NNNN](NNNN-warning-control-flags.md) +* Authors: [Doug Gregor](https://github.com/douggregor), [Dmitrii Galimzianov](https://github.com/DmT021) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#74466](https://github.com/swiftlang/swift/pull/74466) +* Review: ([pitch](https://forums.swift.org/t/warnings-as-errors-exceptions/72925)) + +## Introduction + +This proposal introduces new compiler options that allow fine-grained control over how the compiler emits certain warnings: as warnings, as errors, or not at all. + + +## Motivation + +The current compiler options for controlling how warnings are emitted are very inflexible. Currently, the following options exist: +- `-warnings-as-errors` - upgrades all warnings to errors +- `-no-warnings-as-errors` - cancels the upgrade of warnings to errors +- `-suppress-warnings` - disables the emission of all warnings + +This lack of flexibility leads to situations where users who want to use `-warnings-as-errors` find themselves unable to do so, or unable to upgrade to a new version of the compiler or SDK until all newly diagnosed warnings are resolved. The most striking example of this is deprecation warnings for certain APIs, though they are not limited to them. + +## Proposed solution + +This proposal suggests adding new options that will allow the behavior of warnings to be controlled based on their diagnostic group. +- `-Werror ` - upgrades warnings in the specified group to errors +- `-Wsuppress ` - disables the emission of warnings in the specified group +- `-Wwarning ` - indicates that warnings in the specified group should remain warnings, even if they were previously suppressed or upgraded to errors + +The `` parameter is a string identifier of the diagnostic group. + +A diagnostic group is a stable identifier for an error or warning. It is an abstraction layer over the diagnostic identifiers used within the compiler. This is necessary because diagnostics within the compiler may change, but we need to provide stable user-facing identifiers for them. + +A diagnostic group may include errors, warnings, or other diagnostic groups. For example, the `availability_deprecated` diagnostic group includes warnings related to the use of an API marked with the `@available(..., deprecated: ...)` attribute. The `deprecated` diagnostic group includes the `availability_deprecated` group and other groups related to deprecation. + +Diagnostic groups may expand over time, but they can never become narrower. When a new diagnostic is added to the compiler, it is either included in an existing group or a new group is created for it, which in turn can also be included in one of the broader groups, if appropriate. + +The order in which these flags are specified when invoking the compiler is important. If two or more options change the behavior of the same warning, we follow the rule "the last one wins." + +We also retain the existing compiler options but modify their handling algorithm so that they are considered in the general list with the new options and follow the "last one wins" rule as well. + +Thus, for example, you can use the combination `-warnings-as-errors -Wwarning deprecated`, which will upgrade all warnings to errors except for those in the `deprecated` group. However, if these flags are specified in the reverse order(`-Wwarning deprecated -warnings-as-errors`) it will be interpreted as upgrading all warnings to errors, as the `-warnings-as-errors` flag is the last one. + +We are also introducing a new compiler flag, `-print-diagnostic-groups`, to display the names of diagnostic groups along with the textual representation of the warnings. When used, the warning message will be followed by the name of the narrowest group that includes that warning, enclosed in square brackets. For example: +``` +main.swift:33:1: warning: 'f()' is deprecated [availability_deprecated] +``` + +## Detailed design + +### Diagnostic groups + +Diagnostic groups form an acyclic graph with the following properties: + +- A warning or error can only be included in one diagnostic group. This artificial restriction is introduced to solve two main problems: + - When using the `-print-diagnostic-groups` flag, it would be inconvenient if a warning corresponded to multiple groups. + - Documentation lookup will also be easier for the user if a diagnostic has only one identifier. + +- A diagnostic group may include any number of other diagnostic groups. This will allow organizing groups into sets with similar meanings but different specific diagnostics. For example, the warnings `availability_deprecated` and `unsafe_global_actor_deprecated` are part of the supergroup `deprecated`. + +- A diagnostic group can be included in any number of diagnostic groups. This allows expressing the membership of a group in multiple supergroups, where appropriate. For example, the group `unsafe_global_actor_deprecated` is part of both the `deprecated` and `concurrency` groups. + +The internal structure of the graph may change to some extent. However, the set of diagnostics included in a diagnostic group (directly or transitively) should not shrink. There are two typical situations where the graph structure may change: +- When adding a new diagnostic to the compiler, we also add a new group corresponding to that diagnostic. This new group can also be included in one or more existing groups if it belongs to them. For example, it is expected that the `deprecated` group will continuously include new subgroups. +- If an existing diagnostic is split into more specific versions, and we want to allow users to use the more specific version in compiler options, a separate group is created for it, which **must** be included in the group of the original diagnostic. + + For example, suppose we split the `availability_deprecated` warning into a general version and a specialized version `availability_deprecated_same_module`, which the compiler emits if the deprecated symbol is declared in the same module. In this case, the `availability_deprecated_same_module` group must be added to the `availability_deprecated` group to ensure that the overall composition of the `availability_deprecated` group does not change. The final structure should look like this: + ``` + availability_deprecated (group) + ├─ availability_deprecated (internal diag id) + └─ availability_deprecated_same_module (group) + └─ availability_deprecated_same_module (internal diag id) + ``` + Thus, invoking the compiler with the `-Werror availability_deprecated` parameter will cover both versions of the warning, and the behavior will remain unchanged. At the same time, the user can control the behavior of the narrower `availability_deprecated_same_module` group if they want to. + +### Compiler options evaluation + +Each warning in the compiler is assigned one of three behaviors: `warning`, `error`, or `suppressed`. +Compiler options for controlling the behavior of groups are now processed as a single list. These options include: +``` +-Werror +-Wsuppress +-Wwarning +-warnings-as-errors +-no-warnings-as-errors +-suppress-warnings +``` +When these options are passed to the compiler, we sequentially apply the specified behavior to all warnings within the specified group from left to right. For `-warnings-as-errors`, `-no-warnings-as-errors`, and `-suppress-warnings`, we apply the behavior to all warnings. + +The `-no-warnings-as-errors` option should be read as "set the behavior to 'warning' for all warnings". Thus, it overrides all previously set behaviors, including if the `-suppress-warnings` option was applied earlier or if a warning was suppressed by default. + +Examples of option combinations: +- `-warnings-as-errors -Wwarning deprecated` + + Warnings from the `deprecated` group will be kept as warnings, but all the rest will be upgraded to errors. + +- `-warnings-as-errors -Wwarning deprecated -Wsuppress availability_deprecated` + + Warnings from the `availability_deprecated` group will be suppressed. Other warnings from the `deprecated` group will remain as warnings. All other warnings will be upgraded to errors. + +- `-suppress-warnings -Wwarning deprecated` + + Warnings from the `deprecated` group will remain as warnings. All others will be suppressed. + +It’s crucial to understand that the order in which these flags are applied can significantly affect the behavior of diagnostics. The rule is "the last one wins", meaning that if multiple flags apply to the same diagnostic group, the last one specified on the command line will determine the final behavior. + +It is also important to note that the order matters even if the specified groups are not explicitly related but have a common subgroup. +For example, as mentioned above, the `unsafe_global_actor_deprecated` group is part of both the `deprecated` and `concurrency` groups. So the order in which options for the `deprecated` and `concurrency` groups are applied will change the final behavior of the `unsafe_global_actor_deprecated` group. Specifically: +- `-Wwarning deprecated -Werror concurrency` will make it an error, +- `-Werror concurrency -Wwarning deprecated` will keep it as a warning. + +### Usage of `-print-diagnostic-groups` and `-debug-diagnostic-names` + +As mentioned earlier, we are adding support for the `-print-diagnostic-groups` compiler option, which outputs the group name in square brackets. + +A similar behavior already exists in the compiler and is enabled by the `-debug-diagnostic-names` option, but it prints the internal diagnostic identifiers used in the compiler. For example: +```swift +@available(iOS, deprecated: 10.0, renamed: "newFunction") +func oldFunction() { ... } + +oldFunction() +``` +When compiled with the `-debug-diagnostic-names` option, the following message will be displayed: +``` +'oldFunction()' is deprecated: renamed to 'newFunction' [availability_deprecated_rename] +``` +The string `availability_deprecated_rename` is the internal identifier of this warning, not the group. Accordingly, it is not supported by the new compiler options. + +When compiling the same code with the `-print-diagnostic-groups` option, the following message will be displayed: +``` +'oldFunction()' is deprecated: renamed to 'newFunction' [availability_deprecated] +``` +Here, the string `availability_deprecated` is the diagnostic group. + +Often, group names and internal diagnostic identifiers coincide, but this is not always the case. + +We retain support for `-debug-diagnostic-names` in its current form. However, to avoid confusion between diagnostic IDs and diagnostic groups, we prohibit the simultaneous use of these two options. + +## Source compatibility + +This proposal has no effect on source compatibility. + +## ABI compatibility + +This proposal has no effect on ABI compatibility. + +## Implications on adoption + +The adoption of diagnostic groups and the new compiler options will provide a foundation for flexible and precise control over warning behavior. However, to make this useful to end-users, significant work will be needed to mark existing diagnostics in diagnostic groups. It will also be necessary to develop a process for maintaining the relevance of diagnostic groups when new diagnostics are introduced in the compiler. + +## Future directions + +### Support in the language + +While diagnostic groups are introduced to support the compiler options, it may be possible in the future to standardize the structure of the group graph itself. This could open up the possibility of using these same identifiers in the language, implementing something analogous to `#pragma diagnostic` or `[[attribute]]` in C++. However, such standardization and the design of new constructs in the language go far beyond the scope of this proposal, and we need to gain more experience with diagnostic groups before proceeding with this. + +### Support in SwiftPM + +If this proposal is accepted, it would make sense to support these parameters in SwiftPM as well, allowing the behavior of warnings to be conveniently specified in SwiftSetting. + +## Alternatives considered + +### Alternatives to diagnostic groups +#### Status quo +The lack of control over the behavior of specific diagnostics forces users to abandon the `-warnings-as-errors` compiler option and create ad-hoc compiler wrappers that filter its output. + +#### Using existing diagnostic identifiers +Warnings and errors in Swift can change as the compiler evolves. +For example, one error might be renamed or split into two that are applied in different situations to improve the clarity of the text message depending on the context. Such a change would result in a new ID for the new error variant. + +The example of `availability_deprecated_same_module` illustrates this well. If we used the warning ID, the behavior of the compiler with the `-Wwarning availability_deprecated` option would change when a new version of the warning is introduced, as this warning would no longer be triggered for the specific case of the same module. + +Therefore, we need a solution that allows us to modify errors and warnings within the compiler while providing a reliable mechanism for identifying diagnostics that can be used by the user. + +#### Flat list instead of a graph + +To solve this problem, we could use an additional alias-ID for diagnostics that does not change when the main identifier changes. + +Suppose we split the `availability_deprecated` diagnostic into a generic variant and `availability_deprecated_same_module`. To retain the existing name for the new variant, we could describe these two groups as +``` +availability_deprecated (alias: availability_deprecated) +availability_deprecated_same_module (alias: availability_deprecated) +``` +However, this solution would not allow specifying the narrower `availability_deprecated_same_module` or the broader group `deprecated`. + +#### Using multiple alias IDs for diagnostics +To express a diagnostic's membership in multiple groups, we could allow multiple alias-IDs to be listed. +``` +availability_deprecated aliases: + availability_deprecated + deprecated +availability_deprecated_same_module aliases: + availability_deprecated_same_module + availability_deprecated + deprecated +``` +However, such a declaration lacks structure and makes it difficult to understand which alias-ID is the most specific. + +### Alternative names for the compiler options + +During the design process, other names for the compiler options were considered, which were formed as the singular form of the existing ones: +| Plural | Singular | +|--------------------------|--------------------------------| +| `-warnings-as-errors` | `-warning-as-error ` | +| `-no-warnings-as-errors` | `-no-warning-as-error ` | +| `-suppress-warnings` | `-suppress-warning ` | + +However, with this naming, the combination `-suppress-warning deprecated -no-warning-as-error availability_deprecated` might be misleading. + +In Clang, diagnostic behavior is controlled through `-W...` options, but the format suffers from inconsistency. We adopt the `-W` prefix while making the format consistent. +| Clang | Swift | +|-------------------|----------------------| +| `-W` | `-Wwarning ` | +| `-Wno-` | `-Wsuppress ` | +| `-Werror=` | `-Werror ` | + +Additionally, the option name `-Wwarning` is much better suited when it comes to enabling suppressed-by-default warnings. Today we have several of them behind dedicated flags like `-driver-warn-unused-options` and `-warn-concurrency`. It might be worth having a common infrastructure for warnings that are suppressed by default. + +### Alternative format for `-print-diagnostic-groups` + +Theoretically, we could allow the simultaneous use of `-debug-diagnostic-names` and `-print-diagnostic-groups`, but this would require choosing a different format for printing diagnostic groups. + +Since `-debug-diagnostic-names` has been available in the compiler for a long time, we proceed from the fact that there are people who rely on this option and its format with square brackets. + +To avoid overlap, we would need to use a different format, for example: +``` +'foo()' is deprecated [availability_deprecated] [group:availability_deprecated] +``` + +However, even this does not eliminate the possibility of breaking code that parses the compiler's output. + +Moreover, `-print-diagnostic-groups` provides a formalized version of the same functionality using identifiers suitable for user use. And thus it should supersede the usages of `-debug-diagnostic-names`. Therefore, we believe the best solution would be to use the same format for `-print-diagnostic-groups` and prohibit the simultaneous use of these two options. + From c8dbf85741736e07a2b0ad2662de0a7066cec93c Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 19 Aug 2024 10:31:18 -0700 Subject: [PATCH 3823/4563] Format bullets with * to match elsewhere (#2548) See also commit b36c5687 (Use * for bullet points in the metadata list, 2023-10-12) which makes the same change, for the same reason. References: b36c56879267688ac8a81185dee2599e3c3a1fda --- proposals/0439-trailing-comma-lists.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/0439-trailing-comma-lists.md b/proposals/0439-trailing-comma-lists.md index c5694e86e8..e6b0bed712 100644 --- a/proposals/0439-trailing-comma-lists.md +++ b/proposals/0439-trailing-comma-lists.md @@ -1,11 +1,11 @@ # Allow trailing comma in comma-separated lists -- Proposal: [SE-0439](0439-trailing-comma-lists.md) -- Author: [Mateus Rodrigues](https://github.com/mateusrodriguesxyz) -- Review Manager: [Xiaodi Wu](https://github.com/xwu) -- Status: **Accepted with modifications** -- Implementation: https://github.com/swiftlang/swift/pull/74522# gated behind `-enable-experimental-feature TrailingComma` -- Review: ([pitch](https://forums.swift.org/t/pitch-allow-trailing-comma-in-tuples-arguments-and-if-guard-while-conditions/70170)), ([review](https://forums.swift.org/t/se-0439-allow-trailing-comma-in-comma-separated-lists/72876)), ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0439-allow-trailing-comma-in-comma-separated-lists/73216)) +* Proposal: [SE-0439](0439-trailing-comma-lists.md) +* Author: [Mateus Rodrigues](https://github.com/mateusrodriguesxyz) +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Accepted with modifications** +* Implementation: https://github.com/swiftlang/swift/pull/74522# gated behind `-enable-experimental-feature TrailingComma` +* Review: ([pitch](https://forums.swift.org/t/pitch-allow-trailing-comma-in-tuples-arguments-and-if-guard-while-conditions/70170)), ([review](https://forums.swift.org/t/se-0439-allow-trailing-comma-in-comma-separated-lists/72876)), ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0439-allow-trailing-comma-in-comma-separated-lists/73216)) ## Introduction From d0e50d90a107e70ffc9a2a57ce182d82244bd642 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Mon, 19 Aug 2024 20:01:43 +0200 Subject: [PATCH 3824/4563] [Proposals] fix typos in 90+ proposals --- ...-api-guidelines-to-the-standard-library.md | 2 +- ...0018-flexible-memberwise-initialization.md | 32 +++++++++---------- .../0026-abstract-classes-and-methods.md | 4 +-- proposals/0058-objectivecbridgeable.md | 4 +-- proposals/0073-noescape-once.md | 2 +- proposals/0075-import-test.md | 2 +- proposals/0082-swiftpm-package-edit.md | 2 +- proposals/0088-libdispatch-for-swift3.md | 2 +- proposals/0099-conditionclauses.md | 2 +- proposals/0106-rename-osx-to-macos.md | 2 +- proposals/0107-unsaferawpointer.md | 2 +- proposals/0108-remove-assoctype-inference.md | 2 +- proposals/0119-extensions-access-modifiers.md | 6 ++-- proposals/0138-unsaferawbufferpointer.md | 6 ++-- .../0145-package-manager-version-pinning.md | 2 +- proposals/0147-move-unsafe-initialize-from.md | 2 +- proposals/0156-subclass-existentials.md | 2 +- ...2-package-manager-custom-target-layouts.md | 2 +- proposals/0172-one-sided-ranges.md | 4 +-- proposals/0173-swap-indices.md | 2 +- proposals/0174-filter-range-replaceable.md | 2 +- ...0176-enforce-exclusive-access-to-memory.md | 2 +- proposals/0180-string-index-overhaul.md | 2 +- proposals/0183-substring-affordances.md | 2 +- ...ross-module-inlining-and-specialization.md | 2 +- proposals/0196-diagnostic-directives.md | 2 +- .../0198-playground-quicklook-api-revamp.md | 2 +- proposals/0207-containsOnly.md | 2 +- ...ckage-manager-swift-lang-version-update.md | 2 +- .../0223-array-uninitialized-initializer.md | 2 +- ...28-fix-expressiblebystringinterpolation.md | 2 +- proposals/0231-optional-iteration.md | 4 +-- proposals/0235-add-result.md | 2 +- proposals/0240-ordered-collection-diffing.md | 4 +-- .../0243-codepoint-and-character-literals.md | 2 +- proposals/0244-opaque-result-types.md | 2 +- .../0245-array-uninitialized-initializer.md | 2 +- proposals/0246-mathable.md | 2 +- proposals/0253-callable.md | 2 +- proposals/0254-static-subscripts.md | 2 +- proposals/0259-approximately-equal.md | 2 +- proposals/0260-library-evolution.md | 2 +- proposals/0261-identifiable.md | 2 +- ...nd-punctuations-for-escaped-identifiers.md | 4 +-- proposals/0290-negative-availability.md | 4 +-- ...thesis-for-enums-with-associated-values.md | 2 +- proposals/0296-async-await.md | 2 +- proposals/0298-asyncsequence.md | 2 +- .../0303-swiftpm-extensible-build-tools.md | 8 ++--- proposals/0306-actors.md | 2 +- ...ock-existential-types-for-all-protocols.md | 4 +-- .../0310-effectful-readonly-properties.md | 2 +- proposals/0311-task-locals.md | 8 ++--- proposals/0313-actor-isolation-control.md | 2 +- proposals/0317-async-let.md | 2 +- proposals/0318-package-creation.md | 10 +++--- proposals/0319-never-identifiable.md | 2 +- .../0325-swiftpm-additional-plugin-apis.md | 6 ++-- proposals/0332-swiftpm-command-plugins.md | 2 +- ...ental-migration-to-concurrency-checking.md | 2 +- ...aries-by-default-on-supported-platforms.md | 2 +- proposals/0345-if-let-shorthand.md | 6 ++-- .../0347-type-inference-from-default-exprs.md | 2 +- proposals/0348-buildpartialblock.md | 2 +- proposals/0349-unaligned-loads-and-stores.md | 2 +- proposals/0351-regex-builder.md | 8 ++--- proposals/0352-implicit-open-existentials.md | 2 +- proposals/0354-regex-literals.md | 4 +-- proposals/0356-swift-snippets.md | 2 +- ...0357-regex-string-processing-algorithms.md | 8 ++--- .../0363-unicode-for-string-processing.md | 4 +-- proposals/0367-conditional-attributes.md | 2 +- proposals/0376-function-back-deployment.md | 2 +- proposals/0381-task-group-discard-results.md | 4 +-- ...-declared-objc-interfaces-and-protocols.md | 2 +- proposals/0389-attached-macros.md | 2 +- proposals/0392-custom-actor-executors.md | 30 ++++++++--------- proposals/0400-init-accessors.md | 2 +- proposals/0402-extension-macros.md | 2 +- .../0403-swiftpm-mixed-language-targets.md | 6 ++-- proposals/0406-async-stream-backpressure.md | 2 +- proposals/0410-atomics.md | 8 ++--- proposals/0413-typed-throws.md | 4 +-- proposals/0414-region-based-isolation.md | 4 +-- proposals/0415-function-body-macros.md | 2 +- proposals/0417-task-executor-preference.md | 2 +- proposals/0421-generalize-async-sequence.md | 4 +-- proposals/0423-dynamic-actor-isolation.md | 2 +- ...m-isolation-checking-for-serialexecutor.md | 2 +- proposals/0426-bitwise-copyable.md | 2 +- ...428-resolve-distributed-actor-protocols.md | 2 +- proposals/0433-mutex.md | 6 ++-- proposals/0438-metatype-keypath.md | 2 +- proposals/0440-debug-description-macro.md | 2 +- ...oup-childtaskresult-type-to-be-inferred.md | 2 +- 95 files changed, 169 insertions(+), 169 deletions(-) diff --git a/proposals/0006-apply-api-guidelines-to-the-standard-library.md b/proposals/0006-apply-api-guidelines-to-the-standard-library.md index 2b78b786bd..76349a2287 100644 --- a/proposals/0006-apply-api-guidelines-to-the-standard-library.md +++ b/proposals/0006-apply-api-guidelines-to-the-standard-library.md @@ -1144,7 +1144,7 @@ public struct OpaquePointer : ... { - allowedCharacters: NSCharacterSet - ) -> String? + public func addingPercentEncoding( -+ withAllowedCharaters allowedCharacters: NSCharacterSet ++ withAllowedCharacters allowedCharacters: NSCharacterSet + ) -> String? - public func stringByAddingPercentEscapesUsingEncoding( diff --git a/proposals/0018-flexible-memberwise-initialization.md b/proposals/0018-flexible-memberwise-initialization.md index 9b1646ff2f..2bddbf171c 100644 --- a/proposals/0018-flexible-memberwise-initialization.md +++ b/proposals/0018-flexible-memberwise-initialization.md @@ -20,7 +20,7 @@ When designing initializers for a type we are currently faced with the unfortuna Underlying this problem is the fact that initialization scales with M x N complexity (M members, N initializers). We need as much help from the compiler as we can get! -Flexible and concise initialization for both type authors and consumers will encourages using immutability where possible and removes the need for boilerplate from the concerns one must consider when designing the intializers for a type. +Flexible and concise initialization for both type authors and consumers will encourages using immutability where possible and removes the need for boilerplate from the concerns one must consider when designing the initializers for a type. Quoting [Chris Lattner](https://forums.swift.org/t/proposal-helpers-for-initializing-properties-of-same-name-as-parameters/129/8): @@ -31,9 +31,9 @@ Quoting [Chris Lattner](https://forums.swift.org/t/proposal-helpers-for-initiali 4) var properties with default initializers should have their parameter to the synthesized initializer defaulted. 5) lazy properties with memberwise initializers have problems (the memberwise init eagerly touches it). -Add to the list “all or nothing”. The compiler generates the entire initializer and does not help to eliminate boilerplate for any other initializers where it may be desirable to use memberwise intialization for a subset of members and initialize others manually. +Add to the list “all or nothing”. The compiler generates the entire initializer and does not help to eliminate boilerplate for any other initializers where it may be desirable to use memberwise initialization for a subset of members and initialize others manually. -It is common to have a type with a number of public members that are intended to be configured by clients, but also with some private state comprising implementation details of the type. This is especially prevalent in UI code which may expose many properties for configuring visual appearance, etc. Flexibile memberwise initialization can provide great benefit in these use cases, but it immediately becomes useless if it is "all or nothing". +It is common to have a type with a number of public members that are intended to be configured by clients, but also with some private state comprising implementation details of the type. This is especially prevalent in UI code which may expose many properties for configuring visual appearance, etc. Flexible memberwise initialization can provide great benefit in these use cases, but it immediately becomes useless if it is "all or nothing". We need a flexible solution that can synthesize memberwise initialization for some members while allowing the type author full control over initialization of implementation details. @@ -47,7 +47,7 @@ The two approaches are not mutually exclusive: it is possible to use the *automa The *automatic* model of the current proposal determines the set of properties that receive memberwise initialization parameters by considering *only* the initializer declaration and the declarations for all properties that are *at least* as visible as the initializer (including any behaviors attached to the properties). The rules are as follows: -1. The access level of the property is *at least* as visible as the memberwise initializer. The visiblity of the **setter** is used for `var` properties. +1. The access level of the property is *at least* as visible as the memberwise initializer. The visibility of the **setter** is used for `var` properties. 2. They do not have a behavior which prohibits memberwise initialization (e.g. the 'lazy' behavior). 3. If the property is a `let` property it *may not* have an initial value. @@ -192,7 +192,7 @@ Throughout this design the term **memberwise initialization parameter** is used 1. Determine the set of properties eligible for memberwise initialization synthesis. Properties are eligible for memberwise initialization synthesis if: - 1. The access level of the property is *at least* as visible as the memberwise initializer. The visiblity of the **setter** is used for `var` properties. + 1. The access level of the property is *at least* as visible as the memberwise initializer. The visibility of the **setter** is used for `var` properties. 2. They do not have a behavior which prohibits memberwise initialization. 3. If the property is a `let` property it *may not* have an initial value. @@ -216,7 +216,7 @@ This proposal will also support generating an *implicit* memberwise initializer 2. The type is: 1. a struct 2. a root class - 3. a class whose superclass has a designated intializer requiring no arguments + 3. a class whose superclass has a designated initializer requiring no arguments The implicitly generated memberwise initializer will have the highest access level possible while still allowing all stored properties to be eligible for memberwise parameter synthesis, but will have at most `internal` visibility. Currently this means its visibility will be `internal` when all stored properties of the type have setters with *at least* `internal` visibility, and `private` otherwise (when one or more stored properties are `private` or `private(set)`). @@ -228,7 +228,7 @@ The changes described in this proposal are *almost* entirely additive. The only 1. If the implicitly synthesized memberwise initializer was only used *within* the same source file no change is necessary. An implicit `private` memberwise initializer will still be synthesized by the compiler. 2. A mechanical migration could generate the explicit code necessary to declare the previously implicit initializer. This would be an `internal` memberwise initializer with *explicit* parameters used to manually initialize the stored properties with `private` setters. -3. If the "Access control for init" enhancement were accepted the `private` members could have their access control modified to `private internal(init)` which would allow the implicit memberwise intializer to continue to have `internal` visibility as all stored properties would be eligible for parameter synthesis by an `internal` memberwise initializer. +3. If the "Access control for init" enhancement were accepted the `private` members could have their access control modified to `private internal(init)` which would allow the implicit memberwise initializer to continue to have `internal` visibility as all stored properties would be eligible for parameter synthesis by an `internal` memberwise initializer. The only other impact on existing code is that memberwise parameters corresponding to `var` properties with initial values will now have default values. This will be a change in the behavior of the implicit memberwise initializer but will not break any code. The change will simply allow new code to use that initializer without providing an argument for such parameters. @@ -293,7 +293,7 @@ The rules of the current proposal are designed to synthesize memberwise paramete Introducing a `memberwise` declaration modifier for properties would allow programmers to specify exactly which properties should participate in memberwise initialization synthesis. It allows full control and has the clarity afforded by being explicit. -Specifc use cases this feature would support include allowing `private` properties to receive synthesized memberwise parameters in a `public` initializer, or allow `public` properties to be omitted from parameter synthesis. +Specific use cases this feature would support include allowing `private` properties to receive synthesized memberwise parameters in a `public` initializer, or allow `public` properties to be omitted from parameter synthesis. An example of this @@ -348,7 +348,7 @@ struct S { If this enhancement were submitted the first property eligibility rule would be updates as follows: -1. Their **init** access level is *at least* as visible as the memberwise initializer. If the property does not have an **init** acccess level, the access level of its **setter** must be *at least* as visible as the memberwise initializer. +1. Their **init** access level is *at least* as visible as the memberwise initializer. If the property does not have an **init** access level, the access level of its **setter** must be *at least* as visible as the memberwise initializer. ### @nomemberwise @@ -400,7 +400,7 @@ struct S { init(s: String) { /* synthesized */ self.s = s - // body of the user's intializer remains + // body of the user's initializer remains i = 42 } } @@ -408,7 +408,7 @@ struct S { ### Memberwise initializer chaining / parameter forwarding -Ideally it would be possible to define convenience and delegating initializers without requiring them to manually declare parameters and pass arguments to the designated initializer for memberwise intialized properties. It would also be ideal if designated initializers also did not have to the same for memberwise intialization parmaeters of super. +Ideally it would be possible to define convenience and delegating initializers without requiring them to manually declare parameters and pass arguments to the designated initializer for memberwise initialized properties. It would also be ideal if designated initializers also did not have to the same for memberwise initialization parameters of super. A general solution for parameter forwarding would solve this problem. A future parameter forwarding proposal to support this use case and others is likely to be pursued. @@ -435,7 +435,7 @@ Obviously supporting memberwise initialization with Cocoa classes would require This is a reasonable option and and I expect a healthy debate about which default is better. The decision to adopt the *automatic* model by default was made for several reasons: 1. The memberwise initializer for structs does not currently require an annotation for properties to opt-in. Requiring an annotation for a mechanism designed to supersede that mechanism may be viewed as boilerplate. -2. Stored properties with public visibility are often intialized directly with a value provided by the caller. +2. Stored properties with public visibility are often initialized directly with a value provided by the caller. 3. Stored properties with **less visibility** than a memberwise initializer are not eligible for memberwise initialization. No annotation is required to indicate that and it is usually not desired. 4. The *automatic* model cannot exist unless it is the default. The *opt-in* model can exist alongside the *automatic* model and itself be opted-into simply by specifying the `memberwise` declaration modifier on one or more properties. @@ -462,7 +462,7 @@ Reasons to limit memberwise parameter synthesis to members which are *at least* 5. If a proposal for `@nomemberwise` is put forward and adopted that would allow us to prevent synthesis of parameters for members as desired. Unfortunately `@nomemberwise` would need to be used much more heavily than it otherwise would (i.e. to prevent synthesis of memberwise parameters for more-private members). It would be better if `@nomemberwise` was not necessary most of the time. 6. If callers must be able to provide memberwise arguments for more-private members directly it is still possible to allow that while taking advantage of memberwise initialization for same-or-less-private members. You just need to declare a `memberwise init` with explicitly declared parameters for the more-private members and initialize them manually in the body. If the "Access control for init" enhancement is accepted another option would be upgrading the visibility of `init` for the more-private member while retaining its access level for the getter and setter. Requiring the programmer to explicitly expose a more-private member either via `init` access control or by writing code that it directly is arguably a very good thing. -Reasons we might want to allow memberwise parameter synthesis for members with lower visiblity than the initializer: +Reasons we might want to allow memberwise parameter synthesis for members with lower visibility than the initializer: 1. Not doing so puts tension between access control for stored properties and memberwise inits. You have to choose between narrower access control or getting the benefit of a memberwise init. Another way to say it: this design means that narrow access control leads to boilerplate. @@ -470,7 +470,7 @@ NOTE: The tension mentioned here is lessened by #6 above: memberwise initializat ### Require initializers to explicitly specify memberwise initialization parameters -The thread "[helpers for initializing properties of the same name as parameters](https://forums.swift.org/t/proposal-helpers-for-initializing-properties-of-same-name-as-parameters/129/3)" discussed an idea for synthesizing property initialization in the body of the initializer while requiring the parameters to be declard explicitly. +The thread "[helpers for initializing properties of the same name as parameters](https://forums.swift.org/t/proposal-helpers-for-initializing-properties-of-same-name-as-parameters/129/3)" discussed an idea for synthesizing property initialization in the body of the initializer while requiring the parameters to be declared explicitly. ```swift struct Foo { @@ -494,7 +494,7 @@ Under the current proposal full control is still available. It requires initial I believe the `memberwise` declaration modifier on the initializer and the placeholder in the parameter list make it clear that the compiler will synthesize additional parameters. Furthermore, IDEs and generated documentation will contain the full, synthesized signature of the initializer. -Finally, this idea is not mutually exclusive with the current proposal. It could even work in the declaration of a memberwise initializer, so long the corresponding property was made ineligible for memberwise intialization synthesis. +Finally, this idea is not mutually exclusive with the current proposal. It could even work in the declaration of a memberwise initializer, so long the corresponding property was made ineligible for memberwise initialization synthesis. ### Adopt "type parameter list" syntax like Kotlin and Scala @@ -540,7 +540,7 @@ Responses to these points follow: 1. If the expansion of this syntax does not supply initial values to the synthesized properties and only uses the default value for parameters of the synthesized initializer this is true. The downside of doing this is that `var` properties no longer have an initial value which may be desirable if you write additional initializers for the type. I believe we should continue the discussion about default values for `let` properties. Ideally we can find an acceptable solution that will work with the current proposal, as well as any additional syntactic sugar we add in the future. -2. I don't believe allowing parameter labels for memberwise initialization parameters is a good idea. Callers are directly initializing a property and are best served by a label that matches the name of the property. If you really need to provide a different name you can still do so by writing your initializer manually. With future enhancements to the current proposal you may be able to use memberwise intialization for properties that do not require a custom label while manually initialzing properties that do need one. +2. I don't believe allowing parameter labels for memberwise initialization parameters is a good idea. Callers are directly initializing a property and are best served by a label that matches the name of the property. If you really need to provide a different name you can still do so by writing your initializer manually. With future enhancements to the current proposal you may be able to use memberwise initialization for properties that do not require a custom label while manually initializing properties that do need one. 3. The Scala / Kotlin syntax is indeed more concise in some cases, but not in all cases. Under this proposal the example given above is actually more concise than it is with that syntax: ```swift diff --git a/proposals/0026-abstract-classes-and-methods.md b/proposals/0026-abstract-classes-and-methods.md index c86badb14a..24b89a5eb9 100644 --- a/proposals/0026-abstract-classes-and-methods.md +++ b/proposals/0026-abstract-classes-and-methods.md @@ -16,7 +16,7 @@ in inherited class like protocols. ## Motivation -like pure virtual methods in C++ and abtract classes in Java and C#, frameworks development +like pure virtual methods in C++ and abstract classes in Java and C#, frameworks development sometimes required abstract classes facility. An abstract class is like a regular class, but some methods/properties are not implemented and must be implemented in one of inherited classes. @@ -97,7 +97,7 @@ class MyRestServiceClient : RESTClient { ``` ## Detailed design -An abstract class cannot be instanciated. +An abstract class cannot be instantiated. Abstract method/property cannot have implementation. diff --git a/proposals/0058-objectivecbridgeable.md b/proposals/0058-objectivecbridgeable.md index 6461ecec58..478a23adf4 100644 --- a/proposals/0058-objectivecbridgeable.md +++ b/proposals/0058-objectivecbridgeable.md @@ -77,7 +77,7 @@ public protocol ObjectiveCBridgeable { /// Objective-C thunk or when calling Objective-C code. /// /// - note: This initializer should eagerly perform the - /// conversion without defering any work for later, + /// conversion without deferring any work for later, /// returning `nil` if the conversion fails. init?(bridgedFromObjectiveC: ObjectiveCType) @@ -144,7 +144,7 @@ The compiler generates automatic thunks only when there is no ambiguity, while e 3. Bridged collection types will still observe the protocol conformance if cast to a Swift type (eg: `NSArray as? [Int]` will call the `ObjectiveCBridgeable` implementation on `Array`, which itself will call the implementation on `Int` for the elements) 2. A Swift type may bridge to an Objective-C base class then provide different subclass instances at runtime, but no other Swift type may bridge to that base class or any of its subclasses. 1. The compiler should emit a diagnostic when it detects two Swift types attempting to bridge to the same `ObjectiveCType`. -3. An exception to these rules exists for trivially convertable built-in types like `NSInteger` <--> `Int` when specified outside of a bridged collection type. In those cases the compiler will continue the existing behavior, bypassing the `ObjectiveCBridgeable` protocol. The effect is that types like `Int` will not bridge to `NSNumber` unless contained inside a collection type (see `BuiltInBridgeable below`). +3. An exception to these rules exists for trivially convertible built-in types like `NSInteger` <--> `Int` when specified outside of a bridged collection type. In those cases the compiler will continue the existing behavior, bypassing the `ObjectiveCBridgeable` protocol. The effect is that types like `Int` will not bridge to `NSNumber` unless contained inside a collection type (see `BuiltInBridgeable below`). ### Resiliance diff --git a/proposals/0073-noescape-once.md b/proposals/0073-noescape-once.md index c541f9afd1..793f313095 100644 --- a/proposals/0073-noescape-once.md +++ b/proposals/0073-noescape-once.md @@ -128,7 +128,7 @@ expected. ## Not requiring exactly one execution Assuming that the main goal of this proposal is to relax initialization -requirements, a unique invocation of the closure is not stricly required. +requirements, a unique invocation of the closure is not strictly required. However the requirement of unique invocation makes the proposal simpler to understand. diff --git a/proposals/0075-import-test.md b/proposals/0075-import-test.md index 298e4dcde9..7844243832 100644 --- a/proposals/0075-import-test.md +++ b/proposals/0075-import-test.md @@ -33,7 +33,7 @@ Swift's existing set of build configurations specify platform differences, not m #endif ``` -Guarding code with operating system tests can be less future-proofed than testing for module support. Excluding OS X to use UIColor creates code that might eventually find its way to a Linux plaform. Targeting Apple platforms by inverting a test for Linux essentially broke after the introduction of `Windows` and `FreeBSD` build configurations: +Guarding code with operating system tests can be less future-proofed than testing for module support. Excluding OS X to use UIColor creates code that might eventually find its way to a Linux platform. Targeting Apple platforms by inverting a test for Linux essentially broke after the introduction of `Windows` and `FreeBSD` build configurations: ```swift // Exclusive os tests are brittle diff --git a/proposals/0082-swiftpm-package-edit.md b/proposals/0082-swiftpm-package-edit.md index ff458171b2..5e3e24bfed 100644 --- a/proposals/0082-swiftpm-package-edit.md +++ b/proposals/0082-swiftpm-package-edit.md @@ -93,7 +93,7 @@ This solution is intended to directly address the desired behaviors of the package manager: * By hiding the sources by default, we minimize the distractions in the common - case where a user is programming against a known, well-establised, library + case where a user is programming against a known, well-established, library they do not need to modify. * By adding a new, explicit workflow for switching to an "editable" package, we diff --git a/proposals/0088-libdispatch-for-swift3.md b/proposals/0088-libdispatch-for-swift3.md index 8f17fd830b..19bfd9d3bc 100644 --- a/proposals/0088-libdispatch-for-swift3.md +++ b/proposals/0088-libdispatch-for-swift3.md @@ -259,7 +259,7 @@ struct DispatchData : RandomAccessCollection, _ObjectiveCBridgeable { } ``` -This proposal will introduce new accessor methods to access the bytes in a Data object. Along with becoming iteratable, several methods will be introduced that replace the ```dispatch_data_create_map``` approach used in C: +This proposal will introduce new accessor methods to access the bytes in a Data object. Along with becoming iterable, several methods will be introduced that replace the ```dispatch_data_create_map``` approach used in C: ```swift struct DispatchData : RandomAccessCollection, _ObjectiveCBridgeable { diff --git a/proposals/0099-conditionclauses.md b/proposals/0099-conditionclauses.md index 902b0258de..95ca694357 100644 --- a/proposals/0099-conditionclauses.md +++ b/proposals/0099-conditionclauses.md @@ -9,7 +9,7 @@ ## Introduction -Swift condition clauses appear in `guard`, `if`, and `while` statements. This proposal re-architects the condition grammar to enable an arbitrary mix of Boolean expressions, `let` conditions (which test and unwrap optionals), general `case` clauses for arbitrary pattern matching, and availability tests. It removes `where` clauses from optional binding conditions and case conditions, and eliminates gramatical ambiguity by using commas for separation between clauses instead of using them both to separate clauses and terms within each clause. These modifications streamline Swift's syntax and alleviate the situation where many Swift developers don't know they can use arbitrary Boolean conditions after a value binding. +Swift condition clauses appear in `guard`, `if`, and `while` statements. This proposal re-architects the condition grammar to enable an arbitrary mix of Boolean expressions, `let` conditions (which test and unwrap optionals), general `case` clauses for arbitrary pattern matching, and availability tests. It removes `where` clauses from optional binding conditions and case conditions, and eliminates grammatical ambiguity by using commas for separation between clauses instead of using them both to separate clauses and terms within each clause. These modifications streamline Swift's syntax and alleviate the situation where many Swift developers don't know they can use arbitrary Boolean conditions after a value binding. Swift-evolution thread: [\[Pitch\] making where and , interchangeable in guard conditions](https://forums.swift.org/t/pitch-making-where-and-interchangeable-in-guard-conditions/2702) diff --git a/proposals/0106-rename-osx-to-macos.md b/proposals/0106-rename-osx-to-macos.md index b43afd0446..e5f4714024 100644 --- a/proposals/0106-rename-osx-to-macos.md +++ b/proposals/0106-rename-osx-to-macos.md @@ -73,7 +73,7 @@ This proposal is purely additive. It will not affect existing code other than ad Instead of retaining and aliasing `os(OSX)`, it can be fully replaced by `os(macOS)`. This mirrors the situation with the phoneOS to iOS rename and would require a migration assistant to fixit old-style use. -Charlie Monroe points out: "Since Swift 3.0 is a code-breaking change my guess is that there is no burden if the Xcode migration assistent automatically changes all `#if os(OSX)` to `#if os(macOS)`, thus deprecating the term OSX, not burdening the developer at all. If iOS was renamed to phoneOS and kept versioning, you'd still expect `#if os(iOS)` to be matched when targeting phoneOS and vice-versa." +Charlie Monroe points out: "Since Swift 3.0 is a code-breaking change my guess is that there is no burden if the Xcode migration assistant automatically changes all `#if os(OSX)` to `#if os(macOS)`, thus deprecating the term OSX, not burdening the developer at all. If iOS was renamed to phoneOS and kept versioning, you'd still expect `#if os(iOS)` to be matched when targeting phoneOS and vice-versa." ## Unaddressed Issues diff --git a/proposals/0107-unsaferawpointer.md b/proposals/0107-unsaferawpointer.md index cab2021cf8..6acefab32f 100644 --- a/proposals/0107-unsaferawpointer.md +++ b/proposals/0107-unsaferawpointer.md @@ -160,7 +160,7 @@ value argument could result in miscompilation if the inferred type ever deviates from the user's original expectations. The type parameter also importantly conveys that the raw memory becomes accessible via a pointer to that type at the point of the call. The -type should be explicitly spelled at this point because accesing the +type should be explicitly spelled at this point because accessing the memory via a typed pointer of an unrelated type could also result in miscompilation. diff --git a/proposals/0108-remove-assoctype-inference.md b/proposals/0108-remove-assoctype-inference.md index 77357b6918..b1373d8a75 100644 --- a/proposals/0108-remove-assoctype-inference.md +++ b/proposals/0108-remove-assoctype-inference.md @@ -165,7 +165,7 @@ There are some advantages to this approach. Brevity is slightly improved. A type As well, Dave Abrahams expresses a [potential issue](https://forums.swift.org/t/pitch-remove-type-inference-for-associated-types/3135/17): -> Finally, I am very concerned that there are protocols such as `Collection`, with many inferrable associated types, and that conforming to these protocols could become *much* uglier. +> Finally, I am very concerned that there are protocols such as `Collection`, with many inferable associated types, and that conforming to these protocols could become *much* uglier. As with many proposals, there is a tradeoff between the status quo and the proposed behavior. As *Completing Generics* puts it, diff --git a/proposals/0119-extensions-access-modifiers.md b/proposals/0119-extensions-access-modifiers.md index 7a9728399d..9112147c9a 100644 --- a/proposals/0119-extensions-access-modifiers.md +++ b/proposals/0119-extensions-access-modifiers.md @@ -56,7 +56,7 @@ This simple access control model also allows us to nest types inside each other *Extensions* however behave differently when it comes to their access control: -* The *access modifier* of an *extension* sets the default modifier of its members which do not have their own localy defined modifier. +* The *access modifier* of an *extension* sets the default modifier of its members which do not have their own locally defined modifier. ```swift public struct D {} @@ -234,7 +234,7 @@ I propose to revise the access control on extensions by removing access modifier fileprivate group { - // Every group memebr is `fileprivate` + // Every group member is `fileprivate` func member1() {} func member2() {} func member3() {} @@ -286,7 +286,7 @@ Iff the *access-level-modifier* is not present, the access modifier on extension ```diff - extension SomeType : SomeProtocol { + public extension SomeType : SomeProtocol { - public func someMemeber() + public func someMember() } ``` diff --git a/proposals/0138-unsaferawbufferpointer.md b/proposals/0138-unsaferawbufferpointer.md index 74f3c7b0a5..b425d8356a 100644 --- a/proposals/0138-unsaferawbufferpointer.md +++ b/proposals/0138-unsaferawbufferpointer.md @@ -32,7 +32,7 @@ for binding memory to a type for subsequent normal typed access. However, migration is not always straightforward because SE-0107 provided only minimal support for raw pointers. Extending raw pointer support to the `UnsafeBufferPointer` type will fill in this -funcionality gap. This is especially important for code that currently +functionality gap. This is especially important for code that currently views "raw" bytes of memory as `UnsafeBufferPointer`. Converting between `UInt8` and the client's element type at every API transition is difficult to do @@ -69,7 +69,7 @@ is natural for the same type that encapsulates a raw pointer and length to also allow clients to view that memory as raw bytes without the need to explicitly bind the memory type each time memory is accessed. This would also improve performance in some cases that I've -encoutered by avoiding array copies. Let's call this new type +encountered by avoiding array copies. Let's call this new type `Unsafe[Mutable]RawBufferPointer`. Any array could be viewed as `UnsafeRawBufferPointer`, and that raw @@ -488,7 +488,7 @@ collection of bytes, so there's no loss in functionality: ```swift public final class BufferedOutputByteStream: OutputByteStream { // FIXME: For inmemory implementation we should be share this buffer with OutputByteStream. - // One way to do this is by allowing OuputByteStream to install external buffers. + // One way to do this is by allowing OutputByteStream to install external buffers. private var contents = [UInt8]() override final func writeImpl(_ bytes: UnsafeRawBufferPointer) { diff --git a/proposals/0145-package-manager-version-pinning.md b/proposals/0145-package-manager-version-pinning.md index 77cc29266d..6900359fa2 100644 --- a/proposals/0145-package-manager-version-pinning.md +++ b/proposals/0145-package-manager-version-pinning.md @@ -366,7 +366,7 @@ specification in the manifest (which is the "requirement"). The meaning of pin connotes this transient relationship between the pin action and the underlying dependency. -In constrast, not only does lock have the wrong connotation, but it also is a +In contrast, not only does lock have the wrong connotation, but it also is a heavily overloaded word which can lead to confusion. For example, if the package manager used POSIX file locking to prevent concurrent manipulation of packages (a feature we intend to implement), and we also referred to the pinning files as diff --git a/proposals/0147-move-unsafe-initialize-from.md b/proposals/0147-move-unsafe-initialize-from.md index b40479613e..7964d5d408 100644 --- a/proposals/0147-move-unsafe-initialize-from.md +++ b/proposals/0147-move-unsafe-initialize-from.md @@ -54,7 +54,7 @@ Therefore: - Over-allocating the destination buffer relative to `underestimatedCount` is valid and simply results in sequence underflow with potentially uninitialized buffer memory (a likely case with arrays that reserve more than they need). - The source sequence's actual count may exceed both `underestimatedCount` and the destination buffer size, resulting in sequence overflow. This is also valid and handled by returning an iterator to the uncopied elements as an overflow sequence. -A matching change should also be made to `UnsafeRawBufferPointer.initializeMemory(from:)`. The one difference is that for convenience this should return an `UnsafeMutableBufferPointer` of the (typed) intialized elements instead of an index into the raw buffer. +A matching change should also be made to `UnsafeRawBufferPointer.initializeMemory(from:)`. The one difference is that for convenience this should return an `UnsafeMutableBufferPointer` of the (typed) initialized elements instead of an index into the raw buffer. ## Detailed design diff --git a/proposals/0156-subclass-existentials.md b/proposals/0156-subclass-existentials.md index 74e685f6f4..a3107aef79 100644 --- a/proposals/0156-subclass-existentials.md +++ b/proposals/0156-subclass-existentials.md @@ -190,7 +190,7 @@ let myViewController = MyViewController() myViewController.setup(UIViewController()) ``` -The previous code continues to compile but still crashs if the Objective-C code calls a method of `UITableViewDataSource` or `UITableViewDelegate`. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as: +The previous code continues to compile but still crashes if the Objective-C code calls a method of `UITableViewDataSource` or `UITableViewDelegate`. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as: ```swift class MyViewController { diff --git a/proposals/0162-package-manager-custom-target-layouts.md b/proposals/0162-package-manager-custom-target-layouts.md index 9cce18f915..78d428e350 100644 --- a/proposals/0162-package-manager-custom-target-layouts.md +++ b/proposals/0162-package-manager-custom-target-layouts.md @@ -91,7 +91,7 @@ remember. consider upgrading this to its own type to allow per-file build settings. The new type would conform to `CustomStringConvertible`, so existing declarations would continue to work (except where the strings were - constructed programatically). + constructed programmatically). * `exclude`: This property can be used to exclude certain files and directories from being picked up as sources. Exclude paths are relative diff --git a/proposals/0172-one-sided-ranges.md b/proposals/0172-one-sided-ranges.md index 83286f50cd..0864e86a10 100644 --- a/proposals/0172-one-sided-ranges.md +++ b/proposals/0172-one-sided-ranges.md @@ -64,7 +64,7 @@ variants of `Sequence.enumerated()` when you either want them non-zero-based i.e. `zip(1..., greeting)`, or want to flip the order i.e. `zip(greeting, 0...)`. -This syntax would supercede the existing `prefix` and `suffix` operations that +This syntax would supersede the existing `prefix` and `suffix` operations that take indices, which will be deprecated in a later release. Note that the versions that take distances are not covered by this proposal, and would remain. @@ -171,7 +171,7 @@ extension MutableCollection { where R.Bound == Index { get set } } -extension RangeReplaceableColleciton { +extension RangeReplaceableCollection { public mutating func replaceSubrange( _ subrange: ${Range}, with newElements: C ) where C.Iterator.Element == Iterator.Element, R.Bound == Index diff --git a/proposals/0173-swap-indices.md b/proposals/0173-swap-indices.md index edf2cd57ed..26055065f5 100644 --- a/proposals/0173-swap-indices.md +++ b/proposals/0173-swap-indices.md @@ -82,7 +82,7 @@ protocol MutableCollection { The current `swap` is required to `fatalError` on attempts to swap an element with itself for implementation reasons. This pushes the burden to check this first onto the caller. While swapping an element with itself is often a logic -errror (for example, in a `sort` algorithm where you have a fenceposts bug), it +error (for example, in a `sort` algorithm where you have a fenceposts bug), it is occasionally a valid situation (for example, it can occur easily in an implementation of `shuffle`). This implementation removes the precondition. diff --git a/proposals/0174-filter-range-replaceable.md b/proposals/0174-filter-range-replaceable.md index 6f791b7fc2..ead1768f74 100644 --- a/proposals/0174-filter-range-replaceable.md +++ b/proposals/0174-filter-range-replaceable.md @@ -70,7 +70,7 @@ They may be be relying on an array being returned (albeit often in order to then transform it back into the original type), but this version will still be available (via the extension on `Sequence`) and will be called if forced through type context. The only code that will break is if this operation spans -multple lines: +multiple lines: ```swift // filtered used to be [Character], now String diff --git a/proposals/0176-enforce-exclusive-access-to-memory.md b/proposals/0176-enforce-exclusive-access-to-memory.md index 3deef84685..f11512ff96 100644 --- a/proposals/0176-enforce-exclusive-access-to-memory.md +++ b/proposals/0176-enforce-exclusive-access-to-memory.md @@ -740,7 +740,7 @@ automatically to avoid source-compatibility problems. ## Effect on ABI stability and resilience -In order to gain the performance and language-desing benefits of +In order to gain the performance and language-design benefits of exclusivity, we must be able to assume that it is followed faithfully in various places throughout the ABI. Therefore, exclusivity must be enforced before we commit to a stable ABI, or else we'll be stuck with diff --git a/proposals/0180-string-index-overhaul.md b/proposals/0180-string-index-overhaul.md index 1593ba1c1f..abe8de6074 100644 --- a/proposals/0180-string-index-overhaul.md +++ b/proposals/0180-string-index-overhaul.md @@ -129,7 +129,7 @@ let tagEnd = html.utf16[tagStart...].index(of: close) let tag = html[tagStart...tagEnd] ``` -A property and an intializer will be added to `String.Index`, exposing +A property and an initializer will be added to `String.Index`, exposing the offset of the index in code units (currently only UTF-16) from the beginning of the string: diff --git a/proposals/0183-substring-affordances.md b/proposals/0183-substring-affordances.md index 854ec865ef..eb83064787 100644 --- a/proposals/0183-substring-affordances.md +++ b/proposals/0183-substring-affordances.md @@ -74,7 +74,7 @@ case of `filter`). ## Effect on ABI stability -The switch from conrete to generic types needs to be made before ABI stability. +The switch from concrete to generic types needs to be made before ABI stability. ## Alternatives considered diff --git a/proposals/0193-cross-module-inlining-and-specialization.md b/proposals/0193-cross-module-inlining-and-specialization.md index 20a15b047e..bbdcc7305a 100644 --- a/proposals/0193-cross-module-inlining-and-specialization.md +++ b/proposals/0193-cross-module-inlining-and-specialization.md @@ -171,7 +171,7 @@ The closest analogue in C to `@usableFromInline` is a non-`static` function that ## Alternatives considered -One possible alterative would be to add a new compiler mode where _all_ declarations become implicitly `@inlinable`, and _all_ private and internal declarations become `@usableFromInline`. +One possible alternative would be to add a new compiler mode where _all_ declarations become implicitly `@inlinable`, and _all_ private and internal declarations become `@usableFromInline`. However, such a compilation mode would not solve the problem of delivering a stable ABI and standard library which can be deployed separately from user code. We _don't want_ all declaration bodies in the standard library to be available to the optimizer when building user code. diff --git a/proposals/0196-diagnostic-directives.md b/proposals/0196-diagnostic-directives.md index 51133c3744..2b69a98991 100644 --- a/proposals/0196-diagnostic-directives.md +++ b/proposals/0196-diagnostic-directives.md @@ -155,7 +155,7 @@ On February 1, 2018 the Core Team decided to **accept** this proposal with slight revision over the [original proposal](https://github.com/swiftlang/swift-evolution/blob/ab0c22a2340be9bfcb82e6f237752b4d959a93b7/proposals/0196-diagnostic-directives.md). The only revision over the original proposal is to change the syntax to use -`#warning()` instead of `#warning `. This fits well with +`#warning()` instead of `#warning `. This fits well with most of Swift's existing compiler directives, and was strongly supported in the [review discussion](https://forums.swift.org/t/se-0196-compiler-diagnostic-directives/8734). diff --git a/proposals/0198-playground-quicklook-api-revamp.md b/proposals/0198-playground-quicklook-api-revamp.md index 59b4d0722f..7b6608d542 100644 --- a/proposals/0198-playground-quicklook-api-revamp.md +++ b/proposals/0198-playground-quicklook-api-revamp.md @@ -261,7 +261,7 @@ support the replacement of `CustomPlaygroundQuickLookable` with `CustomPlaygroundDisplayConvertible`. Instead, we intend for Swift 4.1 to be a deprecation period for these APIs, allowing any code bases which implement `CustomPlaygroundQuickLookable` to manually switch to the new protocol. While -this migration may not be trivial programatically, it should -- in most cases -- +this migration may not be trivial programmatically, it should -- in most cases -- be fairly trivial for someone to hand-migrate to `CustomPlaygroundDisplayConvertible`. During the deprecation period, the PlaygroundLogger framework will continue to honor implementations of diff --git a/proposals/0207-containsOnly.md b/proposals/0207-containsOnly.md index df5aa065c7..c82a4d6fa2 100644 --- a/proposals/0207-containsOnly.md +++ b/proposals/0207-containsOnly.md @@ -75,4 +75,4 @@ This change is purely additive so has no API resilience consequences. Not adding it, since it can be trivially (if confusingly) composed. -Much name bikeshedding has ensued. Names considered included `containsOnly` and `all`. `all` has strong precedent in other languages, but was considered unclear at the call site (adding an argument label does not help here given trailing closures omit them). Naming it `all` suggests a renaming of `contains` to `any` would be appropriate – but this is already a fairly heavily-used term elsewhere in Swift, and is less explicit. `containsOnly` is more explicit, and echoes the existing `contains`, but is too easily misread at the use-site as “contains one instance equal to,” especially when considering empty collections. `contains(only:)` was discounted due to trailing closures dropping the argument label, rendering it indistiguishable from `contains(where:)`. +Much name bikeshedding has ensued. Names considered included `containsOnly` and `all`. `all` has strong precedent in other languages, but was considered unclear at the call site (adding an argument label does not help here given trailing closures omit them). Naming it `all` suggests a renaming of `contains` to `any` would be appropriate – but this is already a fairly heavily-used term elsewhere in Swift, and is less explicit. `containsOnly` is more explicit, and echoes the existing `contains`, but is too easily misread at the use-site as “contains one instance equal to,” especially when considering empty collections. `contains(only:)` was discounted due to trailing closures dropping the argument label, rendering it indistinguishable from `contains(where:)`. diff --git a/proposals/0209-package-manager-swift-lang-version-update.md b/proposals/0209-package-manager-swift-lang-version-update.md index def6f95c7d..78b2825f32 100644 --- a/proposals/0209-package-manager-swift-lang-version-update.md +++ b/proposals/0209-package-manager-swift-lang-version-update.md @@ -17,7 +17,7 @@ array. The Swift compiler now allows `4.2` as an accepted value of Swift version flag (`-swift-version`). The `swiftLanguageVersions` API in `Package.swift` currently -accepts an interger array and we need to update this API in order for packages +accepts an integer array and we need to update this API in order for packages to declare this language version if required. ## Proposed solution diff --git a/proposals/0223-array-uninitialized-initializer.md b/proposals/0223-array-uninitialized-initializer.md index 024899b158..cd78c92bc4 100644 --- a/proposals/0223-array-uninitialized-initializer.md +++ b/proposals/0223-array-uninitialized-initializer.md @@ -53,7 +53,7 @@ The new initializer takes a closure that operates on an `UnsafeMutableBufferPoin and an `inout` count of initialized elements. This closure has access to the uninitialized contents of the newly created array's storage, -and must set the intialized count of the array before exiting. +and must set the initialized count of the array before exiting. ```swift var myArray = Array(unsafeUninitializedCapacity: 10) { buffer, initializedCount in diff --git a/proposals/0228-fix-expressiblebystringinterpolation.md b/proposals/0228-fix-expressiblebystringinterpolation.md index 2acc16717b..701036698b 100644 --- a/proposals/0228-fix-expressiblebystringinterpolation.md +++ b/proposals/0228-fix-expressiblebystringinterpolation.md @@ -508,7 +508,7 @@ String(stringInterpolation: .literal("!")) ``` -However, this requires that conformers expose a homogenous return value, which has expressibility and/or efficiency drawbacks. The proposed approach, which is statement based, keeps this as a detail internal to the conformer. +However, this requires that conformers expose a homogeneous return value, which has expressibility and/or efficiency drawbacks. The proposed approach, which is statement based, keeps this as a detail internal to the conformer. ### Have a formal `appendInterpolation(_:)` requirement diff --git a/proposals/0231-optional-iteration.md b/proposals/0231-optional-iteration.md index cb8ac69f6d..ec8dd5f227 100644 --- a/proposals/0231-optional-iteration.md +++ b/proposals/0231-optional-iteration.md @@ -27,7 +27,7 @@ default: print() } ``` -Optional patterns bring a succint way of handling the `.some` case when optional binding is unavailable: +Optional patterns bring a succinct way of handling the `.some` case when optional binding is unavailable: ```swift for case let unwrapped? in sequence { ... } @@ -86,7 +86,7 @@ if let unwrappedArray = array { } ``` -The `?` notation here is a semantic emphasis rather than a functional unit: there is no `for!`. Syntactically marking an optional iteration is redundant, however, in constrast to `switch`, nil values are *skipped silently*. Swift strives to follow a style where silent handling of `nil` is acknowledged via the `?` sigil, distinctly reflected in optional chaining syntax. This decision was primarily based on inconsistency and potential confusion that an otherwise left without syntactic changes `for-in` loop could potentially lead to ("clarity over brevity"). +The `?` notation here is a semantic emphasis rather than a functional unit: there is no `for!`. Syntactically marking an optional iteration is redundant, however, in contrast to `switch`, nil values are *skipped silently*. Swift strives to follow a style where silent handling of `nil` is acknowledged via the `?` sigil, distinctly reflected in optional chaining syntax. This decision was primarily based on inconsistency and potential confusion that an otherwise left without syntactic changes `for-in` loop could potentially lead to ("clarity over brevity"). ``` swift for element in optionalArray { ... } // Silently handling optionals implicitly is a style that Swift prefers to eschew. diff --git a/proposals/0235-add-result.md b/proposals/0235-add-result.md index d7e7f7d733..f4b979f6f7 100644 --- a/proposals/0235-add-result.md +++ b/proposals/0235-add-result.md @@ -293,7 +293,7 @@ This proposal adds a type to the standard library and so will affect the ABI onc ## Effect on API resilience -Addition of `Result` should be future proof against additional needs surrounding error handling. +Addition of `Result` should be future proof against additional needs surrounding error handling. ## Alternatives considered diff --git a/proposals/0240-ordered-collection-diffing.md b/proposals/0240-ordered-collection-diffing.md index 9e64b5a25f..b75c0a8448 100644 --- a/proposals/0240-ordered-collection-diffing.md +++ b/proposals/0240-ordered-collection-diffing.md @@ -64,7 +64,7 @@ extension BidirectionalCollection { /// - areEquivalent: A closure that returns whether the two /// parameters are equivalent. /// - /// - Returns: The difference needed to produce the reciever's state from + /// - Returns: The difference needed to produce the receiver's state from /// the parameter's state. /// /// - Complexity: For pathological inputs, worst case performance is @@ -87,7 +87,7 @@ extension BidirectionalCollection where Element: Equatable { /// - Parameters: /// - other: The base state. /// - /// - Returns: The difference needed to produce the reciever's state from + /// - Returns: The difference needed to produce the receiver's state from /// the parameter's state. /// /// - Complexity: For pathological inputs, worst case performance is diff --git a/proposals/0243-codepoint-and-character-literals.md b/proposals/0243-codepoint-and-character-literals.md index 048bbc907c..6890ca77cf 100644 --- a/proposals/0243-codepoint-and-character-literals.md +++ b/proposals/0243-codepoint-and-character-literals.md @@ -154,7 +154,7 @@ In Swift 1.0, single quotes were reserved for some yet-to-be determined syntacti - In current discussions around [regex literals](https://forums.swift.org/t/string-update/7398/6), most people seem to prefer slashes (`/`). -Given that, and the desire for lightweight syntax for single chararcter syntax, and the precedent in other languages for characters, it is natural to use single quotes for this purpose. +Given that, and the desire for lightweight syntax for single character syntax, and the precedent in other languages for characters, it is natural to use single quotes for this purpose. ### Existing double quote initializers for characters diff --git a/proposals/0244-opaque-result-types.md b/proposals/0244-opaque-result-types.md index da3ea39a61..a6404963e1 100644 --- a/proposals/0244-opaque-result-types.md +++ b/proposals/0244-opaque-result-types.md @@ -634,7 +634,7 @@ The one using opaque typealiases requires an intermediate name, which one must r ## Future Directions -As noted in the introduction, this proposal is the first part of a group of changes we're considering in a [design document for improving the UI of the generics model](https://forums.swift.org/t/improving-the-ui-of-generics/22814). That design document lays out a number of related directions we can go based on the foundation establised by this proposal, including: +As noted in the introduction, this proposal is the first part of a group of changes we're considering in a [design document for improving the UI of the generics model](https://forums.swift.org/t/improving-the-ui-of-generics/22814). That design document lays out a number of related directions we can go based on the foundation established by this proposal, including: - allowing [fully generalized reverse generics](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--reverse-generics) - [generalizing the `some` syntax](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--directly-expressing-constraints) as shorthand for generic arguments, and allowing structural use in generic returns diff --git a/proposals/0245-array-uninitialized-initializer.md b/proposals/0245-array-uninitialized-initializer.md index 7180c5ec9b..28c017a2b9 100644 --- a/proposals/0245-array-uninitialized-initializer.md +++ b/proposals/0245-array-uninitialized-initializer.md @@ -46,7 +46,7 @@ buffer. The new initializer takes a closure that operates on an `UnsafeMutableBufferPointer` and an `inout` count of initialized elements. This closure has access to the uninitialized contents of the newly created array's -storage, and must set the intialized count of the array before exiting. +storage, and must set the initialized count of the array before exiting. ```swift var myArray = Array(unsafeUninitializedCapacity: 10) { buffer, initializedCount in diff --git a/proposals/0246-mathable.md b/proposals/0246-mathable.md index dae0a33fc3..c4fe2be0a8 100644 --- a/proposals/0246-mathable.md +++ b/proposals/0246-mathable.md @@ -311,7 +311,7 @@ to want only the first value, so returning a tuple creates noise: re-exported in Swift where `lgamma` is ambiguous; it can be either the platform shim returning `(T, Int)`, or the C library function returning `Double`; we want to deprecate the first and make the second unavailable. -Simulataneously introducing yet another function with the same name would +Simultaneously introducing yet another function with the same name would create a bit of a mess. ### Future expansion diff --git a/proposals/0253-callable.md b/proposals/0253-callable.md index bf59d62b45..035161bc7b 100644 --- a/proposals/0253-callable.md +++ b/proposals/0253-callable.md @@ -595,7 +595,7 @@ struct Adder { We feel this approach is not ideal because a marker type attribute is not particularly meaningful. The call-syntax delegate methods of a type are what make values of that type callable - a type attribute means nothing by itself. -There's also an unforunate edge case that must be explicitly handled: if a +There's also an unfortunate edge case that must be explicitly handled: if a `@staticCallable` type defines no call-syntax delegate methods, an error must be produced. diff --git a/proposals/0254-static-subscripts.md b/proposals/0254-static-subscripts.md index ca63840d4f..fdc1935b81 100644 --- a/proposals/0254-static-subscripts.md +++ b/proposals/0254-static-subscripts.md @@ -98,7 +98,7 @@ Objective-C class methods with the same selectors as instance subscript methods ## Source compatibility -This proposal is purely additive; it does not change any prevously existing behavior. All syntax it will add support for was previously illegal. +This proposal is purely additive; it does not change any previously existing behavior. All syntax it will add support for was previously illegal. ## ABI compatibility and backwards deployment diff --git a/proposals/0259-approximately-equal.md b/proposals/0259-approximately-equal.md index ff167fbb1b..c8a1249e4f 100644 --- a/proposals/0259-approximately-equal.md +++ b/proposals/0259-approximately-equal.md @@ -133,7 +133,7 @@ extension FloatingPoint { tolerance: Self = Self.ulpOfOne.squareRoot() ) -> Bool { // tolerances outside of [.ulpOfOne,1) yield well-defined but useless results, - // so this is enforced by an assert rathern than a precondition. + // so this is enforced by an assert rather than a precondition. assert(tolerance >= .ulpOfOne && tolerance < 1, "tolerance should be in [.ulpOfOne, 1).") // The simple computation below does not necessarily give sensible // results if one of self or other is infinite; we need to rescale diff --git a/proposals/0260-library-evolution.md b/proposals/0260-library-evolution.md index 7d19902b08..e14420908c 100644 --- a/proposals/0260-library-evolution.md +++ b/proposals/0260-library-evolution.md @@ -27,7 +27,7 @@ _Note: this proposal will use the word "field" to mean "stored instance property As of Swift 5, libraries are able to declare a stable ABI, allowing a library binary to be replaced with a newer version without requiring client programs to be recompiled. -What consitutes the ABI of a library differs from language to language. In C and C++, the public ABI for a library includes information that ideally would be kept purely as an implementation detail. For example, the _size_ of a struct is fixed as part of the ABI, and is known to the library user at compile time. This prevents adding new fields to that type in later releases once the ABI is declared stable. If direct access to fields is allowed, the _layout_ of the struct can also become part of the ABI, so fields cannot then be reordered. +What constitutes the ABI of a library differs from language to language. In C and C++, the public ABI for a library includes information that ideally would be kept purely as an implementation detail. For example, the _size_ of a struct is fixed as part of the ABI, and is known to the library user at compile time. This prevents adding new fields to that type in later releases once the ABI is declared stable. If direct access to fields is allowed, the _layout_ of the struct can also become part of the ABI, so fields cannot then be reordered. This often leads to manual workarounds. A common technique is to have the struct hold only a pointer to an "impl" type, which holds the actual stored properties. All access to these properties is made via function calls, which can be updated to handle changes to the layout of the "impl" type. This has some obvious downsides: diff --git a/proposals/0261-identifiable.md b/proposals/0261-identifiable.md index 48fcc60f47..c19eda84de 100644 --- a/proposals/0261-identifiable.md +++ b/proposals/0261-identifiable.md @@ -101,7 +101,7 @@ doesn't move to the standard library Swift programmers will need to continue using their own variation of this protocol and will need to ensure it is able co-exist with other similar definitions found in other frameworks higher up the dependency stack. Unfortunately none these variations -are likey to be compatible with one another. +are likely to be compatible with one another. ## Proposed solution diff --git a/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md index 0129be339a..c2423c71e4 100644 --- a/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md +++ b/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md @@ -107,10 +107,10 @@ It was considered to extend the grammars for methods declaration only, this was It was suggested, as an alternative, for the testing method names use case to add a method attribute: ``` @test("test validation should succeed when input is less than ten") -func testValidationShouldSuccedWhenInputIsLessThanTen() {} +func testValidationShouldSucceedWhenInputIsLessThanTen() {} ``` It was not considered a valid option for few reasons: -* it introduces information redudancy +* it introduces information redundancy * it is not applicable for the rest of the issues mentioned above * adding a new attribute would likely to introduce more complexity to the compiler and to the test runner diff --git a/proposals/0290-negative-availability.md b/proposals/0290-negative-availability.md index bca419bdc5..2d43fdcce4 100644 --- a/proposals/0290-negative-availability.md +++ b/proposals/0290-negative-availability.md @@ -193,7 +193,7 @@ if #available(iOS 12, iOS 13, *) // Error: Version for 'iOS' already specified Additionally, the specification of different platforms have no effect on the final result -- it depends *only* on the (unique) spec that matches the current platform being compiled. A check like `#available(iOS 12, watchOS 4, *)` compiled in iOS doesn't mean "return true if (iOS 12 **||** watchOS 4 **||** the current platform's minimum deployment target) is available", it simply means "return true if iOS 12 is available". The specs that refer to different platforms are ignored. -Finally, the wildcard represents *only* the platforms that were not explicitly specified in the spec list. When `#available(iOS 13, *)` is compiled for iOS, the wildcard will be ignored in favor of the explictly defined iOS 13 spec. As mentioned before, a platform can only be mentioned once. +Finally, the wildcard represents *only* the platforms that were not explicitly specified in the spec list. When `#available(iOS 13, *)` is compiled for iOS, the wildcard will be ignored in favor of the explicitly defined iOS 13 spec. As mentioned before, a platform can only be mentioned once. For unavailability, the semantics mentioned above means that `#unavailable(*)` and `#unavailable(notWhatImCompiling X, *)` should do the opposite of `#available` and return `false`. Since the minimum deployment target will always be present, the statement can never be true. This behavior is exactly how the current workaround works, and it also matches how the theoretical `!#available(*)` would behave. @@ -242,7 +242,7 @@ On the other hand, given that it's fair to consider that this is a developer's f One point of discussion was the importance of the wildcard in the case of unavailability. Because the wildcard achieves nothing in terms of functionality, we considered alternatives that involved omitting or removing it completely. However, the wildcard is still important from a platform migration point of view, because although we don't need to force the guarded branch to be executed like in `#available`, the presence of the wildcard still play its intended role of allowing code involving unavailability statements to be ported to different platforms without requiring every single statement to be modified. -Additionally, we had lenghty discussions about the *readability* of unavailability statements. We've noticed that even though availability in Swift has never been a boolean expression, it was clear that pretty much every developer's first instinct is to assume that `(iOS 12, *)` is equivalent to `iOS 12 || *`. The main point of discussion then was that the average developer might not understand why a call like `#unavailable(iOS 12, *)` will return `false` in non-iOS platforms, because they will assume the list means `iOS 12 || *` (`true`), while in reality (and as described in the `Semantics` section) the list means just `*` (`false`). During the pitch we tried to come up with alternatives that could eliminate this, and although some of them *did* succeed in doing that, they were doing so at the cost of making `#unavailable` "misleading", just like in the case of `!#available`. We ultimately decided that these improvements would be better suited for a *separate* proposal that focused on improving spec lists in general, which will be mentioned again at the end of this section. +Additionally, we had lengthy discussions about the *readability* of unavailability statements. We've noticed that even though availability in Swift has never been a boolean expression, it was clear that pretty much every developer's first instinct is to assume that `(iOS 12, *)` is equivalent to `iOS 12 || *`. The main point of discussion then was that the average developer might not understand why a call like `#unavailable(iOS 12, *)` will return `false` in non-iOS platforms, because they will assume the list means `iOS 12 || *` (`true`), while in reality (and as described in the `Semantics` section) the list means just `*` (`false`). During the pitch we tried to come up with alternatives that could eliminate this, and although some of them *did* succeed in doing that, they were doing so at the cost of making `#unavailable` "misleading", just like in the case of `!#available`. We ultimately decided that these improvements would be better suited for a *separate* proposal that focused on improving spec lists in general, which will be mentioned again at the end of this section. In general, there was much worry that this confusion could cause developers to misuse `#unavailable` and introduce bugs in their applications. We can prove that this feeling cannot happen in practice by how `#unavailable` doesn't introduce any new behavior -- it's nothing more than a reversed `#available` with a reversed literal name, which is semantically no different than the current workaround of using the `else` branch. Any confusing `#unavailable` scenario can also be conveyed as a confusing `#available` scenario by simply swapping the branches. diff --git a/proposals/0295-codable-synthesis-for-enums-with-associated-values.md b/proposals/0295-codable-synthesis-for-enums-with-associated-values.md index eeb9245f08..599e2db532 100644 --- a/proposals/0295-codable-synthesis-for-enums-with-associated-values.md +++ b/proposals/0295-codable-synthesis-for-enums-with-associated-values.md @@ -196,7 +196,7 @@ public init(from decoder: Decoder) throws { ### User customization -For the existing cases, users can customize which properties are included in the encoded respresentation +For the existing cases, users can customize which properties are included in the encoded representation and map the property name to a custom name for the encoded representation by providing a custom `CodingKeys` declaration, instead of having the compiler generate one. The same should apply to the enum case. diff --git a/proposals/0296-async-await.md b/proposals/0296-async-await.md index d7c0b5c89b..6c32790ac6 100644 --- a/proposals/0296-async-await.md +++ b/proposals/0296-async-await.md @@ -486,7 +486,7 @@ In the first case, Swift's overloading rules prefer to call a function with fewe error: `async` function cannot be called from non-asynchronous context ``` -This presents problems for code evolution, because developers of existing asynchronous libraries would have to either have a hard compatiblity break (e.g, to a new major version) or would need have different names for all of the new `async` versions. The latter would likely result in a scheme such as [C#'s pervasive `Async` suffix](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model). +This presents problems for code evolution, because developers of existing asynchronous libraries would have to either have a hard compatibility break (e.g, to a new major version) or would need have different names for all of the new `async` versions. The latter would likely result in a scheme such as [C#'s pervasive `Async` suffix](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model). The second case, where both functions have the same signature and only differ in `async`, is normally rejected by existing Swift's overloading rules. Those do not allow two functions to differ only in their *effects*, and one can not define two functions that only differ in `throws`, for example. diff --git a/proposals/0298-asyncsequence.md b/proposals/0298-asyncsequence.md index 5e70db8c35..d0899264df 100644 --- a/proposals/0298-asyncsequence.md +++ b/proposals/0298-asyncsequence.md @@ -230,7 +230,7 @@ With this extension, our "first long line" example from earlier becomes simply: let first = try? await myFile.lines().first(where: { $0.count > 80 }) ``` -Or, if the sequence should be processed asynchonously and used later: +Or, if the sequence should be processed asynchronously and used later: ```swift async let first = myFile.lines().first(where: { $0.count > 80 }) diff --git a/proposals/0303-swiftpm-extensible-build-tools.md b/proposals/0303-swiftpm-extensible-build-tools.md index 12ece94388..7761d1b5d5 100644 --- a/proposals/0303-swiftpm-extensible-build-tools.md +++ b/proposals/0303-swiftpm-extensible-build-tools.md @@ -431,7 +431,7 @@ struct Diagnostics { static func remark(_ message: String, file: Path? = #file, line: Int? = #line) /// Emits a diagnostic with the specified severity and descriptive message. - static func emit(_ severity: Serverity, _ message: String, file: Path? = #file, line: Int? = #line) + static func emit(_ severity: Severity, _ message: String, file: Path? = #file, line: Int? = #line) /// The seriousness with which the diagnostic is treated. An error causes /// SwiftPM to consider the plugin to have failed to run. @@ -547,11 +547,11 @@ Prebuild commands should be used when the tool being invoked can produce outputs ##### Build Commands -Commands of type `.buildCommand` that are retured by the plugin are incorporated into the build system's dependency graph, so that they run as needed during the build, based on their declared inputs and outputs. This requires that the paths of any outputs can be known before the command is run. This is usually done by forming the names of the outputs based on some combination of the output directory and the name of the input file. +Commands of type `.buildCommand` that are returned by the plugin are incorporated into the build system's dependency graph, so that they run as needed during the build, based on their declared inputs and outputs. This requires that the paths of any outputs can be known before the command is run. This is usually done by forming the names of the outputs based on some combination of the output directory and the name of the input file. Examples of plugins that can use regular build commands include compiler-like translators such as Protobuf and other tools that take a fixed set of inputs and produce a fixed set of outputs. (note that one nuance with Protobuf in particular is that it is actually up to the source generator invoked by `protoc` to determine the output paths — however, the relevant source generators for Swift and C do produce output files with predictable names). -Other examples include translators that "compile" data files in JSON or other editable formats to a suitable binary runtime respresentation. +Other examples include translators that "compile" data files in JSON or other editable formats to a suitable binary runtime representation. Regular build commands with defined outputs are preferable whenever possible, because such commands don't have to run unless their outputs are missing or their inputs have changed since the last time they ran. @@ -867,7 +867,7 @@ import Foundation // Create a module mappings file. This is something that the Swift source // generator `protoc` plug-in we are using requires. The details are not // important for this proposal, except that it needs to be able to be con- - // structed from the information in the context given to the plugin, and + // structured from the information in the context given to the plugin, and // to be written out to the intermediates directory. let moduleMappingsFile = otherFilesDir.appending("module-mappings") let outputString = ". . . module mappings file . . ." diff --git a/proposals/0306-actors.md b/proposals/0306-actors.md index 8dda3655eb..4a1d6d0ee0 100644 --- a/proposals/0306-actors.md +++ b/proposals/0306-actors.md @@ -544,7 +544,7 @@ We'll describe each scenario in detail. An actor-isolated non-`async` declaration can only be synchronously accessed from another declaration that is isolated to the same actor. For synchronous access to an actor-isolated function, the function must be called from another actor-isolated function. For synchronous access to an actor-isolated instance property or instance subscript, the instance itself must be actor-isolated. -An actor-isolated declaration can be asynchronously accessed from any declaration, whether it is isolated to another actor or is non-isolated. Such accesses are asynchronous operations, and therefore must be annotated with `await`. Semantically, the progam will switch to the actor to perform the synchronous operation, and then switch back to the caller's executor afterward. +An actor-isolated declaration can be asynchronously accessed from any declaration, whether it is isolated to another actor or is non-isolated. Such accesses are asynchronous operations, and therefore must be annotated with `await`. Semantically, the program will switch to the actor to perform the synchronous operation, and then switch back to the caller's executor afterward. For example: diff --git a/proposals/0309-unlock-existential-types-for-all-protocols.md b/proposals/0309-unlock-existential-types-for-all-protocols.md index 72a4f71ff1..460d97f02d 100644 --- a/proposals/0309-unlock-existential-types-for-all-protocols.md +++ b/proposals/0309-unlock-existential-types-for-all-protocols.md @@ -80,7 +80,7 @@ Removing the type-level restriction would mean that adding defaulted requirement ### Type-Erasing Wrappers -Beyond making incremental progress toward the goal of [generalized existentials](https://github.com/apple/swift/blob/main/docs/GenericsManifesto.md#generalized-existentials), removing this restriction is a necessary — albeit not sufficient — condition for eliminating the need for manual type-erasing wrappers like [`AnySequence`](https://developer.apple.com/documentation/swift/anysequence). These containers are not always straightforward to implement, and can become a pain to mantain in resilient environments, since the wrapper must evolve in parallel to the protocol. In the meantime, wrapping the unconstrained existential type instead of resorting to `Any` or boxing the value in a subclass or closure will enable type-erasing containers to be written in a way that's easier for the compiler to optimize, and ABI-compatible with future generalized existentials. For requirements that cannot be accessed on the existential directly, it will be possible to forward the call through the convolution of writing protocol extension methods to open the value inside and have full access to the protocol interface inside the protocol extension: +Beyond making incremental progress toward the goal of [generalized existentials](https://github.com/apple/swift/blob/main/docs/GenericsManifesto.md#generalized-existentials), removing this restriction is a necessary — albeit not sufficient — condition for eliminating the need for manual type-erasing wrappers like [`AnySequence`](https://developer.apple.com/documentation/swift/anysequence). These containers are not always straightforward to implement, and can become a pain to maintain in resilient environments, since the wrapper must evolve in parallel to the protocol. In the meantime, wrapping the unconstrained existential type instead of resorting to `Any` or boxing the value in a subclass or closure will enable type-erasing containers to be written in a way that's easier for the compiler to optimize, and ABI-compatible with future generalized existentials. For requirements that cannot be accessed on the existential directly, it will be possible to forward the call through the convolution of writing protocol extension methods to open the value inside and have full access to the protocol interface inside the protocol extension: ```swift protocol Foo { @@ -314,7 +314,7 @@ Though, if we *were* going to introduce a new syntax for existentials, we think * Simplify the implementation of Standard Library type-erasing wrappers, such as [`AnyHashable`](https://github.com/apple/swift/blob/main/stdlib/public/core/AnyHashable.swift) and [`AnyCollection`](https://github.com/apple/swift/blob/main/stdlib/public/core/ExistentialCollection.swift), using the practical advice from [earlier](#type-erasing-wrappers). * Deemphasize existential types. - It is often that people reach for existential types when they should be employing generic contraints — "should" not merely for performance reasons, but because they truly do not need or intend for any type erasure. Even though the compiler is sometimes able to back us up performance-wise by turning existential code into generic code (as in `func foo(s: Sequence)` vs `func foo(s: S)`), there is an important difference between the two abstractions. Existential types provide value-level abstraction, that is, they eliminate the type-level distinction between different values of the type, and cannot maintain type relationships between independent existential values. Under most cirumstances, value-level abstraction only really makes sense in mutable state, in the elements of heterogeneous containers, or, unless our support of [`some` types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0244-opaque-result-types.md) can turn the tide, in the storage of larger type-erasing constructs. A fitting starting point for giving value-level abstraction more careful consideration early on is the [Language Guide](https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html). + It is often that people reach for existential types when they should be employing generic constraints — "should" not merely for performance reasons, but because they truly do not need or intend for any type erasure. Even though the compiler is sometimes able to back us up performance-wise by turning existential code into generic code (as in `func foo(s: Sequence)` vs `func foo(s: S)`), there is an important difference between the two abstractions. Existential types provide value-level abstraction, that is, they eliminate the type-level distinction between different values of the type, and cannot maintain type relationships between independent existential values. Under most circumstances, value-level abstraction only really makes sense in mutable state, in the elements of heterogeneous containers, or, unless our support of [`some` types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0244-opaque-result-types.md) can turn the tide, in the storage of larger type-erasing constructs. A fitting starting point for giving value-level abstraction more careful consideration early on is the [Language Guide](https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html). * Make existential types "self-conforming" by automatically opening them when passed as generic arguments to functions. Generic instantiations could have them opened as opaque types. * Add an `init(_ box: Hashable)` initializer to `AnyHashable` to alleviate confusion and aid in usability. This initializer would be treated as a workaround, and deprecated should automatic opening of existential types become available. * Introduce [`any P` as a dual to `some P`](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--clarifying-existentials) for explicitly spelling existential types. diff --git a/proposals/0310-effectful-readonly-properties.md b/proposals/0310-effectful-readonly-properties.md index 370cc91d49..af9dc2b7d8 100644 --- a/proposals/0310-effectful-readonly-properties.md +++ b/proposals/0310-effectful-readonly-properties.md @@ -347,7 +347,7 @@ There are a number of places where the effects specifiers be placed: Where `` refers to "position X" in the example. Consider each of these positions: * **Position A** is primarily used by access modifiers like `private(set)` or declaration modifiers like `override`. The more effect-like `mutating`/`nonmutating` is only allowed in Position C, which precedes the accessor declaration, just like a method within a struct. This position was not chosen because phrases like `override async throws var prop` or `async throws override var prop` do not read particularly well. -* **Position B** does not make much sense, because effects are only carried as part of a function type, not other types. So, it would be very confusing, leading people to think `Int async throws` is a type, when that is not. Introducing a new kind of punctuation here was ruled out because there are alteratives to this position. +* **Position B** does not make much sense, because effects are only carried as part of a function type, not other types. So, it would be very confusing, leading people to think `Int async throws` is a type, when that is not. Introducing a new kind of punctuation here was ruled out because there are alternatives to this position. * **Position C** is not bad; it's only occupied by `mutating`/`nonmutating`, but placing effects specifiers here is not consistent with the positioning for functions, which is *after* the subject. Since Position D is available, it makes more sense to use that instead of Position C. * **Position D** is the one ultimately chosen for this proposal. It is an unused place in the grammar, places the effects on the accessor and not the variable or its type. Plus, it is consistent with where effects go on a function declaration, after the subject: `get throws` and `get async throws`, where get is the subject. Another benefit is that it is away from the variable, so it prevents confusion between the accessor's effects and the effects of a function being returned: diff --git a/proposals/0311-task-locals.md b/proposals/0311-task-locals.md index 4c3f895014..d6f48593e5 100644 --- a/proposals/0311-task-locals.md +++ b/proposals/0311-task-locals.md @@ -116,7 +116,7 @@ Each task-local declaration represents its own, independent task-local storage. Because of those pitfalls with creating multiple instances of the same task local identifier, we propose to diagnose and fail at compile time if the `@TaskLocal` property wrapper is not defined on a static or global property. -> In order to do so, we will extend the internal `public static subscript(_enclosingInstance object: T, ...)` subscript mechanism to require "no enclosing instance", which will cause the apropriate compile time error reporting to be triggered if such wrapper is used on a non-static or non-global property. +> In order to do so, we will extend the internal `public static subscript(_enclosingInstance object: T, ...)` subscript mechanism to require "no enclosing instance", which will cause the appropriate compile time error reporting to be triggered if such wrapper is used on a non-static or non-global property. The diagnosed error would look like this: @@ -499,7 +499,7 @@ await withTaskGroup(...) { group in ### Task-local value lifecycle -Task-local values are retained until `withValue`'s `operation` scope exits. Effectively this means that the value is kept alive until all child tasks created in such scope exit as well. This is important because child tasks may be refering to this specific value in the parent task, so it cannot be released earlier. +Task-local values are retained until `withValue`'s `operation` scope exits. Effectively this means that the value is kept alive until all child tasks created in such scope exit as well. This is important because child tasks may be referring to this specific value in the parent task, so it cannot be released earlier. Both value and reference types are allowed to be stored in task-local storage, using their expected respective semantics: @@ -951,7 +951,7 @@ Dispatch offers APIs that allow setting values that are _specific to a dispatch These APIs serve their purpose well, however they are incompatible with Swift Concurrency's task-focused model. Even if actors and asynchronous functions execute on dispatch queues, no capability to carry values over multiple queues is given, which is necessary to work well with Swift Concurrency, as execution may hop back and forth between queues. ## Intended use-cases -It is important to keep in mind the intended use case of this API. Task-local values are not intended to replace passing parameters where doing so explicitly is the right tool for the job. Please note that task local storage is more expensive to access than parameters passed explicitly. They also are "invisible" in API, so take care to avoid accidentally building APIs which absolutely must have some task local value set when they are called as this is very suprising and hard to debug behavior. +It is important to keep in mind the intended use case of this API. Task-local values are not intended to replace passing parameters where doing so explicitly is the right tool for the job. Please note that task local storage is more expensive to access than parameters passed explicitly. They also are "invisible" in API, so take care to avoid accidentally building APIs which absolutely must have some task local value set when they are called as this is very surprising and hard to debug behavior. Only use task local storage for auxiliary _metadata_ or "_execution scoped configuration_", like mocking out some runtime bits for the duration of a _specific call_ but not globally, etc. @@ -1195,7 +1195,7 @@ Some, specialized use-cases however can declare more specific inheritance semant A `TaskLocal` type may declare an inheritance semantics by defining the static `inherit` parameter when declaring the variable: `TaskLocal(inherit: .never)`. -The semantics default to `.default`, which are what one would expect normally — that child tasks are able to lookup values defined in their parents, unless overriden in that specific child. We will discuss the exact semantics of lookups in depth in [Reading task-local values](#reading-task-local-values). +The semantics default to `.default`, which are what one would expect normally — that child tasks are able to lookup values defined in their parents, unless overridden in that specific child. We will discuss the exact semantics of lookups in depth in [Reading task-local values](#reading-task-local-values). In this proposal, we introduce two additional inheritance semantics: `.never` and `.alwaysBestEffort`: diff --git a/proposals/0313-actor-isolation-control.md b/proposals/0313-actor-isolation-control.md index 89fb4d8b6b..72f35a9d04 100644 --- a/proposals/0313-actor-isolation-control.md +++ b/proposals/0313-actor-isolation-control.md @@ -322,7 +322,7 @@ At a high level, isolated parameters and isolated conformances are similar to pa } } ``` - Generally speaking, a variable in Swift has the same type when it's captured in a nested context as it does in its enclosing context, which provides a level of predictability that would be lost with `@sync` types. In the example above, type inference for the call to `f` differs significantly whether you're in the closure or not. A [recent discussion on the forums](https://forums.swift.org/t/implicit-casts-for-verified-type-information/41035) about narrowing types showed resistence to the idea of changing the type of a variable in a nested context, even when doing so could eliminate additional boilerplate. + Generally speaking, a variable in Swift has the same type when it's captured in a nested context as it does in its enclosing context, which provides a level of predictability that would be lost with `@sync` types. In the example above, type inference for the call to `f` differs significantly whether you're in the closure or not. A [recent discussion on the forums](https://forums.swift.org/t/implicit-casts-for-verified-type-information/41035) about narrowing types showed resistance to the idea of changing the type of a variable in a nested context, even when doing so could eliminate additional boilerplate. * The design relies heavily on the implicit conversion from `@sync MyActor` to `MyActor`, e.g., diff --git a/proposals/0317-async-let.md b/proposals/0317-async-let.md index d85965d91c..6720edf791 100644 --- a/proposals/0317-async-let.md +++ b/proposals/0317-async-let.md @@ -783,7 +783,7 @@ func runThrowsOkay() async { } ``` -The rules above attempt to limit the places in which the new `await` syntaxes are required to only those where they are semantically meaningful, i.e., those places where the `async let` child tasks will not already have had their completion explicitly awaited. The rules are complicated enough that we would not expect programmers to be able to correctly write `await` in all of the places where it is required. Rather, the Swift compiler would need to provide error messags with Fix-Its to indicate the places where additional `await` annotations are required, and those `await`s will remain as an artifact for the reader. +The rules above attempt to limit the places in which the new `await` syntaxes are required to only those where they are semantically meaningful, i.e., those places where the `async let` child tasks will not already have had their completion explicitly awaited. The rules are complicated enough that we would not expect programmers to be able to correctly write `await` in all of the places where it is required. Rather, the Swift compiler would need to provide error messages with Fix-Its to indicate the places where additional `await` annotations are required, and those `await`s will remain as an artifact for the reader. We feel that the complexity of the solution for marking all suspension points, which includes both the grammar expansion for marking control-flow edges and the flow-sensitive analysis to only require the additional `await` marking when necessary, exceeds the benefits of adding it. Instead, we feel that the presence of `async let` in a block with complicated control flow is sufficient to imply the presence of additional suspension points. diff --git a/proposals/0318-package-creation.md b/proposals/0318-package-creation.md index 1ad4a894a5..606cf9ad69 100644 --- a/proposals/0318-package-creation.md +++ b/proposals/0318-package-creation.md @@ -233,8 +233,8 @@ let package = Package( name: "___NAME_AS_C99___", sources: "src", dependencies: [ - .product(name: "NIO", pacakge: "swift-nio"), - .product(name: "Crypto", pacakge: "swift-crypto") + .product(name: "NIO", package: "swift-nio"), + .product(name: "Crypto", package: "swift-crypto") ] ), ] @@ -279,8 +279,8 @@ let package = Package( name: "HelloWorld", sources: "src", dependencies: [ - .product(name: "NIO", pacakge: "swift-nio"), - .product(name: "Crypto", pacakge: "swift-crypto") + .product(name: "NIO", package: "swift-nio"), + .product(name: "Crypto", package: "swift-crypto") ] ), ] @@ -358,7 +358,7 @@ No impact. The main alternative is to modify the behavior of `swift package init` such that it better caters to the creation of new packages from scratch. The advantage of this alternative is that it maintains the API surface area. The disadvantages are that any changes to make it better for package creation are likely to make it confusing for transforming existing sources to package. More importantly, changes to the existing command may cause impact on users that have automation tied to the current behavior. -For templates, the main alternative is to use a data file (e.g. JSON) that describes how the package should be constructed. This would hone in the implementation as it defines a finite set of capabilities driven by configuraiton. This was not selected in order to provide a better user experience, and greater flexibility with respect to including other files in a template. +For templates, the main alternative is to use a data file (e.g. JSON) that describes how the package should be constructed. This would hone in the implementation as it defines a finite set of capabilities driven by configuration. This was not selected in order to provide a better user experience, and greater flexibility with respect to including other files in a template. # Future Iterations diff --git a/proposals/0319-never-identifiable.md b/proposals/0319-never-identifiable.md index 25efcce36f..e298dcc55b 100644 --- a/proposals/0319-never-identifiable.md +++ b/proposals/0319-never-identifiable.md @@ -18,7 +18,7 @@ With the acceptance of [SE-0215](https://github.com/swiftlang/swift-evolution/bl The conformance of `Never` to `Equatable` and `Hashable` in SE-0215 was motivated by examples like using `Never` as a generic constraint in types like `Result` and in enumerations. These same use cases motivate the conformance of `Never` to `Identifiable`, which is pervasive in commonly used frameworks like SwiftUI. -For example, the new `TableRowContent` protocol in SwiftUI follows a "recursive type pattern" and has the need for a primitive bottom type with an `Identifiable` assocated type: +For example, the new `TableRowContent` protocol in SwiftUI follows a "recursive type pattern" and has the need for a primitive bottom type with an `Identifiable` associated type: ```swift extension Never: TableRowContent { diff --git a/proposals/0325-swiftpm-additional-plugin-apis.md b/proposals/0325-swiftpm-additional-plugin-apis.md index 70abb2ce63..626710d855 100644 --- a/proposals/0325-swiftpm-additional-plugin-apis.md +++ b/proposals/0325-swiftpm-additional-plugin-apis.md @@ -181,7 +181,7 @@ public protocol Product { /// The targets that directly comprise the product, in the order in which /// they are declared in the package manifest. The product will contain the - /// transitive closure of the these targets and their depdendencies. + /// transitive closure of the these targets and their dependencies. var targets: [Target] { get } } @@ -419,7 +419,7 @@ This proposal also adds the first of what is expected to be a toolbox of APIs to ```swift extension Target { - /// The transitive closure of all the targets on which the reciver depends, + /// The transitive closure of all the targets on which the receiver depends, /// ordered such that every dependency appears before any other target that /// depends on it (i.e. in "topological sort order"). public var recursiveTargetDependencies: [Target] @@ -509,7 +509,7 @@ struct MyPlugin: BuildToolPlugin { // Create a module mappings file. This is something that the Swift source // generator `protoc` plug-in we are using requires. The details are not // important for this proposal, except that it needs to be able to be con- - // structed from the information in the context given to the plugin, and + // structured from the information in the context given to the plugin, and // to be written out to the intermediates directory. let moduleMappingsFile = otherFilesDir.appending("module-mappings") let outputString = ". . . module mappings file . . ." diff --git a/proposals/0332-swiftpm-command-plugins.md b/proposals/0332-swiftpm-command-plugins.md index 8eb0c891a7..3e30dc8957 100644 --- a/proposals/0332-swiftpm-command-plugins.md +++ b/proposals/0332-swiftpm-command-plugins.md @@ -24,7 +24,7 @@ Separately to this proposal, it would also be useful to define custom actions th ## Proposed Solution -This proposal defines a new plugin capability called `command` that allows packages to augment the set of package-related commands availabile in the SwiftPM CLI and in IDEs that support packages. A command plugin specifies the semantic intent of the command — this might be one of the predefined intents such “documentation generation” or “source code formatting”, or it might be a custom intent with a specialized verb that can be passed to the `swift` `package` command. A command plugin can also specify any special permissions it needs (such as the permission to modify the files under the package directory). +This proposal defines a new plugin capability called `command` that allows packages to augment the set of package-related commands available in the SwiftPM CLI and in IDEs that support packages. A command plugin specifies the semantic intent of the command — this might be one of the predefined intents such “documentation generation” or “source code formatting”, or it might be a custom intent with a specialized verb that can be passed to the `swift` `package` command. A command plugin can also specify any special permissions it needs (such as the permission to modify the files under the package directory). The command's intent declaration provides a way of grouping command plugins by their functional categories, so that SwiftPM — or an IDE that supports SwiftPM packages — can show the commands that are available for a particular purpose. For example, this approach supports having different command plugins for generating documentation for a package, while still allowing those different commands to be grouped and discovered by intent. diff --git a/proposals/0337-support-incremental-migration-to-concurrency-checking.md b/proposals/0337-support-incremental-migration-to-concurrency-checking.md index 2bfd135f3c..f3804f5a8f 100644 --- a/proposals/0337-support-incremental-migration-to-concurrency-checking.md +++ b/proposals/0337-support-incremental-migration-to-concurrency-checking.md @@ -73,7 +73,7 @@ Achieving this will require several features working in tandem: * When applied to a nominal declaration, the `@preconcurrency` attribute specifies that a declaration was modified to update it for concurrency checking, so the compiler should allow some uses in Swift 5 mode that violate concurrency checking, and generate code that interoperates with pre-concurrency binaries. -* When applied to an `import` statement, the `@preconcurrency` attribute tells the compiler that it should only diagnose `Sendable`-requiring uses of non-`Sendable` types from that module if the type explicitly declares a `Sendable` conformance that is unavailable or has constraints that are not satisifed; even then, this will only be a warning, not an error. +* When applied to an `import` statement, the `@preconcurrency` attribute tells the compiler that it should only diagnose `Sendable`-requiring uses of non-`Sendable` types from that module if the type explicitly declares a `Sendable` conformance that is unavailable or has constraints that are not satisfied; even then, this will only be a warning, not an error. ## Detailed design diff --git a/proposals/0342-static-link-runtime-libraries-by-default-on-supported-platforms.md b/proposals/0342-static-link-runtime-libraries-by-default-on-supported-platforms.md index c8ade71a5f..788c935013 100644 --- a/proposals/0342-static-link-runtime-libraries-by-default-on-supported-platforms.md +++ b/proposals/0342-static-link-runtime-libraries-by-default-on-supported-platforms.md @@ -206,7 +206,7 @@ The new behavior will take effect with a new version of SwiftPM, and packages bu ### Additional validation when linking libraries SwiftPM currently performs no validation when linking libraries into an executable that statically links the Swift runtime libraries. -This means that users can mistakinly link a library that already has the Swift runtime libraries statically linked into the executable that will also statically link the Swift runtime libraries, which could lead to runtime errors if the versions of the Swift runtime libraries do not match. +This means that users can mistakenly link a library that already has the Swift runtime libraries statically linked into the executable that will also statically link the Swift runtime libraries, which could lead to runtime errors if the versions of the Swift runtime libraries do not match. As part of this proposal, SwiftPM will gain a new post build validation checking for this condition and warning the user accordingly. ## Alternatives considered and future directions diff --git a/proposals/0345-if-let-shorthand.md b/proposals/0345-if-let-shorthand.md index 48e82755ed..265d89d873 100644 --- a/proposals/0345-if-let-shorthand.md +++ b/proposals/0345-if-let-shorthand.md @@ -253,7 +253,7 @@ if foo == true, bar == true { } ``` -To avoid this abiguity, we need some sort of distinct syntax for optional bindings. +To avoid this ambiguity, we need some sort of distinct syntax for optional bindings. ### `if unwrap foo` @@ -295,7 +295,7 @@ Once borrow introducers are added to the language, seeing `ref x` or `inout x` a Borrow introducers will be very useful, but adopting them is a tradeoff between performance and conceptual overhead. Borrows are cheap but come with high conceptual overhead. Copies can be expensive but always work as expected without much extra thought. Given this tradeoff, it likely makes sense for this shorthand syntax to provide a way for users to choose between performing a copy or performing a borrow, rather than limiting users to one or the other. -Additionally, for consistency with existing optional binding conditions, this new shorthand should support the distinction between immutable and mutable variables. Combined with the disctinction between copies and borrows, that would give us the same set of options as normal variables: +Additionally, for consistency with existing optional binding conditions, this new shorthand should support the distinction between immutable and mutable variables. Combined with the distinction between copies and borrows, that would give us the same set of options as normal variables: ```swift // Included in this proposal: @@ -363,7 +363,7 @@ Since `if var foo = foo` is significantly less common than `if let foo = foo`, w `var` shadowing has the potential to be more confusing than `let` shadowing -- `var` introduces a new _mutable_ variable, and any mutations to the new variable are not shared with the original optional variable. On the other hand, `if var foo = foo` already exists, and it seems unlikely that `if var foo` would be more confusing / less clear than the existing syntax. -Since `let` and `var` are interchangable elsewhere in the language, that should also be the case here -- disallowing `if var foo` would be inconsistent with existing optional binding condition syntax. If we were using an alternative spelling that _did not_ use `let`, it may be reasonable to exclude `var` -- but since we are using `let` here, `var` should also be allowed. +Since `let` and `var` are interchangeable elsewhere in the language, that should also be the case here -- disallowing `if var foo` would be inconsistent with existing optional binding condition syntax. If we were using an alternative spelling that _did not_ use `let`, it may be reasonable to exclude `var` -- but since we are using `let` here, `var` should also be allowed. ## Acknowledgments diff --git a/proposals/0347-type-inference-from-default-exprs.md b/proposals/0347-type-inference-from-default-exprs.md index 1f1b90eda3..db105e1d92 100644 --- a/proposals/0347-type-inference-from-default-exprs.md +++ b/proposals/0347-type-inference-from-default-exprs.md @@ -202,7 +202,7 @@ struct Box { ```swift enum Box { case flatRate(dimensions: D = [...], flags: F = DefaultFlags()) -case overnight(dimentions: D = [...], flags: F = DefaultFlags()) +case overnight(dimensions: D = [...], flags: F = DefaultFlags()) ... } ``` diff --git a/proposals/0348-buildpartialblock.md b/proposals/0348-buildpartialblock.md index 967aa96947..4a196e68c6 100644 --- a/proposals/0348-buildpartialblock.md +++ b/proposals/0348-buildpartialblock.md @@ -268,7 +268,7 @@ Such features would greatly complicate the type system. #### Overload `buildBlock` method name -Because the proposed feature overlaps `buildBlock`, one could argue for reusing `buildBlock` as the method base name instead of `buildPartialBlock` and using arguemnt labels to distinguish whether it is the pairwise version, e.g. `buildBlock(partiallyAccumulated:next:)` or `buildBlock(combining:into:)`. +Because the proposed feature overlaps `buildBlock`, one could argue for reusing `buildBlock` as the method base name instead of `buildPartialBlock` and using argument labels to distinguish whether it is the pairwise version, e.g. `buildBlock(partiallyAccumulated:next:)` or `buildBlock(combining:into:)`. ```swift extension Builder { diff --git a/proposals/0349-unaligned-loads-and-stores.md b/proposals/0349-unaligned-loads-and-stores.md index 26c9b30d14..b542077621 100644 --- a/proposals/0349-unaligned-loads-and-stores.md +++ b/proposals/0349-unaligned-loads-and-stores.md @@ -13,7 +13,7 @@ Swift does not currently provide a clear way to load data from an arbitrary sour ## Motivation -The method `UnsafeRawPointer.load(fromByteOffset offset: Int, as type: T.Type) -> T` requires the address at `self+offset` to be properly aligned to access an instance of type `T`. Attempts to use a combination of pointer and byte offset that is not aligned for `T` results in a runtime crash. Unfortunately, in general, data saved to files or network streams does not adhere to the same restrictions as in-memory layouts do, and tends to not be properly aligned. When copying data from such sources to memory, Swift users therefore frequently encounter aligment mismatches that require using a workaround. This is a longstanding issue reported in e.g. [SR-10273](https://bugs.swift.org/browse/SR-10273). +The method `UnsafeRawPointer.load(fromByteOffset offset: Int, as type: T.Type) -> T` requires the address at `self+offset` to be properly aligned to access an instance of type `T`. Attempts to use a combination of pointer and byte offset that is not aligned for `T` results in a runtime crash. Unfortunately, in general, data saved to files or network streams does not adhere to the same restrictions as in-memory layouts do, and tends to not be properly aligned. When copying data from such sources to memory, Swift users therefore frequently encounter alignment mismatches that require using a workaround. This is a longstanding issue reported in e.g. [SR-10273](https://bugs.swift.org/browse/SR-10273). For example, given an arbitrary data stream in which a 4-byte value is encoded between byte offsets 3 through 7: diff --git a/proposals/0351-regex-builder.md b/proposals/0351-regex-builder.md index df32e27fe5..41a39eae1d 100644 --- a/proposals/0351-regex-builder.md +++ b/proposals/0351-regex-builder.md @@ -87,7 +87,7 @@ This proposal introduces all core API for creating and composing regexes that ec ## Motivation -Regex is a fundemental and powerful tool for textual pattern matching. It is a domain-specific language often expressed as text. For example, given the following bank statement: +Regex is a fundamental and powerful tool for textual pattern matching. It is a domain-specific language often expressed as text. For example, given the following bank statement: ``` CREDIT 04062020 PayPal transfer $4.99 @@ -370,7 +370,7 @@ public enum RegexComponentBuilder { Before Swift supports variadic generics, `buildPartialBlock(accumulated:next:)` must be overloaded to support concatenating regexes of supported capture quantities (arities). It is overloaded up to `arity^2` times to account for all possible pairs of regexes that make up 10 captures. -In the initial version of the DSL, we plan to support regexes with up to 10 captures, as 10 captures are sufficient for most use cases. These overloads can be superceded by variadic versions of `buildPartialBlock(first:)` and `buildPartialBlock(accumulated:next:)` in a future release. +In the initial version of the DSL, we plan to support regexes with up to 10 captures, as 10 captures are sufficient for most use cases. These overloads can be superseded by variadic versions of `buildPartialBlock(first:)` and `buildPartialBlock(accumulated:next:)` in a future release. ```swift extension RegexComponentBuilder { @@ -1576,7 +1576,7 @@ The proposed feature does not change the ABI of existing features. ## Effect on API resilience -The proposed feature relies heavily upon overloads of `buildBlock` and `buildPartialBlock(accumulated:next:)` to work for different capture arities. In the fullness of time, we are hoping for variadic generics to supercede existing overloads. Such a change should not involve ABI-breaking modifications as it is merely a change of overload resolution. +The proposed feature relies heavily upon overloads of `buildBlock` and `buildPartialBlock(accumulated:next:)` to work for different capture arities. In the fullness of time, we are hoping for variadic generics to supersede existing overloads. Such a change should not involve ABI-breaking modifications as it is merely a change of overload resolution. ## Future directions @@ -1907,7 +1907,7 @@ extension Capture { } ``` -In this case, since the argument label will not be specfied for the first trailing closure, using `Capture` where the component is a non-builder-closure may cause type-checking ambiguity. +In this case, since the argument label will not be specified for the first trailing closure, using `Capture` where the component is a non-builder-closure may cause type-checking ambiguity. ```swift Regex { diff --git a/proposals/0352-implicit-open-existentials.md b/proposals/0352-implicit-open-existentials.md index 964297e8e8..ccf49c1942 100644 --- a/proposals/0352-implicit-open-existentials.md +++ b/proposals/0352-implicit-open-existentials.md @@ -276,7 +276,7 @@ When binding a generic parameter `T` to an opened existential, `T`, `T` and `T`- will be type-erased to their upper bounds as per the generic signature of the existential that is used to access the member. The upper bounds can be either a class, protocol, protocol composition, or `Any`, depending on the *presence* and *kind* of generic constraints on the associated type. -When `T` or a `T`-rooted associated type appears in a non-covariant position in the result type, `T` cannot be bound to the underlying type of an existential value because there would be no way to represent the type-erased result. This is essentially the same property as descibed for the parameter types that prevents opening of existentials, as described above. For example: +When `T` or a `T`-rooted associated type appears in a non-covariant position in the result type, `T` cannot be bound to the underlying type of an existential value because there would be no way to represent the type-erased result. This is essentially the same property as described for the parameter types that prevents opening of existentials, as described above. For example: ```swift func cannotOpen7(_ value: T) -> X { /*...*/ } diff --git a/proposals/0354-regex-literals.md b/proposals/0354-regex-literals.md index 3523b40aa5..752e22f085 100644 --- a/proposals/0354-regex-literals.md +++ b/proposals/0354-regex-literals.md @@ -490,7 +490,7 @@ It should also be noted that `#regex(...)` would introduce a syntactic inconsist ##### On future extensibility to other foreign language snippets -One of the benefits of `#regex(...)` or `re'...'` is the extensibility to other kinds of foreign langauge snippets, such as SQL. Nothing in this proposal precludes a scalable approach to foreign language snippets using `#lang(...)` or `lang'...'`. If or when that happens, regex could participate as well, but the proposed syntax would still be valuable as regex literals *are* unique in their prevalence as fragments passed directly to API, as well as components of a result builder DSL. +One of the benefits of `#regex(...)` or `re'...'` is the extensibility to other kinds of foreign language snippets, such as SQL. Nothing in this proposal precludes a scalable approach to foreign language snippets using `#lang(...)` or `lang'...'`. If or when that happens, regex could participate as well, but the proposed syntax would still be valuable as regex literals *are* unique in their prevalence as fragments passed directly to API, as well as components of a result builder DSL. #### Shortened magic literal `#(...)` @@ -647,7 +647,7 @@ The regex builder DSL is unable to provide some of the features presented such a Similarly, there is no literal equivalent for some of the regex builder features, but that isn't an argument against them. The regex builder DSL has references which serves this role (though not as concisely) and they are useful beyond just naming captures. -Regex literals should not be outright avoided, they should be used well. Artifically hampering their usage doesn't provide any benefit and we wouldn't want to lock these limitations into Swift's ABI. +Regex literals should not be outright avoided, they should be used well. Artificially hampering their usage doesn't provide any benefit and we wouldn't want to lock these limitations into Swift's ABI. diff --git a/proposals/0356-swift-snippets.md b/proposals/0356-swift-snippets.md index cebb994700..e1ab8692c2 100644 --- a/proposals/0356-swift-snippets.md +++ b/proposals/0356-swift-snippets.md @@ -361,7 +361,7 @@ USAGE: snippet-build ARGUMENTS: - The directory containing Swift snippets - - The diretory in which to place Symbol Graph JSON file(s) representing the snippets + - The directory in which to place Symbol Graph JSON file(s) representing the snippets - The module name to use for the Symbol Graph (typically should be the package name) ``` diff --git a/proposals/0357-regex-string-processing-algorithms.md b/proposals/0357-regex-string-processing-algorithms.md index ca9b7325c7..1cbd60d3f3 100644 --- a/proposals/0357-regex-string-processing-algorithms.md +++ b/proposals/0357-regex-string-processing-algorithms.md @@ -594,7 +594,7 @@ extension Collection where SubSequence == Substring { /// Returns a collection containing all matches of the specified regex. /// - Parameter regex: The regex to search for. /// - Returns: A collection of matches of `regex`. - public func matches(of regex: R) -> some Collection.Match> + public func matches(of regex: R) -> some Collection.Match> } // In RegexBuilder module @@ -613,7 +613,7 @@ extension Collection where SubSequence == Substring { #### Replace -We propose generic collection algorithms that will replace all occurences of a given subsequence: +We propose generic collection algorithms that will replace all occurrences of a given subsequence: ```swift extension RangeReplaceableCollection where Element: Equatable { @@ -788,7 +788,7 @@ extension RangeReplaceableCollection where SubSequence == Substring { public func replacing( _ regex: R, maxReplacements: Int = .max, - with replacement: (Regex.Match) throws -> Replacement + with replacement: (Regex.Match) throws -> Replacement ) rethrows -> Self where Replacement.Element == Element /// Replaces all occurrences of the sequence matching the given regex with @@ -1124,7 +1124,7 @@ This protocol customizes the basic consume-from-the-front functionality. A proto ### Why `where SubSequence == Substring`? -A `Substring` slice requirement allows the regex engine to produce indicies in the original collection by operating over a portion of the input. Unfortunately, this is not one of the requirements of `StringProtocol`. +A `Substring` slice requirement allows the regex engine to produce indices in the original collection by operating over a portion of the input. Unfortunately, this is not one of the requirements of `StringProtocol`. A new protocol for types that can produce a `Substring` on request (e.g. from UTF-8 contents) would have to eagerly produce a `String` copy first and would need requirements to translate indices. When higher-level algorithms are implemented via multiple calls to the lower-level algorithms, these copies could happen many times. Shared strings are future work but a much better solution to this. diff --git a/proposals/0363-unicode-for-string-processing.md b/proposals/0363-unicode-for-string-processing.md index 3f2703b74b..a56e074ddf 100644 --- a/proposals/0363-unicode-for-string-processing.md +++ b/proposals/0363-unicode-for-string-processing.md @@ -373,7 +373,7 @@ When matching with *grapheme cluster semantics* (the default), metacharacters li When matching with *Unicode scalar semantics*, metacharacters and character classes match a single Unicode scalar value, even if that scalar comprises part of a grapheme cluster. Canonical representations are _not_ used, corresponding with the way comparison would work when using a string's `UnicodeScalarView`. -These specific levels of matching, and the options to switch between them, are unique to Swift, but not unprecedented in other regular expression engines. Several engines, including Perl, Java, and ICU-based engines like `NSRegularExpression`, support the `\X` metacharacter for matching a grapheme cluster within otherwise Unicode scalar semantic matching. Rust has a related concept in its [`regex::bytes` type][regexbytes], which matches over abitrary bytes by default but allows switching into Unicode mode for segments of the regular expression. +These specific levels of matching, and the options to switch between them, are unique to Swift, but not unprecedented in other regular expression engines. Several engines, including Perl, Java, and ICU-based engines like `NSRegularExpression`, support the `\X` metacharacter for matching a grapheme cluster within otherwise Unicode scalar semantic matching. Rust has a related concept in its [`regex::bytes` type][regexbytes], which matches over arbitrary bytes by default but allows switching into Unicode mode for segments of the regular expression. These semantic levels lead to different results when working with strings that have characters made up of multiple Unicode scalar values, such as Emoji or decomposed characters. In the following example, `queRegex` matches any 3-character string that begins with `"q"`. @@ -1273,7 +1273,7 @@ Instead, we could use this opportunity to choose default options that are more e ### Include `\O` and `CharacterClass.anyUnicodeScalar` -An earlier draft of this proposal included a metacharacter and `CharacterClass` API for matching an individual Unicode scalar value, regardless of the current matching level, as a counterpart to `\X`/`.anyGraphemeCluster`. The behavior of this character class, particularly when matching with grapheme cluster semantics, is still unclear at this time, however. For example, when matching the expression `\O*`, does the implict grapheme boundary assertion apply between the `\O` and the quantification operator, or should we treat the two as a single unit and apply the assertion after the `*`? +An earlier draft of this proposal included a metacharacter and `CharacterClass` API for matching an individual Unicode scalar value, regardless of the current matching level, as a counterpart to `\X`/`.anyGraphemeCluster`. The behavior of this character class, particularly when matching with grapheme cluster semantics, is still unclear at this time, however. For example, when matching the expression `\O*`, does the implicit grapheme boundary assertion apply between the `\O` and the quantification operator, or should we treat the two as a single unit and apply the assertion after the `*`? At the present time, we prefer to allow authors to write regexes that explicitly shift into and out of Unicode scalar mode, where those kinds of decisions are handled by the explicit scope of the setting. If common patterns emerge that indicate some version of `\O` would be useful, we can add it in the future. diff --git a/proposals/0367-conditional-attributes.md b/proposals/0367-conditional-attributes.md index b6ca2bb8ec..09b915db61 100644 --- a/proposals/0367-conditional-attributes.md +++ b/proposals/0367-conditional-attributes.md @@ -36,7 +36,7 @@ This is unsatisfactory for at least two reasons. First, it's a lot of code dupli I propose two related changes to make it easier to adopt new attributes in existing code: * Allow `#if` checks to surround attributes on a declaration wherever they appear, eliminating the need to clone a declaration just to adopt a new attribute. -* Add a conditional directive `hasAttribute(AttributeName)` that evalutes `true` when the compiler has support for the attribute with the name `AttributeName` in the current language mode. +* Add a conditional directive `hasAttribute(AttributeName)` that evaluates `true` when the compiler has support for the attribute with the name `AttributeName` in the current language mode. The first two of these can be combined to make the initial example less repetitive and more descriptive: diff --git a/proposals/0376-function-back-deployment.md b/proposals/0376-function-back-deployment.md index c5b2465127..396dcc93da 100644 --- a/proposals/0376-function-back-deployment.md +++ b/proposals/0376-function-back-deployment.md @@ -89,7 +89,7 @@ extension Temperature { @available(toasterOS 1.0, ovenOS 1.0, *) @backDeployed(before: toasterOS 2.0, ovenOS 2.0) public var degreesFahrenheit: Double { - return (degreesCelcius * 9 / 5) + 32 + return (degreesCelsius * 9 / 5) + 32 } } diff --git a/proposals/0381-task-group-discard-results.md b/proposals/0381-task-group-discard-results.md index 6ff0e4a2b3..27ec7607b0 100644 --- a/proposals/0381-task-group-discard-results.md +++ b/proposals/0381-task-group-discard-results.md @@ -17,7 +17,7 @@ Task groups are the building block of structured concurrency, allowing for the S The version of Task Groups introduced in [SE-0304](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0304-structured-concurrency.md) provides all of these features. However, it also provides the ability to propagate return values to the user of the task group. This capability provides an unexpected limitation in some use-cases. -As users of Task Groups are able to retrieve the return values of child tasks, it implicitly follows that the Task Group preserves at least the `Result` of any completed child task. As a practical matter, the task group actually preseves the entire `Task` object. This data is preserved until the user consumes it via one of the Task Group consumption APIs, whether that is `next()` or by iterating the Task Group. +As users of Task Groups are able to retrieve the return values of child tasks, it implicitly follows that the Task Group preserves at least the `Result` of any completed child task. As a practical matter, the task group actually preserves the entire `Task` object. This data is preserved until the user consumes it via one of the Task Group consumption APIs, whether that is `next()` or by iterating the Task Group. The result of this is that Task Groups are ill-suited to running for a potentially unbounded amount of time. An example of such a use-case is managing connections accepted from a listening socket. A simplified example of such a workload might be: @@ -103,7 +103,7 @@ public func withThrowingDiscardingTaskGroup( ) async throws -> GroupResult { ... } ``` -And the types themselfes, mostly mirroring the APIs of `TaskGroup`, except that they're missing `next()` and related functionality: +And the types themselves, mostly mirroring the APIs of `TaskGroup`, except that they're missing `next()` and related functionality: ```swift public struct DiscardingTaskGroup { diff --git a/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md index 6718630157..e7da438da2 100644 --- a/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md +++ b/proposals/0384-importing-forward-declared-objc-interfaces-and-protocols.md @@ -227,7 +227,7 @@ Issues: Issues: - It seems achievable to teach the typechecker that `IncompleteFoo.Foo` and `CompleteFoo.Foo` can be implicitly converted between one another, but that is not enough - - Any typechecking contraint that `CompleteFoo.Foo` satisfies must also be satisfied by `IncompleteFoo.Foo`. + - Any typechecking constraint that `CompleteFoo.Foo` satisfies must also be satisfied by `IncompleteFoo.Foo`. - It might be possible to "inject" an extension into the REPL's context that adds any required methods, computed properties and protocol conformances to `IncompleteFoo.Foo`. However, it is not possible to add new inheritances. We believe that given these issues, it is better to disable the feature in the REPL entirely rather than provide a confusing experience. We believe that this diff --git a/proposals/0389-attached-macros.md b/proposals/0389-attached-macros.md index b412eb2655..c3a20dfc6a 100644 --- a/proposals/0389-attached-macros.md +++ b/proposals/0389-attached-macros.md @@ -302,7 +302,7 @@ The `expansion` operation accepts the attribute syntax `node` for the spelling o #### Conformance macros -Conformance macros allow one to introduce new protocol conformances to a type. This would often be paired with other macros whose purpose is to help satisfy the protocol conformance. For example, one could imagine an extended version of the `OptionSetMembers` attributed shown earlier that also adds the `OptionSet` conformance. With it, the mimimal implementation of an option set could be: +Conformance macros allow one to introduce new protocol conformances to a type. This would often be paired with other macros whose purpose is to help satisfy the protocol conformance. For example, one could imagine an extended version of the `OptionSetMembers` attributed shown earlier that also adds the `OptionSet` conformance. With it, the minimal implementation of an option set could be: ```swift @OptionSet diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index af8925e9c4..97baac5ace 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -63,7 +63,7 @@ Nonetheless, it is sometimes useful to more finely control how code is executed: For example, some libraries maintain state in thread-local variables, and running code on the wrong thread will lead to broken assumptions in the library. - For another example, not all execution environments are homogenous; some threads may be pinned to processors with extra capabilities. + For another example, not all execution environments are homogeneous; some threads may be pinned to processors with extra capabilities. - The code's performance may benefit from the programmer being more explicit about where code should run. @@ -73,11 +73,11 @@ Nonetheless, it is sometimes useful to more finely control how code is executed: This is the first proposal discussing custom executors and customization points in the Swift Concurrency runtime, and while it introduces only the most basic customization points, we are certain that it already provides significant value to users seeking tighter control over their actor's execution semantics. -Along with introducing ways to customize where code executes, this proposal also introduces ways to assert and assume the apropriate executor is used. This allows for more confidence when migrating away from other concurrency models to Swift Concurrency. +Along with introducing ways to customize where code executes, this proposal also introduces ways to assert and assume the appropriate executor is used. This allows for more confidence when migrating away from other concurrency models to Swift Concurrency. ## Proposed solution -We propose to give developers the ability to implement simple serial executors, which then can be used with actors in order to ensure that any code executoring on such "actor with custom serial executor" runs on the apropriate thread or context. Implementing a naive executor takes the shape of: +We propose to give developers the ability to implement simple serial executors, which then can be used with actors in order to ensure that any code executoring on such "actor with custom serial executor" runs on the appropriate thread or context. Implementing a naive executor takes the shape of: ```swift final class SpecificThreadExecutor: SerialExecutor { @@ -114,9 +114,9 @@ actor Worker { } ``` -And lastly, in order to increase the confidence during moves from other concurrency models to Swift Concurrency with custom executors, we also provide ways to assert that a piece of code is executing on the apropriate executor. These methods should be used only if there is not better way to express the requirement statically. For example by expressing the code as a method on a specific actor, or annotating it with a `@GlobalActor`, should be preferred to asserting when possible, however sometimes this is not possible due to the old code fulfilling synchronous protocol requirements that still have these threading requirements. +And lastly, in order to increase the confidence during moves from other concurrency models to Swift Concurrency with custom executors, we also provide ways to assert that a piece of code is executing on the appropriate executor. These methods should be used only if there is not better way to express the requirement statically. For example by expressing the code as a method on a specific actor, or annotating it with a `@GlobalActor`, should be preferred to asserting when possible, however sometimes this is not possible due to the old code fulfilling synchronous protocol requirements that still have these threading requirements. -Asserting the apropriate executor is used in a synchronous piece of code looks like this: +Asserting the appropriate executor is used in a synchronous piece of code looks like this: ````swift func synchronousButNeedsMainActorContext() { @@ -128,7 +128,7 @@ func synchronousButNeedsMainActorContext() { } ```` -Furthermore, we also offer a new API to safely "assume" an actor's execution context. For example, a synchronous function may know that it always will be invoked by the `MainActor` however for some reason it cannot be marked using `@MainActor`, this new API allows to assume (or crash if called from another execution context) the apropriate execution context, including the safety of synchronously accessing any state protected by the main actor executor: +Furthermore, we also offer a new API to safely "assume" an actor's execution context. For example, a synchronous function may know that it always will be invoked by the `MainActor` however for some reason it cannot be marked using `@MainActor`, this new API allows to assume (or crash if called from another execution context) the appropriate execution context, including the safety of synchronously accessing any state protected by the main actor executor: ```swift @MainActor func example() {} @@ -407,7 +407,7 @@ public protocol DistributedActor: AnyActor { > Note: It is not possible to express this protocol requirement on `AnyActor` directly because `AnyActor` is a "marker protocol" which are not present at runtime, and cannot have protocol requirements. -The compiler synthesizes an implementation for this requirement for every `(distributed) actor` declaration, unless an explicit implementation is provided. The default implementation synthesized by the compiler uses the default `SerialExecutor`, that uses tha apropriate mechanism for the platform (e.g. Dispatch). Actors using this default synthesized implementation are referred to as "Default Actors", i.e. actors using the default serial executor implementation. +The compiler synthesizes an implementation for this requirement for every `(distributed) actor` declaration, unless an explicit implementation is provided. The default implementation synthesized by the compiler uses the default `SerialExecutor`, that uses tha appropriate mechanism for the platform (e.g. Dispatch). Actors using this default synthesized implementation are referred to as "Default Actors", i.e. actors using the default serial executor implementation. Developers can customize the executor used by an actor on a declaration-by-declaration basis, by implementing this protocol requirement in an actor. For example, thanks to the `sharedUnownedExecutor` static property on `MainActor` it is possible to declare other actors which are also guaranteed to use the same serial executor (i.e. "the main thread"). @@ -439,10 +439,10 @@ It is also possible for libraries to offer protocols where a default, library sp ```swift protocol WithSpecifiedExecutor: Actor { - nonisolated var executor: LibrarySecificExecutor { get } + nonisolated var executor: LibrarySpecificExecutor { get } } -protocol LibrarySecificExecutor: SerialExecutor {} +protocol LibrarySpecificExecutor: SerialExecutor {} extension LibrarySpecificActor { /// Establishes the WithSpecifiedExecutorExecutor as the serial @@ -480,7 +480,7 @@ A library could also provide a default implementation of such executor as well. ### Asserting on executors -A common pattern in event-loop heavy code–not yet using Swift Concurrency–is to ensure/verify that a synchronous piece of code is executed on the exected event-loop. Since one of the goals of making executors customizable is to allow such libraries to adopt Swift Concurrency by making such event-loops conform to `SerialExecutor`, it is useful to allow the checking if code is indeed executing on the apropriate executor, for the library to gain confidence while it is moving towards fully embracing actors and Swift concurrency. +A common pattern in event-loop heavy code–not yet using Swift Concurrency–is to ensure/verify that a synchronous piece of code is executed on the exected event-loop. Since one of the goals of making executors customizable is to allow such libraries to adopt Swift Concurrency by making such event-loops conform to `SerialExecutor`, it is useful to allow the checking if code is indeed executing on the appropriate executor, for the library to gain confidence while it is moving towards fully embracing actors and Swift concurrency. For example, Swift NIO intentionally avoids synchronization checks in some synchronous methods, in order to avoid the overhead of doing so, however in DEBUG mode it performs assertions that given code is running on the expected event-loop: @@ -579,7 +579,7 @@ MainActor.preconditionIsolated() // Precondition failed: Incorrect actor executor assumption; Expected 'MainActorExecutor' executor, but was executing on 'Sample.InlineExecutor'. ```` -It should be noted that this API will return true whenever two actors share an executor. Semantically sharing a serial executor means running in the same isolation domain, however this is only known dynamically and `await`s are stil necessary for calls between such actors: +It should be noted that this API will return true whenever two actors share an executor. Semantically sharing a serial executor means running in the same isolation domain, however this is only known dynamically and `await`s are still necessary for calls between such actors: ```swift actor A { @@ -635,7 +635,7 @@ extension MainActor { } ``` -Similarily to the `preconditionIsolated` API, the executor check is performed against the target actor's executor, so if multiple actors are run on the same executor, this check will succeed in synchronous code invoked by such actors as well. In other words, the following code is also correct: +Similarly to the `preconditionIsolated` API, the executor check is performed against the target actor's executor, so if multiple actors are run on the same executor, this check will succeed in synchronous code invoked by such actors as well. In other words, the following code is also correct: ```swift func check(values: MainActorValues) /* synchronous! */ { @@ -894,7 +894,7 @@ actor Friend { Note that the raw type of the MainActor executor is never exposed, but we merely get unowned wrappers for it. This allows the Swift runtime to pick various specific implementations depending on the runtime environment. -The default global concurrent executor is not accessible direcly from code, however it is the executor that handles all the tasks which do not have a specific executor requirement, or are explicitly required to run on that executor, e.g. like top-level async functions. +The default global concurrent executor is not accessible directly from code, however it is the executor that handles all the tasks which do not have a specific executor requirement, or are explicitly required to run on that executor, e.g. like top-level async functions. ## Source compatibility @@ -908,7 +908,7 @@ Swift's concurrency runtime has already been using executors, jobs and tasks sin The design of `SerialExecutor` currently does not support non-reentrant actors, and it does not support executors for which dispatch is always synchronous (e.g. that just acquire a traditional mutex). -Some of the APIs discussed in this proposal existed from the first introduction of Swift Concurrency, so making any breaking changes to them is not possible. Some APIs were carefully renamed and polished up though. We encourage discussion of all the types and methods present in this proposal, however changing some of them may prove to be challanging or impossible due to ABI impact. +Some of the APIs discussed in this proposal existed from the first introduction of Swift Concurrency, so making any breaking changes to them is not possible. Some APIs were carefully renamed and polished up though. We encourage discussion of all the types and methods present in this proposal, however changing some of them may prove to be challenging or impossible due to ABI impact. ## Effect on API resilience @@ -987,7 +987,7 @@ We will consider adding these, or similar, APIs to enable custom executors to pa ### Specifying Task executors -Specifying executors to tasks has a suprising number of tricky questions it has to answer, so for the time being we are not introducing such capability. Specifically, passing an executor to `Task(startingOn: someExecutor) { ... }` would make the Task _start_ on the specified executor, but detailed semantics about if the _all_ of this Task's body is expected to execute on `someExecutor` (i.e. we have to hop-back to it every time after an `await`), or if it is enough to just start on it and then continue avoiding scheduling more jobs if possible (i.e. allow for aggressive switching). +Specifying executors to tasks has a surprising number of tricky questions it has to answer, so for the time being we are not introducing such capability. Specifically, passing an executor to `Task(startingOn: someExecutor) { ... }` would make the Task _start_ on the specified executor, but detailed semantics about if the _all_ of this Task's body is expected to execute on `someExecutor` (i.e. we have to hop-back to it every time after an `await`), or if it is enough to just start on it and then continue avoiding scheduling more jobs if possible (i.e. allow for aggressive switching). ### DelegateActor property diff --git a/proposals/0400-init-accessors.md b/proposals/0400-init-accessors.md index 95bd502cde..852421b0fd 100644 --- a/proposals/0400-init-accessors.md +++ b/proposals/0400-init-accessors.md @@ -597,7 +597,7 @@ Other syntax suggestions from pitch reviewers included: * Using more concise effect names, e.g. `writes` and `reads` instead of `initializes` and `accesses` * And more! -However, the current syntax in this proposal, which uses an attribute, most accurately models the semantics of initialization effects. An `init` accessor is a function -- not a closure -- that has side-effects related to initialization. _Only_ the `init` accessor has these effects; though the `set` accessor often contains code that looks the same as the code in the `init` accessor, the effects of these accessors are different. Because `init` accessors are called before all of `self` is initialized, they do not recieve a fully-initialized `self` as a parameter like `set` accessors do, and assignments to `initializes` stored properties in `init` accessors have the same semantics as that of a standard initializer, such as suppressing `willSet` and `didSet` observers. +However, the current syntax in this proposal, which uses an attribute, most accurately models the semantics of initialization effects. An `init` accessor is a function -- not a closure -- that has side-effects related to initialization. _Only_ the `init` accessor has these effects; though the `set` accessor often contains code that looks the same as the code in the `init` accessor, the effects of these accessors are different. Because `init` accessors are called before all of `self` is initialized, they do not receive a fully-initialized `self` as a parameter like `set` accessors do, and assignments to `initializes` stored properties in `init` accessors have the same semantics as that of a standard initializer, such as suppressing `willSet` and `didSet` observers. ## Future directions diff --git a/proposals/0402-extension-macros.md b/proposals/0402-extension-macros.md index b47ef4ee4a..80513bae90 100644 --- a/proposals/0402-extension-macros.md +++ b/proposals/0402-extension-macros.md @@ -70,7 +70,7 @@ SE-0389 states that whenever a macro produces declarations that are visible to o * The names of protocols that are listed in the extension's conformance clause. These protocols are specified in the `conformances:` list of the `@attached(conformances:)` attribute. Each name that appears in this list must be a conformance constraint, where a conformance constraint is one of: * A protocol name * A typealias whose underlying type is a conformance constraint - * A protocol composition whose entires are each a conformance constraint + * A protocol composition whose entries are each a conformance constraint The following restrictions apply to generated conformances and names listed in `@attached(extension)`: diff --git a/proposals/0403-swiftpm-mixed-language-targets.md b/proposals/0403-swiftpm-mixed-language-targets.md index 7153cfe920..c339f2e424 100644 --- a/proposals/0403-swiftpm-mixed-language-targets.md +++ b/proposals/0403-swiftpm-mixed-language-targets.md @@ -455,7 +455,7 @@ the public headers directory: The compiler assumes that the above path can be resolved relative to the public header directory. Instead of forcing package authors to structure their packages around that constraint, the Swift compiler's interop header generation -logic will be ammended to do the following in such cases where the target +logic will be amended to do the following in such cases where the target does not have the public headers directory structure of an xcframework: - If an umbrella header that is modularized by the Clang module exists, the @@ -536,11 +536,11 @@ feature will continue to [throw an error][mixed-target-error]. ## Future Directions - Enable package authors to expose non-public headers to their mixed - target's Swift implemention. + target's Swift implementation. - Extend mixed language target support to currently unsupported types of targets (e.g. executables). - Extend this solution so that all targets are mixed language targets by - default. This could simplify the implemention as language-specific types + default. This could simplify the implementation as language-specific types like `ClangTarget`, `SwiftTarget`, and `MixedTarget` could be consolidated into a single type. This approach was avoided in the initial implementation of this feature to reduce the risk of introducing a regression. diff --git a/proposals/0406-async-stream-backpressure.md b/proposals/0406-async-stream-backpressure.md index cb9f4722c2..58817b3ede 100644 --- a/proposals/0406-async-stream-backpressure.md +++ b/proposals/0406-async-stream-backpressure.md @@ -664,7 +664,7 @@ This change is additive and does not affect source compatibility. ## ABI compatibility This change is additive and does not affect ABI compatibility. All new methods -are non-inlineable leaving us flexiblity to change the implementation in the +are non-inlineable leaving us flexibility to change the implementation in the future. ## Future directions diff --git a/proposals/0410-atomics.md b/proposals/0410-atomics.md index ea54a07fac..21d348b3a2 100644 --- a/proposals/0410-atomics.md +++ b/proposals/0410-atomics.md @@ -130,7 +130,7 @@ That said, it seems highly undesirable to add low-level atomics to the default n import Synchronization ``` -We expect that most Swift projects will use atomic operations only indirectly, through higher-level synchronization constructs. Therefore, importing the `Synchronization` module will be a relatively rare occurrance, mostly limited to projects that implement such tools. +We expect that most Swift projects will use atomic operations only indirectly, through higher-level synchronization constructs. Therefore, importing the `Synchronization` module will be a relatively rare occurrence, mostly limited to projects that implement such tools. ### Atomic Memory Orderings @@ -296,7 +296,7 @@ extension Optional: AtomicRepresentable where Wrapped: AtomicOptionalRepresentab * `UnsafeMutableBufferPointer` * `UnsafeRawBufferPointer` * `UnsafeMutableRawBufferPointer` -* On 64 bit plaforms that do not support double-word atomics, the following conformances are not available: +* On 64 bit platforms that do not support double-word atomics, the following conformances are not available: * `Duration` * `UnsafeBufferPointer` * `UnsafeMutableBufferPointer` @@ -1082,7 +1082,7 @@ let myAtomic = makeAnAtomic() -In the same vein, these types must never be passed as `inout` parameters as that declares that the callee has exclusive access to the atomic, which would make the access no longer atomic. Attemping to create an `inout` binding for an atomic variable is also a compile-time error. Parameters that are used to pass `Atomic` values must either be `borrowing` or `consuming`. (Passing a variable as `consuming` is also an exclusive access, but it's destroying the original variable, so we no longer need to care for its atomicity.) +In the same vein, these types must never be passed as `inout` parameters as that declares that the callee has exclusive access to the atomic, which would make the access no longer atomic. Attempting to create an `inout` binding for an atomic variable is also a compile-time error. Parameters that are used to pass `Atomic` values must either be `borrowing` or `consuming`. (Passing a variable as `consuming` is also an exclusive access, but it's destroying the original variable, so we no longer need to care for its atomicity.) ```swift // error: parameter of type 'Atomic' must be declared as either 'borrowing' or 'consuming' @@ -1793,7 +1793,7 @@ While there are some API differences between this proposal and the package, most Previous revisions of this proposal named this type `DoubleWord`. This is a good name and is in fact the name used in the `swift-atomics` package. We felt the prefix `Double*` could cause confusion with the pre-existing type in the standard library `Double`. The name `WordPair` has a couple of advantages: -1. Code completion. Because this name starts with an less common letter in the English alphabet, the likelyhood of seeing this type at the top level in code completion is very unlikely and not generally a type used for newer programmers of Swift. +1. Code completion. Because this name starts with an less common letter in the English alphabet, the likelihood of seeing this type at the top level in code completion is very unlikely and not generally a type used for newer programmers of Swift. 2. Directly conveys the semantic meaning of the type. This type is not semantically equivalent to something like `{U}Int128` (on 64 bit platforms). While although its layout shares the same size, the meaning we want to drive home with this type is quite simply that it's a pair of `UInt` words. If and when the standard library proposes a `{U}Int128` type, that will add a conformance to `AtomicRepresentable` on 64 bit platforms who support double-words as well. That itself wouldn't deprecate uses of `WordPair` however, because it's much easier to grab both words independently with `WordPair` as well as being a portable name for such semantics on both 32 bit and 64 bit platforms. ### A different name for the `Synchronization` module diff --git a/proposals/0413-typed-throws.md b/proposals/0413-typed-throws.md index b5184677cf..9ee9502297 100644 --- a/proposals/0413-typed-throws.md +++ b/proposals/0413-typed-throws.md @@ -1102,7 +1102,7 @@ With the `rethrows` formulation of the `last(where:)` function, `getLast` will h let getLast: ((Int) -> Bool) -> Int? = primes.last(where:) // okay, E is inferred to Never ``` -Note that one would have to do the same thing with the `rethrows` formulation to produce a non-throwing `getLast`, because `rethrows` is not a part of the formal type system. Given that most `rethrows` operations are already generic in other parameters (unlike `last(where:)`), and most uses of such APIs are either calls or have type context, it is expected that the actual source compatibilty impact of replacing `rethrows` with typed errors will be small. +Note that one would have to do the same thing with the `rethrows` formulation to produce a non-throwing `getLast`, because `rethrows` is not a part of the formal type system. Given that most `rethrows` operations are already generic in other parameters (unlike `last(where:)`), and most uses of such APIs are either calls or have type context, it is expected that the actual source compatibility impact of replacing `rethrows` with typed errors will be small. ## Effect on ABI stability @@ -1356,7 +1356,7 @@ This function will only throw when `f` or `g` throw, and in both cases will tran * `rethrows` correctly communicates that this function throws only when the arguments for `f` or `g` do, but the thrown error type is treated as `any Error`. * `throws(SimpleError)` correctly communicates that this function throws errors of type `SimpleError`, but not that it throws when the argument for `f` or `g` do. -One way to address this would be to allow `rethrows` to specify the thrown error type, e.g., `rethrows(SimpleError)`, which captures both of the aspects of how this function behavies---when it throws, and what specific error type it `throws`. +One way to address this would be to allow `rethrows` to specify the thrown error type, e.g., `rethrows(SimpleError)`, which captures both of the aspects of how this function behaves---when it throws, and what specific error type it `throws`. With typed `rethrows`, a bare `rethrows` could be treated as syntactic sugar for `rethrows(any Error)`, similarly to how `throws` is syntactic sugar for `throws(any Error)`. This extension is source-compatible and allows one to express more specific error types with throwing behavior. diff --git a/proposals/0414-region-based-isolation.md b/proposals/0414-region-based-isolation.md index f3f64f9cd4..381b2176fa 100644 --- a/proposals/0414-region-based-isolation.md +++ b/proposals/0414-region-based-isolation.md @@ -853,7 +853,7 @@ longer isolated to the function since: own that the non-`Sendable` value could escape into. * Parameters in a task isolated isolation region cannot be transferred into a - different isolation domain that does have persistant isolated state. + different isolation domain that does have persistent isolated state. Thus the value in the caller's region again becomes disconnected once more and thus can be used after the function returns and be transferred again: @@ -1649,7 +1649,7 @@ because: other value will not cause `x`'s fields to point to different values. 2. When `x` is transferred to a callee, `x` will be passed by value. Thus the - callee will recieve a completely new value type albeit with copied + callee will receive a completely new value type albeit with copied fields. This means that if the callee attempts to modify the value, it will be modifying the new value instead of our caller value implying that we cannot race against any assignment when accessing the field in our diff --git a/proposals/0415-function-body-macros.md b/proposals/0415-function-body-macros.md index 57f2f54767..12797b0a2f 100644 --- a/proposals/0415-function-body-macros.md +++ b/proposals/0415-function-body-macros.md @@ -287,7 +287,7 @@ func myMath(a: Int, b: Int) -> Int { } ``` -The advantage of this approach over allowing a `body` macro to replace a body is that we can type-check the function body as it was written, and only need to do so once---then it becomes a value of function type that's passed along to the underying macro. Also like preamble macros, this approach can compose, because the result of one macro could produce another value of function type that can be passed along to another macro. [Python decorators](https://www.datacamp.com/tutorial/decorators-python) have been successful in that language for customizing the behavior of functions in a similar manner. +The advantage of this approach over allowing a `body` macro to replace a body is that we can type-check the function body as it was written, and only need to do so once---then it becomes a value of function type that's passed along to the underlying macro. Also like preamble macros, this approach can compose, because the result of one macro could produce another value of function type that can be passed along to another macro. [Python decorators](https://www.datacamp.com/tutorial/decorators-python) have been successful in that language for customizing the behavior of functions in a similar manner. ## Alternatives considered diff --git a/proposals/0417-task-executor-preference.md b/proposals/0417-task-executor-preference.md index 3c1d6b6634..9f0d5848fd 100644 --- a/proposals/0417-task-executor-preference.md +++ b/proposals/0417-task-executor-preference.md @@ -272,7 +272,7 @@ Task(executorPreference: specialExecutor) { return 12 } group.addTask(executorPreference: differentExecutor) { - // using 'differentExecutor', overriden preference + // using 'differentExecutor', overridden preference return 42 } group.addTask(executorPreference: nil) { diff --git a/proposals/0421-generalize-async-sequence.md b/proposals/0421-generalize-async-sequence.md index 42df2bab92..c0c19c4550 100644 --- a/proposals/0421-generalize-async-sequence.md +++ b/proposals/0421-generalize-async-sequence.md @@ -103,7 +103,7 @@ The new `next(isolation:)` has a default implementation so that conformances wil ### Adopting typed throws -Concrete `AsyncSequence` and `AsyncIteratorProtocol` types determine whether calling `next()` can `throw`. This can be described in each protocol with a `Failure` associated type that is thrown by the `AsyncIteratorProtcol.next(isolation:)` requirement. Describing the thrown error with an associated type allows conformances to fulfill the requirement with a type parameter, which means that libraries do not need to expose separate throwing and non-throwing concrete types that otherwise have the same async iteration functionality. +Concrete `AsyncSequence` and `AsyncIteratorProtocol` types determine whether calling `next()` can `throw`. This can be described in each protocol with a `Failure` associated type that is thrown by the `AsyncIteratorProtocol.next(isolation:)` requirement. Describing the thrown error with an associated type allows conformances to fulfill the requirement with a type parameter, which means that libraries do not need to expose separate throwing and non-throwing concrete types that otherwise have the same async iteration functionality. #### Error type inference from `for try await` loops @@ -213,7 +213,7 @@ Both function requirements of `AsyncIteratorProtocol` have default implementatio To avoid silently allowing conformances that implement neither requirement, and to facilitate the transition of conformances from `next()` to `next(isolation:)`, we add a new availability rule where the witness checker diagnoses a protocol conformance that uses an deprecated, obsoleted, or unavailable default witness implementation. Deprecated implementations will produce a warning, while obsoleted and unavailable implementations will produce an error. -Because the default implementation of `next(isolation:)` is deprecated, conformances that do not provide a direct implementation will produce a warning. This is desirable because the default implementation of `next(isolation:)` violates `Sendable` checking, so while it's necessary for source compatibilty, it's important to aggressively suggest that conforming types implement the new method. +Because the default implementation of `next(isolation:)` is deprecated, conformances that do not provide a direct implementation will produce a warning. This is desirable because the default implementation of `next(isolation:)` violates `Sendable` checking, so while it's necessary for source compatibility, it's important to aggressively suggest that conforming types implement the new method. ### Associated type inference for `AsyncIteratorProtocol` conformances diff --git a/proposals/0423-dynamic-actor-isolation.md b/proposals/0423-dynamic-actor-isolation.md index 1d4b1b08b3..fb7215418e 100644 --- a/proposals/0423-dynamic-actor-isolation.md +++ b/proposals/0423-dynamic-actor-isolation.md @@ -163,7 +163,7 @@ This feature can be freely adopted and un-adopted in source code with no deploym ### Always emit dynamic checks upon entry to synchronous isolated functions -A previous iteration of this proposal specified that dynamic actor isolation checks are always emitted upon entry to a synchronous isolated function. This approach is foolproof; there's little possiblity for missing a dynamic check for code that can be called from another module that does not have strict concurrency checking at compile time. However, the major downside of this approach is that code will be paying the price of runtime overhead for actor isolation checking even when actor isolation is fully enforced at compile time in Swift 6. +A previous iteration of this proposal specified that dynamic actor isolation checks are always emitted upon entry to a synchronous isolated function. This approach is foolproof; there's little possibility for missing a dynamic check for code that can be called from another module that does not have strict concurrency checking at compile time. However, the major downside of this approach is that code will be paying the price of runtime overhead for actor isolation checking even when actor isolation is fully enforced at compile time in Swift 6. The current approach in this proposal has a very desirable property of eliminated more runtime overhead as more of the Swift ecosystem transitions to Swift 6 at the cost of introducing the potential for missing dynamic checks where synchronous functions can be called from not-statically-checked code. We believe this is the right tradeoff for the long term arc of data race safety in Swift 6 and beyond, but it may require more special cases when we discover code patterns that are not covered by the specific set of rules in this proposal. diff --git a/proposals/0424-custom-isolation-checking-for-serialexecutor.md b/proposals/0424-custom-isolation-checking-for-serialexecutor.md index 3bc0b1e290..d04cc3d7fc 100644 --- a/proposals/0424-custom-isolation-checking-for-serialexecutor.md +++ b/proposals/0424-custom-isolation-checking-for-serialexecutor.md @@ -140,7 +140,7 @@ extension DispatchSerialQueue { An executor that wishes to take advantage of this proposal will need to have some mechanism to identity its active worker thread. If that's not possible or desired, the executor should leave the default implementation (that unconditionally crashes) in place. -### Impact on async code and isolation assumtions +### Impact on async code and isolation assumptions The `assumeIsolated(_:file:line:)` APIs purposefully only accept a **synchronous** closure. This is correct, and it remains correct with these proposed additions. An isolation check on an executor ensures that any actor using the executor is synchronously isolated, and the closure provided to `assumeIsolated` will execute prior to any possible async suspension. This is what makes it safe to access actor-isolated state within the closure. diff --git a/proposals/0426-bitwise-copyable.md b/proposals/0426-bitwise-copyable.md index 9c8d5a008d..f057c4dd5e 100644 --- a/proposals/0426-bitwise-copyable.md +++ b/proposals/0426-bitwise-copyable.md @@ -351,7 +351,7 @@ This would be visible to the programmer in several ways: - different overloads would be selected for a value of concrete type from those selected for a value dynamically cast to `BitwiseCopyable` - dynamic casts to `BitwiseCopyable` could fail, then succeed, then fail again in successive OS versions -On the other hand, these behavioral differences may be desireable. +On the other hand, these behavioral differences may be desirable. Considering that this approach would just ignore the existence of conformances to `BitwiseCopyable`, it would be reasonable to ignore the existence of a suppressed conformance as well. diff --git a/proposals/0428-resolve-distributed-actor-protocols.md b/proposals/0428-resolve-distributed-actor-protocols.md index 4dd61d0d8b..52a6964f52 100644 --- a/proposals/0428-resolve-distributed-actor-protocols.md +++ b/proposals/0428-resolve-distributed-actor-protocols.md @@ -412,7 +412,7 @@ extension Greeter { This simplified snippet does not solve the problem about introducing the new protocol requirement in a binary compatible way, and we'd have to come up with some pattern for it -- however the general direction of allowing introducing new versions of APIs with easier deprecation of old ones is something we'd like to explore in the future. -Support for renaming methods could also be provided, such that the legacy method can be called `__deprecated_greet()` for example, while maintaining the "legacy name" of "`greet()`". Overall, we believe that the protocol evolution story here is somethign we will have to flesh out in the near future, anf feel we have the tools to do so. +Support for renaming methods could also be provided, such that the legacy method can be called `__deprecated_greet()` for example, while maintaining the "legacy name" of "`greet()`". Overall, we believe that the protocol evolution story here is something we will have to flesh out in the near future, anf feel we have the tools to do so. ### Consider customization points for distributed call target metadata assignment diff --git a/proposals/0433-mutex.md b/proposals/0433-mutex.md index 30662cf070..f98a9cef27 100644 --- a/proposals/0433-mutex.md +++ b/proposals/0433-mutex.md @@ -86,7 +86,7 @@ Below is the complete API design for the new `Mutex` type: /// class Manager { /// let cache = Mutex<[Key: Resource]>([:]) /// -/// func saveResouce(_ resource: Resouce, as key: Key) { +/// func saveResource(_ resource: Resource, as key: Key) { /// cache.withLock { /// $0[key] = resource /// } @@ -177,7 +177,7 @@ extension Mutex where State: ~Copyable { ## Interaction with Existing Language Features -`Mutex` will be decorated with the `@_staticExclusiveOnly` attribute, meaning you will not be able to declare a variable of type `Mutex` as `var`. These are the same restrictions imposed on the recently accepted `Atomic` and `AtomicLazyReference` types. Please refer to the [Atomics proposal](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0410-atomics.md) for a more in-depth discussion on what is allowed and not allowed. These restrictions are enabled for `Mutex` for all of the same reasons why it was resticted for `Atomic`. We do not want to introduce dynamic exclusivity checking when accessing a value of `Mutex` as a class stored property for instance. +`Mutex` will be decorated with the `@_staticExclusiveOnly` attribute, meaning you will not be able to declare a variable of type `Mutex` as `var`. These are the same restrictions imposed on the recently accepted `Atomic` and `AtomicLazyReference` types. Please refer to the [Atomics proposal](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0410-atomics.md) for a more in-depth discussion on what is allowed and not allowed. These restrictions are enabled for `Mutex` for all of the same reasons why it was restricted for `Atomic`. We do not want to introduce dynamic exclusivity checking when accessing a value of `Mutex` as a class stored property for instance. ### Interactions with Swift Concurrency @@ -224,7 +224,7 @@ By marking the closure as such, we've effectively declared that the mutex is in The mutex type we're proposing is a synchronous lock. This means when other participants want to acquire the lock to access the protected shared data, they will halt execution until they are able to do so. Threads that are waiting to acquire the lock will not be able to make forward progress until their request to acquire the lock has completed. This can lead to thread contention if the acquired thread's critical section is not able to be executed relatively quickly exhausting resources for the rest of the system to continue making forward progress. Synchronous locks are also prone to deadlocks (which Swift's actors cannot currently encounter due to their re-entrant nature) and live-locks which can leave a process in an unrecoverable state. These scenarios can occur when there is a complex hierarchy of different locks that manage to depend on the acquisition of each other. -Actors work very differently. Typical use of an actor doesn't request access to underlying shared data, but rather instruct the actor to perform some operation or service that has exclusive access to that data. An execution context making this request may need to await on the return value of that operation, but with Swift's `async`/`await` model it can immediately start doing other work allowing it to make forward progress on other tasks. The actor executes requests in a serial fashion in the order they are made. This ensures that the shared mutable state is only accessed by the actor. Deadlocks are not possible with the actor model. Asynchronous code that is dependent on a specific operation and resouce from an actor can be later resumed once the actor has serviced that request. While deadlocking is not possible, there are other problems actors have such as the actor reentrancy problem where the state of the actor has changed when the executing operation got resumed after a suspension point. +Actors work very differently. Typical use of an actor doesn't request access to underlying shared data, but rather instruct the actor to perform some operation or service that has exclusive access to that data. An execution context making this request may need to await on the return value of that operation, but with Swift's `async`/`await` model it can immediately start doing other work allowing it to make forward progress on other tasks. The actor executes requests in a serial fashion in the order they are made. This ensures that the shared mutable state is only accessed by the actor. Deadlocks are not possible with the actor model. Asynchronous code that is dependent on a specific operation and resource from an actor can be later resumed once the actor has serviced that request. While deadlocking is not possible, there are other problems actors have such as the actor reentrancy problem where the state of the actor has changed when the executing operation got resumed after a suspension point. Mutexes and actors are very different synchronization tools that help protect shared mutable state. While they can both achieve synchronization of that data access, they do so in varying ways that may be desirable for some and undesirable for others. The proposed `Mutex` is yet another primitive that Swift should expose to help those achieve concurrency safe programs in cases where actors aren't suitable. diff --git a/proposals/0438-metatype-keypath.md b/proposals/0438-metatype-keypath.md index 2cf3efbf4b..94b2bdb954 100644 --- a/proposals/0438-metatype-keypath.md +++ b/proposals/0438-metatype-keypath.md @@ -116,7 +116,7 @@ This feature is back-deployable but it requires emission of new (property descri The type-checker wouldn't allow to form key paths to static properties of types that come from modules that are built by an older compiler that don't support the feature because dynamic or static library produced for such module won't have all of the required symbols. -Attempting to form a key path to a static property of a type from a module compiled with a complier that doesn't yet support the feature will result in the following error with a note to help the developers: +Attempting to form a key path to a static property of a type from a module compiled with a compiler that doesn't yet support the feature will result in the following error with a note to help the developers: ```swift error: cannot form a keypath to a static property of type diff --git a/proposals/0440-debug-description-macro.md b/proposals/0440-debug-description-macro.md index bfda4c3771..e374da0f85 100644 --- a/proposals/0440-debug-description-macro.md +++ b/proposals/0440-debug-description-macro.md @@ -212,7 +212,7 @@ A similar future direction is to support sharing Swift `customMirror` definition ### Explicit LLDB Summary Strings -The simplest macro implementation is one that performs no Swift-to-LLDB translation and directly accepts an LLDB Summary String. This approach requires users to know LLDB Summary String syntax, which while not complex, still presents a hinderance to adoption. Such a macro would could create redundancy: `debugDescription` and the separate LLDB Summary String. These would need to be manually kept in sync. +The simplest macro implementation is one that performs no Swift-to-LLDB translation and directly accepts an LLDB Summary String. This approach requires users to know LLDB Summary String syntax, which while not complex, still presents a hindrance to adoption. Such a macro would could create redundancy: `debugDescription` and the separate LLDB Summary String. These would need to be manually kept in sync. ### Independent Property (No `debugDescription` Reuse) diff --git a/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md b/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md index 1abe4d92f3..1e10c07770 100644 --- a/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md +++ b/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md @@ -47,7 +47,7 @@ public func withTaskGroup( The function signature of `withThrowingTaskGroup(of:returning:body:)` is nearly identical, so only `withTaskGroup(of:returning:body:)` will be used as an example throughout this proposal. -Note that the `GroupResult` generic is inferrable via the `= GroupResult.self` default argument. This can also be applied to `ChildTaskResult` as of [SE-0326](0326-extending-multi-statement-closure-inference.md). As in: +Note that the `GroupResult` generic is inferable via the `= GroupResult.self` default argument. This can also be applied to `ChildTaskResult` as of [SE-0326](0326-extending-multi-statement-closure-inference.md). As in: ```swift public func withTaskGroup( From aa207ff10acdc8062e115dcb8bc6dfd6eee9061a Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 21 Aug 2024 11:51:58 -0700 Subject: [PATCH 3825/4563] Change function signatures of `expansion` functions of macros to be synchronous (#2546) The different macro proposals have been inconsistent about whether the expansion function should be synchronous or asynchronous. - Synchronous - SE-0415 Function Body Macros - SE-0402 Extension Macros - SE-0407 Member Macro Conformances - SE-0397 Freestanding Declaration Macros (but future directions uses `async` function) - Async - SE-0389 Attached Macros - SE-0382 Expression Macros swift-syntax has always implemented synchronous expansion functions. Update the proposals to consistently use synchronous expansion functions to reflect that. --- proposals/0382-expression-macros.md | 8 ++++---- proposals/0389-attached-macros.md | 10 ++++++---- proposals/0397-freestanding-declaration-macros.md | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index c6ba68d150..0d51d494da 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -92,7 +92,7 @@ public protocol ExpressionMacro: FreestandingMacro { static func expansion( of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext - ) async throws -> ExprSyntax + ) throws -> ExprSyntax } ``` @@ -250,7 +250,7 @@ public protocol ExpressionMacro: FreestandingMacro { static func expansion( of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext - ) async throws -> ExprSyntax + ) throws -> ExprSyntax } ``` @@ -260,8 +260,6 @@ Macro definitions should conform to the `ExpressionMacro` protocol and implement If the macro expansion cannot proceed for some reason, the `expansion(of:in:)` operation can throw an error rather than try to produce a new syntax node. The compiler will then report the error to the user. More detailed diagnostics can be provided via the macro expansion context. -The macro expansion operation is asynchronous, to account for potentially-asynchronous operations that will eventually be added to `MacroExpansionContext`. For example, operations that require additional communication with the compiler to get types of subexpressions, access files in the program, and so on. - #### `MacroExpansionContext` The macro expansion context provides additional information about the environment in which the macro is being expanded. This context can be queried as part of the macro expansion: @@ -567,6 +565,8 @@ Expressions are just one place in the language where macros could be valuable. O ## Revision History +* Revision after acceptance: + * Make the `ExpressionMacro.expansion(of:in:)` requirement non-`async`. * Revisions based on review feedback: * Switch `@expression` to `@freestanding(expression)` to align with the other macros proposals and vision document. * Make the `ExpressionMacro.expansion(of:in:)` requirement `async`. diff --git a/proposals/0389-attached-macros.md b/proposals/0389-attached-macros.md index b412eb2655..3c31ef94e8 100644 --- a/proposals/0389-attached-macros.md +++ b/proposals/0389-attached-macros.md @@ -71,7 +71,7 @@ public PeerMacro: AttachedMacro { of node: AttributeSyntax, providingPeersOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext - ) async throws -> [DeclSyntax] + ) throws -> [DeclSyntax] } ``` @@ -163,7 +163,7 @@ protocol MemberMacro: AttachedMacro { of node: AttributeSyntax, providingMembersOf declaration: some DeclGroupSyntax, in context: some MacroExpansionContext - ) async throws -> [DeclSyntax] + ) throws -> [DeclSyntax] } ``` @@ -232,7 +232,7 @@ protocol AccessorMacro: AttachedMacro { of node: AttributeSyntax, providingAccessorsOf declaration: some DeclSyntaxProtocol, in context: some MacroExpansionContext - ) async throws -> [AccessorDeclSyntax] + ) throws -> [AccessorDeclSyntax] } ``` @@ -294,7 +294,7 @@ protocol MemberAttributeMacro: AttachedMacro { attachedTo declaration: some DeclGroupSyntax, providingAttributesOf member: some DeclSyntaxProtocol, in context: some MacroExpansionContext - ) async throws -> [AttributeSyntax] + ) throws -> [AttributeSyntax] } ``` @@ -660,6 +660,8 @@ It might be possible to provide a macro implementation API that is expressed in ## Revision History +* Revision after acceptance: + * Make the `expansion` requirements non-`async`. * After the first pitch: * Added conformance macros, to produce conformances * Moved the discussion of macro-introduced names from the freestanding macros proposal here. diff --git a/proposals/0397-freestanding-declaration-macros.md b/proposals/0397-freestanding-declaration-macros.md index 7e005623fa..ab59886f02 100644 --- a/proposals/0397-freestanding-declaration-macros.md +++ b/proposals/0397-freestanding-declaration-macros.md @@ -323,7 +323,7 @@ public protocol CodeItemMacro: FreestandingMacro { static func expansion( of node: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext - ) async throws -> [CodeBlockItemSyntax] + ) throws -> [CodeBlockItemSyntax] } ``` From d54d862a37dda0dc7783748439c5e661cb57e7b6 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 21 Aug 2024 14:57:31 -0400 Subject: [PATCH 3826/4563] Update SE-0382 with link to post-acceptance update (#2549) --- proposals/0382-expression-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0382-expression-macros.md b/proposals/0382-expression-macros.md index 0d51d494da..e3a33eb558 100644 --- a/proposals/0382-expression-macros.md +++ b/proposals/0382-expression-macros.md @@ -5,7 +5,7 @@ * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Implemented (Swift 5.9)** * Implementation: Partial implementation is available in `main` under the experimental feature flag `Macros`. An [example macro repository](https://github.com/DougGregor/swift-macro-examples) provides a way to experiment with this feature. -* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861)) ([review #1](https://forums.swift.org/t/se-0382-expression-macros/62090)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0382-expression-macros/62898)) ([pitch #3](https://forums.swift.org/t/se-0382-expression-macros-mini-pitch-for-updates/62810)) ([review #2](https://forums.swift.org/t/se-0382-second-review-expression-macros/63064)) ([acceptance](https://forums.swift.org/t/accepted-se-0382-expression-macros/63495)) +* Review: ([pitch](https://forums.swift.org/t/pitch-expression-macros/61499)) ([pitch #2](https://forums.swift.org/t/pitch-2-expression-macros/61861)) ([review #1](https://forums.swift.org/t/se-0382-expression-macros/62090)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0382-expression-macros/62898)) ([pitch #3](https://forums.swift.org/t/se-0382-expression-macros-mini-pitch-for-updates/62810)) ([review #2](https://forums.swift.org/t/se-0382-second-review-expression-macros/63064)) ([acceptance](https://forums.swift.org/t/accepted-se-0382-expression-macros/63495)) ([post-acceptance update](https://forums.swift.org/t/update-on-se-0382-and-se-0389-expression-macros-and-attached-macros/74094)) ## Introduction From 5979e113283a31a47ec45d7fd5729a2954ba3d61 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 21 Aug 2024 14:58:22 -0400 Subject: [PATCH 3827/4563] Update SE-0389 to link post-acceptance update --- proposals/0389-attached-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0389-attached-macros.md b/proposals/0389-attached-macros.md index 3c31ef94e8..f56a20a4a9 100644 --- a/proposals/0389-attached-macros.md +++ b/proposals/0389-attached-macros.md @@ -5,7 +5,7 @@ * Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Implemented (Swift 5.9)** * Implementation: Implemented on GitHub `main` behind the experimental flag `Macros`. See the [example repository](https://github.com/DougGregor/swift-macro-examples) for more macros. -* Review: ([pitch #1, under the name "declaration macros"](https://forums.swift.org/t/pitch-declaration-macros/62373)) ([pitch #2](https://forums.swift.org/t/pitch-attached-macros/62812)) ([review](https://forums.swift.org/t/se-0389-attached-macros/63165)) ([acceptance](https://forums.swift.org/t/accepted-se-0389-attached-macros/63593)) +* Review: ([pitch #1, under the name "declaration macros"](https://forums.swift.org/t/pitch-declaration-macros/62373)) ([pitch #2](https://forums.swift.org/t/pitch-attached-macros/62812)) ([review](https://forums.swift.org/t/se-0389-attached-macros/63165)) ([acceptance](https://forums.swift.org/t/accepted-se-0389-attached-macros/63593)) ([post-acceptance update](https://forums.swift.org/t/update-on-se-0382-and-se-0389-expression-macros-and-attached-macros/74094)) ## Introduction From cd7d004e523fa55e3cf1e2a6d27b9cd03c10fde6 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 21 Aug 2024 19:06:15 -0400 Subject: [PATCH 3828/4563] Add revision history, review history, historical note --- proposals/0304-structured-concurrency.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/proposals/0304-structured-concurrency.md b/proposals/0304-structured-concurrency.md index 5bb381c6a3..4051b87b1e 100644 --- a/proposals/0304-structured-concurrency.md +++ b/proposals/0304-structured-concurrency.md @@ -5,6 +5,8 @@ * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Implemented (Swift 5.5)** [Acceptance post](https://forums.swift.org/t/accepted-with-modifications-se-0304-structured-concurrency/51850) * Implementation: Available in [recent `main` snapshots](https://swift.org/download/#snapshots) behind the flag `-Xfrontend -enable-experimental-concurrency` +* Review: ([first pitch](https://forums.swift.org/t/concurrency-structured-concurrency/41622)) ([second pitch](https://forums.swift.org/t/pitch-2-structured-concurrency/43452)) ([third pitch](https://forums.swift.org/t/pitch-3-structured-concurrency/44496)) ([first review](https://forums.swift.org/t/se-0304-structured-concurrency/45314)) ([second review](https://forums.swift.org/t/se-0304-2nd-review-structured-concurrency/47217)) ([third review](https://forums.swift.org/t/se-0304-3rd-review-structured-concurrency/48847)) ([fourth review](https://forums.swift.org/t/se-0304-4th-review-structured-concurrency/50281)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0304-structured-concurrency/51850)) ([revision](https://forums.swift.org/t/accepted-with-modifications-se-0304-structured-concurrency/51850/9)) +* Previous revisions: ([first review](https://github.com/swiftlang/swift-evolution/blob/9b5e0cbd552b4c8b570aedcb94c0cb72b9f591b0/proposals/0304-structured-concurrency.md)) ([second review](https://github.com/swiftlang/swift-evolution/blob/3defec78bc3f7cbbc9a14f69511397d27899244d/proposals/0304-structured-concurrency.md)) ([third review](https://github.com/swiftlang/swift-evolution/blob/a0faeabd6718ba99865a940a085927a69494fe94/proposals/0304-structured-concurrency.md)) ([fourth review](https://github.com/swiftlang/swift-evolution/blob/2e9eda9f67ae7aabd89ea17baadd32affa416843/proposals/0304-structured-concurrency.md)) ([as accepted](https://github.com/swiftlang/swift-evolution/blob/c2f363236520589ab94ef91a1419e06f39346133/proposals/0304-structured-concurrency.md)) ## Table of contents @@ -1603,3 +1605,7 @@ Initially the `group.addTask` operation was designed with the idea of being an a This was not implemented nor is it clear how efficient and meaningful this form of back-pressure really would be. A naive version of these semantics is possible to implement by balancing pending and completed task counts in the group by plain variables, so removing this implementation does not prevent developers from implementing such "width limited" operations per se. The way to back-pressure submissions should also be considered in terms of how it relates to async let and general task creation mechanisms, not only groups. We have not figured this out completely, and rather than introduce an not-implemented API which may or may not have the right shape, for now we decided to punt on this feature until we know precisely if and how to apply this style of back-pressure on creating tasks throughout the system. + +## Proposal history + +When this proposal was originally accepted, the description of `withThrowingTaskGroup` stated that any remaining tasks in the group will be cancelled if any task throws after the body function returns normally. This behavior was never implemented; throws from tasks are ignored in this state. By the time this discrepancy was detected, several years had passed, and the Language Steering Group decided that restoring the proposed behavior (if desired) would require a new proposal. This document has been revised to reflect the implemented behavior. From 88329b13e4e68bf2e007f4dc5ed663efad0a19a5 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 22 Aug 2024 08:52:47 -0700 Subject: [PATCH 3829/4563] StorageView -> Span --- proposals/NNNN-non-escapable.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index 7f7110f3b0..9e2530da6b 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -16,7 +16,7 @@ This complements the `~Copyable` types added with SE-0390 by introducing another In addition, these types will support lifetime-dependency constraints (being tracked in a separate proposal), that allow them to safely hold pointers referring to data stored in other types. -This feature is a key requirement for the proposed `StorageView` type. +This feature is a key requirement for the proposed `Span` type. **See Also** @@ -24,7 +24,7 @@ This feature is a key requirement for the proposed `StorageView` type. * [Language Support for Bufferview](https://forums.swift.org/t/roadmap-language-support-for-bufferview/66211) * [Roadmap for improving Swift performance predictability: ARC improvements and ownership control](https://forums.swift.org/t/a-roadmap-for-improving-swift-performance-predictability-arc-improvements-and-ownership-control/54206) * [Ownership Manifesto](https://forums.swift.org/t/manifesto-ownership/5212) -* [Draft StorageView Proposal](https://github.com/apple/swift-evolution/pull/2307) +* [Draft Span Proposal](https://github.com/apple/swift-evolution/pull/2307) * [Draft Lifetime Dependency Annotations Proposal](https://github.com/apple/swift-evolution/pull/2305) ## Motivation @@ -305,9 +305,9 @@ Retrofitting existing generic types so they can support both escapable and nones ## Future directions -#### `StorageView` type +#### `Span` type -This proposal is being driven in large part by the needs of the `StorageView` type that has been discussed elsewhere. +This proposal is being driven in large part by the needs of the `Span` type that has been discussed elsewhere. Briefly, this type would provide an efficient universal “view” of array-like data stored in contiguous memory. Since values of this type do not own any data but only refer to data stored elsewhere, their lifetime must be limited to not exceed that of the owning storage. We expect to publish a sample implementation and proposal for that type very soon. @@ -395,9 +395,9 @@ so it cannot be allowed to contain a nonescapable value. #### Rely on `~Copyable` -As part of the `StorageView` design, we considered whether it would suffice to use `~Copyable` instead of introducing a new type concept. -Andrew Trick's analysis in [Language Support for Bufferview](https://forums.swift.org/t/roadmap-language-support-for-bufferview/66211) concluded that making `StorageView` be non-copyable would not suffice to provide the full semantics we want for that type. -Further, introducing `StorageView` as `~Copyable` would actually preclude us from later expanding it to be `~Escapable`. +As part of the `Span` design, we considered whether it would suffice to use `~Copyable` instead of introducing a new type concept. +Andrew Trick's analysis in [Language Support for Bufferview](https://forums.swift.org/t/roadmap-language-support-for-bufferview/66211) concluded that making `Span` be non-copyable would not suffice to provide the full semantics we want for that type. +Further, introducing `Span` as `~Copyable` would actually preclude us from later expanding it to be `~Escapable`. The iterator example in the beginning of this document provides another motivation: Iterators are routinely copied in order to record a particular point in a collection. From 376201974d0143ec9f84c7a41ceab57e3c5bc141 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 22 Aug 2024 13:58:00 -0700 Subject: [PATCH 3830/4563] edits in motivation --- proposals/NNNN-non-escapable.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index 9e2530da6b..8219ef49b4 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -30,9 +30,9 @@ This feature is a key requirement for the proposed `Span` type. ## Motivation Swift's current notion of an "iterator" has several weaknesses that become apparent when you try to use it in extremely performance-constrained environments. -These weaknesses arise from the desire to ensure safety while simultaneously allowing iterator values to be arbitrarily copied to support multi-iterator algorithms. +These weaknesses arise from the desire to ensure safety while simultaneously allowing iterator values to be arbitrarily copied in support of multi-iterator algorithms. -For example, the standard library iterator for Array logically creates a copy of the Array when it is constructed; this ensures that changes to the array cannot affect the iteration. +For example, the standard library iterator for Array logically creates a copy of the Array when it is initialized; this ensures that changes to the array cannot affect the iteration. This is implemented by having the iterator store a reference-counted pointer to the array storage in order to ensure that the storage cannot be freed while the iterator is active. These safety checks all incur runtime overhead. From fefac775356db69491e3f3cc421bd10e03ca7c7e Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 22 Aug 2024 18:51:24 -0400 Subject: [PATCH 3831/4563] Assign SE-0443 to the warning-control proposal and put it into review --- ...ning-control-flags.md => 0443-warning-control-flags.md} | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename proposals/{NNNN-warning-control-flags.md => 0443-warning-control-flags.md} (98%) diff --git a/proposals/NNNN-warning-control-flags.md b/proposals/0443-warning-control-flags.md similarity index 98% rename from proposals/NNNN-warning-control-flags.md rename to proposals/0443-warning-control-flags.md index bdd5bc9176..8d81731309 100644 --- a/proposals/NNNN-warning-control-flags.md +++ b/proposals/0443-warning-control-flags.md @@ -1,10 +1,10 @@ # Precise Control Flags over Compiler Warnings -* Proposal: [SE-NNNN](NNNN-warning-control-flags.md) +* Proposal: [SE-0443](NNNN-warning-control-flags.md) * Authors: [Doug Gregor](https://github.com/douggregor), [Dmitrii Galimzianov](https://github.com/DmT021) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (August 22nd...September 2nd, 2024)** * Implementation: [apple/swift#74466](https://github.com/swiftlang/swift/pull/74466) * Review: ([pitch](https://forums.swift.org/t/warnings-as-errors-exceptions/72925)) @@ -12,7 +12,6 @@ This proposal introduces new compiler options that allow fine-grained control over how the compiler emits certain warnings: as warnings, as errors, or not at all. - ## Motivation The current compiler options for controlling how warnings are emitted are very inflexible. Currently, the following options exist: From 6571d8456e2548bd76ca23474246700cf1f2fdaf Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 22 Aug 2024 18:55:53 -0400 Subject: [PATCH 3832/4563] Link SE-0443 to its review thread --- proposals/0443-warning-control-flags.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0443-warning-control-flags.md b/proposals/0443-warning-control-flags.md index 8d81731309..5ec5516654 100644 --- a/proposals/0443-warning-control-flags.md +++ b/proposals/0443-warning-control-flags.md @@ -6,7 +6,7 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (August 22nd...September 2nd, 2024)** * Implementation: [apple/swift#74466](https://github.com/swiftlang/swift/pull/74466) -* Review: ([pitch](https://forums.swift.org/t/warnings-as-errors-exceptions/72925)) +* Review: ([pitch](https://forums.swift.org/t/warnings-as-errors-exceptions/72925)) ([review](https://forums.swift.org/t/se-0443-precise-control-flags-over-compiler-warnings/74116)) ## Introduction From 9853602f634573b5bdf68171ea6ded725d4cd89b Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 22 Aug 2024 16:19:06 -0700 Subject: [PATCH 3833/4563] =?UTF-8?q?don=E2=80=99t=20rely=20on=20pseudo=20?= =?UTF-8?q?code=20as=20exposition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proposals/NNNN-non-escapable.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index 8219ef49b4..93bdd49bc3 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -87,6 +87,10 @@ struct NotEscapable: ~Escapable { ``` A nonescapable value is not allowed to escape the local context: + +- It cannot be assigned to a binding in a larger scope +- It cannot be returned from the current scope + ```swift // Example: Basic limits on ~Escapable types func f() -> NotEscapable { @@ -99,7 +103,6 @@ func f() -> NotEscapable { ``` **Note**: -The section "Returned nonescapable values require lifetime dependency" explains the implications for how you must write initializers. Without `~Escapable`, the default for any type is to be escapable. Since `~Escapable` suppresses a capability, you cannot put this in an extension. From c058e570ffaece42a589f073b064552a16dcc4b5 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 22 Aug 2024 16:19:38 -0700 Subject: [PATCH 3834/4563] =?UTF-8?q?add=20a=20link=20to=20the=20=E2=80=9C?= =?UTF-8?q?returned=20values=E2=80=9D=20subsection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proposals/NNNN-non-escapable.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index 93bdd49bc3..a729ac511a 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -103,8 +103,9 @@ func f() -> NotEscapable { ``` **Note**: +The section ["Returned nonescapable values require lifetime dependency"](#Returns) explains the implications for how you must write initializers. -Without `~Escapable`, the default for any type is to be escapable. Since `~Escapable` suppresses a capability, you cannot put this in an extension. +Without `~Escapable`, the default for any type is to be escapable. Since `~Escapable` suppresses a capability, you cannot declare it with an extension. ```swift // Example: Escapable by default @@ -195,7 +196,7 @@ enum NonescapableEnum: ~Escapable { } ``` -#### Returned nonescapable values require lifetime dependency +#### Returned nonescapable values require lifetime dependency As mentioned earlier, a simple return of a nonescapable value is not permitted: ```swift From 4b55adbf1dbad0ce7ac0196066630295c85fc742 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 22 Aug 2024 16:20:30 -0700 Subject: [PATCH 3835/4563] make example names more consistent --- proposals/NNNN-non-escapable.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index a729ac511a..9bfb842514 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -177,7 +177,7 @@ Nonescapable values cannot be stored as class properties, since classes are alwa ```swift // Example -struct OuterEscapable { +struct EscapableStruct { // 🛑 Escapable struct cannot have nonescapable stored property var nonesc: Nonescapable } @@ -187,7 +187,7 @@ enum EscapableEnum { case nonesc(Nonescapable) } -struct OuterNonescapable: ~Escapable { +struct NonescapableStruct: ~Escapable { var nonesc: Nonescapable // OK } From f2faa8964d2498969e184e6a3a768175543855e5 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 22 Aug 2024 16:25:12 -0700 Subject: [PATCH 3836/4563] small tweaks --- proposals/NNNN-non-escapable.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index 9bfb842514..b0f48d1e80 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -172,7 +172,7 @@ This is in contrast to an _escapable_ `consuming` argument which can be disposed #### Values that contain nonescapable values must be nonescapable Stored struct properties and enum payloads can have nonescapable types if the surrounding type is itself nonescapable. -(Equivalently, an escapable struct or enum can only contain escapable values.) +Equivalently, an escapable struct or enum can only contain escapable values. Nonescapable values cannot be stored as class properties, since classes are always inherently escaping. ```swift @@ -217,7 +217,7 @@ This implies that they cannot be stored in global or static variables. #### Closures and nonescapable values Escaping closures cannot capture nonescapable values. -Nonescaping closures can capture nonescapable values subject only to the usual exclusivity restrictions. +Nonescaping closures can capture nonescapable values subject to the usual exclusivity restrictions. Returning a nonescapable value from a closure requires explicit lifetime dependency annotations, as covered in the companion proposal. @@ -245,7 +245,7 @@ extension Box: Escapable where T: Escapable { } ``` This can be used in conjunction with other suppressible protocols. -For example, many general library types will need to be copyable and/or escapable following their contents. +For example, many general library container types will need to be copyable and/or escapable according to their contents. Here's a compact way to declare such a type: ```swift struct Wrapper { ... } From 3656dcc8d7b215e2081739de8367d75bf5c856a5 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 22 Aug 2024 16:27:05 -0700 Subject: [PATCH 3837/4563] clean up argument vs. parameter, value vs. type, value vs. binding - I may be wrong on some of these, but this needs to be consistent with Swift documentation. --- proposals/NNNN-non-escapable.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index b0f48d1e80..0e68da97e9 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -163,13 +163,13 @@ func f() { } ``` -#### Constraints on nonescapable arguments +#### Constraints on nonescapable parameters -A value of nonescapable type received as an argument is subject to the same constraints as any other local variable. -In particular, a nonescapable `consuming` argument (and all direct copies thereof) must actually be destroyed during the execution of the function. -This is in contrast to an _escapable_ `consuming` argument which can be disposed of by being returned or stored to an instance property or global variable. +A value of nonescapable type received as an parameter is subject to the same constraints as any other local variable. +In particular, a nonescapable `consuming` parameter (and all direct copies thereof) must actually be destroyed during the execution of the function. +This is in contrast to an _escapable_ `consuming` parameter which can be disposed of by being returned or stored to an instance property or global variable. -#### Values that contain nonescapable values must be nonescapable +#### Types that contain nonescapable values must be nonescapable Stored struct properties and enum payloads can have nonescapable types if the surrounding type is itself nonescapable. Equivalently, an escapable struct or enum can only contain escapable values. @@ -206,7 +206,7 @@ func f() -> NotEscapable { // 🛑 Cannot return a nonescapable type } ``` -A separate proposal describes “lifetime dependency annotations” that can relax this requirement by tying the lifetime of the returned value to the lifetime of some other object, either an argument to the function or `self` in the case of a method or computed property returning a nonescapable type. +A separate proposal describes “lifetime dependency annotations” that can relax this requirement by tying the lifetime of the returned value to the lifetime of another binding. The other binding can be a parameter of a function returning a vaule of a nonescapable type, or con be `self` for a method or computed property returning a value of a nonescapable type. In particular, struct and enum initializers (which build a new value and return it to the caller) cannot be written without some mechanism similar to that outlined in our companion proposal. #### Globals and static variables cannot be nonescapable @@ -223,7 +223,7 @@ Returning a nonescapable value from a closure requires explicit lifetime depende #### Nonescapable values and concurrency -All of the requirements on use of nonescapable values as function arguments and return values also apply to async functions, including those invoked via `async let`. +All of the requirements on use of nonescapable values as function parameters and return values also apply to async functions, including those invoked via `async let`. The closures used in `Task.init`, `Task.detached`, or `TaskGroup.addTask` are escaping closures and therefore cannot capture nonescapable values. From 57fe29d5d55edb85b14c153b7f4cbead6b6539eb Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Thu, 22 Aug 2024 17:19:14 -0700 Subject: [PATCH 3838/4563] Fix proposal link for SE-0443 --- proposals/0443-warning-control-flags.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0443-warning-control-flags.md b/proposals/0443-warning-control-flags.md index 5ec5516654..c48998652b 100644 --- a/proposals/0443-warning-control-flags.md +++ b/proposals/0443-warning-control-flags.md @@ -1,7 +1,7 @@ # Precise Control Flags over Compiler Warnings -* Proposal: [SE-0443](NNNN-warning-control-flags.md) +* Proposal: [SE-0443](0443-warning-control-flags.md) * Authors: [Doug Gregor](https://github.com/douggregor), [Dmitrii Galimzianov](https://github.com/DmT021) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (August 22nd...September 2nd, 2024)** From 738fafbec17a332a14c26385ebb2481da9142bde Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Fri, 23 Aug 2024 15:48:49 -0700 Subject: [PATCH 3839/4563] Revisit lifetime dependency discussions --- proposals/NNNN-non-escapable.md | 120 ++++++++++---------------------- 1 file changed, 35 insertions(+), 85 deletions(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index 0e68da97e9..c317893f1d 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -14,7 +14,7 @@ We propose adding a new type constraint `~Escapable` for types that can be locally copied but cannot be assigned or transferred outside of the immediate context. This complements the `~Copyable` types added with SE-0390 by introducing another set of compile-time-enforced lifetime controls that can be used for safe, highly-performant APIs. -In addition, these types will support lifetime-dependency constraints (being tracked in a separate proposal), that allow them to safely hold pointers referring to data stored in other types. +In addition, these types will support lifetime-dependency constraints (being tracked in a future proposal), that allow them to safely hold pointers referring to data stored in other types. This feature is a key requirement for the proposed `Span` type. @@ -165,7 +165,7 @@ func f() { #### Constraints on nonescapable parameters -A value of nonescapable type received as an parameter is subject to the same constraints as any other local variable. +A value of nonescapable type received as a parameter is subject to the same constraints as any other local variable. In particular, a nonescapable `consuming` parameter (and all direct copies thereof) must actually be destroyed during the execution of the function. This is in contrast to an _escapable_ `consuming` parameter which can be disposed of by being returned or stored to an instance property or global variable. @@ -206,8 +206,8 @@ func f() -> NotEscapable { // 🛑 Cannot return a nonescapable type } ``` -A separate proposal describes “lifetime dependency annotations” that can relax this requirement by tying the lifetime of the returned value to the lifetime of another binding. The other binding can be a parameter of a function returning a vaule of a nonescapable type, or con be `self` for a method or computed property returning a value of a nonescapable type. -In particular, struct and enum initializers (which build a new value and return it to the caller) cannot be written without some mechanism similar to that outlined in our companion proposal. +A future proposal will describe “lifetime dependency annotations” that can relax this requirement by tying the lifetime of the returned value to the lifetime of another binding. +In particular, struct and enum initializers (which build a new value and return it to the caller) cannot be written without some such mechanism. #### Globals and static variables cannot be nonescapable @@ -219,7 +219,7 @@ This implies that they cannot be stored in global or static variables. Escaping closures cannot capture nonescapable values. Nonescaping closures can capture nonescapable values subject to the usual exclusivity restrictions. -Returning a nonescapable value from a closure requires explicit lifetime dependency annotations, as covered in the companion proposal. +Returning a nonescapable value from a closure will only be possible with explicit lifetime dependency annotations, to be covered in a future proposal. #### Nonescapable values and concurrency @@ -245,43 +245,14 @@ extension Box: Escapable where T: Escapable { } ``` This can be used in conjunction with other suppressible protocols. -For example, many general library container types will need to be copyable and/or escapable according to their contents. +For example, many general library container types will need to be copyable and/or escapable depending on their contents. Here's a compact way to declare such a type: ```swift -struct Wrapper { ... } -extension Wrapper: Copyable where T: ~Escapable {} -extension Wrapper: Escapable where T: ~Copyable {} +struct Wrapper: ~Copyable, ~Escapable { ... } +extension Wrapper: Copyable where T: Copyable {} +extension Wrapper: Escapable where T: Escapable {} ``` -The above declarations all in a single source file will result in a type `Wrapper` that is `Escapable` exactly when `T` is `Escapable` and `Copyable` exactly when `T` is `Copyable`. -To see why, first note that the explicit `extension Wrapper: Escapable` in the same source file implies that the original `struct Wrapper` must be `~Escapable` and similarly for `Copyable`, exactly as if the first line had been -```swift -struct Wrapper: ~Copyable & ~Escapable {} -``` - -Now recall from SE-427 that suppressible protocols must be explicitly suppressed on type parameters in extensions. -This means that -```swift -extension Wrapper: Copyable where T: ~Escapable {} -``` -is exactly the same as -```swift -extension Wrapper: Copyable where T: Copyable & ~Escapable {} -``` -which implies that `Wrapper` becomes `Copyable` when `T` is `Copyable`. -Finally, remember that `~Escapable` means that `Escapable` is not required, so -this condition on `Copyable` applies regardless of whether `T` is `Escapable` or not. - -Similarly, -```swift -extension Wrapper: Escapable where T: ~Copyable {} -``` -is exactly the same as -```swift -extension Wrapper: Escapable where T: Escapable & ~Copyable {} -``` -which means that `Wrapper` is `Escapable` whenever `T` is `Escapable` regardless of whether `T` is `Copyable` or not. - ## Source compatibility The compiler will treat any type without explicit `~Escapable` as escapable. @@ -316,24 +287,36 @@ Briefly, this type would provide an efficient universal “view” of array-like Since values of this type do not own any data but only refer to data stored elsewhere, their lifetime must be limited to not exceed that of the owning storage. We expect to publish a sample implementation and proposal for that type very soon. -#### Lifetime dependency annotations +#### Initializers and Lifetime Dependencies -Nonescapable types have a set of inherent restrictions on how they can be passed as arguments, stored in variables, or returned from functions. -A companion proposal builds on this by supporting more detailed annotations that link the lifetimes of different objects. -This would allow, for example, a container to vend an iterator value that held a direct unmanaged pointer to the container's contents. -The lifetime dependency would ensure that such an iterator could not outlive the container to whose contents it referred. +All values come into existence within the body of some initializer and are returned to the caller of that initializer. +Since nonescapable types cannot be returned, +it follows that nonescapable types cannot have initializers without some additional language affordance. +A subsequent proposal will provide such an affordance. +This will allow values to be returned subject to the requirement that they not outlive some other specific value. +For example, a nonescapable iterator might be initialized so as to not outlive the container that created it: ```swift -// Example: Nonescaping iterator -struct NEIterator { - // `dependsOn(container)` indicates that the constructed value - // cannot outlive the `container` argument. - init(over container: MyContainer) -> dependsOn(container) Self { - ... initialize an iterator suitable for `MyContainer` ... - } +struct Iterator: ~Escapable { + // ⚠️️ Returned Iterator will not be allowed to outlive `container` + // Details in a future proposal: This may involve + // additional syntax or default inference rules. + init(container: borrowing Container) { ... } } + +let iterator: Iterator +do { + let container = Container(...) + let buffer = container.buffer + iterator = Iterator(buffer) + // `container` lifetime ends here +} +use(iterator) // 🛑 'iterator' outlives `container` ``` +These lifetime dependencies will be enforced entirely at compile time without any runtime overhead. +Invalid uses such as the one above will produce compiler errors. + #### Expanding standard library types We expect that many standard library types will need to be updated to support possibly-nonescapable types, including `Optional`, `Array`, `Set`, `Dictionary`, and the `Unsafe*Pointer` family of types. @@ -349,7 +332,8 @@ For example, this can greatly improve the safety of locking APIs that expect to #### Nonescapable classes -We’ve explicitly excluded class types from being nonescapable. In the future, we could allow class types to be declared nonescapable as a way to avoid most reference-counting operations on class objects. +We’ve explicitly excluded class types from being nonescapable. +In the future, we could allow class types to be declared nonescapable as a way to avoid most reference-counting operations on class objects. #### Concurrency @@ -407,40 +391,6 @@ The iterator example in the beginning of this document provides another motivati Iterators are routinely copied in order to record a particular point in a collection. Thus we concluded that non-copyable was not the correct lifetime restriction for types of this sort, and it was worthwhile to introduce a new lifetime concept to the language. -#### Returns and initializers - -This proposal does not by itself provide any way to initialize a nonescapable value, requiring the additional proposed lifetime dependency annotations to support that mechanism. -Since those annotations require that the lifetime of the returned value be bound to that of one of the arguments, this implies that our current proposal does not permit nonescapable types to have trivial initializers: - -```swift -struct NE: ~Escapable { - init() {} // 🛑 Initializer return must depend on an argument -} -``` - -We considered introducing an annotation that would specifically allow this and related uses: - -```swift -struct NE: ~Escapable { - @_unsafeNonescapableResult - init() {} // OK because of annotation -} -``` - -We omitted this annotation from our proposal because there is more than one possible interpretation of such a marker. And we did not see a compelling reason for preferring one particular interpretation because we have yet to find a use case that actually requires this. - -In particular, the use cases we’ve so far considered have all been resolvable by adding an argument specifically for the purpose of anchoring a lifetime dependency: - -```swift -struct NE: ~Escapable { - // Proposed lifetime dependency notation; - // see separate proposal for details. - init(from: SomeType) -> dependsOn(from) Self {} -} -``` - -We expect that future experience with nonescapable types will clarify whether additional lifetime modifiers of this sort are justified. - ## Acknowledgements Many people discussed this proposal and gave important feedback, including: Kavon Farvardin, Meghana Gupta, John McCall, Slava Pestov, Joe Groff, Guillaume Lessard, and Franz Busch. From fe0579788eb90a81d8e83563f8a9439ab209e402 Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Fri, 23 Aug 2024 15:51:03 -0700 Subject: [PATCH 3840/4563] s/Iterator/IteratorProtocol/ Co-authored-by: Karoy Lorentey --- proposals/NNNN-non-escapable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index c317893f1d..212280cd48 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -58,7 +58,7 @@ A separate proposal will show how we can further improve safety by allowing libr These "lifetime dependency" constraints can also be verified at compile time to ensure that the source of the iterator is not modified and that the iterator specifically does not outlive its source. **Note**: We are using iterators here to illustrate the issues we are considering. -We are not at this time proposing any changes to Swift's current `Iterator` protocol. +We are not at this time proposing any changes to Swift's current `IteratorProtocol` construct. ## Detailed design From 0c71b72d5951fab139ba8f76dd54acf4fffc5959 Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Fri, 23 Aug 2024 15:54:45 -0700 Subject: [PATCH 3841/4563] Review feedback from lorentey --- proposals/NNNN-non-escapable.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index 212280cd48..d2e0af2253 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -16,7 +16,7 @@ This complements the `~Copyable` types added with SE-0390 by introducing another In addition, these types will support lifetime-dependency constraints (being tracked in a future proposal), that allow them to safely hold pointers referring to data stored in other types. -This feature is a key requirement for the proposed `Span` type. +This feature is a key requirement for the proposed `Span` family of types. **See Also** @@ -58,7 +58,7 @@ A separate proposal will show how we can further improve safety by allowing libr These "lifetime dependency" constraints can also be verified at compile time to ensure that the source of the iterator is not modified and that the iterator specifically does not outlive its source. **Note**: We are using iterators here to illustrate the issues we are considering. -We are not at this time proposing any changes to Swift's current `IteratorProtocol` construct. +We are not at this time proposing any changes to Swift's current `IteratorProtocol` protocol. ## Detailed design @@ -280,9 +280,9 @@ Retrofitting existing generic types so they can support both escapable and nones ## Future directions -#### `Span` type +#### `Span` family of types -This proposal is being driven in large part by the needs of the `Span` type that has been discussed elsewhere. +This proposal is being driven in large part by the needs of the `Span` types that have been discussed elsewhere. Briefly, this type would provide an efficient universal “view” of array-like data stored in contiguous memory. Since values of this type do not own any data but only refer to data stored elsewhere, their lifetime must be limited to not exceed that of the owning storage. We expect to publish a sample implementation and proposal for that type very soon. From 3c1e92f925938b462f19c70ee649c2a77370ee62 Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Fri, 23 Aug 2024 16:31:21 -0700 Subject: [PATCH 3842/4563] Atrick's corrections --- proposals/NNNN-non-escapable.md | 39 +++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index d2e0af2253..5c00d38b44 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -289,33 +289,44 @@ We expect to publish a sample implementation and proposal for that type very soo #### Initializers and Lifetime Dependencies -All values come into existence within the body of some initializer and are returned to the caller of that initializer. -Since nonescapable types cannot be returned, -it follows that nonescapable types cannot have initializers without some additional language affordance. - -A subsequent proposal will provide such an affordance. -This will allow values to be returned subject to the requirement that they not outlive some other specific value. -For example, a nonescapable iterator might be initialized so as to not outlive the container that created it: +Nonescapable function parameters may not outlive the function scope. +Consequently, nonescapable values can never be returned from a function. +Nonescapable values come into existence within the body of the initializer. +Naturally, the initializer must return its value, and this creates an exception to the rule. +Without further language support, implementing a nonescapable initializer requires an unsafe construct. +That unsafe handling is not covered by this proposal because we don't want to surface unnecessary unsafety in the language. +Instead, a subsequent proposal will support safe initialization by allowing the initializer to specify lifetime dependencies on its parameters. +The parameters to the initializer typically indicate a lifetime that the nonescapable value cannot outlive. +An initializer may, for example, create a nonescapable value that depends on a container variable that is bound to an object with its own lifetime: ```swift struct Iterator: ~Escapable { - // ⚠️️ Returned Iterator will not be allowed to outlive `container` - // Details in a future proposal: This may involve - // additional syntax or default inference rules. init(container: borrowing Container) { ... } } +let container = ... +let iterator = Iterator(container) +consume container +use(iterator) // 🛑 'iterator' outlives the source of its dependency +``` + +Lifetime dependencies will make this use of iterator a compile-time error. +Or, as part of implementing data type internals, a nonescapable initializer may depend on a variable that is bound to a value that is only valid within that variable's local scope. +Subsequent uses of the initialized nonescapable object are exactly as safe or unsafe as it would be to use the variable that the initializer depends at the same point: + +```swift let iterator: Iterator do { let container = Container(...) let buffer = container.buffer iterator = Iterator(buffer) - // `container` lifetime ends here + // `iterator` is safe as long as `buffer` is safe to use. } -use(iterator) // 🛑 'iterator' outlives `container` +use(iterator) // 🛑 'iterator' outlives the source of its dependency ``` -These lifetime dependencies will be enforced entirely at compile time without any runtime overhead. -Invalid uses such as the one above will produce compiler errors. +Again, lifetime dependencies will make this use of iterator a compile-time error. +Typically, a pointer is valid for the duration of its variable binding. +So, in practice, nonescapable value that depends on the pointer to be available within the same scope. #### Expanding standard library types From 001c2723ee2932505676a83d121eb6116077711a Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Fri, 23 Aug 2024 16:46:27 -0700 Subject: [PATCH 3843/4563] More feedback from atrick --- proposals/NNNN-non-escapable.md | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index 5c00d38b44..eb0849e9b4 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -305,28 +305,13 @@ struct Iterator: ~Escapable { let container = ... let iterator = Iterator(container) -consume container -use(iterator) // 🛑 'iterator' outlives the source of its dependency +consume container // `container` lifetime ends here +use(iterator) // 🛑 'iterator' outlives `container` ``` -Lifetime dependencies will make this use of iterator a compile-time error. -Or, as part of implementing data type internals, a nonescapable initializer may depend on a variable that is bound to a value that is only valid within that variable's local scope. -Subsequent uses of the initialized nonescapable object are exactly as safe or unsafe as it would be to use the variable that the initializer depends at the same point: - -```swift -let iterator: Iterator -do { - let container = Container(...) - let buffer = container.buffer - iterator = Iterator(buffer) - // `iterator` is safe as long as `buffer` is safe to use. -} -use(iterator) // 🛑 'iterator' outlives the source of its dependency -``` - -Again, lifetime dependencies will make this use of iterator a compile-time error. -Typically, a pointer is valid for the duration of its variable binding. -So, in practice, nonescapable value that depends on the pointer to be available within the same scope. +Lifetime dependencies will make this and similar misuses into compile-time errors. +This will allow developers to safely define and use values that contain pointers into +other values, ensuring that the pointers never outlive the underlying storage. #### Expanding standard library types From b985812fc7975957faac0d3fc96deb2e4a741a50 Mon Sep 17 00:00:00 2001 From: Dmitrii Galimzianov Date: Sun, 25 Aug 2024 03:47:14 +0200 Subject: [PATCH 3844/4563] Exclude -Wsuppress and -suppress-warnings from the proposal --- proposals/0443-warning-control-flags.md | 43 ++++++++++++++----------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/proposals/0443-warning-control-flags.md b/proposals/0443-warning-control-flags.md index 5ec5516654..4da995c3c9 100644 --- a/proposals/0443-warning-control-flags.md +++ b/proposals/0443-warning-control-flags.md @@ -10,7 +10,7 @@ ## Introduction -This proposal introduces new compiler options that allow fine-grained control over how the compiler emits certain warnings: as warnings, as errors, or not at all. +This proposal introduces new compiler options that allow fine-grained control over how the compiler emits certain warnings: as warnings or as errors. ## Motivation @@ -25,7 +25,6 @@ This lack of flexibility leads to situations where users who want to use `-warni This proposal suggests adding new options that will allow the behavior of warnings to be controlled based on their diagnostic group. - `-Werror ` - upgrades warnings in the specified group to errors -- `-Wsuppress ` - disables the emission of warnings in the specified group - `-Wwarning ` - indicates that warnings in the specified group should remain warnings, even if they were previously suppressed or upgraded to errors The `` parameter is a string identifier of the diagnostic group. @@ -80,28 +79,20 @@ Each warning in the compiler is assigned one of three behaviors: `warning`, `err Compiler options for controlling the behavior of groups are now processed as a single list. These options include: ``` -Werror --Wsuppress -Wwarning -warnings-as-errors -no-warnings-as-errors --suppress-warnings ``` -When these options are passed to the compiler, we sequentially apply the specified behavior to all warnings within the specified group from left to right. For `-warnings-as-errors`, `-no-warnings-as-errors`, and `-suppress-warnings`, we apply the behavior to all warnings. - -The `-no-warnings-as-errors` option should be read as "set the behavior to 'warning' for all warnings". Thus, it overrides all previously set behaviors, including if the `-suppress-warnings` option was applied earlier or if a warning was suppressed by default. +When these options are passed to the compiler, we sequentially apply the specified behavior to all warnings within the specified group from left to right. For `-warnings-as-errors` and `-no-warnings-as-errors`, we apply the behavior to all warnings. Examples of option combinations: - `-warnings-as-errors -Wwarning deprecated` Warnings from the `deprecated` group will be kept as warnings, but all the rest will be upgraded to errors. -- `-warnings-as-errors -Wwarning deprecated -Wsuppress availability_deprecated` - - Warnings from the `availability_deprecated` group will be suppressed. Other warnings from the `deprecated` group will remain as warnings. All other warnings will be upgraded to errors. - -- `-suppress-warnings -Wwarning deprecated` +- `-Werror deprecated -Wwarning availability_deprecated` - Warnings from the `deprecated` group will remain as warnings. All others will be suppressed. + Warnings from the `availability_deprecated` group will remain as warnings. Other warnings from the `deprecated` group will be upgraded to errors. All others will be kept as warnings. It’s crucial to understand that the order in which these flags are applied can significantly affect the behavior of diagnostics. The rule is "the last one wins", meaning that if multiple flags apply to the same diagnostic group, the last one specified on the command line will determine the final behavior. @@ -110,6 +101,14 @@ For example, as mentioned above, the `unsafe_global_actor_deprecated` group is p - `-Wwarning deprecated -Werror concurrency` will make it an error, - `-Werror concurrency -Wwarning deprecated` will keep it as a warning. +#### Interaction with `-suppress-warnings` + +This proposal deliberately excludes `-suppress-warnings` and its group-based counterpart from the new unified model. We retain the behavior of the existing `-suppress-warnings` flag but forbid its usage with the new options. The following rules will be applied: + +- It is forbidden to combine `-suppress-warnings` with `-Wwarning` or `-Werror`. The compiler will produce an error if these options are present in the command line together. +- It is allowed to be combined with `-no-warnings-as-errors`. The current compiler behavior permits the usage of `-no-warnings-as-errors` or `-warnings-as-errors -no-warnings-as-errors` with `-suppress-warnings`. We will maintain this behavior. +- It remains position-independent. Whenever `-no-warnings-as-errors` and `-suppress-warnings` are combined, `-suppress-warnings` will always take precedence over `-no-warnings-as-errors`, regardless of the order in which they are specified. + ### Usage of `-print-diagnostic-groups` and `-debug-diagnostic-names` As mentioned earlier, we are adding support for the `-print-diagnostic-groups` compiler option, which outputs the group name in square brackets. @@ -153,7 +152,7 @@ The adoption of diagnostic groups and the new compiler options will provide a fo ### Support in the language -While diagnostic groups are introduced to support the compiler options, it may be possible in the future to standardize the structure of the group graph itself. This could open up the possibility of using these same identifiers in the language, implementing something analogous to `#pragma diagnostic` or `[[attribute]]` in C++. However, such standardization and the design of new constructs in the language go far beyond the scope of this proposal, and we need to gain more experience with diagnostic groups before proceeding with this. +While diagnostic groups are introduced to support the compiler options, it may be possible in the future to standardize the structure of the group graph itself. This could open up the possibility of using these same identifiers in the language, implementing something analogous to `#pragma diagnostic` or `[[attribute]]` in C++. It could also address suppressing warnings entirely, which isn't covered by this proposal. However, such standardization and the design of new language constructs go far beyond the scope of this proposal, and we need to gain more experience with diagnostic groups before proceeding with this. ### Support in SwiftPM @@ -204,18 +203,15 @@ During the design process, other names for the compiler options were considered, |--------------------------|--------------------------------| | `-warnings-as-errors` | `-warning-as-error ` | | `-no-warnings-as-errors` | `-no-warning-as-error ` | -| `-suppress-warnings` | `-suppress-warning ` | - -However, with this naming, the combination `-suppress-warning deprecated -no-warning-as-error availability_deprecated` might be misleading. In Clang, diagnostic behavior is controlled through `-W...` options, but the format suffers from inconsistency. We adopt the `-W` prefix while making the format consistent. | Clang | Swift | |-------------------|----------------------| | `-W` | `-Wwarning ` | -| `-Wno-` | `-Wsuppress ` | +| `-Wno-` | | | `-Werror=` | `-Werror ` | -Additionally, the option name `-Wwarning` is much better suited when it comes to enabling suppressed-by-default warnings. Today we have several of them behind dedicated flags like `-driver-warn-unused-options` and `-warn-concurrency`. It might be worth having a common infrastructure for warnings that are suppressed by default. +The option name `-Wwarning` is much better suited when it comes to enabling suppressed-by-default warnings. Today we have several of them behind dedicated flags like `-driver-warn-unused-options` and `-warn-concurrency`. It might be worth having a common infrastructure for warnings that are suppressed by default. ### Alternative format for `-print-diagnostic-groups` @@ -232,3 +228,12 @@ However, even this does not eliminate the possibility of breaking code that pars Moreover, `-print-diagnostic-groups` provides a formalized version of the same functionality using identifiers suitable for user use. And thus it should supersede the usages of `-debug-diagnostic-names`. Therefore, we believe the best solution would be to use the same format for `-print-diagnostic-groups` and prohibit the simultaneous use of these two options. +## Revision History + +- Revisions based on review feedback: + - `-Wsuppress` was excluded from the proposal. + - `-suppress-warnings` was excluded from the unified model and addressed separately by forbidding its usage with the new flags. + +## Acknowledgments + +Thank you to [Frederick Kellison-Linn](https://forums.swift.org/u/Jumhyn) for the idea of addressing the `-suppress-warnings` behavior without incorporating it into the new model. From 7a6910f1f18ac80974c215fb08049381812afb57 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 26 Aug 2024 17:34:52 -0400 Subject: [PATCH 3845/4563] Update 0443-warning-control-flags.md Add link to previous revision --- proposals/0443-warning-control-flags.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0443-warning-control-flags.md b/proposals/0443-warning-control-flags.md index 4da995c3c9..ef904c8fd6 100644 --- a/proposals/0443-warning-control-flags.md +++ b/proposals/0443-warning-control-flags.md @@ -7,6 +7,7 @@ * Status: **Active review (August 22nd...September 2nd, 2024)** * Implementation: [apple/swift#74466](https://github.com/swiftlang/swift/pull/74466) * Review: ([pitch](https://forums.swift.org/t/warnings-as-errors-exceptions/72925)) ([review](https://forums.swift.org/t/se-0443-precise-control-flags-over-compiler-warnings/74116)) +* Previous revisions: [1](https://github.com/swiftlang/swift-evolution/blob/57fe29d5d55edb85b14c153b7f4cbead6b6539eb/proposals/0443-warning-control-flags.md) ## Introduction From 135caf73c25b278c4a96c86029d2b9585615698c Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Fri, 30 Aug 2024 15:24:27 -0700 Subject: [PATCH 3846/4563] Require all suppressible for conditional conformance (LSG feedback) --- proposals/NNNN-non-escapable.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index eb0849e9b4..a6d3729ff7 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -249,8 +249,8 @@ For example, many general library container types will need to be copyable and/o Here's a compact way to declare such a type: ```swift struct Wrapper: ~Copyable, ~Escapable { ... } -extension Wrapper: Copyable where T: Copyable {} -extension Wrapper: Escapable where T: Escapable {} +extension Wrapper: Copyable where T: Copyable, T: ~Escapable {} +extension Wrapper: Escapable where T: Escapable, T: ~Copyable {} ``` ## Source compatibility From 96c1f19c917a7b600b7b43d051f8dc04731167f2 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Sun, 1 Sep 2024 12:03:25 -0700 Subject: [PATCH 3847/4563] Update Future direction: Initializers and Lifetime Dependencies Clearly tie initialization of nonescapable values to the experimental lifetime dependencies feature. --- proposals/NNNN-non-escapable.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/proposals/NNNN-non-escapable.md b/proposals/NNNN-non-escapable.md index a6d3729ff7..1f461d20c9 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/NNNN-non-escapable.md @@ -293,9 +293,6 @@ Nonescapable function parameters may not outlive the function scope. Consequently, nonescapable values can never be returned from a function. Nonescapable values come into existence within the body of the initializer. Naturally, the initializer must return its value, and this creates an exception to the rule. -Without further language support, implementing a nonescapable initializer requires an unsafe construct. -That unsafe handling is not covered by this proposal because we don't want to surface unnecessary unsafety in the language. -Instead, a subsequent proposal will support safe initialization by allowing the initializer to specify lifetime dependencies on its parameters. The parameters to the initializer typically indicate a lifetime that the nonescapable value cannot outlive. An initializer may, for example, create a nonescapable value that depends on a container variable that is bound to an object with its own lifetime: ```swift @@ -309,9 +306,10 @@ consume container // `container` lifetime ends here use(iterator) // 🛑 'iterator' outlives `container` ``` -Lifetime dependencies will make this and similar misuses into compile-time errors. -This will allow developers to safely define and use values that contain pointers into -other values, ensuring that the pointers never outlive the underlying storage. +Specifying a dependency from a function parameter to its nonescapable result currently requires an experimental lifetime dependency feature. +With lifetime dependencies, initialization of nonescapable types is safe: misuses similar to the one shown above are compile-time errors. +Adopting new syntax for lifetime dependencies merits a separate, focussed review. +Until then, initialization of nonescapable values remains experimental. #### Expanding standard library types From 9dd33135e05544cce2464e55c6004c5d84f6215f Mon Sep 17 00:00:00 2001 From: Dmitrii Galimzianov Date: Wed, 4 Sep 2024 02:16:55 +0200 Subject: [PATCH 3848/4563] Soften the guideline for adding new diagnostics --- proposals/0443-warning-control-flags.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0443-warning-control-flags.md b/proposals/0443-warning-control-flags.md index 6afe97b670..7fdf135616 100644 --- a/proposals/0443-warning-control-flags.md +++ b/proposals/0443-warning-control-flags.md @@ -7,7 +7,7 @@ * Status: **Active review (August 22nd...September 2nd, 2024)** * Implementation: [apple/swift#74466](https://github.com/swiftlang/swift/pull/74466) * Review: ([pitch](https://forums.swift.org/t/warnings-as-errors-exceptions/72925)) ([review](https://forums.swift.org/t/se-0443-precise-control-flags-over-compiler-warnings/74116)) -* Previous revisions: [1](https://github.com/swiftlang/swift-evolution/blob/57fe29d5d55edb85b14c153b7f4cbead6b6539eb/proposals/0443-warning-control-flags.md) +* Previous revisions: [1](https://github.com/swiftlang/swift-evolution/blob/57fe29d5d55edb85b14c153b7f4cbead6b6539eb/proposals/0443-warning-control-flags.md), [2](https://github.com/swiftlang/swift-evolution/blob/7b12899ad0d96002c793d33ef8109ec47c5d256f/proposals/0443-warning-control-flags.md) ## Introduction @@ -62,7 +62,7 @@ Diagnostic groups form an acyclic graph with the following properties: - A diagnostic group can be included in any number of diagnostic groups. This allows expressing the membership of a group in multiple supergroups, where appropriate. For example, the group `unsafe_global_actor_deprecated` is part of both the `deprecated` and `concurrency` groups. The internal structure of the graph may change to some extent. However, the set of diagnostics included in a diagnostic group (directly or transitively) should not shrink. There are two typical situations where the graph structure may change: -- When adding a new diagnostic to the compiler, we also add a new group corresponding to that diagnostic. This new group can also be included in one or more existing groups if it belongs to them. For example, it is expected that the `deprecated` group will continuously include new subgroups. +- When adding a new diagnostic to the compiler, consider creating a new group corresponding to that diagnostic. If the new group is created it can also be included in one or more existing groups if it belongs to them. For example, it is expected that the `deprecated` group will continuously include new subgroups. - If an existing diagnostic is split into more specific versions, and we want to allow users to use the more specific version in compiler options, a separate group is created for it, which **must** be included in the group of the original diagnostic. For example, suppose we split the `availability_deprecated` warning into a general version and a specialized version `availability_deprecated_same_module`, which the compiler emits if the deprecated symbol is declared in the same module. In this case, the `availability_deprecated_same_module` group must be added to the `availability_deprecated` group to ensure that the overall composition of the `availability_deprecated` group does not change. The final structure should look like this: @@ -234,6 +234,7 @@ Moreover, `-print-diagnostic-groups` provides a formalized version of the same f - Revisions based on review feedback: - `-Wsuppress` was excluded from the proposal. - `-suppress-warnings` was excluded from the unified model and addressed separately by forbidding its usage with the new flags. + - The guideline in the "Diagnostic Groups" subsection for adding a new diagnostic has been softened to a consideration. ## Acknowledgments From 5ea586e31ed1ea9bed5fab4779849129491c6c76 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 4 Sep 2024 07:45:53 -0700 Subject: [PATCH 3849/4563] SE-0427 is implemented in Swift 6.0. (#2555) --- proposals/0427-noncopyable-generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0427-noncopyable-generics.md b/proposals/0427-noncopyable-generics.md index 278cfb580d..e03d521a56 100644 --- a/proposals/0427-noncopyable-generics.md +++ b/proposals/0427-noncopyable-generics.md @@ -3,7 +3,7 @@ * Proposal: [SE-0427](0427-noncopyable-generics.md) * Authors: [Kavon Farvardin](https://github.com/kavon), [Tim Kientzle](https://github.com/tbkka), [Slava Pestov](https://github.com/slavapestov) * Review Manager: [Holly Borla](https://github.com/hborla), [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** +* Status: **Implemented (Swift 6.0)** * Implementation: On `main` gated behind `-enable-experimental-feature NoncopyableGenerics` * Previous Proposal: [SE-0390: Noncopyable structs and enums](0390-noncopyable-structs-and-enums.md) * Review: ([pitch](https://forums.swift.org/t/pitch-noncopyable-generics/68180)) ([first review](https://forums.swift.org/t/se-0427-noncopyable-generics/70525)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0427-noncopyable-generics/72039)) ([second review](https://forums.swift.org/t/second-review-se-0427-noncopyable-generics/72881)) ([acceptance](https://forums.swift.org/t/accepted-se-0427-noncopyable-generics/73560)) From 34c0d9381ac64f11a06df85b477a5e2c416ce4f6 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 4 Sep 2024 12:37:24 -0400 Subject: [PATCH 3850/4563] Accept SE-0443 --- proposals/0443-warning-control-flags.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0443-warning-control-flags.md b/proposals/0443-warning-control-flags.md index 7fdf135616..1dcb5012fc 100644 --- a/proposals/0443-warning-control-flags.md +++ b/proposals/0443-warning-control-flags.md @@ -4,9 +4,9 @@ * Proposal: [SE-0443](0443-warning-control-flags.md) * Authors: [Doug Gregor](https://github.com/douggregor), [Dmitrii Galimzianov](https://github.com/DmT021) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (August 22nd...September 2nd, 2024)** +* Status: **Accepted** * Implementation: [apple/swift#74466](https://github.com/swiftlang/swift/pull/74466) -* Review: ([pitch](https://forums.swift.org/t/warnings-as-errors-exceptions/72925)) ([review](https://forums.swift.org/t/se-0443-precise-control-flags-over-compiler-warnings/74116)) +* Review: ([pitch](https://forums.swift.org/t/warnings-as-errors-exceptions/72925)) ([review](https://forums.swift.org/t/se-0443-precise-control-flags-over-compiler-warnings/74116)) ([acceptance](https://forums.swift.org/t/accepted-se-0443-precise-control-flags-over-compiler-warnings/74377)) * Previous revisions: [1](https://github.com/swiftlang/swift-evolution/blob/57fe29d5d55edb85b14c153b7f4cbead6b6539eb/proposals/0443-warning-control-flags.md), [2](https://github.com/swiftlang/swift-evolution/blob/7b12899ad0d96002c793d33ef8109ec47c5d256f/proposals/0443-warning-control-flags.md) ## Introduction From addf9bba367f911e8bdb9ed6e0d56ac98dfd43d4 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Mon, 9 Sep 2024 15:01:46 -0700 Subject: [PATCH 3851/4563] Add link to the pitch thread in the 'Member import visibility proposal'. --- proposals/NNNN-member-import-visibility.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-member-import-visibility.md b/proposals/NNNN-member-import-visibility.md index d612e7d05a..fad276a050 100644 --- a/proposals/NNNN-member-import-visibility.md +++ b/proposals/NNNN-member-import-visibility.md @@ -7,7 +7,7 @@ * Bug: [apple/swift#46493](https://github.com/apple/swift/issues/46493) * Implementation: [apple/swift#72974](https://github.com/apple/swift/pull/72974), [apple/swift#73063](https://github.com/apple/swift/pull/73063) * Upcoming Feature Flag: `MemberImportVisibility` -* Review: TBD +* Pitch: [Forum Thread](https://forums.swift.org/t/pitch-fixing-member-import-visibility/71432) ## Introduction @@ -146,4 +146,4 @@ One alternative approach to the problem would be to rely exclusively on a new sy ## Acknowledgments -I would like to thank Doug Gregor for providing a proof-of-concept implementation of this pitch. \ No newline at end of file +I would like to thank Doug Gregor for providing a proof-of-concept implementation of this pitch. From 968e64c0723b1cbcfc875c1024dfe792c3d6b758 Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Tue, 10 Sep 2024 16:49:04 -0700 Subject: [PATCH 3852/4563] Add a proposal to allow `nonisolated` to prevent global actor inference. --- ...NNN-nonisolated-for-global-actor-cutoff.md | 291 ++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 proposals/NNNN-nonisolated-for-global-actor-cutoff.md diff --git a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md new file mode 100644 index 0000000000..624f938124 --- /dev/null +++ b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md @@ -0,0 +1,291 @@ +# Allow `nonisolated` to prevent global actor inference + +* Proposal: [SE-NNNN](NNNN-nonisolated-for-global-actor-cutoff.md) +* Authors: [Sima Nerush](https://github.com/simanerush), [Holly Borla](https://github.com/hborla) +* Review Manager: TBD +* Status: **Implemented** +* Implementation: [swiftlang/swift#76395](https://github.com/swiftlang/swift/pull/76395) +* Review: ([pitch](https://forums.swift.org/t/pitch-allow-nonisolated-to-prevent-global-actor-inference/74502)) + +## Introduction + +This proposal allows annotating a set of declarations with `nonisolated` to prevent global actor inference. + +## Motivation + +Global actor inference has a number of different inference sources. For example, a global actor may be inferred on a type that conforms to a protocol because the protocol is annotated with a global actor attribute: + +```swift +@MainActor +protocol GloballyIsolated {} + +struct S: GloballyIsolated {} // implicitly globally-isolated +``` + +In the above code, the struct `S` is inferring the global actor isolation from the explicitly globally-isolated protocol `GloballyIsolated` which it conforms to. While this code is straightforward, the conformance list can quickly get long, and global actor isolation can be inferred through a chain of protocol refinements or superclasses. It can become difficult for a programmer to understand where the global isolation is being inferred from on a given type. + +While it is safe for a type with nonisolated methods to conform to a protocol marked with a global actor attribute, sometimes the programmer may want their type to be nonisolated. However, it is challenging to stop global actor inference from happening altogether. Programmers can annotate individual functions with the `nonisolated` keyword, but there is no straightforward way to prevent global actor inference on a type. + +Currently, there are two common ways a programmer can “cut-off” the global actor inference from happening on a type when inference comes from a conformance. The first way is to conform to a protocol that causes global isolation to be inferred in an extension, and then marking all of its required properties and methods as `nonisolated`: + +```swift +@MainActor +protocol P { + var x: Int { get } +} + +struct S {} + +extension S: P { + nonisolated var x: Int { + get { 1 } + } + nonisolated func test() { + print(x) + } +} +``` + +In the above code, `S` can still conform to the globally-isolated protocol `P` without inferring the isolation, but this comes at a cost of the programmer having to manually annotate each protocol requirement with `nonisolated`. + +However, the above method would not work for cutting off the global isolation inference on a protocol itself. There is a very nonobvious workaround: when the compiler is inferring global actor isolation, if there are multiple inference sources with conflicting global actors, no global actor is inferred. This is demonstrated by the following example: + +```swift +class FakeExecutor: FakeGlobalActor { + static let shared: FakeExecutor = .init() + + func enqueue(_ job: consuming ExecutorJob) { + fatalError() + } +} + +@globalActor +public actor FakeGlobalActor: Sendable { + public static var shared = FakeGlobalActor() + + private init() {} + public nonisolated var unownedExecutor: UnownedSerialExecutor { + FakeGlobalActor.shared.asUnownedSerialExecutor() + } +} + +@MainActor +protocol GloballyIsolated {} + +@FakeGlobalActor +protocol RemoveGlobalActor + +protocol RefinedProtocol: GloballyIsolated, RemoveGlobalActor {} // 'RefinedProtocol' is non-isolated +``` + +In the above code, the programmer creates a new protocol that is isolated to an actor that nominally is isolated to the global actor. This means that the protocol declaration `RefinedProtocol` refining the `RemoveGlobalActor` protocol will result in a conflicting global actor isolation, one from `GloballyIsolated` that’s isolated to `@MainActor`, and another one from `RemoveGlobalActor` that’s isolated to the `@FakeGlobalActor`. This results in the overall declaration having no global actor isolation, while still refining the protocols it conformed to. + + +## Proposed solution + +We propose to allow explicitly writing `nonisolated` on all type and protocol declarations for opting out of the global isolation inference: + +```swift +nonisolated struct S: GloballyIsolated, NonIsolatedProto {} // 'S' won't inherit isolation from 'GloballyIsolated' protocol +``` + +In the above code, the programmer cuts off the global actor inference coming from the `GloballyIsolated` protocol for the struct `S`. Now, the workaround where the programmer had to write an additional protocol with global actor isolation is no longer needed. + +```swift +nonisolated protocol P: GloballyIsolated {} // 'P' won't inherit isolation of 'GloballyIsolated' protocol +``` + +And in the above code, the protocol `P` refines the `GloballyIsolated` protocol. Because `nonisolated` is applied to it, the global actor isolation coming from the `GloballyIsolated` protocol will not be inferred for protocol `P`. + +## Detailed design + +Today, there are a number of places where `nonisolated` can be written, as proposed in [SE-0313: Improved control over actor isolation](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0313-actor-isolation-control.md#non-isolated-declarations): + +* Functions +* Stored properties of classes that are `let` and `Sendable` + +Additionally, under [SE-0434: Usability of global-actor-isolated types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md), `nonisolated` is allowed to be written on mutable `Sendable` storage of globally-isolated value types. + +In this proposal, we expand the above rules by allowing annotating the declarations listed below with `nonisolated`. + +### 1. Protocols + +This proposal allows `nonisolated` attribute to be applied on protocol declarations: + +```swift +nonisolated protocol Refined: GloballyIsolated {} + +struct A: Refined { + var x: NonSendable + nonisolated func printX() { + print(x) // okay, 'x' is non-isolated + } +} +``` + +In the above code, the protocol `Refined` is refining the `GloballyIsolated` protocol, but is declared non-isolated. This means that the `Refined` still has the same requirements as `GloballyIsolated`, but they are not isolated. Therefore, a struct `A` conforming to it is also non-isolated, which allows the programmer for more flexibility when implementing the requirements of a protocol. + +### 2. Extensions + +This proposal allows for `nonisolated` attribute to be applied on extension declarations: + +```swift +nonisolated extension GloballyIsolated { + var x: NonSendable { .init() } + func implicitlyNonisolated() {} +} + +struct C: GloballyIsolated { + nonisolated func explicitlyNonisolated() { + let _ = x // okay + implicitlyNonisolated() // okay + } +} +``` + +In the code above, the `nonisolated` attribute is applied to an extension declaration for a `GloballyIsolated` protocol. When applied to an extension, `nonisolated` applies to all of its members. In this case, `implicitlyNonisolated` method and the computed property `x` are both nonisolated, and therefore are able to be accessed from a nonisolated context in the body of `explicitlyNonisolated` method of a globally-isolated struct `C`. + +### 3. Stored properties of non-`Sendable` types + +Currently, any stored property of a non-`Sendable` type is implicitly treated as non-isolated. This proposal allows for spelling of this behavior: + +```swift +class MyClass { + nonisolated var x: NonSendable = NonSendable() // okay +} +``` + +Because `MyClass` is does not conform to `Sendable`, the compiler guarantees mutually exclusive access to references of `MyClass` instance. `nonisolated` on methods and properties of non-`Sendable` types can be safely called from any isolation domain because the base instance can only be accessed by one isolation domain at a time. + +### 4. Mutable `Sendable` storage of `Sendable` value types + +For global-actor-isolated value types, [SE-0434: Usability of global-actor-isolated types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows accessing `var` stored properties with `Sendable` type from within the module as `nonisolated`. This proposal extends this rule to **all** `Sendable` value types: + +```swift +struct S { + nonisolated var x: Int // okay +} +``` + +In the above code, the value type `S` is implicitly `Sendable` because its storage `x` is of `Sendable` type `Int`. When `Sendable` value types are passed between isolation domains, each isolation domain has an independent copy of the value. Accessing stored properties of a value type from across isolation domains is safe as long as the stored property type is also `Sendable`. Even if the stored property is a `var`, assigning to the property will not risk a data race, because the assignment cannot have effects on copies in other isolation domains. Therefore, synchronized access to `x` in the example above is safe. + +### 5. Classes, structs, and enums + +Finally, we propose allowing writing `nonisolated` on class, struct and enum declarations: + +```swift +nonisolated class K: GloballyIsolated { + var x: NonSendable + init(x: NonSendable) { + self.x = x // okay, 'x' is non-isolated + } +} + +nonisolated struct S: GloballyIsolated { + var x: NonSendable + init(x: NonSendable) { + self.x = x // okay, 'x' is non-isolated + } +} + +nonisolated enum E: GloballyIsolated { + func implicitlyNonisolated() {} + init() {} +} + +struct TestEnum { + nonisolated func call() { + E().implicitlyNonisolated() // okay + } +} +``` + +In all the above declarations, the `nonisolated` attribute propagates to all of their members, therefore making them accessible from a non-isolated context. + +Importantly, types nested inside of explicitly `nonisolated` declarations still infer actor isolation from their own conformance lists: + +```swift +nonisolated struct S: GloballyIsolated { + var value: NotSendable // 'value' is not isolated + struct Nested: GloballyIsolated {} // 'Nested' is still @MainActor-isolated +} +``` + +The above behavior is semantically consistent with the existing rules around global isolation inference for members of a type: + +```swift +@MainActor struct S { + var value: NotSendable // globally-isolated + struct Nested {} // 'Nested' is not @MainActor-isolated +} +``` + +### Restrictions + +Additionally, we propose the following set of rules for when the `nonisolated` attribute **cannot** be applied: + +* Along with some other isolation such as a global actor or an isolated parameter: + +```swift +@MainActor +nonisolated struct Conflict {} // error: 'struct 'Conflict' has multiple actor-isolation attributes ('nonisolated' and 'MainActor')' +``` + +* On a property of a `Sendable` type when the type of the property does not conform to `Sendable`: + +```swift +@MainActor +struct InvalidStruct /* implicitly Sendable */ { + nonisolated let test: NonSendable // error: 'nonisolated' can not be applied to variable with non-'Sendable' type 'NonSendable +} +``` + +* On a property of a `Sendable` class when the property is a var: + +```swift +@MainActor +final class InvalidClass /* implicitly Sendable */ { + nonisolated var test: Int = 1 // error: 'nonisolated' cannot be applied to mutable stored properties +} +``` + +## Source compatibility + +None, this is an additive change to the concurrency model. + +## ABI compatibility + +None, this proposal does not affect any existing inference rules of the concurrency model. + +## Implications on adoption + +Consider the following code: + +```swift +class C: GloballyIsolated {} +``` + +`C` currently has an implicit conformance to `Sendable` based on `@MainActor`-inference. Let’s consider what happens when `nonisolated` is adopted for `C`: + +```swift +nonisolated class C: GloballyIsolated +``` + +Now, `C` is no longer implicitly `Sendable`, since the global actor inference is cut off. This can break source compatibility for clients who have relied on the `Sendable` capability of `C`. + +## Alternatives considered + +### Allowing `nonisolated` on individual types and protocols in the conformance list + +Allowing `nonisolated` on individual types and protocols in the conformance list would allow the programmer to opt-out of the global isolation inference from just one or more protocols or types: + +```swift +@MyActor +protocol MyActorIsolated {} + +struct S: nonisolated GloballyIsolated, MyActorIsolated {} // 'S' is isolated to 'MyActor' +``` + +In the above code, by selectively applying `nonisolated`, the programmer is able to avoid global actor inference happening from just one of these protocols, meaning the struct `S` can retain isolation, in this case, to `MyActor`. + +However, this approach is too cumbersome — the programmer is always able to explicitly specify isolation they want on the type. It also becomes harder to opt-out from any inference from happening, as in the extreme case, the `nonisolated` keyword would have to be applied to every single type or protocol in the conformance list. From 1fc923b87f6b032d96b737e7e9cdcb8a4effaf79 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Tue, 10 Sep 2024 17:16:53 -0700 Subject: [PATCH 3853/4563] Update 'Member import visiblity' proposal to address pre-flight comments. --- proposals/NNNN-member-import-visibility.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/proposals/NNNN-member-import-visibility.md b/proposals/NNNN-member-import-visibility.md index fad276a050..b746110ee2 100644 --- a/proposals/NNNN-member-import-visibility.md +++ b/proposals/NNNN-member-import-visibility.md @@ -93,15 +93,15 @@ In a future language version, or whenever the `MemberImportVisibility` feature i ## Detailed design -A reference to a member in a source file will only be accepted if that member is declared in a module that is contained in the set of visible modules for that source file. A module is in the set of visible modules if any of the following statements are true: +A reference to a member in a source file will only be accepted if that member is declared in a module that is contained in the set of visible modules for that source file. According to the existing rules for top level name lookup, a module is visible if any of the following statements are true: - The module is directly imported. In other words, some import statement in the source file names the module explicitly. - The module is directly imported from the bridging header. - The module is in the set of modules that is re-exported by any module that is either directly imported in the file or directly imported in the bridging header. -A Swift module re-exports any modules that have been imported using the `@_exported` attribute. Clang modules list the modules that they re-export in their modulemap files, and it is common for a Clang module to re-export every module it imports using `export *`. Re-exports are also transitive, so if module `A` re-exports module `B`, and module `B` re-exports module `C`, then declarations from `A`, `B`, and `C` are all in scope in a file that only imports `A` directly. +A re-exported module is one that another module makes visible to any client that imports it. Clang modules list the modules that they re-export in their modulemap files, and it is common for a Clang module to re-export every module it imports using `export *`. There is no officially supported way to re-export a module from a Swift module, but the compiler-internal `@_exported` attribute is sometimes used for this purpose. Re-exports are also transitive, so if module `A` re-exports module `B`, and module `B` re-exports module `C`, then declarations from `A`, `B`, and `C` are all in scope in a file that only imports `A` directly. -Note that there are some imports that are added to every source file implicitly by the compiler for normal programs. The implicitly imported modules include the standard library and the module being compiled. As a subtle consequence implicitly importing the current module, any module that is `@_exported` in any source file is also considered visible in every other source file because it is a re-export of a direct import. +Note that there are some imports that are added to every source file implicitly by the compiler for normal programs. The implicitly imported modules include the standard library and the module being compiled. As a subtle consequence implicitly importing the current module, any module that the current module re-exports in any of its source files will be considered visible in every other source file. ## Source compatibility @@ -126,7 +126,7 @@ let recipe = "1 scoop ice cream, 1 tbs chocolate syrup".parse() With these fix-its, the burden of updating source code to be compatible with the new language mode should be significantly reduced. -This feature will have some impact on source compatibility with older compilers and previous language modes. Adding new direct imports of modules that were previously only transitively imported is a backward compatible change syntactically. However, if the new language mode is necessary in order to make some source code unambiguous, then the ambiguity will become an issue when compiling the same code using an older language mode so maintaining backward compatibility would require additional measures to be taken. +This feature will have some impact on source compatibility with older compilers and previous language modes. Adding new direct imports of modules that were previously only transitively imported is a backward compatible change syntactically. However, it's possible that source code could depend on the new language mode in order to make the code unambiguous. In these cases additional measures, like adding explicit type annotations, might be required to maintain backward compatibility with previous language modes. ## Future directions @@ -138,6 +138,14 @@ This proposal seeks to give developers explicit control over which members are v let recipe = "...".RecipeKit::parse() ``` +#### Implement rules for retroactive conformance visibility + +A typical protocol conformance in Swift is declared in either the module that declares the protocol or the module of the conforming type and this allows the compiler to enforce that conformances are globally unique. However, Swift also allows conformances to be "retroactive", which means that the conformance is declared in some third module and cannot be guaranteed to be unique. Retroactive conformances make it possible for multiple declarations of the same protocol conformance from different dependency modules to be simultaneously visible to the compiler. Currently, there is no defined way to influence which conformance declaration gets chosen when such an ambiguity arises, but a rule similar to the one in this proposal could be implemented for conformance lookup as well. For instance, a direct import of a module could be required in order to bring the conformances from the module into scope in a source file. + +#### Improve lookup for operators and precedence groups + +Swift has bespoke rules for looking up operators and operator precedence groups that differ in important ways from the rules for both top-level names and members. Documenting the existing rules and reforming them to bring more consistency to name lookup in Swift would be worthy of a future proposal. + ## Alternatives considered #### Introduce module qualification syntax for extension members instead From 141d8394bf657097d4bd85183a3155c18ebd100d Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 11 Sep 2024 10:55:23 +0900 Subject: [PATCH 3854/4563] Fix some code snippets in NNNN-nonisolated-for-global-actor-cutoff.md The first code snippet has some non-correct code that would not compile. It's showcasing the bad workaround, but still, it should be compiling code :) --- proposals/NNNN-nonisolated-for-global-actor-cutoff.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md index 624f938124..42a140bf83 100644 --- a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md +++ b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md @@ -51,7 +51,7 @@ In the above code, `S` can still conform to the globally-isolated protocol `P` w However, the above method would not work for cutting off the global isolation inference on a protocol itself. There is a very nonobvious workaround: when the compiler is inferring global actor isolation, if there are multiple inference sources with conflicting global actors, no global actor is inferred. This is demonstrated by the following example: ```swift -class FakeExecutor: FakeGlobalActor { +final class FakeExecutor: SerialExecutor { static let shared: FakeExecutor = .init() func enqueue(_ job: consuming ExecutorJob) { @@ -65,7 +65,7 @@ public actor FakeGlobalActor: Sendable { private init() {} public nonisolated var unownedExecutor: UnownedSerialExecutor { - FakeGlobalActor.shared.asUnownedSerialExecutor() + FakeExecutor.shared.asUnownedSerialExecutor() } } @@ -73,7 +73,7 @@ public actor FakeGlobalActor: Sendable { protocol GloballyIsolated {} @FakeGlobalActor -protocol RemoveGlobalActor +protocol RemoveGlobalActor {} protocol RefinedProtocol: GloballyIsolated, RemoveGlobalActor {} // 'RefinedProtocol' is non-isolated ``` From 2514d13b6f6717e53870ac9d4873a5c450e84ca2 Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Tue, 10 Sep 2024 20:57:50 -0700 Subject: [PATCH 3855/4563] Reword and clarify per feedback. --- ...NNN-nonisolated-for-global-actor-cutoff.md | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md index 42a140bf83..ee0244d8bf 100644 --- a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md +++ b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md @@ -163,11 +163,36 @@ For global-actor-isolated value types, [SE-0434: Usability of global-actor-isola ```swift struct S { - nonisolated var x: Int // okay + var x: Int = 0 // okay ('nonisolated' is inferred within the module) +} + +actor MyActor { + func test(s: S) { + print(s.x) // synchronous access to 'x' after sending `S` to `MyActor` is okay. + } } ``` -In the above code, the value type `S` is implicitly `Sendable` because its storage `x` is of `Sendable` type `Int`. When `Sendable` value types are passed between isolation domains, each isolation domain has an independent copy of the value. Accessing stored properties of a value type from across isolation domains is safe as long as the stored property type is also `Sendable`. Even if the stored property is a `var`, assigning to the property will not risk a data race, because the assignment cannot have effects on copies in other isolation domains. Therefore, synchronized access to `x` in the example above is safe. +In the above code, the value type `S` is implicitly `Sendable` within the module and its storage `x` is of `Sendable` type `Int`. When `Sendable` value types are passed between isolation domains, each isolation domain has an independent copy of the value. Accessing properties stored on a value type from across isolation domains is safe as long as the stored property type is also `Sendable`. Even if the stored property is a `var`, assigning to the property will not risk a data race, because the assignment cannot have effects on copies in other isolation domains. Therefore, synchronous access of `x` from within the module is okay. + +Additionally, [SE-0434](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows explicitly annotating globally-isolated value types' properties such as `x` in the previous example with `nonisolated` for synchronous access from outside the module. This proposal extends this rule to **all** `Sendable` value types: + +```swift +// In Module A +public struct S: Sendable { + nonisolated public var x: Int = 0 // okay + public init() {} +} + +// In Module B +import A + +actor MyActor { + func test(s: S) { + print(s.x) // synchronous access to 'x' after sending `S` to `MyActor` is okay. + } +} +``` ### 5. Classes, structs, and enums @@ -224,23 +249,27 @@ The above behavior is semantically consistent with the existing rules around glo Additionally, we propose the following set of rules for when the `nonisolated` attribute **cannot** be applied: -* Along with some other isolation such as a global actor or an isolated parameter: +#### Along with some other isolation such as a global actor or an isolated parameter: ```swift @MainActor nonisolated struct Conflict {} // error: 'struct 'Conflict' has multiple actor-isolation attributes ('nonisolated' and 'MainActor')' ``` -* On a property of a `Sendable` type when the type of the property does not conform to `Sendable`: +The above code is invalid because the `Conflict` struct cannot simultaneously opt-out of isolation and declare one. + +#### On a property of a `Sendable` type when the type of the property does not conform to `Sendable`: ```swift @MainActor struct InvalidStruct /* implicitly Sendable */ { - nonisolated let test: NonSendable // error: 'nonisolated' can not be applied to variable with non-'Sendable' type 'NonSendable + nonisolated let x: NonSendable // error: 'nonisolated' can not be applied to variable with non-'Sendable' type 'NonSendable } ``` -* On a property of a `Sendable` class when the property is a var: +In the above code, `InvalidStruct` is `Sendable`, allowing it to be sent across the concurrency domains. The property `x` is of `NonSendable` type, and if declared `nonisolated`, it would be allowed to be accessed from outside the main actor domain that `InvalidStruct` is isolated to, thus contradicting its lack of `Sendable` capability. + +#### On a property of a `Sendable` class when the property is a var: ```swift @MainActor @@ -249,6 +278,8 @@ final class InvalidClass /* implicitly Sendable */ { } ``` +In this example, `InvalidClass` is a `Sendable` reference type, which allows concurrent synchronous access to `test` since it is `nonisolated`. This introduces a potential data race. + ## Source compatibility None, this is an additive change to the concurrency model. From 955e432384d6921c2d0454cc3cd6f30e89c98b0d Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 11 Sep 2024 09:16:06 -0700 Subject: [PATCH 3856/4563] Assign and schedule SE-0444 --- ...ort-visibility.md => 0444-member-import-visibility.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{NNNN-member-import-visibility.md => 0444-member-import-visibility.md} (97%) diff --git a/proposals/NNNN-member-import-visibility.md b/proposals/0444-member-import-visibility.md similarity index 97% rename from proposals/NNNN-member-import-visibility.md rename to proposals/0444-member-import-visibility.md index b746110ee2..e4a18863aa 100644 --- a/proposals/NNNN-member-import-visibility.md +++ b/proposals/0444-member-import-visibility.md @@ -1,13 +1,13 @@ # Member import visibility -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0444](0444-member-import-visibility.md) * Authors: [Allan Shortlidge](https://github.com/tshortli) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) +* Status: **Active review (September 11...23, 2024)** * Bug: [apple/swift#46493](https://github.com/apple/swift/issues/46493) * Implementation: [apple/swift#72974](https://github.com/apple/swift/pull/72974), [apple/swift#73063](https://github.com/apple/swift/pull/73063) * Upcoming Feature Flag: `MemberImportVisibility` -* Pitch: [Forum Thread](https://forums.swift.org/t/pitch-fixing-member-import-visibility/71432) +* Review: ([pitch](https://forums.swift.org/t/pitch-fixing-member-import-visibility/71432)) ## Introduction From d825dbf61c84c09f0826d764ce51923498833304 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 11 Sep 2024 09:39:10 -0700 Subject: [PATCH 3857/4563] Start review for SE-0444 --- proposals/0444-member-import-visibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0444-member-import-visibility.md b/proposals/0444-member-import-visibility.md index e4a18863aa..4404f604e6 100644 --- a/proposals/0444-member-import-visibility.md +++ b/proposals/0444-member-import-visibility.md @@ -7,7 +7,7 @@ * Bug: [apple/swift#46493](https://github.com/apple/swift/issues/46493) * Implementation: [apple/swift#72974](https://github.com/apple/swift/pull/72974), [apple/swift#73063](https://github.com/apple/swift/pull/73063) * Upcoming Feature Flag: `MemberImportVisibility` -* Review: ([pitch](https://forums.swift.org/t/pitch-fixing-member-import-visibility/71432)) +* Review: ([pitch](https://forums.swift.org/t/pitch-fixing-member-import-visibility/71432)) ([review](https://forums.swift.org/t/se-0444-member-import-visibility/74519)) ## Introduction From 5c2cf66f734938a461811ca7f4866e0e1ff1df80 Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Wed, 11 Sep 2024 10:06:20 -0700 Subject: [PATCH 3858/4563] Call out the `nonisolated` usability improvements in its own section of the proposal. --- ...NNN-nonisolated-for-global-actor-cutoff.md | 122 ++++++++++-------- 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md index ee0244d8bf..bda709cfc3 100644 --- a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md +++ b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md @@ -9,7 +9,7 @@ ## Introduction -This proposal allows annotating a set of declarations with `nonisolated` to prevent global actor inference. +This proposal allows annotating a set of declarations with `nonisolated` to prevent global actor inference. Additionally, it extends the existing rules for when `nonisolated` can be written on a stored property, improving usability. ## Motivation @@ -78,7 +78,7 @@ protocol RemoveGlobalActor {} protocol RefinedProtocol: GloballyIsolated, RemoveGlobalActor {} // 'RefinedProtocol' is non-isolated ``` -In the above code, the programmer creates a new protocol that is isolated to an actor that nominally is isolated to the global actor. This means that the protocol declaration `RefinedProtocol` refining the `RemoveGlobalActor` protocol will result in a conflicting global actor isolation, one from `GloballyIsolated` that’s isolated to `@MainActor`, and another one from `RemoveGlobalActor` that’s isolated to the `@FakeGlobalActor`. This results in the overall declaration having no global actor isolation, while still refining the protocols it conformed to. +In the above code, the programmer creates a new protocol that is isolated to an actor that nominally is isolated to the global actor. This means that the protocol declaration `RefinedProtocol` refining the `RemoveGlobalActor` protocol will result in a conflicting global actor isolation, one from `GloballyIsolated` that’s isolated to `@MainActor`, and another one from `RemoveGlobalActor` that’s isolated to the `@FakeGlobalActor`. This results in the overall declaration having no global actor isolation, while still refining the protocols it conformed to. ## Proposed solution @@ -97,6 +97,8 @@ nonisolated protocol P: GloballyIsolated {} // 'P' won't inherit isolation of 'G And in the above code, the protocol `P` refines the `GloballyIsolated` protocol. Because `nonisolated` is applied to it, the global actor isolation coming from the `GloballyIsolated` protocol will not be inferred for protocol `P`. +In addition to the above, we propose extending existing rules for when `nonisolated` can be applied to stored properties to improve usability. More precisely, we propose `nonisolated` inference from within the module for mutable storage of `Sendable` value types, and annotating such storage with `nonisolated` to allow synchronous access from outside the module. Additionally, we propose explicit spelling of `nonisolated` for stored properties of non-`Sendable` types. + ## Detailed design Today, there are a number of places where `nonisolated` can be written, as proposed in [SE-0313: Improved control over actor isolation](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0313-actor-isolation-control.md#non-isolated-declarations): @@ -104,11 +106,13 @@ Today, there are a number of places where `nonisolated` can be written, as propo * Functions * Stored properties of classes that are `let` and `Sendable` -Additionally, under [SE-0434: Usability of global-actor-isolated types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md), `nonisolated` is allowed to be written on mutable `Sendable` storage of globally-isolated value types. +Additionally, under [SE-0434: Usability of global-actor-isolated types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md), `nonisolated` is allowed to be written on mutable `Sendable` storage of globally-isolated value types. + +In this proposal, we expand the above rules by allowing annotating more declarations with `nonisolated`. The first batch of these rules is specifically targeting the global actor inference "cut-off", while the second focuses on usability improvements allowing `nonisolated` to be written on more kinds of storage. -In this proposal, we expand the above rules by allowing annotating the declarations listed below with `nonisolated`. +### Allowing `nonisolated` to prevent global actor inference -### 1. Protocols +#### Protocols This proposal allows `nonisolated` attribute to be applied on protocol declarations: @@ -125,7 +129,7 @@ struct A: Refined { In the above code, the protocol `Refined` is refining the `GloballyIsolated` protocol, but is declared non-isolated. This means that the `Refined` still has the same requirements as `GloballyIsolated`, but they are not isolated. Therefore, a struct `A` conforming to it is also non-isolated, which allows the programmer for more flexibility when implementing the requirements of a protocol. -### 2. Extensions +#### Extensions This proposal allows for `nonisolated` attribute to be applied on extension declarations: @@ -145,56 +149,7 @@ struct C: GloballyIsolated { In the code above, the `nonisolated` attribute is applied to an extension declaration for a `GloballyIsolated` protocol. When applied to an extension, `nonisolated` applies to all of its members. In this case, `implicitlyNonisolated` method and the computed property `x` are both nonisolated, and therefore are able to be accessed from a nonisolated context in the body of `explicitlyNonisolated` method of a globally-isolated struct `C`. -### 3. Stored properties of non-`Sendable` types - -Currently, any stored property of a non-`Sendable` type is implicitly treated as non-isolated. This proposal allows for spelling of this behavior: - -```swift -class MyClass { - nonisolated var x: NonSendable = NonSendable() // okay -} -``` - -Because `MyClass` is does not conform to `Sendable`, the compiler guarantees mutually exclusive access to references of `MyClass` instance. `nonisolated` on methods and properties of non-`Sendable` types can be safely called from any isolation domain because the base instance can only be accessed by one isolation domain at a time. - -### 4. Mutable `Sendable` storage of `Sendable` value types - -For global-actor-isolated value types, [SE-0434: Usability of global-actor-isolated types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows accessing `var` stored properties with `Sendable` type from within the module as `nonisolated`. This proposal extends this rule to **all** `Sendable` value types: - -```swift -struct S { - var x: Int = 0 // okay ('nonisolated' is inferred within the module) -} - -actor MyActor { - func test(s: S) { - print(s.x) // synchronous access to 'x' after sending `S` to `MyActor` is okay. - } -} -``` - -In the above code, the value type `S` is implicitly `Sendable` within the module and its storage `x` is of `Sendable` type `Int`. When `Sendable` value types are passed between isolation domains, each isolation domain has an independent copy of the value. Accessing properties stored on a value type from across isolation domains is safe as long as the stored property type is also `Sendable`. Even if the stored property is a `var`, assigning to the property will not risk a data race, because the assignment cannot have effects on copies in other isolation domains. Therefore, synchronous access of `x` from within the module is okay. - -Additionally, [SE-0434](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows explicitly annotating globally-isolated value types' properties such as `x` in the previous example with `nonisolated` for synchronous access from outside the module. This proposal extends this rule to **all** `Sendable` value types: - -```swift -// In Module A -public struct S: Sendable { - nonisolated public var x: Int = 0 // okay - public init() {} -} - -// In Module B -import A - -actor MyActor { - func test(s: S) { - print(s.x) // synchronous access to 'x' after sending `S` to `MyActor` is okay. - } -} -``` - -### 5. Classes, structs, and enums +#### Classes, structs, and enums Finally, we propose allowing writing `nonisolated` on class, struct and enum declarations: @@ -245,6 +200,61 @@ The above behavior is semantically consistent with the existing rules around glo } ``` +### Annotating more types of storage with `nonisolated` + +This section extends the existing rules for when `nonisolated` can be written on a storage of a user-defined type. + +#### Stored properties of non-`Sendable` types + +Currently, any stored property of a non-`Sendable` type is implicitly treated as non-isolated. This proposal allows for spelling of this behavior: + +```swift +class MyClass { + nonisolated var x: NonSendable = NonSendable() // okay +} +``` + +Because `MyClass` does not conform to `Sendable`, it cannot be accessed from multiple isolation domains at once. Therefore, the compiler guarantees mutually exclusive access to references of `MyClass` instance. The `nonisolated` on methods and properties of non-`Sendable` types can be safely called from any isolation domain because the base instance can only be accessed by one isolation domain at a time. Importantly, `nonisolated` does not impact the number of isolation domains that can reference the `self` value. As long as there is a reference to `self` value in one isolation domain, the `nonisolated` method/property can be safely called from that domain. + +#### Mutable `Sendable` storage of `Sendable` value types + +For global-actor-isolated value types, [SE-0434: Usability of global-actor-isolated types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows accessing `var` stored properties with `Sendable` type from within the module as `nonisolated`. This proposal extends this rule to **all** `Sendable` value types: + +```swift +struct S { + var x: Int = 0 // okay ('nonisolated' is inferred within the module) +} + +actor MyActor { + func test(s: S) { + print(s.x) // synchronous access to 'x' after sending `S` to `MyActor` is okay. + } +} +``` + +In the above code, the value type `S` is implicitly `Sendable` within the module and its storage `x` is of `Sendable` type `Int`. When `Sendable` value types are passed between isolation domains, each isolation domain has an independent copy of the value. Accessing properties stored on a value type from across isolation domains is safe as long as the stored property type is also `Sendable`. Even if the stored property is a `var`, assigning to the property will not risk a data race, because the assignment cannot have effects on copies in other isolation domains. Therefore, synchronous access of `x` from within the module is okay. + +Additionally, [SE-0434](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows explicitly annotating globally-isolated value types' properties such as `x` in the previous example with `nonisolated` for synchronous access from outside the module. This proposal extends this rule to **all** `Sendable` value types: + +```swift +// In Module A +public struct S: Sendable { + nonisolated public var x: Int = 0 // okay + public init() {} +} +``` + +```swift +// In Module B +import A + +actor MyActor { + func test(s: S) { + print(s.x) // synchronous access to 'x' after sending `S` to `MyActor` is okay. + } +} +``` + ### Restrictions Additionally, we propose the following set of rules for when the `nonisolated` attribute **cannot** be applied: From 96b90fda924ddc6f483631f3cb92ce84fb16ac30 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 5 Sep 2024 09:23:38 +0100 Subject: [PATCH 3859/4563] [SE-0371] Isolated synchronous deinit is accepted Update status and review fields. --- proposals/0371-isolated-synchronous-deinit.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0371-isolated-synchronous-deinit.md b/proposals/0371-isolated-synchronous-deinit.md index d91e7fd4ae..06341e5aef 100644 --- a/proposals/0371-isolated-synchronous-deinit.md +++ b/proposals/0371-isolated-synchronous-deinit.md @@ -3,9 +3,9 @@ * Proposal: [SE-0371](0371-isolated-synchronous-deinit.md) * Author: [Mykola Pokhylets](https://github.com/nickolas-pohilets) * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) -* Status: **Active review (July 23...August 6, 2024)** +* Status: **Accepted with modifications** * Implementation: [apple/swift#60057](https://github.com/apple/swift/pull/60057) -* Review: ([pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) ([review](https://forums.swift.org/t/se-0371-isolated-synchronous-deinit/59754)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0371-isolated-synchronous-deinit/60060)) ([second review](https://forums.swift.org/t/second-review-se-0371-isolated-synchronous-deinit/73406)) +* Review: ([first pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) ([first review](https://forums.swift.org/t/se-0371-isolated-synchronous-deinit/59754)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0371-isolated-synchronous-deinit/60060)) ([discussion](https://forums.swift.org/t/isolated-synchronous-deinit-2/62565)) ([second pitch](https://forums.swift.org/t/pitch-2-se-0371-isolated-async-deinit/64836)) ([sub-pitch](https://forums.swift.org/t/sub-pitch-task-local-values-in-isolated-synchronous-deinit-and-async-deinit/70060)) ([second review](https://forums.swift.org/t/second-review-se-0371-isolated-synchronous-deinit/73406)) ([accepted with modifications](https://forums.swift.org/t/accepted-with-modifications-se-0371-isolated-synchronous-deinit/74042)) ## Introduction From 1c267ad4e6dbbc272880f0c5d30bade76daa1195 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Mon, 16 Sep 2024 15:23:17 -0700 Subject: [PATCH 3860/4563] Add proposal for useful String.Index descriptions (#2529) * Add proposal for useful String.Index descriptions * Edits and amendments - Add note on LLDB already shipping these displays as data formatters. - Expand Future Directions section with potential API additions that expose the underlying information for programmatic use. --- proposals/NNNN-string-index-printing.md | 176 ++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 proposals/NNNN-string-index-printing.md diff --git a/proposals/NNNN-string-index-printing.md b/proposals/NNNN-string-index-printing.md new file mode 100644 index 0000000000..cdca0638b7 --- /dev/null +++ b/proposals/NNNN-string-index-printing.md @@ -0,0 +1,176 @@ +# Feature name + +* Proposal: [SE-NNNN](NNNN-string-index-printing.md) +* Authors: [Karoy Lorentey](https://github.com/lorentey) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [apple/swift#75433](https://github.com/swiftlang/swift/pull/75433) +* Review: ([pitch](https://forums.swift.org/t/improving-string-index-s-printed-descriptions/57027)) + +## Introduction + +This proposal conforms `String.Index` to `CustomStringConvertible`. + +## Motivation + +String indices represent offsets from the start of the string's underlying storage representation, referencing a particular UTF-8 or UTF-16 code unit, depending on the string's encoding. (Most Swift strings are UTF-8 encoded, but strings bridged over from Objective-C may remain in their original UTF-16 encoded form.) + +If you ever tried printing a string index, you probably noticed that the output is gobbledygook: + +```swift +let string = "👋🏼 Helló" + +print(string.startIndex) // ⟹ Index(_rawBits: 15) +print(string.endIndex) // ⟹ Index(_rawBits: 983047) +print(string.utf16.index(after: string.startIndex)) // ⟹ Index(_rawBits: 16388) +print(string.firstRange(of: "ell")!) // ⟹ Index(_rawBits: 655623).. + +## Detailed design + +``` +@available(SwiftStdlib 6.1, *) +extension String.Index: CustomStringConvertible {} + +extension String.Index { + @backDeployed(before: SwiftStdlib 6.1) + public var description: String {...} +} +``` + +## Source compatibility + +The new conformance changes the result of converting a `String.Index` value to a string. This changes observable behavior: code that attempts to parse the result of `String(describing:)` can be mislead by the change of format. + +However, `String(describing:)` and `String(reflecting:)` explicitly state that when the input type conforms to none of the standard string conversion protocols, then the result of these operations is unspecified. + +Changing the value of an unspecified result is not considered to be a source incompatible change. + +## ABI compatibility + +The proposal retroactively conforms a previously existing standard type to a previously existing standard protocol. This is technically an ABI breaking change -- on ABI stable platforms, we may have preexisting Swift binaries that assume that `String.Index is CustomStringConvertible` returns `false`, or ones that are implementing this conformance on their own. + +We do not expect this to be an issue in practice. + +## Implications on adoption + +The `String.Index.description` property is defined to be backdeployable, but the conformance itself is not. (It cannot be.) + +Code that runs on ABI stable platforms will not get the nicer displays when running on earlier versions of the Swift Standard Library. + +```swift +let str = "🐕 Doggo" +print(str.firstRange(of: "Dog")!) +// older stdlib: Index(_rawBits: 327943).. Date: Mon, 16 Sep 2024 18:27:00 -0400 Subject: [PATCH 3861/4563] Claim SE-0445 (#2562) --- ...ring-index-printing.md => 0445-string-index-printing.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-string-index-printing.md => 0445-string-index-printing.md} (98%) diff --git a/proposals/NNNN-string-index-printing.md b/proposals/0445-string-index-printing.md similarity index 98% rename from proposals/NNNN-string-index-printing.md rename to proposals/0445-string-index-printing.md index cdca0638b7..cb39f791ea 100644 --- a/proposals/NNNN-string-index-printing.md +++ b/proposals/0445-string-index-printing.md @@ -1,9 +1,9 @@ # Feature name -* Proposal: [SE-NNNN](NNNN-string-index-printing.md) +* Proposal: [SE-0445](0445-string-index-printing.md) * Authors: [Karoy Lorentey](https://github.com/lorentey) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Active review (September 16...30, 2024)** * Implementation: [apple/swift#75433](https://github.com/swiftlang/swift/pull/75433) * Review: ([pitch](https://forums.swift.org/t/improving-string-index-s-printed-descriptions/57027)) From 029c4de4b5869d75d9e5629c365eaef863ba8980 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Mon, 16 Sep 2024 18:33:19 -0400 Subject: [PATCH 3862/4563] Update SE-0445 to add review link (and the title) (#2563) --- proposals/0445-string-index-printing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0445-string-index-printing.md b/proposals/0445-string-index-printing.md index cb39f791ea..20e15a709d 100644 --- a/proposals/0445-string-index-printing.md +++ b/proposals/0445-string-index-printing.md @@ -1,11 +1,11 @@ -# Feature name +# Improving `String.Index`'s printed descriptions * Proposal: [SE-0445](0445-string-index-printing.md) * Authors: [Karoy Lorentey](https://github.com/lorentey) * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Active review (September 16...30, 2024)** * Implementation: [apple/swift#75433](https://github.com/swiftlang/swift/pull/75433) -* Review: ([pitch](https://forums.swift.org/t/improving-string-index-s-printed-descriptions/57027)) +* Review: ([pitch](https://forums.swift.org/t/improving-string-index-s-printed-descriptions/57027)) ([review])(https://forums.swift.org/t/se-0445-improving-string-indexs-printed-descriptions/74643) ## Introduction From 6b44a3137647c1d77960da76f71547dea107d855 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Mon, 16 Sep 2024 18:35:00 -0400 Subject: [PATCH 3863/4563] Update SE-0445 to fix Markdown for link to review thread (#2564) --- proposals/0445-string-index-printing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0445-string-index-printing.md b/proposals/0445-string-index-printing.md index 20e15a709d..8c2b5b6557 100644 --- a/proposals/0445-string-index-printing.md +++ b/proposals/0445-string-index-printing.md @@ -5,7 +5,7 @@ * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Active review (September 16...30, 2024)** * Implementation: [apple/swift#75433](https://github.com/swiftlang/swift/pull/75433) -* Review: ([pitch](https://forums.swift.org/t/improving-string-index-s-printed-descriptions/57027)) ([review])(https://forums.swift.org/t/se-0445-improving-string-indexs-printed-descriptions/74643) +* Review: ([pitch](https://forums.swift.org/t/improving-string-index-s-printed-descriptions/57027)) ([review](https://forums.swift.org/t/se-0445-improving-string-indexs-printed-descriptions/74643)) ## Introduction From 77c2554d5b631d1e36d4259deff1f45ee14e0edb Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 17 Sep 2024 14:11:01 -0700 Subject: [PATCH 3864/4563] Start review for nonescaping types as SE-0446 --- .../{NNNN-non-escapable.md => 0446-non-escapable.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{NNNN-non-escapable.md => 0446-non-escapable.md} (98%) diff --git a/proposals/NNNN-non-escapable.md b/proposals/0446-non-escapable.md similarity index 98% rename from proposals/NNNN-non-escapable.md rename to proposals/0446-non-escapable.md index 1f461d20c9..c83492e177 100644 --- a/proposals/NNNN-non-escapable.md +++ b/proposals/0446-non-escapable.md @@ -1,11 +1,11 @@ # Nonescapable Types -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0446](0446-non-escapable.md) * Authors: [Andrew Trick](https://github.com/atrick), [Tim Kientzle](https://github.com/tbkka) -* Review Manager: TBD -* Status: **Awaiting implementation** +* Review Manager: [Joe Groff](https://github.com/jckarter) +* Status: **Active review (September 17...October 1, 2024)** * Roadmap: [BufferView Language Requirements](https://forums.swift.org/t/roadmap-language-support-for-bufferview) -* Implementation: **Pending** +* Implementation: **Implemented** in `main` branch * Upcoming Feature Flag: `NonescapableTypes` * Review: ([pitch](https://forums.swift.org/t/pitch-non-escapable-types-and-lifetime-dependency/69865)) From 141faf87567979cdb1821611a7e012c76225c888 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Tue, 17 Sep 2024 20:01:22 -0600 Subject: [PATCH 3865/4563] [Pitch] Regex Lookbehind Assertions (#2525) * Add regex reverse matching proposal * Resolve Regex builder section TODO (#5) * Resolve Regex builder section TODO * List APIs * Small spelling, documentation fixups * Lookbehind assertions only * Adjust proposal name * Update and rename nnnn-regex-lookbehind-assertions.md to 0448-regex-lookbehind-assertions.md Prepare 0448: regex lookbehind for review. * Update 0448-regex-lookbehind-assertions.md --------- Co-authored-by: Jacob Hearst <8368015+JacobHearst@users.noreply.github.com> Co-authored-by: Stephen Canon --- proposals/0448-regex-lookbehind-assertions.md | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 proposals/0448-regex-lookbehind-assertions.md diff --git a/proposals/0448-regex-lookbehind-assertions.md b/proposals/0448-regex-lookbehind-assertions.md new file mode 100644 index 0000000000..70cb0e6971 --- /dev/null +++ b/proposals/0448-regex-lookbehind-assertions.md @@ -0,0 +1,132 @@ +# Regex lookbehind assertions + +* Proposal: [SE-0448](0448-regex-lookbehind-assertions.md) +* Authors: [Jacob Hearst](https://github.com/JacobHearst) [Michael Ilseman](https://github.com/milseman) +* Review Manager: [Steve Canon](https://github.com/stephentyrone) +* Status: **Active review (September 17...October 1, 2024)** +* Implementation: https://github.com/swiftlang/swift-experimental-string-processing/pull/760 +* Review: ([pitch](https://github.com/swiftlang/swift-evolution/pull/2525))([review]) + + +## Introduction + +Regex supports lookahead assertions, but does not currently support lookbehind assertions. We propose adding these. + +## Motivation + +Modern regular expression engines support lookbehind assertions, whether fixed length (Perl, PCRE2, Python, Java) or arbitrary length (.NET, Javascript). + +## Proposed solution + +We propose supporting arbitrary-length lookbehind regexes which can be achieved by performing matching in reverse. + +Like lookahead assertions, lookbehind assertions are _zero-width_, meaning they do not affect the current match position. + +Examples: + + +```swift +"abc".firstMatch(of: /a(?<=a)bc/) // matches "abc" +"abc".firstMatch(of: /a(?<=b)c/) // no match +"abc".firstMatch(of: /a(?<=.)./) // matches "ab" +"abc".firstMatch(of: /ab(?<=a)c/) // no match +"abc".firstMatch(of: /ab(?<=.a)c/) // no match +"abc".firstMatch(of: /ab(?<=a.)c/) // matches "abc" +``` + +Lookbehind assertions run in reverse, i.e. right-to-left, meaning that right-most eager quantifications have the opportunity to consume more of the input than left-most. This does not affect whether an input matches, but could affect the value of captures inside of a lookbehind assertion: + +```swift +"abcdefg".wholeMatch(of: /(.+)(.+)/) +// Produces ("abcdefg", "abcdef", "g") + +"abcdefg".wholeMatch(of: /.*(?<=(.+)(.+)/)) +// Produces ("abcdefg", "a", "bcdefg") +``` + +## Detailed design + + +### Syntax + +Lookbehind assertion syntax is already supported in the existing [Regex syntax](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0355-regex-syntax-run-time-construction.md#lookahead-and-lookbehind). + +The engine is currently incapable of running them, so a compilation error is thrown: + +```swift +let regex = /(?<=a)b/ +// error: Cannot parse regular expression: lookbehind is not currently supported +``` + +With this proposal, this restriction is lifted and the following syntactic forms will be accepted: + +```swift +// Positive lookbehind +/a(?<=b)c/ +/a(*plb:b)c/ +/a(*positive_lookbehind:b)c/ + +// Negative lookbehind +/a(? Date: Tue, 17 Sep 2024 22:11:36 -0400 Subject: [PATCH 3866/4563] Update 0448-regex-lookbehind-assertions.md (#2566) --- proposals/0448-regex-lookbehind-assertions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0448-regex-lookbehind-assertions.md b/proposals/0448-regex-lookbehind-assertions.md index 70cb0e6971..eb80075b13 100644 --- a/proposals/0448-regex-lookbehind-assertions.md +++ b/proposals/0448-regex-lookbehind-assertions.md @@ -5,7 +5,7 @@ * Review Manager: [Steve Canon](https://github.com/stephentyrone) * Status: **Active review (September 17...October 1, 2024)** * Implementation: https://github.com/swiftlang/swift-experimental-string-processing/pull/760 -* Review: ([pitch](https://github.com/swiftlang/swift-evolution/pull/2525))([review]) +* Review: ([pitch](https://github.com/swiftlang/swift-evolution/pull/2525))([review](https://forums.swift.org/t/se-0448-regex-lookbehind-assertions/74672)) ## Introduction From be7789fea35b8466ccbf9eb16d8dd4681b117914 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 17 Sep 2024 21:27:12 -0700 Subject: [PATCH 3867/4563] Safe Access to Contiguous Storage (#2307) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [draft proposal] Safe Access to Contiguous Storage * edit placeholder proposal url * link to pitch thread * declare typealiases correctly * add “first” and “last” properties * fix inits from raw pointers * Update proposals/nnnn-safe-shared-contiguous-storage.md Co-authored-by: Alex Martini * add `view(as: T)` * incorporate feedback from pitch discussion * enclose index and iterator types in the main type * update protocol declaration * link to additional related pitches * fix a stored property type * rename type, adopt new syntax * various updates * add more RawSpan API, doc-comment fixes * Added more prose, added TODOs for further clarification * Update proposals/nnnn-safe-shared-contiguous-storage.md * remove some trailing whitespace from code blocks * Update * Update * lots of updates * Apply suggestions from code review Co-authored-by: Michael Ilseman * Move byte parsing helpers into a future direction * Fill out the index appendix * tweaks and corrections * add missing keywords * Apply editing suggestions from review Co-authored-by: Karoy Lorentey * annotation adjustments, various edits * some more edits * move `ContiguousStorage` to future directions The state of the compiler does not allow us to propose it at this time. * edits about unsafe initializer usage * remove “generally” from index-sharing note * improve index validation functions * omit some duplicated documentation * add html anchors to important sections * add link to second pitch thread - with whitespace tweaks * more cleanup surrounding `ContiguousStorage` * whitespace fixes * Change some uses of the word “view” to “span” instead * fix misspelling * add missing doc-comment paragraph * change `uncheckedBounds` to `unchecked` * fix doc-comments * rework `load` and company * add the `SurjectiveBitPattern` future direction * more about `SurjectiveBitPattern`, plus an alternative * move reference to SE-0256 to the ContiguousStorage item * reword coroutine accessors * remove undesirable annotations and default values * add containment utilities * Apply suggestions from code review Thanks to @benrimmington's eagle eyes Co-authored-by: Ben Rimmington * remove extension to `Character.UTF8View` - this is redundant, since it is the same as `String.UTF8View` * add closure-taking api, move initializers to future * shrink byte-parsing helpers future direction * formatting, text moved around * rename file to include the word “span” * improve title * add link to preview implementation * lots of changes * remove UBP.withUnsafeSpan and similar * remove another ~Escapable that cannot be promised * add a missing blurb * improve name of bounds-checking functions * addition about closure-based unsafe escape-hatch functions * remove boundsPrecondition, add boundsContain overload - add `boundsContain(_ bounds: ClosedRange)` * start pointer clarification * improve coroutine explanation * convert non-breaking spaces * fix extensions * [feedback] mention initializers earlier * rename span comparison functions * fix span comparison signatures and documentation --------- Co-authored-by: Guillaume Lessard Co-authored-by: Alex Martini Co-authored-by: Michael Ilseman Co-authored-by: Karoy Lorentey Co-authored-by: Ben Rimmington --- ...n-span-access-shared-contiguous-storage.md | 820 ++++++++++++++++++ 1 file changed, 820 insertions(+) create mode 100644 proposals/nnnn-span-access-shared-contiguous-storage.md diff --git a/proposals/nnnn-span-access-shared-contiguous-storage.md b/proposals/nnnn-span-access-shared-contiguous-storage.md new file mode 100644 index 0000000000..e547b07941 --- /dev/null +++ b/proposals/nnnn-span-access-shared-contiguous-storage.md @@ -0,0 +1,820 @@ +# Span: Safe Access to Contiguous Storage + +* Proposal: [SE-NNNN](nnnn-safe-shared-contiguous-storage.md) +* Authors: [Guillaume Lessard](https://github.com/glessard), [Michael Ilseman](https://github.com/milseman), [Andrew Trick](https://github.com/atrick) +* Review Manager: TBD +* Status: **Awaiting implementation**, previewed in a [branch](https://github.com/apple/swift-collections/tree/future) of [swift-collections](https://github.com/apple/swift-collections). +* Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) +* Bug: rdar://48132971, rdar://96837923 +* Implementation: Prototyped in https://github.com/apple/swift-collections (on branch "future") +* Review: ([pitch 1](https://forums.swift.org/t/69888))([pitch 2](https://forums.swift.org/t/72745)) + +## Introduction + +We introduce `Span`, an abstraction for container-agnostic access to contiguous memory. It will expand the expressivity of performant Swift code without compromising on the memory safety properties we rely on: temporal safety, spatial safety, definite initialization and type safety. + +In the C family of programming languages, memory can be shared with any function by using a pointer and (ideally) a length. This allows contiguous memory to be shared with a function that doesn't know the layout of a container being used by the caller. A heap-allocated array, contiguously-stored named fields or even a single stack-allocated instance can all be accessed through a C pointer. We aim to enable a similar idiom in Swift, without compromising Swift's memory safety. + +This proposal builds on [Nonescapable types][PR-2304] (`~Escapable`,) and is a precursor to [Compile-time Lifetime Dependency Annotations][PR-2305], which will be proposed in the following weeks. The [BufferView roadmap](https://forums.swift.org/t/66211) forum thread was an antecedent to this proposal. This proposal also depends on the following proposals: + +- [SE-0426] BitwiseCopyable +- [SE-0427] Noncopyable generics +- [SE-0437] Non-copyable Standard Library Primitives +- [SE-0377] `borrowing` and `consuming` parameter ownership modifiers + +[SE-0426]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0426-bitwise-copyable.md +[SE-0427]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0427-noncopyable-generics.md +[SE-0437]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0437-noncopyable-stdlib-primitives.md +[SE-0377]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0377-parameter-ownership-modifiers.md +[PR-2304]: https://github.com/swiftlang/swift-evolution/pull/2304 +[PR-2305]: https://github.com/swiftlang/swift-evolution/pull/2305 +[PR-2305-pitch]: https://forums.swift.org/t/69865 + +## Motivation + +Swift needs safe and performant types for local processing over values in contiguous memory. Consider for example a program using multiple libraries, including one for [base64](https://datatracker.ietf.org/doc/html/rfc4648) decoding. The program would obtain encoded data from one or more of its dependencies, which could supply the data in the form of `[UInt8]`, `Foundation.Data` or even `String`, among others. None of these types is necessarily more correct than another, but the base64 decoding library must pick an input format. It could declare its input parameter type to be `some Sequence`, but such a generic function can significantly limit performance. This may force the library author to either declare its entry point as inlinable, or to implement an internal fast path using `withContiguousStorageIfAvailable()`, forcing them to use an unsafe type. The ideal interface would have a combination of the properties of both `some Sequence` and `UnsafeBufferPointer`. + +The `UnsafeBufferPointer` passed to a `withUnsafeXXX` closure-style API, while performant, is unsafe in multiple ways: + +1. The pointer itself is unsafe and unmanaged +2. `subscript` is only bounds-checked in debug builds of client code +3. It might escape the duration of the closure + +Even if the body of the `withUnsafeXXX` call does not escape the pointer, other functions called within the closure have to be written in terms of unsafe pointers. This requires programmer vigilance across a project and potentially spreads the use of unsafe types, even if the helper functions could have been written in terms of safe constructs. + +## Proposed solution + +#### `Span` + +`Span` will allow sharing the contiguous internal representation of a type, by providing access to a borrowed view of an interval of contiguous memory. `Span` does not copy the underlying data: it instead relies on a guarantee that the original container cannot be modified or destroyed while the `Span` exists. In the prototype that accompanies this first proposal, `Span`s will be constrained to closures from which they structurally cannot escape. Later, we will introduce a lifetime dependency between a `Span` and the binding of the type vending it, preventing its escape from the scope where it is valid for use. Both of these approaches guarantee temporal safety. `Span` also performs bounds-checking on every access to preserve spatial safety. Additionally `Span` always represents initialized memory, preserving the definite initialization guarantee. + +`Span` is intended as the currency type for local processing over values in contiguous memory. It is a replacement for many API currently using `Array`, `UnsafeBufferPointer`, `Foundation.Data`, etc., that do not need to escape the owning container. + +A `Span` provided by a container represents a borrow of that container. `Span` can therefore provide simultaneous access to a non-copyable container. It can also help avoid unwanted copies of copyable containers. Note that `Span` is not a replacement for a copyable container with owned storage; see [future directions](#Directions) for more details ([Resizable, contiguously-stored, untyped collection in the standard library](#Bytes).) + +In this initial proposal, no initializers are proposed for `Span`. Initializers for non-escapable types such as `Span` require a concept of lifetime dependency, which does not exist at this time. The lifetime dependency annotation will indicate to the compiler how a newly-created `Span` can be used safely. See also ["Initializers"](#Initializers) in [future directions](#Directions). + +#### `RawSpan` + +`RawSpan` allows sharing contiguous memory representing values which may be heterogeneously-typed, such as memory intended for parsing. It makes the same safety guarantees as `Span`. Since it is a fully concrete type, it can achieve great performance in debug builds of client code as well as straightforward performance in library code. + +A `RawSpan` can be obtained from containers of `BitwiseCopyable` elements, as well as be initialized directly from an instance of `Span`. + +## Detailed design + +`Span` is a simple representation of a region of initialized memory. + +```swift +@frozen +public struct Span: Copyable, ~Escapable { + internal var _start: UnsafeRawPointer? + internal var _count: Int +} +``` + +We store a `UnsafeRawPointer` value internally in order to explicitly support reinterpreted views of memory as containing different types of `BitwiseCopyable` elements. Note that the the optionality of the pointer does not affect usage of `Span`, since accesses are bounds-checked and the pointer is only dereferenced when the `Span` isn't empty, and the pointer cannot be `nil`. + +It provides a buffer-like interface to the elements stored in that span of memory: + +```swift +extension Span where Element: ~Copyable { + public var count: Int { get } + public var isEmpty: Bool { get } + + public subscript(_ position: Int) -> Element { _read } +} +``` + +Note that `Span` does _not_ conform to `Collection`. This is because `Collection`, as originally conceived and enshrined in existing source code, assumes pervasive copyability and escapability of the `Collection` itself as well as of element type. In particular a subsequence of a `Collection` is semantically a separate value from the instance it was derived from. In the case of `Span`, a sub-span representing a subrange of its elements _must_ have the same lifetime as the `Span` from which it originates. Another proposal will consider collection-like protocols to accommodate different combinations of `~Copyable` and `~Escapable` for the collection and its elements. + +Like `UnsafeBufferPointer`, `Span` uses a simple offset-based indexing. The first element of a given span is always at position zero, and its last element is always at position `count-1`. + +As a side-effect of not conforming to `Collection` or `Sequence`, `Span` is not directly supported by `for` loops at this time. It is, however, easy to use in a `for` loop via indexing: + +```swift +for i in 0.. Element { _read } +} +``` + +Note that we use a `_read` accessor for the subscript, a requirement in order to `yield` a borrowed non-copyable `Element` (see ["Coroutines"](#Coroutines).) This will be updated to a final syntax at a later time, understanding that we intend the replacement to be source-compatible. + +##### Unchecked access to elements: + +The `subscript` mentioned above has always-on bounds checking of its parameter, in order to prevent out-of-bounds accesses. We also want to provide unchecked variants as an alternative for cases where bounds-checking is proving costly, such as in tight loops: + +```swift +extension Span where Element: ~Copyable { + // Unchecked subscripting and extraction + + /// Accesses the element at the specified `position`. + /// + /// This subscript does not validate `position`; this is an unsafe operation. + /// + /// - Parameter position: The offset of the element to access. `position` + /// must be greater or equal to zero, and less than `count`. + public subscript(unchecked position: Int) -> Element { _read } +} +``` + +##### Index validation utilities: + +Every time `Span` uses a position parameter, it checks for its validity, unless the parameter is marked with the word "unchecked". The validation is performed with these functions: + +```swift +extension Span where Element: ~Copyable { + /// Return true if `index` is a valid offset into this `Span` + /// + /// - Parameters: + /// - index: an index to validate + /// - Returns: true if `index` is a valid index + public func boundsContain(_ index: Int) -> Bool + + /// Return true if `indices` is a valid range of offsets into this `Span` + /// + /// - Parameters: + /// - indices: a range of indices to validate + /// - Returns: true if `indices` is a valid range of indices + public func boundsContain(_ indices: Range) -> Bool + + /// Return true if `indices` is a valid range of offsets into this `Span` + /// + /// - Parameters: + /// - indices: a range of indices to validate + /// - Returns: true if `indices` is a valid range of indices + public func boundsContain(_ indices: ClosedRange) -> Bool +} +``` + +Note: these function names are not ideal. + +##### Identifying whether a `Span` is a subrange of another: + +When working with multiple `Span` instances, it is often desirable to know whether one is identical to or a subrange of another. We include functions to determine whether this is the case, as well as a function to obtain the valid offsets of the subrange within the larger span: + +```swift +extension Span where Element: ~Copyable { + /// Returns true if the other span represents exactly the same memory + public func isIdentical(to span: borrowing Self) -> Bool + + /// Returns true if the memory represented by `self` is a subrange of + /// the memory represented by `span` + /// + /// Parameters: + /// - span: a span of the same type as `self` + /// Returns: whether `self` is a subrange of `span` + public func isWithin(_ span: borrowing Self) -> Bool + + /// Returns the offsets where the memory of `self` is located within + /// the memory represented by `span`, or `nil` + /// + /// Parameters: + /// - span: a subrange of `self` + /// Returns: A range of offsets within `self`, or `nil` + public func indicesWithin(_ span: borrowing Self) -> Range? +} +``` + +##### Interoperability with unsafe code: + +We provide two functions for interoperability with C or other legacy pointer-taking functions. + +```swift +extension Span where Element: ~Copyable { + /// Calls a closure with a pointer to the viewed contiguous storage. + /// + /// The buffer pointer passed as an argument to `body` is valid only + /// during the execution of `withUnsafeBufferPointer(_:)`. + /// Do not store or return the pointer for later use. + /// + /// - Parameter body: A closure with an `UnsafeBufferPointer` parameter + /// that points to the viewed contiguous storage. If `body` has + /// a return value, that value is also used as the return value + /// for the `withUnsafeBufferPointer(_:)` method. The closure's + /// parameter is valid only for the duration of its execution. + /// - Returns: The return value of the `body` closure parameter. + func withUnsafeBufferPointer( + _ body: (_ buffer: UnsafeBufferPointer) throws(E) -> Result + ) throws(E) -> Result +} + +extension Span where Element: BitwiseCopyable { + /// Calls the given closure with a pointer to the underlying bytes of + /// the viewed contiguous storage. + /// + /// The buffer pointer passed as an argument to `body` is valid only + /// during the execution of `withUnsafeBytes(_:)`. + /// Do not store or return the pointer for later use. + /// + /// - Parameter body: A closure with an `UnsafeRawBufferPointer` + /// parameter that points to the viewed contiguous storage. + /// If `body` has a return value, that value is also + /// used as the return value for the `withUnsafeBytes(_:)` method. + /// The closure's parameter is valid only for the duration of + /// its execution. + /// - Returns: The return value of the `body` closure parameter. + func withUnsafeBytes( + _ body: (_ buffer: UnsafeRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result +} +``` + +These functions use a closure to define the scope of validity of `buffer`, ensuring that the underlying `Span` and the binding it depends on both remain valid through the end of the closure. They have the same shape as the equivalents on `Array` because they fulfill the same function, namely to keep the underlying binding alive. + +### RawSpan + +In addition to `Span`, we propose the addition of `RawSpan`, to represent heterogeneously-typed values in contiguous memory. `RawSpan` is similar to `Span`, but represents _untyped_ initialized bytes. `RawSpan` is a specialized type that is intended to support parsing and decoding applications, as well as applications where heavily-used code paths require concrete types as much as possible. Its API supports the data loading operations `unsafeLoad(as:)` and `unsafeLoadUnaligned(as:)`. + +#### `RawSpan` API: + +```swift +@frozen +public struct RawSpan: Copyable, ~Escapable { + internal var _start: UnsafeRawPointer + internal var _count: Int +} +``` + +Initializers, required for library adoption, will be proposed alongside [lifetime annotations][PR-2305]; for details, see "[Initializers](#Initializers)" in the [future directions](#Directions) section. + +##### Accessing the memory of a `RawSpan`: + +`RawSpan` has basic operations to access the contents of its memory: `unsafeLoad(as:)` and `unsafeLoadUnaligned(as:)`. These operations are not type-safe, in that the loaded value returned by the operation can be invalid. Some types have a property that makes this operation safe, but there we don't have a way to [formally identify](#SurjectiveBitPattern) such types at this time. + +```swift +extension RawSpan { + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// The memory at this pointer plus `offset` must be properly aligned for + /// accessing `T` and initialized to `T` or another type that is layout + /// compatible with `T`. + /// + /// This is an unsafe operation. Failure to meet the preconditions + /// above may produce an invalid value of `T`. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance is memory-managed and unassociated + /// with the value in the memory referenced by this pointer. + public func unsafeLoad( + fromByteOffset offset: Int = 0, as: T.Type + ) -> T + + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// The memory at this pointer plus `offset` must be initialized to `T` + /// or another type that is layout compatible with `T`. + /// + /// This is an unsafe operation. Failure to meet the preconditions + /// above may produce an invalid value of `T`. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance isn't associated + /// with the value in the range of memory referenced by this pointer. + public func unsafeLoadUnaligned( + fromByteOffset offset: Int = 0, as: T.Type + ) -> T +``` + +These functions have counterparts which omit bounds-checking for cases where redundant checks affect performance: +```swift + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// The memory at this pointer plus `offset` must be properly aligned for + /// accessing `T` and initialized to `T` or another type that is layout + /// compatible with `T`. + /// + /// This is an unsafe operation. This function does not validate the bounds + /// of the memory access, and failure to meet the preconditions + /// above may produce an invalid value of `T`. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance is memory-managed and unassociated + /// with the value in the memory referenced by this pointer. + public func unsafeLoad( + fromUncheckedByteOffset offset: Int, as: T.Type + ) -> T + + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. + /// + /// The memory at this pointer plus `offset` must be initialized to `T` + /// or another type that is layout compatible with `T`. + /// + /// This is an unsafe operation. This function does not validate the bounds + /// of the memory access, and failure to meet the preconditions + /// above may produce an invalid value of `T`. + /// + /// - Parameters: + /// - offset: The offset from this pointer, in bytes. `offset` must be + /// nonnegative. The default is zero. + /// - type: The type of the instance to create. + /// - Returns: A new instance of type `T`, read from the raw bytes at + /// `offset`. The returned instance isn't associated + /// with the value in the range of memory referenced by this pointer. + public func unsafeLoadUnaligned( + fromUncheckedByteOffset offset: Int, as: T.Type + ) -> T +} +``` + +`RawSpan` provides `withUnsafeBytes` for interoperability with C or other legacy pointer-taking functions: + +```swift +extension RawSpan { + /// Calls the given closure with a pointer to the underlying bytes of + /// the viewed contiguous storage. + /// + /// The buffer pointer passed as an argument to `body` is valid only + /// during the execution of `withUnsafeBytes(_:)`. + /// Do not store or return the pointer for later use. + /// + /// - Parameter body: A closure with an `UnsafeRawBufferPointer` + /// parameter that points to the viewed contiguous storage. + /// If `body` has a return value, that value is also + /// used as the return value for the `withUnsafeBytes(_:)` method. + /// The closure's parameter is valid only for the duration of + /// its execution. + /// - Returns: The return value of the `body` closure parameter. + func withUnsafeBytes( + _ body: (_ buffer: UnsafeRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result +} +``` + +##### Examining `RawSpan` bounds: + +```swift +extension RawSpan { + /// The number of bytes in the span. + public var byteCount: Int { get } + + /// A Boolean value indicating whether the span is empty. + public var isEmpty: Bool { get } +} +``` + +##### `RawSpan` bounds checking: +```swift +extension RawSpan { + /// Return true if `offset` is a valid byte offset into this `RawSpan` + public func boundsContain(_ offset: Int) -> Bool + + /// Return true if `offsets` is a valid range of offsets into this `RawSpan` + public func boundsContain(_ offsets: Range) -> Bool + + /// Return true if `offsets` is a valid range of offsets into this `RawSpan` + public func boundsContain(_ offsets: ClosedRange) -> Bool +} +``` + +##### Identifying whether a `RawSpan` is a subrange of another: + +When working with multiple `RawSpan` instances, it is often desirable to know whether one is identical to or a subrange of another. We include a function to determine whether this is the case, as well as a function to obtain the valid offsets of the subrange within the larger span. The documentation is omitted here, as it is substantially the same as for the equivalent functions on `Span`: + +```swift +extension RawSpan { + public func isIdentical(to span: borrowing Self) -> Bool + + public func isWithin(_ span: borrowing Self) -> Bool + + public func byteOffsetsWithin(_ span: borrowing Self) -> Range? +} +``` + +## Source compatibility + +This proposal is additive and source-compatible with existing code. + +## ABI compatibility + +This proposal is additive and ABI-compatible with existing code. + +## Implications on adoption + +The additions described in this proposal require a new version of the standard library and runtime. + +## Alternatives considered + +##### Make `Span` a noncopyable type +Making `Span` non-copyable was in the early vision of this type. However, we found that would make `Span` a poor match to model borrowing semantics. This realization led to the initial design for non-escapable declarations. + +##### Use a non-escapable index type +A non-escapable index type implies that any indexing operation would borrow its `Span`. This would prevent using such an index for a mutation, since a mutation requires an _exclusive_ borrow. Noting that the usage pattern we desire for `Span` must also apply to `MutableSpan`(described [below](#MutableSpan),) a non-escapable index would make it impossible to also implement a mutating subscript, unless any mutating operation consumes the index. This seems untenable. + +##### Naming + +The ideas in this proposal previously used the name `BufferView`. While the use of the word "buffer" would be consistent with the `UnsafeBufferPointer` type, it is nevertheless not a great name, since "buffer" is commonly used in reference to transient storage. Another previous pitch used the term `StorageView` in reference to the `withContiguousStorageIfAvailable()` standard library function. We also considered the name `StorageSpan`, but that did not add much beyond the shorter name `Span`. `Span` clearly identifies itself as a relative of C++'s `std::span`. + +##### A more sophisticated approach to indexing + +This is discussed more fully in the [indexing appendix](#Indexing) below. + +## Future directions + +#### Initializing and returning `Span` instances + +A `Span` represents a region of memory and, as such, must be initialized using an unsafe pointer. This is an unsafe operation which will typically be performed internally to a container's implementation. In order to bridge to safe code, these initializers require new annotations that indicate to the compiler how the newly-created `Span` can be used safely. + +These annotations have been [pitched][PR-2305-pitch] and are expected to be formally [proposed][PR-2305] soon. `Span` initializers using lifetime annotations will be proposed alongside the annotations themselves. + +#### Obtaining variant `Span`s and `RawSpan`s from `Span` and `RawSpan` + +`Span`s representing subsets of consecutive elements could be extracted out of a larger `Span` with an API similar to the `extracting()` functions recently added to `UnsafeBufferPointer` in support of non-copyable elements: + +```swift +extension Span where Element: ~Copyable { + public func extracting(_ bounds: Range) -> Self +} +``` + +Each variant of such a function needs to return a `Span`, which requires a lifetime dependency. + +Similarly, a `RawSpan` should be initializable from a `Span`, and `RawSpan` should provide a function to unsafely view its content as a typed `Span`: + +```swift +extension RawSpan { + public init(_ span: Span) + + public func unsafeView(as type: T.Type) -> Span +} +``` + +We are subsetting these functions of `Span` and `RawSpan` until the lifetime annotations are proposed. + +#### Coroutine or Projection Accessors + +This proposal includes some `_read` accessors, the coroutine version of the `get` accessor. `_read` accessors are not an official part of the Swift language, but are necessary for some types to be able to provide borrowing access to their internal storage, in particular storage containing non-copyable elements. The correct solution may involve a projection of a different type than is provided by a coroutine. When correct, stable replacement for `_read` accessors is proposed and accepted, the implementation of `Span` and `RawSpan` will be adapted to the new syntax. + +#### Extensions to Standard Library and Foundation types + +The standard library and Foundation has a number of types that can in principle provide access to their internal storage as a `Span`. We could provide `withSpan()` and `withBytes()` closure-taking functions as safe replacements for the existing `withUnsafeBufferPointer()` and `withUnsafeBytes()` functions. We could also also provide lifetime-dependent `span` or `bytes` properties. For example, `Array` could be extended as follows: + +```swift +extension Array { + public func withSpan( + _ body: (_ elements: Span) throws(E) -> Result + ) throws(E) -> Result + + public var span: Span { borrowing get } +} + +extension Array where Element: BitwiseCopyable { + public func withBytes( + _ body: (_ bytes: RawSpan) throws(E) -> Result + ) throws(E) -> Result where Element: BitwiseCopyable + + public var bytes: RawSpan { borrowing get } +} +``` + +Of these, the closure-taking functions can be implemented now, but it is unclear whether they are desirable. The lifetime-dependent computed properties require lifetime annotations, as initializers do. We are deferring proposing these extensions until the lifetime annotations are proposed. + +#### A `ContiguousStorage` protocol + +An earlier version of this proposal proposed a `ContiguousStorage` protocol by which a type could indicate that it can provide a `Span`. `ContiguousStorage` would form a bridge between generically-typed interfaces and a performant concrete implementation. It would supersede the rejected [SE-0256](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0256-contiguous-collection.md). + +For example, for the hypothetical base64 decoding library mentioned in the [motivation](#Motivation) section, a possible API could be: + +```swift +extension HypotheticalBase64Decoder { + public func decode(bytes: some ContiguousStorage) -> [UInt8] +} +``` + +`ContiguousStorage` would have the following definition: + +```swift +public protocol ContiguousStorage: ~Copyable, ~Escapable { + associatedtype Element: ~Copyable & ~Escapable + var storage: Span { _read } +} +``` + +Two issues prevent us from proposing it at this time: (a) the ability to suppress requirements on `associatedtype` declarations was deferred during the review of [SE-0427], and (b) we cannot declare a `_read` accessor as a protocol requirement. + +Many of the standard library collections could conform to `ContiguousStorage`. + +#### Support for `Span` in `for` loops + +This proposal does not define an `IteratorProtocol` conformance, since an iterator for `Span` would need to be non-escapable. This is not compatible with `IteratorProtocol`. As such, `Span` is not directly usable in `for` loops as currently defined. A `BorrowingIterator` protocol for non-escapable and non-copyable containers must be defined, providing a `for` loop syntax where the element is borrowed through each iteration. Ultimately we should arrive at a way to iterate through borrowed elements from a borrowed view: + +```swift +func doSomething(_ e: borrowing Element) { ... } +let span: Span = ... +for borrowing element in span { + doSomething(element) +} +``` + +In the meantime, it is possible to loop through a `Span`'s elements by direct indexing: + +```swift +let span: Span = ... +// either: +var i = 0 +while i < span.count { + doSomething(span[i]) + i += 1 +} + +// ...or: +for i in 0..Layout constraint for safe loading of bit patterns + +`RawSpan` has unsafe functions that interpret the raw bit patterns it contains as values of arbitrary `BitwiseCopyable` types. In order to have safe alternatives to these, we could add a layout constraint refining `BitwiseCopyable`, specifically for types whose mapping from bit pattern to values is a [surjective function](https://en.wikipedia.org/wiki/Surjective_function) (e.g. `SurjectiveBitPattern`). Such types would be safe to [load](#Load) from `RawSpan` instances. 1-byte examples are `Int8` (any of 256 values are valid) and `Bool` (256 bit patterns map to `true` or `false` because only one bit is considered.) + +An alternative to a layout constraint is to add a type validation step to ensure that if a given bit pattern were to be interpreted as a value of type `T`, then all the invariants of type `T` would be respected. This alternative would be more flexible, but may have a higher runtime cost. + +#### Byte parsing helpers + +We could add some API to `RawSpan` to make it better suited for binary parsers and decoders. + +```swift +extension RawSpan { + public struct Cursor: Copyable, ~Escapable { + public let base: RawSpan + + /// The current parsing position + public var position: Int + + /// Parse an instance of `T` and advance. + /// Returns `nil` if there are not enough bytes remaining for an instance of `T`. + public mutating func parse( + _ t: T.Type = T.self + ) -> T? + + /// Parse `numBytes`and advance. + /// Returns `nil` if there are fewer than `numBytes` remaining. + public mutating func parse( + numBytes: some FixedWidthInteger + ) -> RawSpan? + + /// The bytes that we've parsed so far + public var parsedBytes: RawSpan { get } + } +} +``` + +`Cursor` stores and manages a parsing subrange, which alleviates the developer from managing one layer of slicing. + +Alternatively, if some future `RawSpan.Iterator` were 3 words in size (start, current position, and end) instead of 2 (current pointer and end), making it a "resettable", it could host this API instead of introducing a new `Cursor` type or concept. + +##### Example: Parsing PNG + +The code snippet below parses a [PNG Chunk](https://www.w3.org/TR/png-3/#4Concepts.FormatChunks), using the byte parsing helpers defined above: + +```swift +// Parse a PNG chunk +let length = try cursor.parse(UInt32.self).bigEndian +let type = try cursor.parse(UInt32.self).bigEndian +let data = try cursor.parse(numBytes: length) +let crc = try cursor.parse(UInt32.self).bigEndian +``` + +#### Safe mutations of memory with `MutableSpan` + +Some data structures can delegate mutations of their owned memory. In the standard library the function `withMutableBufferPointer()` provides this functionality in an unsafe manner. + +The `UnsafeMutableBufferPointer` passed to a `withUnsafeMutableXXX` closure-style API is unsafe in multiple ways: + +1. The pointer itself is unsafe and unmanaged +2. `subscript` is only bounds-checked in debug builds of client code +3. It might escape the duration of the closure +4. Exclusivity of writes is not enforced +5. Initialization of any particular memory address is not ensured + +in other words, it is unsafe in all the same ways as `UnsafeBufferPointer`-passing closure APIs, in addition to enforcing neither exclusivity nor initialization state. + +Loading an uninitialized non-`BitwiseCopyable` value leads to undefined behavior. Loading an uninitialized `BitwiseCopyable` value does not immediately lead to undefined behavior, but it produces a garbage value which may lead to misbehavior of the program. + +A `MutableSpan` should provide a better, safer alternative to mutable memory in the same way that `Span` provides a better, safer read-only type. `MutableSpan` would apply to initialized memory and would enforce exclusivity of writes, thereby preserving the initialization state of its memory between mutations. + +#### Delegating initialization of memory with `OutputSpan` + +Some data structures can delegate initialization of their initial memory representation, and in some cases the initialization of additional memory. For example, the standard library features the initializer`Array.init(unsafeUninitializedCapacity:initializingWith:)`, which depends on `UnsafeMutableBufferPointer` and is known to be error-prone. A safer abstraction for initialization would make such initializers less dangerous, and would allow for a greater variety of them. + +We can define an `OutputSpan` type, which could support appending to the initialized portion of a data structure's underlying storage. `OutputSpan` allows for uninitialized memory beyond the last position appended. Such an `OutputSpan` would also be a useful abstraction to pass user-allocated storage to low-level API such as networking calls or file I/O. + +#### Resizable, contiguously-stored, untyped collection in the standard library + +The example in the [motivation](#Motivation) section mentions the `Foundation.Data` type. There has been some discussion of either replacing `Data` or moving it to the standard library. This document proposes neither of those. A major issue is that in the "traditional" form of `Foundation.Data`, namely `NSData` from Objective-C, it was easier to control accidental copies because the semantics of the language did not lead to implicit copying. + +Even if `Span` were to replace all uses of a constant `Data` in API, something like `Data` would still be needed, for the same reason as `Array` is needed: such a type allows for resizing mutations (e.g. `RangeReplaceableCollection` conformance.) We may want to add an untyped-element equivalent of `Array` to the standard library at a later time. + +#### Syntactic Sugar for Automatic Conversions + +Even with a `ContiguousStorage` protocol, a generic entry point in terms of `some ContiguousStorage` may add unwanted overhead to resilient libraries. As detailed above, an entry point in an evolution-enabled library requires an inlinable generic public entry point which forwards to a publicly-accessible function defined in terms of `Span`. If `Span` does become a widely-used type to interface between libraries, we could simplify these conversions with a bit of compiler help. + +We could provide an automatic way to use a `ContiguousStorage`-conforming type with a function that takes a `Span` of the appropriate element type: + +```swift +func myStrnlen(_ b: Span) -> Int { + guard let i = b.firstIndex(of: 0) else { return b.count } + return b.distance(from: b.startIndex, to: e) +} +let data = Data((0..<9).reversed()) // Data conforms to ContiguousStorage +let array = Array(data) // Array also conforms to ContiguousStorage +myStrnlen(data) // 8 +myStrnlen(array) // 8 +``` + +This would probably consist of a new type of custom conversion in the language. A type author would provide a way to convert from their type to an owned `Span`, and the compiler would insert that conversion where needed. This would enhance readability and reduce boilerplate. + +#### Interoperability with C++'s `std::span` and with llvm's `-fbounds-safety` + +The [`std::span`](https://en.cppreference.com/w/cpp/container/span) class template from the C++ standard library is a similar representation of a contiguous range of memory. LLVM may soon have a [bounds-checking mode](https://discourse.llvm.org/t/70854) for C. These are opportunities for better, safer interoperation with Swift, via a type such as `Span`. + +## Acknowledgments + +Joe Groff, John McCall, Tim Kientzle, Steve Canon and Karoy Lorentey contributed to this proposal with their clarifying questions and discussions. + +### Appendix: Index and slicing design considerations + +Early prototypes of this proposal defined an `Index` type, `Iterator` types, etc. We are proposing `Int`-based API and are deferring defining `Index` and `Iterator` until more of the non-escapable collection story is sorted out. The below is some of our research into different potential designs of an `Index` type. + +There are 3 potentially-desirable features of `Span`'s `Index` design: + +1. `Span` is its own slice type +2. Indices from a slice can be used on the base collection +3. Additional reuse-after-free checking + +Each of these introduces practical tradeoffs in the design. + +#### `Span` is its own slice type + +Collections which own their storage have the convention of separate slice types, such as `Array` and `String`. This has the advantage of clearly delineating storage ownership in the programming model and the disadvantage of introducing a second type through which to interact. + +When types do not own their storage, separate slice types can be [cumbersome](https://github.com/swiftlang/swift/blob/swift-5.10.1-RELEASE/stdlib/public/core/StringComparison.swift#L175). The reason `UnsafeBufferPointer` has a separate slice type is because it wants to allow indices to be reused across slices and its `Index` is a relative offset from the start (`Int`) rather than an absolute position (such as a pointer). + +`Span` does not own its storage and there is no concern about leaking larger allocations. It would benefit from being its own slice type. + +#### Indices from a slice can be used on the base collection + +There is very strong stdlib precedent that indices from the base collection can be used in a slice and vice-versa. + +```swift +let myCollection = [0,1,2,3,4,5,6] +let idx = myCollection.index(myCollection.startIndex, offsetBy: 4) +myCollection[idx] // 4 +let slice = myCollection[idx...] // [4, 5, 6] +slice[idx] // 4 +myCollection[slice.indices] // [4, 5, 6] +``` + +Code can be written to take advantage of this fact. For example, a simplistic parser can be written as mutating methods on a slice. The slice's indices can be saved for reference into the original collection or another slice. + +```swift +extension Slice where Base == UnsafeRawBufferPointer { + mutating func parse(numBytes: Int) -> Self { + let end = index(startIndex, offsetBy: numBytes) + defer { self = self[end...] } + return self[.. Int { + parse(numBytes: MemoryLayout.stride).loadUnaligned(as: Int.self) + } + + mutating func parseHeader() -> Self { + // Comments show what happens when ran with `myCollection` + + let copy = self + parseInt() // 0 + parseInt() // 1 + parse(numBytes: 8) // [2, 0, 0, 0, 0, 0, 0, 0] + parseInt() // 3 + parse(numBytes: 7) // [4, 0, 0, 0, 0, 0, 0] + + // self: [0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0] + parseInt() // 1280 (0x00_00_05_00 little endian) + // self: [0, 6, 0, 0, 0, 0, 0, 0, 0] + + return copy[..( + _ c: C +) -> C.Element where C.Index == Int { + c[0] +} + +getFirst(myCollection) // 0 +getFirst(slice) // Fatal error: Index out of bounds +``` + +#### Additional reuse-after-free checking + +`Span` bounds-checks its indices, which is important for safety. If the index is based around a pointer (instead of an offset), then bounds checks will also ensure that indices are not used with the wrong span in most situations. However, it is possible for a memory address to be reused after being freed and using a stale index into this reused memory may introduce safety problems. + +```swift +var idx: Span.Index + +let array1: Array = ... +let span1 = array1.span +idx = span1.startIndex.advanced(by: ...) +... +// array1 is freed + +let array2: Array = ... +let span2 = array2.span +// array2 happens to be allocated within the same memory of array1 +// but with a different base address whose offset is not an even +// multiple of `MemoryLayout.stride`. + +span2[idx] // misaligned load, what happens? +``` + +If `T` is `BitwiseCopyable`, then the misaligned load is not undefined behavior, but the value that is loaded is garbage. Whether the program is well-behaved going forwards depends on whether it is resilient to getting garbage values. + +If `T` is not `BitwiseCopyable`, then the misaligned load may introduce undefined behavior. No matter how well-written the rest of the program is, it has a critical safety and security flaw. + +When the reused allocation happens to be stride-aligned, there is no undefined behavior from undefined loads, nor are there "garbage" values in the strictest sense, but it is still reflective of a programming bug. The program may be interacting with an unexpected value. + +Bounds checks protect against critical programmer errors. It would be nice, pending engineering tradeoffs, to also protect against some reuse after free errors and invalid index reuse, especially those that may lead to undefined behavior. + +Future improvements to microarchitecture may make reuse after free checks cheaper, however we need something for the foreseeable future. Any validation we can do reduces the need to switch to other mitigation strategies or make other tradeoffs. + +#### Design approaches for indices + +##### Index is an offset (`Int` or a wrapper around `Int`) + +When `Index` is an offset, there is no undefined behavior from misaligned loads because the `Span`'s base address is advanced by `MemoryLayout.stride * offset`. + +However, there is no protection against invalidly using an index derived from a different span, provided the offset is in-bounds. + +Since `Span` is 2 words (base address and count), indices cannot be interchanged between slices and the base span. In order to do so, `Span` would need to additionally store a base offset, bringing it up to 3 words in size. + +##### Index is a pointer (wrapper around `UnsafeRawPointer`) + +When Index holds a pointer, `Span` only needs to be 2 words in size, as valid index interchange across slices falls out naturally. Additionally, invalid reuse of an index across spans will typically be caught during bounds checking. + +However, in a reuse-after-free situation, misaligned loads (i.e. undefined behavior) are possible. If stride is not a multiple of 2, then alignment checking can be expensive. Alternatively, we could choose not to detect these bugs. + +##### Index is a fat pointer (pointer and allocation ID) + +We can create a per-allocation ID (e.g. a cryptographic `UInt64`) for both `Span` and `Span.Index` to store. This would make `Span` 3 words in size and `Span.Index` 2 words in size. This provides the most protection possible against all forms of invalid index use, including reuse-after-free. However, making `Span` be 3 words and `Span.Index` 2 words for this feature is unfortunate. + +We could instead go with 2 word `Span` and 2 word `Span.Index` by storing the span's `baseAddress` in the `Index`'s second word. This will detect invalid reuse of indices across spans in addition to misaligned reuse-after-free errors. However, indices could not be interchanged without a way for the slice type to know the original span's base address (e.g. through a separate slice type or making `Span` 3 words in size). + +In either approach, making `Span.Index` be 2 words in size is unfortunate. `Range` is now 4 words in size, storing the allocation ID twice. Anything built on top of `Span` that wishes to store multiple indices is either bloated or must hand-extract the pointers and hand-manage the allocation ID. From 0da9c4f5d7cedd3feea879fc19abfe2f3e293f5b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 17 Sep 2024 21:34:03 -0700 Subject: [PATCH 3868/4563] Initiate review of SE-0447 "Span: Safe Access to Contiguous Storage" (#2567) --- ...e.md => 0447-span-access-shared-contiguous-storage.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{nnnn-span-access-shared-contiguous-storage.md => 0447-span-access-shared-contiguous-storage.md} (99%) diff --git a/proposals/nnnn-span-access-shared-contiguous-storage.md b/proposals/0447-span-access-shared-contiguous-storage.md similarity index 99% rename from proposals/nnnn-span-access-shared-contiguous-storage.md rename to proposals/0447-span-access-shared-contiguous-storage.md index e547b07941..61db511040 100644 --- a/proposals/nnnn-span-access-shared-contiguous-storage.md +++ b/proposals/0447-span-access-shared-contiguous-storage.md @@ -1,12 +1,12 @@ # Span: Safe Access to Contiguous Storage -* Proposal: [SE-NNNN](nnnn-safe-shared-contiguous-storage.md) +* Proposal: [SE-0447](0447-safe-shared-contiguous-storage.md) * Authors: [Guillaume Lessard](https://github.com/glessard), [Michael Ilseman](https://github.com/milseman), [Andrew Trick](https://github.com/atrick) -* Review Manager: TBD -* Status: **Awaiting implementation**, previewed in a [branch](https://github.com/apple/swift-collections/tree/future) of [swift-collections](https://github.com/apple/swift-collections). +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active review (September 17...October 1, 2024)** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Bug: rdar://48132971, rdar://96837923 -* Implementation: Prototyped in https://github.com/apple/swift-collections (on branch "future") +* Implementation: https://github.com/swiftlang/swift/pull/76406 * Review: ([pitch 1](https://forums.swift.org/t/69888))([pitch 2](https://forums.swift.org/t/72745)) ## Introduction From c57bb162c644cc81b539b4164b355a1a77465b4c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 17 Sep 2024 21:45:51 -0700 Subject: [PATCH 3869/4563] Add review thread for SE-0447 (#2568) --- proposals/0447-span-access-shared-contiguous-storage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0447-span-access-shared-contiguous-storage.md b/proposals/0447-span-access-shared-contiguous-storage.md index 61db511040..ef80034b6b 100644 --- a/proposals/0447-span-access-shared-contiguous-storage.md +++ b/proposals/0447-span-access-shared-contiguous-storage.md @@ -7,7 +7,7 @@ * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Bug: rdar://48132971, rdar://96837923 * Implementation: https://github.com/swiftlang/swift/pull/76406 -* Review: ([pitch 1](https://forums.swift.org/t/69888))([pitch 2](https://forums.swift.org/t/72745)) +* Review: ([pitch 1](https://forums.swift.org/t/69888))([pitch 2](https://forums.swift.org/t/72745))([review](https://forums.swift.org/t/se-0447-span-safe-access-to-contiguous-storage/74676)) ## Introduction From 0228cd4031121c96b6f38abb2036bf37e89680d5 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Wed, 18 Sep 2024 08:57:07 -0600 Subject: [PATCH 3870/4563] Fix typo in SE-0446 (#2569) --- proposals/0446-non-escapable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0446-non-escapable.md b/proposals/0446-non-escapable.md index c83492e177..55a7fdcfa6 100644 --- a/proposals/0446-non-escapable.md +++ b/proposals/0446-non-escapable.md @@ -42,7 +42,7 @@ In addition, the use of reference counting to ensure correctness at runtime make Currently, the notion of "escapability" appears in the Swift language as a feature of closures. Nonescapable closures can use a very efficient stack-based representation; -closures that are `@escapable` store their captures on the heap. +closures that are `@escaping` store their captures on the heap. By allowing Swift developers to mark various types as nonescapable, we provide a mechanism for them to opt into a specific set of usage limitations that: From 68481e1226cabc93603c90cb704671817477d409 Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Thu, 19 Sep 2024 15:49:56 -0700 Subject: [PATCH 3871/4563] Update Implementation. --- proposals/NNNN-nonisolated-for-global-actor-cutoff.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md index bda709cfc3..7fb7a1fec7 100644 --- a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md +++ b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md @@ -4,7 +4,7 @@ * Authors: [Sima Nerush](https://github.com/simanerush), [Holly Borla](https://github.com/hborla) * Review Manager: TBD * Status: **Implemented** -* Implementation: [swiftlang/swift#76395](https://github.com/swiftlang/swift/pull/76395) +* Implementation: On `main` gated behind `-enable-experimental-feature GlobalActorInferenceCutoff` * Review: ([pitch](https://forums.swift.org/t/pitch-allow-nonisolated-to-prevent-global-actor-inference/74502)) ## Introduction From 4cafc937089fcf8507efb4315d931a722eee2401 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Sat, 7 Sep 2024 12:42:49 -0400 Subject: [PATCH 3872/4563] Initial version of proposal for raw identifiers. --- proposals/NNNN-escaped-identifiers.md | 453 ++++++++++++++++++++++++++ 1 file changed, 453 insertions(+) create mode 100644 proposals/NNNN-escaped-identifiers.md diff --git a/proposals/NNNN-escaped-identifiers.md b/proposals/NNNN-escaped-identifiers.md new file mode 100644 index 0000000000..3d6592d7cb --- /dev/null +++ b/proposals/NNNN-escaped-identifiers.md @@ -0,0 +1,453 @@ +# Raw identifiers + +* Proposal: [SE-NNNN](NNNN-escaped-identifiers.md) +* Author: [Tony Allevato](https://github.com/allevato); based on [SE-0275](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md) by [Alfredo Delli Bovi](https://github.com/adellibovi) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [swiftlang/swift#76636](https://github.com/swiftlang/swift/pull/76636), [swiftlang/swift-syntax#2857](https://github.com/swiftlang/swift-syntax/pull/2857) +* Review: ([pitch](https://forums.swift.org/t/pitch-revisiting-backtick-delimited-identifiers-that-allow-more-non-identifier-characters/74432)) + +## Introduction + +This proposal adds _raw identifiers_ to the Swift grammar, which are backtick-delimited identifiers that can contain characters other than the current set of identifier-allowed characters in the language. + +## Motivation + +When naming things in Swift, we use identifiers that are limited to a specific set of characters defined by the [Swift grammar](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/summaryofthegrammar/). For the vast majority of identifiers, this set is perfectly reasonable: names of APIs should be no more complex than they need to be, and many natural names do fit within these requirements. However, there do exist some use cases that would be improved by more flexibility in naming: + +* Declarations whose names serve a purely descriptive purpose, like tests. +* Externally-defined entities that already have natural names that do not fit within Swift's rules for identifiers. + +The unifying principle behind these motivating cases is that information is lost or complexity is unnecessarily increased when a developer must forego a more natural name for one that meets Swift's requirements. Indeed, Swift already admits this to a degree by defining the backtick syntax to allow reserved keywords to be used as identifiers. + +### Descriptive test naming + +When this feature was originally proposed as [SE-0275](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md), one of the reasons for rejection was that ["[t]he core team feels that this use case could be better handled by test library design that allowed strings to be associated with test cases, rather than try to encode that information entirely in symbol names."](https://forums.swift.org/t/se-0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers/32538/46) + +We now have a new testing library in the Swift project called [swift-testing](https://github/com/apple/swift-testing) that implements the feature described above: + +```swift +@Test("square returns x * x") +func squareIsXTimesX() { + #expect(square(4) == 4 * 4) +} +``` + +Unfortunately, if the user wants to provide a descriptive name for the test, they are now forced to name it **twice**: once in the `@Test` macro and then again in the function itself. As the number of tests increases (see swift-testing's own tests, for [example](https://github.com/swiftlang/swift-testing/blob/main/Tests/TestingTests/ClockTests.swift)), so, too, does the burden on the developer. + +This is not merely redundant; it also creates an inconsistency in how the tests are referenced by different tools. Test result reports that are generated by the framework will use the display name, as may user interfaces that display the test hierarchy. However, lower-level tooling such as the compiler itself, the linker, the debugger, index data used for code navigation, and backtraces, will only show the Swift language symbol. + +Re-designing the testing framework to eliminate the function name would also not be the correct solution. Consider this hypothetical alternative using a trailing closure that would remove one of the redundant names: + +```swift +// Not being proposed +@Test("square returns x * x") { + #expect(square(4) == 4 * 4) +} +``` + +This would be unsatisfactory for a few reasons: + +* The current `@Test func` syntax used by swift-testing is a strength because it provides progressive disclosure rather than introducing a new bespoke DSL for tests. +* There are subtle differences between how the compiler processes closures and regular function declarations that could result in awkward behavior for test authors. +* The testing framework must still contrive a symbol name for the test by either mangling the user-provided description into something that Swift can support or creating a completely unique name. The user now has no control over that name and no easy way to discover it. That name would still appear in the debugger, index data, and backtraces, so the inconsistency described above still remains. + +### Naturally non-alphabetic identifiers + +There are some situations where the best name for a particular symbol is numeric or at least starts with a number. As an example, consider a hypothetical color design system that lets users choose a base hue and then a variant/shade of that hue from a fixed set. These design systems often give the color variants numeric names that indicate their intensity—100, 200, and so forth. A naïve API might represent the variant as a numeric value: + +```swift +struct Color { + init(hue: Hue, variant: Int) +} +``` + +But this API can only enforce correctness at runtime. A developer should naturally reach for an `enum` type for a fixed set of variants like this, but they must transform the name or add unnecessary ceremony to make it fit into Swift's naming rules: + +```swift +struct Color { + init(hue: Hue, variant: ColorVariant) +} + +// Not ideal; leading underscore usually means "don't use this" +enum ColorVariant { + case _50 + case _100 + case _200 + // ... +} +let color = Color(hue: .red, variant: ._100) + +// Repetitive +enum ColorVariant { + case variant50 + case variant100 + case variant200 + // ... +} +let color = Color(hue: .red, variant: .variant100) + +// "v" is for... version? No, that's not right. +enum ColorVariant { + case v50 + // ... +} +``` + +### Code generators and FFI + +Generating code from an external source like a data exchange format specification or transpiling an API from another language is common in programming. When generating such code, names in the original data source may not be usable as Swift identifiers. + +Another example is generating type-safe accessors for resources bundled in a library on the filesystem or in an asset catalog. The names of such resources may not necessarily obey Swift conventions—an example is Apple's own _SF Symbols_, which has images with names like `1.circle`. + +In these situations, the generator needs to implement a fixed set of transformation rules to convert names that aren't valid identifiers into names that are. One problem with this approach is that such conversions have a cascading effect: the result of converting a Swift-incompatible name into a Swift-compatible one might produce a name that could also be a plausible name in the original source. Therefore, code generators must complicate their logic to avoid such collisions so that an unexpected input doesn't produce invalid code and block the user. This collision-breaking logic often results in arbitrary and difficult-to-understand rules that the user must nevertheless internalize in order to know what to write in the destination language when faced with one of these names in the origin language. + +### Module naming at scale + +Swift modules (and Clang modules with which Swift interoperates) occupy a single, flat[^1] namespace. The prevailing code organization pattern among many Swift projects is to treat a module as a semantic unit and to give them short, simple names. This approach has already been proven to cause difficulties in real-world builds, as evidenced by the need for the [module aliasing](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0339-module-aliasing-for-disambiguation.md) feature added in Swift 5.7 to reduce the problems that arise when modules with conflicting names occur in the same dependency graph. + +[^1]: Clang supports submodules, but they are still _contained_ by their parent modules and importing a parent module also imports all of its (implicit) submodules. This is distinct from having independent compilation units that share a hierarchical namespace. Java packages are an example of the latter. + +Experience has shown that this model does not scale well to massively large Swift codebases that use different organizational and build models; for example, companies that use monorepos and build with distributed build systems like Bazel, which we will elaborate on below. + +First, many of the projects now using Swift historically used Objective-C or are using common components that are written in Objective-C. That Objective-C code was not written with modules in mind and uses header-file-based inclusions. In order to interoperate with Swift, all of that code would need to be grouped into modules. This would be a significant undertaking to do manually. + +Additionally, requiring human developers to choose their own module names has high cognitive overhead and easily leads to conflicts. A component in one part of the codebase can add a new dependency that breaks a different project elsewhere simply by having a conflicting module name with something else the project is already using. Module aliasing has originally designed and implemented does not immediately provide a satisfying remedy here, because it still requires that the project _consuming_ the modules opt into aliasing to resolve the conflict, and it forces them to choose a _second, contrived_ name for the module. + +To work around these issues, the approach Bazel has adopted is to automatically derive a module name for a build target based on the unique identifier—a path to its location in the repository—that Bazel already uses to refer to it. This removes the burden from the developer to choose a unique name and reduces the chance of collisions. However, this process still projects a name from a hierarchical namespace onto a flat one, so the build target's natural identifier must be contorted to fit into the identifier-safe naming required by a Swift module. For example, a module defined at the path `myapp/extensions/widget/common/utils` would be given the name `myapp_extensions_widget_common_utils`. + +While this works, it is not ideal. Users often know where the code that they want to import lives before they write the line of code that imports it. In order to write the `import` declaration, they must mentally transform the path to the code into the module name, or infrastructure developers must provide tooling to do so. And like the code generation case discussed above, the transformation is not usually reversible, which makes certain kinds of tooling difficult to write and does not the possibility of collisions. For example, a module named `myapp_extensions_widget_common_utils` could live at `myapp/extensions/widget/common/utils` or at `myapp/extensions/widget/common_utils`. Designing a reversible transform would require making the encoding less friendly to humans who need to read and write those names in their code. + +## Proposed Solution + +We propose extending the backtick-based syntax used to escape keywords as identifiers to support identifiers that can contain characters other than the standard identifier-safe characters allowed by Swift. + +We will distinguish these two uses of backticks with the following terminology: + +* An **escaped identifier** is a sequence of characters surrounded by backticks that starts with `identifier-head` and is followed by zero or more `identifier-character`, as defined in the Swift [grammar](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/summaryofthegrammar/). This is what Swift supports today to treat keywords as identifiers. +* A **raw identifier** is a sequence of characters surrounded by backticks that contains characters other than those allowed in an escaped identifier. The exact contents are described in [Permissible Characters](#permissible-characters) below. + +In both cases, the backticks are **not** considered part of the identifier; they only delimit the identifier from surrounding tokens. + +Raw identifiers would provide more flexibility in naming for use cases like the ones discussed earlier in the document as we explain below. + +### Describing tests + +Test functions could use raw identifiers to describe their purpose clearly without redundancy: + +```swift +@Test func `square returns x * x`() { + #expect(square(4) == 4 * 4) +} +``` + +Here, the user need only provide a **single** description of the test function. That one name will be used anywhere that the test is referenced: test result reports, the debugger, index data, crash logs, and so forth. + +A key observation here is that when using swift-testing, or another framework like XCTest, the name of the test function serves **only to describe the test.** It is _not_ real API, and its only caller is the test framework itself that discovers the function through some dynamic or generative mechanism. Since the name will only be written once at the declaration site, it makes sense to allow for more flexible and verbose naming for the sake of expressibility and simplicity. + +Using raw identifiers for test naming fits very well with Swift's philosophy of progressive disclosure. Rather than using a bespoke API for descriptive naming, the author's journey follows a path through the following stages: + +* You learn how to write a regular Swift function. +* You learn how to make it a unit test by prepending `@Test` to it. +* You learn that raw identifiers exist and how to apply them to the names of your tests, and this knowledge applies for any identifiers in the language rather than just a specific framework. + +### Other kinds of identifiers + +Raw identifiers would provide a clean solution to the cases where the most natural name for an identifier is numeric. Considering the color design system again, we could write: + +```swift +case ColorVariant { + case `50` + case `100` + case `200` +} +let color = Color(hue: .red, variant: .`100`) +``` + +One could claim that the backticks are just as much ceremony as using a different prefix, but we believe that raw identifiers are a better choice because they would be using **a common notation** that applies to **all** such symbols. This _reduces_ complexity across all Swift codebases, rather than requiring each developer/API to come up with their own conventions as they do today, which increases complexity for readers. + +Raw identifiers could likewise be used for other kinds of foreign identifiers introduced by FFI or code generation. For example, a tool that generates strongly-typed accessors for resources could use raw identifiers to produce a more direct mapping from the resource name to the name in code, reducing complexity by making collisions less likely. + +```swift +extension UIImage { + static var `10.circle`: UIImage { ... } +} +``` + +Raw identifiers also alleviate the naming problems for modules in very large codebases. Instead of deriving a name using a non-reversible transformation, the build system could simply name the module with the unique identifier that it already associates with that module: + +```swift +import `myapp/extensions/widget/common/utils` + +// if explicit disambiguation is needed: +`myapp/extensions/widget/common/utils`.SomeClass +``` + +Doing so greatly improves the experience of developers working in large codebases who can more easily map imports to where the code resides and vice versa, and it also trivializes writing automated tooling that manages build dependencies by scanning imports written in code and updating the build system's definition of the targets. + +### Support in other languages + +Several other modern programming languages acknowledge the occasional but important need that exists for identifiers that do not meet their standard requirements and support raw identifiers like the ones being proposed here. + +In [F#](https://fsharp.org/specs/language-spec/4.1/FSharpSpec-4.1-latest.pdf), identifiers that are surrounded by double backticks and can contain any characters excluding newlines, tabs, and double backticks. + +In [Groovy](https://groovy-lang.org/syntax.html#_quoted_identifiers), identifiers after the dot in a dot-expression can be written as quoted string literals containing characters other than those allowed in standard identifiers. + +In [Kotlin](https://kotlinlang.org/docs/reference/grammar.html#Identifier), identifiers can be surrounded by single backticks. The characters that are allowed inside backticks may differ based on the target backend (for example, the JVM places additional restrictions). The closest comparison to Swift would be Kotlin/Native, which permits any character other than carriage return, newline, or backtick. + +In [Scala](https://www.scala-lang.org/files/archive/spec/2.11/01-lexical-syntax.html), an identifier can contain an arbitrary string when surrounded by single backticks, but host systems may impose some restrictions on which strings are legal for identifiers. + +In [Zig](https://ziglang.org/documentation/master/#Identifiers), an identifier that does not meet the standard requirements may be expressed by using an `@` symbol followed by a string literal, such as `@"with a space"`. + +## Detailed Design + +### Permitted characters + +A raw identifier may contain any valid Unicode characters except for the following: + +* The backtick (`` ` ``) itself, which termintes the identifier. +* Carriage return (`U+000D`) or newline (`U+000A`); identifiers must be written on a single line. +* The NUL character (`U+0000`), which already emits a warning if present in Swift source but would be disallowed completely in a raw identifier. +* All other non-printable ASCII code units that are also forbidden in single-line Swift string literals (`U+0001...U+001F`, `U+007F`). + +In addition to these rules, some specific combinations that require special handling are discussed below. + +#### Operator characters + +A raw identifier may start with, contain, or end with operator characters, but it may not contain **only** operator characters. To avoid confusion, a raw identifier containing only operator characters is treated as a parsing error: it is neither a valid identifier nor an operator: + +```swift +func + (lhs: Int, rhs: Int) -> Int // ok +func `+` (lhs: Int, rhs: Int) -> Int // error + +let x = 1 + 2 // ok +let x = 1 `+` 2 // error +``` + +This leaves the door open for a future use of that syntax, should it be desired. There is more discussion of this in [Alternatives Considered](#alternatives-considered). + +### Symbol generation and mangling + +Backticks are required to delimit a raw identifier written in source code, but they are **not part** of the identifier as it relates to symbols generated by the compiler. For example, ``func `with a space`()`` defines a function whose name is `with a space` without backticks. This is the same as escaped identifiers today: ``func `for`()`` defines a function named `for`, not `` `for` ``. + +Fortunately, Swift's symbol mangler already handles non-ASCII-identifier code units in symbol names today by converting them using Punycode. The current algorithm already works for raw identifiers, with one change needed: it does not recognize that an identifier starting with a digit needs to be encoded. Consider the following example: + +```swift +// Module "test" +public struct FontWeight { + public func `100`() { ... } +} +``` + +Without Punycoding an identifier starting with a digit, the current implementation would mangle the function above as `$s4test10FontWeightV3X00yyF`, which round-trips incorrectly as `test.FontWeight.X00() -> ()`. This proposal would implement the necessary change so that it produces the correct mangling `$s4test10FontWeightV004_100_yyF`. + +### Property wrappers + +When a property wrapper wraps a variable named with a raw identifier, backticks must also be used to refer to its backing storage or its projected value. The prefix sigil (`_` or `$`, respectively) is part of those identifiers, so it is placed _inside_ the backticks. + +```swift +@propertyWrapper +struct Wrapper { + var wrappedValue: T + var projectedValue: Wrapper +} + +struct UsesWrapper { + @Wrapper var `with a space`: Int +} + +let x = UsesWrapper() +print(x.`_with a space`) // correct +doSomethingWith(x.`$with a space`) // correct + +print(x._`with a space`) // error +doSomethingWith(x.$`with a space`) // error +``` + +The dollar sign (`$`) has two special meanings in Swift when used at the beginning of an identifier: + +* When followed by an integer, it references an unnamed closure argument at that index (e.g., `$0`). +* When followed by an identifier, it references the projected value of a property wrapper (e.g., `$someBinding`). + +We must then decide what an identifier like `` `$0` `` means. The only correct choice is to treat `` `$0` `` as a regular identifier, not a closure argument. This is necessary to allow property wrapper projections to be accessed on properties whose names are numeric raw identifiers: + +```swift +@propertyWrapper +struct Wrapper { + var wrappedValue: T + var projectedValue: Wrapper +} + +let `$1` = "hello" // error: cannot declare entity named '$1'; the '$' prefix + // is reserved for implicitly-synthesized declarations + +struct UsesWrapper { + @Wrapper var `0`: Int + + func f() { + let closure: (Int) -> Int { + doSomethingWith(`$0`) // ok, refers to projected value of `0` + return $0 // ok, refers to unnamed closure argument + } + } +} +``` + +### Member access expressions + +When escaping a keyword as an identifer, Swift allows the backticks to be omitted in certain contexts when the compiler knows that it cannot be a keyword. Most commonly, this occurs with member access expressions: + +```swift +enum Access { + case `public` // must be escaped here + case `private` +} + +_ = Access.`public` // ok, but not necessary +_ = Access.public // also ok +``` + +Raw identifiers, on the other hand, **must be escaped by backticks in all contexts** since they can contain characters that could be treated as parsing delimiters: + +```swift +struct S { + var `with a space` = 0 +} + +S().with a space // error, the parser can't know where the member + // name is supposed to end +S().`with a space` // correct +``` + +This rule also guarantees an important invariant for tuples: raw identifiers are never confusable with tuple element indices. If a tuple member is accessed with a numeric raw identifier (i.e., `` tuple.`0` ``), that will only interpreted as a tuple element _label_ and never a tuple element _index_. Similarly, if a tuple member is accessed with an unescaped integer (i.e., `tuple.0`), the behavior has not changed: it is only interpreted as an _index_ and never as a _label_, even if there is a label with the same name. For example, + +```swift +let x = (0, 1) +_ = x.`0` // error + +let a = (5, `0`: 10) +let b = y.0 // z <- 5 +let c = y.`0` // z <- 10 +``` + +### Objective-C Compatibility + +A Swift declaration named with a raw identifier must be given an explicit Objective-C name to export it to Objective-C. The compiler will emit an error if a declaration is given an explicit name with the `@objc(...)` attribute and that name is not a valid Objective-C identifier, or if a declaration is otherwise inferred to be `@objc` and the Swift name of that declaration is not a valid Objective-C identifier. + +```swift +@objc class `Class with a Space` { // error + @objc(someFunction) func `some function`() {} // ok + @objc(`not valid`) func myFunction() {} // error +} + +@objc @objcMembers class AnotherClass { + var `some property`: Int // error +} +``` + +The Objective-C runtime can dynamically support class names and selectors that contain characters that are not expressible in source code, but we do not consider that to be something should be supported. Therefore, such names are forbidden even for symbols that would only be exposed to the runtime but not written to the generated header file. + +### Module names + +The Clang module map parser already supports modules with non-identifier characters in their names by writing their names in double quotes: + +``` +module "some/module/name" { + header "..." +} +``` + +Such a module would already import cleanly into Swift simply by allowing the module name in an `import` statement to be a raw identifier: + +```swift +import `some/module/name` +``` + +Swift modules pose a different challenge. The compiler's serialization and import search path logic assumes that compiled module artifacts have filenames that match the name of the module; for example, `SomeModule` would be named `SomeModule.swiftmodule`. Using raw identifiers as module names would limit us to only those characters supported in filenames, and these character sets differ between platforms. + +There is a feature in the compiler called an "explicit Swift module map" that uses a JSON file to explicitly list all dependencies without using search paths. This can be used to give a module a different name than its filename. However, it does not seem appropriate to restrict the use of raw identifier module names only to users of this feature; a solution should also include users of standard module resolution. + +This proposal suggests an alternative: Continue to require that the `-module-name` passed during compilation be a filesystem-compatible name, as is the case today, and then allow aliases set by `-module-alias` to be raw identifiers. This effectively separates the "physical" name of the module (its filesystem name and its ABI name) from what users write in source code. Build systems using this feature would generate the physical names behind the scenes for users and provide the aliases to the compiler, completely transparent to the user. This approach elegantly builds on top of existing features in the compiler and avoids adding more complexity to serialization. + +Finally, users who want the ABI name (the name that appears in symbol manglings) to match exactly what users are typing in source code—for example, to ensure consistency between the module names in source and what appears in the debugger and crash logs—could go one step further and pass the raw identifier using the `-module-abi-name` flag. + +### Impacts on tooling + +During the review of SE-0275, concerns were raised about whether identifiers containing whitespace (or other traditional delimiter characters) would make language tooling harder to write or use. While we cannot address all possible editors here, we believe that modern LSP-driven editors can handle raw identifiers gracefully. One specific concern raised was that double-clicking a word in a raw identifier would not be able to select the whole identifier. Since then, the Language Server Protocol has defined a `textDocument/selectionRange` request that can be used to determine an "interesting" selection range for a text position in a document. If this request is made for the text position that is inside a raw identifier, Swift's language server could return the range of that identifier in the response, allowing the host editor to select it in its entirety. + +Likewise, syntax highlighting for raw identifiers is straightforward; they are no more complicated than a single-line string literal. + +### Impacts on future reflective APIs + +During the review of SE-0275, the point was raised that allowing a name to contain common type delimiters could create ambiguity for future APIs that look up symbols via runtime reflection: + +```swift +struct X { + struct Y {} // 1 +} +struct `X.Y` {} // 2 + +// Does this return 1 or 2? +_ = hypotheticalTypeByName("X.Y") +``` + +While these APIs do not yet exist, we feel that they could be implemented unambiguously. Without designing such an API (it is certainly out of scope for this proposal), we can identify some basic principles to address that concern. + +First, we can imagine that such an API would—at least at a lower level—allow users to drill down through the module context and its descendant contexts explicitly; for example, something in the style of `myModule.type("X", genericArgs: [swiftStdlibModule.type("Int")]).type("Y")` would be clearly distinct from `myModule.type("X.Y")`. + +Then, if a simplified API like `hypotheticalTypeByName` is desired, we can observe that the type name is being passed in a form such that the library would need the capability to parse a subset of the language's type grammar in order to understand which parts are generic arguments, which are member references, and so forth. Therefore, it stands to reason that the argument to `hypotheticalTypeByName` should be **written as it would be in source**, including any raw identifier delimiters. In doing so, each case is easily distinguished: + +```swift +_ = hypotheticalTypeByName("X.Y") // returns 1 above +_ = hypotheticalTypeByName("`X.Y`") // returns 2 above +``` + +## Alternatives Considered + +[SE-0275](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md) originally proposed using backticks to support qualified references to operators, such as the following: + +```swift +let add: (Int, Int) -> Int = Int.+ // not allowed today +let add: (Int, Int) -> Int = Int.`+` // would have been allowed by SE-0275 +``` + +As mentioned above, this proposal does not allow this; it is an error to write a raw identifier containing **only** identifier characters. We agree with the Core Team's original feedback that since backticks remove the "magic" around a special character sequence, there is potential for confusion about whether writing `` `+` `` refers to the operator or to a different regular identifier named `+`. If we intended the former, there are also open questions about how one would distinguish prefix, postfix, infix operators with such a syntax. This feature, if desired, demands its own in-depth design and separate proposal. + +## Future Directions + +There are natural extensions of the raw identifier syntax that could be used to support multi-line identifiers or identifiers containing backticks by adopting a similar syntax to that of raw string literals: + +~~~swift +let x = #`contains`some`backticks`# +let y = ##`contains`#single`#pound`#delimiters`## + +func ``` + This is a function that multiplies two numbers. + It's named this way to make sure you REALLY know + what you're getting into when you multiply two + numbers. + ```(_ x: Int, _ y: Int) { x * y } + +let fifteen = ``` + This is a function that multiplies two numbers. + It's named this way to make sure you REALLY know + what you're getting into when you multiply two + numbers. + ```(3, 5) +~~~ + +At this time, however, we do not believe there are any compelling use cases for such identifiers. + +## Source compatibility + +This proposal is purely additive; it does not affect compatibility with existing source code. + +## ABI compatibility + +This proposal has no effect on existing ABI. It only makes new valid Swift symbol manglings for symbols that were previously invalid. + +## Implications on adoption + +For codebases built entirely from source using the same version of the Swift compiler would see no negative impact from using this feature. + +For example, if a resilient library used raw identifiers in any declarations that are serialized into the textual interface (e.g., `public` or `@usableFromInline`), that interface would not be consumable by versions of the compiler prior to when this feature was introduced because the older compilers would not be able to parse the identifiers. This, however, is the case for any feature that introduces a new syntax into the language. From abd39ea48ca51ec441f88267a19014088577445e Mon Sep 17 00:00:00 2001 From: Jamie <2119834+jamieQ@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:37:29 -0500 Subject: [PATCH 3873/4563] [gardening]: fix typo SE-0427 (#2571) --- proposals/0427-noncopyable-generics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0427-noncopyable-generics.md b/proposals/0427-noncopyable-generics.md index e03d521a56..321c9090e8 100644 --- a/proposals/0427-noncopyable-generics.md +++ b/proposals/0427-noncopyable-generics.md @@ -362,7 +362,7 @@ struct or enum declares a `deinit`. Deterministic destruction requires the type to be unconditionally noncopyable. A conformance to `Copyable` is checked by verifying that every stored property -(of a struct) or associated value (or an enum) itself conforms to `Copyable`. +(of a struct) or associated value (of an enum) itself conforms to `Copyable`. For a conditional `Copyable` conformance, the conditional requirements must be sufficient to ensure this is the case. For example, the following is rejected, because the struct cannot unconditionally conform to `Copyable`, having a From 554069f7c89471cc205cdf00862659267cde7bed Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 24 Sep 2024 09:40:58 -0700 Subject: [PATCH 3874/4563] SE-0447: Fix self-link (#2573) --- proposals/0447-span-access-shared-contiguous-storage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0447-span-access-shared-contiguous-storage.md b/proposals/0447-span-access-shared-contiguous-storage.md index ef80034b6b..e55c1e72dc 100644 --- a/proposals/0447-span-access-shared-contiguous-storage.md +++ b/proposals/0447-span-access-shared-contiguous-storage.md @@ -1,6 +1,6 @@ # Span: Safe Access to Contiguous Storage -* Proposal: [SE-0447](0447-safe-shared-contiguous-storage.md) +* Proposal: [SE-0447](0447-span-access-shared-contiguous-storage.md) * Authors: [Guillaume Lessard](https://github.com/glessard), [Michael Ilseman](https://github.com/milseman), [Andrew Trick](https://github.com/atrick) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active review (September 17...October 1, 2024)** From 174b0f08a70c80a0397655a6c9315b643a3f8b75 Mon Sep 17 00:00:00 2001 From: Alexander Momchilov Date: Tue, 24 Sep 2024 19:44:22 -0400 Subject: [PATCH 3875/4563] SE-0439: Indent code blocks under bullet points (#2574) --- proposals/0439-trailing-comma-lists.md | 259 ++++++++++++------------- 1 file changed, 128 insertions(+), 131 deletions(-) diff --git a/proposals/0439-trailing-comma-lists.md b/proposals/0439-trailing-comma-lists.md index e6b0bed712..05f2e23356 100644 --- a/proposals/0439-trailing-comma-lists.md +++ b/proposals/0439-trailing-comma-lists.md @@ -32,7 +32,7 @@ Other comma-separated lists in the language could also benefit from the flexibil ```swift let numbers = [1, 2, 0, 3, 4, 0, 0, 5] - + let subsequences = numbers.split( separator: 0, // maxSplits: 1 @@ -51,177 +51,174 @@ This proposal adds support for trailing commas in comma-separated lists when the - Tuples and tuple patterns. -```swift - -let velocity = ( - 1.66007664274403694e-03, - 7.69901118419740425e-03, - 6.90460016972063023e-05, -) - -let ( - velocityX, - velocityY, - velocityZ, -) = velocity + ```swift + let velocity = ( + 1.66007664274403694e-03, + 7.69901118419740425e-03, + 6.90460016972063023e-05, + ) -``` + let ( + velocityX, + velocityY, + velocityZ, + ) = velocity + ``` - Parameter and argument lists of initializers, functions, enum associated values, expression macros, attributes, and availability specs. -```swift - -func foo( - input1: Int = 0, - input2: Int = 0, -) { } - -foo( - input1: 1, - input2: 1, -) - -struct S { - init( + ```swift + func foo( input1: Int = 0, input2: Int = 0, ) { } -} -enum E { - case foo( - input1: Int = 0, - input2: Int = 0, + foo( + input1: 1, + input2: 1, ) -} -@Foo( - "input 1", - "input 2", - "input 3", -) -struct S { } - -#foo( - "input 1", - "input 2", - "input 3", -) + struct S { + init( + input1: Int = 0, + input2: Int = 0, + ) { } + } + + enum E { + case foo( + input1: Int = 0, + input2: Int = 0, + ) + } + + @Foo( + "input 1", + "input 2", + "input 3", + ) + struct S { } -struct S { #foo( "input 1", "input 2", "input 3", ) -} -if #unavailable( - iOS 15, - watchOS 9, -) { } + struct S { + #foo( + "input 1", + "input 2", + "input 3", + ) + } + + if #unavailable( + iOS 15, + watchOS 9, + ) { } + ``` -``` - Subscripts, including key path subscripts. -```swift -let value = m[ - x, - y, -] + ```swift + let value = m[ + x, + y, + ] -let keyPath = \Foo.bar[ - x, - y, -] + let keyPath = \Foo.bar[ + x, + y, + ] -f(\.[ - x, - y, -]) -``` + f(\.[ + x, + y, + ]) + ``` - `if`, `guard` and `while` condition lists. -```swift -if - condition1, - condition2, -{ } - -while - condition1, - condition2, -{ } - -guard - condition1, - condition2, -else { } -``` + ```swift + if + condition1, + condition2, + { } + + while + condition1, + condition2, + { } + + guard + condition1, + condition2, + else { } + ``` - `switch` case labels. -```swift -switch number { - case - 1, - 2, - : - ... - default: - .. -} -``` + ```swift + switch number { + case + 1, + 2, + : + ... + default: + .. + } + ``` - Closure capture lists. -```swift -{ [ - capturedValue1, - capturedValue2, - ] in -} -``` + ```swift + { [ + capturedValue1, + capturedValue2, + ] in + } + ``` - Inheritance clauses. -```swift -struct S: - P1, - P2, - P3, -{ } -``` + ```swift + struct S: + P1, + P2, + P3, + { } + ``` - Generic parameters. -```swift -struct S< - T1, - T2, - T3, -> { } -``` + ```swift + struct S< + T1, + T2, + T3, + > { } + ``` - Generic `where` clauses. -```swift -struct S< - T1, - T2, - T3 -> where - T1: P1, - T2: P2, -{ } -``` + ```swift + struct S< + T1, + T2, + T3 + > where + T1: P1, + T2: P2, + { } + ``` - String interpolation -```swift -let s = "\(1, 2,)" -``` + ```swift + let s = "\(1, 2,)" + ``` ## Detailed Design @@ -278,7 +275,7 @@ if condition2, { // ❌ Function produces expected type 'Bool'; did you mean to call it with '()'? return true -} +} { print("something") } ``` @@ -293,7 +290,7 @@ if condition2, { return true -} +} { print("something") } // ❌ Closure expression is unused ``` From 7864fa20cfb3a43aa6874feedb5aedb8be02da2c Mon Sep 17 00:00:00 2001 From: Alexander Momchilov Date: Tue, 24 Sep 2024 19:46:14 -0400 Subject: [PATCH 3876/4563] SE-0439: Link to previous proposal (#2575) --- proposals/0439-trailing-comma-lists.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0439-trailing-comma-lists.md b/proposals/0439-trailing-comma-lists.md index 05f2e23356..54b345f8a5 100644 --- a/proposals/0439-trailing-comma-lists.md +++ b/proposals/0439-trailing-comma-lists.md @@ -5,6 +5,7 @@ * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Accepted with modifications** * Implementation: https://github.com/swiftlang/swift/pull/74522# gated behind `-enable-experimental-feature TrailingComma` +* Previous Proposal: [SE-0084](0084-trailing-commas.md) * Review: ([pitch](https://forums.swift.org/t/pitch-allow-trailing-comma-in-tuples-arguments-and-if-guard-while-conditions/70170)), ([review](https://forums.swift.org/t/se-0439-allow-trailing-comma-in-comma-separated-lists/72876)), ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0439-allow-trailing-comma-in-comma-separated-lists/73216)) ## Introduction From 77dbc936a45b12e9cef5df456b30e8c924e0d64f Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Tue, 24 Sep 2024 22:31:42 -0700 Subject: [PATCH 3877/4563] Update the examples for 'nonisolated' on mutable storage. --- ...NNN-nonisolated-for-global-actor-cutoff.md | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md index 7fb7a1fec7..594a89d424 100644 --- a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md +++ b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md @@ -221,26 +221,29 @@ Because `MyClass` does not conform to `Sendable`, it cannot be accessed from mul For global-actor-isolated value types, [SE-0434: Usability of global-actor-isolated types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows accessing `var` stored properties with `Sendable` type from within the module as `nonisolated`. This proposal extends this rule to **all** `Sendable` value types: ```swift -struct S { - var x: Int = 0 // okay ('nonisolated' is inferred within the module) +protocol P { + @MainActor var x: Int { get } } -actor MyActor { - func test(s: S) { - print(s.x) // synchronous access to 'x' after sending `S` to `MyActor` is okay. +struct S: P { + var x: Int // 'nonisolated' is inferred within the module + + init(x: Int) { + self.x = x // nonisolated access of x is okay } } ``` -In the above code, the value type `S` is implicitly `Sendable` within the module and its storage `x` is of `Sendable` type `Int`. When `Sendable` value types are passed between isolation domains, each isolation domain has an independent copy of the value. Accessing properties stored on a value type from across isolation domains is safe as long as the stored property type is also `Sendable`. Even if the stored property is a `var`, assigning to the property will not risk a data race, because the assignment cannot have effects on copies in other isolation domains. Therefore, synchronous access of `x` from within the module is okay. +In the above code, the value type `S` is implicitly `Sendable` and its protocol requirement stored property `x` is of `Sendable` type `Int`. While the protocol `P` requires `x` to be globally isolated, +under this proposal, the witness `x` is treated as non-isolated within the module. +When `Sendable` value types are passed between isolation domains, each isolation domain has an independent copy of the value. Accessing properties stored on a value type from across isolation domains is safe as long as the stored property type is also `Sendable`. Even if the stored property is a `var`, assigning to the property will not risk a data race, because the assignment cannot have effects on copies in other isolation domains. Therefore, synchronous access of `x` is okay. -Additionally, [SE-0434](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows explicitly annotating globally-isolated value types' properties such as `x` in the previous example with `nonisolated` for synchronous access from outside the module. This proposal extends this rule to **all** `Sendable` value types: +Additionally, [SE-0434](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md) allows explicitly annotating globally-isolated value types' properties such as `x` in the previous example with `nonisolated` for enabling synchronous access from outside the module. This proposal extends this rule to **all** `Sendable` value types: ```swift // In Module A -public struct S: Sendable { - nonisolated public var x: Int = 0 // okay - public init() {} +public protocol P { + @MainActor var x: Int { get } } ``` @@ -248,9 +251,11 @@ public struct S: Sendable { // In Module B import A -actor MyActor { - func test(s: S) { - print(s.x) // synchronous access to 'x' after sending `S` to `MyActor` is okay. +struct S: P { + nonisolated var x: Int // 'nonisolated' is explicitly spelled + + init(x: Int) { + self.x = x // x is non-isolated } } ``` From b94d4ca699b9a8fe025ff36e8b6fe52b9f861d1c Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 25 Sep 2024 23:39:11 +0100 Subject: [PATCH 3878/4563] [SE-0448] Fix link to lookbehind assertions pitch --- proposals/0448-regex-lookbehind-assertions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0448-regex-lookbehind-assertions.md b/proposals/0448-regex-lookbehind-assertions.md index eb80075b13..5702683cee 100644 --- a/proposals/0448-regex-lookbehind-assertions.md +++ b/proposals/0448-regex-lookbehind-assertions.md @@ -5,7 +5,7 @@ * Review Manager: [Steve Canon](https://github.com/stephentyrone) * Status: **Active review (September 17...October 1, 2024)** * Implementation: https://github.com/swiftlang/swift-experimental-string-processing/pull/760 -* Review: ([pitch](https://github.com/swiftlang/swift-evolution/pull/2525))([review](https://forums.swift.org/t/se-0448-regex-lookbehind-assertions/74672)) +* Review: ([pitch](https://forums.swift.org/t/pitch-regex-reverse-matching/73482)) ([review](https://forums.swift.org/t/se-0448-regex-lookbehind-assertions/74672)) ## Introduction From 2281254f6adfa9108948982a42ce1e6e47210c6a Mon Sep 17 00:00:00 2001 From: Anton Reshetnikov <80463332+mr-anton-reshetnikov@users.noreply.github.com> Date: Thu, 26 Sep 2024 09:24:43 +0300 Subject: [PATCH 3879/4563] Update README.md: add Swift 6.0 release date --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41a6b9a289..a3618ded52 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This repository tracks the ongoing evolution of the Swift programming language, | Version | Announced | Released | | :-------- | :----------------------------------------------------------------------- | :----------------------------------------------------------- | -| Swift 6.0 | [2024-02-22](https://forums.swift.org/t/swift-6-0-release-process/70220) | +| Swift 6.0 | [2024-02-22](https://forums.swift.org/t/swift-6-0-release-process/70220) | [2024-09-17](https://www.swift.org/blog/announcing-swift-6/) | | Swift 5.10 | [2023-08-23](https://forums.swift.org/t/swift-5-10-release-process/66911) | [2024-03-05](https://www.swift.org/blog/swift-5.10-released/) | | Swift 5.9 | [2023-03-06](https://forums.swift.org/t/swift-5-9-release-process/63557) | [2023-09-18](https://www.swift.org/blog/swift-5.9-released/) | | Swift 5.8 | [2022-11-19](https://forums.swift.org/t/swift-5-8-release-process/61540) | [2023-03-30](https://www.swift.org/blog/swift-5.8-released/) | From 3012e21efeffaf624c65c52c5c4958c6fce76632 Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Thu, 26 Sep 2024 09:25:02 -0700 Subject: [PATCH 3880/4563] Clarify examples per feedback. --- ...NNN-nonisolated-for-global-actor-cutoff.md | 68 ++++++++++++++++--- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md index 594a89d424..ef1602d50f 100644 --- a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md +++ b/proposals/NNNN-nonisolated-for-global-actor-cutoff.md @@ -127,10 +127,27 @@ struct A: Refined { } ``` -In the above code, the protocol `Refined` is refining the `GloballyIsolated` protocol, but is declared non-isolated. This means that the `Refined` still has the same requirements as `GloballyIsolated`, but they are not isolated. Therefore, a struct `A` conforming to it is also non-isolated, which allows the programmer for more flexibility when implementing the requirements of a protocol. +In the above code, the protocol `Refined` is refining the `GloballyIsolated` protocol, but is declared non-isolated. This means that the `Refined` still has the same requirements as `GloballyIsolated`, but they are not isolated. Therefore, a struct `A` conforming to it is also non-isolated, which allows the programmer for more flexibility when implementing the requirements of a protocol. #### Extensions +Today, it is possible for extensions to be globally-isolated: + +```swift +struct X {} + +@MainActor extension X { + func f() {} // implicitly globally-isolated + var x: Int { get { 1 } } // implicitly globally-isolated +} +``` + +In the above code, `X` is a non-isolated struct, and extension members +`f()` and `x` are globally-isolated. + +However, if `X` was globally-isolated, before this proposal, the only way to stop extension members from inferring the global actor would be to mark every extension member with +`nonisolated`. + This proposal allows for `nonisolated` attribute to be applied on extension declarations: ```swift @@ -147,7 +164,7 @@ struct C: GloballyIsolated { } ``` -In the code above, the `nonisolated` attribute is applied to an extension declaration for a `GloballyIsolated` protocol. When applied to an extension, `nonisolated` applies to all of its members. In this case, `implicitlyNonisolated` method and the computed property `x` are both nonisolated, and therefore are able to be accessed from a nonisolated context in the body of `explicitlyNonisolated` method of a globally-isolated struct `C`. +In the code above, the `nonisolated` attribute is applied to an extension declaration for a `GloballyIsolated` protocol. When applied to an extension, `nonisolated` applies to all of its members. In this case, `implicitlyNonisolated` method and the computed property `x` are both nonisolated, and therefore are able to be accessed from a nonisolated context in the body of `explicitlyNonisolated` method of a globally-isolated struct `C`. #### Classes, structs, and enums @@ -222,14 +239,16 @@ For global-actor-isolated value types, [SE-0434: Usability of global-actor-isola ```swift protocol P { - @MainActor var x: Int { get } + @MainActor var y: Int { get } } struct S: P { - var x: Int // 'nonisolated' is inferred within the module + var y: Int // 'nonisolated' is inferred within the module +} - init(x: Int) { - self.x = x // nonisolated access of x is okay +struct F { + nonisolated func getS(_ s: S) { + let x = s.y // okay } } ``` @@ -243,7 +262,11 @@ Additionally, [SE-0434](https://github.com/swiftlang/swift-evolution/blob/main/p ```swift // In Module A public protocol P { - @MainActor var x: Int { get } + @MainActor var y: Int { get } +} + +public struct S: P { + public nonisolated var y: Int // 'y' is explicitly non-isolated } ``` @@ -251,11 +274,34 @@ public protocol P { // In Module B import A -struct S: P { - nonisolated var x: Int // 'nonisolated' is explicitly spelled +struct F { + nonisolated func getS(_ s: S) { + let x = s.y // okay + } +} +``` + +In contrast, `y` is still treated as globally-isolated without the explicit +`nonisolated` attribute: + +```swift +// In Module A +public protocol P { + @MainActor var y: Int { get } +} + +public struct S: P { + public var y: Int // globally-isolated outside of the module +} +``` + +```swift +// In Module B +import A - init(x: Int) { - self.x = x // x is non-isolated +struct F { + nonisolated func getS(_ s: S) { + let x = s.y // error: main actor-isolated property 'y' can not be referenced from a nonisolated context } } ``` From d002ca85dcd6a327986564c5a916f1a7e1d79d54 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Thu, 26 Sep 2024 11:09:33 -0700 Subject: [PATCH 3881/4563] Accept SE-0444 --- proposals/0444-member-import-visibility.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0444-member-import-visibility.md b/proposals/0444-member-import-visibility.md index 4404f604e6..a98f0b6857 100644 --- a/proposals/0444-member-import-visibility.md +++ b/proposals/0444-member-import-visibility.md @@ -3,11 +3,11 @@ * Proposal: [SE-0444](0444-member-import-visibility.md) * Authors: [Allan Shortlidge](https://github.com/tshortli) * Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) -* Status: **Active review (September 11...23, 2024)** +* Status: **Accepted** * Bug: [apple/swift#46493](https://github.com/apple/swift/issues/46493) * Implementation: [apple/swift#72974](https://github.com/apple/swift/pull/72974), [apple/swift#73063](https://github.com/apple/swift/pull/73063) * Upcoming Feature Flag: `MemberImportVisibility` -* Review: ([pitch](https://forums.swift.org/t/pitch-fixing-member-import-visibility/71432)) ([review](https://forums.swift.org/t/se-0444-member-import-visibility/74519)) +* Review: ([pitch](https://forums.swift.org/t/pitch-fixing-member-import-visibility/71432)) ([review](https://forums.swift.org/t/se-0444-member-import-visibility/74519)) ([acceptance](https://forums.swift.org/t/accepted-se-0444-member-import-visibility/74966)) ## Introduction From 7f9488e0a41576139510dcb6e87f5b3d87359aed Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 1 Oct 2024 14:43:13 -0700 Subject: [PATCH 3882/4563] Prospective vision: Optional Strict Memory Safety for Swift --- visions/memory-safety.md | 165 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 visions/memory-safety.md diff --git a/visions/memory-safety.md b/visions/memory-safety.md new file mode 100644 index 0000000000..add0b35dd5 --- /dev/null +++ b/visions/memory-safety.md @@ -0,0 +1,165 @@ +# [Prospective vision] Optional Strict Memory Safety for Swift + +Swift is a memory-safe language *by default* , meaning that the major language features and standard library APIs are memory-safe. However, it is possible to opt out of memory safety when it’s pragmatic using certain “unsafe” language or library constructs. This document proposes a path toward an optional “strict” subset of Swift that prohibits any unsafe features. This subset is intended to be used for Swift code bases where memory safety is an absolute requirement, such as security-critical libraries. + +## Introduction + +[Memory safety](https://en.wikipedia.org/wiki/Memory_safety) is a popular topic in programming languages nowadays. Essentially, memory safety is a property that prevents programmer errors from manifesting as [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior) at runtime. Undefined behavior effectively breaks the semantic model of a language, with unpredictable results including crashes, data corruption, and otherwise-impossible program states. Much of the recent focus on memory safety is motivated by security, because memory safety issues offer a fairly direct way to compromise a program: in fact, the lack of memory safety in C and C++ has been found to be the root cause for ~70% of reported security issues in various analyses [[1](https://msrc.microsoft.com/blog/2019/07/a-proactive-approach-to-more-secure-code/)][[2](https://www.chromium.org/Home/chromium-security/memory-safety/)]. + +### Memory safety in Swift + +While there are a number of potential definitions for memory safety, the one provided by [this blog post](https://security.apple.com/blog/towards-the-next-generation-of-xnu-memory-safety/) breaks it down into five dimensions of safety: + +* **Lifetime safety** : all accesses to a value are guaranteed to occur during its lifetime. Violations of this property, such as accessing a value after its lifetime has ended, are often called use-after-free errors. +* **Bounds safety**: all accesses to memory are within the intended bounds of the memory allocation, such as accessing elements in an array. Violations of this property are called out-of-bounds accesses. +* **Type safety** : all accesses to a value use the type to which it was initialized, or a type that is compatible with that type. For example, one cannot access a `String` value as if it were an `Array`. Violations of this property are called type confusions. +* **Initialization safety** : all values are initialized property to being used, so they cannot contain unexpected data. Violations of this property often lead to information disclosures (where data that should be invisible becomes available) or even other memory-safety issues like use-after-frees or type confusions. +* **Thread safety:** all values are accessed concurrently in a manner that is synchronized sufficiently to maintain their invariants. Violations of this property are typically called data races, and can lead to any of the other memory safety problems. + +Since its inception, Swift has provided memory safety for the first four dimensions. Lifetime safety is provided for reference types by automatic reference counting and for value types via [memory exclusivity](https://www.swift.org/blog/swift-5-exclusivity/); bounds safety is provided by bounds-checking on `Array` and other collections; type safety is provided by safe features for casting (`as?` , `is` ) and `enum` s; and initialization safety is provided by “definite initialization”, which doesn’t allow a variable to be accessed until it has been defined. Swift 6’s strict concurrency checking extends Swift’s memory safety guarantees to the last dimension. + +Providing memory safety does not imply the absence of run-time failures. Good language design often means defining away runtime failures in the type system. However, memory safely requires only that an error in the program cannot be escalated into a violation of one of the safety properties. For example, having reference types by non-nullable by default defines away most problems with NULL pointers. With explicit optional types, the force-unwrap operator (postfix `!` ) meets the definition of memory safety by trapping at runtime if the unwrapped optional is `nil` . The standard library also provides the [`unsafelyUnwrapped` property](https://developer.apple.com/documentation/swift/optional/unsafelyunwrapped) that does not check for `nil` in release builds: this does not meet the definition of memory safety because it admits violations of initialization and lifetime safety that could be exploited. + +### Unsafe code + +Swift is a memory-safe language *by default* , meaning that the major language features and standard library APIs are memory-safe. However, there exist opt-outs that allow one to write memory-unsafe code in Swift: + +* Language features like `unowned(unsafe)` and `nonisolated(unsafe)` that disable language safety features locally. +* Library constructs like `UnsafeMutableBufferPointer` or `unsafeBitCast(to:)` that provide lower-level access than existing language constructs provide. +* Interoperability with C-family APIs, which are implemented in a non-memory-safe language and tend to traffic in unsafe pointer types. + +The convention of using `unsafe` or `unchecked` in the names of unsafe constructs works fairly well in practice: memory-unsafe code in Swift tends to sticks out because of the need for `withUnsafe<...>` operations, and for large swaths of Swift code there is no need to reach down for the unsafe APIs. + +However, the convention is not entirely sufficient for identifying all Swift code that makes use of unsafe constructs. For example, it is possible to call the C `memcpy` directly from Swift as, e.g., `memcpy(&to, &from, numBytes)` , which can easily violate memory-safety along any dimension: `to` and `from` might be arrays with incompatible types, the number of bytes might be incorrect, etc. However, “unsafe” or “unchecked” do not appear in this code except as the (unseen) type of the parameters to `memcpy` . + +Moreover, some tasks require lower-level access to memory that is only expressible today via the unsafe pointer types, meaning that one must choose between using only safe constructs, or having access to certain APIs and optimizations. For example, all access to contiguous memory requires an `UnsafeMutableBufferPointer` , which compromises on both lifetime and bounds safety. However, it fulfills a vital role for various systems-programming tasks, including interacting directly with specialized hardware or using lower-level system libraries written in the C family. + +## Strictly-safe subset of Swift + +Swift’s by-default memory safety is a pragmatic choice that provides the benefits of memory safety to most Swift code while not requiring excessive ceremony for those places where some code needs to drop down to use unsafe constructs. However, there are code bases where memory safety is more important than programmer convenience, such as in security-critical subsystems handling untrusted data or that are executing with elevated privileges in an OS. + +For such code bases, it’s important to ensure that the code is staying within the strictly-safe subset of Swift. This can be accomplished with a compiler option that produces an error for any use of unsafe code, whether it’s an unsafe language feature or unsafe library construct. Any code written within this strictly-safe subset also works as “normal” Swift and can interoperate with existing Swift code. + +The compiler would flag any use of the following unsafe language features: + +* `@unchecked Sendable` +* `unowned(unsafe)` +* `nonisolated(unsafe)` +* `unsafeAddressor`, `unsafeMutableAddressor` + +In addition, an `@unsafe` attribute would be added to the language and would be used to mark any declaration that is unsafe to use. In the standard library, the following functions and types would be marked `@unsafe` : + +* `Unsafe(Mutable)(Raw)(Buffer)Pointer` +* `(Closed)Range.init(uncheckedBounds:)` +* `OpaquePointer` +* `CVaListPointer` +* `Unmanaged` +* `unsafeBitCast`, `unsafeDowncast` +* `Optional.unsafelyUnwrapped` +* `UnsafeContinuation`, `withUnsafe(Throwing)Continuation` +* `UnsafeCurrentTask` +* `Mutex`'s `unsafeTryLock`, `unsafeLock`, `unsafeUnlock` +* `VolatileMappedRegister.init(unsafeBitPattern:)` +* The `subscript(unchecked:)` introduced by the `Span` proposal. + +Any use of these APIs would be flagged by the compiler as a use of an unsafe construct. In addition to the direct `@unsafe` annotation, any API that uses an `@unsafe` type is considered to itself be unsafe. This includes C-family APIs that use unsafe types, such as the aforementioned `memcpy` that uses `Unsafe(Mutable)RawPointer` in its signature: + +```swift +func memcpy( + _: UnsafeMutableRawPointer?, + _: UnsafeRawPointer?, + _: Int +) -> UnsafeMutableRawPointer? +``` + +The rules described above make it possible to detect and report the use of unsafe constructs in Swift. + +An `@unsafe` function should be allowed to use other unsafe constructs without emitting any diagnostics. However, there are also library functions that encapsulate unsafe behavior in a safe API, such as the standard library’s `Array` and [`Span`](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md)that are necessarily built from unsafe primitives. Such functions need some way to acknowledge the unsafe behavior while still being considered safe from the outside, such as an `unsafe { ... }` code block or a `@safe(unchecked)` attribute. + +The following sections describe language features and library constructs that improve on what can be expressed within the strictly-safe subset of Swift. These improvements will also benefit Swift in general, making it easier to correctly work with contiguous memory and interoperate with APIs from the C-family on languages. + +## Accessing contiguous memory + +Nearly every “unsafe” language feature and standard library API described in the previous section already has safe counterparts in the language: safe concurrency patterns via actors and `Mutex` , safe casting via `as?` , runtime-checked access to optionals (via `!` ) and continuations (`withChecked(Throwing)Continuation` ), and so on. + +One of the primary places where this doesn’t hold is with low-level access to contiguous memory. Even with `ContiguousArray` , which stores its elements contiguously, the only way to access elements is either one-by-one (e.g., subscripting) or to use an operation like `withUnsafeBufferPointer` that provides temporary access the storage via an `Unsafe(Mutable)BufferPointer` argument to a closure. These APIs are memory-unsafe along at least two dimensions: + +* **Lifetime safety**: the unsafe buffer pointer should only be used within the closure, but there is no checking to establish that the pointer does not escape the closure. If it does escape, it could be used after the closure has returned and the pointer could have effectively been “freed.” +* **Bounds safety**: the unsafe buffer pointer types do not perform bounds checking in release builds. + +[Non-escapable types](https://github.com/swiftlang/swift-evolution/pull/2304) provide the ability to create types whose instances cannot escape out of the context in which they were created with no runtime overhead. Non-escapable types allow the creation of a [memory-safe counterpart to the unsafe buffer types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md), proposed under the name `Span` . With `Span` , it becomes possible to access contiguous memory in an array in a manner that maintains memory safety. For example: + +```swift +myInts.withSpan { span in + globalSpan = span // error: span value cannot escape the closure + print(span[myArray.count]) // runtime error: out-of-bounds access + return span.first ?? 0 +} +``` + +[Lifetime dependencies](https://github.com/swiftlang/swift-evolution/pull/2305) can greatly improve the expressiveness of non-escaping types, providing the ability to work with types like `Span` without requiring deeply-nested `with` blocks. Additionally, they make it possible to build more complex data structures out of non-escaping types, extending Swift’s capabilities while maintaining memory safety. + +## Expressing memory-safe interfaces for the C family of languages + +The C family of languages do not provide memory safety along any of the dimensions described in this document. As such, a Swift program that makes use of C APIs is never fully “memory safe” in the strict sense, because any C code called from Swift could undermine the memory safety guarantees Swift is trying to provide. Requiring that all such C code be rewritten in Swift would go against Swift’s general philosophy of incremental adoption into existing ecosystems. Therefore, this document proposes a different strategy: code written in Swift will be auditably memory-safe so long as the C APIs it uses follow reasonable conventions with respect to memory safety. As such, writing new code (or incrementally rewriting code from the C family) will not introduce new memory safety bugs, so that adopting Swift in an existing code base will incrementally improve on memory safety. + +In the C family of languages, the primary memory safety issue for APIs is the widespread use of pointers that have neither lifetime annotations (who owns the pointer?) nor bounds annotations (how many elements does it point to?). As such, the pointers used in C APIs are reflected in Swift as unsafe pointer types, as shown above with `memcpy` . + +Despite the lack of this information, C APIs often follow a reasonable set of conventions that make them usable in Swift without causing memory-safety problems. Swift has a long history of utilizing annotations in C headers to describe these conventions and improve the projection of C APIs into Swift, including: + +* Nullability annotations (`_Nullable`, `_Nonnull`) that describe what values can be NULL, and affects whether a C type is reflected as optional in Swift. +* Non-escaping annotations (e.g., `__attribute__((noescape))`) on function/block pointer parameters, which results in them being imported as non-escaping function parameters. +* `@MainActor` and `Sendable` annotations on C APIs that support Swift 6’s data-race safety model. + +To provide safer interoperability with C APIs, additional annotations can be provided in C that Swift can use to project those C APIs into Swift APIs without any use of unsafe pointers. For example, the Clang [bounds-safety attributes](https://clang.llvm.org/docs/BoundsSafety.html) allow one to express when a C pointer’s size is described by another value: + +```cpp +double average(const double *__counted_by(N) ptr, int N); +``` + +Today, this function would be projected into a Swift function like the following: + +```swift +/*@unsafe*/ func average(_ ptr: UnsafePointer!, _ N: CInt) -> Double +``` + +However, Swift could use the `__counted_by` attribute to provide a more convenient API that bundles the count and length together, e.g., + +```swift +/*@unsafe*/ func average(_ ptr: UnsafeBufferPointer) -> Double +``` + +Now, a Swift caller that passes a local `Double` array would not need to pass the count separately, and cannot get it wrong: + +```swift +var values = [3.14159, 2.71828] +average(values) // ok, no need to pass count separately +``` + +This call is still technically unsafe, because we’re passing a temporary pointer into the array’s storage down to the `average` function. That function could save that pointer into some global variable that gets accessed some time after the call, causing a memory safety violation. The actual implementation of `average` is unlikely to do so, and could express this constraint using the existing `noescape` attribute as follows: + +```cpp +double average(const double *__counted_by(N) __attribute__((noescape)) ptr, int N); +``` + +The `average` function is now expressing that it takes in a `double` pointer referencing `count` values but will not retain the pointer beyond the call. These are the semantic requirements needed to provide a memory-safe Swift projection as follows: + +```swift +func average(_ ptr: Span) -> Double +``` + +More expressive Swift lifetime features can also have corresponding C annotations, allowing more C semantics to be reflected into safe APIs in Swift. For example, consider a C function that finds the minimal element in an array and returns a pointer to it: + +```cpp +const double *min_element(const double *__counted_by(N) __attribute__((noescape)) ptr, int N); +``` + +The returned pointer will point into the buffer passed in, so its lifetime is tied to that of the pointer argument. The aforementioned [lifetime dependencies proposal](https://github.com/swiftlang/swift-evolution/pull/2305) allows this kind of dependency to be expressed in Swift, where the resulting non-escaping value (e.g., a `Span` containing one element) has its lifetime tied to the input argument. + +C++ offers a number of further opportunities for improved safety by modeling lifetimes. For example, `std::list` has a `front()` method that returns a reference to the element at the front of the list: + +```cpp +T& front(); +``` + +The returned reference is valid so long as the list is valid, i.e., its lifetime depends on the `this` parameter. Describing that lifetime dependency in C++ can lead to a safe mapping of this API into Swift without the need to introduce an extra copy of the element. \ No newline at end of file From 682f7c293a3a05bff3e619c3b479bfb68541fb6e Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 2 Oct 2024 10:23:17 -0400 Subject: [PATCH 3883/4563] Accept SE-0445 with modifications (#2583) * Accept SE-0445 with modifications * Fixup: restore an empty line --- proposals/0445-string-index-printing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0445-string-index-printing.md b/proposals/0445-string-index-printing.md index 8c2b5b6557..4c1c92c951 100644 --- a/proposals/0445-string-index-printing.md +++ b/proposals/0445-string-index-printing.md @@ -3,9 +3,9 @@ * Proposal: [SE-0445](0445-string-index-printing.md) * Authors: [Karoy Lorentey](https://github.com/lorentey) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (September 16...30, 2024)** +* Status: **Accepted with modifications** * Implementation: [apple/swift#75433](https://github.com/swiftlang/swift/pull/75433) -* Review: ([pitch](https://forums.swift.org/t/improving-string-index-s-printed-descriptions/57027)) ([review](https://forums.swift.org/t/se-0445-improving-string-indexs-printed-descriptions/74643)) +* Review: ([pitch](https://forums.swift.org/t/improving-string-index-s-printed-descriptions/57027)) ([review](https://forums.swift.org/t/se-0445-improving-string-indexs-printed-descriptions/74643)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0445-improving-string-index-s-printed-descriptions/75108)) ## Introduction From 3a7ff03370556b37df3a463a9991c3d856bc0ae5 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 2 Oct 2024 11:10:58 -0400 Subject: [PATCH 3884/4563] Update 0448-regex-lookbehind-assertions.md (#2584) --- proposals/0448-regex-lookbehind-assertions.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/proposals/0448-regex-lookbehind-assertions.md b/proposals/0448-regex-lookbehind-assertions.md index 5702683cee..744bdbd85c 100644 --- a/proposals/0448-regex-lookbehind-assertions.md +++ b/proposals/0448-regex-lookbehind-assertions.md @@ -3,9 +3,12 @@ * Proposal: [SE-0448](0448-regex-lookbehind-assertions.md) * Authors: [Jacob Hearst](https://github.com/JacobHearst) [Michael Ilseman](https://github.com/milseman) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active review (September 17...October 1, 2024)** +* Status: **Accepted** * Implementation: https://github.com/swiftlang/swift-experimental-string-processing/pull/760 -* Review: ([pitch](https://forums.swift.org/t/pitch-regex-reverse-matching/73482)) ([review](https://forums.swift.org/t/se-0448-regex-lookbehind-assertions/74672)) +* Review: + ([pitch](https://forums.swift.org/t/pitch-regex-reverse-matching/73482)) + ([review](https://forums.swift.org/t/se-0448-regex-lookbehind-assertions/74672)) + ([acceptance](https://forums.swift.org/t/accepted-se-0448-regex-lookbehind-assertions/75111)) ## Introduction From e76147dc6ce2ec5fb493061672b87afab2a38678 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 2 Oct 2024 15:38:32 -0400 Subject: [PATCH 3885/4563] Assign as SE-0449 and start review. --- ...utoff.md => 0449-nonisolated-for-global-actor-cutoff.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-nonisolated-for-global-actor-cutoff.md => 0449-nonisolated-for-global-actor-cutoff.md} (98%) diff --git a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md b/proposals/0449-nonisolated-for-global-actor-cutoff.md similarity index 98% rename from proposals/NNNN-nonisolated-for-global-actor-cutoff.md rename to proposals/0449-nonisolated-for-global-actor-cutoff.md index ef1602d50f..73f8bb5a4f 100644 --- a/proposals/NNNN-nonisolated-for-global-actor-cutoff.md +++ b/proposals/0449-nonisolated-for-global-actor-cutoff.md @@ -1,9 +1,9 @@ # Allow `nonisolated` to prevent global actor inference -* Proposal: [SE-NNNN](NNNN-nonisolated-for-global-actor-cutoff.md) +* Proposal: [SE-0449](0449-nonisolated-for-global-actor-cutoff.md) * Authors: [Sima Nerush](https://github.com/simanerush), [Holly Borla](https://github.com/hborla) -* Review Manager: TBD -* Status: **Implemented** +* Review Manager: [Tony Allevato](https://github.com/allevato) +* Status: **Active Review (October 2...16, 2024)** * Implementation: On `main` gated behind `-enable-experimental-feature GlobalActorInferenceCutoff` * Review: ([pitch](https://forums.swift.org/t/pitch-allow-nonisolated-to-prevent-global-actor-inference/74502)) From f1bfe5b8c311b0d6c4f06fa58530d7997dec112e Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 2 Oct 2024 15:42:55 -0400 Subject: [PATCH 3886/4563] Link to review thread for SE-0449. --- proposals/0449-nonisolated-for-global-actor-cutoff.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0449-nonisolated-for-global-actor-cutoff.md b/proposals/0449-nonisolated-for-global-actor-cutoff.md index 73f8bb5a4f..f7e7260d8a 100644 --- a/proposals/0449-nonisolated-for-global-actor-cutoff.md +++ b/proposals/0449-nonisolated-for-global-actor-cutoff.md @@ -5,7 +5,7 @@ * Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Active Review (October 2...16, 2024)** * Implementation: On `main` gated behind `-enable-experimental-feature GlobalActorInferenceCutoff` -* Review: ([pitch](https://forums.swift.org/t/pitch-allow-nonisolated-to-prevent-global-actor-inference/74502)) +* Review: ([pitch](https://forums.swift.org/t/pitch-allow-nonisolated-to-prevent-global-actor-inference/74502)) ([review](https://forums.swift.org/t/se-0449-allow-nonisolated-to-prevent-global-actor-inference/75116)) ## Introduction From 023becf69dda1afc7d2660cb9f26fbe5d8d53ca9 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 9 Oct 2024 11:17:43 -0700 Subject: [PATCH 3887/4563] [se0447] edits for Span proposal (#2579) * [se0447] edit regarding _read accessor * [se0447] remove an aside * [se0447] small api update - remove `isWithin` - rename `indicesWithin()` to `indices(of:)`, and reverse polarity. * [se0447] update some doc-comments * [se0447] remove index validation utilities - plan to introduce an API on Range instead * [se0447] explicitly give `Span` an `Index` * [se0447] wordsmith around `unsafeLoad` * [se0447] add the `byteOffsets` property to `RawSpan` --- ...7-span-access-shared-contiguous-storage.md | 103 ++++++------------ 1 file changed, 36 insertions(+), 67 deletions(-) diff --git a/proposals/0447-span-access-shared-contiguous-storage.md b/proposals/0447-span-access-shared-contiguous-storage.md index e55c1e72dc..36c76e7c30 100644 --- a/proposals/0447-span-access-shared-contiguous-storage.md +++ b/proposals/0447-span-access-shared-contiguous-storage.md @@ -81,7 +81,10 @@ extension Span where Element: ~Copyable { public var count: Int { get } public var isEmpty: Bool { get } - public subscript(_ position: Int) -> Element { _read } + public typealias Index = Int + public var indices: Range { get } + + public subscript(_ index: Index) -> Element { _read } } ``` @@ -107,14 +110,24 @@ The following properties, functions and subscripts have direct counterparts in t ```swift extension Span where Element: ~Copyable { + /// The number of initialized elements in the span. public var count: Int { get } - public var isEmpty: Bool { get } - public subscript(_ position: Int) -> Element { _read } + /// A Boolean value indicating whether the span is empty. + public var isEmpty: Bool { get } + + /// The type that represents a position in `Span`. + public typealias Index = Int + + /// The range of indices valid for this `Span` + public var indices: Range { get } + + /// Accesses the element at the specified position. + public subscript(_ position: Index) -> Element { _read } } ``` -Note that we use a `_read` accessor for the subscript, a requirement in order to `yield` a borrowed non-copyable `Element` (see ["Coroutines"](#Coroutines).) This will be updated to a final syntax at a later time, understanding that we intend the replacement to be source-compatible. +Note that we use a `_read` accessor for the subscript, a requirement in order to `yield` a borrowed non-copyable `Element` (see ["Coroutines"](#Coroutines).) This yields an element whose lifetime is scoped around this particular access, as opposed to matching the lifetime dependency of the `Span` itself. This is a language limitation we expect to resolve with a followup proposal introducing a new accessor model. The subscript will then be updated to use the new accessor semantics. We expect the updated accessor to be source-compatible, as it will provide a borrowed element with a wider lifetime than a `_read` accessor can provide. ##### Unchecked access to elements: @@ -122,7 +135,6 @@ The `subscript` mentioned above has always-on bounds checking of its parameter, ```swift extension Span where Element: ~Copyable { - // Unchecked subscripting and extraction /// Accesses the element at the specified `position`. /// @@ -130,40 +142,11 @@ extension Span where Element: ~Copyable { /// /// - Parameter position: The offset of the element to access. `position` /// must be greater or equal to zero, and less than `count`. - public subscript(unchecked position: Int) -> Element { _read } + public subscript(unchecked position: Index) -> Element { _read } } ``` -##### Index validation utilities: - -Every time `Span` uses a position parameter, it checks for its validity, unless the parameter is marked with the word "unchecked". The validation is performed with these functions: - -```swift -extension Span where Element: ~Copyable { - /// Return true if `index` is a valid offset into this `Span` - /// - /// - Parameters: - /// - index: an index to validate - /// - Returns: true if `index` is a valid index - public func boundsContain(_ index: Int) -> Bool - - /// Return true if `indices` is a valid range of offsets into this `Span` - /// - /// - Parameters: - /// - indices: a range of indices to validate - /// - Returns: true if `indices` is a valid range of indices - public func boundsContain(_ indices: Range) -> Bool - - /// Return true if `indices` is a valid range of offsets into this `Span` - /// - /// - Parameters: - /// - indices: a range of indices to validate - /// - Returns: true if `indices` is a valid range of indices - public func boundsContain(_ indices: ClosedRange) -> Bool -} -``` - -Note: these function names are not ideal. +When using the unchecked subscript, the index must be known to be valid. While we are not proposing explicit index validation API on `Span` itself, its `indices` property can be use to validate a single index, in the form of the function `Range.contains(_: Int) -> Bool`. We expect that `Range` will also add efficient containment checking of a subrange's endpoints, which should be generally useful for index range validation in this and other contexts. ##### Identifying whether a `Span` is a subrange of another: @@ -174,21 +157,13 @@ extension Span where Element: ~Copyable { /// Returns true if the other span represents exactly the same memory public func isIdentical(to span: borrowing Self) -> Bool - /// Returns true if the memory represented by `self` is a subrange of - /// the memory represented by `span` - /// - /// Parameters: - /// - span: a span of the same type as `self` - /// Returns: whether `self` is a subrange of `span` - public func isWithin(_ span: borrowing Self) -> Bool - - /// Returns the offsets where the memory of `self` is located within - /// the memory represented by `span`, or `nil` + /// Returns the indices within `self` where the memory represented by `span` + /// is located, or `nil` if `span` is not located within `self`. /// /// Parameters: - /// - span: a subrange of `self` + /// - span: a span that may be a subrange of `self` /// Returns: A range of offsets within `self`, or `nil` - public func indicesWithin(_ span: borrowing Self) -> Range? + public func indices(of span: borrowing Self) -> Range? } ``` @@ -256,7 +231,7 @@ Initializers, required for library adoption, will be proposed alongside [lifetim ##### Accessing the memory of a `RawSpan`: -`RawSpan` has basic operations to access the contents of its memory: `unsafeLoad(as:)` and `unsafeLoadUnaligned(as:)`. These operations are not type-safe, in that the loaded value returned by the operation can be invalid. Some types have a property that makes this operation safe, but there we don't have a way to [formally identify](#SurjectiveBitPattern) such types at this time. +`RawSpan` has basic operations to access the contents of its memory: `unsafeLoad(as:)` and `unsafeLoadUnaligned(as:)`: ```swift extension RawSpan { @@ -302,7 +277,10 @@ extension RawSpan { ) -> T ``` -These functions have counterparts which omit bounds-checking for cases where redundant checks affect performance: +These operations are not type-safe, in that the loaded value returned by the operation can be invalid, and violate type invariants. Some types have a property that makes the `unsafeLoad(as:)` function safe, but we don't have a way to [formally identify](#SurjectiveBitPattern) such types at this time. + +The `unsafeLoad` functions have counterparts which omit bounds-checking for cases where redundant checks affect performance: + ```swift /// Returns a new instance of the given type, constructed from the raw memory /// at the specified offset. @@ -382,20 +360,9 @@ extension RawSpan { /// A Boolean value indicating whether the span is empty. public var isEmpty: Bool { get } -} -``` - -##### `RawSpan` bounds checking: -```swift -extension RawSpan { - /// Return true if `offset` is a valid byte offset into this `RawSpan` - public func boundsContain(_ offset: Int) -> Bool - - /// Return true if `offsets` is a valid range of offsets into this `RawSpan` - public func boundsContain(_ offsets: Range) -> Bool - - /// Return true if `offsets` is a valid range of offsets into this `RawSpan` - public func boundsContain(_ offsets: ClosedRange) -> Bool + + /// The range of valid byte offsets into this `RawSpan` + public var byteOffsets: Range { get } } ``` @@ -407,9 +374,7 @@ When working with multiple `RawSpan` instances, it is often desirable to know wh extension RawSpan { public func isIdentical(to span: borrowing Self) -> Bool - public func isWithin(_ span: borrowing Self) -> Bool - - public func byteOffsetsWithin(_ span: borrowing Self) -> Range? + public func byteOffsets(of span: borrowing Self) -> Range? } ``` @@ -501,6 +466,10 @@ extension Array where Element: BitwiseCopyable { Of these, the closure-taking functions can be implemented now, but it is unclear whether they are desirable. The lifetime-dependent computed properties require lifetime annotations, as initializers do. We are deferring proposing these extensions until the lifetime annotations are proposed. +#### Index Validation Utilities + +This proposal originally included index validation utilities for `Span`. such as `boundsContain(_: Index) -> Bool` and `boundsContain(_: Range) -> Bool`. After review feedback, we believe that the utilities proposed would also be useful for index validation on `UnsafeBufferPointer`, `Array`, and other similar `RandomAccessCollection` types. `Range` already a single-element `contains(_: Bound) -> Bool` function which can be made even more efficient. We should add an additional function that identifies whether a `Range` contains the _endpoints_ of another `Range`. Note that this is not the same as the existing `contains(_: some Collection) -> Bool`, which is about the _elements_ of the collection. This semantic difference can lead to different results when examing empty `Range` instances. + #### A `ContiguousStorage` protocol An earlier version of this proposal proposed a `ContiguousStorage` protocol by which a type could indicate that it can provide a `Span`. `ContiguousStorage` would form a bridge between generically-typed interfaces and a performant concrete implementation. It would supersede the rejected [SE-0256](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0256-contiguous-collection.md). From 2ab2cf9ac0a615512152df3880595d793d9ce804 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 9 Oct 2024 11:21:03 -0700 Subject: [PATCH 3888/4563] Extend review of SE-0447 to October 15, 2024 (#2588) --- proposals/0447-span-access-shared-contiguous-storage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0447-span-access-shared-contiguous-storage.md b/proposals/0447-span-access-shared-contiguous-storage.md index 36c76e7c30..0d7542da4c 100644 --- a/proposals/0447-span-access-shared-contiguous-storage.md +++ b/proposals/0447-span-access-shared-contiguous-storage.md @@ -3,7 +3,7 @@ * Proposal: [SE-0447](0447-span-access-shared-contiguous-storage.md) * Authors: [Guillaume Lessard](https://github.com/glessard), [Michael Ilseman](https://github.com/milseman), [Andrew Trick](https://github.com/atrick) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (September 17...October 1, 2024)** +* Status: **Active review (September 17...October 15, 2024)** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Bug: rdar://48132971, rdar://96837923 * Implementation: https://github.com/swiftlang/swift/pull/76406 From 06bc5778fb1e52e2a062e9712540d9657d3ebe42 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 9 Oct 2024 13:39:34 -0700 Subject: [PATCH 3889/4563] [se0447] remove undesirable invisible characters (#2589) --- proposals/0447-span-access-shared-contiguous-storage.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0447-span-access-shared-contiguous-storage.md b/proposals/0447-span-access-shared-contiguous-storage.md index 0d7542da4c..8d687de488 100644 --- a/proposals/0447-span-access-shared-contiguous-storage.md +++ b/proposals/0447-span-access-shared-contiguous-storage.md @@ -114,7 +114,7 @@ extension Span where Element: ~Copyable { public var count: Int { get } /// A Boolean value indicating whether the span is empty. - public var isEmpty: Bool { get } + public var isEmpty: Bool { get } /// The type that represents a position in `Span`. public typealias Index = Int @@ -146,7 +146,7 @@ extension Span where Element: ~Copyable { } ``` -When using the unchecked subscript, the index must be known to be valid. While we are not proposing explicit index validation API on `Span` itself, its `indices` property can be use to validate a single index, in the form of the function `Range.contains(_: Int) -> Bool`. We expect that `Range` will also add efficient containment checking of a subrange's endpoints, which should be generally useful for index range validation in this and other contexts. +When using the unchecked subscript, the index must be known to be valid. While we are not proposing explicit index validation API on `Span` itself, its `indices` property can be use to validate a single index, in the form of the function `Range.contains(_: Int) -> Bool`. We expect that `Range` will also add efficient containment checking of a subrange's endpoints, which should be generally useful for index range validation in this and other contexts. ##### Identifying whether a `Span` is a subrange of another: @@ -468,7 +468,7 @@ Of these, the closure-taking functions can be implemented now, but it is unclear #### Index Validation Utilities -This proposal originally included index validation utilities for `Span`. such as `boundsContain(_: Index) -> Bool` and `boundsContain(_: Range) -> Bool`. After review feedback, we believe that the utilities proposed would also be useful for index validation on `UnsafeBufferPointer`, `Array`, and other similar `RandomAccessCollection` types. `Range` already a single-element `contains(_: Bound) -> Bool` function which can be made even more efficient. We should add an additional function that identifies whether a `Range` contains the _endpoints_ of another `Range`. Note that this is not the same as the existing `contains(_: some Collection) -> Bool`, which is about the _elements_ of the collection. This semantic difference can lead to different results when examing empty `Range` instances. +This proposal originally included index validation utilities for `Span`. such as `boundsContain(_: Index) -> Bool` and `boundsContain(_: Range) -> Bool`. After review feedback, we believe that the utilities proposed would also be useful for index validation on `UnsafeBufferPointer`, `Array`, and other similar `RandomAccessCollection` types. `Range` already a single-element `contains(_: Bound) -> Bool` function which can be made even more efficient. We should add an additional function that identifies whether a `Range` contains the _endpoints_ of another `Range`. Note that this is not the same as the existing `contains(_: some Collection) -> Bool`, which is about the _elements_ of the collection. This semantic difference can lead to different results when examing empty `Range` instances. #### A `ContiguousStorage` protocol From fcf50dcf89e673b47dc099caded9fe4f1e5a35d4 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 9 Oct 2024 15:22:51 -0700 Subject: [PATCH 3890/4563] SE-0445: incorporate LSG review notes and mark as implemented (#2586) * SE-0445: Update to incorporate LSG review notes * SE-0445: Mark as implemented on main --- proposals/0445-string-index-printing.md | 54 +++++++++++++------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/proposals/0445-string-index-printing.md b/proposals/0445-string-index-printing.md index 4c1c92c951..f59a4b9f18 100644 --- a/proposals/0445-string-index-printing.md +++ b/proposals/0445-string-index-printing.md @@ -3,13 +3,14 @@ * Proposal: [SE-0445](0445-string-index-printing.md) * Authors: [Karoy Lorentey](https://github.com/lorentey) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Accepted with modifications** +* Status: **Implemented** (main development branch) * Implementation: [apple/swift#75433](https://github.com/swiftlang/swift/pull/75433) * Review: ([pitch](https://forums.swift.org/t/improving-string-index-s-printed-descriptions/57027)) ([review](https://forums.swift.org/t/se-0445-improving-string-indexs-printed-descriptions/74643)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0445-improving-string-index-s-printed-descriptions/75108)) +* Previous Revision: [v1](https://github.com/swiftlang/swift-evolution/blob/682f7c293a3a05bff3e619c3b479bfb68541fb6e/proposals/0445-string-index-printing.md) ## Introduction -This proposal conforms `String.Index` to `CustomStringConvertible`. +This proposal conforms `String.Index` to `CustomDebugStringConvertible`. ## Motivation @@ -30,7 +31,7 @@ These displays are generated via the default reflection-based string conversion ## Proposed solution -This proposal supplies the missing `CustomStringConvertible` conformance on `String.Index`, resolving this long-standing issue. +This proposal supplies the missing `CustomDebugStringConvertible` conformance on `String.Index`, resolving this long-standing issue. ```swift let string = "👋🏼 Helló" @@ -41,9 +42,9 @@ print(string.utf16.index(after: string.startIndex)) // ⟹ 0[utf8]+1 print(string.firstRange(of: "ell")!) // ⟹ 10[utf8]..<13[utf8] ``` -The sample description strings shown above are illustrative, not normative. This proposal does not specify the exact format and information content of the string returned by the `description` implementation on `String.Index`. As is the case with most conformances to `CustomStringConvertible`, the purpose of these descriptions is to expose internal implementation details for debugging purposes. As those implementation details evolve, the descriptions may need to be changed to match them. Such changes are not generally expected to be part of the Swift Evolution process; so we need to keep the content of these descriptions unspecified. +The sample output strings shown above are illustrative, not normative. This proposal does not specify the exact format and information content of the string returned by the `debugDescription` implementation on `String.Index`. As is the case with most conformances to `CustomDebugStringConvertible`, the purpose of these descriptions is to expose internal implementation details for debugging purposes. As those implementation details evolve, the descriptions may need to be changed to match them. Such changes are not generally expected to be part of the Swift Evolution process; so we need to keep the content of these descriptions unspecified. -(With that said, the example displays shown above are not newly invented -- they have already proved their usefulness in actual use. They were developed while working on subtle string processing problems in Swift 5.7, and [LLDB has been shipping them as built-in data formatters][lldb] since the Swift 5.8 release. +(With that said, the example displays shown above are not newly invented -- they have already proven their usefulness in actual use. They were developed while working on subtle string processing problems in Swift 5.7, and [LLDB has been shipping them as built-in data formatters][lldb] since the Swift 5.8 release. In the displays shown, string indices succinctly display their storage offset, their expected encoding, and an (optional) transcoded offset value. For example, the output `15[utf8]` indicates that the index is addressing the code unit at offset 15 in a UTF-8 encoded `String` value. The `startIndex` is at offset zero, which works the same with _any_ encoding, so it is displayed as `0[any]`. As of Swift 6.0, on some platforms string instances may store their text in UTF-16, and so indices within such strings use `[utf16]` to specify that their offsets are measured in UTF-16 code units. @@ -53,44 +54,42 @@ The `+1` in `0[utf8]+1` is an offset into a _transcoded_ Unicode scalar; this in All of this is really useful information to see while developing or debugging string algorithms, but it is also deeply specific to the particular implementation of `String` that ships in Swift 6.0; therefore it is inherently unstable, and it may change in any Swift release.) - + + + + + + ## Detailed design ``` @available(SwiftStdlib 6.1, *) -extension String.Index: CustomStringConvertible {} +extension String.Index: CustomDebugStringConvertible {} extension String.Index { @backDeployed(before: SwiftStdlib 6.1) - public var description: String {...} + public var debugDescription: String {...} } ``` ## Source compatibility -The new conformance changes the result of converting a `String.Index` value to a string. This changes observable behavior: code that attempts to parse the result of `String(describing:)` can be mislead by the change of format. +The new conformance changes the result of converting a `String.Index` value to a string. This changes observable behavior: code that attempts to parse the result of `String(describing:)` or `String(reflecting:)` can be mislead by the change of format. -However, `String(describing:)` and `String(reflecting:)` explicitly state that when the input type conforms to none of the standard string conversion protocols, then the result of these operations is unspecified. +However, the documentation of these interfaces explicitly state that when the input type conforms to none of the standard string conversion protocols, then the result of these operations is unspecified. Changing the value of an unspecified result is not considered to be a source incompatible change. ## ABI compatibility -The proposal retroactively conforms a previously existing standard type to a previously existing standard protocol. This is technically an ABI breaking change -- on ABI stable platforms, we may have preexisting Swift binaries that assume that `String.Index is CustomStringConvertible` returns `false`, or ones that are implementing this conformance on their own. +The proposal retroactively conforms a previously existing standard type to a previously existing standard protocol. This is technically an ABI breaking change -- on ABI stable platforms, we may have preexisting Swift binaries that assume that `String.Index is CustomDebugStringConvertible` returns `false`, or ones that are implementing this conformance on their own. We do not expect this to be an issue in practice. ## Implications on adoption -The `String.Index.description` property is defined to be backdeployable, but the conformance itself is not. (It cannot be.) +The `String.Index.debugDescription` property is defined to be backdeployable, but the conformance itself is not. (It cannot be.) Code that runs on ABI stable platforms will not get the nicer displays when running on earlier versions of the Swift Standard Library. @@ -101,18 +100,18 @@ print(str.firstRange(of: "Dog")!) // newer stdlib: 5[utf8]..<8[utf8] ``` -This can be somewhat mitigated by explicitly invoking the `description` property, but this isn't recommmended as general practice. +This can be somewhat mitigated by explicitly invoking the `debugDescription` property, but this isn't recommmended as general practice. ```swift -print(str.endIndex.description) +print(str.endIndex.debugDescription) // always: 11[utf8] ``` ## Future directions -### Additional `CustomStringConvertible` conformances +### Additional `CustomStringConvertible` or `CustomDebugStringConvertible` conformances -Other preexisting types in the Standard Library may also usefully gain `CustomStringConvertible` conformances in the future: +Other preexisting types in the Standard Library may also usefully gain custom string conversions in the future: - `Set.Index`, `Dictionary.Index` - `Slice`, `DefaultIndices` @@ -124,7 +123,7 @@ Other preexisting types in the Standard Library may also usefully gain `CustomSt ### New String API to expose the information in these descriptions -The information exposed in the index descriptions shown above is mostly retrievable through public APIs, but not entirely: perhaps most importantly, there is no way to get the expected encoding of a string index through the stdlib's public API surface. The lack of such an API may encourage interested Swift developers to try retrieving this information by parsing the unstable `description` string, or by bitcasting indices to peek at the underlying bit patterns -- neither of which would be healthy for the Swift ecosystem overall. It therefore is desirable to eventually expose this information as well, through API additons like the drafts below: +The information exposed in the index descriptions shown above is mostly retrievable through public APIs, but not entirely: perhaps most importantly, there is no way to get the expected encoding of a string index through the stdlib's public API surface. The lack of such an API may encourage interested Swift developers to try retrieving this information by parsing the unstable `debugDescription` string, or by bitcasting indices to peek at the underlying bit patterns -- neither of which would be healthy for the Swift ecosystem overall. It therefore is desirable to eventually expose this information as well, through API additons like the drafts below: ```swift extension String { @@ -173,4 +172,9 @@ Given that these APIs are quite obscure/subtle, and they pose some interesting d ## Alternatives considered -None. +The original version of this proposal suggested conforming `String.Index` to `CustomStringConvertible`, not `CustomDebugStringConvertible`. The change to the debug-flavored protocol emphasizes that the new descriptions aren't intended to be used outside debugging contexts. + + +## Acknowledgements + +We'd like to express our appreciation to Jordan Rose and Ben Rimmington for scratching at the `CustomStringConvertible` vs `CustomDebugStringConvertible` distinction during the review discussion. From b665179a3c02569648dd5d0b00a738060a55ffd0 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Tue, 15 Oct 2024 18:12:16 +0100 Subject: [PATCH 3891/4563] Incorporate recent vision feedback --- visions/webassembly.md | 82 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/visions/webassembly.md b/visions/webassembly.md index d1b406c071..c1ba36f46c 100644 --- a/visions/webassembly.md +++ b/visions/webassembly.md @@ -2,7 +2,7 @@ ## Introduction -WebAssembly (abbreviated [Wasm](https://webassembly.github.io/spec/core/intro/introduction.html#wasm)) is a virtual +WebAssembly (abbreviated [Wasm](https://webassembly.github.io/spec/core/intro/introduction.html#wasm)) is a virtual machine instruction set focused on portability, security, and high performance. It is vendor-neutral, designed and developed by [W3C](https://w3.org). An implementation of a WebAssembly virtual machine is usually called a *WebAssembly runtime*. @@ -125,3 +125,83 @@ header using the Clang version of attributes. is the counterpart of `@_extern(wasm)` working in the opposite direction. This explicitly makes a Swift declaration available outside of the current module, added to its exports section under a given name. Again, the lack of this attribute in Swift requires use of C headers as a workaround. + +## Platform-specific Considerations + +### Debugging + +Debugging Wasm modules is challenging because Wasm does not expose ways to introspect and control the execution of +the Wasm instance, so a debugger cannot be built on top of Wasm itself. Therefore, special support from the Wasm +execution engine is necessary for debugging. + +The current state of debugging tools in the Wasm ecosystem is not as mature as other platforms but there are two +main directions: + +1. LLDB debugger with Wasm runtime supporting GDB Remote Serial Protocol [1] +2. Wasm runtime with a built-in debugger [2] + +The first approach provides an almost equivalent experience to existing debugging workflows on other platforms. It +can utilize LLDB's Swift support, remote metadata inspection and serialized Swift module information. However, since +Wasm is a Harvard architecture and has no way to allocate executable memory space at runtime, implementing expression +evaluation with JIT in user space is challenging. In other words, gdb stub in Wasm engines need tricky implementations +or need to extend the GDB Remote Serial Protocol. + +The second approach embeds the debugger within the Wasm engine. In scenarios where the Wasm engine is embedded as a guest +language in another host language engine (e.g. within a Web Browser), this approach allows seamless debugging experiences +with the host language by integrating with the host debugger. For example, in cases where JavaScript and Wasm call frames +are interleaved, the debugger works well in both contexts without switching tools. Debugging tools like Chrome DevTools +can use DWARF information embedded in Wasm file to provide debugging support. However, supporting Swift-specific +metadata information and JIT-based expression evaluation will require integrating LLDB's Swift plugin with these debuggers +in some way. + +Given that both approaches can utilize DWARF information, the Swift toolchain should generate as much DWARF information as possible +for statically known things. Also, making Swift type information DWARF-friendly is desirable because the current debug +information generated by the Swift compiler assumes that debuggers can understand certain Swift primitives (e.g. `Swift.Int` +is currently represented as a `DW_TAG_structure_type` node without any member but ideally such types should be represented +as `DW_TAG_base_type` nodes with `DW_AT_encoding` attribute or provide a debug summary string by LLDB Summary Strings) + +[1]: https://github.com/llvm/llvm-project/pull/77949 +[2]: https://github.com/ChromeDevTools/devtools-frontend/blob/main/extensions/cxx_debugging + +### Multi-threading and Concurrency + +WebAssembly has [atomic operations in the instruction set](https://github.com/WebAssembly/threads) (only sequential +consistency is supported), but it does not have a built-in way to create threads. Instead, it relies on the host +environment to provide multi-threading support. This means that multi-threading in Wasm is dependent on the Wasm runtime +that executes the module. There are two proposals to standardize ways to create threads in Wasm: (1) [wasi-threads](https://github.com/WebAssembly/wasi-threads), +which is already supported by some toolchains, runtimes, and libraries but has been superseded by the new one. (2) The +new proposal [shared-everything-threads](https://github.com/WebAssembly/shared-everything-threads) is still in the +early stages of spec development but is expected to be the future of multi-threading in Wasm. + +Swift currently supports two threading models in Wasm: single-threaded (`wasm32-unknown-wasi`) and multi-threaded +using wasi-threads (`wasm32-unknown-wasip1-threads`). Despite the latter supporting multi-threading, Swift Concurrency defaults +to a cooperative single-threaded executor due to the lack of wasi-threads support in libdispatch. Preparing for the +shared-everything-threads proposal is crucial to ensure that Swift Concurrency can adapt to future multi-threading +standards in Wasm. + +### 64-bit address space + +WebAssembly currently uses a 32-bit address space, but the specification is going to support [64-bit address space](https://github.com/WebAssembly/memory64/). +Swift already has support on 64-bit for other platforms, however, WebAssembly is the first platform where relative reference +from data to code is not allowed. Alternative solutions like image-base relative addressing or small "code model" for fitting +64-bit pointer in 32-bit are unavailable, at least for now. This means that we need cooperation from the WebAssembly toolchain +side or different memory layout in Swift metadata to support 64-bit in WebAssembly. + +### Shared libraries + +There are two approaches to using shared libraries in WebAssembly ecosystem: + +1. [Emscripten-style dynamic linking](https://emscripten.org/docs/compiling/Dynamic-Linking.html) +2. [Component Model-based "ahead-of-time" linking](https://github.com/WebAssembly/component-model/blob/main/design/mvp/Linking.md) +Emscripten-style dynamic linking is a traditional way to use shared libraries in WebAssembly, where the host environment provides +non-standard dynamic loading capabilities. + +Component Model-based AOT linking is a new way that transforms shared library WebAssembly Core modules compiled with +Emscripten's dynamic linking ABI into a single WebAssembly Component at build time. The latter approach cannot fully replace the former, +as it is not able to handle dynamic loading of shared libraries at runtime, but it is more portable way to distribute programs linked +with shared libraries because it does not require the host environment to provide any special capabilities except for Component Model +support. + +From the Swift toolchain perspective, they both use the Emscripten's dynamic linking ABI, so the support for shared libraries in +Swift is mostly about making sure that Swift programs can be compiled in position-independent code mode and linked with shared +libraries by following the dynamic linking ABI. From 67909f0dec64a7c84c70be65fff7db20aae6290c Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 16 Oct 2024 11:41:04 +0100 Subject: [PATCH 3892/4563] Clean up wording and formatting --- visions/webassembly.md | 64 ++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/visions/webassembly.md b/visions/webassembly.md index c1ba36f46c..eee743e3a6 100644 --- a/visions/webassembly.md +++ b/visions/webassembly.md @@ -7,14 +7,15 @@ machine instruction set focused on portability, security, and high performance. developed by [W3C](https://w3.org). An implementation of a WebAssembly virtual machine is usually called a *WebAssembly runtime*. -One prominent spec-compliant implementation of a Wasm runtime in Swift is [WasmKit](https://github.com/swiftwasm/WasmKit). It is available as a Swift package, supports multiple -host platforms, and has a simple API for interaction with guest Wasm modules. +One prominent spec-compliant implementation of a Wasm runtime in Swift is [WasmKit](https://github.com/swiftwasm/WasmKit). +It is available as a Swift package, supports multiple host platforms, and has a simple API for interaction with guest +Wasm modules. An application compiled to a Wasm module can run on any platform that has a Wasm runtime available. Despite its origins -in the browser, it is a general-purpose technology that has use cases in client-side and -server-side applications and services. WebAssembly support in Swift makes the language more appealing in those settings, -and also brings it to the browser where it previously wasn't available at all. It facilitates a broader adoption of -Swift in more environments and contexts. +in the browser, it is a general-purpose technology that has use cases in client-side and server-side applications and +services. WebAssembly support in Swift makes the language more appealing in those settings, and also brings it to the +browser where it previously wasn't available at all. It facilitates a broader adoption of Swift in more environments +and contexts. We can't anticipate every possible application Swift developers are going to create with Wasm, but we can provide a few examples of its possible adoption in the Swift toolchain itself. To quote @@ -25,10 +26,10 @@ examples of its possible adoption in the Swift toolchain itself. To quote This can be applicable not only to Swift macros, but also for the evaluation of SwiftPM manifests and plugins. -The WebAssembly instruction set has useful properties from a security perspective, as it has -no interrupts or peripheral access instructions. Access to the underlying system is always done by calling -explicitly imported functions, implementations for which are provided by an imported WebAssembly module or a WebAssembly -runtime itself. The runtime has full control over interactions of the virtual machine with the outside world. +The WebAssembly instruction set has useful properties from a security perspective, as it has no interrupts or +peripheral access instructions. Access to the underlying system is always done by calling explicitly imported +functions, implementations for which are provided by an imported WebAssembly module or a WebAssembly runtime itself. +The runtime has full control over interactions of the virtual machine with the outside world. WebAssembly code and data live in completely separate address spaces, with all executable code in a given module loaded and validated by the runtime upfront. Combined with the lack of "jump to address" and a limited set of control flow @@ -44,20 +45,21 @@ compiled on a client machine to an optimized native binary ahead of time. With r specification it now supports features such as SIMD, atomics, multi-threading, and more. A WebAssembly runtime can generate a restricted subset of native binary code that implements these features with little performance overhead. -This means that adoption of Wasm in developer tools does not imply unavoidable performance overhead. In fact, with -security guarantees that virtualization brings there's no longer a need to spawn a separate process for each -Swift compiler and SwiftPM plugin/manifest invocation. Virtualized Wasm binaries can run in the host process of a -Wasm runtime, removing the overhead of new process setup and IPC infrastructure. +Adoption of Wasm in developer tools does not imply unavoidable performance overhead. With security guarantees that +virtualization brings, there's no longer a need to spawn a separate process for each Swift compiler and SwiftPM +plugin/manifest invocation. Virtualized Wasm binaries can run in the host process of a Wasm runtime, removing the +overhead of new process setup and IPC infrastructure. ### WebAssembly System Interface and the Component Model -The WebAssembly virtual machine has no in-built support for I/O; instead, a Wasm module's access to I/O is dependent entirely upon the runtime that executes it. +The WebAssembly virtual machine has no in-built support for I/O; instead, a Wasm module's access to I/O is dependent +entirely upon the runtime that executes it. A standardized set of APIs implemented by a Wasm runtime for interaction with the host operating system is called -[WebAssembly System Interface (WASI)](https://wasi.dev). A layer on top of WASI that Swift apps compiled to Wasm can already -use thanks to C interop is [WASI libc](https://github.com/WebAssembly/wasi-libc). In fact, the current implementation of -Swift stdlib and runtime for `wasm32-unknown-wasi` triple is based on this C library. It is important for WASI support in -Swift to be as complete as possible to ensure portability of Swift code in the broader Wasm ecosystem. +[WebAssembly System Interface (WASI)](https://wasi.dev). [WASI libc](https://github.com/WebAssembly/wasi-libc) is a +layer on top of WASI that Swift apps compiled to Wasm can already use thanks to C interop. The current implementation +of Swift stdlib and runtime for `wasm32-unknown-wasi` triple is based on this C library. It is important for WASI +support in Swift to be as complete as possible to ensure portability of Swift code in the broader Wasm ecosystem. In the last few years, the W3C WebAssembly Working Group considered multiple proposals for improving the WebAssembly [type system](https://github.com/webassembly/interface-types) and @@ -73,9 +75,9 @@ The Component Model defines these core concepts: - *Canonical ABI* is an ABI for types defined by WIT and used by component interfaces in the Component Model. Preliminary support for WIT has been implemented in -[the `wit-tool` subcommand](https://github.com/swiftwasm/WasmKit/blob/0.0.3/Sources/WITTool/WITTool.swift) of the WasmKit -CLI. Users of this tool can generate `.wit` files from Swift declarations, and vice versa: Swift bindings from `.wit` -files. +[the `wit-tool` subcommand](https://github.com/swiftwasm/WasmKit/blob/0.0.3/Sources/WITTool/WITTool.swift) of the +WasmKit CLI. Users of this tool can generate `.wit` files from Swift declarations, and vice versa: Swift bindings from +`.wit` files. ## Goals @@ -87,12 +89,12 @@ ecosystem: triples. 2. Allow Swift developers to easily install and build with a Swift SDK for WASI. This requires an implementation -of corresponding build scripts and CI jobs to generate and publish such SDK. Some parts of the Swift toolchain and core -libraries need a Swift SDK for running tests for WASI, so this will benefit the previous point in stabilizing support -for this platform. +of corresponding build scripts and CI jobs to generate and publish the Swift SDK. Some parts of the Swift toolchain and +core libraries need a Swift SDK for running tests for WASI, so this will benefit the previous point in stabilizing +support for this platform. -3. Make it easier to evaluate and adopt Wasm with increased API coverage for this platform in the Swift core libraries. As a -virtualized embeddable platform, not all system APIs are always available or easy to port to WASI. For example, +3. Make it easier to evaluate and adopt Wasm with increased API coverage for this platform in the Swift core libraries. +As a virtualized embeddable platform, not all system APIs are always available or easy to port to WASI. For example, multi-threading, file system access, and localization need special support in Wasm runtimes and a certain amount of consideration from a developer adopting these APIs. @@ -107,7 +109,7 @@ a formal specification for Canonical ABI progressing, this goal will become more ### Proposed Language Features -In our work on Wasm support in Swift we experimented with a few function attributes that could be considered +In our work on Wasm support in Swift, we experimented with a few function attributes that could be considered as pitches and eventually Swift Evolution proposals, if the community is interested in their wider adoption. These attributes allow easier interoperation between Swift code and other Wasm modules linked with it by a Wasm runtime. @@ -131,10 +133,10 @@ this attribute in Swift requires use of C headers as a workaround. ### Debugging Debugging Wasm modules is challenging because Wasm does not expose ways to introspect and control the execution of -the Wasm instance, so a debugger cannot be built on top of Wasm itself. Therefore, special support from the Wasm -execution engine is necessary for debugging. +a Wasm module instance, so a debugger cannot be built on top of Wasm itself. Special support from the Wasm execution +engine is necessary for debugging. -The current state of debugging tools in the Wasm ecosystem is not as mature as other platforms but there are two +The current state of debugging tools in the Wasm ecosystem is not as mature as other platforms, but there are two main directions: 1. LLDB debugger with Wasm runtime supporting GDB Remote Serial Protocol [1] From a88c667196d4ea390d0ecfb71963a55a5c8a5d12 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 16 Oct 2024 12:37:10 +0100 Subject: [PATCH 3893/4563] Address feedback --- visions/webassembly.md | 163 +++++++++++++++++++---------------------- 1 file changed, 75 insertions(+), 88 deletions(-) diff --git a/visions/webassembly.md b/visions/webassembly.md index eee743e3a6..c46c3d7a54 100644 --- a/visions/webassembly.md +++ b/visions/webassembly.md @@ -17,15 +17,6 @@ services. WebAssembly support in Swift makes the language more appealing in thos browser where it previously wasn't available at all. It facilitates a broader adoption of Swift in more environments and contexts. -We can't anticipate every possible application Swift developers are going to create with Wasm, but we can provide a few -examples of its possible adoption in the Swift toolchain itself. To quote -[a GSoC 2024 idea](https://www.swift.org/gsoc2024/#building-swift-macros-with-webassembly): - -> WebAssembly could provide a way to build Swift macros into binaries that can be distributed and run anywhere, -> eliminating the need to rebuild them continually. - -This can be applicable not only to Swift macros, but also for the evaluation of SwiftPM manifests and plugins. - The WebAssembly instruction set has useful properties from a security perspective, as it has no interrupts or peripheral access instructions. Access to the underlying system is always done by calling explicitly imported functions, implementations for which are provided by an imported WebAssembly module or a WebAssembly runtime itself. @@ -36,20 +27,6 @@ and validated by the runtime upfront. Combined with the lack of "jump to address instructions that require explicit labels in the same function body, this makes a certain class of attacks impossible to execute in a correctly implemented spec-compliant WebAssembly runtime. -In the context of Swift developer tools, arbitrary code execution during build time can be virtualized with Wasm. -While Swift macros, SwiftPM manifests, and plugins are sandboxed on Darwin platforms, with Wasm we can provide stronger -security guarantees on other platforms that have a compatible Wasm runtime available. - -The WebAssembly instruction set is designed with performance in mind. A WebAssembly module can be JIT-compiled or -compiled on a client machine to an optimized native binary ahead of time. With recently accepted proposals to the Wasm -specification it now supports features such as SIMD, atomics, multi-threading, and more. A WebAssembly runtime can -generate a restricted subset of native binary code that implements these features with little performance overhead. - -Adoption of Wasm in developer tools does not imply unavoidable performance overhead. With security guarantees that -virtualization brings, there's no longer a need to spawn a separate process for each Swift compiler and SwiftPM -plugin/manifest invocation. Virtualized Wasm binaries can run in the host process of a Wasm runtime, removing the -overhead of new process setup and IPC infrastructure. - ### WebAssembly System Interface and the Component Model The WebAssembly virtual machine has no in-built support for I/O; instead, a Wasm module's access to I/O is dependent @@ -79,33 +56,51 @@ Preliminary support for WIT has been implemented in WasmKit CLI. Users of this tool can generate `.wit` files from Swift declarations, and vice versa: Swift bindings from `.wit` files. +## Use Cases + +We can't anticipate every possible application Swift developers are going to create with Wasm, but we can provide a few +examples of its possible adoption in the Swift toolchain itself. To quote +[a GSoC 2024 idea](https://www.swift.org/gsoc2024/#building-swift-macros-with-webassembly): + +> WebAssembly could provide a way to build Swift macros into binaries that can be distributed and run anywhere, +> eliminating the need to rebuild them continually. + +This can be applicable not only to Swift macros, but also for the evaluation of SwiftPM manifests and plugins. + +In the context of Swift developer tools, arbitrary code execution during build time can be virtualized with Wasm. +While Swift macros, SwiftPM manifests, and plugins are sandboxed on Darwin platforms, with Wasm we can provide stronger +security guarantees on other platforms that have a compatible Wasm runtime available. + +The WebAssembly instruction set is designed with performance in mind. A WebAssembly module can be JIT-compiled or +compiled on a client machine to an optimized native binary ahead of time. With recently accepted proposals to the Wasm +specification it now supports features such as SIMD, atomics, multi-threading, and more. A WebAssembly runtime can +generate a restricted subset of native binary code that implements these features with little performance overhead. + +Adoption of Wasm in developer tools does not imply unavoidable performance overhead. With security guarantees that +virtualization brings, there's no longer a need to spawn a separate process for each Swift compiler and SwiftPM +plugin/manifest invocation. Virtualized Wasm binaries can run in the host process of a Wasm runtime, removing the +overhead of new process setup and IPC infrastructure. + ## Goals As of March 2024 all patches necessary for basic Wasm and WASI support have been merged to the Swift toolchain and core libraries. Based on this, we propose a high-level roadmap for WebAssembly support and adoption in the Swift ecosystem: -1. Ensure that Swift toolchain and core libraries have a regularly running test suite for supported Wasm and WASI -triples. - -2. Allow Swift developers to easily install and build with a Swift SDK for WASI. This requires an implementation -of corresponding build scripts and CI jobs to generate and publish the Swift SDK. Some parts of the Swift toolchain and -core libraries need a Swift SDK for running tests for WASI, so this will benefit the previous point in stabilizing -support for this platform. - -3. Make it easier to evaluate and adopt Wasm with increased API coverage for this platform in the Swift core libraries. +1. Make it easier to evaluate and adopt Wasm with increased API coverage for this platform in the Swift core libraries. As a virtualized embeddable platform, not all system APIs are always available or easy to port to WASI. For example, multi-threading, file system access, and localization need special support in Wasm runtimes and a certain amount of consideration from a developer adopting these APIs. -4. Improve support for cross-compilation in Swift and SwiftPM. We can simplify versioning, installation, and overall +2. Improve support for cross-compilation in Swift and SwiftPM. We can simplify versioning, installation, and overall management of Swift SDKs for cross-compilation in general, which is beneficial not only for WebAssembly, but for all platforms. -5. Continue work on Wasm Component Model support in Swift as the Component Model proposal is stabilized. Ensure -that future versions of WASI are available to Swift developers targeting Wasm. A more ambitious long-term goal to -consider is making interoperability with Wasm components as smooth as C and C++ interop already is for Swift. With -a formal specification for Canonical ABI progressing, this goal will become more achievable with time. +3. Continue work on Wasm Component Model support in Swift as the Component Model proposal is stabilized. Ensure +that future versions of WASI are available to Swift developers targeting Wasm. + +4. Make interoperability with Wasm components as smooth as C and C++ interop already is for Swift. With a formal +specification for Canonical ABI progressing, this will become more achievable with time. ### Proposed Language Features @@ -114,20 +109,6 @@ as pitches and eventually Swift Evolution proposals, if the community is interes These attributes allow easier interoperation between Swift code and other Wasm modules linked with it by a Wasm runtime. -* [`@_extern(wasm)` attribute](https://github.com/apple/swift/pull/69107) that corresponds to Clang's -[`__attribute__((import_name("declaration_name")))`](https://clang.llvm.org/docs/AttributeReference.html#export-name) -and [`__attribute__((import_module("module_name")))`](https://clang.llvm.org/docs/AttributeReference.html#import-module). -This indicates that function's implementation is not provided together with its declaration in Swift and is -available externally, imported by a Wasm runtime during the linking phase. The declaration is added to current Wasm -module's imports section. Without `@_extern(wasm)` developers need to rely on C interop to create a declaration in a C -header using the Clang version of attributes. - -* [`@_expose(wasm)` attribute](https://github.com/apple/swift/pull/68524) that corresponds to Clang's -[`__attribute__((export_name("name")))`](https://clang.llvm.org/docs/AttributeReference.html#export-name), which -is the counterpart of `@_extern(wasm)` working in the opposite direction. This explicitly makes a Swift declaration -available outside of the current module, added to its exports section under a given name. Again, the lack of -this attribute in Swift requires use of C headers as a workaround. - ## Platform-specific Considerations ### Debugging @@ -143,24 +124,25 @@ main directions: 2. Wasm runtime with a built-in debugger [2] The first approach provides an almost equivalent experience to existing debugging workflows on other platforms. It -can utilize LLDB's Swift support, remote metadata inspection and serialized Swift module information. However, since +can utilize LLDB's Swift support, remote metadata inspection, and serialized Swift module information. However, since Wasm is a Harvard architecture and has no way to allocate executable memory space at runtime, implementing expression evaluation with JIT in user space is challenging. In other words, gdb stub in Wasm engines need tricky implementations or need to extend the GDB Remote Serial Protocol. -The second approach embeds the debugger within the Wasm engine. In scenarios where the Wasm engine is embedded as a guest -language in another host language engine (e.g. within a Web Browser), this approach allows seamless debugging experiences -with the host language by integrating with the host debugger. For example, in cases where JavaScript and Wasm call frames +The second approach embeds the debugger within the Wasm engine. In scenarios where the Wasm engine is embedded as a +guest in another host engine (e.g. within a Web Browser), this approach allows seamless debugging experiences with the +host language by integrating with the host debugger. For example, in cases where JavaScript and Wasm call frames are interleaved, the debugger works well in both contexts without switching tools. Debugging tools like Chrome DevTools can use DWARF information embedded in Wasm file to provide debugging support. However, supporting Swift-specific -metadata information and JIT-based expression evaluation will require integrating LLDB's Swift plugin with these debuggers -in some way. +metadata information and JIT-based expression evaluation will require integrating LLDB's Swift plugin with these +debuggers in some way. -Given that both approaches can utilize DWARF information, the Swift toolchain should generate as much DWARF information as possible -for statically known things. Also, making Swift type information DWARF-friendly is desirable because the current debug -information generated by the Swift compiler assumes that debuggers can understand certain Swift primitives (e.g. `Swift.Int` -is currently represented as a `DW_TAG_structure_type` node without any member but ideally such types should be represented -as `DW_TAG_base_type` nodes with `DW_AT_encoding` attribute or provide a debug summary string by LLDB Summary Strings) +Given that both approaches can utilize DWARF information, the Swift toolchain should generate as much DWARF information +as possible for what's statically known. Also, making Swift type information DWARF-friendly is desirable, since the +current debug information generated by the Swift compiler assumes that debuggers can understand certain Swift +primitives. E.g. `Swift.Int` is currently represented as a `DW_TAG_structure_type` node without any member, but ideally +such types should be represented as `DW_TAG_base_type` nodes with `DW_AT_encoding` attribute or provide a debug summary +string by LLDB Summary Strings). [1]: https://github.com/llvm/llvm-project/pull/77949 [2]: https://github.com/ChromeDevTools/devtools-frontend/blob/main/extensions/cxx_debugging @@ -170,40 +152,45 @@ as `DW_TAG_base_type` nodes with `DW_AT_encoding` attribute or provide a debug s WebAssembly has [atomic operations in the instruction set](https://github.com/WebAssembly/threads) (only sequential consistency is supported), but it does not have a built-in way to create threads. Instead, it relies on the host environment to provide multi-threading support. This means that multi-threading in Wasm is dependent on the Wasm runtime -that executes the module. There are two proposals to standardize ways to create threads in Wasm: (1) [wasi-threads](https://github.com/WebAssembly/wasi-threads), -which is already supported by some toolchains, runtimes, and libraries but has been superseded by the new one. (2) The -new proposal [shared-everything-threads](https://github.com/WebAssembly/shared-everything-threads) is still in the -early stages of spec development but is expected to be the future of multi-threading in Wasm. +that executes a module. There are two proposals to standardize ways to create threads in Wasm: + +(1) [wasi-threads](https://github.com/WebAssembly/wasi-threads), which is already supported by some toolchains, +runtimes, and libraries but has been superseded; + +(2) The new [shared-everything-threads](https://github.com/WebAssembly/shared-everything-threads) proposal is still +in the early stages, but is expected to be the future of multi-threading in Wasm. Swift currently supports two threading models in Wasm: single-threaded (`wasm32-unknown-wasi`) and multi-threaded -using wasi-threads (`wasm32-unknown-wasip1-threads`). Despite the latter supporting multi-threading, Swift Concurrency defaults -to a cooperative single-threaded executor due to the lack of wasi-threads support in libdispatch. Preparing for the -shared-everything-threads proposal is crucial to ensure that Swift Concurrency can adapt to future multi-threading -standards in Wasm. +using wasi-threads (`wasm32-unknown-wasip1-threads`). Despite the latter supporting multi-threading, Swift Concurrency +defaults to a cooperative single-threaded executor due to the lack of wasi-threads support in libdispatch. Preparing +for the shared-everything-threads proposal is crucial to ensure that Swift Concurrency can adapt to future +multi-threading standards in Wasm. ### 64-bit address space -WebAssembly currently uses a 32-bit address space, but the specification is going to support [64-bit address space](https://github.com/WebAssembly/memory64/). -Swift already has support on 64-bit for other platforms, however, WebAssembly is the first platform where relative reference -from data to code is not allowed. Alternative solutions like image-base relative addressing or small "code model" for fitting -64-bit pointer in 32-bit are unavailable, at least for now. This means that we need cooperation from the WebAssembly toolchain -side or different memory layout in Swift metadata to support 64-bit in WebAssembly. +WebAssembly currently uses a 32-bit address space, but [64-bit address space](https://github.com/WebAssembly/memory64/) +proposal is already in the implementation phase. + +Swift supports 64-bit pointers on other platforms where available, however WebAssembly is the first platform where +relative reference from data to code is not allowed. Alternative solutions like image-base relative addressing or +"small code model" for fitting 64-bit pointer in 32-bit are unavailable, at least for now. This means that we need +cooperation from the WebAssembly toolchain side or different memory layout in Swift metadata to support 64-bit linear +memory support in WebAssembly. ### Shared libraries -There are two approaches to using shared libraries in WebAssembly ecosystem: +There are two approaches to using shared libraries in the WebAssembly ecosystem: 1. [Emscripten-style dynamic linking](https://emscripten.org/docs/compiling/Dynamic-Linking.html) 2. [Component Model-based "ahead-of-time" linking](https://github.com/WebAssembly/component-model/blob/main/design/mvp/Linking.md) -Emscripten-style dynamic linking is a traditional way to use shared libraries in WebAssembly, where the host environment provides -non-standard dynamic loading capabilities. - -Component Model-based AOT linking is a new way that transforms shared library WebAssembly Core modules compiled with -Emscripten's dynamic linking ABI into a single WebAssembly Component at build time. The latter approach cannot fully replace the former, -as it is not able to handle dynamic loading of shared libraries at runtime, but it is more portable way to distribute programs linked -with shared libraries because it does not require the host environment to provide any special capabilities except for Component Model -support. - -From the Swift toolchain perspective, they both use the Emscripten's dynamic linking ABI, so the support for shared libraries in -Swift is mostly about making sure that Swift programs can be compiled in position-independent code mode and linked with shared -libraries by following the dynamic linking ABI. + +Emscripten-style dynamic linking is a traditional way to use shared libraries in WebAssembly, where the host +environment provides non-standard dynamic loading capabilities. + +The latter approach cannot fully replace the former, as it is unable to handle dynamic loading of shared libraries at +runtime, but it is more portable way to distribute programs linked with shared libraries, as it does not require the +host environment to provide any special capabilities except for Component Model support. + +Support for shared libraries in Swift for Wasm is mostly about making sure that Swift programs can be compiled in +position-independent code mode and linked with shared libraries by following the corresponding dynamic linking ABI. + From ce025340fb7c46bde7ef4c95fd1f6c4bd4dcb234 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 18 Oct 2024 02:26:13 +0100 Subject: [PATCH 3894/4563] [SE-0445] Update status (implemented in Swift 6.1) (#2592) --- proposals/0445-string-index-printing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0445-string-index-printing.md b/proposals/0445-string-index-printing.md index f59a4b9f18..f1c2beaa89 100644 --- a/proposals/0445-string-index-printing.md +++ b/proposals/0445-string-index-printing.md @@ -3,7 +3,7 @@ * Proposal: [SE-0445](0445-string-index-printing.md) * Authors: [Karoy Lorentey](https://github.com/lorentey) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Implemented** (main development branch) +* Status: **Implemented (Swift 6.1)** * Implementation: [apple/swift#75433](https://github.com/swiftlang/swift/pull/75433) * Review: ([pitch](https://forums.swift.org/t/improving-string-index-s-printed-descriptions/57027)) ([review](https://forums.swift.org/t/se-0445-improving-string-indexs-printed-descriptions/74643)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0445-improving-string-index-s-printed-descriptions/75108)) * Previous Revision: [v1](https://github.com/swiftlang/swift-evolution/blob/682f7c293a3a05bff3e619c3b479bfb68541fb6e/proposals/0445-string-index-printing.md) From 1180a657213b0566158a139a0a1b26eaa43614c6 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 18 Oct 2024 18:22:14 +0100 Subject: [PATCH 3895/4563] [SE-0442] Update status (implemented in Swift 6.1) (#2593) --- .../0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md b/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md index 1abe4d92f3..546fc01099 100644 --- a/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md +++ b/proposals/0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md @@ -3,7 +3,7 @@ * Proposal: [SE-0442](0442-allow-taskgroup-childtaskresult-type-to-be-inferred.md) * Author: [Richard L Zarth III](https://github.com/rlziii) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Implemented (Swift Next)** +* Status: **Implemented (Swift 6.1)** * Implementation: [apple/swift#74517](https://github.com/apple/swift/pull/74517) * Review: ([pitch](https://forums.swift.org/t/allow-taskgroups-childtaskresult-type-to-be-inferred/72175))([review](https://forums.swift.org/t/se-0442-allow-taskgroups-childtaskresult-type-to-be-inferred/73397))([acceptance](https://forums.swift.org/t/accepted-se-0422-allow-taskgroups-childtaskresult-type-to-be-inferred/73747)) From bdb296763abe6dc6711cda484a2fbc0977d20c0c Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 18 Oct 2024 18:22:43 +0100 Subject: [PATCH 3896/4563] Update README.md with Swift 6.1 announcement (#2591) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a3618ded52..b075c548a0 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ This repository tracks the ongoing evolution of the Swift programming language, | Version | Announced | Released | | :-------- | :----------------------------------------------------------------------- | :----------------------------------------------------------- | +| Swift 6.1 | [2024-10-17](https://forums.swift.org/t/swift-6-1-release-process/75442) | | Swift 6.0 | [2024-02-22](https://forums.swift.org/t/swift-6-0-release-process/70220) | [2024-09-17](https://www.swift.org/blog/announcing-swift-6/) | | Swift 5.10 | [2023-08-23](https://forums.swift.org/t/swift-5-10-release-process/66911) | [2024-03-05](https://www.swift.org/blog/swift-5.10-released/) | | Swift 5.9 | [2023-03-06](https://forums.swift.org/t/swift-5-9-release-process/63557) | [2023-09-18](https://www.swift.org/blog/swift-5.9-released/) | From d86880bd160f5fc958192fc967a24f110a00bb91 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 21 Oct 2024 10:08:48 -0700 Subject: [PATCH 3897/4563] Accept SE-0447 "Span: Safe Access to Contiguous Storage" (#2595) --- proposals/0447-span-access-shared-contiguous-storage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0447-span-access-shared-contiguous-storage.md b/proposals/0447-span-access-shared-contiguous-storage.md index 8d687de488..a196781226 100644 --- a/proposals/0447-span-access-shared-contiguous-storage.md +++ b/proposals/0447-span-access-shared-contiguous-storage.md @@ -3,11 +3,11 @@ * Proposal: [SE-0447](0447-span-access-shared-contiguous-storage.md) * Authors: [Guillaume Lessard](https://github.com/glessard), [Michael Ilseman](https://github.com/milseman), [Andrew Trick](https://github.com/atrick) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active review (September 17...October 15, 2024)** +* Status: **Accepted** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Bug: rdar://48132971, rdar://96837923 * Implementation: https://github.com/swiftlang/swift/pull/76406 -* Review: ([pitch 1](https://forums.swift.org/t/69888))([pitch 2](https://forums.swift.org/t/72745))([review](https://forums.swift.org/t/se-0447-span-safe-access-to-contiguous-storage/74676)) +* Review: ([pitch 1](https://forums.swift.org/t/69888))([pitch 2](https://forums.swift.org/t/72745))([review](https://forums.swift.org/t/se-0447-span-safe-access-to-contiguous-storage/74676))([acceptance](https://forums.swift.org/t/accepted-se-0447-span-safe-access-to-contiguous-storage/75508)) ## Introduction From 3c471fbdee8dd4357e28969bec93808580cac883 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 21 Oct 2024 21:06:22 +0100 Subject: [PATCH 3898/4563] Mark SE-0387 as implemented in 6.1 (#2594) With https://github.com/swiftlang/swift-package-manager/pull/8051 merged, the proposal is fully implemented. --- proposals/0387-cross-compilation-destinations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0387-cross-compilation-destinations.md b/proposals/0387-cross-compilation-destinations.md index ffb1538614..40367a6464 100644 --- a/proposals/0387-cross-compilation-destinations.md +++ b/proposals/0387-cross-compilation-destinations.md @@ -3,7 +3,7 @@ * Proposal: [SE-0387](0387-cross-compilation-destinations.md) * Authors: [Max Desiatov](https://github.com/MaxDesiatov), [Saleem Abdulrasool](https://github.com/compnerd), [Evan Wilde](https://github.com/etcwilde) * Review Manager: [Mishal Shah](https://github.com/shahmishal) -* Status: **Accepted** +* Status: **Implemented (Swift 6.1)** * Implementation: [apple/swift-package-manager#5911](https://github.com/apple/swift-package-manager/pull/5911), [apple/swift-package-manager#5922](https://github.com/apple/swift-package-manager/pull/5922), [apple/swift-package-manager#6023](https://github.com/apple/swift-package-manager/pull/6023), From fc7bd22e3e9779ea959bfd2e65afedf3ee49fdce Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 22 Oct 2024 15:38:42 -0400 Subject: [PATCH 3899/4563] Accept SE-0449. --- proposals/0449-nonisolated-for-global-actor-cutoff.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0449-nonisolated-for-global-actor-cutoff.md b/proposals/0449-nonisolated-for-global-actor-cutoff.md index f7e7260d8a..f4de423806 100644 --- a/proposals/0449-nonisolated-for-global-actor-cutoff.md +++ b/proposals/0449-nonisolated-for-global-actor-cutoff.md @@ -3,9 +3,9 @@ * Proposal: [SE-0449](0449-nonisolated-for-global-actor-cutoff.md) * Authors: [Sima Nerush](https://github.com/simanerush), [Holly Borla](https://github.com/hborla) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Active Review (October 2...16, 2024)** +* Status: **Accepted** * Implementation: On `main` gated behind `-enable-experimental-feature GlobalActorInferenceCutoff` -* Review: ([pitch](https://forums.swift.org/t/pitch-allow-nonisolated-to-prevent-global-actor-inference/74502)) ([review](https://forums.swift.org/t/se-0449-allow-nonisolated-to-prevent-global-actor-inference/75116)) +* Review: ([pitch](https://forums.swift.org/t/pitch-allow-nonisolated-to-prevent-global-actor-inference/74502)) ([review](https://forums.swift.org/t/se-0449-allow-nonisolated-to-prevent-global-actor-inference/75116)) ([acceptance](https://forums.swift.org/t/accepted-se-0449-allow-nonisolated-to-prevent-global-actor-interference/75539)) ## Introduction From a76d73d91c29a8f2aa51e3e468a40f86f79aa5c9 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 22 Oct 2024 19:43:01 -0400 Subject: [PATCH 3900/4563] Forbid backslashes and identifiers consisting only of whitespace. --- proposals/NNNN-escaped-identifiers.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/proposals/NNNN-escaped-identifiers.md b/proposals/NNNN-escaped-identifiers.md index 3d6592d7cb..1799bbafe8 100644 --- a/proposals/NNNN-escaped-identifiers.md +++ b/proposals/NNNN-escaped-identifiers.md @@ -206,12 +206,17 @@ In [Zig](https://ziglang.org/documentation/master/#Identifiers), an identifier t A raw identifier may contain any valid Unicode characters except for the following: * The backtick (`` ` ``) itself, which termintes the identifier. +* The backslash (`\`), which is reserved for potential future escape sequences. * Carriage return (`U+000D`) or newline (`U+000A`); identifiers must be written on a single line. * The NUL character (`U+0000`), which already emits a warning if present in Swift source but would be disallowed completely in a raw identifier. * All other non-printable ASCII code units that are also forbidden in single-line Swift string literals (`U+0001...U+001F`, `U+007F`). In addition to these rules, some specific combinations that require special handling are discussed below. +#### Whitespace + +A raw identifier may have leading, trailing, or internal whitespace; however, it may not consist of *only* whitespace. "Whitespace" is defined here to mean characters satisfying the Unicode `White_Space` property, exposed in Swift by `Unicode.Scalar.Properties.isWhitespace`. + #### Operator characters A raw identifier may start with, contain, or end with operator characters, but it may not contain **only** operator characters. To avoid confusion, a raw identifier containing only operator characters is treated as a parsing error: it is neither a valid identifier nor an operator: @@ -438,6 +443,10 @@ let fifteen = ``` At this time, however, we do not believe there are any compelling use cases for such identifiers. +### Escape sequences inside raw identifiers + +Raw identifiers follow similar parsing rules as string literals with respect to unprintable characters, which raises the question of how to handle backslashes. The use cases served by many backslash escapes—such as writing unprintable characters—are not desirable for identifiers, so we could choose to treat backslashes as regular literal characters. For example, `` `hello\now` `` would mean the identifier `hello\now`. This could be confusing for users though, who might expect the `\n` to be interpreted the same way that it would be in a string literal. Treating backslashes as literal characters today would also close the door on a viable method of escaping characters inside raw identifiers if we decide that it is needed later. For these reasons, we currently forbid backslashes and leave their purpose to be defined in the future. + ## Source compatibility This proposal is purely additive; it does not affect compatibility with existing source code. From 3aa2bc4db8a42d560199e47bfcec38c7a23d15d2 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Thu, 24 Oct 2024 09:53:22 -0700 Subject: [PATCH 3901/4563] Start the review for 0450-swiftpm-package-traits.md --- ...-package-traits.md => 0450-swiftpm-package-traits.md} | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) rename proposals/{NNNN-swiftpm-package-traits.md => 0450-swiftpm-package-traits.md} (98%) diff --git a/proposals/NNNN-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md similarity index 98% rename from proposals/NNNN-swiftpm-package-traits.md rename to proposals/0450-swiftpm-package-traits.md index 8b88460ae8..0a1da605fa 100644 --- a/proposals/NNNN-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -1,9 +1,10 @@ # Package traits -* Proposal: [SE-NNNN](NNNN-swiftpm-package-traits.md) +* Proposal: [SE-0450](0450-swiftpm-package-traits.md) * Authors: [Franz Busch](https://github.com/FranzBusch), [Max Desiatov](https://github.com/MaxDesiatov) -* Review Manager: TBD -* Status: **Experimental implementation available, gated on `@_spi(ExperimentalTraits)` in package manifests and `--experimental` prefix for CLI options** +* Review Manager: [Mishal Shah](https://github.com/shahmishal) +* Status: **Active Review (October 24th...November 7th, 2024**) +* Experimental Implementation: Gated on `@_spi(ExperimentalTraits)` in package manifests and `--experimental` prefix for CLI options - https://github.com/swiftlang/swift-package-manager/pull/7704 - https://github.com/swiftlang/swift-package-manager/pull/7703 @@ -669,4 +670,4 @@ and conditional compilation. - [Gradle](https://gradle.org/) has [feature variants](https://docs.gradle.org/current/userguide/feature_variants.html) that allow conditional compilation and optional dependencies. - [Go](https://golang.org/) has [build constraints](https://golang.org/pkg/go/build/#hdr-Build_Constraints) which can conditionally include a file. - [pip](https://pypi.org/project/pip/) dependencies can have [optional dependencies and extras](https://setuptools.pypa.io/en/latest/userguide/dependency_management.html#optional-dependencies). -- [Hatch](https://hatch.pypa.io/latest/) offers [optional dependencies](https://hatch.pypa.io/latest/config/metadata/#optional) and [features](https://hatch.pypa.io/latest/config/dependency/#features). \ No newline at end of file +- [Hatch](https://hatch.pypa.io/latest/) offers [optional dependencies](https://hatch.pypa.io/latest/config/metadata/#optional) and [features](https://hatch.pypa.io/latest/config/dependency/#features). From 58b0d3c8ebdd439f6de631610d7daf4fd80f9e47 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Thu, 24 Oct 2024 11:18:12 -0700 Subject: [PATCH 3902/4563] Update 0450-swiftpm-package-traits.md to include review links --- proposals/0450-swiftpm-package-traits.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index 0a1da605fa..0738f07956 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -5,6 +5,7 @@ * Review Manager: [Mishal Shah](https://github.com/shahmishal) * Status: **Active Review (October 24th...November 7th, 2024**) * Experimental Implementation: Gated on `@_spi(ExperimentalTraits)` in package manifests and `--experimental` prefix for CLI options +* Review: ([pitch](https://forums.swift.org/t/pitch-package-traits/72191)) ([review](https://forums.swift.org/t/se-0450-package-traits/75598)) - https://github.com/swiftlang/swift-package-manager/pull/7704 - https://github.com/swiftlang/swift-package-manager/pull/7703 From edb51f4d1c8453bdac713af99a068193553063b4 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Thu, 24 Oct 2024 11:20:51 -0700 Subject: [PATCH 3903/4563] Clean up the implementation links for 0450-swiftpm-package-traits.md --- proposals/0450-swiftpm-package-traits.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index 0738f07956..5bf2d9f9b7 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -4,16 +4,10 @@ * Authors: [Franz Busch](https://github.com/FranzBusch), [Max Desiatov](https://github.com/MaxDesiatov) * Review Manager: [Mishal Shah](https://github.com/shahmishal) * Status: **Active Review (October 24th...November 7th, 2024**) +* Implementation: https://github.com/swiftlang/swift-package-manager/pull/7704, https://github.com/swiftlang/swift-package-manager/pull/7703, https://github.com/swiftlang/swift-package-manager/pull/7702, https://github.com/swiftlang/swift-package-manager/pull/7701, https://github.com/swiftlang/swift-package-manager/pull/7694, https://github.com/swiftlang/swift-package-manager/pull/7689 * Experimental Implementation: Gated on `@_spi(ExperimentalTraits)` in package manifests and `--experimental` prefix for CLI options * Review: ([pitch](https://forums.swift.org/t/pitch-package-traits/72191)) ([review](https://forums.swift.org/t/se-0450-package-traits/75598)) -- https://github.com/swiftlang/swift-package-manager/pull/7704 -- https://github.com/swiftlang/swift-package-manager/pull/7703 -- https://github.com/swiftlang/swift-package-manager/pull/7702 -- https://github.com/swiftlang/swift-package-manager/pull/7701 -- https://github.com/swiftlang/swift-package-manager/pull/7694 -- https://github.com/swiftlang/swift-package-manager/pull/7689 - ## Introduction Over the past years the package ecosystem has grown tremendously in both the From 376fdc4e3c212066955bbde08c79d12f2997aaf6 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Thu, 24 Oct 2024 12:19:10 -0700 Subject: [PATCH 3904/4563] Christen as SE-0451 and begin review --- ...N-escaped-identifiers.md => 0451-escaped-identifiers.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-escaped-identifiers.md => 0451-escaped-identifiers.md} (99%) diff --git a/proposals/NNNN-escaped-identifiers.md b/proposals/0451-escaped-identifiers.md similarity index 99% rename from proposals/NNNN-escaped-identifiers.md rename to proposals/0451-escaped-identifiers.md index 1799bbafe8..6036d832c2 100644 --- a/proposals/NNNN-escaped-identifiers.md +++ b/proposals/0451-escaped-identifiers.md @@ -1,9 +1,9 @@ # Raw identifiers -* Proposal: [SE-NNNN](NNNN-escaped-identifiers.md) +* Proposal: [SE-0451](0451-escaped-identifiers.md) * Author: [Tony Allevato](https://github.com/allevato); based on [SE-0275](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md) by [Alfredo Delli Bovi](https://github.com/adellibovi) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Joe Groff](https://github.com/jckarter) +* Status: **Active review (October 24...November 7, 2024)** * Implementation: [swiftlang/swift#76636](https://github.com/swiftlang/swift/pull/76636), [swiftlang/swift-syntax#2857](https://github.com/swiftlang/swift-syntax/pull/2857) * Review: ([pitch](https://forums.swift.org/t/pitch-revisiting-backtick-delimited-identifiers-that-allow-more-non-identifier-characters/74432)) From e841443a05a3d7c1e6b3376a782a0d931964dac0 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 24 Oct 2024 12:59:14 -0700 Subject: [PATCH 3905/4563] Fix typo --- proposals/0451-escaped-identifiers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0451-escaped-identifiers.md b/proposals/0451-escaped-identifiers.md index 6036d832c2..cacf8dfcf1 100644 --- a/proposals/0451-escaped-identifiers.md +++ b/proposals/0451-escaped-identifiers.md @@ -205,7 +205,7 @@ In [Zig](https://ziglang.org/documentation/master/#Identifiers), an identifier t A raw identifier may contain any valid Unicode characters except for the following: -* The backtick (`` ` ``) itself, which termintes the identifier. +* The backtick (`` ` ``) itself, which terminates the identifier. * The backslash (`\`), which is reserved for potential future escape sequences. * Carriage return (`U+000D`) or newline (`U+000A`); identifiers must be written on a single line. * The NUL character (`U+0000`), which already emits a warning if present in Swift source but would be disallowed completely in a raw identifier. From bd5447b73b05a0fec974f79ee4b25b4f2cea5d55 Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Wed, 23 Oct 2024 16:26:57 -0700 Subject: [PATCH 3906/4563] Mark SE-0449 as implemented in Swift 6.1. --- proposals/0449-nonisolated-for-global-actor-cutoff.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0449-nonisolated-for-global-actor-cutoff.md b/proposals/0449-nonisolated-for-global-actor-cutoff.md index f4de423806..92d4bf5217 100644 --- a/proposals/0449-nonisolated-for-global-actor-cutoff.md +++ b/proposals/0449-nonisolated-for-global-actor-cutoff.md @@ -3,8 +3,7 @@ * Proposal: [SE-0449](0449-nonisolated-for-global-actor-cutoff.md) * Authors: [Sima Nerush](https://github.com/simanerush), [Holly Borla](https://github.com/hborla) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Accepted** -* Implementation: On `main` gated behind `-enable-experimental-feature GlobalActorInferenceCutoff` +* Status: **Implemented (Swift 6.1)** * Review: ([pitch](https://forums.swift.org/t/pitch-allow-nonisolated-to-prevent-global-actor-inference/74502)) ([review](https://forums.swift.org/t/se-0449-allow-nonisolated-to-prevent-global-actor-inference/75116)) ([acceptance](https://forums.swift.org/t/accepted-se-0449-allow-nonisolated-to-prevent-global-actor-interference/75539)) ## Introduction From 93575ecac1d292970a389a144d8fc9187d94259c Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Thu, 24 Oct 2024 21:39:36 -0700 Subject: [PATCH 3907/4563] Fix misformatted Status field (#2600) --- proposals/0450-swiftpm-package-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index 5bf2d9f9b7..3f755a4812 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -3,7 +3,7 @@ * Proposal: [SE-0450](0450-swiftpm-package-traits.md) * Authors: [Franz Busch](https://github.com/FranzBusch), [Max Desiatov](https://github.com/MaxDesiatov) * Review Manager: [Mishal Shah](https://github.com/shahmishal) -* Status: **Active Review (October 24th...November 7th, 2024**) +* Status: **Active review (October 24th...November 7th, 2024)** * Implementation: https://github.com/swiftlang/swift-package-manager/pull/7704, https://github.com/swiftlang/swift-package-manager/pull/7703, https://github.com/swiftlang/swift-package-manager/pull/7702, https://github.com/swiftlang/swift-package-manager/pull/7701, https://github.com/swiftlang/swift-package-manager/pull/7694, https://github.com/swiftlang/swift-package-manager/pull/7689 * Experimental Implementation: Gated on `@_spi(ExperimentalTraits)` in package manifests and `--experimental` prefix for CLI options * Review: ([pitch](https://forums.swift.org/t/pitch-package-traits/72191)) ([review](https://forums.swift.org/t/se-0450-package-traits/75598)) From 38745d0fc5124013cc37a906d978e7ab62c1a743 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 25 Oct 2024 12:49:02 +0100 Subject: [PATCH 3908/4563] Fix typo in `0450-swiftpm-package-traits.md` `mutuall exclusive` -> `mutually exclusive` --- proposals/0450-swiftpm-package-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index 3f755a4812..d8079525eb 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -457,7 +457,7 @@ compiler error to detect this during build time. ```swift #if Trait1 && Trait2 -#error("Trait1 and Trait2 are mutuall exclusive") +#error("Trait1 and Trait2 are mutually exclusive") #endif ``` From e705781127b22fe81fbe6adfff98f447370ad0cb Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 29 Oct 2024 13:23:46 +0900 Subject: [PATCH 3909/4563] SE0450: Mention about `swift test` `swift test` was missed in the list of commands accepting `--traits` --- proposals/0450-swiftpm-package-traits.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index 3f755a4812..c9482adb99 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -474,9 +474,9 @@ default traits or adding a new default trait it is important to consider that removing a default trait is a **SemVer-incompatible** change since it can potentially remove APIs. -### Trait specific command line options for `swift build/run` +### Trait specific command line options for `swift test/build/run` -When executing one of `swift build/run` options can be passed to control which +When executing one of `swift test/build/run` options can be passed to control which traits for the root package are enabled: - `--traits` _TRAITS_: Enables the passed traits of the package. Multiple traits From 225d14f0fa5773b3de3a0eea72cd8ae0b7dfdd82 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Tue, 29 Oct 2024 13:26:43 +0900 Subject: [PATCH 3910/4563] SE0450: Replace no-break space with regular space just for markdown formatting --- proposals/0450-swiftpm-package-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index c9482adb99..6f1095c0ee 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -614,7 +614,7 @@ it would be helpful to offer package authors tooling to build and test all combinations. A new option `--all-trait-combinations` could be added to `swift test/build/run` make testing all combinations easy as possible. -### Surface traits in documentation +### Surface traits in documentation If the compiler gains knowledge about package traits in the future, we could extract information if a public API is guarded by a trait and surface this in From c838b6a5faade93f2be290c6c9f4c69fb0a918bf Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 30 Oct 2024 10:19:20 -0400 Subject: [PATCH 3911/4563] Fix typo: `case ColorVariant` -> `enum ColorVariant` --- proposals/0451-escaped-identifiers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0451-escaped-identifiers.md b/proposals/0451-escaped-identifiers.md index 6036d832c2..e04b4fa551 100644 --- a/proposals/0451-escaped-identifiers.md +++ b/proposals/0451-escaped-identifiers.md @@ -156,7 +156,7 @@ Using raw identifiers for test naming fits very well with Swift's philosophy of Raw identifiers would provide a clean solution to the cases where the most natural name for an identifier is numeric. Considering the color design system again, we could write: ```swift -case ColorVariant { +enum ColorVariant { case `50` case `100` case `200` From 33e4eb55141d4e73adfcc9d8a929e899e71638d4 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 31 Oct 2024 15:14:16 +0000 Subject: [PATCH 3912/4563] Apply suggestions from code review Co-authored-by: Danielle --- visions/webassembly.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/visions/webassembly.md b/visions/webassembly.md index c46c3d7a54..565b79c608 100644 --- a/visions/webassembly.md +++ b/visions/webassembly.md @@ -83,13 +83,13 @@ overhead of new process setup and IPC infrastructure. ## Goals -As of March 2024 all patches necessary for basic Wasm and WASI support have been merged to the Swift toolchain and +As of March 2024 all patches necessary for basic Wasm and WASI Preview 1 support have been merged to the Swift toolchain and core libraries. Based on this, we propose a high-level roadmap for WebAssembly support and adoption in the Swift ecosystem: 1. Make it easier to evaluate and adopt Wasm with increased API coverage for this platform in the Swift core libraries. As a virtualized embeddable platform, not all system APIs are always available or easy to port to WASI. For example, -multi-threading, file system access, and localization need special support in Wasm runtimes and a certain amount of +multi-threading, file system access, networking and localization need special support in Wasm runtimes and a certain amount of consideration from a developer adopting these APIs. 2. Improve support for cross-compilation in Swift and SwiftPM. We can simplify versioning, installation, and overall @@ -100,7 +100,7 @@ platforms. that future versions of WASI are available to Swift developers targeting Wasm. 4. Make interoperability with Wasm components as smooth as C and C++ interop already is for Swift. With a formal -specification for Canonical ABI progressing, this will become more achievable with time. +specification for Canonical ABI progressing, this will become more achievable with time. This includes consuming components from, and building components with Swift. ### Proposed Language Features From 510f2c8928b8ac32c35b670f6f446c2f0bd74826 Mon Sep 17 00:00:00 2001 From: "Mykola (Nickolas) Pokhylets" Date: Tue, 5 Nov 2024 13:08:36 +0100 Subject: [PATCH 3913/4563] Updated description of behavior of the task-locals, according to modifications requested during review (#2605) --- proposals/0371-isolated-synchronous-deinit.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/proposals/0371-isolated-synchronous-deinit.md b/proposals/0371-isolated-synchronous-deinit.md index 06341e5aef..ed081a409e 100644 --- a/proposals/0371-isolated-synchronous-deinit.md +++ b/proposals/0371-isolated-synchronous-deinit.md @@ -326,9 +326,16 @@ Ad-hoc job created for isolated synchronous deinit is executed outside a task, s ### Task-local values -This proposal does not define how Swift runtime should behave when running isolated deinit. It may use task-local values as seen at the point of the last release, reset them to default values, or use some other set of values. Behavior is allowed to change without notice. But future proposals may change specification by defining a specific behavior. +Task-local values set by the task/thread that performed last release are blocked inside isolated deinit. +Attempting to read such task-local inside isolated deinit will return a default value without any runtime warnings. +Task-local values set inside the body of the isolated deinit are visible for the corresponding scope. -Client code should not depend on behavior of particular implementation of the Swift runtime. Inside isolated `deinit` it is safe to read only the task-local values that were also set inside the `deinit`. +This behavior ensures that isolated deinit behaves the same way both when running inline and when hopping, without high runtime costs for copying task-local values. + +The point of the last release of the object can be hard to predict and can be changed by optimizations, leading to different behavior between debug and release builds. +Because of this, developers are discouraged from depending on the set of task-local values available at the point of the last release. +Instead of using task-local values, developers are advised to inject dependencies into deinit using the object's stored properties. +This advice applies to non-isolated deinit as well, but this proposal does not change the behavior of the non-isolated deinit. Note that any existing hopping in overridden `retain`/`release` for UIKit classes is unlikely to be aware of task-local values. @@ -513,6 +520,10 @@ a.enqueue { aIsolated in This comes with a performance cost, which is unlikely to be beneficial to most of the users. Leaving behavior of the task-locals undefined allows to potentially change it in the future, after getting more feedback from the users. +### Keeping behavior of task-local values undefined + +Approach of 'make no promises' is likely to result in users inadvertently relying on implementation details which would turn out to be difficult to change later. + ### Implicitly propagate isolation to synchronous `deinit`. This would be a source-breaking change. From d75b909d4ce04c9e7d36abc61ee4e167df378923 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 5 Nov 2024 10:35:18 -0800 Subject: [PATCH 3914/4563] Proposal: Integer Generic Parameters (#2553) * Proposal: Integer Generic Parameters * typo * Updates based on pitch and LSG feedback * Assign review number, kick off review --------- Co-authored-by: Ben Cohen --- proposals/ABCD-integer-generic-parameters.md | 574 +++++++++++++++++++ 1 file changed, 574 insertions(+) create mode 100644 proposals/ABCD-integer-generic-parameters.md diff --git a/proposals/ABCD-integer-generic-parameters.md b/proposals/ABCD-integer-generic-parameters.md new file mode 100644 index 0000000000..4a4716c1e5 --- /dev/null +++ b/proposals/ABCD-integer-generic-parameters.md @@ -0,0 +1,574 @@ +# Integer Generic Parameters + +* Proposal: [SE-0452](0452-integer-generic-parameters.md) +* Authors: [Alejandro Alonso](https://github.com/Azoy), [Joe Groff](https://github.com/jckarter) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Active Review (Nov 5-19 2024)** +* Implementation: [apple/swift#75518](https://github.com/apple/swift/pull/75518) +* Upcoming Feature Flag: `ValueGenerics` + +## Introduction + +In this proposal, we introduce the ability to parameterize generic types +on literal integer parameters. + +## Motivation + +Swift does not currently support fixed-size or fixed-capacity collections +with inline storage. (Or at least, it doesn't do so *well*, not without +forming a struct with some specific number of elements and doing horrible +things with `withUnsafePointer` to handle indexing.) Most of the implementation +of something like a fixed-size array, or a fixed-capacity growable array with +a maximum size, or a hash table with a fixed number of buckets, is agnostic +to any specific size or capacity, so that implementation +would ideally be generic over size so that a library implementation can +be reused for any given size. + +Beyond inline storage sizes, there are other use cases for carrying integers +in type information, such as to represent an operation with a particular +input or output size. Carrying this information in types can allow for APIs +with stronger static guarantees that chains of operations match in the +number of elements they consume or produce. + +## Proposed solution + +Generic types can now be parameterized by integer parameters, declared using +the syntax `let : Int` inside of the generic parameter angle brackets: + +```swift +struct Vector { + /*implementation TBD*/ +} +``` + +A generic type with integer parameters can be instantiated using literal +integer arguments: + +```swift +struct Matrix4x4 { + var matrix: Vector<4, Vector<4, Double>> +} +``` + +Or it can be instantiated using integer generic parameters from the surrounding +generic environment: + +```swift +struct Matrix { + var matrix: Vector> +} +``` + +Generic functions and methods can also be parameterized by integer generic +parameters. As with other generic parameters, the values of the generic +arguments for a call are inferred from the types of the argument values +provided to the call: + +```swift +func matmul( + _ l: Matrix, + _ r: Matrix +) -> Matrix { ... } + +let m1 = Matrix<4, 2>(...) +let m2 = Matrix<2, 5>(...) + +let m3 = matmul(m1, m2) // a = 4, b = 2, c = 5, result type is Matrix<4, 5> +``` + +Within an expression, a reference to an integer generic parameter evaluates +the parameter as a value of type `Int`: + +```swift +extension Vector { + subscript(i: Int) -> Element { + get { + if i < 0 || i >= count { + fatalError("index \(i) out of bounds [0, \(count))") + } + return element(i) + } + } +} +``` + +## Detailed design + +The grammar for generic parameter lists expands to include value generic +parameters: + +```swift +generic-parameter --> 'let' type-name ':' type +``` + +Correspondingly, signed integer literals can now appear as elements in +generic argument lists and as operands of generic requirements: + +```swift +generic-argument --> '-'? integer-literal +same-type-requirement --> type-identifier '==' '-'? integer-literal +``` + +Although they can appear as elements in generic parameter lists, integer +literals are still not allowed to appear as types in and of themselves, and +cannot be used as bindings for type generic parameters. + +```swift +let x: 2 // error, 2 is not a type +let y: Array<2> // error, Array's Element is a type generic parameter +``` + +Likewise, integer generic parameters cannot be used as standalone types in their +generic context. + +```swift +struct Foo { + let y: x // Error, x is not a type + let metax: x.Type // Error, x has no member `.Type` +} +``` + +The type referenced by a value generic parameter declaration must resolve to +the `Swift.Int` standard library type. (Allowing other types of value generic +parameter is a future direction.) + +```swift +struct Foo { } // OK (assuming no shadowing `Int` declaration) +struct Foo2 { } // also OK + +struct BadFoo { } // Error, generic parameters of type Float not supported + +typealias MyInt = Swift.Int +struct Bar { } // OK + +struct Baz: P { + typealias A = Int +} + +struct Zim { } // OK + +func contrived() { + struct Int { } + + struct BadFoo { } // Error, local Int not supported + + struct Foo { } // OK +} +``` + +In a type reference, an integer generic argument can be provided as either +a literal integer, or as a reference to an integer generic parameter from +the enclosing generic context. References to type generic parameters, +type generic parameter packs, or declarations other than integer generic +parameters is an error. (Allowing references to constants of integer type, +or more elaborate constant expressions, as generic parameters is a future +direction.) + +```swift +struct IntParam { } + +let a: IntParam<2> // OK +let b: IntParam<-2> // OK + +struct AlsoIntParam { + let c: IntParam // OK + + static let someIntegerConstant = 42 + let d: IntParam // Error, not an Int generic parameter + + let e: IntParam // Error, is a type generic parameter + let f: IntParam // Error, is a pack generic parameter +} +``` + +Conversely, using an integer generic parameter as an argument for a type +generic parameter is also an error. + +```swift +struct IntAndTypeParam { + let xs: Array // Error, x is an integer type parameter +} +``` + +An integer generic parameter can be constrained to be equal to a specific +literal value using a same-value constraint, spelled with `==` as for a +same-type constraint. Two integer generic parameters can also be constrained +to be equal to each other. + +```swift +struct TwoIntParams {} + +extension TwoIntParams where n == 2 { + func foo() { ... } +} + +extension TwoIntParams where n == m { + func bar() { ... } +} + +let x: TwoIntParams +x.foo() // OK +x.bar() // Error, doesn't match constraint + +let y: TwoIntParams +y.foo() // Error, doesn't match constraint +y.bar() // OK +``` + +Integer generic parameters cannot be constrained to be equal to type generic +parameters, concrete types, or to declarations other than generic parameters. +Integer generic parameters also cannot be constrained to conform to protocols. + +```swift +extension TwoIntParams where n == T {} // error +extension TwoIntParams where T == n {} // error +extension TwoIntParams where n == Int {} // error + +let globalConstant = 42 +extension TwoIntParams where n == globalConstant {} // error + +extension TwoIntParams where n: Collection // error +``` + +(In the same way overload resolution already works in Swift, extensions or +functions with generic constraints on integer parameters will only be chosen +for call sites at which those constraints always hold; we won't "dispatch" +based on the value of an argument from a less-constrained call site.) + +```swift +struct Foo { + func foo() { print("foo #1") } + + func bar() { + // Always prints "foo #1" + self.foo() + } +} + +extension Foo where n == 2 { + func foo() { print("foo #2") } +} + +Foo<2>().bar() // prints "foo #1" +Foo<2>().foo() // prints "foo #2" +``` + +## Source compatibility + +This proposal is a strict extension of the existing language. The `let n: Type` +syntax should ensure source compatibility if we expand the feature to allow +value generic parameters of other types in the future. + +## ABI compatibility + +This proposal does not affect the ABI of existing code. Handling integer +generic parameters in full generality requires new functionality in the +Swift runtime to be able to encode and interpret them as part of type +metadata. + +As with generic parameters in general, adding or removing +integer generic parameters, replacing value parameters of a function with +integer generic parameters, reordering an integer generic parameter relative to +other generic parameters (whether value or type), and adding or removing +same-value constraints are all ABI-breaking changes. + +## Implications on adoption + +### Back-deployment limitations + +On platforms where the vendor ships the Swift runtime with the operating +system, there may be limitations on using integer generic parameters in +programs that want to target earlier versions of those platforms that don't +have the necessary runtime support. + +### Naming conventions + +Integer generic parameters are a new kind of declaration in Swift, and +conventions need to be established as to how they should be named. This +proposal recommends that integer generic parameters follow the convention +of other value bindings and be named using `lowerCamelCase` identifiers. + +## Future directions + +This proposal aims to establish the core functionality of integer generic +parameters. There are many possible improvements that could be built upon +this base: + +### Fixed-size and fixed-capacity collection types + +This proposal provides a foundational mechanism for fixed-size array and +fixed-capacity collection types, but does not itself introduce any +new standard library types or mechanisms for defining those types. We leave +it to future proposals to explore the design of those types. + +### Use of constant bindings as generic parameters + +It would be very useful to be able to use constant bindings as generic +parameter bindings, in addition to literals and existing generic parameter +bindings: + +```swift +static let bufferSize + = MemoryLayout.size * 64 + MemoryLayout.size * 8 + +var buffer = Vector(...) +``` + +This should be possible as long as the bindings referenced are known to be +constant (like `let` bindings are). + +A likely-fundamental limitation to this feature, as well as related +constant evaluation features explored below, is that the type checker will +likely be unable to reason about the value of these bindings. +Type checking influences overload resolution and the overall meaning of +expressions, so cannot rely on the evaluation of those expressions without +creating circularities. We may be able understand that two terms spelled +exactly the same way are equivalent, but we wouldn't recognize two different +expressions with the same result are the same type statically: + +``` +let fourShorts = 4 * MemoryLayout.size +let eightBytes = 8 * MemoryLayout.size + +var v1: Vector = [...] +var v2: Vector = [...] +v1 = v2 // Error, different types +``` + +This is similar to how opaque result types for different declarations are +type-checked as if they are potentially different types even when their +underlying types dynamically resolve to the same type. + +### Arithmetic in generic parameters + +There are many operations that would benefit from being able to express basic +arithmetic relationships among values. For instance, the concatenation of two +fixed-sized arrays would give an array whose length is the sum of the input +lengths: + +```swift +func concat( + _ a: Vector, _ b: Vector +) -> Vector +``` + +Due to the bidirectional nature of Swift's type-checking, there would be +limits to the sorts of relations we would be able to express this way. + +### Relating integer generic parameters and variadic pack shapes + +The "shape" of a parameter pack ultimately compiles down to its length. +Variadic packs don't currently have a way to directly reference or constrain +their shape or length, and integer generic parameters might be one way of doing +so. Among other things, this might allow for a variadic API to express that it +takes as many arguments as one of its integer generic parameters indicates: + +```swift +struct Vector { + // the initializer for a Vector takes one argument + // for every element + init(_ values: repeat each n * T) +} +``` + +### Non-integer value generic parameters + +We may want to eventually allow generic declarations to have value parameters +of type other than `Int`. The proposal's `let Parameter: Type` declaration +syntax maintains space for this: + +```swift +struct MatrixShape { var rows: Int, columns: Int } + +struct Matrix { + var elements: Vector> +} +``` + +Although the syntactic extension is straightforward, there are a lot of +questions to answer about how type equality is determined when values of +arbitrary type are involved, and what sorts of construction and destructuring +operations can be supported at type level. There is some precedent in +other languages to look at here, particularly C++'s non-type template +parameters or Rust's similar const generics feature. However, in relation to +those other languages, Swift puts a bit stronger emphasis on being able to +abstract the layout of types, but the type-level equality of parameters would +be heavily dependent on their types' layout and how initialization and property +access works. + +### Integer parameter packs + +There are use cases for variadic packs of integer generic parameters. +For instance, it might be a way of representing arbitrary multidimensional +matrices of values: + +```swift +struct MDMatrix { ... } + +let mat2d: MDMatrix<4, 4> = ... +let mat4d: MDMatrix<120, 24, 6, 2> = ... +``` + +## Alternatives considered + +### Variable-sized types instead of integer generic parameters + +One of the primary motivators for integer generic parameters is to represent +fixed-size and fixed-capacity collections. One of the reasons this is necessary +is because every value of a Swift type has to have a uniform size; since +a four-element array has a different size from a five-element array, that +implies that they have to be different types `Vector<4, T>` and `Vector<5, T>`. + +However, one could argue that the fundamental type of such a container +doesn't really change with its size; in most cases, a function that can +accept an array of some size can just as well accept an array of any size. +Forcing a type distinction between different-sized arrays forces the majority +of APIs that want to work with arrays to either be generic over their size, +be generic over some more abstract protocol like `Collection` that all +sized arrays conform to (along with unsized `Array` and non-array collections), +or work with the arrays indirectly through some handle type like +`UnsafeBufferPointer` or `Span`. + +So it's interesting to consider an alternative design where we instead +remove the "all values of a type have the same size" constraint. One could +say that the owner of a `Vector` value has to give it some size, but then +a `borrowing` or `inout Vector` can reference a `Vector` of any size, since +the reference representation would carry that size information from the +owner. There are however a lot of open questions following this design +path—if you want to have a two-dimensional `Vector` of `Vector`s, how do you +track the size information of both levels of nesting? There also *are* +functions that want to require taking two input arrays of the same size, +or promise to return an array as the same size as an argument. These +relationships are straightforward to express through the generics system, +and if sizes aren't propagated through types but some other means, it seems +likely we would need a parallel mechanism for reasoning about sizes generically. +Variable-sized types are an interesting idea to explore, but it isn't clear +that they lead to an overall simpler language design. + +### Declaring value parameters without `let` + +One could argue that, since `Int` clearly isn't a protocol constraint, that +it should be sufficient to declare integer generic parameters with the +syntax `` without an introducer like `let`. There are at least +a couple of reasons we choose to adopt the `let` introducer: + +- It makes it clear to the reader (and the compiler) what parameters are + value parameters without needing to do name resolution first. This may not + be a huge deal for `Int`, but if we expand the feature to allow other + types of value generic parameters, then it may not be obvious in an + unfamiliar codebase whether `T: Foo` refers to a protocol constraint + `Foo` or a concrete type `Foo`. +- If we do generalize value generic parameters to allow other types in the + future, it's not entirely out of the question that that could include + existential types, which would make `T: P` potentially ambiguous as to + whether it declares a type parameter constrained to `T` or a value + parameter of type `any P`. (There are perhaps other ways of dealing with that + ambiguity, such as requiring the value parameter form to be written + explicitly with `t: any P`.) + +### Arbitrary-precision integer generic parameters + +Instead of treating integer generic parameters as values of `Int` or any +finite type, another possible design would be to treat type-level integers +as independent of any concrete type, leaving them as ideal arbitrary-precision +integers. This would have some semantic advantages if we want to allow for +type-level arithmetic relationships, since these operations could be defined +in their ideal form without having to deal with overflow and other limitations +of concrete Swift types. In such a design, a reference to an integer generic +parameter in a value expression could be treated as polymorphic, in a similar +way to how integer literals can be used with any type that's +`ExpressibleByIntegerLiteral`. + +Although this model has some appeal, it also has some practical issues. If +type-level integers are arbitrary precision, but value-level integer types are +still finite, then there is the chance for overflow any time a type-level +integer is reified to a finite integer type. This model also would not extend +very naturally to non-integer value parameters if we introduce those in the +future. + +### Generic parameters of integer types other than `Int` + +We discuss generalizing value generic parameters to types other than `Int` +as a future direction above, but a narrower expansion might be to allow all +of Swift's primitive integer types, including all of the sized and +signed/unsigned variants, as types for generic value parameters. One could +argue that `UInt` is particular is desirable to use as the type for fixed-size +and fixed-capacity collections, of which instances can never actually be +constructed for negative sizes. + +However, we would like to continue to promote the use of `Int` as the common +currency type for integers, as we have already established for the standard +library. Introducing mixed integer types as type-level generic parameters +would inevitably lead to the need to be able to perform type conversions +at type level, and the associated need to deal with overflow during these +type-level conversions. + +The established API for the `Collection` protocol, `Array`, and the other +standard library types already use `Int` for `count` and array subscripting +operations, so establishing `Int` as the type for type-level size parameters +avoids the need for type conversions when mixing type- and value-level index +and size values. Types that use integer parameters for sizing can still +refuse to initialize values of types with negative parameters, so that a +type like `Vector<-1, Int>` is uninhabited. Given the restrictions in this +initial proposal, without type-level arithmetic, it is unlikely that +developers would intentionally form such a type with a negative size explicitly. + +### Alternative naming conventions + +We propose recommending a `lowerCamelCase` naming convention for integer +generic parameters, following the recommended convention for other value +declarations such as property and function declarations. This allows for +integer generic parameters to appear consistent with other value references +in expressions. If we add the ability to instantiate generics using value +constants or expressions as integer generic arguments in the future, this will +also maintain consistency between existing generic parameters and other value +declarations used as arguments: + +``` +struct Foo { + static let nSquared = n * n + + // These both consistently use lowerCamelCase + var vector: Vector + var matrix: Vector +} +``` + +Alternatively, we could recommend that integer generic parameters use +`UpperCamelCase`, following the convention of type generic parameters. We +believe that the distinction between values and types is better to prioritize +than the distinction between type-level and value-level parameters. + +### Syntactic separation of value and type parameters + +The angle brackets that enclose generic arguments have to be syntactically +disambiguated from the `<` and `>` operators. This disambiguation relies on +parsing ahead to determine whether the source code following a `<` is parsable +as a list of types followed by a closing `>` and a member access or initializer +call. This lookahead rule has worked well up to this point, but it could impose +constraints on our ability to allow for expressions to be used as generic +arguments, since allowing more expression productions to appear in generic +argument lists will lead to more potentially ambiguous parsing situations. + +It may be worth considering a design that separates value generic parameters +by putting them in a different set of brackets separate from the type +generic parameters, like `Vector[count]`. This would avoid the need +for disambiguation if an arbitrary expression can be used as a `count` in +the future. + +## Acknowledgments + +We would like to thank the following people for prototyping and design +contributions that helped shape this proposal: + +- Holly Borla +- Ben Cohen +- Erik Eckstein +- Doug Gregor +- Tim Kientzle +- Karoy Lorentey +- John McCall +- Kuba Mracek +- Slava Pestov +- Andrew Trick +- Pavel Yaskevich From d90d04ea9df317fa3a7e19100637545c5a63a422 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 5 Nov 2024 10:37:39 -0800 Subject: [PATCH 3915/4563] Rename ABCD-integer-generic-parameters.md to 0452-integer-generic-parameters.md (#2606) --- ...r-generic-parameters.md => 0452-integer-generic-parameters.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{ABCD-integer-generic-parameters.md => 0452-integer-generic-parameters.md} (100%) diff --git a/proposals/ABCD-integer-generic-parameters.md b/proposals/0452-integer-generic-parameters.md similarity index 100% rename from proposals/ABCD-integer-generic-parameters.md rename to proposals/0452-integer-generic-parameters.md From 90f401db5a7b939b6b3e123087cbde0e4cd64601 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 5 Nov 2024 10:41:24 -0800 Subject: [PATCH 3916/4563] Add review thread to SE-0452 (#2607) --- proposals/0452-integer-generic-parameters.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0452-integer-generic-parameters.md b/proposals/0452-integer-generic-parameters.md index 4a4716c1e5..86cfc38699 100644 --- a/proposals/0452-integer-generic-parameters.md +++ b/proposals/0452-integer-generic-parameters.md @@ -5,6 +5,7 @@ * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Active Review (Nov 5-19 2024)** * Implementation: [apple/swift#75518](https://github.com/apple/swift/pull/75518) +* Forum Threads: [Review](https://forums.swift.org/t/se-0452-integer-generic-parameters/75844) * Upcoming Feature Flag: `ValueGenerics` ## Introduction From 632e7fc64b5a2bf16ed85d727157e8d9654c9e70 Mon Sep 17 00:00:00 2001 From: shiz <35151927+stzn@users.noreply.github.com> Date: Wed, 6 Nov 2024 07:27:03 +0900 Subject: [PATCH 3917/4563] Add missing `=` (#2608) --- proposals/0434-global-actor-isolated-types-usability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0434-global-actor-isolated-types-usability.md b/proposals/0434-global-actor-isolated-types-usability.md index ef550fd7bc..c98d005d2e 100644 --- a/proposals/0434-global-actor-isolated-types-usability.md +++ b/proposals/0434-global-actor-isolated-types-usability.md @@ -166,7 +166,7 @@ class NonSendable {} func test() { let ns = NonSendable() - let closure { @MainActor in + let closure = { @MainActor in print(ns) } From a160e45326a1d5c5096e9dcf423cc45c0c941dab Mon Sep 17 00:00:00 2001 From: shiz <35151927+stzn@users.noreply.github.com> Date: Wed, 6 Nov 2024 07:30:08 +0900 Subject: [PATCH 3918/4563] Add missing `=` (#2609) --- proposals/0434-global-actor-isolated-types-usability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0434-global-actor-isolated-types-usability.md b/proposals/0434-global-actor-isolated-types-usability.md index c98d005d2e..da254db685 100644 --- a/proposals/0434-global-actor-isolated-types-usability.md +++ b/proposals/0434-global-actor-isolated-types-usability.md @@ -184,7 +184,7 @@ Note that under region isolation in SE-0414, capturing a non-`Sendable` value in class NonSendable {} func test(ns: NonSendable) async { - let closure { @MainActor in + let closure = { @MainActor in print(ns) // error: task-isolated value 'ns' can't become isolated to the main actor } From 04449d232f1b6b5c3c5dfa4c3ca44bfca9ba36b1 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 7 Nov 2024 00:16:40 +0900 Subject: [PATCH 3919/4563] Mark SE-0371 - isolated deinit implemented in 6.1 (#2603) --- proposals/0371-isolated-synchronous-deinit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0371-isolated-synchronous-deinit.md b/proposals/0371-isolated-synchronous-deinit.md index ed081a409e..d860b1178f 100644 --- a/proposals/0371-isolated-synchronous-deinit.md +++ b/proposals/0371-isolated-synchronous-deinit.md @@ -3,7 +3,7 @@ * Proposal: [SE-0371](0371-isolated-synchronous-deinit.md) * Author: [Mykola Pokhylets](https://github.com/nickolas-pohilets) * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) -* Status: **Accepted with modifications** +* Status: **Implemented (Swift 6.1)** * Implementation: [apple/swift#60057](https://github.com/apple/swift/pull/60057) * Review: ([first pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) ([first review](https://forums.swift.org/t/se-0371-isolated-synchronous-deinit/59754)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0371-isolated-synchronous-deinit/60060)) ([discussion](https://forums.swift.org/t/isolated-synchronous-deinit-2/62565)) ([second pitch](https://forums.swift.org/t/pitch-2-se-0371-isolated-async-deinit/64836)) ([sub-pitch](https://forums.swift.org/t/sub-pitch-task-local-values-in-isolated-synchronous-deinit-and-async-deinit/70060)) ([second review](https://forums.swift.org/t/second-review-se-0371-isolated-synchronous-deinit/73406)) ([accepted with modifications](https://forums.swift.org/t/accepted-with-modifications-se-0371-isolated-synchronous-deinit/74042)) From f863e140ec7e47825f09f31d9e29e4233db75741 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 6 Nov 2024 16:40:25 -0800 Subject: [PATCH 3920/4563] SE-0446 has been accepted --- proposals/0446-non-escapable.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0446-non-escapable.md b/proposals/0446-non-escapable.md index 55a7fdcfa6..854394b9cb 100644 --- a/proposals/0446-non-escapable.md +++ b/proposals/0446-non-escapable.md @@ -3,11 +3,12 @@ * Proposal: [SE-0446](0446-non-escapable.md) * Authors: [Andrew Trick](https://github.com/atrick), [Tim Kientzle](https://github.com/tbkka) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Active review (September 17...October 1, 2024)** +* Status: **Accepted** * Roadmap: [BufferView Language Requirements](https://forums.swift.org/t/roadmap-language-support-for-bufferview) * Implementation: **Implemented** in `main` branch * Upcoming Feature Flag: `NonescapableTypes` * Review: ([pitch](https://forums.swift.org/t/pitch-non-escapable-types-and-lifetime-dependency/69865)) +* Decision Notes: [Acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0446-nonescapable-types/75504) ## Introduction From 0053c5a90c5386014a1b73454885703417d9c838 Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Fri, 8 Nov 2024 03:37:31 -0800 Subject: [PATCH 3921/4563] Metadata fixes for SE-0452 Fix metadata so that the proposal appears properly in the evolution dashboard - Normalize status field name and date - Normalize 'Review' field name - Add missing link to pitch thread in forums --- proposals/0452-integer-generic-parameters.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0452-integer-generic-parameters.md b/proposals/0452-integer-generic-parameters.md index 86cfc38699..417363fbab 100644 --- a/proposals/0452-integer-generic-parameters.md +++ b/proposals/0452-integer-generic-parameters.md @@ -3,9 +3,9 @@ * Proposal: [SE-0452](0452-integer-generic-parameters.md) * Authors: [Alejandro Alonso](https://github.com/Azoy), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (Nov 5-19 2024)** +* Status: **Active review (November 5-19 2024)** * Implementation: [apple/swift#75518](https://github.com/apple/swift/pull/75518) -* Forum Threads: [Review](https://forums.swift.org/t/se-0452-integer-generic-parameters/75844) +* Review: ([pitch](https://forums.swift.org/t/integer-generic-parameters/74181)) ([review](https://forums.swift.org/t/se-0452-integer-generic-parameters/75844)) * Upcoming Feature Flag: `ValueGenerics` ## Introduction From 00063f52a49a7757cb8c5f21a5b52fb5abb4b255 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 8 Nov 2024 15:03:50 +0000 Subject: [PATCH 3922/4563] Address PSG meeting feedback --- visions/webassembly.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/visions/webassembly.md b/visions/webassembly.md index 565b79c608..35587aebf8 100644 --- a/visions/webassembly.md +++ b/visions/webassembly.md @@ -83,12 +83,13 @@ overhead of new process setup and IPC infrastructure. ## Goals -As of March 2024 all patches necessary for basic Wasm and WASI Preview 1 support have been merged to the Swift toolchain and -core libraries. Based on this, we propose a high-level roadmap for WebAssembly support and adoption in the Swift +As of March 2024 all patches necessary for basic Wasm and WASI Preview 1 support have been merged to the Swift +toolchain and core libraries. Based on this, we propose a high-level roadmap for WebAssembly support and adoption in the Swift ecosystem: 1. Make it easier to evaluate and adopt Wasm with increased API coverage for this platform in the Swift core libraries. -As a virtualized embeddable platform, not all system APIs are always available or easy to port to WASI. For example, +Main prerequisite for that is setting up CI jobs for those libraries that run tests for WASI and also Embedded Wasm, where +possible. As a virtualized embeddable platform, not all system APIs are always available or easy to port to WASI. For example, multi-threading, file system access, networking and localization need special support in Wasm runtimes and a certain amount of consideration from a developer adopting these APIs. @@ -100,7 +101,8 @@ platforms. that future versions of WASI are available to Swift developers targeting Wasm. 4. Make interoperability with Wasm components as smooth as C and C++ interop already is for Swift. With a formal -specification for Canonical ABI progressing, this will become more achievable with time. This includes consuming components from, and building components with Swift. +specification for Canonical ABI progressing, this will become more achievable with time. This includes consuming +components from, and building components with Swift. ### Proposed Language Features @@ -137,12 +139,8 @@ can use DWARF information embedded in Wasm file to provide debugging support. Ho metadata information and JIT-based expression evaluation will require integrating LLDB's Swift plugin with these debuggers in some way. -Given that both approaches can utilize DWARF information, the Swift toolchain should generate as much DWARF information -as possible for what's statically known. Also, making Swift type information DWARF-friendly is desirable, since the -current debug information generated by the Swift compiler assumes that debuggers can understand certain Swift -primitives. E.g. `Swift.Int` is currently represented as a `DW_TAG_structure_type` node without any member, but ideally -such types should be represented as `DW_TAG_base_type` nodes with `DW_AT_encoding` attribute or provide a debug summary -string by LLDB Summary Strings). +In summary, debugging in the browser and outside of the browser context are sufficiently different activities to +require separate implementation approaches. [1]: https://github.com/llvm/llvm-project/pull/77949 [2]: https://github.com/ChromeDevTools/devtools-frontend/blob/main/extensions/cxx_debugging From 1f0f8fd1b70fa51209a3ebb0dd6b3b8512de4bba Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 11 Nov 2024 06:55:06 -0800 Subject: [PATCH 3923/4563] Fix a small example so that the example matches the narrative within. (#2613) Specifically, the narrative is talking about sending the variable 't' not the variable 'ns'. --- proposals/0430-transferring-parameters-and-results.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0430-transferring-parameters-and-results.md b/proposals/0430-transferring-parameters-and-results.md index c8fc1bb1d0..349d6b50ee 100644 --- a/proposals/0430-transferring-parameters-and-results.md +++ b/proposals/0430-transferring-parameters-and-results.md @@ -213,7 +213,7 @@ func h() async { // This is a `sending` value. let ns = Nonsending() - // This also a `sending value. + // This also a `sending` value. let ns2 = NonSendable() // Since both ns and ns2 are disconnected, the region associated with @@ -221,7 +221,7 @@ func h() async { let t = (ns, ns2) // ... that can be sent across a concurrency boundary safely. - await sendToMain(ns) + await sendToMain(t) } ``` From 9a71aa521cd08fb040698bea4bc87ae5e497eeff Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Mon, 11 Nov 2024 07:26:54 -0800 Subject: [PATCH 3924/4563] Remove upcoming feature flag from SE-0452 --- proposals/0452-integer-generic-parameters.md | 1 - 1 file changed, 1 deletion(-) diff --git a/proposals/0452-integer-generic-parameters.md b/proposals/0452-integer-generic-parameters.md index 86cfc38699..27c3d93535 100644 --- a/proposals/0452-integer-generic-parameters.md +++ b/proposals/0452-integer-generic-parameters.md @@ -6,7 +6,6 @@ * Status: **Active Review (Nov 5-19 2024)** * Implementation: [apple/swift#75518](https://github.com/apple/swift/pull/75518) * Forum Threads: [Review](https://forums.swift.org/t/se-0452-integer-generic-parameters/75844) -* Upcoming Feature Flag: `ValueGenerics` ## Introduction From c5d8bae2bb489131d0b2e246c447f3c3cbb1812c Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 13 Nov 2024 16:52:59 +0000 Subject: [PATCH 3925/4563] Add debugging as a separate goal to the "Goals" section --- visions/webassembly.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/visions/webassembly.md b/visions/webassembly.md index 35587aebf8..3ae48a47af 100644 --- a/visions/webassembly.md +++ b/visions/webassembly.md @@ -88,21 +88,25 @@ toolchain and core libraries. Based on this, we propose a high-level roadmap for ecosystem: 1. Make it easier to evaluate and adopt Wasm with increased API coverage for this platform in the Swift core libraries. -Main prerequisite for that is setting up CI jobs for those libraries that run tests for WASI and also Embedded Wasm, where -possible. As a virtualized embeddable platform, not all system APIs are always available or easy to port to WASI. For example, -multi-threading, file system access, networking and localization need special support in Wasm runtimes and a certain amount of -consideration from a developer adopting these APIs. + Main prerequisite for that is setting up CI jobs for those libraries that run tests for WASI and also Embedded Wasm, where + possible. As a virtualized embeddable platform, not all system APIs are always available or easy to port to WASI. For example, + multi-threading, file system access, networking and localization need special support in Wasm runtimes and a certain amount of + consideration from a developer adopting these APIs. 2. Improve support for cross-compilation in Swift and SwiftPM. We can simplify versioning, installation, and overall -management of Swift SDKs for cross-compilation in general, which is beneficial not only for WebAssembly, but for all -platforms. + management of Swift SDKs for cross-compilation in general, which is beneficial not only for WebAssembly, but for all + platforms. 3. Continue work on Wasm Component Model support in Swift as the Component Model proposal is stabilized. Ensure -that future versions of WASI are available to Swift developers targeting Wasm. + that future versions of WASI are available to Swift developers targeting Wasm. 4. Make interoperability with Wasm components as smooth as C and C++ interop already is for Swift. With a formal -specification for Canonical ABI progressing, this will become more achievable with time. This includes consuming -components from, and building components with Swift. + specification for Canonical ABI progressing, this will become more achievable with time. This includes consuming + components from, and building components with Swift. + +5. Improve debugging experience of Swift code compiled to Wasm. While rudimentary support for debugging + exists in some Wasm runtimes, we aim to improve it and, where possible, make it as good as debugging Swift code + compiled to other platforms. ### Proposed Language Features From e7b07b3f539062703fcf435e666eb651b67372ad Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 13 Nov 2024 15:24:12 -0800 Subject: [PATCH 3926/4563] proposal-templates: update organisation Update the organisation for the template. This reflects the recent moves for the swift repository on GitHub. --- proposal-templates/0000-swift-template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposal-templates/0000-swift-template.md b/proposal-templates/0000-swift-template.md index 57abc06d07..c73d88ff67 100644 --- a/proposal-templates/0000-swift-template.md +++ b/proposal-templates/0000-swift-template.md @@ -6,8 +6,8 @@ * Status: **Awaiting implementation** or **Awaiting review** * Vision: *if applicable* [Vision Name](https://github.com/swiftlang/swift-evolution/visions/NNNNN.md) * Roadmap: *if applicable* [Roadmap Name](https://forums.swift.org/...) -* Bug: *if applicable* [apple/swift#NNNNN](https://github.com/apple/swift/issues/NNNNN) -* Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) or [swiftlang/swift-evolution-staging#NNNNN](https://github.com/swiftlang/swift-evolution-staging/pull/NNNNN) +* Bug: *if applicable* [swiftlang/swift#NNNNN](https://github.com/swiftlang/swift/issues/NNNNN) +* Implementation: [swiftlang/swift#NNNNN](https://github.com/swiftlang/swift/pull/NNNNN) or [swiftlang/swift-evolution-staging#NNNNN](https://github.com/swiftlang/swift-evolution-staging/pull/NNNNN) * Upcoming Feature Flag: *if applicable* `MyFeatureName` * Previous Proposal: *if applicable* [SE-XXXX](XXXX-filename.md) * Previous Revision: *if applicable* [1](https://github.com/swiftlang/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md) From 3d897a0a06c671ad107375334f6305f424904cb8 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 13 Nov 2024 18:57:21 -0800 Subject: [PATCH 3927/4563] Proposal: Vector (#2587) * Create nnnn-vector.md Update nnnn-vector.md Update nnnn-vector.md Update nnnn-vector.md * Fix some typos * Update proposals/nnnn-vector.md Co-authored-by: Ben Rimmington * Update proposals/nnnn-vector.md Co-authored-by: Ben Rimmington * Update proposals/nnnn-vector.md Co-authored-by: Ben Rimmington * Clarify the shadowing rule a little more. * Change the C interop story to use an upcoming feature instead * Updates based on forum review * Update proposals/nnnn-vector.md Co-authored-by: Ben Rimmington * Fix code examples in motivation and proposed solution Update nnnn-vector.md * Vector is SE-0453 --------- Co-authored-by: Ben Rimmington Co-authored-by: Freddy Kellison-Linn --- proposals/0453-vector.md | 829 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 829 insertions(+) create mode 100644 proposals/0453-vector.md diff --git a/proposals/0453-vector.md b/proposals/0453-vector.md new file mode 100644 index 0000000000..6675ef5b52 --- /dev/null +++ b/proposals/0453-vector.md @@ -0,0 +1,829 @@ +# Vector, a fixed-size array + +* Proposal: [SE-0453](0453-vector.md) +* Authors: [Alejandro Alonso](https://github.com/Azoy) +* Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) +* Status: **Active review (November 13...November 27, 2024)** +* Roadmap: [Approaches for fixed-size arrays](https://forums.swift.org/t/approaches-for-fixed-size-arrays/58894) +* Implementation: [swiftlang/swift#76438](https://github.com/swiftlang/swift/pull/76438) +* Review: ([pitch](https://forums.swift.org/t/vector-a-fixed-size-array/75264)) + +## Introduction + +This proposal introduces a new type to the standard library, `Vector`, which is +a fixed-size array. This is analogous to the +[classical C arrays `T[N]`](https://en.cppreference.com/w/c/language/array), +[C++'s `std::array`](https://en.cppreference.com/w/cpp/container/array), +and [Rust's arrays `[T; N]`](https://doc.rust-lang.org/std/primitive.array.html). + +## Motivation + +Arrays in Swift have served as the go to choice when needing to put items in an +ordered list. They are a great data structure ranging from a variety of +different use cases from teaching new developers all the way up to sophisticated +implementation details of something like a cache. + +However, using `Array` all the time doesn't really make sense in some scenarios. +It's important to understand that `Array` is a heap allocated growable data +structure which can be expensive and unnecessary in some situations. The next +best thing is to force a known quantity of elements onto the stack, probably by +using tuples. + +```swift +func complexAlgorithm() { + let elements = (first, second, third, fourth) +} +``` + +Unfortunately, using tuples in this way is very limited. They don't allow for +dynamic indexing or iteration: + +```swift +func complexAlgorithm() { + let elements = (first, second, third, fourth) + + // Have to manually know the tuple has N elements... + for i in 0 ..< 4 { + // error: cannot access element using subscript for tuple type + // '(Int, Int, Int, Int)'; use '.' notation instead + compute(elements[i]) + } +} +``` + +It wasn't until [SE-0322 Temporary uninitialized buffers](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0322-temporary-buffers.md), which proposed the `withUnsafeTemporaryAllocation` +facilities, that made this situation a little easier to work with by giving us a +direct `UnsafeMutableBufferPointer` pointing either somewhere on the stack or to +a heap allocation. This API allows us to get the indexing and iteration we want, +but it drops down to an unsafe layer which is unfortunate because there should +be much safer ways to achieve the same while not exposing unsafety to +developers. + +While we aren't getting rid of `Array` anytime soon, more and more folks are +looking towards Swift to build safer and performant code and having `Array` be +our only solution to an ordered list of things is less than ideal. `Array` is a +very general purpose array collection that can suit almost any need, but it is +always heap allocated, automatically resizable, and introduces retain/release +traffic. These implicit allocations are becoming more and more of a bottleneck, +especially in embedded domains where there might not be a lot of memory for many +allocations or even heap allocations at all. Swift should be able to provide +developers a safe API to have an ordered list of homogeneous items on the stack, +allowing for things like indexing, iteration, and many other collection +utilities. + +## Proposed solution + +We introduce a new top level type, `Vector`, to the standard library which is a +fixed-size contiguously inline allocated array. We're defining "inline" as using +the most natural allocation pattern depending on the context of where this is +used. It will be stack allocated most of the time, but as a class property +member it will be inline allocated on the heap with the rest of the properties. +`Vector` will never introduce an implicit heap allocation just for its storage +alone. + +```swift +func complexAlgorithm() -> Int { + // This is a stack allocation, no 'malloc's or reference counting here! + let elements: Vector<4, Int> = [1, 2, 3, 4] + + for i in elements.indices { + compute(elements[i]) // OK + } +} +``` + +This type also serves as a safer replacement to `withUnsafeTemporaryAllocation` +when the amount of things to reserve is known statically: + +```swift +// Previously +withUnsafeTemporaryAllocation(of: Int.self, capacity: 16) { + $0.initialize(repeating: 0) + + for i in $0.indices { + $0[i] = compute() + } +} + +// Now +var ints = Vector<16, Int>(repeating: 0) + +for i in ints.indices { + ints[i] = compute() +} +``` + +Vectors of noncopyable values will be possible by using any of the closure based +taking initializers or the literal initializer: + +```swift +// [Atomic(0), Atomic(1), Atomic(2), Atomic(3)] +let incrementingAtomics = Vector<4, Atomic> { i in + Atomic(i) +} // [Atomic(0), Atomic(1), Atomic(2), Atomic(3)] + +// [Atomic(337523057234), Atomic(8726493873498347), Atomic(98573472834767364), +// Atomic(72394735763974)] (maybe) +let randomAtomics = Vector<4, _>(expand: RNG()) { Atomic($0.next()) } + +// [Sprite(), Sprite(), Sprite(), Sprite()] +// Where the 2nd, 3rd, and 4th elements are all copies of the initial first +// value +let copiedSprites = Vector<4, _>(unfold: Sprite()) { $0.copy() } + +// Inferred to be Vector<3, Mutex> +let literalMutexes: Vector = [Mutex(0), Mutex(1), Mutex(2)] +``` + +These closure based initializers are not limited to noncopyable values however! + +## Detailed design + +`Vector` will be a simple noncopyable struct capable of storing other potentially +noncopyable elements. It will be conditionally copyable only when its elements +are. + +```swift +public struct Vector: ~Copyable {} + +extension Vector: Copyable where Element: Copyable {} +extension Vector: BitwiseCopyable where Element: BitwiseCopyable {} +extension Vector: Sendable where Element: Sendable {} +``` + +### MemoryLayout + +The memory layout of a `Vector` is defined by taking its `Element`'s stride and +multiplying that by its `count` for its size and stride. Its alignment is equal +to that of its `Element`: + +```swift +MemoryLayout.stride == 1 +MemoryLayout.alignment == 1 + +MemoryLayout>.size == 4 +MemoryLayout>.stride == 4 +MemoryLayout>.alignment == 1 + +struct Uneven { + let x: UInt32 + let y: Bool +} + +MemoryLayout.stride == 8 +MemoryLayout.alignment == 4 + +MemoryLayout>.size == 32 +MemoryLayout>.stride == 32 +MemoryLayout>.alignment == 4 + +struct ACoupleOfUInt8s { + let x: Vector<2, UInt8> +} + +MemoryLayout.stride == 2 +MemoryLayout.alignment == 1 + +MemoryLayout>.size == 4 +MemoryLayout>.stride == 4 +MemoryLayout>.alignment == 1 +``` + +### Literal Initialization + +Before discussing any of the API, we need to discuss how the array literal +syntax will be used to initialize a value of `Vector`. While naively we could +conform to `ExpressibleByArrayLiteral`, the shape of the initializer always +takes an actual `Array` value. This could be optimized away in the simple cases, +but fundamentally it doesn't make sense to have to do an array allocation to +initialize a stack allocated `Vector`. Therefore, the array literal +initialization for `Vector` will be a special case, at least to start out with. +A stack allocated vector using a vector literal will do in place initialization +of each element at its stack slot. The two below are roughly equivalent: + +```swift +let numbers: Vector<3, Int> = [1, 2, 3] + +// Roughly gets compiled as: + +// This is not a real 'Vector' initializer! +let numbers: Vector<3, Int> = Vector() +numbers[0] = 1 +numbers[1] = 2 +numbers[2] = 3 +``` + +There shouldn't be any intermediary values being copied or moved into the vector. + +Note that the array literal syntax will only create a `Vector` value when the +compiler knows concretely that it is a `Vector` value. We don't want to break +source whatsoever, so whatever current rules the compiler has will still be +intact. Consider the following uses of the array literal syntax and where each +call site creates either a `Swift.Array` or a `Swift.Vector`. + +```swift +let a = [1, 2, 3] // Swift.Array +let b: Vector<3, Int> = [1, 2, 3] // Swift.Vector + +func generic(_: T) {} + +generic([1, 2, 3]) // passes a Swift.Array +generic([1, 2, 3] as Vector<3, Int>) // passes a Swift.Vector + +func test(_: T) {} + +test([1, 2, 3]) // passes a Swift.Array +test([1, 2, 3] as Vector<3, Int>) // error: 'Vector<3, Int>' does not conform to 'ExpressibleByArrayLiteral' + +func array(_: [T]) {} + +array([1, 2, 3]) // passes a Swift.Array +array([1, 2, 3] as Vector<3, Int>) // error: 'Vector<3, Int>' is not convertible to 'Array' + +func vector(_: Vector<3, T>) {} + +vector([1, 2, 3]) // passes a Swift.Vector +vector([1, 2, 3] as [Int]) // error: 'Array' is not convertible to 'Vector<3, Int>' +``` + +I discuss later about a hypothetical `ExpressibleByVectorLiteral` and the design +challenges there in [Future Directions](#expressiblebyvectorliteral). + +The literal initialization allows for more type inference just like the current +literal syntax does by inferring not only the element type, but also the count +as well: + +```swift +let a: Vector<_, Int> = [1, 2, 3] // Vector<3, Int> +let b: Vector<3, _> = [1, 2, 3] // Vector<3, Int> +let c: Vector<_, _> = [1, 2, 3] // Vector<3, Int> +let d: Vector = [1, 2, 3] // Vector<3, Int> + +func takesGenericVector(_: Vector) {} + +takesGenericVector([1, 2, 3]) // Ok, N is inferred to be '3'. +``` + +A compiler diagnostic will occur if the number of elements within the literal +do not match the desired count (as well as element with the usual diagnostic): + +```swift +// error: expected '2' elements in vector literal, but got '3' +let x: Vector<2, Int> = [1, 2, 3] + +func takesVector(_: Vector<2, Int>) {} + +// error: expected '2' elements in vector literal, but got '3' +takesVector([1, 2, 3]) +``` + +### Initialization + +In addition to literal initialization, `Vector` offers a few others forms of +initialization: + +```swift +extension Vector where Element: ~Copyable { + /// Initializes every element in this vector running the given closure value + /// that returns the element to emplace at the given index. + /// + /// This will call the closure `count` times, where `count` is the static + /// count of the vector, to initialize every element by passing the closure + /// the index of the current element being initialized. The closure is allowed + /// to throw an error at any point during initialization at which point the + /// vector will stop initialization, deinitialize every currently initialized + /// element, and throw the given error back out to the caller. + /// + /// - Parameter next: A closure that returns an owned `Element` to emplace at + /// the passed in index. + public init(with next: (Int) throws(E) -> Element) throws(E) + + /// Initializes every element in this vector by running the closure with the + /// passed in mutable state. + /// + /// This will call the closure 'count' times, where 'count' is the static + /// count of the vector, to initialize every element by passing the closure + /// an inout reference to the passed state. The closure is allowed to throw + /// an error at any point during initialization at which point the vector will + /// stop initialization, deinitialize every currently initialized element, and + /// throw the given error back out to the caller. + /// + /// - Parameter state: The mutable state that can be altered during each + /// iteration of the closure initializing the vector. + /// - Parameter next: A closure that passes in an inout reference to the + /// user given mutable state which returns an owned + /// `Element` instance to insert into the vector. + public init( + expand state: consuming State, + with next: (inout State) throws(E) -> Element + ) throws(E) + + /// Initializes every element in this vector by running the closure with the + /// passed in first element. + /// + /// This will call the closure 'count' times, where 'count' is the static + /// count of the vector, to initialize every element by passing the closure + /// an immutable borrow reference to the first element given to the + /// initializer. The closure is allowed to throw an error at any point during + /// initialization at which point the vector will stop initialization, + /// deinitialize every currently initialized element, and throw the given + /// error back out to the caller. + /// + /// - Parameter first: The first value to insert into the vector which will be + /// passed to the closure as a borrow. + /// - Parameter next: A closure that passes in an immutable borrow reference + /// of the given first element of the vector which returns + /// an owned `Element` instance to insert into the vector. + public init( + unfold first: consuming Element, + with next: (borrowing Element) throws(E) -> Element + ) throws(E) +} + +extension Vector where Element: Copyable { + /// Initializes every element in this vector to a copy of the given value. + /// + /// - Parameter value: The instance to initialize this vector with. + public init(repeating: Element) +} +``` + +### Deinitialization and consumption + +Once a vector is no longer used, the compiler will implicitly destroy its value. +This means that it will do an element by element deinitialization, releasing any +class references or calling any `deinit`s on noncopyable elements. + +### Generalized `Sequence` and `Collection` APIs + +While we aren't conforming `Vector` to `Collection` (more information in future +directions), we do want to generalize a lot of APIs that will make this a usable +collection type. + +```swift +extension Vector where Element: ~Copyable { + public typealias Element = Element + public typealias Index = Int + public typealias Indices = Range + + /// Provides the count of the collection statically without an instance. + public static var count: Int { count } + + public var count: Int { count } + public var indices: Range { 0 ..< count } + public var isEmpty: Bool { count == 0 } + public var startIndex: Int { 0 } + public var endIndex: Int { count } + + public borrowing func index(after i: Int) -> Int + public borrowing func index(before i: Int) -> Int + + public borrowing func reduce( + into initialResult: consuming Result, + _ updateAccumulatingResult: (inout Result, borrowing Element) throws(E) -> () + ) throws(E) -> Result + + public mutating func swapAt( + _ i: Int, + _ j: Int + ) + + public subscript(_ index: Int) -> Element +} +``` + +### C Interop changes + +With the introduction of `Vector`, we have a unique opportunity to fix another +pain point within the language with regards to C interop. Currently, the Swift +compiler imports a C array of type `T[24]` as a tuple of `T` with 24 elements. +Previously, this was really the only representation that the compiler could pick +to allow interfacing with C arrays. It was a real challenge working with these +fields from C in Swift. Consider the following C struct: + +```c +struct section_64 { + char sectname[16]; + char segname[16]; + uint64_t addr; + uint64_t size; + uint32_t offset; + uint32_t align; + ... +}; +``` + +Today, this gets imported as the following Swift struct: + +```swift +struct section_64 { + let sectname: (CChar, CChar, CChar, CChar, CChar, CChar, ... 10 more times) + let segname: (CChar, CChar, CChar, CChar, CChar, CChar, ... 10 more times) + let addr: UInt64 + let size: UInt64 + let offset: UInt32 + let align: UInt32 + ... +} +``` + +Using an instance of `section_64` in Swift for the most part is really easy. +Accessing things like `addr` or `size` are simple and easy to use, but using the +`sectname` property introduces a level of complexity that isn't so fun to use. + +```swift +func getSectionName(_ section: section_64) -> String { + withUnsafePointer(to: section.sectname) { + // This is unsafe! 'sectname' isn't guaranteed to have a null byte + // indicating the end of the C string! + String(cString: $0) + } +} + +func iterateSectionNameBytes(_ section: section_64) { + withUnsafeBytes(to: section.sectname) { + for byte in $0 { + ... + } + } +} +``` + +Having to resort to using very unsafe API to do anything useful with imported C +arrays is not something a memory safe language like Swift should be in the +business of. `Vector` allows us to clean up this sore spot in a very elegant and +much more importantly, safe way. + +We plan to introduce a new _upcoming_ feature that folks can enable whenever +they'd like to via `-enable-upcoming-feature ImportCArraysAsVectors`. This will +change the current behavior of importing C arrays as tuples, to instead import +them as `Vector`. For the previous example, we'd instead generate the following: + +```swift +struct section_64 { + let sectname: Vector<16, CChar> + let segname: Vector<16, CChar> + let addr: UInt64 + let size: UInt64 + let offset: UInt32 + let align: UInt32 + ... +} +``` + +By introducing an upcoming feature, it lets folks who are ready to migrate their +codebase using a C interface to use `Vector` entirely, while still allowing +dependencies or dependents to use the old behavior with the same C interface. + +In a future Swift language mode, we plan to make this behavior the default and +drop importing C array fields as tuples and only as `Vector`. This approach +allows developers to get the new behavior early if they'd like to use it, which +will make the language mode transition much smoother because there would be no +source break with regards to this feature. + +## Source compatibility + +`Vector` is a brand new type in the standard library, so source should still be +compatible. + +Given the name of this type however, we foresee this clashing with existing user +defined types named `Vector`. This isn't a particular issue though because the +standard library has special shadowing rules which prefer user defined types by +default. Which means in user code with a custom `Vector` type, that type will +always be preferred over the standard library's `Swift.Vector`. By always I +truly mean _always_. + +Given the following two scenarios: + +```swift +// MyLib +public struct Vector { + +} + +print(Vector.self) + +// error: generic type 'Vector' specialized with too many type parameters +// (got 2, but expected 1) +print(Vector<3, Int>.self) +``` + +Here, we're exercising the fact that this `MyLib.Vector` has a different generic +signature than `Swift.Vector`, but regardless of that we will prefer `MyLib`'s +version even if we supply more generic arguments than it supports. + +```swift +// MyLib +public struct Vector { + +} + +// MyExecutable main.swift +import MyLib + +print(Vector.self) // OK + +// error: generic type 'Vector' specialized with too many type parameters +// (got 2, but expected 1) +print(Vector<3, Int>.self) + +// MyExecutable test.swift + +// error: generic type 'Vector' specialized with too few type parameters +// (got 1, but expected 2) +print(Vector.self) +``` + +And here, we exercise that a module with its own `Vector`, like `MyLib`, will +always prefer its own definition within the module, but even for dependents +who import `MyLib` it will prefer `MyLib.Vector`. For files that don't +explicitly `MyLib`, it will prefer `Swift.Vector`. + +## ABI compatibility + +`Vector` is a brand new type in the standard library, so ABI should still be +compatible. + +## Implications on adoption + +This is a brand new type which means there will be deployment version +requirement to be able to use this type, especially considering it is using new +runtime features from integer generics. + +## Future directions + +### `Equatable`, `Hashable`, `CustomStringConvertible`, and other protocols. + +There are a wide class of protocols that this type has the ability to conform to, +but the issue is that it can only conform to them when the element conforms to +them (this is untrue for `CustomStringConvertible`, but it still requires +copyability). We could introduce these conformances but have them be conditional +right now and generalize it later when we generalize these protocols, but if we +were to ship say Swift X.Y with: + +```swift +@available(SwiftStdlib X.Y) +extension Vector: Equatable where Element: Equatable // & Element: Copyable +``` + +and later down the road in Swift X.(Y + 1): + +```swift +@available(SwiftStdlib X.Y) +extension Vector: Equatable where Element: ~Copyable & Equatable +``` + +Suddenly, this availability isn't quite right because the conformance that +shipped in Swift X.Y doesn't support noncopyable elements. To prevent the +headache of this and any potential new availability feature, we're holding off on +these conformances until they are fully generalized. + +### `Sequence` and `Collection` + +Similarly, we aren't conforming to `Sequence` or `Collection` either. +While we could conform to these protocols when the element is copyable, `Vector` +is unlike `Array` in that there are no copy-on-write semantics; it is eagerly +copied. Conforming to these protocols would potentially open doors to lots of +implicit copies of the underlying vector instance which could be problematic +given the prevalence of generic collection algorithms and slicing behavior. To +avoid this potential performance pitfall, we're explicitly not opting into +conforming this type to `Sequence` or `Collection`. + +We do plan to propose new protocols that look like `Sequence` and `Collection` +that avoid implicit copying making them suitable for types like `Vector` and +containers of noncopyable elements. +[SE-0437 Noncopyable Standard Library Primitives](0437-noncopyable-stdlib-primitives.md) +goes into more depth about this rationale and mentions that creating new +protocols to support noncopyable containers with potentially noncopyable +elements are all marked as future work. + +Much of the `Collection` API that we are generalizing here for this type are all +API we feel confident will be included in any future container protocol. Even if +we find that to not be the case, they are still useful API outside of generic +collection contexts in their own right. + +Remember, one can still iterate a `Vector` instance with the usual `indices` +property (which is what noncopyable vector instances would have had to deal with +regardless until new container protocols have been proposed): + +```swift +let atomicInts: Vector<3, Atomic> = [Atomic(1), Atomic(2), Atomic(3)] + +for i in atomicInts.indices { + print(atomicInts[i].load(ordering: .relaxed)) +} +``` + +### `Span` APIs + +With the recent proposal +[SE-0447 Span: Safe Access to Contiguous Storage](0447-span-access-shared-contiguous-storage.md) +who defines a safe abstraction over viewing contiguous storage, it would make +sense to define API on `Vector` to be able to get one of these `Span`s. However, +the proposal states that: + +> We could provide `withSpan()` and `withBytes()` closure-taking functions as +> safe replacements for the existing `withUnsafeBufferPointer()` and +> `withUnsafeBytes()` functions. We could also also provide lifetime-dependent +> `span` or `bytes` properties. +> ... +> Of these, the closure-taking functions can be implemented now, but it is +> unclear whether they are desirable. The lifetime-dependent computed properties +> require lifetime annotations, as initializers do. We are deferring proposing +> these extensions until the lifetime annotations are proposed. + +All of which is exactly true for the current `Vector` type. We could propose a +`withSpan` style API now, but it's unclear if that's what we truly want vs. a +computed property that returns the span which requires lifetime annotation +features. For now, we're deferring such API until a lifetime proposal is +proposed and accepted. + +### `ExpressibleByVectorLiteral` + +While the proposal does propose a literal initialization for `Vector` that +doesn't use `ExpressibleByArrayLiteral`, we are intentionally not exposing some +`ExpressibleByVectorLiteral` or similar. It's unclear what this protocol would +look like because each design has a different semantic guarantee: + +```swift +public protocol ExpressibleByVectorLiteral: ~Copyable { + associatedtype Element: ~Copyable + + init(vectorLiteral: consuming Vector) +} +``` + +This naive approach would satisfy a lot of types like `Array`, `Set`, +some hypothetical future noncopyable array, etc. These types actually want a +generic count and can allocate just enough space to hold all of those elements. + +However, this shape doesn't quite work for `Vector` itself because initializing +a `Vector<4, Int>` should require that the literal has exactly 4 elements. Note +that we wouldn't be able to impose a new constraint just for the conformer, so +`Vector` couldn't require that `N == count` and still have this witness the +requirement. Similarly, a `Pair` type could be vector initialized, but only if +the vector has exactly 2 elements. If we had the ability to define +`associatedvalue`, then this makes the conformance pretty trivial for both of +these types: + +```swift +public protocol ExpressibleByVectorLiteral: ~Copyable { + associatedtype Element: ~Copyable + associatedvalue count: Int + + init(vectorLiteral: consuming Vector) +} + +extension Vector: ExpressibleByVectorLiteral { + init(vectorLiteral: consuming Vector) { ... } +} + +extension Pair: ExpressibleByVectorLiteral { + init(vectorLiteral: consuming Vector<2, Element>) { ... } +} +``` + +But even with this design it's unsuitable for `Array` itself because it doesn't +want a static count for the literal, it still wants it to be generic. + +It would be nice to define something like this either on top of `Vector`, +parameter packs, or something else that would let us define statically the +number of elements we need for literal initialization or be dynamic if we opt to. + +### `InlineArray` and `SmallArray` + +In the same vein as this type, it may make sense to introduce some `InlineArray` +type which would support appending and removing elements given a fixed-capacity. + +```swift +var numbers: InlineArray<4, Int> = [1, 2] +print(numbers.capacity) // 4 +print(numbers.count) // 2 +numbers.append(3) +print(numbers.count) // 3 +numbers.append(4) +print(numbers.count) // 4 +numbers.append(5) // error: not enough space +``` + +This type is significantly different than the type we're proposing because +`Vector` defines a fixed-size meaning you cannot append or remove from it, but +it also requires that every single element is initialized. There must never be +an uninitialized element within a `Vector`, however for `InlineArray` this is +not true. It would act as a regular array with an initialized prefix and an +uninitialized suffix, it would be inline allocated (stack allocated for locals, +heap allocated if it's a class member, etc.), and it would not be growable. + +The difficulty in proposing such a type right now is that we have no way of +informing the compiler what parts of `InlineArray` are initialized and what +parts are not. This is critical for copy operations, move operations, and +destroy operations. Assuming that an uninitialized element is initialized and +attempting to perform any of these operations on it may lead to runtime crashes +which is definitely undesirable. + +Once we have `InlineArray` and some hypothetical noncopyable heap allocated +array type (which [SE-0437 Noncopyable Standard Library Primitives](0437-noncopyable-stdlib-primitives.md) +dons as `HypoArray` as a placeholder), it should be very trivial to define a +`SmallArray` type similar to the one found in LLVM APIs `llvm::SmallVector`. + +```swift +public enum SmallArray: ~Copyable { + case small(InlineArray) + case large(HypoArray) +} +``` + +which would act as an inline allocated array until one out grew the inline +capacity and would fall back to a dynamic heap allocation. + +### Syntax sugar + +We feel that this type will become as fundamental as `Array` and `Dictionary` +both of which have syntactic sugar for declaring a type of them, `[T]` for +`Array` and `[K: V]` for `Dictionary`. It may make sense to define something +similar for `Vector`, however we leave that as a future direction as the +spelling for such syntax is not critical to landing this type. + +It should be fairly trivial to propose such a syntax in the future either via a +new proposal, or as an amendment to this one. Such a change should only require +a newer compiler that supports the syntax and nothing more. + +Some syntax suggestions: + +* `[N x T]` or `[T x N]` +* `[N * T]` or `[T * N]` +* `T[N]` (from C) +* `[T; N]` (from Rust) + +Note that it may make more sense to have the length appear before the type. I +discuss this more in depth in [Reorder the generic arguments](#reorder-the-generic-arguments-vectort-n-instead-of-vectorn-t). + +## Alternatives considered + +### A name other than `Vector` + +For obvious reasons, we cannot name this type `Swift.Array` to match the +"term of art" that other languages like C, C++, and Rust are using for this +exact type. However, while this name is the de facto for other languages, it +actually mischaracterizes the properties and behaviors of this type considering +existing terminology in mathematics. A. Stepanov mentions in his book, "From +Mathematics to Generic Programming", that using the name `std::vector` for their +dynamically allocated growable array type was perhaps a mistake for this same +reason: + +> If we are coming up with a name for something, or overloading an existing name, +> we should follow these three guidelines: +> +> 1. If there is an established term, use it. +> 2. Do not use an established term inconsistently with its accepted meaning. In +> particular, overload an operator or function name only when you will be +> preserving its existing semantics. +> 3. If there are conflicting usages, the much more established one wins. +> +> The name _vector_ in STL was taken from the earlier programming languages +> Scheme and Common Lisp. Unfortunately, this was inconsistent with the much +> older meaning of the term in mathematics and violates Rule 3; this data structure +> should have been called _array_. Sadly, if you make a mistake and violate these +> principles, the result might stay around for a long time. +> +> \- Stepanov, A. A., Rose, D. E. (2014). _From Mathematics to Generic Programming_. United Kingdom: Addison-Wesley. + +Indeed, the `std::vector` type goes against the definition of vector by being a +growable container, having a non-fixed magnitude. + +We fully acknowledge that the Swift types, `Swift.Array` and `Swift.Vector`, are +complete opposites of the C++ ones, `std::vector` and `std::array`. While it may +be confusing at first, ultimately we feel that our names are more in line with +the mathematical term of art. + +If there was any type we could add to the standard library whose name could be +`Vector`, it must be this one. + +### Reorder the generic arguments (`Vector` instead of `Vector`) + +If we directly followed existing APIs from C++, then obviously the length should +follow the element type. However we realized that when reading this type aloud, +it's "a vector of 3 integers" for example instead of "a vector of integers of +size 3". It gets more interesting the more dimensions you add. +Consider an MxN matrix. In C, you'd write this as `T[N][M]` but index it as +`[m][n]`. We don't want to introduce that sort of confusion (which is a good +argument against `T[N]` as a potential syntax sugar for this type), so the +length being before the underlying element makes the most sense at least for any +potential sugared form. `[M * [N * T]]` would be indexed directly as it is spelt +out in the sugared form, `[m][n]`. In light of that, we wouldn't want the sugar +form to have a different ordering than the generic type itself leading us to +believe that the length must be before the element type. + +## Acknowledgments + +I would like the thank the following people for helping in the design process +of this type: + +* Karoy Lorentey +* Guillaume Lessard +* Joe Groff +* Kuba Mracek +* Andrew Trick +* Erik Eckstein +* Philippe Hausler +* Tim Kientzle From e273da0a1b7414fae9aaceb439715493de54a472 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 13 Nov 2024 22:08:55 -0500 Subject: [PATCH 3928/4563] Link active review (#2617) --- proposals/0453-vector.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0453-vector.md b/proposals/0453-vector.md index 6675ef5b52..cc8d33aafe 100644 --- a/proposals/0453-vector.md +++ b/proposals/0453-vector.md @@ -6,7 +6,7 @@ * Status: **Active review (November 13...November 27, 2024)** * Roadmap: [Approaches for fixed-size arrays](https://forums.swift.org/t/approaches-for-fixed-size-arrays/58894) * Implementation: [swiftlang/swift#76438](https://github.com/swiftlang/swift/pull/76438) -* Review: ([pitch](https://forums.swift.org/t/vector-a-fixed-size-array/75264)) +* Review: ([pitch](https://forums.swift.org/t/vector-a-fixed-size-array/75264)) ([review](https://forums.swift.org/t/se-0453-vector-a-fixed-size-array/76004)) ## Introduction From bdf5f529179d73d268bbb060da2fe0311f7204b5 Mon Sep 17 00:00:00 2001 From: James Dempsey Date: Sun, 24 Nov 2024 07:03:40 -0800 Subject: [PATCH 3929/4563] Normalize SE-0451 author and previous proposal fields --- proposals/0451-escaped-identifiers.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0451-escaped-identifiers.md b/proposals/0451-escaped-identifiers.md index 6bdc158b45..6d01e7035f 100644 --- a/proposals/0451-escaped-identifiers.md +++ b/proposals/0451-escaped-identifiers.md @@ -1,10 +1,11 @@ # Raw identifiers * Proposal: [SE-0451](0451-escaped-identifiers.md) -* Author: [Tony Allevato](https://github.com/allevato); based on [SE-0275](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md) by [Alfredo Delli Bovi](https://github.com/adellibovi) +* Author: [Tony Allevato](https://github.com/allevato) * Review Manager: [Joe Groff](https://github.com/jckarter) * Status: **Active review (October 24...November 7, 2024)** * Implementation: [swiftlang/swift#76636](https://github.com/swiftlang/swift/pull/76636), [swiftlang/swift-syntax#2857](https://github.com/swiftlang/swift-syntax/pull/2857) +* Previous Proposal: [SE-0275](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md) * Review: ([pitch](https://forums.swift.org/t/pitch-revisiting-backtick-delimited-identifiers-that-allow-more-non-identifier-characters/74432)) ## Introduction From f94974346835998d61cd6d4d1cb5b3170b9419f2 Mon Sep 17 00:00:00 2001 From: Mateus Rodrigues Date: Thu, 28 Nov 2024 02:52:23 -0300 Subject: [PATCH 3930/4563] SE-0439: Update to address acceptance decision (#2623) * SE-0439: Update to address acceptance decision * Update 0439-trailing-comma-lists.md * Update SE-0439 with link to previous revision --------- Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- proposals/0439-trailing-comma-lists.md | 191 ++++++++++++------------- 1 file changed, 95 insertions(+), 96 deletions(-) diff --git a/proposals/0439-trailing-comma-lists.md b/proposals/0439-trailing-comma-lists.md index 54b345f8a5..eafd42b52b 100644 --- a/proposals/0439-trailing-comma-lists.md +++ b/proposals/0439-trailing-comma-lists.md @@ -4,13 +4,14 @@ * Author: [Mateus Rodrigues](https://github.com/mateusrodriguesxyz) * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Accepted with modifications** -* Implementation: https://github.com/swiftlang/swift/pull/74522# gated behind `-enable-experimental-feature TrailingComma` +* Implementation: `main` https://github.com/swiftlang/swift/pull/74522# * Previous Proposal: [SE-0084](0084-trailing-commas.md) * Review: ([pitch](https://forums.swift.org/t/pitch-allow-trailing-comma-in-tuples-arguments-and-if-guard-while-conditions/70170)), ([review](https://forums.swift.org/t/se-0439-allow-trailing-comma-in-comma-separated-lists/72876)), ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0439-allow-trailing-comma-in-comma-separated-lists/73216)) +* Previous Revision: ([1](https://github.com/swiftlang/swift-evolution/blob/7864fa20cfb3a43aa6874feedb5aedb8be02da2c/proposals/0439-trailing-comma-lists.md)) ## Introduction -This proposal aims to allow the use of trailing commas, currently restricted to array and dictionary literals, in comma-separated lists whenever there are terminators that enable unambiguous parsing. +This proposal aims to allow the use of trailing commas, currently restricted to array and dictionary literals, in symmetrically delimited comma-separated lists. ## Motivation @@ -48,7 +49,7 @@ The language has also seen the introduction of [parameter packs](https://github. ## Proposed solution -This proposal adds support for trailing commas in comma-separated lists when there's a clear terminator, which are the following: +This proposal adds support for trailing commas in symmetrically delimited comma-separated lists, which are the following: - Tuples and tuple patterns. @@ -66,7 +67,7 @@ This proposal adds support for trailing commas in comma-separated lists when the ) = velocity ``` -- Parameter and argument lists of initializers, functions, enum associated values, expression macros, attributes, and availability specs. +- Parameter and argument lists of initializers, functions, enum associated values, expression macros and attributes. ```swift func foo( @@ -114,10 +115,6 @@ This proposal adds support for trailing commas in comma-separated lists when the ) } - if #unavailable( - iOS 15, - watchOS 9, - ) { } ``` - Subscripts, including key path subscripts. @@ -139,24 +136,67 @@ This proposal adds support for trailing commas in comma-separated lists when the ]) ``` +- Closure capture lists. + + ```swift + { [ + capturedValue1, + capturedValue2, + ] in + } + ``` + +- Generic parameters. + + ```swift + struct S< + T1, + T2, + T3, + > { } + ``` + +- String interpolation. + + ```swift + let s = "\(1, 2,)" + ``` + +## Detailed Design + +Trailing commas will be supported in comma-separated lists when symmetric delimiters (including `(...)`, `[...]`, and `<...>`) enable unambiguous parsing. + +Note that the requirement for symmetric delimiters means that the following cases will not support trailing comma: + - `if`, `guard` and `while` condition lists. ```swift if condition1, - condition2, + condition2, ❌ { } while condition1, - condition2, + condition2, ❌ { } guard condition1, - condition2, + condition2, ❌ else { } ``` + +- Enum case label lists. + + ```swift + enum E { + case + a, + b, + c, ❌ + } + ``` - `switch` case labels. @@ -164,139 +204,90 @@ This proposal adds support for trailing commas in comma-separated lists when the switch number { case 1, - 2, + 2, ❌ : ... default: .. } ``` - -- Closure capture lists. - - ```swift - { [ - capturedValue1, - capturedValue2, - ] in - } - ``` - + - Inheritance clauses. ```swift struct S: P1, P2, - P3, + P3, ❌ { } ``` -- Generic parameters. - - ```swift - struct S< - T1, - T2, - T3, - > { } - ``` - - Generic `where` clauses. ```swift struct S< T1, T2, - T3 + T3, > where T1: P1, - T2: P2, + T2: P2, ❌ { } ``` -- String interpolation - - ```swift - let s = "\(1, 2,)" - ``` - -## Detailed Design - -Trailing commas will be supported in comma-separated lists whenever there is a terminator clear enough that the parser can determine the end of the list. The terminator can be the symbols like `)`, `]`, `>`, `{` and `:`, a keyword like `where` or a pattern code like the body of a `if`, `guard` and `while` statement. - -Note that the requirement for a terminator means that the following cases will not support trailing comma: - -Enum case label lists: +Trailing commas will be allowed in single-element lists but not in zero-element lists, since the trailing comma is actually attached to the last element. +Supporting a zero-element list would require supporting _leading_ commas, which isn't what this proposal is about. ```swift -enum E { - case - a, - b, - c, // ❌ Expected identifier after comma in enum 'case' declaration -} -``` - -Inheritance clauses for associated types in a protocol declaration: - -```swift -protocol Foo { - associatedtype T: - P1, - P2, // ❌ Expected type -} +(1,) // OK +(,) ❌ expected value in tuple ``` -Generic `where` clauses for initializers and functions in a protocol declaration: -```swift -protocol Foo { - func f(a: T1, b: T2) where - T1: P1, - T2: P2, // ❌ Expected type -} -``` +## Source compatibility -Trailing commas will be allowed in single-element lists but not in zero-element lists, since the trailing comma is actually attached to the last element. Supporting a zero-element list would require supporting _leading_ commas, which isn't what this proposal is about. +This is a purely additive change with no source compatibility impact. -```swift -(1,) // OK -(,) // ❌ expected value in tuple -``` +## Alternatives considered +### Allow trailing comma in all comma-separated lists -## Source compatibility +Comma-separated lists that are not symmetrically delimited could also benefit from trailing comma support; for example, condition lists, in which reordering is fairly common. +However, these lists currently rely on the comma after the penultimate element to determine that what comes next is the last element, and some of them present challenges if relying on opening/closing delimiters instead. -Although this change won't impact existing valid code it will change how some invalid code is parsed. Consider the following: +At first sight, `{` may seem a reasonable closing delimiter for `if` and `while` condition lists, but conditions can have a `{` themselves. ```swift if - condition1, - condition2, -{ // ❌ Function produces expected type 'Bool'; did you mean to call it with '()'? - return true -} - -{ print("something") } + condition1, + condition2, + { true }(), +{ } ``` -Currently the parser uses the last comma to determine that whatever follows is the last condition, so `{ return true }` is a condition and `{ print("something") }` is the `if` body. +This particular case can be handled but, given how complex conditions can be, it's hard to conclude that there's absolutely no corner case where ambiguity can arise in currently valid code. -With trailing comma support, the parser will terminate the condition list before the first block that is a valid `if` body, so `{ return true }` will be parsed as the `if` body and `{ print("something") }` will be parsed as an unused closure expression. +Inheritance lists and generic `where` clauses can appear in protocol definitons where there's no clear delimiter, making it harder to disambiguate where the list ends. ```swift -if - condition1, - condition2, -{ - return true +protocol Foo { + associatedtype T: + P1, + P2, ❌ Expected type + ... } +``` -{ print("something") } // ❌ Closure expression is unused +```swift +protocol Foo { + associatedtype T: + P1, + P2, ❌ Expected type + ... +} ``` -## Alternatives considered +Although some comma-separated lists without symmetric delimiters may have a clear terminator in some cases, this proposal restricts trailing comma support to symmetrically delimited ones where it's clear that the presence of a trailing comma will not cause parsing ambiguity. ### Eliding commas @@ -312,3 +303,11 @@ print( This was even [proposed](https://forums.swift.org/t/se-0257-eliding-commas-from-multiline-expression-lists/22889/188) and returned for revision back in 2019. The two approaches are not mutually exclusive. There remain unresolved questions about how the language can accommodate elided commas, and adopting this proposal does not prevent that approach from being considered in the future. + +## Revision History + +- Update to address acceptance decision of restricting trailing comma to lists with symmetric delimiters. + +## Acknowledgments + +Thanks to Alex Hoppen, Xiaodi Wu and others for their help on the proposal text and implementation. From df4ff729fcf9b48c0ea8d0971ac1aecdd91b1d94 Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Wed, 4 Dec 2024 20:53:17 +0100 Subject: [PATCH 3931/4563] [SE-0314] Fix typo The usage of "`AsyncSeries`" looks like a typo. The term doesn't appear anywhere else in the document. The document does use the word "series" a few times as an alternative to "stream", but using it as an identifier seems wrong. I replaced "`AsyncSeries`" with "`AsyncThrowingStream`" because it appears in the section on `AsyncThrowingStream`. Replacing it with "`AsyncStream`" would also work, but since this section is about the throwing variant, `AsyncThrowingStream` seems better. --- proposals/0314-async-stream.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0314-async-stream.md b/proposals/0314-async-stream.md index 6f86e52151..3abb7024f4 100644 --- a/proposals/0314-async-stream.md +++ b/proposals/0314-async-stream.md @@ -135,7 +135,7 @@ Alternatively if a source is just an async function (one that represents a backp ### Creating an `AsyncThrowingStream` -Along with the potentially infinite sequence in the example above, `AsyncSeries` can also adapt APIs like the slightly contrived one below. The `findVegetables` function uses callback closures that are called with each retrieved vegetable, as well as when the vegetables have all been returned or an error occurs. +Along with the potentially infinite sequence in the example above, `AsyncThrowingStream` can also adapt APIs like the slightly contrived one below. The `findVegetables` function uses callback closures that are called with each retrieved vegetable, as well as when the vegetables have all been returned or an error occurs. ```swift func buyVegetables( From dd9af5e9a0a91309a41dfa34e9c9fdf4330e4699 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Wed, 4 Dec 2024 14:54:43 -0800 Subject: [PATCH 3932/4563] SE-0451 is accepted --- proposals/0451-escaped-identifiers.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0451-escaped-identifiers.md b/proposals/0451-escaped-identifiers.md index 6d01e7035f..6971096741 100644 --- a/proposals/0451-escaped-identifiers.md +++ b/proposals/0451-escaped-identifiers.md @@ -3,10 +3,11 @@ * Proposal: [SE-0451](0451-escaped-identifiers.md) * Author: [Tony Allevato](https://github.com/allevato) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Active review (October 24...November 7, 2024)** +* Status: **Accepted** * Implementation: [swiftlang/swift#76636](https://github.com/swiftlang/swift/pull/76636), [swiftlang/swift-syntax#2857](https://github.com/swiftlang/swift-syntax/pull/2857) * Previous Proposal: [SE-0275](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-revisiting-backtick-delimited-identifiers-that-allow-more-non-identifier-characters/74432)) +* Review: ([pitch](https://forums.swift.org/t/pitch-revisiting-backtick-delimited-identifiers-that-allow-more-non-identifier-characters/74432), [review](https://forums.swift.org/t/se-0451-raw-identifiers/75602), [acceptance](https://forums.swift.org/t/accepted-with-revision-se-0451-raw-identifiers/76387)) + ## Introduction From 104a422ff09c7be1ce6c793acad2c97f9397ab08 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 5 Dec 2024 20:24:45 -0800 Subject: [PATCH 3933/4563] Update vector after review feedback (#2626) * Update 0453-vector.md * Fix documentation of the (first:next:) initializer * Remove syntax sugar --- proposals/0453-vector.md | 248 +++++++++++++++------------------------ 1 file changed, 94 insertions(+), 154 deletions(-) diff --git a/proposals/0453-vector.md b/proposals/0453-vector.md index cc8d33aafe..0d413c86a2 100644 --- a/proposals/0453-vector.md +++ b/proposals/0453-vector.md @@ -92,27 +92,6 @@ func complexAlgorithm() -> Int { } ``` -This type also serves as a safer replacement to `withUnsafeTemporaryAllocation` -when the amount of things to reserve is known statically: - -```swift -// Previously -withUnsafeTemporaryAllocation(of: Int.self, capacity: 16) { - $0.initialize(repeating: 0) - - for i in $0.indices { - $0[i] = compute() - } -} - -// Now -var ints = Vector<16, Int>(repeating: 0) - -for i in ints.indices { - ints[i] = compute() -} -``` - Vectors of noncopyable values will be possible by using any of the closure based taking initializers or the literal initializer: @@ -120,16 +99,12 @@ taking initializers or the literal initializer: // [Atomic(0), Atomic(1), Atomic(2), Atomic(3)] let incrementingAtomics = Vector<4, Atomic> { i in Atomic(i) -} // [Atomic(0), Atomic(1), Atomic(2), Atomic(3)] - -// [Atomic(337523057234), Atomic(8726493873498347), Atomic(98573472834767364), -// Atomic(72394735763974)] (maybe) -let randomAtomics = Vector<4, _>(expand: RNG()) { Atomic($0.next()) } +} // [Sprite(), Sprite(), Sprite(), Sprite()] -// Where the 2nd, 3rd, and 4th elements are all copies of the initial first -// value -let copiedSprites = Vector<4, _>(unfold: Sprite()) { $0.copy() } +// Where the 2nd, 3rd, and 4th elements are all copies of their previous +// element. +let copiedSprites = Vector<4, _>(first: Sprite()) { $0.copy() } // Inferred to be Vector<3, Mutex> let literalMutexes: Vector = [Mutex(0), Mutex(1), Mutex(2)] @@ -299,44 +274,25 @@ extension Vector where Element: ~Copyable { public init(with next: (Int) throws(E) -> Element) throws(E) /// Initializes every element in this vector by running the closure with the - /// passed in mutable state. - /// - /// This will call the closure 'count' times, where 'count' is the static - /// count of the vector, to initialize every element by passing the closure - /// an inout reference to the passed state. The closure is allowed to throw - /// an error at any point during initialization at which point the vector will - /// stop initialization, deinitialize every currently initialized element, and - /// throw the given error back out to the caller. - /// - /// - Parameter state: The mutable state that can be altered during each - /// iteration of the closure initializing the vector. - /// - Parameter next: A closure that passes in an inout reference to the - /// user given mutable state which returns an owned - /// `Element` instance to insert into the vector. - public init( - expand state: consuming State, - with next: (inout State) throws(E) -> Element - ) throws(E) - - /// Initializes every element in this vector by running the closure with the - /// passed in first element. + /// previously initialized element. /// /// This will call the closure 'count' times, where 'count' is the static /// count of the vector, to initialize every element by passing the closure - /// an immutable borrow reference to the first element given to the - /// initializer. The closure is allowed to throw an error at any point during - /// initialization at which point the vector will stop initialization, - /// deinitialize every currently initialized element, and throw the given - /// error back out to the caller. + /// an immutable borrow reference to the previously initialized element. The + /// closure is allowed to throw an error at any point during initialization at + /// which point the vector will stop initialization, deinitialize every + /// currently initialized element, and throw the given error back out to the + /// caller. /// /// - Parameter first: The first value to insert into the vector which will be /// passed to the closure as a borrow. /// - Parameter next: A closure that passes in an immutable borrow reference - /// of the given first element of the vector which returns - /// an owned `Element` instance to insert into the vector. + /// of the previously initialized element of the vector + /// which returns an owned `Element` instance to insert into + /// the vector. public init( - unfold first: consuming Element, - with next: (borrowing Element) throws(E) -> Element + first: consuming Element, + next: (borrowing Element) throws(E) -> Element ) throws(E) } @@ -364,7 +320,6 @@ collection type. extension Vector where Element: ~Copyable { public typealias Element = Element public typealias Index = Int - public typealias Indices = Range /// Provides the count of the collection statically without an instance. public static var count: Int { count } @@ -378,11 +333,6 @@ extension Vector where Element: ~Copyable { public borrowing func index(after i: Int) -> Int public borrowing func index(before i: Int) -> Int - public borrowing func reduce( - into initialResult: consuming Result, - _ updateAccumulatingResult: (inout Result, borrowing Element) throws(E) -> () - ) throws(E) -> Result - public mutating func swapAt( _ i: Int, _ j: Int @@ -392,95 +342,6 @@ extension Vector where Element: ~Copyable { } ``` -### C Interop changes - -With the introduction of `Vector`, we have a unique opportunity to fix another -pain point within the language with regards to C interop. Currently, the Swift -compiler imports a C array of type `T[24]` as a tuple of `T` with 24 elements. -Previously, this was really the only representation that the compiler could pick -to allow interfacing with C arrays. It was a real challenge working with these -fields from C in Swift. Consider the following C struct: - -```c -struct section_64 { - char sectname[16]; - char segname[16]; - uint64_t addr; - uint64_t size; - uint32_t offset; - uint32_t align; - ... -}; -``` - -Today, this gets imported as the following Swift struct: - -```swift -struct section_64 { - let sectname: (CChar, CChar, CChar, CChar, CChar, CChar, ... 10 more times) - let segname: (CChar, CChar, CChar, CChar, CChar, CChar, ... 10 more times) - let addr: UInt64 - let size: UInt64 - let offset: UInt32 - let align: UInt32 - ... -} -``` - -Using an instance of `section_64` in Swift for the most part is really easy. -Accessing things like `addr` or `size` are simple and easy to use, but using the -`sectname` property introduces a level of complexity that isn't so fun to use. - -```swift -func getSectionName(_ section: section_64) -> String { - withUnsafePointer(to: section.sectname) { - // This is unsafe! 'sectname' isn't guaranteed to have a null byte - // indicating the end of the C string! - String(cString: $0) - } -} - -func iterateSectionNameBytes(_ section: section_64) { - withUnsafeBytes(to: section.sectname) { - for byte in $0 { - ... - } - } -} -``` - -Having to resort to using very unsafe API to do anything useful with imported C -arrays is not something a memory safe language like Swift should be in the -business of. `Vector` allows us to clean up this sore spot in a very elegant and -much more importantly, safe way. - -We plan to introduce a new _upcoming_ feature that folks can enable whenever -they'd like to via `-enable-upcoming-feature ImportCArraysAsVectors`. This will -change the current behavior of importing C arrays as tuples, to instead import -them as `Vector`. For the previous example, we'd instead generate the following: - -```swift -struct section_64 { - let sectname: Vector<16, CChar> - let segname: Vector<16, CChar> - let addr: UInt64 - let size: UInt64 - let offset: UInt32 - let align: UInt32 - ... -} -``` - -By introducing an upcoming feature, it lets folks who are ready to migrate their -codebase using a C interface to use `Vector` entirely, while still allowing -dependencies or dependents to use the old behavior with the same C interface. - -In a future Swift language mode, we plan to make this behavior the default and -drop importing C array fields as tuples and only as `Vector`. This approach -allows developers to get the new behavior early if they'd like to use it, which -will make the language mode transition much smoother because there would be no -source break with regards to this feature. - ## Source compatibility `Vector` is a brand new type in the standard library, so source should still be @@ -758,6 +619,85 @@ Some syntax suggestions: Note that it may make more sense to have the length appear before the type. I discuss this more in depth in [Reorder the generic arguments](#reorder-the-generic-arguments-vectort-n-instead-of-vectorn-t). +### C Interop changes + +With the introduction of `Vector`, we have a unique opportunity to fix another +pain point within the language with regards to C interop. Currently, the Swift +compiler imports a C array of type `T[24]` as a tuple of `T` with 24 elements. +Previously, this was really the only representation that the compiler could pick +to allow interfacing with C arrays. It was a real challenge working with these +fields from C in Swift. Consider the following C struct: + +```c +struct section_64 { + char sectname[16]; + char segname[16]; + uint64_t addr; + uint64_t size; + uint32_t offset; + uint32_t align; + ... +}; +``` + +Today, this gets imported as the following Swift struct: + +```swift +struct section_64 { + let sectname: (CChar, CChar, CChar, CChar, CChar, CChar, ... 10 more times) + let segname: (CChar, CChar, CChar, CChar, CChar, CChar, ... 10 more times) + let addr: UInt64 + let size: UInt64 + let offset: UInt32 + let align: UInt32 + ... +} +``` + +Using an instance of `section_64` in Swift for the most part is really easy. +Accessing things like `addr` or `size` are simple and easy to use, but using the +`sectname` property introduces a level of complexity that isn't so fun to use. + +```swift +func getSectionName(_ section: section_64) -> String { + withUnsafePointer(to: section.sectname) { + // This is unsafe! 'sectname' isn't guaranteed to have a null byte + // indicating the end of the C string! + String(cString: $0) + } +} + +func iterateSectionNameBytes(_ section: section_64) { + withUnsafeBytes(to: section.sectname) { + for byte in $0 { + ... + } + } +} +``` + +Having to resort to using very unsafe API to do anything useful with imported C +arrays is not something a memory safe language like Swift should be in the +business of. + +Ideally we could migrate the importer from using tuples to this new `Vector` +type, however that would be massively source breaking. A previous revision of +this proposal proposed an _upcoming_ feature flag that modules can opt into, +but this poses issues with the current importer implementation with regards to +inlinable code. + +Another idea was to import struct fields with C array types twice, one with the +existing name with a tuple type (as to not break source) and another with some +`Vector` suffix in the name with the `Vector` type. This works pretty well for +struct fields and globals, but it leaves fields and functions who have pointers +to C arrays in question as well (spelt `char (*x)[4]`). Do we import such +functions twice using a similar method of giving it a different name? Such a +solution would also incur a longer deprecation period to eventually having just +`Vector` be imported and no more tuples. + +We're holding off on any C interop changes here as there are still lots of open +questions as to what the best path forward is. + ## Alternatives considered ### A name other than `Vector` From a0eb13728c54493f1cb9b1f10b00e18a12f925dd Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 5 Dec 2024 23:26:36 -0500 Subject: [PATCH 3934/4563] Return 0453 for revision (#2627) --- proposals/0453-vector.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0453-vector.md b/proposals/0453-vector.md index 0d413c86a2..d5c5dec1a5 100644 --- a/proposals/0453-vector.md +++ b/proposals/0453-vector.md @@ -6,7 +6,7 @@ * Status: **Active review (November 13...November 27, 2024)** * Roadmap: [Approaches for fixed-size arrays](https://forums.swift.org/t/approaches-for-fixed-size-arrays/58894) * Implementation: [swiftlang/swift#76438](https://github.com/swiftlang/swift/pull/76438) -* Review: ([pitch](https://forums.swift.org/t/vector-a-fixed-size-array/75264)) ([review](https://forums.swift.org/t/se-0453-vector-a-fixed-size-array/76004)) +* Review: ([pitch](https://forums.swift.org/t/vector-a-fixed-size-array/75264)) ([review](https://forums.swift.org/t/se-0453-vector-a-fixed-size-array/76004)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0453-vector-a-fixed-size-array/76411)) ## Introduction From fcd0e278e4cffd617796461dd66e1a5d09603604 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 5 Dec 2024 23:39:03 -0500 Subject: [PATCH 3935/4563] Add second review for 0453 (#2628) --- proposals/0453-vector.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0453-vector.md b/proposals/0453-vector.md index d5c5dec1a5..202e727a30 100644 --- a/proposals/0453-vector.md +++ b/proposals/0453-vector.md @@ -6,7 +6,7 @@ * Status: **Active review (November 13...November 27, 2024)** * Roadmap: [Approaches for fixed-size arrays](https://forums.swift.org/t/approaches-for-fixed-size-arrays/58894) * Implementation: [swiftlang/swift#76438](https://github.com/swiftlang/swift/pull/76438) -* Review: ([pitch](https://forums.swift.org/t/vector-a-fixed-size-array/75264)) ([review](https://forums.swift.org/t/se-0453-vector-a-fixed-size-array/76004)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0453-vector-a-fixed-size-array/76411)) +* Review: ([pitch](https://forums.swift.org/t/vector-a-fixed-size-array/75264)) ([review](https://forums.swift.org/t/se-0453-vector-a-fixed-size-array/76004)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0453-vector-a-fixed-size-array/76411)) ([second review](https://forums.swift.org/t/second-review-se-0453-vector-a-fixed-size-array/76412)) ## Introduction From 45cf74d364afdbce019add12692be18b0b60c980 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 5 Dec 2024 23:57:11 -0500 Subject: [PATCH 3936/4563] Review date fix for 0453 (#2629) --- proposals/0453-vector.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0453-vector.md b/proposals/0453-vector.md index 202e727a30..17e5f7924d 100644 --- a/proposals/0453-vector.md +++ b/proposals/0453-vector.md @@ -3,7 +3,7 @@ * Proposal: [SE-0453](0453-vector.md) * Authors: [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) -* Status: **Active review (November 13...November 27, 2024)** +* Status: **Active review (December 5...December 17, 2024)** * Roadmap: [Approaches for fixed-size arrays](https://forums.swift.org/t/approaches-for-fixed-size-arrays/58894) * Implementation: [swiftlang/swift#76438](https://github.com/swiftlang/swift/pull/76438) * Review: ([pitch](https://forums.swift.org/t/vector-a-fixed-size-array/75264)) ([review](https://forums.swift.org/t/se-0453-vector-a-fixed-size-array/76004)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0453-vector-a-fixed-size-array/76411)) ([second review](https://forums.swift.org/t/second-review-se-0453-vector-a-fixed-size-array/76412)) From 1699c5a245e6abba3da8efbecdbdb96ee24ebcdb Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 9 Dec 2024 14:48:07 -0800 Subject: [PATCH 3937/4563] Switch withSpan example over to the "storage" property currently being proposed --- visions/memory-safety.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/visions/memory-safety.md b/visions/memory-safety.md index add0b35dd5..4eb0ae9a25 100644 --- a/visions/memory-safety.md +++ b/visions/memory-safety.md @@ -87,17 +87,17 @@ One of the primary places where this doesn’t hold is with low-level access to * **Lifetime safety**: the unsafe buffer pointer should only be used within the closure, but there is no checking to establish that the pointer does not escape the closure. If it does escape, it could be used after the closure has returned and the pointer could have effectively been “freed.” * **Bounds safety**: the unsafe buffer pointer types do not perform bounds checking in release builds. -[Non-escapable types](https://github.com/swiftlang/swift-evolution/pull/2304) provide the ability to create types whose instances cannot escape out of the context in which they were created with no runtime overhead. Non-escapable types allow the creation of a [memory-safe counterpart to the unsafe buffer types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md), proposed under the name `Span` . With `Span` , it becomes possible to access contiguous memory in an array in a manner that maintains memory safety. For example: +[Non-escapable types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md) provide the ability to create types whose instances cannot escape out of the context in which they were created with no runtime overhead. Non-escapable types allow the creation of a [memory-safe counterpart to the unsafe buffer types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md), `Span` . With `Span` , it becomes possible to access contiguous memory in an array in a manner that maintains memory safety. For example: ```swift -myInts.withSpan { span in - globalSpan = span // error: span value cannot escape the closure - print(span[myArray.count]) // runtime error: out-of-bounds access - return span.first ?? 0 -} +let span = myInts.storage + +globalSpan = span // error: span value cannot escape the scope of myInts +print(span[myArray.count]) // runtime error: out-of-bounds access +print(span.first ?? 0) ``` -[Lifetime dependencies](https://github.com/swiftlang/swift-evolution/pull/2305) can greatly improve the expressiveness of non-escaping types, providing the ability to work with types like `Span` without requiring deeply-nested `with` blocks. Additionally, they make it possible to build more complex data structures out of non-escaping types, extending Swift’s capabilities while maintaining memory safety. +[Lifetime dependencies](https://github.com/swiftlang/swift-evolution/pull/2305) can greatly improve the expressiveness of non-escaping types, making it possible to build more complex data structures while maintaining memory safety. ## Expressing memory-safe interfaces for the C family of languages From 662120eb9fd70cdaf4aeefcb8eb6d2c9b1bfff0a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 9 Dec 2024 16:29:14 -0800 Subject: [PATCH 3938/4563] Introduce more about `lifetimebound` and C++ --- visions/memory-safety.md | 41 +++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/visions/memory-safety.md b/visions/memory-safety.md index 4eb0ae9a25..f1d3fa6211 100644 --- a/visions/memory-safety.md +++ b/visions/memory-safety.md @@ -101,14 +101,14 @@ print(span.first ?? 0) ## Expressing memory-safe interfaces for the C family of languages -The C family of languages do not provide memory safety along any of the dimensions described in this document. As such, a Swift program that makes use of C APIs is never fully “memory safe” in the strict sense, because any C code called from Swift could undermine the memory safety guarantees Swift is trying to provide. Requiring that all such C code be rewritten in Swift would go against Swift’s general philosophy of incremental adoption into existing ecosystems. Therefore, this document proposes a different strategy: code written in Swift will be auditably memory-safe so long as the C APIs it uses follow reasonable conventions with respect to memory safety. As such, writing new code (or incrementally rewriting code from the C family) will not introduce new memory safety bugs, so that adopting Swift in an existing code base will incrementally improve on memory safety. +The C family of languages do not provide memory safety along any of the dimensions described in this document. As such, a Swift program that makes use of C APIs is never fully “memory safe” in the strict sense, because any C code called from Swift could undermine the memory safety guarantees Swift is trying to provide. Requiring that all such C code be rewritten in Swift would go against Swift’s general philosophy of incremental adoption into existing ecosystems. Therefore, this document proposes a different strategy: code written in Swift will be auditably memory-safe so long as the C APIs it uses follow reasonable conventions with respect to memory safety. As such, writing new code (or incrementally rewriting code from the C family) will not introduce new memory safety bugs, so that adopting Swift in an existing code base will incrementally improve on memory safety. This approach is complementary to any improvements made to memory safety within the C family of languages, such as [bounds-safety checks for C](https://clang.llvm.org/docs/BoundsSafety.html) or [C++ standard library hardening](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3471r0.html). In the C family of languages, the primary memory safety issue for APIs is the widespread use of pointers that have neither lifetime annotations (who owns the pointer?) nor bounds annotations (how many elements does it point to?). As such, the pointers used in C APIs are reflected in Swift as unsafe pointer types, as shown above with `memcpy` . Despite the lack of this information, C APIs often follow a reasonable set of conventions that make them usable in Swift without causing memory-safety problems. Swift has a long history of utilizing annotations in C headers to describe these conventions and improve the projection of C APIs into Swift, including: * Nullability annotations (`_Nullable`, `_Nonnull`) that describe what values can be NULL, and affects whether a C type is reflected as optional in Swift. -* Non-escaping annotations (e.g., `__attribute__((noescape))`) on function/block pointer parameters, which results in them being imported as non-escaping function parameters. +* Non-escaping annotations (e.g., `__attribute__((noescape))`) on block pointer parameters, which results in them being imported as non-escaping function parameters. * `@MainActor` and `Sendable` annotations on C APIs that support Swift 6’s data-race safety model. To provide safer interoperability with C APIs, additional annotations can be provided in C that Swift can use to project those C APIs into Swift APIs without any use of unsafe pointers. For example, the Clang [bounds-safety attributes](https://clang.llvm.org/docs/BoundsSafety.html) allow one to express when a C pointer’s size is described by another value: @@ -148,18 +148,45 @@ The `average` function is now expressing that it takes in a `double` pointer ref func average(_ ptr: Span) -> Double ``` -More expressive Swift lifetime features can also have corresponding C annotations, allowing more C semantics to be reflected into safe APIs in Swift. For example, consider a C function that finds the minimal element in an array and returns a pointer to it: +More expressive Swift lifetime features can also have corresponding C annotations, allowing more C APIs to be reflected into safe APIs in Swift. For example, consider a C function that finds the minimal element in an array and returns a pointer to it: ```cpp const double *min_element(const double *__counted_by(N) __attribute__((noescape)) ptr, int N); ``` -The returned pointer will point into the buffer passed in, so its lifetime is tied to that of the pointer argument. The aforementioned [lifetime dependencies proposal](https://github.com/swiftlang/swift-evolution/pull/2305) allows this kind of dependency to be expressed in Swift, where the resulting non-escaping value (e.g., a `Span` containing one element) has its lifetime tied to the input argument. +The returned pointer will point into the buffer passed in, so its lifetime is tied to that of the pointer argument. The aforementioned [lifetime dependencies proposal](https://github.com/swiftlang/swift-evolution/pull/2305) allows this kind of dependency to be expressed in Swift, where the resulting non-escaping value (e.g., a `Span` containing one element) has its lifetime tied to the input argument. Clang provides a [`lifetimebound`](https://clang.llvm.org/docs/AttributeReference.html#id11) attribute that expresses when a return value refers into memory associated with one of the parameters, which offers one way to express this lifetime relationship for C APIs: -C++ offers a number of further opportunities for improved safety by modeling lifetimes. For example, `std::list` has a `front()` method that returns a reference to the element at the front of the list: +```c +const double * _Nullable __counted_by(1) +min_element(const double *__counted_by(N) __attribute__((noescape)) __attribute__((lifetimebound)) ptr, int N); +``` + +The result coudl be the following memory-safe Swift API: + +```swift +@lifetime(ptr) func min_element(_ ptr: Span) -> Span? +``` + +### Affordances for C++ + +C++ offers a number of further opportunities for improved safety by modeling lifetimes. For example, `std::vector` has a `front()` method that returns a reference to the element at the front of the vector: ```cpp -T& front(); +const T& front() const; +``` + +The returned reference is valid so long as the vector instance still exists and has not been modified since the call to `front()`. Describing that lifetime dependency in C++ (for example, with the aforementioned `lifetimebound` attribute) would lead to a safe mapping of this API into Swift without the need to introduce an extra copy of the returned element, improving both safety and, potentially, performance. + +The C++ [`std::span`](https://en.cppreference.com/w/cpp/container/span) type is similar to the Swift `Span` type, in that it also carries both a pointer and bounds to describe a region of memory. However, `std::span` doesn't provide lifetime safety, so it is essentially an unsafe type from the Swift perspective. The same C attributes that provide lifetime safety for C pointers and references could be applied to `std::span` instances to provide safe Swift projections of C++ APIs. For example, the following annotated C++ API: + +```c++ +std::span substring_match(std::span sequence [[clang::lifetimebound]], std::span subsequence [[clang::noescape]]); +``` + +could be imported into Swift as: + +```swift +@lifetime(sequence) +func substring_match(_ sequence: Span, _ subsequence: Span) -> Span ``` -The returned reference is valid so long as the list is valid, i.e., its lifetime depends on the `this` parameter. Describing that lifetime dependency in C++ can lead to a safe mapping of this API into Swift without the need to introduce an extra copy of the element. \ No newline at end of file From 347c367b7019dc0e73f35af0dfb77e4b85683840 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 9 Dec 2024 23:21:42 -0800 Subject: [PATCH 3939/4563] Be clear about how unsafe { ... } blocks or @safe(unchecked) actually work Also add a short paragraph on auditability that relates to the above --- visions/memory-safety.md | 53 +++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/visions/memory-safety.md b/visions/memory-safety.md index f1d3fa6211..eb43b3b7e6 100644 --- a/visions/memory-safety.md +++ b/visions/memory-safety.md @@ -74,11 +74,53 @@ func memcpy( The rules described above make it possible to detect and report the use of unsafe constructs in Swift. -An `@unsafe` function should be allowed to use other unsafe constructs without emitting any diagnostics. However, there are also library functions that encapsulate unsafe behavior in a safe API, such as the standard library’s `Array` and [`Span`](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md)that are necessarily built from unsafe primitives. Such functions need some way to acknowledge the unsafe behavior while still being considered safe from the outside, such as an `unsafe { ... }` code block or a `@safe(unchecked)` attribute. +An `@unsafe` function is allowed to use other unsafe constructs. As such, a Swift module compiled in the strictly-safe subset can contain both safe and unsafe code, but all unsafe code is marked by `@unsafe`. A client of the module can opt to use only the safe parts of that module, potentially using the strict safety checking to ensure this. + +### Wrapping unsafe behavior in safe APIs + +There should also be a way to wrap unsafe behavior into safe APIs. For example, the standard library's `Array` and [`Span`](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md) are necessarily implemented from unsafe primitives, such as `UnsafeRawPointer`, but expose primarily safe APIs. For example, the `Span` type could be defined like this: + +```swift +public struct Span: ~Escapable, Copyable, BitwiseCopyable { + internal let buffer: UnsafeBufferPointer +} +``` + +The subscript operation is safe, but necessarily uses `buffer`, which has an `@unsafe` type. Its implementation must acknowledge that it is using unsafe constructs internally, but that it does so in a manner that preserves safety for clients. There are several potential syntaxes, including an `unsafe { ... }` code block, which could look like this: + +```swift +public subscript(_ position: Int) -> Element { + get { + unsafe { + precondition(position >= 0 && position < buffer.count) + return buffer[position] + } + } +} +``` + +Alternatively, Swift could provide a `@safe(unchecked)` attribute that states that a particular API is safe, but that its safety cannot be checked by the compiler, akin to `@unchecked Sendable` conformances: + +```swift +public subscript(_ position: Int) -> Element { + @safe(unchecked) get { + precondition(position >= 0 && position < buffer.count) + return buffer[position] + } +} +``` + +The specific syntax chosen will be the subject of a specific proposal, and need not be determined by this vision document. Regardless, a Swift module that enables strict safety checking must limit its use of unsafe constructs to `@unsafe` declarations or those parts of the code that have acknowledged local use of unsafe constructs. + +### Auditability + +The aim of optional strict memory safety for Swift is to make it possible to write Swift that avoids unintentional use of unsafe constructs while not preventing their use entirely. To aid projects that wish to set a higher bar for memory safety, such as permitting no unsafe constructs outside of the standard library or requiring additional code review for any uses of unsafe constructs, Swift tooling should provide a way to audit the uses of unsafe constructs within an entire project (including its dependencies). An auditing tool should be able to identify and report Swift modules that were compiled without strict memory safety as well as all of the places where the opt-out mechanism (e.g., `unsafe { ... }` blocks or `@safe(unchecked)`) is used in modules that do opt in to strict memory safety. + +## Improving the expressibility of strictly-safe Swift The following sections describe language features and library constructs that improve on what can be expressed within the strictly-safe subset of Swift. These improvements will also benefit Swift in general, making it easier to correctly work with contiguous memory and interoperate with APIs from the C-family on languages. -## Accessing contiguous memory +### Accessing contiguous memory Nearly every “unsafe” language feature and standard library API described in the previous section already has safe counterparts in the language: safe concurrency patterns via actors and `Mutex` , safe casting via `as?` , runtime-checked access to optionals (via `!` ) and continuations (`withChecked(Throwing)Continuation` ), and so on. @@ -99,7 +141,7 @@ print(span.first ?? 0) [Lifetime dependencies](https://github.com/swiftlang/swift-evolution/pull/2305) can greatly improve the expressiveness of non-escaping types, making it possible to build more complex data structures while maintaining memory safety. -## Expressing memory-safe interfaces for the C family of languages +### Expressing memory-safe interfaces for the C family of languages The C family of languages do not provide memory safety along any of the dimensions described in this document. As such, a Swift program that makes use of C APIs is never fully “memory safe” in the strict sense, because any C code called from Swift could undermine the memory safety guarantees Swift is trying to provide. Requiring that all such C code be rewritten in Swift would go against Swift’s general philosophy of incremental adoption into existing ecosystems. Therefore, this document proposes a different strategy: code written in Swift will be auditably memory-safe so long as the C APIs it uses follow reasonable conventions with respect to memory safety. As such, writing new code (or incrementally rewriting code from the C family) will not introduce new memory safety bugs, so that adopting Swift in an existing code base will incrementally improve on memory safety. This approach is complementary to any improvements made to memory safety within the C family of languages, such as [bounds-safety checks for C](https://clang.llvm.org/docs/BoundsSafety.html) or [C++ standard library hardening](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3471r0.html). @@ -167,7 +209,7 @@ The result coudl be the following memory-safe Swift API: @lifetime(ptr) func min_element(_ ptr: Span) -> Span? ``` -### Affordances for C++ +### Affordances for C++ interoperability C++ offers a number of further opportunities for improved safety by modeling lifetimes. For example, `std::vector` has a `front()` method that returns a reference to the element at the front of the vector: @@ -188,5 +230,4 @@ could be imported into Swift as: ```swift @lifetime(sequence) func substring_match(_ sequence: Span, _ subsequence: Span) -> Span -``` - +``` \ No newline at end of file From 5a72f8e3f07dfc6b9e46e432969033f3bcb3c7ff Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 11 Dec 2024 11:19:20 -0800 Subject: [PATCH 3940/4563] Add a section on incremental adoption --- visions/memory-safety.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/visions/memory-safety.md b/visions/memory-safety.md index eb43b3b7e6..b87be7ae73 100644 --- a/visions/memory-safety.md +++ b/visions/memory-safety.md @@ -18,7 +18,7 @@ While there are a number of potential definitions for memory safety, the one pro Since its inception, Swift has provided memory safety for the first four dimensions. Lifetime safety is provided for reference types by automatic reference counting and for value types via [memory exclusivity](https://www.swift.org/blog/swift-5-exclusivity/); bounds safety is provided by bounds-checking on `Array` and other collections; type safety is provided by safe features for casting (`as?` , `is` ) and `enum` s; and initialization safety is provided by “definite initialization”, which doesn’t allow a variable to be accessed until it has been defined. Swift 6’s strict concurrency checking extends Swift’s memory safety guarantees to the last dimension. -Providing memory safety does not imply the absence of run-time failures. Good language design often means defining away runtime failures in the type system. However, memory safely requires only that an error in the program cannot be escalated into a violation of one of the safety properties. For example, having reference types by non-nullable by default defines away most problems with NULL pointers. With explicit optional types, the force-unwrap operator (postfix `!` ) meets the definition of memory safety by trapping at runtime if the unwrapped optional is `nil` . The standard library also provides the [`unsafelyUnwrapped` property](https://developer.apple.com/documentation/swift/optional/unsafelyunwrapped) that does not check for `nil` in release builds: this does not meet the definition of memory safety because it admits violations of initialization and lifetime safety that could be exploited. +Providing memory safety does not imply the absence of run-time failures. Good language design often means defining away runtime failures in the type system. However, memory safely requires only that an error in the program cannot be escalated into a violation of one of the safety properties. For example, having reference types be non-nullable by default defines away most problems with NULL pointers. With explicit optional types, the force-unwrap operator (postfix `!` ) meets the definition of memory safety by trapping at runtime if the unwrapped optional is `nil` . The standard library also provides the [`unsafelyUnwrapped` property](https://developer.apple.com/documentation/swift/optional/unsafelyunwrapped) that does not check for `nil` in release builds: this does not meet the definition of memory safety because it admits violations of initialization and lifetime safety that could be exploited. ### Unsafe code @@ -203,7 +203,7 @@ const double * _Nullable __counted_by(1) min_element(const double *__counted_by(N) __attribute__((noescape)) __attribute__((lifetimebound)) ptr, int N); ``` -The result coudl be the following memory-safe Swift API: +The result could be the following memory-safe Swift API: ```swift @lifetime(ptr) func min_element(_ ptr: Span) -> Span? @@ -222,7 +222,10 @@ The returned reference is valid so long as the vector instance still exists and The C++ [`std::span`](https://en.cppreference.com/w/cpp/container/span) type is similar to the Swift `Span` type, in that it also carries both a pointer and bounds to describe a region of memory. However, `std::span` doesn't provide lifetime safety, so it is essentially an unsafe type from the Swift perspective. The same C attributes that provide lifetime safety for C pointers and references could be applied to `std::span` instances to provide safe Swift projections of C++ APIs. For example, the following annotated C++ API: ```c++ -std::span substring_match(std::span sequence [[clang::lifetimebound]], std::span subsequence [[clang::noescape]]); +std::span substring_match( + std::span sequence [[clang::lifetimebound]], + std::span subsequence [[clang::noescape]] +); ``` could be imported into Swift as: @@ -230,4 +233,10 @@ could be imported into Swift as: ```swift @lifetime(sequence) func substring_match(_ sequence: Span, _ subsequence: Span) -> Span -``` \ No newline at end of file +``` + +## Incremental adoption + +The introduction of any kind of additional checking into Swift requires a strategy that accounts for the practicalities of adoption within the Swift ecosystem. Different developers adopt new features on their own schedules, and some Swift code will never enable new checking features. Therefore, it is important that a given Swift module can adopt the proposed strict safety checking without requiring any module it depends on to have already done so, and without breaking any of its own clients that have not enabled strict safety checking. + +The optional strict memory safety model proposed by this vision lends itself naturally to incremental adoption. The proposed `@unsafe` attribute is not part of the type of the declaration it is applied to, and therefore does not propagate through the type system in any manner. Additionally, any use of an unsafe construct can be addressed locally, either by encapsulating it (e.g., via `@safe(unchecked)`) or propagating it (with `@unsafe`). This means that a module that has not adopted strict safety checking will not see any diagnostics related to this checking, even when modules it depends on adopt strict safety checking. \ No newline at end of file From a8a8771f1ee6a35c515cc81335669c7722b10c43 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 11 Dec 2024 13:26:46 -0800 Subject: [PATCH 3941/4563] Add a section of whether strict memory safety checking should become the default --- visions/memory-safety.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/visions/memory-safety.md b/visions/memory-safety.md index b87be7ae73..c9f5051065 100644 --- a/visions/memory-safety.md +++ b/visions/memory-safety.md @@ -239,4 +239,13 @@ func substring_match(_ sequence: Span, _ subsequence: Span) -> Spa The introduction of any kind of additional checking into Swift requires a strategy that accounts for the practicalities of adoption within the Swift ecosystem. Different developers adopt new features on their own schedules, and some Swift code will never enable new checking features. Therefore, it is important that a given Swift module can adopt the proposed strict safety checking without requiring any module it depends on to have already done so, and without breaking any of its own clients that have not enabled strict safety checking. -The optional strict memory safety model proposed by this vision lends itself naturally to incremental adoption. The proposed `@unsafe` attribute is not part of the type of the declaration it is applied to, and therefore does not propagate through the type system in any manner. Additionally, any use of an unsafe construct can be addressed locally, either by encapsulating it (e.g., via `@safe(unchecked)`) or propagating it (with `@unsafe`). This means that a module that has not adopted strict safety checking will not see any diagnostics related to this checking, even when modules it depends on adopt strict safety checking. \ No newline at end of file +The optional strict memory safety model proposed by this vision lends itself naturally to incremental adoption. The proposed `@unsafe` attribute is not part of the type of the declaration it is applied to, and therefore does not propagate through the type system in any manner. Additionally, any use of an unsafe construct can be addressed locally, either by encapsulating it (e.g., via `@safe(unchecked)`) or propagating it (with `@unsafe`). This means that a module that has not adopted strict safety checking will not see any diagnostics related to this checking, even when modules it depends on adopt strict safety checking. + +## Should strict memory safety checking become the default? + +This vision proposes that the strict safety checking described be an opt-in feature with no path toward becoming the default behavior in some future language mode. There are several reasons why this checking should remain an opt-in feature for the foreseeable future: + +* The various `Unsafe` pointer types are the only way to work with contiguous memory in Swift today, and the safe replacements (e.g., `Span`) are new constructs that will take a long time to propagate through the ecosystem. Some APIs depending on these `Unsafe` pointer types cannot be replaced because it would break existing clients (either source, binary, or both). +* Interoperability with the C family of languages is an important feature for Swift. Most C(++) APIs are unlikely to ever adopt the safety-related attributes described above, which means that enabling strict safety checking by default would undermine the usability of C(++) interoperability. +* Swift's current (non-strict) memory safety by default is likely to be good enough for the vast majority of users of Swift, so the benefit of enabling stricter checking by default is unlikely to be worth the disruption it would cause. +* The auditing facilities described in this vision should be sufficient for Swift users who require strict memory safety, to establish where unsafe constructs are used and prevent "backsliding" where their use grows in an existing code base. These Swift users are unlikely to benefit much from strict safety being enabled by default in a new language mode, aside from any additional social pressure that would create on Swift programmers to adopt it. \ No newline at end of file From 50d72117f4ba8d450e2818c791884dbccc59a736 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 12 Dec 2024 12:56:09 -0800 Subject: [PATCH 3942/4563] Add a few notes about ABI stability and interaction with Embedded Swift --- visions/memory-safety.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/visions/memory-safety.md b/visions/memory-safety.md index c9f5051065..bae7288b08 100644 --- a/visions/memory-safety.md +++ b/visions/memory-safety.md @@ -241,6 +241,8 @@ The introduction of any kind of additional checking into Swift requires a strate The optional strict memory safety model proposed by this vision lends itself naturally to incremental adoption. The proposed `@unsafe` attribute is not part of the type of the declaration it is applied to, and therefore does not propagate through the type system in any manner. Additionally, any use of an unsafe construct can be addressed locally, either by encapsulating it (e.g., via `@safe(unchecked)`) or propagating it (with `@unsafe`). This means that a module that has not adopted strict safety checking will not see any diagnostics related to this checking, even when modules it depends on adopt strict safety checking. +The strict memory safety checking does not require any changes to the binary interface of a module, so it can be retroactively enabled (including `@unsafe` annotations) with no ABI or back-deployment concerns. Additionally, it is independent of other language subsetting approaches, such as Embedded Swift. + ## Should strict memory safety checking become the default? This vision proposes that the strict safety checking described be an opt-in feature with no path toward becoming the default behavior in some future language mode. There are several reasons why this checking should remain an opt-in feature for the foreseeable future: From f29cb8af705cf18635b64939ac1b894815e2baa6 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Sat, 14 Dec 2024 08:42:10 +0000 Subject: [PATCH 3943/4563] Update README.md focus areas --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index b075c548a0..bd24a420fa 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,7 @@ This repository tracks the ongoing evolution of the Swift programming language, ## Goals and Release Notes -* [Swift project focus areas in 2023](https://forums.swift.org/t/swift-project-focus-areas-in-2023/61522) -* [On the road to Swift 6](https://forums.swift.org/t/on-the-road-to-swift-6/32862) +* [Swift Language focus areas heading into 2025](https://forums.swift.org/t/swift-language-focus-areas-heading-into-2025/76611) * [CHANGELOG](https://github.com/apple/swift/blob/main/CHANGELOG.md) | Version | Announced | Released | From d86a7cb969e837ad9b3f28159f878c4eb98f862d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 16 Dec 2024 16:36:22 -0800 Subject: [PATCH 3944/4563] First draft of "Opt-in Strict Memory Safety Checking" --- proposals/nnnn-strict-memory-safety.md | 387 +++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 proposals/nnnn-strict-memory-safety.md diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md new file mode 100644 index 0000000000..e1c9b2143a --- /dev/null +++ b/proposals/nnnn-strict-memory-safety.md @@ -0,0 +1,387 @@ +# Opt-in Strict Memory Safety Checking + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Doug Gregor](https://github.com/DougGregor) +* Review Manager: TBD +* Status: **Awaiting review** +* Vision: *if applicable* [Opt-in Strict Memory Safety Checking (Prospective)](https://github.com/swiftlang/swift-evolution/pull/2581) +* Implementation: On main with experimental feature flags `AllowUnsafeAttribute` and `WarnUnsafe` +* Review: ([pitch](https://forums.swift.org/...)) + +## Introduction + +[Memory safety](https://en.wikipedia.org/wiki/Memory_safety) is a popular topic in programming languages nowadays. Essentially, memory safety is a property that prevents programmer errors from manifesting as [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior) at runtime. Undefined behavior effectively breaks the semantic model of a language, with unpredictable results including crashes, data corruption, and otherwise-impossible program states, which directly lead to hard-to-reproduce problems as well as security problems. + +Swift provides memory safety with a combination of language affordances and runtime checking. However, Swift also deliberately includes some unsafe constructs, such as the `Unsafe` pointer types in the standard library, language features like `nonisolated(unsafe)`, and interoperability with unsafe languages like C. For most Swift developers, this is a pragmatic solution that appropriate level of memory safety while not getting in the way. + +However, there are certain projects, organizations, and code bases that require stronger memory-safety guarantees, such as in security-critical subsystems handling untrusted data or that are executing with elevated privileges in an OS. This proposal introduces opt-in strict memory safety checking to identify those places in Swift code that make use of unsafe language constructs and APIs. Any code written within this strictly-safe subset also works as “normal” Swift and can interoperate with existing Swift code. + +## Motivation + +Much of the recent focus on memory safety is motivated by security, because memory safety issues offer a fairly direct way to compromise a program: in fact, the lack of memory safety in C and C++ has been found to be the root cause for ~70% of reported security issues in various analyses [[1](https://msrc.microsoft.com/blog/2019/07/a-proactive-approach-to-more-secure-code/)][[2](https://www.chromium.org/Home/chromium-security/memory-safety/)]. + +### Dimensions of memory safety + +While there are a number of potential definitions for memory safety, the one provided by [this blog post](https://security.apple.com/blog/towards-the-next-generation-of-xnu-memory-safety/) breaks it down into five dimensions of safety: + +* **Lifetime safety** : all accesses to a value are guaranteed to occur during its lifetime. Violations of this property, such as accessing a value after its lifetime has ended, are often called use-after-free errors. +* **Bounds safety**: all accesses to memory are within the intended bounds of the memory allocation, such as accessing elements in an array. Violations of this property are called out-of-bounds accesses. +* **Type safety** : all accesses to a value use the type to which it was initialized, or a type that is compatible with that type. For example, one cannot access a `String` value as if it were an `Array`. Violations of this property are called type confusions. +* **Initialization safety** : all values are initialized property to being used, so they cannot contain unexpected data. Violations of this property often lead to information disclosures (where data that should be invisible becomes available) or even other memory-safety issues like use-after-frees or type confusions. +* **Thread safety:** all values are accessed concurrently in a manner that is synchronized sufficiently to maintain their invariants. Violations of this property are typically called data races, and can lead to any of the other memory safety problems. + +### Memory safety in Swift + +Since its inception, Swift has provided memory safety for the first four dimensions. Lifetime safety is provided for reference types by automatic reference counting and for value types via [memory exclusivity](https://www.swift.org/blog/swift-5-exclusivity/); bounds safety is provided by bounds-checking on `Array` and other collections; type safety is provided by safe features for casting (`as?` , `is` ) and `enum` s; and initialization safety is provided by “definite initialization”, which doesn’t allow a variable to be accessed until it has been defined. Swift 6’s strict concurrency checking extends Swift’s memory safety guarantees to the last dimension. + +Providing memory safety does not imply the absence of run-time failures. Good language design often means defining away runtime failures in the type system. However, memory safely requires only that an error in the program cannot be escalated into a violation of one of the safety properties. For example, having reference types be non-nullable by default defines away most problems with NULL pointers. With explicit optional types, the force-unwrap operator (postfix `!` ) meets the definition of memory safety by trapping at runtime if the unwrapped optional is `nil` . The standard library also provides the [`unsafelyUnwrapped` property](https://developer.apple.com/documentation/swift/optional/unsafelyunwrapped) that does not check for `nil` in release builds: this does not meet the definition of memory safety because it admits violations of initialization and lifetime safety that could be exploited. + +## Proposed solution + +This proposal introduces an opt-in strict memory safety checking mode that identifies all uses of unsafe behavior within the given module. There are several parts to this change: + +* A compiler flag `-strict-memory-safety` that enables warnings for all uses of unsafe constructs within a given module. All warnings will be in the diagnostic group `Unsafe`, enabling precise control over memory-safety-related warnings per [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md). +* An attribute `@unsafe` that indicates that a declaration is unsafe to use. Such declarations may use unsafe constructs within their signatures or definitions. +* An attribute `@safe(unchecked)` that indicates that a declaration provides a safe interface despite using unsafe constructs within its definition. The `unchecked` indicates that Swift cannot check this assertion of safety. +* Standard library annotations to identify unsafe declarations. + +### Example of `unsafe` usage + +The `UnsafeBufferPointer` type will be marked with `@unsafe` in the Standard library, as will the other unsafe types (e.g., `UnsafePointer`, `UnsafeRawPointer`): + +```swift +@unsafe +public struct UnsafeBufferPointer { ... } +``` + +This indicates that use of this type is not memory-safe. Any declaration that has `UnsafeBufferPointer` as part of its type is also unsafe, and would produce a warning under this strict safety mode, e.g., + +```swift +extension Array { + // warning on next line: reference to unsafe generic struct 'UnsafeBufferPointer' + func withUnsafeBufferPointerSimplified(_ body: (UnsafeBufferPointer) -> T) -> T { + // ... + } +} +``` + +This warning can be suppressed by marking the function as `@unsafe`: + +```swift +extension Array { + @unsafe + func withUnsafeBufferPointerSimplified(_ body: (UnsafeBufferPointer) -> T) -> T { + // ... + } +} +``` + +Users of this function that also enable strict safety checking will see warnings when using it. For example: + +```swift +extension Array { + func sum() -> Int { + // warning: use of unsafe function 'withUnsafeBufferPointerSimplified' + withUnsafeBufferPointerSimplified { buffer in + c_library_sum_function(buffer.baseAddress, buffer.count, 0) + } + } +} +``` + +Both the call to `withUnsafeBufferPointerSimplified` (which is `@unsafe`) and the call to `c_library_sum_function` (which has a parameter of `@unsafe` type `UnsafePointer`) would trigger warnings about uses of unsafe constructs. The author of `sum` has a choice to suppress the warnings: + +1. Mark the `sum` function as `@unsafe`, propagating the "unsafe" checking out to callers of `sum`; or +2. Mark the `sum` function as `@safe(unchecked)`, taking responsibility for the safety of the code within the body. Here, one needs to verify that the `UnsafeBufferPointer` itself is being used safely (i.e., accesses are in-bounds and the buffer doesn't escape the closure) and that `c_library_sum_function` does the same with the pointer and bounds it is given. + +### Incremental adoption + +The strict memory safety checking proposed here enforces a subset of Swift. Code written within this subset must also be valid Swift code, and must interoperate with Swift code that does not use this strict checking. Compared to other efforts in Swift that introduce stricter checking or a subset, strictly-safe Swift is smaller and more constrained, providing better interoperability and a more gradual adoption curve: + +* Strict concurrency checking, the focus of the Swift 6 language mode, required major changes to the type system, including the propagation of `Sendable` and the understanding of what code must be run on the main actor. These are global properties that don't permit local reasoning, or even local fixes, making the interoperability problem particularly hard. In contrast, strict safety checking has little or no effect on the type system, and unsafety can be encapsulated with `@safe(unchecked)` or ignored by a module that doesn't enable the checking. +* [Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0337-support-incremental-migration-to-concurrency-checking.md) is a subset of Swift that works without a runtime. Like the proposed strictly-safe subset, code written in Embedded Swift will also work as regular Swift. Embedded Swift and the strict safety checking proposed here are orthogonal and can be composed to (for example) ensure that firmware written in Swift has no runtime and provides the best memory-safety guarantees. + +A Swift module that adopts strict safety checking can address all of the resulting diagnostics by applying the `@unsafe` and `@safe(unchecked)` attributes in the appropriate places, without changing any other code. This application of attributes can be automated through Fix-Its, making it possible to enable the mode and silence all diagnostics automatically. It would then be left to the programmer to audit those places that have used `@safe(unchecked)` to encapsulate unsafe behavior within a safe interface, to ensure that they are indeed safe. + +The introduction of the `@safe(unchecked)` attribute on a declaration has no effect on its clients. The introduction of the `@unsafe` attribute on a declaration has no effect on clients compiled without strict safety enabled. For clients that have enabled strict safety, they will start diagnosing uses of the newly-`@unsafe` API. However, these diagnostics are warnings with their own diagnostic group, so a client can ensure that they do not prevent the client from building. Therefore, modules can adopt strict safety checking at their own pace (or not) and clients of those modules are never "stuck" having to make major changes in response. + +## Detailed design + +This section describes how the `@unsafe` and `@safe` attributes interact with the strict type checking mode, and enumerates the places in the language and standard library that introduce non-memory-safe code. + +### `@unsafe` attribute + +The `@unsafe` attribute can be applied to any declaration to indicate that use of that declaration can undermine memory safety. Any use of a declaration marked `@unsafe` will result in a warning. The closest analogue in the language today is `@available(*, deprecated)`, which has effectively no impact on the type system, yet any use of a deprecated declaration results in a warning. + +When a type is marked `@unsafe`, a declaration that uses that type in its interface is implicitly `@unsafe`. For example, consider a program containing three separate modules: + +```swift +// Module A +@unsafe +public struct DataWrapper { + var buffer: UnsafeBufferPointer + + public func checksum() -> Int32 { ... } +} + +// Module B +import A + +public struct MyType { + public var wrapper: DataWrapper +} + +// Module C +import A +import B + +extension MyType { + public func checksum() -> Int32 {} + return wrapper.checksum() + } +} +``` + +Module `A` defines a type, `DataWrapper`, that is `@unsafe`. It can be compiled with or without strict safety checking enabled, and is fine either way. + +Module `B` uses the `DataWrapper` type. If compiled without strict safety checking, there will be no diagnostics about memory safety. If compiled with strict safety checking, there will be a diagnostic about `wrapper` using an `@unsafe` type (`DataWrapper`) in its interface. This diagnostic can be ignored. + +In module `C` enables strict memory safety, the use of `MyType` is considered safe (since it was not marked `@unsafe` and doesn't involve unsafe types in its interface). However, the access to `wrapper` will result in a diagnostic, because the type of `wrapper` involves an `@unsafe` type. + +### Encapsulating unsafe behavior + +When a declaration is marked `@unsafe`, it is free to use any other unsafe declarations as part of its interface or implementation. In the example from the previously section, both `wrapper` and C's `checksum` can be marked as `@unsafe` to suppress diagnostics by explicitly propagating unsafety to their clients: + +```swift +// Module B +import A + +public struct MyType { + @unsafe public var wrapper: DataWrapper +} + +// Module C +import A +import B + +extension MyType { + @unsafe public func checksum() -> Int32 {} + return wrapper.checksum() + } +} +``` + +However, these two APIs diff in the severity of the problem they post for memory safety: `wrapper` has an unsafe type in its interface, so any use of `wrapper` is fundamentally unsafe. However, `MyType` itself can encapsulate the memory-unsafe behavior within a safe API., The `MyType.checksum` operation is actually safe for clients to use. Marking it as `@unsafe` is therefore undesirable, because it (incorrectly) forces clients to treat this as an unsafe API, causing unnecessary extra work and deluting the value of correctly-identified unsafe APIs. + +One option would be for the author of module `C` to simply ignore the memory-safety warnings produced within its body, which will have the effect of encapsulating the unsafe behavior, but would make it harder to ensure that all unsafe behavior has been accounted for in that module. Another option would be to factor this code into a separate module that doesn't enable strict safety checking, but this is a fairly heavyweight solution. + +Instead, introduce an attribute `@safe(unchecked)` that asserts that the definition is safe despite uses of unsafe constructs. This is a programmer assertion that cannot be validated by the compiler, so it should be used carefully. For the `checksum` operation, it would be used as follows to suppress all memory-safety diagnostics within the body of the function: + +```swift +extension MyType { + @safe(unchecked) public func checksum() -> Int32 {} + return wrapper.checksum() + } +} +``` + +Note that `@safe(unchecked)` only suppresses memory-safety diagnostics in the definition. If we were to try to apply it to the `wrapper` property and enable strict safety checking in module `B`, like this: + +```swift +public struct MyType { + @safe(unchecked) public var wrapper: DataWrapper // warning: use of unsafe type 'DataWrapper' +} +``` + +it would not suppress the diagnostic, because `wrapper` is still fundamentally unsafe. + +The `@safe` attribute allows an optional message, which can be used to explain why the use of unsafe constructs is justified, and is meant to help with an audit trail. Bringing back out early example of producing the sum of values in an array, one can provide an explanation here: + +```swift +extension Array { + @safe(unchecked, message: "use of C API that does its own bounds-safety checking") + func sum() -> Int { + withUnsafeBufferPointerSimplified { buffer in + c_library_sum_function(buffer.baseAddress, buffer.count, 0) + } + } +} +``` + +### Unsafe language constructs + +The following language constructs are always considered to be unsafe: + +* `unowned(unsafe)`: Used to store a reference without maintaining its reference count. The safe counterpart, `unowned`, uses dynamic checking to ensure that the reference isn't accessed after the corresponding object has been released. The `unsafe` variant disables that dynamic checking. +* `unsafeAddressor`, `unsafeMutableAddressor`: These accessors vend an unsafe pointer, and are therefore unsafe to declare. Other accessors (e.g., `get` and `set`) can provide safe alternatives. + +The following language constructs are considered to be unsafe when strict concurrency checking is enabled (i.e., in the Swift 6 language mode): + +* `@unchecked Sendable`: A `Sendable` conformance that cannot be checked by the compiler, which could introduce thread safety issues. +* `nonisolated(unsafe)`: Allows a property to be accessed from concurrent code without ensuring that such accesses are done so safely. +* `@preconcurrency` imports: Suppresses diagnostics related to data race safety when they relate to specific imported modules, which can introduce thread safety issues. + +### Unsafe standard library APIs + +In the standard library, the following functions and types would be marked `@unsafe` : + +* `Unsafe(Mutable)(Raw)(Buffer)Pointer`, `OpaquePointer`, `CVaListPointer`: These types provide neither lifetime nor bounds safety. Over time, Swift code is likely to move toward their safe replacements, such as `(Raw)Span`. +* `(Closed)Range.init(uncheckedBounds:)`: This operation makes it possible to create a range that doesn't satisfy invariants on which other bounds safety checking (e.g., in `Array.subscript`) +* `Unmanaged`: Wrapper over reference-counted types that explicitly disables reference counting, potentially introducing lifetime safety issues. +* `unsafeBitCast`: Allows type casts that are not known to be safe, which can introduce type safety problems. +* `unsafeDowncast`: An unchecked form of an `as!` cast that can introduce type safety problems. +* `Optional.unsafelyUnwrapped`: An unchecked form of the postfix `!` operation on optionals that can introduce various type, initialization, or lifetime safety problems when `nil` is interpreted as a typed value. +* `UnsafeContinuation`, `withUnsafe(Throwing)Continuation`: An unsafe form of `withChecked(Throwing)Continuation` that does not verify that the continuation is called exactly once, which can cause various safety problems. +* `withUnsafeCurrentTask` and `UnsafeCurrentTask`: The `UnsafeCurrentTask` type does not provide lifetime safety, and must only be used within the closure passed to `withUnsafeCurrentTask`. +* `Span.subscript(unchecked:)` : An unchecked subscript whose use can introduce bounds safety problems. + +All of these APIs will be marked `@unsafe`. For all of the types that are `@unsafe`, any API that uses that type in its signature will also be marked `@unsafe`, such as `Array.withUnsafeBufferPointer`. Unless mentioned above, standard library APIs that do not have an unsafe type in their signature, but use unsafe constructs in their implementation, will be marked `@safe(unchecked)` because they provide safe abstractions to client code. + +### Unsafe compiler flags + +The `-Ounchecked` compiler flag disables some checking in the standard library, including (for example) bounds checking on array accesses. It is generally discouraged in all Swift code, but is particularly problematic in conjunction with strict memory safety because it removes the checking that makes certain standard library APIs safe. Therefore, the compiler will produce a diagnostic when the two features are combined. + +### Strict safety mode and escalatable warnings + +The strict memory safety mode can be enabled with the new compiler flag `-strict-memory-safety`. + +All of the memory-safety diagnostics produced by the strict memory safety mode will be warnings. These warnings be in the group `Unsafe` (possibly organized into subgroups) so that one can choose to escalate them to errors or keep them as warnings using the compiler flags introduced in [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md). For example, one can choose to enable the mode and make memory-safety issues errors using: + +``` +swiftc -strict-memory-safety -Werror Unsafe +``` + +### SwiftPM integration + +Swift package manifests will need a way to enable strict memory safety mode on a per-module and per-package basis. This proposal extends the `SwiftSetting` type in the manifest with a new option to enable that checking: + +```swift +static func strictMemorySafety( + _ condition: BuildSettingCondition? = nil +) -> SwiftSetting +``` + +## Source compatibility + +The introduce of this strict safety checking mode has no impact on source compatibility for any module that does not enable it. When enabling strict safety checking, source compatibility impact is limited to the introduction of warnings that will not break source compatibility (and can be treated as warnings even under `-warnings-as-errors` mode). The source compatibility and interoperability story is covered in detail in prior sections. + +## ABI compatibility + +The attributes and strict memory-safety checking model proposed here have no impact on ABI. + +## Future Directions + +### Overloading to stage in safe APIs + +When adopting the strict memory safety mode, it's likely that a Swift module will want to replace existing APIs that traffic in unsafe types (such as `UnsafeMutablePointer`) with safer equivalents (such as `Span`). To retain compatibility for older clients, the existing APIs will need to be left in place. Unfortunately, this might mean that the best name for the API is already taken. For example, perhaps we have a data packet that exposes its bytes via a property: + +```swift +public class DataPacket { + @unsafe public let bytes: UnsafeRawBufferPointer +} +``` + +The `bytes` property is necessarily unsafe. Far better would be to produce a `RawSpan`, which we can easily do with another property: + +```swift +extension DataPacket { + @safe(unchecked) public var byteSpan: RawSpan +} +``` + +Clients using the existing `bytes` will continue to work, and those that care about memory safety can choose to move to `byteSpan`. All of this works, but is somewhat annoying because the good name, `bytes`, has been taken for the API we no longer want to use. + +Swift does allow type-based overloading, including on the type of properties, so one could introduce an overloaded `bytes` property, like this: + +```swift +extension DataPacket { + @safe(unchecked) public var bytes: RawSpan +} +``` + +This works for code that accesses `bytes` and then uses it in a context where type inference can figure out whether we need an `UnsafeRawBufferPointer` or a `RawSpan`, but fails if that context does not exist: + +```swift +let unsafeButGoodBytes: UnsafeRawBufferPointer = dataPacket.bytes // ok, uses @unsafe bytes +let goodBytes: RawSpan = dataPacket.bytes // ok, uses safe bytes +let badBytes = dataPacket.bytes // error: ambiguous! +``` + +We could consider extending Swift's overloading rules to make this kind of evolution possible. For example, one could introduce a pair of rules into the language: + +1. When strict memory safety checking is enabled, `@unsafe` declarations are dis-favored vs. safe ones, so the unsafe `bytes: UnsafeRawBufferPointer` would be a worse solution for type inference to pick than the safe alternative, `bytes: RawSpan`. + +2. Overloads that were introduced to replace unsafe declarations could be marked with a new attribute `@safe(unsafeDisfavored)` so that they would be disfavored only when building with strict memory safety checking disabled. + +Assuming these rules, and that the safe `bytes: RawSpan` had the `@safe(unsafeDisfavored)` attribute, the example uses of `DataPacket` would resolve as follows: + +* `unsafeButGoodBytes` would always be initialized with the unsafe `bytes`. If strict memory safety were enabled, this use would produce a warning. +* `goodBytes` would always be initialized with the safe `bytes`. +* `badBytes` would be initialized differently based on whether strict memory safety was enabled: + * If enabled, `badBytes` would choose the safe version of `bytes` to produce the safest code, because the unsafe one is disfavored (rule #1). + * If disabled, `badBytes` would choose the unsafe version of `bytes` to provide source compatibility with existing code, because the safe one is disfavored (rule #2). + +There are downsides to this approach. It partially undermines the source compatibility story for the strict safety mode, because type inference now behaves differently when the mode is enabled. That means, for example, there might be errors---not warnings---because some code like `badBytes` above would change behavior, causing additional failures. Changing the behavior of type inference is also risky in an of itself, because it is not always easy to reason about all of the effects of such a change. That said, the benefit of being able to move toward a more memory-safe future might be worth it. + +## Alternatives considered + +### `unsafe` blocks + +The attribute `@safe(unchecked)` indicates that a definition is safe despite the use of unsafe constructs in its body. The attribute has no effect on the client, and could effectively be eliminated from the public interface (e.g., documentation, Swift textual interfaces, etc.) without changing how clients behave. + +There is an alternative formulation for acknowledging unsafe code that is used in some peer languages like C# and Rust: an `unsafe` block, which is a statement (or expression) that suppresses diagnostics about uses of unsafe code within it. For example, the `sum` example could be written as follows: + +```swift +extension Array { + func sum() -> Int { + unsafe { + withUnsafeBufferPointerSimplified { buffer in + c_library_sum_function(buffer.baseAddress, buffer.count, 0) + } + } + } +} +``` + +For Swift, `unsafe` would be a statement that can also be used as an expression when its body is an expression, much like `if` or `switch` expressions following [SE-0380](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0380-if-switch-expressions.md). + +This proposal suggests the `@safe(unchecked)` attribute instead of `unsafe` blocks for a few reasons, but the choice is effectively arbitrary: either syntax will work with the rest of this proposal. Some of the reasons for preferring `@safe(unchecked)` include: + +* It's easier to stage in this change without affecting the surrounding code. One can add + ```swift + if #hasAttribute(safe) + @safe(unchecked) + #endif + ``` + + to `sum` without changing any other code, which is easy to review and will still work with compilers that predate the introduction of this feature. + +* The `unsafe` blocks end up adding another level of nesting, which also means that introducing them to silence warnings causes unnecessarily large amount of code change when adopting the feature. + +* The `unsafe` blocks aren't a natural boundary between unsafe and safe code in the same way as a function boundary, because there is no place to state the expected invariants. Combined with pressure to make `unsafe` blocks have as little code as possible (as a proxy for that code being safer), it becomes easy for "minimizing the amount of code in unsafe blocks" to introduce more safety problems. For example, `sum` might be incorrectly factored as follows: + + ```swift + extension Array { + func sum() -> Int { + let (ptr, count) = unsafe { + withUnsafeBufferPointerSimplified { buffer in + (buffer.baseAddress, buffer.count) + } + } + return unsafe { c_library_sum_function(ptr, count, 0) } + } + } + ``` + + Here, the base address pointer has escaped the `withUnsafeBufferPointerSimplified` block, causing a member safety problem that wouldn't have been there before. If we tried to do this kind of factoring with the `@safe(unchecked)` approach, the intermediate function producing the pointer would have to be marked `@unsafe`. + + + +### Strictly-safe-by-default + +This proposal introduced strict safety checking as an opt in mode and not an [*upcoming* language feature](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md) because there is no intent to make this feature the default behavior in a future language mode. There are several reasons why this checking should remain an opt-in feature for the foreseeable future: + +* The various `Unsafe` pointer types are the only way to work with contiguous memory in Swift today, and the safe replacements (e.g., `Span`) are new constructs that will take a long time to propagate through the ecosystem. Some APIs depending on these `Unsafe` pointer types cannot be replaced because it would break existing clients (either source, binary, or both). +* Interoperability with the C family of languages is an important feature for Swift. Most C(++) APIs are unlikely to ever adopt the safety-related attributes described above, which means that enabling strict safety checking by default would undermine the usability of C(++) interoperability. +* Swift's current (non-strict) memory safety by default is likely to be good enough for the vast majority of users of Swift, so the benefit of enabling stricter checking by default is unlikely to be worth the disruption it would cause. + From bcc2d961855cb6d6020e67be9199d95d32339068 Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Tue, 17 Dec 2024 14:18:07 +0100 Subject: [PATCH 3945/4563] Remove incorrect rules from trait names proposal The proposal currently adds some rules to trait names. However some of those rules conflict with the statement that all trait names must be valid Swift identifiers. So let's remove those rules. --- proposals/0450-swiftpm-package-traits.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index d6e316831d..dfd0800f11 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -512,14 +512,8 @@ Since traits can show up both in the `Package.swift` and in source code when checking if a trait is enabled, the allowed characters for a trait name are restricted to [legal Swift identifier](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/summaryofthegrammar/). -Hence, the following rules are enforced on trait names: - -- The first character must be a [Unicode XID start - character](https://unicode.org/reports/tr31/#Figure_Code_Point_Categories_for_Identifier_Parsing) - (most letters), a digit, or `_`. -- Subsequent characters must be a [Unicode XID continue - character](https://unicode.org/reports/tr31/#Figure_Code_Point_Categories_for_Identifier_Parsing) - (a digit, `_`, or most letters), `-`, or `+`. +Additional, the following rules are enforced on trait names: + - `default` and `defaults` (in any letter casing combination) are not allowed as trait names to avoid confusion with default traits. From 00ae72f7f4e74762d5c5d02e40acc9562b73fde5 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 17 Dec 2024 10:24:42 -0800 Subject: [PATCH 3946/4563] [SE-0430] Fix typo. (#2637) --- proposals/0430-transferring-parameters-and-results.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0430-transferring-parameters-and-results.md b/proposals/0430-transferring-parameters-and-results.md index 349d6b50ee..712847edbf 100644 --- a/proposals/0430-transferring-parameters-and-results.md +++ b/proposals/0430-transferring-parameters-and-results.md @@ -340,7 +340,7 @@ protocol P2 { Following the function subtyping rules in the previous section, a protocol requirement with a `sending` parameter may be witnessed by a function with a -non-`Sendable` typed parameter: +non-`sending` parameter: ```swift struct X1: P1 { From 989a41411e0a8fa5828ed45debc4debf80b4185e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 17 Dec 2024 10:34:26 -0800 Subject: [PATCH 3947/4563] Fix typo --- proposals/nnnn-strict-memory-safety.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index e1c9b2143a..72dad1594e 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -27,7 +27,7 @@ While there are a number of potential definitions for memory safety, the one pro * **Lifetime safety** : all accesses to a value are guaranteed to occur during its lifetime. Violations of this property, such as accessing a value after its lifetime has ended, are often called use-after-free errors. * **Bounds safety**: all accesses to memory are within the intended bounds of the memory allocation, such as accessing elements in an array. Violations of this property are called out-of-bounds accesses. * **Type safety** : all accesses to a value use the type to which it was initialized, or a type that is compatible with that type. For example, one cannot access a `String` value as if it were an `Array`. Violations of this property are called type confusions. -* **Initialization safety** : all values are initialized property to being used, so they cannot contain unexpected data. Violations of this property often lead to information disclosures (where data that should be invisible becomes available) or even other memory-safety issues like use-after-frees or type confusions. +* **Initialization safety** : all values are initialized properly to being used, so they cannot contain unexpected data. Violations of this property often lead to information disclosures (where data that should be invisible becomes available) or even other memory-safety issues like use-after-frees or type confusions. * **Thread safety:** all values are accessed concurrently in a manner that is synchronized sufficiently to maintain their invariants. Violations of this property are typically called data races, and can lead to any of the other memory safety problems. ### Memory safety in Swift From d1b1b1cf735ddf080a63612862a264f3c2109089 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 17 Dec 2024 10:56:48 -0800 Subject: [PATCH 3948/4563] Describe checking for unsafe overrides and unsafe conformances And fix the grammaro finagolfin pointed out, properly --- proposals/nnnn-strict-memory-safety.md | 52 +++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 72dad1594e..a3e40d1d3d 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -27,7 +27,7 @@ While there are a number of potential definitions for memory safety, the one pro * **Lifetime safety** : all accesses to a value are guaranteed to occur during its lifetime. Violations of this property, such as accessing a value after its lifetime has ended, are often called use-after-free errors. * **Bounds safety**: all accesses to memory are within the intended bounds of the memory allocation, such as accessing elements in an array. Violations of this property are called out-of-bounds accesses. * **Type safety** : all accesses to a value use the type to which it was initialized, or a type that is compatible with that type. For example, one cannot access a `String` value as if it were an `Array`. Violations of this property are called type confusions. -* **Initialization safety** : all values are initialized properly to being used, so they cannot contain unexpected data. Violations of this property often lead to information disclosures (where data that should be invisible becomes available) or even other memory-safety issues like use-after-frees or type confusions. +* **Initialization safety** : all values are initialized properly prior to being used, so they cannot contain unexpected data. Violations of this property often lead to information disclosures (where data that should be invisible becomes available) or even other memory-safety issues like use-after-frees or type confusions. * **Thread safety:** all values are accessed concurrently in a manner that is synchronized sufficiently to maintain their invariants. Violations of this property are typically called data races, and can lead to any of the other memory safety problems. ### Memory safety in Swift @@ -241,6 +241,56 @@ All of these APIs will be marked `@unsafe`. For all of the types that are `@unsa The `-Ounchecked` compiler flag disables some checking in the standard library, including (for example) bounds checking on array accesses. It is generally discouraged in all Swift code, but is particularly problematic in conjunction with strict memory safety because it removes the checking that makes certain standard library APIs safe. Therefore, the compiler will produce a diagnostic when the two features are combined. +### Unsafe overrides + +Overriding a safe method within an `@unsafe` one could introduce unsafety, so it will produce a diagnostic in the strict safety mode: + +```swift +class Super { + func f() { } +} + +class Sub: Super { + @unsafe func f() { ... } // warning: override of safe instance method with unsafe instance method +} +``` + +to suppress this warning, the `Sub` class itself can be marked as `@unsafe`, e.g., + +```swift +@unsafe +class Sub: Super { + func f() { ... } // warning: override of safe instance method with unsafe instance method +} +``` + +The `@unsafe` annotation is at the class level because any use of the `Sub` type can now introduce unsafe behavior, and any indication of that unsafe behavior will be lost once that `Sub` is converted to a `Super` instance. + +### Unsafe conformances + +Implementing a protocol requirement that is not `@unsafe` (and not part of an `@unsafe` protocol) within an `@unsafe` declaration introduces unsafety, so it will produce a diagnostic in the strict safety mode: + +```swift +protocol P { + func f() +} + +struct ConformsToP { } + +extension ConformsToP: P { + @unsafe func f() { } // warning: unsafe instance method 'f()' cannot satisfy safe requirement +} +``` + +To suppress this warning, one can place `@unsafe` on the extension (or type) where the conformance to `P` is supplied. This notes that the conformance itself is unsafe: + +```swift +@unsafe +extension ConformsToP: P { + @unsafe func f() { } +} +``` + ### Strict safety mode and escalatable warnings The strict memory safety mode can be enabled with the new compiler flag `-strict-memory-safety`. From 80c3616bb23e7bf751f886aaf178acb3df09abe5 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Tue, 17 Dec 2024 14:05:35 -0800 Subject: [PATCH 3949/4563] Accepted SE-450 Package Traits --- proposals/0450-swiftpm-package-traits.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index dfd0800f11..c00ecea147 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -3,8 +3,8 @@ * Proposal: [SE-0450](0450-swiftpm-package-traits.md) * Authors: [Franz Busch](https://github.com/FranzBusch), [Max Desiatov](https://github.com/MaxDesiatov) * Review Manager: [Mishal Shah](https://github.com/shahmishal) -* Status: **Active review (October 24th...November 7th, 2024)** -* Implementation: https://github.com/swiftlang/swift-package-manager/pull/7704, https://github.com/swiftlang/swift-package-manager/pull/7703, https://github.com/swiftlang/swift-package-manager/pull/7702, https://github.com/swiftlang/swift-package-manager/pull/7701, https://github.com/swiftlang/swift-package-manager/pull/7694, https://github.com/swiftlang/swift-package-manager/pull/7689 +* Status: **Accepted** +* Implementation: https://github.com/swiftlang/swift-package-manager/pull/7704, https://github.com/swiftlang/swift-package-manager/pull/7703, https://github.com/swiftlang/swift-package-manager/pull/7702, https://github.com/swiftlang/swift-package-manager/pull/7701, https://github.com/swiftlang/swift-package-manager/pull/7694, https://github.com/swiftlang/swift-package-manager/pull/7689, https://github.com/swiftlang/swift-package-manager/pull/8178 * Experimental Implementation: Gated on `@_spi(ExperimentalTraits)` in package manifests and `--experimental` prefix for CLI options * Review: ([pitch](https://forums.swift.org/t/pitch-package-traits/72191)) ([review](https://forums.swift.org/t/se-0450-package-traits/75598)) From d2d4f6bcec10a785e205c7b3729982aa516d84a6 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 19 Dec 2024 22:39:41 -0800 Subject: [PATCH 3950/4563] Move the `@unsafe` for unsafe conformances to where `@unchecked` is for `Sendable` Thank you for the suggestion, Xiaodi! --- proposals/nnnn-strict-memory-safety.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index a3e40d1d3d..cca041a57a 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -282,15 +282,16 @@ extension ConformsToP: P { } ``` -To suppress this warning, one can place `@unsafe` on the extension (or type) where the conformance to `P` is supplied. This notes that the conformance itself is unsafe: +To suppress this warning, one can place `@unsafe` on the conformance to `P` is supplied. This notes that the conformance itself is unsafe: ```swift -@unsafe -extension ConformsToP: P { +extension ConformsToP: @unsafe P { @unsafe func f() { } } ``` +Use of an `@unsafe` conformance for any reason (e.g., when that conformance is needed to call a generic function with a `ConformsToP` requirement) is diagnosed as an unsafe use, much like use of an `@unsafe` declaration. + ### Strict safety mode and escalatable warnings The strict memory safety mode can be enabled with the new compiler flag `-strict-memory-safety`. From 3c305305d3d1e61a8ba0279943e3f26ff054d2b2 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 19 Dec 2024 22:54:39 -0800 Subject: [PATCH 3951/4563] Note that UnownedSerialExecutor needs to be @unsafe --- proposals/nnnn-strict-memory-safety.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index cca041a57a..c14e264bc3 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -227,13 +227,14 @@ In the standard library, the following functions and types would be marked `@uns * `Unsafe(Mutable)(Raw)(Buffer)Pointer`, `OpaquePointer`, `CVaListPointer`: These types provide neither lifetime nor bounds safety. Over time, Swift code is likely to move toward their safe replacements, such as `(Raw)Span`. * `(Closed)Range.init(uncheckedBounds:)`: This operation makes it possible to create a range that doesn't satisfy invariants on which other bounds safety checking (e.g., in `Array.subscript`) +* `Span.subscript(unchecked:)` : An unchecked subscript whose use can introduce bounds safety problems. * `Unmanaged`: Wrapper over reference-counted types that explicitly disables reference counting, potentially introducing lifetime safety issues. * `unsafeBitCast`: Allows type casts that are not known to be safe, which can introduce type safety problems. -* `unsafeDowncast`: An unchecked form of an `as!` cast that can introduce type safety problems. +* `unsafeDowncast`: An unchecked form of an `as!` cast that can introduce type safety problems. * `Optional.unsafelyUnwrapped`: An unchecked form of the postfix `!` operation on optionals that can introduce various type, initialization, or lifetime safety problems when `nil` is interpreted as a typed value. * `UnsafeContinuation`, `withUnsafe(Throwing)Continuation`: An unsafe form of `withChecked(Throwing)Continuation` that does not verify that the continuation is called exactly once, which can cause various safety problems. * `withUnsafeCurrentTask` and `UnsafeCurrentTask`: The `UnsafeCurrentTask` type does not provide lifetime safety, and must only be used within the closure passed to `withUnsafeCurrentTask`. -* `Span.subscript(unchecked:)` : An unchecked subscript whose use can introduce bounds safety problems. +* `UnownedSerialExecutor`: This type is intentionally not lifetime safe. It's primary use is the `unownedExecutor` property of the `Actor` protocol, which documents the lifetime assumptions of the `UnownedSerialExecutor` instance it produces. All of these APIs will be marked `@unsafe`. For all of the types that are `@unsafe`, any API that uses that type in its signature will also be marked `@unsafe`, such as `Array.withUnsafeBufferPointer`. Unless mentioned above, standard library APIs that do not have an unsafe type in their signature, but use unsafe constructs in their implementation, will be marked `@safe(unchecked)` because they provide safe abstractions to client code. From 228b68e34390f43dc7457b475bb261fc14b5b425 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 20 Dec 2024 11:50:07 -0800 Subject: [PATCH 3952/4563] Provide an example of diagnosing a conformance to an unsafe type --- proposals/nnnn-strict-memory-safety.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index c14e264bc3..0219cf666c 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -291,7 +291,17 @@ extension ConformsToP: @unsafe P { } ``` -Use of an `@unsafe` conformance for any reason (e.g., when that conformance is needed to call a generic function with a `ConformsToP` requirement) is diagnosed as an unsafe use, much like use of an `@unsafe` declaration. +Use of an `@unsafe` conformance for any reason (e.g., when that conformance is needed to call a generic function with a `ConformsToP` requirement) is diagnosed as an unsafe use, much like use of an `@unsafe` declaration. For example + +```swift +func acceptP(_: T.Type) { } + +func passUnsafe() { + acceptP(ConformsToP.self) // warning: use of @unsafe conformance of 'ConformsToP' to protocol 'P' +} +``` + + ### Strict safety mode and escalatable warnings From e5e2ab58d7bba29e0a00cd325239bdd4b1fba92e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 24 Dec 2024 09:49:06 -0800 Subject: [PATCH 3953/4563] Remove `@unchecked Sendable` from the list of things to diagnose; it's already covered --- proposals/nnnn-strict-memory-safety.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 0219cf666c..d971a43080 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -217,7 +217,6 @@ The following language constructs are always considered to be unsafe: The following language constructs are considered to be unsafe when strict concurrency checking is enabled (i.e., in the Swift 6 language mode): -* `@unchecked Sendable`: A `Sendable` conformance that cannot be checked by the compiler, which could introduce thread safety issues. * `nonisolated(unsafe)`: Allows a property to be accessed from concurrent code without ensuring that such accesses are done so safely. * `@preconcurrency` imports: Suppresses diagnostics related to data race safety when they relate to specific imported modules, which can introduce thread safety issues. @@ -301,8 +300,6 @@ func passUnsafe() { } ``` - - ### Strict safety mode and escalatable warnings The strict memory safety mode can be enabled with the new compiler flag `-strict-memory-safety`. From 3bb4a176235556c09bfb9a3a4bab21225afb190c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 24 Dec 2024 09:49:57 -0800 Subject: [PATCH 3954/4563] Clarify that it's the uses of unowned(unsafe) and nonisolated(unsafe) that are unsafe --- proposals/nnnn-strict-memory-safety.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index d971a43080..a941234402 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -212,12 +212,12 @@ extension Array { The following language constructs are always considered to be unsafe: -* `unowned(unsafe)`: Used to store a reference without maintaining its reference count. The safe counterpart, `unowned`, uses dynamic checking to ensure that the reference isn't accessed after the corresponding object has been released. The `unsafe` variant disables that dynamic checking. +* `unowned(unsafe)`: Used to store a reference without maintaining its reference count. The safe counterpart, `unowned`, uses dynamic checking to ensure that the reference isn't accessed after the corresponding object has been released. The `unsafe` variant disables that dynamic checking. Uses of `unowned(unsafe)` entities are not memory-safe. * `unsafeAddressor`, `unsafeMutableAddressor`: These accessors vend an unsafe pointer, and are therefore unsafe to declare. Other accessors (e.g., `get` and `set`) can provide safe alternatives. The following language constructs are considered to be unsafe when strict concurrency checking is enabled (i.e., in the Swift 6 language mode): -* `nonisolated(unsafe)`: Allows a property to be accessed from concurrent code without ensuring that such accesses are done so safely. +* `nonisolated(unsafe)`: Allows a property to be accessed from concurrent code without ensuring that such accesses are done so safely. Uses of `nonisolated(unsafe)` entities are not memory-safe. * `@preconcurrency` imports: Suppresses diagnostics related to data race safety when they relate to specific imported modules, which can introduce thread safety issues. ### Unsafe standard library APIs From 8bdcc00c573cb4136476a07a9ed4e74d539852f1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 24 Dec 2024 09:51:46 -0800 Subject: [PATCH 3955/4563] Distinguish between "@unsafe" and "not safe" --- proposals/nnnn-strict-memory-safety.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index a941234402..e64311a46f 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -268,7 +268,7 @@ The `@unsafe` annotation is at the class level because any use of the `Sub` type ### Unsafe conformances -Implementing a protocol requirement that is not `@unsafe` (and not part of an `@unsafe` protocol) within an `@unsafe` declaration introduces unsafety, so it will produce a diagnostic in the strict safety mode: +Implementing a protocol requirement that is safe (and not part of an `@unsafe` protocol) within an `@unsafe` declaration introduces unsafety, so it will produce a diagnostic in the strict safety mode: ```swift protocol P { From 85c76d2aca1f9939f5364e5e4a07767197daf6ec Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 24 Dec 2024 11:29:01 -0800 Subject: [PATCH 3956/4563] Add a section on SerialExecutor and Actor This is in Future Directions for now, because we need lifetime dependencies to be able to express these APIs. --- proposals/nnnn-strict-memory-safety.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index e64311a46f..d5c83996d1 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -330,6 +330,29 @@ The attributes and strict memory-safety checking model proposed here have no imp ## Future Directions +### The `SerialExecutor` and `Actor` protocols + +The `SerialExecutor` protocol provides a somewhat unique challenge for the strict memory safety mode. For one, it is impossible to implement this protocol with entirely safe code due to the presence of the `unownedExecutor` requirement: + +```swift +protocol SerialExecutor: Executor { + // ... + @unsafe var unownedExecutor: UnownedSerialExecutor { get } +} +``` + +To make it possible to safely implement `SerialExecutor`, the protocol will need to be extended with a safe form of `unownedExecutor`, which itself will likely require a [non-escapable](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md) form of `UnownedSerialExecutor` to provide lifetime safety without introducing any overhead. The `Actor` protocol has the same `unownedExecutor` requirement, so it will need the corresponding safe variant. The Swift implementation will need to start using this new requirement for scheduling work on actors to eliminate the implicit use of unsafe constructs. + +The `SerialExecutor` protocol has additional semantic constraints involving the serial execution of the jobs provided to the executor. While conformance to any protocol implies that the conforming type meets the documented semantic requirements, `SerialExecutor` is unique in that the data-race safety model (and therefore the memory safety model) depends on it correctly implementing these semantics: a conforming type that currently executes two jobs will create memory-safety violations. There are a few options for addressing this: + +* The `SerialExecutor` protocol itself could be marked `@unsafe`, meaning that any use of this protocol must account for unsafety. +* Some requirements of the `SerialExecutor` protocol (such as the replacement for `unownedExecutor`) could be marked `@unsafe`, so any use of this protocol's requirements must account for unsafety. +* Conformance to the `SerialExecutor` protocol could require some attestation (such as `@safe(unchecked)`) to make it clear from the source code that there is some unsafety encapsulated in the conformance. + +The first two options are the most straightforward, but the fact that actors have implicit uses of `SerialExecutor` means that it would effectively make every actor `@unsafe`. This pushes the responsibility for acknowledging the memory unsafety to clients of `SerialExecutor`, rather than at the conforming type where the responsibility for a correct implementation lies. The third option appears best, because it provides an auditable place to assert memory safety that corresponds with where extra care must be taken to avoid introducing a problem. + +It is unclear whether `SerialExecutor` is or will be the only protocol of this nature. If there are others, it could be worth providing a special form of the `@unsafe` attribute on the protocol itself, such as `@unsafe(conforms)`, that is only considered unsafe for conforming types. + ### Overloading to stage in safe APIs When adopting the strict memory safety mode, it's likely that a Swift module will want to replace existing APIs that traffic in unsafe types (such as `UnsafeMutablePointer`) with safer equivalents (such as `Span`). To retain compatibility for older clients, the existing APIs will need to be left in place. Unfortunately, this might mean that the best name for the API is already taken. For example, perhaps we have a data packet that exposes its bytes via a property: From 312dc56334734d056ceee5c5ccdbd19e60a290ab Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 24 Dec 2024 11:34:33 -0800 Subject: [PATCH 3957/4563] Add some acknowledgments --- proposals/nnnn-strict-memory-safety.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index d5c83996d1..56126340c6 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -467,3 +467,6 @@ This proposal introduced strict safety checking as an opt in mode and not an [*u * Interoperability with the C family of languages is an important feature for Swift. Most C(++) APIs are unlikely to ever adopt the safety-related attributes described above, which means that enabling strict safety checking by default would undermine the usability of C(++) interoperability. * Swift's current (non-strict) memory safety by default is likely to be good enough for the vast majority of users of Swift, so the benefit of enabling stricter checking by default is unlikely to be worth the disruption it would cause. +## Acknowledgments + +This proposal has been greatly improved by the feedback from Félix Cloutier, Gábor Horváth, Frederick Kellison-Linn, Karl Wagner, and Xiaodi Wu. From 04069f021b08c462d54b692dfa4b263e86a3962a Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 2 Jan 2025 15:47:37 +0000 Subject: [PATCH 3958/4563] Mark SE-0439 (trailing comma) as implemented in Swift 6.1 Code with trailing commas now compiles successfully with Swift 6.1 snapshots from swift.org. --- proposals/0439-trailing-comma-lists.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0439-trailing-comma-lists.md b/proposals/0439-trailing-comma-lists.md index eafd42b52b..4872818ef9 100644 --- a/proposals/0439-trailing-comma-lists.md +++ b/proposals/0439-trailing-comma-lists.md @@ -4,7 +4,7 @@ * Author: [Mateus Rodrigues](https://github.com/mateusrodriguesxyz) * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Accepted with modifications** -* Implementation: `main` https://github.com/swiftlang/swift/pull/74522# +* Implementation: Implemented (Swift 6.1) * Previous Proposal: [SE-0084](0084-trailing-commas.md) * Review: ([pitch](https://forums.swift.org/t/pitch-allow-trailing-comma-in-tuples-arguments-and-if-guard-while-conditions/70170)), ([review](https://forums.swift.org/t/se-0439-allow-trailing-comma-in-comma-separated-lists/72876)), ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0439-allow-trailing-comma-in-comma-separated-lists/73216)) * Previous Revision: ([1](https://github.com/swiftlang/swift-evolution/blob/7864fa20cfb3a43aa6874feedb5aedb8be02da2c/proposals/0439-trailing-comma-lists.md)) From 5731687a484280fb00b55abaf54d26a8c9fa8b1a Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 2 Jan 2025 15:48:35 +0000 Subject: [PATCH 3959/4563] Remove redundant whitespace in `0439-trailing-comma-lists.md` --- proposals/0439-trailing-comma-lists.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0439-trailing-comma-lists.md b/proposals/0439-trailing-comma-lists.md index 4872818ef9..128e3ca8c6 100644 --- a/proposals/0439-trailing-comma-lists.md +++ b/proposals/0439-trailing-comma-lists.md @@ -4,7 +4,7 @@ * Author: [Mateus Rodrigues](https://github.com/mateusrodriguesxyz) * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Accepted with modifications** -* Implementation: Implemented (Swift 6.1) +* Implementation: Implemented (Swift 6.1) * Previous Proposal: [SE-0084](0084-trailing-commas.md) * Review: ([pitch](https://forums.swift.org/t/pitch-allow-trailing-comma-in-tuples-arguments-and-if-guard-while-conditions/70170)), ([review](https://forums.swift.org/t/se-0439-allow-trailing-comma-in-comma-separated-lists/72876)), ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0439-allow-trailing-comma-in-comma-separated-lists/73216)) * Previous Revision: ([1](https://github.com/swiftlang/swift-evolution/blob/7864fa20cfb3a43aa6874feedb5aedb8be02da2c/proposals/0439-trailing-comma-lists.md)) From 73f8452f1bcd38305e2e5cad19de45fdbd5d4706 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 2 Jan 2025 16:00:34 +0000 Subject: [PATCH 3960/4563] Mark SE-0450 (package traits) as implemented in 6.1 This feature was cherry-picked to 6.1 with experimental flags were removed after SE acceptance. --- proposals/0450-swiftpm-package-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index c00ecea147..737364f821 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -3,7 +3,7 @@ * Proposal: [SE-0450](0450-swiftpm-package-traits.md) * Authors: [Franz Busch](https://github.com/FranzBusch), [Max Desiatov](https://github.com/MaxDesiatov) * Review Manager: [Mishal Shah](https://github.com/shahmishal) -* Status: **Accepted** +* Status: Implemented (Swift 6.1) * Implementation: https://github.com/swiftlang/swift-package-manager/pull/7704, https://github.com/swiftlang/swift-package-manager/pull/7703, https://github.com/swiftlang/swift-package-manager/pull/7702, https://github.com/swiftlang/swift-package-manager/pull/7701, https://github.com/swiftlang/swift-package-manager/pull/7694, https://github.com/swiftlang/swift-package-manager/pull/7689, https://github.com/swiftlang/swift-package-manager/pull/8178 * Experimental Implementation: Gated on `@_spi(ExperimentalTraits)` in package manifests and `--experimental` prefix for CLI options * Review: ([pitch](https://forums.swift.org/t/pitch-package-traits/72191)) ([review](https://forums.swift.org/t/se-0450-package-traits/75598)) From 4c2257064f1b903a9f0dadca2fa220bd808007b7 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 3 Jan 2025 12:49:21 +0000 Subject: [PATCH 3961/4563] Fix typo (missing dot) in SE-0450 text `stable public API Currently` -> `stable public API. Currently` --- proposals/0450-swiftpm-package-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index c00ecea147..1573a81c30 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -54,7 +54,7 @@ rules might break in the future. ### Experimental APIs Some packages want to introduce new functionality without yet committing to a -stable public API Currently, those modules and APIs are often underscored or +stable public API. Currently, those modules and APIs are often underscored or specifically annotated. While this approach works it comes with downsides such as hiding the APIs in code completion. From 4a2cc96e109dd164ceecc041307cce5b36667ee3 Mon Sep 17 00:00:00 2001 From: Bar <815372+barbaramartina@users.noreply.github.com> Date: Sat, 4 Jan 2025 11:50:48 +0100 Subject: [PATCH 3962/4563] Update 0414-region-based-isolation.md Adjusting the code to match the scenario described in the example. --- proposals/0414-region-based-isolation.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/0414-region-based-isolation.md b/proposals/0414-region-based-isolation.md index 381b2176fa..bbcee1e072 100644 --- a/proposals/0414-region-based-isolation.md +++ b/proposals/0414-region-based-isolation.md @@ -321,9 +321,10 @@ Now lets apply these rules to some specific examples in Swift code: ```swift func captureInClosure() { let x = NonSendable() - // Regions: [(x)] - let closure = { print(x) } - // Regions: [(x, closure)] + let y = NonSendable() + // Regions: [(x), (y)] + let closure = { print(x); print(y) } + // Regions: [(x, y, closure)] } ``` From c83feb713844ad4c855a8c9b270d4b9bf9e9069f Mon Sep 17 00:00:00 2001 From: Bar <815372+barbaramartina@users.noreply.github.com> Date: Sat, 4 Jan 2025 12:35:43 +0100 Subject: [PATCH 3963/4563] Update 0414-region-based-isolation.md Updating example to clarify func 'caller' and 'callee' (callee was used twice) --- proposals/0414-region-based-isolation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0414-region-based-isolation.md b/proposals/0414-region-based-isolation.md index 381b2176fa..ae128e7754 100644 --- a/proposals/0414-region-based-isolation.md +++ b/proposals/0414-region-based-isolation.md @@ -610,7 +610,7 @@ func nonIsolatedCaller(_ x: NonSendable) async { ``` In the example above, `x` is in a task isolated region. Since -`nonIsolatedCallee` will execute on the same task as `nonIsolatedCallee`, they +`nonIsolatedCallee` will execute on the same task as `nonIsolatedCaller`, they are in the same isolation domain and a transfer does not occur. In contrast, `transferToMainActor` is in a different isolation domain so passing `x` to it is a transfer resulting in an error. From dc8806273317c690ce1c07166647b73cbbc5ae83 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sun, 5 Jan 2025 20:03:00 +0000 Subject: [PATCH 3964/4563] Apply suggestions from code review Co-authored-by: Ben Rimmington --- proposals/0439-trailing-comma-lists.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0439-trailing-comma-lists.md b/proposals/0439-trailing-comma-lists.md index 128e3ca8c6..1c4ada76c5 100644 --- a/proposals/0439-trailing-comma-lists.md +++ b/proposals/0439-trailing-comma-lists.md @@ -3,8 +3,8 @@ * Proposal: [SE-0439](0439-trailing-comma-lists.md) * Author: [Mateus Rodrigues](https://github.com/mateusrodriguesxyz) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Accepted with modifications** -* Implementation: Implemented (Swift 6.1) +* Status: **Implemented (Swift 6.1)** +* Implementation: [swiftlang/swift#74522](https://github.com/swiftlang/swift/pull/74522) * Previous Proposal: [SE-0084](0084-trailing-commas.md) * Review: ([pitch](https://forums.swift.org/t/pitch-allow-trailing-comma-in-tuples-arguments-and-if-guard-while-conditions/70170)), ([review](https://forums.swift.org/t/se-0439-allow-trailing-comma-in-comma-separated-lists/72876)), ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0439-allow-trailing-comma-in-comma-separated-lists/73216)) * Previous Revision: ([1](https://github.com/swiftlang/swift-evolution/blob/7864fa20cfb3a43aa6874feedb5aedb8be02da2c/proposals/0439-trailing-comma-lists.md)) From 1a3020256ce7f2cda9805d89ae9801ec10895eed Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Sun, 5 Jan 2025 20:04:13 +0000 Subject: [PATCH 3965/4563] Apply suggestions from code review Co-authored-by: Ben Rimmington --- proposals/0450-swiftpm-package-traits.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index 737364f821..e634bd8206 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -3,10 +3,10 @@ * Proposal: [SE-0450](0450-swiftpm-package-traits.md) * Authors: [Franz Busch](https://github.com/FranzBusch), [Max Desiatov](https://github.com/MaxDesiatov) * Review Manager: [Mishal Shah](https://github.com/shahmishal) -* Status: Implemented (Swift 6.1) +* Status: **Implemented (Swift 6.1)** * Implementation: https://github.com/swiftlang/swift-package-manager/pull/7704, https://github.com/swiftlang/swift-package-manager/pull/7703, https://github.com/swiftlang/swift-package-manager/pull/7702, https://github.com/swiftlang/swift-package-manager/pull/7701, https://github.com/swiftlang/swift-package-manager/pull/7694, https://github.com/swiftlang/swift-package-manager/pull/7689, https://github.com/swiftlang/swift-package-manager/pull/8178 * Experimental Implementation: Gated on `@_spi(ExperimentalTraits)` in package manifests and `--experimental` prefix for CLI options -* Review: ([pitch](https://forums.swift.org/t/pitch-package-traits/72191)) ([review](https://forums.swift.org/t/se-0450-package-traits/75598)) +* Review: ([pitch](https://forums.swift.org/t/pitch-package-traits/72191)) ([review](https://forums.swift.org/t/se-0450-package-traits/75598)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0450-package-traits/76705)) ## Introduction From 88a94372cc78c5db38ea1d31bd9ce2711063e95b Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 6 Jan 2025 09:17:25 +0000 Subject: [PATCH 3966/4563] Update 0450-swiftpm-package-traits.md Co-authored-by: Ben Rimmington --- proposals/0450-swiftpm-package-traits.md | 1 - 1 file changed, 1 deletion(-) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index e634bd8206..2e7924c3c4 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -5,7 +5,6 @@ * Review Manager: [Mishal Shah](https://github.com/shahmishal) * Status: **Implemented (Swift 6.1)** * Implementation: https://github.com/swiftlang/swift-package-manager/pull/7704, https://github.com/swiftlang/swift-package-manager/pull/7703, https://github.com/swiftlang/swift-package-manager/pull/7702, https://github.com/swiftlang/swift-package-manager/pull/7701, https://github.com/swiftlang/swift-package-manager/pull/7694, https://github.com/swiftlang/swift-package-manager/pull/7689, https://github.com/swiftlang/swift-package-manager/pull/8178 -* Experimental Implementation: Gated on `@_spi(ExperimentalTraits)` in package manifests and `--experimental` prefix for CLI options * Review: ([pitch](https://forums.swift.org/t/pitch-package-traits/72191)) ([review](https://forums.swift.org/t/se-0450-package-traits/75598)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0450-package-traits/76705)) ## Introduction From 5450c018b1912939f86873bf580c8c5c7ed3eb25 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Mon, 6 Jan 2025 13:27:37 -0800 Subject: [PATCH 3967/4563] Update SE-0452 in response to LSG review (#2631) * Update SE-0452 in response to LSG review * Update proposals/0452-integer-generic-parameters.md Co-authored-by: Ben Rimmington * Update 0452-integer-generic-parameters.md --------- Co-authored-by: Ben Rimmington Co-authored-by: Ben Cohen --- proposals/0452-integer-generic-parameters.md | 61 ++++++++++++++++++-- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/proposals/0452-integer-generic-parameters.md b/proposals/0452-integer-generic-parameters.md index 875ff67d5f..e8e2897a40 100644 --- a/proposals/0452-integer-generic-parameters.md +++ b/proposals/0452-integer-generic-parameters.md @@ -3,9 +3,9 @@ * Proposal: [SE-0452](0452-integer-generic-parameters.md) * Authors: [Alejandro Alonso](https://github.com/Azoy), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active review (November 5-19 2024)** +* Status: **Active review (Janyuary 6-14 2024)** * Implementation: [apple/swift#75518](https://github.com/apple/swift/pull/75518) -* Review: ([pitch](https://forums.swift.org/t/integer-generic-parameters/74181)) ([review](https://forums.swift.org/t/se-0452-integer-generic-parameters/75844)) +* Review: ([pitch](https://forums.swift.org/t/integer-generic-parameters/74181)) ([first review](https://forums.swift.org/t/se-0452-integer-generic-parameters/75844)) ([second review](https://forums.swift.org/t/second-review-se-0452-integer-generic-parameters/77043)) ## Introduction @@ -33,7 +33,7 @@ number of elements they consume or produce. ## Proposed solution Generic types can now be parameterized by integer parameters, declared using -the syntax `let : Int` inside of the generic parameter angle brackets: +the syntax `let : Int` inside of the generic parameter angle brackets: ```swift struct Vector { @@ -54,11 +54,29 @@ Or it can be instantiated using integer generic parameters from the surrounding generic environment: ```swift -struct Matrix { - var matrix: Vector> +struct Matrix { + var matrix: Vector> } ``` +Integer generic parameters become static constant members of the type, with +the same visibility as the type itself: + +```swift +public struct Matrix { + // implicitly has these members: + // public static var columns: Int { get } + // public static var rows: Int { get } +} + +// From another module: + +import struct Matrices.Matrix + +print(Matrix<4, 3>.columns) // prints 4 +print(Matrix<4, 3>.rows) // prints 3 +``` + Generic functions and methods can also be parameterized by integer generic parameters. As with other generic parameters, the values of the generic arguments for a call are inferred from the types of the argument values @@ -156,6 +174,18 @@ func contrived() { } ``` +Integer generic parameters of types become static members of that type, +with the same visibility as the type itself. It is an error to try to +declare another static property with the same name as an integer generic +parameter within the type declaration, just as it would if the property +were independently declared: + +```swift +struct Vec { + static let count: Int // error: Vec already has a static property `count` +} +``` + In a type reference, an integer generic argument can be provided as either a literal integer, or as a reference to an integer generic parameter from the enclosing generic context. References to type generic parameters, @@ -556,6 +586,27 @@ generic parameters, like `Vector[count]`. This would avoid the need for disambiguation if an arbitrary expression can be used as a `count` in the future. +### Behavior of static properties corresponding to generic parameters + +In response to the first round of review, we added static properties +corresponding to the integer generic parameters of types, based on feedback +that, in many if not most cases, the values of the generic parameters would end +up being redeclared as static parameters, and the existence and names of the +generic parameters are already essentially part of its public API, since +usage of the type must be able to provide arguments to the parameters, and +extensions can refer to the parameters by their names. + +One good argument against doing this is that we don't already do anything +analogous for type generic parameters, such as presenting them as a typealias +member of the type. We think that it would be beneficial to do so, however, +and in discussion with the LSG, there were attempts to make this happen in +the past, but they ran into source compatibility issues. If there is a chance +to take a source break that enables this functionality for type generic +parameters, the LSG thinks it is worth considering. With integer generic +parameters, we have the opportunity to do the right thing out of the gate, +and we would have similar source-breaking considerations to do it later, so +we believe it is better to do the right thing up front. + ## Acknowledgments We would like to thank the following people for prototyping and design From 961b76abb9638178777188ce5ffeb8d8e4ca073d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 7 Jan 2025 12:47:00 -0800 Subject: [PATCH 3968/4563] The `-disable-access-control` flag should not be combined with strict safety checking Thank you, Gabor! --- proposals/nnnn-strict-memory-safety.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 56126340c6..26bbbad89c 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -241,6 +241,8 @@ All of these APIs will be marked `@unsafe`. For all of the types that are `@unsa The `-Ounchecked` compiler flag disables some checking in the standard library, including (for example) bounds checking on array accesses. It is generally discouraged in all Swift code, but is particularly problematic in conjunction with strict memory safety because it removes the checking that makes certain standard library APIs safe. Therefore, the compiler will produce a diagnostic when the two features are combined. +The `-disable-access-control` flag ignores access specifiers entirely, allowing one to (for example) access a `private` declaration from outside its defining file. This could allow one to break invariants of a type that can lead to memory-safety issues, such as breaking the invariant of `Range` that the lower bound not exceed the upper bound. The compiler will produce a diagnostic when the two features are combined. + ### Unsafe overrides Overriding a safe method within an `@unsafe` one could introduce unsafety, so it will produce a diagnostic in the strict safety mode: From a85712173a5d85d1ba235cf6d2423a0a872ff969 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 8 Jan 2025 06:15:44 -0800 Subject: [PATCH 3969/4563] `@exclusivity(unchecked)` is also an unsafe feature Thank you to Jed Fox! --- proposals/nnnn-strict-memory-safety.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 26bbbad89c..7908f44d14 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -214,6 +214,7 @@ The following language constructs are always considered to be unsafe: * `unowned(unsafe)`: Used to store a reference without maintaining its reference count. The safe counterpart, `unowned`, uses dynamic checking to ensure that the reference isn't accessed after the corresponding object has been released. The `unsafe` variant disables that dynamic checking. Uses of `unowned(unsafe)` entities are not memory-safe. * `unsafeAddressor`, `unsafeMutableAddressor`: These accessors vend an unsafe pointer, and are therefore unsafe to declare. Other accessors (e.g., `get` and `set`) can provide safe alternatives. +* `@exclusivity(unchecked)`: Used to remove dynamic exclusivity checks from a particular variable, which can mean that dynamic exclusivity violations go undetected at run time, causing a memory safety violation. The following language constructs are considered to be unsafe when strict concurrency checking is enabled (i.e., in the Swift 6 language mode): From f33f39992fe5182de3bbfdc8bbc640c9dd409f11 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 8 Jan 2025 11:37:24 -0800 Subject: [PATCH 3970/4563] Address some of Gabor's comments about the merits of unsafe blocks --- proposals/nnnn-strict-memory-safety.md | 28 ++++++++------------------ 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 7908f44d14..c1dd0f419d 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -414,7 +414,7 @@ There are downsides to this approach. It partially undermines the source compati The attribute `@safe(unchecked)` indicates that a definition is safe despite the use of unsafe constructs in its body. The attribute has no effect on the client, and could effectively be eliminated from the public interface (e.g., documentation, Swift textual interfaces, etc.) without changing how clients behave. -There is an alternative formulation for acknowledging unsafe code that is used in some peer languages like C# and Rust: an `unsafe` block, which is a statement (or expression) that suppresses diagnostics about uses of unsafe code within it. For example, the `sum` example could be written as follows: +There is an alternative formulation for acknowledging unsafe code that is used in some peer languages like C# and Rust: an `unsafe` block, which is a statement that suppresses diagnostics about uses of unsafe code within it. For example, the `sum` example could be written as follows: ```swift extension Array { @@ -428,7 +428,12 @@ extension Array { } ``` -For Swift, `unsafe` would be a statement that can also be used as an expression when its body is an expression, much like `if` or `switch` expressions following [SE-0380](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0380-if-switch-expressions.md). +For Swift, an `unsafe` block would be a statement that can also be used as an expression when its body is an expression, much like `if` or `switch` expressions following [SE-0380](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0380-if-switch-expressions.md). + +`unsafe` blocks have some advantages over the proposed `@safe(unchecked)` attribute, including: + +* They can be limited to smaller amounts of code than a whole function, which can make auditing for the unsafe parts of a function easier. +* The `unsafe` blocks are more clearly part of the implementation of a function, not the interface. This proposal suggests the `@safe(unchecked)` attribute instead of `unsafe` blocks for a few reasons, but the choice is effectively arbitrary: either syntax will work with the rest of this proposal. Some of the reasons for preferring `@safe(unchecked)` include: @@ -443,24 +448,7 @@ This proposal suggests the `@safe(unchecked)` attribute instead of `unsafe` bloc * The `unsafe` blocks end up adding another level of nesting, which also means that introducing them to silence warnings causes unnecessarily large amount of code change when adopting the feature. -* The `unsafe` blocks aren't a natural boundary between unsafe and safe code in the same way as a function boundary, because there is no place to state the expected invariants. Combined with pressure to make `unsafe` blocks have as little code as possible (as a proxy for that code being safer), it becomes easy for "minimizing the amount of code in unsafe blocks" to introduce more safety problems. For example, `sum` might be incorrectly factored as follows: - - ```swift - extension Array { - func sum() -> Int { - let (ptr, count) = unsafe { - withUnsafeBufferPointerSimplified { buffer in - (buffer.baseAddress, buffer.count) - } - } - return unsafe { c_library_sum_function(ptr, count, 0) } - } - } - ``` - - Here, the base address pointer has escaped the `withUnsafeBufferPointerSimplified` block, causing a member safety problem that wouldn't have been there before. If we tried to do this kind of factoring with the `@safe(unchecked)` approach, the intermediate function producing the pointer would have to be marked `@unsafe`. - - +* `@safe(unchecked)` can be used in places that aren't executable code, such as protocol conformances. ### Strictly-safe-by-default From abae8cfb265afb1a51c005bff7fa6d1683963837 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 8 Jan 2025 13:34:07 -0800 Subject: [PATCH 3971/4563] Introduce an Alternatives Considered section to talk about `unsafe` as an effect Suggested by Felix Cloutier --- proposals/nnnn-strict-memory-safety.md | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index c1dd0f419d..1daa8c7a8f 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -450,6 +450,36 @@ This proposal suggests the `@safe(unchecked)` attribute instead of `unsafe` bloc * `@safe(unchecked)` can be used in places that aren't executable code, such as protocol conformances. +* `@safe(unchecked)` can be introduced with no source-compatibility impact, whereas the `unsafe` block will break code that calls a function named `unsafe` given a closure. + +### `unsafe` as an effect + +As another alternative to `@safe(unchecked)` and `unsafe` blocks, we could model accesses to unsafe enties as an effect like `async` and `throws`. In such cases, referring to any declaration that is explicitly `@unsafe` or whose type involves an `@unsafe` type would require `unsafe` on the enclosing expression just like using an `async` function requires `await` and a `throws` function requires `try`. This would require more annotations than the `unsafe` block, because individual expressions would need to be marked. For our example with producing the sum of integer arrays, it would look like this: + +```swift +extension Array { + func sum() -> Int { + unsafe withUnsafeBufferPointerSimplified { buffer in + unsafe c_library_sum_function(buffer.baseAddress, buffer.count, 0) + } + } +} +``` + +Unlike with the `unsafe` block, one `unsafe` annotation doesn't cover both expressions: the first `unsafe` is needed because `withUnsafeBufferPointerSimplified` involves unsafe types, and the second because `c_library_sum_function`, `buffer.baseAddress`, and `buffer.count` all involve unsafe types. + +There are some benefits to this approach: + +* It makes it easier to audit those places where unsafe code is used, because `unsafe` only applies to individual expressions, not large chunks of code. +* It builds on the existing behavior of `try` and `await` in a way that should be understandable to most Swift programmers. + +However, both of these have corresponding downsides: + +* The `unsafe` effect could make code very noisy when using unsafe types, and starts to feel quite redundant given that most unsafe APIs already intentionally have "unsafe" somewhere in the name: `unsafe withUnsafeXYZ` sounds like it doubly-punishes the use of unsafe code. While this is perhaps a minor annoyance, it could mean that some unsafe APIs designed with strict safety checking in mind would omit the explicit "unsafe" from the name. This change would make code *not* using strict safety checking less safe, because we'd no longer get the benefits of the longstanding conventions built around calling unsafe functions "unsafe". +* Both `throws` and `async` are part of the type system, whereas `@unsafe` is not, so `unsafe` is effect-like but not *exactly* an effect. There are practical differences here, such as the fact that naming a throwing function but not calling it (e.g., `let g = f`) doesn't require `try` (becaue `throws` is captured in the type of `g`) but would require `unsafe` if `f` is unsafe. Having `unsafe` be effect-like muddles the message about how effects work in Swift. + +Additionally, introducing `unsafe` as an effect has the same source-compatibility impact as the `unsafe` block described in the previous section: code like `unsafe { ... }` can exist today to call a function named `unsafe` with closure, and would get a different meaning when `unsafe` is parsed as an effect. + ### Strictly-safe-by-default This proposal introduced strict safety checking as an opt in mode and not an [*upcoming* language feature](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md) because there is no intent to make this feature the default behavior in a future language mode. There are several reasons why this checking should remain an opt-in feature for the foreseeable future: From b2d5c8d4eebf73b3ffebaff7cd1f3049e60e9411 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 8 Jan 2025 13:37:34 -0800 Subject: [PATCH 3972/4563] Fix typos --- proposals/nnnn-strict-memory-safety.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 1daa8c7a8f..6f66845a0f 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -146,7 +146,7 @@ Module `A` defines a type, `DataWrapper`, that is `@unsafe`. It can be compiled Module `B` uses the `DataWrapper` type. If compiled without strict safety checking, there will be no diagnostics about memory safety. If compiled with strict safety checking, there will be a diagnostic about `wrapper` using an `@unsafe` type (`DataWrapper`) in its interface. This diagnostic can be ignored. -In module `C` enables strict memory safety, the use of `MyType` is considered safe (since it was not marked `@unsafe` and doesn't involve unsafe types in its interface). However, the access to `wrapper` will result in a diagnostic, because the type of `wrapper` involves an `@unsafe` type. +If module `C` enables strict memory safety, the use of `MyType` is considered safe (since it was not marked `@unsafe` and doesn't involve unsafe types in its interface). However, the access to `wrapper` will result in a diagnostic, because the type of `wrapper` involves an `@unsafe` type. ### Encapsulating unsafe behavior @@ -171,7 +171,7 @@ extension MyType { } ``` -However, these two APIs diff in the severity of the problem they post for memory safety: `wrapper` has an unsafe type in its interface, so any use of `wrapper` is fundamentally unsafe. However, `MyType` itself can encapsulate the memory-unsafe behavior within a safe API., The `MyType.checksum` operation is actually safe for clients to use. Marking it as `@unsafe` is therefore undesirable, because it (incorrectly) forces clients to treat this as an unsafe API, causing unnecessary extra work and deluting the value of correctly-identified unsafe APIs. +However, these two APIs differ in the severity of the problem they pose for memory safety: `wrapper` has an unsafe type in its interface, so any use of `wrapper` is fundamentally unsafe. However, `MyType` itself can encapsulate the memory-unsafe behavior within a safe API., The `MyType.checksum` operation is actually safe for clients to use. Marking it as `@unsafe` is therefore undesirable, because it (incorrectly) forces clients to treat this as an unsafe API, causing unnecessary extra work and deluting the value of correctly-identified unsafe APIs. One option would be for the author of module `C` to simply ignore the memory-safety warnings produced within its body, which will have the effect of encapsulating the unsafe behavior, but would make it harder to ensure that all unsafe behavior has been accounted for in that module. Another option would be to factor this code into a separate module that doesn't enable strict safety checking, but this is a fairly heavyweight solution. @@ -263,7 +263,7 @@ to suppress this warning, the `Sub` class itself can be marked as `@unsafe`, e.g ```swift @unsafe class Sub: Super { - func f() { ... } // warning: override of safe instance method with unsafe instance method + func f() { ... } // no more warning } ``` From 309ec25d13372b16a6a41d5d44afab6245f7a87e Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Tue, 1 Oct 2024 10:58:21 -0700 Subject: [PATCH 3973/4563] Add SwiftPM @testable build setting proposal --- .../0000-swiftpm-testable-build-setting.md | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 proposals/0000-swiftpm-testable-build-setting.md diff --git a/proposals/0000-swiftpm-testable-build-setting.md b/proposals/0000-swiftpm-testable-build-setting.md new file mode 100644 index 0000000000..117750f94e --- /dev/null +++ b/proposals/0000-swiftpm-testable-build-setting.md @@ -0,0 +1,64 @@ +# SwiftPM @testable build setting + +* Proposal: [SE-0000](0000-swiftpm-testable-build-setting.md) +* Authors: [Jake Petroules](https://github.com/jakepetroules) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [swiftlang/swift-package-manager#8004](https://github.com/swiftlang/swift-package-manager/pull/8004) +* Review: ([pitch](https://forums.swift.org/t/pitch-swiftpm-testable-build-setting/75084)) + +## Introduction + +The current Swift Package Manager build system is currently hardcoded to pass the `-enable-testing` flag to the Swift compiler to enable `@testable import` when building in debug mode. + +Swift-evolution thread: [Pitch: [SwiftPM] @testable build setting](https://forums.swift.org/t/pitch-swiftpm-testable-build-setting/75084) + +## Motivation + +Not all targets in a given package make use of the `@testable import` feature (or wish to use it at all), but all targets are presently forced to build their code with this support enabled regardless of whether it's needed. + +Developers should be able to disable `@testable import` when it's not needed or desired, just as they're able to do so in Xcode's build system. + +On Windows in particular, where a shared library is limited to 65k exported symbols, disabling `@testable import` provides developers an option to significantly reduce the exported symbol count of a library by hiding all of the unnecessary internal APIs. It can also improve debug build performance as fewer symbols exported from a binary can result in faster linking. + +## Proposed solution + +Add a new Swift target setting API to specify whether testing should be enabled for the specified target, falling back to the current behavior by default. + +## Detailed design + +Add a new `enableTestableImport` API to `SwiftSetting` limited to manifests >= 6.1: + +```swift +public struct SwiftSetting { + // ... other settings + + @available(_PackageDescription, introduced: 6.1) + public static func enableTestableImport( + _ enable: Bool, + _ condition: BuildSettingCondition? = nil + ) -> SwiftSetting { + ... + } +} +``` + +The existing `--enable-testable-imports` / `--disable-testable-imports` command line flag to `swift-test` currently defaults to `--enable-testable-imports`. It will be changed to default to "unspecified" (respecting any target settings), and explicitly passing `--enable-testable-imports` or `--disable-testable-imports` will force all targets to enable or disable testing, respectively. + +Attempting to enable `@testable import` in release builds will result in a build warning. + +## Security + +New language version setting has no implications on security, safety or privacy. + +## Impact on existing packages + +Since this is a new API, all existing packages will use the default behavior - `@testable import` will be enabled when building for the debug configuration, and disabled when building for the release configuration. + +## Future directions + +In a future manifest version, we may want to change `@testable import` to be disabled by default when building for the debug configuration. + +## Alternatives considered + +None. From 56b87fec88a0cd8f3302253c58ee3ff4a4de44d2 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 9 Jan 2025 14:42:57 -0500 Subject: [PATCH 3974/4563] Assign SE-0455. --- ...uild-setting.md => 0455-swiftpm-testable-build-setting.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename proposals/{0000-swiftpm-testable-build-setting.md => 0455-swiftpm-testable-build-setting.md} (96%) diff --git a/proposals/0000-swiftpm-testable-build-setting.md b/proposals/0455-swiftpm-testable-build-setting.md similarity index 96% rename from proposals/0000-swiftpm-testable-build-setting.md rename to proposals/0455-swiftpm-testable-build-setting.md index 117750f94e..602b4c639b 100644 --- a/proposals/0000-swiftpm-testable-build-setting.md +++ b/proposals/0455-swiftpm-testable-build-setting.md @@ -1,8 +1,8 @@ # SwiftPM @testable build setting -* Proposal: [SE-0000](0000-swiftpm-testable-build-setting.md) +* Proposal: [SE-0455](0455-swiftpm-testable-build-setting.md) * Authors: [Jake Petroules](https://github.com/jakepetroules) -* Review Manager: TBD +* Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Awaiting review** * Implementation: [swiftlang/swift-package-manager#8004](https://github.com/swiftlang/swift-package-manager/pull/8004) * Review: ([pitch](https://forums.swift.org/t/pitch-swiftpm-testable-build-setting/75084)) From 8c0497860b606a10abf51d53a5fdc2ee81c6bed2 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 9 Jan 2025 12:46:08 -0800 Subject: [PATCH 3975/4563] [SE-0454]: adopt mimalloc for Windows toolchain (#2618) * proposals: propose adopting mimalloc for Windows toolchain Using mimalloc for the memory allocator in the compiler yields ~4% performance improvement according to local benchmarks. Propose this allocator as the default for the Windows build. * Allocate SE-0454 for the review. * Update 0454-memory-allocator.md Add a link to the build.ps1 change for the feature. --------- Co-authored-by: Alastair Houghton --- proposals/0454-memory-allocator.md | 73 ++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 proposals/0454-memory-allocator.md diff --git a/proposals/0454-memory-allocator.md b/proposals/0454-memory-allocator.md new file mode 100644 index 0000000000..f90f3922bc --- /dev/null +++ b/proposals/0454-memory-allocator.md @@ -0,0 +1,73 @@ +# Custom Allocator for Toolchain + +* Proposal: [SE-0454](0454-memory-allocator.md) +* Authors: [Saleem Abdulrasool](https://github.com/compnerd) +* Review Manager: [Alastair Houghton](https://github.com/al45tair) +* Status: **Awaiting Review** +* Vision: N/A +* Roadmap: N/A +* Bug: N/A +* Implementation: [swiftlang/swift#76563](https://github.com/swiftlang/swift/pull/76563) +* Upcoming Feature Flag: N/A +* Previous Proposal: N/A +* Previous Revision: N/A +* Review: N/A + +## Introduction + +The tools in the Swift toolchain require allocating data structures for +compiling the code. Different memory allocators have differing performance +characteristics. Changing the default memory allocator away from the default +(system) allocator can yield benefits if the allocator is better tuned to the +allocation patterns of the compiler. + +## Motivation + +A more effecient memory allocator would improve the performance of the compiler +on Windows. This allows better developer productivity by reducing compile time. + +## Proposed solution + +We propose to adopt mimalloc as the memory allocator for the Swift toolchain on +Windows. + +## Detailed design + +Building a test codebase yielded a 4% build time decrease when the toolchain was +built with mimalloc. + +## Source compatibility + +This proposal does not affect source compatibility. + +## ABI compatibility + +This proposal does not affect ABI of code. + +## Implications on adoption + +Additional files will need to be built, packaged, and shipped as part of the +toolchain. The mimalloc build is relatively light and the overall build time +impact is minimal. + +This change has no implications for the runtime, only the toolchain is changed. + +## Future directions + +None at this time. + +## Alternatives considered + +Alternative memory allocators were considered, including +[tcmalloc](https://github.com/google/tcmalloc) and +[tbb](https://github.com/intel/tbb). mimalloc is well supported, developed by +Microsoft, and has better characteristics comparatively. + +Leaving the allocator on the default system allocator leaves the compiler +without the performance improvements of an alternative allocator. + +## Acknowledgements + +Special thanks to @hjyamauchi for performing the work to integrate the mimalloc +build into the Windows build and collecting the performance numbers that showed +the improvement. From ad872d0dbeef30f38514de0b4661b9ea863e7ad3 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 10 Jan 2025 09:16:07 +0000 Subject: [PATCH 3976/4563] SE-0454: Update status --- proposals/0454-memory-allocator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0454-memory-allocator.md b/proposals/0454-memory-allocator.md index f90f3922bc..008029a666 100644 --- a/proposals/0454-memory-allocator.md +++ b/proposals/0454-memory-allocator.md @@ -3,7 +3,7 @@ * Proposal: [SE-0454](0454-memory-allocator.md) * Authors: [Saleem Abdulrasool](https://github.com/compnerd) * Review Manager: [Alastair Houghton](https://github.com/al45tair) -* Status: **Awaiting Review** +* Status: **In Review** * Vision: N/A * Roadmap: N/A * Bug: N/A From 9b97029071f76f9857049ee30fcd53288a3a37b1 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 10 Jan 2025 09:56:19 +0000 Subject: [PATCH 3977/4563] [SE-0454] Update metadata to fix dashboard errors --- proposals/0454-memory-allocator.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/proposals/0454-memory-allocator.md b/proposals/0454-memory-allocator.md index 008029a666..614a171316 100644 --- a/proposals/0454-memory-allocator.md +++ b/proposals/0454-memory-allocator.md @@ -3,15 +3,9 @@ * Proposal: [SE-0454](0454-memory-allocator.md) * Authors: [Saleem Abdulrasool](https://github.com/compnerd) * Review Manager: [Alastair Houghton](https://github.com/al45tair) -* Status: **In Review** -* Vision: N/A -* Roadmap: N/A -* Bug: N/A +* Status: **Active review (January 9–23)** * Implementation: [swiftlang/swift#76563](https://github.com/swiftlang/swift/pull/76563) -* Upcoming Feature Flag: N/A -* Previous Proposal: N/A -* Previous Revision: N/A -* Review: N/A +* Review: ([review](https://forums.swift.org/t/se-454-adopt-mimalloc-for-windows-toolchain/77096)) ## Introduction From 65eed26f003af88d4bf56a00932b598c8b1c33e3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 10 Jan 2025 11:38:15 -0800 Subject: [PATCH 3978/4563] Switch from `@safe(unchecked)` over to an `unsafe` effect. Thank you, Felix --- proposals/nnnn-strict-memory-safety.md | 165 +++++++++---------------- 1 file changed, 55 insertions(+), 110 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 6f66845a0f..fc9da1aa93 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -4,9 +4,9 @@ * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: TBD * Status: **Awaiting review** -* Vision: *if applicable* [Opt-in Strict Memory Safety Checking (Prospective)](https://github.com/swiftlang/swift-evolution/pull/2581) +* Vision: [Opt-in Strict Memory Safety Checking (Prospective)](https://github.com/swiftlang/swift-evolution/pull/2581) * Implementation: On main with experimental feature flags `AllowUnsafeAttribute` and `WarnUnsafe` -* Review: ([pitch](https://forums.swift.org/...)) +* Review: ([pitch](https://forums.swift.org/t/pitch-opt-in-strict-memory-safety-checking/76689)) ## Introduction @@ -41,8 +41,8 @@ Providing memory safety does not imply the absence of run-time failures. Good la This proposal introduces an opt-in strict memory safety checking mode that identifies all uses of unsafe behavior within the given module. There are several parts to this change: * A compiler flag `-strict-memory-safety` that enables warnings for all uses of unsafe constructs within a given module. All warnings will be in the diagnostic group `Unsafe`, enabling precise control over memory-safety-related warnings per [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md). -* An attribute `@unsafe` that indicates that a declaration is unsafe to use. Such declarations may use unsafe constructs within their signatures or definitions. -* An attribute `@safe(unchecked)` that indicates that a declaration provides a safe interface despite using unsafe constructs within its definition. The `unchecked` indicates that Swift cannot check this assertion of safety. +* An attribute `@unsafe` that indicates that a declaration is unsafe to use. Such declarations may use unsafe constructs within their signatures. +* An `unsafe` expression that marks any use of unsafe constructs in an expression, much like `try` and `await`. * Standard library annotations to identify unsafe declarations. ### Example of `unsafe` usage @@ -83,31 +83,45 @@ extension Array { func sum() -> Int { // warning: use of unsafe function 'withUnsafeBufferPointerSimplified' withUnsafeBufferPointerSimplified { buffer in + // warning: use of function 'c_library_sum_function' with unsafe type 'UnsafePointer''. c_library_sum_function(buffer.baseAddress, buffer.count, 0) } } } ``` -Both the call to `withUnsafeBufferPointerSimplified` (which is `@unsafe`) and the call to `c_library_sum_function` (which has a parameter of `@unsafe` type `UnsafePointer`) would trigger warnings about uses of unsafe constructs. The author of `sum` has a choice to suppress the warnings: +Both the call to `withUnsafeBufferPointerSimplified` (which is `@unsafe`) and the call to `c_library_sum_function` (which has a parameter of `@unsafe` type `UnsafePointer`) would trigger warnings about uses of unsafe constructs. -1. Mark the `sum` function as `@unsafe`, propagating the "unsafe" checking out to callers of `sum`; or -2. Mark the `sum` function as `@safe(unchecked)`, taking responsibility for the safety of the code within the body. Here, one needs to verify that the `UnsafeBufferPointer` itself is being used safely (i.e., accesses are in-bounds and the buffer doesn't escape the closure) and that `c_library_sum_function` does the same with the pointer and bounds it is given. +To suppress these warnings, both expressions must be marked with `unsafe` in the same manner as one would mark a throwing expression with `try` or an asynchronous expression with `async`. The warning-free version of this code is: + +```swift +extension Array { + func sum() -> Int { + unsafe withUnsafeBufferPointerSimplified { buffer in + unsafe c_library_sum_function(buffer.baseAddress, buffer.count, 0) + } + } +} +``` + +The `unsafe` keyword here indicates the presence of unsafe code within that expression. As with `try` and `await`, it can cover multiple sources of unsafety within that expression: the call to `c_library_sum_function` is unsafe, as is the use of `buffer` and `buffer.baseAddress`, yet they are all covered by one `unsafe`. + +Note that we do *not* require that the `sum` function be marked `@unsafe` just because it has unsafe code in it. The programmer may choose to indicate that `sum` is unsafe, but the assumption is that unsafe behavior is properly encapsulated when using `unsafe`. ### Incremental adoption The strict memory safety checking proposed here enforces a subset of Swift. Code written within this subset must also be valid Swift code, and must interoperate with Swift code that does not use this strict checking. Compared to other efforts in Swift that introduce stricter checking or a subset, strictly-safe Swift is smaller and more constrained, providing better interoperability and a more gradual adoption curve: -* Strict concurrency checking, the focus of the Swift 6 language mode, required major changes to the type system, including the propagation of `Sendable` and the understanding of what code must be run on the main actor. These are global properties that don't permit local reasoning, or even local fixes, making the interoperability problem particularly hard. In contrast, strict safety checking has little or no effect on the type system, and unsafety can be encapsulated with `@safe(unchecked)` or ignored by a module that doesn't enable the checking. +* Strict concurrency checking, the focus of the Swift 6 language mode, required major changes to the type system, including the propagation of `Sendable` and the understanding of what code must be run on the main actor. These are global properties that don't permit local reasoning, or even local fixes, making the interoperability problem particularly hard. In contrast, strict safety checking has little or no effect on the type system, and unsafety can be encapsulated with `unsafe` expressions or ignored by a module that doesn't enable the checking. * [Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0337-support-incremental-migration-to-concurrency-checking.md) is a subset of Swift that works without a runtime. Like the proposed strictly-safe subset, code written in Embedded Swift will also work as regular Swift. Embedded Swift and the strict safety checking proposed here are orthogonal and can be composed to (for example) ensure that firmware written in Swift has no runtime and provides the best memory-safety guarantees. -A Swift module that adopts strict safety checking can address all of the resulting diagnostics by applying the `@unsafe` and `@safe(unchecked)` attributes in the appropriate places, without changing any other code. This application of attributes can be automated through Fix-Its, making it possible to enable the mode and silence all diagnostics automatically. It would then be left to the programmer to audit those places that have used `@safe(unchecked)` to encapsulate unsafe behavior within a safe interface, to ensure that they are indeed safe. +A Swift module that adopts strict safety checking can address all of the resulting diagnostics by applying the `@unsafe` attribute and `unsafe` expression in the appropriate places, without changing any other code. This application of attributes can be automated through Fix-Its, making it possible to enable the mode and silence all diagnostics automatically. It would then be left to the programmer to audit those places that have used `unsafe` to encapsulate unsafe behavior, to ensure that they are indeed safe. -The introduction of the `@safe(unchecked)` attribute on a declaration has no effect on its clients. The introduction of the `@unsafe` attribute on a declaration has no effect on clients compiled without strict safety enabled. For clients that have enabled strict safety, they will start diagnosing uses of the newly-`@unsafe` API. However, these diagnostics are warnings with their own diagnostic group, so a client can ensure that they do not prevent the client from building. Therefore, modules can adopt strict safety checking at their own pace (or not) and clients of those modules are never "stuck" having to make major changes in response. +The introduction of the `@unsafe` attribute on a declaration has no effect on clients compiled without strict safety enabled. For clients that have enabled strict safety, they will start diagnosing uses of the newly-`@unsafe` API. However, these diagnostics are warnings with their own diagnostic group, so a client can ensure that they do not prevent the client from building. Therefore, modules can adopt strict safety checking at their own pace (or not) and clients of those modules are never "stuck" having to make major changes in response. ## Detailed design -This section describes how the `@unsafe` and `@safe` attributes interact with the strict type checking mode, and enumerates the places in the language and standard library that introduce non-memory-safe code. +This section describes how the primary proposed constructs, the `@unsafe` attribute and `unsafe` expression, interact with the strict type checking mode, and enumerates the places in the language, standard library, and compiler that introduce non-memory-safe code. ### `@unsafe` attribute @@ -144,13 +158,13 @@ extension MyType { Module `A` defines a type, `DataWrapper`, that is `@unsafe`. It can be compiled with or without strict safety checking enabled, and is fine either way. -Module `B` uses the `DataWrapper` type. If compiled without strict safety checking, there will be no diagnostics about memory safety. If compiled with strict safety checking, there will be a diagnostic about `wrapper` using an `@unsafe` type (`DataWrapper`) in its interface. This diagnostic can be ignored. +Module `B` uses the `DataWrapper` type. If compiled without strict safety checking, there will be no diagnostics about memory safety. If compiled with strict safety checking, there will be a diagnostic about `wrapper` using an `@unsafe` type (`DataWrapper`) in its interface. This diagnostic can be ignored, but ideally the `wrapper` property will be marked as `@unsafe` (silencing the warning). -If module `C` enables strict memory safety, the use of `MyType` is considered safe (since it was not marked `@unsafe` and doesn't involve unsafe types in its interface). However, the access to `wrapper` will result in a diagnostic, because the type of `wrapper` involves an `@unsafe` type. +If module `C` enables strict memory safety, the use of `MyType` is considered safe (since it was not marked `@unsafe` and doesn't involve unsafe types in its interface). However, the access to `wrapper` will result in a diagnostic, because the type of `wrapper` involves an `@unsafe` type. This diagnostic will occur whether or not `wrapper` has been explicitly marked `@unsafe`. -### Encapsulating unsafe behavior +### `unsafe` expression -When a declaration is marked `@unsafe`, it is free to use any other unsafe declarations as part of its interface or implementation. In the example from the previously section, both `wrapper` and C's `checksum` can be marked as `@unsafe` to suppress diagnostics by explicitly propagating unsafety to their clients: +When a declaration is marked `@unsafe`, it is free to use any other unsafe types as part of its interface. Any time there is executable code that makes use of unsafe constructs, that code must be within an `unsafe` expression or it will receive a diagnostic about uses of unsafe code. In the example from the previous section, `wrapper` can be marked as `@unsafe` to suppress diagnostics by explicitly propagating unsafety to their clients: ```swift // Module B @@ -159,54 +173,23 @@ import A public struct MyType { @unsafe public var wrapper: DataWrapper } +``` + +However, the use of the `wrapper` property in module `C` will produce a diagnostic unless it is part of an `unsafe` expression, like this: +```swift // Module C import A import B extension MyType { - @unsafe public func checksum() -> Int32 {} - return wrapper.checksum() - } -} -``` - -However, these two APIs differ in the severity of the problem they pose for memory safety: `wrapper` has an unsafe type in its interface, so any use of `wrapper` is fundamentally unsafe. However, `MyType` itself can encapsulate the memory-unsafe behavior within a safe API., The `MyType.checksum` operation is actually safe for clients to use. Marking it as `@unsafe` is therefore undesirable, because it (incorrectly) forces clients to treat this as an unsafe API, causing unnecessary extra work and deluting the value of correctly-identified unsafe APIs. - -One option would be for the author of module `C` to simply ignore the memory-safety warnings produced within its body, which will have the effect of encapsulating the unsafe behavior, but would make it harder to ensure that all unsafe behavior has been accounted for in that module. Another option would be to factor this code into a separate module that doesn't enable strict safety checking, but this is a fairly heavyweight solution. - -Instead, introduce an attribute `@safe(unchecked)` that asserts that the definition is safe despite uses of unsafe constructs. This is a programmer assertion that cannot be validated by the compiler, so it should be used carefully. For the `checksum` operation, it would be used as follows to suppress all memory-safety diagnostics within the body of the function: - -```swift -extension MyType { - @safe(unchecked) public func checksum() -> Int32 {} - return wrapper.checksum() + public func checksum() -> Int32 {} + return unsafe wrapper.checksum() } } ``` -Note that `@safe(unchecked)` only suppresses memory-safety diagnostics in the definition. If we were to try to apply it to the `wrapper` property and enable strict safety checking in module `B`, like this: - -```swift -public struct MyType { - @safe(unchecked) public var wrapper: DataWrapper // warning: use of unsafe type 'DataWrapper' -} -``` - -it would not suppress the diagnostic, because `wrapper` is still fundamentally unsafe. - -The `@safe` attribute allows an optional message, which can be used to explain why the use of unsafe constructs is justified, and is meant to help with an audit trail. Bringing back out early example of producing the sum of values in an array, one can provide an explanation here: - -```swift -extension Array { - @safe(unchecked, message: "use of C API that does its own bounds-safety checking") - func sum() -> Int { - withUnsafeBufferPointerSimplified { buffer in - c_library_sum_function(buffer.baseAddress, buffer.count, 0) - } - } -} -``` +The `unsafe` expression is much like `try` and `await`, in that it acknowledges that unsafe constructs (`wrapper`) are used within the subexpression but otherwise does not change the type. Unlike `try` and `await`, which require the enclosing context to handle throwing or be asynchronous, respectively, the `unsafe` expression does not imply any requirements about the enclosing block: it is purely a marker to indicate the presence of unsafe code, silencing a diagnostic. ### Unsafe language constructs @@ -236,7 +219,7 @@ In the standard library, the following functions and types would be marked `@uns * `withUnsafeCurrentTask` and `UnsafeCurrentTask`: The `UnsafeCurrentTask` type does not provide lifetime safety, and must only be used within the closure passed to `withUnsafeCurrentTask`. * `UnownedSerialExecutor`: This type is intentionally not lifetime safe. It's primary use is the `unownedExecutor` property of the `Actor` protocol, which documents the lifetime assumptions of the `UnownedSerialExecutor` instance it produces. -All of these APIs will be marked `@unsafe`. For all of the types that are `@unsafe`, any API that uses that type in its signature will also be marked `@unsafe`, such as `Array.withUnsafeBufferPointer`. Unless mentioned above, standard library APIs that do not have an unsafe type in their signature, but use unsafe constructs in their implementation, will be marked `@safe(unchecked)` because they provide safe abstractions to client code. +All of these APIs will be marked `@unsafe`. For all of the types that are `@unsafe`, any API that uses that type in its signature will also be marked `@unsafe`, such as `Array.withUnsafeBufferPointer`. Unless mentioned above, standard library APIs that do not have an unsafe type in their signature, but use unsafe constructs in their implementation, will be considered to be safe. ### Unsafe compiler flags @@ -325,11 +308,23 @@ static func strictMemorySafety( ## Source compatibility -The introduce of this strict safety checking mode has no impact on source compatibility for any module that does not enable it. When enabling strict safety checking, source compatibility impact is limited to the introduction of warnings that will not break source compatibility (and can be treated as warnings even under `-warnings-as-errors` mode). The source compatibility and interoperability story is covered in detail in prior sections. +The `unsafe` keyword in this proposal will be introduced as a contextual keyword following the precedent set by `await`'s' introduction in [SE-0296](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0296-async-await.md) and `consume`'s introduction in [SE-0366](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0366-move-function.md). This allows `unsafe` to continue to be used as an identifier, albeit with a small potential to break existing source that uses `unsafe` as a function that is then called with a trailing closure, like this: + +```swift +func unsafe(_ body: () -> Void) { } + +unsafe { + // currently calls 'unsafe(_:)', but will become and unsafe expression +} +``` + +As with those proposals, the impact of this source break in expected to be small enough that it is acceptable. If not, the parsing of the `unsafe` expression can be limited to code that has enabled strict safety checking. + +Other than the source break above, the introduction of this strict safety checking mode has no impact on source compatibility for any module that does not enable it. When enabling strict safety checking, source compatibility impact is limited to the introduction of warnings that will not break source compatibility (and can be treated as warnings even under `-warnings-as-errors` mode using the aforementioned diagnostic flags). The interoperability story is covered in detail in prior sections. ## ABI compatibility -The attributes and strict memory-safety checking model proposed here have no impact on ABI. +The attributes, `unsafe` expression, and strict memory-safety checking model proposed here have no impact on ABI. ## Future Directions @@ -370,7 +365,7 @@ The `bytes` property is necessarily unsafe. Far better would be to produce a `Ra ```swift extension DataPacket { - @safe(unchecked) public var byteSpan: RawSpan + public var byteSpan: RawSpan } ``` @@ -380,7 +375,7 @@ Swift does allow type-based overloading, including on the type of properties, so ```swift extension DataPacket { - @safe(unchecked) public var bytes: RawSpan + public var bytes: RawSpan } ``` @@ -412,9 +407,7 @@ There are downsides to this approach. It partially undermines the source compati ### `unsafe` blocks -The attribute `@safe(unchecked)` indicates that a definition is safe despite the use of unsafe constructs in its body. The attribute has no effect on the client, and could effectively be eliminated from the public interface (e.g., documentation, Swift textual interfaces, etc.) without changing how clients behave. - -There is an alternative formulation for acknowledging unsafe code that is used in some peer languages like C# and Rust: an `unsafe` block, which is a statement that suppresses diagnostics about uses of unsafe code within it. For example, the `sum` example could be written as follows: +The `unsafe` expression proposed here covers unsafe constructs within a single expression. For unsafe-heavy code, this can introduce a large number of `unsafe` keywords. There is an alternative formulation for acknowledging unsafe code that is used in some peer languages like C# and Rust: an `unsafe` block, which is a statement that suppresses diagnostics about uses of unsafe code within it. For example, the `sum` example could be written as follows: ```swift extension Array { @@ -430,55 +423,7 @@ extension Array { For Swift, an `unsafe` block would be a statement that can also be used as an expression when its body is an expression, much like `if` or `switch` expressions following [SE-0380](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0380-if-switch-expressions.md). -`unsafe` blocks have some advantages over the proposed `@safe(unchecked)` attribute, including: - -* They can be limited to smaller amounts of code than a whole function, which can make auditing for the unsafe parts of a function easier. -* The `unsafe` blocks are more clearly part of the implementation of a function, not the interface. - -This proposal suggests the `@safe(unchecked)` attribute instead of `unsafe` blocks for a few reasons, but the choice is effectively arbitrary: either syntax will work with the rest of this proposal. Some of the reasons for preferring `@safe(unchecked)` include: - -* It's easier to stage in this change without affecting the surrounding code. One can add - ```swift - if #hasAttribute(safe) - @safe(unchecked) - #endif - ``` - - to `sum` without changing any other code, which is easy to review and will still work with compilers that predate the introduction of this feature. - -* The `unsafe` blocks end up adding another level of nesting, which also means that introducing them to silence warnings causes unnecessarily large amount of code change when adopting the feature. - -* `@safe(unchecked)` can be used in places that aren't executable code, such as protocol conformances. - -* `@safe(unchecked)` can be introduced with no source-compatibility impact, whereas the `unsafe` block will break code that calls a function named `unsafe` given a closure. - -### `unsafe` as an effect - -As another alternative to `@safe(unchecked)` and `unsafe` blocks, we could model accesses to unsafe enties as an effect like `async` and `throws`. In such cases, referring to any declaration that is explicitly `@unsafe` or whose type involves an `@unsafe` type would require `unsafe` on the enclosing expression just like using an `async` function requires `await` and a `throws` function requires `try`. This would require more annotations than the `unsafe` block, because individual expressions would need to be marked. For our example with producing the sum of integer arrays, it would look like this: - -```swift -extension Array { - func sum() -> Int { - unsafe withUnsafeBufferPointerSimplified { buffer in - unsafe c_library_sum_function(buffer.baseAddress, buffer.count, 0) - } - } -} -``` - -Unlike with the `unsafe` block, one `unsafe` annotation doesn't cover both expressions: the first `unsafe` is needed because `withUnsafeBufferPointerSimplified` involves unsafe types, and the second because `c_library_sum_function`, `buffer.baseAddress`, and `buffer.count` all involve unsafe types. - -There are some benefits to this approach: - -* It makes it easier to audit those places where unsafe code is used, because `unsafe` only applies to individual expressions, not large chunks of code. -* It builds on the existing behavior of `try` and `await` in a way that should be understandable to most Swift programmers. - -However, both of these have corresponding downsides: - -* The `unsafe` effect could make code very noisy when using unsafe types, and starts to feel quite redundant given that most unsafe APIs already intentionally have "unsafe" somewhere in the name: `unsafe withUnsafeXYZ` sounds like it doubly-punishes the use of unsafe code. While this is perhaps a minor annoyance, it could mean that some unsafe APIs designed with strict safety checking in mind would omit the explicit "unsafe" from the name. This change would make code *not* using strict safety checking less safe, because we'd no longer get the benefits of the longstanding conventions built around calling unsafe functions "unsafe". -* Both `throws` and `async` are part of the type system, whereas `@unsafe` is not, so `unsafe` is effect-like but not *exactly* an effect. There are practical differences here, such as the fact that naming a throwing function but not calling it (e.g., `let g = f`) doesn't require `try` (becaue `throws` is captured in the type of `g`) but would require `unsafe` if `f` is unsafe. Having `unsafe` be effect-like muddles the message about how effects work in Swift. - -Additionally, introducing `unsafe` as an effect has the same source-compatibility impact as the `unsafe` block described in the previous section: code like `unsafe { ... }` can exist today to call a function named `unsafe` with closure, and would get a different meaning when `unsafe` is parsed as an effect. +`unsafe` blocks are more coarse-grained than the proposed `unsafe` expressions, which represents a trade-off: `unsafe` blocks will be less noisy for unsafe-heavy code, because one `unsafe { ... }` can cover a lot of code. On the other hand, doing so hides which code within the block is actually unsafe, making it harder to audit the unsafe parts. In languages that have `unsafe` blocks, it's considered best practice to make the `unsafe` blocks as narrow as possible. The proposed `unsafe` expressions enforce that best practice at the language level. ### Strictly-safe-by-default From 5606d6cdde86f40add04471ebca50b77b8c0a968 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 10 Jan 2025 11:43:42 -0800 Subject: [PATCH 3979/4563] Write up why I don't want to add @unsafe(message: "blah") --- proposals/nnnn-strict-memory-safety.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index fc9da1aa93..cdd6fa1d35 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -433,6 +433,10 @@ This proposal introduced strict safety checking as an opt in mode and not an [*u * Interoperability with the C family of languages is an important feature for Swift. Most C(++) APIs are unlikely to ever adopt the safety-related attributes described above, which means that enabling strict safety checking by default would undermine the usability of C(++) interoperability. * Swift's current (non-strict) memory safety by default is likely to be good enough for the vast majority of users of Swift, so the benefit of enabling stricter checking by default is unlikely to be worth the disruption it would cause. +### Optional `message` for the `@unsafe` attribute + +We could introduce an optional `message` argument to the `@unsafe` attribute, which would allow programmers to indicate *why* the use of a particular declaration is unsafe and, more importantly, how to safely write code that uses it. However, this argument isn't strictly necessary: a comment could provide the same information, and there is established tooling to expose comments to programmers that wouldn't be present for this attribute's message, so we have omitted this feature. + ## Acknowledgments This proposal has been greatly improved by the feedback from Félix Cloutier, Gábor Horváth, Frederick Kellison-Linn, Karl Wagner, and Xiaodi Wu. From 737a2bbc0797c3ed1742af7415a2a834e43dd41b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 10 Jan 2025 11:56:41 -0800 Subject: [PATCH 3980/4563] Document more compiler flags that break memory safety --- proposals/nnnn-strict-memory-safety.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index cdd6fa1d35..941a609881 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -223,9 +223,12 @@ All of these APIs will be marked `@unsafe`. For all of the types that are `@unsa ### Unsafe compiler flags -The `-Ounchecked` compiler flag disables some checking in the standard library, including (for example) bounds checking on array accesses. It is generally discouraged in all Swift code, but is particularly problematic in conjunction with strict memory safety because it removes the checking that makes certain standard library APIs safe. Therefore, the compiler will produce a diagnostic when the two features are combined. +There are a number of compiler flags that intentionally disable some safety-related checking. For each of these flags, the compiler will produce a diagnostic if they are used with strict memory safety: -The `-disable-access-control` flag ignores access specifiers entirely, allowing one to (for example) access a `private` declaration from outside its defining file. This could allow one to break invariants of a type that can lead to memory-safety issues, such as breaking the invariant of `Range` that the lower bound not exceed the upper bound. The compiler will produce a diagnostic when the two features are combined. +* `-Ounchecked`, which disables some checking in the standard library, including (for example) bounds checking on array accesses. +* `-enforce-exclusivity=unchecked` and `-enforce-exclusivity=none`, which disables exclusivity checking that is needed for memory safety. +* `-strict-concurrency=` for anything other than "complete", because the memory safety model requires strict concurrency to eliminate thread safety issues. +* `-disable-access-control`, which allows one to break invariants of a type that can lead to memory-safety issues, such as breaking the invariant of `Range` that the lower bound not exceed the upper bound. ### Unsafe overrides From 6cb9de24f09966a1e11d88ce2a7a843e4c912bf4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 10 Jan 2025 12:27:20 -0800 Subject: [PATCH 3981/4563] Document why we aren't proposing `@safe(unchecked)` anymore Thanks to Geoff for some great motivation here --- proposals/nnnn-strict-memory-safety.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 941a609881..8d7e683a74 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -408,6 +408,25 @@ There are downsides to this approach. It partially undermines the source compati ## Alternatives considered +### `@safe(unchecked)` attribute to allow unsafe code + +Early iterations of this proposal introduced a `@safe(unchecked)` attribute as an alternative to `unsafe` expressions. The `@safe(unchecked)` attribute would be placed on a function to suppress diagnostics about use of unsafe constructs within its definition. For our `sum` example, this means one would write: + +```swift +extension Array { + @safe(unchecked) + func sum() -> Int { + withUnsafeBufferPointerSimplified { buffer in + c_library_sum_function(buffer.baseAddress, buffer.count, 0) + } + } +} +``` + +This approach means fewer annotations for unsafe code, because one `@safe(unchecked)` covers the entire body of the function. That is a trade-off: it's less verbose, but it makes it much harder to identify exactly what parts of the implementation are actually unsafe. To reduce the amount of code covered by a `@safe(unchecked)` down to just the actual unsafe code, one would have to factor out the unsafe code into many small `@safe(unchecked)` functions, which could end up being quite verbose and make it harder to reason about the code itself. + +This approach also implies that making a function `@unsafe` will suppress any diagnostics about uses of unsafe constructs within the body of that function. Rust's `unsafe` functions have this behavior, and it is the source of [some concern in that community](https://rust-lang.github.io/rfcs/2585-unsafe-block-in-unsafe-fn.html). Part of the issue there involves wanting to tightly scope the uses of unsafe code, but that discussion brings up another reason: it means that `unsafe` on a function actually has a dual role: it both says that the function is unsafe to use *and also* says that the function can freely use unsafe constructs in its definition. + ### `unsafe` blocks The `unsafe` expression proposed here covers unsafe constructs within a single expression. For unsafe-heavy code, this can introduce a large number of `unsafe` keywords. There is an alternative formulation for acknowledging unsafe code that is used in some peer languages like C# and Rust: an `unsafe` block, which is a statement that suppresses diagnostics about uses of unsafe code within it. For example, the `sum` example could be written as follows: From 3f6288cefa53355464752585875ad7e8adb60a64 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Mon, 13 Jan 2025 08:21:10 -0500 Subject: [PATCH 3982/4563] SE-0455: Link to active review --- proposals/0455-swiftpm-testable-build-setting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0455-swiftpm-testable-build-setting.md b/proposals/0455-swiftpm-testable-build-setting.md index 602b4c639b..1310baec79 100644 --- a/proposals/0455-swiftpm-testable-build-setting.md +++ b/proposals/0455-swiftpm-testable-build-setting.md @@ -3,9 +3,9 @@ * Proposal: [SE-0455](0455-swiftpm-testable-build-setting.md) * Authors: [Jake Petroules](https://github.com/jakepetroules) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Awaiting review** +* Status: **Active review (January 9–23, 2025)** * Implementation: [swiftlang/swift-package-manager#8004](https://github.com/swiftlang/swift-package-manager/pull/8004) -* Review: ([pitch](https://forums.swift.org/t/pitch-swiftpm-testable-build-setting/75084)) +* Review: ([pitch](https://forums.swift.org/t/pitch-swiftpm-testable-build-setting/75084)) ([review](https://forums.swift.org/t/se-0455-swiftpm-testable-build-setting/77100)) ## Introduction From 1f5a3ece1c3b6cba9a9fc10d3a0681f5f40fafdd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 13 Jan 2025 13:09:37 -0800 Subject: [PATCH 3983/4563] Add a section on `@unsafe` implying `unsafe` throughout a function body Plus some minor typo corrections and clarifications. --- proposals/nnnn-strict-memory-safety.md | 52 +++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 8d7e683a74..4143758301 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -34,7 +34,7 @@ While there are a number of potential definitions for memory safety, the one pro Since its inception, Swift has provided memory safety for the first four dimensions. Lifetime safety is provided for reference types by automatic reference counting and for value types via [memory exclusivity](https://www.swift.org/blog/swift-5-exclusivity/); bounds safety is provided by bounds-checking on `Array` and other collections; type safety is provided by safe features for casting (`as?` , `is` ) and `enum` s; and initialization safety is provided by “definite initialization”, which doesn’t allow a variable to be accessed until it has been defined. Swift 6’s strict concurrency checking extends Swift’s memory safety guarantees to the last dimension. -Providing memory safety does not imply the absence of run-time failures. Good language design often means defining away runtime failures in the type system. However, memory safely requires only that an error in the program cannot be escalated into a violation of one of the safety properties. For example, having reference types be non-nullable by default defines away most problems with NULL pointers. With explicit optional types, the force-unwrap operator (postfix `!` ) meets the definition of memory safety by trapping at runtime if the unwrapped optional is `nil` . The standard library also provides the [`unsafelyUnwrapped` property](https://developer.apple.com/documentation/swift/optional/unsafelyunwrapped) that does not check for `nil` in release builds: this does not meet the definition of memory safety because it admits violations of initialization and lifetime safety that could be exploited. +Providing memory safety does not imply the absence of run-time failures. Good language design often means defining away runtime failures in the type system. However, memory safety requires only that an error in the program cannot be escalated into a violation of one of the safety properties. For example, having reference types be non-nullable by default defines away most problems with NULL pointers. With explicit optional types, the force-unwrap operator (postfix `!` ) meets the definition of memory safety by trapping at runtime if the unwrapped optional is `nil` . The standard library also provides the [`unsafelyUnwrapped` property](https://developer.apple.com/documentation/swift/optional/unsafelyunwrapped) that does not check for `nil` in release builds: this does not meet the definition of memory safety because it admits violations of initialization and lifetime safety that could be exploited. ## Proposed solution @@ -106,7 +106,7 @@ extension Array { The `unsafe` keyword here indicates the presence of unsafe code within that expression. As with `try` and `await`, it can cover multiple sources of unsafety within that expression: the call to `c_library_sum_function` is unsafe, as is the use of `buffer` and `buffer.baseAddress`, yet they are all covered by one `unsafe`. -Note that we do *not* require that the `sum` function be marked `@unsafe` just because it has unsafe code in it. The programmer may choose to indicate that `sum` is unsafe, but the assumption is that unsafe behavior is properly encapsulated when using `unsafe`. +Note that we do *not* require that the `sum` function be marked `@unsafe` just because it has unsafe code in it. The programmer may choose to indicate that `sum` is unsafe, but the assumption is that unsafe behavior is properly encapsulated when using `unsafe`. Additionally, the `@unsafe` attribute is available for all Swift code, even if it doesn't itself enable the strict safety checking described in this proposal. ### Incremental adoption @@ -197,12 +197,12 @@ The following language constructs are always considered to be unsafe: * `unowned(unsafe)`: Used to store a reference without maintaining its reference count. The safe counterpart, `unowned`, uses dynamic checking to ensure that the reference isn't accessed after the corresponding object has been released. The `unsafe` variant disables that dynamic checking. Uses of `unowned(unsafe)` entities are not memory-safe. * `unsafeAddressor`, `unsafeMutableAddressor`: These accessors vend an unsafe pointer, and are therefore unsafe to declare. Other accessors (e.g., `get` and `set`) can provide safe alternatives. -* `@exclusivity(unchecked)`: Used to remove dynamic exclusivity checks from a particular variable, which can mean that dynamic exclusivity violations go undetected at run time, causing a memory safety violation. +* `@exclusivity(unchecked)`: Used to remove dynamic exclusivity checks from a particular variable, which can mean that dynamic exclusivity violations go undetected at run time, causing a memory safety violation. Uses of `@exclusivity(unchecked)` entities are not memory-safe. The following language constructs are considered to be unsafe when strict concurrency checking is enabled (i.e., in the Swift 6 language mode): * `nonisolated(unsafe)`: Allows a property to be accessed from concurrent code without ensuring that such accesses are done so safely. Uses of `nonisolated(unsafe)` entities are not memory-safe. -* `@preconcurrency` imports: Suppresses diagnostics related to data race safety when they relate to specific imported modules, which can introduce thread safety issues. +* `@preconcurrency` imports: Suppresses diagnostics related to data race safety when they relate to specific imported modules, which can introduce thread safety issues. The `@preconcurrency` import will need to be annotated with `@unsafe` in the strict dsafety mode. ### Unsafe standard library APIs @@ -317,7 +317,7 @@ The `unsafe` keyword in this proposal will be introduced as a contextual keyword func unsafe(_ body: () -> Void) { } unsafe { - // currently calls 'unsafe(_:)', but will become and unsafe expression + // currently calls 'unsafe(_:)', but will become an unsafe expression } ``` @@ -408,6 +408,46 @@ There are downsides to this approach. It partially undermines the source compati ## Alternatives considered +### `@unsafe` implying `unsafe` throughout a function body + +A function marked `@unsafe` is unsafe to use, so any clients that have enabled strict safety checking will need to put uses of the function into an `unsafe` expression. The implementation of that function is likely to use unsafe code (possibly a lot of it), which could result in a large number of annotations: + +```swift +extension UnsafeMutableBufferPointer { + @unsafe public func swapAt(_ i: Int, _ j: Int) { + guard i != j else { return } + precondition(i >= 0 && j >= 0) + precondition(unsafe i < endIndex && j < endIndex) + @unsafe let pi = unsafe (_position! + i) + @unsafe let pj = unsafe (_position! + j) + @unsafe let tmp = unsafe pi.move() + unsafe pi.moveInitialize(from: pj, count: 1) + unsafe pj.initialize(to: tmp) + } +} +``` + +Now, it is very likely that an `@unsafe` function is going to make use of other unsafe constructs, so we could choose to make `@unsafe` on a function acknowledge all uses of unsafe code within its definition. For example, this would mean that marking `swapAt` with `@unsafe` means that one need not have any `unsafe` expressions in its body: + +```swift +extension UnsafeMutableBufferPointer { + @unsafe public func swapAt(_ i: Int, _ j: Int) { + guard i != j else { return } + precondition(i >= 0 && j >= 0) + precondition(i < endIndex && j < endIndex) + let pi = (_position! + i) + let pj = (_position! + j) + let tmp = pi.move() + pi.moveInitialize(from: pj, count: 1) + pj.initialize(to: tmp) + } +} +``` + +This approach reduces the annotation burden in unsafe code, but makes it much harder to tell exactly what aspects of the implementation are unsafe. Indeed, even unsafe functions should still strive to minimize the use of unsafe constructs, and benefit from having the actual unsafe behavior marked in the source. It also conflates the notion of "exposes an unsafe interface" from "has an unsafe implementation". + +Rust's `unsafe` functions have this behavior, where an `unsafe fn` in Rust implies an `unsafe { ... }` block around the entire function body. [Rust RFC #2585](https://rust-lang.github.io/rfcs/2585-unsafe-block-in-unsafe-fn.html) argues for Rust to remove this behavior; the motivation there generally applies to Swift as well. + ### `@safe(unchecked)` attribute to allow unsafe code Early iterations of this proposal introduced a `@safe(unchecked)` attribute as an alternative to `unsafe` expressions. The `@safe(unchecked)` attribute would be placed on a function to suppress diagnostics about use of unsafe constructs within its definition. For our `sum` example, this means one would write: @@ -461,4 +501,4 @@ We could introduce an optional `message` argument to the `@unsafe` attribute, wh ## Acknowledgments -This proposal has been greatly improved by the feedback from Félix Cloutier, Gábor Horváth, Frederick Kellison-Linn, Karl Wagner, and Xiaodi Wu. +This proposal has been greatly improved by the feedback from Félix Cloutier, Geoff Garen, Gábor Horváth, Frederick Kellison-Linn, Karl Wagner, and Xiaodi Wu. From 6a42b44cf3dc92616a795c108970fa07d5bef376 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 13 Jan 2025 13:22:10 -0800 Subject: [PATCH 3984/4563] Add a section about making encapsulation of unsafety more explicit --- proposals/nnnn-strict-memory-safety.md | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 4143758301..8b68d81a87 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -448,6 +448,38 @@ This approach reduces the annotation burden in unsafe code, but makes it much ha Rust's `unsafe` functions have this behavior, where an `unsafe fn` in Rust implies an `unsafe { ... }` block around the entire function body. [Rust RFC #2585](https://rust-lang.github.io/rfcs/2585-unsafe-block-in-unsafe-fn.html) argues for Rust to remove this behavior; the motivation there generally applies to Swift as well. +### Making "encapsulation" of unsafe behavior explicit + +In the proposed design, a function with no unsafe types in its signature is considered safe unless the programmer explicitly marked it `@unsafe`. The implementation may contain any amount of unsafe code, so long as it is covered by an `unsafe` expression: + +```swift +extension Array { + // this function is considered safe + func sum() -> Int { + unsafe withUnsafeBufferPointerSimplified { buffer in + unsafe c_library_sum_function(buffer.baseAddress, buffer.count, 0) + } + } +} +``` + +This differs somewhat from the way in which throwing and asynchronous functions work. A function that has a `try` or `await` in the body needs to be `throws` or `async`, respectively. Essentially, the effect from the body has to also be reflected in the signature. With unsafe code, this could mean that having `unsafe` expressions in the function body requires you to either make the function `@unsafe` or use some other suppression mechanism to acknowledge that you are using unsafe constructs to provide a safe interface. + +There are several options for such a suppression mechanism. An attribute form, `@safe(unchecked)`, is described below as an alternative to the `unsafe` expression. Another approach would be to provide an `unsafe!` form the `unsafe` expression, which (like `try!`) acknowledges the effect but doesn't propagate that effect out to the function. For the `sum` function, it would be used as follows: + +```swift +extension Array { + // this function is considered safe + func sum() -> Int { + unsafe! withUnsafeBufferPointerSimplified { buffer in + unsafe! c_library_sum_function(buffer.baseAddress, buffer.count, 0) + } + } +} +``` + +This proposal chooses not to go down this path, because having a function signature involving no unsafe types is already a strong indication that the function is providing a safe interface, and there is little to be gained from requiring additional ceremony (whether an attribute like `@safe(unchecked)` or the `unsafe!` form described above). + ### `@safe(unchecked)` attribute to allow unsafe code Early iterations of this proposal introduced a `@safe(unchecked)` attribute as an alternative to `unsafe` expressions. The `@safe(unchecked)` attribute would be placed on a function to suppress diagnostics about use of unsafe constructs within its definition. For our `sum` example, this means one would write: From ccbcb7186e4eeb4f07fd07778f097cf9f415e290 Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Tue, 14 Jan 2025 15:47:50 +0100 Subject: [PATCH 3985/4563] [SE-0392] Editorial fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A number of typo fixes and other small editorial fixes. Specifically: - I changed "Swift concurrency" to "Swift Concurrency" in a few places because the document capitalizes the term in most places. - I removed the top level of the table of contents because I don’t think it’s necessary to include a link to the full document in the TOC. --- proposals/0392-custom-actor-executors.md | 61 ++++++++++++------------ 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/proposals/0392-custom-actor-executors.md b/proposals/0392-custom-actor-executors.md index 97baac5ace..13497cff8d 100644 --- a/proposals/0392-custom-actor-executors.md +++ b/proposals/0392-custom-actor-executors.md @@ -16,28 +16,27 @@ ## Table of Contents -- [Custom Actor Executors](#custom-actor-executors) - * [Introduction](#introduction) - * [Motivation](#motivation) - * [Proposed solution](#proposed-solution) - * [Detailed design](#detailed-design) - + [A low-level design](#a-low-level-design) - + [Executors](#executors) - + [Serial Executors](#serial-executors) - + [ExecutorJobs](#executorjobs) - + [Actors with custom SerialExecutors](#actors-with-custom-serialexecutors) - + [Asserting on executors](#asserting-on-executors) - + [Assuming actor executors](#asserting-actor-executors) - + [Default Swift Runtime Executors](#default-swift-runtime-executors) - * [Source compatibility](#source-compatibility) - * [Effect on ABI stability](#effect-on-abi-stability) - * [Effect on API resilience](#effect-on-api-resilience) - * [Alternatives considered](#alternatives-considered) - * [Future Directions](#future-directions) - + [Overriding the MainActor Executor](#overriding-the-mainactor-executor) - + [Executor Switching Optimizations](#executor-switching) - + [Specifying Task executors](#specifying-task-executors) - + [DelegateActor property](#delegateactor-property) +* [Introduction](#introduction) +* [Motivation](#motivation) +* [Proposed solution](#proposed-solution) +* [Detailed design](#detailed-design) + + [A low-level design](#a-low-level-design) + + [Executors](#executors) + + [Serial Executors](#serial-executors) + + [ExecutorJobs](#executorjobs) + + [Actors with custom SerialExecutors](#actors-with-custom-serialexecutors) + + [Asserting on executors](#asserting-on-executors) + + [Assuming actor executors](#asserting-actor-executors) + + [Default Swift Runtime Executors](#default-swift-runtime-executors) +* [Source compatibility](#source-compatibility) +* [Effect on ABI stability](#effect-on-abi-stability) +* [Effect on API resilience](#effect-on-api-resilience) +* [Alternatives considered](#alternatives-considered) +* [Future Directions](#future-directions) + + [Overriding the MainActor Executor](#overriding-the-mainactor-executor) + + [Executor Switching Optimizations](#executor-switching) + + [Specifying Task executors](#specifying-task-executors) + + [DelegateActor property](#delegateactor-property) ## Introduction @@ -252,9 +251,9 @@ public struct UnownedSerialExecutor: Sendable { ### ExecutorJobs -A `ExecutorJob` is a representation of a chunk of of work that an executor should execute. For example, a `Task` effectively consists of a series of jobs that are enqueued onto executors, in order to run them. The name "job" was selected because we do not want to constrain this API to just "partial tasks", or tie them too closely to tasks, even though the most common type of job created by Swift concurrency are "partial tasks". +An `ExecutorJob` is a representation of a chunk of of work that an executor should execute. For example, a `Task` effectively consists of a series of jobs that are enqueued onto executors, in order to run them. The name "job" was selected because we do not want to constrain this API to just "partial tasks", or tie them too closely to tasks, even though the most common type of job created by Swift concurrency are "partial tasks". -Whenever the Swift concurrency needs to execute some piece of work, it enqueues an `UnownedExecutorJob`s on a specific executor the job should be executed on. The `UnownedExecutorJob` type is an opaque wrapper around Swift's low-level representation of such job. It cannot be meaningfully inspected, copied and must never be executed more than once. +Whenever the Swift Concurrency runtime needs to execute some piece of work, it enqueues an `UnownedExecutorJob`s on a specific executor the job should be executed on. The `UnownedExecutorJob` type is an opaque wrapper around Swift's low-level representation of such job. It cannot be meaningfully inspected, copied and must never be executed more than once. ```swift @noncopyable @@ -314,7 +313,7 @@ public struct UnownedExecutorJob: Sendable, CustomStringConvertible { A job's description includes its job or task ID, that can be used to correlate it with task dumps as well as task lists in Instruments and other debugging tools (e.g. `swift-inspect`'s ). A task ID is an unique number assigned to a task, and can be useful when debugging scheduling issues, this is the same ID that is currently exposed in tools like Instruments when inspecting tasks, allowing to correlate debug logs with observations from profiling tools. -Eventually, an executor will want to actually run a job. It may do so right away when it is enqueued, or on some different thread, this is entirely left up to the executor to decide. Running a job is done by calling the `runSynchronously` on a `ExecutorJob` which consumes it. The same method is provided on the `UnownedExecutorJob` type, however that API is not as safe, since it cannot consume the job, and is open to running the same job multiple times accidentally which is undefined behavior. Generally, we urge developers to stick to using `ExecutorJob` APIs whenever possible, and only move to the unowned API if the noncopyable `ExecutorJob`s restrictions prove too strong to do the necessary operations on it. +Eventually, an executor will want to actually run a job. It may do so right away when it is enqueued, or on some different thread, this is entirely left up to the executor to decide. Running a job is done by calling the `runSynchronously` on a `ExecutorJob` which consumes it. The same method is provided on the `UnownedExecutorJob` type, however that API is not as safe, since it cannot consume the job, and is open to running the same job multiple times accidentally, which is undefined behavior. Generally, we urge developers to stick to using `ExecutorJob` APIs whenever possible, and only move to the unowned API if the noncopyable `ExecutorJob`s restrictions prove too strong to do the necessary operations on it. ```swift extension ExecutorJob { @@ -342,7 +341,7 @@ All actors implicitly conform to the `Actor` (or `DistributedActor`) protocols, An actor's executor must conform to the `SerialExecutor` protocol, which refines the Executor protocol, and provides enough guarantees to implement the actor's mutual exclusion guarantees. In the future, `SerialExecutors` may also be extended to support "switching", which is a technique to avoid thread-switching in calls between actors whose executors are compatible to "lending" each other the currently running thread. This proposal does not cover switching semantics. -Actors select which serial executor they should use to run tasks is expressed by the `unownedExecutor` protocol requirement on the `Actor` and `DistributedActor` protocols: +Actors select which serial executor they should use to run jobs by implementing the `unownedExecutor` protocol requirement on the `Actor` and `DistributedActor` protocols: ```swift public protocol Actor: AnyActor { @@ -407,7 +406,7 @@ public protocol DistributedActor: AnyActor { > Note: It is not possible to express this protocol requirement on `AnyActor` directly because `AnyActor` is a "marker protocol" which are not present at runtime, and cannot have protocol requirements. -The compiler synthesizes an implementation for this requirement for every `(distributed) actor` declaration, unless an explicit implementation is provided. The default implementation synthesized by the compiler uses the default `SerialExecutor`, that uses tha appropriate mechanism for the platform (e.g. Dispatch). Actors using this default synthesized implementation are referred to as "Default Actors", i.e. actors using the default serial executor implementation. +The compiler synthesizes an implementation for this requirement for every `(distributed) actor` declaration, unless an explicit implementation is provided. The default implementation synthesized by the compiler uses the default `SerialExecutor`, which uses the appropriate mechanism for the platform (e.g. Dispatch). Actors using this default synthesized implementation are referred to as "Default Actors", i.e. actors using the default serial executor implementation. Developers can customize the executor used by an actor on a declaration-by-declaration basis, by implementing this protocol requirement in an actor. For example, thanks to the `sharedUnownedExecutor` static property on `MainActor` it is possible to declare other actors which are also guaranteed to use the same serial executor (i.e. "the main thread"). @@ -482,10 +481,10 @@ A library could also provide a default implementation of such executor as well. A common pattern in event-loop heavy code–not yet using Swift Concurrency–is to ensure/verify that a synchronous piece of code is executed on the exected event-loop. Since one of the goals of making executors customizable is to allow such libraries to adopt Swift Concurrency by making such event-loops conform to `SerialExecutor`, it is useful to allow the checking if code is indeed executing on the appropriate executor, for the library to gain confidence while it is moving towards fully embracing actors and Swift concurrency. -For example, Swift NIO intentionally avoids synchronization checks in some synchronous methods, in order to avoid the overhead of doing so, however in DEBUG mode it performs assertions that given code is running on the expected event-loop: +For example, SwiftNIO intentionally avoids synchronization checks in some synchronous methods, in order to avoid the overhead of doing so, however in DEBUG mode it performs assertions that given code is running on the expected event-loop: ```swift -// Swift NIO +// SwiftNIO private var _channel: Channel internal var channel: Channel { self.eventLoop.assertInEventLoop() @@ -572,7 +571,7 @@ extension DistributedActor { } ``` -The versions of the APIs offered on `Actor` and `DistributedActor` offer better diagnostics than would be possible to implement using a plain `precondition()` implemented by developers using some `precondition(isOnExpectedExecutor"(someExecutor))` because they offer a description of the actually active executor when mismatched: +The versions of the APIs offered on `Actor` and `DistributedActor` offer better diagnostics than would be possible to implement using a plain `precondition()` implemented by developers using some `precondition(isOnExpectedExecutor(someExecutor))` because they offer a description of the actually active executor when mismatched: ````swift MainActor.preconditionIsolated() @@ -868,7 +867,7 @@ These checks are likely *not* enough to to completely optimize task switching, a ### Default Swift Runtime Executors -Swift concurrency provides a number of default executors already, such as: +Swift Concurrency provides a number of default executors already, such as: - the main actor executor, which services any code annotated using @MainActor, and - the default global concurrent executor, which all (default) actors target by their own per-actor instantiated serial executor instances. From b3fc0c33bf337af6e38ddf5dfe2049ebafab77d0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 14 Jan 2025 11:17:47 -0800 Subject: [PATCH 3986/4563] Clarify places where `@unsafe` isn't required on declarations --- proposals/nnnn-strict-memory-safety.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 8b68d81a87..f195aa1c52 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -162,6 +162,25 @@ Module `B` uses the `DataWrapper` type. If compiled without strict safety checki If module `C` enables strict memory safety, the use of `MyType` is considered safe (since it was not marked `@unsafe` and doesn't involve unsafe types in its interface). However, the access to `wrapper` will result in a diagnostic, because the type of `wrapper` involves an `@unsafe` type. This diagnostic will occur whether or not `wrapper` has been explicitly marked `@unsafe`. +There are a few exemptions to the rule that any unsafe constructs within the signature require the declaration to be `@unsafe`: + +* Local variables involving unsafe types do not need to be marked with `@unsafe`. For example, the local variable `base` will have unsafe type `UnsafePointer?`, but does not require `@unsafe` because every *use* of this local variable will need to be marked using the `unsafe` expression described in the next section. + + ```swift + func sum(array: [Int]) -> Int { + array.withUnsafeBufferPointer { buffer in + let base = /*unsafe*/ buffer.baseAddress + // ... + } + } + ``` + +* Default arguments of functions are part of the implementation of a function, not its signature. For example, the following function does not have any unsafe types in its signature, so it does not require `@unsafe`, even though the default argument for `value` involves unsafe code. That unsafe code is effectively part of the body of the function, so it follows the rules for `unsafe` expressions. + + ```swift + func hasDefault(value: Int = /*unsafe*/ getIntegerUnsafely()) { ... } + ``` + ### `unsafe` expression When a declaration is marked `@unsafe`, it is free to use any other unsafe types as part of its interface. Any time there is executable code that makes use of unsafe constructs, that code must be within an `unsafe` expression or it will receive a diagnostic about uses of unsafe code. In the example from the previous section, `wrapper` can be marked as `@unsafe` to suppress diagnostics by explicitly propagating unsafety to their clients: From 9e763e75350dcf8df158d0f559136d9e2d1d4d7e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 14 Jan 2025 11:37:12 -0800 Subject: [PATCH 3987/4563] Collecting improvements thanks to John's review --- proposals/nnnn-strict-memory-safety.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index f195aa1c52..e5aa608e08 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -10,11 +10,11 @@ ## Introduction -[Memory safety](https://en.wikipedia.org/wiki/Memory_safety) is a popular topic in programming languages nowadays. Essentially, memory safety is a property that prevents programmer errors from manifesting as [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior) at runtime. Undefined behavior effectively breaks the semantic model of a language, with unpredictable results including crashes, data corruption, and otherwise-impossible program states, which directly lead to hard-to-reproduce problems as well as security problems. +[Memory safety](https://en.wikipedia.org/wiki/Memory_safety) is a property of programming languages and their implementations that prevents programmer errors from manifesting as [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior) at runtime. Undefined behavior effectively breaks the semantic model of a language, with unpredictable results including crashes, data corruption, and otherwise-impossible program states. Such behavior can lead to hard-to-reproduce bugs as well as introduce security vulnerabilities. Various studies have shown that memory safety problems in C and C++ account for around [70% of security vulnerabilities in software](https://www.cisa.gov/sites/default/files/2023-12/The-Case-for-Memory-Safe-Roadmaps-508c.pdf). -Swift provides memory safety with a combination of language affordances and runtime checking. However, Swift also deliberately includes some unsafe constructs, such as the `Unsafe` pointer types in the standard library, language features like `nonisolated(unsafe)`, and interoperability with unsafe languages like C. For most Swift developers, this is a pragmatic solution that appropriate level of memory safety while not getting in the way. +Swift provides memory safety with a combination of language affordances and runtime checking. However, Swift also deliberately includes some unsafe constructs, such as the `Unsafe` pointer types in the standard library, language features like `nonisolated(unsafe)`, and interoperability with unsafe languages like C. For most Swift developers, this is a pragmatic solution that provides an appropriate level of memory safety while not getting in the way. -However, there are certain projects, organizations, and code bases that require stronger memory-safety guarantees, such as in security-critical subsystems handling untrusted data or that are executing with elevated privileges in an OS. This proposal introduces opt-in strict memory safety checking to identify those places in Swift code that make use of unsafe language constructs and APIs. Any code written within this strictly-safe subset also works as “normal” Swift and can interoperate with existing Swift code. +However, some projects want require stronger memory-safety guarantees than are provided Swift by default. These projects want to pay closer attention to uses of unsafe constructs in their code, and discourage casual use of unsafe constructs when a safe alternative exists. This proposal introduces opt-in strict memory safety checking to identify those places in Swift code that make use of unsafe language constructs and APIs. Any code written within this strictly-safe subset also works as “normal” Swift and can interoperate with existing Swift code. ## Motivation @@ -34,7 +34,9 @@ While there are a number of potential definitions for memory safety, the one pro Since its inception, Swift has provided memory safety for the first four dimensions. Lifetime safety is provided for reference types by automatic reference counting and for value types via [memory exclusivity](https://www.swift.org/blog/swift-5-exclusivity/); bounds safety is provided by bounds-checking on `Array` and other collections; type safety is provided by safe features for casting (`as?` , `is` ) and `enum` s; and initialization safety is provided by “definite initialization”, which doesn’t allow a variable to be accessed until it has been defined. Swift 6’s strict concurrency checking extends Swift’s memory safety guarantees to the last dimension. -Providing memory safety does not imply the absence of run-time failures. Good language design often means defining away runtime failures in the type system. However, memory safety requires only that an error in the program cannot be escalated into a violation of one of the safety properties. For example, having reference types be non-nullable by default defines away most problems with NULL pointers. With explicit optional types, the force-unwrap operator (postfix `!` ) meets the definition of memory safety by trapping at runtime if the unwrapped optional is `nil` . The standard library also provides the [`unsafelyUnwrapped` property](https://developer.apple.com/documentation/swift/optional/unsafelyunwrapped) that does not check for `nil` in release builds: this does not meet the definition of memory safety because it admits violations of initialization and lifetime safety that could be exploited. +Swift achieves safety with a mixture of static and dynamic checks. Static checks are better when possible, because they are surfaced at compile time and carry no runtime cost. Dynamic checks are sometimes necessary and are still acceptable, so long as the failure can't escalate into a memory safety problem. as long as failure can't escalate. Swift offers unsafe features to allow problems to be solved when neither static nor dynamic checks are sufficient. These nsafe features can still be used without compromising memory safety, but doing so requires more care because they have requirements that Swift can't automatically check. + +For example, Swift solves null references with optional types. Statically, Swift prevents you from using an optional reference without checking it first. If you're sure it's non-null, you can use the `!` operator, which is safe because Swift will dynamically check for `nil`. If you really can't afford that dynamic check, you can use [`unsafelyUnwrapped`](https://developer.apple.com/documentation/swift/optional/unsafelyunwrapped). This can still be correct if you can prove that the reference is definitely non-null for some reason that Swift doesn't know. But it is an unsafe feature because it admits violations if you're wrong. ## Proposed solution @@ -76,7 +78,7 @@ extension Array { } ``` -Users of this function that also enable strict safety checking will see warnings when using it. For example: +Code that does not enable safety checking will ignore the `@unsafe` attribute. Users of this function that also enable strict safety checking will see warnings when using it. For example: ```swift extension Array { @@ -110,12 +112,12 @@ Note that we do *not* require that the `sum` function be marked `@unsafe` just b ### Incremental adoption -The strict memory safety checking proposed here enforces a subset of Swift. Code written within this subset must also be valid Swift code, and must interoperate with Swift code that does not use this strict checking. Compared to other efforts in Swift that introduce stricter checking or a subset, strictly-safe Swift is smaller and more constrained, providing better interoperability and a more gradual adoption curve: +The strict memory safety checking proposed here enforces a subset of Swift. Code written within this subset must also be valid Swift code, and must interoperate with Swift code that does not use this strict checking. Compared to other efforts in Swift that introduce stricter checking or a subset, this mode is smaller and more constrained, providing better interoperability and a more gradual adoption curve: * Strict concurrency checking, the focus of the Swift 6 language mode, required major changes to the type system, including the propagation of `Sendable` and the understanding of what code must be run on the main actor. These are global properties that don't permit local reasoning, or even local fixes, making the interoperability problem particularly hard. In contrast, strict safety checking has little or no effect on the type system, and unsafety can be encapsulated with `unsafe` expressions or ignored by a module that doesn't enable the checking. * [Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0337-support-incremental-migration-to-concurrency-checking.md) is a subset of Swift that works without a runtime. Like the proposed strictly-safe subset, code written in Embedded Swift will also work as regular Swift. Embedded Swift and the strict safety checking proposed here are orthogonal and can be composed to (for example) ensure that firmware written in Swift has no runtime and provides the best memory-safety guarantees. -A Swift module that adopts strict safety checking can address all of the resulting diagnostics by applying the `@unsafe` attribute and `unsafe` expression in the appropriate places, without changing any other code. This application of attributes can be automated through Fix-Its, making it possible to enable the mode and silence all diagnostics automatically. It would then be left to the programmer to audit those places that have used `unsafe` to encapsulate unsafe behavior, to ensure that they are indeed safe. +A Swift module that adopts strict safety checking can address all of the resulting diagnostics by applying the `@unsafe` attribute and `unsafe` expression in the appropriate places, without changing any other code. This application of attributes can be automated through Fix-Its, making it possible to enable the mode and silence all diagnostics automatically. It would then be left to the programmer to audit those places that have used `unsafe` to encapsulate unsafe behavior, to ensure that they are indeed safe. Note that the strict safety checking does not by itself make the code more memory-safety: rather, it identifies those constructs that aren't safe, encouraging the use of safe alternatives and making it easier to audit for unsafe behavior. The introduction of the `@unsafe` attribute on a declaration has no effect on clients compiled without strict safety enabled. For clients that have enabled strict safety, they will start diagnosing uses of the newly-`@unsafe` API. However, these diagnostics are warnings with their own diagnostic group, so a client can ensure that they do not prevent the client from building. Therefore, modules can adopt strict safety checking at their own pace (or not) and clients of those modules are never "stuck" having to make major changes in response. From 92c2c9a00b9f00d9164188df96a8be1be8998607 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Jan 2025 10:23:36 -0800 Subject: [PATCH 3988/4563] Bring `swapAt` example forward and talk through why the annotations exist The code examples early in the presentation are fairly short because they illustrate narrow points. Bring forward the `swapAt` example and talk through why the various `unsafe` expressions make sense. Also note why the function itself still needs to be `@unsafe` even though it's trying to be safe. --- proposals/nnnn-strict-memory-safety.md | 46 ++++++++++++++++++++------ 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index e5aa608e08..706db28d71 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -10,7 +10,7 @@ ## Introduction -[Memory safety](https://en.wikipedia.org/wiki/Memory_safety) is a property of programming languages and their implementations that prevents programmer errors from manifesting as [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior) at runtime. Undefined behavior effectively breaks the semantic model of a language, with unpredictable results including crashes, data corruption, and otherwise-impossible program states. Such behavior can lead to hard-to-reproduce bugs as well as introduce security vulnerabilities. Various studies have shown that memory safety problems in C and C++ account for around [70% of security vulnerabilities in software](https://www.cisa.gov/sites/default/files/2023-12/The-Case-for-Memory-Safe-Roadmaps-508c.pdf). +[Memory safety](https://en.wikipedia.org/wiki/Memory_safety) is a property of programming languages and their implementations that prevents programmer errors from manifesting as [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior) at runtime. Undefined behavior effectively breaks the semantic model of a language, with unpredictable results including crashes, data corruption, and otherwise-impossible program states. Such behavior can lead to hard-to-reproduce bugs as well as introduce security vulnerabilities. Swift provides memory safety with a combination of language affordances and runtime checking. However, Swift also deliberately includes some unsafe constructs, such as the `Unsafe` pointer types in the standard library, language features like `nonisolated(unsafe)`, and interoperability with unsafe languages like C. For most Swift developers, this is a pragmatic solution that provides an appropriate level of memory safety while not getting in the way. @@ -106,9 +106,35 @@ extension Array { } ``` -The `unsafe` keyword here indicates the presence of unsafe code within that expression. As with `try` and `await`, it can cover multiple sources of unsafety within that expression: the call to `c_library_sum_function` is unsafe, as is the use of `buffer` and `buffer.baseAddress`, yet they are all covered by one `unsafe`. +The `unsafe` keyword here indicates the presence of unsafe code within that expression. As with `try` and `await`, it can cover multiple sources of unsafety within that expression: the call to `c_library_sum_function` is unsafe, as is the use of `buffer` and `buffer.baseAddress`, yet they are all covered by one `unsafe`. -Note that we do *not* require that the `sum` function be marked `@unsafe` just because it has unsafe code in it. The programmer may choose to indicate that `sum` is unsafe, but the assumption is that unsafe behavior is properly encapsulated when using `unsafe`. Additionally, the `@unsafe` attribute is available for all Swift code, even if it doesn't itself enable the strict safety checking described in this proposal. +Unlike `try`, `unsafe` doesn't propagate outward: we do *not* require that the `sum` function be marked `@unsafe` just because it has unsafe code in it. Similarly, the call to `withUnsafeBufferPointerSimplified` is unsafe because it involves the `UnsafeBufferPointer` type, not because it was passed a closure containing `unsafe` behavior. The programmer may choose to indicate that `sum` is unsafe, but the assumption is that unsafe behavior is properly encapsulated when using `unsafe` if the signature doesn't contain any unsafe types. Additionally, the `@unsafe` attribute and `unsafe` expression is available for all Swift code, even if it doesn't itself enable the strict safety checking described in this proposal. + +### A larger example: `swapAt` on unsafe pointers + +The operation `UnsafeMutableBufferPointer.swapAt` swaps the values at the given two indices in the buffer. Under the proposed strict safety mode, it would look like this: + +```swift +extension UnsafeMutableBufferPointer { + @unsafe public func swapAt(_ i: Element, _ j: Element) { + guard i != j else { return } + precondition(i >= 0 && j >= 0) + precondition(unsafe i < endIndex && j < endIndex) + let pi = unsafe (baseAddress! + i) + let pj = unsafe (baseAddress! + j) + let tmp = unsafe pi.move() + unsafe pi.moveInitialize(from: pj, count: 1) + unsafe pj.initialize(to: tmp) + } +} +``` + +The `swapAt` implementation uses a mix of safe and unsafe code. The code marked with `unsafe` identifies operations that Swift cannot verify memory safety for: + +* Performing pointer arithmetic on `baseAddress`: Swift cannot reason about the lifetime of that underlying pointer, nor whether the resulting pointer is still within the bounds of the allocation. +* Moving and initializing the actual elements. The elements need to already be initialized. + +The code itself has preconditions to ensure that the provided indices aren't out of bounds before performing the pointer arithmetic. However, there are other safety properties that cannot be checked with preconditions: that the memory associated with the pointer has been properly initialized, has a lifetime that spans the whole call, and is not being used simultaneously by any other part of the code. These safety properties are something that must be established by the *caller* of `swapAt`. Therefore, `swapAt` is marked `@unsafe` because callers of it need to reason about these properties. ### Incremental adoption @@ -435,13 +461,13 @@ A function marked `@unsafe` is unsafe to use, so any clients that have enabled s ```swift extension UnsafeMutableBufferPointer { - @unsafe public func swapAt(_ i: Int, _ j: Int) { + @unsafe public func swapAt(_ i: Element, _ j: Element) { guard i != j else { return } precondition(i >= 0 && j >= 0) precondition(unsafe i < endIndex && j < endIndex) - @unsafe let pi = unsafe (_position! + i) - @unsafe let pj = unsafe (_position! + j) - @unsafe let tmp = unsafe pi.move() + let pi = unsafe (baseAddress! + i) + let pj = unsafe (baseAddress! + j) + let tmp = unsafe pi.move() unsafe pi.moveInitialize(from: pj, count: 1) unsafe pj.initialize(to: tmp) } @@ -452,12 +478,12 @@ Now, it is very likely that an `@unsafe` function is going to make use of other ```swift extension UnsafeMutableBufferPointer { - @unsafe public func swapAt(_ i: Int, _ j: Int) { + @unsafe public func swapAt(_ i: Element, _ j: Element) { guard i != j else { return } precondition(i >= 0 && j >= 0) precondition(i < endIndex && j < endIndex) - let pi = (_position! + i) - let pj = (_position! + j) + let pi = (baseAddress! + i) + let pj = (baseAddress! + j) let tmp = pi.move() pi.moveInitialize(from: pj, count: 1) pj.initialize(to: tmp) From dde5dedf991a85e1d8ddf85968157d66804cc9af Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Jan 2025 10:39:46 -0800 Subject: [PATCH 3989/4563] Add the StrictMemorySafety feature to indicate when -strict-memory-safety is enabled --- proposals/nnnn-strict-memory-safety.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 706db28d71..578c9d84bb 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -4,6 +4,7 @@ * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: TBD * Status: **Awaiting review** +* Feature name: `StrictMemorySafety` * Vision: [Opt-in Strict Memory Safety Checking (Prospective)](https://github.com/swiftlang/swift-evolution/pull/2581) * Implementation: On main with experimental feature flags `AllowUnsafeAttribute` and `WarnUnsafe` * Review: ([pitch](https://forums.swift.org/t/pitch-opt-in-strict-memory-safety-checking/76689)) @@ -42,7 +43,7 @@ For example, Swift solves null references with optional types. Statically, Swift This proposal introduces an opt-in strict memory safety checking mode that identifies all uses of unsafe behavior within the given module. There are several parts to this change: -* A compiler flag `-strict-memory-safety` that enables warnings for all uses of unsafe constructs within a given module. All warnings will be in the diagnostic group `Unsafe`, enabling precise control over memory-safety-related warnings per [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md). +* A compiler flag `-strict-memory-safety` that enables warnings for all uses of unsafe constructs within a given module. All warnings will be in the diagnostic group `Unsafe`, enabling precise control over memory-safety-related warnings per [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md). When strict memory safety is enabled, the `StrictMemorySafety` feature will be set: `#if hasFeature(StrictMemorySafety)` can be used to detect when Swift code is being compiled in this mode. * An attribute `@unsafe` that indicates that a declaration is unsafe to use. Such declarations may use unsafe constructs within their signatures. * An `unsafe` expression that marks any use of unsafe constructs in an expression, much like `try` and `await`. * Standard library annotations to identify unsafe declarations. From ce9401c6c37ca06fc52fd1204ba88a52326c3f94 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Jan 2025 15:45:10 -0800 Subject: [PATCH 3990/4563] Add `@safe` to make a declaration safe despite unsafe types in its signature --- proposals/nnnn-strict-memory-safety.md | 39 ++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 578c9d84bb..2b0f7b7bc2 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -44,7 +44,8 @@ For example, Swift solves null references with optional types. Statically, Swift This proposal introduces an opt-in strict memory safety checking mode that identifies all uses of unsafe behavior within the given module. There are several parts to this change: * A compiler flag `-strict-memory-safety` that enables warnings for all uses of unsafe constructs within a given module. All warnings will be in the diagnostic group `Unsafe`, enabling precise control over memory-safety-related warnings per [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md). When strict memory safety is enabled, the `StrictMemorySafety` feature will be set: `#if hasFeature(StrictMemorySafety)` can be used to detect when Swift code is being compiled in this mode. -* An attribute `@unsafe` that indicates that a declaration is unsafe to use. Such declarations may use unsafe constructs within their signatures. +* An attribute `@unsafe` that indicates that a declaration is unsafe to use. Such declarations may use unsafe constructs within their signatures. +* A corresponding attribute `@safe` that indicates that a declaration whose signature contains unsafe constructs is actually safe to use. For example, the `count` property on `Unsafe(Mutable)BufferPointer` has an unsafe type in its signature (`self`), but is actually safe to use because it is just producing an `Int` value. Marking the property with `@safe` means that its use will be treated as safe. * An `unsafe` expression that marks any use of unsafe constructs in an expression, much like `try` and `await`. * Standard library annotations to identify unsafe declarations. @@ -210,6 +211,40 @@ There are a few exemptions to the rule that any unsafe constructs within the sig func hasDefault(value: Int = /*unsafe*/ getIntegerUnsafely()) { ... } ``` +### `@safe` attribute + +Like the `@unsafe` attribute, the `@safe` attribute ise used on declarations whose signatures involve unsafe types. However, the `@safe` attribute means that the declaration is consider safe to use even though its signature includes unsafe types. For example, marking `UnsafeBufferPointer` as `@unsafe` means that all operations involving an unsafe buffer pointer are implicitly considered `@unsafe`. The `@safe` attribute can be used to say that those particular operations are actually safe. For example, any operation involving buffer indices or count are safe, because they don't touch the memory itself. This can be indicated by marking these APIs `@safe`: + +```swift +extension UnsafeBufferPointer { + @safe public var count: Int + @safe public var startIndex: Int { 0 } + @safe public var endIndex: Int { count } +} +``` + +For an array, the `withUnsafeBufferPointer` operation itself also involves the unsafe type that it passes along to the closure. The array itself takes responsibility for the memory safety of the unsafe buffer pointer it vends, ensuring that the elements have been initialized (which is always the case for array elements), that the bounds are correct, and that nobody else has access to the buffer when it is provided. From that perspective, `withUnsafeBufferPointer` itself can be marked `@safe`, and any unsafety will be in the closure's use of the `UnsafeBufferPointer`. + +```swift +extension Array { + @safe func withUnsafeBufferPointer( + _ body: (UnsafeBufferPointer) throws(E) -> R + ) throws(E) -> R +} +``` + +A use of this API with the `c_library_sum_function` would look like this: + +```swift +extension Array { + func sum() -> Int { + withUnsafeBufferPointer { buffer in + unsafe c_library_sum_function(buffer.baseAddress, buffer.count, 0) + } + } +} +``` + ### `unsafe` expression When a declaration is marked `@unsafe`, it is free to use any other unsafe types as part of its interface. Any time there is executable code that makes use of unsafe constructs, that code must be within an `unsafe` expression or it will receive a diagnostic about uses of unsafe code. In the example from the previous section, `wrapper` can be marked as `@unsafe` to suppress diagnostics by explicitly propagating unsafety to their clients: @@ -267,7 +302,7 @@ In the standard library, the following functions and types would be marked `@uns * `withUnsafeCurrentTask` and `UnsafeCurrentTask`: The `UnsafeCurrentTask` type does not provide lifetime safety, and must only be used within the closure passed to `withUnsafeCurrentTask`. * `UnownedSerialExecutor`: This type is intentionally not lifetime safe. It's primary use is the `unownedExecutor` property of the `Actor` protocol, which documents the lifetime assumptions of the `UnownedSerialExecutor` instance it produces. -All of these APIs will be marked `@unsafe`. For all of the types that are `@unsafe`, any API that uses that type in its signature will also be marked `@unsafe`, such as `Array.withUnsafeBufferPointer`. Unless mentioned above, standard library APIs that do not have an unsafe type in their signature, but use unsafe constructs in their implementation, will be considered to be safe. +All of these APIs will be marked `@unsafe`. For standard library APIs that involve unsafe types, those that are safe to use will be marked `@safe` while those that require the user to maintain some aspect of safety will be marked `@unsafe`. Unless mentioned above, standard library APIs that do not have an unsafe type in their signature, but use unsafe constructs in their implementation, will be considered to be safe. ### Unsafe compiler flags From 695000a93cd8ef0bf871f483d8a4b9fb80b0bbe5 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 15 Jan 2025 16:02:35 -0800 Subject: [PATCH 3991/4563] [pitch] `Span`-providing properties in the standard library (#2620) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * pitch span-providing properties in the standard library * update title * add blurb about performance * add HTML anchor * add HTML anchor * add links * add future directions, including MutableSpan properties * edit alternatives and future directions * add detail regarding performance goals and memory * Add example of improved ergonomics * move `bytes` to be a conditional property of `Span` * add words for clarity * further refinements * add acknowledgement * whitespace fixes * drop the storage properties on `Slice` - these properties are not expressible in the current type system. - if we don’t get the generic ones, we should remove the raw ones too. * be clearer about SIMD --- proposals/AAAA-stdlib-span-properties.md | 295 +++++++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 proposals/AAAA-stdlib-span-properties.md diff --git a/proposals/AAAA-stdlib-span-properties.md b/proposals/AAAA-stdlib-span-properties.md new file mode 100644 index 0000000000..873843d940 --- /dev/null +++ b/proposals/AAAA-stdlib-span-properties.md @@ -0,0 +1,295 @@ +# Add `Span`-providing Properties to Standard Library Types + +* Proposal: [PR-2620](https://github.com/swiftlang/swift-evolution/pull/2620) +* Author: [Guillaume Lessard](https://github.com/glessard) +* Review Manager: (tbd) +* Status: **Pitch** +* Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) +* Bug: rdar://137710901 +* Implementation: (tbd) +* Review: [pitch](https://forums.swift.org/t/76138) + +[SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md +[SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md +[PR-2305]: https://github.com/swiftlang/swift-evolution/pull/2305 +[SE-0453]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0453-vector.md + +## Introduction + +We recently [introduced][SE-0447] the `Span` and `RawSpan` types, but did not provide ways to obtain instances of either from existing types. This proposal adds properties that vend a lifetime-dependent `Span` from a variety of standard library types, as well as vend a lifetime-dependent `RawSpan` when the underlying element type supports it. + +## Motivation + +Many standard library container types can provide direct access to their internal representation. Up to now, it has only been possible to do so in an unsafe way. The standard library provides this unsafe functionality with closure-taking functions such as `withUnsafeBufferPointer()`, `withContiguousStorageIfAvailable()` and `withUnsafeBytes()`. These functions have a few different drawbacks, most prominently their reliance on unsafe types, which makes them unpalatable in security-conscious environments. Closure-taking API can also be difficult to compose with new features and with one another. These issues are addressed head-on with non-escapable types in general, and `Span` in particular. With this proposal, compatible standard library types will provide access to their internal representation via computed properties of type `Span` and `RawSpan`. + +## Proposed solution + +Computed properties returning [non-escapable][SE-0446] copyable values represent a particular case of lifetime relationships between two bindings. While initializing a non-escapable value in general requires [lifetime annotations][PR-2305] in order to correctly describe the lifetime relationship, the specific case of computed properties returning non-escapable copyable values can only represent one type of relationship between the parent binding and the non-escapable instance it provides: a borrowing relationship. + +For example, in the example below we have an instance of type `A`, with a well-defined lifetime because it is non-copyable. An instance of `A` can provide access to a type `B` which borrows the instance `A`: + +```swift +struct A: ~Copyable, Escapable {} +struct B: ~Escapable, Copyable { + init(_ a: borrowing A) {} +} +extension A { + var b: B { B(self) } +} + +func function() { + var a = A() + var b = a.b // access to `a` begins here + read(b) + // `b` has ended here, ending access to `a` + modify(&a) // `modify()` can have exclusive access to `a` +} +``` +If we were to attempt using `b` again after the call to `modify(&a)`, the compiler would report an overlapping access error, due to attempting to mutate `a` (with `modify(&a)`) while it is already being accessed through `b`'s borrow. Note that the copyability of `B` means that it cannot represent a mutation of `A`; it therefore represents a non-exclusive borrowing relationship. + +Given this, we propose to enable the definition of a borrowing relationship via a computed property. With this feature we then propose to add `storage` computed properties to standard library types that can share their internal typed storage, as well as `bytes` computed properties to those standard library types that can safely share their internal storage as untyped memory. + +One of the purposes of `Span` is to provide a safer alternative to `UnsafeBufferPointer`. This proposal builds on it and allows us to rewrite code reliant on `withUnsafeBufferPointer()` to use `storage` properties instead. Eventually, code that requires access to contiguous memory can be rewritten to use `Span`, gaining better composability in the process. For example: + +```swift +let result = try myArray.withUnsafeBufferPointer { buffer in + let indices = findElements(buffer) + var myResult = MyResult() + for i in indices { + try myResult.modify(buffer[i]) + } +} +``` + +This closure-based call is difficult to evolve, such as making `result` have a non-copyable type, adding a concurrent task, or adding typed throws. An alternative based on a vended `Span` property would look like this: + +```swift +let span = myArray.storage +let indices = findElements(span) +var myResult = MyResult() +for i in indices { + try myResult.modify(span[i]) +} +``` + +In this version, code evolution is not constrained by a closure. Incorrect escapes of `span` will be diagnosed by the compiler, and the `modify()` function can be updated with typed throws, concurrency or other features as necessary. + +## Detailed Design + +Computed property getters returning non-escapable and copyable types (`~Escapable & Copyable`) become possible, requiring no additional annotations. The lifetime of their returned value depends on the type vending them. A `~Escapable & Copyable` value borrows another binding. In terms of the law of exclusivity, a borrow is a read-only access. Multiple borrows are allowed to overlap, but cannot overlap with any mutation. + +A computed property getter defined on an `Escapable` type and returning a `~Escapable & Copyable` value establishes a borrowing lifetime relationship of the returned value on the callee's binding. As long as the returned value exists (including local copies,) then the callee's binding remains borrowed. + +A computed property getter defined on a non-escapable and copyable (`~Escapable & Copyable`) type and returning a `~Escapable & Copyable` value copies the lifetime dependency of the callee. The returned value becomes an additional borrow of the callee's dependency, but is otherwise independent from the callee. + +A computed property getter defined on a non-escapable and non-copyable (`~Escapable & ~Copyable`) type returning a `~Escapable & Copyable` value establishes a borrowing lifetime relationship of the returned value on the callee's binding. As long as the returned value exists (including local copies,) then the callee's binding remains borrowed. + +By allowing the language to define lifetime dependencies in these limited ways, we can add `Span`-providing properties to standard library types. + +#### Extensions to Standard Library types + +The standard library and Foundation will provide `storage` and computed properties, returning lifetime-dependent `Span` instances. These computed properties are the safe and composable replacements for the existing `withUnsafeBufferPointer` closure-taking functions. + +```swift +extension Array { + /// Share this `Array`'s elements as a `Span` + var storage: Span { get } +} + +extension ArraySlice { + /// Share this `Array`'s elements as a `Span` + var storage: Span { get } +} + +extension ContiguousArray { + /// Share this `Array`'s elements as a `Span` + var storage: Span { get } +} + +extension String.UTF8View { + /// Share this `UTF8View`'s code units as a `Span` + var storage: Span { get } +} + +extension Substring.UTF8View { + /// Share this `UTF8View`'s code units as a `Span` + var storage: Span { get } +} + +extension CollectionOfOne { + /// Share this `Collection`'s element as a `Span` + var storage: Span { get } +} + +extension SIMD_N_ { // where _N_ ∈ {2, 3, 4 ,8, 16, 32, 64} + /// Share this vector's elements as a `Span` + var storage: Span { get } +} + +extension KeyValuePairs { + /// Share this `Collection`'s elements as a `Span` + var storage: Span<(Key, Value)> { get } +} +``` + +Conditionally to the acceptance of [`Vector`][SE-0453], we will also add the following: + +```swift +extension Vector where Element: ~Copyable { + /// Share this vector's elements as a `Span` + var storage: Span { get } +} +``` + +#### Accessing the raw bytes of a `Span` + +When a `Span`'s element is `BitwiseCopyable`, we allow viewing the underlying storage as raw bytes with `RawSpan`: + +```swift +extension Span where Element: BitwiseCopyable { + /// Share the raw bytes of this `Span`'s elements + var bytes: RawSpan { get } +} +``` + +The returned `RawSpan` instance will borrow the same binding as is borrowed by the `Span`. + +#### Extensions to unsafe buffer types + +We hope that `Span` and `RawSpan` will become the standard ways to access shared contiguous memory in Swift, but current API provide `UnsafeBufferPointer` and `UnsafeRawBufferPointer` instances to do this. We will provide ways to unsafely obtain `Span` and `RawSpan` instances from them, in order to bridge `UnsafeBufferPointer` to contexts that use `Span`, or `UnsafeRawBufferPointer` to contexts that use `RawSpan`. + +```swift +extension UnsafeBufferPointer { + /// Unsafely view this buffer as a `Span` + var storage: Span { get } +} + +extension UnsafeMutableBufferPointer { + /// Unsafely view this buffer as a `Span` + var storage: Span { get } +} + +extension UnsafeRawBufferPointer { + /// Unsafely view this raw buffer as a `RawSpan` + var bytes: RawSpan { get } +} + +extension UnsafeMutableRawBufferPointer { + /// Unsafely view this raw buffer as a `RawSpan` + var bytes: RawSpan { get } +} +``` + +All of these unsafe conversions return a value whose lifetime is dependent on the _binding_ of the UnsafeBufferPointer. Note that this does not keep the underlying memory alive, as usual where the `UnsafePointer` family of types is involved. The programmer must ensure the following invariants for as long as the `Span` or `RawSpan` binding is valid: + + - The underlying memory remains initialized. + - The underlying memory is not mutated. + +Failure to keep these invariants results in undefined behaviour. + +#### Extensions to `Foundation.Data` + +While the `swift-foundation` package and the `Foundation` framework are not governed by the Swift evolution process, `Data` is similar in use to standard library types, and the project acknowledges that it is desirable for it to have similar API when appropriate. Accordingly, we would add the following properties to `Foundation.Data`: + +```swift +extension Foundation.Data { + // Share this `Data`'s bytes as a `Span` + var storage: Span { get } + + // Share this `Data`'s bytes as a `RawSpan` + var bytes: RawSpan { get } +} +``` + +Unlike with the standard library types, we plan to have a `bytes` property on `Foundation.Data` directly. This type conceptually consists of untyped bytes, and `bytes` is likely to be the primary way to directly access its memory. As `Data`'s API presents its storage as a collection of `UInt8` elements, we provide both `bytes` and `storage`. Types similar to `Data` may choose to provide both typed and untyped `Span` properties. + +#### Performance + +The `storage` and `bytes` properties should be performant and return their `Span` or `RawSpan` with very little work, in O(1) time. This is the case for all native standard library types. There is a performance wrinkle for bridged `Array` and `String` instances on Darwin-based platforms, where they can be bridged to Objective-C types that do not guarantee contiguous storage. In such cases the implementation will eagerly copy the underlying data to the native Swift form, and return a `Span` or `RawSpan` pointing to that copy. + +This eager copy behaviour will be specific to the `storage` and `bytes` properties, and therefore the memory usage behaviour of existing unchanged code will remain the same. New code that adopts the `storage` and `bytes` properties will occasionally have higher memory usage due to the eager copies, but we believe this performance compromise is the right approach for the standard library. The alternative is to compromise the design for all platforms supported by Swift, and we consider that a non-starter. + +As a result of the eager copy behaviour for bridged `String.UTF8View` and `Array` instances, the `storage` property for these types will have a documented performance characteristic of "amortized constant time performance." + +## Source compatibility + +This proposal is additive and source-compatible with existing code. + +## ABI compatibility + +This proposal is additive and ABI-compatible with existing code. + +## Implications on adoption + +The additions described in this proposal require a new version of the Swift standard library and runtime. + +## Alternatives considered + +#### Adding `withSpan()` and `withBytes()` closure-taking functions + +The `storage` and `bytes` properties aim to be safe replacements for the `withUnsafeBufferPointer()` and `withUnsafeBytes()` closure-taking functions. We could consider `withSpan()` and `withBytes()` closure-taking functions that would provide an quicker migration away from the older unsafe functions. We do not believe the closure-taking functions are desirable in the long run. In the short run, there may be a desire to clearly mark the scope where a `Span` instance is used. The default method would be to explicitly consume a `Span` instance: +```swift +var a = ContiguousArray(0..<8) +var span = a.storage +read(span) +_ = consume span +a.append(8) +``` + +In order to visually distinguish this lifetime, we could simply use a `do` block: +```swift +var a = ContiguousArray(0..<8) +do { + let span = a.storage + read(span) +} +a.append(8) +``` + +A more targeted solution may be a consuming function that takes a non-escaping closure: +```swift +var a = ContiguousArray(0..<8) +var span = a.storage +consuming(span) { span in + read(span) +} +a.append(8) +``` + +During the evolution of Swift, we have learned that closure-based API are difficult to compose, especially with one another. They can also require alterations to support new language features. For example, the generalization of closure-taking API for non-copyable values as well as typed throws is ongoing; adding more closure-taking API may make future feature evolution more labor-intensive. By instead relying on returned values, whether from computed properties or functions, we build for greater composability. Use cases where this approach falls short should be reported as enhancement requests or bugs. + +#### Giving the properties different names + +We chose the names `storage` and `bytes` because those reflect _what_ they represent. Another option would be to name the properties after _how_ they represent what they do, which would be `span` and `rawSpan`. It is possible the name `storage` would be deemed to clash too much with existing properties of types that would like to provide views of their internal storage with `Span`-providing properties. For example, the Standard Library's concrete `SIMD`-conforming types have a property `var _storage`. The current proposal means that making this property of `SIMD` types into public API would entail a name change more significant than simply removing its leading underscore. + +#### Disallowing the definition of non-escapable properties of non-escapable types + +The particular case of the lifetime dependence created by a property of a copyable non-escapable type is not as simple as when the parent type is escapable. There are two possible ways to define the lifetime of the new instance: it can either depend on the lifetime of the original instance, or it can acquire the lifetime of the original instance and be otherwise independent. We believe that both these cases can be useful, but that in the majority of cases the desired behaviour will be to have an independent return value, where the newly returned value borrows the same binding as the callee. Therefore we believe that is reasonable to reserve the unannotated spelling for this more common case. + +The original version of this pitch disallowed this. As a consequence, the `bytes` property had to be added on each individual type, rather than having `bytes` as a conditional property of `Span`. + +#### Omitting extensions to `UnsafeBufferPointer` and related types + +We could omit the extensions to `UnsafeBufferPointer` and related types, and rely instead of future `Span` and `RawSpan` initializers. The initializers can have the advantage of being able to communicate semantics (somewhat) through their parameter labels. However, they also have a very different shape than the `storage` computed properties we are proposing. We believe that the adding the same API on both safe and unsafe types is advantageous, even if the preconditions for the properties cannot be statically enforced. + +## Future directions + +Note: The future directions stated in [SE-0447](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md#Directions) apply here as well. + +#### Safe mutations with `MutableSpan` + +Some data structures can delegate mutations of their owned memory. In the standard library the function `withMutableBufferPointer()` provides this functionality in an unsafe manner. We expect to add a `MutableSpan` type to support delegating mutations of initialized memory. Standard library types will then add a way to vend `MutableSpan` instances. This could be with a closure-taking `withMutableSpan()` function, or a new property, such as `var mutableStorage`. Note that a computed property providing mutable access needs to have a different name than the `storage` properties proposed here, because we cannot overload the return type of computed properties based on whether mutation is desired. + +#### A `ContiguousStorage` protocol + +An early version of the `Span` proposal ( [SE-0447][SE-0447] ) proposed a `ContiguousStorage` protocol by which a type could indicate that it can provide a `Span`. `ContiguousStorage` would form a bridge between generically-typed interfaces and a performant concrete implementation. It would supersede the rejected [SE-0256](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0256-contiguous-collection.md), and many of the standard library collections could conform to `ContiguousStorage`. + +The properties added by this proposal are largely the concrete implementations of `ContiguousStorage`. As such, it seems like an obvious enhancement to this proposal. + +Unfortunately, a major issue prevents us from proposing it at this time: the ability to suppress requirements on `associatedtype` declarations was deferred during the review of [SE-0427](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0427-noncopyable-generics.md). Once this restriction is lifted, then we could propose a `ContiguousStorage` protocol. + +The other limitation stated in [SE-0447][SE-0447]'s section about `ContiguousStorage` is "the inability to declare a `_read` acessor as a protocol requirement." This proposal's addition to enable defining a borrowing relationship via a computed property is a solution to that, as long as we don't need to use a coroutine accessor to produce a `Span`. While allowing the return of `Span`s through coroutine accessors may be undesirable, whether it is undesirable is unclear until coroutine accessors are formalized in the language. + +## Acknowledgements + +Thanks to Ben Rimmington for suggesting that the `bytes` property should be on `Span` rather than on every type. From 18777961e5b456bd02a42f792fb71c478916ef69 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Jan 2025 22:08:47 -0800 Subject: [PATCH 3992/4563] Initiate review for SE-0456: Add `Span`-providing Properties to Standard Library Types (#2651) --- ...span-properties.md => 0456-stdlib-span-properties.md} | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) rename proposals/{AAAA-stdlib-span-properties.md => 0456-stdlib-span-properties.md} (98%) diff --git a/proposals/AAAA-stdlib-span-properties.md b/proposals/0456-stdlib-span-properties.md similarity index 98% rename from proposals/AAAA-stdlib-span-properties.md rename to proposals/0456-stdlib-span-properties.md index 873843d940..bc8d43dd10 100644 --- a/proposals/AAAA-stdlib-span-properties.md +++ b/proposals/0456-stdlib-span-properties.md @@ -1,12 +1,11 @@ # Add `Span`-providing Properties to Standard Library Types -* Proposal: [PR-2620](https://github.com/swiftlang/swift-evolution/pull/2620) +* Proposal: [SE-0456](0456-stdlib-span-properties.md) * Author: [Guillaume Lessard](https://github.com/glessard) -* Review Manager: (tbd) -* Status: **Pitch** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active Review (January 15...28, 2024) * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) -* Bug: rdar://137710901 -* Implementation: (tbd) +* Implementation: [PR #78561](https://github.com/swiftlang/swift/pull/78561) * Review: [pitch](https://forums.swift.org/t/76138) [SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md From a890bd25b141263de93dc23ea39480cc5b5509b0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Jan 2025 22:17:07 -0800 Subject: [PATCH 3993/4563] Add link to the review thread (#2652) --- proposals/0456-stdlib-span-properties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0456-stdlib-span-properties.md b/proposals/0456-stdlib-span-properties.md index bc8d43dd10..484eb3c2d5 100644 --- a/proposals/0456-stdlib-span-properties.md +++ b/proposals/0456-stdlib-span-properties.md @@ -6,7 +6,7 @@ * Status: **Active Review (January 15...28, 2024) * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: [PR #78561](https://github.com/swiftlang/swift/pull/78561) -* Review: [pitch](https://forums.swift.org/t/76138) +* Review: [Review](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233), [Pitch](https://forums.swift.org/t/76138) [SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md [SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md From dd56edfe2088780d55db9cba9877ac1c822efa29 Mon Sep 17 00:00:00 2001 From: Philipp Gabriel Date: Thu, 16 Jan 2025 18:52:47 +0100 Subject: [PATCH 3994/4563] Expose attosecond representation of `Duration` (#2633) * Add proposal for `Int128` support in `Duration` * Add PR to NNNN-duration-and-int128.md * Rename proposal to be more clear about the intent * Update and rename NNNN-duration-attosecond-represenation.md to 0457-duration-attosecond-represenation.md Assigned SE-0457 --------- Co-authored-by: Stephen Canon --- .../0457-duration-attosecond-represenation.md | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 proposals/0457-duration-attosecond-represenation.md diff --git a/proposals/0457-duration-attosecond-represenation.md b/proposals/0457-duration-attosecond-represenation.md new file mode 100644 index 0000000000..10d6fa1e2a --- /dev/null +++ b/proposals/0457-duration-attosecond-represenation.md @@ -0,0 +1,110 @@ +# Expose attosecond representation of `Duration` + +* Proposal: [SE-0457](0457-duration-attosecond-represenation.md) +* Authors: [Philipp Gabriel](https://github.com/ph1ps) +* Review Manager: [Stephen Canon](https://github.com/stephentyrone) +* Status: **Active review (January 16 ... 30, 2025)** +* Implementation: [swiftlang/swift#78202](https://github.com/swiftlang/swift/pull/78202) +* Review: ([pitch](https://forums.swift.org/t/pitch-adding-int128-support-to-duration)) + +## Introduction +This proposal introduces public APIs to enable seamless integration of `Int128` into the `Duration` type. Specifically, it provides support for directly accessing a `Duration`'s attosecond representation via the newly available `Int128` type and simplifies the creation of `Duration` values from attoseconds. + +## Motivation +The `Duration` type currently offers two ways to construct and decompose itself: + +**Low and high bits** +```swift +public struct Duration: Sendable { + public var _low: UInt64 + public var _high: Int64 + public init(_high: Int64, low: UInt64) { ... } +} +``` +**Components** +```swift +extension Duration { + public var components: (seconds: Int64, attoseconds: Int64) { ... } + public init(secondsComponent: Int64, attosecondsComponent: Int64) { ... } +} +``` +However, both approaches have limitations when it comes to exposing `Duration`'s total attosecond representation: +- The `_low` and `_high` properties are underscored, indicating that their direct use is discouraged. +- The `components` property decomposes the value into seconds and attoseconds, requiring additional arithmetic operations for many use cases. + +This gap becomes particularly evident in scenarios like generating a random `Duration`, which currently requires verbose and potentially inefficient code: +```swift +func randomDuration(upTo maxDuration: Duration) -> Duration { + let attosecondsPerSecond: Int128 = 1_000_000_000_000_000_000 + let upperRange = Int128(maxDuration.components.seconds) * attosecondsPerSecond + Int128(maxDuration.components.attoseconds) + let (seconds, attoseconds) = Int128.random(in: 0.. Duration { + return Duration(attoseconds: Int128.random(in: 0.. Duration { ... } +} +``` + +However, this approach would introduce asymmetry to other factory methods which support both `Double` and `BinaryInteger` overloads: +```swift +extension Duration { + public static func microseconds(_ microseconds: T) -> Duration { ... } + public static func microseconds(_ microseconds: Double) -> Duration { ... } +} +``` +For attoseconds, adding these overloads would lead to practical issues: + +1. A `Double` overload is nonsensical because sub-attoseconds are not supported, meaning the method cannot represent fractional attoseconds. +2. A `BinaryInteger` overload introduces additional complexity. Since it would need to support types other than `Int128`, arithmetic operations would be necessary to ensure correct scaling and truncation, negating the simplicity and precision that the `Int128`-specific initializer aims to provide. + +Ultimately, the static func `attoseconds(_:)` would likely end up as a one-off method with only an `Int128` overload. This inconsistency diminishes the appeal of the factory method approach. The proposed `init(attoseconds:)` initializer avoids these issues, offering a direct and clear way to work with attoseconds, while remaining symmetrical with the existing `Duration` API structure. From 3ee16ac0b7252edee365cdf702a3cdaeda1b0798 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Thu, 16 Jan 2025 12:58:37 -0500 Subject: [PATCH 3995/4563] Update 0457-duration-attosecond-represenation.md (#2656) --- proposals/0457-duration-attosecond-represenation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0457-duration-attosecond-represenation.md b/proposals/0457-duration-attosecond-represenation.md index 10d6fa1e2a..876e5ef432 100644 --- a/proposals/0457-duration-attosecond-represenation.md +++ b/proposals/0457-duration-attosecond-represenation.md @@ -5,7 +5,7 @@ * Review Manager: [Stephen Canon](https://github.com/stephentyrone) * Status: **Active review (January 16 ... 30, 2025)** * Implementation: [swiftlang/swift#78202](https://github.com/swiftlang/swift/pull/78202) -* Review: ([pitch](https://forums.swift.org/t/pitch-adding-int128-support-to-duration)) +* Review: ([pitch](https://forums.swift.org/t/pitch-adding-int128-support-to-duration))([review](https://forums.swift.org/t/se-0457-expose-attosecond-representation-of-duration/77249)) ## Introduction This proposal introduces public APIs to enable seamless integration of `Int128` into the `Duration` type. Specifically, it provides support for directly accessing a `Duration`'s attosecond representation via the newly available `Int128` type and simplifies the creation of `Duration` values from attoseconds. From b7ad56223615053c4f653eef007d6e98f850755c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 16 Jan 2025 10:35:48 -0800 Subject: [PATCH 3996/4563] Consider the unsafe addressors to be part of the "signature" of their enclosing storage declaration --- proposals/nnnn-strict-memory-safety.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 2b0f7b7bc2..ce1e0aaf62 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -279,7 +279,7 @@ The `unsafe` expression is much like `try` and `await`, in that it acknowledges The following language constructs are always considered to be unsafe: * `unowned(unsafe)`: Used to store a reference without maintaining its reference count. The safe counterpart, `unowned`, uses dynamic checking to ensure that the reference isn't accessed after the corresponding object has been released. The `unsafe` variant disables that dynamic checking. Uses of `unowned(unsafe)` entities are not memory-safe. -* `unsafeAddressor`, `unsafeMutableAddressor`: These accessors vend an unsafe pointer, and are therefore unsafe to declare. Other accessors (e.g., `get` and `set`) can provide safe alternatives. +* `unsafeAddressor`, `unsafeMutableAddressor`: These accessors vend an unsafe pointer, and are therefore unsafe to declare. Other accessors (e.g., `get` and `set`) can provide safe alternatives. The accessors are considered to be part of the signature of the property or subscript they're associated with; the property should be marked either `@unsafe` or `@safe` to suppress the safety diagnostic on the declaration of an unsafe accessor. * `@exclusivity(unchecked)`: Used to remove dynamic exclusivity checks from a particular variable, which can mean that dynamic exclusivity violations go undetected at run time, causing a memory safety violation. Uses of `@exclusivity(unchecked)` entities are not memory-safe. The following language constructs are considered to be unsafe when strict concurrency checking is enabled (i.e., in the Swift 6 language mode): From 1732c114a8880fa8c483cb7b1a7e944e08426baa Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 16 Jan 2025 10:36:15 -0800 Subject: [PATCH 3997/4563] Add missing end-bold markup (#2655) --- proposals/0456-stdlib-span-properties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0456-stdlib-span-properties.md b/proposals/0456-stdlib-span-properties.md index 484eb3c2d5..5ed799515c 100644 --- a/proposals/0456-stdlib-span-properties.md +++ b/proposals/0456-stdlib-span-properties.md @@ -3,7 +3,7 @@ * Proposal: [SE-0456](0456-stdlib-span-properties.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review (January 15...28, 2024) +* Status: **Active Review (January 15...28, 2024)** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: [PR #78561](https://github.com/swiftlang/swift/pull/78561) * Review: [Review](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233), [Pitch](https://forums.swift.org/t/76138) From a162a37903b088028eb7b32f2af8b71963beedb7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 16 Jan 2025 10:56:26 -0800 Subject: [PATCH 3998/4563] Add a Future Directions section on unsafe cases in pattern matching --- proposals/nnnn-strict-memory-safety.md | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index ce1e0aaf62..1a0c0d0870 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -437,6 +437,33 @@ The first two options are the most straightforward, but the fact that actors hav It is unclear whether `SerialExecutor` is or will be the only protocol of this nature. If there are others, it could be worth providing a special form of the `@unsafe` attribute on the protocol itself, such as `@unsafe(conforms)`, that is only considered unsafe for conforming types. +### Handling of `@unsafe` cases + +When an enum case is explicitly marked `@unsafe`, but involves no associated data that is unsafe, this proposal doesn't have a way to suppress safety diagnostics when pattern matching that case. For example: + +```swift +enum WeirdAddress { + @unsafe case rawOffsetIntoGlobalArray(Int) +} + +func example(_ address: WeirdAddress) { + if case .rawOffsetIntoGlobalArray(let offset) = weirdAddress { // reference to @unsafe case rawOffsetIntoGlobalArray that can't be suppressed + } +} + +``` + +We have several options here: + +* We could suppress the diagnostic for this use of an `@unsafe case`. One would still get diagnostics when constructing such a case. + +* We could reject `@unsafe` on case declarations that don't involve any unsafe types. + +* We could extend the pattern grammar with an `unsafe` pattern to suppress this diagnostic, e.g., + ```swift + if case unsafe .rawOffsetIntoGlobalArray(let offset) = weirdAddress { ... } + ``` + ### Overloading to stage in safe APIs When adopting the strict memory safety mode, it's likely that a Swift module will want to replace existing APIs that traffic in unsafe types (such as `UnsafeMutablePointer`) with safer equivalents (such as `Span`). To retain compatibility for older clients, the existing APIs will need to be left in place. Unfortunately, this might mean that the best name for the API is already taken. For example, perhaps we have a data packet that exposes its bytes via a property: From 3e68edf4634fbb8a7c62e762af80f0a6ea92c080 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 16 Jan 2025 11:03:58 -0800 Subject: [PATCH 3999/4563] Fixed some minor confusing bits --- proposals/nnnn-strict-memory-safety.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 1a0c0d0870..8cf231e5e6 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -15,7 +15,7 @@ Swift provides memory safety with a combination of language affordances and runtime checking. However, Swift also deliberately includes some unsafe constructs, such as the `Unsafe` pointer types in the standard library, language features like `nonisolated(unsafe)`, and interoperability with unsafe languages like C. For most Swift developers, this is a pragmatic solution that provides an appropriate level of memory safety while not getting in the way. -However, some projects want require stronger memory-safety guarantees than are provided Swift by default. These projects want to pay closer attention to uses of unsafe constructs in their code, and discourage casual use of unsafe constructs when a safe alternative exists. This proposal introduces opt-in strict memory safety checking to identify those places in Swift code that make use of unsafe language constructs and APIs. Any code written within this strictly-safe subset also works as “normal” Swift and can interoperate with existing Swift code. +However, some projects want to require stronger memory-safety guarantees than are Swift provides by default. These projects want to pay closer attention to uses of unsafe constructs in their code, and discourage casual use of unsafe constructs when a safe alternative exists. This proposal introduces opt-in strict memory safety checking to identify those places in Swift code that make use of unsafe language constructs and APIs. Any code written within this strictly-safe subset also works as “normal” Swift and can interoperate with existing Swift code. ## Motivation @@ -35,7 +35,7 @@ While there are a number of potential definitions for memory safety, the one pro Since its inception, Swift has provided memory safety for the first four dimensions. Lifetime safety is provided for reference types by automatic reference counting and for value types via [memory exclusivity](https://www.swift.org/blog/swift-5-exclusivity/); bounds safety is provided by bounds-checking on `Array` and other collections; type safety is provided by safe features for casting (`as?` , `is` ) and `enum` s; and initialization safety is provided by “definite initialization”, which doesn’t allow a variable to be accessed until it has been defined. Swift 6’s strict concurrency checking extends Swift’s memory safety guarantees to the last dimension. -Swift achieves safety with a mixture of static and dynamic checks. Static checks are better when possible, because they are surfaced at compile time and carry no runtime cost. Dynamic checks are sometimes necessary and are still acceptable, so long as the failure can't escalate into a memory safety problem. as long as failure can't escalate. Swift offers unsafe features to allow problems to be solved when neither static nor dynamic checks are sufficient. These nsafe features can still be used without compromising memory safety, but doing so requires more care because they have requirements that Swift can't automatically check. +Swift achieves safety with a mixture of static and dynamic checks. Static checks are better when possible, because they are surfaced at compile time and carry no runtime cost. Dynamic checks are sometimes necessary and are still acceptable, so long as the failure can't escalate into a memory safety problem. Swift offers unsafe features to allow problems to be solved when neither static nor dynamic checks are sufficient. These unsafe features can still be used without compromising memory safety, but doing so requires more care because they have requirements that Swift can't automatically check. For example, Swift solves null references with optional types. Statically, Swift prevents you from using an optional reference without checking it first. If you're sure it's non-null, you can use the `!` operator, which is safe because Swift will dynamically check for `nil`. If you really can't afford that dynamic check, you can use [`unsafelyUnwrapped`](https://developer.apple.com/documentation/swift/optional/unsafelyunwrapped). This can still be correct if you can prove that the reference is definitely non-null for some reason that Swift doesn't know. But it is an unsafe feature because it admits violations if you're wrong. @@ -199,7 +199,7 @@ There are a few exemptions to the rule that any unsafe constructs within the sig ```swift func sum(array: [Int]) -> Int { array.withUnsafeBufferPointer { buffer in - let base = /*unsafe*/ buffer.baseAddress + /*@unsafe is unnecessary here*/ let base = unsafe buffer.baseAddress // ... } } @@ -208,7 +208,7 @@ There are a few exemptions to the rule that any unsafe constructs within the sig * Default arguments of functions are part of the implementation of a function, not its signature. For example, the following function does not have any unsafe types in its signature, so it does not require `@unsafe`, even though the default argument for `value` involves unsafe code. That unsafe code is effectively part of the body of the function, so it follows the rules for `unsafe` expressions. ```swift - func hasDefault(value: Int = /*unsafe*/ getIntegerUnsafely()) { ... } + func hasDefault(value: Int = unsafe getIntegerUnsafely()) { ... } ``` ### `@safe` attribute From 97fae3ca1e8c1c1015b161d5dd6becfecdfbf21c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 16 Jan 2025 11:06:00 -0800 Subject: [PATCH 4000/4563] Yet more minor fixes --- proposals/nnnn-strict-memory-safety.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 8cf231e5e6..7b7843b663 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -285,14 +285,14 @@ The following language constructs are always considered to be unsafe: The following language constructs are considered to be unsafe when strict concurrency checking is enabled (i.e., in the Swift 6 language mode): * `nonisolated(unsafe)`: Allows a property to be accessed from concurrent code without ensuring that such accesses are done so safely. Uses of `nonisolated(unsafe)` entities are not memory-safe. -* `@preconcurrency` imports: Suppresses diagnostics related to data race safety when they relate to specific imported modules, which can introduce thread safety issues. The `@preconcurrency` import will need to be annotated with `@unsafe` in the strict dsafety mode. +* `@preconcurrency` imports: Suppresses diagnostics related to data race safety when they relate to specific imported modules, which can introduce thread safety issues. The `@preconcurrency` import will need to be annotated with `@unsafe` in the strict safety mode. ### Unsafe standard library APIs In the standard library, the following functions and types would be marked `@unsafe` : * `Unsafe(Mutable)(Raw)(Buffer)Pointer`, `OpaquePointer`, `CVaListPointer`: These types provide neither lifetime nor bounds safety. Over time, Swift code is likely to move toward their safe replacements, such as `(Raw)Span`. -* `(Closed)Range.init(uncheckedBounds:)`: This operation makes it possible to create a range that doesn't satisfy invariants on which other bounds safety checking (e.g., in `Array.subscript`) +* `(Closed)Range.init(uncheckedBounds:)`: This operation makes it possible to create a range that doesn't satisfy invariants on which other bounds safety checking (e.g., in `Array.subscript`) relies. * `Span.subscript(unchecked:)` : An unchecked subscript whose use can introduce bounds safety problems. * `Unmanaged`: Wrapper over reference-counted types that explicitly disables reference counting, potentially introducing lifetime safety issues. * `unsafeBitCast`: Allows type casts that are not known to be safe, which can introduce type safety problems. From 11f5ff9414e67c2756176e8d599df24ef37dfe9a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 16 Jan 2025 11:33:03 -0800 Subject: [PATCH 4001/4563] Replace example because withUnsafebufferPointerSimplified is weird --- proposals/nnnn-strict-memory-safety.md | 92 ++++++++++++-------------- 1 file changed, 43 insertions(+), 49 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 7b7843b663..e875ab185b 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -61,56 +61,60 @@ public struct UnsafeBufferPointer { ... } This indicates that use of this type is not memory-safe. Any declaration that has `UnsafeBufferPointer` as part of its type is also unsafe, and would produce a warning under this strict safety mode, e.g., ```swift -extension Array { - // warning on next line: reference to unsafe generic struct 'UnsafeBufferPointer' - func withUnsafeBufferPointerSimplified(_ body: (UnsafeBufferPointer) -> T) -> T { - // ... - } -} +// warning: reference to unsafe generic struct 'UnsafePointer' +func sumIntBuffer(_ address: UnsafePointer?, _ count: Int) -> Int { ... } ``` This warning can be suppressed by marking the function as `@unsafe`: ```swift -extension Array { - @unsafe - func withUnsafeBufferPointerSimplified(_ body: (UnsafeBufferPointer) -> T) -> T { - // ... - } -} +@unsafe +func sumIntBuffer(_ address: UnsafePointer?, _ count: Int, _ start: Int) -> Int { ... } ``` -Code that does not enable safety checking will ignore the `@unsafe` attribute. Users of this function that also enable strict safety checking will see warnings when using it. For example: +Users of this function that enable strict safety checking will see warnings when using it. For example: ```swift extension Array { func sum() -> Int { - // warning: use of unsafe function 'withUnsafeBufferPointerSimplified' - withUnsafeBufferPointerSimplified { buffer in - // warning: use of function 'c_library_sum_function' with unsafe type 'UnsafePointer''. - c_library_sum_function(buffer.baseAddress, buffer.count, 0) + withUnsafeBufferPointer { buffer in + // warning: use of unsafe function 'sumIntBuffer' and unsafe property 'baseAddress' + sumIntBuffer(buffer.baseAddress, buffer.count, 0) } } } ``` -Both the call to `withUnsafeBufferPointerSimplified` (which is `@unsafe`) and the call to `c_library_sum_function` (which has a parameter of `@unsafe` type `UnsafePointer`) would trigger warnings about uses of unsafe constructs. +Both the call to `sumIntBuffer` and access to the property `UnsafeBufferPointer.baseAddress` involve unsafe code, and therefore will produce a warning. Because `UnsafeBufferPointer` and `UnsafePointer` are `@unsafe` types, this code will get a warning regardless of whether the declarations were marked `@unsafe`, because having unsafe types in the signature of a declaration implies that they are `@unsafe`. This helps us identify more unsafe code even when the libraries we depend on haven't enabled strict safety checking themselves. -To suppress these warnings, both expressions must be marked with `unsafe` in the same manner as one would mark a throwing expression with `try` or an asynchronous expression with `async`. The warning-free version of this code is: +To suppress these warnings, the expressions involving unsafe code must be marked with `unsafe` in the same manner as one would mark a throwing expression with `try` or an asynchronous expression with `async`. The warning-free version of this code is: ```swift extension Array { func sum() -> Int { - unsafe withUnsafeBufferPointerSimplified { buffer in - unsafe c_library_sum_function(buffer.baseAddress, buffer.count, 0) + withUnsafeBufferPointer { buffer in + // warning: use of unsafe function 'sumIntBuffer' and unsafe property 'baseAddress' + unsafe sumIntBuffer(buffer.baseAddress, buffer.count, 0) } } } ``` -The `unsafe` keyword here indicates the presence of unsafe code within that expression. As with `try` and `await`, it can cover multiple sources of unsafety within that expression: the call to `c_library_sum_function` is unsafe, as is the use of `buffer` and `buffer.baseAddress`, yet they are all covered by one `unsafe`. +The `unsafe` keyword here indicates the presence of unsafe code within that expression. As with `try` and `await`, it can cover multiple sources of unsafety within that expression: the call to `sumIntBuffer` is unsafe, as is the use of `buffer` and `buffer.baseAddress`, yet they are all covered by one `unsafe`. + +Unlike `try`, `unsafe` doesn't propagate outward: we do *not* require that the `sum` function be marked `@unsafe` just because it has unsafe code in it. Similarly, the call to `withUnsafeBufferPointer` doesn't have to be marked as `unsafe` just because it has a closure that is unsafe. The programmer may choose to indicate that `sum` is unsafe, but the assumption is that unsafe behavior is properly encapsulated when using `unsafe` if the signature doesn't contain any unsafe types. + +The function `Array.withUnsafeBufferPointer` has an unsafe type in its signature, because it passes an unsafe buffer pointer to its closure parameter. However, this function itself is addressing all of the memory-safety issues with providing such a pointer, and it's up to the closure itself to ensure that it is memory safe. Therefore, we mark `withUnsafeBufferPointer` with the `@safe` attribute to indicate that it iself is not introducing memory-safety issues: + +```swift +extension Array { + @safe func withUnsafeBufferPointer( + _ body: (UnsafeBufferPointer) throws(E) -> R + ) throws(E) -> R +} +``` -Unlike `try`, `unsafe` doesn't propagate outward: we do *not* require that the `sum` function be marked `@unsafe` just because it has unsafe code in it. Similarly, the call to `withUnsafeBufferPointerSimplified` is unsafe because it involves the `UnsafeBufferPointer` type, not because it was passed a closure containing `unsafe` behavior. The programmer may choose to indicate that `sum` is unsafe, but the assumption is that unsafe behavior is properly encapsulated when using `unsafe` if the signature doesn't contain any unsafe types. Additionally, the `@unsafe` attribute and `unsafe` expression is available for all Swift code, even if it doesn't itself enable the strict safety checking described in this proposal. +The new attributes `@safe` and `@unsafe`, as well as the `unsafe` expression, are all available in Swift regardless of whether strict safety checking is enabled, and all code using these features retains the same semantics. Strict safety checking will *only* produce diagnostics. ### A larger example: `swapAt` on unsafe pointers @@ -566,8 +570,8 @@ In the proposed design, a function with no unsafe types in its signature is cons extension Array { // this function is considered safe func sum() -> Int { - unsafe withUnsafeBufferPointerSimplified { buffer in - unsafe c_library_sum_function(buffer.baseAddress, buffer.count, 0) + withUnsafeBufferPointer { buffer in + unsafe sumIntBuffer(buffer.baseAddress, buffer.count, 0) } } } @@ -581,8 +585,8 @@ There are several options for such a suppression mechanism. An attribute form, ` extension Array { // this function is considered safe func sum() -> Int { - unsafe! withUnsafeBufferPointerSimplified { buffer in - unsafe! c_library_sum_function(buffer.baseAddress, buffer.count, 0) + withUnsafeBufferPointer { buffer in + unsafe! sumIntBuffer(buffer.baseAddress, buffer.count, 0) } } } @@ -592,34 +596,24 @@ This proposal chooses not to go down this path, because having a function signat ### `@safe(unchecked)` attribute to allow unsafe code -Early iterations of this proposal introduced a `@safe(unchecked)` attribute as an alternative to `unsafe` expressions. The `@safe(unchecked)` attribute would be placed on a function to suppress diagnostics about use of unsafe constructs within its definition. For our `sum` example, this means one would write: - -```swift -extension Array { - @safe(unchecked) - func sum() -> Int { - withUnsafeBufferPointerSimplified { buffer in - c_library_sum_function(buffer.baseAddress, buffer.count, 0) - } - } -} -``` - -This approach means fewer annotations for unsafe code, because one `@safe(unchecked)` covers the entire body of the function. That is a trade-off: it's less verbose, but it makes it much harder to identify exactly what parts of the implementation are actually unsafe. To reduce the amount of code covered by a `@safe(unchecked)` down to just the actual unsafe code, one would have to factor out the unsafe code into many small `@safe(unchecked)` functions, which could end up being quite verbose and make it harder to reason about the code itself. - -This approach also implies that making a function `@unsafe` will suppress any diagnostics about uses of unsafe constructs within the body of that function. Rust's `unsafe` functions have this behavior, and it is the source of [some concern in that community](https://rust-lang.github.io/rfcs/2585-unsafe-block-in-unsafe-fn.html). Part of the issue there involves wanting to tightly scope the uses of unsafe code, but that discussion brings up another reason: it means that `unsafe` on a function actually has a dual role: it both says that the function is unsafe to use *and also* says that the function can freely use unsafe constructs in its definition. +Early iterations of this proposal introduced a `@safe(unchecked)` attribute as an alternative to `unsafe` expressions. The `@safe(unchecked)` attribute would be placed on a function to suppress diagnostics about use of unsafe constructs within its definition. This has all of the same downsides as having `@unsafe` imply cover for all of the uses of unsafe code within the body of a function, albeit while providing a safe interface. ### `unsafe` blocks -The `unsafe` expression proposed here covers unsafe constructs within a single expression. For unsafe-heavy code, this can introduce a large number of `unsafe` keywords. There is an alternative formulation for acknowledging unsafe code that is used in some peer languages like C# and Rust: an `unsafe` block, which is a statement that suppresses diagnostics about uses of unsafe code within it. For example, the `sum` example could be written as follows: +The `unsafe` expression proposed here covers unsafe constructs within a single expression. For unsafe-heavy code, this can introduce a large number of `unsafe` keywords. There is an alternative formulation for acknowledging unsafe code that is used in some peer languages like C# and Rust: an `unsafe` block, which is a statement that suppresses diagnostics about uses of unsafe code within it. For example: ```swift -extension Array { - func sum() -> Int { +extension UnsafeMutableBufferPointer { + @unsafe public func swapAt(_ i: Element, _ j: Element) { + guard i != j else { return } + precondition(i >= 0 && j >= 0) + precondition(i < endIndex && j < endIndex) unsafe { - withUnsafeBufferPointerSimplified { buffer in - c_library_sum_function(buffer.baseAddress, buffer.count, 0) - } + let pi = (baseAddress! + i) + let pj = (baseAddress! + j) + let tmp = pi.move() + pi.moveInitialize(from: pj, count: 1) + pj.initialize(to: tmp) } } } From d52a3453655dbf92b48fc20035707df9e33ccbcd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 16 Jan 2025 12:48:37 -0800 Subject: [PATCH 4002/4563] Move the "overloading to stage in safe APIs" section to Alternatives Considered We probably don't want to ever do this, so move it out of Future Directions --- proposals/nnnn-strict-memory-safety.md | 104 ++++++++++++------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index e875ab185b..67207410a4 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -468,58 +468,6 @@ We have several options here: if case unsafe .rawOffsetIntoGlobalArray(let offset) = weirdAddress { ... } ``` -### Overloading to stage in safe APIs - -When adopting the strict memory safety mode, it's likely that a Swift module will want to replace existing APIs that traffic in unsafe types (such as `UnsafeMutablePointer`) with safer equivalents (such as `Span`). To retain compatibility for older clients, the existing APIs will need to be left in place. Unfortunately, this might mean that the best name for the API is already taken. For example, perhaps we have a data packet that exposes its bytes via a property: - -```swift -public class DataPacket { - @unsafe public let bytes: UnsafeRawBufferPointer -} -``` - -The `bytes` property is necessarily unsafe. Far better would be to produce a `RawSpan`, which we can easily do with another property: - -```swift -extension DataPacket { - public var byteSpan: RawSpan -} -``` - -Clients using the existing `bytes` will continue to work, and those that care about memory safety can choose to move to `byteSpan`. All of this works, but is somewhat annoying because the good name, `bytes`, has been taken for the API we no longer want to use. - -Swift does allow type-based overloading, including on the type of properties, so one could introduce an overloaded `bytes` property, like this: - -```swift -extension DataPacket { - public var bytes: RawSpan -} -``` - -This works for code that accesses `bytes` and then uses it in a context where type inference can figure out whether we need an `UnsafeRawBufferPointer` or a `RawSpan`, but fails if that context does not exist: - -```swift -let unsafeButGoodBytes: UnsafeRawBufferPointer = dataPacket.bytes // ok, uses @unsafe bytes -let goodBytes: RawSpan = dataPacket.bytes // ok, uses safe bytes -let badBytes = dataPacket.bytes // error: ambiguous! -``` - -We could consider extending Swift's overloading rules to make this kind of evolution possible. For example, one could introduce a pair of rules into the language: - -1. When strict memory safety checking is enabled, `@unsafe` declarations are dis-favored vs. safe ones, so the unsafe `bytes: UnsafeRawBufferPointer` would be a worse solution for type inference to pick than the safe alternative, `bytes: RawSpan`. - -2. Overloads that were introduced to replace unsafe declarations could be marked with a new attribute `@safe(unsafeDisfavored)` so that they would be disfavored only when building with strict memory safety checking disabled. - -Assuming these rules, and that the safe `bytes: RawSpan` had the `@safe(unsafeDisfavored)` attribute, the example uses of `DataPacket` would resolve as follows: - -* `unsafeButGoodBytes` would always be initialized with the unsafe `bytes`. If strict memory safety were enabled, this use would produce a warning. -* `goodBytes` would always be initialized with the safe `bytes`. -* `badBytes` would be initialized differently based on whether strict memory safety was enabled: - * If enabled, `badBytes` would choose the safe version of `bytes` to produce the safest code, because the unsafe one is disfavored (rule #1). - * If disabled, `badBytes` would choose the unsafe version of `bytes` to provide source compatibility with existing code, because the safe one is disfavored (rule #2). - -There are downsides to this approach. It partially undermines the source compatibility story for the strict safety mode, because type inference now behaves differently when the mode is enabled. That means, for example, there might be errors---not warnings---because some code like `badBytes` above would change behavior, causing additional failures. Changing the behavior of type inference is also risky in an of itself, because it is not always easy to reason about all of the effects of such a change. That said, the benefit of being able to move toward a more memory-safe future might be worth it. - ## Alternatives considered ### `@unsafe` implying `unsafe` throughout a function body @@ -631,6 +579,58 @@ This proposal introduced strict safety checking as an opt in mode and not an [*u * Interoperability with the C family of languages is an important feature for Swift. Most C(++) APIs are unlikely to ever adopt the safety-related attributes described above, which means that enabling strict safety checking by default would undermine the usability of C(++) interoperability. * Swift's current (non-strict) memory safety by default is likely to be good enough for the vast majority of users of Swift, so the benefit of enabling stricter checking by default is unlikely to be worth the disruption it would cause. +### Overloading to stage in safe APIs + +When adopting the strict memory safety mode, it's likely that a Swift module will want to replace existing APIs that traffic in unsafe types (such as `UnsafeMutablePointer`) with safer equivalents (such as `Span`). To retain compatibility for older clients, the existing APIs will need to be left in place. Unfortunately, this might mean that the best name for the API is already taken. For example, perhaps we have a data packet that exposes its bytes via a property: + +```swift +public class DataPacket { + @unsafe public let bytes: UnsafeRawBufferPointer +} +``` + +The `bytes` property is necessarily unsafe. Far better would be to produce a `RawSpan`, which we can easily do with another property: + +```swift +extension DataPacket { + public var byteSpan: RawSpan +} +``` + +Clients using the existing `bytes` will continue to work, and those that care about memory safety can choose to move to `byteSpan`. All of this works, but is somewhat annoying because the good name, `bytes`, has been taken for the API we no longer want to use. + +Swift does allow type-based overloading, including on the type of properties, so one could introduce an overloaded `bytes` property, like this: + +```swift +extension DataPacket { + public var bytes: RawSpan +} +``` + +This works for code that accesses `bytes` and then uses it in a context where type inference can figure out whether we need an `UnsafeRawBufferPointer` or a `RawSpan`, but fails if that context does not exist: + +```swift +let unsafeButGoodBytes: UnsafeRawBufferPointer = dataPacket.bytes // ok, uses @unsafe bytes +let goodBytes: RawSpan = dataPacket.bytes // ok, uses safe bytes +let badBytes = dataPacket.bytes // error: ambiguous! +``` + +We could consider extending Swift's overloading rules to make this kind of evolution possible. For example, one could introduce a pair of rules into the language: + +1. When strict memory safety checking is enabled, `@unsafe` declarations are dis-favored vs. safe ones, so the unsafe `bytes: UnsafeRawBufferPointer` would be a worse solution for type inference to pick than the safe alternative, `bytes: RawSpan`. + +2. Overloads that were introduced to replace unsafe declarations could be marked with a new attribute `@safe(unsafeDisfavored)` so that they would be disfavored only when building with strict memory safety checking disabled. + +Assuming these rules, and that the safe `bytes: RawSpan` had the `@safe(unsafeDisfavored)` attribute, the example uses of `DataPacket` would resolve as follows: + +* `unsafeButGoodBytes` would always be initialized with the unsafe `bytes`. If strict memory safety were enabled, this use would produce a warning. +* `goodBytes` would always be initialized with the safe `bytes`. +* `badBytes` would be initialized differently based on whether strict memory safety was enabled: + * If enabled, `badBytes` would choose the safe version of `bytes` to produce the safest code, because the unsafe one is disfavored (rule #1). + * If disabled, `badBytes` would choose the unsafe version of `bytes` to provide source compatibility with existing code, because the safe one is disfavored (rule #2). + +There are downsides to this approach. It partially undermines the source compatibility story for the strict safety mode, because type inference now behaves differently when the mode is enabled. That means, for example, there might be errors---not warnings---because some code like `badBytes` above would change behavior, causing additional failures. Changing the behavior of type inference is also risky in an of itself, because it is not always easy to reason about all of the effects of such a change. That said, the benefit of being able to move toward a more memory-safe future might be worth it. + ### Optional `message` for the `@unsafe` attribute We could introduce an optional `message` argument to the `@unsafe` attribute, which would allow programmers to indicate *why* the use of a particular declaration is unsafe and, more importantly, how to safely write code that uses it. However, this argument isn't strictly necessary: a comment could provide the same information, and there is established tooling to expose comments to programmers that wouldn't be present for this attribute's message, so we have omitted this feature. From 740f5ac6f4d2b3f3732af3cbfa92f34da210dad2 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 16 Jan 2025 12:53:13 -0800 Subject: [PATCH 4003/4563] Typo fixes --- proposals/nnnn-strict-memory-safety.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 67207410a4..618c5a6adb 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -15,7 +15,7 @@ Swift provides memory safety with a combination of language affordances and runtime checking. However, Swift also deliberately includes some unsafe constructs, such as the `Unsafe` pointer types in the standard library, language features like `nonisolated(unsafe)`, and interoperability with unsafe languages like C. For most Swift developers, this is a pragmatic solution that provides an appropriate level of memory safety while not getting in the way. -However, some projects want to require stronger memory-safety guarantees than are Swift provides by default. These projects want to pay closer attention to uses of unsafe constructs in their code, and discourage casual use of unsafe constructs when a safe alternative exists. This proposal introduces opt-in strict memory safety checking to identify those places in Swift code that make use of unsafe language constructs and APIs. Any code written within this strictly-safe subset also works as “normal” Swift and can interoperate with existing Swift code. +However, some projects want to require stronger memory-safety guarantees than Swift provides by default. These projects want to pay closer attention to uses of unsafe constructs in their code, and discourage casual use of unsafe constructs when a safe alternative exists. This proposal introduces opt-in strict memory safety checking to identify those places in Swift code that make use of unsafe language constructs and APIs. Any code written within this strictly-safe subset also works as “normal” Swift and can interoperate with existing Swift code. ## Motivation @@ -489,7 +489,7 @@ extension UnsafeMutableBufferPointer { } ``` -Now, it is very likely that an `@unsafe` function is going to make use of other unsafe constructs, so we could choose to make `@unsafe` on a function acknowledge all uses of unsafe code within its definition. For example, this would mean that marking `swapAt` with `@unsafe` means that one need not have any `unsafe` expressions in its body: +We could choose to make `@unsafe` on a function acknowledge all uses of unsafe code within its definition. For example, this would mean that marking `swapAt` with `@unsafe` means that one need not have any `unsafe` expressions in its body: ```swift extension UnsafeMutableBufferPointer { From 0ce7d90917d5a2560950d2f9fa8cbef81985d8b8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 16 Jan 2025 12:56:19 -0800 Subject: [PATCH 4004/4563] Switch @safe example "count" over to "withUnsafeBufferPointer" --- proposals/nnnn-strict-memory-safety.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/nnnn-strict-memory-safety.md index 618c5a6adb..7a4bd5c11a 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/nnnn-strict-memory-safety.md @@ -45,7 +45,7 @@ This proposal introduces an opt-in strict memory safety checking mode that ident * A compiler flag `-strict-memory-safety` that enables warnings for all uses of unsafe constructs within a given module. All warnings will be in the diagnostic group `Unsafe`, enabling precise control over memory-safety-related warnings per [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md). When strict memory safety is enabled, the `StrictMemorySafety` feature will be set: `#if hasFeature(StrictMemorySafety)` can be used to detect when Swift code is being compiled in this mode. * An attribute `@unsafe` that indicates that a declaration is unsafe to use. Such declarations may use unsafe constructs within their signatures. -* A corresponding attribute `@safe` that indicates that a declaration whose signature contains unsafe constructs is actually safe to use. For example, the `count` property on `Unsafe(Mutable)BufferPointer` has an unsafe type in its signature (`self`), but is actually safe to use because it is just producing an `Int` value. Marking the property with `@safe` means that its use will be treated as safe. +* A corresponding attribute `@safe` that indicates that a declaration whose signature contains unsafe constructs is actually safe to use. For example, the `withUnsafeBufferPointer` method on `Array` has an unsafe type in its signature (`self`), but is actually safe to use because it handles safety for the unsafe buffer pointer it vends to its closure argument. The closure itself will need to handle the unsafety when using that unsafe buffer pointer. * An `unsafe` expression that marks any use of unsafe constructs in an expression, much like `try` and `await`. * Standard library annotations to identify unsafe declarations. From db86206315fcef4356fac3bae98f3d04aecea0e5 Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Fri, 17 Jan 2025 02:04:35 +0100 Subject: [PATCH 4005/4563] [SE-0424]: Change checkIsolation() to checkIsolated() (#2650) Before this change, the proposal was inconsistent in naming the proposed API: - 4 instances of `checkIsolation()` - 9 instances of `checkIsolated()` As far as I can tell from the Git history, this discrepancy has been there during the review period, so it's a little unclear which name has actually been accepted ;-). This change unifies the name to `checkIsolated()` because that's the actual name of the API in Swift 6.0. --- .../0424-custom-isolation-checking-for-serialexecutor.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0424-custom-isolation-checking-for-serialexecutor.md b/proposals/0424-custom-isolation-checking-for-serialexecutor.md index d04cc3d7fc..cc7a2d0d29 100644 --- a/proposals/0424-custom-isolation-checking-for-serialexecutor.md +++ b/proposals/0424-custom-isolation-checking-for-serialexecutor.md @@ -54,7 +54,7 @@ One way to think of this proposal is that gives all `SerialExecutor`s the power We propose to add a new last-resort mechanism to executor comparison, which will be used by all the isolation-checking APIs in the concurrency library. -This will be done by providing a new `checkIsolation()` protocol requirement on `SerialExecutor`: +This will be done by providing a new `checkIsolated()` protocol requirement on `SerialExecutor`: ```swift protocol SerialExecutor: Executor { @@ -73,11 +73,11 @@ protocol SerialExecutor: Executor { /// a job itself. /// /// A default implementation is provided that unconditionally causes a fatal error. - func checkIsolation() + func checkIsolated() } extension SerialExecutor { - public func checkIsolation() { + public func checkIsolated() { fatalError("Incorrect actor executor assumption, expected: \(self)") } } @@ -170,7 +170,7 @@ Asynchronous functions should not use dynamic isolation checking. Isolation che ### Introduce `globalMainExecutor` global property and utilize `checkIsolated` on it -This proposal also paves the way to clean up this hard-coded aspect of the runtime, and it would be possible to change these heurystics to instead invoke the `checkIsolation()` method on a "main actor executor" SerialExecutor reference if it were available. +This proposal also paves the way to clean up this hard-coded aspect of the runtime, and it would be possible to change these heurystics to instead invoke the `checkIsolated()` method on a "main actor executor" SerialExecutor reference if it were available. This proposal does not introduce a `globalMainActorExecutor`, however, similar how how [SE-0417: Task ExecutorPreference](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0417-task-executor-preference.md) introduced a: From a0b9c21f37b654f379227531514f9a466a893f07 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 17 Jan 2025 19:32:00 -0500 Subject: [PATCH 4006/4563] Assign SE-0458 to the strict memory safety proposal and put it in review Also fix a typo --- ...strict-memory-safety.md => 0458-strict-memory-safety.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{nnnn-strict-memory-safety.md => 0458-strict-memory-safety.md} (99%) diff --git a/proposals/nnnn-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md similarity index 99% rename from proposals/nnnn-strict-memory-safety.md rename to proposals/0458-strict-memory-safety.md index 7a4bd5c11a..74101fd8ee 100644 --- a/proposals/nnnn-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -1,9 +1,9 @@ # Opt-in Strict Memory Safety Checking -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0458](0458-strict-memory-safety.md) * Authors: [Doug Gregor](https://github.com/DougGregor) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active Review (January 17th...27th, 2025)** * Feature name: `StrictMemorySafety` * Vision: [Opt-in Strict Memory Safety Checking (Prospective)](https://github.com/swiftlang/swift-evolution/pull/2581) * Implementation: On main with experimental feature flags `AllowUnsafeAttribute` and `WarnUnsafe` From 665eabe678bd0d113f922b0c0172f7a7b74a8a3b Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 17 Jan 2025 19:41:11 -0500 Subject: [PATCH 4007/4563] Link SE-0458 to its review thread --- proposals/0458-strict-memory-safety.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index 74101fd8ee..c87e0a8aa9 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -7,7 +7,7 @@ * Feature name: `StrictMemorySafety` * Vision: [Opt-in Strict Memory Safety Checking (Prospective)](https://github.com/swiftlang/swift-evolution/pull/2581) * Implementation: On main with experimental feature flags `AllowUnsafeAttribute` and `WarnUnsafe` -* Review: ([pitch](https://forums.swift.org/t/pitch-opt-in-strict-memory-safety-checking/76689)) +* Review: ([pitch](https://forums.swift.org/t/pitch-opt-in-strict-memory-safety-checking/76689)) ([review](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274)) ## Introduction From db70e9a7da7d9bf5218a3ea3aa58aa9a660ddab2 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 17 Jan 2025 19:10:27 -0800 Subject: [PATCH 4008/4563] [SE0456] fix typo --- proposals/0456-stdlib-span-properties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0456-stdlib-span-properties.md b/proposals/0456-stdlib-span-properties.md index 5ed799515c..7f0fb35959 100644 --- a/proposals/0456-stdlib-span-properties.md +++ b/proposals/0456-stdlib-span-properties.md @@ -87,7 +87,7 @@ By allowing the language to define lifetime dependencies in these limited ways, #### Extensions to Standard Library types -The standard library and Foundation will provide `storage` and computed properties, returning lifetime-dependent `Span` instances. These computed properties are the safe and composable replacements for the existing `withUnsafeBufferPointer` closure-taking functions. +The standard library and Foundation will provide `storage` computed properties, returning lifetime-dependent `Span` instances. These computed properties are the safe and composable replacements for the existing `withUnsafeBufferPointer` closure-taking functions. ```swift extension Array { From 2e2c2679750ac7276bab7a41a8ebb492eaf07f6e Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Sat, 18 Jan 2025 15:58:22 +0100 Subject: [PATCH 4009/4563] [SE-0431] Fix typo --- proposals/0431-isolated-any-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0431-isolated-any-functions.md b/proposals/0431-isolated-any-functions.md index a3dd6498bd..1797df6616 100644 --- a/proposals/0431-isolated-any-functions.md +++ b/proposals/0431-isolated-any-functions.md @@ -336,7 +336,7 @@ to this proposal. In order for a call to an `@isolated(any)` function to be treated as not crossing an isolation boundary, the caller must be known to have the same isolation as the function. Since the isolation of an -`@isoalted(any)` parameter is necessarily an opaque value, this would +`@isolated(any)` parameter is necessarily an opaque value, this would require the caller to be declared with value-specific isolation. It is currently not possible for a local function or closure to be isolated to a specific value that isn't already the isolation of the From 488f5c7d70d30546573b17432063f849fabc7d24 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 20 Jan 2025 08:50:39 -0800 Subject: [PATCH 4010/4563] SE-0458: Replace "Element" with "Index" in the swapAt signature (#2662) --- proposals/0458-strict-memory-safety.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index c87e0a8aa9..b60f7cb53c 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -122,7 +122,7 @@ The operation `UnsafeMutableBufferPointer.swapAt` swaps the values at the given ```swift extension UnsafeMutableBufferPointer { - @unsafe public func swapAt(_ i: Element, _ j: Element) { + @unsafe public func swapAt(_ i: Index, _ j: Index) { guard i != j else { return } precondition(i >= 0 && j >= 0) precondition(unsafe i < endIndex && j < endIndex) @@ -476,7 +476,7 @@ A function marked `@unsafe` is unsafe to use, so any clients that have enabled s ```swift extension UnsafeMutableBufferPointer { - @unsafe public func swapAt(_ i: Element, _ j: Element) { + @unsafe public func swapAt(_ i: Index, _ j: Index) { guard i != j else { return } precondition(i >= 0 && j >= 0) precondition(unsafe i < endIndex && j < endIndex) @@ -493,7 +493,7 @@ We could choose to make `@unsafe` on a function acknowledge all uses of unsafe c ```swift extension UnsafeMutableBufferPointer { - @unsafe public func swapAt(_ i: Element, _ j: Element) { + @unsafe public func swapAt(_ i: Index, _ j: Index) { guard i != j else { return } precondition(i >= 0 && j >= 0) precondition(i < endIndex && j < endIndex) @@ -552,7 +552,7 @@ The `unsafe` expression proposed here covers unsafe constructs within a single ```swift extension UnsafeMutableBufferPointer { - @unsafe public func swapAt(_ i: Element, _ j: Element) { + @unsafe public func swapAt(_ i: Index, _ j: Index) { guard i != j else { return } precondition(i >= 0 && j >= 0) precondition(i < endIndex && j < endIndex) From 61220a472d2a09c42f8e94d4ad5c47288d094f86 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 20 Jan 2025 09:55:01 -0800 Subject: [PATCH 4011/4563] SE-0458: Add missing override keywords (#2663) --- proposals/0458-strict-memory-safety.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index b60f7cb53c..c6aa09f11a 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -327,7 +327,7 @@ class Super { } class Sub: Super { - @unsafe func f() { ... } // warning: override of safe instance method with unsafe instance method + @unsafe override func f() { ... } // warning: override of safe instance method with unsafe instance method } ``` @@ -336,7 +336,7 @@ to suppress this warning, the `Sub` class itself can be marked as `@unsafe`, e.g ```swift @unsafe class Sub: Super { - func f() { ... } // no more warning + override func f() { ... } // no more warning } ``` From f2cab4ddc3381d1dc7a970e813ed29e27b5ae43f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 20 Jan 2025 09:57:01 -0800 Subject: [PATCH 4012/4563] SE-0458 make count a let (#2664) --- proposals/0458-strict-memory-safety.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index c6aa09f11a..b973058c7d 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -221,7 +221,7 @@ Like the `@unsafe` attribute, the `@safe` attribute ise used on declarations who ```swift extension UnsafeBufferPointer { - @safe public var count: Int + @safe public let count: Int @safe public var startIndex: Int { 0 } @safe public var endIndex: Int { count } } From a3c41d72e0678fcac43ee9cb5f7e0706396b5f2e Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 21 Jan 2025 17:51:41 -0500 Subject: [PATCH 4013/4563] Remove "prospective vision" from the title and add some boilerplate text --- visions/memory-safety.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/visions/memory-safety.md b/visions/memory-safety.md index bae7288b08..612695d7fe 100644 --- a/visions/memory-safety.md +++ b/visions/memory-safety.md @@ -1,7 +1,9 @@ -# [Prospective vision] Optional Strict Memory Safety for Swift +# Optional Strict Memory Safety for Swift Swift is a memory-safe language *by default* , meaning that the major language features and standard library APIs are memory-safe. However, it is possible to opt out of memory safety when it’s pragmatic using certain “unsafe” language or library constructs. This document proposes a path toward an optional “strict” subset of Swift that prohibits any unsafe features. This subset is intended to be used for Swift code bases where memory safety is an absolute requirement, such as security-critical libraries. +This document is an official feature [vision document](https://forums.swift.org/t/the-role-of-vision-documents-in-swift-evolution/62101). The Language Steering Group has endorsed the goals and basic approach laid out in this document. This endorsement is not a pre-approval of any of the concrete proposals that may come out of this document. All proposals will undergo normal evolution review, which may result in rejection or revision from how they appear in this document. + ## Introduction [Memory safety](https://en.wikipedia.org/wiki/Memory_safety) is a popular topic in programming languages nowadays. Essentially, memory safety is a property that prevents programmer errors from manifesting as [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior) at runtime. Undefined behavior effectively breaks the semantic model of a language, with unpredictable results including crashes, data corruption, and otherwise-impossible program states. Much of the recent focus on memory safety is motivated by security, because memory safety issues offer a fairly direct way to compromise a program: in fact, the lack of memory safety in C and C++ has been found to be the root cause for ~70% of reported security issues in various analyses [[1](https://msrc.microsoft.com/blog/2019/07/a-proactive-approach-to-more-secure-code/)][[2](https://www.chromium.org/Home/chromium-security/memory-safety/)]. @@ -110,7 +112,7 @@ public subscript(_ position: Int) -> Element { } ``` -The specific syntax chosen will be the subject of a specific proposal, and need not be determined by this vision document. Regardless, a Swift module that enables strict safety checking must limit its use of unsafe constructs to `@unsafe` declarations or those parts of the code that have acknowledged local use of unsafe constructs. +The specific syntax chosen will be the subject of a specific proposal, and need not be determined by this document. Regardless, a Swift module that enables strict safety checking must limit its use of unsafe constructs to `@unsafe` declarations or those parts of the code that have acknowledged local use of unsafe constructs. ### Auditability @@ -239,15 +241,15 @@ func substring_match(_ sequence: Span, _ subsequence: Span) -> Spa The introduction of any kind of additional checking into Swift requires a strategy that accounts for the practicalities of adoption within the Swift ecosystem. Different developers adopt new features on their own schedules, and some Swift code will never enable new checking features. Therefore, it is important that a given Swift module can adopt the proposed strict safety checking without requiring any module it depends on to have already done so, and without breaking any of its own clients that have not enabled strict safety checking. -The optional strict memory safety model proposed by this vision lends itself naturally to incremental adoption. The proposed `@unsafe` attribute is not part of the type of the declaration it is applied to, and therefore does not propagate through the type system in any manner. Additionally, any use of an unsafe construct can be addressed locally, either by encapsulating it (e.g., via `@safe(unchecked)`) or propagating it (with `@unsafe`). This means that a module that has not adopted strict safety checking will not see any diagnostics related to this checking, even when modules it depends on adopt strict safety checking. +The optional strict memory safety model proposed by this lends itself naturally to incremental adoption. The proposed `@unsafe` attribute is not part of the type of the declaration it is applied to, and therefore does not propagate through the type system in any manner. Additionally, any use of an unsafe construct can be addressed locally, either by encapsulating it (e.g., via `@safe(unchecked)`) or propagating it (with `@unsafe`). This means that a module that has not adopted strict safety checking will not see any diagnostics related to this checking, even when modules it depends on adopt strict safety checking. The strict memory safety checking does not require any changes to the binary interface of a module, so it can be retroactively enabled (including `@unsafe` annotations) with no ABI or back-deployment concerns. Additionally, it is independent of other language subsetting approaches, such as Embedded Swift. ## Should strict memory safety checking become the default? -This vision proposes that the strict safety checking described be an opt-in feature with no path toward becoming the default behavior in some future language mode. There are several reasons why this checking should remain an opt-in feature for the foreseeable future: +This proposes that the strict safety checking described be an opt-in feature with no path toward becoming the default behavior in some future language mode. There are several reasons why this checking should remain an opt-in feature for the foreseeable future: * The various `Unsafe` pointer types are the only way to work with contiguous memory in Swift today, and the safe replacements (e.g., `Span`) are new constructs that will take a long time to propagate through the ecosystem. Some APIs depending on these `Unsafe` pointer types cannot be replaced because it would break existing clients (either source, binary, or both). * Interoperability with the C family of languages is an important feature for Swift. Most C(++) APIs are unlikely to ever adopt the safety-related attributes described above, which means that enabling strict safety checking by default would undermine the usability of C(++) interoperability. * Swift's current (non-strict) memory safety by default is likely to be good enough for the vast majority of users of Swift, so the benefit of enabling stricter checking by default is unlikely to be worth the disruption it would cause. -* The auditing facilities described in this vision should be sufficient for Swift users who require strict memory safety, to establish where unsafe constructs are used and prevent "backsliding" where their use grows in an existing code base. These Swift users are unlikely to benefit much from strict safety being enabled by default in a new language mode, aside from any additional social pressure that would create on Swift programmers to adopt it. \ No newline at end of file +* The auditing facilities described in this vision should be sufficient for Swift users who require strict memory safety, to establish where unsafe constructs are used and prevent "backsliding" where their use grows in an existing code base. These Swift users are unlikely to benefit much from strict safety being enabled by default in a new language mode, aside from any additional social pressure that would create on Swift programmers to adopt it. From 6508c6563a42d13587a251c8d3b6d813b02dbfb3 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 22 Jan 2025 13:03:50 -0800 Subject: [PATCH 4014/4563] [SE-0456] Clarifications (#2665) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [SE-0456] Clarify UnsafeBufferPointer invariants * [SE-0456] Clarify “Implications on adoption” --- proposals/0456-stdlib-span-properties.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0456-stdlib-span-properties.md b/proposals/0456-stdlib-span-properties.md index 7f0fb35959..b8010eba97 100644 --- a/proposals/0456-stdlib-span-properties.md +++ b/proposals/0456-stdlib-span-properties.md @@ -179,7 +179,7 @@ extension UnsafeMutableRawBufferPointer { } ``` -All of these unsafe conversions return a value whose lifetime is dependent on the _binding_ of the UnsafeBufferPointer. Note that this does not keep the underlying memory alive, as usual where the `UnsafePointer` family of types is involved. The programmer must ensure the following invariants for as long as the `Span` or `RawSpan` binding is valid: +All of these unsafe conversions return a value whose lifetime is dependent on the _binding_ of the UnsafeBufferPointer. This dependency does not keep the underlying memory alive. As is usual where the `UnsafePointer` family of types is involved, the programmer must ensure the memory remains allocated while it is in use. Additionally, the following invariants must remain true for as long as the `Span` or `RawSpan` value exists: - The underlying memory remains initialized. - The underlying memory is not mutated. @@ -220,7 +220,7 @@ This proposal is additive and ABI-compatible with existing code. ## Implications on adoption -The additions described in this proposal require a new version of the Swift standard library and runtime. +The additions described in this proposal require a version of the Swift standard library which include the `Span` and `RawSpan` types. ## Alternatives considered From f3253b56052802e070b97badfde398e61a951278 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 23 Jan 2025 18:38:27 +0000 Subject: [PATCH 4015/4563] SE-0454 is accepted. --- proposals/0454-memory-allocator.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/0454-memory-allocator.md b/proposals/0454-memory-allocator.md index 614a171316..c7c6f0d755 100644 --- a/proposals/0454-memory-allocator.md +++ b/proposals/0454-memory-allocator.md @@ -3,9 +3,10 @@ * Proposal: [SE-0454](0454-memory-allocator.md) * Authors: [Saleem Abdulrasool](https://github.com/compnerd) * Review Manager: [Alastair Houghton](https://github.com/al45tair) -* Status: **Active review (January 9–23)** +* Status: **Accepted** * Implementation: [swiftlang/swift#76563](https://github.com/swiftlang/swift/pull/76563) * Review: ([review](https://forums.swift.org/t/se-454-adopt-mimalloc-for-windows-toolchain/77096)) + ([acceptance](https://forums.swift.org/t/accepted-se-0454-custom-allocator-for-toolchain-adopt-mimalloc-for-windows-toolchain/77413)) ## Introduction From 97ef144633405eeb2424e84bdcbc8e6206e5e9b1 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Thu, 23 Jan 2025 17:27:10 -0800 Subject: [PATCH 4016/4563] Add Section Placement Control draft --- proposals/0nnn-section-control.md | 447 ++++++++++++++++++++++++++++++ 1 file changed, 447 insertions(+) create mode 100644 proposals/0nnn-section-control.md diff --git a/proposals/0nnn-section-control.md b/proposals/0nnn-section-control.md new file mode 100644 index 0000000000..985be91e6f --- /dev/null +++ b/proposals/0nnn-section-control.md @@ -0,0 +1,447 @@ +# Section Placement Control + +* Proposal: [SE-0NNN](0nnn-section-control.md) +* Authors: [Kuba Mracek](https://github.com/kubamracek) +* Status: Pitch #3 +* Discussion threads: + * Pitch #1: https://forums.swift.org/t/pitch-low-level-linkage-control-attributes-used-and-section/65877 + * Pitch #2: https://forums.swift.org/t/pitch-2-low-level-linkage-control/69752 + * Pitch #3: TBD + +## Introduction + +This proposal builds on top of [Swift Compile-Time Values](https://TBD) and adds two new attributes into the Swift language: `@section` and `@used`. These allow users to directly control which section of the resulting binary should globals variables be emitted into, and give users the ability to disable DCE (dead code elimination) on those. The goal is to enable systems and embedded programming use cases like runtime discovery of test metadata from multiple modules, and also to serve as a low-level building block for higher-level features (e.g. linker sets, plugins). + +The intention is that these attributes are to be used rarely and only by specific use cases; high-level application code should not need to use them directly and instead should rely on libraries, macros and other abstractions over the low-level attributes. + +The scope of this proposal is limited to compile-time behavior and compile-time control. We expect that full user-facing solutions for features like linker sets, test discovery or plugins will also require runtime implementations to discover and iterate the contents of custom sections, possibly from multiple modules. This proposal makes sure to provide the right building blocks and artifacts in binaries for the runtime components, but doesn’t prescribe the shape of those. However, it is providing a significant step towards generalized and safe high-level mechanisms for those use cases. See the discussion of that in the sections [Runtime discovery of data in custom sections](#runtime-discovery-of-data-in-custom-sections) and [Linker sets, plugins as high-level APIs](#linker-sets-plugins-as-high-level-apis) in Future Directions. + +## Motivation + +**Testing frameworks** need to be able to produce test metadata about user’s types and other declarations (e.g. standalone test entrypoints) in a way that they are discoverable and enumerable at runtime. In dynamic languages like Objective-C, this is typically done at runtime using reflection, by querying the language runtime, and/or walking lists of types or exported symbols: + +```swift +// MyXCTestModule +@objc class TableValidationTests: XCTestCase { + func test1() { ... } +} + +// Testing framework, pseudo-code +let classList = objc_copyClassList(...) +for aClass in classList { + if aClass is XCTestCase { + let methodList = class_copyMethodList(aClass) + ... + } +} +``` + +A similarly dynamic approach was proposed in [SE-0385 (Custom Reflection Metadata)](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0385-custom-reflection-metadata.md), but was rejected because for Swift, a more static approach would be a better fit: If Swift code had the ability to produce custom metadata directly into the resulting binaries in a well-understood way, we would be able to directly access the data at runtime via platform loader’s APIs and also use offline binary inspection tools (such as objdump, objcopy, otool). This would be more efficient and not require the language runtime and thus also be palatable in embedded use cases using Embedded Swift. + +Mainstream operating systems support dynamically loading modules from disk at runtime, and large applications tend to build **plugin systems** as a way to separate the development of subsystems, or to support separate compilation of 3rd party code. Loading a module from disk can be done using standard APIs like `dlopen`, but discovering and calling the interface in the plugin usually requires using unsafe C constructs (dlsym, casting of pointers) and/or querying the language runtime for type information, similarly to the testing enumerating approach mentioned above. A better approach could publish the information about a plugin in a structured way into the binary, and a runtime component could locate this metadata, and provide access to it in a type-safe way. + +This proposal recommends to use sections of the various object file formats as the vehicle for custom metadata produced by Swift code. This approach is a good fit to the above mentioned use cases, but also enables others: + +* “**Linker sets**” are an approach in systems programming that collects data from multiple source files or subsystems using a standard linker behavior of collocating symbols that belong to the same section. In principle, this is simply a generalization of the test enumeration and plugin discovery use cases mentioned above. The primary goal is decentralization of the information, for example, linker sets can be used to describe memory requirements of each subsystem (and a boot step at runtime can process those to figure out how much heap should be made available). +* Emitting custom metadata into binaries can be used to convey **information to the debugger**. The `@DebugDescription` macro generates a “summary string” for LLDB that summarizes the contents of the fields of a type without the need for runtime evaluation, but rather as a composition of the fields that LLDB assembles. To make those summary strings discoverable by LLDB, placing them into a custom section is a clean solution allowing LLDB to consume them in the case where LLDB has access to the binary on disk, or even without that. Embedded programs might need to rely on such a mechanism as the only way to get enhanced data visualization in the debugger, because runtime evaluation from a debugger is commonly not possible at all in firmware. + +```swift +@DebugDescription struct Student { + var name: String + var id: Int + + /* synthesized by the @DebugDescription macro, made discoverable by LLDB */ + let __Student_lldb_summary = ("PupilKit.Student", "${var.id}: ${var.name}") +} +``` + +* More embedded and systems programming use cases often require directly control of section placement as well, for example to adhere to a **startup contract** with platform libraries, SDK’s linker scripts or the hardware itself. Such contract can be pre-existing in the platform and require placing a specific data structure into a specific section. Enabling doing that directly in Swift will provide a more intuitive and safer implementation option, and users of Swift for embedded devices won’t need to reach for C/C++ as a workaround. + +## Proposed Solution + +The proposal is to add two new attributes `@section` and `@used` that will allow annotating global and static variables with directives to place the value into a custom section, and to require no-dead-stripping aka "attribute used". The `@section` attribute relies heavily on the facilities provided by [Swift Compile-Time Values](https://TBD), namely the ability to enforce constantness of an expressions. Using `@section` requires that the initializer expression is a constant expression: + +```swift +// Place entry into a section, mark as "do not dead strip". +// Initializer expression must be a constant expression. +// The global variable is implicitly made statically initialized. +@section("__DATA,mysection") +@used +let myLinkerSetEntry: Int = 42 // ✅ + +// Non-constant or statically non-initializable expressions are disallowed +@section("__DATA,mysection") +let myLinkerSetEntry: Int = Int.random(in: 0 ..< 10) // ❌ error + +// Section-placed globals can be "var", the initializer expression still must be constant +@section("__DATA,mysection") +var mutableVariable: Int = 42 // ✅ + +// Some complex data types are allowed (static strings, functions) +typealias PluginData = (version: Int, name: String, initializer: @convention(c) ()->()) + +@section("__DATA,plugins") +@used +let myPlugin: PluginData = ( + version: 1, + name: "MyPlugin", + initializer: { print("init") } +) +``` + +On top of specifying a custom section name with the `@section` attribute, marking a variable as `@used` is needed to prevent otherwise unused symbols from being removed by the compiler. When using section placement to e.g. implement linker sets, such values are typically going to have no usage at compile time, and at the same time they should not be exposed in public interface of libraries (not be made public), therefore we the need the `@used` attribute. + +A related new attribute, `@constInitialized` is to be added, which can be used to enforce that a global or static variable is statically initialized, without placing it into a custom section. Using attribute `@section` implies `@constInitialized`. + +```swift +// Static initialization can be requested separately +@constInitialized +var fourPages = 4 * 4096 +``` + +Different object file formats (ELF, Mach-O, COFF) have different restrictions and rules on what are valid section names, and cross-platform code will have to use different names for different file formats. To support that, custom section names can be specified as either a string literal, or by referencing a constant global/static declaration that contains the name. The name will be directly set for the symbol in the resulting object file, without any processing. A new `#if objectFileFormat(...)` conditional compilation directive will be provided to support conditionalizing based on the file format: + +```swift +#if objectFileFormat(ELF) +@const let mySectionName = ".mysection" +#elseif objectFileFormat(MachO) +@const let mySectionName = "__DATA,mysection" +#endif + +@section(mySectionName) +var global = ... +``` + +For the ELF file format specifically, the compiler will also emit a “section index” into produced object files, containing an entry about each custom section used in the compilation. This is a solution to an ELF specific problem where the behavior of ELF linkers and loaders means that sections are not easily discoverable at runtime. + +## Detailed design + +### Attributes @section and @used on global and static variables + +Two new attributes are to be added: `@section`, which has a single argument specifying the section name, and a argument-less `@used` attribute. The section name must be either a string literal, or a reference to a constant string declaration. + +```swift +// (1) +@section("__DATA,mysection") +@used +let global = ... // ✅ + +// (2) +@const let mySectionName = "__DATA,mysection" + +@section(mySectionName) +let global = ... // ✅ + +// (3) +@section(Bool.rand() ? "a" : "b") +let global = ... // ❌ +``` + +The section name must not be one of Swift’s runtime reserved sections (e.g. `__swift5_types`, etc.), such sections would be rejected: + +```swift +@section("__TEXT,__swift5_types") +let global = ... // ❌ +``` + +The new attributes (`@section` and `@used`) can be used on variable declarations under these circumstances: + +* the variable must be a global variable or a static member variable (no local variables, no non-static member variables) +* the variable must not be declared inside a generic context (either directly in generic type or nested in a generic type) +* the variable must be a stored property (not be a computed property) +* the variable must not have property observers (didSet, willSet) +* the initial expression assigned to the variable must be a constant expression, and it must be eligible for static initilization + +*Note: These restrictions limit the `@section` and `@used` attributes to only be allowed on variables that are expected to be represented as exactly one statically-initialized global storage symbol (in the linker sense) for the variable’s content. This is generally true for all global and static variables in C and C++, but in Swift global variables might have zero global storage symbols (e.g. a computed property), or need non-trivial storage (e.g. lazily initialized variables with runtime code in the initializer expression).* + +```swift +@section("__DATA,mysection") @used +let global = 42 // ✅ + +@section("__DATA,mysection") @used +var global = 42 // ✅ + +@section("__DATA,mysection") @used +var computed: Int { return 42 } // ❌ ERROR: @section cannot be used on computed properties + +struct MyStruct { + @section("__DATA,mysection") @used + static let staticMemberLet = 42 // ✅ + + @section("__DATA,mysection") @used + static var staticMemberVar = 42 // ✅ + + @section("__DATA,mysection") @used + let member = 42 // ❌ ERROR: @section cannot be used on non-static members + + @section("__DATA,mysection") @used + var member = 42 // ❌ ERROR: @section cannot be used on non-static members +} + +struct MyGenericStruct { + @section("__DATA,mysection") @used + static let staticMember = 42 // ❌ ERROR: @section cannot be used in a generic context + + @section("__DATA,mysection") @used + static var staticMember = 42 // ❌ ERROR: @section cannot be used in a generic context +} +``` + +When allowed, the `@section` attribute on a variable declaration has the following effects: + +1. The variable’s initializer expression is going to be constant folded at compile-time, and assigned as the initial value to the storage symbol for the variable, i.e. the variable will be **statically initialized**. The variable’s value will not be lazily computed at runtime, and it will not use the one-time initialization helper code and token. If that’s not possible, an error is diagnosed. +2. The storage symbol for the variable will be placed into a custom section with the specified name. +3. If applied to a global that is declared as part of top-level executable code (i.e. main.swift), the usual non-top-level-code initialization behavior is applied to the global. I.e. the variable is not sequentially initialized at startup. + +When allowed, the `@used` attribute on a variable declaration has the following effect: + +1. The storage symbol for the variable will be marked as “do not dead-strip”. + +The effects described above are applied to the storage symbols and don’t generally affect optimizations and other transformations in the compiler. For example, the compiler is still allowed to propagate and copy a the constant value to code that uses the value, so there’s no guarantee that a value stored into a global with a custom section will not be propagated and “leak” outside of the section. The `@used` annotation, however, does inform the optimizer that such a variable cannot be removed, even when it doesn’t have any observed users or even if it’s inaccessible due to language rules (e.g. if it’s a private static member on an otherwise empty type). + +### Guaranteed static initialization + +Using attribute `@section` requires the initializer expression of the variable to be a constant expression. The `@const` annotation on the expression is not required (it’s implied). On top of the constant-ness, `@section` on a global or static variable enforces **static initialization** on that variable. The variable can be statically initialized, if the constant expression not only represents a valid compile-time constant, but also is able to be constant-folded into a representation that does not require any runtime initialization (pointer relocations/fixups done automatically by the loader are not considered runtime initialization for this purpose). + +This is a property that `@const` alone does not provide, but it’s necessary because we expect the data to be readable without any runtime mechanisms (i.e. reading raw bytes from the section at runtime, or offline binary inspection). + +```swift +@section("__DATA,mysection") +let a = 42 // ✅ + +@section("__DATA,mysection") +let b = @const 42 // warning: @const is superfluous + +@section("__DATA,mysection") +let sectionPlaced = ...expression... // guaranteed to be statically initialized + +let justConstant = @const ...expression... // not guaranteed to be statically initialized +``` + +*Note: As of this writing, all valid constant values are also eligible to be statically initialized, but we don’t expect that to hold in the future. So it’s important to distinguish between (a) a global variable being initialized with a language-level constant value (`@const`), and (b) a global variable that is guaranteed to be statically initialized. The difference can be subtle, and in some cases immaterial in practice, but future enhancements of constant values in Swift might take advantage of this difference — i.e. not all constant values are going to be statically initializable. Consider the following example: If a future language versions allows dictionary literals to be constant values, such values might not be statically initializable because of randomized hash seeding:* + +```swift +let d1 = @const ["a": 42, "b": 777] // constant, but not statically initializable +let d2 = @const d1.count // statically initializable +``` + +*However, using a statically non-initializable value in an expression does not preclude the outer expression from being statically initialized either. In this example, `d1` would not be allowed to be placed into a custom section because it’s not statically initializable. But `d2` could still potentially be statically initializable (even though the definition of `d2` uses a sub-expression that is not statically initializable), as it’s simply an integer.* + +As described in [Swift Compile-Time Values](https://TBD), values of function types are eligible for being compile-time evaluable. Their concrete pointer value is not fully known until link-time or program load-time (depending on type of linking, ASLR, PAC, etc.). For the purposes of guaranteed static initialization, function values are statically initialized into a function pointer. This pointer is still subject to normal linking and loading resolutions and fixups. + +```swift +func foo() { ... } + +@section("__DATA,mysection") +let a = (42, foo) // "foo" is statically initialized into a + // linkable/relocatable pointer +``` + +### Attribute `@constInitialized` + +Static initialization of a global can be useful on its own, without placing data into a custom section. For that, a new attribute `@constInitialized` can be used. This attribute can be only used on variable declarations under the same conditions that `@section` and `@used` require (e.g. only on globals and statics, not in generic contexts, etc.) + +```swift +@constInitialized +var fourPages = 4 * 4096 // ✅ + +struct S { + @constInitialized + var fourPages = 4 * 4096 // ❌ +} +``` + +The effect of this attribute is the same as of the `@section` attribute (static initialization, normal initalization behavior if top-level code) except the symbol is not actually placed into any custom sections. + +### Cross-platform object file format support + +The custom section name specified in the `@section` attribute is not validated by the compiler, instead it’s passed directly as a string to the linker. Different platforms supported by Swift are using different object and binary file formats (Linux uses ELF, Darwin uses Mach-O, Windows uses COFF), and that implies different restrictions and rules on what are valid section names. Because of that, a multi-platform library code is expected to use `#if os(...)` to use different section names for different platforms. Because of that, it’s expected that the attributes are to be typically only used indirectly via macros that hide the low-level nature of sections and object file formats from higher-level code developers: + +```swift +// Example of a potential project-specific "@RegisterPlugin" macro: +@RegisterPlugin +let plugin = ... + +// The macro expands to: +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@section("__DATA_CONST,plugins") +#elseif os(Linux) +@section("plugins") +#elseif os(Windows) +@section(".plugins") +#endif +let plugin = ... +``` + +See [Structured section specifiers](#structured-section-specifiers) below for more rationale. + +In some cases, it’s not possible to differentiate on the OS to support multiple object file formats, for example when using Embedded Swift to target baremetal systems without any OS. For that, a new `#if objectFileFormat(...)` conditional compilation directive will be provided. The allowed values in this directive will match the set of supported object file formats by the Swift compiler (and expand as needed in the future). Currently, they exact values will be (case sensitive): + +* COFF +* ELF +* MachO +* Wasm + +```swift +#if objectFileFormat(MachO) +@section("__DATA_CONST,mysection") +#elseif objectFileFormat(ELF) +@section(".mysection") +#endif +let value = ... +``` + +### ELF section index + +The goal of placing metadata into custom section is to make them discoverable both via offline inspection (e.g. objdump or otool) and at runtime. The facilities for that are dependent on the type of linking (static vs dynamic), and platform’s linker and loader: + +* For **static linking**, the bounds of a section can be statically determined by the linker and on all supported platforms and their file formats (COFF, ELF, MachO, Wasm), the linker-provided “**encapsulation symbols**” can be used to retrieve those bounds. + * In ELF and Wasm formats, these symbols are `__start_
    ` / `__stop_
    `. + * In Mach-O, these symbols are `section$start$$` / `section$end$$`. + * In COFF, these symbols need to be manually constructed by using “grouped sections” (section name is suffixed with a $ + string) which are automatically lexicographically ordered by the linker. For example, by manually placing a start symbol into `.section$A` , end symbol into `.section$C` and all actual section entries into `.section$B`, the two helper symbols’s addresses effectively describe the bounds of the section. +* For **dynamic linking**, the above mentioned encapsulation symbols are available too, but they always only describe the bounds of the section in the current module. Retrieving section content process-wide means collecting metadata from multiple images at runtime, which requires further assistance or support from the loader. + * In Mach-O (Darwin OS's), image headers are present in the address space, and they include section bounds information. The loader provides straightforward image iteration APIs (`_dyld_get_image_header`), as well as image load callbacks (`_dyld_register_func_for_add_image`), and an API to lookup section bounds by name from a particular image (getsectiondata). + * In COFF (Windows), image headers are present in the address space, and they include section bounds information. `Module32FirstW`/`Module32NextW` can be used to enumerate images, and structures such as `IMAGE_DOS_HEADER`, `IMAGE_NT_HEADERS`, `IMAGE_FILE_HEADER`, and `IMAGE_SECTION_HEADER` can be used to walk a module and find its section bounds. + * In Wasm, dynamic linking is work in progress and not generally available yet. + * In ELF, however, section bounds are not guaranteed to be present in the address space at runtime, and in practice they are typically not present. This creates a challenge for retrieving section data in this configuration (ELF + multiple modules with dynamic linking) at runtime. + +To solve this problem for the ELF object file format, the Swift compiler is going to emit a “**section index**” into every compilation that uses any symbols placed into a custom section. The index will be emitted only when producing ELF files, and consists of entries added into its own separate well-named section called `swift5_sections`. Each entry will have the following structure: + +```c +struct SectionIndexEntry { + const char *name; + const void *start; // effectively equal to __start_ + const void *stop; // effectively equal to __stop_ +}; +``` + +The section index will describe the bounds of all custom sections used in Swift code. When compiling into a single object file (e.g. in WMO mode without -num-threads), there will be only a single entry per distinct section name, but in compilation modes that produce multiple object files for a single module, there may be multiple entries for the same section. The entries are going to be “linkonce_odr”, i.e. duplicate entries will be collapsed at link time, so in a linked module, only one entry per section will remain. + +This way, runtime code present in the same module, for example SwiftRT-ELF helper code (swiftrt.o) which is currently being silently linked in to all modules, can walk over the section index using the encapsulation symbols, and register the section bounds in a globally maintained data structure in the Swift runtime. Implementation of that and exposing such a facility in an actual API from the Swift runtime is left as a future direction. + +## Source compatibility + +This proposal is purely additive (adding new attributes), without impact on source compatibility. + +## Effect on ABI stability + +This change does not impact ABI stability for existing code. + +Adding, removing, or changing the `@section` attribute on variables should generally be viewed as an ABI breaking change, section placement can affect linking behavior of that symbol. In some cases, it is possible to make careful non-ABI-breaking changes via the `@section` attribute. + +Adding `@used` does not affect ABI stability. Removing `@used` can be viewed as an ABI breaking change, however not in the traditional sense: The effect of `@used` only exists on symbols that would normally not be exported (e.g. private symbols), which shouldn’t be part of a ABI in the first place. However, dynamic lookups of such symbols are still possible, and if the behavior of those is considered ABI, then removing `@used` can be ABI breaking. + +## Effect on API resilience + +This change does not impact API resilience for existing code. + +Adding, removing, or changing the `@section` attribute on variables does not affect API resilience. + +Adding or removing `@used` does not affect API resilience. + +## Future Directions + +### Section placement for functions + +This proposal only allows data placement into custom sections, however, placing code into custom sections is a relatively useful and common approach in systems and embedded programming. In the future, the `@section` and `@used` attributes could be extended to apply to function declarations, and possibly other language constructs that generate executable code (e.g. closures). A prominent use case is firmware entry points and booting schemes, which often require startup code to be in a predefined section: + +```swift +// code for the function is placed into the custom section +@section("__TEXT,boot") +func firmwareBootEntrypoint() { ... } +``` + +This will require some design decisions to be made around when should that be allowed, whether the attribute should be automatically inherited, and what exact behavior should we expect from the compiler around thunks, compiler-generated helper functions, getters and setters, etc. + +### Runtime discovery of data in custom sections + +As described in [ELF section index](#elf-section-index), accessing records in a custom section at runtime is heavily dependent on the object file format (ELF, Mach-O, Wasm, COFF), type of linking (static vs dynamic) and available APIs from the operating system. For a single configuration, users can directly use an appropriate method of accessing the section data, and e.g. in embedded firmwares this might be completely fine as projects are commonly avoiding any attempts to be multi-platform or portable. + +However, for multi-platform libraries and general purpose packages, supporting the full matrix of combinations would be very impractical. Because of that, it’s expected that a unified API for accessing the bounds and contents of a section (across multiple modules in presence of dynamic linking) is provided either as part of the Swift runtime, the standard library, or as a portable package. This API would likely still be a relatively low-level API, providing access to the raw bytes of sections across multiple loaded modules, but it would provide an shared abstraction across platforms, file formats, and linking types. + +### Linker sets, plugins as high-level APIs + +This proposal only builds the compile-time half of a user-facing “linker set” mechanism (placing structured data into sections). To access and enumerate the data at runtime, one can imagine a direct, still relatively low-level API like this: + +```swift +func enumerateLinkerSet(fromSectionNamed: String) -> Sequence { + // extract section data, assuming the raw data in the section are records of "T" + // probably built on top of a cross-platform section access API mentioned in previous section +} +``` + +But a solution based on macros could achieve a higher-level abstraction for the entire “linker set” mechanism: + +```swift +@DefineLinkerSet("name", type: Int) // other macros understand that linker set "name" + // has entries of type Int + +@LinkerSetEntry("name") let entry1: Int = 42 // ok +@LinkerSetEntry("name") let entry2: Float = 7.7 // error + +for entry in #enumerateLinkerSet("name") { + print(entry) +} +``` + +Similarly, a plugin registration and discovery mechanism based on macros could provide full type safety and hide the low-level aspects completely: + +```swift +// In PluginModule: +@PluginRecord(protocol: PluginProtocol, type: MyPluginType) +let plugin = PluginData(name: "myPlugin", version: 1, initialization: { ... }) + +// In MainModule: +... load available plugins via dlopen ... +for plugin in Plugin.enumerateLoadedPlugins(for: PluginProtocol.self) { + print(plugin.name) + let t = plugin.instantiateType() + ... +} +``` + +### Access to a stable address + +Generally, Swift values and variables do not have a stable address, and converting an inout reference to a UnsafePointer does not guarantee a stable address even on a global variable. However, statically initialized globals/static (either via the `@section` attribute, or via `@constInitialized`) do fundamentally have a stable address because they have an exact location in the binary on disk and in the module’s image at runtime. It’d be useful to provide direct access to that, in a way that adds the missing stable-address guarantees compared to inout-to-pointer conversions, and also allow fetching an address of a `let` variable (inout references only work on `var`): + +```swift +@constInitialized let x = 42 + +let address: UnsafePointer = #address(x) // would only work on statically initialized + // globals/statics +``` + +## Alternatives Considered + +### Requiring explicitly spelled out `@const` when using `@section` + +`@section` annotated globals/statics require their initializer expressions to be constant expressions, but the expression does not have to be marked as `@const` manually, it’s implied instead. An alternative of requiring the `@const` was considered: + +```swift +@section(...) let global: Int = @const 42 +``` + +Because `@const` does not affect parsing or type resolution of the expression, it’s not helpful to the compiler, and it doesn’t seem to improve readability for users either: If the expression is a constant expression or not statically initializable, it will be rejected from compilation with a clear explanation. Adding a `@const` does not convey any new information. + +### Structured section specifiers + +In Mach-O, custom section names are written as a pair of segment (e.g. `__DATA`) + section (e.g. `mysection`). Structured section names with separate segment and section names, `@section(segment: "...", section: "...")` were considered instead, however this pattern does not generalize across object file formats, and is Mach-O specific (ELF and PE/COFF don’t have segments). + +Because different object file formats impose different restrictions on custom section names (length, “.” prefix), a shorthand syntax to specify different section names for different object file formats was considered: `@section(ELF: “...”, MachO: “...”, COFF: “...”)`. This, however, has drawbacks of repeating the file format in cases where the code is only ever targeting a single format (common for example for embedded firmwares on ELF). The benefits of a shorthand syntax is marginal, given that we don’t expect normal application code to used the `@section` attribute directly but instead rely on macros or other higher-level API. + +The alternative of using conditional compilation is what is expected to be used for those cases instead. + +### Umbrella attribute for linkage properties + +Instead of separate `@section` and `@used` attributes, a unified attribute with parameters to control individual linkage properties was considered, spelled for example `@linkage(section: ..., used)`. Further linkage control features would be added into this umbrella attribute. + +This, however, adds challenges on composability — one umbrella attribute would need to allow multiple occurrences and the design would need rules for merging of individual properties from multiple attributes. Separate standalone attributes compose trivially, and also they play nicely with the existing `#if hasAttribute(...)` conditional compilation mechanism. There is currently no mechanism for conditional compilation based on whether a sub-feature of a umbrella attribute is available in the compiler. + +Given the above, and also given that controlling symbol and linker level properties is not something that we expect normal application code to do directly, it’s more appropriate to keep the attribute system simple, and have individual orthogonal composable attributes. + +### `@section` implying `@used` + +In a lot of the list code snippets in this proposal, both `@section` and `@used` were used together, and so it may seem that it’s not necessary for those to be two separate attributes. However: + +* `@section` and `@used` represent separate concepts and all combinations of them can be useful. An example of using `@section` without `@used` is to place for example a large data table from a library into its own section for binary size accounting reasons (so that it shows up separately in per-section binary size listings), but where we’d still expect the data table to be dead-code removed if not used. +* It’s already common to have those attributes as separate options in existing popular systems programming languages (C, C++, Rust). From bc15c64a72b7c40f9949158d0b751299d86fb007 Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Fri, 24 Jan 2025 09:41:56 -0800 Subject: [PATCH 4017/4563] Update links --- proposals/0nnn-section-control.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0nnn-section-control.md b/proposals/0nnn-section-control.md index 985be91e6f..3f0d9e0aca 100644 --- a/proposals/0nnn-section-control.md +++ b/proposals/0nnn-section-control.md @@ -6,11 +6,11 @@ * Discussion threads: * Pitch #1: https://forums.swift.org/t/pitch-low-level-linkage-control-attributes-used-and-section/65877 * Pitch #2: https://forums.swift.org/t/pitch-2-low-level-linkage-control/69752 - * Pitch #3: TBD + * Pitch #3: https://forums.swift.org/t/pitch-3-section-placement-control/77435 ## Introduction -This proposal builds on top of [Swift Compile-Time Values](https://TBD) and adds two new attributes into the Swift language: `@section` and `@used`. These allow users to directly control which section of the resulting binary should globals variables be emitted into, and give users the ability to disable DCE (dead code elimination) on those. The goal is to enable systems and embedded programming use cases like runtime discovery of test metadata from multiple modules, and also to serve as a low-level building block for higher-level features (e.g. linker sets, plugins). +This proposal builds on top of [Swift Compile-Time Values](https://github.com/artemcm/swift-evolution/blob/const-values/proposals/0nnn-const-values.md) and adds two new attributes into the Swift language: `@section` and `@used`. These allow users to directly control which section of the resulting binary should globals variables be emitted into, and give users the ability to disable DCE (dead code elimination) on those. The goal is to enable systems and embedded programming use cases like runtime discovery of test metadata from multiple modules, and also to serve as a low-level building block for higher-level features (e.g. linker sets, plugins). The intention is that these attributes are to be used rarely and only by specific use cases; high-level application code should not need to use them directly and instead should rely on libraries, macros and other abstractions over the low-level attributes. @@ -59,7 +59,7 @@ This proposal recommends to use sections of the various object file formats as t ## Proposed Solution -The proposal is to add two new attributes `@section` and `@used` that will allow annotating global and static variables with directives to place the value into a custom section, and to require no-dead-stripping aka "attribute used". The `@section` attribute relies heavily on the facilities provided by [Swift Compile-Time Values](https://TBD), namely the ability to enforce constantness of an expressions. Using `@section` requires that the initializer expression is a constant expression: +The proposal is to add two new attributes `@section` and `@used` that will allow annotating global and static variables with directives to place the value into a custom section, and to require no-dead-stripping aka "attribute used". The `@section` attribute relies heavily on the facilities provided by [Swift Compile-Time Values](https://github.com/artemcm/swift-evolution/blob/const-values/proposals/0nnn-const-values.md), namely the ability to enforce constantness of an expressions. Using `@section` requires that the initializer expression is a constant expression: ```swift // Place entry into a section, mark as "do not dead strip". @@ -227,7 +227,7 @@ let d2 = @const d1.count // statically initializable *However, using a statically non-initializable value in an expression does not preclude the outer expression from being statically initialized either. In this example, `d1` would not be allowed to be placed into a custom section because it’s not statically initializable. But `d2` could still potentially be statically initializable (even though the definition of `d2` uses a sub-expression that is not statically initializable), as it’s simply an integer.* -As described in [Swift Compile-Time Values](https://TBD), values of function types are eligible for being compile-time evaluable. Their concrete pointer value is not fully known until link-time or program load-time (depending on type of linking, ASLR, PAC, etc.). For the purposes of guaranteed static initialization, function values are statically initialized into a function pointer. This pointer is still subject to normal linking and loading resolutions and fixups. +As described in [Swift Compile-Time Values](https://github.com/artemcm/swift-evolution/blob/const-values/proposals/0nnn-const-values.md), values of function types are eligible for being compile-time evaluable. Their concrete pointer value is not fully known until link-time or program load-time (depending on type of linking, ASLR, PAC, etc.). For the purposes of guaranteed static initialization, function values are statically initialized into a function pointer. This pointer is still subject to normal linking and loading resolutions and fixups. ```swift func foo() { ... } From e5eed166c878566073f268536838bcba9aeaa635 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 28 Jan 2025 05:10:26 -0800 Subject: [PATCH 4018/4563] EnumeratedSequence collection conformance (#2634) * EnumeratedSequence collection conformance * Update the ABI stability section of enumerated * Update and rename nnnn-enumerated-collection.md to 0459-enumerated-collection.md Kick off review --------- Co-authored-by: Ben Cohen --- proposals/0459-enumerated-collection.md | 199 ++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 proposals/0459-enumerated-collection.md diff --git a/proposals/0459-enumerated-collection.md b/proposals/0459-enumerated-collection.md new file mode 100644 index 0000000000..4018b8b7b9 --- /dev/null +++ b/proposals/0459-enumerated-collection.md @@ -0,0 +1,199 @@ +# Add `Collection` conformances for `enumerated()` + +* Previous proposal: [SE-0312](0312-indexed-and-enumerated-zip-collections.md) +* Author: [Alejandro Alonso](https://github.com/Azoy) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **Active Review (28 Jan - February 7 2025)** +* Implementation: [apple/swift#78092](https://github.com/swiftlang/swift/pull/78092) + +## Introduction + +This proposal aims to fix the lack of `Collection` conformance of the sequence returned by `enumerated()`, preventing it from being used in a context that requires a `Collection`. + +Swift-evolution thread: [Pitch](https://forums.swift.org/t/pitch-add-indexed-and-collection-conformances-for-enumerated-and-zip/47288) + +## Motivation + +Currently, `EnumeratedSequence` type conforms to `Sequence`, but not to any of the collection protocols. Adding these conformances was impossible before [SE-0234 Remove `Sequence.SubSequence`](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0234-remove-sequence-subsequence.md), and would have been an ABI breaking change before the language allowed `@available` annotations on protocol conformances ([PR](https://github.com/apple/swift/pull/34651)). Now we can add them! + +Conformance to the collection protocols can be beneficial in a variety of ways, for example: +* `(1000..<2000).enumerated().dropFirst(500)` becomes a constant time operation. +* `"abc".enumerated().reversed()` will return a `ReversedCollection` rather than allocating a new array. +* SwiftUI’s `List` and `ForEach` views will be able to directly take an enumerated collection as their data. + +## Detailed design + +Conditionally conform `EnumeratedSequence` to `Collection`, `BidirectionalCollection`, `RandomAccessCollection`. + +```swift +@available(SwiftStdlib 6.1, *) +extension EnumeratedSequence: Collection where Base: Collection { + // ... +} + +@available(SwiftStdlib 6.1, *) +extension EnumeratedSequence: BidirectionalCollection + where Base: BidirectionalCollection +{ + // ... +} + +@available(SwiftStdlib 6.1, *) +extension EnumeratedSequence: RandomAccessCollection + where Base: RandomAccessCollection {} +``` + +## Source compatibility + +All protocol conformances of an existing type to an existing protocol are potentially source breaking because users could have added the exact same conformances themselves. However, given that `EnumeratedSequence` do not expose their underlying sequences, there is no reasonable way anyone could have conformed to `Collection` themselves. + +## Effect on ABI stability + +These conformances are additive to the ABI, but will affect runtime casting mechanisms like `is` and `as`. On ABI stable platforms, the result of these operations will depend on the OS version of said ABI stable platforms. Similarly, APIs like `underestimatedCount` may return a different result depending on if the OS has these conformances or not. + +## Alternatives considered + +#### Add `LazyCollectionProtocol` conformance for `EnumeratedSequence`. + +Adding `LazySequenceProtocol` conformance for `EnumeratedSequence` is a breaking change for code that relies on the `enumerated()` method currently not propagating `LazySequenceProtocol` conformance in a lazy chain: + +```swift +extension Sequence { + func everyOther_v1() -> [Element] { + let x = self.lazy + .enumerated() + .filter { $0.offset.isMultiple(of: 2) } + .map(\.element) + + // error: Cannot convert return expression of type 'LazyMapSequence<...>' to return type '[Self.Element]' + return x + } + + func everyOther_v2() -> [Element] { + // will keep working, the eager overload of `map` is picked + return self.lazy + .enumerated() + .filter { $0.offset.isMultiple(of: 2) } + .map(\.element) + } +} +``` + +We chose to keep this proposal very small to prevent any such potential headaches of source breaks. + +#### Keep `EnumeratedSequence` the way it is and add an `enumerated()` overload to `Collection` that returns a `Zip2Sequence, Self>`. + +This is tempting because `enumerated()` is little more than `zip(0..., self)`, but this would cause an unacceptable amount of source breakage due to the lack of `offset` and `element` tuple labels that `EnumeratedSequence` provides. + +#### Only conform `EnumeratedSequence` to `BidirectionalCollection` when the base collection conforms to `RandomAccessCollection` rather than `BidirectionalCollection`. + +Here’s what the `Collection` conformance could look like: + +```swift +extension EnumeratedSequence: Collection where Base: Collection { + struct Index { + let base: Base.Index + let offset: Int + } + var startIndex: Index { + Index(base: _base.startIndex, offset: 0) + } + var endIndex: Index { + Index(base: _base.endIndex, offset: 0) + } + func index(after index: Index) -> Index { + Index(base: _base.index(after: index.base), offset: index.offset + 1) + } + subscript(index: Index) -> (offset: Int, element: Base.Element) { + (index.offset, _base[index.base]) + } +} + +extension EnumeratedSequence.Index: Comparable { + static func == (lhs: Self, rhs: Self) -> Bool { + return lhs.base == rhs.base + } + static func < (lhs: Self, rhs: Self) -> Bool { + return lhs.base < rhs.base + } +} +``` + +Here’s what the `Bidirectional` conformance could look like. The question is: should `Base` be required to conform to `BidirectionalCollection` or `RandomAccessCollection`? + +```swift +extension EnumeratedSequence: BidirectionalCollection where Base: ??? { + func index(before index: Index) -> Index { + let currentOffset = index.base == _base.endIndex ? _base.count : index.offset + return Index(base: _base.index(before: index.base), offset: currentOffset - 1) + } +} +``` + +Notice that calling `index(before:)` with the end index requires computing the `count` of the base collection. This is an O(1) operation if the base collection is `RandomAccessCollection`, but O(n) if it's `BidirectionalCollection`. + +##### Option 1: `where Base: BidirectionalCollection` + +A direct consequence of `index(before:)` being O(n) when passed the end index is that some operations like `last` are also O(n): + +```swift +extension BidirectionalCollection { + var last: Element? { + isEmpty ? nil : self[index(before: endIndex)] + } +} + +// A bidirectional collection that is not random-access. +let evenNumbers = (0 ... 1_000_000).lazy.filter { $0.isMultiple(of: 2) } +let enumerated = evenNumbers.enumerated() + +// This is still O(1), ... +let endIndex = enumerated.endIndex + +// ...but this is O(n). +let lastElement = enumerated.last! +print(lastElement) // (offset: 500000, element: 1000000) +``` + +However, since this performance pitfall only applies to the end index, iterating over a reversed enumerated collection stays O(n): + +```swift +// A bidirectional collection that is not random-access. +let evenNumbers = (0 ... 1_000_000).lazy.filter { $0.isMultiple(of: 2) } + +// Reaching the last element is O(n), and reaching every other element is another combined O(n). +for (offset, element) in evenNumbers.enumerated().reversed() { + // ... +} +``` + +In other words, this could make some operations unexpectedly O(n), but it’s not likely to make operations unexpectedly O(n²). + +##### Option 2: `where Base: RandomAccessCollection` + +If `EnumeratedSequence`’s conditional conformance to `BidirectionalCollection` is restricted to when `Base: RandomAccessCollection`, then operations like `last` and `last(where:)` will only be available when they’re guaranteed to be O(1): + +```swift +// A bidirectional collection that is not random-access. +let str = "Hello" + +let lastElement = str.enumerated().last! // error: value of type 'EnumeratedSequence' has no member 'last' +``` + +That said, some algorithms that can benefit from bidirectionality such as `reversed()` and `suffix(_:)` are also available on regular collections, but with a less efficient implementation. That means that the code would still compile if the enumerated sequence is not bidirectional, it would just perform worse — the most general version of `reversed()` on `Sequence` allocates an array and adds every element to that array before reversing it: + +```swift +// A bidirectional collection that is not random-access. +let str = "Hello" + +// This no longer conforms to `BidirectionalCollection`. +let enumerated = str.enumerated() + +// As a result, this now returns a `[(offset: Int, element: Character)]` instead +// of a more efficient `ReversedCollection>`. +let reversedElements = enumerated.reversed() +``` + +The base collection needs to be traversed twice either way, but the defensive approach of giving the `BidirectionalCollection` conformance a stricter bound ultimately results in an extra allocation. + +Taking all of this into account, we've gone with option 1 for the sake of giving collections access to more algorithms and more efficient overloads of some algorithms. Conforming this collection to `BidirectionalCollection` when the base collection conforms to the same protocol is less surprising. We don’t think the possible performance pitfalls pose a large enough risk in practice to negate these benefits. From d5c0878adfdbab4c94b4bee0ab866299d740df45 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 28 Jan 2025 08:47:52 -0500 Subject: [PATCH 4019/4563] Accept SE-0455. --- proposals/0455-swiftpm-testable-build-setting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0455-swiftpm-testable-build-setting.md b/proposals/0455-swiftpm-testable-build-setting.md index 1310baec79..186eb31892 100644 --- a/proposals/0455-swiftpm-testable-build-setting.md +++ b/proposals/0455-swiftpm-testable-build-setting.md @@ -3,9 +3,9 @@ * Proposal: [SE-0455](0455-swiftpm-testable-build-setting.md) * Authors: [Jake Petroules](https://github.com/jakepetroules) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Active review (January 9–23, 2025)** +* Status: **Accepted** * Implementation: [swiftlang/swift-package-manager#8004](https://github.com/swiftlang/swift-package-manager/pull/8004) -* Review: ([pitch](https://forums.swift.org/t/pitch-swiftpm-testable-build-setting/75084)) ([review](https://forums.swift.org/t/se-0455-swiftpm-testable-build-setting/77100)) +* Review: ([pitch](https://forums.swift.org/t/pitch-swiftpm-testable-build-setting/75084)) ([review](https://forums.swift.org/t/se-0455-swiftpm-testable-build-setting/77100)) ([acceptance](https://forums.swift.org/t/accepted-se-0455-swiftpm-testable-build-setting/77510)) ## Introduction From 78c8b36453d6c705a62f0e3a3b402c0e17e9cc8e Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 28 Jan 2025 16:07:03 -0800 Subject: [PATCH 4020/4563] [SE-0456] wording tweak (#2672) --- proposals/0456-stdlib-span-properties.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0456-stdlib-span-properties.md b/proposals/0456-stdlib-span-properties.md index b8010eba97..fb2626ed0c 100644 --- a/proposals/0456-stdlib-span-properties.md +++ b/proposals/0456-stdlib-span-properties.md @@ -184,7 +184,7 @@ All of these unsafe conversions return a value whose lifetime is dependent on th - The underlying memory remains initialized. - The underlying memory is not mutated. -Failure to keep these invariants results in undefined behaviour. +Failure to maintain these invariants results in undefined behaviour. #### Extensions to `Foundation.Data` From cde81e1fe9504898b7b22cb5200f7cb620f4438e Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 29 Jan 2025 14:11:53 +0000 Subject: [PATCH 4021/4563] [SE-0452] "Integer Generic Parameters" is accepted (#2670) --- proposals/0452-integer-generic-parameters.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0452-integer-generic-parameters.md b/proposals/0452-integer-generic-parameters.md index e8e2897a40..faa05bb4d7 100644 --- a/proposals/0452-integer-generic-parameters.md +++ b/proposals/0452-integer-generic-parameters.md @@ -3,9 +3,9 @@ * Proposal: [SE-0452](0452-integer-generic-parameters.md) * Authors: [Alejandro Alonso](https://github.com/Azoy), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active review (Janyuary 6-14 2024)** -* Implementation: [apple/swift#75518](https://github.com/apple/swift/pull/75518) -* Review: ([pitch](https://forums.swift.org/t/integer-generic-parameters/74181)) ([first review](https://forums.swift.org/t/se-0452-integer-generic-parameters/75844)) ([second review](https://forums.swift.org/t/second-review-se-0452-integer-generic-parameters/77043)) +* Status: **Accepted** +* Implementation: [swiftlang/swift#75518](https://github.com/swiftlang/swift/pull/75518), [swiftlang/swift#78248](https://github.com/swiftlang/swift/pull/78248) +* Review: ([pitch](https://forums.swift.org/t/integer-generic-parameters/74181)) ([first review](https://forums.swift.org/t/se-0452-integer-generic-parameters/75844)) ([second review](https://forums.swift.org/t/second-review-se-0452-integer-generic-parameters/77043)) ([acceptance](https://forums.swift.org/t/accepted-se-0452-integer-generic-parameters/77507)) ## Introduction From c9c4b751d038103db21b1d654265cdace60fda27 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Wed, 29 Jan 2025 14:25:18 +0000 Subject: [PATCH 4022/4563] [DRAFT] Introduce @specialize attribute (#2638) * Introduce @specialize attribute * Repond to PR feedback. * fix typos * Feedback from initial pitch * @specialize -> @specialized * Clarify "internal only" as about symbols, not feature * Update and rename NNNN-specialize.md to 0460-specialized.md --------- Co-authored-by: Stephen Canon --- proposals/0460-specialized.md | 264 ++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 proposals/0460-specialized.md diff --git a/proposals/0460-specialized.md b/proposals/0460-specialized.md new file mode 100644 index 0000000000..0c4e7fa0b4 --- /dev/null +++ b/proposals/0460-specialized.md @@ -0,0 +1,264 @@ +# Explicit Specialization + +* Proposal: [SE-0460](0460-specialized.md) +* Authors: [Ben Cohen](https://github.com/airspeedswift) +* Review Manager: [Steve Canon](https://github.com/stephentyrone) +* Status: **Active Review (January 29 ... February 11, 2025)** +* Implementation: Available in nightly toolchains using the underscored `@_specialize` +* Discussion: ([pitch](https://forums.swift.org/t/pitch-explicit-specialization/76967)) + +## Introduction + +The Swift compiler has the ability to "specialize" a generic function at compile time. This specialization creates a custom implementation of the function, where the generic placeholders are substituted with specific types. This can unlock optimizations of that specialized function that can be dramatically faster than the unspecialized version in some circumstances. The compiler can generate this specialized version and call it when it can see at the call site with what concrete types a function is being called, and the body of the function to specialize. + +In some cases, though, this information is obscured from the compiler. This proposal introduces a new attribute, `@specialized`, which allows the author of a generic function to generate pre-specialized versions of that function for specific types. When the unspecialized version of the function is called with one of those types, the compiler will generate code that will re-dispatch to those prespecialized versions if available. + +## Motivation + +Consider the following generic function that sums an array of any binary integer type: + +```swift +extension Sequence where Element: BinaryInteger { + func sum() -> Double { + reduce(0) { $0 + Double($1) } + } +} +``` + +If you call this function directly on an array of a specific integer type (e.g. `Array`): + +``` +let arrayOfInt: [Int] = // ... +let result = arrayOfInt.sum() +``` + +in optimized builds, the Swift compiler will generate a specialized version of `sum` for that type. + +If you inspect the binary, you will see this specialized version under a symbol `_$sST3sumSz7ElementRpzrlEAASdyFSaySiG_Tg5`, which demangles to `generic specialization <[Swift.Int]> of (extension in example):Swift.Sequence< where A.Element: Swift.BinaryInteger>.sum() -> Swift.Double`, alongside the unspecialized version. + +This specialized version of `sum` will be optimized specifically for `Array`. It can move a pointer directly over the the array's buffer, loading the elements into registers directly from memory. On modern hardware, it can make use of dedicated instructions to convert the integers to floating point. It can do this because it can see the implementation of `sum`, and can see the exact types on which it is being called. What exact assembly instructions are generated will differ significantly between e.g. `Array.sum` and `Array.sum`. + +Here is that `Array`-specialized code when compiled to x86-64 with `swiftc -Osize`: + +```assembly +_$sST3sumSz7ElementRpzrlEAASdyFSaySiG_Tg5: + mov rax, qword ptr [rdi + 16] + xorpd xmm0, xmm0 + test rax, rax + je .LBB1_3 + xor ecx, ecx +.LBB1_2: + cvtsi2sd xmm1, qword ptr [rdi + 8*rcx + 32] + inc rcx + addsd xmm0, xmm1 + cmp rax, rcx + jne .LBB1_2 +.LBB1_3: + ret +``` + +Now consider some code that places an optimization barrier between the call site and the concrete type: + +```swift +protocol Summable: Sequence where Element: BinaryInteger { } +extension Array: Summable where Element: BinaryInteger { } + +var summable: any Summable + +// later, when summable has been populated with an array of some kind of integer +let result = summable.sum() +``` + +The compiler now has no way of knowing what type `Summable` is at the call site – neither the sequence type, nor the element type. So its only option is to execute the fully unspecialized code. Instead of advancing a pointer over a buffer and loading values directly from memory, it must iterate the sequence by first calling `makeIterator()` and then calling `next()`, unwrapping optional values until it reaches `nil`. And instead of using a single instruction to convert an `Int` to a `Double`, it must call the generic initializer on `Double` that uses methods on the `BinaryInteger` protocol to convert the value into floating point representation. Unsurprisingly, this code path will be about 2 orders of magnitude slower than the specialized version. This is true even if the actual type being summed ends up being `[Int]` at runtime. + +A similar situation would occur when `sum` is a generic function in a binary framework which has not provided an `@inlinable` implementation. While the Swift compiler might know the types at the call site, it has no ability to generate a specialized version because it cannot see the implementation in order to generate it. So it must call the unspecialized version of `sum`. + +The best way to avoid this situation is just to avoid type erasure in the first place. This is why concrete types and generics should be preferred whenever practical over existential types for performance-sensitive code. But sometimes type erasure, particularly for heterogenous storage, is necessary. + +Similarly, ABI-stable binary framework authors do not always want to expose their implementations to the caller, as this tends to generate a very large and complex ABI surface that cannot be easily changed later. It also prevents upgrades of binary implementations without recompiling the caller, allowing for e.g. an operating system to fix bugs or security vulnerabilities without an app needing to be recompiled. + +## Proposed solution + +A new attribute, `@specialized`, will allow the author of a function to cause the compiler to generate specializations of that function. In the body of the unspecialized version, the types are first checked to see if they are of one of the specialized types. If they are, the specialized version will be called. + +So in our example above: + +```swift +extension Sequence where Element: BinaryInteger { + @specialized(where Self == [Int]) + func sum() -> Double { + reduce(0) { $0 + Double($1) } + } +} +``` + +A specialized version of `[Int].sum` will be generated in the same way as if it had been specialized for a callsite. And inside the unspecialized generic code, the additional check-and-redispatch logic will be inserted at the start of the function. + +Doing this restores the performance of the specialized version of `sum` (less a check and branch of the calling type) even when using the existential `any Summable` type. + +## Detailed design + +The `@specialized` attribute can be placed on any generic function. It takes an argument with the same syntax as a `where` clause for a generic signature. + +Multiple specializations can be listed: + +```swift +extension Sequence where Element: BinaryInteger { + @specialized(where Self == [Int]) + @specialized(where Self == [UInt32]) + @specialized(where Self == [Int8]) + func sum() -> Double { + reduce(0) { $0 + Double($1) } + } +} +``` + +Within the unspecialized function, redispatch will be based on the _exact_ type. That is, in pseudocode: + +```swift +extension Sequence where Element: BinaryInteger { + @specialized(where Self == [Int]) + @specialized(where Self == [Int8]) + func sum() -> Double { + if Self.self == [Int].self { + + } else if Self.self == [Int8].self { + + } else { + reduce(0) { $0 + Double($1) } + } + } +} +``` + +(Note that this is just for illustrative purposes, the actual dispatch mechanism to the specific specialization may differ and change over time) + +No attempt to implicitly convert the type is made (so for example, a specialization for `Int?` would not be executed when called on `Int`), nor is a specialization for a superclass executed when called with the generic type of the subclass. + +These specializations are the same as the ones generated by the caller. They replace the dynamic dispatch of the unspecialized generic protocol with static dispatch to the methods on the concrete type, and this in turn unlocks other optimizations within the specialized function. Note that within these specializations, no different overload resolution takes place now that the concrete type is known. That is, the functions being called will be only those made available via the protocol witness table. This ensures that there is no _semantic_ effect from using `@specialized`, only a change in performance. + +As well as protocol extensions, it can also be used on extensions of generic types, on computed properties (note, it must be put on `get` explicitly, not on the shorthand where it is elided), and on free functions: + +```swift +extension Array where Element: BinaryInteger { + @specialized(where Element == Int) + func sum() -> Double { + reduce(0) { $0 + Double($1) } + } + + var product: Double { + @specialized(where Element == Int8) + get { reduce(1) { $0 * Double($1) } } + } +} + +@specialized(where T == Int) +func sum(_ numbers: T...) -> Double { + numbers.reduce(0) { $0 + Double($1) } +} +``` + +The `where` clause must fully specialize all the generic placeholders of the types in the function signature. In the case of a protocol extension, as seen above, that includes specifying `Self`. All placeholders must be fully specified even if they do not appear to be used in the function body: + +```swift +extension Dictionary where Value: BinaryInteger { + // error: Too few generic parameters are specified in 'specialize' attribute (got 1, but expected 2) + // note: Missing equality constraint for 'Key' in 'specialize' attribute + @specialized(where Value == Int) + func sum() -> Double { + values.reduce(0) { $0 + Double($1) } + } +} +``` + +Bear in mind that even when not explicitly used in the body, they may be used implicitly. For example, in calculating the location of stored properties. Depending on how `Dictionary` is laid out, it may be important to know the size of the `Key` even if key values aren't used. But even if there is absolutely no use of a generic type in the body being specialized, the type must be explicitly specified. This requirement could be loosened in future, see "Partial Specialization" in future directions. + +Where multiple placeholders need to be specified separately, they can be separated by commas, such as in the fix for the above example: + +```swift +extension Dictionary where Value: BinaryInteger { + @specialized(where Value == Int, Key == Int) + func sum() -> Double { + values.reduce(0) { $0 + Double($1) } + } +} +``` + +## Source compatibility + +The addition or removal of explicit specializations has no impact on the caller, and is opt-in. As such it has no source compatability implications. + +## ABI compatibility + +This proposal covers only _internal_ specializations, dispatched to from within the implementation of an unspecialized generic function. As such it has no impact on ABI. It can be applied to existing ABI-stable functions in libraries built for distribution, and can be removed later without ABI impact. Generic functions can continue to be inlinable to be specialized by the caller, and the code to dispatch to specialized versions will not appear in the inlinable code emitted into the swift interface file. + +A future direction where explicit specializations are exposed as ABI appears in Future Directions. + +## Implications on adoption + +Explicit specializations impose no new runtime requirements on either the caller or the called function, so use of them can be back-deployed to earlier Swift runtimes. + +## Future directions + +### Partial Specialization + +The need to fully specify every type when using `@specialized` is somewhat limiting. For example, in the `Dictionary` example above, no reference is made in the code to the `Key` type in summing the values. Having to explicitly state the concrete type for `Key` means you need separate specializations for `[String:Int]`, `[Int:Int]` and so on. + +Even in cases where dynamic dispatch through the protocol is still required for the unspecialized aspects (such as determining where to find the values in a dictionary's layout), this might not be in the hot part of the function and so partial specialization might be the better trade-off. + +Closely related to partial specialization is the ability to specialize once for all particular type layouts. In the `Dictionary.sum` example, it might only matter what the size of the key type is in order to efficiently iterate the values. + +Another example of this is `Array.append`. There is only one single implementation needed for appending an `AnyObject`-constrained to an array. The append operation needs to retain the object, but does not need any other properties. So two class instances could be appended using the same method irrespective of their actual type. Similar techniques could be used to provide shared specializations for `BitwiseCopyable` types, possibly with the addition of a supplied size argument to avoid having to use value witness calls. + +### Making Symbols for Specializations Publicly Available + +This proposal keeps entry points for explicit specializations internal only. Callers outside the module call the generic version of the function, which redispatches to the specialized version. For ABI-stable frameworks, a useful future direction would be to make these symbols publicly available and listed in the `.swiftinterface` file, for callers to link to directly. + +Doing this allows ABI-stable framework authors to expose specializations without exposing the full implementation details of a function as inlinable code. While adding a public specialized symbol to a framework is ABI, it is a much more limited ABI surface compared to providing an inlinable implementation, which requires any future changes to a type to consider the previous inlinable code's behavior to ensure it remains compatible forever in older binaries. A specialized entry point could be updated in a framework to fix a bug without recompiling the caller. Specializations in binary frameworks also have the benefit of avoiding duplication of code into the caller. + +### Requiring Specialization + +A related feature is the ability to _require_ specialization either at the call site, or on a protocol. Specialization does not always have to happen at the call site even when it could – it remains an optimization (albeit a very aggressively applied one currently). If the specializatino does not happen, it would be useful to force the compiler to override its heuristics, similar to forcing linlining of a long but critical function. + +There are also protocols that are only meant to be used in specialized form in optimized builds. Arguably `BinaryInteger` is one of them. It may be worth exploring in these cases an annotation to indicate this to the caller, either via a compile-time errror/warning, or a runtime error. + +### Marking types or extensions as `@specialized` + +It may desirable to mark a number of generic functions "en-mass" as specialized for a particular type, by annotating either the type or an extension grouping. + +This would mostly be just sugar for annotating each function individually. The exception could be annotation of classes or protocols where an entire specialized witness or vtable could then be passed around and used from other unspecialized functions. + +### Tooling Directions + +This proposal only outlines language syntax the developer can use to instruct the compiler to emit a specialized version of a function. Complimentary to this is development of tooling to identify profitable specializations to add, for example by profiling typical usage of an app. It should be noted that specialization is only required in highly performance sensitive code. In many cases, explicit specialization will have little impact on overall performance while increasing binary size. + +The current implementation requires the attribute to be attached directly to the function being specialized. Tooling to produce specializations would benefit from an additional syntax that could be added in a separate file, or even into a separately compiled binary. + +## Alternatives considered + +Many alternatives to this proposal – such as whole-program analysis to determine prespecializations automatically – are complimentary to this technique. Even in the presence of better optimizer heroics, there are still benefits to having explicit control over specialization. + +This proposal takes as a given Swift's current dispatch mechanisms. Some alternatives to this proposal tend to end up requiring fundamental changes to Swift's core generics implementation, and are therefore out of scope for this proposal. + +### Execution of custom functions based on type + +Sometimes, it is desirable to execute not a compiler-generated specialization of a function, but a very different implementation based on the type: + +``` +extension Sequence where Element: BinaryInteger { + func sum() -> Double { + if let arrayOfInt = self as? [Int] { + arrayOfInt.handVectorizedImplementation() + } else { + reduce(0) { $0 + Double($1) } + } + } +} +``` + +The specializations created by this proposal are entirely referentially transparent, using Swift's protocol dispatch semantics to ensure the only (reasonably )observable difference is how quickly the code runs. They are just optimizations where dynamic dispatch is replaced by static, code inlined, low-level optimizations applied to that code etc. + +This is distinct from "when it's this type run this code, when it's that type, run that code", where this code and that code might do very different things semantically. You might not mean for them to differ semantically, but they can. + +This is an interesting area to explore, but it's important to be clear that it's a very different feature (and hence not included in future directions). + From a0fb99f9a7afed2339526076ed3c3f7f0c8effee Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 29 Jan 2025 09:29:47 -0500 Subject: [PATCH 4023/4563] Update 0460-specialized.md (#2674) --- proposals/0460-specialized.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0460-specialized.md b/proposals/0460-specialized.md index 0c4e7fa0b4..4a1ba283bf 100644 --- a/proposals/0460-specialized.md +++ b/proposals/0460-specialized.md @@ -5,7 +5,7 @@ * Review Manager: [Steve Canon](https://github.com/stephentyrone) * Status: **Active Review (January 29 ... February 11, 2025)** * Implementation: Available in nightly toolchains using the underscored `@_specialize` -* Discussion: ([pitch](https://forums.swift.org/t/pitch-explicit-specialization/76967)) +* Discussion: ([pitch](https://forums.swift.org/t/pitch-explicit-specialization/76967))([review](https://forums.swift.org/t/se-0460-explicit-specialization/77541)) ## Introduction From c107545a564d4307ce33832c08ffc184deb5c9e3 Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Sun, 2 Feb 2025 00:45:10 +0000 Subject: [PATCH 4024/4563] Replaceable Library Plugins --- proposals/0461-replaceable-library-plugins.md | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 proposals/0461-replaceable-library-plugins.md diff --git a/proposals/0461-replaceable-library-plugins.md b/proposals/0461-replaceable-library-plugins.md new file mode 100644 index 0000000000..10b3603c35 --- /dev/null +++ b/proposals/0461-replaceable-library-plugins.md @@ -0,0 +1,154 @@ +# Replaceable Library Plugins + +* Proposal: [SE-0461](0641-replaceable-library-plugins.md) +* Authors: [tayloraswift](https://github.com/tayloraswift) +* Review Manager: TBD +* Implementation: [swiftlang/swift-package-manager#8249](https://github.com/swiftlang/swift-package-manager/pull/8249) +* Bugs: [SR-5714](https://github.com/swiftlang/swift-package-manager/issues/5714) + +## Introduction + +SwiftPM currently has no support for non-system binary library dependencies on Linux. This proposal adds support for **Replaceable Library Plugins**, which are a type of dynamic library that is shared across a fleet of machines and can be upgraded without recompiling and redeploying all applications running on those machines. We will distribute Replaceable Library Plugins through the existing `.artifactbundle` format. + +Swift-evolution thread: [Discussion thread topic for that +proposal](https://forums.swift.org/) + + +## Motivation + +Many of us in the Server World have a Big App with a small component that changes very rapidly, much more rapidly than the rest of the App. This component might be something like a filter, or an algorithm, or a plugin that is being constantly tuned. + +We could, for argument’s sake, try and turn this component into data that can be consumed by the Big App, which would probably involve designing a bytecode and an interpreter, and maybe even a whole interpreted domain-specific programming language. But that is Really Hard and we would rather just write this thing In Swift, and let Swift code call Swift code. + +While macOS has Dynamic Library support through XCFrameworks, on Linux we currently have to recompile the Big App from source and redeploy the Big App every time the filter changes, and we don’t want to do that. What we really want instead is to have the Big App link the filter as a Dynamic Library, and redeploy the Dynamic Library as needed. + + +## Proposed solution + +On Linux, there are a lot of obstacles to having fully general support for Dynamic Libraries. Swift is not ABI stable on Linux, and Linux itself is not a single platform but a wide range of similar platforms that provide few binary compatibility guarantees. This means it is pretty much impossible for a public Swift library to vend precompiled binaries that will Just Work for everyone, and we are not going to try to solve that problem in this proposal. + +Instead, we will focus on **Replaceable Library Plugins** (RLPs). We choose this term to emphasize the distinction between our use case and fully general Dynamic Libraries. + +### Organization-Defined Platforms (ODPs) + +Unlike fully general Dynamic Libraries, you would distribute Replaceable Library Plugins strictly for internal consumption within an organization, or to a small set of paying clients. + +The organization that distributes an RLP is responsible for defining what exactly constitutes a “platform” for their purposes. An Organization-Defined Platform (ODP) is not necessarily an operating system or architecture, or even a specific distribution of an operating system. A trivial example of two ODPs might be: + +1. Ubuntu 24.04 with the Swift 6.0.3 runtime installed at `/home/ubuntu/swift` +2. Ubuntu 24.04 with the Swift 6.0.3 runtime installed at `/home/ubuntu/swift-runtime` + +Concepts like Platform Triples are not sufficient to describe an ODP. Even though both ODPs above would probably share the Triple `aarch64-unknown-linux-gnu`, code compiled for one would never be able to run on the other. + +Organizations add and remove ODPs as needed, and trying to define a global registry of all possible ODPs is a non-goal. + +To keep things simple, we identify ODPs by the URL of the Artifact Bundle that contains the RLP. + +### Creating RLPs + +To compile an RLP, you just need to build an ordinary SwiftPM library product with the `-enable-library-evolution` flag. This requires no modifications to SwiftPM. + +You would package an RLP as an `.artifactbundle` just as you would an executable, with the following differences: + +- The `info.json` must have `schemaVersion` set to `1.2` or higher. +- The artifact type must be `library`, a new enum case introduced in this proposal. +- The artifact must have exactly one variant in the `variants` list, and the `supportedTriples` field is forbidden. +- The artifact payload must include the `.swiftinterface` file corresponding to the actual library object. + +Because SwiftPM is not (and cannot be) aware of a particular organization’s ODPs, this enforces the requirement that each ODP must have its own Artifact Bundle. + +The organization that distributes the RLP is responsible for upholding ABI stability guarantees, including the exact Swift compiler and runtime versions needed to safely consume the RLP. + + +### Consuming RLPs + +To consume an RLP, you would add a `binaryTarget` to your `Package.swift` manifest, just as you would for an executable. Because ODPs are identified by the URL of the Artifact Bundle, there are no new fields in the `PackageDescription` API. + +We expect that the logic for selecting the correct RLP for a given ODP would live within the `Package.swift` file, that it would be highly organization-specific, and that it would be manipulated using existing means such as environment variables. + + +### Deploying RLPs + +Deploying RLPs does not involve SwiftPM or Artifact Bundles at all. You would deploy an RLP by copying the latest binaries to the appropriate `@rpath` location on each machine in your fleet. The `@rpath` location is part of the ODP definition, and is not modeled by SwiftPM. + +Some organizations might choose to forgo the `@rpath` mechanism entirely and simply install the RLPs in a system-wide location. + + +## Detailed design + +### Schema extensions + +We will extend the `ArtifactsArchiveMetadata` schema to include a new `library` case in the `ArtifactType` enum. + +```diff +public enum ArtifactType: String, RawRepresentable, Decodable { + case executable ++ case library + case swiftSDK +} +``` + +This also bumps the latest `schemaVersion` to `1.2`. + + +### Artifact Bundle layout + +Below is an example of an `info.json` file for an Artifact Bundle containing a single library called `MyLibrary`. + +```json +{ + "schemaVersion": "1.2", + "artifacts": { + "MyLibrary": { + "type": "library", + "version": "1.0.0", + "variants": [{ "path": "MyLibrary" }] + } + } +} +``` + +The artifact must have exactly one variant in the `variants` list, and the `supportedTriples` field is forbidden. An RLP Artifact Bundle can contain multiple libraries at the top level. + +Below is an example of the layout of an Artifact Bundle containing a single library called `MyLibrary`. Only the `info.json` must appear at the root of the Artifact Bundle; all other files can appear at whatever paths are defined in the `info.json`, as long as they are within the Artifact Bundle. + +```text +📂 example.artifactbundle + 📂 MyLibrary + ⚙️ libMyLibrary.so + 📝 MyLibrary.swiftinterface + 📝 info.json +``` + +A macOS Artifact Bundle would contain a `.dylib` instead of a `.so`. RLPs will be supported on macOS, although we expect this will be an exceedingly rare use case. + + +## Security + +RLPs are not intended for public distribution, and are not subject to the same security concerns as public libraries. Organizations that distribute RLPs are responsible for ensuring that the RLPs are safe to consume. + + +## Impact on existing packages + +There will be no impact on existing packages. All Artifact Bundle schema changes are additive. + + +## Alternatives considered + +### Extending Platform Triples to model ODPs + +SwiftPM currently uses Platform Triples to select among artifact variants when consuming executables. This is workable because it is usually feasible to build executables that are portable across the range of platforms encompassed by a single Platform Triple. + +We could extend Platform Triples to model ODPs, but this would privilege a narrow set of predefined deployment architectures, and if you wanted to add a new ODP, you would have to modify SwiftPM to teach it to recognize the new ODP. + +### Supporting multiple variants of an RLP in the same Artifact Bundle + +We could allow an Artifact Bundle to contain multiple variants of an RLP, but we would still need to support a way to identify those variants, which in practice makes SwiftPM aware of ODPs. + +We also don’t see much value in this feature, as you would probably package and upload RLPs using one CI/CD workflow per ODP anyway. Combining artifacts would require some kind of synchronization mechanism to await all pipelines before fetching and merging bundles. + +One benefit of merging bundles would be that it reduces the number of checksums you need to keep track of, but we expect that most organizations will have a very small number of ODPs, with new ODPs continously phasing out old ODPs. + +### Using a different `ArtifactType` name besides `library` + +We intentionally preserved the structure of the `variants` list in the `info.json` file, despite imposing the current restriction of one variant per library, in order to allow this format to be extended in the future to support fully general Dynamic Libraries. From f3a13de34a3b2b8c957630e2f30a38ef4cad67b5 Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Sun, 2 Feb 2025 00:56:25 +0000 Subject: [PATCH 4025/4563] links to example projects --- proposals/0461-replaceable-library-plugins.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/proposals/0461-replaceable-library-plugins.md b/proposals/0461-replaceable-library-plugins.md index 10b3603c35..6b952b95ae 100644 --- a/proposals/0461-replaceable-library-plugins.md +++ b/proposals/0461-replaceable-library-plugins.md @@ -13,12 +13,15 @@ SwiftPM currently has no support for non-system binary library dependencies on L Swift-evolution thread: [Discussion thread topic for that proposal](https://forums.swift.org/) +Example Producer: [swift-rlp-example](https://github.com/tayloraswift/swift-rlp-example) + +Example Consumer: [swift-rlp-example-client](https://github.com/tayloraswift/swift-rlp-example-client) ## Motivation Many of us in the Server World have a Big App with a small component that changes very rapidly, much more rapidly than the rest of the App. This component might be something like a filter, or an algorithm, or a plugin that is being constantly tuned. -We could, for argument’s sake, try and turn this component into data that can be consumed by the Big App, which would probably involve designing a bytecode and an interpreter, and maybe even a whole interpreted domain-specific programming language. But that is Really Hard and we would rather just write this thing In Swift, and let Swift code call Swift code. +We could, for argument’s sake, try and turn this component into data that can be consumed by the Big App, which would probably involve designing a bytecode and an interpreter, and maybe even a whole interpreted domain-specific programming language. But that is very hard and we would rather just write this thing in Swift, and let Swift code call Swift code. While macOS has Dynamic Library support through XCFrameworks, on Linux we currently have to recompile the Big App from source and redeploy the Big App every time the filter changes, and we don’t want to do that. What we really want instead is to have the Big App link the filter as a Dynamic Library, and redeploy the Dynamic Library as needed. @@ -38,7 +41,7 @@ The organization that distributes an RLP is responsible for defining what exactl 1. Ubuntu 24.04 with the Swift 6.0.3 runtime installed at `/home/ubuntu/swift` 2. Ubuntu 24.04 with the Swift 6.0.3 runtime installed at `/home/ubuntu/swift-runtime` -Concepts like Platform Triples are not sufficient to describe an ODP. Even though both ODPs above would probably share the Triple `aarch64-unknown-linux-gnu`, code compiled for one would never be able to run on the other. +Concepts like Platform Triples are not sufficient to describe an ODP. Even though both ODPs above would probably share the Triple `aarch64-unknown-linux-gnu`, Swift code compiled (without `--static-swift-stdlib`) for one would never be able to run on the other. Organizations add and remove ODPs as needed, and trying to define a global registry of all possible ODPs is a non-goal. From 686a8c96f0121cd183444815390985045b1086ed Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Sun, 2 Feb 2025 01:11:11 +0000 Subject: [PATCH 4026/4563] link to Forums thread --- proposals/0461-replaceable-library-plugins.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0461-replaceable-library-plugins.md b/proposals/0461-replaceable-library-plugins.md index 6b952b95ae..a89ccffc52 100644 --- a/proposals/0461-replaceable-library-plugins.md +++ b/proposals/0461-replaceable-library-plugins.md @@ -10,8 +10,7 @@ SwiftPM currently has no support for non-system binary library dependencies on Linux. This proposal adds support for **Replaceable Library Plugins**, which are a type of dynamic library that is shared across a fleet of machines and can be upgraded without recompiling and redeploying all applications running on those machines. We will distribute Replaceable Library Plugins through the existing `.artifactbundle` format. -Swift-evolution thread: [Discussion thread topic for that -proposal](https://forums.swift.org/) +Swift-evolution thread: [Discussion thread](https://forums.swift.org/t/pitch-replaceable-library-plugins/77605) Example Producer: [swift-rlp-example](https://github.com/tayloraswift/swift-rlp-example) From b02a4ac7241a110e46e0273a81631b575162f7c8 Mon Sep 17 00:00:00 2001 From: Dianna Date: Sat, 1 Feb 2025 21:02:44 -0600 Subject: [PATCH 4027/4563] unassigned number --- ...e-library-plugins.md => NNNN-replaceable-library-plugins.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{0461-replaceable-library-plugins.md => NNNN-replaceable-library-plugins.md} (99%) diff --git a/proposals/0461-replaceable-library-plugins.md b/proposals/NNNN-replaceable-library-plugins.md similarity index 99% rename from proposals/0461-replaceable-library-plugins.md rename to proposals/NNNN-replaceable-library-plugins.md index a89ccffc52..35f2d9e1c3 100644 --- a/proposals/0461-replaceable-library-plugins.md +++ b/proposals/NNNN-replaceable-library-plugins.md @@ -1,6 +1,6 @@ # Replaceable Library Plugins -* Proposal: [SE-0461](0641-replaceable-library-plugins.md) +* Proposal: [SE-NNNN](NNNN-replaceable-library-plugins.md) * Authors: [tayloraswift](https://github.com/tayloraswift) * Review Manager: TBD * Implementation: [swiftlang/swift-package-manager#8249](https://github.com/swiftlang/swift-package-manager/pull/8249) From 4c7c8a387eb497661e3e98be078b25c9b258e291 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Mon, 3 Feb 2025 12:20:10 -0500 Subject: [PATCH 4028/4563] Update 0457-duration-attosecond-represenation.md (#2678) 457 has been accepted. --- proposals/0457-duration-attosecond-represenation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0457-duration-attosecond-represenation.md b/proposals/0457-duration-attosecond-represenation.md index 876e5ef432..6e8da7e7be 100644 --- a/proposals/0457-duration-attosecond-represenation.md +++ b/proposals/0457-duration-attosecond-represenation.md @@ -3,7 +3,7 @@ * Proposal: [SE-0457](0457-duration-attosecond-represenation.md) * Authors: [Philipp Gabriel](https://github.com/ph1ps) * Review Manager: [Stephen Canon](https://github.com/stephentyrone) -* Status: **Active review (January 16 ... 30, 2025)** +* Status: **Accepted** * Implementation: [swiftlang/swift#78202](https://github.com/swiftlang/swift/pull/78202) * Review: ([pitch](https://forums.swift.org/t/pitch-adding-int128-support-to-duration))([review](https://forums.swift.org/t/se-0457-expose-attosecond-representation-of-duration/77249)) From ce8b4bd6bf1d1c0ea385ea9b45ad69497aad3a87 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 4 Feb 2025 10:30:29 -0800 Subject: [PATCH 4029/4563] SE-0458: Allow @safe declarations to subsume some responsibility for their arguments --- proposals/0458-strict-memory-safety.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index b973058c7d..7a5998d53e 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -125,7 +125,7 @@ extension UnsafeMutableBufferPointer { @unsafe public func swapAt(_ i: Index, _ j: Index) { guard i != j else { return } precondition(i >= 0 && j >= 0) - precondition(unsafe i < endIndex && j < endIndex) + precondition(i < endIndex && j < endIndex) let pi = unsafe (baseAddress! + i) let pj = unsafe (baseAddress! + j) let tmp = unsafe pi.move() @@ -217,7 +217,7 @@ There are a few exemptions to the rule that any unsafe constructs within the sig ### `@safe` attribute -Like the `@unsafe` attribute, the `@safe` attribute ise used on declarations whose signatures involve unsafe types. However, the `@safe` attribute means that the declaration is consider safe to use even though its signature includes unsafe types. For example, marking `UnsafeBufferPointer` as `@unsafe` means that all operations involving an unsafe buffer pointer are implicitly considered `@unsafe`. The `@safe` attribute can be used to say that those particular operations are actually safe. For example, any operation involving buffer indices or count are safe, because they don't touch the memory itself. This can be indicated by marking these APIs `@safe`: +Like the `@unsafe` attribute, the `@safe` attribute is used on declarations whose signatures involve unsafe types. However, the `@safe` attribute means that the declaration is consider safe to use even though its signature includes unsafe types. For example, marking `UnsafeBufferPointer` as `@unsafe` means that all operations involving an unsafe buffer pointer are implicitly considered `@unsafe`. The `@safe` attribute can be used to say that those particular operations are actually safe. For example, any operation involving buffer indices or count are safe, because they don't touch the memory itself. This can be indicated by marking these APIs `@safe`: ```swift extension UnsafeBufferPointer { @@ -249,6 +249,20 @@ extension Array { } ``` +The `@safe` annotation on a declaration takes responsibility for its direct arguments, so (for example) a variable of unsafe type used as an argument to a `@safe` function (or as the `self` for a property or subscript reference) will not be diagnosed as unsafe: + +```swift +extension Array { + func sum() -> Int { + withUnsafeBufferPointer { buffer in + let count = buffer.count // count is `@safe`, no diagnostic even though 'buffer' has unsafe type + let address = buffer.baseAddress // warning: 'buffer' and 'baseAddress' are both unsafe + c_library_sum_function(address, count, 0) // warning: 'c_library_sum_function' and 'address' are both unsafe + } + } +} +``` + ### `unsafe` expression When a declaration is marked `@unsafe`, it is free to use any other unsafe types as part of its interface. Any time there is executable code that makes use of unsafe constructs, that code must be within an `unsafe` expression or it will receive a diagnostic about uses of unsafe code. In the example from the previous section, `wrapper` can be marked as `@unsafe` to suppress diagnostics by explicitly propagating unsafety to their clients: From b302ea65f68c90675ec8a16d1a26d6f731530998 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 4 Feb 2025 10:44:28 -0800 Subject: [PATCH 4030/4563] Improve wording of this carve-out a bit --- proposals/0458-strict-memory-safety.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index 7a5998d53e..950098c0c1 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -249,7 +249,7 @@ extension Array { } ``` -The `@safe` annotation on a declaration takes responsibility for its direct arguments, so (for example) a variable of unsafe type used as an argument to a `@safe` function (or as the `self` for a property or subscript reference) will not be diagnosed as unsafe: +The `@safe` annotation on a declaration takes responsibility for any variables of unsafe type that are used as its direct arguments (including the `self`). If such a variable is used to access a `@safe` property or subscript, or in a function call to a `@safe` function, it will not be diagnosed as unsafe: ```swift extension Array { From 46c6781a92c8f76d8275192b86c3cf1d986977c0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 4 Feb 2025 17:23:30 -0800 Subject: [PATCH 4031/4563] SE-0458: Add revision history --- proposals/0458-strict-memory-safety.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index 950098c0c1..63e6a315ad 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -430,6 +430,11 @@ Other than the source break above, the introduction of this strict safety checki The attributes, `unsafe` expression, and strict memory-safety checking model proposed here have no impact on ABI. +## Revision history + +* **Revision 2 (following first review)** + * Specified that variables of unsafe type passed in to uses of `@safe` declarations (e.g., calls, property accesses) are not diagnosed as themselves being unsafe. This makes means that expressions like `unsafeBufferePointer.count` will be considered safe. + ## Future Directions ### The `SerialExecutor` and `Actor` protocols From 86c1028817fabed4bb1816925db6175f98a135ff Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 5 Feb 2025 13:25:43 -0800 Subject: [PATCH 4032/4563] SE-0458: Add Alternatives Considered section on unsafe conformances/overrides --- proposals/0458-strict-memory-safety.md | 64 +++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index 63e6a315ad..12d4288ae6 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -217,7 +217,7 @@ There are a few exemptions to the rule that any unsafe constructs within the sig ### `@safe` attribute -Like the `@unsafe` attribute, the `@safe` attribute is used on declarations whose signatures involve unsafe types. However, the `@safe` attribute means that the declaration is consider safe to use even though its signature includes unsafe types. For example, marking `UnsafeBufferPointer` as `@unsafe` means that all operations involving an unsafe buffer pointer are implicitly considered `@unsafe`. The `@safe` attribute can be used to say that those particular operations are actually safe. For example, any operation involving buffer indices or count are safe, because they don't touch the memory itself. This can be indicated by marking these APIs `@safe`: +Like the `@unsafe` attribute, the `@safe` attribute is used on declarations whose signatures involve unsafe types. However, the `@safe` attribute means that the declaration is considered safe to use even though its signature includes unsafe types. For example, marking `UnsafeBufferPointer` as `@unsafe` means that all operations involving an unsafe buffer pointer are implicitly considered `@unsafe`. The `@safe` attribute can be used to say that those particular operations are actually safe. For example, any operation involving buffer indices or count are safe, because they don't touch the memory itself. This can be indicated by marking these APIs `@safe`: ```swift extension UnsafeBufferPointer { @@ -430,11 +430,6 @@ Other than the source break above, the introduction of this strict safety checki The attributes, `unsafe` expression, and strict memory-safety checking model proposed here have no impact on ABI. -## Revision history - -* **Revision 2 (following first review)** - * Specified that variables of unsafe type passed in to uses of `@safe` declarations (e.g., calls, property accesses) are not diagnosed as themselves being unsafe. This makes means that expressions like `unsafeBufferePointer.count` will be considered safe. - ## Future Directions ### The `SerialExecutor` and `Actor` protocols @@ -489,6 +484,57 @@ We have several options here: ## Alternatives considered +### Prohibiting unsafe conformances and overrides entirely + +This proposal introduces two places where polymorphism interacts with unsafety: protocol conformances and overrides. In both cases, a safe abstraction (e.g., a superclass or protocol) has a specific implementation that is unsafe, and there is a way to note the unsafety: + +* When overriding a safe declaration with an unsafe one, the overriding subclass must be marked `@unsafe`. +* When implementing a safe protocol requirement with an unsafe declaration, the corresponding conformance must be marked `@unsafe`. + +In both cases, the current proposal will consider uses of the type (in the overriding case) or conformance (for that case) as unsafe, respectively. However, that unsafety is not localized, because code that's generally safe can now cause safety problems when calling through polymorphic operations. For example, consider a function that operates on a general collection: + +```swift +func parse(_ input: some Collection) -> ParseResult +``` + +Calling this function with an unsafe buffer pointer will produce a diagnostic due to the use of the unsafe conformance of `UnsafeBufferPointer` to `Collection`: + +```swift +let result = parse(unsafeBufferPointer) // warning: use of unsafe conformance +``` + +Marking the call as `unsafe` will address the diagnostic. However, because `UnsafeBufferPointer` doesn't perform bounds checking, the `parse` function itself can introduce a memory safety problem if it subscripts into the collection with an invalid index. There isn't a way to communicate how the code that is `unsafe` is addressing memory safety issues within the context of the call. + +This proposal could prohibit use of unsafe conformances and overrides entirely, for example by making it impossible to suppress the diagnostics associated with their definition and use. This would require the `parse(unsafeBufferPointer)` call to be refactored to avoid the unsafe conformance, for example by introducing a wrapper type: + +```swift +@safe struct ImmortalBufferWrapper : Collection { + let buffer: UnsafeBufferPointer + + @unsafe init(_ withImmortalBuffer: UnsafeBufferPointer) { + self.buffer = unsafe buffer + } + + subscript(index: Index) -> Element { + precondition(index >= 0 && index < buffer.count) + return unsafe buffer[index] + } + + /* Also: Index, startIndex, endIndex, index(after:) */ +} +``` + +The call would then look like this: + +```swift +let wrapper = unsafe ImmortalBufferWrapper(withImmortalBuffer: buffer) +let result = parse(wrapper) +``` + +This approach is better than the prior one: it improves bounds safety by introducing bounds checking. It clearly documents the assumptions made around lifetime safety. It is both functionally safer (due to bounds checks) and makes it easier to reason that the `unsafe` is correctly used. It does require a lot more code, and the code itself requires careful reasoning about safety (e.g., the right preconditions for bounds checking; the right naming to capture the lifetime implications). + +Unsafe conformances and overrides remain part of this proposal because prohibiting them doesn't fundamentally change the safety model. Rather, it requires the introduction of more abstractions that could be safer--or could just be boilerplate. Swift has a number of constructs that are functionally similar to unsafe conformances, where safety checking can be disabled locally despite that having wide-ranging consequences: `@unchecked Sendable`, `nonisolated(unsafe)`, `unowned(unsafe)`, and `@preconcurrency` all fall into this category. + ### `@unsafe` implying `unsafe` throughout a function body A function marked `@unsafe` is unsafe to use, so any clients that have enabled strict safety checking will need to put uses of the function into an `unsafe` expression. The implementation of that function is likely to use unsafe code (possibly a lot of it), which could result in a large number of annotations: @@ -654,6 +700,12 @@ There are downsides to this approach. It partially undermines the source compati We could introduce an optional `message` argument to the `@unsafe` attribute, which would allow programmers to indicate *why* the use of a particular declaration is unsafe and, more importantly, how to safely write code that uses it. However, this argument isn't strictly necessary: a comment could provide the same information, and there is established tooling to expose comments to programmers that wouldn't be present for this attribute's message, so we have omitted this feature. +## Revision history + +* **Revision 2 (following first review)** + * Specified that variables of unsafe type passed in to uses of `@safe` declarations (e.g., calls, property accesses) are not diagnosed as themselves being unsafe. This makes means that expressions like `unsafeBufferePointer.count` will be considered safe. + * Add an Alternatives Considered section on prohibiting unsafe conformances and overrides. + ## Acknowledgments This proposal has been greatly improved by the feedback from Félix Cloutier, Geoff Garen, Gábor Horváth, Frederick Kellison-Linn, Karl Wagner, and Xiaodi Wu. From a3947c847485b4ccc43ec5983051ec47ac3df569 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 5 Feb 2025 13:34:17 -0800 Subject: [PATCH 4033/4563] SE-0458: Require types to be marked @safe/@unsafe if their storage is unsafe --- proposals/0458-strict-memory-safety.md | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index 12d4288ae6..0ec06c1bb2 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -331,6 +331,35 @@ There are a number of compiler flags that intentionally disable some safety-rela * `-strict-concurrency=` for anything other than "complete", because the memory safety model requires strict concurrency to eliminate thread safety issues. * `-disable-access-control`, which allows one to break invariants of a type that can lead to memory-safety issues, such as breaking the invariant of `Range` that the lower bound not exceed the upper bound. +### Types with unsafe storage + +Types that wrap unsafe types will often encapsulate the unsafe behavior to provide safe interfaces. However, this requires deliberate design and implementation, potentially involving adding specific preconditions. When strict safety checking is enabled, a type whose storage is unsafe will be diagnosed as involving unsafe code. This diagnostic can be suppressed by marking the type as `@safe` or `@unsafe`, in the same manner as any other declaration that has unsafe types or conformances in its signature: + +```swift +// @safe is required to suppress a diagnostic about the 'buffer' property's use +// of an unsafe type. +@safe +struct ImmortalBufferWrapper : Collection { + let buffer: UnsafeBufferPointer + + @unsafe init(_ withImmortalBuffer: UnsafeBufferPointer) { + self.buffer = unsafe buffer + } + + subscript(index: Index) -> Element { + precondition(index >= 0 && index < buffer.count) + return unsafe buffer[index] + } + + /* Also: Index, startIndex, endIndex, index(after:) */ +} +``` + +A type has unsafe storage if: + +* Any stored instance property (for `actor`, `class`, and `struct` types) or associated value (for cases of `enum` types) have a type that involves an unsafe type or conformance. +* Any stored instance property uses one of the unsafe language features (such as `unowned(unsafe)`). + ### Unsafe overrides Overriding a safe method within an `@unsafe` one could introduce unsafety, so it will produce a diagnostic in the strict safety mode: @@ -704,6 +733,7 @@ We could introduce an optional `message` argument to the `@unsafe` attribute, wh * **Revision 2 (following first review)** * Specified that variables of unsafe type passed in to uses of `@safe` declarations (e.g., calls, property accesses) are not diagnosed as themselves being unsafe. This makes means that expressions like `unsafeBufferePointer.count` will be considered safe. + * Require types whose storage involves an unsafe type or conformance to be marked as `@safe` or `@unsafe`, much like other declarations that have unsafe types or conformances in their signature. * Add an Alternatives Considered section on prohibiting unsafe conformances and overrides. ## Acknowledgments From 06ef7bcc8ca908f6af190462ce81e58fe4541383 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 5 Feb 2025 14:01:23 -0800 Subject: [PATCH 4034/4563] Add a Future Directions section on handling unsafe code in macro expansions. --- proposals/0458-strict-memory-safety.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index 0ec06c1bb2..4538a6b2f6 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -511,6 +511,14 @@ We have several options here: if case unsafe .rawOffsetIntoGlobalArray(let offset) = weirdAddress { ... } ``` +### Handling unsafe code in macro expansions + +A macro can expand to any code. If the macro-expanded code contains uses of unsafe constructs not properly covered by `@safe`, `@unsafe`, or an `unsafe` expression within the macro, then strict safety checking will diagnose those safety issues within the macro expansion. In this case, the client of the macro does not have any way to suppress diagnostics within the macro expansion itself without modifying the implementation of the macro. + +There are a number of possible approaches that one could use for suppression. The `unsafe` expression could be made to apply to everything in the macro expansion, which would also require some spelling for attached attributes and other places where expressions aren't permitted. Alternatively, Swift could introduce a general syntax for suppressing a class of warnings within a block of code, and that could be used to surround the macro expansion. + +Note that both of these approaches trade away some of the benefits of the strict safety mode for the convenience of suppressing safety-related diagnostics. + ## Alternatives considered ### Prohibiting unsafe conformances and overrides entirely @@ -735,6 +743,7 @@ We could introduce an optional `message` argument to the `@unsafe` attribute, wh * Specified that variables of unsafe type passed in to uses of `@safe` declarations (e.g., calls, property accesses) are not diagnosed as themselves being unsafe. This makes means that expressions like `unsafeBufferePointer.count` will be considered safe. * Require types whose storage involves an unsafe type or conformance to be marked as `@safe` or `@unsafe`, much like other declarations that have unsafe types or conformances in their signature. * Add an Alternatives Considered section on prohibiting unsafe conformances and overrides. + * Add a Future Directions section on handling unsafe code in macro expansions. ## Acknowledgments From 15f57c72f53d6c36f0a2c1d34ca925d259101945 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 5 Feb 2025 17:55:58 -0500 Subject: [PATCH 4035/4563] Link to the previous revision --- proposals/0458-strict-memory-safety.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index 4538a6b2f6..c63ca13241 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -7,6 +7,7 @@ * Feature name: `StrictMemorySafety` * Vision: [Opt-in Strict Memory Safety Checking (Prospective)](https://github.com/swiftlang/swift-evolution/pull/2581) * Implementation: On main with experimental feature flags `AllowUnsafeAttribute` and `WarnUnsafe` +* Previous Revision: [1](https://github.com/swiftlang/swift-evolution/blob/f2cab4ddc3381d1dc7a970e813ed29e27b5ae43f/proposals/0458-strict-memory-safety.md) * Review: ([pitch](https://forums.swift.org/t/pitch-opt-in-strict-memory-safety-checking/76689)) ([review](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274)) ## Introduction From 9d180aea291c6b430bcc816ce12ef0174ec0237b Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 5 Feb 2025 18:13:00 -0500 Subject: [PATCH 4036/4563] Extend the SE-0458 review --- proposals/0458-strict-memory-safety.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index c63ca13241..c194e8dd49 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -3,12 +3,12 @@ * Proposal: [SE-0458](0458-strict-memory-safety.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (January 17th...27th, 2025)** +* Status: **Active Review (January 17th...February 11th, 2025)** * Feature name: `StrictMemorySafety` * Vision: [Opt-in Strict Memory Safety Checking (Prospective)](https://github.com/swiftlang/swift-evolution/pull/2581) * Implementation: On main with experimental feature flags `AllowUnsafeAttribute` and `WarnUnsafe` * Previous Revision: [1](https://github.com/swiftlang/swift-evolution/blob/f2cab4ddc3381d1dc7a970e813ed29e27b5ae43f/proposals/0458-strict-memory-safety.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-opt-in-strict-memory-safety-checking/76689)) ([review](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274)) +* Review: ([pitch](https://forums.swift.org/t/pitch-opt-in-strict-memory-safety-checking/76689)) ([review](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274)) ([mid-review revision](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274/33)) ## Introduction From bdc64a33ba9e6b51810defcbf8b8ff5c77542894 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 6 Feb 2025 21:33:34 +0000 Subject: [PATCH 4037/4563] [SE-0459] Fix metadata of `enumerated()` proposal (#2671) --- proposals/0459-enumerated-collection.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0459-enumerated-collection.md b/proposals/0459-enumerated-collection.md index 4018b8b7b9..a2df267080 100644 --- a/proposals/0459-enumerated-collection.md +++ b/proposals/0459-enumerated-collection.md @@ -1,17 +1,17 @@ # Add `Collection` conformances for `enumerated()` -* Previous proposal: [SE-0312](0312-indexed-and-enumerated-zip-collections.md) +* Proposal: [SE-0459](0459-enumerated-collection.md) * Author: [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (28 Jan - February 7 2025)** -* Implementation: [apple/swift#78092](https://github.com/swiftlang/swift/pull/78092) +* Status: **Active review (January 28 – February 7, 2025)** +* Implementation: [swiftlang/swift#78092](https://github.com/swiftlang/swift/pull/78092) +* Previous Proposal: [SE-0312](0312-indexed-and-enumerated-zip-collections.md) +* Review: ([pitch](https://forums.swift.org/t/pitch-add-collection-conformance-for-enumeratedsequence/76680)) ([review](https://forums.swift.org/t/se-0459-add-collection-conformances-for-enumerated/77509)) ## Introduction This proposal aims to fix the lack of `Collection` conformance of the sequence returned by `enumerated()`, preventing it from being used in a context that requires a `Collection`. -Swift-evolution thread: [Pitch](https://forums.swift.org/t/pitch-add-indexed-and-collection-conformances-for-enumerated-and-zip/47288) - ## Motivation Currently, `EnumeratedSequence` type conforms to `Sequence`, but not to any of the collection protocols. Adding these conformances was impossible before [SE-0234 Remove `Sequence.SubSequence`](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0234-remove-sequence-subsequence.md), and would have been an ABI breaking change before the language allowed `@available` annotations on protocol conformances ([PR](https://github.com/apple/swift/pull/34651)). Now we can add them! From c27e570d3a0e2fe9f07a2fed8d2f6e14756340a2 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 6 Feb 2025 22:07:57 +0000 Subject: [PATCH 4038/4563] [SE-0460] Fix metadata of `@specialized` proposal (#2686) --- proposals/0460-specialized.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0460-specialized.md b/proposals/0460-specialized.md index 4a1ba283bf..6f0979f9e7 100644 --- a/proposals/0460-specialized.md +++ b/proposals/0460-specialized.md @@ -5,7 +5,7 @@ * Review Manager: [Steve Canon](https://github.com/stephentyrone) * Status: **Active Review (January 29 ... February 11, 2025)** * Implementation: Available in nightly toolchains using the underscored `@_specialize` -* Discussion: ([pitch](https://forums.swift.org/t/pitch-explicit-specialization/76967))([review](https://forums.swift.org/t/se-0460-explicit-specialization/77541)) +* Review: ([pitch](https://forums.swift.org/t/pitch-explicit-specialization/76967)) ([review](https://forums.swift.org/t/se-0460-explicit-specialization/77541)) ## Introduction From 8b38f1739a17b40f939a30deb799144bf48663bc Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 7 Feb 2025 05:29:58 -0800 Subject: [PATCH 4039/4563] [SE-0453] Rename Vector to InlineArray (#2682) --- proposals/0453-vector.md | 307 ++++++++++++++++++++------------------- 1 file changed, 156 insertions(+), 151 deletions(-) diff --git a/proposals/0453-vector.md b/proposals/0453-vector.md index 17e5f7924d..9828b90203 100644 --- a/proposals/0453-vector.md +++ b/proposals/0453-vector.md @@ -1,4 +1,4 @@ -# Vector, a fixed-size array +# InlineArray, a fixed-size array * Proposal: [SE-0453](0453-vector.md) * Authors: [Alejandro Alonso](https://github.com/Azoy) @@ -10,7 +10,7 @@ ## Introduction -This proposal introduces a new type to the standard library, `Vector`, which is +This proposal introduces a new type to the standard library, `InlineArray`, which is a fixed-size array. This is analogous to the [classical C arrays `T[N]`](https://en.cppreference.com/w/c/language/array), [C++'s `std::array`](https://en.cppreference.com/w/cpp/container/array), @@ -73,18 +73,18 @@ utilities. ## Proposed solution -We introduce a new top level type, `Vector`, to the standard library which is a +We introduce a new top level type, `InlineArray`, to the standard library which is a fixed-size contiguously inline allocated array. We're defining "inline" as using the most natural allocation pattern depending on the context of where this is used. It will be stack allocated most of the time, but as a class property member it will be inline allocated on the heap with the rest of the properties. -`Vector` will never introduce an implicit heap allocation just for its storage +`InlineArray` will never introduce an implicit heap allocation just for its storage alone. ```swift func complexAlgorithm() -> Int { // This is a stack allocation, no 'malloc's or reference counting here! - let elements: Vector<4, Int> = [1, 2, 3, 4] + let elements: InlineArray<4, Int> = [1, 2, 3, 4] for i in elements.indices { compute(elements[i]) // OK @@ -92,43 +92,43 @@ func complexAlgorithm() -> Int { } ``` -Vectors of noncopyable values will be possible by using any of the closure based +InlineArrays of noncopyable values will be possible by using any of the closure based taking initializers or the literal initializer: ```swift // [Atomic(0), Atomic(1), Atomic(2), Atomic(3)] -let incrementingAtomics = Vector<4, Atomic> { i in +let incrementingAtomics = InlineArray<4, Atomic> { i in Atomic(i) } // [Sprite(), Sprite(), Sprite(), Sprite()] // Where the 2nd, 3rd, and 4th elements are all copies of their previous // element. -let copiedSprites = Vector<4, _>(first: Sprite()) { $0.copy() } +let copiedSprites = InlineArray<4, _>(first: Sprite()) { $0.copy() } -// Inferred to be Vector<3, Mutex> -let literalMutexes: Vector = [Mutex(0), Mutex(1), Mutex(2)] +// Inferred to be InlineArray<3, Mutex> +let literalMutexes: InlineArray = [Mutex(0), Mutex(1), Mutex(2)] ``` These closure based initializers are not limited to noncopyable values however! ## Detailed design -`Vector` will be a simple noncopyable struct capable of storing other potentially +`InlineArray` will be a simple noncopyable struct capable of storing other potentially noncopyable elements. It will be conditionally copyable only when its elements are. ```swift -public struct Vector: ~Copyable {} +public struct InlineArray: ~Copyable {} -extension Vector: Copyable where Element: Copyable {} -extension Vector: BitwiseCopyable where Element: BitwiseCopyable {} -extension Vector: Sendable where Element: Sendable {} +extension InlineArray: Copyable where Element: Copyable {} +extension InlineArray: BitwiseCopyable where Element: BitwiseCopyable {} +extension InlineArray: Sendable where Element: Sendable {} ``` ### MemoryLayout -The memory layout of a `Vector` is defined by taking its `Element`'s stride and +The memory layout of a `InlineArray` is defined by taking its `Element`'s stride and multiplying that by its `count` for its size and stride. Its alignment is equal to that of its `Element`: @@ -136,9 +136,9 @@ to that of its `Element`: MemoryLayout.stride == 1 MemoryLayout.alignment == 1 -MemoryLayout>.size == 4 -MemoryLayout>.stride == 4 -MemoryLayout>.alignment == 1 +MemoryLayout>.size == 4 +MemoryLayout>.stride == 4 +MemoryLayout>.alignment == 1 struct Uneven { let x: UInt32 @@ -148,176 +148,176 @@ struct Uneven { MemoryLayout.stride == 8 MemoryLayout.alignment == 4 -MemoryLayout>.size == 32 -MemoryLayout>.stride == 32 -MemoryLayout>.alignment == 4 +MemoryLayout>.size == 32 +MemoryLayout>.stride == 32 +MemoryLayout>.alignment == 4 struct ACoupleOfUInt8s { - let x: Vector<2, UInt8> + let x: InlineArray<2, UInt8> } MemoryLayout.stride == 2 MemoryLayout.alignment == 1 -MemoryLayout>.size == 4 -MemoryLayout>.stride == 4 -MemoryLayout>.alignment == 1 +MemoryLayout>.size == 4 +MemoryLayout>.stride == 4 +MemoryLayout>.alignment == 1 ``` ### Literal Initialization Before discussing any of the API, we need to discuss how the array literal -syntax will be used to initialize a value of `Vector`. While naively we could +syntax will be used to initialize a value of `InlineArray`. While naively we could conform to `ExpressibleByArrayLiteral`, the shape of the initializer always takes an actual `Array` value. This could be optimized away in the simple cases, but fundamentally it doesn't make sense to have to do an array allocation to -initialize a stack allocated `Vector`. Therefore, the array literal -initialization for `Vector` will be a special case, at least to start out with. -A stack allocated vector using a vector literal will do in place initialization +initialize a stack allocated `InlineArray`. Therefore, the array literal +initialization for `InlineArray` will be a special case, at least to start out with. +A stack allocated InlineArray using a InlineArray literal will do in place initialization of each element at its stack slot. The two below are roughly equivalent: ```swift -let numbers: Vector<3, Int> = [1, 2, 3] +let numbers: InlineArray<3, Int> = [1, 2, 3] // Roughly gets compiled as: -// This is not a real 'Vector' initializer! -let numbers: Vector<3, Int> = Vector() +// This is not a real 'InlineArray' initializer! +let numbers: InlineArray<3, Int> = InlineArray() numbers[0] = 1 numbers[1] = 2 numbers[2] = 3 ``` -There shouldn't be any intermediary values being copied or moved into the vector. +There shouldn't be any intermediary values being copied or moved into the InlineArray. -Note that the array literal syntax will only create a `Vector` value when the -compiler knows concretely that it is a `Vector` value. We don't want to break +Note that the array literal syntax will only create a `InlineArray` value when the +compiler knows concretely that it is a `InlineArray` value. We don't want to break source whatsoever, so whatever current rules the compiler has will still be intact. Consider the following uses of the array literal syntax and where each -call site creates either a `Swift.Array` or a `Swift.Vector`. +call site creates either a `Swift.Array` or a `Swift.InlineArray`. ```swift let a = [1, 2, 3] // Swift.Array -let b: Vector<3, Int> = [1, 2, 3] // Swift.Vector +let b: InlineArray<3, Int> = [1, 2, 3] // Swift.InlineArray func generic(_: T) {} generic([1, 2, 3]) // passes a Swift.Array -generic([1, 2, 3] as Vector<3, Int>) // passes a Swift.Vector +generic([1, 2, 3] as InlineArray<3, Int>) // passes a Swift.InlineArray func test(_: T) {} test([1, 2, 3]) // passes a Swift.Array -test([1, 2, 3] as Vector<3, Int>) // error: 'Vector<3, Int>' does not conform to 'ExpressibleByArrayLiteral' +test([1, 2, 3] as InlineArray<3, Int>) // error: 'InlineArray<3, Int>' does not conform to 'ExpressibleByArrayLiteral' func array(_: [T]) {} array([1, 2, 3]) // passes a Swift.Array -array([1, 2, 3] as Vector<3, Int>) // error: 'Vector<3, Int>' is not convertible to 'Array' +array([1, 2, 3] as InlineArray<3, Int>) // error: 'InlineArray<3, Int>' is not convertible to 'Array' -func vector(_: Vector<3, T>) {} +func InlineArray(_: InlineArray<3, T>) {} -vector([1, 2, 3]) // passes a Swift.Vector -vector([1, 2, 3] as [Int]) // error: 'Array' is not convertible to 'Vector<3, Int>' +InlineArray([1, 2, 3]) // passes a Swift.InlineArray +InlineArray([1, 2, 3] as [Int]) // error: 'Array' is not convertible to 'InlineArray<3, Int>' ``` -I discuss later about a hypothetical `ExpressibleByVectorLiteral` and the design -challenges there in [Future Directions](#expressiblebyvectorliteral). +I discuss later about a hypothetical `ExpressibleByInlineArrayLiteral` and the design +challenges there in [Future Directions](#expressiblebyInlineArrayliteral). The literal initialization allows for more type inference just like the current literal syntax does by inferring not only the element type, but also the count as well: ```swift -let a: Vector<_, Int> = [1, 2, 3] // Vector<3, Int> -let b: Vector<3, _> = [1, 2, 3] // Vector<3, Int> -let c: Vector<_, _> = [1, 2, 3] // Vector<3, Int> -let d: Vector = [1, 2, 3] // Vector<3, Int> +let a: InlineArray<_, Int> = [1, 2, 3] // InlineArray<3, Int> +let b: InlineArray<3, _> = [1, 2, 3] // InlineArray<3, Int> +let c: InlineArray<_, _> = [1, 2, 3] // InlineArray<3, Int> +let d: InlineArray = [1, 2, 3] // InlineArray<3, Int> -func takesGenericVector(_: Vector) {} +func takesGenericInlineArray(_: InlineArray) {} -takesGenericVector([1, 2, 3]) // Ok, N is inferred to be '3'. +takesGenericInlineArray([1, 2, 3]) // Ok, N is inferred to be '3'. ``` A compiler diagnostic will occur if the number of elements within the literal do not match the desired count (as well as element with the usual diagnostic): ```swift -// error: expected '2' elements in vector literal, but got '3' -let x: Vector<2, Int> = [1, 2, 3] +// error: expected '2' elements in InlineArray literal, but got '3' +let x: InlineArray<2, Int> = [1, 2, 3] -func takesVector(_: Vector<2, Int>) {} +func takesInlineArray(_: InlineArray<2, Int>) {} -// error: expected '2' elements in vector literal, but got '3' -takesVector([1, 2, 3]) +// error: expected '2' elements in InlineArray literal, but got '3' +takesInlineArray([1, 2, 3]) ``` ### Initialization -In addition to literal initialization, `Vector` offers a few others forms of +In addition to literal initialization, `InlineArray` offers a few others forms of initialization: ```swift -extension Vector where Element: ~Copyable { - /// Initializes every element in this vector running the given closure value +extension InlineArray where Element: ~Copyable { + /// Initializes every element in this InlineArray running the given closure value /// that returns the element to emplace at the given index. /// /// This will call the closure `count` times, where `count` is the static - /// count of the vector, to initialize every element by passing the closure + /// count of the InlineArray, to initialize every element by passing the closure /// the index of the current element being initialized. The closure is allowed /// to throw an error at any point during initialization at which point the - /// vector will stop initialization, deinitialize every currently initialized + /// InlineArray will stop initialization, deinitialize every currently initialized /// element, and throw the given error back out to the caller. /// /// - Parameter next: A closure that returns an owned `Element` to emplace at /// the passed in index. - public init(with next: (Int) throws(E) -> Element) throws(E) + public init(_ next: (Int) throws(E) -> Element) throws(E) - /// Initializes every element in this vector by running the closure with the + /// Initializes every element in this InlineArray by running the closure with the /// previously initialized element. /// /// This will call the closure 'count' times, where 'count' is the static - /// count of the vector, to initialize every element by passing the closure + /// count of the InlineArray, to initialize every element by passing the closure /// an immutable borrow reference to the previously initialized element. The /// closure is allowed to throw an error at any point during initialization at - /// which point the vector will stop initialization, deinitialize every + /// which point the InlineArray will stop initialization, deinitialize every /// currently initialized element, and throw the given error back out to the /// caller. /// - /// - Parameter first: The first value to insert into the vector which will be + /// - Parameter first: The first value to insert into the InlineArray which will be /// passed to the closure as a borrow. /// - Parameter next: A closure that passes in an immutable borrow reference - /// of the previously initialized element of the vector + /// of the previously initialized element of the InlineArray /// which returns an owned `Element` instance to insert into - /// the vector. + /// the InlineArray. public init( first: consuming Element, next: (borrowing Element) throws(E) -> Element ) throws(E) } -extension Vector where Element: Copyable { - /// Initializes every element in this vector to a copy of the given value. +extension InlineArray where Element: Copyable { + /// Initializes every element in this InlineArray to a copy of the given value. /// - /// - Parameter value: The instance to initialize this vector with. + /// - Parameter value: The instance to initialize this InlineArray with. public init(repeating: Element) } ``` ### Deinitialization and consumption -Once a vector is no longer used, the compiler will implicitly destroy its value. +Once a InlineArray is no longer used, the compiler will implicitly destroy its value. This means that it will do an element by element deinitialization, releasing any class references or calling any `deinit`s on noncopyable elements. ### Generalized `Sequence` and `Collection` APIs -While we aren't conforming `Vector` to `Collection` (more information in future +While we aren't conforming `InlineArray` to `Collection` (more information in future directions), we do want to generalize a lot of APIs that will make this a usable collection type. ```swift -extension Vector where Element: ~Copyable { +extension InlineArray where Element: ~Copyable { public typealias Element = Element public typealias Index = Int @@ -339,70 +339,71 @@ extension Vector where Element: ~Copyable { ) public subscript(_ index: Int) -> Element + public subscript(unchecked index: Int) -> Element } ``` ## Source compatibility -`Vector` is a brand new type in the standard library, so source should still be +`InlineArray` is a brand new type in the standard library, so source should still be compatible. Given the name of this type however, we foresee this clashing with existing user -defined types named `Vector`. This isn't a particular issue though because the +defined types named `InlineArray`. This isn't a particular issue though because the standard library has special shadowing rules which prefer user defined types by -default. Which means in user code with a custom `Vector` type, that type will -always be preferred over the standard library's `Swift.Vector`. By always I +default. Which means in user code with a custom `InlineArray` type, that type will +always be preferred over the standard library's `Swift.InlineArray`. By always I truly mean _always_. Given the following two scenarios: ```swift // MyLib -public struct Vector { +public struct InlineArray { } -print(Vector.self) +print(InlineArray.self) -// error: generic type 'Vector' specialized with too many type parameters +// error: generic type 'InlineArray' specialized with too many type parameters // (got 2, but expected 1) -print(Vector<3, Int>.self) +print(InlineArray<3, Int>.self) ``` -Here, we're exercising the fact that this `MyLib.Vector` has a different generic -signature than `Swift.Vector`, but regardless of that we will prefer `MyLib`'s +Here, we're exercising the fact that this `MyLib.InlineArray` has a different generic +signature than `Swift.InlineArray`, but regardless of that we will prefer `MyLib`'s version even if we supply more generic arguments than it supports. ```swift // MyLib -public struct Vector { +public struct InlineArray { } // MyExecutable main.swift import MyLib -print(Vector.self) // OK +print(InlineArray.self) // OK -// error: generic type 'Vector' specialized with too many type parameters +// error: generic type 'InlineArray' specialized with too many type parameters // (got 2, but expected 1) -print(Vector<3, Int>.self) +print(InlineArray<3, Int>.self) // MyExecutable test.swift -// error: generic type 'Vector' specialized with too few type parameters +// error: generic type 'InlineArray' specialized with too few type parameters // (got 1, but expected 2) -print(Vector.self) +print(InlineArray.self) ``` -And here, we exercise that a module with its own `Vector`, like `MyLib`, will +And here, we exercise that a module with its own `InlineArray`, like `MyLib`, will always prefer its own definition within the module, but even for dependents -who import `MyLib` it will prefer `MyLib.Vector`. For files that don't -explicitly `MyLib`, it will prefer `Swift.Vector`. +who import `MyLib` it will prefer `MyLib.InlineArray`. For files that don't +explicitly `MyLib`, it will prefer `Swift.InlineArray`. ## ABI compatibility -`Vector` is a brand new type in the standard library, so ABI should still be +`InlineArray` is a brand new type in the standard library, so ABI should still be compatible. ## Implications on adoption @@ -424,14 +425,14 @@ were to ship say Swift X.Y with: ```swift @available(SwiftStdlib X.Y) -extension Vector: Equatable where Element: Equatable // & Element: Copyable +extension InlineArray: Equatable where Element: Equatable // & Element: Copyable ``` and later down the road in Swift X.(Y + 1): ```swift @available(SwiftStdlib X.Y) -extension Vector: Equatable where Element: ~Copyable & Equatable +extension InlineArray: Equatable where Element: ~Copyable & Equatable ``` Suddenly, this availability isn't quite right because the conformance that @@ -442,16 +443,16 @@ these conformances until they are fully generalized. ### `Sequence` and `Collection` Similarly, we aren't conforming to `Sequence` or `Collection` either. -While we could conform to these protocols when the element is copyable, `Vector` +While we could conform to these protocols when the element is copyable, `InlineArray` is unlike `Array` in that there are no copy-on-write semantics; it is eagerly copied. Conforming to these protocols would potentially open doors to lots of -implicit copies of the underlying vector instance which could be problematic +implicit copies of the underlying InlineArray instance which could be problematic given the prevalence of generic collection algorithms and slicing behavior. To avoid this potential performance pitfall, we're explicitly not opting into conforming this type to `Sequence` or `Collection`. We do plan to propose new protocols that look like `Sequence` and `Collection` -that avoid implicit copying making them suitable for types like `Vector` and +that avoid implicit copying making them suitable for types like `InlineArray` and containers of noncopyable elements. [SE-0437 Noncopyable Standard Library Primitives](0437-noncopyable-stdlib-primitives.md) goes into more depth about this rationale and mentions that creating new @@ -463,12 +464,12 @@ API we feel confident will be included in any future container protocol. Even if we find that to not be the case, they are still useful API outside of generic collection contexts in their own right. -Remember, one can still iterate a `Vector` instance with the usual `indices` -property (which is what noncopyable vector instances would have had to deal with +Remember, one can still iterate a `InlineArray` instance with the usual `indices` +property (which is what noncopyable InlineArray instances would have had to deal with regardless until new container protocols have been proposed): ```swift -let atomicInts: Vector<3, Atomic> = [Atomic(1), Atomic(2), Atomic(3)] +let atomicInts: InlineArray<3, Atomic> = [Atomic(1), Atomic(2), Atomic(3)] for i in atomicInts.indices { print(atomicInts[i].load(ordering: .relaxed)) @@ -480,7 +481,7 @@ for i in atomicInts.indices { With the recent proposal [SE-0447 Span: Safe Access to Contiguous Storage](0447-span-access-shared-contiguous-storage.md) who defines a safe abstraction over viewing contiguous storage, it would make -sense to define API on `Vector` to be able to get one of these `Span`s. However, +sense to define API on `InlineArray` to be able to get one of these `Span`s. However, the proposal states that: > We could provide `withSpan()` and `withBytes()` closure-taking functions as @@ -493,24 +494,24 @@ the proposal states that: > require lifetime annotations, as initializers do. We are deferring proposing > these extensions until the lifetime annotations are proposed. -All of which is exactly true for the current `Vector` type. We could propose a +All of which is exactly true for the current `InlineArray` type. We could propose a `withSpan` style API now, but it's unclear if that's what we truly want vs. a computed property that returns the span which requires lifetime annotation features. For now, we're deferring such API until a lifetime proposal is proposed and accepted. -### `ExpressibleByVectorLiteral` +### `ExpressibleByInlineArrayLiteral` -While the proposal does propose a literal initialization for `Vector` that +While the proposal does propose a literal initialization for `InlineArray` that doesn't use `ExpressibleByArrayLiteral`, we are intentionally not exposing some -`ExpressibleByVectorLiteral` or similar. It's unclear what this protocol would +`ExpressibleByInlineArrayLiteral` or similar. It's unclear what this protocol would look like because each design has a different semantic guarantee: ```swift -public protocol ExpressibleByVectorLiteral: ~Copyable { +public protocol ExpressibleByInlineArrayLiteral: ~Copyable { associatedtype Element: ~Copyable - init(vectorLiteral: consuming Vector) + init(InlineArrayLiteral: consuming InlineArray) } ``` @@ -518,46 +519,46 @@ This naive approach would satisfy a lot of types like `Array`, `Set`, some hypothetical future noncopyable array, etc. These types actually want a generic count and can allocate just enough space to hold all of those elements. -However, this shape doesn't quite work for `Vector` itself because initializing -a `Vector<4, Int>` should require that the literal has exactly 4 elements. Note +However, this shape doesn't quite work for `InlineArray` itself because initializing +a `InlineArray<4, Int>` should require that the literal has exactly 4 elements. Note that we wouldn't be able to impose a new constraint just for the conformer, so -`Vector` couldn't require that `N == count` and still have this witness the -requirement. Similarly, a `Pair` type could be vector initialized, but only if -the vector has exactly 2 elements. If we had the ability to define +`InlineArray` couldn't require that `N == count` and still have this witness the +requirement. Similarly, a `Pair` type could be InlineArray initialized, but only if +the InlineArray has exactly 2 elements. If we had the ability to define `associatedvalue`, then this makes the conformance pretty trivial for both of these types: ```swift -public protocol ExpressibleByVectorLiteral: ~Copyable { +public protocol ExpressibleByInlineArrayLiteral: ~Copyable { associatedtype Element: ~Copyable associatedvalue count: Int - init(vectorLiteral: consuming Vector) + init(InlineArrayLiteral: consuming InlineArray) } -extension Vector: ExpressibleByVectorLiteral { - init(vectorLiteral: consuming Vector) { ... } +extension InlineArray: ExpressibleByInlineArrayLiteral { + init(InlineArrayLiteral: consuming InlineArray) { ... } } -extension Pair: ExpressibleByVectorLiteral { - init(vectorLiteral: consuming Vector<2, Element>) { ... } +extension Pair: ExpressibleByInlineArrayLiteral { + init(InlineArrayLiteral: consuming InlineArray<2, Element>) { ... } } ``` But even with this design it's unsuitable for `Array` itself because it doesn't want a static count for the literal, it still wants it to be generic. -It would be nice to define something like this either on top of `Vector`, +It would be nice to define something like this either on top of `InlineArray`, parameter packs, or something else that would let us define statically the number of elements we need for literal initialization or be dynamic if we opt to. -### `InlineArray` and `SmallArray` +### `FixedCapacityArray` and `SmallArray` -In the same vein as this type, it may make sense to introduce some `InlineArray` +In the same vein as this type, it may make sense to introduce some `FixedCapacityArray` type which would support appending and removing elements given a fixed-capacity. ```swift -var numbers: InlineArray<4, Int> = [1, 2] +var numbers: FixedCapacityArray<4, Int> = [1, 2] print(numbers.capacity) // 4 print(numbers.count) // 2 numbers.append(3) @@ -568,28 +569,28 @@ numbers.append(5) // error: not enough space ``` This type is significantly different than the type we're proposing because -`Vector` defines a fixed-size meaning you cannot append or remove from it, but +`InlineArray` defines a fixed-size meaning you cannot append or remove from it, but it also requires that every single element is initialized. There must never be -an uninitialized element within a `Vector`, however for `InlineArray` this is +an uninitialized element within a `InlineArray`, however for `FixedCapacityArray` this is not true. It would act as a regular array with an initialized prefix and an uninitialized suffix, it would be inline allocated (stack allocated for locals, heap allocated if it's a class member, etc.), and it would not be growable. The difficulty in proposing such a type right now is that we have no way of -informing the compiler what parts of `InlineArray` are initialized and what +informing the compiler what parts of `FixedCapacityArray` are initialized and what parts are not. This is critical for copy operations, move operations, and destroy operations. Assuming that an uninitialized element is initialized and attempting to perform any of these operations on it may lead to runtime crashes which is definitely undesirable. -Once we have `InlineArray` and some hypothetical noncopyable heap allocated +Once we have `FixedCapacityArray` and some hypothetical noncopyable heap allocated array type (which [SE-0437 Noncopyable Standard Library Primitives](0437-noncopyable-stdlib-primitives.md) dons as `HypoArray` as a placeholder), it should be very trivial to define a `SmallArray` type similar to the one found in LLVM APIs `llvm::SmallVector`. ```swift public enum SmallArray: ~Copyable { - case small(InlineArray) + case small(FixedCapacityArray) case large(HypoArray) } ``` @@ -602,7 +603,7 @@ capacity and would fall back to a dynamic heap allocation. We feel that this type will become as fundamental as `Array` and `Dictionary` both of which have syntactic sugar for declaring a type of them, `[T]` for `Array` and `[K: V]` for `Dictionary`. It may make sense to define something -similar for `Vector`, however we leave that as a future direction as the +similar for `InlineArray`, however we leave that as a future direction as the spelling for such syntax is not critical to landing this type. It should be fairly trivial to propose such a syntax in the future either via a @@ -617,11 +618,11 @@ Some syntax suggestions: * `[T; N]` (from Rust) Note that it may make more sense to have the length appear before the type. I -discuss this more in depth in [Reorder the generic arguments](#reorder-the-generic-arguments-vectort-n-instead-of-vectorn-t). +discuss this more in depth in [Reorder the generic arguments](#reorder-the-generic-arguments-InlineArrayt-n-instead-of-InlineArrayn-t). ### C Interop changes -With the introduction of `Vector`, we have a unique opportunity to fix another +With the introduction of `InlineArray`, we have a unique opportunity to fix another pain point within the language with regards to C interop. Currently, the Swift compiler imports a C array of type `T[24]` as a tuple of `T` with 24 elements. Previously, this was really the only representation that the compiler could pick @@ -680,7 +681,7 @@ Having to resort to using very unsafe API to do anything useful with imported C arrays is not something a memory safe language like Swift should be in the business of. -Ideally we could migrate the importer from using tuples to this new `Vector` +Ideally we could migrate the importer from using tuples to this new `InlineArray` type, however that would be massively source breaking. A previous revision of this proposal proposed an _upcoming_ feature flag that modules can opt into, but this poses issues with the current importer implementation with regards to @@ -688,18 +689,37 @@ inlinable code. Another idea was to import struct fields with C array types twice, one with the existing name with a tuple type (as to not break source) and another with some -`Vector` suffix in the name with the `Vector` type. This works pretty well for +`InlineArray` suffix in the name with the `InlineArray` type. This works pretty well for struct fields and globals, but it leaves fields and functions who have pointers to C arrays in question as well (spelt `char (*x)[4]`). Do we import such functions twice using a similar method of giving it a different name? Such a solution would also incur a longer deprecation period to eventually having just -`Vector` be imported and no more tuples. +`InlineArray` be imported and no more tuples. We're holding off on any C interop changes here as there are still lots of open questions as to what the best path forward is. ## Alternatives considered +### Reorder the generic arguments (`InlineArray` instead of `InlineArray`) + +If we directly followed existing APIs from C++, then obviously the length should +follow the element type. However we realized that when reading this type aloud, +it's "a InlineArray of 3 integers" for example instead of "a InlineArray of integers of +size 3". It gets more interesting the more dimensions you add. +Consider an MxN matrix. In C, you'd write this as `T[N][M]` but index it as +`[m][n]`. We don't want to introduce that sort of confusion (which is a good +argument against `T[N]` as a potential syntax sugar for this type), so the +length being before the underlying element makes the most sense at least for any +potential sugared form. `[M * [N * T]]` would be indexed directly as it is spelt +out in the sugared form, `[m][n]`. In light of that, we wouldn't want the sugar +form to have a different ordering than the generic type itself leading us to +believe that the length must be before the element type. + +## Revisions + +Previously, this type was named `Vector`, but has since been renamed to `InlineArray`. + ### A name other than `Vector` For obvious reasons, we cannot name this type `Swift.Array` to match the @@ -739,21 +759,6 @@ the mathematical term of art. If there was any type we could add to the standard library whose name could be `Vector`, it must be this one. -### Reorder the generic arguments (`Vector` instead of `Vector`) - -If we directly followed existing APIs from C++, then obviously the length should -follow the element type. However we realized that when reading this type aloud, -it's "a vector of 3 integers" for example instead of "a vector of integers of -size 3". It gets more interesting the more dimensions you add. -Consider an MxN matrix. In C, you'd write this as `T[N][M]` but index it as -`[m][n]`. We don't want to introduce that sort of confusion (which is a good -argument against `T[N]` as a potential syntax sugar for this type), so the -length being before the underlying element makes the most sense at least for any -potential sugared form. `[M * [N * T]]` would be indexed directly as it is spelt -out in the sugared form, `[m][n]`. In light of that, we wouldn't want the sugar -form to have a different ordering than the generic type itself leading us to -believe that the length must be before the element type. - ## Acknowledgments I would like the thank the following people for helping in the design process From e8c98f0f12fd5b3c4d184944982149be22db503b Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 21 Jan 2025 13:09:11 -0800 Subject: [PATCH 4040/4563] Propose MutableSpan and MutableRawSpan --- proposals/nnnn-MutableSpan.md | 498 ++++++++++++++++++++++++++++++++++ 1 file changed, 498 insertions(+) create mode 100644 proposals/nnnn-MutableSpan.md diff --git a/proposals/nnnn-MutableSpan.md b/proposals/nnnn-MutableSpan.md new file mode 100644 index 0000000000..9627e80ca5 --- /dev/null +++ b/proposals/nnnn-MutableSpan.md @@ -0,0 +1,498 @@ +# MutableSpan and MutableRawSpan: delegate mutations of contiguous memory + +* Proposal: TBD +* Author: [Guillaume Lessard](https://github.com/glessard) +* Review Manager: TBD +* Status: **Pitch** +* Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) +* Implementation: "Future" target of [swift-collections](https://github.com/apple/swift-collections/tree/future) +* Review: [Pitch](https://forums.swift.org/) + +[SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md +[SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md +[SE-0456]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0456-stdlib-span-properties.md +[PR-2305]: https://github.com/swiftlang/swift-evolution/pull/2305 +[SE-0453]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0453-vector.md +[SE-0223]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0223-array-uninitialized-initializer.md +[SE-0176]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md + +## Introduction + +We recently [introduced][SE-0447] the `Span` and `RawSpan` types, providing read-only access to borrowed memory shared. This proposal adds mutations of exclusively-borrowed memory with `MutableSpan` and `MutableRawSpan`. + +## Motivation + +Many standard library container types can provide direct access to modify their internal representation. Up to now, it has only been possible to do so in an unsafe way. The standard library provides this unsafe functionality with closure-taking functions such as `withUnsafeMutableBufferPointer()` and `withContiguousMutableStorageIfAvailable()`. + +These functions have a few different drawbacks, most prominently their reliance on unsafe types, which makes them unpalatable in security-conscious environments. We continue addressing these issues with `MutableSpan` and `MutableRawSpan`, new non-copyable and non-escapable types that manage respectively mutations of typed and untyped memory. + +In addition to the new types, we will propose adding new API some standard library types to take advantage of `MutableSpan` and `MutableRawSpan`. + +## Proposed solution +We previously introduced `Span` to provide shared read-only access to containers. A question can be raised as to whether this same type could be used for mutations. We cannot, due to the [law of exclusivity][SE-0176]. `Span` is copyable, and it should be copyable in order to properly model read access under the law of exclusivity: a value can be simultaneously accessed through multiple read-only accesses. Mutations, on the other hand, require _exclusive access_. Exclusive access cannot be modeled through a copyable type, since a copy of the value representing the access would violate exclusivity. We therefore need a type separate from `Span` in order to model mutations. + +#### MutableSpan + +`MutableSpan` allows delegating mutations of a type's contiguous internal representation, by providing access to an exclusively-borrowed view of a range of contiguous, initialized memory. `MutableSpan` relies on guarantees that it has exclusive access to the range of memory it represents, and that the memory it represents will remain valid for the duration of the access. These guarantee data race safety and temporal safety. Like `Span`, `MutableSpan` performs bounds-checking on every access to preserve spatial safety. + +A `MutableSpan` provided by a container represents a mutation of that container, via an exclusive borrow. Mutations are implemented by mutating operations, which let the compiler statically enforce exclusivity. + +#### MutableRawSpan + +`MutableRawSpan` allows delegating mutations to memory representing possibly heterogeneously-typed values, such as memory intended for encoding. It makes the same safety guarantees as `MutableSpan`. A `MutableRawSpan` can be obtained from a `MutableSpan` whose `Element` is `BitwiseCopyable`. + +#### Extensions to standard library types + +The standard library will provide `mutableSpan` computed properties. These return lifetime-dependent `MutableSpan` instances, and represent a mutation of the instance that provided them. These computed properties are the safe and composable replacements for the existing `withUnsafeMutableBufferPointer` closure-taking functions. For example, + +```swift +func(_ array: inout Array) { + var ms = array.mutableSpan + modify(&ms) // call function that mutates a MutableSpan + // array.append(2) // attempt to modify `array` would be an error here + _ = consume ms // access to `array` via `ms` ends here + array.append(1) +} +``` + +These computed properties represent a case of lifetime relationships between two bindings that wasn't covered in [SE-0456][SE-0456]. There we defined lifetime relationships for computed property getters of non-escapable and copyable types (`~Escapable & Copyable`). We now need to define them for properties of non-escapable and non-copyable types (`~Escapable & ~Copyable`). A `~Escapable & ~Copyable` value borrows another binding; if this borrow is also a mutation then it is an exclusive borrow. The scope of the borrow, whether or not it is exclusive, extends until the last use of the dependent binding. + +## Detailed Design + +#### MutableSpan + +`MutableSpan` is a simple representation of a region of initialized memory. It is non-copyable in order to enforce exclusive access for mutations of its memory, as required by the law of exclusivity: + +````swift +@frozen +public struct MutableSpan: ~Copyable, ~Escapable { + internal var _start: UnsafeMutableRawPointer? + internal var _count: Int +} + +extension MutableSpan: @unchecked Sendable where Element: Sendable {} +```` + +We store a `UnsafeMutableRawPointer` value internally in order to explicitly support reinterpreted views of memory as containing different types of `BitwiseCopyable` elements. Note that the the optionality of the pointer does not affect usage of `MutableSpan`, since accesses are bounds-checked and the pointer is only dereferenced when the `MutableSpan` isn't empty, when the pointer cannot be `nil`. + +```swift +extension MutableSpan where Element: ~Copyable { + /// The number of initialized elements in this `MutableSpan`. + var count: Int { get } + + /// A Boolean value indicating whether the span is empty. + var isEmpty: Bool { get } + + /// The type that represents a position in a `MutableSpan`. + typealias Index = Int + + /// The range of indices valid for this `MutableSpan`. + var indices: Range { get } + + /// Accesses the element at the specified position. + subscript(_ index: Index) -> Element { borrowing read; mutate } + + /// Exchange the elements at the two given offsets + mutating func swapAt(_ i: Index, _ j: Index) +} +``` + +Like `Span` before it, `MutableSpan` does not conform to `Collection` or `MutableCollection`. These two protocols assume their conformers and elements are copyable, and as such are not compatible with a non-copyable type such as `MutableSpan`. A later proposal will consider generalized containers. + +The subscript uses a borrowing accessor for read-only element access, and a mutate accessor for element mutation. The read-only borrow is a read access to the entire `MutableSpan` for the duration of the access to the element. The `mutate` accessor is an exclusive access to the entire `MutableSpan` for the duration of the mutation of the element. + +`MutableSpan` uses offset-based indexing. The first element of a given span is always at offset 0, and its last element is always at position `count-1`. + +As a side-effect of not conforming to `Collection` or `Sequence`, `MutableSpan` is not directly supported by `for` loops at this time. It is, however, easy to use in a `for` loop via indexing: + +```swift +for i in myMutableSpan.indices { + mutatingFunction(&myMutableSpan[i]) +} +``` + +##### `MutableSpan` API: + +Initializers, required for library adoption, will be proposed alongside [lifetime annotations][PR-2305]; for details, see "[Initializers](#initializers)" in the [future directions](#Directions) section. + +```swift +extension MutableSpan where Element: ~Copyable { + @unsafe + subscript(unchecked position: Index) -> Element { borrowing read; mutate } + + @unsafe + mutating func swapAt(unchecked i: Index, unchecked j: Index) + + var storage: Span { borrowing get } +} +``` +##### Bulk updating of a `MutableSpan`'s elements: + +We include functions to perform bulk copies into the memory represented by a `MutableSpan`. Updating a `MutableSpan` from known-sized sources (such as `Collection` or `Span`) copies every element of a source. It is an error to do so when there is the span is too short to contain every element from the source. Updating a `MutableSpan` from `Sequence` or `IteratorProtocol` instances will copy as many items as possible, either until the input is empty or until the operation has updated the item at the last index. + +```swift +extension MutableSpan { + mutating func update( + startingAt offset: Index = 0, + repeating repeatedValue: Element + ) + + mutating func update( + startingAt offset: Index = 0, + from source: S + ) -> (unwritten: S.Iterator, index: Index) where S.Element == Element + + mutating func update( + startingAt offset: Index = 0, + from elements: inout some IteratorProtocol + ) -> Index + + mutating func update( + startingAt offset: Index = 0, + fromContentsOf source: some Collection + ) -> Index + + mutating func update( + startingAt offset: Index = 0, + fromContentsOf source: Span + ) -> Index + + mutating func update( + startingAt offset: Index = 0, + fromContentsOf source: borrowing Self + ) -> Index +} + + mutating func moveUpdate( + startingAt offset: Index = 0, + fromContentsOf source: UnsafeMutableBufferPointer + ) -> Index +} + +extension MutableSpan { + mutating func moveUpdate( + startingAt offset: Index = 0, + fromContentsOf source: Slice> + ) -> Index +} +``` +##### Interoperability with unsafe code: + +```swift +extension MutableSpan where Element: ~Copyable { + func withUnsafeBufferPointer( + _ body: (_ buffer: UnsafeBufferPointer) throws(E) -> Result + ) throws(E) -> Result + + mutating func withUnsafeMutableBufferPointer( + _ body: (_ buffer: UnsafeMutableBufferPointer) throws(E) -> Result + ) throws(E) -> Result +} + +extension MutableSpan where Element: BitwiseCopyable { + func withUnsafeBytes( + _ body: (_ buffer: UnsafeRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result + + mutating func withUnsafeMutableBytes( + _ body: (_ buffer: UnsafeMutableRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result +} +``` +These functions use a closure to define the scope of validity of `buffer`, ensuring that the underlying `MutableSpan` and the binding it depends on both remain valid through the end of the closure. They have the same shape as the equivalents on `Array` because they fulfill the same function, namely to keep the underlying binding alive. + +#### MutableRawSpan + +`MutableRawSpan` is similar to `MutableSpan`, but reperesents untyped initialized bytes. `MutableRawSpan` specifically supports encoding and decoding applications. Its API supports `unsafeLoad(as:)` and `storeBytes(of: as:)`, as well as a variety of bulk copying operations. + +##### `MutableRawSpan` API: + +```swift +@frozen +public struct MutableRawSpan: ~Copyable, ~Escapable { + internal var _start: UnsafeMutableRawPointer? + internal var _count: Int +} + +extension MutableRawSpan: @unchecked Sendable +``` + +Initializers, required for library adoption, will be proposed alongside [lifetime annotations][PR-2305]; for details, see "[Initializers](#initializers)" in the [future directions](#Directions) section. + +```swift +extension MutableRawSpan { + /// The number of bytes in the span. + var byteCount: Int { get } + + /// A Boolean value indicating whether the span is empty. + var isEmpty: Bool { get } + + /// The range of valid byte offsets into this `RawSpan` + var byteOffsets: Range { get } +} + +``` + +##### Accessing and modifying the memory of a `MutableRawSpan`: + +The basic operations available on `RawSpan` are available for `MutableRawSpan`. These operations are not type-safe, in that the loaded value returned by the operation can be invalid, and violate type invariants. Some types have a property that makes the `unsafeLoad(as:)` function safe, but we don't have a way to [formally identify](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md#SurjectiveBitPattern) such types at this time. + +```swift +extension MutableRawSpan { + @unsafe + func unsafeLoad( + fromByteOffset offset: Int = 0, as: T.Type + ) -> T + + @unsafe + func unsafeLoadUnaligned( + fromByteOffset offset: Int = 0, as: T.Type + ) -> T + + @unsafe + func unsafeLoad( + fromUncheckedByteOffset offset: Int, as: T.Type + ) -> T + + @unsafe + func unsafeLoadUnaligned( + fromUncheckedByteOffset offset: Int, as: T.Type + ) -> T +} +``` + +To these, `MutableRawSpan` adds functions to store the bytes of a `BitwiseCopyable` value: + +```swift +extension MutableRawSpan { + func storeBytes( + of value: T, toByteOffset offset: Int = 0, as type: T.Type + ) + + @unsafe + func storeBytes( + of value: T, toUncheckedByteOffset offset: Int, as type: T.Type + ) +} +``` + +We include functions to perform bulk copies into the memory represented by a `MutableRawSpan`. Updating a `MutableRawSpan` from a `Collection` or a `Span` copies every element of a source. It is an error to do so when there is are not enough bytes in the span to contain every element from the source. Updating `MutableRawSpan` from `Sequence` or `IteratorProtocol` instance copies as many items as possible, either until the input is empty or until there are not enough bytes in the span to store another element. +```swift +extension MutableRawSpan { + mutating func update( + startingAt byteOffset: Int = 0, + from source: S + ) -> (unwritten: S.Iterator, byteOffset: Int) where S.Element: BitwiseCopyable + + mutating func update( + startingAt byteOffset: Int = 0, + from elements: inout some IteratorProtocol + ) -> Int + + mutating func update( + startingAt byteOffset: Int = 0, + fromContentsOf source: C + ) -> Int where C.Element: BitwiseCopyable + + mutating func update( + startingAt byteOffset: Int = 0, + fromContentsOf source: Span + ) -> Int + + mutating func update( + startingAt byteOffset: Int = 0, + fromContentsOf source: borrowing MutableSpan + ) -> Int + + mutating func update( + startingAt byteOffset: Int = 0, + fromContentsOf source: RawSpan + ) -> Int + + mutating func update( + startingAt byteOffset: Int = 0, + fromContentsOf source: borrowing MutableRawSpan + ) -> Int +} +``` + +##### Interoperability with unsafe code: + +```swift +extension MutableRawSpan { + func withUnsafeBytes( + _ body: (_ buffer: UnsafeRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result + + mutating func withUnsafeMutableBytes( + _ body: (_ buffer: UnsafeMutableRawBufferPointer) throws(E) -> Result + ) throws(E) -> Result +} +``` +These functions use a closure to define the scope of validity of `buffer`, ensuring that the underlying `MutableSpan` and the binding it depends on both remain valid through the end of the closure. They have the same shape as the equivalents on `Array` because they fulfill the same purpose, namely to keep the underlying binding alive. + +##### Accessing and mutating the raw bytes of a `MutableSpan` + +```swift +extension MutableSpan where Element: BitwiseCopyable { + var mutableBytes: MutableRawSpan { mutating get } +} +``` + + + +#### Extensions to Standard Library types + +A `mutating` computed property getter defined on any type and returning a `~Escapable & ~Copyable` value establishes an exclusive borrowing lifetime relationship of the returned value on the callee's binding. As long as the returned value exists, then the callee's binding remains borrowed and cannot be accessed in any other way. + +A `nonmutating` computed property getter returning a `~Escapable & ~Copyable` value establishes a borrowing lifetime relationship, as if returning a `~Escapable & Copyable` value (see [SE-0456][SE-0456].) + +The standard library will provide `mutableSpan` computed properties. These return lifetime-dependent `MutableSpan` instances. These computed properties are the safe and composable replacements for the existing `withUnsafeMutableBufferPointer` closure-taking functions. + +```swift +extension Array { + var mutableSpan: MutableSpan { mutating get } +} + +extension ContiguousArray { + var mutableSpan: MutableSpan { mutating get } +} + +extension ArraySlice { + var mutableSpan: MutableSpan { mutating get } +} + +extension InlineArray { + var mutableSpan: MutableSpan { mutating get } +} + +extension CollectionOfOne { + var mutableSpan: MutableSpan { mutating get } +} +``` + + + +#### Extensions to unsafe buffer types + +We hope that `MutableSpan` and `MutableRawSpan` will become the standard ways to delegate mutations of shared contiguous memory in Swift. Many current API delegate mutations with closure-based functions that receive an `UnsafeMutableBufferPointer` parameter to do this. We will provide ways to unsafely obtain `MutableSpan` instances from `UnsafeMutableBufferPointer` and `MutableRawSpan` instances from `UnsafeMutableRawBufferPointer`, in order to bridge these unsafe types to newer, safer contexts. + +```swift +extension UnsafeMutableBufferPointer { + var mutableSpan: MutableSpan { get } +} + +extension UnsafeMutableRawBufferPointer { + var mutableBytes: MutableRawSpan { get } +} +``` + +These unsafe conversions returns a value whose lifetime is dependent on the _binding_ of the `UnsafeMutable[Raw]BufferPointer`. This dependency does not keep the underlying memory alive. As is usual where the `UnsafePointer` family of types is involved, the programmer must ensure the memory remains allocated while it is in use. Additionally, the following invariants must remain true for as long as the `MutableSpan` or `MutableRawSpan` value exists: + + - The underlying memory remains initialized. + - The underlying memory is not accessed through another means. + +Failure to maintain these invariants results in undefined behaviour. + +#### Extensions to `Foundation.Data` + +While the `swift-foundation` package and the `Foundation` framework are not governed by the Swift evolution process, `Data` is similar in use to standard library types, and the project acknowledges that it is desirable for it to have similar API when appropriate. Accordingly, we plan to propose the following additions to `Foundation.Data`: + +```swift +extension Foundation.Data { + // Mutate this `Data`'s bytes through a `MutableSpan` + var mutableSpan: MutableSpan { mutating get } + + // Mutate this `Data`'s bytes through a `MutableRawSpan` + var mutableBytes: MutableRawSpan { mutating get } +} +``` + +#### Performance + +The `mutableSpan` and `mutableBytes` properties should be performant and return their `MutableSpan` or `MutableRawSpan` with very little work, in O(1) time. In copy-on-write types, however, obtaining a `MutableSpan` is the start of the mutation, and if the backing buffer is not uniquely reference a copy must be made ahead of returning the `MutableSpan`. + +Note that `MutableSpan` incurs no special behaviour for bridged types, since mutations always require a defensive copy of data bridged from Objective-C data structures. + +## Source compatibility + +This proposal is additive and source-compatible with existing code. + +## ABI compatibility + +This proposal is additive and ABI-compatible with existing code. + +## Implications on adoption + +The additions described in this proposal require a new version of the Swift standard library and runtime. + +## Alternatives considered + +#### Adding `withMutableSpan()` closure-taking functions + +The `mutableSpan` and `mutableBytes` properties aim to be safe replacements for the `withUnsafeMutableBufferPointer()` and `withUnsafeMutableBytes()` closure-taking functions. We could consider `withMutableSpan()` and `withMutableBytes()` closure-taking functions that would provide a quicker migration away from the older unsafe functions. We do not believe the closure-taking functions are desirable in the long run. In the short run, there may be a desire to clearly mark the scope where a `MutableSpan` instance is used. The default method would be to explicitly consume a `MutableSpan` instance: + +```swift +var a = ContiguousArray(0..<8) +var span = a.mutableSpan +modify(&span) +_ = consume span +a.append(8) +``` + +During the evolution of Swift, we have learned that closure-based API are difficult to compose, especially with one another. They can also require alterations to support new language features. For example, the generalization of closure-taking API for non-copyable values as well as typed throws is ongoing; adding more closure-taking API may make future feature evolution more labor-intensive. By instead relying on returned values, whether from computed properties or functions, we build for **greater** composability. Use cases where this approach falls short should be reported as enhancement requests or bugs. + +#### Omitting extensions to `UnsafeBufferPointer` and related types + +We could omit the extensions to `UnsafeMutableBufferPointer` and related types, and rely instead of future `MutableSpan` and `MutableRawSpan` initializers. The initializers can have the advantage of being able to communicate semantics (somewhat) through their parameter labels. However, they also have a very different shape than the `storage` computed properties we are proposing for the safe types such as `Array`. We believe that the adding the same API on both safe and unsafe types is advantageous, even if the preconditions for the properties cannot be statically enforced. + +## Future directions + +Note: The future directions stated in [SE-0447](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md#Directions) apply here as well. + +#### Initializing and returning `MutableSpan` instances + +`MutableSpan` represents a region of memory and, as such, must be initialized using an unsafe pointer. This is an unsafe operation which will typically be performed internally to a container's implementation. In order to bridge to safe code, these initializers require new annotations that indicate to the compiler how the newly-created `Span` can be used safely. + +These annotations have been [pitched][PR-2305-pitch] and, after revision, are expected to be pitched again soon. `MutableSpan` initializers using lifetime annotations will be proposed alongside the annotations themselves. + +#### Functions providing variants of `MutableRawSpan` to `MutableSpan` + +`MutableSpan`s representing subsets of consecutive elements could be extracted out of a larger `MutableSpan` with an API similar to the `extracting()` functions recently added to `UnsafeBufferPointer` in support of non-copyable elements: + +```swift +extension MutableSpan where Element: ~Copyable { + public mutating func extracting(_ bounds: Range) -> Self +} +``` + +These functions would require a lifetime dependency annotation. + +Similarly, a `MutableRawSpan` could provide a function to mutate a range of its bytes as a typed `MutableSpan`: + +```swift +extension MutableRawSpan { + @unsafe + public mutating func unsafeMutableView(as type: T.Type) -> MutableSpan +} +``` +We are subsetting functions that require lifetime annotations until such annotations are [proposed][PR-2305]. + +#### Splitting `MutableSpan` instances + +It is desirable to have a way to split a `MutableSpan` in multiple parts, for divide-and-conquer algorithms or other reasons: + +```swift +extension MutableSpan where Element: ~Copyable { + func split(at index: Index) -> (part1: Self, part2: Self) +} +``` + +Unfortunately, tuples do not support non-copyable values yet. We may be able to use the new `Vector`/`Slab`/`InlineArray` type proposed in [SE-0453][SE-0453], but destructuring its non-copyable elements remains a challenge. Solving this issue for `Span` as well as `MutableSpan` is a top priority. + +#### Delegated initialization with `OutputSpan` + +Some data structures can delegate initialization of parts of their owned memory. The standard library added the `Array` initializer `init(unsafeUninitializedCapacity:initializingWith:)` in [SE-0223][SE-0223]. This initializer relies on `UnsafeMutableBufferPointer` and correct usage of initialization primitives. We should present a simpler and safer model of initialization by leveraging non-copyability and non-escapability. + +## Acknowledgements + From 2f625f9cb3b54f2759850622059a937a16720ab0 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 7 Feb 2025 13:54:35 -0800 Subject: [PATCH 4041/4563] Edits, feedback responses --- proposals/nnnn-MutableSpan.md | 95 +++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/proposals/nnnn-MutableSpan.md b/proposals/nnnn-MutableSpan.md index 9627e80ca5..70a76f6172 100644 --- a/proposals/nnnn-MutableSpan.md +++ b/proposals/nnnn-MutableSpan.md @@ -12,6 +12,7 @@ [SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md [SE-0456]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0456-stdlib-span-properties.md [PR-2305]: https://github.com/swiftlang/swift-evolution/pull/2305 +[SE-0437]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0437-noncopyable-stdlib-primitives.md [SE-0453]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0453-vector.md [SE-0223]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0223-array-uninitialized-initializer.md [SE-0176]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md @@ -29,13 +30,13 @@ These functions have a few different drawbacks, most prominently their reliance In addition to the new types, we will propose adding new API some standard library types to take advantage of `MutableSpan` and `MutableRawSpan`. ## Proposed solution -We previously introduced `Span` to provide shared read-only access to containers. A question can be raised as to whether this same type could be used for mutations. We cannot, due to the [law of exclusivity][SE-0176]. `Span` is copyable, and it should be copyable in order to properly model read access under the law of exclusivity: a value can be simultaneously accessed through multiple read-only accesses. Mutations, on the other hand, require _exclusive access_. Exclusive access cannot be modeled through a copyable type, since a copy of the value representing the access would violate exclusivity. We therefore need a type separate from `Span` in order to model mutations. +We introduced `Span` to provide shared read-only access to containers. We cannot use `Span` to also model container mutations, due to the [law of exclusivity][SE-0176]. `Span` is copyable, and must be copyable in order to properly model read access under the law of exclusivity: a value can be simultaneously accessed through multiple read-only accesses. Mutations, on the other hand, require _exclusive access_. Exclusive access cannot be modeled with a copyable type, since a copy of the value representing the access would violate exclusivity by adding a second access. We therefore need a non-copyable type separate from `Span` in order to model mutations. #### MutableSpan -`MutableSpan` allows delegating mutations of a type's contiguous internal representation, by providing access to an exclusively-borrowed view of a range of contiguous, initialized memory. `MutableSpan` relies on guarantees that it has exclusive access to the range of memory it represents, and that the memory it represents will remain valid for the duration of the access. These guarantee data race safety and temporal safety. Like `Span`, `MutableSpan` performs bounds-checking on every access to preserve spatial safety. +`MutableSpan` allows delegating mutations of a type's contiguous internal representation, by providing access to an exclusively-borrowed view of a range of contiguous, initialized memory. `MutableSpan` relies on guarantees that it has exclusive access to the range of memory it represents, and that the memory it represents will remain valid for the duration of the access. These provide data race safety and temporal safety. Like `Span`, `MutableSpan` performs bounds-checking on every access to preserve spatial safety. -A `MutableSpan` provided by a container represents a mutation of that container, via an exclusive borrow. Mutations are implemented by mutating operations, which let the compiler statically enforce exclusivity. +A `MutableSpan` provided by a container represents a mutation of that container, via an exclusive borrow. Mutations are implemented by mutating functions and subscripts, which let the compiler statically enforce exclusivity. #### MutableRawSpan @@ -55,7 +56,7 @@ func(_ array: inout Array) { } ``` -These computed properties represent a case of lifetime relationships between two bindings that wasn't covered in [SE-0456][SE-0456]. There we defined lifetime relationships for computed property getters of non-escapable and copyable types (`~Escapable & Copyable`). We now need to define them for properties of non-escapable and non-copyable types (`~Escapable & ~Copyable`). A `~Escapable & ~Copyable` value borrows another binding; if this borrow is also a mutation then it is an exclusive borrow. The scope of the borrow, whether or not it is exclusive, extends until the last use of the dependent binding. +These computed properties represent a case of lifetime relationships not covered in [SE-0456][SE-0456]. In SE-0456 we defined lifetime relationships for computed property getters of non-escapable and copyable types (`~Escapable & Copyable`). We propose defining them for properties of non-escapable and non-copyable types (`~Escapable & ~Copyable`). A `~Escapable & ~Copyable` value borrows another binding; if this borrow is also a mutation then it is an exclusive borrow. The scope of the borrow, whether or not it is exclusive, extends until the last use of the dependent binding. ## Detailed Design @@ -75,6 +76,8 @@ extension MutableSpan: @unchecked Sendable where Element: Sendable {} We store a `UnsafeMutableRawPointer` value internally in order to explicitly support reinterpreted views of memory as containing different types of `BitwiseCopyable` elements. Note that the the optionality of the pointer does not affect usage of `MutableSpan`, since accesses are bounds-checked and the pointer is only dereferenced when the `MutableSpan` isn't empty, when the pointer cannot be `nil`. +Initializers, required for library adoption, will be proposed alongside [lifetime annotations][PR-2305]; for details, see "[Initializers](#initializers)" in the [future directions](#Directions) section. + ```swift extension MutableSpan where Element: ~Copyable { /// The number of initialized elements in this `MutableSpan`. @@ -90,10 +93,14 @@ extension MutableSpan where Element: ~Copyable { var indices: Range { get } /// Accesses the element at the specified position. - subscript(_ index: Index) -> Element { borrowing read; mutate } + subscript(_ index: Index) -> Element { borrow; mutate } + // accessor syntax from accessors roadmap (https://forums.swift.org/t/76707) /// Exchange the elements at the two given offsets mutating func swapAt(_ i: Index, _ j: Index) + + /// Borrow the underlying memory for read-only access + var span: Span { borrowing get } } ``` @@ -111,24 +118,33 @@ for i in myMutableSpan.indices { } ``` -##### `MutableSpan` API: +##### Unchecked access to elements: -Initializers, required for library adoption, will be proposed alongside [lifetime annotations][PR-2305]; for details, see "[Initializers](#initializers)" in the [future directions](#Directions) section. +The `subscript` mentioned above always checks the bounds of the `MutableSpan` before allowing access to the memory, preventing out-of-bounds accesses. We also provide an unchecked variant of the `subscript` and of the `swapAt` function as an alternative for situations where bounds-checking is costly and has already been performed: ```swift extension MutableSpan where Element: ~Copyable { + /// Accesses the element at the specified `position`. + /// + /// This subscript does not validate `position`; this is an unsafe operation. + /// + /// - Parameter position: The offset of the element to access. `position` + /// must be greater or equal to zero, and less than `count`. @unsafe - subscript(unchecked position: Index) -> Element { borrowing read; mutate } + subscript(unchecked position: Index) -> Element { borrow; mutate } + /// Exchange the elements at the two given offsets + /// + /// This function does not validate `i` or `j`; this is an unsafe operation. @unsafe - mutating func swapAt(unchecked i: Index, unchecked j: Index) - - var storage: Span { borrowing get } + mutating func swapAt(unchecked i: Index, unchecked j: Index) } ``` ##### Bulk updating of a `MutableSpan`'s elements: -We include functions to perform bulk copies into the memory represented by a `MutableSpan`. Updating a `MutableSpan` from known-sized sources (such as `Collection` or `Span`) copies every element of a source. It is an error to do so when there is the span is too short to contain every element from the source. Updating a `MutableSpan` from `Sequence` or `IteratorProtocol` instances will copy as many items as possible, either until the input is empty or until the operation has updated the item at the last index. +We include functions to perform bulk copies of elements into the memory represented by a `MutableSpan`. Updating a `MutableSpan` from known-sized sources (such as `Collection` or `Span`) copies every element of a source. It is an error to do so when there is the span is too short to contain every element from the source. Updating a `MutableSpan` from `Sequence` or `IteratorProtocol` instances will copy as many items as possible, either until the input is empty or until the operation has updated the item at the last index. + +**Note:** This set of functions is sufficiently complete in functionality, but uses a minimal approach to slicing. This is only one of many possible approaches to slicing `MutableSpan`. We could revive the option of using a `some RangeExpression` parameter, or we could use the return value of a `func extracting(_: some RangeExpression)` such as was [recently added][SE-0437] to `UnsafeBufferPointer`. The latter option in combination with `mutating` functions requires the use of intermediate bindings. This section may change in response to feedback and our investigations. ```swift extension MutableSpan { @@ -163,6 +179,7 @@ extension MutableSpan { ) -> Index } +extension MutableSpan where Element: ~Copyable { mutating func moveUpdate( startingAt offset: Index = 0, fromContentsOf source: UnsafeMutableBufferPointer @@ -203,7 +220,7 @@ These functions use a closure to define the scope of validity of `buffer`, ensur #### MutableRawSpan -`MutableRawSpan` is similar to `MutableSpan`, but reperesents untyped initialized bytes. `MutableRawSpan` specifically supports encoding and decoding applications. Its API supports `unsafeLoad(as:)` and `storeBytes(of: as:)`, as well as a variety of bulk copying operations. +`MutableRawSpan` is similar to `MutableSpan`, but represents untyped initialized bytes. `MutableRawSpan` specifically supports encoding and decoding applications. Its API supports `unsafeLoad(as:)` and `storeBytes(of: as:)`, as well as a variety of bulk copying operations. ##### `MutableRawSpan` API: @@ -230,12 +247,26 @@ extension MutableRawSpan { /// The range of valid byte offsets into this `RawSpan` var byteOffsets: Range { get } } - ``` ##### Accessing and modifying the memory of a `MutableRawSpan`: -The basic operations available on `RawSpan` are available for `MutableRawSpan`. These operations are not type-safe, in that the loaded value returned by the operation can be invalid, and violate type invariants. Some types have a property that makes the `unsafeLoad(as:)` function safe, but we don't have a way to [formally identify](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md#SurjectiveBitPattern) such types at this time. +`MutableRawSpan` supports storing the bytes of a `BitwiseCopyable` value to its underlying memory: + +```swift +extension MutableRawSpan { + mutating func storeBytes( + of value: T, toByteOffset offset: Int = 0, as type: T.Type + ) + + @unsafe + mutating func storeBytes( + of value: T, toUncheckedByteOffset offset: Int, as type: T.Type + ) +} +``` + +Additionally, the basic loading operations available on `RawSpan` are available for `MutableRawSpan`. These operations are not type-safe, in that the loaded value returned by the operation can be invalid, and violate type invariants. Some types have a property that makes the `unsafeLoad(as:)` function safe, but we don't have a way to [formally identify](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md#SurjectiveBitPattern) such types at this time. ```swift extension MutableRawSpan { @@ -261,22 +292,10 @@ extension MutableRawSpan { } ``` -To these, `MutableRawSpan` adds functions to store the bytes of a `BitwiseCopyable` value: - -```swift -extension MutableRawSpan { - func storeBytes( - of value: T, toByteOffset offset: Int = 0, as type: T.Type - ) +We include functions to perform bulk copies into the memory represented by a `MutableRawSpan`. Updating a `MutableRawSpan` from a `Collection` or a `Span` copies every element of a source. It is an error to do so when there is are not enough bytes in the span to contain every element from the source. Updating `MutableRawSpan` from `Sequence` or `IteratorProtocol` instance copies as many items as possible, either until the input is empty or until there are not enough bytes in the span to store another element. - @unsafe - func storeBytes( - of value: T, toUncheckedByteOffset offset: Int, as type: T.Type - ) -} -``` +**Note:** This set of functions is sufficiently complete in functionality, but uses a minimal approach to slicing. This is only one of many possible approaches to slicing `MutableRawSpan`. (See the note above for more details on the same considerations.) -We include functions to perform bulk copies into the memory represented by a `MutableRawSpan`. Updating a `MutableRawSpan` from a `Collection` or a `Span` copies every element of a source. It is an error to do so when there is are not enough bytes in the span to contain every element from the source. Updating `MutableRawSpan` from `Sequence` or `IteratorProtocol` instance copies as many items as possible, either until the input is empty or until there are not enough bytes in the span to store another element. ```swift extension MutableRawSpan { mutating func update( @@ -347,7 +366,7 @@ A `mutating` computed property getter defined on any type and returning a `~Esca A `nonmutating` computed property getter returning a `~Escapable & ~Copyable` value establishes a borrowing lifetime relationship, as if returning a `~Escapable & Copyable` value (see [SE-0456][SE-0456].) -The standard library will provide `mutableSpan` computed properties. These return lifetime-dependent `MutableSpan` instances. These computed properties are the safe and composable replacements for the existing `withUnsafeMutableBufferPointer` closure-taking functions. +The standard library will provide `mutating` computed properties providing lifetime-dependent `MutableSpan` instances. These `mutableSpan` computed properties are intended as the safe and composable replacements for the existing `withUnsafeMutableBufferPointer` closure-taking functions. ```swift extension Array { @@ -371,8 +390,6 @@ extension CollectionOfOne { } ``` - - #### Extensions to unsafe buffer types We hope that `MutableSpan` and `MutableRawSpan` will become the standard ways to delegate mutations of shared contiguous memory in Swift. Many current API delegate mutations with closure-based functions that receive an `UnsafeMutableBufferPointer` parameter to do this. We will provide ways to unsafely obtain `MutableSpan` instances from `UnsafeMutableBufferPointer` and `MutableRawSpan` instances from `UnsafeMutableRawBufferPointer`, in order to bridge these unsafe types to newer, safer contexts. @@ -412,7 +429,7 @@ extension Foundation.Data { The `mutableSpan` and `mutableBytes` properties should be performant and return their `MutableSpan` or `MutableRawSpan` with very little work, in O(1) time. In copy-on-write types, however, obtaining a `MutableSpan` is the start of the mutation, and if the backing buffer is not uniquely reference a copy must be made ahead of returning the `MutableSpan`. -Note that `MutableSpan` incurs no special behaviour for bridged types, since mutations always require a defensive copy of data bridged from Objective-C data structures. +Note that `MutableSpan` incurs no special behaviour for bridged types, since mutable bindings always require a defensive copy of data bridged from Objective-C data structures. ## Source compatibility @@ -424,7 +441,7 @@ This proposal is additive and ABI-compatible with existing code. ## Implications on adoption -The additions described in this proposal require a new version of the Swift standard library and runtime. +The additions described in this proposal require a new version of the Swift standard library. ## Alternatives considered @@ -478,7 +495,7 @@ extension MutableRawSpan { ``` We are subsetting functions that require lifetime annotations until such annotations are [proposed][PR-2305]. -#### Splitting `MutableSpan` instances +#### Splitting `MutableSpan` instances – `MutableSpan` in divide-and-conquer algorithms It is desirable to have a way to split a `MutableSpan` in multiple parts, for divide-and-conquer algorithms or other reasons: @@ -488,11 +505,15 @@ extension MutableSpan where Element: ~Copyable { } ``` -Unfortunately, tuples do not support non-copyable values yet. We may be able to use the new `Vector`/`Slab`/`InlineArray` type proposed in [SE-0453][SE-0453], but destructuring its non-copyable elements remains a challenge. Solving this issue for `Span` as well as `MutableSpan` is a top priority. +Unfortunately, tuples do not support non-copyable values yet. We may be able to use `InlineArray` ([SE-0453][SE-0453]), or a bespoke type, but destructuring the non-copyable constituent part remains a challenge. Solving this issue for `Span` and `MutableSpan` is a top priority. + +#### Mutating algorithms + +Algorithms defined on `MutableCollection` such as `sort(by:)` and `partition(by:)` could be defined on `MutableSpan`. We believe we will be able to define these more generally once we have a generalized container protocol hierarchy. #### Delegated initialization with `OutputSpan` Some data structures can delegate initialization of parts of their owned memory. The standard library added the `Array` initializer `init(unsafeUninitializedCapacity:initializingWith:)` in [SE-0223][SE-0223]. This initializer relies on `UnsafeMutableBufferPointer` and correct usage of initialization primitives. We should present a simpler and safer model of initialization by leveraging non-copyability and non-escapability. -## Acknowledgements +We expect to propose an `OutputSpan` type to represent partially-initialized memory, and to support to the initialization of memory by appending to the initialized portion of the underlying storage. From 9923e61c2787efae8b509694ec9f92cb7e8e5f1e Mon Sep 17 00:00:00 2001 From: gangwoon Date: Mon, 10 Feb 2025 16:45:14 +0900 Subject: [PATCH 4042/4563] [SE-0430] Disallow borrowing sending --- proposals/0430-transferring-parameters-and-results.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/proposals/0430-transferring-parameters-and-results.md b/proposals/0430-transferring-parameters-and-results.md index 712847edbf..031685c0ee 100644 --- a/proposals/0430-transferring-parameters-and-results.md +++ b/proposals/0430-transferring-parameters-and-results.md @@ -390,18 +390,12 @@ the callee. Unlike `consuming` parameters, `sending` parameters do not have no-implicit-copying semantics. To opt into no-implicit-copying semantics or to change the default ownership -convention, `sending` may also be paired with an explicit `consuming` or -`borrowing` ownership modifier: +convention, `sending` may also be paired with an explicit `consuming` ownership modifier: ```swift func sendingConsuming(_ x: consuming sending T) { ... } -func sendingBorrowing(_ x: borrowing sending T) { ... } ``` -Note that an explicit `borrowing` annotation always implies no-implicit-copying, -so there is no way to change the default ownership convention of a -`sending` parameter without also opting into no-implicit-copying semantics. - ### Adoption in the Concurrency library There are several APIs in the concurrency library that send a parameter across From 356900ad58f726cdb2ce99d6ba50b5b0af681ccf Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 10 Feb 2025 07:50:18 -0800 Subject: [PATCH 4043/4563] Fix word salad --- proposals/nnnn-MutableSpan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-MutableSpan.md b/proposals/nnnn-MutableSpan.md index 70a76f6172..a76ddba350 100644 --- a/proposals/nnnn-MutableSpan.md +++ b/proposals/nnnn-MutableSpan.md @@ -19,7 +19,7 @@ ## Introduction -We recently [introduced][SE-0447] the `Span` and `RawSpan` types, providing read-only access to borrowed memory shared. This proposal adds mutations of exclusively-borrowed memory with `MutableSpan` and `MutableRawSpan`. +We recently [introduced][SE-0447] the `Span` and `RawSpan` types, providing shared read-only access to borrowed memory. This proposal adds helper types to delegate mutations of exclusively-borrowed memory: `MutableSpan` and `MutableRawSpan`. ## Motivation From 3d7202fc88a5bfc1e98c8ac912a7f8a00dc9fb4e Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 10 Feb 2025 08:59:55 -0800 Subject: [PATCH 4044/4563] Improve solution introduction --- proposals/nnnn-MutableSpan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-MutableSpan.md b/proposals/nnnn-MutableSpan.md index a76ddba350..f128749662 100644 --- a/proposals/nnnn-MutableSpan.md +++ b/proposals/nnnn-MutableSpan.md @@ -30,7 +30,7 @@ These functions have a few different drawbacks, most prominently their reliance In addition to the new types, we will propose adding new API some standard library types to take advantage of `MutableSpan` and `MutableRawSpan`. ## Proposed solution -We introduced `Span` to provide shared read-only access to containers. We cannot use `Span` to also model container mutations, due to the [law of exclusivity][SE-0176]. `Span` is copyable, and must be copyable in order to properly model read access under the law of exclusivity: a value can be simultaneously accessed through multiple read-only accesses. Mutations, on the other hand, require _exclusive access_. Exclusive access cannot be modeled with a copyable type, since a copy of the value representing the access would violate exclusivity by adding a second access. We therefore need a non-copyable type separate from `Span` in order to model mutations. +We introduced `Span` to provide shared read-only access to containers. The natural next step is to provide a similar capability for mutable access. Mutability requires exclusive access, per Swift's [law of exclusivity][SE-0176]. `Span` is copyable, and must be copyable in order to properly model read access under the law of exclusivity: a value can be simultaneously accessed through multiple read-only accesses. Exclusive access cannot be modeled with a copyable type, since a copy would represent an additional access, in violation of the law of exclusivity. We therefore need a non-copyable type separate from `Span` in order to model mutations. #### MutableSpan From 495cf7dbe7d189f4d2480e2861a053210ef454ee Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Tue, 11 Feb 2025 14:21:20 -0700 Subject: [PATCH 4045/4563] Add UTF8Span proposal --- .../nnnn-utf8span-safe-utf8-processing.md | 900 ++++++++++++++++++ 1 file changed, 900 insertions(+) create mode 100644 proposals/nnnn-utf8span-safe-utf8-processing.md diff --git a/proposals/nnnn-utf8span-safe-utf8-processing.md b/proposals/nnnn-utf8span-safe-utf8-processing.md new file mode 100644 index 0000000000..e7984007cb --- /dev/null +++ b/proposals/nnnn-utf8span-safe-utf8-processing.md @@ -0,0 +1,900 @@ + + +# UTF8Span: Safe UTF-8 Processing Over Contiguous Bytes + +* Proposal: [SE-NNNN](nnnn-utf8-span.md) +* Authors: [Michael Ilseman](https://github.com/milseman), [Guillaume Lessard](https://github.com/glessard) +* Review Manager: TBD +* Status: **Awaiting review** +* Bug: rdar://48132971, rdar://96837923 +* Implementation: https://github.com/swiftlang/swift/pull/78531 +* Upcoming Feature Flag: +* Review: ([pitch 1](https://forums.swift.org/t/pitch-utf-8-processing-over-unsafe-contiguous-bytes/69715)) ([pitch 2](https://forums.swift.org/t/pitch-safe-utf-8-processing-over-contiguous-bytes/72742)) + + +## Introduction + +We introduce `UTF8Span` for efficient and safe Unicode processing over contiguous storage. `UTF8Span` is a memory safe non-escapable type [similar to `Span`](https://github.com/swiftlang/swift-evolution/pull/2307). + +Native `String`s are stored as validly-encoded UTF-8 bytes in an internal contiguous memory buffer. The standard library implements `String`'s API as internal methods which operate on top of this buffer, taking advantage of the validly-encoded invariant and specialized Unicode knowledge. We propose making this UTF-8 buffer and its methods public as API for more advanced libraries and developers. + +## Motivation + +Currently, if a developer wants to do `String`-like processing over UTF-8 bytes, they have to make an instance of `String`, which allocates a native storage class, copies all the bytes, and is reference counted. The developer would then need to operate within the new `String`'s views and map between `String.Index` and byte offsets in the original buffer. + +For example, if these bytes were part of a data structure, the developer would need to decide to either cache such a new `String` instance or recreate it on the fly. Caching more than doubles the size and adds caching complexity. Recreating it on the fly adds a linear time factor and class instance allocation/deallocation and potentially reference counting. + +Furthermore, `String` may not be fully available on tightly constrained platforms, especially those that cannot support allocations. Both `String` and `UTF8Span` have some API that require Unicode data tables and that might not be available on embedded (String via its conformance to `Comparable` and `Collection` depend on these data tables while `UTF8Span` has a couple of methods that will be unavailable). + +### UTF-8 validity and efficiency + +UTF-8 validation is a particularly common concern and the subject of a fair amount of [research](https://lemire.me/blog/2020/10/20/ridiculously-fast-unicode-utf-8-validation/). Once an input is known to be validly encoded UTF-8, subsequent operations such as decoding, grapheme breaking, comparison, etc., can be implemented much more efficiently under this assumption of validity. Swift's `String` type's native storage is guaranteed-valid-UTF8 for this reason. + +Failure to guarantee UTF-8 encoding validity creates security and safety concerns. With invalidly-encoded contents, memory safety would become more nuanced. An ill-formed leading byte can dictate a scalar length that is longer than the memory buffer. The buffer may have bounds associated with it, which differs from the bounds dictated by its contents. + +Additionally, a particular scalar value in valid UTF-8 has only one encoding, but invalid UTF-8 could have the same value encoded as an [overlong encoding](https://en.wikipedia.org/wiki/UTF-8#Overlong_encodings), which would compromise code that checks for the presence of a scalar value by looking at the encoded bytes (or that does a byte-wise comparison). + + +## Proposed solution + +We propose a non-escapable `UTF8Span` which exposes `String` functionality for validly-encoded UTF-8 code units in contiguous memory. We also propose rich API describing the kind and location of encoding errors. + +## Detailed design + +`UTF8Span` is a borrowed view into contiguous memory containing validly-encoded UTF-8 code units. + +```swift +@frozen +public struct UTF8Span: Copyable, ~Escapable { + **TODO**: This might end up being UnsafeRawPointer? like in Span + public var unsafeBaseAddress: UnsafeRawPointer + + /* + A bit-packed count and flags (such as isASCII) + + ╔═══════╦═════╦═════╦══════════╦═══════╗ + ║ b63 ║ b62 ║ b61 ║ b60:56 ║ b56:0 ║ + ╠═══════╬═════╬═════╬══════════╬═══════╣ + ║ ASCII ║ NFC ║ SSC ║ reserved ║ count ║ + ╚═══════╩═════╩═════╩══════════╩═══════╝ + + ASCII means the contents are all-ASCII (<0x7F). + NFC means contents are in normal form C for fast comparisons. + SSC means single-scalar Characters (i.e. grapheme clusters): every + `Character` holds only a single `Unicode.Scalar`. + */ + @usableFromInline + internal var _countAndFlags: UInt64 +} + +``` + + + +### UTF-8 validation + +We propose new API for identifying where and what kind of encoding errors are present in UTF-8 content. + +```swift +extension Unicode.UTF8 { + /** + + The kind and location of a UTF-8 encoding error. + + Valid UTF-8 is represented by this table: + + ╔════════════════════╦════════╦════════╦════════╦════════╗ + ║ Scalar value ║ Byte 0 ║ Byte 1 ║ Byte 2 ║ Byte 3 ║ + ╠════════════════════╬════════╬════════╬════════╬════════╣ + ║ U+0000..U+007F ║ 00..7F ║ ║ ║ ║ + ║ U+0080..U+07FF ║ C2..DF ║ 80..BF ║ ║ ║ + ║ U+0800..U+0FFF ║ E0 ║ A0..BF ║ 80..BF ║ ║ + ║ U+1000..U+CFFF ║ E1..EC ║ 80..BF ║ 80..BF ║ ║ + ║ U+D000..U+D7FF ║ ED ║ 80..9F ║ 80..BF ║ ║ + ║ U+E000..U+FFFF ║ EE..EF ║ 80..BF ║ 80..BF ║ ║ + ║ U+10000..U+3FFFF ║ F0 ║ 90..BF ║ 80..BF ║ 80..BF ║ + ║ U+40000..U+FFFFF ║ F1..F3 ║ 80..BF ║ 80..BF ║ 80..BF ║ + ║ U+100000..U+10FFFF ║ F4 ║ 80..8F ║ 80..BF ║ 80..BF ║ + ╚════════════════════╩════════╩════════╩════════╩════════╝ + + ### Classifying errors + + An *unexpected continuation* is when a continuation byte (`10xxxxxx`) occurs + in a position that should be the start of a new scalar value. Unexpected + continuations can often occur when the input contains arbitrary data + instead of textual content. An unexpected continuation at the start of + input might mean that the input was not correctly sliced along scalar + boundaries or that it does not contain UTF-8. + + A *truncated scalar* is a multi-byte sequence that is the start of a valid + multi-byte scalar but is cut off before ending correctly. A truncated + scalar at the end of the input might mean that only part of the entire + input was received. + + A *surrogate code point* (`U+D800..U+DFFF`) is invalid UTF-8. Surrogate + code points are used by UTF-16 to encode scalars in the supplementary + planes. Their presence may mean the input was encoded in a different 8-bit + encoding, such as CESU-8, WTF-8, or Java's Modified UTF-8. + + An *invalid non-surrogate code point* is any code point higher than + `U+10FFFF`. This can often occur when the input is arbitrary data instead + of textual content. + + An *overlong encoding* occurs when a scalar value that could have been + encoded using fewer bytes is encoded in a longer byte sequence. Overlong + encodings are invalid UTF-8 and can lead to security issues if not + correctly detected: + + - https://nvd.nist.gov/vuln/detail/CVE-2008-2938 + - https://nvd.nist.gov/vuln/detail/CVE-2000-0884 + + An overlong encoding of `NUL`, `0xC0 0x80`, is used in Java's Modified + UTF-8 but is invalid UTF-8. Overlong encoding errors often catch attempts + to bypass security measures. + + ### Reporting the range of the error + + The range of the error reported follows the *Maximal subpart of an + ill-formed subsequence* algorithm in which each error is either one byte + long or ends before the first byte that is disallowed. See "U+FFFD + Substitution of Maximal Subparts" in the Unicode Standard. Unicode started + recommending this algorithm in version 6 and is adopted by the W3C. + + The maximal subpart algorithm will produce a single multi-byte range for a + truncated scalar (a multi-byte sequence that is the start of a valid + multi-byte scalar but is cut off before ending correctly). For all other + errors (including overlong encodings, surrogates, and invalid code + points), it will produce an error per byte. + + Since overlong encodings, surrogates, and invalid code points are erroneous + by the second byte (at the latest), the above definition produces the same + ranges as defining such a sequence as a truncated scalar error followed by + unexpected continuation byte errors. The more semantically-rich + classification is reported. + + For example, a surrogate count point sequence `ED A0 80` will be reported + as three `.surrogateCodePointByte` errors rather than a `.truncatedScalar` + followed by two `.unexpectedContinuationByte` errors. + + Other commonly reported error ranges can be constructed from this result. + For example, PEP 383's error-per-byte can be constructed by mapping over + the reported range. Similarly, constructing a single error for the longest + invalid byte range can be constructed by joining adjacent error ranges. + + ╔═════════════════╦══════╦═════╦═════╦═════╦═════╦═════╦═════╦══════╗ + ║ ║ 61 ║ F1 ║ 80 ║ 80 ║ E1 ║ 80 ║ C2 ║ 62 ║ + ╠═════════════════╬══════╬═════╬═════╬═════╬═════╬═════╬═════╬══════╣ + ║ Longest range ║ U+61 ║ err ║ ║ ║ ║ ║ ║ U+62 ║ + ║ Maximal subpart ║ U+61 ║ err ║ ║ ║ err ║ ║ err ║ U+62 ║ + ║ Error per byte ║ U+61 ║ err ║ err ║ err ║ err ║ err ║ err ║ U+62 ║ + ╚═════════════════╩══════╩═════╩═════╩═════╩═════╩═════╩═════╩══════╝ + + */ + @frozen + public struct EncodingError: Error, Sendable, Hashable, Codable { + /// The kind of encoding error + public var kind: Unicode.UTF8.EncodingError.Kind + + /// The range of offsets into our input containing the error + public var range: Range + + @_alwaysEmitIntoClient + public init( + _ kind: Unicode.UTF8.EncodingError.Kind, + _ range: some RangeExpression + ) + + @_alwaysEmitIntoClient + public init(_ kind: Unicode.UTF8.EncodingError.Kind, at: Int) + } +} + +extension UTF8.EncodingError { + /// The kind of encoding error encountered during validation + @frozen + public struct Kind: Error, Sendable, Hashable, Codable, RawRepresentable { + public var rawValue: UInt8 + + @inlinable + public init(rawValue: UInt8) + + /// A continuation byte (`10xxxxxx`) outside of a multi-byte sequence + @_alwaysEmitIntoClient + public static var unexpectedContinuationByte: Self + + /// A byte in a surrogate code point (`U+D800..U+DFFF`) sequence + @_alwaysEmitIntoClient + public static var surrogateCodePointByte: Self + + /// A byte in an invalid, non-surrogate code point (`>U+10FFFF`) sequence + @_alwaysEmitIntoClient + public static var invalidNonSurrogateCodePointByte: Self + + /// A byte in an overlong encoding sequence + @_alwaysEmitIntoClient + public static var overlongEncodingByte: Self + + /// A multi-byte sequence that is the start of a valid multi-byte scalar + /// but is cut off before ending correctly + @_alwaysEmitIntoClient + public static var truncatedScalar: Self + } +} + +@_unavailableInEmbedded +extension UTF8.EncodingError.Kind: CustomStringConvertible { + public var description: String { get } +} + +@_unavailableInEmbedded +extension UTF8.EncodingError: CustomStringConvertible { + public var description: String { get } +} +``` + +**QUESTION**: It would be good to expose this functionality via a general purpose validation API. Question is do we want a `findFirstError` or `findAllErrors` style API, both? E.g.: + +```swift +extension UTF8 { + public static func checkForError( + _ s: some Sequence + ) -> some UTF8.EncodingError { + + ... or + + public static func checkForAllErrors( + _ s: some Sequence + ) -> some Sequence { +``` + + +### Creation and validation + +`UTF8Span` is validated at initialization time and encoding errors are diagnosed and thrown. + + +```swift + +extension UTF8Span { + @lifetime(codeUnits) + public init( + _validating codeUnits: consuming Span + ) throws(UTF8.EncodingError) { + + @lifetime(borrow start) + internal init( + _unsafeAssumingValidUTF8 start: borrowing UnsafeRawPointer, + _countAndFlags: UInt64 + ) +} +``` + +**NOTE**: The final status of underscores, annotations, etc., are pending things like [SE-0456](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233/17) and [Lifetime Dependencies](https://forums.swift.org/t/pitch-non-escapable-types-and-lifetime-dependency/69865/140). + + +### Scalar processing + +We propose a `UTF8Span.ScalarIterator` type that can do scalar processing forwards and backwards. Note that `ScalarIterator` itself is non-escapable, and thus cannot conform to `IteratorProtocol`, etc. + +```swift +extension UTF8Span { + public func _makeScalarIterator() -> ScalarIterator + + /// Iterate the `Unicode.Scalar`s contents of a `UTF8Span`. + public struct ScalarIterator: ~Escapable { + public var codeUnits: UTF8Span + + /// The byte offset of the start of the next scalar. This is + /// always scalar-aligned. + public var currentCodeUnitOffset: Int { get } + + public init(_ codeUnits: UTF8Span) + + /// Decode and return the scalar starting at `currentCodeUnitOffset`. + /// After the function returns, `currentCodeUnitOffset` holds the + /// position at the end of the returned scalar, which is also the start + /// of the next scalar. + /// + /// Returns `nil` if at the end of the `UTF8Span`. + public mutating func next() -> Unicode.Scalar? + + /// Decode and return the scalar ending at `currentCodeUnitOffset`. After + /// the function returns, `currentCodeUnitOffset` holds the position at + /// the start of the returned scalar, which is also the end of the + /// previous scalar. + /// + /// Returns `nil` if at the start of the `UTF8Span`. + public mutating func previous() -> Unicode.Scalar? + + /// Advance `codeUnitOffset` to the end of the current scalar, without + /// decoding it. + /// + /// Returns the number of `Unicode.Scalar`s skipped over, which can be 0 + /// if at the end of the UTF8Span. + public mutating func skipForward() -> Int + + /// Advance `codeUnitOffset` to the end of `n` scalars, without decoding + /// them. + /// + /// Returns the number of `Unicode.Scalar`s skipped over, which can be + /// fewer than `n` if at the end of the UTF8Span. + public mutating func skipForward(by n: Int) -> Int + + /// Move `codeUnitOffset` to the start of the previous scalar, without + /// decoding it. + /// + /// Returns the number of `Unicode.Scalar`s skipped over, which can be 0 + /// if at the start of the UTF8Span. + public mutating func skipBack() -> Bool + + /// Move `codeUnitOffset` to the start of the previous `n` scalars, + /// without decoding them. + /// + /// Returns the number of `Unicode.Scalar`s skipped over, which can be + /// fewer than `n` if at the start of the UTF8Span. + public mutating func skipBack(by n: Int) -> Bool + + /// Reset to the nearest scalar-aligned code unit offset `<= i`. + /// + /// **TODO**: Example + public mutating func reset(roundingBackwardsFrom i: Int) + + /// Reset to the nearest scalar-aligned code unit offset `>= i`. + /// + /// **TODO**: Example + public mutating func reset(roundingForwardsFrom i: Int) + + /// Reset this iterator to code unit offset `i`, skipping _all_ safety + /// checks. + /// + /// Note: This is only for very specific, low-level use cases. If + /// `codeUnitOffset` is not properly scalar-aligned, this function can + /// result in undefined behavior when, e.g., `next()` is called. + /// + /// For example, this could be used by a regex engine to backtrack to a + /// known-valid previous position. + /// + public mutating func reset(uncheckedAssumingAlignedTo i: Int) + + /// Returns the UTF8Span containing all the content up to the iterator's + /// current position. + public func _prefix() -> UTF8Span + + /// Returns the UTF8Span containing all the content after the iterator's + /// current position. + public func _suffix() -> UTF8Span + } +} + +``` + +**QUESTION**: Is it worth also surfacing as `isScalarAligned` API on `UTF8Span` so it's a little easier to find and spell (as well as talk about in doc comments)? + + +### Character processing + +We similarly propose a `UTF8Span.CharacterIterator` type that can do grapheme-breaking forwards and backwards. + +The `CharacterIterator` assumes that the start and end of the `UTF8Span` is the start and end of content. + +Any scalar-aligned position is a valid place to start or reset the grapheme-breaking algorithm to, though you could get different `Character` output if if resetting to a position that isn't `Character`-aligned relative to the start of the `UTF8Span` (e.g. in the middle of a series of regional indicators). + + + +```swift +@_unavailableInEmbedded +extension UTF8Span { + public func _makeCharacterIterator() -> CharacterIterator + + /// Iterate the `Character` contents of a `UTF8Span`. + public struct CharacterIterator: ~Escapable { + public var codeUnits: UTF8Span + + /// The byte offset of the start of the next `Character`. This is + /// always scalar-aligned and `Character`-aligned. + public var currentCodeUnitOffset: Int { get } + + public init(_ span: UTF8Span) + + /// Return the `Character` starting at `currentCodeUnitOffset`. After the + /// function returns, `currentCodeUnitOffset` holds the position at the + /// end of the `Character`, which is also the start of the next + /// `Character`. + /// + /// Returns `nil` if at the end of the `UTF8Span`. + public mutating func next() -> Character? + + /// Return the `Character` ending at `currentCodeUnitOffset`. After the + /// function returns, `currentCodeUnitOffset` holds the position at the + /// start of the returned `Character`, which is also the end of the + /// previous `Character`. + /// + /// Returns `nil` if at the start of the `UTF8Span`. + public mutating func previous() -> Character? + + /// Advance `codeUnitOffset` to the end of the current `Character`, + /// without constructing it. + /// + /// Returns the number of `Character`s skipped over, which can be 0 + /// if at the end of the UTF8Span. + public mutating func skipForward() + + /// Advance `codeUnitOffset` to the end of `n` `Characters`, without + /// constructing them. + /// + /// Returns the number of `Character`s skipped over, which can be + /// fewer than `n` if at the end of the UTF8Span. + public mutating func skipForward(by n: Int) + + /// Move `codeUnitOffset` to the start of the previous `Character`, + /// without constructing it. + /// + /// Returns the number of `Character`s skipped over, which can be 0 + /// if at the start of the UTF8Span. + public mutating func skipBack() + + /// Move `codeUnitOffset` to the start of the previous `n` `Character`s, + /// without constructing them. + /// + /// Returns the number of `Character`s skipped over, which can be + /// fewer than `n` if at the start of the UTF8Span. + public mutating func skipBack(by n: Int) + + /// Reset to the nearest character-aligned position `<= i`. + public mutating func reset(roundingBackwardsFrom i: Int) + + /// Reset to the nearest character-aligned position `>= i`. + public mutating func reset(roundingForwardsFrom i: Int) + + /// Reset this iterator to code unit offset `i`, skipping _all_ safety + /// checks. + /// + /// Note: This is only for very specific, low-level use cases. If + /// `codeUnitOffset` is not properly scalar-aligned, this function can + /// result in undefined behavior when, e.g., `next()` is called. + /// + /// If `i` is scalar-aligned, but not `Character`-aligned, you may get + /// different results from running `Character` iteration. + /// + /// For example, this could be used by a regex engine to backtrack to a + /// known-valid previous position. + /// + public mutating func reset(uncheckedAssumingAlignedTo i: Int) + + /// Returns the UTF8Span containing all the content up to the iterator's + /// current position. + public func prefix() -> UTF8Span + + /// Returns the UTF8Span containing all the content after the iterator's + /// current position. + public func suffix() -> UTF8Span + } + +} +``` + +### Comparisons + +The content of a `UTF8Span` can be compared in a number of ways, including literally (byte semantics) and Unicode canonical equivalence. + +```swift +extension UTF8Span { + /// Whether this span has the same bytes as `other`. + @_alwaysEmitIntoClient + public func bytesEqual(to other: UTF8Span) -> Bool + + /// Whether this span has the same bytes as `other`. + @_alwaysEmitIntoClient + public func bytesEqual(to other: some Sequence) -> Bool + + /// Whether this span has the same `Unicode.Scalar`s as `other`. + @_alwaysEmitIntoClient + public func scalarsEqual( + to other: some Sequence + ) -> Bool + + /// Whether this span has the same `Character`s as `other`, using + /// `Character.==` (i.e. Unicode canonical equivalence). + @_unavailableInEmbedded + @_alwaysEmitIntoClient + public func charactersEqual( + to other: some Sequence + ) -> Bool +} +``` + +We also support literal (i.e. non-canonical) pattern matching against `StaticString`. + +```swift +extension UTF8Span { + static func ~=(_ lhs: UTF8Span, _ rhs: StaticString) -> Bool +} +``` + +#### Canonical equivalence and ordering + +`UTF8Span` can perform Unicode canonical equivalence checks (i.e. the semantics of `String.==` and `Character.==`). + +```swift +extension UTF8Span { + /// Whether `self` is equivalent to `other` under Unicode Canonical + /// Equivalence. + @_unavailableInEmbedded + public func isCanonicallyEquivalent( + to other: UTF8Span + ) -> Bool + + /// Whether `self` orders less than `other` under Unicode Canonical + /// Equivalence using normalized code-unit order (in NFC). + @_unavailableInEmbedded + public func isCanonicallyLessThan( + _ other: UTF8Span + ) -> Bool +} +``` + +#### Extracting sub-spans + +Slicing a `UTF8Span` is nuanced and depends on the caller's desired use. They can only be sliced at scalar-aligned code unit offsets or else it will break the valid-UTF8 invariant. Furthermore, if the caller desires consistent grapheme breaking behavior without externally managing grapheme breaking state, they must be sliced along `Character` boundaries. For this reason, we have exposed slicing as `prefix` and `suffix` operations on `UTF8Span`'s iterators instead of `Span`'s' `extracting` methods. + +### Queries + +`UTF8Span` checks at construction time and remembers whether its contents are all ASCII. Additional checks can be requested and remembered. + +```swift +extension UTF8Span { + /// Returns whether the validated contents were all-ASCII. This is checked at + /// initialization time and remembered. + @inlinable + public var isASCII: Bool { get } + + /// Returns whether the contents are known to be NFC. This is not + /// always checked at initialization time and is set by `checkForNFC`. + @inlinable + @_unavailableInEmbedded + public var isKnownNFC: Bool { get } + + /// Do a scan checking for whether the contents are in Normal Form C. + /// When the contents are in NFC, canonical equivalence checks are much + /// faster. + /// + /// `quickCheck` will check for a subset of NFC contents using the + /// NFCQuickCheck algorithm, which is faster than the full normalization + /// algorithm. However, it cannot detect all NFC contents. + /// + /// Updates the `isKnownNFC` bit. + @_unavailableInEmbedded + public mutating func checkForNFC( + quickCheck: Bool + ) -> Bool + + /// Returns whether every `Character` (i.e. grapheme cluster) + /// is known to be comprised of a single `Unicode.Scalar`. + /// + /// This is not always checked at initialization time. It is set by + /// `checkForSingleScalarCharacters`. + @_unavailableInEmbedded + @inlinable + public var isKnownSingleScalarCharacters: Bool { get } + + /// Do a scan, checking whether every `Character` (i.e. grapheme cluster) + /// is comprised of only a single `Unicode.Scalar`. When a span contains + /// only single-scalar characters, character operations are much faster. + /// + /// `quickCheck` will check for a subset of single-scalar character contents + /// using a faster algorithm than the full grapheme breaking algorithm. + /// However, it cannot detect all single-scalar `Character` contents. + /// + /// Updates the `isKnownSingleScalarCharacters` bit. + @_unavailableInEmbedded + public mutating func checkForSingleScalarCharacters( + quickCheck: Bool + ) -> Bool +} +``` + +**QUESTION**: There is an even quicker quick-check for NFC (checking if all scalars are `<0x300`, which covers extended latin characters). Should we expose that level as well? + +### `UTF8Span` from `String` + +We will add `utf8Span`-style properties to `String` and `Substring`, in line with however [SE-0456](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233/17) turns out. + + +### `Span`-like functionality + +A `UTF8Span` is similar to a `Span`, but with the valid-UTF8 invariant and additional information such as `isASCII`. We propose a way to get a `Span` from a `UTF8Span` as well as some methods directly on `UTF8Span`: + +``` +extension UTF8Span { + @_alwaysEmitIntoClient + public var isEmpty: Bool { get } + + @_alwaysEmitIntoClient + public var storage: Span { get } + + /// Calls a closure with a pointer to the viewed contiguous storage. + /// + /// The buffer pointer passed as an argument to `body` is valid only + /// during the execution of `withUnsafeBufferPointer(_:)`. + /// Do not store or return the pointer for later use. + /// + /// - Parameter body: A closure with an `UnsafeBufferPointer` parameter + /// that points to the viewed contiguous storage. If `body` has + /// a return value, that value is also used as the return value + /// for the `withUnsafeBufferPointer(_:)` method. The closure's + /// parameter is valid only for the duration of its execution. + /// - Returns: The return value of the `body` closure parameter. + @_alwaysEmitIntoClient + borrowing public func withUnsafeBufferPointer< + E: Error, Result: ~Copyable & ~Escapable + >( + _ body: (_ buffer: borrowing UnsafeBufferPointer) throws(E) -> Result + ) throws(E) -> dependsOn(self) Result +} +``` + +### Getting a `String` from a `UTF8Span` + +**QUESTION**: We should make it easier than `String(decoding:as)` to make a `String` copy of the `UTF8Span`, especially since `UTF8Span` cannot conform to `Sequence` or `Collection`. This will form an ARC-managed copy and not something that will share the (ephemeral) storage. + +## Source compatibility + +This proposal is additive and source-compatible with existing code. + +## ABI compatibility + +This proposal is additive and ABI-compatible with existing code. + +## Implications on adoption + +The additions described in this proposal require a new version of the standard library and runtime. + +## Future directions + +### Streaming grapheme breaking + +Grapheme-breaking, which identifies where the boundaries between `Character`s are, is more complex than scalar decoding. Grapheme breaking can be ran from any scalar-aligned position, either with a given state from having processed previous scalars, or with a "fresh" state (as though that position were the start of new content). + +While the code units in a `UTF8Span` are always scalar-aligned (in order to be validly encoded), whether a span is grapheme-cluster aligned depends on its intended use. For example, `AttributedString` stores its content using rope-like storage, in which the entire content is a sequence of spans were each individual span is scalar-aligned but not necessarily grapheme-cluster aligned. + +A potential approach to exposing this functionality is to make the stdlib's `GraphemeBreakingState` public and define API for finding grapheme-breaks. + +```swift +extension Unicode { + public struct GraphemeBreakingState: Sendable, Equatable { + public init() + } +} +``` + +One approach is to add API to the grapheme breaking state so that the state can find the next break (while updating itself). Another is to pass grapheme breaking state to an iterator on UTF8Span, like below: + +```swift +extension UTF8Span { + public struct GraphemeBreakIterator: ~Escapable { + public var codeUnits: UTF8Span + public var currentCodeUnitOffset: Int + public var state: Unicode.GraphemeBreakingState + + public init(_ span: UTF8Span) + + public init(_ span: UTF8Span, using state: Unicode.GraphemeBreakingState) + + public mutating func next() -> Bool + + public mutating func previous() -> Bool + + + public mutating func skipForward() + + public mutating func skipForward(by n: Int) + + public mutating func skipBack() + + public mutating func skipBack(by n: Int) + + public mutating func reset( + roundingBackwardsFrom i: Int, using: Unicode.GraphemeBreakingState + ) + + public mutating func reset( + roundingForwardsFrom i: Int, using: Unicode.GraphemeBreakingState + ) + + public mutating func reset( + uncheckedAssumingAlignedTo i: Int, using: Unicode.GraphemeBreakingState + ) + + public func prefix() -> UTF8Span + public func suffix() -> UTF8Span + } +``` + + +### More alignments + +Future API could include word iterators (either [simple](https://www.unicode.org/reports/tr18/#Simple_Word_Boundaries) or [default](https://www.unicode.org/reports/tr18/#Default_Word_Boundaries)), line iterators, etc. + +### Normalization + +Future API could include checks for whether the content is in a particular normal form (not just NFC). + +### UnicodeScalarView and CharacterView + +Like `Span`, we are deferring adding any collection-like types to non-escapable `UTF8Span`. Future work could include adding view types that conform to a new `Container`-like protocol. + +For an example implementation of those see [the `UTFSpanViews.swift` test file](https://github.com/apple/swift-collections/pull/394). + +### More algorithms + +We propose equality checks (e.g. `scalarsEqual`), as those are incredibly common and useful operations. We have (tentatively) deferred other algorithms until non-escapable collections are figured out. + +However, we can add select high-value algorithms if motivated by the community. + +### More validation API + +Future work includes returning all the encoding errors found in a given input. + +```swift +extension UTF8 { + public static func checkAllErrors( + _ s: some Sequence + ) -> some Sequence +``` + +See [`_checkAllErrors` in `UTF8EncodingError.swift`](https://github.com/apple/swift-collections/pull/394). + +### Transcoded iterators, normalized iterators, case-folded iterators, etc + +We could provide lazily transcoded, normalized, case-folded, etc., iterators. If we do any of these for `UTF8Span`, we should consider adding equivalents views on `String`, `Substring`, etc. + +### Regex or regex-like support + +Future API additions would be to support `Regex`es on `UTF8Span`. We'd expose grapheme-level semantics, scalar-level semantics, and introduce byte-level semantics. + +Another future direction could be to add many routines corresponding to the underlying operations performed by the regex engine, which would be useful for parser-combinator libraries who wish to expose `String`'s model of Unicode by using the stdlib's accelerated implementation. + + +### Canonical Spaceships + +Should a `ComparisonResult` (or [spaceship](https://forums.swift.org/t/pitch-comparison-reform/5662)) be added to Swift, we could support that operation under canonical equivalence in a single pass rather than subsequent calls to `isCanonicallyEquivalent(to:)` and `isCanonicallyLessThan(_:)`. + + +### Exposing `String`'s storage class + +String's internal storage class is null-terminated valid UTF-8 (by substituting replacement characters) and implements range-replaceable operations along scalar boundaries. We could consider exposing the storage class itself, which might be useful for embedded platforms that don't have `String`. + +### Track other bits + +Future work include tracking whether the contents are NULL-terminated (useful for C bridging), whether the contents contain any newlines or only a single newline at the end (useful for accelerating Regex `.`), etc. + +### Putting more API on String + +`String` would also benefit from the query API, such as `isKnownNFC` and corresponding scan methods. Because a string may be a lazily-bridged instance of `NSString`, we don't always have the bits available to query or set, but this may become viable pending future improvements in bridging. + +### Generalize printing and logging facilities + +Many printing and logging protocols and facilities operate in terms of `String`. They could be generalized to work in terms of UTF-8 bytes instead, which is important for embedded. + +## Alternatives considered + +### Invalid start / end of input UTF-8 encoding errors + +Earlier prototypes had `.invalidStartOfInput` and `.invalidEndOfInput` UTF8 validation errors to communicate that the input was perhaps incomplete or not slices along scalar boundaries. In this scenario, `.invalidStartOfInput` is equivalent to `.unexpectedContinuation` with the range's lower bound equal to 0 and `.invalidEndOfInput` is equivalent to `.truncatedScalar` with the range's upper bound equal to `count`. + +This was rejected so as to not have two ways to encode the same error. There is no loss of information and `.unexpectedContinuation`/`.truncatedScalar` with ranges are more semantically precise. + +### An unsafe UTF8 Buffer Pointer type + +An [earlier pitch](https://forums.swift.org/t/pitch-utf-8-processing-over-unsafe-contiguous-bytes/69715) proposed an unsafe version of `UTF8Span`. Now that we have `~Escapable`, a memory-safe `UTF8Span` is better. + +### Alternatives to Iterators + +#### Functions + +A previous version of this pitch had code unit offset taking API directly on UTF8Span instead of using iterators as proposed. This lead to a large number of unweildy API. For example, instead of: + +```swift +extension UTF8Span.ScalarIterator { + public mutating func next() -> Unicode.Scalar? { } +} +``` + +we had: + +```swift +extension UTF8Span { +/// Decode the `Unicode.Scalar` starting at `i`. Return it and the start of + /// the next scalar. + /// + /// `i` must be scalar-aligned. + @_alwaysEmitIntoClient + public func decodeNextScalar( + _ i: Int + ) -> (Unicode.Scalar, nextScalarStart: Int) + + /// Decode the `Unicode.Scalar` starting at `i`. Return it and the start of + /// the next scalar. + /// + /// `i` must be scalar-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + @_alwaysEmitIntoClient + public func decodeNextScalar( + unchecked i: Int + ) -> (Unicode.Scalar, nextScalarStart: Int) + + /// Decode the `Unicode.Scalar` starting at `i`. Return it and the start of + /// the next scalar. + /// + /// `i` must be scalar-aligned. + /// + /// This function does not validate that `i` is within the span's bounds; + /// this is an unsafe operation. + /// + /// + /// This function does not validate that `i` is scalar-aligned; this is an + /// unsafe operation if `i` isn't. + @_alwaysEmitIntoClient + public func decodeNextScalar( + uncheckedAssumingAligned i: Int + ) -> (Unicode.Scalar, nextScalarStart: Int) +} +``` + +Every operation had `unchecked:` and `uncheckedAssumingAligned:` variants, which were needed to implement higher-performance constructs such as iterators and string processing features (including the `Regex` engine). + +This API made the caller manage the scalar-alignment invariant, while the iterator-style API proposed maintains this invariant internally, allowing it to use the most efficient implementation. + +Scalar-alignment can still be checked and managed by the caller through the `reset` API, which safely round forwards or backwards as needed. And, for high performance use cases where the caller knows that a given position is appropriately aligned already (for example, revisiting a prior point in a string during `Regex` processing), there's the `reset(uncheckedAssumingAlignedTo:)` API available. + +### Collections + +Because `Collection`s are `Escapable`, we cannot conform nested `View` types to `Collection`. + + +## Acknowledgments + +Karoy Lorentey, Karl, Geordie_J, and fclout, contributed to this proposal with their clarifying questions and discussions. + + + + + + + + + From 7eab91d2b1dacd993a94680a9eb9639aa728682b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 11 Feb 2025 17:33:31 -0800 Subject: [PATCH 4046/4563] [SE-0458] Don't require declarations with unsafe signatures to be @unsafe This extra step of requiring @unsafe is mostly busywork. Rather, we infer @unsafe from unsafe types, and can later suppress false positives for actually-safe declarations by marking them @safe. --- proposals/0458-strict-memory-safety.md | 94 ++++++++++++-------------- 1 file changed, 44 insertions(+), 50 deletions(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index c194e8dd49..c07036e48d 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -59,20 +59,13 @@ The `UnsafeBufferPointer` type will be marked with `@unsafe` in the Standard lib public struct UnsafeBufferPointer { ... } ``` -This indicates that use of this type is not memory-safe. Any declaration that has `UnsafeBufferPointer` as part of its type is also unsafe, and would produce a warning under this strict safety mode, e.g., +This indicates that use of this type is not memory-safe. Any declaration that has `UnsafeBufferPointer` as part of its type is implicitly `@unsafe`. ```swift -// warning: reference to unsafe generic struct 'UnsafePointer' +// note: implicitly @unsafe due to the use of the unsafe type UnsafePointer func sumIntBuffer(_ address: UnsafePointer?, _ count: Int) -> Int { ... } ``` -This warning can be suppressed by marking the function as `@unsafe`: - -```swift -@unsafe -func sumIntBuffer(_ address: UnsafePointer?, _ count: Int, _ start: Int) -> Int { ... } -``` - Users of this function that enable strict safety checking will see warnings when using it. For example: ```swift @@ -94,14 +87,13 @@ To suppress these warnings, the expressions involving unsafe code must be marked extension Array { func sum() -> Int { withUnsafeBufferPointer { buffer in - // warning: use of unsafe function 'sumIntBuffer' and unsafe property 'baseAddress' unsafe sumIntBuffer(buffer.baseAddress, buffer.count, 0) } } } ``` -The `unsafe` keyword here indicates the presence of unsafe code within that expression. As with `try` and `await`, it can cover multiple sources of unsafety within that expression: the call to `sumIntBuffer` is unsafe, as is the use of `buffer` and `buffer.baseAddress`, yet they are all covered by one `unsafe`. +The `unsafe` keyword here indicates the presence of unsafe code within that expression. As with `try` and `await`, it can cover multiple sources of unsafety within that expression: the call to `sumIntBuffer` is unsafe, as is the use of `buffer` and `buffer.baseAddress`, yet they are all covered by one `unsafe`. It is up to the authors of an unsafe API to document the conditions under which it is safe to use that API, and the user of that API to ensure that those conditions are met within the `unsafe` expression. Unlike `try`, `unsafe` doesn't propagate outward: we do *not* require that the `sum` function be marked `@unsafe` just because it has unsafe code in it. Similarly, the call to `withUnsafeBufferPointer` doesn't have to be marked as `unsafe` just because it has a closure that is unsafe. The programmer may choose to indicate that `sum` is unsafe, but the assumption is that unsafe behavior is properly encapsulated when using `unsafe` if the signature doesn't contain any unsafe types. @@ -123,7 +115,8 @@ The operation `UnsafeMutableBufferPointer.swapAt` swaps the values at the given ```swift extension UnsafeMutableBufferPointer { - @unsafe public func swapAt(_ i: Index, _ j: Index) { + /*implicitly @unsafe*/ + public func swapAt(_ i: Index, _ j: Index) { guard i != j else { return } precondition(i >= 0 && j >= 0) precondition(i < endIndex && j < endIndex) @@ -141,7 +134,7 @@ The `swapAt` implementation uses a mix of safe and unsafe code. The code marked * Performing pointer arithmetic on `baseAddress`: Swift cannot reason about the lifetime of that underlying pointer, nor whether the resulting pointer is still within the bounds of the allocation. * Moving and initializing the actual elements. The elements need to already be initialized. -The code itself has preconditions to ensure that the provided indices aren't out of bounds before performing the pointer arithmetic. However, there are other safety properties that cannot be checked with preconditions: that the memory associated with the pointer has been properly initialized, has a lifetime that spans the whole call, and is not being used simultaneously by any other part of the code. These safety properties are something that must be established by the *caller* of `swapAt`. Therefore, `swapAt` is marked `@unsafe` because callers of it need to reason about these properties. +The code itself has preconditions to ensure that the provided indices aren't out of bounds before performing the pointer arithmetic. However, there are other safety properties that cannot be checked with preconditions: that the memory associated with the pointer has been properly initialized, has a lifetime that spans the whole call, and is not being used simultaneously by any other part of the code. These safety properties are something that must be established by the *caller* of `swapAt`. Therefore, `swapAt` is considered `@unsafe` because callers of it need to reason about these properties. Because it's part of an unsafe type, it is implicitly `@unsafe`, although the author could choose to mark it as `@unsafe` explicitly. ### Incremental adoption @@ -150,17 +143,17 @@ The strict memory safety checking proposed here enforces a subset of Swift. Code * Strict concurrency checking, the focus of the Swift 6 language mode, required major changes to the type system, including the propagation of `Sendable` and the understanding of what code must be run on the main actor. These are global properties that don't permit local reasoning, or even local fixes, making the interoperability problem particularly hard. In contrast, strict safety checking has little or no effect on the type system, and unsafety can be encapsulated with `unsafe` expressions or ignored by a module that doesn't enable the checking. * [Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0337-support-incremental-migration-to-concurrency-checking.md) is a subset of Swift that works without a runtime. Like the proposed strictly-safe subset, code written in Embedded Swift will also work as regular Swift. Embedded Swift and the strict safety checking proposed here are orthogonal and can be composed to (for example) ensure that firmware written in Swift has no runtime and provides the best memory-safety guarantees. -A Swift module that adopts strict safety checking can address all of the resulting diagnostics by applying the `@unsafe` attribute and `unsafe` expression in the appropriate places, without changing any other code. This application of attributes can be automated through Fix-Its, making it possible to enable the mode and silence all diagnostics automatically. It would then be left to the programmer to audit those places that have used `unsafe` to encapsulate unsafe behavior, to ensure that they are indeed safe. Note that the strict safety checking does not by itself make the code more memory-safety: rather, it identifies those constructs that aren't safe, encouraging the use of safe alternatives and making it easier to audit for unsafe behavior. +A Swift module that adopts strict safety checking can address all of the resulting diagnostics by applying the `@unsafe` attribute and `unsafe` expression in the appropriate places, without changing any other code. This application of attributes can be automated through Fix-Its, making it possible to enable the mode and silence all diagnostics automatically. It would then be left to the programmer to audit those places that have used `unsafe` to encapsulate unsafe behavior, to ensure that they are indeed safe. Note that the strict safety checking does not by itself make the code more memory-safe: rather, it identifies those constructs that aren't safe, encouraging the use of safe alternatives and making it easier to audit for unsafe behavior. The introduction of the `@unsafe` attribute on a declaration has no effect on clients compiled without strict safety enabled. For clients that have enabled strict safety, they will start diagnosing uses of the newly-`@unsafe` API. However, these diagnostics are warnings with their own diagnostic group, so a client can ensure that they do not prevent the client from building. Therefore, modules can adopt strict safety checking at their own pace (or not) and clients of those modules are never "stuck" having to make major changes in response. ## Detailed design -This section describes how the primary proposed constructs, the `@unsafe` attribute and `unsafe` expression, interact with the strict type checking mode, and enumerates the places in the language, standard library, and compiler that introduce non-memory-safe code. +This section describes how the primary proposed constructs, the `@unsafe` attribute, `@safe` attribute, and `unsafe` expression, interact with the strict memory safety mode, and enumerates the places in the language, standard library, and compiler that introduce non-memory-safe code. ### `@unsafe` attribute -The `@unsafe` attribute can be applied to any declaration to indicate that use of that declaration can undermine memory safety. Any use of a declaration marked `@unsafe` will result in a warning. The closest analogue in the language today is `@available(*, deprecated)`, which has effectively no impact on the type system, yet any use of a deprecated declaration results in a warning. +The `@unsafe` attribute can be applied to any declaration to indicate that use of that declaration can undermine memory safety. Any use of an `@unsafe` declaration that isn't acknowledged in the source code (e.g., via the `unsafe` expression) will result in a warning. The closest analogue in the language today is `@available(*, deprecated)`, which has effectively no impact on the type system, yet any use of a deprecated declaration results in a warning. When a type is marked `@unsafe`, a declaration that uses that type in its interface is implicitly `@unsafe`. For example, consider a program containing three separate modules: @@ -170,7 +163,9 @@ When a type is marked `@unsafe`, a declaration that uses that type in its interf public struct DataWrapper { var buffer: UnsafeBufferPointer - public func checksum() -> Int32 { ... } + public func checksum() -> Int32 { + ... + } } // Module B @@ -191,12 +186,37 @@ extension MyType { } ``` -Module `A` defines a type, `DataWrapper`, that is `@unsafe`. It can be compiled with or without strict safety checking enabled, and is fine either way. +Module `A` defines a type, `DataWrapper` that stores an `UnsafeBufferPointer`, and is marked `@unsafe`. It can be compiled with or without strict safety checking enabled. If compiled with -Module `B` uses the `DataWrapper` type. If compiled without strict safety checking, there will be no diagnostics about memory safety. If compiled with strict safety checking, there will be a diagnostic about `wrapper` using an `@unsafe` type (`DataWrapper`) in its interface. This diagnostic can be ignored, but ideally the `wrapper` property will be marked as `@unsafe` (silencing the warning). +Module `B` uses the `DataWrapper` type. If compiled without strict safety checking, there will be no diagnostics about memory safety. The `wrapper` property is implicitly `@unsafe`, although the author may mark it as `@unsafe` explicitly if they choose to. If compiled with strict safety checking, the code will produce a warning because the storage of `MyType` involves an unsafe type (`DataWrapper`). This warning can be suppressed by either marking `MyType` as `@unsafe` (propagating unsafety) or `@safe` (`MyType` is safe to use). If left alone, it will be assumed to be safe. If module `C` enables strict memory safety, the use of `MyType` is considered safe (since it was not marked `@unsafe` and doesn't involve unsafe types in its interface). However, the access to `wrapper` will result in a diagnostic, because the type of `wrapper` involves an `@unsafe` type. This diagnostic will occur whether or not `wrapper` has been explicitly marked `@unsafe`. +### `unsafe` expression + +Any time there is executable code that makes use of unsafe constructs, the compiler will produce a diagnostic that indicates the use of those unsafe constructs unless it is within an `unsafe` expression. As noted in the previous section, use of the `wrapper` property (which is implicitly `@unsafe` due to the use of unsafe types) without an enclosing `unsafe` will produce a warning:: + +```swift +extension MyType { + public func checksum() -> Int32 {} + // warning: use of property `wrapper` with unsafe type `DataWrapper` + return wrapper.checksum() + } +} +``` + +To suppress the warning, introduce an `unsafe` prior to the expression involving the unsafe code: + +```swift +extension MyType { + public func checksum() -> Int32 {} + return unsafe wrapper.checksum() + } +} +``` + +The `unsafe` expression is much like `try` and `await`, in that it acknowledges that unsafe constructs (`wrapper`) are used within the subexpression but otherwise does not change the type. Unlike `try` and `await`, which require the enclosing context to handle throwing or be asynchronous, respectively, the `unsafe` expression does not imply any requirements about the enclosing block: it is purely a marker to indicate the presence of unsafe code, silencing a diagnostic. + There are a few exemptions to the rule that any unsafe constructs within the signature require the declaration to be `@unsafe`: * Local variables involving unsafe types do not need to be marked with `@unsafe`. For example, the local variable `base` will have unsafe type `UnsafePointer?`, but does not require `@unsafe` because every *use* of this local variable will need to be marked using the `unsafe` expression described in the next section. @@ -218,7 +238,7 @@ There are a few exemptions to the rule that any unsafe constructs within the sig ### `@safe` attribute -Like the `@unsafe` attribute, the `@safe` attribute is used on declarations whose signatures involve unsafe types. However, the `@safe` attribute means that the declaration is considered safe to use even though its signature includes unsafe types. For example, marking `UnsafeBufferPointer` as `@unsafe` means that all operations involving an unsafe buffer pointer are implicitly considered `@unsafe`. The `@safe` attribute can be used to say that those particular operations are actually safe. For example, any operation involving buffer indices or count are safe, because they don't touch the memory itself. This can be indicated by marking these APIs `@safe`: +The `@safe` attribute is used on declarations whose signatures involve unsafe types but are, nonetheless, safe to use. For example, marking `UnsafeBufferPointer` as `@unsafe` means that all operations involving an unsafe buffer pointer are implicitly considered `@unsafe`. The `@safe` attribute can be used to say that those certain operations are actually safe. For example, any operation involving buffer indices or count are safe, because they don't touch the memory itself. This can be indicated by marking these APIs `@safe`: ```swift extension UnsafeBufferPointer { @@ -264,35 +284,6 @@ extension Array { } ``` -### `unsafe` expression - -When a declaration is marked `@unsafe`, it is free to use any other unsafe types as part of its interface. Any time there is executable code that makes use of unsafe constructs, that code must be within an `unsafe` expression or it will receive a diagnostic about uses of unsafe code. In the example from the previous section, `wrapper` can be marked as `@unsafe` to suppress diagnostics by explicitly propagating unsafety to their clients: - -```swift -// Module B -import A - -public struct MyType { - @unsafe public var wrapper: DataWrapper -} -``` - -However, the use of the `wrapper` property in module `C` will produce a diagnostic unless it is part of an `unsafe` expression, like this: - -```swift -// Module C -import A -import B - -extension MyType { - public func checksum() -> Int32 {} - return unsafe wrapper.checksum() - } -} -``` - -The `unsafe` expression is much like `try` and `await`, in that it acknowledges that unsafe constructs (`wrapper`) are used within the subexpression but otherwise does not change the type. Unlike `try` and `await`, which require the enclosing context to handle throwing or be asynchronous, respectively, the `unsafe` expression does not imply any requirements about the enclosing block: it is purely a marker to indicate the presence of unsafe code, silencing a diagnostic. - ### Unsafe language constructs The following language constructs are always considered to be unsafe: @@ -740,7 +731,10 @@ We could introduce an optional `message` argument to the `@unsafe` attribute, wh ## Revision history -* **Revision 2 (following first review)** +* **Revision 3 (following second review eextension)** + * Do not require declarations with unsafe types in their signature to be marked `@unsafe`; it is implied. They may be marked `@safe` to indicate that they are actually safe. + +* **Revision 2 (following first review extension)** * Specified that variables of unsafe type passed in to uses of `@safe` declarations (e.g., calls, property accesses) are not diagnosed as themselves being unsafe. This makes means that expressions like `unsafeBufferePointer.count` will be considered safe. * Require types whose storage involves an unsafe type or conformance to be marked as `@safe` or `@unsafe`, much like other declarations that have unsafe types or conformances in their signature. * Add an Alternatives Considered section on prohibiting unsafe conformances and overrides. From 694d95ad1f3da2cef69f0d4ac44d7c722e722d0d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 11 Feb 2025 22:17:33 -0800 Subject: [PATCH 4047/4563] SE-0459: Add handling of unsafe for for..in iteration --- proposals/0458-strict-memory-safety.md | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index c07036e48d..3de7b20fd8 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -411,6 +411,42 @@ func passUnsafe() { } ``` +### `for..in` loops + +Swift's `for..in` loops are effectively implemented as syntactic sugar over the `Sequence` and `IteratorProtocol` protocols, where the `for..in` creates a new iterator (with `Sequence.makeIterator()`) and then calls its `next()` operation for each loop iteration. If the conformances to `Sequence` are `IteratorProtocol` is `@unsafe`, the loop will introduce a warning: + +```swift +let someUnsafeBuffer: UnsafeBufferPointer = unsafe ... +for x in someBuffer { // warning: use of unsafe conformance of 'UnsafeBufferPointer' to 'Sequence' + // and someBuffer has unsafe type 'UnsafeBufferPointer' + // ... +} +``` + +Following the precedent set by [SE-0298](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0298-asyncsequence.md), which introduced effects in loops, the `unsafe` keyword that acknowledges unsafe behavior in the iteration will follow the `for`: + +```swift +let someUnsafeBuffer: UnsafeBufferPointer = unsafe ... +for unsafe x in someBuffer { // still warns that someBuffer has unsafe type 'UnsafeBufferPointer' + // ... +} +``` + +This may not be the only unsafe behavior in the `for..in` loop. For example, the expression that produces the sequence itself (via the reference to `someBuffer`) is also unsafe, so it needs to be acknowledged: + +```swift +let someUnsafeBuffer: UnsafeBufferPointer = unsafe ... +for unsafe x in unsafe someBuffer { + // ... +} +``` + +This repeated `unsafe` also occurs with the other effects: if an `async throws` function `getAsyncSequence()` produces an `AsyncSequence` whose iteration can throw, one will end up with two `try` and `await` keywords: + +```swift +for try await x in try await getAsyncSequence() { ... } +``` + ### Strict safety mode and escalatable warnings The strict memory safety mode can be enabled with the new compiler flag `-strict-memory-safety`. @@ -733,6 +769,7 @@ We could introduce an optional `message` argument to the `@unsafe` attribute, wh * **Revision 3 (following second review eextension)** * Do not require declarations with unsafe types in their signature to be marked `@unsafe`; it is implied. They may be marked `@safe` to indicate that they are actually safe. + * Add `unsafe` for iteration via the `for..in` syntax. * **Revision 2 (following first review extension)** * Specified that variables of unsafe type passed in to uses of `@safe` declarations (e.g., calls, property accesses) are not diagnosed as themselves being unsafe. This makes means that expressions like `unsafeBufferePointer.count` will be considered safe. From ee09e8a05a69fd5386581d49e1494f1eb58245d0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 11 Feb 2025 22:47:03 -0800 Subject: [PATCH 4048/4563] Infer @unsafe on imported C structs/enums/classes/unions with pointers in them --- proposals/0458-strict-memory-safety.md | 61 ++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index 3de7b20fd8..c2afa2b9f6 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -447,6 +447,66 @@ This repeated `unsafe` also occurs with the other effects: if an `async throws` for try await x in try await getAsyncSequence() { ... } ``` +### C(++) interoperability + +The C family of languages does not provide an equivalent to the strict safety mode described in this proposal, and unlike Swift, the defaults tend to be unsafe along all of the dimensions of memory safety. C(++) libriaries used within Swift can, therefore, introduce memory safety issues into the Swift code. + +The primary issue with memory safety in C(++) concerns the presence of pointers. C(++) pointers will generally be imported into Swift as an `Unsafe*Pointer` type of some form. For C functions (and C++ member functions), that means that a potentially unsafe API such as + +```swift +char *strstr(const char * haystack, const char *needle); +``` + +will be treated as implicitly `@unsafe` in Swift because its signature contains unsafe types: + +```swift +func strstr( + _ haystack: UnsafePointer?, + _ needle: UnsafePointer? +) -> UnsafeMutablePointer? +``` + +A C function that doesn't use pointer types, on the other hand, will implicitly be considered to be safe, because there are no unsafe types in its Swift signature. For example, the following would be considered safe: + +```swift +// int getchar(void); +func getchar() -> CInt +``` + +C and C++ also have user-defined types in the form of `struct`s, `enum`s, `union`s, and (in C++) `class`es. For such types, this proposal infers them to be `@unsafe` when their non-static data contains any C pointers or C types that are explicitly marked as unsafe. For example, a `Point` struct could be considered safe: + +```cpp +struct Point { + double x, y; +}; +``` + +but a `struct` with a pointer or C++ reference in it would be implicitly `@unsafe` in Swift: + +```swift +struct ListNode { + void *element; + struct ListNode *next; +}; +``` + +The C attribute `swift_attr` can be used to make specific declarations safe or unsafe, e.g., we could mark a C++ class that manages its internal pointer correctly as being safe, e.g., + +```cpp +class __attribute__((swift_attr("@safe"))) MyString { + char *data; + size_t length; + + MyString(const MyString &); + MyString(MyString &&); + MyString &operator=(const MyString&); + MyString &operator=(MyString&&); + ~MyString(); +}; +``` + +Note that C `enum`s will never be inferred to be `@unsafe` because they don't carry any values other than their underlying integral type, which is always a safe type. + ### Strict safety mode and escalatable warnings The strict memory safety mode can be enabled with the new compiler flag `-strict-memory-safety`. @@ -770,6 +830,7 @@ We could introduce an optional `message` argument to the `@unsafe` attribute, wh * **Revision 3 (following second review eextension)** * Do not require declarations with unsafe types in their signature to be marked `@unsafe`; it is implied. They may be marked `@safe` to indicate that they are actually safe. * Add `unsafe` for iteration via the `for..in` syntax. + * Add C(++) interoperability section that infers `@unsafe` for C types that involve pointers. * **Revision 2 (following first review extension)** * Specified that variables of unsafe type passed in to uses of `@safe` declarations (e.g., calls, property accesses) are not diagnosed as themselves being unsafe. This makes means that expressions like `unsafeBufferePointer.count` will be considered safe. From 2ffa2d4fc20fb5e839922fcb07c7d05c35e1b999 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 11 Feb 2025 23:33:43 -0800 Subject: [PATCH 4049/4563] SE-0458: Restructure for improved readability The Detailed Design was becoming a grab-bag of sections, so give it more structure. First, we describe all of the sources of memory unsafety in Swift. Then, note the role of the @safe attribute. Finally, describe all of the ways in which one can acknowledge unsafety. One drive-by fix here is to document the unsafe conformances of the `UnsafeBufferPointer` family of types to the `Collection` protocol hierarchy. --- proposals/0458-strict-memory-safety.md | 327 ++++++++++++------------- 1 file changed, 151 insertions(+), 176 deletions(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index c2afa2b9f6..1a4cff2eed 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -151,90 +151,142 @@ The introduction of the `@unsafe` attribute on a declaration has no effect on cl This section describes how the primary proposed constructs, the `@unsafe` attribute, `@safe` attribute, and `unsafe` expression, interact with the strict memory safety mode, and enumerates the places in the language, standard library, and compiler that introduce non-memory-safe code. -### `@unsafe` attribute +### Sources of unsafety -The `@unsafe` attribute can be applied to any declaration to indicate that use of that declaration can undermine memory safety. Any use of an `@unsafe` declaration that isn't acknowledged in the source code (e.g., via the `unsafe` expression) will result in a warning. The closest analogue in the language today is `@available(*, deprecated)`, which has effectively no impact on the type system, yet any use of a deprecated declaration results in a warning. +There are a number of places in the language where one can introduce memory unsafety. This section enumerates the ways in which the language, library, and user code can introduce memory unsafety. -When a type is marked `@unsafe`, a declaration that uses that type in its interface is implicitly `@unsafe`. For example, consider a program containing three separate modules: +#### Unsafe language constructs + +The following language constructs are always considered to be unsafe: + +* `unowned(unsafe)`: Used to store a reference without maintaining its reference count. The safe counterpart, `unowned`, uses dynamic checking to ensure that the reference isn't accessed after the corresponding object has been released. The `unsafe` variant disables that dynamic checking. Uses of `unowned(unsafe)` entities are not memory-safe. +* `unsafeAddressor`, `unsafeMutableAddressor`: These accessors vend an unsafe pointer, and are therefore unsafe to declare. Other accessors (e.g., `get` and `set`) can provide safe alternatives. The accessors are considered to be part of the signature of the property or subscript they're associated with, making the property implicitly `@unsafe` unless explicitly marked `@safe`. +* `@exclusivity(unchecked)`: Used to remove dynamic exclusivity checks from a particular variable, which can mean that dynamic exclusivity violations go undetected at run time, causing a memory safety violation. Uses of `@exclusivity(unchecked)` entities are not memory-safe. + +The following language constructs are considered to be unsafe when strict concurrency checking is enabled (i.e., in the Swift 6 language mode): + +* `nonisolated(unsafe)`: Allows a property to be accessed from concurrent code without ensuring that such accesses are done so safely. Uses of `nonisolated(unsafe)` entities are not memory-safe. +* `@preconcurrency` imports: Suppresses diagnostics related to data race safety when they relate to specific imported modules, which can introduce thread safety issues. The `@preconcurrency` import will need to be annotated with `@unsafe` in the strict safety mode. + +#### `@unsafe` attribute + +The `@unsafe` attribute can be applied to any declaration to indicate that use of that declaration can undermine memory safety. Here are some examples: ```swift -// Module A @unsafe public struct DataWrapper { var buffer: UnsafeBufferPointer - - public func checksum() -> Int32 { - ... - } } -// Module B -import A +@unsafe +func writeToRegisterAt(integerAddress: Int, value: Int32) { ... } +``` -public struct MyType { - public var wrapper: DataWrapper -} +Uses of `DataWrapper` and `writeToRegisterAt` within executable code will be considered to be memory-unsafe. -// Module C -import A -import B +When a declaration uses unsafe types within its signature, it is implicitly considered to be `@unsafe`. The signature of a declaration is the interface that the declaration presents to clients, including the parameter and result types of functions, the type of properties, and any generic parameters and requirements. For example, a method on `DataWrapper` defined as follows: -extension MyType { - public func checksum() -> Int32 {} - return wrapper.checksum() +```swift +extension DataWrapper { + public func checksum() -> Int32 { + crc32(0, buffer.baseAddress, buffer.count) } } ``` -Module `A` defines a type, `DataWrapper` that stores an `UnsafeBufferPointer`, and is marked `@unsafe`. It can be compiled with or without strict safety checking enabled. If compiled with +will be implicitly `@unsafe` because the type of the implicit `self` parameter is `@unsafe`. -Module `B` uses the `DataWrapper` type. If compiled without strict safety checking, there will be no diagnostics about memory safety. The `wrapper` property is implicitly `@unsafe`, although the author may mark it as `@unsafe` explicitly if they choose to. If compiled with strict safety checking, the code will produce a warning because the storage of `MyType` involves an unsafe type (`DataWrapper`). This warning can be suppressed by either marking `MyType` as `@unsafe` (propagating unsafety) or `@safe` (`MyType` is safe to use). If left alone, it will be assumed to be safe. +Generally speaking, a declaration's signature is everything that isn't within the "body" of the definition that's enclosed in braces or following the `=` of a property. One notable exception to this is default arguments, because default arguments of functions are part of the implementation of a function, not its signature. For example, the following function does not have any unsafe types in its signature, even though the default argument for `value` involves unsafe code. That unsafe code is effectively part of the body of the function, so it follows the rules for `unsafe` expressions. -If module `C` enables strict memory safety, the use of `MyType` is considered safe (since it was not marked `@unsafe` and doesn't involve unsafe types in its interface). However, the access to `wrapper` will result in a diagnostic, because the type of `wrapper` involves an `@unsafe` type. This diagnostic will occur whether or not `wrapper` has been explicitly marked `@unsafe`. +```swift +func hasDefault(value: Int = unsafe getIntegerUnsafely()) { ... } +``` -### `unsafe` expression +#### Unsafe conformances -Any time there is executable code that makes use of unsafe constructs, the compiler will produce a diagnostic that indicates the use of those unsafe constructs unless it is within an `unsafe` expression. As noted in the previous section, use of the `wrapper` property (which is implicitly `@unsafe` due to the use of unsafe types) without an enclosing `unsafe` will produce a warning:: +A given type might implement a protocol in a manner that introduces unsafety, for example because the operations needed to satisfy protocol requirements cannot ensure that all uses through the protocol can maintain memory safety. For example, the `UnsafeBufferPointer` type conforms to the `Collection` protocol, but it cannot do so in a safe way because `UnsafeBufferPointer` does not provide the lifetime, bounds, or initialization safety that clients of the `Collection` protocol expect. Such conformances should be explicitly marked `@unsafe`, e.g., ```swift -extension MyType { - public func checksum() -> Int32 {} - // warning: use of property `wrapper` with unsafe type `DataWrapper` - return wrapper.checksum() - } -} +extension UnsafeBufferPointer: @unsafe Collection { ... } ``` -To suppress the warning, introduce an `unsafe` prior to the expression involving the unsafe code: +Unsafe conformances are similar to unsafe types in that their presence in a declaration's signature will make that declaration implicitly `@unsafe`. For example, the use of a collection algorithm such as `firstIndex(where:)` with an `UnsafeBufferPointer` will be considered unsafe because the conformance above is `@unsafe`. + +#### Unsafe standard library APIs + +Much of the identification of unsafe Swift code that becomes available with the strict memory safety mode is due to the identification of unsafe declarations within the standard library itself, and their propagation to other types that use them. In the standard library, the following functions and types would be marked `@unsafe` : + +* `Unsafe(Mutable)(Raw)(Buffer)Pointer`, `OpaquePointer`, `CVaListPointer`: These types provide neither lifetime nor bounds safety. Over time, Swift code is likely to move toward their safe replacements, such as `(Raw)Span`. +* `(Closed)Range.init(uncheckedBounds:)`: This operation makes it possible to create a range that doesn't satisfy invariants on which other bounds safety checking (e.g., in `Array.subscript`) relies. +* `Span.subscript(unchecked:)` : An unchecked subscript whose use can introduce bounds safety problems. +* `Unmanaged`: Wrapper over reference-counted types that explicitly disables reference counting, potentially introducing lifetime safety issues. +* `unsafeBitCast`: Allows type casts that are not known to be safe, which can introduce type safety problems. +* `unsafeDowncast`: An unchecked form of an `as!` cast that can introduce type safety problems. +* `Optional.unsafelyUnwrapped`: An unchecked form of the postfix `!` operation on optionals that can introduce various type, initialization, or lifetime safety problems when `nil` is interpreted as a typed value. +* `UnsafeContinuation`, `withUnsafe(Throwing)Continuation`: An unsafe form of `withChecked(Throwing)Continuation` that does not verify that the continuation is called exactly once, which can cause various safety problems. +* `withUnsafeCurrentTask` and `UnsafeCurrentTask`: The `UnsafeCurrentTask` type does not provide lifetime safety, and must only be used within the closure passed to `withUnsafeCurrentTask`. +* `UnownedSerialExecutor`: This type is intentionally not lifetime safe. It's primary use is the `unownedExecutor` property of the `Actor` protocol, which documents the lifetime assumptions of the `UnownedSerialExecutor` instance it produces. + +All of these APIs will be marked `@unsafe`. For standard library APIs that involve unsafe types, those that are safe to use will be marked `@safe` while those that require the user to maintain some aspect of safety will be marked `@unsafe`. Unless mentioned above, standard library APIs that do not have an unsafe type in their signature, but use unsafe constructs in their implementation, will be considered to be safe. + +There are also a number of unsafe conformances in the standard library: + +* `Unsafe(Mutable)(Raw)BufferPointer`: The conformances of these types to `Sequence` and the `Collection` protocol hierarchy are all `@unsafe`. +* `Unsafe(Mutable)(Raw)Pointer`: The conformances of these types to `Strideable` are `@unsafe`. + +#### Unsafe compiler flags + +There are a number of compiler flags that intentionally disable some safety-related checking. For each of these flags, the compiler will produce a diagnostic if they are used with strict memory safety: + +* `-Ounchecked`, which disables some checking in the standard library, including (for example) bounds checking on array accesses. +* `-enforce-exclusivity=unchecked` and `-enforce-exclusivity=none`, which disables exclusivity checking that is needed for memory safety. +* `-strict-concurrency=` for anything other than "complete", because the memory safety model requires strict concurrency to eliminate thread safety issues. +* `-disable-access-control`, which allows one to break invariants of a type that can lead to memory-safety issues, such as breaking the invariant of `Range` that the lower bound not exceed the upper bound. + +#### C(++) interoperability + +The C family of languages does not provide an equivalent to the strict safety mode described in this proposal, and unlike Swift, the defaults tend to be unsafe along all of the dimensions of memory safety. C(++) libriaries used within Swift can, therefore, introduce memory safety issues into the Swift code. + +The primary issue with memory safety in C(++) concerns the presence of pointers. C(++) pointers will generally be imported into Swift as an `Unsafe*Pointer` type of some form. For C functions (and C++ member functions), that means that a potentially unsafe API such as ```swift -extension MyType { - public func checksum() -> Int32 {} - return unsafe wrapper.checksum() - } -} +char *strstr(const char * haystack, const char *needle); ``` -The `unsafe` expression is much like `try` and `await`, in that it acknowledges that unsafe constructs (`wrapper`) are used within the subexpression but otherwise does not change the type. Unlike `try` and `await`, which require the enclosing context to handle throwing or be asynchronous, respectively, the `unsafe` expression does not imply any requirements about the enclosing block: it is purely a marker to indicate the presence of unsafe code, silencing a diagnostic. +will be treated as implicitly `@unsafe` in Swift because its signature contains unsafe types: -There are a few exemptions to the rule that any unsafe constructs within the signature require the declaration to be `@unsafe`: +```swift +func strstr( + _ haystack: UnsafePointer?, + _ needle: UnsafePointer? +) -> UnsafeMutablePointer? +``` -* Local variables involving unsafe types do not need to be marked with `@unsafe`. For example, the local variable `base` will have unsafe type `UnsafePointer?`, but does not require `@unsafe` because every *use* of this local variable will need to be marked using the `unsafe` expression described in the next section. +A C function that doesn't use pointer types, on the other hand, will implicitly be considered to be safe, because there are no unsafe types in its Swift signature. For example, the following would be considered safe: - ```swift - func sum(array: [Int]) -> Int { - array.withUnsafeBufferPointer { buffer in - /*@unsafe is unnecessary here*/ let base = unsafe buffer.baseAddress - // ... - } - } - ``` +```swift +// int getchar(void); +func getchar() -> CInt +``` -* Default arguments of functions are part of the implementation of a function, not its signature. For example, the following function does not have any unsafe types in its signature, so it does not require `@unsafe`, even though the default argument for `value` involves unsafe code. That unsafe code is effectively part of the body of the function, so it follows the rules for `unsafe` expressions. +C and C++ also have user-defined types in the form of `struct`s, `enum`s, `union`s, and (in C++) `class`es. For such types, this proposal infers them to be `@unsafe` when their non-static data contains any C pointers or C types that are explicitly marked as unsafe. For example, a `Point` struct could be considered safe: - ```swift - func hasDefault(value: Int = unsafe getIntegerUnsafely()) { ... } - ``` +```cpp +struct Point { + double x, y; +}; +``` + +but a `struct` with a pointer or C++ reference in it would be implicitly `@unsafe` in Swift: + +```swift +struct ListNode { + void *element; + struct ListNode *next; +}; +``` + +Note that C `enum`s will never be inferred to be `@unsafe` because they don't carry any values other than their underlying integral type, which is always a safe type. ### `@safe` attribute @@ -284,48 +336,61 @@ extension Array { } ``` -### Unsafe language constructs +### Acknowledging unsafety -The following language constructs are always considered to be unsafe: +All of the features described above are available in Swift regardless of whether strict memory safety checking is enabled. When strict memory safety checking is enabled, each use of an unsafe construct of any form must be ackwnowledged in the source code with one of the forms below, which provides an in-source auditable indication of where memory unsafety issues can arise. The following section describes each of the features for acknowledging memory unsafety. -* `unowned(unsafe)`: Used to store a reference without maintaining its reference count. The safe counterpart, `unowned`, uses dynamic checking to ensure that the reference isn't accessed after the corresponding object has been released. The `unsafe` variant disables that dynamic checking. Uses of `unowned(unsafe)` entities are not memory-safe. -* `unsafeAddressor`, `unsafeMutableAddressor`: These accessors vend an unsafe pointer, and are therefore unsafe to declare. Other accessors (e.g., `get` and `set`) can provide safe alternatives. The accessors are considered to be part of the signature of the property or subscript they're associated with; the property should be marked either `@unsafe` or `@safe` to suppress the safety diagnostic on the declaration of an unsafe accessor. -* `@exclusivity(unchecked)`: Used to remove dynamic exclusivity checks from a particular variable, which can mean that dynamic exclusivity violations go undetected at run time, causing a memory safety violation. Uses of `@exclusivity(unchecked)` entities are not memory-safe. +#### `unsafe` expression -The following language constructs are considered to be unsafe when strict concurrency checking is enabled (i.e., in the Swift 6 language mode): +Any time there is executable code that makes use of unsafe constructs, the compiler will produce a diagnostic that indicates the use of those unsafe constructs unless it is within an `unsafe` expression. For example, consider the `DataWrapper` example from an earlier section: -* `nonisolated(unsafe)`: Allows a property to be accessed from concurrent code without ensuring that such accesses are done so safely. Uses of `nonisolated(unsafe)` entities are not memory-safe. -* `@preconcurrency` imports: Suppresses diagnostics related to data race safety when they relate to specific imported modules, which can introduce thread safety issues. The `@preconcurrency` import will need to be annotated with `@unsafe` in the strict safety mode. +```swift +public struct DataWrapper { + var buffer: UnsafeBufferPointer +} + +extension DataWrapper { + public func checksum() -> Int32 { + crc32(0, buffer.baseAddress, buffer.count) + } +} +``` -### Unsafe standard library APIs +The property `buffer` uses an unsafe type, `UnsafeBufferPointer`. When using that property in the implementation of `checksum`, the Swift compiler will produce a warning when strict memory safety checking is enabled: -In the standard library, the following functions and types would be marked `@unsafe` : +```swift +warning: expression uses unsafe constructs but is not marked with 'unsafe' +``` -* `Unsafe(Mutable)(Raw)(Buffer)Pointer`, `OpaquePointer`, `CVaListPointer`: These types provide neither lifetime nor bounds safety. Over time, Swift code is likely to move toward their safe replacements, such as `(Raw)Span`. -* `(Closed)Range.init(uncheckedBounds:)`: This operation makes it possible to create a range that doesn't satisfy invariants on which other bounds safety checking (e.g., in `Array.subscript`) relies. -* `Span.subscript(unchecked:)` : An unchecked subscript whose use can introduce bounds safety problems. -* `Unmanaged`: Wrapper over reference-counted types that explicitly disables reference counting, potentially introducing lifetime safety issues. -* `unsafeBitCast`: Allows type casts that are not known to be safe, which can introduce type safety problems. -* `unsafeDowncast`: An unchecked form of an `as!` cast that can introduce type safety problems. -* `Optional.unsafelyUnwrapped`: An unchecked form of the postfix `!` operation on optionals that can introduce various type, initialization, or lifetime safety problems when `nil` is interpreted as a typed value. -* `UnsafeContinuation`, `withUnsafe(Throwing)Continuation`: An unsafe form of `withChecked(Throwing)Continuation` that does not verify that the continuation is called exactly once, which can cause various safety problems. -* `withUnsafeCurrentTask` and `UnsafeCurrentTask`: The `UnsafeCurrentTask` type does not provide lifetime safety, and must only be used within the closure passed to `withUnsafeCurrentTask`. -* `UnownedSerialExecutor`: This type is intentionally not lifetime safe. It's primary use is the `unownedExecutor` property of the `Actor` protocol, which documents the lifetime assumptions of the `UnownedSerialExecutor` instance it produces. +This warning can be suppressed using the `unsafe` expression, as follows: -All of these APIs will be marked `@unsafe`. For standard library APIs that involve unsafe types, those that are safe to use will be marked `@safe` while those that require the user to maintain some aspect of safety will be marked `@unsafe`. Unless mentioned above, standard library APIs that do not have an unsafe type in their signature, but use unsafe constructs in their implementation, will be considered to be safe. +```swift +extension DataWrapper { + public func checksum() -> Int32 { + unsafe crc32(0, buffer.baseAddress, buffer.count) + } +} +``` -### Unsafe compiler flags +The `unsafe` expression is much like `try` and `await`, in that it acknowledges that unsafe constructs (`buffer`) are used within the subexpression but otherwise does not change the type. Unlike `try` and `await`, which require the enclosing context to handle throwing or be asynchronous, respectively, the `unsafe` expression does not imply any requirements about the enclosing block: it is purely a marker to indicate the presence of unsafe code, silencing a diagnostic. -There are a number of compiler flags that intentionally disable some safety-related checking. For each of these flags, the compiler will produce a diagnostic if they are used with strict memory safety: +#### Types with unsafe storage -* `-Ounchecked`, which disables some checking in the standard library, including (for example) bounds checking on array accesses. -* `-enforce-exclusivity=unchecked` and `-enforce-exclusivity=none`, which disables exclusivity checking that is needed for memory safety. -* `-strict-concurrency=` for anything other than "complete", because the memory safety model requires strict concurrency to eliminate thread safety issues. -* `-disable-access-control`, which allows one to break invariants of a type that can lead to memory-safety issues, such as breaking the invariant of `Range` that the lower bound not exceed the upper bound. +Types that wrap unsafe types will often encapsulate the unsafe behavior to provide safe interfaces. However, this requires deliberate design and implementation, potentially involving adding specific preconditions. When strict safety checking is enabled, a type whose storage includes any unsafe types or conformances will be diagnosed as involving unsafe code. For example, the `DataWrapper` struct from the prior section -### Types with unsafe storage +```swift +public struct DataWrapper { + var buffer: UnsafeBufferPointer +} +``` + +contains storage of an unsafe type (`UnsafeBufferPointer`), so the Swift compiler will produce a warning when strict memory safety checking is enabled: + +```swift +warning: type `DataWrapper` that includes unsafe storage must be explicitly marked `@unsafe` or `@safe` +``` -Types that wrap unsafe types will often encapsulate the unsafe behavior to provide safe interfaces. However, this requires deliberate design and implementation, potentially involving adding specific preconditions. When strict safety checking is enabled, a type whose storage is unsafe will be diagnosed as involving unsafe code. This diagnostic can be suppressed by marking the type as `@safe` or `@unsafe`, in the same manner as any other declaration that has unsafe types or conformances in its signature: +As the warning implies, this diagnostic can be suppressed by marking the type as `@safe` or `@unsafe`. The `DataWrapper` type doesn't appear to provide safety over its storage, so it should likely be marked `@unsafe`. In contrast, one can wrap unsafe types to provide safe types. For example: ```swift // @safe is required to suppress a diagnostic about the 'buffer' property's use @@ -347,12 +412,14 @@ struct ImmortalBufferWrapper : Collection { } ``` +Much of the standard library is built up as safe abstractions over unsafe code, and it's expected that user code will do the same. + A type has unsafe storage if: * Any stored instance property (for `actor`, `class`, and `struct` types) or associated value (for cases of `enum` types) have a type that involves an unsafe type or conformance. * Any stored instance property uses one of the unsafe language features (such as `unowned(unsafe)`). -### Unsafe overrides +#### Unsafe overrides Overriding a safe method within an `@unsafe` one could introduce unsafety, so it will produce a diagnostic in the strict safety mode: @@ -377,41 +444,7 @@ class Sub: Super { The `@unsafe` annotation is at the class level because any use of the `Sub` type can now introduce unsafe behavior, and any indication of that unsafe behavior will be lost once that `Sub` is converted to a `Super` instance. -### Unsafe conformances - -Implementing a protocol requirement that is safe (and not part of an `@unsafe` protocol) within an `@unsafe` declaration introduces unsafety, so it will produce a diagnostic in the strict safety mode: - -```swift -protocol P { - func f() -} - -struct ConformsToP { } - -extension ConformsToP: P { - @unsafe func f() { } // warning: unsafe instance method 'f()' cannot satisfy safe requirement -} -``` - -To suppress this warning, one can place `@unsafe` on the conformance to `P` is supplied. This notes that the conformance itself is unsafe: - -```swift -extension ConformsToP: @unsafe P { - @unsafe func f() { } -} -``` - -Use of an `@unsafe` conformance for any reason (e.g., when that conformance is needed to call a generic function with a `ConformsToP` requirement) is diagnosed as an unsafe use, much like use of an `@unsafe` declaration. For example - -```swift -func acceptP(_: T.Type) { } - -func passUnsafe() { - acceptP(ConformsToP.self) // warning: use of @unsafe conformance of 'ConformsToP' to protocol 'P' -} -``` - -### `for..in` loops +#### `for..in` loops Swift's `for..in` loops are effectively implemented as syntactic sugar over the `Sequence` and `IteratorProtocol` protocols, where the `for..in` creates a new iterator (with `Sequence.makeIterator()`) and then calls its `next()` operation for each loop iteration. If the conformances to `Sequence` are `IteratorProtocol` is `@unsafe`, the loop will introduce a warning: @@ -447,66 +480,6 @@ This repeated `unsafe` also occurs with the other effects: if an `async throws` for try await x in try await getAsyncSequence() { ... } ``` -### C(++) interoperability - -The C family of languages does not provide an equivalent to the strict safety mode described in this proposal, and unlike Swift, the defaults tend to be unsafe along all of the dimensions of memory safety. C(++) libriaries used within Swift can, therefore, introduce memory safety issues into the Swift code. - -The primary issue with memory safety in C(++) concerns the presence of pointers. C(++) pointers will generally be imported into Swift as an `Unsafe*Pointer` type of some form. For C functions (and C++ member functions), that means that a potentially unsafe API such as - -```swift -char *strstr(const char * haystack, const char *needle); -``` - -will be treated as implicitly `@unsafe` in Swift because its signature contains unsafe types: - -```swift -func strstr( - _ haystack: UnsafePointer?, - _ needle: UnsafePointer? -) -> UnsafeMutablePointer? -``` - -A C function that doesn't use pointer types, on the other hand, will implicitly be considered to be safe, because there are no unsafe types in its Swift signature. For example, the following would be considered safe: - -```swift -// int getchar(void); -func getchar() -> CInt -``` - -C and C++ also have user-defined types in the form of `struct`s, `enum`s, `union`s, and (in C++) `class`es. For such types, this proposal infers them to be `@unsafe` when their non-static data contains any C pointers or C types that are explicitly marked as unsafe. For example, a `Point` struct could be considered safe: - -```cpp -struct Point { - double x, y; -}; -``` - -but a `struct` with a pointer or C++ reference in it would be implicitly `@unsafe` in Swift: - -```swift -struct ListNode { - void *element; - struct ListNode *next; -}; -``` - -The C attribute `swift_attr` can be used to make specific declarations safe or unsafe, e.g., we could mark a C++ class that manages its internal pointer correctly as being safe, e.g., - -```cpp -class __attribute__((swift_attr("@safe"))) MyString { - char *data; - size_t length; - - MyString(const MyString &); - MyString(MyString &&); - MyString &operator=(const MyString&); - MyString &operator=(MyString&&); - ~MyString(); -}; -``` - -Note that C `enum`s will never be inferred to be `@unsafe` because they don't carry any values other than their underlying integral type, which is always a safe type. - ### Strict safety mode and escalatable warnings The strict memory safety mode can be enabled with the new compiler flag `-strict-memory-safety`. @@ -831,6 +804,8 @@ We could introduce an optional `message` argument to the `@unsafe` attribute, wh * Do not require declarations with unsafe types in their signature to be marked `@unsafe`; it is implied. They may be marked `@safe` to indicate that they are actually safe. * Add `unsafe` for iteration via the `for..in` syntax. * Add C(++) interoperability section that infers `@unsafe` for C types that involve pointers. + * Document the unsafe conformances of the `UnsafeBufferPointer` family of types to the `Collection` protocol hierarchy. + * Restructure detailed design to separate out "sources of unsafety" from "acknowledging unsafety". * **Revision 2 (following first review extension)** * Specified that variables of unsafe type passed in to uses of `@safe` declarations (e.g., calls, property accesses) are not diagnosed as themselves being unsafe. This makes means that expressions like `unsafeBufferePointer.count` will be considered safe. From 682c8aca9ac946c004bc6b29a80d98b33670a340 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 11 Feb 2025 23:38:25 -0800 Subject: [PATCH 4050/4563] SE-0458: Bring back discussion of unsafe witnesses and how to acknowledge them This was lost in my restructuring --- proposals/0458-strict-memory-safety.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index 1a4cff2eed..bb249cd3ce 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -419,6 +419,30 @@ A type has unsafe storage if: * Any stored instance property (for `actor`, `class`, and `struct` types) or associated value (for cases of `enum` types) have a type that involves an unsafe type or conformance. * Any stored instance property uses one of the unsafe language features (such as `unowned(unsafe)`). +#### Unsafe witnesses + +When a type conforms to a given protocol, it must satisfy all of the requirements of that protocol. Part of this process is determining which declaration (called the *witness*) satisfies a given protocol requirement. If a particular witness is unsafe but the corresponding requirement is not safe, the compiler will produce a warning: + +```swift +protocol P { + func f() +} + +struct ConformsToP { } + +extension ConformsToP: P { + @unsafe func f() { } // warning: unsafe instance method 'f()' cannot satisfy safe requirement +} +``` + +This unsafety can be acknowledged by marking the conformance as `@unsafe`, e.g., + +```swift +extension ConformsToP: @unsafe P { + @unsafe func f() { } // okay, it's an unsafe conformance +} +``` + #### Unsafe overrides Overriding a safe method within an `@unsafe` one could introduce unsafety, so it will produce a diagnostic in the strict safety mode: From 48c09949709807271ed7ac4df1c77a0e8c8e5b93 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 11 Feb 2025 23:44:45 -0800 Subject: [PATCH 4051/4563] SE-0458: Move the @safe attribute into the section on acknowledging unsafety --- proposals/0458-strict-memory-safety.md | 78 +++++++++++++------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index bb249cd3ce..5c4f45cc09 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -288,7 +288,45 @@ struct ListNode { Note that C `enum`s will never be inferred to be `@unsafe` because they don't carry any values other than their underlying integral type, which is always a safe type. -### `@safe` attribute +### Acknowledging unsafety + +All of the features described above are available in Swift regardless of whether strict memory safety checking is enabled. When strict memory safety checking is enabled, each use of an unsafe construct of any form must be ackwnowledged in the source code with one of the forms below, which provides an in-source auditable indication of where memory unsafety issues can arise. The following section describes each of the features for acknowledging memory unsafety. + +#### `unsafe` expression + +Any time there is executable code that makes use of unsafe constructs, the compiler will produce a diagnostic that indicates the use of those unsafe constructs unless it is within an `unsafe` expression. For example, consider the `DataWrapper` example from an earlier section: + +```swift +public struct DataWrapper { + var buffer: UnsafeBufferPointer +} + +extension DataWrapper { + public func checksum() -> Int32 { + crc32(0, buffer.baseAddress, buffer.count) + } +} +``` + +The property `buffer` uses an unsafe type, `UnsafeBufferPointer`. When using that property in the implementation of `checksum`, the Swift compiler will produce a warning when strict memory safety checking is enabled: + +```swift +warning: expression uses unsafe constructs but is not marked with 'unsafe' +``` + +This warning can be suppressed using the `unsafe` expression, as follows: + +```swift +extension DataWrapper { + public func checksum() -> Int32 { + unsafe crc32(0, buffer.baseAddress, buffer.count) + } +} +``` + +The `unsafe` expression is much like `try` and `await`, in that it acknowledges that unsafe constructs (`buffer`) are used within the subexpression but otherwise does not change the type. Unlike `try` and `await`, which require the enclosing context to handle throwing or be asynchronous, respectively, the `unsafe` expression does not imply any requirements about the enclosing block: it is purely a marker to indicate the presence of unsafe code, silencing a diagnostic. + +#### `@safe` attribute The `@safe` attribute is used on declarations whose signatures involve unsafe types but are, nonetheless, safe to use. For example, marking `UnsafeBufferPointer` as `@unsafe` means that all operations involving an unsafe buffer pointer are implicitly considered `@unsafe`. The `@safe` attribute can be used to say that those certain operations are actually safe. For example, any operation involving buffer indices or count are safe, because they don't touch the memory itself. This can be indicated by marking these APIs `@safe`: @@ -336,44 +374,6 @@ extension Array { } ``` -### Acknowledging unsafety - -All of the features described above are available in Swift regardless of whether strict memory safety checking is enabled. When strict memory safety checking is enabled, each use of an unsafe construct of any form must be ackwnowledged in the source code with one of the forms below, which provides an in-source auditable indication of where memory unsafety issues can arise. The following section describes each of the features for acknowledging memory unsafety. - -#### `unsafe` expression - -Any time there is executable code that makes use of unsafe constructs, the compiler will produce a diagnostic that indicates the use of those unsafe constructs unless it is within an `unsafe` expression. For example, consider the `DataWrapper` example from an earlier section: - -```swift -public struct DataWrapper { - var buffer: UnsafeBufferPointer -} - -extension DataWrapper { - public func checksum() -> Int32 { - crc32(0, buffer.baseAddress, buffer.count) - } -} -``` - -The property `buffer` uses an unsafe type, `UnsafeBufferPointer`. When using that property in the implementation of `checksum`, the Swift compiler will produce a warning when strict memory safety checking is enabled: - -```swift -warning: expression uses unsafe constructs but is not marked with 'unsafe' -``` - -This warning can be suppressed using the `unsafe` expression, as follows: - -```swift -extension DataWrapper { - public func checksum() -> Int32 { - unsafe crc32(0, buffer.baseAddress, buffer.count) - } -} -``` - -The `unsafe` expression is much like `try` and `await`, in that it acknowledges that unsafe constructs (`buffer`) are used within the subexpression but otherwise does not change the type. Unlike `try` and `await`, which require the enclosing context to handle throwing or be asynchronous, respectively, the `unsafe` expression does not imply any requirements about the enclosing block: it is purely a marker to indicate the presence of unsafe code, silencing a diagnostic. - #### Types with unsafe storage Types that wrap unsafe types will often encapsulate the unsafe behavior to provide safe interfaces. However, this requires deliberate design and implementation, potentially involving adding specific preconditions. When strict safety checking is enabled, a type whose storage includes any unsafe types or conformances will be diagnosed as involving unsafe code. For example, the `DataWrapper` struct from the prior section From 0d965868efd6c53f85ca7ecd6f7052d20ab2256f Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 12 Feb 2025 15:45:41 +0000 Subject: [PATCH 4052/4563] [SE-0453] `InlineArray` proposal is accepted --- proposals/0453-vector.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/0453-vector.md b/proposals/0453-vector.md index 9828b90203..7d143f7413 100644 --- a/proposals/0453-vector.md +++ b/proposals/0453-vector.md @@ -3,10 +3,10 @@ * Proposal: [SE-0453](0453-vector.md) * Authors: [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) -* Status: **Active review (December 5...December 17, 2024)** +* Status: **Accepted** * Roadmap: [Approaches for fixed-size arrays](https://forums.swift.org/t/approaches-for-fixed-size-arrays/58894) * Implementation: [swiftlang/swift#76438](https://github.com/swiftlang/swift/pull/76438) -* Review: ([pitch](https://forums.swift.org/t/vector-a-fixed-size-array/75264)) ([review](https://forums.swift.org/t/se-0453-vector-a-fixed-size-array/76004)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0453-vector-a-fixed-size-array/76411)) ([second review](https://forums.swift.org/t/second-review-se-0453-vector-a-fixed-size-array/76412)) +* Review: ([pitch](https://forums.swift.org/t/vector-a-fixed-size-array/75264)) ([first review](https://forums.swift.org/t/se-0453-vector-a-fixed-size-array/76004)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0453-vector-a-fixed-size-array/76411)) ([second review](https://forums.swift.org/t/second-review-se-0453-vector-a-fixed-size-array/76412)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0453-inlinearray-formerly-vector-a-fixed-size-array/77678)) ## Introduction @@ -82,7 +82,7 @@ member it will be inline allocated on the heap with the rest of the properties. alone. ```swift -func complexAlgorithm() -> Int { +func complexAlgorithm() { // This is a stack allocation, no 'malloc's or reference counting here! let elements: InlineArray<4, Int> = [1, 2, 3, 4] @@ -215,10 +215,10 @@ func array(_: [T]) {} array([1, 2, 3]) // passes a Swift.Array array([1, 2, 3] as InlineArray<3, Int>) // error: 'InlineArray<3, Int>' is not convertible to 'Array' -func InlineArray(_: InlineArray<3, T>) {} +func inlineArray(_: InlineArray<3, T>) {} -InlineArray([1, 2, 3]) // passes a Swift.InlineArray -InlineArray([1, 2, 3] as [Int]) // error: 'Array' is not convertible to 'InlineArray<3, Int>' +inlineArray([1, 2, 3]) // passes a Swift.InlineArray +inlineArray([1, 2, 3] as [Int]) // error: 'Array' is not convertible to 'InlineArray<3, Int>' ``` I discuss later about a hypothetical `ExpressibleByInlineArrayLiteral` and the design @@ -276,7 +276,7 @@ extension InlineArray where Element: ~Copyable { /// Initializes every element in this InlineArray by running the closure with the /// previously initialized element. /// - /// This will call the closure 'count' times, where 'count' is the static + /// This will call the closure `count - 1` times, where `count` is the static /// count of the InlineArray, to initialize every element by passing the closure /// an immutable borrow reference to the previously initialized element. The /// closure is allowed to throw an error at any point during initialization at From 85e2171872f406de6b78f5b2f8b02ebfef8af474 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 12 Feb 2025 09:14:29 -0800 Subject: [PATCH 4053/4563] SE-0458: Fix typos --- proposals/0458-strict-memory-safety.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index 5c4f45cc09..a56d0f2ce5 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -470,7 +470,7 @@ The `@unsafe` annotation is at the class level because any use of the `Sub` type #### `for..in` loops -Swift's `for..in` loops are effectively implemented as syntactic sugar over the `Sequence` and `IteratorProtocol` protocols, where the `for..in` creates a new iterator (with `Sequence.makeIterator()`) and then calls its `next()` operation for each loop iteration. If the conformances to `Sequence` are `IteratorProtocol` is `@unsafe`, the loop will introduce a warning: +Swift's `for..in` loops are effectively implemented as syntactic sugar over the `Sequence` and `IteratorProtocol` protocols, where the `for..in` creates a new iterator (with `Sequence.makeIterator()`) and then calls its `next()` operation for each loop iteration. If the conformances to `Sequence` or `IteratorProtocol` are `@unsafe`, the loop will introduce a warning: ```swift let someUnsafeBuffer: UnsafeBufferPointer = unsafe ... @@ -824,7 +824,7 @@ We could introduce an optional `message` argument to the `@unsafe` attribute, wh ## Revision history -* **Revision 3 (following second review eextension)** +* **Revision 3 (following second review extension)** * Do not require declarations with unsafe types in their signature to be marked `@unsafe`; it is implied. They may be marked `@safe` to indicate that they are actually safe. * Add `unsafe` for iteration via the `for..in` syntax. * Add C(++) interoperability section that infers `@unsafe` for C types that involve pointers. From acff10f5d0c410da597de4e92e6641fa7bc177ed Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 12 Feb 2025 23:12:27 +0000 Subject: [PATCH 4054/4563] SE-0335: `any` syntax is not required in Swift 6 mode This information has become somewhat misleading because it didn't happen. `ExistentialAny` is scheduled for a post-Swift-6 language mode. --- proposals/0335-existential-any.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/proposals/0335-existential-any.md b/proposals/0335-existential-any.md index 8f12ca614e..eaf480380e 100644 --- a/proposals/0335-existential-any.md +++ b/proposals/0335-existential-any.md @@ -62,7 +62,7 @@ Despite these significant and often undesirable implications, existential types ## Proposed solution -I propose to make existential types syntactically explicit in the language using the `any` keyword. This proposal introduces the new syntax in the Swift 5 language mode, and this syntax should be required for existential types under the Swift 6 language mode. +I propose to make existential types syntactically explicit in the language using the `any` keyword. This proposal introduces the new syntax in the Swift 5 language mode, and this syntax should be required for existential types under a future language mode. In Swift 5, anywhere that an existential type can be used today, the `any` keyword can be used to explicitly denote an existential type: @@ -80,10 +80,10 @@ let pq1: P & Q = S() // 'P & Q' in this context is an existential type let pq2: any P & Q = S() // 'any P & Q' is an explicit existential type ``` -In Swift 6, existential types are required be explicitly spelled with `any`: +In a future language mode, existential types are required to be explicitly spelled with `any`: ```swift -// Swift 6 mode +// Future language mode protocol P {} protocol Q {} @@ -96,7 +96,7 @@ let pq1: P & Q = S() // error let pq2: any P & Q = S() // okay ``` -The Swift 6 behavior can be enabled in earlier language modes with the [upcoming feature flag](0362-piecemeal-future-features.md) `ExistentialAny`. +This behavior can be enabled in earlier language modes with the [upcoming feature flag](0362-piecemeal-future-features.md) `ExistentialAny`. ## Detailed design @@ -192,11 +192,9 @@ func generic(value: T) { ... } func generic(value: T) { ... } // error ``` -Once the `any` spelling is required under the Swift 6 language mode, a type alias to a plain protocol name is not a valid type witness for an associated type requirement; existential type witnesses must be explicit in the `typealias` with `any`: +Once the `any` spelling is required under a future language mode, a type alias to a plain protocol name is not a valid type witness for an associated type requirement; existential type witnesses must be explicit in the `typealias` with `any`: ```swift -// Swift 6 code - protocol P {} protocol Requirements { @@ -214,16 +212,16 @@ struct S2: Requirements { ## Source compatibility -Enforcing that existential types use the `any` keyword will require a source change. To ease the migration, I propose to start allowing existential types to be spelled with `any` with the Swift 5.6 compiler, and require existential types to be spelled with `any` under the Swift 6 language mode. The old existential type syntax will continue to be supported under the Swift 5 language mode, and the transition to the new syntax is mechanical, so it can be performed automatically by a migrator. +Enforcing that existential types use the `any` keyword will require a source change. To ease the migration, I propose to start allowing existential types to be spelled with `any` with the Swift 5.6 compiler, and require existential types to be spelled with `any` under a future language mode. The old existential type syntax will continue to be supported under the Swift 5 language mode, and the transition to the new syntax is mechanical, so it can be performed automatically by a migrator. -[SE-0309 Unlock existentials for all protocols](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md) enables more code to be written using existential types. To minimize the amount of new code written that will become invalid in Swift 6, I propose requiring `any` immediately for protocols with `Self` and associated type requirements. This introduces an inconsistency for protocols under the Swift 5 language mode, but this inconsistency already exists today (because you cannot use certain protocols as existential types at all), and the syntax difference serves two purposes: +[SE-0309 Unlock existentials for all protocols](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md) enables more code to be written using existential types. To minimize the amount of new code written that will become invalid under `ExistentialAny`, I propose requiring `any` immediately for protocols with `Self` and associated type requirements. This introduces an inconsistency for protocols under the Swift 5 language mode, but this inconsistency already exists today (because you cannot use certain protocols as existential types at all), and the syntax difference serves two purposes: 1. It saves programmers time in the long run by preventing them from writing new code that will become invalid later. -2. It communicates the existence of `any` and encourages programmers to start using it for other existential types before adopting Swift 6. +2. It communicates the existence of `any` and encourages programmers to start using it for other existential types before adopting `ExistentialAny`. -### Transitioning to `any` in Swift 6 +### Transitioning to `any` -The new `any` syntax will be staged in over several major Swift releases. In the release where `any` is introduced, the compiler will not emit any warnings for the lack of `any` on existential types. After `any` is introduced, warnings will be added to guide programmers toward the new syntax. Finally, these warnings can become errors, or [plain protocol names can be repurposed](#re-purposing-the-plain-protocol-name), in Swift 6. +The new `any` syntax will be staged in over several major Swift releases. In the release where `any` is introduced, the compiler will not emit diagnostics for the lack of `any` on existential types, save for the aforementioned cases. After `any` is introduced, warnings will be added to guide programmers toward the new syntax. Finally, a missing `any` will become an unconditional error, or [plain protocol names may be repurposed](#re-purposing-the-plain-protocol-name) — in Swift 6 or a later language mode. ## Effect on ABI stability From 7d2ae4e7d87682e6fcfa1095d24aa53ffd2aebac Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 29 Jan 2025 09:41:27 +0000 Subject: [PATCH 4055/4563] burp --- ...migration-tooling-for-upcoming-features.md | 335 ++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 proposals/NNNN-migration-tooling-for-upcoming-features.md diff --git a/proposals/NNNN-migration-tooling-for-upcoming-features.md b/proposals/NNNN-migration-tooling-for-upcoming-features.md new file mode 100644 index 0000000000..47097ba859 --- /dev/null +++ b/proposals/NNNN-migration-tooling-for-upcoming-features.md @@ -0,0 +1,335 @@ +# Adoption tooling for Swift features + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Anthony Latsis](https://github.com/AnthonyLatsis) +* Review Manager: TBD +* Status: **Awaiting implementation** +* Implementation: TBD +* Review: TBD + +## Introduction + +The future we envision for Swift will take many more valiant evolutionary +decisions and major transformations with a proportional impact on its +expanding domains. + +Source-breaking changes to Swift were first staged behind the now obsolete +Swift 3 language mode. +Each successive major release has since included a correponding language mode, +using the previous language mode as the default to maximize source +compatibility. +For example, Swift 6 compilers operate in the Swift 5 language mode by default. +Users that are not ready to adopt the new default can still specify an earlier +language mode explicitly. +Once the time is right, old language modes together with the legacy behaviors +they manifest will be proposed to be deprecated. + +The cumulative source compatibility impact of the changes that were accreting +around a converging Swift 6 language mode gave rise to the +[Swift feature model][SE-0362], which enabled piecemeal adoption of individual +features as opposed to an entire language mode. +Upcoming features facilitated sooner adoption of improvements and drastically +reduced the pressures in our evolutionary model. + +This proposal centers seeks to improve the experience of adopting individual +features. +The proposition is that the growing complexity and diversification of Swift +calls for a flexible, integrated mechanism for supporting quality assistance +with feature adoption. +And that — in principle — comprehensive, code-aware assistance can be delivered +without breaking source and acted upon incrementally. + +## Motivation + +Whether you are adjusting code to follow new language rules or researching ways +to apply new functionality, adopting features can be a time-consuming endeavor +at the least. + +Some source-breaking language features are anticipated to generate hundreds +of targeted errors in sizable projects. +Occasionally, errors will also cascade down the dependecy graph or fall out +from changes in behavior without a clear indication of the precise cause or source of +the issue, requiring further investigation. +Developers are left to either resolve all of these errors or address a subset +and take the risk of switching the feature back off before they can resume +development and focus on other important tasks. + +### User Intent + +> [!CAUTION] +> TODO: No way for users to declare an intetion to adopt a feature + +### Automation + +Many existing and prospective upcoming features imply or implement simple and +consistent code modifications to facilitate the adoption process: + +* [`NonfrozenEnumExhaustivity`][SE-0192]: Restore exhaustivity with + `@unknown default:`. +* [`ConciseMagicFile`][SE-0274]: `#file` → `#filePath`. +* [`ForwardTrailingClosures`][SE-0286]: Disambiguate argument matching by + de-trailing closures and/or inlining default arguments. +* [`ExistentialAny`][SE-0335]: `P` → `any P`. +* [`ImplicitOpenExistentials`][SE-0352]: Suppress opening with `as any P` + coercions. +* [`BareSlashRegexLiterals`][SE-0354]: Disambiguate using parentheses, + e.g. `foo(/a, b/)` → `foo((/a), b/)`. +* [`DeprecateApplicationMain`][SE-0383]: `@UIApplicationMain` → `@main`, + `@NSApplicationMain` → `@main`. +* [`DisableOutwardActorInference`][SE-0401]: Specify global actor isolation + explicitly. +* [`InternalImportsByDefault`][SE-0409]: `import X` → `public import X`. +* [`GlobalConcurrency`][SE-0412]: + - Convert the global variable to a `let` (or) + - `@MainActor`-isolate it (or) + - Mark it with `nonisolated(unsafe)` +* [`MemberImportVisibility`][SE-0444]: Add explicit imports appropriately. +* [`InferSendableFromCaptures`][SE-0418]: Suppress inference with coercions + and type annotations. +* [Inherit isolation by default for async functions][async-inherit-isolation-pitch]: + Mark nonisolated functions with the proposed attribute. + +Feature + +Extending diagnostic metadata to include information that allows for +recognizing these diagnostics and distinguishing semantics-preserving fix-its +from alternative source changes would open up numerous opportunities for +higher-level tools — ranging from the Swift package manager to IDEs — to +implement powerful solutions for organizing, automating, and tuning code +migration processes. + +### Flexibility/Ergonomics + +> [!CAUTION] +> Still a draft. + +Although upcoming features should strive to facilitate code migration, + +language design principles may prevail over bespoke code migration solutions. +Some features, like [StrictConcurrency][SE-0337], inherently require user +intervetion + +Adjusting to new behaviors or language requirements can demand research, +careful consideration, coordinated efforts, and manual code refactorings, +sometimes on a case-by-case basis. + +Currently best solution is to implement custom staging solutions. This approach +has limited applications (why?). + +UPCOMING_FEATURE(DynamicActorIsolation, 423, 6) +UPCOMING_FEATURE(GlobalActorIsolatedTypesUsability, 0434, 6) +UPCOMING_FEATURE(StrictConcurrency, 0337, 6) +UPCOMING_FEATURE(IsolatedDefaultValues, 411, 6) +UPCOMING_FEATURE(RegionBasedIsolation, 414, 6) + +## Proposed solution + +Introduce the notion of a "adoption" mode for individual experimental and +upcoming features. +The core idea behind adoption mode is a declaration of intent that can be +leveraged to build holistic supportive adoption experiences for developers. +If enabling a feature communicates an intent to *enact* rules, adoption mode +communicates an intent to *adopt* them. +An immediate benefit of adoption mode is the capability to deliver source +modifications that can be applied to preserve or improve the behavior of +existing code whenever the feature provides for them. + +> [!NOTE] +> The subject of this proposal is an enhancement to the Swift feature model. +> Applications of adoption mode to existing features are beyond its scope. + +## Detailed design + +### Behavior + +The action of enabling a previously disabled source-breaking feature in adoption +mode per se must never produce compilation errors. +Additionally, this action will have no effect on the state of the feature if +it does not implement the mode. +A corresponding warning will be emitted in this case to avoid the false +impression that the impacted source code is compatible with the feature. + +> [!NOTE] +> Experimental features can be both additive and source-breaking. +> Upcoming features are necessarily source-breaking. + +adoption mode will deliver guidance in the shape of warnings, notes, remarks, +and fix-its, as and when appropriate. + +When implemented, adoption mode for upcoming features is expected to anticipate +and call out any behavioral differences that will result from enacting the +feature, coupling diagnostic messages with counteracting source-compatible +changes and helpful alternatives whenever possible. +Adoption mode cannot guarantee to provide exclusively source-compatible +modifications because the impact of a change on dependent source code is +generally unpredictable. +Neither can it promise to always offer fix-its in the first place for the +same reason in regards to user intention. + +### Interface + +#### Compiler + +The `-enable-*-feature` frontend and driver command line options will start +supporting an optional mode specifier with `adoption` as the only valid mode: + +``` +-enable-upcoming-feature [:] +-enable-experimental-feature [:] + + := adoption +``` + +For example: + +``` +-enable-upcoming-feature InternalImportsByDefault:adoption +``` + +In a series of either of these options applied to a given feature, only the +last option will be honored. +If an upcoming feature is both implied by the effective language mode and +enabled in adoption mode using either of the aforementioned options, the latter +will be disregarded. + +#### Swift package manager + +The [`SwiftSetting.enableUpcomingFeature`] and +[`SwiftSetting.enableExperimentalFeature`] methods from the +[`PackageDescription`](https://developer.apple.com/documentation/packagedescription) +library will be augmented with a `mode` parameter defaulted to match the +current behavior: + +```swift +extension SwiftSetting { + @available(_PackageDescription, introduced: 6.2) + public enum SwiftFeatureMode { + case adoption + case on + } +} +``` +```diff + public static func enableUpcomingFeature( + _ name: String, ++ mode: SwiftFeatureMode = .on, + _ condition: BuildSettingCondition? = nil + ) -> SwiftSetting { ++ let argument = switch mode { ++ case .adoption: "\(name):adoption" ++ case .mode: name ++ } ++ + return SwiftSetting( +- name: "enableUpcomingFeature", value: [name], condition: condition) ++ name: "enableUpcomingFeature", value: [argument], condition: condition) + } +``` +```diff + public static func enableExperimentalFeature( + _ name: String, ++ mode: SwiftFeatureMode = .on, + _ condition: BuildSettingCondition? = nil + ) -> SwiftSetting { ++ let argument = switch mode { ++ case .adoption: "\(name):adoption" ++ case .mode: name ++ } ++ + return SwiftSetting( +- name: "enableExperimentalFeature", value: [name], condition: condition) ++ name: "enableExperimentalFeature", value: [argument], condition: condition) + } +``` + +For example: + +``` +SwiftSetting.enableUpcomingFeature("InternalImportsByDefault", mode: .adoption) +``` + +### Diagnostics + +Diagnostics emitted in relation to a specific feature in adoption mode must +belong to a diagnostic group named after the feature. +There are several reasons why this will be useful: +* Future feature-oriented adoption tooling can use the group identifier to + filter out relevant diagnostics. +* IDEs and other diagnostic consumers can integrate group identifiers into + their interfaces to, well, group diagnostics, as well as to communicate + relationships between diagnostics and features. This can prove especially + handy when multiple features are simultaneously enabled in adoption mode. + +## Source compatibility + +This proposal does not affect language rules. The described changes to the API +surface are source-compatible. + +## ABI compatibility + +This proposal does not affect binary compatibility or binary interfaces. + +## Implications on adoption + +Demoting an enabled source-breaking feature to adoption mode may affect +behavior and is therefore a potentially source-breaking action. + +## Future directions + +### Augment diagnostic metadata + + + +### Support baseline features + +Adoption mode can be extrapolated to baseline features, such as `TypedThrows` +or [opaque parameter types][SE-0341], with an emphasis on actionable adoption +tips and otherwise unsolicited educational notes. +These additive features are hard-enabled in all language modes and become an +integral part of the language as soon as they ship. +Baseline feature identifiers are currently kept around for the sole purpose of +supporting [feature availability checks][feature-detection] in conditional +compilation blocks. + +### `swift adopt` + +The Swift package manager could implement an `adopt` subcommand with an +interactive command line interface similar to `git add --patch` for selecting +and applying fix-its ... + +## Alternatives considered + +### Naming + + + +## Acknowledgements + +This proposal was inspired by documents prepared by [Allan Shortlidge][Allan] +and [Holly Borla][Holly]. +Special thanks to Holly for her feedback throughout the draft stage. + + + +[Holly]: https://github.com/hborla +[Allan]: https://github.com/tshortli + +[SE-0192]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0192-non-exhaustive-enums.md +[SE-0274]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0274-magic-file.md +[SE-0286]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0286-forward-scan-trailing-closures.md +[SE-0296]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0296-async-await.md +[SE-0335]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0335-existential-any.md +[SE-0337]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0337-support-incremental-migration-to-concurrency-checking.md +[SE-0341]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0341-opaque-parameters.md +[SE-0352]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md +[SE-0354]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0354-regex-literals.md +[SE-0362]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md +[feature-detection]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md#feature-detection-in-source-code +[SE-0383]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md +[SE-0401]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0401-remove-property-wrapper-isolation.md +[SE-0409]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md +[SE-0411]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0411-isolated-default-values.md +[SE-0412]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0412-strict-concurrency-for-global-variables.md +[SE-0418]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0418-inferring-sendable-for-methods.md +[SE-0444]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md +[async-inherit-isolation-pitch]: https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862 From dafb468cba8eee563e65860151211efffc5eb165 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Fri, 14 Feb 2025 21:36:14 +0000 Subject: [PATCH 4056/4563] burp --- proposals/NNNN-migration-tooling-for-upcoming-features.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/NNNN-migration-tooling-for-upcoming-features.md b/proposals/NNNN-migration-tooling-for-upcoming-features.md index 47097ba859..67da10cf4f 100644 --- a/proposals/NNNN-migration-tooling-for-upcoming-features.md +++ b/proposals/NNNN-migration-tooling-for-upcoming-features.md @@ -24,12 +24,12 @@ language mode explicitly. Once the time is right, old language modes together with the legacy behaviors they manifest will be proposed to be deprecated. -The cumulative source compatibility impact of the changes that were accreting -around a converging Swift 6 language mode gave rise to the +In Swift 5.8, the cumulative source compatibility impact of a still converging +Swift 6 language mode gave rise to the [Swift feature model][SE-0362], which enabled piecemeal adoption of individual features as opposed to an entire language mode. -Upcoming features facilitated sooner adoption of improvements and drastically -reduced the pressures in our evolutionary model. +Among other things, upcoming features facilitated earlier adoption of +improvements and drastically reduced the pressures in our evolutionary model. This proposal centers seeks to improve the experience of adopting individual features. From a7ea7aed14bf4f7612f762020973c3d79988f2cd Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Fri, 14 Feb 2025 22:15:04 +0000 Subject: [PATCH 4057/4563] burp --- proposals/NNNN-migration-tooling-for-upcoming-features.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-migration-tooling-for-upcoming-features.md b/proposals/NNNN-migration-tooling-for-upcoming-features.md index 67da10cf4f..d748799ab7 100644 --- a/proposals/NNNN-migration-tooling-for-upcoming-features.md +++ b/proposals/NNNN-migration-tooling-for-upcoming-features.md @@ -62,7 +62,7 @@ development and focus on other important tasks. ### Automation Many existing and prospective upcoming features imply or implement simple and -consistent code modifications to facilitate the adoption process: +consistent code modifications to facilitate adoption processes: * [`NonfrozenEnumExhaustivity`][SE-0192]: Restore exhaustivity with `@unknown default:`. From 80fa27863bf2dd33eeb591b0317ee96a4198b354 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Fri, 14 Feb 2025 22:41:05 +0000 Subject: [PATCH 4058/4563] burp --- ...NNNN-migration-tooling-for-upcoming-features.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/proposals/NNNN-migration-tooling-for-upcoming-features.md b/proposals/NNNN-migration-tooling-for-upcoming-features.md index d748799ab7..becd83f9b3 100644 --- a/proposals/NNNN-migration-tooling-for-upcoming-features.md +++ b/proposals/NNNN-migration-tooling-for-upcoming-features.md @@ -15,7 +15,7 @@ expanding domains. Source-breaking changes to Swift were first staged behind the now obsolete Swift 3 language mode. -Each successive major release has since included a correponding language mode, +Each successive major release has since included a corresponding language mode, using the previous language mode as the default to maximize source compatibility. For example, Swift 6 compilers operate in the Swift 5 language mode by default. @@ -31,7 +31,7 @@ features as opposed to an entire language mode. Among other things, upcoming features facilitated earlier adoption of improvements and drastically reduced the pressures in our evolutionary model. -This proposal centers seeks to improve the experience of adopting individual +This proposal seeks to improve the experience of adopting individual features. The proposition is that the growing complexity and diversification of Swift calls for a flexible, integrated mechanism for supporting quality assistance @@ -89,8 +89,6 @@ consistent code modifications to facilitate adoption processes: * [Inherit isolation by default for async functions][async-inherit-isolation-pitch]: Mark nonisolated functions with the proposed attribute. -Feature - Extending diagnostic metadata to include information that allows for recognizing these diagnostics and distinguishing semantics-preserving fix-its from alternative source changes would open up numerous opportunities for @@ -124,7 +122,7 @@ UPCOMING_FEATURE(RegionBasedIsolation, 414, 6) ## Proposed solution -Introduce the notion of a "adoption" mode for individual experimental and +Introduce the notion of an "adoption" mode for individual experimental and upcoming features. The core idea behind adoption mode is a declaration of intent that can be leveraged to build holistic supportive adoption experiences for developers. @@ -142,6 +140,9 @@ existing code whenever the feature provides for them. ### Behavior +Adoption mode should deliver guidance in the shape of warnings, notes, remarks, +and fix-its, as and when appropriate. + The action of enabling a previously disabled source-breaking feature in adoption mode per se must never produce compilation errors. Additionally, this action will have no effect on the state of the feature if @@ -153,9 +154,6 @@ impression that the impacted source code is compatible with the feature. > Experimental features can be both additive and source-breaking. > Upcoming features are necessarily source-breaking. -adoption mode will deliver guidance in the shape of warnings, notes, remarks, -and fix-its, as and when appropriate. - When implemented, adoption mode for upcoming features is expected to anticipate and call out any behavioral differences that will result from enacting the feature, coupling diagnostic messages with counteracting source-compatible From 8707bf78f6e11d029948ad880e8a6971c8bacd0d Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 14 Feb 2025 19:11:51 -0800 Subject: [PATCH 4059/4563] Editorial pass over the introduction section. --- ...migration-tooling-for-upcoming-features.md | 39 +++++-------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/proposals/NNNN-migration-tooling-for-upcoming-features.md b/proposals/NNNN-migration-tooling-for-upcoming-features.md index becd83f9b3..32e254c6a9 100644 --- a/proposals/NNNN-migration-tooling-for-upcoming-features.md +++ b/proposals/NNNN-migration-tooling-for-upcoming-features.md @@ -9,35 +9,16 @@ ## Introduction -The future we envision for Swift will take many more valiant evolutionary -decisions and major transformations with a proportional impact on its -expanding domains. - -Source-breaking changes to Swift were first staged behind the now obsolete -Swift 3 language mode. -Each successive major release has since included a corresponding language mode, -using the previous language mode as the default to maximize source -compatibility. -For example, Swift 6 compilers operate in the Swift 5 language mode by default. -Users that are not ready to adopt the new default can still specify an earlier -language mode explicitly. -Once the time is right, old language modes together with the legacy behaviors -they manifest will be proposed to be deprecated. - -In Swift 5.8, the cumulative source compatibility impact of a still converging -Swift 6 language mode gave rise to the -[Swift feature model][SE-0362], which enabled piecemeal adoption of individual -features as opposed to an entire language mode. -Among other things, upcoming features facilitated earlier adoption of -improvements and drastically reduced the pressures in our evolutionary model. - -This proposal seeks to improve the experience of adopting individual -features. -The proposition is that the growing complexity and diversification of Swift -calls for a flexible, integrated mechanism for supporting quality assistance -with feature adoption. -And that — in principle — comprehensive, code-aware assistance can be delivered -without breaking source and acted upon incrementally. +In Swift 5.8 introduced [upcoming features][SE-0362], +which enabled piecemeal adoption of individual source incompatible +changes that are enabled by default in a new langauge mode. +Many upcoming features have a mechanical migration, meaning the compiler can +determine the exact source changes necessary to allow the code to compile under +the upcoming feature while preserving the behavior of the code. +This proposal seeks to improve the experience of enabling individual +upcoming features by providing tools that produce the necessary source code +changes automatically for a given set of upcoming features that a programmer +wants to enable. ## Motivation From 593a0699ef6589cae292223221f416060c5b8756 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 14 Feb 2025 20:03:42 -0800 Subject: [PATCH 4060/4563] Editorial pass over the automation section. --- ...migration-tooling-for-upcoming-features.md | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/proposals/NNNN-migration-tooling-for-upcoming-features.md b/proposals/NNNN-migration-tooling-for-upcoming-features.md index 32e254c6a9..b816352c52 100644 --- a/proposals/NNNN-migration-tooling-for-upcoming-features.md +++ b/proposals/NNNN-migration-tooling-for-upcoming-features.md @@ -77,29 +77,14 @@ higher-level tools — ranging from the Swift package manager to IDEs — to implement powerful solutions for organizing, automating, and tuning code migration processes. -### Flexibility/Ergonomics +It's not always feasible for an upcoming feature to have a mechanical +migration path. For example, the following upcoming features require manual +migration: -> [!CAUTION] -> Still a draft. - -Although upcoming features should strive to facilitate code migration, - -language design principles may prevail over bespoke code migration solutions. -Some features, like [StrictConcurrency][SE-0337], inherently require user -intervetion - -Adjusting to new behaviors or language requirements can demand research, -careful consideration, coordinated efforts, and manual code refactorings, -sometimes on a case-by-case basis. - -Currently best solution is to implement custom staging solutions. This approach -has limited applications (why?). - -UPCOMING_FEATURE(DynamicActorIsolation, 423, 6) -UPCOMING_FEATURE(GlobalActorIsolatedTypesUsability, 0434, 6) -UPCOMING_FEATURE(StrictConcurrency, 0337, 6) -UPCOMING_FEATURE(IsolatedDefaultValues, 411, 6) -UPCOMING_FEATURE(RegionBasedIsolation, 414, 6) +* [`DynamicActorIsolation`][SE-0423] +* [`GlobalActorIsolatedTypesUsability`][SE-0434] +* [`StrictConcurrency`][SE-0337] +* [`IsolatedDefaultValues`][SE-0411] ## Proposed solution @@ -310,5 +295,7 @@ Special thanks to Holly for her feedback throughout the draft stage. [SE-0411]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0411-isolated-default-values.md [SE-0412]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0412-strict-concurrency-for-global-variables.md [SE-0418]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0418-inferring-sendable-for-methods.md +[SE-0423]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0423-dynamic-actor-isolation.md +[SE-0434]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md [SE-0444]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md [async-inherit-isolation-pitch]: https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862 From 78fd5a2efb32b36df133c8112fffc683387b20b3 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 14 Feb 2025 20:08:45 -0800 Subject: [PATCH 4061/4563] Clarify that future proposals introducing upcoming features should include a migration. --- proposals/NNNN-migration-tooling-for-upcoming-features.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/proposals/NNNN-migration-tooling-for-upcoming-features.md b/proposals/NNNN-migration-tooling-for-upcoming-features.md index b816352c52..7cd343e56f 100644 --- a/proposals/NNNN-migration-tooling-for-upcoming-features.md +++ b/proposals/NNNN-migration-tooling-for-upcoming-features.md @@ -98,9 +98,11 @@ An immediate benefit of adoption mode is the capability to deliver source modifications that can be applied to preserve or improve the behavior of existing code whenever the feature provides for them. -> [!NOTE] -> The subject of this proposal is an enhancement to the Swift feature model. -> Applications of adoption mode to existing features are beyond its scope. +This proposal will support the set of existing upcoming features that +have mechanical migrations, as described in the [Automation](#automation) +section. All future proposals that introduce new upcoming features should +include a mechanical migration via adoption mode in the Source compatibility +section of the proposal. ## Detailed design From 9bedf553f2806c6dbd764ed9b06a9de6dfe74a9c Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 17 Feb 2025 09:40:32 +0000 Subject: [PATCH 4062/4563] burp 0.9 --- ...migration-tooling-for-upcoming-features.md | 184 ++++++++++-------- 1 file changed, 106 insertions(+), 78 deletions(-) diff --git a/proposals/NNNN-migration-tooling-for-upcoming-features.md b/proposals/NNNN-migration-tooling-for-upcoming-features.md index 7cd343e56f..d049330244 100644 --- a/proposals/NNNN-migration-tooling-for-upcoming-features.md +++ b/proposals/NNNN-migration-tooling-for-upcoming-features.md @@ -10,40 +10,48 @@ ## Introduction In Swift 5.8 introduced [upcoming features][SE-0362], -which enabled piecemeal adoption of individual source incompatible -changes that are enabled by default in a new langauge mode. +which enabled piecemeal adoption of individual source-incompatible changes that +are included in a language mode. Many upcoming features have a mechanical migration, meaning the compiler can -determine the exact source changes necessary to allow the code to compile under +determine the exact source changes necessary to allow the code to compile under the upcoming feature while preserving the behavior of the code. This proposal seeks to improve the experience of enabling individual -upcoming features by providing tools that produce the necessary source code -changes automatically for a given set of upcoming features that a programmer -wants to enable. +upcoming features by providing a mechanism for producing the necessary source +code changes automatically for a given set of upcoming features that a +programmer wants to enable. ## Motivation -Whether you are adjusting code to follow new language rules or researching ways -to apply new functionality, adopting features can be a time-consuming endeavor -at the least. - -Some source-breaking language features are anticipated to generate hundreds -of targeted errors in sizable projects. -Occasionally, errors will also cascade down the dependecy graph or fall out -from changes in behavior without a clear indication of the precise cause or source of -the issue, requiring further investigation. -Developers are left to either resolve all of these errors or address a subset -and take the risk of switching the feature back off before they can resume -development and focus on other important tasks. - -### User Intent - -> [!CAUTION] -> TODO: No way for users to declare an intetion to adopt a feature +Adopting certain features is a time-consuming endeavor at the least. +It is the responsibility of project maintainers to preserve source (and binary) +compatibility both internally and externally for library clients when enabling +an upcoming feature, which can be difficult or tedious without having tools to +help detect possibly inadvertent changes and perform monotonous migration +shenanigans for you. +*Our* responsibility is to make that an easier task for everybody. + +### User intent + +A primary limiting factor in how proactively and accurately the compiler can +assist developers with adopting a feature is a lack of comprehension of user +intent. +Is the developer expecting guidance on adopting an improvement? +All the compiler knows to do when a feature is enabled is to compile code +accordingly. +This suffices if a feature merely supplants an existing syntactical construct +or changes the behavior of existing code in strictly predictable ways because +Swift can infer the need to suggest a fix just from spotting certain code +patterns. + +Needless to say, not all upcoming features fall under these criteria (and not +all features are source-breaking in the first place). Consider +[`ConciseMagicFile`][SE-0274], which changes the meaning of an existing +literal. ### Automation Many existing and prospective upcoming features imply or implement simple and -consistent code modifications to facilitate adoption processes: +consistent source modifications to facilitate adoption: * [`NonfrozenEnumExhaustivity`][SE-0192]: Restore exhaustivity with `@unknown default:`. @@ -72,14 +80,15 @@ consistent code modifications to facilitate adoption processes: Extending diagnostic metadata to include information that allows for recognizing these diagnostics and distinguishing semantics-preserving fix-its -from alternative source changes would open up numerous opportunities for +from alternative source changes will open up numerous opportunities for higher-level tools — ranging from the Swift package manager to IDEs — to -implement powerful solutions for organizing, automating, and tuning code -migration processes. +implement powerful solutions for organizing, automating, and tuning feature +adoption processes. -It's not always feasible for an upcoming feature to have a mechanical -migration path. For example, the following upcoming features require manual -migration: +It is not always feasible or in line with language design principles for an +upcoming feature to have a mechanical migration path. +For example, the following upcoming features require manual migration to +preserve semantics: * [`DynamicActorIsolation`][SE-0423] * [`GlobalActorIsolatedTypesUsability`][SE-0434] @@ -91,7 +100,7 @@ migration: Introduce the notion of an "adoption" mode for individual experimental and upcoming features. The core idea behind adoption mode is a declaration of intent that can be -leveraged to build holistic supportive adoption experiences for developers. +leveraged to build better supportive adoption experiences for developers. If enabling a feature communicates an intent to *enact* rules, adoption mode communicates an intent to *adopt* them. An immediate benefit of adoption mode is the capability to deliver source @@ -100,21 +109,18 @@ existing code whenever the feature provides for them. This proposal will support the set of existing upcoming features that have mechanical migrations, as described in the [Automation](#automation) -section. All future proposals that introduce new upcoming features should -include a mechanical migration via adoption mode in the Source compatibility -section of the proposal. +section. +All future proposals that introduce a new upcoming feature and provide a +mechanical migration are expected to support adoption mode and detail its +behavior in the *Source compatibility* section of the proposal. ## Detailed design ### Behavior -Adoption mode should deliver guidance in the shape of warnings, notes, remarks, -and fix-its, as and when appropriate. - The action of enabling a previously disabled source-breaking feature in adoption -mode per se must never produce compilation errors. -Additionally, this action will have no effect on the state of the feature if -it does not implement the mode. +mode per se must not cause compilation errors . +Additionally, this action will have no effect if the mode is not supported. A corresponding warning will be emitted in this case to avoid the false impression that the impacted source code is compatible with the feature. @@ -122,15 +128,24 @@ impression that the impacted source code is compatible with the feature. > Experimental features can be both additive and source-breaking. > Upcoming features are necessarily source-breaking. -When implemented, adoption mode for upcoming features is expected to anticipate -and call out any behavioral differences that will result from enacting the -feature, coupling diagnostic messages with counteracting source-compatible -changes and helpful alternatives whenever possible. -Adoption mode cannot guarantee to provide exclusively source-compatible -modifications because the impact of a change on dependent source code is -generally unpredictable. -Neither can it promise to always offer fix-its in the first place for the -same reason in regards to user intention. +Adoption mode should deliver guidance in the shape of regular diagnostics. +For arbitrary upcoming features, adoption mode is expected to anticipate and +call out any compatibility issues that result from enacting the feature, +coupling diagnostic messages with counteracting compatible changes and helpful +alternatives whenever feasible. +Compatibility issues encompass both source and binary compatibility issues, +including behavioral changes. + +Note that adoption mode does not provide any new general guarantees in respect +to fix-its. +We cannot promise to offer exclusively compatible modifications. +Besides the impact of a change on dependent source code being generally +unpredictable, it can be reasonable to couple compatible fix-its with +potentially incompatible, albeit better, alternatives, as in `any P` → `some P`. +The same stands for provision of modifications — features might not have a +mechanical migration path, and the compiler remains inherently limited in the +extent to which it can make assumptions about what is helpful or best for the +programmer. ### Interface @@ -217,14 +232,10 @@ SwiftSetting.enableUpcomingFeature("InternalImportsByDefault", mode: .adoption) ### Diagnostics Diagnostics emitted in relation to a specific feature in adoption mode must -belong to a diagnostic group named after the feature. -There are several reasons why this will be useful: -* Future feature-oriented adoption tooling can use the group identifier to - filter out relevant diagnostics. -* IDEs and other diagnostic consumers can integrate group identifiers into - their interfaces to, well, group diagnostics, as well as to communicate - relationships between diagnostics and features. This can prove especially - handy when multiple features are simultaneously enabled in adoption mode. +belong to a diagnostic group named after the feature. The names of diagnostic +groups can be displayed along with diagnostic messages using +`-print-diagnostic-groups` and can be used to associate a message with a +particular feature. ## Source compatibility @@ -237,43 +248,59 @@ This proposal does not affect binary compatibility or binary interfaces. ## Implications on adoption -Demoting an enabled source-breaking feature to adoption mode may affect -behavior and is therefore a potentially source-breaking action. +Entering or exiting adoption mode may affect behavior and is therefore a +potentially source-breaking action. ## Future directions -### Augment diagnostic metadata - - - -### Support baseline features - -Adoption mode can be extrapolated to baseline features, such as `TypedThrows` -or [opaque parameter types][SE-0341], with an emphasis on actionable adoption -tips and otherwise unsolicited educational notes. -These additive features are hard-enabled in all language modes and become an -integral part of the language as soon as they ship. -Baseline feature identifiers are currently kept around for the sole purpose of -supporting [feature availability checks][feature-detection] in conditional -compilation blocks. +### Applications beyond migration + +Adoption mode can be extrapolated to additive features, such as +[typed `throws`][SE-0413] or [opaque parameter types][SE-0341], by providing +actionable adoption tips. +Additive features are hard-enabled and become an integral part of the language +as soon as they ship. +Many recent additive features are already integrated into the Swift feature +model and kept around for the sole purpose of supporting +[feature availability checks][feature-detection] in conditional compilation +blocks. + +Another potential direction for adoption mode is promotion of best practices. + +### Augmented diagnostic metadata + +The current serialization format for diagnostics does not include information +about diagnostic groups or whether a particular fix-it preserves semantics. +There are several reasons why this data is essential for future tools built +around adoption mode: +* The diagnostic group name can be used to, well, group diagnostics, as well as + to communicate relationships between diagnostics and features and filter out + relevant diagnostics. + This can prove especially handy when multiple features are simultaneously + enabled in adoption mode, or when similar diagnostic messages are caused by + distinct features. +* Fix-its that preserve semantics can be prioritized and auto-applied in + previews. ### `swift adopt` -The Swift package manager could implement an `adopt` subcommand with an -interactive command line interface similar to `git add --patch` for selecting -and applying fix-its ... +The Swift package manager could implement an `adopt` subcommand for interactive +review and application of adoption mode output for a given set of features, +with a command line interface similar to `git add --patch`. ## Alternatives considered ### Naming - +Perhaps the most intuitive alternative to "adoption" is "migration". We +settled on the former because there is no reason for this concept to be limited +to upcoming features or migrational changes. ## Acknowledgements This proposal was inspired by documents prepared by [Allan Shortlidge][Allan] and [Holly Borla][Holly]. -Special thanks to Holly for her feedback throughout the draft stage. +Special thanks to Holly for her guidance throughout the draft stage. @@ -295,6 +322,7 @@ Special thanks to Holly for her feedback throughout the draft stage. [SE-0401]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0401-remove-property-wrapper-isolation.md [SE-0409]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md [SE-0411]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0411-isolated-default-values.md +[SE-0413]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0413-typed-throws.md [SE-0412]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0412-strict-concurrency-for-global-variables.md [SE-0418]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0418-inferring-sendable-for-methods.md [SE-0423]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0423-dynamic-actor-isolation.md From 2c604947334c12e5e1fe20219dd09ab675151b46 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 17 Feb 2025 18:54:36 +0000 Subject: [PATCH 4063/4563] burp 0.10 --- ...NNN-migration-tooling-for-upcoming-features.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/proposals/NNNN-migration-tooling-for-upcoming-features.md b/proposals/NNNN-migration-tooling-for-upcoming-features.md index d049330244..d922d37bbc 100644 --- a/proposals/NNNN-migration-tooling-for-upcoming-features.md +++ b/proposals/NNNN-migration-tooling-for-upcoming-features.md @@ -50,8 +50,8 @@ literal. ### Automation -Many existing and prospective upcoming features imply or implement simple and -consistent source modifications to facilitate adoption: +Many existing and prospective upcoming features come with simple and reliable +migration paths to facilitate adoption: * [`NonfrozenEnumExhaustivity`][SE-0192]: Restore exhaustivity with `@unknown default:`. @@ -119,7 +119,7 @@ behavior in the *Source compatibility* section of the proposal. ### Behavior The action of enabling a previously disabled source-breaking feature in adoption -mode per se must not cause compilation errors . +mode per se must not cause compilation errors. Additionally, this action will have no effect if the mode is not supported. A corresponding warning will be emitted in this case to avoid the false impression that the impacted source code is compatible with the feature. @@ -233,9 +233,8 @@ SwiftSetting.enableUpcomingFeature("InternalImportsByDefault", mode: .adoption) Diagnostics emitted in relation to a specific feature in adoption mode must belong to a diagnostic group named after the feature. The names of diagnostic -groups can be displayed along with diagnostic messages using -`-print-diagnostic-groups` and can be used to associate a message with a -particular feature. +groups can be displayed alongside diagnostic messages using +`-print-diagnostic-groups` and used to associate messages with features. ## Source compatibility @@ -271,8 +270,8 @@ Another potential direction for adoption mode is promotion of best practices. The current serialization format for diagnostics does not include information about diagnostic groups or whether a particular fix-it preserves semantics. -There are several reasons why this data is essential for future tools built -around adoption mode: +There are several reasons why this data can be valuable for users, and why it +is essential for future tools built around adoption mode: * The diagnostic group name can be used to, well, group diagnostics, as well as to communicate relationships between diagnostics and features and filter out relevant diagnostics. From 18300668513e01b4d1e35d5d9094de8299a847ca Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 17 Feb 2025 12:29:11 -0800 Subject: [PATCH 4064/4563] Use `DisableOutwardActorInference` in the example for the "User intent" section. --- ...NN-migration-tooling-for-upcoming-features.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-migration-tooling-for-upcoming-features.md b/proposals/NNNN-migration-tooling-for-upcoming-features.md index d922d37bbc..0b873eeeb9 100644 --- a/proposals/NNNN-migration-tooling-for-upcoming-features.md +++ b/proposals/NNNN-migration-tooling-for-upcoming-features.md @@ -45,8 +45,19 @@ patterns. Needless to say, not all upcoming features fall under these criteria (and not all features are source-breaking in the first place). Consider -[`ConciseMagicFile`][SE-0274], which changes the meaning of an existing -literal. +[`DisableOutwardActorInference`][SE-0401], which changes actor isolation +inference of a type that contains an actor-isolated property wrapper. There +is no way for the programmer to specify that they'd like compiler fix-its to +make the existing actor isolation inference explicit. If they enable the +upcoming feature, their code will simply behave differently. This was a +point of debate in the review of SE-0401, and the Language Steering Group +concluded that automatic migration tooling is the right way to address this +particular workflow, as [noted in the acceptance notes][SE-0401-acceptance: + +> the Language Steering Group believes that separate migration tooling to +> help programmers audit code whose behavior will change under Swift 6 mode +> would be beneficial for all upcoming features that can change behavior +> without necessarily emitting errors. ### Automation @@ -328,3 +339,4 @@ Special thanks to Holly for her guidance throughout the draft stage. [SE-0434]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md [SE-0444]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md [async-inherit-isolation-pitch]: https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862 +[SE-0401-acceptance]: https://forums.swift.org/t/accepted-with-modifications-se-0401-remove-actor-isolation-inference-caused-by-property-wrappers/66241 From 7d62c544c66c21f55202c95e4751fe16c1a01961 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 17 Feb 2025 12:39:55 -0800 Subject: [PATCH 4065/4563] Move source incompatible changes to Future directions. --- ...migration-tooling-for-upcoming-features.md | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/proposals/NNNN-migration-tooling-for-upcoming-features.md b/proposals/NNNN-migration-tooling-for-upcoming-features.md index 0b873eeeb9..8e1f49c734 100644 --- a/proposals/NNNN-migration-tooling-for-upcoming-features.md +++ b/proposals/NNNN-migration-tooling-for-upcoming-features.md @@ -52,7 +52,7 @@ make the existing actor isolation inference explicit. If they enable the upcoming feature, their code will simply behave differently. This was a point of debate in the review of SE-0401, and the Language Steering Group concluded that automatic migration tooling is the right way to address this -particular workflow, as [noted in the acceptance notes][SE-0401-acceptance: +particular workflow, as [noted in the acceptance notes][SE-0401-acceptance]: > the Language Steering Group believes that separate migration tooling to > help programmers audit code whose behavior will change under Swift 6 mode @@ -127,18 +127,19 @@ behavior in the *Source compatibility* section of the proposal. ## Detailed design -### Behavior +Upcoming features that have mechanical migrations will support an adoption +mode, which is a a new mode of building a project that will produce compiler +warnings with attached fix-its that can be applied to preserve the behavior +of the code when the upcoming feature is enabled. Adoption mode must not +cause any new compiler errors, and the fix-its produced must preserve the +source compatibility and behavior of the code. -The action of enabling a previously disabled source-breaking feature in adoption -mode per se must not cause compilation errors. -Additionally, this action will have no effect if the mode is not supported. +Additionally, this action will have no effect if the mode is not supported +for a given upcoming feature, i,e. because the upcoming feature does not +have a mechanical migration. A corresponding warning will be emitted in this case to avoid the false impression that the impacted source code is compatible with the feature. -> [!NOTE] -> Experimental features can be both additive and source-breaking. -> Upcoming features are necessarily source-breaking. - Adoption mode should deliver guidance in the shape of regular diagnostics. For arbitrary upcoming features, adoption mode is expected to anticipate and call out any compatibility issues that result from enacting the feature, @@ -147,17 +148,6 @@ alternatives whenever feasible. Compatibility issues encompass both source and binary compatibility issues, including behavioral changes. -Note that adoption mode does not provide any new general guarantees in respect -to fix-its. -We cannot promise to offer exclusively compatible modifications. -Besides the impact of a change on dependent source code being generally -unpredictable, it can be reasonable to couple compatible fix-its with -potentially incompatible, albeit better, alternatives, as in `any P` → `some P`. -The same stands for provision of modifications — features might not have a -mechanical migration path, and the compiler remains inherently limited in the -extent to which it can make assumptions about what is helpful or best for the -programmer. - ### Interface #### Compiler @@ -263,6 +253,15 @@ potentially source-breaking action. ## Future directions +### Producing source incompatible fix-its + +For some upcoming features, a source change which alters the semantics of +the program is a more desirable approach to addressing an error that comes +from enabling an upcoming feature. For example, programmers might want to +replace cases of `any P` with `some P`. Adoption tooling could support the +option to produce source incompatible fix-its in cases where the compiler +can detect that a different behavior might be more beneficial. + ### Applications beyond migration Adoption mode can be extrapolated to additive features, such as From e3b0d36df9712c785d09ceb4a6f3bef6e7b528c9 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 12 Feb 2025 16:07:02 -0800 Subject: [PATCH 4066/4563] [SE-0456] amend proposal to rename properties to `span` --- proposals/0456-stdlib-span-properties.md | 58 ++++++++++++------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/proposals/0456-stdlib-span-properties.md b/proposals/0456-stdlib-span-properties.md index fb2626ed0c..68a799b90c 100644 --- a/proposals/0456-stdlib-span-properties.md +++ b/proposals/0456-stdlib-span-properties.md @@ -46,9 +46,9 @@ func function() { ``` If we were to attempt using `b` again after the call to `modify(&a)`, the compiler would report an overlapping access error, due to attempting to mutate `a` (with `modify(&a)`) while it is already being accessed through `b`'s borrow. Note that the copyability of `B` means that it cannot represent a mutation of `A`; it therefore represents a non-exclusive borrowing relationship. -Given this, we propose to enable the definition of a borrowing relationship via a computed property. With this feature we then propose to add `storage` computed properties to standard library types that can share their internal typed storage, as well as `bytes` computed properties to those standard library types that can safely share their internal storage as untyped memory. +Given this, we propose to enable the definition of a borrowing relationship via a computed property. With this feature we then propose to add `span` computed properties to standard library types that can share access to their internal typed memory. When a `span` has `BitwiseCopyable` elements, it will have a `bytes` computed property to share a view of the memory it represents as untyped memory. -One of the purposes of `Span` is to provide a safer alternative to `UnsafeBufferPointer`. This proposal builds on it and allows us to rewrite code reliant on `withUnsafeBufferPointer()` to use `storage` properties instead. Eventually, code that requires access to contiguous memory can be rewritten to use `Span`, gaining better composability in the process. For example: +One of the purposes of `Span` is to provide a safer alternative to `UnsafeBufferPointer`. This proposal builds on it and allows us to rewrite code reliant on `withUnsafeBufferPointer()` to use `span` properties instead. Eventually, code that requires access to contiguous memory can be rewritten to use `Span`, gaining better composability in the process. For example: ```swift let result = try myArray.withUnsafeBufferPointer { buffer in @@ -63,7 +63,7 @@ let result = try myArray.withUnsafeBufferPointer { buffer in This closure-based call is difficult to evolve, such as making `result` have a non-copyable type, adding a concurrent task, or adding typed throws. An alternative based on a vended `Span` property would look like this: ```swift -let span = myArray.storage +let span = myArray.span let indices = findElements(span) var myResult = MyResult() for i in indices { @@ -87,47 +87,47 @@ By allowing the language to define lifetime dependencies in these limited ways, #### Extensions to Standard Library types -The standard library and Foundation will provide `storage` computed properties, returning lifetime-dependent `Span` instances. These computed properties are the safe and composable replacements for the existing `withUnsafeBufferPointer` closure-taking functions. +The standard library and Foundation will provide `span` computed properties, returning lifetime-dependent `Span` instances. These computed properties are the safe and composable replacements for the existing `withUnsafeBufferPointer` closure-taking functions. ```swift extension Array { /// Share this `Array`'s elements as a `Span` - var storage: Span { get } + var span: Span { get } } extension ArraySlice { /// Share this `Array`'s elements as a `Span` - var storage: Span { get } + var span: Span { get } } extension ContiguousArray { /// Share this `Array`'s elements as a `Span` - var storage: Span { get } + var span: Span { get } } extension String.UTF8View { /// Share this `UTF8View`'s code units as a `Span` - var storage: Span { get } + var span: Span { get } } extension Substring.UTF8View { /// Share this `UTF8View`'s code units as a `Span` - var storage: Span { get } + var span: Span { get } } extension CollectionOfOne { /// Share this `Collection`'s element as a `Span` - var storage: Span { get } + var span: Span { get } } extension SIMD_N_ { // where _N_ ∈ {2, 3, 4 ,8, 16, 32, 64} /// Share this vector's elements as a `Span` - var storage: Span { get } + var span: Span { get } } extension KeyValuePairs { /// Share this `Collection`'s elements as a `Span` - var storage: Span<(Key, Value)> { get } + var span: Span<(Key, Value)> { get } } ``` @@ -136,13 +136,13 @@ Conditionally to the acceptance of [`Vector`][SE-0453], we will also add the fol ```swift extension Vector where Element: ~Copyable { /// Share this vector's elements as a `Span` - var storage: Span { get } + var span: Span { get } } ``` #### Accessing the raw bytes of a `Span` -When a `Span`'s element is `BitwiseCopyable`, we allow viewing the underlying storage as raw bytes with `RawSpan`: +When a `Span`'s element is `BitwiseCopyable`, we allow viewing the underlying memory as raw bytes with `RawSpan`: ```swift extension Span where Element: BitwiseCopyable { @@ -160,12 +160,12 @@ We hope that `Span` and `RawSpan` will become the standard ways to access shared ```swift extension UnsafeBufferPointer { /// Unsafely view this buffer as a `Span` - var storage: Span { get } + var span: Span { get } } extension UnsafeMutableBufferPointer { /// Unsafely view this buffer as a `Span` - var storage: Span { get } + var span: Span { get } } extension UnsafeRawBufferPointer { @@ -193,22 +193,22 @@ While the `swift-foundation` package and the `Foundation` framework are not gove ```swift extension Foundation.Data { // Share this `Data`'s bytes as a `Span` - var storage: Span { get } + var span: Span { get } // Share this `Data`'s bytes as a `RawSpan` var bytes: RawSpan { get } } ``` -Unlike with the standard library types, we plan to have a `bytes` property on `Foundation.Data` directly. This type conceptually consists of untyped bytes, and `bytes` is likely to be the primary way to directly access its memory. As `Data`'s API presents its storage as a collection of `UInt8` elements, we provide both `bytes` and `storage`. Types similar to `Data` may choose to provide both typed and untyped `Span` properties. +Unlike with the standard library types, we plan to have a `bytes` property on `Foundation.Data` directly. This type conceptually consists of untyped bytes, and `bytes` is likely to be the primary way to directly access its memory. As `Data`'s API presents its storage as a collection of `UInt8` elements, we provide both `bytes` and `span`. Types similar to `Data` may choose to provide both typed and untyped `Span` properties. #### Performance -The `storage` and `bytes` properties should be performant and return their `Span` or `RawSpan` with very little work, in O(1) time. This is the case for all native standard library types. There is a performance wrinkle for bridged `Array` and `String` instances on Darwin-based platforms, where they can be bridged to Objective-C types that do not guarantee contiguous storage. In such cases the implementation will eagerly copy the underlying data to the native Swift form, and return a `Span` or `RawSpan` pointing to that copy. +The `span` and `bytes` properties should be performant and return their `Span` or `RawSpan` with very little work, in O(1) time. This is the case for all native standard library types. There is a performance wrinkle for bridged `Array` and `String` instances on Darwin-based platforms, where they can be bridged to Objective-C types that may not be represented in contiguous memory. In such cases the implementation will eagerly copy the underlying data to the native Swift form, and return a `Span` or `RawSpan` pointing to that copy. -This eager copy behaviour will be specific to the `storage` and `bytes` properties, and therefore the memory usage behaviour of existing unchanged code will remain the same. New code that adopts the `storage` and `bytes` properties will occasionally have higher memory usage due to the eager copies, but we believe this performance compromise is the right approach for the standard library. The alternative is to compromise the design for all platforms supported by Swift, and we consider that a non-starter. +This eager copy behaviour will be specific to the `span` and `bytes` properties, and therefore the memory usage behaviour of existing unchanged code will remain the same. New code that adopts the `span` and `bytes` properties will occasionally have higher memory usage due to the eager copies, but we believe this performance compromise is the right approach for the standard library. The alternative is to compromise the design for all platforms supported by Swift, and we consider that a non-starter. -As a result of the eager copy behaviour for bridged `String.UTF8View` and `Array` instances, the `storage` property for these types will have a documented performance characteristic of "amortized constant time performance." +As a result of the eager copy behaviour for bridged `String.UTF8View` and `Array` instances, the `span` property for these types will have a documented performance characteristic of "amortized constant time performance." ## Source compatibility @@ -226,10 +226,10 @@ The additions described in this proposal require a version of the Swift standard #### Adding `withSpan()` and `withBytes()` closure-taking functions -The `storage` and `bytes` properties aim to be safe replacements for the `withUnsafeBufferPointer()` and `withUnsafeBytes()` closure-taking functions. We could consider `withSpan()` and `withBytes()` closure-taking functions that would provide an quicker migration away from the older unsafe functions. We do not believe the closure-taking functions are desirable in the long run. In the short run, there may be a desire to clearly mark the scope where a `Span` instance is used. The default method would be to explicitly consume a `Span` instance: +The `span` and `bytes` properties aim to be safe replacements for the `withUnsafeBufferPointer()` and `withUnsafeBytes()` closure-taking functions. We could consider `withSpan()` and `withBytes()` closure-taking functions that would provide an quicker migration away from the older unsafe functions. We do not believe the closure-taking functions are desirable in the long run. In the short run, there may be a desire to clearly mark the scope where a `Span` instance is used. The default method would be to explicitly consume a `Span` instance: ```swift var a = ContiguousArray(0..<8) -var span = a.storage +var span = a.span read(span) _ = consume span a.append(8) @@ -239,7 +239,7 @@ In order to visually distinguish this lifetime, we could simply use a `do` block ```swift var a = ContiguousArray(0..<8) do { - let span = a.storage + let span = a.span read(span) } a.append(8) @@ -248,7 +248,7 @@ a.append(8) A more targeted solution may be a consuming function that takes a non-escaping closure: ```swift var a = ContiguousArray(0..<8) -var span = a.storage +var span = a.span consuming(span) { span in read(span) } @@ -257,9 +257,9 @@ a.append(8) During the evolution of Swift, we have learned that closure-based API are difficult to compose, especially with one another. They can also require alterations to support new language features. For example, the generalization of closure-taking API for non-copyable values as well as typed throws is ongoing; adding more closure-taking API may make future feature evolution more labor-intensive. By instead relying on returned values, whether from computed properties or functions, we build for greater composability. Use cases where this approach falls short should be reported as enhancement requests or bugs. -#### Giving the properties different names +#### Different naming for the properties -We chose the names `storage` and `bytes` because those reflect _what_ they represent. Another option would be to name the properties after _how_ they represent what they do, which would be `span` and `rawSpan`. It is possible the name `storage` would be deemed to clash too much with existing properties of types that would like to provide views of their internal storage with `Span`-providing properties. For example, the Standard Library's concrete `SIMD`-conforming types have a property `var _storage`. The current proposal means that making this property of `SIMD` types into public API would entail a name change more significant than simply removing its leading underscore. +We originally proposed the name `storage` for the `span` properties introduced here. That name seems to imply that the returned `Span` is the storage itself, rather than a view of the storage. That would be misleading for types that own their storage, especially those that delegate their storage to another type, such as a `ContiguousArray`. In such cases, it would make sense to have a `storage` property whose type is the type that implements the storage. #### Disallowing the definition of non-escapable properties of non-escapable types @@ -269,7 +269,7 @@ The original version of this pitch disallowed this. As a consequence, the `bytes #### Omitting extensions to `UnsafeBufferPointer` and related types -We could omit the extensions to `UnsafeBufferPointer` and related types, and rely instead of future `Span` and `RawSpan` initializers. The initializers can have the advantage of being able to communicate semantics (somewhat) through their parameter labels. However, they also have a very different shape than the `storage` computed properties we are proposing. We believe that the adding the same API on both safe and unsafe types is advantageous, even if the preconditions for the properties cannot be statically enforced. +We could omit the extensions to `UnsafeBufferPointer` and related types, and rely instead of future `Span` and `RawSpan` initializers. The initializers can have the advantage of being able to communicate semantics (somewhat) through their parameter labels. However, they also have a very different shape than the `span` computed properties we are proposing. We believe that the adding the same API on both safe and unsafe types is advantageous, even if the preconditions for the properties cannot be statically enforced. ## Future directions @@ -277,7 +277,7 @@ Note: The future directions stated in [SE-0447](https://github.com/swiftlang/swi #### Safe mutations with `MutableSpan` -Some data structures can delegate mutations of their owned memory. In the standard library the function `withMutableBufferPointer()` provides this functionality in an unsafe manner. We expect to add a `MutableSpan` type to support delegating mutations of initialized memory. Standard library types will then add a way to vend `MutableSpan` instances. This could be with a closure-taking `withMutableSpan()` function, or a new property, such as `var mutableStorage`. Note that a computed property providing mutable access needs to have a different name than the `storage` properties proposed here, because we cannot overload the return type of computed properties based on whether mutation is desired. +Some data structures can delegate mutations of their owned memory. In the standard library the function `withMutableBufferPointer()` provides this functionality in an unsafe manner. We expect to add a `MutableSpan` type to support delegating mutations of initialized memory. Standard library types will then add a way to vend `MutableSpan` instances. This could be with a closure-taking `withMutableSpan()` function, or a new property, such as `var mutableStorage`. Note that a computed property providing mutable access needs to have a different name than the `span` properties proposed here, because we cannot overload the return type of computed properties based on whether mutation is desired. #### A `ContiguousStorage` protocol From 7ac36f803fa9717560087bb068d6b39dcb6838c3 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 12 Feb 2025 16:09:14 -0800 Subject: [PATCH 4067/4563] [SE-0456] call `InlineArray` by its name --- proposals/0456-stdlib-span-properties.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0456-stdlib-span-properties.md b/proposals/0456-stdlib-span-properties.md index 68a799b90c..6461529d6e 100644 --- a/proposals/0456-stdlib-span-properties.md +++ b/proposals/0456-stdlib-span-properties.md @@ -131,11 +131,11 @@ extension KeyValuePairs { } ``` -Conditionally to the acceptance of [`Vector`][SE-0453], we will also add the following: +Following the acceptance of [`InlineArray`][SE-0453], we will also add the following: ```swift -extension Vector where Element: ~Copyable { - /// Share this vector's elements as a `Span` +extension InlineArray where Element: ~Copyable { + /// Share this `InlineArray`'s elements as a `Span` var span: Span { get } } ``` From 58dbf76a49dc0d09de9a57b67c4dcbe49c109568 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 12 Feb 2025 16:11:07 -0800 Subject: [PATCH 4068/4563] [SE-0456] amendment removing extensions to SIMD --- proposals/0456-stdlib-span-properties.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/proposals/0456-stdlib-span-properties.md b/proposals/0456-stdlib-span-properties.md index 6461529d6e..c18fe06105 100644 --- a/proposals/0456-stdlib-span-properties.md +++ b/proposals/0456-stdlib-span-properties.md @@ -120,11 +120,6 @@ extension CollectionOfOne { var span: Span { get } } -extension SIMD_N_ { // where _N_ ∈ {2, 3, 4 ,8, 16, 32, 64} - /// Share this vector's elements as a `Span` - var span: Span { get } -} - extension KeyValuePairs { /// Share this `Collection`'s elements as a `Span` var span: Span<(Key, Value)> { get } @@ -289,6 +284,10 @@ Unfortunately, a major issue prevents us from proposing it at this time: the abi The other limitation stated in [SE-0447][SE-0447]'s section about `ContiguousStorage` is "the inability to declare a `_read` acessor as a protocol requirement." This proposal's addition to enable defining a borrowing relationship via a computed property is a solution to that, as long as we don't need to use a coroutine accessor to produce a `Span`. While allowing the return of `Span`s through coroutine accessors may be undesirable, whether it is undesirable is unclear until coroutine accessors are formalized in the language. +`span` properties on standard library SIMD types + +This proposal as reviewed included `span` properties for the standard library `SIMD` types. We are deferring this feature at the moment, since it is difficult to define these succinctly. The primary issue is that the `SIMD`-related protocols do not explicitly require contiguous memory; assuming that they are represented in contiguous memory fails with theoretically-possible examples. We could define the `span` property systematically for each concrete SIMD type in the standard library, but that would be very repetitive (and expensive from the point of view of code size.) We could also fix the SIMD protocols to require contiguous memory, enabling a succinct definition of their `span` property. Finally, we could also rely on converting `SIMD` types to `InlineArray`, and use the `span` property defined on `InlineArray`. + ## Acknowledgements Thanks to Ben Rimmington for suggesting that the `bytes` property should be on `Span` rather than on every type. From 5d306c96629efb3641f56d0f3567e58d4934dcec Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 17 Feb 2025 21:18:58 -0800 Subject: [PATCH 4069/4563] [SE-0456] Update Proposal Status --- proposals/0456-stdlib-span-properties.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0456-stdlib-span-properties.md b/proposals/0456-stdlib-span-properties.md index c18fe06105..aaeab8c600 100644 --- a/proposals/0456-stdlib-span-properties.md +++ b/proposals/0456-stdlib-span-properties.md @@ -3,12 +3,13 @@ * Proposal: [SE-0456](0456-stdlib-span-properties.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review (January 15...28, 2024)** +* Status: **Accepted** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: [PR #78561](https://github.com/swiftlang/swift/pull/78561) -* Review: [Review](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233), [Pitch](https://forums.swift.org/t/76138) +* Review: [Pitch](https://forums.swift.org/t/76138), [Review](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233), [Acceptance](https://forums.swift.org/t/77684) [SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md + [SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md [PR-2305]: https://github.com/swiftlang/swift-evolution/pull/2305 [SE-0453]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0453-vector.md From 32b174c139b0d0930a1ee5205378bac86c6ccb45 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 18 Feb 2025 08:09:48 -0500 Subject: [PATCH 4070/4563] Mark SE-0443 as implemented in Swift 6.1. --- proposals/0443-warning-control-flags.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0443-warning-control-flags.md b/proposals/0443-warning-control-flags.md index 1dcb5012fc..99562b689c 100644 --- a/proposals/0443-warning-control-flags.md +++ b/proposals/0443-warning-control-flags.md @@ -4,7 +4,7 @@ * Proposal: [SE-0443](0443-warning-control-flags.md) * Authors: [Doug Gregor](https://github.com/douggregor), [Dmitrii Galimzianov](https://github.com/DmT021) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 6.1)** * Implementation: [apple/swift#74466](https://github.com/swiftlang/swift/pull/74466) * Review: ([pitch](https://forums.swift.org/t/warnings-as-errors-exceptions/72925)) ([review](https://forums.swift.org/t/se-0443-precise-control-flags-over-compiler-warnings/74116)) ([acceptance](https://forums.swift.org/t/accepted-se-0443-precise-control-flags-over-compiler-warnings/74377)) * Previous revisions: [1](https://github.com/swiftlang/swift-evolution/blob/57fe29d5d55edb85b14c153b7f4cbead6b6539eb/proposals/0443-warning-control-flags.md), [2](https://github.com/swiftlang/swift-evolution/blob/7b12899ad0d96002c793d33ef8109ec47c5d256f/proposals/0443-warning-control-flags.md) From 036815c53d796212eb25a53b328c35eb7bf6242c Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 17 Feb 2025 23:00:47 +0000 Subject: [PATCH 4071/4563] burp 1.0 --- ...NN-adoption-tooling-for-swift-features.md} | 157 ++++++++---------- 1 file changed, 70 insertions(+), 87 deletions(-) rename proposals/{NNNN-migration-tooling-for-upcoming-features.md => NNNN-adoption-tooling-for-swift-features.md} (67%) diff --git a/proposals/NNNN-migration-tooling-for-upcoming-features.md b/proposals/NNNN-adoption-tooling-for-swift-features.md similarity index 67% rename from proposals/NNNN-migration-tooling-for-upcoming-features.md rename to proposals/NNNN-adoption-tooling-for-swift-features.md index 8e1f49c734..c51aa9b38d 100644 --- a/proposals/NNNN-migration-tooling-for-upcoming-features.md +++ b/proposals/NNNN-adoption-tooling-for-swift-features.md @@ -9,25 +9,23 @@ ## Introduction -In Swift 5.8 introduced [upcoming features][SE-0362], -which enabled piecemeal adoption of individual source-incompatible changes that -are included in a language mode. +Swift 5.8 introduced [upcoming features][SE-0362], which enabled piecemeal +adoption of individual source-incompatible changes that are included in a +language mode. Many upcoming features have a mechanical migration, meaning the compiler can determine the exact source changes necessary to allow the code to compile under the upcoming feature while preserving the behavior of the code. -This proposal seeks to improve the experience of enabling individual -upcoming features by providing a mechanism for producing the necessary source -code changes automatically for a given set of upcoming features that a -programmer wants to enable. +This proposal seeks to improve the experience of enabling individual Swift +features by providing an integrated mechanism for producing these source code +modifications automatically. ## Motivation -Adopting certain features is a time-consuming endeavor at the least. It is the responsibility of project maintainers to preserve source (and binary) -compatibility both internally and externally for library clients when enabling -an upcoming feature, which can be difficult or tedious without having tools to -help detect possibly inadvertent changes and perform monotonous migration -shenanigans for you. +compatibility both internally and for library clients when enabling an upcoming +feature, which can be difficult or tedious without having tools to help detect +possibly inadvertent changes or perform monotonous migration shenanigans for +you. *Our* responsibility is to make that an easier task for everybody. ### User intent @@ -38,21 +36,22 @@ intent. Is the developer expecting guidance on adopting an improvement? All the compiler knows to do when a feature is enabled is to compile code accordingly. -This suffices if a feature merely supplants an existing syntactical construct -or changes the behavior of existing code in strictly predictable ways because -Swift can infer the need to suggest a fix just from spotting certain code -patterns. +If an upcoming feature supplants an existing grammatical construct or +invalidates an existing behavior, the language rules alone suffice because +Swift can consistently infer the irrefutable need to diagnose certain code +patterns just by spotting them. Needless to say, not all upcoming features fall under these criteria (and not -all features are source-breaking in the first place). Consider -[`DisableOutwardActorInference`][SE-0401], which changes actor isolation -inference of a type that contains an actor-isolated property wrapper. There -is no way for the programmer to specify that they'd like compiler fix-its to -make the existing actor isolation inference explicit. If they enable the -upcoming feature, their code will simply behave differently. This was a -point of debate in the review of SE-0401, and the Language Steering Group -concluded that automatic migration tooling is the right way to address this -particular workflow, as [noted in the acceptance notes][SE-0401-acceptance]: +all features are source-breaking in the first place). +Consider [`DisableOutwardActorInference`][SE-0401], which changes actor +isolation inference rules with respect to wrapped properties. +There is no way for the programmer to specify that they'd like compiler fix-its +to make the existing actor isolation inference explicit. +If they enable the upcoming feature, their code will simply behave differently. +This was a point of debate in the review of [SE-0401], and the Language +Steering Group concluded that automatic migration tooling is the right way to +address this particular workflow, as +[noted in the acceptance notes][SE-0401-acceptance]: > the Language Steering Group believes that separate migration tooling to > help programmers audit code whose behavior will change under Swift 6 mode @@ -61,7 +60,7 @@ particular workflow, as [noted in the acceptance notes][SE-0401-acceptance]: ### Automation -Many existing and prospective upcoming features come with simple and reliable +Many existing and prospective upcoming features account for simple and reliable migration paths to facilitate adoption: * [`NonfrozenEnumExhaustivity`][SE-0192]: Restore exhaustivity with @@ -79,32 +78,17 @@ migration paths to facilitate adoption: * [`DisableOutwardActorInference`][SE-0401]: Specify global actor isolation explicitly. * [`InternalImportsByDefault`][SE-0409]: `import X` → `public import X`. -* [`GlobalConcurrency`][SE-0412]: - - Convert the global variable to a `let` (or) - - `@MainActor`-isolate it (or) - - Mark it with `nonisolated(unsafe)` +* [`GlobalConcurrency`][SE-0412]: Convert the global variable to a `let`, or + `@MainActor`-isolate it, or mark it with `nonisolated(unsafe)`. * [`MemberImportVisibility`][SE-0444]: Add explicit imports appropriately. * [`InferSendableFromCaptures`][SE-0418]: Suppress inference with coercions and type annotations. * [Inherit isolation by default for async functions][async-inherit-isolation-pitch]: Mark nonisolated functions with the proposed attribute. -Extending diagnostic metadata to include information that allows for -recognizing these diagnostics and distinguishing semantics-preserving fix-its -from alternative source changes will open up numerous opportunities for -higher-level tools — ranging from the Swift package manager to IDEs — to -implement powerful solutions for organizing, automating, and tuning feature -adoption processes. - -It is not always feasible or in line with language design principles for an -upcoming feature to have a mechanical migration path. -For example, the following upcoming features require manual migration to -preserve semantics: - -* [`DynamicActorIsolation`][SE-0423] -* [`GlobalActorIsolatedTypesUsability`][SE-0434] -* [`StrictConcurrency`][SE-0337] -* [`IsolatedDefaultValues`][SE-0411] +Application of these adjustments can be fully automated in favor of preserving +behavior, saving time for more important tasks, such as identifying, auditing, +and testing code where a change in behavior is preferable. ## Proposed solution @@ -115,39 +99,35 @@ leveraged to build better supportive adoption experiences for developers. If enabling a feature communicates an intent to *enact* rules, adoption mode communicates an intent to *adopt* them. An immediate benefit of adoption mode is the capability to deliver source -modifications that can be applied to preserve or improve the behavior of -existing code whenever the feature provides for them. +modifications that can be applied to preserve compatibility whenever a feature +provides for them. This proposal will support the set of existing upcoming features that have mechanical migrations, as described in the [Automation](#automation) section. -All future proposals that introduce a new upcoming feature and provide a -mechanical migration are expected to support adoption mode and detail its -behavior in the *Source compatibility* section of the proposal. +All future proposals that intend to introduce an upcoming feature and +provide for a mechanical migration should include an adoption mode and detail +its behavior alongside the migration paths in the *Source compatibility* +section. ## Detailed design Upcoming features that have mechanical migrations will support an adoption -mode, which is a a new mode of building a project that will produce compiler +mode, which is a new mode of building a project that will produce compiler warnings with attached fix-its that can be applied to preserve the behavior -of the code when the upcoming feature is enabled. Adoption mode must not -cause any new compiler errors, and the fix-its produced must preserve the -source compatibility and behavior of the code. +of the code once the upcoming feature is enacted. +The action of enabling a previously disabled upcoming feature in adoption +mode must not cause any new compiler errors or behavioral changes, and the +fix-its produced must preserve compatibility. +Compatibility here refers to both source and binary compatibility, as well as +to behavior. Additionally, this action will have no effect if the mode is not supported -for a given upcoming feature, i,e. because the upcoming feature does not +for a given upcoming feature, i.e., because the upcoming feature does not have a mechanical migration. A corresponding warning will be emitted in this case to avoid the false impression that the impacted source code is compatible with the feature. -Adoption mode should deliver guidance in the shape of regular diagnostics. -For arbitrary upcoming features, adoption mode is expected to anticipate and -call out any compatibility issues that result from enacting the feature, -coupling diagnostic messages with counteracting compatible changes and helpful -alternatives whenever feasible. -Compatibility issues encompass both source and binary compatibility issues, -including behavioral changes. - ### Interface #### Compiler @@ -233,14 +213,14 @@ SwiftSetting.enableUpcomingFeature("InternalImportsByDefault", mode: .adoption) ### Diagnostics Diagnostics emitted in relation to a specific feature in adoption mode must -belong to a diagnostic group named after the feature. The names of diagnostic -groups can be displayed alongside diagnostic messages using -`-print-diagnostic-groups` and used to associate messages with features. +belong to a diagnostic group named after the feature. +The names of diagnostic groups can be displayed alongside diagnostic messages +using `-print-diagnostic-groups` and used to associate messages with features. ## Source compatibility -This proposal does not affect language rules. The described changes to the API -surface are source-compatible. +This proposal does not affect language rules. +The described changes to the API surface are source-compatible. ## ABI compatibility @@ -248,21 +228,22 @@ This proposal does not affect binary compatibility or binary interfaces. ## Implications on adoption -Entering or exiting adoption mode may affect behavior and is therefore a +Entering or exiting adoption mode will affect behavior and is therefore a potentially source-breaking action. ## Future directions ### Producing source incompatible fix-its -For some upcoming features, a source change which alters the semantics of +For some features, a source change that alters the semantics of the program is a more desirable approach to addressing an error that comes -from enabling an upcoming feature. For example, programmers might want to -replace cases of `any P` with `some P`. Adoption tooling could support the -option to produce source incompatible fix-its in cases where the compiler -can detect that a different behavior might be more beneficial. +from enabling the feature. +For example, programmers might want to replace cases of `any P` with `some P`. +Adoption tooling could support the option to produce source incompatible +fix-its in cases where the compiler can detect that a different behavior might +be more beneficial. -### Applications beyond migration +### Applications beyond mechanical migration Adoption mode can be extrapolated to additive features, such as [typed `throws`][SE-0413] or [opaque parameter types][SE-0341], by providing @@ -270,9 +251,9 @@ actionable adoption tips. Additive features are hard-enabled and become an integral part of the language as soon as they ship. Many recent additive features are already integrated into the Swift feature -model and kept around for the sole purpose of supporting -[feature availability checks][feature-detection] in conditional compilation -blocks. +model, and their metadata is kept around either to support +[feature availability checks][SE-0362-feature-detection] in conditional +compilation blocks or because they started off as experimental features. Another potential direction for adoption mode is promotion of best practices. @@ -288,22 +269,24 @@ is essential for future tools built around adoption mode: This can prove especially handy when multiple features are simultaneously enabled in adoption mode, or when similar diagnostic messages are caused by distinct features. -* Fix-its that preserve semantics can be prioritized and auto-applied in - previews. +* Exposing the purpose of a fix-it can help developers make quicker decisions + when offered multiple fix-its. + Furthermore, tools can take advantage of this information by favoring and + auto-applying source-compatible fix-its. ### `swift adopt` The Swift package manager could implement an `adopt` subcommand for interactive review and application of adoption mode output for a given set of features, -with a command line interface similar to `git add --patch`. +with a command-line interface similar to `git add --patch`. ## Alternatives considered ### Naming -Perhaps the most intuitive alternative to "adoption" is "migration". We -settled on the former because there is no reason for this concept to be limited -to upcoming features or migrational changes. +Perhaps the most intuitive alternative to "adoption" is "migration". +We settled on the former because there is no reason for this concept to be +limited to upcoming features or migrational changes. ## Acknowledgements @@ -326,9 +309,10 @@ Special thanks to Holly for her guidance throughout the draft stage. [SE-0352]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md [SE-0354]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0354-regex-literals.md [SE-0362]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md -[feature-detection]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md#feature-detection-in-source-code +[SE-0362-feature-detection]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md#feature-detection-in-source-code [SE-0383]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md [SE-0401]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0401-remove-property-wrapper-isolation.md +[SE-0401-acceptance]: https://forums.swift.org/t/accepted-with-modifications-se-0401-remove-actor-isolation-inference-caused-by-property-wrappers/66241 [SE-0409]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md [SE-0411]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0411-isolated-default-values.md [SE-0413]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0413-typed-throws.md @@ -338,4 +322,3 @@ Special thanks to Holly for her guidance throughout the draft stage. [SE-0434]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md [SE-0444]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0444-member-import-visibility.md [async-inherit-isolation-pitch]: https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862 -[SE-0401-acceptance]: https://forums.swift.org/t/accepted-with-modifications-se-0401-remove-actor-isolation-inference-caused-by-property-wrappers/66241 From 945d93776cf44cefaa5e685ede0fa4fc96d460ab Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 18 Feb 2025 16:46:36 -0800 Subject: [PATCH 4072/4563] Add a vision for improving the approachability of data-race safety. (#2621) * Add a prospective vision for improving the approachability of data-race safety. * Specify approachability of data-race safety for programmers with other concurrent programming experience as an explicit goal of the vision document. * Clarify why diagnostics and documentation are not included in the vision document. * Minor clarifications and typo fixes. * Clarify how diagnostics are considered in the evolution process. * Add boilerplate re acceptance --------- Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- visions/approachable-concurrency.md | 198 ++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 visions/approachable-concurrency.md diff --git a/visions/approachable-concurrency.md b/visions/approachable-concurrency.md new file mode 100644 index 0000000000..de8adefc75 --- /dev/null +++ b/visions/approachable-concurrency.md @@ -0,0 +1,198 @@ +# Improving the approachability of data-race safety + +[SE-0434]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0434-global-actor-isolated-types-usability.md + +> This document is an official feature [vision document](https://forums.swift.org/t/the-role-of-vision-documents-in-swift-evolution/62101). The Language Steering Group has endorsed the goals and basic approach laid out in this document. This endorsement is not a pre-approval of any of the concrete proposals that may come out of this document. All proposals will undergo normal evolution review, which may result in rejection or revision from how they appear in this document. + +## Introduction + +Swift's built-in support for concurrency has three goals: + +1. Extend memory safety guarantees to low-level data races. +2. Maintain progressive disclosure for non-concurrent code, and make basic use of concurrency simple and easy. +3. Make advanced uses of concurrency to improve performance natural to accomplish and reason about. + +The Swift 6 language mode provides a baseline of correctness that meets the first goal, but sometimes it comes at the cost of the second, and it can be frustrating to adopt. Now that we have a lot more user experience under our belt as a community, it’s reasonable to ask what we can do in the language to address that problem. This document lays out several potential paths for improving the usability of Swift 6, focusing on two primary use cases: + +1. Simple situations where programmers aren’t intending to use concurrency at all. +2. Adapting an existing code base that uses concurrency libraries which predate Swift's native concurrency model. + +While performance is not our immediate focus, it’s something we need to keep in mind during this exercise: we don’t want these usability wins to create pervasive regressions or to make it frustratingly difficult to achieve a high level of performance. + +A key tenet of our thinking in this vision is that we want to drastically reduce the number of explicit concurrency annotations necessary in projects that aren’t trying to leverage parallelism for performance. This is important for many kinds of programming, such as UI programming and scripts, where concurrency is often localized and large swathes of the code are generally expected to be constrained to the main actor. At the same time, we want to maintain a smooth path for experienced programmers to opt in to concurrency and maintain the safety of complete data-race checking. + +As we see it, there should be three phases on the progressive disclosure path for concurrency: + +1. **Write sequential, single-threaded code**. By default, programmers writing executable projects will write sequential code; there is no runtime parallelism, and therefore no data-race safety errors are surfaced to the programmer. +2. **Write asynchronous code without data-race safety errors**. When programmers need functionality that can suspend, they can start introducing basic uses of async/await. Programmers won’t have to confront data-race safety at this point, because they aren’t yet introducing parallelism into their code. This is an important distinction, because there are many library APIs that perform work asynchronously, but they don’t need to use the programmer’s shared mutable state from a concurrent task. In these cases, programmers don’t have to understand data-race safety just to call an async API. +3. **Introduce parallelism to improve performance.** When the programmer is ready to embrace concurrency to get better performance, they can explicitly offload work from the main actor to the cooperative thread pool, leverage tasks and structured concurrency, etc, all while relying on the compiler to prevent mistakes that risk a data race. + +## Mitigating false positive data-race safety errors in sequential code + +A lot of code is effectively “single-threaded”. For example, most executables, such as apps, command-line tools, and scripts, start running on the main actor and just stay there unless some part of the code actually does something concurrent (like creating a `Task`). If there isn’t any use of concurrency, the entire program will run sequentially, and there’s no risk of data races — every concurrency diagnostic is necessarily a false positive! It would be good to be able to take advantage of that in the language, both to avoid annoying programmers with unnecessary diagnostics and to reinforce progressive disclosure. Many people get into Swift by writing these kinds of programs, and if we can avoid needing to teach them about concurrency straight away, we’ll make the language much more approachable. + +Now, “If nothing in the program uses concurrency, suppress all the concurrency diagnostics” requires what compiler writers call a *whole-program analysis*, and rules like that tend not to work out well on multiple levels. For one, it would require the compiler to look at all of the code in the program all at once; this might be okay for small scripts, but it would scale poorly as the program got more complex. More importantly, it would make the first adoption of concurrency extremely painful: programmers would be hit by a tidal wave of errors in code they haven’t changed. And, of course, many libraries do use concurrency behind the scenes; importing even a single library like that would force concurrency-safety diagnostics everywhere. + +A better approach is to locally state our assumption that the sequential parts of the program are “single-threaded”. Rather than having to assume the possibility of concurrency, Swift would know that these parts of the code will all run sequentially, which it can use to prove that there aren’t any data races. There can still be concurrent parts of the program elsewhere, but Swift would stop them from accessing the single-threaded bits. Fortunately, this is something that Swift can already model quite well! + +### Single-threaded code and its challenges under Swift 6 + +The easiest and best way to model single-threaded code is with a global actor. Everything on a global actor runs sequentially, and code that isn’t isolated to that actor can’t access its data. All programs start running on the global actor `MainActor`, and if everything in the program is isolated to the main actor, there shouldn’t be any concurrency errors. + +Unfortunately, it’s not quite that simple right now. Writing a single-threaded program is surprisingly difficult under the Swift 6 language mode. This is because Swift 6 defaults to a presumption of concurrency: if a function or type is not annotated or inferred to be isolated, it is treated as non-isolated, meaning it can be used concurrently. This default often leads to conflicts with single-threaded code, producing false positive diagnostics in cases such as: + +* global and static variables, +* conformances of main-actor-isolated types to non-isolated protocols, +* class deinitializers, +* overrides of non-isolated superclass methods in a main-actor-isolated subclass, and +* calls to main-actor-isolated functions from the platform SDK. + +To see this, let’s explore the first of those cases in more detail. A mutable global variable (or an immutable one that stores a non-`Sendable` value) is only memory-safe if it’s used in a single-threaded way. If the whole program is single-threaded, there’s no problem, and the variable is always safe to use. But since Swift 6 presumes concurrency by default, it requires a variable like this to be explicitly isolated to a global actor, like `@MainActor`. A function that uses that variable is then also required to be statically isolated to `@MainActor`: + +```swift +class AudioManager { + @MainActor + static let shared = AudioManager() + + func playSound() { ... } +} + +class Model { + func play() { + AudioManager.shared.playSound() // error: Main actor-isolated static property 'shared' can not be referenced from a nonisolated context + } +} +``` + +And this in turn means that functions that call those functions must also be `@MainActor`, and so on until the `@MainActor` annotation has been laboriously propagated throughout the entire transitive tree of callers. Because main actor isolation is so common, many programmers have resorted to reflexively writing `@MainActor` everywhere, an onerous annotation burden that goes against Swift’s goals of making the simplest things easy. + +Because the default programming model presumes concurrency, it is also hard on programmers who haven’t yet learned about concurrent programming, because they are confronted with the concept of data-race safety and actor isolation too early simply by using these basic language features: + +```swift +class AudioManager { + static let shared = AudioManager() // error: Static property 'shared' is not concurrency-safe because non-'Sendable' type 'AudioManager' may have shared mutable state +} +``` + +Analogous problems arise with all the other kinds of false positives listed above. For example, when using values from generic code, the value’s type usually must conform to one or more protocols. However, actor-isolated types cannot easily conform to protocols that aren’t aware of that isolation: they can declare the conformance, but it’s often impossible to write a useful implementation because the value’s properties will not be available. This is exactly the same kind of conflict as with global variables, where we have generally single-threaded code but a presumption of concurrency from the protocol, except that *this* conflict usually can’t be solved with annotations at all — the only fixes are to change the protocol, avoid all the isolated storage, or dangerously assert (with `assumeIsolated`) that the method is only used dynamically from the right actor. + +### Allowing modules to default to being “single-threaded” + +We believe that the right solution to these problems is to allow code to opt in to being “single-threaded” by default, on a module-by-module basis. This would change the default isolation rule for unannotated code in the module: rather than being non-isolated, and therefore having to deal with the presumption of concurrency, the code would instead be implicitly isolated to `@MainActor`. Code imported from other modules would be unaffected by the current module’s choice of default. When the programmer really wants concurrency, they can request it explicitly by marking a function or type as `nonisolated` (which can be used on any declaration as of [SE-0449](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0449-nonisolated-for-global-actor-cutoff.md)), or they can define it in a module that doesn’t default to main-actor isolation. This doesn’t fundamentally change anything about Swift’s isolation model; it just flips the default, effectively creating a model in which code is single-threaded except where it explicitly requests concurrency. Modules that don’t want this could of course continue to use the current rules. + +Making a module be isolated to the main actor by default would directly fix several of the false positive problems listed above for single-threaded code. Global variables would default to being isolated to the main actor, avoiding the diagnostic when they’re declared. Functions in the module would also default to being isolated to the main actor, allowing them to freely use both those isolated global variables and any main-actor-isolated functions and variables imported from the platform SDK. Class overrides and protocol conformances aren’t quite so easy, but we think we can extend them in ways that allow a natural solution with main actor isolation. We’ll get to how later in this document. + +### Default concurrency rules for executable and library modules + +As mentioned above, executable targets tend to center around the main actor. Command-line tools and scripts all start on the main actor and continue to run there unless they explicitly do something that introduces concurrency. Similarly, most UI programs make heavy use of single-threaded UI frameworks that privilege the main actor. This kind of code would be greatly improved by adopting a single-threaded model by default. Furthermore, since many new Swift programmers find themselves first writing this kind of code, this would also be a significant improvement to Swift’s progressive disclosure: programmers writing code in this mode should not run into data-race safety issues and diagnostics until they intentionally introduce concurrency. We feel that this amounts to a compelling argument that executable targets should default to inferring main actor isolation. + +The same argument does not apply to libraries. Most library functions are meant to be usable from any context, and libraries usually avoid using any global or shared mutable state. Swift also already asks a little more of library authors in general; for example, access control is usually a more significant concern for library authors than for app developers. It would be reasonable for library targets to default to `nonisolated` the same way they do today in Swift 6. + +Specific libraries could still decide to default to the main actor, such as when they’re libraries of UI widgets, or if a library is used for code organization within an executable project. + +### Risks of a language dialect + +Adding a per-module setting to specify the default isolation would introduce a new permanent language dialect. In a sense, Swift adds a new language dialect whenever it adds an upcoming language feature flag, but these are seen as “temporary” because it’s expected that those features will eventually be rolled into a future language mode. Permanent language dialects can be problematic for a variety of reasons: + +* They can harm readability if readers have to know which dialect the code uses before they can understand the code. +* They can harm usability if programmers have to consciously program differently based on the dialect in use in the code or if code cannot be easily moved between projects using different dialects. +* They can harm tools such as IDEs if the tool has to know which dialect the code uses before it can work correctly; this is particularly challenging for code files that may be used in multiple dialects, such as a `.h` file in a C/C++/Objective-C IDE. +* When dialects are platform-dependent, they can harm portability and basic workflows (such as testing) if it is difficult to make the same code build as multiple dialects. + +Some of these problems do not seem to apply to this proposed dialect because it only affects the isolation of declarations. Most IDE services, such as syntax highlighting and code completion, do not need to know the isolation of the surrounding context. And there’s no good reason for a module to build with a different default isolation on different platforms, so the dialect does not seem to introduce any portability concerns. + +Readability does seem to be a fair concern. It is often useful to know what isolation a function will run under, and with this change, that would be dialect-specific. However, it is worth noting that the dynamic isolation of a function is already not always statically knowable because of the way that e.g. synchronous `nonisolated` functions inherit their callers’ isolation. And it would be reasonable for IDEs to be able to present isolation information for the current context: even without this dialect change, it is not always easy to understand how Swift’s isolation inference rules will apply to any particular declaration. + +Programmers will probably not consciously program differently under these different dialects, and the compiler should provide reasonable guidance if they make a mistake. Moving code from a single-threaded module to a nonisolated module might be somewhat more arduous, however. To some degree, this is inherent and arguably even good: the programmer may be moving this code in an effort to generalize it to work concurrently, and any new diagnostics represent real problems that the programmer didn’t have to deal with before this generalization. But when the programmer is not trying to generalize the code to use concurrency, this could be frustrating, and it might be good for IDEs to offer assistance, such as tools to make the single-threaded assumptions of a piece of code explicit. + +On balance, we feel that the costs of this particular dialect are modest and manageable. + +## Isolated conformances + +When checking a conformance to a protocol, Swift 6 often requires implementations to be nonisolated when the requirement is, including when the requirement is synchronous or the parameters are not Sendable. This makes it difficult to implement nonisolated protocols with any kind of isolated type: global-actor-isolated types, certainly, but also actors themselves. This restriction is very important when writing concurrent code, but it's a common source of false positives in single-threaded programs. Even worse, there's often no good solution to the problem: a correct implementation of the protocol for an isolated type usually requires access to the isolated data, and the only way to get that is to assert that the calling context is actually isolated, which completely subverts the static isolation safety that Swift 6 tries to provide. + +In many ways, isolation's interaction with protocol requirements is similar to its interaction with function values. In both situations, we have an abstract signature that doesn't express isolation by default, which Swift wants to interpret as an affirmative statement that the isolation of the implementation doesn't matter. Over the last few years, Swift has gradually added more ways to handle isolated function values: + +- A function value can have a type like `() -> Bool` that says it's not sendable. These functions can have any kind of isolation as long as it's the same as the current concurrency domain. Since the function can't be used from a different context, there's no need for it to spell out its isolation explicitly in its type; Swift just checks that the isolation actually matches whenever it makes a new non-`Sendable` function value. + +- A function value can have a type like `@MainActor () -> Bool` that says it's isolated to a specific global actor. These functions can either be non-isolated or isolated to that actor, and Swift just treats them as the latter unconditionally when calling them. + +- A function value can have a type like `@isolated(any) () -> Bool` that says it might be isolated to a specific actor that it carries around with it dynamically. These functions can be isolated to anything, and the caller has to be prepared to handle it. + +Each of these ideas also works with protocol conformances. If we know that we only intend to use a protocol conformance from the current concurrency domain, we don't really care whether the implementation requires some sort of isolation as long as the current context has that isolation. Similarly, if we know that the implementation of a protocol might be isolated to a specific global actor, we can handle that whenever we use the conformance exactly as if the protocol requirements were declared isolated to that actor. And we could even do that dynamically with a statically-unknown isolation, the same way Swift already does with `@isolated(any)` function values. + +The most important of these for our model of single-threaded code is to be able to express global-actor-isolated conformances. When a type is isolated to a global actor, its methods will be isolated by default. Normally, these methods would not be legal implementations of nonisolated protocol requirements. When Swift recognizes this, it can simply treat the conformance as isolated to that global actor. This is a kind of *isolated conformance*, which will be a new concept in the language. + +Now, an isolated conformance is less flexible than a nonisolated conformance. For example, generic types and functions from nonisolated modules (including all current declarations) will still be interpreted as requiring nonisolated conformances. This will mean that they can't be called with a type that only has an isolated conformance, but it will also allow them to freely use the conformance from any concurrency domain, the same way they can today. Generic types and functions in "single-threaded" modules will default to allowing conformances isolated to the module's default global actor. Since those functions will themselves be isolated to that global actor, they won't have any problem using those conformances. + +A generic function that can work with both isolated and nonisolated conformances should be able to declare that it can accept an isolated conformance. It would then be restricted to only use the conformance from the current concurrency domain, as if it were a sort of "non-sendable conformance". This is an important tool for generic libraries such as the standard library, many of which will never use conformances concurrently and so are fine with accepting isolated conformances. + +This design is still being developed, and there are a lot of details that will have to be figured out. Nonetheless, we are tentatively very excited about the potential for this feature to fix problems with how Swift's generics system interacts with isolated types, especially global-actor-isolated types. + +## Isolated subclasses and overrides + +To achieve data race safety, Swift 6 has to diagnose a variety of problems that are specific to classes: + +- The sendability of a class must match the sendability of its superclass[^1]. +- If a class is sendable and not isolated to a global actor, its stored properties must be sendable. +- If a class is isolated to a global actor, its superclass must either be non-isolated or isolated to the same global actor. +- An override must have the same isolation as the declaration it overrides. + +[^1]: There is a natural exception to this rule: a sendable class can have a non-sendable superclass if the superclass and all of its ancestors only have sendable stored properties. Currently, Swift only implements this exception for the exact class `NSObject`. + +All of these diagnostics are false positives in purely sequential programs. Fortunately, many of them don't apply to global-actor-isolated classes in the first place. The restriction that isolated classes can't inherit from classes isolated to a different global actor is very reasonable, but it's extremely unlikely to affect programmers in practice because they rarely use global actors other than the main actor at all. The only significant false positive here, then, is the restriction on overriding. + +[SE-0434][] changed the rules for global-actor-isolated classes to allow them to inherit from non-sendable classes; the subclass just remains non-sendable. Since the class is non-sendable and isolated to a global actor, it's tempting to say that override restrictions shouldn't be necessary for it: the object reference should only be usable on the global actor in the first place. This isn't quite true today, but we think we can revise SE-0434 to make it true by imposing restrictions on initializers in the subclass. This would be a small source break, but it would greatly improve the usability of these classes. + +Unfortunately, global-actor-isolated classes that inherit from nonisolated sendable classes can't benefit from the same idea. We can only see one way to make it safe to allow isolated overrides of non-isolated methods for these classes without a whole-program prohibition of concurrency: we would have to prevent any reference to the subclass from being converted to its sendable superclass type. This is feasible to implement, but we suspect it would be too restrictive to be widely useful; we can revisit this with more information. + +## Easing the introduction of basic async code + +[SE-0338](https://github.com/hborla/swift-evolution/blob/async-function-isolation/proposals/0338-clarify-execution-non-actor-async.md) specifies that nonisolated async functions never run on an actor's executor. This design decision was made to prevent unnecessary serialization and contention for the actor by switching off of the actor to run the nonisolated async function, and any new tasks it creates that inherit isolation. The actor is then free to make forward progress on other work. This behavior is especially important for preventing unexpected overhang on the main actor. + +Over time, we have learned that this design decision undermines progressive disclosure, because it prioritizes main actor responsiveness at the expense of making basic asynchronous code difficult to write. Always switching off of an actor to run a nonisolated async function imposes data-race safety errors on programmers when they call the API with non-sendable arguments from the main actor. + +Many library APIs have transitioned to using isolated parameters to ensure that an async API runs on the caller by default, because it’s a much easier default to work with in client code. + +The current execution semantics of async functions also impede programmer’s understanding of the concurrency model because there is a significant difference in what `nonisolated` means on synchronous and asynchronous functions. Nonisolated synchronous functions always run in the isolation domain of the caller, while nonisolated async functions always switch off of the caller's actor (if there is one). It's confusing that `nonisolated` does not have a consistent meaning when applied to functions, and the current behavior conflates the concept of actor isolation with the ability for a function to suspend. + +Changing the default execution semantics of nonisolated async functions to run wherever they are called better facilitates progressive disclosure of concurrency. This default allows functions to leverage suspension without forcing callers to cross an isolation boundary and imposing data-race safety checks on arguments and results. A lot of basic asynchronous code can be written correctly and efficiently with only the ability to suspend. When an async function needs to always run off of an actor, the API author can still specify that with a new `@execution(concurrent)` annotation on the function. This provides a better default for most cases while still maintaining the ease of specifying that an async function switches off of an actor to run. + +Many programmers have internalized the SE-0338 semantics, and making this change several years after SE-0338 was accepted creates an unfortunate intermediate state where it's difficult to understand the semantics of a nonisolated async function without understanding the build settings of the module you're writing code in. We can alleviate some of these consequences with a careful migration design. There are more details about migration in the [automatic migration](#automatic-migration) section of this document, and in the [source compatibility](https://github.com/hborla/swift-evolution/blob/async-function-isolation/proposals/NNNN-async-function-isolation.md#source-compatibility) section of the proposal for this change. + +This idea has already been pitched on the forums, and you can read the full proposal for it [here](https://github.com/hborla/swift-evolution/blob/async-function-isolation/proposals/NNNN-async-function-isolation.md). + +## Easing incremental migration to data-race safety + +Many programmers are not new to concurrency as a concept and have extensive experience with multi-threaded programming. However, the Swift 6 data-race safety model is a significant shift for programmers with experience using concurrency libraries that predate Swift's native concurrency features. Moreover, there are many existing, large codebases that were built on such libraries, and migrating these codebases to both leverage modern concurrency features and enable static data-race safety takes significant engineering effort. An explicit goal of improving the approachability of data-race safety is lessening the amount of effort it takes to enable data-race safety in existing codebases. + +### Bridging between synchronous and asynchronous code + +Introducing async/await into an existing codebase is difficult to do incrementally, because the language does not provide tools to bridge between synchronous and asynchronous code. Sometimes programmers can kick off a new unstructured task to perform the async work, and other times that is not suitable, e.g. because the synchronous code needs a result from the async operation. It’s also not always possible to propagate `async` throughout callers, because the function signature might be declared in a library dependency that you don’t own. + +Notably, using actors in programs that make heavy use of the main actor forces programmers to use async/await, because all interactions with an actor must be done asynchronously from outside the actor. This significantly restricts the utility of actors, especially in existing codebases. + +Other concurrency libraries like Dispatch provide a limited tool set to wait on asynchronous work, such as `DispatchQueue.asyncAndWait`. These tools come with serious tradeoffs, including tying up limited system resources and introducing the possibility for deadlocks, but they provide critical functionality that is sometimes necessary in a project. It’s important for programmers to have the ability to express this in the language, and because the language model allows actor re-entrancy and doesn’t have a strict FIFO guarantee for tasks enqueued on an actor, there’s opportunity to mitigate the risk of deadlocks that these tools come with. + +### Mitigating runtime assertions due to isolation mismatches + +[SE-0423: Dynamic actor isolation enforcement from non-strict-concurrency contexts](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0423-dynamic-actor-isolation.md) introduced dynamic actor isolation assertions that are injected by the compiler at the boundaries between data-race safe and unsafe code. This dynamic checking catches actor isolation violations in library dependencies that have not yet migrated to the Swift 6 language mode, and may have data-race safety issues in their implementation. These checks are effective at identifying missing `@Sendable` annotations, but they also make Swift 6 adoption painful for clients when they migrate before their dependencies. + +Some of these runtime crashes are false positives; the runtime checks are inserted based on the static isolation of the function, but the function might not access any mutable state that’s isolated to or derived from the actor. In these cases, the dynamic checks can simply be elided based on analysis of the function implementation. + +In other cases, the dynamic assertion indicates the presence of a runtime data race, because isolated state is being accessed from outside the actor. The correct way to resolve this data race is either to run the function on the actor, or change the function to eliminate access to actor-isolated state. There are two possible avenues for the language to aid programmers in resolving the data race: + +* Instead of directly calling the function in the wrong isolation domain, enqueue a job that calls the function on the actor. This only works if the function does not return a result. +* Use the import-as-async heuristic from [SE-0297: Concurrency Interoperability with Objective-C](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0297-concurrency-objc.md) to automatically import completion handlers of asynchronous functions as `@Sendable`, which will allow the compiler to diagnose access to actor-isolated state in completion handlers. + +## Automatic migration + +Unlike the Swift 6 migration, all language changes with source compatibility impact described in this vision can be automatically migrated to while preserving the semantics of existing code. The source incompatible portions of this vision will be gated behind [upcoming feature flags](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0362-piecemeal-future-features.md), which will be enabled by default in a future 6.x language mode, except for the per-module setting to infer main actor by default. + +Compiler tooling can automatically migrate existing projects when they choose to enable each of these upcoming features, either individually or as part of a future language mode migration. Programmers will be able to perform a “migration build” with one or more upcoming language features enabled, or with a specific language mode that enables a set of upcoming features, and be offered source code changes that would allow the compiler to build the project without errors and without changing semantics. + +## What’s not in this vision + +This vision does not cover existing pain points with task ordering and actor re-entrancy. These are important problems, but they are more prevalent in more advanced uses of concurrency and warrant a separate, dedicated exploration. + +Improving concurrency diagnostics and documentation is also not covered in this document. All language proposals should consider diagnostics to the extent that language design decisions prevent precise and actionable error messages. Beyond that, diagnostics and documentation changes are not governed by the Swift evolution process, because these changes don't have long-term source compatibility and ABI constraints, so gating improvements behind a heavy weight review process isn't necessary. However, diagnostics and documentation are an extremely important tool for making the concurrency model more approachable, and they will be included in the implementation effort behind this vision. From 24e26bb795a92c59b888153dc571b25213aa8431 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 19 Feb 2025 05:43:50 +0000 Subject: [PATCH 4073/4563] Add link to pitch --- proposals/NNNN-adoption-tooling-for-swift-features.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-adoption-tooling-for-swift-features.md b/proposals/NNNN-adoption-tooling-for-swift-features.md index c51aa9b38d..e3cce3289b 100644 --- a/proposals/NNNN-adoption-tooling-for-swift-features.md +++ b/proposals/NNNN-adoption-tooling-for-swift-features.md @@ -5,7 +5,7 @@ * Review Manager: TBD * Status: **Awaiting implementation** * Implementation: TBD -* Review: TBD +* Review: [pitch](https://forums.swift.org/t/pitch-adoption-tooling-for-upcoming-features/77936) ## Introduction @@ -294,7 +294,7 @@ This proposal was inspired by documents prepared by [Allan Shortlidge][Allan] and [Holly Borla][Holly]. Special thanks to Holly for her guidance throughout the draft stage. - + [Holly]: https://github.com/hborla [Allan]: https://github.com/tshortli From c6039af6146b466a121de35c17a1bf10c862ac8d Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 19 Feb 2025 06:09:32 +0000 Subject: [PATCH 4074/4563] Minor clarification for naming rationale --- proposals/NNNN-adoption-tooling-for-swift-features.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-adoption-tooling-for-swift-features.md b/proposals/NNNN-adoption-tooling-for-swift-features.md index e3cce3289b..e9a6b9d230 100644 --- a/proposals/NNNN-adoption-tooling-for-swift-features.md +++ b/proposals/NNNN-adoption-tooling-for-swift-features.md @@ -285,8 +285,8 @@ with a command-line interface similar to `git add --patch`. ### Naming Perhaps the most intuitive alternative to "adoption" is "migration". -We settled on the former because there is no reason for this concept to be -limited to upcoming features or migrational changes. +We settled on the former because there is no reason for this concept to remain +limited to upcoming features or mechanical migration. ## Acknowledgements From b41bec5425b4394fcc35ef90dc30b65f52da6d14 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 19 Feb 2025 17:12:46 +0900 Subject: [PATCH 4075/4563] Proposal: Task.startSynchronously --- ...k-start-synchronously-on-caller-context.md | 411 ++++++++++++++++++ 1 file changed, 411 insertions(+) create mode 100644 proposals/NNNN-task-start-synchronously-on-caller-context.md diff --git a/proposals/NNNN-task-start-synchronously-on-caller-context.md b/proposals/NNNN-task-start-synchronously-on-caller-context.md new file mode 100644 index 0000000000..836873f954 --- /dev/null +++ b/proposals/NNNN-task-start-synchronously-on-caller-context.md @@ -0,0 +1,411 @@ +# Starting tasks synchronously from caller context + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) +* Review Manager: TBD +* Status: **Awaiting implementation** +* Implementation: TODO +* Review: ... + +## Introduction + +Swift Concurrency's primary means of entering an asynchronous context is creating a Task (structured or unstructured), and from there onwards it is possible to call asynchronous functions, and execution of the current work may _suspend_. + +Entering the asynchronous context today incurs the creating and scheduling of a task to be executed at some later point in time. This initial delay may be wasteful for tasks which perform minimal or none (!) work at all. + +This initial delay may also be problematic for some situations where it is known that we are executing on the "right actor" however are *not* in an asynchronous function and therefore in order to call some different asynchronous function we must create a new task and introduce subtle timing differences as compared to just being able to call the target function–which may be isolated to the same actor we're calling from–immediately. + +## Motivation + +Today, the only way to enter an asynchronous execution context is to create a new task which then will be scheduled on the global concurrent executor or some specific actor the task is isolated to, and only once that task is scheduled execution of it may begin. + +This initial scheduling delay can be problematic in some situations where tight control over execution is required. While for most tasks the general semantics are a good choice–not risking overhang on the calling thread–we have found through experience that some UI or performance sensitive use-cases require a new kind of semantic: starting on the calling context, until a suspension occurs, and only then hopping off to another executor once the task is resumed from the suspension. + +This can be especially beneficial for tasks, which *may run to completion very quickly and without ever suspending.* + +A typical situation where this new API may be beneficial often shows up with @MainActor code, such as: + +```swift +@MainActor var thingsHappened: Int + +@MainActor func onThingHappened(context: Context) { + synchronousFunction() +} + +func asyncUpdateThingsHappenedCounter() async { + // for some reason this function MUST be async + thingsHappened += 1 +} + +func synchronousFunction() { + // we know this executes on the MainActor, and can assume so: + MainActor.assumeIsolated { + // we cannot call the asynchronous function asyncUpdateThingsHappenedCounter though! + } + + // Proposed API: + Task.startSynchronously { + // Now we CAN call the asynchronous function below: + await asyncUpdateThingsHappenedCounter() + } +} +``` + +The above example showcases a typical situation where this new API can be useful. While `assumeIsolated` gives us a specific isolation... it still would not allow us to call arbitrary async functions, as we are still in a synchronous context. + +The proposed `Task.startSynchronously` API forms an async context on the calling thread/task/executor, and therefore allows us to call into async code, at the risk of overhanging on the calling executor. So while this should be used sparingly, it allows entering an asynchronous context *synchronously*. + +## Proposed solution + +We propose the introduction of a new family of Task creation APIs, collectively called "start synchronously", which create a Task and use the calling thread to execute the task's "first synchronous section" right until the task suspends for the first time. + +After the suspension happens, execution yields back to an appropriate executor, and does not continue to use the caller's thread anymore. + +The canonical example for using this new API is using an unstructured task like this: + +```swift +func synchronous() { // synchronous function + // executor / thread: "T1" + let task: Task = Task.startSynchronously { + // executor / thread: "T1" + guard keepRunning() else { return } // synchronous call (1) + + // executor / thread: "T1" + await noSuspension() // potential suspension point #1 // (2) + + // executor / thread: "T1" + await suspend() // potential suspension point #2 // (3), suspend, (5) + // executor / thread: "other" + } + + // (4) continue execution + // executor / thread: "T1" +} +``` + +The task created by the `startSynchronously` function begins running immediately _on the calling executor (and thread)_ without any scheduling delay. This new task behaves generally the same as any other unstructured task, it gets a copy of the outer context's task locals, and uses the surrounding context's base priority as its base priority as well. + +Since the task started running immediately, we're able to perform some calls immediately inside it, and potentially return early. + +If a potential suspension point does not actually suspend, we still continue running on the calling context. For example, if potential suspension point `#1` did not suspend, we still continue running synchronously until we reach potential suspension point `#2` which for the sake of discussion let's say does suspend. At this point the calling thread continues executing the scope that created the unstructured task. + +> You can refer to the `(N)` numbers in the above snippet to follow the execution order of this example execution. Specifically, once the execution reaches (3) the calling thread stops executing the unstructured task, and continues executing at (4). Eventually, when the unstructured task is resumed, it gets woken up at (5) and continues running on some other executor and/or thread. + +## Detailed design + +We propose the introduction of a family of "start synchronously" task creation APIs. + +The most frequent use of this API is likely going to be the unstructured task one. This is because we are able to enter an asynchronous context from a synchronous function using it: + +```swift +extension Task { + + @discardableResult + public static func startSynchronously( + // SE-NNNN's proposed 'name: String? = nil' would be here + priority: TaskPriority? = nil, + executorPreference taskExecutor: consuming (any TaskExecutor)? = nil, + operation: sending @escaping async throws(Failure) -> Success, + ) -> Task + + @discardableResult + public static func startSynchronouslyDetached( + // SE-NNNN's proposed 'name: String? = nil' would be here + priority: TaskPriority? = nil, + executorPreference taskExecutor: consuming (any TaskExecutor)? = nil, + operation: sending @escaping async throws(Failure) -> Success, + ) -> Task +} +``` + +We also offer the same API for all kinds of task groups. These create child tasks, which participate in structured concurrency as one would expect of tasks created by task groups. + +```swift +extension (Throwing)TaskGroup { + + // Same add semantics as 'addTask'. + func startTaskSynchronously( + // SE-NNNN's proposed 'name: String? = nil' would be here + priority: TaskPriority? = nil, + executorPreference taskExecutor: (any TaskExecutor)? = nil, + operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult + ) + + // Same add semantics as 'addTaskUnlessCancelled'. + func startTaskSynchronouslyUnlessCancelled( + // SE-NNNN's proposed 'name: String? = nil' would be here + priority: TaskPriority? = nil, + executorPreference taskExecutor: (any TaskExecutor)? = nil, + operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult + ) +} + +extension (Throwing)DiscardingTaskGroup { + // Same add semantics as 'addTask'. + func startTaskSynchronously( + // SE-NNNN's proposed 'name: String? = nil' would be here + priority: TaskPriority? = nil, + executorPreference taskExecutor: (any TaskExecutor)? = nil, + operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult + ) + + // Same add semantics as 'addTaskUnlessCancelled'. + func startTaskSynchronouslyUnlessCancelled( + // SE-NNNN's proposed 'name: String? = nil' would be here + priority: TaskPriority? = nil, + executorPreference taskExecutor: (any TaskExecutor)? = nil, + operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult + ) +} +``` + +The `startTaskSynchronously` mirrors the functionality of `addTask`, unconditionally adding the task to the group, while the `startTaskSynchronouslyUnlessCancelled` mirrors the `addTaskUnlessCancelled` which only adds the task to the group if the group (or task we're running in, and therefore the group as well) are not cancelled. + +### Isolation rules + +Due to the semantics of "starting on the caller context", the isolation rules of the closure passed to `startSynchronously` need to be carefully considered. + +For example, the following example would not be safe, as unlike `Task.init` the task does not actually immediately become isolated to the isolation of its closure: + +```swift +@MainActor var counter: Int = 0 + +func sayHello() { + Task { @MainActor in // ✅ ok + counter += 1 // we're isolated to the main actor immediately and may modify its state + } + + Task.startSynchronously { @MainActor in // ❌ unsafe, must be compile time error + counter += 1 // Not actually running on the main actor at this point (!) + } +} +``` + +The isolation rules for the `startSynchronously` family of APIs need to account for this synchronous "first part" of the execution. We propose the following set of rules to make this API concurrency-safe: + +- The operation closure is `sending`. +- The operation closure may only specify an isolation (e.g. `{ @MainActor in }`), if and only if already statically contained within the same isolation context. + +This allows for the following pattern, where we can enter an asynchronous task context, from a synchronous function, that is _known_ to be isolated to the main actor already: + +```swift +@MainActor var counter: Int = 0 + +func asyncUpdateCounter() async { counter += 1 } + +@MainActor +func sayHelloOnMain() { + Task.startSynchronously { @MainActor in // ✅ ok, caller isolation is also @MainActor + await asyncUpdateCounter() + } + + Task.startSynchronously { @OtherGlobalActor in // ❌ error: MainActor != OtherGlobalActor + await asyncUpdateCounter() + } +} +``` + +Task executors do not influence the static isolation properties of code, and thus have no impact on the isolation semantics of these APIs. In general, task executors are orthogonal to actor isolation, and while they can influence which actual executor a default actor or global async function would use to execute some piece of code they have no impact on isolation properties and therefore safety properties of a piece of code. + +### Interaction with `Actor/assumeIsolated` + +In [SE-0392: Custom Actor Executor](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0392-custom-actor-executors.md) we introduced the ability to dynamically recover isolation information using the `assumeIsolated` API. It can be used to dynamically recover the runtime information about whether we are executing on some specific actor. + +The `assumeIsolated` shares some ideas with `startSynchronously` however it is distinctly different. For example, while both APIs can effectively be used to "notice we are running on the expected actor, and therefore perform some work on its context". However, `assumeIsolated` does _not_ create a new asynchronous context, while `Task.startSynchronously` does: + +```swift +@MainActor +var state: Int = 0 + +@MainActor +func asyncMainActorMethod() async { } + +func synchronous() { + // assert that we are running "on" the MainActor, + // and therefore can access its isolated state: + MainActor.assumeIsolated { + num +=1 // ✅ ok + + await asyncMainActorMethod() // ❌ error: 'async' call in a function that does not support concurrency + } + +} +``` + +We can compose assumeIsolated with `startSynchronously` to ensure we synchronously start a task on the MainActor if we know we are already running on it, like this: + +```swift +func alwaysCalledFromMainActor() { // we know this because e.g. documentation, but the API wasn't annotated + MainActor.assumeIsolated { // @MainActor isolated + assert(num == 0) + Task.startSynchronously { @MainActor in + num +=1 // ✅ ok + assert(num == 1) // since we are guaranteed nothing else executed since the 'num == 0' assertion + + await asyncMainActorMethod() // ✅ ok + } + } +} +``` + +The synchronously started task will not suspend and context switch until any of the called async methods does. For example, we are guaranteed that there will be no interleaved code execution between the `assert(num == 0)` in our example, and the `num += 1` inside the synchronously started task. + +After the suspension point though, there may have been other tasks executed on the main actor, and we should check the value of `num` again. + +### Structured concurrency semantics + +Synchronously started tasks behave exactly the same as their fully asynchronous equivalents. + +In short, cancellation, and priority escalation remains automatic for structured tasks created using TaskGroup APIs, however they do not propagate automatically for unstructured tasks created using the `Task.startSynchronously[Detached](...)` APIs. Task locals and base priority also functions the same way as usual; + +The only difference in behavior is where these synchronously started tasks _begin_ their execution. + +## Source compatibility + +This proposal is purely additive, and does not cause any source compatibility issues. + +## ABI compatibility + +This proposal is purely ABI additive. + +## Alternatives considered + +### Dynamically asserting isolation correctness + +An important use case of this API is to support calling into an actor isolated context when in a synchronous function that is dynamically already running on that actor. This situation can occur both with instance actors and global actors, however the most commonly requested situation where this shows up is synchronous handler methods in existing frameworks, and which often may have had assumptions about the main thread, and did not yet annotate their API surface with @MainActor annotations. + +It would be possible to create a _dynamically asserting_ version of `Task.startSynchronously`, which does handle the happy path where indeed we "know" where we're going to be called quite well, but gives a *false sense of security* as it may crash at runtime, in the same way the `Actor/preconditionIsolated()` or `Actor/assumeIsolated` APIs do. We believe we should not add more such dynamically crashing APIs, but rather lean into the existing APIs and allow them compose well with any new APIs that should aim to complement them. + +The dynamically asserting version would be something like this: + +```swift +// Some Legacy API: documented to be invoked on main thread but NOT @MainActor annotated and NOT 'async' +func onSomethingHappenedAlwaysOnMainThread(something: Something) { + // we "know" we are on the MainActor, however this is a legacy API that is not an 'async' method + // so we cannot call any other async APIs unless we create a new task. + Task.startSynchronously { @MainActor in + await showThingy() + } +} + +func onSomethingHappenedSometimesOnMainThread(something: Something) { + // 💥 Must assert at runtime if not on main thread + Task.startSynchronously { @MainActor in + await showThingy() + } +} + +func showThingy() async { ... } +``` + +This implementation approach yields safe looking code which unfortunately may have to assert at runtime, rather than further improve the compile time safety properties of Swift Concurrency. + +> See *Future Directions: Dynamically "run synchronously if in right context, otherwise enqueue as usual"* for a future direction that would allow implementing somewhat related APIs in a more elegant and correct way. + +### Banning from use in async contexts (@available(*, noasync)) + +During earlier experiments with such API it was considered if this API should be restricted to only non-async contexts, by marking it `@available(*, noasync)` however it quickly became clear that this API also has specific benefits which can be used to ensure certain ordering of operations, which may be useful regardless if done from an asynchronous or synchronous context. + +## Future Directions + +### Partial not-sending closure semantics + +The isolation rules laid out in this proposal are slightly more conservative than necessary. + +Technically one could make use of the information that the part of the closure up until the first potential suspension point is definitely running synchronously, and therefore even access state that would not be able to be accessed even under region isolation analysis rules. + +We believe that most common situations will be handled well enough by region analysis, and sending closures, however this is a future direction that could be explored if it becomes more apparent that implementing these more complex semantics would be very beneficial. + +For example, such analysis could enable the following: + +```swift +actor Caplin { + var num: Int = 0 + + func check() { + Task.startSynchronously { + num += 1 // could be ok; we know we're synchronously executing on caller + + try await Task.sleep(for: .seconds(1)) + + num += 1 // not ok anymore; we're not on the caller context anymore + } + + num += 1 // always ok + } +} +``` + +### Dynamically "run synchronously if in right context, otherwise enqueue as usual" + +The proposed `startSynchronously` API is a tool to be used in performance and correctness work, and any "may sometimes run synchronously, it depends" is not a good solution to the task at hand. Because of that, we aim for this API to provide the _predictable_ behavior of running synchronously on the caller, without impacting the isolation, that can compose naturally with `assumeIsolated` that can recover dynamic into static isolation information. + +For example, we'll be able to build an API that composes the proposed `startSynchronously` with a not yet proposed but under investigation `Task/isIsolated(to: some Actor) -> Bool` API in order to offer an API that implements the semantics that some developers have been asking for a while: + +- if already dynamically isolated on the expected actor, run synchronously, +- if not, schedule a task to execute the same operation later. + +Using a combination of (a) `Task/startSynchronously`, (b) `Actor/assumeIsolated`, and some form of boolean returning the not yet proposed (c) `isIsolated` (which would be a `Bool` returning equivalent of `assumeIsolated`), we will be able to build such function by composing those more fundamental concurrency operations: + +```swift +func tryRunSynchronouslyOrAsynchronouslyOtherwise( + operation: sending @escaping @isolated(any) () async -> Success +) -> Task { + guard let actor = operation.isolation else { + // no specific isolation, just run async + return Task { try await operation() } + } + + if Task.__current.__isIsolated(to: actor) { // (c) !!! does not exist yet !!! + // we definitely are executing on 'actor' + return actor.assumeIsolated { // (b) guaranteed to not crash + // recovered static isolation information about 'actor' + // (a) use startSynchronously with specific actor isolation + return Task.runSynchronously { + [isolated actor] in // !! does not exist yet (closure isolation control) !! + try await operation() + } + } + } else { + // we are not isolated to 'actor' and therefore must schedule a normal unstructured task + return Task { try await operation() } + } +} +``` + +Or even better we could build the same API with structured concurrency: + +```swift +func tryRunSynchronouslyOrAsynchronouslyOtherwise( + operation: sending @escaping @isolated(any) () async throws -> Success +) async rethrows -> Success { /* same, but use TaskGroup inside */ } +``` + +### Expressing closure isolation tied to function parameter: `@isolated(to:)` + +The currently proposed API is working within the limitations of what is expressible in today's isolation model. It would be beneficial to be able to express the startSynchronously API if we could spell something like "this closure must be isolated to the same actor as the calling function" which would allow for the following code: + +```swift +@MainActor +func test() { + Task.startSynchronously { /* inferred to be @MainActor */ + num += 1 + } +} + +@MainActor var num = 0 +``` + +The way to spell this in an API could be something like this: + +```swift +public static func startSynchronously( + ... + isolation: isolated (any Actor)? = #isolation, + operation: @escaping @isolated(to: isolation) sending async throws(Failure) -> Success, +) -> Task +``` + +The introduction of a hypothetical `@isolated(to:)` paired with an `isolated` `#isolation` defaulted actor parameter, would allow us to express "the *operation* closure statically inherits the exact same isolation as is passed to the isolation parameter of the startSynchronously method". This naturally expresses the semantics that the startSynchronously is offering, and would allow to _stay_ on that isolation context after resuming from the first suspension inside the operation closure. + +Implementing this feature is a large task, and while very desirable we are not ready yet to commit to implementing it as part of this proposal. If and when this feature would become available, we would adopt it in the startSynchronously APIs. From f55ef578eab23f16c64c29501384ca05130daebe Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 19 Feb 2025 20:01:38 +0900 Subject: [PATCH 4076/4563] Update NNNN-task-start-synchronously-on-caller-context.md --- proposals/NNNN-task-start-synchronously-on-caller-context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-task-start-synchronously-on-caller-context.md b/proposals/NNNN-task-start-synchronously-on-caller-context.md index 836873f954..d467268e14 100644 --- a/proposals/NNNN-task-start-synchronously-on-caller-context.md +++ b/proposals/NNNN-task-start-synchronously-on-caller-context.md @@ -323,7 +323,7 @@ actor Caplin { var num: Int = 0 func check() { - Task.startSynchronously { + Task.startSynchronouslyDetached { num += 1 // could be ok; we know we're synchronously executing on caller try await Task.sleep(for: .seconds(1)) From 31ff6788affe981b60e76ca9b8661e6e3092e7cd Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 19 Feb 2025 18:45:34 +0000 Subject: [PATCH 4077/4563] Describe the behavior for specifying invalid mode --- proposals/NNNN-adoption-tooling-for-swift-features.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/NNNN-adoption-tooling-for-swift-features.md b/proposals/NNNN-adoption-tooling-for-swift-features.md index e9a6b9d230..e68f1750b3 100644 --- a/proposals/NNNN-adoption-tooling-for-swift-features.md +++ b/proposals/NNNN-adoption-tooling-for-swift-features.md @@ -148,6 +148,9 @@ For example: -enable-upcoming-feature InternalImportsByDefault:adoption ``` +If the specified mode is invalid, the flag will be ignored, and a warning will +be emitted. This warning will belong to the `StrictLanguageFeatures` diagnostic +group. In a series of either of these options applied to a given feature, only the last option will be honored. If an upcoming feature is both implied by the effective language mode and From c1fc2b9ba4528df2bf9d0ee3faf9318ac93e1c4e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 20 Feb 2025 07:52:16 -0800 Subject: [PATCH 4078/4563] Add a proposal to change the execution semantics of nonisolated async functions. (#2572) * WIP: Add a proposal for changing the execution semantics of nonisolated async functions. * Clarify why `@Sendable` function values can't inherit isolation. * Add an alternative considered for using `nonisolated` to control where an async function executes. * Various proposal clarifications. * Add a bit more detail to the ABI compatibility section. * Fill out the source compatibility section. * Fix a typo in a code example. * Proposal revisions. * Address feedback from the Language Steering Group. 1. Make it clear that in the end, `@execution(caller)` will rarely be explicit in source. 2. Simplify the function conversions section using a table, and clarify the rules for non-`@Sendable` function conversions. 3. Address alternative spellings for `@execution` in the alternatives considered section. * Remove a confusing line of reasoning. It's not very compelling anyway. * Editorial changes to the proposed solution. * Clarify that the import-as-async change is not gated behind the upcoming feature. * Assign SE-0461 --------- Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- proposals/0461-async-function-isolation.md | 935 +++++++++++++++++++++ 1 file changed, 935 insertions(+) create mode 100644 proposals/0461-async-function-isolation.md diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md new file mode 100644 index 0000000000..bc2004e320 --- /dev/null +++ b/proposals/0461-async-function-isolation.md @@ -0,0 +1,935 @@ +# Run nonisolated async functions on the caller's actor by default + +* Proposal: [SE-0461](0461-async-function-isolation.md) +* Authors: [Holly Borla](https://github.com/hborla), [John McCall](https://github.com/rjmccall) +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Active review (February 20...March 2, 2025)** +* Vision: [[Prospective Vision] Improving the approachability of data-race safety](https://forums.swift.org/t/prospective-vision-improving-the-approachability-of-data-race-safety/76183) +* Implementation: On `main` behind `-enable-experimental-feature NonIsolatedAsyncInheritsIsolationFromContext` +* Upcoming Feature Flag: `AsyncCallerExecution` +* Previous Proposal: [SE-0338](0338-clarify-execution-non-actor-async.md) +* Review: ([pitch](https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862)) + +## Introduction + +Swift's general philosophy is to prioritize safety and ease-of-use over +performance, while still providing tools to write more efficient code. The +current behavior of nonisolated async functions prioritizes main actor +responsiveness at the expense of usability. + +This proposal changes the behavior of nonisolated async functions to run on +the caller's actor by default, and introduces an explicit way to state that an +async function always switches off of an actor to run. + +## Table of Contents + +- [Motivation](#motivation) +- [Proposed solution](#proposed-solution) +- [Detailed design](#detailed-design) + - [The `@execution` attribute](#the-execution-attribute) + - [`@execution(caller)` functions](#executioncaller-functions) + - [`@execution(concurrent)` functions](#executionconcurrent-functions) + - [Task isolation inheritance](#task-isolation-inheritance) + - [`#isolation` macro expansion](#isolation-macro-expansion) + - [Isolation inference for closures](#isolation-inference-for-closures) + - [Function conversions](#function-conversions) + - [Executor switching](#executor-switching) + - [Import-as-async heuristic](#import-as-async-heuristic) +- [Source compatibility](#source-compatibility) +- [ABI compatibility](#abi-compatibility) +- [Implications on adoption](#implications-on-adoption) +- [Alternatives considered](#alternatives-considered) + - [Changing isolation inference behavior to implicitly capture isolated parameters](#changing-isolation-inference-behavior-to-implicitly-capture-isolated-parameters) + - [Use `nonisolated` instead of a separate `@execution(concurrent)` attribute](#use-nonisolated-instead-of-a-separate-executionconcurrent-attribute) + - [Don't introduce a type attribute for `@execution`](#dont-introduce-a-type-attribute-for-execution) +- [Revisions](#revisions) + +## Motivation + +[SE-0338: Clarify the Execution of Non-Actor-Isolated Async Functions][SE-0338] +specifies that nonisolated async functions never run on an actor's executor. +This design decision was made to prevent unnecessary serialization and +contention for the actor by switching off of the actor to run the nonisolated +async function, and any new tasks it creates that inherit isolation. The actor +is then free to make forward progress on other work. This behavior is +especially important for preventing unexpected overhang on the main actor. + +This decision has a number of unfortunate consequences. + +**`nonisolated` is difficult to understand.** There is a semantic difference +between the isolation behavior of nonisolated synchronous and asynchronous +functions; nonisolated synchronous functions always run on the caller's actor, +while nonisolated async functions always switch off of the caller's actor. This +means that sendable checking applies to arguments and results of nonisolated +async functions, but not nonisolated synchronous functions. + +For example: + +```swift +class NotSendable { + func performSync() { ... } + func performAsync() async { ... } +} + +actor MyActor { + let x: NotSendable + + func call() async { + x.performSync() // okay + + await x.performAsync() // error + } +} +``` + +The call to `performAsync` from the actor results in a data-race safety error +because the call leaves the actor to run the function. This frees up the actor +to run other tasks, but those tasks can access the non-`Sendable` value `x` +concurrently with the call to `performAsync`, which risks a data race. + +It's confusing that the two calls to methods on `NotSendable` have different +isolation behavior, because both methods are `nonisolated`. + +**Async functions that run on the caller's actor are difficult to express.** +It's possible to write an async function that does not leave an actor to run +using isolated parameters and the `#isolation` macro as a default argument: + +```swift +class NotSendable { + func performAsync( + isolation: isolated (any Actor)? = #isolation + ) async { ... } +} + +actor MyActor { + let x: NotSendable + + func call() async { + await x.performAsync() // okay + } +} +``` + +This resolves the data-race safety error because `performAsync` now runs on the +actor. However, this isn't an obvious solution, it's onerous boilerplate to +write, and the default argument is lost if the method is used in a higher-order +manner. + +**It's easy to write invalid async APIs.** If the `performAsync` method were in +a library that the programmer doesn't own, it's not possible to workaround the +data-race safety error without using unsafe opt outs. It's common for library +authors to mistakenly vend an API like this, because the data-race safety error +only manifests when calling the API from an actor. + +The concurrency library itself has made this mistake, and many of the async +APIs in the concurrency library have since transitioned to inheriting the +isolation of the caller using isolated parameters; see [SE-0421][SE-0421] for +an example. + +**It's difficult to write higher-order async APIs.** Consider the following +async API which provides a `with`-style method for acquiring a resource and +performing a scoped operation: + +```swift +public struct Resource { + internal init() {} + internal mutating func close() async {} +} + +public func withResource( + isolation: isolated (any Actor)? = #isolation, + _ body: (inout Resource) async -> Return +) async -> Return { + var resource = Resource() + let result = await body(&resource) + await resource.close() + return result +} +``` + +Despite `withResource` explicitly running on the caller's actor by default, +there's no way to specify that the async `body` function value should also run +in the same context. The compiler treats the async function parameter as +switching off of the actor to run, so it requires sendable checking on the +arguments and results. This particular example happens to pass a value in a +disconnected region to `body`, but passing an argument in the actor's region +would be invalid. In most cases, the call doesn't cross an isolation boundary +at runtime, because the function type is not `@Sendable`, so calling the API +from an actor-isolated context and passing a trailing closure will treat the +closure as isolated to the same actor. This sendable checking is often a source +of false positives that make higher-order async APIs extremely difficult to +write. The checking can't just be eliminated, because it's valid to pass a +nonisolated async function that will switch off the actor to run, which would +lead to a data race if actor-isolated state is passed to the `body` parameter. + +Moreover, the above explanation of isolation rules for async closures is +extremely difficult to understand; the default isolation rules are too +complicated. + +## Proposed solution + +This proposal changes the execution semantics of nonisolated async functions +to always run on the caller's actor by default. This means that nonisolated +functions will have consistent execution semantics by default, regardless of +whether the function is synchronous or asynchronous. + +This change makes the following example from the motivation section +valid, because the call to `x.performAsync()` does not cross an isolation +boundary: + +```swift +class NotSendable { + func performSync() { ... } + func performAsync() async { ... } +} + +actor MyActor { + let x: NotSendable + + func call() async { + x.performSync() // okay + + await x.performAsync() // okay + } +} +``` + +Changing the default execution semantics of async functions can change the +behavior of existing code, so the change is gated behind the +`AsyncCallerExecution` upcoming feature flag. To help stage in the new +behavior, a new `@execution` attribute can be used to explicitly specify the +execution semantics of an async function in any language mode. The +`@execution(concurrent)` attribute is an explicit spelling for the behavior of +async functions in language modes <= Swift 6, and the `@execution(caller)` +attribute is an explicit spelling for async functions that run on the caller's +actor. + +For example: + +```swift +class NotSendable { + func performSync() { ... } + + @execution(caller) + func performAsync() async { ... } + + @execution(concurrent) + func alwaysSwitch() async { ... } +} + +actor MyActor { + let x: NotSendable + + func call() async { + x.performSync() // okay + + await x.performAsync() // okay + + await x.alwaysSwitch() // error + } +} +``` + +`@execution(concurrent)` is the current default for nonisolated async +functions. `@execution(caller)` will become the default for async functions +when the `AsyncCallerExecution` upcoming feature is enabled. + +## Detailed design + +The sections below will explicitly use `@execution(concurrent)` and +`@execution(caller)` to demonstrate examples that will behave consistently +independent of upcoming features or language modes. However, note that the +end state under the `AsyncCallerExecution` upcoming feature will mean that +`@execution(caller)` is not necessary to explicitly write, and +`@execution(caller)` will likely be used sparingly because it has far +stricter data-race safety requirements. + +### The `@execution` attribute + +`@execution` is a declaration and type attribute that specifies the execution +semantics of an async function. `@execution` must be written with an argument +of either `caller` or `concurrent`. The details of each argument are specified +in the following sections. + +> _Naming rationale_: The term `concurrent` in `@execution(concurrent)` was +> chosen because the colloquial phrase "runs concurrently with actors" is a +> good way to describe the semantics of the function execution. Similarly, the +> async function can be described as running on the concurrent executor. + +Only (implicitly or explicitly) `nonisolated` functions can be marked with the +`@execution` attribute; it is an error to use the `@execution` attribute with +an isolation other than `nonisolated`, including global actors, isolated +parameters, and `@isolated(any)`. The `@execution` attribute can be used +together with `@Sendable` or `sending`. + +The `@execution` attribute is preserved in the type system so that the execution +semantics can be distinguished for function vales. + +The `@execution` attribute cannot be applied to synchronous functions. This is +an artificial limitation that could later be lifted if use cases arise. + +#### `@execution(caller)` functions + +Async functions annotated with `@execution(caller)` will always run on the +caller's actor: + +```swift +class NotSendable { + func performSync() { ... } + + @execution(caller) + func performAsync() async { ... } +} + +actor MyActor { + let x: NotSendable + + func call() async { + x.performSync() // okay + + await x.performAsync() // okay + } +} +``` + +In the above code, the call to `x.performAsync()` continues running on the +`self` actor instance. The code does not produce a data-race safety error, +because the `NotSendable` instance `x` does not leave the actor. + +This behavior is accomplished by implicitly passing an optional actor parameter +to the async function. The function will run on this actor's executor. See the +[Executor switching](#executor-switching) section for more details on why the +actor parameter is necessary. + +The type of an `@execution(caller)` function declaration is an +`@execution(caller)` function type. For example: + +```swift +class NotSendable { ... } + +func useAsValue(_ ns: NotSendable) async { ... } + +@MainActor let global: NotSendable = .init() + +@execution(caller) +func runOnActor(ns: NotSendable) async {} + +@MainActor +func callSendableClosure() async { + // the type of 'closure' is '@Sendable @execution(caller) (NotSendable) -> Void' + let closure = runOnActor(ns:) + + let ns = NotSendable() + await closure(ns) // okay + await closure(global) // okay +} + +callSendableClosure(useAsValue) +``` + +In the above code, the calls to `closure` from `callSendableClosure` run on the +main actor, because `closure` is `@execution(caller)` and `callSendableClosure` +is main actor isolated. + +#### `@execution(concurrent)` functions + +Async functions can be declared to always switch off of an actor to run using +the `@execution(concurrent)` attribute: + +```swift +struct S: Sendable { + @execution(concurrent) + func alwaysSwitch() async { ... } +} +``` + +The type of an `@execution(concurrent)` function declaration is an +`@execution(concurrent)` function type. Details on function conversions are +covered in a [later section](#function-conversions). + +When an `@execution(concurrent)` function is called from a context that can +run on an actor, including `@execution(caller)` functions or actor-isolated +functions, sendable checking is performed on the argument and result values. +Either the argument and result values must have a type that conforms to +`Sendable`, or the values must be in a disconnected region so they can be sent +outside of the actor: + +```swift +class NotSendable {} + +@execution(concurrent) +func alwaysSwitch(ns: NotSendable) async { ... } + +actor MyActor { + let ns: NotSendable = .init() + + func callConcurrent() async { + await alwaysSwitch(ns: ns) // error + + let disconnected = NotSendable() + await alwaysSwitch(ns: disconnected) // okay + } +} +``` + +### Task isolation inheritance + +Unstructured tasks created in nonisolated functions never run on an actor +unless explicitly specified. This behavior is consistent for all nonisolated +functions, including synchronous functions, `@execution(caller)` async +functions, and `@execution(concurrent)` async functions. + +For example: + +```swift +class NotSendable { + var value = 0 +} + +@execution(caller) +func createTask(ns: NotSendable) async { + Task { + // This task does not run on the same actor as `createTask` + + ns.value += 1 // error + } +} +``` + +Capturing `ns` in the unstructured task is an error, because the value can +be used concurrently between the caller of `createTask` and the newly +created task. + +This decision is deliberate to match the semantics of unstructured task +creation in nonisolated synchronous functions. Note that unstructured task +creation in methods with isolated parameters also do not inherit isolation +if the isolated parameter is not explicitly captured. + +### `#isolation` macro expansion + +Uses of the `#isolation` macro will expand to the implicit isolated parameter. +For example, the following program prints `Optional(Swift.MainActor)`: + +```swift +nonisolated func printIsolation() async { + let isolation = #isolation + print(isolation) +} + +@main +struct Program { + // implicitly isolated to @MainActor + static func main() async throws { + await printIsolation() + } +} +``` + +This behavior allows async function calls that use `#isolation` as a default +isolated argument to run on the same actor when called from an +`@execution(caller)` function. For example, the following code is valid because +the call to `explicitIsolationInheritance` does not cross an isolation +boundary: + +```swift +class NotSendable { ... } + +func explicitIsolationInheritance( + ns: NotSendable, + isolation: isolated (any Actor)? = #isolation +) async { ... } + +@execution(caller) +func printIsolation(ns: NotSendable) async { + await explicitIsolationInheritance(ns: ns) // okay +} +``` + +Note that this introduces a semantic difference compared to synchronous +nonisolated functions, where there is no implicit isolated parameter and +`#isolation` always expands to `nil`. For example, the following program prints +`nil`: + +```swift +func printIsolation() { + let isolation = #isolation + print(isolation) +} + +@main +struct Program { + // implicitly isolated to @MainActor + static func main() async throws { + printIsolation() + } +} +``` + +In an `@execution(concurrent)` function, the `#isolation` macro expands to +`nil`. + +### Isolation inference for closures + +Note that the rules in this section are not new with this proposal. However, +these rules have not been specified in any other proposal, and they are +necessary for understanding the execution semantics of async closures. + +The isolation of a closure can be explicitly specified with a type annotation +or in the closure signature. If no isolation is specified, the inferred +isolation for a closure depends on two factors: +1. The isolation of the context where the closure is formed. +2. Whether the contextual type of the closure is `@Sendable` or `sending`. + +If the contextual type of the closure is neither `@Sendable` nor `sending`, the +inferred isolation of the closure is the same as the enclosing context: + +```swift +class NotSendable { ... } + +@MainActor +func closureOnMain(ns: NotSendable) async { + let syncClosure: () -> Void = { + // inferred to be @MainActor-isolated + + // capturing main-actor state is okay + print(ns) + } + + // runs on the main actor + syncClosure() + + let asyncClosure: (NotSendable) async -> Void = { + // inferred to be @MainActor-isolated + + print($0) + } + + // runs on the main actor; + // passing main-actor state is okay + await asyncClosure(ns) +} +``` + +If the type of the closure is `@Sendable` or if the closure is passed to a +`sending` parameter, the closure is inferred to be `nonisolated`. + +The closure is also inferred to be `nonisolated` if the enclosing context +has an isolated parameter (including `self` in actor-isolated methods), and +the closure does not explicitly capture the isolated parameter. This is done to +avoid implicitly capturing values that are invisible to the programmer, because +this can lead to reference cycles. + +### Function conversions + +Function conversions can change isolation. You can think of this like a +closure with the new isolation that calls the original function, asynchronously +if necessary. For example, a function conversion from one global-actor-isolated +type to another can be conceptualized as an async closure that calls the +original function with `await`: + +```swift +@globalActor actor OtherActor { ... } + +func convert( + closure: @OtherActor () -> Void +) { + let mainActorFn: @MainActor () async -> Void = closure + + // The above conversion is the same as: + + let mainActorEquivalent: @MainActor () async -> Void = { + await closure() + } +} +``` + +A function conversion that crosses an isolation boundary must only +pass argument and result values that are `Sendable`; this is checked +at the point of the function conversion. For example, converting an +actor-isolated function type to a `nonisolated` function type requires +that the argument and result types conform to `Sendable`: + +```swift +class NotSendable {} +actor MyActor { + var ns = NotSendable() + + func getState() -> NotSendable { ns } +} + +func invalidResult(a: MyActor) async -> NotSendable { + let grabActorState: @execution(caller) () async -> NotSendable = a.getState // error + + return await grabActorState() +} +``` + +In the above code, the conversion from the actor-isolated method `getState` +to a `@execution(caller) nonisolated` function is invalid, because the +result type does not conform to `Sendable` and the result value could be +actor-isolated state. The `nonisolated` function can be called from +anywhere, which would allow access to actor state from outside the actor. + +Not all function conversions cross an isolation boundary, and function +conversions that don't can safely pass non-`Sendable` arguments and results. +For example, a `@execution(caller)` function type can always be converted to an +actor-isolated function type, because the `@execution(caller)` function will +simply run on the actor: + +```swift +class NotSendable {} + +@execution(caller) +nonisolated func performAsync(_ ns: NotSendable) async { ... } + +@MainActor +func convert(ns: NotSendable) async { + // Okay because 'performAsync' will run on the main actor + let runOnMain: @MainActor (NotSendable) async -> Void = performAsync + + await runOnMain(ns) +} +``` + +The following table enumerates each function conversion rule and specifies +which function conversions cross an isolation boundary. Function conversions +that cross an isolation boundary require `Sendable` argument and result types, +and the destination function type must be `async`. Note that the function +conversion rules for synchronous `nonisolated` functions and asynchronous +`@execution(caller) nonisolated` functions are the same; they are both +represented under the "Nonisolated" category in the table: + +| Old isolation | New isolation | Crosses Boundary | +|--------------------------|----------------------------|------------------| +| Nonisolated | Actor isolated | No | +| Nonisolated | `@isolated(any)` | No | +| Nonisolated | `@execution(concurrent)` | Yes | +| Actor isolated | Actor isolated | Yes | +| Actor isolated | `@isolated(any)` | No | +| Actor isolated | Nonisolated | Yes | +| Actor isolated | `@execution(concurrent)` | Yes | +| `@isolated(any)` | Actor isolated | Yes | +| `@isolated(any)` | Nonisolated | Yes | +| `@isolated(any)` | `@execution(concurrent)` | Yes | +| `@execution(concurrent)` | Actor isolated | Yes | +| `@execution(concurrent)` | `@isolated(any)` | No | +| `@execution(concurrent)` | Nonisolated | Yes | + +#### Non-`@Sendable` function conversions + +If a function type is not `@Sendable`, only one isolation domain can +reference the function at a time, and calls to the function may never +happen concurrently. These rules for non-`Sendable` types are enforced +through region isolation. When a non-`@Sendable` function is converted +to an actor-isolated function, the function value itself is merged into the +actor's region, along with any non-`Sendable` function captures: + +```swift +class NotSendable { + var value = 0 +} + +@execution(caller) +func convert(closure: () -> Void) async { + let ns = NotSendable() + let disconnectedClosure = { + ns.value += 1 + } + let valid: @MainActor () -> Void = disconnectedClosure // okay + await valid() + + let invalid: @MainActor () -> Void = closure // error + await invalid() +} +``` + +The function conversion for the `invalid` variable is an error because the +non-`Sendable` captures of `closure` could be used concurrently from the caller +of `convert` and the main actor. + +Converting a non-`@Sendable` function type to an actor-isolated one is invalid +if the original function must leave the actor in order to be called: + +```swift +@execution(caller) +func convert( + fn1: @escaping @execution(concurrent) () async -> Void, +) async { + let fn2: @MainActor () async -> Void = fn1 // error + + await withDiscardingTaskGroup { group in + group.addTask { await fn2() } + group.addTask { await fn2() } + } +} +``` + +In general, a conversion from an actor-isolated function type to a +`nonisolated` function type crosses an isolation boundary, because the +`nonisolated` function type can be called from an arbitrary isolation domain. +However, if the conversion happens on the actor, and the new function type is +not `@Sendable`, then the function must only be called from the actor. In this +case, the function conversion is allowed, and the resulting function value +is merged into the actor's region: + +```swift +class NotSendable {} + +@MainActor class C { + var ns: NotSendable + + func getState() -> NotSendable { ns } +} + +func call(_ closure: () -> NotSendable) -> NotSendable { + return closure() +} + +@MainActor func onMain(c: C) { + // 'result' is in the main actor's region + let result = call(c.getState) +} +``` + +### Executor switching + +Async functions switch executors in the implementation when entering the +function, and after any calls to other async functions. Note that synchronous +functions do not have the ability to switch executors. If a call to a +synchronous function crosses an isolation boundary, the call must happen in an +async context and the executor switch happens at the caller. + +`@execution(concurrent)` async functions switch to the generic executor, and +all other async functions switch to the isolated actor's executor. + +```swift +@MainActor func runOnMainExecutor() async { + // switch to main actor executor + + await runOnGenericExecutor() + + // switch to main actor executor +} + +@execution(concurrent) func runOnGenericExecutor() async { + // switch to generic executor + + await Task { @MainActor in + // switch to main actor executor + + ... + }.value + + // switch to generic executor +} +``` + +`@execution(caller)` functions will switch to the executor of the implicit +actor parameter passed from the caller instead of switching to the generic +executor: + +```swift +@MainActor func runOnMainExecutor() async { + // switch to main actor executor + ... +} + +class NotSendable { + var value = 0 +} + +actor MyActor { + let ns: NotSendable = .init() + + func callNonisolatedFunction() async { + await inheritIsolation(ns) + } +} + +nonisolated func inheritIsolation(_ ns: NotSendable) async { + // switch to isolated parameter's executor + + await runOnMainExecutor() + + // switch to isolated parameter's executor + + ns.value += 1 +} +``` + +For most calls, the switch upon entering the function will have no effect, +because it's already running on the executor of the actor parameter. + +A task executor preference can still be used to configure where a nonisolated +async function runs. However, if the nonisolated async function was called from +an actor with a custom executor, the task executor preference will not apply. +Otherwise, the code will risk a data-race, because the task executor preference +does not apply to actor-isolated methods with custom executors, and the +nonisolated async method can be passed mutable state from the actor. + +### Import-as-async heuristic + +Nonisolated functions imported from Objective-C that match the import-as-async +heuristic from [SE-0297: Concurrency Interoperability with Objective-C][SE-0297] +will implicitly be imported as `@execution(caller)`. Objective-C async +functions already have bespoke code generation that continues running on +the caller's actor to match the semantics of the original completion handler +function, so `@execution(caller)` already better matches the semantics of these +imported `async` functions. This change will eliminate many existing data-race +safety issues that happen when calling an async function on an Objective-C +class from the main actor. Because the only effect of this change is +eliminating concurrency diagnostics -- the runtime behavior of the code will +not change -- it will not be gated behind the upcoming feature. + +## Source compatibility + +This proposal changes the semantics of nonisolated async functions when the +upcoming feature flag is enabled. Without the upcoming feature flag, the default +for nonisolated async functions is `@execution(concurrent)`. When the upcoming +feature flag is enabled, the default for nonisolated async functions changes to +`@execution(caller)`. This applies to both function declarations and function +values that are nonisolated (either implicitly or explicitly). + +Changing the default execution semantics of nonisolated async functions has +minor source compatibility impact if the implementation calls an +`@execution(concurrent)` function and passes non-Sendable state in the actor's +region. In addition to the source compatibility impact, the change can also +regress performance of existing code if, for example, a specific async function +relied on running off of the main actor when called from the main actor to +maintain a responsive UI. + +To avoid breaking source compatibility or silently changing behavior of +existing code, this change must be gated behind an upcoming feature flag. +However, unlike most other changes gated behind upcoming feature flags, this +change allows writing code that is valid with and without the upcoming feature +flag, but means something different. Many programmers have internalized the +SE-0338 semantics, and making this change several years after SE-0338 was +accepted creates an unfortunate intermediate state where it's difficult to +understand the semantics of a nonisolated async function without understanding +the build settings of the module you're writing code in. To mitigate these +consequences, the compiler will emit warnings in all language modes +that do not enable this upcoming feature to prompt programmers to explicitly +specify the execution semantics of a nonisolated async function. + +Without the upcoming feature enabled, the compiler will warn if neither +attribute is specified on a nonisolated async function. With the +upcoming feature enabled, the default for a nonisolated async +function is `@execution(caller)`. Packages that must support older Swift tools +versions can use `#if hasAttribute(execution)` to silence the warning while +maintaining compatibility with tools versions back to Swift 5.8 when +`hasAttribute` was introduced: + +```swift +#if hasAttribute(execution) +@execution(concurrent) +#endif +public func myAsyncAPI() async { ... } +``` + +## ABI compatibility + +Adopting the semantics to run on the caller's actor for an existing nonisolated +async function is an ABI change, because the caller's actor must be passed as +a parameter. However, a number of APIs in the concurrency library have staged +in similar changes using isolated parameters and `#isolation`, and it may be +possible to offer tools to do this transformation automatically for resilient +libraries that want to adopt this behavior. + +For example, if a nonisolated async function is ABI-public and is available +earlier than a version of the Swift runtime that includes this change, the +compiler could emit two separate entry points for the function: + +```swift +@_alwaysEmitIntoClient +public func myAsyncFunc() async { + // original implementation +} + +@execution(concurrent) +@_silgen_name(...) // to preserve the original symbol name +@usableFromInline +internal func abi_myAsyncFunc() async { + // existing compiled code will continue to always run calls to this function + // on the generic executor. + await myAsyncFunc() +} +``` + +This transformation only works if the original function implementation +can be made inlinable. + +## Implications on adoption + +`@execution(caller)` functions must accept an implicit actor parameter. This +means that adding `@execution(caller)` to a function that is actor-isolated, or +changing a function from `@execution(concurrent)` to `@execution(caller)`, is +not a resilient change. + +## Alternatives considered + +### Changing isolation inference behavior to implicitly capture isolated parameters + +The current isolation inference behavior in contexts with isolated parameters +is often surprising with respect to data-race safety. However, this proposal +does not suggest changing the rules, because implicitly capturing an isolated +parameter can lead to silently causing new memory leaks in existing code. One +potential compromise is to keep the current isolation inference behavior, and +offer fix-its to capture the actor if there are any data-race safety errors +from capturing state in the actor's region. + +### Use `nonisolated` instead of a separate `@execution(concurrent)` attribute + +It's tempting to not introduce a new attribute to control where an async +function executes, and instead control this behavior with an explicit +`nonisolated` annotation. However, this approach falls short for the following +reasons: + +1. It does not accomplish the goal of having consistent semantics for + `nonisolated` by default, regardless of whether it's applied to synchronous + or async functions. +2. This approach cuts off the future direction of allowing + `@execution(concurrent)` on synchronous functions. + +### Use "isolation" terminology instead of "execution" + +One other possibility is to use isolation terminology instead of `@execution` +for the syntax. This direction does not accomplish goal 1. in the previous +section to have a consistent meaning for `nonisolated` across synchronous and +async functions. If the attribute were spelled `@isolated(caller)` and +`@isolated(concurrent)`, presumably that attribute would not work together with +`nonisolated`; it would instead be an alternative kind of actor isolation. + +Having `@execution(caller)` as an attribute that is used together with +`nonisolated` leads to a simpler programming model because after the upcoming +feature is enabled, programmers will simply write `nonisolated` on an `async` +function in the same way that `nonisolated` is applied to synchronous +functions. If we choose a different form of isolation like `@isolated(caller)`, +programmers have to learn a separate syntax for `async` functions that +accomplishes the same effect as a `nonisolated` synchronous function. + +### Don't introduce a type attribute for `@execution` + +There are a lot of existing type attributes for concurrency and it's +unfortunate to introduce another one. However, without `@execution` as a type +attribute, referencing nonisolated async functions unapplied is very restrictive, +because sendable checking would need to be performed at the point of the +function reference instead of when the function is called. + +## Revisions + +The proposal was revised with the following changes after the pitch discussion: + +* Gate the behavior change behind an `AsyncCallerExecution` upcoming feature + flag. +* Change the spelling of `@concurrent` to `@execution(concurrent)`, and add an + `@execution(caller)` attribute to allow expressing the new behavior this + proposal introduces when the upcoming feature flag is not enabled. +* Apply `@execution(caller)` to nonisolated async function types by default to + make the execution semantics consistent between async function declarations + and values. +* Change the terminology in the proposal to not use the "inherits isolation" + phrase. + +[SE-0297]: /proposals/0297-concurrency-objc.md +[SE-0338]: /proposals/0338-clarify-execution-non-actor-async.md +[SE-0421]: /proposals/0421-generalize-async-sequence.md From e56820b8eaeb5441ad4b0a4e0132eb501729f291 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Thu, 20 Feb 2025 11:04:47 -0500 Subject: [PATCH 4079/4563] Add link to review thread for SE-0461 (#2701) --- proposals/0461-async-function-isolation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md index bc2004e320..cdece7a616 100644 --- a/proposals/0461-async-function-isolation.md +++ b/proposals/0461-async-function-isolation.md @@ -8,7 +8,7 @@ * Implementation: On `main` behind `-enable-experimental-feature NonIsolatedAsyncInheritsIsolationFromContext` * Upcoming Feature Flag: `AsyncCallerExecution` * Previous Proposal: [SE-0338](0338-clarify-execution-non-actor-async.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862)) +* Review: ([pitch](https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862)) ([review](https://forums.swift.org/t/se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/77987)) ## Introduction From 8ae57e809cfbe17a549fff127b006bf54cbecfdd Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Thu, 20 Feb 2025 23:32:34 +0000 Subject: [PATCH 4080/4563] Clarify diagnostic group for a proposed warning --- proposals/NNNN-adoption-tooling-for-swift-features.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-adoption-tooling-for-swift-features.md b/proposals/NNNN-adoption-tooling-for-swift-features.md index e68f1750b3..a7b69dd356 100644 --- a/proposals/NNNN-adoption-tooling-for-swift-features.md +++ b/proposals/NNNN-adoption-tooling-for-swift-features.md @@ -127,6 +127,7 @@ for a given upcoming feature, i.e., because the upcoming feature does not have a mechanical migration. A corresponding warning will be emitted in this case to avoid the false impression that the impacted source code is compatible with the feature. +This warning will belong to the diagnostic group `StrictLanguageFeatures`. ### Interface @@ -149,8 +150,8 @@ For example: ``` If the specified mode is invalid, the flag will be ignored, and a warning will -be emitted. This warning will belong to the `StrictLanguageFeatures` diagnostic -group. +be emitted. +This warning will belong to the diagnostic group `StrictLanguageFeatures`. In a series of either of these options applied to a given feature, only the last option will be honored. If an upcoming feature is both implied by the effective language mode and From 991e4c803e87360706175529ae7b28bba11e166a Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 21 Feb 2025 08:38:22 +0900 Subject: [PATCH 4081/4563] Task Priority Escalation APIs (#2685) * Task Priority Escalation APIs * Update proposals/NNNN-task-priority-escalation-apis.md Co-authored-by: Franz Busch * Add better example, thanks to @dnadoba * remove the section explaining triggering * Apply suggestions from code review Co-authored-by: Jamie <2119834+jamieQ@users.noreply.github.com> * mark as implemented * Apply suggestions from code review * Task priority escalation is SE-0462 --------- Co-authored-by: Franz Busch Co-authored-by: Jamie <2119834+jamieQ@users.noreply.github.com> Co-authored-by: Frederick Kellison-Linn Co-authored-by: Freddy Kellison-Linn --- .../0462-task-priority-escalation-apis.md | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 proposals/0462-task-priority-escalation-apis.md diff --git a/proposals/0462-task-priority-escalation-apis.md b/proposals/0462-task-priority-escalation-apis.md new file mode 100644 index 0000000000..a0ba3e47f1 --- /dev/null +++ b/proposals/0462-task-priority-escalation-apis.md @@ -0,0 +1,237 @@ +# Task Priority Escalation APIs + +* Proposal: [SE-0462](0462-task-priority-escalation-apis.md) +* Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) +* Review Manager: [Freddy Kellison-Linn](https://github.com/jumhyn) +* Status: **Active review (February 20...March 2, 2025)** +* Implementation: https://github.com/swiftlang/swift/pull/78625 +* Review: ([pitch](https://forums.swift.org/t/pitch-task-priority-escalation-apis/77702)) + +## Introduction + +A large part of Swift Concurrency is its Structured Concurrency model, in which tasks automatically form parent-child relationships, and inherit certain traits from their parent task. For example, a task started from a medium priority task, also starts on the medium priority, and not only that – if the parent task gets awaited on from a higher priority task, the parent's as well as all of its child tasks' task priority will be escalated in order to avoid priority inversion problems. + +This feature is automatic and works transparently for any structured task hierarchy. This proposal will discuss exposing user-facing APIs which can be used to participate in task priority escalation. + +## Motivation + +Generally developers can and should rely on the automatic task priority escalation happening transparently–at least for as long as all tasks necessary to escalate are created using structured concurrency primitives (task groups and `async let`). However, sometimes it is not possible to entirely avoid creating an unstructured task. + +One such example is the async sequence [`merge`](https://github.com/apple/swift-async-algorithms/blob/4c3ea81f81f0a25d0470188459c6d4bf20cf2f97/Sources/AsyncAlgorithms/AsyncAlgorithms.docc/Guides/Merge.md) operation from the [swift-async-algorithms](https://github.com/apple/swift-async-algorithms/) project where the implementation is forced to create an unstructured task for iterating the upstream sequences, which must outlive downstream calls. These libraries would like to participate in task priority escalation to boost the priority of the upstream consuming task, however today they lack the API to do so. + +```swift +// SIMPLIFIED EXAMPLE CODE +// Complete source: https://github.com/apple/swift-async-algorithms/blob/main/Sources/AsyncAlgorithms/Merge/MergeStorage.swift + +struct AsyncMergeSequenceIterator: AsyncIterator { + struct State { + var task: Task? // unstructured upstream consumer task + var buffer: Deque + var upstreamContinuations: [UnsafeContinuation] + var downstreamContinuation: UnsafeContinuation? + } + + let state = Mutex(State()) + + func next() async throws { + self.state.withLock { state in + if state.task == nil { + state.task = Task { + // Consume from the base iterators + // ... + } + } + } + + if let element = self.state.withLock { $0.buffer.popFirst() } { + return element + } else { + // We are handling cancellation here and need to handle task escalation here as well + try await withTaskCancellationHandler { + // HERE: need to handle priority escalation and boost `state.task` + try await withCheckedContinuation { cont in + self.state.withLock { $0.consumerContinuation = cont } + } + } onCancel: { + // trigger cancellation of tasks and fail continuations + } + } + } +} +``` + +The above example showcases a common pattern: often a continuation is paired with a Task used to complete it. Around the suspension on the continuation, waiting for it to be resumed, developers often install a task cancellation handler in order to potentially break out of potentially unbounded waiting for a continuation to be resumed. Around the same suspension (marked with `HERE` in the snippet above), we might want to insert a task priority escalation handler in order to priority boost the task that is used to resume the continuation. This can be important for correctness and performance of such operations, so we should find a way to offer these libraries a mechanism to participate in task priority handling. + +Another example of libraries which may want to reach for manual task priority escalation APIs are libraries which facilitate communication across process boundaries, and would like to react to priority escalation and propagate it to a different process. Relying on the built-in priority escalation mechanisms won't work, because they are necessarily in-process, so libraries like this need to be able to participate and be notified when priority escalation happens, and also be able to efficiently cause the escalation inside the other process. + +## Proposed solution + +In order to address the above use-cases, we propose to add a pair of APIs: to react to priority escalation happening within a block of code, and an API to _cause_ a priority escalation without resorting to trickery by creating new tasks whose only purpose is to escalate the priority of some other task: + +```swift +enum State { + case initialized + case task(Task) + case priority(TaskPriority) +} +let m: Mutex = .init(.initialized) + +await withTaskPriorityEscalationHandler { + await withCheckedContinuation { cc in + let task = Task { cc.resume() } + + let newPriority: TaskPriority? = state.withLock { state -> TaskPriority? in + defer { state = .task(task) } + switch state { + case .initialized: + return nil + case .task: + preconditionFailure("unreachable") + case .priority(let priority): + return priority + } + } + // priority was escalated just before we stored the task in the mutex + if let newPriority { + Task.escalatePriority(task, to: newPriority) + } + } onPriorityEscalated: { newPriority in + state.withLock { state in + switch state { + case .initialized, .priority: + // priority was escalated just before we managed to store the task in the mutex + state = .priority(newPriority) + case .task(let task): + Task.escalatePriority(task, to: newPriority) + } + } + } +} +``` + +The above snippet handles edge various ordering situations, including the task escalation happening after +the time the handler is registered but _before_ we managed to create and store the task. + +In general, task escalation remains a slightly racy affair, we could always observe an escalation "too late" for it to matter, +and have any meaningful effect on the work's execution, however this API and associated patterns handle most situations which +we care about in practice. + +## Detailed design + +We propose the addition of a task priority escalation handler, similar to task cancellation handlers already present in the concurrency library: + +```swift +public func withTaskPriorityEscalationHandler( + operation: () async throws(E) -> T, + onPriorityEscalated handler: @Sendable (TaskPriority) -> Void, + isolation: isolated (any Actor)? = #isolation +) async throws(E) -> T +``` + +The shape of this API is similar to the `withTaskCancellationHandler` API present since initial Swift Concurrency release, however–unlike a cancellation handler–the `onPriorityEscalated` callback may be triggered multiple times. The `TaskPriority` passed to the handler is the "new priority" the surrounding task was escalated to. + +It is guaranteed that priority is ever only increasing, as Swift Concurrency does not allow for a task priority to ever be lowered after it has been escalated. If attempts are made to escalate the task priority from multiple other threads to the same priority, the handler will only trigger once. However if priority is escalated to a high and then even higher priority, the handler may be invoked twice. + +Task escalation handlers are inherently racy, and may sometimes miss an escalation, for example if it happened immediately before the handler was installed, like this: + +```swift +// priority: low +// priority: high! +await withTaskPriorityEscalationHandler { + await work() +} onPriorityEscalated: { newPriority in // may not be triggered if ->high escalation happened before handler was installed + // do something +} +``` + +This is inherent to the nature of priority escalation and even with this behavior, we believe handlers are a worthy addition. One could also check for the `Task.currentPriority` and match it against our expectations inside the `operation` wrapped by the `withTaskPriorityEscalationHandler` if that could be useful to then perform the operation at an already _immediately_ heightened priority. + +Escalation handlers work with any existing task kind (child, unstructured, unstructured detached), and trigger at every level of the hierarchy in an "outside in" order: + +```swift +let t = Task { + await withTaskPriorityEscalationHandler { + await withTaskGroup { group in + group.addTask { + await withTaskPriorityEscalationHandler { + try? await Task.sleep(for: .seconds(1)) + } onPriorityEscalated: { newPriority in print("inner: \(newPriority)") } + } + } + } onPriorityEscalated: { newPriority in print("outer: \(newPriority)") } +} + +// escalate t -> high +// "outer: high" +// "inner: high" +``` + +The API can also be freely composed with `withTaskCancellationHandler` or there may even be multiple task escalation handlers registered on the same task (but in different pieces of the code). + +### Manually propagating priority escalation + +While generally developers should not rely on manual task escalation handling, this API also does introduce a manual way to escalate a task's priority. Primarily this should be used in combination with a task escalation handler to _propagate_ an escalation to an _unstructured task_ which otherwise would miss reacting to the escalation. + +The `escalatePriority` API is offered as a static method on `Task` in order to slightly hide it away from using it accidentally by stumbling upon it if it were directly declared as a member method of a Task. + +```swift +extension Task { + public static func escalatePriority(of task: Task, to newPriority: TaskPriority) +} + +extension UnsafeCurrentTask { + public static func escalatePriority(of task: UnsafeCurrentTask, to newPriority: TaskPriority) +} +``` + +It is possible to escalate both a `Task` and `UnsafeCurrentTask`, however great care must be taken to not attempt to escalate an unsafe task handle if the task has already been destroyed. The `Task` accepting API is always safe. + +Currently it is not possible to escalate a specific child task (created by `async let` or a task group) because those do not return task handles. We are interested in exposing task handles to child tasks in the future, and this design could then be easily amended to gain API to support such child task handles as well. + +## Source compatibility + +This proposal is purely additive, and does not cause any source compatibility issues. + +## ABI compatibility + +This proposal is purely ABI additive. + +## Alternatives considered + +### New Continuation APIs + +We did consider if offering a new kind of continuation might be easier to work with for developers. One shape this might take is: + +```swift +struct State { + var cc = CheckedContinuation? + var task: Task? +} +let C: Mutex + +await withCheckedContinuation2 { cc in + // ... + C.withLock { $0.cc = cc } + + let t = Task { + C.withLock { + $0.cc?.resume() // maybe we'd need to add 'tryResume' + } + } + C.withLock { $0.task = t } +} onCancel: { cc in + // remember the cc can only be resumed once; we'd need to offer 'tryResume' + cc.resume(throwing: CancellationError()) +} onPriorityEscalated: { cc, newPriority in + print("new priority: \(newPriority)") + C.withLock { Task.escalatePriority($0.task, to: newPriority) } +} +``` + +While at first this looks promising, we did not really remove much of the complexity -- careful locking is still necessary, and passing the continuation into the closures only makes it more error prone than not since it has become easier to accidentally multi-resume a continuation. This also does not compose well, and would only be offered around continuations, even if not all use-cases must necessarily suspend on a continuation to benefit from the priority escalation handling. + +Overall, this seems like a tightly knit API that changes current idioms of `with...Handler ` without really saving us from the inherent complexity of these handlers being invoked concurrently, and limiting the usefulness of those handlers to just "around a continuation" which may not always be the case. + +### Acknowledgements + +We'd like to thank John McCall, David Nadoba for their input on the APIs during early reviews. From 1dc6904e6b1ea5091111a7687344afae586e82bc Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 20 Feb 2025 18:43:34 -0500 Subject: [PATCH 4082/4563] Add review link to SE-0462 (#2702) --- proposals/0462-task-priority-escalation-apis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0462-task-priority-escalation-apis.md b/proposals/0462-task-priority-escalation-apis.md index a0ba3e47f1..aa2a883f17 100644 --- a/proposals/0462-task-priority-escalation-apis.md +++ b/proposals/0462-task-priority-escalation-apis.md @@ -5,7 +5,7 @@ * Review Manager: [Freddy Kellison-Linn](https://github.com/jumhyn) * Status: **Active review (February 20...March 2, 2025)** * Implementation: https://github.com/swiftlang/swift/pull/78625 -* Review: ([pitch](https://forums.swift.org/t/pitch-task-priority-escalation-apis/77702)) +* Review: ([pitch](https://forums.swift.org/t/pitch-task-priority-escalation-apis/77702)) ([review](https://forums.swift.org/t/se-0462-task-priority-escalation-apis/77997)) ## Introduction From 24b8954a946eb8e93f625ac9627326fe2e35d3f4 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Sat, 22 Feb 2025 23:53:44 +0900 Subject: [PATCH 4083/4563] [SE-0462] Add a few missing "of:" in code examples (#2704) --- proposals/0462-task-priority-escalation-apis.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0462-task-priority-escalation-apis.md b/proposals/0462-task-priority-escalation-apis.md index aa2a883f17..1f270b4150 100644 --- a/proposals/0462-task-priority-escalation-apis.md +++ b/proposals/0462-task-priority-escalation-apis.md @@ -93,7 +93,7 @@ await withTaskPriorityEscalationHandler { } // priority was escalated just before we stored the task in the mutex if let newPriority { - Task.escalatePriority(task, to: newPriority) + Task.escalatePriority(of: task, to: newPriority) } } onPriorityEscalated: { newPriority in state.withLock { state in @@ -102,7 +102,7 @@ await withTaskPriorityEscalationHandler { // priority was escalated just before we managed to store the task in the mutex state = .priority(newPriority) case .task(let task): - Task.escalatePriority(task, to: newPriority) + Task.escalatePriority(of: task, to: newPriority) } } } @@ -172,7 +172,7 @@ The API can also be freely composed with `withTaskCancellationHandler` or there While generally developers should not rely on manual task escalation handling, this API also does introduce a manual way to escalate a task's priority. Primarily this should be used in combination with a task escalation handler to _propagate_ an escalation to an _unstructured task_ which otherwise would miss reacting to the escalation. -The `escalatePriority` API is offered as a static method on `Task` in order to slightly hide it away from using it accidentally by stumbling upon it if it were directly declared as a member method of a Task. +The `escalatePriority(of:to:)` API is offered as a static method on `Task` in order to slightly hide it away from using it accidentally by stumbling upon it if it were directly declared as a member method of a Task. ```swift extension Task { @@ -224,7 +224,7 @@ await withCheckedContinuation2 { cc in cc.resume(throwing: CancellationError()) } onPriorityEscalated: { cc, newPriority in print("new priority: \(newPriority)") - C.withLock { Task.escalatePriority($0.task, to: newPriority) } + C.withLock { Task.escalatePriority(of: $0.task, to: newPriority) } } ``` @@ -234,4 +234,4 @@ Overall, this seems like a tightly knit API that changes current idioms of `with ### Acknowledgements -We'd like to thank John McCall, David Nadoba for their input on the APIs during early reviews. +I'd like to thank John McCall, David Nadoba for their input on the APIs during early reviews. From f9000ffb95c6f7a309ab03ee7bf937639b3924e5 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sat, 22 Feb 2025 10:22:33 -0800 Subject: [PATCH 4084/4563] [SE-0461] Proposal clarifications based on early review discussion. (#2703) * [SE-0461] Proposal clarifications based on early review discussion. * [SE-0461] Fix the vision document link. --- proposals/0461-async-function-isolation.md | 51 +++++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md index cdece7a616..86655481cf 100644 --- a/proposals/0461-async-function-isolation.md +++ b/proposals/0461-async-function-isolation.md @@ -4,7 +4,7 @@ * Authors: [Holly Borla](https://github.com/hborla), [John McCall](https://github.com/rjmccall) * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Active review (February 20...March 2, 2025)** -* Vision: [[Prospective Vision] Improving the approachability of data-race safety](https://forums.swift.org/t/prospective-vision-improving-the-approachability-of-data-race-safety/76183) +* Vision: [Improving the approachability of data-race safety](/visions/approachable-concurrency.md) * Implementation: On `main` behind `-enable-experimental-feature NonIsolatedAsyncInheritsIsolationFromContext` * Upcoming Feature Flag: `AsyncCallerExecution` * Previous Proposal: [SE-0338](0338-clarify-execution-non-actor-async.md) @@ -41,6 +41,8 @@ async function always switches off of an actor to run. - [Alternatives considered](#alternatives-considered) - [Changing isolation inference behavior to implicitly capture isolated parameters](#changing-isolation-inference-behavior-to-implicitly-capture-isolated-parameters) - [Use `nonisolated` instead of a separate `@execution(concurrent)` attribute](#use-nonisolated-instead-of-a-separate-executionconcurrent-attribute) + - [Use "isolation" terminology instead of "execution"](#use-isolation-terminology-instead-of-execution) + - [Deprecate `nonisolated`](#deprecate-nonisolated) - [Don't introduce a type attribute for `@execution`](#dont-introduce-a-type-attribute-for-execution) - [Revisions](#revisions) @@ -241,7 +243,7 @@ The sections below will explicitly use `@execution(concurrent)` and independent of upcoming features or language modes. However, note that the end state under the `AsyncCallerExecution` upcoming feature will mean that `@execution(caller)` is not necessary to explicitly write, and -`@execution(caller)` will likely be used sparingly because it has far +`@execution(concurrent)` will likely be used sparingly because it has far stricter data-race safety requirements. ### The `@execution` attribute @@ -259,8 +261,26 @@ in the following sections. Only (implicitly or explicitly) `nonisolated` functions can be marked with the `@execution` attribute; it is an error to use the `@execution` attribute with an isolation other than `nonisolated`, including global actors, isolated -parameters, and `@isolated(any)`. The `@execution` attribute can be used -together with `@Sendable` or `sending`. +parameters, and `@isolated(any)`: + +```swift +actor MyActor { + var value = 0 + + // error: '@execution(caller)' can only be used with 'nonisolated' methods + @execution(caller) + func isolatedToSelf() async { + value += 1 + } + + @execution(caller) + nonisolated func canRunAnywhere() async { + // cannot access 'value' or other actor-isolated state + } +} +``` + +The `@execution` attribute can be used together with `@Sendable` or `sending`. The `@execution` attribute is preserved in the type system so that the execution semantics can be distinguished for function vales. @@ -892,12 +912,15 @@ reasons: ### Use "isolation" terminology instead of "execution" -One other possibility is to use isolation terminology instead of `@execution` -for the syntax. This direction does not accomplish goal 1. in the previous +Another possibility is to use isolation terminology instead of `@execution` +for the syntax. This direction does not accomplish the goal of having a section to have a consistent meaning for `nonisolated` across synchronous and async functions. If the attribute were spelled `@isolated(caller)` and `@isolated(concurrent)`, presumably that attribute would not work together with `nonisolated`; it would instead be an alternative kind of actor isolation. +`@isolated(concurrent)` also doesn't make much sense because the concurrent +executor does not provide isolation at all - isolation is only provided by +actors and tasks. Having `@execution(caller)` as an attribute that is used together with `nonisolated` leads to a simpler programming model because after the upcoming @@ -907,6 +930,22 @@ functions. If we choose a different form of isolation like `@isolated(caller)`, programmers have to learn a separate syntax for `async` functions that accomplishes the same effect as a `nonisolated` synchronous function. +### Deprecate `nonisolated` + +Going in the oppose direction, this proposal could effectively deprecate +`nonisolated` and allow you to use `@execution(caller)` everywhere that +`nonisolated` is currently supported, including synchronous methods, stored +properties, type declarations, and extensions. This direction was not chosen +for the following reasons: + +1. This would lead to much more code churn than the current proposal. Part of + the goal of this proposal is to minimize the change to only what is absolutely + necessary to solve the major usability problem with async functions on + non-`Sendable` types, because it's painful both to transition code and to + re-learn parts of the model that have already been internalized. +2. `nonisolated` is nicer to write than `@execution(caller)`, + `@isolated(caller)`, or any other alternative attribute + argument syntax. + ### Don't introduce a type attribute for `@execution` There are a lot of existing type attributes for concurrency and it's From 07baa567d2fb40e7e95bd9aebbaac3beec19ece3 Mon Sep 17 00:00:00 2001 From: tokizuoh Date: Sun, 23 Feb 2025 15:58:56 +0900 Subject: [PATCH 4085/4563] Replace newlines with `
    ` in Mermaid flowchart --- process.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/process.md b/process.md index 241b684b78..d38eeec80e 100644 --- a/process.md +++ b/process.md @@ -162,13 +162,13 @@ flowchart LR %% %% Nodes: - 1{{"Awaiting\nreview"}} - 2{{"Scheduled\nfor review"}} - 3{"Active\nreview"} - 4["Returned\nfor revision"] + 1{{"Awaiting
    review"}} + 2{{"Scheduled
    for review"}} + 3{"Active
    review"} + 4["Returned
    for revision"] 5(["Withdrawn"]) 6(["Rejected"]) - 7_8["Accepted\n(with revisions)"] + 7_8["Accepted
    (with revisions)"] 9[["Previewing"]] 10(["Implemented"]) From ed0867b58af8190b43ce24b7170e9bcb0c762cae Mon Sep 17 00:00:00 2001 From: Joseph Heck Date: Mon, 24 Feb 2025 08:36:53 -0800 Subject: [PATCH 4086/4563] Update 0371-isolated-synchronous-deinit.md (#2707) Per forum thread https://forums.swift.org/t/isolated-deinit-not-in-swift-6-1/78055, the deinit is in Swift 6.2, didn't make it into the cut for Swift 6.1 --- proposals/0371-isolated-synchronous-deinit.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0371-isolated-synchronous-deinit.md b/proposals/0371-isolated-synchronous-deinit.md index d860b1178f..00b88aca31 100644 --- a/proposals/0371-isolated-synchronous-deinit.md +++ b/proposals/0371-isolated-synchronous-deinit.md @@ -3,7 +3,7 @@ * Proposal: [SE-0371](0371-isolated-synchronous-deinit.md) * Author: [Mykola Pokhylets](https://github.com/nickolas-pohilets) * Review Manager: [Frederick Kellison-Linn](https://github.com/jumhyn) -* Status: **Implemented (Swift 6.1)** +* Status: **Implemented (Swift 6.2)** * Implementation: [apple/swift#60057](https://github.com/apple/swift/pull/60057) * Review: ([first pitch](https://forums.swift.org/t/isolated-synchronous-deinit/58177)) ([first review](https://forums.swift.org/t/se-0371-isolated-synchronous-deinit/59754)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0371-isolated-synchronous-deinit/60060)) ([discussion](https://forums.swift.org/t/isolated-synchronous-deinit-2/62565)) ([second pitch](https://forums.swift.org/t/pitch-2-se-0371-isolated-async-deinit/64836)) ([sub-pitch](https://forums.swift.org/t/sub-pitch-task-local-values-in-isolated-synchronous-deinit-and-async-deinit/70060)) ([second review](https://forums.swift.org/t/second-review-se-0371-isolated-synchronous-deinit/73406)) ([accepted with modifications](https://forums.swift.org/t/accepted-with-modifications-se-0371-isolated-synchronous-deinit/74042)) From ea5b8fa5236cff0a990932e9d4106ee0353d880d Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 24 Feb 2025 18:44:09 -0500 Subject: [PATCH 4087/4563] Slight editorial fixes --- proposals/0456-stdlib-span-properties.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0456-stdlib-span-properties.md b/proposals/0456-stdlib-span-properties.md index aaeab8c600..178da8f82b 100644 --- a/proposals/0456-stdlib-span-properties.md +++ b/proposals/0456-stdlib-span-properties.md @@ -6,10 +6,9 @@ * Status: **Accepted** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: [PR #78561](https://github.com/swiftlang/swift/pull/78561) -* Review: [Pitch](https://forums.swift.org/t/76138), [Review](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233), [Acceptance](https://forums.swift.org/t/77684) +* Review: ([pitch](https://forums.swift.org/t/76138)) ([review](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233)) ([acceptance](https://forums.swift.org/t/77684)) [SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md - [SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md [PR-2305]: https://github.com/swiftlang/swift-evolution/pull/2305 [SE-0453]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0453-vector.md From c023f344af885734bdbe853d489647882e1ebd93 Mon Sep 17 00:00:00 2001 From: shiz <35151927+stzn@users.noreply.github.com> Date: Tue, 25 Feb 2025 08:50:48 +0900 Subject: [PATCH 4088/4563] Fix an invalid syntax (#2706) --- proposals/0461-async-function-isolation.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md index 86655481cf..aecc52bea4 100644 --- a/proposals/0461-async-function-isolation.md +++ b/proposals/0461-async-function-isolation.md @@ -327,8 +327,6 @@ The type of an `@execution(caller)` function declaration is an ```swift class NotSendable { ... } -func useAsValue(_ ns: NotSendable) async { ... } - @MainActor let global: NotSendable = .init() @execution(caller) @@ -344,7 +342,7 @@ func callSendableClosure() async { await closure(global) // okay } -callSendableClosure(useAsValue) +callSendableClosure() ``` In the above code, the calls to `closure` from `callSendableClosure` run on the From f30047248257f74ac8a4aae61a6dd5c04ef3b519 Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Tue, 25 Feb 2025 01:46:13 +0000 Subject: [PATCH 4089/4563] replace term RLP with EDSL --- ...environment-dependent-shared-libraries.md} | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) rename proposals/{NNNN-replaceable-library-plugins.md => NNNN-environment-dependent-shared-libraries.md} (63%) diff --git a/proposals/NNNN-replaceable-library-plugins.md b/proposals/NNNN-environment-dependent-shared-libraries.md similarity index 63% rename from proposals/NNNN-replaceable-library-plugins.md rename to proposals/NNNN-environment-dependent-shared-libraries.md index 35f2d9e1c3..e7a0569ab5 100644 --- a/proposals/NNNN-replaceable-library-plugins.md +++ b/proposals/NNNN-environment-dependent-shared-libraries.md @@ -1,6 +1,6 @@ -# Replaceable Library Plugins +# Environment Dependent Shared Libraries -* Proposal: [SE-NNNN](NNNN-replaceable-library-plugins.md) +* Proposal: [SE-NNNN](NNNN-environment-dependent-shared-libraries.md) * Authors: [tayloraswift](https://github.com/tayloraswift) * Review Manager: TBD * Implementation: [swiftlang/swift-package-manager#8249](https://github.com/swiftlang/swift-package-manager/pull/8249) @@ -8,7 +8,7 @@ ## Introduction -SwiftPM currently has no support for non-system binary library dependencies on Linux. This proposal adds support for **Replaceable Library Plugins**, which are a type of dynamic library that is shared across a fleet of machines and can be upgraded without recompiling and redeploying all applications running on those machines. We will distribute Replaceable Library Plugins through the existing `.artifactbundle` format. +SwiftPM currently has no support for non-system binary library dependencies on Linux. This proposal adds support for **Environment Dependent Shared Libraries**, which are a type of dynamic library that is shared across a fleet of machines and can be upgraded without recompiling and redeploying all applications running on those machines. We will distribute Environment Dependent Shared Libraries through the existing `.artifactbundle` format. Swift-evolution thread: [Discussion thread](https://forums.swift.org/t/pitch-replaceable-library-plugins/77605) @@ -29,13 +29,13 @@ While macOS has Dynamic Library support through XCFrameworks, on Linux we curren On Linux, there are a lot of obstacles to having fully general support for Dynamic Libraries. Swift is not ABI stable on Linux, and Linux itself is not a single platform but a wide range of similar platforms that provide few binary compatibility guarantees. This means it is pretty much impossible for a public Swift library to vend precompiled binaries that will Just Work for everyone, and we are not going to try to solve that problem in this proposal. -Instead, we will focus on **Replaceable Library Plugins** (RLPs). We choose this term to emphasize the distinction between our use case and fully general Dynamic Libraries. +Instead, we will focus on **Environment Dependent Shared Libraries** (EDSLs). We choose this term to emphasize the distinction between our use case and fully general Dynamic Libraries. ### Organization-Defined Platforms (ODPs) -Unlike fully general Dynamic Libraries, you would distribute Replaceable Library Plugins strictly for internal consumption within an organization, or to a small set of paying clients. +Unlike fully general Dynamic Libraries, you would distribute Environment Dependent Shared Libraries strictly for internal consumption within an organization, or to a small set of paying clients. -The organization that distributes an RLP is responsible for defining what exactly constitutes a “platform” for their purposes. An Organization-Defined Platform (ODP) is not necessarily an operating system or architecture, or even a specific distribution of an operating system. A trivial example of two ODPs might be: +The organization that distributes an EDSL is responsible for defining what exactly constitutes a “platform” for their purposes. An Organization-Defined Platform (ODP) is not necessarily an operating system or architecture, or even a specific distribution of an operating system. A trivial example of two ODPs might be: 1. Ubuntu 24.04 with the Swift 6.0.3 runtime installed at `/home/ubuntu/swift` 2. Ubuntu 24.04 with the Swift 6.0.3 runtime installed at `/home/ubuntu/swift-runtime` @@ -44,13 +44,13 @@ Concepts like Platform Triples are not sufficient to describe an ODP. Even thoug Organizations add and remove ODPs as needed, and trying to define a global registry of all possible ODPs is a non-goal. -To keep things simple, we identify ODPs by the URL of the Artifact Bundle that contains the RLP. +To keep things simple, we identify ODPs by the URL of the Artifact Bundle that contains the EDSL. -### Creating RLPs +### Creating EDSLs -To compile an RLP, you just need to build an ordinary SwiftPM library product with the `-enable-library-evolution` flag. This requires no modifications to SwiftPM. +To compile an EDSL, you just need to build an ordinary SwiftPM library product with the `-enable-library-evolution` flag. This requires no modifications to SwiftPM. -You would package an RLP as an `.artifactbundle` just as you would an executable, with the following differences: +You would package an EDSL as an `.artifactbundle` just as you would an executable, with the following differences: - The `info.json` must have `schemaVersion` set to `1.2` or higher. - The artifact type must be `library`, a new enum case introduced in this proposal. @@ -59,21 +59,21 @@ You would package an RLP as an `.artifactbundle` just as you would an executable Because SwiftPM is not (and cannot be) aware of a particular organization’s ODPs, this enforces the requirement that each ODP must have its own Artifact Bundle. -The organization that distributes the RLP is responsible for upholding ABI stability guarantees, including the exact Swift compiler and runtime versions needed to safely consume the RLP. +The organization that distributes the EDSL is responsible for upholding ABI stability guarantees, including the exact Swift compiler and runtime versions needed to safely consume the EDSL. -### Consuming RLPs +### Consuming EDSLs -To consume an RLP, you would add a `binaryTarget` to your `Package.swift` manifest, just as you would for an executable. Because ODPs are identified by the URL of the Artifact Bundle, there are no new fields in the `PackageDescription` API. +To consume an EDSL, you would add a `binaryTarget` to your `Package.swift` manifest, just as you would for an executable. Because ODPs are identified by the URL of the Artifact Bundle, there are no new fields in the `PackageDescription` API. -We expect that the logic for selecting the correct RLP for a given ODP would live within the `Package.swift` file, that it would be highly organization-specific, and that it would be manipulated using existing means such as environment variables. +We expect that the logic for selecting the correct EDSL for a given ODP would live within the `Package.swift` file, that it would be highly organization-specific, and that it would be manipulated using existing means such as environment variables. -### Deploying RLPs +### Deploying EDSLs -Deploying RLPs does not involve SwiftPM or Artifact Bundles at all. You would deploy an RLP by copying the latest binaries to the appropriate `@rpath` location on each machine in your fleet. The `@rpath` location is part of the ODP definition, and is not modeled by SwiftPM. +Deploying EDSLs does not involve SwiftPM or Artifact Bundles at all. You would deploy an EDSL by copying the latest binaries to the appropriate `@rpath` location on each machine in your fleet. The `@rpath` location is part of the ODP definition, and is not modeled by SwiftPM. -Some organizations might choose to forgo the `@rpath` mechanism entirely and simply install the RLPs in a system-wide location. +Some organizations might choose to forgo the `@rpath` mechanism entirely and simply install the EDSLs in a system-wide location. ## Detailed design @@ -110,7 +110,7 @@ Below is an example of an `info.json` file for an Artifact Bundle containing a s } ``` -The artifact must have exactly one variant in the `variants` list, and the `supportedTriples` field is forbidden. An RLP Artifact Bundle can contain multiple libraries at the top level. +The artifact must have exactly one variant in the `variants` list, and the `supportedTriples` field is forbidden. An EDSL Artifact Bundle can contain multiple libraries at the top level. Below is an example of the layout of an Artifact Bundle containing a single library called `MyLibrary`. Only the `info.json` must appear at the root of the Artifact Bundle; all other files can appear at whatever paths are defined in the `info.json`, as long as they are within the Artifact Bundle. @@ -122,12 +122,12 @@ Below is an example of the layout of an Artifact Bundle containing a single libr 📝 info.json ``` -A macOS Artifact Bundle would contain a `.dylib` instead of a `.so`. RLPs will be supported on macOS, although we expect this will be an exceedingly rare use case. +A macOS Artifact Bundle would contain a `.dylib` instead of a `.so`. EDSLs will be supported on macOS, although we expect this will be an exceedingly rare use case. ## Security -RLPs are not intended for public distribution, and are not subject to the same security concerns as public libraries. Organizations that distribute RLPs are responsible for ensuring that the RLPs are safe to consume. +EDSLs are not intended for public distribution, and are not subject to the same security concerns as public libraries. Organizations that distribute EDSLs are responsible for ensuring that the EDSLs are safe to consume. ## Impact on existing packages @@ -143,11 +143,11 @@ SwiftPM currently uses Platform Triples to select among artifact variants when c We could extend Platform Triples to model ODPs, but this would privilege a narrow set of predefined deployment architectures, and if you wanted to add a new ODP, you would have to modify SwiftPM to teach it to recognize the new ODP. -### Supporting multiple variants of an RLP in the same Artifact Bundle +### Supporting multiple variants of an EDSL in the same Artifact Bundle -We could allow an Artifact Bundle to contain multiple variants of an RLP, but we would still need to support a way to identify those variants, which in practice makes SwiftPM aware of ODPs. +We could allow an Artifact Bundle to contain multiple variants of an EDSL, but we would still need to support a way to identify those variants, which in practice makes SwiftPM aware of ODPs. -We also don’t see much value in this feature, as you would probably package and upload RLPs using one CI/CD workflow per ODP anyway. Combining artifacts would require some kind of synchronization mechanism to await all pipelines before fetching and merging bundles. +We also don’t see much value in this feature, as you would probably package and upload EDSLs using one CI/CD workflow per ODP anyway. Combining artifacts would require some kind of synchronization mechanism to await all pipelines before fetching and merging bundles. One benefit of merging bundles would be that it reduces the number of checksums you need to keep track of, but we expect that most organizations will have a very small number of ODPs, with new ODPs continously phasing out old ODPs. From 09b508acf14bf56f0e3a35f170bf1a320b6afdc5 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Tue, 25 Feb 2025 10:37:39 -0700 Subject: [PATCH 4090/4563] Update UTF8Span --- .../nnnn-utf8span-safe-utf8-processing.md | 347 ++++++++---------- 1 file changed, 148 insertions(+), 199 deletions(-) diff --git a/proposals/nnnn-utf8span-safe-utf8-processing.md b/proposals/nnnn-utf8span-safe-utf8-processing.md index e7984007cb..30a7816eac 100644 --- a/proposals/nnnn-utf8span-safe-utf8-processing.md +++ b/proposals/nnnn-utf8span-safe-utf8-processing.md @@ -1,5 +1,3 @@ - - # UTF8Span: Safe UTF-8 Processing Over Contiguous Bytes * Proposal: [SE-NNNN](nnnn-utf8-span.md) @@ -24,7 +22,7 @@ Currently, if a developer wants to do `String`-like processing over UTF-8 bytes, For example, if these bytes were part of a data structure, the developer would need to decide to either cache such a new `String` instance or recreate it on the fly. Caching more than doubles the size and adds caching complexity. Recreating it on the fly adds a linear time factor and class instance allocation/deallocation and potentially reference counting. -Furthermore, `String` may not be fully available on tightly constrained platforms, especially those that cannot support allocations. Both `String` and `UTF8Span` have some API that require Unicode data tables and that might not be available on embedded (String via its conformance to `Comparable` and `Collection` depend on these data tables while `UTF8Span` has a couple of methods that will be unavailable). +Furthermore, `String` may not be available on tightly constrained platforms, such as those that cannot support allocations. Both `String` and `UTF8Span` have some API that require Unicode data tables and that might not be available on embedded (String via its conformance to `Comparable` and `Collection` depend on these data tables while `UTF8Span` has a couple of methods that will be unavailable). ### UTF-8 validity and efficiency @@ -46,17 +44,17 @@ We propose a non-escapable `UTF8Span` which exposes `String` functionality for v ```swift @frozen public struct UTF8Span: Copyable, ~Escapable { - **TODO**: This might end up being UnsafeRawPointer? like in Span - public var unsafeBaseAddress: UnsafeRawPointer + @usableFromInline + internal var _unsafeBaseAddress: UnsafeRawPointer? /* A bit-packed count and flags (such as isASCII) - ╔═══════╦═════╦═════╦══════════╦═══════╗ - ║ b63 ║ b62 ║ b61 ║ b60:56 ║ b56:0 ║ - ╠═══════╬═════╬═════╬══════════╬═══════╣ - ║ ASCII ║ NFC ║ SSC ║ reserved ║ count ║ - ╚═══════╩═════╩═════╩══════════╩═══════╝ + ╔═══════╦═════╦══════════╦═══════╗ + ║ b63 ║ b62 ║ b61:56 ║ b56:0 ║ + ╠═══════╬═════╬══════════╬═══════╣ + ║ ASCII ║ NFC ║ reserved ║ count ║ + ╚═══════╩═════╩══════════╩═══════╝ ASCII means the contents are all-ASCII (<0x7F). NFC means contents are in normal form C for fast comparisons. @@ -69,21 +67,6 @@ public struct UTF8Span: Copyable, ~Escapable { ``` - - ### UTF-8 validation We propose new API for identifying where and what kind of encoding errors are present in UTF-8 content. @@ -191,13 +174,11 @@ extension Unicode.UTF8 { /// The range of offsets into our input containing the error public var range: Range - @_alwaysEmitIntoClient public init( _ kind: Unicode.UTF8.EncodingError.Kind, _ range: some RangeExpression ) - @_alwaysEmitIntoClient public init(_ kind: Unicode.UTF8.EncodingError.Kind, at: Int) } } @@ -208,59 +189,35 @@ extension UTF8.EncodingError { public struct Kind: Error, Sendable, Hashable, Codable, RawRepresentable { public var rawValue: UInt8 - @inlinable public init(rawValue: UInt8) /// A continuation byte (`10xxxxxx`) outside of a multi-byte sequence - @_alwaysEmitIntoClient public static var unexpectedContinuationByte: Self /// A byte in a surrogate code point (`U+D800..U+DFFF`) sequence - @_alwaysEmitIntoClient public static var surrogateCodePointByte: Self /// A byte in an invalid, non-surrogate code point (`>U+10FFFF`) sequence - @_alwaysEmitIntoClient public static var invalidNonSurrogateCodePointByte: Self /// A byte in an overlong encoding sequence - @_alwaysEmitIntoClient public static var overlongEncodingByte: Self /// A multi-byte sequence that is the start of a valid multi-byte scalar /// but is cut off before ending correctly - @_alwaysEmitIntoClient public static var truncatedScalar: Self } } -@_unavailableInEmbedded extension UTF8.EncodingError.Kind: CustomStringConvertible { public var description: String { get } } -@_unavailableInEmbedded extension UTF8.EncodingError: CustomStringConvertible { public var description: String { get } } ``` -**QUESTION**: It would be good to expose this functionality via a general purpose validation API. Question is do we want a `findFirstError` or `findAllErrors` style API, both? E.g.: - -```swift -extension UTF8 { - public static func checkForError( - _ s: some Sequence - ) -> some UTF8.EncodingError { - - ... or - - public static func checkForAllErrors( - _ s: some Sequence - ) -> some Sequence { -``` - - ### Creation and validation `UTF8Span` is validated at initialization time and encoding errors are diagnosed and thrown. @@ -269,37 +226,34 @@ extension UTF8 { ```swift extension UTF8Span { - @lifetime(codeUnits) - public init( - _validating codeUnits: consuming Span - ) throws(UTF8.EncodingError) { - - @lifetime(borrow start) - internal init( - _unsafeAssumingValidUTF8 start: borrowing UnsafeRawPointer, - _countAndFlags: UInt64 - ) + /// Creates a UTF8Span containing `codeUnits`. Validates that the input is + /// valid UTF-8, otherwise throws an error. + /// + /// The resulting UTF8Span has the same lifetime constraints as `codeUnits`. + public init(validating codeUnits: Span) throws(UTF8.EncodingError) } ``` -**NOTE**: The final status of underscores, annotations, etc., are pending things like [SE-0456](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233/17) and [Lifetime Dependencies](https://forums.swift.org/t/pitch-non-escapable-types-and-lifetime-dependency/69865/140). - - ### Scalar processing -We propose a `UTF8Span.ScalarIterator` type that can do scalar processing forwards and backwards. Note that `ScalarIterator` itself is non-escapable, and thus cannot conform to `IteratorProtocol`, etc. +We propose a `UTF8Span.UnicodeScalarIterator` type that can do scalar processing forwards and backwards. Note that `UnicodeScalarIterator` itself is non-escapable, and thus cannot conform to `IteratorProtocol`, etc. ```swift extension UTF8Span { - public func _makeScalarIterator() -> ScalarIterator + /// Returns an iterator that will decode the code units into + /// `Unicode.Scalar`s. + /// + /// The resulting iterator has the same lifetime constraints as `self`. + public func makeUnicodeScalarIterator() -> UnicodeScalarIterator - /// Iterate the `Unicode.Scalar`s contents of a `UTF8Span`. - public struct ScalarIterator: ~Escapable { - public var codeUnits: UTF8Span + /// Iterate the `Unicode.Scalar`s contents of a `UTF8Span`. + @frozen + public struct UnicodeScalarIterator: ~Escapable { + public let codeUnits: UTF8Span /// The byte offset of the start of the next scalar. This is /// always scalar-aligned. - public var currentCodeUnitOffset: Int { get } + public var currentCodeUnitOffset: Int { get private(set) } public init(_ codeUnits: UTF8Span) @@ -348,17 +302,13 @@ extension UTF8Span { public mutating func skipBack(by n: Int) -> Bool /// Reset to the nearest scalar-aligned code unit offset `<= i`. - /// - /// **TODO**: Example public mutating func reset(roundingBackwardsFrom i: Int) /// Reset to the nearest scalar-aligned code unit offset `>= i`. - /// - /// **TODO**: Example public mutating func reset(roundingForwardsFrom i: Int) /// Reset this iterator to code unit offset `i`, skipping _all_ safety - /// checks. + /// checks (including bounds checks). /// /// Note: This is only for very specific, low-level use cases. If /// `codeUnitOffset` is not properly scalar-aligned, this function can @@ -371,18 +321,20 @@ extension UTF8Span { /// Returns the UTF8Span containing all the content up to the iterator's /// current position. - public func _prefix() -> UTF8Span + /// + /// The resultant `UTF8Span` has the same lifetime constraints as `self`. + public func prefix() -> UTF8Span /// Returns the UTF8Span containing all the content after the iterator's /// current position. - public func _suffix() -> UTF8Span + /// + /// The resultant `UTF8Span` has the same lifetime constraints as `self`. + public func suffix() -> UTF8Span } } ``` -**QUESTION**: Is it worth also surfacing as `isScalarAligned` API on `UTF8Span` so it's a little easier to find and spell (as well as talk about in doc comments)? - ### Character processing @@ -392,39 +344,22 @@ The `CharacterIterator` assumes that the start and end of the `UTF8Span` is the Any scalar-aligned position is a valid place to start or reset the grapheme-breaking algorithm to, though you could get different `Character` output if if resetting to a position that isn't `Character`-aligned relative to the start of the `UTF8Span` (e.g. in the middle of a series of regional indicators). - - ```swift -@_unavailableInEmbedded + extension UTF8Span { - public func _makeCharacterIterator() -> CharacterIterator + /// Returns an iterator that will construct `Character`s from the underlying + /// UTF-8 content. + /// + /// The resulting iterator has the same lifetime constraints as `self`. + public func makeCharacterIterator() -> CharacterIterator /// Iterate the `Character` contents of a `UTF8Span`. public struct CharacterIterator: ~Escapable { - public var codeUnits: UTF8Span + public let codeUnits: UTF8Span /// The byte offset of the start of the next `Character`. This is /// always scalar-aligned and `Character`-aligned. - public var currentCodeUnitOffset: Int { get } + public var currentCodeUnitOffset: Int { get private(set) } public init(_ span: UTF8Span) @@ -449,28 +384,28 @@ extension UTF8Span { /// /// Returns the number of `Character`s skipped over, which can be 0 /// if at the end of the UTF8Span. - public mutating func skipForward() + public mutating func skipForward() -> Int /// Advance `codeUnitOffset` to the end of `n` `Characters`, without /// constructing them. /// /// Returns the number of `Character`s skipped over, which can be /// fewer than `n` if at the end of the UTF8Span. - public mutating func skipForward(by n: Int) + public mutating func skipForward(by n: Int) -> Int /// Move `codeUnitOffset` to the start of the previous `Character`, /// without constructing it. /// /// Returns the number of `Character`s skipped over, which can be 0 /// if at the start of the UTF8Span. - public mutating func skipBack() + public mutating func skipBack() -> Int /// Move `codeUnitOffset` to the start of the previous `n` `Character`s, /// without constructing them. /// /// Returns the number of `Character`s skipped over, which can be /// fewer than `n` if at the start of the UTF8Span. - public mutating func skipBack(by n: Int) + public mutating func skipBack(by n: Int) -> Int /// Reset to the nearest character-aligned position `<= i`. public mutating func reset(roundingBackwardsFrom i: Int) @@ -479,7 +414,7 @@ extension UTF8Span { public mutating func reset(roundingForwardsFrom i: Int) /// Reset this iterator to code unit offset `i`, skipping _all_ safety - /// checks. + /// checks (including bounds checks). /// /// Note: This is only for very specific, low-level use cases. If /// `codeUnitOffset` is not properly scalar-aligned, this function can @@ -495,13 +430,16 @@ extension UTF8Span { /// Returns the UTF8Span containing all the content up to the iterator's /// current position. + /// + /// The resultant `UTF8Span` has the same lifetime constraints as `self`. public func prefix() -> UTF8Span /// Returns the UTF8Span containing all the content after the iterator's /// current position. + /// + /// The resultant `UTF8Span` has the same lifetime constraints as `self`. public func suffix() -> UTF8Span } - } ``` @@ -512,23 +450,18 @@ The content of a `UTF8Span` can be compared in a number of ways, including liter ```swift extension UTF8Span { /// Whether this span has the same bytes as `other`. - @_alwaysEmitIntoClient public func bytesEqual(to other: UTF8Span) -> Bool /// Whether this span has the same bytes as `other`. - @_alwaysEmitIntoClient public func bytesEqual(to other: some Sequence) -> Bool /// Whether this span has the same `Unicode.Scalar`s as `other`. - @_alwaysEmitIntoClient public func scalarsEqual( to other: some Sequence ) -> Bool /// Whether this span has the same `Character`s as `other`, using /// `Character.==` (i.e. Unicode canonical equivalence). - @_unavailableInEmbedded - @_alwaysEmitIntoClient public func charactersEqual( to other: some Sequence ) -> Bool @@ -551,14 +484,12 @@ extension UTF8Span { extension UTF8Span { /// Whether `self` is equivalent to `other` under Unicode Canonical /// Equivalence. - @_unavailableInEmbedded public func isCanonicallyEquivalent( to other: UTF8Span ) -> Bool /// Whether `self` orders less than `other` under Unicode Canonical /// Equivalence using normalized code-unit order (in NFC). - @_unavailableInEmbedded public func isCanonicallyLessThan( _ other: UTF8Span ) -> Bool @@ -575,15 +506,27 @@ Slicing a `UTF8Span` is nuanced and depends on the caller's desired use. They ca ```swift extension UTF8Span { - /// Returns whether the validated contents were all-ASCII. This is checked at - /// initialization time and remembered. - @inlinable - public var isASCII: Bool { get } + /// Returns whether contents are known to be all-ASCII. A return value of + /// `true` means that all code units are ASCII. A return value of `false` + /// means there _may_ be non-ASCII content. + /// + /// ASCII-ness is checked and remembered during UTF-8 validation, so this + /// is often equivalent to is-ASCII, but there are some situations where + /// we might return `false` even when the content happens to be all-ASCII. + /// + /// For example, a UTF-8 span generated from a `String` that at some point + /// contained non-ASCII content would report false for `isKnownASCII`, even + /// if that String had subsequent mutation operations that removed any + /// non-ASCII content. + public var isKnownASCII: Bool { get } + + /// Do a scan checking for whether the contents are all-ASCII. + /// + /// Updates the `isKnownASCII` bit if contents are all-ASCII. + public mutating func checkForASCII() -> Bool /// Returns whether the contents are known to be NFC. This is not /// always checked at initialization time and is set by `checkForNFC`. - @inlinable - @_unavailableInEmbedded public var isKnownNFC: Bool { get } /// Do a scan checking for whether the contents are in Normal Form C. @@ -595,41 +538,24 @@ extension UTF8Span { /// algorithm. However, it cannot detect all NFC contents. /// /// Updates the `isKnownNFC` bit. - @_unavailableInEmbedded public mutating func checkForNFC( quickCheck: Bool ) -> Bool - - /// Returns whether every `Character` (i.e. grapheme cluster) - /// is known to be comprised of a single `Unicode.Scalar`. - /// - /// This is not always checked at initialization time. It is set by - /// `checkForSingleScalarCharacters`. - @_unavailableInEmbedded - @inlinable - public var isKnownSingleScalarCharacters: Bool { get } - - /// Do a scan, checking whether every `Character` (i.e. grapheme cluster) - /// is comprised of only a single `Unicode.Scalar`. When a span contains - /// only single-scalar characters, character operations are much faster. - /// - /// `quickCheck` will check for a subset of single-scalar character contents - /// using a faster algorithm than the full grapheme breaking algorithm. - /// However, it cannot detect all single-scalar `Character` contents. - /// - /// Updates the `isKnownSingleScalarCharacters` bit. - @_unavailableInEmbedded - public mutating func checkForSingleScalarCharacters( - quickCheck: Bool - ) -> Bool } ``` -**QUESTION**: There is an even quicker quick-check for NFC (checking if all scalars are `<0x300`, which covers extended latin characters). Should we expose that level as well? - ### `UTF8Span` from `String` -We will add `utf8Span`-style properties to `String` and `Substring`, in line with however [SE-0456](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233/17) turns out. +We propose adding `utf8Span` properties to `String` and `Substring`, in line with [SE-0456](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233/17): + +```swift +extension String { + public var utf8Span: UTF8Span { borrowing get } +} +extension Substring { + public var utf8Span: UTF8Span { borrowing get } +} +``` ### `Span`-like functionality @@ -638,37 +564,12 @@ A `UTF8Span` is similar to a `Span`, but with the valid-UTF8 invariant an ``` extension UTF8Span { - @_alwaysEmitIntoClient public var isEmpty: Bool { get } - @_alwaysEmitIntoClient - public var storage: Span { get } - - /// Calls a closure with a pointer to the viewed contiguous storage. - /// - /// The buffer pointer passed as an argument to `body` is valid only - /// during the execution of `withUnsafeBufferPointer(_:)`. - /// Do not store or return the pointer for later use. - /// - /// - Parameter body: A closure with an `UnsafeBufferPointer` parameter - /// that points to the viewed contiguous storage. If `body` has - /// a return value, that value is also used as the return value - /// for the `withUnsafeBufferPointer(_:)` method. The closure's - /// parameter is valid only for the duration of its execution. - /// - Returns: The return value of the `body` closure parameter. - @_alwaysEmitIntoClient - borrowing public func withUnsafeBufferPointer< - E: Error, Result: ~Copyable & ~Escapable - >( - _ body: (_ buffer: borrowing UnsafeBufferPointer) throws(E) -> Result - ) throws(E) -> dependsOn(self) Result + public var span: Span { get } } ``` -### Getting a `String` from a `UTF8Span` - -**QUESTION**: We should make it easier than `String(decoding:as)` to make a `String` copy of the `UTF8Span`, especially since `UTF8Span` cannot conform to `Sequence` or `Collection`. This will form an ARC-managed copy and not something that will share the (ephemeral) storage. - ## Source compatibility This proposal is additive and source-compatible with existing code. @@ -743,19 +644,28 @@ extension UTF8Span { ``` -### More alignments + +### More alignments and alignment queries Future API could include word iterators (either [simple](https://www.unicode.org/reports/tr18/#Simple_Word_Boundaries) or [default](https://www.unicode.org/reports/tr18/#Default_Word_Boundaries)), line iterators, etc. +Similarly, we could add API directly to `UTF8Span` for testing whether a given code unit offset is suitably aligned (including scalar or grapheme-cluster alignment checks). + +### Creating `String` copies + +We could add an initializer to `String` that makes an owned copy of a `UTF8Span`'s contents. Such an initializer can skip UTF-8 validation. + +Alternatively, we could defer adding anything until more of the `Container` protocol story is clear. + ### Normalization Future API could include checks for whether the content is in a particular normal form (not just NFC). ### UnicodeScalarView and CharacterView -Like `Span`, we are deferring adding any collection-like types to non-escapable `UTF8Span`. Future work could include adding view types that conform to a new `Container`-like protocol. +Like `Span`, we are deferring adding any collection-like types to non-escapable `UTF8Span`. Future work could include adding view types that conform to a new `Container`-like protocol. -For an example implementation of those see [the `UTFSpanViews.swift` test file](https://github.com/apple/swift-collections/pull/394). +See "Alternatives Considered" below for more rationale on not adding `Collection`-like API in this proposal. ### More algorithms @@ -765,16 +675,24 @@ However, we can add select high-value algorithms if motivated by the community. ### More validation API -Future work includes returning all the encoding errors found in a given input. +Future API could include a way to find and classify UTF-8 encoding errors in arbitrary byte sequences, beyond just `Span`. + +We could propose something like: ```swift extension UTF8 { - public static func checkAllErrors( + public static func findFirstError( + _ s: some Sequence + ) -> UTF8.EncodingError? + + public static func findAllErrors( _ s: some Sequence - ) -> some Sequence + ) -> some Sequence? ``` -See [`_checkAllErrors` in `UTF8EncodingError.swift`](https://github.com/apple/swift-collections/pull/394). +We are leaving this as future work. It also might be better formulated in line with a segemented-storage `Container`-like protocol instead of `some Sequence`. + +For now, developers can validate UTF-8 and diagnose the location and type of error using `UTF8Span`'s validating initializer, which takes a `Span`. This is similar to how developers do UTF-8 validation [in Rust](https://doc.rust-lang.org/std/str/fn.from_utf8.html). ### Transcoded iterators, normalized iterators, case-folded iterators, etc @@ -786,16 +704,6 @@ Future API additions would be to support `Regex`es on `UTF8Span`. We'd expose gr Another future direction could be to add many routines corresponding to the underlying operations performed by the regex engine, which would be useful for parser-combinator libraries who wish to expose `String`'s model of Unicode by using the stdlib's accelerated implementation. - -### Canonical Spaceships - -Should a `ComparisonResult` (or [spaceship](https://forums.swift.org/t/pitch-comparison-reform/5662)) be added to Swift, we could support that operation under canonical equivalence in a single pass rather than subsequent calls to `isCanonicallyEquivalent(to:)` and `isCanonicallyLessThan(_:)`. - - -### Exposing `String`'s storage class - -String's internal storage class is null-terminated valid UTF-8 (by substituting replacement characters) and implements range-replaceable operations along scalar boundaries. We could consider exposing the storage class itself, which might be useful for embedded platforms that don't have `String`. - ### Track other bits Future work include tracking whether the contents are NULL-terminated (useful for C bridging), whether the contents contain any newlines or only a single newline at the end (useful for accelerating Regex `.`), etc. @@ -827,7 +735,7 @@ An [earlier pitch](https://forums.swift.org/t/pitch-utf-8-processing-over-unsafe A previous version of this pitch had code unit offset taking API directly on UTF8Span instead of using iterators as proposed. This lead to a large number of unweildy API. For example, instead of: ```swift -extension UTF8Span.ScalarIterator { +extension UTF8Span.UnicodeScalarIterator { public mutating func next() -> Unicode.Scalar? { } } ``` @@ -840,7 +748,6 @@ extension UTF8Span { /// the next scalar. /// /// `i` must be scalar-aligned. - @_alwaysEmitIntoClient public func decodeNextScalar( _ i: Int ) -> (Unicode.Scalar, nextScalarStart: Int) @@ -852,7 +759,6 @@ extension UTF8Span { /// /// This function does not validate that `i` is within the span's bounds; /// this is an unsafe operation. - @_alwaysEmitIntoClient public func decodeNextScalar( unchecked i: Int ) -> (Unicode.Scalar, nextScalarStart: Int) @@ -868,7 +774,6 @@ extension UTF8Span { /// /// This function does not validate that `i` is scalar-aligned; this is an /// unsafe operation if `i` isn't. - @_alwaysEmitIntoClient public func decodeNextScalar( uncheckedAssumingAligned i: Int ) -> (Unicode.Scalar, nextScalarStart: Int) @@ -881,20 +786,64 @@ This API made the caller manage the scalar-alignment invariant, while the iterat Scalar-alignment can still be checked and managed by the caller through the `reset` API, which safely round forwards or backwards as needed. And, for high performance use cases where the caller knows that a given position is appropriately aligned already (for example, revisiting a prior point in a string during `Regex` processing), there's the `reset(uncheckedAssumingAlignedTo:)` API available. -### Collections +#### View Collections + +Another forumulation of these operations could be to provide a collection-like API phrased in terms of indices. Because `Collection`s are `Escapable`, we cannot conform nested `View` types to `Collection` so these would not benefit from any `Collection`-generic code, algorithms, etc. + +A benefit of such `Collection`-like views is that it could help serve as adapter code for migration. Existing `Collection`-generic algorithms and methods could be converted to support `UTF8Span` via copy-paste-edit. That is, a developer could interact with `UTF8Span` ala: + +```swift +// view: UTF8Span.UnicodeScalarView +var curIdx = view.startIndex +while curIdx < view.endIndex { + let scalar = view[curIdx] + foo(scalar) + view.formIndex(after: &curIndex) +} +``` + +in addition to the iterator approach of: + +```swift +// iter: UTF8Span.UnicodeScalarIterator (or UTF8Span.UnicodeScalarView.Iterator) +while let scalar = iter.next() { + foo(scalar) +} +``` + +However, the iterator-based approach is the more efficient and direct way to work with a `UTF8Span`. Even if we had `Collection`-like API, we'd still implement a custom iterator type and advocate its use as the best way to interact with `UTF8Span`. The question is whether or not, for a given `FooIterator` we should additionally provide a `FooView`, `FooView.Index`, `FooView.SubSequence`, (possibly) `FooView.Slice`, etc. + +Idiomatic `Collection`-style interfaces support index interchange, even if "support" means reliably crashing after a dynamic check. Any idiomatic index-based interface would need to dynamically check for correct alignment in case the received index was derived from a different span. (There is a whole design space around smart indices and their tradeoffs, discussed in a [lengthy appendix](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md#appendix-index-and-slicing-design-considerations) in the Span proposal). -Because `Collection`s are `Escapable`, we cannot conform nested `View` types to `Collection`. +This means that `UTF8Span.UnicodeScalarView.subscript` would have to check for scalar alignment of its given index, as it does not know whether it originally produced the passed index or not. Similarly, `index(after:)`, `index(before:)`, `index(_:offsetBy:)`, etc., would make these checks on every call. +If we want to give the developer access to efficient formulations of index-style interfaces, we'd additionally propose `uncheckedAssumingAligned:` variants of nearly every method: `subscript(uncheckedAssumingAligned i:)`, `index(uncheckedAssumingAlignedAfter:)`, `index(uncheckedAssumingAlignedBefore:)`, `index(uncheckedAssumingAligned:offsetBy:)`, etc.. This also undermines the value of having an adapter to existing code patterns. + +If we do provide view adapter code, the API could look a little different in that `UnicodeScalarIterator` is called `UnicodeScalarView.Iterator`, `prefix/suffix` are slicing, and the `reset()` functionality is expressed by slicing the view before creating an iterator. However, this would also have the effect of scattering the efficient API use pattern across multiple types, intermingled with inefficient or ill-advised adaptor interfaces which have the more idiomatic names. + +Finally, in the future there will likely be some kind of `Container` protocol for types that can vend segments of contiguous storage. In our case, the segment type is `UTF8Span`, while the element is decoded from the underlying UTF-8. It's likely easier and more straightforward to retrofit or deprecate a single `UnicodeScalarIterator` type than a collection of types interrelated to each other. ## Acknowledgments Karoy Lorentey, Karl, Geordie_J, and fclout, contributed to this proposal with their clarifying questions and discussions. + From 2f08c1dde9f802378d2d5d3e36018541e7934e05 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 25 Feb 2025 19:44:06 -0500 Subject: [PATCH 4091/4563] Accept SE-0458 --- proposals/0458-strict-memory-safety.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index a56d0f2ce5..c3bb3fdaa1 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -3,12 +3,12 @@ * Proposal: [SE-0458](0458-strict-memory-safety.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (January 17th...February 11th, 2025)** +* Status: **Accepted** * Feature name: `StrictMemorySafety` -* Vision: [Opt-in Strict Memory Safety Checking (Prospective)](https://github.com/swiftlang/swift-evolution/pull/2581) +* Vision: [Optional Strict Memory Safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/memory-safety.md) * Implementation: On main with experimental feature flags `AllowUnsafeAttribute` and `WarnUnsafe` -* Previous Revision: [1](https://github.com/swiftlang/swift-evolution/blob/f2cab4ddc3381d1dc7a970e813ed29e27b5ae43f/proposals/0458-strict-memory-safety.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-opt-in-strict-memory-safety-checking/76689)) ([review](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274)) ([mid-review revision](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274/33)) +* Previous Revision: [1](https://github.com/swiftlang/swift-evolution/blob/f2cab4ddc3381d1dc7a970e813ed29e27b5ae43f/proposals/0458-strict-memory-safety.md) [2](https://github.com/swiftlang/swift-evolution/blob/9d180aea291c6b430bcc816ce12ef0174ec0237b/proposals/0458-strict-memory-safety.md) +* Review: ([pitch](https://forums.swift.org/t/pitch-opt-in-strict-memory-safety-checking/76689)) ([review](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274)) ([first revision](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274/33)) ([second revision](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274/51)) ([acceptance](https://forums.swift.org/t/accepted-se-0458-opt-in-strict-memory-safety-checking/78116)) ## Introduction From fac1b44c903e4dbf434ffc60e8004aa07837abfe Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Sat, 15 Feb 2025 14:08:29 -0800 Subject: [PATCH 4092/4563] Add a proposal for importing Objective-C completion handler parameters as `@Sendable`. --- .../NNNN-sendable-completion-handlers.md | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 proposals/NNNN-sendable-completion-handlers.md diff --git a/proposals/NNNN-sendable-completion-handlers.md b/proposals/NNNN-sendable-completion-handlers.md new file mode 100644 index 0000000000..17d45006f1 --- /dev/null +++ b/proposals/NNNN-sendable-completion-handlers.md @@ -0,0 +1,77 @@ +# Import Objective-C completion handler parameters as `@Sendable` + +* Proposal: [SE-NNNN](NNNN-sendable-completion-handlers.md) +* Authors: [Holly Borla](https://github.com/hborla) +* Review Manager: TBD +* Status: **Awaiting review** +* Vision: [[Prospective Vision] Improving the approachability of data-race safety](https://forums.swift.org/t/prospective-vision-improving-the-approachability-of-data-race-safety/76183) +* Implementation: On `main` behind `-enable-experimental-feature SendableCompletionHandlers` +* Review: ([pitch](https://forums.swift.org/t/pitch-import-objective-c-completion-handler-parameters-as-sendable/77904)) + +## Introduction + +This proposal changes the Objective-C importing rules such that completion handler parameters are `@Sendable` by default. + +## Motivation + +Swift's data-race safety model requires function declarations to codify their concurrency invariants in the function signature with annotations. The `@Sendable` annotation indicates that closure parameters are passed over an isolation boundary before they're called. A missing `@Sendable` annotation in a library has negative effects on clients who call the function; the caller can unknowingly introduce data races, and [SE-0423: Dynamic actor isolation enforcement from non-strict-concurrency contexts][SE-0423] injects runtime assertions for non-`Sendable` closure parameters that are passed into libraries that don't have data-race safety checking. This means that a missing `@Sendable` annotation can lead to a runtime crash for any code that calls the API from an actor isolated context, which is extremely painful for projects that are migrating to the Swift 6 language mode. + +There's a large category of APIs with closure parameters that can be automatically identified as `@Sendable` functions, even if the annotation is missing: Objective-C methods with completion handler parameters. `@Sendable` is nearly always the right default for Objective-C completion handlers, and [programmers have already been searching for an automatic way for completion handlers to be `@Sendable` by default when auditing Clang headers](https://forums.swift.org/t/clang-sendability-audit-for-closures/75557). + +## Proposed solution + +I propose automatically importing completion handler parameters from Objective-C methods as `@Sendable` functions. + +## Detailed design + +If an imported method has an async variant (as described in [SE-0297: Concurrency Interoperability with Objective-C][SE-0297]) and the method is (implicitly or explicitly) `nonisolated`, the original method will be imported with a `@Sendable` annotation on its completion handler parameter. + +For example, given the following Objective-C method signature: + +```objc +- (void)performOperation:(NSString * _Nonnull)operation + completionHandler:(void (^ _Nullable)(NSString * _Nullable, NSError * _Nullable))completionHandler; +``` + +Swift will import the method with `@Sendable` on the `completionHandler` parameter: + +```swift +@preconcurrency +func perform( + operation: String, + completionHandler: @Sendable @escaping ((String?, Error?) -> Void)? +) +``` + +When calling the `perform` method from a Swift actor, the inference rules that allow non-`Sendable` closures to be isolated to the context they're formed in will no longer apply. The closure will be inferred as `nonisolated`, and warnings will be produced if any mutable state in the actor's region is accessed from the closure. Note that all APIs imported from C/C++/Objective-C are automatically `@preconcurrency`, so data-race safety violations are only ever warnings, even in the Swift 6 language mode. + +### Completion handlers of global actor isolated functions + +Functions that are isolated to a global actor will not have completion handlers imported as `@Sendable`. Main actor isolated functions with completion handlers that are always called on the main actor is a very common Objective-C pattern, and this carve out will eliminate false positive warnings in the cases where the main actor annotation is missing on the completion handler parameter. This carve out will not add any new dynamic assertions. + +### Opting out of `@Sendable` completion handlers + +If a completion handler does not cross an isolation boundary before it's called, the parameter can be annotated in the header with the `@nonSendable` attribute using `__attribute__((swift_attr(“@nonSendable”)))`. The `@nonSendable` attribute is only for Clang header annotations; it is not meant to be used from Swift code. + +## Source compatibility + +This change has no effect in language modes prior to Swift 6 when using minimal concurrency checking, and it only introduces warnings when using complete concurrency checking, even in the Swift 6 language mode. Declarations imported from C/C++/Objective-C are implicitly `@preconcurrency`, which makes all data-race safety violations warnings. + +## ABI compatibility + +This proposal has no impact on existing ABI. + +## Alternatives considered + +### Import completion handlers as `sending` instead of `@Sendable` + +The choice to import completion handlers as `@Sendable` instead of `sending` is pragmatic - the experimental `SendableCompletionHandlers` implementation has existed since 2021 and has been extensively tested for source compatibility. Similarly, `@Sendable` has been explicitly adopted in Objective-C frameworks for several years, and source compatibility issues resulting from corner cases in the compiler implementation that were intolerable to `@Sendable` mismatches have shaken out over time. `sending` is still a relatively new parameter attribute, it has not been adopted as extensively as `@Sendable`, and it does not support downgrading diagnostics in the Swift 6 language mode when combined with `@preconcurrency`. The pain caused by the dynamic actor isolation runtime assertions is enough that it's worth solving this problem now conservatively using `@Sendable`. + +Changing this proposal later to use `sending` instead will pose source compatibility issues, because it would become invalid to have a protocol requirement that is imported with a `sending` completion handler and implement the requirement with a `@Sendable` completion handler. The same source compatibility issue exists for overridden class methods. If programmers want to take advantage of region isolation, the recommended path is to modernize the code using `async`/`await`. + +## Acknowledgments + +Thank you to Becca Royal-Gordon for implementing the `SendableCompletionHandlers` experimental feature, and thank you to Pavel Yaskevich for consistently fixing compiler bugs where the implementation was intolerant to `@Sendable` mismatches. + +[SE-0297]: /proposals/0297-concurrency-objc.md +[SE-0423]: /proposals/0423-dynamic-actor-isolation.md From f42ebc0a0a0c6cfd1908bedbf9c6898bbbe11960 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 26 Feb 2025 13:24:16 -0800 Subject: [PATCH 4093/4563] Add a revision history section. --- proposals/NNNN-sendable-completion-handlers.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/proposals/NNNN-sendable-completion-handlers.md b/proposals/NNNN-sendable-completion-handlers.md index 17d45006f1..9a9fb08a34 100644 --- a/proposals/NNNN-sendable-completion-handlers.md +++ b/proposals/NNNN-sendable-completion-handlers.md @@ -75,3 +75,9 @@ Thank you to Becca Royal-Gordon for implementing the `SendableCompletionHandlers [SE-0297]: /proposals/0297-concurrency-objc.md [SE-0423]: /proposals/0423-dynamic-actor-isolation.md + +## Revisions + +The proposal was revised with the following changes after the pitch discussion: + +* Add a carve out where global actor isolated functions are still imported with non-`Sendable` completion handlers. From 180e96f7699918de37cc55e49833361823c524bd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 27 Feb 2025 00:12:40 -0800 Subject: [PATCH 4094/4563] SE-0458 "Opt-in Strict Memory Safety Checking" is implemented (#2711) --- proposals/0458-strict-memory-safety.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index c3bb3fdaa1..24854d310b 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -3,10 +3,9 @@ * Proposal: [SE-0458](0458-strict-memory-safety.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Feature name: `StrictMemorySafety` * Vision: [Optional Strict Memory Safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/memory-safety.md) -* Implementation: On main with experimental feature flags `AllowUnsafeAttribute` and `WarnUnsafe` * Previous Revision: [1](https://github.com/swiftlang/swift-evolution/blob/f2cab4ddc3381d1dc7a970e813ed29e27b5ae43f/proposals/0458-strict-memory-safety.md) [2](https://github.com/swiftlang/swift-evolution/blob/9d180aea291c6b430bcc816ce12ef0174ec0237b/proposals/0458-strict-memory-safety.md) * Review: ([pitch](https://forums.swift.org/t/pitch-opt-in-strict-memory-safety-checking/76689)) ([review](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274)) ([first revision](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274/33)) ([second revision](https://forums.swift.org/t/se-0458-opt-in-strict-memory-safety-checking/77274/51)) ([acceptance](https://forums.swift.org/t/accepted-se-0458-opt-in-strict-memory-safety-checking/78116)) @@ -421,7 +420,7 @@ A type has unsafe storage if: #### Unsafe witnesses -When a type conforms to a given protocol, it must satisfy all of the requirements of that protocol. Part of this process is determining which declaration (called the *witness*) satisfies a given protocol requirement. If a particular witness is unsafe but the corresponding requirement is not safe, the compiler will produce a warning: +When a type conforms to a given protocol, it must satisfy all of the requirements of that protocol. Part of this process is determining which declaration (called the *witness*) satisfies a given protocol requirement. If a particular witness is unsafe but the corresponding requirement is safe, the compiler will produce a warning: ```swift protocol P { From 6ba97a33931a9574ce97446a11df7e8aa3496854 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 27 Feb 2025 16:29:59 -0500 Subject: [PATCH 4095/4563] Assign SE-0463 to the sendable completion handlers proposal and put it in review --- ...n-handlers.md => 0463-sendable-completion-handlers.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{NNNN-sendable-completion-handlers.md => 0463-sendable-completion-handlers.md} (95%) diff --git a/proposals/NNNN-sendable-completion-handlers.md b/proposals/0463-sendable-completion-handlers.md similarity index 95% rename from proposals/NNNN-sendable-completion-handlers.md rename to proposals/0463-sendable-completion-handlers.md index 9a9fb08a34..8aafcfdca9 100644 --- a/proposals/NNNN-sendable-completion-handlers.md +++ b/proposals/0463-sendable-completion-handlers.md @@ -1,10 +1,10 @@ # Import Objective-C completion handler parameters as `@Sendable` -* Proposal: [SE-NNNN](NNNN-sendable-completion-handlers.md) +* Proposal: [SE-0463](0463-sendable-completion-handlers.md) * Authors: [Holly Borla](https://github.com/hborla) -* Review Manager: TBD -* Status: **Awaiting review** -* Vision: [[Prospective Vision] Improving the approachability of data-race safety](https://forums.swift.org/t/prospective-vision-improving-the-approachability-of-data-race-safety/76183) +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active Review (Feburary 27th...March 10th, 2025)** +* Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md) * Implementation: On `main` behind `-enable-experimental-feature SendableCompletionHandlers` * Review: ([pitch](https://forums.swift.org/t/pitch-import-objective-c-completion-handler-parameters-as-sendable/77904)) From a3727f80254bf0e1660b164270a3e284d6c3b3b4 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 27 Feb 2025 16:34:57 -0500 Subject: [PATCH 4096/4563] Link SE-0463 to its review thread --- proposals/0463-sendable-completion-handlers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0463-sendable-completion-handlers.md b/proposals/0463-sendable-completion-handlers.md index 8aafcfdca9..8211413d0b 100644 --- a/proposals/0463-sendable-completion-handlers.md +++ b/proposals/0463-sendable-completion-handlers.md @@ -6,7 +6,7 @@ * Status: **Active Review (Feburary 27th...March 10th, 2025)** * Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md) * Implementation: On `main` behind `-enable-experimental-feature SendableCompletionHandlers` -* Review: ([pitch](https://forums.swift.org/t/pitch-import-objective-c-completion-handler-parameters-as-sendable/77904)) +* Review: ([pitch](https://forums.swift.org/t/pitch-import-objective-c-completion-handler-parameters-as-sendable/77904)) ([review](https://forums.swift.org/t/se-0463-import-objective-c-completion-handler-parameters-as-sendable/78169)) ## Introduction From f2b428c31393da20b09a7770ac9d0ad4c91f3753 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Thu, 27 Feb 2025 22:22:22 +0000 Subject: [PATCH 4097/4563] [SE-0463] Fix sendable-completion-handlers status --- proposals/0463-sendable-completion-handlers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0463-sendable-completion-handlers.md b/proposals/0463-sendable-completion-handlers.md index 8211413d0b..3200e21039 100644 --- a/proposals/0463-sendable-completion-handlers.md +++ b/proposals/0463-sendable-completion-handlers.md @@ -3,7 +3,7 @@ * Proposal: [SE-0463](0463-sendable-completion-handlers.md) * Authors: [Holly Borla](https://github.com/hborla) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (Feburary 27th...March 10th, 2025)** +* Status: **Active Review (February 27th...March 10th, 2025)** * Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md) * Implementation: On `main` behind `-enable-experimental-feature SendableCompletionHandlers` * Review: ([pitch](https://forums.swift.org/t/pitch-import-objective-c-completion-handler-parameters-as-sendable/77904)) ([review](https://forums.swift.org/t/se-0463-import-objective-c-completion-handler-parameters-as-sendable/78169)) From e62f7dadc3627c018f8b18aa97e2875ff11375a6 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 27 Feb 2025 16:24:33 -0800 Subject: [PATCH 4098/4563] [SE-0458] Rename diagnostic group from Unsafe to StrictMemorySafety (#2714) This is more self-explanatory and lines up with the feature name. Specific diagnostic group names are non-normative, so I feel okay changing this :) --- proposals/0458-strict-memory-safety.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index 24854d310b..b9ce0f6221 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -43,7 +43,7 @@ For example, Swift solves null references with optional types. Statically, Swift This proposal introduces an opt-in strict memory safety checking mode that identifies all uses of unsafe behavior within the given module. There are several parts to this change: -* A compiler flag `-strict-memory-safety` that enables warnings for all uses of unsafe constructs within a given module. All warnings will be in the diagnostic group `Unsafe`, enabling precise control over memory-safety-related warnings per [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md). When strict memory safety is enabled, the `StrictMemorySafety` feature will be set: `#if hasFeature(StrictMemorySafety)` can be used to detect when Swift code is being compiled in this mode. +* A compiler flag `-strict-memory-safety` that enables warnings for all uses of unsafe constructs within a given module. All warnings will be in the diagnostic group `StrictMemorySafety`, enabling precise control over memory-safety-related warnings per [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md). When strict memory safety is enabled, the `StrictMemorySafety` feature will be set: `#if hasFeature(StrictMemorySafety)` can be used to detect when Swift code is being compiled in this mode. * An attribute `@unsafe` that indicates that a declaration is unsafe to use. Such declarations may use unsafe constructs within their signatures. * A corresponding attribute `@safe` that indicates that a declaration whose signature contains unsafe constructs is actually safe to use. For example, the `withUnsafeBufferPointer` method on `Array` has an unsafe type in its signature (`self`), but is actually safe to use because it handles safety for the unsafe buffer pointer it vends to its closure argument. The closure itself will need to handle the unsafety when using that unsafe buffer pointer. * An `unsafe` expression that marks any use of unsafe constructs in an expression, much like `try` and `await`. @@ -507,7 +507,7 @@ for try await x in try await getAsyncSequence() { ... } The strict memory safety mode can be enabled with the new compiler flag `-strict-memory-safety`. -All of the memory-safety diagnostics produced by the strict memory safety mode will be warnings. These warnings be in the group `Unsafe` (possibly organized into subgroups) so that one can choose to escalate them to errors or keep them as warnings using the compiler flags introduced in [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md). For example, one can choose to enable the mode and make memory-safety issues errors using: +All of the memory-safety diagnostics produced by the strict memory safety mode will be warnings. These warnings be in the group `StrictMemorySafety` (possibly organized into subgroups) so that one can choose to escalate them to errors or keep them as warnings using the compiler flags introduced in [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md). For example, one can choose to enable the mode and make memory-safety issues errors using: ``` swiftc -strict-memory-safety -Werror Unsafe From 720735324854811854ee5175dfe34276239039a7 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Fri, 28 Feb 2025 19:05:53 -0600 Subject: [PATCH 4099/4563] Add a proposal template for Swift Testing (#2709) * Add a proposal template for Swift Testing * Update URL template for "Previous revision" link * Switch to /proposals/testing subdirectory --- .../0000-swift-testing-template.md | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 proposal-templates/0000-swift-testing-template.md diff --git a/proposal-templates/0000-swift-testing-template.md b/proposal-templates/0000-swift-testing-template.md new file mode 100644 index 0000000000..2ea8c57572 --- /dev/null +++ b/proposal-templates/0000-swift-testing-template.md @@ -0,0 +1,184 @@ +# Swift Testing Feature name + +* Proposal: [SWT-NNNN](NNNN-filename.md) +* Authors: [Author 1](https://github.com/author1), [Author 2](https://github.com/author2) +* Status: **Awaiting implementation** or **Awaiting review** +* Bug: _if applicable_ [swiftlang/swift-testing#NNNNN](https://github.com/swiftlang/swift-testing/issues/NNNNN) +* Implementation: [swiftlang/swift-testing#NNNNN](https://github.com/swiftlang/swift-testing/pull/NNNNN) +* Previous Proposal: _if applicable_ [SWT-XXXX](XXXX-filename.md) +* Previous Revision: _if applicable_ [1](https://github.com/swiftlang/swift-evolution/blob/...commit-ID.../proposals/testing/NNNN-filename.md) +* Review: ([pitch](https://forums.swift.org/...)) + +When filling out this template, you should delete or replace all of the text +except for the section headers and the header fields above. For example, you +should delete everything from this paragraph down to the Introduction section +below. + +As a proposal author, you should fill out all of the header fields. Delete any +header fields marked _if applicable_ that are not applicable to your proposal. + +When sharing a link to the proposal while it is still a PR, be sure to share a +live link to the proposal, not an exact commit, so that readers will always see +the latest version when you make changes. On GitHub, you can find this link by +browsing the PR branch: from the PR page, click the "username wants to merge ... +from username:my-branch-name" link and find the proposal file in that branch. + +`Status` should reflect the current implementation status while the proposal is +still a PR. The proposal cannot be reviewed until an implementation is available, +but early readers should see the correct status. + +`Bug` should be used when this proposal is fixing a bug with significant +discussion in the bug report. It is not necessary to link bugs that do not +contain significant discussion or that merely duplicate discussion linked +somewhere else. Do not link bugs from private bug trackers. + +`Implementation` should link to the PR(s) implementing the feature. If the +proposal has not been implemented yet, or if it simply codifies existing +behavior, just say that. If the implementation has already been committed to the +main branch (as an experimental feature or SPI), mention that. If the +implementation is spread across multiple PRs, just link to the most important +ones. + +`Previous Proposal` should be used when there is a specific line of succession +between this proposal and another proposal. For example, this proposal might +have been removed from a previous proposal so that it can be reviewed separately, +or this proposal might supersede a previous proposal in some way that was felt +to exceed the scope of a "revision". Include text briefly explaining the +relationship, such as "Supersedes SWT-1234" or "Extracted from SWT-01234". If +possible, link to a post explaining the relationship, such as a review decision +that asked for part of the proposal to be split off. Otherwise, you can just +link to the previous proposal. + +`Previous Revision` should be added after a major substantive revision of a +proposal that has undergone review. It links to the previously reviewed revision. +It is not necessary to add or update this field after minor editorial changes. + +`Review` is a history of all discussion threads about this proposal, in +chronological order. Use these standardized link names: `pitch` `review` +`revision` `acceptance` `rejection`. If there are multiple such threads, spell +the ordinal out: `first pitch` `second review` etc. + +## Introduction + +A short description of what the feature is. Try to keep it to a single-paragraph +"elevator pitch" so the reader understands what problem this proposal is +addressing. + +## Motivation + +Describe the problems that this proposal seeks to address. If the problem is +that some common pattern is currently hard to express, show how one can +currently get a similar effect and describe its drawbacks. If it's completely +new functionality that cannot be emulated, motivate why this new functionality +would help Swift developers test their code more effectively. + +## Proposed solution + +Describe your solution to the problem. Provide examples and describe how they +work. Show how your solution is better than current workarounds: is it cleaner, +safer, or more efficient? + +This section doesn't have to be comprehensive. Focus on the most important parts +of the proposal and make arguments about why the proposal is better than the +status quo. + +## Detailed design + +Describe the design of the solution in detail. If it includes new API, show the +full API and its documentation comments detailing what it does. If it involves +new macro logic, describe the behavior changes and include a succinct example of +the additions or modifications to the macro expansion code. The detail in this +section should be sufficient for someone who is *not* one of the authors to be +able to reasonably implement the feature. + +## Source compatibility + +Describe the impact of this proposal on source compatibility. As a general rule, +all else being equal, test code that worked in previous releases of the testing +library should work in new releases. That means both that it should continue to +build and that it should continue to behave dynamically the same as it did +before. + +This is not an absolute guarantee, and the testing library administrators will +consider intentional compatibility breaks if their negative impact can be shown +to be small and the current behavior is causing substantial problems in practice. + +For proposals that affect testing library API, consider the impact on existing +clients. If clients provide a similar API, will type-checking find the right one? +If the feature overloads an existing API, is it problematic that existing users +of that API might start resolving to the new API? + +## Integration with supporting tools + +In this section, describe how this proposal affects tools which integrate with +the testing library. Some features depend on supporting tools gaining awareness +of the new feature for users to realize new benefits. Other features do not +strictly require integration but bring improvement opportunities which are worth +considering. Use this section to discuss any impact on tools. + +This section does need not to include details of how this proposal may be +integrated with _specific_ tools, but it should consider the general ways that +tools might support this feature and note any accompanying SPI intended for +tools which are included in the implementation. Note that tools may evolve +independently and have differing release schedules than the testing library, so +special care should be taken to ensure compatibility across versions according +to the needs of each tool. + +## Future directions + +Describe any interesting proposals that could build on this proposal in the +future. This is especially important when these future directions inform the +design of the proposal, for example by making sure an interface meant for tools +integration can be extended to include additional information. + +The rest of the proposal should generally not talk about future directions +except by referring to this section. It is important not to confuse reviewers +about what is covered by this specific proposal. If there's a larger vision that +needs to be explained in order to understand this proposal, consider starting a +discussion thread on the forums to capture your broader thoughts. + +Avoid making affirmative statements in this section, such as "we will" or even +"we should". Describe the proposals neutrally as possibilities to be considered +in the future. + +Consider whether any of these future directions should really just be part of +the current proposal. It's important to make focused, self-contained proposals +that can be incrementally implemented and reviewed, but it's also good when +proposals feel "complete" rather than leaving significant gaps in their design. +An an example from the Swift project, when +[SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md) +introduced the `@inlinable` attribute, it also included the `@usableFromInline` +attribute so that declarations used in inlinable functions didn't have to be +`public`. This was a relatively small addition to the proposal which avoided +creating a serious usability problem for many adopters of `@inlinable`. + +## Alternatives considered + +Describe alternative approaches to addressing the same problem. This is an +important part of most proposal documents. Reviewers are often familiar with +other approaches prior to review and may have reasons to prefer them. This +section is your first opportunity to try to convince them that your approach is +the right one, and even if you don't fully succeed, you can help set the terms +of the conversation and make the review a much more productive exchange of ideas. + +You should be fair about other proposals, but you do not have to be neutral; +after all, you are specifically proposing something else. Describe any +advantages these alternatives might have, but also be sure to explain the +disadvantages that led you to prefer the approach in this proposal. + +You should update this section during the pitch phase to discuss any +particularly interesting alternatives raised by the community. You do not need +to list every idea raised during the pitch, just the ones you think raise points +that are worth discussing. Of course, if you decide the alternative is more +compelling than what's in the current proposal, you should change the main +proposal; be sure to then discuss your previous proposal in this section and +explain why the new idea is better. + +## Acknowledgments + +If significant changes or improvements suggested by members of the community +were incorporated into the proposal as it developed, take a moment here to thank +them for their contributions. This is a collaborative process, and everyone's +input should receive recognition! + +Generally, you should not acknowledge anyone who is listed as a co-author. From 49347edac6069bd7efb23f5427ca07c8fa0a18bf Mon Sep 17 00:00:00 2001 From: shiz <35151927+stzn@users.noreply.github.com> Date: Mon, 3 Mar 2025 07:32:04 +0900 Subject: [PATCH 4100/4563] Fix invalid URLs (#2715) --- visions/approachable-concurrency.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/visions/approachable-concurrency.md b/visions/approachable-concurrency.md index de8adefc75..25e6e6b725 100644 --- a/visions/approachable-concurrency.md +++ b/visions/approachable-concurrency.md @@ -158,9 +158,9 @@ The current execution semantics of async functions also impede programmer’s un Changing the default execution semantics of nonisolated async functions to run wherever they are called better facilitates progressive disclosure of concurrency. This default allows functions to leverage suspension without forcing callers to cross an isolation boundary and imposing data-race safety checks on arguments and results. A lot of basic asynchronous code can be written correctly and efficiently with only the ability to suspend. When an async function needs to always run off of an actor, the API author can still specify that with a new `@execution(concurrent)` annotation on the function. This provides a better default for most cases while still maintaining the ease of specifying that an async function switches off of an actor to run. -Many programmers have internalized the SE-0338 semantics, and making this change several years after SE-0338 was accepted creates an unfortunate intermediate state where it's difficult to understand the semantics of a nonisolated async function without understanding the build settings of the module you're writing code in. We can alleviate some of these consequences with a careful migration design. There are more details about migration in the [automatic migration](#automatic-migration) section of this document, and in the [source compatibility](https://github.com/hborla/swift-evolution/blob/async-function-isolation/proposals/NNNN-async-function-isolation.md#source-compatibility) section of the proposal for this change. +Many programmers have internalized the SE-0338 semantics, and making this change several years after SE-0338 was accepted creates an unfortunate intermediate state where it's difficult to understand the semantics of a nonisolated async function without understanding the build settings of the module you're writing code in. We can alleviate some of these consequences with a careful migration design. There are more details about migration in the [automatic migration](#automatic-migration) section of this document, and in the [source compatibility](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0461-async-function-isolation.md#source-compatibility) section of the proposal for this change. -This idea has already been pitched on the forums, and you can read the full proposal for it [here](https://github.com/hborla/swift-evolution/blob/async-function-isolation/proposals/NNNN-async-function-isolation.md). +This idea has already been pitched on the forums, and you can read the full proposal for it [here](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0461-async-function-isolation.md). ## Easing incremental migration to data-race safety From 6cc9f5f00c395fae37dd6bf430812fb5b0fb4e88 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 3 Mar 2025 15:24:47 +0900 Subject: [PATCH 4101/4563] initial isIsolated revision --- proposals/NNNN-SerialExecutor-isIsolated.md | 195 ++++++++++++++++++++ proposals/nnnn-is-isolated-flow.graffle | Bin 0 -> 157991 bytes proposals/nnnn-is-isolated-flow.png | Bin 0 -> 74137 bytes 3 files changed, 195 insertions(+) create mode 100644 proposals/NNNN-SerialExecutor-isIsolated.md create mode 100644 proposals/nnnn-is-isolated-flow.graffle create mode 100644 proposals/nnnn-is-isolated-flow.png diff --git a/proposals/NNNN-SerialExecutor-isIsolated.md b/proposals/NNNN-SerialExecutor-isIsolated.md new file mode 100644 index 0000000000..6961770056 --- /dev/null +++ b/proposals/NNNN-SerialExecutor-isIsolated.md @@ -0,0 +1,195 @@ +# Improved Custom SerialExecutor isolation checking for Concurrency Runtime + +* Proposal: [SE-NNNN](...) +* Author: [Konrad 'ktoso' Malawski](https://github.com/ktoso) +* Review Manager: +* Status: **WIP** +* Review: TODO + +## Introduction + +In [SE-0424: Custom isolation checking for SerialExecutor](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0424-custom-isolation-checking-for-serialexecutor.md) we introduced a way for custom executors implementing the `SerialExecutor` protocol to assert and and assume the static isolation if the dynamic check succeeded. This proposal extends these capabilities, allowing custom executors to not only "check and crash if assumption was wrong", but also check and act on the result of the check. + +## Motivation + +The previously ([SE-0424](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0424-custom-isolation-checking-for-serialexecutor.md)) introduced family of `Actor/assertIsolated()`, `Actor/preconditionIsolated()` and `Actor/assumeIsolated(operation:)` all rely on the `SerialExecutor/checkIsolated()` API which was introduced in the same proposal. + +These APIs all follow the "*pass or crash*" pattern. Where the crash is caused in order to prevent an incorrect assumption about isolation resulting in unsafe concurrent access to some isolated state. This is frequently used by methods which are "known to be called" on some specific actor to recover the dynamic (known at runtime) isolation information, into its static equivalent and therefore safely access some isolated state, like in this example: + +```swift +@MainActor +var counter: Int = num + +protocol P { + // Always called by the main actor, + // yet protocol author forgot to annotate the method or protocol using @MainActor + func actuallyWeKnowForSureThisIsCalledFromTheMainActor() +} + +struct Impl: P { + func actuallyWeKnowForSureThisIsCalledFromTheMainActor() { + MainActor.assumeIsolated { // we know this is safe here + counter += 1 + } + } +} + +@MainActor +func call(p: some P) { + p.actuallyWeKnowForSureThisIsCalledFromTheMainActor() +} +``` + +This works fine for many situations, however some libraries may need to be more careful when rolling out strict concurrency checks like these, and instead of crashing may want to choose to issue warnings before they adopt a mode that enforces correct use. + +Currently all APIs available are using the `checkIsolated()` API which must crash if called from a context not managed by the serial executor it is invoked on. This method is often implemented using `dispatchPrecondition()` when the serial executor is backed using `Dispatch` or custom `fatalError()` messages otherwise: + +```swift +final class ExampleExecutor: SerialExecutor { + func checkIsolated() { + dispatchPrecondition(condition: .onQueue(self.queue)) + } +} +``` + +This approach is better than not being able to participate in the checks at all (i.e. this API allows for advanced thread/queue sharing between actor and non-actor code), but it has two severe limitations: + +- the crash **messages** offered by these `checkIsolated()` crashes **are often sub-optimal** and confusing + - messages often don't include crucial information about which actor/executor the calling context was _actually_ executing on. Offering only "expected [...]" messages, leading to hard to debug crashes. +- it is **impossible** for the Swift runtime to offer **isolation violation warnings** + - because the Swift runtime _must_ call into a custom executor to verify its isolation, the "pass or crash" method will crash, rather than inform the runtime that a violation ocured and we should warn about it. + +Today, it is not possible for the Swift runtime to issue _warnings_ if something is detected to be on not the expected executor, but somehow we'd still like to continue without crashing the application. + +And for existing situations when `assumeIsolated()` is called and _should_ crash, by using this new API the Swift runtime will be able to provide _more informative_ messages, including all available context about the execution environment. + +## Proposed solution + +We propose to introduce a new `isIsolatingCurrentContext` protocol requirement to the `SerialExecutor` protocol: + +```swift +protocol SerialExecutor { + + /// May be called by the Swift runtime before `checkIsolated()` + /// in order to check for isolation violations at runtime, + /// and potentially issue isolation warnings. + /// + /// [...] + func isIsolatingCurrentContext() -> Bool + + // existing API, since SE-0424 + @available(SwiftStdlib 6.0, *) + func checkIsolated() // must crash if run on a context not managed by this serial executor + + // ... +} + +extension SerialExecutor { + /// Default implementation for backwards compatibility. + func isIsolatingCurrentContext() -> Bool { false } +} +``` + +The Swift runtime is free to call the `isIsolated` function whenever it wants to verify if the current context is apropriately isolated by some serial executor. + +In most cases implementing this new API is preferable to implementing `checkIsolated()`, as the Swift runtime is able to offer more detailed error messages when when an isolation failure detected by a call to `isIsolatingCurrentContext()` is detected. + +## Detailed design + +The newly proposed `isIsolatingCurrentContext()` function participates in the previously established runtime isolation checking flow, and happens _before_ any calls to `checkIsolated()` are attempted. The following diagram explains the order of calls issued by the runtime to dynamically verify an isolation when e.g. `assumeIsolated()` is called: + +![diagram illustrating which method is called when](/Users/ktoso/code/swift-evolution/proposals/nnnn-is-isolated-flow.png) + + + +There are a lot of conditions here and availability of certain features also impacts this decision flow, so it is best to refer to the diagram for detailed analysis of every situation. However the most typical situation involves executing on a task, which has a potentially different executor than the `expected` one. In such situation the runtime will: + +- check for the existence of a "current" task, +- (fast path) if a task is present: + - compare the current task's serial executor it is isolated to (if any) with the expected serial executor, + +- :new: if **`isIsolatingCurrentContext`** **is available** on the `expected` executor: + - invoke `isIsolatingCurrentContext` + - ✅ if it returned true: pass the check. + - :x: if it returned false: fail the check. + - The runtime will **not** proceed to call `checkIsolated` after `isIsolated` is invoked! + +- if **`isIsolatingCurrentContext`** is **not available** on the expected executor, but **`checkIsolated`** **is available**: + - invoke `expected.checkIsolated` which will crash :x: or pass :white_check_mark: depending on its internal checking. +- if neither `checkIsolated` or `isIsolatingCurrentContext` are available, + - :x: crash with a best effort message. + + +This proposal specifically adds the "if `isIsolatingCurrentContext` is available" branch into the existing logic for confirming the isolation expectation. + +If `isIsolatingCurrentContext` is available, effectively it replaces `checkIsolated` because it does offer a sub-par error message experience and is not able to offer a warning if Swift would be asked to check the isolation but not crash upon discovering a violation. + +### Enabling `isIsolatingCurrentContext` checking mode + +Similar as complex equality in `SerialExecutors` this feature must be opted into by flagging it when the `UnownedSerialExecutor` value is returned from a serial executor's `asUnownedSerialExecutor()`. + +Previously this was done by signalling the feature in the `UnownedSerialExecutor` initializer like this: + +```swift +// Existing API +public func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(complexEquality: self) +} +``` + +Which enables the following `isSameExclusiveExecutionContext` check, which can only be used when a "current" executor is present, and cannot be used when running code outside of a Swift concurren ytask (!): + +```swift +// Existing API +public func isSameExclusiveExecutionContext(other: NaiveQueueExecutor) -> Bool { + other.secretIdentifier == self.secretIdentifier +} +``` + +In order to enable the the runtime to call into the `isIsolatingCurrentContext` the `UnownedSerialExecutor` **must** be constructed as follows: + +```swift +public func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(hasIsIsolatingCurrentContext: self) +} +``` + +This sets a flag inside the internal executor reference which makes the swift runtime call into the new `isIsolatingCurrentContext` function, rather than the other versions of isolation checking. In many ways this API is the most general of them all, and generally preferable _if_ your executor is using some kind of mechanism to track the "current" context that the Swift concurrency runtime cannot know about, e.g. like thread local values inside threads managed by your executor. + +### Compatibility strategy for custom SerialExecutor authors + +New executor implementations should prioritize implementing `isIsolatingCurrentContext` when available, using an appropriate `#if swift(>=...)` check to ensure compatibility. Otherwise, they should fall back to implementing the crashing version of this API: `checkIsolated()`. + +While the relationship between `isIsolatingCurrentContext` and `checkIsolated` is not ideal, the boolean-returning version was previously not possible to implement due to runtime restrictions which we have manage to resolve, and thus would like to introduce the better API and better user-experience. + +For authors of custom serial executors, adopting this feature is an incremental process and they can adopt it at their own pace, properly guarding the new feature with necessary availability guards. This feature requires a new version of the Swift concurrency runtime which is aware of this new mode and therefore able to call into the new checking function, therefore libraries should implement and adopt it, however it will only manifest itself when the code is used with a new enough concurrency runtime + +As a result, this change should cause little to no disruption to Swift concurrency users, while providing an improved error reporting experience if using executors which adopt this feature. + +## Source Compatibility + +This proposal is source compatible, a default implementation of the new protocol requirement is introduced along with it, allowing existing `SerialExecutors` to compile without changes. + +## Binary Compatibility + +This proposal is ABI additive, and does not cause any binary incompatibility risks. + +## Future directions + +### Expose `isIsolated` on (Distributed)Actor and MainActor? + +We are _not_ including new public API on Actor types because of concerns of this function being abused. + +If we determine that there are significant good use-cases for this method to be exposed, we might reconsider this position. + +## Alternatives considered + +### Somehow change `checkIsolated` to return a bool value + +This would be ideal, however also problematic since changing a protocol requirements signature would be ABI breaking. + +It would be ideal if this method could have been bool returning initially, but due to some restrictions back then it would not have been implementable the to what concurrency integrations were available to Swift. + +### Deprecate `checkIsolated`? + +In order to make adoption of this new mode less painful and not cause deprecation warnings to libraries which intend to support multiple versions of Swift, the `SerialExcecutor/checkIsolated` protocol requirement remains _not_ deprecated. It may eventually become deprecated in the future, but right now we have no plans of doing so. diff --git a/proposals/nnnn-is-isolated-flow.graffle b/proposals/nnnn-is-isolated-flow.graffle new file mode 100644 index 0000000000000000000000000000000000000000..198985bdb668652be7d77f816ff2c55966e98bce GIT binary patch literal 157991 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

      C+cO*h>h&R{e3J^ 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 literal 0 HcmV?d00001 From 323232889ad803a3631df7f13894354e2a711ee9 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 3 Mar 2025 17:14:40 +0900 Subject: [PATCH 4102/4563] fix hardcoded path from desktop editor --- proposals/NNNN-SerialExecutor-isIsolated.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-SerialExecutor-isIsolated.md b/proposals/NNNN-SerialExecutor-isIsolated.md index 6961770056..7bd3e6e375 100644 --- a/proposals/NNNN-SerialExecutor-isIsolated.md +++ b/proposals/NNNN-SerialExecutor-isIsolated.md @@ -98,7 +98,7 @@ In most cases implementing this new API is preferable to implementing `checkIsol The newly proposed `isIsolatingCurrentContext()` function participates in the previously established runtime isolation checking flow, and happens _before_ any calls to `checkIsolated()` are attempted. The following diagram explains the order of calls issued by the runtime to dynamically verify an isolation when e.g. `assumeIsolated()` is called: -![diagram illustrating which method is called when](/Users/ktoso/code/swift-evolution/proposals/nnnn-is-isolated-flow.png) +![diagram illustrating which method is called when](nnnn-is-isolated-flow.png) From ccc62e9b6dea0f357a8fa4729e7e8c38169cfb6c Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 3 Mar 2025 17:21:57 +0900 Subject: [PATCH 4103/4563] update images --- proposals/nnnn-is-isolated-flow.graffle | Bin 157991 -> 158389 bytes proposals/nnnn-is-isolated-flow.png | Bin 74137 -> 75953 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/proposals/nnnn-is-isolated-flow.graffle b/proposals/nnnn-is-isolated-flow.graffle index 198985bdb668652be7d77f816ff2c55966e98bce..cfe1fc4d456e8156a6bdda297874bcbaae83c397 100644 GIT binary patch delta 125435 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 E08^@Vz5oCK delta 125009 zcmV(}K+wOn)d{E434c&a0|XQR000O8{e)v$03AW4#w`Er>f+#|{K~Yh;L_kCo@rT?Xih_!YqN0MRsJzc+(^_bY_<#R@^!I+=w+Y+K%+Aiv zJm>d$W)n(ltQMz>qCS?<2qOZKNP?uu3w1%>$OrkNuBaRGL)}piBt!lv00p8T6pTVp zPt*(bMtx8y>WliJ{%8P_qk(7;3PXd@5Hu7GL&MPs6pkX$NEC?_C<;-CMoN@|Qr+)# zI5JTd{LP1QA%7}D#mIuDz}tdc?outPL-p>y(@_KbZA3GmH5Gaa?^$rw-Dozn6uA%K zorx?HvK$thE5%l3FOdZ$IgB+G33gL$HE(knc!$$sx0T3x)H->c!&PcGI?T@T{In98 zzsX)*(on;7{Pv#SECML?bm zyUR_%A{fVj7Jq87nXI*DKF(;WDt7>>%x<<@Ov3o$ zg?h_$kt^e14Z;#{CPZ9%mV2VjV`bQFZaIQgq#36HXWVCl)9rP@j1(}9z`LtnuQBZ?n4 z(|3PVmGB)?sMaUvN8nD7K=lt*-apug@Po<0qLro1vwtt=DSI0cL3l1ygbil zaS01Pi5R3pEaH$FX^<9;LeVG|#i4jK4kdv~Wa!;A%HivPKs+oP*5c;N#0-6orM$u= zN;=RjL!WGSSf<-;Mk`FhVsrN@&d{gvWq+PdMKGAL1{MySf^x3CMr@yyp^vk>T=r_Q z+Jr`E`5A>x6uo;tS%eg(=B)ohmynJ4K&Zp{vNuqgc8$Khi+4wHK7EQnCpbq17RCDnpS9q7sDu3O0`z49Vu69m5rXRV^MOBijv8^y}k7l z67}9CvYzAZHiywHA6DhEJMF{d=|-!u-dSZ?jI!(W~0kkA{(yd zjl7Z4(zIElHFFHlXn8GLrsS10&40lulbWYWOSLqg>EA6Gp>D}wd+dY^p=z7Uk&_fB zFPKm$?{X0Qg%9`&8X=>}S(C0Gmy6()k{B;{LML>?>!%aoU*L5#GC4~wM}P0-Z>n*) z5W-ndW``M$bZ9?E1Il;_l0_KHM}yPHVLqc`{#b%7Mrnt$*dXNTq^qMW|bzyv>)uTe0+Gwwq@-BNF@3mS_h#)T&E?3Q{sHpn-dWD5IDFlMHy#BJo zZM<6DPazD~LLPYnUuLYey5s`gOyI0*9dfs($RlqS;{Wl5?ayOm4u4<9J9xkoasx;i z_!?lh&1?~jqugTah>8Eh%Wis!at*J*m4Be15(SdI7lg=f-bIqYu81sN3TJQ&-_m>x zNYK3e0bhu6?|$L7qY@7bf1H+b_jU0(xpGsjqfR_0s3jmvUqF&UfE@b+QXCG*Pzl~; zH29T7Hy+GI6VN1NLVp#g8aV*{-GOGJIp_g2A3ctqM9-j=XdT*!HlZzO2YMatLkG|y zbR2zv&Z6_^Yjg?ygsx$XyI?;YhkKr3W>wl$lqzk31rLRcelpdF!m;U7C@~rw(yPJiL9eI1p7+}A zb=d1uuPa@;bm`qCqDyp_%r3?*t}b)BEbj7bmt9?sbUEMUXYa1w1H6^q-~$>9gJEJ%69`KG%HxeTVr*`{w#q`p)!S z!7aMuGw8JU1xM%)b)j~Z+AV{^_Ok|-NL)YcPs8z+ihOAHQjc1 zd%xR{etv#K{bKzJ{ak+Y{MPxs;rEf>&)oyNE4rt2H+R3Q`;zXPyC3QPZ4aLwLwe|X zOzJVc$A7{eFZFn@$3>ZsY^W?=W|ZA2TO!*gJ1M*3ALvi}XZlzBKj^>Sf4~2i0bK%y z1|$ZU17-)T2zVpl(?BvXEHFOM6nJ;w%D^`R&j)!04GT&MstkG{=((VSL6?F9f>ptW z;Hkk&f?o?h8$yN*4M_>HhCCdyDda@RuRZ(q)PMEld*0u3W6vW!ul5S<72AvNHMiIE zy^i<#wYR)?V(+TnkM!Qs`%E86pYT4}ed_xx?ek`zi=jcGqeD%hb3-?Up6*NZ4ey)N zx1sNfzVG(E(yxENq<%I17WaFj-^KnR{bT!A_J6GZ&i)q$1P&NGz%t;m0lNl#B@dSC z|(}b0UJr=en?9$)?gVP7! zF?hq^(?fiQXoge_d1A<0L#_@THZ*_ey+gMQ{c>2ZVJX9=58E*8!{L6zV}?%|-ZcE! z2(J;E5mh6e9&sp~2xr49!k2`<7ePdD5r3A5ry~xJl#bMnw2f>Uc`~wFq%N{9azo@f zMTjCzF-x&U@om)LsKTg6qTY)7gJLNwwUYXP4xm%%S@d@L2W7a@q+Fsr#`rPgn7f#* z%=fAY6|Y*VI>iRE>FmAi9`<)m%{jP@+y(Vu^XVuvO_t_C&D&b3Hcoq&c7KQV zmr>j(*QiaSz8f7mx@z?L(O-@kHijRwa?GdEgQAVm%cIYZm5-e~cG=joF#}_aF-M1V^_z1p&OyA)IF>FHjavO#Jv)CHGWilWBl#}G9f8pZo zxDDejC9z2jNxPG!$>WnBNj{mzp8`t|ysa^rFz z&ix>7cwTMZ9zzd9v0_+aY%*RcO(}h<^pYvw^n~ea zbFBF>^94SdU%-D+7G1WW?91}#@<+=rRK!#)thi{2w>)Y2zB0M;naZnGnN@45u3Pi1 z&sTd_Pp;l(3$Rt$_S*Z~r+?ZH*F@CJsX04k)Rf1kTyl(etaf7OMCVpl5P+$7Ylqj~ zQ+uv%Y~7N&pX&|vFHQBIYMc7*wD4*7Py1r}xaq4JBn_s9J$DSeW9A*_8g-4!@5FbO z-nr+lu)Aj8b$&+DjI}d;XI9Pp&n#-zf?1dE&cA#6?0&On%>MMAq;J2e)`DL*rgkv>G#Y7&-}W~vh39Iq~)8NhBhr+;kBY} z#pf#xE8kkBS+#C;pVben{(X&Y&Dpg%Yxk~GuUogi@A`)~5F2VYTzIzl*+b7IJhydY z7J53AG}fU z#<9KGdk?-j{>``djobIeTe`P)y&e7bj{T$dZ+l1c&Xxn*fz9u-@4oUM_CK#2pwjB zWA7i=TvlD)b0zD_M?Y2nbnWWgpL_qj_7}}BZ~dD8>lfGRf9vwwqTfgSzWtB!e|&ho z`ucV6CRBhXl*opf?9~c;wap@g>otl@C?$yr0<|LDXwucx=piQM3zQVAP;m-YhI2A9 zPos%|41Y?{WMo98fDp`xM`frSS>Rm>Zz~`M8+<~z+_P0~B*o^e;Y~1HuCcU857BU` z(NO~B;`mwzfGC$c#4gZRh~4u&Jtlg>>KwjY2#CiyjMaQ$q1|3>Fj{MQ=LDgDPOX)9 z6nidkXE2tdiV|5=hl^8Kn&*<_K4mYhgqjTb_kBCDUUVP2AI$}{_8@u)%|j0ZYI_7NK#!uw z0KF|lPoPC;F`&35=qdCxS_)`x8Cs5-&&s3l!hg*@ zqZPvK?X~W-W^sbWSz|R$bB|n*XR}PH)b%iRei_dHWkCkhpGVn#JiVb|-T(ZSI6;QLAhYP$kZ4v{fm}U3@j%*~QCx$OuCNHBwGB9!G1? zTEOJ%(FWkov*Lp;i-uQ1l|PK8L3Hk{A&m*ke(?SECd?jS`X z6GHjD*&l6MGnL1anTb`dJEJCdd@BujZ1TV^nJW$%sk%d5}cP``3h{kNOyzu#1^*Ro!%>r-#4bE9sohm5|lK7ZI#Z>B{qtj!FE z3y#@vJO;-~I9`ThFC53<_zaHAa7YM*f(UUefx)`HT~?ieqL9kM&`x+_?H=gHSM$BQ zdg{%xdBsCl3``Vq_@kntqPlr_mo8Wrkk1NvazgXnt!-RorYy2RTQhEbrhpz;+f{T&J@{-z6-oDU4T7fMK#w!S>A=u zKdxWD-rP@+9|`>F?sp+gvOAb{!o6lxj&S5c*_1zzT_zf zK>3iT90=vJp0bdi?CTkaf^xE_jCTWhn#b8K%C~zQC@*Wi*MA{Tt^;1n(YE7gJ9-uE zK(C>l;I}H_*n=u3wCq3S3OSryAw!>=0U#sKZH2vkyz||LTu%EOMQ@^g=q)rIy^W>_ z`!dE#0YF&H9@j9brNO<^RMhI!akL-3gAM>?|AP*?-|wMA*^cbe?DBl0!&Cw8BHNfv zW%~PlRqELVJb#7>QX=*0;#)AdPTY{1Fxb=8kvrIBuPGQT!WL%$!gH2^Z^+PtC*jRH zPp8A)KDiFN-8BQR?%J(qzy3q=2HR?_*5iKNd&v9)0)v7>iZk>=R?}_+3+>Fy(B~w@ zxtn0}!o0x~20#^DxxM<&M#Er};673)x%;i4Q(H<%hJR-eGo(j*`S>~r@0|ym~@rVsVwgR!pJ8y_$n!*O(P%p^PXSxF?xP0iae9x(-p@SjL_D6jnx*p*M z5h*AQWq%_*Dnye}6`G3f1lX_)twApWRM?4jL-4pCqPX_~ESy0f`$hOsexv;o{IdOW z{fvHs@oBuR z+*JWBCX2(=`T}=7-6A}0=_(eJo4H0uIq!nLu6lco<?PhKP7|LIzmOR4P&gSwCXsrw=zX*n zfZ96rIa-gtKwqK_z@H1~D}ZiA=v$Ny0PYI}HnqRb91wElnrBXwj zMX5NAnr&<}p!4Wr2X9ugY;n1R=WRlv)LP5Kr~3pYHdF3K z%6(yYF+UYzMl)}28Dey?$yx3&PAfJEU5Y_I%*C#$EJwj#npV0$IF0b3RI1z`Ksoqe z7+S*s7CuY#&^&1G(ALt6kZpl%pCr4j#a6&8bN(E3V&L|LgdY; zDUQ?9NYjKPb2yoC|M@L-6!hvN~s0&#QoZsVGRhvH%AV0L|OdAU+Kp+#`R zVm&^#NU-l7oo+{?@ksO)jzmS6Lg`ou8qK;jTC32q+Ljls^@X_i9k8+C4SFeS&4wjqMC2sGL$utHEPvIIU92F>ZB_S$~ORaU4#-<3!EVVgK8k zhuA${Q0fjRB=$+fL4VFnY)TR?yR9jL8l}imnk~S1oQBieDu0Hc{B5+{8!kq5H>h`u zQlA9=2;7l)xRZ76amDrAJ4#RD${#^eQY{k{s78diRXm?u*xqnVad8tS~xME5hiZvTTCm6z# zPDA()8N&D+oj$qsgCV#U*Wr5HfUd}Tm&hVOEHspYV%$2eqBsS`ioQ#D5(HFUD^x@& z5S`gnJgtSP(>rA99l+Gr|81rg!_98hAu`TxHUIy^dVVTzs)Z8@Ux6^Ca*I=MtmYG^ znyj@>OMe{{gwqfT36H!0`Q2BSQu5)jLf+)!%_ADy!%cSrZn_I_(=5PEvjI2V>xrS6 z8<7*3IhC4I(2R;<8A{7&)mpXsZ{nsIcxGGNbay9i`gd^CJ%F3$$V$Ruw#KOV`}u9M z$u|T-_#S*eo`)CVr8s;go{JyA58{UexB&q$@PA&VRVdXOfC}KuR1EO6P&~n@7$`DI z^^HZX(hYbXUWw=91^6+%P;`io2u`%aA;mtAy2Fv?vBkE>g|?QPh;2{&*#>uOc4>?8 zlXyv6C;wFYPQHDY+n$;%2Q_H|HCY8}vIf*-y<1H5Rp18g>Zg~N}f%oEf1u&ZcnxxVwgm7HRFchO!(i)WjW|cSJ12joZ zfy%KOPOVZgS}mnjD{lm~Z{q#<9RbbkYat4L3%?D(wjEI00(}m22z~wonm4Ia^MC#! z&CAm1^*6xT_wXTn7#{~X8(AV#H#7j6g$PG22CbY%bF-uhww2}-oKkq+`9^4a1RrhT z@39W~djj~oxf9y{SEXRMrA%(DGg_?1QY$|~-i%nAk*=wNH&vxL?ZOk#<`!H#;s&`% z!Kd*1_>5pqZk8GW%-)phxP{c5#(y8QNX>^GO3ghH_)S%j6wyz zkAtvBsa1>cRLOz>DS?<8mZ52yV*X}4^a=j7EzmpP84vwC@z585^S+d2hkwPK?a2r) znf}Ij=nMQc{uW=xzX}-dBL1eZfo6pLK$dpz@X!oI9%`jp!GUF=H4IGw=%;9R*z;Wz zzJz}eG1T|iztxpu+mGVzOh-dBBbh7sCw#Rn;QRT18_WC-BK`*|A}~rPNOXnpa*H@{ zLZMZO`C(N{$WJRcN<)M6Du1;?s`2lJ{MQN8M#KrCQ^fxj5hoO@W~+qoCb|;-L;%r?=uJpK)CnKLw?x+W=IsK2+%2iZ+uraXx-}7gL=U&n6WxUX zQ(27-0qN@97bmaZ*I|`FwmgEvT{VR$03bEoPb-+!rU&!i@SNlgZmIvz}F zI+)Zf(WHXGSF1TSL$Q>WgPCgu{J{uLK&WsmtyQwj-^-;EDMV^plbY6*@bW#&hLKsC8Ia#oyw=qAlcc~|TdV3T7YlCVa%7}8p?lGtkdn#BZEo4kJEyF^b zdeg9=FdP7OwP#PbwLz^QtVFe7BP}f}Cn|}mTc=71TYrZJwWc#w`cJ1y+q0(*u%}M2 zr*&XYr-D6gD3N7BJSS||vJBg7CK*jA*MamDy4!^t2B&~Q)yX<`I*KWJ9Gd{XUf=SBXTz}o4BW~L7mgF zLA}3;m`gk;n#czPgZdXU5_>)*7}LL)+FzT{nZzT+0^&)*gmMiHU=kHtO5A?#2#t1R zJRc>VAQlPs@UcIQ=i|h}zc8MQJ2ak4I*sRl!+&@_4aRdR7|-QkJXe76Tw-Rzl23sg0WmB>k}69;eo)d zOEPxKMzppIYl(HldSWBGA{$yF8wCOYL9kZEFsx{6I@)-o6%-3NkZOrlMOJSho^8?i z=YKlX_~(K5FL&-d{+A!ByK$@WmfJ?n`;Kk1s4o&P6WfVbiPwoe#7e=_ZX#a#%PeZ| z22Kl+0WD@aTAvfV?sS{DvC!eLXbrAy_Fp@Q*NB~M{r0YoAMgGf zS<^Scs_p};x*x3S0kEnE-By*RRSK0>BY(~xtR*KHOPW)Fq0}fT2wGT*R*Ac4Dp;wy z#DA(e zEv-^Pl*>?HoYgc#GygMBe1tgKR%VWOdg6b{6Q2ZeI3?>I7IVU%_&jl#R2I;lID7!& z@F9r9#~=>pKpZ~paF-ICv;x*4{A4HC7BDRA-`k}m&JZ89m4~yPyOjUVw|Y}R96kw) z`6y>o#WqQX%)hO7Jx_c|d`o;sTz?_1q7=cqeolPRjz45*RzYi7AyjMp3wJ3v*b7a> zSHwlpaem#dgKa&a*zX&`!QOgg(NT&%_7d?u@k3i5`(uYbmiS55!A5@T-vV7h{7PIS zt_wjdbH+|)!gNK}m--zE^0O}td0aOx!3he9zsQ*`d>%%FpFxK(%=6@&R?A0|^ zerhu&ZEohLi2o5%b9+o%s7ex~l+j#*#WRv41^ttkiHVtm@bt zlW}BxvpFUcIyA@Qz#Q8;5y?MfXj|f$=;)TLKTV$OTew@$A$JRbyMIeM(bs=>_s#vY|IP49NNW5Ety+u5O0t-oNE!vvh`RaP2Xu?? zx`jwgB1>9CVseKfQ3@h4qf_DkA!b{ga`QBsv0C`?AL}%zieEU9yX>vLL><%UjlA7I z5_bs`I0T?UEH>a0T#cvWhw;;RJ>G)%;1l>FK@h>jaDpS!iGNa}k$8|;M64t>5xa;p z#6{u?K=2U&8nS@#Q^_R`(#u{0 z0;&S60aF5=41ZV=uqt3}z~O+e1Fi;20{sGm1H%GG1gZkZ296KR2%Hf(EAZ~X*?~=g zy8`zF?hiZ?_(9-@fgc6_8u(k_A38NA*PUu5L?K@A&Wwu30WPoA>_r7%^|OboCrD7QyMxk zbaH5I=v|?+L+=lr7y4M}bXn+gp}Rxh3;iJUQs@t%mqULF{i84HON_WQ z;_`^!M}Pbg?iKDIt_)8I&ki?**M`pue>i-3_`2{-;hV#Eg})tsI)aHPh?p2r8c`Wx zk9Z^Eort3mry|Zod>%P2QXe@v(h^w}>57~cIXiM)JilLIIDu2pBHBe7bOQ|)~CTbV8m)cJqq&}d| zQD0L(&?Mc1meKv_LG&=1qLs9o*3qf7fo`DRqfgOi=uhacnej{kQ^=SYo|(fuz|3bB zGrO46%x5ZJRS#7!)j(B}Doa(Ra;ffA-K%;|wMq4|>UGsV)dAHh)dkg8s_#_4u~K#r zJAaJLVH??5>}>WCb|JfjUCVA`x3jz0z3hM3pE=BpMWbk+=z!?RXf9e8oqrmg7dh}jymJ?4#=H)B4D`64zTHaa#gHX$}U)(~41TM;`gc6#hxv9n_z zirpIfdhEs6Uvz}dTj#6mp&P8L(oNMh>gMU5&@I!g(yiCMs@oesG=6k^bbNjMjDPrh z;^)RMiC>vGDbbnuT;k@$R}*(99!@-+v^43BX zCv9HZ{InO-Hm4m=yOxg9rRm+%BY)Gm^x|}5x-H$AJ~e%2`hxUF(-)<$O5d1%G-Fr> zmBD6=%81Y0n)zntTbYM4k7RzAc{xj#H99LcD=sTDD>rLGR#{ek*3_)VtXWwPWUa}1 zA?t&zFSEYS_R3Do&dJWtuE?H}Jw5xb?Ah6mXD`)b{ZRdM{oVTe^bhG5=6_zzljM2j z^~m$jQ{*vu1$lSp-Iq5v?}@yp@|Ndq%-fZ>JMYcBck+(r{cOO7kp`_H&X8|dXjoxb zYuIMkV>nU~Cks9-IA3TeG!^oNw!)gihYA-IzEpUu@cqIM z3coD;rtpWt>qW96|Duqh(0`)AMTtcjMfRdQitZ_Tvgl~h*`o7BKNVds_Ad4-4k!*Q z9yxK|#Fr*{P3kczXj1PC7ViKF8RLX*OKd#C6j|E_cxv}UN!z! z8d|C@9aE|+O)a&QI!fo2K3cl0bXDp4(k-QLm+mh;So%TfXC@z0v44p-Rf21qYW{=o z%KPyld@nwlpU*Gmm+~w4XUqM{`;_-BA5uQ7Jgt0L`MUCr<*4Nc%hgIrrB7w|%FxP@m64UoN>!zboJ)kcYs(pw3b^Ag4N&EZuZ)*nDgx5sXsA>{xGGeCeo^ssb+1lg}GF&=-Ll!zpo3c8(Bx!adk;`S@p*}KW6IrpEXGH zjX!2ufBgRdP)h>@6aWAK2mt+rV_KWJ8nuTy0ssujv-d4@gnzxmn)RRjfBiE2Pr1F)VS#sq4}% zXrPiy`th^v_NLw$JXge?qWRH1xVnZ_61CU%16n?q(SJEi$?clmBlo_Q{WX=buKN5^^9r7Frxc)-Xq2qQTg6`-K9sEED;1> zx46Vl$B}Nz>mBfofCb4exMR<=3Zc?qy9x9lJ%Kyx@zDM5ZTytB;yLoA{j~`U%X3=7 znYpODA%Dge38jYNBW3)TQccb7{bDS@R;{QFog6^P0EV}r=~G{4MKUesufhoTuDyt1 zK&gH5i}HK3%`>8DHYsu20@BQP`zcAV5^$bYNpM-CeN+qD40R%_+(U`KNv8SQ{es?D zIg*`YNJ-!QEi$KdF$OcH?_RzaM7E)q~(zS3roaRkMLHX#VMP>@(19xTZW_!h}mCudzY6-3nKOz;TsU-5d%7<)X z=YQ8$x$nU*$|`MDRg0k#=iQ>qSHIV7d?#yJNthx0P({1bsq=N0CHDkg<||T?lJLF3 z!$lKeuph(Zu7g+TF%EDFTlTdJcY7@wzt+AM&K97<=RL@k0dSEockCPoZV%DcR807K zMpb-l-dFFVVG;RT;x+zF5qrCa_alkLw14TdXW_z3$h@W5l`(`GB@lZs%e*bUXYP;; z@Iu_IPtbQ-aCYGY|2-`8IA(5*wH;xHwM9RpG103@p*vw!+T z8?zuB%F9I9&j!{zL6D#>_*ID0#Xtkfv}$LqZ+Kd|p0zbmnHDqjCea*M*ba}8-7?z@o<;K^+&#Q>>cUK689$Z>Vyg^}ATX z#5j>q+a1(r>>XN#+%>Atl-?(kgodtI>3nKiAXNx>v|nSWT-d+=>CQNOWq;4^El&i* zMe@A9aR7oA&9H@4@C~I;RVFpWiAA6_=(`3yj=d25)R)^8v1^Ja%@}I8%LFk!S15P{ zptme6#ej1q;4ZX;7Y7hSYXuvUeqf^mG*YLhSuak~X06NP^ehY(RJ;@Oqj#qaJ?BEA z-vT@yR|CEgRnv{VXBgdfn16XN+PRDJ3qu#rzF;4NTAoK8I6c z?xSQSLKV7cNDYRw2i_TN5FPV&>bU$>(EVP zpP(eC%n_Czcm~P&?|=Vkdvn~uJ@}NeV6xcCWCu1zPOJkGJu64QNlIUP z9(=1qc&n0QNcz?*`_e7*Mb{N-Ztyfd;W;UZ)?sSCN9)n`K;GbV4sdzQyCid84nmW< z>Jw>92$wM1;8%a~P3w~wJqXrL{H1^j;s8-6X2Lel04TwKZhs$z|J26d=u0OdJX`~4 zVsGcVjfDjqbKke+e`aKp#77qe-++{FU?lGQ5&9Z8I1g=xk)}C-Ah_&HC9)-d2K@Xa z$wPtsQ;SXckG{}g5K~H@c7uX^gB1)GV6@|hW9xF0+%7dY1pi37hA=nnOT|7)51UY# zOY8pv--{CGntuTj%?*U*+lIqjNv4$N2~aUwbenz0iIPGUe)~`D3emRlm^DB6WcRnY zf)&Gh`@Cm2kpUskYwF5JyHZqJ&H~O7RjR@OkEW@a{Rb|(SxvdIx54Lz!{ug5l8w$R z4EotoRpiYrH9tE^_eMTV)>&knek%q!^s-#P*+}6E=6|owAbw%t8L)r<$%NH?F1>mV zUw6yf&jGMcbvb~=Gi)YX5Z%E6cA}6JxAyBgRjmA0>wLV|y%QPJ_X28$X`xs0ql(8JMUcONQse>bT?sltjOW@B|Q}+C-acYQ_VOmKVi4{7gT7N2k&`yW!BKw{scB z0y{P)d4F@hWA3xJj^y#oJLn%0)2Y>xmz^4yEme-Vq7bm< zwWsZ*l}Mpr1#t$cPkfgBHd)m8$?I=!BPX<5FltO0R!rMY_PbxSV(Mh~>|Pud8G9(# zE1_iE;2MvP^4Z+srx$&0v9HfL&BR`RY&5x~zJI#50Kt!SLu62)3|r=I`c345{yd{B zmp_j^eNbLqbQ+Kjv<%s& zH-Ft%9Db`p>+sAQ#YS1#ZV#&ibw@9tzZVx{kQvRnmj^3M13c@F4p>0b?7!EqGEdR# z7BF}R5|V5%2gi>s1a&r|gvA{lJhE=v==6oFDGyZ~At5+M)0VU$4Ikig!r* zO7AVLe$WLLhmma)W{*M*SZU~e^k`7QV-_-2{Ce!$Hji&RIwfrm7FjAwZ_{aQ?OHWH zD|Iz|=v&V~JCSm=LZBU`gPi9=$bZVK0@;OeH=zy9WU0rmt24cWf}cFOYyOoCR%2F! z!YDS~azA?@@p-CH1A6X!fJ-xu=Ycf!?A4;P4Wy^pX?H(aZ{4z2^V^Iy*7I_bXZ$^n zapG(8vw(v}wdx|4u9v^gSlFO{)<;8Pm^usy*nZkbAEfSSRaR-ZCW9!k41dw%0B)XM zDwj-GoXD=ogIkVl`z+1N2ONZ}FvD0;Kg6aS+M98`*r~#KwVG#Eq&kb-T&?U$J(E@~ z?@ZMs$Nj6DmkVQd@z;ch#dJYlL3+UmqIg|O9OM-;sr8JiGN^i(xdA_^dAcq5*=)z^wXgPr@b(}P>fEbhGuL_kLgK| z2mzjWae+gHSB=d(Uedxa^W|PKm8FB{G6Gg{nKnK%S1UFlf#|nH={l=qcnt@LY}4>c zQ?bu{h0vUdeN=?-^)kvFZ7y9_oYxjS^~C1=)h?7FJ(QFZrNlpiR(}lEn`5YNIZ_F>4o`q%D@OlRbGkbf|j&W*~W*zpuKDvxlp<=gY2+JKYvA@sF=yi+HH#@Jsp4h zt~@ZkIC#4J=oh&ArISh_6buO&i56lSy=TkNx4fTnx93wj)2#247I$miT=|Aiy4mfZ zBfD-N0er8={5z9cZT=uRqU;YEQ5--V;)+Kj_jS+e1Vw&zmXw?|5m8g~Go0CWme=nHK?#8Tpz_xM$V3a$l zPrbgpU5A82!~)v)Jufm|v>Pyq_U-bqq5*r~|{`$FUz-z;93X zz$sR+O}*drTo`0&WVQ|dyEX;>79tkj3UYvbO24t{)`b=hP&nGg!v0a?e{2YxCOG#5 zx9*1K=YIf|&AsfWK3{*>|3Tvt{UKd=^1E9f6~ib8yKrh@ug^%@bYdPy{YyDj!5Fd* z2T)s}+OWGV{W!o`HLUkvTVp+hp7z7cREA#l1D?GKuZA;oaP7kk2^5S2Jf~|dYi|Dh zLu)LBH2`tniu_-G$mDdr*DTt z&Hm6MqTNu(S*b8@-3M%;(Kf|kUfPq$3lv(+&WNFzR+*azll8hOE}5j6rdxO4dDU{s zt{@`>9Qi=t`ZCeyAQcZVXz_Gmwj_l7i36BfBRRl>!`U35*Zt3~e|G(c@9Omiz0)2& z3xAPhf>et)3$(Pz3oy~TVE@9Sl)$V5EgLr^LhkceY3=G69sZj4f#=uHGbk?X3W?Fy zxwu}+4Np7dxgK|+7VL;*wc?gn5FCIKJqDwbfiT9OUH|O*kKR>@8!0?W`R&kNsXK9i zkuPxcWCunvo&xr%=`?B+e4&@;RTMo{|#3XCE)Ke2w5-$^4BqWkh#}?jSVyV)c|j1f_<<* zpZm9-`-80_Zd@Ae$g0CFFaKVcaeretI_qXIhIWL7ZFMD8ICLoOWj)Vt9<3WNU-g++ z5~)h;NBYU1(L`EAxlSjVUWqssA?mbW5xYwb&mC$aMR30GNdMbo&^2aC1qb-+wA?pL zO^P-s%w^yvq$0zZizyxVPDH0SXy@5Iu_Y93F%GXEWf&s*dwd^Gw5%{pzJI6w9dOJ4 zJdXM=RM~uaFRIh-qvK4z$otdZc$UwV8jZhw$2E{eQSP)|=iP9E9 zGX8QaT_XAbeXX25fT@#-SY;|Nu%LewP9jk=-N>+D)9v23UiW_gYJnEOKfSrnyN!(e0Y1>7$# zX@b8EKOwe&BNM6nphu_C?{L-X;RW=lg{P{vD_J(lHWzAKg`BpT-;zyiF2{eWyn5ND zEdPYswM=Zlk;N#mc`yd8h@R#EqN^zN=fTBY(${y76zRBBI$5JyN*ax0ZFl+ZZS^$S zblK@>#gy0*2D4%n!#qz2c9kbA*i4=;R{<#*#ri;8h9Z>y$L=pR!imbextnXMK3Nk_J-kztthP4Fy_d*eoOGs>J4YLwmOEos55Qg zJ5WI(M^+NB(=Z7XG@JIu@se z88uQTA;nhYo>$~APuL@9O?uOw5(I}{k%otwWO2u18Xk_iJWqcfChNxo%YW;77XPU7 zuT@N`4DFs(8*7y!o$%_hf0I^zNM0+KabG+-wbM)D97!@&7-1cLw*Q;fv%lW5b?1Fa zM=X%V=1anjPEeIQwe&oos;?q1);T=bb|9L>n;&rPhP+^^EX%7N`}nU9XaLDeXa0-D z(VMsv4RKhx_dI_A7WcjJhv@b_&}ZJ0Rolu*d(NiM>oE$9713@0sx$8l5O~EVD_DO4 zY)^^Bm;aLaojK=zUG+nS$u5Nd%mw^6|0|@&y^@K9Q%BTge`3#kFi?b#r3a4DfB!FTlcxaH=1BAEt56fFa z6G08^k?4uZ3$}fPy6a zr1{V-8GC;{iGu8-=wW+n`f=M@s)}@aFT&R-?YtCf@wP=54+`Et*7QOgYZgn(txBZaPJxVm(cV(2u<7IF6rHtD%8R+Vc#h9$506o)LnGC3H3 zQ#*Z&3*NXn%0H|~F~9Ve347ZVwWSMG0$o87dRdR58UsFBggN`VDNskXbLzm(fjvgq zO45IcO)8n*4fp6Z3t3DlyHYrcI5C^!uCi0cNo6C%oy%$HH)u#`5)X2o%%!o}| z)I0aG_r^(^z+I1dB7c^ws!}BESWm#)7!l?P>dC8AzUK%t>m;G7=oM7#aaXtw_{~c66`$rU>|7G`n@lw~=LJJ@jM_Rkj+(V5ai6zwyi+r4NYRi8p zYcD2F+jq?`hGf<>pI&LYW|sJB*YWp(QcQNRGcATySUVVcv5^wf1rxXbsqkzx1wpig%wX=}03k|!9q$ShOxKbD9kcn)G>-@xdQK)UjQ}(J&+Ps6ESoMY2 z-j6mL@*IF~kS-WX)u*+MI#Z<*RAYZSqqFQj+{*B+{@mXcIC406`~LWY*5j96Yw-R` zq3e)f;=wW`!v{?Dg%h{Fb}8*1tNrT#0lTIin*B}p+_y$Q?WpXS$F^$b9drItU%vnC z(+d6(!=ALk=g__dTk)5p+c1TGU<04sd_bn5xT) z)P;SwcrVy-UFLV~x0Spj@3onBbHh_>^ZV-xJkPq+wbqYSEJnMxYc=h1+npG3L_LSd z0ivg=lPq~y*DzD6{deK&sy|HV0DhmHi8d`@e;o$nFm$cR|F;nMKW&*L)?O5R1E&!3 zj&XoOH+nW#S0n6q5$k{662^Zx^QX!`RsK&=IXOfWV5?0tW#~~SlkeE;N0p*pBlO zZT0K)d|QnmXco`s}f)#kmvFOf-=Y#=2jZ>NHBx7=| zxZa&3pK(ysK@PyrH{R-%Zx>v~eaV{1E+=Qx4-W_bKi}BDTTgH3ltQ2ZjRSPUHr1fI zTqg*9#$_vOa3l5`*!q8M+%^udDuVfSaG5vqf6wpqr@}uK{;BX!g?}piQ{kTq|9%vH z!cBdp6^^o@^pqa72q@q+D@YZ)57rPaR!!!|Z;SBsP)U`tUG#g~V;jvIeOdouN~zWy zEF4@%!|JS9p>%l;VENF7_~_riG59yWaQc7FSD62|@}GFE3JHJ8orwN}RFHN5hZsx! z-sTdGhdU7lbSv&exNv}x6u1eBJ1ObT+<2V9y8`)j;m5}CDlZ36^P*tbZ~D0$SScLZ z{@-*q_1`JHe_#z66v!LcCJcfvVZUaIa)9UkWovK${b@9RUgGtqh5Rp){{jp7-+eU~ z_ALiU#?*qSv4>^GIky?f#yz`DuZ!F59G(U5;D^*`yxpyxr_r1iYzITf+ zUp}}Ey{Sv)XSk!gF%ryuCFqa#6S-7DQc2SOEGwrVs|c;zV?GKg+3)T+gtce6q{;i= zOhi6%%@>>crL|r{t7jGB+*77x7KWpsyT4{7%tOMV%8h@?6EysB4gR5AeYGjc3$ZHB z;chX0D8_Z=M1iA38JxFn90%CNeQq0jkoZce={7yi0J1aqkwfrs<#_@k-g zr(YEs)tK!XsMS^ReH^*P<%WLv_(n+rf&Ta4Gl-?UyU7 zwaUytb7N2T{+-tXZoYb}otAFM=h{Ey^~1T{K4Z3Kjx1>NLBTvA?53AdIdE~*LcB&{ zyMTYWkr42uY>5~S*5W#mcw;dMN9IwPHu!@d0%N+Oi(5p7@}Km^gbnmsN0Q+fVl@Zo zStCGbMJ!C4@(f+K``4M2E>W3ot(zrq&uhMCJft3Y$PdUDNskHKk|ia&EHz>?adTO1 za;&m%t?>CIlQ*+k?^l^JCG^UJ^It7q#I=94rTHxx6bjoAn(hj&?Om$M7h5ifXGYLV z7`Es!j3VgH3ZmU+#oJhpDpTyzPxy7=A9)#FvfR1z?Ms#9D_4^*-xAuJwiuv&Q0`Ub zaVJ(W3;}+O>}5+guquCu>dvco!X>74vL_D_{2Oux`}yP6^TRjOp1Z8i?p3`QXx@M1 z06^+K*et4(yBP?k1M3OsPK;@jr=#MqbYz#DWR}_4c5-ZJ8GaRe&9gG zoln`uS-biHR&fDNd>3r4jK%@B-C%z``;LrY-oBd$rU`E;O{X6V z?(DOEck-@r6<_+c{Ov6Z>Rr0@E4Xc~H>oFWLC@bGMHS6@Vq7**DYaqgYFU2+8-dV4 zf^UnnbET*+2dI(hS);qJ-OrPukFb)s3-~8f#mk>~lVOHuVrrZ$5h=$95(3w>f~IAM zj`mzIB&TuR%TsD3yW7&41DLB|dyyaWU~~;gH*Sj1*|v+$Gk-#cgt>3D7pYD9fUKC9 z|9Y({^Rq+gxj3C~z6zEH5;K3U?Bd(9c;KSg^4)l5FY5sZ@W3QrKT1!b%F__6;>?M! zz2Nq@1FUCm3cflY1gvxh%1sJ==29dLKY>G;aBrjOz$VeT)m{m3Z?S^|Ho#9tSW?Si~o zBAdVTm{lJ~e4qHBZSWK-OS*Mc-1YL_cuALFM@*h-fJ^(dyulQ@6INT&Bc_OQpyL=u z?DsiOj%6hww+-B@+b$j|67ej&8fpJp3z_OV~M2JiRTc;+O%jB%RjG!7y$ zFUqvS>peYB_*hu-UxASJ&d;EURcOUxfcq+ROe=oj!ku%Z|leJNpwLOqdG2 z1#h_n1vqtZt^iKCr~}fxKYYhMfCzfe;i#Wq`7~Hh4@LWrkuqq;rNF%T=RL6^cV6;n z5_-5x_$L{{0r+pR9@h>+<|lh%qCt_Bjh^TU+L%T9w1uXAkwiso^5>cOeXy_)S@pm( z0C?8lW~s7?`w)LWWq%4o`U|eEbWC=*T&xQ3KDd9^kJRm&-ugmKby~(( z7@jk`jsAoCgfFWAWe`fmk~ETu9hvJx2y%t?_r`flzlynHZP3e~GCT*YkLPL&mFjKL z;QggvfZGSoJ;hNjDk5n)ug9ShOo!@7E9$p6Z=Zt31`~ggdr_@E%DMjPd-u)Xc6wVP zBAam)e+H#k%q6_1VLm5?th{pDEj`{FncT>^GK47(~r8Kym<%bp@+;!lo5ff{bdkZ2@Q)6;sCdg ztD%Q!XQ`hN!@oKkW?KpDZNtBhE2vm9&wPc|9N2qycS29#4$A%b*UTv>*zA?FGK-O5UPVF!s=wWaaPK`tT(Wa z*E;wjO}%1?R}VT2>x?=aFt1FQ2-Vgmt-?I(%L}1TFV9T0=zqOMMM1nr$#}O1lE`*tzlQ{v_Pd+5E))=wLcGz~;Y7Z=k}j z(*x#^AK8PotWd{>`vFHvYT{RH#(iR~UYXDyxUN6j{qQhw3g_aLjRV!+ZoA6gcFSuCLhz$Cjc@=L z-Kc}~hSf`7l~gB64&Q@flpiJAT@5lnRE$PyO10l;)IOK5)p7@5XvVYes?sNar$>*Y zWifcq36hSHgjDH=3H)S}(kAvb?%th{6GaRGAI+B!SnINFo7pOpq8b%jwV7RJbe z3h%vSLSi|PruAU4b5RMVd*dbaW=@s|@jX_4U~D01>ewK15@zgwd zPpar;>~9x~`F=twa}M7t8foGsoL*Hi{`r}FNP2%=+f_n~h%x_Vo9#c@Lb`v9yY3hz zBOQw0ys$mp`pVc3#p7m^+R+$KvY&R=ZX-rE-0i7IT%^RN%dNANasXgwzQA^_C1UEa zWKohBec%3E#xeI}-**IyFLWvGstP^)sy;-tt(ZS|8k;wgeRoiQ?%Ug?#2Yprh!kri zUU|M`b&_JzIjc`MD?L-Zf=_>m%^&-m>EK>@zaiA3G~4)md45*h1>d}b)9(eBXQVdm z1j%Jo-F6(s%1V$ua;!4P$eAkGEvlN)>PvWZzY_oX%QcT%^;f$to#9d0KhwoOUEClR z_Z=4pQm(Bjp&%2WN2r(-!Tq}QXvLat_oMoI0kvc==hh*r!=U?WzifXJbqV^J_tQ*T zs@Sp(d>mHwUB}FRy>)Gs;ZbSynh>;k7VfjE;3hk?{e;#=5RGes5s5Ie(~DsI8z%RU z2XOWdmdZ+QbQz6UON!sLWvCoK7PglA#Sycr$hb`>&?@OVRQ+@GT>e*7-AM1Y&;eWK zqn3uYh=l>SSX9E^L)w4xw5J2WHtKQf|Nog zQS|30d%2u!>Su@7ORm?uRATw!l$N8tc3|=M3D;cjJgQfFhCEAt5P+KcCi}wkh&8RSIt%n9 zz7-;)(HLgnnL0($;=&P`rBSdA@UwMkjiH&QUYN8=@U(@MDcB`IFX3C9Zap zApBq8TYqTya{x(t-g?Na$L{!LHH?SscB7`Nk9MhMo~THUeG`-~R)+H=;!ED(s*#<2 zOs{!DNS%|(Wz1^MwG7X=F-WxR3kOj>tpLK#zA@^Er(LF%Aiit9#Rbk zClgU}h1b1gD|DXKW@Fvr*JYo_T?%{@uW{+kVyd4lNV$Sco|UBhY&Jw+9q zcbh^SVyS;kiHsG&Jxn)d4fvAYuJ5`wq&}FOZSn5rGr( zgrk$Y&H0uUw~OtbTi?FbXlI&}4vNZ9W9m}{+lf;+($7w~^bv&H>CyRQs)^uEt37(H zr=m@^CT!kF-adN-`)v82viCo#(Y8*crP4bolXHJqGL_QO1Cuo9t5sBj2&_=wcR#@@LBWYf`UyQ2k7kmT?*-!W+0p{9LfRuzVpz?QdAkH5lBzM zF;mR`s^uiaX9T_z{O1aOkU4+=m(_Ev4XoWUKHN@}Y@dg(LTXqo{qe!An7WX>3#jF> zoA8tjT5+Nb(tYuetS_xGPS#61lW zdZ={46O19&Op>D*mZ%f2=wBAr@Xpp0rJDK2GS;KDPDYTA0# z?W;!vgS2aT828Vhq3j`8y;ToFpM>nSisk?^&m!UU3X1Z1Tx$er`;YPb`edhc^BR92 zs{Bjo0Q~6b6llPZ2^~QWBl-`sBAFaM{t&3Qh z);yPgWW7L0G9~-*0RZwoNs21AYVd+RzHiPlw4DRg`CxQu#25o!WQphK;{+}kZC7|a zJap+1Y3geBt{~S)3EB@p(x&HF15#{?j6Cu+3 z*<10&t}5Sn=G6O#Y@sP?O#3x1d0-kWh;0wnBVLVRn4G7y;7=`?dueR!H47oTeH`5* zfBIqM@R0M-H-f8Vsm)S~Anh@|h2el6YBLx!f*gVZ^e=NEV9naCoY{Y)h$yC=hW)nA zO<%{yi*b=#MvlFbk4W6qeFLZO%gB64vFdfB6%d{?t?AAx@14H1h);yso2K>(nFxiK zX$~+-BTBCT=g^l=0<5S4oDs}9B_6DW#6m@a6KEN%GK0QDL{Xx{@XIgvT&^7;R3xWu zXOq11z9b7ne&tb$x=0tSbjNK)E9$(f|7E-)O`UOq zv!2G+)V!`3*?k1R`Z88l^5Q4k_kv^{sm(%$0(%^k;j;gDrG2anoM*0lC?sxHJbbnr z^bywzyU^#Y+L3Sjlbq&qEs@{7|E;p=6vYWf%VgEy5MW%MHaCBGbAD}MPLb(M+2$yJ zc=Ctr<#~(AY2)UQDyxlik@rl~MB|dy1zh=fX$A4jRJuCj9D5wT4d$t%PrJ&(%Jp{j z&p#GvHjMNCdebNQlT)GSr*0AN*avnvD;$?qxr1YC_7M&8f&MkzgilFjc&o=USA*%Pr z?u{C2Q$t_P=`eQa=pw`B9S3L#B8yV3=Yo>ae9M^a21mX#tQ3M{zx;aHj}ZCng*-NL zxHQA|Sr_*0+{UWb27@gWMkOx!w1^s`KdL-gzip*h|BjL!`FjuDm zoZ#9(PcZo;}sfM_KFhNYRk&o z(q?O3oVp46-r06bYY8qg_51cGvO zD9(uMfyC$BS*1O2O&Fb7l#L;QWN;R|&PF z+1~GCoj#hzsSdVo49$t0C2HT8OU-=*sR3_d@Lc{!)$M)_pA#cnlR}@ru63xZpEP}f zuqI#AK3#Q=Vp^ZB{Ni@JEiJNv{xbs5`CWgwopKe&gW{)lbA!U+7^nhLG5F!xM}!p# z`832$uRE zJo(iIjh3Ec>>pOo@7XM|TgsTHe(xpuAgp`YFiJW-{;Spa4=sZ02YrQmNp&0M)$4z= zb6PJ_%kM`$cW8N!T|euZ*rpJj&cBa3ygr@?!*# z;O_8KQlmC+nsKp4x{R&tu8B!-+x35q`NNn3W0_92f2sdZ?91ue5q%2}IjvKWC&P~} zJ)U&;PW9S=w__w=a}`9z)@_wcB&8&lY-C7c?VfH8)SI%kJ5(^v`?h|>kzk?xLO~$s zy!ckpRCnHAdkb(%u(>>3LrT{gUcUpgqg9t`WIs5cBIx0Ex#mN~oGp56r_F!0jc1oW z2_~O>ASon!_;6AaGWPd98|^1ya|JNPA38Q9uv2!Gd3C;p?%deW;-#hKI!dnS-}rPq z^N85v>jt6+Z-~8pY1ae5o_lI-CQ*8sk`HOC^n+9xCpwJkW);%|)v^iOPrT4t*AV@g zaOKH;pFaG(z0-ScU)~|Ea!r3;@st1-Wx=X~&6QxP!KCjx77OHeYQxuw0Rp)Jh~fd) z07=!;Rpt15n`yP7A9kpU=#>X;(c@w1U4n06kHH$;qS$=wL9{fTSoCF}bcF-#@Fwp+ zB&ru8R3_iz-{_(4vNUjC0ZE8>ca_J9khLKMPXZ}6Pi;tXtmhRz;$wg92koC)A_}WV z!x2YNpM5>4bps|Z4VoHM2Q4SCVr_}Vo#o67gVh*r1GwB6)DAx@m zOl?@i)HfrnzSkQW(>mpEVvxK2x#ZX_+lf50v( z>bu1|@w3hwB0)mMBMpm+D?(9@;pqNL@L^bw64=@o#%BEewEwT(bCG!czILpJ3Xbm4UX8blD^gR(VP~nQ2Ab@RoWLvGx8orFnbJb$SU{Y zE_32kiOraiZ050t%cWt#neTquq(*Px?J5^CkE9-Zbo%scmx#2R5C97++fb(E{$*2Q zb%|lJOyn`^B+}1=E`jLv6U>-H3A^L#3r2G5o7N?}^RZ>r$*D+*|<%sIlp_ z6eEAFk=2XMsbU?!3JJLhuVEL=GvzJf^RrA-#NO(Oyr0)m?xSLTjN3xZ0RE zZ~{#%k_#0Ej1cy&S+8rM%3i|meWM0W1&4n(CJydn=$|z?y!tA3>FCiP+(`5%8A~;z z`Aiy#&gn+vJUNDbUDodR!c$bW+z>%Sd=-89@_ePhrnGme{yNX}d%;yvDf$n_Uus&y zT-Rb?Vr`Ok-GXt@BlKLex~6gUbpCbk+fVpZ5jTaZ%~fV^8Ak$q({Bmk$PA+(DmQ;j z>SgGCX}0MBlXUpkqICD)v`E-Ru5H?rIPliw$g4AX(~bNlg0eoc-*)d1n}Xnf(o(rA z_$u8Xfa)C_$`5Ty&q*y0Xr5bsUC1j#!7vT8=f0MwsHVv^5t^0TLz7f*bo@F=J>r2OR#@uzG6Lr z`G6n4E==}_N{}#}u;@v=VVrJv7p)=a-r+2Sr0J+kqw{CN^uXTw5691yE-4BcY%Gxr z8Mf>x_$wk=w8ZiH$^?54-KKInfeaNKBj3`)7e@M(yzVenvAZc671ovE;`gxWq~NzC zDJBP?cd~`l8763MKweuPL&<-lX|!1CpZp-c_1Zk+LbQis{APnq&=!9`$-^t>$k!ci zDyxp0jYAqz$m8?il#L~2xSbdi0h2;qn$*4(g7mq`f5RB{sPS-Blv&>AGkyvd56-)k zUVeEJc1;7j1nUL4$Wsp1?9VMmxzR`8U#DMQ|18<~Dvw~1F8i*vNI!o;(vCb3e&v8~ zG%0zWhjox@$dnW$4J9z-r_d9Dfu&v**B zu%4ZD)cHfLXeo=51 zQ;Q)_Qed=e)C*a2T&gAf^6;~=sjxOkmA&@zo((14?3~l>(=o}x6%c|ltrSv?><|`< zrv|&R#p~L}b!tiONN8uRW9OkZx9r8?v9~=ltxHE21UI?SW<+Wy!@QH)JjRwY4Yga7 z8*(lPOLYiuHp71}t(&l(+AIyNt_|Fq{NmPwOa>d4R!Iz%fdx!Lb6c**f-pw%3+Bcg&CK+fd&yw)cL~ zNwGAE70vZ&y;AvPa?^=q0AMG+!1h7t>Pt*Xtsaa#Ga!rsLxE%SW6U6zEK!dj1-0?Y z#aeap;Yok#k6l4KHEwSjmtMVb)ea4&wO--?{1U9{??n7SFDPKdr_*ZtDmcO6{UMz_ zvQP)j6C*&O5bsdvMq)IM2ekmQkIfV7Rl_szkI($=dZlvwtZfH>zmK%k1JGCMb=}} zqn47$Cl$eD++0ILKh{dH6B&KUyztDPox?AQ2O9GZ1UpM^tKVxzS$nATysed$4x1~5 zi*SEIQQ975K#h)5zmIY#J!l~nn$cYKI#nb#{q8Hh^V8&{hoU2nuY|%jXqv2Sv;sV92+Sl=_a2bvCaW{LAuIXBwLl~ z3Xz3M(FdWj=j>;{lzV>USa%9=2Vis0hz-vR z8ERjS8Qw>2^YSWXo}wz2Ic?i7)i!+7UAAlg`JDmTd&}p8iJ5ds|ipbeY{#3iexWg`~;N@x*vt#b&$7(g3JFfKR|5IAAJR8q^ zO_yTWqAs&)VXjC?FPOlZ4mWVk?Z|Vy+y3c{pNA0%)%Uje&7IU)&AxcshYIm`-<4(3 z-R5visj+#(NR$>;{v@lg;zEDf0zy;$7~r5Ro`c&}cS`_-_*)+=Jx?H# z_JSLUtTshgM^vF z&V9yF-MTu5o%CS)hdvyhXl={-)NtSGQd83{9{l}Wh>ky;&&X$z+;#Kv!ie-G=dL&MNxh7bv;Hp-ioj&sE%VWozej+@Lw zq#fEnv2s?fWPG6R*g0o{xLfGnyCxd;kl}`&fbD?F&~d*%qQkBJC~U`~?CIgc_=LP0 zV`M|Ugt#3f+lH)t{JFQZFMQhy>=1j8*P@#JN7BR$$hf-UULt>9_%vN>4uc<~IgS_L zDEjvJ6}gENUst|wG;4C&Z9Rm?MYZ#WnkkRzzDboIcz#8mK^rrJe>*I&!<;fqnvG&8 zBPO8xP*>fd1Gg_9kMoZb_s;flMwR4#)-rAB8I0eMsM_a~Mf*f2G0fO+A=N?QOf%Y)Ik6 zGk5OHeSh!FnKSp^xij~V{FBMvYiF%zJ>|34UYqJ@{MTm3nB)zGBk+l-O*Ux)=K$#N zDTuw)Ck#k%Pkx{IwE64MXj;jj^!2Du4z|X0eNkNBDwPPaeWxHXWDsU4){!8Xg7jR0 zS^`e^ezSj$oW5b(ncXSe{;eE|aGcAt7#0VI`yW%Hc2;B<>s%-=kbY{JBHqJqY=}mb zx|&_+aHw-LHM7P^In-qG46=_3ycklzzbfyv!H7_XO^!C*1WY=R2>BN&{iI~9% zA-#hWi`l6>zH?8E^eAsTk$eK@Th>zP9-kT1S+maC5b-*r^MHr)GeD7F5?rS3h^rfL z^`^2`kaPjhZUfl-kc0#fkDZY5{M!1~+4+B}aBK6(AJ%rKUip#v{Yw^uh+{`ncc>{j zoCb_|m<@J+=M6R(E!b1=bgR8}fZsyfT6ZHurlX%Nvnc}JfWD-uZ=z>3d-iG&2wZY_ zTvA3|0iQ$AJUKC@3wVGhE^H&|)u>-vjvV(x+M7G=mdgfG>igNQN)P%?Ni*h#8fbsL z=3RgCl#49DGvBWPcPSjCigtF~IlB(iwbB>xPUAQQc`N5tBxjnu|0S{N1*- zOp{OY$_Cm>t7l!|twv3w`v)tD(oG`k|GBlU5k0_k3Q|}n-981)qoPrN7j+_!p>$;W zQ&28}Qi#~JG;9|J6|!)@YilxJ0wK6Us9Oy3ThBqfoW>pF~;i=%Q`>JcZLN=lmhuy zH^sXfOU7H~`{d;E^t>&(zu$k^a~*NI@ucC$=BN=nvR8w82Ce|adJ}A%XAV(dAlEvq z;HxX*=S5s=0i;cFSj@NU+rbC3kaG4)A^Iy~-ekUF+))AoOr6YwHl}wMbZZbM2a;6& zrmNk^He7tJBY-bA)mZ6mT`a^Es`xD}q3sxYqJn1=WN)(xTdx1565jCi+;)PY%&9SnzL?iDc)3k=n6 zwF>+B`F4EZkM<=kE$+D9H69ql{O@2|AX_xY+6<%O(Ka+iNF9HRz!|^4D&p*~H74Rm z_>%N-UIKpeeMC+?0WkEqDpPL)z4E@)|-TNKvSl)jR(h)MPHMb8|U15K|rOA@%zFy*803sllTGz`m zcMWJWX`>0~$L=y7U7gxDwDzkvg}ZBNKiSvmxW*pP{~-Uo!4DCN8y-2JcJ>z-^q4F8 zX-i#jFt$*Q%Ly|E|7w^W)H1TvA3m~1H)_ZUtGcHEaO2AdF-&CuTb0kbO!`qz{up0; zUb1w;nGb(pmFIho=`UPeE-q_YUaX6TYtfod1?Wod83)z^t%F4}_=)E@n6}b{iR>qZ zCpft~QfZ&uXWpfezA@35jse;wlHa?Zd?}^2Hw;(){KBT_?t=h?a8)thA`iM#oT1?n z3>9pX`Fs{C434hrIt6*57K5?#JN55ivHd8Hy=H&5R5flto67602Un=qYg57b%rVSo z1}aNfuX1nS6D&Ce_3BXRV8QF9P%vDr5|(H_iI$_^qhjr!#iHvD#{Q{7(E= zPNPqhQsjM9G`MN5D{$S0ZxD&4MM=fV?Mt%1{lle)G=vJ_4`mD0&(yoFf!;-d9C`8P zRF*nC@7jLGF5-e;e*17H*~x88rFkhjeFK>BeihDeMmZZ5MI6vkHIhYYHIJw)M+CvO*684FdBiZJNw=}orzP)}-yAyZaO6_t z%o#0xcSGg80nLMI;^2*k{;7q801#2{3syN;~b-!kSRzmxFvpmKyxzq1FXcRZ92%K+0$}L z^dnlq=hf_tKF15?YONXW!AHn52zCHUgadZ(;9$Jx?8aU?cH^^4L^D%{)dxX6p1Y`* z-_3c%467N>g{J-Zk;OHJiFSV;kdUA?{}wU*l=4M8>vy*($*PjQQCX?HjnyEBiGLI^ z(SVqR;%zB114#bEQ&9Rm&3<<(_*RaUJO4rmn#N$yn2QaEebIh+mQ68gN?3TrJk1K0hrpgIa#%^GG9T0X~6BA|3Vx4_GB3FwI0L9H0IqW`rx^u zse=F3HE-f=VhmkB!d4A9L=J3Mmz%(W7i^W;msiIzdvV z9b$~&qt4h!FTfme?ACv8VHr;h+${M8mM07zv({_#oPw4*zFK_N z7hi408ekYAU&nvy$fU}v1!@$JcSN{_duQe3@Ov^EN>|1Uon#W9QwpdOh*>aaKDK-y zSuLwAzC8hU^}Nrj&j;f+@$7~~sSNdyD@=W0ymf!N^fqpO0GtkK$>?HZ z>3mjW{lRC*I5E?z`ToF}r?m{+k@t1*`8&I$Oa8^@PCzJw>){*+M zrm3y8CD$o-S5I~NlhgY@x-!Gk?BJYR{b}=m#qsV~a9lS7T;m(fnF?7`Qn3r{>N@>O zlTzWn9yt!CrfPq(8RLWYW;P&BrOizARU#|EPqG3MmWCzdT(9;cRGUqWtA9NztC^`1 z8gL6FjN}B+vo2kYKfbyK0zouUK9h?~*v)<<4?y3C6hr!8L}BH32=ZlOPbCUvRVv3- z*^@;^r1Lg9KYiVbCXB9mGl9RkA|n48*O(wOGr+&T(5-)H`K$`S47QX<*6ekK&}p})>}g_C~dZryEkMBTS*AgbFbXzeWzst*HP$WNdu!1h?=@FQLLp@zZ|<@5*T zO)L+S7x8!ti$i^x0!kv~BQglGNIRspuf|{~_wH{Lx81!Wp`FJaqGP^jtdOAZh|&E! z>-2d_M=5`G4D$`t@5lyBF9x04hlhFNy7P{>?z>h8??Okr8+*3_Az85(Z8*Vwfc} zDfCgyO`3}PZaJyGe%#MPdG0J7cPGvrq!^{Uel~=;7c{?7o`hOU#WWh#)ajPJ2T}*x z<%@rn)Arr@2b)Gka*d|u{^R-91~Y$V09-($zq6vdpeG3i_Vza)KKVF0nFjyo z#1L>{K!GrSi}IGNRPBFsPkQFZjBL&-@#mbC+@;+7d_Qx4v6r_H*S~~Z+N=Q~XlADO zL5Vq~!B8aQZ57~d#m;5ISpvUER{E<&WmBf3wRDHh_TZ}aas}~Xm_F?UTxPN zzXwi0o~vtCfmp>*#Z5h?HWEZLbs4hxt!&u0o02)r#y7<>9@3FdL1bEhdah5VaaBEf z`xIoh9d=?*Q=&1-J0sRR)L{t;{LeaEjoOuenNEQ)=0lffOCM|QK{WULUlj9b=9y>M z8}tmVjm`RRP68L8y<9BRugFhaQAS*V^cL~R>>Jse_o{A*?86m}_SRWd=(DS0=R*$8 zeKtObe*HRk3^8^J!qZY*)G4TGc&CP@s!!^XRj}ZJ)^AX`9rWXRp&;^`Yg;-I(nS}4 zH6+vR>CA3MX;w$P-41}o}f#n;x?t-w<4 za&2?0o-GwRDL>!3anH3vy!zn-%VVtzJ=;Z1bYv{`3x?T?z%@Ma2+Nh$t>g;lp4%;H zZ`O;2d*O(4Vdf3l-P8Wf8N&IG#K&LKhYFu?t+(z>kS+tk!|32xgfw*qU6)~hXmd$c zy(}+LzC+f?Bi_B$SMX+*Ok>V}PAgw6M57^1sHSo(s(Miyxez zW-8x|tp3Pa^N6qBO0*$5{R{U9_GOgDSHxEg-fDkwi3lge7Z05OF$0IdRL3&vG^QTX3q^Na!&3{_|8`Bu<9j_DI=&aN`Nu`iD7o+| z^GYR3-BBH{xY-nSN1bWT!Lj9O`fHJd;!AhYD`pLY%GV_>%7nJL-tpkSZ_E&4ir}JM zMhID}?(Q!X3;DqrOZoJF2SzoX24^YDj(S#svKJw%*$;Asshi^4M1!^9UW(Ehq~BTr zdoQ-uubukhU6r}FZoRVBiYeX2JMfVQ(bq#anOQd++1|XRLlM-l`O!w|fR-%~Nl-9P zy!%bZluhY zyYUN)pJI7^X%T`t?GS!lXiiGPLujE?{p;`!0T0FYQNKW%c27Y)U9p%_=gg*K;PdUt+Wmbe%0lK?3gI&I;Ls zKnNvduMky(td4JuH$>vM2SsM{lIxZ+y%om9#gbN%eO>N`dU~m(M*Q=*6f;sP)z8y^Lj`a=yYa6I zaMrhxFq|TPC-Bs$7{|r5rS%mPrlPW2{a*vvgS!oK?IfSdCPnDLhNmLV0buM=EZMr; z|D^f^z4f&qa>ntVeYHSipTt#Ob=zJ2#p136nRj*HxX>L$dO(HbyyYOFK!DGm2qriE zI$P=I9FSc#7F0c6ZzJ~c*H+rj?eto$^g&o1Xd1PDmVs;-Y>nA7RC!hbOZZ9M`YD<} z8$VHbPr9$R`@bvbPD{4@<@GVuYjs~iy`W==J*C1(nmYcEVj(eV_y+sw!+im(k51{v zIH&Z|b1TcEQt2KX;DR5O75A^W4si5v_uYll1F3|oM3ZjHhf|Ob5*=*cIi>bDwZ;Tv z?9wKG-<-3--#zIwWECy=M)n=|dnb_3#g56qh63T0Wmw0b}=-o${e}BNi z@A;{TbF(Vt5pua-ol4?mR!Ez7g<4+ddnyzh+x24bW;ToYS7wn-hE}z(#DP!eL-KN$ zjf1(ynQ#4Mb}&=w6$)d>e94`C1x$+py<$m!G>-QtCWzC7>hB~)@Z&s9vKXRIL0PAu zAry(P@Dy~_kCI9$MBazSK^xYAYBFRVZj2O63vlQos@(WFb(^`6*oiPLWw?}hpG8^S zhM#K(Lpuc-&?2ebgS|iMxip0O2uXc>3fft!uUJNZ;StMy*_nDroqQPBkB)*|K&>Tz z6(2Yl$)181cxX}Mn*SKY11thF?*ZfO>iGPP^ygHxIW8V%@-NZ6yhlr`lSfWu%E5>| zU%jZKtY^~A(KD!Eh-dMF0hVhg702;tpqS$@GW=Hi{H}Y`n=t=Wr)>9ha*w+JvE_%x z<%br-eg|CXQ2aHu#m05>0dXkKeuCP6f$g$`e$LrDQPKRl>|*suV@3I$s>)AKU$MNv zT0z;Y_fdvyR5lrW%J9Ft)-og$;Lb1&z|7;n14ADLjmbXpefnXVz0Y*S!>Nx!a_x8^ z7W!)S%pBa22J$nMk4Ef(;9#g>iv)P;ahEiZTddh{o?hPlX7b9BP}tO1cO?kD??B_O()_3ZRTmzgq;4v`nNkWVoASB3eGGXv=e}eTjDJA(fQN(R%qF zrMOvK)Pmr+%ZsyOYP~e2n)%-FWAKOgFtjq zpkJW6FaPeDZ4@<=p1^$o!g}z3Iav7MrPsH*TB6TpR0V%r{XI7()#6ov*7a`5mdema z+-F|iUY_3?gs0-wi0XhV$%t%EC?7lpG5Qru>@}``cvJU&Mxiwatrhw(sBq*?4KA$z zHOJRekQ$Rh0qA7we=S5$W+pr6Iwz9(+_Cmul+JJs@LB&vkB{OH6|ReasY`eNqM_2R z5A&C)kw&+$QxH%9B>vBRPI~^wuOL`Hd@F@+hc-JUAXNF*UvJsD08>MiQ_z)X5HeHd zDJZ#yOsS4?rI*dP=KadH!-MZ9-jyfSU6-sb!DnKU6+n4~qlFD-*cl_HZ? zjqv+@lV`S(+;CUftY6&{%{d3VxT{Ll8fo>n#p>xl{X_}FxvF80sH_|`h_HO?)_@sY z%d<%+W_rB9MU0=QkFN{HC6_pn5Zz zT?%V#dIbb7l}#Db`kHs?hHBUa5EuskbB#%X6*M{se?`)xj$-iE!D(QIzhDVi;?Tq2 zBFhfywH<-a>RcAOYX171?bR5sbdV2m)+Lo$Y)VE`II@KJA7sHK=li;KXbt&Ohl~sf zeAM%^RIs^kWVn)lwf^*e(9ZoAFMm*REIG2H+Z&bNXtA~-sDh+IR+urm40oiwG33I5 zxqUF4kCOxT)%Kc&_LL7(?bP#3ThiY{*IIow7by!rIB=!-`JD+Gkk=`-olM{%t}|OD zSozklpVr~to>jf{zgw>jQ^LwdAE#^TspUwUd{#6MUi|le^bo6&7+ew$m!Jduu_Rae zE3u&hEx!*s^y!Km+$NPifwbDZB-wLQjs*vy`0F+cb%X6Y-IDEiE+YJh(<4vC=wxqd zF?l;l`nkCfG5z*4Tc&Xc-ij;v6vV3&EDCo8)M|jQ(4_{mg-^ABl1EjE3j)~Tpcc@bx5mJpU&Ad8)#m_z669RVNib}0a8O^<99@=;+ z!4%9%Mm2<^!9PIEM##j6Up;82*p+)fuXw$%sgzY%MLqoZmg`jsz(lj;;eA|!zmY6t za5*eU>piAqERdOW{jE#4;dbJRHVUujw{Z%(4?fm{($X=J(S{;)_*d`$a-Y~8k@N4P zAj*(`wNFLD^E8Mpa_yd&Sm1pmB&YZH|8tkw8M?UJ08qJ=39nCZorPGzor^fG6fhh4 zB7v>9xH&(4<9nDtvqI|5(R73@VkbB*I{ZLe;rvL5Z45Jm;|?;Z^YS}btI3R^8txf$ zn6-j^j1?_N$MKkIYgRa#+x0StiI6|f(c*l67Nl`wW8=p?F+zB1r`|h1&8FPn0#D;q zd`Gc%U!#{;^(7u;t#^UkkhDN7g*WI~)d1#kr=Ua(Pcu@C?Sfk(bhbIwqopb0UEWW} z_6+=(LXl`d#(@S{iAmvS-2@jJY);7B%_m&NA$O5@yWYH97#FtDX;{gcEog3TAXfH& z;A$pDFNYyl{&v}?bQiVxqoQ@9B%l{aGIT|Zud;uhg^OaXZ`@RUXOtVtvvnzYM3b#B zHOYHb4*jm@=E27_H^Xm)+ag#rCqlkd*2z{| zKIG#JspLBg0)zcgf=!uAGE|oR^O4fs!{LrVM{;CzHR9BZ@f}y=08^>@H!bR)hHy^P~9(;5M z^L^08ZES1&DJWpVv9G{;2+0fo52(kUg1EMP4WLVGsj!l^`e$L5bp;W^&H|rJc*QQV zhCEdIj$BJyRmPLSXAM2^?>F*)h6mAHNwq=y0dmKx@K zdzp*5#EUdfdEPglcnacB-ct>;zN5Ih)t**$`LIt)&RscKME3sc-E=q6Q%yU&^P!tLRX***`XAkYJ2_V_+YDiv zPeIY0GNkJ;E24A5wCdqLA5f%r$w_%tKJkWigC#XbIQEQma;YMt$^G@8!ay!jqpOK# zUP~a&0`ZZWSCNr=Xvz4OOgXY`%i|@V&)il3uksW1cD|F?$UKz%!}P0ztGv{2^JJ$= z8FQn?nj5Wph`VuGrO$YOw6BcQxi@t@_LfQcf%GW`6p+1>XfB)x{B9uB3GbLjl(zZk zlh4A^#{D*3#55bsqkLb|XOZQe=o3bek@3GuGHkOy9Mfd8GmSqOIxtB0zeeD5Jo+Sa zhKOHYS(HP)WV))5F9rG&KWU;=OZ$C&!*CU1Qgg)QJ*<<*1_YXaqgX%vnM%`u3Q9qf z6^hH>*3U?~82U{Irjs}8-5ujUwpg<$qF*awfFfZR3K6ZA7XXpHq)6{>8Ulu$)9(+3lMR7$#)*9Ox>mu-?y@(6V ztSGGur|`3OJ2yPIss!(CpaV;+(2Tbv#QUi2bK{hu_qw=$LVtLUu=n@1nftoqi^o^g zf(AZ{!AVP$>TgV6AdO*UFb_fju|x~*hGN2`Hx4BeO7h<>H^+=ArF0v|j*bG8kFj6!$21MZKmz*tvPGc3{6w`FngPPZ zVK@$ zOzkr&@6xPg=dwBsf7R#c<)x`FWf#35<&yNN!$@|N7Z(V~#ZE>wUxwOF8`^12hRK)f zbT*b4N}rMOQCYLqO(TC9zDyW=2yW!s;lO(nZ_dGuYhX;b=9=>fDg_HGgVhuMp@whlAenp%!e=i7xmH-Kh?Df@eUp$Mz zzP>JH!kVunqeho3%R7bWgEl4Z8mwiaJE`MFl7wgQt5ZMn9jBeVI|Hww4c=TrN4OVF z?U~5l3Gbs5jd|{Vz{G04T+9QZWzR$ zj9B5wmPaoOJE9r#welU_waG-{swSnFe6oBMenXRYwx%>2!kw$@yufE<$sYhp;$ znY8Mp8;<_GFDQ2@B{VH9gf`~y%HRw##r$=TR)q+rzd6D_`W%!Z{XJdZPO^_GUtbVw zs5C(GiX8vxh2l+Vvfpn!dw-_W6?YkbIe_n#|Kl~)>m96k3VLYAe(%ZUYu6H2ql}Dz z%OoqZ2jvy5h5RS0s`%yjFzeu<@JqVU;g7&YdAd9*bD6rwDo?!~kQW=8f3 z(PmL3{eZf^gw%ZJRh*S*1bw;@4knpk3yJZL;d@?Jmq#c@%+MX5f@lO6Cqw1gPb)!@ zrKV3ozrMScleu~HCkH2!{_vlFoxj>@yYPdtogt01hOj>=B>gLF8yK-s$5&+uOv!M% zI#|Y@4UezguOnMOJ%^kgmCeTwN2HsQ2U2X$$|dz)WHNjVZkXH=$0rg^35;zxn}n0@ zjtWMC z&(H2O8cVN7)AH?RpTMy6zO(SjurCw=E2pv?;k^ejO*4>Yukz|`0?@*7)n+DdPN~p- zx^q|eyjz?QdiKMD1HY=_PRSMPqrxcQ`omSM{ z@5Du9xw~ccL%MRPlVG+mwdkkNon71;bs)1B?U3(XChNE(5}q=Dq>~}+Y!Ua&OO!r1 zO^V~odKn^g_5Z4f_m}Pua_0qp3Cl$@B3=-5`2dCq{;UEv*SFn!DgDi2*S95$lnW)( z`yR%k%>z{bK=;oe-I!7e7AlAA)zb-H)UzT$(ldAO6v!-$dV5pyDb0bzI9?XnKX6nw zv1O|5cQ$m&I}zf41&%|o102=VuL#!Z(r@q!b-(A)VM{LhdN~|sj4{s=3hX2Qya*DX zjp|G9;5!cpQpGC%-S+%8y$&IBPNR-puZYXPTBn5z*&FE}(OV}s@j7L@X%&XN=Hry#UVZxICO#U{3X?=&vIkb&D{u-z;zekO?oB#`pBqZH#w;E|2Q%V?vBw z@k=v{-_Xk9rlTDjyd$x8suS`7pr}1g?o@XQsQv35gT_jRvCa*QF^oJIabRmyyd5Yh zz|r;&F$!j_MtN@|7fS~Bk?VDgixn9d(i5MJju`cS!S{E{TT+(Gb1b>l>9P^FOIJYj zBV-wp8)EXFFeUwOa7yCc>Dnd=xIgbxOG>MH&5~<>z^7A?cLaMl3w!FuT7@VAVOMbc z;uPd(!-kl_)C=x%;Gj%!LnFrNzpfHiX8CZ{)?7n1nezwknJ;fwT%0`VO?%{kp)yyI zbl(Jj+*3$3v~Bka7qu}ON8R21(>?a4t9x&s*f@cRQQJY`Gx~|h8I)HYyYB%I-rHW5 zM%1iHi`~6)_Y_o9`1OtFik5`c2>8s4CAqhI$JfktxKBWTN;=1=qwv=mcFu>yx zpqP7)K>OYo=*3U=cGCfwSQ}wj;f-%wKshk?6RmGc8A=(_hq^+(%lYpJ-~ZGs|w)z{4t$ zy@IsqtEwCL~*|daL86 zWd4SwVF|&{f=`WVa9z$Wmh*cPpq9@T8??X3R+%6%)b(c_(}!BfCpJ*346m+%xquQE zz@2dGC-vaV@qWPDT(iy@Wy|z0RbMmY@*kk>XDoZ!+PZ_kBNP8sclQ^kvEf4$h4u32 zAe}C?o8Nx$IQu3)3P5|nT67oA&kbRJ8%i4mUN>G;zVjocnE0GFZvv>3(5AYS4+LU^ z8d(GP?CtxlBa58#b31R*HHMnQ(pu5cT_*KC%QtA*ookq?K&eF1WK-lX>dY?`-zf-t zp*cfH&GAOco_`ff;HsI8^wyADAB&h2saXA%uTQ!DdE8Z)A2aB-4 z0==OhDvy6u3=4~MAoAg~)5KUICNRuoXgRR%&^ZhD{pfqEl=#gr7*uara?#vJI{AA1 z4Q9@rHnEC2DDeCLQLDW|_MA4-2PW^#?;Nfu8k}E`>H9fnU?!j`?7o_V9+Q%Ntt%@m zt{(B0D+;yN0%78qOJ1|deCh#z>6+e%q`w&^Y;=36Hehtv)ZA$((&OeKFu5jBhYMc9 z5D)Q!Nn~HTym@0Q!kXD@vnL|#LQ8?gd<#CX891};PR{n=Xd zE_Xx@TGRXyZ?B@YSXO7UbQPoVs2PI?ckDejk}{$f#R%~TeCA5f>o44Yb+?}h+xDrZ z8LlFHJcC6eihiP zU*fHs?SjfR=;M1A9{qf*Nj>1x4-b)jgf{g{F)LXH{~c~kP#{iq61Wm|AaZXT69i`O z9X(<+%a{!_*JRLlTMGGqJSvGpJx2OK7Ynh1yO*&-WEleVnx3!Eq}+?$j$^%+Tl#cG zEFqzv>Dru}MCl*CW=BKqrS8mnXzMa`}tU zWETjIB*^rvvf&|Qo+m^|B0{c<;GGzjDRec`H!|{r_=SaFm+;$v(Pb~6J4po??cFfb zAD#HOcy=4}N&XF9?`A8d+zmelw343u8VD~SFmZ=7Q0m!9)L1^F`Ldqu}%&clC^#SNRzy>`2txb~Qn6`OF&8RoSNyW-j-k3d# zg{mQYJt00^49+k?OUmoOfSN-jzn|kdzv(ai&wi_$SR`jjqiAIvYiew1^Cz%O4N|$e;4N}J#qklHM@=o`Exc$lOyc6htb!pmK?8H-* zRx+>M+HI6X;x0d~>(M}w3gN=c*J`}qyu-tV(T5k4v~M~MQ13FRy%5Se&s=G2dpino z5!tJ2q=ZI{qMC^1_{nu-;&5Ue`~gSH^Z9&&P*77xqO||xhnRr}Osh|I{{RwyKYzI^|mx2HW}ncb;xqG5hX~JtgA7auUDm;?yLU&F!g0P)Njt@DobAQiY!5BAU?`F zKj@P8Uxj`eO`1+FR$U{U@pB$dHngnqt=IRg&wJ+MU<&S4{?wCUyk|QUxqT>rj7M?S zBE-Reak;>giB6hO7gJtr(D|v{4&;s#2MrmE#s2X&{Km9-w8KKOB(qIH+%nn5DuMFr z-(~SS@dF}VWrO3klMGp7vhm~(_a`3NBPlm@sc>FyHT3XkeX#n)iX zrpOl~WZM%+Pc$!&X!HmDr=i4{&wdJmWv&!|b&C8nq$LjqS4x47YuKTR;6Rb=ckN@> zH`Q*RTVBa9{E*eO4w_P|GyW+oI#WR51n5W?$rotJm!D>b_4?A47tq#{MO!#;E!S0> zCbdGta$$Wj#dyb9YKoHwStnkMM=qxUuek zm{pm-B)`@+$~wTwsjr4c;T+=-7m&Tz|6YF9gMNN#DA&6V=GCjF^4gMN%EdBBPNO|X z+`(k@bI7$HJnA-EjswMj%;wJg`o7pIFl;q@ILT&Q@pWEM`iHVg}!a&nR$DE+W#%2@| zIpkrXE8{o*GawL%JWpei#aNn>l6r>4!0c)eA}JK=jO@MLEe){Y9CvPFcZ0Dr@@dj6s zQK=pqU#ZqW-<^&0Z&{qenw(FvbBmd#A!~VHx&R|qKj@h<0yx?8yUyi@7--JgU8kC< z6&IJT2I9z-e1}U_0VUVJ9U_@*DpV^t4rg?01#k!cA2$c8 z%Str9RG~iHu7wRo{>CGT5x_IN|5{|`&T(n=$x&Q2wyPC(7|TD9ufD}QW=NRa7~~5o z9UqGp5)I+JVM#s(5sJy&#Y7K+V3OP%+@J=C?bprcc_q<@#@|7UxQv^LUWzpZWzxw)C1i=eexmZUs~0q)h1$JT`?p$Aia{@ z@~Ld%b+7pJ@^PmiG0n|J=4LSw$|9u@>5&!S}i;MKpI? zoGIekTKvIxm1pYls+EXPVl^dy8NrWO-~qgXlb_Z$ zf6$8;AOs>C6k6i+yVPG9Vwzm;dP`i}U1}A+I4N|(Wdl4|+lD4wLiUN@8q~pX-0{sj zx$3fYLA>X4OH1;Do0oQ($L;C#ZM8<|(?E!))FBl9CfDN8AWfynTJOkKg!IH%vMaWO zS(^mtIOTm;(9N!Y_77!fIF})3&wg9|{!)IBZXV$MZxe)=RPtk>gmm2(MI#J16#oQg zY)IhaLU+z${}3H<6HC#FHk)RbQR#tqvs!(-X`|nMcGnVy$=r#=#{_{c-5RVK_jb2H z)90+V`gCl2jO2|cCe!9WE2Dc>54gAdw~7}FiDxLq5YHNa(@v|kD$yGMft~8qs;vXr zBioYNc?*WnK|bSX$rsi?c&dMdUIC|L@D3abj(Es!i9X*BUC&cOMX6u$@h;M+i;UrPmy5n>ww9Nk|Y}ll&e(G&&TZYq~YA{Y? zu;tw9eV^2S#E>uS&u;8+r<^3NV|I4&CB*0W2YAR}q@YEXwzIIB5M{(o z$uiWarI}0o4KO-5vab4{xXf5f}0E)R= zX8G(H^ZCG;)`wMo%;0GE!X-=(EuAb5U#mxQz*J9qn>G@yF7SOa+3iXT zRJ^9w==o~pZX?e`j%6)`=uF8$h#^L(B6FQec+9QYW+m$H2$5W>Jk0SKTx+hg1W7TEI6wDZ0Qc~yzQ+mCGrDiOEvEfol=eRFO0duy5O zo(zN0#mqIjx&?~Cze0%rb;^fgVs)(3{L*0E$H|I9hqk6m;cr5D{~Oy#HP-E^wUBu7 zD8uI39Hjzve?JD?+*1AlmWpHZbH3MAxr{$s;{7%yTWKz0Zp$aZtcfUz#o{%K$4;HEaV`o;&0^3&7_%7^N`mm1Y3h9eq( z^%f6`7yi$8QGTlH*^fU3(H)`|r--5`A(;7q-ocimWwl|TTCr@%5W7vV7~|z4lLk%l z)}mB9-ND&SGPff!xX@WJk7lh*y9-<%IUf^)RvtBb>b9{hy>Q>ZA~sCsS??=8V#M#6){VyYJ5z>@I1p8KTBN{Q@ zE%bR7F1R-Q*{X1I#|fLUZyR=;)KT=4Emks|d7=j*eO^k?GXOD(tS|0WTSQ@hF-b-u zFq5zAPSbFQDWt!3wYHw1V7;N2nzTd8dE35^<1`)A)A&&@OFxEMdktx_iRr#b{aPTA z`qtUSryUcmQuFtX&Gl9~9vPm%1f0(H#j$H0fI%Q7~1iV z*V_ae6^BLms>Ba=E+>_lHd;El->*%aSQa+BlNS4x`VEY?!Ys}cV}aO@aGN;STB}AiZe~<}2fv$XQVH)* z5HLAU84mh7Gl{=|$VU~(UJEK1*=N39a~0c?iKFl+xq!JB z4XzM#hgxl4^4urvVq~O8|FA2s8;TsAf|kOG0flRmJ&?F!-fphcH+Hs^f;yGj_j+;? zxbg(fkblFR5#Vv0}aeWQWq``I<)i)V;vYBS2gP2*>FYZ$}%U*H0YppW1 z5>d-9I=QD~K?!nx$wV&(GY zL9?ZN5wmRTSmn^Dl-d}VoYg~^AuaIGxG5XiIGdjopaT^GiU*CfryqY{T)IB1hSPn= z^xq?PjStn`{f| zVs`AVX^5QdO@4KMLCGit7j?opJRNnvF69(NlN_J@^lqNkR=mxil0lMyNBU}@fWh$D zyGm!ieUWC7A8)+e*v|F(Qr)javcS`mv0IcEaQ+J4>EUqSFx;5%yF7U=vHI>p_?NA* zex)gdO-NyeOi4pgQ#pP^K&S`EL<}n#QoGeo&3zB_ttfBIc_lo1dEcMy;1g4f5M74RlQX3s z4UExzAg;wzPy&Rh0uq!u)3_g~bLsf-{X9y$4ecG~o**91dcUtk{VVri6!Q=z{zrA;afeOVhE+*;ODuZ~SUR+CA zkOZZFD>GrJ#BRx1r=kNJE?4v5vjaIfp4m89vA^Z(*1VBwgj-p%!W_~Iyb!yVxl=Cx zt_sG=HsLtnTp8i$JXEDP3HvUv>SmHG7Wytg03B6N|4jFL6jv-{E%Rix5Ni(fPVrw&2v)rFmCTiYKYo{jg(ApqlsYm8K65?1e?Q9lgDa3Y zXk_#`LyA9@W}tY0vKP{pdsjb62?-(?p9~F8eO5!$GWjB2bbUYl6omDjG^$yf6zXP2 z7eznJt9!V0rfNLLTsogsqf1u<98pLw0&UFP`B&NvAp{N&q$@_+4HjvCHaD*JZ)JOb zWT>RLn>%{jV%Dne?1FZor7hNyfqny-%KmQ?mpnl8ZafE;ElX?2vU%>4(lBK0LlH;O zHgO7&sq?3*bm6%Y4ks=@jw75c(Y7vkH4W1;Og-><~kSn?zV_o*ZZZ&4AEVYEv1Nmf@O=T(jfVS$5P@e&*RzkBrJX}!Rm;Xz{Pb; z)QQ^8;rf_C<~ab_KbPflXJzFJr0HD_8lq_M;&b{rmX^&>X8wk&7j8L3K@7T8seR!P zE~)@5bLbSrdkXTyi0Q>CSIa%yDA;nswY6sJzGMgvZ?QAlS8>0{c}MLj@Mpb$JhoE? zKTY(ayr9a#1WrNr1ciPmCs2O7BX9j?gNx;gYTbi3S9_{^ud-Q^8(e-eT^>Xv`eN`F zT#LIpE`kKL)J|8NHQV)&xfAQXcg!u!kd0S9Z`1pVPdwa>4o{?Tm>61qgNNdWY+~=p z;Wx@AWADuwXiu9&g!HpZGRGNzsJr`oV%0qN<%(qN6>+|MM)>QiEMOk@`GCoI=Sk8P zKg7ex+?~SkGZVL_jR^nw(2T9qRf_hO^JR$)}YF4N>Q;YEnWtH>+B=B5NF6V z*^7b06mWHeNDhpj`md)s91bU|CoqqcWv7TCHh=Z3p*1a=4e6074Fg8pLkp%+WP@f) z9StE6N*Ua^{Pn^j`ttL#2%DzJ53}kvM~w`{HHh(fnv3H3gY`~+42@q^q`Wmm(i`K7 z)1NjK>{O05rAi-M?zn${U^rjk>o_aUfhhV$XUb>G)Pn?_f_{*Ze#Iw8cs**Nd>!il z&9_HR{r~d=_y3>zY#Q3E?K1-yn@mf>heY?fg~9W$Bxh9KwK`>_vQcd5Z3>Ly*H1C^6mS^PYGGa zPPUOHJ7ujbLuE^nlwDKV6H*BoGnSA&6s1B4C1l@?U6LeQh%qC{mUGZJYqsBXJ%9ba z-|M=c>$&ddzOU=LpZ@r~UNgt*Jdb&wpX0NAm@z>;#O(JIV zBnrtG>WE`>z;doVj2emgJoIaW&yRZ%+wPO^ae@rrcmQE(9Y5n~nQf@8zp*R$zkxi* zTy%f&(RKNM)|1wdn<43~THVU=sg6ZwDpJeuzIkII4VnwZ%n$DlJ%*o*S3~Wax0aeQ z8Bw)*p!#yb@j}wjb=fu3`GA|ML7y_+tpAs@JFpO8_`vSGBxh|YDd-Wt1t#d5HOUz) zRR46B>+#GNnyQHvd$@$AOtuv!TBiSovs(4hm_cNJPbJSV9uEInpDR5QkfWFNjwGMM z=JVi)u-R_@mrPQ(9b8NS6MuhyPk*5&k1coe%JfSFm7YCr8+*>S1Gow-+l~A$gGI4{ zFq%z2iIGbyG0d3d>dWNK_P@5Lh%t+x?cXJE`snX)_WP*M|2gtsaMb76znF)taF+Rn z&-h+{`L^Jc|3vICU);I$xV;wN_8ih?BEl#Ft35|pO4fYA>bX_B`na>B5}F3DU&5y4 zSJ+=yRwM9AU_X`R*Bo>i1Wmc!C=l9D2i8u!b)=|O4;&_}K(N=YXE5Cjm`li?^7Tka zB!XdSf=nb4ioB619msFXqwO}Q1HHC%fZZm44)ImPn+`k3OT|>BOdf}POT^&{L95XkJ;Z=VCX#R&^cId}~ z>$s2Ev;_Zb8Ve4yX$t?tcOWA6kIFjx2PL4w=>X3x1K8KBDa^dv4jquQ{Zq?nwvWi) zSbk&qjpa9%e_1Toay7?l*R5zAdSLl~_G)RxeFK4txPwO;*@jQ?eDK>=Zuf(1$#B{b zmYH%IV4w1>zOPPtX zKl~gu!7&3ZJav6J1ce|GB8|G}!0nQ5__oNeb)Jg6+=21$;r`CE z0qRo$1r1eOm+aLJdQMYPis0u})&l=Wg!SKmegpaq=r^F>fPMq|4d^$2puYghq2>mx z2Vw#$r*pr&@o0=)r@-PHU^o0pMYeg1K#jxOwG#WDvBgnj*W|Jyr@^a zcmMrjvJo9n)H?a?YOeeIF1ELRGd_R!Uyk(gf9^ko|F@+wnhtohShZl%Ftyv0ysfsw zgc;qGhbQ7~Jqnd=Y_}_a+y1`8N&Y(o7Fmn=VL5yUyOzb!?(+O3t_~Dt7^Bi*q6jr( z5J5E%Ba{B-9GSmYjCN7W+L6=VB>T+Ko?8ri#W_brkdcQ`9?-t$lv7Y1nAA@?@>ur( zMR&a7`(Awd-rPlX|70dvX}pEO=gE=Uzju1sKU zzz}z?>cd^Pb5}$OhYetlGDk}*Yomr`{?oSme?v4yjNjY2Vg!mGU1{#Q%W2=no8RPj zb*9J6HSl2EkLabnOMA<2{quvte{0~OPyWyGl*$gO$UHKC3ayXnbfYQz=J9Pq-%|v- zk3_UrUr`V*|5lPXda5B||EbhOZ}qd3muFd7EZKItu*&E?6n!YF2-A6{U5{cij?#KQ z1-aORYX_!QMxCss@~$hosT}OS_F5*rKkLW2OK7u&#X|r%3@Kx}FmZH%ea#3)ae%z> z<%q6!+0j;io50JN%l0EvfhmHY%v+61Mf$z$s95CC=xsai$&ImB!81* za0UU>QJ1sLHD*4&J$))k&!ar|r9G11J9hV&Ot!Mg{-0VCxBtL-JfPl7hVL zh=${7iTRC$VOP86yWPBSDJ1JTy>Z{jp@c!+KnKwcQqfPQ*F4UKZOKDR}eaISYV(l!fq9|py0S>ZoAq?pyC!No9T#0_>>)(G9=HI;1IBGl@QZJ4 z17~}GCldE(>gRH57JuSLlU}>Y4i{{_vqn89UMICPe#K`azB#R9<91$6LjpyK02A!G z(Pnpgd|!^fpJ;&Dz=&to9$U_*Dql_v)LHJcx{&q(HU*pKK)9L z$u?K>sdD z&0frMs+Rk%8B0WJ>Q62>tmlC*0#g`F@+>?a$^Nb^xX>{)t!w01>9F^{M6Xf8k>s6! zt7SL0o~9Ih=G(ef zeHhp((|lcVpreARvll?GS;`zwQGLnB%m4Ni!2Dd{Lo0filmIc6`r*JNN0b#f{_d59 zt+!N_*A7cUhpp(nsw*UB#1r%>^e3Euh><+yEC^qlA{&HyJ4DFrEIet?HfjhF*VK}q zb~#>3Fl>q~%C(d?*=enl)P#-9?WY6L*0h0JXn0`Mj%D80i&O zb#xzQ-}nP=;+^|9GkEuP1F+FVmCaEayE!qtkD*{1XJ|Zrvd3jgN74^p{NhG`U1N?} zmRD`M)$t7d#Yb;$qzh*5?5YGOSKR(IY5MOGp-qP8BoIRgHXWGA>80MvI5E@Irqa;h z`J9z$s;AFT5+YlxGmb=f<3719-x@C>Ib3b9 zlx{pGaR)PJFB54nN(zJ8E)ti2=JR6O)qTIF!}yko!v?k)XT)pYh8%c)SkC;&kzHZZ z4*iH-m_SZCkdU1xVI&JR51tyK@lcd=e6G|v9xo3;+u|+`TDW`pj98eSwz3wBnMCBl zIgAYb;8oZT*t8$Xlc+;X>!C5XWFdI8B&$#6404)it4z7iN`92efIqu`h3T`2oF4AL zL2_cysX1oW=i%!2D?(#+pM0@eeeGHpMLF%Y=i^UP=snvDUwA!?BB-~3P6#5}kgh`t zg(cl|K-2!!k+I&&4gBrfssUse-pCC) zz%lES#b#aWrap(Po>Ap^9`NqU$Lx41mki5KB?^aM4o~6muvnTT>MS+?`^L1wf^O7; z#rFu-)ll$vR8ovzmZ@3@q`8t8=g7Iu3*@~n{EqABBG+-`U$*I3R zCrU)d24ZUJQVvz!NIk=DDf@{z7Ii>*;aYX|=SWcs3?!3`sHJFO$XP=a-3hPdZf}@Z za~y~$S#GYqF!MTpxkt#F=;~6N?BI7OmFv|~fELmc-GQjHNohX_B@y6Vuw)~FSKwq1 zVwWq+nCIvv-rk#oDxDfRiLPN)uUo5|%(?FaJjMv?fA*x{ziR^O`~Nva<6lUaiAw|@ zSsJGrm~BXbU#9~_&28Zt6rtM!WP#8I$;#S1s#+q}!Y3bp)&rZ)n=k2=bf0Hi8zfty zL`RWMM2#*F2%RigIDS&vX!J$PAv=XmG17#ud-zSTHIoNoyI8uMsMJ^@^**X zxi&-TA?$H8$Fe?Z2O(c|z%+F|5|HUtdH_j4w!e*{A8&s~_l|3i)^4{&SgKueX%dt@ z$#*=tFkd72uA!DOfBXp?Jc*f_VFa~y_LZEaUU6Sl1DAz$%Q}}b%Sfm7r<~P-54Z?s zgRck2Lt-z89;p0q63@4_nV=Z*QIrN0e@M2a1D$6K_TrJOd+J== z1gvYY#AMQ1jmv>iw?#e+hr`~*>yt)pb%n>1R#|P=C ze$O9M-G?u&T{WNHKF#)-CzcNE{!D$^F3^1)9~cR*f32^?r?GxIu_62<$jd9-cHiI? zk0*9P3i5>()nNu(NH_FrhSN>PB0R12dPMH7Hsop$F-c2SDc&yFH-p!{y4_Rt{5-Aq z`djal*{KAtL#MHq{=U^5|7?N(vrnUak-am~?3VKGS%kuUe_!z-mH8*n#j@O^hJl-9 z!LLy!e{>+7vjcF$qUMzdt}Nn z-Heg)#{Oi5Y8chZ;08_4EuJDUOgq_vx1mPk=)ej2${HW$c?~EGzP3XLjB&yk{NV^X z(D#)N#HZ7SG27fkZ<;_hjuOyL{q-K{Pt9q6f4X&bV_NZdm%qFGcj3fnuUX#h^Y-)RJ$r2=(U^t~@e-Fpes9 ze`oz4D&7Ag4n55d$p#J@jKb9ol*s9w2@Qb93pYpm7Ki92g-$=c^KRd}P9>Xr?OLf? ziR8=w>DS8!*9nt-SZ<0G7F5q8i0ijBe-L->_#5bX`P=I{22=`7$vp}$mJ#PU@h#wi z-T8hb6M7b@-bB_NSSEURm(hU;^B^SpFYEpP9fa-o76SiHWC`(?spPUYCT7zsrFtnNY%lwLTsjX<$W!IhtkkcZ+#`DLJCVl&gQp+3TD_^4&(M z@01szNH^rnFu^+k?jU8{wH3qIcS6W1@{dhqb^v$p#+mJ+@a%0^+Qf7}vWzj=89 z@QDvcES@f0c+)K%bJz3;HXA;rwNPJ4mO)Qu!FL;|5IHpNi19Jyi)8G35WliQIpI%Y8YLte+0vU5PJ<&z(5CT z(RW-zgBTs?iBKN;KIAoF@tNyI(vsVrKontJPNk{Z_Sk%+NHTe2Et3wg>?X!GtmzZ= z`r*9AfqYOIUbL>EuCeJ#8{zV8lM>E2{F98UgR=04g{LhwvZUdSTc2j8^D|b?#gH6Z9f6j4$dFzohX{dzkMEf9|%ReF4?BlN%%xd|~W1moR8K;%tRhQ=% z?xaWZhyszjaCVqZO{_0Ml%h*53_y6S2liSmXzadx9XahQa{9G*ed5Z{dB>&Nbed;< zW8!`(r19IAfc_4EG53hwof)Y=cGZUEt(9SIl4{_aYTh^Ie^n9=TkM21ehR~2qjPDK z)0JJ;vb|cN;T=a#W>=f+`WEhIs6H?ua1Atjt&khyL0<2?t9T*FZGC6Wmh4OG!HQ|; z@uA=B|4u=jzGqQm<5!tlo(NAT3c z`C*>rW&)JUf4N-jZ~T_aYaD-$eep=}7SHwE@t^zVVZ5K9mX2I;qT6RnmcF{;sks4( zu#aiiIbOeM8t71q$_+ve##4kuNT~Svjb4BK{=rJ*}j)&~Jsxh5<7Pj)4t#Q%cI2~p3yiMT|we(N;B3PBcJ_FNuy{$HsBVi;C*ml5~6lvUBexp zC$yp`P+p!}MEJJ4IB#_z!>7kK3onymyoYHw7spRJKw}RlyFe{nMn**V8lDL6QRrzG z9aRk$e;?Yv?nypj{PVQ0w@cWA=!sF|G?`sjpP9XXIvt64I^`8NYcMtIZfyLihbiOg zVWW~n$~neJmCz<|weYpY1st;vmK9}>GW34wo2@5eOO1Q8@iyylw2IdX`w4t#*ImQ1 ztyO$lOuHtglX0U@CEpx0+FRp;mikZ-KEY!Be^xBG@XeWtcM~7AeOy0Be@>1Kxir)r zCbEtv8&J%s4=927;W#N}B8sL3zP0Cf3q`L4d-zCaol#e#)Q1^VXQ;2gSln}=+fPdo zpfyuOO5IlGc;45!9ktP2+3cF|MKXn0%AI(7HJ>TNvO~0Tcmq-z?#Cq|Y9_m2d!Q0L ze`^ah>iG&<8f890jWbIFWebM}f)|XwzZ+@T@zaiY=NqMyz zy&I51YGwF*S@9%V!RG_c=a%*C+Q63ku;+Eg z>(UPrigLU9XC|IiE)ftP%}EX;20U|e)Y^8vVLzhX=?33A8~q+(8@2L*0d|Qqf2yOO zj&QN+=Y_HPoL6BaiRf^O4}>j6_aP;&wCa?(LWNEcDtjHD@GZpp0L)|a=$#B!RbdOt zYr>VynIjhf*r&c6!5ay)=RUt1m)rGmHEOP?a&dul0kRRJx&fB(=i*r;@EiZglqFCkjSeiz;y5sCmXnz6TMoFo zKR5luGq9NcJe{n47o)UWR4|gE1JzF6bf6^WAF4i3=|mUOD)l~k4>Y%ffA8d z+`4v5zPDg`I%OmwK-S6q6KUc@f0mhj-&&-ZY1lBuP=F-LAPE^bNPYtGgCo+2TG0OM z-Aw_y8@_#pK^1m4GYHE*TSkd_oEnGtTGT*e4^E>U&4*!qDkHJ@uX(Tfse)N_pi{ky z4!k~#=^jB`O#7*%#xN8&e|I`?x0HEfT4i1Xx}~sokPev2V?nEzm`x!#xNY;%ot9{Y zna_5l1D}-vip;N=f#RhDvB$_~SH-?P3S9Kb)oR&6n_Cs5XVC%^NWiN6~Ya+be1rhSv3_)#jkf4H5G%HYjD!yh?G zWmQlF_fpS&OOygZ+@&llLd20+ig*rY&bgCK5hdkv#qJ{tJ74HD& z#P+`enDd#}&NA&#g+P%%DlF}f4+bzw^1oE%|37sV(xei|T$mBdKnnjVoDypcM~@(x z{;aG&YyLDFuGXOgfB8ps{fwE%_G4n(QwUi@IXUjz0!4lxfF5p z>k?;n@gF}t6AfdHG`#sQFvaPw3G82PJDmS^0=L;&r(SMsX+`-Z1$x@o(qj}hLAIFqW@`ZoXTw&QgkP1Oyxo>_ha(pJy+ z8Nbx+N?i)Sf5#n7HuqyN&EwP(MuC`u29&=>Bo<#u8q7?yACM@ z^z79P`trRqNnt0g1I|GcLn)1VjL^hFT~2~+kKc+pZqHbhA5T7_o$LJk=FXLzp7-s$ zP@Et(A+xVt47!dVQs_C@no;r0x5%mUR`tof9zg~{f2vWBwOIA5O%AX4F#o84oCvVQ zJn+QubQgjPWr4T?*0%*-Zhl_NHL){|!ruJ$>A>zmUv-cPcv7vvwBumOz@0#3&3j4v zfMz1v#0AQ>Rkwks9))>dXJ$)v{5+=dIq~?#PW#=w;fb_#gX>@>-lk`#CX?V0y^R#E ze=Ol5e~CO*k3GO1)M`$;gY#N1YYEVb%8kadA?8bv)sww2AruNchT?!c!^7}~B3%>q z7R^#QrYSEIO3%M}|H8OHd!L8BGJucTFqTRqVrd*BU|}DwM&nMaWZ~E5CzU-mg1!P@ zckH!{`KSBB8&lfe<_4jv^SuwIv?NULb$A>af3g-smZTU%v1BHce@px+pJ1CiL&d}S z$24oNxhcdi`aKKXJXpUti%-sF-}~BCj5MmB_wV%_qAAJrv38Uj5IVinH~$6pABmft6YeXb`TU&M~{ciM%q)O*n0Lp!7^ z)Vy}ZmGTP8Q7Gi8-*n-9jj-U7jvbp7g#SH>{A&rkem->p%{$FfRmB6 z;&7hj0~rIpB{SxJfctp;@fS^o?=-{Tvu&|}nBMHa9NsjO)?-fx1Z(|@!NpH{%@=cK z^=g~lK8<{Go}CF)u8zEze26=Js}kqxFot1-{P~hRQKJJWe{ix}<70qc#-y#|e|iXV zdBk8cyShmg?>ToVTC_uTy#cO&U^;*D8zQY;7^Mw8FEK5mSb-JUghv*f+O{h0i z7{_V;rR>TjwsW03FOl)^UF}>HY2P9UIsZhqYs)o1)%T@pL3G$@iP(&D$b%gQk#9`_ z2EXj|uZ_6OSs8Qd32n9X!|=Jff2?J@%Z6_r*z z3Wb3omSZlxbbv$E01&N=nCB^fG*;>e-*m?oU;C_)Q>M}bI6B_${SN{^-G671&zID+gu>4LaQ|P<$RQ0+k4DqT)bGyPRfmzhi~Sb z2Tb7?9@u)K@o#ciOR2vaZ#g`hdVu2nsS$ z8W`MG^4B4Eq3DrzT~MJvLTYoai=eX^V4Ag{On>uDd%$Kj1ds%GinsFkob6BQE9qWagnx+F5qc^yZ z;X`68wudAvRLhG;f37@>n|ZhI*=9OkIrk#a{$7uleITD_2|r26+~C4|RA*@9xIFqS zgPi$bK1YVaX#JdlRGF5_kK%rQ;rgNFYBG+}QRie+1eIkJyIz#e1BZYc6%KuQFasu{ z?5P+FC;|UOt5;XIhVy~{cNayCr>VjAw#sr}S1N=aureQ#fAh{X7HMVVMwu)|?mMrC z_^DL(V-QjzRNgty!%*LGl<9<#giVk)YEC(Wv+U8)K?7kePQgY`&qWivst4{IZ?7H9 zq1}6ys6+@=Fp;~q>*Y%(`!fej*rNATAiCk)?UIl}cY_o!=p9>I&UUoLSIpn2tB=#g z(%F-JU#N^}e@ap*Q^I*;fM^Vlh3~4%gXHKyw@qZOBr!W#ZF@r`^yBiYq36?=b$7(# z3XMun@6ETeG>H-!lINW_bKrXSsfwYW=m3;QD+rIo_(UK&Z^>y8Wv0V|*Y&%u^{bw- zG11ImyN+#V!FkJM`H3Z(_#TNyEK5DWhB>02PqT&L0GhU>*R zI&iT1e`Yf=hC#Y_$NQhh^})q%Aa||YiYo^LPmS?uizjX0;6LG&?cVoN$lknu&uA>i zHFwpryDHxWdJ)x?YvT+40Z4wR5|4@3F|-fa+TZl(WP|40CFq%dfJel_i6^2#yz0 z`xYiaafR%H803NlawjMn_!bO2bB6R>;{1BY{8L)5wSGM(WxK!EOCnZ5p)O*W4sfo{ ze|u8}kvy3BImWlt_iQh1&)fW|dF0>V!bwbiXj=E2%BY<)*#6w_rSE3e*`}H&>ifKo z#qF{fRFVe6=HIgp34z1wi{uliGmKJI!UrR0z>KFNdcq?)!2;9`zy603e(?(T@_Dz-@iXq=C4h{BDjO>d61$HB2gJ1ezzS z2$(yxRe+KJTibUx)ru7f7g)-Td5rEwjcAr)d3yS{xQ;cZ9zV`^7dEY|vIL*9Atk-1 z1DBD}KW%!Si1pS!4><4QGNTZre^l4nAZ;mnm32|NtdN)8elPIZ;k|QQVac+vh*(A0 zb)ozBANy->y<3&AHH*2f-@^Oi1*@e35S-6>&mofzbp3?(G746a?mS43Q5rAQV*b>^ zv?}=TapmU3AK#^54`}Bl<#E{2I1DSHhZNs}igbl;eXDbAM^awZ`$d(Be;i~-4aFH_ z54tW%EC}r&C>oVfdE2(A!*n1f96{4JmZ~xh`%|m=zwl+}Qwmp|5qEBU)G6QfliS9o zndNCy-TnP>rn{rh9G7?W;Oks97CZYN{5_9$ryOEbu^ZG9>}q^8c6(AjA_@urD{Xn+ z*bTcMXbVbOA_DgUg_UF6e;vj)9Q(Is@S(;T*=dIkq+zCEv^Tf}>^7(KPy6;?XPJf5 zfqv{d8y)Zr?7{tHDUZleWH z7IT#^FK^_%11E|Qf1owyV0vl0^pv7MEVO|Yumk#A&bYNgv+?%#BcN9Cc zx<4?94zQPF7Z&=O;OM=HR>UjhT5uq( z60aMF!9`+exBDUuZw98}cKI{zCm`Yo{X2C@I<#vOCvmk)e`{0zbN~()Hjo!aU@ILp zwsIQ3VlE-4#9&QeQnxuAH~JJqWw-V_MuyYgoQ@cdY3Dmcqs4pBfuDkAk@Am7P1O4r z=ztS3sdI@sd#jV7%e^TcLu6mtfG!=t5O(5VJrJuOy!2LVd{iy#*XzQ%y*BN{RIRXNQd|2v#&~ z2Gfs?x}jJ_9nu`f+zbBk>qUw-HMJE(oFL;*#gh@Cf10Ba_i~0w$zXX~N?q%9PJ~j) z-L>7Qv^rz2L;7d8jN!~EMnH`2L677zZ)$HO3Jb&ev_FDkmiK&r#*KWm+4aLbb%Xif zw^_gM=J}Fig``v1y0G2VqAoHjNrAeWg%ij2k}7j( zEDW9_e~j8!6p`o1xW!4m?OLPPhg!FZi3>YU>P5&Srie8;g*mKU?M5_W4~-qAOjM5t z3%hP%;{08#TAa9x_IW+xt>+r8_q<1by%zD6c?dC|i`n%2^$J~>F;2E>5s#32F`Wi< z;Pc?GlVd-sF-EDtnXLP_XZEOU!bvbFgt*m(f01lG_JU$V&DTy#bXINF`Fz@kWTe_M zxxe8_#ri{av58=IvA)EShfHs>IS#oa3+E0)J0Jp|0)A+h38e!j;5ZrRnmIjIUS7eG zo3dzuCl0$_iZr~OxIo!UKP2z*~re?D9sHz(5A7@VkhujW^NFOxj5OwWRkt%e%y_0n{tVOQcNY#L@ab|Wwn!(aDA?3;@? z8P2t;C%hxPscm;m+x|?mrT9~>3Pv@yo7c=-=Z796jQc^AL@UCLJQ4GrO3_s#e^uI$ z7;Fkt_nngw18RQY0=En*YxUP~RU!U;Hsp+`|->;~WtI*C&;tEQD2*+>qlpp>c7>`yMHL&&M+lxK96S>ki>xV$!s2hBOg{v>d|W>>z0jkzLI^iJx|p2Ge-$!m%Juoh zV}UK+l4OnzlqK01#R;O*FII*Y65#RMZTjbq$v^4sd+sL@Vj=p}$8jJ*@PPEa*SjUQ zwV76H?b+fC9Pk)%pI4!ooz19||@!f1Cc`V1I~<-@#txF8Yf5T%@5<;+9Hf3X*m0RBl7N!F#oL z<^025a^q@t>jzqD{p1ec4ZHL1<zJ^uW{nLBxAtUI1K-6Tf^LdlCX=4oyKPw)F zlpZU3ms8vIWy`9m#nVxpe?3XNzW-JRXLXo=yH=kl^H0lyM(aRAcDDxCYI>-9Pp#bm;MDG12f{hS_>}+=TLu*W^ZW|tUI{gXv$=q*G z2>FG_{rlI#xweeOw;edpiYP54r$(wvZ?iGDfViilg$X1H>|cMx(nkk0ZsL;z-i59@ zIxqF7MH`8M<0Z+A#0&o_EGC@4Az8jX{iJ%v)xg`YNb{lYf9p3UT!I<;@7c;m&PTWt z1M&7Hm8N(%{XS>4i3TICn|zmbhj0F{(ObNI_%3Z@iHr@&eLQL)OANsy)4sRabfZ~_ z=uCo|of$AU$z#$h1k!#tu`DMoZy7Xg_&rU$@{WSSgw{n)tB z+d#kYGG~(`e+O2i^zLe6IwC9W>LzX^%_`}vs(e)#l}{SBDqXaXdfK;fz=HkEgQc56 zsXojn61T){Qu0_Ux7*BJtBBvCt`{$Jil3h~f8lH(T&AQP=PP`)Do#39)y$&f83^mE zjG+TUUQG><_kzDjuhvw`sA(~>Gepknalnm)sr#0Pf4IzG?#5D&OwsC?&J&n`jXO9d zh9$$fX~T2sADn&N#~hra_$N>2%n{}+G7f#QpSyVRA?pXh44zv-J1^mN$Zj<@Qb67U zx`_~>?`kCZ{$*vI&o;SK{w~tBt3~b2@g7sTtxPc$rmb-1NLX2E<>Iw&0(^ag;ra4V ze3>~&f3|NP(Uv@*V(I4@q5H64nRr-6KIgc8$-}0@7d9mho;xe**m|)H(`ifx<{wLG zj_AOJLW@`J-q*3t>^3W&>;i02Woptxdk1= z2PU;k#+JWqwenHe+#6FXpdz|-`xQR+#~PVYDUi9n6YF#~?pDvOlY z9>MaCQaut5e4a!MUcDp?Q{p?PJEHz=yYyMkrf8jtuUvg1bHaH~C-xL~1)jHn-J~GX zf7e=vj8MIWjSk$MoX@#?L6IIw<{sx(mm2Sqm79}VcVkrT4}JE!Q1~2(nrr80P=hS= zMWfFgg zkPcGw)!L6RA}qR&U?Z@cgH7Gg{?a_|f3N1%;PN2x?Z~%`3E5`(zRovM8HRpS2G@x* z^CjIX}p$G#P{A|GB z+4rAQp1F`+9+cGjjAjLC9z0serf^6o7LQ5%%dupoM(Oid(XZ3g?VyJ_IGZuGe~LxN zd$Hhc`4sJ~yuxo=6imCn9?=wXno|s19?HTEb-&q4{yf^+fKqX)X3+ zQIGe9zA2Eh*FDVyQ0oe?Mu_4HLF?%Q`@7E}%1)r)7va;X51Z&JX)9V{$ zmCa06RJt(uJ6I2!pK|BL87Y7<7|1gt#&9)EHy`?S-nK<6Bh^Pk*se6_lE#z(EkPuX z4uD7z$YHGZG}|B&>_Jv5e^B`~xEZe1t^pR(M3YFz=|F)g9!nAFr3rmk6J*+)q#P7k zOP~YCle#gGMiNDl4#de&(-B|OLjxI3OvJ=SSeXJSMs(#eCCgw&WI!B^lfX#Oh`$Ey z?wk%a3*)U1pTXj>k^V*!U++TK?ZDKkux^I++jNJ4+tw2 zh;)Eb6ppu{CLGzIrJW#?B8{B?1PK1e+n)R6!?Lh{z+?W02UA&bR%BkR{DZ1&|MXlx zvl4+Het*!F;2%}q{;c4yK?|>pLNh8Vd|LcIFs*x*f3ka8t>!gxYU=pDSTTFm&oUtA zK{@i|MN=RQ){nbAe~s+7i80cEtjqizTP;fl0|iqx_cg%{Yd_L~VmoC7g%t~2!T4eX zew}o_+)mAsWE4%YPec;pBklVU%Sex0ue{y2P?er(CnNlRo*L7!*0X3JpDzpHzQ+_{%)^TjPN>{mFSc&;^S{z|aPmPJ2oi5z%4$Y$FB@e;<^39vsByq~<+&QibEe zROpwlR)-Gn%MreC)i{3-cD@+F<_W2g`B03c{qzXt669cCU)Oe5NlAWd>fO-Q4(2Gi z;t+#iRJg;4SYQVdQfe_>2- zZbAbEP-f@(gX6X2@bzQnPIznma|zGx9ngSf$@%WSfAWa8Sb%L&+QIKBZ4k+*57&O+ zIsMY6ycev~(kMbg&7T~7eikWmO;l?b)7dDZ1*jQKwZ{SsG9!&lm+1=5ii8<5?bH@)y@6V-H1e_<)Xsc4evtx;+ zt}iI1FYdg!(+X%>UV4kp_*e0+|Agajid+2(OouV^`O{0?O^m>lf`4EnF}-5D-1iPq zqqe#dxBszua#*a;Lsq5js)8kH}VkOGzxPJ53b z*;B0PK)&X_2dPk zabjh+njPLghB(<%Liz@7-1*)VAhxKH#hxR16n8d^$+tpiPT?->k+j8;n7Tfee_sbf z>`rSKuRvVJ+?kk`?3!9R6RPKAunj-HtKE)-L({J6gRUN7=AOSO9$??F;p~V z`T@I*5@K6Jf-5%3$cEJWL~#-d%zcVS#?48MzbiiZZd5+@p6&e1z}4QU@HqY5CbQ8^ zdp(bSN2FjEHSFmC$@WGi70v<#V?g^7A_`vjB|UBot6>7|X~nQl@twHV<&oF+U(*x+#v9h_%Q&4>bZ@VZ+d~k^ zcys#b-6`imZ3|ib8kx1%fe)`T0q_V+ClalXIu6dogJe=jl2bpaI9;lYz`izkv^nYX zOy2`G>M=`+>M)PH{a)z#^2$31(0n)^ElADzZo}^wnshdsU>{pJfB5|EhMf7S+%Izi z+Vz8bl8-!{KBKqA$=a=JKIpnc5o9JQKuuvF^ZaDWw!RX@k^qaB5~+S~aLoTQ)~Cs_ z9Pc2|U$ftFKJ%^lOlED_CiAO>UyBJ?P()Y?)_ZU`R(Fmq2XV*w%Y&af6GaLz|K?Ci zLV!%9!ox4kSt@(+e-bfS%)6QEsF5@ysD%Jub0eFeWQeIx_dlZpd-P(zv)49x-^}o= zZhO^Y-|(p?Qv$ZuV>;CSZC9`4v z%6Si^ydNecM;?74FwpTsgT_9E$BKuxkbJ4-20Gxfe0I-~)cC?~KS9K**P9h1kAw4R zxfVMz$(xR+Spr#6{M0zKSSYqvEPFkISUpTQ^;|z78rL28-0h+IlP4AOO)b&;Vf!w% zU~aL72rERnf0j@L3VU+`e;sxIRLm?JELi!8>pZ`&WnyhUj#y>z(|P*S@a2YmJl!%s zf~r=Sc~l-#6WbB!A$U!zkx~sKmV+4>vCw9}2a~7nulWax;Rad6W}misY{K7uZdN(` z?gQ({6Wo+S&-63KSgv;GDid>~=XlyC>elT;K6oaSe-9;Hws+V+hZ_t&3h)fVF^a+FtM8e~Ircjuy;1?4CX=<};gEn9qSz=drdT zpJhbpcr(en19-ceR_BoBh@3Np<2TsFN57w3k z75bnBnuz985}6{?+AkL);WwI%CR7GlT&W|j@HI8ECL^eI8XKunjzaOorJf@xpTx|k ze<}iV<~*&+T81p2ZDzks_h9GpWlnj~%AMF~>!v{A+v*m>q@a~4c3?ndqvPrvd+V#l zsg${M<8S$lw9=ov406J-mt(G^LHkSk5d4nISRM@nm%>#O)kU8x-@Hfb^T*AKAH_G-n2hC9|;qtGM>e25HW=$f0cub5w?b%YaVY!92yN)Q>qj-0Hv~O1Ws#oAJOY!6i)WYX(=7>v5mjZ-K&)m#Z|sHO^vaYp23cBV){3G;VQ*4Obqv9T|+ zYpjZKE~Wsxpq}ET2-=yHLglnbW_}X|4N@R5T#X zvxf+XE0r^&z1Vt(j#|cG#u`bwg<`tVVr?~JI#kPVMxz|AhkVr5$9sZalvyfW%y6rZ z7F4dv=c!?Ej(h692$Gf_f9Nc@25DU=>DLEti*!0l~cn#nA*Ix*>HRw%W-v-6@K+&aJkV;WI$0vszp(h<6UM0 zyTd0zSH(l`jM=vEe;~382ij0w@UD}%TDBe-zZ1oD+`2oxWNJXZJVxXq`;{&RvGwW* z;{kcr3wH-1Y%rbb?FOO6;_yUFO?yeHRVfsJ;aq91$g0*Cs?D7`@#a<8wS*%>ZQ1!A z?=;`qFp?w&Qu_bcdlzu1-)?XGD@uh#A;%#^jzy>(rjn4Pl5tE`+5dO%_xF3BcmIFS{_W@4 z@9w(X*X8s z@2obXFU1xx#RIx=KqPHR1aY_aCQt6ASjAV)e~Aj0KN?SVvHLQK@fq6Cgp3_?$DcIC zS2s$0yLd^NFf$LJif?S`M5e<`Da2>Cm2|%THDF zlIpJXSAT-m#;G@lY9MyRD`=F%wuTl0Jx-9v^(vK5qap_16bp5{zMAhX^U^CP*89lR z2q%%Xdu>4TJJghNhN%{>|3r=@V)G!obR?*nbzij%u{dwn+iS+8qwj39B(x39Pi*9YeHD|ZCDj`js;$JWxute{e(vLIEX?hqJEktSg;`yf zUI$pc_nOG}cY?}Sn-a;c%HyVOsC~*mi~0^-_@>6?W@~38Fm|d+y_)M}m?vi>v{wLQ zZYHiXcym8xoW2i2(T3h6T6ACzf5JxLhaZkr)M=kr&Mygyx}x|N;x?#y>(+(huJFS( zlW`~SAI>UHWQz1q4|va|dI)_k8ocrL8hbc>f&r8#q*FMcL4-KWo}Yc^#`~T3l-p~6 zPimMfo~>+^Fz0dfGf%nGKbs0HuX-}VHJ7^nqBJb>GhFqNf~ z&mQS3ES!mB^N>96Astt`_4%;Kr4MFSb^M~WK)_rRW*1q2be$e^Ny%g!la3^GlGI%=zG}m)&QKln;_SNj=25Xq)zotnsar zmih`ST|I&yJ}6lWeoC$@WidR$kvdtkn^Hi!PR>=vr&DAC5=pzN2-^$Rm`LbJHCjT+ z$a(C?jNTieL6&Hc!jWpNs07M5z~8B<{|Qx&=|r%?I#U~^$(ocKf3W^k9hqTleNa(g zrJ~NK#eT2^vG-3Yx%ZL@NVer-Nd@eo-^hR&s?6oz4i} zOjFT2Hi8DWZaO@9Vz)$*qTV5g{tEAB~?$B44d7Va?$4&UF(y~lM8GOzI{E~BXZU6~!C((v&8RN<$g!UBUFk^>E!Q~p@L$KpE6?d?rPp%H#e^owoxp=^~?EylSZkczF=9jc# zvg}SVwWg22_j~Ec*aUb}l}U;>JQVR_^L9?S(0#GdY~J^w%YcQJb0d4V*imv8;W@n7 zj0(o~_V1c9ndv%(DH$g}p;#8bzh;=<@25|KCxwoS&P>k1`Io(80E!IrSY(`nj2U4+ zWD!tTf9r%&U#&IJiyjHMO<+F1D^ofC{pdR>7jDg)hq~2%?*1F)ESl`VOlvD~$Nf_( zW6RW%3lP}I?>+q8`>E_nP;as%)&wnpn_O+>jw1A{N5d6V4HVy;xGXl=7Nr}vVc*rf zr@WDs{u#3mez$*bHx?C^abj@!!(9yl6+_8>e{Y|Y!v4JqVdb|u<>KTLLLBGD-~L%W z8_3-BeNUKJ={nYAn>+&ESPg{0ESlhiE1)fv#9i=_j*kqWAgBkRto^NTe}YGX)`A&8 z#U|}O!GfOg9a$nj!-QqOdAPp3Nn3;~cY~6M`jnq0Y2QO*((XQb{)zB$-E2L7sHm~qQ6$4CYf1%pC#_|xwi7HDgMEdrrHmTQUX9m5}=NxQ) zr^jZZV9IYx+$-}!C0CX^`GnP@A0HhqJc!M(R{VU#{@rEX!XuO-zhGCryGueHDLDMG zb=wio>5`!evRzPY^h>GhdPfvimIL;`;x$C}c{tGb#}Ybb%<9U?&R7U9d8(s1enm{qx|1z=;HXWvHA>##y|YRr$CTf5yxNY@(@hic`&W5!xZpO}L7O0R!jz*(DO zO>QP$y!#X2ml>am1LbuC( z&TlL`j@8%P#Fm&lT|5^svHa@gf8*r6uW@od6djAcQ4o7u=W} zSb(u+04;%pxpti+gI>23EJpady39~CZR+6O_dH zZHb(}(b(Xh!(lD2YC7;R*ptWW{pfi(?;7v4RLXhMfxX3NWp_2?XXS_2T8}oR67*X- zg~Ifop9(OBiMSl}VptQm5RJ*=zF?2V2Dx@4J>X@2$_HHE+JB$=KPM6K zV@kd*(Bm#!r%KrgwnB6(JKOC9%>;MCZU*4gPd*M_E+jf%C+aV66*B={>xjJ6iCEgD zGrHDw5f^JK8nYEH=dm>I(e85MG)KL;Yoo(~jaU73NQWV97PJdc@K5%rt_w>B8GpPRN^>asr5aC*i#glr$@8?PWnUlS*q$76Kbw3KZ}aY53=d#-J%k-#xtbFqKWS}^vFUaW+JEc3ESlK$df@T$O@jV# zx|6-bn{nyvEHBaG`oM7!10DgbmvSMyZT?jkk<|_DLpL*ovrJ5?>whyQe#m4$Gr+g-9F^!x(a=AJy7bfd&9fL z*J|48Ldgm%>yuUMo7Y$+6Hez^=3I*k*==?NI1s{O!raZ{$@(I19a402@9T1gYBv`Q zoa51D;~$7Bp0RxT@Ft7?cRB0-n^OF}>q)=&@Q>f!|1YX{%YU@aGXQZ>BYMLd2JnT; zeONyV{GGKBK2m|%WHP*nn~*H1(nDwn-Tm;sbmO} z+&ZmLo9DmnS${&};Shcv0hac_J@+(^?)DfI^BEGcSa5DdkdLh{R}*I1%(G>y2asw@ zRBh^(EEoTN&#mMm48I+<@z%BV`9C+`{1L<+wPyV z^sw5Rq&>9vI$Qw-`bRM>b_xS{s&?^}+p zNOd03Qh!6bz_@vrj)`ht^uKM>HmxgYV-U+CeDZh=Ye=BD`B%v!Z-<^I-$1D>t$J*_ zJj4}t>_6;$I$vrc<>zJCzilZ(kc|E2x>+Pb!>O|e+qdI~KQCI&U2<=3lG?5N_z4g~K) z>DHt0#xzcgs_mEYBBh3sw_5MNOV&~e^Gc#GKKtxG147re@lZLKn(zr(cc!|%VgHOJ zL9jGJ^Y{%OxmKxbhCHJpVXhu?aImXH$baqL_1$V+PX$p9V;;Wr+6 z#Un2Y)SfC2Y#YyS<1svRC{g8j>K(~-7DrzNVQ~+dU==M7dWd+4^o(e+3@36ole%dL z_qk@JTm=h1bioQP>yC}pQC9B=(_Uos6ds|a9RB^+%Z_8u+6IVOmJ!7$UG!fW2+5sI+rVXFH zl1TOkH}`LgK>Zm&xc20~)PL+bFh@Sa0CW=*7yzN5E|)IaPw=5ffkZY`uT8i^p;PDyG0r zwJ+HBk9Q0CZLMT?tbgMFo@Du(-eSK=SZt&sLl^+|1$fO#jEw;x<9!&w)y?NDo3nrG z<1d(N4*pJI0IsXsI4qK;)(yFB(fNB1=f+kkU+Iu!u;g^&<|0D5d-u(+im*w0ABQ?; z^wEG#htpGe&LYl&MGq~WCL;8^QDyAQOsK}0k5z;;g9y*yEq|u8qQa_NQetUvWbY*B zwXGnng>^bov-&_yry%&6yE;1lM^dnJo^8AiJwQ5PYBqO0bzi!HA!krpz@=7?3Dr19 z-(op=>&pByAG;U(mZqJ}a|<>6`jb(P@G+CCv{Wrcq$_9~KV9t5=0Cl*dLsx@rkNvd znREQdr>h+*>VMN)(*1ltJtp&uX?epMr3$1?*@151xT4%Rm8XwF4{%xL#8l?rxa#w$ zr0tH3xo5tSEw8_!cIry0OBBL4l} zpfkwl#g#>`KY12P_AC#Gyr4T>l#$TbZ9fSc!jzgkW&kdeS6e%(Y8ytLX!+!c8`<7% zK7V(w|9>T!xWmqCpZ!z}5iqup~{zoq;3-MU-FmS5E{SPluk zV}c%UBXQ$ELa@Qw@cxLqVmG__>M<@gA#rC7rXD39xvy)0oqPHS^brQY`Z3&aXYO5k zSd&Q7>a1$|%F4=K_oCj=6OJj*WwF4!ce~z-aDRSB+|Nn5=X*J6_SsOk^wu63)naq4 zZ_xhVY6SFm3|6f2s8JS#lmv&w!lXdSHh-!0xh&kbiWj4!j%eKg zOMh4X{T0{$ak;|35r5F#xlH+d6{^Po9LxF{fXSZU^Zzum?$icg0Nu|64$&Lc`e~X| z7MPS@ncaW#ZvCx34u2<;OQz9sZANBfVk5sRy6-eCAnpDELNir3ikR3w_p|C3lmBA! z|0E{=8x0fwPA2Eb(wtfPOMQ>nM7p-uPJiT@sr6PKnk0D97(lF@BA86&`+NG>{Da5u z@ip={KKS2Orv4r0I%Vy5*#8Q+y@CTkw_7U8Vz~<-26fAo`03W zlc{1avt|!tUT@7Y&E&N~)I3&4nR#-|!CSnEWdGC@2B6PEgfpSXPHE|sZ9zTdq9MB3 zCdXn0Dk->g4nfuIz03e|L&zv5TG=U!d>M?JvbLa!u(vY+svOh!K58-VbU4@!v4*VC zDKgp;n1BT`fY$m7K88?%GbP1 zHdI@!FuU~9flwr{|3qR-@_yNz_h0_eO?rNB{H-d}f1lJL%V2Z_$%704Lw_!sZe;*T z_B*hX`52|cO>WwGoIA}#qe(fxRO}y9vHz6^>)$FB_V@B#fest_{SJerPct_oY38qd z7YRN9I|kmQOOo@Lhq~qOXjSl!9)Gx10)&MDG@pQf@6p=F03bqh^sH6+)t=wRozH<_ z{{++gQ z`-36re+Sbs(KVPwt(wjN;s4Ehav@xwI~5*1>;91ZcXu)UM-83d(Xk^<)?jMhTpNUBq`&|+2=2erGd>QKXM@Ye6BVp-PJo_~UA{uu-O=lH1;&fH1Y>biCIWn5v`%#*HH`($%o zB@~{-eau;4Jwp7>htugPTzY zs6xbJh039ga(yH&U8BaAm&4;iCu8qferrk3O+F^yaOA|q&3`1joeZfS29V4ED7Fk> z6bGWGE>^&%P-{>~nFcMQpPoAQg|rwQMj@RcRL6K}Nf2h5stkjs_uUhb z8i^}B)brqE&1y{yTYUFLiF~z1l9!ZV@g z!v%qMyx17^hpT2&N1l0Mlv@AyjVQL00gKPg4~bbhQu?@ zvmLvcRQ_Ce_L)~|(g#a^>C%wB?2GUwuNZR%Agsd`pno~t&`?vs0KyQ)p=$;#6B6lZ zhc0p7Pu_FQMCyS1a8VP=2^n6S*laR3(*)-DnT0@GA4uc$4^{oR`)hitulWkwhbA$a zJI3WtVliqqnRNz5mQ}`s2*}IiP8?h)Kt^})=NK|oSTpO`>};5|ZLiyTr=-W-QAwZg zUkZH1J%22;lS6O;j?F`C+}O!s3Ux=(l@fpR&>o^90X15_T0ODMG?J-m#hWr40p!qyxpsR)0L}IwRtI2A=@?@k^usjz<5F4~?-V5`X$z`+)%E>%(er_5kNAU%cvdHPF7A zu^h6BcN-Bt)tL{7M=Sbk^)+8UOLfS;Y8-@H4S||{a1}d71V>c#Iam@X=?}-pdrx~R z-E)+-tgI-L)G|&neJ=i;b=1OM5AbO`PKXSn`sDWFFhcWHcH)Bt;@z%qZS;LTXCLv$ zn14A+`E`9@ZGx9$nvmh>q+ysS6#S+B%-bvmuwZ`Hw_X4MbLN`XJHi2*#5CUDdIhne8!AL1AM4~o(KS03;`D@Zc~ z9%Y?okE6@dd+2;bRN_z9$_7vRoQ2hgxqr?VOMCKtIB(+t=-ZgwgwV!M$Pg6vg$TW^ zaTk2GzZgN?DQZ~pizolv^5mZqz`3M(v-(vZ@RxnApCbv8?=t|5hYJIky+hiG=`#Rq zT5L6mpX{hz`DANxGoq3D=jGS^&mDz#CzILfbxZJV|9T|qFTNahYPxa=e5tYe-+yr* z?@y%czqLv5Cxmg{DrI_96wk-OFJ~9JDOQ`*dKtWA05fkwboiOeZT|r?>WqtG0B;@@ zQh(aCLV_%sclxE~k5djnPk=T$O~KiPe(tOL|Fl^B@9^Hgh@F4Uq_4P9|JhjtzvJP2 zglJ0~cPH7Bb!X%mKqLd;)$-pHIe*Qi+M~grjJ8u1&rnNMtvxMxMCF8d5l??HbT@?+ zHV!Ytw0Mm5miCFa!~$M%quu0A zI~heeWNR%a>2mel#Ky%H0q04QNAv#SnYn+);QlkhNbe_|s^&NQ7T5|HfPbi3z9n74 zqjt}msnl{1_5K(3b`-&Q_c(={stnp#eGBTpQLDCT232E<=Mh^}sXz-8+FY$JSpTYI z9NhcoMKgX+??awp2v(plblPLO+C1dQL&IZFt`H~Fl6+_DcH8P-`x0HyPVV)M9Lw`0VXBW2fx3vigm#(GqZ)M}l4jg`w6pC0qcs2u|qu;*8 zMaT!-m}28;=M6Wz&in}hL;*hO|6C2L10qMyc?+Q-}MLpZ>n}3MEmGjAqQ=i?p z6KO~ohd$*T>@!TA>?Cr*_R@Cyhf@{ljBDmN`?NfR>L zFhW0zO!;5W2)Eih+TQQAe^V}>(l7bxqtbiuLh=`OJl8)#oa>(|cJ?0cU|KzJDi(it zS@pa5b#&ekID&|jU@WXBmM96=9Kz|jpfWjf^XBoi22Xa510gS+2GW05GASKJ}Ld4K6ccq@eJ%K&n1i3l14 zC}RNAORMO=1pM3?KmuyRy-}3`phys^Er>9}6c_OM5vVf*fG?xSwG>heK<1zeF*PQV zCbSyI09I*f3_w7i%9IwJkZm=LAk&tkDASc1X*qu5Y0E#VN@z&Q}hZV2MBD`KtRV zzhW2R&&~#zAbFdLqVPE>5s^EMCz)0N1e^%tU!+TsCtpEDM(C3H6QI#5i%0gMr#%v8 zGv@iTBIZtq?aF;zbEun-cF9oWLC3)5*8|Pw=7MZud&Nm=vzXnD+yRWi40&?a;L$j;Rt-=(f`S9Wf27%fQy`h zkM-mD%ZWa>hLn6WJtXjVDAKR>GOUzVB&*H|c7I)CHP|Opa++DPl`Ck&mf> zbuTbMPjNBuTebM#W>bC<`iBWEduW1=onDRD7~KeFC0!@_hQ}#NN}cF+Oe(sJmwft~ zprOgH^8Q$0sU_65w*1b0<=;H|KR9TW$;bPt>3^Lu3}9K}HGE-scqg2K!WMwnoW-kj?`$ zX4UR4PJtnrx5woRAI?z1Eav>FhXq>UmCqrS{>|mcfT9aoYH~# z6Fe?aHEmKm5iKaYI2MWrMU#4ooJKUleS#e)mtxN}iY)zDbg?nYc2M0itbfR>i@9_T zntih4>~>IoAbV4m%#2Pnq4&xV%0<-Ogvg#C8T2G5Fux$pWq#;P$k7+KXYa@fZ#w*7 zN19F}szG?kX~bAAcG*sv41W!8(Vgz|qbsT!SG#=Akj+aHyOeD?)2fp5W>sK- zLv(s_P`q>rR}{e<5su)jOn*#(9U~Xx#%2gVix&OSoaW~DZVXDVOP^x^_Il3oJ~av- z#YM|bHfcL|c#X4@LWkkE6L-Nz;Ork1D?~3&?xpIvlMGf~o#cqVCpy$`;b>BMDf8T! zaTx`_?;)_Z>vp1EL`;hh>~k+gpP%)-jwk~N3s1X0A<2(%IWb4U(tlS){k`Kx<~EzT z(MvN^NfZup0SQTNiJfk^OClLiHQv~urOQs)J+NxCw8^QHL374!b+6@a*~pw?*`B?% z)9&g>A7Kes5DOC@ckO>~jEg_Y0B#x`ndsOwcJf@0GHdy0Dq;L8?4Glx$z^KNmkkJ6 zKCulPNrXCAkkfIioPQs3@6;A2MmDzIlUkE9IdjxUF)l~LvEy5^v-nF*iO<>vqySo( z7Mxg3;v^>$KW`~kFaVK&L&hUG)#ih1QVl~7ae-dQ_JQn{K;D}YC87<~EL z1yl)@0aPg!5$HDZZ})QH#*ky7=SQC_(GCv+ysYrM{FPOxbK@)X0@s2DIelSbvKwjm>kM}Mku zA6&!(AG1$8JicW^&XJXD^Ok$n=KU7y6PN9dre3EQFxS*&=RiY`5S3eL9CTg~zk8z? zVI}>|c*O(b!+$pn-{i$>m3Brr>0Q~)sj~d>bRPgKwCG1vYIJ&$?;;+w|pU6U@_5mdnu}P-bvxB-{`$R z0x`~%;0k{+i}Xs3WlXi-x6i7N2OD;w_M)8kFem>4>O~gB;@zzbsziaIAlyJKgzwE6bq#ut&JNKzv?cV;zOM}hVN>{={`F(Bt- zzq_^#;~Zk>lb2Vw1WrbpC2N?+;gi)T`23Qa#4waIuwiym2)UA&iSwr(#f<9W#7b!? zBvcixh@wk7wu0$ftBlPH*cU>?-KB11d%ks5nFTIq^&8c0V z!vM;eSOk@ibw;K+NUIfxiRM2^jv40JgnwaXJAEZt2F|NYvL@6(&WCW1TFWAPn3xYy z$&KeBDwCocXwMjc!|4i&D5UkiVrLZ8sPI+lRNGTkUdhg*(9AD~X1hLpfA=ozt~lu3 zVT&0yRD}TkHH9B)LU@x!v3IxjY6C@NpQ^Rh_MOz(TbyZJ^FYV>WV6TS9^2%L9)AN7 zCmXM)Hpq~wXt^kt#4&-H{%{@frL@>-P>7CXO~Nn`G3j6IH@G}=xO0y(nB8n>K8cnHHiDC@@U5#sjRVN&A9|)@0}|(2FK^$j&>=-gMSq=2d_~K zEi=8@LSb#L$Wt9l7GJcIK``~m?(_B^f8Bel!RTm~Z{+nT-D4 zb#vy^?W1vrD*aR3@Qr)vgQzO@C@lvfh@?$^^RPi;+Dny z2>v9+lzRVLqs|z;3slCJ5|@3(=4RvY&*F=LspyLbio|ydTAk;qzxTOUX1nd;B|j|d z#$pWh2GODuG~P)PC4Yh=>#Zm^+-sfNmBVZCXe+xZ*VNMUI#~*@sw{e|s%|&!ak8+` zH+~{L=nq;qO2pg20mho`O>mb#L94q3_?{nGQQqkjB@Y9~dNy6z>UAmh0 z;t$sC+S|lFFO#TsO^XNX#S<`4+&FrQ>+I^a6YOhLOj*m|GJhswyv|egN!;9?dLxnn zEyo0JM6fX-H$uS-U~m8U$_IR7I|*5JkD?HvJ>`{hz~ABY=*dO*b={dArSb~OK{lC& zi$G0?&XF*gfJ^IFKUNNJENMQB2;vCp$`Ggt&n>UGQ_9Zvv_GL(@Y*Mxbmo6C?dI-O zauB`c5t9DA5`TcDUBBhu1`;Lv;36OY#6-cAYA8zaxbZVc&pcKI$#A2Cx#aZHN{hlf zz!Ic?HIf0`gOHDN<1?o*2aPF;Oc{&fb_utA6`{FH!o2^P$VhEWlGLCFSRsi0!RrSu ztlO%{Pl;pY^!+Mu7ZbFmB@MvC^s9~%(hjv$*hL$X1@G3>+s~dUJqXp&D%~+@nK%P$WOpl zYXqd%BxFj5+=>N_gWCHE9IXq}V6ml-iNmC1$NkBOH{L z*CPiCGH9C2g?vn(?54;D*ys;vakzTD514CD(i(R6t{ERgEBBHLdG0$ZFaQO^6i!{< z`*oJA8_6+rA1zm6D}DbG!L)^+DJI*ZCB=9ZJ%2aVHTWOd=yRGrJ!D*DX?U1A7(M zd4IB-V~T^5nDAnnhN3!C4Bcx{&HZsz_w{E)#rlxNtHsk4m8tU_d8Sf?Ik&5EipB^%KjOnEycm zw-WY7`Y?!Ks!KTm^=1I!Dmo(Ymdm>_F6lXfs!UcV`xqZPsL+vnFgtsL=lE-cpL z?B{;du=HG&oE`;_ob;RT5K|ZEc7OLZzJGx0(ofN+&U&?g#*|wT75w?Qj~?|V)8w2d zCIs4QI!EW*5-;sz(tK^^6p>VPe$zGeG&TShHURmoAJKRk-VD)^BOmZWb2lxshh2BB z2}MtJUcRoybLfX~kn6yQH+tv`FUzZRE_AAGb2E2)l~%6=b~hF_CqyPH6n}4E!~)!o ztQ=FXOXa%I7Zo>==d5ERnPO$?eXFbt%lnQ6LORXl`M7GlGcnSJd=&=b-p{lO7A&k0 zax&f4**69}*{m0|!BS6dq1 zY?A7juM2?A3rk0`7Z*wU?yr2xlak##S=fM9QUq60$n9LRX82Am)4Ltc6>Vp zLgl9wZ=lA~NO@oH)!#_l&QC~su)tMocn|$53f<2}N@^2@KZ@%;dVi?msb9&=tQT1> z4pQ+Pexw*O^Kv*jz`6R;_Z{ND=Q&SL3?02^Z|Go16VGf2F1cF2g<)z1Q~1U z#x|R)d$@~?Tyl}(@PCpVe{(Zs%__Z>&&&^adVb5a70iTe*kX7^WH_=>oho5af!y0F z-HNszLoY;?N9{Y1y6~v%)~$QZw~}>F$bGzj4xq=pMfNI>a?|&7qAEbdUrAi-(P3`wjI;&L2tJM=ZGX~A8kOC$g=k5|L#^A# zGSg=s-!AnL%~AiFN?N3evUUZo4GB_S+qjKqe$w2-vr=l8m* za&`lySik>Ke1Bi>!2@48N4MO!OlcKbK}1RiMtDOTOnNbJbO7p)(^eTW_;})-oNZgR z>7?ERt(*bTCGYB!qLH5G*yX@?!CvFJL?2Qk?IqZ$_kC~91OypXX=A#nWN&4A^8`~+ z3A?9=4GIC9UXS5b?Csi+8J#BtQ_@pHaAfFDhzJ?R1%GcX8#7YPqkPLt?eSLko4E8i z`r4)a2VrX#^Tn{sOyke`6iv6q4j?;I^a3n4?WD-=)0wW~FGTKeq$u@Fy}Mf-b}*pX z%M?;b7aDmFHN*AEw{6=W>$j{3UE39)W7c-(Vt?cCVV3IunBICU5EgjzRrvSMS{6)! z%EiF|u76fl?%bAV3jQ+;;Mr*+gcf#6nRXVv(}v(L$(43y#MbH62pB6=mZ;p067clU zXia_N7k@x3&^GM;xv=khGRzIWX0zPOW_iVJmV*+2f{K{)zR3VwA?*4@RFj= z;7Cit>7RIdJxKANOK$~dpFfIUKYH?W(Z_QVGm?+O0h&S%3d8`ML1fwD&4_vpqD&JL z!pFBgl4*m4|CkO8iP7}`isj_9uZ~vlSdSJoQ@3}FA3Xn5bP3Izzj8F9f&p{}A8G@M z>3^t9wdMz?+9uoM;`hiKI5<`F9wJ2|Zl@2NTbowE)Nf71-IHS7Fpr@>`b+c)4X5un zq}aPYLWJuGyQ7uj(9+F_>qI?T+}@)JIG)?(F^5BMI`28-*cBc<8S#+CKX@OuAN`C} zLVJO70q>`B&?-Go^&bj=cP6BwO`w z!93{044~4hJ&S#MB!c;QgDS?X+)bDOw_}~SO`}^T@1z|*0?GH9R$+Cs;F3_YiDcXGoel;aeoJE zGXPp?4oZV*u6d2am?oEgSUt;Y=&q8;_H1o(eMxm>5DUqMX}ub%b(s| z{c-04pVv#Bqf^XXw}b%!{BtwN7%d-Mzo6^(IioC-)+!Q@!JTTUJ*|4KUXludpR#XZ zLpXL}51j+O!-(jpk~K)dWTzsi6@QVrs#ha(g?(RIUp)3QGktJU{k0SOT#`>$Q}>6~ zrl}KyIuZdgt#q9)Y)_1cN(k3NbYKrJqx;+j_ZgYaiZV%@Re(ty^2}AdWKjVY3G)%?75ri>YZNx_JUh0p%QY9ma`@Suq2C4L@l#KIS>;M8{*mh?+TK2q6n{cKW)zg{DA%>} zE-LE1fKQbXw2ugG$5bpc57pfA905goTQ#(zrH|w09W{1-?;69)W!`(Ov5G_1%yLAQ zw2~J!qaAlU{u2xB_`pu(H4}U<#j|?Z!3n$6PJmF4fe5LF!D8SFcsaeFB(d)^tlt|2 z&!T@;-|PojQ>BSx^`wCHaX?0Z}yk8zj z*{a^@r$~ECSD}B}c5aPL3_JT?$c7x@Vdlk2^4kn*e>Z-FAfrE?c(QRtHT#hTE5!`n ze2y*)<)*!8WOr-ivoT5pQLhnZQ;P!M_V21N&t3K#G?wyfQh#uoP4y|^Dcw~v`k)4& ziWI9B<9Fih_qS0FMnIeeN{VEg3rtdmG2O#ZU|~P zv-$*fW?)&73cfGtaa6WqRBEcbUg6{|n?6Yic9w!P*E1~9=1uGyfqJmpuqJRNssk0f zZ4pg9Nihm=n>qiDs_0ftTspL{w(w;VeSBOaBO#Wzm#6iR7^{46`=}ArsF*&5sY=9q z6C=&Y{(qFiqx!inj{;J1UpBjK@>*Y_?EdzxeY@gXV7joR9;a%BfPDr)k8od?A&oJu z!Px3$e$)lioFYx6X2)O|_?}cY+E`gUr-=yZ14H zfpdSNI)oMFX>{anB`SI9xbL2bDUMj#l>?(627isj6;~9y8Gu#z#EDUlt}dtvox`31 z$OvOm6g~+CFi7bE=ox&~vYzRJ1s1a!)WiCuFXUzl*wzKiNoSkq3Y^w<>&1_p&9rJ* zv}*cHXiK_yxMN5&m)CKE@7&e_g2l9Q972G(<3)%vRMF8Ml&!aKuqnXJXKLo^iLYtDFa?i6*gA*%2;n};0>}~MFTc|NH;j#?+QkRJe zC8@@6GM6nOo-e`fN_XQwrmeaBY)v7f%iK2<@s{+OKlwP7atowdF#ZeKX8*@ zW>4b|j5TYgSawfaVX!ZUuT!;*gA8>PZ63S-XsDX`vA2F!Wv&ZDfB)pA15=+E&=4qW zE;n(Al=Y3Ns1Fe~DTgR>w&q|w7!RpfrGN6fLPIPk&3qb1{EYN!fMt&0MwTNf&VTeT zAU>3*$w8_B@k6zTKh8+-=SIwwCWoaXhl9MrnT*rJx)m%htF_ndgr^h9vIuUHcdqeKf!mUnwmBAG6>`yMJ3vq9bINxH_^kbA1=u(%D^oZ(mv+K#XJP`&q!t zKM6xE+=QIHp7j>Q16^+XDwA)>%cn>e?e7yG28z^Je$D#GX(Mr=gniM8kGg0`xd3mr zYCKGpMa--wddz%-3cjHLZ0+;RRucM%YsCUH6{I_iNSqr{0it@46U1az#KslkV^K}<5Z3!wDtU>3YG(D?_2LC z-*WvNw{P#e0siQ(hXZ26#h zJvcrq75~`6w5Q}~-8Qw5(BFh&BAn*4V28xmax%DcX*#IR1!FxVon~a?R93q-D9D|< zKJor>i$pW9tKdnt1|0hWT!Cy~MMH3?IH(>GZWe3aIH=}Q?2#>R>2aa$i$WM*_(9N% zIjOrL;HxgkHg0Y@8h=%xl*<$q$J0nYA4hKMu$dgRbDjQVIlfP)e|JUl(JhMB#L6|p z$?Qq_b_U?0Og?Ubmmye?J`$0SD|(A;om;X&sG5q3L9w3QDyrjmCbZ_WgLY?q8KAcO3D`0bmyM5gkHz1X9*(HYo7=M;V$j0}z?n@Fs85pt^ z0BYDB^8MwyT`*30yqdmWo1#ayZf!xZR*bDydhj@UxLXcY^jZW?xjMRBk1M5ROqz?o zd-qPt&)lO;1mIrCh@qaQ<-)PAz!f44;P5HXm<7phaHf$QDWYCgTlHG%#KaXe@~PO5 zsU!Kj&}tzos((h}a(nH=ZYoUa!6{5@sQDZNxYHjs1*)bY6`!?>@e}v@uZ8)sWSkPe z;^F!JI*`G;c;&&YfjIE`NeK6Rat!qflW(5FCg4ZtdzZjmUSMYiz&mc^BBn9`xyf6Z z)qtLmtUcmro+T1~vP7KI_eY~Ixq)&W-fXHLB0(!qynomExbetjaQ5_P&45$A2&Liq zcQ(CeuO{27^jxN{M1|GJy?bB5LKDQ;oiDJu3%`>+!Qn7Kg_hd}}hxBa{cw zL$r5Py&|#$?txouU=F(YI|J~lw0XFeasCL}+@gEp(QR+YkpM6$lU4+F2ahIcITI95 z;Wejq;(tFF<&mQ&#f<_h=I=SC8oo9vUM!Z$+;i^xiQpPL`85mrzMlek2aPr`kMeS+ z+H;|rp_#L<8~F3N5ZN^xq!jxsGNpc|0f1jdRf6$wLMG`7;l1@p;s>#H%de>>%Y9-a zdsc|KO)`ko)6bJ?^|24H^@W34Gk?J0LU5AZRf)5CD3|eURgI+T(+uE^ z*?J^iq&5=o%KW=i2wZCQTLbiEasoH!rNc}VQyh^^QA(8*M6JV=Y?&<<3Ly;T$vYds67 zn_F>-v(v&T9PlS?l3fD zz1Zu}ACoXhL5sgX{9TThonh|-6@S+ATDMg8echp#5T6Pgp!f@9YlhVo)B>}#JpyJ{ zzG*9YCCw;J<~t-dgV^3zTwe~iM^$N`Izq-3mzEJ`X5C|%vx*Tj zbDF%+DQ2w<@T|tE7i$?SZ9X> zytYcyDoh@;bV?!D>x=3jg)8<{Wvd^E`o~N33$PSzqL6SCH7B@l1Z0vgsI@m@EpLCj zn>%7#*BABi=7&a2udj-YPk;I5t031SHl1)zMG_K~po zWai-Z`S7ftr;b0E{UG7Xd&qPe4KlpOpnNQ`ZDxB4i`VV?n2VNhYk$$fly2E8V3aD` zryX9GxSf_b*~of4n!lplJ4Q6ppA?-;<5GV^>up#%Mntr@1&Mwr^$}Y-U6VV~HQ^v5 zgKN~u-E6-aq;GZh%Y5ARPtQdLKvW1dAL^qX3#)&SUF8Gi7Q5PM}NM`5P?HK=pbf_5@7XmKCA?X>~X;Qm)knERMxk>U9$)cbUxDK z8NsaaPQhbM8(`QI)_2LI>g=9cbff)V-*(qDsUQY@x|f;yL9CKVAxsy`5wI}ba{IIUiz)n3u) z+23KwQph?s*TOOMn3xaRN6o9#uB(yNkTa%?2gaCEgnyB=EAL2JbWn8%V_<`(sA?2~ z3ob)_ZRkeGBh}YVYb!tNevk?K`o>F((@QH9+{V1F8)wda+`!KI;t3=6OsCMk(YYWj z!=x}mWKAF)N2XpGy#d^MgY@uM~Z>d}82bs^wm9WbWdCQ|L)yMLfg(SQ6fc>-+V|IB93X+Nr|jfz9=*QxYK^gz4%1Js#dKgs@&=)hkrlKe$zcuotXL@?E2uTt98@tsbbqI zv-$Z&#;l~r=d1UQ0!Qz~68d4C)kL+t1YafJd#9iNQ0cV;(Z2g$Ks6mgh3V2btq3fp zZE<8AUxJ|pQN$OU#qD5uk9vD#(dm+k;c(`CT#5euzQ)K_SmSy)>{1Kt;NRbbrvJZ{(|mm;u;zhBB@gxku~XFrwy=O8iJJ z(CoGFb`MJ3=!f)1HZ34=_*=S(Ct$*#^7>|ileXi)e0L$Km0nE5kvQgzh~Eb#!d%u zeInoJL=!F{qa@F6>n`)djc~ApcOHK{8TJ^T&`r{V&CP3e@@ZNI8Q?tC8+rzMBArD*mUWI*@Y`^&Gq|uXzdQ$kdF^7#o2PFWcaAg!WlNhF?I$4 zVf^qTM0V08z#{;w^16)HJ+4}p{MF^o?WF^&*gH=ZuLf2OwclBc{`9Fk?ofX_7r~^y z9B#uHY0nl!35Yn=K4Bz1phF$r&D6!8nT8GM7VIgf4(I=Nk*p?)vGk7KEH z5GH?^RO#mRWsecPH5EFhooy%o;oNFKfT5&g3oaji^TBtLRde4KYJdxLS?=U4liv=!QBauj1uGdpk#?ypomcOAYck75KGj=**y0DVbFhe16Nf_ee>2d zQdPrHZ~j)2_|qF9lPLyf_r;z^TaALnp(}qZgAm8U7=9DZ`qAqxBp=-9ZSC2$ z0@>!YCGJEe#X*H0B4j#S-(M}$meOk>XvRK32k|o8#ll!LFWTRqCCsM>EY*68D27Xm z@l#p|fH_cW#MO-3x}SeVJnJtoiSl*7{|6nEo4mO7e(F{|3Q#+g5`ayf%ORkPM{5>BUZ#Z>g^c8>R%<*jY*Yj+xcH%!* zGp;h+YoxBM2*CFa*rjm^5ht;^?}#3bfE8*pKWFJ(xz1C~dGojPjwAP6k^=`t>7bJm znVBZ$S9|!L%zRIW$ss0|tJyo2F}Zu)w~142>)ALbwTlmajihVL!#JMg+Ah9OG&Oel z5PHN-A2XQEm~nqFrkeyIuy-L3e-%a7LGj9~NI?gm{G{HpZ`VzJRZKX(J+)ubko%R| zG0zj_D#1(#i9|kP7%yd@4O{qoVfjnm_NspwDg_=yl;@6Au$X*hrQ^mmAl`l&C2#!X zSd5H&r&Nx#jR(Pn#+gnK!a{qbph*v>%PRpos3+g2Vmg0t>)`aLNb5Ltz*=wBawk4V zLo-&-?P`Z4$pSn(iG7T$s~p8&W4wQVqT%h210IJ;&$i|xo|nSw$cm@u`@VfS`o(er z!mauIV`voPeP)l6t*MXEo{J8o->ET6?Ythw_t^TL3~mb)`k>Tw?4oshICr!1gRs0u z4cJSR&TfA`6NdD~0Q22QUZg*CuBC73ROa3B;Eb$AMr0nyG5qa@W`yyJjX9t1KBPiw zM!OJg+8&ZzMltoFp4si5IUL~j`kO{C+qO}kVuHEW4fWhL6@glVOf!Y&G;aik4&sLE z<52S!BzNG#>OfWNt&-c{{K{?*Bjw0W+jkdvvMqm@y9}rMa9`i-2ZD|r z$-i(k2i)VSZTaX*e@)UyTf^=t$fSJn*?h=Do2|=Gf*Ic&6?LRx3huFb*tGjW&CmCq z6Ptghs5zX5Rn`kSOUoyx`-vXezs$=isz697Mc?YNwX95Sa%Z9|21&AcBKaB9bY#_4 ze?dyIT3^-N*<5jqeGS#6J{!~n7KVrcK{&A(Mo=I%_;Pca-%4z5w`{M7I=jrtyu0Rk zthfq-d1tS6?DNFS zZ0$14IvvDBlR%6tu`~i91k@$TdpSaN6SdX35lpF&)T-K%GHghDok6@^xUBR#} zI;gLmb((U4u(Wm4c}8am+?vL%vpj$JI2HFG<@AO(I`_;8rrt<3vL@{#@(|ihO^|E} zxOfC;(<-hmZ{|OK8L?F?p(L_+OW<0lw5n%`VTP3!69KjD_zAl55?XKQ8ihZTVUTh1 z$`z;pOa&NS87al>0wwUd8e_`@6| zA=fH}ATXmZ(Nr0_Uz^4RcL3T#i8Ti|iG1;J#W|iuy zi4N~G*WNrpeF4*$ujaXCxl!r4JA2 zq241Tke#Z(Kc}&T?R4YBlC@KjIN6d{&E_7wZ6VUr)pe%9zT^FZ5;}hZ8C8AIo7z9h z@rTJ;a7Ai)D;ONpuHxkvB2)6zcUNuULWG;b9ZjQ?Quhd;kUJk|rJiKG0y!gQ3;7L? z5VlAzKzbF>w|UHWSIdCRp^zD+Yh%-}cY%k4Q!3xY_fD{5<2g$>!{cXtKU1g9P3!t) znqX-8dfrRYDYy9TKtO+JxQ4@gSD}|eaF*Gvo31|;tr3Zg#>vzI{05ki2bA{~sMXXb z+4}naIDB4Re%$r^+fUUUAHoKfS|6((T66mdy$rxeCjkd6bT0cQ^+_H6hFxBlee*&^ zmT1P2uxy83H2>9Vm)4!?G>VXVR#Jg zlaK9@8&YOA$R~vM6~0g6f)H=2C$*p*6sAe?0USIC=^Y25n7C9o?V*9A<%%~PHJ!{{ z?+^1_Etb$@xwC&-<%2~fG3M3`I+u)7PQ2G@g&nMP=oGv?ux{Z|I>2Jfe8skxXWYQF zh5Sy=m?q4U1o}YOr*W|p(s9dp*FqMEEaj||_ofB;T`=+D_4&Fhxu*>VgCCpN_9uKa z5rnULv9B|uwHaFS*;ubSML4wAN>VT7H-Yvs9_{n8Opit@~}PG1_T^r6vEf)zRj#sty!hRU>pE4B{u zX=Un<-@_I#=I)SX<)=qKCrunb#J4yU&r;>dvKLC@4ore*Pz*Adn!XA#M`WBV1P{$H zoG7*c!qR^S7a|fHnxZZlJYKKP{*Badz+`T_UR&Vtf`5(qQ>_W`5vsSwV}pm`Ey!$e zIcgQ$;w4ayGpVln*7#7QWbRzEWpBmlI{zaXc|w>4!MU?WEHdxrXs2i+P)?d5MK>NB z+Jjdn+gz%6B`{nHuUbSZt4367Q43e)Em|Dx#2oJdmk{@L0p~NUMgryt?RY!?B)dZ_v`@&Si z)8I7U9~LdoDrM?kD#Xof2jUZk^}}-QoR40PlDI?%aiZFScL90y_PiHLjk=N4Y8hE( zf{lO89`%gv*hFbKsEC~99ji=>H=NmoJR-cq&cs2a83yAbfm(`=xl!G6h@j7h zds!=q5*8-TIW|az6E+PhwbU%+J#;*{p1pss&8345;%gUYgs3mPLXk%s@E~>O%bCaB z%yPOfyRv%)KHShd+5~2(6bZCDOdu@U$)&q_Bf(J9BS6KBB)UI8%Ib}bK2*S-a}M9O zhHC!3(`L?Ng$~-T#yX_)z)wuNCjK~Nibl6&kG8dib#l08uw4}vsu#G%q!`QgM+Sf2 z7b`4mdl?7A6q53Y@!fn}o7R|Hn--OuvLR#c7f4l^f;;46F4Y+p<IQF>GXo=m9i-b+$|8Zt0Lkxg@fVeAm9=V zxjgNSl!A*fINQAQPGs$R_iIPYfaia^h!cZHZpKY3vx+bU9qHK09u6v$MX%(8YY*^D z#4D?-ylroMXi`KrT<69M-py&uagRoEi|C{EFCNdIXM{7nV*xFSQHWE>PFv_2;6Uqa zhVE(qQf4q*x0RYou~>z2Q_fP0m5A)|t&X`W0cAe<#&y>_WlBHq3Rt9TcMX3?nhR+j zyE-ssdrywaO+?W+O9?isyXn5|ij;Fit0dPMKUH7f+YS-$IHp%@i*DuL@(~@XiTuWu zbKM;CRuA>y_ooq8{}^!ny;zS|lxvgg4vXsH)t|Rr`C;}yW7cmzu(3AjP?C`dWaTn>NuMq)6)N7qZFX!2y~s=;Wb&Z4*0yv_lR6u!UsE^ENkdFb<#_K=`L9+Osq#TJw>Zc zez|i=rsn;W+WLnigM^cHTOo07rvw!}GBfJ#9M?F+7HEmM$*}9-;1^as%k_C_?Q)*e zHFln&&1!@^ZyH{bMiPH(Y;e|Nt+==1L9>%2|NbNrt%?}7d5CR)Pqa$h)|zp5uXRW^ zRmsk%{zNK`PAk-V@WY!b4G57T@DtQ$6yX9^;95zlM(=Zr0L|*$&7H{N%Z-AKCwq87 zU9YjXOof>!JIGGu`ZWRyf0}5}E7&!|Spq0Hwt8mo>NU34WW0aq^N z&Wp4YK%UuW+L=X)#zftq3w-*aMO)*JtmR9oFtsm@Ue{0eWLc>2bL}kHqn#Mi#s?@j zc3REF<7BIeDjm&LEpNRgmtF*vIU2pQ@;Zx}+bK^9#bjW*<{Qr!-3!#o;!s?J<5$6e zg4OmU4kohb^GkoH1qB3IwdR!i>5MpdM9cIm$$1rpUX+Ou>#x!);_E+b+R-isFs7Z2 zn}CcqR9Tg1jqocC>+enssB}E@@jy2<$oD`$#DjhCNuBGC@(ClwByt{&JBg2?gyiJa z)3HZGNfVg%*%}74CWt|ddJ^UADj)Y|f%4dHs}3osOAUX8f1&iDHseyGAob;h_0l%e zos`5r5z+QC>T9U0)niyaD*k&LAMSBzhE?%^YOWY2c<}HPxCpa?z=o;tDTs$eZ{yo1sv;xG4EUqwy{7tZqEIdvF)9NyN~fL4<7QkNK@Ky zXh4B|d$Q7}#=Ah4$d}Lxbu&!Cy9kpG1WYOd1;u~z4`tpAmUS_Uv;@J8$j9JpfKkOF z+#{X@2TI=kjuNx_-!0Q!MheeDudXLqF{?hJuEiqUaLD z5+Mp{W{tg(wH<>*gwK+{kHkKR)Jq57;i|VMg+J^fz4vwaR753jW>N2kISaI{PMo5MkByb0~KU z`0!VPW$WG6j}e~PDb#-%!1_@! z$+q-*Ra$Dsr%z^bDg5_6SqQr%A8OK%|&cGr*rD2k5RKqaN;V9}q~Zw+L04xLD4&o4H~FNZ;Eru^Hw8S{h}F z=Um)ur_O7#ayMl$0WoCTukA;v=yn~r3Gjw&80Nlo&<*Ace!bH4Imdt2qUvm2ig^O) z;)L)%kILt6{(%KzKdcE{osL&n8O1oPi00v7Jo66UB;%6r<==brYCbCQZV>C;^S$Ea z_h+KUNb`Fefv03E$xt{G5Kh*HYu5lyglW#a%k^32YX#r3ELz?rrrq{Q6i~1ZWPW`_ znC;e85OZrbHgx+Ip>}@-*#{PZ`v6{Vx9l&{RLyD2#jhBqd^Uak1s$Y@o}*079ldR6 zxhLx8XmaT)d&e^2;&K9nKN|~6gNRb!9&5GouYBRyF(hhNQKeS&$VvElbFZQ*n1#D7 z=pfFI;m&AR8uTsm(^x5dgeP6QeFtX<&9;7oeN69tLO1uBG@E}Rc+EzNiTR4nFp_YE z@n9rY7jHy9<6WRZRHD7ZqD8+VRNCt6roQ`RvUM~%NJ)q)^H!z2-7{cjey@t~4S>B{ zu0>OXW>%ov_*F3=yKLYM(9)+`p)eJ)_4q^nLemL6bHB8A)#ih|J0oIpr`R5WD5KO@ z?V`7VWE$7A(4v1WI!Hjf)`ew4)%Z&>D3X-&IVf3A4N55tJ{5rs>gF13G;e~^*5!qm6I@l&%CNiavx zZ5a_G$_f_VQtk$!2^1Q+E<@!;E2=FCs=szjER!N|2Mp(4^qQC(e;D+O!h> zIu>&3TBlU30xew2V<}1l?PrxDiUdPf@@R5EOPPP?!9$U}gtVtxqK&0ITfz@N3#c^a z+6Bm2ZjCP9brS1S=kvda_Pa8vl5Qdj#P1Zeq@78>15|=T}5!#s7K0x*O zM;G$8AA>8Rfg?DlQ>IucvYBuDfG1)^E@>_kPkB+oao2M^JNnRghN%b+S*<_ zDvW_S;?(%vJ;-R#E3cVs#WPMrmv7-8`vkUt`qqBh1oC&>NqUBj-~LJ1Sza~mN3sJf zxc)@QQOb=vf=SR8$x@6%?r*okm%`9hq?51Zay;HYwkdg9XAHsA$z(ZT@;$H|xvBM&o69r@k{H51^&@;g$He-o3^zN`HJ8 zoe@)d=A7W1GqpDOqQq0*Fr;d!-30hS7KfjtzJw?N>bUaM5KH$#Yl6x}(zSnY`}6(& zN0L??Q3bUQL-_qF-kB?Bf1;`wX2^!dolOVH`NyDtmv8tgdW#(|Q$hQ9fUbdUV^`8K z*VXxLy+>D4kM3Wbs#6L3;>lFqkcKG>+iJUq#Py=rw-Hd zRwB3^4l7&}taw;jW8aaBoen-1`^s!)=mt1(L)Rr3L>3%+RHV zG}d$8?tSHceWa(_H_LyMWp9hbM+A3`axu86iBR%JO9c;B!aDFcl|jGjOP`<`L5Dw| zfpdLiDQ1(IYCOai&=ia+YzNai3%L<`fWs|dfGkPzY95=pnC}_^7L=b|n)`W2&kLpc zWl`Aom0w18Ow5Q8YGL^ivWC428nw)>je2xOua&49YiJY_6cm5K+cf4_hxSR*b4~eq zs^ghXPt(s(mh6@?h%6CG5%4G7pQ?YV&BqHA_SheOf@<0J(QfMrNZ;w(>vCn8jQD-t zE|U(r1I4J2Qh~X<#2}Iv(I(*@<*t9o$_sBxoxM$~`bEs`SO?p##nbAFff4}kaNMoq zk6N_QE2%IM2$+AO%W%zlP|N`Ndc&A@p+IN3b~3Dap50G>$GEr2>(Zkm`{U6e22$y{ zXfLR{`s~&Udi%vIm>pcRwyE&?)C2Ct8&@;k+g`Ykt9LG@3Z4plAb(6bcL0>mMlC|G z)yqNgBu?fCM{XV&DwumgmO>+aUfmmmZ-(_Q=bv0 z-6qaFrs*RlY5W9Om&G4&tGC}Ue(l++`4MVegOQ;j3Z~xa5_h(r%@_p51){@Fi9uIt zb8BZQ4+(5JpV!xYknHw-dyRGy>i0Tbc6!{=w~_7)wXnRs#Srs+u#9IWi~%O8gepn4 z&DBSI>=A!)Z;TVirHaR-4#~^_wyqm4q?f66eMtRNrx>C^WYhLPz z>E=FPGjQspa{Y_sot(G}Oep~co-F$tG%jPZ3p|Wa-rpug3II%T*H)a)Soj$(ZeFsM zP%6CUdnlsJ+Vf8ArM3Q;7he)VOnV!6GWpadj1_+(@m2OHP)gjr36!oz+SBwZgSWo9 zW-2szI&CziSOo;#4mA0#WuEs6PGrq^x$9 zLx@uD@rj$6inrXIU$NcI(sfDtIcUmw8BN-t&TW~t32i4>Pkvl+*tqa##GJqIbeGPc z>gj)E7hc7$>o+r2cXEpOAOQP5BN#J0CzfRh#Z?vlAV2UKWJoE)`4)h)Ve#BJv6xmP zoEp?(`iGyzWgjVnme)lEyYRq1w$?z6x=J+Hd1fOw_FW#@2L^z7G--+$01hIb^~zob zlDotbBYDQX_j*-P{;zk)7nLe;jZ#t(!A^hO0UKqI^8_eG@CGRu2q9W^(?P-%H58C5 zRe0F@L33s0lSb>T`S9tiit)Z4OycE;C(OD#3P9~J2;s+&TqyF3vaBi{genw4e%*MD zc296-#PBkxr^32cP)&*#0FX|%bp!j@~{PgM5K^}kNFFya$RO*s_7!`1E1sV?Z9-Ym@+Tb6l$GHP} z3@_Ow?3%ym3|T(H^Fs0@XZA4JxV|r7t!l9)Ap1EnJr}z2QdT9N6ag@;Amb0vTzJTF zxK^!Y4zzN*oThZV?zqEo$m(y0;svOGwEBOLrNauA zyYnf!?Fz)Q7@enwqF>WNKK0G*k9%-#CqzVNtNqsSekFJHC4PF)I2{N-+f@-m$U6US zy{8J#dPqbsLhI8x=>t)l<3bHgdZs59`7ZsOzYZ|`dDcb3M#qZ?2OwbOtQp*n$eSKi z*6H$kFd*FL)xz@pv+%McyTX5g;Kv498X{gbVviMbsJaZ=8o*_U@!hCL5P4!@^0v|h z9fZ~W?A%lx7wq{ZLdCY>71@?q)H-yI`h_X}Q%45h5FG?4E8~y6LiC|!T3rbHtB(V} zO2QI!yqZ%`5<%tO4_zlzO+p+CRl~Ctat=q|YX7Q;s6x#CUZL8JS>}I(aC^fDFIywZ zftY^GXCK}iYAzARt+8!5_;e2?81D7rjoiY>x%sG`2!A|de5^ETA=2C63lW^GTBXG& zd;))_J!8BxK#zs1@dU9oh$)IiV;AJd%Pz9d{s_8~f~-ZxYrod^7XxCPh>kjY?4s8# z4wu%wg+}`yh3cg{WfgxH@ksyV3%?GRp5ZAuv*XpX=9_N_Sh>w^YHP64BvUS zQ2KOz{@QB%Le{5h===?Qrq9 zpmOX^N+E=A@Iz2a5_PhcVER~#4mwocJ$P7ldcOO(Yw(xJlEi=OvvyDRhU8B_dS86P zv=nh`vyfm+2hHV^do?ymh$FYtN}w3F_NGyEcpCt1HQEN4xDAGyE|!{zk0F zBe-k!_I-|hwH1FMll#c#udgCieoSKNAc~LyLmfB~(dNLn6^B%Ri_gLOthxElj`KQC zWz|v}xU{RAeBY?{maC3fi0aEXA7jW@A@WcOwJaTP+dP7%gE+lDUnwWS>M%%;@L{o> z#zC_3i(0R58y;uLSJ`ua=dMAo4Qs>5JYL9OT2uajqHKTm-EvyLwas)!MbPHM>GHGP zQA}AELt@R(5;9*?RJM09nxqIuq_0mHS;g9<`-gBJKDxfnU6yw_>sS@be8U%}vLx5e zvxv*T1gxkpkRA+(X+vlpKZtM`?;ec~GOKNB`d}V%Tt0WLZE*L#y?F6umPro1O-m5+ z6y!7z8U%l)2wU~Fvl4gHcMu{CC|sno5$osDS^;W*Ps zn}F5wb)e(bN@n5i)GBSTh~tQBgpaLO|MZu}>%w(u$!`YM-cgD~JK+Ht<={qS-R`y; zL5x&cFk=HeatkQ~;zmyMJZ#T7VH9@PF;lqRHL!nQo;gsr*zIdY(R0cMA(_T0K-Pk5 z5uM7D>&e45eUn~$6=Qar{>-zxCJS-4cN2M<&0*K)O$; zjQ!D9>Qx4)IY;9*XXh5oIXs8l9Rf$3BOf!zCf?}OM`6@4zc3u=mFG(*8d5Reu( zHt&BqNs&#z?l?EoL6~PGe&7Px$){N?0ix*rYyD{ecFW7pZ^pc-K2}2M{#sMP^hy@T zDl5+eR49;5u<2SBM(}t;Ss1=mgn!ZIL-?8CY)q@ygCp_o(>7sCr#9{koX(s{0#TkZ zxM#?`T(jHjCY@d3cn7&~=EP4S8pk)HSjT^D5HP-ai#U@|M+fm%l@}{M7=68H>?J*6 z8YHFVxmWWJ!h1d@i%IT|G=e}IEUZCxqvn*m?9nk0>CH1$r7o4Q$aasZvS=@B*l~GG z9EaT1maF$~KHn&qBF-csSJGfLOK9A)bU`mkR074|aL+pOy`7VTc7>ha{E>aUTjqbw zV5S%E&s=5yvVnhA684UCkulb|e&QR3xEfsk9FmL_@mjl|7hv$F?Qx?qQghfu=A7HT z6qN&AOGd*)ME}F|C?xidZcij+LZne>pnRlP`=WD83e-g+XN~>KPnP3n#Zx2?{8N{i zAR>q{8gE`FSme82eS}bMH0(o=*KvRGeZO^Bf$opaZeDSXHSmxQ6b&n{967$W-yqD%+H4hA+~4%|Y>)slZB4|Yn@ zEREJ89P%D?Tx_y-)L->2PUOyl7F$I@YeT!#=SsRPv3!p)$0m!LjveG(ug>2i-DKrF zdDO64!r&&G@xbg`PLZ|3x{Yly0z2t#drXh4L2b(f-27fL|5V-6>)dbT&pqfrm8I{T zV`C_4xpKeakTD1mg6wpMXi|Sp64kq5tZ;3@^aq#9u*W`NObz$()h3u58Y07Ku%qus%A1xj@cFa4x8Oxn>7`c$OsouYjAeSMR#BZ)k*$mV~Yj}gVvHdEkw z_3dQBJgJGW6OS!S_?1>!X8%>pDwM@b$f0X{&-6>idPI1qV=#!N;5*YAw6?8|lVLNa z$BJ$;>k^c{xouz0oz(Q$G#^F;AUk>Kpf!J>mJYgo#)_8Ogj#?eqKFV0AJI7GC~On= zP;LZjZsyUH>=9x;wlRN8C#54=#Y`@Rc7GKL#~5`Z`BJd7-Xg$uQFnFyRJD*axCFt)_KY|3|vW zJoPQjwV~0@H7hb{@8d1uYAG8LVO*4@0MbN#Bs6f<77&EU$nbqQjtx?TR9l%vGO)Tto|$7E&ju(Ea1 z&E#8+anp*A!FqotF8-%o*rr~d&Rvs}<7Wc;$>$KC3OV5HD=lgkxEXwA1kO+8YQAz^ zbu=S3JL+875Zq{a-73=t+9r2wMAt0}ilCeT(h13=-v>#9?7}L4gts!{@4C;2l10nQ z8Da6@JImKrr_*LJhS>r_FSdtl*ZC*zq_AYKECUL<+oONjWr5xbAl`9?V(H>97d*M+ z5iRb@@^Xl9KI_zv+41%EJnAdt9pvM}y6**&jl|qG)K`1m$G)`QGA~JKl67-PGl9F} z`rOckCpXjMxCDexqy;ij-oOpo8FB8iXqYA`?sKbjetewTk6j&%L4vKE^rVA= z&Tdmxi}`9CY^6MS72%#Kix@x-3No2?QM^(}0UvAL^KA)7kr(3>bYc8C`Ij?v{ zLMEvDxX5DI3`rLNuNKZNTT!2CAJ%bza(D@JG-%kjdPVqXe^5Q?lCv7Jzfk*Bx_G&5 z58`4610kK4Q%#1$g6i+0o6J$Z^(WDrIHh9s{4-UULP%L4tnJ! zO%ynI&^6hCma0HwrKe98CisQiwC!OVT{Jopz1XOuw?D6UC`uoRVUluUjHZpkH5pvn z9I4yg?$@oXBm%8>Kd}_!4&6mPq&`J`ES!Ixv3y2KroKmN{s%YCh9&LFKqyOsDj%=3^|m1dr(7JIK0o+xZDc)BTFR<|++|S_f2|GG70XNs@p6 zU7$m6$GocSAE;a^)dP_oYjr4w0WLix{>}U=Z@A|d?HA;-B#JZcj*k&Obw?jojyKdT z3WJ`nCZJ=3ziPcuxSV*WzOFviIZiy|?lpmPAilc}H~+5;IF6!ighuRWlA%*c*g?;2 zsrR2-O|jdl23HGMBc5H(vdTUpm7RZOb*KgN&#fH!HjgBFrZRRU@6CLFs7JU|x1q-C zurEo*C%$gZ@l?Eu=3@=8{#wL$3|*}M)yVTK%oQ4Ztk=iSNrSsb86A1sg*vPDQ#-A#S@|QP zPb>;Et^5kzt|x1GN!DyN!@7UcxiH=wofBbo4vN766tj!4;|Jo}yC9cG|2q|}MOJs? zWto9D*bPEqPbBkTPrhN6YT5KJk%A-g9zIdsagNVVbxIh{_!xtNZu1fRVq6NOQ$KpVI&KhCgjcs|@)&|9gLG$*u6^@t1F}`Cbbs+FB(Lhz4RYaZ}s0N=l+Wed=cA50gvdDB^pBz5`7tiGnl^PE3mQ)OEnut$G$Gt~M0aWvts|L=lYqr+roTHkhWdRL^Fa)JDPsNDBLLI<+C zkt!G|Mx&zXAhB~`BV^YU0HcF0#HG?fG2F=C1;74uBlO>OGo_pYZ~M_f7YmK)AoTAx zW9(xvLOnu^4jRLjAXmDU|5gx86xBrs{gT%5r-(B|lNhqU`=@`7<8%-YJ6Q@jt~`$1 zoo6%(X8JBhQG+o^`(4j-893GWU+aa%z9XP%@rR}8phKc`5XA{j{!>R}HxsRI`Mddd z7@73D2`G|{4&t`KBB{KRx-=d&I_P&bF>i`G9aP+H!iXtjf4>Wi!J@zG2>uJDe*h)8 zTPflrtk$?6PpyBVXl7$~{#X*N2f}^c^p`(D9`S$mdB{>vs;Bbw3pF5eu>6~b-wYdg5gIJT)_a;G$LTK7})#i0raq9|pI zed-^;?PoPvh%v&NgC6xc2kdAH#fz|EG4-R(=8DFm(@1}s>r=`%u^|2q4-nU{JeoKk zNrw^nSiPi!mj4DG%hx|Se&c@v=I6nmJJLaMEYwu=*X*z81N`qspkLg7KepgMA=4iD zr+!du6dmN^|4Z?gI+e_55(SC=^NfQ39tTt*^6&bk19zaa^Rz@rG985Y-K2=G2>quS zHT`?~Y;Aum|9!`je<@6R{(@n7-33a&6PUwK7Ix=Xmc0{(aLtHdCc-vux}4z`?=xeU zNkydCL{YScQApE3qKaSYL~|1C(%FA_Ur#)S2sJ2 zEBF(-8l5bW211V|Di?t<4fO{<0%Z*dd@&GZ!1{ln6U?s_5gB3h`9b8%k#D-a%1^fB z1<&4^vK9C!CkZO~OOgDiS0w+3qXe%d`yd8kwYr^rb20s3VK@t+(Wr6eyIuum+PpH~ zbE>>8`jCDb#?pj5i%U~&ar&ooB~~}yLI0YR zzo>uv7fOGj^cPD143st%i2hi!oI4QB@rx!vovhMdaG;g#0{S88h=_yE8 z^|_bbq)i~4fE&f`JQa%zvEtY~n6c(cUmFZ!-K_r9{UG$1p7iemfPYQNU&Q@ShSD7x z1}#97^+wkC_xeFCi6(iWApuzO=d@#}J6V6G&d*Q0{h(NA1T&@_rh~GS31nI?UYQOu zmq!VL-z#tX>NB4D-$RG3ElWt(;HCEs6W>1yPLGUe#fiIIeIp(4`lj)vrLPfQn-A8s_rr0kp0B+@|jkl-&w&ctWUnX2%mexIhNP8v!+*E)4 z`xWDV_O;qBj~sv!b*a(2$nO_sTpUAttbQufj?zKzopxyn4?#ckXq;Pg&`}tnoYrYc z2bBuW%!LrkDMB-}%V?|>HToGHbV&(S^Yxz((PQLF8FJ4BEkp-x#W5aJZeUMtZof)x zLWlt~tI+M*Ra6|3ciZMVk+)GK3*&!KH{K!L9qF&RZf+-VKcPQYqK{pR=?8Bf9V9ns z^=lPOi$3w==KI8z>c%*u_%tnR*~g&64*$Fbx^`yb2IUnJ>rYMiwLyEL6iJ3Lh3(U> zLkW$vp2#2uOoIRISn+?-i-kV^FD1MTgYf3GBaH_Tuow5wrRu*Cp5-^;)f0b*siVxE z?|v@o7xAGz|AjKGk_IHwL5g%x&oYU-jSd>;b4m?Z7J#l50?%ljq3B;nzOt;t0A&>A zDYC1O%%)3wVgYlT=_@2y!G3Bmv=)@amU zD$GRk`&Z^-{=d_6tGYs6*z1N*yTsIYi%{DypH+9%#-AD7Dof1}O1pp0xgG_%Mh8Vd z{JqnI@_VPp4dm~Q9!_Dn!A~Cx6sFe4*Rg9pi;>4A(fFxqQ4;#pUn< z*TFwo+D6*d;YP_=-L6qlidDft{V~e%(#1_b?;4K$0573SF1#1m)EXr^`%53H!RN@X z$boYB9XhD`hAka5VSay%1-mFkpgb4o7a zNqzQEX34o#j-nk*X@YO$1+p9hgAk!ssI^`w<>OlfH2Y))b6lph95w{a`5(CQq+Qp! zQx&UtSW-iWNjf^rA5e;WRO*SY7JHF#H}hyM(6FC>4T4`Zp8F-tl&CNy^r z^#_8}68Q?*c?sIsS!LhjLXpSY?WCNG{GaIS_nVRpk)0RdYQ#`n1e^yb zT&cW)HSIA0fBk=TyYj;{Hd=zmOTn01Wt;7Zp|^D8v|<#To5oRJ8Wv0e5et)w=X4Xb z)rBm0iw&el#yWji=1Z~%u1po!j*rqbddBWgs+KclKjpaJa=%z zVNtEcuH{IU&WZ7M>SmTp4(PSS&)7%{aH7`nR-FR%G?M1awxV`1JL=5bWrSxQba8Iq zV6fR;n?pkgT%3QPOa7_ab_WFd``6zuzz-5A?ixv}^ z&lq0oLw14lwNanlLwox;T{nkIxq-r`T!z~5vh2arzGeo&olT?Bvgp4A^WX!*bE_$0k-4P8<12eTr^ zk=%9ZNiF2}lJ7<6Y z*e!V@YT5HYnD|^J{P#X3h|ppN=ct}pt`|dg*`w?NgQ=GiTc_Z*Uir_ z>PyA`EAg!*F$ZXDiZSz`A0#rMgY0@GIA`N=5Z92%F1B^El2uKGy{Dt*cKiRo-}!fn z@VajcW+J{3BG_;eu}TnmocHstHxqy0yKcvr{jOyB#qo3DGbx22362PStTN2wkERtx z4eAk%wWvI2B>#J6=>vL!l%X(~=&-#C^7!mlw z;Np60ER!#e%Jat}4V-I{l+tsQ1fO8c;{J8H=)Nskl**#+-?*Imx@L@cgD7II!$}tO zBEk-weUEtEEO)HXO}tZjC}o1ZTkw-<7h+oxvQ{C$MHg`gko?3|>4kpN{g_!t z-NCEXafBnEpU<2tH!R!jeue0;hwa&yyT;#Z3;uJ?vkMAEa9V@!Nk$zQUL;IYWCs(a zX-e0gFwJ#)tWf)S&M-N_F?7$(nu1dH%rhz@3z>Ycn1BoJ2~>%Wfq8%MaUvn)^x^bh z<;3|<_K_7^iKbRMI^|P&DFYpXZ{!O0U-5tAdIKW|8c|h2Y9p*_w9R+I)MiMtt6pQS zSogH4gs1!~Y*qrQD@j*VMF9ItLc-0zbFKus{w@S|XcIT)LP-EQ$u6yER)BUhUefEK zLc(l^ckKC=w1MN50-}E&TzhTYViUNlUzn$m-z!NyKDE%mk7|L5)9lGh7wOqdz(-BO z2E;j$MpJgQ6LJcJLnn;a`Zau`ge|x+>Xw?@*=!SrO}smmzb{e!uNUMN^*U0d>UT;A z4<;Z+JCY!b)v{LzJ~Igz<=ZD{6?%MFw>S^^l_JH9(pY2yl?{J6qBMb$)kbrUUv*$Y#r46O$n;g` z_I$OfWv-At>T$Z(ct}OwGL^stCj5yCg(OV7<(VSUVo6GM!jbwB>$`8-^lsM3xfb%o zc7FaS)=B#@_&a4Gc;rVYQq`A)X@-dEsrwTrjl*(s3-5no=~*1_Xaza*WeF|2iI*3} z5Zs3v53zT%NC+F}b9zbf2Js}=Twi1bAJ@Xnk>T6NHpVOHmq*q{FAdFkB2N& z=~dnR9HoCstR%vqibbIeMmL-Iw&&cPqjD z3ylL+phk%Ze?lKkIH^%us2aRVL_f>m`_!)qawNwU2b7GaP)YztK)Am{5_l@J1Uf@p z9(Ec+$&H$98yz&k(K($abPRg;FJBZdc!DpYbTREo+xvxoD(a;xGbZ9XuO$kwsfoE$ z>eo+MX8R>=#UlNvf~c-4Oqo&hB$mtJCleq&M?^S&@-e#Jz*VN5`tDuF0hrE9aV$sN zcXxNffkMiezeJ4x>Vp1f?s>sxK=+{r(fd(n5aqLcPmuh)v_K}nd!Ik|R`{Awl=hn* zJ^jWW*_V!gBdcBe{Nhf{Ej*+lSd4T-ccJMeOknwLJQFazZwJr;u#jp-I|ZI0pJrjj zW?zx>s2zOkYfPD!kIz1nU7hlAYWOp$nQ^0eFI8RcND^Fs1~rp-nEny%ig?1iRO}i@kXX6| z>EK)|QEY|nr>T-97~FD{47Jket7ZKG62TU#KhDaT`}w*?bGhkB#Sz=lk!Lgcvg+Ab z+GkIHRqr>~d%E%Eh|?ibmiLKlZJnLAm@DI-2hvLu`^{C})!h|)uJpzBg$LvY4M~5; z5M=a!LV2Ziz|umr$Ar0Jg7(&Q!j)9Q0L6)4rg;lv|i326)G@gW*NEcHt)1+&`LME`g0(6{{Qg<>xK+`34L{v@& zUBl=slgG}z4AHqFBKt#d=|CpmC=-By_!gFw>G5Ha=g8OEc_A$+I^toI`BMzDGO>d) zi8`m)_kEmyetI+%d_?I0Rf-sWPbhDHhD(0YvS*~Y*MW&Lb*KWdDK*loebRc zKPq~i1l)0_n1DBS;_6ipPu)wCCE=fBi$afqTle$4PMj5+uzPgM!gtuO6XIhtr+2T! zzk_oMcN@dT=!S6O+{`0zJPfGg&t4*SZ;#jh{{OpF7!5 z_WnwT4)!yFCKsH0_(xk;5-vuje+Di~HLF;Hm-}wcE?234*_F~k8XbUCLgH{9@z3z3 zai8Xxz%!ldiN5CaJNQce`+M8sm_JuTt7^)Ea;_(%ME5c+C ziZ?kgOpl+cHiNKlG|P0XpNRW^#P zM7loV+tYG~CK+N@kWB1xd$rJl;_yZeUO@&o7xly~-Qwhu>P!T-rPlo&_2$s(1_189?ff@{dc=Y>QmqJvx6W4G+-P79gPh`iKuV#XK;*7HgeP2o0+`axH zbnj1<7PK!U4pV_^_Z`PLRIBRW2Uv3v0Q;IESV!FMQ%pPtZO7N*u%tg%+bD^KLc)hr_RhUM0pi!y;G;(IDgn;i9SM*kO2WZRE4`oeiT_ zq_lhFx(&z0ktxrAKb-CncsTk^ktR52{)A|~Qb#pHa*-bCBD8xaC=*jsUnfm?yR-eU zZ?DyO(v4&8bXn+2!D##z$!K?&Xt^ZYEVayJfs+y(_7L0Z3m##q_$Pw703P%Ib#9Td1mtYA2F^5lz5%j)Eh;mk(zG3;`~m z-OtlY65uU4c9$CC3}uqzjAZk%SLpF5J;qlium{D4@Na=|qg|=12Nrcc1eZA8;vR{o zI{uK4?TIftb?8auS@(S*N+*TbsEQ;(N;qgsy@)uh_lQ(@of@3@G5kB1b$4Aws{c2K z3nofq^ODJbH@g#0VeepDVPmi+v=mK;>WZLtq+y%r=ZQukdXI9uj!t;TQmnSVR$LB0 zrF{YFp;4x!f57+}58zE%3)u&*QsU{gXphx#v_%zZHGWei-Kf>tSk6(_BN}F0N zQ{EwYlL>^Jet)N6Zx_VbdX|qSnAehK)P|iHrNn@LKjuKut7NAq#nEPZ(`>S12L65z zB5vGxaKgYxQvE~IHLELk+e4r&APYxx$k5+FPlMff;_!=!mrs!vi00Feq)^kU;4gdu_Kn6GT6XK)#3Ef*xruuW682qsam_`b=TVD zd2>~Np_B5V)j-BD2}To9Ym^p?C*Z;v+BWTJzOhJ+65Gk~pT}oUr-ZvD)3EA>bKg3n zj|RaGTdBnMVt%Iv4_gqR1%a=q5YWFB?D?eT{%NmX@&4M2%j%-z(o{IY;JNF22_`S*F?G-qwn(Mx+D_fPsq(_QCt({j0d`# z_}PxO5&+#q^ILQEyrdtEXr2lcUmhCt>(P2xuAO125$_w70~4QggvFd6ra0SVn8+o; zPyD`vNYB0)RHU2$-etAh*v=2BA5>gY8^=@Lf~Tn#2&cJL zCcw36Rmn6qq=R_q-x!W%RX?J#MbVo2h}hZhm! zwpuCtKPQq0^4IU&xf6+=ISEw1$i9$&NgsB&-*qOU89fdspqp0XQ71qwY02Va85t7& zHi>F#V4u$$w^l=`zF#KMc1lha2IjOTiFI9jevH7a_m->EN=}?M%lgJeDf0TsSmA#zbwKl9*9OrQ3(Zu)_Z^G(9C(xx8CfK0m^)TSU zgi&~fYuxO>#IEvGJL8K2Whjb&$pHn*)he?B?KI#Q^!M$@j8wbvSjD;fl>SPThzr>*69<{!gZ7LjC$i-uJMwqe6&L`PBbG=ATIuo;no%}UeL z)Jd>pQ`uLs>Q##Hb$$~eAIZzV^vbwp+|~dX8Z`qiSL}dG(~Lkj>P1H1NR`uJvQEp8 z{*HF$f&lu6Z-s%Jw}zbAx<({hf@EwM*Nx+BEcD5uT_LI$b&8ykPVjly(kjjIX*k<> z#bb2&ZFS{?AE?*m$&lgZdJGN_ezl(u?7KqV17; zwZPON=b(UB*Y@iK3+G`-l*E&Jq&f^1Dt z)&9KOZXrM4;Y_tK5{UCHGY2poJKsya-H3JG7LOF(9xbTqxqa>U%g>4u#vsfh^MB#FSxS>5!?Q93z}EzSh$OgYa5g?{bJ8Ba&&jUx z=Peoi=STN)C1iVay!=LpWGB4ceSG%;m_h)4LV}VofRbnO2{k+= zar-{VRvDh%%}8Lhp?UQrNRt#h@O~C(*?sWs@WyF>-piD02EE^A-ri0!(~RN#V$&e{ z;O=AVOyTW1k|`1U6#9EmaHKcUm))){Z&6@D5H6p+Pe8>>&1c0UMe{mvWYyj6%JG86 z{Tu-5d;i}#(S21u<)lkc?u~?YZVmND;GT(MrqIqj>JTE&fTR;bGns=67`hGU!E$GA zi9Q2=oJS}hD$4S$2;YQar1>5KwAcN(JA9KcO3LW{9wso>M|4UEB$$b;nerDQLY<5K z@;tR5#=euxV};g6e~b(IjpX`J$oRrHN$O$8W~W zDzb0rin-ljH{;$D=V2?0e1A}Edx`RpY`xNdM7@g8UTw#-`RYMPe8kGO%~q=`wq6zv z)1UjFCz_PLL%ez{VRz|*iIXV1RtuxGL69u^7)Qv5iy&24mQozns(P=Zc_4b_jxjCe zTgQ z9}9>HJsW>ovJB~sa=rNmnKhCGrx(`y22ukS@MXR&D-iRB{uRyA_lTq`HG*1g&($j? zWw~TByR~XVyHQ?-2;nuK&9;c;-#4yZs2izNetC(c*l|YT(W@ME%3j~EVs7qd@GA$B zmXD#2 zj#ghG@G3D`eT$rA!Aibb;Qkv7*HB!lS$71F!M7TYE|v?%g&3f>)_98nTA` zV1ev$Mq1F5RJAbyb6ftTN<4Rn*|P5 zek!s)(f!u`$qUQlh%*c9S^tDkW0(D@t~4{Sg>2N0F7I1mCoK7L(u_%ej<`vMxVEuL z(_Qs`4gB+|p?V9iY~2D9Aa@V3VbWNk3RW031*z0|a);(a5?OCz0suq80q$Vn#*vaN z+&k4P#wGV!Tro10zcRb^LNf8Lh68NblnCQTNHF$=kv*Cl)W})1bE8L*s!9zb^YLBY zuV2XxWG7s@InOn(aqwJ!cq2c7gPO_Sj$)@F!7dV(f~Du6SbE0*;L_la4yby4;`A}O zwW6YxiXOUYUmCHx6;SxDWjWCPkx(^TfOofH%-}1k8_gr7rAZzuy`({NQv2iCNMy9* zqYKYJRb2ioihMfdkaaYO{&Uqt7%=2&@r@gP>9JN%zr56{4I3{QI}!9=vfavwu(x{8 z8R!i@3w_DamBem5$pSv^_F`W2-rfbH3Ep5fswHZ|Ne*-&s~FPLaV%NmL^rp9LoA!9{-|Mi`D%PUEQ>LPlM>CGM`ZpXyL( z=*Mn2A8fvyYiv4Sc9cbcI?6CW-<*L<(#{Xu`*T4VdCqAVRl1$(JaU z=f=JM5?z`WMJVT(f(~Vi=b!ib)aS&nR8w6T{S$G2m4%Ep99PBk`Nj#Y7Q0;R>?ve! zLkiS5l6SbEgkaRo6LwZ&oEv~_`=I@$b5J>R~`+*bs}>Zp@1i|e&zzX1Dl zX8C@ZboHWcp<2;g=sR#RM(@nQ5$JB(g8@A?;-RhnmKQL^r)Kme|mV zikgYeC0P|I+#7VcXgu1->@=kr>?%UY5_97V5u?{?V-SUx(74B_e${|~!my!$!qc{@tKZ8WX&ajsT`>T-YIU|7 zoZq6yOI}k{nZSz()G%a|gxRt5WM5q3o4~Z|z^hZTsLIf!8+xZb-j?K7H=0rgm05vq z60T?RrR*;csU>>~nVv(lr7F_|s=#zY@39Um+-vK~!5H0;7tOL6%IT>=<`Q@IsK@z# zb?6Za z$=F}d?HwxOL`#OIGBes<1PZY&LMl5}<_Z}-8H%8B_0E}`)d)Q=FTY1c&@QK62MGt# zs~)>vo0_@F{&}~D*df@zhZgp2@Q${=}YxKhsBBH=?b0WojP(KDn~Hi z4I4N2f4sWvy)j4WWCBT-L6+Tg&MjpE*@;&;e?+vvCbK9r^xS$;?*ddWes4g3^s9h{ z&vsu53od>2!uwA3QXa`A{!|G5klG_C`@DDkNXY=+jKK;Vt8`+}o~nWEI4;Hs&{B;P zX+q+OQd{wfre}EeRNU^28#r-r3NT9j*_52{OpY5yGVM{ZhmH9X>n08POVE{WFCO=N z96t8aZ}a38Xvn_pQOYd|G@%fGU1668EPQzL;7hpf(l-v$M{2aFLpZ|?9L)UdeLKQMC=NBmI7k(XnbIQbuP$mIZ)Y~ zwi!G9!dgee0i%B5$LH!-$vQ`)_85JZh}qo2Mq$e1SAK7zZXvYiP|Xtc3BK`s}%OKelaXiR|q5$1`Tpo|iZRgyd?Wgz@!Cc4>H z{`I2nOr$ndLp6EGTHyTg^4Nm-0GRcHHdpWjMT z!!j`kXkw(Sriu7gzGrr6o->ev3qJP)W$KE~*^0=?yI8rryzj)ewCI?Flw~;@4VDy3 zffy#>poyoSn)u3pIqlb(WSH0zqa^Q1FxSW1m?HU+cU4x{hB{_`){<3%=~;3O>L zq#DGXXnNIT!z{vLiFYV3=xt%k(xey$7#rsR3DS?HXy=$iU$DuCGemtXXMJ1oJEgkHtj_E?hLnr%d6 z<%;f)>y}Wql!{YTmf!6yi?i~dS`&M83po=N&kH4W7j#Q&$A-^Beldpo^&wApu*>7r zAf!FLfN=nSH2@K-hvl{SW-V#A|F}5lTZ~L+H!ZD+x~|$lcr~9|Uo>Nq+;BFL?E<`L z10MntDP{tMTyg9Q$FZ<@z!BkdTXv{n__qJOLy>y8@@4-URqlJ2eXBqez{{!x1LnRb5GMz49f0j`}M*3d)U@DD!VzauTD$8NINI z0E}qE0n`u#sPHxyLPQWL$=-R63orc*-%M)8Ng=yrjd&CjBwV9@Uboe`4 zjfTPq*{~|#=4p5nLy4pvPwH!_N>E)}j1Css+W9tI#~D6`d($PrDbexj8*h`zQQhEm zsubdX(d<)P*{?9tDCH?Bz)A8`f01Yl{)UFp-D4GX-kvLow#dAM>p6~1FVB>R?8Saq z$mDB=jk8nOz*SZxo%La;;plid?KpT7T6&*on_X9te$1oxxe-tFZcEL6(R0W6Qll&T zjjqD2&`riLcUUxBY6Bh7pbIKXHDt}A!)s%Iwo1KTo{;l;kdz8J;_eptN_re>bVSUy z;Why>xQ{ilzV<$r_NvhfXj}zI`B4m|sgCUp7eieCY`Ne3BBJp#TYc6-$_Uimv`K;o(AdOi;&nz?C7H2=Dz_u@c* z$fT)mgyb=E__R3dYpawBdJ5_^?Gintftw~kf`y|i>b-~xB=XQQbkc)wye%%?&)bi> zm}?Ao9XLXZ8~>TXo$j2a!IxhjG*20Pw=8x$cg zgYow%y;e4_Kbzh^ww7Ao6c!|HEB4KeO{3EKaG=hDj}Y7TDJmDL19gg&w;VwhC2+1t zWJ+<+yb9qui7%~2FRR`;>T@_Q;E}DH{^HR~-Q2E1yJPq<8lZPJy6FXzP1riGWPwZEz+*G^5;CI}5=CjtrDyD$ETagk%+u{HpN ze?UYT2Nxh>jDz57qDPd@vDdTS%D3kOsAN$wldDw=Qz|8h?ijLvQ#_00a!C1p93h7Z zl=g+^fXYNzM4C|>n#-&FlBSt4+<+@g!Czqj>&Nr%%(6}PGwsACHziSs0izXDhHqmj zHPVpWw{o6($zc-Kihs86khWD^Vn^P1ilOKD;JrKaA>%@c+HAl0^k^wu&WkIY-$!YJ zE(FmwYzUMK49iA;+JD7;DB0{2Fh#p#ZdKK;`=kk7*{({tQXhJ}Ri_exTj3(9P~L;r zskf$3WAJiZ*lot)iKR58RbZ`8E4fXzp~rK@@tTR8Pq>Xm|LoE8B35R&M-=TSfW|{F zgmR*r3}Lt75z$46xn^LVBn2Dsw*)y09fCXx4lsR~H5LG&%m`0G|E$AH9jl45i4zDXn8P}=tik!Q9Ya>ngk3WBHHj-Vf4 zV>V}TRxFp4jPt>T&uD_5sH)%=v7@CIMzwCW8?LN)kkSxvC8$_x>coe2MZ}zgjQV!! ze)W-h?pAbZcp*89!a*`xUY(20kRW5iD?aJkd&#+f)x`B&FMrlqRY=otex*{=OZo22^WK!SlQ(LL+C*oW30P;*4}{sPa4NseN<}J1J}Re1D*B zt}#cJ+#9M+v)Y)c4SZaq9m4_DLu5EUYJ&^NB~mRK)?I7knwNbZo#jlmMuc7ysM^Kp z+_QCm*L$Ok#jGpfH5efuQ??p*7!-Sp)aq}LUr0M|;`?@q56$B(Rdf4Y!%`}w+hR-P zy5W@v5l0GkF*-42c`JSd_$2%)xJ}i!wa0hNs>=K{U9p;28=keFtbAHr&|Jn9@>K0* za&(Wx-6CrqwD?Tf02?>dLeD4cp2hdHv&V**9tgn=kJ<6@&4ww^rx;OXJFTV z^E3G(nSc*&ti^y{Rxd@Q@A}aSAw~Ucko@%Mwa*1xdd;>*M;U_0DL~S=N8%2?Ddw(! zM?Fmf)FI-YU^r-}NLS6;C@8`rLf|P zE1Vsey)aiIDUXr_VyNfQ%~$F9D7hn8ipLl-zSQSF8U)wzz0?{eV3iy}J;i8$dkAZs zW}n4{(az4Qfu#uN-);hf)7&IESn*1n$g zjYDaISLdX2h*)tj+Oc+|BDxpaI>`jwxyMW7YpRocMyMgPu4X^`ZyfGEelIJJ6i5@a zcO>(*e&U-&uh^6QEx|fi#6c66+&*^u@eY|2wX4&bby-ty(=?4+p6jH4IJ~FZybFQq zfjv}3zGzGZycr+g(FPlblZwS&Q+1Z1=*d76937pJVV$>AUE)u9Pmj=3qZ%PzUI5dH zctJdiO9{kb&vIK99*UWgOD zutDc16A%tKv&St1e*uwyU0s=YVZlvJFH?mtMgLhzPe&@p4Rn(gO012{eFc=yMLcrI zz_(xhxuuJWeQP(NSDarwl;ZyV!7Epb-Bw=W<%u!-$y@qIVK;vkrHGuEIiKI-D=|Hp z)gjczmZtG%xS9{j#7oc0O(kH==^BGX=yAbt?VreN@3k3zKQ3({TEP7DlxM%$Ofy3RBF~=q2 zf=^+)=|&8$w{!4_8TpY)-*MPncqs{MFq*UO(Ql=E%qO9<-6i3U>`AA{PhvxU-^J-( zEQ(9flbC=rj1Sd+n>JHN7HxG>#(@!JNbBM}erYG=+w*2y$E;Xs69-?nbIWUW+HLJ$ z)t#=A*%@5K*@+Yo%uI$L;JidrmoqUs!jktAaYjysoNkIGDKq zxXQ*XO9?@GA?~I%xK5VP2j@sV9f4?Glq~pAxGLVF>RESxQgc}M9xl1daeD2R#8gcW zm%|4;zGVDPi+>etnL8G*@RnRSH&Qr@6T`kp3EZ;UW3RH8T_o*fF?8tT2}xdJzy9vj zu0z=6WFa=PbvtZq18EBul1ygJdmty`QhzU$U9a7d@K2tBC43RM&w=s|M zB?FC;P-2*U{nl zvb$gq)f#C)&x?rqowkWu=v7J9?SB;?acvJ9^E z`vhEc3R=1WM!Ph`I0lXyTh`j+WjlPFFW>D>G}F*f_^uveaiG*E>_f{&28%f~dOqxi z5BnT{u2o8B&ZC719K_Sq!I!xOij*%Iu}bMO=MuIPiavZuu$~u?d!oEn_JKP-t=@}e zAqrR$^V&3Rbq=?}QHa2{-o22p=x=(%wS2H#Mzhtv^6NU!v$}i5B3wL^l*jOLp(~tx zq@E@msRSi~Fek1~!YW`y(Sh5VxQcC*HHM;p67b~n+7`5zs^c>?_T|LGQNMpCD|IuB zbQQL(j%WFn%@#8zFqmE(JPqH=iX$#Df%y)UA`=+p)nNjcA5^ey?fEz7iY-_UVqT0j zGY1zY@YTTSFFeKhfB&)Ke=@E0KT=Bk&Bzg9T9^Y%P=NuhXwzxm7MurTpe#J#(QCDT zs>6x+6@Fz^NLonQ9rn9oV#58SjqvggShdm09R`CP-edHV_TPQNzIWulX)vF0!w8mL zQyeKUWqe>)n~deMkO13B$$KgnYgV6O*34-vX}eK}rytm0izlIHF%eL0q~=%X5rm;i zswSGh@s4UDS>$w>{&H%9#q`^gusKyIWnK#N{RFL&nmDXL02jd1MU-5JJEo zKsu)>*WWB$B*Wp)ZrTS#mXsfVIsXm2uivmc_Gh>GL&|^dFw}qbj6WKRCSCEJJWr{n zXP|Tu(ZtIy3vKxJdOuK~UUYZV6-h4gl~p$mKX>BNy`p?R2PH+;jPhZBI`ohI=0Z`Y zSD?LpjM^Z)Ft@(&iQ=$^Sk);zsy}lqQKr zATMhbUsP6C9ZrAq;6qM-g{oZQ#YMz&{;nGbo~9gfQNS7hl~P>K!d=P`kQJ{uLq9Ts z94@C~swbjjcC`gFKH3K3M{!M`_nlSaHcX2yqAcD^96yI&Jnv1?zF_CDxvf5fd!=g1 z*r!4@`-vCc@QoEs%gvZk{2H(ndu+tw=7C6`J8$$JXHl~gPbwyVu!J{9m}Eo~ET0|i zFppyb?CxMWxq&i6+)O{`S@EK(($r1l0j@Uaa$miZpUnE#LmhV1ng2eQ@n0;kzb0`F z2)AKLS~9A&c7O4sw?8ogS#j&;8Kyz~0iP_d+d4bPv3GcOHXaDFJo}SNh;7S2n508H z!%h)p0`K17K&4@S-1h!>@FC~28WS)$g(g`PqSvH=;B^BeFB9O?r-w0tg*-LJJ}qCy z`7DO?V=Bv1^Tk4xTSCfrw00GZWaj%0gcp z;?+#x@K;W}rgbBpw81w9XGGm&0)#9pmMH7OhzpPpIJ(e(HCv_OR3Ri6X0CeWJdrO# zw>V4twHLy-^4YxQu5qs)y=kY|=7VAi`7VpKp-nJ<8Xg!y3H;-BY7Bli%+oxE35XRp z$TyXDRF`Uhr$lGpnk+8Py5i15H zSxUTaElres5fNJfIW&`BSzGBD*3JZ!R%*j#n(C{*-P?1jLx^p);-5PA<|In4qojbc z)RPllIyiB?OBs^U4nbLE(?0$IgPLrYQtfSWZV2gr&OhfofOuol3LKJ>Cu9D_G#}{8)BvD)s1Ns9-Za%;Bt1t$>>cv;6Mc$!7Na%JG?2?F8Y_KYB zY}LocJ}dh+Vfw7V2VR*_p`^Y`)`Qpn=^Ky!HO5?XWJ!e;D9;4;y{5b$)-%bS+_uwc zRkF4Z$#3S)Q#d`;_drAW#SWJN+D>bm#d!`dkl&x|LGg=df5?&Kgm}^@+7&2pA?I6v zVmGPy(~u|UnM;-!&sYHYd*RIjl%F3*Oj4c<`!Kx2;e3m?Thb%TX36ajLuW4Tks9tO zMuDTDC>B*YRU6ol>?G^v)C8*^7Y663-U@pY8OYuTgsnXu!MJ6@1MH*!0#I4UM&U#n zq-g>qM|>+RO1Ie)1t~>jrF?sra$E0zi>v{EW1?|uDf_;>ArcohkqP{dvR~77xv%n; z9=d&xE!3~J(jj&E>K!|gXmI33z|DnjV=bB~?K&NUyh<>iu(zeZsTh-9vWj+eRJ_dF z;vhd2bu?Sg?|Yk^?@BE$Z}o4-i#$IEo8V(`(Py!f9!=;NSWqpFeeh%>RIBw}7TR1AScXCgezt3Sk+K2d!B*v=3# zlTfzu(?o0G{|a+Ihv#5GX|nXU^``@8aZef!oAY};5LNgRWW^orBKb4=@Oi1vez58L zhgO>UIG;GR{C~c8<{nN;;8XNQ z$}P%6GG<(jVGg52F@X>NQnzseKMOlQTeD8@hRwp0U}pL6qPF-t|I=m%cM9$OoNvX8 zs? z|M-lWn~w)w`S!DajFbtP2{44JVMHU^3C;-gLN>?*SUuQdNZ8_qtflL)RM;?$kG35@ zg-l=qJdcU^9c3tt2{fntpeg}t{+ z6QC*IK{q#4zl�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 SOs8AmpS+C9qvD6gum1-)9r!T- literal 74137 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

      C+cO*h>h&R{e3J^ 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 From d9a5748948ae5ff7469541a069fa77b97109bd5c Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 3 Mar 2025 19:44:38 +0900 Subject: [PATCH 4104/4563] Update NNNN-SerialExecutor-isIsolated.md Co-authored-by: Mykola (Nickolas) Pokhylets --- proposals/NNNN-SerialExecutor-isIsolated.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-SerialExecutor-isIsolated.md b/proposals/NNNN-SerialExecutor-isIsolated.md index 7bd3e6e375..1d8d5bac1d 100644 --- a/proposals/NNNN-SerialExecutor-isIsolated.md +++ b/proposals/NNNN-SerialExecutor-isIsolated.md @@ -137,7 +137,7 @@ public func asUnownedSerialExecutor() -> UnownedSerialExecutor { } ``` -Which enables the following `isSameExclusiveExecutionContext` check, which can only be used when a "current" executor is present, and cannot be used when running code outside of a Swift concurren ytask (!): +Which enables the following `isSameExclusiveExecutionContext` check, which can only be used when a "current" executor is present, and cannot be used when running code outside of a Swift concurrency task (!): ```swift // Existing API From 6a4af1f9c74fe291afa0d7d92f0e84aaad49a7fe Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 3 Mar 2025 19:44:48 +0900 Subject: [PATCH 4105/4563] Update NNNN-SerialExecutor-isIsolated.md Co-authored-by: Volodymyr Myroniuk --- proposals/NNNN-SerialExecutor-isIsolated.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-SerialExecutor-isIsolated.md b/proposals/NNNN-SerialExecutor-isIsolated.md index 1d8d5bac1d..e60b53a5ae 100644 --- a/proposals/NNNN-SerialExecutor-isIsolated.md +++ b/proposals/NNNN-SerialExecutor-isIsolated.md @@ -146,7 +146,7 @@ public func isSameExclusiveExecutionContext(other: NaiveQueueExecutor) -> Bool { } ``` -In order to enable the the runtime to call into the `isIsolatingCurrentContext` the `UnownedSerialExecutor` **must** be constructed as follows: +In order to enable the runtime to call into the `isIsolatingCurrentContext` the `UnownedSerialExecutor` **must** be constructed as follows: ```swift public func asUnownedSerialExecutor() -> UnownedSerialExecutor { From 47118c37500420e8efb26db4bae6f1fafcefc750 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Mon, 3 Mar 2025 12:43:54 -0700 Subject: [PATCH 4106/4563] Newests updates to drop ABI annotations --- .../nnnn-utf8span-safe-utf8-processing.md | 59 +++---------------- 1 file changed, 9 insertions(+), 50 deletions(-) diff --git a/proposals/nnnn-utf8span-safe-utf8-processing.md b/proposals/nnnn-utf8span-safe-utf8-processing.md index 30a7816eac..26308fcadf 100644 --- a/proposals/nnnn-utf8span-safe-utf8-processing.md +++ b/proposals/nnnn-utf8span-safe-utf8-processing.md @@ -42,31 +42,11 @@ We propose a non-escapable `UTF8Span` which exposes `String` functionality for v `UTF8Span` is a borrowed view into contiguous memory containing validly-encoded UTF-8 code units. ```swift -@frozen -public struct UTF8Span: Copyable, ~Escapable { - @usableFromInline - internal var _unsafeBaseAddress: UnsafeRawPointer? - - /* - A bit-packed count and flags (such as isASCII) - - ╔═══════╦═════╦══════════╦═══════╗ - ║ b63 ║ b62 ║ b61:56 ║ b56:0 ║ - ╠═══════╬═════╬══════════╬═══════╣ - ║ ASCII ║ NFC ║ reserved ║ count ║ - ╚═══════╩═════╩══════════╩═══════╝ - - ASCII means the contents are all-ASCII (<0x7F). - NFC means contents are in normal form C for fast comparisons. - SSC means single-scalar Characters (i.e. grapheme clusters): every - `Character` holds only a single `Unicode.Scalar`. - */ - @usableFromInline - internal var _countAndFlags: UInt64 -} - +public struct UTF8Span: Copyable, ~Escapable, BitwiseCopyable {} ``` +`UTF8Span` is a trivial struct and is 2 words in size on 64-bit platforms. + ### UTF-8 validation We propose new API for identifying where and what kind of encoding errors are present in UTF-8 content. @@ -166,7 +146,6 @@ extension Unicode.UTF8 { ╚═════════════════╩══════╩═════╩═════╩═════╩═════╩═════╩═════╩══════╝ */ - @frozen public struct EncodingError: Error, Sendable, Hashable, Codable { /// The kind of encoding error public var kind: Unicode.UTF8.EncodingError.Kind @@ -185,7 +164,6 @@ extension Unicode.UTF8 { extension UTF8.EncodingError { /// The kind of encoding error encountered during validation - @frozen public struct Kind: Error, Sendable, Hashable, Codable, RawRepresentable { public var rawValue: UInt8 @@ -247,7 +225,6 @@ extension UTF8Span { public func makeUnicodeScalarIterator() -> UnicodeScalarIterator /// Iterate the `Unicode.Scalar`s contents of a `UTF8Span`. - @frozen public struct UnicodeScalarIterator: ~Escapable { public let codeUnits: UTF8Span @@ -292,14 +269,14 @@ extension UTF8Span { /// /// Returns the number of `Unicode.Scalar`s skipped over, which can be 0 /// if at the start of the UTF8Span. - public mutating func skipBack() -> Bool + public mutating func skipBack() -> Int /// Move `codeUnitOffset` to the start of the previous `n` scalars, /// without decoding them. /// /// Returns the number of `Unicode.Scalar`s skipped over, which can be /// fewer than `n` if at the start of the UTF8Span. - public mutating func skipBack(by n: Int) -> Bool + public mutating func skipBack(by n: Int) -> Int /// Reset to the nearest scalar-aligned code unit offset `<= i`. public mutating func reset(roundingBackwardsFrom i: Int) @@ -335,14 +312,13 @@ extension UTF8Span { ``` - ### Character processing We similarly propose a `UTF8Span.CharacterIterator` type that can do grapheme-breaking forwards and backwards. The `CharacterIterator` assumes that the start and end of the `UTF8Span` is the start and end of content. -Any scalar-aligned position is a valid place to start or reset the grapheme-breaking algorithm to, though you could get different `Character` output if if resetting to a position that isn't `Character`-aligned relative to the start of the `UTF8Span` (e.g. in the middle of a series of regional indicators). +Any scalar-aligned position is a valid place to start or reset the grapheme-breaking algorithm to, though you could get different `Character` output if resetting to a position that isn't `Character`-aligned relative to the start of the `UTF8Span` (e.g. in the middle of a series of regional indicators). ```swift @@ -357,8 +333,9 @@ extension UTF8Span { public struct CharacterIterator: ~Escapable { public let codeUnits: UTF8Span - /// The byte offset of the start of the next `Character`. This is - /// always scalar-aligned and `Character`-aligned. + /// The byte offset of the start of the next `Character`. This is always + /// scalar-aligned. It is always `Character`-aligned relative to the last + /// call to `reset` (or the start of the span if not called). public var currentCodeUnitOffset: Int { get private(set) } public init(_ span: UTF8Span) @@ -827,23 +804,5 @@ Finally, in the future there will likely be some kind of `Container` protocol fo Karoy Lorentey, Karl, Geordie_J, and fclout, contributed to this proposal with their clarifying questions and discussions. - From 72751e7161c0c48dd1028200e912a821ede40a68 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 3 Mar 2025 11:59:31 -0800 Subject: [PATCH 4107/4563] [SE-0447] Mark as implemented. (#2717) --- proposals/0447-span-access-shared-contiguous-storage.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0447-span-access-shared-contiguous-storage.md b/proposals/0447-span-access-shared-contiguous-storage.md index a196781226..95d5c8346b 100644 --- a/proposals/0447-span-access-shared-contiguous-storage.md +++ b/proposals/0447-span-access-shared-contiguous-storage.md @@ -3,11 +3,11 @@ * Proposal: [SE-0447](0447-span-access-shared-contiguous-storage.md) * Authors: [Guillaume Lessard](https://github.com/glessard), [Michael Ilseman](https://github.com/milseman), [Andrew Trick](https://github.com/atrick) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Bug: rdar://48132971, rdar://96837923 -* Implementation: https://github.com/swiftlang/swift/pull/76406 -* Review: ([pitch 1](https://forums.swift.org/t/69888))([pitch 2](https://forums.swift.org/t/72745))([review](https://forums.swift.org/t/se-0447-span-safe-access-to-contiguous-storage/74676))([acceptance](https://forums.swift.org/t/accepted-se-0447-span-safe-access-to-contiguous-storage/75508)) +* Implementation: [apple/swift#76406](https://github.com/swiftlang/swift/pull/76406) +* Review: ([Pitch 1](https://forums.swift.org/t/69888))([Pitch 2](https://forums.swift.org/t/72745))([Review](https://forums.swift.org/t/se-0447-span-safe-access-to-contiguous-storage/74676))([Acceptance](https://forums.swift.org/t/accepted-se-0447-span-safe-access-to-contiguous-storage/75508)) ## Introduction From 40c77c613013283a35b514a1e8833c73af46d490 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 3 Mar 2025 16:20:04 -0500 Subject: [PATCH 4108/4563] Proposal to add exit tests to Swift Testing This PR adds the draft proposal for Exit Tests in Swift Testing. --- proposals/testing/NNNN-exit-tests.md | 787 +++++++++++++++++++++++++++ 1 file changed, 787 insertions(+) create mode 100644 proposals/testing/NNNN-exit-tests.md diff --git a/proposals/testing/NNNN-exit-tests.md b/proposals/testing/NNNN-exit-tests.md new file mode 100644 index 0000000000..06f740d63e --- /dev/null +++ b/proposals/testing/NNNN-exit-tests.md @@ -0,0 +1,787 @@ +# Exit tests + +* Proposal: [SWT-NNNN](NNNN-exit-tests.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Status: **Awaiting review** +* Bug: [apple/swift-testing#157](https://github.com/apple/swift-testing/issues/157) +* Implementation: [apple/swift-testing#307](https://github.com/apple/swift-testing/pull/307) +* Review: ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) + +## Introduction + +One of the first enhancement requests we received for Swift Testing was the +ability to test for precondition failures and other critical failures that +terminate the current process when they occur. This feature is also frequently +requested for XCTest. With Swift Testing, we have the opportunity to build such +a feature in an ergonomic way. + +> [!NOTE] +> This feature has various names in the relevant literature, e.g. "exit tests", +> "death tests", "death assertions", "termination tests", etc. We consistently +> use the term "exit tests" to refer to them. + +## Motivation + +Imagine a function, implemented in a package, that includes a precondition: + +```swift +func eat(_ taco: consuming Taco) { + precondition(taco.isDelicious, "Tasty tacos only!") + ... +} +``` + +Today, a test author can write unit tests for this function, but there is no way +to make sure that the function rejects a taco whose `isDelicious` property is +`false` because a test that passes such a taco as input will crash (correctly!) +when it calls `precondition()`. + +An exit test allows testing this sort of functionality. The mechanism by which +an exit test is implemented varies between testing libraries and languages, but +a common implementation involves spawning a new process, performing the work +there, and checking that the spawned process ultimately terminates with a +particular (possibly platform-specific) exit status. + +Adding exit tests to Swift Testing would allow an entirely new class of tests +and would improve code coverage for existing test targets that adopt them. + +## Proposed solution + +This proposal introduces new overloads of the `#expect()` and `#require()` +macros that take, as an argument, a closure to be executed in a child process. +When called, these macros spawn a new process using the relevant +platform-specific interface (`posix_spawn()`, `CreateProcessW()`, etc.), call +the closure from within that process, and suspend the caller until that process +terminates. The exit status of the process is then compared against a known +value passed to the macro, allowing the test to pass or fail as appropriate. + +The function from earlier can then be tested using either of the new +overloads: + +```swift +await #expect(exitsWith: .failure) { + var taco = Taco() + taco.isDelicious = false + eat(taco) // should trigger a precondition failure and process termination +} +``` + +## Detailed design + +### New expectations + +We will introduce the following new overloads of `#expect()` and `#require()` to +the testing library: + +```swift +/// Check that an expression causes the process to terminate in a given fashion. +/// +/// - Parameters: +/// - expectedExitCondition: The expected exit condition. +/// - observedValues: An array of key paths representing results from within +/// the exit test that should be observed and returned by this macro. The +/// ``ExitTest/Result/statusAtExit`` property is always returned. +/// - comment: A comment describing the expectation. +/// - sourceLocation: The source location to which recorded expectations and +/// issues should be attributed. +/// - expression: The expression to be evaluated. +/// +/// - Returns: If the exit test passed, an instance of ``ExitTest/Result`` +/// describing the state of the exit test when it exited. If the exit test +/// fails, the result is `nil`. +/// +/// Use this overload of `#expect()` when an expression will cause the current +/// process to terminate and the nature of that termination will determine if +/// the test passes or fails. For example, to test that calling `fatalError()` +/// causes a process to terminate: +/// +/// await #expect(exitsWith: .failure) { +/// fatalError() +/// } +/// +/// - Note: A call to this expectation macro is called an "exit test." +/// +/// ## How exit tests are run +/// +/// When an exit test is performed at runtime, the testing library starts a new +/// process with the same executable as the current process. The current task is +/// then suspended (as with `await`) and waits for the child process to +/// terminate. `expression` is not called in the parent process. +/// +/// Meanwhile, in the child process, `expression` is called directly. To ensure +/// a clean environment for execution, it is not called within the context of +/// the original test. If `expression` does not terminate the child process, the +/// process is terminated automatically as if the main function of the child +/// process were allowed to return naturally. If an error is thrown from +/// `expression`, it is handed as if the error were thrown from `main()` and the +/// process is terminated. +/// +/// Once the child process terminates, the parent process resumes and compares +/// its exit status against `expectedExitCondition`. If they match, the exit +/// test has passed; otherwise, it has failed and an issue is recorded. +/// +/// ## Child process output +/// +/// By default, the child process is configured without a standard output or +/// standard error stream. If your test needs to review the content of either of +/// these streams, you can pass its key path in the `observedValues` argument: +/// +/// let result = await #expect( +/// exitsWith: .failure, +/// observing: [\.standardOutputContent] +/// ) { +/// print("Goodbye, world!") +/// fatalError() +/// } +/// if let result { +/// #expect(result.standardOutputContent.contains(UInt8(ascii: "G"))) +/// } +/// +/// - Note: The content of the standard output and standard error streams may +/// contain any arbitrary sequence of bytes, including sequences that are not +/// valid UTF-8 and cannot be decoded by [`String.init(cString:)`](https://developer.apple.com/documentation/swift/string/init(cstring:)-6kr8s). +/// These streams are globally accessible within the child process, and any +/// code running in an exit test may write to it including the operating +/// system and any third-party dependencies you have declared in your package. +/// +/// The actual exit condition of the child process is always reported by the +/// testing library even if you do not specify it in `observedValues`. +/// +/// ## Runtime constraints +/// +/// Exit tests cannot capture any state originating in the parent process or +/// from the enclosing lexical context. For example, the following exit test +/// will fail to compile because it captures an argument to the enclosing +/// parameterized test: +/// +/// @Test(arguments: 100 ..< 200) +/// func sellIceCreamCones(count: Int) async { +/// await #expect(exitsWith: .failure) { +/// precondition( +/// count < 10, // ERROR: A C function pointer cannot be formed from a +/// // closure that captures context +/// "Too many ice cream cones" +/// ) +/// } +/// } +/// +/// An exit test cannot run within another exit test. +#if SWT_NO_EXIT_TESTS +@available(*, unavailable, message: "Exit tests are not available on this platform.") +#endif +@discardableResult +@freestanding(expression) public macro expect( + exitsWith expectedExitCondition: ExitTest.Condition, + observing observedValues: [any PartialKeyPath & Sendable] = [], + _ comment: @autoclosure () -> Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, + performing expression: @convention(thin) () async throws -> Void +) -> ExitTest.Result? = #externalMacro(module: "TestingMacros", type: "ExitTestExpectMacro") + +/// Check that an expression causes the process to terminate in a given fashion +/// and throw an error if it did not. +/// +/// [...] +#if SWT_NO_EXIT_TESTS +@available(*, unavailable, message: "Exit tests are not available on this platform.") +#endif +@discardableResult +@freestanding(expression) public macro require( + exitsWith expectedExitCondition: ExitTest.Condition, + observing observedValues: [any PartialKeyPath & Sendable] = [], + _ comment: @autoclosure () -> Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, + performing expression: @convention(thin) () async throws -> Void +) -> ExitTest.Result = #externalMacro(module: "TestingMacros", type: "ExitTestRequireMacro") +``` + +> [!NOTE] +> These interfaces are currently implemented and available on **macOS**, +> **Linux**, **FreeBSD**, **OpenBSD**, and **Windows**. If a platform does not +> support exit tests (generally because it does not support spawning or awaiting +> child processes), then we define `SWT_NO_EXIT_TESTS` when we build it. +> +> `SWT_NO_EXIT_TESTS` is not defined during test target builds. + +### Representing an exit test in Swift + +A new type, `ExitTest`, represents an exit test: + +```swift +/// A type describing an exit test. +/// +/// Instances of this type describe exit tests you create using the +/// ``expect(exitsWith:observing:_:sourceLocation:performing:)`` or +/// ``require(exitsWith:observing:_:sourceLocation:performing:)`` macro. You +/// don't usually need to interact directly with an instance of this type. +#if SWT_NO_EXIT_TESTS +@available(*, unavailable, message: "Exit tests are not available on this platform.") +#endif +public struct ExitTest: Sendable, ~Copyable { + /// The exit test that is running in the current process, if any. + /// + /// If the current process was created to run an exit test, the value of this + /// property describes that exit test. If this process is the parent process + /// of an exit test, or if no exit test is currently running, the value of + /// this property is `nil`. + /// + /// The value of this property is constant across all tasks in the current + /// process. + public static var current: ExitTest? { get } +} +``` + +### Exit conditions + +These macros take an argument of the new type `ExitTest.Condition`. This type +describes how the child process is expected to have exited: + +- With a specific exit code (as passed to the C standard function `exit()` or a + platform-specific equivalent); +- With a specific signal (on platforms that support signal handling[^winsig]); +- With any successful status; or +- With any failure status. + +[^winsig]: Windows nominally supports signal handling as it is part of the C + standard, but not to the degree that signals are supported by POSIX-like or + UNIX-derived operating systems. Swift Testing makes a "best effort" to emulate + signal-handling support on Windows. See [this](https://forums.swift.org/t/swift-on-windows-question-about-signals-and-exceptions/76640/2) + Swift forum message for more information. + +The type is declared as: + +```swift +#if SWT_NO_EXIT_TESTS +@available(*, unavailable, message: "Exit tests are not available on this platform.") +#endif +extension ExitTest { + /// The possible conditions under which an exit test will complete. + /// + /// Values of this type are used to describe the conditions under which an + /// exit test is expected to pass or fail by passing them to + /// ``expect(exitsWith:observing:_:sourceLocation:performing:)`` or + /// ``require(exitsWith:observing:_:sourceLocation:performing:)``. + /// + /// ## Topics + /// + /// ### Successful exit conditions + /// + /// - ``success`` + /// + /// ### Failing exit conditions + /// + /// - ``failure`` + /// - ``exitCode(_:)`` + /// - ``signal(_:)`` + public struct Condition: Sendable { + /// A condition that matches when a process terminates successfully with exit + /// code `EXIT_SUCCESS`. + /// + /// The C programming language defines two [standard exit codes](https://en.cppreference.com/w/c/program/EXIT_status), + /// `EXIT_SUCCESS` and `EXIT_FAILURE` as well as `0` (as a synonym for + /// `EXIT_SUCCESS`.) + public static var success: Self { get } + + /// A condition that matches when a process terminates abnormally with any + /// exit code other than `EXIT_SUCCESS` or with any signal. + public static var failure: Self { get } + + public init(_ statusAtExit: StatusAtExit) + + /// Creates a condition that matches when a process terminates with a given + /// exit code. + /// + /// - Parameters: + /// - exitCode: The exit code yielded by the process. + /// + /// The C programming language defines two [standard exit codes](https://en.cppreference.com/w/c/program/EXIT_status), + /// `EXIT_SUCCESS` and `EXIT_FAILURE`. Platforms may additionally define their + /// own non-standard exit codes: + /// + /// | Platform | Header | + /// |-|-| + /// | macOS | [``](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/_Exit.3.html), [``](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sysexits.3.html) | + /// | Linux | [``](https://sourceware.org/glibc/manual/latest/html_node/Exit-Status.html), `` | + /// | FreeBSD | [``](https://man.freebsd.org/cgi/man.cgi?exit(3)), [``](https://man.freebsd.org/cgi/man.cgi?sysexits(3)) | + /// | OpenBSD | [``](https://man.openbsd.org/exit.3), [``](https://man.openbsd.org/sysexits.3) | + /// | Windows | [``](https://learn.microsoft.com/en-us/cpp/c-runtime-library/exit-success-exit-failure) | + /// + /// On macOS, FreeBSD, OpenBSD, and Windows, the full exit code reported by + /// the process is yielded to the parent process. Linux and other POSIX-like + /// systems may only reliably report the low unsigned 8 bits (0–255) of + /// the exit code. + public static func exitCode(_ exitCode: CInt) -> Self + + /// Creates a condition that matches when a process terminates with a given + /// signal. + /// + /// - Parameters: + /// - signal: The signal that terminated the process. + /// + /// The C programming language defines a number of [standard signals](https://en.cppreference.com/w/c/program/SIG_types). + /// Platforms may additionally define their own non-standard signal codes: + /// + /// | Platform | Header | + /// |-|-| + /// | macOS | [``](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/signal.3.html) | + /// | Linux | [``](https://sourceware.org/glibc/manual/latest/html_node/Standard-Signals.html) | + /// | FreeBSD | [``](https://man.freebsd.org/cgi/man.cgi?signal(3)) | + /// | OpenBSD | [``](https://man.openbsd.org/signal.3) | + /// | Windows | [``](https://learn.microsoft.com/en-us/cpp/c-runtime-library/signal-constants) | + public static func signal(_ signal: CInt) -> Self + } +} +``` + +### Exit status + +The set of possible status codes reported by the child process are represented +by the `StatusAtExit` enumeration: + +```swift +/// An enumeration describing possible status a process will yield on exit. +/// +/// You can convert an instance of this type to an instance of +/// ``ExitTest/Condition`` using ``ExitTest/Condition/init(_:)``. That value +/// can then be used to describe the condition under which an exit test is +/// expected to pass or fail by passing it to +/// ``expect(exitsWith:observing:_:sourceLocation:performing:)`` or +/// ``require(exitsWith:observing:_:sourceLocation:performing:)``. +#if SWT_NO_PROCESS_SPAWNING +@available(*, unavailable, message: "Exit tests are not available on this platform.") +#endif +public enum StatusAtExit: Sendable { + /// The process terminated with the given exit code. + /// + /// [...] + case exitCode(_ exitCode: CInt) + + /// The process terminated with the given signal. + /// + /// [...] + case signal(_ signal: CInt) +} +``` + +### Exit test results + +These macros return an instance of the new type `ExitTest.Result`. This type +describes the results of the process including its reported exit condition and +the contents of its standard output and standard error streams, if requested. + +```swift +#if SWT_NO_EXIT_TESTS +@available(*, unavailable, message: "Exit tests are not available on this platform.") +#endif +extension ExitTest { + /// A type representing the result of an exit test after it has exited and + /// returned control to the calling test function. + /// + /// Both ``expect(exitsWith:observing:_:sourceLocation:performing:)`` and + /// ``require(exitsWith:observing:_:sourceLocation:performing:)`` return + /// instances of this type. + public struct Result: Sendable { + /// The status of the process hosting the exit test at the time it exits. + /// + /// When the exit test passes, the value of this property is equal to the + /// exit status reported by the process that hosted the exit test. + public var statusAtExit: StatusAtExit { get set } + + /// All bytes written to the standard output stream of the exit test before + /// it exited. + /// + /// The value of this property may contain any arbitrary sequence of bytes, + /// including sequences that are not valid UTF-8 and cannot be decoded by + /// [`String.init(cString:)`](https://developer.apple.com/documentation/swift/string/init(cstring:)-6kr8s). + /// Consider using [`String.init(validatingCString:)`](https://developer.apple.com/documentation/swift/string/init(validatingcstring:)-992vo) + /// instead. + /// + /// When checking the value of this property, keep in mind that the standard + /// output stream is globally accessible, and any code running in an exit + /// test may write to it including including the operating system and any + /// third-party dependencies you have declared in your package. Rather than + /// comparing the value of this property with [`==`](https://developer.apple.com/documentation/swift/array/==(_:_:)), + /// use [`contains(_:)`](https://developer.apple.com/documentation/swift/collection/contains(_:)) + /// to check if expected output is present. + /// + /// To enable gathering output from the standard output stream during an + /// exit test, pass `\.standardOutputContent` in the `observedValues` + /// argument of ``expect(exitsWith:observing:_:sourceLocation:performing:)`` + /// or ``require(exitsWith:observing:_:sourceLocation:performing:)``. + /// + /// If you did not request standard output content when running an exit test, + /// the value of this property is the empty array. + public var standardOutputContent: [UInt8] { get set } + + /// All bytes written to the standard error stream of the exit test before + /// it exited. + /// + /// [...] + public var standardErrorContent: [UInt8] { get set } + } +} +``` + +### Usage + +These macros can be used within a test function: + +```swift +@Test func `We only eat delicious tacos`() async { + await #expect(exitsWith: .failure) { + var taco = Taco() + taco.isDelicious = false + eat(taco) + } +} +``` + +Given the definition of `eat(_:)` above, this test can be expected to hit a +precondition failure and crash the process; because `.failure` was the specified +exit condition, this is treated as a successful test. + +It is often interesting to examine what is written to the standard output and +standard error streams by code running in an exit test. Callers can request that +either or both stream be captured and included in the result of the call to +`#expect(exitsWith:)` or `#require(exitsWith:)`. Capturing these streams can be +a memory-intensive operation, so the caller must explicitly opt in: + +```swift +@Test func `We only eat delicious tacos`() async throws { + let result = try await #require(exitsWith: .failure, observing: [\.standardErrorContent])) { ... } + #expect(result.standardOutputContent.contains("ERROR: This taco tastes terrible!".utf8) +} +``` + +There are some constraints on valid exit tests: + +1. Because exit tests are run in child processes, they cannot capture any state + from the calling context (hence their body closures are `@convention(thin)` + or `@convention(c)`.) See the **Future directions** for further discussion. +1. Exit tests cannot recursively invoke other exit tests; this is a constraint + that could potentially be lifted in the future, but it would be technically + complex to do so. + +If a Swift Testing issue such as an expectation failure occurs while running an +exit test, it is reported to the parent process and to the user as if it +happened locally. If an error is thrown from an exit test and not caught, it +behaves the same way a Swift program would if an error were thrown from its +`main()` function (that is, the program terminates abnormally.) + +## Source compatibility + +This is a new interface that is unlikely to collide with any existing +client-provided interfaces. The typical Swift disambiguation tools can be used +if needed. + +## Integration with supporting tools + +SPI is provided to allow testing environments other than Swift Package Manager +to detect and run exit tests: + +```swift +@_spi(ForToolsIntegrationOnly) +extension ExitTest { + /// A type whose instances uniquely identify instances of ``ExitTest``. + public struct ID: Sendable, Equatable, Codable { /* ... */ } + + /// A value that uniquely identifies this instance. + public var id: ID { get set } + + /// Key paths representing results from within this exit test that should be + /// observed and returned to the caller. + /// + /// The testing library sets this property to match what was passed by the + /// developer to the `#expect(exitsWith:)` or `#require(exitsWith:)` macro. + /// If you are implementing an exit test handler, you can check the value of + /// this property to determine what information you need to preserve from your + /// child process. + /// + /// The value of this property always includes ``ExitTest/Result/statusAtExit`` + /// even if the test author does not specify it. + /// + /// Within a child process running an exit test, the value of this property is + /// otherwise unspecified. + public var observedValues: [any PartialKeyPath & Sendable] { get set } + + /// Call the exit test in the current process. + /// + /// This function invokes the closure originally passed to + /// `#expect(exitsWith:)` _in the current process_. That closure is expected + /// to terminate the process; if it does not, the testing library will + /// terminate the process as if its `main()` function returned naturally. + public consuming func callAsFunction() async -> Never + + /// Find the exit test function at the given source location. + /// + /// - Parameters: + /// - id: The unique identifier of the exit test to find. + /// + /// - Returns: The specified exit test function, or `nil` if no such exit test + /// could be found. + public static func find(identifiedBy id: ExitTest.ID) -> Self? + + /// A handler that is invoked when an exit test starts. + /// + /// - Parameters: + /// - exitTest: The exit test that is starting. + /// + /// - Returns: The result of the exit test including the condition under which + /// it exited. + /// + /// - Throws: Any error that prevents the normal invocation or execution of + /// the exit test. + /// + /// This handler is invoked when an exit test (i.e. a call to either + /// ``expect(exitsWith:observing:_:sourceLocation:performing:)`` or + /// ``require(exitsWith:observing:_:sourceLocation:performing:)``) is started. + /// The handler is responsible for initializing a new child environment + /// (typically a child process) and running the exit test identified by + /// `sourceLocation` there. + /// + /// In the child environment, you can find the exit test again by calling + /// ``ExitTest/find(at:)`` and can run it by calling + /// ``ExitTest/callAsFunction()``. + /// + /// The parent environment should suspend until the results of the exit test + /// are available or the child environment is otherwise terminated. The parent + /// environment is then responsible for interpreting those results and + /// recording any issues that occur. + public typealias Handler = @Sendable (_ exitTest: borrowing ExitTest) async throws -> ExitTest.Result +} + +@_spi(ForToolsIntegrationOnly) +extension Configuration { + /// A handler that is invoked when an exit test starts. + /// + /// For an explanation of how this property is used, see ``ExitTest/Handler``. + /// + /// When using the `swift test` command from Swift Package Manager, this + /// property is pre-configured. Otherwise, the default value of this property + /// records an issue indicating that it has not been configured. + public var exitTestHandler: ExitTest.Handler { get set } +} +``` + +Any tools that use `swift build --build-tests`, `swift test`, or equivalent to +compile executables for testing will inherit the functionality provided for +`swift test` and do not need to implement their own exit test handlers. Tools +that directly compile test targets or otherwise do not leverage Swift Package +Manager will need to provide an implementation. + +## Future directions + +### Support for iOS, WASI, etc. + +The need for exit tests on other platforms is just as strong as it is on the +supported platforms (macOS, Linux, FreeBSD/OpenBSD, and Windows). These +platforms do not support spawning new processes, so a different mechanism for +running exit tests would be needed. + +Android _does_ have `posix_spawn()` and related API and may be able to use the +same implementation as Linux. Android support is an ongoing area of research for +Swift Testing's core team. + +### Recursive exit tests + +The technical constraints preventing recursive exit test invocation can be +resolved if there is a need to do so. However, we don't anticipate that this +constraint will be a serious issue for developers. + +### Support for passing state + +Arbitrary state is necessarily not preserved between the parent and child +processes, but there is little to prevent us from adding a variadic `arguments:` +argument and passing values whose types conform to `Codable`. + +The blocker right now is that there is no type information during macro +expansion, meaning that the testing library can emit the glue code to _encode_ +arguments, but does not know what types to use when _decoding_ those arguments. +If generic types were made available during macro expansion via the macro +expansion context, then it would be possible to synthesize the correct logic. + +Alternatively, if the language gained something akin to C++'s `decltype()`, we +could leverage closures' capture list syntax. Subjectively, capture lists ought +to be somewhat intuitive for developers in this context: + +```swift +let (lettuce, cheese, crema) = taco.addToppings() +await #expect(exitsWith: .failure) { [taco, plant = lettuce, cheese, crema] in + try taco.removeToppings(plant, cheese, crema) +} +``` + +### More nuanced support for throwing errors from exit test bodies + +Currently, if an error is thrown from an exit test without being caught, the +test behaves the same way a program does when an error is thrown from an +explicit or implicit `main() throws` function: the process terminates abnormally +and control returns to the test function that is awaiting the exit test: + +```swift +await #expect(exitsWith: .failure) { + throw TacoError.noTacosFound +} +``` + +If the test function is expecting `.failure`, this means the test passes. +Although this behavior is consistent with modelling an exit test as an +independent program (i.e. the exit test acts like its own `main()` function), it +may be surprising to test authors who aren't thinking about error handling. In +the future, we may want to offer a compile-time diagnostic if an error is thrown +from an exit test body without being caught, or offer a distinct exit condition +(i.e. `.errorNotCaught(_ error: Error & Codable)`) for these uncaught errors. +For error types that conform to `Codable`, we could offer rethrowing behavior, +but this is not possible for error types that cannot be sent across process +boundaries. + +### Exit-testing customized processes + +The current model of exit tests is that they run in approximately the same +environment as the test process by spawning a copy of the executable under test. +There is a very real use case for allowing testing other processes and +inspecting their output. In the future, we could provide API to spawn a process +with particular arguments and environment variables, then inspect its exit +condition and standard output/error streams: + +```swift +let result = try await #require( + executableAt: "/usr/bin/swift", + passing: ["build", "--package-path", ...], + environment: [:], + exitsWith: .success +) +#expect(result.standardOutputContent.contains("Build went well!").utf8) +``` + +We could also investigate explicitly integrating with [`Foundation.Process`](https://developer.apple.com/documentation/foundation/process) +or the proposed [`Foundation.Subprocess`](https://github.com/swiftlang/swift-foundation/blob/main/Proposals/0007-swift-subprocess.md) +as an alternative: + +```swift +let process = Process() +process.executableURL = URL(filePath: "/usr/bin/swift", directoryHint: .notDirectory) +process.arguments = ["build", "--package-path", ...] +let result = try await #require(process, exitsWith: .success) +#expect(result.standardOutputContent.contains("Build went well!").utf8) +``` + +## Alternatives considered + +- Doing nothing. + +- Marking exit tests using a trait rather than a new `#expect()` overload: + + ```swift + @Test(.exits(with: .failure)) + func `We only eat delicious tacos`() { + var taco = Taco() + taco.isDelicious = false + eat(taco) + } + ``` + + This syntax would require separate test functions for each exit test, while + reusing the same function for relatively concise tests may be preferable. + + It would also potentially conflict with parameterized tests, as it is not + possible to pass arbitrary parameters to the child process. It would be + necessary to teach the testing library's macro target about the + `.exits(with:)` trait so that it could produce a diagnostic when used with a + parameterized test function. + +- Inferring exit tests from test functions that return `Never`: + + ```swift + @Test func `No seafood for me, thanks!`() -> Never { + var taco = Taco() + taco.toppings.append(.shrimp) + eat(taco) + fatalError("Should not have eaten that!") + } + ``` + + There's a certain synergy in inferring that a test function that returns + `Never` must necessarily be a crasher and should be handled out of process. + However, this forces the test author to add a call to `fatalError()` or + similar in the event that the code under test does _not_ terminate, and there + is no obvious way to express that a specific exit code, signal, or other + condition is expected (as opposed to just "it exited".) + + We might want to support that sort of inference in the future (i.e. "don't run + this test in-process because it will terminate the test run"), but without + also inferring success or failure from the process' exit status. + +- Naming the macro something else such as: + + - `#exits(with:_:)`; + - `#exits(because:_:)`; + - `#expect(exitsBecause:_:)`; + - `#expect(terminatesBecause:_:)`; etc. + + While "with" is normally avoided in symbol names in Swift, it sometimes really + is the best preposition for the job. "Because", "due to", and others don't + sound "right" when the entire expression is read out loud. For example, you + probably wouldn't say "exits due to success" in English. + +- Combining `StatusAtExit` and `ExitTest.Condition` into a single type: + + ```swift + enum ExitCondition { + case failure // any failure + case exitCode(CInt) + case signal(CInt) + } + ``` + + This simplified the set of types used for exit tests, but made comparing two + exit conditions complicated and necessitated a `==` operator that did not + satisfy the requirements of the `Equatable` protocol. + +- Naming `StatusAtExit` something else such as: + + - `ExitStatus`, which could be too easily confusable with exit _codes_ such as + `EXIT_SUCCESS`; + - `ProcessStatus`, but we don't say "process" in our API surface elsewhere; + - `Status`, which is too generic, + - `ExitReason`, but "status" is a more widely-used term of art for this + concept; or + - `terminationStatus` (which Foundation to represent approximately the same + concept), but we don't use "termination" in Swift Testing's API anywhere. + + I settled on `StatusAtExit` because it was distinct and makes it clear that it + represents the status of a process _at exit time_. `ExitStatus` could be + interpreted as the status of the exit itself, i.e.: + + ```swift + enum ExitStatus { + case running + case suspended + case exiting + case exited + } + ``` + +- Changing the implementation of `precondition()`, `fatalError()`, etc. in the + standard library so that they do not terminate the current process while + testing, thus removing the need to spawn a child process for an exit test. + + Most of the functions in this family return `Never`, and changing their return + types would be ABI-breaking (as well as a pessimization in production code.) + Even if we did modify these functions in the Swift standard library, other + ways to terminate the process exist and would not be covered: + + - Calling the C standard function `exit()`; + - Throwing an uncaught Objective-C or C++ exception; + - Sending a signal to the process; or + - Misusing memory (e.g. trying to dereference a null pointer.) + + Modifying the C or C++ standard library, or modifying the Objective-C runtime, + would be well beyond the scope of this proposal. + +## Acknowledgments + +Many thanks to the XCTest and Swift Testing team. Thanks to @compnerd for his +help with the Windows implementation. Thanks to my colleagues Coops, +Danny N., David R., Drew Y., and Robert K. at Apple for +their help with the nuances of crash reporting on macOS. From b2a77d6128d38765d4d0bc7a7e25f6678e0a6c41 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 3 Mar 2025 16:33:06 -0500 Subject: [PATCH 4109/4563] Proposal to add attachments to Swift Testing This PR adds the draft proposal for attachments in Swift Testing. --- proposals/testing/NNNN-attachments.md | 436 ++++++++++++++++++++++++++ 1 file changed, 436 insertions(+) create mode 100644 proposals/testing/NNNN-attachments.md diff --git a/proposals/testing/NNNN-attachments.md b/proposals/testing/NNNN-attachments.md new file mode 100644 index 0000000000..ce3d5efdeb --- /dev/null +++ b/proposals/testing/NNNN-attachments.md @@ -0,0 +1,436 @@ +# Attachments + +* Proposal: [SWT-NNNN](NNNN-attachments.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Status: **Awaiting review** +* Bug: [swiftlang/swift-testing#714](https://github.com/swiftlang/swift-testing/issues/714) +* Implementation: [swiftlang/swift-testing#796](https://github.com/swiftlang/swift-testing/pull/796) +* Review: ([pitch](https://forums.swift.org/...)) + +## Introduction + +Test authors frequently need to include out-of-band data with tests that can be +used to diagnose issues when a test fails. This proposal introduces a new API +called "attachments" (analogous to the same-named feature in XCTest) as well as +the infrastructure necessary to create new attachments and handle them in tools +like VS Code. + +## Motivation + +When a test fails, especially in a remote environment like CI, it can often be +difficult to determine what exactly has gone wrong. Data that was produced +during the test can be useful, but there is currently no mechanism in Swift +Testing to output arbitrary data other than via `stdout`/`stderr` or via an +artificially-generated issue. A dedicated interface for attaching arbitrary +information to a test would allow test authors to gather relevant information +from a test in a structured way. + +## Proposed solution + +We propose introducing a new type to Swift Testing, `Attachment`, that represents +some arbitrary "attachment" to associate with a test. Along with `Attachment`, +we will introduce a new protocol, `Attachable`, to which types can conform to +indicate they can be attached to a test. + +Default conformances to `Attachable` will be provided for standard library types +that can reasonably be attached. We will also introduce a **cross-import overlay** +with Foundation—that is, a tertiary module that is automatically imported when +a test target imports both Foundation _and_ Swift Testing—that includes +additional conformances for Foundation types such as `Data` and `URL` and +provides support for attaching values that also conform to `Encodable` or +`NSSecureCoding`. + +## Detailed design + +The `Attachment` type is defined as follows: + +```swift +/// A type describing values that can be attached to the output of a test run +/// and inspected later by the user. +/// +/// Attachments are included in test reports in Xcode or written to disk when +/// tests are run at the command line. To create an attachment, you need a value +/// of some type that conforms to ``Attachable``. Initialize an instance of +/// ``Attachment`` with that value and, optionally, a preferred filename to use +/// when writing to disk. +public struct Attachment: ~Copyable where AttachableValue: Attachable & ~Copyable { + /// A filename to use when writing this attachment to a test report or to a + /// file on disk. + /// + /// The value of this property is used as a hint to the testing library. The + /// testing library may substitute a different filename as needed. If the + /// value of this property has not been explicitly set, the testing library + /// will attempt to generate its own value. + public var preferredName: String { get } + + /// The value of this attachment. + public var attachableValue: AttachableValue { get } + + /// Initialize an instance of this type that encloses the given attachable + /// value. + /// + /// - Parameters: + /// - attachableValue: The value that will be attached to the output of the + /// test run. + /// - preferredName: The preferred name of the attachment when writing it to + /// a test report or to disk. If `nil`, the testing library attempts to + /// derive a reasonable filename for the attached value. + /// - sourceLocation: The source location of the call to this initializer. + /// This value is used when recording issues associated with the + /// attachment. + public init( + _ attachableValue: consuming AttachableValue, + named preferredName: String? = nil, + sourceLocation: SourceLocation = #_sourceLocation + ) + + /// Attach this instance to the current test. + /// + /// - Parameters: + /// - sourceLocation: The source location of the call to this function. + /// + /// When attaching a value of a type that does not conform to both + /// [`Sendable`](https://developer.apple.com/documentation/swift/sendable) and + /// [`Copyable`](https://developer.apple.com/documentation/swift/copyable), + /// the testing library encodes it as data immediately. If the value cannot be + /// encoded and an error is thrown, that error is recorded as an issue in the + /// current test and the attachment is not written to the test report or to + /// disk. + /// + /// An attachment can only be attached once. + public consuming func attach(sourceLocation: SourceLocation = #_sourceLocation) + + /// Call a function and pass a buffer representing the value of this + /// instance's ``attachableValue-2tnj5`` property to it. + /// + /// - Parameters: + /// - body: A function to call. A temporary buffer containing a data + /// representation of this instance is passed to it. + /// + /// - Returns: Whatever is returned by `body`. + /// + /// - Throws: Whatever is thrown by `body`, or any error that prevented the + /// creation of the buffer. + /// + /// The testing library uses this function when writing an attachment to a + /// test report or to a file on disk. This function calls the + /// ``Attachable/withUnsafeBytes(for:_:)`` function on this attachment's + /// ``attachableValue-2tnj5`` property. + @inlinable public borrowing func withUnsafeBytes( + _ body: (UnsafeRawBufferPointer) throws -> R + ) throws -> R +} + +extension Attachment: Copyable where AttachableValue: Copyable {} +extension Attachment: Sendable where AttachableValue: Sendable {} +``` + +With `Attachment` comes `Attachable`, a protocol to which "attachable values" +conform: + +```swift +/// A protocol describing a type that can be attached to a test report or +/// written to disk when a test is run. +/// +/// To attach an attachable value to a test report or test run output, use it to +/// initialize a new instance of ``Attachment``, then call +/// ``Attachment/attach(sourceLocation:)``. An attachment can only be attached +/// once. +/// +/// The testing library provides default conformances to this protocol for a +/// variety of standard library types. Most user-defined types do not need to +/// conform to this protocol. +/// +/// A type should conform to this protocol if it can be represented as a +/// sequence of bytes that would be diagnostically useful if a test fails. If a +/// type cannot conform directly to this protocol (such as a non-final class or +/// a type declared in a third-party module), you can create a container type +/// that conforms to ``AttachableContainer`` to act as a proxy. +public protocol Attachable: ~Copyable { + /// An estimate of the number of bytes of memory needed to store this value as + /// an attachment. + /// + /// The testing library uses this property to determine if an attachment + /// should be held in memory or should be immediately persisted to storage. + /// Larger attachments are more likely to be persisted, but the algorithm the + /// testing library uses is an implementation detail and is subject to change. + /// + /// The value of this property is approximately equal to the number of bytes + /// that will actually be needed, or `nil` if the value cannot be computed + /// efficiently. The default implementation of this property returns `nil`. + /// + /// - Complexity: O(1) unless `Self` conforms to `Collection`, in which case + /// up to O(_n_) where _n_ is the length of the collection. + var estimatedAttachmentByteCount: Int? { get } + + /// Call a function and pass a buffer representing this instance to it. + /// + /// - Parameters: + /// - attachment: The attachment that is requesting a buffer (that is, the + /// attachment containing this instance.) + /// - body: A function to call. A temporary buffer containing a data + /// representation of this instance is passed to it. + /// + /// - Returns: Whatever is returned by `body`. + /// + /// - Throws: Whatever is thrown by `body`, or any error that prevented the + /// creation of the buffer. + /// + /// The testing library uses this function when writing an attachment to a + /// test report or to a file on disk. The format of the buffer is + /// implementation-defined, but should be "idiomatic" for this type: for + /// example, if this type represents an image, it would be appropriate for + /// the buffer to contain an image in PNG format, JPEG format, etc., but it + /// would not be idiomatic for the buffer to contain a textual description of + /// the image. + borrowing func withUnsafeBytes(for attachment: borrowing Attachment, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R + + /// Generate a preferred name for the given attachment. + /// + /// - Parameters: + /// - attachment: The attachment that needs to be named. + /// - suggestedName: A suggested name to use as the basis of the preferred + /// name. This string was provided by the developer when they initialized + /// `attachment`. + /// + /// - Returns: The preferred name for `attachment`. + /// + /// The testing library uses this function to determine the best name to use + /// when adding `attachment` to a test report or persisting it to storage. The + /// default implementation of this function returns `suggestedName` without + /// any changes. + borrowing func preferredName(for attachment: borrowing Attachment, basedOn suggestedName: String) -> String +} +``` + +Default conformances to `Attachable` are provided for: + +- `Array`, `ContiguousArray`, and `ArraySlice` +- `String` and `Substring` +- `Data` (if Foundation is also imported) + +Default _implementations_ are provided for types when they conform to +`Attachable` and either `Encodable` or `NSSecureCoding` (or both.) To use these +conformances, Foundation must be imported because `JSONEncoder` and +`PropertyListEncoder` are members of Foundation, not the Swift standard library. + +Some types cannot conform directly to `Attachable` because they require +additional information to encode correctly, or because they are not directly +`Sendable` or `Copyable`. A second protocol, `AttachableContainer`, is provided +that refines `Attachable`: + +```swift +/// A protocol describing a type that can be attached to a test report or +/// written to disk when a test is run and which contains another value that it +/// stands in for. +/// +/// To attach an attachable value to a test report or test run output, use it to +/// initialize a new instance of ``Attachment``, then call +/// ``Attachment/attach(sourceLocation:)``. An attachment can only be attached +/// once. +/// +/// A type can conform to this protocol if it represents another type that +/// cannot directly conform to ``Attachable``, such as a non-final class or a +/// type declared in a third-party module. +public protocol AttachableContainer: Attachable, ~Copyable { + /// The type of the attachable value represented by this type. + associatedtype AttachableValue + + /// The attachable value represented by this instance. + var attachableValue: AttachableValue { get } +} + +extension Attachment where AttachableValue: AttachableContainer & ~Copyable { + /// The value of this attachment. + /// + /// When the attachable value's type conforms to ``AttachableContainer``, the + /// value of this property equals the container's underlying attachable value. + /// To access the attachable value as an instance of `T` (where `T` conforms + /// to ``AttachableContainer``), specify the type explicitly: + /// + /// ```swift + /// let attachableValue = attachment.attachableValue as T + /// ``` + public var attachableValue: AttachableValue.AttachableValue { get } +} +``` + +The cross-import overlay with Foundation also provides the following convenience +interface for attaching the contents of a file or directory on disk: + +```swift +extension Attachment where AttachableValue == _AttachableURLContainer { + /// Initialize an instance of this type with the contents of the given URL. + /// + /// - Parameters: + /// - url: The URL containing the attachment's data. + /// - preferredName: The preferred name of the attachment when writing it to + /// a test report or to disk. If `nil`, the name of the attachment is + /// derived from the last path component of `url`. + /// - sourceLocation: The source location of the call to this initializer. + /// This value is used when recording issues associated with the + /// attachment. + /// + /// - Throws: Any error that occurs attempting to read from `url`. + public init( + contentsOf url: URL, + named preferredName: String? = nil, + sourceLocation: SourceLocation = #_sourceLocation + ) async throws +} +``` + +`_AttachableURLContainer` is a type that conforms to `AttachableContainer` and +encloses the URL and corresponding mapped data. As an implementation detail, it +is omitted from this proposal for brevity. + +## Source compatibility + +This proposal is additive and has no impact on existing code. + +## Integration with supporting tools + +We will add a new command-line argument to the `swift test` command in Swift +Package Manager: + +```sh +--attachments-path Path where attachments should be saved. +``` + +If specified, an attachment will be written to that path when its `attach()` +method is called. If not specified, attachments are not saved to disk. Tools +that indirectly use Swift Testing through `swift test` can specify a path (e.g. +to a directory created inside the system's temporary directory), then move or +delete the created files as needed. + +The JSON event stream ABI will be amended correspondingly: + +```diff +--- a/Documentation/ABI/JSON.md ++++ b/Documentation/ABI/JSON.md + ::= { + "kind": , + "instant": , ; when the event occurred + ["issue": ,] ; the recorded issue (if "kind" is "issueRecorded") ++ ["attachment": ,] ; the attachment (if kind is "valueAttached") + "messages": , + ["testID": ,] + } + + ::= "runStarted" | "testStarted" | "testCaseStarted" | + "issueRecorded" | "testCaseEnded" | "testEnded" | "testSkipped" | +- "runEnded" ; additional event kinds may be added in the future ++ "runEnded" | "valueAttached"; additional event kinds may be added in the future + ++ ::= { ++ "path": , ; the absolute path to the attachment on disk ++} +``` + +As these changes are additive only, the JSON schema version does not need to be +incremented to support them. We are separately planning to increment the JSON +schema version to support other features; these changes will apply to the newer +version too. + +## Future directions + +- Attachment lifetime management: XCTest's attachments allow for specifying a + "lifetime", with two lifetimes currently available: + + ```objc + typedef NS_ENUM(NSInteger, XCTAttachmentLifetime) { + XCTAttachmentLifetimeKeepAlways = 0, + XCTAttachmentLifetimeDeleteOnSuccess = 1 + }; + ``` + + If a test passes, it is probably not necessary to keep its attachments saved + to disk. The exact "shape" this feature should take in Swift Testing is not + yet clear. + +- Image attachments: it is often useful to be able to attach images to tests, + however there is no cross-platform solution for this functionality. An + experimental implementation that allows attaching an instance of `CGImage` (on + Apple platforms) is available in Swift Testing's repository and shows what it + might look like for us to provide this functionality. + +- Additional conformances for types in other modules: in order to keep Swift + Testing's dependency graph as small as possible, we cannot link it to + arbitrary packages such as (for example) swift-collections even if it would be + useful to do so. That means we can't directly provide conformances to + `Attachable` for types in those modules. Adding additional cross-import + overlays would allow us to provide those conformances when both Swift Testing + and those packages are imported at the same time. + + This functionality may require changes in Swift Package Manager that are + beyond the scope of this proposal. + +- Adopting `RawSpan` instead of `UnsafeRawBufferPointer`: `RawSpan` represents a + safer alternative to `UnsafeRawBufferPointer`, but it is not yet available + everywhere we'd need it in the standard library, and our minimum deployment + targets on Apple's platforms do not allow us to require the use of `RawSpan` + (as no shipping version of Apple's platforms includes it.) + +- Adding an associated `Metadata` type to `Attachable` allowing for inclusion of + arbitrary out-of-band data to attachments: we see several uses for such a + feature: + + - Fine-grained control of the serialization format used for `Encodable` types; + - Metrics (scaling factor, rotation, etc.) for images; and + - Compression algorithms to use for attached files and directories. + + The exact shape of this interface needs further consideration, but it could be + added in the future without disrupting the interface we are proposing here. + [swiftlang/swift-testing#824](https://github.com/swiftlang/swift-testing/pull/824) + includes an experimental implementation of this feature. + +## Alternatives considered + +- Doing nothing: there's sufficient demand for this feature that we know we want + to address it. + +- Reusing the existing `XCTAttachment` API from XCTest: while this would + _probably_ have saved me a lot of typing, `XCTAttachment` is an Objective-C + class and is only available on Apple's platforms. The open-source + swift-corelibs-xctest package does not include it or an equivalent interface. + As well, this would create a dependency on XCTest in Swift Testing that does + not currently exist. + +- Implementing `Attachment` as a non-generic type and eagerly serializing + non-sendable or move-only attachable values: an earlier implementation did + exactly this, but it forced us to include an existential box in `Attachment` + to store the attachable value, and that would preclude ever supporting + attachments in Embedded Swift. + +- Having `Attachment` take a byte buffer rather than an attachable value, or + having it take a closure that returns a byte buffer: this would just raise the + problem of attaching arbitrary values up to the test author's layer, and that + would no doubt produce a lot of duplicate implementations of "turn this value + into a byte buffer" while also worsening the interface's ergonomics. + +- Adding a `var contentType: UTType { get set }` property to `Attachment` or to + `Attachable`: `XCTAttachment` lets you specify a Uniform Type Identifier that + tells Xcode the type of data. Uniform Type Identifiers are proprietary and not + available on Linux or Windows, and adding that property would force us to also + add a public dependency on the `UniformTypeIdentifiers` framework and, + indirectly, on Foundation, which would prevent Foundation from authoring tests + using Swift Testing in the future due to the resulting circular dependency. + + We considered using a MIME type instead, but there is no portable mechanism + for turning a MIME type into a path extension, which is ultimately what we + need when writing an attachment to persistent storage. + + Instead, `Attachable` includes the function `preferredName(for:basedOn:)` that + allows an implementation (such as that of `Encodable & Attachable`) to add a + path extension to the filename specified by the test author if needed. + +## Acknowledgments + +Thanks to Stuart Montgomery and Brian Croom for goading me into finally writing +this proposal! + +Thanks to Wil Addario-Turner for his feedback, in particular around `UTType` and +MIME type support. + +Thanks to Honza Dvorsky for his earlier work on attachments in XCTest and his +ideas on how to improve Swift Testing's implementation. From 54569d464d785c596b3f5b92739fa61a50cd8155 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 3 Mar 2025 16:56:57 -0500 Subject: [PATCH 4110/4563] Update prefix --- proposals/testing/NNNN-attachments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/NNNN-attachments.md b/proposals/testing/NNNN-attachments.md index ce3d5efdeb..ae4b08fedd 100644 --- a/proposals/testing/NNNN-attachments.md +++ b/proposals/testing/NNNN-attachments.md @@ -1,6 +1,6 @@ # Attachments -* Proposal: [SWT-NNNN](NNNN-attachments.md) +* Proposal: [ST-NNNN](NNNN-attachments.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Status: **Awaiting review** * Bug: [swiftlang/swift-testing#714](https://github.com/swiftlang/swift-testing/issues/714) From 7e9a4fd8986a877e694b680aad023649b04ec881 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 3 Mar 2025 16:57:41 -0500 Subject: [PATCH 4111/4563] Update prefix --- proposals/testing/NNNN-exit-tests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/NNNN-exit-tests.md b/proposals/testing/NNNN-exit-tests.md index 06f740d63e..b273fab3f3 100644 --- a/proposals/testing/NNNN-exit-tests.md +++ b/proposals/testing/NNNN-exit-tests.md @@ -1,6 +1,6 @@ # Exit tests -* Proposal: [SWT-NNNN](NNNN-exit-tests.md) +* Proposal: [ST-NNNN](NNNN-exit-tests.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Status: **Awaiting review** * Bug: [apple/swift-testing#157](https://github.com/apple/swift-testing/issues/157) From f08054cb6fa2bd5a4ac21942fe1a514df827780d Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 3 Mar 2025 17:09:47 -0500 Subject: [PATCH 4112/4563] Update implementation link to point to the PR that promotes exit tests to API --- proposals/testing/NNNN-exit-tests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/NNNN-exit-tests.md b/proposals/testing/NNNN-exit-tests.md index b273fab3f3..76a4edff3a 100644 --- a/proposals/testing/NNNN-exit-tests.md +++ b/proposals/testing/NNNN-exit-tests.md @@ -4,7 +4,7 @@ * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Status: **Awaiting review** * Bug: [apple/swift-testing#157](https://github.com/apple/swift-testing/issues/157) -* Implementation: [apple/swift-testing#307](https://github.com/apple/swift-testing/pull/307) +* Implementation: [apple/swift-testing#324](https://github.com/swiftlang/swift-testing/pull/324) * Review: ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) ## Introduction From a9c639f1262e63cffd9378d8157b4698c74928ae Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 3 Mar 2025 17:10:23 -0500 Subject: [PATCH 4113/4563] Update implementation link to point to the PR that promotes attachments to API --- proposals/testing/NNNN-attachments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/NNNN-attachments.md b/proposals/testing/NNNN-attachments.md index ae4b08fedd..26aecfcca5 100644 --- a/proposals/testing/NNNN-attachments.md +++ b/proposals/testing/NNNN-attachments.md @@ -4,7 +4,7 @@ * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Status: **Awaiting review** * Bug: [swiftlang/swift-testing#714](https://github.com/swiftlang/swift-testing/issues/714) -* Implementation: [swiftlang/swift-testing#796](https://github.com/swiftlang/swift-testing/pull/796) +* Implementation: [swiftlang/swift-testing#973](https://github.com/swiftlang/swift-testing/pull/973) * Review: ([pitch](https://forums.swift.org/...)) ## Introduction From 63a0034f61eb6b027215198d304905b7ddcbc6b9 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 3 Mar 2025 17:11:31 -0500 Subject: [PATCH 4114/4563] Add pitch URL --- proposals/testing/NNNN-attachments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/NNNN-attachments.md b/proposals/testing/NNNN-attachments.md index 26aecfcca5..02c02f5610 100644 --- a/proposals/testing/NNNN-attachments.md +++ b/proposals/testing/NNNN-attachments.md @@ -5,7 +5,7 @@ * Status: **Awaiting review** * Bug: [swiftlang/swift-testing#714](https://github.com/swiftlang/swift-testing/issues/714) * Implementation: [swiftlang/swift-testing#973](https://github.com/swiftlang/swift-testing/pull/973) -* Review: ([pitch](https://forums.swift.org/...)) +* Review: ([pitch](https://forums.swift.org/t/pitch-attachments/78072)) ## Introduction From b4daebfb4bab5a6b6f9620a0f093773cd060526b Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 4 Mar 2025 15:05:58 +0900 Subject: [PATCH 4115/4563] link to partial implementation --- proposals/NNNN-task-start-synchronously-on-caller-context.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-task-start-synchronously-on-caller-context.md b/proposals/NNNN-task-start-synchronously-on-caller-context.md index d467268e14..068be78c57 100644 --- a/proposals/NNNN-task-start-synchronously-on-caller-context.md +++ b/proposals/NNNN-task-start-synchronously-on-caller-context.md @@ -3,8 +3,8 @@ * Proposal: [SE-NNNN](NNNN-filename.md) * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: TBD -* Status: **Awaiting implementation** -* Implementation: TODO +* Status: **Partially implemented on `main`** +* Implementation: [TODO](https://github.com/swiftlang/swift/pull/79608) * Review: ... ## Introduction From 59b9d9c51cc87eec61d515f6a7e3438a8e5ce8d9 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 4 Mar 2025 15:06:49 +0900 Subject: [PATCH 4116/4563] edit filename in text --- proposals/NNNN-task-start-synchronously-on-caller-context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-task-start-synchronously-on-caller-context.md b/proposals/NNNN-task-start-synchronously-on-caller-context.md index 068be78c57..ccb621e83f 100644 --- a/proposals/NNNN-task-start-synchronously-on-caller-context.md +++ b/proposals/NNNN-task-start-synchronously-on-caller-context.md @@ -1,6 +1,6 @@ # Starting tasks synchronously from caller context -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-NNNN](NNNN-task-start-synchronously-on-caller-context.md) * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: TBD * Status: **Partially implemented on `main`** From 041c19eabe56028eba40e9a719e8fe37b161b8f6 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 4 Mar 2025 10:10:31 -0500 Subject: [PATCH 4117/4563] fix typo --- proposals/testing/NNNN-exit-tests.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/proposals/testing/NNNN-exit-tests.md b/proposals/testing/NNNN-exit-tests.md index 76a4edff3a..c822fd90fb 100644 --- a/proposals/testing/NNNN-exit-tests.md +++ b/proposals/testing/NNNN-exit-tests.md @@ -448,8 +448,12 @@ a memory-intensive operation, so the caller must explicitly opt in: ```swift @Test func `We only eat delicious tacos`() async throws { - let result = try await #require(exitsWith: .failure, observing: [\.standardErrorContent])) { ... } - #expect(result.standardOutputContent.contains("ERROR: This taco tastes terrible!".utf8) + let result = try await #require( + exitsWith: .failure, + observing: [\.standardErrorContent]) + ) { ... } + let stdout = result.standardOutputContent + #expect(stdout.contains("ERROR: This taco tastes terrible!".utf8)) } ``` From 8c709f14c7a103c0419676440bcb697e9f0fc445 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Tue, 4 Mar 2025 09:14:14 -0600 Subject: [PATCH 4118/4563] Import existing, already-implemented Swift Testing proposals (#2721) * Import existing, already-implemented Swift Testing proposals * Update relative link --- .../0000-swift-testing-template.md | 6 +- proposals/testing/0001-refactor-bug-inits.md | 173 ++++++ proposals/testing/0002-json-abi.md | 428 +++++++++++++++ .../testing/0003-make-serialized-trait-api.md | 156 ++++++ ...ranularity-of-test-time-limit-durations.md | 207 +++++++ .../testing/0005-ranged-confirmations.md | 191 +++++++ .../0006-return-errors-from-expect-throws.md | 272 +++++++++ proposals/testing/0007-test-scoping-traits.md | 515 ++++++++++++++++++ 8 files changed, 1945 insertions(+), 3 deletions(-) create mode 100644 proposals/testing/0001-refactor-bug-inits.md create mode 100644 proposals/testing/0002-json-abi.md create mode 100644 proposals/testing/0003-make-serialized-trait-api.md create mode 100644 proposals/testing/0004-constrain-the-granularity-of-test-time-limit-durations.md create mode 100644 proposals/testing/0005-ranged-confirmations.md create mode 100644 proposals/testing/0006-return-errors-from-expect-throws.md create mode 100644 proposals/testing/0007-test-scoping-traits.md diff --git a/proposal-templates/0000-swift-testing-template.md b/proposal-templates/0000-swift-testing-template.md index 2ea8c57572..9e0377961e 100644 --- a/proposal-templates/0000-swift-testing-template.md +++ b/proposal-templates/0000-swift-testing-template.md @@ -1,11 +1,11 @@ # Swift Testing Feature name -* Proposal: [SWT-NNNN](NNNN-filename.md) +* Proposal: [ST-NNNN](NNNN-filename.md) * Authors: [Author 1](https://github.com/author1), [Author 2](https://github.com/author2) * Status: **Awaiting implementation** or **Awaiting review** * Bug: _if applicable_ [swiftlang/swift-testing#NNNNN](https://github.com/swiftlang/swift-testing/issues/NNNNN) * Implementation: [swiftlang/swift-testing#NNNNN](https://github.com/swiftlang/swift-testing/pull/NNNNN) -* Previous Proposal: _if applicable_ [SWT-XXXX](XXXX-filename.md) +* Previous Proposal: _if applicable_ [ST-XXXX](XXXX-filename.md) * Previous Revision: _if applicable_ [1](https://github.com/swiftlang/swift-evolution/blob/...commit-ID.../proposals/testing/NNNN-filename.md) * Review: ([pitch](https://forums.swift.org/...)) @@ -44,7 +44,7 @@ between this proposal and another proposal. For example, this proposal might have been removed from a previous proposal so that it can be reviewed separately, or this proposal might supersede a previous proposal in some way that was felt to exceed the scope of a "revision". Include text briefly explaining the -relationship, such as "Supersedes SWT-1234" or "Extracted from SWT-01234". If +relationship, such as "Supersedes ST-1234" or "Extracted from ST-01234". If possible, link to a post explaining the relationship, such as a review decision that asked for part of the proposal to be split off. Otherwise, you can just link to the previous proposal. diff --git a/proposals/testing/0001-refactor-bug-inits.md b/proposals/testing/0001-refactor-bug-inits.md new file mode 100644 index 0000000000..385047be48 --- /dev/null +++ b/proposals/testing/0001-refactor-bug-inits.md @@ -0,0 +1,173 @@ +# Dedicated `.bug()` functions for URLs and IDs + +* Proposal: [ST-0001](0001-refactor-bug-inits.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Status: **Implemented (Swift 6.0)** +* Implementation: [swiftlang/swift-testing#401](https://github.com/swiftlang/swift-testing/pull/401) +* Review: ([pitch](https://forums.swift.org/t/pitch-dedicated-bug-functions-for-urls-and-ids/71842)), ([acceptance](https://forums.swift.org/t/swt-0001-dedicated-bug-functions-for-urls-and-ids/71842/2)) + +> [!NOTE] +> This proposal was accepted before Swift Testing began using the Swift +> evolution review process. Its original identifier was +> [SWT-0001](https://github.com/swiftlang/swift-testing/blob/main/Documentation/Proposals/0001-refactor-bug-inits.md). + +## Introduction + +One of the features of swift-testing is a test traits system that allows +associating metadata with a test suite or test function. One trait in +particular, `.bug()`, has the potential for integration with development tools +but needs some refinement before integration would be practical. + +## Motivation + +A test author can associate a bug (AKA issue, problem, ticket, etc.) with a test +using the `.bug()` trait, to which they pass an "identifier" for the bug. The +swift-testing team's intent here was that a test author would pass the unique +identifier of the bug in the test author's preferred bug-tracking system (e.g. +GitHub Issues, Bugzilla, etc.) and that any tooling built around this trait +would be able to infer where the bug was located and how to view it. + +It became clear immediately that a generic system for looking up bugs by unique +identifier in an arbitrary and unspecified database wouldn't be a workable +solution. So we modified the description of `.bug()` to explain that, if the +identifier passed to it was a valid URL, then it would be "interpreted" as a URL +and that tools could be designed to open that URL as needed. + +This design change then placed the burden of parsing each `.bug()` trait and +potentially mapping it to a URL on tools. swift-testing itself avoids linking to +or using Foundation API such as `URL`, so checking for a valid URL inside the +testing library was not feasible either. + +## Proposed solution + +To solve the underlying problem and allow test authors to specify a URL when +available, or just an opaque identifier otherwise, we propose splitting the +`.bug()` function up into two overloads: + +- The first overload takes a URL string and additional optional metadata; +- The second overload takes a bug identifier as an opaque string or integer and, + optionally, a URL string. + +Test authors are then free to specify any combination of URL and opaque +identifier depending on the information they have available and their specific +needs. Tools authors are free to consume either or both of these properties and +present them where appropriate. + +## Detailed design + +The `Bug` trait type and `.bug()` trait factory function shall be refactored +thusly: + +```swift +/// A type representing a bug report tracked by a test. +/// +/// To add this trait to a test, use one of the following functions: +/// +/// - ``Trait/bug(_:_:)`` +/// - ``Trait/bug(_:id:_:)-10yf5`` +/// - ``Trait/bug(_:id:_:)-3vtpl`` +public struct Bug: TestTrait, SuiteTrait, Equatable, Hashable, Codable { + /// A URL linking to more information about the bug, if available. + /// + /// The value of this property represents a URL conforming to + /// [RFC 3986](https://www.ietf.org/rfc/rfc3986.txt). + public var url: String? + + /// A unique identifier in this bug's associated bug-tracking system, if + /// available. + /// + /// For more information on how the testing library interprets bug + /// identifiers, see . + public var id: String? + + /// The human-readable title of the bug, if specified by the test author. + public var title: Comment? +} + +extension Trait where Self == Bug { + /// Construct a bug to track with a test. + /// + /// - Parameters: + /// - url: A URL referring to this bug in the associated bug-tracking + /// system. + /// - title: Optionally, the human-readable title of the bug. + /// + /// - Returns: An instance of ``Bug`` representing the specified bug. + public static func bug(_ url: _const String, _ title: Comment? = nil) -> Self + + /// Construct a bug to track with a test. + /// + /// - Parameters: + /// - url: A URL referring to this bug in the associated bug-tracking + /// system. + /// - id: The unique identifier of this bug in its associated bug-tracking + /// system. + /// - title: Optionally, the human-readable title of the bug. + /// + /// - Returns: An instance of ``Bug`` representing the specified bug. + public static func bug(_ url: _const String? = nil, id: some Numeric, _ title: Comment? = nil) -> Self + + /// Construct a bug to track with a test. + /// + /// - Parameters: + /// - url: A URL referring to this bug in the associated bug-tracking + /// system. + /// - id: The unique identifier of this bug in its associated bug-tracking + /// system. + /// - title: Optionally, the human-readable title of the bug. + /// + /// - Returns: An instance of ``Bug`` representing the specified bug. + public static func bug(_ url: _const String? = nil, id: _const String, _ title: Comment? = nil) -> Self +} +``` + +The `@Test` and `@Suite` macros have already been modified so that they perform +basic validation of a URL string passed as input and emit a diagnostic if the +URL string appears malformed. + +## Source compatibility + +This change is expected to be source-breaking for test authors who have already +adopted the existing `.bug()` functions. This change is source-breaking for code +that directly refers to these functions by their signatures. This change is +source-breaking for code that uses the `identifier` property of the `Bug` type +or expects it to contain a URL. + +## Integration with supporting tools + +Tools that integrate with swift-testing and provide lists of tests or record +results after tests have run can use the `Bug` trait on tests to present +relevant identifiers and/or URLs to users. + +Tools that use the experimental event stream output feature of the testing +library will need a JSON schema for bug traits on tests. This work is tracked in +a separate upcoming proposal. + +## Alternatives considered + +- Inferring whether or not a bug identifier was a URL by parsing it at runtime + in tools. As discussed above, this option would require every tool that + integrates with swift-testing to provide its own URL-parsing logic. + +- Using different argument labels (e.g. the label `url` for the URL argument + and/or no label for the `id` argument.) We felt that URLs, which are + recognizable by their general structure, did not need labels. At least one + argument must have a label to avoid ambiguous resolution of the `.bug()` + function at compile time. + +- Inferring whether or not a bug identifier was a URL by parsing it at compile- + time or at runtime using `Foundation.URL` or libcurl. swift-testing actively + avoids linking to Foundation if at all possible, and libcurl would be a + platform-specific solution (Windows doesn't ship with libcurl, but does have + `InternetCrackUrlW()` whose parsing engine differs.) We also run the risk of + inappropriately interpreting some arbitrary bug identifier as a URL when it is + not meant to be parsed that way. + +- Removing the `.bug()` trait. We see this particular trait as having strong + potential for integration with tools and for use by test authors; removing it + because we can't reliably parse URLs would be unfortunate. + +## Acknowledgments + +Thanks to the swift-testing team and managers for their contributions! Thanks to +our community for the initial feedback around this feature. diff --git a/proposals/testing/0002-json-abi.md b/proposals/testing/0002-json-abi.md new file mode 100644 index 0000000000..e13bb1ad37 --- /dev/null +++ b/proposals/testing/0002-json-abi.md @@ -0,0 +1,428 @@ +# A stable JSON-based ABI for tools integration + +* Proposal: [ST-0002](0002-json-abi.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Status: **Implemented (Swift 6.0)** +* Implementation: [swiftlang/swift-testing#383](https://github.com/swiftlang/swift-testing/pull/383), + [swiftlang/swift-testing#402](https://github.com/swiftlang/swift-testing/pull/402) +* Review: ([pitch](https://forums.swift.org/t/pitch-a-stable-json-based-abi-for-tools-integration/72627)), ([acceptance](https://forums.swift.org/t/pitch-a-stable-json-based-abi-for-tools-integration/72627/4)) + +> [!NOTE] +> This proposal was accepted before Swift Testing began using the Swift +> evolution review process. Its original identifier was +> [SWT-0002](https://github.com/swiftlang/swift-testing/blob/main/Documentation/Proposals/0002-json-abi.md). + +## Introduction + +One of the core components of Swift Testing is its ability to interoperate with +Xcode 16, VS Code, and other tools. Swift Testing has been fully open-sourced +across all platforms supported by Swift, and can be added as a package +dependency (or—eventually—linked from the Swift toolchain.) + +## Motivation + +Because Swift Testing may be used in various forms, and because integration with +various tools is critical to its success, we need it to have a stable interface +that can be used regardless of how it's been added to a package. There are a few +patterns in particular we know we need to support: + +- An IDE (e.g. Xcode 16) that builds and links its own copy of Swift Testing: + the copy used by the IDE might be the same as the copy that tests use, in + which case interoperation is trivial, but it may also be distinct if the tests + use Swift Testing as a package dependency. + + In the case of Xcode 16, Swift Testing is built as a framework much like + XCTest and is automatically linked by test targets in an Xcode project or + Swift package, but if the test target specifies a package dependency on Swift + Testing, that dependency will take priority when the test code is compiled. + +- An IDE (e.g. VS Code) that does _not_ link directly to Swift Testing (and + perhaps, as with VS Code, cannot because it is not natively compiled): such an + IDE needs a way to configure and invoke test code and then to read events back + as they occur, but cannot touch the Swift symbols used by the tests. + + In the case of VS Code, because it is implemented using TypeScript, it is not + able to directly link to Swift Testing or other Swift libraries. In order for + it to interpret events from a test run like "test started" or "issue + recorded", it needs to receive those events in a format it can understand. + +Tools integration is important to the success of Swift Testing. The more tools +provide integrations for it, the more likely developers are to adopt it. The +more developers adopt, the more tests are written. And the more tests are +written, the better our lives as software engineers will be. + +## Proposed solution + +We propose defining and implementing a stable ABI for using Swift Testing that +can be reliably adopted by various IDEs and other tools. There are two aspects +of this ABI we need to implement: + +- A stable entry point function that can be resolved dynamically at runtime (on + platforms with dynamic loaders such as Darwin, Linux, and Windows.) This + function needs a signature that will not change over time and which will take + input and pass back asynchronous output in a format that a wide variety of + tools will be able to interpret (whether they are written in Swift or not.) + + This function should be implemented in Swift as it is expected to be used by + code that can call into Swift, but which cannot rely on the specific binary + minutiae of a given copy of Swift Testing. + +- A stable format for input that can be passed to the entry point function and + which can also be passed at the command line; and a stable format for output + that can be consumed by tools to interpret test results. + + Some tools cannot directly link to Swift code and must instead rely on + command-line invocations of `swift test`. These tools will be able to pass + their test configuration and options as an argument in the stable format and + will be able to receive event information in the same stable format via a + dedicated channel such as a file or named pipe. + +> [!NOTE] +> This document proposes defining a stable format for input and output, but only +> actually defines the JSON schema for _output_. We intend to define the schema +> for input in a subsequent proposal. +> +> In the interim, early adopters can encode an instance of Swift Testing's +> `__CommandLineArguments_v0` type using `JSONEncoder`. + +## Detailed design + +We propose defining the stable input and output format using JSON as it is +widely supported across platforms and languages. The proposed JSON schema for +output is defined [here](https://github.com/swiftlang/swift-testing/blob/main/Documentation/ABI/JSON.md). + +### Example output + +The proposed schema is a sequence of JSON objects written to an event handler or +file stream. When a test run starts, Swift Testing first emits a sequence of +JSON objects representing each test that is part of the planned run. For +example, this is the JSON representation of Swift Testing's own `canGetStdout()` +test function: + +```json +{ + "kind": "test", + "payload": { + "displayName": "Can get stdout", + "id": "TestingTests.FileHandleTests/canGetStdout()/FileHandleTests.swift:33:4", + "isParameterized": false, + "kind": "function", + "name": "canGetStdout()", + "sourceLocation": { + "column": 4, + "fileID": "TestingTests/FileHandleTests.swift", + "line": 33 + } + }, + "version": 0 +} +``` + +A tool that is observing this data stream can build a map or dictionary of test +IDs to comprehensive test details if needed. Once all tests in the planned run +have been written out, testing begins. Swift Testing writes a sequence of JSON +objects representing various events such as "test started" or "issue recorded". +For example, here is an abridged sequence of events generated for a test that +records a failed expectation: + +```json +{ + "kind": "event", + "payload": { + "instant": { + "absolute": 266418.545786299, + "since1970": 1718302639.76747 + }, + "kind": "testStarted", + "messages": [ + { + "symbol": "default", + "text": "Test \"Can get stdout\" started." + } + ], + "testID": "TestingTests.FileHandleTests/canGetStdout()/FileHandleTests.swift:33:4" + }, + "version": 0 +} + +{ + "kind": "event", + "payload": { + "instant": { + "absolute": 266636.524236724, + "since1970": 1718302857.74857 + }, + "issue": { + "isKnown": false, + "sourceLocation": { + "column": 7, + "fileID": "TestingTests/FileHandleTests.swift", + "line": 29 + } + }, + "kind": "issueRecorded", + "messages": [ + { + "symbol": "fail", + "text": "Expectation failed: (EOF → -1) == (feof(fileHandle) → 0)" + } + ], + "testID": "TestingTests.FileHandleTests/canGetStdout()/FileHandleTests.swift:33:4" + }, + "version": 0 +} + +{ + "kind": "event", + "payload": { + "instant": { + "absolute": 266636.524741106, + "since1970": 1718302857.74908 + }, + "kind": "testEnded", + "messages": [ + { + "symbol": "fail", + "text": "Test \"Can get stdout\" failed after 0.001 seconds with 1 issue." + } + ], + "testID": "TestingTests.FileHandleTests/canGetStdout()/FileHandleTests.swift:33:4" + }, + "version": 0 +} +``` + +Each event includes zero or more "messages" that Swift Testing intends to +present to the user. These messages contain human-readable text as well as +abstractly-specified symbols that correspond to the output written to the +standard error stream of the test process. Tools can opt to present these +messages in whatever ways are appropriate for their interfaces. + +### Invoking from the command line + +When invoking `swift test`, we propose adding three new arguments to Swift +Package Manager: + +| Argument | Value Type | Description | +|---|:-:|---| +| `--configuration-path` | File system path | Specifies a path to a file, named pipe, etc. containing test configuration/options. | +| `--event-stream-output-path` | File system path | Specifies a path to a file, named pipe, etc. to which output should be written. | +| `--event-stream-version` | Integer | Specifies the version of the stable JSON schema to use for output. | + +The process for adding arguments to Swift Package Manager is separate from the +process for Swift Testing API changes, so the names of these arguments are +speculative and are subject to change as part of the Swift Package Manager +review process. + +If `--configuration-path` is specified, Swift Testing will open it for reading +and attempt to decode its contents as JSON. If `--event-stream-output-path` is +specified, Swift Testing will open it for writing and will write a sequence of +[JSON Lines](https://jsonlines.org) to it representing the data and events +produced by the test run. `--event-stream-version` determines the stable schema +used for output; pass `0` to match the schema proposed in this document. + +> [!NOTE] +> If `--event-stream-output-path` is specified but `--event-stream-version` is +> not, the format _currently_ used is based on direct JSON encodings of the +> internal Swift structures used by Swift Testing. This format is necessary to +> support Xcode 16 Beta 1. In the future, the default value of this argument +> will be assumed to equal the newest available JSON schema version (`0` as of +> this document's acceptance, i.e. the JSON schema will match what we are +> proposing here until a new schema supersedes it.) +> +> Tools authors that rely on the JSON schema are strongly advised to specify a +> version rather than relying on this behavior to avoid breaking changes in the +> future. + +On platforms that support them, callers can use a named pipe with +`--event-stream-output-path` to get live results back from the test run rather +than needing to wait until the file is closed by the test process. Named pipes +can be created on Darwin or Linux with the POSIX [`mkfifo()`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/mkfifo.2.html) +function or on Windows with the [`CreateNamedPipe()`](https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createnamedpipew) +function. + +If `--configuration-path` is specified in addition to explicit command-line +options like `--no-parallel`, the explicit command-line options take priority. + +### Invoking from Swift + +Tools that can link to and call Swift directly have the option of instantiating +the tools-only SPI type `Runner`, however this is only possible if the tools and +the test target link to the exact same copy of Swift Testing. To support tools +that may link to a different copy (intentionally or otherwise), we propose +adding an exported symbol to the Swift Testing library with the following Swift +signature: + +```swift +@_spi(ForToolsIntegrationOnly) +public enum ABIv0 { + /* ... */ + + /// The type of the entry point to the testing library used by tools that want + /// to remain version-agnostic regarding the testing library. + /// + /// - Parameters: + /// - configurationJSON: A buffer to memory representing the test + /// configuration and options. If `nil`, a new instance is synthesized + /// from the command-line arguments to the current process. + /// - recordHandler: A JSON record handler to which is passed a buffer to + /// memory representing each record as described in `ABI/JSON.md`. + /// + /// - Returns: Whether or not the test run finished successfully. + /// + /// - Throws: Any error that occurred prior to running tests. Errors that are + /// thrown while tests are running are handled by the testing library. + public typealias EntryPoint = @convention(thin) @Sendable ( + _ configurationJSON: UnsafeRawBufferPointer?, + _ recordHandler: @escaping @Sendable (_ recordJSON: UnsafeRawBufferPointer) -> Void + ) async throws -> Bool + + /// The entry point to the testing library used by tools that want to remain + /// version-agnostic regarding the testing library. + /// + /// The value of this property is a Swift function that can be used by tools + /// that do not link directly to the testing library and wish to invoke tests + /// in a binary that has been loaded into the current process. The value of + /// this property is accessible from C and C++ as a function with name + /// `"swt_abiv0_getEntryPoint"` and can be dynamically looked up at runtime + /// using `dlsym()` or a platform equivalent. + /// + /// The value of this property can be thought of as equivalent to + /// `swift test --event-stream-output-path` except that, instead of streaming + /// JSON records to a named pipe or file, it streams them to an in-process + /// callback. + public static var entryPoint: EntryPoint { get } +} +``` + +The inputs and outputs to this function are typed as `UnsafeRawBufferPointer` +rather than `Data` because the latter is part of Foundation, and adding a public +dependency on a Foundation type would make it very difficult for Foundation to +adopt Swift Testing. It is a goal of the Swift Testing team to keep our Swift +dependency list as small as possible. + +### Invoking from C or C++ + +We expect most tools that need to make use of this entry point will not be able +to directly link to the exported Swift symbol and will instead need to look it +up at runtime using a platform-specific interface such as [`dlsym()`](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html) +or [`GetProcAddress()`](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress). +The `ABIv0.entryPoint` property's getter will be exported to C and C++ as: + +```c++ +extern "C" const void *_Nonnull swt_abiv0_getEntryPoint(void); +``` + +The value returned from this C function is a direct representation of the value +of `ABIv0.entryPoint` and can be cast back to its Swift function type using +[`unsafeBitCast(_:to:)`](https://developer.apple.com/documentation/swift/unsafebitcast%28_%3Ato%3A%29). + +On platforms where data-pointer-to-function-pointer conversion is disallowed per +the C standard, this operation is unsupported. See §6.3.2.3 and §J.5.7 of +[the C standard](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf). + +> [!NOTE] +> Swift Testing is statically linked into the main executable when it is +> included as a package dependency. On Linux and other platforms that use the +> ELF executable format, symbol information for the main executable may not be +> available at runtime unless the `--export-dynamic` flag is passed to the +> linker. + +## Source compatibility + +The changes proposed in this document are additive. + +## Integration with supporting tools + +Tools are able to use the proposed additions as described above. + +## Future directions + +- Extending the JSON schema to cover _input_ as well as _output_. As discussed, + we will do so in a subsequent proposal. + +- Extending the JSON schema to include richer information about events such as + specific mismatched values in `#expect()` calls. This information is complex + and we need to take care to model it efficiently and clearly. + +- Adding Markdown or other formats to event messages. Rich text can be used by + tools to emphasize values, switch to code voice, provide improved + accessibility, etc. + +- Adding additional entry points for different access patterns. We anticipate + that a Swift function and a command-line interface are sufficient to cover + most real-world use cases, but it may be the case that tools could use other + mechanisms for starting test runs such as: + - Pure C or Objective-C interfaces; + - A WebAssembly and/or JavaScript [`async`-compatible](https://github.com/WebAssembly/component-model/blob/2f447274b5028f54c549cb4e28ceb493a471dd4b/design/mvp/Async.md) + interface; + - Platform-specific interfaces; or + - Direct bindings to other languages like Rust, Go, C#, etc. + +## Alternatives considered + +- Doing nothing. If we made no changes, we would be effectively requiring + developers to use Xcode for all Swift Testing development and would be + requiring third-party tools to parse human-readable command-line output. This + approach would run counter to several of the Swift project's high-level goals + and would not represent a true cross-platform solution. + +- Using direct JSON encodings of Swift Testing's internal types to represent + output. We initially attempted this and you can see the results in the Swift + Testing repository if you look for "snapshot" types. A major downside became + apparent quickly: these data types don't make for particularly usable JSON + unless you're using `JSONDecoder` to convert back to them, and the default + JSON encodings produced with `JSONEncoder` are not stable if we e.g. add + enumeration cases with associated values or add non-optional fields to types. + +- Using a format other than JSON. We considered using XML, YAML, Apple property + lists, and a few other formats. JSON won out pretty quickly though: it is + widely supported across platforms and languages and it is trivial to create + Swift structures that encode to a well-designed JSON schema using + `JSONEncoder`. Property lists would be just as easy to create, but it is a + proprietary format and would not be trivially decodable on non-Apple platforms + or using non-Apple tools. + +- Exposing the C interface as a function that returns heap-allocated memory + containing a Swift function reference. This allows us to emit a "thick" Swift + function but requires callers to manually manage the resulting memory, and it + may be difficult to reason about code that requires an extra level of pointer + indirection. By having the C entry point function return a thin Swift function + instead, the caller need only bitcast it and can call it directly, and the + equivalent Swift interface can simply be a property getter rather than a + function call. + +- Exposing the C interface as a function that takes a callback and a completion + handler as might traditionally used by Objective-C callers, of the form: + + ```c++ + extern "C" void swt_abiv0_entryPoint( + __attribute__((__noescape__)) const void *_Nullable configurationJSON, + size_t configurationJSONLength, + void *_Null_unspecified context, + void (*_Nonnull recordHandler)( + __attribute__((__noescape__)) const void *recordJSON, + size_t recordJSONLength, + void *_Null_unspecified context + ), + void (*_Nonnull completionHandler)( + _Bool success, + void *_Null_unspecified context + ) + ); + ``` + + The known clients of the native entry point function are all able to call + Swift code and do not need this sort of interface. If there are other clients + that would need the entry point to use a signature like this one, it would be + straightforward to implement it in a future amendment to this proposal. + +## Acknowledgments + +Thanks much to [Dennis Weissmann](https://github.com/dennisweissmann) for his +tireless work in this area and to [Paul LeMarquand](https://github.com/plemarquand) +for putting up with my incessant revisions and nitpicking while he worked on +VS Code's Swift Testing support. + +Thanks to the rest of the Swift Testing team for reviewing this proposal and the +JSON schema and to the community for embracing Swift Testing! diff --git a/proposals/testing/0003-make-serialized-trait-api.md b/proposals/testing/0003-make-serialized-trait-api.md new file mode 100644 index 0000000000..73a6fbe1a2 --- /dev/null +++ b/proposals/testing/0003-make-serialized-trait-api.md @@ -0,0 +1,156 @@ +# Make .serialized trait API + +* Proposal: [ST-0003](0003-make-serialized-trait-api.md) +* Authors: [Dennis Weissmann](https://github.com/dennisweissmann) +* Status: **Implemented (Swift 6.0)** +* Implementation: +[swiftlang/swift-testing#535](https://github.com/swiftlang/swift-testing/pull/535) +* Review: +([pitch](https://forums.swift.org/t/pitch-make-serialized-trait-public-api/73147)), +([acceptance](https://forums.swift.org/t/pitch-make-serialized-trait-public-api/73147/5)) + +> [!NOTE] +> This proposal was accepted before Swift Testing began using the Swift +> evolution review process. Its original identifier was +> [SWT-0003](https://github.com/swiftlang/swift-testing/blob/main/Documentation/Proposals/0003-make-serialized-trait-api.md). + +## Introduction + +We propose promoting the existing `.serialized` trait to public API. This trait +enables developers to designate tests or test suites to run serially, ensuring +sequential execution where necessary. + +## Motivation + +The Swift Testing library defaults to parallel execution of tests, promoting +efficiency and isolation. However, certain test scenarios demand strict +sequential execution due to shared state or complex dependencies between tests. +The `.serialized` trait provides a solution by allowing developers to enforce +serial execution for specific tests or suites. + +While global actors ensure that only one task associated with that actor runs +at any given time, thus preventing concurrent access to actor state, tasks can +yield and allow other tasks to proceed, potentially interleaving execution. +That means global actors do not ensure that a specific test runs entirely to +completion before another begins. A testing library requires a construct that +guarantees that each annotated test runs independently and completely (in its +suite), one after another, without interleaving. + +## Proposed Solution + +We propose exposing the `.serialized` trait as a public API. This attribute can +be applied to individual test functions or entire test suites, modifying the +test execution behavior to enforce sequential execution where specified. + +Annotating just a single test in a suite does not enforce any serialization +behavior - the testing library encourages parallelization and the bar to +degrade overall performance of test execution should be high. +Additionally, traits apply inwards - it would be unexpected to impact the exact +conditions of a another test in a suite without applying a trait to the suite +itself. +Thus, this trait should only be applied to suites (to enforce serial execution +of all tests inside it) or parameterized tests. If applied to just a test this +trait does not have any effect. + +## Detailed Design + +The `.serialized` trait functions as an attribute that alters the execution +scheduling of tests. When applied, it ensures that tests or suites annotated +with `.serialized` run serially. + +```swift +/// A type that affects whether or not a test or suite is parallelized. +/// +/// When added to a parameterized test function, this trait causes that test to +/// run its cases serially instead of in parallel. When applied to a +/// non-parameterized test function, this trait has no effect. When applied to a +/// test suite, this trait causes that suite to run its contained test functions +/// and sub-suites serially instead of in parallel. +/// +/// This trait is recursively applied: if it is applied to a suite, any +/// parameterized tests or test suites contained in that suite are also +/// serialized (as are any tests contained in those suites, and so on.) +/// +/// This trait does not affect the execution of a test relative to its peers or +/// to unrelated tests. This trait has no effect if test parallelization is +/// globally disabled (by, for example, passing `--no-parallel` to the +/// `swift test` command.) +/// +/// To add this trait to a test, use ``Trait/serialized``. +public struct ParallelizationTrait: TestTrait, SuiteTrait {} + +extension Trait where Self == ParallelizationTrait { + /// A trait that serializes the test to which it is applied. + /// + /// ## See Also + /// + /// - ``ParallelizationTrait`` + public static var serialized: Self { get } +} +``` + +The call site looks like this: + +```swift +@Test(.serialized, arguments: Food.allCases) func prepare(food: Food) { + // This function will be invoked serially, once per food, because it has the + // .serialized trait. +} + +@Suite(.serialized) struct FoodTruckTests { + @Test(arguments: Condiment.allCases) func refill(condiment: Condiment) { + // This function will be invoked serially, once per condiment, because the + // containing suite has the .serialized trait. + } + + @Test func startEngine() async throws { + // This function will not run while refill(condiment:) is running. One test + // must end before the other will start. + } +} + +@Suite struct FoodTruckTests { + @Test(.serialized) func startEngine() async throws { + // This function will not run serially - it's not a parameterized test and + // the suite is not annotated with the `.serialized` trait. + } + + @Test func prepareFood() async throws { + // It doesn't matter if this test is `.serialized` or not, traits applied + // to other tests won't affect this test don't impact other tests. + } +} +``` + +## Source Compatibility + +Introducing `.serialized` as a public API does not have any impact on existing +code. Tests will continue to run in parallel by default unless explicitly +marked with `.serialized`. + +## Integration with Supporting Tools + +N/A. + +## Future Directions + +There might be asks for more advanced and complex ways to affect parallelization +which include ways to specify dependencies between tests ie. "Require `foo()` to +run before `bar()`". + +## Alternatives Considered + +Alternative approaches, such as relying solely on global actors for test +isolation, were considered. However, global actors do not provide the +deterministic, sequential execution required for certain testing scenarios. The +`.serialized` trait offers a more explicit and flexible mechanism, ensuring +that each designated test or suite runs to completion without interruption. + +Various more complex parallelization and serialization options were discussed +and considered but ultimately disregarded in favor of this simple yet powerful +implementation. + +## Acknowledgments + +Thanks to the swift-testing team and managers for their contributions! Thanks +to our community for the initial feedback around this feature. diff --git a/proposals/testing/0004-constrain-the-granularity-of-test-time-limit-durations.md b/proposals/testing/0004-constrain-the-granularity-of-test-time-limit-durations.md new file mode 100644 index 0000000000..4817bc955c --- /dev/null +++ b/proposals/testing/0004-constrain-the-granularity-of-test-time-limit-durations.md @@ -0,0 +1,207 @@ +# Constrain the granularity of test time limit durations + +* Proposal: [ST-0004](0004-constrain-the-granularity-of-test-time-limit-durations.md) +* Authors: [Dennis Weissmann](https://github.com/dennisweissmann) +* Status: **Implemented (Swift 6.0)** +* Implementation: +[swiftlang/swift-testing#534](https://github.com/swiftlang/swift-testing/pull/534) +* Review: +([pitch](https://forums.swift.org/t/pitch-constrain-the-granularity-of-test-time-limit-durations/73146)), +([acceptance](https://forums.swift.org/t/pitch-constrain-the-granularity-of-test-time-limit-durations/73146/3)) + +> [!NOTE] +> This proposal was accepted before Swift Testing began using the Swift +> evolution review process. Its original identifier was +> [SWT-0004](https://github.com/swiftlang/swift-testing/blob/main/Documentation/Proposals/0004-constrain-the-granularity-of-test-time-limit-durations.md). + +## Introduction + +Sometimes tests might get into a state (either due the test code itself or due +to the code they're testing) where they don't make forward progress and hang. +Swift Testing provides a way to handle these issues using the TimeLimit trait: + +```swift +@Test(.timeLimit(.minutes(60)) +func testFunction() { ... } +``` + +Currently there exist multiple overloads for the `.timeLimit` trait: one that +takes a `Swift.Duration` which allows for arbitrary `Duration` values to be +passed, and one that takes a `TimeLimitTrait.Duration` which constrains the +minimum time limit as well as the increment to 1 minute. + +## Motivation + +Small time limit values in particular cause more harm than good due to tests +running in environments with drastically differing performance characteristics. +Particularly when running in CI systems or on virtualized hardware tests can +run much slower than at desk. +Swift Testing should help developers use a reasonable time limit value in its +API without developers having to refer to the documentation. + +It is crucial to emphasize that unit tests failing due to exceeding their +timeout should be exceptionally rare. At the same time, a spurious unit test +failure caused by a short timeout can be surprisingly costly, potentially +leading to an entire CI pipeline being rerun. Determining an appropriate +timeout for a specific test can be a challenging task. + +Additionally, when the system intentionally runs multiple tests simultaneously +to optimize resource utilization, the scheduler becomes the arbiter of test +execution. Consequently, the test may take significantly longer than +anticipated, potentially due to external factors beyond the control of the code +under test. + +A unit test should be capable of failing due to hanging, but it should not fail +due to being slow, unless the developer has explicitly indicated that it +should, effectively transforming it into a performance test. + +The time limit feature is *not* intended to be used to apply small timeouts to +tests to ensure test runtime doesn't regress by small amounts. This feature is +intended to be used to guard against hangs and pathologically long running +tests. + +## Proposed Solution + +We propose changing the `.timeLimit` API to accept values of a new `Duration` +type defined in `TimeLimitTrait` which only allows for `.minute` values to be +passed. +This type already exists as SPI and this proposal is seeking to making it API. + +## Detailed Design + +The `TimeLimitTrait.Duration` struct only has one factory method: +```swift +public static func minutes(_ minutes: some BinaryInteger) -> Self +``` + +That ensures 2 things: +1. It's impossible to create short time limits (under a minute). +2. It's impossible to create high-precision increments of time. + +Both of these features are important to ensure the API is self documenting and +conveying the intended purpose. + +For parameterized tests these time limits apply to each individual test case. + +The `TimeLimitTrait.Duration` struct is declared as follows: + +```swift +/// A type that defines a time limit to apply to a test. +/// +/// To add this trait to a test, use one of the following functions: +/// +/// - ``Trait/timeLimit(_:)`` +@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) +public struct TimeLimitTrait: TestTrait, SuiteTrait { + /// A type representing the duration of a time limit applied to a test. + /// + /// This type is intended for use specifically for specifying test timeouts + /// with ``TimeLimitTrait``. It is used instead of Swift's built-in `Duration` + /// type because test timeouts do not support high-precision, arbitrarily + /// short durations. The smallest allowed unit of time is minutes. + public struct Duration: Sendable { + + /// Construct a time limit duration given a number of minutes. + /// + /// - Parameters: + /// - minutes: The number of minutes the resulting duration should + /// represent. + /// + /// - Returns: A duration representing the specified number of minutes. + public static func minutes(_ minutes: some BinaryInteger) -> Self + } + + /// The maximum amount of time a test may run for before timing out. + public var timeLimit: Swift.Duration { get set } +} +``` + +The extension on `Trait` that allows for `.timeLimit(...)` to work is defined +like this: + +```swift +/// Construct a time limit trait that causes a test to time out if it runs for +/// too long. +/// +/// - Parameters: +/// - timeLimit: The maximum amount of time the test may run for. +/// +/// - Returns: An instance of ``TimeLimitTrait``. +/// +/// Test timeouts do not support high-precision, arbitrarily short durations +/// due to variability in testing environments. The time limit must be at +/// least one minute, and can only be expressed in increments of one minute. +/// +/// When this trait is associated with a test, that test must complete within +/// a time limit of, at most, `timeLimit`. If the test runs longer, an issue +/// of kind ``Issue/Kind/timeLimitExceeded(timeLimitComponents:)`` is +/// recorded. This timeout is treated as a test failure. +/// +/// The time limit amount specified by `timeLimit` may be reduced if the +/// testing library is configured to enforce a maximum per-test limit. When +/// such a maximum is set, the effective time limit of the test this trait is +/// applied to will be the lesser of `timeLimit` and that maximum. This is a +/// policy which may be configured on a global basis by the tool responsible +/// for launching the test process. Refer to that tool's documentation for +/// more details. +/// +/// If a test is parameterized, this time limit is applied to each of its +/// test cases individually. If a test has more than one time limit associated +/// with it, the shortest one is used. A test run may also be configured with +/// a maximum time limit per test case. +public static func timeLimit(_ timeLimit: Self.Duration) -> Self +``` + +And finally, the call site of the API looks like this: + +```swift +@Test(.timeLimit(.minutes(60)) +func serve100CustomersInOneHour() async { + for _ in 0 ..< 100 { + let customer = await Customer.next() + await customer.order() + ... + } +} +``` + +The `TimeLimitTrait.Duration` struct has various `unavailable` overloads that +are included for diagnostic purposes only. They are all documented and +annotated like this: + +```swift +/// Construct a time limit duration given a number of . +/// +/// This function is unavailable and is provided for diagnostic purposes only. +@available(*, unavailable, message: "Time limit must be specified in minutes") +``` + +## Source Compatibility + +This impacts clients that have adopted the `.timeLimit` trait and use overloads +of the trait that accept an arbitrary `Swift.Duration` except if they used the +`minutes` overload. + +## Integration with Supporting Tools + +N/A + +## Future Directions + +We could allow more finegrained time limits in the future that scale with the +performance of the test host device. +Or take a more manual approach where we detect the type of environment +(like CI vs local) and provide a way to use different timeouts depending on the +environment. + +## Alternatives Considered + +We have considered using `Swift.Duration` as the currency type for this API but +decided against it to avoid common pitfalls and misuses of this feature such as +providing very small time limits that lead to flaky tests in different +environments. + +## Acknowledgments + +The authors acknowledge valuable contributions and feedback from the Swift +Testing community during the development of this proposal. diff --git a/proposals/testing/0005-ranged-confirmations.md b/proposals/testing/0005-ranged-confirmations.md new file mode 100644 index 0000000000..93997c86e9 --- /dev/null +++ b/proposals/testing/0005-ranged-confirmations.md @@ -0,0 +1,191 @@ +# Range-based confirmations + +* Proposal: [ST-0005](0005-ranged-confirmations.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Status: **Implemented (Swift 6.1)** +* Bug: rdar://138499457 +* Implementation: [swiftlang/swift-testing#598](https://github.com/swiftlang/swift-testing/pull/598), [swiftlang/swift-testing#689](https://github.com/swiftlang/swift-testing/pull689) +* Review: ([pitch](https://forums.swift.org/t/pitch-range-based-confirmations/74589)), + ([acceptance](https://forums.swift.org/t/pitch-range-based-confirmations/74589/7)) + +> [!NOTE] +> This proposal was accepted before Swift Testing began using the Swift +> evolution review process. Its original identifier was +> [SWT-0005](https://github.com/swiftlang/swift-testing/blob/main/Documentation/Proposals/0005-ranged-confirmations.md). + +## Introduction + +Swift Testing includes [an interface](https://swiftpackageindex.com/swiftlang/swift-testing/main/documentation/testing/confirmation(_:expectedcount:isolation:sourcelocation:_:)) +for checking that some asynchronous event occurs a given number of times +(typically exactly once or never at all.) This proposal enhances that interface +to allow arbitrary ranges of event counts so that a test can be written against +code that may not always fire said event the exact same number of times. + +## Motivation + +Some tests rely on fixtures or external state that is not perfectly +deterministic. For example, consider a test that checks that clicking the mouse +button will generate a `.mouseClicked` event. Such a test might use the +`confirmation()` interface: + +```swift +await confirmation(expectedCount: 1) { mouseClicked in + var eventLoop = EventLoop() + eventLoop.eventHandler = { event in + if event == .mouseClicked { + mouseClicked() + } + } + await eventLoop.simulate(.mouseClicked) +} +``` + +But what happens if the user _actually_ clicks a mouse button while this test is +running? That might trigger a _second_ `.mouseClicked` event, and then the test +will fail spuriously. + +## Proposed solution + +If the test author could instead indicate to Swift Testing that their test will +generate _one or more_ events, they could avoid spurious failures: + +```swift +await confirmation(expectedCount: 1...) { mouseClicked in + ... +} +``` + +With this proposal, we add an overload of `confirmation()` that takes any range +expression instead of a single integer value (which is still accepted via the +existing overload.) + +## Detailed design + +A new overload of `confirmation()` is added: + +```swift +/// Confirm that some event occurs during the invocation of a function. +/// +/// - Parameters: +/// - comment: An optional comment to apply to any issues generated by this +/// function. +/// - expectedCount: A range of integers indicating the number of times the +/// expected event should occur when `body` is invoked. +/// - isolation: The actor to which `body` is isolated, if any. +/// - sourceLocation: The source location to which any recorded issues should +/// be attributed. +/// - body: The function to invoke. +/// +/// - Returns: Whatever is returned by `body`. +/// +/// - Throws: Whatever is thrown by `body`. +/// +/// Use confirmations to check that an event occurs while a test is running in +/// complex scenarios where `#expect()` and `#require()` are insufficient. For +/// example, a confirmation may be useful when an expected event occurs: +/// +/// - In a context that cannot be awaited by the calling function such as an +/// event handler or delegate callback; +/// - More than once, or never; or +/// - As a callback that is invoked as part of a larger operation. +/// +/// To use a confirmation, pass a closure containing the work to be performed. +/// The testing library will then pass an instance of ``Confirmation`` to the +/// closure. Every time the event in question occurs, the closure should call +/// the confirmation: +/// +/// ```swift +/// let minBuns = 5 +/// let maxBuns = 10 +/// await confirmation( +/// "Baked between \(minBuns) and \(maxBuns) buns", +/// expectedCount: minBuns ... maxBuns +/// ) { bunBaked in +/// foodTruck.eventHandler = { event in +/// if event == .baked(.cinnamonBun) { +/// bunBaked() +/// } +/// } +/// await foodTruck.bakeTray(of: .cinnamonBun) +/// } +/// ``` +/// +/// When the closure returns, the testing library checks if the confirmation's +/// preconditions have been met, and records an issue if they have not. +/// +/// If an exact count is expected, use +/// ``confirmation(_:expectedCount:isolation:sourceLocation:_:)`` instead. +public func confirmation( + _ comment: Comment? = nil, + expectedCount: some RangeExpression & Sequence Sendable, + isolation: isolated (any Actor)? = #isolation, + sourceLocation: SourceLocation = #_sourceLocation, + _ body: (Confirmation) async throws -> sending R +) async rethrows -> R +``` + +### Ranges without lower bounds + +Certain types of range, specifically [`PartialRangeUpTo`](https://developer.apple.com/documentation/swift/partialrangeupto) +and [`PartialRangeThrough`](https://developer.apple.com/documentation/swift/partialrangethrough), +may have surprising behavior when used with this new interface because they +implicitly include `0`. If a test author writes `...10`, do they mean "zero to +ten" or "one to ten"? The programmatic meaning is the former, but some test +authors might mean the latter. If an event does not occur, a test using +`confirmation()` and this `expectedCount` value would pass when the test author +meant for it to fail. + +The unbounded range (`...`) type `UnboundedRange` is effectively useless when +used with this interface and any use of it here is almost certainly a programmer +error. + +`PartialRangeUpTo` and `PartialRangeThrough` conform to `RangeExpression`, but +not to `Sequence`, so they will be rejected at compile time. `UnboundedRange` is +a non-nominal type and will not match either. We will provide unavailable +overloads of `confirmation()` for these types with messages that explain why +they are unavailable, e.g.: + +```swift +@available(*, unavailable, message: "Unbounded range '...' has no effect when used with a confirmation.") +public func confirmation( + _ comment: Comment? = nil, + expectedCount: UnboundedRange, + isolation: isolated (any Actor)? = #isolation, + sourceLocation: SourceLocation = #_sourceLocation, + _ body: (Confirmation) async throws -> R +) async rethrows -> R +``` + +## Source compatibility + +This change is additive. Existing tests are unaffected. + +Code that refers to `confirmation(_:expectedCount:isolation:sourceLocation:_:)` +by symbol name may need to add a contextual type to disambiguate the two +overloads at compile time. + +## Integration with supporting tools + +The type of the associated value `expected` for the `Issue.Kind` case +`confirmationMiscounted(actual:expected:)` will change from `Int` to +`any RangeExpression & Sendable`[^1]. Tools that implement event handlers and +distinguish between `Issue.Kind` cases are advised not to assume the type of +this value is `Int`. + +## Alternatives considered + +- Doing nothing. We have identified real-world use cases for this interface + including in Swift Testing’s own test target. +- Allowing the use of any value as the `expectedCount` argument so long as it + conforms to a protocol `ExpectedCount` (we'd have range types and `Int` + conform by default.) It was unclear what this sort of flexibility would let + us do, and posed challenges for encoding and decoding events and issues when + using the JSON event stream interface. + +## Acknowledgments + +Thanks to the testing team for their help preparing this pitch! + +[^1]: In the future, this type will change to + `any RangeExpression & Sendable`. Compiler support is required + ([96960993](rdar://96960993)). diff --git a/proposals/testing/0006-return-errors-from-expect-throws.md b/proposals/testing/0006-return-errors-from-expect-throws.md new file mode 100644 index 0000000000..c1ba77d5df --- /dev/null +++ b/proposals/testing/0006-return-errors-from-expect-throws.md @@ -0,0 +1,272 @@ +# Return errors from `#expect(throws:)` + +* Proposal: [ST-0006](0006-return-errors-from-expect-throws.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Status: **Implemented (Swift 6.1)** +* Bug: rdar://138235250 +* Implementation: [swiftlang/swift-testing#780](https://github.com/swiftlang/swift-testing/pull/780) +* Review: ([pitch](https://forums.swift.org/t/pitch-returning-errors-from-expect-throws/75567)), ([acceptance](https://forums.swift.org/t/pitch-returning-errors-from-expect-throws/75567/5)) + +> [!NOTE] +> This proposal was accepted before Swift Testing began using the Swift +> evolution review process. Its original identifier was +> [SWT-0006](https://github.com/swiftlang/swift-testing/blob/main/Documentation/Proposals/0006-return-errors-from-expect-throws.md). + +## Introduction + +Swift Testing includes overloads of `#expect()` and `#require()` that can be +used to assert that some code throws an error. They are useful when validating +that your code's failure cases are correctly detected and handled. However, for +more complex validation cases, they aren't particularly ergonomic. This proposal +seeks to resolve that issue by having these overloads return thrown errors for +further inspection. + +## Motivation + +We offer three variants of `#expect(throws:)`: + +- One that takes an error type, and matches any error of the same type; +- One that takes an error _instance_ (conforming to `Equatable`) and matches any + error that compares equal to it; and +- One that takes a trailing closure and allows test authors to write arbitrary + validation logic. + +The third overload has proven to be somewhat problematic. First, it yields the +error to its closure as an instance of `any Error`, which typically forces the +developer to cast it before doing any useful comparisons. Second, the test +author must return `true` to indicate the error matched and `false` to indicate +it didn't, which can be both logically confusing and difficult to express +concisely: + +```swift +try #require { + let potato = try Sack.randomPotato() + try potato.turnIntoFrenchFries() +} throws: { error in + guard let error = error as PotatoError else { + return false + } + guard case .potatoNotPeeled = error else { + return false + } + return error.variety != .russet +} +``` + +The first impulse many test authors have here is to use `#expect()` in the +second closure, but it doesn't return the necessary boolean value _and_ it can +result in multiple issues being recorded in a test when there's really only one. + +## Proposed solution + +I propose deprecating [`#expect(_:sourceLocation:performing:throws:)`](https://developer.apple.com/documentation/testing/expect(_:sourcelocation:performing:throws:)) +and [`#require(_:sourceLocation:performing:throws:)`](https://developer.apple.com/documentation/testing/require(_:sourcelocation:performing:throws:)) +and modifying the other overloads so that, on success, they return the errors +that were thrown. + +## Detailed design + +All overloads of `#expect(throws:)` and `#require(throws:)` will be updated to +return an instance of the error type specified by their arguments, with the +problematic overloads returning `any Error` since more precise type information +is not statically available. The problematic overloads will also be deprecated: + +```diff +--- a/Sources/Testing/Expectations/Expectation+Macro.swift ++++ b/Sources/Testing/Expectations/Expectation+Macro.swift ++@discardableResult + @freestanding(expression) public macro expect( + throws errorType: E.Type, + _ comment: @autoclosure () -> Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, + performing expression: () async throws -> R +-) ++) -> E? where E: Error + ++@discardableResult + @freestanding(expression) public macro require( + throws errorType: E.Type, + _ comment: @autoclosure () -> Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, + performing expression: () async throws -> R +-) where E: Error ++) -> E where E: Error + ++@discardableResult + @freestanding(expression) public macro expect( + throws error: E, + _ comment: @autoclosure () -> Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, + performing expression: () async throws -> R +-) where E: Error & Equatable ++) -> E? where E: Error & Equatable + ++@discardableResult + @freestanding(expression) public macro require( + throws error: E, + _ comment: @autoclosure () -> Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, + performing expression: () async throws -> R +-) where E: Error & Equatable ++) -> E where E: Error & Equatable + ++@available(swift, deprecated: 100000.0, message: "Examine the result of '#expect(throws:)' instead.") ++@discardableResult + @freestanding(expression) public macro expect( + _ comment: @autoclosure () -> Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, + performing expression: () async throws -> R, + throws errorMatcher: (any Error) async throws -> Bool +-) ++) -> (any Error)? + ++@available(swift, deprecated: 100000.0, message: "Examine the result of '#require(throws:)' instead.") ++@discardableResult + @freestanding(expression) public macro require( + _ comment: @autoclosure () -> Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, + performing expression: () async throws -> R, + throws errorMatcher: (any Error) async throws -> Bool +-) ++) -> any Error +``` + +(More detailed information about the deprecations will be provided via DocC.) + +The `#expect(throws:)` overloads return an optional value that is `nil` if the +expectation failed, while the `#require(throws:)` overloads return non-optional +values and throw instances of `ExpectationFailedError` on failure (as before.) + +> [!NOTE] +> Instances of `ExpectationFailedError` thrown by `#require(throws:)` on failure +> are not returned as that would defeat the purpose of using `#require(throws:)` +> instead of `#expect(throws:)`. + +Test authors will be able to use the result of the above functions to verify +that the thrown error is correct: + +```swift +let error = try #require(throws: PotatoError.self) { + let potato = try Sack.randomPotato() + try potato.turnIntoFrenchFries() +} +#expect(error == .potatoNotPeeled) +#expect(error.variety != .russet) +``` + +The new code is more concise than the old code and avoids boilerplate casting +from `any Error`. + +## Source compatibility + +In most cases, this change does not affect source compatibility. Swift does not +allow forming references to macros at runtime, so we don't need to worry about +type mismatches assigning one to some local variable. + +We have identified two scenarios where a new warning will be emitted. + +### Inferred return type from macro invocation + +The return type of the macro may be used by the compiler to infer the return +type of an enclosing closure. If the return value is then discarded, the +compiler may emit a warning: + +```swift +func pokePotato(_ pPotato: UnsafePointer) throws { ... } + +let potato = Potato() +try await Task.sleep(for: .months(3)) +withUnsafePointer(to: potato) { pPotato in + // ^ ^ ^ ⚠️ Result of call to 'withUnsafePointer(to:_:)' is unused + #expect(throws: PotatoError.rotten) { + try pokePotato(pPotato) + } +} +``` + +This warning can be suppressed by assigning the result of the macro invocation +or the result of the function call to `_`: + +```swift +withUnsafePointer(to: potato) { pPotato in + _ = #expect(throws: PotatoError.rotten) { + try pokePotato(pPotato) + } +} +``` + +### Use of `#require(throws:)` in a generic context with `Never.self` + +If `#require(throws:)` (but not `#expect(throws:)`) is used in a generic context +where the type of thrown error is a generic parameter, and the type is resolved +to `Never`, there is no valid value for the invocation to return: + +```swift +func wrapper(throws type: E.Type, _ body: () throws -> Void) throws -> E { + return try #require(throws: type) { + try body() + } +} +let error = try #require(throws: Never.self) { ... } +``` + +We don't think this particular pattern is common (and outside of our own test +target, I'd be surprised if anybody's attempted it yet.) However, we do need to +handle it gracefully. If this pattern is encountered, Swift Testing will record +an "API Misused" issue for the current test and advise the test author to switch +to `#expect(throws:)` or to not pass `Never.self` here. + +## Integration with supporting tools + +N/A + +## Future directions + +- Adopting [typed throws](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0413-typed-throws.md) + to statically require that the error thrown from test code is of the correct + type. + + If we adopted typed throws in the signatures of these macros, it would force + adoption of typed throws in the code under test even when it may not be + appropriate. For example, if we adopted typed throws, the following code would + not compile: + + ```swift + func cook(_ food: consuming some Food) throws { ... } + + let error: PotatoError? = #expect(throws: PotatoError.self) { + var potato = Potato() + potato.fossilize() + try cook(potato) // 🛑 ERROR: Invalid conversion of thrown error type + // 'any Error' to 'PotatoError' + } + ``` + + We believe it may be possible to overload these macros or their expansions so + that the code sample above _does_ compile and behave as intended. We intend to + experiment further with this idea and potentially revisit typed throws support + in a future proposal. + +## Alternatives considered + +- Leaving the existing implementation and signatures in place. We've had + sufficient feedback about the ergonomics of this API that we want to address + the problem. + +- Having the return type of the macros be `any Error` and returning _any_ error + that was thrown even on mismatch. This would make the ergonomics of the + subsequent test code less optimal because the test author would need to cast + the error to the appropriate type before inspecting it. + + There's a philosophical argument to be made here that if a mismatched error is + thrown, then the test has already failed and is in an inconsistent state, so + we should allow the test to fail rather than return what amounts to "bad + output". + + If the test author wants to inspect any arbitrary thrown error, they can + specify `(any Error).self` instead of a concrete error type. + +## Acknowledgments + +Thanks to the team and to [@jakepetroules](https://github.com/jakepetroules) for +starting the discussion that ultimately led to this proposal. diff --git a/proposals/testing/0007-test-scoping-traits.md b/proposals/testing/0007-test-scoping-traits.md new file mode 100644 index 0000000000..d397a792e9 --- /dev/null +++ b/proposals/testing/0007-test-scoping-traits.md @@ -0,0 +1,515 @@ +# Test Scoping Traits + +* Proposal: [ST-0007](0007-test-scoping-traits.md) +* Authors: [Stuart Montgomery](https://github.com/stmontgomery) +* Status: **Implemented (Swift 6.1)** +* Implementation: [swiftlang/swift-testing#733](https://github.com/swiftlang/swift-testing/pull/733), [swiftlang/swift-testing#86](https://github.com/swiftlang/swift-testing/pull/86) +* Review: ([pitch](https://forums.swift.org/t/pitch-custom-test-execution-traits/75055)), ([review](https://forums.swift.org/t/proposal-test-scoping-traits/76676)), ([acceptance](https://forums.swift.org/t/proposal-test-scoping-traits/76676/3)) + +> [!NOTE] +> This proposal was accepted before Swift Testing began using the Swift +> evolution review process. Its original identifier was +> [SWT-0007](https://github.com/swiftlang/swift-testing/blob/main/Documentation/Proposals/0007-test-scoping-traits.md). + +### Revision history + +* **v1**: Initial pitch. +* **v2**: Dropped 'Custom' prefix from the proposed API names (although kept the + word in certain documentation passages where it clarified behavior). +* **v3**: Changed the `Trait` requirement from a property to a method which + accepts the test and/or test case, and modify its default implementations such + that custom behavior is either performed per-suite or per-test case by default. +* **v4**: Renamed the APIs to use "scope" as the base verb instead of "execute". + +## Introduction + +This introduces API which enables a `Trait`-conforming type to provide a custom +execution scope for test functions and suites, including running code before or +after them. + +## Motivation + +One of the primary motivations for the trait system in Swift Testing, as +[described in the vision document](https://github.com/swiftlang/swift-evolution/blob/main/visions/swift-testing.md#trait-extensibility), +is to provide a way to customize the behavior of tests which have things in +common. If all the tests in a given suite type need the same custom behavior, +`init` and/or `deinit` (if applicable) can be used today. But if only _some_ of +the tests in a suite need custom behavior, or tests across different levels of +the suite hierarchy need it, traits would be a good place to encapsulate common +logic since they can be applied granularly per-test or per-suite. This aspect of +the vision for traits hasn't been realized yet, though: the `Trait` protocol +does not offer a way for a trait to customize the execution of the tests or +suites it's applied to. + +Customizing a test's behavior typically means running code either before or +after it runs, or both. Consolidating common set-up and tear-down logic allows +each test function to be more succinct with less repetitive boilerplate so it +can focus on what makes it unique. + +## Proposed solution + +At a high level, this proposal entails adding API to the `Trait` protocol +allowing a conforming type to opt-in to providing a custom execution scope for a +test. We discuss how that capability should be exposed to trait types below. + +### Supporting scoped access + +There are different approaches one could take to expose hooks for a trait to +customize test behavior. To illustrate one of them, consider the following +example of a `@Test` function with a custom trait whose purpose is to set mock +API credentials for the duration of each test it's applied to: + +```swift +@Test(.mockAPICredentials) +func example() { + // ... +} + +struct MockAPICredentialsTrait: TestTrait { ... } + +extension Trait where Self == MockAPICredentialsTrait { + static var mockAPICredentials: Self { ... } +} +``` + +In this hypothetical example, the current API credentials are stored via a +static property on an `APICredentials` type which is part of the module being +tested: + +```swift +struct APICredentials { + var apiKey: String + + static var shared: Self? +} +``` + +One way that this custom trait could customize the API credentials during each +test is if the `Trait` protocol were to expose a pair of method requirements +which were then called before and after the test, respectively: + +```swift +public protocol Trait: Sendable { + // ... + func setUp() async throws + func tearDown() async throws +} + +extension Trait { + // ... + public func setUp() async throws { /* No-op */ } + public func tearDown() async throws { /* No-op */ } +} +``` + +The custom trait type could adopt these using code such as the following: + +```swift +extension MockAPICredentialsTrait { + func setUp() { + APICredentials.shared = .init(apiKey: "...") + } + + func tearDown() { + APICredentials.shared = nil + } +} +``` + +Many testing systems use this pattern, including XCTest. However, this approach +encourages the use of global mutable state such as the `APICredentials.shared` +variable, and this limits the testing library's ability to parallelize test +execution, which is +[another part of the Swift Testing vision](https://github.com/swiftlang/swift-evolution/blob/main/visions/swift-testing.md#parallelization-and-concurrency). + +The use of nonisolated static variables is generally discouraged now, and in +Swift 6 the above `APICredentials.shared` property produces an error. One way +to resolve that is to change it to a `@TaskLocal` variable, as this would be +concurrency-safe and still allow tests accessing this state to run in parallel: + +```swift +extension APICredentials { + @TaskLocal static var current: Self? +} +``` + +Binding task local values requires using the scoped access +[`TaskLocal.withValue()`](https://developer.apple.com/documentation/swift/tasklocal/withvalue(_:operation:isolation:file:line:)) +API though, and that would not be possible if `Trait` exposed separate methods +like `setUp()` and `tearDown()`. + +For these reasons, I believe it's important to expose this trait capability +using a single, scoped access-style API which accepts a closure. A simplified +version of that idea might look like this: + +```swift +public protocol Trait: Sendable { + // ... + + // Simplified example, not the actual proposal + func executeTest(_ body: @Sendable () async throws -> Void) async throws +} + +extension MockAPICredentialsTrait { + func executeTest(_ body: @Sendable () async throws -> Void) async throws { + let mockCredentials = APICredentials(apiKey: "...") + try await APICredentials.$current.withValue(mockCredentials) { + try await body() + } + } +} +``` + +### Avoiding unnecessarily lengthy backtraces + +A scoped access-style API has some potential downsides. To apply this approach +to a test function, the scoped call of a trait must wrap the invocation of that +test function, and every _other_ trait applied to that same test which offers +custom behavior _also_ must wrap the other traits' calls in a nesting fashion. +To visualize this, imagine a test function with multiple traits: + +```swift +@Test(.traitA, .traitB, .traitC) +func exampleTest() { + // ... +} +``` + +If all three of those traits provide a custom scope for tests, then each of them +needs to wrap the call to the next one, and the last trait needs to wrap the +invocation of the test, illustrated by the following: + +``` +TraitA.executeTest { + TraitB.executeTest { + TraitC.executeTest { + exampleTest() + } + } +} +``` + +Tests may have an arbitrary number of traits applied to them, including those +inherited from containing suite types. A naïve implementation in which _every_ +trait is given the opportunity to customize test behavior by calling its scoped +access API might cause unnecessarily lengthy backtraces that make debugging the +body of tests more difficult. Or worse: if the number of traits is great enough, +it could cause a stack overflow. + +In practice, most traits probably do _not_ need to provide a custom scope for +the tests they're applied to, so to mitigate these downsides it's important that +there be some way to distinguish traits which customize test behavior. That way, +the testing library can limit these scoped access calls to only traits which +need it. + +### Avoiding unnecessary (re-)execution + +Traits can be applied to either test functions or suites, and traits applied to +suites can optionally support inheritance by implementing the `isRecursive` +property of the `SuiteTrait` protocol. When a trait is directly applied to a +test function, if the trait customizes the behavior of tests it's applied to, it +should be given the opportunity to perform its custom behavior once for every +invocation of that test function. In particular, if the test function is +parameterized and runs multiple times, then the trait applied to it should +perform its custom behavior once for every invocation. This should not be +surprising to users, since it's consistent with the behavior of `init` and +`deinit` for an instance `@Test` method. + +It may be useful for certain kinds of traits to perform custom logic once for +_all_ the invocations of a parameterized test. Although this should be possible, +we believe it shouldn't be the default since it could lead to work being +repeated multiple times needlessly, or unintentional state sharing across tests, +unless the trait is implemented carefully to avoid those problems. + +When a trait conforms to `SuiteTrait` and is applied to a suite, the question of +when its custom scope (if any) should be applied is less obvious. Some suite +traits support inheritance and are recursively applied to all the test functions +they contain (including transitively, via sub-suites). Other suite traits don't +support inheritance, and only affect the specific suite they're applied to. +(It's also worth noting that a sub-suite _can_ have the same non-recursive suite +trait one of its ancestors has, as long as it's applied explicitly.) + +As a general rule of thumb, we believe most traits will either want to perform +custom logic once for _all_ children or once for _each_ child, not both. +Therefore, when it comes to suite traits, the default behavior should depend on +whether it supports inheritance: a recursive suite trait should by default +perform custom logic before each test, and a non-recursive one per-suite. But +the APIs should be flexible enough to support both, for advanced traits which +need it. + +## Detailed design + +I propose the following new APIs: + +- A new protocol `TestScoping` with a single required `provideScope(...)` method. + This will be called to provide scope for a test, and allows the conforming + type to perform custom logic before or after. +- A new method `scopeProvider(for:testCase:)` on the `Trait` protocol whose + result type is an `Optional` value of a type conforming to `TestScoping`. A + `nil` value returned by this method will skip calling the `provideScope(...)` + method. +- A default implementation of `Trait.scopeProvider(...)` which returns `nil`. +- A conditional implementation of `Trait.scopeProvider(...)` which returns `self` + in the common case where the trait type conforms to `TestScoping` itself. + +Since the `scopeProvider(...)` method's return type is optional and returns `nil` +by default, the testing library cannot invoke the `provideScope(...)` method +unless a trait customizes test behavior. This avoids the "unnecessarily lengthy +backtraces" problem above. + +Below are the proposed interfaces: + +```swift +/// A protocol that allows providing a custom execution scope for a test +/// function (and each of its cases) or a test suite by performing custom code +/// before or after it runs. +/// +/// Types conforming to this protocol may be used in conjunction with a +/// ``Trait``-conforming type by implementing the +/// ``Trait/scopeProvider(for:testCase:)-cjmg`` method, allowing custom traits +/// to provide custom scope for tests. Consolidating common set-up and tear-down +/// logic for tests which have similar needs allows each test function to be +/// more succinct with less repetitive boilerplate so it can focus on what makes +/// it unique. +public protocol TestScoping: Sendable { + /// Provide custom execution scope for a function call which is related to the + /// specified test and/or test case. + /// + /// - Parameters: + /// - test: The test under which `function` is being performed. + /// - testCase: The test case, if any, under which `function` is being + /// performed. When invoked on a suite, the value of this argument is + /// `nil`. + /// - function: The function to perform. If `test` represents a test suite, + /// this function encapsulates running all the tests in that suite. If + /// `test` represents a test function, this function is the body of that + /// test function (including all cases if it is parameterized.) + /// + /// - Throws: Whatever is thrown by `function`, or an error preventing this + /// type from providing a custom scope correctly. An error thrown from this + /// method is recorded as an issue associated with `test`. If an error is + /// thrown before `function` is called, the corresponding test will not run. + /// + /// When the testing library is preparing to run a test, it starts by finding + /// all traits applied to that test, including those inherited from containing + /// suites. It begins with inherited suite traits, sorting them + /// outermost-to-innermost, and if the test is a function, it then adds all + /// traits applied directly to that functions in the order they were applied + /// (left-to-right). It then asks each trait for its scope provider (if any) + /// by calling ``Trait/scopeProvider(for:testCase:)-cjmg``. Finally, it calls + /// this method on all non-`nil` scope providers, giving each an opportunity + /// to perform arbitrary work before or after invoking `function`. + /// + /// This method should either invoke `function` once before returning or throw + /// an error if it is unable to provide a custom scope. + /// + /// Issues recorded by this method are associated with `test`. + func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws +} + +public protocol Trait: Sendable { + // ... + + /// The type of the test scope provider for this trait. + /// + /// The default type is `Never`, which cannot be instantiated. The + /// ``scopeProvider(for:testCase:)-cjmg`` method for any trait with this + /// default type must return `nil`, meaning that trait will not provide a + /// custom scope for the tests it's applied to. + associatedtype TestScopeProvider: TestScoping = Never + + /// Get this trait's scope provider for the specified test and/or test case, + /// if any. + /// + /// - Parameters: + /// - test: The test for which a scope provider is being requested. + /// - testCase: The test case for which a scope provider is being requested, + /// if any. When `test` represents a suite, the value of this argument is + /// `nil`. + /// + /// - Returns: A value conforming to ``Trait/TestScopeProvider`` which may be + /// used to provide custom scoping for `test` and/or `testCase`, or `nil` if + /// they should not have any custom scope. + /// + /// If this trait's type conforms to ``TestScoping``, the default value + /// returned by this method depends on `test` and/or `testCase`: + /// + /// - If `test` represents a suite, this trait must conform to ``SuiteTrait``. + /// If the value of this suite trait's ``SuiteTrait/isRecursive`` property + /// is `true`, then this method returns `nil`; otherwise, it returns `self`. + /// This means that by default, a suite trait will _either_ provide its + /// custom scope once for the entire suite, or once per-test function it + /// contains. + /// - Otherwise `test` represents a test function. If `testCase` is `nil`, + /// this method returns `nil`; otherwise, it returns `self`. This means that + /// by default, a trait which is applied to or inherited by a test function + /// will provide its custom scope once for each of that function's cases. + /// + /// A trait may explicitly implement this method to further customize the + /// default behaviors above. For example, if a trait should provide custom + /// test scope both once per-suite and once per-test function in that suite, + /// it may implement the method and return a non-`nil` scope provider under + /// those conditions. + /// + /// A trait may also implement this method and return `nil` if it determines + /// that it does not need to provide a custom scope for a particular test at + /// runtime, even if the test has the trait applied. This can improve + /// performance and make diagnostics clearer by avoiding an unnecessary call + /// to ``TestScoping/provideScope(for:testCase:performing:)``. + /// + /// If this trait's type does not conform to ``TestScoping`` and its + /// associated ``Trait/TestScopeProvider`` type is the default `Never`, then + /// this method returns `nil` by default. This means that instances of this + /// trait will not provide a custom scope for tests to which they're applied. + func scopeProvider(for test: Test, testCase: Test.Case?) -> TestScopeProvider? +} + +extension Trait where Self: TestScoping { + // Returns `nil` if `testCase` is `nil`, else `self`. + public func scopeProvider(for test: Test, testCase: Test.Case?) -> Self? +} + +extension SuiteTrait where Self: TestScoping { + // If `test` is a suite, returns `nil` if `isRecursive` is `true`, else `self`. + // Otherwise, `test` is a function and this returns `nil` if `testCase` is + // `nil`, else `self`. + public func scopeProvider(for test: Test, testCase: Test.Case?) -> Self? +} + +extension Trait where TestScopeProvider == Never { + // Returns `nil`. + public func scopeProvider(for test: Test, testCase: Test.Case?) -> Never? +} + +extension Never: TestScoping {} +``` + +Here is a complete example of the usage scenario described earlier, showcasing +the proposed APIs: + +```swift +@Test(.mockAPICredentials) +func example() { + // ...validate API usage, referencing `APICredentials.current`... +} + +struct MockAPICredentialsTrait: TestTrait, TestScoping { + func provideScope(for test: Test, testCase: Test.Case?, performing function: @Sendable () async throws -> Void) async throws { + let mockCredentials = APICredentials(apiKey: "...") + try await APICredentials.$current.withValue(mockCredentials) { + try await function() + } + } +} + +extension Trait where Self == MockAPICredentialsTrait { + static var mockAPICredentials: Self { + Self() + } +} +``` + +## Source compatibility + +The proposed APIs are purely additive. + +This proposal will replace the existing `CustomExecutionTrait` SPI, and after +further refactoring we anticipate it will obsolete the need for the +`SPIAwareTrait` SPI as well. + +## Integration with supporting tools + +Although some built-in traits are relevant to supporting tools (such as +SourceKit-LSP statically discovering `.tags` traits), custom test behaviors are +only relevant within the test executable process while tests are running. We +don't anticipate any particular need for this feature to integrate with +supporting tools. + +## Future directions + +### Access to suite type instances + +Some test authors have expressed interest in allowing custom traits to access +the instance of a suite type for `@Test` instance methods, so the trait could +inspect or mutate the instance. Currently, only instance-level members of a +suite type (including `init`, `deinit`, and the test function itself) can access +`self`, so this would grant traits applied to an instance test method access to +the instance as well. This is certainly interesting, but poses several technical +challenges that puts it out of scope of this proposal. + +### Convenience trait for setting task locals + +Some reviewers of this proposal pointed out that the hypothetical usage example +shown earlier involving setting a task local value while a test is executing +will likely become a common use of these APIs. To streamline that pattern, it +would be very natural to add a built-in trait type which facilitates this. I +have prototyped this idea and plan to add it once this new trait functionality +lands. + +## Alternatives considered + +### Separate set up & tear down methods on `Trait` + +This idea was discussed in [Supporting scoped access](#supporting-scoped-access) +above, and as mentioned there, the primary problem with this approach is that it +cannot be used with scoped access-style APIs, including (importantly) +`TaskLocal.withValue()`. For that reason, it prevents using that common Swift +concurrency technique and reduces the potential for test parallelization. + +### Add `provideScope(...)` directly to the `Trait` protocol + +The proposed `provideScope(...)` method could be added as a requirement of the +`Trait` protocol instead of being part of a separate `TestScoping` protocol, and +it could have a default implementation which directly invokes the passed-in +closure. But this approach would suffer from the lengthy backtrace problem +described above. + +### Extend the `Trait` protocol + +The original, experimental implementation of this feature included a protocol +named`CustomExecutionTrait` which extended `Trait` and had roughly the same +method requirement as the `TestScoping` protocol proposed above. This design +worked, provided scoped access, and avoided the lengthy backtrace problem. + +After evaluating the design and usage of this SPI though, it seemed unfortunate +to structure it as a sub-protocol of `Trait` because it means that the full +capabilities of the trait system are spread across multiple protocols. In the +proposed design, the ability to return a test scoping provider is exposed via +the main `Trait` protocol, and it relies on an associated type to conditionally +opt-in to custom test behavior. In other words, the proposed design expresses +custom test behavior as just a _capability_ that a trait may have, rather than a +distinct sub-type of trait. + +Also, the implementation of this approach within the testing library was not +ideal as it required a conditional `trait as? CustomExecutionTrait` downcast at +runtime, in contrast to the simpler and more performant Optional property of the +proposed API. + +### API names + +We first considered "execute" as the base verb for the proposed new concept, but +felt this wasn't appropriate since these trait types are not "the executor" of +tests, they merely customize behavior and provide scope(s) for tests to run +within. Also, the term "executor" has prior art in Swift Concurrency, and +although that word is used in other contexts too, it may be helpful to avoid +potential confusion with concurrency executors. + +We also considered "run" as the base verb for the proposed new concept instead +of "execute", which would imply the names `TestRunning`, `TestRunner`, +`runner(for:testCase)`, and `run(_:for:testCase:)`. The word "run" is used in +many other contexts related to testing though, such as the `Runner` SPI type and +more casually to refer to a run which occurred of a test, in the past tense, so +overloading this term again may cause confusion. + +## Acknowledgments + +Thanks to [Dennis Weissmann](https://github.com/dennisweissmann) for originally +implementing this as SPI, and for helping promote its usefulness. + +Thanks to [Jonathan Grynspan](https://github.com/grynspan) for exploring ideas +to refine the API, and considering alternatives to avoid unnecessarily long +backtraces. + +Thanks to [Brandon Williams](https://github.com/mbrandonw) for feedback on the +Forum pitch thread which ultimately led to the refinements described in the +"Avoiding unnecessary (re-)execution" section. From 22bfdc28b73523c89145705d3de46f3cd4ddaa9c Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 4 Mar 2025 10:50:05 -0500 Subject: [PATCH 4119/4563] Add alternative considered: parameter packs --- proposals/testing/NNNN-exit-tests.md | 48 ++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/proposals/testing/NNNN-exit-tests.md b/proposals/testing/NNNN-exit-tests.md index c822fd90fb..29d2ac0775 100644 --- a/proposals/testing/NNNN-exit-tests.md +++ b/proposals/testing/NNNN-exit-tests.md @@ -246,7 +246,7 @@ describes how the child process is expected to have exited: standard, but not to the degree that signals are supported by POSIX-like or UNIX-derived operating systems. Swift Testing makes a "best effort" to emulate signal-handling support on Windows. See [this](https://forums.swift.org/t/swift-on-windows-question-about-signals-and-exceptions/76640/2) - Swift forum message for more information. + Swift forum message for more information. The type is declared as: @@ -287,7 +287,7 @@ extension ExitTest { public static var failure: Self { get } public init(_ statusAtExit: StatusAtExit) - + /// Creates a condition that matches when a process terminates with a given /// exit code. /// @@ -311,7 +311,7 @@ extension ExitTest { /// systems may only reliably report the low unsigned 8 bits (0–255) of /// the exit code. public static func exitCode(_ exitCode: CInt) -> Self - + /// Creates a condition that matches when a process terminates with a given /// signal. /// @@ -624,7 +624,7 @@ and control returns to the test function that is awaiting the exit test: ```swift await #expect(exitsWith: .failure) { - throw TacoError.noTacosFound + throw TacoError.noTacosFound } ``` @@ -737,7 +737,7 @@ let result = try await #require(process, exitsWith: .success) case signal(CInt) } ``` - + This simplified the set of types used for exit tests, but made comparing two exit conditions complicated and necessitated a `==` operator that did not satisfy the requirements of the `Equatable` protocol. @@ -756,7 +756,7 @@ let result = try await #require(process, exitsWith: .success) I settled on `StatusAtExit` because it was distinct and makes it clear that it represents the status of a process _at exit time_. `ExitStatus` could be interpreted as the status of the exit itself, i.e.: - + ```swift enum ExitStatus { case running @@ -766,6 +766,42 @@ let result = try await #require(process, exitsWith: .success) } ``` +- Using parameter packs to specify observed values and return types: + + ```swift + @freestanding(expression) public macro require( + exitsWith expectedExitCondition: ExitTest.Condition, + observing observedValues: (repeat (KeyPath)) = (), + _ comment: @autoclosure () -> Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation, + performing expression: @escaping @Sendable @convention(thin) () async throws -> Void + ) -> (repeat each T) + ``` + + Using a parameter pack in this way would make it impossible to access + properties of the returned `ExitTest.Result` value that weren't observed, and + in general would mean developers wouldn't even need to use `ExitTest.Result`: + + ```swift + let (status, stderr) = try await #expect( + exitsWith: .failure, + observing: (\.statusAtExit, \.standardErrorContent) + ) { ... } + #expect(status == ...) + #expect(stderr.contains(...)) + ``` + + Unfortunately, the `#expect(exitsWith:)` and `#require(exitsWith:)` macros do + not have enough information at compile time to correctly infer the types of + the key paths passed as `observedValues` above, so we end up with rather + obscure errors: + + > 🛑 Cannot convert value of type 'KeyPath<_, _>' to expected argument type + > 'KeyPath' + + If, in the future, this error is resolved, we may wish to revisit this option, + so it can also be considered a "future direction" for the feature. + - Changing the implementation of `precondition()`, `fatalError()`, etc. in the standard library so that they do not terminate the current process while testing, thus removing the need to spawn a child process for an exit test. From 45b3f89a19db3f91db192288473e1ff1cdda64bc Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Wed, 5 Mar 2025 00:43:17 +0000 Subject: [PATCH 4120/4563] add documentation link --- proposals/NNNN-environment-dependent-shared-libraries.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-environment-dependent-shared-libraries.md b/proposals/NNNN-environment-dependent-shared-libraries.md index e7a0569ab5..6a6cdbe8e9 100644 --- a/proposals/NNNN-environment-dependent-shared-libraries.md +++ b/proposals/NNNN-environment-dependent-shared-libraries.md @@ -4,6 +4,7 @@ * Authors: [tayloraswift](https://github.com/tayloraswift) * Review Manager: TBD * Implementation: [swiftlang/swift-package-manager#8249](https://github.com/swiftlang/swift-package-manager/pull/8249) +* Documentation: [How to use Environment-Dependent Shared Libraries](https://github.com/tayloraswift/swift-edsl-example-client/blob/master/Sources/KrustyKrab/docs.docc/Getting%20Started.md) * Bugs: [SR-5714](https://github.com/swiftlang/swift-package-manager/issues/5714) ## Introduction @@ -12,9 +13,9 @@ SwiftPM currently has no support for non-system binary library dependencies on L Swift-evolution thread: [Discussion thread](https://forums.swift.org/t/pitch-replaceable-library-plugins/77605) -Example Producer: [swift-rlp-example](https://github.com/tayloraswift/swift-rlp-example) +Example Producer: [swift-edsl-example](https://github.com/tayloraswift/swift-edsl-example) -Example Consumer: [swift-rlp-example-client](https://github.com/tayloraswift/swift-rlp-example-client) +Example Consumer: [swift-edsl-example-client](https://github.com/tayloraswift/swift-edsl-example-client) ## Motivation From 5d58ff928d6ef07fe1ad6906abd3cc3d7af8ea01 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Tue, 4 Mar 2025 17:08:24 -0800 Subject: [PATCH 4121/4563] Proposal to add epoch properties to SuspendingClock and ContinuousClock --- proposals/NNNN-ClockEpochs.md | 58 +++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 proposals/NNNN-ClockEpochs.md diff --git a/proposals/NNNN-ClockEpochs.md b/proposals/NNNN-ClockEpochs.md new file mode 100644 index 0000000000..88d77fe96c --- /dev/null +++ b/proposals/NNNN-ClockEpochs.md @@ -0,0 +1,58 @@ +# Clock Epochs + +* Proposal: [SE-NNNN](NNNN-ClockEpochs.md) +* Authors: [Philippe Hausler](https://github.com/phausler) +* Review Manager: TBD +* Status: **Awaiting implementation** +* Implementation: + +* Previous Proposal: *if applicable* [SE-0329](0329-clock-instant-duration.md) +* Review: ([pitch](https://forums.swift.org/t/pitch-suspendingclock-and-continuousclock-epochs/78017)) + +## Introduction + +[The proposal for Clock, Instant and Duration](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0329-clock-instant-duration.md) brought in two primary clock types: `SuspendingClock` and `ContinuousClock`. These both have a concept of a reference point for their `Instant` types. + +## Motivation + +Not all clocks have a starting point, however in these cases they do. Generally, it cannot required for a clock's instant definition to have a start or the start may not be a fixed point. However it can be useful that a given instant can be constructed to determine the elapsed duration from the starting point of that clock if it does have it. + +## Proposed solution + +Two new properties will be added, one to `SuspendingClock` and another to `ContinuousClock`. Both of these properties will be the epoch for which all `Instant` types are derived from; practically speaking this is the "zero" point for these clocks. + +## Detailed design + +```swift +extension ContinousClock { + public var epoch: Instant { get } +} + +extension SuspendingClock { + public var epoch: Instant { get } +} +``` + +These can be used to gather information like for example the uptime of a system, or the active time of a system; + +```swift +let clock = ContinousClock() +let uptime = clock.now - clock.epoch +``` + +Or likewise; + +```swift +let clock = SuspendingClock() +let activeTime = clock.now - clock.epoch +``` + +## ABI compatibility + +This is a purely additive change and provides no direct impact to existing ABI. It only carries the ABI impact of new properties being added to an existing type. + +## Alternatives considered + +It was considered to add a constructor or static member to the `SuspendingClock.Instant` and `ContinousClock.Instant` however the home on the clock itself provides a more discoverable and nameable location. + +It is suggested that this be used as an informal protocol for other clocks. It was considered as an additional protocol but that was ultimately rejected because no generic function made much sense that would not be better served with generic specialization or explicit clock parameter types. \ No newline at end of file From b85a6b32fce631e13a62bc8c8ccb132b7f9e9845 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 5 Mar 2025 19:20:37 +0900 Subject: [PATCH 4122/4563] Update proposals/NNNN-SerialExecutor-isIsolated.md Co-authored-by: TTOzzi --- proposals/NNNN-SerialExecutor-isIsolated.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-SerialExecutor-isIsolated.md b/proposals/NNNN-SerialExecutor-isIsolated.md index e60b53a5ae..d73787dffd 100644 --- a/proposals/NNNN-SerialExecutor-isIsolated.md +++ b/proposals/NNNN-SerialExecutor-isIsolated.md @@ -8,7 +8,7 @@ ## Introduction -In [SE-0424: Custom isolation checking for SerialExecutor](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0424-custom-isolation-checking-for-serialexecutor.md) we introduced a way for custom executors implementing the `SerialExecutor` protocol to assert and and assume the static isolation if the dynamic check succeeded. This proposal extends these capabilities, allowing custom executors to not only "check and crash if assumption was wrong", but also check and act on the result of the check. +In [SE-0424: Custom isolation checking for SerialExecutor](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0424-custom-isolation-checking-for-serialexecutor.md) we introduced a way for custom executors implementing the `SerialExecutor` protocol to assert and assume the static isolation if the dynamic check succeeded. This proposal extends these capabilities, allowing custom executors to not only "check and crash if assumption was wrong", but also check and act on the result of the check. ## Motivation From 87d86ad19273f88f3a14251d3f299371e9213e04 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 5 Mar 2025 19:21:00 +0900 Subject: [PATCH 4123/4563] Apply suggestions from code review Co-authored-by: TTOzzi --- proposals/NNNN-SerialExecutor-isIsolated.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/NNNN-SerialExecutor-isIsolated.md b/proposals/NNNN-SerialExecutor-isIsolated.md index d73787dffd..98ad33ea3b 100644 --- a/proposals/NNNN-SerialExecutor-isIsolated.md +++ b/proposals/NNNN-SerialExecutor-isIsolated.md @@ -57,7 +57,7 @@ This approach is better than not being able to participate in the checks at all - the crash **messages** offered by these `checkIsolated()` crashes **are often sub-optimal** and confusing - messages often don't include crucial information about which actor/executor the calling context was _actually_ executing on. Offering only "expected [...]" messages, leading to hard to debug crashes. - it is **impossible** for the Swift runtime to offer **isolation violation warnings** - - because the Swift runtime _must_ call into a custom executor to verify its isolation, the "pass or crash" method will crash, rather than inform the runtime that a violation ocured and we should warn about it. + - because the Swift runtime _must_ call into a custom executor to verify its isolation, the "pass or crash" method will crash, rather than inform the runtime that a violation occurred and we should warn about it. Today, it is not possible for the Swift runtime to issue _warnings_ if something is detected to be on not the expected executor, but somehow we'd still like to continue without crashing the application. @@ -90,9 +90,9 @@ extension SerialExecutor { } ``` -The Swift runtime is free to call the `isIsolated` function whenever it wants to verify if the current context is apropriately isolated by some serial executor. +The Swift runtime is free to call the `isIsolated` function whenever it wants to verify if the current context is appropriately isolated by some serial executor. -In most cases implementing this new API is preferable to implementing `checkIsolated()`, as the Swift runtime is able to offer more detailed error messages when when an isolation failure detected by a call to `isIsolatingCurrentContext()` is detected. +In most cases implementing this new API is preferable to implementing `checkIsolated()`, as the Swift runtime is able to offer more detailed error messages when an isolation failure detected by a call to `isIsolatingCurrentContext()` is detected. ## Detailed design From 911ad36dfaf2c23b98288caaa11cd9deb1c1478f Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 5 Mar 2025 19:21:41 +0900 Subject: [PATCH 4124/4563] Link to partial implementation --- proposals/NNNN-SerialExecutor-isIsolated.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-SerialExecutor-isIsolated.md b/proposals/NNNN-SerialExecutor-isIsolated.md index 98ad33ea3b..d5db0285fe 100644 --- a/proposals/NNNN-SerialExecutor-isIsolated.md +++ b/proposals/NNNN-SerialExecutor-isIsolated.md @@ -3,7 +3,8 @@ * Proposal: [SE-NNNN](...) * Author: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: -* Status: **WIP** +* Status: **Partial implementation** +* Implementation: https://github.com/swiftlang/swift/pull/79788 * Review: TODO ## Introduction From 5accdb5b143b0bf089fe647f0beee6ce95c849e4 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 5 Mar 2025 13:40:52 -0500 Subject: [PATCH 4125/4563] Assign SE-0464. --- ...processing.md => 0464-utf8span-safe-utf8-processing.md} | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename proposals/{nnnn-utf8span-safe-utf8-processing.md => 0464-utf8span-safe-utf8-processing.md} (99%) diff --git a/proposals/nnnn-utf8span-safe-utf8-processing.md b/proposals/0464-utf8span-safe-utf8-processing.md similarity index 99% rename from proposals/nnnn-utf8span-safe-utf8-processing.md rename to proposals/0464-utf8span-safe-utf8-processing.md index 26308fcadf..4576916bbe 100644 --- a/proposals/nnnn-utf8span-safe-utf8-processing.md +++ b/proposals/0464-utf8span-safe-utf8-processing.md @@ -1,12 +1,11 @@ # UTF8Span: Safe UTF-8 Processing Over Contiguous Bytes -* Proposal: [SE-NNNN](nnnn-utf8-span.md) +* Proposal: [SE-0464](0464-utf8-span.md) * Authors: [Michael Ilseman](https://github.com/milseman), [Guillaume Lessard](https://github.com/glessard) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Tony Allevato](https://github.com/allevato) +* Status: **Active review (March 5–19, 2025)** * Bug: rdar://48132971, rdar://96837923 * Implementation: https://github.com/swiftlang/swift/pull/78531 -* Upcoming Feature Flag: * Review: ([pitch 1](https://forums.swift.org/t/pitch-utf-8-processing-over-unsafe-contiguous-bytes/69715)) ([pitch 2](https://forums.swift.org/t/pitch-safe-utf-8-processing-over-contiguous-bytes/72742)) From 80decd57475c3c4f136507931feb173d7e57c428 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 5 Mar 2025 13:43:16 -0500 Subject: [PATCH 4126/4563] SE-0464: Link to active review thread. --- proposals/0464-utf8span-safe-utf8-processing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0464-utf8span-safe-utf8-processing.md b/proposals/0464-utf8span-safe-utf8-processing.md index 4576916bbe..cb9cb817fd 100644 --- a/proposals/0464-utf8span-safe-utf8-processing.md +++ b/proposals/0464-utf8span-safe-utf8-processing.md @@ -6,7 +6,7 @@ * Status: **Active review (March 5–19, 2025)** * Bug: rdar://48132971, rdar://96837923 * Implementation: https://github.com/swiftlang/swift/pull/78531 -* Review: ([pitch 1](https://forums.swift.org/t/pitch-utf-8-processing-over-unsafe-contiguous-bytes/69715)) ([pitch 2](https://forums.swift.org/t/pitch-safe-utf-8-processing-over-contiguous-bytes/72742)) +* Review: ([pitch 1](https://forums.swift.org/t/pitch-utf-8-processing-over-unsafe-contiguous-bytes/69715)) ([pitch 2](https://forums.swift.org/t/pitch-safe-utf-8-processing-over-contiguous-bytes/72742)) ([review](https://forums.swift.org/t/se-0464-utf8span-safe-utf-8-processing-over-contiguous-bytes/78307)) ## Introduction From 6af0259937cd87067c795b165f2951d7a525a9a7 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 5 Mar 2025 11:21:43 -0800 Subject: [PATCH 4127/4563] Nonescapable stdlib primitives (#2657) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial version of the pitch text * Proofreading edits * Resolve FIXMEs that I left in by accident * Add link to forum pitch; update TOC * Dedupe sections on lifetime management * Remove unsafeBitCast generalization for now The proposed variant is much too limited, and it also introduces a new axis of unsafety that may better be expressed with dedicated API. We do not see any _immediate_ need for bit casting between escapable/nonescapable types, so it seems we can safely defer this work. * Reword section on ObjectIdentifier; add note on ==/!= operators over metatypes * Add metatype comparisons; take back Optional.unsafelyUnwrapped; minor edits - Add generalizations for `==`/`!=` over existential metatypes - Update ObjectIdentifier.init to use the right definition now that it works - Take out the generalization of `Optional.unsafelyUnwrapped` due to issues with C++ interop. (I expect we’ll fix this in the next proposal in this series.) - Add explicit (illustrative) attributes to describe the lifetime semantics of generalized API. - General copy editing * One more round of updates - Correct number of SE-0437 (!) - Provisionally reintroduce `Optional.unsafelyUnwrapped`. (We trust the interop issue can be fixed soon.) - Add explicit sections in Detailed Design describing inferred lifetime semantics for enum operations - Reword the introduction of infinite lifetimes - More copy editing --- .../NNNN-nonescapable-stdlib-primitives.md | 892 ++++++++++++++++++ 1 file changed, 892 insertions(+) create mode 100644 proposals/NNNN-nonescapable-stdlib-primitives.md diff --git a/proposals/NNNN-nonescapable-stdlib-primitives.md b/proposals/NNNN-nonescapable-stdlib-primitives.md new file mode 100644 index 0000000000..788faef5cf --- /dev/null +++ b/proposals/NNNN-nonescapable-stdlib-primitives.md @@ -0,0 +1,892 @@ +# Standard Library Primitives for Nonescapable Types + +* Proposal: [SE-NNNN](NNNN-nonescapable-stdlib-primitives.md) +* Authors: [Karoy Lorentey](https://github.com/lorentey) +* Review Manager: TBD +* Status: **Awaiting review** +* Roadmap: [Improving Swift performance predictability: ARC improvements and ownership control][Roadmap] +* Implementation: https://github.com/swiftlang/swift/pull/73258 +* Review: ([Pitch]) + +[Roadmap]: https://forums.swift.org/t/a-roadmap-for-improving-swift-performance-predictability-arc-improvements-and-ownership-control/54206 +[Pitch]: https://forums.swift.org/t/pitch-nonescapable-standard-library-primitives/77253 + + + +Related proposals: + +- [SE-0377] `borrowing` and `consuming` parameter ownership modifiers +- [SE-0390] Noncopyable structs and enums +- [SE-0426] `BitwiseCopyable` +- [SE-0427] Noncopyable generics +- [SE-0429] Partial consumption of noncopyable values +- [SE-0432] Borrowing and consuming pattern matching for noncopyable types +- [SE-0437] Noncopyable Standard Library Primitives +- [SE-0446] Nonescapable Types +- [SE-0447] `Span`: Safe Access to Contiguous Storage +- [SE-0452] Integer Generic Parameters +- [SE-0453] `InlineArray`, a fixed-size array +- [SE-0456] Add `Span`-providing Properties to Standard Library Types + + + +[SE-0370]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0370-pointer-family-initialization-improvements.md +[SE-0377]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0377-parameter-ownership-modifiers.md +[SE-0390]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0390-noncopyable-structs-and-enums.md +[SE-0426]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0426-bitwise-copyable.md +[SE-0427]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0427-noncopyable-generics.md +[SE-0429]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0429-partial-consumption.md +[SE-0432]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0432-noncopyable-switch.md +[SE-0437]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0437-noncopyable-stdlib-primitives.md +[SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md +[SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md +[SE-0452]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0452-integer-generic-parameters.md +[SE-0453]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0453-vector.md +[SE-0456]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0456-stdlib-span-properties.md + +### Table of Contents + + * [Introduction](#introduction) + * [Motivation](#motivation) + * [Proposed Solution](#proposed-solution) + * [Nonescapable optionals](#nonescapable-optionals) + * [Nonescapable Result](#nonescapable-result) + * [Retrieving the memory layout of nonescapable types](#retrieving-the-memory-layout-of-nonescapable-types) + * [Lifetime management](#lifetime-management) + * [Metatype comparisons](#metatype-comparisons) + * [Object identifiers](#object-identifiers) + * [Odds and ends](#odds-and-ends) + * [Detailed Design](#detailed-design) + * [Inferred lifetime behavior of nonescapable enum types](#inferred-lifetime-behavior-of-nonescapable-enum-types) + * [Inferred lifetime behavior of Optional's notational conveniences](#inferred-lifetime-behavior-of-optionals-notational-conveniences) + * [protocol ExpressibleByNilLiteral](#protocol-expressiblebynilliteral) + * [enum Optional](#enum-optional) + * [enum Result](#enum-result) + * [enum MemoryLayout](#enum-memorylayout) + * [Lifetime Management](#lifetime-management-1) + * [Metatype equality](#metatype-equality) + * [struct ObjectIdentifier](#struct-objectidentifier) + * [ManagedBufferPointer equatability](#managedbufferpointer-equatability) + * [Making indices universally available on unsafe buffer pointers](#making-indices-universally-available-on-unsafe-buffer-pointers) + * [Buffer pointer operations on Slice](#buffer-pointer-operations-on-slice) + * [Source compatibility](#source-compatibility) + * [ABI compatibility](#abi-compatibility) + * [Note on protocol generalizations](#note-on-protocol-generalizations) + * [Alternatives Considered](#alternatives-considered) + * [Future Work](#future-work) + * [Acknowledgements](#acknowledgements) + +## Introduction + +This document proposes to allow `Optional` and `Result` to hold instances of nonescapable types, and continues the work of adding support for noncopyable and nonescapable types throughout the Swift Standard Library. + + +## Motivation + +[SE-0437] started integrating noncopyable types into our Standard Library abstractions, by generalizing existing APIs and introducing new ones. In the time since that proposal, [SE-0446] has introduced nonescapable types to Swift, adding a new direction of generalization. + +This proposal continues the work of [SE-0437] by extending some basic constructs to support nonescapable types, where it is already possible to do so. For now, we are focusing on further generalizing a subset of the constructs covered by [SE-0437]: `MemoryLayout`, `Optional`, and `Result` types. Our immediate aim is to unblock the use of nonescapable types, especially in API surfaces. We also smooth out some minor inconsistencies that [SE-0437] has left unresolved. + +Like before, our aim is to implement these generalizations with as little disruption as possible. Existing code implicitly assumes copyability and escapability, and it needs to continue working as before. + +## Proposed Solution + +This proposal is focusing on achieving the following results: + +- Allow `Optional` to wrap nonescapable types, itself becoming conditionally escapable. +- Do the same for `Result`, allowing its success case to hold a nonescapable item. +- Generalize `MemoryLayout` to allow querying basic information on the memory layout of nonescapable types. +- Continue generalizing basic lifetime management functions; introduce a new `extendLifetime()` function that avoids a closure argument. +- Allow generating `ObjectIdentifier` instances for noncopyable and/or nonescapable metatypes. +- Allow comparing noncopyable and nonescapable metatypes for equality. + +We also propose to fix a handful of minor copyability-related omissions that have been discovered since [SE-0437] was accepted: + +- Allow `ManagedBufferPointer` instances to be equatable even when `Element` is noncopyable. +- Make the `Unsafe[Mutable]BufferPointer.indices` property universally available for all `Element` types. +- Generalize select operations on unsafe buffer pointer slices, to restore consistency with the same operations on the buffer pointers themselves. + +### Nonescapable optionals + +We want `Optional` to support wrapping all Swift types, whether or not they're copyable or escapable. This means that `Optional` needs to become conditionally escapable, depending on the escapability of its `Wrapped` type. + +`Optional` must itself become nonescapable when it is wrapping a nonescapable type, and such optional values need to be subject to precisely the same lifetime constraints as their wrapped item: we cannot allow the act of wrapping a nonescapable value in an optional to allow that value to escape its intended context. + +There are many ways to construct optional values in Swift: for example, we can explicitly invoke the factory for the `.some` case, we can rely on Swift's implicit optional promotion rules, or we can invoke the default initializer. We propose to generalize all of these basic/primitive mechanisms to support nonescapable use. For instance, given a non-optional `Span` value, this code exercises these three basic ways of constructing non-nil nonescapable optionals: + +```swift +func sample(_ span: Span) { + let a = Optional.some(span) // OK, explicit case factory + let b: Optional = span // OK, implicit optional promotion + let c = Optional(span) // OK, explicit initializer invocation +} +``` + +`a`, `b`, and `c` hold the same span instance, and their lifetimes are subject to the same constraints as the original span -- they can be used within the context of the `sample` function, but they cannot escape outside of it. (At least not without explicit lifetime dependency annotations, to be introduced in the future.) + +Of course, it also needs to be possible to make empty `Optional` values that do not hold anything. We have three basic ways to do that: we can explicitly invoke the factory for the `none` case, we can reach for the special `nil` literal, or (for `var`s) we can rely on implicit optional initialization. This proposal generalizes all three mechanisms to support noncopyable wrapped types: + +```swift +func sample(_ span: Span) { + var d: Span? = .none // OK, explicit factory invocation + var e: Span? = nil // OK, nil literal expression + var f: Span? // OK, implicit nil default value +} +``` + +Empty optionals of nonescapable types are still technically nonescapable, but they aren't inherently constrained to any particular context -- empty optionals are born with "immortal" (or "static") lifetimes, i.e., they have no lifetime dependencies, and so they are allowed to stay around for the entire execution of a Swift program. Nil optionals can be passed to any operation that takes a nonescapable optional, no matter what expectations it may dictate about its lifetime dependencies; they can also be returned from any function that returns a nonescapable optional. (Note though that Swift does not yet provide a stable way to define such functions.) + +Of course, we also expect to be able to reassign variables, rebinding them to a new value. Reassignments of local variables are allowed to arbitrarily change lifetime dependencies. There is no expectation that the lifetime dependencies of the new value have any particular relation to the old: local variable reassignments can freely "narrow" or "widen" dependencies, as they see fit. + +For instance, the code below initializes an optional variable to an immortal nil value; it then assigns it a new value that has definite lifetime constraints; and finally it turns it back to an immortal nil value: + +```swift +func sample(_ span: Span) { + var maybe: Span? = nil // immortal + maybe = span // definite lifetime + maybe = nil // immortal again +} +``` + +(Assigning `span` to `maybe` is not an escape, as the local variable will be destroyed before the function returns, even without the subsequent reassignment.) + +This flexibility will not necessarily apply to other kinds of variables, like stored properties in custom nonescapable structs, global variables, or computed properties -- I expect those variables to carry specific lifetime dependencies that cannot vary through reassignment. (For example, a global variable of a nonescapable type may be required to hold immortal values only.) However, for now, we're limiting our reasoning to local variables. + +Of course, an optional is of limited use unless we are able to decide whether it contains a value, and (if so) to unwrap it and look at its contents. We need to be able to operate on nonescapable optionals using the familiar basic mechanisms: + + - `switch` and `if case`/`guard case` statements that pattern match over them: + + ```swift + // Variant 1: Full pattern matching + func count(of maybeSpan: Span?) -> Int { + switch maybeSpan { + case .none: return 0 + case .some(let span): return span.count + } + } + + // Variant 2: Pattern matching with optional sugar + func count(of maybeSpan: Span?) -> Int { + switch maybeSpan { + case nil: return 0 + case let span?: return span.count + } + } + ``` + + - The force-unwrapping `!` special form, and its unsafe cousin, the Standard Library's `unsafelyUnwrapped` property. + + ```swift + func count(of maybeSpan: Span?) -> Int { + if case .none = maybeSpan { return 0 } + return maybeSpan!.count + } + ``` + +- The optional chaining special form `?`: + + ```swift + func count(of maybeSpan: Span?) -> Int { + guard let c = maybeSpan?.count else { return 0 } + return c + } + ``` + +- Optional bindings such as `if let` or `guard let` statements: + + ```swift + func count(of maybeSpan: Span?) -> Int { + guard let span = maybeSpan else { return 0 } + return span.count + } + ``` + +These variants all work as expected. To avoid escapability violations, unwrapping the nonescapable optional results in a value with precisely the same lifetime dependencies as the original optional value. This applies to all forms of unwrapping, including pattern matching forms that bind copies of associated values to new variables, like `let span` above -- the resulting `span` value always has the same lifetime as the optional it comes from. + +The standard `Optional` type has custom support for comparing optional instances against `nil` using the traditional `==` operator, whether or not the wrapped type conforms to `Equatable`. [SE-0437] generalized this mechanism for noncopyable wrapped types, and it is reasonable to extend this to also cover the nonescapable case: + +```swift +func count(of maybeSpan: Span?) -> Int { + if maybeSpan == nil { return 0 } // OK! + return maybeSpan!.count +} +``` + +This core set of functionality makes nonescapable optionals usable, but it does not yet enable the use of more advanced APIs. Eventually, we'd also like to use the standard `Optional.map` function (and similar higher-order functions) to operate on (or to return) nonescapable optional types, as in the example below: + +```swift +func sample(_ maybeArray: Array?) { + // Assuming `Array.storage` returns a nonescapable `Span`: + let maybeSpan = maybeArray.map { $0.storage } + ... +} +``` + +These operations require precise reasoning about lifetime dependencies though, so they have to wait until we have a stable way to express lifetime annotations on their definitions. We expect lifetime semantics to become an integral part of the signatures of functions dealing with nonescapable entities -- for the simplest cases they can often remain implicit, but for something like `map` above, we'll need to explicitly describe how the lifetime of the function's result relates to the lifetime of the result of the function argument. We need to defer this work until we have the means to express such annotations in the language. + +One related omission from the list of generalizations above is the standard nil-coalescing operator `??`. This is currently defined as follows (along with another variant that returns an `Optional`): + +```swift +func ?? ( + optional: consuming T?, + defaultValue: @autoclosure () throws -> T +) rethrows -> T +``` + +To generalize this to also allow nonescapable `T` types, we'll need to specify that the returned value's lifetime is tied to the _intersection_ of the lifetime of the left argument and the lifetime of the result of the right argument (a function). We aren't currently able to express that, so this generalization has to be deferred as well until the advent of such a language feature. + +### Nonescapable `Result` + +We generalize `Result` along the same lines as `Optional`, allowing its `success` case to wrap a nonescapable value. For now, we need to mostly rely on Swift's general enum facilities to operate on nonescapable `Result` values: switch statements, case factories, pattern matching, associated value bindings etc. + +Important convenience APIs such as `Result.init(catching:)` or `Result.map` will need to require escapability until we introduce a way to formally specify lifetime dependencies. This is unfortunate, but it still enables intrepid Swift developers to experiment with defining interfaces that take (or perhaps even return!) `Result` values. + +However, we are already able to generalize a small handful of methods: `get` and the two error-mapping utilities, `mapError` and `flatMapError`. + +```swift +func sample(_ res: Result, E>) -> Int { + guard let span = try? res.get() else { return 42 } + return 3 * span.count + 9 +} +``` + +Like unwrapping an `Optional`, calling `get()` on a nonescapable `Result` returns a value whose lifetime requirements exactly match that of the original `Result` instance -- the act of unwrapping a result cannot allow its content to escape its intended context. + +### Retrieving the memory layout of nonescapable types + +This proposal generalizes `enum MemoryLayout` to support retrieving information about the layout of nonescapable types: + +```swift +print(MemoryLayout>.size) // ⟹ 16 +print(MemoryLayout>.stride) // ⟹ 16 +print(MemoryLayout>.alignment) // ⟹ 8 +``` + +(Of course, the values returned will vary depending on the target architecture.) + +The information returned is going to be of somewhat limited use until we generalize unsafe pointer types to support nonescapable pointees, which this proposal does not include -- but there is no reason to delay this work until then. + +To usefully allow pointers to nonescapable types, we'll need to assign precise lifetime semantics to their `pointee` (and pointer dereferencing in general), and we'll most likely also need a way to allow developers to unsafely override the resulting default lifetime semantics. This requires explicit lifetime annotations, and as such, that work is postponed to a future proposal. + +### Lifetime management + +We once again generalize the `withExtendedLifetime` family of functions, this time to support calling them on nonescapable values. + +```swift +let span = someArray.storage +withExtendedLifetime(span) { span in + // `someArray` is being actively borrowed while this closure is running +} +// At this point, `someArray` may be ready to be mutated +``` + +We've now run proposals to generalize `withExtendedLifetime` for (1) typed throws, (2) noncopyable inputs and results, and (3) nonescapable inputs. It is getting unwieldy to keep having to tweak these APIs, especially since in actual practice, `withExtendedLifetime` is most often called with an empty closure, to serve as a sort of fence protecting against early destruction. The closure-based design of these interfaces are no longer fitting the real-life practices of Swift developers. These functions were originally designed to be used with a non-empty closure, like in the example below: + +```swift +withExtendedLifetime(obj) { + weak var ref = obj + foo(ref!) +} +``` + +In most cases, the formulation we actually recommend these days is to use a defer statement, with the function getting passed an empty closure: + +```swift +weak var ref = obj +defer { withExtendedLifetime(obj) {} } // Ugh 😖 +foo(ref!) +``` + +These functions clearly weren't designed to accommodate this widespread practice. To acknowledge and embrace this new style, we propose to introduce a new public Standard Library function that simply extends the lifetime of whatever variable it is given: + +```swift +func extendLifetime(_ x: borrowing T) +``` + +This allows `defer` incantations like the one above to be reformulated into a more readable form: + +```swift +// Slightly improved reality +weak var ref = obj +defer { extendLifetime(obj) } +foo(ref!) +``` + +To avoid disrupting working code, this proposal does not deprecate the existing closure-based functions in favor of the new `extendLifetime` operation. (Introducing the new function will still considerably reduce the need for future Swift releases to continue repeatedly generalizing the existing functions -- for example, to allow async use, or to allow nonescapable results.) + +### Metatype comparisons + +Swift's metatypes do not currently conform to `Equatable`, but the Standard Library still provides top-level `==` and `!=` operators that implement the expected equality relation. Previously, these operators only worked on metatypes of `Copyable` and `Escapable` types; we propose to relax this requirement. + +```swift +print(Atomic.self == Span.self) // ⟹ false +``` + +The classic operators support existential metatypes `Any.Type`; the new variants also accept generalized existentials: + +```swift +let t1: any (~Copyable & ~Escapable).Type = Atomic.self +let t2: any (~Copyable & ~Escapable).Type = Span.self +print(t1 != t2) // ⟹ true +print(t1 == t1) // ⟹ true +``` + +### Object identifiers + +The `ObjectIdentifier` construct is primarily used to generate a Comparable/Hashable value that identifies a class instance. However, it is also able to identify metatypes: + +```swift +let id1 = ObjectIdentifier(Int.self) +let id2 = ObjectIdentifier(String.self) +print(id1 == id2) // ⟹ false +``` + +[SE-0437] did not generalize this initializer; we can now allow it to work with both noncopyable and nonescapable types: + +```swift +import Synchronization + +let id3 = ObjectIdentifier(Atomic.self) // OK, noncopyable input type +let id4 = ObjectIdentifier(Span.self) // OK, nonescapable input type +print(id3 == id4) // ⟹ false +``` + +The object identifier of a noncopyable/nonescapable type is still a regular copyable and escapable identifier -- for instance, it can be compared against other ids and hashed. + +### Odds and ends + +[SE-0437] omitted generalizing the `Equatable` conformance of `ManagedBufferPointer`; this proposal allows comparing `ManagedBufferPointer` instances for equality even if their `Element` happens to be noncopyable. + +[SE-0437] kept the `indices` property of unsafe buffer pointer types limited to cases where `Element` is copyable. This proposal generalizes `indices` to be also available on buffer pointers of noncopyable elements. (In the time since the original proposal, [SE-0447] has introduced a `Span` type that ships with an unconditional `indices` property, and [SE-0453] followed suit by introducing `Vector` with the same property. It makes sense to also provide this interface on buffer pointers, for consistency.) `indices` is useful for iterating through these collection types, especially until we ship a new iteration model that supports noncopyable/nonescapable containers. + +Finally, [SE-0437] neglected to generalize any of the buffer pointer operations that [SE-0370] introduced on the standard `Slice` type. In this proposal, we correct this omission by generalizing the handful of operations that can support noncopyable result elements: `moveInitializeMemory(as:fromContentsOf:)`, `bindMemory(to:)`, `withMemoryRebound(to:_:)`, and `assumingMemoryBound(to:)`. `Slice` itself continues to require its `Element` to be copyable (at least for now), preventing the generalization of other operations. + +## Detailed Design + +Note that Swift provides no way to define the lifetime dependencies of a function's nonescapable result, nor to set lifetime constraints on input parameters. Until the language gains an official way to express such constraints, the Swift Standard Library will define the APIs generalized in this proposal with unstable syntax that isn't generally available. In this text, we'll be using an illustrative substitute -- the hypothetical `@_lifetime` attribute. We will loosely describe its meaning as we go. + +Note: The `@_lifetime` attribute is not real; it is merely a didactic placeholder. The eventual lifetime annotations proposal may or may not propose syntax along these lines. We expect the Standard Library to immediately switch to whatever syntax Swift eventually embraces, as soon as it becomes available. + +### Inferred lifetime behavior of nonescapable enum types + +[SE-0446] has introduced the concept of a nonescapable enum type to Swift. While that proposal did not explicitly spell this out, this inherently included a set of implicit lifetime rules for the principal language features that interact with enum types: enum construction using case factories and pattern matching. To generalize `Optional` and `Result`, we need to understand how these implicit inference rules work for enum types with a single nonescapable associated value. + +1. When constructing an enum case with a single nonescapable associated value, the resulting enum value is inferred to carry precisely the same lifetime dependencies as the origional input. +2. Pattern matching over such an enum case exposes the nonescapable associated value, inferring precisely the same lifetime dependencies for it as the original enum. + +```swift +enum Foo { + case a(T) + case b +} + +func test(_ array: Array) { + let span = array.span + let foo = Foo.a(span) // (1) + switch foo { + case .a(let span2): ... // (2) + case .b: ... + } +} +``` + +In statement (1), `foo` is defined to implicitly copy the lifetime dependencies of `span`; neither variable can escape the body of the `test` function. The let binding in the pattern match on `.a` in statement (2) creates `span2` with exactly the same lifetime dependencies as `foo`. + +(We do not describe the implicit semantics of enum cases with _multiple_ nonescapable associated values here, as they are relevant to neither `Optional` nor `Result`.) + +### Inferred lifetime behavior of `Optional`'s notational conveniences + +The `Optional` enum comes with a rich set of notational conveniences that are built directly into the language. This proposal extends these conveniences to work on nonescapable optionals; therefore it inherently needs to introduce new implicit lifetime inference rules, along the same lines as the two existing once we described above: + +1. The result of implicit optional promotion of a nonescapable value is a nonescapable optional carrying precisely the same lifetime dependencies as the original input. +2. The force-unwrapping special form `!` and the optional chaining special form `?` both implicitly infer the lifetime dependencies of the wrapped value (if any) by directly copying those of the optional. + +### `protocol ExpressibleByNilLiteral` + +In order to generalize `Optional`, we need the `ExpressibleByNilLiteral` protocol to support nonescapable conforming types. By definition, the `nil` form needs to behave like a regular, escapable value; accordingly, the required initializer needs to establish "immortal" or "static" lifetime semantics on the resulting instance. + +```swift +protocol ExpressibleByNilLiteral: ~Copyable, ~Escapable { + @_lifetime(immortal) // Illustrative syntax + init(nilLiteral: ()) +} +``` + +In this illustration, `@_lifetime(immortal)` specifies that the initializer places no constraints on the lifetime of its result. We expect a future proposal to define a stable syntax for expressing such lifetime dependency constraints. + +Preexisting types that conform to `ExpressibleByNilLiteral` are all escapable, and escapable values always have immortal lifetimes, by definition. Therefore, initializer implementations in existing conformances already satisfy this new refinement of the initializer requirement -- it only makes a difference in the newly introduced `~Escapable` case. + +### `enum Optional` + +We generalize `Optional` to allow nonescapable wrapped types in addition to noncopyable ones. + +```swift +enum Optional: ~Copyable, ~Escapable { + case none + case some(Wrapped) +} + +extension Optional: Copyable where Wrapped: Copyable & ~Escapable {} +extension Optional: Escapable where Wrapped: Escapable & ~Copyable {} +extension Optional: BitwiseCopyable where Wrapped: BitwiseCopyable & ~Escapable {} +extension Optional: Sendable where Wrapped: ~Copyable & ~Escapable & Sendable {} +``` + +To allow the use of the `nil` syntax with nonescapable optional types, we generalize `Optional`'s conformance to `ExpressibleByNilLiteral`: + +```swift +extension Optional: ExpressibleByNilLiteral +where Wrapped: ~Copyable & ~Escapable { + @_lifetime(immortal) // Illustrative syntax + init(nilLiteral: ()) +} +``` + +As discussed above, `nil` optionals have no lifetime dependencies, and they continue to work like escapable values. + +We need to generalize the existing unlabeled initializer to support the nonescapable case. When passed a nonescapable entity, the initializer creates an optional that has precisely the same lifetime dependencies as the original entity. Once again, Swift has not yet provided a stable way to express this dependency; so to define such an initializer, the Standard Library needs to use some unstable mechanism. We use the hypothetical `@_lifetime(copying some)` syntax to do this -- this placeholder notation is intended to reflect that the lifetime dependencies of the result are copied verbatim from the `some` argument. + +```swift +extension Optional where Wrapped: ~Copyable & ~Escapable { + @_lifetime(copying some) // Illustrative syntax + init(_ some: consuming Wrapped) +} +``` + +As we've seen, the language also has built-in mechanisms for constructing `Optional` values that avoid invoking this initializer: it implements implicit optional promotions and explicit case factories. When given values of nonescapable types, these methods also _implicitly_ result in the result's lifetime dependencies being copied directly from the original input. + +Swift offers many built-in ways for developers to unwrap optional values: we have force unwrapping, optional chaining, pattern matching, optional bindings, etc. Many of these rely on direct compiler support that is already able to properly handle lifetime matters; but the stdlib also includes its own forms of unwrapping, and these require some API changes. + +In this proposal, we generalize `take()` to work on nonescapable optionals. It resets `self` to nil and returns its original value with precisely the same lifetime dependency as we started with. The `nil` value it leaves behind is still constrained to the same lifetime -- we do not have a way for a mutating function to affect the lifetime dependencies of its `self` argument. + +```swift +extension Optional where Wrapped: ~Copyable & ~Escapable { + @_lifetime(copying self) // Illustrative syntax + mutating func take() -> Self +} +``` + +We are also ready to generalize the `unsafelyUnwrapped` property: + +```swift +extension Optional where Wrapped: ~Escapable { + @_lifetime(copying self) // Illustrative syntax + var unsafelyUnwrapped: Wrapped { get } +} +``` + +This property continues to require copyability for now, as supporting noncopyable wrapped types requires the invention of new accessors that hasn't happened yet. + +As noted above, we defer generalizing the nil-coalescing operator `??`. We expect to tackle it when it becomes possible to express the lifetime dependency of its result as an intersection of the lifetimes of its left argument and the _result_ of the right argument (an autoclosure). We also do not attempt to generalize similar higher-order API, like `Optional.map` or `.flatMap`. + +The Standard Library provides special support for comparing arbitrary optional values against `nil`. We generalize this mechanism to support nonescapable cases: + +``` +extension Optional where Wrapped: ~Copyable & ~Escapable { + static func ~=( + lhs: _OptionalNilComparisonType, + rhs: borrowing Wrapped? + ) -> Bool + + static func ==( + lhs: borrowing Wrapped?, + rhs: _OptionalNilComparisonType + ) -> Bool + + static func !=( + lhs: borrowing Wrapped?, + rhs: _OptionalNilComparisonType + ) -> Bool + + static func ==( + lhs: _OptionalNilComparisonType, + rhs: borrowing Wrapped? + ) -> Bool + + static func !=( + lhs: _OptionalNilComparisonType, + rhs: borrowing Wrapped? + ) -> Bool +} +``` + +### `enum Result` + +For `Result`, this proposal concentrates on merely allowing the success case to contain a nonescapable value. + +```swift +enum Result { + case success(Success) + case failure(Failure) +} + +extension Result: Copyable where Success: Copyable & ~Escapable {} +extension Result: Escapable where Success: Escapable & ~Copyable {} +extension Result: Sendable where Success: Sendable & ~Copyable & ~Escapable {} +``` + +We postpone generalizing most of the higher-order functions that make `Result` convenient to use, as we currently lack the means to reason about lifetime dependencies for such functions. But we are already able to generalize the two functions that do not have complicated lifetime semantics: `mapError` and `flatMapError`. + +```swift +extension Result where Success: ~Copyable & ~Escapable { + @_lifetime(copying self) // Illustrative syntax + consuming func mapError( + _ transform: (Failure) -> NewFailure + ) -> Result +} + +extension Result where Success: ~Copyable & ~Escapable { + @_lifetime(copying self) // Illustrative syntax + consuming func flatMapError( + _ transform: (Failure) -> Result + ) -> Result +} +``` + +Both of these functions return a value with the same lifetime as the original `Result` instance. + +We can also generalize the convenient `get()` function, which is roughly equivalent to optional unwrapping: + +``` +extension Result where Success: ~Copyable & ~Escapable { + @_lifetime(copying self) // Illustrative syntax + consuming func get() throws(Failure) -> Success +} +``` + +In the non-escapable case, this function returns a value with a lifetime that precisely matches the original `Result`. + +### `enum MemoryLayout` + +Swift is not yet ready to introduce pointers to nonescapable values -- we currently lack the ability to assign proper lifetime semantics to the addressed items. + +However, a nonescapable type does still have a well-defined memory layout, and it makes sense to allow developers to query the size, stride, and alignment of such instances. This information is associated with the type itself, and it is independent of the lifetime constraints of its instances. Therefore, we can generalize the `MemoryLayout` enumeration to allow its subject to be a nonescapable type: + +```swift +enum MemoryLayout +: ~BitwiseCopyable, Copyable, Escapable {} + +extension MemoryLayout where T: ~Copyable & ~Escapable { + static var size: Int { get } + static var stride: Int { get } + static var alignment: Int { get } +} + +extension MemoryLayout where T: ~Copyable & ~Escapable { + static func size(ofValue value: borrowing T) -> Int + static func stride(ofValue value: borrowing T) -> Int + static func alignment(ofValue value: borrowing T) -> Int +} +``` + +### Lifetime Management + +[SE-0437] generalized the `withExtendedLifetime` family of functions to support extending the lifetime of noncopyable entities. This proposal further generalizes these to also allow operating on nonescapable entities: + +```swift +func withExtendedLifetime< + T: ~Copyable & ~Escapable, + E: Error, + Result: ~Copyable +>( + _ x: borrowing T, + _ body: () throws(E) -> Result +) throws(E) -> Result + +func withExtendedLifetime< + T: ~Copyable & ~Escapable, + E: Error, + Result: ~Copyable +>( + _ x: borrowing T, + _ body: (borrowing T) throws(E) -> Result +) throws(E) -> Result +``` + +Note that the `Result` is still required to be escapable. + +We also propose the addition of a new function variant that eliminates the closure argument, to better accommodate the current best practice of invoking these functions in `defer` blocks: + +```swift +func extendLifetime(_ x: borrowing T) +``` + +### Metatype equality + +Swift's metatypes do not conform to `Equatable`, but the Standard Library does implement the `==`/`!=` operators over them: + +```swift +func == (t0: Any.Type?, t1: Any.Type?) -> Bool { ... } +func != (t0: Any.Type?, t1: Any.Type?) -> Bool { ... } +``` + +Note how these are defined on optional metatype existentials, typically relying on implicit optional promotion. We propose to generalize these to support metatypes of noncopyable and/or nonescapable types: + +```swift +func == ( + t0: (any (~Copyable & ~Escapable).Type)?, + t1: (any (~Copyable & ~Escapable).Type)? +) -> Bool { ... } +func != ( + t0: (any (~Copyable & ~Escapable).Type)?, + t1: (any (~Copyable & ~Escapable).Type)? +) -> Bool { ... } +``` + +### `struct ObjectIdentifier` + +The `ObjectIdentifier` construct is primarily used to generate a `Comparable`/`Hashable` value that identifies a class instance. However, it is also able to generate hashable type identifiers: + +```swift +extension ObjectIdentifier { + init(_ x: Any.Type) +} +``` + +We propose to generalize this initializer to allow generating identifiers for noncopyable and nonescapable types as well, using generalized metatype existentials: + +```swift +extension ObjectIdentifier { + init(_ x: any (~Copyable & ~Escapable).Type) +} +``` + +### `ManagedBufferPointer` equatability + +The `ManagedBufferPointer` type conforms to `Equatable`; its `==` implementation works by comparing the identity of the class instances it is referencing. [SE-0437] has generalized the type to allow a noncopyable `Element` type, but it did not generalize this specific conformance. This proposal aims to correct this oversight: + +```swift +extension ManagedBufferPointer: Equatable where Element: ~Copyable { + static func ==( + lhs: ManagedBufferPointer, + rhs: ManagedBufferPointer + ) -> Bool +} +``` + +Managed buffer pointers are pointer types -- as such, they can be compared whether or not they are addressing a buffer of copyable items. + +(Note: conformance generalizations like this can cause compatibility issues when newly written code is deployed on older platforms that pre-date the generalization. We do not expect this to be an issue in this case, as the generalization is compatible with the implementations we previously shipped.) + +### Making `indices` universally available on unsafe buffer pointers + +[SE-0437] kept the `indices` property of unsafe buffer pointer types limited to cases where `Element` is copyable. In the time since that proposal, [SE-0447] has introduced a `Span` type that ships with an unconditional `indices` property, and [SE-0453] followed suit by introducing `Vector` with the same property. For consistency, it makes sense to also allow developers to unconditionally access `Unsafe[Mutable]BufferPointer.indices`, whether or not `Element` is copyable. + +```swift +extension UnsafeBufferPointer where Element: ~Copyable { + var indices: Range { get } +} + +extension UnsafeMutableBufferPointer where Element: ~Copyable { + var indices: Range { get } +} +``` + +This allows Swift programmers to iterate over the indices of a buffer pointer with simpler syntax, independent of what `Element` they are addressing: + +```swift +for i in buf.indices { + ... +} +``` + +We consider `indices` to be slightly more convenient than the equivalent expression `0 ..< buf.count`. + +(Of course, we are still planning to introduce direct support for for-in loops over noncopyable/nonescapable containers, which will provide a far more flexible solution. `indices` is merely a stopgap solution to bide us over until we are ready to propose that.) + +### Buffer pointer operations on `Slice` + +Finally, to address an inconsistency that was left unresolved by [SE-0437], we generalize a handful of buffer pointer operations that are defined on buffer slices. This consists of the following list, originally introduced in [SE-0370]: + +- Initializing a slice of a mutable raw buffer pointer by moving items out of a typed mutable buffer pointer: + + ```swift + extension Slice where Base == UnsafeMutableRawBufferPointer { + func moveInitializeMemory( + as type: T.Type, + fromContentsOf source: UnsafeMutableBufferPointer + ) -> UnsafeMutableBufferPointer + } + ``` + +- Binding memory of raw buffer pointer slices: + + ```swift + extension Slice where Base == UnsafeMutableRawBufferPointer { + func bindMemory( + to type: T.Type + ) -> UnsafeMutableBufferPointer + } + + extension Slice where Base == UnsafeRawBufferPointer { + func bindMemory( + to type: T.Type + ) -> UnsafeBufferPointer + } + ``` + +- Temporarily rebinding memory of a (typed or untyped, mutable or immutable) buffer pointer slice for the duration of a function call: + + ```swift + extension Slice where Base == UnsafeMutableRawBufferPointer { + func withMemoryRebound( + to type: T.Type, + _ body: (UnsafeMutableBufferPointer) throws(E) -> Result + ) throws(E) -> Result + } + + extension Slice where Base == UnsafeRawBufferPointer { + func withMemoryRebound( + to type: T.Type, + _ body: (UnsafeBufferPointer) throws(E) -> Result + ) throws(E) -> Result + } + + extension Slice { + func withMemoryRebound< + T: ~Copyable, E: Error, Result: ~Copyable, Element + >( + to type: T.Type, + _ body: (UnsafeBufferPointer) throws(E) -> Result + ) throws(E) -> Result + where Base == UnsafeBufferPointer + + public func withMemoryRebound< + T: ~Copyable, E: Error, Result: ~Copyable, Element + >( + to type: T.Type, + _ body: (UnsafeMutableBufferPointer) throws(E) -> Result + ) throws(E) -> Result + where Base == UnsafeMutableBufferPointer + } + ``` + +- Finally, converting a slice of a raw buffer pointer into a typed buffer pointer, assuming its memory is already bound to the correct type: + + ```swift + extension Slice where Base == UnsafeMutableRawBufferPointer { + func assumingMemoryBound( + to type: T.Type + ) -> UnsafeMutableBufferPointer + } + + extension Slice where Base == UnsafeRawBufferPointer { + func assumingMemoryBound( + to type: T.Type + ) -> UnsafeBufferPointer + } + ``` + +All of these forward to operations on the underlying base buffer pointer that have already been generalized in [SE-0437]. These changes are simply restoring feature parity between buffer pointer and their slices, where possible. (`Slice` still requires its `Element` to be copyable, which limits generalization of other buffer pointer APIs defined on it.) + +These generalizations are limited to copyability for now. We do expect that pointer types (including buffer pointers) will need to be generalized to allow non-escapable pointees; however, we have to postpone that work until we are able to precisely reason about lifetime requirements. + + + +## Source compatibility + +Like [SE-0437], this proposal also heavily relies on the assurance that removing the assumption of escapability on these constructs will not break existing code that used to rely on the original, escaping definitions. [SE-0437] has explored a few cases where this may not be the case; these can potentially affect code that relies on substituting standard library API with its own implementations. With the original ungeneralized definitions, such custom reimplementations could have shadowed the originals. However, this may no longer be the case with the generalizations included, and this can lead to ambiguous function invocations. + +This proposal mostly touches APIs that were already changed by [SE-0437], and that reduces the likelihood of it causing new issues. That said, it does generalize some previously unchanged interfaces that may provide new opportunities for such shadowing declarations to cause trouble. + +Like previously, we do have engineering options to mitigate such issues in case we do encounter them in practice: for example, we can choose to amend Swift's shadowing rules to ignore differences in throwing, noncopyability, and nonescapability, or we can manually patch affected definitions to make the expression checker consider them to be less specific than any custom overloads. + +## ABI compatibility + +The introduction of support for nonescapable types is (in general) a compile-time matter, with minimal (or even zero) runtime impact. This greatly simplifies the task of generalizing previously shipping types for use in nonescapable contexts. Another simplifying aspect is that while it can be relatively easy for classic Swift code to accidentally copy a value, it tends to be rare for functions to accidentally _escape_ their arguments -- previous versions of a function are less likely to accidentally violate nonescapability than noncopyability. + +The implementation of this proposal adopts the same approaches as [SE-0437] to ensure forward and backward compatibility of newly compiled (and existing) binaries, including the Standard Library itself. We expect that code that exercises the new features introduced in this proposal will be able to run on earlier versions of the Swift stdlib -- to the extent that noncopyable and/or nonescapable types are allowed to backdeploy. + +[SE-0437] has already arranged ABI compatibility symbols to get exported as needed to support ABI continuity. It has also already reimplemented most of the entry points that this proposal touches, in a way that forces them to get embedded in client binaries. This allows the changes in this proposal to get backdeployed without any additional friction. + +Like its precursor, this proposal does assume that the `~Copyable`/`~Escapable` generalization of the `ExpressibleByNilLiteral` protocol will not have an ABI impact on existing conformers of it. However, it goes a step further, by also adding a lifetime annotation on the protocol's initializer requirement; this requires that such annotations must not interfere with backward/forward binary compatibility, either. (E.g., it requires that such lifetime annotations do not get mangled into exported symbol names.) + +### Note on protocol generalizations + +Like [SE-0437], this proposal mostly avoids generalizing standard protocols, with the sole exception of `ExpressibleByNilLiteral`, which has now been generalized to allow both noncopyable and nonescapable conforming types. + +As a general rule, protocol generalizations like that may not be arbitrarily backdeployable -- it seems likely that we'll at least need to support limiting the availability of _conformance_ generalizations, if not generalizations of the protocol itself. In this proposal, we follow [SE-0437] in assuming that this potential issue will not apply to the specific case of `ExpressibleByNilLiteral`, because of its particularly narrow use case. Our experience with [SE-0437] is reinforcing this assumption, but it is still possible there is an ABI back-compatibility issue that we haven't uncovered yet. In the (unlikely, but possible) case we do discover such an issue, we may need to do extra work to patch protocol conformances in earlier stdlibs, or we may decide to limit the use of `nil` with noncopyable/nonescapable optionals to recent enough runtimes. + +To illustrate the potential problem, let's consider `Optional`'s conformance to `Equatable`: + +```swift +extension Optional: Equatable where Wrapped: Equatable { + public static func ==(lhs: Wrapped?, rhs: Wrapped?) -> Bool { + switch (lhs, rhs) { + case let (l?, r?): return l == r + case (nil, nil): return true + default: return false + } + } +} +``` + +This conformance is currently limited to copyable and escapable cases, and it is using the classic, copying form of the switch statement, with `case let (l?, r?)` semantically making full copies of the two wrapped values. We do intend to soon generalize the `Equatable` protocol to support noncopyable and/or nonescapable conforming types. When that becomes possible, `Optional` will want to immediately embrace this generalization, to allow comparing two noncopyable/nonescapable instances for equality: + +```swift +extension Optional: Equatable where Wrapped: Equatable & ~Copyable & ~Escapable { + public static func ==(lhs: borrowing Wrapped?, rhs: borrowing Wrapped?) -> Bool { + switch (lhs, rhs) { + case let (l?, r?): return l == r + case (nil, nil): return true + default: return false + } + } +} +``` + +On the surface, this seems like a straightforward change. Unfortunately, switching to `borrowing` arguments changes the semantics of the implementation, converting the original copying switch statement to the borrowing form introduced by [SE-0432]. This new variant avoids copying wrapped values to compare them, enabling the use of this function on noncopyable data. However, the old implementation of `==` did assume (and exercise!) copyability, so the `Equatable` conformance cannot be allowed to dispatch to `==` implementations that shipped in Standard Library releases that predate this generalization. + +To mitigate such problems, we'll either need to retroactively patch/substitute the generic implementations in previously shipping stdlibs, or we need to somehow limit availability of the generalized conformance, without affecting the original copyable/escapable one. + +This issue is more pressing for noncopyable cases, as preexisting implementations are far more likely to perform accidental copying than to accidentally escape their arguments. + +Our hypothesis is that `ExpressibleByNilLiteral` conformances are generally free of such issues. + +## Alternatives Considered + +Most of the changes proposed here follow directly from the introduction of nonescapable types. The API generalizations follow the patterns established by [SE-0437], and are largely mechanical in nature. For the most part, the decision points aren't about the precise form of any particular change, but more about what changes we are ready to propose _right now_. + +The single exception is the `extendLifetime` function, which is a brand new API; it comes from our experience using (and maintaining) the `withExtendedLifetime` function family. + +## Future Work + +For the most part, this proposal is concentrating on resolving the first item from [SE-0437]'s wish list (nonescapable `Optional` and `Result`), and it adds minor coherency improvements to the feature set we shipped there. + +Most other items listed as future work in that proposal continue to remain on our agenda. The advent of nonescapable types extends this list with additional items, including the following topics: + +1. We need to define stable syntax for expressing lifetime dependencies as explicit annotations, and we need to define what semantics we apply by default on functions that do not explicitly specify these. + +2. We will need an unsafe mechanism to override lifetime dependencies of nonescapable entities. We also expect to eventually need to allow unsafe bit casting to and from nonescapable types. + +3. We will need to allow pointer types to address nonescapable items: `UnsafePointer`, `UnsafeBufferPointer` type families, perhaps `ManagedBuffer`. The primary design task here is to decide what lifetime semantics we want to assign to pointer dereferencing operations, including mutations. + +4. Once we have pointers, we will also need to allow the construction of generic containers of nonescapable items, with some Sequence/Collection-like capabilities (iteration, indexing, generic algorithms, etc.). We expect the noncopyable/nonescapable container model to heavily rely on the `Span` type, which we intend to use as the basic unit of iteration, providing direct access to contiguous storage chunks. For containers of nonescapables in particular, this means we'll also need to generalize `Span` to allow it to capture nonescapable elements. + +5. We'll want to generalize most of the preexisting standard library protocols to allow nonescapable conforming types and (if possible) associated types. This is in addition to supporting noncopyability. This work will require adding carefully considered lifetime annotations on protocol requirements, while also carefully maintaining seamless forward/backward compatibility with the currently shipping protocol versions. This is expected to take several proposals; in some cases, it may include carefully reworking existing semantic requirements to better match noncopyable/nonescapable use cases. Some protocols may not be generalizable without breaking existing code; in those cases, we may need to resort to replacing or augmenting them with brand-new protocols. However, protocol generalizations for nonescapables are generally expected to be a smoother process than it is for noncopyables. + +## Acknowledgements + +Many people contributed to the discussions that led to this proposal. We'd like to especially thank the following individuals for their continued, patient and helpful input: + +- Alejandro Alonso +- Steve Canon +- Ben Cohen +- Kavon Farvardin +- Doug Gregor +- Joe Groff +- Megan Gupta +- Tim Kientzle +- Guillaume Lessard +- John McCall +- Tony Parker +- Andrew Trick +- Rauhul Varma From 68da31ee2d3c21ab839a66d7459485412eb932ed Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 5 Mar 2025 11:28:51 -0800 Subject: [PATCH 4128/4563] Initiate review of SE-0465: Standard Library Primitives for Nonescapable Types (#2724) --- ...ves.md => 0465-nonescapable-stdlib-primitives.md} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename proposals/{NNNN-nonescapable-stdlib-primitives.md => 0465-nonescapable-stdlib-primitives.md} (99%) diff --git a/proposals/NNNN-nonescapable-stdlib-primitives.md b/proposals/0465-nonescapable-stdlib-primitives.md similarity index 99% rename from proposals/NNNN-nonescapable-stdlib-primitives.md rename to proposals/0465-nonescapable-stdlib-primitives.md index 788faef5cf..a4021f0a16 100644 --- a/proposals/NNNN-nonescapable-stdlib-primitives.md +++ b/proposals/0465-nonescapable-stdlib-primitives.md @@ -1,12 +1,12 @@ # Standard Library Primitives for Nonescapable Types -* Proposal: [SE-NNNN](NNNN-nonescapable-stdlib-primitives.md) +* Proposal: [SE-0465](0465-nonescapable-stdlib-primitives.md) * Authors: [Karoy Lorentey](https://github.com/lorentey) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Doug Gregor](https://github.com/douggregor) +* Status: **Active review (March 5...18, 2025)** * Roadmap: [Improving Swift performance predictability: ARC improvements and ownership control][Roadmap] * Implementation: https://github.com/swiftlang/swift/pull/73258 -* Review: ([Pitch]) +* Review: ([Pitch](https://forums.swift.org/t/pitch-nonescapable-standard-library-primitives/77253)) [Roadmap]: https://forums.swift.org/t/a-roadmap-for-improving-swift-performance-predictability-arc-improvements-and-ownership-control/54206 [Pitch]: https://forums.swift.org/t/pitch-nonescapable-standard-library-primitives/77253 @@ -184,7 +184,7 @@ Of course, an optional is of limited use unless we are able to decide whether it ``` - The optional chaining special form `?`: - + ```swift func count(of maybeSpan: Span?) -> Int { guard let c = maybeSpan?.count else { return 0 } @@ -200,7 +200,7 @@ Of course, an optional is of limited use unless we are able to decide whether it return span.count } ``` - + These variants all work as expected. To avoid escapability violations, unwrapping the nonescapable optional results in a value with precisely the same lifetime dependencies as the original optional value. This applies to all forms of unwrapping, including pattern matching forms that bind copies of associated values to new variables, like `let span` above -- the resulting `span` value always has the same lifetime as the optional it comes from. The standard `Optional` type has custom support for comparing optional instances against `nil` using the traditional `==` operator, whether or not the wrapped type conforms to `Equatable`. [SE-0437] generalized this mechanism for noncopyable wrapped types, and it is reasonable to extend this to also cover the nonescapable case: From f6efcfdfcf9e1423ba8d9f1b3a0a39caefb17be7 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 5 Mar 2025 11:35:55 -0800 Subject: [PATCH 4129/4563] SE-0465: Add link to review thread (#2725) --- proposals/0465-nonescapable-stdlib-primitives.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0465-nonescapable-stdlib-primitives.md b/proposals/0465-nonescapable-stdlib-primitives.md index a4021f0a16..20e74ec1ec 100644 --- a/proposals/0465-nonescapable-stdlib-primitives.md +++ b/proposals/0465-nonescapable-stdlib-primitives.md @@ -6,7 +6,7 @@ * Status: **Active review (March 5...18, 2025)** * Roadmap: [Improving Swift performance predictability: ARC improvements and ownership control][Roadmap] * Implementation: https://github.com/swiftlang/swift/pull/73258 -* Review: ([Pitch](https://forums.swift.org/t/pitch-nonescapable-standard-library-primitives/77253)) +* Review: ([Review](https://forums.swift.org/t/se-0465-standard-library-primitives-for-nonescapable-types/78310)) ([Pitch](https://forums.swift.org/t/pitch-nonescapable-standard-library-primitives/77253)) [Roadmap]: https://forums.swift.org/t/a-roadmap-for-improving-swift-performance-predictability-arc-improvements-and-ownership-control/54206 [Pitch]: https://forums.swift.org/t/pitch-nonescapable-standard-library-primitives/77253 From 2df8b28f2559ce48b7a1f7b17b836139d3b3967c Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Wed, 5 Mar 2025 23:22:12 +0000 Subject: [PATCH 4130/4563] [SE-0464] Fix metadata of `UTF8Span` proposal --- proposals/0464-utf8span-safe-utf8-processing.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0464-utf8span-safe-utf8-processing.md b/proposals/0464-utf8span-safe-utf8-processing.md index cb9cb817fd..a2b09f6528 100644 --- a/proposals/0464-utf8span-safe-utf8-processing.md +++ b/proposals/0464-utf8span-safe-utf8-processing.md @@ -1,17 +1,17 @@ # UTF8Span: Safe UTF-8 Processing Over Contiguous Bytes -* Proposal: [SE-0464](0464-utf8-span.md) +* Proposal: [SE-0464](0464-utf8span-safe-utf8-processing.md) * Authors: [Michael Ilseman](https://github.com/milseman), [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Active review (March 5–19, 2025)** * Bug: rdar://48132971, rdar://96837923 -* Implementation: https://github.com/swiftlang/swift/pull/78531 -* Review: ([pitch 1](https://forums.swift.org/t/pitch-utf-8-processing-over-unsafe-contiguous-bytes/69715)) ([pitch 2](https://forums.swift.org/t/pitch-safe-utf-8-processing-over-contiguous-bytes/72742)) ([review](https://forums.swift.org/t/se-0464-utf8span-safe-utf-8-processing-over-contiguous-bytes/78307)) +* Implementation: [swiftlang/swift#78531](https://github.com/swiftlang/swift/pull/78531) +* Review: ([first pitch](https://forums.swift.org/t/pitch-utf-8-processing-over-unsafe-contiguous-bytes/69715)) ([second pitch](https://forums.swift.org/t/pitch-safe-utf-8-processing-over-contiguous-bytes/72742)) ([third pitch](https://forums.swift.org/t/pitch-utf8span-safe-utf-8-processing-over-contiguous-bytes/77483)) ([review](https://forums.swift.org/t/se-0464-utf8span-safe-utf-8-processing-over-contiguous-bytes/78307)) ## Introduction -We introduce `UTF8Span` for efficient and safe Unicode processing over contiguous storage. `UTF8Span` is a memory safe non-escapable type [similar to `Span`](https://github.com/swiftlang/swift-evolution/pull/2307). +We introduce `UTF8Span` for efficient and safe Unicode processing over contiguous storage. `UTF8Span` is a memory safe non-escapable type [similar to `Span`](0447-span-access-shared-contiguous-storage.md). Native `String`s are stored as validly-encoded UTF-8 bytes in an internal contiguous memory buffer. The standard library implements `String`'s API as internal methods which operate on top of this buffer, taking advantage of the validly-encoded invariant and specialized Unicode knowledge. We propose making this UTF-8 buffer and its methods public as API for more advanced libraries and developers. @@ -522,7 +522,7 @@ extension UTF8Span { ### `UTF8Span` from `String` -We propose adding `utf8Span` properties to `String` and `Substring`, in line with [SE-0456](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233/17): +We propose adding `utf8Span` properties to `String` and `Substring`, in line with [SE-0456](0456-stdlib-span-properties.md): ```swift extension String { From 4b3973c780329ad08aeba254b4b23661bbc896b8 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 5 Mar 2025 18:16:47 -0800 Subject: [PATCH 4131/4563] Add a proposal for configuring default actor isolation per module. (#2667) * Add a proposal for configuring default actor isolation per module. * Add an alternatives considered section for using an enum in the package manifest API. * Update alternatives considered based on pitch feedback. * Update and rename NNNN-control-default-actor-isolation.md to 0466-control-default-actor-isolation.md Assign SE-0466 and update metadata for review --------- Co-authored-by: Stephen Canon --- .../0466-control-default-actor-isolation.md | 219 ++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 proposals/0466-control-default-actor-isolation.md diff --git a/proposals/0466-control-default-actor-isolation.md b/proposals/0466-control-default-actor-isolation.md new file mode 100644 index 0000000000..062ceeded2 --- /dev/null +++ b/proposals/0466-control-default-actor-isolation.md @@ -0,0 +1,219 @@ +# Control default actor isolation inference + +* Proposal: [SE-0466](0466-control-default-actor-isolation.md) +* Authors: [Holly Borla](https://github.com/hborla) +* Review Manager: [Steve Canon](https://github.com/stephentyrone) +* Status: **Active Review (March 5...19, 2025)** +* Vision: [Improving the approachability of data-race safety](/visions/approachable-concurrency.md) +* Implementation: On `main` behind the `UnspecifiedMeansMainActorIsolated` experimental feature. +* Review: ([pitch](https://forums.swift.org/t/pitch-control-default-actor-isolation-inference/77482)) + +## Introduction + +This proposal introduces a new compiler setting for inferring `@MainActor` isolation by default within the module to mitigate false-positive data-race safety errors in sequential code. + +## Motivation + +> Note: This motivation section was adapted from the [prospective vision for approachable data-race safety](https://github.com/hborla/swift-evolution/blob/approachable-concurrency-vision/visions/approachable-concurrency.md#mitigating-false-positive-data-race-safety-errors-in-sequential-code). Please see the vision document for extended motivation. + +A lot of code is effectively “single-threaded”. For example, most executables, such as apps, command-line tools, and scripts, start running on the main actor and stay there unless some part of the code does something concurrent (like creating a `Task`). If there isn’t any use of concurrency, the entire program will run sequentially, and there’s no risk of data races — every concurrency diagnostic is necessarily a false positive! It would be good to be able to take advantage of that in the language, both to avoid annoying programmers with unnecessary diagnostics and to reinforce progressive disclosure. Many people get into Swift by writing these kinds of programs, and if we can avoid needing to teach them about concurrency straight away, we’ll make the language much more approachable. + +The easiest and best way to model single-threaded code is with a global actor. Everything on a global actor runs sequentially, and code that isn’t isolated to that actor can’t access the data that is. All programs start running on the global actor `MainActor`, and if everything in the program is isolated to the main actor, there shouldn’t be any concurrency errors. + +Unfortunately, it’s not quite that simple right now. Writing a single-threaded program is surprisingly difficult under the Swift 6 language mode. This is because Swift 6 defaults to a presumption of concurrency: if a function or type is not annotated or inferred to be isolated, it is treated as non-isolated, meaning it can be used concurrently. This default often leads to conflicts with single-threaded code, producing false-positive diagnostics in cases such as: + +- global and static variables, +- conformances of main-actor-isolated types to non-isolated protocols, +- class deinitializers, +- overrides of non-isolated superclass methods in a main-actor-isolated subclass, and +- calls to main-actor-isolated functions from the platform SDK. + +## Proposed solution + +This proposal allows code to opt in to being “single-threaded” by default, on a module-by-module basis. A new `-default-isolation` compiler flag specifies the default isolation within the module, and a corresponding `SwiftSetting` method specifies the default isolation per target within a Swift package. + +This would change the default isolation rule for unannotated code in the module: rather than being non-isolated, and therefore having to deal with the presumption of concurrency, the code would instead be implicitly isolated to `@MainActor`. Code imported from other modules would be unaffected by the current module’s choice of default. When the programmer really wants concurrency, they can request it explicitly by marking a function or type as `nonisolated` (which can be used on any declaration as of [SE-0449](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0449-nonisolated-for-global-actor-cutoff.md)), or they can define it in a module that doesn’t default to main-actor isolation. + +## Detailed design + +### Specifying default isolation per module + +#### `-default-isolation` compiler flag + +The `-default-isolation` flag can be used to control the default actor isolation for all code in the module. The only valid arguments to `-default-isolation` are `MainActor` and `nonisolated`. It is an error to specify both `-default-isolation MainActor` and `-default-isolation nonisolated`. If no `-default-isolation` flag is specified, the default isolation for the module is `nonisolated`. + +#### `SwiftSetting.defaultIsolation` method + +The following method on `SwiftSetting` can be used to specify the default actor isolation per target in a Swift package manifest: + +```swift +extension SwiftSetting { + @available(_PackageDescription, introduced: 6.2) + public static func defaultIsolation( + _ globalActor: MainActor.Type?, + _ condition: BuildSettingCondition? = nil + ) -> SwiftSetting +} +``` + +The only valid values for the `globalActor` argument are `MainActor.self` and `nil`. The `nil` argument corresponds to `nonisolated`; `.defaultIsolation(nil)` will default to `nonisolated` within the module. When no `.defaultIsolation` setting is specified, the default isolation within the module is `nonisolated`. + +### Default actor isolation inference + +When the default actor isolation is specified as `MainActor`, declarations are inferred to be `@MainActor`-isolated by default. Default isolation does not apply in the following cases: + +* Declarations with explicit actor isolation +* Declarations with inferred actor isolation from a superclass, overridden method, protocol conformance, or member propagation +* All declarations inside an `actor` type, including static variables, methods, initializers, and deinitializers +* Declarations that cannot have global actor isolation, including typealiases, import statements, enum cases, and individual accessors + +The following code example shows the inferred actor isolation in comments given the code is built with `-default-isolation MainActor`: + +```swift +// @MainActor +func f() {} + +// @MainActor +class C { + // @MainActor + init() { ... } + + // @MainActor + deinit { ... } + + // @MainActor + struct Nested { ... } + + // @MainActor + static var value = 10 +} + +@globalActor +actor MyActor { + // nonisolated + init() { ... } + + // nonisolated + deinit { ... } + + // nonisolated + static let shared = MyActor() +} + +@MyActor +protocol P {} + +// @MyActor +struct S: P { + // @MyActor + func f() { ... } +} +``` + +This proposal does not change the default isolation inference rules for closures. Non-Sendable closures and closures passed to `Task.init` already have the same isolation as the enclosing context by default. When specifying `MainActor` isolation by default in a module, non-`@Sendable` closures and `Task.init` closures will have inferred `@MainActor` isolation when the default `@MainActor` inference rules apply to the enclosing context: + +```swift +// Built with -default-isolation MainActor + +// @MainActor +func f() { + Task { // @MainActor in + ... + } + + Task.detached { // nonisolated in + ... + } +} + +nonisolated func g() { + Task { // nonisolated in + ... + } +} +``` + +## Source compatibility + +Changing the default actor isolation for a given module or source file is a source incompatible change. The default isolation will remain the same for existing projects unless they explicitly opt into `@MainActor` inference by default via `-default-isolation MainActor` or `defaultIsolation(MainActor.self)` in a package manifest. + +## ABI compatibility + +This proposal has no ABI impact on existing code. + +## Implications on adoption + +This proposal does not change the adoption implications of adding `@MainActor` to a declaration that was previously `nonisolated` and vice versa. The source and ABI compatibility implications of changing actor isolation are documented in the Swift migration guide's [Library Evolution](https://github.com/apple/swift-migration-guide/blob/29d6e889e3bd43c42fe38a5c3f612141c7cefdf7/Guide.docc/LibraryEvolution.md#main-actor-annotations) article. + +## Future directions + +### Specify build settings per file + +There are some build settings that are applicable on a per-file basis, including specifying default actor isolation and controlling diagnostic behavior. We could consider allowing settings in individual files which the setting should apply to by introducing a `#pragma`-like compiler directive. This idea has been [pitched separately](https://forums.swift.org/t/pitch-compilersettings-a-top-level-statement-for-enabling-compiler-flags-locally-in-a-specific-file/77994). + +## Alternatives considered + +### Allow defaulting isolation to a custom global actor + +The `-default-isolation` flag could allow a custom global actor as the argument, and the `SwiftSetting` API could be updated to accept a string that represents a custom global actor in the target. + +This proposal only supports `MainActor` because any other global actor does not help with progressive disclosure. It has the opposite effect - it forces asynchrony on any main-actor-isolated caller. However, there's nothing in this proposal prohibits generalizing these settings to supporting arbitrary global actors in the future if a compelling use case arises. + +### Infer `MainActor` by default as an upcoming feature + +Instead of introducing a separate mode for configuring default actor isolation inference, the default isolation could be changed to be `MainActor` under an upcoming feature that is enabled by default in a future Swift language mode. The upcoming feature approach was not taken because `MainActor` isolation is the wrong default for many kinds of modules, including libraries that offer APIs that can be used from any isolation domain, and highly-concurrent server applications. + +Similarly, a future language mode could enable main actor isolation by default, and require an opt out for using `nonisolated` as the default actor isolation. However, as the Swift package ecosystem grows, it's more likely for `nonisolated` to be the more common default amongst projects. If we discover that not to be true in practice, nothing in this proposal prevents changing the default actor isolation in a future language mode. + +See the approachable data-race safety vision document for an [analysis on the risks of introducing a language dialect](https://github.com/hborla/swift-evolution/blob/approachable-concurrency-vision/visions/approachable-concurrency.md#risks-of-a-language-dialect) for default actor isolation. + +### Don't apply default actor isolation to explicitly `Sendable` types + +This proposal includes few excepts where the specified default actor isolation does not apply. An additional case that should be considered is types with a conformance to `Sendable`: + +```swift +struct SimpleValue: Sendable { + var value: Int +} +``` + +This is an attractive carve out upon first glance, but there are a number of downsides: + +* The carve out may be confusing if a conformance to `Sendable` is implied, e.g. through a conformance to another protocol. +* Global actor isolation implies a conformance to `Sendable`, so it's not clear that a `Sendable` type should not be global actor isolated. +* Methods on a `Sendable` type may still use other types and methods that have default actor isolation applied, which would lead to failures if the `Sendable` type was exempt from default isolation inference. + +A middle ground might be to not apply default actor isolation to types with an explicit conformance to `Sendable` within the same source file as the type. This approach would still have some of the downsides listed above, but it would be more straightforward to spot types that have this exemption. + +### Use an enum for the package manifest API + +An alternative to using a `MainActor` metatype for the Swift package manifest API is to use an enum, e.g. + +```swift +public enum DefaultActorIsolation { + case mainActor + case nonisolated +} + +extension SwiftSetting { + @available(_PackageDescription, introduced: 6.2) + public static func defaultIsolation( + _ isolation: DefaultActorIsolation, + _ condition: BuildSettingCondition? = nil + ) -> SwiftSetting +} + +// in a package manifest + +swiftSettings: [ + .defaultIsolation(.mainActor) +] +``` + +The enum approach introduces a different way of writing main actor isolation that does not involve the `MainActor` global actor type. The proposed design matches exactly the values used for `#isolation`, i.e. `MainActor.self` for main actor isolation and `nil` for `nonisolated`, which programmers are already familiar with. + +The primary argument for using an enum is that it can be extended in the future to support custom global actor types. This proposal deliberately puts supporting custom global actors in the alternatives considered and not future directions, because defaulting a module to a different global actor does not help improve progressive disclosure for concurrency. + +## Acknowledgments + +Thank you to John McCall for providing much of the motivation for this pitch in the approachable data-race safety vision document, and to Michael Gottesman for helping with the implementation. From 82aa810bdd3bf49d1e8a246f97ca59ecaaa40c00 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 5 Mar 2025 21:21:22 -0500 Subject: [PATCH 4132/4563] Update 0466-control-default-actor-isolation.md (#2728) Link back to review thread for SE-0466 --- proposals/0466-control-default-actor-isolation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0466-control-default-actor-isolation.md b/proposals/0466-control-default-actor-isolation.md index 062ceeded2..81f289408c 100644 --- a/proposals/0466-control-default-actor-isolation.md +++ b/proposals/0466-control-default-actor-isolation.md @@ -6,7 +6,7 @@ * Status: **Active Review (March 5...19, 2025)** * Vision: [Improving the approachability of data-race safety](/visions/approachable-concurrency.md) * Implementation: On `main` behind the `UnspecifiedMeansMainActorIsolated` experimental feature. -* Review: ([pitch](https://forums.swift.org/t/pitch-control-default-actor-isolation-inference/77482)) +* Review: ([pitch](https://forums.swift.org/t/pitch-control-default-actor-isolation-inference/77482))([review](https://forums.swift.org/t/se-0466-control-default-actor-isolation-inference/78321)) ## Introduction From b74e6ac1b68530495b2ccf2d0b3e2cb1f6481415 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Wed, 5 Mar 2025 22:15:09 -0600 Subject: [PATCH 4133/4563] Update the Swift evolution process documentation to incorporate Swift Testing --- process.md | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/process.md b/process.md index d38eeec80e..d9c71b07b3 100644 --- a/process.md +++ b/process.md @@ -5,10 +5,15 @@ Swift is a powerful and intuitive programming language that is designed to make ## Scope The Swift evolution process covers all design changes, no matter how small, to the Swift language, its standard library, and the core tools necessary to build Swift programs. This includes additions, removals, and changes to: + - the features of the Swift language, - the public interface of the Swift standard library, -- the configuration of the Swift compiler, and -- the core tools of the Swift package ecosystem, including the configuration of the [Swift package manager](https://www.swift.org/package-manager/) and the design of its manifest files. +- the configuration of the Swift compiler, +- the core tools of the Swift package ecosystem, including the configuration of + the [Swift package manager](https://www.swift.org/package-manager/) and the + design of its manifest files, and +- the features and public interfaces of + [Swift Testing](https://github.com/swiftlang/swift-testing). The design of other tools, such as IDEs, debuggers, and documentation generators, is not covered by the evolution process. The Core Team may create workgroups to guide and make recommendations about the development of these tools, but the output of those workgroups is not reviewed. @@ -31,9 +36,11 @@ There is a natural tension between these two goals. Open evolution processes are The [Core Team](https://www.swift.org/community/#core-team) is responsible for the strategic direction of Swift. The Core Team creates workgroups focused on specific parts of the project. When the Core Team gives a workgroup authority over part of the evolution of the project, that workgroup is called an evolution workgroup. Evolution workgroups manage the evolution process for proposals under their authority, working together with other workgroups as needed. -Currently, there is only one evolution workgroup: +Currently, there are two evolution workgroups: * The [Language Steering Group][language-steering-group] has authority over the evolution of the Swift language and its standard library. +* The [Testing Workgroup][testing-workgroup] has authority over the evolution of + the Swift Testing and Corelibs XCTest projects. The Core Team manages (or delegates) the evolution process for proposals outside these areas. The Core Team also retains the power to override the evolution decisions of workgroups when necessary. @@ -93,8 +100,11 @@ Please state explicitly whether you believe that the proposal should be accepted 1. **Develop the proposal and implementation** - 1. Expand the rough sketch into a formal proposal using the [proposal template](proposal-templates/0000-swift-template.md). - 1. In the [swift-evolution repository][swift-evolution-repo], open a [draft pull request][draft-pr] that adds your proposal to the [proposals directory](/proposals). + 1. Expand the rough sketch into a formal proposal using the + [relevant proposal template](#proposal-templates). + 1. In the [swift-evolution repository][swift-evolution-repo], open a + [draft pull request][draft-pr] that adds your proposal to the appropriate + [proposal directory](#proposal-locations). 1. Announce the pull request on the forums and edit the root post to link out to the pull request. 1. Refine the formal proposal in the open as you receive further feedback on the forums or the pull request. A ripe proposal is expected to address commentary from present and past @@ -128,6 +138,28 @@ Please state explicitly whether you believe that the proposal should be accepted [draft-pr]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests +### Proposal templates + +When writing a formal proposal document, start with the most relevant template +given the primary subject of the proposal: + +- Use the [Swift template][swift-template] for proposals concerning the Swift + language, compiler, or standard library. +- Use the [Swift Package Manager template][swiftpm-template] for proposals + related to SwiftPM, including the design of its package manifest files and + command-line tools. +- Use the [Swift Testing template][swift-testing-template] for proposals focused + on Swift Testing features or public interfaces. + +### Proposal locations + +When opening a pull request to add a new proposal to the +[swift-evolution repository][swift-evolution-repo], place proposals which use +the [Swift][swift-template] or [Swift Package Manager][swiftpm-template] +templates in the top-level [proposals](/proposals) directory. Place proposals +which use the newer [Swift Testing template][swift-testing-template] in the +[proposals/testing](/proposals/testing) subdirectory. + ## Review process The review process for a particular proposal begins when a member of @@ -211,6 +243,10 @@ A given proposal can be in one of several states: [status-page]: https://apple.github.io/swift-evolution/ [preview-package]: https://github.com/apple/swift-standard-library-preview/ [language-steering-group]: https://www.swift.org/language-steering-group +[testing-workgroup]: https://www.swift.org/testing-workgroup "Testing Workgroup page on Swift.org" +[swift-template]: proposal-templates/0000-swift-template.md "Swift proposal template" +[swiftpm-template]: proposal-templates/0000-swiftpm-template.md "Swift Package Manager proposal template" +[swift-testing-template]: proposal-templates/0000-swift-testing-template.md "Swift Testing proposal template" ## Review announcement From 47d96fc2add76b8ae808790a75706a1c22f4fe6d Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Fri, 7 Mar 2025 00:42:27 +0000 Subject: [PATCH 4134/4563] update link --- proposals/NNNN-environment-dependent-shared-libraries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-environment-dependent-shared-libraries.md b/proposals/NNNN-environment-dependent-shared-libraries.md index 6a6cdbe8e9..d1068526ea 100644 --- a/proposals/NNNN-environment-dependent-shared-libraries.md +++ b/proposals/NNNN-environment-dependent-shared-libraries.md @@ -4,7 +4,7 @@ * Authors: [tayloraswift](https://github.com/tayloraswift) * Review Manager: TBD * Implementation: [swiftlang/swift-package-manager#8249](https://github.com/swiftlang/swift-package-manager/pull/8249) -* Documentation: [How to use Environment-Dependent Shared Libraries](https://github.com/tayloraswift/swift-edsl-example-client/blob/master/Sources/KrustyKrab/docs.docc/Getting%20Started.md) +* Documentation: [How to use Environment-Dependent Shared Libraries](https://github.com/swiftlang/swift-package-manager/blob/b586467575580f2365e8f5a29c949379724db795/Documentation/EDSLs.md) * Bugs: [SR-5714](https://github.com/swiftlang/swift-package-manager/issues/5714) ## Introduction From 2e676204dc7b4d0668d859cf23e0846c6e76f3d0 Mon Sep 17 00:00:00 2001 From: Jed Fox Date: Sat, 8 Mar 2025 19:04:31 -0800 Subject: [PATCH 4135/4563] Mark SE-0436 as implemented in Swift 6.1 --- proposals/0436-objc-implementation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0436-objc-implementation.md b/proposals/0436-objc-implementation.md index aa2e049179..517599b667 100644 --- a/proposals/0436-objc-implementation.md +++ b/proposals/0436-objc-implementation.md @@ -3,8 +3,8 @@ * Proposal: [SE-0436](0436-objc-implementation.md) * Authors: [Becca Royal-Gordon](https://github.com/beccadax) * Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) -* Status: **Accepted** -* Implementation: Proposed syntax implemented in [apple/swift#73309](https://github.com/apple/swift/pull/73309); enable using the `ObjCImplementation` experimental feature flag +* Status: **Implemented (Swift 6.1)** +* Implementation: [swiftlang/swift#73309](https://github.com/swiftlang/swift/pull/73309), [swiftlang/swift#74801](https://github.com/swiftlang/swift/pull/74801) * Review: ([first pitch](https://forums.swift.org/t/pitch-objective-c-implementations-in-swift/61907)) ([second pitch](https://forums.swift.org/t/pitch-2-objective-c-implementations-in-swift/68090)) ([third pitch](https://forums.swift.org/t/pitch-3-objective-c-implementations-in-swift/71315)) ([review](https://forums.swift.org/t/se-0436-objective-c-implementations-in-swift/71712)) ([acceptance](https://forums.swift.org/t/accepted-se-0436-objective-c-implementations-in-swift/72053)) ## Introduction From a39c5fa4d9dfdc0aa83f209b332ba54b15a8f09c Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Mon, 10 Mar 2025 05:24:13 +0000 Subject: [PATCH 4136/4563] [SE-0465] Rename `Vector`, add syntax highlighting --- proposals/0465-nonescapable-stdlib-primitives.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0465-nonescapable-stdlib-primitives.md b/proposals/0465-nonescapable-stdlib-primitives.md index 20e74ec1ec..687c0f999e 100644 --- a/proposals/0465-nonescapable-stdlib-primitives.md +++ b/proposals/0465-nonescapable-stdlib-primitives.md @@ -357,7 +357,7 @@ The object identifier of a noncopyable/nonescapable type is still a regular copy [SE-0437] omitted generalizing the `Equatable` conformance of `ManagedBufferPointer`; this proposal allows comparing `ManagedBufferPointer` instances for equality even if their `Element` happens to be noncopyable. -[SE-0437] kept the `indices` property of unsafe buffer pointer types limited to cases where `Element` is copyable. This proposal generalizes `indices` to be also available on buffer pointers of noncopyable elements. (In the time since the original proposal, [SE-0447] has introduced a `Span` type that ships with an unconditional `indices` property, and [SE-0453] followed suit by introducing `Vector` with the same property. It makes sense to also provide this interface on buffer pointers, for consistency.) `indices` is useful for iterating through these collection types, especially until we ship a new iteration model that supports noncopyable/nonescapable containers. +[SE-0437] kept the `indices` property of unsafe buffer pointer types limited to cases where `Element` is copyable. This proposal generalizes `indices` to be also available on buffer pointers of noncopyable elements. (In the time since the original proposal, [SE-0447] has introduced a `Span` type that ships with an unconditional `indices` property, and [SE-0453] followed suit by introducing `InlineArray` with the same property. It makes sense to also provide this interface on buffer pointers, for consistency.) `indices` is useful for iterating through these collection types, especially until we ship a new iteration model that supports noncopyable/nonescapable containers. Finally, [SE-0437] neglected to generalize any of the buffer pointer operations that [SE-0370] introduced on the standard `Slice` type. In this proposal, we correct this omission by generalizing the handful of operations that can support noncopyable result elements: `moveInitializeMemory(as:fromContentsOf:)`, `bindMemory(to:)`, `withMemoryRebound(to:_:)`, and `assumingMemoryBound(to:)`. `Slice` itself continues to require its `Element` to be copyable (at least for now), preventing the generalization of other operations. @@ -481,7 +481,7 @@ As noted above, we defer generalizing the nil-coalescing operator `??`. We expec The Standard Library provides special support for comparing arbitrary optional values against `nil`. We generalize this mechanism to support nonescapable cases: -``` +```swift extension Optional where Wrapped: ~Copyable & ~Escapable { static func ~=( lhs: _OptionalNilComparisonType, @@ -547,7 +547,7 @@ Both of these functions return a value with the same lifetime as the original `R We can also generalize the convenient `get()` function, which is roughly equivalent to optional unwrapping: -``` +```swift extension Result where Success: ~Copyable & ~Escapable { @_lifetime(copying self) // Illustrative syntax consuming func get() throws(Failure) -> Success @@ -670,7 +670,7 @@ Managed buffer pointers are pointer types -- as such, they can be compared wheth ### Making `indices` universally available on unsafe buffer pointers -[SE-0437] kept the `indices` property of unsafe buffer pointer types limited to cases where `Element` is copyable. In the time since that proposal, [SE-0447] has introduced a `Span` type that ships with an unconditional `indices` property, and [SE-0453] followed suit by introducing `Vector` with the same property. For consistency, it makes sense to also allow developers to unconditionally access `Unsafe[Mutable]BufferPointer.indices`, whether or not `Element` is copyable. +[SE-0437] kept the `indices` property of unsafe buffer pointer types limited to cases where `Element` is copyable. In the time since that proposal, [SE-0447] has introduced a `Span` type that ships with an unconditional `indices` property, and [SE-0453] followed suit by introducing `InlineArray` with the same property. For consistency, it makes sense to also allow developers to unconditionally access `Unsafe[Mutable]BufferPointer.indices`, whether or not `Element` is copyable. ```swift extension UnsafeBufferPointer where Element: ~Copyable { From 0527db5becdcd78c35cf3d7ff8cc777190f19290 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Mon, 10 Mar 2025 16:48:28 -0500 Subject: [PATCH 4137/4563] Mention the Platform Steering Group in applicable places too Co-authored-by: John McCall --- process.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/process.md b/process.md index d9c71b07b3..b7765840e9 100644 --- a/process.md +++ b/process.md @@ -36,9 +36,10 @@ There is a natural tension between these two goals. Open evolution processes are The [Core Team](https://www.swift.org/community/#core-team) is responsible for the strategic direction of Swift. The Core Team creates workgroups focused on specific parts of the project. When the Core Team gives a workgroup authority over part of the evolution of the project, that workgroup is called an evolution workgroup. Evolution workgroups manage the evolution process for proposals under their authority, working together with other workgroups as needed. -Currently, there are two evolution workgroups: +Currently, there are three evolution workgroups: -* The [Language Steering Group][language-steering-group] has authority over the evolution of the Swift language and its standard library. +* The [Language Steering Group][language-steering-group] has authority over the evolution of the Swift language, its standard library, and any language configuration features of the Swift package manager. +* The [Platform Steering Group][platform-steering-group] has authority over the evolution of all other features of the Swift package manager and its manifest files. * The [Testing Workgroup][testing-workgroup] has authority over the evolution of the Swift Testing and Corelibs XCTest projects. @@ -243,6 +244,7 @@ A given proposal can be in one of several states: [status-page]: https://apple.github.io/swift-evolution/ [preview-package]: https://github.com/apple/swift-standard-library-preview/ [language-steering-group]: https://www.swift.org/language-steering-group +[platform-steering-group]: https://www.swift.org/platform-steering-group [testing-workgroup]: https://www.swift.org/testing-workgroup "Testing Workgroup page on Swift.org" [swift-template]: proposal-templates/0000-swift-template.md "Swift proposal template" [swiftpm-template]: proposal-templates/0000-swiftpm-template.md "Swift Package Manager proposal template" From bbcf82e613451c2abf6ed22120fb0b6e289fff4c Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Mon, 10 Mar 2025 16:48:55 -0500 Subject: [PATCH 4138/4563] Use a list to mention Swift Testing and XCTest Co-authored-by: John McCall --- process.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/process.md b/process.md index b7765840e9..ac6131ea56 100644 --- a/process.md +++ b/process.md @@ -12,8 +12,9 @@ The Swift evolution process covers all design changes, no matter how small, to t - the core tools of the Swift package ecosystem, including the configuration of the [Swift package manager](https://www.swift.org/package-manager/) and the design of its manifest files, and -- the features and public interfaces of - [Swift Testing](https://github.com/swiftlang/swift-testing). +- the public interfaces of the following libraries: + - [Swift Testing](https://github.com/swiftlang/swift-testing) + - [XCTest](https://github.com/swiftlang/swift-corelibs-xctest) The design of other tools, such as IDEs, debuggers, and documentation generators, is not covered by the evolution process. The Core Team may create workgroups to guide and make recommendations about the development of these tools, but the output of those workgroups is not reviewed. From 4a6a87dd227366ebc379fe742e62dd6bbb93689f Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Mon, 10 Mar 2025 17:04:24 -0500 Subject: [PATCH 4139/4563] Replace two outdated links in 'process.md' This is a small PR to replace two links in `process.md` which still reference the Apple GitHub organization instead of swiftlang. (I noticed these while working on #2729 but they were out of scope for that PR.) The existing links aren't broken, they do redirect successfully, but it's nicer to use the correct URLs. --- process.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/process.md b/process.md index d38eeec80e..93919f4c2e 100644 --- a/process.md +++ b/process.md @@ -111,7 +111,7 @@ Please state explicitly whether you believe that the proposal should be accepted Proposals that can ship as part of the [Standard Library Preview package][preview-package] should be paired with a pull request against the [swift-evolution-staging repository][swift-evolution-staging]. All other proposals should be paired with an implementation pull request - against the [main Swift repository](https://github.com/apple/swift). + against the [main Swift repository](https://github.com/swiftlang/swift). The preview package can accept new types, new protocols, and extensions to existing types and protocols that can be implemented without access to @@ -208,7 +208,7 @@ A given proposal can be in one of several states: [swift-evolution-repo]: https://github.com/swiftlang/swift-evolution "Swift evolution repository" [swift-evolution-staging]: https://github.com/swiftlang/swift-evolution-staging "Swift evolution staging repository" [proposal-reviews]: https://forums.swift.org/c/evolution/proposal-reviews "'Proposal reviews' category of the Swift forums" -[status-page]: https://apple.github.io/swift-evolution/ +[status-page]: https://www.swift.org/swift-evolution [preview-package]: https://github.com/apple/swift-standard-library-preview/ [language-steering-group]: https://www.swift.org/language-steering-group From 0344e4f3c3393981437d2bc9197817aeaac687e4 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 6 Mar 2025 17:55:35 -0800 Subject: [PATCH 4140/4563] Lot of edits --- proposals/nnnn-MutableSpan.md | 375 ++++++++++++++++++++++++++-------- 1 file changed, 289 insertions(+), 86 deletions(-) diff --git a/proposals/nnnn-MutableSpan.md b/proposals/nnnn-MutableSpan.md index f128749662..fce33b83e7 100644 --- a/proposals/nnnn-MutableSpan.md +++ b/proposals/nnnn-MutableSpan.md @@ -30,13 +30,19 @@ These functions have a few different drawbacks, most prominently their reliance In addition to the new types, we will propose adding new API some standard library types to take advantage of `MutableSpan` and `MutableRawSpan`. ## Proposed solution -We introduced `Span` to provide shared read-only access to containers. The natural next step is to provide a similar capability for mutable access. Mutability requires exclusive access, per Swift's [law of exclusivity][SE-0176]. `Span` is copyable, and must be copyable in order to properly model read access under the law of exclusivity: a value can be simultaneously accessed through multiple read-only accesses. Exclusive access cannot be modeled with a copyable type, since a copy would represent an additional access, in violation of the law of exclusivity. We therefore need a non-copyable type separate from `Span` in order to model mutations. + +We introduced `Span` to provide shared read-only access to containers. The natural next step is to provide a similar capability for mutable access. A library whose API provides access to its internal storage makes a decision regarding the type of access it provides; it may provide read-only access or provide the ability to mutate its storage. That decision is made by the API author. If mutations were enabled by simply binding a `Span` value to a mutable binding (`var` binding or `inout` parameter), that decision would rest with the user of the API instead of its author. This explains why mutations must be modeled by a type separate from `Span`. + +Mutability requires exclusive access, per Swift's [law of exclusivity][SE-0176]. `Span` is copyable, and must be copyable in order to properly model read access under the law of exclusivity: a value can be simultaneously accessed through multiple read-only accesses. Exclusive access cannot be modeled with a copyable type, since a copy would represent an additional access, in violation of the law of exclusivity. This explains why the type which models mutations must be non-copyable. #### MutableSpan -`MutableSpan` allows delegating mutations of a type's contiguous internal representation, by providing access to an exclusively-borrowed view of a range of contiguous, initialized memory. `MutableSpan` relies on guarantees that it has exclusive access to the range of memory it represents, and that the memory it represents will remain valid for the duration of the access. These provide data race safety and temporal safety. Like `Span`, `MutableSpan` performs bounds-checking on every access to preserve spatial safety. +`MutableSpan` allows delegating mutations of a type's contiguous internal representation, by providing access to an exclusively-borrowed view of a range of contiguous, initialized memory. `MutableSpan`'s memory safety' relies on guarantees that: +- it has exclusive access to the range of memory it represents, providing data race safety and enforced by `~Copyable`. +- the memory it represents will remain valid for the duration of the access, providing lifetime safety and enforced by `~Escapable`. +- each access is guarded by bounds checking, providing bounds safety. -A `MutableSpan` provided by a container represents a mutation of that container, via an exclusive borrow. Mutations are implemented by mutating functions and subscripts, which let the compiler statically enforce exclusivity. +A `MutableSpan` provided by a container represents a mutation of that container, as an extended mutation access. Mutations are implemented by mutating functions and subscripts, which let the compiler statically enforce exclusivity. #### MutableRawSpan @@ -44,7 +50,7 @@ A `MutableSpan` provided by a container represents a mutation of that container, #### Extensions to standard library types -The standard library will provide `mutableSpan` computed properties. These return lifetime-dependent `MutableSpan` instances, and represent a mutation of the instance that provided them. These computed properties are the safe and composable replacements for the existing `withUnsafeMutableBufferPointer` closure-taking functions. For example, +The standard library will provide `mutableSpan` computed properties. These return a new lifetime-dependent `MutableSpan` instance, and that `MutableSpan` represents a mutation of the instance that provided it. The `mutableSpan` computed properties are the safe and composable replacements for the existing `withUnsafeMutableBufferPointer` closure-taking functions. For example, ```swift func(_ array: inout Array) { @@ -56,7 +62,47 @@ func(_ array: inout Array) { } ``` -These computed properties represent a case of lifetime relationships not covered in [SE-0456][SE-0456]. In SE-0456 we defined lifetime relationships for computed property getters of non-escapable and copyable types (`~Escapable & Copyable`). We propose defining them for properties of non-escapable and non-copyable types (`~Escapable & ~Copyable`). A `~Escapable & ~Copyable` value borrows another binding; if this borrow is also a mutation then it is an exclusive borrow. The scope of the borrow, whether or not it is exclusive, extends until the last use of the dependent binding. +The `mutableSpan` computed property represents a case of lifetime relationships not covered until now. The `mutableSpan` computed properties proposed here will represent mutations of their callee. This relationship will be illustrated with a hypothetical `@_lifetime` attribute, which ties the lifetime of a return value to an input parameter in a specific way. + +Note: The `@_lifetime` attribute is not real; it is a placeholder. The eventual lifetime annotations proposal may or may not propose syntax along these lines. We expect that, as soon as Swift adopts a syntax do describe lifetime dependencies, the Standard Library will be modified to adopt that new syntax. + +```swift +extension Array { + public var mutableSpan: MutableSpan { + @_lifetime(inout self) + mutating get { ... } + } +} +``` + +Here, the lifetime of the returned `MutableSpan` is tied to an `inout` access of `self` (the `Array`.) As long as the returned instance exists, the source `Array` is being mutated, and no other access to the `Array` can occur. + +This lifetime relationship will apply to all the safe `var mutableSpan: MutableSpan` and `var mutableBytes: MutableRawSpan` properties described in this proposal. + +#### Slicing `MutableSpan` or `MutableRawSpan` instances + +An important category of use cases for `MutableSpan` and `MutableRawSpan` consists of bulk copying operations. Often times, such bulk operations do not necessarily start at the beginning of the span, thus having a method to select a sub-span is necessary. This means producing an instance derived from the callee instance. We adopt the nomenclature already introduced in [SE-0437][SE-0437], with a family of `extracting()` methods. + +```swift +extension MutableSpan where Element: ~Copyable & ~Escapable { + @_lifetime(inout self) + public mutating func extracting(_ range: Range) -> Self +} +``` + +This function returns an instance of `MutableSpan` that represents a mutation of the same memory as represented by the callee. The callee can therefore no longer be mutated while the returned value exists: + +```swift +var array = [1, 2, 3, 4, 5] +var span1 = array.mutableSpan +var span2 = span1.extracting(3..<5) +// span1 cannot be accessed here +span2.swapAt(0, 1) +_ = consume span2 // explicitly end scope for `span2` +print(array) // [1, 2, 3, 5, 4] +``` + +As established in [SE-0437][SE-0437], the instance returned by the `extracting()` function does not share indices with the function's callee. ## Detailed Design @@ -79,7 +125,7 @@ We store a `UnsafeMutableRawPointer` value internally in order to explicitly sup Initializers, required for library adoption, will be proposed alongside [lifetime annotations][PR-2305]; for details, see "[Initializers](#initializers)" in the [future directions](#Directions) section. ```swift -extension MutableSpan where Element: ~Copyable { +extension MutableSpan where Element: ~Copyable & ~Escapable { /// The number of initialized elements in this `MutableSpan`. var count: Int { get } @@ -100,7 +146,7 @@ extension MutableSpan where Element: ~Copyable { mutating func swapAt(_ i: Index, _ j: Index) /// Borrow the underlying memory for read-only access - var span: Span { borrowing get } + var span: Span { @_lifetime(borrow self) borrowing get } } ``` @@ -118,99 +164,164 @@ for i in myMutableSpan.indices { } ``` -##### Unchecked access to elements: +##### Bulk updates of a `MutableSpan`'s elements: -The `subscript` mentioned above always checks the bounds of the `MutableSpan` before allowing access to the memory, preventing out-of-bounds accesses. We also provide an unchecked variant of the `subscript` and of the `swapAt` function as an alternative for situations where bounds-checking is costly and has already been performed: +We include functions to perform bulk copies of elements into the memory represented by a `MutableSpan`. Updating a `MutableSpan` from known-sized sources (such as `Collection` or `Span`) copies every element of a source. It is an error to do so when there is the span is too short to contain every element from the source. Updating a `MutableSpan` from `Sequence` or `IteratorProtocol` instances will copy as many items as possible, either until the input is empty or until the operation has updated the item at the last index. The bulk operations return the index following the last element updated. ```swift -extension MutableSpan where Element: ~Copyable { - /// Accesses the element at the specified `position`. - /// - /// This subscript does not validate `position`; this is an unsafe operation. - /// - /// - Parameter position: The offset of the element to access. `position` - /// must be greater or equal to zero, and less than `count`. - @unsafe - subscript(unchecked position: Index) -> Element { borrow; mutate } - - /// Exchange the elements at the two given offsets - /// - /// This function does not validate `i` or `j`; this is an unsafe operation. - @unsafe - mutating func swapAt(unchecked i: Index, unchecked j: Index) -} -``` -##### Bulk updating of a `MutableSpan`'s elements: - -We include functions to perform bulk copies of elements into the memory represented by a `MutableSpan`. Updating a `MutableSpan` from known-sized sources (such as `Collection` or `Span`) copies every element of a source. It is an error to do so when there is the span is too short to contain every element from the source. Updating a `MutableSpan` from `Sequence` or `IteratorProtocol` instances will copy as many items as possible, either until the input is empty or until the operation has updated the item at the last index. - -**Note:** This set of functions is sufficiently complete in functionality, but uses a minimal approach to slicing. This is only one of many possible approaches to slicing `MutableSpan`. We could revive the option of using a `some RangeExpression` parameter, or we could use the return value of a `func extracting(_: some RangeExpression)` such as was [recently added][SE-0437] to `UnsafeBufferPointer`. The latter option in combination with `mutating` functions requires the use of intermediate bindings. This section may change in response to feedback and our investigations. - -```swift -extension MutableSpan { +extension MutableSpan where Element: Copyable{ + /// Updates every element of this span's to the given value. mutating func update( - startingAt offset: Index = 0, repeating repeatedValue: Element ) - + + /// Updates the span's elements with the elements from the source mutating func update( - startingAt offset: Index = 0, from source: S ) -> (unwritten: S.Iterator, index: Index) where S.Element == Element + /// Updates the span's elements with the elements from the source mutating func update( - startingAt offset: Index = 0, - from elements: inout some IteratorProtocol + from source: inout some IteratorProtocol ) -> Index + /// Updates the span's elements with every element of the source. mutating func update( - startingAt offset: Index = 0, fromContentsOf source: some Collection ) -> Index - +} + +extension MutableSpan where Element: ~Copyable + /// Updates the span's elements with every element of the source. mutating func update( - startingAt offset: Index = 0, fromContentsOf source: Span ) -> Index + /// Updates the span's elements with every element of the source. mutating func update( - startingAt offset: Index = 0, - fromContentsOf source: borrowing Self + fromContentsOf source: borrowing MutableSpan ) -> Index -} - -extension MutableSpan where Element: ~Copyable { + + /// Updates the span's elements with every element of the source, + /// leaving the source uninitialized. mutating func moveUpdate( - startingAt offset: Index = 0, fromContentsOf source: UnsafeMutableBufferPointer ) -> Index } -extension MutableSpan { +extension MutableSpan where Element: Copyable { + /// Updates the span's elements with every element of the source, + /// leaving the source uninitialized. mutating func moveUpdate( - startingAt offset: Index = 0, fromContentsOf source: Slice> ) -> Index } ``` + +##### Extracting sub-spans +These functions extract sub-spans of the callee. The first two perform strict bounds-checking. The last four return prefixes or suffixes, where the number of elements in the returned sub-span is bounded by the number of elements in the parent `MutableSpan`. + +```swift +extension MutableSpan where Element: ~Copable & ~Escapable { + /// Returns a span over the items within the supplied range of + /// positions within this span. + @_lifetime(inout self) + mutating public func extracting(_ bounds: Range) -> Self + + /// Returns a span over the items within the supplied range of + /// positions within this span. + @_lifetime(inout self) + mutating public func extracting(_ bounds: some RangeExpression) -> Self + + /// Returns a span containing the initial elements of this span, + /// up to the specified maximum length. + @_lifetime(inout self) + mutating public func extracting(first maxLength: Int) -> Self + + /// Returns a span over all but the given number of trailing elements. + @_lifetime(inout self) + mutating public func extracting(droppingLast k: Int) -> Self + + /// Returns a span containing the final elements of the span, + /// up to the given maximum length. + @_lifetime(inout self) + mutating public func extracting(last maxLegnth: Int) -> Self + + /// Returns a span over all but the given number of initial elements. + @_lifetime(inout self) + mutating public func extracting(droppingFirst k: Int) -> Self +} +``` + +##### Unchecked access to elements or sub-spans: + +The `subscript` and index-taking functions mentioned above always check the bounds of the `MutableSpan` before allowing access to the memory, preventing out-of-bounds accesses. We also provide unchecked variants of the `subscript`, the `swapAt()` and `extracting()` functions as alternatives in situations where repeated bounds-checking is costly and has already been performed: + +```swift +extension MutableSpan where Element: ~Copyable { + /// Accesses the element at the specified `position`. + /// + /// This subscript does not validate `position`; this is an unsafe operation. + /// + /// - Parameter position: The offset of the element to access. `position` + /// must be greater or equal to zero, and less than `count`. + @unsafe + subscript(unchecked position: Index) -> Element { borrow; mutate } + + /// Exchange the elements at the two given offsets + /// + /// This function does not validate `i` or `j`; this is an unsafe operation. + @unsafe + mutating func swapAt(unchecked i: Index, unchecked j: Index) + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// This function does not validate `bounds`; this is an unsafe operation. + @unsafe + @_lifetime(inout self) + mutating func extracting(unchecked bounds: Range) -> Self + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// This function does not validate `bounds`; this is an unsafe operation. + @unsafe + @_lifetime(inout self) + mutating func extracting(unchecked bounds: ClosedRange) -> Self +} +``` + + ##### Interoperability with unsafe code: ```swift extension MutableSpan where Element: ~Copyable { + /// Calls a closure with a pointer to the viewed contiguous storage. func withUnsafeBufferPointer( _ body: (_ buffer: UnsafeBufferPointer) throws(E) -> Result ) throws(E) -> Result + /// Calls a closure with a pointer to the viewed mutable contiguous + /// storage. mutating func withUnsafeMutableBufferPointer( _ body: (_ buffer: UnsafeMutableBufferPointer) throws(E) -> Result ) throws(E) -> Result } extension MutableSpan where Element: BitwiseCopyable { + /// Calls a closure with a pointer to the underlying bytes of + /// the viewed contiguous storage. func withUnsafeBytes( _ body: (_ buffer: UnsafeRawBufferPointer) throws(E) -> Result ) throws(E) -> Result + /// Calls a closure with a pointer to the underlying bytes of + /// the viewed mutable contiguous storage. + /// + /// Note: mutating the bytes may result in the violation of + /// invariants in the internal representation of `Element` + @unsafe mutating func withUnsafeMutableBytes( _ body: (_ buffer: UnsafeMutableRawBufferPointer) throws(E) -> Result ) throws(E) -> Result @@ -255,10 +366,14 @@ extension MutableRawSpan { ```swift extension MutableRawSpan { + /// Stores the given value's bytes into raw memory at the specified offset. mutating func storeBytes( of value: T, toByteOffset offset: Int = 0, as type: T.Type ) + /// Stores the given value's bytes into raw memory at the specified offset. + /// + /// This function does not validate `offset`; this is an unsafe operation. @unsafe mutating func storeBytes( of value: T, toUncheckedByteOffset offset: Int, as type: T.Type @@ -270,21 +385,29 @@ Additionally, the basic loading operations available on `RawSpan` are available ```swift extension MutableRawSpan { + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. @unsafe func unsafeLoad( fromByteOffset offset: Int = 0, as: T.Type ) -> T + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. @unsafe func unsafeLoadUnaligned( fromByteOffset offset: Int = 0, as: T.Type ) -> T + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. @unsafe func unsafeLoad( fromUncheckedByteOffset offset: Int, as: T.Type ) -> T + /// Returns a new instance of the given type, constructed from the raw memory + /// at the specified offset. @unsafe func unsafeLoadUnaligned( fromUncheckedByteOffset offset: Int, as: T.Type @@ -294,55 +417,116 @@ extension MutableRawSpan { We include functions to perform bulk copies into the memory represented by a `MutableRawSpan`. Updating a `MutableRawSpan` from a `Collection` or a `Span` copies every element of a source. It is an error to do so when there is are not enough bytes in the span to contain every element from the source. Updating `MutableRawSpan` from `Sequence` or `IteratorProtocol` instance copies as many items as possible, either until the input is empty or until there are not enough bytes in the span to store another element. -**Note:** This set of functions is sufficiently complete in functionality, but uses a minimal approach to slicing. This is only one of many possible approaches to slicing `MutableRawSpan`. (See the note above for more details on the same considerations.) - ```swift extension MutableRawSpan { + /// Updates the span's bytes with the bytes of the elements from the source mutating func update( - startingAt byteOffset: Int = 0, from source: S ) -> (unwritten: S.Iterator, byteOffset: Int) where S.Element: BitwiseCopyable + /// Updates the span's bytes with the bytes of the elements from the source mutating func update( - startingAt byteOffset: Int = 0, from elements: inout some IteratorProtocol ) -> Int + /// Updates the span's bytes with every byte of the source. mutating func update( - startingAt byteOffset: Int = 0, fromContentsOf source: C ) -> Int where C.Element: BitwiseCopyable + /// Updates the span's bytes with every byte of the source. mutating func update( - startingAt byteOffset: Int = 0, fromContentsOf source: Span ) -> Int + /// Updates the span's bytes with every byte of the source. mutating func update( - startingAt byteOffset: Int = 0, fromContentsOf source: borrowing MutableSpan ) -> Int + /// Updates the span's bytes with every byte of the source. mutating func update( - startingAt byteOffset: Int = 0, fromContentsOf source: RawSpan ) -> Int + /// Updates the span's bytes with every byte of the source. mutating func update( - startingAt byteOffset: Int = 0, fromContentsOf source: borrowing MutableRawSpan ) -> Int } ``` +##### Extracting sub-spans +These functions extract sub-spans of the callee. The first two perform strict bounds-checking. The last four return prefixes or suffixes, where the number of elements in the returned sub-span is bounded by the number of elements in the parent `MutableRawSpan`. + +```swift +extension MutableRawSpan { + /// Returns a span over the items within the supplied range of + /// positions within this span. + @_lifetime(inout self) + mutating public func extracting(_ byteOffsets: Range) -> Self + + /// Returns a span over the items within the supplied range of + /// positions within this span. + @_lifetime(inout self) + mutating public func extracting(_ byteOffsets: some RangeExpression) -> Self + + /// Returns a span containing the initial elements of this span, + /// up to the specified maximum length. + @_lifetime(inout self) + mutating public func extracting(first maxLength: Int) -> Self + + /// Returns a span over all but the given number of trailing elements. + @_lifetime(inout self) + mutating public func extracting(droppingLast k: Int) -> Self + + /// Returns a span containing the final elements of the span, + /// up to the given maximum length. + @_lifetime(inout self) + mutating public func extracting(last maxLegnth: Int) -> Self + + /// Returns a span over all but the given number of initial elements. + @_lifetime(inout self) + mutating public func extracting(droppingFirst k: Int) -> Self +} +``` + +We also provide unchecked variants of the `extracting()` functions as alternatives in situations where repeated bounds-checking is costly and has already been performed: + +```swift +extension MutableRawSpan { + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// This function does not validate `byteOffsets`; this is an unsafe operation. + @unsafe + @_lifetime(inout self) + mutating func extracting(unchecked byteOffsets: Range) -> Self + + /// Constructs a new span over the items within the supplied range of + /// positions within this span. + /// + /// This function does not validate `byteOffsets`; this is an unsafe operation. + @unsafe + @_lifetime(inout self) + mutating func extracting(unchecked byteOffsets: ClosedRange) -> Self +} +``` + + + ##### Interoperability with unsafe code: ```swift extension MutableRawSpan { + /// Calls a closure with a pointer to the underlying bytes of + /// the viewed contiguous storage. func withUnsafeBytes( _ body: (_ buffer: UnsafeRawBufferPointer) throws(E) -> Result ) throws(E) -> Result + /// Calls a closure with a pointer to the underlying bytes of + /// the viewed mutable contiguous storage. mutating func withUnsafeMutableBytes( _ body: (_ buffer: UnsafeMutableRawBufferPointer) throws(E) -> Result ) throws(E) -> Result @@ -350,57 +534,69 @@ extension MutableRawSpan { ``` These functions use a closure to define the scope of validity of `buffer`, ensuring that the underlying `MutableSpan` and the binding it depends on both remain valid through the end of the closure. They have the same shape as the equivalents on `Array` because they fulfill the same purpose, namely to keep the underlying binding alive. +#### Properties providing `MutableSpan` or `MutableRawSpan` instances + ##### Accessing and mutating the raw bytes of a `MutableSpan` +When a `MutableSpan`'s element is `BitwiseCopyable`, we allow mutations of the underlying storage as raw bytes, as a `MutableRawSpan`. + ```swift extension MutableSpan where Element: BitwiseCopyable { - var mutableBytes: MutableRawSpan { mutating get } + /// Access the underlying raw bytes of this `MutableSpan`'s elements + /// + /// Note: mutating the bytes may result in the violation of + /// invariants in the internal representation of `Element` + @unsafe + var mutableBytes: MutableRawSpan { @_lifetime(inout self) mutating get } } ``` +The standard library will provide `mutating` computed properties providing lifetime-dependent `MutableSpan` instances. These `mutableSpan` computed properties are intended as the safe and composable replacements for the existing `withUnsafeMutableBufferPointer` closure-taking functions. - -#### Extensions to Standard Library types - -A `mutating` computed property getter defined on any type and returning a `~Escapable & ~Copyable` value establishes an exclusive borrowing lifetime relationship of the returned value on the callee's binding. As long as the returned value exists, then the callee's binding remains borrowed and cannot be accessed in any other way. - -A `nonmutating` computed property getter returning a `~Escapable & ~Copyable` value establishes a borrowing lifetime relationship, as if returning a `~Escapable & Copyable` value (see [SE-0456][SE-0456].) - -The standard library will provide `mutating` computed properties providing lifetime-dependent `MutableSpan` instances. These `mutableSpan` computed properties are intended as the safe and composable replacements for the existing `withUnsafeMutableBufferPointer` closure-taking functions. +##### Extensions to Standard Library types ```swift extension Array { - var mutableSpan: MutableSpan { mutating get } + /// Access this Array's elements as mutable contiguous storage. + var mutableSpan: MutableSpan { @_lifetime(inout self) mutating get } } extension ContiguousArray { - var mutableSpan: MutableSpan { mutating get } + /// Access this Array's elements as mutable contiguous storage. + var mutableSpan: MutableSpan { @_lifetime(inout self) mutating get } } extension ArraySlice { - var mutableSpan: MutableSpan { mutating get } + /// Access this Array's elements as mutable contiguous storage. + var mutableSpan: MutableSpan { @_lifetime(inout self) mutating get } } extension InlineArray { - var mutableSpan: MutableSpan { mutating get } + /// Access this Array's elements as mutable contiguous storage. + var mutableSpan: MutableSpan { @_lifetime(inout self) mutating get } } extension CollectionOfOne { - var mutableSpan: MutableSpan { mutating get } + /// Access this Collection's element as mutable contiguous storage. + var mutableSpan: MutableSpan { @_lifetime(inout self) mutating get } } ``` -#### Extensions to unsafe buffer types +##### Extensions to unsafe buffer types -We hope that `MutableSpan` and `MutableRawSpan` will become the standard ways to delegate mutations of shared contiguous memory in Swift. Many current API delegate mutations with closure-based functions that receive an `UnsafeMutableBufferPointer` parameter to do this. We will provide ways to unsafely obtain `MutableSpan` instances from `UnsafeMutableBufferPointer` and `MutableRawSpan` instances from `UnsafeMutableRawBufferPointer`, in order to bridge these unsafe types to newer, safer contexts. +We hope that `MutableSpan` and `MutableRawSpan` will become the standard ways to delegate mutations of shared contiguous memory in Swift. Many current API delegate mutations via closure-based functions that receive an `UnsafeMutableBufferPointer` parameter. We will provide ways to unsafely obtain `MutableSpan` instances from `UnsafeMutableBufferPointer` and `MutableRawSpan` instances from `UnsafeMutableRawBufferPointer`, in order to bridge these unsafe types to newer, safer contexts. ```swift extension UnsafeMutableBufferPointer { - var mutableSpan: MutableSpan { get } + /// Unsafely access this buffer as a MutableSpan + @unsafe + var mutableSpan: MutableSpan { @_lifetime(borrow self) get } } extension UnsafeMutableRawBufferPointer { - var mutableBytes: MutableRawSpan { get } + /// Unsafely access this buffer as a MutableRawSpan + @unsafe + var mutableBytes: MutableRawSpan { @_lifetime(borrow self) get } } ``` @@ -411,23 +607,23 @@ These unsafe conversions returns a value whose lifetime is dependent on the _bin Failure to maintain these invariants results in undefined behaviour. -#### Extensions to `Foundation.Data` +##### Extensions to `Foundation.Data` While the `swift-foundation` package and the `Foundation` framework are not governed by the Swift evolution process, `Data` is similar in use to standard library types, and the project acknowledges that it is desirable for it to have similar API when appropriate. Accordingly, we plan to propose the following additions to `Foundation.Data`: ```swift extension Foundation.Data { - // Mutate this `Data`'s bytes through a `MutableSpan` - var mutableSpan: MutableSpan { mutating get } + // Access this instance's bytes as mutable contiguous storage + var mutableSpan: MutableSpan { @_lifetime(inout self) mutating get } - // Mutate this `Data`'s bytes through a `MutableRawSpan` - var mutableBytes: MutableRawSpan { mutating get } + // Access this instance's bytes as mutable contiguous bytes + var mutableBytes: MutableRawSpan { @_lifetime(inout self) mutating get } } ``` #### Performance -The `mutableSpan` and `mutableBytes` properties should be performant and return their `MutableSpan` or `MutableRawSpan` with very little work, in O(1) time. In copy-on-write types, however, obtaining a `MutableSpan` is the start of the mutation, and if the backing buffer is not uniquely reference a copy must be made ahead of returning the `MutableSpan`. +The `mutableSpan` and `mutableBytes` properties should be performant and return their `MutableSpan` or `MutableRawSpan` with very little work, in O(1) time. In copy-on-write types, however, obtaining a `MutableSpan` is the start of the mutation. When the backing buffer is not uniquely referenced then a full copy must be made ahead of returning the `MutableSpan`. Note that `MutableSpan` incurs no special behaviour for bridged types, since mutable bindings always require a defensive copy of data bridged from Objective-C data structures. @@ -509,11 +705,18 @@ Unfortunately, tuples do not support non-copyable values yet. We may be able to #### Mutating algorithms -Algorithms defined on `MutableCollection` such as `sort(by:)` and `partition(by:)` could be defined on `MutableSpan`. We believe we will be able to define these more generally once we have a generalized container protocol hierarchy. +Algorithms defined on `MutableCollection` such as `sort(by:)` and `partition(by:)` could be defined on `MutableSpan`. We believe we will be able to define these more generally once we have a generalized container protocol hierarchy. + +#### Exclusive Access + +The `mutating` functions in this proposal generally do not represent mutations of the binding itself, but of memory being referenced. `mutating` is necessary in order to model the necessary exclusive access to the memory. We could conceive of an access level between "shared" (`let`) and "exclusive" (`var`) that would model an exclusive access while allowing the pointer and count information to be stored in registers. + +#### Harmonizing `extracting()` functions across types + +The range of `extracting()` functions proposed here expands upon the range accepted in [SE-0437][SE-0437]. If the prefix and suffix variants are accepted, we should add them to `UnsafeBufferPointer` types as well. `Span` and `RawSpan` should also have `extracting()` functions with appropriate lifetime dependencies. #### Delegated initialization with `OutputSpan` Some data structures can delegate initialization of parts of their owned memory. The standard library added the `Array` initializer `init(unsafeUninitializedCapacity:initializingWith:)` in [SE-0223][SE-0223]. This initializer relies on `UnsafeMutableBufferPointer` and correct usage of initialization primitives. We should present a simpler and safer model of initialization by leveraging non-copyability and non-escapability. -We expect to propose an `OutputSpan` type to represent partially-initialized memory, and to support to the initialization of memory by appending to the initialized portion of the underlying storage. - +We expect to propose an `OutputSpan` type to represent partially-initialized memory, and to support to the initialization of memory by appending to the initialized portion of the underlying storage. From 5340db1e42756b6738efaaee3ef9a90adedeb262 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 11 Mar 2025 13:34:11 -0700 Subject: [PATCH 4141/4563] editorial fixes --- proposals/nnnn-MutableSpan.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/nnnn-MutableSpan.md b/proposals/nnnn-MutableSpan.md index fce33b83e7..803fdd0424 100644 --- a/proposals/nnnn-MutableSpan.md +++ b/proposals/nnnn-MutableSpan.md @@ -37,7 +37,7 @@ Mutability requires exclusive access, per Swift's [law of exclusivity][SE-0176]. #### MutableSpan -`MutableSpan` allows delegating mutations of a type's contiguous internal representation, by providing access to an exclusively-borrowed view of a range of contiguous, initialized memory. `MutableSpan`'s memory safety' relies on guarantees that: +`MutableSpan` allows delegating mutations of a type's contiguous internal representation, by providing access to an exclusively-borrowed view of a range of contiguous, initialized memory. `MutableSpan`'s memory safety relies on guarantees that: - it has exclusive access to the range of memory it represents, providing data race safety and enforced by `~Copyable`. - the memory it represents will remain valid for the duration of the access, providing lifetime safety and enforced by `~Escapable`. - each access is guarded by bounds checking, providing bounds safety. @@ -90,7 +90,7 @@ extension MutableSpan where Element: ~Copyable & ~Escapable { } ``` -This function returns an instance of `MutableSpan` that represents a mutation of the same memory as represented by the callee. The callee can therefore no longer be mutated while the returned value exists: +This function returns an instance of `MutableSpan` that represents a mutation of the same memory as represented by the callee. The callee can therefore no longer be accessed (read or mutated) while the returned value exists: ```swift var array = [1, 2, 3, 4, 5] @@ -713,7 +713,7 @@ The `mutating` functions in this proposal generally do not represent mutations o #### Harmonizing `extracting()` functions across types -The range of `extracting()` functions proposed here expands upon the range accepted in [SE-0437][SE-0437]. If the prefix and suffix variants are accepted, we should add them to `UnsafeBufferPointer` types as well. `Span` and `RawSpan` should also have `extracting()` functions with appropriate lifetime dependencies. +The range of `extracting()` functions proposed here expands upon the range accepted in [SE-0437][SE-0437]. If the prefix and suffix variants are accepted, we should add them to `UnsafeBufferPointer` types as well. `Span` and `RawSpan` should also have `extracting()` functions with appropriate lifetime dependencies. #### Delegated initialization with `OutputSpan` From b15d39a8d9def33623bf904f2bd1892b5500550c Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 11 Mar 2025 17:13:37 -0700 Subject: [PATCH 4142/4563] more typos --- proposals/nnnn-MutableSpan.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/proposals/nnnn-MutableSpan.md b/proposals/nnnn-MutableSpan.md index 803fdd0424..7ba38b7a00 100644 --- a/proposals/nnnn-MutableSpan.md +++ b/proposals/nnnn-MutableSpan.md @@ -96,10 +96,12 @@ This function returns an instance of `MutableSpan` that represents a mutation of var array = [1, 2, 3, 4, 5] var span1 = array.mutableSpan var span2 = span1.extracting(3..<5) -// span1 cannot be accessed here +// neither array nor span1 can be accessed here span2.swapAt(0, 1) _ = consume span2 // explicitly end scope for `span2` -print(array) // [1, 2, 3, 5, 4] +span1.swapAt(0, 1) +_ = consume span1 // explicitly end scope for `span1` +print(array) // [2, 1, 3, 5, 4] ``` As established in [SE-0437][SE-0437], the instance returned by the `extracting()` function does not share indices with the function's callee. @@ -169,7 +171,7 @@ for i in myMutableSpan.indices { We include functions to perform bulk copies of elements into the memory represented by a `MutableSpan`. Updating a `MutableSpan` from known-sized sources (such as `Collection` or `Span`) copies every element of a source. It is an error to do so when there is the span is too short to contain every element from the source. Updating a `MutableSpan` from `Sequence` or `IteratorProtocol` instances will copy as many items as possible, either until the input is empty or until the operation has updated the item at the last index. The bulk operations return the index following the last element updated. ```swift -extension MutableSpan where Element: Copyable{ +extension MutableSpan where Element: Copyable { /// Updates every element of this span's to the given value. mutating func update( repeating repeatedValue: Element @@ -179,12 +181,12 @@ extension MutableSpan where Element: Copyable{ mutating func update( from source: S ) -> (unwritten: S.Iterator, index: Index) where S.Element == Element - + /// Updates the span's elements with the elements from the source mutating func update( from source: inout some IteratorProtocol ) -> Index - + /// Updates the span's elements with every element of the source. mutating func update( fromContentsOf source: some Collection @@ -196,7 +198,7 @@ extension MutableSpan where Element: ~Copyable mutating func update( fromContentsOf source: Span ) -> Index - + /// Updates the span's elements with every element of the source. mutating func update( fromContentsOf source: borrowing MutableSpan @@ -245,7 +247,7 @@ extension MutableSpan where Element: ~Copable & ~Escapable { /// Returns a span containing the final elements of the span, /// up to the given maximum length. @_lifetime(inout self) - mutating public func extracting(last maxLegnth: Int) -> Self + mutating public func extracting(last maxLength: Int) -> Self /// Returns a span over all but the given number of initial elements. @_lifetime(inout self) @@ -292,8 +294,7 @@ extension MutableSpan where Element: ~Copyable { } ``` - -##### Interoperability with unsafe code: +##### Interoperability with unsafe code ```swift extension MutableSpan where Element: ~Copyable { @@ -457,6 +458,7 @@ extension MutableRawSpan { ``` ##### Extracting sub-spans + These functions extract sub-spans of the callee. The first two perform strict bounds-checking. The last four return prefixes or suffixes, where the number of elements in the returned sub-span is bounded by the number of elements in the parent `MutableRawSpan`. ```swift @@ -513,8 +515,6 @@ extension MutableRawSpan { } ``` - - ##### Interoperability with unsafe code: ```swift @@ -697,11 +697,11 @@ It is desirable to have a way to split a `MutableSpan` in multiple parts, for di ```swift extension MutableSpan where Element: ~Copyable { - func split(at index: Index) -> (part1: Self, part2: Self) + public mutating func split(at index: Index) -> (part1: Self, part2: Self) } ``` -Unfortunately, tuples do not support non-copyable values yet. We may be able to use `InlineArray` ([SE-0453][SE-0453]), or a bespoke type, but destructuring the non-copyable constituent part remains a challenge. Solving this issue for `Span` and `MutableSpan` is a top priority. +Unfortunately, tuples do not support non-copyable or non-escapable values yet. We may be able to use `InlineArray` ([SE-0453][SE-0453]), or a bespoke type, but destructuring the non-copyable constituent part remains a challenge. Solving this issue for `Span` and `MutableSpan` is a top priority. #### Mutating algorithms From 7a3ed493f68ed49770fa932173d35960cb2784dd Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 11 Mar 2025 17:16:05 -0700 Subject: [PATCH 4143/4563] christen MutableSpan as SE-0467 and begin review --- proposals/{nnnn-MutableSpan.md => 0467-MutableSpan.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{nnnn-MutableSpan.md => 0467-MutableSpan.md} (99%) diff --git a/proposals/nnnn-MutableSpan.md b/proposals/0467-MutableSpan.md similarity index 99% rename from proposals/nnnn-MutableSpan.md rename to proposals/0467-MutableSpan.md index 7ba38b7a00..0ed4faecc7 100644 --- a/proposals/nnnn-MutableSpan.md +++ b/proposals/0467-MutableSpan.md @@ -1,12 +1,12 @@ # MutableSpan and MutableRawSpan: delegate mutations of contiguous memory -* Proposal: TBD +* Proposal: [SE-0467](0467-MutableSpan.md) * Author: [Guillaume Lessard](https://github.com/glessard) -* Review Manager: TBD -* Status: **Pitch** +* Review Manager: [Joe Groff](https://github.com/jckarter) +* Status: **Active review (March 11...25, 2025)** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: "Future" target of [swift-collections](https://github.com/apple/swift-collections/tree/future) -* Review: [Pitch](https://forums.swift.org/) +* Review: [Pitch](https://forums.swift.org/t/pitch-mutablespan/77790) [SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md [SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md From 5022e3e1266f2e1783a595932015e51f7871cdf6 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 12 Mar 2025 15:17:44 -0400 Subject: [PATCH 4144/4563] SE-0451 implementation has been merged, will be in 6.2. --- proposals/0451-escaped-identifiers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0451-escaped-identifiers.md b/proposals/0451-escaped-identifiers.md index 6971096741..639a1e1a9b 100644 --- a/proposals/0451-escaped-identifiers.md +++ b/proposals/0451-escaped-identifiers.md @@ -3,7 +3,7 @@ * Proposal: [SE-0451](0451-escaped-identifiers.md) * Author: [Tony Allevato](https://github.com/allevato) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Implementation: [swiftlang/swift#76636](https://github.com/swiftlang/swift/pull/76636), [swiftlang/swift-syntax#2857](https://github.com/swiftlang/swift-syntax/pull/2857) * Previous Proposal: [SE-0275](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0275-allow-more-characters-like-whitespaces-and-punctuations-for-escaped-identifiers.md) * Review: ([pitch](https://forums.swift.org/t/pitch-revisiting-backtick-delimited-identifiers-that-allow-more-non-identifier-characters/74432), [review](https://forums.swift.org/t/se-0451-raw-identifiers/75602), [acceptance](https://forums.swift.org/t/accepted-with-revision-se-0451-raw-identifiers/76387)) From dd024db6d983d5b15cd7995920a9d321a89c19d0 Mon Sep 17 00:00:00 2001 From: "Mykola (Nickolas) Pokhylets" Date: Thu, 13 Mar 2025 02:39:26 +0100 Subject: [PATCH 4145/4563] Proposal to add `Hashable` conformance to `Async(Throwing)Stream.Continuation` (#2700) * Proposal to add `Hashable` conformance to `Async(Throwing)Stream.Continuation` * Apply suggestions from code review Co-authored-by: Frederick Kellison-Linn * Apply review comments * Removed backdeployment considerations * Continuation hashable conformance is 0468 --------- Co-authored-by: Frederick Kellison-Linn Co-authored-by: Freddy Kellison-Linn --- ...tream-continuation-hashable-conformance.md | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 proposals/0468-async-stream-continuation-hashable-conformance.md diff --git a/proposals/0468-async-stream-continuation-hashable-conformance.md b/proposals/0468-async-stream-continuation-hashable-conformance.md new file mode 100644 index 0000000000..00791b140f --- /dev/null +++ b/proposals/0468-async-stream-continuation-hashable-conformance.md @@ -0,0 +1,130 @@ +# `Hashable` conformance for `Async(Throwing)Stream.Continuation` + +* Proposal: [SE-0468](0468-async-stream-continuation-hashable-conformance.md) +* Authors: [Mykola Pokhylets](https://github.com/nickolas-pohilets) +* Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) +* Status: **Active review (March 12...25, 2025)** +* Implementation: [swiftlang/swift#79457](https://github.com/swiftlang/swift/pull/79457) +* Review: ([pitch](https://forums.swift.org/t/pitch-add-hashable-conformance-to-asyncstream-continuation/77897)) + +## Introduction + +This proposal adds a `Hashable` conformance to `Async(Throwing)Stream.Continuation` +to simplify working with multiple streams. + +## Motivation + +Use cases operating with multiple `AsyncStream`s may need to store multiple continuations. +When handling `onTermination` callback, client code needs to remove the relevant continuation. + +To identify the relevant continuation, client code needs to be able to compare continuations. + +It is possible to associate a lookup key with each continuation, but this is inefficient. +`AsyncStream.Continuation` already stores a reference to `AsyncStream._Storage`, +whose identity can be used to provide simple and efficient `Hashable` conformance. + +Consider this simple Observer pattern with an `AsyncSequence`-based API. +To avoid implementing `AsyncSequence` from scratch it uses `AsyncStream` as a building block. +To support multiple subscribers, a new stream is returned every time. + +```swift +@MainActor private class Sender { + var value: Int = 0 { + didSet { + for c in continuations { + c.yield(value) + } + } + } + + var values: some AsyncSequence { + AsyncStream(bufferingPolicy: .bufferingNewest(1)) { continuation in + continuation.yield(value) + self.continuations.insert(continuation) + continuation.onTermination = { _ in + DispatchQueue.main.async { + self.continuations.remove(continuation) + } + } + } + } + + private var continuations: Set.Continuation> = [] +} +``` + +Without a `Hashable` conformance, each continuation needs to be associated with an artificial identifier. +E.g. wrapping continuation in a class, identity of the wrapper object can be used: + +```swift +@MainActor private class Sender { + var value: Int = 0 { + didSet { + for c in continuations { + c.value.yield(value) + } + } + } + + var values: some AsyncSequence { + AsyncStream { (continuation: AsyncStream.Continuation) -> Void in + continuation.yield(value) + let box = ContinuationBox(value: continuation) + self.continuations.insert(box) + continuation.onTermination = { _ in + DispatchQueue.main.async { + self.continuations.remove(box) + } + } + } + } + + private var continuations: Set = [] + + private final class ContinuationBox: Hashable, Sendable { + let value: AsyncStream.Continuation + + init(value: AsyncStream.Continuation) { + self.value = value + } + + static func == (lhs: Sender.ContinuationBox, rhs: Sender.ContinuationBox) -> Bool { + lhs === rhs + } + + func hash(into hasher: inout Hasher) { + hasher.combine(ObjectIdentifier(self)) + } + } +} +``` + +Note that capturing `continuation` or `box` in `onTermination` is safe, because `onTermination` is dropped after being called +(and it is _always_ called, even if `AsyncStream` is discarded without being iterated). + +## Proposed solution + +Add a `Hashable` conformance to `Async(Throwing)Stream.Continuation`. + +## Detailed design + +Every time when the `build` closure of the `Async(Throwing)Stream.init()` is called, +it receives a continuation distinct from all other continuations. +All copies of the same continuation should compare equal. +Yielding values or errors, finishing the stream, or cancelling iteration should not affect equality. +Assigning `onTermination` closures should not affect equality. + +## Source compatibility + +This is an additive change. + +Retroactive conformances are unlikely to exist, because current public API of the `Async(Throwing)Stream.Continuation` +does not provide anything that could be reasonably used to implement `Hashable` or `Equatable` conformances. + +## ABI compatibility + +This is an additive change. + +## Implications on adoption + +Adopters will need a new version of the standard library. From f492f715dec6fd035debab894f41e81d1464a662 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 12 Mar 2025 22:00:51 -0400 Subject: [PATCH 4146/4563] Update 0462-task-priority-escalation-apis.md (#2735) --- proposals/0462-task-priority-escalation-apis.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0462-task-priority-escalation-apis.md b/proposals/0462-task-priority-escalation-apis.md index 1f270b4150..5174719ca9 100644 --- a/proposals/0462-task-priority-escalation-apis.md +++ b/proposals/0462-task-priority-escalation-apis.md @@ -3,9 +3,9 @@ * Proposal: [SE-0462](0462-task-priority-escalation-apis.md) * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: [Freddy Kellison-Linn](https://github.com/jumhyn) -* Status: **Active review (February 20...March 2, 2025)** +* Status: **Accepted with modifications** * Implementation: https://github.com/swiftlang/swift/pull/78625 -* Review: ([pitch](https://forums.swift.org/t/pitch-task-priority-escalation-apis/77702)) ([review](https://forums.swift.org/t/se-0462-task-priority-escalation-apis/77997)) +* Review: ([pitch](https://forums.swift.org/t/pitch-task-priority-escalation-apis/77702)) ([review](https://forums.swift.org/t/se-0462-task-priority-escalation-apis/77997))([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0462-task-priority-escalation-apis/78488)) ## Introduction From 834651da2bdc2d578a9fd2c4db47ab50b719594c Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 13 Mar 2025 01:05:40 -0400 Subject: [PATCH 4147/4563] Accept SE-0463 --- proposals/0463-sendable-completion-handlers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0463-sendable-completion-handlers.md b/proposals/0463-sendable-completion-handlers.md index 3200e21039..498ab8ba80 100644 --- a/proposals/0463-sendable-completion-handlers.md +++ b/proposals/0463-sendable-completion-handlers.md @@ -3,10 +3,10 @@ * Proposal: [SE-0463](0463-sendable-completion-handlers.md) * Authors: [Holly Borla](https://github.com/hborla) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (February 27th...March 10th, 2025)** +* Status: **Accepted** * Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md) * Implementation: On `main` behind `-enable-experimental-feature SendableCompletionHandlers` -* Review: ([pitch](https://forums.swift.org/t/pitch-import-objective-c-completion-handler-parameters-as-sendable/77904)) ([review](https://forums.swift.org/t/se-0463-import-objective-c-completion-handler-parameters-as-sendable/78169)) +* Review: ([pitch](https://forums.swift.org/t/pitch-import-objective-c-completion-handler-parameters-as-sendable/77904)) ([review](https://forums.swift.org/t/se-0463-import-objective-c-completion-handler-parameters-as-sendable/78169)) ([acceptance](https://forums.swift.org/t/accepted-se-0463-import-objective-c-completion-handler-parameters-as-sendable/78489)) ## Introduction From ceabe4683d8914afb67edd8e3af876cc8210c2c2 Mon Sep 17 00:00:00 2001 From: Harjas Monga Date: Thu, 13 Mar 2025 15:09:01 -0700 Subject: [PATCH 4148/4563] Swift Task Naming API (#2619) * Initial Draft * API Renaming * Update to some APIs * Update APIs a little bit * make snippets swift syntax * Link to implementation, and startSynchronously proposal * Add unsafeCurrentTask accessor to proposal * Update 0454-task-names.md * remove the nil default * Update proposal header and assign SE number to task naming. --------- Co-authored-by: Konrad `ktoso` Malawski Co-authored-by: Holly Borla --- proposals/0469-task-names.md | 272 +++++++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 proposals/0469-task-names.md diff --git a/proposals/0469-task-names.md b/proposals/0469-task-names.md new file mode 100644 index 0000000000..20d872fe89 --- /dev/null +++ b/proposals/0469-task-names.md @@ -0,0 +1,272 @@ +# Task Naming + +* Proposal: [SE-0469](0469-task-names.md) +* Authors: [Konrad Malawski](https://github.com/ktoso), [Harjas Monga](https://github.com/Harjas12) +* Review Manager: [Holly Borla](https://github.com/hborla) +* Status: **Active Review (March 13 - March 27, 2025)** +* Implementation: [swiftlang/swift#79600](https://github.com/swiftlang/swift/pull/79600) +* Review: ([pitch](https://forums.swift.org/t/pitch-task-naming-api/76115)) + +## Introduction + +In this proposal, we introduce several new APIs to allow developers to name their Swift Tasks for the purposes of identifying tasks in a human-readable way. These names can then be used to identify tasks by printing their names, programatically inspecting the name property, or by tools which dump and inspect tasks–such as debuggers, swift-inspect or others. + +## Motivation + +In previous generations of concurrency technologies, developer tools, such as debuggers, have had access to some kind of label to help describe a process’s concurrent work. Ex: Pthread names or Grand Central Dispatch queue names. These names are very helpful to provide extra context to developers when using debugging and profiling tools. + +Currently, Swift Concurrency has no affordances to allow developers to label a Task, which can be troublesome for developers trying to identify "which task" is taking a long time to process or similar questions when observing the system externally. In order to ease the debugging and profiling of Swift concurrency code, developers should be able to annotate their Swift Tasks to describe an asynchronous workload. + +## Proposed solution + +In order to allow developers to provide helpful names for Swift Tasks, the Swift Task creation APIs should be modified to *optionally* allow developers to provide a name for that task. + +Consider the example: + +```swift +let getUsers = Task { + await users.get(accountID)) +} +``` + +In order to ease debugging, a developer could create this unstructured task by passing in a name instead: + +```swift +let getUsers = Task(name: "Get Users") { + await users.get(accountID) +} +``` + +Or, if a developer has a lot of similar tasks, they can provide more contextual information using string interpolation. + +```swift +let getUsers = Task("Get Users for \(accountID)") { + await users.get(accountID) +} +``` + +By introducing this API in Swift itself, rather than developers each inventing their own task-local with a name, runtime inspection tools and debuggers can become aware of task names and show you exactly which accountID was causing the crash or a profiling tool could tell you which accountID request was slow to load. + +## Detailed design + +Naming tasks is only allowed during their creation, and modifying names is not allowed. + +Names are arbitrary user-defined strings, which may be computed at runtime because they often contain identifying information such as the request ID or similar runtime information. + +The following APIs will be provided on `Task`: + +```swift +extension Task where Failure == /* both Never and Error cases */ { + init( + name: String?, + executorPreference taskExecutor: (any TaskExecutor)? = nil, + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async /*throws */-> Success) + + static func detached( + name: String?, + executorPreference taskExecutor: (any TaskExecutor)? = nil, + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async /*throws */ -> Success) +} +``` + +In addition to these APIs to name unstructured Tasks, the following API will be added to all kinds of task groups: + +```swift +mutating func addTask( + name: String?, + executorPreference taskExecutor: (any TaskExecutor)? = nil, + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async -> ChildTaskResult + ) + + mutating func addTaskUnlessCancelled( + name: String?, + executorPreference taskExecutor: (any TaskExecutor)? = nil, + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async -> ChildTaskResult + ) +``` + +These APIs would be added to all kinds of task groups, including throwing, discarding ones. With the signature being appropriately matching the existing addTask signatures of those groups. + +> Concurrently under review with this proposal is the `Task.startSynchronously` (working name, pending changes) proposal; +> If both this and the synchronous starting tasks proposals are accepted, these APIs would also gain the additional `name: String? = nil` parameter. + +In addition to that, it will be possible to read a name off a task, similar to how the current task's priority is possible to be read: + +```swift +extension Task { + static var name: String? { get } +} + +extension UnsafeCurrentTask { + var name: String? { get } +} +``` + +### `UnsafeCurrentTask` access from `UnownedJob` + +In order to have an `Executor` be able to inspect a task name, either to print "Now running [Task A]" or for other reasons, we propose to offer the access to an `UnsafeCurrentTask` representation of a `ExecutorJob` (or `UnownedJob`): + +```swift +extension ExecutorJob / UnownedJob { + public var unsafeCurrentTask: UnsafeCurrentTask? { ... } +} +``` + +This allows executors to inspect the task name if the `job` is a task, and has a name: + +```swift +public nonisolated func enqueue(_ job: consuming ExecutorJob) { + log.trace("Running task named: \(job?.unsafeCurrentTask?.name ?? "")") +} +``` + +We use the `UnsafeCurrentTask` type because it is possible to obtain it from an `UnownedTask` and therefore it is not safe to refer to it without knowladge about the job's lifetime. +One should not refer to the unsafe current task after invoking `runSynchronously` on the job, as the job may have completed and been destroyed; therefore the use of the existing `UnsafeCurrentTask` type here is quite appropriate. + +This also allows us to expose other information off a task, such as task local values in the future, if the `UnsafeCurrentTask` were to gain such APIs, without having to replicate "the same" accessors into yet another API that would be accessible directly from an `ExecutorJob`. + +## Source compatibility + +This proposal only contains additive changes to the API surface. + +Since Swift Tasks names will be optional, there will be no source compatibility issues. + +## ABI compatibility + +This proposal is ABI additive and does not change any existing ABI. + +## Implications on adoption + +Because runtime changes are required, these new APIs will only be available on newer OSes. + +## Future directions + +This proposal does not contain a method to name Swift Tasks created using the `async let` syntax. Unlike the other methods of creating Tasks, the `async let` syntax didn’t have an obvious way to allow a developer to provide a string. A suggestion of how we may provide automatic names to Tasks created via this method will be shown below in the [Alternatives Considered section](##Alternatives-considered). + +### Task names for "startSynchronously" + +If the ["start synchronously" tasks proposal](https://github.com/swiftlang/swift-evolution/pull/2698) would be accepted, the name parameter would also be included in those APIs. + +## Alternatives considered + +### Actor & DistributedActor Identity + +#### Actor Identity + +> Note: While not really an alternative, we would like to explain why this proposal does not propose to change anything about how actors are identified. + +This proposal focuses on task names, however, another important part of Swift Concurrency is actors, so in this section we’d like to discuss how there isn’t an actual need for new API to address *actor naming* because of how actors can already conform to protocols. + +An actor can conform e.g. to the `Identifiable` protocol. This works well with constant identifiers, as an actor can have a constant let property implement the `id` requirement from this protocol: + +```swift +actor Worker: Identifiable { + let id: String + + init(id: String) { + self.id = id + } +} +``` + +It is also likely that such identity is how a developer might want to look up and identify such actor in traces or logs, so making use of `Identifiable` seems like a good pattern to follow. + +It is also worth reminding that thread-safety of an actor is ensured even if the `id` were to be implemented using a computed property, because it will be forced to be `nonisolated` because of Swift’s conformance and actor isolation rules: + +```swift +actor Worker: Identifiable { + let workCategory: String = "fetching" // "building" etc... + let workID: Int + + nonisolated var id: String { + "\(workCategory)-\(workID)" + } +} +``` + +#### Distributed Actor Identity + +Distributed actors already implicitly conform to `Identifiable` protocol and have a very useful `id` representation that is always assigned by the actor system by which an actor is managed. + +This id is the natural human readable representation of such actor identity, and tools which want to print an “actor identity” should rely on this. In other words, this simply follows the same general pattern that makes sense for other objects and actors of using Identifiable when available to identify things. + +```swift +distributed actor Worker { // implicitly Identifiable + // nonisolated var id: Self.ActorSystem.ActorID { get } +} +``` + +### AsyncLet Task Naming + +While there is no clear way on how to name Swift Task using `async let`, the following were considered. + +#### Approach 1: + +Since we effectively want to express that “the task” is some specific task, we had considered introducing some special casing where if the right hand side of an async let we want to say at creation time that this task is something specific, thus we arrive at the following: + +```swift +async let example: String = Task(name: "get-example") { "example" } +``` + +In order to make this syntax work, we need to avoid double creating tasks. When the compiler sees the `async let` syntax and the `Task {}` initializer, it would need to not create a Task to immediately create another Task inside it, but instead use that Task initializer that we explicitly wrote. + +While, this approach could in theory allow us to name Tasks created using `async let`. It has at least one major issue: + +It can cause surprising behavior and it can be unclear that this would only work when the Task initializer is visible from the async let declaration... I.e. moving the initialization into a method like this: + +```swift +async let example: String = getTask() // error String != Task + +func getTask() -> Task { Task(name: "get-example") { "example" } } +``` + +This would not only break refactoring, as the types are not the same; but also execution semantics, as this refactoring has now caused the task to become an unstructured task “by accident”. Therefore this approach is not viable because it introduces too many easy to make mistakes. + +#### Approach 2: + +Instead of attempting to adding a naming API to the `async let` syntax, we could instead take a different where if developers really want to name a structured Task they can use a `TaskGroup` and the compiler would generate a good default name for the Tasks created using the `async let` syntax. Drawing inspiration from how closures and dispatch blocks are named, we count the declaration in the scope and use that to name it. For example: + +```swift +func getUserImages() async -> [Image] { + + async let profileImg = getProfilePicture() // <- Named "getUserImages.asyncLet-1" + async let headerImg = getHeaderPicture() // <- Named "getUserImages.asyncLet-2" + + . + . + . +} +``` + +These names at the very least give some indication of what the task was created to do, and the developer can opt to use the `TaskGroup` API if more control is desired. + +A slight alternative to this suggestion, is instead of using the name of the surrounding scope, use the name of the parent task instead. For example: + +```swift +Task(name: "get user images for \(userID)") { + async let profileImg = getProfilePicture() // <- Named "getUserImages.asyncLet-1" + async let headerImg = getHeaderPicture() // <- Named "getUserImages.asyncLet-2" + + . + . + . +} +``` + +This approach doesn’t allow developers full control over naming tasks, but it is in same spirit of allowing developer tools to provide more context for a task. + +## Structured Names + +There was some thought given to the idea of allowing developers to group similar tasks (in name only). Consider programs that create hundreds of tasks for network requests; by allowing grouping a runtime analysis tool could surface that in a textual or graphical UI. The API needed would be similar to the one proposed, but with an additional optional `category` argument for the Task initializer. For example: + +```swift +Task(category: "Networking", name: "download profile image for \(userID)) { ... } +``` + +Then a debugger than wanted to print all the tasks running when a break point is hit, it could group them by this optional “Networking” category. + +This is not in the actual proposal in order to keep the API simple and doesn’t add much additional value over a simple name. From 60a85e72fd7487fb012d88b3a28e4882f1b5327e Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 13 Mar 2025 15:14:33 -0700 Subject: [PATCH 4149/4563] Link to review thread in SE-0469 (#2739) --- proposals/0469-task-names.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0469-task-names.md b/proposals/0469-task-names.md index 20d872fe89..ebded646f8 100644 --- a/proposals/0469-task-names.md +++ b/proposals/0469-task-names.md @@ -5,7 +5,7 @@ * Review Manager: [Holly Borla](https://github.com/hborla) * Status: **Active Review (March 13 - March 27, 2025)** * Implementation: [swiftlang/swift#79600](https://github.com/swiftlang/swift/pull/79600) -* Review: ([pitch](https://forums.swift.org/t/pitch-task-naming-api/76115)) +* Review: ([pitch](https://forums.swift.org/t/pitch-task-naming-api/76115)) ([review](https://forums.swift.org/t/se-0469-task-naming/78509)) ## Introduction From 1b82a5557c30d8442481df436e6d35862595ab68 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 17 Mar 2025 16:50:32 -0400 Subject: [PATCH 4150/4563] Update attach() function after discussion on the forums and with Stuart --- proposals/testing/NNNN-attachments.md | 51 ++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/proposals/testing/NNNN-attachments.md b/proposals/testing/NNNN-attachments.md index 02c02f5610..91e6887990 100644 --- a/proposals/testing/NNNN-attachments.md +++ b/proposals/testing/NNNN-attachments.md @@ -84,9 +84,10 @@ public struct Attachment: ~Copyable where AttachableValue: Atta sourceLocation: SourceLocation = #_sourceLocation ) - /// Attach this instance to the current test. + /// Attach an attachment to the current test. /// /// - Parameters: + /// - attachment: The attachment to attach. /// - sourceLocation: The source location of the call to this function. /// /// When attaching a value of a type that does not conform to both @@ -98,7 +99,27 @@ public struct Attachment: ~Copyable where AttachableValue: Atta /// disk. /// /// An attachment can only be attached once. - public consuming func attach(sourceLocation: SourceLocation = #_sourceLocation) + public static func attach(_ attachment: consuming Self, sourceLocation: SourceLocation = #_sourceLocation) + + /// Attach a value to the current test. + /// + /// - Parameters: + /// - attachableValue: The value to attach. + /// - sourceLocation: The source location of the call to this function. + /// + /// When attaching a value of a type that does not conform to both + /// [`Sendable`](https://developer.apple.com/documentation/swift/sendable) and + /// [`Copyable`](https://developer.apple.com/documentation/swift/copyable), + /// the testing library encodes it as data immediately. If the value cannot be + /// encoded and an error is thrown, that error is recorded as an issue in the + /// current test and the attachment is not written to the test report or to + /// disk. + /// + /// This function creates a new instance of ``Attachment`` and immediately + /// attaches it to the current test. + /// + /// An attachment can only be attached once. + public static func attach(_ attachment: consuming AttachableValue, sourceLocation: SourceLocation = #_sourceLocation) /// Call a function and pass a buffer representing the value of this /// instance's ``attachableValue-2tnj5`` property to it. @@ -134,7 +155,7 @@ conform: /// /// To attach an attachable value to a test report or test run output, use it to /// initialize a new instance of ``Attachment``, then call -/// ``Attachment/attach(sourceLocation:)``. An attachment can only be attached +/// ``Attachment/attach(_:sourceLocation:)``. An attachment can only be attached /// once. /// /// The testing library provides default conformances to this protocol for a @@ -226,7 +247,7 @@ that refines `Attachable`: /// /// To attach an attachable value to a test report or test run output, use it to /// initialize a new instance of ``Attachment``, then call -/// ``Attachment/attach(sourceLocation:)``. An attachment can only be attached +/// ``Attachment/attach(_:sourceLocation:)``. An attachment can only be attached /// once. /// /// A type can conform to this protocol if it represents another type that @@ -297,11 +318,12 @@ Package Manager: --attachments-path Path where attachments should be saved. ``` -If specified, an attachment will be written to that path when its `attach()` -method is called. If not specified, attachments are not saved to disk. Tools -that indirectly use Swift Testing through `swift test` can specify a path (e.g. -to a directory created inside the system's temporary directory), then move or -delete the created files as needed. +If specified, an attachment will be written to that path when the attachment is +passed to one of the `Attachment.attach(_:sourceLocation:)` methods. If not +specified, attachments are not saved to disk. Tools that indirectly use Swift +Testing through `swift test` can specify a path (e.g. to a directory created +inside the system's temporary directory), then move or delete the created files +as needed. The JSON event stream ABI will be amended correspondingly: @@ -384,6 +406,12 @@ version too. [swiftlang/swift-testing#824](https://github.com/swiftlang/swift-testing/pull/824) includes an experimental implementation of this feature. +- Attaching attachments to issues or to activities: XCTest supports attachments + on `XCTIssue`; Swift Testing does not currently allow developers to create an + issue without immediately recording it, so there is no opportunity to attach + anything to one. XCTest also supports the concept of activities as subsections + of tests; they remain a future direction for Swift Testing. + ## Alternatives considered - Doing nothing: there's sufficient demand for this feature that we know we want @@ -423,6 +451,11 @@ version too. Instead, `Attachable` includes the function `preferredName(for:basedOn:)` that allows an implementation (such as that of `Encodable & Attachable`) to add a path extension to the filename specified by the test author if needed. + +- Making the `Attachment.attach(_:sourceLocation:)` methods a single instance + method of `Attachment` named `attach()`: this was in the initial pitch but the + community discussed several more ergonomic options and we chose + `Attachment.attach(_:sourceLocation:)` instead. ## Acknowledgments From b35dcb6dc27088506f858dcd630f8ec6e091a1ed Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 17 Mar 2025 16:52:54 -0400 Subject: [PATCH 4151/4563] Add a link to the cross-import overlay pitch --- proposals/testing/NNNN-attachments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/NNNN-attachments.md b/proposals/testing/NNNN-attachments.md index 91e6887990..8610d5397e 100644 --- a/proposals/testing/NNNN-attachments.md +++ b/proposals/testing/NNNN-attachments.md @@ -33,7 +33,7 @@ we will introduce a new protocol, `Attachable`, to which types can conform to indicate they can be attached to a test. Default conformances to `Attachable` will be provided for standard library types -that can reasonably be attached. We will also introduce a **cross-import overlay** +that can reasonably be attached. We will also introduce a [cross-import overlay](https://forums.swift.org/t/cross-import-overlays/36710) with Foundation—that is, a tertiary module that is automatically imported when a test target imports both Foundation _and_ Swift Testing—that includes additional conformances for Foundation types such as `Data` and `URL` and From 60a149180491dfce5e6d22275f11d66727434944 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 17 Mar 2025 21:25:23 +0000 Subject: [PATCH 4152/4563] "adopt" -> "migrate" & alternatives considered * Move SwiftPM changes to alternatives considered * Consider the alternative of an independent command line option * Add more alternative naming considerations --- ...NNN-adoption-tooling-for-swift-features.md | 190 +++++++++--------- 1 file changed, 96 insertions(+), 94 deletions(-) diff --git a/proposals/NNNN-adoption-tooling-for-swift-features.md b/proposals/NNNN-adoption-tooling-for-swift-features.md index a7b69dd356..e8f9ca34b7 100644 --- a/proposals/NNNN-adoption-tooling-for-swift-features.md +++ b/proposals/NNNN-adoption-tooling-for-swift-features.md @@ -1,4 +1,4 @@ -# Adoption tooling for Swift features +# Migration tooling for Swift features * Proposal: [SE-NNNN](NNNN-filename.md) * Authors: [Anthony Latsis](https://github.com/AnthonyLatsis) @@ -92,32 +92,30 @@ and testing code where a change in behavior is preferable. ## Proposed solution -Introduce the notion of an "adoption" mode for individual experimental and +Introduce the notion of a ***migrate*** mode for individual experimental and upcoming features. -The core idea behind adoption mode is a declaration of intent that can be +The core idea behind migration mode is a declaration of intent that can be leveraged to build better supportive adoption experiences for developers. -If enabling a feature communicates an intent to *enact* rules, adoption mode -communicates an intent to *adopt* them. -An immediate benefit of adoption mode is the capability to deliver source -modifications that can be applied to preserve compatibility whenever a feature -provides for them. +If enabling a feature communicates an intent to *enact* rules, migration mode +communicates an intent to migrate code so as to preserve compatibility once the +feature is enabled. This proposal will support the set of existing upcoming features that have mechanical migrations, as described in the [Automation](#automation) section. All future proposals that intend to introduce an upcoming feature and -provide for a mechanical migration should include an adoption mode and detail +provide for a mechanical migration should include a migration mode and detail its behavior alongside the migration paths in the *Source compatibility* section. ## Detailed design -Upcoming features that have mechanical migrations will support an adoption +Upcoming features that have mechanical migrations will support a migration mode, which is a new mode of building a project that will produce compiler warnings with attached fix-its that can be applied to preserve the behavior -of the code once the upcoming feature is enacted. +of the code under the feature. -The action of enabling a previously disabled upcoming feature in adoption +The action of enabling a previously disabled upcoming feature in migration mode must not cause any new compiler errors or behavioral changes, and the fix-its produced must preserve compatibility. Compatibility here refers to both source and binary compatibility, as well as @@ -131,92 +129,33 @@ This warning will belong to the diagnostic group `StrictLanguageFeatures`. ### Interface -#### Compiler - The `-enable-*-feature` frontend and driver command line options will start -supporting an optional mode specifier with `adoption` as the only valid mode: +supporting an optional mode specifier with `migrate` as the only valid mode: ``` -enable-upcoming-feature [:] -enable-experimental-feature [:] - := adoption + := migrate ``` For example: ``` --enable-upcoming-feature InternalImportsByDefault:adoption +-enable-upcoming-feature InternalImportsByDefault:migrate ``` -If the specified mode is invalid, the flag will be ignored, and a warning will +If the specified mode is invalid, the option will be ignored, and a warning will be emitted. This warning will belong to the diagnostic group `StrictLanguageFeatures`. In a series of either of these options applied to a given feature, only the last option will be honored. If an upcoming feature is both implied by the effective language mode and -enabled in adoption mode using either of the aforementioned options, the latter -will be disregarded. - -#### Swift package manager - -The [`SwiftSetting.enableUpcomingFeature`] and -[`SwiftSetting.enableExperimentalFeature`] methods from the -[`PackageDescription`](https://developer.apple.com/documentation/packagedescription) -library will be augmented with a `mode` parameter defaulted to match the -current behavior: - -```swift -extension SwiftSetting { - @available(_PackageDescription, introduced: 6.2) - public enum SwiftFeatureMode { - case adoption - case on - } -} -``` -```diff - public static func enableUpcomingFeature( - _ name: String, -+ mode: SwiftFeatureMode = .on, - _ condition: BuildSettingCondition? = nil - ) -> SwiftSetting { -+ let argument = switch mode { -+ case .adoption: "\(name):adoption" -+ case .mode: name -+ } -+ - return SwiftSetting( -- name: "enableUpcomingFeature", value: [name], condition: condition) -+ name: "enableUpcomingFeature", value: [argument], condition: condition) - } -``` -```diff - public static func enableExperimentalFeature( - _ name: String, -+ mode: SwiftFeatureMode = .on, - _ condition: BuildSettingCondition? = nil - ) -> SwiftSetting { -+ let argument = switch mode { -+ case .adoption: "\(name):adoption" -+ case .mode: name -+ } -+ - return SwiftSetting( -- name: "enableExperimentalFeature", value: [name], condition: condition) -+ name: "enableExperimentalFeature", value: [argument], condition: condition) - } -``` - -For example: - -``` -SwiftSetting.enableUpcomingFeature("InternalImportsByDefault", mode: .adoption) -``` +enabled in migration, the latter will be disregarded. ### Diagnostics -Diagnostics emitted in relation to a specific feature in adoption mode must +Diagnostics emitted in relation to a specific feature in migration mode must belong to a diagnostic group named after the feature. The names of diagnostic groups can be displayed alongside diagnostic messages using `-print-diagnostic-groups` and used to associate messages with features. @@ -232,7 +171,7 @@ This proposal does not affect binary compatibility or binary interfaces. ## Implications on adoption -Entering or exiting adoption mode will affect behavior and is therefore a +Entering or exiting migration mode can affect behavior and is therefore a potentially source-breaking action. ## Future directions @@ -243,14 +182,14 @@ For some features, a source change that alters the semantics of the program is a more desirable approach to addressing an error that comes from enabling the feature. For example, programmers might want to replace cases of `any P` with `some P`. -Adoption tooling could support the option to produce source incompatible +Migration tooling could support the option to produce source incompatible fix-its in cases where the compiler can detect that a different behavior might be more beneficial. ### Applications beyond mechanical migration -Adoption mode can be extrapolated to additive features, such as -[typed `throws`][SE-0413] or [opaque parameter types][SE-0341], by providing +The concept of migration mode could be extrapolated to additive features, such +as [typed `throws`][SE-0413] or [opaque parameter types][SE-0341], by providing actionable adoption tips. Additive features are hard-enabled and become an integral part of the language as soon as they ship. @@ -259,49 +198,112 @@ model, and their metadata is kept around either to support [feature availability checks][SE-0362-feature-detection] in conditional compilation blocks or because they started off as experimental features. -Another potential direction for adoption mode is promotion of best practices. +Another feasible extension of migration mode is promotion of best practices. ### Augmented diagnostic metadata The current serialization format for diagnostics does not include information about diagnostic groups or whether a particular fix-it preserves semantics. There are several reasons why this data can be valuable for users, and why it -is essential for future tools built around adoption mode: +is essential for future tools built around migration mode: * The diagnostic group name can be used to, well, group diagnostics, as well as to communicate relationships between diagnostics and features and filter out relevant diagnostics. This can prove especially handy when multiple features are simultaneously - enabled in adoption mode, or when similar diagnostic messages are caused by + enabled in migration mode, or when similar diagnostic messages are caused by distinct features. * Exposing the purpose of a fix-it can help developers make quicker decisions when offered multiple fix-its. Furthermore, tools can take advantage of this information by favoring and auto-applying source-compatible fix-its. -### `swift adopt` +### `swift migrate` -The Swift package manager could implement an `adopt` subcommand for interactive -review and application of adoption mode output for a given set of features, +The Swift package manager could implement a `migrate` subcommand for interactive +review and application of migration mode output for a given set of features, with a command-line interface similar to `git add --patch`. ## Alternatives considered +### A distinct `-migrate` option + +This direction has a questionably balanced set of advantanges and downsides. +On one hand, it would provide an adequate foundation for invoking migration +for a language mode in addition to individual features. +On the other hand, an independent option is less discoverable, has a steeper +learning curve, and makes the necessary relationships between it and the +existing `-enable-*-feature` options harder to infer. +Perhaps more notably, a bespoke option by itself would not scale to any future +modes, setting what might be an unfortunate example for further decentralizion +of language feature control. + +### API for package manifests + +The decision around surfacing migration mode in the `PackageDescription` +library depends on whether there is a concensus on the value of enabling it as +a persistent setting as opposed to a automated procedure in the long run. + +Here is how an API change could look like for the proposed solution: + +```swift ++extension SwiftSetting { ++ @available(_PackageDescription, introduced: 6.2) ++ public enum SwiftFeatureMode { ++ case migrate ++ case on ++ } ++} +``` +```diff + public static func enableUpcomingFeature( + _ name: String, ++ mode: SwiftFeatureMode = .on, + _ condition: BuildSettingCondition? = nil + ) -> SwiftSetting + + public static func enableExperimentalFeature( + _ name: String, ++ mode: SwiftFeatureMode = .on, + _ condition: BuildSettingCondition? = nil + ) -> SwiftSetting +``` + +It can be argued that both Swift modules and the volume of changes required for +migration can be large enough to justify spreading the review over several +sessions, especially if migration mode gains support for parallel +[source-incompatible fix-its][#producing-source-incompatible-fix-its]. +However, we also expect higher-level migration tooling to allow for +incremental progress. + ### Naming -Perhaps the most intuitive alternative to "adoption" is "migration". -We settled on the former because there is no reason for this concept to remain -limited to upcoming features or mechanical migration. +The next candidates in line per discussions are ***adopt***, ***audit***, +***stage***, and ***preview***, respectively. +* ***preview*** and ***stage*** can both be understood as to report on the + impact of a change, but are less commonly used in the sense of code + migration. +* ***audit*** best denotes a recurrent action in this context, which we believe + is more characteristic of the static analysis domain, such as enforcing a set + of custom compile-time rules on code. +* An important reservation about ***adoption*** of source-breaking features is + that it comprises both code migration and integration. + It may be more prudent to save this term for a future add-on mode that, + unlike migration mode, implies that the feature is enabled and can be invoked + in any language mode to aid developers in making better use of new behaviors + or rules. + To illustrate, this mode could appropriately suggest switching from `any P` + to `some P` for `ExistentialAny`. ## Acknowledgements -This proposal was inspired by documents prepared by [Allan Shortlidge][Allan] -and [Holly Borla][Holly]. +This proposal was inspired by documents prepared by [Allan Shortlidge] and +[Holly Borla]. Special thanks to Holly for her guidance throughout the draft stage. -[Holly]: https://github.com/hborla -[Allan]: https://github.com/tshortli +[Holly Borla]: https://github.com/hborla +[Allan Shortlidge]: https://github.com/tshortli [SE-0192]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0192-non-exhaustive-enums.md [SE-0274]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0274-magic-file.md From 23ba17fccc9a0bee8f7f7b2d0a00e24ea5d436e1 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Tue, 18 Mar 2025 09:51:51 +0900 Subject: [PATCH 4153/4563] adjust how we check for the presence of the is... API --- proposals/NNNN-SerialExecutor-isIsolated.md | 45 +++++++------------- proposals/nnnn-is-isolated-flow.graffle | Bin 158389 -> 112899 bytes proposals/nnnn-is-isolated-flow.png | Bin 75953 -> 80747 bytes 3 files changed, 15 insertions(+), 30 deletions(-) diff --git a/proposals/NNNN-SerialExecutor-isIsolated.md b/proposals/NNNN-SerialExecutor-isIsolated.md index d5db0285fe..03eb470d26 100644 --- a/proposals/NNNN-SerialExecutor-isIsolated.md +++ b/proposals/NNNN-SerialExecutor-isIsolated.md @@ -2,9 +2,12 @@ * Proposal: [SE-NNNN](...) * Author: [Konrad 'ktoso' Malawski](https://github.com/ktoso) -* Review Manager: -* Status: **Partial implementation** -* Implementation: https://github.com/swiftlang/swift/pull/79788 +* Review Manager: TBD +* Status: Implemented + * https://github.com/swiftlang/swift/pull/79788 + * https://github.com/swiftlang/swift/pull/79946 + +* Pitch: [[Pitch][SerialExecutor] Improved Custom SerialExecutor isolation checking](https://forums.swift.org/t/pitch-serialexecutor-improved-custom-serialexecutor-isolation-checking/78237/) * Review: TODO ## Introduction @@ -125,37 +128,15 @@ This proposal specifically adds the "if `isIsolatingCurrentContext` is available If `isIsolatingCurrentContext` is available, effectively it replaces `checkIsolated` because it does offer a sub-par error message experience and is not able to offer a warning if Swift would be asked to check the isolation but not crash upon discovering a violation. -### Enabling `isIsolatingCurrentContext` checking mode - -Similar as complex equality in `SerialExecutors` this feature must be opted into by flagging it when the `UnownedSerialExecutor` value is returned from a serial executor's `asUnownedSerialExecutor()`. - -Previously this was done by signalling the feature in the `UnownedSerialExecutor` initializer like this: - -```swift -// Existing API -public func asUnownedSerialExecutor() -> UnownedSerialExecutor { - UnownedSerialExecutor(complexEquality: self) -} -``` - -Which enables the following `isSameExclusiveExecutionContext` check, which can only be used when a "current" executor is present, and cannot be used when running code outside of a Swift concurrency task (!): +### Detecting the `isIsolatingCurrentContext` checking mode -```swift -// Existing API -public func isSameExclusiveExecutionContext(other: NaiveQueueExecutor) -> Bool { - other.secretIdentifier == self.secretIdentifier -} -``` +The `isIsolatingCurrentContext` method effectively replaces the `checkIsolated` method, because it can answer the same question _if it is implemented_. -In order to enable the runtime to call into the `isIsolatingCurrentContext` the `UnownedSerialExecutor` **must** be constructed as follows: +Some runtimes may not be able to implement a the returning `isIsolatingCurrentContext`, and they are not expected to implement the new protocol requirement. -```swift -public func asUnownedSerialExecutor() -> UnownedSerialExecutor { - UnownedSerialExecutor(hasIsIsolatingCurrentContext: self) -} -``` +The general guidance about which method to implement is to implement `isIsolatingCurrentContext` whenever possible. This method can be used by the Swift runtime in "warning mode". When running a check in this mode, the `checkIsolated` method cannot and will not be used because it would cause an unexpected crash. -This sets a flag inside the internal executor reference which makes the swift runtime call into the new `isIsolatingCurrentContext` function, rather than the other versions of isolation checking. In many ways this API is the most general of them all, and generally preferable _if_ your executor is using some kind of mechanism to track the "current" context that the Swift concurrency runtime cannot know about, e.g. like thread local values inside threads managed by your executor. +The presence of a non-default implementation of the `isIsolatingCurrentContext` protocol requirement. In other words, if there is an implementation of the requirement available _other than_ the default one provided in the concurrency library, the runtime will attempt to use this method _over_ the `checkIsolated` API. This allows for a smooth migration to the new API, and enables the use of this method in if the runtime would like issue a check that cannot cause a crash. ### Compatibility strategy for custom SerialExecutor authors @@ -194,3 +175,7 @@ It would be ideal if this method could have been bool returning initially, but d ### Deprecate `checkIsolated`? In order to make adoption of this new mode less painful and not cause deprecation warnings to libraries which intend to support multiple versions of Swift, the `SerialExcecutor/checkIsolated` protocol requirement remains _not_ deprecated. It may eventually become deprecated in the future, but right now we have no plans of doing so. + +## Changelog + +- removed the manual need to signal to the runtime that the specific executor supports the new checking mode. It is now detected by the compiler and runtime, checking for the presence of a non-default implementation of the protocol requirement. diff --git a/proposals/nnnn-is-isolated-flow.graffle b/proposals/nnnn-is-isolated-flow.graffle index cfe1fc4d456e8156a6bdda297874bcbaae83c397..b476f5ab47a94be7a4a840e9951c310ea7ba826e 100644 GIT binary patch literal 112899 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 literal 158389 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

      5e5{bPUWtNXgvR_590sfiUWZDE3L5 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 z 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

      g}9B@X5SAb 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 z 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 G-~R$m4s+!I literal 75953 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 SOs8AmpS+C9qvD6gum1-)9r!T- From 9128d2a1220398889b296a6e9912c97ad7b692f0 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Tue, 18 Mar 2025 10:00:36 +0900 Subject: [PATCH 4154/4563] Update isIsolated diagram to be more precise --- proposals/NNNN-SerialExecutor-isIsolated.md | 10 +++------- proposals/nnnn-is-isolated-flow.graffle | Bin 112899 -> 170172 bytes proposals/nnnn-is-isolated-flow.png | Bin 80747 -> 84498 bytes 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/proposals/NNNN-SerialExecutor-isIsolated.md b/proposals/NNNN-SerialExecutor-isIsolated.md index 03eb470d26..0125b5baf3 100644 --- a/proposals/NNNN-SerialExecutor-isIsolated.md +++ b/proposals/NNNN-SerialExecutor-isIsolated.md @@ -132,18 +132,16 @@ If `isIsolatingCurrentContext` is available, effectively it replaces `checkIsola The `isIsolatingCurrentContext` method effectively replaces the `checkIsolated` method, because it can answer the same question _if it is implemented_. -Some runtimes may not be able to implement a the returning `isIsolatingCurrentContext`, and they are not expected to implement the new protocol requirement. +Some runtimes may not be able to implement a the returning `isIsolatingCurrentContext`, and they are not required to implement the new protocol requirement. -The general guidance about which method to implement is to implement `isIsolatingCurrentContext` whenever possible. This method can be used by the Swift runtime in "warning mode". When running a check in this mode, the `checkIsolated` method cannot and will not be used because it would cause an unexpected crash. +The general guidance about which method to implement is to implement `isIsolatingCurrentContext` whenever possible. This method can be used by the Swift runtime in "warning mode". When running a check in this mode, the `checkIsolated` method cannot and will not be used because it would cause an unexpected crash. A runtime may still want to implement the `checkIsolated` function if it truly is unable to return a true/false response to the isolation question, but can only assert on an illegal state. This function will not be used when the runtime does not expect a potential for a crash. -The presence of a non-default implementation of the `isIsolatingCurrentContext` protocol requirement. In other words, if there is an implementation of the requirement available _other than_ the default one provided in the concurrency library, the runtime will attempt to use this method _over_ the `checkIsolated` API. This allows for a smooth migration to the new API, and enables the use of this method in if the runtime would like issue a check that cannot cause a crash. +The presence of a non-default implementation of the `isIsolatingCurrentContext` protocol witness is detected by the compiler and the runtime can detect this information in order to determine if the new function should be used for these checks. In other words, if there is an implementation of the requirement available _other than_ the default one provided in the concurrency library, the runtime will attempt to use this method _over_ the `checkIsolated` API. This allows for a smooth migration to the new API, and enables the use of this method in if the runtime would like issue a check that cannot cause a crash. ### Compatibility strategy for custom SerialExecutor authors New executor implementations should prioritize implementing `isIsolatingCurrentContext` when available, using an appropriate `#if swift(>=...)` check to ensure compatibility. Otherwise, they should fall back to implementing the crashing version of this API: `checkIsolated()`. -While the relationship between `isIsolatingCurrentContext` and `checkIsolated` is not ideal, the boolean-returning version was previously not possible to implement due to runtime restrictions which we have manage to resolve, and thus would like to introduce the better API and better user-experience. - For authors of custom serial executors, adopting this feature is an incremental process and they can adopt it at their own pace, properly guarding the new feature with necessary availability guards. This feature requires a new version of the Swift concurrency runtime which is aware of this new mode and therefore able to call into the new checking function, therefore libraries should implement and adopt it, however it will only manifest itself when the code is used with a new enough concurrency runtime As a result, this change should cause little to no disruption to Swift concurrency users, while providing an improved error reporting experience if using executors which adopt this feature. @@ -170,8 +168,6 @@ If we determine that there are significant good use-cases for this method to be This would be ideal, however also problematic since changing a protocol requirements signature would be ABI breaking. -It would be ideal if this method could have been bool returning initially, but due to some restrictions back then it would not have been implementable the to what concurrency integrations were available to Swift. - ### Deprecate `checkIsolated`? In order to make adoption of this new mode less painful and not cause deprecation warnings to libraries which intend to support multiple versions of Swift, the `SerialExcecutor/checkIsolated` protocol requirement remains _not_ deprecated. It may eventually become deprecated in the future, but right now we have no plans of doing so. diff --git a/proposals/nnnn-is-isolated-flow.graffle b/proposals/nnnn-is-isolated-flow.graffle index b476f5ab47a94be7a4a840e9951c310ea7ba826e..9215bacf9e3508f078ca6b7837a01ee2c143568a 100644 GIT binary patch literal 170172 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 literal 112899 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 diff --git a/proposals/nnnn-is-isolated-flow.png b/proposals/nnnn-is-isolated-flow.png index bb86ddf5487b5b3133121eb91a2f8adb4383b9ed..97db91af276480fdef60f67090f87904e26389c5 100644 GIT binary patch literal 84498 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 Gy#FUmc2!sa literal 80747 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 z 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 G-~R$m4s+!I From 54d5d8f67966555b833323a60da81ffc015f3ad4 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Tue, 18 Mar 2025 13:57:16 -0700 Subject: [PATCH 4155/4563] Do not generalize Result.flatMapError yet --- proposals/0465-nonescapable-stdlib-primitives.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/proposals/0465-nonescapable-stdlib-primitives.md b/proposals/0465-nonescapable-stdlib-primitives.md index 687c0f999e..1d906e306f 100644 --- a/proposals/0465-nonescapable-stdlib-primitives.md +++ b/proposals/0465-nonescapable-stdlib-primitives.md @@ -241,7 +241,7 @@ We generalize `Result` along the same lines as `Optional`, allowing its `success Important convenience APIs such as `Result.init(catching:)` or `Result.map` will need to require escapability until we introduce a way to formally specify lifetime dependencies. This is unfortunate, but it still enables intrepid Swift developers to experiment with defining interfaces that take (or perhaps even return!) `Result` values. -However, we are already able to generalize a small handful of methods: `get` and the two error-mapping utilities, `mapError` and `flatMapError`. +However, we are already able to generalize a couple of methods: `get` and the error-mapping utility `mapError`. ```swift func sample(_ res: Result, E>) -> Int { @@ -525,7 +525,7 @@ extension Result: Escapable where Success: Escapable & ~Copyable {} extension Result: Sendable where Success: Sendable & ~Copyable & ~Escapable {} ``` -We postpone generalizing most of the higher-order functions that make `Result` convenient to use, as we currently lack the means to reason about lifetime dependencies for such functions. But we are already able to generalize the two functions that do not have complicated lifetime semantics: `mapError` and `flatMapError`. +We postpone generalizing most of the higher-order functions that make `Result` convenient to use, as we currently lack the means to reason about lifetime dependencies for such functions. But we are already able to generalize the one function that does not have complicated lifetime semantics: `mapError`. ```swift extension Result where Success: ~Copyable & ~Escapable { @@ -534,16 +534,9 @@ extension Result where Success: ~Copyable & ~Escapable { _ transform: (Failure) -> NewFailure ) -> Result } - -extension Result where Success: ~Copyable & ~Escapable { - @_lifetime(copying self) // Illustrative syntax - consuming func flatMapError( - _ transform: (Failure) -> Result - ) -> Result -} ``` -Both of these functions return a value with the same lifetime as the original `Result` instance. +The returned value has the same lifetime constraints as the original `Result` instance. We can also generalize the convenient `get()` function, which is roughly equivalent to optional unwrapping: @@ -888,5 +881,6 @@ Many people contributed to the discussions that led to this proposal. We'd like - Guillaume Lessard - John McCall - Tony Parker +- Ben Rimmington - Andrew Trick - Rauhul Varma From a88c17732df31776066a2c5a67f55ffe265b4d41 Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Tue, 18 Mar 2025 20:57:14 +0000 Subject: [PATCH 4156/4563] Minor wording amendment --- proposals/NNNN-adoption-tooling-for-swift-features.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/NNNN-adoption-tooling-for-swift-features.md b/proposals/NNNN-adoption-tooling-for-swift-features.md index e8f9ca34b7..2d750e459a 100644 --- a/proposals/NNNN-adoption-tooling-for-swift-features.md +++ b/proposals/NNNN-adoption-tooling-for-swift-features.md @@ -92,7 +92,7 @@ and testing code where a change in behavior is preferable. ## Proposed solution -Introduce the notion of a ***migrate*** mode for individual experimental and +Introduce the notion of a migration mode for individual experimental and upcoming features. The core idea behind migration mode is a declaration of intent that can be leveraged to build better supportive adoption experiences for developers. @@ -150,8 +150,8 @@ be emitted. This warning will belong to the diagnostic group `StrictLanguageFeatures`. In a series of either of these options applied to a given feature, only the last option will be honored. -If an upcoming feature is both implied by the effective language mode and -enabled in migration, the latter will be disregarded. +If a feature is both implied by the effective language mode and enabled in +migration mode, the latter option will be disregarded. ### Diagnostics @@ -241,7 +241,7 @@ of language feature control. The decision around surfacing migration mode in the `PackageDescription` library depends on whether there is a concensus on the value of enabling it as -a persistent setting as opposed to a automated procedure in the long run. +a persistent setting as opposed to an automated procedure in the long run. Here is how an API change could look like for the proposed solution: @@ -288,7 +288,7 @@ The next candidates in line per discussions are ***adopt***, ***audit***, * An important reservation about ***adoption*** of source-breaking features is that it comprises both code migration and integration. It may be more prudent to save this term for a future add-on mode that, - unlike migration mode, implies that the feature is enabled and can be invoked + unlike migration mode, implies that the feature is enabled, and can be invoked in any language mode to aid developers in making better use of new behaviors or rules. To illustrate, this mode could appropriately suggest switching from `any P` From 7069fb5c1faca0d08c21459183c9dfe55ca057d5 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 18 Mar 2025 14:52:24 -0700 Subject: [PATCH 4157/4563] Accept SE-465 "Standard Library Primitives for Nonescapable Types" --- proposals/0465-nonescapable-stdlib-primitives.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0465-nonescapable-stdlib-primitives.md b/proposals/0465-nonescapable-stdlib-primitives.md index 1d906e306f..1f8bc5fad4 100644 --- a/proposals/0465-nonescapable-stdlib-primitives.md +++ b/proposals/0465-nonescapable-stdlib-primitives.md @@ -3,7 +3,7 @@ * Proposal: [SE-0465](0465-nonescapable-stdlib-primitives.md) * Authors: [Karoy Lorentey](https://github.com/lorentey) * Review Manager: [Doug Gregor](https://github.com/douggregor) -* Status: **Active review (March 5...18, 2025)** +* Status: **Accepted** * Roadmap: [Improving Swift performance predictability: ARC improvements and ownership control][Roadmap] * Implementation: https://github.com/swiftlang/swift/pull/73258 * Review: ([Review](https://forums.swift.org/t/se-0465-standard-library-primitives-for-nonescapable-types/78310)) ([Pitch](https://forums.swift.org/t/pitch-nonescapable-standard-library-primitives/77253)) From 61dd6adec716a0f3a61f422f9e1fa930f8085dbb Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Thu, 20 Mar 2025 15:26:53 +0100 Subject: [PATCH 4158/4563] Update and rename NNNN-exit-tests.md to 0008-exit-tests.md --- .../testing/{NNNN-exit-tests.md => 0008-exit-tests.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/testing/{NNNN-exit-tests.md => 0008-exit-tests.md} (99%) diff --git a/proposals/testing/NNNN-exit-tests.md b/proposals/testing/0008-exit-tests.md similarity index 99% rename from proposals/testing/NNNN-exit-tests.md rename to proposals/testing/0008-exit-tests.md index 29d2ac0775..da116c10f1 100644 --- a/proposals/testing/NNNN-exit-tests.md +++ b/proposals/testing/0008-exit-tests.md @@ -1,11 +1,11 @@ # Exit tests -* Proposal: [ST-NNNN](NNNN-exit-tests.md) +* Proposal: [ST-0008](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0008-exit-tests.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) -* Status: **Awaiting review** +* Status: **Active Review (March 21...April 8, 2025)** * Bug: [apple/swift-testing#157](https://github.com/apple/swift-testing/issues/157) * Implementation: [apple/swift-testing#324](https://github.com/swiftlang/swift-testing/pull/324) -* Review: ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) +* Review: (review), ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) ## Introduction From c866493afe9c2c61ef221a0469cbf7762c4507f1 Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Thu, 20 Mar 2025 17:18:14 +0100 Subject: [PATCH 4159/4563] Update 0008-exit-tests.md - Added link to review topic Added link to review topic --- proposals/testing/0008-exit-tests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0008-exit-tests.md b/proposals/testing/0008-exit-tests.md index da116c10f1..b7b74e1d73 100644 --- a/proposals/testing/0008-exit-tests.md +++ b/proposals/testing/0008-exit-tests.md @@ -5,7 +5,7 @@ * Status: **Active Review (March 21...April 8, 2025)** * Bug: [apple/swift-testing#157](https://github.com/apple/swift-testing/issues/157) * Implementation: [apple/swift-testing#324](https://github.com/swiftlang/swift-testing/pull/324) -* Review: (review), ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) +* Review: ([review](https://forums.swift.org/t/st-0008-exit-tests/78692)), ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) ## Introduction From a4419800fe85d8c7f520e6d14207824fb8dbddc1 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 20 Mar 2025 10:57:00 -0700 Subject: [PATCH 4160/4563] Mark SE-0444 as implemented in Swift 6.1 (#2746) --- proposals/0444-member-import-visibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0444-member-import-visibility.md b/proposals/0444-member-import-visibility.md index a98f0b6857..7904ebc4d5 100644 --- a/proposals/0444-member-import-visibility.md +++ b/proposals/0444-member-import-visibility.md @@ -3,7 +3,7 @@ * Proposal: [SE-0444](0444-member-import-visibility.md) * Authors: [Allan Shortlidge](https://github.com/tshortli) * Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) -* Status: **Accepted** +* Status: **Implemented (Swift 6.1)** * Bug: [apple/swift#46493](https://github.com/apple/swift/issues/46493) * Implementation: [apple/swift#72974](https://github.com/apple/swift/pull/72974), [apple/swift#73063](https://github.com/apple/swift/pull/73063) * Upcoming Feature Flag: `MemberImportVisibility` From 8273f0e7cae43b528a7de267201b97fa776343dc Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Thu, 20 Mar 2025 15:07:12 -0400 Subject: [PATCH 4161/4563] Update attach() -> record() --- proposals/testing/NNNN-attachments.md | 33 +++++++++++++++------------ 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/proposals/testing/NNNN-attachments.md b/proposals/testing/NNNN-attachments.md index 8610d5397e..28aa7947ab 100644 --- a/proposals/testing/NNNN-attachments.md +++ b/proposals/testing/NNNN-attachments.md @@ -99,12 +99,15 @@ public struct Attachment: ~Copyable where AttachableValue: Atta /// disk. /// /// An attachment can only be attached once. - public static func attach(_ attachment: consuming Self, sourceLocation: SourceLocation = #_sourceLocation) + public static func record(_ attachment: consuming Self, sourceLocation: SourceLocation = #_sourceLocation) /// Attach a value to the current test. /// /// - Parameters: /// - attachableValue: The value to attach. + /// - preferredName: The preferred name of the attachment when writing it to + /// a test report or to disk. If `nil`, the testing library attempts to + /// derive a reasonable filename for the attached value. /// - sourceLocation: The source location of the call to this function. /// /// When attaching a value of a type that does not conform to both @@ -119,7 +122,7 @@ public struct Attachment: ~Copyable where AttachableValue: Atta /// attaches it to the current test. /// /// An attachment can only be attached once. - public static func attach(_ attachment: consuming AttachableValue, sourceLocation: SourceLocation = #_sourceLocation) + public static func record(_ attachableValue: consuming AttachableValue, named preferredName: String? = nil, sourceLocation: SourceLocation = #_sourceLocation) /// Call a function and pass a buffer representing the value of this /// instance's ``attachableValue-2tnj5`` property to it. @@ -153,10 +156,11 @@ conform: /// A protocol describing a type that can be attached to a test report or /// written to disk when a test is run. /// -/// To attach an attachable value to a test report or test run output, use it to -/// initialize a new instance of ``Attachment``, then call -/// ``Attachment/attach(_:sourceLocation:)``. An attachment can only be attached -/// once. +/// To attach an attachable value to a test, pass it to ``Attachment/record(_:named:sourceLocation:)``. +/// To further configure an attachable value before you attach it, use it to +/// initialize an instance of ``Attachment`` and set its properties before +/// passing it to ``Attachment/record(_:sourceLocation:)``. An attachable +/// value can only be attached to a test once. /// /// The testing library provides default conformances to this protocol for a /// variety of standard library types. Most user-defined types do not need to @@ -245,10 +249,11 @@ that refines `Attachable`: /// written to disk when a test is run and which contains another value that it /// stands in for. /// -/// To attach an attachable value to a test report or test run output, use it to -/// initialize a new instance of ``Attachment``, then call -/// ``Attachment/attach(_:sourceLocation:)``. An attachment can only be attached -/// once. +/// To attach an attachable value to a test, pass it to ``Attachment/record(_:named:sourceLocation:)``. +/// To further configure an attachable value before you attach it, use it to +/// initialize an instance of ``Attachment`` and set its properties before +/// passing it to ``Attachment/record(_:sourceLocation:)``. An attachable +/// value can only be attached to a test once. /// /// A type can conform to this protocol if it represents another type that /// cannot directly conform to ``Attachable``, such as a non-final class or a @@ -452,10 +457,10 @@ version too. allows an implementation (such as that of `Encodable & Attachable`) to add a path extension to the filename specified by the test author if needed. -- Making the `Attachment.attach(_:sourceLocation:)` methods a single instance - method of `Attachment` named `attach()`: this was in the initial pitch but the - community discussed several more ergonomic options and we chose - `Attachment.attach(_:sourceLocation:)` instead. +- Making the `Attachment.record(_:[named:]sourceLocation:)` methods a single + instance method of `Attachment` named `attach()`: this was in the initial + pitch but the community discussed several more ergonomic options and we chose + `Attachment.record(_:sourceLocation:)` instead. ## Acknowledgments From 762a5a076cd065112d696ce14924ade54d86970e Mon Sep 17 00:00:00 2001 From: Rachel Brindle Date: Thu, 20 Mar 2025 13:47:09 -0700 Subject: [PATCH 4162/4563] Get ready for moving to active review --- .../testing/{NNNN-attachments.md => 0009-attachments.md} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename proposals/testing/{NNNN-attachments.md => 0009-attachments.md} (99%) diff --git a/proposals/testing/NNNN-attachments.md b/proposals/testing/0009-attachments.md similarity index 99% rename from proposals/testing/NNNN-attachments.md rename to proposals/testing/0009-attachments.md index 28aa7947ab..ad6e16804e 100644 --- a/proposals/testing/NNNN-attachments.md +++ b/proposals/testing/0009-attachments.md @@ -1,8 +1,9 @@ # Attachments -* Proposal: [ST-NNNN](NNNN-attachments.md) +* Proposal: [ST-0009](0009-attachments.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) -* Status: **Awaiting review** +* Review Manager: [Rachel Brindle](https://github.com/younata) +* Status: **Active Review (March 21 - April 8, 2025)** * Bug: [swiftlang/swift-testing#714](https://github.com/swiftlang/swift-testing/issues/714) * Implementation: [swiftlang/swift-testing#973](https://github.com/swiftlang/swift-testing/pull/973) * Review: ([pitch](https://forums.swift.org/t/pitch-attachments/78072)) From 7462f2076b0c5a19ada4f8178ad63e859e935b11 Mon Sep 17 00:00:00 2001 From: Rachel Brindle Date: Thu, 20 Mar 2025 13:59:43 -0700 Subject: [PATCH 4163/4563] Link to the review thread for ST-0009 --- proposals/testing/0009-attachments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0009-attachments.md b/proposals/testing/0009-attachments.md index ad6e16804e..565c01acc3 100644 --- a/proposals/testing/0009-attachments.md +++ b/proposals/testing/0009-attachments.md @@ -6,7 +6,7 @@ * Status: **Active Review (March 21 - April 8, 2025)** * Bug: [swiftlang/swift-testing#714](https://github.com/swiftlang/swift-testing/issues/714) * Implementation: [swiftlang/swift-testing#973](https://github.com/swiftlang/swift-testing/pull/973) -* Review: ([pitch](https://forums.swift.org/t/pitch-attachments/78072)) +* Review: ([review](https://forums.swift.org/t/st-0009-attachments/78698)), ([pitch](https://forums.swift.org/t/pitch-attachments/78072)) ## Introduction From 1f1cfee5d172cce1b99f87f5cf4c9e814730c00b Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Thu, 20 Mar 2025 16:57:52 -0500 Subject: [PATCH 4164/4563] Add Review Manager field to header of Swift Testing proposal template --- proposal-templates/0000-swift-testing-template.md | 10 +++++++--- proposals/testing/0008-exit-tests.md | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/proposal-templates/0000-swift-testing-template.md b/proposal-templates/0000-swift-testing-template.md index 9e0377961e..642ac7bd53 100644 --- a/proposal-templates/0000-swift-testing-template.md +++ b/proposal-templates/0000-swift-testing-template.md @@ -2,6 +2,7 @@ * Proposal: [ST-NNNN](NNNN-filename.md) * Authors: [Author 1](https://github.com/author1), [Author 2](https://github.com/author2) +* Review Manager: TBD * Status: **Awaiting implementation** or **Awaiting review** * Bug: _if applicable_ [swiftlang/swift-testing#NNNNN](https://github.com/swiftlang/swift-testing/issues/NNNNN) * Implementation: [swiftlang/swift-testing#NNNNN](https://github.com/swiftlang/swift-testing/pull/NNNNN) @@ -14,8 +15,10 @@ except for the section headers and the header fields above. For example, you should delete everything from this paragraph down to the Introduction section below. -As a proposal author, you should fill out all of the header fields. Delete any -header fields marked _if applicable_ that are not applicable to your proposal. +As a proposal author, you should fill out all of the header fields except +`Review Manager`. The review manager will set that field and change several +others as part of initiating the review. Delete any header fields marked _if +applicable_ that are not applicable to your proposal. When sharing a link to the proposal while it is still a PR, be sure to share a live link to the proposal, not an exact commit, so that readers will always see @@ -181,4 +184,5 @@ were incorporated into the proposal as it developed, take a moment here to thank them for their contributions. This is a collaborative process, and everyone's input should receive recognition! -Generally, you should not acknowledge anyone who is listed as a co-author. +Generally, you should not acknowledge anyone who is listed as a co-author or as +the review manager. diff --git a/proposals/testing/0008-exit-tests.md b/proposals/testing/0008-exit-tests.md index b7b74e1d73..57953ff9ee 100644 --- a/proposals/testing/0008-exit-tests.md +++ b/proposals/testing/0008-exit-tests.md @@ -2,6 +2,7 @@ * Proposal: [ST-0008](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0008-exit-tests.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Review Manager: [Maarten Engels](https://github.com/maartene) * Status: **Active Review (March 21...April 8, 2025)** * Bug: [apple/swift-testing#157](https://github.com/apple/swift-testing/issues/157) * Implementation: [apple/swift-testing#324](https://github.com/swiftlang/swift-testing/pull/324) From 0a317843f06163de925fb1fd61ff160a8608c3a4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 20 Mar 2025 21:48:27 -0700 Subject: [PATCH 4165/4563] Global-actor isolated conformances (#2720) * Draft: isolated conformances * Add an introduction, switch to `isolated P` syntax * Turn this into a real proposal with introduction / motivation / etc Also pull in Slava's lovely idea of the SendableMetatype protocol, and narrow the "strict sendable metatypes" check to type parameters that have non-marker protocol requirements. * Future work entry for non-isolated types with isolated conformances * Future direction: consider inference of `isolated` along with `@MainActor` * Add alternative considered for isolated conformance requirements * Discuss using "non-Sendable" terminology instead of "isolated conformances" * [Isolated conformances] Note the need for `nonisolated` if we infer `isolated` * [Isolated conformances] Add alternative for the "@MainActor P" spelling Instead of using `isolated P` for an isolated conformance, we could use the global actor spelling. Document it. * [Isolated conformances] Remove some leftover text This was rewritten and moved in an earlier iteration. * [Isolated conformances] Switch to @GlobalActorName spelling I've gotten a bunch of feedback that `isolated P` is confusing, and `@MainActor P` is both clearer and more extensible. Rework the proposal to use this new syntax, and bring in a few future directions that are enabled by it. * Now that SE-0466 is in review, promote "infer @MainActor conformances" This was in future work, but make this an official part of the proposal now that SE-0466 is in review. * Fix a few inconsistencies and odd wording choices * Introduce a second necessary restriction on dynamic casting Thanks to John for noticing that we need this dynamic check as well. * Add a "Future Direction" for inferring global actor isolation more generally * Address PR comments, thank you Xiaodi! * Add key path example * Improve exposition of rule (2) Instead of solely talking about the metatype being Sendable, be explicit about the two halves of type checking for generic code and what they mean for isolated conformances, how Sendable changes the interface contract, and why we slip SendableMetatype in there. * Bring the upcoming feature InferIsolatedConformances into the proposal * Fix a sentence fragment * Assign SE-0470 --------- Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- proposals/0470-isolated-conformances.md | 662 ++++++++++++++++++++++++ 1 file changed, 662 insertions(+) create mode 100644 proposals/0470-isolated-conformances.md diff --git a/proposals/0470-isolated-conformances.md b/proposals/0470-isolated-conformances.md new file mode 100644 index 0000000000..b8c221639a --- /dev/null +++ b/proposals/0470-isolated-conformances.md @@ -0,0 +1,662 @@ +# Global-actor isolated conformances + +* Proposal: [SE-0470](0470-isolated-conformances.md) +* Authors: [Doug Gregor](https://github.com/DougGregor) +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Active review (March 21...April 3, 2025)** +* Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md) +* Implementation: On `main` with the experimental features `IsolatedConformances` and `StrictSendableMetatypes`. +* Upcoming Feature Flag: `InferIsolatedConformances` +* Review: ([pitch](https://forums.swift.org/t/pre-pitch-isolated-conformances/77726)) + +## Introduction + +Types isolated to a global actor (such as `@MainActor`) are useful for representing data that can only ever be used from a single concurrency context. They occur both in single-threaded programs where all code is expected to run on the main actor as well as larger applications where interaction with the UI occurs through the main actor. Unfortunately, such types are unable to conform to most protocols due to isolation mismatches: + +```swift +@MainActor +class MyModelType: Equatable { + var name: String + + init(name: String) { + self.name = name + } + + // error: main-actor-isolated static function '==' cannot satisfy non-isolated requirement 'Equatable.==' + static func ==(lhs: MyModelType, rhs: MyModelType) -> Bool { + lhs.name == rhs.name + } +} +``` + +This proposal introduces the notion of an *isolated conformance*, which is a conformance that can only be used within the isolation domain of the type. For the code above, the conformance to `Equatable` can be specified as being isolated to the main actor as follows: + +```swift +@MainActor +class MyModelType: @MainActor Equatable { + // unchanged from the above ... +} +``` + +This allows `MyModelType` to provide a conformance to `Equatable` that works like every other conformance, except that it can only be used from the main actor. + +## Motivation + +Types isolated to the global actor are common in single-threaded programs and UI applications, among others, but their inability to conform to protocols without workarounds means that they cannot integrate with any Swift code using generics, cutting them off from interacting with many libraries. The workarounds themselves can be onerous: each operation that is used to satisfy a protocol requirement must be marked as `nonisolated`, e.g., + +```swift + nonisolated static func ==(lhs: MyModelType, rhs: MyModelType) -> Bool { + lhs.name == rhs.name + } +``` + +However, this is incompatible with using types or data on the main actor, and results in an error: + +```swift + 3 | @MainActor + 4 | class MyModelType: Equatable { + 5 | var name: String + | `- note: property declared here + 6 | + 7 | init(name: String) { + : +10 | +11 | nonisolated static func ==(lhs: MyModelType, rhs: MyModelType) -> Bool { +12 | lhs.name == rhs.name + | `- error: main actor-isolated property 'name' can not be referenced from a nonisolated context +13 | } +14 | } +``` + +We can work around this issue by assuming that this function will only ever be called on the main actor using [`MainActor.assumeIsolated`](https://developer.apple.com/documentation/swift/mainactor/assumeisolated(_:file:line:)): + +```swift + nonisolated static func ==(lhs: MyModelType, rhs: MyModelType) -> Bool { + MainActor.assumeIsolated { + lhs.name == rhs.name + } + } +``` + +This is effectively saying that `MyModelType` will only ever be considered `Equatable` on the main actor. Violating this assumption will result in a run-time error detected when `==` is called from outside the main actor. There are two problems with this approach. First, it's dynamically enforcing data-race safety for something that seems like it should be statically verifiable (but can't easily be expressed). Second, this same `nonisolated`/`assumeIsolated` pattern has to be replicated for every function that satisfies a protocol requirement, creating a lot of boilerplate. + +## Proposed solution + +This proposal introduces the notion of an *isolated conformance*. Isolated conformances are conformances whose use is restricted to a particular global actor. This is the same effective restriction as the `nonisolated`/`assumeIsolated` pattern above, but enforced statically by the compiler and without any boilerplate. The following defines an isolated conformance of `MyModelType` to `Equatable`: + +```swift +@MainActor +class MyModelType: @MainActor Equatable { + var name: String + + init(name: String) { + self.name = name + } + + static func ==(lhs: MyModelType, rhs: MyModelType) -> Bool { + lhs.name == rhs.name + } +} +``` + +Any attempt to use this conformance outside of the main actor will result in a compiler error: + +```swift +/*nonisolated*/ func hasMatching(_ value: MyModelType, in modelValues: [MyModelType]) -> Bool { + // error: cannot use main-actor-isolated conformance of 'MyModelType' to 'Equatable' in + // non-isolated function. + return modelValues.contains(value) +} +``` + +Additionally, we need to make sure that generic code cannot take the conformance and send it to another isolation domain. The [`Sequence.contains`](https://developer.apple.com/documentation/swift/sequence/contains(_:)) operation above clearly won't do that, but one could imagine a similar operation that uses concurrency to attempt the search in parallel: + +```swift +extension Sequence { + func parallelContains(_ element: Element) -> Bool where Element: Equatable & Sendable { + // ... + } +} +``` + +This `parallelContains` function can send values of type `Element` to another isolation domain, and from there call the `Equatable.==` function. If the conformance to `Equatable` is isolated, this would violate the data race safety guarantees. Therefore, this proposal specifies that an isolated conformance cannot be used in conjunction with a `Sendable` conformance: + +```swift +@MainActor +func parallelHasMatching(_ value: MyModelType, in modelValues: [MyModelType]) -> Bool { + // error: isolated conformance of 'MyModelType' to 'Equatable' cannot be used to + // satisfy conformance requirement for a `Sendable` type parameter 'Element'. + return modelValues.parallelContains(value) +} +``` + +The corresponding restriction needs to be in place within generic functions, ensuring that they don't leak (potentially) isolated conformances across isolation boundaries. For example, the following code could introduce a data race if the conformance of `T` to `GlobalLookup` were isolated: + +```swift +protocol GlobalLookup { + static func lookupByName(_ name: String) -> Self? +} + +func hasNamed(_: T.Type, name: String) async -> Bool { + return await Task.detached { + return T.lookupByName(name) != nil + }.value +} +``` + +Here, the type `T` itself is not `Sendable`, but because *all* metatypes are `Sendable` it is considered safe to use `T` from another isolation domain within the generic function. The use of `T`'s conformance to `GlobalLookup` within that other isolation domain introduces a data-race problem if the conformance were isolated. To prevent such problems in generic code, this proposal treats conformances within generic code as if they are isolated *unless* the conforming type opts in to being sendable. The above code, which is accepted in Swift 6 today, would be rejected by the proposed changes here with an error message like: + +```swift +error: cannot use potentially-isolated conformance of non-sendable type `T` to `GlobalLookup` in 'sending' closure +``` + +A function like `hasNamed` can indicate that its type parameter `T`'s requires non-isolated conformance by introducing a requirement `T: SendableMetatype`, e.g., + +```swift +func hasNamed(_: T.Type, name: String) async -> Bool { + return await Task.detached { + return T.lookupByName(name) != nil + }.value +} +``` + +As with `Sendable`, an isolated conformance cannot be combined with a `SendableMetatype` constraint: + +```swift +extension MyModelType: isolated GlobalLookup { + static func lookupByName(_ name: String) -> Self? { ... } +} + +// error: isolated conformance of 'MyModelType' to 'MyModelType' cannot be used to +// satisfy conformance requirement for a `SendableMetatype` type parameter 'T'. +if hasNamed(MyModelType.self, "root") { ... } +``` + +Note that `Sendable` inherits from `SendableMetatype`, so any type `T` with a `Sendable` requirement also implies a requirement `T: SendableMetatype`. + +Protocol conformances can also be discovered dynamically with the `as?` and `is` operators. For example, one could try to produce an `any Equatable` from a value of unknown type in any isolation domain: + +```swift +func tryEquatable(_ lhs: Any, rhs: Any) -> Bool { + if let eLHS = lhs as? any Equatable { + // use Equatable.== + } else { + return false + } +} +``` + +The `Any` value could contain `MyModelType`, in which case the conformance to `Equatable` will be isolated. In such cases, the `as?` operation will check whether the code is running on the executor associated with the conformance's isolation. If so, the cast can succeed; otherwise, the case will fail (and produce `nil`). + +## Detailed design + +The proposed solution describes the basic shape of isolated conformances and how they interact with the type system. This section goes into more detail on the data-race safety issues that arise from the introduction of isolated conformances into the language. Then it details three rules that, together, ensure freedom from data race safety issues in the presence of isolated conformances: + +1. An isolated conformance can only be used within its isolation domain. +2. When an isolated conformance is used to satisfy a generic constraint `T: P`, the generic signature must not include either of the following constraints: `T: Sendable` or `T: SendableMetatype`. +3. A value using a conformance isolated to a given global actor is within the same region as that global actor. + +### Data-race safety issues + +An isolated conformance must only be used within its actor's isolation domain. Here are a few examples that demonstrate the kinds of problems that need to be addressed by a design for isolated conformances to ensure that this property holds. + +First, using an isolated conformance outside of its isolation domain creates immediate problems. For example: + +```swift +protocol Q { + static func g() { } +} + +extension C: @MainActor Q { + @MainActor static func g() { } +} + +nonisolated func callQG() { + let qType: Q.Type = C.self + qType.g() // problem: called @MainActor function from nonisolated code +} +``` + +Here, a call to `C.g()` would have been rejected because it's calling a `@MainActor` function from non-isolated code and cannot `await`. However, if we're allowed to use the isolated conformance of `C: Q`, we would subvert the checking because `Q.g()` is non-isolated. + +We can address this specific issue by prohibiting the use of an isolated conformance from outside its isolation domain, i.e., the use of `C: Q` to convert `C.Type` to `Q.Type` in a non-`@MainActor` function would be an error. + +However, this is not sufficient to ensure that this conformance `C: P` will only be used from the main actor. Consider a function like this: + +```swift +@MainActor func badReturn(c: C) -> any Sendable & P { // okay so far + c // uses C: P from the main actor context (okay) + // uses C: Sendable (okay) +} + +@MainActor func useBadReturn(c: C) { + let anyP = badReturn(c: c) + Task.detached { + anyP.f() // PROBLEM: C.f is called from off the main actor + } +} +``` + +Here, the conformance `C: P` is used from within a `@MainActor` function, but a value that stores the conformance (in the `any Sendable & P`) is returned that no longer carries the isolation restriction. The caller is free to copy that value to another isolation domain, and will end up calling `@MainActor` code from outside the main actor. + +The issue is not limited to return values. For example, a generic parameter might escape to another isolation domain: + +```swift +@MainActor func sendMe(_ value: T) { + Task.detached { + value.f() + } +} + +extension C: @unchecked Sendable { } + +@MainActor func doSend(c: C) { + sendMe(c) // uses C: P from the main actor context + // uses C: Sendable +} +``` + +Here, `sendMe` ends up calling `C.f()` from outside the main actor. The combination of an isolated conformance and a `Sendable` requirement on the same type underlies this issue. To address the problem, we can prohibit the use of an isolated conformance if the corresponding type parameter (e.g, `T` in the example above) also has a `Sendable` requirement. + +However, that doesn't address all issues, because region isolation permits sending non-`Sendable` values: + +```swift +@MainActor func badSendingReturn() -> sending any P { // okay so far + C() // uses C: P from the main actor context (okay) + // returned value is in its own region +} + +@MainActor func useBadSendingReturn(c: C) { + let anyP = badSendingReturn() + Task.detached { + anyP.f() // PROBLEM: C.f is called from off the main actor + } +} +``` + +There are similar examples for `sending` parameters, but they're not conceptually different from the return case. This particular issue can be addressed by treating a value that depends on an isolated conformance as being within the region as the actor it's isolated to. So a newly-created value of type `C` is in its own region, but if it's type-erased to an `any P`, its region is merged with the region for the main actor. This would make the return expression in `badSendingReturn` ill-formed, because the returned value is not in its own region. + +Conformances can cross isolation boundaries even if no values cross the boundary: + +```swift +nonisolated func callQGElsewhere(_: T.Type) { + Task.detached { + T.g() + } +} + +@MainActor func isolationWithStatics() { + callQGElsewhere(C.self) +} +``` + +Here, the generic type `T` is used from another isolation domain inside `callQGElsewhere`. When the isolated conformance of `C: Q` is provided to this function, it opens up a data-race safety hole because `C.g()` ends up getting called through generic code. Addressing this problem either means ensuring that there are no operations on the metatype that go through a potentially-isolated protocol conformance or that the metatype is itself does not leave the isolation domain. + +One last issue concerns dynamic casting. Generic code can query a conformance at runtime with a dynamic cast like this: + +```swift +nonisolated func f(_ value: Any) { + if let p = value as? any P { + p.f() + } +} +``` + +If the provided `value` is an instance of `C` , and this code is invoked off the main actor, allowing it to enter the `if` branch would introduce a data race. Therefore, dynamic casting will have to determine when the conformance it depends on is isolated to an actor and check whether the code is running on the executor for that actor. + +Additionally, a dynamic cast that involves a `Sendable` or `SendableMetatype` constraint should not accept an isolated conformance even if the code is running on that global actor, e.g., + +```swift + if let p = value as? any Sendable & P { // never allows an isolated conformance to P + p.f() + } +``` + +### Rule 1: Isolated conformance can only be used within its isolation domain + +Rule (1) is straightforward: the conformance can only be used within a context that is also isolated to the same global actor. This applies to any use of a conformance anywhere in the language. For example: + +```swift +struct S: @MainActor P { } + +struct WrapsP: P { + var value: T + + init(_ value: T) { self.value = value } +} + +func badFunc() -> WrapsP { } // error: non-@MainActor-isolated function uses @MainActor-isolated conformance `S: P` + +func badFunc2() -> any P { + S() // error: non-@MainActor-isolated function uses @MainActor-isolated conformance `S: P` +} + +func acceptsP(_ value: T) { } + +func badFunc3() { + acceptsP(S()) // error: non-@MainActor-isolated function uses @MainActor-isolated conformance `S: P` +} + +protocol P2 { + associatedtype A: P +} + +struct S2: P2 { // error: conformance of S2: P2 depends on @MainActor-isolated conformance `S: P` + // note: fix by making conformance of S2: P2 also @MainActor-isolated + typealias A = S +} + +protocol HasName { + var name: String { get } +} + +@MainActor class Named: @MainActor HasName { + var name: String + // ... +} + +@MainActor +func useName() { + let named = Named() + Task.detached { + named[keyPath: \HasName.name] // error: uses main-actor isolated conformance Named: P + // outside of the main actor + } +} +``` + +Note that the types can have different isolation from their conformances. For example, an `actor` or non-isolated type can have a `@MainActor` conformance: + +```swift +actor MyActor: @MainActor P { + // okay, so long as the declarations satisfying the requirements to P are + // @MainActor or nonisolated +} + +/*nonisolated*/ struct MyStruct: @MainActor P { + // okay, so long as the declarations satisfying the requirements to P are + // @MainActor or nonisolated +} +``` + +### Rule 2: Isolated conformances can only be abstracted away for non-`Sendable` types + +Rule (2) ensures that when information about an isolated conformance is abstracted away by the generics system, the conformance cannot leave its original isolation domain. This requires a way to determine when a given generic function is permitted to pass a conformance it receives across isolation domains. Consider the example above where a generic function uses one of its conformances in different isolation domain: + +```swift +protocol Q { + static func g() { } +} + +nonisolated func callQGElsewhere(_: T.Type) { + Task.detached { + T.g() // use of the conformance T: Q in a different isolation domain + } +} + +extension C: @MainActor Q { ... } + +@MainActor func isolationWithStatics() { + callQGElsewhere(C.self) // passing an isolated conformance +} +``` + +The above code must be rejected to prevent a data race. There are two options for diagnosing this data race: + +1. Reject the definition of `callQGElsewhere` because it is using the conformance from a different isolation domain. +2. Reject the call to `callQGElsewhere` because it does not support isolated conformances. + +This proposal takes option (1): we assume that generic code accepts isolated conformances unless it has indicated otherwise with a `Sendable` constraint (more information on that below). Since most generic code doesn't deal with concurrency at all, it will be unaffected. And generic code that does make use of concurrency should already have `Sendable` constraints that indicate that it will not work with isolated conformances. + +The specific requirement for option (1) is enforced both in the caller to a generic function and in the implementation of that function. The caller can use an isolated conformance to satisfy a conformance requirement `T: P` so long as the generic function does not also contain a requirement `T: Sendable`. This prevents isolated conformances to be used in conjunction with types that can cross isolation domains, preventing the data race from being introduced at the call site. Here are some examples of this rule: + +```swift +func acceptsSendableP(_ value: T) { } +func acceptsAny(_ value: T) { } +func acceptsSendable(_ value: T) { } + +@MainActor func passIsolated(s: S) { + acceptsP(s) // okay: the type parameter 'T' requires P but not Sendable + acceptsSendableP(s) // error: the type parameter 'T' requires Sendable + acceptsAny(s) // okay: no isolated conformance + acceptsSendable(s) // okay: no isolated conformance +} +``` + +The same checking occurs when the type parameter is hidden, for example when dealing with `any` or `some` types: + +```swift +@MainActor func isolatedAnyGood(s: S) { + let a: any P = s // okay: the 'any P' cannot leave the isolation domain +} + +@MainActor func isolatedAnyBad(s: S) { + let a: any Sendable & P = s // error: the (hidden) type parameter for the 'any' is Sendable +} + +@MainActor func returnIsolatedSomeGood(s: S) -> some P { + return s // okay: the 'any P' cannot leave the isolation domain +} + +@MainActor func returnIsolatedSomeBad(s: S) -> some Sendable & P { + return s // error: the (hidden) type parameter for the 'any' is Sendable +} +``` + +Within the implementation, a conformance requirement `T: Q` is considered to be isolated if there is no requirement `T: Sendable`. This mirrors the rule on the caller side, and causes the following code to be ill-formed: + +```swift +protocol Q { + static func g() { } +} + +nonisolated func callQGElsewhere(_: T.Type) { + Task.detached { + T.g() // error: use of potentially-isolated conformance of non-Sendable type T to Q + } +} +``` + +To correct this function, add a constraint `T: Sendable`, which allows the function to send the conformance across isolation domains. As described above, it also prevents the caller from providing an isolated conformance to satisfy the `T: Q` requirement, preventing the data race. + +The `Sendable` requirement described above is a stricter contract than is necessary for a function such as `callQGElsewhere`, which won't ever pass *values* of the type `T` across an isolation domain. Therefore, we introduce a new marker protocol `SendableMetatype` to capture the idea that values of the metatype of `T` (i.e., `T.Type`) will cross isolation domains and take conformances with them. A requirement `T: SendableMetatype` prohibits isolated conformances from being used on type `T`. Now, `callQGElsewhere` can be correctly expressed as follows: + +```swift +nonisolated func callQGElsewhere(_: T.Type) { + Task.detached { + T.g() + } +} +``` + +The `SendableMetatype` protocol is somewhat special, because according to SE-0302 *all* metatypes are `Sendable`. This proposal refines that statement slightly: all concrete types (structs, enums, classes, actors) implicitly conform to `SendableMetatype`, because their metatypes (e.g., `MyModelType.Type`) are all `Sendable`. Therefore, a call to `callQGElsewhere` for any concrete type will succeed so long as that type has a (non-isolated) conformance to `Q`. + +The `Sendable` protocol inherits from the new `SendableMetatype` protocol: + +```swift +/*@marker*/ protocol SendableMetatype { } +/*@marker*/ protocol Sendable: SendableMetatype { } +``` + +This means that a requirement `T: Sendable` implies `T: SendableMetatype`, so a generic function that uses concurrency along with `Sendable` requirements, like this:: + +```swift +func doSomethingElsewhere(_ value: T) { + Task.detached { + value.f() // okay + } +} +``` + +will continue to work with the stricter model for generic functions in this proposal. + +The proposed change for generic functions does have an impact on source compatibility, where functions like `callQGElsewhere` will be rejected. However, the source break is limited to generic code that: + +1. Uses a conformance requirement (`T: P`) of a non-marker protocol `P` in another isolated domain, +2. Does not have a corresponding constraint `T: Sendable` requirement; and +3. Is compiled with strict concurrency enabled (either as Swift 6 or with warnings). + +Experiments with the prototype implementation of this feature uncovered very little code that was affected by this change. The benefit to introducing this source break is that the vast majority of existing generic code will work unmodified with isolated conformances, or (if it's using concurrency) correctly reject the use of isolated conformances in their callers. + +### Rule 3: Isolated conformances are in their global actor's region + +With [region-based isolation](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0414-region-based-isolation.md), values of non-`Sendable` type can be transferred to another isolation domain when it can be proven that they are in their own "region" of code that separate from all other regions. Isolated conformances are considered to be within the region of their global actor, so any value formed that involves an isolated conformance will have its region merged with that of the isolated conformance. For example: + +```swift +@MainActor func acceptSending(_ value: sending any P) { } + +@MainActor func passSending() { + let c1 = C() // in its own region + let ap1: any P = c1 // merges region of c1 with region of the conformance of C: P (MainActor) + acceptSending(ap1) // error: argument to sending parameter is within the MainActor region + + let c2 = C() // in its own region + let wp2 = WrapsP(c2) // merges region of c2 with region of the conformance of C: P (MainActor) + acceptSending(c) // error: argument to sending parameter is within the MainActor region +} +``` + +### Inferring global actor isolation for global-actor-isolated types + +Types that are isolated to a global actor are very likely to want to have their conformances to be isolated to that global actor. This is especially true because the members of global-actor isolated types are implicitly isolated to that global actor, so obvious-looking code is rejected: + +```swift +@MainActor +class MyModelType: P { + func f() { } // error: implements P.f, is implicitly @MainActor + // but conformance to P is not isolated +} +``` + +With this proposal, the fix is to mark the conformance as `@MainActor`: + +```swift +@MainActor +class MyModelType: @MainActor P { + func f() { } // okay: implements P.f, is implicitly @MainActor +} +``` + +However, the inference rule feels uneven: why is the `@MainActor` in one place inferred but not in the other? + +In the future, we'd like to extend the global actor inference rule for global-actor isolated types to also infer global actor isolated on their conformances. This makes the obvious code above also correct: + +```swift +@MainActor +class MyModelType: /*inferred @MainActor*/ P { + func f() { } // implements P.f, is implicitly @MainActor +} +``` + +If this inference is not desired, for example because the code will use `nonisolated` members to satisfy the requirements of the protocol, it can use `nonisolated` on the conformance: + +```swift +@MainActor +class MyModelType: nonisolated P { + nonisolated func f() { } // implements P.f, is non-isolated +} +``` + +This mirrors the rules for global actor inference elsewhere in the language, providing a more consistent answer. + +This proposed change is source-breaking, so it should be staged in via an upcoming feature (`InferIsolatedConformances`) that can be folded into a future language mode. Fortunately, it is mechanically migratable: existing code migrating to `InferIsolatedConformances` could introduce `nonisolated` for each conformance of a global-actor-isolated type. + +### Infer `@MainActor` conformances + +[SE-0466](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0466-control-default-actor-isolation.md) provides the ability to specify that a given module will infer `@MainActor` on any code that hasn't explicitly stated isolated (or non-isolation, via `nonisolated`). In a module that infers `@MainActor`, the upcoming feature `InferIsolatedConformances` (from the prior section) should also be enabled. This means that types will get main-actor isolation and also have their conformances main-actor isolated, extending the "mostly single-threaded" view of SE-0466 to interactions with generic code: + +```swift +/*implicit @MainActor*/ +class MyClass: /*implicit @MainActor*/P { ... } +``` + +## Source compatibility + +As discussed in the section on rule (2), this proposal introduces a source compatibility break for code that is using strict concurrency and passes uses conformances of non-`Sendable` type parameters in other isolation domains. The overall amount of such code is expected to be small, because it's likely to be rare that the conformances of generic types cross isolation boundaries but values of those types do not. + +Initial testing of an implementation of this proposal found very little code that relied on `Sendable` metatypes where the corresponding type was not also `Sendable`. Therefore, this proposal suggests to accept this as a source-breaking change with strict concurrency (as a warning in Swift 5, error in Swift 6) rather than staging the change through an upcoming feature or alternative language mode. + +## ABI compatibility + +Isolated conformances can be introduced into the Swift ABI without any breaking changes, by extending the existing runtime metadata for protocol conformances. All existing (non-isolated) protocol conformances can work with newer Swift runtimes, and isolated protocol conformances will be usable with older Swift runtimes as well. There is no technical requirement to restrict isolated conformances to newer Swift runtimes. + +However, there is one likely behavioral difference with isolated conformances between newer and older runtimes. In newer Swift runtimes, the functions that evaluate `as?` casts will check of an isolated conformance and validate that the code is running on the proper executor before the cast succeeds. Older Swift runtimes that don't know about isolated conformances will allow the cast to succeed even outside of the isolation domain of the conformance, which can lead to different behavior that potentially involves data races. + +## Future Directions + +### Actor-instance isolated conformances + +Actor-instance isolated conformances are considerably more difficult than global-actor isolated conformances, because the conformance needs to be associated with a specific instance of that actor. Even enforcing rule (1) is nonobvious. As with `isolated` parameters, we could spell actor-instance isolation to a protocol `P` with `isolated P`. The semantics would need to be similar to what follows: + +```swift +actor A: isolated P { + func f() { } // implements P.f() +} + +func instanceActors(a1: isolated A, a2: A) { + let anyP1: any P = a1 // okay: uses isolated conformance 'A: P' only on a1, to which this function is isolated + let anyP2: any P = a2 // error: uses isolated conformance 'A: P' on a2, which is not the actor to which this function is isolated + + let a3 = a1 + let anyP3: any P = a3 // okay? requires dataflow analysis to determine that a3 and + // a1 are in the isolation domain of this function + + let wrappedA1: WrapsP // error? isolated conformance 'A: P' used without being + // anchored to the actor instance a1 + var wrappedA2: WrapsP = .init(a1) // okay? isolated conformance 'A: P' is used with a1 + wrappedA2.value = a3 // error: isolated conformance 'A: P' used in the type is + // in a different isolation domain than 'a1' +} +``` + +It's possible that these problems can be addressed by relying more heavily on region-based isolation akin to rule (3). This can be revisited in the future if the need justifies the additional complexity and we find a suitable implementation strategy. + +## Alternatives considered + +### "Non-Sendable" terminology instead of isolated conformances + +Isolated conformances are a lot like non-`Sendable` types, in that they can be freely used within the isolation domain in which they are created, but can't necessarily cross isolation domain boundaries. We could consider using "sendable" terminology instead of "isolation" terminology, e.g., all existing conformances are "Sendable" conformances (you can freely share them across isolation domain boundaries) and these new conformances are "non-Sendable" conformances. Trying to send such a conformance across an isolation domain boundary is, of course, an error. + +However, the "sendable" analogy breaks down or causes awkwardness in a few places: + +* Values of non-`Sendable` type can be sent across isolation domain boundaries due to [region-based isolation](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0414-region-based-isolation.md), but the same cannot be said of isolated conformances, so they are more non-Sendable than most non-Sendable things. + +* Global-actor-isolated types are usually `Sendable`, but their conformances would generally need to be non-`Sendable`. + +* Usually things are non-`Sendable` but have to be explicitly opt-in to being `Sendable`, whereas conformances would be the opposite. + +* Diagnostics for invalid conformance declarations that could be addressed with isolated conformances are necessarily described in terms of isolation, e.g., + ```` + error: main-actor isolated method 'f' cannot satisfy non-isolated requirement `f` of protocol P + ```` + + It wouldn't make sense to recast that diagnostic in terms of "sendable", and would also be odd for the fix to an isolation-related error message to be "add non-Sendable." + +* There is no established spelling for "not Sendable" that would work well on a conformance. + +### Isolated conformance requirements + +This proposal introduces the notion of isolated conformances, which can satisfy a conformance requirement only when the corresponding type isn't `Sendable`. There is no way for a generic function to express that some protocol requirements are intended to allow isolated conformances while others are not. That could be made explicit, for example by allowing requirements of the form `T: isolated P` (which would work with both isolated and non-isolated conformances) and `T: nonisolated P` (which only allows non-isolated conformances). One could combine these in a given generic signature: + +```swift +func mixedConformances(_ x: [T]) { + for item in x { + item.foo() // Can use requirements of P + print(x.id) // Can use requirements of Identifiable + } + + Task.detached { + for item in x { + item.foo() // error: cannot capture isolated conformance of 'T' to 'P' in a closure in a different isolation domain + print(x.id) // okay: conformance to Identifable is nonisolated + } + } +} +``` + +This is a generalization of the proposed rules that makes more explicit when conformances can cross isolation domains within generic code, as well as allowing mixing of isolated and non-isolated conformances as in the example. One can explain this proposal's rule involving `SendableMetatype` requirements and isolated conformances in terms of (non)-isolated requirements. For a given conformance requirement `T: P` : + +* If `T: SendableMetatype`, `T: P` is interpreted as `T: nonisolated P`. +* If not `T: SendableMetatype`, `T: P` is interepreted as `T: isolated P`. + +The main down side of this alternative is the additional complexity it introduces into generic requirements. It should be possible to introduce this approach later if it proves to be necessary, by treating it as a generalization of the existing rules in this proposal. From be55df54147e25ed2a35885af6060d57a729da17 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Thu, 20 Mar 2025 22:02:18 -0700 Subject: [PATCH 4166/4563] Add link to review thread (#2749) --- proposals/0470-isolated-conformances.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0470-isolated-conformances.md b/proposals/0470-isolated-conformances.md index b8c221639a..91c87b8b53 100644 --- a/proposals/0470-isolated-conformances.md +++ b/proposals/0470-isolated-conformances.md @@ -7,7 +7,7 @@ * Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md) * Implementation: On `main` with the experimental features `IsolatedConformances` and `StrictSendableMetatypes`. * Upcoming Feature Flag: `InferIsolatedConformances` -* Review: ([pitch](https://forums.swift.org/t/pre-pitch-isolated-conformances/77726)) +* Review: ([pitch](https://forums.swift.org/t/pre-pitch-isolated-conformances/77726)) ([review](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704)) ## Introduction From 9232e2ff1f540f023ead85c7fe1cd6e7f3e56470 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Tue, 25 Mar 2025 11:08:48 -0600 Subject: [PATCH 4167/4563] Update UTF8Span proposal --- .../0464-utf8span-safe-utf8-processing.md | 96 ++++++++++++------- 1 file changed, 63 insertions(+), 33 deletions(-) diff --git a/proposals/0464-utf8span-safe-utf8-processing.md b/proposals/0464-utf8span-safe-utf8-processing.md index a2b09f6528..dc7b5d7d5f 100644 --- a/proposals/0464-utf8span-safe-utf8-processing.md +++ b/proposals/0464-utf8span-safe-utf8-processing.md @@ -121,16 +121,6 @@ extension Unicode.UTF8 { errors (including overlong encodings, surrogates, and invalid code points), it will produce an error per byte. - Since overlong encodings, surrogates, and invalid code points are erroneous - by the second byte (at the latest), the above definition produces the same - ranges as defining such a sequence as a truncated scalar error followed by - unexpected continuation byte errors. The more semantically-rich - classification is reported. - - For example, a surrogate count point sequence `ED A0 80` will be reported - as three `.surrogateCodePointByte` errors rather than a `.truncatedScalar` - followed by two `.unexpectedContinuationByte` errors. - Other commonly reported error ranges can be constructed from this result. For example, PEP 383's error-per-byte can be constructed by mapping over the reported range. Similarly, constructing a single error for the longest @@ -208,6 +198,25 @@ extension UTF8Span { /// /// The resulting UTF8Span has the same lifetime constraints as `codeUnits`. public init(validating codeUnits: Span) throws(UTF8.EncodingError) + + /// Creates a UTF8Span unsafely containing `uncheckedBytes`, skipping validation. + /// + /// `uncheckedBytes` _must_ be valid UTF-8 or else undefined behavior may + /// emerge from any use of the resulting UTF8Span, including any use of a + /// `String` created by copying the resultant UTF8Span + @unsafe + public init(unsafeAssumingValidUTF8 uncheckedCodeUnits: Span) +} +``` + +Similarly, `String`s can be created from `UTF8Span`s without re-validating their contents. + +```swift +extension String { + /// Create's a String containing a copy of the UTF-8 content in `codeUnits`. + /// Skips + /// validation. + public init(copying codeUnits: UTF8Span) } ``` @@ -217,7 +226,7 @@ We propose a `UTF8Span.UnicodeScalarIterator` type that can do scalar processing ```swift extension UTF8Span { - /// Returns an iterator that will decode the code units into + /// Returns an iterator that will decode the code units into /// `Unicode.Scalar`s. /// /// The resulting iterator has the same lifetime constraints as `self`. @@ -315,7 +324,7 @@ extension UTF8Span { We similarly propose a `UTF8Span.CharacterIterator` type that can do grapheme-breaking forwards and backwards. -The `CharacterIterator` assumes that the start and end of the `UTF8Span` is the start and end of content. +The `CharacterIterator` assumes that the start and end of the `UTF8Span` is the start and end of content. Any scalar-aligned position is a valid place to start or reset the grapheme-breaking algorithm to, though you could get different `Character` output if resetting to a position that isn't `Character`-aligned relative to the start of the `UTF8Span` (e.g. in the middle of a series of regional indicators). @@ -342,7 +351,7 @@ extension UTF8Span { /// Return the `Character` starting at `currentCodeUnitOffset`. After the /// function returns, `currentCodeUnitOffset` holds the position at the /// end of the `Character`, which is also the start of the next - /// `Character`. + /// `Character`. /// /// Returns `nil` if at the end of the `UTF8Span`. public mutating func next() -> Character? @@ -350,7 +359,7 @@ extension UTF8Span { /// Return the `Character` ending at `currentCodeUnitOffset`. After the /// function returns, `currentCodeUnitOffset` holds the position at the /// start of the returned `Character`, which is also the end of the - /// previous `Character`. + /// previous `Character`. /// /// Returns `nil` if at the start of the `UTF8Span`. public mutating func previous() -> Character? @@ -394,7 +403,7 @@ extension UTF8Span { /// /// Note: This is only for very specific, low-level use cases. If /// `codeUnitOffset` is not properly scalar-aligned, this function can - /// result in undefined behavior when, e.g., `next()` is called. + /// result in undefined behavior when, e.g., `next()` is called. /// /// If `i` is scalar-aligned, but not `Character`-aligned, you may get /// different results from running `Character` iteration. @@ -444,13 +453,6 @@ extension UTF8Span { } ``` -We also support literal (i.e. non-canonical) pattern matching against `StaticString`. - -```swift -extension UTF8Span { - static func ~=(_ lhs: UTF8Span, _ rhs: StaticString) -> Bool -} -``` #### Canonical equivalence and ordering @@ -466,7 +468,7 @@ extension UTF8Span { /// Whether `self` orders less than `other` under Unicode Canonical /// Equivalence using normalized code-unit order (in NFC). - public func isCanonicallyLessThan( + public func canonicallyPrecedes( _ other: UTF8Span ) -> Bool } @@ -482,17 +484,17 @@ Slicing a `UTF8Span` is nuanced and depends on the caller's desired use. They ca ```swift extension UTF8Span { - /// Returns whether contents are known to be all-ASCII. A return value of - /// `true` means that all code units are ASCII. A return value of `false` + /// Returns whether contents are known to be all-ASCII. A return value of + /// `true` means that all code units are ASCII. A return value of `false` /// means there _may_ be non-ASCII content. /// /// ASCII-ness is checked and remembered during UTF-8 validation, so this - /// is often equivalent to is-ASCII, but there are some situations where + /// is often equivalent to is-ASCII, but there are some situations where /// we might return `false` even when the content happens to be all-ASCII. /// - /// For example, a UTF-8 span generated from a `String` that at some point - /// contained non-ASCII content would report false for `isKnownASCII`, even - /// if that String had subsequent mutation operations that removed any + /// For example, a UTF-8 span generated from a `String` that at some point + /// contained non-ASCII content would report false for `isKnownASCII`, even + /// if that String had subsequent mutation operations that removed any /// non-ASCII content. public var isKnownASCII: Bool { get } @@ -620,16 +622,24 @@ extension UTF8Span { ``` - ### More alignments and alignment queries Future API could include word iterators (either [simple](https://www.unicode.org/reports/tr18/#Simple_Word_Boundaries) or [default](https://www.unicode.org/reports/tr18/#Default_Word_Boundaries)), line iterators, etc. Similarly, we could add API directly to `UTF8Span` for testing whether a given code unit offset is suitably aligned (including scalar or grapheme-cluster alignment checks). +### `~=` and other operators + +`UTF8Span` supports both binary equivalence and Unicode canonical equivalence. For example, a textual format parser using `UTF8Span` might operate in terms of binary equivalence for processing the textual format itself and then in terms of Unicode canonical equivalnce when interpreting the content of the fields. + +We are deferring making any decision on what a "default" comparison semantics should be as future work, which would include defining a `~=` operator (which would allow one to switch over a `UTF8Span` and match against literals). + +It may also be the case that it makes more sense for a library or application to define wrapper types around `UTF8Span` which can define `~=` with their preferred comparison semantics. + + ### Creating `String` copies -We could add an initializer to `String` that makes an owned copy of a `UTF8Span`'s contents. Such an initializer can skip UTF-8 validation. +We could add an initializer to `String` that makes an owned copy of a `UTF8Span`'s contents. Such an initializer can skip UTF-8 validation. Alternatively, we could defer adding anything until more of the `Container` protocol story is clear. @@ -639,7 +649,7 @@ Future API could include checks for whether the content is in a particular norma ### UnicodeScalarView and CharacterView -Like `Span`, we are deferring adding any collection-like types to non-escapable `UTF8Span`. Future work could include adding view types that conform to a new `Container`-like protocol. +Like `Span`, we are deferring adding any collection-like types to non-escapable `UTF8Span`. Future work could include adding view types that conform to a new `Container`-like protocol. See "Alternatives Considered" below for more rationale on not adding `Collection`-like API in this proposal. @@ -694,6 +704,26 @@ Many printing and logging protocols and facilities operate in terms of `String`. ## Alternatives considered +### Problems arising from the unsafe init + +The combination of the unsafe init on `UTF8Span` and the copying init on `String` creates a new kind of easily-accesible backdoor to `String`'s security and safety, namely the invariant that it holds validly encoded UTF-8 when in native form. + +Currently, String is 100% safe outside of crazy custom subclass shenanigans (only on ObjC platforms) or arbitrarily scribbling over memory (which is true of all of Swift). Both are highly visible and require writing many lines of advanced-knowledge code. + +Without these two API, it is in theory possible to skip validation and produce a String instance of the [indirect contiguous UTF-8](https://forums.swift.org/t/piercing-the-string-veil/21700) flavor through a custom subclass of NSString. But, it is only available on Obj-C platforms and involves creating a custom subclass of `NSString`, having knowledge of lazy bridging internals (which can and sometimes do change from release to release of Swift), and writing very specialized code. The product would be an unsafe lazily bridged instance of `String`, which could more than offset any performance gains from the workaround itself. + +With these two API, you can get to UB via a: + +```swift +let codeUnits = unsafe UTF8Span(unsafeAssumingValidUTF8: bytes) +... +String(copying: codeUnits) +``` + +We are (very) weakly in favor of keeping the unsafe init, because there are many low-level situations in which the valid-UTF8 invariant is held by the system itself (such as a data structure using a custom allocator). + + + ### Invalid start / end of input UTF-8 encoding errors Earlier prototypes had `.invalidStartOfInput` and `.invalidEndOfInput` UTF8 validation errors to communicate that the input was perhaps incomplete or not slices along scalar boundaries. In this scenario, `.invalidStartOfInput` is equivalent to `.unexpectedContinuation` with the range's lower bound equal to 0 and `.invalidEndOfInput` is equivalent to `.truncatedScalar` with the range's upper bound equal to `count`. @@ -764,7 +794,7 @@ Scalar-alignment can still be checked and managed by the caller through the `res #### View Collections -Another forumulation of these operations could be to provide a collection-like API phrased in terms of indices. Because `Collection`s are `Escapable`, we cannot conform nested `View` types to `Collection` so these would not benefit from any `Collection`-generic code, algorithms, etc. +Another forumulation of these operations could be to provide a collection-like API phrased in terms of indices. Because `Collection`s are `Escapable`, we cannot conform nested `View` types to `Collection` so these would not benefit from any `Collection`-generic code, algorithms, etc. A benefit of such `Collection`-like views is that it could help serve as adapter code for migration. Existing `Collection`-generic algorithms and methods could be converted to support `UTF8Span` via copy-paste-edit. That is, a developer could interact with `UTF8Span` ala: From 3f2e465ee76b61a621876375718d401c852ea13d Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 25 Mar 2025 13:06:06 -0700 Subject: [PATCH 4168/4563] Update 0459-enumerated-collection.md (#2754) --- proposals/0459-enumerated-collection.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0459-enumerated-collection.md b/proposals/0459-enumerated-collection.md index a2df267080..e9044be172 100644 --- a/proposals/0459-enumerated-collection.md +++ b/proposals/0459-enumerated-collection.md @@ -3,10 +3,10 @@ * Proposal: [SE-0459](0459-enumerated-collection.md) * Author: [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active review (January 28 – February 7, 2025)** +* Status: **Accepted** * Implementation: [swiftlang/swift#78092](https://github.com/swiftlang/swift/pull/78092) * Previous Proposal: [SE-0312](0312-indexed-and-enumerated-zip-collections.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-add-collection-conformance-for-enumeratedsequence/76680)) ([review](https://forums.swift.org/t/se-0459-add-collection-conformances-for-enumerated/77509)) +* Review: ([pitch](https://forums.swift.org/t/pitch-add-collection-conformance-for-enumeratedsequence/76680)) ([review](https://forums.swift.org/t/se-0459-add-collection-conformances-for-enumerated/77509)) ([acceptance](https://forums.swift.org/t/accepted-with-modification-se-0459-add-collection-conformances-for-enumerated/78082)) ## Introduction From b7cf8c0d72f72ff4ef92a117f85e2556359eaa70 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 25 Mar 2025 16:06:38 -0400 Subject: [PATCH 4169/4563] Update 0460-specialized.md (#2755) --- proposals/0460-specialized.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0460-specialized.md b/proposals/0460-specialized.md index 6f0979f9e7..ff54c65de9 100644 --- a/proposals/0460-specialized.md +++ b/proposals/0460-specialized.md @@ -3,7 +3,7 @@ * Proposal: [SE-0460](0460-specialized.md) * Authors: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active Review (January 29 ... February 11, 2025)** +* Status: **Accepted** * Implementation: Available in nightly toolchains using the underscored `@_specialize` * Review: ([pitch](https://forums.swift.org/t/pitch-explicit-specialization/76967)) ([review](https://forums.swift.org/t/se-0460-explicit-specialization/77541)) From a22bf6d4f2e8428e74b2d40c23f7aa3a2232c6e1 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 25 Mar 2025 16:12:49 -0400 Subject: [PATCH 4170/4563] Update 0460-specialized.md (#2756) --- proposals/0460-specialized.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0460-specialized.md b/proposals/0460-specialized.md index ff54c65de9..e39ffc84c8 100644 --- a/proposals/0460-specialized.md +++ b/proposals/0460-specialized.md @@ -5,7 +5,7 @@ * Review Manager: [Steve Canon](https://github.com/stephentyrone) * Status: **Accepted** * Implementation: Available in nightly toolchains using the underscored `@_specialize` -* Review: ([pitch](https://forums.swift.org/t/pitch-explicit-specialization/76967)) ([review](https://forums.swift.org/t/se-0460-explicit-specialization/77541)) +* Review: ([pitch](https://forums.swift.org/t/pitch-explicit-specialization/76967)) ([review](https://forums.swift.org/t/se-0460-explicit-specialization/77541)) ([acceptance](https://forums.swift.org/t/accepted-se-0460-explicit-specialization/78583)) ## Introduction From ae330664dc9d9dfc0f891eeb7299aadc7d5676d3 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 25 Mar 2025 14:31:45 -0700 Subject: [PATCH 4171/4563] Mark metatype keypaths and swift language mode proposals as implemented (#2757) * SE-0438: Mark proposal as implemented in Swift 6.1 * SE-0441: Mark proposal as implemented in Swift 6.1 New SwiftPM build settings are available in Swift 6.0 but `-language-mode` was introduced in 6.1 compiler. --- proposals/0438-metatype-keypath.md | 2 +- proposals/0441-formalize-language-mode-terminology.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0438-metatype-keypath.md b/proposals/0438-metatype-keypath.md index 94b2bdb954..e12bd4afb6 100644 --- a/proposals/0438-metatype-keypath.md +++ b/proposals/0438-metatype-keypath.md @@ -3,7 +3,7 @@ * Proposal: [SE-0438](0438-metatype-keypath.md) * Authors: [Amritpan Kaur](https://github.com/amritpan), [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Accepted** +* Status: **Implemented (Swift 6.1)** * Implementation: [apple/swift#73242](https://github.com/apple/swift/pull/73242) * Review: ([pitch](https://forums.swift.org/t/pitch-metatype-keypaths/70767)) ([review](https://forums.swift.org/t/se-0438-metatype-keypaths/72172)) ([acceptance](https://forums.swift.org/t/accepted-se-0438-metatype-keypaths/72878)) diff --git a/proposals/0441-formalize-language-mode-terminology.md b/proposals/0441-formalize-language-mode-terminology.md index dcfbc9d2d6..d34b6eeacf 100644 --- a/proposals/0441-formalize-language-mode-terminology.md +++ b/proposals/0441-formalize-language-mode-terminology.md @@ -3,7 +3,7 @@ * Proposal: [SE-0441](0441-formalize-language-mode-terminology.md) * Author: [James Dempsey](https://github.com/dempseyatgithub) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 6.1)** * Implementation: [swiftlang/swift-package-manager#7620](https://github.com/swiftlang/swift-package-manager/pull/7620), [swiftlang/swift#75564](https://github.com/swiftlang/swift/pull/75564) * Review: ([first pitch](https://forums.swift.org/t/pitch-formalize-swift-language-mode-naming-in-tools-and-api/71733)) ([second pitch](https://forums.swift.org/t/pitch-2-formalize-language-mode-naming-in-tools-and-api/72136)) ([review](https://forums.swift.org/t/se-0441-formalize-language-mode-terminology/73182)) ([acceptance](https://forums.swift.org/t/accepted-se-0441-formalize-language-mode-terminology/73716)) From 836695b49ec40bfd95cfb03b075d2eaa19d0a71f Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Tue, 25 Mar 2025 15:57:29 -0700 Subject: [PATCH 4172/4563] Update the epoch name to systemEpoch and add some prose about how it is system relative and not globally relative --- proposals/NNNN-ClockEpochs.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/NNNN-ClockEpochs.md b/proposals/NNNN-ClockEpochs.md index 88d77fe96c..b79cb30084 100644 --- a/proposals/NNNN-ClockEpochs.md +++ b/proposals/NNNN-ClockEpochs.md @@ -19,17 +19,17 @@ Not all clocks have a starting point, however in these cases they do. Generally, ## Proposed solution -Two new properties will be added, one to `SuspendingClock` and another to `ContinuousClock`. Both of these properties will be the epoch for which all `Instant` types are derived from; practically speaking this is the "zero" point for these clocks. +Two new properties will be added, one to `SuspendingClock` and another to `ContinuousClock`. Both of these properties will be the epoch for which all `Instant` types are derived from; practically speaking this is the "zero" point for these clocks. Since the values may be relative to the particular system they are being used on (albeit many may implement them literally as zero) they are named in accordance to reflect that they are designed to be representative of the system's sense of an epoch and should not be expected to be serializable across systems. ## Detailed design ```swift extension ContinousClock { - public var epoch: Instant { get } + public var systemEpoch: Instant { get } } extension SuspendingClock { - public var epoch: Instant { get } + public var systemEpoch: Instant { get } } ``` @@ -37,14 +37,14 @@ These can be used to gather information like for example the uptime of a system, ```swift let clock = ContinousClock() -let uptime = clock.now - clock.epoch +let uptime = clock.now - clock.systemEpoch ``` Or likewise; ```swift let clock = SuspendingClock() -let activeTime = clock.now - clock.epoch +let activeTime = clock.now - clock.systemEpoch ``` ## ABI compatibility From 30ab4bf0ab604860e14d51613f9ca409a57d6c5d Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 26 Mar 2025 08:18:23 +0900 Subject: [PATCH 4173/4563] Add why not tri-state return --- proposals/NNNN-SerialExecutor-isIsolated.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/proposals/NNNN-SerialExecutor-isIsolated.md b/proposals/NNNN-SerialExecutor-isIsolated.md index 0125b5baf3..053475c69b 100644 --- a/proposals/NNNN-SerialExecutor-isIsolated.md +++ b/proposals/NNNN-SerialExecutor-isIsolated.md @@ -172,6 +172,25 @@ This would be ideal, however also problematic since changing a protocol requirem In order to make adoption of this new mode less painful and not cause deprecation warnings to libraries which intend to support multiple versions of Swift, the `SerialExcecutor/checkIsolated` protocol requirement remains _not_ deprecated. It may eventually become deprecated in the future, but right now we have no plans of doing so. +### Offer a tri-state return value rather than `Bool` + +We briefly considered offering a tri-state `enum DetectedSerialExecutorIsolation` as the return value of `isIsolatingCurrentContext`, however could not find many realistic use-cases for it. + +The return type could be defined as: + +```swift +// not great name +enum DetectedSerialExecutorIsolation { + case isolated // returned when isolated by this executor + case notIsolated // returned when definitely NOT isolated by this executor + case unknown // when the isIsolatingCurrentContext could not determine if the caller is isolated or not +} +``` + +If we used the `.unknown` as default implementation of the new protocol requirement, this would allow for programatic detection if we called the default implementation, or an user provided implementation which could check a proper isolated/not-isolated state of the executing context. + +Technically there may exist new implementations which return the `.unknown` however it would have to be treated defensively as `.notIsolated` in any asserting APIs or other use-cases which rely on this check for runtime correctness. We are uncertain if introducing this tri-state is actually helpful in real situations and therefore the proposal currently proposes the use of a plain `Bool` value. + ## Changelog - removed the manual need to signal to the runtime that the specific executor supports the new checking mode. It is now detected by the compiler and runtime, checking for the presence of a non-default implementation of the protocol requirement. From e6f3aa93664df15ed68fdb713e8c5e7e46487b9c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 18 Mar 2025 15:00:48 -0700 Subject: [PATCH 4174/4563] Acceptance link for SE-0465 --- proposals/0465-nonescapable-stdlib-primitives.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0465-nonescapable-stdlib-primitives.md b/proposals/0465-nonescapable-stdlib-primitives.md index 1f8bc5fad4..2f0c6540d8 100644 --- a/proposals/0465-nonescapable-stdlib-primitives.md +++ b/proposals/0465-nonescapable-stdlib-primitives.md @@ -6,7 +6,7 @@ * Status: **Accepted** * Roadmap: [Improving Swift performance predictability: ARC improvements and ownership control][Roadmap] * Implementation: https://github.com/swiftlang/swift/pull/73258 -* Review: ([Review](https://forums.swift.org/t/se-0465-standard-library-primitives-for-nonescapable-types/78310)) ([Pitch](https://forums.swift.org/t/pitch-nonescapable-standard-library-primitives/77253)) +* Review: ([Acceptance](https://forums.swift.org/t/accepted-se-0465-standard-library-primitives-for-nonescapable-type/78637)) ([Review](https://forums.swift.org/t/se-0465-standard-library-primitives-for-nonescapable-types/78310)) ([Pitch](https://forums.swift.org/t/pitch-nonescapable-standard-library-primitives/77253)) [Roadmap]: https://forums.swift.org/t/a-roadmap-for-improving-swift-performance-predictability-arc-improvements-and-ownership-control/54206 [Pitch]: https://forums.swift.org/t/pitch-nonescapable-standard-library-primitives/77253 From f8846c3e939f93b4525d5450b0f2c4eea69485ed Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 25 Mar 2025 21:27:25 -0700 Subject: [PATCH 4175/4563] Initiate review of SE-0471: Improved Custom SerialExecutor isolation checking for Concurrency Runtime --- ...olated.md => 0471-SerialExecutor-isIsolated.md} | 13 +++++-------- ...-flow.graffle => 0471-is-isolated-flow.graffle} | Bin ...isolated-flow.png => 0471-is-isolated-flow.png} | Bin 3 files changed, 5 insertions(+), 8 deletions(-) rename proposals/{NNNN-SerialExecutor-isIsolated.md => 0471-SerialExecutor-isIsolated.md} (97%) rename proposals/{nnnn-is-isolated-flow.graffle => 0471-is-isolated-flow.graffle} (100%) rename proposals/{nnnn-is-isolated-flow.png => 0471-is-isolated-flow.png} (100%) diff --git a/proposals/NNNN-SerialExecutor-isIsolated.md b/proposals/0471-SerialExecutor-isIsolated.md similarity index 97% rename from proposals/NNNN-SerialExecutor-isIsolated.md rename to proposals/0471-SerialExecutor-isIsolated.md index 053475c69b..79227eea99 100644 --- a/proposals/NNNN-SerialExecutor-isIsolated.md +++ b/proposals/0471-SerialExecutor-isIsolated.md @@ -1,14 +1,11 @@ # Improved Custom SerialExecutor isolation checking for Concurrency Runtime -* Proposal: [SE-NNNN](...) +* Proposal: [SE-0471](0471-SerialExecutor-isIsolated.md) * Author: [Konrad 'ktoso' Malawski](https://github.com/ktoso) -* Review Manager: TBD -* Status: Implemented - * https://github.com/swiftlang/swift/pull/79788 - * https://github.com/swiftlang/swift/pull/79946 - +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active Review (March 25...April 8, 2025) +* Implementation: https://github.com/swiftlang/swift/pull/79788 & https://github.com/swiftlang/swift/pull/79946 * Pitch: [[Pitch][SerialExecutor] Improved Custom SerialExecutor isolation checking](https://forums.swift.org/t/pitch-serialexecutor-improved-custom-serialexecutor-isolation-checking/78237/) -* Review: TODO ## Introduction @@ -102,7 +99,7 @@ In most cases implementing this new API is preferable to implementing `checkIsol The newly proposed `isIsolatingCurrentContext()` function participates in the previously established runtime isolation checking flow, and happens _before_ any calls to `checkIsolated()` are attempted. The following diagram explains the order of calls issued by the runtime to dynamically verify an isolation when e.g. `assumeIsolated()` is called: -![diagram illustrating which method is called when](nnnn-is-isolated-flow.png) +![diagram illustrating which method is called when](0471-is-isolated-flow.png) diff --git a/proposals/nnnn-is-isolated-flow.graffle b/proposals/0471-is-isolated-flow.graffle similarity index 100% rename from proposals/nnnn-is-isolated-flow.graffle rename to proposals/0471-is-isolated-flow.graffle diff --git a/proposals/nnnn-is-isolated-flow.png b/proposals/0471-is-isolated-flow.png similarity index 100% rename from proposals/nnnn-is-isolated-flow.png rename to proposals/0471-is-isolated-flow.png From bd892637a7685658bbb4df243ce0b42e48924d87 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 25 Mar 2025 21:32:30 -0700 Subject: [PATCH 4176/4563] Link SE-0471 to the review thread --- proposals/0471-SerialExecutor-isIsolated.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0471-SerialExecutor-isIsolated.md b/proposals/0471-SerialExecutor-isIsolated.md index 79227eea99..dcbb66db9f 100644 --- a/proposals/0471-SerialExecutor-isIsolated.md +++ b/proposals/0471-SerialExecutor-isIsolated.md @@ -5,7 +5,7 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active Review (March 25...April 8, 2025) * Implementation: https://github.com/swiftlang/swift/pull/79788 & https://github.com/swiftlang/swift/pull/79946 -* Pitch: [[Pitch][SerialExecutor] Improved Custom SerialExecutor isolation checking](https://forums.swift.org/t/pitch-serialexecutor-improved-custom-serialexecutor-isolation-checking/78237/) +* Review: [Pitch](https://forums.swift.org/t/pitch-serialexecutor-improved-custom-serialexecutor-isolation-checking/78237/), [Review](https://forums.swift.org/t/se-0471-improved-custom-serialexecutor-isolation-checking-for-concurrency-runtime/78834) ## Introduction From a27f65da3f90ba8ad1faf144bf910fb43f89ca45 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 26 Mar 2025 22:14:07 +0900 Subject: [PATCH 4177/4563] Apply suggestions from code review Co-authored-by: Tony Allevato --- ...k-start-synchronously-on-caller-context.md | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/proposals/NNNN-task-start-synchronously-on-caller-context.md b/proposals/NNNN-task-start-synchronously-on-caller-context.md index ccb621e83f..557189546f 100644 --- a/proposals/NNNN-task-start-synchronously-on-caller-context.md +++ b/proposals/NNNN-task-start-synchronously-on-caller-context.md @@ -11,7 +11,7 @@ Swift Concurrency's primary means of entering an asynchronous context is creating a Task (structured or unstructured), and from there onwards it is possible to call asynchronous functions, and execution of the current work may _suspend_. -Entering the asynchronous context today incurs the creating and scheduling of a task to be executed at some later point in time. This initial delay may be wasteful for tasks which perform minimal or none (!) work at all. +Entering the asynchronous context today incurs the creating and scheduling of a task to be executed at some later point in time. This initial delay may be wasteful for tasks which perform minimal or no (!) work at all. This initial delay may also be problematic for some situations where it is known that we are executing on the "right actor" however are *not* in an asynchronous function and therefore in order to call some different asynchronous function we must create a new task and introduce subtle timing differences as compared to just being able to call the target function–which may be isolated to the same actor we're calling from–immediately. @@ -51,7 +51,7 @@ func synchronousFunction() { } ``` -The above example showcases a typical situation where this new API can be useful. While `assumeIsolated` gives us a specific isolation... it still would not allow us to call arbitrary async functions, as we are still in a synchronous context. +The above example showcases a typical situation where this new API can be useful. While `assumeIsolated` gives us a specific isolation, it still would not allow us to call arbitrary async functions, as we are still in a synchronous context. The proposed `Task.startSynchronously` API forms an async context on the calling thread/task/executor, and therefore allows us to call into async code, at the risk of overhanging on the calling executor. So while this should be used sparingly, it allows entering an asynchronous context *synchronously*. @@ -102,18 +102,18 @@ extension Task { @discardableResult public static func startSynchronously( - // SE-NNNN's proposed 'name: String? = nil' would be here + // SE-0469's proposed 'name: String? = nil' would be here if accepted priority: TaskPriority? = nil, executorPreference taskExecutor: consuming (any TaskExecutor)? = nil, - operation: sending @escaping async throws(Failure) -> Success, + operation: sending @escaping async throws(Failure) -> Success ) -> Task @discardableResult public static func startSynchronouslyDetached( - // SE-NNNN's proposed 'name: String? = nil' would be here + // SE-0469's proposed 'name: String? = nil' would be here if accepted priority: TaskPriority? = nil, executorPreference taskExecutor: consuming (any TaskExecutor)? = nil, - operation: sending @escaping async throws(Failure) -> Success, + operation: sending @escaping async throws(Failure) -> Success ) -> Task } ``` @@ -125,41 +125,41 @@ extension (Throwing)TaskGroup { // Same add semantics as 'addTask'. func startTaskSynchronously( - // SE-NNNN's proposed 'name: String? = nil' would be here + // SE-0469's proposed 'name: String? = nil' would be here priority: TaskPriority? = nil, executorPreference taskExecutor: (any TaskExecutor)? = nil, - operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult + operation: sending @escaping () async throws -> ChildTaskResult ) // Same add semantics as 'addTaskUnlessCancelled'. func startTaskSynchronouslyUnlessCancelled( - // SE-NNNN's proposed 'name: String? = nil' would be here + // SE-0469's proposed 'name: String? = nil' would be here priority: TaskPriority? = nil, executorPreference taskExecutor: (any TaskExecutor)? = nil, - operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult + operation: sending @escaping () async throws -> ChildTaskResult ) } extension (Throwing)DiscardingTaskGroup { // Same add semantics as 'addTask'. func startTaskSynchronously( - // SE-NNNN's proposed 'name: String? = nil' would be here + // SE-0469's proposed 'name: String? = nil' would be here priority: TaskPriority? = nil, executorPreference taskExecutor: (any TaskExecutor)? = nil, - operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult + operation: sending @escaping () async throws -> ChildTaskResult ) // Same add semantics as 'addTaskUnlessCancelled'. func startTaskSynchronouslyUnlessCancelled( - // SE-NNNN's proposed 'name: String? = nil' would be here + // SE-0469's proposed 'name: String? = nil' would be here priority: TaskPriority? = nil, executorPreference taskExecutor: (any TaskExecutor)? = nil, - operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult + operation: sending @escaping () async throws -> ChildTaskResult ) } ``` -The `startTaskSynchronously` mirrors the functionality of `addTask`, unconditionally adding the task to the group, while the `startTaskSynchronouslyUnlessCancelled` mirrors the `addTaskUnlessCancelled` which only adds the task to the group if the group (or task we're running in, and therefore the group as well) are not cancelled. +The `startTaskSynchronously` function mirrors the functionality of `addTask`, unconditionally adding the task to the group, while the `startTaskSynchronouslyUnlessCancelled` mirrors the `addTaskUnlessCancelled` which only adds the task to the group if the group (or task we're running in, and therefore the group as well) are not cancelled. ### Isolation rules @@ -256,7 +256,7 @@ After the suspension point though, there may have been other tasks executed on t Synchronously started tasks behave exactly the same as their fully asynchronous equivalents. -In short, cancellation, and priority escalation remains automatic for structured tasks created using TaskGroup APIs, however they do not propagate automatically for unstructured tasks created using the `Task.startSynchronously[Detached](...)` APIs. Task locals and base priority also functions the same way as usual; +In short, cancellation, and priority escalation remains automatic for structured tasks created using TaskGroup APIs, however they do not propagate automatically for unstructured tasks created using the `Task.startSynchronously[Detached](...)` APIs. Task locals and base priority also functions the same way as usual. The only difference in behavior is where these synchronously started tasks _begin_ their execution. From 8fa35dc01fbfe166b9de916d384d6aeefd2b5e8d Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 26 Mar 2025 22:14:46 +0900 Subject: [PATCH 4178/4563] Apply suggestions from code review --- proposals/NNNN-task-start-synchronously-on-caller-context.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-task-start-synchronously-on-caller-context.md b/proposals/NNNN-task-start-synchronously-on-caller-context.md index 557189546f..f9f7f27359 100644 --- a/proposals/NNNN-task-start-synchronously-on-caller-context.md +++ b/proposals/NNNN-task-start-synchronously-on-caller-context.md @@ -349,7 +349,7 @@ Using a combination of (a) `Task/startSynchronously`, (b) `Actor/assumeIsolated` ```swift func tryRunSynchronouslyOrAsynchronouslyOtherwise( - operation: sending @escaping @isolated(any) () async -> Success + operation: sending @escaping () async -> Success ) -> Task { guard let actor = operation.isolation else { // no specific isolation, just run async @@ -377,7 +377,7 @@ Or even better we could build the same API with structured concurrency: ```swift func tryRunSynchronouslyOrAsynchronouslyOtherwise( - operation: sending @escaping @isolated(any) () async throws -> Success + operation: sending @escaping () async throws -> Success ) async rethrows -> Success { /* same, but use TaskGroup inside */ } ``` From 4c523525549c2077307b7e4b7f1680e0a5d1511c Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 26 Mar 2025 08:15:55 -0700 Subject: [PATCH 4179/4563] Proposal revisions for SE-0461. (#2751) * [SE-0461] Rename `@execution(concurrent)` to `@concurrent`. * [SE-0461] Remove the migration-related warning in favor of automatic migration tooling. * [SE-0461] Add a section about removing `noasync` from the `assumeIsolated` API family. * [SE-0461] Add a section about region isolation rules. * [SE-0461] Update table of contents. --- proposals/0461-async-function-isolation.md | 238 ++++++++++++++------- 1 file changed, 159 insertions(+), 79 deletions(-) diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md index aecc52bea4..5ba6c142e1 100644 --- a/proposals/0461-async-function-isolation.md +++ b/proposals/0461-async-function-isolation.md @@ -26,22 +26,25 @@ async function always switches off of an actor to run. - [Motivation](#motivation) - [Proposed solution](#proposed-solution) - [Detailed design](#detailed-design) - - [The `@execution` attribute](#the-execution-attribute) + - [The `@execution` and `@concurrent` attributes](#the-execution-and-concurrent-attributes) - [`@execution(caller)` functions](#executioncaller-functions) - - [`@execution(concurrent)` functions](#executionconcurrent-functions) + - [`@concurrent` functions](#concurrent-functions) - [Task isolation inheritance](#task-isolation-inheritance) - [`#isolation` macro expansion](#isolation-macro-expansion) - [Isolation inference for closures](#isolation-inference-for-closures) - [Function conversions](#function-conversions) + - [Non-`@Sendable` function conversions](#non-sendable-function-conversions) + - [Region isolation rules](#region-isolation-rules) - [Executor switching](#executor-switching) + - [Dynamic actor isolation APIs in async contexts](#dynamic-actor-isolation-apis-in-async-contexts) - [Import-as-async heuristic](#import-as-async-heuristic) - [Source compatibility](#source-compatibility) - [ABI compatibility](#abi-compatibility) - [Implications on adoption](#implications-on-adoption) - [Alternatives considered](#alternatives-considered) - [Changing isolation inference behavior to implicitly capture isolated parameters](#changing-isolation-inference-behavior-to-implicitly-capture-isolated-parameters) - - [Use `nonisolated` instead of a separate `@execution(concurrent)` attribute](#use-nonisolated-instead-of-a-separate-executionconcurrent-attribute) - - [Use "isolation" terminology instead of "execution"](#use-isolation-terminology-instead-of-execution) + - [Use `nonisolated` instead of a separate `@concurrent` attribute](#use-nonisolated-instead-of-a-separate-concurrent-attribute) + - [Alternative syntax choices](#alternative-syntax-choices) - [Deprecate `nonisolated`](#deprecate-nonisolated) - [Don't introduce a type attribute for `@execution`](#dont-introduce-a-type-attribute-for-execution) - [Revisions](#revisions) @@ -201,7 +204,7 @@ behavior of existing code, so the change is gated behind the `AsyncCallerExecution` upcoming feature flag. To help stage in the new behavior, a new `@execution` attribute can be used to explicitly specify the execution semantics of an async function in any language mode. The -`@execution(concurrent)` attribute is an explicit spelling for the behavior of +`@concurrent` attribute is an explicit spelling for the behavior of async functions in language modes <= Swift 6, and the `@execution(caller)` attribute is an explicit spelling for async functions that run on the caller's actor. @@ -215,7 +218,7 @@ class NotSendable { @execution(caller) func performAsync() async { ... } - @execution(concurrent) + @concurrent func alwaysSwitch() async { ... } } @@ -232,34 +235,28 @@ actor MyActor { } ``` -`@execution(concurrent)` is the current default for nonisolated async +`@concurrent` is the current default for nonisolated async functions. `@execution(caller)` will become the default for async functions when the `AsyncCallerExecution` upcoming feature is enabled. ## Detailed design -The sections below will explicitly use `@execution(concurrent)` and +The sections below will explicitly use `@concurrent` and `@execution(caller)` to demonstrate examples that will behave consistently independent of upcoming features or language modes. However, note that the end state under the `AsyncCallerExecution` upcoming feature will mean that `@execution(caller)` is not necessary to explicitly write, and -`@execution(concurrent)` will likely be used sparingly because it has far +`@concurrent` will likely be used sparingly because it has far stricter data-race safety requirements. -### The `@execution` attribute +### The `@execution` and `@concurrent` attributes -`@execution` is a declaration and type attribute that specifies the execution -semantics of an async function. `@execution` must be written with an argument -of either `caller` or `concurrent`. The details of each argument are specified -in the following sections. +`@execution(caller)` and `@concurrent` are both declaration and type attributes +that specify the execution semantics of an async function. The details of +each attribute are specified in the following sections. -> _Naming rationale_: The term `concurrent` in `@execution(concurrent)` was -> chosen because the colloquial phrase "runs concurrently with actors" is a -> good way to describe the semantics of the function execution. Similarly, the -> async function can be described as running on the concurrent executor. - -Only (implicitly or explicitly) `nonisolated` functions can be marked with the -`@execution` attribute; it is an error to use the `@execution` attribute with +Only (implicitly or explicitly) `nonisolated` functions can be marked with +`@execution(caller)` or `@concurrent`; it is an error to use the these attributes with an isolation other than `nonisolated`, including global actors, isolated parameters, and `@isolated(any)`: @@ -280,13 +277,15 @@ actor MyActor { } ``` -The `@execution` attribute can be used together with `@Sendable` or `sending`. +`@execution(caller)` and `@concurrent` can be used together with `@Sendable` or +`sending`. -The `@execution` attribute is preserved in the type system so that the execution -semantics can be distinguished for function vales. +`@execution(caller)` and `@concurrent` are preserved in the type system so that +the execution semantics can be distinguished for function vales. -The `@execution` attribute cannot be applied to synchronous functions. This is -an artificial limitation that could later be lifted if use cases arise. +`@execution(caller)` and `@concurrent` cannot be applied to synchronous +functions. This is an artificial limitation that could later be lifted if use +cases arise. #### `@execution(caller)` functions @@ -349,23 +348,23 @@ In the above code, the calls to `closure` from `callSendableClosure` run on the main actor, because `closure` is `@execution(caller)` and `callSendableClosure` is main actor isolated. -#### `@execution(concurrent)` functions +#### `@concurrent` functions Async functions can be declared to always switch off of an actor to run using -the `@execution(concurrent)` attribute: +the `@concurrent` attribute: ```swift struct S: Sendable { - @execution(concurrent) + @concurrent func alwaysSwitch() async { ... } } ``` -The type of an `@execution(concurrent)` function declaration is an -`@execution(concurrent)` function type. Details on function conversions are +The type of an `@concurrent` function declaration is an +`@concurrent` function type. Details on function conversions are covered in a [later section](#function-conversions). -When an `@execution(concurrent)` function is called from a context that can +When an `@concurrent` function is called from a context that can run on an actor, including `@execution(caller)` functions or actor-isolated functions, sendable checking is performed on the argument and result values. Either the argument and result values must have a type that conforms to @@ -375,7 +374,7 @@ outside of the actor: ```swift class NotSendable {} -@execution(concurrent) +@concurrent func alwaysSwitch(ns: NotSendable) async { ... } actor MyActor { @@ -395,7 +394,7 @@ actor MyActor { Unstructured tasks created in nonisolated functions never run on an actor unless explicitly specified. This behavior is consistent for all nonisolated functions, including synchronous functions, `@execution(caller)` async -functions, and `@execution(concurrent)` async functions. +functions, and `@concurrent` async functions. For example: @@ -483,7 +482,7 @@ struct Program { } ``` -In an `@execution(concurrent)` function, the `#isolation` macro expands to +In an `@concurrent` function, the `#isolation` macro expands to `nil`. ### Isolation inference for closures @@ -617,21 +616,21 @@ conversion rules for synchronous `nonisolated` functions and asynchronous `@execution(caller) nonisolated` functions are the same; they are both represented under the "Nonisolated" category in the table: -| Old isolation | New isolation | Crosses Boundary | -|--------------------------|----------------------------|------------------| -| Nonisolated | Actor isolated | No | -| Nonisolated | `@isolated(any)` | No | -| Nonisolated | `@execution(concurrent)` | Yes | -| Actor isolated | Actor isolated | Yes | -| Actor isolated | `@isolated(any)` | No | -| Actor isolated | Nonisolated | Yes | -| Actor isolated | `@execution(concurrent)` | Yes | -| `@isolated(any)` | Actor isolated | Yes | -| `@isolated(any)` | Nonisolated | Yes | -| `@isolated(any)` | `@execution(concurrent)` | Yes | -| `@execution(concurrent)` | Actor isolated | Yes | -| `@execution(concurrent)` | `@isolated(any)` | No | -| `@execution(concurrent)` | Nonisolated | Yes | +| Old isolation | New isolation | Crosses Boundary | +|----------------------|------------------------|------------------| +| Nonisolated | Actor isolated | No | +| Nonisolated | `@isolated(any)` | No | +| Nonisolated | `@concurrent` | Yes | +| Actor isolated | Actor isolated | Yes | +| Actor isolated | `@isolated(any)` | No | +| Actor isolated | Nonisolated | Yes | +| Actor isolated | `@concurrent` | Yes | +| `@isolated(any)` | Actor isolated | Yes | +| `@isolated(any)` | Nonisolated | Yes | +| `@isolated(any)` | `@concurrent` | Yes | +| `@concurrent` | Actor isolated | Yes | +| `@concurrent` | `@isolated(any)` | No | +| `@concurrent` | Nonisolated | Yes | #### Non-`@Sendable` function conversions @@ -671,7 +670,7 @@ if the original function must leave the actor in order to be called: ```swift @execution(caller) func convert( - fn1: @escaping @execution(concurrent) () async -> Void, + fn1: @escaping @concurrent () async -> Void, ) async { let fn2: @MainActor () async -> Void = fn1 // error @@ -709,6 +708,45 @@ func call(_ closure: () -> NotSendable) -> NotSendable { } ``` +### Region isolation rules + +`@execution(caller)` functions have the same region isolation rules as +synchronous `nonisolated` functions. When calling an `@execution(caller)` +function, all non-`Sendable` parameter and result values are merged into +the same region, but they are only merged into the caller's actor region if +one of those non-`Sendable` values is already in the actor's region. + +For example: + +```swift +class NotSendable {} + +@execution(caller) +nonisolated func identity(_ t: T) async -> T { + return t +} + +actor MyActor { + func isolatedToSelf() async -> sending NotSendable { + let ns = NotSendable() + return await identity(ns) + } +} +``` + +The above code is valid; the implementation of `identity` can't access the +actor's state unless isolated state is passed in via one of the parameters. +Note that this code would be invalid if `identity` accepted an isolated +parameter, because the non-`Sendable` parameters and results would always be +merged into the actor's region. + +This proposal allows you to access `#isolation` in the implementation of an +`@execution(caller)` function for the purpose of forwarding it along to a +method that accepts an `isolated (any Actor)?`. This is still safe, because +there's no way to access the actor's isolated state via the `Actor` protocol, +and dynamic casting to a concrete actor type will not result in a value that +the function is known to be isolated to. + ### Executor switching Async functions switch executors in the implementation when entering the @@ -717,7 +755,7 @@ functions do not have the ability to switch executors. If a call to a synchronous function crosses an isolation boundary, the call must happen in an async context and the executor switch happens at the caller. -`@execution(concurrent)` async functions switch to the generic executor, and +`@concurrent` async functions switch to the generic executor, and all other async functions switch to the isolated actor's executor. ```swift @@ -729,7 +767,7 @@ all other async functions switch to the isolated actor's executor. // switch to main actor executor } -@execution(concurrent) func runOnGenericExecutor() async { +@concurrent func runOnGenericExecutor() async { // switch to generic executor await Task { @MainActor in @@ -785,6 +823,14 @@ Otherwise, the code will risk a data-race, because the task executor preference does not apply to actor-isolated methods with custom executors, and the nonisolated async method can be passed mutable state from the actor. +### Dynamic actor isolation APIs in async contexts + +Because nonisolated async functions may now execute on a specific actor at +runtime, the APIs in the Concurrency library for enforcing actor isolation +assertions and preconditions are now useful in these contexts. As such, the +`noasync` attribute will be removed from `assertIsolated`, `assumeIsolated`, +and `preconditionIsolated` on `Actor` and `MainActor`. + ### Import-as-async heuristic Nonisolated functions imported from Objective-C that match the import-as-async @@ -803,14 +849,14 @@ not change -- it will not be gated behind the upcoming feature. This proposal changes the semantics of nonisolated async functions when the upcoming feature flag is enabled. Without the upcoming feature flag, the default -for nonisolated async functions is `@execution(concurrent)`. When the upcoming +for nonisolated async functions is `@concurrent`. When the upcoming feature flag is enabled, the default for nonisolated async functions changes to `@execution(caller)`. This applies to both function declarations and function values that are nonisolated (either implicitly or explicitly). Changing the default execution semantics of nonisolated async functions has minor source compatibility impact if the implementation calls an -`@execution(concurrent)` function and passes non-Sendable state in the actor's +`@concurrent` function and passes non-Sendable state in the actor's region. In addition to the source compatibility impact, the change can also regress performance of existing code if, for example, a specific async function relied on running off of the main actor when called from the main actor to @@ -824,25 +870,14 @@ flag, but means something different. Many programmers have internalized the SE-0338 semantics, and making this change several years after SE-0338 was accepted creates an unfortunate intermediate state where it's difficult to understand the semantics of a nonisolated async function without understanding -the build settings of the module you're writing code in. To mitigate these -consequences, the compiler will emit warnings in all language modes -that do not enable this upcoming feature to prompt programmers to explicitly -specify the execution semantics of a nonisolated async function. - -Without the upcoming feature enabled, the compiler will warn if neither -attribute is specified on a nonisolated async function. With the -upcoming feature enabled, the default for a nonisolated async -function is `@execution(caller)`. Packages that must support older Swift tools -versions can use `#if hasAttribute(execution)` to silence the warning while -maintaining compatibility with tools versions back to Swift 5.8 when -`hasAttribute` was introduced: +the build settings of the module you're writing code in. -```swift -#if hasAttribute(execution) -@execution(concurrent) -#endif -public func myAsyncAPI() async { ... } -``` +To make it easy to discover what kind of async function you're working with, +SourceKit will surface the implicit `@execution(caller)` or `@concurrent` +attribute for IDE inspection features like Quick Help in Xcode and Hover in +VSCode. To ease the transition to the upcoming feature flag, [migration +tooling][adoption-tooling] will provide fix-its to preserve behavior by +annotating nonisolated async functions with `@concurrent`. ## ABI compatibility @@ -863,7 +898,7 @@ public func myAsyncFunc() async { // original implementation } -@execution(concurrent) +@concurrent @_silgen_name(...) // to preserve the original symbol name @usableFromInline internal func abi_myAsyncFunc() async { @@ -880,7 +915,7 @@ can be made inlinable. `@execution(caller)` functions must accept an implicit actor parameter. This means that adding `@execution(caller)` to a function that is actor-isolated, or -changing a function from `@execution(concurrent)` to `@execution(caller)`, is +changing a function from `@concurrent` to `@execution(caller)`, is not a resilient change. ## Alternatives considered @@ -895,7 +930,7 @@ potential compromise is to keep the current isolation inference behavior, and offer fix-its to capture the actor if there are any data-race safety errors from capturing state in the actor's region. -### Use `nonisolated` instead of a separate `@execution(concurrent)` attribute +### Use `nonisolated` instead of a separate `@concurrent` attribute It's tempting to not introduce a new attribute to control where an async function executes, and instead control this behavior with an explicit @@ -906,9 +941,43 @@ reasons: `nonisolated` by default, regardless of whether it's applied to synchronous or async functions. 2. This approach cuts off the future direction of allowing - `@execution(concurrent)` on synchronous functions. - -### Use "isolation" terminology instead of "execution" + `@concurrent` on synchronous functions. + +### Alternative syntax choices + +This proposal was originally pitched using the `@concurrent` syntax, and many +reviewers surfaced objects about why `@concurrent` may be misleading, such as: + +* `@concurrent` is not the only source of concurrency; concurrency can arise from + many other things. +* The execution of an `@concurrent` function is not concurrent from the local + perspective of the current task. + +It's true that concurrency can only arise if there are multiple "impetuses" +(such as tasks or event sources) in the program that are running with different +isolation. But for the most part, we can assume that there are multiple +impetuses; and while those impetuses might otherwise share isolation, +`@concurrent` is the only isolation specification under this proposal that +guarantees that they do not and therefore forces concurrency. Indeed, we expect +that programmers will be reaching for `@concurrent` exactly for that reason: +they want the current function to run concurrently with whatever else might +happen in the process. So, this proposal uses `@concurrent` because out of the +other alternatives we explored, it best reflects the programmer's intent for +using the attribute. + +A previous iteration of this proposal used the syntax `@execution(concurrent)` +instead of `@concurrent`. The review thread explored several variations of +this syntax, including `@executor(concurrent)` and `@executor(global)`. + +However, `@execution` or `@executor` encourages +thinking about async function semantics in terms of the lower level model of +executors and threads, and we should be encouraging programmers to think about +these semantics at the higher abstraction level of actor isolation and tasks. +Trying to understand the semantics in proposal in terms of executors can also +be misleading, both because isolation does not always map naively to executor +requests and because executors are used for other things than isolation. +For example, an `@executor(global)` function could end up running on some +executor other than the global executor via task executor preferences. Another possibility is to use isolation terminology instead of `@execution` for the syntax. This direction does not accomplish the goal of having a @@ -954,6 +1023,15 @@ function reference instead of when the function is called. ## Revisions +The proposal was revised with the following changes after the first review: + +* Renamed `@execution(concurrent)` back to `@concurrent`. +* Removed the unconditional warning about nonisolated async functions that + don't explicitly specify `@execution(caller)` or `@concurrent`. +* Removed `noasync` from the `assumeIsolated` API family. +* Specified the region isolation rules for `@execution(caller)` functions [as + discussed in the first review][region-isolation]. + The proposal was revised with the following changes after the pitch discussion: * Gate the behavior change behind an `AsyncCallerExecution` upcoming feature @@ -970,3 +1048,5 @@ The proposal was revised with the following changes after the pitch discussion: [SE-0297]: /proposals/0297-concurrency-objc.md [SE-0338]: /proposals/0338-clarify-execution-non-actor-async.md [SE-0421]: /proposals/0421-generalize-async-sequence.md +[adoption-tooling]: https://forums.swift.org/t/pitch-adoption-tooling-for-upcoming-features/77936 +[region-isolation]: https://forums.swift.org/t/se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/77987/36 \ No newline at end of file From 11f0e9e8c319a0e37a2ccdd8dedcceddd24260df Mon Sep 17 00:00:00 2001 From: Dmitrii Galimzianov Date: Wed, 19 Mar 2025 02:51:46 +0100 Subject: [PATCH 4180/4563] Warning Control Settings for SwiftPM --- proposals/NNNN-swiftpm-warning-control.md | 285 ++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 proposals/NNNN-swiftpm-warning-control.md diff --git a/proposals/NNNN-swiftpm-warning-control.md b/proposals/NNNN-swiftpm-warning-control.md new file mode 100644 index 0000000000..622b8c9971 --- /dev/null +++ b/proposals/NNNN-swiftpm-warning-control.md @@ -0,0 +1,285 @@ +# Warning Control Settings for SwiftPM + +* Proposal: [SE-NNNN](NNNN-swiftpm-warning-control.md) +* Authors: [Dmitrii Galimzianov](https://github.com/DmT021) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [swiftlang/swift-package-manager#8315](https://github.com/swiftlang/swift-package-manager/pull/8315) +* Review: ([Pitch](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666)) +* Previous Proposal: [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md) + +## Introduction + +This proposal adds new settings to SwiftPM to control how the Swift, C, and C++ compilers treat warnings during the build process. It builds on [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md), which introduced warning control flags for the Swift compiler but left SwiftPM support as a future direction. + +## Motivation + +The Swift Package Manager currently lacks a unified way to control warnings across Swift, C, and C++ compilation. This limitation forces developers to either use `unsafeFlags` or accept the default warning settings. + +## Proposed solution + +This proposal introduces new methods to SwiftPM's build settings API, allowing fine-grained control over warnings. + +### API + +#### Cross-language API (Swift, C, and C++) + +```swift +/// The level at which a compiler warning should be treated. +public enum WarningLevel: String { + /// Treat as a warning. + /// + /// Warnings will be displayed during compilation but will not cause the build to fail. + case warning + + /// Treat as an error. + /// + /// Warnings will be elevated to errors, causing the build to fail if any such warnings occur. + case error +} + +public static func treatAllWarnings( + as level: WarningLevel, + _ condition: BuildSettingCondition? = nil +) -> SwiftSetting // or CSetting or CXXSetting + +public static func treatWarning( + _ name: String, + as level: WarningLevel, + _ condition: BuildSettingCondition? = nil +) -> SwiftSetting // or CSetting or CXXSetting +``` + +#### C/C++-specific API + +In C/C++ targets, we can also enable or disable specific warning groups, in addition to controlling their severity. + +```swift +public static func enableWarning( + _ name: String, + _ condition: BuildSettingCondition? = nil +) -> CSetting // or CXXSetting + +public static func disableWarning( + _ name: String, + _ condition: BuildSettingCondition? = nil +) -> CSetting // or CXXSetting +``` +_The necessity of these functions is also explained below in the Alternatives considered section._ + +### Example usage + +```swift +.target( + name: "MyLib", + swiftSettings: [ + .treatAllWarnings(as: .error), + .treatWarning("DeprecatedDeclaration", as: .warning), + ], + cSettings: [ + .enableWarning("all"), + .disableWarning("unused-function"), + + .treatAllWarnings(as: .error), + .treatWarning("unused-variable", as: .warning), + ], + cxxSettings: [ + .enableWarning("all"), + .disableWarning("unused-function"), + + .treatAllWarnings(as: .error), + .treatWarning("unused-variable", as: .warning), + ] +) +``` + +## Detailed design + +### Settings and their corresponding compiler flags + +| Method | Swift | C/C++ | +|--------|-------|-------| +| `treatAllWarnings(as: .error)` | `-warnings-as-errors` | `-Werror` | +| `treatAllWarnings(as: .warning)` | `-no-warnings-as-errors` | `-Wno-error` | +| `treatWarning("XXXX", as: .error)` | `-Werror XXXX` | `-Werror=XXXX` | +| `treatWarning("XXXX", as: .warning)` | `-Wwarning XXXX` | `-Wno-error=XXXX` | +| `enableWarning("XXXX")` | N/A | `-WXXXX` | +| `disableWarning("XXXX")` | N/A | `-Wno-XXXX` | + +### Order of settings evaluation + +The order in which warning control settings are specified in a target's settings array directly affects the order of the resulting compiler flags. This is critical because when multiple flags affect the same warning group, compilers apply them sequentially with the last flag taking precedence. + +For example, consider these two different orderings for C++ settings: + +```swift +// Example 1: "unused-variable" in front of "unused" +cxxSettings: [ + .treatWarning("unused-variable", as: .error), + .treatWarning("unused", as: .warning), +] + +// Example 2: "unused" in front of "unused-variable" +cxxSettings: [ + .treatWarning("unused", as: .warning), + .treatWarning("unused-variable", as: .error), +] +``` + +In Example 1, the compiler will receive flags in this order: +``` +-Werror=unused-variable -Wno-error=unused +``` +Since "unused-variable" is a specific subgroup of the broader "unused" group, and the "unused" flag is applied last, all unused warnings (including unused-variable) will be treated as warnings. + +In Example 2, the compiler will receive flags in this order: +``` +-Wno-error=unused -Werror=unused-variable +``` +Due to the "last one wins" rule, unused-variable warnings will be treated as errors, while other unused warnings remain as warnings. + +The same principle applies when combining any of the new build settings: + +```swift +cxxSettings: [ + .enableWarning("all"), // Enable the "all" warning group + .enableWarning("extra"), // Enable the "extra" warning group + .disableWarning("unused-parameter"), // Disable the "unused-parameter" warning group + .treatAllWarnings(as: .error), // Treat all warnings as errors + .treatWarning("unused", as: .warning), // Keep warnings of the "unused" group as warnings +] +``` + +This will result in compiler flags: +``` +-Wall -Wextra -Wno-unused-parameter -Werror -Wno-error=unused +``` + +When configuring warnings, be mindful of the order to achieve the desired behavior. + +### Remote targets behavior + +When a target is remote (pulled from a package dependency rather than defined in the local package), the warning control settings specified in the manifest do not apply to it. SwiftPM will strip all of the warning control flags for remote targets and substitute them with options for suppressing warnings (`-w` for Clang and `-suppress-warnings` for Swift). + +This behavior is already in place but takes into account only `-warnings-as-errors` (for Swift) and `-Werror` (for Clang) flags. We expand this list to include the following warning-related flags: + +**For C/C++:** +* `-Wxxxx` +* `-Wno-xxxx` +* `-Werror` +* `-Werror=xxxx` +* `-Wno-error` +* `-Wno-error=xxxx` + +**For Swift:** +* `-warnings-as-errors` +* `-no-warnings-as-errors` +* `-Wwarning xxxx` +* `-Werror xxxx` + +This approach ensures that warning control settings are applied only to the targets you directly maintain in your package, while dependencies remain buildable without warnings regardless of their warning settings. + +### Interaction with command-line flags + +SwiftPM allows users to pass additional flags to the compilers using the `-Xcc`, `-Xswiftc`, and `-Xcxx` options with the `swift build` command. These flags are appended **after** the flags generated from the package manifest. + +This ordering enables users to modify or override package-defined warning settings without modifying the package manifest. + +#### Example + +```swift +let package = Package( + name: "MyExecutable", + targets: [ + // C target with warning settings + .target( + name: "cfoo", + cSettings: [ + .enableWarning("all"), + .treatAllWarnings(as: .error), + .treatWarning("unused-variable", as: .warning), + ] + ), + // Swift target with warning settings + .executableTarget( + name: "swiftfoo", + swiftSettings: [ + .treatAllWarnings(as: .error), + .treatWarning("DeprecatedDeclaration", as: .warning), + ] + ), + ] +) +``` + +When built with additional command-line flags: + +```sh +swift build -Xcc -Wno-error -Xswiftc -no-warnings-as-errors +``` + +The resulting compiler invocations will include both sets of flags: + +``` +# C compiler invocation +clang ... -Wall -Werror -Wno-error=unused-variable ... -Wno-error ... + +# Swift compiler invocation +swiftc ... -warnings-as-errors -Wwarning DeprecatedDeclaration ... -no-warnings-as-errors -Xcc -Wno-error ... +``` + +Flags are processed from left to right, and since `-no-warnings-as-errors` and `-Wno-error` apply globally to all warnings, they override the warning treating flags defined in the package manifest. + +#### Limitations + +This approach has a limitation when used with `-suppress-warnings`, which is mutually exclusive with other warning control flags: + +```sh +swift build -Xswiftc -suppress-warnings +``` + +Results in compiler errors: + +``` +error: conflicting options '-warnings-as-errors' and '-suppress-warnings' +error: conflicting options '-Wwarning' and '-suppress-warnings' +``` + + +## Security + +This change has no impact on security, safety, or privacy. + +## Impact on existing packages + +The proposed API will only be available to packages that specify a tools version equal to or later than the SwiftPM version in which this functionality is implemented. + +## Alternatives considered + +### Disabling a warning via a treat level + +Clang allows users to completely disable a specific warning, so for C/C++ settings we could implement that as a new case in the `WarningLevel` enum: + +```swift +public enum WarningLevel { + case warning + case error + case ignored +} +``` + +_(Since Swift doesn't allow selective warning suppression, we would actually have to split the enum into two: `SwiftWarningLevel` and `CFamilyWarningLevel`)_ + +But some warnings in Clang are disabled by default. If we simply pass `-Wno-error=unused-variable`, the compiler won't actually produce a warning for an unused variable. It only makes sense to use it if we have enabled the warning: `-Wunused-variable -Werror -Wno-error=unused-variable`. + +This necessitates separate functions to enable and disable warnings. Therefore, instead of `case ignored`, we propose the functions `enableWarning` and `disableWarning`. + +## Future directions + +### Package-level settings + +It has been noted that warning control settings are often similar across all targets. It makes sense to declare them at the package level while allowing target-level customizations. However, many other settings would also likely benefit from such inheritance, and SwiftPM doesn't currently provide such an option. Therefore, it was decided to factor this improvement out and look at all the settings holistically in the future. + +## Acknowledgments + +Thank you to [Doug Gregor](https://github.com/douggregor) for the motivation, and to both [Doug Gregor](https://github.com/douggregor) and [Holly Borla](https://github.com/hborla) for their guidance during the implementation of this API. From 89c8849dbd83059250087eef8471784de66a40b3 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 27 Mar 2025 12:01:28 -0400 Subject: [PATCH 4181/4563] Assign SE-0472. --- ...d => 0472-task-start-synchronously-on-caller-context.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-task-start-synchronously-on-caller-context.md => 0472-task-start-synchronously-on-caller-context.md} (98%) diff --git a/proposals/NNNN-task-start-synchronously-on-caller-context.md b/proposals/0472-task-start-synchronously-on-caller-context.md similarity index 98% rename from proposals/NNNN-task-start-synchronously-on-caller-context.md rename to proposals/0472-task-start-synchronously-on-caller-context.md index f9f7f27359..a5c4701336 100644 --- a/proposals/NNNN-task-start-synchronously-on-caller-context.md +++ b/proposals/0472-task-start-synchronously-on-caller-context.md @@ -1,11 +1,11 @@ # Starting tasks synchronously from caller context -* Proposal: [SE-NNNN](NNNN-task-start-synchronously-on-caller-context.md) +* Proposal: [SE-0472](0472-task-start-synchronously-on-caller-context.md) * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) -* Review Manager: TBD +* Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Partially implemented on `main`** * Implementation: [TODO](https://github.com/swiftlang/swift/pull/79608) -* Review: ... +* Review: ([pitch](https://forums.swift.org/t/pitch-concurrency-starting-tasks-synchronously-from-caller-context/77960/)) ## Introduction From cf5f862af80a58b1041046a67b5325507dd910de Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 27 Mar 2025 12:05:41 -0400 Subject: [PATCH 4182/4563] Add review thread link to SE-0472. --- proposals/0472-task-start-synchronously-on-caller-context.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0472-task-start-synchronously-on-caller-context.md b/proposals/0472-task-start-synchronously-on-caller-context.md index a5c4701336..4e9daacd9f 100644 --- a/proposals/0472-task-start-synchronously-on-caller-context.md +++ b/proposals/0472-task-start-synchronously-on-caller-context.md @@ -4,8 +4,8 @@ * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Partially implemented on `main`** -* Implementation: [TODO](https://github.com/swiftlang/swift/pull/79608) -* Review: ([pitch](https://forums.swift.org/t/pitch-concurrency-starting-tasks-synchronously-from-caller-context/77960/)) +* Implementation: https://github.com/swiftlang/swift/pull/79608 +* Review: ([pitch](https://forums.swift.org/t/pitch-concurrency-starting-tasks-synchronously-from-caller-context/77960/)) ([review](https://forums.swift.org/t/se-0472-starting-tasks-synchronously-from-caller-context/78883)) ## Introduction From 8521e61fea904c5c4953e52a5d57ffa5b66ab96c Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 27 Mar 2025 13:24:13 -0700 Subject: [PATCH 4183/4563] Prepare SE-0461 for re-review focused on naming. (#2752) * [SE-0461] Rename `@execution(caller)` to `nonisolated(nonsending)`. * [SE-0461] Elaborate on the alternative syntax choices. --- proposals/0461-async-function-isolation.md | 342 +++++++++++++-------- 1 file changed, 214 insertions(+), 128 deletions(-) diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md index 5ba6c142e1..8fd24a5b28 100644 --- a/proposals/0461-async-function-isolation.md +++ b/proposals/0461-async-function-isolation.md @@ -26,9 +26,8 @@ async function always switches off of an actor to run. - [Motivation](#motivation) - [Proposed solution](#proposed-solution) - [Detailed design](#detailed-design) - - [The `@execution` and `@concurrent` attributes](#the-execution-and-concurrent-attributes) - - [`@execution(caller)` functions](#executioncaller-functions) - - [`@concurrent` functions](#concurrent-functions) + - [`nonisolated(nonsending)` functions](#nonisolatednonsending-functions) + - [`@concurrent` functions](#concurrent-functions) - [Task isolation inheritance](#task-isolation-inheritance) - [`#isolation` macro expansion](#isolation-macro-expansion) - [Isolation inference for closures](#isolation-inference-for-closures) @@ -45,8 +44,13 @@ async function always switches off of an actor to run. - [Changing isolation inference behavior to implicitly capture isolated parameters](#changing-isolation-inference-behavior-to-implicitly-capture-isolated-parameters) - [Use `nonisolated` instead of a separate `@concurrent` attribute](#use-nonisolated-instead-of-a-separate-concurrent-attribute) - [Alternative syntax choices](#alternative-syntax-choices) + - [No explicit spelling for `nonisolated(nonsending)`](#no-explicit-spelling-for-nonisolatednonsending) + - [Justification for `@concurrent`](#justification-for-concurrent) + - [`@executor`](#executor) + - [`@isolated`](#isolated) + - [`nonisolated` argument spelling](#nonisolated-argument-spelling) - [Deprecate `nonisolated`](#deprecate-nonisolated) - - [Don't introduce a type attribute for `@execution`](#dont-introduce-a-type-attribute-for-execution) + - [Don't introduce a type attribute for `@concurrent`](#dont-introduce-a-type-attribute-for-concurrent) - [Revisions](#revisions) ## Motivation @@ -202,22 +206,35 @@ actor MyActor { Changing the default execution semantics of async functions can change the behavior of existing code, so the change is gated behind the `AsyncCallerExecution` upcoming feature flag. To help stage in the new -behavior, a new `@execution` attribute can be used to explicitly specify the -execution semantics of an async function in any language mode. The -`@concurrent` attribute is an explicit spelling for the behavior of -async functions in language modes <= Swift 6, and the `@execution(caller)` -attribute is an explicit spelling for async functions that run on the caller's -actor. +behavior, new syntax can be used to explicitly specify the +execution semantics of an async function in any language mode. -For example: +A new `nonsending` argument can be written with `nonisolated` to indicate +that by default, the argument and result values are not sent over an +isolation boundary when the function is called: ```swift class NotSendable { - func performSync() { ... } - - @execution(caller) + nonisolated(nonsending) func performAsync() async { ... } +} + +actor MyActor { + let x: NotSendable + + func call() async { + await x.performAsync() // okay + } +} +``` +The `@concurrent` attribute is an explicit spelling for the behavior of +async functions in language modes <= Swift 6. `@concurrent` indicates +that calling the function always switches off of an actor to run, so +the function will run concurrently with other tasks on the caller's actor: + +```swift +class NotSendable { @concurrent func alwaysSwitch() async { ... } } @@ -226,77 +243,35 @@ actor MyActor { let x: NotSendable func call() async { - x.performSync() // okay - - await x.performAsync() // okay - await x.alwaysSwitch() // error } } ``` `@concurrent` is the current default for nonisolated async -functions. `@execution(caller)` will become the default for async functions +functions. `nonisolated(nonsending)` will become the default for async functions when the `AsyncCallerExecution` upcoming feature is enabled. ## Detailed design The sections below will explicitly use `@concurrent` and -`@execution(caller)` to demonstrate examples that will behave consistently +`nonisolated(nonsending)` to demonstrate examples that will behave consistently independent of upcoming features or language modes. However, note that the end state under the `AsyncCallerExecution` upcoming feature will mean that -`@execution(caller)` is not necessary to explicitly write, and +`(nonsending)` is not necessary to explicitly write, and `@concurrent` will likely be used sparingly because it has far stricter data-race safety requirements. -### The `@execution` and `@concurrent` attributes - -`@execution(caller)` and `@concurrent` are both declaration and type attributes -that specify the execution semantics of an async function. The details of -each attribute are specified in the following sections. - -Only (implicitly or explicitly) `nonisolated` functions can be marked with -`@execution(caller)` or `@concurrent`; it is an error to use the these attributes with -an isolation other than `nonisolated`, including global actors, isolated -parameters, and `@isolated(any)`: - -```swift -actor MyActor { - var value = 0 - - // error: '@execution(caller)' can only be used with 'nonisolated' methods - @execution(caller) - func isolatedToSelf() async { - value += 1 - } - - @execution(caller) - nonisolated func canRunAnywhere() async { - // cannot access 'value' or other actor-isolated state - } -} -``` - -`@execution(caller)` and `@concurrent` can be used together with `@Sendable` or -`sending`. +### `nonisolated(nonsending)` functions -`@execution(caller)` and `@concurrent` are preserved in the type system so that -the execution semantics can be distinguished for function vales. - -`@execution(caller)` and `@concurrent` cannot be applied to synchronous -functions. This is an artificial limitation that could later be lifted if use -cases arise. - -#### `@execution(caller)` functions - -Async functions annotated with `@execution(caller)` will always run on the +Async functions annotated with `nonisolated(nonsending)` will always run on the caller's actor: ```swift class NotSendable { func performSync() { ... } - @execution(caller) + nonisolated(nonsending) func performAsync() async { ... } } @@ -313,27 +288,29 @@ actor MyActor { In the above code, the call to `x.performAsync()` continues running on the `self` actor instance. The code does not produce a data-race safety error, -because the `NotSendable` instance `x` does not leave the actor. +because the `NotSendable` instance `x` does not leave the actor. In other +words, the arguments are not send across an isolation boundary when calling +`performAsync` by default. This behavior is accomplished by implicitly passing an optional actor parameter to the async function. The function will run on this actor's executor. See the [Executor switching](#executor-switching) section for more details on why the actor parameter is necessary. -The type of an `@execution(caller)` function declaration is an -`@execution(caller)` function type. For example: +The type of an `nonisolated(nonsending)` function declaration is an +`nonisolated(nonsending)` function type. For example: ```swift class NotSendable { ... } @MainActor let global: NotSendable = .init() -@execution(caller) +nonisolated(nonsending) func runOnActor(ns: NotSendable) async {} @MainActor func callSendableClosure() async { - // the type of 'closure' is '@Sendable @execution(caller) (NotSendable) -> Void' + // the type of 'closure' is '@Sendable nonisolated(nonsending) (NotSendable) -> Void' let closure = runOnActor(ns:) let ns = NotSendable() @@ -345,10 +322,10 @@ callSendableClosure() ``` In the above code, the calls to `closure` from `callSendableClosure` run on the -main actor, because `closure` is `@execution(caller)` and `callSendableClosure` +main actor, because `closure` is `nonisolated(nonsending)` and `callSendableClosure` is main actor isolated. -#### `@concurrent` functions +### `@concurrent` functions Async functions can be declared to always switch off of an actor to run using the `@concurrent` attribute: @@ -360,12 +337,40 @@ struct S: Sendable { } ``` +Only (implicitly or explicitly) `nonisolated` functions can be marked with +`@concurrent`; it is an error to use the these attributes with +an isolation other than `nonisolated`, including global actors, isolated +parameters, and `@isolated(any)`: + +```swift +actor MyActor { + var value = 0 + + // error: '@concurrent' can only be used with 'nonisolated' methods + @concurrent + func isolatedToSelf() async { + value += 1 + } + + @concurrent + nonisolated func canRunAnywhere() async { + // cannot access 'value' or other actor-isolated state + } +} +``` + +`@concurrent` can be used together with `@Sendable` or `sending`. + +`@concurrent` cannot be applied to synchronous +functions. This is an artificial limitation that could later be lifted if use +cases arise. + The type of an `@concurrent` function declaration is an `@concurrent` function type. Details on function conversions are covered in a [later section](#function-conversions). When an `@concurrent` function is called from a context that can -run on an actor, including `@execution(caller)` functions or actor-isolated +run on an actor, including `nonisolated(nonsending)` functions or actor-isolated functions, sendable checking is performed on the argument and result values. Either the argument and result values must have a type that conforms to `Sendable`, or the values must be in a disconnected region so they can be sent @@ -393,7 +398,7 @@ actor MyActor { Unstructured tasks created in nonisolated functions never run on an actor unless explicitly specified. This behavior is consistent for all nonisolated -functions, including synchronous functions, `@execution(caller)` async +functions, including synchronous functions, `nonisolated(nonsending)` async functions, and `@concurrent` async functions. For example: @@ -403,7 +408,7 @@ class NotSendable { var value = 0 } -@execution(caller) +nonisolated(nonsending) func createTask(ns: NotSendable) async { Task { // This task does not run on the same actor as `createTask` @@ -444,7 +449,7 @@ struct Program { This behavior allows async function calls that use `#isolation` as a default isolated argument to run on the same actor when called from an -`@execution(caller)` function. For example, the following code is valid because +`nonisolated(nonsending)` function. For example, the following code is valid because the call to `explicitIsolationInheritance` does not cross an isolation boundary: @@ -456,7 +461,7 @@ func explicitIsolationInheritance( isolation: isolated (any Actor)? = #isolation ) async { ... } -@execution(caller) +nonisolated(nonsending) func printIsolation(ns: NotSendable) async { await explicitIsolationInheritance(ns: ns) // okay } @@ -575,29 +580,29 @@ actor MyActor { } func invalidResult(a: MyActor) async -> NotSendable { - let grabActorState: @execution(caller) () async -> NotSendable = a.getState // error + let grabActorState: nonisolated(nonsending) () async -> NotSendable = a.getState // error return await grabActorState() } ``` In the above code, the conversion from the actor-isolated method `getState` -to a `@execution(caller) nonisolated` function is invalid, because the +to a `nonisolated(nonsending)` function is invalid, because the result type does not conform to `Sendable` and the result value could be actor-isolated state. The `nonisolated` function can be called from anywhere, which would allow access to actor state from outside the actor. Not all function conversions cross an isolation boundary, and function conversions that don't can safely pass non-`Sendable` arguments and results. -For example, a `@execution(caller)` function type can always be converted to an -actor-isolated function type, because the `@execution(caller)` function will +For example, a `nonisolated(nonsending)` function type can always be converted to an +actor-isolated function type, because the `nonisolated(nonsending)` function will simply run on the actor: ```swift class NotSendable {} -@execution(caller) -nonisolated func performAsync(_ ns: NotSendable) async { ... } +nonisolated(nonsending) +func performAsync(_ ns: NotSendable) async { ... } @MainActor func convert(ns: NotSendable) async { @@ -613,7 +618,7 @@ which function conversions cross an isolation boundary. Function conversions that cross an isolation boundary require `Sendable` argument and result types, and the destination function type must be `async`. Note that the function conversion rules for synchronous `nonisolated` functions and asynchronous -`@execution(caller) nonisolated` functions are the same; they are both +`nonisolated(nonsending)` functions are the same; they are both represented under the "Nonisolated" category in the table: | Old isolation | New isolation | Crosses Boundary | @@ -646,7 +651,7 @@ class NotSendable { var value = 0 } -@execution(caller) +nonisolated(nonsending) func convert(closure: () -> Void) async { let ns = NotSendable() let disconnectedClosure = { @@ -668,7 +673,7 @@ Converting a non-`@Sendable` function type to an actor-isolated one is invalid if the original function must leave the actor in order to be called: ```swift -@execution(caller) +nonisolated(nonsending) func convert( fn1: @escaping @concurrent () async -> Void, ) async { @@ -710,8 +715,8 @@ func call(_ closure: () -> NotSendable) -> NotSendable { ### Region isolation rules -`@execution(caller)` functions have the same region isolation rules as -synchronous `nonisolated` functions. When calling an `@execution(caller)` +`nonisolated(nonsending)` functions have the same region isolation rules as +synchronous `nonisolated` functions. When calling an `nonisolated(nonsending)` function, all non-`Sendable` parameter and result values are merged into the same region, but they are only merged into the caller's actor region if one of those non-`Sendable` values is already in the actor's region. @@ -721,8 +726,8 @@ For example: ```swift class NotSendable {} -@execution(caller) -nonisolated func identity(_ t: T) async -> T { +nonisolated(nonsending) +func identity(_ t: T) async -> T { return t } @@ -741,7 +746,7 @@ parameter, because the non-`Sendable` parameters and results would always be merged into the actor's region. This proposal allows you to access `#isolation` in the implementation of an -`@execution(caller)` function for the purpose of forwarding it along to a +`nonisolated(nonsending)` function for the purpose of forwarding it along to a method that accepts an `isolated (any Actor)?`. This is still safe, because there's no way to access the actor's isolated state via the `Actor` protocol, and dynamic casting to a concrete actor type will not result in a value that @@ -780,7 +785,7 @@ all other async functions switch to the isolated actor's executor. } ``` -`@execution(caller)` functions will switch to the executor of the implicit +`nonisolated(nonsending)` functions will switch to the executor of the implicit actor parameter passed from the caller instead of switching to the generic executor: @@ -835,10 +840,10 @@ and `preconditionIsolated` on `Actor` and `MainActor`. Nonisolated functions imported from Objective-C that match the import-as-async heuristic from [SE-0297: Concurrency Interoperability with Objective-C][SE-0297] -will implicitly be imported as `@execution(caller)`. Objective-C async +will implicitly be imported as `nonisolated(nonsending)`. Objective-C async functions already have bespoke code generation that continues running on the caller's actor to match the semantics of the original completion handler -function, so `@execution(caller)` already better matches the semantics of these +function, so `nonisolated(nonsending)` already better matches the semantics of these imported `async` functions. This change will eliminate many existing data-race safety issues that happen when calling an async function on an Objective-C class from the main actor. Because the only effect of this change is @@ -851,7 +856,7 @@ This proposal changes the semantics of nonisolated async functions when the upcoming feature flag is enabled. Without the upcoming feature flag, the default for nonisolated async functions is `@concurrent`. When the upcoming feature flag is enabled, the default for nonisolated async functions changes to -`@execution(caller)`. This applies to both function declarations and function +`nonisolated(nonsending)`. This applies to both function declarations and function values that are nonisolated (either implicitly or explicitly). Changing the default execution semantics of nonisolated async functions has @@ -873,7 +878,7 @@ understand the semantics of a nonisolated async function without understanding the build settings of the module you're writing code in. To make it easy to discover what kind of async function you're working with, -SourceKit will surface the implicit `@execution(caller)` or `@concurrent` +SourceKit will surface the implicit `nonisolated(nonsending)` or `@concurrent` attribute for IDE inspection features like Quick Help in Xcode and Hover in VSCode. To ease the transition to the upcoming feature flag, [migration tooling][adoption-tooling] will provide fix-its to preserve behavior by @@ -913,9 +918,9 @@ can be made inlinable. ## Implications on adoption -`@execution(caller)` functions must accept an implicit actor parameter. This -means that adding `@execution(caller)` to a function that is actor-isolated, or -changing a function from `@concurrent` to `@execution(caller)`, is +`nonisolated(nonsending)` functions must accept an implicit actor parameter. This +means that adding `nonisolated(nonsending)` to a function that is actor-isolated, or +changing a function from `@concurrent` to `nonisolated(nonsending)`, is not a resilient change. ## Alternatives considered @@ -945,6 +950,41 @@ reasons: ### Alternative syntax choices +Several different options for the spelling of `nonisolated(nonsending)` +and `@concurrent` were explored. An earlier iteration of this proposal +used the same base attribute for both annotations. However, these two +annotations serve very different purposes. `@concurrent` is the long-term +right way to move functions and closures off of actors. +`nonisolated(nonsending)` is necessary for the transition to the new behavior, +but it's not a syntax that will stick around long term in Swift codebases; the +ideal end state is that this is expressed via the default behavior for +(explicitly or implicitly) `nonisolated` async functions. + +Note that it is well understood that there is no perfect syntax which will +explain the semantics without other context such as educational material or +documentation. This is true for all syntax design decisions. + +#### No explicit spelling for `nonisolated(nonsending)` + +It's reasonable to question whether `nonisolated(nonsending)` is necessary +at all given that its only purpose is transitioning to the new behavior +for async functions. An explicit spelling that has consistent behavior +independent of upcoming features and language modes is valuable when +undertaking a transition that changes the meaning of existing code. + +An explicit, transitory attribute is valuable because there will be a period of +time where it is not immediately clear from source what kind of async function +a programmer is working with. It's necessary to be able to discover that +information from source, such as by showing an inferred attribute explicitly +in SourceKit's cursor info request (surfaced by "Quick Help" in Xcode and +"Hover" in LSP / VSCode). An explicit spelling that has consistent behavior +independent of language mode is also valuable for code generation tools like +macros, so that they do not have to consider build settings to determine the +right code to generate, it's valuable for posting code snippets on the forums +during the transition period, etc. + +#### Justification for `@concurrent` + This proposal was originally pitched using the `@concurrent` syntax, and many reviewers surfaced objects about why `@concurrent` may be misleading, such as: @@ -965,6 +1005,8 @@ happen in the process. So, this proposal uses `@concurrent` because out of the other alternatives we explored, it best reflects the programmer's intent for using the attribute. +#### `@executor` + A previous iteration of this proposal used the syntax `@execution(concurrent)` instead of `@concurrent`. The review thread explored several variations of this syntax, including `@executor(concurrent)` and `@executor(global)`. @@ -979,28 +1021,38 @@ requests and because executors are used for other things than isolation. For example, an `@executor(global)` function could end up running on some executor other than the global executor via task executor preferences. -Another possibility is to use isolation terminology instead of `@execution` -for the syntax. This direction does not accomplish the goal of having a -section to have a consistent meaning for `nonisolated` across synchronous and -async functions. If the attribute were spelled `@isolated(caller)` and -`@isolated(concurrent)`, presumably that attribute would not work together with -`nonisolated`; it would instead be an alternative kind of actor isolation. -`@isolated(concurrent)` also doesn't make much sense because the concurrent -executor does not provide isolation at all - isolation is only provided by -actors and tasks. - -Having `@execution(caller)` as an attribute that is used together with -`nonisolated` leads to a simpler programming model because after the upcoming -feature is enabled, programmers will simply write `nonisolated` on an `async` -function in the same way that `nonisolated` is applied to synchronous -functions. If we choose a different form of isolation like `@isolated(caller)`, -programmers have to learn a separate syntax for `async` functions that -accomplishes the same effect as a `nonisolated` synchronous function. - -### Deprecate `nonisolated` +#### `@isolated` + +An alternative to `nonisolated(nonsending)` is to use the "isolated" +terminology, such as `@isolated(caller)`. However, this approach has very +unsatisfying answers for how it interacts with `nonisolated`. There are +two options: + +1. `@isolated(caller)` must be written together with `nonisolated`, + + This approach leads to the verbose and oxymoronic spelling + `@isolated(caller) nonisolated`. Though there + exists a perfectly reasonable explanation about how `nonisolated` is the + static isolation while `@isolated(caller)` is the dynamic isolation, most + programmers do not have this deep of an understanding of actor isolation, + and they should not have to in order to make basic use of nonisolated async + functions. +2. `@isolated(caller)` implies `nonisolated` and can be written alone as an + alternative. + + This direction means that programmers would sometimes write + `nonisolated` and sometimes write `@isolated(caller)`, which is not a good + end state to be in because programmers have to learn a separate syntax for + `async` functions that accomplishes the same effect as a `nonisolated` + synchronous function. Or, if we view `@isolated(caller)` as only used for + the transition to the new behavior, then the assumption is that some day + people will remove `@isolated(caller)` if it is written in source. If + `@isolated(caller)` implies `nonisolated`, then the code could change + behavior if it's in a context where global or instance actor isolation would + otherwise be inferred. Going in the oppose direction, this proposal could effectively deprecate -`nonisolated` and allow you to use `@execution(caller)` everywhere that +`nonisolated` and allow you to use `@isolated(caller)` everywhere that `nonisolated` is currently supported, including synchronous methods, stored properties, type declarations, and extensions. This direction was not chosen for the following reasons: @@ -1010,26 +1062,60 @@ for the following reasons: necessary to solve the major usability problem with async functions on non-`Sendable` types, because it's painful both to transition code and to re-learn parts of the model that have already been internalized. -2. `nonisolated` is nicer to write than `@execution(caller)`, - `@isolated(caller)`, or any other alternative attribute + argument syntax. - -### Don't introduce a type attribute for `@execution` - -There are a lot of existing type attributes for concurrency and it's -unfortunate to introduce another one. However, without `@execution` as a type -attribute, referencing nonisolated async functions unapplied is very restrictive, -because sendable checking would need to be performed at the point of the -function reference instead of when the function is called. +2. `nonisolated` is nicer to write than `@isolated(caller)` + or any other alternative attribute + argument syntax. + +#### `nonisolated` argument spelling + +An argument to `nonisolated` is more compelling than a separate attribute +to specify that an async function runs on the caller's actor because it +defines away the problem of whether this annotation implies `nonisolated` when +written alone. + +A few different options for the argument to `nonisolated` were explored. + +**`nonisolated(nosend)`**. +`nonisolated(nosend)` effectively the same as `nonisolated(nonsending)` as +proposed, but it states that the call itself does not constitute a "send", +rather than stating that the call is not "sending" its argument and result +values over an isolation boundary. `nonisolated(nosend)` is shorter, but +`nonisolated(nonsending)` is more consistent with existing Swift naming +conventions. + +**`nonisolated(caller)`**. +`nonisolated(caller)` is meant to indicate that the function is statically +`nonisolated` and dynamically isolated to the caller. However, putting those +terms together into one `nonisolated(caller)` attribute is misleading, because +it appears the mean exactly the opposite of what it actually means; +`nonisolated(caller)` reads "not isolated to the caller". + +**`nonisolated(nonconcurrent)`**. +If `@concurrent` is applied to a function, then the function must run +concurrently with the caller's actor (assuming multiple isolated tasks +in the program). `nonconcurrent` conveys the inverse; if `nonconcurrent` is +applied to an async function, then the function must not run concurrently +with the caller's actor. However, this statement isn't quite true, because the +implementation of the function can perform work concurrently, though that work +cannot involve the non-`Sendable` parameter values. + +**`nonisolated(static)`**. +`nonisolated(static)` is meant to convey that a function is only `nonisolated` +statically, but it may be dynamically isolated to a specific actor at runtime. +However, we have not yet introduced "static" into the language surface to mean +"at compile time". `static` also has an existing, different meaning; +`nonisolated static func` would mean something quite different from +`nonisolated(static) func`, despite having extremely similar spelling. ## Revisions The proposal was revised with the following changes after the first review: * Renamed `@execution(concurrent)` back to `@concurrent`. +* Renamed `@execution(caller)` to `nonisolated(nonsending)` * Removed the unconditional warning about nonisolated async functions that - don't explicitly specify `@execution(caller)` or `@concurrent`. + don't explicitly specify `nonisolated(nonsending)` or `@concurrent`. * Removed `noasync` from the `assumeIsolated` API family. -* Specified the region isolation rules for `@execution(caller)` functions [as +* Specified the region isolation rules for `nonisolated(nonsending)` functions [as discussed in the first review][region-isolation]. The proposal was revised with the following changes after the pitch discussion: From e37fb650638a550fc82f7c830c53fb9d77011216 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 27 Mar 2025 15:47:34 -0700 Subject: [PATCH 4184/4563] Update proposals/NNNN-ClockEpochs.md Co-authored-by: Ben Rimmington --- proposals/NNNN-ClockEpochs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-ClockEpochs.md b/proposals/NNNN-ClockEpochs.md index b79cb30084..65ed3f626a 100644 --- a/proposals/NNNN-ClockEpochs.md +++ b/proposals/NNNN-ClockEpochs.md @@ -24,7 +24,7 @@ Two new properties will be added, one to `SuspendingClock` and another to `Conti ## Detailed design ```swift -extension ContinousClock { +extension ContinuousClock { public var systemEpoch: Instant { get } } From 9095dcdced0646591a392e2a2bf80700c858e158 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 27 Mar 2025 15:47:42 -0700 Subject: [PATCH 4185/4563] Update proposals/NNNN-ClockEpochs.md Co-authored-by: Ben Rimmington --- proposals/NNNN-ClockEpochs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-ClockEpochs.md b/proposals/NNNN-ClockEpochs.md index 65ed3f626a..2820db62fc 100644 --- a/proposals/NNNN-ClockEpochs.md +++ b/proposals/NNNN-ClockEpochs.md @@ -36,7 +36,7 @@ extension SuspendingClock { These can be used to gather information like for example the uptime of a system, or the active time of a system; ```swift -let clock = ContinousClock() +let clock = ContinuousClock() let uptime = clock.now - clock.systemEpoch ``` From d65c66a2cc0537ade9ea95e02aadf7bc5225e6af Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 27 Mar 2025 15:47:58 -0700 Subject: [PATCH 4186/4563] Update proposals/NNNN-ClockEpochs.md Co-authored-by: Ben Rimmington --- proposals/NNNN-ClockEpochs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-ClockEpochs.md b/proposals/NNNN-ClockEpochs.md index 2820db62fc..61c6741eed 100644 --- a/proposals/NNNN-ClockEpochs.md +++ b/proposals/NNNN-ClockEpochs.md @@ -1,6 +1,6 @@ # Clock Epochs -* Proposal: [SE-NNNN](NNNN-ClockEpochs.md) +* Proposal: [SE-NNNN](NNNN-clock-epochs.md) * Authors: [Philippe Hausler](https://github.com/phausler) * Review Manager: TBD * Status: **Awaiting implementation** From 897d6d3168a7b03e381edfcb238f31f609f04b08 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 27 Mar 2025 19:56:12 -0400 Subject: [PATCH 4187/4563] Wording updates --- proposals/NNNN-ClockEpochs.md | 58 -------------------------------- proposals/NNNN-clock-epochs.md | 60 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 58 deletions(-) delete mode 100644 proposals/NNNN-ClockEpochs.md create mode 100644 proposals/NNNN-clock-epochs.md diff --git a/proposals/NNNN-ClockEpochs.md b/proposals/NNNN-ClockEpochs.md deleted file mode 100644 index 61c6741eed..0000000000 --- a/proposals/NNNN-ClockEpochs.md +++ /dev/null @@ -1,58 +0,0 @@ -# Clock Epochs - -* Proposal: [SE-NNNN](NNNN-clock-epochs.md) -* Authors: [Philippe Hausler](https://github.com/phausler) -* Review Manager: TBD -* Status: **Awaiting implementation** -* Implementation: - -* Previous Proposal: *if applicable* [SE-0329](0329-clock-instant-duration.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-suspendingclock-and-continuousclock-epochs/78017)) - -## Introduction - -[The proposal for Clock, Instant and Duration](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0329-clock-instant-duration.md) brought in two primary clock types: `SuspendingClock` and `ContinuousClock`. These both have a concept of a reference point for their `Instant` types. - -## Motivation - -Not all clocks have a starting point, however in these cases they do. Generally, it cannot required for a clock's instant definition to have a start or the start may not be a fixed point. However it can be useful that a given instant can be constructed to determine the elapsed duration from the starting point of that clock if it does have it. - -## Proposed solution - -Two new properties will be added, one to `SuspendingClock` and another to `ContinuousClock`. Both of these properties will be the epoch for which all `Instant` types are derived from; practically speaking this is the "zero" point for these clocks. Since the values may be relative to the particular system they are being used on (albeit many may implement them literally as zero) they are named in accordance to reflect that they are designed to be representative of the system's sense of an epoch and should not be expected to be serializable across systems. - -## Detailed design - -```swift -extension ContinuousClock { - public var systemEpoch: Instant { get } -} - -extension SuspendingClock { - public var systemEpoch: Instant { get } -} -``` - -These can be used to gather information like for example the uptime of a system, or the active time of a system; - -```swift -let clock = ContinuousClock() -let uptime = clock.now - clock.systemEpoch -``` - -Or likewise; - -```swift -let clock = SuspendingClock() -let activeTime = clock.now - clock.systemEpoch -``` - -## ABI compatibility - -This is a purely additive change and provides no direct impact to existing ABI. It only carries the ABI impact of new properties being added to an existing type. - -## Alternatives considered - -It was considered to add a constructor or static member to the `SuspendingClock.Instant` and `ContinousClock.Instant` however the home on the clock itself provides a more discoverable and nameable location. - -It is suggested that this be used as an informal protocol for other clocks. It was considered as an additional protocol but that was ultimately rejected because no generic function made much sense that would not be better served with generic specialization or explicit clock parameter types. \ No newline at end of file diff --git a/proposals/NNNN-clock-epochs.md b/proposals/NNNN-clock-epochs.md new file mode 100644 index 0000000000..4ef36495f7 --- /dev/null +++ b/proposals/NNNN-clock-epochs.md @@ -0,0 +1,60 @@ +# Clock Epochs + +* Proposal: [SE-NNNN](NNNN-clock-epochs.md) +* Authors: [Philippe Hausler](https://github.com/phausler) +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Awaiting implementation** +* Implementation: +* Review: ([pitch](https://forums.swift.org/t/pitch-suspendingclock-and-continuousclock-epochs/78017)) + +## Introduction + +[SE-0329: Clock, Instant, and Duration](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0329-clock-instant-duration.md) introduced three concrete clock types: `SuspendingClock`, `ContinuousClock`, and `UTCClock`. While not all clocks have a meaningful concept of a reference or zero instant, `SuspendingClock` and `ContinuousClock` do, and having access to it can be useful. + +## Motivation + +The `Instant` type of a `Clock` represents a moment in time as measured by that clock. `Clock` intentionally imposes very few requirements on `Instant` because different kinds of clocks can have very different characteristics. Just because something does not belong on the generic `Clock` protocol, however, does not mean it shouldn't be exposed in the interface of a concrete clock type. + +Many clocks have a concept of a reference instant, also called an "epoch", that has special meaning for the clock. For example, the Unix `gettimeofday` function measures the nominal elapsed time since 00:00 UTC on January 1st, 1970, an instant often called the "Unix epoch". Swift's `SuspendingClock` and `ContinuousClock` are defined using system facilities that similarly measure time relative to an epoch, and while the exact definition of the epoch is system-specific, it is at least consistent for any given system. This means that durations since the epoch can be meaningfully compared within the system, even with code in other processes or written in other languages (as long as it uses the same system facilities). + +## Proposed solution + +Two new properties will be added, one to `SuspendingClock` and another to `ContinuousClock`. These properties define the system epoch that all `Instant` types for the clock are derived from; practically speaking, this is the "zero" point for these clocks. Since the values may be relative to the particular system they are being used on, their names reflect that they are representative of the system's sense of an epoch and should not be expected to be meaningfully comparable (or serializable) across systems. + +## Detailed design + +```swift +extension ContinuousClock { + public var systemEpoch: Instant { get } +} + +extension SuspendingClock { + public var systemEpoch: Instant { get } +} +``` + +On both Darwin and Linux, the system epochs of these clocks is set at boot time, and so measurements relative to the epoch can used to gather information such as the uptime or active time of a system: + +```swift +let clock = ContinousClock() +let uptime = clock.now - clock.systemEpoch +``` + +Likewise: + +```swift +let clock = SuspendingClock() +let activeTime = clock.now - clock.systemEpoch +``` + +Swift will make an effort to maintain this property on other supported systems when possible. However, it cannot be guaranteed for all systems, such as when the concept of uptime either doesn't apply or is intentionally not exposed to the programming environment for privacy reasons. + +## ABI compatibility + +This is a purely additive change and provides no direct impact to existing ABI. It only carries the ABI impact of new properties being added to an existing type. + +## Alternatives considered + +We considered adding a constructor or static member to `SuspendingClock.Instant` and `ContinousClock.Instant` instead of on the clock. However, placing it on the clock itself provides a more discoverable and nameable location. + +As proposed, `systemEpoch` is an informal protocol that works across multiple clock implementations. We consider formalizing it as a new protocol, but ultimately we decided not to because no generic function made much sense that would not be better served with generic specialization or explicit clock parameter types. From d1fbc32a41cf6669b12af97c19dbe1c4afecc8ad Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 28 Mar 2025 15:20:30 +0000 Subject: [PATCH 4188/4563] [SE-0471] Amend the status field --- proposals/0471-SerialExecutor-isIsolated.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0471-SerialExecutor-isIsolated.md b/proposals/0471-SerialExecutor-isIsolated.md index dcbb66db9f..539f1adc84 100644 --- a/proposals/0471-SerialExecutor-isIsolated.md +++ b/proposals/0471-SerialExecutor-isIsolated.md @@ -3,7 +3,7 @@ * Proposal: [SE-0471](0471-SerialExecutor-isIsolated.md) * Author: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review (March 25...April 8, 2025) +* Status: **Active Review (March 25...April 8, 2025)** * Implementation: https://github.com/swiftlang/swift/pull/79788 & https://github.com/swiftlang/swift/pull/79946 * Review: [Pitch](https://forums.swift.org/t/pitch-serialexecutor-improved-custom-serialexecutor-isolation-checking/78237/), [Review](https://forums.swift.org/t/se-0471-improved-custom-serialexecutor-isolation-checking-for-concurrency-runtime/78834) From 8df1b2b9132a257575f170f4a74b5fb108e43fb0 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 28 Mar 2025 15:26:24 +0000 Subject: [PATCH 4189/4563] [SE-0472] Amend the status field --- proposals/0472-task-start-synchronously-on-caller-context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0472-task-start-synchronously-on-caller-context.md b/proposals/0472-task-start-synchronously-on-caller-context.md index 4e9daacd9f..ae636da68c 100644 --- a/proposals/0472-task-start-synchronously-on-caller-context.md +++ b/proposals/0472-task-start-synchronously-on-caller-context.md @@ -3,7 +3,7 @@ * Proposal: [SE-0472](0472-task-start-synchronously-on-caller-context.md) * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Partially implemented on `main`** +* Status: **Active Review (March 27...April 10, 2025)** * Implementation: https://github.com/swiftlang/swift/pull/79608 * Review: ([pitch](https://forums.swift.org/t/pitch-concurrency-starting-tasks-synchronously-from-caller-context/77960/)) ([review](https://forums.swift.org/t/se-0472-starting-tasks-synchronously-from-caller-context/78883)) From 150133f0c0aecb7344a079a5d88edde6d87325c9 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Fri, 28 Mar 2025 18:21:44 -0400 Subject: [PATCH 4190/4563] Update SE-0461 for focused re-review (#2765) --- proposals/0461-async-function-isolation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md index 8fd24a5b28..755fa646d0 100644 --- a/proposals/0461-async-function-isolation.md +++ b/proposals/0461-async-function-isolation.md @@ -3,12 +3,12 @@ * Proposal: [SE-0461](0461-async-function-isolation.md) * Authors: [Holly Borla](https://github.com/hborla), [John McCall](https://github.com/rjmccall) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (February 20...March 2, 2025)** +* Status: **Active review (March 28...April 4, 2025)** * Vision: [Improving the approachability of data-race safety](/visions/approachable-concurrency.md) * Implementation: On `main` behind `-enable-experimental-feature NonIsolatedAsyncInheritsIsolationFromContext` * Upcoming Feature Flag: `AsyncCallerExecution` * Previous Proposal: [SE-0338](0338-clarify-execution-non-actor-async.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862)) ([review](https://forums.swift.org/t/se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/77987)) +* Review: ([pitch](https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862)) ([first review](https://forums.swift.org/t/se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/77987)) ([acceptance with focused re-review](https://forums.swift.org/t/accepted-with-modifications-and-focused-re-review-se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/78920)) ([second review](https://forums.swift.org/t/focused-re-review-se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/78921)) ## Introduction @@ -1135,4 +1135,4 @@ The proposal was revised with the following changes after the pitch discussion: [SE-0338]: /proposals/0338-clarify-execution-non-actor-async.md [SE-0421]: /proposals/0421-generalize-async-sequence.md [adoption-tooling]: https://forums.swift.org/t/pitch-adoption-tooling-for-upcoming-features/77936 -[region-isolation]: https://forums.swift.org/t/se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/77987/36 \ No newline at end of file +[region-isolation]: https://forums.swift.org/t/se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/77987/36 From a89b64fad71766571d6c2b32858a581720723c60 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 28 Mar 2025 18:47:27 -0400 Subject: [PATCH 4191/4563] Assign SE-0473 to the clocks proposal and put it into review Also some more assorted wording updates --- .../{NNNN-clock-epochs.md => 0473-clock-epochs.md} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename proposals/{NNNN-clock-epochs.md => 0473-clock-epochs.md} (76%) diff --git a/proposals/NNNN-clock-epochs.md b/proposals/0473-clock-epochs.md similarity index 76% rename from proposals/NNNN-clock-epochs.md rename to proposals/0473-clock-epochs.md index 4ef36495f7..bc2981752a 100644 --- a/proposals/NNNN-clock-epochs.md +++ b/proposals/0473-clock-epochs.md @@ -1,9 +1,9 @@ # Clock Epochs -* Proposal: [SE-NNNN](NNNN-clock-epochs.md) +* Proposal: [SE-0473](0473-clock-epochs.md) * Authors: [Philippe Hausler](https://github.com/phausler) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Awaiting implementation** +* Status: **Active Review (March 28th...April 9th, 2025)** * Implementation: * Review: ([pitch](https://forums.swift.org/t/pitch-suspendingclock-and-continuousclock-epochs/78017)) @@ -15,11 +15,11 @@ The `Instant` type of a `Clock` represents a moment in time as measured by that clock. `Clock` intentionally imposes very few requirements on `Instant` because different kinds of clocks can have very different characteristics. Just because something does not belong on the generic `Clock` protocol, however, does not mean it shouldn't be exposed in the interface of a concrete clock type. -Many clocks have a concept of a reference instant, also called an "epoch", that has special meaning for the clock. For example, the Unix `gettimeofday` function measures the nominal elapsed time since 00:00 UTC on January 1st, 1970, an instant often called the "Unix epoch". Swift's `SuspendingClock` and `ContinuousClock` are defined using system facilities that similarly measure time relative to an epoch, and while the exact definition of the epoch is system-specific, it is at least consistent for any given system. This means that durations since the epoch can be meaningfully compared within the system, even with code in other processes or written in other languages (as long as it uses the same system facilities). +Many clocks have a concept of a reference instant, also called an "epoch", that has special meaning for the clock. For example, the Unix `gettimeofday` function measures the nominal elapsed time since 00:00 UTC on January 1st, 1970, an instant often called the "Unix epoch". Swift's `SuspendingClock` and `ContinuousClock` are defined using system facilities that similarly measure time relative to an epoch, and while the exact definition of the epoch is system-specific, it is at least consistent for any given system. This means that durations since the epoch can be meaningfully compared within the system, even across multiple processes or with code written in other languages (as long as they use the same system facilities). ## Proposed solution -Two new properties will be added, one to `SuspendingClock` and another to `ContinuousClock`. These properties define the system epoch that all `Instant` types for the clock are derived from; practically speaking, this is the "zero" point for these clocks. Since the values may be relative to the particular system they are being used on, their names reflect that they are representative of the system's sense of an epoch and should not be expected to be meaningfully comparable (or serializable) across systems. +Two new properties will be added, one to `SuspendingClock` and another to `ContinuousClock`. These properties define the system epoch that all `Instant` types for the clock are derived from; practically speaking, this is the "zero" point for these clocks. Since the values may be relative to the particular system they are being used on, their names reflect that they are a system-specific definition and should not be expected to be consistent (or meaningfully serializable) across systems. ## Detailed design @@ -33,7 +33,7 @@ extension SuspendingClock { } ``` -On both Darwin and Linux, the system epochs of these clocks is set at boot time, and so measurements relative to the epoch can used to gather information such as the uptime or active time of a system: +On most platforms, including Apple platforms, Linux, and Windows, the system epoch of these clocks is set at boot time, and so measurements relative to the epoch can used to gather information such as the uptime or active time of a system: ```swift let clock = ContinousClock() @@ -47,7 +47,7 @@ let clock = SuspendingClock() let activeTime = clock.now - clock.systemEpoch ``` -Swift will make an effort to maintain this property on other supported systems when possible. However, it cannot be guaranteed for all systems, such as when the concept of uptime either doesn't apply or is intentionally not exposed to the programming environment for privacy reasons. +However, this cannot be guaranteed for all possible platforms. A platform may choose to use a different instant for its system epoch, perhaps because the concept of uptime doesn't apply cleanly on the platform or because it is intentionally not exposed to the programming environment for privacy reasons. ## ABI compatibility From 3ba7dede6c42589206ea1d3e4ad653a03dade17d Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 28 Mar 2025 18:52:33 -0400 Subject: [PATCH 4192/4563] Link SE-0473 to its review thread --- proposals/0473-clock-epochs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0473-clock-epochs.md b/proposals/0473-clock-epochs.md index bc2981752a..4565338dcc 100644 --- a/proposals/0473-clock-epochs.md +++ b/proposals/0473-clock-epochs.md @@ -5,7 +5,7 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active Review (March 28th...April 9th, 2025)** * Implementation: -* Review: ([pitch](https://forums.swift.org/t/pitch-suspendingclock-and-continuousclock-epochs/78017)) +* Review: ([pitch](https://forums.swift.org/t/pitch-suspendingclock-and-continuousclock-epochs/78017)) ([review](https://forums.swift.org/t/se-0473-clock-epochs/78923)) ## Introduction From b5bf19dd29385f7d647d4e7a41290323d1765940 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Fri, 28 Mar 2025 22:08:21 -0400 Subject: [PATCH 4193/4563] Update 0466-control-default-actor-isolation.md (#2767) --- proposals/0466-control-default-actor-isolation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0466-control-default-actor-isolation.md b/proposals/0466-control-default-actor-isolation.md index 81f289408c..51ffc99c7c 100644 --- a/proposals/0466-control-default-actor-isolation.md +++ b/proposals/0466-control-default-actor-isolation.md @@ -3,10 +3,10 @@ * Proposal: [SE-0466](0466-control-default-actor-isolation.md) * Authors: [Holly Borla](https://github.com/hborla) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active Review (March 5...19, 2025)** +* Status: **Accepted** * Vision: [Improving the approachability of data-race safety](/visions/approachable-concurrency.md) * Implementation: On `main` behind the `UnspecifiedMeansMainActorIsolated` experimental feature. -* Review: ([pitch](https://forums.swift.org/t/pitch-control-default-actor-isolation-inference/77482))([review](https://forums.swift.org/t/se-0466-control-default-actor-isolation-inference/78321)) +* Review: ([pitch](https://forums.swift.org/t/pitch-control-default-actor-isolation-inference/77482))([review](https://forums.swift.org/t/se-0466-control-default-actor-isolation-inference/78321))([acceptance](https://forums.swift.org/t/accepted-se-0466-control-default-actor-isolation-inference/78926)) ## Introduction From a00d4eee3d5b848dffd8e7388aecd0948cc8de98 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 31 Mar 2025 11:03:24 -0700 Subject: [PATCH 4194/4563] [SE-0467] incorporate review feedback (#2737) * [se0467] extract slicing from future directions * [se0467] clean up element constraints * [se0467] fix another typo * [se0467] fix a typo and parameter name --- proposals/0467-MutableSpan.md | 36 +++++++---------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/proposals/0467-MutableSpan.md b/proposals/0467-MutableSpan.md index 0ed4faecc7..4ee89aeb13 100644 --- a/proposals/0467-MutableSpan.md +++ b/proposals/0467-MutableSpan.md @@ -84,7 +84,7 @@ This lifetime relationship will apply to all the safe `var mutableSpan: MutableS An important category of use cases for `MutableSpan` and `MutableRawSpan` consists of bulk copying operations. Often times, such bulk operations do not necessarily start at the beginning of the span, thus having a method to select a sub-span is necessary. This means producing an instance derived from the callee instance. We adopt the nomenclature already introduced in [SE-0437][SE-0437], with a family of `extracting()` methods. ```swift -extension MutableSpan where Element: ~Copyable & ~Escapable { +extension MutableSpan where Element: ~Copyable { @_lifetime(inout self) public mutating func extracting(_ range: Range) -> Self } @@ -114,12 +114,12 @@ As established in [SE-0437][SE-0437], the instance returned by the `extracting() ````swift @frozen -public struct MutableSpan: ~Copyable, ~Escapable { +public struct MutableSpan: ~Copyable, ~Escapable { internal var _start: UnsafeMutableRawPointer? internal var _count: Int } -extension MutableSpan: @unchecked Sendable where Element: Sendable {} +extension MutableSpan: @unchecked Sendable where Element: Sendable & ~Copyable {} ```` We store a `UnsafeMutableRawPointer` value internally in order to explicitly support reinterpreted views of memory as containing different types of `BitwiseCopyable` elements. Note that the the optionality of the pointer does not affect usage of `MutableSpan`, since accesses are bounds-checked and the pointer is only dereferenced when the `MutableSpan` isn't empty, when the pointer cannot be `nil`. @@ -127,7 +127,7 @@ We store a `UnsafeMutableRawPointer` value internally in order to explicitly sup Initializers, required for library adoption, will be proposed alongside [lifetime annotations][PR-2305]; for details, see "[Initializers](#initializers)" in the [future directions](#Directions) section. ```swift -extension MutableSpan where Element: ~Copyable & ~Escapable { +extension MutableSpan where Element: ~Copyable { /// The number of initialized elements in this `MutableSpan`. var count: Int { get } @@ -172,7 +172,7 @@ We include functions to perform bulk copies of elements into the memory represen ```swift extension MutableSpan where Element: Copyable { - /// Updates every element of this span's to the given value. + /// Updates every element of this span to the given value. mutating func update( repeating repeatedValue: Element ) @@ -224,7 +224,7 @@ extension MutableSpan where Element: Copyable { These functions extract sub-spans of the callee. The first two perform strict bounds-checking. The last four return prefixes or suffixes, where the number of elements in the returned sub-span is bounded by the number of elements in the parent `MutableSpan`. ```swift -extension MutableSpan where Element: ~Copable & ~Escapable { +extension MutableSpan where Element: ~Copyable { /// Returns a span over the items within the supplied range of /// positions within this span. @_lifetime(inout self) @@ -427,7 +427,7 @@ extension MutableRawSpan { /// Updates the span's bytes with the bytes of the elements from the source mutating func update( - from elements: inout some IteratorProtocol + from source: inout some IteratorProtocol ) -> Int /// Updates the span's bytes with every byte of the source. @@ -669,28 +669,6 @@ Note: The future directions stated in [SE-0447](https://github.com/swiftlang/swi These annotations have been [pitched][PR-2305-pitch] and, after revision, are expected to be pitched again soon. `MutableSpan` initializers using lifetime annotations will be proposed alongside the annotations themselves. -#### Functions providing variants of `MutableRawSpan` to `MutableSpan` - -`MutableSpan`s representing subsets of consecutive elements could be extracted out of a larger `MutableSpan` with an API similar to the `extracting()` functions recently added to `UnsafeBufferPointer` in support of non-copyable elements: - -```swift -extension MutableSpan where Element: ~Copyable { - public mutating func extracting(_ bounds: Range) -> Self -} -``` - -These functions would require a lifetime dependency annotation. - -Similarly, a `MutableRawSpan` could provide a function to mutate a range of its bytes as a typed `MutableSpan`: - -```swift -extension MutableRawSpan { - @unsafe - public mutating func unsafeMutableView(as type: T.Type) -> MutableSpan -} -``` -We are subsetting functions that require lifetime annotations until such annotations are [proposed][PR-2305]. - #### Splitting `MutableSpan` instances – `MutableSpan` in divide-and-conquer algorithms It is desirable to have a way to split a `MutableSpan` in multiple parts, for divide-and-conquer algorithms or other reasons: From e82773fbe6f5158d9b6114c6e4d33082eac8f133 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Mon, 31 Mar 2025 14:05:12 -0400 Subject: [PATCH 4195/4563] Update 0467-MutableSpan.md (#2769) --- proposals/0467-MutableSpan.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0467-MutableSpan.md b/proposals/0467-MutableSpan.md index 4ee89aeb13..75bb7f7b1e 100644 --- a/proposals/0467-MutableSpan.md +++ b/proposals/0467-MutableSpan.md @@ -3,10 +3,10 @@ * Proposal: [SE-0467](0467-MutableSpan.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Active review (March 11...25, 2025)** +* Status: **Accepted** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: "Future" target of [swift-collections](https://github.com/apple/swift-collections/tree/future) -* Review: [Pitch](https://forums.swift.org/t/pitch-mutablespan/77790) +* Review: ([Pitch](https://forums.swift.org/t/pitch-mutablespan/77790)) ([Review](https://forums.swift.org/t/se-0467-mutablespan/78454)) ([Acceptance](https://forums.swift.org/t/accepted-se-0467-mutablespan/78875)) [SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md [SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md From 06d3b8f3737b83d234ad53c03e8db38530b1d2fa Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 31 Mar 2025 11:07:37 -0700 Subject: [PATCH 4196/4563] [SE-0447] Span proposal edits (#2738) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [se0447] reorder future-direction sections - “Index Validation Utilites” was mistakenly inserted between two other sections that should remain contiguous. * [se0447] use the indices property in an example * [se0447] add omitted `Sendable` conformances - while these were discussed in the form threads and were in the prototype implementations, they weren’t in this document. * [se0447] fix typos * [se0447] address telemetry domain’s Span naming * [se0447] discuss sendability of RawSpan --- ...7-span-access-shared-contiguous-storage.md | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/proposals/0447-span-access-shared-contiguous-storage.md b/proposals/0447-span-access-shared-contiguous-storage.md index 95d5c8346b..71b3ff8aa4 100644 --- a/proposals/0447-span-access-shared-contiguous-storage.md +++ b/proposals/0447-span-access-shared-contiguous-storage.md @@ -66,10 +66,12 @@ A `RawSpan` can be obtained from containers of `BitwiseCopyable` elements, as we ```swift @frozen -public struct Span: Copyable, ~Escapable { +public struct Span: Copyable, ~Escapable { internal var _start: UnsafeRawPointer? internal var _count: Int } + +extension Span: Sendable where Element: Sendable & ~Copyable {} ``` We store a `UnsafeRawPointer` value internally in order to explicitly support reinterpreted views of memory as containing different types of `BitwiseCopyable` elements. Note that the the optionality of the pointer does not affect usage of `Span`, since accesses are bounds-checked and the pointer is only dereferenced when the `Span` isn't empty, and the pointer cannot be `nil`. @@ -95,7 +97,7 @@ Like `UnsafeBufferPointer`, `Span` uses a simple offset-based indexing. The firs As a side-effect of not conforming to `Collection` or `Sequence`, `Span` is not directly supported by `for` loops at this time. It is, however, easy to use in a `for` loop via indexing: ```swift -for i in 0..Sendability of `RawSpan` + +This proposal makes `RawSpan` a `Sendable` type. We believe this is the right decision. The sendability of `RawSpan` could be used to unsafely transfer a pointer value across an isolation boundary, despite the non-sendability of pointers. For example, suppose a `RawSpan` were obtained from an existing `Array` variable. We could send the `RawSpan` across the isolation boundary, and there extract the pointer using `rawSpan.unsafeLoad(as: UnsafeRawPointer.self)`. While this is an unsafe outcome, a similar operation can be done encoding a pointer as an `Int`, and then using `UnsafeRawPointer(bitPattern: mySentInt)` on the other side of the isolation boundary. + ##### A more sophisticated approach to indexing This is discussed more fully in the [indexing appendix](#Indexing) below. @@ -444,7 +454,7 @@ This proposal includes some `_read` accessors, the coroutine version of the `get #### Extensions to Standard Library and Foundation types -The standard library and Foundation has a number of types that can in principle provide access to their internal storage as a `Span`. We could provide `withSpan()` and `withBytes()` closure-taking functions as safe replacements for the existing `withUnsafeBufferPointer()` and `withUnsafeBytes()` functions. We could also also provide lifetime-dependent `span` or `bytes` properties. For example, `Array` could be extended as follows: +The standard library and Foundation has a number of types that can in principle provide access to their internal storage as a `Span`. We could provide `withSpan()` and `withBytes()` closure-taking functions as safe replacements for the existing `withUnsafeBufferPointer()` and `withUnsafeBytes()` functions. We could also provide lifetime-dependent `span` or `bytes` properties. For example, `Array` could be extended as follows: ```swift extension Array { @@ -466,10 +476,6 @@ extension Array where Element: BitwiseCopyable { Of these, the closure-taking functions can be implemented now, but it is unclear whether they are desirable. The lifetime-dependent computed properties require lifetime annotations, as initializers do. We are deferring proposing these extensions until the lifetime annotations are proposed. -#### Index Validation Utilities - -This proposal originally included index validation utilities for `Span`. such as `boundsContain(_: Index) -> Bool` and `boundsContain(_: Range) -> Bool`. After review feedback, we believe that the utilities proposed would also be useful for index validation on `UnsafeBufferPointer`, `Array`, and other similar `RandomAccessCollection` types. `Range` already a single-element `contains(_: Bound) -> Bool` function which can be made even more efficient. We should add an additional function that identifies whether a `Range` contains the _endpoints_ of another `Range`. Note that this is not the same as the existing `contains(_: some Collection) -> Bool`, which is about the _elements_ of the collection. This semantic difference can lead to different results when examing empty `Range` instances. - #### A `ContiguousStorage` protocol An earlier version of this proposal proposed a `ContiguousStorage` protocol by which a type could indicate that it can provide a `Span`. `ContiguousStorage` would form a bridge between generically-typed interfaces and a performant concrete implementation. It would supersede the rejected [SE-0256](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0256-contiguous-collection.md). @@ -495,6 +501,10 @@ Two issues prevent us from proposing it at this time: (a) the ability to suppres Many of the standard library collections could conform to `ContiguousStorage`. +#### Index Validation Utilities + +This proposal originally included index validation utilities for `Span`. such as `boundsContain(_: Index) -> Bool` and `boundsContain(_: Range) -> Bool`. After review feedback, we believe that the utilities proposed would also be useful for index validation on `UnsafeBufferPointer`, `Array`, and other similar `RandomAccessCollection` types. `Range` already a single-element `contains(_: Bound) -> Bool` function which can be made even more efficient. We should add an additional function that identifies whether a `Range` contains the _endpoints_ of another `Range`. Note that this is not the same as the existing `contains(_: some Collection) -> Bool`, which is about the _elements_ of the collection. This semantic difference can lead to different results when examining empty `Range` instances. + #### Support for `Span` in `for` loops This proposal does not define an `IteratorProtocol` conformance, since an iterator for `Span` would need to be non-escapable. This is not compatible with `IteratorProtocol`. As such, `Span` is not directly usable in `for` loops as currently defined. A `BorrowingIterator` protocol for non-escapable and non-copyable containers must be defined, providing a `for` loop syntax where the element is borrowed through each iteration. Ultimately we should arrive at a way to iterate through borrowed elements from a borrowed view: From 2ea525f6fa27f96f712701b7dd2020b8d864be98 Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 1 Apr 2025 14:14:37 -0700 Subject: [PATCH 4197/4563] SE-0446 is implemented. --- proposals/0446-non-escapable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0446-non-escapable.md b/proposals/0446-non-escapable.md index 854394b9cb..ab78724fd4 100644 --- a/proposals/0446-non-escapable.md +++ b/proposals/0446-non-escapable.md @@ -3,7 +3,7 @@ * Proposal: [SE-0446](0446-non-escapable.md) * Authors: [Andrew Trick](https://github.com/atrick), [Tim Kientzle](https://github.com/tbkka) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Roadmap: [BufferView Language Requirements](https://forums.swift.org/t/roadmap-language-support-for-bufferview) * Implementation: **Implemented** in `main` branch * Upcoming Feature Flag: `NonescapableTypes` From 5055d9a00facd435682db199a1bef3d5c8e72e1c Mon Sep 17 00:00:00 2001 From: Mykola Pokhylets Date: Wed, 2 Apr 2025 19:16:07 +0200 Subject: [PATCH 4198/4563] Proposal to unban weak let bindings --- proposals/NNNN-weak-let.md | 106 +++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 proposals/NNNN-weak-let.md diff --git a/proposals/NNNN-weak-let.md b/proposals/NNNN-weak-let.md new file mode 100644 index 0000000000..e91230bd47 --- /dev/null +++ b/proposals/NNNN-weak-let.md @@ -0,0 +1,106 @@ +# Feature name + +* Proposal: [SE-NNNN](NNNN-weak-let.md) +* Authors: [Mykola Pokhylets](https://github.com/nickolas-pohilets) +* Review Manager: TBD +* Status: **Awaiting implementation** +* Implementation: [swiftlang/swift#80440](https://github.com/swiftlang/swift/pull/80440) +* Upcoming Feature Flag: `WeakLet` +* Review: ([discussion](https://forums.swift.org/t/weak-captures-in-sendable-sending-closures/78498)) + +## Introduction + +Currently Swift requires weak stored variables to be mutable. +This restriction is rather artificial, and causes friction with sendability checking. + +## Motivation + +Currently swift classes with weak stored properties cannot be `Sendable`, +because weak properties have to be mutable, and mutable properties are +not allowed in `Sendable` classes. + +Similarly, closures with `weak` captures cannot be `@Sendable`, +because such captures are implicitly made mutable. + +Usually developers are not aware of this implicit mutability and have no intention to modify the captured variable. +Implicit mutability of weak captures is inconsistent with `unowned` or default captures. + +Wrapping weak reference into a single-field struct, allows stored properties and captures to be immutable. + +```swift +final class C: Sendable {} + +struct WeakRef { + weak var ref: C? +} + +final class User: Sendable { + weak let ref1: C? // error: 'weak' must be a mutable variable, because it may change at runtime + let ref2: WeakRef // ok +} + +func makeClosure() -> @Sendable () -> Void { + let c = C() + return { [weak c] in + c?.foo() // error: reference to captured var 'c' in concurrently-executing code + c = nil // nobody does this + } + return { [c = WeakRef(ref: c)] in + c.ref?.foo() // ok + } +} +``` + +Existence of this workaround shows that ban on `weak let` variables is artificial, and can be lifted. + +Note that resetting weak references on object destruction is different from regular variable modification. +Resetting on destruction is implemented in a thread-safe manner, and can safely coexist with concurrent reads or writes. +But regular writing to a variable requires exclusive access to that memory location. + +## Proposed solution + +Allow `weak let` declarations for local variables and stored properties. + +Proposal maintains status quo regarding use of `weak` on function arguments and computed properties: +* there is no valid syntax to indicate that function argument is a weak reference; +* `weak` on computed properties is allowed, but has not effect. + +Weak captures are immutable under this proposal. If mutable capture is desired, +mutable variable need to be explicit declared and captured. + +```swift +func makeClosure() -> @Sendable () -> Void { + let c = C() + // Closure is @Sendable + return { [weak c] in + c?.foo() + c = nil // error: cannot assign to value: 'c' is an immutable capture + } + + weak var explicitlyMutable: C? = c + // Closure cannot be @Sendable anymore + return { + explicitlyMutable?.foo() + explicitlyMutable = nil // but assigned is ok + } +} +``` + +## Source compatibility + +Allowing `weak let` bindings is a source-compatible change +that makes previously invalid code valid. + +Treating weak captures as immutable is a source-breaking change. +Any code that attempts to write to the capture will stop compiling. +The overall amount of such code is expected to be small. + +## ABI compatibility + +This is an ABI-compatible change. + +## Implications on adoption + +This feature can be freely adopted and un-adopted in source +code with no deployment constraints and without affecting source or ABI +compatibility. From fda62f8240e22a276d161a4d14c1480c3931c1ea Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 3 Apr 2025 12:41:10 +0100 Subject: [PATCH 4199/4563] Clean up wording and links in `visions/webassembly.md` --- visions/webassembly.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/visions/webassembly.md b/visions/webassembly.md index 3ae48a47af..6f5cac3344 100644 --- a/visions/webassembly.md +++ b/visions/webassembly.md @@ -14,7 +14,7 @@ Wasm modules. An application compiled to a Wasm module can run on any platform that has a Wasm runtime available. Despite its origins in the browser, it is a general-purpose technology that has use cases in client-side and server-side applications and services. WebAssembly support in Swift makes the language more appealing in those settings, and also brings it to the -browser where it previously wasn't available at all. It facilitates a broader adoption of Swift in more environments +browser where it previously wasn't available at all[^1]. It facilitates a broader adoption of Swift in more environments and contexts. The WebAssembly instruction set has useful properties from a security perspective, as it has no interrupts or @@ -42,7 +42,7 @@ In the last few years, the W3C WebAssembly Working Group considered multiple pro [type system](https://github.com/webassembly/interface-types) and [module linking](https://github.com/webassembly/module-linking). These were later subsumed into a combined [Component Model](https://component-model.bytecodealliance.org) proposal thanks to the ongoing work on -[WASI Preview 2](https://github.com/WebAssembly/WASI/blob/main/preview2/README.md), which served as playground for +[WASI Preview 2](https://github.com/WebAssembly/WASI/blob/main/preview2/README.md), which served as playground for the new design. The Component Model defines these core concepts: @@ -83,7 +83,7 @@ overhead of new process setup and IPC infrastructure. ## Goals -As of March 2024 all patches necessary for basic Wasm and WASI Preview 1 support have been merged to the Swift +As of March 2024 all patches necessary for basic Wasm and WASI Preview 1 support have been merged to the Swift toolchain and core libraries. Based on this, we propose a high-level roadmap for WebAssembly support and adoption in the Swift ecosystem: @@ -126,13 +126,13 @@ engine is necessary for debugging. The current state of debugging tools in the Wasm ecosystem is not as mature as other platforms, but there are two main directions: -1. LLDB debugger with Wasm runtime supporting GDB Remote Serial Protocol [1] -2. Wasm runtime with a built-in debugger [2] +1. [LLDB debugger with Wasm runtime](https://github.com/llvm/llvm-project/pull/77949) supporting GDB Remote Serial Protocol; +2. [Wasm runtime with a built-in debugger](https://book.swiftwasm.org/getting-started/debugging.html#enhanced-dwarf-extension-for-swift). The first approach provides an almost equivalent experience to existing debugging workflows on other platforms. It can utilize LLDB's Swift support, remote metadata inspection, and serialized Swift module information. However, since Wasm is a Harvard architecture and has no way to allocate executable memory space at runtime, implementing expression -evaluation with JIT in user space is challenging. In other words, gdb stub in Wasm engines need tricky implementations +evaluation with JIT in user space is challenging. In other words, GDB stub in Wasm engines need tricky implementations or need to extend the GDB Remote Serial Protocol. The second approach embeds the debugger within the Wasm engine. In scenarios where the Wasm engine is embedded as a @@ -146,9 +146,6 @@ debuggers in some way. In summary, debugging in the browser and outside of the browser context are sufficiently different activities to require separate implementation approaches. -[1]: https://github.com/llvm/llvm-project/pull/77949 -[2]: https://github.com/ChromeDevTools/devtools-frontend/blob/main/extensions/cxx_debugging - ### Multi-threading and Concurrency WebAssembly has [atomic operations in the instruction set](https://github.com/WebAssembly/threads) (only sequential @@ -193,6 +190,7 @@ The latter approach cannot fully replace the former, as it is unable to handle d runtime, but it is more portable way to distribute programs linked with shared libraries, as it does not require the host environment to provide any special capabilities except for Component Model support. -Support for shared libraries in Swift for Wasm is mostly about making sure that Swift programs can be compiled in +Support for shared libraries in Swift means ensuring that Swift programs can be compiled in position-independent code mode and linked with shared libraries by following the corresponding dynamic linking ABI. +[^1]: We aim to address browser-specific use cases in a separate future document. From 726445345def1372b0dca0bc65cff7639a7a02b2 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 3 Apr 2025 15:42:36 +0100 Subject: [PATCH 4200/4563] Fix broken `wasip2` link in `webassembly.md` --- visions/webassembly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visions/webassembly.md b/visions/webassembly.md index 6f5cac3344..95aa29f884 100644 --- a/visions/webassembly.md +++ b/visions/webassembly.md @@ -42,7 +42,7 @@ In the last few years, the W3C WebAssembly Working Group considered multiple pro [type system](https://github.com/webassembly/interface-types) and [module linking](https://github.com/webassembly/module-linking). These were later subsumed into a combined [Component Model](https://component-model.bytecodealliance.org) proposal thanks to the ongoing work on -[WASI Preview 2](https://github.com/WebAssembly/WASI/blob/main/preview2/README.md), which served as playground for +[WASI Preview 2](https://github.com/WebAssembly/WASI/blob/main/wasip2/README.md), which served as playground for the new design. The Component Model defines these core concepts: From 24d39d1c13908f98ac8054ece1e182bd965ddc32 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Sat, 5 Apr 2025 13:12:42 -0400 Subject: [PATCH 4201/4563] Accept SE-0468 (#2773) --- .../0468-async-stream-continuation-hashable-conformance.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0468-async-stream-continuation-hashable-conformance.md b/proposals/0468-async-stream-continuation-hashable-conformance.md index 00791b140f..e276c80235 100644 --- a/proposals/0468-async-stream-continuation-hashable-conformance.md +++ b/proposals/0468-async-stream-continuation-hashable-conformance.md @@ -3,9 +3,9 @@ * Proposal: [SE-0468](0468-async-stream-continuation-hashable-conformance.md) * Authors: [Mykola Pokhylets](https://github.com/nickolas-pohilets) * Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) -* Status: **Active review (March 12...25, 2025)** +* Status: **Accepted** * Implementation: [swiftlang/swift#79457](https://github.com/swiftlang/swift/pull/79457) -* Review: ([pitch](https://forums.swift.org/t/pitch-add-hashable-conformance-to-asyncstream-continuation/77897)) +* Review: ([pitch](https://forums.swift.org/t/pitch-add-hashable-conformance-to-asyncstream-continuation/77897)) ([review](https://forums.swift.org/t/se-0468-hashable-conformance-for-async-throwing-stream-continuation/78487)) ([acceptance](https://forums.swift.org/t/accepted-se-0468-hashable-conformance-for-async-throwing-stream-continuation/79116)) ## Introduction From 8991630f5a7d0a896bdadddb2b5b2f0db943e525 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Sat, 5 Apr 2025 13:22:14 -0400 Subject: [PATCH 4202/4563] Accept SE-0461 (#2774) * Accept SE-0461 * Fixup decision link --- proposals/0461-async-function-isolation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md index 755fa646d0..f8712cde7d 100644 --- a/proposals/0461-async-function-isolation.md +++ b/proposals/0461-async-function-isolation.md @@ -3,12 +3,12 @@ * Proposal: [SE-0461](0461-async-function-isolation.md) * Authors: [Holly Borla](https://github.com/hborla), [John McCall](https://github.com/rjmccall) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (March 28...April 4, 2025)** +* Status: **Accepted with modifications** * Vision: [Improving the approachability of data-race safety](/visions/approachable-concurrency.md) * Implementation: On `main` behind `-enable-experimental-feature NonIsolatedAsyncInheritsIsolationFromContext` * Upcoming Feature Flag: `AsyncCallerExecution` * Previous Proposal: [SE-0338](0338-clarify-execution-non-actor-async.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862)) ([first review](https://forums.swift.org/t/se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/77987)) ([acceptance with focused re-review](https://forums.swift.org/t/accepted-with-modifications-and-focused-re-review-se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/78920)) ([second review](https://forums.swift.org/t/focused-re-review-se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/78921)) +* Review: ([pitch](https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862)) ([first review](https://forums.swift.org/t/se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/77987)) ([acceptance with focused re-review](https://forums.swift.org/t/accepted-with-modifications-and-focused-re-review-se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/78920)) ([second review](https://forums.swift.org/t/focused-re-review-se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/78921)) ([second acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0461-run-nonisolated-async-functions-on-the-caller-s-actor-by-default/79117)) ## Introduction From 6c9a5a0eba8f1bf34bd22cf18290a3603e3facfd Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 8 Apr 2025 12:59:01 -0700 Subject: [PATCH 4203/4563] [Pitch] `yielding` coroutine accessors (#2697) * [CoroutineAccessors] Pitch. * Revisions based on feedback from second pitch thread - Provide links to the accessors vision - Use the names `yielding borrow` and `yielding mutate` for the coroutine based accessors - Add discussion of when to favor these accessors over `get` and `set` under implications for adoption * update credits * Update and rename NNNN-yielding-accessors.md to 0474-yielding-accessors.md --------- Co-authored-by: Nate Chandler Co-authored-by: Stephen Canon --- proposals/0474-yielding-accessors.md | 668 +++++++++++++++++++++++++++ 1 file changed, 668 insertions(+) create mode 100644 proposals/0474-yielding-accessors.md diff --git a/proposals/0474-yielding-accessors.md b/proposals/0474-yielding-accessors.md new file mode 100644 index 0000000000..d9fb323da2 --- /dev/null +++ b/proposals/0474-yielding-accessors.md @@ -0,0 +1,668 @@ +# Yielding accessors + +* Proposal: [SE-0474](0474-yielding-accessors.md) +* Authors: [Ben Cohen](https://github.com/airspeedswift), [Nate Chandler](https://github.com/nate-chandler), [Joe Groff](https://github.com/jckarter/) +* Review Manager: [Steve Canon](https://github.com/stephentyrone) +* Status: **Active Review (Apr 8...22)** +* Vision: [A Prospective Vision for Accessors in Swift](https://github.com/rjmccall/swift-evolution/blob/accessors-vision/visions/accessors.md) +* Implementation: Partially available on main behind the frontend flag `-enable-experimental-feature CoroutineAccessors` +* Review: ([pitch 1](https://forums.swift.org/t/modify-accessors/31872)), ([pitch 2](https://forums.swift.org/t/pitch-modify-and-read-accessors/75627)), ([pitch 3](https://forums.swift.org/t/pitch-3-yielding-coroutine-accessors/77956)), (review) + +## Introduction + +We propose the introduction of two new accessors, `yielding mutate` and `yielding borrow`, for implementing computed properties and subscripts alongside the current `get` and `set`. + +By contrast with `get` and `set`, whose bodies behave like traditional methods, the body of a `yielding` accessor will be a coroutine, using a new contextual keyword `yield` to pause the coroutine and lend access of a value to the caller. +When the caller ends its access to the lent value, the coroutine's execution will continue after `yield`. + +These `yielding` accessors enable values to be accessed and modified without requiring a copy. +This is essential for noncopyable types and often desirable for performance even with copyable types. + +This feature has been available (but not supported) since Swift 5.0 via the `_modify` and `_read` keywords. +Additionally, the feature is available via `read` and `modify` on recent builds from the compiler's `main` branch using the flag `-enable-experimental-feature CoroutineAccessors`. + +## Motivation + +### `yielding mutate` + +Swift's `get`/`set` syntax allows users to expose computed properties and subscripts that behave as l-values. +This powerful feature allows for the creation of succinct idiomatic APIs, such as this use of `Dictionary`'s defaulting subscript: + +```swift +var wordFrequencies: [String:Int] = [:] +wordFrequencies["swift", default: 0] += 1 +// wordFrequencies == ["swift":1] +``` + +While this provides the illusion of "in-place" mutation, it actually involves three separate operations: +1. a `get` of a copy of the value +2. the mutation, performed on that returned value +3. finally, a `set` replacing the original value with the mutated temporary value. + +This can be seen by performing side effects inside of the getter and setter; for example: + +```swift +struct GetSet { + var x: String = "👋🏽 Hello" + + var property: String { + get { print("Getting",x); return x } + set { print("Setting",newValue); x = newValue } + } +} + +var getSet = GetSet() +getSet.property.append(", 🌍!") +// prints: +// Getting 👋🏽 Hello +// Setting 👋🏽 Hello, 🌍! +``` + +#### Performance + +When the property or subscript is of copyable type, this simulation of in-place mutation works well for user ergonomics but has a major performance shortcoming, which can be seen even in our simple `GetSet` type above. +Strings in Swift aren't bitwise-copyable types; once they grow beyond a small fixed size, they allocate a reference-counted buffer to hold their contents. +Mutation is handled via the copy-on-write technique: +When you make a copy of a string, only the reference to the buffer is copied, not the buffer itself. +Then, when either copy of the string is mutated, the mutation operation checks if the buffer is uniquely referenced. +If it isn't (because the string has been copied), it first duplicates the buffer before mutating it, preserving the value semantics of `String` while avoiding unnecessary eager copies. + +Given this, we can see a performance problem when appending to `GetSet.property` in our example above: + +- `GetSet.property { get }` is called and returns a copy of `x`. +- Because a copy is returned, the buffer backing the string is no longer uniquely referenced. +- The append operation must therefore duplicate the buffer before mutating it. +- `GetSet.property { set }` writes this copy back over the top of `x`, destroying the original string. +- The original buffer's reference count drops to zero, and it's destroyed too. + +So, despite looking like in-place mutation, every mutating operation on `x` made through `property` is actually causing a full copy of `x`'s backing buffer. +This is a linear-time operation. +If we appended to this property in a loop, that loop would end up being quadratic in complexity. +This is likely very surprising to the developer and is a frequent performance pitfall. + +[An accessor](#design-modify) which _lends_ a value to the caller in place, without producing a temporary copy, is desirable to avoid this quadratic time complexity while preserving Swift's ability for computed properties to provide expressive and ergonomic APIs. + +#### Non-`Copyable` types + +When the value being mutated is noncopyable, the common `get`/`set` pattern often becomes impossible to implement: +the very first step of mutation makes a copy! +Thus, `get` and `set` can't be used to wrap access to a noncopyable value: + +```swift +struct UniqueString : ~Copyable {...} + +struct UniqueGetSet : ~Copyable { + var x: UniqueString + + var property: UniqueString { + get { // error: 'self' is borrowed and cannot be consumed + x + } + set { x = newValue } + } +} +``` + +`get` borrows `self` but then tries to _give_ ownership of `x` to its caller. Since `self` is only borrowed, it cannot give up ownership of its `x` value, and since `UniqueString` is not `Copyable`, `x` cannot be copied either, making `get` impossible to implement. +Therefore, to provide computed properties on noncopyable types with comparable expressivity, we again need [an accessor](#design-modify) that borrows `self` and _lends_ access to `x` to its caller without implying independent ownership. + +### `yielding borrow` + +For properties and subscripts of noncopyable type, the current official accessors are insufficient not only for mutating, but even for _inspecting_. +Even if we remove `set` from our `UniqueGetSet` type above, we still hit the same error in its getter: + +```swift +struct UniqueString : ~Copyable {...} + +struct UniqueGet : ~Copyable { + var x: UniqueString + + var property: UniqueString { + get { // error: 'self' is borrowed and cannot be consumed + return x + } + } +} +``` + +As discussed above, `UniqueGet.property { get }` borrows `self` but attempts to transfer ownership of `self.x` to the caller, which is impossible. + +This particular error could be addressed by marking the getter `consuming`: + +```swift +struct UniqueString : ~Copyable {...} + +struct UniqueConsumingGet : ~Copyable { + var x: UniqueString + + var property: UniqueString { + consuming get { + return x + } + } +} +``` + +This allows the getter to take ownership of the `UniqueConsumingGet`, +allowing it to destructively extract `x` and transfer ownership to the caller. +Here's how that looks in the caller: + +```swift +let container = UniqueConsumingGet() +let x = container.property // consumes container! +// container is no longer valid +``` + +This might sometimes be desirable, but for many typical uses of properties and subscripts, it is not. If a container holds a number of noncopyable fields, it should be possible to inspect each field in turn, but doing so wouldn't be possible if inspecting one consumes the container. + +Similar to the mutating case, what's needed here is [an accessor](#design-read) which _borrows_ `self` and which _lends_ `x`--this time immutably--to the caller. + +## Proposed solution + +We propose two new accessor kinds: +- `yielding mutate`, to enable mutating a value without first copying it +- `yielding borrow`, to enable inspecting a value without copying it. + +## Detailed design + +### `yielding borrow` + +[`UniqueGet`](#read-motivation) can now allow its clients to inspect its field non-destructively with `yielding borrow`: + +```swift +struct UniqueString : ~Copyable {...} + +struct UniqueBorrow : ~Copyable { + var x: UniqueString + + var property: UniqueString { + yielding borrow { + yield x + } + } +} +``` + +The `UniqueBorrow.property { yielding borrow }` accessor is a "yield-once coroutine". +When called, it borrows `self`, and then runs until reaching a `yield`, at which point it suspends, lending the yielded value back to the caller. +Once the caller is finished accessing the value, it resumes the accessor's execution. +The accessor continues running where it left off after the `yield`. + +If a a property or subscript provides a `yielding borrow`, it cannot also provide a `get`. + +#### `yielding borrow` as a protocol requirement + +Such accessors should be usable on values of generic and existential type. +To indicate that a protocol provides immutable access to a property or subscript via a `yielding borrow` coroutine, we propose allowing `yielding borrow` to appear where `get` does today: + +```swift +protocol Containing { + var property: UniqueString { yielding borrow } +} +``` + +A protocol requirement cannot specify both `yielding borrow` and `get`. + +A `yielding borrow` requirement can be witnessed by a stored property, a `yielding borrow` accessor, or a getter. + +#### `get` of noncopyable type as a protocol requirement + +Property and subscript requirements in protocols have up to this point only been able to express readability in terms of `get` requirements. +This becomes insufficient when the requirement has a noncopyable type: + +```swift +protocol Producing { + var property: UniqueString { get } +} +``` + +To fulfill such a requirement, the conformance must provide a getter, meaning its implementation must be able to produce a value whose ownership can be transferred to the caller. +In practical terms, this means that the requirement cannot be witnessed by a stored property or a `yielding borrow` accessor when the result is of noncopyable type,[^2] since the storage of a stored property is owned by the containing aggregate and the result of a `yielding borrow` is owned by the suspended coroutine, and it would be necessary to copy to provide ownership to the caller. +However, if the type of the `get` requirement is copyable, the compiler can synthesize the getter from the other accessor kinds by introducing copies as necessary. + +[^2]: While the compiler does currently accept such code currently, it does so by interpreting that `get` as a `yielding borrow`, which is a bug. + +### `yielding mutate` + +The `GetSet` type [above](#modify-motivation) could be implemented with `yielding mutate` as follows: + +```swift +struct GetMutate { + var x: String = "👋🏽 Hello" + + var property: String { + get { print("Getting", x); return x } + yielding mutate { + print("Yielding", x) + yield &x + print("Post yield", x) + } + } +} + +var getMutate = GetMutate() +getMutate.property.append(", 🌍!") +// prints: +// Yielding 👋🏽 Hello +// Post yield 👋🏽 Hello, 🌍! +``` + +Like `UniqueBorrow.property { yielding borrow }` above, `GetMutate.property { yielding mutate }` is a yield-once coroutine. +However, the `yielding mutate` accessor lends `x` to the caller _mutably_. + +Things to note about this example: +* `get` is never called — the property access is handled entirely by the `yielding mutate` accessor +* the `yield` is similar to a `return`, but control returns to the `yielding mutate` after the `append` completes +* there is no more `newValue` – the yielded value is modified by `append` +* because it's granting _mutable_ access to the caller, `yield` uses the `&` sigil, similar to passing an argument `inout` + +Unlike the `get`/`set` pair, the `yielding mutate` accessor is able to safely provide access to the yielded value without copying it. +This can be done safely because the accessor preserves ownership of the value until it has completely finished running: +When it yields the value, it only lends it to the caller. +The caller is exclusively borrowing the value yielded by the coroutine. + +`get` is still used when only fetching, not modifying, the property: + +```swift +_ = getMutate.property +// prints: +// Getting 👋🏽 Hello, 🌍! +``` + +`yielding mutate` is sufficient to allow assignment to a property: + +``` +getMutate.property = "Hi, 🌍, 'sup?" +// prints: +// Yielding 👋🏽 Hello, 🌍! +// Post yield Hi, 🌍, 'sup? +``` + +It is, however, also possible to supply _both_ a `yielding mutate` and a `set` accessor. +The `set` accessor will be favored in the case of a whole-value reassignment, which may be more efficient than preparing and yielding a value to be overwritten: + +```swift +struct GetSetMutate { + var x: String = "👋🏽 Hello" + + var property: String { + get { x } + yielding mutate { yield &x } + set { print("Setting",newValue); x = newValue } + } +} +var getSetMutate = GetSetMutate() +getSetMutate.property = "Hi 🌍, 'sup?" +// prints: +// Setting Hi 🌍, 'sup? +``` + +#### Pre- and post-processing in `yielding mutate` + +As with `set`, `yielding mutate` gives the property or subscript implementation an opportunity to perform some post-processing on the new value after assignment. Even in these cases, `yielding mutate` can often allow for a more efficient implementation than a traditional `get`/`set` pair would provide. +Consider the following implementation of an enhanced version of `Array.first` that allows the user to modify the first value of the array: + +```swift +extension Array { + var first: Element? { + get { isEmpty ? nil : self[0] } + yielding mutate { + var tmp: Optional + if isEmpty { + tmp = nil + yield &tmp + if let newValue = tmp { + self.append(newValue) + } + } else { + tmp = self[0] + yield &tmp + if let newValue = tmp { + self[0] = newValue + } else { + self.removeFirst() + } + } + } + } +} +``` + +This implementation takes a similar approach to `Swift.Dictionary`'s key-based subscript: +if there is no `first` element, the accessor appends one. +If `nil` is assigned, the element is removed. +Otherwise, the element is updated to the value from the assigned `Optional`'s payload. + +Because the fetch and update code are all contained in one block, the `isEmpty` check is not duplicated; if the same logic were implemented with a `get`/`set` pair, `isEmpty` would need to be checked independently in both accessors. With `yielding mutate`, whether the array was initially empty is part of the accessor's state, which remains present until the accessor is resumed and completed. + +#### Taking advantage of exclusive access + +The optional return value of `first` in the code above means that, despite using `yielding mutate`, we have reintroduced the problem of triggering copy-on-write when mutating through the `first` property. +That act of placing the value in the array inside of an `Optional` (namely, `tmp = self[0]`) creates a copy. + +Like a `set` accessor, a `yielding mutate` accessor in a `struct` or `enum` property is `mutating` by default (unless explicitly declared a `nonmutating yielding mutate`). +This means that the `yielding mutate` accessor has exclusive access to `self`, and that exclusive access extends for the duration of the accessor's execution, including its suspension after `yield`-ing. +Since the implementation of `Array.first { yielding mutate }` has exclusive access to its underlying buffer, it can move the value of `self[0]` directly into the `Optional`, yield it, and then move the result back after being resumed: + +```swift +extension Array { + var first: Element? { + yielding mutate { + var tmp: Optional + if isEmpty { + // Unchanged + } else { + // Illustrative code only, Array's real internals are fiddlier. + // _storage is an UnsafeMutablePointer to the Array's storage. + + // Move first element in _storage into a temporary, leaving that slot + // in the storage buffer as uninintialized memory. + tmp = _storage.move() + + // Yield that moved value to the caller + yield &tmp + + // Once the caller returns, restore the array to a valid state + if let newValue = tmp { + // Re-initialize the storage slot with the modified value + _storage.initialize(to: newValue) + } else { + // Element removed. Slide other elements down on top of the + // uninitialized first slot: + _storage.moveInitialize(from: _storage + 1, count: self.count - 1) + self.count -= 1 + } + } + } +} +``` + +While the `yielding mutate` coroutine is suspended after yielding, the `Array` is left in an invalid state: the memory location where the first element is stored is left uninitialized, and must not be accessed. +However, this is safe thanks to Swift's rules preventing conflicting access to memory. +Unlike a `get`, the `yielding mutate` is guaranteed to have an opportunity to put the element back (or to remove the invalid memory if the entry is set to `nil`) after the caller resumes it, restoring the array to a valid state in all circumstances before any other code can access it. + +### The "yield once" rule + +Notice that there are _two_ yields in this `Array.first { yielding mutate }` implementation, for the empty and non-empty branches. +Nonetheless, exactly one `yield` is executed on any path through the accessor. +In general, the rules for yields in yield-once coroutines are similar to those of deferred initialization of `let` variables: +it must be possible for the compiler to guarantee there is exactly one yield on every path. +In other words, there must not be a path through the yield-once coroutine's body with either zero[^1] or more than one yield. +The `Array.first` example is valid since there is a `yield` in both the `if` and the `else` branch. +In cases where the compiler cannot statically guarantee this, refactoring or use of `fatalError()` to assert unreachable code paths may be necessary. + +[^1]: Note that it is legal for a path without any yields to terminate in a `fatalError`. Such a path is not _through_ the function. + +### Throwing callers + +The `Array.first { yielding mutate }` implementation above is correct even if the caller throws while the coroutine is suspended. + +```swift +try? myArray.first?.throwingMutatingOp() +``` + +Thanks to Swift's rule that `inout` arguments be initialized at function exit, the element must be a valid value when `throwingMutatingOp` throws. +When `throwingMutatingOp` does throw, control returns back to the caller. +The body of `Array.first { yielding mutate }` is resumed, and `tmp` is a valid value. +Then the code after the `yield` executes. +The coroutine cleans up as usual, writing the updated temporary value in `tmp` back into the storage buffer. + +## Source compatibility + +The following code is legal today: + +```swift +func borrow(_ c : () -> T) -> T { c() } +var reader : Int { + borrow { + fatalError() + } +} +``` + +Currently, the code declares a property `reader` with an implicit getter. +The implicit getter has an implicit return. +The expression implicitly returned is a call to the function `borrow` with a trailing closure. + +An analogous situation exists for `mutate`. + +This proposal takes the identifiers `borrow` and `mutate` as contextual keywords +as part of the `yielding borrow` and `yielding mutate` accessor declarations. +By themselves, these two new accessor forms do not immediately require a source break; however, as the [accessors vision](https://github.com/rjmccall/swift-evolution/blob/accessors-vision/visions/accessors.md) lays out, we will more than likely want to introduce non-`yielding` variants of `borrow` and `mutate` in the near future as well. + +Therefore, we propose an alternate interpretation for this code: +that it declare a property `reader` with a `borrow` accessor. +Although such an accessor does not yet exist, making the syntax change now will prepare the language for subsequent accessors we introduce in the near future. + +If this presents an unacceptable source compatibility break, the change may have to be gated on a language version. + +## ABI compatibility + +Adding a `yielding mutate` accessor to an existing read-only subscript or computed property has the same ABI implications as adding a setter; it must be guarded by availability on ABI-stable platforms. +The stable ABI for mutable properties and subscripts provides `get`, `set`, and `_modify` coroutines, deriving `set` from `_modify` or vice versa if one is not explicitly implemented. +Therefore, adding or removing `yielding mutate`, adding or removing `set`, replacing a `set` with a `yielding mutate`, or replacing `yielding mutate` with `set` are all ABI-compatible changes, so long as the property or subscript keeps at least one of `set` or `yielding mutate`, and those accessors agree on whether they are `mutating` or `nonmutating` on `self`. + +By contrast, `yielding borrow` and `get` are mutually exclusive. Replacing one with the other is always an ABI-breaking change. +Replacing `get` with `yielding borrow` is also an API-breaking change for properties or subscripts of noncopyable type. + +Renaming the current `_modify` (as used by the standard library, e.g.) to `yielding mutate` is an ABI additive change: a new `yielding mutate` symbol will be added. +When the compiler sees a `yielding mutate` with an early enough availability, the compiler will synthesize a corresponding `_modify` whose body will just forward to `yielding mutate`. +This is required for ABI stability: code compiled against an older standard library which calls `_modify` will continue to do so. +Meanwhile, code compiled against a newer standard library will call the new `yielding mutate`. +The same applies to renaming `_read` to `yielding borrow`. + +## Implications on adoption + +### Runtime support + +The new ABI will require runtime support which would need to be back deployed in order to be used on older deployment targets. + +### When to favor coroutines over `get` and `set` + +Developers will need to understand when to take advantage of `yielding` accessors, and we should provide some guidance: + +#### `yielding mutate` + +`yielding mutate` is desirable when the value of a property or subscript element is likely to be subject to frequent in-place modifications, especially when the value's type uses copy-on-write and the temporary value created by a `get`-update-`set` sequence is likely to trigger full copies of the buffer that could otherwise be avoided. +Additionally, if the mutation operation involves complex setup and teardown that requires persisting state across the operation, `yielding mutate` allows for arbitrary state to be preserved across the access. + +For noncopyable types, implementing `yielding mutate` is necessary to provide in-place mutation support if the value of the property or subscript cannot be read using a `get`. + +Even in cases where `yielding mutate` is beneficial, it is often still profitable to also provide a `set` accessor for whole-value reassignment. `set` may be able to avoid setup necessary to support an arbitrary in-place mutation and can execute as a standard function call, which may be more efficient than a coroutine. + +#### `yielding borrow` + +For read-only operations on `Copyable` types, the performance tradeoffs between `get` and `yielding borrow` are more subtle. +`borrowing yield` can avoid copying a result value that is stored as part of the originating value. +On the other hand, `get` can execute as a normal function without the overhead of a coroutine. +(As part of the implementation work for this proposal, we are implementing a more efficient coroutine ABI that should be significantly faster than the implementation used by `_read` and `_modify` today; however, a plain function call is likely to remain somewhat faster.) +`yielding borrow` may nonetheless be preferable for providing access to large value types in memory that would be expensive to copy as part of returning from a `get`. + +`yielding borrow` also introduces the ability to perform both setup work before and teardown work after the caller has accessed the value, whereas `get` can at best perform setup work before returning control to the caller. +Particularly in conjunction with non-`Escapable` types, which could eventually be lifetime-bound to their access, a `yielding borrow` accessor producing a non-`Escapable` value could provide limited access to a resource in a similar manner to `withSomething { }` closure-based APIs, without the "pyramid of doom" or scoping limitations of closure-based APIs. + +For properties and subscripts with noncopyable types, the choice between `yielding borrow` and `get` will often be forced by whether the API contract specifies a borrowed or owned result. +An operation that provides temporary borrowed access to a component of an existing value without transferring ownership, when that value is expected to persist after the access is complete, must be implemented using `yielding borrow`. +An operation that computes a new value every time is best implemented using `get`. +(Such an operation can in principle also be implemented using `yielding borrow`; it would compute the temporary value, yield a borrow to the caller, and then destroy the temporary value when the coroutine is resumed. +This would be inefficient, and would furthermore prevent the caller from being able to perform consuming operations on the result.) + +For polymorphic interfaces such as protocol requirements and overridable class members that are properties or subscripts of noncopyable type, the most general requirement is `yielding borrow`, since a `yielding borrow` requirement can be satisfied by either a `yielding borrow` or a `get` implementation (in the latter case, by wrapping the getter in a synthesized `yielding borrow` that invokes the getter, yields a borrow the result, then destroys the value on resume). +Using `yielding borrow` in polymorphic or ABI-stable interfaces may thus desirable to allow for maximum evolution flexibility if being able to consume the result is never expected to be part of the API contract. + +## Future directions + +### Yield-once functions + +Further ergonomic enhancements to the language may be needed over time to make the most of this feature. +For example, coroutine accessors do not compose well with functions because functions cannot themselves currently yield values. +In the future, it may be desirable to also support yield-once functions: + +```swift +var value: C { yielding mutate { ... } } +yielding func updateValue(...) -> inout C { + yield &self.value + additionalWork(value) +} +``` + +### Forwarding both consuming and borrowing accesses to the same declaration + +When a property or subscript has a `consuming get`, a caller can take ownership of the field at the expense of also destroying the rest of object. +When a property or subscript has a `yielding borrow` accessor, a caller can borrow the field to inspect it without taking ownership of it. + +As proposed here, it's not yet possible for a single field to provide both of these behaviors to different callers. +Since both of these behaviors have their uses, and many interfaces want to provide "perfect forwarding" where any of consuming, mutating, or borrowing operations on a wrapper can be performed by passing through the same operation to an underlying value, it may be desirable in the future to allow a single field to provide both a `yielding borrow` and a `consuming get`: + +```swift +subscript(index: Int) -> Value { + consuming get {...} + yielding borrow {...} +} +``` + +The rules that Swift currently uses to determine what accessor(s) to use in order to evaluate an expression are currently only defined in terms of whether the access is mutable or not, so these rules would need further elaboration to distinguish non-mutating accesses by whether they are consuming or borrowing in order to determine which accessor to favor. + +### Transitioning between owned and borrowed accessors for API evolution + +When a noncopyable API first comes into existence, its authors may not want to commit to it producing an owned value. As discussed [above](#read-implications), providing a `yielding borrow` accessor is often the most conservative API choice for noncopyable properties and subscripts: + +```swift +subscript(index: Int) -> Value { + yielding borrow {...} +} +``` + +As their module matures, however, the authors may decide that committing to providing a consumable value is worthwhile. +To support this use-case, in the future, it may be desirable to allow for forward-compatible API and ABI evolution by promoting a `yielding borrow` accessor to `get`: + +```swift +subscript(index: Int) -> Value { + // Deprecate the `yielding borrow` + @available(*, deprecated) + yielding borrow {...} + + // Favor the `get` for new code + get {...} +} +``` + + +That would enable the module to evolve to a greater commitment while preserving ABI. + +For APIs of copyable type, the reverse evolution is also a possibility; an API could originally be published using a `get` and later decide that a `yielding borrow` accessor is overall more efficient. + +Since it would typically be ambiguous whether the `yielding borrow` or `get` should be favored at a use site, it would make sense to require that one or the other accessor be deprecated or have earlier availability than the other in order to show that one is unambiguously preferred over the other for newly compiled code with recent enough availability. + +### Non-`yielding` `borrow` and `mutate` accessors + +A `yielding` accessor lends the value it yields to its caller. +The caller only has access to that value until it resumes the coroutine. +After the coroutine is resumed, it has the opportunity to clean up. +This enables a `yielding borrow` or `mutate` to do interesting work such as constructing aggregates from its base object's fields: + +```swift +struct Pair : ~Copyable { + var left: Left + var right: Right + + var reversed: Pair { + yielding borrow { + let result = Pair(left: right, right: left) + yield result + self = .init(left: result.right, right: result.left) + } + } +} +``` + +That the borrow ends when the coroutine is resumed means that the lifetime of the lent value is strictly shorter than that of the base value. +In the example above, the lifetime of `reversed` is shorter than that of the `Pair` it is called on. + +As discussed in the [accessors vision](https://github.com/rjmccall/swift-evolution/blob/accessors-vision/visions/accessors.md), when a value is merely being projected from the base object and does not need any cleanup after being accessed, this is undesirably limiting: +a value projected from a base naturally has _the same_ lifetime as the base. + +This is especially problematic in the context of composition with non-`Escapable` types. +Consider the following wrapper type[^3]: + +[^3]: This example involves writing out a `yielding borrow` accessor. The same issue exists when the compiler synthesizes a `yielding borrow` accessor for a stored property exported from a resilient module. + +```swift +struct Wrapper : ~Copyable & ~Escapable { + var _stuffing: Stuffing + + var stuffing: Stuffing { + yielding borrow { + yield _stuffing + } + } +} +``` + +When the instance of `Wrapper` is local to a function, the strict nesting of lifetimes may not immediately be a problem: + +```swift +{ + let wrapper: Wrapper = ... + borrowStuffing(wrapper.stuffing) + // lifetime of wrapper.stuffing ends (at coroutine resumption) + // lifetime of wrapper ends +} +``` + +But when `Wrapper` is a parameter, or otherwise nonlocal to the function, it is natural to expect to return the `Stuffing` back to the source of the `Wrapper`, but the `yielding borrow` accessor artificially limits its lifetime: + +```swift +@lifetime(borrow wrapper) +func getStuffing(from wrapper: borrowing Wrapper) -> Stuffing { + return wrapper.stuffing // error +} +``` + +The issue is that the lifetime of `stuffing` ends _within_ `getStuffing`, when the `yielding borrow` coroutine is resumed, which prevents `stuffing` from being returned. + +To address use cases like this, in the future, it may be desirable to introduce a variant of `borrow` and `mutate` accessors that immediately `return`s the borrowed or mutable value without involving a coroutine: + +```swift +var stuffing: Stuffing { + borrow { + return _stuffing + } +} +``` + +A non-`yielding` `borrow` or `mutate` accessor would be limited to returning values that can be accessed immediately and which do not require any cleanup (implicit or explicit) when the access ends. As such, the `yielding` variants would still provide the most flexibility in implementation, at the cost of the additional lifetime constraint of the coroutine. Similar to the [discussion around evolving `yielding borrow` accessors into `get` accessors](#ownership-evolution), there is also likely to be a need to allow for APIs initially published in terms of `yielding` accessors to transition to their corresponding non-`yielding` accessors in order to trade stronger implementation guarantees for more flexibility in the lifetime of derived `~Escapable` values. + +## Alternatives considered + +### Unwinding the accessor when an error is thrown in the caller + +A previous version of this proposal specified that if an error is thrown in a coroutine caller while a coroutine is suspended, the coroutine is to "unwind" and the code after the `yield` is not to run. +In the [example above](#throwing-callers), the code after the `yield` would not run if `throwingMutatingOp` threw an error. + +This approach was tied up with the idea that a `yielding mutate` accessor might clean up differently if an error was thrown in the caller. +The intervening years of experience with the feature have not borne out the need for this. +If an error is thrown in a caller into which a value has been yielded, the _caller_ must put the yielded mutable value back into a consistent state. +As with `inout` function arguments, the compiler enforces this: +it is an error to consume the value yielded from a `yielding mutate` accessor without reinitializing it before resuming the `yielding mutate` accessor. +When there are higher-level invariants which the value being modified must satisfy, in general, only the caller will be in a position to ensure that they are satisfied on the throwing path. + +Once that basis has been removed, there is no longer a reason to enable a coroutine to "unwind" when an error was thrown in the caller. +It should always finish execution the same way. + +### Naming scheme + +These coroutine accessors have been a long-standing "unofficial" feature of the Swift compiler under the names `_read` and `_modify`. +Previous revisions of this proposal merely dropped the underscore and proposed the coroutine accessors be provided under the name `read` and `modify`. +However, as we have continued to build out Swift's support for ownership and lifetime dependencies with `~Copyable` and `~Escapable` types, we have since identified the need for non-coroutine accessors that can produce borrowed and/or mutable values without imposing a lifetime dependency on a coroutine access. + +In order to avoid a proliferation of unrelated-seeming accessor names, this revision of the proposal uses the name `yielding borrow` instead of `read` and `yielding mutate` instead of `modify`. +We feel these names better connect the accessors to what ownership of the result is given: +a `borrow` accessor gives `borrowing` access to its result, and a `mutate` accessor gives `mutating` (in other words, `inout`) access. +Using `yielding` as an added modifier relates these accessors to potential non-coroutine variants of the accessors that could exist in the future; a `borrow` accessor (without `yielding`) would in the future be an accessor that returns a borrow without involving a coroutine. +The same `yielding` modifier could also be used in other places in the future, such as `func` declarations, to allow for yield-once coroutines to be defined as regular functions outside of accessors. + +## Acknowledgments + +John McCall and Arnold Schwaighofer provided much of the original implementation of accessor coroutines. Tim Kientzle and John McCall authored the accessors vision document this proposal serves as part of the implementation of. + From badc7558e01ee2f43f247043504a4a792b5723e0 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 8 Apr 2025 16:06:15 -0400 Subject: [PATCH 4204/4563] Update 0474-yielding-accessors.md (#2778) Add link back to forums review. --- proposals/0474-yielding-accessors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0474-yielding-accessors.md b/proposals/0474-yielding-accessors.md index d9fb323da2..2e9f24d22d 100644 --- a/proposals/0474-yielding-accessors.md +++ b/proposals/0474-yielding-accessors.md @@ -6,7 +6,7 @@ * Status: **Active Review (Apr 8...22)** * Vision: [A Prospective Vision for Accessors in Swift](https://github.com/rjmccall/swift-evolution/blob/accessors-vision/visions/accessors.md) * Implementation: Partially available on main behind the frontend flag `-enable-experimental-feature CoroutineAccessors` -* Review: ([pitch 1](https://forums.swift.org/t/modify-accessors/31872)), ([pitch 2](https://forums.swift.org/t/pitch-modify-and-read-accessors/75627)), ([pitch 3](https://forums.swift.org/t/pitch-3-yielding-coroutine-accessors/77956)), (review) +* Review: ([pitch 1](https://forums.swift.org/t/modify-accessors/31872)), ([pitch 2](https://forums.swift.org/t/pitch-modify-and-read-accessors/75627)), ([pitch 3](https://forums.swift.org/t/pitch-3-yielding-coroutine-accessors/77956)), ([review](https://forums.swift.org/t/se-0474-yielding-accessors/79170)) ## Introduction From e2b1b4d4ee4a9585a280e4ba434aface11f205ea Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 8 Apr 2025 13:08:39 -0700 Subject: [PATCH 4205/4563] [SE-0470] Reinstate sendable-metatype model for type checking generics (#2763) * [SE-0470] Reinstate sendable-metatype model for type checking generics Based on discussions in the review thread, reinstate the sendable-metatype model for type checking generic definitions rather than the potentially-isolated-conformance model. While slightly less expressive, the sendable-metatype model is easier to reason about and teach. It's still possible to generalize the model toward the potentially-isolated-conformance model, as is already captured in Future Directions. * [SE-0470] Tighten up requirements within generic functions Close a potential data race with my attempt to loosen the rules. In essence, if we're allowed to pass a metatype `T.Type` across isolation boundaries when there are no constraints of the form `T: P`, then we will incorrectly allow a dynamic cast `as? any T & P` to bind to an isolated conformance (because `T` is not `SendableMetatype`). --- proposals/0470-isolated-conformances.md | 50 ++++++++++++++----------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/proposals/0470-isolated-conformances.md b/proposals/0470-isolated-conformances.md index 91c87b8b53..0809817d2d 100644 --- a/proposals/0470-isolated-conformances.md +++ b/proposals/0470-isolated-conformances.md @@ -82,7 +82,7 @@ This is effectively saying that `MyModelType` will only ever be considered `Equa ## Proposed solution -This proposal introduces the notion of an *isolated conformance*. Isolated conformances are conformances whose use is restricted to a particular global actor. This is the same effective restriction as the `nonisolated`/`assumeIsolated` pattern above, but enforced statically by the compiler and without any boilerplate. The following defines an isolated conformance of `MyModelType` to `Equatable`: +This proposal introduces the notion of an *isolated conformance*. Isolated conformances are conformances whose use is restricted to a particular global actor. This is the same effective restriction as the `nonisolated`/`assumeIsolated` pattern above, but enforced statically by the compiler and without any boilerplate. The following defines a main-actor-isolated conformance of `MyModelType` to `Equatable`: ```swift @MainActor @@ -144,10 +144,10 @@ func hasNamed(_: T.Type, name: String) async -> Bool { } ``` -Here, the type `T` itself is not `Sendable`, but because *all* metatypes are `Sendable` it is considered safe to use `T` from another isolation domain within the generic function. The use of `T`'s conformance to `GlobalLookup` within that other isolation domain introduces a data-race problem if the conformance were isolated. To prevent such problems in generic code, this proposal treats conformances within generic code as if they are isolated *unless* the conforming type opts in to being sendable. The above code, which is accepted in Swift 6 today, would be rejected by the proposed changes here with an error message like: +Here, the type `T` itself is not `Sendable`, but because *all* metatypes are `Sendable` it is considered safe to use `T` from another isolation domain within the generic function. The use of `T`'s conformance to `GlobalLookup` within that other isolation domain introduces a data-race problem if the conformance were isolated. To prevent such problems in generic code, this proposal introduces a notion of *non-sendable metatypes*. Specifically, if a type parameter `T` does not conform to either `Sendable` or to a new protocol, `SendableMetatype`, then its metatype, `T.Type`, is not considered `Sendable` and cannot cross isolation boundaries. The above code, which is accepted in Swift 6 today, would be rejected by the proposed changes here with an error message like: ```swift -error: cannot use potentially-isolated conformance of non-sendable type `T` to `GlobalLookup` in 'sending' closure +error: cannot capture non-sendable type 'T.Type' in 'sending' closure ``` A function like `hasNamed` can indicate that its type parameter `T`'s requires non-isolated conformance by introducing a requirement `T: SendableMetatype`, e.g., @@ -379,7 +379,7 @@ actor MyActor: @MainActor P { } ``` -### Rule 2: Isolated conformances can only be abstracted away for non-`Sendable` types +### Rule 2: Isolated conformances can only be abstracted away for non-`SendableMetatype` types Rule (2) ensures that when information about an isolated conformance is abstracted away by the generics system, the conformance cannot leave its original isolation domain. This requires a way to determine when a given generic function is permitted to pass a conformance it receives across isolation domains. Consider the example above where a generic function uses one of its conformances in different isolation domain: @@ -406,20 +406,20 @@ The above code must be rejected to prevent a data race. There are two options fo 1. Reject the definition of `callQGElsewhere` because it is using the conformance from a different isolation domain. 2. Reject the call to `callQGElsewhere` because it does not support isolated conformances. -This proposal takes option (1): we assume that generic code accepts isolated conformances unless it has indicated otherwise with a `Sendable` constraint (more information on that below). Since most generic code doesn't deal with concurrency at all, it will be unaffected. And generic code that does make use of concurrency should already have `Sendable` constraints that indicate that it will not work with isolated conformances. +This proposal takes option (1): we assume that generic code accepts isolated conformances unless it has indicated otherwise with a `SendableMetatype` constraint. Since most generic code doesn't deal with concurrency at all, it will be unaffected. And generic code that does make use of concurrency should already have `Sendable` constraints (which imply `SendableMetatype` constraints) that indicate that it will not work with isolated conformances. -The specific requirement for option (1) is enforced both in the caller to a generic function and in the implementation of that function. The caller can use an isolated conformance to satisfy a conformance requirement `T: P` so long as the generic function does not also contain a requirement `T: Sendable`. This prevents isolated conformances to be used in conjunction with types that can cross isolation domains, preventing the data race from being introduced at the call site. Here are some examples of this rule: +The specific requirement for option (1) is enforced both in the caller to a generic function and in the implementation of that function. The caller can use an isolated conformance to satisfy a conformance requirement `T: P` so long as the generic function does not also contain a requirement `T: SendableMetatype`. This prevents isolated conformances to be used in conjunction with types that can cross isolation domains, preventing the data race from being introduced at the call site. Here are some examples of this rule: ```swift -func acceptsSendableP(_ value: T) { } +func acceptsSendableMetatypeP(_ value: T) { } func acceptsAny(_ value: T) { } -func acceptsSendable(_ value: T) { } +func acceptsSendableMetatype(_ value: T) { } @MainActor func passIsolated(s: S) { - acceptsP(s) // okay: the type parameter 'T' requires P but not Sendable - acceptsSendableP(s) // error: the type parameter 'T' requires Sendable - acceptsAny(s) // okay: no isolated conformance - acceptsSendable(s) // okay: no isolated conformance + acceptsP(s) // okay: the type parameter 'T' requires P but not SendableMetatype + acceptsSendableMetatypeP(s) // error: the type parameter 'T' requires SendableMetatype + acceptsAny(s) // okay: no isolated conformance + acceptsSendableMetatype(s) // okay: no isolated conformance } ``` @@ -431,19 +431,19 @@ The same checking occurs when the type parameter is hidden, for example when dea } @MainActor func isolatedAnyBad(s: S) { - let a: any Sendable & P = s // error: the (hidden) type parameter for the 'any' is Sendable + let a: any SendableMetatype & P = s // error: the (hidden) type parameter for the 'any' is SendableMetatype } @MainActor func returnIsolatedSomeGood(s: S) -> some P { return s // okay: the 'any P' cannot leave the isolation domain } -@MainActor func returnIsolatedSomeBad(s: S) -> some Sendable & P { +@MainActor func returnIsolatedSomeBad(s: S) -> some SendableMetatype & P { return s // error: the (hidden) type parameter for the 'any' is Sendable } ``` -Within the implementation, a conformance requirement `T: Q` is considered to be isolated if there is no requirement `T: Sendable`. This mirrors the rule on the caller side, and causes the following code to be ill-formed: +Within the implementation, we ensure that a conformance that could be isolated cannot cross an isolation boundary. This is done by making the a metatype `T.Type` `Sendable` only when there existing a constraint `T: SendableMetatype`. Therefore, the following program is ill-formed: ```swift protocol Q { @@ -452,14 +452,14 @@ protocol Q { nonisolated func callQGElsewhere(_: T.Type) { Task.detached { - T.g() // error: use of potentially-isolated conformance of non-Sendable type T to Q + T.g() // error: non-sendable metatype of `T` captured in 'sending' closure } } ``` -To correct this function, add a constraint `T: Sendable`, which allows the function to send the conformance across isolation domains. As described above, it also prevents the caller from providing an isolated conformance to satisfy the `T: Q` requirement, preventing the data race. +To correct this function, add a constraint `T: SendableMetatype`, which allows the function to send the metatype (along with its conformances) across isolation domains. As described above, it also prevents the caller from providing an isolated conformance to satisfy the `T: Q` requirement, preventing the data race. -The `Sendable` requirement described above is a stricter contract than is necessary for a function such as `callQGElsewhere`, which won't ever pass *values* of the type `T` across an isolation domain. Therefore, we introduce a new marker protocol `SendableMetatype` to capture the idea that values of the metatype of `T` (i.e., `T.Type`) will cross isolation domains and take conformances with them. A requirement `T: SendableMetatype` prohibits isolated conformances from being used on type `T`. Now, `callQGElsewhere` can be correctly expressed as follows: +`SendableMetatype` is a new marker protocol that captures the idea that values of the metatype of `T` (i.e., `T.Type`) will cross isolation domains and can take conformances with them. It is less restrictive than a `Sendable` requirement, which specifies that *values* of a type can be sent across isolation boundaries. All concrete types (structs, enums, classes, actors) conform to `SendableMetatype` implicitly, so fixing `callQGElsewhere` will not affect any non-generic code: ```swift nonisolated func callQGElsewhere(_: T.Type) { @@ -467,9 +467,10 @@ nonisolated func callQGElsewhere(_: T.Type) { T.g() } } -``` -The `SendableMetatype` protocol is somewhat special, because according to SE-0302 *all* metatypes are `Sendable`. This proposal refines that statement slightly: all concrete types (structs, enums, classes, actors) implicitly conform to `SendableMetatype`, because their metatypes (e.g., `MyModelType.Type`) are all `Sendable`. Therefore, a call to `callQGElsewhere` for any concrete type will succeed so long as that type has a (non-isolated) conformance to `Q`. +struct MyTypeThatConformsToQ: Q { ... } +callQGElsewhere(MyTypeThatConformsToQ()) // still works +``` The `Sendable` protocol inherits from the new `SendableMetatype` protocol: @@ -492,7 +493,7 @@ will continue to work with the stricter model for generic functions in this prop The proposed change for generic functions does have an impact on source compatibility, where functions like `callQGElsewhere` will be rejected. However, the source break is limited to generic code that: -1. Uses a conformance requirement (`T: P`) of a non-marker protocol `P` in another isolated domain, +1. Passes the metatype `T.Type` of a generic parameter `T` across isolation boundaries; 2. Does not have a corresponding constraint `T: Sendable` requirement; and 3. Is compiled with strict concurrency enabled (either as Swift 6 or with warnings). @@ -580,7 +581,7 @@ Initial testing of an implementation of this proposal found very little code tha Isolated conformances can be introduced into the Swift ABI without any breaking changes, by extending the existing runtime metadata for protocol conformances. All existing (non-isolated) protocol conformances can work with newer Swift runtimes, and isolated protocol conformances will be usable with older Swift runtimes as well. There is no technical requirement to restrict isolated conformances to newer Swift runtimes. -However, there is one likely behavioral difference with isolated conformances between newer and older runtimes. In newer Swift runtimes, the functions that evaluate `as?` casts will check of an isolated conformance and validate that the code is running on the proper executor before the cast succeeds. Older Swift runtimes that don't know about isolated conformances will allow the cast to succeed even outside of the isolation domain of the conformance, which can lead to different behavior that potentially involves data races. +However, there is one likely behavioral difference with isolated conformances between newer and older runtimes. In newer Swift runtimes, the functions that evaluate `as?` casts will check of an isolated conformance and validate that the code is running on the proper executor before the cast succeeds. Older Swift runtimes that don't know about isolated conformances will allow the cast to succeed even outside of the isolation domain of the conformance, which can lead to different behavior that potentially involves data races. It should be possible to provide (optional) warnings when running on newer Swift runtimes when a cast fails due to isolated conformances but would incorrectly succeed on older platforms. ## Future Directions @@ -660,3 +661,8 @@ This is a generalization of the proposed rules that makes more explicit when con * If not `T: SendableMetatype`, `T: P` is interepreted as `T: isolated P`. The main down side of this alternative is the additional complexity it introduces into generic requirements. It should be possible to introduce this approach later if it proves to be necessary, by treating it as a generalization of the existing rules in this proposal. + +## Revision history + +* Changes in review: + * Within a generic function, use sendability of metatypes of generic parameters as the basis for checking, rather than treating specific conformances as potentially isolated. This model is easier to reason about and fits better with `SendableMetatype`, and was used in earlier drafts of this proposal. From 964afd9798792975becaa77d411c63b32110d567 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 9 Apr 2025 11:24:43 -0400 Subject: [PATCH 4206/4563] Accept SE-0470 (#2780) --- proposals/0470-isolated-conformances.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0470-isolated-conformances.md b/proposals/0470-isolated-conformances.md index 0809817d2d..146dcd95bd 100644 --- a/proposals/0470-isolated-conformances.md +++ b/proposals/0470-isolated-conformances.md @@ -3,11 +3,11 @@ * Proposal: [SE-0470](0470-isolated-conformances.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (March 21...April 3, 2025)** +* Status: **Accepted** * Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md) * Implementation: On `main` with the experimental features `IsolatedConformances` and `StrictSendableMetatypes`. * Upcoming Feature Flag: `InferIsolatedConformances` -* Review: ([pitch](https://forums.swift.org/t/pre-pitch-isolated-conformances/77726)) ([review](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704)) +* Review: ([pitch](https://forums.swift.org/t/pre-pitch-isolated-conformances/77726)) ([review](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704)) ([acceptance](https://forums.swift.org/t/accepted-se-0470-global-actor-isolated-conformances/79189)) ## Introduction From cd7fec5c2294947a27c2f4948f73b75ae775aa8b Mon Sep 17 00:00:00 2001 From: Rachel Brindle Date: Wed, 9 Apr 2025 12:42:27 -0700 Subject: [PATCH 4207/4563] Mark ST-0009: Attachments as accepted Apply the modifications of AttachableContainer -> AttachableWrapper and update other corresponding text. --- proposals/testing/0009-attachments.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/proposals/testing/0009-attachments.md b/proposals/testing/0009-attachments.md index 565c01acc3..80225a2f75 100644 --- a/proposals/testing/0009-attachments.md +++ b/proposals/testing/0009-attachments.md @@ -3,10 +3,10 @@ * Proposal: [ST-0009](0009-attachments.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Review Manager: [Rachel Brindle](https://github.com/younata) -* Status: **Active Review (March 21 - April 8, 2025)** +* Status: **Accepted** * Bug: [swiftlang/swift-testing#714](https://github.com/swiftlang/swift-testing/issues/714) * Implementation: [swiftlang/swift-testing#973](https://github.com/swiftlang/swift-testing/pull/973) -* Review: ([review](https://forums.swift.org/t/st-0009-attachments/78698)), ([pitch](https://forums.swift.org/t/pitch-attachments/78072)) +* Review: ([acceptance](https://forums.swift.org/t/accepted-with-modifications-st-0009-attachments/79193)), ([review](https://forums.swift.org/t/st-0009-attachments/78698)), ([pitch](https://forums.swift.org/t/pitch-attachments/78072)) ## Introduction @@ -170,8 +170,8 @@ conform: /// A type should conform to this protocol if it can be represented as a /// sequence of bytes that would be diagnostically useful if a test fails. If a /// type cannot conform directly to this protocol (such as a non-final class or -/// a type declared in a third-party module), you can create a container type -/// that conforms to ``AttachableContainer`` to act as a proxy. +/// a type declared in a third-party module), you can create a wrapper type +/// that conforms to ``AttachableWrapper`` to act as a proxy. public protocol Attachable: ~Copyable { /// An estimate of the number of bytes of memory needed to store this value as /// an attachment. @@ -242,12 +242,12 @@ conformances, Foundation must be imported because `JSONEncoder` and Some types cannot conform directly to `Attachable` because they require additional information to encode correctly, or because they are not directly -`Sendable` or `Copyable`. A second protocol, `AttachableContainer`, is provided +`Sendable` or `Copyable`. A second protocol, `AttachableWrapper`, is provided that refines `Attachable`: ```swift /// A protocol describing a type that can be attached to a test report or -/// written to disk when a test is run and which contains another value that it +/// written to disk when a test is run and which wraps another value that it /// stands in for. /// /// To attach an attachable value to a test, pass it to ``Attachment/record(_:named:sourceLocation:)``. @@ -259,7 +259,7 @@ that refines `Attachable`: /// A type can conform to this protocol if it represents another type that /// cannot directly conform to ``Attachable``, such as a non-final class or a /// type declared in a third-party module. -public protocol AttachableContainer: Attachable, ~Copyable { +public protocol AttachableWrapper: Attachable, ~Copyable { /// The type of the attachable value represented by this type. associatedtype AttachableValue @@ -267,13 +267,13 @@ public protocol AttachableContainer: Attachable, ~Copyable { var attachableValue: AttachableValue { get } } -extension Attachment where AttachableValue: AttachableContainer & ~Copyable { +extension Attachment where AttachableValue: AttachableWrapper & ~Copyable { /// The value of this attachment. /// - /// When the attachable value's type conforms to ``AttachableContainer``, the - /// value of this property equals the container's underlying attachable value. + /// When the attachable value's type conforms to ``AttachableWrapper``, the + /// value of this property equals the wrappers's underlying attachable value. /// To access the attachable value as an instance of `T` (where `T` conforms - /// to ``AttachableContainer``), specify the type explicitly: + /// to ``AttachableWrapper``), specify the type explicitly: /// /// ```swift /// let attachableValue = attachment.attachableValue as T @@ -286,7 +286,7 @@ The cross-import overlay with Foundation also provides the following convenience interface for attaching the contents of a file or directory on disk: ```swift -extension Attachment where AttachableValue == _AttachableURLContainer { +extension Attachment where AttachableValue == _AttachableURLWrapper { /// Initialize an instance of this type with the contents of the given URL. /// /// - Parameters: @@ -307,7 +307,7 @@ extension Attachment where AttachableValue == _AttachableURLContainer { } ``` -`_AttachableURLContainer` is a type that conforms to `AttachableContainer` and +`_AttachableURLWrapper` is a type that conforms to `AttachableWrapper` and encloses the URL and corresponding mapped data. As an implementation detail, it is omitted from this proposal for brevity. From cc52603f2f57222aa9e1e2b4c9ef66de5d4a66aa Mon Sep 17 00:00:00 2001 From: Lody Date: Thu, 10 Apr 2025 05:33:52 +0900 Subject: [PATCH 4208/4563] [SE-0317] fix minor typo (#2779) --- proposals/0317-async-let.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0317-async-let.md b/proposals/0317-async-let.md index 6720edf791..245dd4e7e0 100644 --- a/proposals/0317-async-let.md +++ b/proposals/0317-async-let.md @@ -148,7 +148,7 @@ func asynchronous() async { and inside asynchronous closures: ```swift -func callMe(_ maybe: () async -> String) async -> String +func callMe(_ maybe: () async -> String) async -> String { return await maybe() } From 5ff0900999e537b5c086146686ab49d304ab09f4 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 9 Apr 2025 17:32:43 -0400 Subject: [PATCH 4209/4563] Adjustments to ST-0009. This PR makes some further adjustments to ST-0009 to ensure the rename from `AttachableContainer` to `AttachableWrapper` is also applied to its associated type (i.e. `AttachableValue` -> `Wrapped`) and corresponding property (i.e. `attachableValue` -> `wrappedValue`). --- proposals/testing/0009-attachments.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/testing/0009-attachments.md b/proposals/testing/0009-attachments.md index 80225a2f75..e8442b2a81 100644 --- a/proposals/testing/0009-attachments.md +++ b/proposals/testing/0009-attachments.md @@ -3,7 +3,7 @@ * Proposal: [ST-0009](0009-attachments.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Review Manager: [Rachel Brindle](https://github.com/younata) -* Status: **Accepted** +* Status: **Accepted with revisions** * Bug: [swiftlang/swift-testing#714](https://github.com/swiftlang/swift-testing/issues/714) * Implementation: [swiftlang/swift-testing#973](https://github.com/swiftlang/swift-testing/pull/973) * Review: ([acceptance](https://forums.swift.org/t/accepted-with-modifications-st-0009-attachments/79193)), ([review](https://forums.swift.org/t/st-0009-attachments/78698)), ([pitch](https://forums.swift.org/t/pitch-attachments/78072)) @@ -259,12 +259,12 @@ that refines `Attachable`: /// A type can conform to this protocol if it represents another type that /// cannot directly conform to ``Attachable``, such as a non-final class or a /// type declared in a third-party module. -public protocol AttachableWrapper: Attachable, ~Copyable { - /// The type of the attachable value represented by this type. - associatedtype AttachableValue +public protocol AttachableWrapper: Attachable, ~Copyable { + /// The type of the underlying value represented by this type. + associatedtype Wrapped - /// The attachable value represented by this instance. - var attachableValue: AttachableValue { get } + /// The underlying value represented by this instance. + var wrappedValue: Wrapped { get } } extension Attachment where AttachableValue: AttachableWrapper & ~Copyable { @@ -278,7 +278,7 @@ extension Attachment where AttachableValue: AttachableWrapper & ~Copyable { /// ```swift /// let attachableValue = attachment.attachableValue as T /// ``` - public var attachableValue: AttachableValue.AttachableValue { get } + public var attachableValue: AttachableValue.Wrapped { get } } ``` From 81787427101d6f8c944fe31e377c48233af70421 Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Thu, 10 Apr 2025 08:44:50 +0200 Subject: [PATCH 4210/4563] ST-0008: add link to second review topic --- proposals/testing/0008-exit-tests.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/testing/0008-exit-tests.md b/proposals/testing/0008-exit-tests.md index 57953ff9ee..4256c976ab 100644 --- a/proposals/testing/0008-exit-tests.md +++ b/proposals/testing/0008-exit-tests.md @@ -3,10 +3,10 @@ * Proposal: [ST-0008](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0008-exit-tests.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Review Manager: [Maarten Engels](https://github.com/maartene) -* Status: **Active Review (March 21...April 8, 2025)** +* Status: **Active Review (April 10...April 21, 2025)** * Bug: [apple/swift-testing#157](https://github.com/apple/swift-testing/issues/157) * Implementation: [apple/swift-testing#324](https://github.com/swiftlang/swift-testing/pull/324) -* Review: ([review](https://forums.swift.org/t/st-0008-exit-tests/78692)), ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) +* Review: ([second review](https://forums.swift.org/t/second-review-st-0008-exit-tests/79198)), ([review](https://forums.swift.org/t/st-0008-exit-tests/78692)), ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) ## Introduction From 406db1e2db3c6fa2dfb5dd3540436e8dda29630a Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 10 Apr 2025 15:43:28 -0400 Subject: [PATCH 4211/4563] Accept SE-0464. --- proposals/0464-utf8span-safe-utf8-processing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0464-utf8span-safe-utf8-processing.md b/proposals/0464-utf8span-safe-utf8-processing.md index dc7b5d7d5f..bfc006ac3e 100644 --- a/proposals/0464-utf8span-safe-utf8-processing.md +++ b/proposals/0464-utf8span-safe-utf8-processing.md @@ -3,10 +3,10 @@ * Proposal: [SE-0464](0464-utf8span-safe-utf8-processing.md) * Authors: [Michael Ilseman](https://github.com/milseman), [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Active review (March 5–19, 2025)** +* Status: **Accepted** * Bug: rdar://48132971, rdar://96837923 * Implementation: [swiftlang/swift#78531](https://github.com/swiftlang/swift/pull/78531) -* Review: ([first pitch](https://forums.swift.org/t/pitch-utf-8-processing-over-unsafe-contiguous-bytes/69715)) ([second pitch](https://forums.swift.org/t/pitch-safe-utf-8-processing-over-contiguous-bytes/72742)) ([third pitch](https://forums.swift.org/t/pitch-utf8span-safe-utf-8-processing-over-contiguous-bytes/77483)) ([review](https://forums.swift.org/t/se-0464-utf8span-safe-utf-8-processing-over-contiguous-bytes/78307)) +* Review: ([first pitch](https://forums.swift.org/t/pitch-utf-8-processing-over-unsafe-contiguous-bytes/69715)) ([second pitch](https://forums.swift.org/t/pitch-safe-utf-8-processing-over-contiguous-bytes/72742)) ([third pitch](https://forums.swift.org/t/pitch-utf8span-safe-utf-8-processing-over-contiguous-bytes/77483)) ([review](https://forums.swift.org/t/se-0464-utf8span-safe-utf-8-processing-over-contiguous-bytes/78307)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0464-safe-utf-8-processing-over-contiguous-bytes/79218)) ## Introduction From 941cfbc08f1f297edae0064ee02bc56a22394b97 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 10 Apr 2025 16:45:19 -0400 Subject: [PATCH 4212/4563] Accept SE-0473 --- proposals/0473-clock-epochs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0473-clock-epochs.md b/proposals/0473-clock-epochs.md index 4565338dcc..0eebef737b 100644 --- a/proposals/0473-clock-epochs.md +++ b/proposals/0473-clock-epochs.md @@ -3,9 +3,9 @@ * Proposal: [SE-0473](0473-clock-epochs.md) * Authors: [Philippe Hausler](https://github.com/phausler) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active Review (March 28th...April 9th, 2025)** +* Status: **Accepted** * Implementation: -* Review: ([pitch](https://forums.swift.org/t/pitch-suspendingclock-and-continuousclock-epochs/78017)) ([review](https://forums.swift.org/t/se-0473-clock-epochs/78923)) +* Review: ([pitch](https://forums.swift.org/t/pitch-suspendingclock-and-continuousclock-epochs/78017)) ([review](https://forums.swift.org/t/se-0473-clock-epochs/78923)) ([acceptance](https://forums.swift.org/t/accepted-se-0473-clock-epochs/79221)) ## Introduction From 986a981ad41cf52ecd1f5ca19902d00b07eb9163 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 10 Apr 2025 16:50:19 -0400 Subject: [PATCH 4213/4563] Add an implementation link to SE-0473 --- proposals/0473-clock-epochs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0473-clock-epochs.md b/proposals/0473-clock-epochs.md index 0eebef737b..c674352d3c 100644 --- a/proposals/0473-clock-epochs.md +++ b/proposals/0473-clock-epochs.md @@ -4,7 +4,7 @@ * Authors: [Philippe Hausler](https://github.com/phausler) * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Accepted** -* Implementation: +* Implementation: [PR #80409](https://github.com/swiftlang/swift/pull/80409) * Review: ([pitch](https://forums.swift.org/t/pitch-suspendingclock-and-continuousclock-epochs/78017)) ([review](https://forums.swift.org/t/se-0473-clock-epochs/78923)) ([acceptance](https://forums.swift.org/t/accepted-se-0473-clock-epochs/79221)) ## Introduction From c956aac190ff3d6f6d3d45e6d25c04a6a7984793 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Thu, 10 Apr 2025 18:54:52 -0700 Subject: [PATCH 4214/4563] [Observation] Proposal to add a transactional observation of values (#2726) * Proposal to add a transactional observation of values * Fix the placeholder SE# * Update the interfaces to use `@isolated(any)` instead of `isolated` parameters * Add a number of example cases to describe in more detail expected behaviors and potential edge cases * Fix some spellings to the new behavioral section * Transition to a non-optional construction and add a Iteration return version for explicitly constructing Observed untilFinished * Observed is SE-0475 --------- Co-authored-by: Freddy Kellison-Linn --- proposals/0475-observed.md | 529 +++++++++++++++++++++++++++++++++++++ 1 file changed, 529 insertions(+) create mode 100644 proposals/0475-observed.md diff --git a/proposals/0475-observed.md b/proposals/0475-observed.md new file mode 100644 index 0000000000..488cebc102 --- /dev/null +++ b/proposals/0475-observed.md @@ -0,0 +1,529 @@ +# Transactional Observation of Values + +* Proposal: [SE-0475](0475-observed.md) +* Authors: [Philippe Hausler](https://github.com/phausler) +* Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) +* Status: **Active Review (Apr 10...24)** +* Implementation: https://github.com/swiftlang/swift/pull/79817 +* Review: ([pitch](https://forums.swift.org/t/pitch-transactional-observation-of-values/78315)) + +## Introduction + +Observation was introduced to add the ability to observe changes in graphs of +objects. The initial tools for observation afforded seamless integration into +SwiftUI, however aiding SwiftUI is not the only intent of the module - it is +more general than that. This proposal describes a new safe, ergonomic and +composable way to observe changes to models using an AsyncSequence, starting +transactions at the first willSet and then emitting a value upon that +transaction end at the first point of consistency by interoperating with +Swift Concurrency. + +## Motivation + +Observation was designed to allow future support for providing an `AsyncSequence` +of values, as described in the initial [Observability proposal](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0395-observability.md). +This follow-up proposal offers tools for enabling asynchronous sequences of +values, allowing non-SwiftUI systems to have the same level of "just-the-right-amount-of-magic" +as when using SwiftUI. + +Numerous frameworks in the Darwin SDKs provide APIs for accessing an +`AsyncSequence` of values emitted from changes to a property on a given model +type. For example, DockKit provides `trackingStates` and Group Activities +provides `localParticipantStates`. These are much like other APIs that provide +`AsyncSequence` from a model type; they hand crafted to provide events from when +that object changes. These manual implementations are not trivial and require +careful book-keeping to get right. In addition, library and application code +faces the same burden to use this pattern for observing changes. Each of these +uses would benefit from having a centralized and easy mechanism to implement +this kind of sequence. + +Observation was built to let developers avoid the complexity inherent when +making sure the UI is updated upon value changes. For developers using SwiftUI +and the `@Observable` macro to mark their types, this principle is already +realized; directly using values over time should mirror this ease of use, +providing the same level of power and flexibility. That model of tracking changes +by a graph allows for perhaps the most compelling part of Observation; it +can track changes by utilizing naturally written Swift code that is written just +like the logic of other plain functions. In practice that means that any solution +will also follow that same concept even for disjoint graphs that do not share +connections. The solution will allow for iterating changed values for applications +that do not use UI as seamlessly as those that do. + +## Proposed solution + +This proposal adds a straightforward new tool: a closure-initialized `Observed` +type that acts as a sequence of closure-returned values, emitting new values +when something within that closure changes. + +This new type makes it easy to write asynchronous sequences to track changes +but also ensures that access is safe with respect to concurrency. + +The simple `Person` type declared here will be used for examples in the +remainder of this proposal: + +```swift +@Observable +final class Person { + var firstName: String + var lastName: String + + var name: String { firstName + " " + lastName } + + init(firstName: String, lastName: String) { + self.firstName = firstName + self.lastName = lastName + } +} +``` + +Creating an `Observed` asynchronous sequence is straightforward. This example +creates an asynchronous sequence that yields a value every time the composed +`name` property is updated: + +```swift +let names = Observed { person.name } +``` + +However if the example was more complex and the `Person` type in the previous +example had a `var pet: Pet?` property which was also `@Observable` then the +closure can be written with a more complex expression. + +```swift +let greetings = Observed { + if let pet = person.pet { + return "Hello \(person.name) and \(pet.name)" + } else { + return "Hello \(person.name)" + } +} +``` + +In that example it would track both the assignment of a new pet and then consequently +that pet's name. + +## Detailed design + +There a few behaviors that are prerequisites to understanding the requirements +of the actual design. These two key behaviors are how the model handles tearing +and how the model handles sharing. + +Tearing is where a value that is expected to be assigned as a singular +transactional operation can potentially be observed in an intermediate and +inconsistent state. The example `Person` type shows this when a `firstName` is +set and then the `lastName` is set. If the observation was triggered just on the +trailing edge (the `didSet` operation) then an assignment to both properties +would garner an event for both properties and potentially get an inconsistent +value emitted from `name`. Swift has a mechanism for expressing the grouping of +changes together: isolation. When an actor or an isolated type is modified it is +expected (enforced by the language itself) to be in a consistent state at the +next suspension point. This means that if we can utilize the isolation that is +safe for the type then the suspensions on that isolation should result in safe +(and non torn values). This means that the implementation must be transactional +upon that suspension; starting the transaction on the first trigger of a leading +edge (the `willSet`) and then completing the transaction on the next suspension +of that isolation. + +The simple example of tearing would work as the following: + +```swift +let person = Person(firstName: "", lastName: "") +// willSet \.firstName - start a transaction +person.firstName = "Jane" +// didSet \.firstName +// willSet \.lastName - the transaction is still dirty +person.lastName = "Appleseed" +// didSet \.lastName +// the next suspension the `name` property will be valid +``` + +Suspensions are any point where a task can be calling out to something where +they `await`. Swift concurrency enforces safety around these by making sure that +isolation is respected. Any time a function has a suspension point data +associated with the type must be ready to be read by the definitions of actor +isolation. In the previous example of the `Person` instance the `firstName` and +`lastName` properties are mutated together in the same isolation, that means +that no other access in that isolation can read those values when they are torn +without the type being `Sendable` (able to be read from multiple isolations). +That means that in the case of a non-`Sendable` type the access must be +constrained to an isolation, and in the `Sendable` cases the mutation is guarded +by some sort of mechanism like a lock, In either case it means that the next +time one can read a safe value is on that same isolation of the safe access to +start with and that happens on that isolations next suspension. + +Observing at the next suspension point means that we can also address the second +issue too; sharing. The expectation of observing a property from a type as an +AsyncSequence is that multiple iterations of the same sequence from multiple +tasks will emit the same values at the same iteration points. The following code +is expected to emit the same values in both tasks. + +```swift + +let names = Observed { person.firstName + " " + person.lastName } + +Task.detached { + for await name in names { + print("Task1: \(name)") + } +} + +Task.detached { + for await name in names { + print("Task2: \(name)") + } +} +``` + +In this case both tasks will get the same values upon the same events. This can +be achieved without needing an extra buffer since the suspension of each side of +the iteration are continuations resuming all together upon the accessor's +execution on the specified isolation. This facilitates subject-like behavior +such that the values are sent from the isolation for access to the iteration's +continuation. + +The previous initialization using the closure is a sequence of values of the computed +properties as a `String`. This has no sense of termination locally within the +construction. Making the return value of that closure be a lifted `Optional` suffers +the potential conflation of a terminal value and a value that just happens to be nil. +This means that there is a need for a second construction mechanism that offers a +way of expressing that the `Observed` sequence iteration will run until finished. + +For the example if `Person` then has a new optional field of `homePage` which +is an optional URL it then means that the construction can disambiguate +by returning the iteration as the `next` value or the `finished` value. + +``` +@Observable +final class Person { + var firstName: String + var lastName: String + var homePage: URL? + + var name: String { firstName + " " + lastName } + + init(firstName: String, lastName: String) { + self.firstName = firstName + self.lastName = lastName + } +} + +let hosts = Observed.untilFinished { [weak person] in + if let person { + .next(person.homePage?.host) + } else { + .finished + } +} +``` + +Putting this together grants a signature as such: + +```swift +public struct Observed: AsyncSequence, Sendable { + public init( + @_inheritActorContext _ emit: @escaping @isolated(any) @Sendable () throws(Failure) -> Element + ) + + public enum Iteration: Sendable { + case next(Element) + case finished + } + + public static func untilFinished( + @_inheritActorContext _ emit: @escaping @isolated(any) @Sendable () throws(Failure) -> Iteration + ) -> Observed +} +``` + +Picking the initializer apart first captures the current isolation of the +creation of the `Observed` instance. Then it captures a `Sendable` closure that +inherits that current isolation. This means that the closure may only execute on +the captured isolation. That closure is run to determine which properties are +accessed by using Observation's `withObservationTracking`. So any access to a +tracked property of an `@Observable` type will compose for the determination of +which properties to track. + +The closure is not run immediately it is run asynchronously upon the first call +to the iterator's `next` method. This establishes the first tracking state for +Observation by invoking the closure inside a `withObservationTracking` on the +implicitly specified isolation. Then upon the first `willSet` it will enqueue on +to the isolation a new execution of the closure and finishing the transaction to +prime for the next call to the iterator's `next` method. + +The closure has two other features that are important for common usage; firstly +the closure is typed-throws such that any access to that emission closure will +potentially throw an error if the developer specifies. This allows for complex +composition of potentially failable systems. Any thrown error will mean that the +`Observed` sequence is complete and loops that are currently iterating will +terminate with that given failure. Subsequent calls then to `next` on those +iterators will return `nil` - indicating that the iteration is complete. + +## Behavioral Notes + +There are a number of scenarios of iteration that can occur. These can range from production rate to iteration rate differentials to isolation differentials to concurrent iterations. Enumerating all possible combinations is of course not possible but the following explanations should illustrate some key usages. `Observed` does not make unsafe code somehow safe - the concepts of isolation protection or exclusive access are expected to be brought to the table by the types involved. It does however require the enforcements via Swift Concurrency particularly around the marking of the types and closures being required to be `Sendable`. The following examples will only illustrate well behaved types and avoid fully unsafe behavior that would lead to crashes because the types being used are circumventing that language safety. + +The most trivial case is where a single produce and single consumer are active. In this case they both are isolated to the same isolation domain. For ease of reading; this example is limited to the `@MainActor` but could just as accurately be represented in some other actor isolation. + +```swift +@MainActor +func iterate(_ names: Observed) async { + for await name in names { + print(name) + } +} + +@MainActor +func example() async throws { + let person = Person(firstName: "", lastName: "") + + // note #2 + let names = Observed { + person.name + } + + Task { + await iterate(names) + } + + for i in 0..<5 { + person.firstName = "\(i)" + person.lastName = "\(i)" + try await Task.sleep(for: .seconds(0.1)) // note #1 + } +} + +try await example() + +``` + +The result of the observation will print the following output. + +``` +0 0 +1 1 +2 2 +3 3 +4 4 +``` + +The values are by the virtue of the suspension at `note #1` are all emitted, the first name and last name are conjoined because they are both mutated before the suspension. The type `Person` does not need to be `Sendable` because `note #2` is implicitly picking up the `@MainActor` isolation of the enclosing isolation context. That isolation means that the person is always safe to access in that scope. + +Next is the case where the mutation of the properties out-paces the iteration. Again the example is isolated to the same domain. + +```swift +@MainActor +func iterate(_ names: Observed) async { + for await name in names { + print(name) + try? await Task.sleep(for: .seconds(0.095)) + } +} + +@MainActor +func example() async throws { + let person = Person(firstName: "", lastName: "") + + // @MainActor is captured here as the isolation + let names = Observed { + person.name + } + + Task { + await iterate(names) + } + + for i in 0..<5 { + person.firstName = "\(i)" + person.lastName = "\(i)" + try await Task.sleep(for: .seconds(0.1)) + } +} + +try await example() + +``` + +The result of the observation may print the following output, but the primary property is that the values are conjoined to the same consistent view. It is expected that some values may not be represented during the iteration because the transaction has not yet been handled by the iteration. + +``` +0 0 +1 1 +2 2 +3 3 +``` + +This case dropped the last value of the iteration because the accumulated differential exceeded the production; however the potentially confusing part here is that the sleep in the iterate competes with the scheduling in the emitter. This becomes clearer of a relationship when the boundaries of isolation are crossed. + +Observed can be used across boundaries of concurrency. This is where the iteration is done on a different isolation than the mutations. The types however are accessed always in the isolation that the creation of the Observed closure is executed. This means that if the `Observed` instance is created on the main actor then the subsequent calls to the closure will be done on the main actor. + +```swift +@globalActor +actor ExcplicitlyAnotherActor: GlobalActor { + static let shared = ExcplicitlyAnotherActor() +} + +@ExcplicitlyAnotherActor +func iterate(_ names: Observed) async { + for await name in names { + print(name) + } +} + +@MainActor +func example() async throws { + let person = Person(firstName: "", lastName: "") + + // @MainActor is captured here as the isolation + let names = Observed { + person.name + } + + Task.detached { + await iterate(names) + } + + for i in 0..<5 { + person.firstName = "\(i)" + person.lastName = "\(i)" + try await Task.sleep(for: .seconds(0.1)) + } +} + +``` + +The values still will be conjoined as expected for their changes, however just like the out-paced case there is a potential in which an alteration may slip between the isolations and only a subsequent value is represented during the iteration. However since is particular example has no lengthy execution (greater than 0.1 seconds) it means that it does not get out paced by production and returns all values. + +``` +0 0 +1 1 +2 2 +3 3 +4 4 +``` + +If the `iterate` function was altered to have a similar `sleep` call that exceeded the production then it would result in similar behavior of the previous producer/consumer rate case. + +The next behavioral illustration is the value distribution behaviors; this is where two or more copies of an `Observed` are iterated concurrently. + +```swift + +@MainActor +func iterate1(_ names: Observed) async { + for await name in names { + print("A", name) + } +} + + +@MainActor +func iterate2(_ names: Observed) async { + for await name in names { + print("B", name) + } +} + +@MainActor +func example() async throws { + let person = Person(firstName: "", lastName: "") + + // @MainActor is captured here as the isolation + let names = Observed { + person.name + } + + Task.detached { + await iterate1(names) + } + + Task.detached { + await iterate2(names) + } + + for i in 0..<5 { + person.firstName = "\(i)" + person.lastName = "\(i)" + try await Task.sleep(for: .seconds(0.1)) + } +} + +try await example() +``` + +This situation commonly comes up when the asynchronous sequence is stored as a property of a type. By vending these as a shared instance to a singular source of truth it can provide both a consistent view and reduce overhead for design considerations. However when the sequences are then combined with other isolations the previous caveats come in to play. + +``` +A 0 0 +B 1 1 +A 1 1 +B 2 2 +A 2 2 +A 3 3 +B 3 3 +A 4 4 +B 4 4 +``` + +The same rate commentary applies here as before but an additional wrinkle is that the delivery between the A and B sides is non-determinstic (in some cases it can deliver as A then B and other cases B then A). + +## Effect on ABI stability & API resilience + +This provides no alteration to existing APIs and is purely additive. However it +does have a few points of interest about future source compatibility; namely +the initializer does ferry the inherited actor context as a parameter and if +in the future Swift develops a mechanism to infer this without a user +overridable parameter then there may be a source breaking ambiguity that would +need to be disambiguated. + +## Notes to API authors + +This proposal does not change the fact that the spectrum of APIs may range from +favoring `AsyncSequence` properties to purely `@Observable` models. They both +have their place. However the calculus of determining the best exposition may +be slightly more refined now with `Observed`. + +If a type is representative of a model and is either transactional in that +some properties may be linked in their meaning and would be a mistake to read +in a disjoint manner (the tearing example from previous sections), or if the +model interacts with UI systems it now more so than ever makes sense to use +`@Observable` especially with `Observed` now as an option. Some cases may have +previously favored exposing those `AsyncSequence` properties and would now +instead favor allowing the users of those APIs compose things by using `Observed`. +The other side of the spectrum will still exist but now is more strongly +relegated to types that have independent value streams that are more accurately +described as `AsyncSequence` types being exposed. The suggestion for API authors +is that now with `Observed` favoring `@Observable` perhaps should take more +of a consideration than it previously did. + +## Alternatives Considered + +Both initialization mechanisms could potentially be collapsed into an optional, +however that creates potential ambiguity of valid nil elements versus termination. + +There have been many iterations of this feature so far but these are some of the +highlights of alternative mechanisms that were considered. + +Just expose a closure with `didSet`: This misses the mark with regards to concurrency +safety but also faces a large problem with regards to transactionality. This would also +be out sync with the expected behavior of existing observation uses like SwiftUI. +The one benefit of that approach is that each setter call would have a corresponding +callback and would be more simple to implement with the existing infrastructure. It +was ultimately rejected because that would fall prey to the issue of tearing and +the general form of composition was not as ergonomic as other solutions. + +Expose an AsyncSequence based on `didSet`: This also falls to the same issues with the +closure approach except is perhaps slightly more ergonomic to compose. This was also +rejected due to the tearing problem stated in the proposal. + +Expose an AsyncSequence property extension based on `KeyPath`: This could be adapted +to the `willSet` and perhaps transactional models, but faces problems when attempting +to use `KeyPath` across concurrency domains (since by default they are not Sendable). +The implementation of that approach would require considerable improvement to handling +of `KeyPath` and concurrency (which may be an optimization path that could be considered +in the future if the API merits it). As it stands however the `KeyPath` approach in +comparison to the closure initializer is considerably less easy to compose. + +The closure type passed to the initializer does not absolutely require @Sendable in the +cases where the initialization occurs in an isolated context, if the initializer had a +parameter of an isolation that was non-nullable this could be achieved for that restriction +however up-coming changes to Swift's Concurrency will make this approach less appealing. +If this route would be taken it would restrict the potential advanced uses cases where +the construction would be in an explicitly non-isolated context. From 89c39cbaa68b7687ec0caab527868638f354c249 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Thu, 10 Apr 2025 21:57:26 -0400 Subject: [PATCH 4215/4563] Link review thread for SE-0475 (#2786) --- proposals/0475-observed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0475-observed.md b/proposals/0475-observed.md index 488cebc102..2df388465f 100644 --- a/proposals/0475-observed.md +++ b/proposals/0475-observed.md @@ -5,7 +5,7 @@ * Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) * Status: **Active Review (Apr 10...24)** * Implementation: https://github.com/swiftlang/swift/pull/79817 -* Review: ([pitch](https://forums.swift.org/t/pitch-transactional-observation-of-values/78315)) +* Review: ([pitch](https://forums.swift.org/t/pitch-transactional-observation-of-values/78315)) ([review](https://forums.swift.org/t/se-0475-transactional-observation-of-values/79224)) ## Introduction From 8f1c1e3efd9223fc3264ba2562d5d3568b8d3de6 Mon Sep 17 00:00:00 2001 From: David Catmull Date: Fri, 11 Apr 2025 09:12:45 -0600 Subject: [PATCH 4216/4563] [ST-0010] Add ConditionTrait.evaluate() (#2740) * Proposal for ConditionTrait.evaluate(), moved from swift-testing * Include "extension ConditionTrait" in code exerpts * Remove mentions of EvaluationResult, since it now uses Bool * Add myself as review manager * Note the associated issue * Assign proposal ST number 0010 and update state to Active Review --------- Co-authored-by: Stuart Montgomery --- proposals/testing/0010-evaluate-condition.md | 65 ++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 proposals/testing/0010-evaluate-condition.md diff --git a/proposals/testing/0010-evaluate-condition.md b/proposals/testing/0010-evaluate-condition.md new file mode 100644 index 0000000000..d490a4b5ba --- /dev/null +++ b/proposals/testing/0010-evaluate-condition.md @@ -0,0 +1,65 @@ +# Public API to evaluate ConditionTrait + +* Proposal: [ST-0010](0010-evaluate-condition.md) +* Authors: [David Catmull](https://github.com/Uncommon) +* Review Manager: [Stuart Montgomery](https://github.com/stmontgomery) +* Status: **Active review (April 11...April 22, 2025)** +* Bug: [swiftlang/swift-testing#903](https://github.com/swiftlang/swift-testing/issues/903) +* Implementation: [swiftlang/swift-testing#909](https://github.com/swiftlang/swift-testing/pull/909) +* Review: ([pitch](https://forums.swift.org/t/pitch-introduce-conditiontrait-evaluate/77242)) + +## Introduction + +This adds an `evaluate()` method to `ConditionTrait` to evaluate the condition +without requiring a `Test` instance. + +## Motivation + +Currently, the only way a `ConditionTrait` is evaluated is inside the +`prepare(for:)` method. This makes it difficult for third-party libraries to +utilize these traits because evaluating a condition would require creating a +dummy `Test` to pass to that method. + +## Proposed solution + +The proposal is to add a `ConditionTrait.evaluate()` method which returns the +result of the evaluation. The existing `prepare(for:)` method is updated to call +`evaluate()` so that the logic is not duplicated. + +## Detailed design + +The `evaluate()` method is as follows, containing essentially the same logic +as was in `prepare(for:)`: + +```swift +extension ConditionTrait { + /// Evaluate this instance's underlying condition. + /// + /// - Returns: The result of evaluating this instance's underlying condition. + /// + /// The evaluation is performed each time this function is called, and is not + /// cached. + public func evaluate() async throws -> Bool +} +``` + +## Source compatibility + +This change is purely additive. + +## Integration with supporting tools + +This change allows third-party libraries to apply condition traits at other +levels than suites or whole test functions, for example if tests are broken up +into smaller sections. + +## Future directions + +This change seems sufficient for third party libraries to make use of +`ConditionTrait`. Changes for other traits can be tackled in separate proposals. + +## Alternatives considered + +Exposing `ConditionTrait.Kind` and `.kind` was also considered, but it seemed +unnecessary to go that far, and it would encourage duplicating the logic that +already exists in `prepare(for:)`. From 94fd1f5786d7f960c118737ab18bef06408591e6 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Fri, 11 Apr 2025 10:29:31 -0500 Subject: [PATCH 4217/4563] Link to review topic (#2787) --- proposals/testing/0010-evaluate-condition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0010-evaluate-condition.md b/proposals/testing/0010-evaluate-condition.md index d490a4b5ba..577b991d3b 100644 --- a/proposals/testing/0010-evaluate-condition.md +++ b/proposals/testing/0010-evaluate-condition.md @@ -6,7 +6,7 @@ * Status: **Active review (April 11...April 22, 2025)** * Bug: [swiftlang/swift-testing#903](https://github.com/swiftlang/swift-testing/issues/903) * Implementation: [swiftlang/swift-testing#909](https://github.com/swiftlang/swift-testing/pull/909) -* Review: ([pitch](https://forums.swift.org/t/pitch-introduce-conditiontrait-evaluate/77242)) +* Review: ([pitch](https://forums.swift.org/t/pitch-introduce-conditiontrait-evaluate/77242)) ([review](https://forums.swift.org/t/st-0010-public-api-to-evaluate-conditiontrait/79232)) ## Introduction From 8fdd34add87beabe36fb533837dc80d193852820 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Fri, 11 Apr 2025 08:51:38 -0700 Subject: [PATCH 4218/4563] Add `@abi` proposal (#2768) * Add `@abi` proposal * Take corrections from Ben Rimmington Co-authored-by: Ben Rimmington * Assign SE-0476 and put into active review. --------- Co-authored-by: Ben Rimmington Co-authored-by: Holly Borla --- proposals/0476-abi-attr.md | 946 +++++++++++++++++++++++++++++++++++++ 1 file changed, 946 insertions(+) create mode 100644 proposals/0476-abi-attr.md diff --git a/proposals/0476-abi-attr.md b/proposals/0476-abi-attr.md new file mode 100644 index 0000000000..a4e409cd26 --- /dev/null +++ b/proposals/0476-abi-attr.md @@ -0,0 +1,946 @@ +# Controlling the ABI of a function, initializer, property, or subscript + +* Proposal: [SE-0476](0476-abi-attr.md) +* Authors: [Becca Royal-Gordon](https://github.com/beccadax) +* Review Manager: [Holly Borla](https://github.com/hborla) +* Status: **Active Review (April 11 - April 25, 2025)** +* Implementation: behind experimental feature `ABIAttribute` (refinements in [swiftlang/swift#80383](https://github.com/swiftlang/swift/pull/80383)) +* Review: ([pitch](https://forums.swift.org/t/pitch-controlling-the-abi-of-a-declaration/75123)) + +## Introduction + +We propose introducing the `@abi` attribute, which provides an alternate +version of the declaration used for name mangling. This feature would allow +developers of ABI-stable libraries to make minor changes, such as changing +the sendability of a parameter or renaming a declaration (so long as source +compatibility is preserved in a backwards-deployable way), without requiring +deep knowledge of compiler implementation details. + +## Motivation + +Maintainers of ABI-stable libraries sometimes need to update or correct +existing declarations for various reasons: + +1. To adopt new language features, like changing `@Sendable` to `sending`, + in an existing declaration. + +2. To replace an existing declaration with a source-compatible but ABI-breaking + equivalent, like replacing a `rethrows` method with one using typed + `throws`. + +3. To correct a mistake, like removing an unnecessary `@escaping` attribute or + adding a `Sendable` generic constraint. + +4. To rename an API whose name is felt to be catastrophically confusing. + +Many revisions will cause fundamental changes in how an API will be used at the +machine code level that clients must account for; for instance, changing `` +to `` requires callers to generate code that will pass the witness +table for `T`'s `Hashable` conformance. However, some features are designed to +have little or no impact on the code generated by the caller. For example, +these two declarations: + +```swift +// `T` must be `Sendable` +func fn(_: T) {} + +// `T` parameter must be `sending` +func fn(_: borrowing sending T) {} // note: 'borrowing sending' is currently banned, + // pending a decision on whether it should have the + // meaning we want it to have here +``` + +Have identical parameter signatures at the IR level, with one pointer to the +argument and another pointer to `T`'s value witness table, and use the same +result type and calling convention too: + +```text +define hidden swiftcc void @"$s4main2fnyyxs8SendableRzlF"(ptr noalias %0, ptr %T) + ^~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~ +define hidden swiftcc void @"$s4main2fnyyxlF"(ptr noalias %0, ptr %T) + ^~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~ +``` + +Other details, such as the parameter ownership conventions, also line up to +make this work; suffice it to say, the function generated when you use +`borrowing sending` is perfectly capable of handling the arguments passed by +callers that think the parameter is `Sendable`. The only differences between +them are the compile-time checks applied by the compiler and the part of their +mangled names that indicates the feature being used: + +```text +define hidden swiftcc void @"$s4main2fnyyxs8SendableRzlF"(ptr noalias %0, ptr %T) + ^~~~~~~~~~~~~ 'T: Sendable' +define hidden swiftcc void @"$s4main2fnyyxlF"(ptr noalias %0, ptr %T) + ^ 'T' ('sending' is not indicated by the mangled name) +``` + +Thus, if there was a way to tell the compiler to continue using the mangled +name for `fn(_: T)`, a library designer could actually change the +declaration to be treated like `fn(_: borrowing sending T)` when compiling +with the new version of the library *without* breaking ABI compatibility. + +This is part of how the `@preconcurrency` attribute works. `@preconcurrency` +has two effects: It instructs the type checker to permit Swift 5 code to use +the declaration in ways that would violate the rules of certain concurrency +annotations, and it causes those annotations to be omitted from the +declaration's mangled name. That makes it perfect for retrofitting sendability +checking onto APIs that were created before Swift Concurrency was introduced. +However, it is designed specifically for that exact task, which makes it +inflexible: It cannot be used to suppress some concurrency features but not +others (for instance, to amend a mistake in one parameter without affecting +other parts of the declaration), and it cannot be applied to adopt +non-concurrency features which have the same property of being ABI-compatible +except for a different mangled name. + +For everything else, there's the compiler-internal `@_silgen_name` attribute. +`@_silgen_name` is an internal hack that overrides name mangling at specific +points in the compiler, replacing the mangled name with an arbitrary string. +If you know the original mangled name, therefore, you can use this attribute +to keep that name stable even if the declaration has evolved enough that it +would normally use a different name. That makes `@_silgen_name` enormously +flexible—it can be used to handle an arbitrary set of changes, and the +standard library uses it extensively for this purpose. For example, when the +standard library introduced a new `Collection.map(_:)` that used typed +`throws` in [SE-0413][], it continued to support clients expecting the old +`rethrows`-based `map` by using a `@_silgen_name` hack and +`@usableFromInline internal`: + +```swift +extension Collection { + // New `map(_:)` using typed `throws`: + @inlinable + @backDeployed(...) // slight lie, but that's irrelevant here + public func map( + _ transform: (Element) throws(E) -> T + ) throws(E) -> [T] { + // ...actual implementation of `map` omitted... + } + + // Wrapper with the same ABI as the old `map(_:)` which used `rethrows`: + @_silgen_name("$sSlsE3mapySayqd__Gqd__7ElementQzKXEKlF") + // ^-- func map<$T>(_: (Self.Element) throws -> $T) rethrows -> Swift.Array<$T> + // in Swift.Collection extension from module Swift + @usableFromInline + func __rethrows_map( + _ transform: (Element) throws -> T + ) throws -> [T] { // 'throws' and 'rethrows' have the same ABI + try map(transform) // calls through to the new `map(_:)` + } +} +``` + +This creates a declaration which is written in Swift source code as +`__rethrows_map(_:)`, but which has the mangled name of a function named +`map(_:)`. When a module is compiled against this new version of the standard +library, calls to `map(_:)` will use the new method directly; if a module is +compiled against an older standard library, though, it will end up calling the +`__rethrows_map(_:)` compatibility wrapper instead. + +Although it is a powerful tool, `@_silgen_name` has its own set of serious +drawbacks: + +* It has absolutely no compile-time safety checking. +* It works only with functions and is incompatible with certain function + features like opaque return types and `@backDeployed`.[1] +* It requires deep knowledge of the name mangling and calling convention to use + correctly. + +In practice, you basically need to be a Swift compiler or runtime engineer to +use it correctly. For this reason `@_silgen_name` has never been proposed to +Swift Evolution or recommended for general use. + +Library maintainers need a tool that is much more flexible than +`@preconcurrency` but also much safer and more ergonomic than `@_silgen_name`. + +> *[1] This is because the name mangling has facilities to create multiple +> symbols that are all related to the same declaration, but `@_silgen_name` +> only provides an override for the name of the main symbol. Any declaration +> that requires more than one symbol—such as a type declaration, a function +> with an opaque return type, or a function with a back-deployment +> thunk—would have no way to generate a mangled name for these additional +> symbols.* + + [SE-0413]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0413-typed-throws.md#effect-on-abi-stability + +## Proposed solution + +We propose a new attribute, called `@abi`, which specifies an alternate +declaration that provides its ABI for name mangling purposes. This alternate +declaration is enclosed within the argument parentheses; it has no body or +initializer expression but is otherwise a syntactically complete declaration. + +For example, the `@_silgen_name`-using `__rethrows_map(_:)` method shown in the +Motivation section could be written much more clearly by using `@abi`: + +```swift +extension Collection { + // Wrapper with the same ABI as the old `map(_:)` which used `rethrows`: + @abi( + func map( + _ transform: (Element) throws -> T + ) rethrows -> [T] + ) + @usableFromInline + func __rethrows_map( + _ transform: (Element) throws -> T + ) throws -> [T] { // 'throws' and 'rethrows' have the same ABI + try map(transform) // calls through to the new `map(_:)` + } +} +``` + +Notice how the `@abi` attribute basically contains the original version of the +declaration. When Swift is performing name mangling, this declaration is what +it will use; for all other functions, it will use the outer `__rethrows_map` +declaration. In particular, the `map(_:)` call in the body doesn't get resolved +to the `map(_:)` function in the `@abi` attribute; it looks for other +implementations and eventually finds the new typed-throws `map(_:)`. + +What's more, the ABI declaration can be checked against the original one to +make sure they're compatible. For example, at the ABI level `throws` and +`rethrows` are interchangeable, but a non-`throws`/`rethrows` method handles +its return values differently from them. If the maintainer accidentally dropped +the `throws` effect while implementing this function, the compiler would +complain about the mismatch: + +```swift +extension Collection { + @abi( + func map( + _ transform: (Element) throws -> T + ) rethrows -> [T] // error: 'rethrows' doesn't match API + ) + @usableFromInline + func __rethrows_map( + _ transform: (Element) throws -> T + ) -> [T] { // Whoops, should be 'throws' or 'rethrows'! + try map(transform) + } +} +``` + +This checking also makes sure that the details specified in the `@abi` +attribute are actually relevant. For example, the `@abi` attribute +automatically inherits the access control, availability, and `@objc`-ness of +the API it's attached to, so these are omitted from the `@abi` attribute. +Default arguments, too, are left out because they're irrelevant to ABI. The +compiler will diagnose this unnecessary information and suggest removing it. + +All sorts of precision changes are possible. Here's another use for a +`@_silgen_name` hack in the standard library: The maintainers discovered a data +race safety bug in an API that had already shipped and needed to add an +`@Sendable` attribute to prevent it, but `@preconcurrency` alone would have +also suppressed the `@Sendable` attribute on the parameter that had been +correctly annotated. `@abi` makes it easy to fix this sort of problem: + +```swift +public struct AsyncStream { + // ...other declarations omitted... + + @abi( + init( + unfolding produce: @escaping /* not @Sendable */ () async -> Element?, + onCancel: (@Sendable () -> Void)? = nil + ) + ) + @preconcurrency + public init( + unfolding produce: @escaping @Sendable () async -> Element?, + onCancel: (@Sendable () -> Void)? = nil + ) { + // Implementation omitted + } +} +``` + +Because `@preconcurrency` is applied to the outer declaration, but not to the +one inside the `@abi` attribute, its typechecking effects will be applied +(improving source compatibility for code written before the second `@Sendable` +was added) but its name mangling effects will not (keeping the mangled name +stable to preserve ABI compatibility). + +This feature goes beyond what `@_silgen_name` could do, however. For example, +it can be applied to `var` and `let` declarations: + +```swift +@abi(var oldName: Int) +public var newName: Int +``` + +The mangled name of an accessor includes the mangled name of the variable or +subscript it belongs to; thanks to `@abi`, the accessors for this variable will +have `oldName` mangled into their names. + +### Supported changes (and unsupported uses of them) + +This feature can be used to override the mangling of a declaration's: + +* Name, argument labels, and (for unary operator functions) fixity (`prefix` + vs. `postfix`) + +* Preconcurrency status, actor isolation (where this does not affect calling + convention), and execution environment + +* Generic constraints to marker protocols (`BitwiseCopyable`, `Copyable`, + `Escapable`, `Sendable`) + +* Certain aspects of parameter and `self` behavior (variadic (vs. `Array`); + `@autoclosure`; `sending`; ownership specifiers as long as the behavior is + compatible) + +* Certain aspects of argument, result, and thrown types (marker protocols in + existentials; tuple element labels; `@escaping`, `@Sendable`, and `sending` + results on closures) + +Note that some of these changes relate to safety properties of your code, such +as data race safety and escapability. When you use `@abi` to maintain ABI +compatibility with older versions of your library while tightening safety +constraints for new clients, you must take special care to remember that +clients compiled without those changes may violate the new constraints. In +practice, this means that you should probably only use `@abi` to make +retroactive changes to safety constraints when you know that violating the +constraint was *always* unsafe and it simply wasn't enforced until now. + +For instance, the `AsyncStream.init(unfolding:onCancel:)` example above adds +`@Sendable` to a closure parameter that previously didn't have the attribute. +This is appropriate because the closure was *always* run concurrently; code +that passed a non-`@Sendable` closure was already buggy, so this change merely +made the bug easier to detect. It would have been inappropriate if the closure +was originally run synchronously and was changed to run concurrently, because +code that previously worked fine would now have new data races. + +(`@abi` can still be used to help implement behavior changes, but the pattern +is different: you make the original version `@usableFromInline internal` and +change its API name to something you won't use by accident, applying `@abi` to +keep its mangled name the same as always. Then you create a new declaration +with the old API name and the behavior changes, using `@backDeployed` to ensure +that new binaries can interoperate with old versions of your library. +`__rethrows_map(_:)` is a good example of this pattern.) + +In short: Much like when `@inlinable` is used, **it is the developer's +responsibility to ensure that the current behavior of the declaration is +compatible with clients built against older versions of it. The compiler +doesn't understand the history of your codebase and cannot detect some +mistakes.** + +## Detailed design + +### Grammar + +An `@abi` attribute's argument list must have exactly one argument, which in +this proposal must be one of the following productions: + +* *function-declaration* +* *initializer-declaration* +* *constant-declaration* +* *variable-declaration* +* *subscript-declaration* + +This argument must *not* include any of the following sub-productions: + +* *code-block* +* *getter-setter-block* +* *getter-setter-keyword-block* +* *willSet-didSet-block* +* *initializer* (initial value expression) + +To that end, we amend the following productions in the Swift grammar to make +code blocks optional: + +```diff + initializer-declaration → initializer-head generic-parameter-clause? + parameter-clause async? throws-clause? +- generic-where-clause? initializer-body ++ generic-where-clause? initializer-body? + + initializer-declaration → initializer-head generic-parameter-clause? + parameter-clause async? 'rethrows' +- generic-where-clause? initializer-body ++ generic-where-clause? initializer-body? + + subscript-declaration → subscript-head subscript-result generic-where-clause? +- code-block ++ code-block? +``` + +We don't need to worry about ambiguity in terminating these productions because +the block-less forms always occur in `@abi` attributes; its closing parenthesis serves as a terminator for the declaration. + +> **Note**: If future development of the `@abi` attribute requires additional +> information to be added to it, this can be done by adding new productions at +> the beginning of the argument list, terminated by a comma or colon to +> distinguish them from declaration modifiers: +> +> ```swift +> @abi(unchecked, func liveDangerously(_: AnyObject)) // Future direction +> func liveDangerously(_ object: AnyObject?) { ... } +> ``` + +### Terminology and basic concepts + +Syntactically, an `@abi` attribute involves two declarations. The *ABI-only +declaration* is the one in the attribute's argument list; the *API-only +declaration* is the one the attribute is attached to. + +```swift +@abi(func abiOnlyDeclaration()) +func apiOnlyDeclaration() {} +``` + +A declaration which does not involve an `@abi` attribute at all—that is, which +is neither API-only nor ABI-only—is called a *normal declaration*. + +There are two *ABI roles*: + +* An *API-providing declaration* determines the behavior of the declaration in + source code: what name developers write to address it, what constraints and + behaviors are applied at use sites, how it is implemented (its body, + accessors, or members), etc. + +* An *ABI-providing declaration* determines how the declaration affects mangled + symbol names--both its own name and any names derived from it. + +Every declaration has at least one of these roles. Every declaration also has a +*counterpart* which fulfills the roles it does not. When the compiler wants to +compute some aspect of a declaration pertaining to a role that declaration does +not have, it automatically substitutes the declaration's counterpart. + +Roles and counterparts work as follows: + +| Declaration is… | ABI-providing | API-providing | Counterpart | +| --------------- | ------------- | ------------- | ------------------------------------------- | +| Normal | ✅ | ✅ | Is its own counterpart | +| ABI-only | ✅ | | Declaration `@abi` attribute is attached to | +| API-only | | ✅ | Declaration in `@abi` attribute | + +### Declaration checking + +When you use the `@abi` attribute, Swift validates various aspects of the +ABI-providing declaration in light of its API counterpart. An *aspect* is any +way in which the external appearance of a declaration might vary. Attributes +and modifiers are aspects, but so are the declaration's name, its result or +value types, its generic signature (if it has one), its parameter list (if it +has one), its effects (if it has any), and so on. + +#### Aspects with no ABI impact must be omitted + +Many aspects of a declaration only matter for an API-providing declaration; +they're irrelevant on a declaration that's ABI-only. These include: + +* Default arguments for parameters + +* Attributes which only affect compile-time checking or behavior, such as + `@unsafe`, `@discardableResult`, or result builder attributes + +* Certain attributes and modifiers which have ABI effects, but where the + compiler has been designed to inherit the ABI-providing declaration's + behavior from its API counterpart where needed: + + * `@objc` (and its ilk) and `dynamic`, including inference behaviors + * Access control modifiers and `@usableFromInline` + * `@inlinable` and other attributes controlling inlining + * `@available` and `@backDeployed` + * `override` + +These aspects are generally *forbidden* on an ABI-providing declaration. If +they are present, the compiler will diagnose an error and suggest they be +removed. + +In practice, this means that an `@abi` attribute is often significantly shorter +than the declaration it's attached to because it doesn't need to specify as +much information: + +```swift +@abi( + // Same signature as below, except `T` is not `Sendable`. + static + func assumeIsolated( + _ operation: @MainActor () throws -> T, + file: StaticString, + line: UInt + ) rethrows -> T +) +@available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) // not needed in @abi +@usableFromInline // not needed in @abi +internal // not needed in @abi +static +func assumeIsolated( + _ operation: @MainActor () throws -> T, + file: StaticString = #fileID, // default argument not needed in @abi + line: UInt = #line // default argument not needed in @abi +) rethrows -> T { + ... +} +``` + +The intended workflow is that a developer can paste the entire original +declaration into an `@abi` attribute and the compiler will then tell them which +parts of it they should remove. + +#### Call compatibility + +For aspects which *do* have ABI impact, the compiler enforces that the +ABI-providing declaration is *call-compatible* with its API-providing +counterpart. Broadly, "call compatibility" means that, other than the mangled +names, the machine code generated to call the ABI-providing declaration would +be equally able to call its API counterpart. For instance: + +* Declarations are of the same fundamental kind (a `func` for a `func`, a `var` + for a `var`, etc.), so they expose the same basic capabilities and entry + points. + +* Effects match closely enough that the caller and callee will agree on which + stack should be used and which implicit parameters will be passed. + +* Inputs and outputs are passed similarly enough to ensure type and memory + safety, including compatible memory management behavior, and including the + implicit inputs and outputs used for generic parameters, `self`, and + throwing. + +However, call compatibility does *not* require aspects of the declaration +that only change the mangled name and/or compile-time checking to match: + +* Names, argument labels, or other name-like traits of a declaration (such + as the fixity modifiers for operator functions) may vary. + +* Aspects which affect only syntax (and possibly mangling) may vary. For + instance, a regular closure may be used instead of an `@autoclosure`; tuple + types with different element labels may be used; an array parameter may be + used instead of a variadic parameter; ordinary optionals may be used instead + of implicitly-unwrapped optionals; `throws` and `rethrows` may be used + interchangeably. + +* Concurrency safety and lifetime restrictions which don't affect the ability + to call the declaration may vary. For instance, a non-escaping closure may be + used instead of an `@escaping` closure; `sending` modifiers, `Sendable` + constraints, or neither may be used interchangeably so long as memory + management isn't affected; isolation may vary so long as extra data does not + need to be passed; `~Copyable` and `~Escapable` constraints may vary. + +#### Impact on redeclaration checking + +A declaration must have a unique signature in each of its roles. That is, an +API-only declaration is checked against API-providing declarations; an +ABI-only declaration is checked against ABI-providing declarations; a normal +declaration is checked twice, first against API-providing declarations and then +against ABI-providing declarations. + +In general, name lookup will return declarations with the API-providing role +and will ignore declarations with the ABI-providing role. Even when you're +writing an ABI-only declaration, you should use the API names of other +declarations, not the ABI names. + +#### Declaring multiple variables + +When `var` or `let` is used, the ABI-providing declaration must bind the same +number of patterns, each of which has the same number of variables, as its API +counterpart. That is, the first of these is valid, while the others are not: + +```swift +// OK: +@abi(var x, y: Int) +var a, b: Int + +// Mismatched: +@abi(var x, y, z: Int) +var a, b: Int + +// Also mismatched: +@abi(var x, y: Int) +var a: Int, (b1, b2): (Int, Int) + +// Mismatched even though the total adds up: +@abi(var x, y, z: Int) +var a: Int, (b1, b2): (Int, Int) +``` + +An ABI-providing declaration does *not* infer missing types from its API +counterpart. In practice, this means that an ABI-providing declaration +may need to explicitly declare types that its API counterpart infers from an +initial value expression. + +An ABI-providing `var` or `let` does not have a list of accessors or specify +anything about them; in a sense, it can be thought of as inferring its +accessors from its API counterpart. + +### Limitations on feature scope + +#### Supported declaration kinds + +In this proposal, `@abi` may be applied only to `func`, `init`, `var`, `let`, +and `subscript` declarations. Other declarations are less straightforward to +support in various ways; see the future directions section for details. + +#### Language features with auxiliary declarations + +`@abi` can neither contain, nor be applied alongside, `lazy` or a property +wrapper. These features implicitly create auxiliary declarations, and it isn't +clear how those should interact with `@abi`. + +#### Limited support for macros + +Neither attached nor freestanding macros can be used inside an `@abi` +attribute. None of the attached macro roles would be useful since ABI-providing +declarations do not have bodies, members, accessors, or extensions; the +freestanding macro roles, on the other hand, expand to complete declarations, +while some of the future directions involve supporting special stub syntax +which would be incompatible here. + +`@abi` can still be applied *alongside* an attached macro or *to* a +freestanding macro, although in practice many macros will need to handle `@abi` +attributes specially. + +### Non-normative: Precise rules as currently implemented + +To help evaluate how these principles will work in practice, we've listed the +current implementation's rules below. **However, we do not guarantee that the +rules listed here will exactly match the final behavior of the feature.** +Basically, we don't want to put every bug fix through an amendment or every +tiny, straightforward expansion of capabilities through a proposal. + +#### Must be omitted (no ABI impact or inheritance in place) + +* Default arguments on parameters +* Result builder attributes on parameters or declarations +* `@available` +* `@inlinable`, `@inline`, `@backDeployed`, `@usableFromInline`, + `@_alwaysEmitIntoClient`, `@_transparent` +* Objective-C opt-in attributes (`@objc`, `@IBAction`, `@IBDesignable`, + `@IBInspectable`, `@IBOutlet`, `@IBSegueAction`, `@GKInspectable`, + `@NSManaged`, `@nonobjc`) +* `optional` modifier in `@objc` protocols +* `@NSCopying` +* `@_expose` and `@_cdecl` +* `@LLDBDebuggerFunction` +* `dynamic` modifier and `@_dynamicReplacement` +* `@specialize` on functions and initializers +* `override` modifier +* Access control (`open`, `public`, `package`, `internal`, `fileprivate`, + `private`) +* Setter access control (`open(set)`, `public(set)`, `package(set)`, + `internal(set)`, `fileprivate(set)`, `private(set)`) +* `@_spi` and `@_spi_available` +* Reference ownership (`weak`, `unowned`, `unowned(unsafe)`) +* `@warn_unqualified_access` +* `@discardableResult` +* `@implementation` on functions +* `@differentiable`, `@derivative`, `@transpose` +* `@noDerivative` on declarations other than parameters +* `@exclusivity` +* `@safe` and `@unsafe` +* `@abi` +* Unsupported features (`lazy`, property wrapper attributes, attached macro + attributes) + +#### Must be specified and must match + +* Declaration kind (`func`, `var`, etc.) +* `convenience` and `required` modifiers on initializers +* `distributed` modifier +* Result type of functions +* Failability of initializers +* Value type of subscripts and variables +* Number of parameters on functions, initializers, and subscripts +* Parameter types +* `inout` on parameters and `mutating`/`nonmutating` modifiers on members +* `@noDerivative` on parameters +* `@_addressable` on parameters and `@_addressableSelf` on members +* `@lifetime` attributes (NOTE: this probably ought to be "vary with + constraints", but an interaction with `@_addressableForDependencies` needs + to be worked out) +* `async` effect +* Aspects of types which are not listed elsewhere + +#### Allowed to vary, but with constraints + +* `throws` effect and thrown type (`rethrows` is equivalent to `throws`) +* Generic signature of functions, initializers, and subscripts (marker + protocols may vary) +* Variadic parameter types (`T...` and `Array` are treated as equivalent) +* Parameter ownership specifiers and `self` ownership modifiers (ones with + equivalent memory management behavior may be substituted for one another) +* `sending` on parameter and result types (so long as its ownership behavior + is preserved) +* `static`, `class`, and `final` modifiers (`class final` is equivalent to + `static`) +* Actor isolation (other than `@isolated(any)`, which is incompatible with + the others) + +#### Allowed to vary arbitrarily + +* Base names of functions and variables +* Argument labels and parameter names +* `prefix` and `postfix` modifiers on operator functions +* Whether optionals are implicitly unwrapped +* Element labels in tuple types +* `@autoclosure` on parameters +* `@escaping` on closures +* `@Sendable` on closures +* `isolated` on parameters +* `_const` on parameters and variables +* Generic parameter names +* Use of type sugar (e.g. `Optional` and `T?` are equivalent) +* Use of generic types that have a same-type constraint (e.g. in the + presence of `T == Int`, `T` and `Int` are equivalent) +* Use of marker protocols in existential types +* `@Sendable` on functions and initializers +* `@preconcurrency` +* `@execution` +* Aspects of declarations which are not listed elsewhere + +## Source compatibility + +This feature is additive and affects only the ABI. However, many of the changes +that can be effected using it can be source-breaking unless done with care. For +example: + +* When renaming a declaration, make sure there's a declaration with the + original name that can be called in the same situations, and consider using + `@backDeployed` to ensure recompiled clients don't have to raise their + minimum deployment target. + +* When a type changes, make sure that it will either become more broad, or that + you are willing to accept any breakage that results. For example, switching + from a `Sendable` constraint to a `(borrowing) sending` parameter strictly + increases the set of valid callers, so that's probably always okay; switching + from no constraint to a `Sendable` constraint, on the other hand, will break + some callers, but might be acceptable if the missing `Sendable` constraint + created an opportunity for data races. + +## ABI compatibility + +This feature is intended to give libraries additional options to evolve APIs +without breaking the corresponding ABIs. + +We are currently evaluating adoption in `stdlib/public`. So far, it looks like +we can replace all uses of `@_silgen_name` to specify mangled Swift names with +uses of `@abi`, and in some cases remove hacks; this will be roughly 75 +declarations. + +Note that this feature does not subsume the use of `@_silgen_name` with an +arbitrary, C-style symbol name to either declare a function implemented in +C++ using the Swift calling convention, or to generate a symbol that's easy to +access from C-family code or a compiler intrinsic. About 200 of the uses of +`@_silgen_name` in `stdlib/public` are of this type; we expect these to remain +as-is. + +## Implications on adoption + +This feature is intended to help ease the adoption of other new features by +allowing a declaration's ABI to be "pinned" to its original form even as it +continues to evolve. Note that there is only ever a need to specify the +*original* form of the declaration, not any revisions that may have occurred +between then and the current form; there is therefore never a reason you would +need to specify more than one `@abi` attribute, nor to tie an `@abi` attribute +to a specific platform version. + +In module interfaces, the `@abi` attribute is partially suppressible. +Specifically, for `func`s that do not use `@backDeployed` and do not have +opaque result types, the compiler emits a module interface that falls back to +using an equivalent `@_silgen_name` attribute. For other declarations, however, +the compiler falls back to an `@available(*, unavailable)` attribute instead, +with a message indicating that the developer will need a newer compiler to use +the declaration. + +## Future directions + +### Unchecked mode + +There may be situations where a skilled engineer knows that a specific use of +`@abi` is compatible, but the compiler does not know how to prove that. While +that can often be considered a compiler bug—the checker *should* be able to +tell that the code is safe—it may be useful, either as a workaround or to +handle extreme edge cases, to be able to turn off `@abi`'s compatibility +checking: + +```swift +@abi(unchecked, func liveDangerously(_: AnyObject)) +func liveDangerously(_ object: AnyObject?) { ... } +``` + +### Support for types and extensions + +It ought to be possible to use `@abi` with types: + +```swift +@abi(struct Buffer: ~Copyable) +public struct FrameBuffer: ~Copyable { ... } + +@available(*, unavailable, renamed: "FrameBuffer") +@abi(typealias FrameBuffer) // keeps `typealias Buffer` from colliding with `struct Buffer` +typealias Buffer = FrameBuffer +``` + +Here, the library maintainer discovered after shipping that the name `Buffer` +is too vague—clients didn't understand what it meant, and some of them even +had another type named `Buffer`. When they rebuild with the new version of the +library, they will get an error with a fix-it to change `Buffer` to +`FrameBuffer`, but it will still use the name `Buffer` at the ABI level so +that existing binaries don't break. This will apply not only to the type +itself, but also to its members and even to functions with a `FrameBuffer` in +their overload signature. + +Type renaming may create challenges for module interface source stability, +since a module interfaces could refer to a type by an older or newer name than +its current one. It might be possible to address this by making module +interfaces always refer to types by their ABI name. (This should be +non-breaking as long as it's introduced at the same time as `@abi` for types.) + +This could also be used to affect the inference of properties of other +declarations. Consider this example: + +```swift +@abi(protocol Component) +@preconcurrency @MainActor // Added after shipping +public protocol Component { ... } + +extension Component { + public func onEvent(_ handler: @Sendable @escaping () -> Void) -> some Component { ... } +} +``` + +The library maintainer decided after the fact that `Component`s should be +isolated to the main actor, but that broke some Swift 5 code, so they added +`@preconcurrency` for its typechecking effects. However, `@preconcurrency` then +got applied to `onEvent(_:)`, suppressing its `@Sendable` attribute, which +changed its ABI. Using `@abi` on `Component` should override the ABI effects of +`@preconcurrency` not just for `Component` itself, but for every declaration +nested inside it. + +To support this kind of inference, the compiler may need to add an inferred +`@abi` attribute when a declaration with both roles depends on one which +provides only one role. For instance, if a type conforms to a protocol whose +`@abi` attribute specifies different actor isolation or different marker +protocols, Swift may need to add an inferred `@abi` attribute so the type's ABI +will be compatible with the protocol's ABI while its API will be compatible +with the protocol's ABI. + +### Support for enum cases + +It would probably be possible to allow `@abi` to be attached to a `case` +declaration, allowing it to backwards-compatibly rename or otherwise control +the ABI of enum cases. + +### Support for auxiliary declarations + +It might be possible to allow `@abi` to be used with `lazy` and property +wrappers either by coming up with rules to derive an `@abi` attribute for those +declarations, or by creating a syntax that can specify them: + +```swift +@abi(nonisolated var currentValue: Int) +@abi(for: projection, nonisolated var $currentValue: Binding) +@abi(for: storage, nonisolated var _currentValue: Binding) +@MainActor @Binding var currentValue: Int +``` + +### Support for accessors + +It might be possible to allow `@abi` to be attached to individual accessors. + +### Support for context changes + +It might be possible to allow an ABI-providing declaration to belong to a +different context than its counterpart—for instance, turning a global variable +into a static property, or moving a method to a single-property `@frozen` +wrapper struct. + +A particularly interesting one might be allowing an extension member to +be mangled as a member of the main type declaration, or vice versa, since users +may not be aware of the ABI impact of moving a declaration from one to the +other. + +### Equivalent type attribute + +Many uses of `@abi` only change one or two types in a complicated declaration. +It might be possible to provide an `@abi` *type* attribute that can be applied +on the spot as a shorthand: + +```swift +public func runConcurrently( + _ body: @escaping @abi(() -> Void) @Sendable () -> Void +) { ... } + +// Equivalent to: +@abi(func runConcurrently(_: @escaping () -> Void)) +public func runConcurrently( + _ body: @escaping @Sendable () -> Void +) { ... } +``` + +### Support for moving declarations to a different module + +It might be possible to roll some of the functionality of the compiler-internal +`@_originallyDefinedIn` attribute into this attribute. + +## Alternatives considered + +### Many narrow features + +The original motivation for this proposal involved fairly narrow cases where +`@preconcurrency` was too blunt an instrument, such as suppressing the ABI +impact of one sendability annotation while leaving others intact. One could +imagine designing individual type or decl attributes for each specific change +one might wish to make, but this would require both a lot of effort from +compiler engineers to design a specific tool for each problem, and a lot of +effort from library maintainers to figure out which tool to apply to a given +task and how to use it. + +### An argument that describes the differences + +Rather than creating many totally separate features, one could imagine an +`@abi` declaration attribute with an argument list which somehow described the +differences between the API and ABI. However, we see use cases for changing +virtually every aspect of an API—its name; adding or removing declaration +attributes, modifiers, and effects; adding or removing inherited protocols and +generic constraints; changing parameter, result, and error types; changing type +attributes and modifiers; even changing individual sub-types within generic +types, function types, and protocol compositions at any position in the +declaration—and a mini-language to address and edit all of these different +aspects of a declaration seems difficult to design and tedious to learn. +By contrast, re-specifying the entire declaration in the argument reuses the +developer's existing knowledge of how to read and write declarations and gives +them an easy way to adopt it (just copy the existing declaration into an `@abi` +attribute before you start editing in new features). + +### Syntax where the two declarations are peers + +We could design this differently such that the API-only and ABI-only +declarations are peers in the same context: + +```swift +// This declaration provides the ABI... +@abi(for: __rethrows_map(_:)) @usableFromInline func map( + _ transform: (Element) throws -> T +) rethrows -> [T] + +// ...for this declaration +@usableFromInline func __rethrows_map( + _ transform: (Element) throws -> T +) throws -> [T] { + try map(transform) +} +``` + +In theory, this design could simplify parsing, since the `@abi` attribute's +argument might just be an ordinary expression. However, it introduces several +complications: + +1. Merely giving the name of a declaration may not be specific enough in the + presence of overloads, or even when the API and ABI have the same name but + slight type differences. We might normally tell developers to work around + this by using more specific names, but that's not really an appropriate + answer for a tool which is designed to allow fine control of API and ABI + naming. +2. A lot of compiler logic would have to be modified to filter out ABI-only or + API-only declarations when it walked through lists of top-level decls or + members. The current design, where the ABI-only declarations are tucked away + in attributes, keeps them from being accessed by accident. +3. If the future direction for `@abi` on type declarations is taken, the + productions for full type declarations will not be suitable, as they require + member blocks. + +## Acknowledgments + +Thanks to Holly Borla for recognizing the need for an `@abi` attribute. From 4b0e497abab19d0e548c3732c63450170a9fa0a9 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 11 Apr 2025 08:59:34 -0700 Subject: [PATCH 4219/4563] Link to review thread in SE-0476. (#2788) --- proposals/0476-abi-attr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0476-abi-attr.md b/proposals/0476-abi-attr.md index a4e409cd26..e7bea6bf57 100644 --- a/proposals/0476-abi-attr.md +++ b/proposals/0476-abi-attr.md @@ -5,7 +5,7 @@ * Review Manager: [Holly Borla](https://github.com/hborla) * Status: **Active Review (April 11 - April 25, 2025)** * Implementation: behind experimental feature `ABIAttribute` (refinements in [swiftlang/swift#80383](https://github.com/swiftlang/swift/pull/80383)) -* Review: ([pitch](https://forums.swift.org/t/pitch-controlling-the-abi-of-a-declaration/75123)) +* Review: ([pitch](https://forums.swift.org/t/pitch-controlling-the-abi-of-a-declaration/75123)) ([review](https://forums.swift.org/t/se-0476-controlling-the-abi-of-a-function-initializer-property-or-subscript/79233)) ## Introduction From fdfc7867df4e35e29b2a24edee34ea4412ec15b0 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 11 Apr 2025 13:58:53 -0400 Subject: [PATCH 4220/4563] Add missing conformances to `CustomStringConvertible` and `Equatable` in exit tests proposal. This PR adds a couple of missing protocol conformances to types in the exit tests proposal. --- proposals/testing/0008-exit-tests.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/testing/0008-exit-tests.md b/proposals/testing/0008-exit-tests.md index 4256c976ab..1145e3df17 100644 --- a/proposals/testing/0008-exit-tests.md +++ b/proposals/testing/0008-exit-tests.md @@ -274,7 +274,7 @@ extension ExitTest { /// - ``failure`` /// - ``exitCode(_:)`` /// - ``signal(_:)`` - public struct Condition: Sendable { + public struct Condition: Sendable, CustomStringConvertible { /// A condition that matches when a process terminates successfully with exit /// code `EXIT_SUCCESS`. /// @@ -351,7 +351,7 @@ by the `StatusAtExit` enumeration: #if SWT_NO_PROCESS_SPAWNING @available(*, unavailable, message: "Exit tests are not available on this platform.") #endif -public enum StatusAtExit: Sendable { +public enum StatusAtExit: Sendable, Equatable, CustomStringConvertible { /// The process terminated with the given exit code. /// /// [...] From 15edadc8cdeed87393ffafd837aef3b5a8a2aeed Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 15 Apr 2025 11:26:50 -0400 Subject: [PATCH 4221/4563] [ST-0008] Update proposal for second review round. This PR incorporates feedback we have received so far (up to and past the start of the second/focused round of review.) --- proposals/testing/0008-exit-tests.md | 224 ++++++++++++++++++--------- 1 file changed, 153 insertions(+), 71 deletions(-) diff --git a/proposals/testing/0008-exit-tests.md b/proposals/testing/0008-exit-tests.md index 1145e3df17..1950c173f4 100644 --- a/proposals/testing/0008-exit-tests.md +++ b/proposals/testing/0008-exit-tests.md @@ -6,6 +6,7 @@ * Status: **Active Review (April 10...April 21, 2025)** * Bug: [apple/swift-testing#157](https://github.com/apple/swift-testing/issues/157) * Implementation: [apple/swift-testing#324](https://github.com/swiftlang/swift-testing/pull/324) +* Previous Revision: [1](https://github.com/swiftlang/swift-evolution/blob/fdfc7867df4e35e29b2a24edee34ea4412ec15b0/proposals/testing/0008-exit-tests.md) * Review: ([second review](https://forums.swift.org/t/second-review-st-0008-exit-tests/79198)), ([review](https://forums.swift.org/t/st-0008-exit-tests/78692)), ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) ## Introduction @@ -60,7 +61,7 @@ The function from earlier can then be tested using either of the new overloads: ```swift -await #expect(exitsWith: .failure) { +await #expect(processExitsWith: .failure) { var taco = Taco() taco.isDelicious = false eat(taco) // should trigger a precondition failure and process termination @@ -81,7 +82,7 @@ the testing library: /// - expectedExitCondition: The expected exit condition. /// - observedValues: An array of key paths representing results from within /// the exit test that should be observed and returned by this macro. The -/// ``ExitTest/Result/statusAtExit`` property is always returned. +/// ``ExitTest/Result/exitStatus`` property is always returned. /// - comment: A comment describing the expectation. /// - sourceLocation: The source location to which recorded expectations and /// issues should be attributed. @@ -96,7 +97,7 @@ the testing library: /// the test passes or fails. For example, to test that calling `fatalError()` /// causes a process to terminate: /// -/// await #expect(exitsWith: .failure) { +/// await #expect(processExitsWith: .failure) { /// fatalError() /// } /// @@ -128,7 +129,7 @@ the testing library: /// these streams, you can pass its key path in the `observedValues` argument: /// /// let result = await #expect( -/// exitsWith: .failure, +/// processExitsWith: .failure, /// observing: [\.standardOutputContent] /// ) { /// print("Goodbye, world!") @@ -157,7 +158,7 @@ the testing library: /// /// @Test(arguments: 100 ..< 200) /// func sellIceCreamCones(count: Int) async { -/// await #expect(exitsWith: .failure) { +/// await #expect(processExitsWith: .failure) { /// precondition( /// count < 10, // ERROR: A C function pointer cannot be formed from a /// // closure that captures context @@ -172,7 +173,7 @@ the testing library: #endif @discardableResult @freestanding(expression) public macro expect( - exitsWith expectedExitCondition: ExitTest.Condition, + processExitsWith expectedExitCondition: ExitTest.Condition, observing observedValues: [any PartialKeyPath & Sendable] = [], _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, @@ -188,7 +189,7 @@ the testing library: #endif @discardableResult @freestanding(expression) public macro require( - exitsWith expectedExitCondition: ExitTest.Condition, + processExitsWith expectedExitCondition: ExitTest.Condition, observing observedValues: [any PartialKeyPath & Sendable] = [], _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, @@ -202,7 +203,8 @@ the testing library: > support exit tests (generally because it does not support spawning or awaiting > child processes), then we define `SWT_NO_EXIT_TESTS` when we build it. > -> `SWT_NO_EXIT_TESTS` is not defined during test target builds. +> `SWT_NO_EXIT_TESTS` is not defined during test target builds and is presented +> here for illustrative purposes only. ### Representing an exit test in Swift @@ -212,9 +214,9 @@ A new type, `ExitTest`, represents an exit test: /// A type describing an exit test. /// /// Instances of this type describe exit tests you create using the -/// ``expect(exitsWith:observing:_:sourceLocation:performing:)`` or -/// ``require(exitsWith:observing:_:sourceLocation:performing:)`` macro. You -/// don't usually need to interact directly with an instance of this type. +/// ``expect(processExitsWith:observing:_:sourceLocation:performing:)`` or +/// ``require(processExitsWith:observing:_:sourceLocation:performing:)`` macro. +/// You don't usually need to interact directly with an instance of this type. #if SWT_NO_EXIT_TESTS @available(*, unavailable, message: "Exit tests are not available on this platform.") #endif @@ -260,8 +262,8 @@ extension ExitTest { /// /// Values of this type are used to describe the conditions under which an /// exit test is expected to pass or fail by passing them to - /// ``expect(exitsWith:observing:_:sourceLocation:performing:)`` or - /// ``require(exitsWith:observing:_:sourceLocation:performing:)``. + /// ``expect(processExitsWith:observing:_:sourceLocation:performing:)`` or + /// ``require(processExitsWith:observing:_:sourceLocation:performing:)``. /// /// ## Topics /// @@ -287,7 +289,7 @@ extension ExitTest { /// exit code other than `EXIT_SUCCESS` or with any signal. public static var failure: Self { get } - public init(_ statusAtExit: StatusAtExit) + public init(_ exitStatus: ExitStatus) /// Creates a condition that matches when a process terminates with a given /// exit code. @@ -337,7 +339,7 @@ extension ExitTest { ### Exit status The set of possible status codes reported by the child process are represented -by the `StatusAtExit` enumeration: +by the `ExitStatus` enumeration: ```swift /// An enumeration describing possible status a process will yield on exit. @@ -346,12 +348,12 @@ by the `StatusAtExit` enumeration: /// ``ExitTest/Condition`` using ``ExitTest/Condition/init(_:)``. That value /// can then be used to describe the condition under which an exit test is /// expected to pass or fail by passing it to -/// ``expect(exitsWith:observing:_:sourceLocation:performing:)`` or -/// ``require(exitsWith:observing:_:sourceLocation:performing:)``. +/// ``expect(processExitsWith:observing:_:sourceLocation:performing:)`` or +/// ``require(processExitsWith:observing:_:sourceLocation:performing:)``. #if SWT_NO_PROCESS_SPAWNING @available(*, unavailable, message: "Exit tests are not available on this platform.") #endif -public enum StatusAtExit: Sendable, Equatable, CustomStringConvertible { +public enum ExitStatus: Sendable, Equatable, CustomStringConvertible { /// The process terminated with the given exit code. /// /// [...] @@ -378,15 +380,15 @@ extension ExitTest { /// A type representing the result of an exit test after it has exited and /// returned control to the calling test function. /// - /// Both ``expect(exitsWith:observing:_:sourceLocation:performing:)`` and - /// ``require(exitsWith:observing:_:sourceLocation:performing:)`` return - /// instances of this type. + /// Both ``expect(processExitsWith:observing:_:sourceLocation:performing:)`` + /// and ``require(processExitsWith:observing:_:sourceLocation:performing:)`` + /// return instances of this type. public struct Result: Sendable { /// The status of the process hosting the exit test at the time it exits. /// /// When the exit test passes, the value of this property is equal to the /// exit status reported by the process that hosted the exit test. - public var statusAtExit: StatusAtExit { get set } + public var exitStatus: ExitStatus { get set } /// All bytes written to the standard output stream of the exit test before /// it exited. @@ -407,8 +409,8 @@ extension ExitTest { /// /// To enable gathering output from the standard output stream during an /// exit test, pass `\.standardOutputContent` in the `observedValues` - /// argument of ``expect(exitsWith:observing:_:sourceLocation:performing:)`` - /// or ``require(exitsWith:observing:_:sourceLocation:performing:)``. + /// argument of ``expect(processExitsWith:observing:_:sourceLocation:performing:)`` + /// or ``require(processExitsWith:observing:_:sourceLocation:performing:)``. /// /// If you did not request standard output content when running an exit test, /// the value of this property is the empty array. @@ -429,7 +431,7 @@ These macros can be used within a test function: ```swift @Test func `We only eat delicious tacos`() async { - await #expect(exitsWith: .failure) { + await #expect(processExitsWith: .failure) { var taco = Taco() taco.isDelicious = false eat(taco) @@ -444,13 +446,14 @@ exit condition, this is treated as a successful test. It is often interesting to examine what is written to the standard output and standard error streams by code running in an exit test. Callers can request that either or both stream be captured and included in the result of the call to -`#expect(exitsWith:)` or `#require(exitsWith:)`. Capturing these streams can be -a memory-intensive operation, so the caller must explicitly opt in: +`#expect(processExitsWith:)` or `#require(processExitsWith:)`. Capturing these +streams can be a memory-intensive operation, so the caller must explicitly opt +in: ```swift @Test func `We only eat delicious tacos`() async throws { let result = try await #require( - exitsWith: .failure, + processExitsWith: .failure, observing: [\.standardErrorContent]) ) { ... } let stdout = result.standardOutputContent @@ -497,12 +500,12 @@ extension ExitTest { /// observed and returned to the caller. /// /// The testing library sets this property to match what was passed by the - /// developer to the `#expect(exitsWith:)` or `#require(exitsWith:)` macro. - /// If you are implementing an exit test handler, you can check the value of - /// this property to determine what information you need to preserve from your - /// child process. + /// developer to the `#expect(processExitsWith:)` or `#require(processExitsWith:)` + /// macro. If you are implementing an exit test handler, you can check the + /// value of this property to determine what information you need to preserve + /// from your child process. /// - /// The value of this property always includes ``ExitTest/Result/statusAtExit`` + /// The value of this property always includes ``ExitTest/Result/exitStatus`` /// even if the test author does not specify it. /// /// Within a child process running an exit test, the value of this property is @@ -512,9 +515,9 @@ extension ExitTest { /// Call the exit test in the current process. /// /// This function invokes the closure originally passed to - /// `#expect(exitsWith:)` _in the current process_. That closure is expected - /// to terminate the process; if it does not, the testing library will - /// terminate the process as if its `main()` function returned naturally. + /// `#expect(processExitsWith:)` _in the current process_. That closure is + /// expected to terminate the process; if it does not, the testing library + /// will terminate the process as if its `main()` function returned naturally. public consuming func callAsFunction() async -> Never /// Find the exit test function at the given source location. @@ -538,11 +541,11 @@ extension ExitTest { /// the exit test. /// /// This handler is invoked when an exit test (i.e. a call to either - /// ``expect(exitsWith:observing:_:sourceLocation:performing:)`` or - /// ``require(exitsWith:observing:_:sourceLocation:performing:)``) is started. - /// The handler is responsible for initializing a new child environment - /// (typically a child process) and running the exit test identified by - /// `sourceLocation` there. + /// ``expect(processExitsWith:observing:_:sourceLocation:performing:)`` or + /// ``require(processExitsWith:observing:_:sourceLocation:performing:)``) is + /// started. The handler is responsible for initializing a new child + /// environment (typically a child process) and running the exit test + /// identified by `sourceLocation` there. /// /// In the child environment, you can find the exit test again by calling /// ``ExitTest/find(at:)`` and can run it by calling @@ -587,6 +590,11 @@ Android _does_ have `posix_spawn()` and related API and may be able to use the same implementation as Linux. Android support is an ongoing area of research for Swift Testing's core team. +> [!NOTE] +> In the event we can add support for exit tests on a new platform _without_ any +> changes to the feature's public interface, the Testing Workgroup has agreed +> that an additional Swift Evolution proposal will not be necessary. + ### Recursive exit tests The technical constraints preventing recursive exit test invocation can be @@ -610,9 +618,9 @@ could leverage closures' capture list syntax. Subjectively, capture lists ought to be somewhat intuitive for developers in this context: ```swift -let (lettuce, cheese, crema) = taco.addToppings() -await #expect(exitsWith: .failure) { [taco, plant = lettuce, cheese, crema] in - try taco.removeToppings(plant, cheese, crema) +let (lettuce, cheese) = taco.addToppings() +await #expect(processExitsWith: .failure) { [taco, plant = lettuce, cheese] in + try taco.removeToppings(plant, cheese) } ``` @@ -624,7 +632,7 @@ explicit or implicit `main() throws` function: the process terminates abnormally and control returns to the test function that is awaiting the exit test: ```swift -await #expect(exitsWith: .failure) { +await #expect(processExitsWith: .failure) { throw TacoError.noTacosFound } ``` @@ -671,6 +679,24 @@ let result = try await #require(process, exitsWith: .success) #expect(result.standardOutputContent.contains("Build went well!").utf8) ``` +### Conformance of ExitStatus to ExpressibleByIntegerLiteral + +A contributor on the Swift forums suggested having `ExitStatus` conform to +[`ExpressibleByIntegerLiteral`](https://developer.apple.com/documentation/swift/expressiblebyintegerliteral) +and interpreting an integer literal as an exit code, such that a test author +could write: + +```swift +await #expect(processExitsWith: EX_CANTCREAT) { + ... +} +``` + +This would be convenient for test authors who are dealing with a variety of exit +codes, but is beyond the scope of this proposal. Adding conformance to this +protocol also requires some care to ensure that signal constants such as +`SIGABRT` cannot be accidentally interpreted as exit codes. + ## Alternatives considered - Doing nothing. @@ -729,7 +755,35 @@ let result = try await #require(process, exitsWith: .success) sound "right" when the entire expression is read out loud. For example, you probably wouldn't say "exits due to success" in English. -- Combining `StatusAtExit` and `ExitTest.Condition` into a single type: + A contributor in the Swift forums suggested `#expect(crashes:)`: + + ```swift + await #expect(crashes: { + ... + }) + ``` + + This would preclude the possibility of writing an exit test that is expected + to exit successfully—a scenario for which we have real-world use cases. It was + also not clear that the word "crash" applied to every failing exit status. For + example, a process that exits with the POSIX-defined exit code `EX_TEMPFAIL` + likely has not _crashed_; it has just reported that the requested operation + has failed. + + This signature would also be subject to label elision when used with trailing + closure syntax, resulting in: + + ```swift + await #expect { + ... + } + ``` + + The lack of any distinguishing label here would unacceptably impact the test's + readability as it gives no indication that the code is running out-of-process + or is expected to terminate its process. + +- Combining `ExitStatus` and `ExitTest.Condition` into a single type: ```swift enum ExitCondition { @@ -743,35 +797,46 @@ let result = try await #require(process, exitsWith: .success) exit conditions complicated and necessitated a `==` operator that did not satisfy the requirements of the `Equatable` protocol. -- Naming `StatusAtExit` something else such as: +- Naming `ExitStatus` something else such as: - - `ExitStatus`, which could be too easily confusable with exit _codes_ such as - `EXIT_SUCCESS`; + - `StatusAtExit`, which might avoid some confusion with exit _codes_ but which + is not idiomatic Swift; - `ProcessStatus`, but we don't say "process" in our API surface elsewhere; - `Status`, which is too generic, - `ExitReason`, but "status" is a more widely-used term of art for this concept; or - - `terminationStatus` (which Foundation to represent approximately the same - concept), but we don't use "termination" in Swift Testing's API anywhere. - - I settled on `StatusAtExit` because it was distinct and makes it clear that it - represents the status of a process _at exit time_. `ExitStatus` could be - interpreted as the status of the exit itself, i.e.: - - ```swift - enum ExitStatus { - case running - case suspended - case exiting - case exited - } - ``` + - `TerminationStatus` (which Foundation uses to represent approximately the + same concept), but we don't use "termination" in Swift Testing's API + anywhere. + + In particular, there was some interest in using "termination" instead of + "exit" for consistency with Foundation. Foundation and the upcoming + `Subprocess` package use both terms interchangeably, so there is precedent for + either. "Exit" is more concise; "terminate" may be read to imply that the + process was _forced_ to stop running. + +- Naming `ExitStatus.exitCode(_:)` just `.code(_:)`. Some contributors on the + forums felt that the use of "exit" here was redundant given the proposed + `exitsWith:` and `processExitsWith:` labels. However, "code" is potentially + ambiguous: does it refer to an exit code, a signal code, the code the test + author is writing, etc.? + + We certainly don't want the exit test interface to be redundant. However, + given that: + + - We _expect_ (no pun intended) most uses of exit tests will check for + `.failure` rather than a specific exit code; + - "Exit code" is an established term of art; and + - `.exitCode(_:)` may appear in other contexts (not just as an argument to + `#expect(processExitsWith:)`) + + We have opted to keep the full case name. - Using parameter packs to specify observed values and return types: ```swift @freestanding(expression) public macro require( - exitsWith expectedExitCondition: ExitTest.Condition, + processExitsWith expectedExitCondition: ExitTest.Condition, observing observedValues: (repeat (KeyPath)) = (), _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, @@ -785,17 +850,17 @@ let result = try await #require(process, exitsWith: .success) ```swift let (status, stderr) = try await #expect( - exitsWith: .failure, - observing: (\.statusAtExit, \.standardErrorContent) + processExitsWith: .failure, + observing: (\.exitStatus, \.standardErrorContent) ) { ... } #expect(status == ...) #expect(stderr.contains(...)) ``` - Unfortunately, the `#expect(exitsWith:)` and `#require(exitsWith:)` macros do - not have enough information at compile time to correctly infer the types of - the key paths passed as `observedValues` above, so we end up with rather - obscure errors: + Unfortunately, the `#expect(processExitsWith:)` and `#require(processExitsWith:)` + macros do not have enough information at compile time to correctly infer the + types of the key paths passed as `observedValues` above, so we end up with + rather obscure errors: > 🛑 Cannot convert value of type 'KeyPath<_, _>' to expected argument type > 'KeyPath' @@ -820,6 +885,23 @@ let result = try await #require(process, exitsWith: .success) Modifying the C or C++ standard library, or modifying the Objective-C runtime, would be well beyond the scope of this proposal. +- Skipping test functions containing exit tests on platforms that do not support + exit tests. + + This would avoid the need to write `if os(...)`, `@available(...)`, or + `if #available(...)` in a cross-platform test function before using exit + tests. Swift Testing does not currently support skipping a test that has + already started executing, and the implementation of such a feature is beyond + the scope of this proposal. + + Even if the library supported this sort of action, it would likely be + surprising to test authors that they could write a test that compiles for e.g. + iOS but doesn't run and doesn't report any problems. + + Further, in general this is not a pattern that is used in the Swift ecosystem + for platform-specific functionality; instead, `#if os(...)` and availability + checks are the normal way to mark code as platform-specific. + ## Acknowledgments Many thanks to the XCTest and Swift Testing team. Thanks to @compnerd for his From 1ca81dcb3e50efe1af9c89768c07dc29c05c1309 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Tue, 15 Apr 2025 11:59:06 -0700 Subject: [PATCH 4222/4563] Test Issue Warnings --- .../testing/XXXX-issue-severity-warning.md | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 proposals/testing/XXXX-issue-severity-warning.md diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md new file mode 100644 index 0000000000..4aa5f4b4a2 --- /dev/null +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -0,0 +1,99 @@ +# [DRAFT] Test Issue Warnings + +* Proposal: [ST-XXXX](XXXX-issue-severity-warning.md) +* Authors: [Suzy Ratcliff](https://github.com/suzannaratcliff) +* Review Manager: TBD +* Status: **Pitched** +* Bug: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075) +* Implementation: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075) + +## Introduction + +I propose introducing a new API to Swift Testing that allows developers to record issues with a specified severity level. By default, all issues will have severity level “error”, and a new “warning” level will be added to represent less severe issues. The effects of the warning recorded on a test will not cause a failure but will be included in the test results for inspection after the run is complete. + +## Motivation + +Currently, when an issue arises during a test, the only possible outcome is to mark the test as failed. This presents a challenge for users who want a deeper insight into the events occurring within their tests. By introducing a dedicated mechanism to record issues that do not cause test failure, users can more effectively inspect and diagnose problems at runtime and review results afterward. This enhancement provides greater flexibility and clarity in test reporting, ultimately improving the debugging and analysis process. + +## Proposed solution +We propose introducing a new property on Issues in Swift Testing called `Severity`, that represents if an issue is a warning or an error. +The default Issue severity will still be error and users can create set the severity when they record an issue. + +Test authors will be able to inspect if the issue is a failing issue and will be able to check the severity. + +## Detailed design + +*Severity Enum* + +We introduce a Severity enum to categorize issues detected during testing. This enum is crucial for distinguishing between different levels of test issues and is defined as follows: + +The `Severity` enum is defined as follows: + +```swift + public enum Severity: Sendable { + /// The severity level for an issue which should be noted but is not + /// necessarily an error. + /// + /// An issue with warning severity does not cause the test it's associated + /// with to be marked as a failure, but is noted in the results. + case warning + + /// The severity level for an issue which represents an error in a test. + /// + /// An issue with error severity causes the test it's associated with to be + /// marked as a failure. + case error + } +``` + +*Recording Non-Failing Issues* +To enable test authors to log non-failing issues without affecting test results, we provide a method for recording such issues: + +```swift +Issue.record("My comment", severity: .warning) +``` + +*Issue Type Enhancements* +The Issue type is enhanced with two new properties to better handle and report issues: +- `severity`: This property allows access to the specific severity level of an issue, enabling more precise handling of test results. + +```swift +/// The severity of the issue. +public var severity: Severity + +``` +- `isFailure`: A boolean property to determine if an issue results in a test failure, thereby helping in result aggregation and reporting. +```swift + + /// Whether or not this issue should cause the test it's associated with to be + /// considered a failure. + /// + /// The value of this property is `true` for issues which have a severity level of + /// ``Issue/Severity/error`` or greater and are not known issues via + /// ``withKnownIssue(_:isIntermittent:sourceLocation:_:when:matching:)``. + /// Otherwise, the value of this property is `false.` + /// + /// Use this property to determine if an issue should be considered a failure, instead of + /// directly comparing the value of the ``severity`` property. +public var isFailure: Bool +``` + +For more details, refer to the [Issue Documentation](https://developer.apple.com/documentation/testing/issue). + +This revision aims to clarify the functionality and usage of the `Severity` enum and `Issue` properties while maintaining consistency with the existing Swift API standards. + +## Alternatives considered + +- Doing Nothing: Although there have been recurring requests over the years to support non-failing issues, we did not have a comprehensive solution until now. This year, we finally had the necessary components to implement this feature effectively. + +- Separate Issue Creation and Recording: We considered providing a mechanism to create issues independently before recording them, rather than passing the issue details directly to the `record` method. This approach was ultimately set aside in favor of simplicity and directness in usage. + +- Naming of `isFailure` vs. `isFailing`: We evaluated whether to name the property `isFailing` instead of `isFailure`. The decision to use `isFailure` was made to adhere to naming conventions and ensure clarity and consistency within the API. + +- Severity-Only Checking: We deliberated not exposing `isFailure` and relying solely on `severity` checks. However, this was rejected because it would require test authors to overhaul their code should we introduce additional severity levels in the future. By providing `isFailure`, we offer a straightforward way to determine test outcome impact, complementing the severity feature. + +## Acknowledgments + +Thanks to Stuart Montgomery for creating and implementing severity in Swift Testing. + +Thanks to Brian Croom and Jonathan Grynspan for feedback on warnings along the way. From dbcea32f69f5d4fb24162536b9e1945f4c48b51f Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Wed, 16 Apr 2025 08:48:20 -0500 Subject: [PATCH 4223/4563] Add a proposal for a string interpolation default parameter (#2772) * Add a proposal for the string interpolation default parameter * Claim SE-0477 and prepare for review --------- Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- .../0477-default-interpolation-values.md | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 proposals/0477-default-interpolation-values.md diff --git a/proposals/0477-default-interpolation-values.md b/proposals/0477-default-interpolation-values.md new file mode 100644 index 0000000000..8856ea150f --- /dev/null +++ b/proposals/0477-default-interpolation-values.md @@ -0,0 +1,171 @@ +# Default Value in String Interpolations + +* Proposal: [SE-0477](0477-default-interpolation-values.md) +* Authors: [Nate Cook](https://github.com/natecook1000) +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Active review (April 16...29, 2025)** +* Implementation: [swiftlang/swift#80547](https://github.com/swiftlang/swift/pull/80547) +* Review: ([pitch](https://forums.swift.org/t/pitch-default-values-for-string-interpolations/69381)) + +## Introduction + +A new string interpolation syntax for providing a default string +when interpolating an optional value. + +## Motivation + +String interpolations are a streamlined and powerful way to include values within a string literal. +When one of those values is optional, however, +interpolating is not so simple; +in many cases, a developer must fall back to unpalatable code +or output that exposes type information. + +For example, +placing an optional string in an interpolation +yields an important warning and two suggested fixes, +only one of which is ideal: + +```swift +let name: String? = nil +print("Hello, \(name)!") +// warning: string interpolation produces a debug description for an optional value; did you mean to make this explicit? +// print("Hello, \(name)!") +// ^~~~ +// note: use 'String(describing:)' to silence this warning +// print("Hello, \(name)!") +// ^~~~ +// String(describing: ) +// note: provide a default value to avoid this warning +// print("Hello, \(name)!") +// ^~~~ +// ?? <#default value#> + +``` + +The first suggestion, adding `String(describing:)`, +silences the warning but includes `nil` in the output of the string — +maybe okay for a quick shell script, +but not really appropriate result for anything user-facing. + +The second suggestion is good, +allowing us to provide whatever default string we'd like: + +```swift +let name: String? = nil +print("Hello, \(name ?? "new friend")!") +``` + +However, the nil-coalescing operator (`??`) +only works with values of the same type as the optional value, +making it awkward or impossible to use when providing a default for non-string types. +In this example, the `age` value is an optional `Int`, +and there isn't a suitable integer to use when it's `nil`: + +```swift +let age: Int? = nil +print("Your age: \(age)") +// warning, etc.... +``` + +To provide a default string when `age` is missing, +we have to write some gnarly code, +or split out the missing case altogether: + +```swift +let age: Int? = nil +// Optional.map +print("Your age: \(age.map { "\($0)" } ?? "missing")") +// Ternary expression +print("Your age: \(age != nil ? "\(age!)" : "missing")") +// if-let statement +if let age { + print("Your age: \(age)") +} else { + print("Your age: missing") +} +``` + +## Proposed solution + +The standard library should add a string interpolation overload +that lets you write the intended default as a string, +no matter what the type of value: + +```swift +let age: Int? = nil +print("Your age: \(age, default: "missing")") +// Prints "Your age: missing" +``` + +This addition will improve the clarity of code that uses string interpolations +and encourage developers to provide sensible defaults +instead of letting `nil` leak into string output. + +## Detailed design + +The implementation of this new interpolation overload looks like this, +added as an extension to the `DefaultStringInterpolation` type: + +```swift +extension DefaultStringInterpolation { + mutating func appendInterpolation( + _ value: T?, + default: @autoclosure () -> String + ) { + if let value { + self.appendInterpolation(value) + } else { + self.appendInterpolation(`default`()) + } + } +} +``` + +The new interpolation's `default:` parameter name +matches the one in the `Dictionary` subscript that has a similar purpose. + +You can try this out yourself by copy/pasting the snippet above into a project or playground, +or by experimenting with [this Swift Fiddle](https://swiftfiddle.com/nxttprythnfbvlm4hwjyt2jbjm). + +## Source compatibility + +This proposal adds one new API to the standard library, +which should not be source-breaking for any existing projects. +If a project or a dependency has added a similar overload, +it will take precedence over the new standard library API. + +## ABI compatibility + +This proposal is purely an extension of the ABI of the +standard library and does not change any existing features. + +## Implications on adoption + +The new API will be included in a new version of the Swift runtime, +and is marked as backward deployable. + +## Future directions + +There are [some cases][reflecting] where a `String(reflecting:)` conversion +is more appropriate than the `String(describing:)` normally used via string interpolation. +Additional string interpolation overloads could make it easier to use that alternative conversion, +and to provide a default when working with optional values. + +[reflecting]: https://forums.swift.org/t/pitch-default-values-for-string-interpolations/69381/58 + +## Alternatives considered + +**An interpolation like `"\(describing: value)"`** +This alternative would provide a shorthand for the first suggested fix, +using `String(describing:)`. +Unlike the solution proposed, +this kind of interpolation doesn't make it clear that you're working with an optional value, +so you could end up silently including `nil` in output without expecting it +(which is the original reason for the compiler warnings). + +**Extend `StringInterpolationProtocol` instead** +The proposed new interpolation works with _any_ optional value, +but some types only accept a limited or constrained set of types interpolations. +If the new `\(_, default:)` interpolation proves to be a useful pattern, +other types can add it as appropriate. + From 93037c0f5dfefd5dfcdbf40f4e609f57fb713037 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 16 Apr 2025 09:52:16 -0400 Subject: [PATCH 4224/4563] SE-0477: Add link to review thread (#2791) --- proposals/0477-default-interpolation-values.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0477-default-interpolation-values.md b/proposals/0477-default-interpolation-values.md index 8856ea150f..60248240ab 100644 --- a/proposals/0477-default-interpolation-values.md +++ b/proposals/0477-default-interpolation-values.md @@ -5,7 +5,7 @@ * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Active review (April 16...29, 2025)** * Implementation: [swiftlang/swift#80547](https://github.com/swiftlang/swift/pull/80547) -* Review: ([pitch](https://forums.swift.org/t/pitch-default-values-for-string-interpolations/69381)) +* Review: ([pitch](https://forums.swift.org/t/pitch-default-values-for-string-interpolations/69381)) ([review](https://forums.swift.org/t/se-0477-default-value-in-string-interpolations/79302)) ## Introduction From 7f16311d11a01b95fd48b6e33d22f03dfcb2de9c Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 16 Apr 2025 10:02:55 -0700 Subject: [PATCH 4225/4563] Mark SE-0452 and SE-0453 as implemented --- proposals/0452-integer-generic-parameters.md | 2 +- proposals/0453-vector.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0452-integer-generic-parameters.md b/proposals/0452-integer-generic-parameters.md index faa05bb4d7..b2ca7b2083 100644 --- a/proposals/0452-integer-generic-parameters.md +++ b/proposals/0452-integer-generic-parameters.md @@ -3,7 +3,7 @@ * Proposal: [SE-0452](0452-integer-generic-parameters.md) * Authors: [Alejandro Alonso](https://github.com/Azoy), [Joe Groff](https://github.com/jckarter) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Implementation: [swiftlang/swift#75518](https://github.com/swiftlang/swift/pull/75518), [swiftlang/swift#78248](https://github.com/swiftlang/swift/pull/78248) * Review: ([pitch](https://forums.swift.org/t/integer-generic-parameters/74181)) ([first review](https://forums.swift.org/t/se-0452-integer-generic-parameters/75844)) ([second review](https://forums.swift.org/t/second-review-se-0452-integer-generic-parameters/77043)) ([acceptance](https://forums.swift.org/t/accepted-se-0452-integer-generic-parameters/77507)) diff --git a/proposals/0453-vector.md b/proposals/0453-vector.md index 7d143f7413..d755bf66f1 100644 --- a/proposals/0453-vector.md +++ b/proposals/0453-vector.md @@ -3,7 +3,7 @@ * Proposal: [SE-0453](0453-vector.md) * Authors: [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Roadmap: [Approaches for fixed-size arrays](https://forums.swift.org/t/approaches-for-fixed-size-arrays/58894) * Implementation: [swiftlang/swift#76438](https://github.com/swiftlang/swift/pull/76438) * Review: ([pitch](https://forums.swift.org/t/vector-a-fixed-size-array/75264)) ([first review](https://forums.swift.org/t/se-0453-vector-a-fixed-size-array/76004)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0453-vector-a-fixed-size-array/76411)) ([second review](https://forums.swift.org/t/second-review-se-0453-vector-a-fixed-size-array/76412)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0453-inlinearray-formerly-vector-a-fixed-size-array/77678)) From 48728ee0a8f491b84f501455d49352367891f069 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 16 Apr 2025 14:46:37 -0400 Subject: [PATCH 4226/4563] Return SE-0472 for revision. --- proposals/0472-task-start-synchronously-on-caller-context.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0472-task-start-synchronously-on-caller-context.md b/proposals/0472-task-start-synchronously-on-caller-context.md index ae636da68c..836920fce3 100644 --- a/proposals/0472-task-start-synchronously-on-caller-context.md +++ b/proposals/0472-task-start-synchronously-on-caller-context.md @@ -3,9 +3,9 @@ * Proposal: [SE-0472](0472-task-start-synchronously-on-caller-context.md) * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Active Review (March 27...April 10, 2025)** +* Status: **Returned for revision** * Implementation: https://github.com/swiftlang/swift/pull/79608 -* Review: ([pitch](https://forums.swift.org/t/pitch-concurrency-starting-tasks-synchronously-from-caller-context/77960/)) ([review](https://forums.swift.org/t/se-0472-starting-tasks-synchronously-from-caller-context/78883)) +* Review: ([pitch](https://forums.swift.org/t/pitch-concurrency-starting-tasks-synchronously-from-caller-context/77960/)) ([review](https://forums.swift.org/t/se-0472-starting-tasks-synchronously-from-caller-context/78883)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0472-starting-tasks-synchronously-from-caller-context/79311)) ## Introduction From a1fcbcbc51e3ef4f313cf29ce51c7c5a5d39c695 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Thu, 17 Apr 2025 14:51:29 -0700 Subject: [PATCH 4227/4563] Clean up format --- proposals/testing/XXXX-issue-severity-warning.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index 4aa5f4b4a2..823c19e846 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -1,4 +1,4 @@ -# [DRAFT] Test Issue Warnings +# Test Issue Severity * Proposal: [ST-XXXX](XXXX-issue-severity-warning.md) * Authors: [Suzy Ratcliff](https://github.com/suzannaratcliff) @@ -16,8 +16,8 @@ I propose introducing a new API to Swift Testing that allows developers to recor Currently, when an issue arises during a test, the only possible outcome is to mark the test as failed. This presents a challenge for users who want a deeper insight into the events occurring within their tests. By introducing a dedicated mechanism to record issues that do not cause test failure, users can more effectively inspect and diagnose problems at runtime and review results afterward. This enhancement provides greater flexibility and clarity in test reporting, ultimately improving the debugging and analysis process. ## Proposed solution -We propose introducing a new property on Issues in Swift Testing called `Severity`, that represents if an issue is a warning or an error. -The default Issue severity will still be error and users can create set the severity when they record an issue. +We propose introducing a new property on `Issue` in Swift Testing called `Severity`, that represents if an issue is a `warning` or an `error`. +The default Issue severity will still be `error` and users can set the severity when they record an issue. Test authors will be able to inspect if the issue is a failing issue and will be able to check the severity. @@ -27,7 +27,7 @@ Test authors will be able to inspect if the issue is a failing issue and will be We introduce a Severity enum to categorize issues detected during testing. This enum is crucial for distinguishing between different levels of test issues and is defined as follows: -The `Severity` enum is defined as follows: +The `Severity` enum: ```swift public enum Severity: Sendable { @@ -54,16 +54,20 @@ Issue.record("My comment", severity: .warning) ``` *Issue Type Enhancements* + The Issue type is enhanced with two new properties to better handle and report issues: - `severity`: This property allows access to the specific severity level of an issue, enabling more precise handling of test results. ```swift +// ... + /// The severity of the issue. public var severity: Severity ``` - `isFailure`: A boolean property to determine if an issue results in a test failure, thereby helping in result aggregation and reporting. ```swift +// ... /// Whether or not this issue should cause the test it's associated with to be /// considered a failure. @@ -78,7 +82,7 @@ public var severity: Severity public var isFailure: Bool ``` -For more details, refer to the [Issue Documentation](https://developer.apple.com/documentation/testing/issue). +For more details on `Issue`, refer to the [Issue Documentation](https://developer.apple.com/documentation/testing/issue). This revision aims to clarify the functionality and usage of the `Severity` enum and `Issue` properties while maintaining consistency with the existing Swift API standards. From 6f26d42f35f02188e2437323c967d706d1951d98 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Thu, 17 Apr 2025 15:55:42 -0700 Subject: [PATCH 4228/4563] Add example for isFailure and severity --- proposals/testing/XXXX-issue-severity-warning.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index 823c19e846..2fd9364bd2 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -82,6 +82,16 @@ public var severity: Severity public var isFailure: Bool ``` +Example usage of `severity` and `isFailure: +```swift +// ... +withKnownIssue { + // ... +} matching: { issue in + return issue.isFailure || issue.severity > .warning +} +``` + For more details on `Issue`, refer to the [Issue Documentation](https://developer.apple.com/documentation/testing/issue). This revision aims to clarify the functionality and usage of the `Severity` enum and `Issue` properties while maintaining consistency with the existing Swift API standards. From 55dd90e54fa9292565160c74cc982e652cf47bf4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 18 Apr 2025 09:58:58 -0700 Subject: [PATCH 4229/4563] SE-0470 is implemented in Swift 6.2 --- proposals/0470-isolated-conformances.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0470-isolated-conformances.md b/proposals/0470-isolated-conformances.md index 146dcd95bd..83c4b3a51a 100644 --- a/proposals/0470-isolated-conformances.md +++ b/proposals/0470-isolated-conformances.md @@ -3,7 +3,7 @@ * Proposal: [SE-0470](0470-isolated-conformances.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md) * Implementation: On `main` with the experimental features `IsolatedConformances` and `StrictSendableMetatypes`. * Upcoming Feature Flag: `InferIsolatedConformances` From 65ba8836d7303e1f5465bd0cbd5602bb6347ce8c Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Fri, 18 Apr 2025 10:12:28 -0700 Subject: [PATCH 4230/4563] Add Acknowledgments --- proposals/testing/XXXX-issue-severity-warning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index 2fd9364bd2..05c122ff61 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -110,4 +110,4 @@ This revision aims to clarify the functionality and usage of the `Severity` enum Thanks to Stuart Montgomery for creating and implementing severity in Swift Testing. -Thanks to Brian Croom and Jonathan Grynspan for feedback on warnings along the way. +Thanks to Joel Middendorf, Dorothy Fu, Brian Croom, and Jonathan Grynspan for feedback on severity along the way. From ddffc2e7074dfb90ca022baa5953932a1f1fb608 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 21 Apr 2025 18:30:51 -0700 Subject: [PATCH 4231/4563] Add a proposal for specifying default actor isolation per-file using a type alias. (#2777) * Add a draft proposal for specifying default isolation via typealias. * Clarify that it is invalid to use `Never` as the underlying type of `DefaultIsolation`. * Update and rename NNNN-default-isolation-typealias.md to 0478-default-isolation-typealias.md Assigned SE-0478 --------- Co-authored-by: Stephen Canon --- proposals/0478-default-isolation-typealias.md | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 proposals/0478-default-isolation-typealias.md diff --git a/proposals/0478-default-isolation-typealias.md b/proposals/0478-default-isolation-typealias.md new file mode 100644 index 0000000000..35f2adadb2 --- /dev/null +++ b/proposals/0478-default-isolation-typealias.md @@ -0,0 +1,115 @@ +# Default actor isolation typealias + +* Proposal: [SE-0478](0478-default-isolation-typealias.md) +* Authors: [Holly Borla](https://github.com/hborla) +* Review Manager: [Steve Canon](https://github.com/stephentyrone) +* Status: **Active Review (April 21 ... May 5, 2025)** +* Vision: [Improving the approachability of data-race safety](/visions/approachable-concurrency.md) +* Implementation: [swiftlang/swift#80572](https://github.com/swiftlang/swift/pull/80572) +* Experimental Feature Flag: `DefaultIsolationTypealias` +* Previous Proposal: [SE-0466: Control default actor isolation inference][SE-0466] +* Review: ([pitch](https://forums.swift.org/t/pitch-a-typealias-for-per-file-default-actor-isolation/79150)) + +## Introduction + +[SE-0466: Control default actor isolation inference][SE-0466] introduced the ability to specify default actor isolation on a per-module basis. This proposal introduces a new typealias for specifying default actor isolation in individual source files within a module. This allows specific files to opt out of main actor isolation within a main-actor-by-default module, and opt into main actor isolation within a nonisolated-by-default module. + +## Motivation + +SE-0466 allows code to opt in to being “single-threaded” by default by isolating everything in the module to the main actor. When the programmer really wants concurrency, they can request it explicitly by marking a function or type as `nonisolated`, or they can define it in a module that does not default to main-actor isolation. However, it's very common to group multiple declarations used in concurrent code into one source file or a small set of source files. Instead of choosing between writing `nonisolated` on each individual declaration or splitting those files into a separate module, it's desirable to state that all declarations in those files default to `nonisolated`. + +## Proposed solution + +This proposal allows writing a private typealias named `DefaultIsolation` to specify the default actor isolation for a file. + +An underlying type of `MainActor` specifies that all declarations in the file default to main actor isolated: + +```swift +// main.swift + +private typealias DefaultIsolation = MainActor + +// Implicitly '@MainActor' +var global = 0 + +// Implicitly '@MainActor' +func main() { ... } + +main() +``` + +An underlying type of `nonisolated` specifies that all declarations in the file default to `nonisolated`: + +```swift +// Point.swift + +private typealias DefaultIsolation = nonisolated + +// Implicitly 'nonisolated' +struct Point { + var x: Int + var y: Int +} +``` + +## Detailed design + + A typealias named `DefaultIsolation` can specify the actor isolation to use for the source file it's written in under the following conditions: + +* The typealias is written at the top-level. +* The typealias is `private` or `fileprivate`; the `DefaultIsolation` typealias cannot be used to set the default isolation for the entire module, so its access level cannot be `internal` or above. +* The underlying type is either `MainActor` or `nonisolated`. + + It is not invalid to write a typealias called `DefaultIsolation` that does not meet the above conditions. Any typealias named `DefaultIsolation` that does not meet the above conditions will be skipped when looking up the default isolation for the source file. The compiler will emit a warning for any `DefaultIsolation` typealias that is not considered for default actor isolation along with the reason why: + +```swift +@globalActor +actor CustomGlobalActor { + static let shared = CustomGlobalActor() +} + +private typealias DefaultIsolation = CustomGlobalActor // warning: not used for default actor isolation +``` + +To allow writing `nonisolated` as the underlying type of a typealias, this proposal adds a typealias named `nonisolated` to the Concurrency library: + +```swift +public typealias nonisolated = Never +``` + +This typealias serves no purpose beyond specifying default actor isolation. To specify `nonisolated` using the `DefaultIsolation` typealias, the underlying type must be `nonisolated` exactly; it is invalid to write `private typealias DefaultIsolation = Never`. + +## Source compatibility + +Technically source breaking if someone happens to have written a private `DefaultIsolation` typealias with an underlying type of `MainActor`, which will start to infer every declaration in that file as `@MainActor`-isolated after this change. This seems extremely unlikely. + +## ABI compatibility + +This proposal has no ABI impact on existing code. + +## Implications on adoption + +This proposal does not change the adoption implications of adding `@MainActor` to a declaration that was previously nonisolated and vice versa. The source and ABI compatibility implications of changing actor isolation are documented in the Swift migration guide's [Library Evolution](https://github.com/apple/swift-migration-guide/blob/29d6e889e3bd43c42fe38a5c3f612141c7cefdf7/Guide.docc/LibraryEvolution.md#main-actor-annotations) article. + +## Alternatives considered + +Adding a typealias named `nonisolated` to `Never` to the Concurrency library to enable writing it as the underlying type of a typealias is pretty strange; this approach leveraging the fact that `nonisolated` is a contextual keyword, so it's valid to use `nonisolated` as an identifier. This proposal uses a typealias instead of an empty struct or enum type to avoid the complications of having a new type be only available with the Swift 6.2 standard library. + +It's extremely valuable to have a consistent way to spell `nonisolated`. Introducing a type that follows standard naming conventions, such as `Nonisolated`, or using an existing type like `Never` is more consistent with recommended style, but overall complicates the concurrency model because it means you need to spell `nonisolated` differently when specifying it per file versus writing it on a declaration. And because the underlying type of this typealias is used to infer actor isolation, it's not used as a type in the same way that other typealiases are. + +Another alternative is to introduce a bespoke syntax such as `using MainActor` or `using nonisolated`. This approach preserves a consistent spelling for `nonisolated`, but at the cost of adding new language syntax that deviates from other defaulting rules such as the default literal types and the default actor system types. + +Having a `nonisolated` typealias may also allow us to improve the package manifest APIs for specifying default isolation, allowing us to move away from using `nil` to specify `nonisolated`: + +```swift +SwiftSetting.defaultIsolation(nonisolated.self) +``` + +We can also pursue allowing bare metatypes without `.self` to allow: + +```swift +SwiftSetting.defaultIsolation(nonisolated) +SwiftSetting.defaultIsolation(MainActor) +``` + +[SE-0466]: /proposals/0466-control-default-actor-isolation.md From 38229b1ca7ef5171ba139a50eadea168558aeeae Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Mon, 21 Apr 2025 21:36:53 -0400 Subject: [PATCH 4232/4563] Update 0478-default-isolation-typealias.md (#2797) --- proposals/0478-default-isolation-typealias.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0478-default-isolation-typealias.md b/proposals/0478-default-isolation-typealias.md index 35f2adadb2..3d29a4932a 100644 --- a/proposals/0478-default-isolation-typealias.md +++ b/proposals/0478-default-isolation-typealias.md @@ -8,7 +8,7 @@ * Implementation: [swiftlang/swift#80572](https://github.com/swiftlang/swift/pull/80572) * Experimental Feature Flag: `DefaultIsolationTypealias` * Previous Proposal: [SE-0466: Control default actor isolation inference][SE-0466] -* Review: ([pitch](https://forums.swift.org/t/pitch-a-typealias-for-per-file-default-actor-isolation/79150)) +* Review: ([pitch](https://forums.swift.org/t/pitch-a-typealias-for-per-file-default-actor-isolation/79150))([review](https://forums.swift.org/t/se-0478-default-actor-isolation-typealias/79436)) ## Introduction From 966d5cb21cd7f7e9adc277ca9a22daada356e76b Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 21 Apr 2025 22:14:12 -0700 Subject: [PATCH 4233/4563] Accept SE-0469: Task naming. (#2798) --- proposals/0469-task-names.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0469-task-names.md b/proposals/0469-task-names.md index ebded646f8..a5f1c6af62 100644 --- a/proposals/0469-task-names.md +++ b/proposals/0469-task-names.md @@ -3,9 +3,9 @@ * Proposal: [SE-0469](0469-task-names.md) * Authors: [Konrad Malawski](https://github.com/ktoso), [Harjas Monga](https://github.com/Harjas12) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (March 13 - March 27, 2025)** +* Status: **Accepted** * Implementation: [swiftlang/swift#79600](https://github.com/swiftlang/swift/pull/79600) -* Review: ([pitch](https://forums.swift.org/t/pitch-task-naming-api/76115)) ([review](https://forums.swift.org/t/se-0469-task-naming/78509)) +* Review: ([pitch](https://forums.swift.org/t/pitch-task-naming-api/76115)) ([review](https://forums.swift.org/t/se-0469-task-naming/78509)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0469-task-naming/79438)) ## Introduction From 91f492645f11dbdfba68e93c760855ccedf028c3 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 22 Apr 2025 16:42:35 -0700 Subject: [PATCH 4234/4563] Mark SE-0466 as implemented in Swift 6.2. (#2799) --- proposals/0466-control-default-actor-isolation.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0466-control-default-actor-isolation.md b/proposals/0466-control-default-actor-isolation.md index 51ffc99c7c..8fb8015003 100644 --- a/proposals/0466-control-default-actor-isolation.md +++ b/proposals/0466-control-default-actor-isolation.md @@ -3,9 +3,8 @@ * Proposal: [SE-0466](0466-control-default-actor-isolation.md) * Authors: [Holly Borla](https://github.com/hborla) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Vision: [Improving the approachability of data-race safety](/visions/approachable-concurrency.md) -* Implementation: On `main` behind the `UnspecifiedMeansMainActorIsolated` experimental feature. * Review: ([pitch](https://forums.swift.org/t/pitch-control-default-actor-isolation-inference/77482))([review](https://forums.swift.org/t/se-0466-control-default-actor-isolation-inference/78321))([acceptance](https://forums.swift.org/t/accepted-se-0466-control-default-actor-isolation-inference/78926)) ## Introduction From 53242c83e3c8ab5472ddd78e7b9b4fd1b8086bdb Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Thu, 30 Jan 2025 14:01:13 -0800 Subject: [PATCH 4235/4563] Add proposal text. --- .../NNNN-method-and-initializer-keypaths.md | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 proposals/NNNN-method-and-initializer-keypaths.md diff --git a/proposals/NNNN-method-and-initializer-keypaths.md b/proposals/NNNN-method-and-initializer-keypaths.md new file mode 100644 index 0000000000..40951ad8ba --- /dev/null +++ b/proposals/NNNN-method-and-initializer-keypaths.md @@ -0,0 +1,165 @@ +# Method and Initializer Key Paths + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Amritpan Kaur](https://github.com/amritpan), [Pavel Yaskevich](https://github.com/xedin) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [swiftlang/swift#78823](https://github.com/swiftlang/swift/pull/78823) [swiftsyntax/swiftsyntax#2950](https://github.com/swiftlang/swift-syntax/pull/2950) +* Upcoming Feature Flag: `KeyPathWithMethodMembers` +* Review: ([pitch](https://forums.swift.org/t/pitch-method-key-paths/76678)) + +## Introduction + +Swift key paths can be written to properties and subscripts. This proposal extends key path usage to include references to method members, such as instance and type methods, and initializers. + +## Motivation + +Key paths to method members and their advantages have been explored in several discussions on the Swift forum, specifically to [unapplied instance methods](https://forums.swift.org/t/allow-key-paths-to-reference-unapplied-instance-methods/35582) and to [partially and applied methods](https://forums.swift.org/t/pitch-allow-keypaths-to-represent-functions/67630). Extending key paths to include reference to methods and initializers and handling them similarly to properties and subscripts will unify instance and type member access for a more consistent API. Key path methods and initializers will also enjoy all of the benefits offered by existing key path component kinds, e.g. simplify code by abstracting away details of how these properties/subscripts/methods are modified/accessed/invoked, reusability via generic functions that accept key paths to methods as parameters, and supporting dynamic invocation while maintaining type safety. + +## Proposed solution + +We propose the following usage: + +```swift +struct Calculator { + func square(of number: Int) -> Int { + return number * number * multiplier + } + + func cube(of number: Int) -> Int { + return number * number * number * multiplier + } + + init(multiplier: Int) { + self.multiplier = multiplier + } + + let multiplier: Int +} + +// Key paths to Calculator methods +let squareKeyPath = \Calculator.square +let cubeKeyPath = \Calculator.cube +``` + +These key paths can then be invoked dynamically with a generic function: + +```swift +func invoke(object: T, keyPath: KeyPath U>, param: U) -> U { + return object[keyPath: keyPath](param) +} + +let calc = Calculator(multiplier: 2) + +let squareResult = invoke(object: calc, keyPath: squareKeyPath, param: 3) +let cubeResult = invoke(object: calc, keyPath: cubeKeyPath, param: 3) +``` + +Or used to dynamically create a new instance of Calculator: + +```swift +let initializerKeyPath = \Calculator.Type.init(multiplier: 5) +``` + +This proposed feature homogenizes the treatment of member declarations by extending the expressive power of key paths to method and initializer members. + +## Detailed design + +Key path expressions can refer to instance methods, type methods and initializers, and imitate the syntax of non-key path member references. + +### Argument Application + +Key paths can reference methods in two forms: + +1. Without argument application: The key path represents the unapplied method signature. +2. With argument application: The key path references the method with arguments already applied. + +Continuing our `Calculator` example, we can write either: + +```swift +let squareWithoutArgs: KeyPath Int> = \Calculator.square +let squareWithArgs: KeyPath = \Calculator.square(of: 3) +``` + +If the member is a metatype (e.g., a static method, class method, initializer, or when referring to the type of an instance), you must explicitly include `.Type` in the key path root type. + +```swift +struct Calculator { + func add(_ a: Int, _ b: Int) -> Int { + return a + b + } +} + +let calc = Calculator.self +let addKeyPath: KeyPath (Int, Int) -> Int> = \Calculator.Type.add +``` + +Here, `addKeyPath` is a key path that references the add method of `Calculator` as a metatype member. The key path’s root type is `Calculator.Type`, and it resolves to an unapplied instance method: `(Calculator) -> (Int, Int) -> Int`. This represents a curried function where the first step binds an instance of `Calculator`, and the second step applies the method arguments. + +```swift +let addFunction = calc[keyPath: addKeyPath] +let fullyApplied = addFunction(Calculator())(20, 30)` +``` + +`addFunction` applies an instance of Calculator to the key path method. `fullyApplied` further applies the arguments (20, 30) to produce the final result. + +### Overloads + +Keypaths to methods with the same base name and distinct argument labels can be disambiguated by explicitly including the argument labels: + +```swift +struct Calculator { + var subtract: (Int, Int) -> Int { return { $0 + $1 } } + func subtract(this: Int) -> Int { this + this} + func subtract(that: Int) -> Int { that + that } +} + +let kp1 = \S.subtract // KeyPath Int +let kp2 = \S.subtract(this:) // WritableKeyPath Int> +let kp3 = \S.subtract(that:) // WritableKeyPath Int> +let kp4 = \S.subtract(that: 1) // WritableKeyPath +``` + +### Dynamic member lookups + +`@dynamicMemberLookup` can resolve method references through key paths, allowing methods to be accessed dynamically without explicit function calls: + +```swift +@dynamicMemberLookup +struct DynamicKeyPathWrapper { + var root: Root + + subscript(dynamicMember keyPath: KeyPath) -> Member { + root[keyPath: keyPath] + } +} + +let dynamicCalculator = DynamicKeyPathWrapper(root: Calculator()) +let subtract = dynamicCalculator.subtract +print(subtract(10)) +``` + +### Effectful value types + +Methods annotated with `nonisolated` and `consuming` are supported by this feature. `mutating`, `throwing` and `async` are not supported for any other component type and will similarly not be supported for methods. Keypaths cannot capture method arguments that are not `Hashable`/`Equatable`, so `escaping` is also not supported. + +### Component chaining + +Component chaining between methods or from method to other key path types is also supported with this feature and will continue to behave as `Hashable`/`Equatable` types. + +```swift +let kp5 = \Calculator.subtract(this: 1).signum() +let kp6 = \Calculator.subtract(this: 2).description +``` + +## Source compatibility + +This feature has no effect on source compatibility. + +## ABI compatibility + +This feature does not affect ABI compatibility. + +## Implications on adoption + +This feature has no implications on adoption. From 7a8f5349ba73bd9e2b783afce0f780e4914dd96f Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Tue, 4 Mar 2025 09:17:00 -0800 Subject: [PATCH 4236/4563] Make updates. --- .../NNNN-method-and-initializer-keypaths.md | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/proposals/NNNN-method-and-initializer-keypaths.md b/proposals/NNNN-method-and-initializer-keypaths.md index 40951ad8ba..bed57bc292 100644 --- a/proposals/NNNN-method-and-initializer-keypaths.md +++ b/proposals/NNNN-method-and-initializer-keypaths.md @@ -4,7 +4,7 @@ * Authors: [Amritpan Kaur](https://github.com/amritpan), [Pavel Yaskevich](https://github.com/xedin) * Review Manager: TBD * Status: **Awaiting review** -* Implementation: [swiftlang/swift#78823](https://github.com/swiftlang/swift/pull/78823) [swiftsyntax/swiftsyntax#2950](https://github.com/swiftlang/swift-syntax/pull/2950) +* Implementation: [swiftlang/swift#78823](https://github.com/swiftlang/swift/pull/78823), [swiftlang/swiftsyntax#2950](https://github.com/swiftlang/swift-syntax/pull/2950), [swiftlang/swiftfoundation#1179](https://github.com/swiftlang/swift-foundation/pull/1179) * Upcoming Feature Flag: `KeyPathWithMethodMembers` * Review: ([pitch](https://forums.swift.org/t/pitch-method-key-paths/76678)) @@ -85,27 +85,19 @@ If the member is a metatype (e.g., a static method, class method, initializer, o ```swift struct Calculator { - func add(_ a: Int, _ b: Int) -> Int { + static func add(_ a: Int, _ b: Int) -> Int { return a + b } } -let calc = Calculator.self -let addKeyPath: KeyPath (Int, Int) -> Int> = \Calculator.Type.add +let addKeyPath: KeyPath = \Calculator.Type.add(4, 5) ``` -Here, `addKeyPath` is a key path that references the add method of `Calculator` as a metatype member. The key path’s root type is `Calculator.Type`, and it resolves to an unapplied instance method: `(Calculator) -> (Int, Int) -> Int`. This represents a curried function where the first step binds an instance of `Calculator`, and the second step applies the method arguments. - -```swift -let addFunction = calc[keyPath: addKeyPath] -let fullyApplied = addFunction(Calculator())(20, 30)` -``` - -`addFunction` applies an instance of Calculator to the key path method. `fullyApplied` further applies the arguments (20, 30) to produce the final result. +Here, `addKeyPath` is a key path that references the add method of `Calculator` as a metatype member. The key path’s root type is `Calculator.Type`, and the value resolves to an applied instance method result type of`Int`. ### Overloads -Keypaths to methods with the same base name and distinct argument labels can be disambiguated by explicitly including the argument labels: +Keypaths to methods with the same base name and distinct argument labels can be disambiguated with explicit argument labels: ```swift struct Calculator { @@ -114,10 +106,25 @@ struct Calculator { func subtract(that: Int) -> Int { that + that } } -let kp1 = \S.subtract // KeyPath Int -let kp2 = \S.subtract(this:) // WritableKeyPath Int> -let kp3 = \S.subtract(that:) // WritableKeyPath Int> -let kp4 = \S.subtract(that: 1) // WritableKeyPath +let kp1 = \Calculator.subtract // KeyPath Int +let kp2 = \Calculator.subtract(this:) // KeyPath Int> +let kp3 = \Calculator.subtract(that:) // KeyPath Int> +let kp4 = \Calculator.subtract(that: 1) // KeyPath +``` + +### Implicit closure conversion + +This feature also supports implicit closure conversion of key path methods, allowing them to used in expressions where closures are expected, such as in higher order functions: + +```swift +struct Calculator { + func power(of base: Int, exponent: Int) -> Int { + return Int(pow(Double(base), Double(exponent))) + } +} + +let calculators = [Calculator(), Calculator()] +let results = calculators.map(\.power(of: 2, exponent: 3)) ``` ### Dynamic member lookups @@ -141,7 +148,7 @@ print(subtract(10)) ### Effectful value types -Methods annotated with `nonisolated` and `consuming` are supported by this feature. `mutating`, `throwing` and `async` are not supported for any other component type and will similarly not be supported for methods. Keypaths cannot capture method arguments that are not `Hashable`/`Equatable`, so `escaping` is also not supported. +Methods annotated with `nonisolated` and `consuming` are supported by this feature. However, noncopying root and value types [are not supported](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0437-noncopyable-stdlib-primitives.md#additional-future-work). `mutating`, `throws` and `async` are not supported for any other component type and will similarly not be supported for methods. Additionally keypaths cannot capture closure arguments that are not `Hashable`/`Equatable`. ### Component chaining From 25675132c2fe48131a9cffa339e6e658534116e9 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 22 Apr 2025 16:49:45 -0700 Subject: [PATCH 4237/4563] Mark SE-0463 as implemented in Swift 6.2. (#2800) --- proposals/0463-sendable-completion-handlers.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0463-sendable-completion-handlers.md b/proposals/0463-sendable-completion-handlers.md index 498ab8ba80..85729b6ae5 100644 --- a/proposals/0463-sendable-completion-handlers.md +++ b/proposals/0463-sendable-completion-handlers.md @@ -3,9 +3,8 @@ * Proposal: [SE-0463](0463-sendable-completion-handlers.md) * Authors: [Holly Borla](https://github.com/hborla) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md) -* Implementation: On `main` behind `-enable-experimental-feature SendableCompletionHandlers` * Review: ([pitch](https://forums.swift.org/t/pitch-import-objective-c-completion-handler-parameters-as-sendable/77904)) ([review](https://forums.swift.org/t/se-0463-import-objective-c-completion-handler-parameters-as-sendable/78169)) ([acceptance](https://forums.swift.org/t/accepted-se-0463-import-objective-c-completion-handler-parameters-as-sendable/78489)) ## Introduction From 8ad47c44227d24f59b5865f0d0a4b090dc6d8f64 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 23 Apr 2025 08:54:51 +0900 Subject: [PATCH 4238/4563] Update SE-0471 to return Bool? SE review follow up, moving to a simpler model of detecting if we were able to determine the isolation without crashing. --- proposals/0471-SerialExecutor-isIsolated.md | 34 +++++---------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/proposals/0471-SerialExecutor-isIsolated.md b/proposals/0471-SerialExecutor-isIsolated.md index 539f1adc84..85dbd1b3a2 100644 --- a/proposals/0471-SerialExecutor-isIsolated.md +++ b/proposals/0471-SerialExecutor-isIsolated.md @@ -87,7 +87,7 @@ protocol SerialExecutor { extension SerialExecutor { /// Default implementation for backwards compatibility. - func isIsolatingCurrentContext() -> Bool { false } + func isIsolatingCurrentContext() -> Bool? { nil } } ``` @@ -101,8 +101,6 @@ The newly proposed `isIsolatingCurrentContext()` function participates in the pr ![diagram illustrating which method is called when](0471-is-isolated-flow.png) - - There are a lot of conditions here and availability of certain features also impacts this decision flow, so it is best to refer to the diagram for detailed analysis of every situation. However the most typical situation involves executing on a task, which has a potentially different executor than the `expected` one. In such situation the runtime will: - check for the existence of a "current" task, @@ -125,15 +123,17 @@ This proposal specifically adds the "if `isIsolatingCurrentContext` is available If `isIsolatingCurrentContext` is available, effectively it replaces `checkIsolated` because it does offer a sub-par error message experience and is not able to offer a warning if Swift would be asked to check the isolation but not crash upon discovering a violation. -### Detecting the `isIsolatingCurrentContext` checking mode +### The `isIsolatingCurrentContext` checking mode -The `isIsolatingCurrentContext` method effectively replaces the `checkIsolated` method, because it can answer the same question _if it is implemented_. +The `isIsolatingCurrentContext` method effectively replaces the `checkIsolated` method, because it can answer the same question if it is implemented. Some runtimes may not be able to implement a the returning `isIsolatingCurrentContext`, and they are not required to implement the new protocol requirement. -The general guidance about which method to implement is to implement `isIsolatingCurrentContext` whenever possible. This method can be used by the Swift runtime in "warning mode". When running a check in this mode, the `checkIsolated` method cannot and will not be used because it would cause an unexpected crash. A runtime may still want to implement the `checkIsolated` function if it truly is unable to return a true/false response to the isolation question, but can only assert on an illegal state. This function will not be used when the runtime does not expect a potential for a crash. +The default implementation returns `nil` which is to be interpreted by the runtime as "unknown" or "unable to confirm the isolation", and the runtime may proceeed to call futher isolation checking APIs when this function returned `nil`. + +The general guidance about which method to implement is to implement `isIsolatingCurrentContext` whenever possible. This method can be used by the Swift runtime in "warning mode". When running a check in this mode, the `checkIsolated` method cannot and will not be used because it would cause an unexpected crash. An executor may still want to implement the `checkIsolated` function if it truly is unable to return a true/false response to the isolation question, but can only assert on an illegal state. The `checkIsolated` function will not be used when the runtime cannot tollerate the potential of crashing while performing an isolation check (e.g. isolated conformance checks, or when issuing warnings). -The presence of a non-default implementation of the `isIsolatingCurrentContext` protocol witness is detected by the compiler and the runtime can detect this information in order to determine if the new function should be used for these checks. In other words, if there is an implementation of the requirement available _other than_ the default one provided in the concurrency library, the runtime will attempt to use this method _over_ the `checkIsolated` API. This allows for a smooth migration to the new API, and enables the use of this method in if the runtime would like issue a check that cannot cause a crash. +The runtime will always invoke the `isIsolatingCurrentContext` before making attempts to call `checkIsolated`, and if the prior returns either `true` or `false`, the latter (`checkIsolated`) will not be invoked at all. ### Compatibility strategy for custom SerialExecutor authors @@ -169,25 +169,7 @@ This would be ideal, however also problematic since changing a protocol requirem In order to make adoption of this new mode less painful and not cause deprecation warnings to libraries which intend to support multiple versions of Swift, the `SerialExcecutor/checkIsolated` protocol requirement remains _not_ deprecated. It may eventually become deprecated in the future, but right now we have no plans of doing so. -### Offer a tri-state return value rather than `Bool` - -We briefly considered offering a tri-state `enum DetectedSerialExecutorIsolation` as the return value of `isIsolatingCurrentContext`, however could not find many realistic use-cases for it. - -The return type could be defined as: - -```swift -// not great name -enum DetectedSerialExecutorIsolation { - case isolated // returned when isolated by this executor - case notIsolated // returned when definitely NOT isolated by this executor - case unknown // when the isIsolatingCurrentContext could not determine if the caller is isolated or not -} -``` - -If we used the `.unknown` as default implementation of the new protocol requirement, this would allow for programatic detection if we called the default implementation, or an user provided implementation which could check a proper isolated/not-isolated state of the executing context. - -Technically there may exist new implementations which return the `.unknown` however it would have to be treated defensively as `.notIsolated` in any asserting APIs or other use-cases which rely on this check for runtime correctness. We are uncertain if introducing this tri-state is actually helpful in real situations and therefore the proposal currently proposes the use of a plain `Bool` value. - ## Changelog +- changed return value of `isIsolatingCurrentContext` from `Bool` to `Bool?`, where the `nil` is to be interpreted as "unknown", and the default implementation of `isIsolatingCurrentContext` now returns `nil`. - removed the manual need to signal to the runtime that the specific executor supports the new checking mode. It is now detected by the compiler and runtime, checking for the presence of a non-default implementation of the protocol requirement. From be98eb82098a38cf0e20320544bc428e733deba1 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Tue, 22 Apr 2025 16:57:29 -0700 Subject: [PATCH 4239/4563] Assign SE-0479 and schedule its review --- ...aths.md => 0479-method-and-initializer-keypaths.md} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename proposals/{NNNN-method-and-initializer-keypaths.md => 0479-method-and-initializer-keypaths.md} (93%) diff --git a/proposals/NNNN-method-and-initializer-keypaths.md b/proposals/0479-method-and-initializer-keypaths.md similarity index 93% rename from proposals/NNNN-method-and-initializer-keypaths.md rename to proposals/0479-method-and-initializer-keypaths.md index bed57bc292..2315599438 100644 --- a/proposals/NNNN-method-and-initializer-keypaths.md +++ b/proposals/0479-method-and-initializer-keypaths.md @@ -1,11 +1,11 @@ # Method and Initializer Key Paths -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0479](0479-method-and-initializer-keypaths.md) * Authors: [Amritpan Kaur](https://github.com/amritpan), [Pavel Yaskevich](https://github.com/xedin) -* Review Manager: TBD -* Status: **Awaiting review** -* Implementation: [swiftlang/swift#78823](https://github.com/swiftlang/swift/pull/78823), [swiftlang/swiftsyntax#2950](https://github.com/swiftlang/swift-syntax/pull/2950), [swiftlang/swiftfoundation#1179](https://github.com/swiftlang/swift-foundation/pull/1179) -* Upcoming Feature Flag: `KeyPathWithMethodMembers` +* Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) +* Status: **Active Review (April 22 ... May 5, 2025)** +* Implementation: [swiftlang/swift#78823](https://github.com/swiftlang/swift/pull/78823), [swiftlang/swiftsyntax#2950](https://github.com/swiftlang/swift-syntax/pull/2950), [swiftlang/swiftfoundation#1179](https://github.com/swiftlang/swift-foundation/pull/1179) +* Experimental Feature Flag: `KeyPathWithMethodMembers` * Review: ([pitch](https://forums.swift.org/t/pitch-method-key-paths/76678)) ## Introduction From 3229a3a89892192507fba100444d8d12a1499ee2 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Wed, 23 Apr 2025 09:36:16 +0900 Subject: [PATCH 4240/4563] updat diagram as well --- proposals/0471-is-isolated-flow.graffle | Bin 170172 -> 170150 bytes proposals/0471-is-isolated-flow.png | Bin 84498 -> 84869 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/proposals/0471-is-isolated-flow.graffle b/proposals/0471-is-isolated-flow.graffle index 9215bacf9e3508f078ca6b7837a01ee2c143568a..05f3a5aa8d609d77bdf5e16bfaced49f62396f60 100644 GIT binary patch delta 150848 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

      wQmFreQ^G$GVo0DIg?+d4B+-7`$>6 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 delta 150826 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�t{WX^xZ7 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 d00000000000001_fmD})m;xULG?)Sa002*fY%~A> diff --git a/proposals/0471-is-isolated-flow.png b/proposals/0471-is-isolated-flow.png index 97db91af276480fdef60f67090f87904e26389c5..bdf5bb0ba8fcef956c2260bd085be3e9517bb7c2 100644 GIT binary patch delta 57613 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 z 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 z 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�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 e{oxaQ Date: Tue, 22 Apr 2025 17:37:14 -0700 Subject: [PATCH 4241/4563] Start SE-0479 --- proposals/0479-method-and-initializer-keypaths.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0479-method-and-initializer-keypaths.md b/proposals/0479-method-and-initializer-keypaths.md index 2315599438..a4f352978e 100644 --- a/proposals/0479-method-and-initializer-keypaths.md +++ b/proposals/0479-method-and-initializer-keypaths.md @@ -6,7 +6,7 @@ * Status: **Active Review (April 22 ... May 5, 2025)** * Implementation: [swiftlang/swift#78823](https://github.com/swiftlang/swift/pull/78823), [swiftlang/swiftsyntax#2950](https://github.com/swiftlang/swift-syntax/pull/2950), [swiftlang/swiftfoundation#1179](https://github.com/swiftlang/swift-foundation/pull/1179) * Experimental Feature Flag: `KeyPathWithMethodMembers` -* Review: ([pitch](https://forums.swift.org/t/pitch-method-key-paths/76678)) +* Review: ([pitch](https://forums.swift.org/t/pitch-method-key-paths/76678)) ([review](https://forums.swift.org/t/se-0479-method-and-initializer-key-paths/79457)) ## Introduction From 882598a72217fa8aadc091b617d544b3983e97f3 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 26 Mar 2025 11:13:26 -0700 Subject: [PATCH 4242/4563] OutputSpan and OutputRawSpan --- proposals/nnnn-outputspan.md | 622 +++++++++++++++++++++++++++++++++++ 1 file changed, 622 insertions(+) create mode 100644 proposals/nnnn-outputspan.md diff --git a/proposals/nnnn-outputspan.md b/proposals/nnnn-outputspan.md new file mode 100644 index 0000000000..b1488126d7 --- /dev/null +++ b/proposals/nnnn-outputspan.md @@ -0,0 +1,622 @@ +# OutputSpan: delegate initialization of contiguous memory + +* Proposal: TBD +* Author: [Guillaume Lessard](https://github.com/glessard) +* Review Manager: TBD +* Status: **Pitch** +* Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) +* Implementation: "Future" target of [swift-collections](https://github.com/apple/swift-collections/tree/future) +* Review: [Pitch](https://forums.swift.org/) + +[SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md +[SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md +[SE-0456]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0456-stdlib-span-properties.md +[SE-0467]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0467-MutableSpan.md +[PR-LifetimeAnnotations]: https://github.com/swiftlang/swift-evolution/pull/2750 +[SE-0453]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0453-vector.md + +## Introduction + +Following the introduction of [`Span`][SE-0447] and [`MutableSpan`][SE-0467], this proposal adds a general facility for initialization of exclusively-borrowed memory with the `OutputSpan` and `OutputRawSpan` types. + +## Motivation + +Some standard library container types can delegate initialization of some or all of their storage to user code. Up to now, it has only been possible to do so with explicitly unsafe ways, which have also proven error-prone. The standard library provides this unsafe functionality with the closure-taking initializers `Array.init(unsafeUninitializedCapacity:initializingWith:)` and `String.init(unsafeUninitializedCapacity:initializingUTF8With:)`. + +These functions have a few different drawbacks, most prominently their reliance on unsafe types, which makes them unpalatable in security-conscious environments. We continue addressing these issues with `OutputSpan` and `OutputRawSpan`, new non-copyable and non-escapable types that manage initialization of typed and untyped memory. + +In addition to the new types, we will propose adding new API for some standard library types to take advantage of `OutputSpan` and `OutputRawSpan`, and improve upon the `Array` and `String` initializers mentioned above. + +## Proposed solution + +#### OutputSpan + +`OutputSpan` allows delegating the initialization of a type's memory, by providing access to an exclusively-borrowed view of a range of contiguous memory. `OutputSpan`'s contiguous memory consists of a prefix of initialized memory, followed by a suffix of uninitialized memory. Like `MutableSpan`, `OutputSpan` relies on two guarantees: (a) that it has exclusive access to the range of memory it represents, and (b) that the memory locations it represents will remain valid for the duration of the access. These guarantee data race safety and temporal safety. `OutputSpan` performs bounds-checking on every access to preserve spatial safety. + +An `OutputSpan` provided by a container represents a mutation of that container, and is therefore an exclusive access. + +#### OutputRawSpan + +`OutputRawSpan` allows delegating the initialization of heterogeneously-typed memory, such as memory being prepared by an encoder. It makes the same safety guarantees as `OutputSpan`. + +#### Extensions to standard library types + +The standard library will provide new container initializers that delegate to an `OutputSpan`. Delegated initialization generally requires a container to perform some operations after the initialization has happened. In the case of `Array` this is simply noting the number of initialized elements; in the case of `String` this consists of validating the input. This post-processing implies the need for a scope, and we believe that scope is best represented by a closure. The `Array` initializer will be as follows: + +```swift +extension Array { + public init( + capacity: Int, + initializingWith: (_ span: inout OutputSpan) throws(E) -> Void + ) throws(E) +} +``` + +We will also extend `String` and `InlineArray` in a similar manner. + +#### `@lifetime` attribute + +Some of the API presented here must establish a lifetime relationship between a non-escapable returned value and a callee binding. This relationship will be illustrated using the `@lifetime` attribute pitched in [PR-LifetimeAnnotations]. For the purposes of this proposal, the lifetime attribute ties the lifetime of a function's return value to one of its input parameters. + +Note: The `@lifetime` attribute is not final. The eventual lifetime annotations proposal may or may not adopt this syntax. We expect that, as soon as Swift adopts a syntax do describe lifetime dependencies, the Standard Library will be modified to adopt that new syntax. + +## Detailed Design + +#### OutputSpan + +`OutputSpan` is a simple representation of a partially-initialized region of memory. It is non-copyable in order to enforce exclusive access for mutations of its memory, as required by the law of exclusivity: + +````swift +@frozen +public struct OutputSpan: ~Copyable, ~Escapable { + internal let _start: UnsafeMutableRawPointer? + public let capacity: Int + internal var _count: Int +} +```` + +The memory represented by an `OutputSpan` instance consists of `count` initialized instances of `Element`, followed by uninitialized memory with storage space for `capacity - count` additional elements of `Element`. + +```swift +extension OutputSpan where Element: ~Copyable { + /// The number of initialized elements in this `OutputSpan`. + public var count: Int { get } + + /// A Boolean value indicating whether the span is empty. + public var isEmpty: Bool { get } + + /// A Boolean value indicating whether the span is full. + public var isFull: Bool { get } + + /// The number of additional elements that can be added to this `OutputSpan` + public var freeCapacity: Int { get } // capacity - count +} +``` + +##### Single-element operations + +The basic operation supported by `OutputSpan` is appending an element. When an element is appended, the correct amount of memory needed to represent it is initialized, and the `count` property is incremented by 1. If the `OutputSpan` has no available space (`capacity == count`), this operation traps. +```swift +extension OutputSpan where Element: ~Copyable { + /// Append a single element to this `OutputSpan`. + @lifetime(self: copy self) + public mutating func append(_ value: consuming Element) +} +``` +The converse operation `removeLast()` is also supported, and returns the removed element if `count` was greater than zero. +```swift +extension OutputSpan where Element: ~Copyable { + /// Remove the last initialized element from this `OutputSpan`. + @discardableResult + @lifetime(self: copy self) + public mutating func removeLast() -> Element? +} +``` + +##### Bulk initialization of an `OutputSpan`'s memory: + +We include functions to perform bulk initialization of the memory represented by an `OutputSpan`. Initializing an `OutputSpan` from known-sized sources (such as `Collection` or `Span`) uses every element of the source. It is an error to do so when the available storage of the `OutputSpan` is too little to contain every element from the source. Initializing an `OutputSpan` from `Sequence` or `IteratorProtocol` will copy as many items as possible, either until the input is empty or the `OutputSpan`'s available storage is zero. + +```swift +extension OutputSpan { + /// Initialize this span's suffix to the repetitions of the given value. + @lifetime(self: copy self) + public mutating func append(repeating repeatedValue: Element, count: Int) + + /// Initialize this span's suffix with the elements from the source. + /// + /// Returns true if the iterator has filled all the free capacity in the span. + @discardableResult + @lifetime(self: copy self) + public mutating func append( + from source: inout some IteratorProtocol + ) -> Bool + + /// Initialize this span's suffix with every element of the source. + @lifetime(self: copy self) + public mutating func append( + fromContentsOf source: some Sequence + ) + + /// Initialize this span's suffix with every element of the source. + @lifetime(self: copy self) + public mutating func append( + fromContentsOf source: Span + ) + + /// Initialize this span's suffix with every element of the source. + @lifetime(self: copy self) + public mutating func append( + fromContentsOf source: UnsafeBufferPointer + ) +} + +extension OutputSpan where Element: ~Copyable { + /// Initialize this span's suffix by moving every element from the source. + @lifetime(self: copy self) + public mutating func moveAppend( + fromContentsOf source: inout OutputSpan + ) + + /// Initialize this span's suffix by moving every element from the source. + @lifetime(self: copy self) + public mutating func moveAppend( + fromContentsOf source: UnsafeMutableBufferPointer + ) +} + +extension OutputSpan { + /// Initialize this span's suffix by moving every element from the source. + @lifetime(self: copy self) + public mutating func moveAppend( + fromContentsOf source: Slice> + ) +} +``` + +A bulk operation to deinitialize all of an `OutputSpan`'s memory is also available: +```swift +extension OutputSpan where Element: ~Copyable { + /// Remove all this span's elements and return its memory to the uninitialized state. + @lifetime(self: copy self) + public mutating func removeAll() +} +``` + +##### Accessing an `OutputSpan`'s initialized memory: + +The initialized elements are accessible for read-only or mutating access via the `span` and `mutableSpan` properties: + +```swift +extension OutputSpan where Element: ~Copyable { + /// Borrow the underlying initialized memory for read-only access. + public var span: Span { + @lifetime(borrow self) borrowing get + } + + /// Exclusively borrow the underlying initialized memory for mutation. + public mutating var mutableSpan: MutableSpan { + @lifetime(&self) mutating get + } +} +``` + + + +##### Interoperability with unsafe code + +We provide a method to process or populate an `OutputSpan` using unsafe operations, which can also be used for out-of-order initialization. + +```swift +extension OutputSpan where Element: ~Copyable { + /// Call the given closure with the unsafe buffer pointer addressed by this + /// OutputSpan and a mutable reference to its count of initialized elements. + /// + /// This method provides a way to process or populate an `OutputSpan` using + /// unsafe operations, such as dispatching to code written in legacy + /// (memory-unsafe) languages. + /// + /// The supplied closure may process the buffer in any way it wants; however, + /// when it finishes (whether by returning or throwing), it must leave the + /// buffer in a state that satisfies the invariants of the output span: + /// + /// 1. The inout integer passed in as the second argument must be the exact + /// number of initialized items in the buffer passed in as the first + /// argument. + /// 2. These initialized elements must be located in a single contiguous + /// region starting at the beginning of the buffer. The rest of the buffer + /// must hold uninitialized memory. + /// + /// This function cannot verify these two invariants, and therefore + /// this is an unsafe operation. Violating the invariants of `OutputSpan` + /// may result in undefined behavior. + @lifetime(self: copy self) + public mutating func withUnsafeMutableBufferPointer( + _ body: ( + UnsafeMutableBufferPointer, + _ initializedCount: inout Int + ) throws(E) -> R + ) throws(E) -> R +} +``` + + + +##### Creating an `OutputSpan` instance: + +Creating an `OutputSpan` is an unsafe operation. It requires having knowledge of the initialization state of the range of memory being targeted. The range of memory must be in two regions: the first region contains initialized instances of `Element`, and the second region is uninitialized. The number of initialized instances is passed to the `OutputSpan` initializer through its `initializedCount` argument. + +```swift +extension OutputSpan where Element: ~Copyable { + /// Unsafely create an OutputSpan over partly-initialized memory. + /// + /// The memory in `buffer` must remain valid throughout the lifetime + /// of the newly-created `OutputSpan`. Its prefix must contain + /// `initializedCount` initialized instances, followed by uninitialized + /// memory. + /// + /// - Parameters: + /// - buffer: an `UnsafeMutableBufferPointer` to be initialized + /// - initializedCount: the number of initialized elements + /// at the beginning of `buffer`. + @unsafe + @lifetime(borrow buffer) + public init( + buffer: UnsafeMutableBufferPointer, + initializedCount: Int + ) + + /// Create an OutputSpan with zero capacity + @lifetime(immortal) + public init() +} + +extension OutputSpan { + /// Unsafely create an OutputSpan over partly-initialized memory. + /// + /// The memory in `buffer` must remain valid throughout the lifetime + /// of the newly-created `OutputSpan`. Its prefix must contain + /// `initializedCount` initialized instances, followed by uninitialized + /// memory. + /// + /// - Parameters: + /// - buffer: an `UnsafeMutableBufferPointer` to be initialized + /// - initializedCount: the number of initialized elements + /// at the beginning of `buffer`. + @unsafe + @lifetime(borrow buffer) + public init( + buffer: borrowing Slice>, + initializedCount: Int + ) +} +``` + + + +##### Retrieving initialized memory from an `OutputSpan` + +Once memory has been initialized using `OutputSpan`, the owner of the memory must consume the `OutputSpan` in order to retake ownership of the initialized memory. The owning type must pass the memory used to initialize the `OutputSpan` to the `finalize(for:)` function. Passing the wrong buffer is a programmer error and the function traps. `finalize()` consumes the `OutputSpan` instance and returns the number of initialized elements. + +```swift +extension OutputSpan where Element: ~Copyable { + /// Consume the OutputSpan and return the number of initialized elements. + /// + /// Parameters: + /// - buffer: The buffer being finalized. This must be the same buffer as used to + /// initialize the `OutputSpan` instance. + /// Returns: The number of elements that were initialized. + @unsafe + public consuming func finalize( + for buffer: UnsafeMutableBufferPointer + ) -> Int +} + +extension OutputSpan { + /// Consume the OutputSpan and return the number of initialized elements. + /// + /// Parameters: + /// - buffer: The buffer being finalized. This must be the same buffer as used to + /// initialize the `OutputSpan` instance. + /// Returns: The number of bytes that were initialized. + @unsafe + public consuming func finalize( + for buffer: Slice> + ) -> Int +} +``` + + + + +#### `OutputRawSpan` +`OutputRawSpan` is similar to `OutputSpan`, but represents an untyped partially-initialized region of memory. Its API supports appending the bytes of instances of `BitwiseCopyable` types, as well as a variety of bulk initialization operations. +```swift +@frozen +public struct OutputRawSpan: ~Copyable, ~Escapable { + internal var _start: UnsafeMutableRawPointer? + public let capacity: Int + internal var _count: Int +} +``` + +The memory represented by an `OutputRawSpan` contains `byteCount` initialized bytes, followed by uninitialized memory. + +```swift +extension OutputRawSpan { + /// The number of initialized bytes in this `OutputRawSpan` + public var byteCount: Int { get } + + /// A Boolean value indicating whither the span is empty. + public var isEmpty: Bool { get } + + /// A Boolean value indicating whither the span is full. + public var isFull: Bool { get } + + /// The nmuber of uninitialized bytes remaining in this `OutputRawSpan` + public var available: Int { get } // capacity - byteCount +} +``` + + + +##### Appending to `OutputRawSpan` + +The basic operation is to append the bytes of some value to an `OutputRawSpan`: +```swift +extension OutputRawSpan { + /// Appends the given value's bytes to this span's initialized bytes + @lifetime(self: copy self) + public mutating func appendBytes( + of value: T, as type: T.Type + ) +} +``` + +This is also supported with bulk operations. Initializing an `OutputRawSpan` from known-sized sources (such as `Collection` or `Span`) uses every element of the source. It is an error to do so when the available storage of the `OutputRawSpan` is too little to contain every element from the source. Initializing an `OutputRawSpan` from `Sequence` or `IteratorProtocol` will copy as many items as possible, either until the input is empty or the `OutputRawSpan` has too few bytes available to store another element. +```swift +extension OutputRawSpan + /// Initialize this span's suffix to the repetitions of the given value's bytes. + @lifetime(self: copy self) + public mutating func append(repeating repeatedValue: T, count: Int) + + /// Initialize the span's bytes with the bytes of the elements of the source. + /// + /// Returns true if the iterator has filled all the free capacity in the span. + @lifetime(self: copy self) + public mutating func append( + from source: inout some IteratorProtocol + ) -> Bool + + /// Initialize the span's bytes with every byte of the source. + @lifetime(self: copy self) + public mutating func append( + fromContentsOf source: some Sequence + ) + + /// Initialize the span's bytes with every byte of the source. + @lifetime(self: copy self) + public mutating func append( + fromContentsOf source: Span + ) + + /// Initialize the span's bytes with every byte of the source. + @lifetime(self: copy self) + public mutating func append( + fromContentsOf source: RawSpan + ) + + /// Initialize the span's bytes with every byte of the source. + @lifetime(self: copy self) + public mutating func append( + fromContentsOf source: UnsafeRawBufferPointer + ) +} +``` + +An `OutputRawSpan`'s initialized memory is accessible for read-only or mutating access via the `bytes` and `mutableBytes` properties: + +```swift +extension OutputRawSpan { + /// Borrow the underlying initialized memory for read-only access. + public var bytes: RawSpan { + @lifetime(borrow self) borrowing get + } + + /// Exclusively borrow the underlying initialized memory for mutation. + public var mutableBytes: MutableRawSpan { + @lifetime(&self) mutating get + } +} +``` + + + +##### Interoperability with unsafe code + +We provide a method to process or populate an `OutputRawSpan` using unsafe operations, which can also be used for out-of-order initialization. + +```swift +extension OutputRawSpan { + /// Call the given closure with the unsafe buffer pointer addressed by this + /// OutputRawSpan and a mutable reference to its count of initialized bytes. + /// + /// This method provides a way to process or populate an `OutputRawSpan` using + /// unsafe operations, such as dispatching to code written in legacy + /// (memory-unsafe) languages. + /// + /// The supplied closure may process the buffer in any way it wants; however, + /// when it finishes (whether by returning or throwing), it must leave the + /// buffer in a state that satisfies the invariants of the output span: + /// + /// 1. The inout integer passed in as the second argument must be the exact + /// number of initialized bytes in the buffer passed in as the first + /// argument. + /// 2. These initialized elements must be located in a single contiguous + /// region starting at the beginning of the buffer. The rest of the buffer + /// must hold uninitialized memory. + /// + /// This function cannot verify these two invariants, and therefore + /// this is an unsafe operation. Violating the invariants of `OutputRawSpan` + /// may result in undefined behavior. + @lifetime(self: copy self) + public mutating func withUnsafeMutableBytes( + _ body: ( + UnsafeMutableRawBufferPointer, + _ initializedCount: inout Int + ) throws(E) -> R + ) throws(E) -> R +} +``` + + + +##### Creating `OutputRawSpan` instances + +Creating an `OutputRawSpan` is an unsafe operation. It requires having knowledge of the initialization state of the range of memory being targeted. The range of memory must be in two regions: the first region contains initialized bytes, and the second region is uninitialized. The number of initialized bytes is passed to the `OutputRawSpan` initializer through its `initializedCount` argument. + +```swift +extension OutputRawSpan { + + /// Unsafely create an OutputRawSpan over partly-initialized memory. + /// + /// The memory in `buffer` must remain valid throughout the lifetime + /// of the newly-created `OutputRawSpan`. Its prefix must contain + /// `initializedCount` initialized bytes, followed by uninitialized + /// memory. + /// + /// - Parameters: + /// - buffer: an `UnsafeMutableBufferPointer` to be initialized + /// - initializedCount: the number of initialized elements + /// at the beginning of `buffer`. + @unsafe + @lifetime(borrow buffer) + public init( + buffer: UnsafeMutableRawBufferPointer, + initializedCount: Int + ) + + /// Create an OutputRawSpan with zero capacity + @lifetime(immortal) + public init() + + /// Unsafely create an OutputRawSpan over partly-initialized memory. + /// + /// The memory in `buffer` must remain valid throughout the lifetime + /// of the newly-created `OutputRawSpan`. Its prefix must contain + /// `initializedCount` initialized bytes, followed by uninitialized + /// memory. + /// + /// - Parameters: + /// - buffer: an `UnsafeMutableBufferPointer` to be initialized + /// - initializedCount: the number of initialized elements + /// at the beginning of `buffer`. + @unsafe + @lifetime(borrow buffer) + public init( + buffer: Slice, + initializedCount: Int + ) +} +``` + +##### Retrieving initialized memory from an `OutputRawSpan` + +Once memory has been initialized using `OutputRawSpan`, the owner of the memory must consume the instance in order to retake ownership of the initialized memory. The owning type must pass the memory used to initialize the `OutputRawSpan` to the `finalize(for:)` function. Passing the wrong buffer is a programmer error and the function traps. `finalize()` consumes the `OutputRawSpan` instance and returns the number of initialized bytes. + +```swift +extension OutputRawSpan { + /// Consume the OutputRawSpan and return the number of initialized bytes. + /// + /// Parameters: + /// - buffer: The buffer being finalized. This must be the same buffer as used to + /// create the `OutputRawSpan` instance. + /// Returns: The number of initialized bytes. + @unsafe + public consuming func finalize( + for buffer: UnsafeMutableRawBufferPointer + ) -> Int + + /// Consume the OutputRawSpan and return the number of initialized bytes. + /// + /// Parameters: + /// - buffer: The buffer being finalized. This must be the same buffer as used to + /// create the `OutputRawSpan` instance. + /// Returns: The number of initialized bytes. + @unsafe + public consuming func finalize( + for buffer: Slice + ) -> Int +} +``` + + + +#### Extensions to Standard Library types + +The standard library and Foundation will add a few initializers that enable initialization in place, intermediated by an `OutputSpan` instance, passed as a parameter to a closure: + +```swift +extension Array { + public init( + capacity: Int, + initializingWith initializer: (inout OutputSpan) throws(E) -> Void + ) throws(E) +} + +extension String { + public init( + capacity: Int, + initializingUTF8With initializer: (inout OutputSpan) throws(E) -> Void + ) + + public init?( + capacity: Int, + initializingValidUTF8With initializer: (inout OutputSpan) throws(E) -> Void + ) throws(E) +} + +extension InlineArray { + public init( + initializingWith initializer: (inout OutputSpan) throws(E) -> Void + ) throws(E) +} +``` + +#### Extensions to `Foundation.Data` + +While the `swift-foundation` package and the `Foundation` framework are not governed by the Swift evolution process, `Data` is similar in use to standard library types, and the project acknowledges that it is desirable for it to have similar API when appropriate. Accordingly, we plan to propose the following additions to `Foundation.Data`: +```swift +extension Data { + public init( + capacity: Int, + initializingWith initializer: (inout OutputSpan) throws(E) -> Void + ) throws(E) + + public init( + rawCapacity: Int, + initializingWith initializer: (inout OutputRawSpan) throws(E) -> Void + ) throws(E) +} +``` + +## Source compatibility + +This proposal is additive and source-compatible with existing code. + +## ABI compatibility + +This proposal is additive and ABI-compatible with existing code. + +## Implications on adoption + +The additions described in this proposal require a new version of the Swift standard library and runtime. + +## Future directions + +#### Helpers to initialize memory in an arbitrary order + +Some applications may benefit from the ability to initialize a range of memory in a different order than implemented by `OutputSpan`. This may be from back-to-front or even arbitrary order. There are many possible forms such an initialization helper can take, depending on how much memory safety the application is willing to give up in the process of initializing the memory. At the unsafe end, this can be delegating to an `UnsafeMutableBufferPointer` along with a set of requirements; this option is proposed here. At the safe end, this could be delegating to a data structure which keeps track of initialized memory using a bitmap. It is unclear how much need there is for this more heavy-handed approach, so we leave it as a future enhancement if it is deemed useful. + +## Acknowledgements + From 7c44259ae0fba3808bd5a7443ba078ede37a785f Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 23 Apr 2025 11:25:38 -0700 Subject: [PATCH 4243/4563] Proposal edits --- proposals/nnnn-outputspan.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/nnnn-outputspan.md b/proposals/nnnn-outputspan.md index b1488126d7..1423d3b02a 100644 --- a/proposals/nnnn-outputspan.md +++ b/proposals/nnnn-outputspan.md @@ -13,6 +13,7 @@ [SE-0456]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0456-stdlib-span-properties.md [SE-0467]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0467-MutableSpan.md [PR-LifetimeAnnotations]: https://github.com/swiftlang/swift-evolution/pull/2750 +[Forum-LifetimeAnnotations]: https://forums.swift.org/t/78638 [SE-0453]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0453-vector.md ## Introduction @@ -56,9 +57,9 @@ We will also extend `String` and `InlineArray` in a similar manner. #### `@lifetime` attribute -Some of the API presented here must establish a lifetime relationship between a non-escapable returned value and a callee binding. This relationship will be illustrated using the `@lifetime` attribute pitched in [PR-LifetimeAnnotations]. For the purposes of this proposal, the lifetime attribute ties the lifetime of a function's return value to one of its input parameters. +Some of the API presented here must establish a lifetime relationship between a non-escapable returned value and a callee binding. This relationship will be illustrated using the `@lifetime` attribute recently [pitched][PR-LifetimeAnnotations] and [formalized][Forum-LifetimeAnnotations]. For the purposes of this proposal, the lifetime attribute ties the lifetime of a function's return value to one of its input parameters. -Note: The `@lifetime` attribute is not final. The eventual lifetime annotations proposal may or may not adopt this syntax. We expect that, as soon as Swift adopts a syntax do describe lifetime dependencies, the Standard Library will be modified to adopt that new syntax. +Note: The eventual lifetime annotations proposal may adopt a syntax different than the syntax used here. We expect that the Standard Library will be modified to adopt an updated lifetime dependency syntax as soon as it is finalized. ## Detailed Design @@ -434,7 +435,7 @@ extension OutputRawSpan { ##### Interoperability with unsafe code -We provide a method to process or populate an `OutputRawSpan` using unsafe operations, which can also be used for out-of-order initialization. +We provide a method to process or populate an `OutputRawSpan` using unsafe operations, which can also be used for out-of-order initialization. ```swift extension OutputRawSpan { From d0426fbf5050f094dfcde9c4052cf9d02313e9d1 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 23 Apr 2025 13:09:11 -0700 Subject: [PATCH 4244/4563] Proposal edits, append-in-place additions --- proposals/nnnn-outputspan.md | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/proposals/nnnn-outputspan.md b/proposals/nnnn-outputspan.md index 1423d3b02a..cc7a18beac 100644 --- a/proposals/nnnn-outputspan.md +++ b/proposals/nnnn-outputspan.md @@ -53,7 +53,7 @@ extension Array { } ``` -We will also extend `String` and `InlineArray` in a similar manner. +We will also extend `String`, `UnicodeScalarView` and `InlineArray` with similar initializers, and add append-in-place operations where appropriate. #### `@lifetime` attribute @@ -61,7 +61,7 @@ Some of the API presented here must establish a lifetime relationship between a Note: The eventual lifetime annotations proposal may adopt a syntax different than the syntax used here. We expect that the Standard Library will be modified to adopt an updated lifetime dependency syntax as soon as it is finalized. -## Detailed Design +## Detailed Design #### OutputSpan @@ -563,18 +563,45 @@ extension Array { capacity: Int, initializingWith initializer: (inout OutputSpan) throws(E) -> Void ) throws(E) + + public mutating func append( + addingCapacity: Int, + initializingWith initializer: (inout OutputSpan) throws(E) -> Void + ) throws(E) } extension String { public init( - capacity: Int, + utf8Capacity: Int, initializingUTF8With initializer: (inout OutputSpan) throws(E) -> Void ) public init?( - capacity: Int, + utf8Capacity: Int, initializingValidUTF8With initializer: (inout OutputSpan) throws(E) -> Void ) throws(E) + + public mutating func append( + addingUTF8Capacity: Int, + initializingUTF8With initializer: (inout OUtputSpan) throws(E) -> Void + ) throws(E) +} + +extension UnicodeScalarView { + public init( + utf8Capacity: Int, + initializingUTF8With initializer: (inout OutputSpan) throws(E) -> Void + ) + + public init?( + utf8Capacity: Int, + initializingValidUTF8With initializer: (inout OutputSpan) throws(E) -> Void + ) throws(E) + + public mutating func append( + addingUTF8Capacity: Int, + initializingUTF8With initializer: (inout OUtputSpan) throws(E) -> Void + ) throws(E) } extension InlineArray { From fc5b957b3adf651e2f75da244913faf0c45bc06b Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 23 Apr 2025 13:52:51 -0700 Subject: [PATCH 4245/4563] Additions --- proposals/nnnn-outputspan.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/proposals/nnnn-outputspan.md b/proposals/nnnn-outputspan.md index cc7a18beac..f7f6bca341 100644 --- a/proposals/nnnn-outputspan.md +++ b/proposals/nnnn-outputspan.md @@ -18,7 +18,7 @@ ## Introduction -Following the introduction of [`Span`][SE-0447] and [`MutableSpan`][SE-0467], this proposal adds a general facility for initialization of exclusively-borrowed memory with the `OutputSpan` and `OutputRawSpan` types. +Following the introduction of [`Span`][SE-0447] and [`MutableSpan`][SE-0467], this proposal adds a general facility for initialization of exclusively-borrowed memory with the `OutputSpan` and `OutputRawSpan` types. The memory represented by `OutputSpan` consists of a number of initialized elements, followed by uninitialized memory. The operations of `OutputSpan` can change the number of initialized elements in memory, unlike `MutableSpan` which always represents memory that is initialized. ## Motivation @@ -640,6 +640,14 @@ This proposal is additive and ABI-compatible with existing code. The additions described in this proposal require a new version of the Swift standard library and runtime. + +## Alternatives Considered + +#### Vending `OutputSpan` as a property + +`OutputSpan` changes the number of initialized elements in a container (or collection), and this requires some operation to update the container after the `OutputSpan` is consumed. Let's call that update operation a "cleanup" operation. The cleanup operation needs to be scheduled in some way. We could associate the cleanup with the `deinit` of `OutputSpan`, or the `deinit` of a wrapper of `OutputSpan`. Neither of these seem appealing; the mechanisms would involve an arbitrary closure executed at `deinit` time, or having to write a full wrapper for each type that vends an `OutputSpan`. We could potentially schedule the cleanup operation as part of a coroutine accessor, but these are not productized yet. The pattern established by closure-taking API is well established, and it fits the needs of `OutputSpan` well. + + ## Future directions #### Helpers to initialize memory in an arbitrary order @@ -648,3 +656,4 @@ Some applications may benefit from the ability to initialize a range of memory i ## Acknowledgements +Thanks to Karoy Lorentey, Nate Cook and Tony Parker for their feedback. From e1340f0b4f22e8cf91555195c66b53ec6214237c Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 23 Apr 2025 17:02:08 -0400 Subject: [PATCH 4246/4563] Assign SE-0480 to the warning control settings proposal --- ...warning-control.md => 0480-swiftpm-warning-control.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{NNNN-swiftpm-warning-control.md => 0480-swiftpm-warning-control.md} (97%) diff --git a/proposals/NNNN-swiftpm-warning-control.md b/proposals/0480-swiftpm-warning-control.md similarity index 97% rename from proposals/NNNN-swiftpm-warning-control.md rename to proposals/0480-swiftpm-warning-control.md index 622b8c9971..84e6d929c2 100644 --- a/proposals/NNNN-swiftpm-warning-control.md +++ b/proposals/0480-swiftpm-warning-control.md @@ -1,11 +1,11 @@ # Warning Control Settings for SwiftPM -* Proposal: [SE-NNNN](NNNN-swiftpm-warning-control.md) +* Proposal: [SE-0480](0480-swiftpm-warning-control.md) * Authors: [Dmitrii Galimzianov](https://github.com/DmT021) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [John McCall](https://github.com/rjmccall), [Franz Busch]([https:](https://github.com/FranzBusch) +* Status: **Active review (April 23...May 5th, 2025)** * Implementation: [swiftlang/swift-package-manager#8315](https://github.com/swiftlang/swift-package-manager/pull/8315) -* Review: ([Pitch](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666)) +* Review: ([pitch](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666)) * Previous Proposal: [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md) ## Introduction From d9828bcf036b2a360cc89411c7a8c3e7f6400e26 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 23 Apr 2025 17:11:07 -0400 Subject: [PATCH 4247/4563] Fix a link in the SE-0480 proposal and add the review link --- proposals/0480-swiftpm-warning-control.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0480-swiftpm-warning-control.md b/proposals/0480-swiftpm-warning-control.md index 84e6d929c2..47598f0973 100644 --- a/proposals/0480-swiftpm-warning-control.md +++ b/proposals/0480-swiftpm-warning-control.md @@ -2,10 +2,10 @@ * Proposal: [SE-0480](0480-swiftpm-warning-control.md) * Authors: [Dmitrii Galimzianov](https://github.com/DmT021) -* Review Manager: [John McCall](https://github.com/rjmccall), [Franz Busch]([https:](https://github.com/FranzBusch) +* Review Manager: [John McCall](https://github.com/rjmccall), [Franz Busch](https://github.com/FranzBusch) * Status: **Active review (April 23...May 5th, 2025)** * Implementation: [swiftlang/swift-package-manager#8315](https://github.com/swiftlang/swift-package-manager/pull/8315) -* Review: ([pitch](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666)) +* Review: ([pitch](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666)) ([review](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475)) * Previous Proposal: [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md) ## Introduction From 9b4e4308afd7eecf08a08ecd9e80f1c14653d0fc Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 23 Apr 2025 15:32:31 -0700 Subject: [PATCH 4248/4563] Clarify bulk-initialization contract --- proposals/nnnn-outputspan.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/proposals/nnnn-outputspan.md b/proposals/nnnn-outputspan.md index f7f6bca341..462a304f41 100644 --- a/proposals/nnnn-outputspan.md +++ b/proposals/nnnn-outputspan.md @@ -116,7 +116,7 @@ extension OutputSpan where Element: ~Copyable { ##### Bulk initialization of an `OutputSpan`'s memory: -We include functions to perform bulk initialization of the memory represented by an `OutputSpan`. Initializing an `OutputSpan` from known-sized sources (such as `Collection` or `Span`) uses every element of the source. It is an error to do so when the available storage of the `OutputSpan` is too little to contain every element from the source. Initializing an `OutputSpan` from `Sequence` or `IteratorProtocol` will copy as many items as possible, either until the input is empty or the `OutputSpan`'s available storage is zero. +We include functions to perform bulk initialization of the memory represented by an `OutputSpan`. Initializing an `OutputSpan` from a `Sequence` or a fixed-size source must use every element of the source. Initializing an `OutputSpan` from `IteratorProtocol` will copy as many items as possible, either until the input is empty or the `OutputSpan`'s available storage is zero. ```swift extension OutputSpan { @@ -134,18 +134,24 @@ extension OutputSpan { ) -> Bool /// Initialize this span's suffix with every element of the source. + /// + /// It is a precondition that the `OutputSpan` can contain every element of the source. @lifetime(self: copy self) public mutating func append( fromContentsOf source: some Sequence ) /// Initialize this span's suffix with every element of the source. + /// + /// It is a precondition that the `OutputSpan` can contain every element of the source. @lifetime(self: copy self) public mutating func append( fromContentsOf source: Span ) /// Initialize this span's suffix with every element of the source. + /// + /// It is a precondition that the `OutputSpan` can contain every element of the source. @lifetime(self: copy self) public mutating func append( fromContentsOf source: UnsafeBufferPointer @@ -154,12 +160,16 @@ extension OutputSpan { extension OutputSpan where Element: ~Copyable { /// Initialize this span's suffix by moving every element from the source. + /// + /// It is a precondition that the `OutputSpan` can contain every element of the source. @lifetime(self: copy self) public mutating func moveAppend( fromContentsOf source: inout OutputSpan ) /// Initialize this span's suffix by moving every element from the source. + /// + /// It is a precondition that the `OutputSpan` can contain every element of the source. @lifetime(self: copy self) public mutating func moveAppend( fromContentsOf source: UnsafeMutableBufferPointer @@ -168,6 +178,8 @@ extension OutputSpan where Element: ~Copyable { extension OutputSpan { /// Initialize this span's suffix by moving every element from the source. + /// + /// It is a precondition that the `OutputSpan` can contain every element of the source. @lifetime(self: copy self) public mutating func moveAppend( fromContentsOf source: Slice> From 61fae31fd98afc171813d85782352ee49c7c3eac Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 23 Apr 2025 15:32:52 -0700 Subject: [PATCH 4249/4563] Add `removeLast(_: Int)` --- proposals/nnnn-outputspan.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/proposals/nnnn-outputspan.md b/proposals/nnnn-outputspan.md index 462a304f41..92e6012024 100644 --- a/proposals/nnnn-outputspan.md +++ b/proposals/nnnn-outputspan.md @@ -187,9 +187,14 @@ extension OutputSpan { } ``` -A bulk operation to deinitialize all of an `OutputSpan`'s memory is also available: +Bulk operations to deinitialize some or all of an `OutputSpan`'s memory is also available: ```swift extension OutputSpan where Element: ~Copyable { + /// Remove the last N elements, returning the memory they occupy + /// to the uninitialized state. + @lifetime(self: copy self) + public mutating func removeLast(_ n: Int) + /// Remove all this span's elements and return its memory to the uninitialized state. @lifetime(self: copy self) public mutating func removeAll() From cdd1c14af87e250c45c4d0992a9aa6a65df69677 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 23 Apr 2025 18:46:25 -0400 Subject: [PATCH 4250/4563] Wording suggestions for the weak let proposal --- proposals/NNNN-weak-let.md | 97 ++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/proposals/NNNN-weak-let.md b/proposals/NNNN-weak-let.md index e91230bd47..9acd0bce47 100644 --- a/proposals/NNNN-weak-let.md +++ b/proposals/NNNN-weak-let.md @@ -2,71 +2,87 @@ * Proposal: [SE-NNNN](NNNN-weak-let.md) * Authors: [Mykola Pokhylets](https://github.com/nickolas-pohilets) -* Review Manager: TBD -* Status: **Awaiting implementation** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Awaiting review** * Implementation: [swiftlang/swift#80440](https://github.com/swiftlang/swift/pull/80440) * Upcoming Feature Flag: `WeakLet` -* Review: ([discussion](https://forums.swift.org/t/weak-captures-in-sendable-sending-closures/78498)) +* Review: ([discussion](https://forums.swift.org/t/weak-captures-in-sendable-sending-closures/78498)) ([pitch](https://forums.swift.org/t/pitch-weak-let/79271)) + +[SE-0302]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md ## Introduction -Currently Swift requires weak stored variables to be mutable. -This restriction is rather artificial, and causes friction with sendability checking. +Swift provides weak object references using the `weak` modifier on variables and stored properties. Weak references become `nil` when the object is destroyed, causing the value of the variable to seem to change. Swift has therefore always required `weak` references to be declared with the `var` keyword rather than `let`. However, that causes unnecessary friction with [sendability checking][SE-0302]: because weak references must be mutable, classes and closures with such references are unsafe to share between concurrent contexts. This proposal lifts that restriction and allows `weak` to be combined with `let`. ## Motivation -Currently swift classes with weak stored properties cannot be `Sendable`, -because weak properties have to be mutable, and mutable properties are -not allowed in `Sendable` classes. +Currently, Swift classes with weak stored properties cannot be `Sendable`, because weak properties have to be mutable, and mutable properties are not allowed in `Sendable` classes: -Similarly, closures with `weak` captures cannot be `@Sendable`, -because such captures are implicitly made mutable. +```swift +final class C: Sendable {} -Usually developers are not aware of this implicit mutability and have no intention to modify the captured variable. -Implicit mutability of weak captures is inconsistent with `unowned` or default captures. +final class VarUser: Sendable { + weak var ref1: C? // error: stored property 'ref1' of 'Sendable'-conforming class 'VarUser' is mutable +} +``` -Wrapping weak reference into a single-field struct, allows stored properties and captures to be immutable. +Similarly, closures with explicit `weak` captures cannot be `@Sendable`, because such captures are implicitly *made* mutable, and `@Sendable` closures cannot capture mutable variables. This is surprising to most programmers, because every other kind of explicit capture is immutable. It is extremely rare for Swift code to directly mutate a `weak` capture. ```swift -final class C: Sendable {} +func makeClosure() -> @Sendable () -> Void { + let c = C() + return { [weak c] in + c?.foo() // error: reference to captured var 'c' in concurrently-executing code + + c = nil // allowed, but surprising and very rare + } +} +``` +In both cases, allowing the weak reference to be immutable would solve the problem, but this is not currently allowed: + +```swift +final class LetUser: Sendable { + weak let ref1: C? // error: 'weak' must be a mutable variable, because it may change at runtime +} +``` + +The restriction that weak references have to be mutable is based on the idea that the reference is mutated when the referenced object is destroyed. Since it's mutated, it must be kept in mutable storage, and hence the storage must be declared with `var`. This way of thinking about weak references is problematic, however; it does not work very well to explain the behavior of weak references that are components of other values, such as `struct`s. For example, a return value is normally an immutable value, but a `struct` return value can contain a weak reference that may become `nil` at any point. + +In fact, wrapping weak references in a single-property `struct` is a viable workaround to the `var` restriction in both properties and captures: + +```swift struct WeakRef { weak var ref: C? } -final class User: Sendable { - weak let ref1: C? // error: 'weak' must be a mutable variable, because it may change at runtime - let ref2: WeakRef // ok +final class WeakStructUser: Sendable { + let ref: WeakRef // ok } func makeClosure() -> @Sendable () -> Void { let c = C() - return { [weak c] in - c?.foo() // error: reference to captured var 'c' in concurrently-executing code - c = nil // nobody does this - } return { [c = WeakRef(ref: c)] in c.ref?.foo() // ok } } ``` -Existence of this workaround shows that ban on `weak let` variables is artificial, and can be lifted. +The existence of this simple workaround is itself an argument that the prohibition of `weak let` is not enforcing some fundamentally important rule. + +It is true that the value of a `weak` variable can be observed to change when the referenced object is destroyed. However, this does not have to be thought of as a mutation of the variable. A different way of thinking about it is that the variable continues to hold the same weak reference to the object, but that the program is simply not allowed to observe the object through that weak reference after the object is destroyed. This better explains the behavior of weak references in `struct`s: it's not that the destruction of the object changes the `struct` value, it's that the weak reference that's part of the `struct` value will now return `nil` if you try to observe it. -Note that resetting weak references on object destruction is different from regular variable modification. -Resetting on destruction is implemented in a thread-safe manner, and can safely coexist with concurrent reads or writes. -But regular writing to a variable requires exclusive access to that memory location. +Note that all of this relies on the fact that the thread-safety of observing a weak reference is fundamentally different from the thread-safety of assigning `nil` into a `weak var`. Swift's weak references are thread-safe against concurrent destruction: well-ordered reads and writes to a `weak var` or `weak let` will always behave correctly even if the referenced object is concurrently destroyed. But they are not *atomic* in the sense that writing to a `weak var` will behave correctly if another context is concurrently reading or writing to that same `var`. In this sense, a `weak var` is like any other `var`: mutations need to be well-ordered with all other accesses. ## Proposed solution -Allow `weak let` declarations for local variables and stored properties. +`weak` can now be freely combined with `let` in any position that `weak var` would be allowed. -Proposal maintains status quo regarding use of `weak` on function arguments and computed properties: -* there is no valid syntax to indicate that function argument is a weak reference; -* `weak` on computed properties is allowed, but has not effect. +This proposal maintains the status quo regarding `weak` on function arguments and computed properties: +* There is no valid syntax to indicate that function argument is a weak reference. +* `weak` on computed properties is allowed, but has no effect. -Weak captures are immutable under this proposal. If mutable capture is desired, -mutable variable need to be explicit declared and captured. +An explicit `weak` capture is now immutable under this proposal, like any other explicit capture. If the programmer really needs a mutable capture, they must capture a separate `weak var`: ```swift func makeClosure() -> @Sendable () -> Void { @@ -76,31 +92,32 @@ func makeClosure() -> @Sendable () -> Void { c?.foo() c = nil // error: cannot assign to value: 'c' is an immutable capture } +} +func makeNonSendableClosure() -> () -> Void { + let c = C() weak var explicitlyMutable: C? = c // Closure cannot be @Sendable anymore return { explicitlyMutable?.foo() - explicitlyMutable = nil // but assigned is ok + explicitlyMutable = nil // ok } } ``` ## Source compatibility -Allowing `weak let` bindings is a source-compatible change -that makes previously invalid code valid. +Allowing `weak let` bindings is an additive change that makes previously invalid code valid. It is therefore perfectly source-compatible. -Treating weak captures as immutable is a source-breaking change. -Any code that attempts to write to the capture will stop compiling. +Treating weak captures as immutable is a source-breaking change. Any code that attempts to write to the capture will stop compiling. The overall amount of such code is expected to be small. +Since the captures of a closure are opaque and cannot be observed outside of the closure, changing the mutability of weak captures has no impact on clients of the closure. + ## ABI compatibility -This is an ABI-compatible change. +There is no ABI impact of this change. ## Implications on adoption -This feature can be freely adopted and un-adopted in source -code with no deployment constraints and without affecting source or ABI -compatibility. +This feature can be freely adopted and un-adopted in source code with no deployment constraints and without affecting source or ABI compatibility. From 35d81b6dc690510c24ff26a73633601fd6d69b34 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 23 Apr 2025 18:30:44 -0700 Subject: [PATCH 4251/4563] Clarify some requirements --- proposals/nnnn-outputspan.md | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/proposals/nnnn-outputspan.md b/proposals/nnnn-outputspan.md index 92e6012024..c642798fe3 100644 --- a/proposals/nnnn-outputspan.md +++ b/proposals/nnnn-outputspan.md @@ -108,9 +108,11 @@ The converse operation `removeLast()` is also supported, and returns the removed ```swift extension OutputSpan where Element: ~Copyable { /// Remove the last initialized element from this `OutputSpan`. + /// + /// Returns the last element. The `OutputSpan` must not be empty @discardableResult @lifetime(self: copy self) - public mutating func removeLast() -> Element? + public mutating func removeLast() -> Element } ``` @@ -135,7 +137,8 @@ extension OutputSpan { /// Initialize this span's suffix with every element of the source. /// - /// It is a precondition that the `OutputSpan` can contain every element of the source. + /// It is a precondition that the `OutputSpan`'s uninitialized suffix + /// can contain every element of the source. @lifetime(self: copy self) public mutating func append( fromContentsOf source: some Sequence @@ -143,7 +146,8 @@ extension OutputSpan { /// Initialize this span's suffix with every element of the source. /// - /// It is a precondition that the `OutputSpan` can contain every element of the source. + /// It is a precondition that the `OutputSpan`'s uninitialized suffix + /// can contain every element of the source. @lifetime(self: copy self) public mutating func append( fromContentsOf source: Span @@ -151,7 +155,8 @@ extension OutputSpan { /// Initialize this span's suffix with every element of the source. /// - /// It is a precondition that the `OutputSpan` can contain every element of the source. + /// It is a precondition that the `OutputSpan`'s uninitialized suffix + /// can contain every element of the source. @lifetime(self: copy self) public mutating func append( fromContentsOf source: UnsafeBufferPointer @@ -161,7 +166,8 @@ extension OutputSpan { extension OutputSpan where Element: ~Copyable { /// Initialize this span's suffix by moving every element from the source. /// - /// It is a precondition that the `OutputSpan` can contain every element of the source. + /// It is a precondition that the `OutputSpan`'s uninitialized suffix + /// can contain every element of the source. @lifetime(self: copy self) public mutating func moveAppend( fromContentsOf source: inout OutputSpan @@ -169,7 +175,8 @@ extension OutputSpan where Element: ~Copyable { /// Initialize this span's suffix by moving every element from the source. /// - /// It is a precondition that the `OutputSpan` can contain every element of the source. + /// It is a precondition that the `OutputSpan`'s uninitialized suffix + /// can contain every element of the source. @lifetime(self: copy self) public mutating func moveAppend( fromContentsOf source: UnsafeMutableBufferPointer @@ -179,7 +186,8 @@ extension OutputSpan where Element: ~Copyable { extension OutputSpan { /// Initialize this span's suffix by moving every element from the source. /// - /// It is a precondition that the `OutputSpan` can contain every element of the source. + /// It is a precondition that the `OutputSpan`'s uninitialized suffix + /// can contain every element of the source. @lifetime(self: copy self) public mutating func moveAppend( fromContentsOf source: Slice> @@ -192,6 +200,8 @@ Bulk operations to deinitialize some or all of an `OutputSpan`'s memory is also extension OutputSpan where Element: ~Copyable { /// Remove the last N elements, returning the memory they occupy /// to the uninitialized state. + /// + /// `n` must not be greater than `count` @lifetime(self: copy self) public mutating func removeLast(_ n: Int) From 9a796710273e66e4d6617c6e298ade368cdfc4e2 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Thu, 24 Apr 2025 12:10:27 +0900 Subject: [PATCH 4252/4563] add withSerialExecutor to Actor --- proposals/0471-SerialExecutor-isIsolated.md | 51 +++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/proposals/0471-SerialExecutor-isIsolated.md b/proposals/0471-SerialExecutor-isIsolated.md index 85dbd1b3a2..d937e27773 100644 --- a/proposals/0471-SerialExecutor-isIsolated.md +++ b/proposals/0471-SerialExecutor-isIsolated.md @@ -135,6 +135,52 @@ The general guidance about which method to implement is to implement `isIsolatin The runtime will always invoke the `isIsolatingCurrentContext` before making attempts to call `checkIsolated`, and if the prior returns either `true` or `false`, the latter (`checkIsolated`) will not be invoked at all. +### Checking if currently isolated to some `Actor` + +We also introduce a way to obtain `SerialExecutor` from an `Actor`, which was previously not possible. + +This API needs to be scoped because the lifetime of the serial executor must be tied to the Actor's lifetime: + +```swift +extension Actor { + /// Perform an operation with the actor's ``SerialExecutor``. + /// + /// This converts the actor's ``Actor/unownedExecutor`` to a ``SerialExecutor`` while + /// retaining the actor for the duration of the operation. This is to ensure the lifetime + /// of the executor while performing the operation. + @_alwaysEmitIntoClient + @available(SwiftStdlib 5.1, *) + public nonisolated func withSerialExecutor(_ operation: (any SerialExecutor) throws -> T) rethrows -> T + + /// Perform an operation with the actor's ``SerialExecutor``. + /// + /// This converts the actor's ``Actor/unownedExecutor`` to a ``SerialExecutor`` while + /// retaining the actor for the duration of the operation. This is to ensure the lifetime + /// of the executor while performing the operation. + @_alwaysEmitIntoClient + @available(SwiftStdlib 5.1, *) + public nonisolated func withSerialExecutor(_ operation: (any SerialExecutor) async throws -> T) async rethrows -> T + +} +``` + +This allows developers to write "warn if wrong isolation" code, before moving on to enable preconditions in a future release of a library. This gives library developers, and their adopters, time to adjust their code usage before enabling more strict validation mode in the future, for example like this: + +```swift +func something(operation: @escaping @isolated(any) () -> ()) { + operation.isolation.withSerialExecutor { se in + if !se.isIsolatingCurrentContext() { + warn("'something' must be called from the same isolation as the operation closure is isolated to!" + + "This will become a runtime crash in future releases of this library.") + } + } +} +``` + + + +This API will be backdeployed and will be available independently of runtime version of the concurrency runtime. + ### Compatibility strategy for custom SerialExecutor authors New executor implementations should prioritize implementing `isIsolatingCurrentContext` when available, using an appropriate `#if swift(>=...)` check to ensure compatibility. Otherwise, they should fall back to implementing the crashing version of this API: `checkIsolated()`. @@ -169,7 +215,12 @@ This would be ideal, however also problematic since changing a protocol requirem In order to make adoption of this new mode less painful and not cause deprecation warnings to libraries which intend to support multiple versions of Swift, the `SerialExcecutor/checkIsolated` protocol requirement remains _not_ deprecated. It may eventually become deprecated in the future, but right now we have no plans of doing so. +### Model the SerialExecutor lifetime dependency on Actor using `~Escapable` + +It is currently not possible to express this lifetime dependency using `~Escapable` types, because combining `any SerialExecutor` which is an `AnyObject` constrained type, cannot be combined with `~Escapable`. Perhaps in a future revision it would be possible to offer a non-escapable serial executor in order to model this using non-escapable types, rather than a `with`-style API. + ## Changelog +- added way to obtain `SerialExecutor` from `Actor` in a safe, scoped, way. This enables using the `isIsolatingCurrentContext()` API when we have an `any Actor`, e.g. from an `@isolated(any)` closure. - changed return value of `isIsolatingCurrentContext` from `Bool` to `Bool?`, where the `nil` is to be interpreted as "unknown", and the default implementation of `isIsolatingCurrentContext` now returns `nil`. - removed the manual need to signal to the runtime that the specific executor supports the new checking mode. It is now detected by the compiler and runtime, checking for the presence of a non-default implementation of the protocol requirement. From 9e6bb4ec84bfb6971fe7833e4db555a1dcf4e2f0 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 24 Apr 2025 13:13:24 +0900 Subject: [PATCH 4253/4563] Mark as implemented: SE-0469-task-names Implemented, only pending a slight availability change to be done in: https://github.com/swiftlang/swift/pull/80984 --- proposals/0469-task-names.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0469-task-names.md b/proposals/0469-task-names.md index a5f1c6af62..0d1faf7de7 100644 --- a/proposals/0469-task-names.md +++ b/proposals/0469-task-names.md @@ -3,7 +3,7 @@ * Proposal: [SE-0469](0469-task-names.md) * Authors: [Konrad Malawski](https://github.com/ktoso), [Harjas Monga](https://github.com/Harjas12) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Implementation: [swiftlang/swift#79600](https://github.com/swiftlang/swift/pull/79600) * Review: ([pitch](https://forums.swift.org/t/pitch-task-naming-api/76115)) ([review](https://forums.swift.org/t/se-0469-task-naming/78509)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0469-task-naming/79438)) From 2d33986f412e6642fa51ba707017000cae5cc03f Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 24 Apr 2025 13:22:21 +0900 Subject: [PATCH 4254/4563] Mark as implemented: 0462-task-priority-escalation-apis This was implemented and is part of 6.2 --- proposals/0462-task-priority-escalation-apis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0462-task-priority-escalation-apis.md b/proposals/0462-task-priority-escalation-apis.md index 5174719ca9..f12b6dd375 100644 --- a/proposals/0462-task-priority-escalation-apis.md +++ b/proposals/0462-task-priority-escalation-apis.md @@ -3,7 +3,7 @@ * Proposal: [SE-0462](0462-task-priority-escalation-apis.md) * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: [Freddy Kellison-Linn](https://github.com/jumhyn) -* Status: **Accepted with modifications** +* Status: **Implemented (Swift 6.2)** * Implementation: https://github.com/swiftlang/swift/pull/78625 * Review: ([pitch](https://forums.swift.org/t/pitch-task-priority-escalation-apis/77702)) ([review](https://forums.swift.org/t/se-0462-task-priority-escalation-apis/77997))([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0462-task-priority-escalation-apis/78488)) From 4b882a887ed0e1c5ceb3f452404e834f1d7501fd Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 24 Apr 2025 23:14:17 +0900 Subject: [PATCH 4255/4563] Use typed throws in withSerialExecutor --- proposals/0471-SerialExecutor-isIsolated.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0471-SerialExecutor-isIsolated.md b/proposals/0471-SerialExecutor-isIsolated.md index d937e27773..6de9754b93 100644 --- a/proposals/0471-SerialExecutor-isIsolated.md +++ b/proposals/0471-SerialExecutor-isIsolated.md @@ -150,7 +150,7 @@ extension Actor { /// of the executor while performing the operation. @_alwaysEmitIntoClient @available(SwiftStdlib 5.1, *) - public nonisolated func withSerialExecutor(_ operation: (any SerialExecutor) throws -> T) rethrows -> T + public nonisolated func withSerialExecutor(_ operation: (any SerialExecutor) throws(E) -> T) throws(E) -> T /// Perform an operation with the actor's ``SerialExecutor``. /// @@ -159,7 +159,7 @@ extension Actor { /// of the executor while performing the operation. @_alwaysEmitIntoClient @available(SwiftStdlib 5.1, *) - public nonisolated func withSerialExecutor(_ operation: (any SerialExecutor) async throws -> T) async rethrows -> T + public nonisolated func withSerialExecutor(_ operation: (any SerialExecutor) async throws(E) -> T) async throws(E) -> T } ``` From 8c7b57b284689553ad25713e3b07b662c37ce90d Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Thu, 24 Apr 2025 09:01:20 -0700 Subject: [PATCH 4256/4563] Add future directions. --- proposals/0479-method-and-initializer-keypaths.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/0479-method-and-initializer-keypaths.md b/proposals/0479-method-and-initializer-keypaths.md index a4f352978e..0bdbef0fc6 100644 --- a/proposals/0479-method-and-initializer-keypaths.md +++ b/proposals/0479-method-and-initializer-keypaths.md @@ -170,3 +170,7 @@ This feature does not affect ABI compatibility. ## Implications on adoption This feature has no implications on adoption. + +## Future directions + +The effectful value types that are unsupported by this feature will all require new `KeyPath` types and so have been left out of this proposal. Additionally, this lack of support impacts existing key path component kinds and could be addressed in a unified proposal that resolves this gap across all key path component kinds. From aa2dfe3974078b1a4d48856802627363ba654b52 Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Thu, 24 Apr 2025 09:04:30 -0700 Subject: [PATCH 4257/4563] Revise motivation to account for limitations. --- proposals/0479-method-and-initializer-keypaths.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0479-method-and-initializer-keypaths.md b/proposals/0479-method-and-initializer-keypaths.md index 0bdbef0fc6..22850e43ed 100644 --- a/proposals/0479-method-and-initializer-keypaths.md +++ b/proposals/0479-method-and-initializer-keypaths.md @@ -14,7 +14,7 @@ Swift key paths can be written to properties and subscripts. This proposal exten ## Motivation -Key paths to method members and their advantages have been explored in several discussions on the Swift forum, specifically to [unapplied instance methods](https://forums.swift.org/t/allow-key-paths-to-reference-unapplied-instance-methods/35582) and to [partially and applied methods](https://forums.swift.org/t/pitch-allow-keypaths-to-represent-functions/67630). Extending key paths to include reference to methods and initializers and handling them similarly to properties and subscripts will unify instance and type member access for a more consistent API. Key path methods and initializers will also enjoy all of the benefits offered by existing key path component kinds, e.g. simplify code by abstracting away details of how these properties/subscripts/methods are modified/accessed/invoked, reusability via generic functions that accept key paths to methods as parameters, and supporting dynamic invocation while maintaining type safety. +Key paths to method members and their advantages have been explored in several discussions on the Swift forum, specifically to [unapplied instance methods](https://forums.swift.org/t/allow-key-paths-to-reference-unapplied-instance-methods/35582) and to [partially and applied methods](https://forums.swift.org/t/pitch-allow-keypaths-to-represent-functions/67630). Extending key paths to include reference to methods and initializers and handling them similarly to properties and subscripts will unify instance and type member access for a more consistent API. While this does not yet encompass all method kinds, particularly those with effectful or non-hashable arguments, it lays the groundwork for more expressive, type-safe APIs. In doing so, it brings many of the benefits of existing key path components to supported methods and initializers, such as abstraction, reusability via generic functions and dynamic invocation with state type safety. ## Proposed solution From 0bac2947de068b83f51b2ef83b1783942e5655e3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 24 Apr 2025 11:05:50 -0700 Subject: [PATCH 4258/4563] Extend SE-0471 review through April 29 --- proposals/0471-SerialExecutor-isIsolated.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0471-SerialExecutor-isIsolated.md b/proposals/0471-SerialExecutor-isIsolated.md index 6de9754b93..e20eecdae0 100644 --- a/proposals/0471-SerialExecutor-isIsolated.md +++ b/proposals/0471-SerialExecutor-isIsolated.md @@ -3,7 +3,7 @@ * Proposal: [SE-0471](0471-SerialExecutor-isIsolated.md) * Author: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review (March 25...April 8, 2025)** +* Status: **Active Review (March 25...April 29, 2025)** * Implementation: https://github.com/swiftlang/swift/pull/79788 & https://github.com/swiftlang/swift/pull/79946 * Review: [Pitch](https://forums.swift.org/t/pitch-serialexecutor-improved-custom-serialexecutor-isolation-checking/78237/), [Review](https://forums.swift.org/t/se-0471-improved-custom-serialexecutor-isolation-checking-for-concurrency-runtime/78834) From d62a69b70edd64a2b1b1f1ddefc5d7dd8f1488bc Mon Sep 17 00:00:00 2001 From: Amritpan Kaur Date: Thu, 24 Apr 2025 16:04:15 -0700 Subject: [PATCH 4259/4563] Update example. --- proposals/0479-method-and-initializer-keypaths.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0479-method-and-initializer-keypaths.md b/proposals/0479-method-and-initializer-keypaths.md index 22850e43ed..bd1db9fe2e 100644 --- a/proposals/0479-method-and-initializer-keypaths.md +++ b/proposals/0479-method-and-initializer-keypaths.md @@ -142,8 +142,8 @@ struct DynamicKeyPathWrapper { } let dynamicCalculator = DynamicKeyPathWrapper(root: Calculator()) -let subtract = dynamicCalculator.subtract -print(subtract(10)) +let power = dynamicCalculator.power +print(power(10, 2)) ``` ### Effectful value types From 7248f7b0128dd6d786e8adeb320084f1a22379b8 Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Mon, 28 Apr 2025 15:57:18 +0200 Subject: [PATCH 4260/4563] Update 0008-exit-tests.md Updated after acceptance of proposal (with modifications) --- proposals/testing/0008-exit-tests.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/testing/0008-exit-tests.md b/proposals/testing/0008-exit-tests.md index 1950c173f4..6a7849b0a6 100644 --- a/proposals/testing/0008-exit-tests.md +++ b/proposals/testing/0008-exit-tests.md @@ -3,11 +3,11 @@ * Proposal: [ST-0008](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0008-exit-tests.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Review Manager: [Maarten Engels](https://github.com/maartene) -* Status: **Active Review (April 10...April 21, 2025)** +* Status: **Accepted with Modifications** * Bug: [apple/swift-testing#157](https://github.com/apple/swift-testing/issues/157) * Implementation: [apple/swift-testing#324](https://github.com/swiftlang/swift-testing/pull/324) * Previous Revision: [1](https://github.com/swiftlang/swift-evolution/blob/fdfc7867df4e35e29b2a24edee34ea4412ec15b0/proposals/testing/0008-exit-tests.md) -* Review: ([second review](https://forums.swift.org/t/second-review-st-0008-exit-tests/79198)), ([review](https://forums.swift.org/t/st-0008-exit-tests/78692)), ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) +* Review: ([acceptance](https://forums.swift.org/t/accepted-with-modifications-st-0008-exit-tests/79553)) ([second review](https://forums.swift.org/t/second-review-st-0008-exit-tests/79198)), ([review](https://forums.swift.org/t/st-0008-exit-tests/78692)), ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) ## Introduction From 315b2ac314833493c1331a34aebdf87e232ef9ec Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Mon, 28 Apr 2025 19:08:10 +0200 Subject: [PATCH 4261/4563] [SE-0343] Fix typo (#2812) --- proposals/0343-top-level-concurrency.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0343-top-level-concurrency.md b/proposals/0343-top-level-concurrency.md index 8549c3f78b..e11e040563 100644 --- a/proposals/0343-top-level-concurrency.md +++ b/proposals/0343-top-level-concurrency.md @@ -40,7 +40,7 @@ semantic changes, potentially breaking scripts that already exist. The solutions will only apply when the top-level code is an asynchronous context. As a synchronous context, the behavior of top-level code does not -change. In order trigger making the top-level context an asynchronous context, I +change. In order to trigger making the top-level context an asynchronous context, I propose using the presence of an `await` in one of the top-level expressions. An await nested within a function declaration or a closure will not trigger the From e3864541f352cecc896fb0b24efe667fa75a4ae5 Mon Sep 17 00:00:00 2001 From: Philipp Gabriel Date: Mon, 28 Apr 2025 20:32:45 +0200 Subject: [PATCH 4262/4563] Update 0457-duration-attosecond-represenation.md (#2775) --- proposals/0457-duration-attosecond-represenation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0457-duration-attosecond-represenation.md b/proposals/0457-duration-attosecond-represenation.md index 6e8da7e7be..b88984362b 100644 --- a/proposals/0457-duration-attosecond-represenation.md +++ b/proposals/0457-duration-attosecond-represenation.md @@ -3,7 +3,7 @@ * Proposal: [SE-0457](0457-duration-attosecond-represenation.md) * Authors: [Philipp Gabriel](https://github.com/ph1ps) * Review Manager: [Stephen Canon](https://github.com/stephentyrone) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Implementation: [swiftlang/swift#78202](https://github.com/swiftlang/swift/pull/78202) * Review: ([pitch](https://forums.swift.org/t/pitch-adding-int128-support-to-duration))([review](https://forums.swift.org/t/se-0457-expose-attosecond-representation-of-duration/77249)) From 1df2060fb6da07f823c6eca0a1584263d344961c Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Mon, 28 Apr 2025 15:28:43 -0700 Subject: [PATCH 4263/4563] Specify the correct warning group for -Werror in SE-0458 proposal. The warning group is `StrictMemorySafety`, rather than `Unsafe`. --- proposals/0458-strict-memory-safety.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0458-strict-memory-safety.md b/proposals/0458-strict-memory-safety.md index b9ce0f6221..dce70053c2 100644 --- a/proposals/0458-strict-memory-safety.md +++ b/proposals/0458-strict-memory-safety.md @@ -510,7 +510,7 @@ The strict memory safety mode can be enabled with the new compiler flag `-strict All of the memory-safety diagnostics produced by the strict memory safety mode will be warnings. These warnings be in the group `StrictMemorySafety` (possibly organized into subgroups) so that one can choose to escalate them to errors or keep them as warnings using the compiler flags introduced in [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md). For example, one can choose to enable the mode and make memory-safety issues errors using: ``` -swiftc -strict-memory-safety -Werror Unsafe +swiftc -strict-memory-safety -Werror StrictMemorySafety ``` ### SwiftPM integration From c336eab95dde9eb0432e52d323b6c96896c69e52 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Tue, 29 Apr 2025 11:50:53 -0500 Subject: [PATCH 4264/4563] Update state of ST-0010 and link to acceptance (#2816) --- proposals/testing/0010-evaluate-condition.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/testing/0010-evaluate-condition.md b/proposals/testing/0010-evaluate-condition.md index 577b991d3b..b0ac6ecbaa 100644 --- a/proposals/testing/0010-evaluate-condition.md +++ b/proposals/testing/0010-evaluate-condition.md @@ -3,10 +3,10 @@ * Proposal: [ST-0010](0010-evaluate-condition.md) * Authors: [David Catmull](https://github.com/Uncommon) * Review Manager: [Stuart Montgomery](https://github.com/stmontgomery) -* Status: **Active review (April 11...April 22, 2025)** +* Status: **Accepted** * Bug: [swiftlang/swift-testing#903](https://github.com/swiftlang/swift-testing/issues/903) * Implementation: [swiftlang/swift-testing#909](https://github.com/swiftlang/swift-testing/pull/909) -* Review: ([pitch](https://forums.swift.org/t/pitch-introduce-conditiontrait-evaluate/77242)) ([review](https://forums.swift.org/t/st-0010-public-api-to-evaluate-conditiontrait/79232)) +* Review: ([pitch](https://forums.swift.org/t/pitch-introduce-conditiontrait-evaluate/77242)), ([review](https://forums.swift.org/t/st-0010-public-api-to-evaluate-conditiontrait/79232)), ([acceptance](https://forums.swift.org/t/accepted-st-0010-public-api-to-evaluate-conditiontrait/79577)) ## Introduction From 9c5318ce7e2d860122265d6709444d1e1b7c87f1 Mon Sep 17 00:00:00 2001 From: Mykola Pokhylets Date: Wed, 30 Apr 2025 14:11:25 +0200 Subject: [PATCH 4265/4563] Added a note about optional type --- proposals/NNNN-weak-let.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/NNNN-weak-let.md b/proposals/NNNN-weak-let.md index 9acd0bce47..feacf3f02f 100644 --- a/proposals/NNNN-weak-let.md +++ b/proposals/NNNN-weak-let.md @@ -77,6 +77,7 @@ Note that all of this relies on the fact that the thread-safety of observing a w ## Proposed solution `weak` can now be freely combined with `let` in any position that `weak var` would be allowed. +Similar to `weak var`, `weak let` declarations also must be of `Optional` type. This proposal maintains the status quo regarding `weak` on function arguments and computed properties: * There is no valid syntax to indicate that function argument is a weak reference. From c173228edff342a0841b349c10f598b533c1ccdd Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 30 Apr 2025 17:11:29 -0400 Subject: [PATCH 4266/4563] Assign SE-0481 to the `weak let` proposal and put it in review --- proposals/{NNNN-weak-let.md => 0481-weak-let.md} | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) rename proposals/{NNNN-weak-let.md => 0481-weak-let.md} (98%) diff --git a/proposals/NNNN-weak-let.md b/proposals/0481-weak-let.md similarity index 98% rename from proposals/NNNN-weak-let.md rename to proposals/0481-weak-let.md index feacf3f02f..69c59ae8cf 100644 --- a/proposals/NNNN-weak-let.md +++ b/proposals/0481-weak-let.md @@ -1,11 +1,10 @@ # Feature name -* Proposal: [SE-NNNN](NNNN-weak-let.md) +* Proposal: [SE-0481](0481-weak-let.md) * Authors: [Mykola Pokhylets](https://github.com/nickolas-pohilets) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Awaiting review** +* Status: **Active review (April 30th...May 13th, 2025)** * Implementation: [swiftlang/swift#80440](https://github.com/swiftlang/swift/pull/80440) -* Upcoming Feature Flag: `WeakLet` * Review: ([discussion](https://forums.swift.org/t/weak-captures-in-sendable-sending-closures/78498)) ([pitch](https://forums.swift.org/t/pitch-weak-let/79271)) [SE-0302]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md From 196c7b321d210efd6cd87029eb0ac0e151a93148 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 30 Apr 2025 17:12:39 -0400 Subject: [PATCH 4267/4563] Name SE-0481 --- proposals/0481-weak-let.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0481-weak-let.md b/proposals/0481-weak-let.md index 69c59ae8cf..60a15f1884 100644 --- a/proposals/0481-weak-let.md +++ b/proposals/0481-weak-let.md @@ -1,4 +1,4 @@ -# Feature name +# `weak let` * Proposal: [SE-0481](0481-weak-let.md) * Authors: [Mykola Pokhylets](https://github.com/nickolas-pohilets) From a87b818da018a2cd4644abe744c8f1381344f3ae Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 30 Apr 2025 15:01:52 -0700 Subject: [PATCH 4268/4563] Move `swift migrate` from Future Directions to Detailed Design section --- ...NNN-adoption-tooling-for-swift-features.md | 63 +++++++++++++++++-- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/proposals/NNNN-adoption-tooling-for-swift-features.md b/proposals/NNNN-adoption-tooling-for-swift-features.md index 2d750e459a..3a118f1190 100644 --- a/proposals/NNNN-adoption-tooling-for-swift-features.md +++ b/proposals/NNNN-adoption-tooling-for-swift-features.md @@ -160,6 +160,63 @@ belong to a diagnostic group named after the feature. The names of diagnostic groups can be displayed alongside diagnostic messages using `-print-diagnostic-groups` and used to associate messages with features. +### `swift migrate` command + +To enable seemless migration experience for Swift packages, I'd like to propose a new Swift Package Manager command - `swift migrate` to complement the Swift compiler-side changes. + +The command would accept one or more features that have migration mode enabled and optionally a set of targets to migrate, if no targets are specified the whole package is going to be migrated to use new features. + +#### Interface + +``` +USAGE: swift migrate [] --to-feature ... + +OPTIONS: + --targets The targets to migrate to specified set of features or a new language mode. + --to-feature + The Swift language upcoming/experimental feature to migrate to. + -h, --help Show help information. +``` + +#### Use case + +``` +swift migrate --targets MyTarget,MyTest --to-feature ExistentialAny +``` + +This command would attempt to build `MyTarget` and `MyTest` targets with `ExistentialAny:migrate` feature flag, apply any fix-its associated with +the feature produced by the compiler, and update the `Package.swift` to +enable the feature(s) if both of the previous actions are successful: + +``` +.target( + name: "MyTarget", + ... + swiftSettings: [ + // ... existing settings, + .enableUpcomingFeature("ExistentialAny") + ] +) +... +.testTarget( + name: "MyTest", + ... + swiftSettings: [ + // ... existing settings, + .enableUpcomingFeature("ExistentialAny") + ] +) +``` + +In the "whole package" mode, every target is going to be updated to include +new feature flag(s). This is supported by the same functionality as `swift package add-setting` command. + +If it's, for some reason, impossible to add the setting the diagnostic message would suggest what to add and where i.e. `...; please add '.enableUpcomingFeature("ExistentialAny")' to `MyTarget` target manually`. + +#### Impact on Interface + +This proposal introduces a new command but does that does not interfere with existing commands. It follows the same pattern as `swift build` and `swift test` in a consistent manner. + ## Source compatibility This proposal does not affect language rules. @@ -217,12 +274,6 @@ is essential for future tools built around migration mode: Furthermore, tools can take advantage of this information by favoring and auto-applying source-compatible fix-its. -### `swift migrate` - -The Swift package manager could implement a `migrate` subcommand for interactive -review and application of migration mode output for a given set of features, -with a command-line interface similar to `git add --patch`. - ## Alternatives considered ### A distinct `-migrate` option From 777d3a385b56a7795cc7e07ca842834ac8700d7f Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 30 Apr 2025 21:25:39 -0400 Subject: [PATCH 4269/4563] Accept SE-0477 (#2818) --- proposals/0477-default-interpolation-values.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0477-default-interpolation-values.md b/proposals/0477-default-interpolation-values.md index 60248240ab..c880eba3b3 100644 --- a/proposals/0477-default-interpolation-values.md +++ b/proposals/0477-default-interpolation-values.md @@ -3,9 +3,9 @@ * Proposal: [SE-0477](0477-default-interpolation-values.md) * Authors: [Nate Cook](https://github.com/natecook1000) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (April 16...29, 2025)** +* Status: **Accepted with modifications** * Implementation: [swiftlang/swift#80547](https://github.com/swiftlang/swift/pull/80547) -* Review: ([pitch](https://forums.swift.org/t/pitch-default-values-for-string-interpolations/69381)) ([review](https://forums.swift.org/t/se-0477-default-value-in-string-interpolations/79302)) +* Review: ([pitch](https://forums.swift.org/t/pitch-default-values-for-string-interpolations/69381)) ([review](https://forums.swift.org/t/se-0477-default-value-in-string-interpolations/79302)) ([acceptance](https://forums.swift.org/t/accepted-with-modification-se-0477-default-value-in-string-interpolations/79609)) ## Introduction From 3fe4b4cccb74f1a04d1bd8bc4c583e1d7fa472c7 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Thu, 1 May 2025 19:33:38 +0900 Subject: [PATCH 4270/4563] Update Task.immediate proposal to new semantics --- ...k-start-synchronously-on-caller-context.md | 340 +++++++++++------- 1 file changed, 219 insertions(+), 121 deletions(-) diff --git a/proposals/0472-task-start-synchronously-on-caller-context.md b/proposals/0472-task-start-synchronously-on-caller-context.md index 836920fce3..93cfb85323 100644 --- a/proposals/0472-task-start-synchronously-on-caller-context.md +++ b/proposals/0472-task-start-synchronously-on-caller-context.md @@ -19,20 +19,16 @@ This initial delay may also be problematic for some situations where it is known Today, the only way to enter an asynchronous execution context is to create a new task which then will be scheduled on the global concurrent executor or some specific actor the task is isolated to, and only once that task is scheduled execution of it may begin. -This initial scheduling delay can be problematic in some situations where tight control over execution is required. While for most tasks the general semantics are a good choice–not risking overhang on the calling thread–we have found through experience that some UI or performance sensitive use-cases require a new kind of semantic: starting on the calling context, until a suspension occurs, and only then hopping off to another executor once the task is resumed from the suspension. +This initial scheduling delay can be problematic in some situations where tight control over execution is required. While for most tasks the general semantics are a good choice–not risking overhang on the calling thread–we have found through experience that some UI or performance sensitive use-cases require a new kind of semantic: immediately starting a task on the calling context. After a suspension happens the task will resume on the the executor as implied by the task operation's isolation, as would be the case normally. -This can be especially beneficial for tasks, which *may run to completion very quickly and without ever suspending.* +This new behavior can especially beneficial for tasks, which *may run to completion very quickly and without ever suspending.* A typical situation where this new API may be beneficial often shows up with @MainActor code, such as: ```swift -@MainActor var thingsHappened: Int +@MainActor var thingsHappened: Int = 0 -@MainActor func onThingHappened(context: Context) { - synchronousFunction() -} - -func asyncUpdateThingsHappenedCounter() async { +@MainActor func asyncUpdateThingsHappenedCounter() async { // for some reason this function MUST be async thingsHappened += 1 } @@ -40,33 +36,39 @@ func asyncUpdateThingsHappenedCounter() async { func synchronousFunction() { // we know this executes on the MainActor, and can assume so: MainActor.assumeIsolated { - // we cannot call the asynchronous function asyncUpdateThingsHappenedCounter though! + // The following would error: + // await asyncUpdateThingsHappenedCounter() + // because is it is an async call; cannot call from synchronous context } - // Proposed API: - Task.startSynchronously { - // Now we CAN call the asynchronous function below: + // Using the newly proposed Immediate Task: + let task = Task.immediate { + // Now we CAN call the asynchronous function below: await asyncUpdateThingsHappenedCounter() } + + // cannot await on the `task` since still in synchronous context } ``` -The above example showcases a typical situation where this new API can be useful. While `assumeIsolated` gives us a specific isolation, it still would not allow us to call arbitrary async functions, as we are still in a synchronous context. +The above example showcases a typical situation where this new API can be useful. While `assumeIsolated` gives us a specific isolation, but it would not allow us to call the async functions, as we are still in a synchronous context. + +The proposed `Task.immediate` API forms an async context on the calling thread/task/executor, and therefore allows us to call into async code, at the risk of overhanging on the calling executor. -The proposed `Task.startSynchronously` API forms an async context on the calling thread/task/executor, and therefore allows us to call into async code, at the risk of overhanging on the calling executor. So while this should be used sparingly, it allows entering an asynchronous context *synchronously*. +While this should be used sparingly, it allows entering an asynchronous context *synchronously*. ## Proposed solution -We propose the introduction of a new family of Task creation APIs, collectively called "start synchronously", which create a Task and use the calling thread to execute the task's "first synchronous section" right until the task suspends for the first time. +We propose the introduction of a new family of Task creation APIs collectively called "**immediate tasks**", which create a task and use the calling execution context to run the task's immediately, before yielding control back to the calling context upon encountering the first suspension point inside the immediate task. -After the suspension happens, execution yields back to an appropriate executor, and does not continue to use the caller's thread anymore. +Upon first suspension inside the immediate task, the calling executor is freed up and able to continue executing other work, including the code surrounding the creation of the immediate task. This happens specifically when when a real suspension happens, and not for "potential suspension point" (which are marked using the `await` keyword). -The canonical example for using this new API is using an unstructured task like this: +The canonical example for using this new API is using an *unstructured immediate task* like this: ```swift func synchronous() { // synchronous function // executor / thread: "T1" - let task: Task = Task.startSynchronously { + let task: Task = Task.immediate { // executor / thread: "T1" guard keepRunning() else { return } // synchronous call (1) @@ -83,17 +85,17 @@ func synchronous() { // synchronous function } ``` -The task created by the `startSynchronously` function begins running immediately _on the calling executor (and thread)_ without any scheduling delay. This new task behaves generally the same as any other unstructured task, it gets a copy of the outer context's task locals, and uses the surrounding context's base priority as its base priority as well. +The task created by the `immediate` function begins running immediately _on the calling executor (and thread)_ without any scheduling delay. This new task behaves generally the same as any other unstructured task, it gets a copy of the outer context's task locals, and uses the surrounding context's base priority as its base priority as well. -Since the task started running immediately, we're able to perform some calls immediately inside it, and potentially return early. +Since the task started running immediately, we're able to perform some calls immediately inside it, and potentially return early, without any additional scheduling delays. -If a potential suspension point does not actually suspend, we still continue running on the calling context. For example, if potential suspension point `#1` did not suspend, we still continue running synchronously until we reach potential suspension point `#2` which for the sake of discussion let's say does suspend. At this point the calling thread continues executing the scope that created the unstructured task. +If a potential suspension point does not actually suspend, we still continue running on the calling context. For example, if potential suspension point `#1` did not suspend, we still continue running synchronously until we reach potential suspension point `#2` which for the sake of discussion let's say does suspend. At this point the calling thread continues executing the code that created the unstructured task. > You can refer to the `(N)` numbers in the above snippet to follow the execution order of this example execution. Specifically, once the execution reaches (3) the calling thread stops executing the unstructured task, and continues executing at (4). Eventually, when the unstructured task is resumed, it gets woken up at (5) and continues running on some other executor and/or thread. ## Detailed design -We propose the introduction of a family of "start synchronously" task creation APIs. +We propose the introduction of a family of APIs that allow for the creation of *immediate tasks*. The most frequent use of this API is likely going to be the unstructured task one. This is because we are able to enter an asynchronous context from a synchronous function using it: @@ -101,39 +103,38 @@ The most frequent use of this API is likely going to be the unstructured task on extension Task { @discardableResult - public static func startSynchronously( - // SE-0469's proposed 'name: String? = nil' would be here if accepted + public static func immediate( + name: String? = nil, // Introduced by SE-0469 priority: TaskPriority? = nil, executorPreference taskExecutor: consuming (any TaskExecutor)? = nil, - operation: sending @escaping async throws(Failure) -> Success + @_inheritActorContext(always) operation: sending @escaping async throws(Failure) -> Success ) -> Task @discardableResult - public static func startSynchronouslyDetached( - // SE-0469's proposed 'name: String? = nil' would be here if accepted + public static func immediateDetached( + name: String? = nil, // Introduced by SE-0469 priority: TaskPriority? = nil, executorPreference taskExecutor: consuming (any TaskExecutor)? = nil, - operation: sending @escaping async throws(Failure) -> Success + @_inheritActorContext(always) operation: sending @escaping async throws(Failure) -> Success ) -> Task } ``` -We also offer the same API for all kinds of task groups. These create child tasks, which participate in structured concurrency as one would expect of tasks created by task groups. +We also introduce the same API for all kinds of task groups. These create child tasks, which participate in structured concurrency as one would expect of tasks created by task groups. ```swift extension (Throwing)TaskGroup { - - // Same add semantics as 'addTask'. - func startTaskSynchronously( - // SE-0469's proposed 'name: String? = nil' would be here + // Similar semantics as the usual 'addTask'. + func addImmediateTask( + name: String? = nil, // Introduced by SE-0469 priority: TaskPriority? = nil, executorPreference taskExecutor: (any TaskExecutor)? = nil, operation: sending @escaping () async throws -> ChildTaskResult ) - // Same add semantics as 'addTaskUnlessCancelled'. - func startTaskSynchronouslyUnlessCancelled( - // SE-0469's proposed 'name: String? = nil' would be here + // Similar semantics as the usual 'addTaskUnlessCancelled'. + func addImmediateTaskUnlessCancelled( + name: String? = nil, // Introduced by SE-0469 priority: TaskPriority? = nil, executorPreference taskExecutor: (any TaskExecutor)? = nil, operation: sending @escaping () async throws -> ChildTaskResult @@ -141,17 +142,17 @@ extension (Throwing)TaskGroup { } extension (Throwing)DiscardingTaskGroup { - // Same add semantics as 'addTask'. - func startTaskSynchronously( - // SE-0469's proposed 'name: String? = nil' would be here + // Similar semantics as the usual 'addTask'. + func addImmediateTask( + name: String? = nil, // Introduced by SE-0469 priority: TaskPriority? = nil, executorPreference taskExecutor: (any TaskExecutor)? = nil, operation: sending @escaping () async throws -> ChildTaskResult ) - // Same add semantics as 'addTaskUnlessCancelled'. - func startTaskSynchronouslyUnlessCancelled( - // SE-0469's proposed 'name: String? = nil' would be here + // Similar semantics as the usual 'addTaskUnlessCancelled'. + func addImmediateTaskUnlessCancelled( + name: String? = nil, // Introduced by SE-0469 priority: TaskPriority? = nil, executorPreference taskExecutor: (any TaskExecutor)? = nil, operation: sending @escaping () async throws -> ChildTaskResult @@ -159,59 +160,162 @@ extension (Throwing)DiscardingTaskGroup { } ``` -The `startTaskSynchronously` function mirrors the functionality of `addTask`, unconditionally adding the task to the group, while the `startTaskSynchronouslyUnlessCancelled` mirrors the `addTaskUnlessCancelled` which only adds the task to the group if the group (or task we're running in, and therefore the group as well) are not cancelled. +The `addImmediateTask` function mirrors the functionality of `addTask`, unconditionally adding the task to the task group, while the `addImmediateTaskUnlessCancelled` mirrors the `addTaskUnlessCancelled` which only adds the task to the group if the group (or task we're running in, and therefore the group as well) are not cancelled. ### Isolation rules -Due to the semantics of "starting on the caller context", the isolation rules of the closure passed to `startSynchronously` need to be carefully considered. +Due to the semantics of "starting on the caller context", the isolation rules of the closure passed to `Task.immediate` need to be carefully considered. + +The isolation rules for the `immediate` family of APIs need to account for this synchronous "first part" of the execution. We propose the following set of rules to make this API concurrency-safe: -For example, the following example would not be safe, as unlike `Task.init` the task does not actually immediately become isolated to the isolation of its closure: +- The operation closure is `sending`. +- The operation closure may only specify an isolation (e.g. `{ @AnyGlobalActor in }`) + +Another significant way in which `Task.immediate` differs from the `Task.init` initializer is that the inheritance of the surrounding actor context is performed more eagerly. This is because immediate tasks always attempt to execute on the "current" executor, unlike `Task.init` which only execute on the "surrounding" actor context when the task's operation closure closes over an isolated parameter, or was formed in a global actor isolated context: ```swift -@MainActor var counter: Int = 0 +@MainActor +func alreadyDefinitelyOnMainActor() { + Task { + // @MainActor isolated, enqueue + } + Task.immediate { + // @MainActor isolated, run immediately + } +} +``` -func sayHello() { - Task { @MainActor in // ✅ ok - counter += 1 // we're isolated to the main actor immediately and may modify its state +```swift +actor Caplin { + var anything: Int = 0 + + func act() { + Task { + // nonisolated, enqueue on global concurrent executor + } + Task { + // self isolated, enqueue + self.anything // any capture of 'self' + } + + Task.immediate { // regardless of captures + // self isolated, run immediately + } } +} + +func go(with caplin: isolated Caplin) async { + Task { + // nonisolated, enqueue on global concurrent executor + } + Task { + // 'caplin' isolated, enqueue + caplin.anything // any capture of 'caplin' + } - Task.startSynchronously { @MainActor in // ❌ unsafe, must be compile time error - counter += 1 // Not actually running on the main actor at this point (!) + Task.immediate { // regardless of captures + // 'caplin' isolated, run immediately + } + } +} + +func notSpecificallyIsolatedAnywhere() { + Task { + // nonisolated, enqueue on global concurrent executor + } + + Task.immediate { + // nonisolated. + // attempt to run on current executor, + // or enqueue to global as fallback + } } } ``` -The isolation rules for the `startSynchronously` family of APIs need to account for this synchronous "first part" of the execution. We propose the following set of rules to make this API concurrency-safe: +The `Task.immediateDetached` does not inherit isolation automatically, same as it's non-immediate `Task.detached` equivalent. -- The operation closure is `sending`. -- The operation closure may only specify an isolation (e.g. `{ @MainActor in }`), if and only if already statically contained within the same isolation context. +Task group methods which create immediate child tasks do not inherit isolation automatically, although they are allowed to specify an isolation explicitly. This is the same as existing TaskGroup APIs (`addTask`). -This allows for the following pattern, where we can enter an asynchronous task context, from a synchronous function, that is _known_ to be isolated to the main actor already: +### Scheduling immediate tasks given matching current and requested isolation -```swift -@MainActor var counter: Int = 0 +The Swift concurrency runtime maintains a notion of the "current executor" in order to be able to perform executor switching and isolation checking dynamically. This information is managed runtime, and is closely related to compile time isolation rules, but it is also maintained throughout nonisolated and synchronous functions. + +Immediate tasks make use of this executor tracking to determine on which executor we're asking the task to "immediately" execute. It is possible to start an immediate task in a synchronous context, and even require it to have some specific isolation. -func asyncUpdateCounter() async { counter += 1 } +The following example invokes the synchronous `sayHello()` function from a `@MainActor` isolated function. The static information about this isolation is _lost_ by the synchronous function. And the compiler will assume, that the `sayHello()` function is not isolated to any specific context -- after all, the actual isolated context would depending on where we call it from, and we're not passing an `isolated` parameter to this synchronous function. + +By using an immediate task the runtime is able to notice that the requested, and current, executor are actually the same (`MainActor`) and therefore execute the task _immediately_ on the caller's executor _and_ with the expected `@MainActor` isolation, which is guaranteed to be correct: + +```swift +@MainActor var counterUsual = 0 +@MainActor var counterImmediate = 0 @MainActor func sayHelloOnMain() { - Task.startSynchronously { @MainActor in // ✅ ok, caller isolation is also @MainActor - await asyncUpdateCounter() - } + sayHello() // call synchronous function from @MainActor +} + +// synchronous function +func sayHello() { + // We are "already on" the main actor + MainActor.assertIsolated() - Task.startSynchronously { @OtherGlobalActor in // ❌ error: MainActor != OtherGlobalActor - await asyncUpdateCounter() + // Performs an enqueue and will execute task "later" + Task { @MainActor in + counterUsual += 1 + } + // At this point (in this specific example), `counterUsual` is still 0. + // We did not "give up" the main actor executor, so the new Task could not execute yet. + + // Execute the task immediately on the calling context (!) + Task.immediate { @MainActor in + counterImmediate += 1 } + // At this point (in this specific cexample), + // `counterImmediate` is guaranteed to == 1! } ``` -Task executors do not influence the static isolation properties of code, and thus have no impact on the isolation semantics of these APIs. In general, task executors are orthogonal to actor isolation, and while they can influence which actual executor a default actor or global async function would use to execute some piece of code they have no impact on isolation properties and therefore safety properties of a piece of code. +The difference between the use of `Task.init` and `Task.immediate` is _only_ in the specific execution ordering semantics those two tasks exhibit. + +Because we are dynamically already on the expected executor, the immediate task will not need to enqueue and "run later" the new task, but instead will take over the calling executor, and run the task body immediately (up until the first suspension point). + +This can have importand implications about the observed order of effects and task execution, so it is important for developers to internalize this difference in scheduling semantics. + +If the same `sayHello()` function were to be invoked from some execution context _other than_ the main actor, both tasks–which specify the requested isolation to be `@MainActor`–will perform the usual enqueue and "run later": + +```swift +@MainActor var counterUsual = 0 +@MainActor var counterImmediate = 0 + +actor Caplin { + func sayHelloFromCaplin() { + sayHello() // call synchronous function from Caplin + } +} + +func sayHello() { + Task { @MainActor in // enqueue, "run later" + counterUsual += 1 + } + Task.immediate { @MainActor in // enqueue, "run later" + counterImmediate += 1 + } + + // at this point, no guarantees can be made ablue the values of the `counter` variables +} +``` + +This means that a `Task.immediate` can be used to opportunistically attempt to "run immediately, if the caller matches my required isolation, otherwise, enqueue and run the task later". Which is a semantic that many developers have requested in the past, most often in relation to the `MainActor`. + +The same technique of specifying a required target isolation may be used with the new TaskGroup APIs, such as `TaskGroup/addImmediateTask`. ### Interaction with `Actor/assumeIsolated` -In [SE-0392: Custom Actor Executor](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0392-custom-actor-executors.md) we introduced the ability to dynamically recover isolation information using the `assumeIsolated` API. It can be used to dynamically recover the runtime information about whether we are executing on some specific actor. +In [SE-0392: Custom Actor Executors](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0392-custom-actor-executors.md) we introduced the ability to dynamically recover isolation information using the `Actor/assumeIsolated` API. It can be used to dynamically recover the runtime information about whether we are executing on some specific actor. -The `assumeIsolated` shares some ideas with `startSynchronously` however it is distinctly different. For example, while both APIs can effectively be used to "notice we are running on the expected actor, and therefore perform some work on its context". However, `assumeIsolated` does _not_ create a new asynchronous context, while `Task.startSynchronously` does: +The `assumeIsolated` shares some ideas with `Task/immediate` however it is distinctly different. For example, while both APIs can effectively be used to "notice we are running on the expected actor, and therefore perform some work on its context". However, `assumeIsolated` does _not_ create a new asynchronous context, while `Task.startSynchronously` does: ```swift @MainActor @@ -232,13 +336,13 @@ func synchronous() { } ``` -We can compose assumeIsolated with `startSynchronously` to ensure we synchronously start a task on the MainActor if we know we are already running on it, like this: +We can compose `assumeIsolated` with `Task.immediate` to both assert that the current execution context must the the expected actor, and form a new asynchronous task that will immediately start on that actor: ```swift func alwaysCalledFromMainActor() { // we know this because e.g. documentation, but the API wasn't annotated MainActor.assumeIsolated { // @MainActor isolated assert(num == 0) - Task.startSynchronously { @MainActor in + Task.startSynchronously { // @MainActor isolated num +=1 // ✅ ok assert(num == 1) // since we are guaranteed nothing else executed since the 'num == 0' assertion @@ -248,15 +352,50 @@ func alwaysCalledFromMainActor() { // we know this because e.g. documentation, b } ``` -The synchronously started task will not suspend and context switch until any of the called async methods does. For example, we are guaranteed that there will be no interleaved code execution between the `assert(num == 0)` in our example, and the `num += 1` inside the synchronously started task. +The immediately started task will not suspend and context switch until any of the called async methods does. For example, we are guaranteed that there will be no interleaved code execution between the `assert(num == 0)` in our example, and the `num += 1` inside the synchronously started task. After the suspension point though, there may have been other tasks executed on the main actor, and we should check the value of `num` again. -### Structured concurrency semantics +### Immediate child tasks + +Immediate child tasks tasks can be created using the various `*TaskGroup/addImmediateTask*` methods behave similarily to their normal structured child task API counterparts (`*TaskGroup/addTask*`). + +Child tasks, including immediate child tasks, do not infer their isolation from the enclosing context, and by default are `nonisolated`. + +```swift +actor Worker { + func workIt(work: Work) async { + await withDiscardingTaskGroup { + group.addImmediateTask { // nonisolated + work.synchronousWork() + } + } + } +} +``` + +While the immediate task in the above example is indeed `nonisolated` and does not inherit the Worker's explicit isolation, it will start out immediately on the Worker's executor. Since this example features _no suspension points_ in the task group child tasks, this is effectively synchronously going to execute those child tasks on the caller (`self`). In other words, this is not performing any of its work in parallel. + +If we were to modify the work to have potential suspension points like so: + +```swift +actor Worker { + func workIt(work: Work) async { + await withDiscardingTaskGroup { + group.addImmediateTask { // nonisolated + // [1] starts on caller immediately + let partialResult = await work.work() // [2] actually suspends + // [3] resumes on global executor (or task executor, if there was one set) + work.moreWork(partialResult) + } + } + } +} +``` -Synchronously started tasks behave exactly the same as their fully asynchronous equivalents. +The actual suspension happening in the `work()` call, means that this task group actually would exhibit some amount of concurrent execution with the calling actor -- the remainder between `[2]` and `[3]` would execute on the global concurrent pool -- concurrently to the enclosing actor. -In short, cancellation, and priority escalation remains automatic for structured tasks created using TaskGroup APIs, however they do not propagate automatically for unstructured tasks created using the `Task.startSynchronously[Detached](...)` APIs. Task locals and base priority also functions the same way as usual. +Cancellation, task locals, priority escalation, and any other structured concurrency semantics remain the same for structured child tasks automatically for unstructured tasks created using the `Task/immediate[Detached]` APIs. The only difference in behavior is where these synchronously started tasks _begin_ their execution. @@ -283,14 +422,14 @@ The dynamically asserting version would be something like this: func onSomethingHappenedAlwaysOnMainThread(something: Something) { // we "know" we are on the MainActor, however this is a legacy API that is not an 'async' method // so we cannot call any other async APIs unless we create a new task. - Task.startSynchronously { @MainActor in + Task.immediate { @MainActor in await showThingy() } } func onSomethingHappenedSometimesOnMainThread(something: Something) { // 💥 Must assert at runtime if not on main thread - Task.startSynchronously { @MainActor in + Task.immediate { @MainActor in await showThingy() } } @@ -323,7 +462,7 @@ actor Caplin { var num: Int = 0 func check() { - Task.startSynchronouslyDetached { + Task.immediateDetached { num += 1 // could be ok; we know we're synchronously executing on caller try await Task.sleep(for: .seconds(1)) @@ -336,52 +475,7 @@ actor Caplin { } ``` -### Dynamically "run synchronously if in right context, otherwise enqueue as usual" - -The proposed `startSynchronously` API is a tool to be used in performance and correctness work, and any "may sometimes run synchronously, it depends" is not a good solution to the task at hand. Because of that, we aim for this API to provide the _predictable_ behavior of running synchronously on the caller, without impacting the isolation, that can compose naturally with `assumeIsolated` that can recover dynamic into static isolation information. - -For example, we'll be able to build an API that composes the proposed `startSynchronously` with a not yet proposed but under investigation `Task/isIsolated(to: some Actor) -> Bool` API in order to offer an API that implements the semantics that some developers have been asking for a while: - -- if already dynamically isolated on the expected actor, run synchronously, -- if not, schedule a task to execute the same operation later. - -Using a combination of (a) `Task/startSynchronously`, (b) `Actor/assumeIsolated`, and some form of boolean returning the not yet proposed (c) `isIsolated` (which would be a `Bool` returning equivalent of `assumeIsolated`), we will be able to build such function by composing those more fundamental concurrency operations: - -```swift -func tryRunSynchronouslyOrAsynchronouslyOtherwise( - operation: sending @escaping () async -> Success -) -> Task { - guard let actor = operation.isolation else { - // no specific isolation, just run async - return Task { try await operation() } - } - - if Task.__current.__isIsolated(to: actor) { // (c) !!! does not exist yet !!! - // we definitely are executing on 'actor' - return actor.assumeIsolated { // (b) guaranteed to not crash - // recovered static isolation information about 'actor' - // (a) use startSynchronously with specific actor isolation - return Task.runSynchronously { - [isolated actor] in // !! does not exist yet (closure isolation control) !! - try await operation() - } - } - } else { - // we are not isolated to 'actor' and therefore must schedule a normal unstructured task - return Task { try await operation() } - } -} -``` - -Or even better we could build the same API with structured concurrency: - -```swift -func tryRunSynchronouslyOrAsynchronouslyOtherwise( - operation: sending @escaping () async throws -> Success -) async rethrows -> Success { /* same, but use TaskGroup inside */ } -``` - -### Expressing closure isolation tied to function parameter: `@isolated(to:)` +### Implementation detail: Expressing closure isolation tied to function parameter: `@isolated(to:)` The currently proposed API is working within the limitations of what is expressible in today's isolation model. It would be beneficial to be able to express the startSynchronously API if we could spell something like "this closure must be isolated to the same actor as the calling function" which would allow for the following code: @@ -409,3 +503,7 @@ public static func startSynchronously( The introduction of a hypothetical `@isolated(to:)` paired with an `isolated` `#isolation` defaulted actor parameter, would allow us to express "the *operation* closure statically inherits the exact same isolation as is passed to the isolation parameter of the startSynchronously method". This naturally expresses the semantics that the startSynchronously is offering, and would allow to _stay_ on that isolation context after resuming from the first suspension inside the operation closure. Implementing this feature is a large task, and while very desirable we are not ready yet to commit to implementing it as part of this proposal. If and when this feature would become available, we would adopt it in the startSynchronously APIs. + +### Changelog + +- Moved the alternative considered of "attempt to run immediately, or otherwise just enqueue as usual" into the proposal proper From 2add1051788c6e94cd761374724a1ddcf78b268f Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Thu, 1 May 2025 16:55:08 -0500 Subject: [PATCH 4271/4563] Change state of ST-0010 to Implemented (#2820) * Change state of ST-0010 to Implemented * Add second implementation PR link --- proposals/testing/0010-evaluate-condition.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/testing/0010-evaluate-condition.md b/proposals/testing/0010-evaluate-condition.md index b0ac6ecbaa..6a68caad10 100644 --- a/proposals/testing/0010-evaluate-condition.md +++ b/proposals/testing/0010-evaluate-condition.md @@ -3,9 +3,9 @@ * Proposal: [ST-0010](0010-evaluate-condition.md) * Authors: [David Catmull](https://github.com/Uncommon) * Review Manager: [Stuart Montgomery](https://github.com/stmontgomery) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Bug: [swiftlang/swift-testing#903](https://github.com/swiftlang/swift-testing/issues/903) -* Implementation: [swiftlang/swift-testing#909](https://github.com/swiftlang/swift-testing/pull/909) +* Implementation: [swiftlang/swift-testing#909](https://github.com/swiftlang/swift-testing/pull/909), [swiftlang/swift-testing#1097](https://github.com/swiftlang/swift-testing/pull/1097) * Review: ([pitch](https://forums.swift.org/t/pitch-introduce-conditiontrait-evaluate/77242)), ([review](https://forums.swift.org/t/st-0010-public-api-to-evaluate-conditiontrait/79232)), ([acceptance](https://forums.swift.org/t/accepted-st-0010-public-api-to-evaluate-conditiontrait/79577)) ## Introduction From c3a4e6d50f314f54624eb14e8c15eddc6b91779a Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Fri, 2 May 2025 07:17:32 +0900 Subject: [PATCH 4272/4563] amend a few remaining startSynchronously words --- ...task-start-synchronously-on-caller-context.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/proposals/0472-task-start-synchronously-on-caller-context.md b/proposals/0472-task-start-synchronously-on-caller-context.md index 93cfb85323..75bfaec705 100644 --- a/proposals/0472-task-start-synchronously-on-caller-context.md +++ b/proposals/0472-task-start-synchronously-on-caller-context.md @@ -315,7 +315,7 @@ The same technique of specifying a required target isolation may be used with th In [SE-0392: Custom Actor Executors](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0392-custom-actor-executors.md) we introduced the ability to dynamically recover isolation information using the `Actor/assumeIsolated` API. It can be used to dynamically recover the runtime information about whether we are executing on some specific actor. -The `assumeIsolated` shares some ideas with `Task/immediate` however it is distinctly different. For example, while both APIs can effectively be used to "notice we are running on the expected actor, and therefore perform some work on its context". However, `assumeIsolated` does _not_ create a new asynchronous context, while `Task.startSynchronously` does: +The `assumeIsolated` shares some ideas with `Task/immediate` however it is distinctly different. For example, while both APIs can effectively be used to "notice we are running on the expected actor, and therefore perform some work on its context". However, `assumeIsolated` does _not_ create a new asynchronous context, while `Task.immediate` does: ```swift @MainActor @@ -342,7 +342,7 @@ We can compose `assumeIsolated` with `Task.immediate` to both assert that the cu func alwaysCalledFromMainActor() { // we know this because e.g. documentation, but the API wasn't annotated MainActor.assumeIsolated { // @MainActor isolated assert(num == 0) - Task.startSynchronously { // @MainActor isolated + Task.immediate { // @MainActor isolated num +=1 // ✅ ok assert(num == 1) // since we are guaranteed nothing else executed since the 'num == 0' assertion @@ -413,7 +413,7 @@ This proposal is purely ABI additive. An important use case of this API is to support calling into an actor isolated context when in a synchronous function that is dynamically already running on that actor. This situation can occur both with instance actors and global actors, however the most commonly requested situation where this shows up is synchronous handler methods in existing frameworks, and which often may have had assumptions about the main thread, and did not yet annotate their API surface with @MainActor annotations. -It would be possible to create a _dynamically asserting_ version of `Task.startSynchronously`, which does handle the happy path where indeed we "know" where we're going to be called quite well, but gives a *false sense of security* as it may crash at runtime, in the same way the `Actor/preconditionIsolated()` or `Actor/assumeIsolated` APIs do. We believe we should not add more such dynamically crashing APIs, but rather lean into the existing APIs and allow them compose well with any new APIs that should aim to complement them. +It would be possible to create a _dynamically asserting_ version of `Task.immediate`, which does handle the happy path where indeed we "know" where we're going to be called quite well, but gives a *false sense of security* as it may crash at runtime, in the same way the `Actor/preconditionIsolated()` or `Actor/assumeIsolated` APIs do. We believe we should not add more such dynamically crashing APIs, but rather lean into the existing APIs and allow them compose well with any new APIs that should aim to complement them. The dynamically asserting version would be something like this: @@ -477,12 +477,12 @@ actor Caplin { ### Implementation detail: Expressing closure isolation tied to function parameter: `@isolated(to:)` -The currently proposed API is working within the limitations of what is expressible in today's isolation model. It would be beneficial to be able to express the startSynchronously API if we could spell something like "this closure must be isolated to the same actor as the calling function" which would allow for the following code: +The currently proposed API is working within the limitations of what is expressible in today's isolation model. It would be beneficial to be able to express the immediate API if we could spell something like "this closure must be isolated to the same actor as the calling function" which would allow for the following code: ```swift @MainActor func test() { - Task.startSynchronously { /* inferred to be @MainActor */ + Task.immediate { /* inferred to be @MainActor */ num += 1 } } @@ -493,16 +493,16 @@ func test() { The way to spell this in an API could be something like this: ```swift -public static func startSynchronously( +public static func immediate( ... isolation: isolated (any Actor)? = #isolation, operation: @escaping @isolated(to: isolation) sending async throws(Failure) -> Success, ) -> Task ``` -The introduction of a hypothetical `@isolated(to:)` paired with an `isolated` `#isolation` defaulted actor parameter, would allow us to express "the *operation* closure statically inherits the exact same isolation as is passed to the isolation parameter of the startSynchronously method". This naturally expresses the semantics that the startSynchronously is offering, and would allow to _stay_ on that isolation context after resuming from the first suspension inside the operation closure. +The introduction of a hypothetical `@isolated(to:)` paired with an `isolated` `#isolation` defaulted actor parameter, would allow us to express "the *operation* closure statically inherits the exact same isolation as is passed to the isolation parameter of the `immediate` method". This naturally expresses the semantics that the `immediate` is offering, and would allow to _stay_ on that isolation context after resuming from the first suspension inside the operation closure. -Implementing this feature is a large task, and while very desirable we are not ready yet to commit to implementing it as part of this proposal. If and when this feature would become available, we would adopt it in the startSynchronously APIs. +Implementing this feature is a large task, and while very desirable we are not ready yet to commit to implementing it as part of this proposal. If and when this feature would become available, we would adopt it in the `immediate` APIs. ### Changelog From 0a99356424f0914e847451cc6706d8bb86fa3e97 Mon Sep 17 00:00:00 2001 From: Rachel Brindle Date: Thu, 1 May 2025 20:41:18 -0700 Subject: [PATCH 4273/4563] Mark ST-0009 Attachments as Implemented in Swift 6.2 --- proposals/testing/0009-attachments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0009-attachments.md b/proposals/testing/0009-attachments.md index e8442b2a81..8fee84e792 100644 --- a/proposals/testing/0009-attachments.md +++ b/proposals/testing/0009-attachments.md @@ -3,7 +3,7 @@ * Proposal: [ST-0009](0009-attachments.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Review Manager: [Rachel Brindle](https://github.com/younata) -* Status: **Accepted with revisions** +* Status: **Implemented (Swift 6.2)** * Bug: [swiftlang/swift-testing#714](https://github.com/swiftlang/swift-testing/issues/714) * Implementation: [swiftlang/swift-testing#973](https://github.com/swiftlang/swift-testing/pull/973) * Review: ([acceptance](https://forums.swift.org/t/accepted-with-modifications-st-0009-attachments/79193)), ([review](https://forums.swift.org/t/st-0009-attachments/78698)), ([pitch](https://forums.swift.org/t/pitch-attachments/78072)) From db11d70a85874baa9560e562315082fca89dd5dd Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Fri, 2 May 2025 09:54:09 +0200 Subject: [PATCH 4274/4563] Update 0008-exit-tests.md: update status to 'Implemented' --- proposals/testing/0008-exit-tests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0008-exit-tests.md b/proposals/testing/0008-exit-tests.md index 6a7849b0a6..ce4f95def1 100644 --- a/proposals/testing/0008-exit-tests.md +++ b/proposals/testing/0008-exit-tests.md @@ -3,7 +3,7 @@ * Proposal: [ST-0008](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0008-exit-tests.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Review Manager: [Maarten Engels](https://github.com/maartene) -* Status: **Accepted with Modifications** +* Status: **Implemented (Swift 6.2)** * Bug: [apple/swift-testing#157](https://github.com/apple/swift-testing/issues/157) * Implementation: [apple/swift-testing#324](https://github.com/swiftlang/swift-testing/pull/324) * Previous Revision: [1](https://github.com/swiftlang/swift-evolution/blob/fdfc7867df4e35e29b2a24edee34ea4412ec15b0/proposals/testing/0008-exit-tests.md) From 74fd4e039859fa494bbacb5dc880d2eecd2721a4 Mon Sep 17 00:00:00 2001 From: Daniel Grumberg Date: Wed, 12 Mar 2025 16:47:11 +0000 Subject: [PATCH 4275/4563] First draft of proposal for static library binary target on non apple platforms --- ...brary-binary-target-non-apple-platforms.md | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 proposals/XXXX-swiftpm-static-library-binary-target-non-apple-platforms.md diff --git a/proposals/XXXX-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/XXXX-swiftpm-static-library-binary-target-non-apple-platforms.md new file mode 100644 index 0000000000..e9701dcc60 --- /dev/null +++ b/proposals/XXXX-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -0,0 +1,161 @@ +# Binary Static Library Dependencies + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: +* Review Manager: TBD +* Status: **Awaiting implementation** + + + +* Implementation: [swiftlang/swift-package-manager#6967](https://github.com/swiftlang/swift-package-manager/pull/6967) [swiftlang/swift-package-manager#8605](https://github.com/swiftlang/swift-package-manager/pull/8605) +* Bugs: [Swift Package Manger Issue](https://github.com/swiftlang/swift-package-manager/issues/7035) + + + + +## Introduction + +Swift continues to grow as a cross-platform language supporting a wide variety of use cases from [programming embedded device](https://www.swift.org/blog/embedded-swift-examples/) to [server-side development](https://www.swift.org/documentation/server/) across a multitude of [operating systems](https://www.swift.org/documentation/articles/static-linux-getting-started.html). +However, currently SwiftPM supports linking against binary dependencies on Apple platforms only. +This proposal aims to make it possible to provide static library dependencies on non-Apple platforms. + +Swift-evolution thread: + +## Motivation + +The Swift Package Manager’s [`binaryTarget` type](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md) lets packages vend libraries that either cannot be built in Swift Package Manager for technical reasons, +or for which the source code cannot be published for legal or other reasons. + +In the current version of SwiftPM, binary targets support the following: + +* Libraries in an Xcode-oriented format called XCFramework, and only for Apple platforms, introduced in [SE-0272](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md). +* Executables through the use of artifact bundles introduced in [SE-0305](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md). + +We aim here to bring a subset of the XCFramework capabilities to non-Apple platforms in a safe way. + +While this proposal is specifically focused on binary static library dependencies without unresolved external symbols on non-Apple platforms, +it tries to do so in a way that will not prevent broader future support for static libraries and dynamically linked libraries. + +## Proposed solution + +This proposal extends artifact bundles introduced by [SE-0305](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md) to include a new kind of artifact type to represent a binary library dependency: `staticLibrary`. +The artifact manifest would encode the following information for each variant: + +* The static library to pass to the linker. + On Apple and Linux platforms, this would be `.a` files and on Windows it would be a `.lib` file. +* Enough information to be able to use the library's API in the packages source code, + i.e., headers and module maps for libraries exporting a C-based interface. + +Additionnaly, we propose the addition of an auditing tool that can validate the library artifact is safe to use across the platforms supported by the Swift project. +Such a tool would ensure that people do not accidentally distribute artifacts that require dependencies that are not met on the various deployment platforms. + +## Detailed design + +This section describes the changes to artifact bundle manifests in detail, the semantic impact of the changes on SwiftPM's build infrastructure, and describes the operation of the auditing tool. + +### Artifact Manifest Semantics + +The artifact manifest JSON format for a static library is described below: + +```json +{ + "schemaVersion": "1.0", + "artifacts": { + "": { + "version": "", + "type": "staticLibrary", + "variants": [ + { + "path": "", + "headerPaths": [", ...], + "moduleMapPath": "", + "supportedTriples": ["", ... ], + }, + ... + ] + }, + ... + } +} +``` + +The additions are: + +* The `staticLibrary` artifact `type` that indicates this binary artifact is not an executable but rather a static library to link against. +* The `headerPaths` field specifies directory paths relative to the root of the artifact bundle that contain the header interfaces to the static library. + These are forwarded along to the swift compiler (or the C compiler) using the usual search path arguments. + Each of these directories can optionally contain a `module.modulemap` file that will be used for importing the API into Swift code. +* The optional `moduleMapPath` field specifies a custom module map to use if the header paths do not contain the module definitions or to provide custom overrides. + +As with executable binary artifacts, the `path` field represents the relative path to the binary from the root of the artifact bundle, +and the `supportedTriples` field provides information about the target triples supported by this variant. + +An example artifact might look like: +```json +{ + "schemaVersion": "1.0", + "artifacts": { + "my-artifact": { + "type": "staticLibrary", + "version": "1.0.0", + "variants": [ + { + "path": "artifact.a", + "headerPaths": ["include"], + "supportedTriples": ["aarch64-unknown-linux-gnu"] + } + ] + } + } +} +``` + +### Auditing tool + +Without proper auditing it would be very easy to provide binary static library artifacts that call into unresolved external symbols that are not available on the runtime platform, e.g., due to missing linkage to a system dynamic library. + +We propose the introduction of a new tool that can validate the "safety" of a binary library artifact across the platforms it supports and the corresponding runtime environment. + +In this proposal we restrict ourselves to static libraries that do not have any external dependencies. +To achieve this we need to be able to detect validate this property across the three object file formats used in static libraries on our supported platforms: [Mach-O](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeFootprint/Articles/MachOOverview.html#//apple_ref/doc/uid/20001860-BAJGJEJC) on Apple platforms, [ELF](https://refspecs.linuxfoundation.org/elf/elf.pdf) on Linux-based platforms, and [COFF](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format) on Windows. +All three formats express references to external symbols as _relocations_ which reside in a single section of each object file. + +The tool would scan every object file in the static library and construct a complete list of symbols defined and referenced across the entire library. +The tool would then check that the referenced symbols list is a subset of the set of defined symbols and emit an error otherwise. + +## Security + +This proposal brings the security implications outlined in [SE-0272](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md#security) to non-Apple platforms, +namely that a malicious attacker having access to both the server hosting the artifact and the git repository that vends the Package Manifest could provide a malicious library. +Users should exercise caution when onboarding binary dependencies. + +## Impact on existing packages + +No current package should be affected by this change since this is only an additive change in enabling SwiftPM to use binary target library dependencies on non-Apple platforms. + +## Future directions + +### Extend binary compatibility guarantees + +This proposal makes no guarantees regarding the availability of symbols in the runtime environment and therefore the auditing tool validates that all referenced symbols in the binary dependency are already resolved. +In the future we would like to provide more exhaustive guarantees about core system libraries and symbols that are guaranteed to be present on supported runtime environments, e.g. `libc.so`. +This is very similar to the [`manylinux`](https://peps.python.org/pep-0513/) effort in the Python community, but the standardization effort would need to extend to Apple platforms and Windows platforms. +Specifically we would extend the auditing tool to allow unresolved external symbols to symbols that are known to exist. + +### Support Swift static libraries (needs fact checking) + +Today Swift static libraries can not be fully statically linked. +However, once binary compatibility guarantees are extended to include Swift SDK symbols, +static libraries exposing a purely Swift API should be supported. +To do this we would extend the static library binary artifact manifest to provide a `.swiftinterface` file that can be consumed by the Swift compiler. + +### Add support for dynamically linked dependencies + +On Windows dynamic linking requires an _import library_ which is a small static library that contains stubs for symbols exported by the dynamic library. +These stubs are roughly equivalent to a PLT entry in an ELF executable, but are generated during the build of the dynamic library and must be provided to clients of the library for linking purposes. +Similarly on Linux and Apple platforms binary artifact maintainers may wish to provide a dynamic library stub to improve link performance. +To support these use cases the library binary artifact manifest schema could be extended to provide facilities to provide both a link-time and runtime dependency. From 2ae6bde3b6c3a2e7d4c87c053786a8ed65f9c384 Mon Sep 17 00:00:00 2001 From: Daniel Grumberg Date: Thu, 13 Mar 2025 15:45:06 +0000 Subject: [PATCH 4276/4563] Specify operation of auditing tool using llvm-objdump --- ...brary-binary-target-non-apple-platforms.md | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/proposals/XXXX-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/XXXX-swiftpm-static-library-binary-target-non-apple-platforms.md index e9701dcc60..d01b826d3b 100644 --- a/proposals/XXXX-swiftpm-static-library-binary-target-non-apple-platforms.md +++ b/proposals/XXXX-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -1,7 +1,7 @@ # Binary Static Library Dependencies * Proposal: [SE-NNNN](NNNN-filename.md) -* Authors: +* Authors: [Daniel Grumberg](https://github.com/daniel-grumberg), [Max Desiatov](https://github.com/MaxDesiatov), [Franz Busch](https://github.com/FranzBusch) * Review Manager: TBD * Status: **Awaiting implementation** @@ -21,7 +21,7 @@ Swift continues to grow as a cross-platform language supporting a wide variety of use cases from [programming embedded device](https://www.swift.org/blog/embedded-swift-examples/) to [server-side development](https://www.swift.org/documentation/server/) across a multitude of [operating systems](https://www.swift.org/documentation/articles/static-linux-getting-started.html). However, currently SwiftPM supports linking against binary dependencies on Apple platforms only. -This proposal aims to make it possible to provide static library dependencies on non-Apple platforms. +This proposal aims to make it possible to provide static library dependencies exposing a C interface on non-Apple platforms. Swift-evolution thread: @@ -37,7 +37,7 @@ In the current version of SwiftPM, binary targets support the following: We aim here to bring a subset of the XCFramework capabilities to non-Apple platforms in a safe way. -While this proposal is specifically focused on binary static library dependencies without unresolved external symbols on non-Apple platforms, +While this proposal is specifically focused on binary static library dependencies without unexpected unresolved external symbols on non-Apple platforms, it tries to do so in a way that will not prevent broader future support for static libraries and dynamically linked libraries. ## Proposed solution @@ -50,7 +50,7 @@ The artifact manifest would encode the following information for each variant: * Enough information to be able to use the library's API in the packages source code, i.e., headers and module maps for libraries exporting a C-based interface. -Additionnaly, we propose the addition of an auditing tool that can validate the library artifact is safe to use across the platforms supported by the Swift project. +Additionnaly, we propose the addition of an auditing tool that can validate the library artifact is safe to use across the Linux-based platforms supported by the Swift project. Such a tool would ensure that people do not accidentally distribute artifacts that require dependencies that are not met on the various deployment platforms. ## Detailed design @@ -120,13 +120,20 @@ Without proper auditing it would be very easy to provide binary static library a We propose the introduction of a new tool that can validate the "safety" of a binary library artifact across the platforms it supports and the corresponding runtime environment. -In this proposal we restrict ourselves to static libraries that do not have any external dependencies. +In this proposal we restrict ourselves to static libraries that do not have any external dependencies beyond the C standard library and runtime. To achieve this we need to be able to detect validate this property across the three object file formats used in static libraries on our supported platforms: [Mach-O](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeFootprint/Articles/MachOOverview.html#//apple_ref/doc/uid/20001860-BAJGJEJC) on Apple platforms, [ELF](https://refspecs.linuxfoundation.org/elf/elf.pdf) on Linux-based platforms, and [COFF](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format) on Windows. All three formats express references to external symbols as _relocations_ which reside in a single section of each object file. -The tool would scan every object file in the static library and construct a complete list of symbols defined and referenced across the entire library. +We propose adding the `llvm-objdump` to the toolchain to provide the capability to inspect relocations across all three supported object file formats. The tool would use `llvm-objdump` every object file in the static library and construct a complete list of symbols defined and referenced across the entire library. +Additionally, the tool would construct a simple C compiler invocation to derive to generate a default linker invocation. +This would be used to derive the libraries linked by default in a C program, these libraries would then be scanned to contribute to the list of defined symbols. The tool would then check that the referenced symbols list is a subset of the set of defined symbols and emit an error otherwise. +This would be sufficient to guarantee that all symbols from the static library would be available at runtime for statically linked executables or for ones running on the build host. +To ensure maximum runtime compatibility we would also provide a Linux-based Docker image that uses the oldest supported `glibc` for a given Swift version. +As `glibc` is backwards compatible, a container running the audit on a given static library would ensure that the version of `glibc` on any runtime platform would be compatible with the binary artifact. +This strategy as been succesfully employed in the Python community with [`manylinux`](https://peps.python.org/pep-0513/). + ## Security This proposal brings the security implications outlined in [SE-0272](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md#security) to non-Apple platforms, @@ -139,19 +146,16 @@ No current package should be affected by this change since this is only an addit ## Future directions -### Extend binary compatibility guarantees +### Support Swift static libraries -This proposal makes no guarantees regarding the availability of symbols in the runtime environment and therefore the auditing tool validates that all referenced symbols in the binary dependency are already resolved. -In the future we would like to provide more exhaustive guarantees about core system libraries and symbols that are guaranteed to be present on supported runtime environments, e.g. `libc.so`. -This is very similar to the [`manylinux`](https://peps.python.org/pep-0513/) effort in the Python community, but the standardization effort would need to extend to Apple platforms and Windows platforms. -Specifically we would extend the auditing tool to allow unresolved external symbols to symbols that are known to exist. +To do this we would extend the static library binary artifact manifest to provide a `.swiftinterface` file that can be consumed by the Swift compiler to import the Swift APIs. +Additionally we would extend the auditing tool to validate the usage of Swift standard library and runtime symbols, e.g., from `libSwiftCore`. -### Support Swift static libraries (needs fact checking) +### Extend binary compatibility guarantees -Today Swift static libraries can not be fully statically linked. -However, once binary compatibility guarantees are extended to include Swift SDK symbols, -static libraries exposing a purely Swift API should be supported. -To do this we would extend the static library binary artifact manifest to provide a `.swiftinterface` file that can be consumed by the Swift compiler. +This proposal limits itself to providing facilities for binary compatibility only with the C standard library and runtime. +In the future we could provide a system to allow binary artifact distributors to specify additional linkage dependencies for their binary artifacts. +These would be used to customize the operation of the audit tool and perform automatic linking of them in any client target that depends on the binary artifact, in the same way [CMake](https://cmake.org/cmake/help/v4.0/prop_tgt/INTERFACE_LINK_LIBRARIES.html) propagates link dependencies transitively. ### Add support for dynamically linked dependencies From 191d46102c66b12fb615bb4eb204d1573a5dff0e Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 2 May 2025 14:13:08 +0100 Subject: [PATCH 4277/4563] Updated PR with number. --- ...tpm-static-library-binary-target-non-apple-platforms.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{XXXX-swiftpm-static-library-binary-target-non-apple-platforms.md => 0467-swiftpm-static-library-binary-target-non-apple-platforms.md} (98%) diff --git a/proposals/XXXX-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md similarity index 98% rename from proposals/XXXX-swiftpm-static-library-binary-target-non-apple-platforms.md rename to proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md index d01b826d3b..d453502706 100644 --- a/proposals/XXXX-swiftpm-static-library-binary-target-non-apple-platforms.md +++ b/proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -1,9 +1,9 @@ # Binary Static Library Dependencies -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0467](0467-swiftpm-static-library-binary-target-non-apple-platforms.md) * Authors: [Daniel Grumberg](https://github.com/daniel-grumberg), [Max Desiatov](https://github.com/MaxDesiatov), [Franz Busch](https://github.com/FranzBusch) -* Review Manager: TBD -* Status: **Awaiting implementation** +* Review Manager: [Kuba Mracek](https://github.com/kubamracek) +* Status: **Active Review (2 May...15 May, 2005)** From 55867ad901067022312c81c548bfddf96ac5afe1 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 2 May 2025 14:15:26 +0100 Subject: [PATCH 4278/4563] Revert "[SE-0467] Binary Static Library Artifacts Proposal" --- ...brary-binary-target-non-apple-platforms.md | 165 ------------------ 1 file changed, 165 deletions(-) delete mode 100644 proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md diff --git a/proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md deleted file mode 100644 index d453502706..0000000000 --- a/proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md +++ /dev/null @@ -1,165 +0,0 @@ -# Binary Static Library Dependencies - -* Proposal: [SE-0467](0467-swiftpm-static-library-binary-target-non-apple-platforms.md) -* Authors: [Daniel Grumberg](https://github.com/daniel-grumberg), [Max Desiatov](https://github.com/MaxDesiatov), [Franz Busch](https://github.com/FranzBusch) -* Review Manager: [Kuba Mracek](https://github.com/kubamracek) -* Status: **Active Review (2 May...15 May, 2005)** - - - -* Implementation: [swiftlang/swift-package-manager#6967](https://github.com/swiftlang/swift-package-manager/pull/6967) [swiftlang/swift-package-manager#8605](https://github.com/swiftlang/swift-package-manager/pull/8605) -* Bugs: [Swift Package Manger Issue](https://github.com/swiftlang/swift-package-manager/issues/7035) - - - - -## Introduction - -Swift continues to grow as a cross-platform language supporting a wide variety of use cases from [programming embedded device](https://www.swift.org/blog/embedded-swift-examples/) to [server-side development](https://www.swift.org/documentation/server/) across a multitude of [operating systems](https://www.swift.org/documentation/articles/static-linux-getting-started.html). -However, currently SwiftPM supports linking against binary dependencies on Apple platforms only. -This proposal aims to make it possible to provide static library dependencies exposing a C interface on non-Apple platforms. - -Swift-evolution thread: - -## Motivation - -The Swift Package Manager’s [`binaryTarget` type](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md) lets packages vend libraries that either cannot be built in Swift Package Manager for technical reasons, -or for which the source code cannot be published for legal or other reasons. - -In the current version of SwiftPM, binary targets support the following: - -* Libraries in an Xcode-oriented format called XCFramework, and only for Apple platforms, introduced in [SE-0272](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md). -* Executables through the use of artifact bundles introduced in [SE-0305](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md). - -We aim here to bring a subset of the XCFramework capabilities to non-Apple platforms in a safe way. - -While this proposal is specifically focused on binary static library dependencies without unexpected unresolved external symbols on non-Apple platforms, -it tries to do so in a way that will not prevent broader future support for static libraries and dynamically linked libraries. - -## Proposed solution - -This proposal extends artifact bundles introduced by [SE-0305](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md) to include a new kind of artifact type to represent a binary library dependency: `staticLibrary`. -The artifact manifest would encode the following information for each variant: - -* The static library to pass to the linker. - On Apple and Linux platforms, this would be `.a` files and on Windows it would be a `.lib` file. -* Enough information to be able to use the library's API in the packages source code, - i.e., headers and module maps for libraries exporting a C-based interface. - -Additionnaly, we propose the addition of an auditing tool that can validate the library artifact is safe to use across the Linux-based platforms supported by the Swift project. -Such a tool would ensure that people do not accidentally distribute artifacts that require dependencies that are not met on the various deployment platforms. - -## Detailed design - -This section describes the changes to artifact bundle manifests in detail, the semantic impact of the changes on SwiftPM's build infrastructure, and describes the operation of the auditing tool. - -### Artifact Manifest Semantics - -The artifact manifest JSON format for a static library is described below: - -```json -{ - "schemaVersion": "1.0", - "artifacts": { - "": { - "version": "", - "type": "staticLibrary", - "variants": [ - { - "path": "", - "headerPaths": [", ...], - "moduleMapPath": "", - "supportedTriples": ["", ... ], - }, - ... - ] - }, - ... - } -} -``` - -The additions are: - -* The `staticLibrary` artifact `type` that indicates this binary artifact is not an executable but rather a static library to link against. -* The `headerPaths` field specifies directory paths relative to the root of the artifact bundle that contain the header interfaces to the static library. - These are forwarded along to the swift compiler (or the C compiler) using the usual search path arguments. - Each of these directories can optionally contain a `module.modulemap` file that will be used for importing the API into Swift code. -* The optional `moduleMapPath` field specifies a custom module map to use if the header paths do not contain the module definitions or to provide custom overrides. - -As with executable binary artifacts, the `path` field represents the relative path to the binary from the root of the artifact bundle, -and the `supportedTriples` field provides information about the target triples supported by this variant. - -An example artifact might look like: -```json -{ - "schemaVersion": "1.0", - "artifacts": { - "my-artifact": { - "type": "staticLibrary", - "version": "1.0.0", - "variants": [ - { - "path": "artifact.a", - "headerPaths": ["include"], - "supportedTriples": ["aarch64-unknown-linux-gnu"] - } - ] - } - } -} -``` - -### Auditing tool - -Without proper auditing it would be very easy to provide binary static library artifacts that call into unresolved external symbols that are not available on the runtime platform, e.g., due to missing linkage to a system dynamic library. - -We propose the introduction of a new tool that can validate the "safety" of a binary library artifact across the platforms it supports and the corresponding runtime environment. - -In this proposal we restrict ourselves to static libraries that do not have any external dependencies beyond the C standard library and runtime. -To achieve this we need to be able to detect validate this property across the three object file formats used in static libraries on our supported platforms: [Mach-O](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeFootprint/Articles/MachOOverview.html#//apple_ref/doc/uid/20001860-BAJGJEJC) on Apple platforms, [ELF](https://refspecs.linuxfoundation.org/elf/elf.pdf) on Linux-based platforms, and [COFF](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format) on Windows. -All three formats express references to external symbols as _relocations_ which reside in a single section of each object file. - -We propose adding the `llvm-objdump` to the toolchain to provide the capability to inspect relocations across all three supported object file formats. The tool would use `llvm-objdump` every object file in the static library and construct a complete list of symbols defined and referenced across the entire library. -Additionally, the tool would construct a simple C compiler invocation to derive to generate a default linker invocation. -This would be used to derive the libraries linked by default in a C program, these libraries would then be scanned to contribute to the list of defined symbols. -The tool would then check that the referenced symbols list is a subset of the set of defined symbols and emit an error otherwise. - -This would be sufficient to guarantee that all symbols from the static library would be available at runtime for statically linked executables or for ones running on the build host. -To ensure maximum runtime compatibility we would also provide a Linux-based Docker image that uses the oldest supported `glibc` for a given Swift version. -As `glibc` is backwards compatible, a container running the audit on a given static library would ensure that the version of `glibc` on any runtime platform would be compatible with the binary artifact. -This strategy as been succesfully employed in the Python community with [`manylinux`](https://peps.python.org/pep-0513/). - -## Security - -This proposal brings the security implications outlined in [SE-0272](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md#security) to non-Apple platforms, -namely that a malicious attacker having access to both the server hosting the artifact and the git repository that vends the Package Manifest could provide a malicious library. -Users should exercise caution when onboarding binary dependencies. - -## Impact on existing packages - -No current package should be affected by this change since this is only an additive change in enabling SwiftPM to use binary target library dependencies on non-Apple platforms. - -## Future directions - -### Support Swift static libraries - -To do this we would extend the static library binary artifact manifest to provide a `.swiftinterface` file that can be consumed by the Swift compiler to import the Swift APIs. -Additionally we would extend the auditing tool to validate the usage of Swift standard library and runtime symbols, e.g., from `libSwiftCore`. - -### Extend binary compatibility guarantees - -This proposal limits itself to providing facilities for binary compatibility only with the C standard library and runtime. -In the future we could provide a system to allow binary artifact distributors to specify additional linkage dependencies for their binary artifacts. -These would be used to customize the operation of the audit tool and perform automatic linking of them in any client target that depends on the binary artifact, in the same way [CMake](https://cmake.org/cmake/help/v4.0/prop_tgt/INTERFACE_LINK_LIBRARIES.html) propagates link dependencies transitively. - -### Add support for dynamically linked dependencies - -On Windows dynamic linking requires an _import library_ which is a small static library that contains stubs for symbols exported by the dynamic library. -These stubs are roughly equivalent to a PLT entry in an ELF executable, but are generated during the build of the dynamic library and must be provided to clients of the library for linking purposes. -Similarly on Linux and Apple platforms binary artifact maintainers may wish to provide a dynamic library stub to improve link performance. -To support these use cases the library binary artifact manifest schema could be extended to provide facilities to provide both a link-time and runtime dependency. From a1fa71db11a6211e59cc574e51d76ab6f7ec81c7 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 2 May 2025 14:15:55 +0100 Subject: [PATCH 4279/4563] Revert "Revert "[SE-0467] Binary Static Library Artifacts Proposal"" --- ...brary-binary-target-non-apple-platforms.md | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md diff --git a/proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md new file mode 100644 index 0000000000..d453502706 --- /dev/null +++ b/proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -0,0 +1,165 @@ +# Binary Static Library Dependencies + +* Proposal: [SE-0467](0467-swiftpm-static-library-binary-target-non-apple-platforms.md) +* Authors: [Daniel Grumberg](https://github.com/daniel-grumberg), [Max Desiatov](https://github.com/MaxDesiatov), [Franz Busch](https://github.com/FranzBusch) +* Review Manager: [Kuba Mracek](https://github.com/kubamracek) +* Status: **Active Review (2 May...15 May, 2005)** + + + +* Implementation: [swiftlang/swift-package-manager#6967](https://github.com/swiftlang/swift-package-manager/pull/6967) [swiftlang/swift-package-manager#8605](https://github.com/swiftlang/swift-package-manager/pull/8605) +* Bugs: [Swift Package Manger Issue](https://github.com/swiftlang/swift-package-manager/issues/7035) + + + + +## Introduction + +Swift continues to grow as a cross-platform language supporting a wide variety of use cases from [programming embedded device](https://www.swift.org/blog/embedded-swift-examples/) to [server-side development](https://www.swift.org/documentation/server/) across a multitude of [operating systems](https://www.swift.org/documentation/articles/static-linux-getting-started.html). +However, currently SwiftPM supports linking against binary dependencies on Apple platforms only. +This proposal aims to make it possible to provide static library dependencies exposing a C interface on non-Apple platforms. + +Swift-evolution thread: + +## Motivation + +The Swift Package Manager’s [`binaryTarget` type](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md) lets packages vend libraries that either cannot be built in Swift Package Manager for technical reasons, +or for which the source code cannot be published for legal or other reasons. + +In the current version of SwiftPM, binary targets support the following: + +* Libraries in an Xcode-oriented format called XCFramework, and only for Apple platforms, introduced in [SE-0272](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md). +* Executables through the use of artifact bundles introduced in [SE-0305](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md). + +We aim here to bring a subset of the XCFramework capabilities to non-Apple platforms in a safe way. + +While this proposal is specifically focused on binary static library dependencies without unexpected unresolved external symbols on non-Apple platforms, +it tries to do so in a way that will not prevent broader future support for static libraries and dynamically linked libraries. + +## Proposed solution + +This proposal extends artifact bundles introduced by [SE-0305](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0305-swiftpm-binary-target-improvements.md) to include a new kind of artifact type to represent a binary library dependency: `staticLibrary`. +The artifact manifest would encode the following information for each variant: + +* The static library to pass to the linker. + On Apple and Linux platforms, this would be `.a` files and on Windows it would be a `.lib` file. +* Enough information to be able to use the library's API in the packages source code, + i.e., headers and module maps for libraries exporting a C-based interface. + +Additionnaly, we propose the addition of an auditing tool that can validate the library artifact is safe to use across the Linux-based platforms supported by the Swift project. +Such a tool would ensure that people do not accidentally distribute artifacts that require dependencies that are not met on the various deployment platforms. + +## Detailed design + +This section describes the changes to artifact bundle manifests in detail, the semantic impact of the changes on SwiftPM's build infrastructure, and describes the operation of the auditing tool. + +### Artifact Manifest Semantics + +The artifact manifest JSON format for a static library is described below: + +```json +{ + "schemaVersion": "1.0", + "artifacts": { + "": { + "version": "", + "type": "staticLibrary", + "variants": [ + { + "path": "", + "headerPaths": [", ...], + "moduleMapPath": "", + "supportedTriples": ["", ... ], + }, + ... + ] + }, + ... + } +} +``` + +The additions are: + +* The `staticLibrary` artifact `type` that indicates this binary artifact is not an executable but rather a static library to link against. +* The `headerPaths` field specifies directory paths relative to the root of the artifact bundle that contain the header interfaces to the static library. + These are forwarded along to the swift compiler (or the C compiler) using the usual search path arguments. + Each of these directories can optionally contain a `module.modulemap` file that will be used for importing the API into Swift code. +* The optional `moduleMapPath` field specifies a custom module map to use if the header paths do not contain the module definitions or to provide custom overrides. + +As with executable binary artifacts, the `path` field represents the relative path to the binary from the root of the artifact bundle, +and the `supportedTriples` field provides information about the target triples supported by this variant. + +An example artifact might look like: +```json +{ + "schemaVersion": "1.0", + "artifacts": { + "my-artifact": { + "type": "staticLibrary", + "version": "1.0.0", + "variants": [ + { + "path": "artifact.a", + "headerPaths": ["include"], + "supportedTriples": ["aarch64-unknown-linux-gnu"] + } + ] + } + } +} +``` + +### Auditing tool + +Without proper auditing it would be very easy to provide binary static library artifacts that call into unresolved external symbols that are not available on the runtime platform, e.g., due to missing linkage to a system dynamic library. + +We propose the introduction of a new tool that can validate the "safety" of a binary library artifact across the platforms it supports and the corresponding runtime environment. + +In this proposal we restrict ourselves to static libraries that do not have any external dependencies beyond the C standard library and runtime. +To achieve this we need to be able to detect validate this property across the three object file formats used in static libraries on our supported platforms: [Mach-O](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeFootprint/Articles/MachOOverview.html#//apple_ref/doc/uid/20001860-BAJGJEJC) on Apple platforms, [ELF](https://refspecs.linuxfoundation.org/elf/elf.pdf) on Linux-based platforms, and [COFF](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format) on Windows. +All three formats express references to external symbols as _relocations_ which reside in a single section of each object file. + +We propose adding the `llvm-objdump` to the toolchain to provide the capability to inspect relocations across all three supported object file formats. The tool would use `llvm-objdump` every object file in the static library and construct a complete list of symbols defined and referenced across the entire library. +Additionally, the tool would construct a simple C compiler invocation to derive to generate a default linker invocation. +This would be used to derive the libraries linked by default in a C program, these libraries would then be scanned to contribute to the list of defined symbols. +The tool would then check that the referenced symbols list is a subset of the set of defined symbols and emit an error otherwise. + +This would be sufficient to guarantee that all symbols from the static library would be available at runtime for statically linked executables or for ones running on the build host. +To ensure maximum runtime compatibility we would also provide a Linux-based Docker image that uses the oldest supported `glibc` for a given Swift version. +As `glibc` is backwards compatible, a container running the audit on a given static library would ensure that the version of `glibc` on any runtime platform would be compatible with the binary artifact. +This strategy as been succesfully employed in the Python community with [`manylinux`](https://peps.python.org/pep-0513/). + +## Security + +This proposal brings the security implications outlined in [SE-0272](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0272-swiftpm-binary-dependencies.md#security) to non-Apple platforms, +namely that a malicious attacker having access to both the server hosting the artifact and the git repository that vends the Package Manifest could provide a malicious library. +Users should exercise caution when onboarding binary dependencies. + +## Impact on existing packages + +No current package should be affected by this change since this is only an additive change in enabling SwiftPM to use binary target library dependencies on non-Apple platforms. + +## Future directions + +### Support Swift static libraries + +To do this we would extend the static library binary artifact manifest to provide a `.swiftinterface` file that can be consumed by the Swift compiler to import the Swift APIs. +Additionally we would extend the auditing tool to validate the usage of Swift standard library and runtime symbols, e.g., from `libSwiftCore`. + +### Extend binary compatibility guarantees + +This proposal limits itself to providing facilities for binary compatibility only with the C standard library and runtime. +In the future we could provide a system to allow binary artifact distributors to specify additional linkage dependencies for their binary artifacts. +These would be used to customize the operation of the audit tool and perform automatic linking of them in any client target that depends on the binary artifact, in the same way [CMake](https://cmake.org/cmake/help/v4.0/prop_tgt/INTERFACE_LINK_LIBRARIES.html) propagates link dependencies transitively. + +### Add support for dynamically linked dependencies + +On Windows dynamic linking requires an _import library_ which is a small static library that contains stubs for symbols exported by the dynamic library. +These stubs are roughly equivalent to a PLT entry in an ELF executable, but are generated during the build of the dynamic library and must be provided to clients of the library for linking purposes. +Similarly on Linux and Apple platforms binary artifact maintainers may wish to provide a dynamic library stub to improve link performance. +To support these use cases the library binary artifact manifest schema could be extended to provide facilities to provide both a link-time and runtime dependency. From cba174878fc9a86c7b03c1c3746556d2c7c5317b Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 2 May 2025 14:17:56 +0100 Subject: [PATCH 4280/4563] Fix SE number --- ...swiftpm-static-library-binary-target-non-apple-platforms.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{0467-swiftpm-static-library-binary-target-non-apple-platforms.md => 0482-swiftpm-static-library-binary-target-non-apple-platforms.md} (99%) diff --git a/proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md similarity index 99% rename from proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md rename to proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md index d453502706..9e246c9afd 100644 --- a/proposals/0467-swiftpm-static-library-binary-target-non-apple-platforms.md +++ b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -1,6 +1,6 @@ # Binary Static Library Dependencies -* Proposal: [SE-0467](0467-swiftpm-static-library-binary-target-non-apple-platforms.md) +* Proposal: [SE-0482](0482-swiftpm-static-library-binary-target-non-apple-platforms.md) * Authors: [Daniel Grumberg](https://github.com/daniel-grumberg), [Max Desiatov](https://github.com/MaxDesiatov), [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Kuba Mracek](https://github.com/kubamracek) * Status: **Active Review (2 May...15 May, 2005)** From 94910c9b3d643355a2c721d19263010a0c693c13 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 2 May 2025 14:26:48 +0100 Subject: [PATCH 4281/4563] [SE-0482] Update review fields. --- ...tic-library-binary-target-non-apple-platforms.md | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md index 9e246c9afd..6991b6f942 100644 --- a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md +++ b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -3,20 +3,11 @@ * Proposal: [SE-0482](0482-swiftpm-static-library-binary-target-non-apple-platforms.md) * Authors: [Daniel Grumberg](https://github.com/daniel-grumberg), [Max Desiatov](https://github.com/MaxDesiatov), [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Kuba Mracek](https://github.com/kubamracek) -* Status: **Active Review (2 May...15 May, 2005)** - - - +* Status: **Active Review (May 2nd...May 15th, 2025)** * Implementation: [swiftlang/swift-package-manager#6967](https://github.com/swiftlang/swift-package-manager/pull/6967) [swiftlang/swift-package-manager#8605](https://github.com/swiftlang/swift-package-manager/pull/8605) +* Review: ([discussion](https://forums.swift.org/t/se-0482-binary-static-library-dependencies/79634)) ([pitch](https://forums.swift.org/t/pitch-swiftpm-support-for-binary-static-library-dependencies/78619)) * Bugs: [Swift Package Manger Issue](https://github.com/swiftlang/swift-package-manager/issues/7035) - - - ## Introduction Swift continues to grow as a cross-platform language supporting a wide variety of use cases from [programming embedded device](https://www.swift.org/blog/embedded-swift-examples/) to [server-side development](https://www.swift.org/documentation/server/) across a multitude of [operating systems](https://www.swift.org/documentation/articles/static-linux-getting-started.html). From 83cd77fb448cba929fc222a66ea8afc40725d80a Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Fri, 2 May 2025 09:59:08 -0700 Subject: [PATCH 4282/4563] Update the SE-0443 proposal to reflect the style chosen for diagnostic group names. The text of SE-0443 uses snake case diagnostic group names, but in practice we've settled on using Pascal case for diagnostic group names. Additionally, the name `DeprecatedDeclaration` was chosen for the deprecation diagnostic group. I think it would be best for the proposal to reflect both of these stylistic choices so that it can serve as accurate documentation for the feature in practice. --- proposals/0443-warning-control-flags.md | 73 +++++++++++++------------ 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/proposals/0443-warning-control-flags.md b/proposals/0443-warning-control-flags.md index 99562b689c..eb24169971 100644 --- a/proposals/0443-warning-control-flags.md +++ b/proposals/0443-warning-control-flags.md @@ -32,7 +32,7 @@ The `` parameter is a string identifier of the diagnostic group. A diagnostic group is a stable identifier for an error or warning. It is an abstraction layer over the diagnostic identifiers used within the compiler. This is necessary because diagnostics within the compiler may change, but we need to provide stable user-facing identifiers for them. -A diagnostic group may include errors, warnings, or other diagnostic groups. For example, the `availability_deprecated` diagnostic group includes warnings related to the use of an API marked with the `@available(..., deprecated: ...)` attribute. The `deprecated` diagnostic group includes the `availability_deprecated` group and other groups related to deprecation. +A diagnostic group may include errors, warnings, or other diagnostic groups. For example, the `DeprecatedDeclaration` diagnostic group includes warnings related to the use of an API marked with the `@available(..., deprecated: ...)` attribute. The `Deprecated` diagnostic group includes the `DeprecatedDeclaration` group and other groups related to deprecation. Diagnostic groups may expand over time, but they can never become narrower. When a new diagnostic is added to the compiler, it is either included in an existing group or a new group is created for it, which in turn can also be included in one of the broader groups, if appropriate. @@ -40,11 +40,11 @@ The order in which these flags are specified when invoking the compiler is impor We also retain the existing compiler options but modify their handling algorithm so that they are considered in the general list with the new options and follow the "last one wins" rule as well. -Thus, for example, you can use the combination `-warnings-as-errors -Wwarning deprecated`, which will upgrade all warnings to errors except for those in the `deprecated` group. However, if these flags are specified in the reverse order(`-Wwarning deprecated -warnings-as-errors`) it will be interpreted as upgrading all warnings to errors, as the `-warnings-as-errors` flag is the last one. +Thus, for example, you can use the combination `-warnings-as-errors -Wwarning Deprecated`, which will upgrade all warnings to errors except for those in the `Deprecated` group. However, if these flags are specified in the reverse order(`-Wwarning Deprecated -warnings-as-errors`) it will be interpreted as upgrading all warnings to errors, as the `-warnings-as-errors` flag is the last one. We are also introducing a new compiler flag, `-print-diagnostic-groups`, to display the names of diagnostic groups along with the textual representation of the warnings. When used, the warning message will be followed by the name of the narrowest group that includes that warning, enclosed in square brackets. For example: ``` -main.swift:33:1: warning: 'f()' is deprecated [availability_deprecated] +main.swift:33:1: warning: 'f()' is deprecated [#DeprecatedDeclaration] ``` ## Detailed design @@ -57,22 +57,22 @@ Diagnostic groups form an acyclic graph with the following properties: - When using the `-print-diagnostic-groups` flag, it would be inconvenient if a warning corresponded to multiple groups. - Documentation lookup will also be easier for the user if a diagnostic has only one identifier. -- A diagnostic group may include any number of other diagnostic groups. This will allow organizing groups into sets with similar meanings but different specific diagnostics. For example, the warnings `availability_deprecated` and `unsafe_global_actor_deprecated` are part of the supergroup `deprecated`. +- A diagnostic group may include any number of other diagnostic groups. This will allow organizing groups into sets with similar meanings but different specific diagnostics. For example, the warnings `DeprecatedDeclaration` and `UnsafeGlobalActorDeprecated` are part of the supergroup `Deprecated`. -- A diagnostic group can be included in any number of diagnostic groups. This allows expressing the membership of a group in multiple supergroups, where appropriate. For example, the group `unsafe_global_actor_deprecated` is part of both the `deprecated` and `concurrency` groups. +- A diagnostic group can be included in any number of diagnostic groups. This allows expressing the membership of a group in multiple supergroups, where appropriate. For example, the group `UnsafeGlobalActorDeprecated` is part of both the `Deprecated` and `Concurrency` groups. The internal structure of the graph may change to some extent. However, the set of diagnostics included in a diagnostic group (directly or transitively) should not shrink. There are two typical situations where the graph structure may change: -- When adding a new diagnostic to the compiler, consider creating a new group corresponding to that diagnostic. If the new group is created it can also be included in one or more existing groups if it belongs to them. For example, it is expected that the `deprecated` group will continuously include new subgroups. +- When adding a new diagnostic to the compiler, consider creating a new group corresponding to that diagnostic. If the new group is created it can also be included in one or more existing groups if it belongs to them. For example, it is expected that the `Deprecated` group will continuously include new subgroups. - If an existing diagnostic is split into more specific versions, and we want to allow users to use the more specific version in compiler options, a separate group is created for it, which **must** be included in the group of the original diagnostic. - For example, suppose we split the `availability_deprecated` warning into a general version and a specialized version `availability_deprecated_same_module`, which the compiler emits if the deprecated symbol is declared in the same module. In this case, the `availability_deprecated_same_module` group must be added to the `availability_deprecated` group to ensure that the overall composition of the `availability_deprecated` group does not change. The final structure should look like this: + For example, suppose we split the `DeprecatedDeclaration` warning into a general version and a specialized version `DeprecatedDeclarationSameModule`, which the compiler emits if the deprecated symbol is declared in the same module. In this case, the `DeprecatedDeclarationSameModule` group must be added to the `DeprecatedDeclaration` group to ensure that the overall composition of the `DeprecatedDeclaration` group does not change. The final structure should look like this: ``` - availability_deprecated (group) - ├─ availability_deprecated (internal diag id) - └─ availability_deprecated_same_module (group) - └─ availability_deprecated_same_module (internal diag id) + DeprecatedDeclaration (group) + ├─ DeprecatedDeclaration (internal diag id) + └─ DeprecatedDeclarationSameModule (group) + └─ DeprecatedDeclarationSameModule (internal diag id) ``` - Thus, invoking the compiler with the `-Werror availability_deprecated` parameter will cover both versions of the warning, and the behavior will remain unchanged. At the same time, the user can control the behavior of the narrower `availability_deprecated_same_module` group if they want to. + Thus, invoking the compiler with the `-Werror DeprecatedDeclaration` parameter will cover both versions of the warning, and the behavior will remain unchanged. At the same time, the user can control the behavior of the narrower `DeprecatedDeclarationSameModule` group if they want to. ### Compiler options evaluation @@ -87,20 +87,21 @@ Compiler options for controlling the behavior of groups are now processed as a s When these options are passed to the compiler, we sequentially apply the specified behavior to all warnings within the specified group from left to right. For `-warnings-as-errors` and `-no-warnings-as-errors`, we apply the behavior to all warnings. Examples of option combinations: -- `-warnings-as-errors -Wwarning deprecated` +- `-warnings-as-errors -Wwarning Deprecated` - Warnings from the `deprecated` group will be kept as warnings, but all the rest will be upgraded to errors. + Warnings from the `Deprecated` group will be kept as warnings, but all the rest will be upgraded to errors. -- `-Werror deprecated -Wwarning availability_deprecated` +- `-Werror Deprecated -Wwarning DeprecatedDeclaration` - Warnings from the `availability_deprecated` group will remain as warnings. Other warnings from the `deprecated` group will be upgraded to errors. All others will be kept as warnings. + Warnings from the `DeprecatedDeclaration` group will remain as warnings. Other warnings from the `Deprecated` group will be upgraded to errors. All others will be kept as warnings. It’s crucial to understand that the order in which these flags are applied can significantly affect the behavior of diagnostics. The rule is "the last one wins", meaning that if multiple flags apply to the same diagnostic group, the last one specified on the command line will determine the final behavior. It is also important to note that the order matters even if the specified groups are not explicitly related but have a common subgroup. -For example, as mentioned above, the `unsafe_global_actor_deprecated` group is part of both the `deprecated` and `concurrency` groups. So the order in which options for the `deprecated` and `concurrency` groups are applied will change the final behavior of the `unsafe_global_actor_deprecated` group. Specifically: -- `-Wwarning deprecated -Werror concurrency` will make it an error, -- `-Werror concurrency -Wwarning deprecated` will keep it as a warning. +For example, as mentioned above, the `UnsafeGlobalActorDeprecated` group is part of both the `Deprecated` and `Concurrency` groups. So the order in which options for the `Deprecated` and `Concurrency` groups are applied will change the final behavior of the `UnsafeGlobalActorDeprecated` group. Specifically: + +- `-Wwarning Deprecated -Werror Concurrency` will make it an error, +- `-Werror Concurrency -Wwarning Deprecated` will keep it as a warning. #### Interaction with `-suppress-warnings` @@ -123,15 +124,15 @@ oldFunction() ``` When compiled with the `-debug-diagnostic-names` option, the following message will be displayed: ``` -'oldFunction()' is deprecated: renamed to 'newFunction' [availability_deprecated_rename] +'oldFunction()' is deprecated: renamed to 'newFunction' [#RenamedDeprecatedDeclaration] ``` -The string `availability_deprecated_rename` is the internal identifier of this warning, not the group. Accordingly, it is not supported by the new compiler options. +The string `RenamedDeprecatedDeclaration` is the internal identifier of this warning, not the group. Accordingly, it is not supported by the new compiler options. When compiling the same code with the `-print-diagnostic-groups` option, the following message will be displayed: ``` -'oldFunction()' is deprecated: renamed to 'newFunction' [availability_deprecated] +'oldFunction()' is deprecated: renamed to 'newFunction' [#DeprecatedDeclaration] ``` -Here, the string `availability_deprecated` is the diagnostic group. +Here, the string `DeprecatedDeclaration` is the diagnostic group. Often, group names and internal diagnostic identifiers coincide, but this is not always the case. @@ -169,7 +170,7 @@ The lack of control over the behavior of specific diagnostics forces users to ab Warnings and errors in Swift can change as the compiler evolves. For example, one error might be renamed or split into two that are applied in different situations to improve the clarity of the text message depending on the context. Such a change would result in a new ID for the new error variant. -The example of `availability_deprecated_same_module` illustrates this well. If we used the warning ID, the behavior of the compiler with the `-Wwarning availability_deprecated` option would change when a new version of the warning is introduced, as this warning would no longer be triggered for the specific case of the same module. +The example of `DeprecatedDeclarationSameModule` illustrates this well. If we used the warning ID, the behavior of the compiler with the `-Wwarning DeprecatedDeclaration` option would change when a new version of the warning is introduced, as this warning would no longer be triggered for the specific case of the same module. Therefore, we need a solution that allows us to modify errors and warnings within the compiler while providing a reliable mechanism for identifying diagnostics that can be used by the user. @@ -177,23 +178,23 @@ Therefore, we need a solution that allows us to modify errors and warnings withi To solve this problem, we could use an additional alias-ID for diagnostics that does not change when the main identifier changes. -Suppose we split the `availability_deprecated` diagnostic into a generic variant and `availability_deprecated_same_module`. To retain the existing name for the new variant, we could describe these two groups as +Suppose we split the `DeprecatedDeclaration` diagnostic into a generic variant and `DeprecatedDeclarationSameModule`. To retain the existing name for the new variant, we could describe these two groups as ``` -availability_deprecated (alias: availability_deprecated) -availability_deprecated_same_module (alias: availability_deprecated) +DeprecatedDeclaration (alias: DeprecatedDeclaration) +DeprecatedDeclarationSameModule (alias: DeprecatedDeclaration) ``` -However, this solution would not allow specifying the narrower `availability_deprecated_same_module` or the broader group `deprecated`. +However, this solution would not allow specifying the narrower `DeprecatedDeclarationSameModule` or the broader group `Deprecated`. #### Using multiple alias IDs for diagnostics To express a diagnostic's membership in multiple groups, we could allow multiple alias-IDs to be listed. ``` -availability_deprecated aliases: - availability_deprecated - deprecated -availability_deprecated_same_module aliases: - availability_deprecated_same_module - availability_deprecated - deprecated +DeprecatedDeclaration aliases: + DeprecatedDeclaration + Deprecated +DeprecatedDeclarationSameModule aliases: + DeprecatedDeclarationSameModule + DeprecatedDeclaration + Deprecated ``` However, such a declaration lacks structure and makes it difficult to understand which alias-ID is the most specific. @@ -222,7 +223,7 @@ Since `-debug-diagnostic-names` has been available in the compiler for a long ti To avoid overlap, we would need to use a different format, for example: ``` -'foo()' is deprecated [availability_deprecated] [group:availability_deprecated] +'foo()' is deprecated [#DeprecatedDeclaration] [group:#DeprecatedDeclaration] ``` However, even this does not eliminate the possibility of breaking code that parses the compiler's output. From 02a4c0201ded1f9e547ccf4bf0e4c42469a22714 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Fri, 2 May 2025 12:08:22 -0700 Subject: [PATCH 4283/4563] InlineArray sugar proposal (#2776) * First draft of InlineArray sugar proposal * Add flattened array future direction * Add alternative of of * flesh out alternatives considered * Clarify x is a keyword * typo * Assign `InlineArray` type sugar to SE-0483 and put into review. * Fix a missing link to SE-0453. --------- Co-authored-by: Holly Borla --- proposals/0483-inline-array-sugar.md | 161 +++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 proposals/0483-inline-array-sugar.md diff --git a/proposals/0483-inline-array-sugar.md b/proposals/0483-inline-array-sugar.md new file mode 100644 index 0000000000..c4ee2430cd --- /dev/null +++ b/proposals/0483-inline-array-sugar.md @@ -0,0 +1,161 @@ +# `InlineArray` Literal Syntax + +* Proposal: [SE-0483](0483-inline-array-sugar.md) +* Authors: [Hamish Knight](https://github.com/hamishknight), [Ben Cohen](https://github.com/airspeedswift) +* Review Manager: [Holly Borla](https://github.com/hborla) +* Status: **Active Review (May 2 - May 16, 2025)** +* Implementation: On `main` under the `InlineArrayTypeSugar` experimental feature flag. +* Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) + +## Introduction + +We propose the introduction of type sugar for the `InlineArray` type, providing more succinct syntax for declaring an inline array. + +## Motivation + +[SE-0453](/proposals/0453-vector.md) introduced a new type, `InlineArray`, which includes a size parameter as part of its type: + +``` +let fiveIntegers: InlineArray<5, Int> = .init(repeating: 99) +``` + +Declaring this type is more cumbersome than its equivalent dyanmicaly-sized array, which has sugar for the type syntax: + +``` +let fiveIntegers: [Int] = .init(repeating: 99, count: 5) +``` + +This becomes more pronounced when dealing with multiple dimensions: + +``` +let fiveByFive: InlineArray<5, InlineArray<5, Int>> = .init(repeating: .init(repeating: 99)) +``` + +## Proposed solution + +A new sugared version of the `InlineArray` type is proposed: + +```swift +let fiveIntegers: [5 x Int] = .init(repeating: 99) +``` + +The `x` here is the ASCII character, and is chosen to evoke the common shorthand use to represent "by", as in "4x4" or "2 in x 4 in". + +Note that although it is used in the manner of an operator, `x` here serves more like a contextual keyword, similar to if the syntax were `[5 of Int]`. + +## Detailed design + +The new syntax consists of the value for the integer generic paramter and the type of the element generic paramter, separated by `x`. + +This will be added to the grammar alongside the current type sugar: + +> **Grammar of a type** +> _type → sized-array-type_ +> +> **Grammar of a sized array type** +> _sized-array-type → [ expression `x` type ]_ + +Note that while the grammar allows for any expression, this is currently limited to only integer literals. + +The new sugar is equivalent to declaring a type of `InlineArray`, so all rules that can be applied to the generic placeholders for the unsugared version also apply to the sugared version: + +``` +// Nesting +let fiveByFive: InlineArray<5, InlineArray<5, Int>> = .init(repeating: .init(repeating: 99)) +let fiveByFive: [5 x [5 x Int]] = .init(repeating: .init(repeating: 99)) + +// Inference from context: +let fiveIntegers: [5 x _] = .init(repeating: 99) +let fourBytes: [_ x Int8] = [1,2,3,4] +let fourIntegers: [_ x _] = [1,2,3,4] + +// use on rhs +let fiveDoubles = [5 x _](repeating: 1.23) +``` + +The sugar can also be used in place of the unsugared type wherever it might appear: + +``` +[5 x Int](repeating: 99) +MemoryLayout<[5 x Int]>.size +unsafeBitCast((1,2,3), to: [3 x Int].self) +``` + +There must be whitespace on either side of the separator i.e. you cannot write `[5x Int]`. There are no requirements to balance whitespace, `[5 x Int]` is permitted. A new line can appear after the `x` but not before it, as while this is not ambiguous, this aids with the parser recovery logic, leading to better syntax error diagnostics. + +## Source Compatibility + +Since it is not currently possible to write any form of the proposed syntax in Swift today, this proposal does not alter the meaning of any existing code. + +## Impact on ABI + +This is purely compile-time sugar for the existing type. It is resolved at compile time, and does not appear in the ABI nor rely on any version of the runtime. + +## Future Directions + +### Repeated value equivalent + +Analogous to arrays, there is an equivalent _value_ sugar for literals of a specific size: + +``` +// type inferred to be [5 x Int] +let fiveInts = [5 x 99] +// type inferred to be [5 x [5 x Int]] +let fiveByFive = [5 x [5 x 99]] +``` + +Unlike the sugar for the type, this would also have applicability for existing types: + +``` +// equivalent to .init(repeating: 99, count: 5) +let dynamic: [Int] = [5 x 99] +``` + +This is a much bigger design space, potentially requiring a new expressible-by-literal protocol and a way to map the literal to an initializer. As such, it is left for a future proposal. + +### Flattened multi-dimensional arrays + +For multi-dimensional arrays, `[5 x [5 x Int]]` could be flattened to `[5 x 5 x Int]` without any additional parsing issues. This could be an alternative considered, but is in future directions as it could also be introduced as sugar for the former case at a later date. + +## Alternatives Considered + +### Choice of delimiter + +The most obvious alternative here is the choice of separator. Other options include: + +- `[5 * Int]`, using the standard ASCII symbol for multiplication. +- `[5 ⨉ Int]`, the Unicode n-ary times operator. This looks nice but is impactical as not keyboard-accessible. +- `[5; Int]` is what Rust uses, but appears to have little association with "times" or "many". Similarly other arbitrary punctuation e.g. `,` or `/` or `#`. +- `[5 of Int]` is more verbose than `x` but could be considered more clear. It has the upside or downside, depending on your preference, of being almost, but not quite, grammatical. +- `:` is of course ruled out as it is used for dictionary literals. + +Note that `*` is an existing operator, and may lead to ambiguity in fuure when expressions can be used to determine the size: `[5 * N * Int]`. `x` is clearer in this case: `[5 * N x Int]`. It also avoids parsing ambiguity, as the grammar does not allow two identifiers in succession. But it would be less clear if `x` also appeared as an identifier: `[5 * x x Int]` (which is not yet permitted but may be in future use cases). + +This becomes more important if the future direction of a value equivalent is pursued. `[2 * 2 * 2]` could be interpreted as `[2, 2, 2, 2]`, `[4, 4,]`, or `[8]`. + +Since `x` cannot follow another identifier today, `[x x Int]` is unambiguous,[^type] but would clearly be hard to read. This is likely a hypothetical concern rather than a practical one. While `x` is used often in scratch code for a local variable, a more meaningful name is usually preferable, and this would be especially the case if it is found being used for the size of an array literal. In addition, while `i`, `j`, or `n` are often legitimate counters that might be suited to the size of an array, `x` is generally not used for such things. + +[^type]: or even `[x x x]`, since `x` can be a type name, albeit one that defies Swift's naming conventions. + +Another thing to consider is how that separator looks in the fully inferred version, which tend to start to look a little like ascii diagrams: + +``` +[_ x _] +[_ * _] +[_; _] +[_ of _] +``` + +### Order of size and type + +The order of size first, then type is determined by the ordering of the unsugared type, and deviating from this for the sugared version is not an option. + +### Whitespace around the delimeter + +In theory, when using integer literals or `_` the whitespace could be omitted (`[5x_]` is unabiguously `[5 x _]`). However, special casing allowing whitespace omission is not desirable. + +### Choice of brackets + +`InlineArray` has a lot in common with tuples – especially in sharing "copy on copy" behavior, unlike regular `Array`. So `(5 x Int)` may be an appropriate alternative to the square brackets, echoing this similarity. + +Beyond varying the separator, there may be other dramatically different syntax that moves further from the "like Array sugar, but with a size argument". From ffbbbec7678b23e427f9e53b42562de365d4dbd2 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 2 May 2025 12:12:34 -0700 Subject: [PATCH 4284/4563] Link SE-0483 review thread. (#2830) --- proposals/0483-inline-array-sugar.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0483-inline-array-sugar.md b/proposals/0483-inline-array-sugar.md index c4ee2430cd..bd203ee27e 100644 --- a/proposals/0483-inline-array-sugar.md +++ b/proposals/0483-inline-array-sugar.md @@ -5,7 +5,7 @@ * Review Manager: [Holly Borla](https://github.com/hborla) * Status: **Active Review (May 2 - May 16, 2025)** * Implementation: On `main` under the `InlineArrayTypeSugar` experimental feature flag. -* Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) +* Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) ([review](https://forums.swift.org/t/se-0483-inlinearray-literal-syntax/79643)) ## Introduction From 1915847451e06eb8a0f53538527b55973575c64d Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 2 May 2025 14:37:17 -0700 Subject: [PATCH 4285/4563] Accept SE-0476. (#2831) --- proposals/0476-abi-attr.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0476-abi-attr.md b/proposals/0476-abi-attr.md index e7bea6bf57..6bd6c3efa6 100644 --- a/proposals/0476-abi-attr.md +++ b/proposals/0476-abi-attr.md @@ -3,9 +3,9 @@ * Proposal: [SE-0476](0476-abi-attr.md) * Authors: [Becca Royal-Gordon](https://github.com/beccadax) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (April 11 - April 25, 2025)** +* Status: **Accepted** * Implementation: behind experimental feature `ABIAttribute` (refinements in [swiftlang/swift#80383](https://github.com/swiftlang/swift/pull/80383)) -* Review: ([pitch](https://forums.swift.org/t/pitch-controlling-the-abi-of-a-declaration/75123)) ([review](https://forums.swift.org/t/se-0476-controlling-the-abi-of-a-function-initializer-property-or-subscript/79233)) +* Review: ([pitch](https://forums.swift.org/t/pitch-controlling-the-abi-of-a-declaration/75123)) ([review](https://forums.swift.org/t/se-0476-controlling-the-abi-of-a-function-initializer-property-or-subscript/79233)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0476-controlling-the-abi-of-a-function-initializer-property-or-subscript/79644)) ## Introduction From 1267bbda5130cd94bd859c964b9d3437db433b08 Mon Sep 17 00:00:00 2001 From: Naoki Hiroshima Date: Fri, 2 May 2025 17:08:25 -0700 Subject: [PATCH 4286/4563] Update 0483-inline-array-sugar.md (#2832) Typos --- proposals/0483-inline-array-sugar.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0483-inline-array-sugar.md b/proposals/0483-inline-array-sugar.md index bd203ee27e..146174dc75 100644 --- a/proposals/0483-inline-array-sugar.md +++ b/proposals/0483-inline-array-sugar.md @@ -19,7 +19,7 @@ We propose the introduction of type sugar for the `InlineArray` type, providing let fiveIntegers: InlineArray<5, Int> = .init(repeating: 99) ``` -Declaring this type is more cumbersome than its equivalent dyanmicaly-sized array, which has sugar for the type syntax: +Declaring this type is more cumbersome than its equivalent dynamically-sized array, which has sugar for the type syntax: ``` let fiveIntegers: [Int] = .init(repeating: 99, count: 5) @@ -45,7 +45,7 @@ Note that although it is used in the manner of an operator, `x` here serves more ## Detailed design -The new syntax consists of the value for the integer generic paramter and the type of the element generic paramter, separated by `x`. +The new syntax consists of the value for the integer generic parameter and the type of the element generic parameter, separated by `x`. This will be added to the grammar alongside the current type sugar: @@ -124,12 +124,12 @@ For multi-dimensional arrays, `[5 x [5 x Int]]` could be flattened to `[5 x 5 x The most obvious alternative here is the choice of separator. Other options include: - `[5 * Int]`, using the standard ASCII symbol for multiplication. -- `[5 ⨉ Int]`, the Unicode n-ary times operator. This looks nice but is impactical as not keyboard-accessible. +- `[5 ⨉ Int]`, the Unicode n-ary times operator. This looks nice but is impractical as not keyboard-accessible. - `[5; Int]` is what Rust uses, but appears to have little association with "times" or "many". Similarly other arbitrary punctuation e.g. `,` or `/` or `#`. - `[5 of Int]` is more verbose than `x` but could be considered more clear. It has the upside or downside, depending on your preference, of being almost, but not quite, grammatical. - `:` is of course ruled out as it is used for dictionary literals. -Note that `*` is an existing operator, and may lead to ambiguity in fuure when expressions can be used to determine the size: `[5 * N * Int]`. `x` is clearer in this case: `[5 * N x Int]`. It also avoids parsing ambiguity, as the grammar does not allow two identifiers in succession. But it would be less clear if `x` also appeared as an identifier: `[5 * x x Int]` (which is not yet permitted but may be in future use cases). +Note that `*` is an existing operator, and may lead to ambiguity in future when expressions can be used to determine the size: `[5 * N * Int]`. `x` is clearer in this case: `[5 * N x Int]`. It also avoids parsing ambiguity, as the grammar does not allow two identifiers in succession. But it would be less clear if `x` also appeared as an identifier: `[5 * x x Int]` (which is not yet permitted but may be in future use cases). This becomes more important if the future direction of a value equivalent is pursued. `[2 * 2 * 2]` could be interpreted as `[2, 2, 2, 2]`, `[4, 4,]`, or `[8]`. @@ -152,7 +152,7 @@ The order of size first, then type is determined by the ordering of the unsugare ### Whitespace around the delimeter -In theory, when using integer literals or `_` the whitespace could be omitted (`[5x_]` is unabiguously `[5 x _]`). However, special casing allowing whitespace omission is not desirable. +In theory, when using integer literals or `_` the whitespace could be omitted (`[5x_]` is unambiguously `[5 x _]`). However, special casing allowing whitespace omission is not desirable. ### Choice of brackets From 63b8dfcdbdc1a8508ea185c6e32e9b4c6863e0d0 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Sat, 3 May 2025 06:14:29 -0700 Subject: [PATCH 4287/4563] Fix dates for active reviews show correctly in dashboard. --- proposals/0474-yielding-accessors.md | 2 +- proposals/0475-observed.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0474-yielding-accessors.md b/proposals/0474-yielding-accessors.md index 2e9f24d22d..fc8702d683 100644 --- a/proposals/0474-yielding-accessors.md +++ b/proposals/0474-yielding-accessors.md @@ -3,7 +3,7 @@ * Proposal: [SE-0474](0474-yielding-accessors.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Nate Chandler](https://github.com/nate-chandler), [Joe Groff](https://github.com/jckarter/) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active Review (Apr 8...22)** +* Status: **Active Review (April 8...22, 2025)** * Vision: [A Prospective Vision for Accessors in Swift](https://github.com/rjmccall/swift-evolution/blob/accessors-vision/visions/accessors.md) * Implementation: Partially available on main behind the frontend flag `-enable-experimental-feature CoroutineAccessors` * Review: ([pitch 1](https://forums.swift.org/t/modify-accessors/31872)), ([pitch 2](https://forums.swift.org/t/pitch-modify-and-read-accessors/75627)), ([pitch 3](https://forums.swift.org/t/pitch-3-yielding-coroutine-accessors/77956)), ([review](https://forums.swift.org/t/se-0474-yielding-accessors/79170)) diff --git a/proposals/0475-observed.md b/proposals/0475-observed.md index 2df388465f..4f944e3f18 100644 --- a/proposals/0475-observed.md +++ b/proposals/0475-observed.md @@ -3,7 +3,7 @@ * Proposal: [SE-0475](0475-observed.md) * Authors: [Philippe Hausler](https://github.com/phausler) * Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) -* Status: **Active Review (Apr 10...24)** +* Status: **Active Review (April 10...24, 2025)** * Implementation: https://github.com/swiftlang/swift/pull/79817 * Review: ([pitch](https://forums.swift.org/t/pitch-transactional-observation-of-values/78315)) ([review](https://forums.swift.org/t/se-0475-transactional-observation-of-values/79224)) From eeef3f49cfd264ba016f599358dccccdd702e17a Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Sat, 3 May 2025 06:18:37 -0700 Subject: [PATCH 4288/4563] Add explicit end months for proposals. --- proposals/0474-yielding-accessors.md | 2 +- proposals/0475-observed.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0474-yielding-accessors.md b/proposals/0474-yielding-accessors.md index fc8702d683..1c9754dbde 100644 --- a/proposals/0474-yielding-accessors.md +++ b/proposals/0474-yielding-accessors.md @@ -3,7 +3,7 @@ * Proposal: [SE-0474](0474-yielding-accessors.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Nate Chandler](https://github.com/nate-chandler), [Joe Groff](https://github.com/jckarter/) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active Review (April 8...22, 2025)** +* Status: **Active Review (April 8 ... April 22, 2025)** * Vision: [A Prospective Vision for Accessors in Swift](https://github.com/rjmccall/swift-evolution/blob/accessors-vision/visions/accessors.md) * Implementation: Partially available on main behind the frontend flag `-enable-experimental-feature CoroutineAccessors` * Review: ([pitch 1](https://forums.swift.org/t/modify-accessors/31872)), ([pitch 2](https://forums.swift.org/t/pitch-modify-and-read-accessors/75627)), ([pitch 3](https://forums.swift.org/t/pitch-3-yielding-coroutine-accessors/77956)), ([review](https://forums.swift.org/t/se-0474-yielding-accessors/79170)) diff --git a/proposals/0475-observed.md b/proposals/0475-observed.md index 4f944e3f18..ccb1901934 100644 --- a/proposals/0475-observed.md +++ b/proposals/0475-observed.md @@ -3,7 +3,7 @@ * Proposal: [SE-0475](0475-observed.md) * Authors: [Philippe Hausler](https://github.com/phausler) * Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) -* Status: **Active Review (April 10...24, 2025)** +* Status: **Active Review (April 10 ... April 24, 2025)** * Implementation: https://github.com/swiftlang/swift/pull/79817 * Review: ([pitch](https://forums.swift.org/t/pitch-transactional-observation-of-values/78315)) ([review](https://forums.swift.org/t/se-0475-transactional-observation-of-values/79224)) From b9fd2786e75b126081067e2e8607249ccad67207 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Mon, 5 May 2025 09:33:05 -0400 Subject: [PATCH 4289/4563] Add link to second review of SE-0472. --- proposals/0472-task-start-synchronously-on-caller-context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0472-task-start-synchronously-on-caller-context.md b/proposals/0472-task-start-synchronously-on-caller-context.md index 75bfaec705..d5a3acd522 100644 --- a/proposals/0472-task-start-synchronously-on-caller-context.md +++ b/proposals/0472-task-start-synchronously-on-caller-context.md @@ -5,7 +5,7 @@ * Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Returned for revision** * Implementation: https://github.com/swiftlang/swift/pull/79608 -* Review: ([pitch](https://forums.swift.org/t/pitch-concurrency-starting-tasks-synchronously-from-caller-context/77960/)) ([review](https://forums.swift.org/t/se-0472-starting-tasks-synchronously-from-caller-context/78883)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0472-starting-tasks-synchronously-from-caller-context/79311)) +* Review: ([pitch](https://forums.swift.org/t/pitch-concurrency-starting-tasks-synchronously-from-caller-context/77960/)) ([first review](https://forums.swift.org/t/se-0472-starting-tasks-synchronously-from-caller-context/78883)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0472-starting-tasks-synchronously-from-caller-context/79311)) ([second review](https://forums.swift.org/t/second-review-se-0472-starting-tasks-synchronously-from-caller-context/79683)) ## Introduction From d095422ecd7dce88da35897aaf5c719e2a42f88a Mon Sep 17 00:00:00 2001 From: Joe Groff Date: Tue, 6 May 2025 10:55:58 -0700 Subject: [PATCH 4290/4563] SE-0474: Fix typo --- proposals/0474-yielding-accessors.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0474-yielding-accessors.md b/proposals/0474-yielding-accessors.md index 1c9754dbde..ddc572f4a8 100644 --- a/proposals/0474-yielding-accessors.md +++ b/proposals/0474-yielding-accessors.md @@ -559,7 +559,7 @@ Since it would typically be ambiguous whether the `yielding borrow` or `get` sho A `yielding` accessor lends the value it yields to its caller. The caller only has access to that value until it resumes the coroutine. After the coroutine is resumed, it has the opportunity to clean up. -This enables a `yielding borrow` or `mutate` to do interesting work such as constructing aggregates from its base object's fields: +This enables a `yielding borrow` or `mutate` to do interesting work, such as constructing a temporary aggregate from its base object's fields: ```swift struct Pair : ~Copyable { @@ -567,16 +567,16 @@ struct Pair : ~Copyable { var right: Right var reversed: Pair { - yielding borrow { + yielding mutate { let result = Pair(left: right, right: left) - yield result + yield &result self = .init(left: result.right, right: result.left) } } } ``` -That the borrow ends when the coroutine is resumed means that the lifetime of the lent value is strictly shorter than that of the base value. +That the access ends when the coroutine is resumed means that the lifetime of the lent value is strictly shorter than that of the base value. In the example above, the lifetime of `reversed` is shorter than that of the `Pair` it is called on. As discussed in the [accessors vision](https://github.com/rjmccall/swift-evolution/blob/accessors-vision/visions/accessors.md), when a value is merely being projected from the base object and does not need any cleanup after being accessed, this is undesirably limiting: From 5d03375330e975cec9aa105eae257179b4f91a98 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Wed, 7 May 2025 00:42:56 -0500 Subject: [PATCH 4291/4563] Consistently format the 'Review:' header field of all Swift Testing proposals (#2837) --- proposals/testing/0001-refactor-bug-inits.md | 2 +- proposals/testing/0002-json-abi.md | 2 +- proposals/testing/0003-make-serialized-trait-api.md | 4 +--- ...-constrain-the-granularity-of-test-time-limit-durations.md | 4 +--- proposals/testing/0005-ranged-confirmations.md | 3 +-- proposals/testing/0006-return-errors-from-expect-throws.md | 2 +- proposals/testing/0007-test-scoping-traits.md | 2 +- proposals/testing/0008-exit-tests.md | 2 +- proposals/testing/0009-attachments.md | 2 +- proposals/testing/0010-evaluate-condition.md | 2 +- 10 files changed, 10 insertions(+), 15 deletions(-) diff --git a/proposals/testing/0001-refactor-bug-inits.md b/proposals/testing/0001-refactor-bug-inits.md index 385047be48..4d9a77b5e7 100644 --- a/proposals/testing/0001-refactor-bug-inits.md +++ b/proposals/testing/0001-refactor-bug-inits.md @@ -4,7 +4,7 @@ * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Status: **Implemented (Swift 6.0)** * Implementation: [swiftlang/swift-testing#401](https://github.com/swiftlang/swift-testing/pull/401) -* Review: ([pitch](https://forums.swift.org/t/pitch-dedicated-bug-functions-for-urls-and-ids/71842)), ([acceptance](https://forums.swift.org/t/swt-0001-dedicated-bug-functions-for-urls-and-ids/71842/2)) +* Review: ([pitch](https://forums.swift.org/t/pitch-dedicated-bug-functions-for-urls-and-ids/71842)) ([acceptance](https://forums.swift.org/t/swt-0001-dedicated-bug-functions-for-urls-and-ids/71842/2)) > [!NOTE] > This proposal was accepted before Swift Testing began using the Swift diff --git a/proposals/testing/0002-json-abi.md b/proposals/testing/0002-json-abi.md index e13bb1ad37..516b986838 100644 --- a/proposals/testing/0002-json-abi.md +++ b/proposals/testing/0002-json-abi.md @@ -5,7 +5,7 @@ * Status: **Implemented (Swift 6.0)** * Implementation: [swiftlang/swift-testing#383](https://github.com/swiftlang/swift-testing/pull/383), [swiftlang/swift-testing#402](https://github.com/swiftlang/swift-testing/pull/402) -* Review: ([pitch](https://forums.swift.org/t/pitch-a-stable-json-based-abi-for-tools-integration/72627)), ([acceptance](https://forums.swift.org/t/pitch-a-stable-json-based-abi-for-tools-integration/72627/4)) +* Review: ([pitch](https://forums.swift.org/t/pitch-a-stable-json-based-abi-for-tools-integration/72627)) ([acceptance](https://forums.swift.org/t/pitch-a-stable-json-based-abi-for-tools-integration/72627/4)) > [!NOTE] > This proposal was accepted before Swift Testing began using the Swift diff --git a/proposals/testing/0003-make-serialized-trait-api.md b/proposals/testing/0003-make-serialized-trait-api.md index 73a6fbe1a2..488035bedf 100644 --- a/proposals/testing/0003-make-serialized-trait-api.md +++ b/proposals/testing/0003-make-serialized-trait-api.md @@ -5,9 +5,7 @@ * Status: **Implemented (Swift 6.0)** * Implementation: [swiftlang/swift-testing#535](https://github.com/swiftlang/swift-testing/pull/535) -* Review: -([pitch](https://forums.swift.org/t/pitch-make-serialized-trait-public-api/73147)), -([acceptance](https://forums.swift.org/t/pitch-make-serialized-trait-public-api/73147/5)) +* Review: ([pitch](https://forums.swift.org/t/pitch-make-serialized-trait-public-api/73147)) ([acceptance](https://forums.swift.org/t/pitch-make-serialized-trait-public-api/73147/5)) > [!NOTE] > This proposal was accepted before Swift Testing began using the Swift diff --git a/proposals/testing/0004-constrain-the-granularity-of-test-time-limit-durations.md b/proposals/testing/0004-constrain-the-granularity-of-test-time-limit-durations.md index 4817bc955c..ea00728fca 100644 --- a/proposals/testing/0004-constrain-the-granularity-of-test-time-limit-durations.md +++ b/proposals/testing/0004-constrain-the-granularity-of-test-time-limit-durations.md @@ -5,9 +5,7 @@ * Status: **Implemented (Swift 6.0)** * Implementation: [swiftlang/swift-testing#534](https://github.com/swiftlang/swift-testing/pull/534) -* Review: -([pitch](https://forums.swift.org/t/pitch-constrain-the-granularity-of-test-time-limit-durations/73146)), -([acceptance](https://forums.swift.org/t/pitch-constrain-the-granularity-of-test-time-limit-durations/73146/3)) +* Review: ([pitch](https://forums.swift.org/t/pitch-constrain-the-granularity-of-test-time-limit-durations/73146)) ([acceptance](https://forums.swift.org/t/pitch-constrain-the-granularity-of-test-time-limit-durations/73146/3)) > [!NOTE] > This proposal was accepted before Swift Testing began using the Swift diff --git a/proposals/testing/0005-ranged-confirmations.md b/proposals/testing/0005-ranged-confirmations.md index 93997c86e9..7a53229ae2 100644 --- a/proposals/testing/0005-ranged-confirmations.md +++ b/proposals/testing/0005-ranged-confirmations.md @@ -5,8 +5,7 @@ * Status: **Implemented (Swift 6.1)** * Bug: rdar://138499457 * Implementation: [swiftlang/swift-testing#598](https://github.com/swiftlang/swift-testing/pull/598), [swiftlang/swift-testing#689](https://github.com/swiftlang/swift-testing/pull689) -* Review: ([pitch](https://forums.swift.org/t/pitch-range-based-confirmations/74589)), - ([acceptance](https://forums.swift.org/t/pitch-range-based-confirmations/74589/7)) +* Review: ([pitch](https://forums.swift.org/t/pitch-range-based-confirmations/74589)) ([acceptance](https://forums.swift.org/t/pitch-range-based-confirmations/74589/7)) > [!NOTE] > This proposal was accepted before Swift Testing began using the Swift diff --git a/proposals/testing/0006-return-errors-from-expect-throws.md b/proposals/testing/0006-return-errors-from-expect-throws.md index c1ba77d5df..eee9037cd3 100644 --- a/proposals/testing/0006-return-errors-from-expect-throws.md +++ b/proposals/testing/0006-return-errors-from-expect-throws.md @@ -5,7 +5,7 @@ * Status: **Implemented (Swift 6.1)** * Bug: rdar://138235250 * Implementation: [swiftlang/swift-testing#780](https://github.com/swiftlang/swift-testing/pull/780) -* Review: ([pitch](https://forums.swift.org/t/pitch-returning-errors-from-expect-throws/75567)), ([acceptance](https://forums.swift.org/t/pitch-returning-errors-from-expect-throws/75567/5)) +* Review: ([pitch](https://forums.swift.org/t/pitch-returning-errors-from-expect-throws/75567)) ([acceptance](https://forums.swift.org/t/pitch-returning-errors-from-expect-throws/75567/5)) > [!NOTE] > This proposal was accepted before Swift Testing began using the Swift diff --git a/proposals/testing/0007-test-scoping-traits.md b/proposals/testing/0007-test-scoping-traits.md index d397a792e9..619d1f48c6 100644 --- a/proposals/testing/0007-test-scoping-traits.md +++ b/proposals/testing/0007-test-scoping-traits.md @@ -4,7 +4,7 @@ * Authors: [Stuart Montgomery](https://github.com/stmontgomery) * Status: **Implemented (Swift 6.1)** * Implementation: [swiftlang/swift-testing#733](https://github.com/swiftlang/swift-testing/pull/733), [swiftlang/swift-testing#86](https://github.com/swiftlang/swift-testing/pull/86) -* Review: ([pitch](https://forums.swift.org/t/pitch-custom-test-execution-traits/75055)), ([review](https://forums.swift.org/t/proposal-test-scoping-traits/76676)), ([acceptance](https://forums.swift.org/t/proposal-test-scoping-traits/76676/3)) +* Review: ([pitch](https://forums.swift.org/t/pitch-custom-test-execution-traits/75055)) ([review](https://forums.swift.org/t/proposal-test-scoping-traits/76676)) ([acceptance](https://forums.swift.org/t/proposal-test-scoping-traits/76676/3)) > [!NOTE] > This proposal was accepted before Swift Testing began using the Swift diff --git a/proposals/testing/0008-exit-tests.md b/proposals/testing/0008-exit-tests.md index ce4f95def1..a543c03981 100644 --- a/proposals/testing/0008-exit-tests.md +++ b/proposals/testing/0008-exit-tests.md @@ -7,7 +7,7 @@ * Bug: [apple/swift-testing#157](https://github.com/apple/swift-testing/issues/157) * Implementation: [apple/swift-testing#324](https://github.com/swiftlang/swift-testing/pull/324) * Previous Revision: [1](https://github.com/swiftlang/swift-evolution/blob/fdfc7867df4e35e29b2a24edee34ea4412ec15b0/proposals/testing/0008-exit-tests.md) -* Review: ([acceptance](https://forums.swift.org/t/accepted-with-modifications-st-0008-exit-tests/79553)) ([second review](https://forums.swift.org/t/second-review-st-0008-exit-tests/79198)), ([review](https://forums.swift.org/t/st-0008-exit-tests/78692)), ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) +* Review: ([pitch](https://forums.swift.org/t/pitch-exit-tests/78071)) ([review](https://forums.swift.org/t/st-0008-exit-tests/78692)) ([second review](https://forums.swift.org/t/second-review-st-0008-exit-tests/79198)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-st-0008-exit-tests/79553)) ## Introduction diff --git a/proposals/testing/0009-attachments.md b/proposals/testing/0009-attachments.md index 8fee84e792..e9e23102ee 100644 --- a/proposals/testing/0009-attachments.md +++ b/proposals/testing/0009-attachments.md @@ -6,7 +6,7 @@ * Status: **Implemented (Swift 6.2)** * Bug: [swiftlang/swift-testing#714](https://github.com/swiftlang/swift-testing/issues/714) * Implementation: [swiftlang/swift-testing#973](https://github.com/swiftlang/swift-testing/pull/973) -* Review: ([acceptance](https://forums.swift.org/t/accepted-with-modifications-st-0009-attachments/79193)), ([review](https://forums.swift.org/t/st-0009-attachments/78698)), ([pitch](https://forums.swift.org/t/pitch-attachments/78072)) +* Review: ([pitch](https://forums.swift.org/t/pitch-attachments/78072)) ([review](https://forums.swift.org/t/st-0009-attachments/78698)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-st-0009-attachments/79193)) ## Introduction diff --git a/proposals/testing/0010-evaluate-condition.md b/proposals/testing/0010-evaluate-condition.md index 6a68caad10..d6a7336ff1 100644 --- a/proposals/testing/0010-evaluate-condition.md +++ b/proposals/testing/0010-evaluate-condition.md @@ -6,7 +6,7 @@ * Status: **Implemented (Swift 6.2)** * Bug: [swiftlang/swift-testing#903](https://github.com/swiftlang/swift-testing/issues/903) * Implementation: [swiftlang/swift-testing#909](https://github.com/swiftlang/swift-testing/pull/909), [swiftlang/swift-testing#1097](https://github.com/swiftlang/swift-testing/pull/1097) -* Review: ([pitch](https://forums.swift.org/t/pitch-introduce-conditiontrait-evaluate/77242)), ([review](https://forums.swift.org/t/st-0010-public-api-to-evaluate-conditiontrait/79232)), ([acceptance](https://forums.swift.org/t/accepted-st-0010-public-api-to-evaluate-conditiontrait/79577)) +* Review: ([pitch](https://forums.swift.org/t/pitch-introduce-conditiontrait-evaluate/77242)) ([review](https://forums.swift.org/t/st-0010-public-api-to-evaluate-conditiontrait/79232)) ([acceptance](https://forums.swift.org/t/accepted-st-0010-public-api-to-evaluate-conditiontrait/79577)) ## Introduction From e649f9c15811672202be7e3786de3fd733f2609f Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Wed, 7 May 2025 13:03:47 -0700 Subject: [PATCH 4292/4563] Add use cases --- proposals/testing/XXXX-issue-severity-warning.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index 05c122ff61..ed4c691d4d 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -15,6 +15,21 @@ I propose introducing a new API to Swift Testing that allows developers to recor Currently, when an issue arises during a test, the only possible outcome is to mark the test as failed. This presents a challenge for users who want a deeper insight into the events occurring within their tests. By introducing a dedicated mechanism to record issues that do not cause test failure, users can more effectively inspect and diagnose problems at runtime and review results afterward. This enhancement provides greater flexibility and clarity in test reporting, ultimately improving the debugging and analysis process. +### Use Cases + +- Warning about a Percentage Discrepancy in Image Comparison: + - Scenario: When comparing two images to assess their similarity, a warning can be triggered if there's a 95% pixel match, while a test failure is set at a 90% similarity threshold. + - Reason: In practices like snapshot testing, minor changes (such as a timestamp) might cause a discrepancy. Setting a 90% match as a pass ensures test integrity. However, a warning at 95% alerts testers that, although the images aren't identical, the test has passed, which may warrant further investigation. +- Warning for Duplicate Argument Inputs in Tests: + - Scenario: In a test library, issue a warning if a user inputs the same argument twice, rather than flagging an error. + - Reason: Although passing the same argument twice might not be typical, some users may have valid reasons for doing so. Thus, a warning suffices, allowing flexibility without compromising the test's execution. +- Warning for Recoverable Unexpected Events: + - Scenario: During an integration test where data is retrieved from a server, a warning can be issued if the primary server is down, prompting a switch to an alternative server. Usually mocking is the solution for this but may not test everything needed for an integration test. + - Reason: Since server downtime might happen and can be beyond the tester's control, issuing a warning rather than a failure helps in debugging and understanding potential issues without impacting the test's overall success. +- Warning for a retry during setup for a test: + - Scenario: During test setup part of your code may be configured to retry, it would be nice to notify in the results that a retry happened + - Reason: This makes sense to be a warning and not a failure because if the retry succeeds the test may still verify the code correctly + ## Proposed solution We propose introducing a new property on `Issue` in Swift Testing called `Severity`, that represents if an issue is a `warning` or an `error`. The default Issue severity will still be `error` and users can set the severity when they record an issue. From d51c8e8d9d58a0d062eaefadf5554edd6f29f7c6 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Wed, 7 May 2025 13:09:16 -0700 Subject: [PATCH 4293/4563] Updates based on comments and future direction --- .../testing/XXXX-issue-severity-warning.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index ed4c691d4d..6222ea240a 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -4,8 +4,8 @@ * Authors: [Suzy Ratcliff](https://github.com/suzannaratcliff) * Review Manager: TBD * Status: **Pitched** -* Bug: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075) * Implementation: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075) +* Review: ([pitch](https://forums.swift.org/...)) ## Introduction @@ -31,7 +31,7 @@ Currently, when an issue arises during a test, the only possible outcome is to m - Reason: This makes sense to be a warning and not a failure because if the retry succeeds the test may still verify the code correctly ## Proposed solution -We propose introducing a new property on `Issue` in Swift Testing called `Severity`, that represents if an issue is a `warning` or an `error`. +We propose introducing a new property on `Issue` in Swift Testing called `severity`, that represents if an issue is a `warning` or an `error`. The default Issue severity will still be `error` and users can set the severity when they record an issue. Test authors will be able to inspect if the issue is a failing issue and will be able to check the severity. @@ -45,8 +45,9 @@ We introduce a Severity enum to categorize issues detected during testing. This The `Severity` enum: ```swift - public enum Severity: Sendable { - /// The severity level for an issue which should be noted but is not +extension Issue { + // ... + public enum Severity: Codable, Comparable, CustomStringConvertible, Sendable { /// The severity level for an issue which should be noted but is not /// necessarily an error. /// /// An issue with warning severity does not cause the test it's associated @@ -59,6 +60,8 @@ The `Severity` enum: /// marked as a failure. case error } + // ... +} ``` *Recording Non-Failing Issues* @@ -121,6 +124,12 @@ This revision aims to clarify the functionality and usage of the `Severity` enum - Severity-Only Checking: We deliberated not exposing `isFailure` and relying solely on `severity` checks. However, this was rejected because it would require test authors to overhaul their code should we introduce additional severity levels in the future. By providing `isFailure`, we offer a straightforward way to determine test outcome impact, complementing the severity feature. +## Future directions + +- In the future I could see the warnings being able to be promoted to errors in order to run with a more strict testing configuration + +- In the future I could see adding other levels of severity such as Info and Debug for users to create issues with other information. + ## Acknowledgments Thanks to Stuart Montgomery for creating and implementing severity in Swift Testing. From a9cff4ff7ed87381028a38b9a844283404a79cb8 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Wed, 7 May 2025 13:11:20 -0700 Subject: [PATCH 4294/4563] Add pitch link --- proposals/testing/XXXX-issue-severity-warning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index 6222ea240a..dee36e088a 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -5,7 +5,7 @@ * Review Manager: TBD * Status: **Pitched** * Implementation: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075) -* Review: ([pitch](https://forums.swift.org/...)) +* Review: ([pitch](https://forums.swift.org/t/pitch-test-issue-warnings/79285) ## Introduction From 12448481de26eb801d4824bb4faa93b08e528906 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 7 May 2025 15:04:12 -0700 Subject: [PATCH 4295/4563] Address the rounds of feedback with some more behavioral refinements and an adjustment to the naming of `Observed` to now name as `Observations` (#2838) * Address the rounds of feedback with some more behavioral refinements and an adjustment to the naming of `Observed` to now name as `Observations` * Correct the next to last example to the latest mechansim's output --- proposals/0475-observed.md | 115 +++++++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 29 deletions(-) diff --git a/proposals/0475-observed.md b/proposals/0475-observed.md index ccb1901934..d421aba55f 100644 --- a/proposals/0475-observed.md +++ b/proposals/0475-observed.md @@ -51,7 +51,7 @@ that do not use UI as seamlessly as those that do. ## Proposed solution -This proposal adds a straightforward new tool: a closure-initialized `Observed` +This proposal adds a straightforward new tool: a closure-initialized `Observations` type that acts as a sequence of closure-returned values, emitting new values when something within that closure changes. @@ -76,12 +76,12 @@ final class Person { } ``` -Creating an `Observed` asynchronous sequence is straightforward. This example +Creating an `Observations` asynchronous sequence is straightforward. This example creates an asynchronous sequence that yields a value every time the composed `name` property is updated: ```swift -let names = Observed { person.name } +let names = Observations { person.name } ``` However if the example was more complex and the `Person` type in the previous @@ -89,7 +89,7 @@ example had a `var pet: Pet?` property which was also `@Observable` then the closure can be written with a more complex expression. ```swift -let greetings = Observed { +let greetings = Observations { if let pet = person.pet { return "Hello \(person.name) and \(pet.name)" } else { @@ -158,7 +158,7 @@ is expected to emit the same values in both tasks. ```swift -let names = Observed { person.firstName + " " + person.lastName } +let names = Observations { person.firstName + " " + person.lastName } Task.detached { for await name in names { @@ -185,7 +185,7 @@ properties as a `String`. This has no sense of termination locally within the construction. Making the return value of that closure be a lifted `Optional` suffers the potential conflation of a terminal value and a value that just happens to be nil. This means that there is a need for a second construction mechanism that offers a -way of expressing that the `Observed` sequence iteration will run until finished. +way of expressing that the `Observations` sequence iteration will run until finished. For the example if `Person` then has a new optional field of `homePage` which is an optional URL it then means that the construction can disambiguate @@ -206,7 +206,7 @@ final class Person { } } -let hosts = Observed.untilFinished { [weak person] in +let hosts = Observations.untilFinished { [weak person] in if let person { .next(person.homePage?.host) } else { @@ -218,7 +218,7 @@ let hosts = Observed.untilFinished { [weak person] in Putting this together grants a signature as such: ```swift -public struct Observed: AsyncSequence, Sendable { +public struct Observations: AsyncSequence, Sendable { public init( @_inheritActorContext _ emit: @escaping @isolated(any) @Sendable () throws(Failure) -> Element ) @@ -230,12 +230,12 @@ public struct Observed: AsyncSequence, Sendab public static func untilFinished( @_inheritActorContext _ emit: @escaping @isolated(any) @Sendable () throws(Failure) -> Iteration - ) -> Observed + ) -> Observations } ``` Picking the initializer apart first captures the current isolation of the -creation of the `Observed` instance. Then it captures a `Sendable` closure that +creation of the `Observations` instance. Then it captures a `Sendable` closure that inherits that current isolation. This means that the closure may only execute on the captured isolation. That closure is run to determine which properties are accessed by using Observation's `withObservationTracking`. So any access to a @@ -253,19 +253,19 @@ The closure has two other features that are important for common usage; firstly the closure is typed-throws such that any access to that emission closure will potentially throw an error if the developer specifies. This allows for complex composition of potentially failable systems. Any thrown error will mean that the -`Observed` sequence is complete and loops that are currently iterating will +`Observations` sequence is complete and loops that are currently iterating will terminate with that given failure. Subsequent calls then to `next` on those iterators will return `nil` - indicating that the iteration is complete. ## Behavioral Notes -There are a number of scenarios of iteration that can occur. These can range from production rate to iteration rate differentials to isolation differentials to concurrent iterations. Enumerating all possible combinations is of course not possible but the following explanations should illustrate some key usages. `Observed` does not make unsafe code somehow safe - the concepts of isolation protection or exclusive access are expected to be brought to the table by the types involved. It does however require the enforcements via Swift Concurrency particularly around the marking of the types and closures being required to be `Sendable`. The following examples will only illustrate well behaved types and avoid fully unsafe behavior that would lead to crashes because the types being used are circumventing that language safety. +There are a number of scenarios of iteration that can occur. These can range from production rate to iteration rate differentials to isolation differentials to concurrent iterations. Enumerating all possible combinations is of course not possible but the following explanations should illustrate some key usages. `Observations` does not make unsafe code somehow safe - the concepts of isolation protection or exclusive access are expected to be brought to the table by the types involved. It does however require the enforcements via Swift Concurrency particularly around the marking of the types and closures being required to be `Sendable`. The following examples will only illustrate well behaved types and avoid fully unsafe behavior that would lead to crashes because the types being used are circumventing that language safety. The most trivial case is where a single produce and single consumer are active. In this case they both are isolated to the same isolation domain. For ease of reading; this example is limited to the `@MainActor` but could just as accurately be represented in some other actor isolation. ```swift @MainActor -func iterate(_ names: Observed) async { +func iterate(_ names: Observations) async { for await name in names { print(name) } @@ -276,7 +276,7 @@ func example() async throws { let person = Person(firstName: "", lastName: "") // note #2 - let names = Observed { + let names = Observations { person.name } @@ -311,7 +311,7 @@ Next is the case where the mutation of the properties out-paces the iteration. A ```swift @MainActor -func iterate(_ names: Observed) async { +func iterate(_ names: Observations) async { for await name in names { print(name) try? await Task.sleep(for: .seconds(0.095)) @@ -323,7 +323,7 @@ func example() async throws { let person = Person(firstName: "", lastName: "") // @MainActor is captured here as the isolation - let names = Observed { + let names = Observations { person.name } @@ -353,7 +353,7 @@ The result of the observation may print the following output, but the primary pr This case dropped the last value of the iteration because the accumulated differential exceeded the production; however the potentially confusing part here is that the sleep in the iterate competes with the scheduling in the emitter. This becomes clearer of a relationship when the boundaries of isolation are crossed. -Observed can be used across boundaries of concurrency. This is where the iteration is done on a different isolation than the mutations. The types however are accessed always in the isolation that the creation of the Observed closure is executed. This means that if the `Observed` instance is created on the main actor then the subsequent calls to the closure will be done on the main actor. +Observations can be used across boundaries of concurrency. This is where the iteration is done on a different isolation than the mutations. The types however are accessed always in the isolation that the creation of the Observations closure is executed. This means that if the `Observations` instance is created on the main actor then the subsequent calls to the closure will be done on the main actor. ```swift @globalActor @@ -362,7 +362,7 @@ actor ExcplicitlyAnotherActor: GlobalActor { } @ExcplicitlyAnotherActor -func iterate(_ names: Observed) async { +func iterate(_ names: Observations) async { for await name in names { print(name) } @@ -373,7 +373,7 @@ func example() async throws { let person = Person(firstName: "", lastName: "") // @MainActor is captured here as the isolation - let names = Observed { + let names = Observations { person.name } @@ -402,12 +402,12 @@ The values still will be conjoined as expected for their changes, however just l If the `iterate` function was altered to have a similar `sleep` call that exceeded the production then it would result in similar behavior of the previous producer/consumer rate case. -The next behavioral illustration is the value distribution behaviors; this is where two or more copies of an `Observed` are iterated concurrently. +The next behavioral illustration is the value distribution behaviors; this is where two or more copies of an `Observations` are iterated concurrently. ```swift @MainActor -func iterate1(_ names: Observed) async { +func iterate1(_ names: Observations) async { for await name in names { print("A", name) } @@ -415,7 +415,7 @@ func iterate1(_ names: Observed) async { @MainActor -func iterate2(_ names: Observed) async { +func iterate2(_ names: Observations) async { for await name in names { print("B", name) } @@ -426,7 +426,7 @@ func example() async throws { let person = Person(firstName: "", lastName: "") // @MainActor is captured here as the isolation - let names = Observed { + let names = Observations { person.name } @@ -452,18 +452,64 @@ This situation commonly comes up when the asynchronous sequence is stored as a p ``` A 0 0 +B 0 0 B 1 1 A 1 1 -B 2 2 A 2 2 +B 2 2 A 3 3 B 3 3 -A 4 4 B 4 4 +A 4 4 ``` The same rate commentary applies here as before but an additional wrinkle is that the delivery between the A and B sides is non-determinstic (in some cases it can deliver as A then B and other cases B then A). +There is one additional clarification of expected behaviors - the iterators should have an initial state to determine if that specific iterator is active yet or not. This means that upon the first call to next the value will be obtained by calling into the isolation of the constructing closure to "prime the pump" for observation and obtain a first value. This can be encapsulated into an exaggerated test example as the following: + +```swift + +@MainActor +func example() async { + let person = Person(firstName: "0", lastName: "0") + + // @MainActor is captured here as the isolation + let names = Observations { + person.name + } + Task { + try await Task.sleep(for: .seconds(2)) + person.firstName = "1" + person.lastName = "1" + + } + Task { + for await name in names { + print("A = \(name)") + } + } + Task { + for await name in names { + print("B = \(name)") + } + } + try? await Task.sleep(for: .seconds(10)) +} + +await example() +``` + +Which results in the following output: + +``` +A = 0 0 +B = 0 0 +B = 1 1 +A = 1 1 +``` + +This ensures the first value is produced such that every sequence will always be primed with a value and will eventually come to a mutual consistency to the values no matter the isolation. + ## Effect on ABI stability & API resilience This provides no alteration to existing APIs and is purely additive. However it @@ -478,19 +524,19 @@ need to be disambiguated. This proposal does not change the fact that the spectrum of APIs may range from favoring `AsyncSequence` properties to purely `@Observable` models. They both have their place. However the calculus of determining the best exposition may -be slightly more refined now with `Observed`. +be slightly more refined now with `Observations`. If a type is representative of a model and is either transactional in that some properties may be linked in their meaning and would be a mistake to read in a disjoint manner (the tearing example from previous sections), or if the model interacts with UI systems it now more so than ever makes sense to use -`@Observable` especially with `Observed` now as an option. Some cases may have +`@Observable` especially with `Observations` now as an option. Some cases may have previously favored exposing those `AsyncSequence` properties and would now -instead favor allowing the users of those APIs compose things by using `Observed`. +instead favor allowing the users of those APIs compose things by using `Observations`. The other side of the spectrum will still exist but now is more strongly relegated to types that have independent value streams that are more accurately described as `AsyncSequence` types being exposed. The suggestion for API authors -is that now with `Observed` favoring `@Observable` perhaps should take more +is that now with `Observations` favoring `@Observable` perhaps should take more of a consideration than it previously did. ## Alternatives Considered @@ -527,3 +573,14 @@ parameter of an isolation that was non-nullable this could be achieved for that however up-coming changes to Swift's Concurrency will make this approach less appealing. If this route would be taken it would restrict the potential advanced uses cases where the construction would be in an explicitly non-isolated context. + +A name of `Observed` was considered, however that type name led to some objections that +rightfully claimed it was a bit odd as a name since it is bending the "nouning" of names +pretty strongly. This lead to the alternate name `Observations` which strongly leans +into the plurality of the name indicating that it is more than one observation - lending +to the sequence nature. + +It was seriously considered during the feedback to remove the initializer methods and only +have construction by two global functions named `observe` and `observeUntilFinished` +that would act as the current initializer methods. Since the types must still be returned +to allow for storing that return into a property it does not offer a distinct advantage. From cc662fd86b38401b70a73e679829b9b167761e61 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 7 May 2025 18:07:45 -0400 Subject: [PATCH 4296/4563] Extend review of SE-0475 (#2839) --- proposals/0475-observed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0475-observed.md b/proposals/0475-observed.md index d421aba55f..f176573c28 100644 --- a/proposals/0475-observed.md +++ b/proposals/0475-observed.md @@ -3,7 +3,7 @@ * Proposal: [SE-0475](0475-observed.md) * Authors: [Philippe Hausler](https://github.com/phausler) * Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) -* Status: **Active Review (April 10 ... April 24, 2025)** +* Status: **Active Review (April 10 ... May 13, 2025)** * Implementation: https://github.com/swiftlang/swift/pull/79817 * Review: ([pitch](https://forums.swift.org/t/pitch-transactional-observation-of-values/78315)) ([review](https://forums.swift.org/t/se-0475-transactional-observation-of-values/79224)) From a8eb41111d916b5a4f1a24fb3969fcba9e0a9057 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Fri, 9 May 2025 10:53:50 -0700 Subject: [PATCH 4297/4563] Add a review proposal announcement template for Swift Testing to process documentation (#2842) --- process.md | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/process.md b/process.md index fe32a48828..3408e2d567 100644 --- a/process.md +++ b/process.md @@ -241,7 +241,7 @@ A given proposal can be in one of several states: [swift-evolution-repo]: https://github.com/swiftlang/swift-evolution "Swift evolution repository" [swift-evolution-staging]: https://github.com/swiftlang/swift-evolution-staging "Swift evolution staging repository" -[proposal-reviews]: https://forums.swift.org/c/evolution/proposal-reviews "'Proposal reviews' category of the Swift forums" +[proposal-reviews]: https://forums.swift.org/c/evolution/proposal-reviews "'Proposal reviews' subcategory of the Swift forums" [status-page]: https://www.swift.org/swift-evolution [preview-package]: https://github.com/apple/swift-standard-library-preview/ [language-steering-group]: https://www.swift.org/language-steering-group @@ -253,11 +253,15 @@ A given proposal can be in one of several states: ## Review announcement -When a proposal enters review, a new topic will be posted to the ["Proposal Reviews" section of the Swift forums][proposal-reviews] -using the following template: +When a proposal enters review, a new topic will be posted to the +["Proposal Reviews" subcategory of the Swift forums][proposal-reviews] using the +relevant announcement template below: --- +

      +Swift language, compiler, and standard library + Hello Swift community, The review of "\<\>" begins now and runs through \<\ + +
      +Swift Testing public interfaces and features + +Hello Swift community, + +The review of "\<\>" begins now and runs through \<\>. +The proposal is available here: + +> https://linkToProposal + +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. + +##### Trying it out + +To try this feature out, add a dependency to the `main` branch of +`swift-testing` to your package: + +```swift +dependencies: [ + ... + .package(url: "https://github.com/swiftlang/swift-testing.git", branch: "main"), +] +``` + +Then, add a target dependency to your test target: + +```swift +.testTarget( + ... + dependencies: [ + ... + .product(name: "Testing", package: "swift-testing"), + ] +``` + +Finally, import Swift Testing using `@_spi(Experimental) import Testing`. + +##### 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 + Testing? +* Does this proposal fit well with the feel and direction of Swift Testing? +* 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/swiftlang/swift-evolution/blob/main/process.md + +Thank you, + +-\<\> + +Review Manager + +
      From 2668f20b72bcb1c224fddea8846990488398288f Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 12 May 2025 08:24:36 -0700 Subject: [PATCH 4298/4563] Mark SE-0461 as implemented in Swift 6.2. (#2844) --- proposals/0461-async-function-isolation.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md index f8712cde7d..b81520083c 100644 --- a/proposals/0461-async-function-isolation.md +++ b/proposals/0461-async-function-isolation.md @@ -3,10 +3,9 @@ * Proposal: [SE-0461](0461-async-function-isolation.md) * Authors: [Holly Borla](https://github.com/hborla), [John McCall](https://github.com/rjmccall) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Accepted with modifications** +* Status: **Implemented (Swift 6.2)** * Vision: [Improving the approachability of data-race safety](/visions/approachable-concurrency.md) -* Implementation: On `main` behind `-enable-experimental-feature NonIsolatedAsyncInheritsIsolationFromContext` -* Upcoming Feature Flag: `AsyncCallerExecution` +* Upcoming Feature Flag: `NonisolatedNonsendingByDefault` * Previous Proposal: [SE-0338](0338-clarify-execution-non-actor-async.md) * Review: ([pitch](https://forums.swift.org/t/pitch-inherit-isolation-by-default-for-async-functions/74862)) ([first review](https://forums.swift.org/t/se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/77987)) ([acceptance with focused re-review](https://forums.swift.org/t/accepted-with-modifications-and-focused-re-review-se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/78920)) ([second review](https://forums.swift.org/t/focused-re-review-se-0461-run-nonisolated-async-functions-on-the-callers-actor-by-default/78921)) ([second acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0461-run-nonisolated-async-functions-on-the-caller-s-actor-by-default/79117)) From 5ff1fa18664ca5277d35fd6eb757082d0eee441b Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Mon, 12 May 2025 20:13:20 +0200 Subject: [PATCH 4299/4563] [SE-0461] Fix typo (#2834) --- proposals/0461-async-function-isolation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md index b81520083c..c123ce36b7 100644 --- a/proposals/0461-async-function-isolation.md +++ b/proposals/0461-async-function-isolation.md @@ -288,7 +288,7 @@ actor MyActor { In the above code, the call to `x.performAsync()` continues running on the `self` actor instance. The code does not produce a data-race safety error, because the `NotSendable` instance `x` does not leave the actor. In other -words, the arguments are not send across an isolation boundary when calling +words, the arguments are not sent across an isolation boundary when calling `performAsync` by default. This behavior is accomplished by implicitly passing an optional actor parameter From 408dd1640e8cf434a33c8be0893356499f12fb9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C5=93ur?= Date: Mon, 12 May 2025 20:15:12 +0200 Subject: [PATCH 4300/4563] typo: ends -> begins (#2840) --- proposals/0433-mutex.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0433-mutex.md b/proposals/0433-mutex.md index f98a9cef27..cdb7946574 100644 --- a/proposals/0433-mutex.md +++ b/proposals/0433-mutex.md @@ -330,7 +330,7 @@ mutex.withLock { // borrow access ends do { - // borrow access ends + // borrow access begins let locked = mutex.lock() ... From 1190985f0afada80964b9a91208fb73eb2ea830e Mon Sep 17 00:00:00 2001 From: Itai Ferber Date: Mon, 12 May 2025 18:48:49 -0400 Subject: [PATCH 4301/4563] Allow Additional Arguments to `@dynamicMemberLookup` Subscripts (#2814) * Add proposal for Allow Additional Arguments to `@dynamicMemberLookup` Subscripts * Incorporate pitch thread feedback * Update review manager * Assign SE-0484 --------- Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- ...-args-to-dynamicmemberlookup-subscripts.md | 281 ++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 proposals/0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md diff --git a/proposals/0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md b/proposals/0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md new file mode 100644 index 0000000000..a6664a2b3d --- /dev/null +++ b/proposals/0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md @@ -0,0 +1,281 @@ +# Allow Additional Arguments to `@dynamicMemberLookup` Subscripts + +* Proposal: [SE-0484](0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md) +* Authors: [Itai Ferber](https://github.com/itaiferber) +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Active review (May 12...25, 2025)** +* Implementation: [swiftlang/swift#81148](https://github.com/swiftlang/swift/pull/81148) +* Previous Proposals: [SE-0195](0195-dynamic-member-lookup.md), [SE-0252](0252-keypath-dynamic-member-lookup.md) +* Review: ([pitch](https://forums.swift.org/t/pitch-allow-additional-arguments-to-dynamicmemberlookup-subscripts/79558)) + +## Introduction + +SE-0195 and SE-0252 introduced and refined `@dynamicMemberLookup` to provide type-safe "dot"-syntax access to arbitrary members of a type by reflecting the existence of certain `subscript(dynamicMember:)` methods on that type, turning + +```swift +let _ = x.member +x.member = 42 +ƒ(&x.member) +``` + +into + +```swift +let _ = x[dynamicMember: ] +x[dynamicMember: ] = 42 +ƒ(&x[dynamicMember: ]) +``` + +when `x.member` doesn't otherwise exist statically. Currently, in order to be eligible to satisfy `@dynamicMemberLookup` requirements, a subscript must: + +1. Take _exactly one_ argument with an explicit `dynamicMember` argument label, +2. Whose type is non-variadic and is either + * A `{{Reference}Writable}KeyPath`, or + * A concrete type conforming to `ExpressibleByStringLiteral` + +This proposal intends to relax the "exactly one" requirement above to allow eligible subscripts to take additional arguments after `dynamicMember` as long as they have a default value (or are variadic, and thus have an implicit default value). + +## Motivation + +Dynamic member lookup is often used to provide expressive and succinct API in wrapping some underlying data, be it a type-erased foreign language object (e.g., a Python `PyVal` or a JavaScript `JSValue`) or a native Swift type. This (and [`callAsFunction()`](0253-callable.md)) allow a generalized API interface such as + +```swift +struct Value { + subscript(_ property: String) -> Value { + get { ... } + set { ... } + } + + func invoke(_ method: String, _ args: Any...) -> Value { + ... + } +} + +let x: Value = ... +let _ = x["member"] +x["member"] = Value(42) +x.invoke("someMethod", 1, 2, 3) +``` + +to be expressed much more naturally: + +```swift +@dynamicMemberLookup +struct Value { + struct Method { + func callAsFunction(_ args: Any...) -> Value { ... } + } + + subscript(dynamicMember property: String) -> Value { + get { ... } + set { ... } + } + + subscript(dynamicMember method: String) -> Method { ... } +} + +let x: Value = ... +let _ = x.member +x.member = Value(42) +x.someMethod(1, 2, 3) +``` + +However, as wrappers for underlying data, sometimes interfaces like this need to be able to "thread through" additional information. For example, it might be helpful to provide information about call sites for debugging purposes: + +```swift +struct Value { + subscript( + _ property: String, + function: StaticString = #function, + file: StaticString = #fileID, + line: UInt = #line + ) -> Value { + ... + } + + func invokeMethod( + _ method: String, + function: StaticString = #function, + file: StaticString = #fileID, + line: UInt = #line, + _ args: Any... + ) -> Value { + ... + } +} +``` + +When additional arguments like this have default values, they don't affect the appearance of call sites at all: + +```swift +let x: Value = ... +let _ = x["member"] +x["member"] = Value(42) +x.invoke("someMethod", 1, 2, 3) +``` + +However, these are not valid for use with dynamic member lookup subscripts, since the additional arguments prevent subscripts from being eligible for dynamic member lookup: + +```swift +@dynamicMemberLookup // error: @dynamicMemberLookupAttribute requires 'Value' to have a 'subscript(dynamicMember:)' method that accepts either 'ExpressibleByStringLiteral' or a key path +struct Value { + subscript( + dynamicMember property: String, + function: StaticString = #function, + file: StaticString = #fileID, + line: UInt = #line + ) -> Value { + ... + } + + subscript( + dynamicMember method: String, + function: StaticString = #function, + file: StaticString = #fileID, + line: UInt = #line + ) -> Method { + ... + } +} +``` + +## Proposed solution + +We can amend the rules for such subscripts to make them eligible. With this proposal, in order to be eligible to satisfy `@dynamicMemberLookup` requirements, a subscript must: + +1. Take an initial argument with an explicit `dynamicMember` argument label, +2. Whose parameter type is non-variadic and is either: + * A `{{Reference}Writable}KeyPath`, or + * A concrete type conforming to `ExpressibleByStringLiteral`, +3. And whose following arguments (if any) are all either variadic or have a default value + +## Detailed design + +Since compiler support for dynamic member lookup is already robust, implementing this requires primarily: + +1. Type-checking of `@dynamicMemberLookup`-annotated declarations to also consider `subscript(dynamicMember:...)` methods following the above rules as valid, and +2. Syntactic transformation of `T.` to `T[dynamicMember:...]` in the constraint system to fill in default arguments expressions for any following arguments + +## Source compatibility + +This is largely an additive change with minimal impact to source compatibility. Types which do not opt in to `@dynamicMemberLookup` are unaffected, as are types which do opt in and only offer `subscript(dynamicMember:)` methods which take a single argument. + +However, types which opt in to `@dynamicMemberLookup` and currently offer an overload of `subscript(dynamicMember:...)`—which today is not eligible for consideration for dynamic member lookup—_may_ now select this overload when they wouldn't have before. + +### Overload resolution + +Dynamic member lookups go through regular overload resolution, with an additional disambiguation rule that prefers keypath-based subscript overloads over string-based ones. Since the `dynamicMember` argument to dynamic member subscripts is implicit, overloads of `subscript(dynamicMember:)` are primarily selected based on their return type (and typically for keypath-based subscripts, how that return type is used in forming the type of a keypath parameter). + +With this proposal, all arguments to `subscript(dynamicMember:...)` are still implicit, so overloads are still primarily selected based on return type, with the additional disambiguation rule that prefers overloads with fewer arguments over overloads with more arguments. (This rule applies "for free" since it already applies to method calls, which dynamic member lookups are transformed into.) + +This means that if a type today offers a valid `subscript(dynamicMember:) -> T` and a (currently-unconsidered) `subscript(dynamicMember:...) -> U`, + +1. If `T == U` then the former will still be the preferred overload in all circumstances +2. If `T` and `U` are compatible (and equally-specific) at a callsite then the former will still be the preferred overload +3. If `T` and `U` are incompatible, or if one is more specific than the other, then the more specific type will be preferred + +For example: + +```swift +@dynamicMemberLookup +struct A { + /* (1) */ subscript(dynamicMember member: String) -> String { ... } + /* (2) */ subscript(dynamicMember member: String, _: StaticString = #function) -> String { ... } +} + +@dynamicMemberLookup +struct B { + /* (3) */ subscript(dynamicMember member: String) -> String { ... } + /* (4) */ subscript(dynamicMember member: String, _: StaticString = #function) -> Int { ... } +} + +@dynamicMemberLookup +struct C { + /* (5) */ subscript(dynamicMember member: String) -> String { ... } + /* (6) */ subscript(dynamicMember member: String, _: StaticString = #function) -> String? { ... } +} + +// T == U +let _ = A().member // (1) preferred over (2); no ambiguity +let _: String = A().member // (1) preferred over (2); no ambiguity + +// T and U are compatible +let _: Any = A().member // (1) preferred over (2); no ambiguity +let _: Any = B().member // (3) preferred over (4); no ambiguity +let _: Any = C().member // (5) preferred over (6); no ambiguity + +// T and U are incompatible/differently-specific +let _: String = B().member // (3) +let _: Int = B().member // (4);️ would not previously compile +let _: String = C().member // (5); no ambiguity +let _: String? = C().member // (6) preferred over (5); ⚠️ previously (5) ⚠️ +``` + +This last case is the only source of behavior change: (6) was previously not considered a valid candidate, but has a return type more specific than (5), and is now picked at a callsite. + +In practice, it is expected that this situation is exceedingly rare. + +## ABI compatibility + +This feature is implemented entirely in the compiler as a syntactic transformation and has no impact on the ABI. + +## Implications on adoption + +The changes in this proposal require the adoption of a new version of the Swift compiler. + +## Alternatives considered + +The main alternative to this proposal is to not implement it, as: +1. It was noted in [the pitch thread](https://forums.swift.org/t/pitch-allow-additional-arguments-to-dynamicmemberlookup-subscripts/79558) that allowing additional arguments to dynamic member lookup widens the gap in capabilities between dynamic members and regular members — dynamic members would be able to + + 1. Have caller side effects (i.e., have access to `#function`, `#file`, `#line`, etc.), + 2. Constrain themselves via generics, and + 3. Apply isolation to themselves via `#isolation` + + where regular members cannot. However, (i) and (iii) are not considered an imbalance in functionality but instead are the raison d'être of this proposal. (ii) is also already possible today as dynamic member subscripts can be constrained via generics (and this is often used with keypath-based lookup). +2. This is possible to work around using explicit methods such as `get()` and `set(_:)`: + + ```swift + @dynamicMemberLookup + struct Value { + struct Property { + func get( + function: StaticString = #function, + file: StaticString = #file, + line: UInt = #line + ) -> Value { + ... + } + + func set( + _ value: Value, + function: StaticString = #function, + file: StaticString = #file, + line: UInt = #line + ) { + ... + } + } + + subscript(dynamicMember member: String) -> Property { ... } + } + + let x: Value = ... + let _ = x.member.get() // x.member + x.member.set(Value(42)) // x.member = Value(42) + ``` + + However, this feels non-idiomatic, and for long chains of getters and setters, can become cumbersome: + + ```swift + let x: Value = ... + let _ = x.member.get().inner.get().nested.get() // x.member.inner.nested + x.member.get().inner.get().nested.set(Value(42)) // x.member.inner.nested = Value(42) + ``` + +### Source compatibility + +It is possible to avoid the risk of the behavior change noted above by adjusting the constraint system to always prefer `subscript(dynamicMember:) -> T` overloads over `subscript(dynamicMember:...) -> U` overloads (if `T` and `U` are compatible), even if `U` is more specific than `T`. However, + +1. This would be a departure from the normal method overload resolution behavior that Swift developers are familiar with, and +2. If `T` were a supertype of `U`, it would be impossible to ever call the more specific overload except by direct subscript access From 624d429bf0c4adbdf7d0c1f448f8d771bcc56e11 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Mon, 12 May 2025 18:53:47 -0400 Subject: [PATCH 4302/4563] Add review link for SE-0484 (#2845) --- ...4-allow-additional-args-to-dynamicmemberlookup-subscripts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md b/proposals/0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md index a6664a2b3d..1e3d05745c 100644 --- a/proposals/0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md +++ b/proposals/0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md @@ -6,7 +6,7 @@ * Status: **Active review (May 12...25, 2025)** * Implementation: [swiftlang/swift#81148](https://github.com/swiftlang/swift/pull/81148) * Previous Proposals: [SE-0195](0195-dynamic-member-lookup.md), [SE-0252](0252-keypath-dynamic-member-lookup.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-allow-additional-arguments-to-dynamicmemberlookup-subscripts/79558)) +* Review: ([pitch](https://forums.swift.org/t/pitch-allow-additional-arguments-to-dynamicmemberlookup-subscripts/79558)) ([review](https://forums.swift.org/t/se-0484-allow-additional-arguments-to-dynamicmemberlookup-subscripts/79853)) ## Introduction From d829c9777f6f3ec5d2a2781ae198665235a3ba2b Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 13 May 2025 04:46:57 -0700 Subject: [PATCH 4303/4563] Updates and edits for `OutputSpan` proposal --- proposals/nnnn-outputspan.md | 348 +++++++++++++++++++++++++++++------ 1 file changed, 291 insertions(+), 57 deletions(-) diff --git a/proposals/nnnn-outputspan.md b/proposals/nnnn-outputspan.md index c642798fe3..0b8fb73d29 100644 --- a/proposals/nnnn-outputspan.md +++ b/proposals/nnnn-outputspan.md @@ -18,31 +18,31 @@ ## Introduction -Following the introduction of [`Span`][SE-0447] and [`MutableSpan`][SE-0467], this proposal adds a general facility for initialization of exclusively-borrowed memory with the `OutputSpan` and `OutputRawSpan` types. The memory represented by `OutputSpan` consists of a number of initialized elements, followed by uninitialized memory. The operations of `OutputSpan` can change the number of initialized elements in memory, unlike `MutableSpan` which always represents memory that is initialized. +Following the introduction of [`Span`][SE-0447] and [`MutableSpan`][SE-0467], this proposal adds a general facility for initialization of exclusively-borrowed memory with the `OutputSpan` and `OutputRawSpan` types. The memory represented by `OutputSpan` consists of a number of initialized elements, followed by uninitialized memory. The operations of `OutputSpan` can change the number of initialized elements in memory, unlike `MutableSpan` which always represent initialized memory representing a fixed number of elements. ## Motivation -Some standard library container types can delegate initialization of some or all of their storage to user code. Up to now, it has only been possible to do so with explicitly unsafe ways, which have also proven error-prone. The standard library provides this unsafe functionality with the closure-taking initializers `Array.init(unsafeUninitializedCapacity:initializingWith:)` and `String.init(unsafeUninitializedCapacity:initializingUTF8With:)`. +Some standard library container types can delegate initialization of some or all of their storage to user code. Up to now, it has only been possible to do so with explicitly unsafe functions, which have also proven error-prone. The standard library provides this unsafe functionality with the closure-taking initializers `Array.init(unsafeUninitializedCapacity:initializingWith:)` and `String.init(unsafeUninitializedCapacity:initializingUTF8With:)`. These functions have a few different drawbacks, most prominently their reliance on unsafe types, which makes them unpalatable in security-conscious environments. We continue addressing these issues with `OutputSpan` and `OutputRawSpan`, new non-copyable and non-escapable types that manage initialization of typed and untyped memory. -In addition to the new types, we will propose adding new API for some standard library types to take advantage of `OutputSpan` and `OutputRawSpan`, and improve upon the `Array` and `String` initializers mentioned above. +In addition to the new types, we propose adding new API for some standard library types to take advantage of `OutputSpan` and `OutputRawSpan`. ## Proposed solution #### OutputSpan -`OutputSpan` allows delegating the initialization of a type's memory, by providing access to an exclusively-borrowed view of a range of contiguous memory. `OutputSpan`'s contiguous memory consists of a prefix of initialized memory, followed by a suffix of uninitialized memory. Like `MutableSpan`, `OutputSpan` relies on two guarantees: (a) that it has exclusive access to the range of memory it represents, and (b) that the memory locations it represents will remain valid for the duration of the access. These guarantee data race safety and temporal safety. `OutputSpan` performs bounds-checking on every access to preserve spatial safety. +`OutputSpan` allows delegating the initialization of a type's memory, by providing access to an exclusively-borrowed view of a range of contiguous memory. `OutputSpan`'s contiguous memory always consists of a prefix of initialized memory, followed by a suffix of uninitialized memory. `OutputSpan`'s operations manage the initialization state in order to preserve that invariant. The common usage pattern we expect to see for `OutputSpan` consists of passing it as an `inout` parameter to a function, allowing the function to produce an output by writing into a previously uninitialized region. -An `OutputSpan` provided by a container represents a mutation of that container, and is therefore an exclusive access. +Like `MutableSpan`, `OutputSpan` relies on two guarantees: (a) that it has exclusive access to the range of memory it represents, and (b) that the memory locations it represents will remain valid for the duration of the access. These guarantee data race safety and temporal safety. `OutputSpan` performs bounds-checking on every access to preserve spatial safety. `OutputSpan` manages the initialization state of the memory in represents on behalf of the memory's owner. #### OutputRawSpan -`OutputRawSpan` allows delegating the initialization of heterogeneously-typed memory, such as memory being prepared by an encoder. It makes the same safety guarantees as `OutputSpan`. +`OutputRawSpan` allows delegating the initialization of heterogeneously-typed memory, such as memory being prepared by an encoder. It makes the same safety guarantees as `OutputSpan`, but manages untyped memory. #### Extensions to standard library types -The standard library will provide new container initializers that delegate to an `OutputSpan`. Delegated initialization generally requires a container to perform some operations after the initialization has happened. In the case of `Array` this is simply noting the number of initialized elements; in the case of `String` this consists of validating the input. This post-processing implies the need for a scope, and we believe that scope is best represented by a closure. The `Array` initializer will be as follows: +The standard library will provide new container initializers that delegate to an `OutputSpan`. Delegated initialization generally requires a container to perform some operations after the initialization has happened. In the case of `Array` this is simply noting the number of initialized elements; in the case of `String` this consists of validating the input, then noting metadata about the input. This post-processing implies the need for a scope, and we believe that scope is best represented by a closure. The `Array` initializer will be as follows: ```swift extension Array { @@ -65,7 +65,7 @@ Note: The eventual lifetime annotations proposal may adopt a syntax different th #### OutputSpan -`OutputSpan` is a simple representation of a partially-initialized region of memory. It is non-copyable in order to enforce exclusive access for mutations of its memory, as required by the law of exclusivity: +`OutputSpan` is a simple representation of a partially-initialized region of memory. It is non-copyable in order to enforce exclusive access during mutations of its memory, as required by the law of exclusivity: ````swift @frozen @@ -132,7 +132,7 @@ extension OutputSpan { @discardableResult @lifetime(self: copy self) public mutating func append( - from source: inout some IteratorProtocol + contentsOf source: inout some IteratorProtocol ) -> Bool /// Initialize this span's suffix with every element of the source. @@ -141,7 +141,7 @@ extension OutputSpan { /// can contain every element of the source. @lifetime(self: copy self) public mutating func append( - fromContentsOf source: some Sequence + contentsOf source: some Sequence ) /// Initialize this span's suffix with every element of the source. @@ -150,7 +150,7 @@ extension OutputSpan { /// can contain every element of the source. @lifetime(self: copy self) public mutating func append( - fromContentsOf source: Span + copying source: Span ) /// Initialize this span's suffix with every element of the source. @@ -159,7 +159,7 @@ extension OutputSpan { /// can contain every element of the source. @lifetime(self: copy self) public mutating func append( - fromContentsOf source: UnsafeBufferPointer + copying source: UnsafeBufferPointer ) } @@ -169,8 +169,8 @@ extension OutputSpan where Element: ~Copyable { /// It is a precondition that the `OutputSpan`'s uninitialized suffix /// can contain every element of the source. @lifetime(self: copy self) - public mutating func moveAppend( - fromContentsOf source: inout OutputSpan + public mutating func append( + moving source: inout OutputSpan ) /// Initialize this span's suffix by moving every element from the source. @@ -178,8 +178,8 @@ extension OutputSpan where Element: ~Copyable { /// It is a precondition that the `OutputSpan`'s uninitialized suffix /// can contain every element of the source. @lifetime(self: copy self) - public mutating func moveAppend( - fromContentsOf source: UnsafeMutableBufferPointer + public mutating func append( + consuming source: UnsafeMutableBufferPointer ) } @@ -189,13 +189,13 @@ extension OutputSpan { /// It is a precondition that the `OutputSpan`'s uninitialized suffix /// can contain every element of the source. @lifetime(self: copy self) - public mutating func moveAppend( - fromContentsOf source: Slice> + public mutating func append( + consuming source: Slice> ) } ``` -Bulk operations to deinitialize some or all of an `OutputSpan`'s memory is also available: +Bulk operations to deinitialize some or all of an `OutputSpan`'s memory are also available: ```swift extension OutputSpan where Element: ~Copyable { /// Remove the last N elements, returning the memory they occupy @@ -204,7 +204,7 @@ extension OutputSpan where Element: ~Copyable { /// `n` must not be greater than `count` @lifetime(self: copy self) public mutating func removeLast(_ n: Int) - + /// Remove all this span's elements and return its memory to the uninitialized state. @lifetime(self: copy self) public mutating func removeAll() @@ -230,7 +230,6 @@ extension OutputSpan where Element: ~Copyable { ``` - ##### Interoperability with unsafe code We provide a method to process or populate an `OutputSpan` using unsafe operations, which can also be used for out-of-order initialization. @@ -269,7 +268,6 @@ extension OutputSpan where Element: ~Copyable { ``` - ##### Creating an `OutputSpan` instance: Creating an `OutputSpan` is an unsafe operation. It requires having knowledge of the initialization state of the range of memory being targeted. The range of memory must be in two regions: the first region contains initialized instances of `Element`, and the second region is uninitialized. The number of initialized instances is passed to the `OutputSpan` initializer through its `initializedCount` argument. @@ -321,10 +319,9 @@ extension OutputSpan { ``` - ##### Retrieving initialized memory from an `OutputSpan` -Once memory has been initialized using `OutputSpan`, the owner of the memory must consume the `OutputSpan` in order to retake ownership of the initialized memory. The owning type must pass the memory used to initialize the `OutputSpan` to the `finalize(for:)` function. Passing the wrong buffer is a programmer error and the function traps. `finalize()` consumes the `OutputSpan` instance and returns the number of initialized elements. +Once memory has been initialized using `OutputSpan`, the owner of the memory must consume the `OutputSpan` in order to retake ownership of the initialized memory. The owning type must pass the memory used to initialize the `OutputSpan` to the `finalize(for:)` function. Passing the wrong buffer is a programmer error and the function traps. `finalize(for:)` consumes the `OutputSpan` instance and returns the number of initialized elements. If `finalize(for:)` is not called, the initialized portion of `OutputSpan`'s memory will be deinitialized when the binding goes out of scope. ```swift extension OutputSpan where Element: ~Copyable { @@ -355,10 +352,9 @@ extension OutputSpan { ``` - - #### `OutputRawSpan` -`OutputRawSpan` is similar to `OutputSpan`, but represents an untyped partially-initialized region of memory. Its API supports appending the bytes of instances of `BitwiseCopyable` types, as well as a variety of bulk initialization operations. +`OutputRawSpan` is similar to `OutputSpan`, but its initialized memory is untyped. Its API supports appending the bytes of instances of `BitwiseCopyable` types, as well as a variety of bulk initialization operations. + ```swift @frozen public struct OutputRawSpan: ~Copyable, ~Escapable { @@ -387,7 +383,6 @@ extension OutputRawSpan { ``` - ##### Appending to `OutputRawSpan` The basic operation is to append the bytes of some value to an `OutputRawSpan`: @@ -395,7 +390,7 @@ The basic operation is to append the bytes of some value to an `OutputRawSpan`: extension OutputRawSpan { /// Appends the given value's bytes to this span's initialized bytes @lifetime(self: copy self) - public mutating func appendBytes( + public mutating func append( of value: T, as type: T.Type ) } @@ -406,38 +401,40 @@ This is also supported with bulk operations. Initializing an `OutputRawSpan` fro extension OutputRawSpan /// Initialize this span's suffix to the repetitions of the given value's bytes. @lifetime(self: copy self) - public mutating func append(repeating repeatedValue: T, count: Int) + public mutating func append( + repeating repeatedValue: T, count: Int, as type: T.Type + ) /// Initialize the span's bytes with the bytes of the elements of the source. /// /// Returns true if the iterator has filled all the free capacity in the span. @lifetime(self: copy self) - public mutating func append( - from source: inout some IteratorProtocol + public mutating func append, as type: T.Type ) -> Bool /// Initialize the span's bytes with every byte of the source. @lifetime(self: copy self) - public mutating func append( - fromContentsOf source: some Sequence + public mutating func append, as type: T.Type ) /// Initialize the span's bytes with every byte of the source. @lifetime(self: copy self) - public mutating func append( - fromContentsOf source: Span + public mutating func append, as type: T.Type ) /// Initialize the span's bytes with every byte of the source. @lifetime(self: copy self) public mutating func append( - fromContentsOf source: RawSpan + contentsOf source: RawSpan ) /// Initialize the span's bytes with every byte of the source. @lifetime(self: copy self) public mutating func append( - fromContentsOf source: UnsafeRawBufferPointer + contentsOf source: UnsafeRawBufferPointer ) } ``` @@ -459,7 +456,6 @@ extension OutputRawSpan { ``` - ##### Interoperability with unsafe code We provide a method to process or populate an `OutputRawSpan` using unsafe operations, which can also be used for out-of-order initialization. @@ -498,7 +494,6 @@ extension OutputRawSpan { ``` - ##### Creating `OutputRawSpan` instances Creating an `OutputRawSpan` is an unsafe operation. It requires having knowledge of the initialization state of the range of memory being targeted. The range of memory must be in two regions: the first region contains initialized bytes, and the second region is uninitialized. The number of initialized bytes is passed to the `OutputRawSpan` initializer through its `initializedCount` argument. @@ -579,18 +574,49 @@ extension OutputRawSpan { ``` - #### Extensions to Standard Library types The standard library and Foundation will add a few initializers that enable initialization in place, intermediated by an `OutputSpan` instance, passed as a parameter to a closure: ```swift extension Array { + /// Creates an array with the specified capacity, then calls the given + /// closure with an OutputSpan to initialize the array's contents. public init( capacity: Int, initializingWith initializer: (inout OutputSpan) throws(E) -> Void ) throws(E) - + + /// Grows the array to ensure capacity for the specified number of elements, + /// then calls the closure with an OutputSpan covering the array's + /// uninitialized memory. + public mutating func append( + addingCapacity: Int, + initializingWith initializer: (inout OutputSpan) throws(E) -> Void + ) throws(E) +} + +extension ContiguousArray { + /// Creates an array with the specified capacity, then calls the given + /// closure with an OutputSpan to initialize the array's contents. + public init( + capacity: Int, + initializingWith initializer: (inout OutputSpan) throws(E) -> Void + ) throws(E) + + /// Grows the array to ensure capacity for the specified number of elements, + /// then calls the closure with an OutputSpan covering the array's + /// uninitialized memory. + public mutating func append( + addingCapacity: Int, + initializingWith initializer: (inout OutputSpan) throws(E) -> Void + ) throws(E) +} + +extension ArraySlice { + /// Grows the array to ensure capacity for the specified number of elements, + /// then calls the closure with an OutputSpan covering the array's + /// uninitialized memory. public mutating func append( addingCapacity: Int, initializingWith initializer: (inout OutputSpan) throws(E) -> Void @@ -598,40 +624,84 @@ extension Array { } extension String { + /// Creates a new string with the specified capacity in UTF-8 code units, and + /// then calls the given closure with a OutputSpan to initialize the string's contents. + /// + /// This initializer replaces ill-formed UTF-8 sequences with the Unicode + /// replacement character (`"\u{FFFD}"`). This may require resizing + /// the buffer beyond its original capacity. public init( - utf8Capacity: Int, - initializingUTF8With initializer: (inout OutputSpan) throws(E) -> Void - ) + repairingUTF8WithCapacity capacity: Int, + initializingUTF8With initializer: ( + inout OutputSpan + ) throws(E) -> Void + ) throws(E) + /// Creates a new string with the specified capacity in UTF-8 code units, and + /// then calls the given closure with a OutputSpan to initialize the string's contents. + /// + /// This initializer does not try to repair ill-formed code unit sequences. + /// If any are found, the result of the initializer is `nil`. public init?( - utf8Capacity: Int, - initializingValidUTF8With initializer: (inout OutputSpan) throws(E) -> Void + validatingUTF8WithCapacity capacity: Int, + initializingUTF8With initializer: ( + inout OutputSpan + ) throws(E) -> Void ) throws(E) - + + /// Grows the string to ensure capacity for the specified number + /// of UTF-8 code units, then calls the closure with an OutputSpan covering the string's + /// uninitialized memory. public mutating func append( addingUTF8Capacity: Int, - initializingUTF8With initializer: (inout OUtputSpan) throws(E) -> Void + initializingUTF8With initializer: ( + inout OutputSpan + ) throws(E) -> Void ) throws(E) } extension UnicodeScalarView { + /// Creates a new string with the specified capacity in UTF-8 code units, and + /// then calls the given closure with a OutputSpan to initialize the string's contents. + /// + /// This initializer replaces ill-formed UTF-8 sequences with the Unicode + /// replacement character (`"\u{FFFD}"`). This may require resizing + /// the buffer beyond its original capacity. public init( - utf8Capacity: Int, - initializingUTF8With initializer: (inout OutputSpan) throws(E) -> Void - ) + repairingUTF8WithCapacity capacity: Int, + initializingUTF8With initializer: ( + inout OutputSpan + ) throws(E) -> Void + ) throws(E) + /// Creates a new string with the specified capacity in UTF-8 code units, and + /// then calls the given closure with a OutputSpan to initialize the string's contents. + /// + /// This initializer does not try to repair ill-formed code unit sequences. + /// If any are found, the result of the initializer is `nil`. public init?( - utf8Capacity: Int, - initializingValidUTF8With initializer: (inout OutputSpan) throws(E) -> Void + validatingUTF8WithCapacity capacity: Int, + initializingUTF8With initializer: ( + inout OutputSpan + ) throws(E) -> Void ) throws(E) - + + /// Grows the string to ensure capacity for the specified number + /// of UTF-8 code units, then calls the closure with an OutputSpan covering the string's + /// uninitialized memory. public mutating func append( addingUTF8Capacity: Int, - initializingUTF8With initializer: (inout OUtputSpan) throws(E) -> Void + initializingUTF8With initializer: ( + inout OutputSpan + ) throws(E) -> Void ) throws(E) } extension InlineArray { + /// Creates an array, then calls the given closure with an OutputSpan + /// to initialize the array's elements. + /// + /// NOTE: This initializer will trap if the closure fails to initialize every element. public init( initializingWith initializer: (inout OutputSpan) throws(E) -> Void ) throws(E) @@ -652,9 +722,86 @@ extension Data { rawCapacity: Int, initializingWith initializer: (inout OutputRawSpan) throws(E) -> Void ) throws(E) + + public mutating func append( + addingCapacity: Int, + initializingWith initializer: (inout OutputSpan) throws(E) -> Void + ) throws(E) + + public mutating func append( + addingRawCapacity: Int, + initializingWith initializer: (inout OutputRawSpan) throws(E) -> Void + ) throws(E) } ``` +#### Changes to `MutableSpan` and `MutableRawSpan` + +This proposal considers the naming of `OutputSpan`'s bulk-copying methods more carefully than [SE-0467][SE-0467] about `MutableSpan`. Accordingly, we would like to replace the `update()` functions proposed there in accordance with the scheme proposed in this proposal: + +```swift +extension MutableSpan where Element: Copyable { + public mutating func update( + fromContentsOf source: inout some IteratorProtocol + ) -> Index + + public mutating func update( + fromContentsOf source: some Sequence + ) -> Index + + public mutating func update( + copying source: borrowing Span + ) -> Index + + public mutating func update( + copying source: UnsafeBufferPointer + ) -> Index +} + +extension MutableSpan where Element: ~Copyable { + public mutating func update( + moving source: inout OutputSpan + ) -> Index + + public mutating func update( + consuming source: UnsafeMutableBufferPointer + ) -> Index +} + +extension MutableSpan { + public mutating func append( + consuming source: Slice> + ) +} +``` + +Similarly, `MutableRawSpan`'s bulk-copying methods would be replaced by + +```swift +extension MutableRawSpan { + public mutating func update( + fromContentsOf source: inout some IteratorProtocol, as type: T.Type + ) -> Index + + public mutating func update( + fromContentsOf source: some Sequence, as type: T.Type + ) -> Index + + public mutating func update( + fromContentsOf source: borrowing Span, as type: T.Type + ) -> Index + + public mutating func update( + fromContentsOf source: borrowing RawSpan + ) -> Index + + public mutating func append( + fromContentsOf source: UnsafeRawBufferPointer + ) -> Index +} +``` + + ## Source compatibility This proposal is additive and source-compatible with existing code. @@ -672,8 +819,46 @@ The additions described in this proposal require a new version of the Swift stan #### Vending `OutputSpan` as a property -`OutputSpan` changes the number of initialized elements in a container (or collection), and this requires some operation to update the container after the `OutputSpan` is consumed. Let's call that update operation a "cleanup" operation. The cleanup operation needs to be scheduled in some way. We could associate the cleanup with the `deinit` of `OutputSpan`, or the `deinit` of a wrapper of `OutputSpan`. Neither of these seem appealing; the mechanisms would involve an arbitrary closure executed at `deinit` time, or having to write a full wrapper for each type that vends an `OutputSpan`. We could potentially schedule the cleanup operation as part of a coroutine accessor, but these are not productized yet. The pattern established by closure-taking API is well established, and it fits the needs of `OutputSpan` well. +`OutputSpan` changes the number of initialized elements in a container (or collection), and this requires some operation to update the container after the `OutputSpan` is consumed. Let's call that update operation a "cleanup" operation. The cleanup operation needs to be scheduled in some way. We could associate the cleanup with the `deinit` of `OutputSpan`, or the `deinit` of a wrapper of `OutputSpan`. Neither of these seem appealing; the mechanisms would involve an arbitrary closure executed at `deinit` time, or having to write a full wrapper for each type that vends an `OutputSpan`. We could potentially schedule the cleanup operation as part of a coroutine accessor, but these are not productized yet. The pattern established by closure-taking API is well established, and that pattern fits the needs of `OutputSpan` well. + +#### Alternative names for `append()` methods + +`OutputSpan` lays the groundwork for new, generalized `Container` protocols that will expand upon and succeed the `Collection` hierarchy while allowing non-copyability and non-escapability to be applied to both containers and elements. We hope to find method and property names that will be applicable types The origin of the `append(contentsOf:)` method we are expanding upon is the `RangeReplaceableCollection` protocol, which always represents copyable and escapable collections and copyable and escapable elements. The definition is as follows: `mutating func append(contentsOf newElements: __owned S)`. This support copying elements from the source, while also destroying the source if we happen to own its only copy. This is obviously not sufficient if the elements are non-copyable, or if we only have access to a borrow of the source. + +When the elements are non-copyable, we must append elements that are removed from the source. Afterwards, there are two possible dispositions of the source: destruction (`consuming`,) where the source can no longer be used, or mutation (`inout`,) where the source has been emptied but is still usable. + +When the elements are copyable, we can simply copy the elements from the source. Afterwards, there are two possible dispositions of the source: releasing a borrowed source, or `consuming`. The latter is approximately the same behaviour as `RangeReplaceableCollection`'s `append()` function shown above. + +In a more ideal world, we would like to use the same name for all of these variants: +```swift +extension OutputSpan { + mutating func append(contentsOf: some Sequence) + mutating func append(contentsOf: borrowing some Container) +} +extension OutputSpan where Element: ~Copyable { + mutating func append(contentsOf: consuming some ConsumableContainer) + mutating func append(contentsOf: inout some RangeReplaceableContainer) +} +``` + +However, this would break down in particular for `UnsafeMutableBufferPointer`, since it would make it impossible to differentiate between just copying the elements out of it, or moving its elements out (and deinitializing its memory). Once the `Container` protocols exist, we can expect that the same issue would exist for any type that conforms to more than one of the protocols involved in the list above. For example if a type conforms to `Container` as well as `Sequence`, then there would be an ambiguity. +We could fix this by extending the syntax of the language. It is already possible to overload two functions where they differ only by whether a parameter is `inout`, for example. This is a future direction. + +Instead of the "ideal" solution above, this proposal includes `append()` functions in the following form: + +```swift +extension OutputSpan { + mutating func append(contentsOf: some Sequence) + mutating func append(copying: borrowing some Container) +} +extension OutputSpan where Element: ~Copyable { + mutating func append(consuming: consuming some ConsumableContainer) + mutating func append(moving: inout some RangeReplaceableContainer) +} +``` + +The `update()` methods of `MutableSpan` are updated in a similar manner for the same reasons. ## Future directions @@ -681,6 +866,55 @@ The additions described in this proposal require a new version of the Swift stan Some applications may benefit from the ability to initialize a range of memory in a different order than implemented by `OutputSpan`. This may be from back-to-front or even arbitrary order. There are many possible forms such an initialization helper can take, depending on how much memory safety the application is willing to give up in the process of initializing the memory. At the unsafe end, this can be delegating to an `UnsafeMutableBufferPointer` along with a set of requirements; this option is proposed here. At the safe end, this could be delegating to a data structure which keeps track of initialized memory using a bitmap. It is unclear how much need there is for this more heavy-handed approach, so we leave it as a future enhancement if it is deemed useful. +#### Insertions and indexing + +A use case similar to appending is insertions. Appending is simply inserting at the end. Inserting at positions other than the end is an important capability. It requires an indexing model, which we do not propose here. We expect to add indexing and insertions soon if `OutputSpan` is accepted. Until then, a workaround is to append, then rotate the elements to the desired position using the `mutableSpan` view. The current proposal does not add a concept of indexing to `OutputSpan`, though that is required in order to support insertions at an arbitrary position. We defer this discussion in order to focus on the basic functionality of the type. + +#### Generalized removals + +Similarly to generalized insertions (i.e. not from the end,) we can think about removals of one or more elements starting at a given position. This also requires adding indexing. We expect to add generalized removals along with insertions if `OutputSpan` is accepted. + +#### Variations on `Array.append(addingCapacity:initializingWith:)` + +The function proposed here only exposes uninitialized capacity in the `OutputSpan` parameter to its closure. A different function (perhaps named `edit()`) could also pass the initialized portion of the container, allowing an algorithm to remove _or_ add elements. This could be considered in addition to `append()`. + +#### Method to distinguish between ownership modes for function arguments + +In the "Alternates Considered" subsection about [naming the `append()` methods](#contentsOf), we suggest a currently unachievable naming scheme: +```swift +extension OutputSpan { + mutating func append(contentsOf: some Sequence) + mutating func append(contentsOf: borrowing some Container) +} +extension OutputSpan where Element: ~Copyable { + mutating func append(contentsOf: consuming some ConsumableContainer) + mutating func append(contentsOf: inout some RangeReplaceableContainer) +} +``` + +The language partially support disambiguating this naming scheme, in that we can already distinguish functions over the mutability of a single parameter: + +```swift +func foo(_ a: borrowing A) {} +func foo(_ a: inout A) {} + +var a = A() +foo(a) +foo(&a) +``` + +We could expand upon this ability to disambiguate by using keywords or even new sigils: + +```swift +let buffer: UnsafeMutableBufferPointer = ... +let array = Array(capacity: buffer.count*2) { + (o: inout OutputSpan) in + o.append(contentsOf: borrowing buffer) + o.append(contentsOf: consuming buffer) +} +``` + + ## Acknowledgements Thanks to Karoy Lorentey, Nate Cook and Tony Parker for their feedback. From 74e6ab7e1fe648bae1af38d6eca63f570a36fd9c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 14 May 2025 10:15:05 -0700 Subject: [PATCH 4304/4563] Accept SE-0471: Improved Custom SerialExecutor isolation checking for Concurrency Runtime --- proposals/0471-SerialExecutor-isIsolated.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0471-SerialExecutor-isIsolated.md b/proposals/0471-SerialExecutor-isIsolated.md index e20eecdae0..d5e38f80a8 100644 --- a/proposals/0471-SerialExecutor-isIsolated.md +++ b/proposals/0471-SerialExecutor-isIsolated.md @@ -3,9 +3,9 @@ * Proposal: [SE-0471](0471-SerialExecutor-isIsolated.md) * Author: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review (March 25...April 29, 2025)** +* Status: **Accepted** * Implementation: https://github.com/swiftlang/swift/pull/79788 & https://github.com/swiftlang/swift/pull/79946 -* Review: [Pitch](https://forums.swift.org/t/pitch-serialexecutor-improved-custom-serialexecutor-isolation-checking/78237/), [Review](https://forums.swift.org/t/se-0471-improved-custom-serialexecutor-isolation-checking-for-concurrency-runtime/78834) +* Review: [Pitch](https://forums.swift.org/t/pitch-serialexecutor-improved-custom-serialexecutor-isolation-checking/78237/), [Review](https://forums.swift.org/t/se-0471-improved-custom-serialexecutor-isolation-checking-for-concurrency-runtime/78834), [Acceptance](https://forums.swift.org/t/accepted-se-0471-improved-custom-serialexecutor-isolation-checking-for-concurrency-runtime/79894) ## Introduction From 4517a7aa726030b6c214a83747c825976888cb4f Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 14 May 2025 14:22:53 -0400 Subject: [PATCH 4305/4563] Accept SE-0481 --- proposals/0481-weak-let.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0481-weak-let.md b/proposals/0481-weak-let.md index 60a15f1884..03a4cb4e5b 100644 --- a/proposals/0481-weak-let.md +++ b/proposals/0481-weak-let.md @@ -3,9 +3,9 @@ * Proposal: [SE-0481](0481-weak-let.md) * Authors: [Mykola Pokhylets](https://github.com/nickolas-pohilets) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (April 30th...May 13th, 2025)** +* Status: **Accepted** * Implementation: [swiftlang/swift#80440](https://github.com/swiftlang/swift/pull/80440) -* Review: ([discussion](https://forums.swift.org/t/weak-captures-in-sendable-sending-closures/78498)) ([pitch](https://forums.swift.org/t/pitch-weak-let/79271)) +* Review: ([discussion](https://forums.swift.org/t/weak-captures-in-sendable-sending-closures/78498)) ([pitch](https://forums.swift.org/t/pitch-weak-let/79271)) ([acceptance](https://forums.swift.org/t/accepted-se-0481-weak-let/79895)) [SE-0302]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md From 836e1cbcda549e4228c46adba37cc389e872a45c Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Wed, 14 May 2025 22:00:03 +0200 Subject: [PATCH 4306/4563] [SE-0478] Fix typo (#2847) --- proposals/0478-default-isolation-typealias.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0478-default-isolation-typealias.md b/proposals/0478-default-isolation-typealias.md index 3d29a4932a..590ea5fd7b 100644 --- a/proposals/0478-default-isolation-typealias.md +++ b/proposals/0478-default-isolation-typealias.md @@ -93,7 +93,7 @@ This proposal does not change the adoption implications of adding `@MainActor` t ## Alternatives considered -Adding a typealias named `nonisolated` to `Never` to the Concurrency library to enable writing it as the underlying type of a typealias is pretty strange; this approach leveraging the fact that `nonisolated` is a contextual keyword, so it's valid to use `nonisolated` as an identifier. This proposal uses a typealias instead of an empty struct or enum type to avoid the complications of having a new type be only available with the Swift 6.2 standard library. +Adding a typealias named `nonisolated` to `Never` to the Concurrency library to enable writing it as the underlying type of a typealias is pretty strange; this approach leverages the fact that `nonisolated` is a contextual keyword, so it's valid to use `nonisolated` as an identifier. This proposal uses a typealias instead of an empty struct or enum type to avoid the complications of having a new type be only available with the Swift 6.2 standard library. It's extremely valuable to have a consistent way to spell `nonisolated`. Introducing a type that follows standard naming conventions, such as `Nonisolated`, or using an existing type like `Never` is more consistent with recommended style, but overall complicates the concurrency model because it means you need to spell `nonisolated` differently when specifying it per file versus writing it on a declaration. And because the underlying type of this typealias is used to infer actor isolation, it's not used as a type in the same way that other typealiases are. From 5355577663dfb711f1b02cf48c160ecb2c173e1e Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Wed, 14 May 2025 22:00:51 +0200 Subject: [PATCH 4307/4563] [SE-0466] Remove "prospective" when referring to vision doc (#2846) The proposal refers to the vision for approachable data-race safety as "prospective" in one place, but the vision has been accepted. So remove the "prospective". Also fixes a few typos. --- proposals/0466-control-default-actor-isolation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0466-control-default-actor-isolation.md b/proposals/0466-control-default-actor-isolation.md index 8fb8015003..763a1ff12d 100644 --- a/proposals/0466-control-default-actor-isolation.md +++ b/proposals/0466-control-default-actor-isolation.md @@ -13,7 +13,7 @@ This proposal introduces a new compiler setting for inferring `@MainActor` isola ## Motivation -> Note: This motivation section was adapted from the [prospective vision for approachable data-race safety](https://github.com/hborla/swift-evolution/blob/approachable-concurrency-vision/visions/approachable-concurrency.md#mitigating-false-positive-data-race-safety-errors-in-sequential-code). Please see the vision document for extended motivation. +> Note: This motivation section was adapted from the [vision for approachable data-race safety](https://github.com/hborla/swift-evolution/blob/approachable-concurrency-vision/visions/approachable-concurrency.md#mitigating-false-positive-data-race-safety-errors-in-sequential-code). Please see the vision document for extended motivation. A lot of code is effectively “single-threaded”. For example, most executables, such as apps, command-line tools, and scripts, start running on the main actor and stay there unless some part of the code does something concurrent (like creating a `Task`). If there isn’t any use of concurrency, the entire program will run sequentially, and there’s no risk of data races — every concurrency diagnostic is necessarily a false positive! It would be good to be able to take advantage of that in the language, both to avoid annoying programmers with unnecessary diagnostics and to reinforce progressive disclosure. Many people get into Swift by writing these kinds of programs, and if we can avoid needing to teach them about concurrency straight away, we’ll make the language much more approachable. @@ -156,7 +156,7 @@ There are some build settings that are applicable on a per-file basis, including The `-default-isolation` flag could allow a custom global actor as the argument, and the `SwiftSetting` API could be updated to accept a string that represents a custom global actor in the target. -This proposal only supports `MainActor` because any other global actor does not help with progressive disclosure. It has the opposite effect - it forces asynchrony on any main-actor-isolated caller. However, there's nothing in this proposal prohibits generalizing these settings to supporting arbitrary global actors in the future if a compelling use case arises. +This proposal only supports `MainActor` because any other global actor does not help with progressive disclosure. It has the opposite effect - it forces asynchrony on any main-actor-isolated caller. However, there's nothing in this proposal that prohibits generalizing these settings to supporting arbitrary global actors in the future if a compelling use case arises. ### Infer `MainActor` by default as an upcoming feature @@ -168,7 +168,7 @@ See the approachable data-race safety vision document for an [analysis on the ri ### Don't apply default actor isolation to explicitly `Sendable` types -This proposal includes few excepts where the specified default actor isolation does not apply. An additional case that should be considered is types with a conformance to `Sendable`: +This proposal includes few exceptions where the specified default actor isolation does not apply. An additional case that should be considered is types with a conformance to `Sendable`: ```swift struct SimpleValue: Sendable { From 84c2fca68b0009733702aaa8727a1459faed8a84 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 16 May 2025 11:01:23 -0700 Subject: [PATCH 4308/4563] Add Alternatives Considered section for `swift migrate` vs. `swift package migrate` --- proposals/NNNN-adoption-tooling-for-swift-features.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-adoption-tooling-for-swift-features.md b/proposals/NNNN-adoption-tooling-for-swift-features.md index 3a118f1190..93aed515e5 100644 --- a/proposals/NNNN-adoption-tooling-for-swift-features.md +++ b/proposals/NNNN-adoption-tooling-for-swift-features.md @@ -1,7 +1,7 @@ # Migration tooling for Swift features * Proposal: [SE-NNNN](NNNN-filename.md) -* Authors: [Anthony Latsis](https://github.com/AnthonyLatsis) +* Authors: [Anthony Latsis](https://github.com/AnthonyLatsis), [Pavel Yaskevich](https://github.com/xedin) * Review Manager: TBD * Status: **Awaiting implementation** * Implementation: TBD @@ -344,6 +344,12 @@ The next candidates in line per discussions are ***adopt***, ***audit***, or rules. To illustrate, this mode could appropriately suggest switching from `any P` to `some P` for `ExistentialAny`. + +### `swift migrate` vs. `swift package migrate` + +Rather than have migrate as a subcommand (ie. `swift package migrate`), another option is add another top level command, ie. `swift migrate`. + +As the command applies to the current package, we feel a `swift package` sub-command fits better than a new top-level command. This also aligns with the recently added package refactorings (eg. `add-target`). ## Acknowledgements From bb4248a4144cd92307476a901f20d3e5f5ec9a2c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 16 May 2025 13:48:57 -0700 Subject: [PATCH 4309/4563] SE-0472: Add missing `()` to function type in `Task.immediate{Detached}` declarations --- proposals/0472-task-start-synchronously-on-caller-context.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0472-task-start-synchronously-on-caller-context.md b/proposals/0472-task-start-synchronously-on-caller-context.md index d5a3acd522..c7f0701080 100644 --- a/proposals/0472-task-start-synchronously-on-caller-context.md +++ b/proposals/0472-task-start-synchronously-on-caller-context.md @@ -107,7 +107,7 @@ extension Task { name: String? = nil, // Introduced by SE-0469 priority: TaskPriority? = nil, executorPreference taskExecutor: consuming (any TaskExecutor)? = nil, - @_inheritActorContext(always) operation: sending @escaping async throws(Failure) -> Success + @_inheritActorContext(always) operation: sending @escaping () async throws(Failure) -> Success ) -> Task @discardableResult @@ -115,7 +115,7 @@ extension Task { name: String? = nil, // Introduced by SE-0469 priority: TaskPriority? = nil, executorPreference taskExecutor: consuming (any TaskExecutor)? = nil, - @_inheritActorContext(always) operation: sending @escaping async throws(Failure) -> Success + @_inheritActorContext(always) operation: sending @escaping () async throws(Failure) -> Success ) -> Task } ``` From bd7c340a6e7effca825614da5d0d40dd89be002c Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 16 May 2025 15:56:20 -0700 Subject: [PATCH 4310/4563] More updates and edits for `OutputSpan` --- proposals/nnnn-outputspan.md | 224 ++++++++++++++++++++++++----------- 1 file changed, 155 insertions(+), 69 deletions(-) diff --git a/proposals/nnnn-outputspan.md b/proposals/nnnn-outputspan.md index 0b8fb73d29..7f8ffa6665 100644 --- a/proposals/nnnn-outputspan.md +++ b/proposals/nnnn-outputspan.md @@ -16,6 +16,30 @@ [Forum-LifetimeAnnotations]: https://forums.swift.org/t/78638 [SE-0453]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0453-vector.md + +#### Table of Contents + +- [Introduction](#introduction) + +- [Motivation](#motivation) + +- [Proposed solution](#proposed-solution) + +- [Detailed Design](#detailed-design) + +- [Source compatibility](#source-compatibility) + +- [ABI compatibility](#abi-compatibility) + +- [Implications on adoption](#implications-on-adoption) + +- [Alternatives Considered](#alternatives-considered) + +- [Future directions](#future-directions) + +- [Acknowledgements](#acknowledgements) + + ## Introduction Following the introduction of [`Span`][SE-0447] and [`MutableSpan`][SE-0467], this proposal adds a general facility for initialization of exclusively-borrowed memory with the `OutputSpan` and `OutputRawSpan` types. The memory represented by `OutputSpan` consists of a number of initialized elements, followed by uninitialized memory. The operations of `OutputSpan` can change the number of initialized elements in memory, unlike `MutableSpan` which always represent initialized memory representing a fixed number of elements. @@ -102,6 +126,10 @@ extension OutputSpan where Element: ~Copyable { /// Append a single element to this `OutputSpan`. @lifetime(self: copy self) public mutating func append(_ value: consuming Element) + + /// Repeatedly append an element to this `OutputSpan`. + @lifetime(self: copy self) + public mutating func append(repeating repeatedValue: Element, count: Int) } ``` The converse operation `removeLast()` is also supported, and returns the removed element if `count` was greater than zero. @@ -109,39 +137,47 @@ The converse operation `removeLast()` is also supported, and returns the removed extension OutputSpan where Element: ~Copyable { /// Remove the last initialized element from this `OutputSpan`. /// - /// Returns the last element. The `OutputSpan` must not be empty + /// Returns the last element. The `OutputSpan` must not be empty. @discardableResult @lifetime(self: copy self) public mutating func removeLast() -> Element } ``` +`OutputSpan` provides the ability to access its initialized elements by index: +```swift +extension OutputSpan wehre Element: ~Copyable { + /// The type that represents an initialized position in an `OutputSpan`. + typealias Index = Int + + /// The range of initialized positions for this `OutputSpan`. + var indices: Range { get } + + /// Accesses the element at the specified initialized position. + subscript(_ index: Index) -> Element { borrow; mutate } + // accessor syntax from accessors roadmap (https://forums.swift.org/t/76707) + + /// Exchange the elements at the two given offsets + mutating func swapAt(_ i: Index, _ j: Index) +} +``` + + ##### Bulk initialization of an `OutputSpan`'s memory: -We include functions to perform bulk initialization of the memory represented by an `OutputSpan`. Initializing an `OutputSpan` from a `Sequence` or a fixed-size source must use every element of the source. Initializing an `OutputSpan` from `IteratorProtocol` will copy as many items as possible, either until the input is empty or the `OutputSpan`'s available storage is zero. +We include functions to perform bulk initialization of the memory represented by an `OutputSpan`. Initializing an `OutputSpan` from a `Sequence` must use every element of the source. Initializing an `OutputSpan` from `IteratorProtocol` will copy as many items as possible, either until the input is empty or the `OutputSpan`'s available storage is zero. + +See [below](#contentsOf) for a discussion of the argument labels used with the `append()` functions. ```swift extension OutputSpan { - /// Initialize this span's suffix to the repetitions of the given value. - @lifetime(self: copy self) - public mutating func append(repeating repeatedValue: Element, count: Int) - - /// Initialize this span's suffix with the elements from the source. - /// - /// Returns true if the iterator has filled all the free capacity in the span. - @discardableResult - @lifetime(self: copy self) - public mutating func append( - contentsOf source: inout some IteratorProtocol - ) -> Bool - /// Initialize this span's suffix with every element of the source. /// /// It is a precondition that the `OutputSpan`'s uninitialized suffix /// can contain every element of the source. @lifetime(self: copy self) public mutating func append( - contentsOf source: some Sequence + contentsOf source: consuming some Sequence ) /// Initialize this span's suffix with every element of the source. @@ -159,7 +195,7 @@ extension OutputSpan { /// can contain every element of the source. @lifetime(self: copy self) public mutating func append( - copying source: UnsafeBufferPointer + contentsOf source: UnsafeBufferPointer ) } @@ -173,6 +209,15 @@ extension OutputSpan where Element: ~Copyable { moving source: inout OutputSpan ) + /// Initialize this span's suffix by moving every element from the source. + /// + /// It is a precondition that the `OutputSpan`'s uninitialized suffix + /// can contain every element of the source. + @lifetime(self: copy self) + public mutating func append( + consuming source: consuming InlineArray + ) + /// Initialize this span's suffix by moving every element from the source. /// /// It is a precondition that the `OutputSpan`'s uninitialized suffix @@ -192,6 +237,21 @@ extension OutputSpan { public mutating func append( consuming source: Slice> ) + + /// Initialize this span's suffix with the elements from the source. + /// + /// Use this function if it isn't possible to fulfill the precondition + /// of the other `append()` functions, and to contain every element + /// of the source. + /// + /// Returns true if the iterator has filled all the free capacity in the span. + /// When the free capacity has been filled, there may be some elements + /// remaining in the iterator. + @discardableResult + @lifetime(self: copy self) + public mutating func append( + from source: inout some IteratorProtocol + ) -> Bool } ``` @@ -321,15 +381,15 @@ extension OutputSpan { ##### Retrieving initialized memory from an `OutputSpan` -Once memory has been initialized using `OutputSpan`, the owner of the memory must consume the `OutputSpan` in order to retake ownership of the initialized memory. The owning type must pass the memory used to initialize the `OutputSpan` to the `finalize(for:)` function. Passing the wrong buffer is a programmer error and the function traps. `finalize(for:)` consumes the `OutputSpan` instance and returns the number of initialized elements. If `finalize(for:)` is not called, the initialized portion of `OutputSpan`'s memory will be deinitialized when the binding goes out of scope. +Once memory has been initialized using `OutputSpan`, the owner of the memory must consume the `OutputSpan` in order to retake ownership of the initialized memory. The owning type must pass the memory used to initialize the `OutputSpan` to the `finalize(for:)` function. Passing the wrong buffer is a programmer error and the function traps; this requirement also ensures that user code does not wrongly replace the `OutputSpan` with an unrelated instance. The `finalize(for:)` function consumes the `OutputSpan` instance and returns the number of initialized elements. If `finalize(for:)` is not called, the initialized portion of `OutputSpan`'s memory will be deinitialized when the binding goes out of scope. ```swift extension OutputSpan where Element: ~Copyable { /// Consume the OutputSpan and return the number of initialized elements. - /// + /// /// Parameters: - /// - buffer: The buffer being finalized. This must be the same buffer as used to - /// initialize the `OutputSpan` instance. + /// - buffer: The buffer being finalized. This must be the same buffer as used + /// to initialize the `OutputSpan` instance. /// Returns: The number of elements that were initialized. @unsafe public consuming func finalize( @@ -339,10 +399,10 @@ extension OutputSpan where Element: ~Copyable { extension OutputSpan { /// Consume the OutputSpan and return the number of initialized elements. - /// + /// /// Parameters: - /// - buffer: The buffer being finalized. This must be the same buffer as used to - /// initialize the `OutputSpan` instance. + /// - buffer: The buffer being finalized. This must be the same buffer as used + /// to initialize the `OutputSpan` instance. /// Returns: The number of bytes that were initialized. @unsafe public consuming func finalize( @@ -385,7 +445,7 @@ extension OutputRawSpan { ##### Appending to `OutputRawSpan` -The basic operation is to append the bytes of some value to an `OutputRawSpan`: +The basic operation is to append the bytes of some value to an `OutputRawSpan`. Note that since the fundamental operation is appending bytes, `OutputRawSpan` does not concern itself with memory alignment. ```swift extension OutputRawSpan { /// Appends the given value's bytes to this span's initialized bytes @@ -393,30 +453,23 @@ extension OutputRawSpan { public mutating func append( of value: T, as type: T.Type ) -} -``` -This is also supported with bulk operations. Initializing an `OutputRawSpan` from known-sized sources (such as `Collection` or `Span`) uses every element of the source. It is an error to do so when the available storage of the `OutputRawSpan` is too little to contain every element from the source. Initializing an `OutputRawSpan` from `Sequence` or `IteratorProtocol` will copy as many items as possible, either until the input is empty or the `OutputRawSpan` has too few bytes available to store another element. -```swift -extension OutputRawSpan - /// Initialize this span's suffix to the repetitions of the given value's bytes. + /// Appends the given value's bytes repeatedly to this span's initialized bytes @lifetime(self: copy self) public mutating func append( repeating repeatedValue: T, count: Int, as type: T.Type ) +} +``` - /// Initialize the span's bytes with the bytes of the elements of the source. - /// - /// Returns true if the iterator has filled all the free capacity in the span. - @lifetime(self: copy self) - public mutating func append, as type: T.Type - ) -> Bool +Initializing an `OutputRawSpan` from a `Sequence` or other container type must use every element of the source. Initializing an `OutputRawSpan` from an `IteratorProtocol` instance will copy as many items as possible, either until the input is empty or the `OutputRawSpan` cannot contain another element. +```swift +extension OutputRawSpan { /// Initialize the span's bytes with every byte of the source. @lifetime(self: copy self) public mutating func append, as type: T.Type + contentsOf source: consuming some Sequence, as type: T.Type ) /// Initialize the span's bytes with every byte of the source. @@ -436,6 +489,20 @@ extension OutputRawSpan public mutating func append( contentsOf source: UnsafeRawBufferPointer ) + + /// Initialize the span's bytes with the bytes of the elements of the source. + /// + /// Use this function if it isn't possible to fulfill the precondition + /// of the other `append()` functions, and to contain every element + /// of the source. + /// + /// Returns true if the iterator has filled all the free capacity in the span. + /// When the free capacity has been filled, there may be some elements + /// remaining in the iterator. + @lifetime(self: copy self) + public mutating func append, as type: T.Type + ) -> Bool } ``` @@ -550,10 +617,10 @@ Once memory has been initialized using `OutputRawSpan`, the owner of the memory ```swift extension OutputRawSpan { /// Consume the OutputRawSpan and return the number of initialized bytes. - /// + /// /// Parameters: - /// - buffer: The buffer being finalized. This must be the same buffer as used to - /// create the `OutputRawSpan` instance. + /// - buffer: The buffer being finalized. This must be the same buffer as used + /// to create the `OutputRawSpan` instance. /// Returns: The number of initialized bytes. @unsafe public consuming func finalize( @@ -561,10 +628,10 @@ extension OutputRawSpan { ) -> Int /// Consume the OutputRawSpan and return the number of initialized bytes. - /// + /// /// Parameters: - /// - buffer: The buffer being finalized. This must be the same buffer as used to - /// create the `OutputRawSpan` instance. + /// - buffer: The buffer being finalized. This must be the same buffer as used + /// to create the `OutputRawSpan` instance. /// Returns: The number of initialized bytes. @unsafe public consuming func finalize( @@ -625,8 +692,9 @@ extension ArraySlice { extension String { /// Creates a new string with the specified capacity in UTF-8 code units, and - /// then calls the given closure with a OutputSpan to initialize the string's contents. - /// + /// then calls the given closure with a OutputSpan to initialize the string's + /// contents. + /// /// This initializer replaces ill-formed UTF-8 sequences with the Unicode /// replacement character (`"\u{FFFD}"`). This may require resizing /// the buffer beyond its original capacity. @@ -638,8 +706,9 @@ extension String { ) throws(E) /// Creates a new string with the specified capacity in UTF-8 code units, and - /// then calls the given closure with a OutputSpan to initialize the string's contents. - /// + /// then calls the given closure with a OutputSpan to initialize the string's + /// contents. + /// /// This initializer does not try to repair ill-formed code unit sequences. /// If any are found, the result of the initializer is `nil`. public init?( @@ -650,8 +719,8 @@ extension String { ) throws(E) /// Grows the string to ensure capacity for the specified number - /// of UTF-8 code units, then calls the closure with an OutputSpan covering the string's - /// uninitialized memory. + /// of UTF-8 code units, then calls the closure with an OutputSpan covering + /// the string's uninitialized memory. public mutating func append( addingUTF8Capacity: Int, initializingUTF8With initializer: ( @@ -662,8 +731,9 @@ extension String { extension UnicodeScalarView { /// Creates a new string with the specified capacity in UTF-8 code units, and - /// then calls the given closure with a OutputSpan to initialize the string's contents. - /// + /// then calls the given closure with a OutputSpan to initialize + /// the string's contents. + /// /// This initializer replaces ill-formed UTF-8 sequences with the Unicode /// replacement character (`"\u{FFFD}"`). This may require resizing /// the buffer beyond its original capacity. @@ -675,8 +745,9 @@ extension UnicodeScalarView { ) throws(E) /// Creates a new string with the specified capacity in UTF-8 code units, and - /// then calls the given closure with a OutputSpan to initialize the string's contents. - /// + /// then calls the given closure with a OutputSpan to initialize + /// the string's contents. + /// /// This initializer does not try to repair ill-formed code unit sequences. /// If any are found, the result of the initializer is `nil`. public init?( @@ -687,8 +758,8 @@ extension UnicodeScalarView { ) throws(E) /// Grows the string to ensure capacity for the specified number - /// of UTF-8 code units, then calls the closure with an OutputSpan covering the string's - /// uninitialized memory. + /// of UTF-8 code units, then calls the closure with an OutputSpan covering + /// the string's uninitialized memory. public mutating func append( addingUTF8Capacity: Int, initializingUTF8With initializer: ( @@ -701,7 +772,8 @@ extension InlineArray { /// Creates an array, then calls the given closure with an OutputSpan /// to initialize the array's elements. /// - /// NOTE: This initializer will trap if the closure fails to initialize every element. + /// NOTE: The closure must initialize every element of the `OutputSpan`. + /// If the closure does not do so, the initializer will trap. public init( initializingWith initializer: (inout OutputSpan) throws(E) -> Void ) throws(E) @@ -713,21 +785,33 @@ extension InlineArray { While the `swift-foundation` package and the `Foundation` framework are not governed by the Swift evolution process, `Data` is similar in use to standard library types, and the project acknowledges that it is desirable for it to have similar API when appropriate. Accordingly, we plan to propose the following additions to `Foundation.Data`: ```swift extension Data { + /// Creates a data instance with the specified capacity, then calls + /// the given closure with an OutputSpan to initialize the instances's + /// contents. public init( capacity: Int, initializingWith initializer: (inout OutputSpan) throws(E) -> Void ) throws(E) + /// Creates a data instance with the specified capacity, then calls + /// the given closure with an OutputSpan to initialize the instances's + /// contents. public init( rawCapacity: Int, initializingWith initializer: (inout OutputRawSpan) throws(E) -> Void ) throws(E) + /// Ensures the data instance has enough capacity for the specified + /// number of bytes, then calls the closure with an OutputSpan covering + /// the uninitialized memory. public mutating func append( addingCapacity: Int, initializingWith initializer: (inout OutputSpan) throws(E) -> Void ) throws(E) + /// Ensures the data instance has enough capacity for the specified + /// number of bytes, then calls the closure with an OutputSpan covering + /// the uninitialized memory. public mutating func append( addingRawCapacity: Int, initializingWith initializer: (inout OutputRawSpan) throws(E) -> Void @@ -742,7 +826,7 @@ This proposal considers the naming of `OutputSpan`'s bulk-copying methods more c ```swift extension MutableSpan where Element: Copyable { public mutating func update( - fromContentsOf source: inout some IteratorProtocol + from source: inout some IteratorProtocol ) -> Index public mutating func update( @@ -762,7 +846,7 @@ extension MutableSpan where Element: ~Copyable { public mutating func update( moving source: inout OutputSpan ) -> Index - + public mutating func update( consuming source: UnsafeMutableBufferPointer ) -> Index @@ -775,12 +859,12 @@ extension MutableSpan { } ``` -Similarly, `MutableRawSpan`'s bulk-copying methods would be replaced by +Similarly, `MutableRawSpan`'s bulk-copying methods would be replaced by the following: ```swift extension MutableRawSpan { public mutating func update( - fromContentsOf source: inout some IteratorProtocol, as type: T.Type + from source: inout some IteratorProtocol, as type: T.Type ) -> Index public mutating func update( @@ -832,7 +916,7 @@ When the elements are copyable, we can simply copy the elements from the source. In a more ideal world, we would like to use the same name for all of these variants: ```swift extension OutputSpan { - mutating func append(contentsOf: some Sequence) + mutating func append(contentsOf: consuming some Sequence) mutating func append(contentsOf: borrowing some Container) } extension OutputSpan where Element: ~Copyable { @@ -849,7 +933,7 @@ Instead of the "ideal" solution above, this proposal includes `append()` functio ```swift extension OutputSpan { - mutating func append(contentsOf: some Sequence) + mutating func append(contentsOf: consuming some Sequence) mutating func append(copying: borrowing some Container) } extension OutputSpan where Element: ~Copyable { @@ -858,7 +942,9 @@ extension OutputSpan where Element: ~Copyable { } ``` -The `update()` methods of `MutableSpan` are updated in a similar manner for the same reasons. +The `update()` methods of `MutableSpan` are updated in a similar manner, for the same reasons. + +We note that the four variants of `append()` are required for generalized containers. We can therefore expect that the names we choose will later appear on many types of collections that interact with the future `Container` protocols. This proposal's `append()` nomenclature could become ubiquitous when interacting with `Collection` instances, once it is applied to the `Container` protocol family. ## Future directions @@ -876,14 +962,14 @@ Similarly to generalized insertions (i.e. not from the end,) we can think about #### Variations on `Array.append(addingCapacity:initializingWith:)` -The function proposed here only exposes uninitialized capacity in the `OutputSpan` parameter to its closure. A different function (perhaps named `edit()`) could also pass the initialized portion of the container, allowing an algorithm to remove _or_ add elements. This could be considered in addition to `append()`. +The function proposed here only exposes uninitialized capacity in the `OutputSpan` parameter to its closure. A different function (perhaps named `edit()`) could also pass the initialized portion of the container, allowing an algorithm to remove or to add elements. This could be considered in addition to `append()`. -#### Method to distinguish between ownership modes for function arguments +#### Language syntax to distinguish between ownership modes for function arguments In the "Alternates Considered" subsection about [naming the `append()` methods](#contentsOf), we suggest a currently unachievable naming scheme: ```swift extension OutputSpan { - mutating func append(contentsOf: some Sequence) + mutating func append(contentsOf: consuming some Sequence) mutating func append(contentsOf: borrowing some Container) } extension OutputSpan where Element: ~Copyable { @@ -892,7 +978,7 @@ extension OutputSpan where Element: ~Copyable { } ``` -The language partially support disambiguating this naming scheme, in that we can already distinguish functions over the mutability of a single parameter: +The language partially supports disambiguating this naming scheme, in that we can already distinguish functions over the mutability of a single parameter: ```swift func foo(_ a: borrowing A) {} From 5585bc6269b07177b31eb0f52d09c3c3076916d9 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 16 May 2025 16:50:34 -0700 Subject: [PATCH 4311/4563] Move a section --- proposals/nnnn-outputspan.md | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/proposals/nnnn-outputspan.md b/proposals/nnnn-outputspan.md index 7f8ffa6665..e829fbf6af 100644 --- a/proposals/nnnn-outputspan.md +++ b/proposals/nnnn-outputspan.md @@ -25,7 +25,7 @@ - [Proposed solution](#proposed-solution) -- [Detailed Design](#detailed-design) +- [Detailed Design](#design) - [Source compatibility](#source-compatibility) @@ -144,24 +144,6 @@ extension OutputSpan where Element: ~Copyable { } ``` -`OutputSpan` provides the ability to access its initialized elements by index: -```swift -extension OutputSpan wehre Element: ~Copyable { - /// The type that represents an initialized position in an `OutputSpan`. - typealias Index = Int - - /// The range of initialized positions for this `OutputSpan`. - var indices: Range { get } - - /// Accesses the element at the specified initialized position. - subscript(_ index: Index) -> Element { borrow; mutate } - // accessor syntax from accessors roadmap (https://forums.swift.org/t/76707) - - /// Exchange the elements at the two given offsets - mutating func swapAt(_ i: Index, _ j: Index) -} -``` - ##### Bulk initialization of an `OutputSpan`'s memory: @@ -289,6 +271,24 @@ extension OutputSpan where Element: ~Copyable { } ``` +`OutputSpan` also provides the ability to access its individual initialized elements by index: +```swift +extension OutputSpan wehre Element: ~Copyable { + /// The type that represents an initialized position in an `OutputSpan`. + typealias Index = Int + + /// The range of initialized positions for this `OutputSpan`. + var indices: Range { get } + + /// Accesses the element at the specified initialized position. + subscript(_ index: Index) -> Element { borrow; mutate } + // accessor syntax from accessors roadmap (https://forums.swift.org/t/76707) + + /// Exchange the elements at the two given offsets + mutating func swapAt(_ i: Index, _ j: Index) +} +``` + ##### Interoperability with unsafe code From e5fa401a583ea4e2874fd2689063109f04e56733 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 16 May 2025 17:53:30 -0700 Subject: [PATCH 4312/4563] A few more updates, and omissions remedied --- proposals/nnnn-outputspan.md | 49 ++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/proposals/nnnn-outputspan.md b/proposals/nnnn-outputspan.md index e829fbf6af..1a56f51f51 100644 --- a/proposals/nnnn-outputspan.md +++ b/proposals/nnnn-outputspan.md @@ -273,7 +273,7 @@ extension OutputSpan where Element: ~Copyable { `OutputSpan` also provides the ability to access its individual initialized elements by index: ```swift -extension OutputSpan wehre Element: ~Copyable { +extension OutputSpan where Element: ~Copyable { /// The type that represents an initialized position in an `OutputSpan`. typealias Index = Int @@ -448,6 +448,10 @@ extension OutputRawSpan { The basic operation is to append the bytes of some value to an `OutputRawSpan`. Note that since the fundamental operation is appending bytes, `OutputRawSpan` does not concern itself with memory alignment. ```swift extension OutputRawSpan { + /// Append a single byte to this span + @lifetime(self: copy self) + public mutating func append(_ value: UInt8) + /// Appends the given value's bytes to this span's initialized bytes @lifetime(self: copy self) public mutating func append( @@ -522,6 +526,29 @@ extension OutputRawSpan { } ``` +Methods to deinitialize memory from an `OutputRawSpan`: + +```swift +extension OutputRawSpan { + + /// Remove the last byte from this span + @lifetime(self: copy self) + public mutating func removeLast() -> UInt8 { + + /// Remove the last N elements, returning the memory they occupy + /// to the uninitialized state. + /// + /// `n` must not be greater than `count` + @lifetime(self: copy self) + public mutating func removeLast(_ n: Int) + + /// Remove all this span's elements and return its memory + /// to the uninitialized state. + @lifetime(self: copy self) + public mutating func removeAll() +} +``` + ##### Interoperability with unsafe code @@ -825,10 +852,6 @@ This proposal considers the naming of `OutputSpan`'s bulk-copying methods more c ```swift extension MutableSpan where Element: Copyable { - public mutating func update( - from source: inout some IteratorProtocol - ) -> Index - public mutating func update( fromContentsOf source: some Sequence ) -> Index @@ -856,6 +879,10 @@ extension MutableSpan { public mutating func append( consuming source: Slice> ) + + public mutating func update( + from source: inout some IteratorProtocol + ) -> Index } ``` @@ -863,10 +890,6 @@ Similarly, `MutableRawSpan`'s bulk-copying methods would be replaced by the foll ```swift extension MutableRawSpan { - public mutating func update( - from source: inout some IteratorProtocol, as type: T.Type - ) -> Index - public mutating func update( fromContentsOf source: some Sequence, as type: T.Type ) -> Index @@ -882,6 +905,10 @@ extension MutableRawSpan { public mutating func append( fromContentsOf source: UnsafeRawBufferPointer ) -> Index + + public mutating func update( + from source: inout some IteratorProtocol, as type: T.Type + ) -> Index } ``` @@ -995,8 +1022,8 @@ We could expand upon this ability to disambiguate by using keywords or even new let buffer: UnsafeMutableBufferPointer = ... let array = Array(capacity: buffer.count*2) { (o: inout OutputSpan) in - o.append(contentsOf: borrowing buffer) - o.append(contentsOf: consuming buffer) + o.append(contentsOf: borrow buffer) + o.append(contentsOf: consume buffer) } ``` From d693d3f3248769a81a921a7987d6399af2f851d1 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Fri, 16 May 2025 18:08:46 -0700 Subject: [PATCH 4313/4563] Even more edits --- proposals/nnnn-outputspan.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/nnnn-outputspan.md b/proposals/nnnn-outputspan.md index 1a56f51f51..6412770deb 100644 --- a/proposals/nnnn-outputspan.md +++ b/proposals/nnnn-outputspan.md @@ -934,7 +934,7 @@ The additions described in this proposal require a new version of the Swift stan #### Alternative names for `append()` methods -`OutputSpan` lays the groundwork for new, generalized `Container` protocols that will expand upon and succeed the `Collection` hierarchy while allowing non-copyability and non-escapability to be applied to both containers and elements. We hope to find method and property names that will be applicable types The origin of the `append(contentsOf:)` method we are expanding upon is the `RangeReplaceableCollection` protocol, which always represents copyable and escapable collections and copyable and escapable elements. The definition is as follows: `mutating func append(contentsOf newElements: __owned S)`. This support copying elements from the source, while also destroying the source if we happen to own its only copy. This is obviously not sufficient if the elements are non-copyable, or if we only have access to a borrow of the source. +`OutputSpan` lays the groundwork for new, generalized `Container` protocols that will expand upon and succeed the `Collection` hierarchy while allowing non-copyability and non-escapability to be applied to both containers and elements. We hope to find method and property names that will be generally applicable. The origin of the `append(contentsOf:)` method we are expanding upon is the `RangeReplaceableCollection` protocol, which always represents copyable and escapable collections with copyable and escapable elements. The definition is as follows: `mutating func append(contentsOf newElements: __owned S)`. This supports copying elements from the source, while also destroying the source if we happen to hold its only copy. This is obviously not sufficient if the elements are non-copyable, or if we only have access to a borrow of the source. When the elements are non-copyable, we must append elements that are removed from the source. Afterwards, there are two possible dispositions of the source: destruction (`consuming`,) where the source can no longer be used, or mutation (`inout`,) where the source has been emptied but is still usable. @@ -979,9 +979,9 @@ We note that the four variants of `append()` are required for generalized contai Some applications may benefit from the ability to initialize a range of memory in a different order than implemented by `OutputSpan`. This may be from back-to-front or even arbitrary order. There are many possible forms such an initialization helper can take, depending on how much memory safety the application is willing to give up in the process of initializing the memory. At the unsafe end, this can be delegating to an `UnsafeMutableBufferPointer` along with a set of requirements; this option is proposed here. At the safe end, this could be delegating to a data structure which keeps track of initialized memory using a bitmap. It is unclear how much need there is for this more heavy-handed approach, so we leave it as a future enhancement if it is deemed useful. -#### Insertions and indexing +#### Insertions -A use case similar to appending is insertions. Appending is simply inserting at the end. Inserting at positions other than the end is an important capability. It requires an indexing model, which we do not propose here. We expect to add indexing and insertions soon if `OutputSpan` is accepted. Until then, a workaround is to append, then rotate the elements to the desired position using the `mutableSpan` view. The current proposal does not add a concept of indexing to `OutputSpan`, though that is required in order to support insertions at an arbitrary position. We defer this discussion in order to focus on the basic functionality of the type. +A use case similar to appending is insertions. Appending is simply inserting at the end. Inserting at positions other than the end is an important capability. We expect to add insertions soon if `OutputSpan` is accepted. Until then, a workaround is to append, then rotate the elements to the desired position using the `mutableSpan` view. The current proposal does not add a concept of indexing to `OutputSpan`, though that is required in order to support insertions at an arbitrary position. We defer this discussion in order to focus on the basic functionality of the type. #### Generalized removals @@ -993,7 +993,7 @@ The function proposed here only exposes uninitialized capacity in the `OutputSpa #### Language syntax to distinguish between ownership modes for function arguments -In the "Alternates Considered" subsection about [naming the `append()` methods](#contentsOf), we suggest a currently unachievable naming scheme: +In the "Alternatives Considered" subsection about [naming the `append()` methods](#contentsOf), we suggest a currently unachievable naming scheme: ```swift extension OutputSpan { mutating func append(contentsOf: consuming some Sequence) From 9f94d22a9254231742f4f7c51b59cc9f1f483d06 Mon Sep 17 00:00:00 2001 From: Daniel Grumberg Date: Mon, 19 May 2025 14:22:21 +0100 Subject: [PATCH 4314/4563] Mention limitations on artifacts in introduction --- ...tpm-static-library-binary-target-non-apple-platforms.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md index 6991b6f942..ab1ec702f7 100644 --- a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md +++ b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -10,11 +10,10 @@ ## Introduction -Swift continues to grow as a cross-platform language supporting a wide variety of use cases from [programming embedded device](https://www.swift.org/blog/embedded-swift-examples/) to [server-side development](https://www.swift.org/documentation/server/) across a multitude of [operating systems](https://www.swift.org/documentation/articles/static-linux-getting-started.html). +Swift continues to grow as a cross-platform language supporting a wide variety of use cases from [programming embedded device](https://www.swift.org/blog/embedded-swift-examples/) to [server-side development](https://www.swift.org/documentation/server/) across a multitude of [operating systems](https://www.swift.org/documentation/articles/static-linux-getting-started.html). However, currently SwiftPM supports linking against binary dependencies on Apple platforms only. -This proposal aims to make it possible to provide static library dependencies exposing a C interface on non-Apple platforms. - -Swift-evolution thread: +This proposal aims to make it possible to provide static library dependencies exposing a C interface on non-Apple platforms that depend only the standard C library. +Unfortunately, this proposal doesn't provide support for distributing Swift libraries built in this way due to the Swift ABI not being stable on all platforms. ## Motivation From 40c804ed669b726160b6bfef740d9328a855099c Mon Sep 17 00:00:00 2001 From: Daniel Grumberg Date: Mon, 19 May 2025 14:27:11 +0100 Subject: [PATCH 4315/4563] Update json schema for artifact bundle --- ...brary-binary-target-non-apple-platforms.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md index ab1ec702f7..bc47042fbb 100644 --- a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md +++ b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -61,9 +61,11 @@ The artifact manifest JSON format for a static library is described below: "variants": [ { "path": "", - "headerPaths": [", ...], - "moduleMapPath": "", "supportedTriples": ["", ... ], + "staticLibraryMetadata": { + "headerPaths": [", ...], + "moduleMapPath": "" + } }, ... ] @@ -71,6 +73,7 @@ The artifact manifest JSON format for a static library is described below: ... } } + ``` The additions are: @@ -85,18 +88,22 @@ As with executable binary artifacts, the `path` field represents the relative pa and the `supportedTriples` field provides information about the target triples supported by this variant. An example artifact might look like: + ```json { "schemaVersion": "1.0", "artifacts": { - "my-artifact": { + "example": { "type": "staticLibrary", "version": "1.0.0", "variants": [ { - "path": "artifact.a", - "headerPaths": ["include"], - "supportedTriples": ["aarch64-unknown-linux-gnu"] + "path": "libExample.a", + "supportedTriples": ["aarch64-unknown-linux-gnu"], + "staticLibraryMetadata": { + "headerPaths": ["include"], + "moduleMapPath": "include/example.modulemap" + } } ] } From 3fbde18ad0690e55a67b9416c65a91fba038703f Mon Sep 17 00:00:00 2001 From: Daniel Grumberg Date: Mon, 19 May 2025 14:31:06 +0100 Subject: [PATCH 4316/4563] Call out requirement to provide a modulemap for Swift imports --- ...wiftpm-static-library-binary-target-non-apple-platforms.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md index bc47042fbb..bae28fb457 100644 --- a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md +++ b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -81,8 +81,8 @@ The additions are: * The `staticLibrary` artifact `type` that indicates this binary artifact is not an executable but rather a static library to link against. * The `headerPaths` field specifies directory paths relative to the root of the artifact bundle that contain the header interfaces to the static library. These are forwarded along to the swift compiler (or the C compiler) using the usual search path arguments. - Each of these directories can optionally contain a `module.modulemap` file that will be used for importing the API into Swift code. -* The optional `moduleMapPath` field specifies a custom module map to use if the header paths do not contain the module definitions or to provide custom overrides. +* The optional `moduleMapPath` field specifies the path relative to the root of the artifact bundle that contains a custom module map to use if the header paths do not contain the module definitions or to provide custom overrides. + This field is required if the library's API is to be imported into Swift code. As with executable binary artifacts, the `path` field represents the relative path to the binary from the root of the artifact bundle, and the `supportedTriples` field provides information about the target triples supported by this variant. From d55587e70cd33b73460d7bed0f5703697453ee8f Mon Sep 17 00:00:00 2001 From: Daniel Grumberg Date: Mon, 19 May 2025 15:00:16 +0100 Subject: [PATCH 4317/4563] Call out options for distributing artifacts with known dependencies --- ...-swiftpm-static-library-binary-target-non-apple-platforms.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md index bae28fb457..b45c555874 100644 --- a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md +++ b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -42,6 +42,8 @@ The artifact manifest would encode the following information for each variant: Additionnaly, we propose the addition of an auditing tool that can validate the library artifact is safe to use across the Linux-based platforms supported by the Swift project. Such a tool would ensure that people do not accidentally distribute artifacts that require dependencies that are not met on the various deployment platforms. +However when an artifact isn't widely consumed and all dependent packages are known, +artifact vendors can provide artifacts with dependencies on other C libraries provided that each client target depends explicitly on all required dependencies of the artifact. ## Detailed design From 51edf3a6ba888cc00a5411569eae5bd72403f9a1 Mon Sep 17 00:00:00 2001 From: Daniel Grumberg Date: Mon, 19 May 2025 15:01:24 +0100 Subject: [PATCH 4318/4563] Fix typos --- ...wiftpm-static-library-binary-target-non-apple-platforms.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md index b45c555874..1557019a3b 100644 --- a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md +++ b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -40,7 +40,7 @@ The artifact manifest would encode the following information for each variant: * Enough information to be able to use the library's API in the packages source code, i.e., headers and module maps for libraries exporting a C-based interface. -Additionnaly, we propose the addition of an auditing tool that can validate the library artifact is safe to use across the Linux-based platforms supported by the Swift project. +Additionally, we propose the addition of an auditing tool that can validate the library artifact is safe to use across the Linux-based platforms supported by the Swift project. Such a tool would ensure that people do not accidentally distribute artifacts that require dependencies that are not met on the various deployment platforms. However when an artifact isn't widely consumed and all dependent packages are known, artifact vendors can provide artifacts with dependencies on other C libraries provided that each client target depends explicitly on all required dependencies of the artifact. @@ -131,7 +131,7 @@ The tool would then check that the referenced symbols list is a subset of the se This would be sufficient to guarantee that all symbols from the static library would be available at runtime for statically linked executables or for ones running on the build host. To ensure maximum runtime compatibility we would also provide a Linux-based Docker image that uses the oldest supported `glibc` for a given Swift version. As `glibc` is backwards compatible, a container running the audit on a given static library would ensure that the version of `glibc` on any runtime platform would be compatible with the binary artifact. -This strategy as been succesfully employed in the Python community with [`manylinux`](https://peps.python.org/pep-0513/). +This strategy as been successfully employed in the Python community with [`manylinux`](https://peps.python.org/pep-0513/). ## Security From aa82059073d9f994046f1c3b36dce9c2e420fd42 Mon Sep 17 00:00:00 2001 From: Daniel Grumberg Date: Mon, 19 May 2025 17:37:12 +0100 Subject: [PATCH 4319/4563] fixup! Mention limitations on artifacts in introduction --- ...wiftpm-static-library-binary-target-non-apple-platforms.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md index 1557019a3b..59ac4ad8d8 100644 --- a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md +++ b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -12,8 +12,8 @@ Swift continues to grow as a cross-platform language supporting a wide variety of use cases from [programming embedded device](https://www.swift.org/blog/embedded-swift-examples/) to [server-side development](https://www.swift.org/documentation/server/) across a multitude of [operating systems](https://www.swift.org/documentation/articles/static-linux-getting-started.html). However, currently SwiftPM supports linking against binary dependencies on Apple platforms only. -This proposal aims to make it possible to provide static library dependencies exposing a C interface on non-Apple platforms that depend only the standard C library. -Unfortunately, this proposal doesn't provide support for distributing Swift libraries built in this way due to the Swift ABI not being stable on all platforms. +This proposal aims to make it possible to provide static library dependencies exposing a C interface on non-Apple platforms that depend only on the standard C library. +The scope of this proposal is C libraries only, distributing Swift libraries has additional challenges (see [Future directions](#future-directions). ## Motivation From e4b25834c1cddd254bedd6497b2534190b712c31 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 19 May 2025 13:35:03 -0700 Subject: [PATCH 4320/4563] Replace `swift migrate` with `swift package migrate` --- .../NNNN-adoption-tooling-for-swift-features.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/NNNN-adoption-tooling-for-swift-features.md b/proposals/NNNN-adoption-tooling-for-swift-features.md index 93aed515e5..38217aac80 100644 --- a/proposals/NNNN-adoption-tooling-for-swift-features.md +++ b/proposals/NNNN-adoption-tooling-for-swift-features.md @@ -1,6 +1,6 @@ # Migration tooling for Swift features -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-NNNN](NNNN-migratable-features.md) * Authors: [Anthony Latsis](https://github.com/AnthonyLatsis), [Pavel Yaskevich](https://github.com/xedin) * Review Manager: TBD * Status: **Awaiting implementation** @@ -160,16 +160,16 @@ belong to a diagnostic group named after the feature. The names of diagnostic groups can be displayed alongside diagnostic messages using `-print-diagnostic-groups` and used to associate messages with features. -### `swift migrate` command +### `swift package migrate` command -To enable seemless migration experience for Swift packages, I'd like to propose a new Swift Package Manager command - `swift migrate` to complement the Swift compiler-side changes. +To enable seemless migration experience for Swift packages, I'd like to propose a new Swift Package Manager command - `swift package migrate` to complement the Swift compiler-side changes. The command would accept one or more features that have migration mode enabled and optionally a set of targets to migrate, if no targets are specified the whole package is going to be migrated to use new features. #### Interface ``` -USAGE: swift migrate [] --to-feature ... +USAGE: swift package migrate [] --to-feature ... OPTIONS: --targets The targets to migrate to specified set of features or a new language mode. @@ -181,7 +181,7 @@ OPTIONS: #### Use case ``` -swift migrate --targets MyTarget,MyTest --to-feature ExistentialAny +swift package migrate --targets MyTarget,MyTest --to-feature ExistentialAny ``` This command would attempt to build `MyTarget` and `MyTest` targets with `ExistentialAny:migrate` feature flag, apply any fix-its associated with @@ -211,7 +211,7 @@ enable the feature(s) if both of the previous actions are successful: In the "whole package" mode, every target is going to be updated to include new feature flag(s). This is supported by the same functionality as `swift package add-setting` command. -If it's, for some reason, impossible to add the setting the diagnostic message would suggest what to add and where i.e. `...; please add '.enableUpcomingFeature("ExistentialAny")' to `MyTarget` target manually`. +If it's, for some reason, impossible to add the setting the diagnostic message would suggest what to add and where i.e. `...; please add 'ExistentialAny' feature to `MyTarget` target manually`. #### Impact on Interface @@ -345,7 +345,7 @@ The next candidates in line per discussions are ***adopt***, ***audit***, To illustrate, this mode could appropriately suggest switching from `any P` to `some P` for `ExistentialAny`. -### `swift migrate` vs. `swift package migrate` +### `swift package migrate` vs. `swift migrate` Rather than have migrate as a subcommand (ie. `swift package migrate`), another option is add another top level command, ie. `swift migrate`. From 0accc4c2dd2a45e13c189734e02fe317aea73464 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Fri, 16 May 2025 12:55:11 -0500 Subject: [PATCH 4321/4563] Add proposal for Issue Handling Traits --- .../testing/NNNN-issue-handling-traits.md | 495 ++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 proposals/testing/NNNN-issue-handling-traits.md diff --git a/proposals/testing/NNNN-issue-handling-traits.md b/proposals/testing/NNNN-issue-handling-traits.md new file mode 100644 index 0000000000..ff73502ab4 --- /dev/null +++ b/proposals/testing/NNNN-issue-handling-traits.md @@ -0,0 +1,495 @@ +# Issue Handling Traits + +* Proposal: [ST-NNNN](NNNN-issue-handling-trait.md) +* Authors: [Stuart Montgomery](https://github.com/stmontgomery) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [swiftlang/swift-testing#1080](https://github.com/swiftlang/swift-testing/pull/1080), [swiftlang/swift-testing#1121](https://github.com/swiftlang/swift-testing/pull/1121) +* Review: ([pitch](https://forums.swift.org/t/pitch-issue-handling-traits/80019)) + +## Introduction + +This proposal introduces a built-in trait for handling issues in Swift Testing, +enabling test authors to customize how expectation failures and other issues +recorded by tests are represented. Using a custom issue handler, developers can +transform issue details, perform additional actions, or suppress certain issues. + +## Motivation + +Swift Testing offers ways to customize test attributes and perform custom logic +using traits, but there's currently no way to customize how issues (such as +`#expect` failures) are handled when they occur during testing. + +The ability to handle issues using custom logic would enable test authors to +modify, supplement, or filter issues based on their specific requirements before +the testing library processes them. This capability could open the door to more +flexible testing approaches, improve integration with external reporting systems, +or improve the clarity of results in complex testing scenarios. The sections +below discuss several potential use cases for this functionality. + +### Adding information to issues + +#### Comments + +Sometimes test authors want to include context-specific information to certain +types of failures. For example, they might want to automatically add links to +documentation for specific categories of test failures, or include supplemental +information about the history of a particular expectation in case it fails. An +issue handler could intercept issues after they're recorded and add these +details to the issue's comments before the testing library processes them. + +#### Attachments + +Test failures often benefit from additional diagnostic data beyond the basic +issue description. Swift Testing now supports attachments (as of +[ST-0009](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0009-attachments.md)), +and the ability to add an attachment to an indiviual issue was mentioned as a +future direction in that proposal. The general capability of adding attachments +to issues is outside the scope of this proposal, but if such a capability were +introduced, an issue handler could programmatically attach log files, +screenshots, or other diagnostic artifacts when specific issues occur, making it +easier to diagnose test failures. + +### Suppressing warnings + +Recently, a new API was [pitched][severity-proposal] which would introduce the +concept of severity to issues, along with a new _warning_ severity level, making +it possible to record warnings that do not cause a test to be marked as a +failure. If that feature is accepted, there may be cases where a test author +wants to suppress certain warnings entirely or in specific contexts. + +For instance, they might choose to suppress warnings recorded by the testing +library indicating that two or more arguments to a parameterized test appear +identical, or for one of the other scenarios listed as potential use cases for +warning issues in that proposal. An issue handler would provide a mechanism to +filter issues. + +### Raising or lowering an issue's severity + +Beyond suppressing issues altogether, a test author might want to modify the +severity of an issue (again, assuming the recently pitched +[Issue Severity][severity-proposal] proposal is accepted). They might wish to +either _lower_ an issue with the default error-level severity to a warning (but +not suppress it), or conversely _raise_ a warning issue to an error. + +The Swift compiler now allows control over warning diagnostics (as of +[SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md)). +An issue handling trait would offer analogous functionality for test issues. + +### Normalizing issue details + +Tests that involve randomized or non-deterministic inputs can generate different +issue descriptions on each run, making it difficult to identify duplicates or +recognize patterns in failures. For example, a test verifying random number +generation might produce an expectation failure with different random values +each time: + +``` +Expectation failed: (randomValue → 0.8234) > 0.5 +Expectation failed: (randomValue → 0.6521) > 0.5 +``` + +An issue handler could normalize these issues to create a more consistent +representation: + +``` +Expectation failed: (randomValue → 0.NNNN) > 0.5 +``` + +The original numeric value could be preserved via a comment after being +obfuscated—see [Comments](#comments) under [Adding information to issues](#adding-information-to-issues) +above. + +> [!NOTE] +> This example involves an expectation failure. The value of the `kind` property +> for such an issue would be `.expectationFailed(_:)` and it would have an +> associated value of type `Expectation`. To transform the issue in the way +> described above would require modifying details of the associated `Expectation` +> and its substructure, but these details are currently SPI so test authors +> cannot modify them directly. +> +> Exposing these details is out of scope for this proposal, but a test author +> could still transform this issue to achieve a similar result by changing the +> issue's kind from `.expectationFailed(_:)` to `.unconditional`. This +> experience could be improved in the future in subsequent proposals if desired. + +This normalization can significantly improve the ability to triage failures, as +it becomes easier to recognize when multiple test failures have the same root +cause despite different specific values. + +## Proposed solution + +This proposal introduces a new trait type that can customize how issues are +processed during test execution. + +Here's one contrived example showing how this could be used to add a comment to +each issue recorded by a test: + +```swift +@Test(.transformIssues { issue in + var issue = issue + issue.comments.append("Checking whether two literals are equal") + return issue +}) +func literalComparisons() { + #expect(1 == 1) // ✅ + #expect(2 == 3) // ❌ Will invoke issue handler + #expect("a" == "b") // ❌ Will invoke issue handler again +} +``` + +Here's an example showing how warning issues matching a specific criteria could +be suppressed using `.filterIssues`. It also showcases a technique for reusing +an issue handler across multiple tests, by defining it as a computed property in +an extension on `Trait`: + +```swift +extension Trait where Self == IssueHandlingTrait { + static var ignoreSensitiveWarnings: Self { + .filterIssues { issue in + let description = String(describing: issue) + + // Note: 'Issue.severity' has been pitched but not accepted. + return issue.severity <= .warning && SensitiveTerms.all.contains { description.contains($0) } + } + } +} + +@Test(.ignoreSensitiveWarnings) func exampleA() { + ... +} + +@Test(.ignoreSensitiveWarnings) func exampleB() { + ... +} +``` + +The sections below discuss some of the proposed new trait's behavioral details. + +### Precedence order of handlers + +If multiple issue handling traits are applied to or inherited by a test, they +are executed in trailing-to-leading, innermost-to-outermost order. For example, +given the following code: + +```swift +@Suite(.transformIssues { ... /* A */ }) +struct ExampleSuite { + @Test(.filterIssues { ... /* B */ }, + .transformIssues { ... /* C */ }) + func example() { + ... + } +} +``` + +If an issue is recorded in `example()`, it's processed first by closure C, then +by B, and finally by A. (Unless an issue is suppressed, in which case it will +not advance to any subsequent handler's closure.) This ordering provides +predictable behavior and allows more specific handlers to process issues before +more general ones. + +### Accessing task-local context from handlers + +The closure of an issue handler is invoked synchronously at the point where an +issue is recorded. This means the closure can access task local state from that +context, and potentially use that to augment issues with extra information. +Here's an example: + +```swift +// In module under test: +actor Session { + @TaskLocal static var current: Session? + + let id: String + func connect() { ... } + var isConnected: Bool { ... } + ... +} + +// In test code: +@Test(.transformIssues { issue in + var issue = issue + if let session = Session.current { + issue.comments.append("Current session ID: \(session.id)") + } + return issue +}) +func example() async { + let session = Session(id: "ABCDEF") + await Session.$current.withValue(session) { + await session.connect() + #expect(await session.isConnected) // ❌ Expectation failed: await session.isConnected + // Current session ID: ABCDEF + } +} +``` + +### Recording issues from handlers + +Issue handling traits can record additional issues during their execution. These +newly recorded issues will be processed by any later issue handling traits in +the processing chain (see [Precedence order of handlers](#precedence-order-of-handlers)). +This capability allows handlers to augment or provide context to existing issues +by recording related information. + +For example: + +```swift +@Test( + .transformIssues { issue in + // This closure will be called for any issue recorded by the test function + // or by the `.filterIssues` trait below. + ... + }, + .filterIssues { issue in + guard let terms = SensitiveTerms.all else { + Issue.record("Cannot determine the set of sensitive terms. Filtering issue by default.") + return true + } + + let description = String(describing: issue).lowercased() + return terms.contains { description.contains($0) } + } +) +func example() { + ... +} +``` + +### Handling issues from other traits + +Issue handling traits process all issues recorded in the context of a test, +including those generated by other traits applied to the test. For instance, if +a test uses the `.enabled(if:)` trait and the condition closure throws an error, +that error will be recorded as an issue, the test will be skipped, and the issue +will be passed to any issue handling traits for processing. + +This comprehensive approach ensures that all issues related to a test, +regardless of their source, are subject to the same customized handling. It +provides a unified mechanism for issue processing that works consistently across +the testing library. + +### Effects in issue handler closures + +The closure of an issue handling trait must be: + +- **Non-`async`**: This reflects the fact that events in + Swift Testing are posted synchronously, which is a fundamental design decision + that, among other things, avoids the need for `await` before every `#expect`. + + While this means that issue handlers cannot directly perform asynchronous work + when processing an individual issue, future enhancements could offer + alternative mechanisms for asynchronous issue processing work at the end of a + test. See the [Future directions](#future-directions) section for more + discussion about this. + +- **Non-`throws`**: Since these handlers are already + being called in response to a failure (the recorded issue), allowing them to + throw errors would introduce ambiguity about how such errors should be + interpreted and reported. + + If an issue handler encounters an error, it can either: + + - Return a modified issue that includes information about the problem, or + - Record a separate issue using the standard issue recording mechanisms (as + [discussed](#recording-issues-from-handlers) above). + +## Detailed design + +This proposal includes the following: + +* A new `IssueHandlingTrait` type that conforms to `TestTrait` and `SuiteTrait`. + * An instance method `handleIssue(_:)` which can be called directly on a + handler trait. This may be useful for composing multiple issue handling + traits. +* Static functions on `Trait` for creating instances of this type with the + following capabilities: + * A function `transformIssues(_:)` which returns a trait that can transform + recorded issues. The function takes a closure which is passed an issue and + returns either a modified issue or `nil` to suppress it. + * A function `filterIssues(_:)` which returns a trait that can filter recorded + issues. The function takes a predicate closure that returns a boolean + indicating whether to keep (`true`) or suppress (`false`) an issue. + +Below are the proposed interfaces: + +```swift +/// A type that allows transforming or filtering the issues recorded by a test. +/// +/// Use this type to observe or customize the issue(s) recorded by the test this +/// trait is applied to. You can transform a recorded issue by copying it, +/// modifying one or more of its properties, and returning the copy. You can +/// observe recorded issues by returning them unmodified. Or you can suppress an +/// issue by either filtering it using ``Trait/filterIssues(_:)`` or returning +/// `nil` from the closure passed to ``Trait/transformIssues(_:)``. +/// +/// When an instance of this trait is applied to a suite, it is recursively +/// inherited by all child suites and tests. +/// +/// To add this trait to a test, use one of the following functions: +/// +/// - ``Trait/transformIssues(_:)`` +/// - ``Trait/filterIssues(_:)`` +public struct IssueHandlingTrait: TestTrait, SuiteTrait { + /// Handle a specified issue. + /// + /// - Parameters: + /// - issue: The issue to handle. + /// + /// - Returns: An issue to replace `issue`, or else `nil` if the issue should + /// not be recorded. + public func handleIssue(_ issue: Issue) -> Issue? +} + +extension Trait where Self == IssueHandlingTrait { + /// Constructs an trait that transforms issues recorded by a test. + /// + /// - Parameters: + /// - transformer: The closure called for each issue recorded by the test + /// this trait is applied to. It is passed a recorded issue, and returns + /// an optional issue to replace the passed-in one. + /// + /// - Returns: An instance of ``IssueHandlingTrait`` that transforms issues. + /// + /// The `transformer` closure is called synchronously each time an issue is + /// recorded by the test this trait is applied to. The closure is passed the + /// recorded issue, and if it returns a non-`nil` value, that will be recorded + /// instead of the original. Otherwise, if the closure returns `nil`, the + /// issue is suppressed and will not be included in the results. + /// + /// The `transformer` closure may be called more than once if the test records + /// multiple issues. If more than one instance of this trait is applied to a + /// test (including via inheritance from a containing suite), the `transformer` + /// closure for each instance will be called in right-to-left, innermost-to- + /// outermost order, unless `nil` is returned, which will skip invoking the + /// remaining traits' closures. + /// + /// Within `transformer`, you may access the current test or test case (if any) + /// using ``Test/current`` ``Test/Case/current``, respectively. You may also + /// record new issues, although they will only be handled by issue handling + /// traits which precede this trait or were inherited from a containing suite. + public static func transformIssues(_ transformer: @escaping @Sendable (Issue) -> Issue?) -> Self + + /// Constructs a trait that filters issues recorded by a test. + /// + /// - Parameters: + /// - isIncluded: The predicate with which to filter issues recorded by the + /// test this trait is applied to. It is passed a recorded issue, and + /// should return `true` if the issue should be included, or `false` if it + /// should be suppressed. + /// + /// - Returns: An instance of ``IssueHandlingTrait`` that filters issues. + /// + /// The `isIncluded` closure is called synchronously each time an issue is + /// recorded by the test this trait is applied to. The closure is passed the + /// recorded issue, and if it returns `true`, the issue will be preserved in + /// the test results. Otherwise, if the closure returns `false`, the issue + /// will not be included in the test results. + /// + /// The `isIncluded` closure may be called more than once if the test records + /// multiple issues. If more than one instance of this trait is applied to a + /// test (including via inheritance from a containing suite), the `isIncluded` + /// closure for each instance will be called in right-to-left, innermost-to- + /// outermost order, unless `false` is returned, which will skip invoking the + /// remaining traits' closures. + /// + /// Within `isIncluded`, you may access the current test or test case (if any) + /// using ``Test/current`` ``Test/Case/current``, respectively. You may also + /// record new issues, although they will only be handled by issue handling + /// traits which precede this trait or were inherited from a containing suite. + public static func filterIssues(_ isIncluded: @escaping @Sendable (Issue) -> Bool) -> Self +} +``` + +## Source compatibility + +This new trait is additive and should not affect source compatibility of +existing test code. + +If any users have an existing extension on `Trait` containing a static function +whose name conflicts with one in this proposal, the standard technique of +fully-qualifying its callsite with the relevant module name can be used to +resolve any ambiguity, but this should be rare. + +## Integration with supporting tools + +Most tools which integrate with the testing library interpret recorded issues in +some way, whether by writing them to a persistent data file or presenting them +in UI. These mechanisms will continue working as before, but the issues they act +on will be the result of any issue handling traits. If an issue handler +transforms an issue, the integrated tool will only receive the transformed issue, +and if a trait suppresses an issue, the tool will not be notified about the +issue at all. + +## Future directions + +### "Test ended" trait + +The current proposal does not allow `await` in an issue handling closure--see +[Non-`async`](#non-async) above. In addition to not allowing concurrency, the +proposed behavior is that the issue handler is called once for _each_ issue +recorded. + +Both of these policies could be problematic for some use cases. Some users may +want to collect additional diagnostics if a test fails, but only do so once per +per test (typically after it finishes) instead of once per _issue_, since the +latter may lead to redundant or wasteful work. Also, collecting diagnostics may +require calling `async` APIs. + +In the future, a new trait could be added which offers a closure that is +unconditionally called once after a test ends. The closure could be provided the +result of the test (e.g. pass/fail/skip) and access to all the issues it +recorded. This hypothetical trait's closure could be safely made `async`, since +it wouldn't be subject to the same limitations as event delivery, and this could +complement the APIs proposed above. + +### Comprehensive event observation API + +As a more generalized form of the ["Test ended" trait](#test-ended-trait) idea +above, Swift Testing could offer a more comprehensive suite of APIs for +observing test events of all kinds. This would a much larger effort, but was +[mentioned](https://github.com/swiftlang/swift-evolution/blob/main/visions/swift-testing.md#flexibility) +as a goal in the +[Swift Testing vision document](https://github.com/swiftlang/swift-evolution/blob/main/visions/swift-testing.md). + +## Alternatives considered + +### Allow issue handler closures to throw errors + +The current proposal does not allow throwing an error from an issue handling +closure--see [Non-`throws`](#non-throws) above. This artificial restriction +could be lifted, and errors thrown by issue handler closures could be caught +and recorded as issues, matching the behavior of test functions. + +As mentioned earlier, allowing thrown errors could make test results more +confusing. We expect that most often, a test author will add an issue handler +because they want to make failures easier to interpret, and they generally won't +want an issue handler to record _more_ issues while doing so even if it can. Not +allowing errors to be thrown forces the author of the issue handler to make an +explicit decision about whether they want an additional issue to be recorded if +the handler encounters an error. + +### Alternate names for the static trait functions + +We could choose different names for the static `transformIssues(_:)` or +`filterIssues(_:)` functions. Some alternate names considered were: + +- `compactMapIssues` instead of `transformIssues`. This arguably aligns better + with the word "filter" of `filterIssues`, but it felt strange since often, + "map" is used to return a value of a _different_ type, whereas here it must + return a value of the same type, it's just that it can be mutated. +- `handleIssues` instead of `transformIssues`. The word "handle" is in the name + of the trait type already; it's a more general word for what all of these + usage patterns enable, so it felt too broad. +- Using singular "issue" rather than plural "issues" in both APIs. This may not + adequately convey that the closure can be invoked more than once. + +## Acknowledgments + +Thanks to [Brian Croom](https://github.com/briancroom) for feedback on the +initial concept, and for making a suggestion which led to the +["Test ended" trait](#test-ended-trait) idea mentioned in Alternatives +considered. + +[severity-proposal]: https://forums.swift.org/t/pitch-test-issue-warnings/79285 From aa91afb097ffe1d21b955a05dede0309eae4814d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 21 May 2025 08:36:16 +0100 Subject: [PATCH 4322/4563] [SE-0485] Initiate review of OutputSpan: delegate initialization of contiguous memory --- proposals/{nnnn-outputspan.md => 0485-outputspan.md} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename proposals/{nnnn-outputspan.md => 0485-outputspan.md} (98%) diff --git a/proposals/nnnn-outputspan.md b/proposals/0485-outputspan.md similarity index 98% rename from proposals/nnnn-outputspan.md rename to proposals/0485-outputspan.md index 6412770deb..3d244045ea 100644 --- a/proposals/nnnn-outputspan.md +++ b/proposals/0485-outputspan.md @@ -1,9 +1,9 @@ # OutputSpan: delegate initialization of contiguous memory -* Proposal: TBD +* Proposal: [SE-0485](0485-outputspan.md) * Author: [Guillaume Lessard](https://github.com/glessard) -* Review Manager: TBD -* Status: **Pitch** +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Active Review (May 20...June 3, 2025)** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: "Future" target of [swift-collections](https://github.com/apple/swift-collections/tree/future) * Review: [Pitch](https://forums.swift.org/) @@ -58,7 +58,7 @@ In addition to the new types, we propose adding new API for some standard librar `OutputSpan` allows delegating the initialization of a type's memory, by providing access to an exclusively-borrowed view of a range of contiguous memory. `OutputSpan`'s contiguous memory always consists of a prefix of initialized memory, followed by a suffix of uninitialized memory. `OutputSpan`'s operations manage the initialization state in order to preserve that invariant. The common usage pattern we expect to see for `OutputSpan` consists of passing it as an `inout` parameter to a function, allowing the function to produce an output by writing into a previously uninitialized region. -Like `MutableSpan`, `OutputSpan` relies on two guarantees: (a) that it has exclusive access to the range of memory it represents, and (b) that the memory locations it represents will remain valid for the duration of the access. These guarantee data race safety and temporal safety. `OutputSpan` performs bounds-checking on every access to preserve spatial safety. `OutputSpan` manages the initialization state of the memory in represents on behalf of the memory's owner. +Like `MutableSpan`, `OutputSpan` relies on two guarantees: (a) that it has exclusive access to the range of memory it represents, and (b) that the memory locations it represents will remain valid for the duration of the access. These guarantee data race safety and lifetime safety. `OutputSpan` performs bounds-checking on every access to preserve bounds safety. `OutputSpan` manages the initialization state of the memory in represents on behalf of the memory's owner. #### OutputRawSpan @@ -936,7 +936,7 @@ The additions described in this proposal require a new version of the Swift stan `OutputSpan` lays the groundwork for new, generalized `Container` protocols that will expand upon and succeed the `Collection` hierarchy while allowing non-copyability and non-escapability to be applied to both containers and elements. We hope to find method and property names that will be generally applicable. The origin of the `append(contentsOf:)` method we are expanding upon is the `RangeReplaceableCollection` protocol, which always represents copyable and escapable collections with copyable and escapable elements. The definition is as follows: `mutating func append(contentsOf newElements: __owned S)`. This supports copying elements from the source, while also destroying the source if we happen to hold its only copy. This is obviously not sufficient if the elements are non-copyable, or if we only have access to a borrow of the source. -When the elements are non-copyable, we must append elements that are removed from the source. Afterwards, there are two possible dispositions of the source: destruction (`consuming`,) where the source can no longer be used, or mutation (`inout`,) where the source has been emptied but is still usable. +When the elements are non-copyable, we must append elements that are removed from the source. Afterwards, there are two possible dispositions of the source: destruction (`consuming`), where the source can no longer be used, or mutation (`inout`), where the source has been emptied but is still usable. When the elements are copyable, we can simply copy the elements from the source. Afterwards, there are two possible dispositions of the source: releasing a borrowed source, or `consuming`. The latter is approximately the same behaviour as `RangeReplaceableCollection`'s `append()` function shown above. @@ -985,7 +985,7 @@ A use case similar to appending is insertions. Appending is simply inserting at #### Generalized removals -Similarly to generalized insertions (i.e. not from the end,) we can think about removals of one or more elements starting at a given position. This also requires adding indexing. We expect to add generalized removals along with insertions if `OutputSpan` is accepted. +Similarly to generalized insertions (i.e. not from the end), we can think about removals of one or more elements starting at a given position. This also requires adding indexing. We expect to add generalized removals along with insertions if `OutputSpan` is accepted. #### Variations on `Array.append(addingCapacity:initializingWith:)` From 4ecced2d14aa8b1c82349f3434a4ffc14db65957 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 21 May 2025 08:40:04 +0100 Subject: [PATCH 4323/4563] Add review thread for SE-0485 --- proposals/0485-outputspan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0485-outputspan.md b/proposals/0485-outputspan.md index 3d244045ea..d9b05b8e4c 100644 --- a/proposals/0485-outputspan.md +++ b/proposals/0485-outputspan.md @@ -6,7 +6,7 @@ * Status: **Active Review (May 20...June 3, 2025)** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: "Future" target of [swift-collections](https://github.com/apple/swift-collections/tree/future) -* Review: [Pitch](https://forums.swift.org/) +* Review: [Pitch](https://forums.swift.org/), [Review](https://forums.swift.org/t/se-0485-outputspan-delegate-initialization-of-contiguous-memory/80032) [SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md [SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md From f67e90e55379259524d6491e5c666eb1bcac42d9 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 21 May 2025 09:54:51 -0400 Subject: [PATCH 4324/4563] Accept SE-0472. --- proposals/0472-task-start-synchronously-on-caller-context.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0472-task-start-synchronously-on-caller-context.md b/proposals/0472-task-start-synchronously-on-caller-context.md index c7f0701080..1f47857b65 100644 --- a/proposals/0472-task-start-synchronously-on-caller-context.md +++ b/proposals/0472-task-start-synchronously-on-caller-context.md @@ -3,9 +3,9 @@ * Proposal: [SE-0472](0472-task-start-synchronously-on-caller-context.md) * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Returned for revision** +* Status: **Accepted** * Implementation: https://github.com/swiftlang/swift/pull/79608 -* Review: ([pitch](https://forums.swift.org/t/pitch-concurrency-starting-tasks-synchronously-from-caller-context/77960/)) ([first review](https://forums.swift.org/t/se-0472-starting-tasks-synchronously-from-caller-context/78883)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0472-starting-tasks-synchronously-from-caller-context/79311)) ([second review](https://forums.swift.org/t/second-review-se-0472-starting-tasks-synchronously-from-caller-context/79683)) +* Review: ([pitch](https://forums.swift.org/t/pitch-concurrency-starting-tasks-synchronously-from-caller-context/77960/)) ([first review](https://forums.swift.org/t/se-0472-starting-tasks-synchronously-from-caller-context/78883)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0472-starting-tasks-synchronously-from-caller-context/79311)) ([second review](https://forums.swift.org/t/second-review-se-0472-starting-tasks-synchronously-from-caller-context/79683)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0472-starting-tasks-synchronously-from-caller-context/80037)) ## Introduction From acca3fd80d2eefb9c22fc6147ab688b429760933 Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Wed, 21 May 2025 17:53:55 +0200 Subject: [PATCH 4325/4563] [SE-0485] Initiate review of Migration tooling for Swift featuress --- ...tures.md => 0486-adoption-tooling-for-swift-features.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{NNNN-adoption-tooling-for-swift-features.md => 0486-adoption-tooling-for-swift-features.md} (99%) diff --git a/proposals/NNNN-adoption-tooling-for-swift-features.md b/proposals/0486-adoption-tooling-for-swift-features.md similarity index 99% rename from proposals/NNNN-adoption-tooling-for-swift-features.md rename to proposals/0486-adoption-tooling-for-swift-features.md index 38217aac80..bf50318ac3 100644 --- a/proposals/NNNN-adoption-tooling-for-swift-features.md +++ b/proposals/0486-adoption-tooling-for-swift-features.md @@ -1,9 +1,9 @@ # Migration tooling for Swift features -* Proposal: [SE-NNNN](NNNN-migratable-features.md) +* Proposal: [SE-0486](0486-adoption-tooling-for-swift-features.md) * Authors: [Anthony Latsis](https://github.com/AnthonyLatsis), [Pavel Yaskevich](https://github.com/xedin) -* Review Manager: TBD -* Status: **Awaiting implementation** +* Review Manager: [Franz Busch](https://github.com/FranzBusch) +* Status: **Active Review (May 21...June 4, 2025)** * Implementation: TBD * Review: [pitch](https://forums.swift.org/t/pitch-adoption-tooling-for-upcoming-features/77936) From 772d03cc3f44488b653f2757639a3df481e07cfa Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Wed, 21 May 2025 17:00:54 +0100 Subject: [PATCH 4326/4563] [SE-0461] Update remaining mentions of old feature name (#2856) --- proposals/0461-async-function-isolation.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md index c123ce36b7..47633dda33 100644 --- a/proposals/0461-async-function-isolation.md +++ b/proposals/0461-async-function-isolation.md @@ -204,7 +204,7 @@ actor MyActor { Changing the default execution semantics of async functions can change the behavior of existing code, so the change is gated behind the -`AsyncCallerExecution` upcoming feature flag. To help stage in the new +`NonisolatedNonsendingByDefault` upcoming feature flag. To help stage in the new behavior, new syntax can be used to explicitly specify the execution semantics of an async function in any language mode. @@ -249,15 +249,15 @@ actor MyActor { `@concurrent` is the current default for nonisolated async functions. `nonisolated(nonsending)` will become the default for async functions -when the `AsyncCallerExecution` upcoming feature is enabled. +when the `NonisolatedNonsendingByDefault` upcoming feature is enabled. ## Detailed design The sections below will explicitly use `@concurrent` and `nonisolated(nonsending)` to demonstrate examples that will behave consistently independent of upcoming features or language modes. However, note that the -end state under the `AsyncCallerExecution` upcoming feature will mean that -`(nonsending)` is not necessary to explicitly write, and +end state under the `NonisolatedNonsendingByDefault` upcoming feature will mean +that `(nonsending)` is not necessary to explicitly write, and `@concurrent` will likely be used sparingly because it has far stricter data-race safety requirements. @@ -1119,8 +1119,8 @@ The proposal was revised with the following changes after the first review: The proposal was revised with the following changes after the pitch discussion: -* Gate the behavior change behind an `AsyncCallerExecution` upcoming feature - flag. +* Gate the behavior change behind an `NonisolatedNonsendingByDefault` upcoming + feature flag. * Change the spelling of `@concurrent` to `@execution(concurrent)`, and add an `@execution(caller)` attribute to allow expressing the new behavior this proposal introduces when the upcoming feature flag is not enabled. From 085467aa13398f263b813f850a2f9a6b4c152ea6 Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Wed, 21 May 2025 09:44:37 -0700 Subject: [PATCH 4327/4563] Accept SE-0482 --- ...wiftpm-static-library-binary-target-non-apple-platforms.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md index 59ac4ad8d8..569c018de1 100644 --- a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md +++ b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -3,9 +3,9 @@ * Proposal: [SE-0482](0482-swiftpm-static-library-binary-target-non-apple-platforms.md) * Authors: [Daniel Grumberg](https://github.com/daniel-grumberg), [Max Desiatov](https://github.com/MaxDesiatov), [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Kuba Mracek](https://github.com/kubamracek) -* Status: **Active Review (May 2nd...May 15th, 2025)** +* Status: **Accepted** * Implementation: [swiftlang/swift-package-manager#6967](https://github.com/swiftlang/swift-package-manager/pull/6967) [swiftlang/swift-package-manager#8605](https://github.com/swiftlang/swift-package-manager/pull/8605) -* Review: ([discussion](https://forums.swift.org/t/se-0482-binary-static-library-dependencies/79634)) ([pitch](https://forums.swift.org/t/pitch-swiftpm-support-for-binary-static-library-dependencies/78619)) +* Review: ([discussion](https://forums.swift.org/t/se-0482-binary-static-library-dependencies/79634)) ([pitch](https://forums.swift.org/t/pitch-swiftpm-support-for-binary-static-library-dependencies/78619)) ([acceptance](https://forums.swift.org/t/accepted-se-0482-binary-static-library-dependencies/80042)) * Bugs: [Swift Package Manger Issue](https://github.com/swiftlang/swift-package-manager/issues/7035) ## Introduction From eed1fed8339f3919cf5c0f36b804431022d44720 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 23 May 2025 13:52:34 -0400 Subject: [PATCH 4328/4563] Return SE-0480 for revision --- proposals/0480-swiftpm-warning-control.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0480-swiftpm-warning-control.md b/proposals/0480-swiftpm-warning-control.md index 47598f0973..18157ce824 100644 --- a/proposals/0480-swiftpm-warning-control.md +++ b/proposals/0480-swiftpm-warning-control.md @@ -3,9 +3,9 @@ * Proposal: [SE-0480](0480-swiftpm-warning-control.md) * Authors: [Dmitrii Galimzianov](https://github.com/DmT021) * Review Manager: [John McCall](https://github.com/rjmccall), [Franz Busch](https://github.com/FranzBusch) -* Status: **Active review (April 23...May 5th, 2025)** +* Status: **Returned for revision** * Implementation: [swiftlang/swift-package-manager#8315](https://github.com/swiftlang/swift-package-manager/pull/8315) -* Review: ([pitch](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666)) ([review](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475)) +* Review: ([pitch](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666)) ([review](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475)) ([returned for revision](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475/8)) * Previous Proposal: [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md) ## Introduction From 9c93e5734b5b52d9a9b53710cfc33c9b9febf277 Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Sun, 25 May 2025 23:36:13 +0200 Subject: [PATCH 4329/4563] Pitch for extensible enums (#2679) * Extensible enums * Review * Reviews and same module/package enums * Clarify API impact of `@extensible` and add alternatives considered section * More review comments * Pitch feedback * Pitch feedback: Adding migration path and expand on implications inside the same package * Fix small mistake * Address adding associated values * Remove new annotations and re-use `@frozen` * Update metadata with implementation and pitch * Small fixups * Add migration paths * Minor spelling fix ups * Update proposal to focus on `@extensible` attribute * Change implementation link * Add `@preEnumExtensibility` to the proposal * Source compat section for preEnumExtensibility * Kick off review Also rewords opening description of resilience. --------- Co-authored-by: Ben Cohen --- proposals/NNNN-extensible-enums.md | 319 +++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 proposals/NNNN-extensible-enums.md diff --git a/proposals/NNNN-extensible-enums.md b/proposals/NNNN-extensible-enums.md new file mode 100644 index 0000000000..1878defc70 --- /dev/null +++ b/proposals/NNNN-extensible-enums.md @@ -0,0 +1,319 @@ +# Extensible enums + +* Proposal: [SE-NNNN](NNNN-extensible-enums.md) +* Authors: [Pavel Yaskevich](https://github.com/xedin), [Franz Busch](https://github.com/FranzBusch), [Cory Benfield](https://github.com/lukasa) +* Review Manager: [Ben Cohen](https://github.com/airspeedswift) +* Status: **In active review (May 25—Jun 5, 2025)** +* Bug: [apple/swift#55110](https://github.com/swiftlang/swift/issues/55110) +* Implementation: [apple/swift#80503](https://github.com/swiftlang/swift/pull/80503) +* Upcoming Feature Flag: `ExtensibleAttribute` +* Review: ([pitch](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649)) + +Previously pitched in: + +- https://forums.swift.org/t/extensible-enumerations-for-non-resilient-libraries/35900 +- https://forums.swift.org/t/pitch-non-frozen-enumerations/68373 + +Revisions: +- Re-focused this proposal on introducing a new `@extensible` attribute and + moved the language feature to a future direction +- Introduced a second annotation `@nonExtensible` to allow a migration path into + both directions +- Added future directions for adding additional associated values +- Removed both the `@extensible` and `@nonExtensible` annotation in favour of + re-using the existing `@frozen` annotation +- Added the high level goals that this proposal aims to achieve +- Expanded on the proposed migration path for packages with regards to their + willingness to break API +- Added future directions for exhaustive matching for larger compilation units +- Added alternatives considered section for a hypothetical + `@preEnumExtensibility` +- Added a section for `swift package diagnose-api-breaking-changes` + +## Introduction + +This proposal provides developers the capabilities to mark public enums in +non-resilient Swift libraries as extensible. This makes Swift `enum`s vastly +more useful in public API of such libraries. + +## Motivation + +When Swift was enhanced to add support for ABI-stable libraries that were built with +"library evolution" enabled ("resilient" libraries as we call them in this proposal), +the Swift language had to support these libraries vending enums that might have cases +added to them in a later version. Swift supports exhaustive switching over cases. +When binaries are compiled against a ABI-stable library they need to be able to handle the +addition of a new case by that library later on, without needing to be rebuilt. + +Consider the following simple library to your favorite pizza place: + +```swift +public enum PizzaFlavor { + case hawaiian + case pepperoni + case cheese +} +``` + +In the standard "non-resilient" mode, users of the library can write exhaustive switch +statements over the enum `PizzaFlavor`: + +```swift +switch pizzaFlavor { +case .hawaiian: + throw BadFlavorError() +case .pepperoni: + try validateNoVegetariansEating() + return .delicious +case .cheese: + return .delicious +} +``` + +Swift requires switches to be exhaustive i.e. the must handle every possibility. +If the author of the above switch statement was missing a case (perhaps they forgot +`.hawaiian` is a flavor), the compiler will error, and force the user to either add a +`default:` clause, or to add the missing case. + +If later a new case is added to the enum (maybe `.veggieSupreme`), exhaustive switches +over that enum might no longer be exhaustive. This is often _desirable_ within a single +codebase (even one split up into multiple modules). A case is added, and the compiler will +assist in finding all the places where this new case must be handled. + +But it presents a problem for authors of both resilient and non-resilient libraries: + +- For non-resilient libraries, adding a case is a source-breaking API change: clients +exhaustively switching over the enum will no longer compile. So can only be done with +a major semantic version bump. +- For resilient libraries, even that is not an option. An ABI-stable library cannot allow +a situation where a binary that has not yet been recompiled can no longer rely on its +switches over an enum are exhaustive. + +Because of the implications on ABI and the requirement to be able to evolve +libraries with public enumerations in their API, the resilient language dialect introduced +"non-exhaustive enums" in [SE-0192](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0192-non-exhaustive-enums.md). + +If the library was compiled with `-enable-library-evolution`, when a user attempts to +exhaustively switch over the `PizzaFlavor` enum the compiler will emit an error +(when in Swift 6 language mode, a warning in prior language modes), requiring users +to add an `@unknown default:` clause: + +```swift +switch pizzaFlavor { +case .hawaiian: + throw BadFlavorError() +case .pepperoni: + try validateNoVegetariansEating() + return .delicious +case .cheese: + return .delicious +@unknown default: + try validateNoVegetariansEating() + return .delicious +} +``` + +The user is forced to specify how cases are handled if they are introduced later. This +allows ABI-stable libraries to add cases without risking undefined behavior in client +binaries that haven't yet been recompiled. + +When a resilient library knows that an enumeration will never be extended, the author +can annotate the enum with `@frozen`, which in the case of enums is a guarantee that no +further cases can be added. For example, the `Optional` type in the standard library is +frozen, as no third option beyond `some` and `none` will ever be added. This brings +performance benefits, and also the convenience of not requiring an `@unknown default` case. + +`@frozen` is a powerful attribute that can be applied to both structs and enums. It has a +wide ranging number of effects, including exposing their size directly as part of the ABI +and providing direct access to stored properties. However, on enums it happens to +have source-level effects on the behavior of switch statements by clients of a library. +This difference was introduced late in the process of reviewing SE-0192. + +Extensibility of enums is also desirable for non-resilient libraries. Without it, there is no +way for a Swift package to be able to evolve a public enumeration without breaking the API. +However, in Swift today it is not possible for the default, "non-resilient" dialect to opt-in +to the extensible enumeration behavior. This is a substantial limitation, and greatly reduces +the utility of enumerations in non-resilient Swift. + +Over the past years, many packages have run into this limitation when trying to express APIs +using enums. As a non-exhaustive list of problems this can cause: + +- Using enumerations to represent `Error`s is inadvisable, as if new errors need + to be introduced they cannot be added to existing enumerations. This leads to + a proliferation of `Error` enumerations. "Fake" enumerations can be made using + `struct`s and `static let`s, but these do not work with the nice `Error` + pattern-match logic in catch blocks, requiring type casts. +- Using an enumeration to refer to a group of possible ideas without entirely + exhaustively evaluating the set is potentially dangerous, requiring a + deprecate-and-replace if any new elements appear. +- Using an enumeration to represent any concept that is inherently extensible is + tricky. For example, `SwiftNIO` uses an enumeration to represent HTTP status + codes. If new status codes are added, SwiftNIO needs to either mint new + enumerations and do a deprecate-and-replace, or it needs to force these new + status codes through the .custom enum case. + +This proposal plans to address these limitations on enumerations in +non-resilient Swift. + +## Proposed solution + +We propose to introduce a new `@extensible` attribute that can be applied to +enumerations to mark them as extensible. Such enums will behave the same way as +non-frozen enums from resilient Swift libraries. + +An example of using the new attribute is below: + +```swift +/// Module A +@extensible +public enum PizzaFlavor { + case hawaiian + case pepperoni + case cheese +} + +/// Module B +switch pizzaFlavor { // error: Switch covers known cases, but 'MyEnum' may have additional unknown values, possibly added in future versions +case .hawaiian: + throw BadFlavorError() +case .pepperoni: + try validateNoVegetariansEating() + return .delicious +case .cheese: + return .delicious +} +``` + +### Exhaustive switching inside same module/package + +Code inside the same module or package can be thought of as one co-developed +unit of code. Inside the same module or package, switching exhaustively over an +`@extensible` enum inside will not require an`@unknown default`, and using +one will generate a warning. + +### `@extensible` and `@frozen` + +An enum cannot be `@frozen` and `@extensible` at the same time. Thus, marking an +enum both `@extensible` and `@frozen` is not allowed and will result in a +compiler error. + +### API breaking checker + +The behavior of `swift package diagnose-api-breaking-changes` is also updated +to understand the new `@extensible` attribute. + +### Staging in using `@preEnumExtensibility` + +We also propose adding a new `@preEnumExtensibility` attribute that can be used +to mark enumerations as pre-existing to the `@extensible` attribute. This allows +developers to mark existing public enumerations as `@preEnumExtensibility` in +addition to `@extensible`. This is useful for developers that want to stage in +changing an existing non-extensible enum to be extensible over multiple +releases. Below is an example of how this can be used: + +```swift +// Package A +public enum Foo { + case foo +} + +// Package B +switch foo { +case .foo: break +} + +// Package A wants to make the existing enum extensible +@preEnumExtensibility @extensible +public enum Foo { + case foo +} + +// Package B now emits a warning downgraded from an error +switch foo { // warning: Enum might be extended later. Add an @unknown default case. +case .foo: break +} + +// Later Package A decides to extend the enum and releases a new major version +@preEnumExtensibility @extensible +public enum Foo { + case foo + case bar +} + +// Package B didn't add the @unknown default case yet. So now we we emit a warning and an error +switch foo { // error: Unhandled case bar & warning: Enum might be extended later. Add an @unknown default case. +case .foo: break +} +``` + +While the `@preEnumExtensibility` attribute doesn't solve the need of requiring +a new major when a new case is added it allows developers to stage in changing +an existing non-extensible enum to become extensible in a future release by +surfacing a warning about this upcoming break early. + +## Source compatibility + +### Resilient modules + +- Adding or removing the `@extensible` attribute has no-effect since it is the default in this language dialect. +- Adding the `@preEnumExtensibility` attribute has no-effect since it only downgrades the error to a warning. +- Removing the `@preEnumExtensibility` attribute is an API breaking since it upgrades the warning to an error again. + +### Non-resilient modules + +- Adding the `@extensible` attribute is an API breaking change. +- Removing the `@extensible` attribute is an API stable change. +- Adding the `@preEnumExtensibility` attribute has no-effect since it only downgrades the error to a warning. +- Removing the `@preEnumExtensibility` attribute is an API breaking since it upgrades the warning to an error again. + +## ABI compatibility + +The new attribute does not affect the ABI of an enum since it is already the +default in resilient modules. + +## Future directions + +### Aligning the language dialects + +In a previous iteration of this proposal, we proposed to add a new language +feature to align the language dialects in a future language mode. The main +motivation behind this is that the current default of non-extensible enums is a +common pitfall and results in tremendous amounts of unnoticed API breaks in the +Swift package ecosystem. We still believe that a future proposal should try +aligning the language dialects. This proposal is focused on providing a first +step to allow extensible enums in non-resilient modules. + +Regardless of whether a future language mode changes the default for non-resilient +libraries, a way of staging in this change will be required (similar to how the +`@preconcurency` attribute facilitated incremental adoption of Swift concurrency). + +### `@unknown catch` + +Enums can be used for errors. Catching and pattern matching enums could add +support for an `@unknown catch` to make pattern matching of typed throws align +with `switch` pattern matching. + +### Allow adding additional associated values + +Adding additional associated values to an enum can also be seen as extending it +and we agree that this is interesting to explore in the future. However, this +proposal focuses on solving the primary problem of the usability of public +enumerations in non-resilient modules. + +### Larger compilation units than packages + +During the pitch it was brought up that a common pattern for application +developers is to split an application into multiple smaller packages. Those +packages are versioned together and want to have the same exhaustive matching +behavior as code within a single package. As a future direction, build and +package tooling could allow to define larger compilation units to express this. +Until then developers are encouraged to use `@frozen` attributes on their +enumerations to achieve the same effect. + +## Alternatives considered + +### Different names for the attribute + +We considered different names for the attribute such as `@nonFrozen`; however, +we felt that `@extensible` communicates the idea of an extensible enum more +clearly. From c306f2216d53175ab13f2f2900c58080ab7a26bd Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Sun, 25 May 2025 14:38:34 -0700 Subject: [PATCH 4330/4563] Number SE 0487-extensible-enums.md (#2862) --- .../{NNNN-extensible-enums.md => 0487-extensible-enums.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{NNNN-extensible-enums.md => 0487-extensible-enums.md} (99%) diff --git a/proposals/NNNN-extensible-enums.md b/proposals/0487-extensible-enums.md similarity index 99% rename from proposals/NNNN-extensible-enums.md rename to proposals/0487-extensible-enums.md index 1878defc70..d218c50789 100644 --- a/proposals/NNNN-extensible-enums.md +++ b/proposals/0487-extensible-enums.md @@ -1,6 +1,6 @@ # Extensible enums -* Proposal: [SE-NNNN](NNNN-extensible-enums.md) +* Proposal: [SE-0487](0487-extensible-enums.md) * Authors: [Pavel Yaskevich](https://github.com/xedin), [Franz Busch](https://github.com/FranzBusch), [Cory Benfield](https://github.com/lukasa) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **In active review (May 25—Jun 5, 2025)** From 1c8e13f914c31f39f6d8826c95cb8e8b09c55e8b Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Mon, 26 May 2025 11:24:16 +0200 Subject: [PATCH 4331/4563] Add forum review link to 0486-adoption-tooling-for-swift-features.md --- proposals/0486-adoption-tooling-for-swift-features.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0486-adoption-tooling-for-swift-features.md b/proposals/0486-adoption-tooling-for-swift-features.md index bf50318ac3..1e3038e362 100644 --- a/proposals/0486-adoption-tooling-for-swift-features.md +++ b/proposals/0486-adoption-tooling-for-swift-features.md @@ -3,9 +3,9 @@ * Proposal: [SE-0486](0486-adoption-tooling-for-swift-features.md) * Authors: [Anthony Latsis](https://github.com/AnthonyLatsis), [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [Franz Busch](https://github.com/FranzBusch) -* Status: **Active Review (May 21...June 4, 2025)** -* Implementation: TBD -* Review: [pitch](https://forums.swift.org/t/pitch-adoption-tooling-for-upcoming-features/77936) +* Status: **Active Review (May 26...June 8, 2025)** +* Implementation: https://github.com/swiftlang/swift-package-manager/pull/8613 +* Review: [Pitch](https://forums.swift.org/t/pitch-adoption-tooling-for-upcoming-features/77936), [Review](https://forums.swift.org/t/se-0486-migration-tooling-for-swift-features/80121) ## Introduction From 74112972c6b3284860574555ecc9d202309d30dd Mon Sep 17 00:00:00 2001 From: Jamie <2119834+jamieQ@users.noreply.github.com> Date: Mon, 26 May 2025 11:53:55 -0500 Subject: [PATCH 4332/4563] [gardening]: minor typo fixes/edits for SE-0486 --- .../0486-adoption-tooling-for-swift-features.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/0486-adoption-tooling-for-swift-features.md b/proposals/0486-adoption-tooling-for-swift-features.md index 1e3038e362..a2def8fc49 100644 --- a/proposals/0486-adoption-tooling-for-swift-features.md +++ b/proposals/0486-adoption-tooling-for-swift-features.md @@ -162,7 +162,7 @@ using `-print-diagnostic-groups` and used to associate messages with features. ### `swift package migrate` command -To enable seemless migration experience for Swift packages, I'd like to propose a new Swift Package Manager command - `swift package migrate` to complement the Swift compiler-side changes. +To enable seamless migration experience for Swift packages, I'd like to propose a new Swift Package Manager command - `swift package migrate` to complement the Swift compiler-side changes. The command would accept one or more features that have migration mode enabled and optionally a set of targets to migrate, if no targets are specified the whole package is going to be migrated to use new features. @@ -211,11 +211,11 @@ enable the feature(s) if both of the previous actions are successful: In the "whole package" mode, every target is going to be updated to include new feature flag(s). This is supported by the same functionality as `swift package add-setting` command. -If it's, for some reason, impossible to add the setting the diagnostic message would suggest what to add and where i.e. `...; please add 'ExistentialAny' feature to `MyTarget` target manually`. +If it's, for some reason, impossible to add the setting the diagnostic message would suggest what to add and where i.e. `...; please add 'ExistentialAny' feature to 'MyTarget' target manually`. #### Impact on Interface -This proposal introduces a new command but does that does not interfere with existing commands. It follows the same pattern as `swift build` and `swift test` in a consistent manner. +This proposal introduces a new command but does not interfere with existing commands. It follows the same pattern as `swift build` and `swift test` in a consistent manner. ## Source compatibility @@ -278,20 +278,20 @@ is essential for future tools built around migration mode: ### A distinct `-migrate` option -This direction has a questionably balanced set of advantanges and downsides. +This direction has a questionably balanced set of advantages and downsides. On one hand, it would provide an adequate foundation for invoking migration for a language mode in addition to individual features. On the other hand, an independent option is less discoverable, has a steeper learning curve, and makes the necessary relationships between it and the existing `-enable-*-feature` options harder to infer. Perhaps more notably, a bespoke option by itself would not scale to any future -modes, setting what might be an unfortunate example for further decentralizion +modes, setting what might be an unfortunate example for further decentralization of language feature control. ### API for package manifests The decision around surfacing migration mode in the `PackageDescription` -library depends on whether there is a concensus on the value of enabling it as +library depends on whether there is a consensus on the value of enabling it as a persistent setting as opposed to an automated procedure in the long run. Here is how an API change could look like for the proposed solution: @@ -347,7 +347,7 @@ The next candidates in line per discussions are ***adopt***, ***audit***, ### `swift package migrate` vs. `swift migrate` -Rather than have migrate as a subcommand (ie. `swift package migrate`), another option is add another top level command, ie. `swift migrate`. +Rather than have migrate as a subcommand (ie. `swift package migrate`), another option is to add another top level command, ie. `swift migrate`. As the command applies to the current package, we feel a `swift package` sub-command fits better than a new top-level command. This also aligns with the recently added package refactorings (eg. `add-target`). From 5914796ecc445e40671a7d67ba64a45295d61527 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Mon, 26 May 2025 22:23:52 +0330 Subject: [PATCH 4333/4563] Fix `SE-0487` status code fixes: ```bash curl https://download.swift.org/swift-evolution/v1/evolution.json | jq '.proposals[] | select(.id == "SE-0487") | .errors' % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 696k 100 696k 0 0 771k 0 --:--:-- --:--:-- --:--:-- 770k [ { "code": 0, "kind": "error", "message": "Missing or invalid proposal status.", "suggestion": "" } ] ``` --- proposals/0487-extensible-enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0487-extensible-enums.md b/proposals/0487-extensible-enums.md index d218c50789..9381a96d7f 100644 --- a/proposals/0487-extensible-enums.md +++ b/proposals/0487-extensible-enums.md @@ -3,7 +3,7 @@ * Proposal: [SE-0487](0487-extensible-enums.md) * Authors: [Pavel Yaskevich](https://github.com/xedin), [Franz Busch](https://github.com/FranzBusch), [Cory Benfield](https://github.com/lukasa) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **In active review (May 25—Jun 5, 2025)** +* Status: **Active Review (May 25 - Jun 5, 2025)** * Bug: [apple/swift#55110](https://github.com/swiftlang/swift/issues/55110) * Implementation: [apple/swift#80503](https://github.com/swiftlang/swift/pull/80503) * Upcoming Feature Flag: `ExtensibleAttribute` From a8f5279f553c3f386c0b072730b30c8196e40929 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Tue, 27 May 2025 14:26:42 +0330 Subject: [PATCH 4334/4563] Update proposals/0487-extensible-enums.md Co-authored-by: Ben Rimmington --- proposals/0487-extensible-enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0487-extensible-enums.md b/proposals/0487-extensible-enums.md index 9381a96d7f..9860b441e9 100644 --- a/proposals/0487-extensible-enums.md +++ b/proposals/0487-extensible-enums.md @@ -3,7 +3,7 @@ * Proposal: [SE-0487](0487-extensible-enums.md) * Authors: [Pavel Yaskevich](https://github.com/xedin), [Franz Busch](https://github.com/FranzBusch), [Cory Benfield](https://github.com/lukasa) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (May 25 - Jun 5, 2025)** +* Status: **Active Review (May 25 - June 5, 2025)** * Bug: [apple/swift#55110](https://github.com/swiftlang/swift/issues/55110) * Implementation: [apple/swift#80503](https://github.com/swiftlang/swift/pull/80503) * Upcoming Feature Flag: `ExtensibleAttribute` From 8cbeeb767b936059279aefb764c331c2c30cc00b Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 27 May 2025 16:34:49 -0400 Subject: [PATCH 4335/4563] Accept SE-0484 (#2867) --- ...allow-additional-args-to-dynamicmemberlookup-subscripts.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md b/proposals/0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md index 1e3d05745c..744cebb58b 100644 --- a/proposals/0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md +++ b/proposals/0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md @@ -3,10 +3,10 @@ * Proposal: [SE-0484](0484-allow-additional-args-to-dynamicmemberlookup-subscripts.md) * Authors: [Itai Ferber](https://github.com/itaiferber) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (May 12...25, 2025)** +* Status: **Accepted** * Implementation: [swiftlang/swift#81148](https://github.com/swiftlang/swift/pull/81148) * Previous Proposals: [SE-0195](0195-dynamic-member-lookup.md), [SE-0252](0252-keypath-dynamic-member-lookup.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-allow-additional-arguments-to-dynamicmemberlookup-subscripts/79558)) ([review](https://forums.swift.org/t/se-0484-allow-additional-arguments-to-dynamicmemberlookup-subscripts/79853)) +* Review: ([pitch](https://forums.swift.org/t/pitch-allow-additional-arguments-to-dynamicmemberlookup-subscripts/79558)) ([review](https://forums.swift.org/t/se-0484-allow-additional-arguments-to-dynamicmemberlookup-subscripts/79853)) ([acceptance](https://forums.swift.org/t/accepted-se-0484-allow-additional-arguments-to-dynamicmemberlookup-subscripts/80167)) ## Introduction From 5a3aafa47f0258f9035e14e0c3fc5d8e6e0f3210 Mon Sep 17 00:00:00 2001 From: Dmitrii Galimzianov Date: Wed, 28 May 2025 03:14:13 +0200 Subject: [PATCH 4336/4563] [SE-0480] Add Future Directions for compiler support and dev-only settings Based on the review discussion, this change expands the "Future Directions" section to include: - Considerations for supporting C/C++ compilers beyond Clang. - A discussion on formalizing "development-only" build settings. Minor fixes for clarity in the API section are also included. --- proposals/0480-swiftpm-warning-control.md | 90 ++++++++++++++++++----- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/proposals/0480-swiftpm-warning-control.md b/proposals/0480-swiftpm-warning-control.md index 18157ce824..9915b62708 100644 --- a/proposals/0480-swiftpm-warning-control.md +++ b/proposals/0480-swiftpm-warning-control.md @@ -7,6 +7,7 @@ * Implementation: [swiftlang/swift-package-manager#8315](https://github.com/swiftlang/swift-package-manager/pull/8315) * Review: ([pitch](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666)) ([review](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475)) ([returned for revision](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475/8)) * Previous Proposal: [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md) +* Previous revisions: [1](https://github.com/swiftlang/swift-evolution/blob/eed1fed8339f3919cf5c0f36b804431022d44720/proposals/0480-swiftpm-warning-control.md) ## Introduction @@ -38,16 +39,18 @@ public enum WarningLevel: String { case error } -public static func treatAllWarnings( - as level: WarningLevel, - _ condition: BuildSettingCondition? = nil -) -> SwiftSetting // or CSetting or CXXSetting - -public static func treatWarning( - _ name: String, - as level: WarningLevel, - _ condition: BuildSettingCondition? = nil -) -> SwiftSetting // or CSetting or CXXSetting +extension SwiftSetting { // Same for CSetting and CXXSetting + public static func treatAllWarnings( + as level: WarningLevel, + _ condition: BuildSettingCondition? = nil + ) -> SwiftSetting // or CSetting or CXXSetting + + public static func treatWarning( + _ name: String, + as level: WarningLevel, + _ condition: BuildSettingCondition? = nil + ) -> SwiftSetting // or CSetting or CXXSetting +} ``` #### C/C++-specific API @@ -55,15 +58,17 @@ public static func treatWarning( In C/C++ targets, we can also enable or disable specific warning groups, in addition to controlling their severity. ```swift -public static func enableWarning( - _ name: String, - _ condition: BuildSettingCondition? = nil -) -> CSetting // or CXXSetting - -public static func disableWarning( - _ name: String, - _ condition: BuildSettingCondition? = nil -) -> CSetting // or CXXSetting +extension CSetting { // Same for CXXSetting + public static func enableWarning( + _ name: String, + _ condition: BuildSettingCondition? = nil + ) -> CSetting // or CXXSetting + + public static func disableWarning( + _ name: String, + _ condition: BuildSettingCondition? = nil + ) -> CSetting // or CXXSetting +} ``` _The necessity of these functions is also explained below in the Alternatives considered section._ @@ -280,6 +285,53 @@ This necessitates separate functions to enable and disable warnings. Therefore, It has been noted that warning control settings are often similar across all targets. It makes sense to declare them at the package level while allowing target-level customizations. However, many other settings would also likely benefit from such inheritance, and SwiftPM doesn't currently provide such an option. Therefore, it was decided to factor this improvement out and look at all the settings holistically in the future. +### Support for other C/C++ Compilers + +The C/C++ warning control settings introduced in this proposal are initially implemented with Clang's warning flag syntax as the primary target. However, the API itself is largely compiler-agnostic, and there's potential to extend support to other C/C++ compilers in the future. + +For instance, many of the proposed functions could be mapped to flags for other compilers like MSVC: + +| SwiftPM Setting | Clang | MSVC (Potential Mapping) | +| :-------------------------------- | :---------------- | :----------------------- | +| `.treatAllWarnings(as: .error)` | `-Werror` | `/WX` | +| `.treatAllWarnings(as: .warning)` | `-Wno-error` | `/WX-` | +| `.treatWarning("name", as: .error)`| `-Werror=name` | `/we####` (where `####` is MSVC warning code) | +| `.treatWarning("name", as: .warning)`| `-Wno-error=name` | No direct equivalent | +| `.enableWarning("name")` | `-Wname` | `/wL####` (e.g., `/w4####` to enable at level 4) | +| `.disableWarning("name")` | `-Wno-name` | `/wd####` | + +Where direct mappings are incomplete (like `.treatWarning(as: .warning)` for MSVC, which doesn't have a per-warning equivalent to Clang's `-Wno-error=XXXX`), SwiftPM could emit diagnostics indicating the setting is not fully supported by the current compiler. If more fine-grained control is needed for a specific compiler (e.g., MSVC's warning levels `0-4` for `enableWarning`), future enhancements could introduce compiler-specific settings or extend the existing API. + +A key consideration is the handling of warning names or codes (the `"name"` parameter in the API). SwiftPM does not maintain a comprehensive list of all possible warning identifiers and their mapping across different compilers. Instead, package authors would be responsible for providing the correct warning name or code for the intended compiler. + +To facilitate this, if support for other C/C++ compilers is added, the existing `BuildSettingCondition` API could be extended to allow settings to be applied conditionally based on the active C/C++ compiler. For example: + +```swift +cxxSettings: [ + // Clang-specific warning + .enableWarning("unused-variable", .when(cxxCompiler: .clang)), + // MSVC-specific warning (using its numeric code) + .enableWarning("4101", .when(cxxCompiler: .msvc)), + // Common setting that maps well + .treatAllWarnings(as: .error) +] +``` + +This approach, combined with the existing behavior where remote (dependency) packages have their warning control flags stripped and replaced with suppression flags, would allow projects to adopt new compilers. Even if a dependency uses Clang-specific warning flags, it would not cause build failures when the main project is built with a different compiler like MSVC, as those flags would be ignored. + +### Formalizing "Development-Only" Build Settings + +The warning control settings introduced by this proposal only apply when a package is built directly and are suppressed when the package is consumed as a remote dependency. + +During the review of this proposal, it was suggested that this "development-only" characteristic could be made more explicit, perhaps by introducing a distinct category of settings (e.g., `devSwiftSettings`). This is an interesting avenue for future exploration. SwiftPM already has a few other settings that exhibit similar behavior. A dedicated future proposal for "development-only" settings could address all such use cases holistically, providing a clearer and more general mechanism for package authors to distinguish between "dev-only" settings and those that propagate to consumers. + +## Revision History + +Revisions based on review feedback: + +- Added a future direction discussing potential support for C/C++ compilers beyond Clang. +- Added a future direction to explore formalizing "dev-only" build settings. + ## Acknowledgments Thank you to [Doug Gregor](https://github.com/douggregor) for the motivation, and to both [Doug Gregor](https://github.com/douggregor) and [Holly Borla](https://github.com/hborla) for their guidance during the implementation of this API. From ee052bb9fb6674b92b7087bc2f2f320ed1d27a83 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 27 May 2025 21:26:31 -0400 Subject: [PATCH 4337/4563] Remove revision information --- proposals/0480-swiftpm-warning-control.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/proposals/0480-swiftpm-warning-control.md b/proposals/0480-swiftpm-warning-control.md index 9915b62708..9183b1091a 100644 --- a/proposals/0480-swiftpm-warning-control.md +++ b/proposals/0480-swiftpm-warning-control.md @@ -7,7 +7,6 @@ * Implementation: [swiftlang/swift-package-manager#8315](https://github.com/swiftlang/swift-package-manager/pull/8315) * Review: ([pitch](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666)) ([review](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475)) ([returned for revision](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475/8)) * Previous Proposal: [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md) -* Previous revisions: [1](https://github.com/swiftlang/swift-evolution/blob/eed1fed8339f3919cf5c0f36b804431022d44720/proposals/0480-swiftpm-warning-control.md) ## Introduction @@ -325,13 +324,6 @@ The warning control settings introduced by this proposal only apply when a packa During the review of this proposal, it was suggested that this "development-only" characteristic could be made more explicit, perhaps by introducing a distinct category of settings (e.g., `devSwiftSettings`). This is an interesting avenue for future exploration. SwiftPM already has a few other settings that exhibit similar behavior. A dedicated future proposal for "development-only" settings could address all such use cases holistically, providing a clearer and more general mechanism for package authors to distinguish between "dev-only" settings and those that propagate to consumers. -## Revision History - -Revisions based on review feedback: - -- Added a future direction discussing potential support for C/C++ compilers beyond Clang. -- Added a future direction to explore formalizing "dev-only" build settings. - ## Acknowledgments Thank you to [Doug Gregor](https://github.com/douggregor) for the motivation, and to both [Doug Gregor](https://github.com/douggregor) and [Holly Borla](https://github.com/hborla) for their guidance during the implementation of this API. From 4c4a56638f643c641dcb1c9cf9f3c2ef63258d6f Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 28 May 2025 16:20:37 -0700 Subject: [PATCH 4338/4563] [SE-0485] bugfix --- proposals/0485-outputspan.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/0485-outputspan.md b/proposals/0485-outputspan.md index d9b05b8e4c..52b257a3c8 100644 --- a/proposals/0485-outputspan.md +++ b/proposals/0485-outputspan.md @@ -126,7 +126,9 @@ extension OutputSpan where Element: ~Copyable { /// Append a single element to this `OutputSpan`. @lifetime(self: copy self) public mutating func append(_ value: consuming Element) +} +extension OutputSpan { /// Repeatedly append an element to this `OutputSpan`. @lifetime(self: copy self) public mutating func append(repeating repeatedValue: Element, count: Int) From b833a2986a7cbd22f8cad6e9cb02ea8ddf6ddd0d Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 28 May 2025 16:20:49 -0700 Subject: [PATCH 4339/4563] [SE-0485] Clarification --- proposals/0485-outputspan.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/proposals/0485-outputspan.md b/proposals/0485-outputspan.md index 52b257a3c8..a4bfc9f67f 100644 --- a/proposals/0485-outputspan.md +++ b/proposals/0485-outputspan.md @@ -473,24 +473,36 @@ Initializing an `OutputRawSpan` from a `Sequence` or other container type must u ```swift extension OutputRawSpan { /// Initialize the span's bytes with every byte of the source. + /// + /// It is a precondition that the `OutputRawSpan`'s uninitialized suffix + /// can contain every byte of the source. @lifetime(self: copy self) public mutating func append, as type: T.Type ) /// Initialize the span's bytes with every byte of the source. + /// + /// It is a precondition that the `OutputRawSpan`'s uninitialized suffix + /// can contain every byte of the source. @lifetime(self: copy self) public mutating func append, as type: T.Type ) /// Initialize the span's bytes with every byte of the source. + /// + /// It is a precondition that the `OutputRawSpan`'s uninitialized suffix + /// can contain every byte of the source. @lifetime(self: copy self) public mutating func append( contentsOf source: RawSpan ) /// Initialize the span's bytes with every byte of the source. + /// + /// It is a precondition that the `OutputRawSpan`'s uninitialized suffix + /// can contain every byte of the source. @lifetime(self: copy self) public mutating func append( contentsOf source: UnsafeRawBufferPointer From b11e17be7b5475b4d6b06c82708b1d1d8d4b3e22 Mon Sep 17 00:00:00 2001 From: Ben Rimmington Date: Fri, 23 May 2025 09:28:22 +0100 Subject: [PATCH 4340/4563] [SE-0485] Update pitch and implementation links Also rename `available` to `freeCapacity`. --- proposals/0485-outputspan.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0485-outputspan.md b/proposals/0485-outputspan.md index a4bfc9f67f..2f790b1e39 100644 --- a/proposals/0485-outputspan.md +++ b/proposals/0485-outputspan.md @@ -5,8 +5,8 @@ * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Active Review (May 20...June 3, 2025)** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) -* Implementation: "Future" target of [swift-collections](https://github.com/apple/swift-collections/tree/future) -* Review: [Pitch](https://forums.swift.org/), [Review](https://forums.swift.org/t/se-0485-outputspan-delegate-initialization-of-contiguous-memory/80032) +* Implementation: [swiftlang/swift#81637](https://github.com/swiftlang/swift/pull/81637) +* Review: [Pitch](https://forums.swift.org/t/pitch-outputspan/79473), [Review](https://forums.swift.org/t/se-0485-outputspan-delegate-initialization-of-contiguous-memory/80032) [SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md [SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md @@ -439,8 +439,8 @@ extension OutputRawSpan { /// A Boolean value indicating whither the span is full. public var isFull: Bool { get } - /// The nmuber of uninitialized bytes remaining in this `OutputRawSpan` - public var available: Int { get } // capacity - byteCount + /// The number of uninitialized bytes remaining in this `OutputRawSpan` + public var freeCapacity: Int { get } // capacity - byteCount } ``` From 26d1699d59bd0942f7b116e50926455f488a9f4a Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 29 May 2025 15:05:57 -0700 Subject: [PATCH 4341/4563] [SE-0485] fix an argument label Co-authored-by: Ben Rimmington --- proposals/0485-outputspan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0485-outputspan.md b/proposals/0485-outputspan.md index 2f790b1e39..1e4098b78f 100644 --- a/proposals/0485-outputspan.md +++ b/proposals/0485-outputspan.md @@ -457,7 +457,7 @@ extension OutputRawSpan { /// Appends the given value's bytes to this span's initialized bytes @lifetime(self: copy self) public mutating func append( - of value: T, as type: T.Type + _ value: T, as type: T.Type ) /// Appends the given value's bytes repeatedly to this span's initialized bytes From 37aafd3695a768bbb3cd32cdee4185a4ac5b85d4 Mon Sep 17 00:00:00 2001 From: Philipp Gabriel Date: Sun, 1 Jun 2025 02:21:22 +0200 Subject: [PATCH 4342/4563] Add Swift 6.1 release and 6.2 announcement (#2810) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bd24a420fa..e1f5fdb53b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ This repository tracks the ongoing evolution of the Swift programming language, | Version | Announced | Released | | :-------- | :----------------------------------------------------------------------- | :----------------------------------------------------------- | -| Swift 6.1 | [2024-10-17](https://forums.swift.org/t/swift-6-1-release-process/75442) | +| Swift 6.2 | [2025-03-08](https://forums.swift.org/t/swift-6-2-release-process/78371) | | +| Swift 6.1 | [2024-10-17](https://forums.swift.org/t/swift-6-1-release-process/75442) | [2025-03-31](https://www.swift.org/blog/swift-6.1-released/) | | Swift 6.0 | [2024-02-22](https://forums.swift.org/t/swift-6-0-release-process/70220) | [2024-09-17](https://www.swift.org/blog/announcing-swift-6/) | | Swift 5.10 | [2023-08-23](https://forums.swift.org/t/swift-5-10-release-process/66911) | [2024-03-05](https://www.swift.org/blog/swift-5.10-released/) | | Swift 5.9 | [2023-03-06](https://forums.swift.org/t/swift-5-9-release-process/63557) | [2023-09-18](https://www.swift.org/blog/swift-5.9-released/) | From 698b6ed4539e031f38e98324c0e2dd071b48a171 Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Sun, 1 Jun 2025 15:44:49 +0000 Subject: [PATCH 4343/4563] [SE-0481] Add link to review thread SE-0481 was missing a link to its review thread on the forums. --- proposals/0481-weak-let.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0481-weak-let.md b/proposals/0481-weak-let.md index 03a4cb4e5b..ca885adc1c 100644 --- a/proposals/0481-weak-let.md +++ b/proposals/0481-weak-let.md @@ -5,7 +5,7 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Accepted** * Implementation: [swiftlang/swift#80440](https://github.com/swiftlang/swift/pull/80440) -* Review: ([discussion](https://forums.swift.org/t/weak-captures-in-sendable-sending-closures/78498)) ([pitch](https://forums.swift.org/t/pitch-weak-let/79271)) ([acceptance](https://forums.swift.org/t/accepted-se-0481-weak-let/79895)) +* Review: ([discussion](https://forums.swift.org/t/weak-captures-in-sendable-sending-closures/78498)) ([pitch](https://forums.swift.org/t/pitch-weak-let/79271)) ([review](https://forums.swift.org/t/se-0481-weak-let/79603)) ([acceptance](https://forums.swift.org/t/accepted-se-0481-weak-let/79895)) [SE-0302]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md From 02f6e1b97f4addf898652f6ee0df1592aedd697c Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Wed, 4 Jun 2025 16:23:14 -0500 Subject: [PATCH 4344/4563] Rename transformIssues to compactMapIssues --- .../testing/NNNN-issue-handling-traits.md | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/proposals/testing/NNNN-issue-handling-traits.md b/proposals/testing/NNNN-issue-handling-traits.md index ff73502ab4..00bb26bc1d 100644 --- a/proposals/testing/NNNN-issue-handling-traits.md +++ b/proposals/testing/NNNN-issue-handling-traits.md @@ -126,7 +126,7 @@ Here's one contrived example showing how this could be used to add a comment to each issue recorded by a test: ```swift -@Test(.transformIssues { issue in +@Test(.compactMapIssues { issue in var issue = issue issue.comments.append("Checking whether two literals are equal") return issue @@ -173,10 +173,10 @@ are executed in trailing-to-leading, innermost-to-outermost order. For example, given the following code: ```swift -@Suite(.transformIssues { ... /* A */ }) +@Suite(.compactMapIssues { ... /* A */ }) struct ExampleSuite { @Test(.filterIssues { ... /* B */ }, - .transformIssues { ... /* C */ }) + .compactMapIssues { ... /* C */ }) func example() { ... } @@ -208,7 +208,7 @@ actor Session { } // In test code: -@Test(.transformIssues { issue in +@Test(.compactMapIssues { issue in var issue = issue if let session = Session.current { issue.comments.append("Current session ID: \(session.id)") @@ -237,7 +237,7 @@ For example: ```swift @Test( - .transformIssues { issue in + .compactMapIssues { issue in // This closure will be called for any issue recorded by the test function // or by the `.filterIssues` trait below. ... @@ -305,7 +305,7 @@ This proposal includes the following: traits. * Static functions on `Trait` for creating instances of this type with the following capabilities: - * A function `transformIssues(_:)` which returns a trait that can transform + * A function `compactMapIssues(_:)` which returns a trait that can transform recorded issues. The function takes a closure which is passed an issue and returns either a modified issue or `nil` to suppress it. * A function `filterIssues(_:)` which returns a trait that can filter recorded @@ -322,14 +322,14 @@ Below are the proposed interfaces: /// modifying one or more of its properties, and returning the copy. You can /// observe recorded issues by returning them unmodified. Or you can suppress an /// issue by either filtering it using ``Trait/filterIssues(_:)`` or returning -/// `nil` from the closure passed to ``Trait/transformIssues(_:)``. +/// `nil` from the closure passed to ``Trait/compactMapIssues(_:)``. /// /// When an instance of this trait is applied to a suite, it is recursively /// inherited by all child suites and tests. /// /// To add this trait to a test, use one of the following functions: /// -/// - ``Trait/transformIssues(_:)`` +/// - ``Trait/compactMapIssues(_:)`` /// - ``Trait/filterIssues(_:)`` public struct IssueHandlingTrait: TestTrait, SuiteTrait { /// Handle a specified issue. @@ -346,30 +346,34 @@ extension Trait where Self == IssueHandlingTrait { /// Constructs an trait that transforms issues recorded by a test. /// /// - Parameters: - /// - transformer: The closure called for each issue recorded by the test + /// - transform: A closure called for each issue recorded by the test /// this trait is applied to. It is passed a recorded issue, and returns /// an optional issue to replace the passed-in one. /// /// - Returns: An instance of ``IssueHandlingTrait`` that transforms issues. /// - /// The `transformer` closure is called synchronously each time an issue is + /// The `transform` closure is called synchronously each time an issue is /// recorded by the test this trait is applied to. The closure is passed the /// recorded issue, and if it returns a non-`nil` value, that will be recorded /// instead of the original. Otherwise, if the closure returns `nil`, the /// issue is suppressed and will not be included in the results. /// - /// The `transformer` closure may be called more than once if the test records + /// The `transform` closure may be called more than once if the test records /// multiple issues. If more than one instance of this trait is applied to a - /// test (including via inheritance from a containing suite), the `transformer` + /// test (including via inheritance from a containing suite), the `transform` /// closure for each instance will be called in right-to-left, innermost-to- /// outermost order, unless `nil` is returned, which will skip invoking the /// remaining traits' closures. /// - /// Within `transformer`, you may access the current test or test case (if any) + /// Within `transform`, you may access the current test or test case (if any) /// using ``Test/current`` ``Test/Case/current``, respectively. You may also /// record new issues, although they will only be handled by issue handling /// traits which precede this trait or were inherited from a containing suite. - public static func transformIssues(_ transformer: @escaping @Sendable (Issue) -> Issue?) -> Self + /// + /// - Note: `transform` will never be passed an issue for which the value of + /// ``Issue/kind`` is ``Issue/Kind/system``, and may not return such an + /// issue. + public static func compactMapIssues(_ transform: @escaping @Sendable (Issue) -> Issue?) -> Self /// Constructs a trait that filters issues recorded by a test. /// @@ -398,6 +402,9 @@ extension Trait where Self == IssueHandlingTrait { /// using ``Test/current`` ``Test/Case/current``, respectively. You may also /// record new issues, although they will only be handled by issue handling /// traits which precede this trait or were inherited from a containing suite. + /// + /// - Note: `isIncluded` will never be passed an issue for which the value of + /// ``Issue/kind`` is ``Issue/Kind/system``. public static func filterIssues(_ isIncluded: @escaping @Sendable (Issue) -> Bool) -> Self } ``` @@ -472,14 +479,12 @@ the handler encounters an error. ### Alternate names for the static trait functions -We could choose different names for the static `transformIssues(_:)` or +We could choose different names for the static `compactMapIssues(_:)` or `filterIssues(_:)` functions. Some alternate names considered were: -- `compactMapIssues` instead of `transformIssues`. This arguably aligns better - with the word "filter" of `filterIssues`, but it felt strange since often, - "map" is used to return a value of a _different_ type, whereas here it must - return a value of the same type, it's just that it can be mutated. -- `handleIssues` instead of `transformIssues`. The word "handle" is in the name +- `transformIssues` instead of `compactMapIssues`. "Compact map" seemed to align + better with "filter" of `filterIssues`, however. +- `handleIssues` instead of `compactMapIssues`. The word "handle" is in the name of the trait type already; it's a more general word for what all of these usage patterns enable, so it felt too broad. - Using singular "issue" rather than plural "issues" in both APIs. This may not From d537cca0d9c36e86071d2f20d3fbd38e5b236ec7 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Wed, 4 Jun 2025 16:23:25 -0500 Subject: [PATCH 4345/4563] Note latest implementation PR and reformat the list --- proposals/testing/NNNN-issue-handling-traits.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/testing/NNNN-issue-handling-traits.md b/proposals/testing/NNNN-issue-handling-traits.md index 00bb26bc1d..3ac1bc4124 100644 --- a/proposals/testing/NNNN-issue-handling-traits.md +++ b/proposals/testing/NNNN-issue-handling-traits.md @@ -4,7 +4,9 @@ * Authors: [Stuart Montgomery](https://github.com/stmontgomery) * Review Manager: TBD * Status: **Awaiting review** -* Implementation: [swiftlang/swift-testing#1080](https://github.com/swiftlang/swift-testing/pull/1080), [swiftlang/swift-testing#1121](https://github.com/swiftlang/swift-testing/pull/1121) +* Implementation: [swiftlang/swift-testing#1080](https://github.com/swiftlang/swift-testing/pull/1080), + [swiftlang/swift-testing#1121](https://github.com/swiftlang/swift-testing/pull/1121), + [swiftlang/swift-testing#1136](https://github.com/swiftlang/swift-testing/pull/1136) * Review: ([pitch](https://forums.swift.org/t/pitch-issue-handling-traits/80019)) ## Introduction From 83fca406e047ed9b791d6360bb15302ee0d6b11d Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Wed, 4 Jun 2025 16:29:12 -0500 Subject: [PATCH 4346/4563] Document special handling of system issues --- proposals/testing/NNNN-issue-handling-traits.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/proposals/testing/NNNN-issue-handling-traits.md b/proposals/testing/NNNN-issue-handling-traits.md index 3ac1bc4124..d048a1a6d4 100644 --- a/proposals/testing/NNNN-issue-handling-traits.md +++ b/proposals/testing/NNNN-issue-handling-traits.md @@ -297,6 +297,20 @@ The closure of an issue handling trait must be: - Record a separate issue using the standard issue recording mechanisms (as [discussed](#recording-issues-from-handlers) above). +### Handling of non-user issues + +Issue handling traits are applied to a test by a user, and are only intended for +handling issues recorded by tests written by the user. If an issue is recorded +by the testing library itself or the underlying system, not due to a failure +within the tests being run, such an issue will not be passed to an issue +handling trait. + +Concretely, this policy means that issues for which the value of the `kind` +property is `.system` will not be passed to the closure of an issue handling +trait. Similarly, it is not supported for a closure passed to +`compactMapIssues(_:)` to return an issue for which the value of `kind` is +`.system`. + ## Detailed design This proposal includes the following: From f5070955c1a97c6d67a34bd8c4c35583119b8f00 Mon Sep 17 00:00:00 2001 From: Philippe Hausler Date: Wed, 4 Jun 2025 15:11:46 -0700 Subject: [PATCH 4347/4563] [Observation]Add a few clarifications and refinements to address the last bits of feedback for the review thread (#2874) --- proposals/0475-observed.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/proposals/0475-observed.md b/proposals/0475-observed.md index f176573c28..368c21ef23 100644 --- a/proposals/0475-observed.md +++ b/proposals/0475-observed.md @@ -173,7 +173,7 @@ Task.detached { } ``` -In this case both tasks will get the same values upon the same events. This can +In this case both tasks will get consistently safe accessed values. This can be achieved without needing an extra buffer since the suspension of each side of the iteration are continuations resuming all together upon the accessor's execution on the specified isolation. This facilitates subject-like behavior @@ -257,6 +257,12 @@ composition of potentially failable systems. Any thrown error will mean that the terminate with that given failure. Subsequent calls then to `next` on those iterators will return `nil` - indicating that the iteration is complete. +The type `Observations` will conform to `AsyncSequence`. This means that it +adheres to the cancellation behavior of other `AsyncSequence` types; if the task +is cancelled then the iterator will return nil, and any time it becomes +terminal for any reason that sequence will remain terminal and continue returning nil. +Termination by cancellation however is independent for each instance. + ## Behavioral Notes There are a number of scenarios of iteration that can occur. These can range from production rate to iteration rate differentials to isolation differentials to concurrent iterations. Enumerating all possible combinations is of course not possible but the following explanations should illustrate some key usages. `Observations` does not make unsafe code somehow safe - the concepts of isolation protection or exclusive access are expected to be brought to the table by the types involved. It does however require the enforcements via Swift Concurrency particularly around the marking of the types and closures being required to be `Sendable`. The following examples will only illustrate well behaved types and avoid fully unsafe behavior that would lead to crashes because the types being used are circumventing that language safety. @@ -351,7 +357,7 @@ The result of the observation may print the following output, but the primary pr 3 3 ``` -This case dropped the last value of the iteration because the accumulated differential exceeded the production; however the potentially confusing part here is that the sleep in the iterate competes with the scheduling in the emitter. This becomes clearer of a relationship when the boundaries of isolation are crossed. +The last value is never observed because the program ends before it would be. If the program did not terminate then another value would be observed. Observations can be used across boundaries of concurrency. This is where the iteration is done on a different isolation than the mutations. The types however are accessed always in the isolation that the creation of the Observations closure is executed. This means that if the `Observations` instance is created on the main actor then the subsequent calls to the closure will be done on the main actor. From e877721b93ef4df0711905717ecc2d3397e940de Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Wed, 4 Jun 2025 17:12:34 -0500 Subject: [PATCH 4348/4563] Note 'inout' as an alternative considered --- proposals/testing/NNNN-issue-handling-traits.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/proposals/testing/NNNN-issue-handling-traits.md b/proposals/testing/NNNN-issue-handling-traits.md index d048a1a6d4..e9bc50af5c 100644 --- a/proposals/testing/NNNN-issue-handling-traits.md +++ b/proposals/testing/NNNN-issue-handling-traits.md @@ -493,6 +493,20 @@ allowing errors to be thrown forces the author of the issue handler to make an explicit decision about whether they want an additional issue to be recorded if the handler encounters an error. +### Make the closure's issue parameter `inout` + +The closure parameter of `compactMapIssues(_:)` currently has one parameter of +type `Issue` and returns an optional `Issue?` to support returning `nil` in +order to suppress an issue. This closure could instead have a `Void` return type +and its parameter could be `inout`, and this would mean that use cases that +involve modifying an issue would not need to first copy the issue to a variable +(`var`) before modifying it. + +However, in order to _suppress_ an issue, the parameter would also need to +become optional (`inout Issue?`) and this would mean that all usages would first +need to be unwrapped. This feels non-ergonomic, and would differ from the +standard library's typical pattern for `compactMap` functions. + ### Alternate names for the static trait functions We could choose different names for the static `compactMapIssues(_:)` or From f703cbc1a6c8aeff2818abd917819a57202ee943 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Thu, 5 Jun 2025 11:46:27 -0500 Subject: [PATCH 4349/4563] Mention the standalone function suggestion as a future direction --- proposals/testing/NNNN-issue-handling-traits.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/proposals/testing/NNNN-issue-handling-traits.md b/proposals/testing/NNNN-issue-handling-traits.md index e9bc50af5c..c6967d3b72 100644 --- a/proposals/testing/NNNN-issue-handling-traits.md +++ b/proposals/testing/NNNN-issue-handling-traits.md @@ -476,6 +476,16 @@ observing test events of all kinds. This would a much larger effort, but was as a goal in the [Swift Testing vision document](https://github.com/swiftlang/swift-evolution/blob/main/visions/swift-testing.md). +### Standalone function + +It could be useful to offer the functionality of an issue handling trait as a +standalone function (similar to `withKnownIssue { }`) so that it could be +applied to a narrower section of code than an entire test or suite. This idea +came up during the pitch phase, and we believe that sort of pattern may be +useful more broadly for other kinds of traits. Accordingly, it may make more +sense to address this in a separate proposal and design it in a way that +encompasses any trait. + ## Alternatives considered ### Allow issue handler closures to throw errors From d6f8cd455e20d82dc29f817ac8129e8364516cd6 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 3 Jun 2025 17:56:53 -0700 Subject: [PATCH 4350/4563] initial pitch --- proposals/nnnn-extracting.md | 130 +++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 proposals/nnnn-extracting.md diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md new file mode 100644 index 0000000000..c097cb762b --- /dev/null +++ b/proposals/nnnn-extracting.md @@ -0,0 +1,130 @@ +# Expand the `extracting()` slicing pattern to more types + +* Proposal: [SE-0485](0485-outputspan.md) +* Author: [Guillaume Lessard](https://github.com/glessard) +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Pitch** +* Implementation: underscored `_extracting()` members of `Span` and `RawSpan`, pending elsewhere. +* Review: Pending + +[SE-0437]: proposals/0437-noncopyable-stdlib-primitives.md +[SE-0447]: proposals/0447-span-access-shared-contiguous-storage.md +[SE-0467]: proposals/0467-MutableSpan.md +[Forum-LifetimeAnnotations]: https://forums.swift.org/t/78638 + +## Introduction and Motivation + +Slicing containers is an important operation, and non-copyable values have introduced a significant change in the spelling of that operation. When we [introduced][SE-0437] non-copyable primitives to the standard library, we allowed slicing `UnsafeBufferPointer` and related types via a family of `extracting()` methods. We expanded upon these when introducing [`MutableSpan`][SE-0467]. + +Now that we have a [stable spelling][Forum-LifetimeAnnotations] for lifetime dependencies, we propose adding the `extracting()` methods to `Span` and `RawSpan`, as well as members of the `UnsafeBufferPointer` family that were missed in [SE-0437][SE-0437]. + + + +## Proposed solution + +The family of `extracting()` methods is as follows: +```swift +public func extracting(_ bounds: Range) -> Self +public func extracting(_ bounds: some RangeExpression) -> Self +public func extracting(_ UnboundedRange) -> Self +@unsafe public func extracting(unchecked bounds: Range) -> Self +@unsafe public func extracting(unchecked bounds: ClosedRange) -> Self + +public func extracting(first maxLength: Int) -> Self +public func extracting(droppingLast k: Int) -> Self +public func extracting(last maxLength: Int) -> Self +public func extracting(droppingFirst k: Int) -> Self +``` + +These should be provided for the following standard library types: +```swift +Span +RawSpan +UnsafeBufferPointer +UnsafeMutableBufferPointer +Slice> +Slice> +UnsafeRawBufferPointer +UnsafeMutableRawBufferPointer +Slice +Slice +``` +Some of the types in the list above already have a subset of the `extracting()` functions; their support will be rounded out to the full set. + +## Detailed design + +The general declarations for these functions is as follows: +```swift +/// Returns an extracted slice over the items within +/// the supplied range of positions. +/// +/// Traps if any position within the range is invalid. +@_lifetime(copy self) +public func extracting(_ byteOffsets: Range) -> Self + +/// Returns an extracted slice over the items within +/// the supplied range of positions. +/// +/// Traps if any position within the range is invalid. +@_lifetime(copy self) +public func extracting(_ byteOffsets: some RangeExpression) -> Self + +/// Returns an extracted slice over all items of this container. +@_lifetime(copy self) +public func extracting(_ UnboundedRange) -> Self + +/// Returns an extracted slice over the items within +/// the supplied range of positions. +/// +/// This function does not validate `bounds`; this is an unsafe operation. +@unsafe @_lifetime(copy self) +public func extracting(unchecked bounds: Range) -> Self + +/// Returns an extracted slice over the items within +/// the supplied range of positions. +/// +/// This function does not validate `bounds`; this is an unsafe operation. +@unsafe @_lifetime(copy self) +public func extracting(unchecked bounds: ClosedRange) -> Self + +/// Returns an extracted slice over the initial elements +/// of this container, up to the specified maximum length. +@_lifetime(copy self) +public func extracting(first maxLength: Int) -> Self + +/// Returns an extracted slice excluding +/// the given number of trailing elements. +@_lifetime(copy self) +public func extracting(droppingLast k: Int) -> Self + +/// Returns an extracted slice containing the final elements +/// of this container, up to the given maximum length. +@_lifetime(copy self) +public func extracting(last maxLength: Int) -> Self + +/// Returns an extracted slice excluding +/// the given number of initial elements. +@_lifetime(copy self) +public func extracting(droppingFirst k: Int) -> Self +``` +For non-escapable types, the `@_lifetime` attribute is ignored as being non-applicable. + +## Source compatibility +This proposal is additive and source-copmatible with existing code. + +## ABI compatibility +This proposal is additive and ABI-compatible with existing code. + +## Implications on adoption +The additions described in this proposal require a new version of the Swift standard library. + +## Alternatives considered +This is an extension of an existing pattern. We are not considering a different pattern at this time. + +## Future directions +#### Disambiguation over ownership type +The `extracting()` functions proposed here are semantically consuming. `MutableSpan` has versions defined as mutations, but it could benefit from consuming ones as well. In order to do this, we could establish a pattern for disambiguation by name, or we could invent new syntax to disambiguate by ownership type. This is a complex topic left to future proposals. + +## Acknowledgements +Thanks to Karoy Lorentey and Tony Parker. + From 54565a0f390f8b82b17aefd27c7a1b2686606e3c Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 12:26:07 -0700 Subject: [PATCH 4351/4563] sanitize copy-pasted information --- proposals/nnnn-extracting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index c097cb762b..b82eb09824 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -1,8 +1,8 @@ # Expand the `extracting()` slicing pattern to more types -* Proposal: [SE-0485](0485-outputspan.md) +* Proposal: TBD * Author: [Guillaume Lessard](https://github.com/glessard) -* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Review Manager: TBD * Status: **Pitch** * Implementation: underscored `_extracting()` members of `Span` and `RawSpan`, pending elsewhere. * Review: Pending From a4b2002c1e2789cf9d183bb83c80a98f9752ec3c Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 14:04:18 -0700 Subject: [PATCH 4352/4563] add usage hints --- proposals/nnnn-extracting.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index b82eb09824..e65bb0f56a 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -109,6 +109,18 @@ public func extracting(droppingFirst k: Int) -> Self ``` For non-escapable types, the `@_lifetime` attribute is ignored as being non-applicable. +#### Usage hints + +The `extracting()` pattern, while not completely new, is still a departure over the slice pattern established by the `Collection` protocol. For `Span`, `RawSpan`, `MutableSpan` and `MutableRawSpan`, we can add unavailable subscripts and function with hints towards the corresponding `extracting()` function: + +```swift +@available(*, unavailable, renamed: "extracting(_ bounds:)") +public subscript(bounds: Range) -> Self { extracting(bounds) } + +@available(*, unavailable, renamed: "extracting(first:)") +public func droppingFirst(_ k: Int) -> Self { extracting(first: k) } +``` + ## Source compatibility This proposal is additive and source-copmatible with existing code. From 38a77e9d017f278381ac947cb177a9e3edbe9730 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 14:04:58 -0700 Subject: [PATCH 4353/4563] improve opening graf of proposed solution --- proposals/nnnn-extracting.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index e65bb0f56a..4e96d807a7 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -22,7 +22,9 @@ Now that we have a [stable spelling][Forum-LifetimeAnnotations] for lifetime dep ## Proposed solution -The family of `extracting()` methods is as follows: +As previously discussed in [SE-0437][SE-0437], the slicing pattern established by the `Collection` protocol cannot be generalized for either non-copyable elements or non-escapable containers. The solution is a family of functions named `extracting()`, with appropriate argument labels. + +The family of `extracting()` methods established by the [`MutableSpan` proposal][SE-0467] is as follows: ```swift public func extracting(_ bounds: Range) -> Self public func extracting(_ bounds: some RangeExpression) -> Self From 0113278618391112020934bc939b7c26ee3a3f91 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 14:05:17 -0700 Subject: [PATCH 4354/4563] spelling and formatting --- proposals/nnnn-extracting.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 4e96d807a7..1e42a040c9 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -12,6 +12,7 @@ [SE-0467]: proposals/0467-MutableSpan.md [Forum-LifetimeAnnotations]: https://forums.swift.org/t/78638 + ## Introduction and Motivation Slicing containers is an important operation, and non-copyable values have introduced a significant change in the spelling of that operation. When we [introduced][SE-0437] non-copyable primitives to the standard library, we allowed slicing `UnsafeBufferPointer` and related types via a family of `extracting()` methods. We expanded upon these when introducing [`MutableSpan`][SE-0467]. @@ -19,7 +20,6 @@ Slicing containers is an important operation, and non-copyable values have intro Now that we have a [stable spelling][Forum-LifetimeAnnotations] for lifetime dependencies, we propose adding the `extracting()` methods to `Span` and `RawSpan`, as well as members of the `UnsafeBufferPointer` family that were missed in [SE-0437][SE-0437]. - ## Proposed solution As previously discussed in [SE-0437][SE-0437], the slicing pattern established by the `Collection` protocol cannot be generalized for either non-copyable elements or non-escapable containers. The solution is a family of functions named `extracting()`, with appropriate argument labels. @@ -28,7 +28,7 @@ The family of `extracting()` methods established by the [`MutableSpan` proposal] ```swift public func extracting(_ bounds: Range) -> Self public func extracting(_ bounds: some RangeExpression) -> Self -public func extracting(_ UnboundedRange) -> Self +public func extracting(_: UnboundedRange) -> Self @unsafe public func extracting(unchecked bounds: Range) -> Self @unsafe public func extracting(unchecked bounds: ClosedRange) -> Self @@ -38,7 +38,7 @@ public func extracting(last maxLength: Int) -> Self public func extracting(droppingFirst k: Int) -> Self ``` -These should be provided for the following standard library types: +These will be provided for the following standard library types: ```swift Span RawSpan @@ -53,6 +53,7 @@ Slice ``` Some of the types in the list above already have a subset of the `extracting()` functions; their support will be rounded out to the full set. + ## Detailed design The general declarations for these functions is as follows: @@ -73,7 +74,7 @@ public func extracting(_ byteOffsets: some RangeExpression) -> Self /// Returns an extracted slice over all items of this container. @_lifetime(copy self) -public func extracting(_ UnboundedRange) -> Self +public func extracting(_: UnboundedRange) -> Self /// Returns an extracted slice over the items within /// the supplied range of positions. @@ -109,7 +110,8 @@ public func extracting(last maxLength: Int) -> Self @_lifetime(copy self) public func extracting(droppingFirst k: Int) -> Self ``` -For non-escapable types, the `@_lifetime` attribute is ignored as being non-applicable. +For escapable types, the `@_lifetime` attribute is not applied. + #### Usage hints @@ -124,7 +126,7 @@ public func droppingFirst(_ k: Int) -> Self { extracting(first: k) } ``` ## Source compatibility -This proposal is additive and source-copmatible with existing code. +This proposal is additive and source-compatible with existing code. ## ABI compatibility This proposal is additive and ABI-compatible with existing code. From 495f9be58cde92318ed00889214c6758c99c9367 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 15:16:32 -0700 Subject: [PATCH 4355/4563] formatting --- proposals/nnnn-extracting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 1e42a040c9..2c7e0cf4a7 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -113,7 +113,7 @@ public func extracting(droppingFirst k: Int) -> Self For escapable types, the `@_lifetime` attribute is not applied. -#### Usage hints +### Usage hints The `extracting()` pattern, while not completely new, is still a departure over the slice pattern established by the `Collection` protocol. For `Span`, `RawSpan`, `MutableSpan` and `MutableRawSpan`, we can add unavailable subscripts and function with hints towards the corresponding `extracting()` function: From 0184b3044b1d06a839e4bb6f08c6e2686575b8b8 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 15:16:43 -0700 Subject: [PATCH 4356/4563] clarification --- proposals/nnnn-extracting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 2c7e0cf4a7..f455481523 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -139,7 +139,7 @@ This is an extension of an existing pattern. We are not considering a different ## Future directions #### Disambiguation over ownership type -The `extracting()` functions proposed here are semantically consuming. `MutableSpan` has versions defined as mutations, but it could benefit from consuming ones as well. In order to do this, we could establish a pattern for disambiguation by name, or we could invent new syntax to disambiguate by ownership type. This is a complex topic left to future proposals. +The `extracting()` functions proposed here are borrowing. `MutableSpan` has versions defined as mutating, but it could benefit from consuming ones as well. In general there could be a need for all three ownership variants of a given operation (`borrowing`, `consuming`, or `mutating`.) In order to handle these variants, we could establish a pattern for disambiguation by name, or we could invent new syntax to disambiguate by ownership type. This is a complex topic left to future proposals. ## Acknowledgements Thanks to Karoy Lorentey and Tony Parker. From 9a1371e1d4af3a9bba5ce4209f73ec6cdeece22a Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 15:39:28 -0700 Subject: [PATCH 4357/4563] tweak title --- proposals/nnnn-extracting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index f455481523..059a458316 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -1,4 +1,4 @@ -# Expand the `extracting()` slicing pattern to more types +# Apply the extracting() slicing pattern more widely * Proposal: TBD * Author: [Guillaume Lessard](https://github.com/glessard) From cc360ddf41d12401a85109f23216f3c965b4e8ed Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 15:41:35 -0700 Subject: [PATCH 4358/4563] link to the proposal pull request --- proposals/nnnn-extracting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 059a458316..27c39171ae 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -1,6 +1,6 @@ # Apply the extracting() slicing pattern more widely -* Proposal: TBD +* Proposal: [TBD](https://github.com/swiftlang/swift-evolution/pull/2877) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: TBD * Status: **Pitch** From fa3e8a4fcb8ff271853e38a0e3dafc7d0f15f348 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 15:51:08 -0700 Subject: [PATCH 4359/4563] fix from review comments --- proposals/nnnn-extracting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 27c39171ae..096fb65d36 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -63,14 +63,14 @@ The general declarations for these functions is as follows: /// /// Traps if any position within the range is invalid. @_lifetime(copy self) -public func extracting(_ byteOffsets: Range) -> Self +public func extracting(_ bounds: Range) -> Self /// Returns an extracted slice over the items within /// the supplied range of positions. /// /// Traps if any position within the range is invalid. @_lifetime(copy self) -public func extracting(_ byteOffsets: some RangeExpression) -> Self +public func extracting(_ bounds: some RangeExpression) -> Self /// Returns an extracted slice over all items of this container. @_lifetime(copy self) From 19c78ae5b5c46a48d2a2827bc90f10ad9292f808 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 16:01:50 -0700 Subject: [PATCH 4360/4563] nit-picky wording change --- proposals/nnnn-extracting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 096fb65d36..2bfcc556d7 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -17,7 +17,7 @@ Slicing containers is an important operation, and non-copyable values have introduced a significant change in the spelling of that operation. When we [introduced][SE-0437] non-copyable primitives to the standard library, we allowed slicing `UnsafeBufferPointer` and related types via a family of `extracting()` methods. We expanded upon these when introducing [`MutableSpan`][SE-0467]. -Now that we have a [stable spelling][Forum-LifetimeAnnotations] for lifetime dependencies, we propose adding the `extracting()` methods to `Span` and `RawSpan`, as well as members of the `UnsafeBufferPointer` family that were missed in [SE-0437][SE-0437]. +Now that we have a [supported spelling][Forum-LifetimeAnnotations] for lifetime dependencies, we propose adding the `extracting()` methods to `Span` and `RawSpan`, as well as members of the `UnsafeBufferPointer` family that were missed in [SE-0437][SE-0437]. ## Proposed solution From 73c18af6acb68df94b4f35b16d7eea8b2102383c Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 5 Jun 2025 22:34:02 -0400 Subject: [PATCH 4361/4563] Accept SE-0480 --- proposals/0480-swiftpm-warning-control.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0480-swiftpm-warning-control.md b/proposals/0480-swiftpm-warning-control.md index 9183b1091a..5cdb54c201 100644 --- a/proposals/0480-swiftpm-warning-control.md +++ b/proposals/0480-swiftpm-warning-control.md @@ -3,9 +3,9 @@ * Proposal: [SE-0480](0480-swiftpm-warning-control.md) * Authors: [Dmitrii Galimzianov](https://github.com/DmT021) * Review Manager: [John McCall](https://github.com/rjmccall), [Franz Busch](https://github.com/FranzBusch) -* Status: **Returned for revision** +* Status: **Accepted** * Implementation: [swiftlang/swift-package-manager#8315](https://github.com/swiftlang/swift-package-manager/pull/8315) -* Review: ([pitch](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666)) ([review](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475)) ([returned for revision](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475/8)) +* Review: ([pitch](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666)) ([review](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475)) ([returned for revision](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475/8)) ([acceptance](https://forums.swift.org/t/accepted-se-0480-warning-control-settings-for-swiftpm/80327)) * Previous Proposal: [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md) ## Introduction From fe7db65a32433e4be5f100f9ba4838c9fd1fc672 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Fri, 6 Jun 2025 15:56:48 -0700 Subject: [PATCH 4362/4563] Update InlineArray sugar proposal from x to of (#2861) * Update 0483-inline-array-sugar.md Switch from `x` to `of` as the separator. Introduce an alternatives considered option that covers the naming of `InlineArray`. * Update 0483-inline-array-sugar.md * Update 0483-inline-array-sugar.md * Update 0483-inline-array-sugar.md * Update 0483-inline-array-sugar.md * Fix typos discovered by @ole and @benrimmington. * Minor editorial changes. * Update the review status in preparation for revision review. --------- Co-authored-by: Holly Borla --- proposals/0483-inline-array-sugar.md | 125 ++++++++++++++++++--------- 1 file changed, 82 insertions(+), 43 deletions(-) diff --git a/proposals/0483-inline-array-sugar.md b/proposals/0483-inline-array-sugar.md index 146174dc75..48dcfd6a3b 100644 --- a/proposals/0483-inline-array-sugar.md +++ b/proposals/0483-inline-array-sugar.md @@ -1,11 +1,11 @@ -# `InlineArray` Literal Syntax +# `InlineArray` Type Sugar * Proposal: [SE-0483](0483-inline-array-sugar.md) * Authors: [Hamish Knight](https://github.com/hamishknight), [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (May 2 - May 16, 2025)** +* Status: **Active Review (June 6- June 16, 2025)** * Implementation: On `main` under the `InlineArrayTypeSugar` experimental feature flag. -* Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) ([review](https://forums.swift.org/t/se-0483-inlinearray-literal-syntax/79643)) +* Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) ([first review](https://forums.swift.org/t/se-0483-inlinearray-literal-syntax/79643)) ## Introduction @@ -21,31 +21,31 @@ let fiveIntegers: InlineArray<5, Int> = .init(repeating: 99) Declaring this type is more cumbersome than its equivalent dynamically-sized array, which has sugar for the type syntax: -``` +```swift let fiveIntegers: [Int] = .init(repeating: 99, count: 5) ``` This becomes more pronounced when dealing with multiple dimensions: -``` +```swift let fiveByFive: InlineArray<5, InlineArray<5, Int>> = .init(repeating: .init(repeating: 99)) ``` +Almost every other language in a similar category to Swift – C, C++, Objective-C, Pascal, Go, Rust, Zig, Java, C# – has a simple syntax for their fixed-size array type. The introduction of a fixed-size array type into Swift should also introduce a shorthand syntax, in keeping with Swift's general approach of low ceremony and concise syntax. Swift further deviates from its peer languages by giving its _dynamic_ array type, `Array` (known in many other languages as `vector`) a sugared form. This can lead to an assumption that `Array` should be used under almost all circumstances, despite it having significant downsides in many uses (see further discussion in alternatives considered). + ## Proposed solution A new sugared version of the `InlineArray` type is proposed: ```swift -let fiveIntegers: [5 x Int] = .init(repeating: 99) +let fiveIntegers: [5 of Int] = .init(repeating: 99) ``` -The `x` here is the ASCII character, and is chosen to evoke the common shorthand use to represent "by", as in "4x4" or "2 in x 4 in". - -Note that although it is used in the manner of an operator, `x` here serves more like a contextual keyword, similar to if the syntax were `[5 of Int]`. +The choice of `of` forms something close to a grammatical phrase ("an array of five ints"). A short contextual keyword is also in keeping with Swift's tradition in other areas such as `in` or `let`. ## Detailed design -The new syntax consists of the value for the integer generic parameter and the type of the element generic parameter, separated by `x`. +The new syntax consists of the value for the integer generic parameter and the type of the element generic parameter, separated by `of`. This will be added to the grammar alongside the current type sugar: @@ -53,35 +53,35 @@ This will be added to the grammar alongside the current type sugar: > _type → sized-array-type_ > > **Grammar of a sized array type** -> _sized-array-type → [ expression `x` type ]_ +> _sized-array-type → [ expression `of` type ]_ -Note that while the grammar allows for any expression, this is currently limited to only integer literals. +Note that while the grammar allows for any expression, this is currently limited to only integer literals or integer type parameters, as required by the current implementation of `InlineArray`. If that restriction changes, so would the value allowed in the expression in the sugar. The new sugar is equivalent to declaring a type of `InlineArray`, so all rules that can be applied to the generic placeholders for the unsugared version also apply to the sugared version: -``` +```swift // Nesting let fiveByFive: InlineArray<5, InlineArray<5, Int>> = .init(repeating: .init(repeating: 99)) -let fiveByFive: [5 x [5 x Int]] = .init(repeating: .init(repeating: 99)) +let fiveByFive: [5 of [5 of Int]] = .init(repeating: .init(repeating: 99)) // Inference from context: -let fiveIntegers: [5 x _] = .init(repeating: 99) -let fourBytes: [_ x Int8] = [1,2,3,4] -let fourIntegers: [_ x _] = [1,2,3,4] +let fiveIntegers: [5 of _] = .init(repeating: 99) +let fourBytes: [_ of Int8] = [1,2,3,4] +let fourIntegers: [_ of _] = [1,2,3,4] // use on rhs -let fiveDoubles = [5 x _](repeating: 1.23) +let fiveDoubles = [5 of _](repeating: 1.23) ``` The sugar can also be used in place of the unsugared type wherever it might appear: -``` -[5 x Int](repeating: 99) -MemoryLayout<[5 x Int]>.size -unsafeBitCast((1,2,3), to: [3 x Int].self) +```swift +[5 of Int](repeating: 99) +MemoryLayout<[5 of Int]>.size +unsafeBitCast((1,2,3), to: [3 of Int].self) ``` -There must be whitespace on either side of the separator i.e. you cannot write `[5x Int]`. There are no requirements to balance whitespace, `[5 x Int]` is permitted. A new line can appear after the `x` but not before it, as while this is not ambiguous, this aids with the parser recovery logic, leading to better syntax error diagnostics. +There must be whitespace on either side of the separator; i.e., you cannot write `[5of Int]`. There are no requirements to balance whitespace; `[5 of Int]` is permitted. A new line can appear after the `of` but not before it, as while this is not ambiguous, this aids with the parser recovery logic, leading to better syntax error diagnostics. ## Source Compatibility @@ -89,63 +89,102 @@ Since it is not currently possible to write any form of the proposed syntax in S ## Impact on ABI -This is purely compile-time sugar for the existing type. It is resolved at compile time, and does not appear in the ABI nor rely on any version of the runtime. +This is purely compile-time sugar for the existing type. It is resolved at compile time and does not appear in the ABI nor rely on any version of the runtime. ## Future Directions ### Repeated value equivalent -Analogous to arrays, there is an equivalent _value_ sugar for literals of a specific size: +Analogous to arrays, there is an equivalent *value* sugar for literals of a specific size: -``` -// type inferred to be [5 x Int] -let fiveInts = [5 x 99] -// type inferred to be [5 x [5 x Int]] -let fiveByFive = [5 x [5 x 99]] +```swift +// type inferred to be [5 of Int] +let fiveInts = [5 of 99] + +// type inferred to be [5 of [5 of Int]] +let fiveByFive = [5 of [5 of 99]] ``` Unlike the sugar for the type, this would also have applicability for existing types: -``` +```swift // equivalent to .init(repeating: 99, count: 5) -let dynamic: [Int] = [5 x 99] +let dynamic: [Int] = [5 of 99] ``` This is a much bigger design space, potentially requiring a new expressible-by-literal protocol and a way to map the literal to an initializer. As such, it is left for a future proposal. +However, the choice of syntax for the type sugar has a significant impact on the viability of this future direction (see alternatives considered). Given the potential benefit of such a value syntax, any choice for the type sugar should consider its future extension to value sugar. + +[^expressions]: It could also cause confusion once expressions are allowed for declaring an `InlineArray` i.e. `[5 * 5 * Int]` would be allowed. + ### Flattened multi-dimensional arrays -For multi-dimensional arrays, `[5 x [5 x Int]]` could be flattened to `[5 x 5 x Int]` without any additional parsing issues. This could be an alternative considered, but is in future directions as it could also be introduced as sugar for the former case at a later date. +For multi-dimensional arrays, `[5 of [5 of Int]]` could be flattened to `[5 of 5 of Int]` without any additional parsing issues. This could be an alternative considered but is in future directions as it could also be introduced as sugar for the former case at a later date. ## Alternatives Considered +### Indication of the "inline" nature via the sugar. + +The naming of `InlineArray` incorporates important information about the nature of the type – that it includes its values inline rather than indirectly via a pointer. This name was chosen over other alternatives such as `FixedSizeArray` because the "inline-ness" was considered the more fundamental property, and so a better driver for the name. + +This has led to suggestions that this inline nature is important to include in the sugar was well. However, the current state privileges the position of `Array` as the only array type that is sugared. This implies that `Array` is the right choice in all circumstances, with inline arrays being a rare micro-optimization. This is not the case. + +For example, consider a translation of this code from the popular [Ray Tracing in One Weekend](https://raytracing.github.io/books/RayTracingInOneWeekend.html#thevec3class) tutorial: + +```cpp +class vec3 { + public: + double e[3]; + + double x() const { return e[0]; } + double y() const { return e[1]; } + double z() const { return e[2]; } + // etc +} +``` + +The way in which Swift privileges `[Double]` with sugar strongly implies you should use that in this translation. This would be the wrong choice. Every access to those coordinate accessors would need to check the bounds (because while the author might ensure that the value of `e` will only ever have length 3, the compiler cannot easily know this) and, in the case of mutation, a check for uniqueness of the pointer. It would also make `vec3` a nontrivial type, which has significant performance implications wherever it is used. `InlineArray<3, Double>` has none of these problems. + +Other examples include using nested `Array` types i.e. using `[[Double]]` to represent a matrix – which would also have significant negative consequences depending on the use case, compared to using an `InlineArray` to model the same values. In other cases, the "copy on write" nature of `Array` can mislead users into thinking that copies are risk-free, when actually copying an array can lead to "defeating" copy on write in subtle ways that can cause difficult-to-hunt-down performance issues. In all these cases, you need to pick the right one of two options for the performance goals you are trying to achieve. + +It is likely that Swift's choice (deviating from many of its peers) to emphasize its dynamic array through sugar, has led to a negative impact on the culture of writing performant Swift code, with the nicely sugared dynamic arrays (and array value literals) being over-favored. This is not intended to make the case that Swift should _not_ have this sugar. Dynamic arrays are widely useful and Swift's readability goals are improved by Swift having a concise syntax for creating them. But writing performant Swift code inevitably involves having an understanding of the underlying performance characteristics of _all_ the types you are using, and syntax or type naming alone cannot solve this. + +Of course, all this only matters when you are trying to write code that maximizes performance. But that is a really important use case for Swift. The goal for Swift is a language that is as safe and enjoyable to write as many high-level non-performant languages, but also can achieve peak performance when that is your goal. And the idea is that when you are targeting that level of performance, you don't have to go into "ugly, no longer nice swift" mode to do it, with nice sugared `[Double]` replaced with less pleasant full type name of `InlineArray` – something a user coming from Go or C++ or Rust might find a downgrade. Similarly, attempting to incorporate the word "inline" into the sugar e.g. `[5 inline Int]` creates a worst of both worlds solution that many would find offputting to use, without solving the fundamental issue. + +For these reasons, we should be considering `InlineArray` a peer of `Array` (even if the need for it is less common – just not "niche"), and providing a pleasant to use sugar for both types. + ### Choice of delimiter The most obvious alternative here is the choice of separator. Other options include: +- `[5 by Int]` is similar to `of`, but is less applicable to the value syntax (`[5 by 5]` doesn't read as an array of 5 instances of 5), without being clearer for the type syntax. +- `[5 x Int]`, using the ascii letter `x`, as an approximation for multiplication, reflecting common uses such as "a 4x4 vehicle". This was the choice of a previous revision of this proposal. - `[5 * Int]`, using the standard ASCII symbol for multiplication. - `[5 ⨉ Int]`, the Unicode n-ary times operator. This looks nice but is impractical as not keyboard-accessible. -- `[5; Int]` is what Rust uses, but appears to have little association with "times" or "many". Similarly other arbitrary punctuation e.g. `,` or `/` or `#`. -- `[5 of Int]` is more verbose than `x` but could be considered more clear. It has the upside or downside, depending on your preference, of being almost, but not quite, grammatical. -- `:` is of course ruled out as it is used for dictionary literals. +- `[5; Int]` is what Rust uses, but appears to have little association with "times" or "many". Similarly other arbitrary punctuation e.g. `,` or `/`. `:` is of course ruled out as it is used for dictionary literals. +- `#` does have an association with counts in some areas such as set theory, but is used as a prefix operator rather than infix i.e. `[#5 Int]`. This is less expected than the infix form, and could also be read as "the fifth `Int`". It is also unclear how this would work with expressions like an array of size `5*5`. +- No delimiter at all i.e. `[5 Int]`. While this might be made to parse, the lack of any separator is found unsettling by some users and is less visually clear, especially once expressions are allowed instead of the `5`. -Note that `*` is an existing operator, and may lead to ambiguity in future when expressions can be used to determine the size: `[5 * N * Int]`. `x` is clearer in this case: `[5 * N x Int]`. It also avoids parsing ambiguity, as the grammar does not allow two identifiers in succession. But it would be less clear if `x` also appeared as an identifier: `[5 * x x Int]` (which is not yet permitted but may be in future use cases). +Note that `*` is an existing operator, and may lead to ambiguity in future when expressions can be used to determine the size: `[5 * N * Int]`. `of` is clearer in this case: `[5 * N of Int]`. It also avoids parsing ambiguity, as the grammar does not allow two identifiers in succession. This becomes more important if the future direction of a value equivalent is pursued. `[2 * 2 * 2]` could be interpreted as `[2, 2, 2, 2]`, `[4, 4,]`, or `[8]`. -This becomes more important if the future direction of a value equivalent is pursued. `[2 * 2 * 2]` could be interpreted as `[2, 2, 2, 2]`, `[4, 4,]`, or `[8]`. +Since `of` cannot follow another identifier today, `[of of Int]` is unambiguous,[^type] but would clearly be hard to read. This is likely a hypothetical concern rather than a practical one since `of` is very rare as a variable name. The previous proposal's use of `x` involved a variable name that was more common. -Since `x` cannot follow another identifier today, `[x x Int]` is unambiguous,[^type] but would clearly be hard to read. This is likely a hypothetical concern rather than a practical one. While `x` is used often in scratch code for a local variable, a more meaningful name is usually preferable, and this would be especially the case if it is found being used for the size of an array literal. In addition, while `i`, `j`, or `n` are often legitimate counters that might be suited to the size of an array, `x` is generally not used for such things. +`x` is also less clear when used for the value version: `[5 x 5]` can be parsed unambiguously, but looks similar to five times five, and so visually has the same challenges as with the `*` operator, even if this isn't a problem for the compiler. -[^type]: or even `[x x x]`, since `x` can be a type name, albeit one that defies Swift's naming conventions. +[^type]: or even `[of of of]`, since `of` can be a type name, albeit one that defies Swift's naming conventions. Another thing to consider is how that separator looks in the fully inferred version, which tend to start to look a little like ascii diagrams: ``` +[_ of _] [_ x _] [_ * _] [_; _] -[_ of _] ``` +Of all these, the `of` choice is less susceptible to the ascii art problem. + ### Order of size and type The order of size first, then type is determined by the ordering of the unsugared type, and deviating from this for the sugared version is not an option. @@ -156,6 +195,6 @@ In theory, when using integer literals or `_` the whitespace could be omitted (` ### Choice of brackets -`InlineArray` has a lot in common with tuples – especially in sharing "copy on copy" behavior, unlike regular `Array`. So `(5 x Int)` may be an appropriate alternative to the square brackets, echoing this similarity. +`InlineArray` has a lot in common with tuples – especially in sharing "copy on copy" behavior, unlike regular `Array`. So `(5 of Int)` may be an appropriate alternative to the square brackets, echoing this similarity. However, tuples and `InlineArray`s remain very different types, and it could be misleading to imply that `InlineArray` is "just" a tuple. -Beyond varying the separator, there may be other dramatically different syntax that moves further from the "like Array sugar, but with a size argument". +Beyond varying the separator, there may be other dramatically different syntax that moves further from the "like Array sugar, but with a size argument". For example, dropping the brackets altogether (i.e. `let a: 5 of Int`). However, these are probably too much of a departure from the current Swift idioms, so likely to cause further confusion without any real upside. From 542a2a2cda2e078ffb095ed7fc8193d5ff732204 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 6 Jun 2025 17:58:24 -0500 Subject: [PATCH 4363/4563] Correct review period for SE-0483 (#2879) --- proposals/0483-inline-array-sugar.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0483-inline-array-sugar.md b/proposals/0483-inline-array-sugar.md index 48dcfd6a3b..5c469f2b55 100644 --- a/proposals/0483-inline-array-sugar.md +++ b/proposals/0483-inline-array-sugar.md @@ -3,7 +3,7 @@ * Proposal: [SE-0483](0483-inline-array-sugar.md) * Authors: [Hamish Knight](https://github.com/hamishknight), [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (June 6- June 16, 2025)** +* Status: **Active Review (June 6- June 20, 2025)** * Implementation: On `main` under the `InlineArrayTypeSugar` experimental feature flag. * Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) ([first review](https://forums.swift.org/t/se-0483-inlinearray-literal-syntax/79643)) From ea8178fe9213e566e6ef20fbe486484ca35e7276 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Fri, 6 Jun 2025 18:01:59 -0500 Subject: [PATCH 4364/4563] Link second review for SE-0483. (#2880) --- proposals/0483-inline-array-sugar.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0483-inline-array-sugar.md b/proposals/0483-inline-array-sugar.md index 5c469f2b55..4b817e413e 100644 --- a/proposals/0483-inline-array-sugar.md +++ b/proposals/0483-inline-array-sugar.md @@ -5,7 +5,7 @@ * Review Manager: [Holly Borla](https://github.com/hborla) * Status: **Active Review (June 6- June 20, 2025)** * Implementation: On `main` under the `InlineArrayTypeSugar` experimental feature flag. -* Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) ([first review](https://forums.swift.org/t/se-0483-inlinearray-literal-syntax/79643)) +* Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) ([first review](https://forums.swift.org/t/se-0483-inlinearray-literal-syntax/79643)) ([second review](https://forums.swift.org/t/second-review-se-0483-inlinearray-type-sugar/80337)) ## Introduction From ba32f1a22e0e3deae2797784764640403a4dcfe8 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 9 Jun 2025 07:37:58 -0700 Subject: [PATCH 4365/4563] Update proposals/nnnn-extracting.md Co-authored-by: Ben Rimmington --- proposals/nnnn-extracting.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 2bfcc556d7..e488808f98 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -7,9 +7,9 @@ * Implementation: underscored `_extracting()` members of `Span` and `RawSpan`, pending elsewhere. * Review: Pending -[SE-0437]: proposals/0437-noncopyable-stdlib-primitives.md -[SE-0447]: proposals/0447-span-access-shared-contiguous-storage.md -[SE-0467]: proposals/0467-MutableSpan.md +[SE-0437]: 0437-noncopyable-stdlib-primitives.md +[SE-0447]: 0447-span-access-shared-contiguous-storage.md +[SE-0467]: 0467-MutableSpan.md [Forum-LifetimeAnnotations]: https://forums.swift.org/t/78638 From ea042022ca92ab8a894f8ba7ffa283f55723f2e9 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 9 Jun 2025 19:00:31 -0700 Subject: [PATCH 4366/4563] [SE-0485] Post-review amendments and deferrals --- proposals/0485-outputspan.md | 340 +++++++---------------------------- 1 file changed, 67 insertions(+), 273 deletions(-) diff --git a/proposals/0485-outputspan.md b/proposals/0485-outputspan.md index 1e4098b78f..d6416839d5 100644 --- a/proposals/0485-outputspan.md +++ b/proposals/0485-outputspan.md @@ -79,9 +79,9 @@ extension Array { We will also extend `String`, `UnicodeScalarView` and `InlineArray` with similar initializers, and add append-in-place operations where appropriate. -#### `@lifetime` attribute +#### `@_lifetime` attribute -Some of the API presented here must establish a lifetime relationship between a non-escapable returned value and a callee binding. This relationship will be illustrated using the `@lifetime` attribute recently [pitched][PR-LifetimeAnnotations] and [formalized][Forum-LifetimeAnnotations]. For the purposes of this proposal, the lifetime attribute ties the lifetime of a function's return value to one of its input parameters. +Some of the API presented here must establish a lifetime relationship between a non-escapable returned value and a callee binding. This relationship will be illustrated using the `@_lifetime` attribute recently [pitched][PR-LifetimeAnnotations] and [formalized][Forum-LifetimeAnnotations]. For the purposes of this proposal, the lifetime attribute ties the lifetime of a function's return value to one of its input parameters. Note: The eventual lifetime annotations proposal may adopt a syntax different than the syntax used here. We expect that the Standard Library will be modified to adopt an updated lifetime dependency syntax as soon as it is finalized. @@ -124,13 +124,13 @@ The basic operation supported by `OutputSpan` is appending an element. When an e ```swift extension OutputSpan where Element: ~Copyable { /// Append a single element to this `OutputSpan`. - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func append(_ value: consuming Element) } extension OutputSpan { /// Repeatedly append an element to this `OutputSpan`. - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func append(repeating repeatedValue: Element, count: Int) } ``` @@ -141,103 +141,12 @@ extension OutputSpan where Element: ~Copyable { /// /// Returns the last element. The `OutputSpan` must not be empty. @discardableResult - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func removeLast() -> Element } ``` - -##### Bulk initialization of an `OutputSpan`'s memory: - -We include functions to perform bulk initialization of the memory represented by an `OutputSpan`. Initializing an `OutputSpan` from a `Sequence` must use every element of the source. Initializing an `OutputSpan` from `IteratorProtocol` will copy as many items as possible, either until the input is empty or the `OutputSpan`'s available storage is zero. - -See [below](#contentsOf) for a discussion of the argument labels used with the `append()` functions. - -```swift -extension OutputSpan { - /// Initialize this span's suffix with every element of the source. - /// - /// It is a precondition that the `OutputSpan`'s uninitialized suffix - /// can contain every element of the source. - @lifetime(self: copy self) - public mutating func append( - contentsOf source: consuming some Sequence - ) - - /// Initialize this span's suffix with every element of the source. - /// - /// It is a precondition that the `OutputSpan`'s uninitialized suffix - /// can contain every element of the source. - @lifetime(self: copy self) - public mutating func append( - copying source: Span - ) - - /// Initialize this span's suffix with every element of the source. - /// - /// It is a precondition that the `OutputSpan`'s uninitialized suffix - /// can contain every element of the source. - @lifetime(self: copy self) - public mutating func append( - contentsOf source: UnsafeBufferPointer - ) -} - -extension OutputSpan where Element: ~Copyable { - /// Initialize this span's suffix by moving every element from the source. - /// - /// It is a precondition that the `OutputSpan`'s uninitialized suffix - /// can contain every element of the source. - @lifetime(self: copy self) - public mutating func append( - moving source: inout OutputSpan - ) - - /// Initialize this span's suffix by moving every element from the source. - /// - /// It is a precondition that the `OutputSpan`'s uninitialized suffix - /// can contain every element of the source. - @lifetime(self: copy self) - public mutating func append( - consuming source: consuming InlineArray - ) - - /// Initialize this span's suffix by moving every element from the source. - /// - /// It is a precondition that the `OutputSpan`'s uninitialized suffix - /// can contain every element of the source. - @lifetime(self: copy self) - public mutating func append( - consuming source: UnsafeMutableBufferPointer - ) -} - -extension OutputSpan { - /// Initialize this span's suffix by moving every element from the source. - /// - /// It is a precondition that the `OutputSpan`'s uninitialized suffix - /// can contain every element of the source. - @lifetime(self: copy self) - public mutating func append( - consuming source: Slice> - ) - - /// Initialize this span's suffix with the elements from the source. - /// - /// Use this function if it isn't possible to fulfill the precondition - /// of the other `append()` functions, and to contain every element - /// of the source. - /// - /// Returns true if the iterator has filled all the free capacity in the span. - /// When the free capacity has been filled, there may be some elements - /// remaining in the iterator. - @discardableResult - @lifetime(self: copy self) - public mutating func append( - from source: inout some IteratorProtocol - ) -> Bool -} -``` +##### Bulk removals from an `OutputSpan`'s memory: Bulk operations to deinitialize some or all of an `OutputSpan`'s memory are also available: ```swift @@ -246,11 +155,11 @@ extension OutputSpan where Element: ~Copyable { /// to the uninitialized state. /// /// `n` must not be greater than `count` - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func removeLast(_ n: Int) /// Remove all this span's elements and return its memory to the uninitialized state. - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func removeAll() } ``` @@ -263,12 +172,12 @@ The initialized elements are accessible for read-only or mutating access via the extension OutputSpan where Element: ~Copyable { /// Borrow the underlying initialized memory for read-only access. public var span: Span { - @lifetime(borrow self) borrowing get + @_lifetime(borrow self) borrowing get } /// Exclusively borrow the underlying initialized memory for mutation. public mutating var mutableSpan: MutableSpan { - @lifetime(&self) mutating get + @_lifetime(&self) mutating get } } ``` @@ -291,7 +200,6 @@ extension OutputSpan where Element: ~Copyable { } ``` - ##### Interoperability with unsafe code We provide a method to process or populate an `OutputSpan` using unsafe operations, which can also be used for out-of-order initialization. @@ -319,7 +227,7 @@ extension OutputSpan where Element: ~Copyable { /// This function cannot verify these two invariants, and therefore /// this is an unsafe operation. Violating the invariants of `OutputSpan` /// may result in undefined behavior. - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func withUnsafeMutableBufferPointer( _ body: ( UnsafeMutableBufferPointer, @@ -329,7 +237,6 @@ extension OutputSpan where Element: ~Copyable { } ``` - ##### Creating an `OutputSpan` instance: Creating an `OutputSpan` is an unsafe operation. It requires having knowledge of the initialization state of the range of memory being targeted. The range of memory must be in two regions: the first region contains initialized instances of `Element`, and the second region is uninitialized. The number of initialized instances is passed to the `OutputSpan` initializer through its `initializedCount` argument. @@ -348,15 +255,11 @@ extension OutputSpan where Element: ~Copyable { /// - initializedCount: the number of initialized elements /// at the beginning of `buffer`. @unsafe - @lifetime(borrow buffer) + @_lifetime(borrow buffer) public init( buffer: UnsafeMutableBufferPointer, initializedCount: Int ) - - /// Create an OutputSpan with zero capacity - @lifetime(immortal) - public init() } extension OutputSpan { @@ -372,7 +275,7 @@ extension OutputSpan { /// - initializedCount: the number of initialized elements /// at the beginning of `buffer`. @unsafe - @lifetime(borrow buffer) + @_lifetime(borrow buffer) public init( buffer: borrowing Slice>, initializedCount: Int @@ -380,6 +283,17 @@ extension OutputSpan { } ``` +We also provide a default (no-parameter) initializer to create an empty, zero-capacity `OutputSpan`. Such an initializer is useful in order to be able to materialize an empty span for the `nil` case of an Optional, for example, or to exchange with another span in a mutable struct. + +```swift +extension OutputSpan where Element: ~Copyable { + /// Create an OutputSpan with zero capacity + @_lifetime(immortal) + public init() +} +``` + +Such an empty `OutputSpan` does not depend on a memory allocation, and therefore has the longest lifetime possible :`immortal`. This capability is important enough that we also propose to immediately define similar empty initializers for `Span`, `RawSpan`, `MutableSpan` and `MutableRawSpan`. ##### Retrieving initialized memory from an `OutputSpan` @@ -451,119 +365,62 @@ The basic operation is to append the bytes of some value to an `OutputRawSpan`. ```swift extension OutputRawSpan { /// Append a single byte to this span - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func append(_ value: UInt8) /// Appends the given value's bytes to this span's initialized bytes - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func append( _ value: T, as type: T.Type ) /// Appends the given value's bytes repeatedly to this span's initialized bytes - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func append( repeating repeatedValue: T, count: Int, as type: T.Type ) } ``` -Initializing an `OutputRawSpan` from a `Sequence` or other container type must use every element of the source. Initializing an `OutputRawSpan` from an `IteratorProtocol` instance will copy as many items as possible, either until the input is empty or the `OutputRawSpan` cannot contain another element. - -```swift -extension OutputRawSpan { - /// Initialize the span's bytes with every byte of the source. - /// - /// It is a precondition that the `OutputRawSpan`'s uninitialized suffix - /// can contain every byte of the source. - @lifetime(self: copy self) - public mutating func append, as type: T.Type - ) - - /// Initialize the span's bytes with every byte of the source. - /// - /// It is a precondition that the `OutputRawSpan`'s uninitialized suffix - /// can contain every byte of the source. - @lifetime(self: copy self) - public mutating func append, as type: T.Type - ) - - /// Initialize the span's bytes with every byte of the source. - /// - /// It is a precondition that the `OutputRawSpan`'s uninitialized suffix - /// can contain every byte of the source. - @lifetime(self: copy self) - public mutating func append( - contentsOf source: RawSpan - ) - - /// Initialize the span's bytes with every byte of the source. - /// - /// It is a precondition that the `OutputRawSpan`'s uninitialized suffix - /// can contain every byte of the source. - @lifetime(self: copy self) - public mutating func append( - contentsOf source: UnsafeRawBufferPointer - ) - - /// Initialize the span's bytes with the bytes of the elements of the source. - /// - /// Use this function if it isn't possible to fulfill the precondition - /// of the other `append()` functions, and to contain every element - /// of the source. - /// - /// Returns true if the iterator has filled all the free capacity in the span. - /// When the free capacity has been filled, there may be some elements - /// remaining in the iterator. - @lifetime(self: copy self) - public mutating func append, as type: T.Type - ) -> Bool -} -``` - An `OutputRawSpan`'s initialized memory is accessible for read-only or mutating access via the `bytes` and `mutableBytes` properties: ```swift extension OutputRawSpan { /// Borrow the underlying initialized memory for read-only access. public var bytes: RawSpan { - @lifetime(borrow self) borrowing get + @_lifetime(borrow self) borrowing get } /// Exclusively borrow the underlying initialized memory for mutation. public var mutableBytes: MutableRawSpan { - @lifetime(&self) mutating get + @_lifetime(&self) mutating get } } ``` -Methods to deinitialize memory from an `OutputRawSpan`: +Deinitializing memory from an `OutputRawSpan`: ```swift extension OutputRawSpan { /// Remove the last byte from this span - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func removeLast() -> UInt8 { /// Remove the last N elements, returning the memory they occupy /// to the uninitialized state. /// /// `n` must not be greater than `count` - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func removeLast(_ n: Int) /// Remove all this span's elements and return its memory /// to the uninitialized state. - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func removeAll() } ``` - ##### Interoperability with unsafe code We provide a method to process or populate an `OutputRawSpan` using unsafe operations, which can also be used for out-of-order initialization. @@ -591,7 +448,7 @@ extension OutputRawSpan { /// This function cannot verify these two invariants, and therefore /// this is an unsafe operation. Violating the invariants of `OutputRawSpan` /// may result in undefined behavior. - @lifetime(self: copy self) + @_lifetime(self: copy self) public mutating func withUnsafeMutableBytes( _ body: ( UnsafeMutableRawBufferPointer, @@ -601,7 +458,6 @@ extension OutputRawSpan { } ``` - ##### Creating `OutputRawSpan` instances Creating an `OutputRawSpan` is an unsafe operation. It requires having knowledge of the initialization state of the range of memory being targeted. The range of memory must be in two regions: the first region contains initialized bytes, and the second region is uninitialized. The number of initialized bytes is passed to the `OutputRawSpan` initializer through its `initializedCount` argument. @@ -621,14 +477,14 @@ extension OutputRawSpan { /// - initializedCount: the number of initialized elements /// at the beginning of `buffer`. @unsafe - @lifetime(borrow buffer) + @_lifetime(borrow buffer) public init( buffer: UnsafeMutableRawBufferPointer, initializedCount: Int ) /// Create an OutputRawSpan with zero capacity - @lifetime(immortal) + @_lifetime(immortal) public init() /// Unsafely create an OutputRawSpan over partly-initialized memory. @@ -643,7 +499,7 @@ extension OutputRawSpan { /// - initializedCount: the number of initialized elements /// at the beginning of `buffer`. @unsafe - @lifetime(borrow buffer) + @_lifetime(borrow buffer) public init( buffer: Slice, initializedCount: Int @@ -860,101 +716,55 @@ extension Data { } ``` -#### Changes to `MutableSpan` and `MutableRawSpan` +## Source compatibility -This proposal considers the naming of `OutputSpan`'s bulk-copying methods more carefully than [SE-0467][SE-0467] about `MutableSpan`. Accordingly, we would like to replace the `update()` functions proposed there in accordance with the scheme proposed in this proposal: +This proposal is additive and source-compatible with existing code. -```swift -extension MutableSpan where Element: Copyable { - public mutating func update( - fromContentsOf source: some Sequence - ) -> Index - - public mutating func update( - copying source: borrowing Span - ) -> Index - - public mutating func update( - copying source: UnsafeBufferPointer - ) -> Index -} +## ABI compatibility -extension MutableSpan where Element: ~Copyable { - public mutating func update( - moving source: inout OutputSpan - ) -> Index +This proposal is additive and ABI-compatible with existing code. - public mutating func update( - consuming source: UnsafeMutableBufferPointer - ) -> Index -} +## Implications on adoption -extension MutableSpan { - public mutating func append( - consuming source: Slice> - ) +The additions described in this proposal require a new version of the Swift standard library and runtime. - public mutating func update( - from source: inout some IteratorProtocol - ) -> Index -} -``` -Similarly, `MutableRawSpan`'s bulk-copying methods would be replaced by the following: +## Alternatives Considered -```swift -extension MutableRawSpan { - public mutating func update( - fromContentsOf source: some Sequence, as type: T.Type - ) -> Index - - public mutating func update( - fromContentsOf source: borrowing Span, as type: T.Type - ) -> Index - - public mutating func update( - fromContentsOf source: borrowing RawSpan - ) -> Index - - public mutating func append( - fromContentsOf source: UnsafeRawBufferPointer - ) -> Index - - public mutating func update( - from source: inout some IteratorProtocol, as type: T.Type - ) -> Index -} -``` +#### Vending `OutputSpan` as a property +`OutputSpan` changes the number of initialized elements in a container (or collection), and this requires some operation to update the container after the `OutputSpan` is consumed. Let's call that update operation a "cleanup" operation. The cleanup operation needs to be scheduled in some way. We could associate the cleanup with the `deinit` of `OutputSpan`, or the `deinit` of a wrapper of `OutputSpan`. Neither of these seem appealing; the mechanisms would involve an arbitrary closure executed at `deinit` time, or having to write a full wrapper for each type that vends an `OutputSpan`. We could potentially schedule the cleanup operation as part of a coroutine accessor, but these are not productized yet. The pattern established by closure-taking API is well established, and that pattern fits the needs of `OutputSpan` well. -## Source compatibility +## Future directions -This proposal is additive and source-compatible with existing code. +#### Helpers to initialize memory in an arbitrary order -## ABI compatibility +Some applications may benefit from the ability to initialize a range of memory in a different order than implemented by `OutputSpan`. This may be from back-to-front or even arbitrary order. There are many possible forms such an initialization helper can take, depending on how much memory safety the application is willing to give up in the process of initializing the memory. At the unsafe end, this can be delegating to an `UnsafeMutableBufferPointer` along with a set of requirements; this option is proposed here. At the safe end, this could be delegating to a data structure which keeps track of initialized memory using a bitmap. It is unclear how much need there is for this more heavy-handed approach, so we leave it as a future enhancement if it is deemed useful. -This proposal is additive and ABI-compatible with existing code. +#### Insertions -## Implications on adoption +A use case similar to appending is insertions. Appending is simply inserting at the end. Inserting at positions other than the end is an important capability. We expect to add insertions soon in a followup proposal if `OutputSpan` is accepted. Until then, a workaround is to append, then rotate the elements to the desired position using the `mutableSpan` view. -The additions described in this proposal require a new version of the Swift standard library and runtime. +#### Generalized removals +Similarly to generalized insertions (i.e. not from the end), we can think about removals of one or more elements starting at a given position. We expect to add generalized removals along with insertions in a followup proposal after `OutputSpan` is accepted. -## Alternatives Considered +#### Variations on `Array.append(addingCapacity:initializingWith:)` -#### Vending `OutputSpan` as a property +The function proposed here only exposes uninitialized capacity in the `OutputSpan` parameter to its closure. A different function (perhaps named `edit()`) could also pass the initialized portion of the container, allowing an algorithm to remove or to add elements. This could be considered in addition to `append()`. -`OutputSpan` changes the number of initialized elements in a container (or collection), and this requires some operation to update the container after the `OutputSpan` is consumed. Let's call that update operation a "cleanup" operation. The cleanup operation needs to be scheduled in some way. We could associate the cleanup with the `deinit` of `OutputSpan`, or the `deinit` of a wrapper of `OutputSpan`. Neither of these seem appealing; the mechanisms would involve an arbitrary closure executed at `deinit` time, or having to write a full wrapper for each type that vends an `OutputSpan`. We could potentially schedule the cleanup operation as part of a coroutine accessor, but these are not productized yet. The pattern established by closure-taking API is well established, and that pattern fits the needs of `OutputSpan` well. +#### Methods to initialize or update in bulk -#### Alternative names for `append()` methods +The `RangeReplaceableCollection` protocol has a foundational method `append(contentsOf:)` for which this document does not propose a corresponding method. We expect to first add such bulk-copying functions as part of of a package. -`OutputSpan` lays the groundwork for new, generalized `Container` protocols that will expand upon and succeed the `Collection` hierarchy while allowing non-copyability and non-escapability to be applied to both containers and elements. We hope to find method and property names that will be generally applicable. The origin of the `append(contentsOf:)` method we are expanding upon is the `RangeReplaceableCollection` protocol, which always represents copyable and escapable collections with copyable and escapable elements. The definition is as follows: `mutating func append(contentsOf newElements: __owned S)`. This supports copying elements from the source, while also destroying the source if we happen to hold its only copy. This is obviously not sufficient if the elements are non-copyable, or if we only have access to a borrow of the source. +`OutputSpan` lays the groundwork for new, generalized `Container` protocols that will expand upon and succeed the `Collection` hierarchy while allowing non-copyability and non-escapability to be applied to both containers and elements. We hope to find method and property names that will be generally applicable. The `append(contentsOf:)` method we refer to above always represents copyable and escapable collections with copyable and escapable elements. The definition is as follows: `mutating func append(contentsOf newElements: __owned S)`. This supports copying elements from the source, while also destroying the source if we happen to hold its only copy. This is obviously not sufficient if the elements are non-copyable, or if we only have access to a borrowed source. When the elements are non-copyable, we must append elements that are removed from the source. Afterwards, there are two possible dispositions of the source: destruction (`consuming`), where the source can no longer be used, or mutation (`inout`), where the source has been emptied but is still usable. -When the elements are copyable, we can simply copy the elements from the source. Afterwards, there are two possible dispositions of the source: releasing a borrowed source, or `consuming`. The latter is approximately the same behaviour as `RangeReplaceableCollection`'s `append()` function shown above. +When the elements are copyable, we can simply copy the elements from the source. Afterwards, there are two possible dispositions of the source: releasing a borrowed source, or `consuming`. The latter is approximately the same behaviour as `RangeReplaceableCollection`'s `append(contentsOf:)` function shown above. + +In an ideal world, we would like to use the same name for all of these variants: -In a more ideal world, we would like to use the same name for all of these variants: ```swift extension OutputSpan { mutating func append(contentsOf: consuming some Sequence) @@ -968,9 +778,9 @@ extension OutputSpan where Element: ~Copyable { However, this would break down in particular for `UnsafeMutableBufferPointer`, since it would make it impossible to differentiate between just copying the elements out of it, or moving its elements out (and deinitializing its memory). Once the `Container` protocols exist, we can expect that the same issue would exist for any type that conforms to more than one of the protocols involved in the list above. For example if a type conforms to `Container` as well as `Sequence`, then there would be an ambiguity. -We could fix this by extending the syntax of the language. It is already possible to overload two functions where they differ only by whether a parameter is `inout`, for example. This is a future direction. +We could fix this by extending the syntax of the language. It is already possible to overload two functions where they differ only by whether a parameter is `inout`, for example. This is a more advanced [future direction](#contentsOf-syntax). -Instead of the "ideal" solution above, this proposal includes `append()` functions in the following form: +Instead of the "ideal" solution, we could propose `append()` functions in the following form: ```swift extension OutputSpan { @@ -983,31 +793,15 @@ extension OutputSpan where Element: ~Copyable { } ``` -The `update()` methods of `MutableSpan` are updated in a similar manner, for the same reasons. +In this form, we continue to use the `contentsOf` label for `Sequence` parameters, but use different labels for the other types of containers. The `update()` methods of `MutableSpan` could be updated in a similar manner, for the same reasons. -We note that the four variants of `append()` are required for generalized containers. We can therefore expect that the names we choose will later appear on many types of collections that interact with the future `Container` protocols. This proposal's `append()` nomenclature could become ubiquitous when interacting with `Collection` instances, once it is applied to the `Container` protocol family. +We note that the four variants of `append()` are required for generalized containers. We can therefore expect that the names we choose will appear later on many types of collections that interact with the future `Container` protocols. Since this nomenclature could become ubiquitous when interacting with `Collection` and `Container` instances, we defer a formal proposal until we have more experience and feedback. -## Future directions - -#### Helpers to initialize memory in an arbitrary order - -Some applications may benefit from the ability to initialize a range of memory in a different order than implemented by `OutputSpan`. This may be from back-to-front or even arbitrary order. There are many possible forms such an initialization helper can take, depending on how much memory safety the application is willing to give up in the process of initializing the memory. At the unsafe end, this can be delegating to an `UnsafeMutableBufferPointer` along with a set of requirements; this option is proposed here. At the safe end, this could be delegating to a data structure which keeps track of initialized memory using a bitmap. It is unclear how much need there is for this more heavy-handed approach, so we leave it as a future enhancement if it is deemed useful. - -#### Insertions - -A use case similar to appending is insertions. Appending is simply inserting at the end. Inserting at positions other than the end is an important capability. We expect to add insertions soon if `OutputSpan` is accepted. Until then, a workaround is to append, then rotate the elements to the desired position using the `mutableSpan` view. The current proposal does not add a concept of indexing to `OutputSpan`, though that is required in order to support insertions at an arbitrary position. We defer this discussion in order to focus on the basic functionality of the type. - -#### Generalized removals - -Similarly to generalized insertions (i.e. not from the end), we can think about removals of one or more elements starting at a given position. This also requires adding indexing. We expect to add generalized removals along with insertions if `OutputSpan` is accepted. - -#### Variations on `Array.append(addingCapacity:initializingWith:)` - -The function proposed here only exposes uninitialized capacity in the `OutputSpan` parameter to its closure. A different function (perhaps named `edit()`) could also pass the initialized portion of the container, allowing an algorithm to remove or to add elements. This could be considered in addition to `append()`. +In the meantime, many applications will work efficiently with repeated calls to `append()` in a loop. The bulk initialization functions are implementable by using `withUnsafeBufferPointer` as a workaround as well, if performance is an issue before a package-based solution is released. #### Language syntax to distinguish between ownership modes for function arguments -In the "Alternatives Considered" subsection about [naming the `append()` methods](#contentsOf), we suggest a currently unachievable naming scheme: +In the the previous "Future Direction" subsection about [bulk initialization methods](#contentsOf), we suggest a currently unachievable naming scheme: ```swift extension OutputSpan { mutating func append(contentsOf: consuming some Sequence) From dcd4e971b1819814352ce319dca18fa0c6d73016 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Tue, 10 Jun 2025 09:28:24 -0400 Subject: [PATCH 4367/4563] Accept SE-0475 (#2881) --- proposals/0475-observed.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0475-observed.md b/proposals/0475-observed.md index 368c21ef23..70095e746c 100644 --- a/proposals/0475-observed.md +++ b/proposals/0475-observed.md @@ -3,9 +3,9 @@ * Proposal: [SE-0475](0475-observed.md) * Authors: [Philippe Hausler](https://github.com/phausler) * Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) -* Status: **Active Review (April 10 ... May 13, 2025)** +* Status: **Accepted** * Implementation: https://github.com/swiftlang/swift/pull/79817 -* Review: ([pitch](https://forums.swift.org/t/pitch-transactional-observation-of-values/78315)) ([review](https://forums.swift.org/t/se-0475-transactional-observation-of-values/79224)) +* Review: ([pitch](https://forums.swift.org/t/pitch-transactional-observation-of-values/78315)) ([review](https://forums.swift.org/t/se-0475-transactional-observation-of-values/79224)) ([acceptance](https://forums.swift.org/t/accepted-se-0475-transactional-observation-of-values/80389)) ## Introduction From 79a1a8efa26f52612f18dbdedf776fc8d121afa3 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 10 Jun 2025 07:38:21 -0700 Subject: [PATCH 4368/4563] [SE-0485] add constructor pattern to alternatives considered --- proposals/0485-outputspan.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/proposals/0485-outputspan.md b/proposals/0485-outputspan.md index d6416839d5..3466a57b89 100644 --- a/proposals/0485-outputspan.md +++ b/proposals/0485-outputspan.md @@ -735,6 +735,23 @@ The additions described in this proposal require a new version of the Swift stan `OutputSpan` changes the number of initialized elements in a container (or collection), and this requires some operation to update the container after the `OutputSpan` is consumed. Let's call that update operation a "cleanup" operation. The cleanup operation needs to be scheduled in some way. We could associate the cleanup with the `deinit` of `OutputSpan`, or the `deinit` of a wrapper of `OutputSpan`. Neither of these seem appealing; the mechanisms would involve an arbitrary closure executed at `deinit` time, or having to write a full wrapper for each type that vends an `OutputSpan`. We could potentially schedule the cleanup operation as part of a coroutine accessor, but these are not productized yet. The pattern established by closure-taking API is well established, and that pattern fits the needs of `OutputSpan` well. +#### Container construction pattern + +A constrained version of possible `OutputSpan` use consists of in-place container initialization. This proposal introduces a few initializers in this vein, such as `Array.init(capacity:initializingWith:)`, which rely on closure to establish a scope. A different approach would be to use intermediate types to perform such operations: + +```swift +struct ArrayConstructor: ~Copyable { + @_lifetime(&self) mutating var outputSpan: OutputSpan + private let _ArrayBuffer + + init(capacity: Int) +} + +extension Array { + init(_: consuming ArrayConstructor) +} +``` + ## Future directions #### Helpers to initialize memory in an arbitrary order @@ -835,7 +852,6 @@ let array = Array(capacity: buffer.count*2) { } ``` - ## Acknowledgements Thanks to Karoy Lorentey, Nate Cook and Tony Parker for their feedback. From c793e7cab939f99526c19a520cd11015c7dd4e60 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 10 Jun 2025 09:48:00 -0700 Subject: [PATCH 4369/4563] [SE-0485] notes about `MutableSpan` --- proposals/0485-outputspan.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/0485-outputspan.md b/proposals/0485-outputspan.md index 3466a57b89..d3c039734c 100644 --- a/proposals/0485-outputspan.md +++ b/proposals/0485-outputspan.md @@ -716,6 +716,10 @@ extension Data { } ``` +#### Changes to `MutableSpan` and `MutableRawSpan` + +This proposal considers the naming of `OutputSpan`'s bulk-initialization methods, and elects to defer their implementation until we have more experience with the various kinds of container we need to support. We also introduced bulk updating functions to `MutableSpan` and `MutableRawSpan` in [SE-0467][SE-0467]. It is clear that they have the same kinds of parameters as `OutputSpan`'s bulk-initialization methods, but the discussion has taken a [different direction](#contentsOf) in the latter case. We would like both of these sets of operations to match. Accordingly, we will remove the bulk `update()` functions proposedin [SE-0467][SE-0467], to be replaced with a better naming scheme later. Prototype bulk-update functionality will also be added via a package in the meantime. + ## Source compatibility This proposal is additive and source-compatible with existing code. From 4d70fc3ecc5f728f39f3ad5a153a2c049ba64186 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Thu, 12 Jun 2025 12:00:25 -0700 Subject: [PATCH 4370/4563] Add another alternative approach --- proposals/testing/XXXX-issue-severity-warning.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index dee36e088a..bc28ccd0eb 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -123,6 +123,7 @@ This revision aims to clarify the functionality and usage of the `Severity` enum - Naming of `isFailure` vs. `isFailing`: We evaluated whether to name the property `isFailing` instead of `isFailure`. The decision to use `isFailure` was made to adhere to naming conventions and ensure clarity and consistency within the API. - Severity-Only Checking: We deliberated not exposing `isFailure` and relying solely on `severity` checks. However, this was rejected because it would require test authors to overhaul their code should we introduce additional severity levels in the future. By providing `isFailure`, we offer a straightforward way to determine test outcome impact, complementing the severity feature. +- Naming `Severity.error` `Severity.failure` instead because this will always be a failing issue and test authors often think of test failures. Error and warning match build naming conventions so that is why we continued with this. ## Future directions From 98825de212306fd9d7bdf76c90d192339360b1c9 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Thu, 12 Jun 2025 12:07:31 -0700 Subject: [PATCH 4371/4563] Update Alternatives considered --- proposals/testing/XXXX-issue-severity-warning.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index bc28ccd0eb..5fcac65dc6 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -116,8 +116,6 @@ This revision aims to clarify the functionality and usage of the `Severity` enum ## Alternatives considered -- Doing Nothing: Although there have been recurring requests over the years to support non-failing issues, we did not have a comprehensive solution until now. This year, we finally had the necessary components to implement this feature effectively. - - Separate Issue Creation and Recording: We considered providing a mechanism to create issues independently before recording them, rather than passing the issue details directly to the `record` method. This approach was ultimately set aside in favor of simplicity and directness in usage. - Naming of `isFailure` vs. `isFailing`: We evaluated whether to name the property `isFailing` instead of `isFailure`. The decision to use `isFailure` was made to adhere to naming conventions and ensure clarity and consistency within the API. From baba8c7eb0c136bf112307aa29898cba8a5ad255 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Thu, 12 Jun 2025 12:09:38 -0700 Subject: [PATCH 4372/4563] Update format --- proposals/testing/XXXX-issue-severity-warning.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index 5fcac65dc6..c190c65539 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -38,7 +38,7 @@ Test authors will be able to inspect if the issue is a failing issue and will be ## Detailed design -*Severity Enum* +###Severity Enum We introduce a Severity enum to categorize issues detected during testing. This enum is crucial for distinguishing between different levels of test issues and is defined as follows: @@ -64,14 +64,14 @@ extension Issue { } ``` -*Recording Non-Failing Issues* +###Recording Non-Failing Issues To enable test authors to log non-failing issues without affecting test results, we provide a method for recording such issues: ```swift Issue.record("My comment", severity: .warning) ``` -*Issue Type Enhancements* +###Issue Type Enhancements The Issue type is enhanced with two new properties to better handle and report issues: - `severity`: This property allows access to the specific severity level of an issue, enabling more precise handling of test results. From c50f733d4dd703fcca78d3d150ba1ee13783b551 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Thu, 12 Jun 2025 12:12:21 -0700 Subject: [PATCH 4373/4563] update comment --- proposals/testing/XXXX-issue-severity-warning.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index c190c65539..e9bff1ce2c 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -47,7 +47,8 @@ The `Severity` enum: ```swift extension Issue { // ... - public enum Severity: Codable, Comparable, CustomStringConvertible, Sendable { /// The severity level for an issue which should be noted but is not + public enum Severity: Codable, Comparable, CustomStringConvertible, Sendable { + /// The severity level for an issue which should be noted but is not /// necessarily an error. /// /// An issue with warning severity does not cause the test it's associated From 7b35eac2aecb8738380a0ae5c62f91290fc6e40e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 12 Jun 2025 21:42:43 +0100 Subject: [PATCH 4374/4563] Accept SE-485: OutputSpan: delegate initialization of contiguous memory --- proposals/0485-outputspan.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0485-outputspan.md b/proposals/0485-outputspan.md index d3c039734c..8772bdecfb 100644 --- a/proposals/0485-outputspan.md +++ b/proposals/0485-outputspan.md @@ -3,10 +3,10 @@ * Proposal: [SE-0485](0485-outputspan.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Active Review (May 20...June 3, 2025)** +* Status: **Accepted** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: [swiftlang/swift#81637](https://github.com/swiftlang/swift/pull/81637) -* Review: [Pitch](https://forums.swift.org/t/pitch-outputspan/79473), [Review](https://forums.swift.org/t/se-0485-outputspan-delegate-initialization-of-contiguous-memory/80032) +* Review: [Pitch](https://forums.swift.org/t/pitch-outputspan/79473), [Review](https://forums.swift.org/t/se-0485-outputspan-delegate-initialization-of-contiguous-memory/80032), [Acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0485-outputspan-delegate-initialization-of-contiguous-memory/80435) [SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md [SE-0447]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md @@ -822,7 +822,7 @@ In the meantime, many applications will work efficiently with repeated calls to #### Language syntax to distinguish between ownership modes for function arguments -In the the previous "Future Direction" subsection about [bulk initialization methods](#contentsOf), we suggest a currently unachievable naming scheme: +In the previous "Future Direction" subsection about [bulk initialization methods](#contentsOf), we suggest a currently unachievable naming scheme: ```swift extension OutputSpan { mutating func append(contentsOf: consuming some Sequence) From 46f5a206373cc6443bf3fdd10db9987ab7f964e9 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Thu, 12 Jun 2025 15:12:00 -0700 Subject: [PATCH 4375/4563] Fix spacing and definitions --- .../testing/XXXX-issue-severity-warning.md | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index e9bff1ce2c..510050352e 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -72,6 +72,30 @@ To enable test authors to log non-failing issues without affecting test results, Issue.record("My comment", severity: .warning) ``` +Here is the `Issue.record` method definition with severity as a parameter. +```swift + /// Record an issue when a running test fails unexpectedly. + /// + /// - Parameters: + /// - comment: A comment describing the expectation. + /// - severity: The severity of the issue. + /// - sourceLocation: The source location to which the issue should be + /// attributed. + /// + /// - Returns: The issue that was recorded. + /// + /// Use this function if, while running a test, an issue occurs that cannot be + /// represented as an expectation (using the ``expect(_:_:sourceLocation:)`` + /// or ``require(_:_:sourceLocation:)-5l63q`` macros.) + @discardableResult public static func record( + _ comment: Comment? = nil, + severity: Severity = .error, + sourceLocation: SourceLocation = #_sourceLocation + ) -> Self + + // ... +``` + ###Issue Type Enhancements The Issue type is enhanced with two new properties to better handle and report issues: @@ -80,13 +104,18 @@ The Issue type is enhanced with two new properties to better handle and report i ```swift // ... +extension Issue { + /// The severity of the issue. -public var severity: Severity +public var severity: Severity { get set } + +} ``` - `isFailure`: A boolean property to determine if an issue results in a test failure, thereby helping in result aggregation and reporting. ```swift -// ... +extension Issue { + // ... /// Whether or not this issue should cause the test it's associated with to be /// considered a failure. @@ -98,7 +127,8 @@ public var severity: Severity /// /// Use this property to determine if an issue should be considered a failure, instead of /// directly comparing the value of the ``severity`` property. -public var isFailure: Bool + public var isFailure: Bool { get } +} ``` Example usage of `severity` and `isFailure: From e481b32bdd2febc410a173d4e55971a6f0b0db17 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 13 Jun 2025 09:49:15 -0400 Subject: [PATCH 4376/4563] [ST] Tweak the calling convention of exit test closures. This change removes the use of `@convention(thin)` in exit tests' closures as it is not a stable language feature. (This is a technical adjustment only and does not affect the substance of the proposal as accepted.) --- proposals/testing/0008-exit-tests.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/testing/0008-exit-tests.md b/proposals/testing/0008-exit-tests.md index a543c03981..2a69716107 100644 --- a/proposals/testing/0008-exit-tests.md +++ b/proposals/testing/0008-exit-tests.md @@ -177,7 +177,7 @@ the testing library: observing observedValues: [any PartialKeyPath & Sendable] = [], _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: @convention(thin) () async throws -> Void + performing expression: @escaping @Sendable () async throws -> Void ) -> ExitTest.Result? = #externalMacro(module: "TestingMacros", type: "ExitTestExpectMacro") /// Check that an expression causes the process to terminate in a given fashion @@ -193,7 +193,7 @@ the testing library: observing observedValues: [any PartialKeyPath & Sendable] = [], _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: @convention(thin) () async throws -> Void + performing expression: @escaping @Sendable () async throws -> Void ) -> ExitTest.Result = #externalMacro(module: "TestingMacros", type: "ExitTestRequireMacro") ``` @@ -464,8 +464,8 @@ in: There are some constraints on valid exit tests: 1. Because exit tests are run in child processes, they cannot capture any state - from the calling context (hence their body closures are `@convention(thin)` - or `@convention(c)`.) See the **Future directions** for further discussion. + from the calling context. See the **Future directions** for further + discussion. 1. Exit tests cannot recursively invoke other exit tests; this is a constraint that could potentially be lifted in the future, but it would be technically complex to do so. @@ -840,7 +840,7 @@ protocol also requires some care to ensure that signal constants such as observing observedValues: (repeat (KeyPath)) = (), _ comment: @autoclosure () -> Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation, - performing expression: @escaping @Sendable @convention(thin) () async throws -> Void + performing expression: @escaping @Sendable () async throws -> Void ) -> (repeat each T) ``` From 8957b65eb91a644ace7d44ecf56653f995c4ec4c Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Fri, 13 Jun 2025 09:44:37 -0700 Subject: [PATCH 4377/4563] Mark SE-0465 as implemented --- proposals/0465-nonescapable-stdlib-primitives.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0465-nonescapable-stdlib-primitives.md b/proposals/0465-nonescapable-stdlib-primitives.md index 2f0c6540d8..798025fef9 100644 --- a/proposals/0465-nonescapable-stdlib-primitives.md +++ b/proposals/0465-nonescapable-stdlib-primitives.md @@ -3,7 +3,7 @@ * Proposal: [SE-0465](0465-nonescapable-stdlib-primitives.md) * Authors: [Karoy Lorentey](https://github.com/lorentey) * Review Manager: [Doug Gregor](https://github.com/douggregor) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Roadmap: [Improving Swift performance predictability: ARC improvements and ownership control][Roadmap] * Implementation: https://github.com/swiftlang/swift/pull/73258 * Review: ([Acceptance](https://forums.swift.org/t/accepted-se-0465-standard-library-primitives-for-nonescapable-type/78637)) ([Review](https://forums.swift.org/t/se-0465-standard-library-primitives-for-nonescapable-types/78310)) ([Pitch](https://forums.swift.org/t/pitch-nonescapable-standard-library-primitives/77253)) From 6331dfef5171918fa07676d2f81da853877fdd60 Mon Sep 17 00:00:00 2001 From: Jaeho Yoo Date: Sat, 14 Jun 2025 04:59:14 +0900 Subject: [PATCH 4378/4563] [SE-0439] Remove redundant generic where example (#2824) * [SE-0439] Restore generic where example * [SE-0439] Remove redundant generic where example --- proposals/0439-trailing-comma-lists.md | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/proposals/0439-trailing-comma-lists.md b/proposals/0439-trailing-comma-lists.md index 1c4ada76c5..1de9044bbb 100644 --- a/proposals/0439-trailing-comma-lists.md +++ b/proposals/0439-trailing-comma-lists.md @@ -43,7 +43,7 @@ let subsequences = numbers.split( ### The Language Evolved -Back in 2016, a similar [proposal](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0084-trailing-commas.md) with a narrower scope was reviewed and rejected for Swift 3. Since that time, the language has evolved substantially that challenges the basis for rejection. The code style that "puts the terminating right parenthesis on a line following the arguments to that call" has been widely adopted by community, Swift standard library codebase, swift-format, docc documentation and Xcode. Therefore, not encouraging or endorsing this code style doesn't hold true anymore. +Back in 2016, a similar [proposal](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0084-trailing-commas.md) with a narrower scope was reviewed and rejected for Swift 3. Since that time, the language has evolved substantially that challenges the basis for rejection. The code style that "puts the terminating right parenthesis on a line following the arguments to that call" has been widely adopted by community, Swift standard library codebase, swift-format, DocC documentation and Xcode. Therefore, not encouraging or endorsing this code style doesn't hold true anymore. The language has also seen the introduction of [parameter packs](https://github.com/apple/swift-evolution/blob/main/proposals/0393-parameter-packs.md), which enables APIs that are generic over variable numbers of type parameters, and code generation tools like plugins and macros that, with trailing comma support, wouldn't have to worry about a special condition for the last element when generating comma-separated lists. @@ -267,16 +267,7 @@ if This particular case can be handled but, given how complex conditions can be, it's hard to conclude that there's absolutely no corner case where ambiguity can arise in currently valid code. -Inheritance lists and generic `where` clauses can appear in protocol definitons where there's no clear delimiter, making it harder to disambiguate where the list ends. - -```swift -protocol Foo { - associatedtype T: - P1, - P2, ❌ Expected type - ... -} -``` +Inheritance lists and generic `where` clauses can appear in protocol definitions where there's no clear delimiter, making it harder to disambiguate where the list ends. ```swift protocol Foo { From 4d88a383038a288b3f3800a057987aa8181603de Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 16 Jun 2025 10:08:48 -0700 Subject: [PATCH 4379/4563] Mark SE-0467 and SE-0456 as implemented (#2882) * [SE-0467] Mark as implemented * [SE-0456] Mark as implemented --- proposals/0456-stdlib-span-properties.md | 4 ++-- proposals/0467-MutableSpan.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0456-stdlib-span-properties.md b/proposals/0456-stdlib-span-properties.md index 178da8f82b..bffb608e00 100644 --- a/proposals/0456-stdlib-span-properties.md +++ b/proposals/0456-stdlib-span-properties.md @@ -3,9 +3,9 @@ * Proposal: [SE-0456](0456-stdlib-span-properties.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) -* Implementation: [PR #78561](https://github.com/swiftlang/swift/pull/78561) +* Implementation: [swift PR #78561](https://github.com/swiftlang/swift/pull/78561), [swift PR #80116](https://github.com/swiftlang/swift/pull/80116), [swift-foundation PR#1276](https://github.com/swiftlang/swift-foundation/pull/1276) * Review: ([pitch](https://forums.swift.org/t/76138)) ([review](https://forums.swift.org/t/se-0456-add-span-providing-properties-to-standard-library-types/77233)) ([acceptance](https://forums.swift.org/t/77684)) [SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md diff --git a/proposals/0467-MutableSpan.md b/proposals/0467-MutableSpan.md index 75bb7f7b1e..846697c393 100644 --- a/proposals/0467-MutableSpan.md +++ b/proposals/0467-MutableSpan.md @@ -3,9 +3,9 @@ * Proposal: [SE-0467](0467-MutableSpan.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Joe Groff](https://github.com/jckarter) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) -* Implementation: "Future" target of [swift-collections](https://github.com/apple/swift-collections/tree/future) +* Implementation: [PR #79650](https://github.com/swiftlang/swift/pull/79650), [PR #80517](https://github.com/swiftlang/swift/pull/80517) * Review: ([Pitch](https://forums.swift.org/t/pitch-mutablespan/77790)) ([Review](https://forums.swift.org/t/se-0467-mutablespan/78454)) ([Acceptance](https://forums.swift.org/t/accepted-se-0467-mutablespan/78875)) [SE-0446]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md From 9265769330635910e8789c7ec111b86a74e32b4f Mon Sep 17 00:00:00 2001 From: Marc Prud'hommeaux Date: Mon, 16 Jun 2025 13:09:19 -0400 Subject: [PATCH 4380/4563] [SE-0475] Fix typo (#2854) --- proposals/0475-observed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0475-observed.md b/proposals/0475-observed.md index 70095e746c..7690394895 100644 --- a/proposals/0475-observed.md +++ b/proposals/0475-observed.md @@ -103,7 +103,7 @@ that pet's name. ## Detailed design -There a few behaviors that are prerequisites to understanding the requirements +There are a few behaviors that are prerequisites to understanding the requirements of the actual design. These two key behaviors are how the model handles tearing and how the model handles sharing. From 54d4b8d8ff9e7fa9984a323875cca456cd11b1ae Mon Sep 17 00:00:00 2001 From: Jamie <2119834+jamieQ@users.noreply.github.com> Date: Mon, 16 Jun 2025 12:10:34 -0500 Subject: [PATCH 4381/4563] [gardening]: minor copy edit in footnote of 0474-yielding-accessors.md (#2841) remove a superfluous word --- proposals/0474-yielding-accessors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0474-yielding-accessors.md b/proposals/0474-yielding-accessors.md index ddc572f4a8..6ede23b7cf 100644 --- a/proposals/0474-yielding-accessors.md +++ b/proposals/0474-yielding-accessors.md @@ -220,7 +220,7 @@ To fulfill such a requirement, the conformance must provide a getter, meaning it In practical terms, this means that the requirement cannot be witnessed by a stored property or a `yielding borrow` accessor when the result is of noncopyable type,[^2] since the storage of a stored property is owned by the containing aggregate and the result of a `yielding borrow` is owned by the suspended coroutine, and it would be necessary to copy to provide ownership to the caller. However, if the type of the `get` requirement is copyable, the compiler can synthesize the getter from the other accessor kinds by introducing copies as necessary. -[^2]: While the compiler does currently accept such code currently, it does so by interpreting that `get` as a `yielding borrow`, which is a bug. +[^2]: While the compiler does currently accept such code, it does so by interpreting that `get` as a `yielding borrow`, which is a bug. ### `yielding mutate` From 6bfef2b2e1228a560b0a02adcc89f999b049aa86 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 17 Jun 2025 02:11:08 +0900 Subject: [PATCH 4382/4563] Update 0468-async-stream-continuation-hashable-conformance.md (#2806) Was done in: https://github.com/swiftlang/swift/pull/79457 We missed the 6.2 cherry pick, I'll do that --- .../0468-async-stream-continuation-hashable-conformance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0468-async-stream-continuation-hashable-conformance.md b/proposals/0468-async-stream-continuation-hashable-conformance.md index e276c80235..5412d1734e 100644 --- a/proposals/0468-async-stream-continuation-hashable-conformance.md +++ b/proposals/0468-async-stream-continuation-hashable-conformance.md @@ -3,7 +3,7 @@ * Proposal: [SE-0468](0468-async-stream-continuation-hashable-conformance.md) * Authors: [Mykola Pokhylets](https://github.com/nickolas-pohilets) * Review Manager: [Freddy Kellison-Linn](https://github.com/Jumhyn) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Implementation: [swiftlang/swift#79457](https://github.com/swiftlang/swift/pull/79457) * Review: ([pitch](https://forums.swift.org/t/pitch-add-hashable-conformance-to-asyncstream-continuation/77897)) ([review](https://forums.swift.org/t/se-0468-hashable-conformance-for-async-throwing-stream-continuation/78487)) ([acceptance](https://forums.swift.org/t/accepted-se-0468-hashable-conformance-for-async-throwing-stream-continuation/79116)) From d89189e7b438d90fd121ead562530d13962f8a37 Mon Sep 17 00:00:00 2001 From: Ole Begemann Date: Mon, 16 Jun 2025 17:12:05 +0000 Subject: [PATCH 4383/4563] [SE-0471] Make diagram readable in dark mode (#2866) This change replaces the diagram embedded in "SE-0471: Improved Custom SerialExecutor isolation checking for Concurrency Runtime" with a version that is readable in dark mode. I did this by exporting the diagram from OmniGraffle with a solid white background. The previous version had a transparent background, which made the black arrows between the boxes invisible in dark mode. --- proposals/0471-is-isolated-flow.png | Bin 84869 -> 75134 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/proposals/0471-is-isolated-flow.png b/proposals/0471-is-isolated-flow.png index bdf5bb0ba8fcef956c2260bd085be3e9517bb7c2..6a9e939edf1a6de12e379be2b227a380d70dc14e 100644 GIT binary patch literal 75134 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 From 7b4215ebc83a2f5c591edc51690365e350c3901b Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 16 Jun 2025 13:17:02 -0400 Subject: [PATCH 4384/4563] [TWG] Exit test value capturing proposal Add the initial draft of ST-NNNN Capturing values in exit tests. --- .../testing/NNNN-exit-test-value-capturing.md | 264 ++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 proposals/testing/NNNN-exit-test-value-capturing.md diff --git a/proposals/testing/NNNN-exit-test-value-capturing.md b/proposals/testing/NNNN-exit-test-value-capturing.md new file mode 100644 index 0000000000..fc6af79558 --- /dev/null +++ b/proposals/testing/NNNN-exit-test-value-capturing.md @@ -0,0 +1,264 @@ +# Capturing values in exit tests + +* Proposal: [ST-NNNN](NNNN-filename.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Review Manager: TBD +* Status: **Awaiting review** +* Bug: [swiftlang/swift-testing#1157](https://github.com/swiftlang/swift-testing/issues/1157) +* Implementation: [swiftlang/swift-testing#1040](https://github.com/swiftlang/swift-testing/pull/1040) _et al._ +* Review: ([pitch](https://forums.swift.org/t/pitch-capturing-values-in-exit-tests/80494)) + +## Introduction + +In Swift 6.2, we introduced the concept of an _exit test_: a section of code in +a test function that would run in an independent process and allow test authors +to test code that terminates the process. For example: + +```swift +enum Fruit: Sendable, Codable, Equatable { + case apple, orange, olive, tomato + var isSweet: Bool { get } + + consuming func feed(to bat: FruitBat) { + precondition(self.isSweet, "Fruit bats don't like savory fruits!") + ... + } +} + +@Test func `Fruit bats don't eat savory fruits`() async { + await #expect(processExitsWith: .failure) { + let fruit = Fruit.olive + let bat = FruitBat(named: "Chauncey") + fruit.feed(to: bat) // should trigger a precondition failure and process termination + } +} +``` + +This proposal extends exit tests to support capturing state from the enclosing +context (subject to several practical constraints.) + +## Motivation + +Exit tests in their current form are useful, but there is no reliable way to +pass non-constant information from the parent process to the child process, +which makes them difficult to use with parameterized tests. Consider: + +```swift +@Test(arguments: [Fruit.olive, .tomato]) +func `Fruit bats don't eat savory fruits`(_ fruit: Fruit) async { + await #expect(processExitsWith: .failure) { + let bat = FruitBat(named: "Chauncey") + fruit.feed(to: bat) // 🛑 can't capture 'fruit' from enclosing scope + } +} +``` + +In the above example, the test function's argument cannot be passed into the +exit test. In a trivial example like this one, it wouldn't be difficult to write +two tests that differ only in the case of `Fruit` they use in their exit test +bodies, but this approach doesn't scale very far and is generally an +anti-pattern when using Swift Testing. + +## Proposed solution + +We propose allowing the capture of values in an exit test when they are +specified in a closure capture list on the exit test's body. + +## Detailed design + +The signatures of the exit test macros `expect(processExitsWith:)` and +`require(processExitsWith:)` are unchanged. A test author may now add a closure +capture list to the body of an exit test: + +```swift +@Test(arguments: [Fruit.olive, .tomato]) +func `Fruit bats don't eat savory fruits`(_ fruit: Fruit) async { + await #expect(processExitsWith: .failure) { [fruit] in + let bat = FruitBat(named: "Chauncey") + fruit.feed(to: bat) + } +} +``` + +This feature has some necessary basic constraints: + +### Captured values must be explicitly listed in a closure capture list + +Swift Testing needs to know what values need to be encoded, sent to the child +process, and decoded. Swift macros including `#expect(processExitsWith:)` must +rely solely on syntax—that is, the code typed by a test author. An implicit +capture within an exit test body is indistinguishable from any other identifier +or symbol name. + +Hence, only values listed in the closure's capture list will be captured. +Implicitly captured values will produce a compile-time diagnostic as they do +today. + +### Captured values must conform to Sendable and Codable + +Captured values will be sent across process boundaries and, in order to support +that operation, must conform to `Codable`. As well, captured values need to make +their way through the various internal mechanisms of Swift Testing and its host +infrastructure, and so must conform to `Sendable`. Conformance to `Copyable` and +`Escapable` is implied. + +If a value that does _not_ conform to the above protocols is specified in an +exit test body's capture list, a diagnostic is emitted: + +```swift +let bat: FruitBat = ... +await #expect(processExitsWith: .failure) { [bat] in + // 🛑 Type of captured value 'bat' must conform to 'Sendable' and 'Codable' + ... +} +``` + +### Captured values' types must be visible to the exit test macro + +In order for us to successfully _decode_ captured values in the child process, +we must know their Swift types. Type information is not readily available during +macro expansion and we must, in general, rely on the parsed syntax tree for it. + +The type of `self` and the types of arguments to the calling function are, +generally, known and can be inferred from context[^shadows]. The types of other +values, including local variables and global state, are not visible in the +syntax tree and must be specified explicitly in the capture list using an `as` +expression: + +```swift +await #expect(processExitsWith: .failure) { [fruit = fruit as Fruit] in + ... +} +``` + +Finally, the types of captured literals (e.g. `[x = 123]`) are known at compile +time and can always be inferred as `IntegerLiteralType` etc., although we don't +anticipate this will be particularly useful in practice. + +If the type of a captured value cannot be resolved from context, the test author +will see an error at compile time: + +```swift +await #expect(processExitsWith: .failure) { [fruit] in + // 🛑 Type of captured value 'fruit' is ambiguous + // Fix-It: Add '= fruit as T' + ... +} +``` + +See the **Future directions** section of this proposal for more information on +how we hope to lift this constraint. If we are able to lift this constraint in +the future, we expect it will not require (no pun intended) a second Swift +Evolution proposal. + +[^shadows]: If a local variable is declared that shadows `self` or a function + argument, we may incorrectly infer the type of that value when captured. When + this occurs, Swift Testing emits a diagnostic of the form "🛑 Type of captured + value 'foo' is ambiguous". + +## Source compatibility + +This change is additive and relies on syntax that would previously be rejected +at compile time. + +## Integration with supporting tools + +Xcode, Swift Package Manager, and the Swift VS Code plugin _already_ support +captured values in exit tests as they use Swift Testing's built-in exit test +handling logic. + +Tools that implement their own exit test handling logic will need to account for +captured values. The `ExitTest` type now has a new SPI property: + +```swift +extension ExitTest { + /// The set of values captured in the parent process before the exit test is + /// called. + /// + /// This property is automatically set by the testing library when using the + /// built-in exit test handler and entry point functions. Do not modify the + /// value of this property unless you are implementing a custom exit test + /// handler or entry point function. + /// + /// The order of values in this array must be the same between the parent and + /// child processes. + @_spi(ForToolsIntegrationOnly) + public var capturedValues: [CapturedValue] { get set } +} +``` + +In the parent process (that is, for an instance of `ExitTest` passed to +`Configuration.exitTestHandler`), this property represents the values captured +at runtime by the exit test. In the child process (that is, for an instance of +`ExitTest` returned from `ExitTest.find(identifiedBy:)`), the elements in this +array do not have values associated with them until the hosting tool provides +them. + +## Future directions + +- Supporting captured values without requiring type information + + We need the types of captured values in order to successfully decode them, but + we are constrained by macros being syntax-only. In the future, the compiler + may gain a language feature similar to `decltype()` in C++ or `typeof()` in + C23, in which case we should be able to use it and avoid the need for explicit + types in the capture list. ([rdar://153389205](rdar://153389205)) + +- Supporting capturing values that do not conform to `Codable` + + Alternatives to `Codable` exist or have been proposed, such as + [`NSSecureCoding`](https://developer.apple.com/documentation/foundation/nssecurecoding) + or [`JSONCodable`](https://forums.swift.org/t/the-future-of-serialization-deserialization-apis/78585). + In the future, we may want to extend support for values that conform to these + protocols instead of `Codable`. + +## Alternatives considered + +- Doing nothing. There is sufficient motivation to support capturing values in + exit tests and it is within our technical capabilities. + +- Passing captured values as arguments to `#expect(processExitsWith:)` and its + body closure. For example: + + ```swift + await #expect( + processExitsWith: .failure, + arguments: [fruit, bat] + ) { fruit, bat in + ... + } + ``` + + This is technically feasible, but: + + - It requires that the caller state the capture list twice; + - Type information still isn't available for captured values, so you'd still + need to _actually_ write `{ (fruit: Fruit, bat: Bat) in ... }` (or otherwise + specify the types somewhere in the macro invocation); and + - The language already has a dedicated syntax for specifying lists of values + that should be captured in a closure. + +- Supporting non-`Sendable` or non-`Codable` captured values. Since exit tests' + bodies are, by definition, in separate isolation domains from the caller, and + since they, by nature, run in separate processes, conformance to these + protocols is fundamentally necessary. + +- Implicitly capturing `self`. This would require us to statically detect during + macro expansion whether `self` conformed to the necessary protocols _and_ + would preclude capturing any state from static or free test functions. + +- Forking the exit test process such that all captured values are implicitly + copied by the kernel into the new process. Forking, in the UNIX fashion, is + fundamentally incompatible with the Swift runtime and the Swift thread pool. + On Darwin, you [cannot fork a process that links to Core Foundation without + immediately calling `exec()`](https://duckduckgo.com/?q=__THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__), + and `fork()` isn't even present on Windows. + +## Acknowledgments + +Thanks to @rintaro for assistance investigating swift-syntax diagnostic support +and to @xedin for humouring my questions about `decltype()`. + +Thanks to the Swift Testing team and the Testing Workgroup as always. And thanks +to those individuals, who shall remain unnamed, who nerd-sniped me into building +this feature. From b9981317f60d0f9e396eddadfba09089e7a61f8f Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Tue, 17 Jun 2025 16:25:29 -0700 Subject: [PATCH 4385/4563] Update 0486-adoption-tooling-for-swift-features.md --- proposals/0486-adoption-tooling-for-swift-features.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0486-adoption-tooling-for-swift-features.md b/proposals/0486-adoption-tooling-for-swift-features.md index a2def8fc49..81f3424f5e 100644 --- a/proposals/0486-adoption-tooling-for-swift-features.md +++ b/proposals/0486-adoption-tooling-for-swift-features.md @@ -3,7 +3,7 @@ * Proposal: [SE-0486](0486-adoption-tooling-for-swift-features.md) * Authors: [Anthony Latsis](https://github.com/AnthonyLatsis), [Pavel Yaskevich](https://github.com/xedin) * Review Manager: [Franz Busch](https://github.com/FranzBusch) -* Status: **Active Review (May 26...June 8, 2025)** +* Status: **Implemented (Swift 6.2)** * Implementation: https://github.com/swiftlang/swift-package-manager/pull/8613 * Review: [Pitch](https://forums.swift.org/t/pitch-adoption-tooling-for-upcoming-features/77936), [Review](https://forums.swift.org/t/se-0486-migration-tooling-for-swift-features/80121) @@ -172,7 +172,7 @@ The command would accept one or more features that have migration mode enabled a USAGE: swift package migrate [] --to-feature ... OPTIONS: - --targets The targets to migrate to specified set of features or a new language mode. + --target The targets to migrate to specified set of features or a new language mode. --to-feature The Swift language upcoming/experimental feature to migrate to. -h, --help Show help information. @@ -181,7 +181,7 @@ OPTIONS: #### Use case ``` -swift package migrate --targets MyTarget,MyTest --to-feature ExistentialAny +swift package migrate --target MyTarget,MyTest --to-feature ExistentialAny ``` This command would attempt to build `MyTarget` and `MyTest` targets with `ExistentialAny:migrate` feature flag, apply any fix-its associated with From 3abda053af70db36b8b1fb78e27a95cf0d8040cd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 23 Jun 2025 18:32:36 -0700 Subject: [PATCH 4386/4563] [Memory safety vision] Fix typos --- visions/memory-safety.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/visions/memory-safety.md b/visions/memory-safety.md index 612695d7fe..69a206bc79 100644 --- a/visions/memory-safety.md +++ b/visions/memory-safety.md @@ -15,7 +15,7 @@ While there are a number of potential definitions for memory safety, the one pro * **Lifetime safety** : all accesses to a value are guaranteed to occur during its lifetime. Violations of this property, such as accessing a value after its lifetime has ended, are often called use-after-free errors. * **Bounds safety**: all accesses to memory are within the intended bounds of the memory allocation, such as accessing elements in an array. Violations of this property are called out-of-bounds accesses. * **Type safety** : all accesses to a value use the type to which it was initialized, or a type that is compatible with that type. For example, one cannot access a `String` value as if it were an `Array`. Violations of this property are called type confusions. -* **Initialization safety** : all values are initialized property to being used, so they cannot contain unexpected data. Violations of this property often lead to information disclosures (where data that should be invisible becomes available) or even other memory-safety issues like use-after-frees or type confusions. +* **Initialization safety** : all values are initialized prior to being used, so they cannot contain unexpected data. Violations of this property often lead to information disclosures (where data that should be invisible becomes available) or even other memory-safety issues like use-after-frees or type confusions. * **Thread safety:** all values are accessed concurrently in a manner that is synchronized sufficiently to maintain their invariants. Violations of this property are typically called data races, and can lead to any of the other memory safety problems. Since its inception, Swift has provided memory safety for the first four dimensions. Lifetime safety is provided for reference types by automatic reference counting and for value types via [memory exclusivity](https://www.swift.org/blog/swift-5-exclusivity/); bounds safety is provided by bounds-checking on `Array` and other collections; type safety is provided by safe features for casting (`as?` , `is` ) and `enum` s; and initialization safety is provided by “definite initialization”, which doesn’t allow a variable to be accessed until it has been defined. Swift 6’s strict concurrency checking extends Swift’s memory safety guarantees to the last dimension. @@ -134,10 +134,10 @@ One of the primary places where this doesn’t hold is with low-level access to [Non-escapable types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0446-non-escapable.md) provide the ability to create types whose instances cannot escape out of the context in which they were created with no runtime overhead. Non-escapable types allow the creation of a [memory-safe counterpart to the unsafe buffer types](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md), `Span` . With `Span` , it becomes possible to access contiguous memory in an array in a manner that maintains memory safety. For example: ```swift -let span = myInts.storage +let span = myInts.span globalSpan = span // error: span value cannot escape the scope of myInts -print(span[myArray.count]) // runtime error: out-of-bounds access +print(span[myInts.count]) // runtime error: out-of-bounds access print(span.first ?? 0) ``` From e1849d9cb0812e55451d3970197d4f880ff92623 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Tue, 24 Jun 2025 13:54:00 -0500 Subject: [PATCH 4387/4563] Set Paul as the review manager per our discussion --- proposals/testing/NNNN-issue-handling-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/NNNN-issue-handling-traits.md b/proposals/testing/NNNN-issue-handling-traits.md index c6967d3b72..27b2356d8d 100644 --- a/proposals/testing/NNNN-issue-handling-traits.md +++ b/proposals/testing/NNNN-issue-handling-traits.md @@ -2,7 +2,7 @@ * Proposal: [ST-NNNN](NNNN-issue-handling-trait.md) * Authors: [Stuart Montgomery](https://github.com/stmontgomery) -* Review Manager: TBD +* Review Manager: [Paul LeMarquand](https://github.com/plemarquand) * Status: **Awaiting review** * Implementation: [swiftlang/swift-testing#1080](https://github.com/swiftlang/swift-testing/pull/1080), [swiftlang/swift-testing#1121](https://github.com/swiftlang/swift-testing/pull/1121), From 674cf6d292eb5917f91121cd7992a01412eb2305 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Tue, 24 Jun 2025 15:05:14 -0400 Subject: [PATCH 4388/4563] Rename to ST-0011 and add review dates --- ...NNN-issue-handling-traits.md => 0011-issue-handling-traits.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/testing/{NNNN-issue-handling-traits.md => 0011-issue-handling-traits.md} (100%) diff --git a/proposals/testing/NNNN-issue-handling-traits.md b/proposals/testing/0011-issue-handling-traits.md similarity index 100% rename from proposals/testing/NNNN-issue-handling-traits.md rename to proposals/testing/0011-issue-handling-traits.md From d9789c01f02e2bc81b4ca5e51d7962f8f5e0613f Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Tue, 24 Jun 2025 15:07:20 -0400 Subject: [PATCH 4389/4563] Update dates --- proposals/testing/0011-issue-handling-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0011-issue-handling-traits.md b/proposals/testing/0011-issue-handling-traits.md index 27b2356d8d..5b18aeda58 100644 --- a/proposals/testing/0011-issue-handling-traits.md +++ b/proposals/testing/0011-issue-handling-traits.md @@ -3,7 +3,7 @@ * Proposal: [ST-NNNN](NNNN-issue-handling-trait.md) * Authors: [Stuart Montgomery](https://github.com/stmontgomery) * Review Manager: [Paul LeMarquand](https://github.com/plemarquand) -* Status: **Awaiting review** +* Status: **Active Review (Jun 25 - July 8, 2025)** * Implementation: [swiftlang/swift-testing#1080](https://github.com/swiftlang/swift-testing/pull/1080), [swiftlang/swift-testing#1121](https://github.com/swiftlang/swift-testing/pull/1121), [swiftlang/swift-testing#1136](https://github.com/swiftlang/swift-testing/pull/1136) From 6bdedfe3302f02b076c711f89db07afff09dd92e Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Tue, 24 Jun 2025 15:09:13 -0400 Subject: [PATCH 4390/4563] Update proposal name --- proposals/testing/0011-issue-handling-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0011-issue-handling-traits.md b/proposals/testing/0011-issue-handling-traits.md index 5b18aeda58..4c2772415d 100644 --- a/proposals/testing/0011-issue-handling-traits.md +++ b/proposals/testing/0011-issue-handling-traits.md @@ -1,6 +1,6 @@ # Issue Handling Traits -* Proposal: [ST-NNNN](NNNN-issue-handling-trait.md) +* Proposal: [ST-0011](0011-issue-handling-trait.md) * Authors: [Stuart Montgomery](https://github.com/stmontgomery) * Review Manager: [Paul LeMarquand](https://github.com/plemarquand) * Status: **Active Review (Jun 25 - July 8, 2025)** From 74e2de5f7222c5fe6828d12be0498c10f3feaac1 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Tue, 24 Jun 2025 15:12:15 -0400 Subject: [PATCH 4391/4563] Fixup proposal link --- proposals/testing/0011-issue-handling-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0011-issue-handling-traits.md b/proposals/testing/0011-issue-handling-traits.md index 4c2772415d..3b4fbbaddf 100644 --- a/proposals/testing/0011-issue-handling-traits.md +++ b/proposals/testing/0011-issue-handling-traits.md @@ -1,6 +1,6 @@ # Issue Handling Traits -* Proposal: [ST-0011](0011-issue-handling-trait.md) +* Proposal: [ST-0011](0011-issue-handling-traits.md) * Authors: [Stuart Montgomery](https://github.com/stmontgomery) * Review Manager: [Paul LeMarquand](https://github.com/plemarquand) * Status: **Active Review (Jun 25 - July 8, 2025)** From ee3d67091f543e798b9304de288f763ef8385be4 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Tue, 24 Jun 2025 15:25:26 -0400 Subject: [PATCH 4392/4563] ST-0011 - Add link to review forum post --- proposals/testing/0011-issue-handling-traits.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/testing/0011-issue-handling-traits.md b/proposals/testing/0011-issue-handling-traits.md index 3b4fbbaddf..d27f2e9a5f 100644 --- a/proposals/testing/0011-issue-handling-traits.md +++ b/proposals/testing/0011-issue-handling-traits.md @@ -3,11 +3,11 @@ * Proposal: [ST-0011](0011-issue-handling-traits.md) * Authors: [Stuart Montgomery](https://github.com/stmontgomery) * Review Manager: [Paul LeMarquand](https://github.com/plemarquand) -* Status: **Active Review (Jun 25 - July 8, 2025)** +* Status: **Active Review (Jun 24 - July 8, 2025)** * Implementation: [swiftlang/swift-testing#1080](https://github.com/swiftlang/swift-testing/pull/1080), [swiftlang/swift-testing#1121](https://github.com/swiftlang/swift-testing/pull/1121), [swiftlang/swift-testing#1136](https://github.com/swiftlang/swift-testing/pull/1136) -* Review: ([pitch](https://forums.swift.org/t/pitch-issue-handling-traits/80019)) +* Review: ([pitch](https://forums.swift.org/t/pitch-issue-handling-traits/80019)) ([review](https://forums.swift.org/t/st-0011-issue-handling-traits/80644)) ## Introduction From f80e78e1d902acd15c51d2ec65a03bc839cffa6b Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Mon, 30 Jun 2025 16:38:15 +0100 Subject: [PATCH 4393/4563] Mark `0474-yielding-accessors.md` as Accepted (#2892) Per https://forums.swift.org/t/accepted-se-0474-yielding-accessors/80273 --- proposals/0474-yielding-accessors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0474-yielding-accessors.md b/proposals/0474-yielding-accessors.md index 6ede23b7cf..a109cee548 100644 --- a/proposals/0474-yielding-accessors.md +++ b/proposals/0474-yielding-accessors.md @@ -3,10 +3,10 @@ * Proposal: [SE-0474](0474-yielding-accessors.md) * Authors: [Ben Cohen](https://github.com/airspeedswift), [Nate Chandler](https://github.com/nate-chandler), [Joe Groff](https://github.com/jckarter/) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active Review (April 8 ... April 22, 2025)** +* Status: **Accepted** * Vision: [A Prospective Vision for Accessors in Swift](https://github.com/rjmccall/swift-evolution/blob/accessors-vision/visions/accessors.md) * Implementation: Partially available on main behind the frontend flag `-enable-experimental-feature CoroutineAccessors` -* Review: ([pitch 1](https://forums.swift.org/t/modify-accessors/31872)), ([pitch 2](https://forums.swift.org/t/pitch-modify-and-read-accessors/75627)), ([pitch 3](https://forums.swift.org/t/pitch-3-yielding-coroutine-accessors/77956)), ([review](https://forums.swift.org/t/se-0474-yielding-accessors/79170)) +* Review: ([pitch 1](https://forums.swift.org/t/modify-accessors/31872)), ([pitch 2](https://forums.swift.org/t/pitch-modify-and-read-accessors/75627)), ([pitch 3](https://forums.swift.org/t/pitch-3-yielding-coroutine-accessors/77956)), ([review](https://forums.swift.org/t/se-0474-yielding-accessors/79170)), [Acceptance](https://forums.swift.org/t/accepted-se-0474-yielding-accessors/80273) ## Introduction From ae3640f2d1df1660d4b0d0da6ad1284896991649 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Mon, 30 Jun 2025 12:05:33 -0700 Subject: [PATCH 4394/4563] Update Integration with supporting tools section --- .../testing/XXXX-issue-severity-warning.md | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index 510050352e..a45511430c 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -145,6 +145,26 @@ For more details on `Issue`, refer to the [Issue Documentation](https://develope This revision aims to clarify the functionality and usage of the `Severity` enum and `Issue` properties while maintaining consistency with the existing Swift API standards. +### Integration with supporting tools + +Issue severity will be in the event stream output when a `issueRecorded` event occurs. This will be a breaking change because some tools may assume that all `issueRecorded` events are failing. Due to this we will be bumping the event stream version and v1 will maintain it's behavior and not output any events for non failing issues. + +The JSON event stream ABI will be amended correspondingly: + +``` + ::= { + "isKnown": , ; is this a known issue or not? ++ "severity": , ; the severity of the issue + ["sourceLocation": ,] ; where the issue occurred, if known +} +``` + +Example of an `issueRecorded` event in the json output: + +``` +{"kind":"event","payload":{"instant":{"absolute":302928.100968,"since1970":1751305230.364087},"issue":{"_backtrace":[{"address":4437724864},{"address":4427566652},{"address":4437724280},{"address":4438635916},{"address":4438635660},{"address":4440823880},{"address":4437933556},{"address":4438865080},{"address":4438884348},{"address":11151272236},{"address":4438862360},{"address":4438940324},{"address":4437817340},{"address":4438134208},{"address":4438132164},{"address":4438635048},{"address":4440836660},{"address":4440835536},{"address":4440834989},{"address":4438937653},{"address":4438963225},{"address":4438895773},{"address":4438896161},{"address":4438891517},{"address":4438937117},{"address":4438962637},{"address":4439236617},{"address":4438936181},{"address":4438962165},{"address":4438639149},{"address":4438935045},{"address":4438935513},{"address":11151270653},{"address":11151269797},{"address":4438738225},{"address":4438872065},{"address":4438933417},{"address":4438930265},{"address":4438930849},{"address":4438909741},{"address":4438965489},{"address":11151508333}],"_severity":"error","isKnown":false,"sourceLocation":{"_filePath":"\/Users\/swift-testing\/Tests\/TestingTests\/EntryPointTests.swift","column":23,"fileID":"TestingTests\/EntryPointTests.swift","line":46}},"kind":"issueRecorded","messages":[{"symbol":"fail","text":"Issue recorded"},{"symbol":"details","text":"Unexpected issue Issue recorded (warning) was recorded."}],"testID":"TestingTests.EntryPointTests\/warningIssues()\/EntryPointTests.swift:33:4"},"version":0} +``` + ## Alternatives considered - Separate Issue Creation and Recording: We considered providing a mechanism to create issues independently before recording them, rather than passing the issue details directly to the `record` method. This approach was ultimately set aside in favor of simplicity and directness in usage. @@ -152,7 +172,7 @@ This revision aims to clarify the functionality and usage of the `Severity` enum - Naming of `isFailure` vs. `isFailing`: We evaluated whether to name the property `isFailing` instead of `isFailure`. The decision to use `isFailure` was made to adhere to naming conventions and ensure clarity and consistency within the API. - Severity-Only Checking: We deliberated not exposing `isFailure` and relying solely on `severity` checks. However, this was rejected because it would require test authors to overhaul their code should we introduce additional severity levels in the future. By providing `isFailure`, we offer a straightforward way to determine test outcome impact, complementing the severity feature. -- Naming `Severity.error` `Severity.failure` instead because this will always be a failing issue and test authors often think of test failures. Error and warning match build naming conventions so that is why we continued with this. +- Naming `Severity.error` `Severity.failure` instead because this will always be a failing issue and test authors often think of test failures. Error and warning match build naming conventions and XCTest severity naming convention. ## Future directions From 3adec7d52f83ee08b126b9869457ed3b80634006 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Mon, 30 Jun 2025 13:50:00 -0700 Subject: [PATCH 4395/4563] Add console output and details to try this out --- .../testing/XXXX-issue-severity-warning.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index a45511430c..f8687a4c5e 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -165,6 +165,31 @@ Example of an `issueRecorded` event in the json output: {"kind":"event","payload":{"instant":{"absolute":302928.100968,"since1970":1751305230.364087},"issue":{"_backtrace":[{"address":4437724864},{"address":4427566652},{"address":4437724280},{"address":4438635916},{"address":4438635660},{"address":4440823880},{"address":4437933556},{"address":4438865080},{"address":4438884348},{"address":11151272236},{"address":4438862360},{"address":4438940324},{"address":4437817340},{"address":4438134208},{"address":4438132164},{"address":4438635048},{"address":4440836660},{"address":4440835536},{"address":4440834989},{"address":4438937653},{"address":4438963225},{"address":4438895773},{"address":4438896161},{"address":4438891517},{"address":4438937117},{"address":4438962637},{"address":4439236617},{"address":4438936181},{"address":4438962165},{"address":4438639149},{"address":4438935045},{"address":4438935513},{"address":11151270653},{"address":11151269797},{"address":4438738225},{"address":4438872065},{"address":4438933417},{"address":4438930265},{"address":4438930849},{"address":4438909741},{"address":4438965489},{"address":11151508333}],"_severity":"error","isKnown":false,"sourceLocation":{"_filePath":"\/Users\/swift-testing\/Tests\/TestingTests\/EntryPointTests.swift","column":23,"fileID":"TestingTests\/EntryPointTests.swift","line":46}},"kind":"issueRecorded","messages":[{"symbol":"fail","text":"Issue recorded"},{"symbol":"details","text":"Unexpected issue Issue recorded (warning) was recorded."}],"testID":"TestingTests.EntryPointTests\/warningIssues()\/EntryPointTests.swift:33:4"},"version":0} ``` +### Console output + +When there is an issue recorded with severity warning the output looks like this: + +```swift + Issue.record("My comment", severity: .warning) +``` + +``` +􀟈 Test "All elements of two ranges are equal" started. +􀄣 Test "All elements of two ranges are equal" recorded a warning at ZipTests.swift:32:17: Issue recorded +􀄵 My comment +􁁛 Test "All elements of two ranges are equal" passed after 0.001 seconds with 1 warning. +``` + +### Trying this out + +To use severity today, checkout the branch here: https://github.com/swiftlang/swift-testing/pull/1189 + +``` +.package(url: "https://github.com/swiftlang/swift-testing.git", branch: "suzannaratcliff:suzannaratcliff/enable-severity"), +``` + +For more details on how to checkout a branch for a package refer to this: https://developer.apple.com/documentation/packagedescription/package/dependency/package(url:branch:) + ## Alternatives considered - Separate Issue Creation and Recording: We considered providing a mechanism to create issues independently before recording them, rather than passing the issue details directly to the `record` method. This approach was ultimately set aside in favor of simplicity and directness in usage. From 53b3b52f40457be1332218c96a4d0c59f9e36a08 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 1 Jul 2025 11:19:24 +0900 Subject: [PATCH 4396/4563] Mark SE-0472 Task.immediate as implemented This was accepted and is implemented in 6.2 --- proposals/0472-task-start-synchronously-on-caller-context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0472-task-start-synchronously-on-caller-context.md b/proposals/0472-task-start-synchronously-on-caller-context.md index 1f47857b65..0aea24abbf 100644 --- a/proposals/0472-task-start-synchronously-on-caller-context.md +++ b/proposals/0472-task-start-synchronously-on-caller-context.md @@ -3,7 +3,7 @@ * Proposal: [SE-0472](0472-task-start-synchronously-on-caller-context.md) * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Implementation: https://github.com/swiftlang/swift/pull/79608 * Review: ([pitch](https://forums.swift.org/t/pitch-concurrency-starting-tasks-synchronously-from-caller-context/77960/)) ([first review](https://forums.swift.org/t/se-0472-starting-tasks-synchronously-from-caller-context/78883)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0472-starting-tasks-synchronously-from-caller-context/79311)) ([second review](https://forums.swift.org/t/second-review-se-0472-starting-tasks-synchronously-from-caller-context/79683)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0472-starting-tasks-synchronously-from-caller-context/80037)) From b66be7ed65d237cf3eb7c13a29e5f520dcfe8503 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 1 Jul 2025 11:59:39 +0900 Subject: [PATCH 4397/4563] Include more implementation PRs --- proposals/0472-task-start-synchronously-on-caller-context.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proposals/0472-task-start-synchronously-on-caller-context.md b/proposals/0472-task-start-synchronously-on-caller-context.md index 0aea24abbf..1d266bfba4 100644 --- a/proposals/0472-task-start-synchronously-on-caller-context.md +++ b/proposals/0472-task-start-synchronously-on-caller-context.md @@ -4,7 +4,10 @@ * Authors: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Implemented (Swift 6.2)** -* Implementation: https://github.com/swiftlang/swift/pull/79608 +* Implementation: + * https://github.com/swiftlang/swift/pull/79608 + * https://github.com/swiftlang/swift/pull/81428 + * https://github.com/swiftlang/swift/pull/81572 * Review: ([pitch](https://forums.swift.org/t/pitch-concurrency-starting-tasks-synchronously-from-caller-context/77960/)) ([first review](https://forums.swift.org/t/se-0472-starting-tasks-synchronously-from-caller-context/78883)) ([returned for revision](https://forums.swift.org/t/returned-for-revision-se-0472-starting-tasks-synchronously-from-caller-context/79311)) ([second review](https://forums.swift.org/t/second-review-se-0472-starting-tasks-synchronously-from-caller-context/79683)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0472-starting-tasks-synchronously-from-caller-context/80037)) ## Introduction From bc9dbf02bc2c11ad65e86f9fb23c132ab0ba9244 Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Tue, 1 Jul 2025 21:22:52 +0200 Subject: [PATCH 4398/4563] Rename `@extensible` to `@nonexhaustive` and `@nonexhaustive(warn)` (#2897) --- proposals/0487-extensible-enums.md | 54 +++++++++++++++--------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/proposals/0487-extensible-enums.md b/proposals/0487-extensible-enums.md index d218c50789..7401fa8e0a 100644 --- a/proposals/0487-extensible-enums.md +++ b/proposals/0487-extensible-enums.md @@ -15,6 +15,7 @@ Previously pitched in: - https://forums.swift.org/t/pitch-non-frozen-enumerations/68373 Revisions: +- Renamed the attribute to `@nonexhaustive` and `@nonexhaustive(warn)` respectively - Re-focused this proposal on introducing a new `@extensible` attribute and moved the language feature to a future direction - Introduced a second annotation `@nonExtensible` to allow a migration path into @@ -157,7 +158,7 @@ non-resilient Swift. ## Proposed solution -We propose to introduce a new `@extensible` attribute that can be applied to +We propose to introduce a new `@nonexhaustive` attribute that can be applied to enumerations to mark them as extensible. Such enums will behave the same way as non-frozen enums from resilient Swift libraries. @@ -165,7 +166,7 @@ An example of using the new attribute is below: ```swift /// Module A -@extensible +@nonexhaustive public enum PizzaFlavor { case hawaiian case pepperoni @@ -188,28 +189,27 @@ case .cheese: Code inside the same module or package can be thought of as one co-developed unit of code. Inside the same module or package, switching exhaustively over an -`@extensible` enum inside will not require an`@unknown default`, and using +`@nonexhaustive` enum inside will not require an`@unknown default`, and using one will generate a warning. -### `@extensible` and `@frozen` +### `@nonexhaustive` and `@frozen` -An enum cannot be `@frozen` and `@extensible` at the same time. Thus, marking an -enum both `@extensible` and `@frozen` is not allowed and will result in a +An enum cannot be `@frozen` and `@nonexhaustive` at the same time. Thus, marking an +enum both `@nonexhaustive` and `@frozen` is not allowed and will result in a compiler error. ### API breaking checker The behavior of `swift package diagnose-api-breaking-changes` is also updated -to understand the new `@extensible` attribute. +to understand the new `@nonexhaustive` attribute. -### Staging in using `@preEnumExtensibility` +### Staging in using `@nonexhaustive(warn)` -We also propose adding a new `@preEnumExtensibility` attribute that can be used -to mark enumerations as pre-existing to the `@extensible` attribute. This allows -developers to mark existing public enumerations as `@preEnumExtensibility` in -addition to `@extensible`. This is useful for developers that want to stage in -changing an existing non-extensible enum to be extensible over multiple -releases. Below is an example of how this can be used: +We also propose adding a new `@nonexhaustive(warn)` attribute that can be used +to mark enumerations as pre-existing to when they became extensible.This is +useful for developers that want to stage in changing an existing non-extensible +enum to be extensible over multiple releases. Below is an example of how this +can be used: ```swift // Package A @@ -223,7 +223,7 @@ case .foo: break } // Package A wants to make the existing enum extensible -@preEnumExtensibility @extensible +@nonexhaustive(warn) public enum Foo { case foo } @@ -234,7 +234,7 @@ case .foo: break } // Later Package A decides to extend the enum and releases a new major version -@preEnumExtensibility @extensible +@nonexhaustive(warn) public enum Foo { case foo case bar @@ -246,7 +246,7 @@ case .foo: break } ``` -While the `@preEnumExtensibility` attribute doesn't solve the need of requiring +While the `@nonexhaustive(warn)` attribute doesn't solve the need of requiring a new major when a new case is added it allows developers to stage in changing an existing non-extensible enum to become extensible in a future release by surfacing a warning about this upcoming break early. @@ -255,16 +255,16 @@ surfacing a warning about this upcoming break early. ### Resilient modules -- Adding or removing the `@extensible` attribute has no-effect since it is the default in this language dialect. -- Adding the `@preEnumExtensibility` attribute has no-effect since it only downgrades the error to a warning. -- Removing the `@preEnumExtensibility` attribute is an API breaking since it upgrades the warning to an error again. +- Adding or removing the `@nonexhaustive` attribute has no-effect since it is the default in this language dialect. +- Adding the `@nonexhaustive(warn)` attribute has no-effect since it only downgrades the error to a warning. +- Removing the `@nonexhaustive(warn)` attribute is an API breaking since it upgrades the warning to an error again. ### Non-resilient modules -- Adding the `@extensible` attribute is an API breaking change. -- Removing the `@extensible` attribute is an API stable change. -- Adding the `@preEnumExtensibility` attribute has no-effect since it only downgrades the error to a warning. -- Removing the `@preEnumExtensibility` attribute is an API breaking since it upgrades the warning to an error again. +- Adding the `@nonexhaustive` attribute is an API breaking change. +- Removing the `@nonexhaustive` attribute is an API stable change. +- Adding the `@nonexhaustive(warn)` attribute has no-effect since it only downgrades the error to a warning. +- Removing the `@nonexhaustive(warn)` attribute is an API breaking since it upgrades the warning to an error again. ## ABI compatibility @@ -314,6 +314,6 @@ enumerations to achieve the same effect. ### Different names for the attribute -We considered different names for the attribute such as `@nonFrozen`; however, -we felt that `@extensible` communicates the idea of an extensible enum more -clearly. +We considered different names for the attribute such as `@nonFrozen` or +`@extensible`; however, we felt that `@nonexhaustive` communicates the idea of +an extensible enum more clearly. From 6699354f5c2c862658eb912d3381e06886a61118 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 1 Jul 2025 12:27:55 -0700 Subject: [PATCH 4399/4563] Kick off second review of SE-0487 (#2898) --- proposals/0487-extensible-enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0487-extensible-enums.md b/proposals/0487-extensible-enums.md index 7401fa8e0a..beb87ecaf1 100644 --- a/proposals/0487-extensible-enums.md +++ b/proposals/0487-extensible-enums.md @@ -3,7 +3,7 @@ * Proposal: [SE-0487](0487-extensible-enums.md) * Authors: [Pavel Yaskevich](https://github.com/xedin), [Franz Busch](https://github.com/FranzBusch), [Cory Benfield](https://github.com/lukasa) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **In active review (May 25—Jun 5, 2025)** +* Status: **In active review (July 1—Jul 10, 2025)** * Bug: [apple/swift#55110](https://github.com/swiftlang/swift/issues/55110) * Implementation: [apple/swift#80503](https://github.com/swiftlang/swift/pull/80503) * Upcoming Feature Flag: `ExtensibleAttribute` From cf2bfb49c2ab191e8e545feb782a21d3ef472401 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 2 Jul 2025 14:47:57 +0330 Subject: [PATCH 4400/4563] Fix SE-487 status state --- proposals/0487-extensible-enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0487-extensible-enums.md b/proposals/0487-extensible-enums.md index ed5ceaada9..f20b9363cc 100644 --- a/proposals/0487-extensible-enums.md +++ b/proposals/0487-extensible-enums.md @@ -3,7 +3,7 @@ * Proposal: [SE-0487](0487-extensible-enums.md) * Authors: [Pavel Yaskevich](https://github.com/xedin), [Franz Busch](https://github.com/FranzBusch), [Cory Benfield](https://github.com/lukasa) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **In active review (July 1 — July 10, 2025)** +* Status: **Active Review (July 1 — July 10, 2025)** * Bug: [apple/swift#55110](https://github.com/swiftlang/swift/issues/55110) * Implementation: [apple/swift#80503](https://github.com/swiftlang/swift/pull/80503) * Upcoming Feature Flag: `ExtensibleAttribute` From 47c86c579e8ee8ed98080278360ed02e9f6c6354 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 2 Jul 2025 08:36:50 -0400 Subject: [PATCH 4401/4563] Assign SE-0488. --- proposals/{nnnn-extracting.md => 0488-extracting.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{nnnn-extracting.md => 0488-extracting.md} (94%) diff --git a/proposals/nnnn-extracting.md b/proposals/0488-extracting.md similarity index 94% rename from proposals/nnnn-extracting.md rename to proposals/0488-extracting.md index e488808f98..0b1bf80595 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/0488-extracting.md @@ -1,11 +1,11 @@ # Apply the extracting() slicing pattern more widely -* Proposal: [TBD](https://github.com/swiftlang/swift-evolution/pull/2877) +* Proposal: [SE-0488](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0488-extracting.md) * Author: [Guillaume Lessard](https://github.com/glessard) -* Review Manager: TBD -* Status: **Pitch** +* Review Manager: [Tony Allevato](https://github.com/allevato) +* Status: **Active Review (July 2–July 16, 2025)** * Implementation: underscored `_extracting()` members of `Span` and `RawSpan`, pending elsewhere. -* Review: Pending +* Review: ([pitch](https://forums.swift.org/t/pitch-apply-the-extracting-slicing-pattern-to-span-and-rawspan/80322)) [SE-0437]: 0437-noncopyable-stdlib-primitives.md [SE-0447]: 0447-span-access-shared-contiguous-storage.md From 302aa1190ddf93ebde0810125b418f782d29d098 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Wed, 2 Jul 2025 08:40:44 -0400 Subject: [PATCH 4402/4563] Link to SE-0488 review thread. --- proposals/0488-extracting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0488-extracting.md b/proposals/0488-extracting.md index 0b1bf80595..a4f00b6caa 100644 --- a/proposals/0488-extracting.md +++ b/proposals/0488-extracting.md @@ -5,7 +5,7 @@ * Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Active Review (July 2–July 16, 2025)** * Implementation: underscored `_extracting()` members of `Span` and `RawSpan`, pending elsewhere. -* Review: ([pitch](https://forums.swift.org/t/pitch-apply-the-extracting-slicing-pattern-to-span-and-rawspan/80322)) +* Review: ([pitch](https://forums.swift.org/t/pitch-apply-the-extracting-slicing-pattern-to-span-and-rawspan/80322)) ([review](https://forums.swift.org/t/se-0488-apply-the-extracting-slicing-pattern-more-widely/80854)) [SE-0437]: 0437-noncopyable-stdlib-primitives.md [SE-0447]: 0447-span-access-shared-contiguous-storage.md From b9c88e9c41dc27e0a5db64f402d9f785cf1fa699 Mon Sep 17 00:00:00 2001 From: Mahdi Bahrami Date: Wed, 2 Jul 2025 17:41:24 +0330 Subject: [PATCH 4403/4563] [SE-488] Fix Proposal ID link --- proposals/0488-extracting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0488-extracting.md b/proposals/0488-extracting.md index a4f00b6caa..4bf57d7ec9 100644 --- a/proposals/0488-extracting.md +++ b/proposals/0488-extracting.md @@ -1,6 +1,6 @@ # Apply the extracting() slicing pattern more widely -* Proposal: [SE-0488](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0488-extracting.md) +* Proposal: [SE-0488](0488-extracting.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Active Review (July 2–July 16, 2025)** From e7f53cd073c4bf60114a987e18493d844912a9a0 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Fri, 4 Jul 2025 09:02:11 -0400 Subject: [PATCH 4404/4563] Rename to ST-0012 and add review dates --- ...value-capturing.md => 0012-exit-test-value-capturing.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/testing/{NNNN-exit-test-value-capturing.md => 0012-exit-test-value-capturing.md} (98%) diff --git a/proposals/testing/NNNN-exit-test-value-capturing.md b/proposals/testing/0012-exit-test-value-capturing.md similarity index 98% rename from proposals/testing/NNNN-exit-test-value-capturing.md rename to proposals/testing/0012-exit-test-value-capturing.md index fc6af79558..0f0043fd19 100644 --- a/proposals/testing/NNNN-exit-test-value-capturing.md +++ b/proposals/testing/0012-exit-test-value-capturing.md @@ -1,9 +1,9 @@ # Capturing values in exit tests -* Proposal: [ST-NNNN](NNNN-filename.md) +* Proposal: [ST-0012](0012-exit-test-value-capturing.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Paul LeMarquand](https://github.com/plemarquand) +* Status: **Active Review (Jul 4 - July 18, 2025)** * Bug: [swiftlang/swift-testing#1157](https://github.com/swiftlang/swift-testing/issues/1157) * Implementation: [swiftlang/swift-testing#1040](https://github.com/swiftlang/swift-testing/pull/1040) _et al._ * Review: ([pitch](https://forums.swift.org/t/pitch-capturing-values-in-exit-tests/80494)) From 4f55c8f8bad75083f7086b1fb3ffe05cde69a726 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Fri, 4 Jul 2025 09:12:27 -0400 Subject: [PATCH 4405/4563] Update dates --- proposals/testing/0012-exit-test-value-capturing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0012-exit-test-value-capturing.md b/proposals/testing/0012-exit-test-value-capturing.md index 0f0043fd19..593b866f90 100644 --- a/proposals/testing/0012-exit-test-value-capturing.md +++ b/proposals/testing/0012-exit-test-value-capturing.md @@ -3,7 +3,7 @@ * Proposal: [ST-0012](0012-exit-test-value-capturing.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Review Manager: [Paul LeMarquand](https://github.com/plemarquand) -* Status: **Active Review (Jul 4 - July 18, 2025)** +* Status: **Active Review (Jul 7 - July 21, 2025)** * Bug: [swiftlang/swift-testing#1157](https://github.com/swiftlang/swift-testing/issues/1157) * Implementation: [swiftlang/swift-testing#1040](https://github.com/swiftlang/swift-testing/pull/1040) _et al._ * Review: ([pitch](https://forums.swift.org/t/pitch-capturing-values-in-exit-tests/80494)) From 18c1d98a4d377682ab1482d4c51ee7287e732681 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 4 Jul 2025 09:38:47 -0400 Subject: [PATCH 4406/4563] Add future direction --- proposals/testing/0012-exit-test-value-capturing.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/proposals/testing/0012-exit-test-value-capturing.md b/proposals/testing/0012-exit-test-value-capturing.md index 593b866f90..6a52b7d723 100644 --- a/proposals/testing/0012-exit-test-value-capturing.md +++ b/proposals/testing/0012-exit-test-value-capturing.md @@ -204,6 +204,18 @@ them. C23, in which case we should be able to use it and avoid the need for explicit types in the capture list. ([rdar://153389205](rdar://153389205)) +- Explicitly marking the body closure as requiring explicit captures + + Currently, if the body closure implicitly captures a value, the diagnostic the + compiler provides is a bit opaque: + + > 🛑 A C function pointer cannot be formed from a closure that captures context + + In the future, it may be possible to annotate the body closure with an + attribute, keyword, or other decoration that tells the compiler we need an + explicit capture list, which would allow it to provide a clearer diagnostic if + a value is implicitly captured. + - Supporting capturing values that do not conform to `Codable` Alternatives to `Codable` exist or have been proposed, such as From cd33d6611cbcbceb29d3a568c31d89cf3c7fb300 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Mon, 7 Jul 2025 11:46:00 -0400 Subject: [PATCH 4407/4563] Add review link for ST-0012 --- proposals/testing/0012-exit-test-value-capturing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0012-exit-test-value-capturing.md b/proposals/testing/0012-exit-test-value-capturing.md index 6a52b7d723..43c226b4ae 100644 --- a/proposals/testing/0012-exit-test-value-capturing.md +++ b/proposals/testing/0012-exit-test-value-capturing.md @@ -6,7 +6,7 @@ * Status: **Active Review (Jul 7 - July 21, 2025)** * Bug: [swiftlang/swift-testing#1157](https://github.com/swiftlang/swift-testing/issues/1157) * Implementation: [swiftlang/swift-testing#1040](https://github.com/swiftlang/swift-testing/pull/1040) _et al._ -* Review: ([pitch](https://forums.swift.org/t/pitch-capturing-values-in-exit-tests/80494)) +* Review: ([pitch](https://forums.swift.org/t/pitch-capturing-values-in-exit-tests/80494)) ([review](https://forums.swift.org/t/st-0012-capturing-values-in-exit-tests/80963)) ## Introduction From 01b4631eab017d14686d807c4674d97e0d8b75f1 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Mon, 7 Jul 2025 14:40:02 -0400 Subject: [PATCH 4408/4563] Update 0464-utf8span-safe-utf8-processing.md (#2904) Mark UTF8 Span implemented. --- proposals/0464-utf8span-safe-utf8-processing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0464-utf8span-safe-utf8-processing.md b/proposals/0464-utf8span-safe-utf8-processing.md index bfc006ac3e..1f366a6004 100644 --- a/proposals/0464-utf8span-safe-utf8-processing.md +++ b/proposals/0464-utf8span-safe-utf8-processing.md @@ -3,7 +3,7 @@ * Proposal: [SE-0464](0464-utf8span-safe-utf8-processing.md) * Authors: [Michael Ilseman](https://github.com/milseman), [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Accepted** +* Status: **Implemented (6.2)** * Bug: rdar://48132971, rdar://96837923 * Implementation: [swiftlang/swift#78531](https://github.com/swiftlang/swift/pull/78531) * Review: ([first pitch](https://forums.swift.org/t/pitch-utf-8-processing-over-unsafe-contiguous-bytes/69715)) ([second pitch](https://forums.swift.org/t/pitch-safe-utf-8-processing-over-contiguous-bytes/72742)) ([third pitch](https://forums.swift.org/t/pitch-utf8span-safe-utf-8-processing-over-contiguous-bytes/77483)) ([review](https://forums.swift.org/t/se-0464-utf8span-safe-utf-8-processing-over-contiguous-bytes/78307)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0464-safe-utf-8-processing-over-contiguous-bytes/79218)) From e7004a141da364362af217be40ffcf471755bb2d Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Mon, 7 Jul 2025 20:18:10 -0700 Subject: [PATCH 4409/4563] Fix formatting --- proposals/testing/XXXX-issue-severity-warning.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index f8687a4c5e..3ba06650b4 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -5,7 +5,7 @@ * Review Manager: TBD * Status: **Pitched** * Implementation: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075) -* Review: ([pitch](https://forums.swift.org/t/pitch-test-issue-warnings/79285) +* Review: ([pitch](https://forums.swift.org/t/pitch-test-issue-warnings/79285)) ## Introduction @@ -38,7 +38,7 @@ Test authors will be able to inspect if the issue is a failing issue and will be ## Detailed design -###Severity Enum +### Severity Enum We introduce a Severity enum to categorize issues detected during testing. This enum is crucial for distinguishing between different levels of test issues and is defined as follows: @@ -65,7 +65,7 @@ extension Issue { } ``` -###Recording Non-Failing Issues +### Recording Non-Failing Issues To enable test authors to log non-failing issues without affecting test results, we provide a method for recording such issues: ```swift @@ -96,7 +96,7 @@ Here is the `Issue.record` method definition with severity as a parameter. // ... ``` -###Issue Type Enhancements +### Issue Type Enhancements The Issue type is enhanced with two new properties to better handle and report issues: - `severity`: This property allows access to the specific severity level of an issue, enabling more precise handling of test results. @@ -131,7 +131,7 @@ extension Issue { } ``` -Example usage of `severity` and `isFailure: +Example usage of `severity` and `isFailure`: ```swift // ... withKnownIssue { @@ -185,7 +185,7 @@ When there is an issue recorded with severity warning the output looks like this To use severity today, checkout the branch here: https://github.com/swiftlang/swift-testing/pull/1189 ``` -.package(url: "https://github.com/swiftlang/swift-testing.git", branch: "suzannaratcliff:suzannaratcliff/enable-severity"), +.package(url: "https://github.com/suzannaratcliff/swift-testing.git", branch: "suzannaratcliff:suzannaratcliff/enable-severity"), ``` For more details on how to checkout a branch for a package refer to this: https://developer.apple.com/documentation/packagedescription/package/dependency/package(url:branch:) From 6af9507551a69efca72c83f9a3073a7d47af17b9 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Mon, 7 Jul 2025 20:45:03 -0700 Subject: [PATCH 4410/4563] Add isFailure --- proposals/testing/XXXX-issue-severity-warning.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/XXXX-issue-severity-warning.md index 3ba06650b4..9681c60be3 100644 --- a/proposals/testing/XXXX-issue-severity-warning.md +++ b/proposals/testing/XXXX-issue-severity-warning.md @@ -147,7 +147,7 @@ This revision aims to clarify the functionality and usage of the `Severity` enum ### Integration with supporting tools -Issue severity will be in the event stream output when a `issueRecorded` event occurs. This will be a breaking change because some tools may assume that all `issueRecorded` events are failing. Due to this we will be bumping the event stream version and v1 will maintain it's behavior and not output any events for non failing issues. +Issue severity will be in the event stream output when a `issueRecorded` event occurs. This will be a breaking change because some tools may assume that all `issueRecorded` events are failing. Due to this we will be bumping the event stream version and v1 will maintain it's behavior and not output any events for non failing issues. We will also be adding `isFailure` to the issue so that clients will know if the issue should be treated as a failure. The JSON event stream ABI will be amended correspondingly: @@ -155,6 +155,7 @@ The JSON event stream ABI will be amended correspondingly: ::= { "isKnown": , ; is this a known issue or not? + "severity": , ; the severity of the issue ++ "isFailure": , ; if the issue is a failing issue ["sourceLocation": ,] ; where the issue occurred, if known } ``` @@ -162,7 +163,7 @@ The JSON event stream ABI will be amended correspondingly: Example of an `issueRecorded` event in the json output: ``` -{"kind":"event","payload":{"instant":{"absolute":302928.100968,"since1970":1751305230.364087},"issue":{"_backtrace":[{"address":4437724864},{"address":4427566652},{"address":4437724280},{"address":4438635916},{"address":4438635660},{"address":4440823880},{"address":4437933556},{"address":4438865080},{"address":4438884348},{"address":11151272236},{"address":4438862360},{"address":4438940324},{"address":4437817340},{"address":4438134208},{"address":4438132164},{"address":4438635048},{"address":4440836660},{"address":4440835536},{"address":4440834989},{"address":4438937653},{"address":4438963225},{"address":4438895773},{"address":4438896161},{"address":4438891517},{"address":4438937117},{"address":4438962637},{"address":4439236617},{"address":4438936181},{"address":4438962165},{"address":4438639149},{"address":4438935045},{"address":4438935513},{"address":11151270653},{"address":11151269797},{"address":4438738225},{"address":4438872065},{"address":4438933417},{"address":4438930265},{"address":4438930849},{"address":4438909741},{"address":4438965489},{"address":11151508333}],"_severity":"error","isKnown":false,"sourceLocation":{"_filePath":"\/Users\/swift-testing\/Tests\/TestingTests\/EntryPointTests.swift","column":23,"fileID":"TestingTests\/EntryPointTests.swift","line":46}},"kind":"issueRecorded","messages":[{"symbol":"fail","text":"Issue recorded"},{"symbol":"details","text":"Unexpected issue Issue recorded (warning) was recorded."}],"testID":"TestingTests.EntryPointTests\/warningIssues()\/EntryPointTests.swift:33:4"},"version":0} +{"kind":"event","payload":{"instant":{"absolute":302928.100968,"since1970":1751305230.364087},"issue":{"_backtrace":[{"address":4437724864},{"address":4427566652},{"address":4437724280},{"address":4438635916},{"address":4438635660},{"address":4440823880},{"address":4437933556},{"address":4438865080},{"address":4438884348},{"address":11151272236},{"address":4438862360},{"address":4438940324},{"address":4437817340},{"address":4438134208},{"address":4438132164},{"address":4438635048},{"address":4440836660},{"address":4440835536},{"address":4440834989},{"address":4438937653},{"address":4438963225},{"address":4438895773},{"address":4438896161},{"address":4438891517},{"address":4438937117},{"address":4438962637},{"address":4439236617},{"address":4438936181},{"address":4438962165},{"address":4438639149},{"address":4438935045},{"address":4438935513},{"address":11151270653},{"address":11151269797},{"address":4438738225},{"address":4438872065},{"address":4438933417},{"address":4438930265},{"address":4438930849},{"address":4438909741},{"address":4438965489},{"address":11151508333}],"_severity":"error","isFailure":true, "isKnown":false,"sourceLocation":{"_filePath":"\/Users\/swift-testing\/Tests\/TestingTests\/EntryPointTests.swift","column":23,"fileID":"TestingTests\/EntryPointTests.swift","line":46}},"kind":"issueRecorded","messages":[{"symbol":"fail","text":"Issue recorded"},{"symbol":"details","text":"Unexpected issue Issue recorded (warning) was recorded."}],"testID":"TestingTests.EntryPointTests\/warningIssues()\/EntryPointTests.swift:33:4"},"version":0} ``` ### Console output From 90018998158955a91223440155726646e4cfe1b1 Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Tue, 8 Jul 2025 21:05:19 +0200 Subject: [PATCH 4411/4563] Review preparation --- ...X-issue-severity-warning.md => 0013-issue-severity-warning.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/testing/{XXXX-issue-severity-warning.md => 0013-issue-severity-warning.md} (100%) diff --git a/proposals/testing/XXXX-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md similarity index 100% rename from proposals/testing/XXXX-issue-severity-warning.md rename to proposals/testing/0013-issue-severity-warning.md From 957cb3afb38e3fdc1aad89a5e51d52fee342d107 Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Tue, 8 Jul 2025 21:05:51 +0200 Subject: [PATCH 4412/4563] Review preparation --- .../testing/0013-issue-severity-warning.md | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/proposals/testing/0013-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md index 9681c60be3..f5670187c7 100644 --- a/proposals/testing/0013-issue-severity-warning.md +++ b/proposals/testing/0013-issue-severity-warning.md @@ -1,11 +1,11 @@ # Test Issue Severity -* Proposal: [ST-XXXX](XXXX-issue-severity-warning.md) -* Authors: [Suzy Ratcliff](https://github.com/suzannaratcliff) -* Review Manager: TBD -* Status: **Pitched** -* Implementation: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075) -* Review: ([pitch](https://forums.swift.org/t/pitch-test-issue-warnings/79285)) +- Proposal: [ST-0013](0013-issue-severity-warning.md) +- Authors: [Suzy Ratcliff](https://github.com/suzannaratcliff) +- Review Manager: [Maarten Engels](https://github.com/maartene) +- Status: **Awaiting Review** +- Implementation: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075) +- Review: ([pitch](https://forums.swift.org/t/pitch-test-issue-warnings/79285)) ## Introduction @@ -17,20 +17,21 @@ Currently, when an issue arises during a test, the only possible outcome is to m ### Use Cases -- Warning about a Percentage Discrepancy in Image Comparison: - - Scenario: When comparing two images to assess their similarity, a warning can be triggered if there's a 95% pixel match, while a test failure is set at a 90% similarity threshold. - - Reason: In practices like snapshot testing, minor changes (such as a timestamp) might cause a discrepancy. Setting a 90% match as a pass ensures test integrity. However, a warning at 95% alerts testers that, although the images aren't identical, the test has passed, which may warrant further investigation. +- Warning about a Percentage Discrepancy in Image Comparison: + - Scenario: When comparing two images to assess their similarity, a warning can be triggered if there's a 95% pixel match, while a test failure is set at a 90% similarity threshold. + - Reason: In practices like snapshot testing, minor changes (such as a timestamp) might cause a discrepancy. Setting a 90% match as a pass ensures test integrity. However, a warning at 95% alerts testers that, although the images aren't identical, the test has passed, which may warrant further investigation. - Warning for Duplicate Argument Inputs in Tests: - - Scenario: In a test library, issue a warning if a user inputs the same argument twice, rather than flagging an error. - - Reason: Although passing the same argument twice might not be typical, some users may have valid reasons for doing so. Thus, a warning suffices, allowing flexibility without compromising the test's execution. + - Scenario: In a test library, issue a warning if a user inputs the same argument twice, rather than flagging an error. + - Reason: Although passing the same argument twice might not be typical, some users may have valid reasons for doing so. Thus, a warning suffices, allowing flexibility without compromising the test's execution. - Warning for Recoverable Unexpected Events: - - Scenario: During an integration test where data is retrieved from a server, a warning can be issued if the primary server is down, prompting a switch to an alternative server. Usually mocking is the solution for this but may not test everything needed for an integration test. - - Reason: Since server downtime might happen and can be beyond the tester's control, issuing a warning rather than a failure helps in debugging and understanding potential issues without impacting the test's overall success. + - Scenario: During an integration test where data is retrieved from a server, a warning can be issued if the primary server is down, prompting a switch to an alternative server. Usually mocking is the solution for this but may not test everything needed for an integration test. + - Reason: Since server downtime might happen and can be beyond the tester's control, issuing a warning rather than a failure helps in debugging and understanding potential issues without impacting the test's overall success. - Warning for a retry during setup for a test: - - Scenario: During test setup part of your code may be configured to retry, it would be nice to notify in the results that a retry happened - - Reason: This makes sense to be a warning and not a failure because if the retry succeeds the test may still verify the code correctly + - Scenario: During test setup part of your code may be configured to retry, it would be nice to notify in the results that a retry happened + - Reason: This makes sense to be a warning and not a failure because if the retry succeeds the test may still verify the code correctly ## Proposed solution + We propose introducing a new property on `Issue` in Swift Testing called `severity`, that represents if an issue is a `warning` or an `error`. The default Issue severity will still be `error` and users can set the severity when they record an issue. @@ -47,7 +48,7 @@ The `Severity` enum: ```swift extension Issue { // ... - public enum Severity: Codable, Comparable, CustomStringConvertible, Sendable { + public enum Severity: Codable, Comparable, CustomStringConvertible, Sendable { /// The severity level for an issue which should be noted but is not /// necessarily an error. /// @@ -66,6 +67,7 @@ extension Issue { ``` ### Recording Non-Failing Issues + To enable test authors to log non-failing issues without affecting test results, we provide a method for recording such issues: ```swift @@ -73,6 +75,7 @@ Issue.record("My comment", severity: .warning) ``` Here is the `Issue.record` method definition with severity as a parameter. + ```swift /// Record an issue when a running test fails unexpectedly. /// @@ -92,13 +95,14 @@ Here is the `Issue.record` method definition with severity as a parameter. severity: Severity = .error, sourceLocation: SourceLocation = #_sourceLocation ) -> Self - + // ... ``` ### Issue Type Enhancements The Issue type is enhanced with two new properties to better handle and report issues: + - `severity`: This property allows access to the specific severity level of an issue, enabling more precise handling of test results. ```swift @@ -112,7 +116,9 @@ public var severity: Severity { get set } } ``` + - `isFailure`: A boolean property to determine if an issue results in a test failure, thereby helping in result aggregation and reporting. + ```swift extension Issue { // ... @@ -132,6 +138,7 @@ extension Issue { ``` Example usage of `severity` and `isFailure`: + ```swift // ... withKnownIssue { @@ -147,7 +154,7 @@ This revision aims to clarify the functionality and usage of the `Severity` enum ### Integration with supporting tools -Issue severity will be in the event stream output when a `issueRecorded` event occurs. This will be a breaking change because some tools may assume that all `issueRecorded` events are failing. Due to this we will be bumping the event stream version and v1 will maintain it's behavior and not output any events for non failing issues. We will also be adding `isFailure` to the issue so that clients will know if the issue should be treated as a failure. +Issue severity will be in the event stream output when a `issueRecorded` event occurs. This will be a breaking change because some tools may assume that all `issueRecorded` events are failing. Due to this we will be bumping the event stream version and v1 will maintain it's behavior and not output any events for non failing issues. We will also be adding `isFailure` to the issue so that clients will know if the issue should be treated as a failure. The JSON event stream ABI will be amended correspondingly: @@ -198,7 +205,7 @@ For more details on how to checkout a branch for a package refer to this: https: - Naming of `isFailure` vs. `isFailing`: We evaluated whether to name the property `isFailing` instead of `isFailure`. The decision to use `isFailure` was made to adhere to naming conventions and ensure clarity and consistency within the API. - Severity-Only Checking: We deliberated not exposing `isFailure` and relying solely on `severity` checks. However, this was rejected because it would require test authors to overhaul their code should we introduce additional severity levels in the future. By providing `isFailure`, we offer a straightforward way to determine test outcome impact, complementing the severity feature. -- Naming `Severity.error` `Severity.failure` instead because this will always be a failing issue and test authors often think of test failures. Error and warning match build naming conventions and XCTest severity naming convention. +- Naming `Severity.error` `Severity.failure` instead because this will always be a failing issue and test authors often think of test failures. Error and warning match build naming conventions and XCTest severity naming convention. ## Future directions From 330023ddf86eb058a3d2c38429df4f8bd39ab27c Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Tue, 8 Jul 2025 21:11:22 +0200 Subject: [PATCH 4413/4563] Review preparation --- proposals/testing/0013-issue-severity-warning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0013-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md index f5670187c7..67e25264e9 100644 --- a/proposals/testing/0013-issue-severity-warning.md +++ b/proposals/testing/0013-issue-severity-warning.md @@ -3,7 +3,7 @@ - Proposal: [ST-0013](0013-issue-severity-warning.md) - Authors: [Suzy Ratcliff](https://github.com/suzannaratcliff) - Review Manager: [Maarten Engels](https://github.com/maartene) -- Status: **Awaiting Review** +- Status: **Active Review (July 9...July 23, 2025)** - Implementation: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075) - Review: ([pitch](https://forums.swift.org/t/pitch-test-issue-warnings/79285)) From 5dc4dbc774b21a0c37aa4ceb34b633f7cf55f6f4 Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Tue, 8 Jul 2025 21:20:39 +0200 Subject: [PATCH 4414/4563] ST-0013 review - add link to review thread --- proposals/testing/0013-issue-severity-warning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0013-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md index 67e25264e9..fe89f37c1f 100644 --- a/proposals/testing/0013-issue-severity-warning.md +++ b/proposals/testing/0013-issue-severity-warning.md @@ -5,7 +5,7 @@ - Review Manager: [Maarten Engels](https://github.com/maartene) - Status: **Active Review (July 9...July 23, 2025)** - Implementation: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075) -- Review: ([pitch](https://forums.swift.org/t/pitch-test-issue-warnings/79285)) +- Review: ([pitch](https://forums.swift.org/t/pitch-test-issue-warnings/79285)) ([review](https://forums.swift.org/t/st-0013-test-issue-warnings/80991)) ## Introduction From c1a42d47452c655830c8936d83dec81326019c72 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 8 Jul 2025 12:43:56 -0700 Subject: [PATCH 4415/4563] [SE-0466] Disable `@MainActor` inference when type conforms to a SendableMetatype protocol (#2894) * [SE-0466] Disable `@MainActor` inference when type conforms to a SendableMetatype protocol Libraries need a way to communicate that certain protocols can't reasonably be used with global-actor-isolated types. When the primary definition of a type conforms to such a protocol, it will disable `@MainActor` inference for that type. This has the effect of keeping more types `nonisolated` when there's signal that they should not be on the main actor. There are some protocols like this in the standard library (e.g., CodingKey, which was hardcoded to be nonisolated in the synthesis code), where the following code is ill-formed in with main-actor isolation by default: struct S: Codable { var a: Int // error: CodingKeys inferred to `@MainActor`, which makes the conformance // to CodingKey main-actor-isolated and then fails (because the requirements // need to be nonisolated). enum CodingKeys: CodingKey { case a } } With this amendment, the conformance to `CodingKey` (which inherits from `Sendable`), prevents `CodingKeys` from being inferred to be`@MainActor`. * Provide more extensive rationale for the design --- .../0466-control-default-actor-isolation.md | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/proposals/0466-control-default-actor-isolation.md b/proposals/0466-control-default-actor-isolation.md index 763a1ff12d..37f543ac54 100644 --- a/proposals/0466-control-default-actor-isolation.md +++ b/proposals/0466-control-default-actor-isolation.md @@ -65,6 +65,7 @@ When the default actor isolation is specified as `MainActor`, declarations are i * Declarations with inferred actor isolation from a superclass, overridden method, protocol conformance, or member propagation * All declarations inside an `actor` type, including static variables, methods, initializers, and deinitializers * Declarations that cannot have global actor isolation, including typealiases, import statements, enum cases, and individual accessors +* Declarations whose primary definition directly conforms to a protocol that inherits `SendableMetatype` The following code example shows the inferred actor isolation in comments given the code is built with `-default-isolation MainActor`: @@ -107,6 +108,16 @@ struct S: P { // @MyActor func f() { ... } } + +nonisolated protocol Q: Sendable { } + +// nonisolated +struct S2: Q { } + +// @MainActor +struct S3 { } + +extension S3: Q { } ``` This proposal does not change the default isolation inference rules for closures. Non-Sendable closures and closures passed to `Task.init` already have the same isolation as the enclosing context by default. When specifying `MainActor` isolation by default in a module, non-`@Sendable` closures and `Task.init` closures will have inferred `@MainActor` isolation when the default `@MainActor` inference rules apply to the enclosing context: @@ -166,23 +177,37 @@ Similarly, a future language mode could enable main actor isolation by default, See the approachable data-race safety vision document for an [analysis on the risks of introducing a language dialect](https://github.com/hborla/swift-evolution/blob/approachable-concurrency-vision/visions/approachable-concurrency.md#risks-of-a-language-dialect) for default actor isolation. -### Don't apply default actor isolation to explicitly `Sendable` types +### Alternative to `SendableMetatype` for suppressing main-actor inference + +The protocols to which a type conforms can affect the isolation of the type. Conforming to a global-actor-isolated protocol can infer global-actor isolatation for the type. When the default actor isolation is `MainActor`, it is valuable for protocols to be able to push inference toward keeping conforming types `nonisolated`, for example because conforming types are meant to be usable from any isolation domain. -This proposal includes few exceptions where the specified default actor isolation does not apply. An additional case that should be considered is types with a conformance to `Sendable`: +In this proposal, inheritance from `SendableMetatype` (introduced in [SE-0470](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0470-isolated-conformances.md)) is used as an indication that types conforming to the protocol should be `nonisolated`. The `SendableMetatype` marker protocol indicates when a type (but not necessarily its instances) can cross isolation domains, which implies that the type generally needs to be usable from any isolation domain. Additionally, protocols that inherit from `SendableMetatype` can only be meaningfully be used with nonisolated conformances, as discussed in SE-0470. Experience using default main actor isolation uncovered a number of existing protocols that reinforce the notion of `SendableMetatype` inheritance is a reasonable heuristic to indicate that a conforming type should be nonisolated: the standard library's [`CodingKey`](https://developer.apple.com/documentation/swift/codingkey) protocol inherits `Sendable` (which in turn inherits `SendableMetatype`) so a typical conformance will fail to compile with default main actor isolation: ```swift -struct SimpleValue: Sendable { - var value: Int +struct S: Codable { + var a: Int + + // error if CodingKeys is inferred to `@MainActor`. The conformance cannot be main-actor-isolated, and + // the requirements of the (nonisolated) CodingKey cannot be satisfied by main-actor-isolated members of + // CodingKeys. + enum CodingKeys: CodingKey { + case a + } } ``` -This is an attractive carve out upon first glance, but there are a number of downsides: +Other places that have similar issues with default main actor isolation include the [`Transferable`](https://developer.apple.com/documentation/coretransferable/transferable) protocol and the uses of key paths in the [`@Model` macro](https://developer.apple.com/documentation/swiftdata/model()). + +Instead of using `SendableMetatype` inheritance, this proposal could introduce new syntax for a protocol to explicitly indicate -* The carve out may be confusing if a conformance to `Sendable` is implied, e.g. through a conformance to another protocol. -* Global actor isolation implies a conformance to `Sendable`, so it's not clear that a `Sendable` type should not be global actor isolated. -* Methods on a `Sendable` type may still use other types and methods that have default actor isolation applied, which would lead to failures if the `Sendable` type was exempt from default isolation inference. +```swift +@nonisolatedConformingTypes +public protocol CodingKey { + // ... +} +``` -A middle ground might be to not apply default actor isolation to types with an explicit conformance to `Sendable` within the same source file as the type. This approach would still have some of the downsides listed above, but it would be more straightforward to spot types that have this exemption. +This would make the behavior pushing conforming types toward `nonisolated` opt-in. However, it means that existing protocols (such as the ones mentioned above) would all need to adopt this spelling before code using default main actor isolation will work well. Given the strong semantic link between `SendableMetatype` and `nonisolated` conformances and types, the proposed rule based on `SendableMetatype` inheritance is likely to make more code work well with default main actor isolation. An explicit opt-in attribute like the above could be added at a later time if needed. ### Use an enum for the package manifest API From cf2a0fa2ec5e338ace98b0cf988b6a63035f44a7 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 8 Jul 2025 15:56:27 -0400 Subject: [PATCH 4416/4563] Update 0466-control-default-actor-isolation.md (#2907) --- proposals/0466-control-default-actor-isolation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/0466-control-default-actor-isolation.md b/proposals/0466-control-default-actor-isolation.md index 37f543ac54..5c1c0a08ab 100644 --- a/proposals/0466-control-default-actor-isolation.md +++ b/proposals/0466-control-default-actor-isolation.md @@ -1,11 +1,11 @@ # Control default actor isolation inference * Proposal: [SE-0466](0466-control-default-actor-isolation.md) -* Authors: [Holly Borla](https://github.com/hborla) +* Authors: [Holly Borla](https://github.com/hborla), [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Implemented (Swift 6.2)** +* Status: **Implemented (Swift 6.2)**, **Amendmendment review July 8 - 15, 2025** * Vision: [Improving the approachability of data-race safety](/visions/approachable-concurrency.md) -* Review: ([pitch](https://forums.swift.org/t/pitch-control-default-actor-isolation-inference/77482))([review](https://forums.swift.org/t/se-0466-control-default-actor-isolation-inference/78321))([acceptance](https://forums.swift.org/t/accepted-se-0466-control-default-actor-isolation-inference/78926)) +* Review: ([pitch](https://forums.swift.org/t/pitch-control-default-actor-isolation-inference/77482))([review](https://forums.swift.org/t/se-0466-control-default-actor-isolation-inference/78321))([acceptance](https://forums.swift.org/t/accepted-se-0466-control-default-actor-isolation-inference/78926))([amendment pitch](https://forums.swift.org/t/pitch-amend-se-0466-se-0470-to-improve-isolation-inference/79854))([amendment review](https://forums.swift.org/t/amendment-se-0466-control-default-actor-isolation-inference/80994)) ## Introduction From 2b01fe103fd486155771b2d5ff60bc720e802d5e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 8 Jul 2025 19:43:21 -0700 Subject: [PATCH 4417/4563] [SE-0470] Infer `nonisolated` on conformances (#2893) * [SE-0470] Infer `nonisolated` on conformances Introduce two inference rules that infer `nonisolated` when `InferIsolatedConformances` is enabled. These inference rules ensure that most conformances that should be nonisolated remain nonisolated, particular when we're in the main-actor-by-default mode. The end result is that much less code needs to change when enabling isolated conformance inference (including via main-actor-by-default mode). The two rules are: * If the protocol inherits from `SendableMetatype` (including indirectly, e.g., from `Sendable`), then the isolated conformance could never be used, so it is inferred to be `nonisolated`. * If all of the declarations used to satisfy protocol requirements are `nonisolated`, the conformance will be assumed to be `nonisolated`. * More justification for why this feature is needed, and a possible future removing it * Fix typo * Clarify that a nonisolated conformance might evolve to an isolated one * Update headers and revision history --------- Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- proposals/0470-isolated-conformances.md | 40 ++++++++++++++++++++----- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/proposals/0470-isolated-conformances.md b/proposals/0470-isolated-conformances.md index 83c4b3a51a..a1cb0750b4 100644 --- a/proposals/0470-isolated-conformances.md +++ b/proposals/0470-isolated-conformances.md @@ -3,11 +3,11 @@ * Proposal: [SE-0470](0470-isolated-conformances.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Implemented (Swift 6.2)** +* Status: **Active review (July 8...15, 2025)** * Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md) * Implementation: On `main` with the experimental features `IsolatedConformances` and `StrictSendableMetatypes`. * Upcoming Feature Flag: `InferIsolatedConformances` -* Review: ([pitch](https://forums.swift.org/t/pre-pitch-isolated-conformances/77726)) ([review](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704)) ([acceptance](https://forums.swift.org/t/accepted-se-0470-global-actor-isolated-conformances/79189)) +* Review: ([pitch](https://forums.swift.org/t/pre-pitch-isolated-conformances/77726)) ([review](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704)) ([acceptance](https://forums.swift.org/t/accepted-se-0470-global-actor-isolated-conformances/79189)) ([amendment pitch](https://forums.swift.org/t/pitch-amend-se-0466-se-0470-to-improve-isolation-inference/79854)) ## Introduction @@ -549,18 +549,21 @@ class MyModelType: /*inferred @MainActor*/ P { } ``` -If this inference is not desired, for example because the code will use `nonisolated` members to satisfy the requirements of the protocol, it can use `nonisolated` on the conformance: +If this inference is not desired, one can use `nonisolated` on the conformances: ```swift @MainActor -class MyModelType: nonisolated P { - nonisolated func f() { } // implements P.f, is non-isolated +class MyModelType: nonisolated Q { + nonisolated static func g() { } // implements Q.g, is non-isolated } ``` -This mirrors the rules for global actor inference elsewhere in the language, providing a more consistent answer. +There are two additional inference rules that imply `nonisolated` on a conformance of a global-actor-isolated type: -This proposed change is source-breaking, so it should be staged in via an upcoming feature (`InferIsolatedConformances`) that can be folded into a future language mode. Fortunately, it is mechanically migratable: existing code migrating to `InferIsolatedConformances` could introduce `nonisolated` for each conformance of a global-actor-isolated type. +* If the protocol inherits from `SendableMetatype` (including indirectly, e.g., from `Sendable`), then the isolated conformance could never be used, so it is inferred to be `nonisolated`. +* If all of the declarations used to satisfy protocol requirements are `nonisolated`, the conformance will be assumed to be `nonisolated`. The conformance of `MyModelType` to `Q` would be inferred to be `nonisolated` because the static method `g` used to satisfy `Q.g` is `nonisolated.` + +This proposed change is source-breaking in the cases where a conformance is currently `nonisolated`, the rules above would not infer `nonisolated`, and the conformance crosses isolation domains. There, conformance isolation inference is staged in via an upcoming feature (`InferIsolatedConformances`) that can be folded into a future language mode. Fortunately, it is mechanically migratable: existing code migrating to `InferIsolatedConformances` could introduce `nonisolated` for each conformance of a global-actor-isolated type. ### Infer `@MainActor` conformances @@ -662,7 +665,30 @@ This is a generalization of the proposed rules that makes more explicit when con The main down side of this alternative is the additional complexity it introduces into generic requirements. It should be possible to introduce this approach later if it proves to be necessary, by treating it as a generalization of the existing rules in this proposal. +### Require `nonisolated` rather than inferring it + +Under the upcoming feature `InferIsolatedConformances`, this proposal infers `nonisolated` for conformances when all of the declarations that satisfy requirements of a protocol are themselves `nonisolated`. For example: + +```swift +nonisolated protocol Q { + static func create() -> Self +} + +@MainActor struct MyType: /*infers nonisolated*/ Q { + nonisolated static func create() -> MyType { ... } +} +``` + +This inference is important for providing source compatibility with and without `InferIsolatedConformances`, and is especially useful useful when combined with default main-actor isolation ([SE-0466](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0466-control-default-actor-isolation.md)), where many more types will become main-actor isolated. Experience with using these features together also identified some macros (such as [`@Observable`](https://developer.apple.com/documentation/observation/observable())) that produced `nonisolated` members for a protocol conformances, but had not yet been updated to mark the conformance as `nonisolated`. Macro-generated code is much harder for users to update when a source-compatibility issue arises, which makes `nonisolated` conformance inference particularly important for source compatibility. + +However, this inference rule has downsides. It means one needs to examine a protocol and how a type conforms to that protocol to determine whether the conformance might be `nonisolated`, which can be a lot of work for the developer reading the code as well as the compiler. It can also change over time: for example, a default implementation of a protocol requirement will likely be `nonisolated`, but a user-written one within a main-actor-isolated type would be `@MainActor` and, therefore, make the conformance `@MainActor`. + +One alternative would be to introduce this inference rule for source compatibility, but treat it as a temporary measure to be disabled again in some future language mode. Introducing the inference rule in this proposal does not foreclose on that possibility: if we find that the `nonisolated` conformance inference rule here is harmful to readability, a separate proposal can deprecate it in a future language mode, providing a suitable migration timeframe. + ## Revision history +* Changes in amendment review: + * If the protocol inherits from `SendableMetatype` (including indirectly, e.g., from `Sendable`), then the isolated conformance could never be used, so it is inferred to be `nonisolated`. + * If all of the declarations used to satisfy protocol requirements are `nonisolated`, the conformance will be assumed to be `nonisolated`. * Changes in review: * Within a generic function, use sendability of metatypes of generic parameters as the basis for checking, rather than treating specific conformances as potentially isolated. This model is easier to reason about and fits better with `SendableMetatype`, and was used in earlier drafts of this proposal. From 2af25b6dcf694410d7d4270bb64848bc4c5b57d6 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 8 Jul 2025 23:03:16 -0400 Subject: [PATCH 4418/4563] Add amendment review link for SE-0470 (#2909) --- proposals/0470-isolated-conformances.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0470-isolated-conformances.md b/proposals/0470-isolated-conformances.md index a1cb0750b4..0854f6da16 100644 --- a/proposals/0470-isolated-conformances.md +++ b/proposals/0470-isolated-conformances.md @@ -7,7 +7,7 @@ * Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md) * Implementation: On `main` with the experimental features `IsolatedConformances` and `StrictSendableMetatypes`. * Upcoming Feature Flag: `InferIsolatedConformances` -* Review: ([pitch](https://forums.swift.org/t/pre-pitch-isolated-conformances/77726)) ([review](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704)) ([acceptance](https://forums.swift.org/t/accepted-se-0470-global-actor-isolated-conformances/79189)) ([amendment pitch](https://forums.swift.org/t/pitch-amend-se-0466-se-0470-to-improve-isolation-inference/79854)) +* Review: ([pitch](https://forums.swift.org/t/pre-pitch-isolated-conformances/77726)) ([review](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704)) ([acceptance](https://forums.swift.org/t/accepted-se-0470-global-actor-isolated-conformances/79189)) ([amendment pitch](https://forums.swift.org/t/pitch-amend-se-0466-se-0470-to-improve-isolation-inference/79854)) ([amendment review](https://forums.swift.org/t/amendment-se-0470-global-actor-isolated-conformances/80999)) ## Introduction From a828476fea297af3ac38889b39cbbbee3a08b3e0 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Wed, 9 Jul 2025 13:03:46 +0100 Subject: [PATCH 4419/4563] Fix sample code in `0428-resolve-distributed-actor-protocols.md` This code caused an error with `ActorSystem` type parameter unbound: ``` import Distributed @Resolvable protocol Greeter where ActorSystem: DistributedActorSystem { distributed func greet(name: String) -> String } ``` --- proposals/0428-resolve-distributed-actor-protocols.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0428-resolve-distributed-actor-protocols.md b/proposals/0428-resolve-distributed-actor-protocols.md index 52a6964f52..e7b4128e3d 100644 --- a/proposals/0428-resolve-distributed-actor-protocols.md +++ b/proposals/0428-resolve-distributed-actor-protocols.md @@ -90,7 +90,7 @@ The macro must be attached to the a `protocol` declaration that is a `Distribute import Distributed @Resolvable -protocol Greeter where ActorSystem: DistributedActorSystem { +protocol Greeter: DistributedActor where ActorSystem: DistributedActorSystem { distributed func greet(name: String) -> String } ``` From 1b1ab5ca1a3df162a580863065be9053e63c06f1 Mon Sep 17 00:00:00 2001 From: Zev Eisenberg Date: Wed, 9 Jul 2025 13:14:31 -0400 Subject: [PATCH 4420/4563] Add SE proposal for Codable error printing (#2843) * Add SE proposal for Codable error printing. * Claim SE-0489 --------- Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- proposals/0489-codable-error-printing.md | 160 +++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 proposals/0489-codable-error-printing.md diff --git a/proposals/0489-codable-error-printing.md b/proposals/0489-codable-error-printing.md new file mode 100644 index 0000000000..c7bbfa3673 --- /dev/null +++ b/proposals/0489-codable-error-printing.md @@ -0,0 +1,160 @@ +# Improve `EncodingError` and `DecodingError`'s printed descriptions + +* Proposal: [SE-0489](0489-codable-error-printing.md) +* Authors: [Zev Eisenberg](https://github.com/ZevEisenberg) +* Review Manager: [Xiaodi Wu](https://github.com/xwu) +* Status: **Active review (July 9...22, 2025)** +* Implementation: https://github.com/swiftlang/swift/pull/80941 +* Review: ([pitch](https://forums.swift.org/t/pitch-improve-encodingerror-and-decodingerror-s-printed-descriptions/79872)) ([review]()) + +## Introduction + +`EncodingError` and `DecodingError` do not specify any custom debug description. The default descriptions bury the useful information in a format that is difficult to read. Less experienced developers may assume they are not human-readable at all, even though they contain useful information. The proposal is to conform `EncodingError` and `DecodingError` to `CustomDebugStringConvertible` and provide nicely formatted debug output. + +## Motivation + +Consider the following example model structs: + +```swift +struct Person: Codable { + var name: String + var home: Home +} + +struct Home: Codable { + var city: String + var country: Country +} + +struct Country: Codable { + var name: String + var population: Int +} +``` + +Now let us attempt to decode some invalid JSON. In this case, it is missing a field in a deeply nested struct. + +```swift +// Note missing "population" field +let jsonData = Data(""" +[ + { + "name": "Ada Lovelace", + "home": { + "city": "London", + "country": { + "name": "England" + } + } + } +] +""".utf8) + +do { + _ = try JSONDecoder().decode([Person].self, from: jsonData) +} catch { + print(error) +} +``` + +This outputs the following: + +`keyNotFound(CodingKeys(stringValue: "population", intValue: nil), Swift.DecodingError.Context(codingPath: [_CodingKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "home", intValue: nil), CodingKeys(stringValue: "country", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: \"population\", intValue: nil) (\"population\").", underlyingError: nil))` + +All the information you need is there: +- The kind of error: a missing key +- Which key was missing: `"population"` +- The path of the value that had a missing key: index 0, then key `"home"`, then key `"country"` +- The underlying error: none, in this case + +However, it is not easy or pleasant to read such an error, particularly when dealing with large structures or long type names. It is common for newer developers to assume the above output is some kind of log spam and not even realize it contains exactly the information they are looking for. + +## Proposed solution + +Conform `EncodingError` and `DecodingError` to `CustomDebugStringConvertible` and provide a clean, readable debug description for each. Here is an example of the proposed change for the same decoding error as above. + +``` +Key 'population' not found in keyed decoding container. +Debug description: No value associated with key CodingKeys(stringValue: "population", intValue: nil) ("population"). +Path: [0]/home/country +``` + +Complete examples of the before/after diffs are available in the description of the [implementation pull request](https://github.com/swiftlang/swift/pull/80941) that accompanies this proposal. + +**Note 1:** this proposal is _not_ intended to specify an exact output format. The above is provided as an example, and is not a guarantee of current or future behavior. You are still free to inspect the contents of thrown errors directly if you need to detect specific problems. + +**Note 2:** the output could be further improved by modifying `JSONDecoder` to write a better debug description. See [Future Directions](#future-directions) for more. + +## Detailed design + +```swift +@available(SwiftStdlib 6.2, *) +extension EncodingError: CustomDebugStringConvertible { + public var debugDescription: String {...} +} + +@available(SwiftStdlib 6.2, *) +extension DecodingError: CustomDebugStringConvertible { + public var debugDescription: String {...} +} +``` + +## Source compatibility + +The new conformance changes the result of converting an `EncodingError` or `DecodingError` value to a string. This changes observable behavior: code that attempts to parse the result of `String(describing:)` or `String(reflecting:)` can be misled by the change of format. + +However, the documentation of these interfaces explicitly state that when the input type conforms to none of the standard string conversion protocols, then the result of these operations is unspecified. + +Changing the value of an unspecified result is not considered to be a source incompatible change. + +## ABI compatibility + +The proposal conforms two previously existing stdlib types to a previously existing stdlib protocol. This is technically an ABI breaking change: on ABI-stable platforms, we may have preexisting Swift binaries that implement a retroactive `CustomDebugStringConvertible` conformance, or binaries that assume that the existing error types do _not_ conform to the protocol. + +We do not expect this to be an issue in practice, since checking an arbitrary error for conformance to `CustomDebugStringConvertible` at run-time seems unlikely. In the event that it now conforms where it didn't before, it will presumably use the new implementation instead of whatever fallback was being provided previously. + +## Implications on adoption + +### Conformance to `CustomDebugStringConvertible` + +The conformance to `CustomDebugStringConvertible` is not backdeployable. As a result, code that runs on ABI-stable platforms with earlier versions of the standard library won't output the new debug descriptions. + +### `debugDescription` Property + +It is technically possible to backdeploy the `debugDescription` property, but without the protocol conformance, it is of limited utility. + +## Future directions + +### Better error generation from Foundation encoders/decoders + +The debug descriptions generated in Foundation sometimes contain the same information as the new debug descriptions from this proposal. A future change to the standard JSON and Plist encoders and decoders could provide more compact debug descriptions once they can be sure they have the new standard library descriptions available. They could also use a more compact description when rendering the description of a `CodingKey`. Using part of the example from above: + +``` +Debug description: No value associated with key CodingKeys(stringValue: "population", intValue: nil) ("population"). +``` + +The `CodingKeys(stringValue: "population", intValue: nil) ("population")` part is coming from the default `description` of `CodingKey`, plus an extra parenthesized string value at the end for good measure. The Foundation (de|en)coders could construct a more compact description that does not repeat the key, just like we do within this proposal in the context of printing a coding path. + +### Print context of surrounding lines in source data + +When a decoding error occurs, in addition to printing the path, the error message could include some surrounding lines from the source data. This was explored in this proposal's antecedent, [UsefulDecode](https://github.com/ZevEisenberg/UsefulDecode). But more detailed messages would require passing more context data from the decoder and changing the public interface of `DecodingError` to carry more data. This option is best left as something to think about as [we design `Codable`'s successor](https://forums.swift.org/t/the-future-of-serialization-deserialization-apis/78585). But just to give an example of the _kind_ of context that could be provided (please do not read anything into the specifics of the syntax; this is a sketch, not a proposal): + +``` +Value not found: expected 'name' (String) at [0]/address/city/birds/[1]/name, got: +{ + "feathers" : "some", + "name" : null +} +``` + +## Alternatives considered + +We could conform `EncodingError` and `DecodingError` to `CustomStringConvertible` instead of `CustomDebugStringConvertible`. The use of the debug-flavored protocol emphasizes that the new descriptions aren't intended to be used outside debugging contexts. This is in keeping with the precedent set by [SE-0445](0445-string-index-printing.md). + +We could change `CodingKey.description` to return the bare string or int value, which would improve the formatting and reduce duplication as seen in [Proposed solution](#proposed-solution). But changing the exsting implementation of an existing public method seems needlessly risky, as existing code may (however inadvisably) be depending on the format of the current `description`. Additionally, the encoders and decoders in Foundation should not depend on implementation details of `CodingKey.description` that are not guaranteed. If we want the encoders/decoders to produce better formatting, they should be responsible for generating those strings directly. See [further discussion in the PR](https://github.com/swiftlang/swift/pull/80941#discussion_r2064277369). + +## Acknowledgments + +This proposal follows in the footsteps of [SE-0445](0445-string-index-printing.md). Thanks to [Karoy Lorentey](https://github.com/lorentey) for writing that proposal, and for flagging it as similar to this one. + +Thanks to Kevin Perry [for suggesting](https://forums.swift.org/t/the-future-of-serialization-deserialization-apis/78585/77) that this would make a good standalone change regardless of the direction of future serialization tools, and for engaging with the PR from the beginning. From 06b13a1d2509d18a47bdbba9df80c7d7142c3b7f Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 9 Jul 2025 13:22:33 -0400 Subject: [PATCH 4421/4563] Add review link for SE-0489 (#2911) --- proposals/0489-codable-error-printing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0489-codable-error-printing.md b/proposals/0489-codable-error-printing.md index c7bbfa3673..2d3968aea6 100644 --- a/proposals/0489-codable-error-printing.md +++ b/proposals/0489-codable-error-printing.md @@ -5,7 +5,7 @@ * Review Manager: [Xiaodi Wu](https://github.com/xwu) * Status: **Active review (July 9...22, 2025)** * Implementation: https://github.com/swiftlang/swift/pull/80941 -* Review: ([pitch](https://forums.swift.org/t/pitch-improve-encodingerror-and-decodingerror-s-printed-descriptions/79872)) ([review]()) +* Review: ([pitch](https://forums.swift.org/t/pitch-improve-encodingerror-and-decodingerror-s-printed-descriptions/79872)) ([review](https://forums.swift.org/t/se-0489-improve-encodingerror-and-decodingerrors-printed-descriptions/81021)) ## Introduction From 5a7b84c55915f8147eb5d16976eddac86b3ee7ee Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 9 Jul 2025 13:45:27 -0400 Subject: [PATCH 4422/4563] Fixups for SE-0466 (#2908) * Fixups for SE-0466 * Edit a typo in the header field * Add a succinct revision history * Revise status for automated tooling --- proposals/0466-control-default-actor-isolation.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/proposals/0466-control-default-actor-isolation.md b/proposals/0466-control-default-actor-isolation.md index 5c1c0a08ab..0da31e90d4 100644 --- a/proposals/0466-control-default-actor-isolation.md +++ b/proposals/0466-control-default-actor-isolation.md @@ -3,7 +3,7 @@ * Proposal: [SE-0466](0466-control-default-actor-isolation.md) * Authors: [Holly Borla](https://github.com/hborla), [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Implemented (Swift 6.2)**, **Amendmendment review July 8 - 15, 2025** +* Status: **Active review (July 8...15, 2025)** * Vision: [Improving the approachability of data-race safety](/visions/approachable-concurrency.md) * Review: ([pitch](https://forums.swift.org/t/pitch-control-default-actor-isolation-inference/77482))([review](https://forums.swift.org/t/se-0466-control-default-actor-isolation-inference/78321))([acceptance](https://forums.swift.org/t/accepted-se-0466-control-default-actor-isolation-inference/78926))([amendment pitch](https://forums.swift.org/t/pitch-amend-se-0466-se-0470-to-improve-isolation-inference/79854))([amendment review](https://forums.swift.org/t/amendment-se-0466-control-default-actor-isolation-inference/80994)) @@ -238,6 +238,11 @@ The enum approach introduces a different way of writing main actor isolation tha The primary argument for using an enum is that it can be extended in the future to support custom global actor types. This proposal deliberately puts supporting custom global actors in the alternatives considered and not future directions, because defaulting a module to a different global actor does not help improve progressive disclosure for concurrency. +## Revision history + +* Changes in amendment review: + * Disable `@MainActor` inference when type conforms to a `SendableMetatype` protocol + ## Acknowledgments Thank you to John McCall for providing much of the motivation for this pitch in the approachable data-race safety vision document, and to Michael Gottesman for helping with the implementation. From 07e7020336a3760581e6bd4792bd598a7e9b43e8 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Thu, 10 Jul 2025 11:07:30 -0500 Subject: [PATCH 4423/4563] Additions and clarifications to ST-0011: Issue Handling Traits proposal (#2912) * Link to final enablement PR * Expand discussion of inout alternative to encompass the Void? suggestion * Clarify .system and .apiMisused policy --- .../testing/0011-issue-handling-traits.md | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/proposals/testing/0011-issue-handling-traits.md b/proposals/testing/0011-issue-handling-traits.md index d27f2e9a5f..c6592bba6f 100644 --- a/proposals/testing/0011-issue-handling-traits.md +++ b/proposals/testing/0011-issue-handling-traits.md @@ -6,7 +6,8 @@ * Status: **Active Review (Jun 24 - July 8, 2025)** * Implementation: [swiftlang/swift-testing#1080](https://github.com/swiftlang/swift-testing/pull/1080), [swiftlang/swift-testing#1121](https://github.com/swiftlang/swift-testing/pull/1121), - [swiftlang/swift-testing#1136](https://github.com/swiftlang/swift-testing/pull/1136) + [swiftlang/swift-testing#1136](https://github.com/swiftlang/swift-testing/pull/1136), + [swiftlang/swift-testing#1198](https://github.com/swiftlang/swift-testing/pull/1198) * Review: ([pitch](https://forums.swift.org/t/pitch-issue-handling-traits/80019)) ([review](https://forums.swift.org/t/st-0011-issue-handling-traits/80644)) ## Introduction @@ -303,13 +304,15 @@ Issue handling traits are applied to a test by a user, and are only intended for handling issues recorded by tests written by the user. If an issue is recorded by the testing library itself or the underlying system, not due to a failure within the tests being run, such an issue will not be passed to an issue -handling trait. +handling trait. Similarly, an issue handling trait should not return an issue +which represents a problem they could not have caused in their test. Concretely, this policy means that issues for which the value of the `kind` property is `.system` will not be passed to the closure of an issue handling -trait. Similarly, it is not supported for a closure passed to +trait. Also, it is not supported for a closure passed to `compactMapIssues(_:)` to return an issue for which the value of `kind` is -`.system`. +either `.system` or `.apiMisused` (unless the passed-in issue had that kind, +which should only be possible for `.apiMisused`). ## Detailed design @@ -507,15 +510,29 @@ the handler encounters an error. The closure parameter of `compactMapIssues(_:)` currently has one parameter of type `Issue` and returns an optional `Issue?` to support returning `nil` in -order to suppress an issue. This closure could instead have a `Void` return type -and its parameter could be `inout`, and this would mean that use cases that -involve modifying an issue would not need to first copy the issue to a variable -(`var`) before modifying it. - -However, in order to _suppress_ an issue, the parameter would also need to -become optional (`inout Issue?`) and this would mean that all usages would first -need to be unwrapped. This feels non-ergonomic, and would differ from the -standard library's typical pattern for `compactMap` functions. +order to suppress an issue. If an issue handler wants to modify an issue, it +first needs to copy it to a mutable variable (`var`), mutate it, then return the +modified copy. These copy and return steps require extra lines of code within +the closure, and they could be eliminated if the parameter was declared `inout`. + +The most straightforward way to achieve this would be for the closure to instead +have a `Void` return type and for its parameter to become `inout`. However, in +order to _suppress_ an issue, the parameter would also need to become optional +(`inout Issue?`) and this would mean that all usages would first need to be +unwrapped. This feels non-ergonomic, and would differ from the standard +library's typical pattern for `compactMap` functions. + +Another way to achieve this ([suggested](https://forums.swift.org/t/st-0011-issue-handling-traits/80644/3) +by [@Val](https://forums.swift.org/u/Val) during proposal review) could be to +declare the return type of the closure `Void?` and the parameter type +`inout Issue` (non-optional). This alternative would not require unwrapping the +issue first and would still permit suppressing issues by returning `nil`. It +could also make one of the alternative names (such as `transformIssues` +discussed below) more fitting. However, this is a novel API pattern which isn't +widely used in Swift, and may be confusing to users. There were also concerns +raised by other reviewers that the language's implicit return for `Void` may not +be intentionally applied to `Optional` and that this mechanism could break +in the future. ### Alternate names for the static trait functions From e95300cb1c50c88f189fcbc85837014a7906b933 Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Thu, 10 Jul 2025 18:20:38 +0000 Subject: [PATCH 4424/4563] revise proposal --- ...-environment-dependent-shared-libraries.md | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/proposals/NNNN-environment-dependent-shared-libraries.md b/proposals/NNNN-environment-dependent-shared-libraries.md index d1068526ea..5c7a228bf9 100644 --- a/proposals/NNNN-environment-dependent-shared-libraries.md +++ b/proposals/NNNN-environment-dependent-shared-libraries.md @@ -1,21 +1,21 @@ -# Environment Dependent Shared Libraries +# Environment Constrained Shared Libraries -* Proposal: [SE-NNNN](NNNN-environment-dependent-shared-libraries.md) +* Proposal: [SE-NNNN](NNNN-environment-constrained-shared-libraries.md) * Authors: [tayloraswift](https://github.com/tayloraswift) * Review Manager: TBD * Implementation: [swiftlang/swift-package-manager#8249](https://github.com/swiftlang/swift-package-manager/pull/8249) -* Documentation: [How to use Environment-Dependent Shared Libraries](https://github.com/swiftlang/swift-package-manager/blob/b586467575580f2365e8f5a29c949379724db795/Documentation/EDSLs.md) +* Documentation: [How to use Environment-Constrained Shared Libraries](https://github.com/swiftlang/swift-package-manager/blob/b586467575580f2365e8f5a29c949379724db795/Documentation/ECSLs.md) * Bugs: [SR-5714](https://github.com/swiftlang/swift-package-manager/issues/5714) ## Introduction -SwiftPM currently has no support for non-system binary library dependencies on Linux. This proposal adds support for **Environment Dependent Shared Libraries**, which are a type of dynamic library that is shared across a fleet of machines and can be upgraded without recompiling and redeploying all applications running on those machines. We will distribute Environment Dependent Shared Libraries through the existing `.artifactbundle` format. +SwiftPM currently has no support for non-system binary library dependencies on Linux. This proposal adds support for **Environment Constrained Shared Libraries**, which are a type of dynamic library that is shared across a fleet of machines and can be upgraded without recompiling and redeploying all applications running on those machines. We will distribute Environment Constrained Shared Libraries through the existing `.artifactbundle` format. Swift-evolution thread: [Discussion thread](https://forums.swift.org/t/pitch-replaceable-library-plugins/77605) -Example Producer: [swift-edsl-example](https://github.com/tayloraswift/swift-edsl-example) +Example Producer: [swift-ECSL-example](https://github.com/tayloraswift/swift-ECSL-example) -Example Consumer: [swift-edsl-example-client](https://github.com/tayloraswift/swift-edsl-example-client) +Example Consumer: [swift-ECSL-example-client](https://github.com/tayloraswift/swift-ECSL-example-client) ## Motivation @@ -30,51 +30,54 @@ While macOS has Dynamic Library support through XCFrameworks, on Linux we curren On Linux, there are a lot of obstacles to having fully general support for Dynamic Libraries. Swift is not ABI stable on Linux, and Linux itself is not a single platform but a wide range of similar platforms that provide few binary compatibility guarantees. This means it is pretty much impossible for a public Swift library to vend precompiled binaries that will Just Work for everyone, and we are not going to try to solve that problem in this proposal. -Instead, we will focus on **Environment Dependent Shared Libraries** (EDSLs). We choose this term to emphasize the distinction between our use case and fully general Dynamic Libraries. +Instead, we will focus on **Environment Constrained Shared Libraries** (ECSLs). We choose this term to emphasize the distinction between our use case and fully general Dynamic Libraries. -### Organization-Defined Platforms (ODPs) +### Target environment -Unlike fully general Dynamic Libraries, you would distribute Environment Dependent Shared Libraries strictly for internal consumption within an organization, or to a small set of paying clients. +Unlike fully general Dynamic Libraries, you would distribute Environment Constrained Shared Libraries strictly for controlled consumption within a known environment, such as a fleet of servers maintained by a single organization. -The organization that distributes an EDSL is responsible for defining what exactly constitutes a “platform” for their purposes. An Organization-Defined Platform (ODP) is not necessarily an operating system or architecture, or even a specific distribution of an operating system. A trivial example of two ODPs might be: +ECSLs are an advanced tool, and maintaining the prerequisite environment to deploy them safely is neither trivial nor recommended for most users. -1. Ubuntu 24.04 with the Swift 6.0.3 runtime installed at `/home/ubuntu/swift` -2. Ubuntu 24.04 with the Swift 6.0.3 runtime installed at `/home/ubuntu/swift-runtime` +The organization that distributes an ECSL is responsible for defining what exactly constitutes a “platform” for their purposes. An organization-defined platform is not necessarily an operating system or architecture, or even a specific distribution of an operating system. A trivial example of two such platforms might be: -Concepts like Platform Triples are not sufficient to describe an ODP. Even though both ODPs above would probably share the Triple `aarch64-unknown-linux-gnu`, Swift code compiled (without `--static-swift-stdlib`) for one would never be able to run on the other. +1. Ubuntu 24.04 with the Swift 6.1.2 runtime installed at `/home/ubuntu/swift` +2. Ubuntu 24.04 with the Swift 6.1.2 runtime installed at `/home/ubuntu/swift-runtime` -Organizations add and remove ODPs as needed, and trying to define a global registry of all possible ODPs is a non-goal. +Concepts like Platform Triples are not sufficient to describe an ECSL deployment target. Even though both “platforms” above would probably share the Triple `aarch64-unknown-linux-gnu`, Swift code compiled (without `--static-swift-stdlib`) for one would never be able to run on the other. -To keep things simple, we identify ODPs by the URL of the Artifact Bundle that contains the EDSL. +Organizations will add and remove environments as needed, and trying to define a global registry of all possible environments is a non-goal. -### Creating EDSLs +The proposed ECSL distribution format does not support shipping multiple variants of ECSLs targeting multiple environments in the same Artifact Bundle, nor does it specify a standardized means for identifying the environment in which a particular ECSL is intended to execute in. +Users are responsible for computing the correct URL of the Artifact Bundle for the environment they are building for, possibly within the package manifest. Swift tooling will not, on its own, diagnose or prevent the installation of an incompatible ECSL. -To compile an EDSL, you just need to build an ordinary SwiftPM library product with the `-enable-library-evolution` flag. This requires no modifications to SwiftPM. +### Creating ECSLs -You would package an EDSL as an `.artifactbundle` just as you would an executable, with the following differences: +To compile an ECSL, you just need to build an ordinary SwiftPM library product with the `-enable-library-evolution` flag. This requires no modifications to SwiftPM. + +You would package an ECSL as an `.artifactbundle` just as you would an executable, with the following differences: - The `info.json` must have `schemaVersion` set to `1.2` or higher. - The artifact type must be `library`, a new enum case introduced in this proposal. - The artifact must have exactly one variant in the `variants` list, and the `supportedTriples` field is forbidden. - The artifact payload must include the `.swiftinterface` file corresponding to the actual library object. -Because SwiftPM is not (and cannot be) aware of a particular organization’s ODPs, this enforces the requirement that each ODP must have its own Artifact Bundle. +Because SwiftPM is not (and cannot be) aware of a particular organization’s set of deployment environments, this enforces the requirement that each environment must have its own Artifact Bundle. -The organization that distributes the EDSL is responsible for upholding ABI stability guarantees, including the exact Swift compiler and runtime versions needed to safely consume the EDSL. +The organization that distributes the ECSL is responsible for upholding ABI stability guarantees, including the exact Swift compiler and runtime versions needed to safely consume the ECSL. -### Consuming EDSLs +### Consuming ECSLs -To consume an EDSL, you would add a `binaryTarget` to your `Package.swift` manifest, just as you would for an executable. Because ODPs are identified by the URL of the Artifact Bundle, there are no new fields in the `PackageDescription` API. +To consume an ECSL, you would add a `binaryTarget` to your `Package.swift` manifest, just as you would for an executable. Because organizations are responsible for defining their set of supported environments, they are also responsible for defining the URLs that the Artifact Bundles for each environment are hosted under, so there are no new fields in the `PackageDescription` API. -We expect that the logic for selecting the correct EDSL for a given ODP would live within the `Package.swift` file, that it would be highly organization-specific, and that it would be manipulated using existing means such as environment variables. +We expect that the logic for selecting the correct ECSL for a given environment would live within the `Package.swift` file, that it would be highly organization-specific, and that it would be manipulated using existing means such as environment variables. -### Deploying EDSLs +### Deploying ECSLs -Deploying EDSLs does not involve SwiftPM or Artifact Bundles at all. You would deploy an EDSL by copying the latest binaries to the appropriate `@rpath` location on each machine in your fleet. The `@rpath` location is part of the ODP definition, and is not modeled by SwiftPM. +Deploying ECSLs does not involve SwiftPM or Artifact Bundles at all. You would deploy an ECSL by copying the latest binaries to the appropriate `@rpath` location on each machine in your fleet. The `@rpath` location is part of the organization-specific environment definition, and is not modeled by SwiftPM. -Some organizations might choose to forgo the `@rpath` mechanism entirely and simply install the EDSLs in a system-wide location. +Some organizations might choose to forgo the `@rpath` mechanism entirely and simply install the ECSLs in a system-wide location. ## Detailed design @@ -111,7 +114,7 @@ Below is an example of an `info.json` file for an Artifact Bundle containing a s } ``` -The artifact must have exactly one variant in the `variants` list, and the `supportedTriples` field is forbidden. An EDSL Artifact Bundle can contain multiple libraries at the top level. +The artifact must have exactly one variant in the `variants` list, and the `supportedTriples` field is forbidden. An ECSL Artifact Bundle can contain multiple libraries at the top level. Below is an example of the layout of an Artifact Bundle containing a single library called `MyLibrary`. Only the `info.json` must appear at the root of the Artifact Bundle; all other files can appear at whatever paths are defined in the `info.json`, as long as they are within the Artifact Bundle. @@ -123,12 +126,12 @@ Below is an example of the layout of an Artifact Bundle containing a single libr 📝 info.json ``` -A macOS Artifact Bundle would contain a `.dylib` instead of a `.so`. EDSLs will be supported on macOS, although we expect this will be an exceedingly rare use case. +A macOS Artifact Bundle would contain a `.dylib` instead of a `.so`. ECSLs will be supported on macOS, although we expect this will be an exceedingly rare use case. ## Security -EDSLs are not intended for public distribution, and are not subject to the same security concerns as public libraries. Organizations that distribute EDSLs are responsible for ensuring that the EDSLs are safe to consume. +ECSLs are not intended for public distribution, and are not subject to the same security concerns as public libraries. Organizations that distribute ECSLs are responsible for ensuring that the ECSLs are safe to consume. ## Impact on existing packages @@ -138,19 +141,19 @@ There will be no impact on existing packages. All Artifact Bundle schema changes ## Alternatives considered -### Extending Platform Triples to model ODPs +### Extending Platform Triples to model deployment targets SwiftPM currently uses Platform Triples to select among artifact variants when consuming executables. This is workable because it is usually feasible to build executables that are portable across the range of platforms encompassed by a single Platform Triple. -We could extend Platform Triples to model ODPs, but this would privilege a narrow set of predefined deployment architectures, and if you wanted to add a new ODP, you would have to modify SwiftPM to teach it to recognize the new ODP. +We could extend Platform Triples to model ECSL deployment targets, but this would privilege a narrow set of predefined deployment architectures, and if you wanted to add a new environment, you would have to modify SwiftPM to teach it to recognize the new environment. -### Supporting multiple variants of an EDSL in the same Artifact Bundle +### Supporting multiple variants of an ECSL in the same Artifact Bundle -We could allow an Artifact Bundle to contain multiple variants of an EDSL, but we would still need to support a way to identify those variants, which in practice makes SwiftPM aware of ODPs. +We could allow an Artifact Bundle to contain multiple variants of an ECSL, but we would still need to support a way to identify those variants, which in practice forces SwiftPM to become aware of organization-defined environments. -We also don’t see much value in this feature, as you would probably package and upload EDSLs using one CI/CD workflow per ODP anyway. Combining artifacts would require some kind of synchronization mechanism to await all pipelines before fetching and merging bundles. +We also don’t see much value in this feature, as you would probably package and upload ECSLs using one CI/CD workflow per environment anyway. Combining artifacts would require some kind of synchronization mechanism to await all pipelines before fetching and merging bundles. -One benefit of merging bundles would be that it reduces the number of checksums you need to keep track of, but we expect that most organizations will have a very small number of ODPs, with new ODPs continously phasing out old ODPs. +One benefit of merging bundles would be that it reduces the number of checksums you need to keep track of, but we expect that most organizations will have a very small number of supported environments, with new environments continously phasing out old environments. ### Using a different `ArtifactType` name besides `library` From e731f51d247ac853175f214c3b59f5b80a4c2f8d Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Thu, 10 Jul 2025 18:24:53 +0000 Subject: [PATCH 4425/4563] rename proposal --- ...raries.md => NNNN-environment-constrained-shared-libraries.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{NNNN-environment-dependent-shared-libraries.md => NNNN-environment-constrained-shared-libraries.md} (100%) diff --git a/proposals/NNNN-environment-dependent-shared-libraries.md b/proposals/NNNN-environment-constrained-shared-libraries.md similarity index 100% rename from proposals/NNNN-environment-dependent-shared-libraries.md rename to proposals/NNNN-environment-constrained-shared-libraries.md From 345640f7a3d924a816a455ea21b8ba4245162514 Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Thu, 10 Jul 2025 21:07:58 +0000 Subject: [PATCH 4426/4563] example URLs --- proposals/NNNN-environment-constrained-shared-libraries.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-environment-constrained-shared-libraries.md b/proposals/NNNN-environment-constrained-shared-libraries.md index 5c7a228bf9..2d963c6c57 100644 --- a/proposals/NNNN-environment-constrained-shared-libraries.md +++ b/proposals/NNNN-environment-constrained-shared-libraries.md @@ -13,9 +13,9 @@ SwiftPM currently has no support for non-system binary library dependencies on L Swift-evolution thread: [Discussion thread](https://forums.swift.org/t/pitch-replaceable-library-plugins/77605) -Example Producer: [swift-ECSL-example](https://github.com/tayloraswift/swift-ECSL-example) +Example Producer: [swift-dynamic-library-example](https://github.com/tayloraswift/swift-dynamic-library-example) -Example Consumer: [swift-ECSL-example-client](https://github.com/tayloraswift/swift-ECSL-example-client) +Example Consumer: [swift-dynamic-library-example-client](https://github.com/tayloraswift/swift-dynamic-library-example-client) ## Motivation From e3b6bb27df3f1992da04a2ae859d0ec74f5eb91e Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Thu, 10 Jul 2025 21:11:13 +0000 Subject: [PATCH 4427/4563] =?UTF-8?q?library=20=E2=86=92=20dynamicLibrary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NNNN-environment-constrained-shared-libraries.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/NNNN-environment-constrained-shared-libraries.md b/proposals/NNNN-environment-constrained-shared-libraries.md index 2d963c6c57..f2394d4d2f 100644 --- a/proposals/NNNN-environment-constrained-shared-libraries.md +++ b/proposals/NNNN-environment-constrained-shared-libraries.md @@ -57,7 +57,7 @@ To compile an ECSL, you just need to build an ordinary SwiftPM library product w You would package an ECSL as an `.artifactbundle` just as you would an executable, with the following differences: - The `info.json` must have `schemaVersion` set to `1.2` or higher. -- The artifact type must be `library`, a new enum case introduced in this proposal. +- The artifact type must be `dynamicLibrary`, a new enum case introduced in this proposal. - The artifact must have exactly one variant in the `variants` list, and the `supportedTriples` field is forbidden. - The artifact payload must include the `.swiftinterface` file corresponding to the actual library object. @@ -84,12 +84,12 @@ Some organizations might choose to forgo the `@rpath` mechanism entirely and sim ### Schema extensions -We will extend the `ArtifactsArchiveMetadata` schema to include a new `library` case in the `ArtifactType` enum. +We will extend the `ArtifactsArchiveMetadata` schema to include a new `dynamicLibrary` case in the `ArtifactType` enum. ```diff public enum ArtifactType: String, RawRepresentable, Decodable { case executable -+ case library ++ case dynamicLibrary case swiftSDK } ``` @@ -106,7 +106,7 @@ Below is an example of an `info.json` file for an Artifact Bundle containing a s "schemaVersion": "1.2", "artifacts": { "MyLibrary": { - "type": "library", + "type": "dynamicLibrary", "version": "1.0.0", "variants": [{ "path": "MyLibrary" }] } @@ -155,6 +155,6 @@ We also don’t see much value in this feature, as you would probably package an One benefit of merging bundles would be that it reduces the number of checksums you need to keep track of, but we expect that most organizations will have a very small number of supported environments, with new environments continously phasing out old environments. -### Using a different `ArtifactType` name besides `library` +### Using a different `ArtifactType` name besides `dynamicLibrary` We intentionally preserved the structure of the `variants` list in the `info.json` file, despite imposing the current restriction of one variant per library, in order to allow this format to be extended in the future to support fully general Dynamic Libraries. From e320e31d49ce6833736ab268368034f0fe68f0c6 Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Thu, 10 Jul 2025 21:13:29 +0000 Subject: [PATCH 4428/4563] ce --- proposals/NNNN-environment-constrained-shared-libraries.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-environment-constrained-shared-libraries.md b/proposals/NNNN-environment-constrained-shared-libraries.md index f2394d4d2f..9a170b0058 100644 --- a/proposals/NNNN-environment-constrained-shared-libraries.md +++ b/proposals/NNNN-environment-constrained-shared-libraries.md @@ -90,6 +90,7 @@ We will extend the `ArtifactsArchiveMetadata` schema to include a new `dynamicLi public enum ArtifactType: String, RawRepresentable, Decodable { case executable + case dynamicLibrary + case staticLibrary case swiftSDK } ``` @@ -126,7 +127,7 @@ Below is an example of the layout of an Artifact Bundle containing a single libr 📝 info.json ``` -A macOS Artifact Bundle would contain a `.dylib` instead of a `.so`. ECSLs will be supported on macOS, although we expect this will be an exceedingly rare use case. +A macOS Artifact Bundle would contain a `.dylib` instead of a `.so`. ECSLs will be supported on macOS, although we expect this will be an exceedingly rare use case, as this need is already well-served by the XCFramework. ## Security From fa2415526f8ee8210fd3675ebb1581b967243cd3 Mon Sep 17 00:00:00 2001 From: Suzy Ratcliff Date: Tue, 15 Jul 2025 11:07:43 -0700 Subject: [PATCH 4429/4563] Updates to the issue severity proposal --- proposals/testing/0013-issue-severity-warning.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/testing/0013-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md index fe89f37c1f..a48c3b1187 100644 --- a/proposals/testing/0013-issue-severity-warning.md +++ b/proposals/testing/0013-issue-severity-warning.md @@ -77,11 +77,11 @@ Issue.record("My comment", severity: .warning) Here is the `Issue.record` method definition with severity as a parameter. ```swift - /// Record an issue when a running test fails unexpectedly. + /// Record an issue when a running test and an issue occurs. /// /// - Parameters: /// - comment: A comment describing the expectation. - /// - severity: The severity of the issue. + /// - severity: The severity level of the issue. This factor impacts whether the issue constitutes a failure. /// - sourceLocation: The source location to which the issue should be /// attributed. /// @@ -117,7 +117,7 @@ public var severity: Severity { get set } ``` -- `isFailure`: A boolean property to determine if an issue results in a test failure, thereby helping in result aggregation and reporting. +- `isFailure`: A boolean computed property to determine if an issue results in a test failure, thereby helping in result aggregation and reporting. ```swift extension Issue { @@ -154,7 +154,7 @@ This revision aims to clarify the functionality and usage of the `Severity` enum ### Integration with supporting tools -Issue severity will be in the event stream output when a `issueRecorded` event occurs. This will be a breaking change because some tools may assume that all `issueRecorded` events are failing. Due to this we will be bumping the event stream version and v1 will maintain it's behavior and not output any events for non failing issues. We will also be adding `isFailure` to the issue so that clients will know if the issue should be treated as a failure. +Issue severity will be in the event stream output when a `issueRecorded` event occurs. This will be a breaking change because some tools may assume that all `issueRecorded` events are failing. Due to this we will be bumping the event stream version and v1 will maintain it's behavior and not output any events for non failing issues. We will also be adding `isFailure` to the issue so that clients will know if the issue should be treated as a failure. `isFailure` is a computed property. The JSON event stream ABI will be amended correspondingly: From f374c6c06f79b07e28615cbe29c8e3b5767b6921 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 15 Jul 2025 12:58:36 -0700 Subject: [PATCH 4430/4563] [SE-0446] Amend to clarify that types within nonisolated types don't get default main actor inference (#2915) --- proposals/0466-control-default-actor-isolation.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/proposals/0466-control-default-actor-isolation.md b/proposals/0466-control-default-actor-isolation.md index 0da31e90d4..5a1e975d25 100644 --- a/proposals/0466-control-default-actor-isolation.md +++ b/proposals/0466-control-default-actor-isolation.md @@ -66,6 +66,7 @@ When the default actor isolation is specified as `MainActor`, declarations are i * All declarations inside an `actor` type, including static variables, methods, initializers, and deinitializers * Declarations that cannot have global actor isolation, including typealiases, import statements, enum cases, and individual accessors * Declarations whose primary definition directly conforms to a protocol that inherits `SendableMetatype` +* Declarations that are types nested within a nonisolated type The following code example shows the inferred actor isolation in comments given the code is built with `-default-isolation MainActor`: @@ -112,7 +113,13 @@ struct S: P { nonisolated protocol Q: Sendable { } // nonisolated -struct S2: Q { } +struct S2: Q { + // nonisolated + struct Inner { } + + // @MyActor + struct IsolatedInner: P +} // @MainActor struct S3 { } From 6c2e08db9063e0ffa425dad9b6bab15d22ba4722 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Wed, 16 Jul 2025 09:55:34 -0400 Subject: [PATCH 4431/4563] Accept SE-0470 amendment (#2917) --- proposals/0470-isolated-conformances.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0470-isolated-conformances.md b/proposals/0470-isolated-conformances.md index 0854f6da16..879bd8d05e 100644 --- a/proposals/0470-isolated-conformances.md +++ b/proposals/0470-isolated-conformances.md @@ -3,11 +3,11 @@ * Proposal: [SE-0470](0470-isolated-conformances.md) * Authors: [Doug Gregor](https://github.com/DougGregor) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (July 8...15, 2025)** +* Status: **Implemented (Swift 6.2)** * Vision: [Improving the approachability of data-race safety](https://github.com/swiftlang/swift-evolution/blob/main/visions/approachable-concurrency.md) * Implementation: On `main` with the experimental features `IsolatedConformances` and `StrictSendableMetatypes`. * Upcoming Feature Flag: `InferIsolatedConformances` -* Review: ([pitch](https://forums.swift.org/t/pre-pitch-isolated-conformances/77726)) ([review](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704)) ([acceptance](https://forums.swift.org/t/accepted-se-0470-global-actor-isolated-conformances/79189)) ([amendment pitch](https://forums.swift.org/t/pitch-amend-se-0466-se-0470-to-improve-isolation-inference/79854)) ([amendment review](https://forums.swift.org/t/amendment-se-0470-global-actor-isolated-conformances/80999)) +* Review: ([pitch](https://forums.swift.org/t/pre-pitch-isolated-conformances/77726)) ([review](https://forums.swift.org/t/se-0470-global-actor-isolated-conformances/78704)) ([acceptance](https://forums.swift.org/t/accepted-se-0470-global-actor-isolated-conformances/79189)) ([amendment pitch](https://forums.swift.org/t/pitch-amend-se-0466-se-0470-to-improve-isolation-inference/79854)) ([amendment review](https://forums.swift.org/t/amendment-se-0470-global-actor-isolated-conformances/80999)) ([amendment acceptance](https://forums.swift.org/t/amendment-accepted-se-0470-global-actor-isolated-conformances/81144)) ## Introduction From b6a9bb7257d95d56ea5ea58114347a63dae9c9cb Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Thu, 17 Jul 2025 10:50:47 -0500 Subject: [PATCH 4432/4563] Update state of ST-0011 to Implemented and link to acceptance (#2918) --- proposals/testing/0011-issue-handling-traits.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/testing/0011-issue-handling-traits.md b/proposals/testing/0011-issue-handling-traits.md index c6592bba6f..4d16da9c70 100644 --- a/proposals/testing/0011-issue-handling-traits.md +++ b/proposals/testing/0011-issue-handling-traits.md @@ -3,12 +3,12 @@ * Proposal: [ST-0011](0011-issue-handling-traits.md) * Authors: [Stuart Montgomery](https://github.com/stmontgomery) * Review Manager: [Paul LeMarquand](https://github.com/plemarquand) -* Status: **Active Review (Jun 24 - July 8, 2025)** +* Status: **Implemented (Swift 6.2)** * Implementation: [swiftlang/swift-testing#1080](https://github.com/swiftlang/swift-testing/pull/1080), [swiftlang/swift-testing#1121](https://github.com/swiftlang/swift-testing/pull/1121), [swiftlang/swift-testing#1136](https://github.com/swiftlang/swift-testing/pull/1136), [swiftlang/swift-testing#1198](https://github.com/swiftlang/swift-testing/pull/1198) -* Review: ([pitch](https://forums.swift.org/t/pitch-issue-handling-traits/80019)) ([review](https://forums.swift.org/t/st-0011-issue-handling-traits/80644)) +* Review: ([pitch](https://forums.swift.org/t/pitch-issue-handling-traits/80019)) ([review](https://forums.swift.org/t/st-0011-issue-handling-traits/80644)) ([acceptance](https://forums.swift.org/t/accepted-st-0011-issue-handling-traits/81112)) ## Introduction From 8653025fe43750a1aabd50bcd5d1a7748ea476bd Mon Sep 17 00:00:00 2001 From: taylorswift Date: Sat, 19 Jul 2025 16:38:31 -0500 Subject: [PATCH 4433/4563] =?UTF-8?q?Dianna=20=E2=86=92=20Diana=20(#2920)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- proposals/0184-unsafe-pointers-add-missing.md | 2 +- proposals/0243-codepoint-and-character-literals.md | 2 +- proposals/0266-synthesized-comparable-for-enumerations.md | 2 +- proposals/0370-pointer-family-initialization-improvements.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0184-unsafe-pointers-add-missing.md b/proposals/0184-unsafe-pointers-add-missing.md index 772376bbdb..a8cff99c6b 100644 --- a/proposals/0184-unsafe-pointers-add-missing.md +++ b/proposals/0184-unsafe-pointers-add-missing.md @@ -1,7 +1,7 @@ # Unsafe[Mutable][Raw][Buffer]Pointer: add missing methods, adjust existing labels for clarity, and remove deallocation size * Proposal: [SE-0184](0184-unsafe-pointers-add-missing.md) -* Author: [Dianna Ma (“Taylor Swift”)](https://github.com/tayloraswift) +* Author: [Diana Ma (“Taylor Swift”)](https://github.com/tayloraswift) * Review Manager: [Doug Gregor](https://github.com/DougGregor) * Status: **Implemented (Swift 4.1)** * Implementation: [apple/swift#12200](https://github.com/apple/swift/pull/12200) diff --git a/proposals/0243-codepoint-and-character-literals.md b/proposals/0243-codepoint-and-character-literals.md index 6890ca77cf..7df6c0ee75 100644 --- a/proposals/0243-codepoint-and-character-literals.md +++ b/proposals/0243-codepoint-and-character-literals.md @@ -1,7 +1,7 @@ # Integer-convertible character literals * Proposal: [SE-0243](0243-codepoint-and-character-literals.md) -* Authors: [Dianna Ma (“Taylor Swift”)](https://github.com/tayloraswift), [Chris Lattner](https://github.com/lattner), [John Holdsworth](https://github.com/johnno1962) +* Authors: [Diana Ma (“Taylor Swift”)](https://github.com/tayloraswift), [Chris Lattner](https://github.com/lattner), [John Holdsworth](https://github.com/johnno1962) * Review manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Rejected** ([Rationale](https://forums.swift.org/t/se-0243-codepoint-and-character-literals/21188/341)) * Implementation: [apple/swift#21873](https://github.com/apple/swift/pull/21873) diff --git a/proposals/0266-synthesized-comparable-for-enumerations.md b/proposals/0266-synthesized-comparable-for-enumerations.md index 2ed30a3bd4..1c43e83890 100644 --- a/proposals/0266-synthesized-comparable-for-enumerations.md +++ b/proposals/0266-synthesized-comparable-for-enumerations.md @@ -1,7 +1,7 @@ # Synthesized `Comparable` conformance for `enum` types * Proposal: [SE-0266](0266-synthesized-comparable-for-enumerations.md) -* Author: [Dianna Ma (taylorswift)](https://github.com/tayloraswift) +* Author: [Diana Ma (taylorswift)](https://github.com/tayloraswift) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) * Status: **Implemented (Swift 5.3)** * Implementation: [apple/swift#25696](https://github.com/apple/swift/pull/25696) diff --git a/proposals/0370-pointer-family-initialization-improvements.md b/proposals/0370-pointer-family-initialization-improvements.md index 4c107ed4ed..9786754117 100644 --- a/proposals/0370-pointer-family-initialization-improvements.md +++ b/proposals/0370-pointer-family-initialization-improvements.md @@ -1758,6 +1758,6 @@ One of the pre-existing returned tuples does not have element labels, and the or ## Acknowledgments -[Dianna Ma](https://github.com/tayloraswift) (aka [Taylor Swift](https://forums.swift.org/u/taylorswift/summary))'s initial versions of the pitch that became SE-0184 included more functions to manipulate initialization state. These were deferred, but much of the deferred functionality has not been pitched again until now. +[Diana Ma](https://github.com/tayloraswift) (aka [Taylor Swift](https://forums.swift.org/u/taylorswift/summary))'s initial versions of the pitch that became SE-0184 included more functions to manipulate initialization state. These were deferred, but much of the deferred functionality has not been pitched again until now. Members of the Swift Standard Library team for valuable discussions. From 7aaac24233e784295a25b1df0c8a0ed71472e34f Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Mon, 21 Jul 2025 13:28:13 -0400 Subject: [PATCH 4434/4563] Update 0488-extracting.md --- proposals/0488-extracting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0488-extracting.md b/proposals/0488-extracting.md index 4bf57d7ec9..351bae8bdd 100644 --- a/proposals/0488-extracting.md +++ b/proposals/0488-extracting.md @@ -3,9 +3,9 @@ * Proposal: [SE-0488](0488-extracting.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Active Review (July 2–July 16, 2025)** +* Status: **Accepted** * Implementation: underscored `_extracting()` members of `Span` and `RawSpan`, pending elsewhere. -* Review: ([pitch](https://forums.swift.org/t/pitch-apply-the-extracting-slicing-pattern-to-span-and-rawspan/80322)) ([review](https://forums.swift.org/t/se-0488-apply-the-extracting-slicing-pattern-more-widely/80854)) +* Review: ([pitch](https://forums.swift.org/t/pitch-apply-the-extracting-slicing-pattern-to-span-and-rawspan/80322)) ([review](https://forums.swift.org/t/se-0488-apply-the-extracting-slicing-pattern-more-widely/80854)) ([acceptance](https://forums.swift.org/t/accepted-se-0488-apply-the-extracting-slicing-pattern-more-widely/81235)) [SE-0437]: 0437-noncopyable-stdlib-primitives.md [SE-0447]: 0447-span-access-shared-contiguous-storage.md From f5c40777918924786dd687a715e9afb13d418361 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 25 Jul 2025 10:10:33 +0100 Subject: [PATCH 4435/4563] [Vision] Add Platform Support vision document. Publish the Platform Support vision document. --- visions/platform-support.md | 285 ++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 visions/platform-support.md diff --git a/visions/platform-support.md b/visions/platform-support.md new file mode 100644 index 0000000000..c413e252f1 --- /dev/null +++ b/visions/platform-support.md @@ -0,0 +1,285 @@ +# Swift Platform Support: A Vision for Evolution + +The Swift programming language has evolved into a versatile and powerful tool +for developers across a wide range of platforms. As the ecosystem continues to +grow, it is essential to establish a clear and forward-looking vision for +platform support. This vision has two main goals: + +1. To establish common terminology and definitions for platform support. + +2. To document a process for platforms to become officially supported in Swift. + +## Understanding Platforms in the Swift Ecosystem + +The term "platform" carries multiple interpretations. For our purposes, a +platform represents the confluence of operating system, architecture, and +environment where Swift code executes. Each platform is identified using a +version-stripped LLVM `Triple`—a precise technical identifier that captures the +essential characteristics of a host environment (e.g., +`x86_64-unknown-windows-msvc`). + +## The Anatomy of a `Triple` + +At its core, a `Triple` comprises 11 distinct elements arranged in a specific +pattern: + +``` +[architecture][sub-architecture][extensions][endian]-[vendor]-[kernel/OS][version]-[libc/environment][abi][version]-[object format] +``` + +This naming convention might initially appear complex, but it offers remarkable +precision. When a public entity isn't associated with a toolchain, the +placeholder `unknown` is used for the vendor field. Similarly, bare-metal +environments—those without an operating system—employ `none` as their OS/kernel +designation. + +While many of these fields may be elided, for use in Swift, the vendor and OS +fields are always included, even if they are placeholder values. + +Consider these illustrative examples: + +- `armv7eb-unknown-linux-uclibceabihf-coff`: A Linux system running on ARMv7 in big-endian mode, with the µClibc library and PE/COFF object format. +- `aarch64-unknown-windows-msvc-macho`: Windows NT on the ARM64 architecture using the MSVC runtime with Mach-O object format. +- `riscv64gcv-apple-ios14-macabi`: An iOS 14 environment running on a RISC-V processor with specific ISA extensions. + +This nomenclature creates a shared language for discussing platform capabilities +and constraints—an essential foundation for our support framework. + +## Distributions within Platforms + +A platform and distribution, while related, serve distinct roles in the Swift +ecosystem. A platform refers to the broader combination of Operating System, +architecture, and environment where Swift code executes and establishes the +foundational compatibility and functionality of Swift. + +A distribution, on the other hand, represents a specific implementation or +variant within a platform. For example, while Linux as a platform is supported, +individual distributions such as Ubuntu, Fedora, or Amazon Linux require +additional work to ensure that Swift integrates seamlessly. This includes +addressing distribution-specific configurations, dependencies, and conventions. + +Distributions are treated similarly to platforms in that they require a +designated owner. This owner is responsible for ensuring that Swift functions +properly on the distribution, adheres to the distribution's standards, and +remains a responsible citizen within that ecosystem. By assigning ownership, the +Swift community ensures that each distribution receives the attention and +stewardship necessary to maintain a high-quality experience for developers. + +## Platform Stewardship + +The health of each platform depends on active stewardship. Every platform in the +Swift ecosystem requires a designated owner who reviews platform-specific +changes and manages release activities. Platforms without active owners enter a +dormant state, reverting to exploratory status until new leadership emerges. + +This ownership model ensures that platform support remains intentional rather +than accidental—each supported environment has an advocate invested in its +success. + +The Platform Steering Group will regularly review the list of supported +platforms against the tier criteria below. While the Platform Steering Group +reserves the right to update the list or the tier criteria at any time, it is +expected that most such changes will be aligned with the Swift release cycle. + +## A Tiered Approach to Platform Support + +Swift's platform support strategy employs three distinct tiers, each +representing a different level of maturity. The requirements for each tier build +upon those of the previous tier. + +### Tier 1: "Supported" Platforms + +These are Swift's most mature environments, where the language must consistently +build successfully and pass comprehensive test suites. Swift on these platforms +offers the strongest guarantees of stability and performance. + +Platforms that are in Tier 1 should: + +- [ ] Digitally sign their release artifacts. +- [ ] Include a Software Bill of Materials (SBOM). + +- [ ] Include at a minimum the following Swift libraries: + + - [ ] Swift Standard Library + - [ ] Swift Supplemental Libraries + - [ ] Swift Core Libraries + - [ ] Swift Testing Frameworks (if applicable) + + (See [the Swift Runtime Libraries + document](https://github.com/swiftlang/swift/blob/main/Runtimes/Readme.md) + in [the Swift repository](https://github.com/swiftlang/swift).) + +- [ ] Maintain a three-version window of support, including: + + - [ ] At least one stable release. + - [ ] The next planned release. + - [ ] The development branch (`main`). + +- [ ] Have a clear, documented, story for debugging, to allow users to set up + an environment where their products can be executed on a device or + simulator and be debugged. + +- [ ] Have testing in CI, including PR testing. + +- [ ] Ship SDKs as regular release from [swift.org](https://swift.org) + +- [ ] Ensure that instructions needed to get started on the platform + are publicly available, ideally on or linked to from + [swift.org](https://swift.org). + +An important aspect of Tier 1 platforms is that maintenance of support +of these platforms is the collective responsibility of the Swift +project as a whole, rather than falling entirely on the platform +owner. This means: + +- Contributions should not be accepted if they break a Tier 1 platform. + +- If a Tier 1 platform does break, whoever is responsible for the code + that is breaking must work with the platform owner on some kind of + resolution, which may mean backing out the relevant changes. + +- New features should aim to function on all Tier 1 + platforms, subject to the availability of appropriate supporting + functionality on each platform. + +- There is a presumption that a release of Swift will be blocked if a + Tier 1 platform is currently broken. This is not a hard and fast + rule, and can be overridden if it is in the interests of the Swift + project as a whole. + +### Tier 1: "Supported" Toolchain Hosts + +Each toolchain host is an expensive addition to the testing matrix. +In addition to the requirements above, a toolchain host platform should: + +- [ ] Have CI coverage for the toolchain, including PR testing. + +- [ ] Offer toolchain distributions from + [swift.org](https://swift.org) as an official source, though + other distributions may also be available. + +- [ ] Include the following toolchain components: + + - [ ] Swift compiler (`swiftc`). + - [ ] C/C++ compiler (`clang`, `clang++`). + - [ ] Assembler (LLVM integrated assembler, built into `clang`). + - [ ] Linker (_typically_ `lld`). + - [ ] Debugger (`lldb`). + - [ ] Swift Package Manager (SwiftPM). + - [ ] Language Server (`sourcekit-lsp`). + - [ ] Debug Adapter (`lldb-dap`). + +- [ ] Code-sign individual tools as appropriate for the platform. + +Note that the bar for accepting a platform as a toolchain host is somewhat +higher than the bar for accepting a non-toolchain-host platform. + +### Tier 2: "Experimental" Platforms + +Experimental platforms occupy the middle ground—they must maintain the ability +to build but may experience occasional test failures. These platforms +represent Swift's expanding frontier. + +Platforms in this tier should: + +- [ ] Ensure that dependencies beyond the platform SDK can build from source. + +- [ ] Provide provenance information to validate the software supply chain. + +- [ ] Include at a minimum the following Swift libraries: + + - [ ] Swift Standard Library + - [ ] Swift Supplemental Libraries + - [ ] Swift Core Libraries + - [ ] Swift Testing Frameworks (if applicable) + + (See [the Swift Runtime Libraries + document](https://github.com/swiftlang/swift/blob/main/Runtimes/Readme.md) + in [the Swift repository](https://github.com/swiftlang/swift).) + +- [ ] Maintain at least a two-version window of support, including + + - [ ] The next planned release. + - [ ] The development branch (`main`). + +Unlike Tier 1, the Swift project does not assume collective +responsibility for experimental platforms. Platform owners should +work with individual contributors to keep their platform in a +buildable state. + +### Tier 3: "Exploratory" Platforms + +At the boundary of Swift's reach are exploratory platforms. +Exploratory status offers an entry point for platforms taking their +first steps into the Swift ecosystem. + +Platforms in this tier should: + +- [ ] Support reproducible builds without requiring external + patches, though there is no requirement that these build completely + or consistently. + +- [ ] Maintain support in the current development branch (`main`). + +The Swift Project does not assume collective responsibility for +exploratory platforms. Platform owners are responsible for keeping +their platform in a buildable state. + +## Platform Inclusion Process and Promotion + +Adding platform support begins with a formal request to the Platform Steering +Group, accompanied by a platform owner nomination. This structured yet +accessible approach balances Swift's need for stability with its aspiration for +growth. + +The request should include: + +- [ ] The preferred name of the platform. + +- [ ] The name and contact details of the platform owner. + +- [ ] The support tier into which the platform should be placed. + +- [ ] Instructions to build Swift for the platform, assuming someone + is starting from scratch, including any requirements for the + build system. + +- [ ] A list of tier requirements that are currently _not_ met by the + platform, including an explanation as to _why_ they are not met + and what the proposal is to meet them, if any. + +- [ ] Whether there has been any discussion about provisioning of CI + resources, and if so a copy of or link to that discussion. This + is particularly relevant for a Tier 1 platform request. + +Note that it is _not_ the case that a platform _must_ meet every +requirement of the requested tier in order to be placed into that +tier. The Platform Steering Group will consider each case on its +merits, and will make a decision based on the information at hand as +well as the overall benefit to the Swift Project. It should be +emphasized that the Platform Steering Group reserves the right to +consider factors other than those listed here when making decisions +about official platform support. + +The same process should be used to request a promotion to a higher +tier. + +## Grandfathering and Demotion + +The following platforms are grandfathered into Tier 1 regardless of +any text in this document: + +- All Apple platforms (macOS, iOS and so on). +- Linux +- Windows + +The Platform Steering Group reserves the right to demote any +platform to a lower tier, but regards demotion as a last resort +and will by preference work with platform owners to maintain +support appropriate for their platform's existing tier. + +Note that if your platform is one of the above special cases, and +there is some requirement in this document that is not being met, it +is expected that either there is a very good reason for the +requirement not being met, or that there is some plan to meet it in +future. From df4ef4cbbe372aa5d70232b286451cc3c1ad6b77 Mon Sep 17 00:00:00 2001 From: Cal Stephens Date: Fri, 25 Jul 2025 07:12:01 -0700 Subject: [PATCH 4436/4563] Fix dead link in SE-0451 (#2924) --- proposals/0451-escaped-identifiers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0451-escaped-identifiers.md b/proposals/0451-escaped-identifiers.md index 639a1e1a9b..3105c97626 100644 --- a/proposals/0451-escaped-identifiers.md +++ b/proposals/0451-escaped-identifiers.md @@ -127,7 +127,7 @@ We propose extending the backtick-based syntax used to escape keywords as identi We will distinguish these two uses of backticks with the following terminology: * An **escaped identifier** is a sequence of characters surrounded by backticks that starts with `identifier-head` and is followed by zero or more `identifier-character`, as defined in the Swift [grammar](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/summaryofthegrammar/). This is what Swift supports today to treat keywords as identifiers. -* A **raw identifier** is a sequence of characters surrounded by backticks that contains characters other than those allowed in an escaped identifier. The exact contents are described in [Permissible Characters](#permissible-characters) below. +* A **raw identifier** is a sequence of characters surrounded by backticks that contains characters other than those allowed in an escaped identifier. The exact contents are described in [Permitted Characters](#permitted-characters) below. In both cases, the backticks are **not** considered part of the identifier; they only delimit the identifier from surrounding tokens. From a1b9559b75c4c140c5d35e609e623c06e9ae277e Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Mon, 28 Jul 2025 17:26:07 -0400 Subject: [PATCH 4437/4563] Update 0483-inline-array-sugar.md (#2896) * Update 0483-inline-array-sugar.md Update wording describing dynamic array downsides. * Update 0483-inline-array-sugar.md --- proposals/0483-inline-array-sugar.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/proposals/0483-inline-array-sugar.md b/proposals/0483-inline-array-sugar.md index 4b817e413e..fe1be440da 100644 --- a/proposals/0483-inline-array-sugar.md +++ b/proposals/0483-inline-array-sugar.md @@ -144,11 +144,17 @@ class vec3 { } ``` -The way in which Swift privileges `[Double]` with sugar strongly implies you should use that in this translation. This would be the wrong choice. Every access to those coordinate accessors would need to check the bounds (because while the author might ensure that the value of `e` will only ever have length 3, the compiler cannot easily know this) and, in the case of mutation, a check for uniqueness of the pointer. It would also make `vec3` a nontrivial type, which has significant performance implications wherever it is used. `InlineArray<3, Double>` has none of these problems. +The way in which Swift privileges `[Double]` with sugar strongly implies you should use that in this translation. Doing so would have significant performance downsides: +- Creating new instances of `Vec3` requires a heap allocation, and destroying them require a free operation. +- `Vec3` could no longer be `BitwiseCopyable`, instead requiring a reference counting operation to make a copy. +- Access to a coordinate would require pointer chasing, and a contiguous array of `Vec3` objects would not be guaranteed to exist in contiguous memory. +- Every access to those coordinate accessors would need to check the bounds (because while the author might ensure that the value of `e` will only ever have length 3, the compiler cannot easily know this) and, in the case of mutation, a check for uniqueness of the pointer. `InlineArray<3, Double>` has none of these problems. -Other examples include using nested `Array` types i.e. using `[[Double]]` to represent a matrix – which would also have significant negative consequences depending on the use case, compared to using an `InlineArray` to model the same values. In other cases, the "copy on write" nature of `Array` can mislead users into thinking that copies are risk-free, when actually copying an array can lead to "defeating" copy on write in subtle ways that can cause difficult-to-hunt-down performance issues. In all these cases, you need to pick the right one of two options for the performance goals you are trying to achieve. +Other examples include the use of nested `Array` types i.e. using `[[Double]]` to represent a matrix of known size, which would also have noticeable negative performance impact depending on the use case, compared to using an `InlineArray>` (or perhaps `[InlineArray]`) to model the same values. Today, this is possible via custom subscripts that logically represent the inner array within a single `[Double]`, but the introduction of `InlineArray` introduces other potentially more ergonomic options. -It is likely that Swift's choice (deviating from many of its peers) to emphasize its dynamic array through sugar, has led to a negative impact on the culture of writing performant Swift code, with the nicely sugared dynamic arrays (and array value literals) being over-favored. This is not intended to make the case that Swift should _not_ have this sugar. Dynamic arrays are widely useful and Swift's readability goals are improved by Swift having a concise syntax for creating them. But writing performant Swift code inevitably involves having an understanding of the underlying performance characteristics of _all_ the types you are using, and syntax or type naming alone cannot solve this. +In other cases, the "copy on write" nature of `Array` can mislead users into thinking that copies are risk-free, when actually copying an array can lead to "defeating" copy on write in subtle ways that can cause difficult-to-hunt-down performance issues. In all these cases, you need to pick the right one of two options for the performance goals you are trying to achieve. + +Swift's choice (deviating from many of its peers) to only have a dynamic array type, and to emphasize the utility of this type through sugar, has led to a shaping of the culture of writing Swift code that favors the sugared dynamic array even when this leads to otherwise-avoidable negative performance impact. This is not intended to make the case that Swift should _not_ have this sugar. Dynamic arrays are widely useful and Swift's readability goals are improved by Swift having a concise syntax for creating them. But writing performant Swift code inevitably involves having an understanding of the underlying performance characteristics of _all_ the types you are using, and syntax or type naming alone cannot solve this. Of course, all this only matters when you are trying to write code that maximizes performance. But that is a really important use case for Swift. The goal for Swift is a language that is as safe and enjoyable to write as many high-level non-performant languages, but also can achieve peak performance when that is your goal. And the idea is that when you are targeting that level of performance, you don't have to go into "ugly, no longer nice swift" mode to do it, with nice sugared `[Double]` replaced with less pleasant full type name of `InlineArray` – something a user coming from Go or C++ or Rust might find a downgrade. Similarly, attempting to incorporate the word "inline" into the sugar e.g. `[5 inline Int]` creates a worst of both worlds solution that many would find offputting to use, without solving the fundamental issue. From 28e242fb99ae940c98beea6ef2ef64cd311c2330 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 29 Jul 2025 10:30:52 +0100 Subject: [PATCH 4438/4563] Update Platform Support document wording. Apparently "grandfathered" is associated with some unfortunate history. Replace it with other wording. --- visions/platform-support.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/visions/platform-support.md b/visions/platform-support.md index c413e252f1..0c38d50c52 100644 --- a/visions/platform-support.md +++ b/visions/platform-support.md @@ -264,10 +264,10 @@ about official platform support. The same process should be used to request a promotion to a higher tier. -## Grandfathering and Demotion +## Existing Platforms and Demotion -The following platforms are grandfathered into Tier 1 regardless of -any text in this document: +The following existing platforms are in Tier 1 regardless of any +text in this document: - All Apple platforms (macOS, iOS and so on). - Linux From 8c281796b4742fed8431dde4a94944be767840c1 Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Wed, 30 Jul 2025 07:57:16 +0200 Subject: [PATCH 4439/4563] ST-0013 has been accepted Update status and added link to acceptance forum post --- proposals/testing/0013-issue-severity-warning.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/testing/0013-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md index a48c3b1187..5c7f0751f7 100644 --- a/proposals/testing/0013-issue-severity-warning.md +++ b/proposals/testing/0013-issue-severity-warning.md @@ -3,9 +3,9 @@ - Proposal: [ST-0013](0013-issue-severity-warning.md) - Authors: [Suzy Ratcliff](https://github.com/suzannaratcliff) - Review Manager: [Maarten Engels](https://github.com/maartene) -- Status: **Active Review (July 9...July 23, 2025)** +- Status: **Accepted** - Implementation: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075) -- Review: ([pitch](https://forums.swift.org/t/pitch-test-issue-warnings/79285)) ([review](https://forums.swift.org/t/st-0013-test-issue-warnings/80991)) +- Review: ([pitch](https://forums.swift.org/t/pitch-test-issue-warnings/79285)) ([review](https://forums.swift.org/t/st-0013-test-issue-warnings/80991)) ([accepted](https://forums.swift.org/t/accepted-st-0013-test-issue-severity/81385)) ## Introduction From 785f4edebb1af6d50aa6c869d6b86da8a3431dd7 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 2 Jul 2025 12:16:54 -0400 Subject: [PATCH 4440/4563] [WIP] Proposal to add image attachments support to Swift Testing. --- proposals/testing/NNNN-cgimage-attachments.md | 416 ++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 proposals/testing/NNNN-cgimage-attachments.md diff --git a/proposals/testing/NNNN-cgimage-attachments.md b/proposals/testing/NNNN-cgimage-attachments.md new file mode 100644 index 0000000000..28d8764372 --- /dev/null +++ b/proposals/testing/NNNN-cgimage-attachments.md @@ -0,0 +1,416 @@ +# Image attachments in Swift Testing (Apple platforms) + +* Proposal: [ST-NNNN](NNNN-cgimage-attachments.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Review Manager: TBD +* Status: **Awaiting review** +* Bug: rdar://154869058 +* Implementation: [swiftlang/swift-testing#827](https://github.com/swiftlang/swift-testing/pull/827), _et al._ +* Review: ([pitch](https://forums.swift.org/t/pitch-image-attachments-in-swift-testing/80867)) + +## Introduction + +We introduced the ability to add attachments to tests in Swift 6.2. This +proposal augments that feature to support attaching images on Apple platforms. + +## Motivation + +It is frequently useful to be able to attach images to tests for engineers to +review, e.g. if a UI element is not being drawn correctly. If something doesn't +render correctly in a CI environment, for instance, it is very useful to test +authors to be able to download the failed rendering and examine it at-desk. + +Today, Swift Testing offers support for **attachments** which allow a test +author to save arbitrary files created during a test run. However, if those +files are images, the test author must write their own code to encode them as +(for example) JPEG or PNG files before they can be attached to a test. + +## Proposed solution + +We propose adding support for images as a category of Swift type that can be +encoded using standard graphics formats such as JPEG or PNG. Image serialization +is beyond the purview of the testing library, so Swift Testing will defer to the +operating system to provide the relevant functionality. As such, this proposal +covers support for **Apple platforms** only. Support for other platforms such as +Windows is discussed in the **Future directions** section of this proposal. + +## Detailed design + +A new protocol is introduced for Apple platforms: + +```swift +/// A protocol describing images that can be converted to instances of +/// ``Testing/Attachment``. +/// +/// Instances of types conforming to this protocol do not themselves conform to +/// ``Testing/Attachable``. Instead, the testing library provides additional +/// initializers on ``Testing/Attachment`` that take instances of such types and +/// handle converting them to image data when needed. +/// +/// The following system-provided image types conform to this protocol and can +/// be attached to a test: +/// +/// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage) +/// - [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage) +/// - [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) +/// (macOS) +/// - [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) +/// (iOS, watchOS, tvOS, visionOS, and Mac Catalyst) +/// +/// You do not generally need to add your own conformances to this protocol. If +/// you have an image in another format that needs to be attached to a test, +/// first convert it to an instance of one of the types above. +@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +public protocol AttachableAsCGImage { + /// An instance of `CGImage` representing this image. + /// + /// - Throws: Any error that prevents the creation of an image. + var attachableCGImage: CGImage { get throws } +} +``` + +And conformances are provided for the following types: + +- [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage) +- [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage) +- [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) + (macOS) +- [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) + (iOS, watchOS, tvOS, visionOS, and Mac Catalyst) + +The implementation of `CGImage.attachableCGImage` simply returns `self`, while +the other implementations extract an underlying `CGImage` instance if available +or render one on-demand. + +> [!NOTE] +> The list of conforming types may be extended in the future. The Testing +> Workgroup will determine if additional Swift Evolution reviews are needed. + +### Attaching a conforming image + +New overloads of `Attachment.init()` and `Attachment.record()` are provided: + +```swift +@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension Attachment { + /// Initialize an instance of this type that encloses the given image. + /// + /// - Parameters: + /// - attachableValue: The value that will be attached to the output of + /// the test run. + /// - preferredName: The preferred name of the attachment when writing it + /// to a test report or to disk. If `nil`, the testing library attempts + /// to derive a reasonable filename for the attached value. + /// - imageFormat: The image format with which to encode `attachableValue`. + /// - sourceLocation: The source location of the call to this initializer. + /// This value is used when recording issues associated with the + /// attachment. + /// + /// The following system-provided image types conform to the + /// ``AttachableAsCGImage`` protocol and can be attached to a test: + /// + /// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage) + /// - [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage) + /// - [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) + /// (macOS) + /// - [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) + /// (iOS, watchOS, tvOS, visionOS, and Mac Catalyst) + /// + /// The testing library uses the image format specified by `imageFormat`. Pass + /// `nil` to let the testing library decide which image format to use. If you + /// pass `nil`, then the image format that the testing library uses depends on + /// the path extension you specify in `preferredName`, if any. If you do not + /// specify a path extension, or if the path extension you specify doesn't + /// correspond to an image format the operating system knows how to write, the + /// testing library selects an appropriate image format for you. + public init( + _ attachableValue: T, + named preferredName: String? = nil, + as imageFormat: AttachableImageFormat? = nil, + sourceLocation: SourceLocation = #_sourceLocation + ) where AttachableValue == _AttachableImageWrapper + + /// Attach an image to the current test. + /// + /// - Parameters: + /// - image: The value to attach. + /// - preferredName: The preferred name of the attachment when writing it to + /// a test report or to disk. If `nil`, the testing library attempts to + /// derive a reasonable filename for the attached value. + /// - imageFormat: The image format with which to encode `attachableValue`. + /// - sourceLocation: The source location of the call to this function. + /// + /// This function creates a new instance of ``Attachment`` wrapping `image` + /// and immediately attaches it to the current test. + /// + /// The following system-provided image types conform to the + /// ``AttachableAsCGImage`` protocol and can be attached to a test: + /// + /// - [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage) + /// - [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage) + /// - [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) + /// (macOS) + /// - [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) + /// (iOS, watchOS, tvOS, visionOS, and Mac Catalyst) + /// + /// The testing library uses the image format specified by `imageFormat`. Pass + /// `nil` to let the testing library decide which image format to use. If you + /// pass `nil`, then the image format that the testing library uses depends on + /// the path extension you specify in `preferredName`, if any. If you do not + /// specify a path extension, or if the path extension you specify doesn't + /// correspond to an image format the operating system knows how to write, the + /// testing library selects an appropriate image format for you. + public static func record( + _ image: T, + named preferredName: String? = nil, + as imageFormat: AttachableImageFormat? = nil, + sourceLocation: SourceLocation = #_sourceLocation + ) where AttachableValue == _AttachableImageWrapper +} +``` + +> [!NOTE] +> `_AttachableImageWrapper` is an implementation detail required by Swift's +> generic type system and is not itself part of this proposal. For completeness, +> its public interface is: +> +> ```swift +> @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +> public struct _AttachableImageWrapper: Sendable, AttachableWrapper where Image: AttachableAsCGImage { +> public var wrappedValue: Image { get } +> } +> ``` + +### Specifying image formats + +A test author can specify the image format to use with `AttachableImageFormat`. +This type abstractly represents the destination image format and, where +applicable, encoding quality: + +```swift +/// A type describing image formats supported by the system that can be used +/// when attaching an image to a test. +/// +/// When you attach an image to a test, you can pass an instance of this type to +/// ``Attachment/record(_:named:as:sourceLocation:)`` so that the testing +/// library knows the image format you'd like to use. If you don't pass an +/// instance of this type, the testing library infers which format to use based +/// on the attachment's preferred name. +/// +/// The PNG and JPEG image formats are always supported. The set of additional +/// supported image formats is platform-specific: +/// +/// - On Apple platforms, you can use [`CGImageDestinationCopyTypeIdentifiers()`](https://developer.apple.com/documentation/imageio/cgimagedestinationcopytypeidentifiers()) +/// from the [Image I/O framework](https://developer.apple.com/documentation/imageio) +/// to determine which formats are supported. +@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +public struct AttachableImageFormat: Sendable { + /// The encoding quality to use for this image format. + /// + /// The meaning of the value is format-specific with `0.0` being the lowest + /// supported encoding quality and `1.0` being the highest supported encoding + /// quality. The value of this property is ignored for image formats that do + /// not support variable encoding quality. + public var encodingQuality: Float { get } +} +``` + +Conveniences for the PNG and JPEG formats are provided as they are very widely +used and supported across almost all modern platforms, Web browsers, etc.: + +```swift +@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension AttachableImageFormat { + /// The PNG image format. + public static var png: Self { get } + + /// The JPEG image format with maximum encoding quality. + public static var jpeg: Self { get } + + /// The JPEG image format. + /// + /// - Parameters: + /// - encodingQuality: The encoding quality to use when serializing an + /// image. A value of `0.0` indicates the lowest supported encoding + /// quality and a value of `1.0` indicates the highest supported encoding + /// quality. + /// + /// - Returns: An instance of this type representing the JPEG image format + /// with the specified encoding quality. + public static func jpeg(withEncodingQuality encodingQuality: Float) -> Self +} +``` + +For instance, to save an image in the JPEG format with 50% image quality, you +can use `.jpeg(withEncodingQuality: 0.5)`. + +On Apple platforms, a convenience initializer that takes an instance of `UTType` +is also provided and lets you select any format supported by the underlying +Image I/O framework: + +```swift +@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension AttachableImageFormat { + /// The content type corresponding to this image format. + /// + /// The value of this property always conforms to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image). + public var contentType: UTType { get } + + /// Initialize an instance of this type with the given content type and + /// encoding quality. + /// + /// - Parameters: + /// - contentType: The image format to use when encoding images. + /// - encodingQuality: The encoding quality to use when encoding images. For + /// the lowest supported quality, pass `0.0`. For the highest supported + /// quality, pass `1.0`. + /// + /// If the target image format does not support variable-quality encoding, + /// the value of the `encodingQuality` argument is ignored. + /// + /// If `contentType` does not conform to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image), + /// the result is undefined. + public init(_ contentType: UTType, encodingQuality: Float = 1.0) +} +``` + +### Example usage + +A developer may then easily attach an image to a test by calling +`Attachment.record()` and passing the image of interest. For example, to attach +a rendering of a SwiftUI view as a PNG file: + +```swift +import Testing +import UIKit +import SwiftUI + +@MainActor @Test func `attaching a SwiftUI view as an image`() throws { + let myView: some View = ... + let image = try #require(ImageRenderer(content: myView).uiImage) + Attachment.record(image, named: "my view", as: .png) + // OR: Attachment.record(image, named: "my view.png") +} +``` + +## Source compatibility + +This change is additive only. + +## Integration with supporting tools + +None needed. + +## Future directions + +- Adding support for [`SwiftUI.Image`](https://developer.apple.com/documentation/swiftui/image) + and/or [`SwiftUI.GraphicsContext.ResolvedImage`](https://developer.apple.com/documentation/swiftui/graphicscontext/resolvedimage). + These types do not directly wrap an instance of `CGImage`. + + Since `SwiftUI.Image` conforms to [`SwiftUI.View`](https://developer.apple.com/documentation/swiftui/view), + it is possible to convert an instance of that type to an instance of `CGImage` + using [`SwiftUI.ImageRenderer`](https://developer.apple.com/documentation/swiftui/imagerenderer). + This approach is generalizable to all `SwiftUI.View`-cnforming types, and the + correct approach here may be to provide an `_AttachableViewWrapper` + type similar to the described `_AttachableImageWrapper` type. + +- Adding support for Windows image types. Windows has several generations of + imaging libraries: + + - Graphics Device Interface (GDI), which shipped with the original Windows in + 1985; + - GDI+, which was introduced with Windows XP in 2001; + - Windows Imaging Component (WIC) with Windows Vista in 2006; and + - Direct2D with Windows 7 in 2008. + + Of these libraries, only the original GDI provides a C interface that can be + directly referenced from Swift. The GDI+ interface is written in C++ and the + WIC and Direct2D interfaces are built on top of COM (a C++ abstraction layer.) + This reliance on C++ poses challenges for Swift Testing. Swift/C++ interop is + still a young technology and is not yet able to provide abstractions for + virtual C++ classes. + + None of these Windows' libraries are source compatible with Apple's Core + Graphics API, so support for any of them will require a different protocol. As + of this writing, [an experimental](https://github.com/swiftlang/swift-testing/pull/1245) + GDI- and (partially) GDI+-compatible protocol is available in Swift Testing + that allows a test author to attach an image represented by an `HBITMAP` or + `HICON` instance. Further work will be needed to make this experimental + Windows support usable with the newer libraries' image types. + +- Adding support for X11-compatible image types such as Qt's [`QImage`](https://doc.qt.io/qt-6/qimage.html) + or GTK's [`GdkPixbuf`](https://docs.gtk.org/gdk-pixbuf/class.Pixbuf.html). + We're also interested in implementing something here, but GUI-level libraries + aren't guaranteed to be present on Linux systems, so we cannot rely on their + headers or modules being accessible while building the Swift toolchain. It may + be appropriate to roll such functionality into a hypothetical `swift-x11`, + `swift-wayland`, `swift-qt`, `swift-gtk`, etc. package if one is ever created. + +- Adding support for Android's [`android.graphics.Bitmap`](https://developer.android.com/reference/android/graphics/Bitmap) + type. The Android NDK includes the [`AndroidBitmap_compress()`](https://developer.android.com/ndk/reference/group/bitmap#androidbitmap_compress) + function, but proper support for attaching an Android `Bitmap` may require a + dependency on [`swift-java`](https://github.com/swiftlang/swift-java) in some + form. Going forward, we hope to work with the new [Android Workgroup](https://www.swift.org/android-workgroup/) + to enhance Swift Testing's Android support. + +- Adding support for rendering to a PDF instead of an image. While technically + feasible using [existing](https://developer.apple.com/documentation/coregraphics/cgcontext/init(consumer:mediabox:_:)) + Core Graphics API, we haven't identified sufficient demand for this + functionality. + +## Alternatives considered + +- Doing nothing. Developers would need to write their own image conversion code. + Since this is a very common operation, it makes sense to incorporate it into + Swift Testing directly. + +- Making `CGImage` etc. conform directly to `Attachable`. Doing so would + prevent us from including sidecar data such as the desired `UTType` or + encoding quality as these types do not provide storage for that information. + As well, `NSImage` does not conform to `Sendable` and would be forced down a + code path that eagerly serializes it, which could pessimize its performance + once we introduce attachment lifetimes in a future proposal. + +- Designing a platform-agnostic solution. This would likely require adding a + dependency on an open-source image package such as [ImageMagick](https://github.com/ImageMagick/ImageMagick). + While we appreciate the value of such libraries and we want Swift Testing to + be as portable as possible, that would be a significant new dependency for the + testing library and the Swift toolchain at large. As well, we expect a typical + use case to involve an instance of `NSImage`, `CGImage`, etc. + +- Designing a solution that does not require `UTType` so as to support earlier + Apple platforms. The implementation is based on Apple's Image I/O framework + which requires a Uniform Type Identifier as input anyway, and the older + `CFString`-based interfaces we would need to use have been deprecated for + several years now. The `AttachableImageFormat` type allows us to abstract away + our platform-specific dependency on `UTType` so that, in the future, other + platforms can reuse `AttachableImageFormat` instead of implementing their own + equivalent solution. (As an example, the experimental Windows support + mentioned previously allows a developer to specify an image codec's `CLSID`.) + +- Designing a solution based around _drawing_ into a `CGContext` rather than + acquiring an instance of `CGImage`. If the proposed protocol looked like: + + ```swift + protocol AttachableByDrawing { + func draw(in context: CGContext, for attachment: Attachment) throws + } + ``` + + It would be easier to support alternative destination contexts (primarily PDF + contexts), but we would need to make a complete copy of an image in memory + before serializing it. If you start with an instance of `CGImage` or an object + that wraps an instance of `CGImage`, you can pass it directly to Image I/O. + +- Including convenience getters for additional image formats in + `AttachableImageFormat`. The set of formats we provide up-front support for is + intentionally small and limited to formats that are universally supported by + the various graphics libraries in use today. If we provided a larger set of + formats that are supported on Apple's platforms, developers may run into + difficulties porting their test code to platforms that _don't_ support those + additional formats. + +## Acknowledgments + +Thanks to Apple's testing teams and to the Testing Workgroup for their support +and advice on this project. From 0c5be06f73384218c7859cc7f0bccfb3bfdf3aeb Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Tue, 5 Aug 2025 14:04:43 -0400 Subject: [PATCH 4441/4563] Update status of ST-0012 Exit Test Value Capturing to Accepted --- proposals/testing/0012-exit-test-value-capturing.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/testing/0012-exit-test-value-capturing.md b/proposals/testing/0012-exit-test-value-capturing.md index 43c226b4ae..c31fd772ae 100644 --- a/proposals/testing/0012-exit-test-value-capturing.md +++ b/proposals/testing/0012-exit-test-value-capturing.md @@ -3,10 +3,10 @@ * Proposal: [ST-0012](0012-exit-test-value-capturing.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Review Manager: [Paul LeMarquand](https://github.com/plemarquand) -* Status: **Active Review (Jul 7 - July 21, 2025)** +* Status: **Implemented (Swift 6.3)** * Bug: [swiftlang/swift-testing#1157](https://github.com/swiftlang/swift-testing/issues/1157) -* Implementation: [swiftlang/swift-testing#1040](https://github.com/swiftlang/swift-testing/pull/1040) _et al._ -* Review: ([pitch](https://forums.swift.org/t/pitch-capturing-values-in-exit-tests/80494)) ([review](https://forums.swift.org/t/st-0012-capturing-values-in-exit-tests/80963)) +* Implementation: [swiftlang/swift-testing#1040](https://github.com/swiftlang/swift-testing/pull/1040), [swiftlang/swift-testing#1165](https://github.com/swiftlang/swift-testing/pull/1165) _et al._ +* Review: ([pitch](https://forums.swift.org/t/pitch-capturing-values-in-exit-tests/80494)) ([review](https://forums.swift.org/t/st-0012-capturing-values-in-exit-tests/80963)) ([acceptance](https://forums.swift.org/t/accepted-st-0012-capturing-values-in-exit-tests/81250)) ## Introduction From dbc790b53d2359fdc67ecd43ebff72780f002489 Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Tue, 5 Aug 2025 21:06:19 +0200 Subject: [PATCH 4442/4563] Update and rename NNNN-cgimage-attachments.md to 0014-image-attachments-in-swift-testing-apple-platforms.md --- ...4-image-attachments-in-swift-testing-apple-platforms.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/testing/{NNNN-cgimage-attachments.md => 0014-image-attachments-in-swift-testing-apple-platforms.md} (98%) diff --git a/proposals/testing/NNNN-cgimage-attachments.md b/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md similarity index 98% rename from proposals/testing/NNNN-cgimage-attachments.md rename to proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md index 28d8764372..f9c9414a66 100644 --- a/proposals/testing/NNNN-cgimage-attachments.md +++ b/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md @@ -1,9 +1,9 @@ # Image attachments in Swift Testing (Apple platforms) -* Proposal: [ST-NNNN](NNNN-cgimage-attachments.md) +* Proposal: [ST-0014](0014-image-attachments-in-swift-testing-apple-platforms.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Maarten Engels](https://github.com/maartene/) +* Status: **Active Review (August 8th...August 20th, 2025)** * Bug: rdar://154869058 * Implementation: [swiftlang/swift-testing#827](https://github.com/swiftlang/swift-testing/pull/827), _et al._ * Review: ([pitch](https://forums.swift.org/t/pitch-image-attachments-in-swift-testing/80867)) From bcbd3f3f348d8484438ec974664889fdaf96e0b1 Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Tue, 5 Aug 2025 21:14:06 +0200 Subject: [PATCH 4443/4563] Update 0014-image-attachments-in-swift-testing-apple-platforms.md --- .../0014-image-attachments-in-swift-testing-apple-platforms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md b/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md index f9c9414a66..20f050cc0c 100644 --- a/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md +++ b/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md @@ -6,7 +6,7 @@ * Status: **Active Review (August 8th...August 20th, 2025)** * Bug: rdar://154869058 * Implementation: [swiftlang/swift-testing#827](https://github.com/swiftlang/swift-testing/pull/827), _et al._ -* Review: ([pitch](https://forums.swift.org/t/pitch-image-attachments-in-swift-testing/80867)) +* Review: ([pitch](https://forums.swift.org/t/pitch-image-attachments-in-swift-testing/80867)) ([review](https://forums.swift.org/t/st-0014-image-attachments-in-swift-testing-apple-platforms/81507)) ## Introduction From ed69d008575c21f478690f72f2b54c1aa747cc02 Mon Sep 17 00:00:00 2001 From: Ben Cohen Date: Tue, 5 Aug 2025 12:41:07 -0700 Subject: [PATCH 4444/4563] Proposal acceptance (#2931) --- proposals/0487-extensible-enums.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0487-extensible-enums.md b/proposals/0487-extensible-enums.md index f20b9363cc..0d3f19f5b0 100644 --- a/proposals/0487-extensible-enums.md +++ b/proposals/0487-extensible-enums.md @@ -1,9 +1,9 @@ -# Extensible enums +# Nonexhuastive enums * Proposal: [SE-0487](0487-extensible-enums.md) * Authors: [Pavel Yaskevich](https://github.com/xedin), [Franz Busch](https://github.com/FranzBusch), [Cory Benfield](https://github.com/lukasa) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Active Review (July 1 — July 10, 2025)** +* Status: **[Accepted](https://forums.swift.org/t/accepted-se-0487-nonexhaustive-enums/81508)** * Bug: [apple/swift#55110](https://github.com/swiftlang/swift/issues/55110) * Implementation: [apple/swift#80503](https://github.com/swiftlang/swift/pull/80503) * Upcoming Feature Flag: `ExtensibleAttribute` From 9f8e4b209fafcde7032fb11d3809b836ce7f0844 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 5 Aug 2025 17:19:07 -0400 Subject: [PATCH 4445/4563] Accept SE-0483. (#2932) --- proposals/0483-inline-array-sugar.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0483-inline-array-sugar.md b/proposals/0483-inline-array-sugar.md index fe1be440da..0b6623edeb 100644 --- a/proposals/0483-inline-array-sugar.md +++ b/proposals/0483-inline-array-sugar.md @@ -3,9 +3,9 @@ * Proposal: [SE-0483](0483-inline-array-sugar.md) * Authors: [Hamish Knight](https://github.com/hamishknight), [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active Review (June 6- June 20, 2025)** +* Status: **Accepted** * Implementation: On `main` under the `InlineArrayTypeSugar` experimental feature flag. -* Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) ([first review](https://forums.swift.org/t/se-0483-inlinearray-literal-syntax/79643)) ([second review](https://forums.swift.org/t/second-review-se-0483-inlinearray-type-sugar/80337)) +* Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) ([first review](https://forums.swift.org/t/se-0483-inlinearray-literal-syntax/79643)) ([second review](https://forums.swift.org/t/second-review-se-0483-inlinearray-type-sugar/80337)) ([acceptance](https://forums.swift.org/t/accepted-se-0483-inlinearray-type-sugar/81509)) ## Introduction From 617ce514899747a9eb462142bdbebfdfa9e604cb Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 6 Aug 2025 06:29:39 +0900 Subject: [PATCH 4446/4563] Mark SE-0471 as implemented in 6.2 (#2933) This is implemented in 6.2 --- proposals/0471-SerialExecutor-isIsolated.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0471-SerialExecutor-isIsolated.md b/proposals/0471-SerialExecutor-isIsolated.md index d5e38f80a8..b91e3693ad 100644 --- a/proposals/0471-SerialExecutor-isIsolated.md +++ b/proposals/0471-SerialExecutor-isIsolated.md @@ -3,7 +3,7 @@ * Proposal: [SE-0471](0471-SerialExecutor-isIsolated.md) * Author: [Konrad 'ktoso' Malawski](https://github.com/ktoso) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Implementation: https://github.com/swiftlang/swift/pull/79788 & https://github.com/swiftlang/swift/pull/79946 * Review: [Pitch](https://forums.swift.org/t/pitch-serialexecutor-improved-custom-serialexecutor-isolation-checking/78237/), [Review](https://forums.swift.org/t/se-0471-improved-custom-serialexecutor-isolation-checking-for-concurrency-runtime/78834), [Acceptance](https://forums.swift.org/t/accepted-se-0471-improved-custom-serialexecutor-isolation-checking-for-concurrency-runtime/79894) From 8e7e25e4a486265c1b0b39d834cceca964ff80db Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 5 Aug 2025 17:54:38 -0400 Subject: [PATCH 4447/4563] Flag that SE-0480 is implemented in Swift 6.2 The implementation was "merged" to the 6.2 branch in https://github.com/swiftlang/swift-package-manager/commit/82119770453c29228d318920cf7fb42463d2e648. --- proposals/0480-swiftpm-warning-control.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0480-swiftpm-warning-control.md b/proposals/0480-swiftpm-warning-control.md index 5cdb54c201..c0faff43e7 100644 --- a/proposals/0480-swiftpm-warning-control.md +++ b/proposals/0480-swiftpm-warning-control.md @@ -3,7 +3,7 @@ * Proposal: [SE-0480](0480-swiftpm-warning-control.md) * Authors: [Dmitrii Galimzianov](https://github.com/DmT021) * Review Manager: [John McCall](https://github.com/rjmccall), [Franz Busch](https://github.com/FranzBusch) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Implementation: [swiftlang/swift-package-manager#8315](https://github.com/swiftlang/swift-package-manager/pull/8315) * Review: ([pitch](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666)) ([review](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475)) ([returned for revision](https://forums.swift.org/t/se-0480-warning-control-settings-for-swiftpm/79475/8)) ([acceptance](https://forums.swift.org/t/accepted-se-0480-warning-control-settings-for-swiftpm/80327)) * Previous Proposal: [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md) From 86a3bf655008f48b6f998245f2371c273a02b591 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 5 Aug 2025 18:35:16 -0400 Subject: [PATCH 4448/4563] Mark SE-0483 as implemented. (#2935) --- proposals/0483-inline-array-sugar.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0483-inline-array-sugar.md b/proposals/0483-inline-array-sugar.md index 0b6623edeb..507605ec34 100644 --- a/proposals/0483-inline-array-sugar.md +++ b/proposals/0483-inline-array-sugar.md @@ -3,8 +3,7 @@ * Proposal: [SE-0483](0483-inline-array-sugar.md) * Authors: [Hamish Knight](https://github.com/hamishknight), [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Accepted** -* Implementation: On `main` under the `InlineArrayTypeSugar` experimental feature flag. +* Status: **Implemented (Swift 6.2)** * Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) ([first review](https://forums.swift.org/t/se-0483-inlinearray-literal-syntax/79643)) ([second review](https://forums.swift.org/t/second-review-se-0483-inlinearray-type-sugar/80337)) ([acceptance](https://forums.swift.org/t/accepted-se-0483-inlinearray-type-sugar/81509)) ## Introduction From 4e2a82161fc95b6714398df7a367d7ea2ce3a5ae Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 5 Aug 2025 21:46:07 -0600 Subject: [PATCH 4449/4563] Accept SE-0489 (#2927) Updating the text to remove implementation details for which the language steering group would encourage further change separately from the Evolution process. --- proposals/0489-codable-error-printing.md | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/proposals/0489-codable-error-printing.md b/proposals/0489-codable-error-printing.md index 2d3968aea6..6dd70e71a5 100644 --- a/proposals/0489-codable-error-printing.md +++ b/proposals/0489-codable-error-printing.md @@ -3,9 +3,9 @@ * Proposal: [SE-0489](0489-codable-error-printing.md) * Authors: [Zev Eisenberg](https://github.com/ZevEisenberg) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Active review (July 9...22, 2025)** +* Status: **Accepted** * Implementation: https://github.com/swiftlang/swift/pull/80941 -* Review: ([pitch](https://forums.swift.org/t/pitch-improve-encodingerror-and-decodingerror-s-printed-descriptions/79872)) ([review](https://forums.swift.org/t/se-0489-improve-encodingerror-and-decodingerrors-printed-descriptions/81021)) +* Review: ([pitch](https://forums.swift.org/t/pitch-improve-encodingerror-and-decodingerror-s-printed-descriptions/79872)) ([review](https://forums.swift.org/t/se-0489-improve-encodingerror-and-decodingerrors-printed-descriptions/81021)) ([acceptance](https://forums.swift.org/t/accepted-se-0489-improve-encodingerror-and-decodingerrors-printed-descriptions/81380)) ## Introduction @@ -71,19 +71,13 @@ However, it is not easy or pleasant to read such an error, particularly when dea ## Proposed solution -Conform `EncodingError` and `DecodingError` to `CustomDebugStringConvertible` and provide a clean, readable debug description for each. Here is an example of the proposed change for the same decoding error as above. - -``` -Key 'population' not found in keyed decoding container. -Debug description: No value associated with key CodingKeys(stringValue: "population", intValue: nil) ("population"). -Path: [0]/home/country -``` +Conform `EncodingError` and `DecodingError` to `CustomDebugStringConvertible` and provide a clean, readable debug description for each. Complete examples of the before/after diffs are available in the description of the [implementation pull request](https://github.com/swiftlang/swift/pull/80941) that accompanies this proposal. -**Note 1:** this proposal is _not_ intended to specify an exact output format. The above is provided as an example, and is not a guarantee of current or future behavior. You are still free to inspect the contents of thrown errors directly if you need to detect specific problems. +**Note 1:** This proposal is _not_ intended to specify an exact output format, and any examples are not a guarantee of current or future behavior. You are still free to inspect the contents of thrown errors directly if you need to detect specific problems. -**Note 2:** the output could be further improved by modifying `JSONDecoder` to write a better debug description. See [Future Directions](#future-directions) for more. +**Note 2:** The output could be further improved by modifying `JSONDecoder` to write a better debug description. See [Future Directions](#future-directions) for more. ## Detailed design @@ -127,7 +121,7 @@ It is technically possible to backdeploy the `debugDescription` property, but wi ### Better error generation from Foundation encoders/decoders -The debug descriptions generated in Foundation sometimes contain the same information as the new debug descriptions from this proposal. A future change to the standard JSON and Plist encoders and decoders could provide more compact debug descriptions once they can be sure they have the new standard library descriptions available. They could also use a more compact description when rendering the description of a `CodingKey`. Using part of the example from above: +The debug descriptions generated in Foundation sometimes contain the same information as the new debug descriptions from this proposal. A future change to the standard JSON and Plist encoders and decoders could provide more compact debug descriptions once they can be sure they have the new standard library descriptions available. They could also use a more compact description when rendering the description of a `CodingKey`. Take, for example: ``` Debug description: No value associated with key CodingKeys(stringValue: "population", intValue: nil) ("population"). From 25a4771abe0afae298bb1ab69cb5aa5d3e21df63 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu <13952+xwu@users.noreply.github.com> Date: Tue, 5 Aug 2025 21:46:40 -0600 Subject: [PATCH 4450/4563] SE-0487: fixup typo (#2936) --- proposals/0487-extensible-enums.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0487-extensible-enums.md b/proposals/0487-extensible-enums.md index 0d3f19f5b0..0b173fe24b 100644 --- a/proposals/0487-extensible-enums.md +++ b/proposals/0487-extensible-enums.md @@ -1,4 +1,4 @@ -# Nonexhuastive enums +# Nonexhaustive enums * Proposal: [SE-0487](0487-extensible-enums.md) * Authors: [Pavel Yaskevich](https://github.com/xedin), [Franz Busch](https://github.com/FranzBusch), [Cory Benfield](https://github.com/lukasa) From 5a99e00990bfbe056e1a95deb07cca3536188500 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 7 Aug 2025 12:18:48 -0700 Subject: [PATCH 4451/4563] Mark some stdlib proposals as implemented (#2937) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Mark some stdlib proposals as implemented * Mark SE-0477 as implemented * Don’t mark SE-0473 as implemented --- proposals/0459-enumerated-collection.md | 2 +- proposals/0477-default-interpolation-values.md | 2 +- proposals/0485-outputspan.md | 2 +- proposals/0488-extracting.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0459-enumerated-collection.md b/proposals/0459-enumerated-collection.md index e9044be172..14071906cd 100644 --- a/proposals/0459-enumerated-collection.md +++ b/proposals/0459-enumerated-collection.md @@ -3,7 +3,7 @@ * Proposal: [SE-0459](0459-enumerated-collection.md) * Author: [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Implementation: [swiftlang/swift#78092](https://github.com/swiftlang/swift/pull/78092) * Previous Proposal: [SE-0312](0312-indexed-and-enumerated-zip-collections.md) * Review: ([pitch](https://forums.swift.org/t/pitch-add-collection-conformance-for-enumeratedsequence/76680)) ([review](https://forums.swift.org/t/se-0459-add-collection-conformances-for-enumerated/77509)) ([acceptance](https://forums.swift.org/t/accepted-with-modification-se-0459-add-collection-conformances-for-enumerated/78082)) diff --git a/proposals/0477-default-interpolation-values.md b/proposals/0477-default-interpolation-values.md index c880eba3b3..fc024885c9 100644 --- a/proposals/0477-default-interpolation-values.md +++ b/proposals/0477-default-interpolation-values.md @@ -3,7 +3,7 @@ * Proposal: [SE-0477](0477-default-interpolation-values.md) * Authors: [Nate Cook](https://github.com/natecook1000) * Review Manager: [Xiaodi Wu](https://github.com/xwu) -* Status: **Accepted with modifications** +* Status: **Implemented (Swift 6.2)** * Implementation: [swiftlang/swift#80547](https://github.com/swiftlang/swift/pull/80547) * Review: ([pitch](https://forums.swift.org/t/pitch-default-values-for-string-interpolations/69381)) ([review](https://forums.swift.org/t/se-0477-default-value-in-string-interpolations/79302)) ([acceptance](https://forums.swift.org/t/accepted-with-modification-se-0477-default-value-in-string-interpolations/79609)) diff --git a/proposals/0485-outputspan.md b/proposals/0485-outputspan.md index 8772bdecfb..76f467f6fa 100644 --- a/proposals/0485-outputspan.md +++ b/proposals/0485-outputspan.md @@ -3,7 +3,7 @@ * Proposal: [SE-0485](0485-outputspan.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Doug Gregor](https://github.com/DougGregor) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** ([Extensions to standard library types](#extensions) pending) * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: [swiftlang/swift#81637](https://github.com/swiftlang/swift/pull/81637) * Review: [Pitch](https://forums.swift.org/t/pitch-outputspan/79473), [Review](https://forums.swift.org/t/se-0485-outputspan-delegate-initialization-of-contiguous-memory/80032), [Acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0485-outputspan-delegate-initialization-of-contiguous-memory/80435) diff --git a/proposals/0488-extracting.md b/proposals/0488-extracting.md index 351bae8bdd..529b1d7e53 100644 --- a/proposals/0488-extracting.md +++ b/proposals/0488-extracting.md @@ -3,7 +3,7 @@ * Proposal: [SE-0488](0488-extracting.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Accepted** +* Status: **Implemented (Swift 6.2)** * Implementation: underscored `_extracting()` members of `Span` and `RawSpan`, pending elsewhere. * Review: ([pitch](https://forums.swift.org/t/pitch-apply-the-extracting-slicing-pattern-to-span-and-rawspan/80322)) ([review](https://forums.swift.org/t/se-0488-apply-the-extracting-slicing-pattern-more-widely/80854)) ([acceptance](https://forums.swift.org/t/accepted-se-0488-apply-the-extracting-slicing-pattern-more-widely/81235)) From 82a284da5ae16e23b61c00ee85347c510260d6b5 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Tue, 12 Aug 2025 16:07:55 -0500 Subject: [PATCH 4452/4563] Fix metadata formatting for SE-0487 I think the linked "Accepted" is the reason this proposal isn't showing up in the list on swift.org. --- proposals/0487-extensible-enums.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0487-extensible-enums.md b/proposals/0487-extensible-enums.md index 0b173fe24b..a8e226c95b 100644 --- a/proposals/0487-extensible-enums.md +++ b/proposals/0487-extensible-enums.md @@ -3,11 +3,11 @@ * Proposal: [SE-0487](0487-extensible-enums.md) * Authors: [Pavel Yaskevich](https://github.com/xedin), [Franz Busch](https://github.com/FranzBusch), [Cory Benfield](https://github.com/lukasa) * Review Manager: [Ben Cohen](https://github.com/airspeedswift) -* Status: **[Accepted](https://forums.swift.org/t/accepted-se-0487-nonexhaustive-enums/81508)** +* Status: **Accepted** * Bug: [apple/swift#55110](https://github.com/swiftlang/swift/issues/55110) * Implementation: [apple/swift#80503](https://github.com/swiftlang/swift/pull/80503) * Upcoming Feature Flag: `ExtensibleAttribute` -* Review: ([pitch](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649)) +* Review: ([pitch](https://forums.swift.org/t/pitch-extensible-enums-for-non-resilient-modules/77649)) ([first review](https://forums.swift.org/t/se-0487-extensible-enums/80114)) ([second review](https://forums.swift.org/t/second-review-se-0487-extensible-enums/80837)) ([acceptance](https://forums.swift.org/t/accepted-se-0487-nonexhaustive-enums/81508)) Previously pitched in: From 82cb84c0e2439da2ee792c6632634c5064be5474 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 20 Aug 2025 20:40:18 -0700 Subject: [PATCH 4453/4563] Mark SE-0460 as implemented in Swift Next. (#2941) --- proposals/0460-specialized.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0460-specialized.md b/proposals/0460-specialized.md index e39ffc84c8..8c5ead4bd2 100644 --- a/proposals/0460-specialized.md +++ b/proposals/0460-specialized.md @@ -3,8 +3,7 @@ * Proposal: [SE-0460](0460-specialized.md) * Authors: [Ben Cohen](https://github.com/airspeedswift) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Accepted** -* Implementation: Available in nightly toolchains using the underscored `@_specialize` +* Status: **Implemented (Swift Next)** * Review: ([pitch](https://forums.swift.org/t/pitch-explicit-specialization/76967)) ([review](https://forums.swift.org/t/se-0460-explicit-specialization/77541)) ([acceptance](https://forums.swift.org/t/accepted-se-0460-explicit-specialization/78583)) ## Introduction From 08deb52a8f1516ba477d25589c3e832b47ccaeb1 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Wed, 20 Aug 2025 22:17:27 -0700 Subject: [PATCH 4454/4563] Mark SE-0476 as implemented in Swift 6.2. (#2942) --- proposals/0476-abi-attr.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/0476-abi-attr.md b/proposals/0476-abi-attr.md index 6bd6c3efa6..22caeee1a2 100644 --- a/proposals/0476-abi-attr.md +++ b/proposals/0476-abi-attr.md @@ -3,8 +3,7 @@ * Proposal: [SE-0476](0476-abi-attr.md) * Authors: [Becca Royal-Gordon](https://github.com/beccadax) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Accepted** -* Implementation: behind experimental feature `ABIAttribute` (refinements in [swiftlang/swift#80383](https://github.com/swiftlang/swift/pull/80383)) +* Status: **Implemented (Swift 6.2)** * Review: ([pitch](https://forums.swift.org/t/pitch-controlling-the-abi-of-a-declaration/75123)) ([review](https://forums.swift.org/t/se-0476-controlling-the-abi-of-a-function-initializer-property-or-subscript/79233)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0476-controlling-the-abi-of-a-function-initializer-property-or-subscript/79644)) ## Introduction From e432c48f9a9b8447f118368d12dbf657a34c4029 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Fri, 22 Aug 2025 17:06:22 -0500 Subject: [PATCH 4455/4563] Update header fields --- proposals/testing/0013-issue-severity-warning.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/testing/0013-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md index 5c7f0751f7..6acec8a213 100644 --- a/proposals/testing/0013-issue-severity-warning.md +++ b/proposals/testing/0013-issue-severity-warning.md @@ -3,9 +3,10 @@ - Proposal: [ST-0013](0013-issue-severity-warning.md) - Authors: [Suzy Ratcliff](https://github.com/suzannaratcliff) - Review Manager: [Maarten Engels](https://github.com/maartene) -- Status: **Accepted** -- Implementation: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075) -- Review: ([pitch](https://forums.swift.org/t/pitch-test-issue-warnings/79285)) ([review](https://forums.swift.org/t/st-0013-test-issue-warnings/80991)) ([accepted](https://forums.swift.org/t/accepted-st-0013-test-issue-severity/81385)) +- Status: **Implemented (Swift 6.3)** +- Implementation: [swiftlang/swift-testing#1075](https://github.com/swiftlang/swift-testing/pull/1075), + [swiftlang/swift-testing#1247](https://github.com/swiftlang/swift-testing/pull/1247) +- Review: ([pitch](https://forums.swift.org/t/pitch-test-issue-warnings/79285)) ([review](https://forums.swift.org/t/st-0013-test-issue-warnings/80991)) ([acceptance](https://forums.swift.org/t/accepted-st-0013-test-issue-severity/81385)) ## Introduction From 8c9787aff8b8ffc65867e0e9b2033f4382626bee Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Fri, 22 Aug 2025 17:07:23 -0500 Subject: [PATCH 4456/4563] Fix presentation of the event stream format diff --- proposals/testing/0013-issue-severity-warning.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/testing/0013-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md index 6acec8a213..398f0b1ed5 100644 --- a/proposals/testing/0013-issue-severity-warning.md +++ b/proposals/testing/0013-issue-severity-warning.md @@ -159,13 +159,13 @@ Issue severity will be in the event stream output when a `issueRecorded` event o The JSON event stream ABI will be amended correspondingly: -``` - ::= { - "isKnown": , ; is this a known issue or not? -+ "severity": , ; the severity of the issue -+ "isFailure": , ; if the issue is a failing issue - ["sourceLocation": ,] ; where the issue occurred, if known -} +```diff + ::= { + "isKnown": , ; is this a known issue or not? ++ "severity": , ; the severity of the issue ++ "isFailure": , ; if the issue is a failing issue + ["sourceLocation": ,] ; where the issue occurred, if known + } ``` Example of an `issueRecorded` event in the json output: From c7c3942254a1061c1e121a5af75ef5e91f739916 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Fri, 22 Aug 2025 17:10:05 -0500 Subject: [PATCH 4457/4563] Improve formatting of the "Console output" section: fix indentation and use Unicode status symbols instead of SF symbols which don't render on a web page --- proposals/testing/0013-issue-severity-warning.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/proposals/testing/0013-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md index 398f0b1ed5..5c49209f2e 100644 --- a/proposals/testing/0013-issue-severity-warning.md +++ b/proposals/testing/0013-issue-severity-warning.md @@ -176,17 +176,19 @@ Example of an `issueRecorded` event in the json output: ### Console output -When there is an issue recorded with severity warning the output looks like this: +When there is an issue recorded with severity warning, such as using the following code: ```swift - Issue.record("My comment", severity: .warning) +Issue.record("My comment", severity: .warning) ``` +the console output will look like the following: + ``` -􀟈 Test "All elements of two ranges are equal" started. -􀄣 Test "All elements of two ranges are equal" recorded a warning at ZipTests.swift:32:17: Issue recorded -􀄵 My comment -􁁛 Test "All elements of two ranges are equal" passed after 0.001 seconds with 1 warning. +◇ Test "All elements of two ranges are equal" started. +� Test "All elements of two ranges are equal" recorded a warning at ZipTests.swift:32:17: Issue recorded +↳ My comment +✔ Test "All elements of two ranges are equal" passed after 0.001 seconds with 1 warning. ``` ### Trying this out From 3f6d2582bb133fd87dee51d388d29bf632de006f Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Fri, 22 Aug 2025 17:11:14 -0500 Subject: [PATCH 4458/4563] Improve headers within "Integration with supporting tools" --- proposals/testing/0013-issue-severity-warning.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/testing/0013-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md index 5c49209f2e..aace60f793 100644 --- a/proposals/testing/0013-issue-severity-warning.md +++ b/proposals/testing/0013-issue-severity-warning.md @@ -153,7 +153,9 @@ For more details on `Issue`, refer to the [Issue Documentation](https://develope This revision aims to clarify the functionality and usage of the `Severity` enum and `Issue` properties while maintaining consistency with the existing Swift API standards. -### Integration with supporting tools +## Integration with supporting tools + +### Event stream Issue severity will be in the event stream output when a `issueRecorded` event occurs. This will be a breaking change because some tools may assume that all `issueRecorded` events are failing. Due to this we will be bumping the event stream version and v1 will maintain it's behavior and not output any events for non failing issues. We will also be adding `isFailure` to the issue so that clients will know if the issue should be treated as a failure. `isFailure` is a computed property. From f0717e55b96d978e6b5ac066aed3c4f06194b508 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Sun, 24 Aug 2025 21:37:43 -0500 Subject: [PATCH 4459/4563] Remove "Trying it out" section of the proposal, since this is typically only included in the proposal review announcement on the forums --- proposals/testing/0013-issue-severity-warning.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/proposals/testing/0013-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md index aace60f793..42ee86c3d8 100644 --- a/proposals/testing/0013-issue-severity-warning.md +++ b/proposals/testing/0013-issue-severity-warning.md @@ -193,16 +193,6 @@ the console output will look like the following: ✔ Test "All elements of two ranges are equal" passed after 0.001 seconds with 1 warning. ``` -### Trying this out - -To use severity today, checkout the branch here: https://github.com/swiftlang/swift-testing/pull/1189 - -``` -.package(url: "https://github.com/suzannaratcliff/swift-testing.git", branch: "suzannaratcliff:suzannaratcliff/enable-severity"), -``` - -For more details on how to checkout a branch for a package refer to this: https://developer.apple.com/documentation/packagedescription/package/dependency/package(url:branch:) - ## Alternatives considered - Separate Issue Creation and Recording: We considered providing a mechanism to create issues independently before recording them, rather than passing the issue details directly to the `record` method. This approach was ultimately set aside in favor of simplicity and directness in usage. From eac78275136da2ba7c1e8259d5f636f1bf2a2246 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Sun, 24 Aug 2025 21:42:07 -0500 Subject: [PATCH 4460/4563] Various stylistic and whitespace fixes --- .../testing/0013-issue-severity-warning.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/proposals/testing/0013-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md index 42ee86c3d8..ca085886ce 100644 --- a/proposals/testing/0013-issue-severity-warning.md +++ b/proposals/testing/0013-issue-severity-warning.md @@ -49,6 +49,7 @@ The `Severity` enum: ```swift extension Issue { // ... + public enum Severity: Codable, Comparable, CustomStringConvertible, Sendable { /// The severity level for an issue which should be noted but is not /// necessarily an error. @@ -63,7 +64,6 @@ extension Issue { /// marked as a failure. case error } - // ... } ``` @@ -78,6 +78,9 @@ Issue.record("My comment", severity: .warning) Here is the `Issue.record` method definition with severity as a parameter. ```swift +extension Issue { + // ... + /// Record an issue when a running test and an issue occurs. /// /// - Parameters: @@ -96,8 +99,7 @@ Here is the `Issue.record` method definition with severity as a parameter. severity: Severity = .error, sourceLocation: SourceLocation = #_sourceLocation ) -> Self - - // ... +} ``` ### Issue Type Enhancements @@ -107,13 +109,11 @@ The Issue type is enhanced with two new properties to better handle and report i - `severity`: This property allows access to the specific severity level of an issue, enabling more precise handling of test results. ```swift -// ... - extension Issue { + // ... -/// The severity of the issue. -public var severity: Severity { get set } - + /// The severity of the issue. + public var severity: Severity { get set } } ``` @@ -141,11 +141,10 @@ extension Issue { Example usage of `severity` and `isFailure`: ```swift -// ... withKnownIssue { // ... } matching: { issue in - return issue.isFailure || issue.severity > .warning + issue.isFailure || issue.severity > .warning } ``` From 44302dcc27e1f14f2fcd77e06e6c768078424dcf Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Sun, 24 Aug 2025 21:40:28 -0500 Subject: [PATCH 4461/4563] Add a Source Compatibility section to ST-0013 explaining motivation for keeping and deprecating the prior Issue.record() overload --- .../testing/0013-issue-severity-warning.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/proposals/testing/0013-issue-severity-warning.md b/proposals/testing/0013-issue-severity-warning.md index 5c7f0751f7..03223bb502 100644 --- a/proposals/testing/0013-issue-severity-warning.md +++ b/proposals/testing/0013-issue-severity-warning.md @@ -152,6 +152,36 @@ For more details on `Issue`, refer to the [Issue Documentation](https://develope This revision aims to clarify the functionality and usage of the `Severity` enum and `Issue` properties while maintaining consistency with the existing Swift API standards. +## Source compatibility + +The aspect of this proposal which adds a new `severity:` parameter to the +`Issue.record` function introduces the possibility of a source breakage for any +clients who are capturing a reference to the function. Existing code could break +despite the fact that the new parameter specifies a default value of `.error`. +Here's a contrived example: + +``` +// ❌ Source breakage due to new `Issue.Severity` parameter +let myRecordFunc: (Comment?, SourceLocation) -> Issue = Issue.record +``` + +To avoid source breakage, we will maintain the existing overload and preserve +its signature, but mark it deprecated, disfavored, and hidden from documentation: + +```swift +extension Issue { + // ... + + @available(*, deprecated, message: "Use record(_:severity:sourceLocation:) instead.") + @_disfavoredOverload + @_documentation(visibility: private) + @discardableResult public static func record( + _ comment: Comment? = nil, + sourceLocation: SourceLocation = #_sourceLocation + ) -> Self +} +``` + ### Integration with supporting tools Issue severity will be in the event stream output when a `issueRecorded` event occurs. This will be a breaking change because some tools may assume that all `issueRecorded` events are failing. Due to this we will be bumping the event stream version and v1 will maintain it's behavior and not output any events for non failing issues. We will also be adding `isFailure` to the issue so that clients will know if the issue should be treated as a failure. `isFailure` is a computed property. From a623ddca9237b760a988d696ec67c72280cec47d Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Fri, 29 Aug 2025 13:26:41 +0200 Subject: [PATCH 4462/4563] Update 0014-image-attachments-in-swift-testing-apple-platforms.md --- ...0014-image-attachments-in-swift-testing-apple-platforms.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md b/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md index 20f050cc0c..b4c95d641a 100644 --- a/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md +++ b/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md @@ -3,10 +3,10 @@ * Proposal: [ST-0014](0014-image-attachments-in-swift-testing-apple-platforms.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Review Manager: [Maarten Engels](https://github.com/maartene/) -* Status: **Active Review (August 8th...August 20th, 2025)** +* Status: **Accepted** * Bug: rdar://154869058 * Implementation: [swiftlang/swift-testing#827](https://github.com/swiftlang/swift-testing/pull/827), _et al._ -* Review: ([pitch](https://forums.swift.org/t/pitch-image-attachments-in-swift-testing/80867)) ([review](https://forums.swift.org/t/st-0014-image-attachments-in-swift-testing-apple-platforms/81507)) +* Review: ([pitch](https://forums.swift.org/t/pitch-image-attachments-in-swift-testing/80867)) ([review](https://forums.swift.org/t/st-0014-image-attachments-in-swift-testing-apple-platforms/81507)) ([acceptance](https://forums.swift.org/t/accepted-st-0014-image-attachments-in-swift-testing-apple-platforms/81868)) ## Introduction From 6bb1157bf84ec8ff64c048e95dbd3dacab939a44 Mon Sep 17 00:00:00 2001 From: Rick van Voorden Date: Tue, 3 Jun 2025 20:11:45 -0700 Subject: [PATCH 4463/4563] add is identical methods --- proposals/NNNN-add-is-identical-methods.md | 713 +++++++++++++++++++++ 1 file changed, 713 insertions(+) create mode 100644 proposals/NNNN-add-is-identical-methods.md diff --git a/proposals/NNNN-add-is-identical-methods.md b/proposals/NNNN-add-is-identical-methods.md new file mode 100644 index 0000000000..aa6c13db53 --- /dev/null +++ b/proposals/NNNN-add-is-identical-methods.md @@ -0,0 +1,713 @@ +# Add `isIdentical(to:)` Methods for Quick Comparisons to Concrete Types + +* Proposal: [SE-NNNN](NNNN-add-is-identical-methods.md) +* Authors: [Rick van Voorden](https://github.com/vanvoorden), [Karoy Lorentey](https://github.com/lorentey) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: ([String, Substring](https://github.com/swiftlang/swift/pull/82055)), ([Array, ArraySlice, ContiguousArray](https://github.com/swiftlang/swift/pull/82438)), ([Dictionary, Set](https://github.com/swiftlang/swift/pull/82439)) +* Review: ([Pre-Pitch](https://forums.swift.org/t/-/78792)), ([Pitch #1](https://forums.swift.org/t/-/79145)), ([Pitch #2](https://forums.swift.org/t/-/80496)) + +### Table of Contents + + * [Introduction](#introduction) + * [Motivation](#motivation) + * [Prior Art](#prior-art) + * [Proposed Solution](#proposed-solution) + * [Detailed Design](#detailed-design) + * [`String`](#string) + * [`Substring`](#substring) + * [`Array`](#array) + * [`ArraySlice`](#arrayslice) + * [`ContiguousArray`](#contiguousarray) + * [`Dictionary`](#dictionary) + * [`Set`](#set) + * [Source Compatibility](#source-compatibility) + * [Impact on ABI](#impact-on-abi) + * [Future Directions](#future-directions) + * [Alternatives Considered](#alternatives-considered) + * [Exposing Identity](#exposing-identity) + * [Different Names](#different-names) + * [Generic Contexts](#generic-contexts) + * [Overload for Reference Comparison](#overload-for-reference-comparison) + * [Support for Optionals](#support-for-optionals) + * [Alternative Semantics](#alternative-semantics) + * [Acknowledgments](#acknowledgments) + +## Introduction + +We propose new `isIdentical(to:)` instance methods to concrete types for quickly determining if two instances must be equal by-value. + +## Motivation + +Suppose we need an algorithm that transforms an `Array` of `Int` values to select only the even numbers: + +```swift +func result(for input: [Int]) -> [Int] { + print("computing new result") + return input.filter { + $0 % 2 == 0 + } +} +``` + +This produces a correct answer… but what about performance? We expect our `result` function to run in `O(n)` time across the size of our `input` value. Suppose we need for this algorithm to be called *many* times over the course of our application. It might also be the case that we sometimes call this algorithm with the same `input` value more than once: + +```swift +let a = [1, 2, 3, 4] +print(result(for: a)) +// Prints "computing new result" +// Prints "[2, 4]" +let b = a +print(result(for: b)) +// Prints "computing new result" +// Prints "[2, 4]" +let c = [1, 2, 3, 4] +print(result(for: c)) +// Prints "computing new result" +// Prints "[2, 4]" +let d = [1, 2, 3, 4, 5, 6] +print(result(for: d)) +// Prints "computing new result" +// Prints "[2, 4, 6]" +let e = d +print(result(for: e)) +// Prints "computing new result" +// Prints "[2, 4, 6]" +let f = [1, 2, 3, 4, 5, 6] +print(result(for: f)) +// Prints "computing new result" +// Prints "[2, 4, 6]" +``` + +If we call our `result` function with an `Array` of values and then pass the same `Array` of values again, we might want to return our previous `result` *without* performing another `O(n)` operation. Because our `result` function is a pure function and free of side-effects, we can check the new `input` value against the last `input` value we used to compute our `result`. If the `input` values have not changed, the `result` value *also* must not have changed. + +Here is an attempt to *memoize* our `result`: + +```swift +final class Memoizer { + private var input: [Int]? + private var result: [Int]? + + func result(for input: [Int]) -> [Int] { + if let result = self.result, + self.input == input { + return result + } else { + print("computing new result") + self.input = input + let result = input.filter { + $0 % 2 == 0 + } + self.result = result + return result + } + } +} +``` + +When we pass `input` values we can see that a new `result` is not computed if we already computed a `result` for those same `input` values: + +```swift +let memoizer = Memoizer() +let a = [1, 2, 3, 4] +print(memoizer.result(for: a)) +// Prints "computing new result" +// Prints "[2, 4]" +let b = a +print(memoizer.result(for: b)) +// Prints "[2, 4]" +let c = [1, 2, 3, 4] +print(memoizer.result(for: c)) +// Prints "[2, 4]" +let d = [1, 2, 3, 4, 5, 6] +print(memoizer.result(for: d)) +// Prints "computing new result" +// Prints "[2, 4, 6]" +let e = d +print(memoizer.result(for: e)) +// Prints "[2, 4, 6]" +let f = [1, 2, 3, 4, 5, 6] +print(memoizer.result(for: f)) +// Prints "[2, 4, 6]" +``` + +This looks like a big improvement… until we begin to investigate a little closer. There’s a subtle performance bottleneck here now from a different direction. Our memoization algorithm depends on the value equality of our `input` values — and this is *also* an `O(n)` operation. So while it is true that we have reduced the amount of `O(n)` operations that take place to compute our `result` values, we have *added* `O(n)` operations to determine value equality. As the amount of time spent computing value equality grows, we might no longer see any performance wins from memoization: it would be cheaper to just go ahead and compute a new `result` every time. + +Let’s see another example. Suppose we are working on our SwiftUI app to display Contacts from [SE-0261](0261-identifiable.md). Let’s begin with our basic data model: + +```swift +struct Contact: Identifiable, Equatable { + let id: Int + var name: String + var isFavorite: Bool +} +``` + +We added an `isFavorite` property to indicate our user added this `Contact` value as one of their favorites. + +Here is a SwiftUI view component that displays our favorite `Contact` values in a `FavoriteContactList`: + +```swift +struct FavoriteContactList: View { + @State private var selection: Contact.ID? + + private let contacts: [Contact] + + init(_ contacts: [Contact]) { + self.contacts = contacts + } + + private var favorites: [Contact] { + self.contacts.filter { + $0.isFavorite + } + } + + var body: some View { + List(self.favorites, selection: self.$selection) { contact in + FavoriteCell(contact) + } + } +} +``` + +When we compute our `body` property we also compute our `favorites` property. The implication is that *every* time our `body` property is computed we perform *another* `O(n)` algorithm across our `contacts`. Because our `FavoriteContactList` supports selection, every time our user selects a `Contact` value we update our `State`. Updating our `State` computes our `body` which computes our `favorites` property. So even though our `contacts` values *have not changed*, we *still* pay the performance penalty of *another* `O(n)` operation just to support cell selection. + +This might look like a good opportunity for another attempt at memoization. Here is an approach using a dynamic property wrapper: + +```swift +@propertyWrapper struct Favorites: DynamicProperty { + @State private var storage: Storage + private let contacts: [Contact] + + init(_ contacts: [Contact]) { + self.storage = Storage(contacts) + self.contacts = contacts + } + + func update() { + self.storage.update(self.contacts) + } + + var wrappedValue: [Contact] { + self.storage.wrappedValue + } +} + +extension Favorites { + private final class Storage { + private var contacts: [Contact] + private var favorites: [Contact]? + + init(_ contacts: [Contact]) { + self.contacts = contacts + self.favorites = nil + } + + func update(_ contacts: [Contact]) { + if self.contacts != contacts { + self.contacts = contacts + self.favorites = nil + } + } + + var wrappedValue: [Contact] { + if let favorites = self.favorites { + return favorites + } + print("computing new result") + let favorites = self.contacts.filter { + $0.isFavorite + } + self.favorites = favorites + return favorites + } + } +} +``` + +Here is what that looks like used from our `FavoriteContactList`: + +```swift +struct FavoriteContactList: View { + @State private var selection: Contact.ID? + + @Favorites private var favorites: [Contact] + + init(_ contacts: [Contact]) { + self._favorites = Favorites(contacts) + } + + var body: some View { + List(self.favorites, selection: self.$selection) { contact in + FavoriteCell(contact) + } + } +} +``` + +When we build and run our app we see that we no longer compute our `favorites` values every time our user selects a new `Contact`. But similar to what we saw in our command line utility, we have traded performance in a different direction. The value equality operation we perform is *also* `O(n)`. As the amount of time we spend computing value equality grows, we can begin to spend more time computing value equality than we would have spent computing our `favorites`: we no longer see the performance benefits of memoization. + +This proposal introduces an advanced performance hook for situations like this: a set of `isIdentical(to:)` methods that are designed to return *faster* than an operation to determine value equality. The `isIdentical(to:)` methods can return `true` in `O(1)` to indicate two values *must* be equal. + +## Prior Art + +We said that the performance of the value equality operator on an `Array` value was `O(n)`. This is true in the *worst case*, but there does exist an important “fast path” that can return `true` in constant time. + +Many types in Standard Library are “copy-on-write” data structures. These types present as value types, but can leverage a reference to some shared state to optimize for performance. When we copy this value we copy a reference to shared storage. If we perform a mutation on a copy we can preserve value semantics by copying the storage reference to a unique value before we write our mutation: we “copy” on “write”. + +This means that many types in Standard Library already have some private reference that can be checked in constant time to determine if two values are identical. Because these types copy before writing, two values that are identical by their shared storage *must* be equal by value. What we propose here is a way to “expose” this fast path operation. + +Product engineers have evolved patterns over the years that can already come close to what we are proposing. Product engineers building on `Array` can use `withUnsafeBufferPointer` or `withContiguousStorageIfAvailable` to compare the “identity” of two `Array` values. One drawback here is that these are only guaranteed to return an identity in constant time if there already exists a contiguous storage. If there does *not* exist a contiguous storage, we might have to perform an `O(n)` algorithm — which defeats the purpose of us choosing this as a fast path. Another option might be `withUnsafeBytes`, but this carries some restrictions on the `Element` of our `Array` and also might require for a contiguous storage to be created: an `O(n)` algorithm. + +Even if we were able to use `withUnsafeBytes` for other data structures, a comparison using `memcmp` might compare “unnecessary” bits that do not affect the identity. This slows down our algorithm and also returns “false negatives”: returning `false` when these instances should be treated as identical. + +A solution for modern operating systems is the support we added from [SE-0456](0456-stdlib-span-properties.md) to bridge an `Array` to `Span`. We can then compare these instances using the `isIdentical(to:)` method on `Span`. One drawback here is that we are blocked on back-deploying support for bridging `Array` to `Span`: it is only available on the most modern operating systems. Another drawback is that if our `Array` does not have a contiguous storage, we have to copy one: an `O(n)` operation. We are also blocked on bringing support for `Span` to collection types like `Dictionary` that do not already implement contiguous storage. + +A new `isIdentical(to:)` method could work around all these restrictions. We could return in constant time *without* needing to copy memory to a contiguous storage. We could adopt this method on many types that might not *ever* have a contiguous storage. We could also work with our library maintainers to discuss a back-deployment strategy that could bring this method to legacy operating systems. + +Our proposal would not be the first example of `isIdentical(to:)` shipping across Swift. `String` already ships a public-but-underscored version of this API.[^1] + +```swift +extension String { + /// Returns a boolean value indicating whether this string is identical to + /// `other`. + /// + /// Two string values are identical if there is no way to distinguish between + /// them. + /// + /// Comparing strings this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// string storage object. Therefore, identical strings are guaranteed to + /// compare equal with `==`, but not all equal strings are considered + /// identical. + /// + /// - Performance: O(1) + @_alwaysEmitIntoClient + public func _isIdentical(to other: Self) -> Bool { + self._guts.rawBits == other._guts.rawBits + } +} +``` + +We don’t see this API currently being used in Standard Library, but it’s possible this API is already being used to optimize performance in private frameworks from Apple. + +Many more examples of `isIdentical(to:)` functions are currently shipping in `Swift-Collections`[^2][^3][^4][^5][^6][^7][^8][^9][^10][^11][^12][^13], `Swift-Markdown`[^14], and `Swift-CowBox`[^15]. We also support `isIdentical(to:)` on the upcoming `Span` and `RawSpan` types from Standard Library.[^16] + +## Proposed Solution + +Before we look at the concrete types in this proposal, let’s begin with some more general principles and ideas we would expect for *all* concrete types to follow when adopting this new method. While this specific proposal is not adding a new protocol to Standard Library, it could be helpful to think of an “informal” protocol that guides us in choosing the types to adopt this new method. This could then serve as a guide for library maintainers that might choose to adopt this method on *new* types in the future. + +Suppose we are proposing an `isIdentical(to:)` method on a type `T`. We propose the following axioms that library maintainers should adopt: +* `a.isIdentical(to: a)` is always `true` (Reflexivity) +* If `T` is `Equatable`: + * `a.isIdentical(to: b)` implies `a == b` (*or else `a` and `b` are exceptional values*) + * `isIdentical(to:)` is *meaningfully* faster than `==` + +Let’s look through these axioms a little closer: + +**`a.isIdentical(to: a)` is always `true` (Reflexivity)** + +* An implementation of `isIdentical(to:)` that always returns `false` would not be an impactful API. We must guarantee that `isIdentical(to:)` *can* return `true` at least *some* of the time. + +**If `T` is `Equatable` then `a.isIdentical(to: b)` implies `a == b`** + +* This is the “fast path” performance optimization that will speed up the memoization examples we saw earlier. One important side effect here is that when `a.isIdentical(to: b)` returns `false` we make *no* guarantees about whether or not `a` is equal to `b`. +* We assume this axiom holds only if `a` and `b` are not “exceptional” values. A example of an exceptional value would be if a container that is generic over `Float` contains `nan`. + +**If `T` is `Equatable` then `isIdentical(to:)` is *meaningfully* faster than `==`** + +* While we could implement `isIdentical(to:)` on types like `Int` or `Bool`, these types are not included in this proposal. Our proposal focuses on types that have the ability to return from `isIdentical(to:)` meaningfully faster than `==`. If a type would perform the same amount of work in `isIdentical(to:)` that takes place in `==`, our advice is that library maintainers should *not* adopt `isIdentical(to:)` on this type. There should exist some legit internal fast-path on this type: like a pointer to a storage buffer that can be compared by reference identity. + +This proposal focuses on concrete types that are `Equatable`, but it might also be the case that a library maintainer would adopt `isIdentical(to:)` on a type that is *not* `Equatable`: like `Span`. Our expectation is that a library maintainer adopting `isIdentical(to:)`on a type that is not `Equatable` has some strong and impactful real-world use-cases ready to make use of this API. Just because a library maintainer *can* adopt this API does not imply they *should*. A library maintainer should also be ready to document for product engineers exactly what is implied from `a.isIdentical(to: b)` returning `true`. What does it *mean* for `a` to be “identical” to `b` if we do not have the implication that `a == b`? We leave this decision to the library maintainers that have the most context on the types they have built. + +Suppose we had an `isIdentical(to:)` method available on `Array`. Let’s go back to our earlier example and see how we can use this as an alternative to checking for value equality from our command line utility: + +```swift +final class Memoizer { + ... + + func result(for input: [Int]) -> [Int] { + if let result = self.result, + self.input.isIdentical(to: input) { + return result + } else { + ... + } + } +} +``` + +We can run our previous example and confirm that we are not computing new results when the input has not changed: + +```swift +let memoizer = Memoizer() +let a = [1, 2, 3, 4] +print(memoizer.result(for: a)) +// Prints "computing new result" +// Prints "[2, 4]" +let b = a +print(memoizer.result(for: b)) +// Prints "[2, 4]" +let c = [1, 2, 3, 4] +print(memoizer.result(for: c)) +// Prints "computing new result" +// Prints "[2, 4]" +let d = [1, 2, 3, 4, 5, 6] +print(memoizer.result(for: d)) +// Prints "computing new result" +// Prints "[2, 4, 6]" +let e = d +print(memoizer.result(for: e)) +// Prints "[2, 4, 6]" +let f = [1, 2, 3, 4, 5, 6] +print(memoizer.result(for: f)) +// Prints "computing new result" +// Prints "[2, 4, 6]" +``` + +When we return `true` from `isIdentical(to:)` we skip computing a new `result`. When `isIdentical(to:)` returns `false` we compute a new `result`. Because `isIdentical(to:)` *can* return `false` when two values are equal, we might be computing the same `result` more than once. The performance tradeoff is that because the operation to compute a new `result` is `O(n)` time, we might not *want* to perform another `O(n)` value equality operation to determine if we should compute a new `result`. Our `isIdentical(to:)` will return in constant time no matter how many elements are in `input` or how expensive this value equality operation would be. + +Let’s go back to our SwiftUI app for displaying `Contact` values. Here is what the change would look like to use `isIdentical(to:)` in place of value equality to memoize `favorites`: + +```swift +extension Favorites { + private final class Storage { + ... + + func update(_ contacts: [Contact]) { + if self.contacts.isIdentical(to: contacts) == false { + self.contacts = contacts + self.favorites = nil + } + } + + ... + } +} +``` + +When we build and run our SwiftUI app we confirm that we are not computing new `favorites` when the user selects new `Contact` values from `FavoriteContactList`. + +## Detailed Design + +We propose adding `isIdentical` methods to the following concrete types from Standard Library: +* `String` +* `Substring` +* `Array` +* `ArraySlice` +* `ContiguousArray` +* `Dictionary` +* `Set` + +For each type being presented we codify important semantics in our header documentation. + +### `String` + +```swift +extension String { + /// Returns a boolean value indicating whether this string is identical to + /// `other`. + /// + /// Two string values are identical if there is no way to distinguish between + /// them. + /// + /// For any values `a`, `b`, and `c`: + /// + /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) + /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, + /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) + /// - `a.isIdentical(b)` implies `a == b` + /// + /// Comparing strings this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// string storage object. Therefore, identical strings are guaranteed to + /// compare equal with `==`, but not all equal strings are considered + /// identical. + /// + /// - Performance: O(1) + public func isIdentical(to other: Self) -> Bool { ... } +} +``` + +### `Substring` + +```swift +extension Substring { + /// Returns a boolean value indicating whether this substring is identical to + /// `other`. + /// + /// Two substring values are identical if there is no way to distinguish + /// between them. + /// + /// For any values `a`, `b`, and `c`: + /// + /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) + /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, + /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) + /// - `a.isIdentical(b)` implies `a == b` + /// + /// Comparing substrings this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// substring storage object. Therefore, identical substrings are guaranteed + /// to compare equal with `==`, but not all equal substrings are considered + /// identical. + /// + /// - Performance: O(1) + public func isIdentical(to other: Self) -> Bool { ... } +} +``` + +### `Array` + +```swift +extension Array { + /// Returns a boolean value indicating whether this array is identical to + /// `other`. + /// + /// Two array values are identical if there is no way to distinguish between + /// them. + /// + /// For any values `a`, `b`, and `c`: + /// + /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) + /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, + /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) + /// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` + /// + /// Comparing arrays this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// array storage object. Therefore, identical arrays are guaranteed to + /// compare equal with `==`, but not all equal arrays are considered + /// identical. + /// + /// - Performance: O(1) + public func isIdentical(to other: Self) -> Bool { ... } +} +``` + +### `ArraySlice` + +```swift +extension ArraySlice { + /// Returns a boolean value indicating whether this array is identical to + /// `other`. + /// + /// Two array values are identical if there is no way to distinguish between + /// them. + /// + /// For any values `a`, `b`, and `c`: + /// + /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) + /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, + /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) + /// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` + /// + /// Comparing arrays this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// array storage object. Therefore, identical arrays are guaranteed to + /// compare equal with `==`, but not all equal arrays are considered + /// identical. + /// + /// - Performance: O(1) + public func isIdentical(to other: Self) -> Bool { ... } +} +``` + +### `ContiguousArray` + +```swift +extension ContiguousArray { + /// Returns a boolean value indicating whether this array is identical to + /// `other`. + /// + /// Two array values are identical if there is no way to distinguish between + /// them. + /// + /// For any values `a`, `b`, and `c`: + /// + /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) + /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, + /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) + /// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` + /// + /// Comparing arrays this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// array storage object. Therefore, identical arrays are guaranteed to + /// compare equal with `==`, but not all equal arrays are considered + /// identical. + /// + /// - Performance: O(1) + public func isIdentical(to other: Self) -> Bool { ... } +} +``` + +### `Dictionary` + +```swift +extension Dictionary { + /// Returns a boolean value indicating whether this dictionary is identical to + /// `other`. + /// + /// Two dictionary values are identical if there is no way to distinguish + /// between them. + /// + /// For any values `a`, `b`, and `c`: + /// + /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) + /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, + /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) + /// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` + /// + /// Comparing dictionaries this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying + /// dictionary storage object. Therefore, identical dictionaries are + /// guaranteed to compare equal with `==`, but not all equal dictionaries are + /// considered identical. + /// + /// - Performance: O(1) + public func isIdentical(to other: Self) -> Bool { ... } +} +``` + +### `Set` + +```swift +extension Set { + /// Returns a boolean value indicating whether this set is identical to + /// `other`. + /// + /// Two set values are identical if there is no way to distinguish between + /// them. + /// + /// For any values `a`, `b`, and `c`: + /// + /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) + /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, + /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) + /// - `a.isIdentical(b)` implies `a == b` + /// + /// Comparing sets this way includes comparing (normally) hidden + /// implementation details such as the memory location of any underlying set + /// storage object. Therefore, identical sets are guaranteed to compare equal + /// with `==`, but not all equal sets are considered identical. + /// + /// - Performance: O(1) + public func isIdentical(to other: Self) -> Bool { ... } +} +``` + +## Source Compatibility + +This proposal is additive and source-compatible with existing code. + +## Impact on ABI + +This proposal is additive and ABI-compatible with existing code. + +## Future Directions + +Any Standard Library types that are copy-on-write values could be good candidates to add `isIdentical` functions. Here are some potential types to consider for a future proposal: + +* `Character` +* `Dictionary.Keys` +* `Dictionary.Values` +* `KeyValuePairs` +* `StaticBigInt` +* `StaticString` +* `String.UnicodeScalarView` +* `String.UTF16View` +* `String.UTF8View` +* `Substring.UnicodeScalarView` +* `Substring.UTF16View` +* `Substring.UTF8View` +* `UTF8Span` + +This proposal focuses on what we see as the most high-impact types to support from Standard Library. This proposal *is not* meant to discourage adding `isIdentical(to:)` on any of these types at some point in the future. A follow-up “second-round” proposal could focus on these remaining types. + +## Alternatives Considered + +### Exposing Identity + +Our proposal introduces a new instance method on types that uses some underlying concept of “identity” to perform quick comparisons between two instances. A different approach would be to *return* the underlying identity to product engineers. If a product engineer wanted to test two instances for equality by identity they could perform that check themselves. + +There’s a lot of interesting directions to go with that idea… but we don’t think this is right approach for now. Introducing some concept of an “escapable” identity to value types like `Array` would require *a lot* of design. It’s overthinking the problem and solving for something we don’t need right now. + +### Different Names + +Multiple different names have been suggested for these operations. Including: + +* `hasSameRepresentation(as:)` +* `isKnownIdentical(to:)` + +We prefer `isIdentical(to:)`. This also has the benefit of being consistent with the years of prior art we have accumulated across the Swift ecosystem. + +### Generic Contexts + +We proposed an “informal” protocol for library maintainers adopting `isIdentical(to:)` on new types. Could we just build a new protocol in Standard Library? Maybe. We don’t see a big need for this right now. If product engineers would want for these types to conform to some common protocol to use across generic contexts, those product engineers can define that protocol in their own packages. If these protocols “incubate” in the community and become a common practice, we can consider proposing a new protocol in Standard Library. + +Instead of a new protocol, could we somehow add `isIdentical(to:)` on `Equatable`? Maybe. This would introduce some more tricky questions. If we adopt this on *all* `Equatable` types, what do we do about types like `Int` or `Bool` that do not have an ability to perform a fast check for identity? Similar to our last idea, we prefer to focus just on concrete types for now. If product engineers want to make `isIdentical(to:)` available on generic contexts across `Equatable`, we encourage them to experiment with their own extension for that. If this pattern becomes popular in the community, we can consider a new proposal to add this on `Equatable` in Standard Library. + +### Overload for Reference Comparison + +Could we “overload” the `===` operator from `AnyObject`? This proposal considers that question to be orthogonal to our goal of exposing identity equality with the `isIdentical` methods. We could choose to overload `===`, but this would be a larger “conceptual” and “philosophical” change because the `===` operator is currently meant for `AnyObject` types — not value types like `Array`. + +### Support for Optionals + +We can support `Optional` values with the following extension: + +```swift +extension Optional { + public func isIdentical(to other: Self) -> Bool + where Wrapped == Array { + switch (self, other) { + case let (value?, other?): + return value.isIdentical(to: other) + case (nil, nil): + return true + default: + return false + } + } +} +``` + +Because this extension needs no `private` or `internal` symbols from Standard Library, we can omit this extension from our proposal. Product engineers that want this extension can choose to implement it for themselves. + +### Alternative Semantics + +Instead of publishing an `isIdentical` method which implies two types *must* be equal, could we think of things from the opposite direction? Could we publish a `maybeDifferent` method which implies two types *might not* be equal? This then introduces some potential ambiguity for product engineers: to what extent does “maybe different” imply “probably different”? This ambiguity could be settled with extra documentation on the method, but `isIdentical` solves that ambiguity up-front. The `isIdentical` method is also consistent with the prior art in this space. + +In the same way this proposal exposes a way to quickly check if two values *must* be equal, product engineers might want a way to quickly check if two values *must not* be equal. This is an interesting idea, but this can exist as an independent proposal. We don’t need to block the review of this proposal on a review of `isNotIdentical` semantics. + +## Acknowledgments + +Thanks to [Ben Cohen](https://forums.swift.org/t/-/78792/7) for helping to think through and generalize the original use-case and problem-statement. + +Thanks to [David Nadoba](https://forums.swift.org/t/-/80496/61/) for proposing the formal equivalence relation semantics and axioms on concrete types. + +Thanks to [Xiaodi Wu](https://forums.swift.org/t/-/80496/67) for proposing that our equivalence relation semantics would carve-out for “exceptional” values like `Float.nan`. + +[^1]: https://github.com/swiftlang/swift/blob/swift-6.1.2-RELEASE/stdlib/public/core/String.swift#L397-L415 +[^2]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/DequeModule/Deque._Storage.swift#L223-L225 +[^3]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/HashTreeCollections/HashNode/_HashNode.swift#L78-L80 +[^4]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/HashTreeCollections/HashNode/_RawHashNode.swift#L50-L52 +[^5]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/RopeModule/BigString/Conformances/BigString%2BEquatable.swift#L14-L16 +[^6]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/RopeModule/BigString/Views/BigString%2BUnicodeScalarView.swift#L77-L79 +[^7]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/RopeModule/BigString/Views/BigString%2BUTF8View.swift#L39-L41 +[^8]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/RopeModule/BigString/Views/BigString%2BUTF16View.swift#L39-L41 +[^9]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/RopeModule/BigString/Views/BigSubstring.swift#L100-L103 +[^10]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/RopeModule/BigString/Views/BigSubstring%2BUnicodeScalarView.swift#L94-L97 +[^11]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/RopeModule/BigString/Views/BigSubstring%2BUTF8View.swift#L64-L67 +[^12]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/RopeModule/BigString/Views/BigSubstring%2BUTF16View.swift#L87-L90 +[^13]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/RopeModule/Rope/Basics/Rope.swift#L68-L70 +[^14]: https://github.com/swiftlang/swift-markdown/blob/swift-6.1.1-RELEASE/Sources/Markdown/Base/Markup.swift#L370-L372 +[^15]: https://github.com/Swift-CowBox/Swift-CowBox/blob/1.1.0/Sources/CowBox/CowBox.swift#L19-L27 +[^16]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0447-span-access-shared-contiguous-storage.md From 53c4b2ddbabb5814a57e49119358abd83e99d7a1 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Wed, 3 Sep 2025 15:27:36 -0700 Subject: [PATCH 4464/4563] Update Section Control proposal to be independent of @const --- proposals/0nnn-section-control.md | 107 ++++++++++++++---------------- 1 file changed, 49 insertions(+), 58 deletions(-) diff --git a/proposals/0nnn-section-control.md b/proposals/0nnn-section-control.md index 3f0d9e0aca..4d30b0bfc7 100644 --- a/proposals/0nnn-section-control.md +++ b/proposals/0nnn-section-control.md @@ -62,7 +62,7 @@ This proposal recommends to use sections of the various object file formats as t The proposal is to add two new attributes `@section` and `@used` that will allow annotating global and static variables with directives to place the value into a custom section, and to require no-dead-stripping aka "attribute used". The `@section` attribute relies heavily on the facilities provided by [Swift Compile-Time Values](https://github.com/artemcm/swift-evolution/blob/const-values/proposals/0nnn-const-values.md), namely the ability to enforce constantness of an expressions. Using `@section` requires that the initializer expression is a constant expression: ```swift -// Place entry into a section, mark as "do not dead strip". +// Place an entry into a section, mark as "do not dead strip". // Initializer expression must be a constant expression. // The global variable is implicitly made statically initialized. @section("__DATA,mysection") @@ -91,24 +91,14 @@ let myPlugin: PluginData = ( On top of specifying a custom section name with the `@section` attribute, marking a variable as `@used` is needed to prevent otherwise unused symbols from being removed by the compiler. When using section placement to e.g. implement linker sets, such values are typically going to have no usage at compile time, and at the same time they should not be exposed in public interface of libraries (not be made public), therefore we the need the `@used` attribute. -A related new attribute, `@constInitialized` is to be added, which can be used to enforce that a global or static variable is statically initialized, without placing it into a custom section. Using attribute `@section` implies `@constInitialized`. - -```swift -// Static initialization can be requested separately -@constInitialized -var fourPages = 4 * 4096 -``` - -Different object file formats (ELF, Mach-O, COFF) have different restrictions and rules on what are valid section names, and cross-platform code will have to use different names for different file formats. To support that, custom section names can be specified as either a string literal, or by referencing a constant global/static declaration that contains the name. The name will be directly set for the symbol in the resulting object file, without any processing. A new `#if objectFileFormat(...)` conditional compilation directive will be provided to support conditionalizing based on the file format: +Different object file formats (ELF, Mach-O, COFF) have different restrictions and rules on what are valid section names, and cross-platform code will have to use different names for different file formats. To support that, custom section names can be specified as a string literal. The name will be directly set for the symbol in the resulting object file, without any processing. A new `#if objectFileFormat(...)` conditional compilation directive will be provided to support conditionalizing based on the file format: ```swift #if objectFileFormat(ELF) -@const let mySectionName = ".mysection" +@section(".mysection") #elseif objectFileFormat(MachO) -@const let mySectionName = "__DATA,mysection" +@section("__DATA,mysection") #endif - -@section(mySectionName) var global = ... ``` @@ -118,7 +108,7 @@ For the ELF file format specifically, the compiler will also emit a “section i ### Attributes @section and @used on global and static variables -Two new attributes are to be added: `@section`, which has a single argument specifying the section name, and a argument-less `@used` attribute. The section name must be either a string literal, or a reference to a constant string declaration. +Two new attributes are to be added: `@section`, which has a single argument specifying the section name, and a argument-less `@used` attribute. The section name must be a string literal. The attributes can be used either together or independently. ```swift // (1) @@ -127,32 +117,23 @@ Two new attributes are to be added: `@section`, which has a single argument spec let global = ... // ✅ // (2) -@const let mySectionName = "__DATA,mysection" - -@section(mySectionName) +@section("__DATA,mysection") let global = ... // ✅ // (3) -@section(Bool.rand() ? "a" : "b") -let global = ... // ❌ -``` - -The section name must not be one of Swift’s runtime reserved sections (e.g. `__swift5_types`, etc.), such sections would be rejected: - -```swift -@section("__TEXT,__swift5_types") -let global = ... // ❌ +@used +let global = ... // ✅ ``` The new attributes (`@section` and `@used`) can be used on variable declarations under these circumstances: * the variable must be a global variable or a static member variable (no local variables, no non-static member variables) * the variable must not be declared inside a generic context (either directly in generic type or nested in a generic type) -* the variable must be a stored property (not be a computed property) +* the variable must be a stored property (not a computed property) * the variable must not have property observers (didSet, willSet) * the initial expression assigned to the variable must be a constant expression, and it must be eligible for static initilization -*Note: These restrictions limit the `@section` and `@used` attributes to only be allowed on variables that are expected to be represented as exactly one statically-initialized global storage symbol (in the linker sense) for the variable’s content. This is generally true for all global and static variables in C and C++, but in Swift global variables might have zero global storage symbols (e.g. a computed property), or need non-trivial storage (e.g. lazily initialized variables with runtime code in the initializer expression).* +> *Note: These restrictions limit the `@section` and `@used` attributes to only be allowed on variables that are expected to be represented as exactly one statically-initialized global storage symbol (in the linker sense) for the variable’s content. This is generally true for all global and static variables in C and C++, but in Swift global variables might have zero global storage symbols (e.g. a computed property), or need non-trivial storage (e.g. lazily initialized variables with runtime code in the initializer expression).* ```swift @section("__DATA,mysection") @used @@ -201,31 +182,34 @@ The effects described above are applied to the storage symbols and don’t gener ### Guaranteed static initialization -Using attribute `@section` requires the initializer expression of the variable to be a constant expression. The `@const` annotation on the expression is not required (it’s implied). On top of the constant-ness, `@section` on a global or static variable enforces **static initialization** on that variable. The variable can be statically initialized, if the constant expression not only represents a valid compile-time constant, but also is able to be constant-folded into a representation that does not require any runtime initialization (pointer relocations/fixups done automatically by the loader are not considered runtime initialization for this purpose). +Using attribute `@section` requires the initializer expression of the variable to be a **constant expression**. It's not required to separately annotate the expression for being a compile-time expression, instead this is implied from the `@section` attribute. On top of the constant-ness, `@section` on a global or static variable enforces **static initialization** on that variable. + +We consider the variable to be eligible for static initialization when: -This is a property that `@const` alone does not provide, but it’s necessary because we expect the data to be readable without any runtime mechanisms (i.e. reading raw bytes from the section at runtime, or offline binary inspection). +1. the initializer expression represents a valid compile-time constant, and +2. the initializer expression can be constant-folded into a representation that does not require any runtime initialization (pointer relocations/fixups done automatically by the loader are not considered runtime initialization for this purpose). + +Not all constant expressions are necessarily statically initializable. For section placement we require the stronger property (static initialization) because we expect the data to be readable without any runtime mechanisms (i.e. reading raw bytes from the section at runtime, or offline binary inspection). ```swift @section("__DATA,mysection") let a = 42 // ✅ @section("__DATA,mysection") -let b = @const 42 // warning: @const is superfluous +let sectionPlaced = ...expression... // ✅, guaranteed to be statically initialized @section("__DATA,mysection") -let sectionPlaced = ...expression... // guaranteed to be statically initialized - -let justConstant = @const ...expression... // not guaranteed to be statically initialized +let notStaticallyInitializable = ...expression that cannot be statically initialized... // ❌ ``` -*Note: As of this writing, all valid constant values are also eligible to be statically initialized, but we don’t expect that to hold in the future. So it’s important to distinguish between (a) a global variable being initialized with a language-level constant value (`@const`), and (b) a global variable that is guaranteed to be statically initialized. The difference can be subtle, and in some cases immaterial in practice, but future enhancements of constant values in Swift might take advantage of this difference — i.e. not all constant values are going to be statically initializable. Consider the following example: If a future language versions allows dictionary literals to be constant values, such values might not be statically initializable because of randomized hash seeding:* +> *Note: As of this writing, all valid constant values are also eligible to be statically initialized, but we don’t expect that to hold in the future. So it’s important to distinguish between (a) a global variable being initialized with a language-level constant value, and (b) a global variable that is guaranteed to be statically initialized. The difference can be subtle, and in some cases immaterial in practice, but future enhancements of constant values in Swift might take advantage of this difference — i.e. not all constant values are going to be statically initializable. Consider the following example: If a future language versions allows dictionary literals to be constant values, such values might not be statically initializable because of randomized hash seeding:* -```swift -let d1 = @const ["a": 42, "b": 777] // constant, but not statically initializable -let d2 = @const d1.count // statically initializable -``` +> ```swift +> let d1 = ["a": 42, "b": 777] // constant, but not statically initializable +> let d2 = d1.count // statically initializable +> ``` -*However, using a statically non-initializable value in an expression does not preclude the outer expression from being statically initialized either. In this example, `d1` would not be allowed to be placed into a custom section because it’s not statically initializable. But `d2` could still potentially be statically initializable (even though the definition of `d2` uses a sub-expression that is not statically initializable), as it’s simply an integer.* +> *However, using a statically non-initializable value in an expression does not preclude the outer expression from being statically initialized either. In this example, `d1` would not be allowed to be placed into a custom section because it’s not statically initializable. But `d2` could still potentially be statically initializable (even though the definition of `d2` uses a sub-expression that is not statically initializable), as it’s simply an integer.* As described in [Swift Compile-Time Values](https://github.com/artemcm/swift-evolution/blob/const-values/proposals/0nnn-const-values.md), values of function types are eligible for being compile-time evaluable. Their concrete pointer value is not fully known until link-time or program load-time (depending on type of linking, ASLR, PAC, etc.). For the purposes of guaranteed static initialization, function values are statically initialized into a function pointer. This pointer is still subject to normal linking and loading resolutions and fixups. @@ -237,22 +221,6 @@ let a = (42, foo) // "foo" is statically initialized into a // linkable/relocatable pointer ``` -### Attribute `@constInitialized` - -Static initialization of a global can be useful on its own, without placing data into a custom section. For that, a new attribute `@constInitialized` can be used. This attribute can be only used on variable declarations under the same conditions that `@section` and `@used` require (e.g. only on globals and statics, not in generic contexts, etc.) - -```swift -@constInitialized -var fourPages = 4 * 4096 // ✅ - -struct S { - @constInitialized - var fourPages = 4 * 4096 // ❌ -} -``` - -The effect of this attribute is the same as of the `@section` attribute (static initialization, normal initalization behavior if top-level code) except the symbol is not actually placed into any custom sections. - ### Cross-platform object file format support The custom section name specified in the `@section` attribute is not validated by the compiler, instead it’s passed directly as a string to the linker. Different platforms supported by Swift are using different object and binary file formats (Linux uses ELF, Darwin uses Mach-O, Windows uses COFF), and that implies different restrictions and rules on what are valid section names. Because of that, a multi-platform library code is expected to use `#if os(...)` to use different section names for different platforms. Because of that, it’s expected that the attributes are to be typically only used indirectly via macros that hide the low-level nature of sections and object file formats from higher-level code developers: @@ -305,7 +273,7 @@ The goal of placing metadata into custom section is to make them discoverable bo * In Wasm, dynamic linking is work in progress and not generally available yet. * In ELF, however, section bounds are not guaranteed to be present in the address space at runtime, and in practice they are typically not present. This creates a challenge for retrieving section data in this configuration (ELF + multiple modules with dynamic linking) at runtime. -To solve this problem for the ELF object file format, the Swift compiler is going to emit a “**section index**” into every compilation that uses any symbols placed into a custom section. The index will be emitted only when producing ELF files, and consists of entries added into its own separate well-named section called `swift5_sections`. Each entry will have the following structure: +To solve this problem for the ELF object file format, the Swift compiler is going to emit a “**section index**” into every compilation that uses any symbols placed into a custom section. The index will be emitted only when producing ELF files, and consists of entries added into its own separate well-named section called `swift5_sections`. Each entry will have the following structure: ```c struct SectionIndexEntry { @@ -353,6 +321,25 @@ func firmwareBootEntrypoint() { ... } This will require some design decisions to be made around when should that be allowed, whether the attribute should be automatically inherited, and what exact behavior should we expect from the compiler around thunks, compiler-generated helper functions, getters and setters, etc. +### Standalone attribute for required static initialization + +Static initialization of a global can be useful on its own, without placing data into a custom section, and a separate attribute for that could be added. This way, one can get the same effects as the `@section` attribute (static initialization, normal initalization behavior if top-level code) except the symbol would not be actually placed into any custom section. + +### Allowing a reference to a constant string declaration as a section name + +The requirement to only use string literals as the section names could be lifted in the future, and we might allow referring to a declaration of variable with a compile-time string. This would be useful to avoid repetition when placing multiple values into the same section without needing to use macros. + +```swift +#if objectFileFormat(ELF) +let mySectionName = ".mysection" // required to be a compile-time value +#elseif objectFileFormat(MachO) +let mySectionName = "__DATA,mysection" // required to be a compile-time value +#endif + +@section(mySectionName) +var global = ... +``` + ### Runtime discovery of data in custom sections As described in [ELF section index](#elf-section-index), accessing records in a custom section at runtime is heavily dependent on the object file format (ELF, Mach-O, Wasm, COFF), type of linking (static vs dynamic) and available APIs from the operating system. For a single configuration, users can directly use an appropriate method of accessing the section data, and e.g. in embedded firmwares this might be completely fine as projects are commonly avoiding any attempts to be multi-platform or portable. @@ -445,3 +432,7 @@ In a lot of the list code snippets in this proposal, both `@section` and `@used` * `@section` and `@used` represent separate concepts and all combinations of them can be useful. An example of using `@section` without `@used` is to place for example a large data table from a library into its own section for binary size accounting reasons (so that it shows up separately in per-section binary size listings), but where we’d still expect the data table to be dead-code removed if not used. * It’s already common to have those attributes as separate options in existing popular systems programming languages (C, C++, Rust). + +### Blocking section placement into compiler reserved sections + +In most cases, placing data into one of Swift’s runtime reserved sections (e.g. `__swift5_types`, etc.) without relying on extreme details of the compiler and runtime would result in invalid binaries. It was considered to simply reject using `@section` to target one of these reserved sections, but ultimately that would introduce both false positives (*what if we at some point wanted to write compiler/runtime code in Swift to actually legitimately place data into these sections?*) and false negatives (*there are many other "reserved" sections that the Swift compiler and language cannot know about*), and is thus left out of this proposal. From 1b8f13e0948d442a9e1f52695465824a3230dd89 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 27 Aug 2025 16:06:57 -0400 Subject: [PATCH 4465/4563] [ST-NNNN] Test cancellation --- proposals/testing/NNNN-test-cancellation.md | 398 ++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 proposals/testing/NNNN-test-cancellation.md diff --git a/proposals/testing/NNNN-test-cancellation.md b/proposals/testing/NNNN-test-cancellation.md new file mode 100644 index 0000000000..20794db902 --- /dev/null +++ b/proposals/testing/NNNN-test-cancellation.md @@ -0,0 +1,398 @@ +# Test cancellation + +* Proposal: [ST-NNNN](NNNN-test-cancellation.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Review Manager: TBD +* Status: **Awaiting review** +* Bug: [swiftlang/swift-testing#120](https://github.com/swiftlang/swift-testing/issues/120) +* Implementation: [swiftlang/swift-testing#1284](https://github.com/swiftlang/swift-testing/pull/1284) +* Review: ([pitch](https://forums.swift.org/t/pitch-test-cancellation/81847)) + +## Introduction + +Swift Testing provides the ability to conditionally skip a test before it runs +using the [`.enabled(if:)`](https://developer.apple.com/documentation/testing/trait/enabled(if:_:sourcelocation:)), +[`.disabled(if:)`](https://developer.apple.com/documentation/testing/trait/disabled(if:_:sourcelocation:)), +etc. family of traits: + +```swift +@Test(.enabled(if: Tyrannosaurus.isTheLizardKing)) +func `Tyrannosaurus is scary`() { + let dino = Tyrannosaurus() + #expect(dino.isScary) + // ... +} +``` + +This proposal extends that feature to allow cancelling a test after it has +started but before it has ended. + +## Motivation + +We have received feedback from a number of developers indicating that their +tests have constraints that can only be checked after a test has started, and +they would like the ability to end a test early and see that state change +reflected in their development tools. + +To date, we have not provided an API for ending a test's execution early because +we want to encourage developers to use the [`.enabled(if:)`](https://developer.apple.com/documentation/testing/trait/enabled(if:_:sourcelocation:)) +_et al._ trait. This trait can be evaluated early and lets Swift Testing plan a +test run more efficiently. However, we recognize that these traits aren't +sufficient. Some test constraints are dependent on data that isn't available +until the test starts, while others only apply to specific test cases in a +parameterized test function. + +## Proposed solution + +A static `cancel()` function is added to the [`Test`](https://developer.apple.com/documentation/testing/test) +and [`Test.Case`](https://developer.apple.com/documentation/testing/test/case) +types. When a test author calls these functions from within the body of a test +(or from within the implementation of a trait, e.g. from [`prepare(for:)`](https://developer.apple.com/documentation/testing/trait/prepare(for:))), +Swift Testing cancels the currently-running test or test case, respectively. + +### Relationship between tasks and tests + +Each test runs in its own task during a test run, and each test case in a test +also runs in its own task. Cancelling the current task from within the body of a +test will, therefore, cancel the current test case, but not the current test: + +```swift +@Test(arguments: Species.all(in: .dinosauria)) +func `Are all dinosaurs extinct?`(_ species: Species) { + if species.in(.aves) { + // Birds aren't extinct (I hope) + withUnsafeCurrentTask { $0?.cancel() } + return + } + // ... +} +``` + +Using [`withUnsafeCurrentTask(body:)`](https://developer.apple.com/documentation/swift/withunsafecurrenttask(body:)-6gvhl) +here is not ideal. It's not clear that the intent is to cancel the test case, +and [`UnsafeCurrentTask`](https://developer.apple.com/documentation/swift/unsafecurrenttask) +is, unsurprisingly, an unsafe interface. + +> [!NOTE] +> The version of Swift Testing included with Swift 6.2 does not correctly handle +> task cancellation under all conditions. See [swiftlang/swift-testing#1289](https://github.com/swiftlang/swift-testing/issues/1289). + +## Detailed design + +New static members are added to [`Test`](https://developer.apple.com/documentation/testing/test) +and [`Test.Case`](https://developer.apple.com/documentation/testing/test/case): + +```swift +extension Test { + /// Cancel the current test. + /// + /// - Parameters: + /// - comment: A comment describing why you are cancelling the test. + /// - sourceLocation: The source location to which the testing library will + /// attribute the cancellation. + /// + /// - Throws: An error indicating that the current test case has been + /// cancelled. + /// + /// The testing library runs each test in its own task. When you call this + /// function, the testing library cancels the task associated with the current + /// test: + /// + /// ```swift + /// @Test func `Food truck is well-stocked`() throws { + /// guard businessHours.contains(.now) else { + /// try Test.cancel("We're off the clock.") + /// } + /// // ... + /// } + /// ``` + /// + /// If the current test is parameterized, all of its pending and running test + /// cases are cancelled. If the current test is a suite, all of its pending + /// and running tests are cancelled. If you have already cancelled the current + /// test or if it has already finished running, this function throws an error + /// but does not attempt to cancel the test a second time. + /// + /// - Important: If the current task is not associated with a test (for + /// example, because it was created with [`Task.detached(name:priority:operation:)`](https://developer.apple.com/documentation/swift/task/detached(name:priority:operation:)-795w1)) + /// this function records an issue and cancels the current task. + /// + /// To cancel the current test case but leave other test cases of the current + /// test alone, call ``Test/Case/cancel(_:sourceLocation:)`` instead. + public static func cancel(_ comment: Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation) throws -> Never +} + +extension Test.Case { + /// Cancel the current test case. + /// + /// - Parameters: + /// - comment: A comment describing why you are cancelling the test case. + /// - sourceLocation: The source location to which the testing library will + /// attribute the cancellation. + /// + /// - Throws: An error indicating that the current test case has been + /// cancelled. + /// + /// The testing library runs each test case of a test in its own task. When + /// you call this function, the testing library cancels the task associated + /// with the current test case: + /// + /// ```swift + /// @Test(arguments: [Food.burger, .fries, .iceCream]) + /// func `Food truck is well-stocked`(_ food: Food) throws { + /// if food == .iceCream && Season.current == .winter { + /// try Test.Case.cancel("It's too cold for ice cream.") + /// } + /// // ... + /// } + /// ``` + /// + /// If the current test is parameterized, the test's other test cases continue + /// running. If the current test case has already been cancelled, this + /// function throws an error but does not attempt to cancel the test case a + /// second time. + /// + /// - Important: If the current task is not associated with a test case (for + /// example, because it was created with [`Task.detached(name:priority:operation:)`](https://developer.apple.com/documentation/swift/task/detached(name:priority:operation:)-795w1)) + /// this function records an issue and cancels the current task. + /// + /// To cancel all test cases in the current test, call + /// ``Test/cancel(_:sourceLocation:)`` instead. + public static func cancel(_ comment: Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation) throws -> Never +} +``` + +These functions behave similarly, and are distinguished by the level of the test +to which they apply: + +- `Test.cancel()` cancels the current test. + - If the current test is parameterized, it implicitly cancels all running and + pending test cases of said test. + - If the current test is a suite (only applicable during trait evaluation), it + recursively cancels all test suites and test functions within said suite. +- `Test.Case.cancel()` cancels the current test case. + - If the current test is parameterized, other test cases are unaffected. + - If the current test is _not_ parameterized, `Test.Case.cancel()` behaves the + same as `Test.cancel()`. + +Cancelling a test or test case implicitly cancels its associated task (and any +child tasks thereof) as if [`Task.cancel()`](https://developer.apple.com/documentation/swift/task/cancel()) +were called on that task. + +### Throwing semantics + +Unlike [`Task.cancel()`](https://developer.apple.com/documentation/swift/task/cancel()), +these functions always throw an error instead of returning. This simplifies +control flow when a test is cancelled; instead of having to write: + +```swift +if condition { + theTask.cancel() + return +} +``` + +A test author need only write: + +```swift +if condition { + try Test.cancel() +} +``` + +The errors these functions throw are of a type internal to Swift Testing that is +semantically similar to [`CancellationError`](https://developer.apple.com/documentation/swift/cancellationerror) +but carries additional information (namely the `comment` and `sourceLocation` +arguments to `cancel(_:sourceLocation:)`) that Swift Testing can present to the +user. When Swift Testing catches an error of this type[^cancellationErrorToo], +it does not record an issue for the current test or test case. + +[^cancellationErrorToo]: Swift Testing also catches errors of type + [`CancellationError`](https://developer.apple.com/documentation/swift/cancellationerror) + if the current task has been cancelled. If the current task has not been + cancelled, errors of this type are still recorded as issues. + +Suppressing these errors with `do`/`catch` or `try?` does not uncancel a test, +test case, or task, but can be useful if you have additional local work you need +to do before the test or test case ends. + +### Support for CancellationError + +Cancelling a test's or test case's associated task is equivalent to cancelling +the test or test case. Hence, if a test or test case throws an instance of +[`CancellationError`](https://developer.apple.com/documentation/swift/cancellationerror) +_and_ the current task has been cancelled, it is treated as if the test or test +case were cancelled. + +### Support for XCTSkip + +XCTest has an approximate equivalent to test cancellation: throwing an instance +of [`XCTSkip`](https://developer.apple.com/documentation/xctest/xctskip-swift.struct) +from the body of an XCTest test function causes that test function to be skipped +(equivalent to cancelling it). + +While we encourage developers to adopt `Test.cancel()` and `Test.Case.cancel()`, +we recognize the need for interoperability with XCTest. As such, Swift Testing +will recognize when a test or test case throws an instance of +[`XCTSkip`](https://developer.apple.com/documentation/xctest/xctskip-swift.struct) +and will treat it as cancelling the test or test case. + +An instance of [`XCTSkip`](https://developer.apple.com/documentation/xctest/xctskip-swift.struct) +must be caught by Swift Testing in order for it to cancel the current test. It +is not sufficient to create and discard an instance of this error type or to +catch one before Swift Testing can catch it. This behavior is consistent with +that of XCTest. + +> [!NOTE] +> This compatibility does **not** extend to Objective-C. In Objective-C, XCTest +> implements [`XCTSkip()`](https://developer.apple.com/documentation/xctest/xctskip-c.macro?language=objc) +> as a macro that throws an Objective-C exception. Exceptions are not supported +> in Swift, and Swift Testing does not attempt to catch these exceptions. + +### Interaction with recorded issues + +If you cancel a test or test case that has previously recorded an issue, that +issue is not overridden or nullified. In particular, if the test or test case +has already recorded an issue of severity **error** when you call +`cancel(_:sourceLocation:)`, the test or test case will still fail. + +### Example usage + +To cancel the current test case and let other test cases run: + +```swift +@Test(arguments: Species.all(in: .dinosauria)) +func `Are all dinosaurs extinct?`(_ species: Species) throws { + if species.in(.aves) { + try Test.Case.cancel("\(species) is birds!") + } + // ... +} +``` + +Or, to cancel all remaining test cases in the current test: + +```swift +@Test(arguments: Species.all(in: .dinosauria)) +func `Are all dinosaurs extinct?`(_ species: Species) throws { + if species.is(.godzilla) { + try Test.cancel("Forget about unit tests! Run for your life!") + } + // ... +} +``` + +## Source compatibility + +This change is additive only. + +## Integration with supporting tools + +The JSON event stream Swift Testing provides is updated to include two new event +kinds: + +```diff + ::= "runStarted" | "testStarted" | "testCaseStarted" | + "issueRecorded" | "testCaseEnded" | "testEnded" | "testSkipped" | +- "runEnded" | "valueAttached" ++ "runEnded" | "valueAttached" | "testCancelled" | "testCaseCancelled" +``` + +And new fields are added to event records to represent the comment and source +location passed to `cancel(_:sourceLocation:)`: + +```diff + ::= { + "kind": , + "instant": , ; when the event occurred + ["issue": ,] ; the recorded issue (if "kind" is "issueRecorded") + ["attachment": ,] ; the attachment (if kind is "valueAttached") + "messages": , + ["testID": ,] ++ ["comments": ,] ++ ["sourceLocation": ,] + } +``` + +These new fields are populated for the new event kinds as well as other event +kinds that can populate them. + +These new event kinds and fields will be included in the next revision of the +JSON schema (currently expected to be schema version `"6.3"`). + +## Future directions + +- Adding a corresponding `Test.checkCancellation()` function and/or + `Test.isCancelled` static property. These are beyond the scope of this + proposal, primarily because [`Task.isCancelled`](https://developer.apple.com/documentation/swift/task/iscancelled-swift.type.property) + and [`Task.checkCancellation()`](https://developer.apple.com/documentation/swift/task/checkcancellation()) + already work in a test. + +## Alternatives considered + +- Doing nothing. While we do want test authors to use [`.enabled(if:)`](https://developer.apple.com/documentation/testing/trait/enabled(if:_:sourcelocation:)) + _et al._ trait, we recognize it does not provide the full set of functionality + that test authors need. + +- Ignoring task cancellation or treating [`CancellationError`](https://developer.apple.com/documentation/swift/cancellationerror) + as a normal error even when the current task has been cancelled. It is not + possible for Swift Testing to outright ignore task cancellation, and a + [`CancellationError`](https://developer.apple.com/documentation/swift/cancellationerror) + instance thrown from [`Task.checkCancellation()`](https://developer.apple.com/documentation/swift/task/checkcancellation()) + is not really a test issue but rather a manifestation of control flow. + +- Using the [`XCTSkip`](https://developer.apple.com/documentation/xctest/xctskip-swift.struct) + type from XCTest. Interoperation with XCTest is an area of exploration for us, + but core functionality of Swift Testing needs to be usable without also + importing XCTest. + +- Spelling the functions `static func cancel(_:sourceLocation:) -> some Error` + and requiring it be called as `throw Test.cancel()`. This is closer to how + the [`XCTSkip`](https://developer.apple.com/documentation/xctest/xctskip-swift.struct) + type is used in XCTest. We have received indirect feedback about [`XCTSkip`](https://developer.apple.com/documentation/xctest/xctskip-swift.struct) + indicating its usage is unclear, and sometimes need to help developers who + have written: + + ```swift + if x { + XCTSkip() + } + ``` + + And don't understand why it has failed to stop the test. More broadly, it is + not common practice in Swift for a function to return an error that the caller + is then responsible for throwing. + +- Providing additional `cancel(if:)` and `cancel(unless:)` functions. In + Objective-C, XCTest provides the [`XCTSkipIf()`](https://developer.apple.com/documentation/xctest/xctskipif) + and [`XCTSkipUnless()`](https://developer.apple.com/documentation/xctest/xctskipunless) + macros which capture their condition arguments as strings for display to the + test author. This functionality is not available in Swift, but XCTest's Swift + interface provides equivalent throwing functions as conveniences. We could + provide these functions (without any sort of string-capturing ability) too, + but they provide little additional clarity above an `if` or `guard` statement. + +- Implementing cancellation using Swift macros so we can capture an `if` or + `unless` argument as a string. A macro for this feature is probably the wrong + tradeoff between compile-time magic and technical debt. + +- Relying solely on [`Task.cancel()`](https://developer.apple.com/documentation/swift/task/cancel()). + Ignoring the interplay between tests and test cases, this approach is + difficult for test authors to use because the current [`Task`](https://developer.apple.com/documentation/swift/task) + instance isn't visible _within_ that task. Instead, a test author would need + to use [`withUnsafeCurrentTask(body:)`](https://developer.apple.com/documentation/swift/withunsafecurrenttask(body:)-6gvhl) + to get a temporary reference to the task and cancel _that_ value. We would + also not have the ability to include a comment and source location information + in the test's console output or an IDE's test result interface. + + With that said, [`UnsafeCurrentTask.cancel()`](https://developer.apple.com/documentation/swift/unsafecurrenttask/cancel()) + _does_ cancel the test or test case associated with the current task. + +## Acknowledgments + +Thanks team! + +Thanks Arthur! That's right, dinosaurs _do_ say "roar!" + +And thanks to [@allevato](https://github.com/allevato) for nerd-sniping me into +writing this proposal. From 5bc18da6ed7886553453dfe26076d9e394b281d4 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 5 Sep 2025 14:31:41 +0100 Subject: [PATCH 4466/4563] Allocate SE-0490 --- ...ries.md => 0490-environment-constrained-shared-libraries.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename proposals/{NNNN-environment-constrained-shared-libraries.md => 0490-environment-constrained-shared-libraries.md} (99%) diff --git a/proposals/NNNN-environment-constrained-shared-libraries.md b/proposals/0490-environment-constrained-shared-libraries.md similarity index 99% rename from proposals/NNNN-environment-constrained-shared-libraries.md rename to proposals/0490-environment-constrained-shared-libraries.md index 9a170b0058..c4e97a81a4 100644 --- a/proposals/NNNN-environment-constrained-shared-libraries.md +++ b/proposals/0490-environment-constrained-shared-libraries.md @@ -1,6 +1,6 @@ # Environment Constrained Shared Libraries -* Proposal: [SE-NNNN](NNNN-environment-constrained-shared-libraries.md) +* Proposal: [SE-0490](0490-environment-constrained-shared-libraries.md) * Authors: [tayloraswift](https://github.com/tayloraswift) * Review Manager: TBD * Implementation: [swiftlang/swift-package-manager#8249](https://github.com/swiftlang/swift-package-manager/pull/8249) From e9dfa0ff5eb9cfc1176740d677e6763a830cf7d8 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 5 Sep 2025 14:54:03 +0100 Subject: [PATCH 4467/4563] Update status and links for SE-0490 --- proposals/0490-environment-constrained-shared-libraries.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/proposals/0490-environment-constrained-shared-libraries.md b/proposals/0490-environment-constrained-shared-libraries.md index c4e97a81a4..d4d9447eb1 100644 --- a/proposals/0490-environment-constrained-shared-libraries.md +++ b/proposals/0490-environment-constrained-shared-libraries.md @@ -2,10 +2,12 @@ * Proposal: [SE-0490](0490-environment-constrained-shared-libraries.md) * Authors: [tayloraswift](https://github.com/tayloraswift) -* Review Manager: TBD +* Review Manager: [Alastair Houghton](https://github.com/al45tair) +* Status: **Active Review (Sep 5 - Sep 18, 2025)** * Implementation: [swiftlang/swift-package-manager#8249](https://github.com/swiftlang/swift-package-manager/pull/8249) -* Documentation: [How to use Environment-Constrained Shared Libraries](https://github.com/swiftlang/swift-package-manager/blob/b586467575580f2365e8f5a29c949379724db795/Documentation/ECSLs.md) +* Documentation: [How to use Environment-Constrained Shared Libraries](https://github.com/swiftlang/swift-package-manager/blob/1eaf59d2facc74c88574f38395aa49983b2badcc/Documentation/ECSLs.md) * Bugs: [SR-5714](https://github.com/swiftlang/swift-package-manager/issues/5714) +* Review: ([pitch](https://forums.swift.org/t/pitch-replaceable-library-plugins/77605)) ([review](https://forums.swift.org/t/se-0490-environment-constrained-shared-libraries/81975)) ## Introduction From 8149f46f4f56bcc9b8a9461b39c4d1be2fdd89b6 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Tue, 2 Sep 2025 14:28:43 -0700 Subject: [PATCH 4468/4563] Inline always proposal --- proposals/NNNN-inline-always.md | 348 ++++++++++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 proposals/NNNN-inline-always.md diff --git a/proposals/NNNN-inline-always.md b/proposals/NNNN-inline-always.md new file mode 100644 index 0000000000..3b2cf8dd0f --- /dev/null +++ b/proposals/NNNN-inline-always.md @@ -0,0 +1,348 @@ +# `@inline(always)` attribute + +* Proposal: [SE-NNNN](NNNN-inline-always.md) +* Authors: [Arnold Schwaighofer](https://github.com/aschwaighofer) +* Implementation: [swiftlang/swift#84178](https://github.com/swiftlang/swift/pull/84178) + +## Introduction + +The Swift compiler performs an optimization that expands the body of a function +into the caller called inlining. Inlining exposes the code in the callee to the +code in the caller. After inlining, the Swift compiler has more context to +optimize the code across caller and callee leading to better optimization in +many cases. Inlining can increase code size. To avoid unnecessary code size +increases, the Swift compiler uses heuristics (properties of the code) to +determine whether to perform inlining. Sometimes these heuristics tell the +compiler not to inline a function even though it would be beneficial to do so. +The proposed attribute `@inline(always)` instructs the compiler to always inline +the annotated function into the caller giving the author explicit control over +the optimization. + +## Motivation + +Inlining a function referenced by a function call enables the optimizer to see +across function call boundaries. This can enable further optimization. The +decision whether to inline a function is driven by compiler heuristics that +depend on the shape of the code and can vary between compiler versions. + +In the following example the decision to inline might depend on the number of +instructions in `callee` and on detecting that the call to callee is frequently +executed because it is surrounded by a loop. Inlining this case would be +beneficial because the compiler is able to eliminate a store to a stack slot in +the `caller` after inlining the `callee` because the function's `inout` calling +convention ABI that requires an address no longer applies and further +optimizations are enabled by the caller's function's context. + +```swift +func callee(_ result: inout SomeValue, _ cond: Bool) { + result = SomeValue() + if cond { + // many lines of code ... + } +} + +func caller() { + var cond: Bool = false + var x : SomeValue = SomeValue() + for i in 0 ..< 1 { + callee(&x, cond) + } +} + +func callerAfterInlining(_ cond: Bool { + var x : SomeValue = SomeValue() + var cond: Bool = false + for i in 0 ..< 1 { + // Inlined `callee()`: + // Can keep `SomeValue()` in registers because no longer + // passed as an `inout` argument. + x = SomeValue() // Can hoist `x` out of the loop and perform constant + // propagation. + if cond { // Can remove the code under the conditional because it is + // known not to execute. + // many lines of code ... + } + } +} +``` + +The heuristic might fail to detect that code is frequently executed (surrounding +loop structures might be several calls up in the call chain) or the number of +instructions in the callee might be to large for the heuristic to decide that +inlining is beneficial. +Heuristics might change between compiler versions either directly or indirectly +because some properties of the internal representation of the optimized code +changes. +To give code authors reliable control over the inlining process we propose to +add an `@inline(always)` function attribute. + +This optimization control should instruct the compiler to inline the referenced +function or emit an error when it is not possible to do so. + +```swift +@inline(always) +func callee(_ result: inout SomeValue, _ cond: Bool) { + result = SomeValue() + if cond { + // many lines of code ... + } +} +``` + +## Proposed solution + +We desire for the attribute to function as an optimization control. That means +that the proposed `@inline(always)` attribute should emit an error if inlining +cannot be guaranteed in all optimization modes. The value of the function at a +call site can might determined dynamically at runtime. In such cases the +compiler cannot determine a call site which function is applied without doing +global analysis. In these cases we don't guarantee inlining even if the dynamic +value of the applied function was annotated with `@inline(always)`. +We only guarantee inlining if the annotated function is directly referenced and +not derived by some function value computation such as method lookup or function +value (closure) formation and diagnose errors if this guarantee cannot be +upheld. + +A sufficiently clever optimizer might be able to derive the dynamic value at the +call site, in such cases the optimizer shall respect the optimization control +and perform inlining. + +```swift +protocol SomeProtocol { + func mightBeOverriden() +} + +class C : SomeProtocol{ + @inline(always) + func mightBeOverriden() { + } +} + +@inline(always) +func callee() { +} + +func applyFunctionValues(_ funValue: () -> (), c: C, p: SomeProtocol) { + funValue() // function value, not guaranteed + c.mightBeOverriden() // dynamic method lookup, not guaranteed + p.mightBeOverriden() // dynamic method lookup, not guaranteed + callee() // directly referenced, guaranteed +} + +func caller() { + applyFunctionValue(callee, C()) +} + +caller() +``` + +Code authors shall be able to rely on that if a function is marked with +`@inline(always)` and directly referenced from any context (within or outside of +the defining module) that the function can be inlined or an error is emitted. + + +## Detailed design + +We want to diagnose an error if a directly referenced function is marked with +`@inline(always)` and cannot be inlined. What are the cases where this might not +be possible? + +### Interaction with `@inlinable` + +Function bodies of functions referenceable outside of the defining module are +only available to the outside module if the definition is marked `@inlinable`. + +Therefore, a function marked with `@inline(always)` must be marked `@inlinable` +if it has `open`, `public`, or `package` level access. + +```swift +@inline(always) // error: a public function marked @inline(always) must be marked @inlinable +public func callee() { +} +``` + +### Interaction with `@usableFromInline` + +A `public` `@inlinable` function can reference a function with `internal` access +if it is either `@inlinable` (see above) or `@usableFromInline`. `@usableFromInline` +ensures that there is a public entry point to the `internal` level function but +does not ensure that the body of the function is available to external +modules. Therefore, it is an error to combine `@inline(always)` with a +`@usableFromInline` function as we cannot guaranteed that the function can +always be inlined. + +```swift +@inline(always) // error: an internal function marked with `@inline(always)` and + `@usableFromInline` could be referenced from an + `@inlinable` function and must be marked inlinable +@usableFromInline +internal func callee() {} + +@inlinable +public func caller() { + callee() // could not inline callee into external module +} +``` + +### Module internal access levels + +It is okay to mark `internal`, `private` and `fileprivate` function declarations +with `@inline(always)` in cases other than the ones mention above without the +`@inlinable` attribute as they can only be referenced from within the module. + + +```swift +public func caller() { + callee() +} + +@inline(always) // okay because caller would force either `@inlinable` or + // `@usableFromInline` if it was marked @inlinable itself +internal func callee() { +} + + +@inline(always) // okay can only referenced from within the module +private func callee2() { +} +``` + +#### Infinite recursion during inlining + +We will diagnose if inlining cannot happen due to calls within a +[strongly connected component](https://en.wikipedia.org/wiki/Strongly_connected_component) +marked with `@inline(always)` as errors. + +```swift +@inline(always) +func callee() { + ... + if cond2 { + caller() + } +} + +@inline(always) +func caller() { + ... + if cond { + callee() + } +} +``` + +### Dynamic function values + +As outlined earlier the attribute does not guarantee inlining or diagnose the +failure to inline when the function value is dynamic at a call site: a function +value is applied, or the function value is obtained via class method lookup or +protocol lookup. + +```swift +@inline(always) +func callee() {} +func useFunctionValue() { + let f = callee + ... + f() // function value use, not guaranteed to be inlined +} + +class SomeClass : SomeProto{ + @inline(always) + func nonFinalMethod() {} + + @inline(always) + func method() {} +} + +protocol SomeProto { + func method() +} + + +func dynamicMethodLookup() { + let c = SomeClass() + ... + c.nonFinalMethod() // method lookup, not guaranteed to be inlined + + let p: SomeProto = SomeClass() + p.method() // method lookup, not guaranteed to be inlined +} + +class A { + func finalInSub() {} + final func finalMethod() {} +} +class B : A { + overrided final func finalInSub() {} +} + +func noMethodLookup() { + let a = A() + a.finalMethod() // no method lookup, guaranteed to be inlined + + let b = B() + b.finalInSubClass() // no method lookup, guaranteed to be inlined +} +``` + + +## Source compatibility + +This proposal is additive. Existing code has not used the attribute. It has no +impact on existing code. Existing references to functions in libraries that are +now marked with `@inline(always)` will continue to compile successfully with the +added effect that functions will get inlined (that could have happened with +changes to inlining heuristic). + +## ABI compatibility + +The addition of the attribute has no effect on ABI compatibility. + +## Implications on adoption + +This feature can be freely adopted and un-adopted in source +code with no deployment constraints and without affecting source or ABI +compatibility. + +## Future directions + +`@inline(always)` can be too restrictive in cases where inlining is only +required within a module. For such cases we can introduce an `@inline(module)` +attribute in the future. + + +```swift +@inlinable +public caller() { + if coldPath { + callee() + } +} + +public otherCaller() { + if hotPath { + callee() + } +} + +@inline(module) +@usableFromInline +internal func callee() { +} +``` + +## Alternatives considered + +We could treat `@inline(always)` as an optimization hint that does not need to +be enforced or applied at all optimization levels similar to how the existing +`@inline(__always)` attribute functions and not emit errors if it cannot be +guaranteed to be uphold when the function is directly referenced. +This would deliver less predictable optimization behavior in cases where authors +overlooked requirements for inlining to happen such as not marking a public +function as `@inlinable`. + + +## Acknowledgments + +TODO: .... From 500310ed0d59e46b6fa8e12ea19c7a7d774a2c51 Mon Sep 17 00:00:00 2001 From: Rick van Voorden Date: Wed, 10 Sep 2025 22:28:32 -0700 Subject: [PATCH 4469/4563] add is identical methods --- proposals/NNNN-add-is-identical-methods.md | 92 ++++++++++++++++++++-- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/proposals/NNNN-add-is-identical-methods.md b/proposals/NNNN-add-is-identical-methods.md index aa6c13db53..4eb0536cc6 100644 --- a/proposals/NNNN-add-is-identical-methods.md +++ b/proposals/NNNN-add-is-identical-methods.md @@ -393,7 +393,13 @@ When we build and run our SwiftUI app we confirm that we are not computing new ` We propose adding `isIdentical` methods to the following concrete types from Standard Library: * `String` +* `String.UnicodeScalarView` +* `String.UTF16View` +* `String.UTF8View` * `Substring` +* `Substring.UnicodeScalarView` +* `Substring.UTF16View` +* `Substring.UTF8View` * `Array` * `ArraySlice` * `ContiguousArray` @@ -419,6 +425,16 @@ extension String { /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) /// - `a.isIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isIdentical(b)` + /// + /// Values produced by copying the same value, with no intervening mutations, + /// will compare identical: + /// + /// ```swift + /// let d = c + /// print(c.isIdentical(to: d)) + /// // Prints true + /// ``` /// /// Comparing strings this way includes comparing (normally) hidden /// implementation details such as the memory location of any underlying @@ -431,6 +447,11 @@ extension String { } ``` +The following types will adopt the same semantic guarantees as `String`: +* `String.UnicodeScalarView` +* `String.UTF16View` +* `String.UTF8View` + ### `Substring` ```swift @@ -448,6 +469,16 @@ extension Substring { /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) /// - `a.isIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isIdentical(b)` + /// + /// Values produced by copying the same value, with no intervening mutations, + /// will compare identical: + /// + /// ```swift + /// let d = c + /// print(c.isIdentical(to: d)) + /// // Prints true + /// ``` /// /// Comparing substrings this way includes comparing (normally) hidden /// implementation details such as the memory location of any underlying @@ -460,6 +491,11 @@ extension Substring { } ``` +The following types will adopt the same semantic guarantees as `Substring`: +* `Substring.UnicodeScalarView` +* `Substring.UTF16View` +* `Substring.UTF8View` + ### `Array` ```swift @@ -477,6 +513,16 @@ extension Array { /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) /// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isIdentical(b)` + /// + /// Values produced by copying the same value, with no intervening mutations, + /// will compare identical: + /// + /// ```swift + /// let d = c + /// print(c.isIdentical(to: d)) + /// // Prints true + /// ``` /// /// Comparing arrays this way includes comparing (normally) hidden /// implementation details such as the memory location of any underlying @@ -506,6 +552,16 @@ extension ArraySlice { /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) /// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isIdentical(b)` + /// + /// Values produced by copying the same value, with no intervening mutations, + /// will compare identical: + /// + /// ```swift + /// let d = c + /// print(c.isIdentical(to: d)) + /// // Prints true + /// ``` /// /// Comparing arrays this way includes comparing (normally) hidden /// implementation details such as the memory location of any underlying @@ -535,6 +591,16 @@ extension ContiguousArray { /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) /// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isIdentical(b)` + /// + /// Values produced by copying the same value, with no intervening mutations, + /// will compare identical: + /// + /// ```swift + /// let d = c + /// print(c.isIdentical(to: d)) + /// // Prints true + /// ``` /// /// Comparing arrays this way includes comparing (normally) hidden /// implementation details such as the memory location of any underlying @@ -564,6 +630,16 @@ extension Dictionary { /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) /// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isIdentical(b)` + /// + /// Values produced by copying the same value, with no intervening mutations, + /// will compare identical: + /// + /// ```swift + /// let d = c + /// print(c.isIdentical(to: d)) + /// // Prints true + /// ``` /// /// Comparing dictionaries this way includes comparing (normally) hidden /// implementation details such as the memory location of any underlying @@ -593,6 +669,16 @@ extension Set { /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) /// - `a.isIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isIdentical(b)` + /// + /// Values produced by copying the same value, with no intervening mutations, + /// will compare identical: + /// + /// ```swift + /// let d = c + /// print(c.isIdentical(to: d)) + /// // Prints true + /// ``` /// /// Comparing sets this way includes comparing (normally) hidden /// implementation details such as the memory location of any underlying set @@ -622,12 +708,6 @@ Any Standard Library types that are copy-on-write values could be good candidate * `KeyValuePairs` * `StaticBigInt` * `StaticString` -* `String.UnicodeScalarView` -* `String.UTF16View` -* `String.UTF8View` -* `Substring.UnicodeScalarView` -* `Substring.UTF16View` -* `Substring.UTF8View` * `UTF8Span` This proposal focuses on what we see as the most high-impact types to support from Standard Library. This proposal *is not* meant to discourage adding `isIdentical(to:)` on any of these types at some point in the future. A follow-up “second-round” proposal could focus on these remaining types. From 94defbe18bb4e3e356561a8fe151a407e0c676bd Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Sat, 13 Sep 2025 16:23:11 -0700 Subject: [PATCH 4470/4563] Add module selectors proposal (#2914) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add module selectors proposal * Minor formatting and wording improvements * Make changes based on parsing code review * Clarify and expand discussions in pitch Particularly future directions and alternatives considered. * Flip-flop on restricting newlines after `::` If reviewers don’t like it, we can revisit the question. * Late edits to module selector proposal * Describe keyword behavior more specifically * Allow module selector in `@available(renamed:)` * Use different Markdown syntax for links * Module selectors is SE-0491 --------- Co-authored-by: Freddy Kellison-Linn From ef78ed4e7628f1991758b360dcf5a2db3a69abbf Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Sat, 13 Sep 2025 19:25:37 -0400 Subject: [PATCH 4471/4563] Add SE-0491 (#2954) --- proposals/0491-module-selectors.md | 754 +++++++++++++++++++++++++++++ 1 file changed, 754 insertions(+) create mode 100644 proposals/0491-module-selectors.md diff --git a/proposals/0491-module-selectors.md b/proposals/0491-module-selectors.md new file mode 100644 index 0000000000..936c00311c --- /dev/null +++ b/proposals/0491-module-selectors.md @@ -0,0 +1,754 @@ +# Module selectors for name disambiguation + +* Proposal: [SE-0491](0491-module-selectors.md) +* Authors: [Becca Royal-Gordon](https://github.com/beccadax) +* Review Manager: [Freddy Kellison-Linn](https) +* Status: **Awaiting review** +* Bug: [swiftlang/swift#53580](https://github.com/swiftlang/swift/issues/53580) (SR-11183) +* Implementation: [swiftlang/swift#34556](https://github.com/swiftlang/swift/pull/34556) +* Review: ([pitch](https://forums.swift.org/t/pitch-module-selectors/80835)) + +Previously pitched in: + +* [Pitch: Fully qualified name syntax](https://forums.swift.org/t/pitch-fully-qualified-name-syntax/28482) + +## Introduction + +We propose that Swift's grammar be extended so that, wherever an identifier +is written in source code to reference a declaration, it can be prefixed by +`ModuleName::` to disambiguate which module the declaration is expected to +come from. This syntax will provide a way to resolve several types of name +ambiguities and conflicts. + +## Motivation + +Swift's name lookup rules promote its goal of allowing code to be written in a +very clean, readable style. However, in some circumstances it can be very +difficult to unambiguously reference the declaration you want. + +### Background + +When Swift looks up a name in your source code to find the declaration it +refers to, that lookup can be either *qualified* or *unqualified*. Qualified +lookups are restricted to looking inside a certain declaration, while +unqualified lookups search more broadly. For example, in a chain of names such +as: + +```swift +mission().booster().launch() +``` + +`booster()` can only refer to members of whatever type is returned by +`mission()`, and `launch()` can only refer to members of whatever type is +returned by `booster()`, so Swift will find them using a qualified lookup. +`mission()`, on the other hand, does not have to be a member of some specific +type, so Swift will find that declaration using an unqualified lookup. + +> **Note**: Although the examples given here mostly concern uses in +> expressions, qualified and unqualified lookups are also used for names in +> type syntax, such as `Mission.Booster.Launch`. The exact lookup rules are +> slightly different but the principles are the same. + +Both kinds of lookups are slightly sensitive to context in that, since the +acceptance of [SE-0444 Member Import Visibility][SE-0444], +they are both limited to declarations imported in the current source file; +however, unqualified lookups take *much* more than just that into account. They +search through any enclosing scopes to find the "closest" use of that name. For +example, in code like: + +```swift +import RocketEngine +import IonThruster + +extension Mission { + struct Booster { + func launch(_ crew: Int) { + let attempt = 1 + ignite() + } + } +} +``` + +Swift will look for `ignite` in the following places: + +1. The local declarations inside `launch(_:)` +2. The parameters to `launch(_:)` +3. Instance members and generic parameters of the enclosing type `Booster` + (including its extensions, superclasses, conformances, etc.) +4. Static members and generic parameters of the enclosing type `Mission` +5. Top-level declarations in this module +6. Top-level declarations in other imported modules +7. The names of imported modules + +These rules are a little complicated when written out like this, but their +effect is pretty simple: Swift finds whichever `ignite` is in the "closest" +scope to the use site. If both `Booster` and `Mission` have an `ignite`, for +example, Swift will use the one in `Booster` and ignore the one in `Mission`. + +Of particular note is the last place Swift looks: the names of imported +modules. This is intended to help with situations where two modules have +declarations with the same name. For example, if both `RocketEngine` and +`IonThruster` declare an `ignite()`, `RocketEngine.ignite()` will find `ignite` +using a qualified lookup inside the module `RocketEngine`, filtering out the +one in `IonThruster`. This works in simple cases, but it breaks down in a +number of complicated ones. + +### Unqualified lookups are prone to shadowing + +Swift does not prevent declarations in different scopes from having the same +name. For example, there's nothing preventing you from having both a top-level +type and a nested type with the same name: + +```swift +struct Scrubber { ... } + +struct LifeSupport { + struct Scrubber { ... } +} +``` + +This means that the same name can have different meanings in different places: + +```swift +// This returns the top-level `Scrubber`: +func makeScrubber() -> Scrubber { ... } + +extension LifeSupport { + // This returns `LifeSupport.Scrubber`: + func makeScrubber() -> Scrubber { ... } +} +``` + +Specifically, we say that within the extension, `LifeSupport.Scrubber` +*shadows* the top-level `Scrubber`. + +This poses certain challenges—especially for mechanically-generated code, such +as module interface files—but it's usually not completely insurmountable +because you can qualify a top-level declaration with its module name. However, +it becomes a problem if *the module name itself* is shadowed by a type with the +same name: + +```swift +// Module RocketEngine +public struct RocketEngine { ... } +public struct Fuel { ... } + +// Another module +import RocketEngine + +_ = RocketEngine.Fuel() // Oops, this is looking for a nested type in the + // struct RocketEngine.RocketEngine! +``` + +In this situation, we can no longer qualify top-level declarations with module +names. That makes code generation *really* complicated, because there is no +syntax that works reliably—qualifying will help with some failures but cause +others. + +That may sound like a farfetched edge case, but it's surprisingly common for a +module to contain a type with the same name. For instance, the `XCTest` module +includes an `XCTest` class, which is a base class for `XCTestCase` and +`XCTestSuite`. To avoid this kind of trouble, developers must be careful to +give modules different names from the types inside them—the `Observation` +module, for example, might have been called `Observable` if it didn't have a +type with that name. + +### Qualified lookups can be unresolvably ambiguous + +Extensions create the possibility that a type may have two members with the +same name and similar or even outright conflicting overload signatures, +distinguished only by being in different modules. This is not a problem for +Swift's ABI because the mangled name of an extension member includes the +module it was declared in; however, there is no way to add a module name to +an already-qualified non-top-level lookup, so there's no way to express this +distinction in the surface language. Developers' only option may be to fiddle +with their imports in an attempt to make sure the desired member is the only +one that's visible. + +### Macros don't support module qualification + +Macros cannot have members--the grammar of a macro expansion allows only a +single identifier, and any subsequent `.` is taken to be a member lookup on +the expansion--so there is currently no way to qualify a macro expansion with +a module name. This limitation was discussed during the [second review of SE-0382][SE-0382-review-2] +and the author suggested the only viable solution was to add a new, +grammatically-distinguishable syntax for module qualification. + +### These problems afflict module interfaces, but aren't unique to them + +These issues show up most often in module interfaces because the compiler +needs to generate syntax that reliably resolves to a specific declaration, but +the rules' sensitivity to context and module contents (which might change over +time!) makes that very difficult. In practice, the compiler does not attempt to +fully account for shadowing and name conflicts--by default it qualifies names +as fully as the language allows (which works about 95% of the time) and offers +a number of (undocumented) workaround flags to adjust that which are added by a +maintainer when they discover that their module is in the remaining 5%. These +flags aren't enabled automatically, though, and they don't affect the module +interfaces of downstream modules which need to reference affected declarations. +In short, the situation is a mess. + +It's important to keep in mind, though, that this doesn't *just* affect module +interfaces and generated code. Code written by humans can also run into these +issues; it's just that a person will notice the build error and fiddle with +their code until they get something that works. It therefore makes sense to +introduce a new syntax that can be used by both machines and humans. + +### Separate modules make this uniquely severe + +While problematic conflicts can sometimes occur between two declarations in a +single module, the authors believe that per-module disambiguation is the right +approach because shadowing within a module is much easier to detect and +resolve. The developer will generally notice shadowing problems when they build +or test their code, and since they control both the declaration site and the +use site, they have options to resolve any problems that are not otherwise +available (like renaming declarations or tweaking their overload signatures). +The compiler also detects and prevents outright conflicts within a specific +module, such as two extensions declaring the exact same member, which it would +allow if the declarations were in different modules. + +## Proposed solution + +We propose adding *module selectors* to the language. A module selector is +spelled `::` and can be placed before an identifier to indicate +which module it is expected to come from: + +```swift +_ = RocketEngine::Fuel() // Picks up the `Fuel` in `RocketEngine`, bypassing + // any other `Fuel`s that might be in scope +``` + +On an unqualified lookup, a module selector also indicates that lookup should +start at the top level, skipping over the declarations in contextually-visible +scopes: + +```swift +// In module NASA + +struct Scrubber { ... } + +struct LifeSupport { + struct Scrubber { ... } +} + +extension LifeSupport { + // This returns the top-level `Scrubber` + func makeMissionScrubber() -> NASA::Scrubber { ... } +} +``` + +Module selectors may also be placed on qualified lookups to indicate which +module an extension member should belong to: + +```swift +// In module IonThruster +extension Spacecraft { + public struct Engine { ... } +} + +// In module RocketEngine +extension Spacecraft { + public struct Engine { ... } +} + +// In module NASA +import IonThruster +import RocketEngine + +func makeIonThruster() -> Spacecraft.IonThruster::Engine { ... } +``` + +Module selectors are permitted at locations in the type and expression syntax +where a declaration from elsewhere is referenced by name. However, it is +invalid to use one on the name of a *new* declaration: + +```swift +struct NASA::Scrubber { // Invalid--new declarations are always in the current module + ... +} +``` + +We chose this syntax—module name plus `::` operator prefixing the name they +qualify—because `::` is unused in Swift (it can't even be a custom operator) +and because using `::` in this fashion is highly precedented in other +languages. (C++, PHP, Java, and Rust all use it to indicate that the name on +the right should be looked up inside the scope on the left; Ruby and Perl use +it *specifically* to look up declarations inside modules.) + +## Detailed design + +### Grammar and parsing + +A module selector has the following grammar: + +> *module-selector* → *identifier* `::` + +The following productions may now optionally include a module selector (changes are in bold): + +> *type-identifier* → ***module-selector?*** *type-name* *generic-argument-clause?* | ***module-selector?*** *type-name* *generic-argument-clause?* `.` *type-identifier* +> +> *primary-expression* → ***module-selector?*** *identifier* *generic-argument-clause?* +> +> *implicit-member-expression* → `.` ***module-selector?*** *identifier*
      +> *implicit-member-expression* → `.` ***module-selector?*** *identifier* `.` *postfix-expression* +> +> *macro-expansion-expression* → `#` ***module-selector?*** *identifier* *generic-argument-clause?* *function-call-argument-clause?* *trailing-closures?* +> +> *key-path-component* → ***module-selector?*** *identifier* *key-path-postfixes?* | *key-path-postfixes* +> +> *function-call-argument* → ***module-selector?*** *operator* | *identifier* `:` ***module-selector?*** *operator* +> +> *initializer-expression* → *postfix-expression* `.` ***module-selector?*** `init`
      +> *initializer-expression* → *postfix-expression* `.` ***module-selector?*** `init` `(` *argument-names* `)` +> +> *explicit-member-expression* → *postfix-expression* `.` ***module-selector?*** *identifier* *generic-argument-clause?*
      +> *explicit-member-expression* → *postfix-expression* `.` ***module-selector?*** *identifier* `(` *argument-names* `)` +> +> *attribute-name* → ***module-selector?*** *identifier* +> +> *enum-case-pattern* → *type-identifier?* `.` ***module-selector?*** *enum-case-name* *tuple-pattern?* + +Additionally, a new production allows a scoped `import` declaration to use a +module selector and identifier instead of an import path: + +> *import-declaration* → *attributes?* `import` *import-kind?* *import-path*
      +> ***import-declaration* → *attributes?* `import` *import-kind* *module-selector* *identifier*** + +Note that this new *import-declaration* production does not allow a submodule +to be specified. Use the old `.`-operator-based syntax for submodules. + +#### Token-level behavior + +The `::` operator may be separated from its *identifier* by any whitespace, +including newlines. However, the `::` operator must *not* be separated from the +token after it by a newline: + +```swift +NationalAeronauticsAndSpaceAdministration:: + RocketEngine // Invalid +NationalAeronauticsAndSpaceAdministration + ::RocketEngine // OK +``` + +> **Note**: This restriction aids in recovery when parsing incomplete code; +> the member-lookup `.` operator follows a similar rule. + +If the token after the `::` operator is a keyword, it will be treated as an +ordinary identifier unless it would have special meaning: + +```swift +print(default) // Invalid; 'default' is a keyword and needs backticks +print(NASA.default) // OK under SE-0071 +print(NASA::default) // OK under this proposal +``` + +Depending on context, the following keywords may still be treated as special in +expressions: + +* `deinit` +* `init` +* `subscript` + +> **Note**: This behavior is analogous to [SE-0071 Allow (most) keywords in member references][SE-0071]. + +Similarly, attributes that use a module selector will always be treated as +custom attributes, not built-in attributes. (Put another way, built-in +attributes do not belong to *any* module—not even `Swift`.) Like all custom +attributes, any arguments must be valid expressions. + +```swift +@Swift::available(macOS 15.0.1, *) // Invalid; not parsed as the built-in `@available` +class X {} +``` + +#### Patterns + +Module selectors are allowed in *enum-case-pattern* and in *type* and +*expression* productions nested inside patterns. However, *identifier-pattern* +is unmodified and does *not* permit a module selector, even in shorthand +syntaxes designed to declare a shadow of an existing variable. If a module +selector is needed, you must use an explicit initializer expression. + +```swift +if let NASA::rocket { ... } // Invalid +if let rocket = NASA::rocket { ... } // OK + +Task { [NASA::rocket] in ... } // Invalid +Task { [rocket = NASA::rocket] in ... } // OK +``` + +#### Operator and precedence group declarations + +The *precedence-group-name* production is unmodified and does not permit +a module selector. Precedence group names exist in a separate namespace from +other identifiers and no need for this feature has been demonstrated. + +#### Parsed declaration names + +A parsed declaration name, such as the name in an `@available(renamed:)` +argument, may use module selectors on the declaration's base name and context +names. + +```swift +@available(*, deprecated, renamed: "NASA::launch(_:from:)") // OK +public func launch(_ mission: Mission) { + launch(mission, from: LaunchPad.default) +} +``` + +Module selectors are not valid on base names in clang `swift_name` and +`swift_async_name` attributes, since these specify the name of the current +declaration, rather than referencing a different declaration. + +> **Note**: Clang Importer currently cannot apply import-as-member `swift_name` +> or `swift_async_name` attributes that name a context in a different module, +> but if this limitation is ever lifted, module selectors ought to be supported +> on context names in these clang attributes. + +#### Syntaxes reserved for future directions + +It is never valid to write two module selectors in a row; if you want to access +a declaration which belongs to a clang submodule, you should just write the +top-level module name in the module selector. + +It is never valid to write a keyword, operator, or `_` in place of a module +name; if a module's name would be mistaken for one of these, it must be +wrapped in backticks to form an identifier. + +### Effects on lookup + +When a reference to a declaration is prefixed by a module selector, only +declarations declared in, or re-exported by, the indicated module will be +considered as candidates. All other declarations will be filtered out. + +For example, in the following macOS code: + +```swift +import Foundation + +class NSString {} + +func fn(string: Foundation::NSString) {} +``` + +`string` will be of type `Foundation.NSString`, rather than the `NSString` +class declared in the same file. Because the AppKit module +re-exports Foundation, this example would also behave the same way: + +```swift +import AppKit + +class NSString {} + +func fn(string: AppKit::NSString) {} +``` + +> **Note**: Allowing re-exports ensures that "hoisting" a type from its +> original module up to another module it imports is not a source-breaking +> change. It also helps with situations where developers don't realize where a +> given type is declared; for instance, many developers believe `NSObject` is +> declared in `Foundation`, not `ObjectiveC`. + +Additionally, when a reference to a declaration prefixed by a module selector +is used for an unqualified lookup, the lookup will begin at the module-level +scope, skipping any intervening enclosing scopes. That means a top-level +declaration will not be shadowed by local variables, parameters, generic +parameters, or members of enclosing types: + +```swift +// In module MyModule + +class Shadowed { + struct Shadowed { + let Shadowed = 42 + func Shadowed(Shadowed: () -> Void) { + let Shadowed = "str" + let x = MyModule::Shadowed() // refers to top-level `class Shadowed` + } + } +} +``` + +A module selector can only rule out declarations that might otherwise have been +chosen instead of the desired declaration; it cannot access a declaration which +some other language feature has ruled out. For example, if a declaration is +inaccessible because of access control or hasn't been imported into the current +source file, a module selector will not allow it to be accessed. + +## Source compatibility + +This change is purely additive; it only affects the behavior of code which uses +the new `::` token. In the current language, this sequence can only appear +in valid Swift code in the selector of an `@objc` attribute, and the parser +has been modified to split the token when it is encountered there. + +## ABI compatibility + +This change does not affect the ABI of existing code. The Swift compiler has +always resolved declarations to a specific module and then embedded that +information in the ABI's symbol names; this proposal gives developers new ways +to influence those resolution decisions but doesn't expand the ABI in any way. + +## Implications on adoption + +Older compilers will not be able to parse source code which uses module +selectors. This means package authors may need to increase their tools version +if they want to use the feature, and authors of inlinable code may need to +weigh backwards compatibility concerns. + +Similarly, when a newer compiler emits module selectors into its module +interfaces, older compilers won't be able to understand those files. This isn't +a dealbreaker since Swift does not guarantee backwards compatibility for module +interfaces, but handling it will require careful staging and there may be a +period where ABI-stable module authors must opt in to emitting module +interfaces that use the feature. + +## Future directions + +### Special syntax for the current module + +We could allow a special token, or no token, to be used in place of the module +name to force a lookup to start at the top level, but not restrict it to a +specific module. Candidates include: + +```swift +Self::ignite() +_::ignite() +*::ignite() +::ignite() +``` + +These syntaxes have all been intentionally kept invalid (a module named `Self`, +for instance, would have to be wrapped in backticks: `` `Self`::someName ``), +so one of them can be added later if there's demand for it. + +### Disambiguation for subscripts + +There is currently no way to add a module selector to a use of a subscript. We +could add support for a syntax like: + +```swift +myArray.Swift::[myIndex] +``` + +### Disambiguation for conformances + +Retroactive conformances have a similar problem to extension members—the ABI +distinguishes between otherwise identical conformances in different modules, +but the surface syntax has no way to resolve any ambiguity—so a feature which +addressed them might be nice. However, there is no visible syntax associated +with use of a conformance that can be qualified with a module selector, so it's +difficult to address as part of this proposal. + +It's worth keeping in mind that [SE-0364's introduction of `@retroactive`][SE-0364] +reflects a judgment that retroactive conformances should be used with care. The +absence of such a feature is one of the complications `@retroactive` is meant +to flag. + +### Support selecting conflicting protocol requirements + +Suppose that a single type conforms to two protocols with conflicting protocol +requirements: + +```swift +protocol Employable { + /// Terminate `self`'s employment. + func fire() +} + +protocol Combustible { + /// Immolate `self`. + func fire() +} + +struct Technician: Employable, Combustible { ... } +``` + +It'd be very useful to be able to unambiguously specify which protocol's +requirement you're trying to call: + +```swift +if myTechnician.isGoofingOff { + myTechnician.Employable::fire() +} +if myTechnician.isTooCloseToTheLaunch { + myTechnician.Combustible::fire() +} +``` + +However, allowing a protocol name—rather than a module name—to be written +before the `::` token re-introduces the same ambiguity this proposal seeks +to solve because a protocol name could accidentally shadow a module name. +We'll probably need a different feature with a distinct syntax to resolve +this use case—perhaps something like: + +```swift +if myTechnician.isGoofingOff { + (myTechnician as some Employable).fire() +} +``` + +### Support selecting default implementations + +Similarly, it would be useful to be able to specify that you want to call a +default implementation of a protocol requirement even if the conformance +provides another witness. (This could be used similarly to how `super` is used +in overrides.) However, this runs into similar problems with reintroducing +ambiguity, and it also just doesn't quite fit the shape of the syntax (there's +no name to uniquely identify the default implementation you want). Once again, +this probably requires a different feature with a distinct syntax—perhaps +something a little more like how `super` works. + +## Alternatives considered + +### Change lookup rules in module interfaces + +Some of the problems with module interfaces could be resolved by changing the +rules for qualified lookup *within module interface files specifically*. For +instance, we could decide that in a module interface file, unqualified lookups +can only find module names, and the compiler must always qualify every name +with a module name. + +This would probably be a viable solution with enough effort, but it has a +number of challenges: + +1. There are some declarations—generic parameters, for instance—which are + not accessible through any qualified lookup (they are neither top-level + declarations nor accessible through the member syntax). We would have to + invent some way to reference these. + +2. Existing module interfaces have already been produced which would be broken + by this change, so it would have to somehow be made conditional. + +3. Currently, inlinable function bodies are not comprehensively regenerated + for module interfaces; instead, the original textual source code is + inserted with minor programmatic edits to remove comments and `#if` blocks. + This means we would have to revert to the normal lookup rules within an + inlinable function body. + +It also would not help with ambiguous *qualified* lookups, such as when two +modules use extensions to add identically-named nested types to a top-level +type, and it would not give developers new options for handling ambiguity in +human-written code. + +### Add a fallback lookup rule for module name shadowing + +The issue with shadowing of module names could be addressed by adding a narrow +rule saying that, when a type has the same name as its enclosing module and a +qualified lookup inside it doesn't find any viable candidates, Swift will fall +back to looking in the module it shadowed. + +This would address the `XCTest.XCTestCase` problem, which is the most common +seen in practice, but it wouldn't help with more complicated situations (like +a nested type shadowing a top-level type, or a type in one module having the +same name as a different module). It's also not a very principled rule and +making it work properly in expressions might complicate the type checker. + +### Use a syntax involving a special prefix + +We considered creating a special syntax which would indicate unambiguously that +the next name must be a module name, such as `#Modules.FooKit.bar`. However, +this would only have helped with top-level declarations, not members of +extensions. + +### Use a syntax that avoids `::`'s shortcomings + +Although we have good reasons to propose using the `::` operator (see "Proposed +solution" above), we do not think it's a perfect choice. It appears visually +"heavier" than the `.` operator, which means developers reading the code might +mentally group the identifiers incorrectly: + +```swift +Mission.NASA::Booster.Exhaust // Looks like it means `(Mission.NASA) :: (Booster.Exhaust)` + // but actually means `Mission . (NASA::Booster) . Exhaust` +``` + +This is not unprecedented—in C++, `myObject.MyClass::myMember` means +`(myObject) . (MyClass::myMember)`—but it's awkward for developers without +a background in a language that works like this. + +We rejected a number of alternatives that would avoid this problem. + +#### Make module selectors qualify different names + +One alternative would be to have the module selector qualify the *rightmost* +name in the member chain, rather than the leftmost, so that a module selector +could only appear at the head of a member chain. The previous example would +then be written as: + +```swift +(NASA::Mission.Booster).Exhaust +``` + +We don't favor this design because we believe: + +1. Developers more frequently need to qualify top-level names (which exist in a + very crowded quasi-global namespace) than member names (which are already + limited by the type they're looking inside); a syntax that makes qualifying + the member the default is optimizing for the wrong case. + +2. The distance between the module and the identifier it qualifies increases + the cognitive burden of pairing up modules to the names they apply to. + +3. Subjectively, it's just *weird* that the selector applies to a name that's a + considerable distance from it, rather than the name immediately adjacent. + +A closely related alternative would be to have the module selector qualify +*all* names in the member chain, so that in `(NASA::Mission.Booster).Exhaust`, +both `Mission` and `Booster` must be in module `NASA`. We think point #1 from +the list above applies to this design too: `Mission` is a sparse enough +namespace that developers are more likely to be hindered by `Booster` being +qualified by the `NASA` module than helped by it. + +#### Use a totally different spelling + +We've considered and rejected a number of other spellings for this feature, +such as: + +```swift +Mission.#module(NASA).Booster.Exhaust // Much wordier, not implementable as a macro +Mission.::NASA.Booster.Exhaust // Looks weird in member position +Mission.Booster@NASA.Exhaust // Ignores Swift's left-to-right nesting convention +Mission.'NASA.Booster.Exhaust // Older compilers would mis-lex in inactive `#if` blocks +Mission.NASA'Booster.Exhaust // " +Mission.(NASA)Booster.Exhaust // Arbitrary; little connection to prior art +Mission.'NASA'.Booster.Exhaust // " +``` + +### Don't restrict whitespace to the right of the `::` + +Allowing a newline between `::` and the identifier following it would mean +that, when an incomplete line of code ended with a module selector, Swift might +interpret a keyword on the next line as a variable name, likely causing multiple +confusing syntax errors in the next statement. For instance: + +```swift +let x = NASA:: // Would be interpreted as `let x = NASA::if x { ... }`, +if x { ... } // causing several confusing syntax errors. +``` + +Forbidding it, however, has a cost: it restricts the code styles developers can +use. If a developer wants to put a line break in the middle of a name with a +module selector, they will not be able to format it like this: + +```swift +SuperLongAndComplicatedModuleName:: + superLongAndComplicatedFunctionName() +``` + +And will have to write it like this instead: + +```swift +SuperLongAndComplicatedModuleName + ::superLongAndComplicatedFunctionName() +``` + +The member-lookup `.` operator has a similar restriction, but developers may +not want to style them in exactly the same way, particularly since C++ +developers often split a line after a `::`. + + [SE-0071]: "Allow (most) keywords in member references" + [SE-0364]: "Warning for Retroactive Conformances of External Types" + [SE-0382-review-2]: "SE-0382 (second review): Expression Macros" + [SE-0444]: "SE-0444 Member Import Visibility" From 500dde709a7220b4bf27adb2e379c056376f8f0d Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Sat, 13 Sep 2025 19:35:25 -0400 Subject: [PATCH 4472/4563] Put SE-0491 into review (#2955) --- proposals/0491-module-selectors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0491-module-selectors.md b/proposals/0491-module-selectors.md index 936c00311c..7d2c769591 100644 --- a/proposals/0491-module-selectors.md +++ b/proposals/0491-module-selectors.md @@ -3,10 +3,10 @@ * Proposal: [SE-0491](0491-module-selectors.md) * Authors: [Becca Royal-Gordon](https://github.com/beccadax) * Review Manager: [Freddy Kellison-Linn](https) -* Status: **Awaiting review** +* Status: **Active review (September 13 ... September 26, 2025)** * Bug: [swiftlang/swift#53580](https://github.com/swiftlang/swift/issues/53580) (SR-11183) * Implementation: [swiftlang/swift#34556](https://github.com/swiftlang/swift/pull/34556) -* Review: ([pitch](https://forums.swift.org/t/pitch-module-selectors/80835)) +* Review: ([pitch](https://forums.swift.org/t/pitch-module-selectors/80835)) ([review](https://forums.swift.org/t/se-0491-module-selectors-for-name-disambiguation/82124)) Previously pitched in: From ca06ee3cf2805b6553cd16c9aefce6b3d4ffd619 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Tue, 16 Sep 2025 12:40:10 -0700 Subject: [PATCH 4473/4563] Update proposal to state that @inline(always) should imply @inlinable on public'ish declarations --- proposals/NNNN-inline-always.md | 57 +++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/proposals/NNNN-inline-always.md b/proposals/NNNN-inline-always.md index 3b2cf8dd0f..80f5fe2bc1 100644 --- a/proposals/NNNN-inline-always.md +++ b/proposals/NNNN-inline-always.md @@ -3,6 +3,7 @@ * Proposal: [SE-NNNN](NNNN-inline-always.md) * Authors: [Arnold Schwaighofer](https://github.com/aschwaighofer) * Implementation: [swiftlang/swift#84178](https://github.com/swiftlang/swift/pull/84178) +* Pitch thread: https://forums.swift.org/t/pitch-inline-always-attribute/82040 ## Introduction @@ -149,18 +150,55 @@ be possible? ### Interaction with `@inlinable` -Function bodies of functions referenceable outside of the defining module are -only available to the outside module if the definition is marked `@inlinable`. - -Therefore, a function marked with `@inline(always)` must be marked `@inlinable` -if it has `open`, `public`, or `package` level access. +`@inlinable` and `@_alwaysEmitIntoClient` make the function body available to +clients (callers in other modules) in library evolution mode. `@inlinable` makes +the body of the function available to the client and causes an ABI entry point +in the vending module to vended. `@_alwaysEmitIntoClient` makes the body of the +function available for clients but does not cause emission of an ABI entry +point. Functions with `open`, `public`, or `package` level access cause emission +of an ABI entry point for clients to call but in the absence of aforementioned +attributes do not make the body available to the client. + +`@inline(always)` intention is to be able to guarantee that inlining will happen +for any caller inside or outside the defining module therefore it makes sense to +require the use some form of "inline-ability" attribute with them. This +attribute could be required to be explicitly stated. And for it to be an error +when the attribute is omitted. ```swift +@inline(always) +@inlinable // or @_alwaysEmitIntoClient +public func caller() { ... } + @inline(always) // error: a public function marked @inline(always) must be marked @inlinable public func callee() { } ``` +Alternatively, the attribute could be implicitly implied by the usage of +`@inline(always)`. In this proposal, we take the position that it should be +implied to avoid the redundancy of spelling this out. The intention of +`@inline(always)` is for it to inline in all contexts. Instead of an error in the +absence of the attribute we should imply "inline-ability". The question is what +should we default to? + +`@_alwaysEmitIntoClient`'s semantics seems preferable for new functions. We +intend for the function to be always inlined, why should there be an ABI entry +point? + +`@inlinable` semantics allows for annotating existing functions with +`@inline(always)` without breaking ABI compatibility. `@inlinable` keeps an +entry point in the vending module for older code that assumed the existence of +an entry point. + +This proposals takes the position to give `@inline(always)` the semantics of +`@inlineable` and provide an alternative spelling for the case when we desire +`@_alwaysEmitIntoClient` semantics: `@inline(only)`. + +For access levels equal and lower than `internal` `@inlinable` should not be +implied. + + ### Interaction with `@usableFromInline` A `public` `@inlinable` function can reference a function with `internal` access @@ -297,7 +335,9 @@ changes to inlining heuristic). ## ABI compatibility -The addition of the attribute has no effect on ABI compatibility. +The addition of the attribute has no effect on ABI compatibility. We chose to +imply `@inlinable` for `public` (et al.) declarations which will continue to +emit an entry point for existing binary clients. ## Implications on adoption @@ -342,6 +382,11 @@ This would deliver less predictable optimization behavior in cases where authors overlooked requirements for inlining to happen such as not marking a public function as `@inlinable`. +With respect to `@inlinable` an initial draft of the proposal suggested to +require spelling the `@inlinable` attribute on `public` declarations or an error +would be displayed. The argument was that this would ensure that authors would +be aware of the additional semantics implied by the attribute: the body is +exposed. ## Acknowledgments From 0406799a3c47b5245bf0fc1edd7cf25c44087707 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Fri, 19 Sep 2025 13:04:23 +0100 Subject: [PATCH 4474/4563] Mark SE-0482 as implemented in 6.2 The main implementation PR was included in `swift-6.2-RELEASE` tag. --- ...wiftpm-static-library-binary-target-non-apple-platforms.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md index 569c018de1..5c7d90c92c 100644 --- a/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md +++ b/proposals/0482-swiftpm-static-library-binary-target-non-apple-platforms.md @@ -3,8 +3,8 @@ * Proposal: [SE-0482](0482-swiftpm-static-library-binary-target-non-apple-platforms.md) * Authors: [Daniel Grumberg](https://github.com/daniel-grumberg), [Max Desiatov](https://github.com/MaxDesiatov), [Franz Busch](https://github.com/FranzBusch) * Review Manager: [Kuba Mracek](https://github.com/kubamracek) -* Status: **Accepted** -* Implementation: [swiftlang/swift-package-manager#6967](https://github.com/swiftlang/swift-package-manager/pull/6967) [swiftlang/swift-package-manager#8605](https://github.com/swiftlang/swift-package-manager/pull/8605) +* Status: **Implemented (Swift 6.2)** +* Implementation: [swiftlang/swift-package-manager#8639](https://github.com/swiftlang/swift-package-manager/pull/8639) [swiftlang/swift-package-manager#8741](https://github.com/swiftlang/swift-package-manager/pull/8741) * Review: ([discussion](https://forums.swift.org/t/se-0482-binary-static-library-dependencies/79634)) ([pitch](https://forums.swift.org/t/pitch-swiftpm-support-for-binary-static-library-dependencies/78619)) ([acceptance](https://forums.swift.org/t/accepted-se-0482-binary-static-library-dependencies/80042)) * Bugs: [Swift Package Manger Issue](https://github.com/swiftlang/swift-package-manager/issues/7035) From a0aa0d49fb7db40df57236381eedd643d22864b1 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Fri, 19 Sep 2025 09:07:28 -0700 Subject: [PATCH 4475/4563] Simplify the introduction section --- proposals/0nnn-section-control.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/0nnn-section-control.md b/proposals/0nnn-section-control.md index 4d30b0bfc7..344385cf1d 100644 --- a/proposals/0nnn-section-control.md +++ b/proposals/0nnn-section-control.md @@ -10,11 +10,7 @@ ## Introduction -This proposal builds on top of [Swift Compile-Time Values](https://github.com/artemcm/swift-evolution/blob/const-values/proposals/0nnn-const-values.md) and adds two new attributes into the Swift language: `@section` and `@used`. These allow users to directly control which section of the resulting binary should globals variables be emitted into, and give users the ability to disable DCE (dead code elimination) on those. The goal is to enable systems and embedded programming use cases like runtime discovery of test metadata from multiple modules, and also to serve as a low-level building block for higher-level features (e.g. linker sets, plugins). - -The intention is that these attributes are to be used rarely and only by specific use cases; high-level application code should not need to use them directly and instead should rely on libraries, macros and other abstractions over the low-level attributes. - -The scope of this proposal is limited to compile-time behavior and compile-time control. We expect that full user-facing solutions for features like linker sets, test discovery or plugins will also require runtime implementations to discover and iterate the contents of custom sections, possibly from multiple modules. This proposal makes sure to provide the right building blocks and artifacts in binaries for the runtime components, but doesn’t prescribe the shape of those. However, it is providing a significant step towards generalized and safe high-level mechanisms for those use cases. See the discussion of that in the sections [Runtime discovery of data in custom sections](#runtime-discovery-of-data-in-custom-sections) and [Linker sets, plugins as high-level APIs](#linker-sets-plugins-as-high-level-apis) in Future Directions. +This proposal adds `@section` and `@used` attributes which can be applied to global variables. These allow users to directly control which section of the resulting binary should global variables be emitted into, and give users the ability to disable DCE (dead code elimination) on those. The goal is to enable systems and embedded programming use cases like runtime discovery of test metadata from multiple modules, and also to serve as a low-level building block for higher-level features (e.g. linker sets, plugins). ## Motivation @@ -104,6 +100,10 @@ var global = ... For the ELF file format specifically, the compiler will also emit a “section index” into produced object files, containing an entry about each custom section used in the compilation. This is a solution to an ELF specific problem where the behavior of ELF linkers and loaders means that sections are not easily discoverable at runtime. +> Note: The intention is that the `@section` and `@used` attributes are to be used rarely and only by specific use cases; high-level application code should not need to use them directly and instead should rely on libraries, macros and other abstractions over the low-level attributes. + +> The scope of this proposal is limited to compile-time behavior and compile-time control. We expect that full user-facing solutions for features like linker sets, test discovery or plugins will also require runtime implementations to discover and iterate the contents of custom sections, possibly from multiple modules. This proposal makes sure to provide the right building blocks and artifacts in binaries for the runtime components, but doesn’t prescribe the shape of those. However, it is providing a significant step towards generalized and safe high-level mechanisms for those use cases. See the discussion in [Runtime discovery of data in custom sections](#runtime-discovery-of-data-in-custom-sections) and [Linker sets, plugins as high-level APIs](#linker-sets-plugins-as-high-level-apis) in Future Directions. + ## Detailed design ### Attributes @section and @used on global and static variables From 4dd4f53fec73695ffbab43d8051687a4df2b16ac Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Fri, 19 Sep 2025 11:00:43 -0700 Subject: [PATCH 4476/4563] Add definition of constant expressions, add 'lazy' --- proposals/0nnn-section-control.md | 72 ++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/proposals/0nnn-section-control.md b/proposals/0nnn-section-control.md index 344385cf1d..72db9555a7 100644 --- a/proposals/0nnn-section-control.md +++ b/proposals/0nnn-section-control.md @@ -55,7 +55,7 @@ This proposal recommends to use sections of the various object file formats as t ## Proposed Solution -The proposal is to add two new attributes `@section` and `@used` that will allow annotating global and static variables with directives to place the value into a custom section, and to require no-dead-stripping aka "attribute used". The `@section` attribute relies heavily on the facilities provided by [Swift Compile-Time Values](https://github.com/artemcm/swift-evolution/blob/const-values/proposals/0nnn-const-values.md), namely the ability to enforce constantness of an expressions. Using `@section` requires that the initializer expression is a constant expression: +The proposal is to add two new attributes `@section` and `@used` that will allow annotating global and static variables with directives to place the value into a custom section, and to require no-dead-stripping aka "attribute used". Using `@section` requires that the initializer expression is a constant expression (see Constant Expressions below for the definition of that): ```swift // Place an entry into a section, mark as "do not dead strip". @@ -100,6 +100,13 @@ var global = ... For the ELF file format specifically, the compiler will also emit a “section index” into produced object files, containing an entry about each custom section used in the compilation. This is a solution to an ELF specific problem where the behavior of ELF linkers and loaders means that sections are not easily discoverable at runtime. +When placing variables into a custom section, it's also allowed to use the `lazy` keyword to opt out of the mandatory static initialization and mandatory constant expression behavior, while still achieving section placement of the backing store of the data: + +```swift +@section("__DATA,colocated") lazy let data1: Int = 42 // ✅ +@section("__DATA,colocated") lazy let data2: Int = Int.random(in: 0 ..< 10) // ✅ +``` + > Note: The intention is that the `@section` and `@used` attributes are to be used rarely and only by specific use cases; high-level application code should not need to use them directly and instead should rely on libraries, macros and other abstractions over the low-level attributes. > The scope of this proposal is limited to compile-time behavior and compile-time control. We expect that full user-facing solutions for features like linker sets, test discovery or plugins will also require runtime implementations to discover and iterate the contents of custom sections, possibly from multiple modules. This proposal makes sure to provide the right building blocks and artifacts in binaries for the runtime components, but doesn’t prescribe the shape of those. However, it is providing a significant step towards generalized and safe high-level mechanisms for those use cases. See the discussion in [Runtime discovery of data in custom sections](#runtime-discovery-of-data-in-custom-sections) and [Linker sets, plugins as high-level APIs](#linker-sets-plugins-as-high-level-apis) in Future Directions. @@ -180,6 +187,54 @@ When allowed, the `@used` attribute on a variable declaration has the following The effects described above are applied to the storage symbols and don’t generally affect optimizations and other transformations in the compiler. For example, the compiler is still allowed to propagate and copy a the constant value to code that uses the value, so there’s no guarantee that a value stored into a global with a custom section will not be propagated and “leak” outside of the section. The `@used` annotation, however, does inform the optimizer that such a variable cannot be removed, even when it doesn’t have any observed users or even if it’s inaccessible due to language rules (e.g. if it’s a private static member on an otherwise empty type). +### Constant expressions + +Swift currently does not have a formal notion of a **constant expression**, i.e. an expression with a syntactic form that *guarantees the ability to know it's value at compile-time*. This proposal provides a definition of a "bare minimum" constant expression, with the understanding that this does not cover the language needs in generality, and the expectation that the Swift compiler and language will keep expanding the allowed forms of constant expressions. See [Generalized constant values and expressions](#generalized-constant-values-and-expressions) in Future Directions for further discussion on this. + +This proposal defines a **constant expression** as being one of: + +- an integer literal using any of built-in integer types (Int, UInt, Int8/16/32/64/128, UInt8/16/32/64/128) +- a floating-point literal of type Float or Double +- a boolean literal of type Bool +- a direct reference to a non-generic function using its name (the function itself is not generic, and also it must not be defined in a generic context) +- a direct reference to a non-generic metatype using the type name directly (the type itself is not generic, and also it must not be defined in a generic context), where the type is non-resilient +- a tuple composed of only other constant expressions +- an array literal of type InlineArray composed of only other constant expressions + +Explicitly, this definition currently does **not allow** any operators, using any user-defined named types, any other built-in type (e.g. strings, dictionaries, sets), using closures, or referencing any variables by name. See below for examples of valid and invalid constant expressions: + +```swift +@section("...") let a = 42 // ✅ +@section("...") let b = 3.14 // ✅ +@section("...") let c = 1 + 1 // ❌ operators not allowed +@section("...") let d = Int.max // ❌ not a literal +@section("...") let e: UInt8 = 42 // ✅ +@section("...") let f = UInt8(42) // ❌ not a literal +@section("...") let g: MyCustomExpressibleByIntegerLiteral = 42 // ❌ not a built-in type + +@section("...") let composition1 = (1, 2, 3, 2.718, true) // ✅ +@section("...") let composition2 = (1, 2, Int.max) // ❌ tuple component not constant +@section("...") let composition3: InlineArray = [1, 2, 3] // ✅ +@section("...") let composition4: InlineArray = [1, 2, Int.max] // ❌ array component not constant +@section("...") let composition5: (Int, [1 of Int], [1 of (Int, Int)]) = (1, [1], [(1, 1)]) // ✅ + +func foo() -> Int { return 42 } +@section("...") let func1 = foo // ✅ +@section("...") let func2 = foo() // ❌ not a function reference +@section("...") let func3 = Bool.random // ✅ +@section("...") let func4 = Bool.self.random // ❌ not a direct reference +@section("...") let func5 = (Bool.self as Bool.Type).random // ❌ not a direct reference +@section("...") let func6 = [Int].randomElement // ❌ generic +@section("...") let func7 = { } // ❌ not using name + +struct S { } +@section("...") let metatype1 = S.self // ✅ +@section("...") let metatype2 = Int.self // ✅ +@section("...") let metatype3 = Int.self.self // ❌ not a direct reference +import Foundation +@section("...") let metatype4 = URL.self // ❌ resilient +``` + ### Guaranteed static initialization Using attribute `@section` requires the initializer expression of the variable to be a **constant expression**. It's not required to separately annotate the expression for being a compile-time expression, instead this is implied from the `@section` attribute. On top of the constant-ness, `@section` on a global or static variable enforces **static initialization** on that variable. @@ -221,6 +276,17 @@ let a = (42, foo) // "foo" is statically initialized into a // linkable/relocatable pointer ``` +### Lazy variables with section placement + +On global and static variables that are annotated with `@section`, the compiler will now allow the `lazy` keyword (which is currently disallowed on all global and static variables). Using it will opt such variable out of the "mandatory static initialization" behavior and instead use the at-runtime lazy initialization that traditional global and static variables have. The initializer expression does not need to be a constant expression in this case. This is useful for the uncommon use case of placing variables into a custom section purely for colocation (e.g. to improve performance by increasing page/cacheline locality): + +```swift +@section("__DATA,colocated") lazy let data1: Int = 42 // ✅ +@section("__DATA,colocated") lazy let data2: Int = Int.random(in: 0 ..< 10) // ✅ +``` + +Traditional global and static variables are backed by two symbols: An init-once token and the actual storage for the variable's content. Both of these symbols are going to be placed into the custom section when using `@section` with `lazy`. This also means that any offline or in-process introspection mechanisms cannot assume a specific layout or state of such variables and their storage bytes in the sections, as the exact layout and content of the symbols of lazy variables is an implementation detail of the Swift language runtime. + ### Cross-platform object file format support The custom section name specified in the `@section` attribute is not validated by the compiler, instead it’s passed directly as a string to the linker. Different platforms supported by Swift are using different object and binary file formats (Linux uses ELF, Darwin uses Mach-O, Windows uses COFF), and that implies different restrictions and rules on what are valid section names. Because of that, a multi-platform library code is expected to use `#if os(...)` to use different section names for different platforms. Because of that, it’s expected that the attributes are to be typically only used indirectly via macros that hide the low-level nature of sections and object file formats from higher-level code developers: @@ -325,6 +391,10 @@ This will require some design decisions to be made around when should that be al Static initialization of a global can be useful on its own, without placing data into a custom section, and a separate attribute for that could be added. This way, one can get the same effects as the `@section` attribute (static initialization, normal initalization behavior if top-level code) except the symbol would not be actually placed into any custom section. +### Generalized constant values and expressions + +The notions of constant expressions and constant values is applicable to a much wider set of use cases that just section placement, and the set of allowed types and syntactical forms should be expanded in the future into a full-featured system for compile-time programming. A dedicated proposal, [Swift Compile-Time Values](https://github.com/artemcm/swift-evolution/blob/const-values/proposals/0nnn-const-values.md), is being pitched [on the forums](https://forums.swift.org/t/pitch-3-swift-compile-time-values/77434) and describes in detail the possible future of generalized constants, the relevant motivation and use cases. + ### Allowing a reference to a constant string declaration as a section name The requirement to only use string literals as the section names could be lifted in the future, and we might allow referring to a declaration of variable with a compile-time string. This would be useful to avoid repetition when placing multiple values into the same section without needing to use macros. From 19a0bda2f7b497072e3632f8f1b5437f5b18e6d5 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Fri, 19 Sep 2025 11:03:02 -0700 Subject: [PATCH 4477/4563] Fix inline link --- proposals/0nnn-section-control.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0nnn-section-control.md b/proposals/0nnn-section-control.md index 72db9555a7..74af21dbd3 100644 --- a/proposals/0nnn-section-control.md +++ b/proposals/0nnn-section-control.md @@ -55,7 +55,7 @@ This proposal recommends to use sections of the various object file formats as t ## Proposed Solution -The proposal is to add two new attributes `@section` and `@used` that will allow annotating global and static variables with directives to place the value into a custom section, and to require no-dead-stripping aka "attribute used". Using `@section` requires that the initializer expression is a constant expression (see Constant Expressions below for the definition of that): +The proposal is to add two new attributes `@section` and `@used` that will allow annotating global and static variables with directives to place the value into a custom section, and to require no-dead-stripping aka "attribute used". Using `@section` requires that the initializer expression is a constant expression (see [Constant expressions](#constant-expressions) below for the definition of that): ```swift // Place an entry into a section, mark as "do not dead strip". From 39a4ec9ecfff9eb4cd4bfc9d3c3e8a172f68e4d4 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Fri, 19 Sep 2025 14:34:29 -0400 Subject: [PATCH 4478/4563] [ST-0015] Swift Testing image attachments (Windows) (#2940) * [WIP] Swift Testing image attachments (Windows) * Update NNNN-image-attachments-in-swift-testing-windows.md Add pitch link * Add another link to ST-0014. * Update tools integration blurb * Assign number (ST-0015) to image attachments on Windows proposal and adjust header fields * Fill in my name as review manager --------- Co-authored-by: Stuart Montgomery --- ...ge-attachments-in-swift-testing-windows.md | 393 ++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 proposals/testing/0015-image-attachments-in-swift-testing-windows.md diff --git a/proposals/testing/0015-image-attachments-in-swift-testing-windows.md b/proposals/testing/0015-image-attachments-in-swift-testing-windows.md new file mode 100644 index 0000000000..954b0f51a9 --- /dev/null +++ b/proposals/testing/0015-image-attachments-in-swift-testing-windows.md @@ -0,0 +1,393 @@ +# Image attachments in Swift Testing (Windows) + +* Proposal: [ST-0015](0015-image-attachments-in-swift-testing-windows.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Review Manager: [Stuart Montgomery](https://github.com/stmontgomery) +* Status: **Active Review (September 19…29, 2025)** +* Implementation: [swiftlang/swift-testing#1245](https://github.com/swiftlang/swift-testing/pull/1245), [swiftlang/swift-testing#1254](https://github.com/swiftlang/swift-testing/pull/1254), _et al_. +* Review: ([pitch](https://forums.swift.org/t/pitch-image-attachments-in-swift-testing-windows/81871)) + +## Introduction + +In [ST-0014](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md), +we added to Swift Testing the ability to attach images (of types `CGImage`, +`NSImage`, `UIImage`, and `CIImage`) on Apple platforms. This proposal builds on +that one to add support for attaching images on Windows. + +## Motivation + +It is frequently useful to be able to attach images to tests for engineers to +review, e.g. if a UI element is not being drawn correctly. If something doesn't +render correctly in a CI environment, for instance, it is very useful to test +authors to be able to download the failed rendering and examine it at-desk. + +In [ST-0014](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md#integration-with-supporting-tools), +we introduced the ability to attach images to tests on Apple's platforms. Swift +Testing is a cross-platform testing library, so we should extend this +functionality to other platforms too. This proposal covers Windows in +particular. + +## Proposed solution + +We propose adding the ability to automatically encode images to standard +graphics formats such as JPEG or PNG using Windows' built-in Windows Image +Component library, similar to how we added support on Apple platforms using Core +Graphics. + +## Detailed design + +### Some background about Windows' image types + +Windows has several generations of API for representing and encoding images. The +earliest Windows API of interest to this proposal is the Graphics Device +Interface (GDI) which dates back to the earliest versions of Windows. Image +types in GDI that are of interest to us are `HBITMAP` and `HICON`, which are +_handles_ (pointers-to-pointers) and which are not reference-counted. Both types +are projected into Swift as typealiases of `UnsafeMutablePointer`. + +Windows' latest[^direct2d] graphics API is the Windows Imaging Component (WIC) +which uses types based on the Component Object Model (COM). COM types (including +those implemented in WIC) are C++ classes that inherit from `IUnknown`. + +[^direct2d]: There is an even newer API in this area, Direct2D, but it is beyond + the scope of this proposal. A developer who has an instance of e.g. + `ID2D1Bitmap` can use WIC API to convert it to a WIC bitmap source before + attaching it to a test. + +`IUnknown` is conceptually similar to Cocoa's `NSObject` class in that it +provides basic reference-counting and reflection functionality. As of this +proposal, the Swift C/C++ importer is not aware of COM classes and does not +project them into Swift as reference-counted classes. Rather, they are projected +as `UnsafeMutablePointer`, and developers who use them must manually manage +their reference counts and must use `QueryInterface()` to cast them to other COM +classes. + +In short: the types we need to support are all specializations of +`UnsafeMutablePointer`, but we do not need to support all specializations of +`UnsafeMutablePointer` unconditionally. + +### Defining a new protocol for Windows image attachments + +A new protocol is introduced for Windows, similar to the `AttachableAsCGImage` +protocol we introduced for Apple's platforms: + +```swift +/// A protocol describing images that can be converted to instances of +/// [`Attachment`](https://developer.apple.com/documentation/testing/attachment). +/// +/// Instances of types conforming to this protocol do not themselves conform to +/// [`Attachable`](https://developer.apple.com/documentation/testing/attachable). +/// Instead, the testing library provides additional initializers on [`Attachment`](https://developer.apple.com/documentation/testing/attachment) +/// that take instances of such types and handle converting them to image data when needed. +/// +/// You can attach instances of the following system-provided image types to a +/// test: +/// +/// | Platform | Supported Types | +/// |-|-| +/// | macOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) | +/// | iOS, watchOS, tvOS, and visionOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) | +/// | Windows | [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps), [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons), [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource) (including its subclasses declared by Windows Imaging Component) | +/// +/// You do not generally need to add your own conformances to this protocol. If +/// you have an image in another format that needs to be attached to a test, +/// first convert it to an instance of one of the types above. +public protocol AttachableAsIWICBitmapSource: SendableMetatype { + /// Create a WIC bitmap source representing an instance of this type. + /// + /// - Returns: A pointer to a new WIC bitmap source representing this image. + /// The caller is responsible for releasing this image when done with it. + /// + /// - Throws: Any error that prevented the creation of the WIC bitmap source. + func copyAttachableIWICBitmapSource() throws -> UnsafeMutablePointer +} +``` + +Conformance to this protocol is added to `UnsafeMutablePointer` when its +`Pointee` type is one of the following types: + +- [`HBITMAP.Pointee`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps) +- [`HICON.Pointee`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons) +- [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource) + (including its subclasses declared by Windows Imaging Component) + +> [!NOTE] +> The list of conforming types may be extended in the future. The Testing +> Workgroup will determine if additional Swift Evolution reviews are needed. + +A type in Swift can only conform to a protocol with **one** set of constraints, +so we need a helper protocol in order to make `UnsafeMutablePointer` +conditionally conform for all of the above types. This protocol must be `public` +so that Swift Testing can refer to it in API, but it is an implementation detail +and not part of this proposal: + +```swift +public protocol _AttachableByAddressAsIWICBitmapSource {} + +extension HBITMAP.Pointee: _AttachableByAddressAsIWICBitmapSource {} +extension HICON.Pointee: _AttachableByAddressAsIWICBitmapSource {} +extension IWICBitmapSource: _AttachableByAddressAsIWICBitmapSource {} + +extension UnsafeMutablePointer: AttachableAsIWICBitmapSource + where Pointee: _AttachableByAddressAsIWICBitmapSource {} +``` + +See the **Future directions** section (specifically the point about COM and C++ +interop) for more information on why the helper protocol is excluded from this +proposal. + +### Attaching a conforming image + +New overloads of `Attachment.init()` and `Attachment.record()` are provided: + +```swift +extension Attachment { + /// Initialize an instance of this type that encloses the given image. + /// + /// - Parameters: + /// - image: A pointer to the value that will be attached to the output of + /// the test run. + /// - preferredName: The preferred name of the attachment when writing it + /// to a test report or to disk. If `nil`, the testing library attempts + /// to derive a reasonable filename for the attached value. + /// - imageFormat: The image format with which to encode `image`. + /// - sourceLocation: The source location of the call to this initializer. + /// This value is used when recording issues associated with the + /// attachment. + /// + /// You can attach instances of the following system-provided image types to a + /// test: + /// + /// | Platform | Supported Types | + /// |-|-| + /// | macOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) | + /// | iOS, watchOS, tvOS, and visionOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) | + /// | Windows | [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps), [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons), [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource) (including its subclasses declared by Windows Imaging Component) | + /// + /// The testing library uses the image format specified by `imageFormat`. Pass + /// `nil` to let the testing library decide which image format to use. If you + /// pass `nil`, then the image format that the testing library uses depends on + /// the path extension you specify in `preferredName`, if any. If you do not + /// specify a path extension, or if the path extension you specify doesn't + /// correspond to an image format the operating system knows how to write, the + /// testing library selects an appropriate image format for you. + public init( + _ image: T, + named preferredName: String? = nil, + as imageFormat: AttachableImageFormat? = nil, + sourceLocation: SourceLocation = #_sourceLocation + ) where T: AttachableAsIWICBitmapSource, AttachableValue == _AttachableImageWrapper + + /// Attach an image to the current test. + /// + /// - Parameters: + /// - image: The value to attach. + /// - preferredName: The preferred name of the attachment when writing it + /// to a test report or to disk. If `nil`, the testing library attempts + /// to derive a reasonable filename for the attached value. + /// - imageFormat: The image format with which to encode `image`. + /// - sourceLocation: The source location of the call to this initializer. + /// This value is used when recording issues associated with the + /// attachment. + /// + /// This function creates a new instance of ``Attachment`` wrapping `image` + /// and immediately attaches it to the current test. You can attach instances + /// of the following system-provided image types to a test: + /// + /// | Platform | Supported Types | + /// |-|-| + /// | macOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`NSImage`](https://developer.apple.com/documentation/appkit/nsimage) | + /// | iOS, watchOS, tvOS, and visionOS | [`CGImage`](https://developer.apple.com/documentation/coregraphics/cgimage), [`CIImage`](https://developer.apple.com/documentation/coreimage/ciimage), [`UIImage`](https://developer.apple.com/documentation/uikit/uiimage) | + /// | Windows | [`HBITMAP`](https://learn.microsoft.com/en-us/windows/win32/gdi/bitmaps), [`HICON`](https://learn.microsoft.com/en-us/windows/win32/menurc/icons), [`IWICBitmapSource`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapsource) (including its subclasses declared by Windows Imaging Component) | + /// + /// The testing library uses the image format specified by `imageFormat`. Pass + /// `nil` to let the testing library decide which image format to use. If you + /// pass `nil`, then the image format that the testing library uses depends on + /// the path extension you specify in `preferredName`, if any. If you do not + /// specify a path extension, or if the path extension you specify doesn't + /// correspond to an image format the operating system knows how to write, the + /// testing library selects an appropriate image format for you. + public static func record( + _ image: T, + named preferredName: String? = nil, + as imageFormat: AttachableImageFormat? = nil, + sourceLocation: SourceLocation = #_sourceLocation + ) where T: AttachableAsIWICBitmapSource, AttachableValue == _AttachableImageWrapper +} +``` + +> [!NOTE] +> `_AttachableImageWrapper` was described in [ST-0014](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md#attaching-a-conforming-image). +> The only difference on Windows is that its associated `Image` type is +> constrained to `AttachableAsIWICBitmapSource` instead of `AttachableAsCGImage`. + +### Specifying image formats + +As on Apple platforms, a test author can specify the image format to use with +`AttachableImageFormat`. See [ST-0014](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md#specifying-image-formats) +for more information about that type. + +Windows does not use Uniform Type Identifiers, so those `AttachableImageFormat` +members that use `UTType` are not available here. Instead, Windows uses a +variety of COM classes that implement codecs for different image formats. +Conveniences over those COM classes' `CLSID` values are provided: + +```swift +extension AttachableImageFormat { + /// The `CLSID` value of the Windows Imaging Component (WIC) encoder class + /// that corresponds to this image format. + /// + /// For example, if this image format equals ``png``, the value of this + /// property equals [`CLSID_WICPngEncoder`](https://learn.microsoft.com/en-us/windows/win32/wic/-wic-guids-clsids#wic-guids-and-clsids). + public var encoderCLSID: CLSID { get } + + /// Construct an instance of this type with the `CLSID` value of a Windows + /// Imaging Component (WIC) encoder class and the desired encoding quality. + /// + /// - Parameters: + /// - encoderCLSID: The `CLSID` value of the Windows Imaging Component + /// encoder class to use when encoding images. + /// - encodingQuality: The encoding quality to use when encoding images. For + /// the lowest supported quality, pass `0.0`. For the highest supported + /// quality, pass `1.0`. + /// + /// If the target image encoder does not support variable-quality encoding, + /// the value of the `encodingQuality` argument is ignored. + /// + /// If `clsid` does not represent an image encoder class supported by WIC, the + /// result is undefined. For a list of image encoder classes supported by WIC, + /// see the documentation for the [`IWICBitmapEncoder`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapencoder) + /// class. + public init(encoderCLSID: CLSID, encodingQuality: Float = 1.0) +} +``` + +For convenience, an initializer is provided that takes a path extension and +tries to map it to the appropriate codec's `CLSID` value: + +```swift +extension AttachableImageFormat { + /// Construct an instance of this type with the given path extension and + /// encoding quality. + /// + /// - Parameters: + /// - pathExtension: A path extension corresponding to the image format to + /// use when encoding images. + /// - encodingQuality: The encoding quality to use when encoding images. For + /// the lowest supported quality, pass `0.0`. For the highest supported + /// quality, pass `1.0`. + /// + /// If the target image format does not support variable-quality encoding, + /// the value of the `encodingQuality` argument is ignored. + /// + /// If `pathExtension` does not correspond to a recognized image format, this + /// initializer returns `nil`: + /// + /// - On Apple platforms, the content type corresponding to `pathExtension` + /// must conform to [`UTType.image`](https://developer.apple.com/documentation/uniformtypeidentifiers/uttype-swift.struct/image). + /// - On Windows, there must be a corresponding subclass of [`IWICBitmapEncoder`](https://learn.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapencoder) + /// registered with Windows Imaging Component. + public init?(pathExtension: String, encodingQuality: Float = 1.0) +} +``` + +For consistency, `init(pathExtension:encodingQuality:)` is provided on Apple +platforms too. (This is the only part of this proposal that affects platforms +other than Windows.) + +### Example usage + +A developer may then easily attach an image to a test by calling +`Attachment.record()` and passing the image of interest. For example, to attach +an icon to a test as a PNG file: + +```swift +import Testing +import WinSDK + +@MainActor @Test func `attaching an icon`() throws { + let hIcon: HICON = ... + defer { + DestroyIcon(hIcon) + } + Attachment.record(hIcon, named: "my icon", as: .png) + // OR: Attachment.record(hIcon, named: "my icon.png") +} +``` + +## Source compatibility + +This change is additive only. + +## Integration with supporting tools + +Tools that handle attachments created by Swift Testing will gain support for +this functionality automatically and do not need to make any changes. + +## Future directions + +- Adding support for projecting COM classes as foreign-reference-counted Swift + classes. The C++ interop team is interested in implementing this feature, but + it is beyond the scope of this proposal. **If this feature is implemented in + the future**, it will cause types like `IWICBitmapSource` to be projected + directly into Swift instead of as `UnsafeMutablePointer` specializations. This + would be a source-breaking change for Swift Testing, but it would make COM + classes much easier to use in Swift. + + In the context of this proposal, `IWICBitmapSource` would be able to directly + conform to `AttachableAsIWICBitmapSource` and we would no longer need the + `_AttachableByAddressAsIWICBitmapSource` helper protocol. The + `AttachableAsIWICBitmapSource` protocol's `copyAttachableIWICBitmapSource()` + requirement would likely change to a property (i.e. + `var attachableIWICBitmapSource: IWICBitmapSource { get throws }`) as it would + be able to participate in Swift's automatic reference counting. + + The Swift team is tracking COM interop with [swiftlang/swift#84056](https://github.com/swiftlang/swift/issues/84056). + +- Adding support for managed (.NET or C#) image types. Support for managed types + on Windows would first require a new Swift/.NET or Swift/C# interop feature + and is therefore beyond the scope of this proposal. + +- Adding support for WinRT image types. WinRT is a thin wrapper around COM and + has C++ and .NET projections, neither of which are readily accessible from + Swift. It may be possible to add support for WinRT image types if COM interop + is implemented. + +- Adding support for other platforms. See [ST-0014](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md#future-directions) + for further discussion about supporting additional platforms. + +## Alternatives considered + +- Doing nothing. We have already added support for attaching images on Apple's + platforms, and Swift Testing is meant to be a cross-platform library, so we + should make a best effort to provide the same functionality on Windows and, + eventually, other platforms. + +- Using more Windows-/COM-like terminology and spelling, e.g. + `CloneAttachableBitmapSource()` instead of `copyAttachableIWICBitmapSource()`. + Swift API should follow Swift API guidelines, even when extending types and + calling functions implemented under other standards. + +- Making `IWICBitmapSource` conform directly to `Attachable`. As with `CGImage` + in [ST-0014](https://github.com/swiftlang/swift-evolution/blob/main/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md#alternatives-considered), + this would prevent us from including additional information (i.e. an instance + of `AttachableImageFormat`). Further, it would be difficult to correctly + manage the lifetime of Windows' 'image objects as they do not participate in + automatic reference counting. + +- Using the GDI+ type [`Gdiplus.Image`](https://learn.microsoft.com/en-us/windows/win32/api/gdiplusheaders/nl-gdiplusheaders-image) + as our currency type instead of `IWICBitmapSource`. This type is a C++ class + but is not a COM class, and so it is not projected into Swift except as + `OpaquePointer` which makes it unsafe to extend it with protocol conformances. + As well, GDI+ is a much older API than WIC and is not recommended by Microsoft + for new development. + +- Designing a platform-agnostic solution. This would likely require adding a + dependency on an open-source image package such as [ImageMagick](https://github.com/ImageMagick/ImageMagick). + Such a library would be a significant new dependency for the testing library + and the Swift toolchain at large. + +## Acknowledgments + +Thank you to @compnerd and the C++ interop team for their help with Windows and +the COM API. From 0c208005794b7e18926534f4318c8740e984164d Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Fri, 19 Sep 2025 13:56:44 -0500 Subject: [PATCH 4479/4563] ST-0015: Add link to forum review topic (#2961) --- .../testing/0015-image-attachments-in-swift-testing-windows.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0015-image-attachments-in-swift-testing-windows.md b/proposals/testing/0015-image-attachments-in-swift-testing-windows.md index 954b0f51a9..74549f0ba7 100644 --- a/proposals/testing/0015-image-attachments-in-swift-testing-windows.md +++ b/proposals/testing/0015-image-attachments-in-swift-testing-windows.md @@ -5,7 +5,7 @@ * Review Manager: [Stuart Montgomery](https://github.com/stmontgomery) * Status: **Active Review (September 19…29, 2025)** * Implementation: [swiftlang/swift-testing#1245](https://github.com/swiftlang/swift-testing/pull/1245), [swiftlang/swift-testing#1254](https://github.com/swiftlang/swift-testing/pull/1254), _et al_. -* Review: ([pitch](https://forums.swift.org/t/pitch-image-attachments-in-swift-testing-windows/81871)) +* Review: ([pitch](https://forums.swift.org/t/pitch-image-attachments-in-swift-testing-windows/81871)) ([review](https://forums.swift.org/t/st-0015-image-attachments-in-swift-testing-windows/82241)) ## Introduction From 152203aab0e2ab66ff81b84d76cbf772864fce5f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 22 Sep 2025 12:31:21 -0700 Subject: [PATCH 4480/4563] Initiate review of SE-0492 "Section Placement Control" --- .../{0nnn-section-control.md => 0492-section-control.md} | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename proposals/{0nnn-section-control.md => 0492-section-control.md} (99%) diff --git a/proposals/0nnn-section-control.md b/proposals/0492-section-control.md similarity index 99% rename from proposals/0nnn-section-control.md rename to proposals/0492-section-control.md index 74af21dbd3..1585f96da8 100644 --- a/proposals/0nnn-section-control.md +++ b/proposals/0492-section-control.md @@ -1,8 +1,10 @@ # Section Placement Control -* Proposal: [SE-0NNN](0nnn-section-control.md) +* Proposal: [SE-0492](0492-section-control.md) * Authors: [Kuba Mracek](https://github.com/kubamracek) -* Status: Pitch #3 +* Status: **Active review (September 22 ... October 6, 2025)** +* Implementation: available in recent `main` snapshots under the experimental feature `SymbolLinkageMarkers` and with undercored attribute names `@_section` and `@_used`. +* Review: TBD * Discussion threads: * Pitch #1: https://forums.swift.org/t/pitch-low-level-linkage-control-attributes-used-and-section/65877 * Pitch #2: https://forums.swift.org/t/pitch-2-low-level-linkage-control/69752 From 2666e940998f1639adbdd66ca6f792dfabdcad44 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 22 Sep 2025 12:35:33 -0700 Subject: [PATCH 4481/4563] Add review link for SE-0492 --- proposals/0492-section-control.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0492-section-control.md b/proposals/0492-section-control.md index 1585f96da8..0f84487b38 100644 --- a/proposals/0492-section-control.md +++ b/proposals/0492-section-control.md @@ -4,7 +4,7 @@ * Authors: [Kuba Mracek](https://github.com/kubamracek) * Status: **Active review (September 22 ... October 6, 2025)** * Implementation: available in recent `main` snapshots under the experimental feature `SymbolLinkageMarkers` and with undercored attribute names `@_section` and `@_used`. -* Review: TBD +* Review: [review](https://forums.swift.org/t/se-0492-section-placement-control/82289) * Discussion threads: * Pitch #1: https://forums.swift.org/t/pitch-low-level-linkage-control-attributes-used-and-section/65877 * Pitch #2: https://forums.swift.org/t/pitch-2-low-level-linkage-control/69752 From 10daeacd15e39d02cb24412e3919b0b8101ebd3d Mon Sep 17 00:00:00 2001 From: "Kuba (Brecka) Mracek" Date: Mon, 22 Sep 2025 13:23:30 -0700 Subject: [PATCH 4482/4563] Update 0492-section-control.md to avoid using a String in the example --- proposals/0492-section-control.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/proposals/0492-section-control.md b/proposals/0492-section-control.md index 0f84487b38..8fa1924e75 100644 --- a/proposals/0492-section-control.md +++ b/proposals/0492-section-control.md @@ -75,14 +75,13 @@ let myLinkerSetEntry: Int = Int.random(in: 0 ..< 10) // ❌ error @section("__DATA,mysection") var mutableVariable: Int = 42 // ✅ -// Some complex data types are allowed (static strings, functions) -typealias PluginData = (version: Int, name: String, initializer: @convention(c) ()->()) +// Some complex data types are allowed (tuples, function references) +typealias PluginData = (version: Int, identifier: UInt64, initializer: @convention(c) ()->()) @section("__DATA,plugins") @used let myPlugin: PluginData = ( version: 1, - name: "MyPlugin", initializer: { print("init") } ) ``` From 4467c59bb235b25910cbaccfba69cd10e959a26f Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Mon, 22 Sep 2025 18:33:11 -0400 Subject: [PATCH 4483/4563] Add defer async proposal (#2943) * Add defer async proposal * Update proposal in response to pitch discussion * Assign SE-0493 to async defer and schedule for review. --------- Co-authored-by: Holly Borla --- proposals/0493-defer-async.md | 132 ++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 proposals/0493-defer-async.md diff --git a/proposals/0493-defer-async.md b/proposals/0493-defer-async.md new file mode 100644 index 0000000000..619b0a2643 --- /dev/null +++ b/proposals/0493-defer-async.md @@ -0,0 +1,132 @@ +# Support `async` calls in `defer` bodies + +* Proposal: [SE-0493](0493-defer-async.md) +* Authors: [Freddy Kellison-Linn](https://github.com/Jumhyn) +* Review Manager: [Holly Borla](https://github.com/hborla) +* Status: **Active review (September 22 - October 6, 2025)** +* Implementation: [swiftlang/swift#83891](https://github.com/swiftlang/swift/pull/83891) +* Review: ([pitch](https://forums.swift.org/t/support-async-calls-in-defer-bodies/81790)) + +## Introduction + +This is a targeted proposal to introduce support for asynchronous calls within `defer` statements. Such calls must be marked with `await` as any other asynchronous call would be, and `defer` statements which do asynchronous work will be implicitly awaited at any relevant scope exit point. + +## Motivation + +The `defer` statement was introduced in Swift 2 (before Swift was even open source) as the method for performing scope-based cleanup in a reliable way. Whenever a lexical scope is exited, the bodies of prior `defer` statements within that scope are executed (in reverse order, in the case of multiple `defer` statements). + +```swift +func sendLog(_ message: String) async throws { + let localLog = FileHandle("log.txt") + + // Will be executed even if we throw + defer { localLog.close() } + + localLog.appendLine(message) + try await sendNetworkLog(message) +} +``` + +This lets cleanup operations be syntactically colocated with the corresponding setup while also preventing the need to manually insert the cleanup along every possible exit path. + +While this provides a convenient and less-bug-prone way to perform important cleanup, the bodies of `defer` statements are not permitted to do any asynchronous work. If you attempt to `await` something in the body of a `defer` statement, you'll get an error even if the enclosing context is `async`: + +```swift +func f() async { + await setUp() + // error: 'async' call cannot occur in a defer body + defer { await performAsyncTeardown() } + + try doSomething() +} +``` + +If a particular operation *requires* asynchronous cleanup, then there aren't any great options today. An author can either resort to inserting the cleanup on each exit path manually (risking that they or a future editor will miss a path), or else spawn a new top-level `Task` to perform the cleanup: + +```swift +defer { + // We'll clean this up... eventually + Task { await performAsyncTeardown() } +} +``` + +## Proposed solution + +This proposal allows `await` statements to appear in `defer` bodies whenever the enclosing context is already `async`. Whenever a scope is exited, the bodies of all prior `defer` statements will be executed in reverse order of declaration, just as before. The bodies of any `defer` statements containing asynchronous work will be `await`ed, and run to completion before the function returns. + +Thus, the example from **Motivation** above will become valid code: +```swift +func f() async { + await setUp() + defer { await performAsyncTeardown() } // OK + + try doSomething() +} +``` + +## Detailed design + +When a `defer` statement contains asynchronous work, we will generate an implicit `await` when it is called on scope exit. See **Alternatives Considered** for further discussion. + +We always require that the parent context of the `defer` be explicitly or implicitly `async` in order for `defer` to contain an `await`. That is, the following is not valid: + +```swift +func f() { + // error: 'async' call in a function that does not support concurrency + defer { await g() } +} +``` + +In positions where `async` can be inferred, such as for the types of closures, an `await` within the body of a `defer` is sufficient to infer `async`: + +```swift +// 'f' implicitly has type '() async -> ()' +let f = { + defer { await g() } +} +``` + +The body of a `defer` statement will always inherit the isolation of its enclosing scope, so an asynchronous `defer` body will never introduce *additional* suspension points beyond whatever suspension points are introduced by the functions it calls. + +## Source compatibility + +This change is additive and opt-in. Since no `defer` bodies today can do any asynchronous work, the behavior of existing code will not change. + +## ABI compatibility + +This proposal does not have any impact at the ABI level. It is purely an implementation detail. + +## Implications on adoption + +Adoping asynchronous `defer` is an implementation-level detail and does not have any implications on ABI or API stability. + +## Alternatives considered + +### Require some statement-level marking such as `defer async` + +We do not require any more source-level annotation besides the `await` that will appear on the actual line within the `defer` which invokes the asynchronous work. We could go further and require one to write something like: +```swift +defer async { + await fd.close() +} +``` + +This proposal declines to introduce such requirement. Because `defer` bodies are typically small, targeted cleanup work, we do not believe that substantial clarity is gained by requiring another marker which would remain local *to the `defer`* statement itself. Moreover, the enclosing context of such `defer` statements will *already* be required to be `async`. In the case of `func` declarations, this will be explicit. In the case of closures, this may be inferred, but will be no less implicit than the inference that already happens from having an `await` in a closure body. + +### Require some sort of explicit `await` marking on scope exit + +The decision to implicltly await asyncrhonous `defer` bodies has the potential to introduce unexpected suspension points within function bodies. This proposal takes the position that the implicit suspension points introduced by asynchronous `defer` bodies is almost entirely analagous to the [analysis](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0317-async-let.md#requiring-an-awaiton-any-execution-path-that-waits-for-an-async-let) provided by the `async let` proposal. Both of these proposals would require marking every possible control flow edge which exits a scope. + +If anything, the analysis here is even more favorable to `defer`. In the case of `async let` it is possible to have an implicit suspension point without `await` appearing anywhere in the source—with `defer`, any suspension point within the body will be marked with `await`. + +### Suppress task cancellation within `defer` bodies + +During discussion of the behavior expected from asynchronous `defer` bodies, one point raised was whether we ought to remove the ability of code within a `defer` to observe the current task's cancellation state. Under this proposal, no such change is adopted, and if the current task is cancelled then all code called from the `defer` will observe that cancellation (via `Task.isCancelled`, `Task.checkCancellation()`, etc.) just as it would if called from within the main function body. + +The alternative suggestion here noted that because task cancellation can sometimes cause code to be skipped, it is not in the general case appropriate to run necessary cleanup code within an already-cancelled task. For instance, if one wishes to run some cleanup on a timeout (via `Task.sleep`) or via an HTTP request or filesystem operation, these operations could interpret running in a cancelled task as an indication that they _should not perform the requested work_. + +We could, instead, notionally 'un-cancel' the current task if we enter a `defer` body: all code called from within the `defer` would observe `Task.isCancelled == true`, `Task.checkCancellation()` would not throw, etc. This would allow timeouts to continue to function and ensure that any downstream cleanup would not misinterpret task cancellation as an indication that it should early-exit. + +This proposal does not adopt such behavior, for a combination of reasons: +1. Synchronous `defer` bodies already observe cancellation 'normally', i.e., `Task.isCancelled` can be accessed within the body of a synchronous `defer`, and it will reflect the actual cancellation status of the enclosing task. While it is perhaps less likely that existing synchronous code exhibits behavior differences with respect to cancellation status, it would be undesirable if merely adding `await` in one part of a `defer` body could result in behavior changes for other, unrelated code in the same `defer` body. +2. We do not want a difference in behavior that could occur merely from moving existing code into a `defer`. Existing APIs which are sensitive to cancellation must already be used with care even in straight-line code where `defer` may not be used (since cancellation can happen at any time), and this proposal takes the position that such APIs are more appropriately addressed by a general `withCancellationIgnored { ... }` feature (or similar) as discussed in the pitch thread. From 8c5f155660a7b6fd87627528e20c3612a7b9dbcc Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Mon, 22 Sep 2025 15:39:07 -0700 Subject: [PATCH 4484/4563] Link SE-0493 review thread. (#2965) --- proposals/0493-defer-async.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0493-defer-async.md b/proposals/0493-defer-async.md index 619b0a2643..c460b1cbd4 100644 --- a/proposals/0493-defer-async.md +++ b/proposals/0493-defer-async.md @@ -5,7 +5,7 @@ * Review Manager: [Holly Borla](https://github.com/hborla) * Status: **Active review (September 22 - October 6, 2025)** * Implementation: [swiftlang/swift#83891](https://github.com/swiftlang/swift/pull/83891) -* Review: ([pitch](https://forums.swift.org/t/support-async-calls-in-defer-bodies/81790)) +* Review: ([pitch](https://forums.swift.org/t/support-async-calls-in-defer-bodies/81790)) ([review](https://forums.swift.org/t/se-0493-support-async-calls-in-defer-bodies/82293)) ## Introduction From e87f745125c0304be7bbcb3b08647ee34d902801 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 22 Sep 2025 20:09:24 -0400 Subject: [PATCH 4485/4563] Assign SE-0494 to the `isIdentical(to:)` proposal Also fix some of the header fields --- ...ntical-methods.md => 0494-add-is-identical-methods.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/{NNNN-add-is-identical-methods.md => 0494-add-is-identical-methods.md} (99%) diff --git a/proposals/NNNN-add-is-identical-methods.md b/proposals/0494-add-is-identical-methods.md similarity index 99% rename from proposals/NNNN-add-is-identical-methods.md rename to proposals/0494-add-is-identical-methods.md index 4eb0536cc6..b8679fa993 100644 --- a/proposals/NNNN-add-is-identical-methods.md +++ b/proposals/0494-add-is-identical-methods.md @@ -1,11 +1,11 @@ # Add `isIdentical(to:)` Methods for Quick Comparisons to Concrete Types -* Proposal: [SE-NNNN](NNNN-add-is-identical-methods.md) +* Proposal: [SE-0494](0494-add-is-identical-methods.md) * Authors: [Rick van Voorden](https://github.com/vanvoorden), [Karoy Lorentey](https://github.com/lorentey) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [John McCall](https://github.com/rjmccall) +* Status: **Active review (September 22nd...October 6th, 2025)** * Implementation: ([String, Substring](https://github.com/swiftlang/swift/pull/82055)), ([Array, ArraySlice, ContiguousArray](https://github.com/swiftlang/swift/pull/82438)), ([Dictionary, Set](https://github.com/swiftlang/swift/pull/82439)) -* Review: ([Pre-Pitch](https://forums.swift.org/t/-/78792)), ([Pitch #1](https://forums.swift.org/t/-/79145)), ([Pitch #2](https://forums.swift.org/t/-/80496)) +* Review: ([prepitch](https://forums.swift.org/t/-/78792)) ([first pitch](https://forums.swift.org/t/-/79145)) ([second pitch](https://forums.swift.org/t/-/80496)) ### Table of Contents From 8b998ad48d19d43758b90bbfb2248afd5aeda0c5 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 22 Sep 2025 20:14:13 -0400 Subject: [PATCH 4486/4563] Add review link to SE-0494 --- proposals/0494-add-is-identical-methods.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0494-add-is-identical-methods.md b/proposals/0494-add-is-identical-methods.md index b8679fa993..076d35f9a5 100644 --- a/proposals/0494-add-is-identical-methods.md +++ b/proposals/0494-add-is-identical-methods.md @@ -5,7 +5,7 @@ * Review Manager: [John McCall](https://github.com/rjmccall) * Status: **Active review (September 22nd...October 6th, 2025)** * Implementation: ([String, Substring](https://github.com/swiftlang/swift/pull/82055)), ([Array, ArraySlice, ContiguousArray](https://github.com/swiftlang/swift/pull/82438)), ([Dictionary, Set](https://github.com/swiftlang/swift/pull/82439)) -* Review: ([prepitch](https://forums.swift.org/t/-/78792)) ([first pitch](https://forums.swift.org/t/-/79145)) ([second pitch](https://forums.swift.org/t/-/80496)) +* Review: ([prepitch](https://forums.swift.org/t/-/78792)) ([first pitch](https://forums.swift.org/t/-/79145)) ([second pitch](https://forums.swift.org/t/-/80496)) ([review](https://forums.swift.org/t/se-0494-add-isidentical-to-methods-for-quick-comparisons-to-concrete-types/82296)) ### Table of Contents From 5c706ee498f6165e17cc327cfd93469f5a199411 Mon Sep 17 00:00:00 2001 From: omochimetaru Date: Tue, 23 Sep 2025 11:55:25 +0900 Subject: [PATCH 4487/4563] fix trait syntax --- proposals/0450-swiftpm-package-traits.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0450-swiftpm-package-traits.md b/proposals/0450-swiftpm-package-traits.md index 8670adc75c..70950f32d4 100644 --- a/proposals/0450-swiftpm-package-traits.md +++ b/proposals/0450-swiftpm-package-traits.md @@ -75,7 +75,7 @@ let package = Package( enabledTraits: [ // Other traits that are enabled when this trait is being enabled "Foo", ] - ) + ), .trait( name: "FooBar", enabledTraits: [ @@ -92,7 +92,7 @@ let package = Package( When depending on a package the `default` trait is enabled. However, the enabled traits can be customized by passing a set of enabled traits when declaring the dependency. When specifying the enabled traits of the dependencies the -`.default` trait can be passed which will enable the default trait. The below +`.defaults` trait can be passed which will enable the default trait. The below example enables the default trait and the additional `SomeTrait` of the package. ```swift @@ -101,7 +101,7 @@ dependencies: [ url: "https://github.com/Org/SomePackage.git", from: "1.0.0", traits: [ - .default, + .defaults, "SomeTrait" ] ), @@ -130,7 +130,7 @@ dependencies: [ url: "https://github.com/Org/SomePackage.git", from: "1.0.0", traits:[ - .trait("SomeOtherTrait", condition: .when(traits: ["Foo"])), + .trait(name: "SomeOtherTrait", condition: .when(traits: ["Foo"])), ] ), ] From 343d43a67568bf71869bf80a571c85354abcc05d Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 23 Sep 2025 11:06:51 -0400 Subject: [PATCH 4488/4563] Update proposal --- proposals/testing/NNNN-test-cancellation.md | 159 ++++++-------------- 1 file changed, 47 insertions(+), 112 deletions(-) diff --git a/proposals/testing/NNNN-test-cancellation.md b/proposals/testing/NNNN-test-cancellation.md index 20794db902..8e79189e69 100644 --- a/proposals/testing/NNNN-test-cancellation.md +++ b/proposals/testing/NNNN-test-cancellation.md @@ -16,10 +16,8 @@ using the [`.enabled(if:)`](https://developer.apple.com/documentation/testing/tr etc. family of traits: ```swift -@Test(.enabled(if: Tyrannosaurus.isTheLizardKing)) -func `Tyrannosaurus is scary`() { - let dino = Tyrannosaurus() - #expect(dino.isScary) +@Test(.disabled(if: Species.all.in(: .dinosauria).isEmpty) +func `Are all dinosaurs extinct?`() { // ... } ``` @@ -45,10 +43,13 @@ parameterized test function. ## Proposed solution A static `cancel()` function is added to the [`Test`](https://developer.apple.com/documentation/testing/test) -and [`Test.Case`](https://developer.apple.com/documentation/testing/test/case) -types. When a test author calls these functions from within the body of a test -(or from within the implementation of a trait, e.g. from [`prepare(for:)`](https://developer.apple.com/documentation/testing/trait/prepare(for:))), -Swift Testing cancels the currently-running test or test case, respectively. +type. When a test author calls this function from within the body of a test (or +from within the implementation of a trait, e.g. from [`prepare(for:)`](https://developer.apple.com/documentation/testing/trait/prepare(for:))), +Swift Testing cancels the currently-running test. + +Parameterized tests are special-cased: if the currently-running test is +parameterized and you call `cancel()`, only the current test case is cancelled +and other test cases in the same test continue to run. ### Relationship between tasks and tests @@ -79,24 +80,24 @@ is, unsurprisingly, an unsafe interface. ## Detailed design -New static members are added to [`Test`](https://developer.apple.com/documentation/testing/test) -and [`Test.Case`](https://developer.apple.com/documentation/testing/test/case): +A new static function is added to [`Test`](https://developer.apple.com/documentation/testing/test): ```swift extension Test { - /// Cancel the current test. + /// Cancel the current test or test case. /// /// - Parameters: - /// - comment: A comment describing why you are cancelling the test. + /// - comment: A comment describing why you are cancelling the test or test + /// case. /// - sourceLocation: The source location to which the testing library will /// attribute the cancellation. /// - /// - Throws: An error indicating that the current test case has been + /// - Throws: An error indicating that the current test or test case has been /// cancelled. /// - /// The testing library runs each test in its own task. When you call this - /// function, the testing library cancels the task associated with the current - /// test: + /// The testing library runs each test and each test case in its own task. + /// When you call this function, the testing library cancels the task + /// associated with the current test: /// /// ```swift /// @Test func `Food truck is well-stocked`() throws { @@ -107,74 +108,25 @@ extension Test { /// } /// ``` /// - /// If the current test is parameterized, all of its pending and running test - /// cases are cancelled. If the current test is a suite, all of its pending - /// and running tests are cancelled. If you have already cancelled the current - /// test or if it has already finished running, this function throws an error - /// but does not attempt to cancel the test a second time. - /// - /// - Important: If the current task is not associated with a test (for - /// example, because it was created with [`Task.detached(name:priority:operation:)`](https://developer.apple.com/documentation/swift/task/detached(name:priority:operation:)-795w1)) - /// this function records an issue and cancels the current task. + /// If the current test is a parameterized test function, this function + /// instead cancels the current test case. Other test cases in the test + /// function are not affected. /// - /// To cancel the current test case but leave other test cases of the current - /// test alone, call ``Test/Case/cancel(_:sourceLocation:)`` instead. - public static func cancel(_ comment: Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation) throws -> Never -} - -extension Test.Case { - /// Cancel the current test case. - /// - /// - Parameters: - /// - comment: A comment describing why you are cancelling the test case. - /// - sourceLocation: The source location to which the testing library will - /// attribute the cancellation. - /// - /// - Throws: An error indicating that the current test case has been - /// cancelled. - /// - /// The testing library runs each test case of a test in its own task. When - /// you call this function, the testing library cancels the task associated - /// with the current test case: - /// - /// ```swift - /// @Test(arguments: [Food.burger, .fries, .iceCream]) - /// func `Food truck is well-stocked`(_ food: Food) throws { - /// if food == .iceCream && Season.current == .winter { - /// try Test.Case.cancel("It's too cold for ice cream.") - /// } - /// // ... - /// } - /// ``` + /// If the current test is a suite, the testing library cancels all of its + /// pending and running tests. /// - /// If the current test is parameterized, the test's other test cases continue - /// running. If the current test case has already been cancelled, this - /// function throws an error but does not attempt to cancel the test case a + /// If you have already cancelled the current test or if it has already + /// finished running, this function throws an error to indicate that the + /// current test has been cancelled, but does not attempt to cancel the test a /// second time. /// - /// - Important: If the current task is not associated with a test case (for + /// - Important: If the current task is not associated with a test (for /// example, because it was created with [`Task.detached(name:priority:operation:)`](https://developer.apple.com/documentation/swift/task/detached(name:priority:operation:)-795w1)) /// this function records an issue and cancels the current task. - /// - /// To cancel all test cases in the current test, call - /// ``Test/cancel(_:sourceLocation:)`` instead. public static func cancel(_ comment: Comment? = nil, sourceLocation: SourceLocation = #_sourceLocation) throws -> Never } ``` -These functions behave similarly, and are distinguished by the level of the test -to which they apply: - -- `Test.cancel()` cancels the current test. - - If the current test is parameterized, it implicitly cancels all running and - pending test cases of said test. - - If the current test is a suite (only applicable during trait evaluation), it - recursively cancels all test suites and test functions within said suite. -- `Test.Case.cancel()` cancels the current test case. - - If the current test is parameterized, other test cases are unaffected. - - If the current test is _not_ parameterized, `Test.Case.cancel()` behaves the - same as `Test.cancel()`. - Cancelling a test or test case implicitly cancels its associated task (and any child tasks thereof) as if [`Task.cancel()`](https://developer.apple.com/documentation/swift/task/cancel()) were called on that task. @@ -224,31 +176,6 @@ the test or test case. Hence, if a test or test case throws an instance of _and_ the current task has been cancelled, it is treated as if the test or test case were cancelled. -### Support for XCTSkip - -XCTest has an approximate equivalent to test cancellation: throwing an instance -of [`XCTSkip`](https://developer.apple.com/documentation/xctest/xctskip-swift.struct) -from the body of an XCTest test function causes that test function to be skipped -(equivalent to cancelling it). - -While we encourage developers to adopt `Test.cancel()` and `Test.Case.cancel()`, -we recognize the need for interoperability with XCTest. As such, Swift Testing -will recognize when a test or test case throws an instance of -[`XCTSkip`](https://developer.apple.com/documentation/xctest/xctskip-swift.struct) -and will treat it as cancelling the test or test case. - -An instance of [`XCTSkip`](https://developer.apple.com/documentation/xctest/xctskip-swift.struct) -must be caught by Swift Testing in order for it to cancel the current test. It -is not sufficient to create and discard an instance of this error type or to -catch one before Swift Testing can catch it. This behavior is consistent with -that of XCTest. - -> [!NOTE] -> This compatibility does **not** extend to Objective-C. In Objective-C, XCTest -> implements [`XCTSkip()`](https://developer.apple.com/documentation/xctest/xctskip-c.macro?language=objc) -> as a macro that throws an Objective-C exception. Exceptions are not supported -> in Swift, and Swift Testing does not attempt to catch these exceptions. - ### Interaction with recorded issues If you cancel a test or test case that has previously recorded an issue, that @@ -264,19 +191,7 @@ To cancel the current test case and let other test cases run: @Test(arguments: Species.all(in: .dinosauria)) func `Are all dinosaurs extinct?`(_ species: Species) throws { if species.in(.aves) { - try Test.Case.cancel("\(species) is birds!") - } - // ... -} -``` - -Or, to cancel all remaining test cases in the current test: - -```swift -@Test(arguments: Species.all(in: .dinosauria)) -func `Are all dinosaurs extinct?`(_ species: Species) throws { - if species.is(.godzilla) { - try Test.cancel("Forget about unit tests! Run for your life!") + try Test.cancel("\(species) is birds!") } // ... } @@ -317,6 +232,10 @@ location passed to `cancel(_:sourceLocation:)`: These new fields are populated for the new event kinds as well as other event kinds that can populate them. +An event of kind `"testCancelled"` is posted any time an entire test function or +test suite is cancelled. An event of kind `"testCaseCancelled"` is posted any +time a single test case is cancelled. + These new event kinds and fields will be included in the next revision of the JSON schema (currently expected to be schema version `"6.3"`). @@ -328,6 +247,10 @@ JSON schema (currently expected to be schema version `"6.3"`). and [`Task.checkCancellation()`](https://developer.apple.com/documentation/swift/task/checkcancellation()) already work in a test. +- Adding a `Test.Case.cancelAll()` interface that explicitly cancels all test + cases in a test function. We want to further evaluate the use cases and + semantics for such a function before we commit to introducing it as API. + ## Alternatives considered - Doing nothing. While we do want test authors to use [`.enabled(if:)`](https://developer.apple.com/documentation/testing/trait/enabled(if:_:sourcelocation:)) @@ -387,6 +310,18 @@ JSON schema (currently expected to be schema version `"6.3"`). With that said, [`UnsafeCurrentTask.cancel()`](https://developer.apple.com/documentation/swift/unsafecurrenttask/cancel()) _does_ cancel the test or test case associated with the current task. + +- Providing both `Test.cancel()` and `Test.Case.cancel()`, with `Test.cancel()` + _always_ cancelling the current test in its entirety and `Test.Case.cancel()` + _always_ cancelling the current test _case_ and leaving other test cases + alone. + + We have received pitch feedback from multiple test authors indicating that + they could introduce subtle bugs while refactoring test functions into + parameterized test functions. If they had written `Test.cancel()` and forgot + to change the call to `Test.Case.cancel()` when refactoring, they could + introduce a bug causing none of their test cases to run (because the entire + test is cancelled instead of just the current test case). ## Acknowledgments From f52bc4bf8704342aceb1c2397cdd6df05fe9dc16 Mon Sep 17 00:00:00 2001 From: Ali AlSalman Date: Tue, 23 Sep 2025 20:05:39 +0300 Subject: [PATCH 4489/4563] Update README.md add 6.2 release --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e1f5fdb53b..47a172b81a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This repository tracks the ongoing evolution of the Swift programming language, | Version | Announced | Released | | :-------- | :----------------------------------------------------------------------- | :----------------------------------------------------------- | -| Swift 6.2 | [2025-03-08](https://forums.swift.org/t/swift-6-2-release-process/78371) | | +| Swift 6.2 | [2025-03-08](https://forums.swift.org/t/swift-6-2-release-process/78371) | [2025-09-15](https://www.swift.org/blog/swift-6.2-released/) | | Swift 6.1 | [2024-10-17](https://forums.swift.org/t/swift-6-1-release-process/75442) | [2025-03-31](https://www.swift.org/blog/swift-6.1-released/) | | Swift 6.0 | [2024-02-22](https://forums.swift.org/t/swift-6-0-release-process/70220) | [2024-09-17](https://www.swift.org/blog/announcing-swift-6/) | | Swift 5.10 | [2023-08-23](https://forums.swift.org/t/swift-5-10-release-process/66911) | [2024-03-05](https://www.swift.org/blog/swift-5.10-released/) | From bcf0acad5f45b9461d86499817d64d20fd0127b6 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Tue, 23 Sep 2025 12:07:46 -0700 Subject: [PATCH 4490/4563] SE-0492: Clarify wording (built-in -> standard) and code snippets --- proposals/0492-section-control.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/proposals/0492-section-control.md b/proposals/0492-section-control.md index 8fa1924e75..ac8a9d05e8 100644 --- a/proposals/0492-section-control.md +++ b/proposals/0492-section-control.md @@ -92,7 +92,7 @@ Different object file formats (ELF, Mach-O, COFF) have different restrictions an ```swift #if objectFileFormat(ELF) -@section(".mysection") +@section("mysection") #elseif objectFileFormat(MachO) @section("__DATA,mysection") #endif @@ -190,11 +190,11 @@ The effects described above are applied to the storage symbols and don’t gener ### Constant expressions -Swift currently does not have a formal notion of a **constant expression**, i.e. an expression with a syntactic form that *guarantees the ability to know it's value at compile-time*. This proposal provides a definition of a "bare minimum" constant expression, with the understanding that this does not cover the language needs in generality, and the expectation that the Swift compiler and language will keep expanding the allowed forms of constant expressions. See [Generalized constant values and expressions](#generalized-constant-values-and-expressions) in Future Directions for further discussion on this. +Swift currently does not have a formal notion of a **constant expression**, i.e. an expression with a syntactic form that *guarantees the ability to know it's value at compile-time*. This proposal provides a definition of a "bare minimum" constant expression, with the understanding that this does not cover the language needs in generality, and with the expectation that the Swift compiler and language will keep expanding the allowed forms of constant expressions in the future. See [Generalized constant values and expressions](#generalized-constant-values-and-expressions) in Future Directions for further discussion on this. This proposal defines a **constant expression** as being one of: -- an integer literal using any of built-in integer types (Int, UInt, Int8/16/32/64/128, UInt8/16/32/64/128) +- an integer literal using any of standard integer types (Int, UInt, Int8/16/32/64/128, UInt8/16/32/64/128) - a floating-point literal of type Float or Double - a boolean literal of type Bool - a direct reference to a non-generic function using its name (the function itself is not generic, and also it must not be defined in a generic context) @@ -202,7 +202,7 @@ This proposal defines a **constant expression** as being one of: - a tuple composed of only other constant expressions - an array literal of type InlineArray composed of only other constant expressions -Explicitly, this definition currently does **not allow** any operators, using any user-defined named types, any other built-in type (e.g. strings, dictionaries, sets), using closures, or referencing any variables by name. See below for examples of valid and invalid constant expressions: +Explicitly, this definition currently does **not allow** any operators, using any user-defined named types, any other standard type (e.g. strings, dictionaries, sets), using closures, or referencing any variables by name. See below for examples of valid and invalid constant expressions: ```swift @section("...") let a = 42 // ✅ @@ -211,7 +211,7 @@ Explicitly, this definition currently does **not allow** any operators, using an @section("...") let d = Int.max // ❌ not a literal @section("...") let e: UInt8 = 42 // ✅ @section("...") let f = UInt8(42) // ❌ not a literal -@section("...") let g: MyCustomExpressibleByIntegerLiteral = 42 // ❌ not a built-in type +@section("...") let g: MyCustomExpressibleByIntegerLiteral = 42 // ❌ not a standard type @section("...") let composition1 = (1, 2, 3, 2.718, true) // ✅ @section("...") let composition2 = (1, 2, Int.max) // ❌ tuple component not constant @@ -321,7 +321,7 @@ In some cases, it’s not possible to differentiate on the OS to support multipl #if objectFileFormat(MachO) @section("__DATA_CONST,mysection") #elseif objectFileFormat(ELF) -@section(".mysection") +@section("mysection") #endif let value = ... ``` @@ -402,7 +402,7 @@ The requirement to only use string literals as the section names could be lifted ```swift #if objectFileFormat(ELF) -let mySectionName = ".mysection" // required to be a compile-time value +let mySectionName = "mysection" // required to be a compile-time value #elseif objectFileFormat(MachO) let mySectionName = "__DATA,mysection" // required to be a compile-time value #endif From 5949add13f255d3edaba045d7a0525e1fd75694d Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 24 Sep 2025 17:53:59 -0700 Subject: [PATCH 4491/4563] Revise SE-0491 with carveout for generic params (#2970) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the review, Slava Pestov explained that it didn’t make sense to try to apply a module selector to a member type whose base was a generic parameter, as the member type will always represent the union of all associated types by that name. Edit the proposal to specify that module selectors are not valid in this position. --- proposals/0491-module-selectors.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/proposals/0491-module-selectors.md b/proposals/0491-module-selectors.md index 7d2c769591..60bcd020da 100644 --- a/proposals/0491-module-selectors.md +++ b/proposals/0491-module-selectors.md @@ -476,6 +476,28 @@ some other language feature has ruled out. For example, if a declaration is inaccessible because of access control or hasn't been imported into the current source file, a module selector will not allow it to be accessed. +#### Member types of type parameters + +A member type of a type parameter must not be qualified by a module selector. + +```swift +func fn(_: T) where T.Swift::ID == Int { // not allowed + ... +} +``` + +This is because, when a generic parameter conforms to two protocols that have +associated types with the same name, the member type actually refers to *both* +of those associated types. It doesn't make sense to use a module name to select +one associated type or the other--it will always encompass both of them. + +(In some cases, a type parameter's member type might end up referring to a +concrete type—typically a typealias in a protocol extension–which +theoretically *could* be disambiguated in this way. However, in these +situations you could always use the protocol instead of the generic parameter +as the base (and apply a module selector to it if needed), so we've chosen not +to make an exception for them.) + ## Source compatibility This change is purely additive; it only affects the behavior of code which uses From e8e3cb68ae699e905be224cbf4ca179971d00bb6 Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 24 Sep 2025 20:56:33 -0400 Subject: [PATCH 4492/4563] Extend review period (#2971) --- proposals/0491-module-selectors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0491-module-selectors.md b/proposals/0491-module-selectors.md index 60bcd020da..d609228a11 100644 --- a/proposals/0491-module-selectors.md +++ b/proposals/0491-module-selectors.md @@ -3,7 +3,7 @@ * Proposal: [SE-0491](0491-module-selectors.md) * Authors: [Becca Royal-Gordon](https://github.com/beccadax) * Review Manager: [Freddy Kellison-Linn](https) -* Status: **Active review (September 13 ... September 26, 2025)** +* Status: **Active review (September 13 ... September 30, 2025)** * Bug: [swiftlang/swift#53580](https://github.com/swiftlang/swift/issues/53580) (SR-11183) * Implementation: [swiftlang/swift#34556](https://github.com/swiftlang/swift/pull/34556) * Review: ([pitch](https://forums.swift.org/t/pitch-module-selectors/80835)) ([review](https://forums.swift.org/t/se-0491-module-selectors-for-name-disambiguation/82124)) From 97cd4b2c511db7e46d04a3f18c7a36051ce63f74 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Wed, 24 Sep 2025 20:54:58 -0700 Subject: [PATCH 4493/4563] SE-0492: Clarify the intent of using section names verbatim --- proposals/0492-section-control.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/proposals/0492-section-control.md b/proposals/0492-section-control.md index ac8a9d05e8..875a9b3ba2 100644 --- a/proposals/0492-section-control.md +++ b/proposals/0492-section-control.md @@ -88,7 +88,7 @@ let myPlugin: PluginData = ( On top of specifying a custom section name with the `@section` attribute, marking a variable as `@used` is needed to prevent otherwise unused symbols from being removed by the compiler. When using section placement to e.g. implement linker sets, such values are typically going to have no usage at compile time, and at the same time they should not be exposed in public interface of libraries (not be made public), therefore we the need the `@used` attribute. -Different object file formats (ELF, Mach-O, COFF) have different restrictions and rules on what are valid section names, and cross-platform code will have to use different names for different file formats. To support that, custom section names can be specified as a string literal. The name will be directly set for the symbol in the resulting object file, without any processing. A new `#if objectFileFormat(...)` conditional compilation directive will be provided to support conditionalizing based on the file format: +Different object file formats (ELF, Mach-O, COFF) have different restrictions and rules on what are valid section names, and cross-platform code will have to use different names for different file formats. To support that, custom section names can be specified as a string literal. The string will be used directly, without any processing, as the section name for the symbol. A new `#if objectFileFormat(...)` conditional compilation directive will be provided to support conditionalizing based on the file format: ```swift #if objectFileFormat(ELF) @@ -180,13 +180,14 @@ When allowed, the `@section` attribute on a variable declaration has the followi 1. The variable’s initializer expression is going to be constant folded at compile-time, and assigned as the initial value to the storage symbol for the variable, i.e. the variable will be **statically initialized**. The variable’s value will not be lazily computed at runtime, and it will not use the one-time initialization helper code and token. If that’s not possible, an error is diagnosed. 2. The storage symbol for the variable will be placed into a custom section with the specified name. + - Concretely, the section name string value will be set verbatim as a section specifier for the storage symbol at the LLVM IR level of the compiler. This means that any special behavior that the optimizer, the backend, the assembler or the linker applies based on known section names (or attributes specified as suffixes on the section name) will apply. 3. If applied to a global that is declared as part of top-level executable code (i.e. main.swift), the usual non-top-level-code initialization behavior is applied to the global. I.e. the variable is not sequentially initialized at startup. When allowed, the `@used` attribute on a variable declaration has the following effect: 1. The storage symbol for the variable will be marked as “do not dead-strip”. -The effects described above are applied to the storage symbols and don’t generally affect optimizations and other transformations in the compiler. For example, the compiler is still allowed to propagate and copy a the constant value to code that uses the value, so there’s no guarantee that a value stored into a global with a custom section will not be propagated and “leak” outside of the section. The `@used` annotation, however, does inform the optimizer that such a variable cannot be removed, even when it doesn’t have any observed users or even if it’s inaccessible due to language rules (e.g. if it’s a private static member on an otherwise empty type). +The effects described above are applied to the storage symbols and don’t generally affect optimizations and other transformations in the compiler. For example, the compiler is still allowed to propagate and copy a constant value of a `let` variable to code that uses the variable, therefore there’s no guarantee that a value stored into a global with a custom section will not be propagated and “leak” outside of the section. The `@used` annotation, however, does inform the optimizer that such a variable cannot be removed, even when it doesn’t have any observed users or even if it’s inaccessible due to language rules (e.g. if it’s a private static member on an otherwise empty type). ### Constant expressions From 40115fa4aae9e849635ec9f7e6ba513ff674b25d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 25 Sep 2025 08:31:57 -0700 Subject: [PATCH 4494/4563] [Pitch] C compatible functions and enums with `@c` (#2813) * [Pitch] Base proposal to formalize @cdecl * [Pitch] Update @cdecl proposal after pitch and implementation * Mark the @cdecl proposal as ready for review * cdecl: apply early LSG comments * Update and rename NNNN-cdecl.md to 0495-cdecl.md Assign SE-0495 --------- Co-authored-by: Stephen Canon --- proposals/0495-cdecl.md | 228 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 proposals/0495-cdecl.md diff --git a/proposals/0495-cdecl.md b/proposals/0495-cdecl.md new file mode 100644 index 0000000000..1eaa1195cb --- /dev/null +++ b/proposals/0495-cdecl.md @@ -0,0 +1,228 @@ +# C compatible functions and enums + +* Proposal: [SE-0495](0495-cdecl.md) +* Author: [Alexis Laferrière](https://github.com/xymus) +* Review Manager: [Steve Canon](https://github.com/stephentyrone) +* Status: **Ready for Review** +* Implementation: On `main` with the experimental feature flags `CDecl` for `@c`, and `CImplementation` for `@c @implementation`. With the exception of the `@objc` support for global functions which is available under the name `@_cdecl`. +* Review: ([pitch](https://forums.swift.org/t/pitch-formalize-cdecl/79557)) + +## Introduction + +Implementing a C function in Swift eases integration of Swift and C code. This proposal introduces `@c` to mark Swift functions as callable from C, and enums as representable in C. It provides the same behavior under the `@objc` attribute for Objective-C compatible global functions. + +To expose the function to C clients, this proposal adds a new C block to the compatibility header where `@c` functions are printed. As an alternative, this proposal extends `@implementation` support to global functions, allowing users to declare the function in a hand-written C header. + +> Note: This proposal aims to formalize and extend the long experimental `@_cdecl`. While experimental this attribute has been widely in use so we will refer to it as needed for clarity in this document. + +## Motivation + +Swift already offers some integration with C, notably it can import declarations from C headers and call C functions. Swift also already offers a wide integration with Objective-C: import headers, call methods, print the compatibility header, and implement Objective-C classes in Swift with `@implementation`. These language features have proven to be useful for integration with Objective-C. Offering a similar language support for C will further ease integrating Swift and C, and encourage incremental adoption of Swift in existing C code bases. + +Offering a C compatibility type-checking ensures `@c` functions only reference types representable in C. This type-checking helps cross-platform development as one can define a `@c` while working from an Objective-C compatible environment and still see the restrictions from a C only environment. + +Printing the C representation of `@c` functions in a C header will enable a mixed-source software to easily call the functions from C code. The current generated header is limited to Objective-C and C++ content. Adding a section for C compatible clients will extend its usefulness to this language. + +Extending `@implementation` to support global C functions will provide support to developers through type-checking by ensuring the C declaration matches the corresponding definition in Swift. + +## Proposed solution + +We propose to introduce the new `@c` attribute for global functions and enums, extend `@objc` for global functions, and support `@c @implementation`. + +### `@c` global functions + +Introduce the `@c` attribute to mark a global function as a C function implemented in Swift. That function uses the C calling convention and its signature can only reference types representable in C. Its body is implemented in Swift as usual. The signature of that function is printed in the compatibility header using C corresponding types, allowing C source code to import the compatibility header and call the function. + +A `@c` function is declared with an optional C function name, by default the Swift base name is used as C name: +```swift +@c func foo() {} + +@c(mirrorCName) +func mirror(value: CInt) -> CInt { return value } +``` + +### `@objc` global functions + +Extends the `@objc` attribute to be accepted on a global function. It offers the same behavior as `@c` while allowing the signature to reference types representable in Objective-C. The signature of a `@objc` function is printed in the compatibility header using corresponding Objective-C types. + +A `@objc` function is declared with an optional C compatible name without parameter labels: + +```swift +@objc func bar() {} + +@objc(mirrorObjCName) +func objectMirror(value: NSObject) -> NSObject { return value } +``` + +> Note: The attribute `@objc` can be used on a global function to replace `@_cdecl` as it preserves the behavior of the unofficial attribute. + +### `@c` enums + +Accept `@c` on enums to mark them as C compatible. These enums can be referenced from `@c` or `@objc` functions. They are printed in the compatibility header as a C enum or a similar type. + +A `@c` enum may declare a custom C name, and must declare an integer raw type compatible with C: + +```swift +@c +enum CEnum: CInt { + case a + case b +} +``` + +The attribute `@objc` is already accepted on enums. These enums qualify as an Objective-C representable type and are usable from `@objc` global function signatures but not from `@c` functions. + +### `@c @implementation` global functions + +Extend support for the `@implementation` attribute, introduced in [SE-0436](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0436-objc-implementation.md), to global functions marked with either `@c` or `@objc`. These functions are declared in an imported C or Objective-C header, while the Swift function provides their implementation. Type-checking ensures the declaration matches the implementation signature in Swift. Functions marked `@implementation` are not printed in the compatibility header. + +The declaration and implementation are distinct across languages and must have matching names and types: + +```c +// C header +int cImplMirror(int value); +``` + +```swift +// Swift sources +@c @implementation +func cImplMirror(_ value: CInt) -> CInt { return value } +``` + +## Detailed design + +This proposal extends the language syntax, type-checking for both global functions and enums, supporting logic for `@implementation`, and the content printed to the compatibility header. + +### Syntax + +Required syntax changes involve one new attribute and the reuse of two existing attributes. + +* Introduce the attribute `@c` accepted on global functions and enums. It accepts one optional parameter specifying the corresponding C name of the declaration. The C name defaults to the Swift base identifier of the declaration, it doesn't consider parameter names. + +* Extend `@objc` to be accepted on global functions, using the optional parameter to define the C function name instead of the Objective-C symbol. Again here, the C function name defaults to the base identifier of the Swift function. + +* Extend `@implementation` to be accepted on global functions marked with either `@c` or `@objc`. + +### Type-checking of global functions signatures + +Global functions marked with `@c` or `@objc` need type-checking to ensure types used in their signature are representable in the target language. + +The following types are accepted in the signature of `@c` global functions: + +- Primitive types defined in the standard library: Int, UInt, Int8, Float, Double, Bool, etc. +- Pointers defined in the standard library: OpaquePointer and the variants of Unsafe{Mutable}{Raw}Pointer. +- C primitive types defined in the standard library: CChar, CInt, CUnsignedInt, CLong, CLongLong, etc. +- Function references using the C calling convention, marked with `@convention(c)`. +- SIMD types where the scalar is representable in C. +- Enums marked with `@c`. +- Imported C types. + +In addition to the types above, the following types should be accepted in the signature of `@objc` global functions: + +- `@objc` classes, enums and protocols. +- Imported Objective-C types. + +For both `@c` and `@objc` global functions, type-checking should reject: + +- Optional non-pointer types. +- Non-`@objc` classes. +- Swift structs. +- Non-`@c` enums. +- Protocol existentials. + +### Type-checking of `@c` enums + +For `@c` enums to be representable in C, type-checking should ensure the raw type is defined to an integer value that is itself representable in C. This is the same check as already applied to `@objc` enums. + +### `@c @implementation` and `@objc @implementation` + +A global function marked with `@c @implementation` or `@objc @implementation` needs to be associated with the corresponding declaration from imported headers. The compiler should report uses without a corresponding C declaration or inconsistencies in the match. + +### Compatibility header printing + +The compiler should print a single compatibility header for all languages, adding a block specific to C as is currently done for Objective-C and C++. Printing the header is requested using the preexisting compiler flags: `-emit-objc-header`, `-emit-objc-header-path` or `-emit-clang-header-path`. + +This C block declares the `@c` global functions and enums using C types, while `@objc` functions are printed in the Objective-C block with Objective-C types. + +The C block should be printed in a way it's parseable by compilers targeting C, Objective-C and C++. To do so, ensure that only C types are printed, there is no unprotected use of non-standard C features, and the syntax is C compatible. + +### Type mapping to C + +When printing `@c` functions in the compatibility header, Swift types are mapped to their corresponding C representations. Here is a partial mapping: + +- Swift `Bool` maps to C `bool` from `stdbool.h`. +- Swift `Int` maps to `ptrdiff_t`, `UInt` to `size_t`, `Int8` to `int8_t`, `UInt32` to `uint32_t`, etc. +- Swift floating-point types `Float` and `Double` map to C `float` and `double` respectively. +- Swift's version of C primitive types map to their C equivalents: `CInt` to `int`, `CLong` to long, etc. +- Swift SIMD types map to vector types printed as needed in the compatibility header. +- Function references with `@convention(c)` map to C function pointers. + +## Source compatibility + +This proposal preserves all source compatibility as the new features are opt-in. + +Existing adopters of `@_cdecl` can replace the attribute with `@objc` to preserve the same behavior. Alternatively, they can update it to `@c` to get the more restrictive C compatibility check. Using `@c` will however change how the corresponding C function is printed in the compatibility header so it may be necessary to update sources calling into the function. + +## ABI compatibility + +Marking a global function with `@c` or `@objc` makes it use the C calling convention. Adding or removing these attributes on a function is an ABI breaking change. Updating existing `@_cdecl` to `@objc` or `@c` is ABI stable. + +Adding or removing the `@c` attribute on an enum is ABI stable, but changing its raw type is not. + +Moving the implementation of an existing C function to Swift using `@c` or `@objc @implementation` within the same binary is ABI stable. + +## Implications on adoption + +The changes proposed here are backwards compatible with older runtimes. + +## Future directions + +This work opens the door to closer interoperability with the C language. + +### `@c` struct support + +A valuable addition would be supporting C compatible structs declared in Swift. We could consider exposing them to C as opaque data, or produce structs with a memory layout representable in C. Both have different use cases and advantages: + +* Using an opaque data representation would hide Swift details from C. Hiding these details allows the Swift struct to reference any Swift types and language features, without the concern of finding an equivalent C representation. This approach should be enough for the standard references to user data in C APIs. + +* Producing a Swift struct with a C memory layout would give the C code direct access to the data. This struct could be printed in the compatibility header as a normal C struct. This approach would need to be more restrictive on the Swift types and features used in the struct, starting with accepting only C representable types. + +### Custom calling conventions + +Defining a custom calling convention on a function may be a requirement by some API for callback functions and such. + +With this proposal, it should be possible to declare a custom calling convention by using `@c @implementation`. This allows to apply any existing C attribute on the definition in the C header. + +We could allow specifying the C calling conventions from Swift code with further work. Either by extending `@convention` to be accepted on `@c` and `@objc` global functions, and have it accept a wider set of conventions. Or by adding an optional named parameter to the `@c` attribute in the style of `@c(customCName, convention: stdcall)`. + +## Alternatives considered + +### `@c` attribute name + +This proposal uses the `@c` attribute on functions to identify them as C functions implemented in Swift and on enums to identify them as C compatible. This concise attribute clearly references interoperability with the C language. Plus, having an attribute specific to this feature aligns with `@objc` which is already used on some functions, enums, and for `@objc @implementation`. + +We considered some alternatives: + +- An official `@cdecl` may be more practical for discoverability but the terms *cdecl* and *decl* are compiler implementation details we do not wish to surface in the language. + +- An official `@expose(c)`, formalizing the experimental `@_expose(Cxx)`, would align the global function use case with what has been suggested for the C++ interop. However, sharing an attribute for the features described here may add complexity to both compiler implementation and user understanding of the language. + + While `@_expose(Cxx)` supports enums, it doesn't have the same requirement as `@objc` and `@c` for the raw type. The generated representation in the compatibility header for the enums differs too. The attribute `@_expose(Cxx)` also supports structs, while we consider supporting `@c` structs in the future, we have yet to pick the best approach so it would likely differ from the C++ one. + + Although sharing an attribute avoids adding a new one to the language, it also implies a similar behavior between the language interops. However, these behaviors already diverge and we may want to have each feature evolve differently in the future. + +### `@objc` attribute on global functions + +We use the `@objc` attribute on global functions to identify them as C functions implemented in Swift that are callable from Objective-C. This was more of a natural choice as `@objc` is already widely used for interoperability with Objective-C. + +We considered using instead `@c @objc` to make it more explicit that the behavior is similar to `@c`, and extending it to Objective-C is additive. We went against this option as it doesn't add much useful information besides being closer to the compiler implementation. + +### Compatibility header + +We decided to extend the existing compatibility header instead of introducing a new one specific to C compatibility. This allows content printed for Objective-C to reference C types printed earlier in the same header. Plus this follows the current behavior of the C++ interop which prints its own block in the same compatibility header. + +Since we use the same compatibility header, we also use the same compiler flags to request it being emitted. We considered adding a C specific flag as the main one, `-emit-objc-header`, is Objective-C specific. In practice build systems tend to use the `-path` variant, in that case we already have `-emit-clang-header-path` that applies well to the C language. We could add a `-emit-clang-header` flag but the practical use of such a flag would be limited. + +## Acknowledgements + +A special thank you goes to Becca Royal-Gordon, Joe Groff and many others for the past work on `@_cdecl` on which this proposal is built. From 79b68f94c5f7431015363d40e47ea96c89efd456 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Thu, 25 Sep 2025 11:59:24 -0400 Subject: [PATCH 4495/4563] Update 0495-cdecl.md (#2973) --- proposals/0495-cdecl.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0495-cdecl.md b/proposals/0495-cdecl.md index 1eaa1195cb..debdcb3ed4 100644 --- a/proposals/0495-cdecl.md +++ b/proposals/0495-cdecl.md @@ -3,9 +3,9 @@ * Proposal: [SE-0495](0495-cdecl.md) * Author: [Alexis Laferrière](https://github.com/xymus) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Ready for Review** +* Status: **Active Review (Sept 25 ... Oct 9, 2025)** * Implementation: On `main` with the experimental feature flags `CDecl` for `@c`, and `CImplementation` for `@c @implementation`. With the exception of the `@objc` support for global functions which is available under the name `@_cdecl`. -* Review: ([pitch](https://forums.swift.org/t/pitch-formalize-cdecl/79557)) +* Review: ([pitch](https://forums.swift.org/t/pitch-formalize-cdecl/79557))([review](https://forums.swift.org/t/se-0495-c-compatible-functions-and-enums/82365)) ## Introduction From 806a2eb973d35ad3efb52aa6f333064fde66ee32 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Thu, 25 Sep 2025 17:21:16 -0400 Subject: [PATCH 4496/4563] Fix typo --- proposals/testing/NNNN-test-cancellation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/testing/NNNN-test-cancellation.md b/proposals/testing/NNNN-test-cancellation.md index 8e79189e69..327e2182e5 100644 --- a/proposals/testing/NNNN-test-cancellation.md +++ b/proposals/testing/NNNN-test-cancellation.md @@ -134,7 +134,7 @@ were called on that task. ### Throwing semantics Unlike [`Task.cancel()`](https://developer.apple.com/documentation/swift/task/cancel()), -these functions always throw an error instead of returning. This simplifies +this function always throws an error instead of returning. This simplifies control flow when a test is cancelled; instead of having to write: ```swift @@ -152,7 +152,7 @@ if condition { } ``` -The errors these functions throw are of a type internal to Swift Testing that is +The errors this function throws are of a type internal to Swift Testing that is semantically similar to [`CancellationError`](https://developer.apple.com/documentation/swift/cancellationerror) but carries additional information (namely the `comment` and `sourceLocation` arguments to `cancel(_:sourceLocation:)`) that Swift Testing can present to the @@ -269,7 +269,7 @@ JSON schema (currently expected to be schema version `"6.3"`). but core functionality of Swift Testing needs to be usable without also importing XCTest. -- Spelling the functions `static func cancel(_:sourceLocation:) -> some Error` +- Spelling the function `static func cancel(_:sourceLocation:) -> some Error` and requiring it be called as `throw Test.cancel()`. This is closer to how the [`XCTSkip`](https://developer.apple.com/documentation/xctest/xctskip-swift.struct) type is used in XCTest. We have received indirect feedback about [`XCTSkip`](https://developer.apple.com/documentation/xctest/xctskip-swift.struct) From b18ea77cc3feba5d8336d70606fdd31e9c4b12da Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Thu, 25 Sep 2025 10:51:05 -0700 Subject: [PATCH 4497/4563] Address feedback --- proposals/NNNN-inline-always.md | 414 ++++++++++++++++++++------------ 1 file changed, 263 insertions(+), 151 deletions(-) diff --git a/proposals/NNNN-inline-always.md b/proposals/NNNN-inline-always.md index 80f5fe2bc1..97a423ad65 100644 --- a/proposals/NNNN-inline-always.md +++ b/proposals/NNNN-inline-always.md @@ -93,77 +93,271 @@ func callee(_ result: inout SomeValue, _ cond: Bool) { ## Proposed solution We desire for the attribute to function as an optimization control. That means -that the proposed `@inline(always)` attribute should emit an error if inlining -cannot be guaranteed in all optimization modes. The value of the function at a -call site can might determined dynamically at runtime. In such cases the -compiler cannot determine a call site which function is applied without doing -global analysis. In these cases we don't guarantee inlining even if the dynamic -value of the applied function was annotated with `@inline(always)`. -We only guarantee inlining if the annotated function is directly referenced and -not derived by some function value computation such as method lookup or function -value (closure) formation and diagnose errors if this guarantee cannot be -upheld. - -A sufficiently clever optimizer might be able to derive the dynamic value at the -call site, in such cases the optimizer shall respect the optimization control -and perform inlining. +that the proposed `@inline(always)` attribute should emit an error diagnostic if +inlining is not possible in all optimization modes. However, this gets +complicated by the fact that the value of the function at a call site might be +determined dynamically at runtime: + +- Calls through first class function values + ```swift + @inline(always) f() {...} + + func a() { + let fv = f + fv() + } + ``` +- Calls through protocol values and protocol constraint generic types + ```swift + protocol P { + func method() + } + struct S : P { + @inline(always) + func method() {...} + } + func a(_ t: T) { + t.method() + let p : P = S() + p.method() + } + ``` +- Calls through class instance values and the method referenced is not `final` + ```swift + class C { + @inline(always) + func method() {...} + } + func a(c: C) { + c.method() + } + ``` + +In such cases, the compiler cannot determine at a call site which function is +applied without doing non-local analysis: either dataflow, or class hiarchy +analysis. +These cases are in contrast to when the called function can statically be +determined purely by looking at the call site, we refer to this set as direct +function references in the following: + +- Calls to free standing functions +- Calls to methods of `actor`, `struct`, `enum` type +- Calls to final methods of `class` type, and type (`static/class`) methods of + `class` type + +Therefore, in cases where the value of the function at a usage site is +dynamically derived we don't emit an error even if the dynamic value of the +applied function was annotated with `@inline(always)`. We only emit an error if +the annotated function is directly referenced and something would cause it to be +not inlined or if some property at the declaration site of the function would +make it not possible in the common case. + +Listing the different scenarios that can occur for a function marked with +`@inline(always)`: + +1. A function can definitely be inlined at the use site: direct function + references barring recursion cycles +2. A function can never be always inlined at a use site and we diagnose an + error: cycles in `@inline(always)` functions calling each other and all + references are direct. +3. A function can not be inlined reliably and we diagnose an error at the + declaration site: non-final method declaration +4. A function can not be inlined and we don't diagnose an error: calls through + first class function values, protocol values, and protocol constraint generic + types. + +### Direct function references + +Calls to freestanding functions, methods of `enum`, `struct`, `actor` types, +final methods of `class` types, and type methods of `class` types don't +dynamically dispatch to different implementations. Calls to such methods can +always be inlined barring the recursion limitation (see later). (case 1) ```swift -protocol SomeProtocol { - func mightBeOverriden() +struct S { + @inline(always) + final func method() {} } -class C : SomeProtocol{ - @inline(always) - func mightBeOverriden() { - } +func f() { + let s: S = ... + s.method() // can definitely be inlined +} + +class C { + @inline(always) + final func finalMethod() {} + + @inline(always) + class func method() {} +} + +class Sub : C {} + +func f2() { + let c: C = ... + c.finalMethod() // can definitely be inlined + let c2: Sub = .. + c2.finalMethod() // can definitely be inlined + C.method() // can definitely be inlined } @inline(always) -func callee() { +func freestanding() {} + +func f3() { + freestanding() // can definitely be inlined +} + +``` + +### Non final class methods + +Swift performs dynamic dispatch for non-final methods of classes based on the +dynamic receiver type of the class instance value at a use site. Inferring the +value of that dynamic computation at compile time is not possible in many cases +and the success of inlining cannot be ensured. We treat a non-final method +declaration with `@inline(always)` as an declaration site error because we +assume that the intention of the attribute is that the method will be inlined in +most cases and this cannot be guaranteed (case 3). + +```swift +class C { + @inline(always) // error: non-final method marked @inline(always) + func method() {} } -func applyFunctionValues(_ funValue: () -> (), c: C, p: SomeProtocol) { - funValue() // function value, not guaranteed - c.mightBeOverriden() // dynamic method lookup, not guaranteed - p.mightBeOverriden() // dynamic method lookup, not guaranteed - callee() // directly referenced, guaranteed +class C2 : C { + @inline(always) // error: non-final method marked @inline(always) + override func method() {} } +func f(c: C) { + c.method() // dynamic type of c might be C or C2, could not ensure success + // of inlining in general +} +``` + +### Recursion + +Repeatedly inlining `@inline(always)` functions calling each other would lead to +an infinite cycle of inlining. We can never follow the `@inline(always)` +semantics and diagnose an error (case 2). + +```swift +@inline(always) +func callee() { + ... + if cond2 { + caller() // error: caller is marked @inline(always) and would create an + // inlining cycle + } +} + +@inline(always) func caller() { - applyFunctionValue(callee, C()) + ... + if cond { + callee() + } } +``` -caller() +### First class function values + +Swift allows for functions as first class objects. They can be assigned to +variables and passed as arguments. The reference function of a function value +cannot be reliably be determined at the usage and is therefore not diagnosed as +an error (case 4). + +```swift +@inline(always) +func callee() {} + +func use(_ f: () -> ()) { + f() +} +func useFunctionValue() { + let f = callee + ... + f() // function value use, may be inlined but not diagnosed if not + use(callee) // function value use, may be inlined in `use()` but not diagnosed + // if not +} ``` -Code authors shall be able to rely on that if a function is marked with -`@inline(always)` and directly referenced from any context (within or outside of -the defining module) that the function can be inlined or an error is emitted. +### Protocol methods +Protocol constraint or protocol typed values require a dynamic computation to +determine the eventual method called. Inferring the value of the eventual method +called at compile time is not possible in general and the success of inlining +cannot be ensured. We don't diagnose a usage site error if the underlying method +is marked with `@inline(always)` (case 4) -## Detailed design +```swift +protocol P { + func method() +} +struct S : P { + @inline(always) + func method() {} +} +final class C : P { + @inline(always) + func method() {} +} + +@inline(always) +func generic (_ t: T) { + t.method() +} + +func f() { + let p: P = S() + p.method() // might not get inlined, not diagnosed + generic(S()) // might not get inlined, not diagnosed + let p2: P = C() + p2.method() // might not get inlined, not diagnosed + generic(C()) // might not get inlined, not diagnosed +} +``` + +### Optimization control as optimization hint + +A clever optimizer might be able to derive the dynamic value at the +call site, in such cases the optimizer shall respect the optimization control +and perform inlining. + +In the following example the functions will be inlined when build with higher +optimization levels than `-Onone`. + +```swift +@inline(always) +func binaryOp(_ left: T, _ right: T, _ op: (T, T) -> T) -> T { + op(left, right) +} + +@inline(always) +func add(_ left: Int, _ right: Int) -> Int { left + right } + +print(binaryOp(5, 10, add)) +print(binaryOp(5, 10) { add($0, $1) }) +``` -We want to diagnose an error if a directly referenced function is marked with -`@inline(always)` and cannot be inlined. What are the cases where this might not -be possible? ### Interaction with `@inlinable` -`@inlinable` and `@_alwaysEmitIntoClient` make the function body available to -clients (callers in other modules) in library evolution mode. `@inlinable` makes -the body of the function available to the client and causes an ABI entry point -in the vending module to vended. `@_alwaysEmitIntoClient` makes the body of the -function available for clients but does not cause emission of an ABI entry -point. Functions with `open`, `public`, or `package` level access cause emission -of an ABI entry point for clients to call but in the absence of aforementioned -attributes do not make the body available to the client. +`@inlinable` makes the function body available to clients (callers in other +modules) in library evolution mode. Functions with `open`, `public`, or +`package` level access cause emission of an ABI entry point for clients to call +but in the absence of aforementioned attributes do not make the body available +to the client. `@inline(always)` intention is to be able to guarantee that inlining will happen for any caller inside or outside the defining module therefore it makes sense to -require the use some form of "inline-ability" attribute with them. This -attribute could be required to be explicitly stated. And for it to be an error -when the attribute is omitted. +require the use of "@inlinable" attribute with them. This attribute could be +required to be explicitly stated. And for it to be an error when the attribute +is omitted. ```swift @inline(always) @@ -176,28 +370,23 @@ public func callee() { ``` Alternatively, the attribute could be implicitly implied by the usage of -`@inline(always)`. In this proposal, we take the position that it should be -implied to avoid the redundancy of spelling this out. The intention of -`@inline(always)` is for it to inline in all contexts. Instead of an error in the -absence of the attribute we should imply "inline-ability". The question is what -should we default to? - -`@_alwaysEmitIntoClient`'s semantics seems preferable for new functions. We -intend for the function to be always inlined, why should there be an ABI entry -point? +`@inline(always)`. We take the position that it should be implied to avoid the +redundancy of spelling it out. -`@inlinable` semantics allows for annotating existing functions with -`@inline(always)` without breaking ABI compatibility. `@inlinable` keeps an -entry point in the vending module for older code that assumed the existence of -an entry point. +For access levels equal and lower than `internal` `@inlinable` is not implied. -This proposals takes the position to give `@inline(always)` the semantics of -`@inlineable` and provide an alternative spelling for the case when we desire -`@_alwaysEmitIntoClient` semantics: `@inline(only)`. +As a consequence all the rules that apply to `@inlinable` also apply to +`public`/`open`/`package` declarations marked with `@inline(always). -For access levels equal and lower than `internal` `@inlinable` should not be -implied. +```swift +internal func g() { ... } +@inline(always) +public func inlinableImplied() { + g() // error: global function 'g()' is internal and cannot be referenced from an + '@inlinable' function +} +``` ### Interaction with `@usableFromInline` @@ -206,7 +395,7 @@ if it is either `@inlinable` (see above) or `@usableFromInline`. `@usableFromInl ensures that there is a public entry point to the `internal` level function but does not ensure that the body of the function is available to external modules. Therefore, it is an error to combine `@inline(always)` with a -`@usableFromInline` function as we cannot guaranteed that the function can +`@usableFromInline` function as we cannot guarantee that the function can always be inlined. ```swift @@ -224,9 +413,11 @@ public func caller() { ### Module internal access levels -It is okay to mark `internal`, `private` and `fileprivate` function declarations -with `@inline(always)` in cases other than the ones mention above without the -`@inlinable` attribute as they can only be referenced from within the module. +To mark `internal`, `private` and `fileprivate` function declarations +with `@inline(always)` does not imply the `@inlinable` attribute's semantics. +They can only be referenced from within the module. `internal` declarations can +be marked with `@inlinable` if this is required by the presence of other +`@inlinable` (or public `@inline(always)`) functions that reference them. ```swift @@ -245,86 +436,6 @@ private func callee2() { } ``` -#### Infinite recursion during inlining - -We will diagnose if inlining cannot happen due to calls within a -[strongly connected component](https://en.wikipedia.org/wiki/Strongly_connected_component) -marked with `@inline(always)` as errors. - -```swift -@inline(always) -func callee() { - ... - if cond2 { - caller() - } -} - -@inline(always) -func caller() { - ... - if cond { - callee() - } -} -``` - -### Dynamic function values - -As outlined earlier the attribute does not guarantee inlining or diagnose the -failure to inline when the function value is dynamic at a call site: a function -value is applied, or the function value is obtained via class method lookup or -protocol lookup. - -```swift -@inline(always) -func callee() {} -func useFunctionValue() { - let f = callee - ... - f() // function value use, not guaranteed to be inlined -} - -class SomeClass : SomeProto{ - @inline(always) - func nonFinalMethod() {} - - @inline(always) - func method() {} -} - -protocol SomeProto { - func method() -} - - -func dynamicMethodLookup() { - let c = SomeClass() - ... - c.nonFinalMethod() // method lookup, not guaranteed to be inlined - - let p: SomeProto = SomeClass() - p.method() // method lookup, not guaranteed to be inlined -} - -class A { - func finalInSub() {} - final func finalMethod() {} -} -class B : A { - overrided final func finalInSub() {} -} - -func noMethodLookup() { - let a = A() - a.finalMethod() // no method lookup, guaranteed to be inlined - - let b = B() - b.finalInSubClass() // no method lookup, guaranteed to be inlined -} -``` - - ## Source compatibility This proposal is additive. Existing code has not used the attribute. It has no @@ -384,9 +495,10 @@ function as `@inlinable`. With respect to `@inlinable` an initial draft of the proposal suggested to require spelling the `@inlinable` attribute on `public` declarations or an error -would be displayed. The argument was that this would ensure that authors would -be aware of the additional semantics implied by the attribute: the body is -exposed. +would be displayed. The argument was made that this would ensure that authors +would be aware of the additional semantics implied by the attribute: the body is +exposed. This was juxtaposed by the argument that spelling both `@inlinable` and +`@inline(always)` is redundant. ## Acknowledgments From 2dfe5145ca7a8a61b5adb2eb3bc204103ec2b7cc Mon Sep 17 00:00:00 2001 From: Bassam Khouri Date: Tue, 30 Sep 2025 10:20:29 -0400 Subject: [PATCH 4498/4563] SwiftPM: update organization in template Update the GitHub organization of the Swift Package Manager --- proposal-templates/0000-swiftpm-template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposal-templates/0000-swiftpm-template.md b/proposal-templates/0000-swiftpm-template.md index 82898bbc05..bd65132b9e 100644 --- a/proposal-templates/0000-swiftpm-template.md +++ b/proposal-templates/0000-swiftpm-template.md @@ -7,7 +7,7 @@ *During the review process, add the following fields as needed:* -* Implementation: [apple/swift-package-manager#NNNNN](https://github.com/apple/swift-package-manager/pull/NNNNN) +* Implementation: [swiftlang/swift-package-manager#NNNNN](https://github.com/swiftlang/swift-package-manager/pull/NNNNN) * Decision Notes: [Rationale](https://forums.swift.org/), [Additional Commentary](https://forums.swift.org/) * Bugs: [SR-NNNN](https://bugs.swift.org/browse/SR-NNNN), [SR-MMMM](https://bugs.swift.org/browse/SR-MMMM) * Previous Revision: [1](https://github.com/swiftlang/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md) From 122a32e0983966b13c2eec7338d7e88af0d7b2ea Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Wed, 1 Oct 2025 08:24:21 -0700 Subject: [PATCH 4499/4563] Add Acknowledgments --- proposals/NNNN-inline-always.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/proposals/NNNN-inline-always.md b/proposals/NNNN-inline-always.md index 97a423ad65..1c8fa26f58 100644 --- a/proposals/NNNN-inline-always.md +++ b/proposals/NNNN-inline-always.md @@ -502,4 +502,8 @@ exposed. This was juxtaposed by the argument that spelling both `@inlinable` and ## Acknowledgments -TODO: .... +Thanks to [Jordan Rose](https://forums.swift.org/t/optimization-controls-and-optimization-hints/81612/7) for pointing out that inlining can't be always guaranteed, specifically the case of closures. +Thanks to [Xiaodi Wu](https://forums.swift.org/t/pitch-inline-always-attribute/82040/7) for proposing inferring `@inlinable`. +Thanks to [Tony Allevato](https://github.com/swiftlang/swift-evolution/pull/2958#discussion_r2379238582) for suggesting to error on on non-final methods and +providing editing feedback. +Thanks to [Doug Gregor](https://github.com/DougGregor), [Joe Groff](https://github.com/jckarter), [Tim Kientzle](https://github.com/tbkka), and [Allan Shortlidge](https://github.com/tshortli) for discussions related to the feature. From b1ff577c3e36ac4629696b5b9eeabd6d35c37002 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 2 Oct 2025 09:22:39 -0400 Subject: [PATCH 4500/4563] Assign SE-0496. --- proposals/{NNNN-inline-always.md => 0496-inline-always.md} | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename proposals/{NNNN-inline-always.md => 0496-inline-always.md} (98%) diff --git a/proposals/NNNN-inline-always.md b/proposals/0496-inline-always.md similarity index 98% rename from proposals/NNNN-inline-always.md rename to proposals/0496-inline-always.md index 1c8fa26f58..94922c087f 100644 --- a/proposals/NNNN-inline-always.md +++ b/proposals/0496-inline-always.md @@ -1,9 +1,11 @@ # `@inline(always)` attribute -* Proposal: [SE-NNNN](NNNN-inline-always.md) +* Proposal: [SE-0496](0496-inline-always.md) * Authors: [Arnold Schwaighofer](https://github.com/aschwaighofer) +* Review Manager: [Tony Allevato](https://github.com/allevato) +* Status: **Active review (October 2–16, 2025)** * Implementation: [swiftlang/swift#84178](https://github.com/swiftlang/swift/pull/84178) -* Pitch thread: https://forums.swift.org/t/pitch-inline-always-attribute/82040 +* Review: ((pitch)[https://forums.swift.org/t/pitch-inline-always-attribute/82040]) ## Introduction From 754d7056d37a0871d7856f4ed6c4e2b689d1892e Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 2 Oct 2025 09:27:33 -0400 Subject: [PATCH 4501/4563] Link to review thread. --- proposals/0496-inline-always.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0496-inline-always.md b/proposals/0496-inline-always.md index 94922c087f..eab9725ea1 100644 --- a/proposals/0496-inline-always.md +++ b/proposals/0496-inline-always.md @@ -5,7 +5,7 @@ * Review Manager: [Tony Allevato](https://github.com/allevato) * Status: **Active review (October 2–16, 2025)** * Implementation: [swiftlang/swift#84178](https://github.com/swiftlang/swift/pull/84178) -* Review: ((pitch)[https://forums.swift.org/t/pitch-inline-always-attribute/82040]) +* Review: ([pitch](https://forums.swift.org/t/pitch-inline-always-attribute/82040)) ([review](https://forums.swift.org/t/se-0496-inline-always-attribute/82480)) ## Introduction From 9d0b21074c595206875a3f95af6ba91ba95ff9e8 Mon Sep 17 00:00:00 2001 From: Kenta Kubo <601636+kkebo@users.noreply.github.com> Date: Sun, 5 Oct 2025 00:48:44 +0900 Subject: [PATCH 4502/4563] [SE-0496] Fix a typo --- proposals/0496-inline-always.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0496-inline-always.md b/proposals/0496-inline-always.md index eab9725ea1..f10d161433 100644 --- a/proposals/0496-inline-always.md +++ b/proposals/0496-inline-always.md @@ -378,7 +378,7 @@ redundancy of spelling it out. For access levels equal and lower than `internal` `@inlinable` is not implied. As a consequence all the rules that apply to `@inlinable` also apply to -`public`/`open`/`package` declarations marked with `@inline(always). +`public`/`open`/`package` declarations marked with `@inline(always)`. ```swift internal func g() { ... } From 837750e0801dde00828bc9dfcf46664a0824cd60 Mon Sep 17 00:00:00 2001 From: Rick van Voorden Date: Tue, 7 Oct 2025 16:06:21 -0700 Subject: [PATCH 4503/4563] add is identical methods updates --- proposals/0494-add-is-identical-methods.md | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/proposals/0494-add-is-identical-methods.md b/proposals/0494-add-is-identical-methods.md index 076d35f9a5..007c875549 100644 --- a/proposals/0494-add-is-identical-methods.md +++ b/proposals/0494-add-is-identical-methods.md @@ -21,6 +21,8 @@ * [`ContiguousArray`](#contiguousarray) * [`Dictionary`](#dictionary) * [`Set`](#set) + * [`UnsafeBufferPointer`](#unsafebufferpointer) + * [`UTF8Span`](#utf8span) * [Source Compatibility](#source-compatibility) * [Impact on ABI](#impact-on-abi) * [Future Directions](#future-directions) @@ -405,6 +407,11 @@ We propose adding `isIdentical` methods to the following concrete types from Sta * `ContiguousArray` * `Dictionary` * `Set` +* `UnsafeBufferPointer` +* `UnsafeMutableBufferPointer` +* `UnsafeMutableRawBufferPointer` +* `UnsafeRawBufferPointer` +* `UTF8Span` For each type being presented we codify important semantics in our header documentation. @@ -690,6 +697,30 @@ extension Set { } ``` +### `UnsafeBufferPointer` + +```swift +extension UnsafeBufferPointer where Element: ~Copyable { + /// Returns a Boolean value indicating whether two `UnsafeBufferPointer` + /// instances refer to the same region in memory. + public func isIdentical(to other: Self) -> Bool { ... } +} +``` + +The following types will adopt the same semantic guarantees as `UnsafeBufferPointer`: +* `UnsafeMutableBufferPointer` +* `UnsafeMutableRawBufferPointer` +* `UnsafeRawBufferPointer` + +### `UTF8Span` + +```swift +extension UTF8Span where Element: ~Copyable { + /// Returns a Boolean value indicating whether two `UTF8Span` instances + /// refer to the same region in memory. + public func isIdentical(to other: Self) -> Bool { ... } +``` + ## Source Compatibility This proposal is additive and source-compatible with existing code. From cc971d980eeb33119d95573fa7244db8515aaa90 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Wed, 8 Oct 2025 11:01:35 -0500 Subject: [PATCH 4504/4563] Update status of ST-0015 to Accepted and link to acceptance announcement (#2987) --- .../0015-image-attachments-in-swift-testing-windows.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/testing/0015-image-attachments-in-swift-testing-windows.md b/proposals/testing/0015-image-attachments-in-swift-testing-windows.md index 74549f0ba7..d23e8793cb 100644 --- a/proposals/testing/0015-image-attachments-in-swift-testing-windows.md +++ b/proposals/testing/0015-image-attachments-in-swift-testing-windows.md @@ -3,9 +3,9 @@ * Proposal: [ST-0015](0015-image-attachments-in-swift-testing-windows.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Review Manager: [Stuart Montgomery](https://github.com/stmontgomery) -* Status: **Active Review (September 19…29, 2025)** +* Status: **Accepted** * Implementation: [swiftlang/swift-testing#1245](https://github.com/swiftlang/swift-testing/pull/1245), [swiftlang/swift-testing#1254](https://github.com/swiftlang/swift-testing/pull/1254), _et al_. -* Review: ([pitch](https://forums.swift.org/t/pitch-image-attachments-in-swift-testing-windows/81871)) ([review](https://forums.swift.org/t/st-0015-image-attachments-in-swift-testing-windows/82241)) +* Review: ([pitch](https://forums.swift.org/t/pitch-image-attachments-in-swift-testing-windows/81871)) ([review](https://forums.swift.org/t/st-0015-image-attachments-in-swift-testing-windows/82241)) ([acceptance](https://forums.swift.org/t/accepted-st-0015-image-attachments-in-swift-testing-windows/82575)) ## Introduction From 99c18ea9c7921874e4428fca3aaeda1f5180e3cf Mon Sep 17 00:00:00 2001 From: Frederick Kellison-Linn Date: Wed, 8 Oct 2025 20:36:14 -0400 Subject: [PATCH 4505/4563] Update status of SE-0491 to Accepted (#2988) --- proposals/0491-module-selectors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0491-module-selectors.md b/proposals/0491-module-selectors.md index d609228a11..ad86323f3b 100644 --- a/proposals/0491-module-selectors.md +++ b/proposals/0491-module-selectors.md @@ -3,10 +3,10 @@ * Proposal: [SE-0491](0491-module-selectors.md) * Authors: [Becca Royal-Gordon](https://github.com/beccadax) * Review Manager: [Freddy Kellison-Linn](https) -* Status: **Active review (September 13 ... September 30, 2025)** +* Status: **Accepted** * Bug: [swiftlang/swift#53580](https://github.com/swiftlang/swift/issues/53580) (SR-11183) * Implementation: [swiftlang/swift#34556](https://github.com/swiftlang/swift/pull/34556) -* Review: ([pitch](https://forums.swift.org/t/pitch-module-selectors/80835)) ([review](https://forums.swift.org/t/se-0491-module-selectors-for-name-disambiguation/82124)) +* Review: ([pitch](https://forums.swift.org/t/pitch-module-selectors/80835)) ([review](https://forums.swift.org/t/se-0491-module-selectors-for-name-disambiguation/82124)) ([acceptance](https://forums.swift.org/t/accepted-se-0491-module-selectors-for-name-disambiguation/82589)) Previously pitched in: From 384df6d3baf858791b138ec7b55629f18903a47a Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 7 Oct 2025 16:06:04 -0400 Subject: [PATCH 4506/4563] [ST-NNNN] Adjustments to image attachments in Swift Testing --- .../NNNN-image-attachment-adjustments.md | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 proposals/testing/NNNN-image-attachment-adjustments.md diff --git a/proposals/testing/NNNN-image-attachment-adjustments.md b/proposals/testing/NNNN-image-attachment-adjustments.md new file mode 100644 index 0000000000..093661fd15 --- /dev/null +++ b/proposals/testing/NNNN-image-attachment-adjustments.md @@ -0,0 +1,162 @@ +# Adjustments to image attachments in Swift Testing + +* Proposal: [ST-NNNN](NNNN-image-attachment-adjustments.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [swiftlang/swift-testing#1359](https://github.com/swiftlang/swift-testing/pull/1359) +* Review: ([pitch](https://forums.swift.org/...)) + +## Introduction + +This proposal includes a small number of adjustments to the API surface of Swift +Testing's image attachments feature introduced in [ST-0014](0014-image-attachments-in-swift-testing-apple-platforms.md) +and [ST-0015](0015-image-attachments-in-swift-testing-windows.md). + +## Motivation + +These changes will help to align the platform-specific interfaces of the feature +more closely. + +## Proposed solution + +The `AttachableAsCGImage` and `AttachableAsIWICBitmapSource` protocols are +combined into a single protocol, `AttachableAsImage` with adjusted protocol +requirements; a change is made to `AttachableImageFormat` to more closely +align its interface between Darwin and Windows; `AttachableImageFormat` is made +to conform to `Equatable` and `Hashable`; and an additional property is added to +`Attachment` to query its image format. + +## Detailed design + +The following changes are proposed: + +### Combining AttachableAsCGImage and AttachableAsIWICBitmapSource + +The `AttachableAsCGImage` and `AttachableAsIWICBitmapSource` protocols are +combined into a single protocol, `AttachableAsImage`. + +These platform-specific requirements are removed: + +```diff +- var attachableCGImage: CGImage { get throws } +- func copyAttachableIWICBitmapSource() throws -> UnsafeMutablePointer +``` + +They are replaced with a new requirement that encapsulates the image encoding +operation. This requirement is implemented by the CoreGraphics and WinSDK +overlays and is made publicly available for test authors who wish to declare +additional conformances to this protocol for types that are not based on +`CGImage` or `IWICBitmapSource`: + +```swift +public protocol AttachableAsImage { + // ... + + /// Encode a representation of this image in a given image format. + /// + /// - Parameters: + /// - imageFormat: The image format to use when encoding this image. + /// - body: A function to call. A temporary buffer containing a data + /// representation of this instance is passed to it. + /// + /// - Returns: Whatever is returned by `body`. + /// + /// - Throws: Whatever is thrown by `body`, or any error that prevented the + /// creation of the buffer. + /// + /// The testing library uses this function when saving an image as an + /// attachment. The implementation should use `imageFormat` to determine what + /// encoder to use. + borrowing func withUnsafeBytes(as imageFormat: AttachableImageFormat, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R +} +``` + +If a developer has an image type that should conform to `AttachableAsImage` and +wraps an instance of `CGImage` or `IWICBitmapSource`, it is straightforward for +them to delegate to that object. For example: + +```swift +import Testing +import CoreGraphics + +struct MyImage { + var cgImage: CGImage + // ... +} + +extension MyImage: AttachableAsImage { + func withUnsafeBytes(as imageFormat: AttachableImageFormat, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + try cgImage.withUnsafeBytes(as: imageFormat, body) + } +} +``` + +### Adjusting AttachableImageFormat + +The following Apple-specific `AttachableImageFormat` initializer is renamed so +that its first argument has an explicit label: + +```diff + public struct AttachableImageFormat { + // ... +- public init(_ contentType: UTType, encodingQuality: Float = 1.0) ++ public init(contentType: UTType, encodingQuality: Float = 1.0) + } +``` + +This change makes the type's interface more consistent between Darwin and +Windows (where it has an `init(encoderCLSID:encodingQuality:)` initializer.) + +As well, conformance to `Equatable` and `Hashable` is added: + +```swift +extension AttachableImageFormat: Equatable, Hashable {} +``` + +Conformance to `Equatable` is necessary to correctly implement the +`withUnsafeBytes(as:_:)` protocol requirement mentioned above, and conformance +to `Hashable` is generally useful and straightforward to implement. + +### Adding an imageFormat property to Attachment + +The following property is added to `Attachment` when the attachable value is an +image: + +```swift +extension Attachment where AttachableValue: AttachableWrapper, + AttachableValue.Wrapped: AttachableAsImage { + /// The image format to use when encoding the represented image. + public var imageFormat: AttachableImageFormat? { get } +} +``` + +## Source compatibility + +These changes are breaking for anyone who has created a type that conforms to +either `AttachableAsCGImage` or `AttachableAsIWICBitmapSource`, or anyone who +has adopted `AttachableImageFormat.init(_:encodingQuality:)`. + +This feature is new in Swift 6.3 and has not shipped to developers outside of +nightly toolchain builds. As such, we feel confident that any real-world impact +to developers will be both minimal and manageable. + +## Integration with supporting tools + +No changes. + +## Future directions + +N/A + +## Alternatives considered + +- Leaving the two protocols separate. Combining them allows us to lower more + code into the main Swift Testing library and improves our ability to generate + DocC documentation, while also simplifying the story for developers who want + to use this feature across platforms. + +## Acknowledgments + +Thanks to my colleagues for their feedback on the image attachments feature and +to the Swift community for putting up with the churn! From fbcdd13ba66546c28feb46aa46df6de429b94ead Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Thu, 9 Oct 2025 09:31:49 -0700 Subject: [PATCH 4507/4563] SE-0492: objectFileFormat -> objectFormat --- proposals/0492-section-control.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/proposals/0492-section-control.md b/proposals/0492-section-control.md index 875a9b3ba2..8410d9466c 100644 --- a/proposals/0492-section-control.md +++ b/proposals/0492-section-control.md @@ -88,12 +88,12 @@ let myPlugin: PluginData = ( On top of specifying a custom section name with the `@section` attribute, marking a variable as `@used` is needed to prevent otherwise unused symbols from being removed by the compiler. When using section placement to e.g. implement linker sets, such values are typically going to have no usage at compile time, and at the same time they should not be exposed in public interface of libraries (not be made public), therefore we the need the `@used` attribute. -Different object file formats (ELF, Mach-O, COFF) have different restrictions and rules on what are valid section names, and cross-platform code will have to use different names for different file formats. To support that, custom section names can be specified as a string literal. The string will be used directly, without any processing, as the section name for the symbol. A new `#if objectFileFormat(...)` conditional compilation directive will be provided to support conditionalizing based on the file format: +Different object file formats (ELF, Mach-O, COFF) have different restrictions and rules on what are valid section names, and cross-platform code will have to use different names for different file formats. To support that, custom section names can be specified as a string literal. The string will be used directly, without any processing, as the section name for the symbol. A new `#if objectFormat(...)` conditional compilation directive will be provided to support conditionalizing based on the file format: ```swift -#if objectFileFormat(ELF) +#if objectFormat(ELF) @section("mysection") -#elseif objectFileFormat(MachO) +#elseif objectFormat(MachO) @section("__DATA,mysection") #endif var global = ... @@ -311,7 +311,7 @@ let plugin = ... See [Structured section specifiers](#structured-section-specifiers) below for more rationale. -In some cases, it’s not possible to differentiate on the OS to support multiple object file formats, for example when using Embedded Swift to target baremetal systems without any OS. For that, a new `#if objectFileFormat(...)` conditional compilation directive will be provided. The allowed values in this directive will match the set of supported object file formats by the Swift compiler (and expand as needed in the future). Currently, they exact values will be (case sensitive): +In some cases, it’s not possible to differentiate on the OS to support multiple object file formats, for example when using Embedded Swift to target baremetal systems without any OS. For that, a new `#if objectFormat(...)` conditional compilation directive will be provided. The allowed values in this directive will match the set of supported object file formats by the Swift compiler (and expand as needed in the future). Currently, they exact values will be (case sensitive): * COFF * ELF @@ -319,9 +319,9 @@ In some cases, it’s not possible to differentiate on the OS to support multipl * Wasm ```swift -#if objectFileFormat(MachO) +#if objectFormat(MachO) @section("__DATA_CONST,mysection") -#elseif objectFileFormat(ELF) +#elseif objectFormat(ELF) @section("mysection") #endif let value = ... @@ -402,9 +402,9 @@ The notions of constant expressions and constant values is applicable to a much The requirement to only use string literals as the section names could be lifted in the future, and we might allow referring to a declaration of variable with a compile-time string. This would be useful to avoid repetition when placing multiple values into the same section without needing to use macros. ```swift -#if objectFileFormat(ELF) +#if objectFormat(ELF) let mySectionName = "mysection" // required to be a compile-time value -#elseif objectFileFormat(MachO) +#elseif objectFormat(MachO) let mySectionName = "__DATA,mysection" // required to be a compile-time value #endif From 244c2d53284fb8c37427d04772e7e1e444621427 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Thu, 9 Oct 2025 09:32:40 -0700 Subject: [PATCH 4508/4563] SE-0492: Remove the 'lazy' portion of the proposal --- proposals/0492-section-control.md | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/proposals/0492-section-control.md b/proposals/0492-section-control.md index 8410d9466c..758a8574f6 100644 --- a/proposals/0492-section-control.md +++ b/proposals/0492-section-control.md @@ -101,13 +101,6 @@ var global = ... For the ELF file format specifically, the compiler will also emit a “section index” into produced object files, containing an entry about each custom section used in the compilation. This is a solution to an ELF specific problem where the behavior of ELF linkers and loaders means that sections are not easily discoverable at runtime. -When placing variables into a custom section, it's also allowed to use the `lazy` keyword to opt out of the mandatory static initialization and mandatory constant expression behavior, while still achieving section placement of the backing store of the data: - -```swift -@section("__DATA,colocated") lazy let data1: Int = 42 // ✅ -@section("__DATA,colocated") lazy let data2: Int = Int.random(in: 0 ..< 10) // ✅ -``` - > Note: The intention is that the `@section` and `@used` attributes are to be used rarely and only by specific use cases; high-level application code should not need to use them directly and instead should rely on libraries, macros and other abstractions over the low-level attributes. > The scope of this proposal is limited to compile-time behavior and compile-time control. We expect that full user-facing solutions for features like linker sets, test discovery or plugins will also require runtime implementations to discover and iterate the contents of custom sections, possibly from multiple modules. This proposal makes sure to provide the right building blocks and artifacts in binaries for the runtime components, but doesn’t prescribe the shape of those. However, it is providing a significant step towards generalized and safe high-level mechanisms for those use cases. See the discussion in [Runtime discovery of data in custom sections](#runtime-discovery-of-data-in-custom-sections) and [Linker sets, plugins as high-level APIs](#linker-sets-plugins-as-high-level-apis) in Future Directions. @@ -278,17 +271,6 @@ let a = (42, foo) // "foo" is statically initialized into a // linkable/relocatable pointer ``` -### Lazy variables with section placement - -On global and static variables that are annotated with `@section`, the compiler will now allow the `lazy` keyword (which is currently disallowed on all global and static variables). Using it will opt such variable out of the "mandatory static initialization" behavior and instead use the at-runtime lazy initialization that traditional global and static variables have. The initializer expression does not need to be a constant expression in this case. This is useful for the uncommon use case of placing variables into a custom section purely for colocation (e.g. to improve performance by increasing page/cacheline locality): - -```swift -@section("__DATA,colocated") lazy let data1: Int = 42 // ✅ -@section("__DATA,colocated") lazy let data2: Int = Int.random(in: 0 ..< 10) // ✅ -``` - -Traditional global and static variables are backed by two symbols: An init-once token and the actual storage for the variable's content. Both of these symbols are going to be placed into the custom section when using `@section` with `lazy`. This also means that any offline or in-process introspection mechanisms cannot assume a specific layout or state of such variables and their storage bytes in the sections, as the exact layout and content of the symbols of lazy variables is an implementation detail of the Swift language runtime. - ### Cross-platform object file format support The custom section name specified in the `@section` attribute is not validated by the compiler, instead it’s passed directly as a string to the linker. Different platforms supported by Swift are using different object and binary file formats (Linux uses ELF, Darwin uses Mach-O, Windows uses COFF), and that implies different restrictions and rules on what are valid section names. Because of that, a multi-platform library code is expected to use `#if os(...)` to use different section names for different platforms. Because of that, it’s expected that the attributes are to be typically only used indirectly via macros that hide the low-level nature of sections and object file formats from higher-level code developers: From d7b5c2734e87b3d75511b658fdf717b281143b00 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Thu, 9 Oct 2025 14:33:00 -0700 Subject: [PATCH 4509/4563] SE-0492: Change syntax to use @section(MachO: ..., ELF: ..., default: ...) --- proposals/0492-section-control.md | 162 ++++++++++++++---------------- 1 file changed, 74 insertions(+), 88 deletions(-) diff --git a/proposals/0492-section-control.md b/proposals/0492-section-control.md index 758a8574f6..405270c10c 100644 --- a/proposals/0492-section-control.md +++ b/proposals/0492-section-control.md @@ -63,22 +63,22 @@ The proposal is to add two new attributes `@section` and `@used` that will allow // Place an entry into a section, mark as "do not dead strip". // Initializer expression must be a constant expression. // The global variable is implicitly made statically initialized. -@section("__DATA,mysection") +@section(MachO: "__DATA,mysection") @used let myLinkerSetEntry: Int = 42 // ✅ // Non-constant or statically non-initializable expressions are disallowed -@section("__DATA,mysection") +@section(MachO: "__DATA,mysection") let myLinkerSetEntry: Int = Int.random(in: 0 ..< 10) // ❌ error // Section-placed globals can be "var", the initializer expression still must be constant -@section("__DATA,mysection") +@section(MachO: "__DATA,mysection") var mutableVariable: Int = 42 // ✅ // Some complex data types are allowed (tuples, function references) typealias PluginData = (version: Int, identifier: UInt64, initializer: @convention(c) ()->()) -@section("__DATA,plugins") +@section(MachO: "__DATA,plugins") @used let myPlugin: PluginData = ( version: 1, @@ -88,13 +88,19 @@ let myPlugin: PluginData = ( On top of specifying a custom section name with the `@section` attribute, marking a variable as `@used` is needed to prevent otherwise unused symbols from being removed by the compiler. When using section placement to e.g. implement linker sets, such values are typically going to have no usage at compile time, and at the same time they should not be exposed in public interface of libraries (not be made public), therefore we the need the `@used` attribute. -Different object file formats (ELF, Mach-O, COFF) have different restrictions and rules on what are valid section names, and cross-platform code will have to use different names for different file formats. To support that, custom section names can be specified as a string literal. The string will be used directly, without any processing, as the section name for the symbol. A new `#if objectFormat(...)` conditional compilation directive will be provided to support conditionalizing based on the file format: +Custom section names can be specified as a string literal. The string will be used directly, without any processing, as the section name for the symbol. Different object file formats (ELF, Mach-O, COFF) have different restrictions and rules on what are valid section names, and cross-platform code will have to use different names for different file formats. To support that, the `@section` attribute will allow specifying one or more section names per object file formats, optionally with a fallback: ```swift -#if objectFormat(ELF) -@section("mysection") -#elseif objectFormat(MachO) -@section("__DATA,mysection") +@section(MachO: "__DATA,mysection") var global = ... // ok when compiling for Mach-O, error if compiling for ELF +@section(MachO: "__DATA,mysection", ELF: "mysection") var global = ... +@section(MachO: "__DATA,mysection", default: "mysection") var global = ... +``` + +A new `#if objectFormat(...)` conditional compilation directive will be provided to support conditionalizing based on the file format: + +```swift +#if objectFormat(MachO) +@section(MachO: "__DATA,mysection") // Custom section on Mach-O, normal placement otherwise #endif var global = ... ``` @@ -109,21 +115,29 @@ For the ELF file format specifically, the compiler will also emit a “section i ### Attributes @section and @used on global and static variables -Two new attributes are to be added: `@section`, which has a single argument specifying the section name, and a argument-less `@used` attribute. The section name must be a string literal. The attributes can be used either together or independently. +Two new attributes are to be added: `@section`, with a labeled list of arguments specifying the section name(s), and a argument-less `@used` attribute. The attributes can be used either together or independently. + +On `@section`, the arguments are labeled by object file format type (see [Cross-platform object file format support](#cross_platform_object_file_format_support) for the full list), there must be at least one labeled argument present, and optionally there can be a `default:` argument with a fallback section. The section names must be string literals. If compiling for a object file format that is not listed, and no `default:` argument is present, the compilation fails. ```swift // (1) -@section("__DATA,mysection") +@section(MachO: "__DATA,mysection") @used let global = ... // ✅ // (2) -@section("__DATA,mysection") +@section(MachO: "__DATA,mysection") let global = ... // ✅ // (3) @used let global = ... // ✅ + +// (4) +@section let global = ... // ❌ no argument +@section(abc: "section") let global = ... // ❌ "abc" not a valid object file format +@section(ELF: "section") let global = ... // ok if compiling for ELF, compilation failure otherwise +@section(ELF: "section", default: "default_section") let global = ... // ✅ ``` The new attributes (`@section` and `@used`) can be used on variable declarations under these circumstances: @@ -137,34 +151,34 @@ The new attributes (`@section` and `@used`) can be used on variable declarations > *Note: These restrictions limit the `@section` and `@used` attributes to only be allowed on variables that are expected to be represented as exactly one statically-initialized global storage symbol (in the linker sense) for the variable’s content. This is generally true for all global and static variables in C and C++, but in Swift global variables might have zero global storage symbols (e.g. a computed property), or need non-trivial storage (e.g. lazily initialized variables with runtime code in the initializer expression).* ```swift -@section("__DATA,mysection") @used +@section(MachO: "__DATA,mysection") @used let global = 42 // ✅ -@section("__DATA,mysection") @used +@section(MachO: "__DATA,mysection") @used var global = 42 // ✅ -@section("__DATA,mysection") @used +@section(MachO: "__DATA,mysection") @used var computed: Int { return 42 } // ❌ ERROR: @section cannot be used on computed properties struct MyStruct { - @section("__DATA,mysection") @used + @section(MachO: "__DATA,mysection") @used static let staticMemberLet = 42 // ✅ - @section("__DATA,mysection") @used + @section(MachO: "__DATA,mysection") @used static var staticMemberVar = 42 // ✅ - @section("__DATA,mysection") @used + @section(MachO: "__DATA,mysection") @used let member = 42 // ❌ ERROR: @section cannot be used on non-static members - @section("__DATA,mysection") @used + @section(MachO: "__DATA,mysection") @used var member = 42 // ❌ ERROR: @section cannot be used on non-static members } struct MyGenericStruct { - @section("__DATA,mysection") @used + @section(MachO: "__DATA,mysection") @used static let staticMember = 42 // ❌ ERROR: @section cannot be used in a generic context - @section("__DATA,mysection") @used + @section(MachO: "__DATA,mysection") @used static var staticMember = 42 // ❌ ERROR: @section cannot be used in a generic context } ``` @@ -176,6 +190,8 @@ When allowed, the `@section` attribute on a variable declaration has the followi - Concretely, the section name string value will be set verbatim as a section specifier for the storage symbol at the LLVM IR level of the compiler. This means that any special behavior that the optimizer, the backend, the assembler or the linker applies based on known section names (or attributes specified as suffixes on the section name) will apply. 3. If applied to a global that is declared as part of top-level executable code (i.e. main.swift), the usual non-top-level-code initialization behavior is applied to the global. I.e. the variable is not sequentially initialized at startup. +The custom section name specified in the `@section` attribute is not validated by the compiler, instead it’s passed directly as a string to the linker. + When allowed, the `@used` attribute on a variable declaration has the following effect: 1. The storage symbol for the variable will be marked as “do not dead-strip”. @@ -199,35 +215,35 @@ This proposal defines a **constant expression** as being one of: Explicitly, this definition currently does **not allow** any operators, using any user-defined named types, any other standard type (e.g. strings, dictionaries, sets), using closures, or referencing any variables by name. See below for examples of valid and invalid constant expressions: ```swift -@section("...") let a = 42 // ✅ -@section("...") let b = 3.14 // ✅ -@section("...") let c = 1 + 1 // ❌ operators not allowed -@section("...") let d = Int.max // ❌ not a literal -@section("...") let e: UInt8 = 42 // ✅ -@section("...") let f = UInt8(42) // ❌ not a literal -@section("...") let g: MyCustomExpressibleByIntegerLiteral = 42 // ❌ not a standard type - -@section("...") let composition1 = (1, 2, 3, 2.718, true) // ✅ -@section("...") let composition2 = (1, 2, Int.max) // ❌ tuple component not constant -@section("...") let composition3: InlineArray = [1, 2, 3] // ✅ -@section("...") let composition4: InlineArray = [1, 2, Int.max] // ❌ array component not constant -@section("...") let composition5: (Int, [1 of Int], [1 of (Int, Int)]) = (1, [1], [(1, 1)]) // ✅ +@section(...) let a = 42 // ✅ +@section(...) let b = 3.14 // ✅ +@section(...) let c = 1 + 1 // ❌ operators not allowed +@section(...) let d = Int.max // ❌ not a literal +@section(...) let e: UInt8 = 42 // ✅ +@section(...) let f = UInt8(42) // ❌ not a literal +@section(...) let g: MyCustomExpressibleByIntegerLiteral = 42 // ❌ not a standard type + +@section(...) let composition1 = (1, 2, 3, 2.718, true) // ✅ +@section(...) let composition2 = (1, 2, Int.max) // ❌ tuple component not constant +@section(...) let composition3: InlineArray = [1, 2, 3] // ✅ +@section(...) let composition4: InlineArray = [1, 2, Int.max] // ❌ array component not constant +@section(...) let composition5: (Int, [1 of Int], [1 of (Int, Int)]) = (1, [1], [(1, 1)]) // ✅ func foo() -> Int { return 42 } -@section("...") let func1 = foo // ✅ -@section("...") let func2 = foo() // ❌ not a function reference -@section("...") let func3 = Bool.random // ✅ -@section("...") let func4 = Bool.self.random // ❌ not a direct reference -@section("...") let func5 = (Bool.self as Bool.Type).random // ❌ not a direct reference -@section("...") let func6 = [Int].randomElement // ❌ generic -@section("...") let func7 = { } // ❌ not using name +@section(...) let func1 = foo // ✅ +@section(...) let func2 = foo() // ❌ not a function reference +@section(...) let func3 = Bool.random // ✅ +@section(...) let func4 = Bool.self.random // ❌ not a direct reference +@section(...) let func5 = (Bool.self as Bool.Type).random // ❌ not a direct reference +@section(...) let func6 = [Int].randomElement // ❌ generic +@section(...) let func7 = { } // ❌ not using name struct S { } -@section("...") let metatype1 = S.self // ✅ -@section("...") let metatype2 = Int.self // ✅ -@section("...") let metatype3 = Int.self.self // ❌ not a direct reference +@section(...) let metatype1 = S.self // ✅ +@section(...) let metatype2 = Int.self // ✅ +@section(...) let metatype3 = Int.self.self // ❌ not a direct reference import Foundation -@section("...") let metatype4 = URL.self // ❌ resilient +@section(...) let metatype4 = URL.self // ❌ resilient ``` ### Guaranteed static initialization @@ -242,13 +258,13 @@ We consider the variable to be eligible for static initialization when: Not all constant expressions are necessarily statically initializable. For section placement we require the stronger property (static initialization) because we expect the data to be readable without any runtime mechanisms (i.e. reading raw bytes from the section at runtime, or offline binary inspection). ```swift -@section("__DATA,mysection") +@section(MachO: "__DATA,mysection") let a = 42 // ✅ -@section("__DATA,mysection") +@section(MachO: "__DATA,mysection") let sectionPlaced = ...expression... // ✅, guaranteed to be statically initialized -@section("__DATA,mysection") +@section(MachO: "__DATA,mysection") let notStaticallyInitializable = ...expression that cannot be statically initialized... // ❌ ``` @@ -266,34 +282,14 @@ As described in [Swift Compile-Time Values](https://github.com/artemcm/swift-evo ```swift func foo() { ... } -@section("__DATA,mysection") +@section(MachO: "__DATA,mysection") let a = (42, foo) // "foo" is statically initialized into a // linkable/relocatable pointer ``` ### Cross-platform object file format support -The custom section name specified in the `@section` attribute is not validated by the compiler, instead it’s passed directly as a string to the linker. Different platforms supported by Swift are using different object and binary file formats (Linux uses ELF, Darwin uses Mach-O, Windows uses COFF), and that implies different restrictions and rules on what are valid section names. Because of that, a multi-platform library code is expected to use `#if os(...)` to use different section names for different platforms. Because of that, it’s expected that the attributes are to be typically only used indirectly via macros that hide the low-level nature of sections and object file formats from higher-level code developers: - -```swift -// Example of a potential project-specific "@RegisterPlugin" macro: -@RegisterPlugin -let plugin = ... - -// The macro expands to: -#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) -@section("__DATA_CONST,plugins") -#elseif os(Linux) -@section("plugins") -#elseif os(Windows) -@section(".plugins") -#endif -let plugin = ... -``` - -See [Structured section specifiers](#structured-section-specifiers) below for more rationale. - -In some cases, it’s not possible to differentiate on the OS to support multiple object file formats, for example when using Embedded Swift to target baremetal systems without any OS. For that, a new `#if objectFormat(...)` conditional compilation directive will be provided. The allowed values in this directive will match the set of supported object file formats by the Swift compiler (and expand as needed in the future). Currently, they exact values will be (case sensitive): +In some cases, it’s useful to conditionalize code to support multiple object file formats, for example when using Embedded Swift to target baremetal systems without any OS. For that, a new `#if objectFormat(...)` conditional compilation directive will be provided. The allowed values in this directive will match the set of supported object file formats by the Swift compiler (and expand as needed in the future). Currently, they exact values will be (case sensitive): * COFF * ELF @@ -301,12 +297,10 @@ In some cases, it’s not possible to differentiate on the OS to support multipl * Wasm ```swift -#if objectFormat(MachO) -@section("__DATA_CONST,mysection") -#elseif objectFormat(ELF) -@section("mysection") +#if objectFormat(MachO) || objectFormat(ELF) +@section(MachO: "__DATA_CONST,mysection", ELF: "mysection") #endif -let value = ... +let value = ... // the value gets a custom section on MachO and ELF, but not on other object file formats ``` ### ELF section index @@ -365,7 +359,7 @@ This proposal only allows data placement into custom sections, however, placing ```swift // code for the function is placed into the custom section -@section("__TEXT,boot") +@section(MachO: "__TEXT,boot") func firmwareBootEntrypoint() { ... } ``` @@ -384,13 +378,13 @@ The notions of constant expressions and constant values is applicable to a much The requirement to only use string literals as the section names could be lifted in the future, and we might allow referring to a declaration of variable with a compile-time string. This would be useful to avoid repetition when placing multiple values into the same section without needing to use macros. ```swift -#if objectFormat(ELF) -let mySectionName = "mysection" // required to be a compile-time value -#elseif objectFormat(MachO) +#if DEBUG let mySectionName = "__DATA,mysection" // required to be a compile-time value +#else +let mySectionName = "__DATA,myothersection" // required to be a compile-time value #endif -@section(mySectionName) +@section(MachO: mySectionName) var global = ... ``` @@ -464,14 +458,6 @@ let address: UnsafePointer = #address(x) // would only work on statically i Because `@const` does not affect parsing or type resolution of the expression, it’s not helpful to the compiler, and it doesn’t seem to improve readability for users either: If the expression is a constant expression or not statically initializable, it will be rejected from compilation with a clear explanation. Adding a `@const` does not convey any new information. -### Structured section specifiers - -In Mach-O, custom section names are written as a pair of segment (e.g. `__DATA`) + section (e.g. `mysection`). Structured section names with separate segment and section names, `@section(segment: "...", section: "...")` were considered instead, however this pattern does not generalize across object file formats, and is Mach-O specific (ELF and PE/COFF don’t have segments). - -Because different object file formats impose different restrictions on custom section names (length, “.” prefix), a shorthand syntax to specify different section names for different object file formats was considered: `@section(ELF: “...”, MachO: “...”, COFF: “...”)`. This, however, has drawbacks of repeating the file format in cases where the code is only ever targeting a single format (common for example for embedded firmwares on ELF). The benefits of a shorthand syntax is marginal, given that we don’t expect normal application code to used the `@section` attribute directly but instead rely on macros or other higher-level API. - -The alternative of using conditional compilation is what is expected to be used for those cases instead. - ### Umbrella attribute for linkage properties Instead of separate `@section` and `@used` attributes, a unified attribute with parameters to control individual linkage properties was considered, spelled for example `@linkage(section: ..., used)`. Further linkage control features would be added into this umbrella attribute. From 8b9b78645b1a866244432d19126e256f4dcd195a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 9 Oct 2025 16:28:10 -0700 Subject: [PATCH 4510/4563] SE-0492: Extend review until October 14, 2025 to discuss changes --- proposals/0492-section-control.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0492-section-control.md b/proposals/0492-section-control.md index 405270c10c..9cb160550f 100644 --- a/proposals/0492-section-control.md +++ b/proposals/0492-section-control.md @@ -2,7 +2,7 @@ * Proposal: [SE-0492](0492-section-control.md) * Authors: [Kuba Mracek](https://github.com/kubamracek) -* Status: **Active review (September 22 ... October 6, 2025)** +* Status: **Active review (September 22 ... October 14, 2025)** * Implementation: available in recent `main` snapshots under the experimental feature `SymbolLinkageMarkers` and with undercored attribute names `@_section` and `@_used`. * Review: [review](https://forums.swift.org/t/se-0492-section-placement-control/82289) * Discussion threads: From af84ee6fd48954aba41852bede143ba405f581d2 Mon Sep 17 00:00:00 2001 From: Stuart Montgomery Date: Fri, 10 Oct 2025 12:43:08 -0500 Subject: [PATCH 4511/4563] Change status of ST-0015 to Implemented (#2993) --- .../0015-image-attachments-in-swift-testing-windows.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/testing/0015-image-attachments-in-swift-testing-windows.md b/proposals/testing/0015-image-attachments-in-swift-testing-windows.md index d23e8793cb..1b26056068 100644 --- a/proposals/testing/0015-image-attachments-in-swift-testing-windows.md +++ b/proposals/testing/0015-image-attachments-in-swift-testing-windows.md @@ -3,8 +3,8 @@ * Proposal: [ST-0015](0015-image-attachments-in-swift-testing-windows.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Review Manager: [Stuart Montgomery](https://github.com/stmontgomery) -* Status: **Accepted** -* Implementation: [swiftlang/swift-testing#1245](https://github.com/swiftlang/swift-testing/pull/1245), [swiftlang/swift-testing#1254](https://github.com/swiftlang/swift-testing/pull/1254), _et al_. +- Status: **Implemented (Swift 6.3)** +* Implementation: [swiftlang/swift-testing#1245](https://github.com/swiftlang/swift-testing/pull/1245), [swiftlang/swift-testing#1254](https://github.com/swiftlang/swift-testing/pull/1254), [swiftlang/swift-testing#1333](https://github.com/swiftlang/swift-testing/pull/1333), _et al_. * Review: ([pitch](https://forums.swift.org/t/pitch-image-attachments-in-swift-testing-windows/81871)) ([review](https://forums.swift.org/t/st-0015-image-attachments-in-swift-testing-windows/82241)) ([acceptance](https://forums.swift.org/t/accepted-st-0015-image-attachments-in-swift-testing-windows/82575)) ## Introduction From a7601c9f0e88652622a5171a651c7c9f1e5a8205 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Tue, 14 Oct 2025 13:24:20 -0700 Subject: [PATCH 4512/4563] Minor edits to SE-0496 --- proposals/0496-inline-always.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/0496-inline-always.md b/proposals/0496-inline-always.md index f10d161433..450c97337f 100644 --- a/proposals/0496-inline-always.md +++ b/proposals/0496-inline-always.md @@ -357,13 +357,13 @@ to the client. `@inline(always)` intention is to be able to guarantee that inlining will happen for any caller inside or outside the defining module therefore it makes sense to -require the use of "@inlinable" attribute with them. This attribute could be +require the use of an `@inlinable` attribute with them. This attribute could be required to be explicitly stated. And for it to be an error when the attribute is omitted. ```swift @inline(always) -@inlinable // or @_alwaysEmitIntoClient +@inlinable public func caller() { ... } @inline(always) // error: a public function marked @inline(always) must be marked @inlinable @@ -467,13 +467,13 @@ attribute in the future. ```swift @inlinable -public caller() { +public func caller() { if coldPath { callee() } } -public otherCaller() { +public func otherCaller() { if hotPath { callee() } From 6d878281f886e7c2a269fcd361a0a2c7413d02bf Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Tue, 14 Oct 2025 14:14:39 -0700 Subject: [PATCH 4513/4563] SE-0492: Drop per-object-format labels in the syntax of @section --- proposals/0492-section-control.md | 160 ++++++++++++++++-------------- 1 file changed, 88 insertions(+), 72 deletions(-) diff --git a/proposals/0492-section-control.md b/proposals/0492-section-control.md index 9cb160550f..1a59c13894 100644 --- a/proposals/0492-section-control.md +++ b/proposals/0492-section-control.md @@ -63,22 +63,22 @@ The proposal is to add two new attributes `@section` and `@used` that will allow // Place an entry into a section, mark as "do not dead strip". // Initializer expression must be a constant expression. // The global variable is implicitly made statically initialized. -@section(MachO: "__DATA,mysection") +@section("__DATA,mysection") @used let myLinkerSetEntry: Int = 42 // ✅ // Non-constant or statically non-initializable expressions are disallowed -@section(MachO: "__DATA,mysection") +@section("__DATA,mysection") let myLinkerSetEntry: Int = Int.random(in: 0 ..< 10) // ❌ error // Section-placed globals can be "var", the initializer expression still must be constant -@section(MachO: "__DATA,mysection") +@section("__DATA,mysection") var mutableVariable: Int = 42 // ✅ // Some complex data types are allowed (tuples, function references) typealias PluginData = (version: Int, identifier: UInt64, initializer: @convention(c) ()->()) -@section(MachO: "__DATA,plugins") +@section("__DATA,plugins") @used let myPlugin: PluginData = ( version: 1, @@ -88,19 +88,13 @@ let myPlugin: PluginData = ( On top of specifying a custom section name with the `@section` attribute, marking a variable as `@used` is needed to prevent otherwise unused symbols from being removed by the compiler. When using section placement to e.g. implement linker sets, such values are typically going to have no usage at compile time, and at the same time they should not be exposed in public interface of libraries (not be made public), therefore we the need the `@used` attribute. -Custom section names can be specified as a string literal. The string will be used directly, without any processing, as the section name for the symbol. Different object file formats (ELF, Mach-O, COFF) have different restrictions and rules on what are valid section names, and cross-platform code will have to use different names for different file formats. To support that, the `@section` attribute will allow specifying one or more section names per object file formats, optionally with a fallback: +Different object file formats (ELF, Mach-O, COFF) have different restrictions and rules on what are valid section names, and cross-platform code will have to use different names for different file formats. To support that, custom section names can be specified as a string literal. The string will be used directly, without any processing, as the section name for the symbol. A new `#if objectFormat(...)` conditional compilation directive will be provided to support conditionalizing based on the file format: ```swift -@section(MachO: "__DATA,mysection") var global = ... // ok when compiling for Mach-O, error if compiling for ELF -@section(MachO: "__DATA,mysection", ELF: "mysection") var global = ... -@section(MachO: "__DATA,mysection", default: "mysection") var global = ... -``` - -A new `#if objectFormat(...)` conditional compilation directive will be provided to support conditionalizing based on the file format: - -```swift -#if objectFormat(MachO) -@section(MachO: "__DATA,mysection") // Custom section on Mach-O, normal placement otherwise +#if objectFormat(ELF) +@section("mysection") +#elseif objectFormat(MachO) +@section("__DATA,mysection") #endif var global = ... ``` @@ -115,29 +109,21 @@ For the ELF file format specifically, the compiler will also emit a “section i ### Attributes @section and @used on global and static variables -Two new attributes are to be added: `@section`, with a labeled list of arguments specifying the section name(s), and a argument-less `@used` attribute. The attributes can be used either together or independently. - -On `@section`, the arguments are labeled by object file format type (see [Cross-platform object file format support](#cross_platform_object_file_format_support) for the full list), there must be at least one labeled argument present, and optionally there can be a `default:` argument with a fallback section. The section names must be string literals. If compiling for a object file format that is not listed, and no `default:` argument is present, the compilation fails. +Two new attributes are to be added: `@section`, which has a single argument specifying the section name, and a argument-less `@used` attribute. The section name must be a string literal. The attributes can be used either together or independently. ```swift // (1) -@section(MachO: "__DATA,mysection") +@section("__DATA,mysection") @used let global = ... // ✅ // (2) -@section(MachO: "__DATA,mysection") +@section("__DATA,mysection") let global = ... // ✅ // (3) @used let global = ... // ✅ - -// (4) -@section let global = ... // ❌ no argument -@section(abc: "section") let global = ... // ❌ "abc" not a valid object file format -@section(ELF: "section") let global = ... // ok if compiling for ELF, compilation failure otherwise -@section(ELF: "section", default: "default_section") let global = ... // ✅ ``` The new attributes (`@section` and `@used`) can be used on variable declarations under these circumstances: @@ -151,34 +137,34 @@ The new attributes (`@section` and `@used`) can be used on variable declarations > *Note: These restrictions limit the `@section` and `@used` attributes to only be allowed on variables that are expected to be represented as exactly one statically-initialized global storage symbol (in the linker sense) for the variable’s content. This is generally true for all global and static variables in C and C++, but in Swift global variables might have zero global storage symbols (e.g. a computed property), or need non-trivial storage (e.g. lazily initialized variables with runtime code in the initializer expression).* ```swift -@section(MachO: "__DATA,mysection") @used +@section("__DATA,mysection") @used let global = 42 // ✅ -@section(MachO: "__DATA,mysection") @used +@section("__DATA,mysection") @used var global = 42 // ✅ -@section(MachO: "__DATA,mysection") @used +@section("__DATA,mysection") @used var computed: Int { return 42 } // ❌ ERROR: @section cannot be used on computed properties struct MyStruct { - @section(MachO: "__DATA,mysection") @used + @section("__DATA,mysection") @used static let staticMemberLet = 42 // ✅ - @section(MachO: "__DATA,mysection") @used + @section("__DATA,mysection") @used static var staticMemberVar = 42 // ✅ - @section(MachO: "__DATA,mysection") @used + @section("__DATA,mysection") @used let member = 42 // ❌ ERROR: @section cannot be used on non-static members - @section(MachO: "__DATA,mysection") @used + @section("__DATA,mysection") @used var member = 42 // ❌ ERROR: @section cannot be used on non-static members } struct MyGenericStruct { - @section(MachO: "__DATA,mysection") @used + @section("__DATA,mysection") @used static let staticMember = 42 // ❌ ERROR: @section cannot be used in a generic context - @section(MachO: "__DATA,mysection") @used + @section("__DATA,mysection") @used static var staticMember = 42 // ❌ ERROR: @section cannot be used in a generic context } ``` @@ -215,35 +201,35 @@ This proposal defines a **constant expression** as being one of: Explicitly, this definition currently does **not allow** any operators, using any user-defined named types, any other standard type (e.g. strings, dictionaries, sets), using closures, or referencing any variables by name. See below for examples of valid and invalid constant expressions: ```swift -@section(...) let a = 42 // ✅ -@section(...) let b = 3.14 // ✅ -@section(...) let c = 1 + 1 // ❌ operators not allowed -@section(...) let d = Int.max // ❌ not a literal -@section(...) let e: UInt8 = 42 // ✅ -@section(...) let f = UInt8(42) // ❌ not a literal -@section(...) let g: MyCustomExpressibleByIntegerLiteral = 42 // ❌ not a standard type - -@section(...) let composition1 = (1, 2, 3, 2.718, true) // ✅ -@section(...) let composition2 = (1, 2, Int.max) // ❌ tuple component not constant -@section(...) let composition3: InlineArray = [1, 2, 3] // ✅ -@section(...) let composition4: InlineArray = [1, 2, Int.max] // ❌ array component not constant -@section(...) let composition5: (Int, [1 of Int], [1 of (Int, Int)]) = (1, [1], [(1, 1)]) // ✅ +@section("...") let a = 42 // ✅ +@section("...") let b = 3.14 // ✅ +@section("...") let c = 1 + 1 // ❌ operators not allowed +@section("...") let d = Int.max // ❌ not a literal +@section("...") let e: UInt8 = 42 // ✅ +@section("...") let f = UInt8(42) // ❌ not a literal +@section("...") let g: MyCustomExpressibleByIntegerLiteral = 42 // ❌ not a standard type + +@section("...") let composition1 = (1, 2, 3, 2.718, true) // ✅ +@section("...") let composition2 = (1, 2, Int.max) // ❌ tuple component not constant +@section("...") let composition3: InlineArray = [1, 2, 3] // ✅ +@section("...") let composition4: InlineArray = [1, 2, Int.max] // ❌ array component not constant +@section("...") let composition5: (Int, [1 of Int], [1 of (Int, Int)]) = (1, [1], [(1, 1)]) // ✅ func foo() -> Int { return 42 } -@section(...) let func1 = foo // ✅ -@section(...) let func2 = foo() // ❌ not a function reference -@section(...) let func3 = Bool.random // ✅ -@section(...) let func4 = Bool.self.random // ❌ not a direct reference -@section(...) let func5 = (Bool.self as Bool.Type).random // ❌ not a direct reference -@section(...) let func6 = [Int].randomElement // ❌ generic -@section(...) let func7 = { } // ❌ not using name +@section("...") let func1 = foo // ✅ +@section("...") let func2 = foo() // ❌ not a function reference +@section("...") let func3 = Bool.random // ✅ +@section("...") let func4 = Bool.self.random // ❌ not a direct reference +@section("...") let func5 = (Bool.self as Bool.Type).random // ❌ not a direct reference +@section("...") let func6 = [Int].randomElement // ❌ generic +@section("...") let func7 = { } // ❌ not using name struct S { } -@section(...) let metatype1 = S.self // ✅ -@section(...) let metatype2 = Int.self // ✅ -@section(...) let metatype3 = Int.self.self // ❌ not a direct reference +@section("...") let metatype1 = S.self // ✅ +@section("...") let metatype2 = Int.self // ✅ +@section("...") let metatype3 = Int.self.self // ❌ not a direct reference import Foundation -@section(...) let metatype4 = URL.self // ❌ resilient +@section("...") let metatype4 = URL.self // ❌ resilient ``` ### Guaranteed static initialization @@ -258,13 +244,13 @@ We consider the variable to be eligible for static initialization when: Not all constant expressions are necessarily statically initializable. For section placement we require the stronger property (static initialization) because we expect the data to be readable without any runtime mechanisms (i.e. reading raw bytes from the section at runtime, or offline binary inspection). ```swift -@section(MachO: "__DATA,mysection") +@section("__DATA,mysection") let a = 42 // ✅ -@section(MachO: "__DATA,mysection") +@section("__DATA,mysection") let sectionPlaced = ...expression... // ✅, guaranteed to be statically initialized -@section(MachO: "__DATA,mysection") +@section("__DATA,mysection") let notStaticallyInitializable = ...expression that cannot be statically initialized... // ❌ ``` @@ -282,14 +268,34 @@ As described in [Swift Compile-Time Values](https://github.com/artemcm/swift-evo ```swift func foo() { ... } -@section(MachO: "__DATA,mysection") +@section("__DATA,mysection") let a = (42, foo) // "foo" is statically initialized into a // linkable/relocatable pointer ``` ### Cross-platform object file format support -In some cases, it’s useful to conditionalize code to support multiple object file formats, for example when using Embedded Swift to target baremetal systems without any OS. For that, a new `#if objectFormat(...)` conditional compilation directive will be provided. The allowed values in this directive will match the set of supported object file formats by the Swift compiler (and expand as needed in the future). Currently, they exact values will be (case sensitive): +Different platforms supported by Swift are using different object and binary file formats (Linux uses ELF, Darwin uses Mach-O, Windows uses COFF), and that implies different restrictions and rules on what are valid section names. Because of that, a multi-platform library code is expected to use `#if os(...)` to use different section names for different platforms. Because of that, it’s expected that the attributes are to be typically only used indirectly via macros that hide the low-level nature of sections and object file formats from higher-level code developers: + +```swift +// Example of a potential project-specific "@RegisterPlugin" macro: +@RegisterPlugin +let plugin = ... + +// The macro expands to: +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@section("__DATA_CONST,plugins") +#elseif os(Linux) +@section("plugins") +#elseif os(Windows) +@section(".plugins") +#endif +let plugin = ... +``` + +See [Structured section specifiers](#structured-section-specifiers) below for more rationale. + +In some cases, it’s not possible to differentiate on the OS to support multiple object file formats, for example when using Embedded Swift to target baremetal systems without any OS. For that, a new `#if objectFormat(...)` conditional compilation directive will be provided. The allowed values in this directive will match the set of supported object file formats by the Swift compiler (and expand as needed in the future). Currently, they exact values will be (case sensitive): * COFF * ELF @@ -297,10 +303,12 @@ In some cases, it’s useful to conditionalize code to support multiple object f * Wasm ```swift -#if objectFormat(MachO) || objectFormat(ELF) -@section(MachO: "__DATA_CONST,mysection", ELF: "mysection") +#if objectFormat(MachO) +@section("__DATA_CONST,mysection") +#elseif objectFormat(ELF) +@section("mysection") #endif -let value = ... // the value gets a custom section on MachO and ELF, but not on other object file formats +let value = ... ``` ### ELF section index @@ -359,7 +367,7 @@ This proposal only allows data placement into custom sections, however, placing ```swift // code for the function is placed into the custom section -@section(MachO: "__TEXT,boot") +@section("__TEXT,boot") func firmwareBootEntrypoint() { ... } ``` @@ -378,13 +386,13 @@ The notions of constant expressions and constant values is applicable to a much The requirement to only use string literals as the section names could be lifted in the future, and we might allow referring to a declaration of variable with a compile-time string. This would be useful to avoid repetition when placing multiple values into the same section without needing to use macros. ```swift -#if DEBUG +#if objectFormat(ELF) +let mySectionName = "mysection" // required to be a compile-time value +#elseif objectFormat(MachO) let mySectionName = "__DATA,mysection" // required to be a compile-time value -#else -let mySectionName = "__DATA,myothersection" // required to be a compile-time value #endif -@section(MachO: mySectionName) +@section(mySectionName) var global = ... ``` @@ -458,6 +466,14 @@ let address: UnsafePointer = #address(x) // would only work on statically i Because `@const` does not affect parsing or type resolution of the expression, it’s not helpful to the compiler, and it doesn’t seem to improve readability for users either: If the expression is a constant expression or not statically initializable, it will be rejected from compilation with a clear explanation. Adding a `@const` does not convey any new information. +### Structured section specifiers + +In Mach-O, custom section names are written as a pair of segment (e.g. `__DATA`) + section (e.g. `mysection`). Structured section names with separate segment and section names, `@section(segment: "...", section: "...")` were considered instead, however this pattern does not generalize across object file formats, and is Mach-O specific (ELF and PE/COFF don’t have segments). + +Because different object file formats impose different restrictions on custom section names (length, “.” prefix), a shorthand syntax to specify different section names for different object file formats was considered: `@section(ELF: “...”, MachO: “...”, COFF: “...”)`. This, however, has drawbacks of repeating the file format in cases where the code is only ever targeting a single format (common for example for embedded firmwares on ELF). The benefits of a shorthand syntax is marginal, given that we don’t expect normal application code to use the `@section` attribute directly but instead rely on macros or other higher-level API. + +The alternative of using conditional compilation is what is expected to be used for those cases instead. + ### Umbrella attribute for linkage properties Instead of separate `@section` and `@used` attributes, a unified attribute with parameters to control individual linkage properties was considered, spelled for example `@linkage(section: ..., used)`. Further linkage control features would be added into this umbrella attribute. From aff84ac73a69320a3dada604971e19445c1c3c9e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 25 Sep 2025 14:20:52 -0700 Subject: [PATCH 4514/4563] Draft: Controlling function definition visibility in clients --- proposals/nnnn-definition-visibility.md | 67 +++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 proposals/nnnn-definition-visibility.md diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md new file mode 100644 index 0000000000..0e1efb1555 --- /dev/null +++ b/proposals/nnnn-definition-visibility.md @@ -0,0 +1,67 @@ +# Controlling function definition visibility in clients + +* Proposal: [SE-NNNN](NNNN-filename.md) +* Authors: [Doug Gregor](https://github.com/DougGregor/) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: Functionality is available via hidden `@_alwaysEmitIntoClient` and `@_neverEmitIntoClient` attributes in recent `main` snapshots. +* Review: ([pitch](https://forums.swift.org/...)) + +## Introduction + +A number of compiler optimizations depend on whether a caller of a particular function can see the definition (body) of that function. If the caller has access to the definition, it can be specialized for the call site, for example by substituting in generic arguments, constant argument values, or any other information known at the call site that can affect how the function is compiled. The (potentially specialized) definition can also be inlined, eliminating the overhead of a function call. Even if it is neither specialized nor inlined, the function's definition can be analyzed to help the caller produce better code. For example, if an object is passed into the function, and the function definition neither shares the object nor destroys it, the caller could potentially allocate the object on the stack rather than on the heap. + +On the other hand, making the function definition available to the caller means that you can no longer recompile only the function definition, relink the program, and see the effects of that change. This can mean slower incremental builds (because more must be rebuilt) as well as limiting the kinds of changes that can be made while retaining binary compatibility, for example when Library Evolution is enabled or a project wants to retain the ability to replace an implementation just by linking in different library versions. + +The `@inlinable` attribute introduced in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md) provides the ability to explicitly make the definition of a function available for callers. It ensures that the definition can be specialized, inlined, or otherwise used in clients to produce better code. However, it also compiles the definition into the module's binary so that the caller can choose to call it directly without emitting a copy of the definition. + +The Swift compiler has optimizations that make some functions implicitly inlinable. The primary one is cross-module-optimization (CMO), which is enabled by default in release builds with the Swift Package Manager. A more aggressive form of cross-module optimization is used in [Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md), where it is necessary to (for example) ensure that all generic functions and types get specialized. + +## Motivation + +`@inlinable` strikes a balance that enables optimization without requiring it. One can make an existing function `@inlinable` without breaking binary compatibility, and it will enable better optimizations going forward. However, `@inlinable` by itself has proven insufficient. A separate hidden attribute, `@_alwaysEmitIntoClient`, states that the definition of the function is available to clients but is not guaranteed to be available in the library binary itself. It is the primary manner in which functionality can be added to the standard library without having an impact on its ABI, allowing the back-deployment of changes as well as keeping the ABI surface smaller. + +The Embedded Swift compilation model, in particular its use of aggressive cross-module optimization, makes essentially every function inlinable. That can introduce a different kind of problem: if a particular function needs to be part of the ABI, for example because it is referenced from outside of Swift, there is no way to force the definition to be emitted into a particular binary. + +The `@inlinable` attribute provides explicit permission to the compiler to expose the definition of a function to its callers. However, the examples above illustrate that more control over when a function definition is emitted into a binary is needed for certain cases. + +## Proposed solution + +This proposal introduces two modifiers on the existing `@inlinable` attribute: + +* `@inlinable(only)`: means that the function definition is inlinable and can *only* be used by inlining. Practically speaking, this means that a client has must emit its own definition of the function in order to use it, because the defining module does not emit a copy as a public, callable symbol. This spelling formalizes `@_alwaysEmitIntoClient`. +* `@inlinable(never)`: means that the function definition is never available to callers, even if the compiler options (aggressive CMO, Embedded Swift, etc.) would make it so by default. The defining module will emit a public, callable symbol that the client can use. At the time of this writing, the Swift `main` branch provides this behavior with the `@_neverEmitIntoClient` attribute. + +The `@inlinable` attribute without a modifier remains as specified in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md): it makes the definition available to the client (for any purpose) as well as emitting a public, callable symbol. It is essentially a midpoint between `only` and `never`, leaving it up to the optimizer to determine when and how to make use of the definition. + +## Detailed design + +`@inlinable(only)` inherits all of the restrictions as `@inlinable` that are outlined in SE-0193, for example, the definition itself can only reference public entities or those that are themselves `@usableFromInline`. `@inlinable(never)`, on the other hand, suppresses inlinability and allows the definition to reference any entity visible to it. + +## Relationship to `@inline(always)` / `@inline(never)` + +The `@inline(always)` attribute [under discussion now](https://forums.swift.org/t/pitch-inline-always-attribute/82040) instructs the compiler to inline the function definition. The existing `@inline(never)` prevents the compiler from inlining the function. These have an effect on the heuristics the compiler's optimizer uses to decide when to inline. That's a matter of policy, but it does not impact whether a binary provides a definition for the given symbol that other callers can use. Therefore, despite the naming similarity between `@inline` and `@inlinable`, these concepts are orthogonal. + +The following table captures the ways in which these attributes interact. + +| | `@inline(always)` | `@inline(never)` | +| ------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| `@inlinable(only)` | Always inlined everywhere; callers emit their own definitions. | Never inlined; callers emit their own definitions. | +| `@inlinable` | Always inlined everywhere; a symbol exists that would only be used by non-Swift clients. | Never inlined; callers may emit their own definitions or may call the definition in the function's module. | +| `@inlinable(never)` | Always inlined within the function's module; a symbol exists for callers outside the function's module. | Never inlined; callers may call the definition in the function's module. | + +## Source compatibility + +Introducing new modifiers to an existing attribute has no source compatibility impact. + +## ABI compatibility + +The attribute modifiers in this proposal explicitly control ABI. `@inlinable(only)` attribute ensures that a function is not part of the ABI; `@inlinable(never)`, like the existing `@inlinable`, ensures that the function is part of the ABI. Functions that do not adopt these attributes are unaffected by this proposal. + +## Alternatives considered + +The primary alternatives here involing naming of this functionality. This proposal opts to build on the "inlinable" terminology introduced by [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md) to cover the specific notion of making a function's definition available to clients. There are many other potential spellings for these features; to me, none of them are clearly better enough to introduce new terminology vs. expanding on what `@inlinable` already does. Suggestions have included: + +* Remove the underscores from the existing attributes, which use the phrase "emit into client" to mean that the client (calling module) is responsible for emitting the definition if it needs it. This would mean two new attributes, `@alwaysEmitIntoClient` and `@neverEmitIntoClient`. +* Make this an aspect of access control. For example, `public(definition)` could say that the definition is public (in addition to the interface), and `private(definition)` could mean that the definition stays private. + From 1975f2a24312ffa7f30098c05677483163120ba2 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 30 Sep 2025 13:16:11 -0700 Subject: [PATCH 4515/4563] Switch to `@export(interface, implementation)` syntax --- proposals/nnnn-definition-visibility.md | 60 +++++++++++++++++-------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md index 0e1efb1555..80bd269d72 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/nnnn-definition-visibility.md @@ -13,55 +13,77 @@ A number of compiler optimizations depend on whether a caller of a particular fu On the other hand, making the function definition available to the caller means that you can no longer recompile only the function definition, relink the program, and see the effects of that change. This can mean slower incremental builds (because more must be rebuilt) as well as limiting the kinds of changes that can be made while retaining binary compatibility, for example when Library Evolution is enabled or a project wants to retain the ability to replace an implementation just by linking in different library versions. -The `@inlinable` attribute introduced in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md) provides the ability to explicitly make the definition of a function available for callers. It ensures that the definition can be specialized, inlined, or otherwise used in clients to produce better code. However, it also compiles the definition into the module's binary so that the caller can choose to call it directly without emitting a copy of the definition. +The `@inlinable` attribute introduced in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md) provides the ability to explicitly make the definition of a function available for callers. It ensures that the definition can be specialized, inlined, or otherwise used in clients to produce better code. However, it also compiles the definition into the module's binary so that the caller can choose to call it directly without emitting a copy of the definition. The `@inlinable` attribute has very little to do with inlining per se; rather, it's about visibility of the definition. -The Swift compiler has optimizations that make some functions implicitly inlinable. The primary one is cross-module-optimization (CMO), which is enabled by default in release builds with the Swift Package Manager. A more aggressive form of cross-module optimization is used in [Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md), where it is necessary to (for example) ensure that all generic functions and types get specialized. +The Swift compiler has optimizations that make some function definitions implicitly available across modules. The primary one is cross-module-optimization (CMO), which is enabled by default in release builds with the Swift Package Manager. A more aggressive form of cross-module optimization is used in [Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md), where it is necessary to (for example) ensure that all generic functions and types get specialized. ## Motivation `@inlinable` strikes a balance that enables optimization without requiring it. One can make an existing function `@inlinable` without breaking binary compatibility, and it will enable better optimizations going forward. However, `@inlinable` by itself has proven insufficient. A separate hidden attribute, `@_alwaysEmitIntoClient`, states that the definition of the function is available to clients but is not guaranteed to be available in the library binary itself. It is the primary manner in which functionality can be added to the standard library without having an impact on its ABI, allowing the back-deployment of changes as well as keeping the ABI surface smaller. -The Embedded Swift compilation model, in particular its use of aggressive cross-module optimization, makes essentially every function inlinable. That can introduce a different kind of problem: if a particular function needs to be part of the ABI, for example because it is referenced from outside of Swift, there is no way to force the definition to be emitted into a particular binary. +The Embedded Swift compilation model, in particular its use of aggressive cross-module optimization, makes essentially every function inlinable, including internal and private functions. That can introduce a different kind of problem: if a particular function needs to be part of the ABI, for example because it is referenced from outside of Swift, or needs to be replaceable at link time, there is no way to force the definition to be emitted into a particular binary. The `@inlinable` attribute provides explicit permission to the compiler to expose the definition of a function to its callers. However, the examples above illustrate that more control over when a function definition is emitted into a binary is needed for certain cases. ## Proposed solution -This proposal introduces two modifiers on the existing `@inlinable` attribute: +This proposal introduces a new attribute `@exported` that provides the required control over the ability of clients to make use of the callable interface or the definition of a particular function (or both). The `@exported` attribute takes one or both of the following arguments in parentheses: -* `@inlinable(only)`: means that the function definition is inlinable and can *only* be used by inlining. Practically speaking, this means that a client has must emit its own definition of the function in order to use it, because the defining module does not emit a copy as a public, callable symbol. This spelling formalizes `@_alwaysEmitIntoClient`. -* `@inlinable(never)`: means that the function definition is never available to callers, even if the compiler options (aggressive CMO, Embedded Swift, etc.) would make it so by default. The defining module will emit a public, callable symbol that the client can use. At the time of this writing, the Swift `main` branch provides this behavior with the `@_neverEmitIntoClient` attribute. +* `interface`: means that a symbol is present in the binary in a manner that can be called by clients. +* `implementation`: means that the function definition is available for clients to use for any purpose, including specializtion, inlining, or merely analyzing the body for optimization purposes. -The `@inlinable` attribute without a modifier remains as specified in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md): it makes the definition available to the client (for any purpose) as well as emitting a public, callable symbol. It is essentially a midpoint between `only` and `never`, leaving it up to the optimizer to determine when and how to make use of the definition. +The existing `@inlinable` for public symbols is subsumed by `@export(interface, implementation)`, meaning that there is a callable symbol, but the definition is also available for specialization/inlining/etc. The existing `@_alwaysEmitIntoClient` is subsumed by `@export(implementation)`, meaning that the definition is available and each client that uses it must emit their own copy of the definition, because there is not symbol. The `@_neverEmitIntoClient` attribute on `main`is subsumed by `@export(interface)`, meaning that a callable symbol is emitted but the definition is not available to callers for any reason. ## Detailed design -`@inlinable(only)` inherits all of the restrictions as `@inlinable` that are outlined in SE-0193, for example, the definition itself can only reference public entities or those that are themselves `@usableFromInline`. `@inlinable(never)`, on the other hand, suppresses inlinability and allows the definition to reference any entity visible to it. +`@export` that includes the `implementation` argument inherits all of the restrictions as `@inlinable` that are outlined in SE-0193, for example, the definition itself can only reference public entities or those that are themselves `@usableFromInline`. + +`@export` that includes `interface` always produces a symbol in the object file. + +`@export` cannot be used without arguments. ## Relationship to `@inline(always)` / `@inline(never)` -The `@inline(always)` attribute [under discussion now](https://forums.swift.org/t/pitch-inline-always-attribute/82040) instructs the compiler to inline the function definition. The existing `@inline(never)` prevents the compiler from inlining the function. These have an effect on the heuristics the compiler's optimizer uses to decide when to inline. That's a matter of policy, but it does not impact whether a binary provides a definition for the given symbol that other callers can use. Therefore, despite the naming similarity between `@inline` and `@inlinable`, these concepts are orthogonal. +The `@inline(always)` attribute [under discussion now](https://forums.swift.org/t/pitch-inline-always-attribute/82040) instructs the compiler to inline the function definition. The existing `@inline(never)` prevents the compiler from inlining the function. These have an effect on the heuristics the compiler's optimizer uses to decide when to inline. That's a matter of policy, but it does not impact whether a binary provides a definition for the given symbol that other callers can use. The notion of inlining is orthogonal to that of definition visibility and symbol availability. The following table captures the ways in which these attributes interact. -| | `@inline(always)` | `@inline(never)` | -| ------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| `@inlinable(only)` | Always inlined everywhere; callers emit their own definitions. | Never inlined; callers emit their own definitions. | -| `@inlinable` | Always inlined everywhere; a symbol exists that would only be used by non-Swift clients. | Never inlined; callers may emit their own definitions or may call the definition in the function's module. | -| `@inlinable(never)` | Always inlined within the function's module; a symbol exists for callers outside the function's module. | Never inlined; callers may call the definition in the function's module. | +| | `@inline(always)` | `@inline(never)` | +| ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| `@export(implementation)` | Always inlined everywhere; callers emit their own definitions. Use this when a function should not be part of the ABI and should always be inlined for performance reasons. | Never inlined; callers emit their own definitions. Use this when a function should not be part of the ABI but never needs to be inlined. | +| `@export(interface, implementation)` | Always inlined everywhere; a symbol exists that could only be used by non-Swift clients. | Never inlined; callers may emit their own definitions or may call the definition in the function's module. | +| `@export(interface)` | Always inlined within the function's module; a symbol exists for callers outside the function's module. | Never inlined; callers may call the definition in the function's module. Use this to fully encapsulate a function definition so that it can be replaced at link time without affecting any other code. | ## Source compatibility -Introducing new modifiers to an existing attribute has no source compatibility impact. +Introduces a new attribute. This could cause a source-compatibility problem with an attached macro of the same name, but otherwise has no impact. ## ABI compatibility -The attribute modifiers in this proposal explicitly control ABI. `@inlinable(only)` attribute ensures that a function is not part of the ABI; `@inlinable(never)`, like the existing `@inlinable`, ensures that the function is part of the ABI. Functions that do not adopt these attributes are unaffected by this proposal. +The attribute modifiers in this proposal explicitly control ABI. The `interface` argument to `@export` ensures that the function is part of the ABI; its absence in the `@export` attribute ensures that the function is not part of the ABI. Functions that do not adopt this new attribute are unaffected by this proposal. ## Alternatives considered -The primary alternatives here involing naming of this functionality. This proposal opts to build on the "inlinable" terminology introduced by [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md) to cover the specific notion of making a function's definition available to clients. There are many other potential spellings for these features; to me, none of them are clearly better enough to introduce new terminology vs. expanding on what `@inlinable` already does. Suggestions have included: +The primary alternatives here involving naming of this functionality. There are many other potential spellings for these features, including: + +### Parameterize `@inlinable` + +The `@inlinable` attribute already exists and is equivalent to the proposed `@export(interface, implementation)`. We could extend that attribute with two other forms: + +* `@inlinable(only)`, equivalent to `@export(implementation`), means that the function definition is inlinable and can *only* be used by inlining. Practically speaking, this means that a client has must emit its own definition of the function in order to use it, because the defining module does not emit a copy as a public, callable symbol. This spelling formalizes `@_alwaysEmitIntoClient`. +* `@inlinable(never)`, equivalent to `@export(interface)`, means that the function definition is never available to callers, even if the compiler options (aggressive CMO, Embedded Swift, etc.) would make it so by default. The defining module will emit a public, callable symbol that the client can use. At the time of this writing, the Swift `main` branch provides this behavior with the `@_neverEmitIntoClient` attribute. + +The `@inlinable` attribute without a modifier would remain as specified in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md): it makes the definition available to the client (for any purpose) as well as emitting a public, callable symbol. It is essentially a midpoint between `only` and `never`, leaving it up to the optimizer to determine when and how to make use of the definition. + +### Remove underscores from the existing attributes + +We could remove the underscores from the existing attributes, which use the phrase "emit into client" to mean that the client (calling module) is responsible for emitting the definition if it needs it. This would mean two new attributes, `@alwaysEmitIntoClient` and `@neverEmitIntoClient`. + +### Make this part of access control + +Instead of introducing a new attribute, the `interface` and `implementation` options could be provided to the access control modifiers, such as `public`, `open`, and `package`. This runs some risk of complicating a feature that developers learn very early on (`public`) with a very advanced notion (the proposed `@export` attribute), but is otherwise equivalent. + +## Acknowledgments -* Remove the underscores from the existing attributes, which use the phrase "emit into client" to mean that the client (calling module) is responsible for emitting the definition if it needs it. This would mean two new attributes, `@alwaysEmitIntoClient` and `@neverEmitIntoClient`. -* Make this an aspect of access control. For example, `public(definition)` could say that the definition is public (in addition to the interface), and `private(definition)` could mean that the definition stays private. +Thank you to Andy Trick for the `@export` syntax suggestion. From 147ab4e9a8ca237dc923d3715c1748ec4578df15 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 30 Sep 2025 13:22:46 -0700 Subject: [PATCH 4516/4563] Add future direction for visibility/dllexport --- proposals/nnnn-definition-visibility.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md index 80bd269d72..183163c6fb 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/nnnn-definition-visibility.md @@ -83,6 +83,17 @@ We could remove the underscores from the existing attributes, which use the phra Instead of introducing a new attribute, the `interface` and `implementation` options could be provided to the access control modifiers, such as `public`, `open`, and `package`. This runs some risk of complicating a feature that developers learn very early on (`public`) with a very advanced notion (the proposed `@export` attribute), but is otherwise equivalent. +## Future Directions + +### Visibility extensions + +The `@export` attribute could be extended to support visibility-related descriptions, such as those provided by the GCC [`visibility` attribute](https://gcc.gnu.org/wiki/Visibility) as well as the Visual C++ notions of [`dllimport` and `dllexport`](https://learn.microsoft.com/en-us/cpp/cpp/dllexport-dllimport?view=msvc-170). For example: + +```swift +@export(interface, visibility: hidden) +public func f() { } +``` + ## Acknowledgments Thank you to Andy Trick for the `@export` syntax suggestion. From 57ca9b7687cee39c6611ec4bcaf8437329b08f17 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 1 Oct 2025 16:22:49 -0700 Subject: [PATCH 4517/4563] Revisions and lots more explanation --- proposals/nnnn-definition-visibility.md | 109 +++++++++++++++++++++++- 1 file changed, 106 insertions(+), 3 deletions(-) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md index 183163c6fb..2b833a0145 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/nnnn-definition-visibility.md @@ -15,7 +15,7 @@ On the other hand, making the function definition available to the caller means The `@inlinable` attribute introduced in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md) provides the ability to explicitly make the definition of a function available for callers. It ensures that the definition can be specialized, inlined, or otherwise used in clients to produce better code. However, it also compiles the definition into the module's binary so that the caller can choose to call it directly without emitting a copy of the definition. The `@inlinable` attribute has very little to do with inlining per se; rather, it's about visibility of the definition. -The Swift compiler has optimizations that make some function definitions implicitly available across modules. The primary one is cross-module-optimization (CMO), which is enabled by default in release builds with the Swift Package Manager. A more aggressive form of cross-module optimization is used in [Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md), where it is necessary to (for example) ensure that all generic functions and types get specialized. +This proposal provides explicit control over whether a function (1) generates a callable symbol in a binary and (2) makes its definition available for callers outside the module to be used for specialization, inlining, or other optimizations. ## Motivation @@ -25,6 +25,63 @@ The Embedded Swift compilation model, in particular its use of aggressive cross- The `@inlinable` attribute provides explicit permission to the compiler to expose the definition of a function to its callers. However, the examples above illustrate that more control over when a function definition is emitted into a binary is needed for certain cases. +### Existing controls for symbols and exposing function definitions + +The Swift language model itself mostly avoids defining what symbols are emitted into the binary when compiling code. However, there are some places in the language where the presence of a symbol in the final binary has been implied: + +* `@c` declarations ([SE-0495](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0495-cdecl.md)) and `@objc` classes need to produce symbols that can be referenced by compilers for the C and Objective-C languages, respectively. +* The `@main` attribute ([SE-0281](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0281-main-attribute.md)) needs to produce a symbol that is known to the operating system's loader as an entry point. +* The `@section` and `@used` attributes ([SE-0492](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0492-section-control.md)) imply that the compiler should produce a symbol. + +Similarly, whether the definition of a function is available to callers or not is mostly outside of the realm of the language. However, it has been touched on by several language features: + +* Library Evolution ([SE-0260](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0260-library-evolution.md)) explicitly ensures that clients cannot see the definition of a function within another module that was compiled with library evolution. +* The `@inlinable` attribute ([SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md)) explicitly allows clients to see the definition of a function across modules. This attribute became particularly important with Library Evolution (above), which normally prevents clients from seeing the definition of a function. + +These are relatively indirect ways in which one can state whether a symbol should be generated for a function and whether a function's definition can be used by clients outside of the module. + +### Effect of compiler optimizations + +Outside of those constraints on the interpretation of the language, the Swift compiler and build systems have an enormous amount of flexibility as to when to emit symbols and when to make the definition of functions available to clients. Various optimizations and compilation flags can affect both of these decisions. For example: + +* Incremental compilation (typical of debug builds) allows the compiler to avoid emitting symbols for `fileprivate` and `private` functions if they aren't needed elsewhere in the file, for example because all of their uses have been inlined (or there were no uses). +* Whole-module optimization (WMO) allows the definitions of `internal` , `fileprivate`, and `private` functions to be available to other source files in the same module. The compiler may choose not to emit symbols for `internal`, `fileprivate`, or `private` entities at all if they aren't needed. (For example, because they've been inlined into all callers) +* Cross-module optimization (CMO) allows the definitions of functions to be made available to clients in other modules. The "conservative" form of CMO, which has been enabled by the Swift Package Manager since Swift 5.8, does this primarily for `public` functions. A more aggressive form of cross-module optimization can also make the definitions of `internal`, `fileprivate`, or `private` entities available to clients (for the compiler's use only!). +* [Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md) relies on WMO and the aggressive CMO described above. It will also avoid emitting symbols to binaries unless they appear to be needed, which helps reduce code size. It is also necessary, because Embedded Swift cannot create symbols for certain + +The same Swift source code may very well be compiled in a number of different ways at different times: debug builds often use incremental compilation, release builds generally use WMO and conservative CMO, and an embedded build would use the more aggressive CMO. The differences in symbol availability and the use of function definitions by clients don't generally matter. It is expected that the default behavior may shift over time: for example, the build system might enable progressively more aggressive CMO to improve performance. + +This proposal provides a mechanism to explicitly state the intent to emit symbols or provide the function definition to clients independent of the compilation mode, optimization settings, or language features (from the prior section) that infer these properties. This can be important, for example, when some external system expects certain symbols to be present, but the compiler might not choose to emit the symbol in some cases. + +### Implementation hiding + +When the definition of a function is not available to clients, it can make use of declarations that are not available to those clients. For example, it can use `internal` or `private` declarations from the same module or file, respectively, that have not been marked `@usableFromInline`. It can also use declarations imported from other modules that were imported using an `internal` or `private` import ([SE-0409](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md)). + +Although it was left to a [future direction in SE-0409](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md#hiding-dependencies-for-non-resilient-modules), implementation hiding can be used to avoid transitive dependencies on modules. For example, given the following setup: + +```swift +// module A +public func f() { } + +// module B +@_implementationOnly internal import A + +public func g() { + f() +} + +// module C +import B + +func h() { + g() +} +``` + +Module B makes use of module A only in its implementation, to call the function `A.f`. When module C imports module B, it conceptually does not need to know about module A. However, whether is true in practice depends on how the code is compiled: if `B` is built with library evolution enabled, then `C` does not need to know about `A`. If the modules are built with Embedded Swift, the definition of `B.g()` will be available to module `C`, so `C` will have to know about `A`. + +This can present a code portability problem for Embedded Swift. The proposed attribute that allows one to hide the definition of a function can help ensure that specific implementations stay hidden, making it possible to avoid transitive dependencies. It is by no means a complete solution: see the commentary about the effect of type layout on transitive dependencies in [SE-0409](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md). However, it is a practical solution for improving portability of Swift code across the different compilation modes. + ## Proposed solution This proposal introduces a new attribute `@exported` that provides the required control over the ability of clients to make use of the callable interface or the definition of a particular function (or both). The `@exported` attribute takes one or both of the following arguments in parentheses: @@ -36,13 +93,37 @@ The existing `@inlinable` for public symbols is subsumed by `@export(interface, ## Detailed design -`@export` that includes the `implementation` argument inherits all of the restrictions as `@inlinable` that are outlined in SE-0193, for example, the definition itself can only reference public entities or those that are themselves `@usableFromInline`. +`@export` that includes the `implementation` argument inherits all of the restrictions as `@inlinable` that are outlined in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md), for example, the definition itself can only reference public entities or those that are themselves `@usableFromInline`. `@export` that includes `interface` always produces a symbol in the object file. `@export` cannot be used without arguments. -## Relationship to `@inline(always)` / `@inline(never)` +### Relationship to access control + +The `@export` attribute is orthogonal to access control, because the visibility of a declaration for the programmer (`public`, `internal`, etc.) can be different from the visibility of its definition from the compiler's perspective, depending on what compiler optimizations are being used. For example, consider the following two modules: + +```swift +// module A +private func secret() { /* ... */ } + +public func f() { + secret() +} + +// module B +import A + +func g() { + f() +} +``` + +Module B cannot call the function `secret` under any circumstance. However, with aggressive CMO or Embedded Swift, the compiler will still make the definition available when compiling `B`, which can be used to (for example) inline both `f()` and `secret` into the body of `g`. + +If this behavior is not desired, the `secret` function could be marked as `@export(interface)` to ensure that it is compiled to a symbol that it usable from outside of module A. It is still `private`, meaning that it still cannot be referenced by source code outside of that file. + +### Relationship to `@inline(always)` / `@inline(never)` The `@inline(always)` attribute [under discussion now](https://forums.swift.org/t/pitch-inline-always-attribute/82040) instructs the compiler to inline the function definition. The existing `@inline(never)` prevents the compiler from inlining the function. These have an effect on the heuristics the compiler's optimizer uses to decide when to inline. That's a matter of policy, but it does not impact whether a binary provides a definition for the given symbol that other callers can use. The notion of inlining is orthogonal to that of definition visibility and symbol availability. @@ -54,6 +135,28 @@ The following table captures the ways in which these attributes interact. | `@export(interface, implementation)` | Always inlined everywhere; a symbol exists that could only be used by non-Swift clients. | Never inlined; callers may emit their own definitions or may call the definition in the function's module. | | `@export(interface)` | Always inlined within the function's module; a symbol exists for callers outside the function's module. | Never inlined; callers may call the definition in the function's module. Use this to fully encapsulate a function definition so that it can be replaced at link time without affecting any other code. | +### Embedded Swift limitations + +Embedded Swift depends on "monomorphizing" all generic functions, meaning that the compiler needs to produce a specialize with concrete generic arguments for every use in the program. It is not possible to emit a single generic implementation that works for all generic arguments. This requires the definition to be available for any module that might create a specialization: + +```swift +// module A +private func secretGeneric(_: T) { } + +public func fGeneric(_ value: T) { + secretGeneric(T) +} + +// module B +struct MyType { } + +func h() { + fGeneric(MyType()) // must specialize fGeneric and secretGeneric +} +``` + +This means that generic functions are incompatible with `@export(interface)`, because there is no way to export a generic interface without the implementation. + ## Source compatibility Introduces a new attribute. This could cause a source-compatibility problem with an attached macro of the same name, but otherwise has no impact. From b4c0d752752de515f7f2ffe6d913147a7798aa09 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 1 Oct 2025 19:36:56 -0700 Subject: [PATCH 4518/4563] Address more feedback --- proposals/nnnn-definition-visibility.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md index 2b833a0145..5c12558b5c 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/nnnn-definition-visibility.md @@ -47,7 +47,7 @@ Outside of those constraints on the interpretation of the language, the Swift co * Incremental compilation (typical of debug builds) allows the compiler to avoid emitting symbols for `fileprivate` and `private` functions if they aren't needed elsewhere in the file, for example because all of their uses have been inlined (or there were no uses). * Whole-module optimization (WMO) allows the definitions of `internal` , `fileprivate`, and `private` functions to be available to other source files in the same module. The compiler may choose not to emit symbols for `internal`, `fileprivate`, or `private` entities at all if they aren't needed. (For example, because they've been inlined into all callers) * Cross-module optimization (CMO) allows the definitions of functions to be made available to clients in other modules. The "conservative" form of CMO, which has been enabled by the Swift Package Manager since Swift 5.8, does this primarily for `public` functions. A more aggressive form of cross-module optimization can also make the definitions of `internal`, `fileprivate`, or `private` entities available to clients (for the compiler's use only!). -* [Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md) relies on WMO and the aggressive CMO described above. It will also avoid emitting symbols to binaries unless they appear to be needed, which helps reduce code size. It is also necessary, because Embedded Swift cannot create symbols for certain +* [Embedded Swift](https://github.com/swiftlang/swift-evolution/blob/main/visions/embedded-swift.md) relies on WMO and the aggressive CMO described above. It will also avoid emitting symbols to binaries unless they appear to be needed, which helps reduce code size. It is also necessary, because Embedded Swift cannot create symbols for certain functions, such as unspecialized generic functions. The same Swift source code may very well be compiled in a number of different ways at different times: debug builds often use incremental compilation, release builds generally use WMO and conservative CMO, and an embedded build would use the more aggressive CMO. The differences in symbol availability and the use of function definitions by clients don't generally matter. It is expected that the default behavior may shift over time: for example, the build system might enable progressively more aggressive CMO to improve performance. @@ -89,7 +89,7 @@ This proposal introduces a new attribute `@exported` that provides the required * `interface`: means that a symbol is present in the binary in a manner that can be called by clients. * `implementation`: means that the function definition is available for clients to use for any purpose, including specializtion, inlining, or merely analyzing the body for optimization purposes. -The existing `@inlinable` for public symbols is subsumed by `@export(interface, implementation)`, meaning that there is a callable symbol, but the definition is also available for specialization/inlining/etc. The existing `@_alwaysEmitIntoClient` is subsumed by `@export(implementation)`, meaning that the definition is available and each client that uses it must emit their own copy of the definition, because there is not symbol. The `@_neverEmitIntoClient` attribute on `main`is subsumed by `@export(interface)`, meaning that a callable symbol is emitted but the definition is not available to callers for any reason. +The existing `@inlinable` for public symbols is subsumed by `@export(interface, implementation)`, meaning that there is a callable symbol, but the definition is also available for specialization/inlining/etc. The existing `@_alwaysEmitIntoClient` is subsumed by `@export(implementation)`, meaning that the definition is available and each client that uses it must emit their own copy of the definition, because there is no symbol. The `@_neverEmitIntoClient` attribute on `main`is subsumed by `@export(interface)`, meaning that a callable symbol is emitted but the definition is not available to callers for any reason. ## Detailed design @@ -121,7 +121,7 @@ func g() { Module B cannot call the function `secret` under any circumstance. However, with aggressive CMO or Embedded Swift, the compiler will still make the definition available when compiling `B`, which can be used to (for example) inline both `f()` and `secret` into the body of `g`. -If this behavior is not desired, the `secret` function could be marked as `@export(interface)` to ensure that it is compiled to a symbol that it usable from outside of module A. It is still `private`, meaning that it still cannot be referenced by source code outside of that file. +If this behavior is not desired, there are two options. The easiest option to is mark `f` with `@export(interface)`, so that it's definition won't be available to clients and therefore cannot leak `secret`. Alternatively, the `secret` function could be marked as `@export(interface)` and `@inline(never)` to ensure that it is compiled to a symbol that it usable from outside of module A, and that its body is never inlined anywhere, including into `f`. It is still `private`, meaning that it still cannot be referenced by source code outside of that file. ### Relationship to `@inline(always)` / `@inline(never)` @@ -129,11 +129,11 @@ The `@inline(always)` attribute [under discussion now](https://forums.swift.org/ The following table captures the ways in which these attributes interact. -| | `@inline(always)` | `@inline(never)` | -| ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | -| `@export(implementation)` | Always inlined everywhere; callers emit their own definitions. Use this when a function should not be part of the ABI and should always be inlined for performance reasons. | Never inlined; callers emit their own definitions. Use this when a function should not be part of the ABI but never needs to be inlined. | -| `@export(interface, implementation)` | Always inlined everywhere; a symbol exists that could only be used by non-Swift clients. | Never inlined; callers may emit their own definitions or may call the definition in the function's module. | -| `@export(interface)` | Always inlined within the function's module; a symbol exists for callers outside the function's module. | Never inlined; callers may call the definition in the function's module. Use this to fully encapsulate a function definition so that it can be replaced at link time without affecting any other code. | +| | `@inline(always)` | `@inline(never)` | (no @inline) | +| ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| `@export(implementation)` | Always inlined everywhere; callers emit their own definitions. Use this when a function should not be part of the ABI and should always be inlined for performance reasons. | Never inlined; callers emit their own definitions. Use this when a function should not be part of the ABI but never needs to be inlined. | May be inlined. Callers emit their own definitions. Use when the function should not be part of the ABI, but leave it up to the optimizer to decide when to inline. | +| `@export(interface, implementation)` | Always inlined everywhere; a symbol exists that could only be used by non-Swift clients. | Never inlined; callers may emit their own definitions or may call the definition in the function's module. | May be inlined. Callers may emit their own definitions for specialization/inlining/etc. or may call the definition in the function's module, depending on the optimizer. | +| `@export(interface)` | Always inlined within the function's module; a symbol exists for callers outside the function's module. | Never inlined; callers may call the definition in the function's module. Use this to fully encapsulate a function definition so that it can be replaced at link time without affecting any other code. | May be inlined within the function's module, if the optimizer determines that it should be profitable. A symbol exists for callers from outside the module to use. | ### Embedded Swift limitations From 54d6e289e72ba9967aa9b3c9736939176e1ef071 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 2 Oct 2025 13:36:04 -0700 Subject: [PATCH 4519/4563] Note the `@emitIntoClient(always|never)` syntactic variant --- proposals/nnnn-definition-visibility.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md index 5c12558b5c..9b25916c4e 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/nnnn-definition-visibility.md @@ -182,6 +182,8 @@ The `@inlinable` attribute without a modifier would remain as specified in [SE-0 We could remove the underscores from the existing attributes, which use the phrase "emit into client" to mean that the client (calling module) is responsible for emitting the definition if it needs it. This would mean two new attributes, `@alwaysEmitIntoClient` and `@neverEmitIntoClient`. +A variant of this would be `@emitIntoClient(always)` or `@emitIntoClient(never)`. That does leave space for a third option to be the equivalent of `@export(interface,implementation)`. + ### Make this part of access control Instead of introducing a new attribute, the `interface` and `implementation` options could be provided to the access control modifiers, such as `public`, `open`, and `package`. This runs some risk of complicating a feature that developers learn very early on (`public`) with a very advanced notion (the proposed `@export` attribute), but is otherwise equivalent. From b27565b747a636f3cfae1a989fa94f734b627695 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 2 Oct 2025 15:16:55 -0700 Subject: [PATCH 4520/4563] Allow `@export` on stored properties and types --- proposals/nnnn-definition-visibility.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md index 9b25916c4e..04f4bc6f8c 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/nnnn-definition-visibility.md @@ -157,6 +157,14 @@ func h() { This means that generic functions are incompatible with `@export(interface)`, because there is no way to export a generic interface without the implementation. +### `@export` attribute on stored properties and types + +Stored properties and types can also result in symbols being produced within the binary, in much the same manner as functions. For stored properties, the symbols describe the storage itself. For types, the symbols are for metadata associated with the type. In both cases, it can be reasonable for the compiler to defer creation of the symbol until use. For example, Embedded Swift will defer emission of a stored property until it is referenced, and will only emit type metadata when it is required (e.g., for use with `AnyObject`). + +For stored properties and types,`@exported(interface)` would emit the stored property symbol and type metadata eagerly, similar to the emission of the symbol for a function. + +`@exported(implementation)` is less immediately relevant. For stored properties, it could mean that the initializer value is available for clients to use. For types, it would effectively be the equivalent of `@frozen` in Library Evolution (`@frozen` exposes implementation and layout details), but there does not exist a notion of non-`@frozen` types outside of Library Evolution. + ## Source compatibility Introduces a new attribute. This could cause a source-compatibility problem with an attached macro of the same name, but otherwise has no impact. From 51e4c95b144a5ee6cb509044053383625b5b4273 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 7 Oct 2025 16:03:44 -0700 Subject: [PATCH 4521/4563] Document relationship to @usableFromInline --- proposals/nnnn-definition-visibility.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md index 04f4bc6f8c..6c3e972602 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/nnnn-definition-visibility.md @@ -89,11 +89,18 @@ This proposal introduces a new attribute `@exported` that provides the required * `interface`: means that a symbol is present in the binary in a manner that can be called by clients. * `implementation`: means that the function definition is available for clients to use for any purpose, including specializtion, inlining, or merely analyzing the body for optimization purposes. -The existing `@inlinable` for public symbols is subsumed by `@export(interface, implementation)`, meaning that there is a callable symbol, but the definition is also available for specialization/inlining/etc. The existing `@_alwaysEmitIntoClient` is subsumed by `@export(implementation)`, meaning that the definition is available and each client that uses it must emit their own copy of the definition, because there is no symbol. The `@_neverEmitIntoClient` attribute on `main`is subsumed by `@export(interface)`, meaning that a callable symbol is emitted but the definition is not available to callers for any reason. +The existing `@_alwaysEmitIntoClient` is subsumed by `@export(implementation)`, meaning that the definition is available and each client that uses it must emit their own copy of the definition, because there is no symbol. The `@_neverEmitIntoClient` attribute on `main` is subsumed by `@export(interface)`, meaning that a callable symbol is emitted but the definition is not available to callers for any reason. + +The two attributes introduced by [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md) are mostly subsumed by `@export`: + +* `@inlinable` for public symbols provides both a definition in the owning module and makes the definition available for callers, akin to `@export(interface, implementation)`. Note that `@inlinable` doesn't *guarantee* the presence of a symbol in the way that `@export(interface)` does: in practice, Embedded Swift may not produce a symbol, but non-Embedded Swift will. +* `@usableFromInline` for making a less-than-public symbol available for use in an inlinable function (per SE-0193) is akin to `@export(interface)`. As with `@inlinable`, `@usableFromInline` does not *guarantee* the presence of a symbol in the way that `@export(interface)` does: in practice, Embedded Swift may not produce a symbol, but non-Embedded Swift will. + +`@export` cannot be combined with any of `@inlinable`, `@usableFromInline`, `@_alwaysEmitIntoClient`, or `@_neverEmitIntoClient`. ## Detailed design -`@export` that includes the `implementation` argument inherits all of the restrictions as `@inlinable` that are outlined in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md), for example, the definition itself can only reference public entities or those that are themselves `@usableFromInline`. +`@export` that includes the `implementation` argument inherits all of the restrictions as `@inlinable` that are outlined in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md), for example, the definition itself can only reference public entities or those that are themselves `@usableFromInline` or `@export(interface)`. `@export` that includes `interface` always produces a symbol in the object file. @@ -129,7 +136,7 @@ The `@inline(always)` attribute [under discussion now](https://forums.swift.org/ The following table captures the ways in which these attributes interact. -| | `@inline(always)` | `@inline(never)` | (no @inline) | +| | `@inline(always)` | `@inline(never)` | (no `@inline`) | | ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | `@export(implementation)` | Always inlined everywhere; callers emit their own definitions. Use this when a function should not be part of the ABI and should always be inlined for performance reasons. | Never inlined; callers emit their own definitions. Use this when a function should not be part of the ABI but never needs to be inlined. | May be inlined. Callers emit their own definitions. Use when the function should not be part of the ABI, but leave it up to the optimizer to decide when to inline. | | `@export(interface, implementation)` | Always inlined everywhere; a symbol exists that could only be used by non-Swift clients. | Never inlined; callers may emit their own definitions or may call the definition in the function's module. | May be inlined. Callers may emit their own definitions for specialization/inlining/etc. or may call the definition in the function's module, depending on the optimizer. | From 6de2bc20cfd86179b05c609a0cd0ab34751e7204 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 14 Oct 2025 12:32:30 -0700 Subject: [PATCH 4522/4563] Settle on @export --- proposals/nnnn-definition-visibility.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md index 6c3e972602..d392debed1 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/nnnn-definition-visibility.md @@ -84,7 +84,7 @@ This can present a code portability problem for Embedded Swift. The proposed att ## Proposed solution -This proposal introduces a new attribute `@exported` that provides the required control over the ability of clients to make use of the callable interface or the definition of a particular function (or both). The `@exported` attribute takes one or both of the following arguments in parentheses: +This proposal introduces a new attribute `@export` that provides the required control over the ability of clients to make use of the callable interface or the definition of a particular function (or both). The `@export` attribute takes one or both of the following arguments in parentheses: * `interface`: means that a symbol is present in the binary in a manner that can be called by clients. * `implementation`: means that the function definition is available for clients to use for any purpose, including specializtion, inlining, or merely analyzing the body for optimization purposes. @@ -168,9 +168,9 @@ This means that generic functions are incompatible with `@export(interface)`, be Stored properties and types can also result in symbols being produced within the binary, in much the same manner as functions. For stored properties, the symbols describe the storage itself. For types, the symbols are for metadata associated with the type. In both cases, it can be reasonable for the compiler to defer creation of the symbol until use. For example, Embedded Swift will defer emission of a stored property until it is referenced, and will only emit type metadata when it is required (e.g., for use with `AnyObject`). -For stored properties and types,`@exported(interface)` would emit the stored property symbol and type metadata eagerly, similar to the emission of the symbol for a function. +For stored properties and types,`@export(interface)` would emit the stored property symbol and type metadata eagerly, similar to the emission of the symbol for a function. -`@exported(implementation)` is less immediately relevant. For stored properties, it could mean that the initializer value is available for clients to use. For types, it would effectively be the equivalent of `@frozen` in Library Evolution (`@frozen` exposes implementation and layout details), but there does not exist a notion of non-`@frozen` types outside of Library Evolution. +`@export(implementation)` is less immediately relevant. For stored properties, it could mean that the initializer value is available for clients to use. For types, it would effectively be the equivalent of `@frozen` in Library Evolution (`@frozen` exposes implementation and layout details), but there does not exist a notion of non-`@frozen` types outside of Library Evolution. ## Source compatibility From 8ea98b9210cda4cad7a2c96cccd7e7dffcfb16f9 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 14 Oct 2025 12:35:02 -0700 Subject: [PATCH 4523/4563] Fix a misplaced backtick --- proposals/nnnn-definition-visibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md index d392debed1..5f5a227008 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/nnnn-definition-visibility.md @@ -188,7 +188,7 @@ The primary alternatives here involving naming of this functionality. There are The `@inlinable` attribute already exists and is equivalent to the proposed `@export(interface, implementation)`. We could extend that attribute with two other forms: -* `@inlinable(only)`, equivalent to `@export(implementation`), means that the function definition is inlinable and can *only* be used by inlining. Practically speaking, this means that a client has must emit its own definition of the function in order to use it, because the defining module does not emit a copy as a public, callable symbol. This spelling formalizes `@_alwaysEmitIntoClient`. +* `@inlinable(only)`, equivalent to `@export(implementation)`, means that the function definition is inlinable and can *only* be used by inlining. Practically speaking, this means that a client has must emit its own definition of the function in order to use it, because the defining module does not emit a copy as a public, callable symbol. This spelling formalizes `@_alwaysEmitIntoClient`. * `@inlinable(never)`, equivalent to `@export(interface)`, means that the function definition is never available to callers, even if the compiler options (aggressive CMO, Embedded Swift, etc.) would make it so by default. The defining module will emit a public, callable symbol that the client can use. At the time of this writing, the Swift `main` branch provides this behavior with the `@_neverEmitIntoClient` attribute. The `@inlinable` attribute without a modifier would remain as specified in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md): it makes the definition available to the client (for any purpose) as well as emitting a public, callable symbol. It is essentially a midpoint between `only` and `never`, leaving it up to the optimizer to determine when and how to make use of the definition. From efcb8289d42d1dc7791dcb3e90cc4c73dd78cf6f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 14 Oct 2025 13:04:05 -0700 Subject: [PATCH 4524/4563] Pitch link --- proposals/nnnn-definition-visibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md index 5f5a227008..4b126dc07d 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/nnnn-definition-visibility.md @@ -5,7 +5,7 @@ * Review Manager: TBD * Status: **Awaiting review** * Implementation: Functionality is available via hidden `@_alwaysEmitIntoClient` and `@_neverEmitIntoClient` attributes in recent `main` snapshots. -* Review: ([pitch](https://forums.swift.org/...)) +* Review: ([pitch](https://forums.swift.org/t/pitch-controlling-function-definition-visibility-in-clients/82372)) ## Introduction From 3a80bb2439a2a29045f7db506ae34eec817131f9 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 14 Oct 2025 13:21:55 -0700 Subject: [PATCH 4525/4563] Fix typo --- proposals/nnnn-definition-visibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md index 4b126dc07d..13bae55390 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/nnnn-definition-visibility.md @@ -87,7 +87,7 @@ This can present a code portability problem for Embedded Swift. The proposed att This proposal introduces a new attribute `@export` that provides the required control over the ability of clients to make use of the callable interface or the definition of a particular function (or both). The `@export` attribute takes one or both of the following arguments in parentheses: * `interface`: means that a symbol is present in the binary in a manner that can be called by clients. -* `implementation`: means that the function definition is available for clients to use for any purpose, including specializtion, inlining, or merely analyzing the body for optimization purposes. +* `implementation`: means that the function definition is available for clients to use for any purpose, including specialization, inlining, or merely analyzing the body for optimization purposes. The existing `@_alwaysEmitIntoClient` is subsumed by `@export(implementation)`, meaning that the definition is available and each client that uses it must emit their own copy of the definition, because there is no symbol. The `@_neverEmitIntoClient` attribute on `main` is subsumed by `@export(interface)`, meaning that a callable symbol is emitted but the definition is not available to callers for any reason. From 95610e749bf8a09ed6919b9e18e5f414f9426a99 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 14 Oct 2025 14:44:54 -0700 Subject: [PATCH 4526/4563] Add a future direction on implementation hiding for internal/private imports --- proposals/nnnn-definition-visibility.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/nnnn-definition-visibility.md index 13bae55390..ad0d0c508e 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/nnnn-definition-visibility.md @@ -214,6 +214,10 @@ The `@export` attribute could be extended to support visibility-related descript public func f() { } ``` +### Implementation hiding for internal and private imports + +One of the motivations for this proposal is implementation hiding for uses of `private` and `internal` imports. This motivation would be weakened by the [future direction in SE-0409](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md#hiding-dependencies-for-non-resilient-modules) where the transitive dependencies from those imports are hidden, because one would no longer need to use `@export(interface)` to explicitly hide a transitive dependency. In such a case, `@export(interface)` would make explicit what would happen implicitly when the definition of such a function references something available via an internal import. + ## Acknowledgments Thank you to Andy Trick for the `@export` syntax suggestion. From d89c1959e61aa3eb4d9aae93fe9437054007f62b Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Tue, 14 Oct 2025 15:41:09 -0700 Subject: [PATCH 4527/4563] Schedule review for SE-0497 --- ...finition-visibility.md => 0497-definition-visibility.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{nnnn-definition-visibility.md => 0497-definition-visibility.md} (99%) diff --git a/proposals/nnnn-definition-visibility.md b/proposals/0497-definition-visibility.md similarity index 99% rename from proposals/nnnn-definition-visibility.md rename to proposals/0497-definition-visibility.md index ad0d0c508e..137f6d4ed2 100644 --- a/proposals/nnnn-definition-visibility.md +++ b/proposals/0497-definition-visibility.md @@ -1,9 +1,9 @@ # Controlling function definition visibility in clients -* Proposal: [SE-NNNN](NNNN-filename.md) +* Proposal: [SE-0497](0497-filename.md) * Authors: [Doug Gregor](https://github.com/DougGregor/) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) +* Status: **Active review (October 13...20, 2025)** * Implementation: Functionality is available via hidden `@_alwaysEmitIntoClient` and `@_neverEmitIntoClient` attributes in recent `main` snapshots. * Review: ([pitch](https://forums.swift.org/t/pitch-controlling-function-definition-visibility-in-clients/82372)) From aac23c18b85514b84762e7dba217cf1ebcd83a8a Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Tue, 14 Oct 2025 17:11:21 -0700 Subject: [PATCH 4528/4563] Start SE-0497 --- proposals/0497-definition-visibility.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0497-definition-visibility.md b/proposals/0497-definition-visibility.md index 137f6d4ed2..a2ad773dbe 100644 --- a/proposals/0497-definition-visibility.md +++ b/proposals/0497-definition-visibility.md @@ -1,11 +1,11 @@ # Controlling function definition visibility in clients -* Proposal: [SE-0497](0497-filename.md) +* Proposal: [SE-0497](0497-definition-visibility.md) * Authors: [Doug Gregor](https://github.com/DougGregor/) * Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) * Status: **Active review (October 13...20, 2025)** * Implementation: Functionality is available via hidden `@_alwaysEmitIntoClient` and `@_neverEmitIntoClient` attributes in recent `main` snapshots. -* Review: ([pitch](https://forums.swift.org/t/pitch-controlling-function-definition-visibility-in-clients/82372)) +* Review: ([pitch](https://forums.swift.org/t/pitch-controlling-function-definition-visibility-in-clients/82372)) ([review](https://forums.swift.org/t/se-0497-controlling-function-definition-visibility-in-clients/82666)) ## Introduction From da6f0884f23cdb2ef476be46c0167b5686a3f434 Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Tue, 14 Oct 2025 23:52:12 -0700 Subject: [PATCH 4529/4563] Fix the review date for 0495-cdecl.md --- proposals/0495-cdecl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0495-cdecl.md b/proposals/0495-cdecl.md index debdcb3ed4..727e910214 100644 --- a/proposals/0495-cdecl.md +++ b/proposals/0495-cdecl.md @@ -3,7 +3,7 @@ * Proposal: [SE-0495](0495-cdecl.md) * Author: [Alexis Laferrière](https://github.com/xymus) * Review Manager: [Steve Canon](https://github.com/stephentyrone) -* Status: **Active Review (Sept 25 ... Oct 9, 2025)** +* Status: **Active Review (September 25th...October 9th, 2025)** * Implementation: On `main` with the experimental feature flags `CDecl` for `@c`, and `CImplementation` for `@c @implementation`. With the exception of the `@objc` support for global functions which is available under the name `@_cdecl`. * Review: ([pitch](https://forums.swift.org/t/pitch-formalize-cdecl/79557))([review](https://forums.swift.org/t/se-0495-c-compatible-functions-and-enums/82365)) From fab10496b02598ccec1c32c0d9412da88f1369bb Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Tue, 14 Oct 2025 23:54:54 -0700 Subject: [PATCH 4530/4563] Fix the review date for SE-0490 --- proposals/0490-environment-constrained-shared-libraries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0490-environment-constrained-shared-libraries.md b/proposals/0490-environment-constrained-shared-libraries.md index d4d9447eb1..39e3624387 100644 --- a/proposals/0490-environment-constrained-shared-libraries.md +++ b/proposals/0490-environment-constrained-shared-libraries.md @@ -3,7 +3,7 @@ * Proposal: [SE-0490](0490-environment-constrained-shared-libraries.md) * Authors: [tayloraswift](https://github.com/tayloraswift) * Review Manager: [Alastair Houghton](https://github.com/al45tair) -* Status: **Active Review (Sep 5 - Sep 18, 2025)** +* Status: **Active Review (September 5th...September 18th, 2025)** * Implementation: [swiftlang/swift-package-manager#8249](https://github.com/swiftlang/swift-package-manager/pull/8249) * Documentation: [How to use Environment-Constrained Shared Libraries](https://github.com/swiftlang/swift-package-manager/blob/1eaf59d2facc74c88574f38395aa49983b2badcc/Documentation/ECSLs.md) * Bugs: [SR-5714](https://github.com/swiftlang/swift-package-manager/issues/5714) From c4f5b46b8c99800457cf4204aa1a52c4a2122d76 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Wed, 15 Oct 2025 10:54:17 -0400 Subject: [PATCH 4531/4563] Update ST-0014 Minor tweak to **alternatives considered** in ST-0014 to give an example of a platform that does not offer support for a wide variety of image formats. --- .../0014-image-attachments-in-swift-testing-apple-platforms.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md b/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md index b4c95d641a..0aa9221b7a 100644 --- a/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md +++ b/proposals/testing/0014-image-attachments-in-swift-testing-apple-platforms.md @@ -408,7 +408,8 @@ None needed. the various graphics libraries in use today. If we provided a larger set of formats that are supported on Apple's platforms, developers may run into difficulties porting their test code to platforms that _don't_ support those - additional formats. + additional formats. For example, Android's [image encoding API](https://developer.android.com/reference/android/graphics/Bitmap.CompressFormat) + only supports the PNG, JPEG, and WEBP formats. ## Acknowledgments From 9abfb895d93bf217e96d26dd9175dd89675ad236 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 15 Oct 2025 10:27:26 -0700 Subject: [PATCH 4532/4563] Correct scheduling of SE-0497 The review was accidentally scheduled to end after one week instead of two. --- proposals/0497-definition-visibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0497-definition-visibility.md b/proposals/0497-definition-visibility.md index a2ad773dbe..71dfeb6678 100644 --- a/proposals/0497-definition-visibility.md +++ b/proposals/0497-definition-visibility.md @@ -3,7 +3,7 @@ * Proposal: [SE-0497](0497-definition-visibility.md) * Authors: [Doug Gregor](https://github.com/DougGregor/) * Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) -* Status: **Active review (October 13...20, 2025)** +* Status: **Active review (October 13...27, 2025)** * Implementation: Functionality is available via hidden `@_alwaysEmitIntoClient` and `@_neverEmitIntoClient` attributes in recent `main` snapshots. * Review: ([pitch](https://forums.swift.org/t/pitch-controlling-function-definition-visibility-in-clients/82372)) ([review](https://forums.swift.org/t/se-0497-controlling-function-definition-visibility-in-clients/82666)) From d692ec05eaa752b35198fa8f9e18498aa7785d79 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Oct 2025 14:33:07 -0700 Subject: [PATCH 4533/4563] [SE-0492] Accept SE-0492: Section Placement Control --- proposals/0492-section-control.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0492-section-control.md b/proposals/0492-section-control.md index 1a59c13894..f0a706ce7c 100644 --- a/proposals/0492-section-control.md +++ b/proposals/0492-section-control.md @@ -2,7 +2,7 @@ * Proposal: [SE-0492](0492-section-control.md) * Authors: [Kuba Mracek](https://github.com/kubamracek) -* Status: **Active review (September 22 ... October 14, 2025)** +* Status: **Accepted** * Implementation: available in recent `main` snapshots under the experimental feature `SymbolLinkageMarkers` and with undercored attribute names `@_section` and `@_used`. * Review: [review](https://forums.swift.org/t/se-0492-section-placement-control/82289) * Discussion threads: From c295686f1f9b9b8c86e4e80857960ee12017dc69 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 15 Oct 2025 20:20:24 -0400 Subject: [PATCH 4534/4563] Accept SE-0494 with modifications --- proposals/0494-add-is-identical-methods.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0494-add-is-identical-methods.md b/proposals/0494-add-is-identical-methods.md index 076d35f9a5..61a61e9427 100644 --- a/proposals/0494-add-is-identical-methods.md +++ b/proposals/0494-add-is-identical-methods.md @@ -3,9 +3,9 @@ * Proposal: [SE-0494](0494-add-is-identical-methods.md) * Authors: [Rick van Voorden](https://github.com/vanvoorden), [Karoy Lorentey](https://github.com/lorentey) * Review Manager: [John McCall](https://github.com/rjmccall) -* Status: **Active review (September 22nd...October 6th, 2025)** +* Status: **Accepted with modifications** * Implementation: ([String, Substring](https://github.com/swiftlang/swift/pull/82055)), ([Array, ArraySlice, ContiguousArray](https://github.com/swiftlang/swift/pull/82438)), ([Dictionary, Set](https://github.com/swiftlang/swift/pull/82439)) -* Review: ([prepitch](https://forums.swift.org/t/-/78792)) ([first pitch](https://forums.swift.org/t/-/79145)) ([second pitch](https://forums.swift.org/t/-/80496)) ([review](https://forums.swift.org/t/se-0494-add-isidentical-to-methods-for-quick-comparisons-to-concrete-types/82296)) +* Review: ([prepitch](https://forums.swift.org/t/-/78792)) ([first pitch](https://forums.swift.org/t/-/79145)) ([second pitch](https://forums.swift.org/t/-/80496)) ([review](https://forums.swift.org/t/se-0494-add-isidentical-to-methods-for-quick-comparisons-to-concrete-types/82296)) ([revision](https://forums.swift.org/t/se-0494-add-isidentical-to-methods-for-quick-comparisons-to-concrete-types/82296/142)) ([acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0494-add-isidentical-to-methods-for-quick-comparison-to-concrete-types/82695)) ### Table of Contents From b0b569de28231f62ad18b64af2a5b0ed44d87e51 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 15 Oct 2025 19:32:13 -0700 Subject: [PATCH 4535/4563] [SE-0492] Add link to acceptance --- proposals/0492-section-control.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0492-section-control.md b/proposals/0492-section-control.md index f0a706ce7c..441e4a760a 100644 --- a/proposals/0492-section-control.md +++ b/proposals/0492-section-control.md @@ -4,7 +4,7 @@ * Authors: [Kuba Mracek](https://github.com/kubamracek) * Status: **Accepted** * Implementation: available in recent `main` snapshots under the experimental feature `SymbolLinkageMarkers` and with undercored attribute names `@_section` and `@_used`. -* Review: [review](https://forums.swift.org/t/se-0492-section-placement-control/82289) +* Review: [review](https://forums.swift.org/t/se-0492-section-placement-control/82289), [acceptance](https://forums.swift.org/t/accepted-with-modifications-se-0492-section-placement-control/82701) * Discussion threads: * Pitch #1: https://forums.swift.org/t/pitch-low-level-linkage-control-attributes-used-and-section/65877 * Pitch #2: https://forums.swift.org/t/pitch-2-low-level-linkage-control/69752 From b0e8ec080e182f3f41683536cf4909a2e3342352 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Thu, 16 Oct 2025 09:18:32 -0700 Subject: [PATCH 4536/4563] Fix references to `class` methods of `class` types The are dynamically dispatched --- proposals/0496-inline-always.md | 52 ++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/proposals/0496-inline-always.md b/proposals/0496-inline-always.md index 450c97337f..f964562a62 100644 --- a/proposals/0496-inline-always.md +++ b/proposals/0496-inline-always.md @@ -133,6 +133,16 @@ determined dynamically at runtime: func a(c: C) { c.method() } +- Calls through `class` methods on `class` types and the method referenced + is not `final` + ```swift + class C { + @inline(always) + class func method() {...} + } + func a(c: C.Type) { + c.method() + } ``` In such cases, the compiler cannot determine at a call site which function is @@ -144,8 +154,8 @@ function references in the following: - Calls to free standing functions - Calls to methods of `actor`, `struct`, `enum` type -- Calls to final methods of `class` type, and type (`static/class`) methods of - `class` type +- Calls to final methods, final `class` type methods of `class` type, and + `static` type methods of `class` type Therefore, in cases where the value of the function at a usage site is dynamically derived we don't emit an error even if the dynamic value of the @@ -171,9 +181,10 @@ Listing the different scenarios that can occur for a function marked with ### Direct function references Calls to freestanding functions, methods of `enum`, `struct`, `actor` types, -final methods of `class` types, and type methods of `class` types don't -dynamically dispatch to different implementations. Calls to such methods can -always be inlined barring the recursion limitation (see later). (case 1) +final methods of `class` types, and `static` (but not `class`) type methods of +`class` types don't dynamically dispatch to different implementations. Calls to +such methods can always be inlined barring the recursion limitation (see later). +(case 1) ```swift struct S { @@ -191,7 +202,10 @@ class C { final func finalMethod() {} @inline(always) - class func method() {} + static func method() {} + + @inline(always) + final class func finalTypeMethod() } class Sub : C {} @@ -202,6 +216,8 @@ func f2() { let c2: Sub = .. c2.finalMethod() // can definitely be inlined C.method() // can definitely be inlined + let c: C.Type = ... + c.finalTypeMethod() // can definitely be inlined } @inline(always) @@ -215,28 +231,36 @@ func f3() { ### Non final class methods -Swift performs dynamic dispatch for non-final methods of classes based on the -dynamic receiver type of the class instance value at a use site. Inferring the -value of that dynamic computation at compile time is not possible in many cases -and the success of inlining cannot be ensured. We treat a non-final method -declaration with `@inline(always)` as an declaration site error because we -assume that the intention of the attribute is that the method will be inlined in -most cases and this cannot be guaranteed (case 3). +Swift performs dynamic dispatch for non-final methods of classes and non final +`class` methods of classes based on the dynamic receiver type of the class +instance/class type value at a use site. Inferring the value of that dynamic +computation at compile time is not possible in many cases and the success of +inlining cannot be ensured. We treat a non-final method declaration with +`@inline(always)` as an declaration site error because we assume that the +intention of the attribute is that the method will be inlined in most cases and +this cannot be guaranteed (case 3). ```swift class C { @inline(always) // error: non-final method marked @inline(always) func method() {} + + @inline(always) // error: non-final method marked @inline(always) + class func class_method() {} } class C2 : C { @inline(always) // error: non-final method marked @inline(always) override func method() {} + + class func class_method() {} } -func f(c: C) { +func f(c: C, c2: C.Type) { c.method() // dynamic type of c might be C or C2, could not ensure success // of inlining in general + c2.method() // dynamic type of c2 might be C.self or C2.self, could not + // ensure success of inlining in general } ``` From d75b62debcbb7103c9534ad1f55c9077e14c8182 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Thu, 16 Oct 2025 09:42:46 -0700 Subject: [PATCH 4537/4563] Fix class_method reference --- proposals/0496-inline-always.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0496-inline-always.md b/proposals/0496-inline-always.md index f964562a62..10b74a18ba 100644 --- a/proposals/0496-inline-always.md +++ b/proposals/0496-inline-always.md @@ -259,8 +259,8 @@ class C2 : C { func f(c: C, c2: C.Type) { c.method() // dynamic type of c might be C or C2, could not ensure success // of inlining in general - c2.method() // dynamic type of c2 might be C.self or C2.self, could not - // ensure success of inlining in general + c2.class_method() // dynamic type of c2 might be C.self or C2.self, could not + // ensure success of inlining in general } ``` From bc2130df0ad2d65c0fa23a3d2b82a6b37e0cf09d Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Tue, 7 Oct 2025 16:06:04 -0400 Subject: [PATCH 4538/4563] [ST-NNNN] Adjustments to image attachments in Swift Testing --- .../NNNN-image-attachment-adjustments.md | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 proposals/testing/NNNN-image-attachment-adjustments.md diff --git a/proposals/testing/NNNN-image-attachment-adjustments.md b/proposals/testing/NNNN-image-attachment-adjustments.md new file mode 100644 index 0000000000..600b0d155b --- /dev/null +++ b/proposals/testing/NNNN-image-attachment-adjustments.md @@ -0,0 +1,166 @@ +# Adjustments to image attachments in Swift Testing + +* Proposal: [ST-NNNN](NNNN-image-attachment-adjustments.md) +* Authors: [Jonathan Grynspan](https://github.com/grynspan) +* Review Manager: TBD +* Status: **Awaiting review** +* Implementation: [swiftlang/swift-testing#1359](https://github.com/swiftlang/swift-testing/pull/1359) +* Review: ([pitch](https://forums.swift.org/...)) + +## Introduction + +This proposal includes a small number of adjustments to the API surface of Swift +Testing's image attachments feature introduced in [ST-0014](0014-image-attachments-in-swift-testing-apple-platforms.md) +and [ST-0015](0015-image-attachments-in-swift-testing-windows.md). + +## Motivation + +These changes will help to align the platform-specific interfaces of the feature +more closely. + +## Proposed solution + +The `AttachableAsCGImage` and `AttachableAsIWICBitmapSource` protocols are +combined into a single protocol, `AttachableAsImage` with adjusted protocol +requirements; a change is made to `AttachableImageFormat` to more closely +align its interface between Darwin and Windows; `AttachableImageFormat` is made +to conform to `Equatable` and `Hashable`; and an additional property is added to +`Attachment` to query its image format. + +## Detailed design + +The following changes are proposed: + +### Combining AttachableAsCGImage and AttachableAsIWICBitmapSource + +The `AttachableAsCGImage` and `AttachableAsIWICBitmapSource` protocols are +combined into a single protocol, `AttachableAsImage`. + +These platform-specific requirements are removed: + +```diff +- var attachableCGImage: CGImage { get throws } +- func copyAttachableIWICBitmapSource() throws -> UnsafeMutablePointer +``` + +They are replaced with a new requirement that encapsulates the image encoding +operation. This requirement is implemented by the CoreGraphics and WinSDK +overlays and is made publicly available for test authors who wish to declare +additional conformances to this protocol for types that are not based on +`CGImage` or `IWICBitmapSource`: + +```swift +public protocol AttachableAsImage { + // ... + + /// Encode a representation of this image in a given image format. + /// + /// - Parameters: + /// - imageFormat: The image format to use when encoding this image. + /// - body: A function to call. A temporary buffer containing a data + /// representation of this instance is passed to it. + /// + /// - Returns: Whatever is returned by `body`. + /// + /// - Throws: Whatever is thrown by `body`, or any error that prevented the + /// creation of the buffer. + /// + /// The testing library uses this function when saving an image as an + /// attachment. The implementation should use `imageFormat` to determine what + /// encoder to use. + borrowing func withUnsafeBytes(as imageFormat: AttachableImageFormat, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R +} +``` + +If a developer has an image type that should conform to `AttachableAsImage` and +wraps an instance of `CGImage` or `IWICBitmapSource`, it is straightforward for +them to delegate to that object. For example: + +```swift +import Testing +import CoreGraphics + +struct MyImage { + var cgImage: CGImage + // ... +} + +extension MyImage: AttachableAsImage { + func withUnsafeBytes(as imageFormat: AttachableImageFormat, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R { + try cgImage.withUnsafeBytes(as: imageFormat, body) + } +} +``` + +### Adjusting AttachableImageFormat + +The following Apple-specific `AttachableImageFormat` initializer is renamed so +that its first argument has an explicit label: + +```diff + public struct AttachableImageFormat { + // ... +- public init(_ contentType: UTType, encodingQuality: Float = 1.0) ++ public init(contentType: UTType, encodingQuality: Float = 1.0) + } +``` + +This change makes the type's interface more consistent between Darwin and +Windows (where it has an `init(encoderCLSID:encodingQuality:)` initializer.) + +As well, conformances to `Equatable`, `Hashable`, `CustomStringConvertible`, and +`CustomDebugStringConvertible` are added: + +```swift +extension AttachableImageFormat: Equatable, Hashable {} +extension AttachableImageFormat: CustomStringConvertible, CustomDebugStringConvertible {} +``` + +Conformance to `Equatable` is necessary to correctly implement the +`withUnsafeBytes(as:_:)` protocol requirement mentioned above, and conformance +to `Hashable` is generally useful and straightforward to implement. Conformance +to `CustomStringConvertible` and `CustomDebugStringConvertible` allows for +better diagnostic output (especially if an encoding failure occurs.) + +### Adding an imageFormat property to Attachment + +The following property is added to `Attachment` when the attachable value is an +image: + +```swift +extension Attachment where AttachableValue: AttachableWrapper, + AttachableValue.Wrapped: AttachableAsImage { + /// The image format to use when encoding the represented image. + public var imageFormat: AttachableImageFormat? { get } +} +``` + +## Source compatibility + +These changes are breaking for anyone who has created a type that conforms to +either `AttachableAsCGImage` or `AttachableAsIWICBitmapSource`, or anyone who +has adopted `AttachableImageFormat.init(_:encodingQuality:)`. + +This feature is new in Swift 6.3 and has not shipped to developers outside of +nightly toolchain builds. As such, we feel confident that any real-world impact +to developers will be both minimal and manageable. + +## Integration with supporting tools + +No changes. + +## Future directions + +N/A + +## Alternatives considered + +- Leaving the two protocols separate. Combining them allows us to lower more + code into the main Swift Testing library and improves our ability to generate + DocC documentation, while also simplifying the story for developers who want + to use this feature across platforms. + +## Acknowledgments + +Thanks to my colleagues for their feedback on the image attachments feature and +to the Swift community for putting up with the churn! From d6962ce1a206f1949b0ba35bf76ea0de0450dde2 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 16 Oct 2025 14:05:47 -0700 Subject: [PATCH 4539/4563] [SE-0497] Try to clarify that @export(interface, implementation) does not full replace @inlinable --- proposals/0497-definition-visibility.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0497-definition-visibility.md b/proposals/0497-definition-visibility.md index 71dfeb6678..0964c2d088 100644 --- a/proposals/0497-definition-visibility.md +++ b/proposals/0497-definition-visibility.md @@ -186,12 +186,12 @@ The primary alternatives here involving naming of this functionality. There are ### Parameterize `@inlinable` -The `@inlinable` attribute already exists and is equivalent to the proposed `@export(interface, implementation)`. We could extend that attribute with two other forms: +The `@inlinable` attribute already exists and is the same as the proposed `@export(interface, implementation)` when Library Evolution is enabled. Outside of Library Evolution, `@inlinable` makes the definition available to clients but does not necessarily create a callable symbol. If we assume that the distinction is not important, we could extend `@inlinable` with two other forms: * `@inlinable(only)`, equivalent to `@export(implementation)`, means that the function definition is inlinable and can *only* be used by inlining. Practically speaking, this means that a client has must emit its own definition of the function in order to use it, because the defining module does not emit a copy as a public, callable symbol. This spelling formalizes `@_alwaysEmitIntoClient`. * `@inlinable(never)`, equivalent to `@export(interface)`, means that the function definition is never available to callers, even if the compiler options (aggressive CMO, Embedded Swift, etc.) would make it so by default. The defining module will emit a public, callable symbol that the client can use. At the time of this writing, the Swift `main` branch provides this behavior with the `@_neverEmitIntoClient` attribute. -The `@inlinable` attribute without a modifier would remain as specified in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md): it makes the definition available to the client (for any purpose) as well as emitting a public, callable symbol. It is essentially a midpoint between `only` and `never`, leaving it up to the optimizer to determine when and how to make use of the definition. +The `@inlinable` attribute without a modifier would remain as specified in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md): it makes the definition available to the client (for any purpose) as well as emitting a public, callable symbol in Library Evolution. It is essentially a midpoint between `only` and `never`, leaving it up to the optimizer to determine when and how to make use of the definition. ### Remove underscores from the existing attributes From 7490512af3720c990e1e1b8961b892b3d8eabbfb Mon Sep 17 00:00:00 2001 From: Rick van Voorden Date: Fri, 17 Oct 2025 14:24:30 -0700 Subject: [PATCH 4540/4563] add is identical methods updates --- proposals/0494-add-is-identical-methods.md | 289 +++++++-------------- 1 file changed, 99 insertions(+), 190 deletions(-) diff --git a/proposals/0494-add-is-identical-methods.md b/proposals/0494-add-is-identical-methods.md index 007c875549..c8ccb2600d 100644 --- a/proposals/0494-add-is-identical-methods.md +++ b/proposals/0494-add-is-identical-methods.md @@ -1,4 +1,4 @@ -# Add `isIdentical(to:)` Methods for Quick Comparisons to Concrete Types +# Add `isTriviallyIdentical(to:)` Methods for Quick Comparisons to Concrete Types * Proposal: [SE-0494](0494-add-is-identical-methods.md) * Authors: [Rick van Voorden](https://github.com/vanvoorden), [Karoy Lorentey](https://github.com/lorentey) @@ -17,8 +17,6 @@ * [`String`](#string) * [`Substring`](#substring) * [`Array`](#array) - * [`ArraySlice`](#arrayslice) - * [`ContiguousArray`](#contiguousarray) * [`Dictionary`](#dictionary) * [`Set`](#set) * [`UnsafeBufferPointer`](#unsafebufferpointer) @@ -37,7 +35,7 @@ ## Introduction -We propose new `isIdentical(to:)` instance methods to concrete types for quickly determining if two instances must be equal by-value. +We propose new `isTriviallyIdentical(to:)` instance methods to concrete types for quickly determining if two instances must be equal by-value. ## Motivation @@ -250,7 +248,7 @@ struct FavoriteContactList: View { When we build and run our app we see that we no longer compute our `favorites` values every time our user selects a new `Contact`. But similar to what we saw in our command line utility, we have traded performance in a different direction. The value equality operation we perform is *also* `O(n)`. As the amount of time we spend computing value equality grows, we can begin to spend more time computing value equality than we would have spent computing our `favorites`: we no longer see the performance benefits of memoization. -This proposal introduces an advanced performance hook for situations like this: a set of `isIdentical(to:)` methods that are designed to return *faster* than an operation to determine value equality. The `isIdentical(to:)` methods can return `true` in `O(1)` to indicate two values *must* be equal. +This proposal introduces an advanced performance hook for situations like this: a set of `isTriviallyIdentical(to:)` methods that are designed to return *faster* than an operation to determine value equality. The `isTriviallyIdentical(to:)` methods can return `true` in `O(1)` to indicate two values *must* be equal. ## Prior Art @@ -266,9 +264,9 @@ Even if we were able to use `withUnsafeBytes` for other data structures, a compa A solution for modern operating systems is the support we added from [SE-0456](0456-stdlib-span-properties.md) to bridge an `Array` to `Span`. We can then compare these instances using the `isIdentical(to:)` method on `Span`. One drawback here is that we are blocked on back-deploying support for bridging `Array` to `Span`: it is only available on the most modern operating systems. Another drawback is that if our `Array` does not have a contiguous storage, we have to copy one: an `O(n)` operation. We are also blocked on bringing support for `Span` to collection types like `Dictionary` that do not already implement contiguous storage. -A new `isIdentical(to:)` method could work around all these restrictions. We could return in constant time *without* needing to copy memory to a contiguous storage. We could adopt this method on many types that might not *ever* have a contiguous storage. We could also work with our library maintainers to discuss a back-deployment strategy that could bring this method to legacy operating systems. +A new `isTriviallyIdentical(to:)` method could work around all these restrictions. We could return in constant time *without* needing to copy memory to a contiguous storage. We could adopt this method on many types that might not *ever* have a contiguous storage. We could also work with our library maintainers to discuss a back-deployment strategy that could bring this method to legacy operating systems. -Our proposal would not be the first example of `isIdentical(to:)` shipping across Swift. `String` already ships a public-but-underscored version of this API.[^1] +`String` already ships a public-but-underscored version of this API.[^1] ```swift extension String { @@ -294,36 +292,36 @@ extension String { We don’t see this API currently being used in Standard Library, but it’s possible this API is already being used to optimize performance in private frameworks from Apple. -Many more examples of `isIdentical(to:)` functions are currently shipping in `Swift-Collections`[^2][^3][^4][^5][^6][^7][^8][^9][^10][^11][^12][^13], `Swift-Markdown`[^14], and `Swift-CowBox`[^15]. We also support `isIdentical(to:)` on the upcoming `Span` and `RawSpan` types from Standard Library.[^16] +Many more examples of `isIdentical(to:)` functions are currently shipping in `Swift-Collections`[^2][^3][^4][^5][^6][^7][^8][^9][^10][^11][^12][^13], `Swift-Markdown`[^14], and `Swift-CowBox`[^15]. We also support `isIdentical(to:)` on the `Span` and `RawSpan` types from Standard Library.[^16] ## Proposed Solution Before we look at the concrete types in this proposal, let’s begin with some more general principles and ideas we would expect for *all* concrete types to follow when adopting this new method. While this specific proposal is not adding a new protocol to Standard Library, it could be helpful to think of an “informal” protocol that guides us in choosing the types to adopt this new method. This could then serve as a guide for library maintainers that might choose to adopt this method on *new* types in the future. -Suppose we are proposing an `isIdentical(to:)` method on a type `T`. We propose the following axioms that library maintainers should adopt: -* `a.isIdentical(to: a)` is always `true` (Reflexivity) +Suppose we are proposing an `isTriviallyIdentical(to:)` method on a type `T`. We propose the following axioms that library maintainers should adopt: +* `a.isTriviallyIdentical(to: a)` is always `true` (Reflexivity) * If `T` is `Equatable`: - * `a.isIdentical(to: b)` implies `a == b` (*or else `a` and `b` are exceptional values*) - * `isIdentical(to:)` is *meaningfully* faster than `==` + * `a.isTriviallyIdentical(to: b)` implies `a == b` (*or else `a` and `b` are exceptional values*) + * `isTriviallyIdentical(to:)` is *meaningfully* faster than `==` Let’s look through these axioms a little closer: -**`a.isIdentical(to: a)` is always `true` (Reflexivity)** +**`a.isTriviallyIdentical(to: a)` is always `true` (Reflexivity)** -* An implementation of `isIdentical(to:)` that always returns `false` would not be an impactful API. We must guarantee that `isIdentical(to:)` *can* return `true` at least *some* of the time. +* An implementation of `isTriviallyIdentical(to:)` that always returns `false` would not be an impactful API. We must guarantee that `isTriviallyIdentical(to:)` *can* return `true` at least *some* of the time. -**If `T` is `Equatable` then `a.isIdentical(to: b)` implies `a == b`** +**If `T` is `Equatable` then `a.isTriviallyIdentical(to: b)` implies `a == b`** -* This is the “fast path” performance optimization that will speed up the memoization examples we saw earlier. One important side effect here is that when `a.isIdentical(to: b)` returns `false` we make *no* guarantees about whether or not `a` is equal to `b`. +* This is the “fast path” performance optimization that will speed up the memoization examples we saw earlier. One important side effect here is that when `a.isTriviallyIdentical(to: b)` returns `false` we make *no* guarantees about whether or not `a` is equal to `b`. * We assume this axiom holds only if `a` and `b` are not “exceptional” values. A example of an exceptional value would be if a container that is generic over `Float` contains `nan`. -**If `T` is `Equatable` then `isIdentical(to:)` is *meaningfully* faster than `==`** +**If `T` is `Equatable` then `isTriviallyIdentical(to:)` is *meaningfully* faster than `==`** -* While we could implement `isIdentical(to:)` on types like `Int` or `Bool`, these types are not included in this proposal. Our proposal focuses on types that have the ability to return from `isIdentical(to:)` meaningfully faster than `==`. If a type would perform the same amount of work in `isIdentical(to:)` that takes place in `==`, our advice is that library maintainers should *not* adopt `isIdentical(to:)` on this type. There should exist some legit internal fast-path on this type: like a pointer to a storage buffer that can be compared by reference identity. +* While we could implement `isTriviallyIdentical(to:)` on types like `Int` or `Bool`, these types are not included in this proposal. Our proposal focuses on types that have the ability to return from `isTriviallyIdentical(to:)` meaningfully faster than `==`. If a type would perform the same amount of work in `isTriviallyIdentical(to:)` that takes place in `==`, our advice is that library maintainers should *not* adopt `isTriviallyIdentical(to:)` on this type. There should exist some legit internal fast-path on this type: like a pointer to a storage buffer that can be compared by reference identity. -This proposal focuses on concrete types that are `Equatable`, but it might also be the case that a library maintainer would adopt `isIdentical(to:)` on a type that is *not* `Equatable`: like `Span`. Our expectation is that a library maintainer adopting `isIdentical(to:)`on a type that is not `Equatable` has some strong and impactful real-world use-cases ready to make use of this API. Just because a library maintainer *can* adopt this API does not imply they *should*. A library maintainer should also be ready to document for product engineers exactly what is implied from `a.isIdentical(to: b)` returning `true`. What does it *mean* for `a` to be “identical” to `b` if we do not have the implication that `a == b`? We leave this decision to the library maintainers that have the most context on the types they have built. +This proposal focuses on concrete types that are `Equatable`, but it might also be the case that a library maintainer would adopt `isTriviallyIdentical(to:)` on a type that is *not* `Equatable`: like `Span`. Our expectation is that a library maintainer adopting `isTriviallyIdentical(to:)`on a type that is not `Equatable` has some strong and impactful real-world use-cases ready to make use of this API. Just because a library maintainer *can* adopt this API does not imply they *should*. A library maintainer should also be ready to document for product engineers exactly what is implied from `a.isTriviallyIdentical(to: b)` returning `true`. What does it *mean* for `a` to be “identical” to `b` if we do not have the implication that `a == b`? We leave this decision to the library maintainers that have the most context on the types they have built. -Suppose we had an `isIdentical(to:)` method available on `Array`. Let’s go back to our earlier example and see how we can use this as an alternative to checking for value equality from our command line utility: +Suppose we had an `isTriviallyIdentical(to:)` method available on `Array`. Let’s go back to our earlier example and see how we can use this as an alternative to checking for value equality from our command line utility: ```swift final class Memoizer { @@ -331,7 +329,7 @@ final class Memoizer { func result(for input: [Int]) -> [Int] { if let result = self.result, - self.input.isIdentical(to: input) { + self.input.isTriviallyIdentical(to: input) { return result } else { ... @@ -368,9 +366,9 @@ print(memoizer.result(for: f)) // Prints "[2, 4, 6]" ``` -When we return `true` from `isIdentical(to:)` we skip computing a new `result`. When `isIdentical(to:)` returns `false` we compute a new `result`. Because `isIdentical(to:)` *can* return `false` when two values are equal, we might be computing the same `result` more than once. The performance tradeoff is that because the operation to compute a new `result` is `O(n)` time, we might not *want* to perform another `O(n)` value equality operation to determine if we should compute a new `result`. Our `isIdentical(to:)` will return in constant time no matter how many elements are in `input` or how expensive this value equality operation would be. +When we return `true` from `isTriviallyIdentical(to:)` we skip computing a new `result`. When `isTriviallyIdentical(to:)` returns `false` we compute a new `result`. Because `isTriviallyIdentical(to:)` *can* return `false` when two values are equal, we might be computing the same `result` more than once. The performance tradeoff is that because the operation to compute a new `result` is `O(n)` time, we might not *want* to perform another `O(n)` value equality operation to determine if we should compute a new `result`. Our `isTriviallyIdentical(to:)` will return in constant time no matter how many elements are in `input` or how expensive this value equality operation would be. -Let’s go back to our SwiftUI app for displaying `Contact` values. Here is what the change would look like to use `isIdentical(to:)` in place of value equality to memoize `favorites`: +Let’s go back to our SwiftUI app for displaying `Contact` values. Here is what the change would look like to use `isTriviallyIdentical(to:)` in place of value equality to memoize `favorites`: ```swift extension Favorites { @@ -378,7 +376,7 @@ extension Favorites { ... func update(_ contacts: [Contact]) { - if self.contacts.isIdentical(to: contacts) == false { + if self.contacts.isTriviallyIdentical(to: contacts) == false { self.contacts = contacts self.favorites = nil } @@ -393,7 +391,7 @@ When we build and run our SwiftUI app we confirm that we are not computing new ` ## Detailed Design -We propose adding `isIdentical` methods to the following concrete types from Standard Library: +We propose adding `isTriviallyIdentical(to:)` methods to the following concrete types from Standard Library: * `String` * `String.UnicodeScalarView` * `String.UTF16View` @@ -412,6 +410,8 @@ We propose adding `isIdentical` methods to the following concrete types from Sta * `UnsafeMutableRawBufferPointer` * `UnsafeRawBufferPointer` * `UTF8Span` +* `Span` +* `RawSpan` For each type being presented we codify important semantics in our header documentation. @@ -419,27 +419,23 @@ For each type being presented we codify important semantics in our header docume ```swift extension String { - /// Returns a boolean value indicating whether this string is identical to - /// `other`. + /// Returns a boolean value indicating whether this string is identical to `other`. /// - /// Two string values are identical if there is no way to distinguish between - /// them. + /// Two string values are identical if there is no way to distinguish between them. /// /// For any values `a`, `b`, and `c`: /// - /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) - /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) - /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, - /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) - /// - `a.isIdentical(b)` implies `a == b` - /// - `a == b` does not imply `a.isIdentical(b)` + /// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`. (Symmetry) + /// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)` are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`. (Transitivity) + /// - `a.isTriviallyIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isTriviallyIdentical(b)` /// - /// Values produced by copying the same value, with no intervening mutations, - /// will compare identical: + /// Values produced by copying the same value, with no intervening mutations, will compare identical: /// /// ```swift /// let d = c - /// print(c.isIdentical(to: d)) + /// print(c.isTriviallyIdentical(to: d)) /// // Prints true /// ``` /// @@ -450,11 +446,11 @@ extension String { /// identical. /// /// - Performance: O(1) - public func isIdentical(to other: Self) -> Bool { ... } + public func isTriviallyIdentical(to other: Self) -> Bool { ... } } ``` -The following types will adopt the same semantic guarantees as `String`: +The following types will adopt `isTriviallyIdentical(to:)` with the same semantic guarantees as `String`: * `String.UnicodeScalarView` * `String.UTF16View` * `String.UTF8View` @@ -463,27 +459,23 @@ The following types will adopt the same semantic guarantees as `String`: ```swift extension Substring { - /// Returns a boolean value indicating whether this substring is identical to - /// `other`. + /// Returns a boolean value indicating whether this substring is identical to `other`. /// - /// Two substring values are identical if there is no way to distinguish - /// between them. + /// Two substring values are identical if there is no way to distinguish between them. /// /// For any values `a`, `b`, and `c`: /// - /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) - /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) - /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, - /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) - /// - `a.isIdentical(b)` implies `a == b` - /// - `a == b` does not imply `a.isIdentical(b)` + /// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`. (Symmetry) + /// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)` are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`. (Transitivity) + /// - `a.isTriviallyIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isTriviallyIdentical(b)` /// - /// Values produced by copying the same value, with no intervening mutations, - /// will compare identical: + /// Values produced by copying the same value, with no intervening mutations, will compare identical: /// /// ```swift /// let d = c - /// print(c.isIdentical(to: d)) + /// print(c.isTriviallyIdentical(to: d)) /// // Prints true /// ``` /// @@ -494,11 +486,11 @@ extension Substring { /// identical. /// /// - Performance: O(1) - public func isIdentical(to other: Self) -> Bool { ... } + public func isTriviallyIdentical(to other: Self) -> Bool { ... } } ``` -The following types will adopt the same semantic guarantees as `Substring`: +The following types will adopt `isTriviallyIdentical(to:)` with the same semantic guarantees as `Substring`: * `Substring.UnicodeScalarView` * `Substring.UTF16View` * `Substring.UTF8View` @@ -507,66 +499,23 @@ The following types will adopt the same semantic guarantees as `Substring`: ```swift extension Array { - /// Returns a boolean value indicating whether this array is identical to - /// `other`. + /// Returns a boolean value indicating whether this array is identical to `other`. /// - /// Two array values are identical if there is no way to distinguish between - /// them. + /// Two array values are identical if there is no way to distinguish between them. /// /// For any values `a`, `b`, and `c`: /// - /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) - /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) - /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, - /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) - /// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` - /// - `a == b` does not imply `a.isIdentical(b)` - /// - /// Values produced by copying the same value, with no intervening mutations, - /// will compare identical: - /// - /// ```swift - /// let d = c - /// print(c.isIdentical(to: d)) - /// // Prints true - /// ``` - /// - /// Comparing arrays this way includes comparing (normally) hidden - /// implementation details such as the memory location of any underlying - /// array storage object. Therefore, identical arrays are guaranteed to - /// compare equal with `==`, but not all equal arrays are considered - /// identical. - /// - /// - Performance: O(1) - public func isIdentical(to other: Self) -> Bool { ... } -} -``` - -### `ArraySlice` - -```swift -extension ArraySlice { - /// Returns a boolean value indicating whether this array is identical to - /// `other`. - /// - /// Two array values are identical if there is no way to distinguish between - /// them. - /// - /// For any values `a`, `b`, and `c`: - /// - /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) - /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) - /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, - /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) - /// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` - /// - `a == b` does not imply `a.isIdentical(b)` + /// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`. (Symmetry) + /// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)` are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`. (Transitivity) + /// - If `a` and `b` are `Equatable`, then `a.isTriviallyIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isTriviallyIdentical(b)` /// - /// Values produced by copying the same value, with no intervening mutations, - /// will compare identical: + /// Values produced by copying the same value, with no intervening mutations, will compare identical: /// /// ```swift /// let d = c - /// print(c.isIdentical(to: d)) + /// print(c.isTriviallyIdentical(to: d)) /// // Prints true /// ``` /// @@ -577,74 +526,36 @@ extension ArraySlice { /// identical. /// /// - Performance: O(1) - public func isIdentical(to other: Self) -> Bool { ... } + public func isTriviallyIdentical(to other: Self) -> Bool { ... } } ``` -### `ContiguousArray` +The following types will adopt `isTriviallyIdentical(to:)` with the same semantic guarantees as `Array`: -```swift -extension ContiguousArray { - /// Returns a boolean value indicating whether this array is identical to - /// `other`. - /// - /// Two array values are identical if there is no way to distinguish between - /// them. - /// - /// For any values `a`, `b`, and `c`: - /// - /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) - /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) - /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, - /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) - /// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` - /// - `a == b` does not imply `a.isIdentical(b)` - /// - /// Values produced by copying the same value, with no intervening mutations, - /// will compare identical: - /// - /// ```swift - /// let d = c - /// print(c.isIdentical(to: d)) - /// // Prints true - /// ``` - /// - /// Comparing arrays this way includes comparing (normally) hidden - /// implementation details such as the memory location of any underlying - /// array storage object. Therefore, identical arrays are guaranteed to - /// compare equal with `==`, but not all equal arrays are considered - /// identical. - /// - /// - Performance: O(1) - public func isIdentical(to other: Self) -> Bool { ... } -} -``` +* `ArraySlice` +* `ContiguousArray` ### `Dictionary` ```swift extension Dictionary { - /// Returns a boolean value indicating whether this dictionary is identical to - /// `other`. + /// Returns a boolean value indicating whether this dictionary is identical to `other`. /// - /// Two dictionary values are identical if there is no way to distinguish - /// between them. + /// Two dictionary values are identical if there is no way to distinguish between them. /// /// For any values `a`, `b`, and `c`: /// - /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) - /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) - /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, - /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) - /// - If `a` and `b` are `Equatable`, then `a.isIdentical(b)` implies `a == b` - /// - `a == b` does not imply `a.isIdentical(b)` + /// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`. (Symmetry) + /// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)` are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`. (Transitivity) + /// - If `a` and `b` are `Equatable`, then `a.isTriviallyIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isTriviallyIdentical(b)` /// - /// Values produced by copying the same value, with no intervening mutations, - /// will compare identical: + /// Values produced by copying the same value, with no intervening mutations, will compare identical: /// /// ```swift /// let d = c - /// print(c.isIdentical(to: d)) + /// print(c.isTriviallyIdentical(to: d)) /// // Prints true /// ``` /// @@ -655,7 +566,7 @@ extension Dictionary { /// considered identical. /// /// - Performance: O(1) - public func isIdentical(to other: Self) -> Bool { ... } + public func isTriviallyIdentical(to other: Self) -> Bool { ... } } ``` @@ -663,27 +574,23 @@ extension Dictionary { ```swift extension Set { - /// Returns a boolean value indicating whether this set is identical to - /// `other`. + /// Returns a boolean value indicating whether this set is identical to `other`. /// - /// Two set values are identical if there is no way to distinguish between - /// them. + /// Two set values are identical if there is no way to distinguish between them. /// /// For any values `a`, `b`, and `c`: /// - /// - `a.isIdentical(to: a)` is always `true`. (Reflexivity) - /// - `a.isIdentical(to: b)` implies `b.isIdentical(to: a)`. (Symmetry) - /// - If `a.isIdentical(to: b)` and `b.isIdentical(to: c)` are both `true`, - /// then `a.isIdentical(to: c)` is also `true`. (Transitivity) - /// - `a.isIdentical(b)` implies `a == b` - /// - `a == b` does not imply `a.isIdentical(b)` + /// - `a.isTriviallyIdentical(to: a)` is always `true`. (Reflexivity) + /// - `a.isTriviallyIdentical(to: b)` implies `b.isTriviallyIdentical(to: a)`. (Symmetry) + /// - If `a.isTriviallyIdentical(to: b)` and `b.isTriviallyIdentical(to: c)` are both `true`, then `a.isTriviallyIdentical(to: c)` is also `true`. (Transitivity) + /// - `a.isTriviallyIdentical(b)` implies `a == b` + /// - `a == b` does not imply `a.isTriviallyIdentical(b)` /// - /// Values produced by copying the same value, with no intervening mutations, - /// will compare identical: + /// Values produced by copying the same value, with no intervening mutations, will compare identical: /// /// ```swift /// let d = c - /// print(c.isIdentical(to: d)) + /// print(c.isTriviallyIdentical(to: d)) /// // Prints true /// ``` /// @@ -693,7 +600,7 @@ extension Set { /// with `==`, but not all equal sets are considered identical. /// /// - Performance: O(1) - public func isIdentical(to other: Self) -> Bool { ... } + public func isTriviallyIdentical(to other: Self) -> Bool { ... } } ``` @@ -701,13 +608,12 @@ extension Set { ```swift extension UnsafeBufferPointer where Element: ~Copyable { - /// Returns a Boolean value indicating whether two `UnsafeBufferPointer` - /// instances refer to the same region in memory. - public func isIdentical(to other: Self) -> Bool { ... } + /// Returns a Boolean value indicating whether two `UnsafeBufferPointer` instances refer to the same region in memory. + public func isTriviallyIdentical(to other: Self) -> Bool { ... } } ``` -The following types will adopt the same semantic guarantees as `UnsafeBufferPointer`: +The following types will adopt `isTriviallyIdentical(to:)` with the same semantic guarantees as `UnsafeBufferPointer`: * `UnsafeMutableBufferPointer` * `UnsafeMutableRawBufferPointer` * `UnsafeRawBufferPointer` @@ -716,11 +622,15 @@ The following types will adopt the same semantic guarantees as `UnsafeBufferPoin ```swift extension UTF8Span where Element: ~Copyable { - /// Returns a Boolean value indicating whether two `UTF8Span` instances - /// refer to the same region in memory. - public func isIdentical(to other: Self) -> Bool { ... } + /// Returns a Boolean value indicating whether two `UTF8Span` instances refer to the same region in memory. + public func isTriviallyIdentical(to other: Self) -> Bool { ... } ``` +The following types will adopt `isTriviallyIdentical(to:)` with the same semantic guarantees as `UTF8Span`: + +* `Span` +* `RawSpan` + ## Source Compatibility This proposal is additive and source-compatible with existing code. @@ -731,7 +641,7 @@ This proposal is additive and ABI-compatible with existing code. ## Future Directions -Any Standard Library types that are copy-on-write values could be good candidates to add `isIdentical` functions. Here are some potential types to consider for a future proposal: +Any Standard Library types that are copy-on-write values could be good candidates to add `isTriviallyIdentical(to:)` functions. Here are some potential types to consider for a future proposal: * `Character` * `Dictionary.Keys` @@ -741,7 +651,7 @@ Any Standard Library types that are copy-on-write values could be good candidate * `StaticString` * `UTF8Span` -This proposal focuses on what we see as the most high-impact types to support from Standard Library. This proposal *is not* meant to discourage adding `isIdentical(to:)` on any of these types at some point in the future. A follow-up “second-round” proposal could focus on these remaining types. +This proposal focuses on what we see as the most high-impact types to support from Standard Library. This proposal *is not* meant to discourage adding `isTriviallyIdentical(to:)` on any of these types at some point in the future. A follow-up “second-round” proposal could focus on these remaining types. ## Alternatives Considered @@ -755,20 +665,19 @@ There’s a lot of interesting directions to go with that idea… but we don’t Multiple different names have been suggested for these operations. Including: +* `isIdentical(to:)` * `hasSameRepresentation(as:)` * `isKnownIdentical(to:)` -We prefer `isIdentical(to:)`. This also has the benefit of being consistent with the years of prior art we have accumulated across the Swift ecosystem. - ### Generic Contexts -We proposed an “informal” protocol for library maintainers adopting `isIdentical(to:)` on new types. Could we just build a new protocol in Standard Library? Maybe. We don’t see a big need for this right now. If product engineers would want for these types to conform to some common protocol to use across generic contexts, those product engineers can define that protocol in their own packages. If these protocols “incubate” in the community and become a common practice, we can consider proposing a new protocol in Standard Library. +We proposed an “informal” protocol for library maintainers adopting `isTriviallyIdentical(to:)` on new types. Could we just build a new protocol in Standard Library? Maybe. We don’t see a big need for this right now. If product engineers would want for these types to conform to some common protocol to use across generic contexts, those product engineers can define that protocol in their own packages. If these protocols “incubate” in the community and become a common practice, we can consider proposing a new protocol in Standard Library. -Instead of a new protocol, could we somehow add `isIdentical(to:)` on `Equatable`? Maybe. This would introduce some more tricky questions. If we adopt this on *all* `Equatable` types, what do we do about types like `Int` or `Bool` that do not have an ability to perform a fast check for identity? Similar to our last idea, we prefer to focus just on concrete types for now. If product engineers want to make `isIdentical(to:)` available on generic contexts across `Equatable`, we encourage them to experiment with their own extension for that. If this pattern becomes popular in the community, we can consider a new proposal to add this on `Equatable` in Standard Library. +Instead of a new protocol, could we somehow add `isTriviallyIdentical(to:)` on `Equatable`? Maybe. This would introduce some more tricky questions. If we adopt this on *all* `Equatable` types, what do we do about types like `Int` or `Bool` that do not have an ability to perform a fast check for identity? Similar to our last idea, we prefer to focus just on concrete types for now. If product engineers want to make `isTriviallyIdentical(to:)` available on generic contexts across `Equatable`, we encourage them to experiment with their own extension for that. If this pattern becomes popular in the community, we can consider a new proposal to add this on `Equatable` in Standard Library. ### Overload for Reference Comparison -Could we “overload” the `===` operator from `AnyObject`? This proposal considers that question to be orthogonal to our goal of exposing identity equality with the `isIdentical` methods. We could choose to overload `===`, but this would be a larger “conceptual” and “philosophical” change because the `===` operator is currently meant for `AnyObject` types — not value types like `Array`. +Could we “overload” the `===` operator from `AnyObject`? This proposal considers that question to be orthogonal to our goal of exposing identity equality with the `isTriviallyIdentical(to:)` methods. We could choose to overload `===`, but this would be a larger “conceptual” and “philosophical” change because the `===` operator is currently meant for `AnyObject` types — not value types like `Array`. ### Support for Optionals @@ -776,11 +685,11 @@ We can support `Optional` values with the following extension: ```swift extension Optional { - public func isIdentical(to other: Self) -> Bool + public func isTriviallyIdentical(to other: Self) -> Bool where Wrapped == Array { switch (self, other) { case let (value?, other?): - return value.isIdentical(to: other) + return value.isTriviallyIdentical(to: other) case (nil, nil): return true default: @@ -794,7 +703,7 @@ Because this extension needs no `private` or `internal` symbols from Standard Li ### Alternative Semantics -Instead of publishing an `isIdentical` method which implies two types *must* be equal, could we think of things from the opposite direction? Could we publish a `maybeDifferent` method which implies two types *might not* be equal? This then introduces some potential ambiguity for product engineers: to what extent does “maybe different” imply “probably different”? This ambiguity could be settled with extra documentation on the method, but `isIdentical` solves that ambiguity up-front. The `isIdentical` method is also consistent with the prior art in this space. +Instead of publishing an `isTriviallyIdentical(to:)` method which implies two types *must* be equal, could we think of things from the opposite direction? Could we publish a `maybeDifferent` method which implies two types *might not* be equal? This then introduces some potential ambiguity for product engineers: to what extent does “maybe different” imply “probably different”? This ambiguity could be settled with extra documentation on the method, but `isTriviallyIdentical(to:)` solves that ambiguity up-front. The `isTriviallyIdentical(to:)` method is also consistent with the prior art in this space. In the same way this proposal exposes a way to quickly check if two values *must* be equal, product engineers might want a way to quickly check if two values *must not* be equal. This is an interesting idea, but this can exist as an independent proposal. We don’t need to block the review of this proposal on a review of `isNotIdentical` semantics. From fef7e1a0d53ed5129977471c729de682b8d18c9a Mon Sep 17 00:00:00 2001 From: Rick van Voorden Date: Fri, 17 Oct 2025 14:33:36 -0700 Subject: [PATCH 4541/4563] add is identical methods updates --- proposals/0494-add-is-identical-methods.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proposals/0494-add-is-identical-methods.md b/proposals/0494-add-is-identical-methods.md index c8ccb2600d..b5b8f8932d 100644 --- a/proposals/0494-add-is-identical-methods.md +++ b/proposals/0494-add-is-identical-methods.md @@ -171,6 +171,8 @@ struct FavoriteContactList: View { } ``` +We can assume there is another view component in our application that could be editing these `Contact` values. It's not very important for us right now to show *how* these `Contact` values could change — let's just assume that our `FavoriteContactList` component might need to recompute its `body` over time with new `Contact` values. + When we compute our `body` property we also compute our `favorites` property. The implication is that *every* time our `body` property is computed we perform *another* `O(n)` algorithm across our `contacts`. Because our `FavoriteContactList` supports selection, every time our user selects a `Contact` value we update our `State`. Updating our `State` computes our `body` which computes our `favorites` property. So even though our `contacts` values *have not changed*, we *still* pay the performance penalty of *another* `O(n)` operation just to support cell selection. This might look like a good opportunity for another attempt at memoization. Here is an approach using a dynamic property wrapper: @@ -715,6 +717,8 @@ Thanks to [David Nadoba](https://forums.swift.org/t/-/80496/61/) for proposing t Thanks to [Xiaodi Wu](https://forums.swift.org/t/-/80496/67) for proposing that our equivalence relation semantics would carve-out for “exceptional” values like `Float.nan`. +Thanks to [QuinceyMorris](https://forums.swift.org/t/-/82296/72) for proposing the name `isTriviallyIdentical(to:)`. + [^1]: https://github.com/swiftlang/swift/blob/swift-6.1.2-RELEASE/stdlib/public/core/String.swift#L397-L415 [^2]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/DequeModule/Deque._Storage.swift#L223-L225 [^3]: https://github.com/apple/swift-collections/blob/1.2.0/Sources/HashTreeCollections/HashNode/_HashNode.swift#L78-L80 From 6bbd2d45a4e84c553f589587459cee5ba5f3face Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 20 Oct 2025 09:21:14 -0400 Subject: [PATCH 4542/4563] Fix typo --- proposals/testing/NNNN-test-cancellation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/NNNN-test-cancellation.md b/proposals/testing/NNNN-test-cancellation.md index 327e2182e5..7ea317eb1a 100644 --- a/proposals/testing/NNNN-test-cancellation.md +++ b/proposals/testing/NNNN-test-cancellation.md @@ -16,7 +16,7 @@ using the [`.enabled(if:)`](https://developer.apple.com/documentation/testing/tr etc. family of traits: ```swift -@Test(.disabled(if: Species.all.in(: .dinosauria).isEmpty) +@Test(.disabled(if: Species.all(in: .dinosauria).isEmpty) func `Are all dinosaurs extinct?`() { // ... } From 94de61b3bbf1636d517a5926b39aa43787279542 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 20 Oct 2025 16:49:24 -0400 Subject: [PATCH 4543/4563] Adjust title --- proposals/testing/NNNN-image-attachment-adjustments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/NNNN-image-attachment-adjustments.md b/proposals/testing/NNNN-image-attachment-adjustments.md index 600b0d155b..21f621fa17 100644 --- a/proposals/testing/NNNN-image-attachment-adjustments.md +++ b/proposals/testing/NNNN-image-attachment-adjustments.md @@ -1,4 +1,4 @@ -# Adjustments to image attachments in Swift Testing +# Consolidate Swift Testing's image attachments API across platforms * Proposal: [ST-NNNN](NNNN-image-attachment-adjustments.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) From 6fa2f5c80d0a472c794d5d1e72d8952a37c1c48f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 21 Oct 2025 15:09:12 -0700 Subject: [PATCH 4544/4563] [SE-0497] Remove `@exported(interface, implementation)` for now based on review feedback --- proposals/0497-definition-visibility.md | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/proposals/0497-definition-visibility.md b/proposals/0497-definition-visibility.md index 0964c2d088..8cf389c2f4 100644 --- a/proposals/0497-definition-visibility.md +++ b/proposals/0497-definition-visibility.md @@ -91,20 +91,20 @@ This proposal introduces a new attribute `@export` that provides the required co The existing `@_alwaysEmitIntoClient` is subsumed by `@export(implementation)`, meaning that the definition is available and each client that uses it must emit their own copy of the definition, because there is no symbol. The `@_neverEmitIntoClient` attribute on `main` is subsumed by `@export(interface)`, meaning that a callable symbol is emitted but the definition is not available to callers for any reason. -The two attributes introduced by [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md) are mostly subsumed by `@export`: +The two attributes introduced by [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md) are partially subsumed by `@export`: -* `@inlinable` for public symbols provides both a definition in the owning module and makes the definition available for callers, akin to `@export(interface, implementation)`. Note that `@inlinable` doesn't *guarantee* the presence of a symbol in the way that `@export(interface)` does: in practice, Embedded Swift may not produce a symbol, but non-Embedded Swift will. +* `@inlinable` for making a definition available to callers, similar to `@export(implementation)`. `@inlinable` is guaranteed to produce a symbol under Library Evolution, but there are no guarantees otherwise. In practice, non-Embedded Swift will produce a symbol, but Embedded Swift generally does not. * `@usableFromInline` for making a less-than-public symbol available for use in an inlinable function (per SE-0193) is akin to `@export(interface)`. As with `@inlinable`, `@usableFromInline` does not *guarantee* the presence of a symbol in the way that `@export(interface)` does: in practice, Embedded Swift may not produce a symbol, but non-Embedded Swift will. `@export` cannot be combined with any of `@inlinable`, `@usableFromInline`, `@_alwaysEmitIntoClient`, or `@_neverEmitIntoClient`. ## Detailed design -`@export` that includes the `implementation` argument inherits all of the restrictions as `@inlinable` that are outlined in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md), for example, the definition itself can only reference public entities or those that are themselves `@usableFromInline` or `@export(interface)`. +`@export(implementation)` inherits all of the restrictions as `@inlinable` that are outlined in [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md), for example, the definition itself can only reference public entities or those that are themselves `@usableFromInline` or `@export(interface)`. -`@export` that includes `interface` always produces a symbol in the object file. +`@export(interface)` always produces a symbol in the object file. -`@export` cannot be used without arguments. +`@export` cannot be used without arguments. There can only be one of `@export(interface)` or `@export(implementation)` on a given declaration. ### Relationship to access control @@ -136,11 +136,10 @@ The `@inline(always)` attribute [under discussion now](https://forums.swift.org/ The following table captures the ways in which these attributes interact. -| | `@inline(always)` | `@inline(never)` | (no `@inline`) | -| ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | -| `@export(implementation)` | Always inlined everywhere; callers emit their own definitions. Use this when a function should not be part of the ABI and should always be inlined for performance reasons. | Never inlined; callers emit their own definitions. Use this when a function should not be part of the ABI but never needs to be inlined. | May be inlined. Callers emit their own definitions. Use when the function should not be part of the ABI, but leave it up to the optimizer to decide when to inline. | -| `@export(interface, implementation)` | Always inlined everywhere; a symbol exists that could only be used by non-Swift clients. | Never inlined; callers may emit their own definitions or may call the definition in the function's module. | May be inlined. Callers may emit their own definitions for specialization/inlining/etc. or may call the definition in the function's module, depending on the optimizer. | -| `@export(interface)` | Always inlined within the function's module; a symbol exists for callers outside the function's module. | Never inlined; callers may call the definition in the function's module. Use this to fully encapsulate a function definition so that it can be replaced at link time without affecting any other code. | May be inlined within the function's module, if the optimizer determines that it should be profitable. A symbol exists for callers from outside the module to use. | +| | `@inline(always)` | `@inline(never)` | (no `@inline`) | +| ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| `@export(implementation)` | Always inlined everywhere; callers emit their own definitions. Use this when a function should not be part of the ABI and should always be inlined for performance reasons. | Never inlined; callers emit their own definitions. Use this when a function should not be part of the ABI but never needs to be inlined. | May be inlined. Callers emit their own definitions. Use when the function should not be part of the ABI, but leave it up to the optimizer to decide when to inline. | +| `@export(interface)` | Always inlined within the function's module; a symbol exists for callers outside the function's module. | Never inlined; callers may call the definition in the function's module. Use this to fully encapsulate a function definition so that it can be replaced at link time without affecting any other code. | May be inlined within the function's module, if the optimizer determines that it should be profitable. A symbol exists for callers from outside the module to use. | ### Embedded Swift limitations @@ -186,7 +185,7 @@ The primary alternatives here involving naming of this functionality. There are ### Parameterize `@inlinable` -The `@inlinable` attribute already exists and is the same as the proposed `@export(interface, implementation)` when Library Evolution is enabled. Outside of Library Evolution, `@inlinable` makes the definition available to clients but does not necessarily create a callable symbol. If we assume that the distinction is not important, we could extend `@inlinable` with two other forms: +The `@inlinable` attribute already exists and is the combination of the proposed `@export(interface)` and `@export(implementation)` when Library Evolution is enabled. Outside of Library Evolution, `@inlinable` makes the definition available to clients but does not necessarily create a callable symbol. If we assume that the distinction is not important, we could extend `@inlinable` with two other forms: * `@inlinable(only)`, equivalent to `@export(implementation)`, means that the function definition is inlinable and can *only* be used by inlining. Practically speaking, this means that a client has must emit its own definition of the function in order to use it, because the defining module does not emit a copy as a public, callable symbol. This spelling formalizes `@_alwaysEmitIntoClient`. * `@inlinable(never)`, equivalent to `@export(interface)`, means that the function definition is never available to callers, even if the compiler options (aggressive CMO, Embedded Swift, etc.) would make it so by default. The defining module will emit a public, callable symbol that the client can use. At the time of this writing, the Swift `main` branch provides this behavior with the `@_neverEmitIntoClient` attribute. @@ -218,6 +217,10 @@ public func f() { } One of the motivations for this proposal is implementation hiding for uses of `private` and `internal` imports. This motivation would be weakened by the [future direction in SE-0409](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0409-access-level-on-imports.md#hiding-dependencies-for-non-resilient-modules) where the transitive dependencies from those imports are hidden, because one would no longer need to use `@export(interface)` to explicitly hide a transitive dependency. In such a case, `@export(interface)` would make explicit what would happen implicitly when the definition of such a function references something available via an internal import. +### Allow both `@export(interface)` and `@export(implementation)` + +The initial revision of this proposal allowed `@export(interface, implementation)` to mean that both a symbol is exported and the definition is available to callers. This feature was lacking any use case: it matches `@inlinable` for Library Evolution, but does not replace it elsewhere. The presence of this feature complicates the story, so it has been removed. If use cases are found for this case later, it's easy to lift the restriction. + ## Acknowledgments Thank you to Andy Trick for the `@export` syntax suggestion. From 623c22389e6c30f2b71734f548213cd106e90c6f Mon Sep 17 00:00:00 2001 From: Rachel Brindle Date: Wed, 22 Oct 2025 18:13:52 -0700 Subject: [PATCH 4545/4563] Begin review of image attachment consolidation - Assign the proposal ST-0017 - Rename the proposal document as appropriate - Update the relevant metadata about the current state of the proposal document --- ...ustments.md => 0017-image-attachment-consolidation.md} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename proposals/testing/{NNNN-image-attachment-adjustments.md => 0017-image-attachment-consolidation.md} (95%) diff --git a/proposals/testing/NNNN-image-attachment-adjustments.md b/proposals/testing/0017-image-attachment-consolidation.md similarity index 95% rename from proposals/testing/NNNN-image-attachment-adjustments.md rename to proposals/testing/0017-image-attachment-consolidation.md index 21f621fa17..c5bff460b5 100644 --- a/proposals/testing/NNNN-image-attachment-adjustments.md +++ b/proposals/testing/0017-image-attachment-consolidation.md @@ -1,11 +1,11 @@ # Consolidate Swift Testing's image attachments API across platforms -* Proposal: [ST-NNNN](NNNN-image-attachment-adjustments.md) +* Proposal: [ST-0017](0017-image-attachment-consolidation.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Rachel Brindle](https://github.com/younata) +* Status: **Active Review (October 22-30, 2025)** * Implementation: [swiftlang/swift-testing#1359](https://github.com/swiftlang/swift-testing/pull/1359) -* Review: ([pitch](https://forums.swift.org/...)) +* Review: ([pitch](https://forums.swift.org/t/pitch-adjustments-to-image-attachments-in-swift-testing/82581), Review TBA) ## Introduction From 4db06aaf5cefac07bcde6c4ace1b1adc12f926c5 Mon Sep 17 00:00:00 2001 From: Rachel Brindle Date: Wed, 22 Oct 2025 19:03:59 -0700 Subject: [PATCH 4546/4563] ST-0017: Link the review thread --- proposals/testing/0017-image-attachment-consolidation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0017-image-attachment-consolidation.md b/proposals/testing/0017-image-attachment-consolidation.md index c5bff460b5..3866869514 100644 --- a/proposals/testing/0017-image-attachment-consolidation.md +++ b/proposals/testing/0017-image-attachment-consolidation.md @@ -5,7 +5,7 @@ * Review Manager: [Rachel Brindle](https://github.com/younata) * Status: **Active Review (October 22-30, 2025)** * Implementation: [swiftlang/swift-testing#1359](https://github.com/swiftlang/swift-testing/pull/1359) -* Review: ([pitch](https://forums.swift.org/t/pitch-adjustments-to-image-attachments-in-swift-testing/82581), Review TBA) +* Review: ([pitch](https://forums.swift.org/t/pitch-adjustments-to-image-attachments-in-swift-testing/82581), [review](https://forums.swift.org/t/st-0017-consolidate-swift-testing-s-image-attachments-api-across-platforms/82815)) ## Introduction From 78a3ce14becff180b9f414ae81b44a39f74b4b50 Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Thu, 23 Oct 2025 09:31:58 +0200 Subject: [PATCH 4547/4563] Get proposal ready for review --- proposals/testing/NNNN-test-cancellation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/testing/NNNN-test-cancellation.md b/proposals/testing/NNNN-test-cancellation.md index 7ea317eb1a..e9e8397d40 100644 --- a/proposals/testing/NNNN-test-cancellation.md +++ b/proposals/testing/NNNN-test-cancellation.md @@ -1,9 +1,9 @@ # Test cancellation -* Proposal: [ST-NNNN](NNNN-test-cancellation.md) +* Proposal: [ST-0016](0016-test-cancellation.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Maarten Engels](https://github.com/maartene) +* Status: **Active review (October 23...November 5, 2025)** * Bug: [swiftlang/swift-testing#120](https://github.com/swiftlang/swift-testing/issues/120) * Implementation: [swiftlang/swift-testing#1284](https://github.com/swiftlang/swift-testing/pull/1284) * Review: ([pitch](https://forums.swift.org/t/pitch-test-cancellation/81847)) From d6e913c1709fe07908513e7a99b3dbe4eac1ac28 Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Thu, 23 Oct 2025 09:32:46 +0200 Subject: [PATCH 4548/4563] Rename test cancellation proposal file --- .../{NNNN-test-cancellation.md => 0016-test-cancellation.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/testing/{NNNN-test-cancellation.md => 0016-test-cancellation.md} (100%) diff --git a/proposals/testing/NNNN-test-cancellation.md b/proposals/testing/0016-test-cancellation.md similarity index 100% rename from proposals/testing/NNNN-test-cancellation.md rename to proposals/testing/0016-test-cancellation.md From 24153dc0b52314014e49bbf97565af258be40535 Mon Sep 17 00:00:00 2001 From: Maarten Engels Date: Thu, 23 Oct 2025 09:44:04 +0200 Subject: [PATCH 4549/4563] Add link to review thread --- proposals/testing/0016-test-cancellation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/testing/0016-test-cancellation.md b/proposals/testing/0016-test-cancellation.md index e9e8397d40..51b10938be 100644 --- a/proposals/testing/0016-test-cancellation.md +++ b/proposals/testing/0016-test-cancellation.md @@ -6,7 +6,7 @@ * Status: **Active review (October 23...November 5, 2025)** * Bug: [swiftlang/swift-testing#120](https://github.com/swiftlang/swift-testing/issues/120) * Implementation: [swiftlang/swift-testing#1284](https://github.com/swiftlang/swift-testing/pull/1284) -* Review: ([pitch](https://forums.swift.org/t/pitch-test-cancellation/81847)) +* Review: ([pitch](https://forums.swift.org/t/pitch-test-cancellation/81847)) ([review](https://forums.swift.org/t/st-0016-test-cancellation/82817)) ## Introduction From 138d04bb86060f9c1cbb18428ffd1f1616b830c9 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Thu, 23 Oct 2025 09:12:49 -0400 Subject: [PATCH 4550/4563] Accept SE-0496. --- proposals/0496-inline-always.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0496-inline-always.md b/proposals/0496-inline-always.md index 10b74a18ba..8a8784e97f 100644 --- a/proposals/0496-inline-always.md +++ b/proposals/0496-inline-always.md @@ -3,9 +3,9 @@ * Proposal: [SE-0496](0496-inline-always.md) * Authors: [Arnold Schwaighofer](https://github.com/aschwaighofer) * Review Manager: [Tony Allevato](https://github.com/allevato) -* Status: **Active review (October 2–16, 2025)** +* Status: **Accepted** * Implementation: [swiftlang/swift#84178](https://github.com/swiftlang/swift/pull/84178) -* Review: ([pitch](https://forums.swift.org/t/pitch-inline-always-attribute/82040)) ([review](https://forums.swift.org/t/se-0496-inline-always-attribute/82480)) +* Review: ([pitch](https://forums.swift.org/t/pitch-inline-always-attribute/82040)) ([review](https://forums.swift.org/t/se-0496-inline-always-attribute/82480)) ([acceptance](https://forums.swift.org/t/accepted-se-0496-inline-always-attribute/82825)) ## Introduction From d6cd425bf593d8aabbb2f4e581f548168198c3bf Mon Sep 17 00:00:00 2001 From: Bar <815372+barbaramartina@users.noreply.github.com> Date: Wed, 29 Oct 2025 14:58:52 +0100 Subject: [PATCH 4551/4563] Update async function isolation documentation Small typo / unnecessary "the these" words combination. --- proposals/0461-async-function-isolation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md index 47633dda33..1d7d2de812 100644 --- a/proposals/0461-async-function-isolation.md +++ b/proposals/0461-async-function-isolation.md @@ -337,7 +337,7 @@ struct S: Sendable { ``` Only (implicitly or explicitly) `nonisolated` functions can be marked with -`@concurrent`; it is an error to use the these attributes with +`@concurrent`; it is an error to use these attributes with an isolation other than `nonisolated`, including global actors, isolated parameters, and `@isolated(any)`: From 68dd95590d977cb3986927ec7bafbf2c4c262bb1 Mon Sep 17 00:00:00 2001 From: Asaad Jaber <13552884+asaadjaber@users.noreply.github.com> Date: Thu, 30 Oct 2025 18:34:28 +0200 Subject: [PATCH 4552/4563] Update 0317-async-let.md fixed some typos and grammatical errors --- proposals/0317-async-let.md | 44 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/proposals/0317-async-let.md b/proposals/0317-async-let.md index 245dd4e7e0..113e762a6a 100644 --- a/proposals/0317-async-let.md +++ b/proposals/0317-async-let.md @@ -122,7 +122,7 @@ The child task begins running as soon as the `async let` is encountered. By defa The right-hand side of a `async let` expression can be thought of as an implicit `@Sendable closure`, similar to how the `Task.detached { ... }` API works, however the resulting task is a *child task* of the currently executing task. Because of this, and the need to suspend to await the results of such expression, `async let` declarations may only occur within an asynchronous context, i.e. an `async` function or closure. -For single statement expressions in the `async let` initializer, the `await` and `try` keywords may be omitted. The effects they represent carry through to the introduced constant and will have to be used when waiting on the constant. In the example shown above, the veggies are declared as `async let veggies = chopVegetables()`, and even through `chopVegetables` is `async` and `throws`, the `await` and `try` keywords do not have to be used on that line of code. Once waiting on the value of that `async let` constant, the compiler will enforce that the expression where the `veggies` appear must be covered by both `await` and some form of `try`. +For single statement expressions in the `async let` initializer, the `await` and `try` keywords may be omitted. The effects they represent carry through to the introduced constant and will have to be used when waiting on the constant. In the example shown above, the veggies are declared as `async let veggies = chopVegetables()`, and even though `chopVegetables` is `async` and `throws`, the `await` and `try` keywords do not have to be used on that line of code. Once waiting on the value of that `async let` constant, the compiler will enforce that the expression where the `veggies` appear must be covered by both `await` and some form of `try`. Because the main body of the function executes concurrently with its child tasks, it is possible that the parent task (the body of `makeDinner` in this example) will reach the point where it needs the value of a `async let` (say,`veggies`) before that value has been produced. To account for that, reading a variable defined by a `async let` is treated as a potential suspension point, and therefore must be marked with `await`. @@ -133,7 +133,7 @@ and therefore must be marked with `await`. `async let` declarations are similar to `let` declarations, however they can only appear in specific contexts. -Because the asynchronous task must be able to be awaited on in the scope it is created, it is only possible to declare `async let`s in contexts where it would also be legal to write an explicit `await`, i.e. asynchronous functions: +Because the asynchronous task must be able to be awaited on in the scope it is created in, it is only possible to declare `async let`s in contexts where it would also be legal to write an explicit `await`, i.e. asynchronous functions: ```swift func greet() async -> String { "hi" } @@ -236,7 +236,7 @@ async let (l, r) = { meaning that the entire initializer of the `async let` is a single task, and if multiple asynchronous function calls are made inside it, they are performed one-by one. This is a specific application of the general rule of `async let` initializers being allowed to omit a single leading `await` keyword before their expressions. Because in this example, we invoke two asynchronous functions to form a tuple, the `await` can be moved outside the expression, and that await is what is omitted in the shorthand form of the `async let` that we've seen in the first snippet. -This also means that as soon as we enter continue past the line of `await l` it is known that the `r` value also has completed successfully (and will not need to emit an "implicit await" which we'll discuss in detail below). +This also means that as soon as we continue past the line of `await l`, it is known that the `r` value also has completed successfully (and will not need to emit an "implicit await" which we'll discuss in detail below). Another implication of these semantics is that if _any_ piece of the initializer throws, any await on such pattern declared `async let` shall be considered throwing, as they are initialized "together". To visualize this, let us consider the following: @@ -288,13 +288,13 @@ _ = try await ohNo _ = try await ohNo ``` -This is a simple rule and allows us to bring the feature forward already. It might be possible to employ control flow based analysis to enable "only the first reference to the specific `async let` on each control flow path has to be an `await`", as technically speaking, every following await will be a no-op and will not suspend as the value is already completed, and the placeholder has been filled in. +This is a simple rule and allows us to bring the feature forward already. It might be possible to employ control flow based analysis to enable "only the first reference to the specific `async let` on each control flow path has to be an `await`", as technically speaking, every following await will be a no-op and will not suspend as the value is already completed and the placeholder has been filled in. ### Implicit `async let` awaiting -A `async let` that was declared but never awaited on *explicitly* as the scope in which it was declared exits, will be awaited on implicitly. These semantics are put in place to uphold the Structured Concurrency guarantees provided by `async let`. +A `async let` that was declared but never awaited on *explicitly*, as the scope in which it was declared exits, will be awaited on implicitly. These semantics are put in place to uphold the Structured Concurrency guarantees provided by `async let`. -To showcase these semantics, let us have a look at this function which spawns two child tasks, `fast` and `slow` but does not await on any of them: +To showcase these semantics, let us have a look at this function which spawns two child tasks, `fast` and `slow`, but does not await on any of them: ```swift func go() async { @@ -312,7 +312,7 @@ Assuming the execution times of `fast()` and `slow()` are as the comments next t As we return from the `go()` function without ever having awaited on the `f` or `s` values, both of them will be implicitly cancelled and awaited on before returning from the function `go()`. This is the very nature of structured concurrency, and avoiding this can _only_ be done by creating non-child tasks, e.g. by using `Task.detached` or other future APIs which would allow creation of non-child tasks. -If we instead awaited on one of the values, e.g. the fast one (`f`) the emitted code would not need to implicitly cancel or await it, as this was already taken care of explicitly: +If we instead awaited on one of the values, e.g. the fast one (`f`), the emitted code would not need to implicitly cancel or await it, as this was already taken care of explicitly: ```swift func go2() async { @@ -327,7 +327,7 @@ func go2() async { The duration of the `go2()` call remains the same, it is always `time(go2) == max(time(f), time(s))`. -Special attention needs to be given to the `async let _ = ...` form of declarations. This form is interesting because it creates a child-task of the right-hand-side initializer, however it actively chooses to ignore the result. Such a declaration (and the associated child-task) will run and be cancelled and awaited-on implicitly, as the scope it was declared in is about to exit — the same way as an unused `async let` declaration would be. +Special attention needs to be given to the `async let _ = ...` form of declarations. This form is interesting because it creates a child-task from the right-hand-side initializer, however it actively chooses to ignore the result. Such a declaration (and the associated child-task) will run and be cancelled and awaited-on implicitly, as the scope it was declared in is about to exit — the same way as an unused `async let` declaration would be. ### `async let` and closures @@ -368,7 +368,7 @@ await greet { await name } // error: cannot escape 'async let' value While it is legal to declare a `async let` and never explicitly `await` on it, it also implies that we do not particularly care about its result. -This is the same as spawning a number of child-tasks in a task group, and not collecting their results, like so: +This is the same as spawning a number of child-tasks in a task group and not collecting their results, like so: ```swift try await withThrowingTaskGroup(of: Int.self) { group in @@ -378,7 +378,7 @@ try await withThrowingTaskGroup(of: Int.self) { group in } // returns 0 ``` -The above TaskGroup example will ignore the `Boom` thrown by its child task. However, it _will_ await for the task (and any other tasks it had spawned) to run to completion before the `withThrowingTaskGroup` returns. If we wanted to surface all potential throws of tasks spawned in the group, we should have written: `for try await _ in group {}` which would have re-thrown the `Boom()`. +The above TaskGroup example will ignore the `Boom` thrown by its child task. However, it _will_ await for the task (and any other tasks it has spawned) to run to completion before the `withThrowingTaskGroup` returns. If we wanted to surface all potential throws of tasks spawned in the group, we should have written: `for try await _ in group {}` which would have re-thrown the `Boom()`. The same concept carries over to `async let`, where the scope of the group is replaced by the syntactic scope in which the `async let` was declared. For example, the following snippet is semantically equivalent to the above TaskGroup one: @@ -485,11 +485,11 @@ func toyParallelMapExactly2(_ items: [A], f: (A) async -> B) async -> [B] } ``` -And while the second example reads very nicely, it cannot work in practice to implement such parallel map function, because the size of the input `items` is not known (and we'd have to implement `1...n` versions of such function). +And while the second example reads very nicely, it cannot work in practice to implement such parallel map function, because the size of the input `items` is not known (and we'd have to implement `1...n` versions of such a function). Another API which is not implementable with `async let` and will require using a task group is anything that requires some notion of completion order. Because `async let` declarations must be awaited on it is not possible to express "whichever completes first", and a task group must be used to implement such API. -For example, the `race(left:right:)` function shown below, runs two child tasks in parallel, and returns whichever completed first. Such API is not possible to implement using async let and must be implemented using a group: +For example, the `race(left:right:)` function shown below, runs two child tasks in parallel, and returns whichever completes first. Such API is not possible to implement using async let and must be implemented using a group: ```swift func race(left: () async -> Int, right: () async -> Int) async -> Int { @@ -508,7 +508,7 @@ func race(left: () async -> Int, right: () async -> Int) async -> Int { It is worth comparing `async let` declarations with the one other API proposed so far that is able to start asynchronous tasks: `Task {}`, and `Task.detached {}`, proposed in [SE-0304: Structured Concurrency](0304-structured-concurrency.md). -First off, `Task.detached` most of the time should not be used at all, because it does _not_ propagate task priority, task-local values or the execution context of the caller. Not only that but a detached task is inherently not _structured_ and thus may out-live its defining scope. +First off, `Task.detached` most of the time should not be used at all, because it does _not_ propagate task priority, task-local values or the execution context of the caller. Not only that but a detached task is inherently not _structured_ and thus may outlive its defining scope. This immediately shows how `async let` and the general concept of child-tasks are superior to detached tasks. They automatically propagate all necessary information about scheduling and metadata necessary for execution tracing. And they can be allocated more efficiently than detached tasks. @@ -583,7 +583,7 @@ func run() async { This snippet is semantically equivalent to the one before it, in that the `await alcatraz` happens before the `escapeFrom` function is able to run. -While it is only a small syntactic improvement over the second snippet in this section, it is a welcome and consistent one with prior patterns in swift, where it is possible to capture a `[weak variable]` in closures. +While it is only a small syntactic improvement over the second snippet in this section, it is a welcome and consistent one with prior patterns in Swift, where it is possible to capture a `[weak variable]` in closures. The capture list is only necessary for `@escaping` closures, as non-escaping ones are guaranteed to not "out-live" the scope from which they are called, and thus cannot violate the structured concurrency guarantees an `async let` relies on. @@ -591,7 +591,7 @@ The capture list is only necessary for `@escaping` closures, as non-escaping one It is reasonable to request that specific `async let` initializers run on specific executors. -While this usually not necessary to actor based code, because actor invocations will implicitly "hop" to the right actor as it is called, like in the example below: +This is usually not necessary for actor based code, because actor invocations will implicitly "hop" to the right actor as it is called, like in the example below: ```swift actor Worker { func work() {} } @@ -600,11 +600,11 @@ let worker: Worker = ... async let x = worker.work() // implicitly hops to the worker to perform the work ``` -The reasons it may be beneficial to specify an executor child-tasks should run are multiple, and the list is by no means exhaustive, but to give an idea, specifying the executor of child-tasks may: +There are many reasons it may be beneficial to specify an executor in which child-tasks should run, and the list is by no means exhaustive, but to give an idea, specifying the executor of child-tasks may: -- pro-actively fine-tune executors to completely avoid any thread and executor hopping in such tasks, +- proactively fine-tune executors to completely avoid any thread and executor hopping in such tasks, - execute child-tasks concurrently however _not_ in parallel with the creating task (e.g. make child tasks run on the same serial executor as the calling actor), -- if the child-task work is known to be heavy and blocking, it may be beneficial to delegate it to a specific "blocking executor" which would have a dedicated, small, number of threads on which it would execute the blocking work; Thanks to such separation, the main global thread-pool would not be impacted by starvation issues which such blocking tasks would otherwise cause. +- if the child-task work is known to be heavy and blocking, it may be beneficial to delegate it to a specific "blocking executor" which would have a dedicated, small, number of threads on which it would execute the blocking work; thanks to such separation, the main global thread-pool would not be impacted by starvation issues which such blocking tasks would otherwise cause. - various other examples where tight control over the execution context is required... We should be able to allow such configuration based on scope, like this: @@ -635,7 +635,7 @@ The details of the API remain to be seen, but the general ability to specify an ### Explicit futures -As discussed in the [structured concurrency proposal](0304-structured-concurrency.md#prominent-futures), we choose not to expose futures or `Task`s for child tasks in task groups, because doing so either can undermine the hierarchy of tasks, by escaping from their parent task group and being awaited on indefinitely later, or would result in there being two kinds of future, one of which dynamically asserts that it's only used within the task's scope. `async let` allows for future-like data flow from child tasks to parent, without the need for general-purpose futures to be exposed. +As discussed in the [structured concurrency proposal](0304-structured-concurrency.md#prominent-futures), we choose not to expose futures or `Task`s for child tasks in task groups, because doing so can either undermine the hierarchy of tasks, by escaping from their parent task group and being awaited on indefinitely later, or would result in there being two kinds of future, one of which dynamically asserts that it's only used within the task's scope. `async let` allows for future-like data flow from child tasks to parent, without the need for general-purpose futures to be exposed. ### "Don't spawn tasks when in cancelled parent" @@ -825,7 +825,7 @@ class AsyncLet { A property-wrapper approach is forced to create unstructured concurrency to capture the task, which is then subject to escaping (e.g., the synthesized backing storage property `_veggies`). Once we have unstructured concurrency, there is no way to get the structure back: the deinitializer cannot wait on completion of the task, so the task would keep running after the `@AsyncLet` property has been destroyed. The lack of structure also affects the compiler's ability to reason about (and therefore optimize) the use of this feature: as a structured concurrency primitive, `async let` can be optimized by the compiler to (e.g.) share storage of its async stack frames with its parent async task, eliminating spurious allocations, and provide more optimal access patterns for the resulting value. To address the semantic and performance issues with using property wrappers, an `@AsyncLet` property wrapper would effectively be hard-coded syntax in the compiler that is property-wrapper-like, but not actually a property wrapper. -One thing that is lost with the property-wrapper approach that the definition of a property such as +One thing that is lost with the property-wrapper approach is that the definition of a property such as ```swift @AsyncLet var veggies = try await chopVegetables() @@ -841,13 +841,13 @@ await veggies ### Braces around the `async let` initializer -The expression on the right-hand side of an `async let` declaration is executed in a separate, child task that is running concurrently with the function that initiates the `async let`. It has been suggested that the task should be called out more explicitly by adding a separate set of braces around the expression, e.g., +The expression on the right-hand side of an `async let` declaration is executed in a separate child task that is running concurrently with the function that initiates the `async let`. It has been suggested that the task should be called out more explicitly by adding a separate set of braces around the expression, e.g., ```swift async let veggies = { try await chopVegetables() } ``` -The problem with requiring braces is that it breaks the equivalence between the type of the entity being declared (`veggies` is of type `[Vegetable]`) and the value it is initialized with (which now appears to be `@Sendable () async throws -> [Vegetable]`). This equivalence holds throughout nearly all of the language; the only real exception is the `if let` syntax, which which strips a level of optionality and is often considered a design mistake in Swift. For `async let`, requiring the braces would become particularly awkward if one were defining a value of closure type: +The problem with requiring braces is that it breaks the equivalence between the type of the entity being declared (`veggies` is of type `[Vegetable]`) and the value it is initialized with (which now appears to be `@Sendable () async throws -> [Vegetable]`). This equivalence holds throughout nearly all of the language; the only real exception is the `if let` syntax, which strips a level of optionality and is often considered a design mistake in Swift. For `async let`, requiring the braces would become particularly awkward if one were defining a value of closure type: ```swift async let closure = { { try await getClosure() } } From 00bcd87bc2e59b2f3e9e1b0c7d4170c4990de815 Mon Sep 17 00:00:00 2001 From: Jonathan Grynspan Date: Mon, 3 Nov 2025 15:33:39 -0500 Subject: [PATCH 4553/4563] [ST-0017] Add a future direction to move to `RawSpan`. (#3013) --- proposals/testing/0017-image-attachment-consolidation.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/proposals/testing/0017-image-attachment-consolidation.md b/proposals/testing/0017-image-attachment-consolidation.md index 3866869514..5f40134d12 100644 --- a/proposals/testing/0017-image-attachment-consolidation.md +++ b/proposals/testing/0017-image-attachment-consolidation.md @@ -151,7 +151,13 @@ No changes. ## Future directions -N/A +- Migrating from `UnsafeRawBufferPointer` to `RawSpan`. We shipped the initial + attachments feature using `UnsafeRawBufferPointer` before `RawSpan` was + available and, in particular, before it was back-deployed to earlier Apple + platforms. We want the attachments API to consistently use the same types at + all layers, so adoption of `RawSpan` only in the image attachments layer is a + non-goal. In the future, we may wish to deprecate the existing APIs that use + `UnsafeRawBufferPointer` and introduce replacements that use `RawSpan`. ## Alternatives considered From 82b7bdf8256caa42f3c94110858f30ebd65e9cee Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 3 Nov 2025 13:34:28 -0800 Subject: [PATCH 4554/4563] Update proposals/0461-async-function-isolation.md Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> --- proposals/0461-async-function-isolation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0461-async-function-isolation.md b/proposals/0461-async-function-isolation.md index 1d7d2de812..fef2459dc9 100644 --- a/proposals/0461-async-function-isolation.md +++ b/proposals/0461-async-function-isolation.md @@ -337,7 +337,7 @@ struct S: Sendable { ``` Only (implicitly or explicitly) `nonisolated` functions can be marked with -`@concurrent`; it is an error to use these attributes with +`@concurrent`; it is an error to use this attribute with an isolation other than `nonisolated`, including global actors, isolated parameters, and `@isolated(any)`: From f37347d15cee83eff7a97475c636cce5672d6c07 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 4 Nov 2025 08:19:58 -0800 Subject: [PATCH 4555/4563] Mark SE-0493 as accepted. (#3014) --- proposals/0493-defer-async.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0493-defer-async.md b/proposals/0493-defer-async.md index c460b1cbd4..1ec46c74a5 100644 --- a/proposals/0493-defer-async.md +++ b/proposals/0493-defer-async.md @@ -3,9 +3,9 @@ * Proposal: [SE-0493](0493-defer-async.md) * Authors: [Freddy Kellison-Linn](https://github.com/Jumhyn) * Review Manager: [Holly Borla](https://github.com/hborla) -* Status: **Active review (September 22 - October 6, 2025)** +* Status: **Accepted** * Implementation: [swiftlang/swift#83891](https://github.com/swiftlang/swift/pull/83891) -* Review: ([pitch](https://forums.swift.org/t/support-async-calls-in-defer-bodies/81790)) ([review](https://forums.swift.org/t/se-0493-support-async-calls-in-defer-bodies/82293)) +* Review: ([pitch](https://forums.swift.org/t/support-async-calls-in-defer-bodies/81790)) ([review](https://forums.swift.org/t/se-0493-support-async-calls-in-defer-bodies/82293)) ([acceptance](https://forums.swift.org/t/accepted-se-0493-support-async-calls-in-defer-bodies/83023)) ## Introduction From 61963826270a75c6be126194b1bd3b2557983a49 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 5 Nov 2025 10:32:55 +0900 Subject: [PATCH 4556/4563] Proposal: Expose demangle() function in Runtime module (#2989) * proposal for exposing demangling * update demangle to use UTF8Span in and Output span out * we also demangle C++ symbols * Update and rename NNNN-runtime-demangle.md to 0498-runtime-demangle.md --------- Co-authored-by: Stephen Canon --- proposals/0498-runtime-demangle.md | 123 +++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 proposals/0498-runtime-demangle.md diff --git a/proposals/0498-runtime-demangle.md b/proposals/0498-runtime-demangle.md new file mode 100644 index 0000000000..7f117d0c16 --- /dev/null +++ b/proposals/0498-runtime-demangle.md @@ -0,0 +1,123 @@ +# Expose demangle function in Runtime module + +* Proposal: [SE-0498](0498-runtime-demangle.md) +* Authors: [Konrad'ktoso'Malawski](https://github.com/ktoso), [Alejandro Alonso](https://github.com/Azoy) +* Review Manager: [Steve Canon](https://github.com/stephentyrone) +* Status: **Active Review (November 4 ..< 18)** +* Implementation: [PR #84788](https://github.com/swiftlang/swift/pull/84788) +* Review: + * Previous [pitch](https://forums.swift.org/t/demangle-function/25416/16) + +## Introduction + +Swift symbols are subject to name mangling. These mangled names then show up in backtraces and other profiling tools. Mangled names may look something like this `$sSS7cStringSSSPys4Int8VG_tcfC` and often end up visible to developers, unless they are demangled before displaying. + +In manu situations, it is much preferable to demangle the identifiers before displaying them. For example, the previously shown identifier would can be demangled as `Swift.String.init(cString: Swift.UnsafePointer) -> Swift.String`, which is a nice human-readable format, that a Swift developer can easily understand. + +This proposal introduces a new API that allows calling out to the Swift runtime's demangler, without leaving the process. + +## Motivation + +Currently, many tools that need to display symbol names to developers are forced to create a process and execute the `swift-demangle` tool, or use unofficial runtime APIs to invoke the runtime's demangler. + +Neither of these approaches are satisfactionary, because either we are paying a high cost for creating processes, or we're relying on unofficial APIs. + +This proposal introduces an official `demangle(:String) -> String?` function that offers a maintained and safe way to call the Swift demangled from a running Swift application. + +## Proposed solution + +We propose to introduce two `demangle` functions in the `Runtime` module: + +A simple demangle method, returning an optional `String`: + +```swift +public func demangle(_ mangledName: String) -> String? +``` + +The demangling function supports all valid Swift symbols. Valid Swift 5.0 and later symbols begin with `$s` (preceded by an optional platform-specific prefix). + +And an overload which accepts an `UTF8Span` into which the demangled string can be written: + +```swift +public func demangle( + _ mangledName: borrowing UTF8Span, + into output: inout OutputSpan +) -> DemanglingResult + +public enum DemanglingResult: Equatable { + case success + case failed + case truncated(Int) +} +``` + +The span accepting API is necessary for performance sensitive use-cases, which attempt to demangle symbols in process, before displaying or sending them for further processing. In those use-cases it is common to have a known maximum buffer size into which we are willing to write the demangled representation. + +The output from this API is an `OutputSpan` of `UTF8.CodeUnit`s, and it may not necessarily be well-formed UTF8, because of the potential of truncation happening between two code units which would render the UTF8 invalid. + +If the demangled representation does not fit the preallocated buffer, the demangle method will return `truncated(actualSize)` such that developers can determine by how much the buffer might need to be increased to handle the complete demangling. + +To construct an `UTF8Span` or valid `String` from the `OutputSpan` you can do the following: + +```swift +var demangledOutputSpan: OutputSpan = ... + +if demangle("$sSG", into: &demangledOutputSpan) == .success { + let utf8 = try UTF8Span(validating: demangledOutputSpan.span) + let demangledString = String(copying: utf8) + print(demangledString) // Swift.RandomNumberGenerator +} +``` + +### Demangling format + +While the mangled strings are part of Swift ABI and can therefore not really change on platforms with stable ABI, the demangled representation returned by the `demangle` functions is _not guaranteed to be stable in any way_. + +The demangled representation may change without any warning, during even patch releases of Swift. The returned strings should be treated mostly as nicer to present to developers human readable representations, and it is not a goal to provide any form of guarantee about the exact shape of these. + +## Future directions + +### Customizing demangling behavior with options + +We intentionally do not offer customization of demangling modes in this initial revision of this API. The Swift demangler does offer various options about hot demangling should be performed, however we are not confident enough in selecting a subset of those options to offer as API at this point. + +Adding those options will be possible in future revisions, by adding an additional `options: DemanglingOptions` parameter to the demangle functions introduced in this proposal. + +## Source compatibility + +This proposal is purely additive. + +## ABI compatibility + +This proposal is purely additive. + +## Implications on adoption + +The runtime demangling func becoming an official entry point will help prevent libraries call swift internals. + +## Alternatives considered + +### Do nothing + +Not exposing this demangling capabilities officially, would result in tools authors continuing to use +unofficial ways to get to this API. + +It also means that further locking down access to `swift_` APIs may be difficult, +as they are crucial and load bearing for some tools (such as *continuous profiling*). +E.g. recent versions of Swift issue the following warning when accessing some of its `swift_` namespaced runtime functions: + +> symbol name 'swift_...' is reserved for the Swift runtime and cannot be directly referenced without causing unpredictable behavior; this will become an error + +So if we wanted to further lock down such uses, including `swift_demangle`, it would be preferable to offer an official solution instead. + +### Expose from Swift module + +Previously, this was considered to expose from just the `Swift` module, however the `Runtime` +module is much more aligned with the use and utility of this function. + +Demangling is already used by the `Backtrace` type which is located in the Runtime module, +so the demangling functions should be exposed from the same place. + +## Acknowledgments + +Thanks to [Alejandro Alonso](https://github.com/Azoy), who did an initial version of this pitch many years ago, and this proposal is heavily based on his initial pitch. From c30efdf5145b95fb3b2d6a45d9d9f69d7e0dcaad Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Tue, 4 Nov 2025 20:37:03 -0500 Subject: [PATCH 4557/4563] Update 0498-runtime-demangle.md (#3015) --- proposals/0498-runtime-demangle.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proposals/0498-runtime-demangle.md b/proposals/0498-runtime-demangle.md index 7f117d0c16..f7ef196d19 100644 --- a/proposals/0498-runtime-demangle.md +++ b/proposals/0498-runtime-demangle.md @@ -7,6 +7,9 @@ * Implementation: [PR #84788](https://github.com/swiftlang/swift/pull/84788) * Review: * Previous [pitch](https://forums.swift.org/t/demangle-function/25416/16) + * [(pitch)](https://forums.swift.org/t/pitch-expose-demangle-function-in-runtime-module/82605) + [(review)](https://forums.swift.org/t/se-0498-expose-demangle-function-in-runtime-module/83032) + ## Introduction From 09db595f97277d05531e1fbd8533145a25482793 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Wed, 5 Nov 2025 08:32:50 -0500 Subject: [PATCH 4558/4563] Update 0498-runtime-demangle.md (#3016) add link to SE-0262 --- proposals/0498-runtime-demangle.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/0498-runtime-demangle.md b/proposals/0498-runtime-demangle.md index f7ef196d19..39e45b22b9 100644 --- a/proposals/0498-runtime-demangle.md +++ b/proposals/0498-runtime-demangle.md @@ -1,6 +1,7 @@ # Expose demangle function in Runtime module * Proposal: [SE-0498](0498-runtime-demangle.md) +* Previous Proposal: [SE-0262](0262-demangle.md) * Authors: [Konrad'ktoso'Malawski](https://github.com/ktoso), [Alejandro Alonso](https://github.com/Azoy) * Review Manager: [Steve Canon](https://github.com/stephentyrone) * Status: **Active Review (November 4 ..< 18)** From 4be7b79ab48f035dddeb7d9078f07778149648b8 Mon Sep 17 00:00:00 2001 From: Janosch Hildebrand Date: Wed, 5 Nov 2025 14:50:41 +0100 Subject: [PATCH 4559/4563] Fix minor typos in SE-0498 --- proposals/0498-runtime-demangle.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0498-runtime-demangle.md b/proposals/0498-runtime-demangle.md index 39e45b22b9..93fe3a9a5e 100644 --- a/proposals/0498-runtime-demangle.md +++ b/proposals/0498-runtime-demangle.md @@ -26,7 +26,7 @@ Currently, many tools that need to display symbol names to developers are forced Neither of these approaches are satisfactionary, because either we are paying a high cost for creating processes, or we're relying on unofficial APIs. -This proposal introduces an official `demangle(:String) -> String?` function that offers a maintained and safe way to call the Swift demangled from a running Swift application. +This proposal introduces an official `demangle(:String) -> String?` function that offers a maintained and safe way to call the Swift demangler from a running Swift application. ## Proposed solution @@ -83,7 +83,7 @@ The demangled representation may change without any warning, during even patch r ### Customizing demangling behavior with options -We intentionally do not offer customization of demangling modes in this initial revision of this API. The Swift demangler does offer various options about hot demangling should be performed, however we are not confident enough in selecting a subset of those options to offer as API at this point. +We intentionally do not offer customization of demangling modes in this initial revision of this API. The Swift demangler does offer various options about how demangling should be performed, however we are not confident enough in selecting a subset of those options to offer as API at this point. Adding those options will be possible in future revisions, by adding an additional `options: DemanglingOptions` parameter to the demangle functions introduced in this proposal. From 2cfc843cb39568ed3379ab5164731970e9ab4187 Mon Sep 17 00:00:00 2001 From: Rachel Brindle Date: Wed, 5 Nov 2025 09:28:11 -0800 Subject: [PATCH 4560/4563] ST-0017: Review complete and proposal is accepted --- proposals/testing/0017-image-attachment-consolidation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/testing/0017-image-attachment-consolidation.md b/proposals/testing/0017-image-attachment-consolidation.md index 5f40134d12..a0b4975777 100644 --- a/proposals/testing/0017-image-attachment-consolidation.md +++ b/proposals/testing/0017-image-attachment-consolidation.md @@ -3,9 +3,9 @@ * Proposal: [ST-0017](0017-image-attachment-consolidation.md) * Authors: [Jonathan Grynspan](https://github.com/grynspan) * Review Manager: [Rachel Brindle](https://github.com/younata) -* Status: **Active Review (October 22-30, 2025)** +* Status: **Accepted** * Implementation: [swiftlang/swift-testing#1359](https://github.com/swiftlang/swift-testing/pull/1359) -* Review: ([pitch](https://forums.swift.org/t/pitch-adjustments-to-image-attachments-in-swift-testing/82581), [review](https://forums.swift.org/t/st-0017-consolidate-swift-testing-s-image-attachments-api-across-platforms/82815)) +* Review: ([pitch](https://forums.swift.org/t/pitch-adjustments-to-image-attachments-in-swift-testing/82581), [review](https://forums.swift.org/t/st-0017-consolidate-swift-testing-s-image-attachments-api-across-platforms/82815), [acceptance](https://forums.swift.org/t/accepted-st-0017-consolidate-swift-testing-s-image-attachments-api-across-platforms/83045)) ## Introduction From 87099ce6354a93c5ae392c636580b1099f39207e Mon Sep 17 00:00:00 2001 From: Lukas Kollmer Date: Wed, 5 Nov 2025 20:40:29 +0100 Subject: [PATCH 4561/4563] fix typo in demangle function signature --- proposals/0498-runtime-demangle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0498-runtime-demangle.md b/proposals/0498-runtime-demangle.md index 93fe3a9a5e..c26ec6b2a7 100644 --- a/proposals/0498-runtime-demangle.md +++ b/proposals/0498-runtime-demangle.md @@ -26,7 +26,7 @@ Currently, many tools that need to display symbol names to developers are forced Neither of these approaches are satisfactionary, because either we are paying a high cost for creating processes, or we're relying on unofficial APIs. -This proposal introduces an official `demangle(:String) -> String?` function that offers a maintained and safe way to call the Swift demangler from a running Swift application. +This proposal introduces an official `demangle(_: String) -> String?` function that offers a maintained and safe way to call the Swift demangler from a running Swift application. ## Proposed solution From fbfbc80b9d3240901650ec09480a2ad5fc0426d2 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 6 Nov 2025 08:42:13 -0800 Subject: [PATCH 4562/4563] SE-0497: Clarify that @usableFromInline also doesn't prevent the definition from being visible to clients --- proposals/0497-definition-visibility.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0497-definition-visibility.md b/proposals/0497-definition-visibility.md index 8cf389c2f4..21078cae05 100644 --- a/proposals/0497-definition-visibility.md +++ b/proposals/0497-definition-visibility.md @@ -86,7 +86,7 @@ This can present a code portability problem for Embedded Swift. The proposed att This proposal introduces a new attribute `@export` that provides the required control over the ability of clients to make use of the callable interface or the definition of a particular function (or both). The `@export` attribute takes one or both of the following arguments in parentheses: -* `interface`: means that a symbol is present in the binary in a manner that can be called by clients. +* `interface`: means that a symbol is present in the binary in a manner that can be called by clients that can see the symbol. * `implementation`: means that the function definition is available for clients to use for any purpose, including specialization, inlining, or merely analyzing the body for optimization purposes. The existing `@_alwaysEmitIntoClient` is subsumed by `@export(implementation)`, meaning that the definition is available and each client that uses it must emit their own copy of the definition, because there is no symbol. The `@_neverEmitIntoClient` attribute on `main` is subsumed by `@export(interface)`, meaning that a callable symbol is emitted but the definition is not available to callers for any reason. @@ -94,7 +94,7 @@ The existing `@_alwaysEmitIntoClient` is subsumed by `@export(implementation)`, The two attributes introduced by [SE-0193](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0193-cross-module-inlining-and-specialization.md) are partially subsumed by `@export`: * `@inlinable` for making a definition available to callers, similar to `@export(implementation)`. `@inlinable` is guaranteed to produce a symbol under Library Evolution, but there are no guarantees otherwise. In practice, non-Embedded Swift will produce a symbol, but Embedded Swift generally does not. -* `@usableFromInline` for making a less-than-public symbol available for use in an inlinable function (per SE-0193) is akin to `@export(interface)`. As with `@inlinable`, `@usableFromInline` does not *guarantee* the presence of a symbol in the way that `@export(interface)` does: in practice, Embedded Swift may not produce a symbol, but non-Embedded Swift will. +* `@usableFromInline` for making a less-than-public symbol available for use in an inlinable function (per SE-0193) is akin to `@export(interface)`. As with `@inlinable`, `@usableFromInline` does not *guarantee* the presence of a symbol in the way that `@export(interface)` does: in practice, Embedded Swift may not produce a symbol, but non-Embedded Swift will. Additionally, `@usableFromInline` does not prohibit the definition of the function from being made available to clients the way that `@export(interface)` does. `@export` cannot be combined with any of `@inlinable`, `@usableFromInline`, `@_alwaysEmitIntoClient`, or `@_neverEmitIntoClient`. From dbb85f32ba6e9d914404564fec475dce6821ac73 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Thu, 6 Nov 2025 11:58:17 -0800 Subject: [PATCH 4563/4563] Accept SE-0497 --- proposals/0497-definition-visibility.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/0497-definition-visibility.md b/proposals/0497-definition-visibility.md index 21078cae05..8b1a9f735a 100644 --- a/proposals/0497-definition-visibility.md +++ b/proposals/0497-definition-visibility.md @@ -3,9 +3,9 @@ * Proposal: [SE-0497](0497-definition-visibility.md) * Authors: [Doug Gregor](https://github.com/DougGregor/) * Review Manager: [Becca Royal-Gordon](https://github.com/beccadax) -* Status: **Active review (October 13...27, 2025)** +* Status: **Accepted** * Implementation: Functionality is available via hidden `@_alwaysEmitIntoClient` and `@_neverEmitIntoClient` attributes in recent `main` snapshots. -* Review: ([pitch](https://forums.swift.org/t/pitch-controlling-function-definition-visibility-in-clients/82372)) ([review](https://forums.swift.org/t/se-0497-controlling-function-definition-visibility-in-clients/82666)) +* Review: ([pitch](https://forums.swift.org/t/pitch-controlling-function-definition-visibility-in-clients/82372)) ([review](https://forums.swift.org/t/se-0497-controlling-function-definition-visibility-in-clients/82666)) ([acceptance](https://forums.swift.org/t/accepted-se-0497-controlling-function-definition-visibility-in-clients/83068)) ## Introduction